willba-component-library 0.3.19 → 0.3.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/components/FilterBar/components/FilterPanels/Locations/Locations.d.ts +0 -1
- package/lib/components/FilterBar/components/FilterTabs/FilterTabs.d.ts +1 -1
- package/lib/components/FilterBar/hooks/index.d.ts +1 -1
- package/lib/components/FilterBar/hooks/useFilterRefs.d.ts +8 -0
- package/lib/components/FilterBar/hooks/useFilterUi.d.ts +2 -1
- package/lib/components/FilterBar/hooks/usePanelPosition.d.ts +2 -2
- package/lib/components/FilterBar/hooks/useScrollInToView.d.ts +1 -1
- package/lib/components/FilterBar/providers/FilterBarProvider.d.ts +3 -2
- package/lib/core/hooks/useCloseFilterSection.d.ts +1 -1
- package/lib/index.esm.js +57 -57
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +57 -57
- package/lib/index.js.map +1 -1
- package/lib/index.umd.js +57 -57
- package/lib/index.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/FilterBar/FilterBar.tsx +1 -2
- package/src/components/FilterBar/components/FilterControls/FilterControls.tsx +10 -6
- package/src/components/FilterBar/components/FilterPanels/Dates/Dates.css +7 -1
- package/src/components/FilterBar/components/FilterPanels/FilterPanels.tsx +10 -12
- package/src/components/FilterBar/components/FilterPanels/Guests/Guests.css +2 -1
- package/src/components/FilterBar/components/FilterPanels/Locations/Locations.tsx +1 -3
- package/src/components/FilterBar/components/FilterPanels/SectionHeader/SectionHeader.css +5 -1
- package/src/components/FilterBar/components/FilterTabs/FilterTabs.tsx +26 -23
- package/src/components/FilterBar/components/ImageCard/ImageCard.css +7 -1
- package/src/components/FilterBar/hooks/index.ts +1 -1
- package/src/components/FilterBar/hooks/{useFilterUi.tsx → useFilterRefs.tsx} +5 -3
- package/src/components/FilterBar/hooks/usePanelPosition.tsx +4 -4
- package/src/components/FilterBar/hooks/useScrollInToView.tsx +4 -4
- package/src/components/FilterBar/providers/FilterBarProvider.tsx +8 -6
- package/src/components/FilterCalendar/FilterCalendar.tsx +2 -2
- package/src/core/hooks/useCloseFilterSection.tsx +5 -5
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@ import '../../themes/Default.css'
|
|
|
11
11
|
import './FilterBar.css'
|
|
12
12
|
|
|
13
13
|
export const FilterBar = ({
|
|
14
|
-
language
|
|
14
|
+
language,
|
|
15
15
|
ageCategories,
|
|
16
16
|
redirectUrl = REDIRECT_URL_FALLBACK,
|
|
17
17
|
palette,
|
|
@@ -62,4 +62,3 @@ export const FilterBar = ({
|
|
|
62
62
|
////////////
|
|
63
63
|
|
|
64
64
|
const REDIRECT_URL_FALLBACK = 'http://localhost:3000/'
|
|
65
|
-
const LANGUAGE_FALLBACK = 'en'
|
|
@@ -27,15 +27,13 @@ export const FilterControls = () => {
|
|
|
27
27
|
locations,
|
|
28
28
|
innerLoading,
|
|
29
29
|
outerLoading,
|
|
30
|
-
|
|
31
30
|
handleSubmit,
|
|
32
31
|
handleSelectedFilter,
|
|
33
32
|
|
|
34
|
-
// Refs
|
|
35
33
|
previouslyFocusedButtonRef,
|
|
36
34
|
buttonRefs,
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
tabsRef,
|
|
36
|
+
filtersRef,
|
|
39
37
|
} = useFilterBar()
|
|
40
38
|
|
|
41
39
|
// Store previously focused button and restore focus when closing
|
|
@@ -64,9 +62,15 @@ export const FilterControls = () => {
|
|
|
64
62
|
return (
|
|
65
63
|
<div
|
|
66
64
|
className={`will-filter-bar-controls ${mode || 'light'}`}
|
|
67
|
-
ref={
|
|
65
|
+
ref={(el) => {
|
|
66
|
+
filtersRef.current = el
|
|
67
|
+
|
|
68
|
+
if (tabs?.length === 1) {
|
|
69
|
+
tabsRef.current = el
|
|
70
|
+
}
|
|
71
|
+
}}
|
|
68
72
|
>
|
|
69
|
-
{
|
|
73
|
+
{locations?.data?.length && locations.data.length > 1 && (
|
|
70
74
|
<>
|
|
71
75
|
<SelectButton
|
|
72
76
|
ref={(el) => (buttonRefs.current[FilterSections.LOCATIONS] = el)}
|
|
@@ -29,7 +29,7 @@ export const FilterPanels = () => {
|
|
|
29
29
|
isMobile,
|
|
30
30
|
panelRef,
|
|
31
31
|
buttonRefs,
|
|
32
|
-
|
|
32
|
+
filtersRef,
|
|
33
33
|
setSelectedLocations,
|
|
34
34
|
setCalendarRange,
|
|
35
35
|
handleSelectedFilter,
|
|
@@ -38,12 +38,12 @@ export const FilterPanels = () => {
|
|
|
38
38
|
} = useFilterBar()
|
|
39
39
|
|
|
40
40
|
// Handle close filter section
|
|
41
|
-
const {
|
|
41
|
+
const { filterSectionRef } = useCloseFilterSection({ handleSelectedFilter })
|
|
42
42
|
|
|
43
43
|
const { localStyles } = usePanelPosition({
|
|
44
44
|
selectedFilter,
|
|
45
45
|
panelRef,
|
|
46
|
-
|
|
46
|
+
filtersRef,
|
|
47
47
|
buttonRefs,
|
|
48
48
|
isMobile,
|
|
49
49
|
})
|
|
@@ -54,7 +54,7 @@ export const FilterPanels = () => {
|
|
|
54
54
|
return (
|
|
55
55
|
<Dates
|
|
56
56
|
autoFocus
|
|
57
|
-
ref={
|
|
57
|
+
ref={filterSectionRef}
|
|
58
58
|
onClose={() => handleSelectedFilter(false)}
|
|
59
59
|
calendarRange={calendarRange}
|
|
60
60
|
setCalendarRange={setCalendarRange}
|
|
@@ -67,7 +67,7 @@ export const FilterPanels = () => {
|
|
|
67
67
|
return (
|
|
68
68
|
<Guests
|
|
69
69
|
autoFocus
|
|
70
|
-
ref={
|
|
70
|
+
ref={filterSectionRef}
|
|
71
71
|
ageCategories={ageCategories}
|
|
72
72
|
ageCategoryCounts={ageCategoryCounts}
|
|
73
73
|
updateGuestsCount={updateGuestsCount}
|
|
@@ -82,9 +82,8 @@ export const FilterPanels = () => {
|
|
|
82
82
|
return (
|
|
83
83
|
<Locations
|
|
84
84
|
autoFocus
|
|
85
|
-
ref={
|
|
85
|
+
ref={filterSectionRef}
|
|
86
86
|
locations={locations?.data}
|
|
87
|
-
language={language}
|
|
88
87
|
selectedLocations={selectedLocations}
|
|
89
88
|
setSelectedLocations={setSelectedLocations}
|
|
90
89
|
multiSelect={locations?.multiSelect}
|
|
@@ -102,11 +101,10 @@ export const FilterPanels = () => {
|
|
|
102
101
|
<div
|
|
103
102
|
ref={panelRef}
|
|
104
103
|
className={`will-filter-bar-panels ${mode || 'light'}`}
|
|
105
|
-
style={
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
104
|
+
style={{
|
|
105
|
+
...localStyles,
|
|
106
|
+
...((!tabs || tabs.length < 2) && !isMobile ? { top: 66 } : {}),
|
|
107
|
+
}}
|
|
110
108
|
>
|
|
111
109
|
{renderContent()}
|
|
112
110
|
</div>
|
|
@@ -7,12 +7,13 @@
|
|
|
7
7
|
flex-direction: column;
|
|
8
8
|
min-width: 400px;
|
|
9
9
|
gap: 20px;
|
|
10
|
-
padding: 0
|
|
10
|
+
padding: 0 30px 16px 30px;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
@media (max-width: 960px) {
|
|
14
14
|
.will-guests-filter-container {
|
|
15
15
|
min-width: auto;
|
|
16
|
+
padding: 0 20px 16px 20px;
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -10,7 +10,6 @@ import './Locations.css'
|
|
|
10
10
|
|
|
11
11
|
type Props = {
|
|
12
12
|
locations?: Location[]
|
|
13
|
-
language?: string
|
|
14
13
|
selectedLocations: Location[]
|
|
15
14
|
setSelectedLocations: (locations: Location[]) => void
|
|
16
15
|
autoFocus?: boolean
|
|
@@ -22,7 +21,6 @@ export const Locations = forwardRef<HTMLDivElement, Props>(
|
|
|
22
21
|
(
|
|
23
22
|
{
|
|
24
23
|
locations,
|
|
25
|
-
language,
|
|
26
24
|
selectedLocations,
|
|
27
25
|
setSelectedLocations,
|
|
28
26
|
autoFocus,
|
|
@@ -71,7 +69,7 @@ export const Locations = forwardRef<HTMLDivElement, Props>(
|
|
|
71
69
|
/>
|
|
72
70
|
|
|
73
71
|
<div className="will-locations-filter-container">
|
|
74
|
-
{!!
|
|
72
|
+
{!!locations?.length &&
|
|
75
73
|
locations.map((location, index) => {
|
|
76
74
|
return (
|
|
77
75
|
<ImageCard
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
display: flex;
|
|
3
3
|
justify-content: space-between;
|
|
4
4
|
align-items: center;
|
|
5
|
-
padding: 16px;
|
|
5
|
+
padding: 16px 30px;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
.will-filter-section-title {
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
@media (max-width: 960px) {
|
|
27
|
+
.will-filter-section-header {
|
|
28
|
+
padding: 16px 20px;
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
.will-filter-section-title {
|
|
28
32
|
font-size: 18px;
|
|
29
33
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { useMemo } from 'react'
|
|
2
2
|
|
|
3
3
|
import { useTranslation } from 'react-i18next'
|
|
4
4
|
|
|
@@ -15,33 +15,36 @@ export const FilterTabs = () => {
|
|
|
15
15
|
selectedPath,
|
|
16
16
|
mode,
|
|
17
17
|
tabs,
|
|
18
|
+
tabsRef,
|
|
18
19
|
handleSelectedFilter,
|
|
19
20
|
setSelectedPath,
|
|
20
21
|
handleResetFilters,
|
|
21
|
-
//
|
|
22
|
-
targetFilterBarRef,
|
|
23
22
|
} = useFilterBar()
|
|
24
23
|
|
|
24
|
+
const sortedTabs = useMemo(
|
|
25
|
+
() => [...(tabs ?? [])].sort((a, b) => a.order - b.order),
|
|
26
|
+
[tabs]
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
const handleTabClick = (path: string) => {
|
|
30
|
+
setSelectedPath(path)
|
|
31
|
+
handleResetFilters()
|
|
32
|
+
handleSelectedFilter(false)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (sortedTabs.length <= 1) return null
|
|
36
|
+
|
|
25
37
|
return (
|
|
26
|
-
tabs
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
handleResetFilters()
|
|
38
|
-
handleSelectedFilter(false)
|
|
39
|
-
}}
|
|
40
|
-
active={selectedPath === tab.path}
|
|
41
|
-
mode={mode}
|
|
42
|
-
/>
|
|
43
|
-
))}
|
|
44
|
-
</div>
|
|
45
|
-
)
|
|
38
|
+
<div className="will-filter-bar-tabs" ref={tabsRef}>
|
|
39
|
+
{sortedTabs.map((tab) => (
|
|
40
|
+
<TabButton
|
|
41
|
+
key={tab.path}
|
|
42
|
+
label={tab.label || t(`tabs.${tab.path.slice(1)}`)}
|
|
43
|
+
onClick={() => handleTabClick(tab.path)}
|
|
44
|
+
active={selectedPath === tab.path}
|
|
45
|
+
mode={mode}
|
|
46
|
+
/>
|
|
47
|
+
))}
|
|
48
|
+
</div>
|
|
46
49
|
)
|
|
47
50
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
gap: 20px;
|
|
4
4
|
justify-content: space-between;
|
|
5
5
|
align-items: center;
|
|
6
|
-
padding: 8px
|
|
6
|
+
padding: 8px 30px;
|
|
7
7
|
cursor: pointer;
|
|
8
8
|
user-select: none;
|
|
9
9
|
min-height: 40px;
|
|
@@ -22,3 +22,9 @@
|
|
|
22
22
|
height: 68px;
|
|
23
23
|
object-fit: cover;
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
@media (max-width: 960px) {
|
|
27
|
+
.will-image-card {
|
|
28
|
+
padding: 8px 20px;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { useScrollInToView } from './useScrollInToView'
|
|
2
2
|
export { useFilterState } from './useFilterState'
|
|
3
3
|
export { useFilterActions } from './useFilterActions'
|
|
4
|
-
export {
|
|
4
|
+
export { useFilterRefs } from './useFilterRefs'
|
|
5
5
|
export { usePanelPosition } from './usePanelPosition'
|
|
@@ -2,18 +2,20 @@ import { useRef } from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { useScrollInToView } from './useScrollInToView'
|
|
4
4
|
|
|
5
|
-
export const
|
|
5
|
+
export const useFilterRefs = (selectedFilter: string | boolean) => {
|
|
6
6
|
const buttonRefs = useRef<Record<string, HTMLButtonElement | null>>({})
|
|
7
7
|
const panelRef = useRef<HTMLDivElement | null>(null)
|
|
8
8
|
const previouslyFocusedButtonRef = useRef<HTMLButtonElement | null>(null)
|
|
9
|
+
const filtersRef = useRef<HTMLDivElement | null>(null)
|
|
9
10
|
|
|
10
|
-
const { isMobile,
|
|
11
|
+
const { isMobile, tabsRef } = useScrollInToView({ selectedFilter })
|
|
11
12
|
|
|
12
13
|
return {
|
|
13
14
|
previouslyFocusedButtonRef,
|
|
14
15
|
isMobile,
|
|
15
|
-
|
|
16
|
+
tabsRef,
|
|
16
17
|
panelRef,
|
|
17
18
|
buttonRefs,
|
|
19
|
+
filtersRef,
|
|
18
20
|
}
|
|
19
21
|
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
type Props = {
|
|
10
10
|
selectedFilter: string | boolean
|
|
11
11
|
panelRef: RefObject<HTMLDivElement | null>
|
|
12
|
-
|
|
12
|
+
filtersRef: RefObject<HTMLDivElement | null>
|
|
13
13
|
buttonRefs: MutableRefObject<Record<string, HTMLButtonElement | null>>
|
|
14
14
|
isMobile: boolean
|
|
15
15
|
}
|
|
@@ -17,7 +17,7 @@ type Props = {
|
|
|
17
17
|
export const usePanelPosition = ({
|
|
18
18
|
selectedFilter,
|
|
19
19
|
panelRef,
|
|
20
|
-
|
|
20
|
+
filtersRef,
|
|
21
21
|
buttonRefs,
|
|
22
22
|
isMobile,
|
|
23
23
|
}: Props) => {
|
|
@@ -30,7 +30,7 @@ export const usePanelPosition = ({
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const panel = panelRef.current
|
|
33
|
-
const container =
|
|
33
|
+
const container = filtersRef.current
|
|
34
34
|
const button = buttonRefs.current[selectedFilter]
|
|
35
35
|
|
|
36
36
|
if (!panel || !container || !button) return
|
|
@@ -39,7 +39,7 @@ export const usePanelPosition = ({
|
|
|
39
39
|
const containerRect = container.getBoundingClientRect()
|
|
40
40
|
const buttonRect = button.getBoundingClientRect()
|
|
41
41
|
|
|
42
|
-
const buttonLeft = buttonRect.left - containerRect.left
|
|
42
|
+
const buttonLeft = buttonRect.left - containerRect.left - 30 // Offset by 30px to account for controls section spacing
|
|
43
43
|
|
|
44
44
|
const left = Math.max(
|
|
45
45
|
0,
|
|
@@ -6,7 +6,7 @@ type Props = {
|
|
|
6
6
|
|
|
7
7
|
export const useScrollInToView = ({ selectedFilter }: Props) => {
|
|
8
8
|
const [isMobile, setIsMobile] = useState(true)
|
|
9
|
-
const
|
|
9
|
+
const tabsRef = useRef<HTMLDivElement | null>(null)
|
|
10
10
|
|
|
11
11
|
useEffect(() => {
|
|
12
12
|
if (typeof window !== 'undefined' && window.innerWidth > 960) {
|
|
@@ -14,16 +14,16 @@ export const useScrollInToView = ({ selectedFilter }: Props) => {
|
|
|
14
14
|
return
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
if (
|
|
17
|
+
if (tabsRef.current && selectedFilter) {
|
|
18
18
|
window.scrollTo({
|
|
19
19
|
behavior: 'smooth',
|
|
20
20
|
top:
|
|
21
|
-
|
|
21
|
+
tabsRef.current.getBoundingClientRect().top -
|
|
22
22
|
document.body.getBoundingClientRect().top -
|
|
23
23
|
30,
|
|
24
24
|
})
|
|
25
25
|
}
|
|
26
26
|
}, [selectedFilter])
|
|
27
27
|
|
|
28
|
-
return { isMobile,
|
|
28
|
+
return { isMobile, tabsRef }
|
|
29
29
|
}
|
|
@@ -9,7 +9,7 @@ import React, {
|
|
|
9
9
|
import { DateRange } from 'react-day-picker'
|
|
10
10
|
|
|
11
11
|
import { AgeCategoryCount, Location, FilterBarTypes } from '../FilterBarTypes'
|
|
12
|
-
import { useFilterActions, useFilterState,
|
|
12
|
+
import { useFilterActions, useFilterState, useFilterRefs } from '../hooks'
|
|
13
13
|
|
|
14
14
|
type FilterBarProviderProps = PropsWithChildren<FilterBarTypes>
|
|
15
15
|
|
|
@@ -38,10 +38,11 @@ type FilterBarContextType = FilterBarTypes & {
|
|
|
38
38
|
previouslyFocusedButtonRef: MutableRefObject<HTMLButtonElement | null>
|
|
39
39
|
panelRef: MutableRefObject<HTMLDivElement | null>
|
|
40
40
|
buttonRefs: MutableRefObject<Record<string, HTMLButtonElement | null>>
|
|
41
|
+
filtersRef: MutableRefObject<HTMLDivElement | null>
|
|
41
42
|
|
|
42
43
|
// Mobile
|
|
43
44
|
isMobile: boolean
|
|
44
|
-
|
|
45
|
+
tabsRef: MutableRefObject<HTMLDivElement | null>
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
const FilterBarContext = createContext<FilterBarContextType | undefined>(
|
|
@@ -95,7 +96,7 @@ export const FilterBarProvider = ({
|
|
|
95
96
|
setInnerLoading,
|
|
96
97
|
})
|
|
97
98
|
|
|
98
|
-
const
|
|
99
|
+
const filterRefs = useFilterRefs(selectedFilter)
|
|
99
100
|
|
|
100
101
|
const contextValue = useMemo(
|
|
101
102
|
() => ({
|
|
@@ -128,7 +129,7 @@ export const FilterBarProvider = ({
|
|
|
128
129
|
outerLoading,
|
|
129
130
|
locations,
|
|
130
131
|
|
|
131
|
-
...
|
|
132
|
+
...filterRefs,
|
|
132
133
|
}),
|
|
133
134
|
[
|
|
134
135
|
selectedFilter,
|
|
@@ -149,8 +150,9 @@ export const FilterBarProvider = ({
|
|
|
149
150
|
tabs,
|
|
150
151
|
outerLoading,
|
|
151
152
|
locations,
|
|
152
|
-
|
|
153
|
-
|
|
153
|
+
filterRefs.isMobile,
|
|
154
|
+
filterRefs.tabsRef,
|
|
155
|
+
filterRefs.filtersRef,
|
|
154
156
|
]
|
|
155
157
|
)
|
|
156
158
|
|
|
@@ -62,14 +62,14 @@ export default function FilterCalendar({
|
|
|
62
62
|
useAwaitRender()
|
|
63
63
|
|
|
64
64
|
// Handle close filter section
|
|
65
|
-
const {
|
|
65
|
+
const { filterSectionRef } = useCloseFilterSection({
|
|
66
66
|
handleSelectedFilter: setToggleCalendar,
|
|
67
67
|
})
|
|
68
68
|
|
|
69
69
|
return (
|
|
70
70
|
<div className={`will-root`} style={themePalette}>
|
|
71
71
|
{toggleCalendar && (
|
|
72
|
-
<div className={`will-calendar-wrapper`} ref={
|
|
72
|
+
<div className={`will-calendar-wrapper`} ref={filterSectionRef}>
|
|
73
73
|
<div className={`will-calendar-header`}>
|
|
74
74
|
<h2>{t('filterBar:calendar.title')}</h2>
|
|
75
75
|
<CloseButton handleClose={() => setToggleCalendar(false)} />
|
|
@@ -6,13 +6,13 @@ type Props = {
|
|
|
6
6
|
|
|
7
7
|
// TODO - Refactor and rename this hook
|
|
8
8
|
export const useCloseFilterSection = ({ handleSelectedFilter }: Props) => {
|
|
9
|
-
const
|
|
9
|
+
const filterSectionRef = useRef<HTMLDivElement | null>(null)
|
|
10
10
|
|
|
11
11
|
useEffect(() => {
|
|
12
12
|
const handleClickOutside = (event: MouseEvent) => {
|
|
13
13
|
if (
|
|
14
|
-
|
|
15
|
-
!
|
|
14
|
+
filterSectionRef.current &&
|
|
15
|
+
!filterSectionRef.current.contains(event.target as Node)
|
|
16
16
|
) {
|
|
17
17
|
handleSelectedFilter(false)
|
|
18
18
|
}
|
|
@@ -23,7 +23,7 @@ export const useCloseFilterSection = ({ handleSelectedFilter }: Props) => {
|
|
|
23
23
|
return () => {
|
|
24
24
|
document.removeEventListener('mousedown', handleClickOutside)
|
|
25
25
|
}
|
|
26
|
-
}, [
|
|
26
|
+
}, [filterSectionRef])
|
|
27
27
|
|
|
28
|
-
return {
|
|
28
|
+
return { filterSectionRef }
|
|
29
29
|
}
|