@limonify/zest-ui 0.1.1 → 0.1.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/lib/accordion/index.d.ts +1 -0
- package/lib/accordion/index.d.ts.map +1 -1
- package/lib/accordion/index.js +3 -1
- package/lib/accordion/index.js.map +1 -1
- package/lib/accordion/panel/AccordionPanel.d.ts +19 -1
- package/lib/accordion/panel/AccordionPanel.d.ts.map +1 -1
- package/lib/accordion/panel/AccordionPanel.js +18 -2
- package/lib/accordion/panel/AccordionPanel.js.map +1 -1
- package/lib/collapsible/index.d.ts +1 -0
- package/lib/collapsible/index.d.ts.map +1 -1
- package/lib/collapsible/index.js +3 -1
- package/lib/collapsible/index.js.map +1 -1
- package/lib/collapsible/panel/CollapsiblePanel.d.ts +19 -1
- package/lib/collapsible/panel/CollapsiblePanel.d.ts.map +1 -1
- package/lib/collapsible/panel/CollapsiblePanel.js +18 -2
- package/lib/collapsible/panel/CollapsiblePanel.js.map +1 -1
- package/lib/collapsible/panel/CollapsiblePanelContext.d.ts +21 -0
- package/lib/collapsible/panel/CollapsiblePanelContext.d.ts.map +1 -0
- package/lib/collapsible/panel/CollapsiblePanelContext.js +48 -0
- package/lib/collapsible/panel/CollapsiblePanelContext.js.map +1 -0
- package/lib/collapsible/panel/useCollapsiblePanel.d.ts +17 -0
- package/lib/collapsible/panel/useCollapsiblePanel.d.ts.map +1 -1
- package/lib/collapsible/panel/useCollapsiblePanel.js +4 -2
- package/lib/collapsible/panel/useCollapsiblePanel.js.map +1 -1
- package/lib/combobox/portal/ComboboxPortal.js +1 -1
- package/lib/dialog/portal/DialogPortal.d.ts +4 -2
- package/lib/dialog/portal/DialogPortal.d.ts.map +1 -1
- package/lib/dialog/portal/DialogPortal.js +5 -3
- package/lib/dialog/portal/DialogPortal.js.map +1 -1
- package/lib/menu/portal/MenuPortal.js +1 -1
- package/lib/popover/portal/PopoverPortal.js +1 -1
- package/lib/select/portal/SelectPortal.js +1 -1
- package/lib/switch/thumb/SwitchThumb.d.ts +19 -2
- package/lib/switch/thumb/SwitchThumb.d.ts.map +1 -1
- package/lib/switch/thumb/SwitchThumb.js +21 -3
- package/lib/switch/thumb/SwitchThumb.js.map +1 -1
- package/lib/toast/root/ToastRoot.d.ts +20 -8
- package/lib/toast/root/ToastRoot.d.ts.map +1 -1
- package/lib/toast/root/ToastRoot.js +28 -8
- package/lib/toast/root/ToastRoot.js.map +1 -1
- package/lib/toast/store.js +1 -1
- package/lib/toast/store.js.map +1 -1
- package/lib/toast/useToastManager.d.ts +7 -0
- package/lib/toast/useToastManager.d.ts.map +1 -1
- package/lib/tooltip/portal/TooltipPortal.js +1 -1
- package/package.json +1 -1
- package/src/accordion/index.ts +1 -0
- package/src/accordion/panel/AccordionPanel.tsx +40 -2
- package/src/collapsible/index.ts +1 -0
- package/src/collapsible/panel/CollapsiblePanel.tsx +40 -2
- package/src/collapsible/panel/CollapsiblePanelContext.ts +34 -0
- package/src/collapsible/panel/useCollapsiblePanel.tsx +27 -3
- package/src/combobox/portal/ComboboxPortal.tsx +1 -1
- package/src/dialog/portal/DialogPortal.tsx +5 -3
- package/src/menu/portal/MenuPortal.tsx +1 -1
- package/src/popover/portal/PopoverPortal.tsx +1 -1
- package/src/select/portal/SelectPortal.tsx +1 -1
- package/src/switch/thumb/SwitchThumb.tsx +40 -5
- package/src/toast/root/ToastRoot.tsx +37 -9
- package/src/toast/store.ts +1 -1
- package/src/toast/useToastManager.ts +7 -0
- package/src/tooltip/portal/TooltipPortal.tsx +1 -1
|
@@ -33,7 +33,7 @@ export function PopoverPortal(props: PopoverPortal.Props) {
|
|
|
33
33
|
<Modal
|
|
34
34
|
transparent
|
|
35
35
|
visible={open}
|
|
36
|
-
animationType="
|
|
36
|
+
animationType="fade"
|
|
37
37
|
statusBarTranslucent
|
|
38
38
|
navigationBarTranslucent
|
|
39
39
|
onRequestClose={(event: NativeSyntheticEvent<unknown>) => {
|
|
@@ -27,7 +27,7 @@ export function SelectPortal(props: SelectPortal.Props) {
|
|
|
27
27
|
<Modal
|
|
28
28
|
transparent
|
|
29
29
|
visible={open}
|
|
30
|
-
animationType="
|
|
30
|
+
animationType="fade"
|
|
31
31
|
statusBarTranslucent
|
|
32
32
|
navigationBarTranslucent
|
|
33
33
|
onRequestClose={(event: NativeSyntheticEvent<unknown>) => {
|
|
@@ -2,29 +2,64 @@
|
|
|
2
2
|
import { View } from 'react-native';
|
|
3
3
|
import { useSwitchRootContext } from '../root/SwitchRootContext';
|
|
4
4
|
import { useRenderElement } from '../../use-render/useRenderElement';
|
|
5
|
+
import { useIsoLayoutEffect } from '../../hooks/useIsoLayoutEffect';
|
|
6
|
+
import { useTransitionStatus, type TransitionStatus } from '../../internals/useTransitionStatus';
|
|
5
7
|
import type { SwitchRootState } from '../root/SwitchRoot';
|
|
6
8
|
import type { ZestUIComponentProps } from '../../types';
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* The movable part of the switch that indicates whether the switch is on or off.
|
|
10
|
-
* Renders a `<View
|
|
12
|
+
* Renders a `<View>`, or nothing when the switch is unchecked.
|
|
13
|
+
*
|
|
14
|
+
* Follows the animation contract: with `keepMounted`, the thumb stays mounted
|
|
15
|
+
* while unchecked and publishes `transitionStatus` so the consumer can animate
|
|
16
|
+
* it out. Without it, the thumb unmounts the moment it unchecks — React Native
|
|
17
|
+
* cannot report when an exit animation finished.
|
|
11
18
|
*/
|
|
12
19
|
export function SwitchThumb(componentProps: SwitchThumb.Props) {
|
|
13
|
-
const { render, className, style, ref, ...elementProps } = componentProps;
|
|
20
|
+
const { render, className, style, keepMounted = false, ref, ...elementProps } = componentProps;
|
|
14
21
|
|
|
15
|
-
const
|
|
22
|
+
const rootState = useSwitchRootContext();
|
|
23
|
+
|
|
24
|
+
const { mounted, transitionStatus, setMounted } = useTransitionStatus(rootState.checked);
|
|
25
|
+
|
|
26
|
+
// Nothing reports when an exit animation finished, so a thumb that isn't
|
|
27
|
+
// kept mounted is unmounted the moment it unchecks.
|
|
28
|
+
useIsoLayoutEffect(() => {
|
|
29
|
+
if (!rootState.checked && mounted && !keepMounted) {
|
|
30
|
+
setMounted(false);
|
|
31
|
+
}
|
|
32
|
+
}, [rootState.checked, mounted, keepMounted, setMounted]);
|
|
33
|
+
|
|
34
|
+
const state: SwitchThumbState = { ...rootState, transitionStatus };
|
|
35
|
+
|
|
36
|
+
const shouldRender = keepMounted || mounted;
|
|
16
37
|
|
|
17
38
|
return useRenderElement(View, componentProps, {
|
|
18
39
|
state,
|
|
19
40
|
ref,
|
|
41
|
+
enabled: shouldRender,
|
|
20
42
|
props: elementProps,
|
|
21
43
|
});
|
|
22
44
|
}
|
|
23
45
|
|
|
24
|
-
export interface SwitchThumbState extends SwitchRootState {
|
|
46
|
+
export interface SwitchThumbState extends SwitchRootState {
|
|
47
|
+
/**
|
|
48
|
+
* The transition status of the thumb, for driving enter/exit animations.
|
|
49
|
+
*/
|
|
50
|
+
transitionStatus: TransitionStatus;
|
|
51
|
+
}
|
|
25
52
|
|
|
26
53
|
export interface SwitchThumbProps
|
|
27
|
-
extends ZestUIComponentProps<typeof View, SwitchThumbState> {
|
|
54
|
+
extends ZestUIComponentProps<typeof View, SwitchThumbState> {
|
|
55
|
+
/**
|
|
56
|
+
* Whether to keep the element mounted when the switch is unchecked.
|
|
57
|
+
*
|
|
58
|
+
* Required to animate the thumb out; without it, it unmounts on uncheck.
|
|
59
|
+
* @default false
|
|
60
|
+
*/
|
|
61
|
+
keepMounted?: boolean | undefined;
|
|
62
|
+
}
|
|
28
63
|
|
|
29
64
|
export namespace SwitchThumb {
|
|
30
65
|
export type State = SwitchThumbState;
|
|
@@ -6,6 +6,7 @@ import { useToastProviderContext } from '../provider/ToastProviderContext';
|
|
|
6
6
|
import { useRenderElement } from '../../use-render/useRenderElement';
|
|
7
7
|
import { useIsoLayoutEffect } from '../../hooks/useIsoLayoutEffect';
|
|
8
8
|
import { useStableCallback } from '../../hooks/useStableCallback';
|
|
9
|
+
import { AnimationFrame } from '../../hooks/useAnimationFrame';
|
|
9
10
|
import type { ZestUIComponentProps } from '../../types';
|
|
10
11
|
import type { ToastObject } from '../useToastManager';
|
|
11
12
|
import { ToastRootContext } from './ToastRootContext';
|
|
@@ -23,12 +24,17 @@ export type ToastSwipeDirection = 'up' | 'down' | 'left' | 'right';
|
|
|
23
24
|
* `transitionStatus`, its stacking `offsetY`/`index`, and how far a swipe has
|
|
24
25
|
* travelled, and the consumer turns those into a transform.
|
|
25
26
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* `
|
|
31
|
-
*
|
|
27
|
+
* **Enter:** `transitionStatus` starts as `'starting'` (auto-clears to
|
|
28
|
+
* `undefined` after one frame). Key off `state.transitionStatus === 'starting'`
|
|
29
|
+
* to trigger a slide-in or fade-in animation.
|
|
30
|
+
*
|
|
31
|
+
* **Exit:** `removeOnClose` (on by default) drops the toast the moment it
|
|
32
|
+
* closes — the right default for a consumer who does not animate. To animate
|
|
33
|
+
* the exit, set `removeOnClose={false}`, drive the exit from
|
|
34
|
+
* `state.transitionStatus === 'ending'` using `state.measuredHeight` as the
|
|
35
|
+
* starting value (the animation target is `0`), and call
|
|
36
|
+
* `useToastManager().remove(id)` when the animation finishes.
|
|
37
|
+
* This is the same lever as `Collapsible.Panel`'s `keepMounted`.
|
|
32
38
|
*/
|
|
33
39
|
export function ToastRoot(componentProps: ToastRoot.Props) {
|
|
34
40
|
const {
|
|
@@ -64,13 +70,26 @@ export function ToastRoot(componentProps: ToastRoot.Props) {
|
|
|
64
70
|
}
|
|
65
71
|
}, [removeOnClose, toast.transitionStatus, toast.id, store]);
|
|
66
72
|
|
|
73
|
+
// `transitionStatus` starts as `'starting'` and stays that way forever.
|
|
74
|
+
// Auto-clear it after one frame so the consumer can react to `'starting'`
|
|
75
|
+
// for an enter animation without repeatedly re-triggering.
|
|
76
|
+
useIsoLayoutEffect(() => {
|
|
77
|
+
if (toast.transitionStatus === 'starting') {
|
|
78
|
+
const frame = AnimationFrame.request(() => {
|
|
79
|
+
store.updateToastInternal(toast.id, { transitionStatus: undefined });
|
|
80
|
+
});
|
|
81
|
+
return () => AnimationFrame.cancel(frame);
|
|
82
|
+
}
|
|
83
|
+
return undefined;
|
|
84
|
+
}, [toast.transitionStatus, toast.id, store]);
|
|
85
|
+
|
|
67
86
|
const handleLayout = useStableCallback((event: LayoutChangeEvent) => {
|
|
68
87
|
const { height } = event.nativeEvent.layout;
|
|
69
88
|
|
|
70
89
|
// A closing toast reports a height of 0 through the store; measuring it again
|
|
71
90
|
// here would undo that and leave a gap in the stack.
|
|
72
91
|
if (toast.transitionStatus !== 'ending' && height !== toast.height) {
|
|
73
|
-
store.updateToastInternal(toast.id, { height });
|
|
92
|
+
store.updateToastInternal(toast.id, { height, measuredHeight: height });
|
|
74
93
|
}
|
|
75
94
|
});
|
|
76
95
|
|
|
@@ -124,6 +143,7 @@ export function ToastRoot(componentProps: ToastRoot.Props) {
|
|
|
124
143
|
visibleIndex,
|
|
125
144
|
offsetY,
|
|
126
145
|
height: toast.height ?? 0,
|
|
146
|
+
measuredHeight: toast.measuredHeight ?? toast.height ?? 0,
|
|
127
147
|
swiping,
|
|
128
148
|
swipeMovement,
|
|
129
149
|
swipeDirection,
|
|
@@ -137,6 +157,7 @@ export function ToastRoot(componentProps: ToastRoot.Props) {
|
|
|
137
157
|
visibleIndex,
|
|
138
158
|
offsetY,
|
|
139
159
|
toast.height,
|
|
160
|
+
toast.measuredHeight,
|
|
140
161
|
swiping,
|
|
141
162
|
swipeMovement,
|
|
142
163
|
swipeDirection,
|
|
@@ -192,8 +213,9 @@ function getDisplacement(
|
|
|
192
213
|
|
|
193
214
|
export interface ToastRootState {
|
|
194
215
|
/**
|
|
195
|
-
* The toast's transition status: `'starting'` as it arrives
|
|
196
|
-
* is
|
|
216
|
+
* The toast's transition status: `'starting'` as it arrives (auto-clears to
|
|
217
|
+
* `undefined` after one frame for enter animation), `'ending'` once it is
|
|
218
|
+
* closing.
|
|
197
219
|
*/
|
|
198
220
|
transitionStatus: 'starting' | 'ending' | undefined;
|
|
199
221
|
/**
|
|
@@ -227,6 +249,12 @@ export interface ToastRootState {
|
|
|
227
249
|
* The toast's measured height.
|
|
228
250
|
*/
|
|
229
251
|
height: number;
|
|
252
|
+
/**
|
|
253
|
+
* The last measured height before the toast closed, preserved for the
|
|
254
|
+
* consumer to use as the starting value for an exit animation.
|
|
255
|
+
* Falls back to `height` when no close has occurred yet.
|
|
256
|
+
*/
|
|
257
|
+
measuredHeight: number;
|
|
230
258
|
/**
|
|
231
259
|
* Whether a swipe is currently in progress.
|
|
232
260
|
*/
|
package/src/toast/store.ts
CHANGED
|
@@ -301,7 +301,7 @@ export class ToastStore extends ReactStore<State, {}, typeof selectors> {
|
|
|
301
301
|
|
|
302
302
|
const endingToasts = toasts.map((item) =>
|
|
303
303
|
closeAll || item.id === toastId
|
|
304
|
-
? { ...item, transitionStatus: 'ending' as const, height: 0 }
|
|
304
|
+
? { ...item, transitionStatus: 'ending' as const, height: 0, measuredHeight: item.height }
|
|
305
305
|
: item,
|
|
306
306
|
);
|
|
307
307
|
const newToasts = applyLimited(endingToasts, limit);
|
|
@@ -70,6 +70,13 @@ export interface ToastObject<Data extends object> {
|
|
|
70
70
|
* The height of the toast.
|
|
71
71
|
*/
|
|
72
72
|
height?: number | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* The last measured height of the toast, captured at the moment it closes.
|
|
75
|
+
* Unlike `height` (which is set to `0` on close for stack collapse), this
|
|
76
|
+
* preserves the value so the consumer can use it as the starting point for
|
|
77
|
+
* an exit animation.
|
|
78
|
+
*/
|
|
79
|
+
measuredHeight?: number | undefined;
|
|
73
80
|
/**
|
|
74
81
|
* Callback function to be called when the toast is closed.
|
|
75
82
|
*/
|
|
@@ -38,7 +38,7 @@ export function TooltipPortal(props: TooltipPortal.Props) {
|
|
|
38
38
|
<Modal
|
|
39
39
|
transparent
|
|
40
40
|
visible={open}
|
|
41
|
-
animationType="
|
|
41
|
+
animationType="fade"
|
|
42
42
|
statusBarTranslucent
|
|
43
43
|
navigationBarTranslucent
|
|
44
44
|
onRequestClose={(event: NativeSyntheticEvent<unknown>) => {
|