@cdx-ui/components 0.3.4 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/coverage.svg +1 -1
- package/docs/components/bar-chart/content.md +703 -0
- package/docs/components/content.md +9 -1
- package/docs/components/line-chart/content.md +567 -0
- package/docs/components/pie-chart/content.md +493 -0
- package/docs/components/progress-segmented/content.md +1 -1
- package/docs/components/select/content.md +144 -2
- package/docs/components/stepper/content.md +599 -0
- package/docs/components/system-alert/content.md +533 -0
- package/docs/foundations/expressions/icons/content.md +1 -1
- package/docs/foundations/styles/tokens/all-tokens/content.md +2 -1
- package/docs/llms.txt +10 -0
- package/docs/patterns/content.md +1 -0
- package/docs/patterns/dashboard/content.md +423 -19
- package/docs/patterns/insights/content.md +590 -0
- package/docs/resources/release-notes/0.3.4/content.md +27 -0
- package/docs/resources/release-notes/0.3.5/content.md +7 -0
- package/docs/resources/release-notes/content.md +2 -0
- package/lib/commonjs/components/BarChart/geometry.js +1130 -0
- package/lib/commonjs/components/BarChart/geometry.js.map +1 -0
- package/lib/commonjs/components/BarChart/index.js +942 -0
- package/lib/commonjs/components/BarChart/index.js.map +1 -0
- package/lib/commonjs/components/BarChart/styles.js +26 -0
- package/lib/commonjs/components/BarChart/styles.js.map +1 -0
- package/lib/commonjs/components/Button/index.js +2 -2
- package/lib/commonjs/components/Button/index.js.map +1 -1
- package/lib/commonjs/components/Chip/styles.js +3 -1
- package/lib/commonjs/components/Chip/styles.js.map +1 -1
- package/lib/commonjs/components/LineChart/geometry.js +698 -0
- package/lib/commonjs/components/LineChart/geometry.js.map +1 -0
- package/lib/commonjs/components/LineChart/index.js +747 -0
- package/lib/commonjs/components/LineChart/index.js.map +1 -0
- package/lib/commonjs/components/LineChart/styles.js +25 -0
- package/lib/commonjs/components/LineChart/styles.js.map +1 -0
- package/lib/commonjs/components/PieChart/geometry.js +216 -0
- package/lib/commonjs/components/PieChart/geometry.js.map +1 -0
- package/lib/commonjs/components/PieChart/index.js +476 -0
- package/lib/commonjs/components/PieChart/index.js.map +1 -0
- package/lib/commonjs/components/PieChart/styles.js +88 -0
- package/lib/commonjs/components/PieChart/styles.js.map +1 -0
- package/lib/commonjs/components/ProgressCircle/ProgressCircleRing.js +9 -31
- package/lib/commonjs/components/ProgressCircle/ProgressCircleRing.js.map +1 -1
- package/lib/commonjs/components/ProgressCircle/ProgressRing.js +52 -0
- package/lib/commonjs/components/ProgressCircle/ProgressRing.js.map +1 -0
- package/lib/commonjs/components/ScrollShadow/useScrollShadow.js +8 -7
- package/lib/commonjs/components/ScrollShadow/useScrollShadow.js.map +1 -1
- package/lib/commonjs/components/Stepper/index.js +369 -0
- package/lib/commonjs/components/Stepper/index.js.map +1 -0
- package/lib/commonjs/components/Stepper/styles.js +211 -0
- package/lib/commonjs/components/Stepper/styles.js.map +1 -0
- package/lib/commonjs/components/Stepper/useStepperLabelFit.js +22 -0
- package/lib/commonjs/components/Stepper/useStepperLabelFit.js.map +1 -0
- package/lib/commonjs/components/Stepper/useStepperLabelFit.web.js +52 -0
- package/lib/commonjs/components/Stepper/useStepperLabelFit.web.js.map +1 -0
- package/lib/commonjs/components/Stepper/useStepperRingColors.js +31 -0
- package/lib/commonjs/components/Stepper/useStepperRingColors.js.map +1 -0
- package/lib/commonjs/components/SystemAlert/animation.js +218 -0
- package/lib/commonjs/components/SystemAlert/animation.js.map +1 -0
- package/lib/commonjs/components/SystemAlert/index.js +525 -0
- package/lib/commonjs/components/SystemAlert/index.js.map +1 -0
- package/lib/commonjs/components/SystemAlert/styles.js +178 -0
- package/lib/commonjs/components/SystemAlert/styles.js.map +1 -0
- package/lib/commonjs/components/SystemAlert/useSystemAlertStatusBarStyle.js +53 -0
- package/lib/commonjs/components/SystemAlert/useSystemAlertStatusBarStyle.js.map +1 -0
- package/lib/commonjs/components/Tabs/index.js +4 -0
- package/lib/commonjs/components/Tabs/index.js.map +1 -1
- package/lib/commonjs/components/Tabs/styles.js +1 -1
- package/lib/commonjs/components/Tabs/styles.js.map +1 -1
- package/lib/commonjs/components/chart/ChartInspector.js +610 -0
- package/lib/commonjs/components/chart/ChartInspector.js.map +1 -0
- package/lib/commonjs/components/chart/format.js +93 -0
- package/lib/commonjs/components/chart/format.js.map +1 -0
- package/lib/commonjs/components/chart/index.js +2 -0
- package/lib/commonjs/components/chart/index.js.map +1 -0
- package/lib/commonjs/components/chart/motion.js +97 -0
- package/lib/commonjs/components/chart/motion.js.map +1 -0
- package/lib/commonjs/components/chart/palette.js +178 -0
- package/lib/commonjs/components/chart/palette.js.map +1 -0
- package/lib/commonjs/components/chart/size.js +34 -0
- package/lib/commonjs/components/chart/size.js.map +1 -0
- package/lib/commonjs/components/chart/ticks.js +59 -0
- package/lib/commonjs/components/chart/ticks.js.map +1 -0
- package/lib/commonjs/components/chart/types.js +6 -0
- package/lib/commonjs/components/chart/types.js.map +1 -0
- package/lib/commonjs/components/chart/useChartHostWidth.js +27 -0
- package/lib/commonjs/components/chart/useChartHostWidth.js.map +1 -0
- package/lib/commonjs/components/index.js +60 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/figma/SystemAlert.Button.figma.js +28 -0
- package/lib/commonjs/figma/SystemAlert.Button.figma.js.map +1 -0
- package/lib/commonjs/figma/SystemAlert.Close.figma.js +22 -0
- package/lib/commonjs/figma/SystemAlert.Close.figma.js.map +1 -0
- package/lib/commonjs/figma/SystemAlert.Description.figma.js +20 -0
- package/lib/commonjs/figma/SystemAlert.Description.figma.js.map +1 -0
- package/lib/commonjs/figma/SystemAlert.IconButton.figma.js +31 -0
- package/lib/commonjs/figma/SystemAlert.IconButton.figma.js.map +1 -0
- package/lib/commonjs/figma/SystemAlert.figma.js +48 -0
- package/lib/commonjs/figma/SystemAlert.figma.js.map +1 -0
- package/lib/module/components/BarChart/geometry.js +1110 -0
- package/lib/module/components/BarChart/geometry.js.map +1 -0
- package/lib/module/components/BarChart/index.js +936 -0
- package/lib/module/components/BarChart/index.js.map +1 -0
- package/lib/module/components/BarChart/styles.js +22 -0
- package/lib/module/components/BarChart/styles.js.map +1 -0
- package/lib/module/components/Button/index.js +2 -2
- package/lib/module/components/Button/index.js.map +1 -1
- package/lib/module/components/Chip/styles.js +3 -1
- package/lib/module/components/Chip/styles.js.map +1 -1
- package/lib/module/components/LineChart/geometry.js +685 -0
- package/lib/module/components/LineChart/geometry.js.map +1 -0
- package/lib/module/components/LineChart/index.js +741 -0
- package/lib/module/components/LineChart/index.js.map +1 -0
- package/lib/module/components/LineChart/styles.js +21 -0
- package/lib/module/components/LineChart/styles.js.map +1 -0
- package/lib/module/components/PieChart/geometry.js +209 -0
- package/lib/module/components/PieChart/geometry.js.map +1 -0
- package/lib/module/components/PieChart/index.js +471 -0
- package/lib/module/components/PieChart/index.js.map +1 -0
- package/lib/module/components/PieChart/styles.js +84 -0
- package/lib/module/components/PieChart/styles.js.map +1 -0
- package/lib/module/components/ProgressCircle/ProgressCircleRing.js +10 -32
- package/lib/module/components/ProgressCircle/ProgressCircleRing.js.map +1 -1
- package/lib/module/components/ProgressCircle/ProgressRing.js +48 -0
- package/lib/module/components/ProgressCircle/ProgressRing.js.map +1 -0
- package/lib/module/components/ScrollShadow/useScrollShadow.js +8 -7
- package/lib/module/components/ScrollShadow/useScrollShadow.js.map +1 -1
- package/lib/module/components/Stepper/index.js +360 -0
- package/lib/module/components/Stepper/index.js.map +1 -0
- package/lib/module/components/Stepper/styles.js +206 -0
- package/lib/module/components/Stepper/styles.js.map +1 -0
- package/lib/module/components/Stepper/useStepperLabelFit.js +18 -0
- package/lib/module/components/Stepper/useStepperLabelFit.js.map +1 -0
- package/lib/module/components/Stepper/useStepperLabelFit.web.js +48 -0
- package/lib/module/components/Stepper/useStepperLabelFit.web.js.map +1 -0
- package/lib/module/components/Stepper/useStepperRingColors.js +28 -0
- package/lib/module/components/Stepper/useStepperRingColors.js.map +1 -0
- package/lib/module/components/SystemAlert/animation.js +213 -0
- package/lib/module/components/SystemAlert/animation.js.map +1 -0
- package/lib/module/components/SystemAlert/index.js +515 -0
- package/lib/module/components/SystemAlert/index.js.map +1 -0
- package/lib/module/components/SystemAlert/styles.js +174 -0
- package/lib/module/components/SystemAlert/styles.js.map +1 -0
- package/lib/module/components/SystemAlert/useSystemAlertStatusBarStyle.js +49 -0
- package/lib/module/components/SystemAlert/useSystemAlertStatusBarStyle.js.map +1 -0
- package/lib/module/components/Tabs/index.js +4 -0
- package/lib/module/components/Tabs/index.js.map +1 -1
- package/lib/module/components/Tabs/styles.js +1 -1
- package/lib/module/components/Tabs/styles.js.map +1 -1
- package/lib/module/components/chart/ChartInspector.js +600 -0
- package/lib/module/components/chart/ChartInspector.js.map +1 -0
- package/lib/module/components/chart/format.js +84 -0
- package/lib/module/components/chart/format.js.map +1 -0
- package/lib/module/components/chart/index.js +2 -0
- package/lib/module/components/chart/index.js.map +1 -0
- package/lib/module/components/chart/motion.js +89 -0
- package/lib/module/components/chart/motion.js.map +1 -0
- package/lib/module/components/chart/palette.js +168 -0
- package/lib/module/components/chart/palette.js.map +1 -0
- package/lib/module/components/chart/size.js +29 -0
- package/lib/module/components/chart/size.js.map +1 -0
- package/lib/module/components/chart/ticks.js +55 -0
- package/lib/module/components/chart/ticks.js.map +1 -0
- package/lib/module/components/chart/types.js +4 -0
- package/lib/module/components/chart/types.js.map +1 -0
- package/lib/module/components/chart/useChartHostWidth.js +23 -0
- package/lib/module/components/chart/useChartHostWidth.js.map +1 -0
- package/lib/module/components/index.js +5 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/figma/SystemAlert.Button.figma.js +22 -0
- package/lib/module/figma/SystemAlert.Button.figma.js.map +1 -0
- package/lib/module/figma/SystemAlert.Close.figma.js +18 -0
- package/lib/module/figma/SystemAlert.Close.figma.js.map +1 -0
- package/lib/module/figma/SystemAlert.Description.figma.js +14 -0
- package/lib/module/figma/SystemAlert.Description.figma.js.map +1 -0
- package/lib/module/figma/SystemAlert.IconButton.figma.js +25 -0
- package/lib/module/figma/SystemAlert.IconButton.figma.js.map +1 -0
- package/lib/module/figma/SystemAlert.figma.js +42 -0
- package/lib/module/figma/SystemAlert.figma.js.map +1 -0
- package/lib/typescript/components/BarChart/geometry.d.ts +236 -0
- package/lib/typescript/components/BarChart/geometry.d.ts.map +1 -0
- package/lib/typescript/components/BarChart/index.d.ts +193 -0
- package/lib/typescript/components/BarChart/index.d.ts.map +1 -0
- package/lib/typescript/components/BarChart/styles.d.ts +32 -0
- package/lib/typescript/components/BarChart/styles.d.ts.map +1 -0
- package/lib/typescript/components/Chip/styles.d.ts.map +1 -1
- package/lib/typescript/components/LineChart/geometry.d.ts +248 -0
- package/lib/typescript/components/LineChart/geometry.d.ts.map +1 -0
- package/lib/typescript/components/LineChart/index.d.ts +105 -0
- package/lib/typescript/components/LineChart/index.d.ts.map +1 -0
- package/lib/typescript/components/LineChart/styles.d.ts +12 -0
- package/lib/typescript/components/LineChart/styles.d.ts.map +1 -0
- package/lib/typescript/components/PieChart/geometry.d.ts +43 -0
- package/lib/typescript/components/PieChart/geometry.d.ts.map +1 -0
- package/lib/typescript/components/PieChart/index.d.ts +147 -0
- package/lib/typescript/components/PieChart/index.d.ts.map +1 -0
- package/lib/typescript/components/PieChart/styles.d.ts +27 -0
- package/lib/typescript/components/PieChart/styles.d.ts.map +1 -0
- package/lib/typescript/components/ProgressCircle/ProgressCircleRing.d.ts.map +1 -1
- package/lib/typescript/components/ProgressCircle/ProgressRing.d.ts +17 -0
- package/lib/typescript/components/ProgressCircle/ProgressRing.d.ts.map +1 -0
- package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts.map +1 -1
- package/lib/typescript/components/Stepper/index.d.ts +68 -0
- package/lib/typescript/components/Stepper/index.d.ts.map +1 -0
- package/lib/typescript/components/Stepper/styles.d.ts +46 -0
- package/lib/typescript/components/Stepper/styles.d.ts.map +1 -0
- package/lib/typescript/components/Stepper/useStepperLabelFit.d.ts +15 -0
- package/lib/typescript/components/Stepper/useStepperLabelFit.d.ts.map +1 -0
- package/lib/typescript/components/Stepper/useStepperLabelFit.web.d.ts +10 -0
- package/lib/typescript/components/Stepper/useStepperLabelFit.web.d.ts.map +1 -0
- package/lib/typescript/components/Stepper/useStepperRingColors.d.ts +6 -0
- package/lib/typescript/components/Stepper/useStepperRingColors.d.ts.map +1 -0
- package/lib/typescript/components/SystemAlert/animation.d.ts +56 -0
- package/lib/typescript/components/SystemAlert/animation.d.ts.map +1 -0
- package/lib/typescript/components/SystemAlert/index.d.ts +110 -0
- package/lib/typescript/components/SystemAlert/index.d.ts.map +1 -0
- package/lib/typescript/components/SystemAlert/styles.d.ts +38 -0
- package/lib/typescript/components/SystemAlert/styles.d.ts.map +1 -0
- package/lib/typescript/components/SystemAlert/useSystemAlertStatusBarStyle.d.ts +14 -0
- package/lib/typescript/components/SystemAlert/useSystemAlertStatusBarStyle.d.ts.map +1 -0
- package/lib/typescript/components/Tabs/index.d.ts.map +1 -1
- package/lib/typescript/components/chart/ChartInspector.d.ts +101 -0
- package/lib/typescript/components/chart/ChartInspector.d.ts.map +1 -0
- package/lib/typescript/components/chart/format.d.ts +16 -0
- package/lib/typescript/components/chart/format.d.ts.map +1 -0
- package/lib/typescript/components/chart/index.d.ts +3 -0
- package/lib/typescript/components/chart/index.d.ts.map +1 -0
- package/lib/typescript/components/chart/motion.d.ts +27 -0
- package/lib/typescript/components/chart/motion.d.ts.map +1 -0
- package/lib/typescript/components/chart/palette.d.ts +44 -0
- package/lib/typescript/components/chart/palette.d.ts.map +1 -0
- package/lib/typescript/components/chart/size.d.ts +25 -0
- package/lib/typescript/components/chart/size.d.ts.map +1 -0
- package/lib/typescript/components/chart/ticks.d.ts +3 -0
- package/lib/typescript/components/chart/ticks.d.ts.map +1 -0
- package/lib/typescript/components/chart/types.d.ts +163 -0
- package/lib/typescript/components/chart/types.d.ts.map +1 -0
- package/lib/typescript/components/chart/useChartHostWidth.d.ts +9 -0
- package/lib/typescript/components/chart/useChartHostWidth.d.ts.map +1 -0
- package/lib/typescript/components/index.d.ts +5 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/figma/SystemAlert.Button.figma.d.ts +11 -0
- package/lib/typescript/figma/SystemAlert.Button.figma.d.ts.map +1 -0
- package/lib/typescript/figma/SystemAlert.Close.figma.d.ts +11 -0
- package/lib/typescript/figma/SystemAlert.Close.figma.d.ts.map +1 -0
- package/lib/typescript/figma/SystemAlert.Description.figma.d.ts +8 -0
- package/lib/typescript/figma/SystemAlert.Description.figma.d.ts.map +1 -0
- package/lib/typescript/figma/SystemAlert.IconButton.figma.d.ts +11 -0
- package/lib/typescript/figma/SystemAlert.IconButton.figma.d.ts.map +1 -0
- package/lib/typescript/figma/SystemAlert.figma.d.ts +8 -0
- package/lib/typescript/figma/SystemAlert.figma.d.ts.map +1 -0
- package/package.json +5 -5
- package/src/components/BarChart/geometry.ts +1469 -0
- package/src/components/BarChart/index.tsx +1424 -0
- package/src/components/BarChart/styles.ts +31 -0
- package/src/components/Button/index.tsx +1 -1
- package/src/components/Chip/styles.ts +3 -1
- package/src/components/LineChart/geometry.ts +957 -0
- package/src/components/LineChart/index.tsx +1109 -0
- package/src/components/LineChart/styles.ts +26 -0
- package/src/components/PieChart/geometry.ts +295 -0
- package/src/components/PieChart/index.tsx +715 -0
- package/src/components/PieChart/styles.ts +101 -0
- package/src/components/ProgressCircle/ProgressCircleRing.tsx +10 -39
- package/src/components/ProgressCircle/ProgressRing.tsx +60 -0
- package/src/components/ScrollShadow/useScrollShadow.ts +12 -11
- package/src/components/Stepper/index.tsx +452 -0
- package/src/components/Stepper/styles.ts +234 -0
- package/src/components/Stepper/useStepperLabelFit.ts +21 -0
- package/src/components/Stepper/useStepperLabelFit.web.ts +53 -0
- package/src/components/Stepper/useStepperRingColors.ts +29 -0
- package/src/components/SystemAlert/animation.ts +252 -0
- package/src/components/SystemAlert/index.tsx +684 -0
- package/src/components/SystemAlert/styles.ts +212 -0
- package/src/components/SystemAlert/useSystemAlertStatusBarStyle.ts +57 -0
- package/src/components/Tabs/index.tsx +4 -0
- package/src/components/Tabs/styles.ts +1 -1
- package/src/components/chart/ChartInspector.tsx +726 -0
- package/src/components/chart/format.ts +95 -0
- package/src/components/chart/index.ts +24 -0
- package/src/components/chart/motion.ts +98 -0
- package/src/components/chart/palette.tsx +247 -0
- package/src/components/chart/size.ts +21 -0
- package/src/components/chart/ticks.ts +53 -0
- package/src/components/chart/types.ts +183 -0
- package/src/components/chart/useChartHostWidth.ts +26 -0
- package/src/components/index.ts +5 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useCallback, useEffect, useLayoutEffect, useState, type RefCallback } from 'react';
|
|
2
|
+
import type { View } from 'react-native';
|
|
3
|
+
import type { StepperLabelFit, StepperLabelFitResult } from './useStepperLabelFit';
|
|
4
|
+
|
|
5
|
+
export type { StepperLabelFit, StepperLabelFitResult } from './useStepperLabelFit';
|
|
6
|
+
|
|
7
|
+
interface FitDecision {
|
|
8
|
+
readonly key: string;
|
|
9
|
+
readonly fit: StepperLabelFit;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* CSS flex cannot keep min-content minimums only while they fit: `min-width: auto` overflows a
|
|
14
|
+
* tight row and `min-width: 0` breaks words even when there is room. So labels start at
|
|
15
|
+
* `'words'`, and if the root then overflows they switch to `'shrink'` before paint. The decision
|
|
16
|
+
* is kept until the root's width or the step count changes.
|
|
17
|
+
*/
|
|
18
|
+
export function useStepperLabelFit(enabled: boolean, count: number): StepperLabelFitResult {
|
|
19
|
+
const [node, setNode] = useState<HTMLElement | null>(null);
|
|
20
|
+
const [width, setWidth] = useState(0);
|
|
21
|
+
const [decision, setDecision] = useState<FitDecision | null>(null);
|
|
22
|
+
|
|
23
|
+
const key = `${String(width)}:${String(count)}`;
|
|
24
|
+
const fit: StepperLabelFit = decision?.key === key ? decision.fit : 'words';
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!enabled || node === null || typeof ResizeObserver === 'undefined') {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
const observer = new ResizeObserver(() => {
|
|
31
|
+
setWidth(node.clientWidth);
|
|
32
|
+
});
|
|
33
|
+
observer.observe(node);
|
|
34
|
+
return () => observer.disconnect();
|
|
35
|
+
}, [enabled, node]);
|
|
36
|
+
|
|
37
|
+
// No deps: label text can change without resizing the root, and the measurement is cheap.
|
|
38
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
39
|
+
useLayoutEffect(() => {
|
|
40
|
+
if (!enabled || node === null || fit !== 'words') {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (node.scrollWidth > node.clientWidth + 1) {
|
|
44
|
+
setDecision({ key, fit: 'shrink' });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const measureRef = useCallback<RefCallback<View>>((value) => {
|
|
49
|
+
setNode(value as unknown as HTMLElement | null);
|
|
50
|
+
}, []);
|
|
51
|
+
|
|
52
|
+
return { fit: enabled ? fit : 'shrink', measureRef: enabled ? measureRef : undefined };
|
|
53
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useCSSVariable } from 'uniwind';
|
|
2
|
+
|
|
3
|
+
/** Light-theme fallbacks when Uniwind CSS context is unavailable (e.g. Jest). */
|
|
4
|
+
const FALLBACKS = {
|
|
5
|
+
track: '#e9e9e9',
|
|
6
|
+
brand: '#346099',
|
|
7
|
+
neutral: '#616263',
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
function toColor(value: string | number | undefined, fallback: string): string {
|
|
11
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
return fallback;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Ring stroke colors for `Stepper.Indicator`: brand on the current step, neutral otherwise. */
|
|
18
|
+
export function useStepperRingColors(isCurrent: boolean) {
|
|
19
|
+
const trackToken = useCSSVariable('--color-stroke-secondary');
|
|
20
|
+
const brandToken = useCSSVariable('--color-surface-brand-strong');
|
|
21
|
+
const neutralToken = useCSSVariable('--color-surface-neutral-strong');
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
track: toColor(trackToken, FALLBACKS.track),
|
|
25
|
+
indicator: isCurrent
|
|
26
|
+
? toColor(brandToken, FALLBACKS.brand)
|
|
27
|
+
: toColor(neutralToken, FALLBACKS.neutral),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState, type RefObject } from 'react';
|
|
2
|
+
import { Platform, useWindowDimensions, type View } from 'react-native';
|
|
3
|
+
import {
|
|
4
|
+
Easing,
|
|
5
|
+
useAnimatedStyle,
|
|
6
|
+
useSharedValue,
|
|
7
|
+
withTiming,
|
|
8
|
+
type SharedValue,
|
|
9
|
+
} from 'react-native-reanimated';
|
|
10
|
+
import { scheduleOnRN } from 'react-native-worklets';
|
|
11
|
+
import { useCombinedAnimationDisabledState, type AnimationRootDisableAll } from '../../animation';
|
|
12
|
+
|
|
13
|
+
// No motion spec exists for SystemAlert; these mirror FullScreenDialog's surface motion.
|
|
14
|
+
export const SYSTEM_ALERT_ENTER_MS = 300;
|
|
15
|
+
export const SYSTEM_ALERT_EXIT_MS = 220;
|
|
16
|
+
/** Unmounts an exiting web surface when `transitionend` never fires (reduced motion, hidden tab). */
|
|
17
|
+
export const SYSTEM_ALERT_WEB_SAFETY_MS = SYSTEM_ALERT_EXIT_MS + 100;
|
|
18
|
+
|
|
19
|
+
const ENTER_TIMING = { duration: SYSTEM_ALERT_ENTER_MS, easing: Easing.bezier(0.32, 0.72, 0, 1) };
|
|
20
|
+
const EXIT_TIMING = { duration: SYSTEM_ALERT_EXIT_MS, easing: Easing.bezier(0.4, 0, 1, 1) };
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* `entering` and `exiting` exist only while motion runs. With motion off, `isOpen`
|
|
24
|
+
* moves straight between `open` and `closed`.
|
|
25
|
+
*/
|
|
26
|
+
export type SystemAlertPresencePhase = 'closed' | 'entering' | 'open' | 'exiting';
|
|
27
|
+
|
|
28
|
+
/** Web `data-state`. `exiting` maps to `closed`, as in Dialog and FullScreenDialog. */
|
|
29
|
+
export type SystemAlertWebState = 'entering' | 'open' | 'closed';
|
|
30
|
+
|
|
31
|
+
export interface UseSystemAlertPresenceOptions {
|
|
32
|
+
isOpen: boolean;
|
|
33
|
+
animation: AnimationRootDisableAll | undefined;
|
|
34
|
+
/** Wrapper height while no surface pushes past it: the reserved top inset, or 0. */
|
|
35
|
+
collapsedHeight: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface SystemAlertPresence {
|
|
39
|
+
phase: SystemAlertPresencePhase;
|
|
40
|
+
isSurfaceMounted: boolean;
|
|
41
|
+
wrapperStyle: ReturnType<typeof useAnimatedStyle>;
|
|
42
|
+
/** Reports the surface's natural height, from a synchronous read or `onLayout`. */
|
|
43
|
+
setSurfaceHeight: (height: number) => void;
|
|
44
|
+
/** Bottom edge of the surface, `b`, from 0 (above the screen edge) to its height. */
|
|
45
|
+
edge: SharedValue<number>;
|
|
46
|
+
surfaceHeight: SharedValue<number>;
|
|
47
|
+
/** True while `open` and no motion runs: the wrapper sizes to its content. */
|
|
48
|
+
isSettled: SharedValue<boolean>;
|
|
49
|
+
/** Web only: drives the CSS transitions on the wrapper and the surface. */
|
|
50
|
+
webState: SystemAlertWebState | undefined;
|
|
51
|
+
/** Web only: the wrapper's inline height. `undefined` sizes it to its content. */
|
|
52
|
+
webWrapperHeight: number | undefined;
|
|
53
|
+
/** Web only: the wrapper node whose `height` transition ends the exit. */
|
|
54
|
+
wrapperRef: RefObject<View | null>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Presence lifecycle and contact-push motion for an in-flow SystemAlert. One value, the
|
|
59
|
+
* surface's bottom edge `b`, is driven; the wrapper's height is `max(collapsedHeight, b)`,
|
|
60
|
+
* so content below only moves once the surface leaves the reserved strip.
|
|
61
|
+
*
|
|
62
|
+
* Web runs the same lifecycle on CSS transitions keyed on `data-state` instead of Reanimated:
|
|
63
|
+
* `entering` → (rAF) → `open`, and `closed` → `transitionend` on the wrapper's `height`, or a
|
|
64
|
+
* safety timer → unmount. The inset is 0 there, so the wrapper's height is `b`, and the surface,
|
|
65
|
+
* aligned to the wrapper's bottom edge, follows the same transition.
|
|
66
|
+
*/
|
|
67
|
+
export function useSystemAlertPresence({
|
|
68
|
+
isOpen,
|
|
69
|
+
animation,
|
|
70
|
+
collapsedHeight,
|
|
71
|
+
}: UseSystemAlertPresenceOptions): SystemAlertPresence {
|
|
72
|
+
const isWeb = Platform.OS === 'web';
|
|
73
|
+
const isAllAnimationsDisabled = useCombinedAnimationDisabledState(animation);
|
|
74
|
+
const hasMotion = !isAllAnimationsDisabled;
|
|
75
|
+
|
|
76
|
+
const [phase, setPhase] = useState<SystemAlertPresencePhase>(isOpen ? 'open' : 'closed');
|
|
77
|
+
const [prevIsOpen, setPrevIsOpen] = useState(isOpen);
|
|
78
|
+
if (isOpen !== prevIsOpen) {
|
|
79
|
+
setPrevIsOpen(isOpen);
|
|
80
|
+
if (isOpen) {
|
|
81
|
+
setPhase(hasMotion ? 'entering' : 'open');
|
|
82
|
+
} else {
|
|
83
|
+
setPhase(hasMotion ? 'exiting' : 'closed');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const edge = useSharedValue(0);
|
|
88
|
+
const surfaceHeight = useSharedValue(0);
|
|
89
|
+
const isSettled = useSharedValue(isOpen);
|
|
90
|
+
const [webSurfaceHeight, setWebSurfaceHeight] = useState(0);
|
|
91
|
+
const wrapperRef = useRef<View | null>(null);
|
|
92
|
+
|
|
93
|
+
// Read by the surface's layout callbacks, which run before this hook's own effects.
|
|
94
|
+
const phaseRef = useRef(phase);
|
|
95
|
+
phaseRef.current = phase;
|
|
96
|
+
// The JS-side copy of `surfaceHeight`. A shared value read on the JS thread right after a
|
|
97
|
+
// JS write can still return the old value, which would start the first enter toward 0.
|
|
98
|
+
const surfaceHeightRef = useRef(0);
|
|
99
|
+
|
|
100
|
+
const settle = useCallback((from: SystemAlertPresencePhase, to: SystemAlertPresencePhase) => {
|
|
101
|
+
setPhase((current) => (current === from ? to : current));
|
|
102
|
+
}, []);
|
|
103
|
+
|
|
104
|
+
// `withTiming` starts from the current edge, so a reversal continues from where it is.
|
|
105
|
+
const startEnter = useCallback(() => {
|
|
106
|
+
edge.set(
|
|
107
|
+
withTiming(surfaceHeightRef.current, ENTER_TIMING, (finished) => {
|
|
108
|
+
'worklet';
|
|
109
|
+
if (finished) scheduleOnRN(settle, 'entering', 'open');
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
}, [edge, settle]);
|
|
113
|
+
|
|
114
|
+
const setSurfaceHeight = useCallback(
|
|
115
|
+
(height: number) => {
|
|
116
|
+
if (height <= 0) return;
|
|
117
|
+
if (isWeb) {
|
|
118
|
+
setWebSurfaceHeight(height);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (height === surfaceHeightRef.current) return;
|
|
122
|
+
surfaceHeightRef.current = height;
|
|
123
|
+
surfaceHeight.set(height);
|
|
124
|
+
if (phaseRef.current === 'open') {
|
|
125
|
+
edge.set(height);
|
|
126
|
+
} else if (phaseRef.current === 'entering') {
|
|
127
|
+
startEnter();
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
[isWeb, edge, surfaceHeight, startEnter],
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// A layout effect so the surface's synchronous measurement (a child effect) has already
|
|
134
|
+
// run and the first animated frame targets the measured height. Without one, `onLayout`
|
|
135
|
+
// starts the enter through `setSurfaceHeight`.
|
|
136
|
+
useLayoutEffect(() => {
|
|
137
|
+
if (isWeb) return;
|
|
138
|
+
switch (phase) {
|
|
139
|
+
case 'open':
|
|
140
|
+
edge.set(surfaceHeightRef.current);
|
|
141
|
+
isSettled.set(true);
|
|
142
|
+
break;
|
|
143
|
+
case 'closed':
|
|
144
|
+
isSettled.set(false);
|
|
145
|
+
edge.set(0);
|
|
146
|
+
break;
|
|
147
|
+
case 'entering':
|
|
148
|
+
isSettled.set(false);
|
|
149
|
+
if (surfaceHeightRef.current > 0) startEnter();
|
|
150
|
+
break;
|
|
151
|
+
case 'exiting':
|
|
152
|
+
isSettled.set(false);
|
|
153
|
+
edge.set(
|
|
154
|
+
withTiming(0, EXIT_TIMING, (finished) => {
|
|
155
|
+
'worklet';
|
|
156
|
+
if (finished) scheduleOnRN(settle, 'exiting', 'closed');
|
|
157
|
+
}),
|
|
158
|
+
);
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}, [isWeb, phase, edge, isSettled, startEnter, settle]);
|
|
162
|
+
|
|
163
|
+
// One frame at `entering` (height 0, surface above the clip) so the browser has a start
|
|
164
|
+
// value to transition from.
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
if (!isWeb || phase !== 'entering') return;
|
|
167
|
+
const frame = requestAnimationFrame(() => {
|
|
168
|
+
settle('entering', 'open');
|
|
169
|
+
});
|
|
170
|
+
return () => cancelAnimationFrame(frame);
|
|
171
|
+
}, [isWeb, phase, settle]);
|
|
172
|
+
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (!isWeb || phase !== 'exiting') return;
|
|
175
|
+
|
|
176
|
+
const node = wrapperRef.current as unknown as HTMLElement | null;
|
|
177
|
+
const finish = () => {
|
|
178
|
+
settle('exiting', 'closed');
|
|
179
|
+
};
|
|
180
|
+
const safetyTimer = setTimeout(finish, SYSTEM_ALERT_WEB_SAFETY_MS);
|
|
181
|
+
|
|
182
|
+
if (typeof node?.addEventListener !== 'function') {
|
|
183
|
+
return () => clearTimeout(safetyTimer);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// The surface's own `transform` transition bubbles here too.
|
|
187
|
+
const handleTransitionEnd = (event: Event) => {
|
|
188
|
+
if (event.target !== node) return;
|
|
189
|
+
if ((event as unknown as { propertyName?: string }).propertyName === 'height') finish();
|
|
190
|
+
};
|
|
191
|
+
node.addEventListener('transitionend', handleTransitionEnd);
|
|
192
|
+
|
|
193
|
+
return () => {
|
|
194
|
+
clearTimeout(safetyTimer);
|
|
195
|
+
node.removeEventListener('transitionend', handleTransitionEnd);
|
|
196
|
+
};
|
|
197
|
+
}, [isWeb, phase, settle]);
|
|
198
|
+
|
|
199
|
+
// CSS cannot transition to `auto`, so a moving wrapper gets the measured px height.
|
|
200
|
+
// Before the first measurement (or with motion off) it sizes to its content; `auto` → px
|
|
201
|
+
// does not transition, so a banner shown on first render does not animate.
|
|
202
|
+
let webWrapperHeight: number | undefined;
|
|
203
|
+
if (isWeb && hasMotion) {
|
|
204
|
+
webWrapperHeight = phase === 'open' ? webSurfaceHeight || undefined : 0;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Explicit deps for Vite/web without the Reanimated Babel plugin.
|
|
208
|
+
const wrapperStyle = useAnimatedStyle(
|
|
209
|
+
() => ({
|
|
210
|
+
height: isSettled.get() ? 'auto' : Math.max(collapsedHeight, edge.get()),
|
|
211
|
+
}),
|
|
212
|
+
[isSettled, edge, collapsedHeight],
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
phase,
|
|
217
|
+
isSurfaceMounted: phase !== 'closed',
|
|
218
|
+
wrapperStyle,
|
|
219
|
+
setSurfaceHeight,
|
|
220
|
+
edge,
|
|
221
|
+
surfaceHeight,
|
|
222
|
+
isSettled,
|
|
223
|
+
webState: isWeb ? (phase === 'exiting' ? 'closed' : phase) : undefined,
|
|
224
|
+
webWrapperHeight,
|
|
225
|
+
wrapperRef,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* The surface's slide, `translateY = b − H`. Call it from a component that mounts with the
|
|
231
|
+
* surface: Reanimated computes the initial style once, on the hook's first render.
|
|
232
|
+
*/
|
|
233
|
+
export function useSystemAlertSurfaceStyle({
|
|
234
|
+
edge,
|
|
235
|
+
surfaceHeight,
|
|
236
|
+
isSettled,
|
|
237
|
+
}: Pick<SystemAlertPresence, 'edge' | 'surfaceHeight' | 'isSettled'>): ReturnType<
|
|
238
|
+
typeof useAnimatedStyle
|
|
239
|
+
> {
|
|
240
|
+
const { height: windowHeight } = useWindowDimensions();
|
|
241
|
+
|
|
242
|
+
// Explicit deps for Vite/web without the Reanimated Babel plugin.
|
|
243
|
+
return useAnimatedStyle(() => {
|
|
244
|
+
const height = surfaceHeight.get();
|
|
245
|
+
let translateY = 0;
|
|
246
|
+
if (!isSettled.get()) {
|
|
247
|
+
// Before the first measurement, park the surface above the clip.
|
|
248
|
+
translateY = height > 0 ? edge.get() - height : -windowHeight;
|
|
249
|
+
}
|
|
250
|
+
return { transform: [{ translateY }] };
|
|
251
|
+
}, [edge, surfaceHeight, isSettled, windowHeight]);
|
|
252
|
+
}
|