panelui-native 0.63.0 → 0.66.1
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 +5 -0
- package/lib/module/components/badge/index.js +1 -1
- package/lib/module/components/badge/index.js.map +1 -1
- package/lib/module/components/bar-chart/index.js +39 -2
- package/lib/module/components/bar-chart/index.js.map +1 -1
- package/lib/module/components/bottom-sheet/index.js +2 -2
- package/lib/module/components/bottom-sheet/index.js.map +1 -1
- package/lib/module/components/button/index.js +19 -9
- package/lib/module/components/button/index.js.map +1 -1
- package/lib/module/components/carousel/carousel-lifecycle.js +51 -0
- package/lib/module/components/carousel/carousel-lifecycle.js.map +1 -0
- package/lib/module/components/carousel/index.js +86 -35
- package/lib/module/components/carousel/index.js.map +1 -1
- package/lib/module/components/checkbox/index.js +2 -3
- package/lib/module/components/checkbox/index.js.map +1 -1
- package/lib/module/components/context-menu/context-menu-invocation.js +23 -0
- package/lib/module/components/context-menu/context-menu-invocation.js.map +1 -0
- package/lib/module/components/context-menu/index.js +78 -9
- package/lib/module/components/context-menu/index.js.map +1 -1
- package/lib/module/components/dialog/index.js +2 -2
- package/lib/module/components/dialog/index.js.map +1 -1
- package/lib/module/components/drawer/index.js +2 -2
- package/lib/module/components/drawer/index.js.map +1 -1
- package/lib/module/components/fab/index.js +4 -3
- package/lib/module/components/fab/index.js.map +1 -1
- package/lib/module/components/field/index.js +41 -6
- package/lib/module/components/field/index.js.map +1 -1
- package/lib/module/components/flow/flow-accessibility.js +100 -0
- package/lib/module/components/flow/flow-accessibility.js.map +1 -0
- package/lib/module/components/flow/flow-identifiers.js +81 -0
- package/lib/module/components/flow/flow-identifiers.js.map +1 -0
- package/lib/module/components/flow/index.js +112 -57
- package/lib/module/components/flow/index.js.map +1 -1
- package/lib/module/components/form/use-form.js +93 -66
- package/lib/module/components/form/use-form.js.map +1 -1
- package/lib/module/components/heatmap-chart/index.js +2 -2
- package/lib/module/components/heatmap-chart/index.js.map +1 -1
- package/lib/module/components/hex-chart/index.js +2 -2
- package/lib/module/components/hex-chart/index.js.map +1 -1
- package/lib/module/components/kpi/index.js +3 -1
- package/lib/module/components/kpi/index.js.map +1 -1
- package/lib/module/components/line-chart/index.js +3 -3
- package/lib/module/components/line-chart/index.js.map +1 -1
- package/lib/module/components/map/index.js +71 -8
- package/lib/module/components/map/index.js.map +1 -1
- package/lib/module/components/map/map-accessibility.js +20 -0
- package/lib/module/components/map/map-accessibility.js.map +1 -0
- package/lib/module/components/map/maplibre.js.map +1 -1
- package/lib/module/components/menu/index.js +4 -1
- package/lib/module/components/menu/index.js.map +1 -1
- package/lib/module/components/message-scroller/index.js +17 -9
- package/lib/module/components/message-scroller/index.js.map +1 -1
- package/lib/module/components/message-scroller/message-scroller-math.js +20 -0
- package/lib/module/components/message-scroller/message-scroller-math.js.map +1 -0
- package/lib/module/components/number-input/index.js +1 -23
- package/lib/module/components/number-input/index.js.map +1 -1
- package/lib/module/components/number-input/number-input-math.js +44 -0
- package/lib/module/components/number-input/number-input-math.js.map +1 -0
- package/lib/module/components/plot/index.js +2 -2
- package/lib/module/components/plot/index.js.map +1 -1
- package/lib/module/components/popover/index.js.map +1 -1
- package/lib/module/components/radio-group/index.js +1 -2
- package/lib/module/components/radio-group/index.js.map +1 -1
- package/lib/module/components/selection-mode/index.js +26 -31
- package/lib/module/components/selection-mode/index.js.map +1 -1
- package/lib/module/components/selection-mode/selection-mode-contracts.js +48 -0
- package/lib/module/components/selection-mode/selection-mode-contracts.js.map +1 -0
- package/lib/module/components/signature/index.js +62 -20
- package/lib/module/components/signature/index.js.map +1 -1
- package/lib/module/components/signature/signature-accessibility.js +58 -0
- package/lib/module/components/signature/signature-accessibility.js.map +1 -0
- package/lib/module/components/slider/index.js +22 -18
- package/lib/module/components/slider/index.js.map +1 -1
- package/lib/module/components/sortable/index.js +5 -55
- package/lib/module/components/sortable/index.js.map +1 -1
- package/lib/module/components/sortable/reorder.js +63 -0
- package/lib/module/components/sortable/reorder.js.map +1 -0
- package/lib/module/components/swipe/index.js +4 -4
- package/lib/module/components/swipe/index.js.map +1 -1
- package/lib/module/components/switch/index.js +36 -3
- package/lib/module/components/switch/index.js.map +1 -1
- package/lib/module/components/time-picker/accessibility.js +21 -0
- package/lib/module/components/time-picker/accessibility.js.map +1 -0
- package/lib/module/components/time-picker/index.js +34 -8
- package/lib/module/components/time-picker/index.js.map +1 -1
- package/lib/module/components/waterfall-chart/index.js +1325 -0
- package/lib/module/components/waterfall-chart/index.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/primitives/modal-isolation-store.js +29 -0
- package/lib/module/primitives/modal-isolation-store.js.map +1 -0
- package/lib/module/primitives/portal.js +36 -4
- package/lib/module/primitives/portal.js.map +1 -1
- package/lib/module/primitives/scrim.js +94 -3
- package/lib/module/primitives/scrim.js.map +1 -1
- package/lib/module/providers/panel-ui-provider.js +27 -6
- package/lib/module/providers/panel-ui-provider.js.map +1 -1
- package/lib/typescript/src/components/badge/index.d.ts.map +1 -1
- package/lib/typescript/src/components/bar-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/bottom-sheet/index.d.ts +3 -0
- package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
- package/lib/typescript/src/components/button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/carousel/carousel-lifecycle.d.ts +23 -0
- package/lib/typescript/src/components/carousel/carousel-lifecycle.d.ts.map +1 -0
- package/lib/typescript/src/components/carousel/index.d.ts +5 -2
- package/lib/typescript/src/components/carousel/index.d.ts.map +1 -1
- package/lib/typescript/src/components/checkbox/index.d.ts.map +1 -1
- package/lib/typescript/src/components/context-menu/context-menu-invocation.d.ts +20 -0
- package/lib/typescript/src/components/context-menu/context-menu-invocation.d.ts.map +1 -0
- package/lib/typescript/src/components/context-menu/index.d.ts +18 -8
- package/lib/typescript/src/components/context-menu/index.d.ts.map +1 -1
- package/lib/typescript/src/components/dialog/index.d.ts +3 -0
- package/lib/typescript/src/components/dialog/index.d.ts.map +1 -1
- package/lib/typescript/src/components/drawer/index.d.ts +3 -0
- package/lib/typescript/src/components/drawer/index.d.ts.map +1 -1
- package/lib/typescript/src/components/fab/index.d.ts.map +1 -1
- package/lib/typescript/src/components/field/index.d.ts +8 -1
- package/lib/typescript/src/components/field/index.d.ts.map +1 -1
- package/lib/typescript/src/components/flow/flow-accessibility.d.ts +57 -0
- package/lib/typescript/src/components/flow/flow-accessibility.d.ts.map +1 -0
- package/lib/typescript/src/components/flow/flow-identifiers.d.ts +24 -0
- package/lib/typescript/src/components/flow/flow-identifiers.d.ts.map +1 -0
- package/lib/typescript/src/components/flow/index.d.ts +20 -7
- package/lib/typescript/src/components/flow/index.d.ts.map +1 -1
- package/lib/typescript/src/components/form/use-form.d.ts.map +1 -1
- package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/hex-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/kpi/index.d.ts.map +1 -1
- package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/map/index.d.ts +13 -5
- package/lib/typescript/src/components/map/index.d.ts.map +1 -1
- package/lib/typescript/src/components/map/map-accessibility.d.ts +19 -0
- package/lib/typescript/src/components/map/map-accessibility.d.ts.map +1 -0
- package/lib/typescript/src/components/map/maplibre.d.ts +2 -0
- package/lib/typescript/src/components/map/maplibre.d.ts.map +1 -1
- package/lib/typescript/src/components/menu/index.d.ts.map +1 -1
- package/lib/typescript/src/components/message-scroller/index.d.ts.map +1 -1
- package/lib/typescript/src/components/message-scroller/message-scroller-math.d.ts +8 -0
- package/lib/typescript/src/components/message-scroller/message-scroller-math.d.ts.map +1 -0
- package/lib/typescript/src/components/number-input/index.d.ts.map +1 -1
- package/lib/typescript/src/components/number-input/number-input-math.d.ts +9 -0
- package/lib/typescript/src/components/number-input/number-input-math.d.ts.map +1 -0
- package/lib/typescript/src/components/plot/index.d.ts.map +1 -1
- package/lib/typescript/src/components/popover/index.d.ts +3 -0
- package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
- package/lib/typescript/src/components/radio-group/index.d.ts.map +1 -1
- package/lib/typescript/src/components/selection-mode/index.d.ts +6 -4
- package/lib/typescript/src/components/selection-mode/index.d.ts.map +1 -1
- package/lib/typescript/src/components/selection-mode/selection-mode-contracts.d.ts +18 -0
- package/lib/typescript/src/components/selection-mode/selection-mode-contracts.d.ts.map +1 -0
- package/lib/typescript/src/components/signature/index.d.ts +6 -0
- package/lib/typescript/src/components/signature/index.d.ts.map +1 -1
- package/lib/typescript/src/components/signature/signature-accessibility.d.ts +27 -0
- package/lib/typescript/src/components/signature/signature-accessibility.d.ts.map +1 -0
- package/lib/typescript/src/components/slider/index.d.ts.map +1 -1
- package/lib/typescript/src/components/sortable/index.d.ts.map +1 -1
- package/lib/typescript/src/components/sortable/reorder.d.ts +14 -0
- package/lib/typescript/src/components/sortable/reorder.d.ts.map +1 -0
- package/lib/typescript/src/components/swipe/index.d.ts.map +1 -1
- package/lib/typescript/src/components/switch/index.d.ts +3 -3
- package/lib/typescript/src/components/switch/index.d.ts.map +1 -1
- package/lib/typescript/src/components/time-picker/accessibility.d.ts +10 -0
- package/lib/typescript/src/components/time-picker/accessibility.d.ts.map +1 -0
- package/lib/typescript/src/components/time-picker/index.d.ts.map +1 -1
- package/lib/typescript/src/components/waterfall-chart/index.d.ts +379 -0
- package/lib/typescript/src/components/waterfall-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +3 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/modal-isolation-store.d.ts +10 -0
- package/lib/typescript/src/primitives/modal-isolation-store.d.ts.map +1 -0
- package/lib/typescript/src/primitives/portal.d.ts +6 -0
- package/lib/typescript/src/primitives/portal.d.ts.map +1 -1
- package/lib/typescript/src/primitives/scrim.d.ts +5 -3
- package/lib/typescript/src/primitives/scrim.d.ts.map +1 -1
- package/lib/typescript/src/providers/panel-ui-provider.d.ts.map +1 -1
- package/package.json +11 -5
- package/src/components/badge/index.tsx +4 -1
- package/src/components/bar-chart/index.tsx +40 -3
- package/src/components/bottom-sheet/index.tsx +6 -3
- package/src/components/button/index.tsx +15 -21
- package/src/components/carousel/carousel-lifecycle.ts +74 -0
- package/src/components/carousel/index.tsx +118 -41
- package/src/components/checkbox/index.tsx +1 -2
- package/src/components/context-menu/context-menu-invocation.ts +44 -0
- package/src/components/context-menu/index.tsx +113 -13
- package/src/components/dialog/index.tsx +6 -3
- package/src/components/drawer/index.tsx +6 -3
- package/src/components/fab/index.tsx +8 -12
- package/src/components/field/index.tsx +52 -10
- package/src/components/flow/flow-accessibility.ts +123 -0
- package/src/components/flow/flow-identifiers.ts +98 -0
- package/src/components/flow/index.tsx +191 -69
- package/src/components/form/use-form.ts +101 -65
- package/src/components/heatmap-chart/index.tsx +2 -3
- package/src/components/hex-chart/index.tsx +2 -3
- package/src/components/kpi/index.tsx +3 -1
- package/src/components/line-chart/index.tsx +3 -2
- package/src/components/map/index.tsx +110 -7
- package/src/components/map/map-accessibility.ts +41 -0
- package/src/components/map/maplibre.ts +2 -0
- package/src/components/menu/index.tsx +4 -1
- package/src/components/message-scroller/index.tsx +39 -7
- package/src/components/message-scroller/message-scroller-math.ts +24 -0
- package/src/components/number-input/index.tsx +1 -26
- package/src/components/number-input/number-input-math.ts +50 -0
- package/src/components/plot/index.tsx +2 -1
- package/src/components/popover/index.tsx +3 -0
- package/src/components/radio-group/index.tsx +1 -2
- package/src/components/selection-mode/index.tsx +37 -30
- package/src/components/selection-mode/selection-mode-contracts.ts +53 -0
- package/src/components/signature/index.tsx +107 -20
- package/src/components/signature/signature-accessibility.ts +67 -0
- package/src/components/slider/index.tsx +28 -18
- package/src/components/sortable/index.tsx +12 -64
- package/src/components/sortable/reorder.ts +88 -0
- package/src/components/swipe/index.tsx +7 -4
- package/src/components/switch/index.tsx +42 -6
- package/src/components/time-picker/accessibility.ts +24 -0
- package/src/components/time-picker/index.tsx +48 -2
- package/src/components/waterfall-chart/index.tsx +1589 -0
- package/src/index.ts +25 -0
- package/src/primitives/modal-isolation-store.ts +31 -0
- package/src/primitives/portal.tsx +34 -4
- package/src/primitives/scrim.tsx +127 -7
- package/src/providers/panel-ui-provider.tsx +41 -9
- package/theme.css +30 -3
package/src/index.ts
CHANGED
|
@@ -209,6 +209,8 @@ export {
|
|
|
209
209
|
type FlowControlsProps,
|
|
210
210
|
type FlowMiniMapProps,
|
|
211
211
|
type FlowConnection,
|
|
212
|
+
type FlowEndpoint,
|
|
213
|
+
type FlowEndpointReference,
|
|
212
214
|
type FlowViewport,
|
|
213
215
|
type FlowNodePosition,
|
|
214
216
|
type FlowEdgeVariant,
|
|
@@ -304,6 +306,7 @@ export {
|
|
|
304
306
|
type MapArcProps,
|
|
305
307
|
type MapGeoJSONProps,
|
|
306
308
|
type MapClusterProps,
|
|
309
|
+
type MapFeatureAccessibility,
|
|
307
310
|
type MapHeatmapProps,
|
|
308
311
|
type MapUserLocationProps,
|
|
309
312
|
type BasemapSource,
|
|
@@ -1095,6 +1098,28 @@ export {
|
|
|
1095
1098
|
type TypographyParagraphProps,
|
|
1096
1099
|
type TypographyCodeProps,
|
|
1097
1100
|
} from './components/typography';
|
|
1101
|
+
export {
|
|
1102
|
+
WaterfallChart,
|
|
1103
|
+
useWaterfallChart,
|
|
1104
|
+
waterfallSteps,
|
|
1105
|
+
type WaterfallChartProps,
|
|
1106
|
+
type WaterfallChartHandle,
|
|
1107
|
+
type WaterfallChartHeaderProps,
|
|
1108
|
+
type WaterfallChartGridProps,
|
|
1109
|
+
type WaterfallChartBarsProps,
|
|
1110
|
+
type WaterfallChartConnectorsProps,
|
|
1111
|
+
type WaterfallChartValuesProps,
|
|
1112
|
+
type WaterfallChartSkeletonProps,
|
|
1113
|
+
type WaterfallChartXAxisProps,
|
|
1114
|
+
type WaterfallChartYAxisProps,
|
|
1115
|
+
type WaterfallChartTooltipProps,
|
|
1116
|
+
type WaterfallChartLegendProps,
|
|
1117
|
+
type WaterfallChartStatus,
|
|
1118
|
+
type WaterfallChartOrientation,
|
|
1119
|
+
type WaterfallDatum,
|
|
1120
|
+
type WaterfallStep,
|
|
1121
|
+
type WaterfallKind,
|
|
1122
|
+
} from './components/waterfall-chart';
|
|
1098
1123
|
|
|
1099
1124
|
// Icons
|
|
1100
1125
|
export {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Tracks modal owners without unisolating the app while a nested modal remains. */
|
|
2
|
+
export class ModalIsolationStore {
|
|
3
|
+
private owners = new Set<string>();
|
|
4
|
+
private listeners = new Set<() => void>();
|
|
5
|
+
|
|
6
|
+
acquire = (owner: string): (() => void) => {
|
|
7
|
+
const wasActive = this.getSnapshot();
|
|
8
|
+
this.owners.add(owner);
|
|
9
|
+
if (this.getSnapshot() !== wasActive) this.emit();
|
|
10
|
+
|
|
11
|
+
let released = false;
|
|
12
|
+
return () => {
|
|
13
|
+
if (released) return;
|
|
14
|
+
released = true;
|
|
15
|
+
const activeBeforeRelease = this.getSnapshot();
|
|
16
|
+
this.owners.delete(owner);
|
|
17
|
+
if (this.getSnapshot() !== activeBeforeRelease) this.emit();
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
subscribe = (listener: () => void): (() => void) => {
|
|
22
|
+
this.listeners.add(listener);
|
|
23
|
+
return () => this.listeners.delete(listener);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
getSnapshot = (): boolean => this.owners.size > 0;
|
|
27
|
+
|
|
28
|
+
private emit() {
|
|
29
|
+
this.listeners.forEach((listener) => listener());
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
useSyncExternalStore,
|
|
8
8
|
type ReactNode,
|
|
9
9
|
} from 'react';
|
|
10
|
+
import { ModalIsolationStore } from './modal-isolation-store';
|
|
10
11
|
|
|
11
12
|
type PortalMap = ReadonlyMap<string, ReactNode>;
|
|
12
13
|
|
|
@@ -39,13 +40,18 @@ class PortalStore {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
const PortalContext = createContext<PortalStore | null>(null);
|
|
43
|
+
const ModalIsolationContext = createContext<ModalIsolationStore | null>(null);
|
|
42
44
|
|
|
43
45
|
export function PortalProvider({ children }: { children: ReactNode }) {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
+
const portalStoreRef = useRef<PortalStore | null>(null);
|
|
47
|
+
const modalStoreRef = useRef<ModalIsolationStore | null>(null);
|
|
48
|
+
portalStoreRef.current ??= new PortalStore();
|
|
49
|
+
modalStoreRef.current ??= new ModalIsolationStore();
|
|
46
50
|
return (
|
|
47
|
-
<PortalContext.Provider value={
|
|
48
|
-
{
|
|
51
|
+
<PortalContext.Provider value={portalStoreRef.current}>
|
|
52
|
+
<ModalIsolationContext.Provider value={modalStoreRef.current}>
|
|
53
|
+
{children}
|
|
54
|
+
</ModalIsolationContext.Provider>
|
|
49
55
|
</PortalContext.Provider>
|
|
50
56
|
);
|
|
51
57
|
}
|
|
@@ -71,6 +77,30 @@ export function Portal({ children }: { children: ReactNode }) {
|
|
|
71
77
|
return null;
|
|
72
78
|
}
|
|
73
79
|
|
|
80
|
+
/** A portal that owns modal focus until it unmounts. */
|
|
81
|
+
export function ModalPortal({ children }: { children: ReactNode }) {
|
|
82
|
+
const store = useModalIsolationStore('ModalPortal');
|
|
83
|
+
const owner = useId();
|
|
84
|
+
|
|
85
|
+
useEffect(() => store.acquire(owner), [owner, store]);
|
|
86
|
+
|
|
87
|
+
return <Portal>{children}</Portal>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Whether any modal portal currently owns focus. */
|
|
91
|
+
export function useModalIsolationActive(): boolean {
|
|
92
|
+
const store = useModalIsolationStore('useModalIsolationActive');
|
|
93
|
+
return useSyncExternalStore(store.subscribe, store.getSnapshot);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function useModalIsolationStore(component: string): ModalIsolationStore {
|
|
97
|
+
const store = useContext(ModalIsolationContext);
|
|
98
|
+
if (!store) {
|
|
99
|
+
throw new Error(`${component} must be used within a <PanelUIProvider>`);
|
|
100
|
+
}
|
|
101
|
+
return store;
|
|
102
|
+
}
|
|
103
|
+
|
|
74
104
|
/** Mount point for portaled content. PanelUIProvider renders one automatically. */
|
|
75
105
|
export function PortalHost() {
|
|
76
106
|
const store = usePortalStore('PortalHost');
|
package/src/primitives/scrim.tsx
CHANGED
|
@@ -10,17 +10,20 @@
|
|
|
10
10
|
* that never blurs anything installs nothing. When it is asked for and the
|
|
11
11
|
* package is missing, this falls back to a dim rather than failing: a blur you
|
|
12
12
|
* cannot draw is better shown as a darkened screen than as a crash.
|
|
13
|
+
* Reduce Transparency replaces the blur with an opaque, tint-aware surface.
|
|
13
14
|
*
|
|
14
15
|
* It fills its parent and does not intercept touches itself — the overlay layers
|
|
15
16
|
* its own dismiss `Pressable` over it — so it is purely the visual backdrop.
|
|
16
17
|
*/
|
|
17
|
-
import { type ComponentType } from 'react';
|
|
18
|
-
import { StyleSheet, View, type ViewProps } from 'react-native';
|
|
18
|
+
import { useEffect, useState, type ComponentType } from 'react';
|
|
19
|
+
import { AccessibilityInfo, StyleSheet, View, type ViewProps } from 'react-native';
|
|
19
20
|
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
|
20
21
|
|
|
22
|
+
type BlurTint = 'light' | 'dark' | 'default' | 'systemMaterial';
|
|
23
|
+
|
|
21
24
|
interface BlurViewProps {
|
|
22
25
|
intensity?: number;
|
|
23
|
-
tint?:
|
|
26
|
+
tint?: BlurTint;
|
|
24
27
|
style?: unknown;
|
|
25
28
|
children?: React.ReactNode;
|
|
26
29
|
}
|
|
@@ -43,16 +46,112 @@ const BlurView: ComponentType<BlurViewProps> | null = (() => {
|
|
|
43
46
|
/** True when a real blur can be drawn — for a caller that wants to know. */
|
|
44
47
|
export const hasBlur = BlurView !== null;
|
|
45
48
|
|
|
49
|
+
type ReduceTransparencySource = {
|
|
50
|
+
isReduceTransparencyEnabled?: () => Promise<boolean>;
|
|
51
|
+
addEventListener?: (
|
|
52
|
+
event: 'reduceTransparencyChanged',
|
|
53
|
+
listener: (enabled: boolean) => void
|
|
54
|
+
) => { remove?: () => void } | undefined;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
function observeReduceTransparency(
|
|
58
|
+
source: ReduceTransparencySource,
|
|
59
|
+
onChange: (enabled: boolean) => void
|
|
60
|
+
) {
|
|
61
|
+
let active = true;
|
|
62
|
+
let changed = false;
|
|
63
|
+
const update = (enabled: boolean) => {
|
|
64
|
+
changed = true;
|
|
65
|
+
if (active) onChange(Boolean(enabled));
|
|
66
|
+
};
|
|
67
|
+
/*
|
|
68
|
+
* A platform that cannot report the preference has no preference to respect,
|
|
69
|
+
* so blur stays available. A platform that has the API and failed to answer
|
|
70
|
+
* might have it switched on, and that is the case worth being careful about.
|
|
71
|
+
*/
|
|
72
|
+
const fallback = (queryable: boolean) => () => {
|
|
73
|
+
if (active && !changed) onChange(queryable);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
let subscription: { remove?: () => void } | undefined;
|
|
77
|
+
try {
|
|
78
|
+
subscription = source.addEventListener?.('reduceTransparencyChanged', update);
|
|
79
|
+
} catch {
|
|
80
|
+
// Some web and test shims expose AccessibilityInfo without every event.
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
if (typeof source.isReduceTransparencyEnabled !== 'function') fallback(false)();
|
|
85
|
+
else {
|
|
86
|
+
void source.isReduceTransparencyEnabled().then((enabled) => {
|
|
87
|
+
// A preference event that arrived while the query was pending is newer.
|
|
88
|
+
if (active && !changed) update(enabled);
|
|
89
|
+
}, fallback(true));
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
fallback(true)();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return () => {
|
|
96
|
+
active = false;
|
|
97
|
+
try {
|
|
98
|
+
subscription?.remove?.();
|
|
99
|
+
} catch {
|
|
100
|
+
// Cleanup stays safe for partial platform shims.
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The last answer the platform gave, shared by every scrim in the process.
|
|
107
|
+
*
|
|
108
|
+
* The query is asynchronous and the preference belongs to the device, not to
|
|
109
|
+
* any one overlay. Without this, each overlay opens not knowing it and draws
|
|
110
|
+
* opaque until the answer arrives — a flash on every dialog for the many people
|
|
111
|
+
* who have the preference off. Remembered here, only the first overlay of a
|
|
112
|
+
* launch can see it.
|
|
113
|
+
*/
|
|
114
|
+
let knownReduceTransparency: boolean | null = null;
|
|
115
|
+
|
|
116
|
+
function useReduceTransparency() {
|
|
117
|
+
// Not knowing is deliberately conservative: draw opaque until the platform
|
|
118
|
+
// says blur is allowed, rather than flashing blur at someone who opted out.
|
|
119
|
+
const [enabled, setEnabled] = useState<boolean | null>(knownReduceTransparency);
|
|
120
|
+
useEffect(
|
|
121
|
+
() =>
|
|
122
|
+
observeReduceTransparency(AccessibilityInfo, (next) => {
|
|
123
|
+
knownReduceTransparency = next;
|
|
124
|
+
setEnabled(next);
|
|
125
|
+
}),
|
|
126
|
+
[]
|
|
127
|
+
);
|
|
128
|
+
return enabled;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
type ScrimMode = 'blur' | 'dim' | 'opaque';
|
|
132
|
+
|
|
133
|
+
function scrimMode(blur: boolean, canBlur: boolean, reduceTransparency: boolean | null): ScrimMode {
|
|
134
|
+
if (!blur) return 'dim';
|
|
135
|
+
if (reduceTransparency !== false) return 'opaque';
|
|
136
|
+
return canBlur ? 'blur' : 'dim';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function opaqueClassName(tint: BlurTint) {
|
|
140
|
+
if (tint === 'light') return 'bg-white';
|
|
141
|
+
if (tint === 'dark') return 'bg-black';
|
|
142
|
+
return 'bg-background';
|
|
143
|
+
}
|
|
144
|
+
|
|
46
145
|
export interface ScrimProps extends Omit<ViewProps, 'children'> {
|
|
47
146
|
/** Frost the backdrop instead of dimming it. Falls back to a dim if it can't. */
|
|
48
147
|
blur?: boolean;
|
|
49
148
|
/** Blur strength, 0–100. */
|
|
50
149
|
intensity?: number;
|
|
51
150
|
/** Which way the blur tints. */
|
|
52
|
-
tint?:
|
|
151
|
+
tint?: BlurTint;
|
|
53
152
|
/**
|
|
54
|
-
* The dim used when not blurring
|
|
55
|
-
*
|
|
153
|
+
* The dim used when not blurring, when blur is unavailable, and over the
|
|
154
|
+
* opaque Reduce Transparency fallback. A popover passes a lighter one than a dialog.
|
|
56
155
|
*/
|
|
57
156
|
dimClassName?: string;
|
|
58
157
|
}
|
|
@@ -65,7 +164,10 @@ export function Scrim({
|
|
|
65
164
|
style,
|
|
66
165
|
...props
|
|
67
166
|
}: ScrimProps) {
|
|
68
|
-
|
|
167
|
+
const reduceTransparency = useReduceTransparency();
|
|
168
|
+
const mode = scrimMode(blur, BlurView !== null, reduceTransparency);
|
|
169
|
+
|
|
170
|
+
if (mode === 'blur' && BlurView) {
|
|
69
171
|
return (
|
|
70
172
|
<Animated.View
|
|
71
173
|
entering={FadeIn.duration(180)}
|
|
@@ -81,6 +183,24 @@ export function Scrim({
|
|
|
81
183
|
);
|
|
82
184
|
}
|
|
83
185
|
|
|
186
|
+
if (mode === 'opaque') {
|
|
187
|
+
return (
|
|
188
|
+
<Animated.View
|
|
189
|
+
entering={FadeIn.duration(180)}
|
|
190
|
+
exiting={FadeOut.duration(150)}
|
|
191
|
+
style={[StyleSheet.absoluteFill, style]}
|
|
192
|
+
{...props}
|
|
193
|
+
>
|
|
194
|
+
<View
|
|
195
|
+
pointerEvents="none"
|
|
196
|
+
style={StyleSheet.absoluteFill}
|
|
197
|
+
className={opaqueClassName(tint)}
|
|
198
|
+
/>
|
|
199
|
+
<View pointerEvents="none" style={StyleSheet.absoluteFill} className={dimClassName} />
|
|
200
|
+
</Animated.View>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
84
204
|
return (
|
|
85
205
|
<Animated.View
|
|
86
206
|
entering={FadeIn.duration(150)}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { Fragment, type ComponentType, type ReactNode } from 'react';
|
|
2
|
-
import { StyleSheet, View } from 'react-native';
|
|
2
|
+
import { Platform, StyleSheet, View } from 'react-native';
|
|
3
3
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
PortalHost,
|
|
6
|
+
PortalProvider,
|
|
7
|
+
useModalIsolationActive,
|
|
8
|
+
} from '../primitives/portal';
|
|
5
9
|
import { ToastViewport } from '../components/toast';
|
|
6
10
|
import { cn } from '../utils/cn';
|
|
7
11
|
|
|
@@ -58,19 +62,47 @@ export function PanelUIProvider({
|
|
|
58
62
|
{/* Outermost of ours, so every field below it can avoid the keyboard.
|
|
59
63
|
A no-op Fragment when the controller is not installed. */}
|
|
60
64
|
<KeyboardProvider>
|
|
61
|
-
<
|
|
62
|
-
<
|
|
65
|
+
<PortalProvider>
|
|
66
|
+
<ProviderSurface
|
|
67
|
+
background={background}
|
|
68
|
+
className={className}
|
|
69
|
+
>
|
|
63
70
|
{children}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
<PortalHost />
|
|
67
|
-
</PortalProvider>
|
|
68
|
-
</View>
|
|
71
|
+
</ProviderSurface>
|
|
72
|
+
</PortalProvider>
|
|
69
73
|
</KeyboardProvider>
|
|
70
74
|
</GestureHandlerRootView>
|
|
71
75
|
);
|
|
72
76
|
}
|
|
73
77
|
|
|
78
|
+
function ProviderSurface({
|
|
79
|
+
children,
|
|
80
|
+
className,
|
|
81
|
+
background,
|
|
82
|
+
}: Required<Pick<PanelUIProviderProps, 'children' | 'background'>> &
|
|
83
|
+
Pick<PanelUIProviderProps, 'className'>) {
|
|
84
|
+
const modalActive = useModalIsolationActive();
|
|
85
|
+
const isolateApp = Platform.OS === 'android' && modalActive;
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<View className={cn('flex-1', background && 'bg-background', className)}>
|
|
89
|
+
{/* This wrapper is the app accessibility boundary. PortalHost stays its
|
|
90
|
+
sibling, so hiding the background can never hide the active modal. */}
|
|
91
|
+
<View
|
|
92
|
+
collapsable={false}
|
|
93
|
+
style={styles.content}
|
|
94
|
+
importantForAccessibility={isolateApp ? 'no-hide-descendants' : 'auto'}
|
|
95
|
+
>
|
|
96
|
+
{children}
|
|
97
|
+
</View>
|
|
98
|
+
{/* Sits before PortalHost so it can portal into it. */}
|
|
99
|
+
<ToastViewport />
|
|
100
|
+
<PortalHost />
|
|
101
|
+
</View>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
74
105
|
const styles = StyleSheet.create({
|
|
75
106
|
root: { flex: 1 },
|
|
107
|
+
content: { flex: 1 },
|
|
76
108
|
});
|
package/theme.css
CHANGED
|
@@ -78,12 +78,16 @@
|
|
|
78
78
|
--color-accent-foreground: unset;
|
|
79
79
|
--color-destructive: unset;
|
|
80
80
|
--color-destructive-foreground: unset;
|
|
81
|
+
--color-destructive-solid-foreground: unset;
|
|
81
82
|
--color-info: unset;
|
|
82
83
|
--color-info-foreground: unset;
|
|
84
|
+
--color-info-solid-foreground: unset;
|
|
83
85
|
--color-success: unset;
|
|
84
86
|
--color-success-foreground: unset;
|
|
87
|
+
--color-success-solid-foreground: unset;
|
|
85
88
|
--color-warning: unset;
|
|
86
89
|
--color-warning-foreground: unset;
|
|
90
|
+
--color-warning-solid-foreground: unset;
|
|
87
91
|
--color-border: unset;
|
|
88
92
|
--color-input: unset;
|
|
89
93
|
--color-ring: unset;
|
|
@@ -226,15 +230,19 @@
|
|
|
226
230
|
/* status: {hue}-500, foreground {hue}-700 */
|
|
227
231
|
--color-destructive: #ef4444;
|
|
228
232
|
--color-destructive-foreground: #b91c1c;
|
|
233
|
+
--color-destructive-solid-foreground: #171717;
|
|
229
234
|
|
|
230
235
|
--color-info: #3b82f6;
|
|
231
236
|
--color-info-foreground: #1d4ed8;
|
|
237
|
+
--color-info-solid-foreground: #171717;
|
|
232
238
|
|
|
233
239
|
--color-success: #10b981;
|
|
234
240
|
--color-success-foreground: #047857;
|
|
241
|
+
--color-success-solid-foreground: #171717;
|
|
235
242
|
|
|
236
243
|
--color-warning: #f59e0b;
|
|
237
244
|
--color-warning-foreground: #b45309;
|
|
245
|
+
--color-warning-solid-foreground: #171717;
|
|
238
246
|
|
|
239
247
|
/* border: black 8%, input: black 10% */
|
|
240
248
|
--color-border: rgba(0, 0, 0, 0.08);
|
|
@@ -327,15 +335,19 @@
|
|
|
327
335
|
/* color-mix(red-500 90%, white), foreground {hue}-400 */
|
|
328
336
|
--color-destructive: #f15757;
|
|
329
337
|
--color-destructive-foreground: #f87171;
|
|
338
|
+
--color-destructive-solid-foreground: #171717;
|
|
330
339
|
|
|
331
340
|
--color-info: #3b82f6;
|
|
332
341
|
--color-info-foreground: #60a5fa;
|
|
342
|
+
--color-info-solid-foreground: #171717;
|
|
333
343
|
|
|
334
344
|
--color-success: #10b981;
|
|
335
345
|
--color-success-foreground: #34d399;
|
|
346
|
+
--color-success-solid-foreground: #171717;
|
|
336
347
|
|
|
337
348
|
--color-warning: #f59e0b;
|
|
338
349
|
--color-warning-foreground: #fbbf24;
|
|
350
|
+
--color-warning-solid-foreground: #171717;
|
|
339
351
|
|
|
340
352
|
/* border: white 6%, input: white 8% */
|
|
341
353
|
--color-border: rgba(255, 255, 255, 0.06);
|
|
@@ -446,15 +458,19 @@
|
|
|
446
458
|
|
|
447
459
|
--color-destructive: #eb5757;
|
|
448
460
|
--color-destructive-foreground: #c62b30;
|
|
461
|
+
--color-destructive-solid-foreground: #08090a;
|
|
449
462
|
|
|
450
463
|
--color-info: #4ea7fc;
|
|
451
464
|
--color-info-foreground: #0b6ec4;
|
|
465
|
+
--color-info-solid-foreground: #08090a;
|
|
452
466
|
|
|
453
467
|
--color-success: #27a644;
|
|
454
468
|
--color-success-foreground: #1c7a32;
|
|
469
|
+
--color-success-solid-foreground: #08090a;
|
|
455
470
|
|
|
456
471
|
--color-warning: #f2c94c;
|
|
457
472
|
--color-warning-foreground: #8a6d0b;
|
|
473
|
+
--color-warning-solid-foreground: #08090a;
|
|
458
474
|
|
|
459
475
|
--color-border: rgba(0, 0, 0, 0.08);
|
|
460
476
|
--color-input: rgba(0, 0, 0, 0.12);
|
|
@@ -531,15 +547,19 @@
|
|
|
531
547
|
|
|
532
548
|
--color-destructive: #eb5757;
|
|
533
549
|
--color-destructive-foreground: #ff8a8a;
|
|
550
|
+
--color-destructive-solid-foreground: #08090a;
|
|
534
551
|
|
|
535
552
|
--color-info: #4ea7fc;
|
|
536
553
|
--color-info-foreground: #8ec5ff;
|
|
554
|
+
--color-info-solid-foreground: #08090a;
|
|
537
555
|
|
|
538
556
|
--color-success: #27a644;
|
|
539
557
|
--color-success-foreground: #4cc061;
|
|
558
|
+
--color-success-solid-foreground: #08090a;
|
|
540
559
|
|
|
541
560
|
--color-warning: #f2c94c;
|
|
542
561
|
--color-warning-foreground: #f5d97a;
|
|
562
|
+
--color-warning-solid-foreground: #08090a;
|
|
543
563
|
|
|
544
564
|
/* The hairlines are opaque, not translucent: the surface ladder is only
|
|
545
565
|
a few points wide, so a border mixed with whatever is under it would
|
|
@@ -606,10 +626,9 @@
|
|
|
606
626
|
/* the sunken band under a card or dialog: black at 3.5% */
|
|
607
627
|
--color-inset: rgba(0, 0, 0, 0.035);
|
|
608
628
|
|
|
609
|
-
/*
|
|
610
|
-
darker ramp step so the contrast ratio holds. */
|
|
629
|
+
/* Dark green ink preserves the brand fill and clears the body-text floor. */
|
|
611
630
|
--color-primary: #24b47e;
|
|
612
|
-
--color-primary-foreground: #
|
|
631
|
+
--color-primary-foreground: #052e1b;
|
|
613
632
|
|
|
614
633
|
--color-secondary: rgba(0, 0, 0, 0.05);
|
|
615
634
|
--color-secondary-foreground: #171717;
|
|
@@ -622,15 +641,19 @@
|
|
|
622
641
|
|
|
623
642
|
--color-destructive: #ef4444;
|
|
624
643
|
--color-destructive-foreground: #b91c1c;
|
|
644
|
+
--color-destructive-solid-foreground: #171717;
|
|
625
645
|
|
|
626
646
|
--color-info: #3b82f6;
|
|
627
647
|
--color-info-foreground: #1d4ed8;
|
|
648
|
+
--color-info-solid-foreground: #171717;
|
|
628
649
|
|
|
629
650
|
--color-success: #24b47e;
|
|
630
651
|
--color-success-foreground: #16794c;
|
|
652
|
+
--color-success-solid-foreground: #171717;
|
|
631
653
|
|
|
632
654
|
--color-warning: #f59e0b;
|
|
633
655
|
--color-warning-foreground: #b45309;
|
|
656
|
+
--color-warning-solid-foreground: #171717;
|
|
634
657
|
|
|
635
658
|
--color-border: rgba(0, 0, 0, 0.08);
|
|
636
659
|
--color-input: rgba(0, 0, 0, 0.1);
|
|
@@ -707,15 +730,19 @@
|
|
|
707
730
|
|
|
708
731
|
--color-destructive: #f15757;
|
|
709
732
|
--color-destructive-foreground: #f87171;
|
|
733
|
+
--color-destructive-solid-foreground: #171717;
|
|
710
734
|
|
|
711
735
|
--color-info: #3b82f6;
|
|
712
736
|
--color-info-foreground: #60a5fa;
|
|
737
|
+
--color-info-solid-foreground: #171717;
|
|
713
738
|
|
|
714
739
|
--color-success: #3ecf8e;
|
|
715
740
|
--color-success-foreground: #4ade80;
|
|
741
|
+
--color-success-solid-foreground: #171717;
|
|
716
742
|
|
|
717
743
|
--color-warning: #f59e0b;
|
|
718
744
|
--color-warning-foreground: #fbbf24;
|
|
745
|
+
--color-warning-solid-foreground: #171717;
|
|
719
746
|
|
|
720
747
|
--color-border: rgba(255, 255, 255, 0.07);
|
|
721
748
|
--color-input: rgba(255, 255, 255, 0.1);
|