@westpac/ui 0.50.0 → 0.50.3
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/CHANGELOG.md +18 -0
- package/dist/component-type.json +1 -1
- package/dist/components/accordion/components/accordion-item/accordion-item.component.js +43 -24
- package/dist/components/accordion/components/accordion-item/accordion-item.styles.d.ts +18 -6
- package/dist/components/accordion/components/accordion-item/accordion-item.styles.js +10 -6
- package/dist/components/autocomplete/autocomplete.component.d.ts +1 -1
- package/dist/components/autocomplete/autocomplete.component.js +3 -2
- package/dist/components/autocomplete/autocomplete.types.d.ts +6 -0
- package/dist/components/footer/footer.component.js +4 -11
- package/dist/components/footer/footer.styles.d.ts +3 -21
- package/dist/components/footer/footer.styles.js +3 -9
- package/dist/css/westpac-ui.css +15 -36
- package/dist/css/westpac-ui.min.css +15 -36
- package/dist/hook/breakpoints.hook.js +59 -10
- package/dist/hook/index.d.ts +1 -0
- package/dist/hook/index.js +1 -0
- package/package.json +3 -2
- package/src/components/accordion/components/accordion-item/accordion-item.component.tsx +47 -29
- package/src/components/accordion/components/accordion-item/accordion-item.styles.ts +10 -6
- package/src/components/autocomplete/autocomplete.component.tsx +3 -1
- package/src/components/autocomplete/autocomplete.types.ts +6 -0
- package/src/components/footer/footer.component.tsx +8 -12
- package/src/components/footer/footer.styles.ts +2 -6
- package/src/hook/breakpoints.hook.ts +71 -11
- package/src/hook/index.ts +1 -0
|
@@ -1,21 +1,70 @@
|
|
|
1
|
-
import { useEffect
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { create } from 'zustand';
|
|
2
3
|
import { BREAKPOINTS } from '../tailwind/constants/index.js';
|
|
3
4
|
function checkBreakpoint() {
|
|
5
|
+
if (typeof window === 'undefined') {
|
|
6
|
+
return 'initial';
|
|
7
|
+
}
|
|
4
8
|
const breakpointsAsArray = Object.entries(BREAKPOINTS).reverse();
|
|
5
9
|
const breakpoint = breakpointsAsArray.find(([, value])=>window.matchMedia(`(min-width: ${value})`).matches);
|
|
6
10
|
return breakpoint ? breakpoint[0] : 'initial';
|
|
7
11
|
}
|
|
12
|
+
const BREAKPOINTS_ENTRIES = Object.entries(BREAKPOINTS);
|
|
13
|
+
const BREAKPOINTS_MEDIA = BREAKPOINTS_ENTRIES.reduce((acc, [key, value], index)=>{
|
|
14
|
+
const finalValue = (()=>{
|
|
15
|
+
const nextBreakpoint = BREAKPOINTS_ENTRIES[index + 1];
|
|
16
|
+
if (nextBreakpoint) {
|
|
17
|
+
return `(min-width: ${value}) and (max-width: ${+nextBreakpoint[1].replace('px', '') - 1}px)`;
|
|
18
|
+
}
|
|
19
|
+
return `(min-width: ${value})`;
|
|
20
|
+
})();
|
|
21
|
+
return {
|
|
22
|
+
...acc,
|
|
23
|
+
[key]: finalValue
|
|
24
|
+
};
|
|
25
|
+
}, {
|
|
26
|
+
initial: `(max-width: ${+BREAKPOINTS_ENTRIES[0][1].replace('px', '') - 1}px)`
|
|
27
|
+
});
|
|
28
|
+
const useBreakpointStore = create()((set, get)=>({
|
|
29
|
+
breakpoint: 'initial',
|
|
30
|
+
mediaQueryListeners: null,
|
|
31
|
+
initialised: false,
|
|
32
|
+
ensureInitialized: ()=>{
|
|
33
|
+
if (get().initialised) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const listeners = Object.entries(BREAKPOINTS_MEDIA).map(([label, query])=>{
|
|
37
|
+
const mq = window.matchMedia(query);
|
|
38
|
+
const listener = (e)=>{
|
|
39
|
+
if (e.matches) {
|
|
40
|
+
set({
|
|
41
|
+
breakpoint: label
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
mq.addEventListener('change', listener);
|
|
46
|
+
return {
|
|
47
|
+
mq,
|
|
48
|
+
listener
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
set({
|
|
52
|
+
mediaQueryListeners: listeners,
|
|
53
|
+
initialised: true,
|
|
54
|
+
breakpoint: checkBreakpoint()
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
removeListeners: ()=>{
|
|
58
|
+
var _get_mediaQueryListeners;
|
|
59
|
+
(_get_mediaQueryListeners = get().mediaQueryListeners) === null || _get_mediaQueryListeners === void 0 ? void 0 : _get_mediaQueryListeners.forEach(({ mq, listener })=>{
|
|
60
|
+
mq.removeEventListener('change', listener);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}));
|
|
8
64
|
export function useBreakpoint() {
|
|
9
|
-
const
|
|
65
|
+
const { breakpoint, ensureInitialized: initIfNotInitialised } = useBreakpointStore();
|
|
10
66
|
useEffect(()=>{
|
|
11
|
-
|
|
12
|
-
const breakpoint = checkBreakpoint();
|
|
13
|
-
setBreakpoint(breakpoint);
|
|
14
|
-
};
|
|
15
|
-
window.addEventListener('resize', listener);
|
|
16
|
-
return ()=>{
|
|
17
|
-
window.removeEventListener('resize', listener);
|
|
18
|
-
};
|
|
67
|
+
initIfNotInitialised();
|
|
19
68
|
}, []);
|
|
20
69
|
return breakpoint;
|
|
21
70
|
}
|
package/dist/hook/index.d.ts
CHANGED
package/dist/hook/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@westpac/ui",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"type": "module",
|
|
@@ -270,7 +270,8 @@
|
|
|
270
270
|
"lodash.throttle": "~4.1.1",
|
|
271
271
|
"motion": "~12.23.12",
|
|
272
272
|
"react-aria": "~3.41.1",
|
|
273
|
-
"react-stately": "~3.39.0"
|
|
273
|
+
"react-stately": "~3.39.0",
|
|
274
|
+
"zustand": "~4.5.4"
|
|
274
275
|
},
|
|
275
276
|
"overrides": {
|
|
276
277
|
"react-aria": {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* eslint-disable sonarjs/deprecation */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
3
3
|
import { useAccordionItem } from '@react-aria/accordion';
|
|
4
|
-
import {
|
|
5
|
-
import React, { useRef } from 'react';
|
|
4
|
+
import { LazyMotion, m, useAnimate } from 'motion/react';
|
|
5
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
6
6
|
import { mergeProps, useFocusRing, useHover, useLocale } from 'react-aria';
|
|
7
7
|
|
|
8
8
|
import { ArrowLeftIcon, ArrowRightIcon } from '../../../icon/index.js';
|
|
@@ -23,11 +23,44 @@ export function AccordionItem<T = HTMLElement>({
|
|
|
23
23
|
const { state, item } = props;
|
|
24
24
|
const { buttonProps, regionProps } = useAccordionItem<T>(props, state, ref);
|
|
25
25
|
const { isFocusVisible, focusProps } = useFocusRing();
|
|
26
|
-
const
|
|
26
|
+
const isOpenState = state.expandedKeys.has(item.key);
|
|
27
27
|
const isDisabled = state.disabledKeys.has(item.key);
|
|
28
28
|
const { hoverProps } = useHover({ isDisabled });
|
|
29
29
|
const { direction } = useLocale();
|
|
30
|
-
|
|
30
|
+
// Open/close animation needs to be done with useAnimate as AnimatePresence will unmount component
|
|
31
|
+
const [scope, animate] = useAnimate();
|
|
32
|
+
const [enableOpenStyle, setEnableOpenStyle] = useState(false);
|
|
33
|
+
|
|
34
|
+
const styles = accordionItemStyles({
|
|
35
|
+
isOpen: enableOpenStyle,
|
|
36
|
+
isOpenState,
|
|
37
|
+
isDisabled,
|
|
38
|
+
look,
|
|
39
|
+
isFocusVisible,
|
|
40
|
+
rounded,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
// setEnableStyle here as opening animation isn't working correctly if done below
|
|
45
|
+
if (isOpenState) setEnableOpenStyle(true);
|
|
46
|
+
|
|
47
|
+
if (enableOpenStyle) {
|
|
48
|
+
animate(scope.current, { height: 'auto' }, { duration: 0.3, ease: [0.25, 0.1, 0.25, 1.0] });
|
|
49
|
+
}
|
|
50
|
+
if (!isOpenState) {
|
|
51
|
+
animate(
|
|
52
|
+
scope.current,
|
|
53
|
+
{ height: '0px' },
|
|
54
|
+
{
|
|
55
|
+
duration: 0.3,
|
|
56
|
+
ease: [0.25, 0.1, 0.25, 1.0],
|
|
57
|
+
// set some styles after animation completes so content doesn't disappear on close
|
|
58
|
+
onComplete: () => setEnableOpenStyle(false),
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
63
|
+
}, [isOpenState, enableOpenStyle]);
|
|
31
64
|
|
|
32
65
|
return (
|
|
33
66
|
<Tag className={styles.base({ className })}>
|
|
@@ -47,33 +80,18 @@ export function AccordionItem<T = HTMLElement>({
|
|
|
47
80
|
</h3>
|
|
48
81
|
<div {...regionProps}>
|
|
49
82
|
<LazyMotion features={loadAnimations}>
|
|
50
|
-
<
|
|
51
|
-
<
|
|
52
|
-
className=
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
height: 'auto',
|
|
58
|
-
}}
|
|
59
|
-
exit={{
|
|
60
|
-
height: 0,
|
|
83
|
+
<m.div className="overflow-hidden" initial={{ height: isOpenState ? 'auto' : 0 }} ref={scope}>
|
|
84
|
+
<div
|
|
85
|
+
className={styles.content()}
|
|
86
|
+
// TODO: Remove below with updated accordion that uses disclosure as the issue doesn't happen with that version
|
|
87
|
+
// Need to call stopPropagation here as some events from children are bubbling up and focusing the accordion i.e. inputs
|
|
88
|
+
onBlur={e => {
|
|
89
|
+
e.stopPropagation();
|
|
61
90
|
}}
|
|
62
|
-
transition={{ duration: 0.3, ease: [0.25, 0.1, 0.25, 1.0] }}
|
|
63
|
-
key={`${item.index}-${isOpen}`}
|
|
64
91
|
>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// Need to call stopPropagation here as some events from children are bubbling up and focusing the accordion i.e. inputs
|
|
69
|
-
onBlur={e => {
|
|
70
|
-
e.stopPropagation();
|
|
71
|
-
}}
|
|
72
|
-
>
|
|
73
|
-
{item.props.children}
|
|
74
|
-
</div>
|
|
75
|
-
</m.div>
|
|
76
|
-
</AnimatePresence>
|
|
92
|
+
{item.props.children}
|
|
93
|
+
</div>
|
|
94
|
+
</m.div>
|
|
77
95
|
</LazyMotion>
|
|
78
96
|
</div>
|
|
79
97
|
</Tag>
|
|
@@ -7,7 +7,7 @@ export const styles = tv(
|
|
|
7
7
|
itemHeader: 'typography-body-9 flex w-full flex-1 items-center justify-between px-3 py-2 group-first:border-t-0',
|
|
8
8
|
headerTitleWrapper: 'flex-1 pr-2 text-left',
|
|
9
9
|
indicator: 'size-3 rotate-90',
|
|
10
|
-
content: '
|
|
10
|
+
content: '',
|
|
11
11
|
},
|
|
12
12
|
variants: {
|
|
13
13
|
look: {
|
|
@@ -19,13 +19,17 @@ export const styles = tv(
|
|
|
19
19
|
'mb-[-1px] border-l-[0.375rem] border-r border-border bg-light shadow-[inset_0px_1px_0_0_var(--tw-shadow-color),inset_0_-1px_0_0_var(--tw-shadow-color)] !shadow-border transition-colors',
|
|
20
20
|
},
|
|
21
21
|
},
|
|
22
|
+
isOpenState: {
|
|
23
|
+
false: {
|
|
24
|
+
itemHeader: 'background-transition hover:bg-background',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
22
27
|
isOpen: {
|
|
23
28
|
true: {
|
|
24
|
-
content: 'block border-border p-3',
|
|
29
|
+
content: 'visible block border-border p-3',
|
|
25
30
|
},
|
|
26
31
|
false: {
|
|
27
|
-
|
|
28
|
-
itemHeader: 'background-transition hover:bg-background',
|
|
32
|
+
content: 'hidden',
|
|
29
33
|
},
|
|
30
34
|
},
|
|
31
35
|
isDisabled: {
|
|
@@ -45,13 +49,13 @@ export const styles = tv(
|
|
|
45
49
|
compoundSlots: [
|
|
46
50
|
{
|
|
47
51
|
slots: ['indicator'],
|
|
48
|
-
|
|
52
|
+
isOpenState: true,
|
|
49
53
|
className: '-rotate-90',
|
|
50
54
|
},
|
|
51
55
|
{
|
|
52
56
|
slots: ['itemHeader'],
|
|
53
57
|
look: 'lego',
|
|
54
|
-
|
|
58
|
+
isOpenState: true,
|
|
55
59
|
className: 'border-l-hero',
|
|
56
60
|
},
|
|
57
61
|
{
|
|
@@ -41,13 +41,15 @@ function Autocomplete<T extends object>(
|
|
|
41
41
|
className,
|
|
42
42
|
width = 'full',
|
|
43
43
|
loadingState,
|
|
44
|
+
comboBoxState,
|
|
44
45
|
...props
|
|
45
46
|
}: AutocompleteProps<T>,
|
|
46
47
|
ref: ForwardedRef<HTMLInputElement>,
|
|
47
48
|
) {
|
|
48
49
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
49
50
|
const { contains } = useFilter({ sensitivity: 'base' });
|
|
50
|
-
const
|
|
51
|
+
const internalState = useComboBoxState({ isDisabled, ...props, defaultFilter: contains });
|
|
52
|
+
const state = comboBoxState ?? internalState;
|
|
51
53
|
const { isFocusVisible, focusProps } = useFocusRing();
|
|
52
54
|
const { isFocusVisible: isInputFocusVisible, focusProps: inputFocusProps } = useFocusRing();
|
|
53
55
|
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ComboBoxProps } from '@react-types/combobox';
|
|
2
2
|
import { type AriaLabelingProps } from '@react-types/shared';
|
|
3
3
|
import { type ReactNode } from 'react';
|
|
4
|
+
import { ComboBoxState } from 'react-stately';
|
|
4
5
|
import { type VariantProps } from 'tailwind-variants';
|
|
5
6
|
|
|
6
7
|
import { HintProps, InputProps } from '../index.js';
|
|
@@ -63,5 +64,10 @@ export type AutocompleteProps<T extends object> = {
|
|
|
63
64
|
* Width of autocomplete
|
|
64
65
|
*/
|
|
65
66
|
width?: InputProps['width'];
|
|
67
|
+
/**
|
|
68
|
+
* Pass through comboBox state from consuming component. If not specified,
|
|
69
|
+
* will be handled internally.
|
|
70
|
+
*/
|
|
71
|
+
comboBoxState?: ComboBoxState<T>;
|
|
66
72
|
} & ComboBoxProps<T> &
|
|
67
73
|
AriaLabelingProps;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { useFocusRing } from 'react-aria';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { VisuallyHidden } from '../index.js';
|
|
7
7
|
import {
|
|
8
8
|
BOMMultibrandSmallLogo,
|
|
9
9
|
BSAMultibrandSmallLogo,
|
|
@@ -54,18 +54,14 @@ export function Footer({
|
|
|
54
54
|
return (
|
|
55
55
|
<footer className={styles.base({ className })} {...props}>
|
|
56
56
|
<div className={styles.wrapper()}>
|
|
57
|
-
<
|
|
58
|
-
<GridItem span={12}>{children}</GridItem>
|
|
59
|
-
</Grid>
|
|
57
|
+
<div>{children}</div>
|
|
60
58
|
{!hideLogo && (
|
|
61
|
-
<
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
</GridItem>
|
|
68
|
-
</Grid>
|
|
59
|
+
<div className={styles.logoWrapper()}>
|
|
60
|
+
<a href={logoLink} className={styles.link()} {...focusProps}>
|
|
61
|
+
{srOnlyText && <VisuallyHidden>{srOnlyText}</VisuallyHidden>}
|
|
62
|
+
<Logo align="right" aria-label={logoAssistiveText} />
|
|
63
|
+
</a>
|
|
64
|
+
</div>
|
|
69
65
|
)}
|
|
70
66
|
</div>
|
|
71
67
|
</footer>
|
|
@@ -4,9 +4,9 @@ export const styles = tv(
|
|
|
4
4
|
{
|
|
5
5
|
slots: {
|
|
6
6
|
base: 'relative overflow-hidden border-t border-t-border',
|
|
7
|
-
wrapper: 'pt-3 max-md:px-2 max-md:pb-3 md:px-4 md:pb-
|
|
8
|
-
topRow: '',
|
|
7
|
+
wrapper: 'pt-3 max-md:px-2 max-md:pb-3 md:px-4 md:pb-3',
|
|
9
8
|
link: 'float-right block',
|
|
9
|
+
logoWrapper: 'flex justify-end',
|
|
10
10
|
},
|
|
11
11
|
variants: {
|
|
12
12
|
offsetSidebar: {
|
|
@@ -18,10 +18,6 @@ export const styles = tv(
|
|
|
18
18
|
isFocusVisible: {
|
|
19
19
|
true: { link: 'focus-outline' },
|
|
20
20
|
},
|
|
21
|
-
hideLogo: {
|
|
22
|
-
true: '',
|
|
23
|
-
false: { topRow: 'max-md:mb-7 md:mb-4' },
|
|
24
|
-
},
|
|
25
21
|
},
|
|
26
22
|
},
|
|
27
23
|
{ responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { useEffect
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { create } from 'zustand';
|
|
2
3
|
|
|
3
4
|
import { BREAKPOINTS, Breakpoint } from '../tailwind/constants/index.js';
|
|
4
5
|
|
|
5
6
|
function checkBreakpoint(): Breakpoint | 'initial' {
|
|
7
|
+
if (typeof window === 'undefined') {
|
|
8
|
+
return 'initial';
|
|
9
|
+
}
|
|
6
10
|
const breakpointsAsArray = Object.entries(BREAKPOINTS).reverse() as [Breakpoint, string][];
|
|
7
11
|
const breakpoint = breakpointsAsArray.find(([, value]) => window.matchMedia(`(min-width: ${value})`).matches) as [
|
|
8
12
|
Breakpoint,
|
|
@@ -11,18 +15,74 @@ function checkBreakpoint(): Breakpoint | 'initial' {
|
|
|
11
15
|
return breakpoint ? breakpoint[0] : 'initial';
|
|
12
16
|
}
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
const BREAKPOINTS_ENTRIES = Object.entries(BREAKPOINTS);
|
|
19
|
+
const BREAKPOINTS_MEDIA: Record<Breakpoint | 'initial', string> = BREAKPOINTS_ENTRIES.reduce(
|
|
20
|
+
(acc, [key, value], index) => {
|
|
21
|
+
const finalValue = (() => {
|
|
22
|
+
const nextBreakpoint = BREAKPOINTS_ENTRIES[index + 1];
|
|
23
|
+
if (nextBreakpoint) {
|
|
24
|
+
return `(min-width: ${value}) and (max-width: ${+nextBreakpoint[1].replace('px', '') - 1}px)`;
|
|
25
|
+
}
|
|
26
|
+
return `(min-width: ${value})`;
|
|
27
|
+
})();
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
setBreakpoint(breakpoint);
|
|
21
|
-
};
|
|
22
|
-
window.addEventListener('resize', listener);
|
|
23
|
-
return () => {
|
|
24
|
-
window.removeEventListener('resize', listener);
|
|
29
|
+
return {
|
|
30
|
+
...acc,
|
|
31
|
+
[key]: finalValue,
|
|
25
32
|
};
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
initial: `(max-width: ${+BREAKPOINTS_ENTRIES[0][1].replace('px', '') - 1}px)`,
|
|
36
|
+
} as Record<Breakpoint | 'initial', string>,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
type BreakpointState = {
|
|
40
|
+
breakpoint: Breakpoint | 'initial';
|
|
41
|
+
mediaQueryListeners:
|
|
42
|
+
| {
|
|
43
|
+
mq: MediaQueryList;
|
|
44
|
+
listener: (e: MediaQueryListEvent) => void;
|
|
45
|
+
}[]
|
|
46
|
+
| null;
|
|
47
|
+
initialised: boolean;
|
|
48
|
+
ensureInitialized: () => void;
|
|
49
|
+
removeListeners: () => void;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const useBreakpointStore = create<BreakpointState>()((set, get) => ({
|
|
53
|
+
breakpoint: 'initial',
|
|
54
|
+
mediaQueryListeners: null,
|
|
55
|
+
initialised: false,
|
|
56
|
+
ensureInitialized: () => {
|
|
57
|
+
if (get().initialised) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const listeners = Object.entries(BREAKPOINTS_MEDIA).map(([label, query]) => {
|
|
61
|
+
const mq = window.matchMedia(query);
|
|
62
|
+
const listener = (e: MediaQueryListEvent) => {
|
|
63
|
+
if (e.matches) {
|
|
64
|
+
set({ breakpoint: label as Breakpoint });
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
mq.addEventListener('change', listener);
|
|
68
|
+
return {
|
|
69
|
+
mq,
|
|
70
|
+
listener,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
set({ mediaQueryListeners: listeners, initialised: true, breakpoint: checkBreakpoint() });
|
|
74
|
+
},
|
|
75
|
+
removeListeners: () => {
|
|
76
|
+
get().mediaQueryListeners?.forEach(({ mq, listener }) => {
|
|
77
|
+
mq.removeEventListener('change', listener);
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
}));
|
|
81
|
+
|
|
82
|
+
export function useBreakpoint() {
|
|
83
|
+
const { breakpoint, ensureInitialized: initIfNotInitialised } = useBreakpointStore();
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
initIfNotInitialised();
|
|
26
86
|
}, []);
|
|
27
87
|
|
|
28
88
|
return breakpoint;
|
package/src/hook/index.ts
CHANGED