@cdx-ui/components 0.0.1-beta.159 → 0.0.1-beta.160
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/coverage.svg +1 -1
- package/lib/commonjs/components/Menu/index.js +499 -0
- package/lib/commonjs/components/Menu/index.js.map +1 -0
- package/lib/commonjs/components/Menu/styles.js +100 -0
- package/lib/commonjs/components/Menu/styles.js.map +1 -0
- package/lib/commonjs/components/Select/index.js +57 -22
- package/lib/commonjs/components/Select/index.js.map +1 -1
- package/lib/commonjs/components/Select/styles.js +1 -40
- package/lib/commonjs/components/Select/styles.js.map +1 -1
- package/lib/commonjs/components/Slider/getSliderPopupPosition.js +16 -6
- package/lib/commonjs/components/Slider/getSliderPopupPosition.js.map +1 -1
- package/lib/commonjs/components/Slider/index.js +17 -5
- package/lib/commonjs/components/Slider/index.js.map +1 -1
- package/lib/commonjs/components/Toast/index.js +1 -1
- package/lib/commonjs/components/Toast/index.js.map +1 -1
- package/lib/commonjs/components/index.js +12 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/components/picker/surfaces.js +16 -9
- package/lib/commonjs/components/picker/surfaces.js.map +1 -1
- package/lib/commonjs/figma/Menu.Item.figma.js +52 -0
- package/lib/commonjs/figma/Menu.Item.figma.js.map +1 -0
- package/lib/commonjs/figma/Menu.figma.js +29 -0
- package/lib/commonjs/figma/Menu.figma.js.map +1 -0
- package/lib/commonjs/providers/forge-design/provider.js +25 -11
- package/lib/commonjs/providers/forge-design/provider.js.map +1 -1
- package/lib/commonjs/providers/toast/insets-container.js +4 -1
- package/lib/commonjs/providers/toast/insets-container.js.map +1 -1
- package/lib/commonjs/providers/toast/provider.js +126 -45
- package/lib/commonjs/providers/toast/provider.js.map +1 -1
- package/lib/commonjs/providers/toast/toast-item-renderer.js +2 -1
- package/lib/commonjs/providers/toast/toast-item-renderer.js.map +1 -1
- package/lib/module/components/Menu/index.js +495 -0
- package/lib/module/components/Menu/index.js.map +1 -0
- package/lib/module/components/Menu/styles.js +96 -0
- package/lib/module/components/Menu/styles.js.map +1 -0
- package/lib/module/components/Select/index.js +55 -20
- package/lib/module/components/Select/index.js.map +1 -1
- package/lib/module/components/Select/styles.js +0 -39
- package/lib/module/components/Select/styles.js.map +1 -1
- package/lib/module/components/Slider/getSliderPopupPosition.js +16 -6
- package/lib/module/components/Slider/getSliderPopupPosition.js.map +1 -1
- package/lib/module/components/Slider/index.js +20 -8
- package/lib/module/components/Slider/index.js.map +1 -1
- package/lib/module/components/Toast/index.js +1 -1
- package/lib/module/components/Toast/index.js.map +1 -1
- package/lib/module/components/index.js +1 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/components/picker/surfaces.js +18 -11
- package/lib/module/components/picker/surfaces.js.map +1 -1
- package/lib/module/figma/Menu.Item.figma.js +46 -0
- package/lib/module/figma/Menu.Item.figma.js.map +1 -0
- package/lib/module/figma/Menu.figma.js +23 -0
- package/lib/module/figma/Menu.figma.js.map +1 -0
- package/lib/module/providers/forge-design/provider.js +26 -12
- package/lib/module/providers/forge-design/provider.js.map +1 -1
- package/lib/module/providers/toast/insets-container.js +5 -2
- package/lib/module/providers/toast/insets-container.js.map +1 -1
- package/lib/module/providers/toast/provider.js +126 -45
- package/lib/module/providers/toast/provider.js.map +1 -1
- package/lib/module/providers/toast/toast-item-renderer.js +2 -1
- package/lib/module/providers/toast/toast-item-renderer.js.map +1 -1
- package/lib/typescript/components/Menu/index.d.ts +119 -0
- package/lib/typescript/components/Menu/index.d.ts.map +1 -0
- package/lib/typescript/components/Menu/styles.d.ts +24 -0
- package/lib/typescript/components/Menu/styles.d.ts.map +1 -0
- package/lib/typescript/components/Select/index.d.ts.map +1 -1
- package/lib/typescript/components/Select/styles.d.ts +0 -7
- package/lib/typescript/components/Select/styles.d.ts.map +1 -1
- package/lib/typescript/components/Slider/getSliderPopupPosition.d.ts +1 -1
- package/lib/typescript/components/Slider/getSliderPopupPosition.d.ts.map +1 -1
- package/lib/typescript/components/Slider/index.d.ts.map +1 -1
- package/lib/typescript/components/Toast/types.d.ts +2 -2
- package/lib/typescript/components/Toast/types.d.ts.map +1 -1
- package/lib/typescript/components/index.d.ts +1 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/components/picker/surfaces.d.ts.map +1 -1
- package/lib/typescript/figma/Menu.Item.figma.d.ts +8 -0
- package/lib/typescript/figma/Menu.Item.figma.d.ts.map +1 -0
- package/lib/typescript/figma/Menu.figma.d.ts +8 -0
- package/lib/typescript/figma/Menu.figma.d.ts.map +1 -0
- package/lib/typescript/providers/forge-design/provider.d.ts.map +1 -1
- package/lib/typescript/providers/toast/insets-container.d.ts.map +1 -1
- package/lib/typescript/providers/toast/lifecycle.context.d.ts +2 -2
- package/lib/typescript/providers/toast/lifecycle.context.d.ts.map +1 -1
- package/lib/typescript/providers/toast/provider.d.ts.map +1 -1
- package/lib/typescript/providers/toast/toast-item-renderer.d.ts.map +1 -1
- package/lib/typescript/providers/toast/types.d.ts +22 -8
- package/lib/typescript/providers/toast/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/components/Menu/index.tsx +654 -0
- package/src/components/Menu/styles.ts +153 -0
- package/src/components/Select/index.tsx +58 -23
- package/src/components/Select/styles.ts +0 -59
- package/src/components/Slider/getSliderPopupPosition.ts +19 -7
- package/src/components/Slider/index.tsx +25 -9
- package/src/components/Toast/index.tsx +1 -1
- package/src/components/Toast/types.ts +2 -2
- package/src/components/index.ts +1 -0
- package/src/components/picker/surfaces.tsx +13 -9
- package/src/providers/forge-design/provider.tsx +35 -16
- package/src/providers/toast/insets-container.tsx +3 -2
- package/src/providers/toast/lifecycle.context.tsx +2 -2
- package/src/providers/toast/provider.tsx +150 -51
- package/src/providers/toast/toast-item-renderer.tsx +5 -1
- package/src/providers/toast/types.ts +22 -8
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { SafeAreaListener } from 'react-native-safe-area-context';
|
|
3
3
|
import { Uniwind } from 'uniwind';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
OverlayInsetsProvider,
|
|
6
|
+
OverlayStackProvider,
|
|
7
|
+
PortalHost,
|
|
8
|
+
PortalProvider,
|
|
9
|
+
type EdgeInsets,
|
|
10
|
+
} from '@cdx-ui/primitives';
|
|
5
11
|
import { GlobalAnimationSettingsProvider } from '../animation-settings';
|
|
6
12
|
import { TextComponentProvider } from '../text-component/provider';
|
|
7
13
|
import { ToastProvider } from '../toast/provider';
|
|
@@ -28,6 +34,12 @@ import type { ForgeDesignProviderProps } from './types';
|
|
|
28
34
|
*/
|
|
29
35
|
const ForgeDesignProvider: React.FC<ForgeDesignProviderProps> = ({ children, config = {} }) => {
|
|
30
36
|
const { textProps, toast, animation } = config;
|
|
37
|
+
const [overlayInsets, setOverlayInsets] = React.useState<EdgeInsets>({
|
|
38
|
+
top: 0,
|
|
39
|
+
right: 0,
|
|
40
|
+
bottom: 0,
|
|
41
|
+
left: 0,
|
|
42
|
+
});
|
|
31
43
|
|
|
32
44
|
// Determine if toast should be enabled and get props
|
|
33
45
|
const isToastEnabled = toast !== false && toast !== 'disabled';
|
|
@@ -37,23 +49,30 @@ const ForgeDesignProvider: React.FC<ForgeDesignProviderProps> = ({ children, con
|
|
|
37
49
|
<SafeAreaListener
|
|
38
50
|
onChange={({ insets }) => {
|
|
39
51
|
Uniwind.updateInsets(insets);
|
|
52
|
+
setOverlayInsets(insets);
|
|
40
53
|
}}
|
|
41
54
|
>
|
|
42
|
-
<
|
|
43
|
-
<
|
|
44
|
-
{
|
|
45
|
-
<
|
|
46
|
-
{
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
<PortalProvider>
|
|
56
|
+
<OverlayStackProvider>
|
|
57
|
+
<OverlayInsetsProvider value={overlayInsets}>
|
|
58
|
+
<GlobalAnimationSettingsProvider animation={animation}>
|
|
59
|
+
<TextComponentProvider value={{ textProps }}>
|
|
60
|
+
{isToastEnabled ? (
|
|
61
|
+
<ToastProvider {...toastProps}>
|
|
62
|
+
{children}
|
|
63
|
+
<PortalHost />
|
|
64
|
+
</ToastProvider>
|
|
65
|
+
) : (
|
|
66
|
+
<>
|
|
67
|
+
{children}
|
|
68
|
+
<PortalHost />
|
|
69
|
+
</>
|
|
70
|
+
)}
|
|
71
|
+
</TextComponentProvider>
|
|
72
|
+
</GlobalAnimationSettingsProvider>
|
|
73
|
+
</OverlayInsetsProvider>
|
|
74
|
+
</OverlayStackProvider>
|
|
75
|
+
</PortalProvider>
|
|
57
76
|
</SafeAreaListener>
|
|
58
77
|
);
|
|
59
78
|
};
|
|
@@ -2,7 +2,7 @@ import type { ReactNode } from 'react';
|
|
|
2
2
|
import { useCallback, useContext, useEffect, useMemo, useRef } from 'react';
|
|
3
3
|
import { Platform, View } from 'react-native';
|
|
4
4
|
import { SafeAreaInsetsContext, type EdgeInsets } from 'react-native-safe-area-context';
|
|
5
|
-
import { FullWindowOverlay } from '@cdx-ui/primitives';
|
|
5
|
+
import { FullWindowOverlay, NOTIFICATION_OVERLAY_Z_INDEX } from '@cdx-ui/primitives';
|
|
6
6
|
import type { ToastInsets } from './types';
|
|
7
7
|
|
|
8
8
|
const ZERO_INSETS: EdgeInsets = { top: 0, bottom: 0, left: 0, right: 0 };
|
|
@@ -199,7 +199,7 @@ export function InsetsContainer({
|
|
|
199
199
|
const content = (
|
|
200
200
|
<View
|
|
201
201
|
ref={containerRef}
|
|
202
|
-
className="absolute inset-0 pointer-events-box-none"
|
|
202
|
+
className="absolute web:fixed inset-0 pointer-events-box-none"
|
|
203
203
|
role="region"
|
|
204
204
|
aria-label={announceLabel}
|
|
205
205
|
{...(Platform.OS === 'web' && {
|
|
@@ -207,6 +207,7 @@ export function InsetsContainer({
|
|
|
207
207
|
'aria-atomic': false,
|
|
208
208
|
})}
|
|
209
209
|
style={{
|
|
210
|
+
...(Platform.OS === 'web' ? { zIndex: NOTIFICATION_OVERLAY_Z_INDEX } : {}),
|
|
210
211
|
paddingTop: finalInsets.top,
|
|
211
212
|
paddingBottom: finalInsets.bottom,
|
|
212
213
|
paddingLeft: finalInsets.left,
|
|
@@ -18,8 +18,8 @@ export interface ToastLifecycleContextValue {
|
|
|
18
18
|
remove: (id: string) => void;
|
|
19
19
|
/** IDs of toasts that have been marked closed but are still mounted. */
|
|
20
20
|
pendingRemovalIds: ReadonlySet<string>;
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/** IDs of the front-most non-pending toast in each placement stack. */
|
|
22
|
+
frontmostIds: ReadonlySet<string>;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export const ToastLifecycleContext = createContext<ToastLifecycleContextValue | null>(null);
|
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
} from 'react';
|
|
10
10
|
import { Platform, View } from 'react-native';
|
|
11
11
|
import { useSharedValue } from 'react-native-reanimated';
|
|
12
|
+
import { createPortalFn, useOverlayStackRegistration } from '@cdx-ui/primitives';
|
|
12
13
|
import { DefaultToast } from '../../components/Toast';
|
|
14
|
+
import type { ToastPlacement } from '../../components/Toast/types';
|
|
13
15
|
import { InsetsContainer } from './insets-container';
|
|
14
16
|
import { ToastLifecycleContext, type ToastLifecycleContextValue } from './lifecycle.context';
|
|
15
17
|
import { toastReducer } from './reducer';
|
|
@@ -26,6 +28,54 @@ import type {
|
|
|
26
28
|
} from './types';
|
|
27
29
|
|
|
28
30
|
const DEFAULT_DURATION = 4000;
|
|
31
|
+
const TOAST_PLACEMENTS: readonly ToastPlacement[] = [
|
|
32
|
+
'top-start',
|
|
33
|
+
'top',
|
|
34
|
+
'top-end',
|
|
35
|
+
'bottom-start',
|
|
36
|
+
'bottom',
|
|
37
|
+
'bottom-end',
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
function useToastPlacementStacks() {
|
|
41
|
+
const topStartTotal = useSharedValue(0);
|
|
42
|
+
const topStartHeights = useSharedValue<Record<string, number>>({});
|
|
43
|
+
const topTotal = useSharedValue(0);
|
|
44
|
+
const topHeights = useSharedValue<Record<string, number>>({});
|
|
45
|
+
const topEndTotal = useSharedValue(0);
|
|
46
|
+
const topEndHeights = useSharedValue<Record<string, number>>({});
|
|
47
|
+
const bottomStartTotal = useSharedValue(0);
|
|
48
|
+
const bottomStartHeights = useSharedValue<Record<string, number>>({});
|
|
49
|
+
const bottomTotal = useSharedValue(0);
|
|
50
|
+
const bottomHeights = useSharedValue<Record<string, number>>({});
|
|
51
|
+
const bottomEndTotal = useSharedValue(0);
|
|
52
|
+
const bottomEndHeights = useSharedValue<Record<string, number>>({});
|
|
53
|
+
|
|
54
|
+
return useMemo(
|
|
55
|
+
() => ({
|
|
56
|
+
'top-start': { total: topStartTotal, heights: topStartHeights },
|
|
57
|
+
top: { total: topTotal, heights: topHeights },
|
|
58
|
+
'top-end': { total: topEndTotal, heights: topEndHeights },
|
|
59
|
+
'bottom-start': { total: bottomStartTotal, heights: bottomStartHeights },
|
|
60
|
+
bottom: { total: bottomTotal, heights: bottomHeights },
|
|
61
|
+
'bottom-end': { total: bottomEndTotal, heights: bottomEndHeights },
|
|
62
|
+
}),
|
|
63
|
+
[
|
|
64
|
+
bottomEndHeights,
|
|
65
|
+
bottomEndTotal,
|
|
66
|
+
bottomHeights,
|
|
67
|
+
bottomStartHeights,
|
|
68
|
+
bottomStartTotal,
|
|
69
|
+
bottomTotal,
|
|
70
|
+
topEndHeights,
|
|
71
|
+
topEndTotal,
|
|
72
|
+
topHeights,
|
|
73
|
+
topStartHeights,
|
|
74
|
+
topStartTotal,
|
|
75
|
+
topTotal,
|
|
76
|
+
],
|
|
77
|
+
);
|
|
78
|
+
}
|
|
29
79
|
|
|
30
80
|
/**
|
|
31
81
|
* Context for toast manager
|
|
@@ -137,19 +187,30 @@ export function ToastProvider({
|
|
|
137
187
|
|
|
138
188
|
const isToastVisible = toasts.length > 0;
|
|
139
189
|
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
const total = useSharedValue<number>(0);
|
|
190
|
+
const placementStacks = useToastPlacementStacks();
|
|
143
191
|
|
|
144
192
|
/**
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
* was prone to drift when hide + show ran in the same tick or when
|
|
148
|
-
* auto-dismiss raced with a manual hide (stale-closure mismatch).
|
|
193
|
+
* Each placement owns independent animation metrics. A toast at bottom-end
|
|
194
|
+
* must not compress or fade because unrelated toasts are open at top-start.
|
|
149
195
|
*/
|
|
150
196
|
useEffect(() => {
|
|
151
|
-
|
|
152
|
-
|
|
197
|
+
const totals: Record<ToastPlacement, number> = {
|
|
198
|
+
'top-start': 0,
|
|
199
|
+
top: 0,
|
|
200
|
+
'top-end': 0,
|
|
201
|
+
'bottom-start': 0,
|
|
202
|
+
bottom: 0,
|
|
203
|
+
'bottom-end': 0,
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
for (const toast of toasts) {
|
|
207
|
+
totals[toast.placement] += 1;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
for (const placement of TOAST_PLACEMENTS) {
|
|
211
|
+
placementStacks[placement].total.set(totals[placement]);
|
|
212
|
+
}
|
|
213
|
+
}, [placementStacks, toasts]);
|
|
153
214
|
|
|
154
215
|
const idCounter = useRef(0);
|
|
155
216
|
|
|
@@ -199,17 +260,21 @@ export function ToastProvider({
|
|
|
199
260
|
|
|
200
261
|
dispatch({ type: 'REMOVE', payload: { ids: idsArray } });
|
|
201
262
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
263
|
+
for (const placement of TOAST_PLACEMENTS) {
|
|
264
|
+
placementStacks[placement].heights.modify(
|
|
265
|
+
<T extends Record<string, number>>(value: T): T => {
|
|
266
|
+
'worklet';
|
|
267
|
+
const result = { ...value };
|
|
268
|
+
for (const id of idsArray) {
|
|
269
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
270
|
+
delete result[id];
|
|
271
|
+
}
|
|
272
|
+
return result;
|
|
273
|
+
},
|
|
274
|
+
);
|
|
275
|
+
}
|
|
211
276
|
},
|
|
212
|
-
[
|
|
277
|
+
[placementStacks],
|
|
213
278
|
);
|
|
214
279
|
|
|
215
280
|
/**
|
|
@@ -358,18 +423,18 @@ export function ToastProvider({
|
|
|
358
423
|
|
|
359
424
|
const lifecycleContextValue = useMemo<ToastLifecycleContextValue>(() => {
|
|
360
425
|
const pending = new Set<string>();
|
|
361
|
-
|
|
426
|
+
const frontmostByPlacement = new Map<ToastPlacement, string>();
|
|
362
427
|
for (const toast of toasts) {
|
|
363
428
|
if (toast.pendingRemoval) {
|
|
364
429
|
pending.add(toast.id);
|
|
365
430
|
} else {
|
|
366
|
-
|
|
431
|
+
frontmostByPlacement.set(toast.placement, toast.id);
|
|
367
432
|
}
|
|
368
433
|
}
|
|
369
434
|
return {
|
|
370
435
|
remove: (id: string) => remove([id]),
|
|
371
436
|
pendingRemovalIds: pending,
|
|
372
|
-
|
|
437
|
+
frontmostIds: new Set(frontmostByPlacement.values()),
|
|
373
438
|
};
|
|
374
439
|
}, [remove, toasts]);
|
|
375
440
|
|
|
@@ -391,6 +456,7 @@ export function ToastProvider({
|
|
|
391
456
|
normalizedOptions = {
|
|
392
457
|
id: undefined,
|
|
393
458
|
component: createStringToastComponent(options, globalConfig),
|
|
459
|
+
placement: globalConfig?.placement,
|
|
394
460
|
duration: DEFAULT_DURATION,
|
|
395
461
|
};
|
|
396
462
|
duration = DEFAULT_DURATION;
|
|
@@ -404,6 +470,7 @@ export function ToastProvider({
|
|
|
404
470
|
normalizedOptions = {
|
|
405
471
|
id: config.id,
|
|
406
472
|
component: createConfigToastComponent(config, globalConfig),
|
|
473
|
+
placement: config.placement,
|
|
407
474
|
duration,
|
|
408
475
|
onShow: config.onShow,
|
|
409
476
|
onHide: config.onHide,
|
|
@@ -418,6 +485,7 @@ export function ToastProvider({
|
|
|
418
485
|
|
|
419
486
|
const id =
|
|
420
487
|
normalizedOptions.id ?? `toast-${String(Date.now())}-${String(idCounter.current++)}`;
|
|
488
|
+
const placement = normalizedOptions.placement ?? globalConfig?.placement ?? 'bottom';
|
|
421
489
|
|
|
422
490
|
// If an explicit ID was provided, check if a toast with that ID already exists
|
|
423
491
|
// If it exists, skip adding a new toast and return the existing ID
|
|
@@ -432,6 +500,7 @@ export function ToastProvider({
|
|
|
432
500
|
type: 'SHOW',
|
|
433
501
|
payload: {
|
|
434
502
|
id,
|
|
503
|
+
placement,
|
|
435
504
|
component: normalizedOptions.component,
|
|
436
505
|
duration,
|
|
437
506
|
onShow: normalizedOptions.onShow,
|
|
@@ -497,40 +566,70 @@ export function ToastProvider({
|
|
|
497
566
|
[show, hide, pauseAll, resumeAll, isToastVisible],
|
|
498
567
|
);
|
|
499
568
|
|
|
569
|
+
useOverlayStackRegistration({
|
|
570
|
+
active: toasts.length > 0,
|
|
571
|
+
capabilities: [],
|
|
572
|
+
id: 'forge-ui-toast-viewport',
|
|
573
|
+
groupId: 'forge-ui-notifications',
|
|
574
|
+
layer: 'notification',
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
const placementIndexes: Record<ToastPlacement, number> = {
|
|
578
|
+
'top-start': 0,
|
|
579
|
+
top: 0,
|
|
580
|
+
'top-end': 0,
|
|
581
|
+
'bottom-start': 0,
|
|
582
|
+
bottom: 0,
|
|
583
|
+
'bottom-end': 0,
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
const toastViewport =
|
|
587
|
+
toasts.length > 0 ? (
|
|
588
|
+
<InsetsContainer
|
|
589
|
+
insets={insets}
|
|
590
|
+
announceLabel={announceLabel}
|
|
591
|
+
contentWrapper={contentWrapper}
|
|
592
|
+
disableFullWindowOverlay={disableFullWindowOverlay}
|
|
593
|
+
unstable_accessibilityContainerViewIsModal={unstable_accessibilityContainerViewIsModal}
|
|
594
|
+
pauseAll={pauseAll}
|
|
595
|
+
resumeAll={resumeAll}
|
|
596
|
+
hide={hide}
|
|
597
|
+
>
|
|
598
|
+
<View className="flex-1">
|
|
599
|
+
{toasts.map((toastItem) => {
|
|
600
|
+
const placement = toastItem.placement;
|
|
601
|
+
const index = placementIndexes[placement];
|
|
602
|
+
const stack = placementStacks[placement];
|
|
603
|
+
placementIndexes[placement] += 1;
|
|
604
|
+
|
|
605
|
+
return (
|
|
606
|
+
<ToastItemRenderer
|
|
607
|
+
key={toastItem.id}
|
|
608
|
+
toastItem={toastItem}
|
|
609
|
+
show={show}
|
|
610
|
+
hide={hide}
|
|
611
|
+
index={index}
|
|
612
|
+
total={stack.total}
|
|
613
|
+
heights={stack.heights}
|
|
614
|
+
maxVisibleToasts={maxVisibleToasts}
|
|
615
|
+
/>
|
|
616
|
+
);
|
|
617
|
+
})}
|
|
618
|
+
</View>
|
|
619
|
+
</InsetsContainer>
|
|
620
|
+
) : null;
|
|
621
|
+
|
|
622
|
+
const renderedToastViewport =
|
|
623
|
+
Platform.OS === 'web' && toastViewport && createPortalFn && typeof document !== 'undefined'
|
|
624
|
+
? createPortalFn(toastViewport, document.body)
|
|
625
|
+
: toastViewport;
|
|
626
|
+
|
|
500
627
|
return (
|
|
501
628
|
<ToastConfigContext.Provider value={globalConfig}>
|
|
502
629
|
<ToasterContext.Provider value={contextValue}>
|
|
503
630
|
<ToastLifecycleContext.Provider value={lifecycleContextValue}>
|
|
504
631
|
{children}
|
|
505
|
-
{
|
|
506
|
-
<InsetsContainer
|
|
507
|
-
insets={insets}
|
|
508
|
-
announceLabel={announceLabel}
|
|
509
|
-
contentWrapper={contentWrapper}
|
|
510
|
-
disableFullWindowOverlay={disableFullWindowOverlay}
|
|
511
|
-
unstable_accessibilityContainerViewIsModal={
|
|
512
|
-
unstable_accessibilityContainerViewIsModal
|
|
513
|
-
}
|
|
514
|
-
pauseAll={pauseAll}
|
|
515
|
-
resumeAll={resumeAll}
|
|
516
|
-
hide={hide}
|
|
517
|
-
>
|
|
518
|
-
<View className="flex-1">
|
|
519
|
-
{toasts.map((toastItem, index) => (
|
|
520
|
-
<ToastItemRenderer
|
|
521
|
-
key={toastItem.id}
|
|
522
|
-
toastItem={toastItem}
|
|
523
|
-
show={show}
|
|
524
|
-
hide={hide}
|
|
525
|
-
index={index}
|
|
526
|
-
total={total}
|
|
527
|
-
heights={heights}
|
|
528
|
-
maxVisibleToasts={maxVisibleToasts}
|
|
529
|
-
/>
|
|
530
|
-
))}
|
|
531
|
-
</View>
|
|
532
|
-
</InsetsContainer>
|
|
533
|
-
)}
|
|
632
|
+
{renderedToastViewport}
|
|
534
633
|
</ToastLifecycleContext.Provider>
|
|
535
634
|
</ToasterContext.Provider>
|
|
536
635
|
</ToastConfigContext.Provider>
|
|
@@ -13,6 +13,7 @@ export const ToastItemRenderer = memo(
|
|
|
13
13
|
|
|
14
14
|
const content = toastItem.component({
|
|
15
15
|
id: toastItem.id,
|
|
16
|
+
placement: toastItem.placement,
|
|
16
17
|
index,
|
|
17
18
|
total,
|
|
18
19
|
heights,
|
|
@@ -33,8 +34,11 @@ export const ToastItemRenderer = memo(
|
|
|
33
34
|
return (
|
|
34
35
|
prevProps.toastItem.id === nextProps.toastItem.id &&
|
|
35
36
|
prevProps.toastItem.component === nextProps.toastItem.component &&
|
|
37
|
+
prevProps.toastItem.placement === nextProps.toastItem.placement &&
|
|
36
38
|
prevProps.toastItem.pendingRemoval === nextProps.toastItem.pendingRemoval &&
|
|
37
|
-
prevProps.index === nextProps.index
|
|
39
|
+
prevProps.index === nextProps.index &&
|
|
40
|
+
prevProps.total === nextProps.total &&
|
|
41
|
+
prevProps.heights === nextProps.heights
|
|
38
42
|
);
|
|
39
43
|
},
|
|
40
44
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SharedValue } from 'react-native-reanimated';
|
|
2
|
-
import type { ToastProps } from '../../components/Toast/types';
|
|
2
|
+
import type { ToastPlacement, ToastProps } from '../../components/Toast/types';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Global toast configuration
|
|
@@ -120,15 +120,19 @@ export interface ToastComponentProps {
|
|
|
120
120
|
*/
|
|
121
121
|
id: string;
|
|
122
122
|
/**
|
|
123
|
-
*
|
|
123
|
+
* Placement used to isolate this toast's stack metrics.
|
|
124
|
+
*/
|
|
125
|
+
placement: ToastPlacement;
|
|
126
|
+
/**
|
|
127
|
+
* The index of the toast within its placement stack (0-based).
|
|
124
128
|
*/
|
|
125
129
|
index: number;
|
|
126
130
|
/**
|
|
127
|
-
* The total number of toasts currently displayed
|
|
131
|
+
* The total number of toasts currently displayed at this placement.
|
|
128
132
|
*/
|
|
129
133
|
total: SharedValue<number>;
|
|
130
134
|
/**
|
|
131
|
-
* Heights of
|
|
135
|
+
* Heights of toast items at this placement, keyed by toast ID.
|
|
132
136
|
*/
|
|
133
137
|
heights: SharedValue<Record<string, number>>;
|
|
134
138
|
/**
|
|
@@ -225,6 +229,11 @@ export interface ToastShowOptionsWithComponent {
|
|
|
225
229
|
* A function that receives toast props and returns a React element
|
|
226
230
|
*/
|
|
227
231
|
component: (props: ToastComponentProps) => React.ReactElement;
|
|
232
|
+
/**
|
|
233
|
+
* Placement used for stack isolation and passed to the custom component.
|
|
234
|
+
* @default ToastProvider default placement, then 'bottom'
|
|
235
|
+
*/
|
|
236
|
+
placement?: ToastPlacement;
|
|
228
237
|
/**
|
|
229
238
|
* Duration in milliseconds before the toast automatically disappears
|
|
230
239
|
* Set to `'persistent'` to prevent auto-hide (toast will remain until manually dismissed)
|
|
@@ -243,7 +252,7 @@ export interface ToastShowOptionsWithComponent {
|
|
|
243
252
|
|
|
244
253
|
/**
|
|
245
254
|
* Conditional type for toast show options
|
|
246
|
-
* - If component is provided:
|
|
255
|
+
* - If component is provided: lifecycle options plus placement are allowed
|
|
247
256
|
* - If component is NOT provided: all config props are available
|
|
248
257
|
*/
|
|
249
258
|
export type ToastShowOptions =
|
|
@@ -258,6 +267,11 @@ export interface ToastItem {
|
|
|
258
267
|
* Unique identifier for the toast
|
|
259
268
|
*/
|
|
260
269
|
id: string;
|
|
270
|
+
/**
|
|
271
|
+
* Resolved placement used to isolate stack animation metrics.
|
|
272
|
+
* @internal
|
|
273
|
+
*/
|
|
274
|
+
placement: ToastPlacement;
|
|
261
275
|
/**
|
|
262
276
|
* A function that receives toast props and returns a React element
|
|
263
277
|
*/
|
|
@@ -388,15 +402,15 @@ export interface ToastItemRendererProps {
|
|
|
388
402
|
*/
|
|
389
403
|
toastItem: ToastItem;
|
|
390
404
|
/**
|
|
391
|
-
* The index of the toast
|
|
405
|
+
* The index of the toast within its placement stack (0-based).
|
|
392
406
|
*/
|
|
393
407
|
index: number;
|
|
394
408
|
/**
|
|
395
|
-
* The total number of toasts currently displayed
|
|
409
|
+
* The total number of toasts currently displayed at this placement.
|
|
396
410
|
*/
|
|
397
411
|
total: SharedValue<number>;
|
|
398
412
|
/**
|
|
399
|
-
* Heights of
|
|
413
|
+
* Heights of toast items at this placement, keyed by toast ID.
|
|
400
414
|
*/
|
|
401
415
|
heights: SharedValue<Record<string, number>>;
|
|
402
416
|
/**
|