Skip to content

Commit

Permalink
(PC-33099) feat(venueMap): filter header v2
Browse files Browse the repository at this point in the history
  • Loading branch information
mmeissonnier-pass committed Nov 25, 2024
1 parent 136044b commit fa6566e
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import { Filter as FilterIcon } from 'ui/svg/icons/Filter'
import { getSpacing } from 'ui/theme'

type Props = {
activeFilters: number
activeFilters?: number
children?: never
}

export const FilterButton: FunctionComponent<Props> = ({ activeFilters }) => {
export const FilterButton: FunctionComponent<Props> = ({ activeFilters = 0 }) => {
const accessibilityLabel =
activeFilters > 0
? plural(activeFilters, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { customFocusOutline } from 'ui/theme/customFocusOutline/customFocusOutli
import { getHoverStyle } from 'ui/theme/getHoverStyle/getHoverStyle'

type IsSelectedProps = {
isSelected: boolean
isSelected?: boolean
}

type SingleFilterButtonProps = IsSelectedProps & {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react'
import styled from 'styled-components/native'

import { FILTER_BANNER_HEIGHT } from 'features/venueMap/constant'
import { useFeatureFlag } from 'libs/firebase/firestore/featureFlags/useFeatureFlag'
import { RemoteStoreFeatureFlags } from 'libs/firebase/firestore/types'
import { useGetHeaderHeight } from 'ui/components/headers/PageHeaderWithoutPlaceholder'
import { getSpacing } from 'ui/theme'

import { FilterCategoriesBannerContainer } from './FilterCategoriesBannerContainer'
import { SingleFilterBannerContainer } from './SingleFilterBannerContainer'

export const FilterBannerContainer = () => {
const headerHeight = useGetHeaderHeight()

const filterCategoriesActive = useFeatureFlag(
RemoteStoreFeatureFlags.WIP_VENUE_MAP_TYPE_FILTER_V2
)

return (
<Container headerHeight={headerHeight}>
{filterCategoriesActive ? (
<FilterCategoriesBannerContainer />
) : (
<SingleFilterBannerContainer />
)}
</Container>
)
}

const Container = styled.View<{ headerHeight: number }>(({ headerHeight }) => ({
height: FILTER_BANNER_HEIGHT,
position: 'absolute',
zIndex: 1,
top: headerHeight,
left: 0,
right: 0,
paddingHorizontal: getSpacing(6),
paddingVertical: getSpacing(1),
}))
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import colorAlpha from 'color-alpha'
import React from 'react'
import LinearGradient from 'react-native-linear-gradient'
import styled from 'styled-components/native'

import { FilterButton } from 'features/search/components/Buttons/FilterButton/FilterButton'
import { SingleFilterButton } from 'features/search/components/Buttons/SingleFilterButton/SingleFilterButton'
import { FILTERS_VENUE_TYPE_MAPPING } from 'features/venueMap/constant'
import { useVenueMapFilters } from 'features/venueMap/hook/useVenueMapFilters'
import { theme } from 'theme'
import { getSpacing } from 'ui/theme'

const BULLET_SIZE = 12

type FilterGroupKey = keyof typeof FILTERS_VENUE_TYPE_MAPPING

type FilterGroupData = {
id: FilterGroupKey
label: string
color: string
}
const filterGroups: FilterGroupData[] = [
{ id: 'OUTINGS', label: 'Sorties', color: theme.colors.coral },
{ id: 'SHOPS', label: 'Boutiques', color: theme.colors.primary },
{ id: 'OTHERS', label: 'Autres', color: theme.colors.skyBlue },
]

export const FilterCategoriesBannerContainer = () => {
const { getSelectedMacroFilters, addMacroFilter, removeMacroFilter } = useVenueMapFilters()

const selectedGroups = getSelectedMacroFilters()

const handleCategoryPress = (categoryId: FilterGroupKey) => {
if (selectedGroups.includes(categoryId)) {
removeMacroFilter(categoryId)
} else {
addMacroFilter(categoryId)
}
}

return (
<Container>
<FilterButton />
{filterGroups.map(({ color, id, label }) => (
<SingleFilterButton
key={id}
testID={id}
icon={<ColoredGradientBullet color={color} />}
label={label}
isSelected={selectedGroups.includes(id)}
onPress={() => handleCategoryPress(id)}
/>
))}
</Container>
)
}

const Container = styled.View({
flexDirection: 'row',
columnGap: getSpacing(1),
})

const ColoredGradientBullet = styled(LinearGradient).attrs(({ color }: { color: string }) => ({
colors: [color, colorAlpha(color, 0.7)],
}))<{ color: string }>({
backgroundColor: 'black',
width: BULLET_SIZE,
height: BULLET_SIZE,
borderRadius: BULLET_SIZE * 0.5,
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react'
import styled from 'styled-components/native'

import { SingleFilterButton } from 'features/search/components/Buttons/SingleFilterButton/SingleFilterButton'
import { getVenueTypeLabel } from 'features/venueMap/helpers/getVenueTypeLabel/getVenueTypeLabel'
import { VenueTypeModal } from 'features/venueMap/pages/modals/VenueTypeModal/VenueTypeModal'
import { useVenueTypeCode } from 'features/venueMap/store/venueTypeCodeStore'
import { ellipseString } from 'shared/string/ellipseString'
import { Li } from 'ui/components/Li'
import { useModal } from 'ui/components/modals/useModal'
import { Ul } from 'ui/components/Ul'
import { Check } from 'ui/svg/icons/Check'
import { getSpacing } from 'ui/theme'

const MAX_VENUE_CHARACTERS = 20

export const SingleFilterBannerContainer = () => {
const venueTypeCode = useVenueTypeCode()
const venueTypeLabel = getVenueTypeLabel(venueTypeCode) ?? 'Tous les lieux'

const {
visible: venueTypeModalVisible,
showModal: showVenueTypeModal,
hideModal: hideVenueTypeModal,
} = useModal(false)

return (
<React.Fragment>
<StyledUl>
<StyledLi>
<SingleFilterButton
label={ellipseString(venueTypeLabel, MAX_VENUE_CHARACTERS)}
isSelected={venueTypeCode !== null}
onPress={showVenueTypeModal}
icon={venueTypeCode ? <FilterSelectedIcon /> : undefined}
/>
</StyledLi>
</StyledUl>
<VenueTypeModal hideModal={hideVenueTypeModal} isVisible={venueTypeModalVisible} />
</React.Fragment>
)
}

const StyledUl = styled(Ul)({
alignItems: 'center',
})

const StyledLi = styled(Li)({
marginLeft: getSpacing(1),
marginVertical: getSpacing(1),
})

const FilterSelectedIcon = styled(Check).attrs<{ testID?: string }>(({ theme, testID }) => ({
size: theme.icons.sizes.extraSmall,
color: theme.colors.black,
testID: testID ? `${testID}Icon` : 'filterButtonIcon',
}))``
12 changes: 9 additions & 3 deletions src/features/venueMap/hook/useVenueMapFilters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ export const useVenueMapFilters = () => {
const activeFilters = useVenuesFilter()
const { setVenuesFilters, addVenuesFilters, removeVenuesFilters } = useVenuesFilterActions()

const applyMacroFilters = (macro: keyof typeof FILTERS_VENUE_TYPE_MAPPING) => {
const addMacroFilter = (macro: keyof typeof FILTERS_VENUE_TYPE_MAPPING) => {
const filters = getFiltersByMacro(macro)
setVenuesFilters(filters)
setVenuesFilters([...activeFilters, ...filters])
}

const removeMacroFilter = (macro: keyof typeof FILTERS_VENUE_TYPE_MAPPING) => {
const filters = getFiltersByMacro(macro)
setVenuesFilters(activeFilters.filter((filter) => !filters.includes(filter)))
}

const toggleFilter = (filter: VenueTypeCodeKey) => {
Expand All @@ -27,7 +32,8 @@ export const useVenueMapFilters = () => {

return {
activeFilters,
applyMacroFilters,
addMacroFilter,
removeMacroFilter,
toggleFilter,
getSelectedMacroFilters,
}
Expand Down
68 changes: 3 additions & 65 deletions src/features/venueMap/pages/VenueMap/VenueMapBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,20 @@ import styled from 'styled-components/native'

import { getSearchStackConfig } from 'features/navigation/SearchStackNavigator/helpers'
import { useGoBack } from 'features/navigation/useGoBack'
import { SingleFilterButton } from 'features/search/components/Buttons/SingleFilterButton/SingleFilterButton'
import { FilterBannerContainer } from 'features/venueMap/components/FilterBannerContainer/FilterBannerContainer'
import { FILTER_BANNER_HEIGHT } from 'features/venueMap/constant'
import { getVenueTypeLabel } from 'features/venueMap/helpers/getVenueTypeLabel/getVenueTypeLabel'
import { VenueTypeModal } from 'features/venueMap/pages/modals/VenueTypeModal/VenueTypeModal'
import {
useVenueTypeCode,
useVenueTypeCodeActions,
} from 'features/venueMap/store/venueTypeCodeStore'
import { ellipseString } from 'shared/string/ellipseString'
import { useVenueTypeCodeActions } from 'features/venueMap/store/venueTypeCodeStore'
import {
PageHeaderWithoutPlaceholder,
useGetHeaderHeight,
} from 'ui/components/headers/PageHeaderWithoutPlaceholder'
import { Li } from 'ui/components/Li'
import { useModal } from 'ui/components/modals/useModal'
import { Ul } from 'ui/components/Ul'
import { Check } from 'ui/svg/icons/Check'
import { getSpacing } from 'ui/theme'

const MAX_VENUE_CHARACTERS = 20

export const VenueMapBase: FunctionComponent<PropsWithChildren> = ({ children }) => {
const { goBack } = useGoBack(...getSearchStackConfig('SearchLanding'))

const venueTypeCode = useVenueTypeCode()
const { setVenueTypeCode } = useVenueTypeCodeActions()

const headerHeight = useGetHeaderHeight()

const {
visible: venueTypeModalVisible,
showModal: showVenueTypeModal,
hideModal: hideVenueTypeModal,
} = useModal(false)

const venueTypeLabel = getVenueTypeLabel(venueTypeCode) ?? 'Tous les lieux'

const handleGoBack = () => {
setVenueTypeCode(null)
goBack()
Expand All @@ -50,22 +27,9 @@ export const VenueMapBase: FunctionComponent<PropsWithChildren> = ({ children })
<Container>
<StyledHeader title="Carte des lieux" onGoBack={handleGoBack} />
<PlaceHolder headerHeight={headerHeight + FILTER_BANNER_HEIGHT} />

<FilterBannerContainer headerHeight={headerHeight}>
<StyledUl>
<StyledLi>
<SingleFilterButton
label={ellipseString(venueTypeLabel, MAX_VENUE_CHARACTERS)}
isSelected={venueTypeCode !== null}
onPress={showVenueTypeModal}
icon={venueTypeCode ? <FilterSelectedIcon /> : undefined}
/>
</StyledLi>
</StyledUl>
</FilterBannerContainer>
<FilterBannerContainer />
{children}
</Container>
<VenueTypeModal hideModal={hideVenueTypeModal} isVisible={venueTypeModalVisible} />
</React.Fragment>
)
}
Expand All @@ -81,29 +45,3 @@ const StyledHeader = styled(PageHeaderWithoutPlaceholder)(({ theme }) => ({
const PlaceHolder = styled.View<{ headerHeight: number }>(({ headerHeight }) => ({
height: headerHeight,
}))

const FilterBannerContainer = styled.View<{ headerHeight: number }>(({ headerHeight }) => ({
height: FILTER_BANNER_HEIGHT,
position: 'absolute',
zIndex: 1,
top: headerHeight,
left: 0,
right: 0,
paddingHorizontal: getSpacing(6),
paddingVertical: getSpacing(1),
}))

const StyledUl = styled(Ul)({
alignItems: 'center',
})

const StyledLi = styled(Li)({
marginLeft: getSpacing(1),
marginVertical: getSpacing(1),
})

const FilterSelectedIcon = styled(Check).attrs<{ testID?: string }>(({ theme, testID }) => ({
size: theme.icons.sizes.extraSmall,
color: theme.colors.black,
testID: testID ? `${testID}Icon` : 'filterButtonIcon',
}))``
1 change: 1 addition & 0 deletions src/libs/firebase/firestore/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export enum RemoteStoreFeatureFlags {
WIP_STEPPER_RETRY_UBBLE = 'wipStepperRetryUbble',
WIP_THEMATIC_SEARCH_MUSIC = 'wipThematicSearchMusic',
WIP_VENUE_MAP = 'wipVenueMap',
WIP_VENUE_MAP_TYPE_FILTER_V2 = 'wipVenueMapTypeFilterV2',
WIP_VENUE_MAP_HIDDEN_POI = 'wipVenueMapHiddenPOI',
WIP_VENUE_MAP_IN_SEARCH = 'wipVenueMapInSearch',
WIP_VENUE_MAP_PIN_V2 = 'wipVenueMapPinV2',
Expand Down

0 comments on commit fa6566e

Please sign in to comment.