@castui/cast-ui 4.8.0 → 4.10.0
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 +78 -1
- package/dist/components/Accordion/Accordion.js +4 -3
- package/dist/components/Backdrop/Backdrop.js +7 -8
- package/dist/components/BottomSheet/BottomSheet.js +12 -14
- package/dist/components/Drawer/Drawer.js +12 -14
- package/dist/components/Progress/Progress.js +5 -4
- package/dist/components/Skeleton/Skeleton.js +11 -13
- package/dist/components/SpeedDial/SpeedDial.js +3 -5
- package/dist/components/Spinner/Spinner.js +6 -5
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/useBreakpoint.d.ts +22 -0
- package/dist/hooks/useBreakpoint.js +45 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +23 -2
- package/dist/theme/ThemeContext.d.ts +12 -1
- package/dist/theme/ThemeContext.js +5 -2
- package/dist/theme/applyCastTheme.d.ts +32 -2
- package/dist/theme/applyCastTheme.js +72 -0
- package/dist/theme/index.d.ts +1 -0
- package/dist/theme/index.js +3 -1
- package/dist/theme/useMotion.d.ts +32 -0
- package/dist/theme/useMotion.js +55 -0
- package/dist/tokens/breakpoints.d.ts +57 -0
- package/dist/tokens/breakpoints.js +92 -0
- package/dist/tokens/index.d.ts +2 -0
- package/dist/tokens/index.js +17 -1
- package/dist/tokens/motion.d.ts +196 -0
- package/dist/tokens/motion.js +175 -0
- package/package.json +2 -1
- package/skills/cast-ui-component/SKILL.md +834 -0
- package/skills/cast-ui-docs-site/SKILL.md +114 -0
- package/skills/cast-ui-usage/SKILL.md +587 -0
package/README.md
CHANGED
|
@@ -18,8 +18,18 @@ so what designers see in Figma is what ships in the app. Every component
|
|
|
18
18
|
supports light and dark mode, three spacing densities, and your own brand
|
|
19
19
|
colours — all switchable while the app is running, with no rebuild.
|
|
20
20
|
|
|
21
|
+
Documentation site — live examples, patterns, templates, themes, motion,
|
|
22
|
+
and the system graph: **https://connagh.github.io/cast-ui/**
|
|
23
|
+
|
|
21
24
|
Browse every component live in the
|
|
22
|
-
[hosted Storybook](https://main--6990f00d7b8682c18d2ed5f3.chromatic.com)
|
|
25
|
+
[hosted Storybook](https://main--6990f00d7b8682c18d2ed5f3.chromatic.com),
|
|
26
|
+
or grab the open source
|
|
27
|
+
[Figma kit](https://www.figma.com/community/file/1648821010844688421/cast-ui-kit-for-react-native).
|
|
28
|
+
|
|
29
|
+
Motion is part of the token system too: durations, easing curves, and
|
|
30
|
+
springs live in the kit's `motion` variable collection, ship as
|
|
31
|
+
`theme.motion`, honour the OS reduce-motion setting, and can be retimed at
|
|
32
|
+
runtime like any colour.
|
|
23
33
|
|
|
24
34
|
## Installation
|
|
25
35
|
|
|
@@ -163,6 +173,61 @@ The full guide lives in the
|
|
|
163
173
|
[hosted Storybook](https://main--6990f00d7b8682c18d2ed5f3.chromatic.com)
|
|
164
174
|
under **Guides → Customisation**.
|
|
165
175
|
|
|
176
|
+
## Responsive layout
|
|
177
|
+
|
|
178
|
+
Cast UI ships a set of breakpoints and hooks for building layouts that adapt
|
|
179
|
+
across phones, tablets, and desktops. The values follow the Material 3 window
|
|
180
|
+
size classes, so the same numbers hold up across watches, phones, foldables,
|
|
181
|
+
tablets, and large screens.
|
|
182
|
+
|
|
183
|
+
| Tier | Range (dp) | Typical devices |
|
|
184
|
+
|------|-----------|-----------------|
|
|
185
|
+
| `base` | `< 600` | Watches and every phone in portrait. Your default layout. |
|
|
186
|
+
| `sm` | `>= 600` | Large phones in landscape, foldables unfolded, small tablets |
|
|
187
|
+
| `md` | `>= 840` | Tablets |
|
|
188
|
+
| `lg` | `>= 1200` | Laptops and desktops |
|
|
189
|
+
| `xl` | `>= 1600` | Large desktops and TVs |
|
|
190
|
+
|
|
191
|
+
The hooks read the live window width, so they update on resize, rotation, and
|
|
192
|
+
foldables, and they behave the same on the web. The most common one picks a
|
|
193
|
+
value per tier:
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
import { useResponsiveValue, useBreakpoint, useMinWidth } from '@castui/cast-ui';
|
|
197
|
+
|
|
198
|
+
function Gallery() {
|
|
199
|
+
const columns = useResponsiveValue({ base: 1, md: 2, xl: 4 });
|
|
200
|
+
// 1 column on phones, 2 on tablets, 4 on large desktops
|
|
201
|
+
...
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`useResponsiveValue` is mobile-first: a tier with no value falls back to the
|
|
206
|
+
nearest one below it, so `{ base: 1, md: 2 }` gives 1 up to `md` and 2 from
|
|
207
|
+
`md` on. The other two hooks cover the rest:
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
const tier = useBreakpoint(); // 'base' | 'sm' | 'md' | 'lg' | 'xl'
|
|
211
|
+
const isWide = useMinWidth('lg'); // true from 1200dp up
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The raw thresholds are also exported as `breakpoints` if you need a number
|
|
215
|
+
directly.
|
|
216
|
+
|
|
217
|
+
A few things worth knowing. React Native has no CSS media queries, so a
|
|
218
|
+
breakpoint here is a width threshold you compare against, not automatic
|
|
219
|
+
restyling. `base` is the mobile-first default: you write the phone layout with
|
|
220
|
+
no breakpoint, then add overrides for larger screens. The gap between a small
|
|
221
|
+
and a large phone is better handled with flexible layout (flex, percentages,
|
|
222
|
+
`maxWidth`) than with a breakpoint.
|
|
223
|
+
|
|
224
|
+
Breakpoints are a fixed foundation, which sets them apart from the rest of the
|
|
225
|
+
theme. They are not part of `ThemeProvider`: they do not change with density,
|
|
226
|
+
they are not touched by brand colour overrides, and they are not carried in
|
|
227
|
+
`cast-theme.json`. The scale stays identical in every app, so layouts stay
|
|
228
|
+
predictable. The same values live as the `breakpoint/*` primitive variables in
|
|
229
|
+
the [cast-ui-kit Figma file](https://www.figma.com/design/JGtlpxLPJMZcwvQ3UZ9ZUl/cast-ui-kit).
|
|
230
|
+
|
|
166
231
|
## Theming from Figma — the cast-sync plugin
|
|
167
232
|
|
|
168
233
|
[`cast-sync/`](./cast-sync) is a Figma plugin that turns the Figma file's
|
|
@@ -210,6 +275,18 @@ npm run build # compile to dist/
|
|
|
210
275
|
| `npm run build-storybook` | Build static Storybook |
|
|
211
276
|
| `npm run build` | TypeScript compilation to `dist/` |
|
|
212
277
|
|
|
278
|
+
### Documentation site
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
cd site
|
|
282
|
+
npm install
|
|
283
|
+
npm run dev # local dev server
|
|
284
|
+
npm run smoke # render every route + evaluate every live example
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
The site aliases `@castui/cast-ui` to `../src`, so it always documents the
|
|
288
|
+
code in your working tree.
|
|
289
|
+
|
|
213
290
|
## CI/CD
|
|
214
291
|
|
|
215
292
|
| Workflow | Trigger | Purpose |
|
|
@@ -81,6 +81,7 @@ function toOpenArray(v, type) {
|
|
|
81
81
|
function AccordionItem({ value, title, leadingIcon, disabled = false, children, style, accessibilityLabel, }) {
|
|
82
82
|
const { openValues, toggle, size } = useAccordionContext('AccordionItem');
|
|
83
83
|
const { components, colors, scheme } = (0, theme_1.useTheme)();
|
|
84
|
+
const motion = (0, theme_1.useMotion)();
|
|
84
85
|
const [isHovered, setIsHovered] = (0, react_1.useState)(false);
|
|
85
86
|
const sizeTokens = components.accordion[size];
|
|
86
87
|
const isOpen = openValues.includes(value);
|
|
@@ -89,11 +90,11 @@ function AccordionItem({ value, title, leadingIcon, disabled = false, children,
|
|
|
89
90
|
(0, react_1.useEffect)(() => {
|
|
90
91
|
react_native_1.Animated.timing(spin, {
|
|
91
92
|
toValue: isOpen ? 1 : 0,
|
|
92
|
-
duration:
|
|
93
|
-
easing:
|
|
93
|
+
duration: motion.scale(motion.transition.expand.duration),
|
|
94
|
+
easing: motion.transition.expand.easing,
|
|
94
95
|
useNativeDriver: true,
|
|
95
96
|
}).start();
|
|
96
|
-
}, [isOpen, spin]);
|
|
97
|
+
}, [isOpen, spin, motion]);
|
|
97
98
|
const rotate = spin.interpolate({
|
|
98
99
|
inputRange: [0, 1],
|
|
99
100
|
outputRange: ['0deg', '90deg'],
|
|
@@ -23,15 +23,12 @@ const theme_1 = require("../../theme");
|
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
24
|
// Constants
|
|
25
25
|
// ---------------------------------------------------------------------------
|
|
26
|
-
/** Fade timing. */
|
|
27
|
-
const DURATION = 220;
|
|
28
|
-
/** react-native-web does not support the native animation driver. */
|
|
29
|
-
const USE_NATIVE_DRIVER = react_native_1.Platform.OS !== 'web';
|
|
30
26
|
// ---------------------------------------------------------------------------
|
|
31
27
|
// Component
|
|
32
28
|
// ---------------------------------------------------------------------------
|
|
33
29
|
function Backdrop({ open, onPress, invisible = false, children, style, accessibilityLabel, }) {
|
|
34
30
|
const { scheme } = (0, theme_1.useTheme)();
|
|
31
|
+
const motion = (0, theme_1.useMotion)();
|
|
35
32
|
const targetOpacity = invisible ? 0 : scheme.overlay.scrimOpacity;
|
|
36
33
|
const fade = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
|
|
37
34
|
const [mounted, setMounted] = (0, react_1.useState)(open);
|
|
@@ -40,15 +37,17 @@ function Backdrop({ open, onPress, invisible = false, children, style, accessibi
|
|
|
40
37
|
setMounted(true);
|
|
41
38
|
react_native_1.Animated.timing(fade, {
|
|
42
39
|
toValue: 1,
|
|
43
|
-
duration:
|
|
44
|
-
|
|
40
|
+
duration: motion.scale(motion.transition.standard.duration),
|
|
41
|
+
easing: motion.transition.standard.easing,
|
|
42
|
+
useNativeDriver: motion.useNativeDriver,
|
|
45
43
|
}).start();
|
|
46
44
|
}
|
|
47
45
|
else if (mounted) {
|
|
48
46
|
react_native_1.Animated.timing(fade, {
|
|
49
47
|
toValue: 0,
|
|
50
|
-
duration:
|
|
51
|
-
|
|
48
|
+
duration: motion.scale(motion.transition.standard.duration),
|
|
49
|
+
easing: motion.transition.standard.easing,
|
|
50
|
+
useNativeDriver: motion.useNativeDriver,
|
|
52
51
|
}).start(({ finished }) => {
|
|
53
52
|
if (finished)
|
|
54
53
|
setMounted(false);
|
|
@@ -34,10 +34,6 @@ const tokens_1 = require("../../tokens");
|
|
|
34
34
|
// ---------------------------------------------------------------------------
|
|
35
35
|
/** The sheet never grows past this share of the screen height. */
|
|
36
36
|
const MAX_HEIGHT_RATIO = 0.9;
|
|
37
|
-
/** Animation timing. */
|
|
38
|
-
const DURATION = 220;
|
|
39
|
-
/** react-native-web does not support the native animation driver. */
|
|
40
|
-
const USE_NATIVE_DRIVER = react_native_1.Platform.OS !== 'web';
|
|
41
37
|
/** Upward shadow for web (matches Figma shadow/lg, cast above the sheet). */
|
|
42
38
|
const SHADOW_WEB = {
|
|
43
39
|
boxShadow: '0px -4px 6px rgba(0,0,0,0.04), 0px -10px 15px rgba(0,0,0,0.08)',
|
|
@@ -97,6 +93,7 @@ function BottomSheetContent({ title: titleText, showHandle = true, children, sty
|
|
|
97
93
|
// ---------------------------------------------------------------------------
|
|
98
94
|
function BottomSheet({ open, onClose, closeOnBackdropPress = true, ...contentProps }) {
|
|
99
95
|
const { scheme } = (0, theme_1.useTheme)();
|
|
96
|
+
const motion = (0, theme_1.useMotion)();
|
|
100
97
|
const scrimOpacity = scheme.overlay.scrimOpacity;
|
|
101
98
|
const screenHeight = react_native_1.Dimensions.get('window').height;
|
|
102
99
|
const translateY = (0, react_1.useRef)(new react_native_1.Animated.Value(screenHeight)).current;
|
|
@@ -108,15 +105,14 @@ function BottomSheet({ open, onClose, closeOnBackdropPress = true, ...contentPro
|
|
|
108
105
|
react_native_1.Animated.parallel([
|
|
109
106
|
react_native_1.Animated.timing(backdrop, {
|
|
110
107
|
toValue: 1,
|
|
111
|
-
duration:
|
|
112
|
-
|
|
108
|
+
duration: motion.scale(motion.transition.standard.duration),
|
|
109
|
+
easing: motion.transition.standard.easing,
|
|
110
|
+
useNativeDriver: motion.useNativeDriver,
|
|
113
111
|
}),
|
|
114
112
|
react_native_1.Animated.spring(translateY, {
|
|
115
113
|
toValue: 0,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
mass: 0.9,
|
|
119
|
-
useNativeDriver: USE_NATIVE_DRIVER,
|
|
114
|
+
...motion.spring.overlay,
|
|
115
|
+
useNativeDriver: motion.useNativeDriver,
|
|
120
116
|
}),
|
|
121
117
|
]).start();
|
|
122
118
|
}
|
|
@@ -124,13 +120,15 @@ function BottomSheet({ open, onClose, closeOnBackdropPress = true, ...contentPro
|
|
|
124
120
|
react_native_1.Animated.parallel([
|
|
125
121
|
react_native_1.Animated.timing(backdrop, {
|
|
126
122
|
toValue: 0,
|
|
127
|
-
duration:
|
|
128
|
-
|
|
123
|
+
duration: motion.scale(motion.transition.standard.duration),
|
|
124
|
+
easing: motion.transition.standard.easing,
|
|
125
|
+
useNativeDriver: motion.useNativeDriver,
|
|
129
126
|
}),
|
|
130
127
|
react_native_1.Animated.timing(translateY, {
|
|
131
128
|
toValue: screenHeight,
|
|
132
|
-
duration:
|
|
133
|
-
|
|
129
|
+
duration: motion.scale(motion.transition.standard.duration),
|
|
130
|
+
easing: motion.transition.standard.easing,
|
|
131
|
+
useNativeDriver: motion.useNativeDriver,
|
|
134
132
|
}),
|
|
135
133
|
]).start(({ finished }) => {
|
|
136
134
|
if (finished)
|
|
@@ -36,10 +36,6 @@ const tokens_1 = require("../../tokens");
|
|
|
36
36
|
const DEFAULT_WIDTH = 320;
|
|
37
37
|
/** Top/bottom panels never grow past this share of the screen height. */
|
|
38
38
|
const MAX_HEIGHT_RATIO = 0.9;
|
|
39
|
-
/** Animation timing. */
|
|
40
|
-
const DURATION = 240;
|
|
41
|
-
/** react-native-web does not support the native animation driver. */
|
|
42
|
-
const USE_NATIVE_DRIVER = react_native_1.Platform.OS !== 'web';
|
|
43
39
|
const SHADOW_WEB = {
|
|
44
40
|
boxShadow: '0px 0px 6px rgba(0,0,0,0.04), 0px 0px 15px rgba(0,0,0,0.08)',
|
|
45
41
|
};
|
|
@@ -99,6 +95,7 @@ function DrawerContent({ anchor = 'left', title: titleText, children, style, acc
|
|
|
99
95
|
// ---------------------------------------------------------------------------
|
|
100
96
|
function Drawer({ open, onClose, closeOnBackdropPress = true, anchor = 'left', ...contentProps }) {
|
|
101
97
|
const { scheme } = (0, theme_1.useTheme)();
|
|
98
|
+
const motion = (0, theme_1.useMotion)();
|
|
102
99
|
const scrimOpacity = scheme.overlay.scrimOpacity;
|
|
103
100
|
const screen = react_native_1.Dimensions.get('window');
|
|
104
101
|
const horizontal = isHorizontal(anchor);
|
|
@@ -113,15 +110,14 @@ function Drawer({ open, onClose, closeOnBackdropPress = true, anchor = 'left', .
|
|
|
113
110
|
react_native_1.Animated.parallel([
|
|
114
111
|
react_native_1.Animated.timing(backdrop, {
|
|
115
112
|
toValue: 1,
|
|
116
|
-
duration:
|
|
117
|
-
|
|
113
|
+
duration: motion.scale(motion.transition.standard.duration),
|
|
114
|
+
easing: motion.transition.standard.easing,
|
|
115
|
+
useNativeDriver: motion.useNativeDriver,
|
|
118
116
|
}),
|
|
119
117
|
react_native_1.Animated.spring(offset, {
|
|
120
118
|
toValue: 0,
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
mass: 0.9,
|
|
124
|
-
useNativeDriver: USE_NATIVE_DRIVER,
|
|
119
|
+
...motion.spring.overlay,
|
|
120
|
+
useNativeDriver: motion.useNativeDriver,
|
|
125
121
|
}),
|
|
126
122
|
]).start();
|
|
127
123
|
}
|
|
@@ -129,13 +125,15 @@ function Drawer({ open, onClose, closeOnBackdropPress = true, anchor = 'left', .
|
|
|
129
125
|
react_native_1.Animated.parallel([
|
|
130
126
|
react_native_1.Animated.timing(backdrop, {
|
|
131
127
|
toValue: 0,
|
|
132
|
-
duration:
|
|
133
|
-
|
|
128
|
+
duration: motion.scale(motion.transition.standard.duration),
|
|
129
|
+
easing: motion.transition.standard.easing,
|
|
130
|
+
useNativeDriver: motion.useNativeDriver,
|
|
134
131
|
}),
|
|
135
132
|
react_native_1.Animated.timing(offset, {
|
|
136
133
|
toValue: distance * sign,
|
|
137
|
-
duration:
|
|
138
|
-
|
|
134
|
+
duration: motion.scale(motion.transition.standard.duration),
|
|
135
|
+
easing: motion.transition.standard.easing,
|
|
136
|
+
useNativeDriver: motion.useNativeDriver,
|
|
139
137
|
}),
|
|
140
138
|
]).start(({ finished }) => {
|
|
141
139
|
if (finished)
|
|
@@ -38,6 +38,7 @@ const clamp = (n) => Math.max(0, Math.min(100, n));
|
|
|
38
38
|
// ---------------------------------------------------------------------------
|
|
39
39
|
function Progress({ value, intent = 'brand', size = 'default', style, accessibilityLabel = 'Loading', }) {
|
|
40
40
|
const { components, colors, scheme } = (0, theme_1.useTheme)();
|
|
41
|
+
const motion = (0, theme_1.useMotion)();
|
|
41
42
|
const { trackHeight } = components.progress[size];
|
|
42
43
|
const borderRadius = components.progress.borderRadius;
|
|
43
44
|
const fill = colors[intent].bold.default.bg;
|
|
@@ -50,18 +51,18 @@ function Progress({ value, intent = 'brand', size = 'default', style, accessibil
|
|
|
50
51
|
const onLayout = (e) => setTrackWidth(e.nativeEvent.layout.width);
|
|
51
52
|
const slide = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
|
|
52
53
|
(0, react_1.useEffect)(() => {
|
|
53
|
-
if (!isIndeterminate || trackWidth === 0)
|
|
54
|
+
if (!isIndeterminate || trackWidth === 0 || motion.reduceMotion)
|
|
54
55
|
return;
|
|
55
56
|
slide.setValue(0);
|
|
56
57
|
const loop = react_native_1.Animated.loop(react_native_1.Animated.timing(slide, {
|
|
57
58
|
toValue: 1,
|
|
58
|
-
duration:
|
|
59
|
-
easing:
|
|
59
|
+
duration: motion.loop.indeterminate.duration,
|
|
60
|
+
easing: motion.loop.indeterminate.easing,
|
|
60
61
|
useNativeDriver: true,
|
|
61
62
|
}));
|
|
62
63
|
loop.start();
|
|
63
64
|
return () => loop.stop();
|
|
64
|
-
}, [isIndeterminate, trackWidth, slide]);
|
|
65
|
+
}, [isIndeterminate, trackWidth, slide, motion]);
|
|
65
66
|
const barWidth = trackWidth * INDETERMINATE_BAR_FRACTION;
|
|
66
67
|
const translateX = slide.interpolate({
|
|
67
68
|
inputRange: [0, 1],
|
|
@@ -19,11 +19,6 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
19
19
|
const react_1 = require("react");
|
|
20
20
|
const react_native_1 = require("react-native");
|
|
21
21
|
const theme_1 = require("../../theme");
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
// Constants
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// react-native-web has no native driver and warns if asked for one.
|
|
26
|
-
const USE_NATIVE_DRIVER = react_native_1.Platform.OS !== 'web';
|
|
27
22
|
/** Default size + corner radius per shape (radius/2, surface/overlay/radius, radius/full). */
|
|
28
23
|
const SHAPE_DEFAULTS = {
|
|
29
24
|
text: { width: 120, height: 12, radius: 4 },
|
|
@@ -35,29 +30,32 @@ const SHAPE_DEFAULTS = {
|
|
|
35
30
|
// ---------------------------------------------------------------------------
|
|
36
31
|
function Skeleton({ shape = 'text', width, height, radius, animated = true, style, accessibilityLabel = 'Loading', }) {
|
|
37
32
|
const { scheme } = (0, theme_1.useTheme)();
|
|
33
|
+
const motion = (0, theme_1.useMotion)();
|
|
38
34
|
const skeletonColors = scheme.skeleton;
|
|
39
35
|
const defaults = SHAPE_DEFAULTS[shape];
|
|
40
36
|
const opacity = (0, react_1.useRef)(new react_native_1.Animated.Value(1)).current;
|
|
41
37
|
(0, react_1.useEffect)(() => {
|
|
42
|
-
if (!animated) {
|
|
38
|
+
if (!animated || motion.reduceMotion) {
|
|
43
39
|
opacity.setValue(1);
|
|
44
40
|
return;
|
|
45
41
|
}
|
|
46
42
|
const loop = react_native_1.Animated.loop(react_native_1.Animated.sequence([
|
|
47
43
|
react_native_1.Animated.timing(opacity, {
|
|
48
|
-
toValue:
|
|
49
|
-
duration:
|
|
50
|
-
|
|
44
|
+
toValue: motion.loop.pulse.to,
|
|
45
|
+
duration: motion.loop.pulse.duration,
|
|
46
|
+
easing: motion.loop.pulse.easing,
|
|
47
|
+
useNativeDriver: motion.useNativeDriver,
|
|
51
48
|
}),
|
|
52
49
|
react_native_1.Animated.timing(opacity, {
|
|
53
|
-
toValue:
|
|
54
|
-
duration:
|
|
55
|
-
|
|
50
|
+
toValue: motion.loop.pulse.from,
|
|
51
|
+
duration: motion.loop.pulse.duration,
|
|
52
|
+
easing: motion.loop.pulse.easing,
|
|
53
|
+
useNativeDriver: motion.useNativeDriver,
|
|
56
54
|
}),
|
|
57
55
|
]));
|
|
58
56
|
loop.start();
|
|
59
57
|
return () => loop.stop();
|
|
60
|
-
}, [animated, opacity]);
|
|
58
|
+
}, [animated, opacity, motion]);
|
|
61
59
|
return ((0, jsx_runtime_1.jsx)(react_native_1.Animated.View, { accessibilityRole: "image", accessibilityLabel: accessibilityLabel, style: [
|
|
62
60
|
{
|
|
63
61
|
width: width ?? defaults.width,
|
|
@@ -58,9 +58,6 @@ const ACTION_ICON = {
|
|
|
58
58
|
default: 'default',
|
|
59
59
|
large: 'default',
|
|
60
60
|
};
|
|
61
|
-
const DURATION_IN = 160;
|
|
62
|
-
const DURATION_OUT = 140;
|
|
63
|
-
const USE_NATIVE_DRIVER = react_native_1.Platform.OS !== 'web';
|
|
64
61
|
const SHADOW_WEB = {
|
|
65
62
|
boxShadow: '0px 4px 6px -1px rgba(0,0,0,0.12), 0px 2px 4px -2px rgba(0,0,0,0.1)',
|
|
66
63
|
};
|
|
@@ -111,6 +108,7 @@ function SpeedDialAction({ icon, label, onPress, disabled = false }) {
|
|
|
111
108
|
// ---------------------------------------------------------------------------
|
|
112
109
|
function SpeedDial({ children, icon = 'add', openIcon = 'close', open: controlledOpen, onOpenChange, defaultOpen = false, direction = 'up', intent = 'brand', size = 'default', backdrop = true, style, accessibilityLabel, }) {
|
|
113
110
|
const { components, colors, scheme } = (0, theme_1.useTheme)();
|
|
111
|
+
const motion = (0, theme_1.useMotion)();
|
|
114
112
|
const { fabSize, actionSize, gap } = components.speedDial[size];
|
|
115
113
|
const fabColors = colors[intent].bold.default;
|
|
116
114
|
const isControlled = controlledOpen !== undefined;
|
|
@@ -128,10 +126,10 @@ function SpeedDial({ children, icon = 'add', openIcon = 'close', open: controlle
|
|
|
128
126
|
(0, react_1.useEffect)(() => {
|
|
129
127
|
if (open) {
|
|
130
128
|
setActionsMounted(true);
|
|
131
|
-
react_native_1.Animated.timing(anim, { toValue: 1, duration:
|
|
129
|
+
react_native_1.Animated.timing(anim, { toValue: 1, duration: motion.scale(motion.transition.enter.duration), easing: motion.transition.enter.easing, useNativeDriver: motion.useNativeDriver }).start();
|
|
132
130
|
}
|
|
133
131
|
else if (actionsMounted) {
|
|
134
|
-
react_native_1.Animated.timing(anim, { toValue: 0, duration:
|
|
132
|
+
react_native_1.Animated.timing(anim, { toValue: 0, duration: motion.scale(motion.transition.exit.duration), easing: motion.transition.exit.easing, useNativeDriver: motion.useNativeDriver }).start(({ finished }) => {
|
|
135
133
|
if (finished)
|
|
136
134
|
setActionsMounted(false);
|
|
137
135
|
});
|
|
@@ -36,28 +36,29 @@ const theme_1 = require("../../theme");
|
|
|
36
36
|
// ---------------------------------------------------------------------------
|
|
37
37
|
// Constants
|
|
38
38
|
// ---------------------------------------------------------------------------
|
|
39
|
-
/** One full rotation, in milliseconds. */
|
|
40
|
-
const ROTATION_DURATION = 800;
|
|
41
39
|
// ---------------------------------------------------------------------------
|
|
42
40
|
// Component
|
|
43
41
|
// ---------------------------------------------------------------------------
|
|
44
42
|
function Spinner({ intent = 'brand', size = 'default', style, accessibilityLabel = 'Loading', }) {
|
|
45
43
|
const { components, colors, scheme } = (0, theme_1.useTheme)();
|
|
44
|
+
const motion = (0, theme_1.useMotion)();
|
|
46
45
|
const { diameter, stroke } = components.spinner[size];
|
|
47
46
|
const arc = colors[intent].bold.default.bg;
|
|
48
47
|
const trackColor = scheme.spinner.track;
|
|
49
48
|
const spin = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
|
|
50
49
|
(0, react_1.useEffect)(() => {
|
|
51
50
|
spin.setValue(0);
|
|
51
|
+
if (motion.reduceMotion)
|
|
52
|
+
return;
|
|
52
53
|
const loop = react_native_1.Animated.loop(react_native_1.Animated.timing(spin, {
|
|
53
54
|
toValue: 1,
|
|
54
|
-
duration:
|
|
55
|
-
easing:
|
|
55
|
+
duration: motion.loop.spin.duration,
|
|
56
|
+
easing: motion.loop.spin.easing,
|
|
56
57
|
useNativeDriver: true,
|
|
57
58
|
}));
|
|
58
59
|
loop.start();
|
|
59
60
|
return () => loop.stop();
|
|
60
|
-
}, [spin]);
|
|
61
|
+
}, [spin, motion]);
|
|
61
62
|
const rotate = spin.interpolate({
|
|
62
63
|
inputRange: [0, 1],
|
|
63
64
|
outputRange: ['0deg', '360deg'],
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useBreakpoint, useMinWidth, useResponsiveValue } from './useBreakpoint';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useResponsiveValue = exports.useMinWidth = exports.useBreakpoint = void 0;
|
|
4
|
+
var useBreakpoint_1 = require("./useBreakpoint");
|
|
5
|
+
Object.defineProperty(exports, "useBreakpoint", { enumerable: true, get: function () { return useBreakpoint_1.useBreakpoint; } });
|
|
6
|
+
Object.defineProperty(exports, "useMinWidth", { enumerable: true, get: function () { return useBreakpoint_1.useMinWidth; } });
|
|
7
|
+
Object.defineProperty(exports, "useResponsiveValue", { enumerable: true, get: function () { return useBreakpoint_1.useResponsiveValue; } });
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Breakpoint, type BreakpointKey } from '../tokens/breakpoints';
|
|
2
|
+
/**
|
|
3
|
+
* The active breakpoint tier for the current window width. Mobile-first:
|
|
4
|
+
* 'base' below `sm`, then 'sm' | 'md' | 'lg' | 'xl' as the width grows.
|
|
5
|
+
*
|
|
6
|
+
* const bp = useBreakpoint(); // 'base' | 'sm' | 'md' | 'lg' | 'xl'
|
|
7
|
+
*/
|
|
8
|
+
export declare function useBreakpoint(): BreakpointKey;
|
|
9
|
+
/**
|
|
10
|
+
* True when the window is at or above the given breakpoint.
|
|
11
|
+
*
|
|
12
|
+
* const isWide = useMinWidth('lg'); // true from 1024dp up
|
|
13
|
+
*/
|
|
14
|
+
export declare function useMinWidth(breakpoint: Breakpoint): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Pick a value for the current breakpoint, mobile-first. Supply values for any
|
|
17
|
+
* subset of tiers; the value at the current tier wins, falling back to the
|
|
18
|
+
* nearest defined tier below it.
|
|
19
|
+
*
|
|
20
|
+
* const columns = useResponsiveValue({ base: 1, md: 2, xl: 4 });
|
|
21
|
+
*/
|
|
22
|
+
export declare function useResponsiveValue<T>(values: Partial<Record<BreakpointKey, T>>): T | undefined;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useBreakpoint = useBreakpoint;
|
|
4
|
+
exports.useMinWidth = useMinWidth;
|
|
5
|
+
exports.useResponsiveValue = useResponsiveValue;
|
|
6
|
+
/**
|
|
7
|
+
* Responsive hooks built on the breakpoint scale.
|
|
8
|
+
*
|
|
9
|
+
* These read the live window width via react-native's useWindowDimensions, so
|
|
10
|
+
* they recompute on resize, rotation, and foldable changes, and they work the
|
|
11
|
+
* same on web (react-native-web). Breakpoints are a standalone foundation: they
|
|
12
|
+
* are not part of the theme and do not change with density.
|
|
13
|
+
*/
|
|
14
|
+
const react_native_1 = require("react-native");
|
|
15
|
+
const breakpoints_1 = require("../tokens/breakpoints");
|
|
16
|
+
/**
|
|
17
|
+
* The active breakpoint tier for the current window width. Mobile-first:
|
|
18
|
+
* 'base' below `sm`, then 'sm' | 'md' | 'lg' | 'xl' as the width grows.
|
|
19
|
+
*
|
|
20
|
+
* const bp = useBreakpoint(); // 'base' | 'sm' | 'md' | 'lg' | 'xl'
|
|
21
|
+
*/
|
|
22
|
+
function useBreakpoint() {
|
|
23
|
+
const { width } = (0, react_native_1.useWindowDimensions)();
|
|
24
|
+
return (0, breakpoints_1.resolveBreakpoint)(width);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* True when the window is at or above the given breakpoint.
|
|
28
|
+
*
|
|
29
|
+
* const isWide = useMinWidth('lg'); // true from 1024dp up
|
|
30
|
+
*/
|
|
31
|
+
function useMinWidth(breakpoint) {
|
|
32
|
+
const { width } = (0, react_native_1.useWindowDimensions)();
|
|
33
|
+
return width >= breakpoints_1.breakpoints[breakpoint];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Pick a value for the current breakpoint, mobile-first. Supply values for any
|
|
37
|
+
* subset of tiers; the value at the current tier wins, falling back to the
|
|
38
|
+
* nearest defined tier below it.
|
|
39
|
+
*
|
|
40
|
+
* const columns = useResponsiveValue({ base: 1, md: 2, xl: 4 });
|
|
41
|
+
*/
|
|
42
|
+
function useResponsiveValue(values) {
|
|
43
|
+
const current = useBreakpoint();
|
|
44
|
+
return (0, breakpoints_1.resolveResponsiveValue)(values, current);
|
|
45
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { lightColors, darkColors, colorSchemes, intentColors, disabledColors, controlTokens, surfaceTokens, textTokens, overlayTokens, selectColors, menuColors, tagTokens, errorTokens, listColors, checkboxColors, toggleColors, progressColors, tabsColors, radioColors, avatarColors, skeletonColors, sliderColors, tableColors, fontFamily, fontWeight, label, title, body, heading, display, caption, type IntentName, type ProminenceName, type StateName, type ColorMode, type ColorScheme, type LabelSize, iconSize, type IconSize, } from './tokens';
|
|
2
|
-
export { ThemeProvider, useTheme, themes, applyCastTheme, type Theme, type ThemeProviderProps, type CastThemeFile, type CastThemeProps, type DensityTheme, type ComponentTokens, type ButtonSizeTokens, type ButtonThemeTokens, type DialogSizeTokens, type DialogThemeTokens, type InputSizeTokens, type InputThemeTokens, type SelectContentTokens, type SelectOptionTokens, type SelectGroupTokens, type SelectSeparatorTokens, type SelectThemeTokens, type ListItemTokens, type ListSubheaderTokens, type ListThemeTokens, type CheckboxSizeTokens, type CheckboxThemeTokens, type AlertSizeTokens, type AlertThemeTokens, type ToggleSizeTokens, type ToggleThemeTokens, type CardSizeTokens, type CardThemeTokens, type BadgeSizeTokens, type BadgeThemeTokens, type RadioSizeTokens, type RadioThemeTokens, type ToastSizeTokens, type ToastThemeTokens, type ChipSizeTokens, type ChipThemeTokens, type AvatarSizeTokens, type AvatarThemeTokens, type PopoverSizeTokens, type PopoverThemeTokens, type TooltipSizeTokens, type TooltipThemeTokens, type ProgressSizeTokens, type ProgressThemeTokens, type TabsSizeTokens, type TabsThemeTokens, type SpinnerSizeTokens, type SpinnerThemeTokens, type BottomSheetThemeTokens, type LinkSizeTokens, type LinkThemeTokens, type BreadcrumbsSizeTokens, type BreadcrumbsThemeTokens, type CodeBlockSizeTokens, type CodeBlockThemeTokens, type DrawerThemeTokens, type MenuItemTokens, type MenuGroupTokens, type MenuThemeTokens, type ToggleButtonGroupSizeTokens, type ToggleButtonGroupThemeTokens, type AppBarSizeTokens, type AppBarThemeTokens, type SliderSizeTokens, type SliderThemeTokens, type SpeedDialSizeTokens, type SpeedDialThemeTokens, type TableSizeTokens, type TableThemeTokens, type DeepPartial, } from './theme';
|
|
1
|
+
export { lightColors, darkColors, colorSchemes, intentColors, disabledColors, controlTokens, surfaceTokens, textTokens, overlayTokens, selectColors, menuColors, tagTokens, errorTokens, listColors, checkboxColors, toggleColors, progressColors, tabsColors, radioColors, avatarColors, skeletonColors, sliderColors, tableColors, fontFamily, fontWeight, label, title, body, heading, display, caption, type IntentName, type ProminenceName, type StateName, type ColorMode, type ColorScheme, type LabelSize, iconSize, type IconSize, breakpoints, breakpointOrder, resolveBreakpoint, resolveResponsiveValue, type Breakpoint, type BreakpointKey, duration, cycle, easing, easingBezier, spring, transition, feedback, loop, motionTokens, resolveMotion, type MotionTokens, type MotionTransition, type MotionOverrides, type MotionDurations, type MotionCycles, type EasingName, type EasingBezierPoints, type SpringConfig, } from './tokens';
|
|
2
|
+
export { ThemeProvider, useTheme, useMotion, themes, applyCastTheme, type Theme, type Motion, type ThemeProviderProps, type CastThemeFile, type CastThemeProps, type DensityTheme, type ComponentTokens, type ButtonSizeTokens, type ButtonThemeTokens, type DialogSizeTokens, type DialogThemeTokens, type InputSizeTokens, type InputThemeTokens, type SelectContentTokens, type SelectOptionTokens, type SelectGroupTokens, type SelectSeparatorTokens, type SelectThemeTokens, type ListItemTokens, type ListSubheaderTokens, type ListThemeTokens, type CheckboxSizeTokens, type CheckboxThemeTokens, type AlertSizeTokens, type AlertThemeTokens, type ToggleSizeTokens, type ToggleThemeTokens, type CardSizeTokens, type CardThemeTokens, type BadgeSizeTokens, type BadgeThemeTokens, type RadioSizeTokens, type RadioThemeTokens, type ToastSizeTokens, type ToastThemeTokens, type ChipSizeTokens, type ChipThemeTokens, type AvatarSizeTokens, type AvatarThemeTokens, type PopoverSizeTokens, type PopoverThemeTokens, type TooltipSizeTokens, type TooltipThemeTokens, type ProgressSizeTokens, type ProgressThemeTokens, type TabsSizeTokens, type TabsThemeTokens, type SpinnerSizeTokens, type SpinnerThemeTokens, type BottomSheetThemeTokens, type LinkSizeTokens, type LinkThemeTokens, type BreadcrumbsSizeTokens, type BreadcrumbsThemeTokens, type CodeBlockSizeTokens, type CodeBlockThemeTokens, type DrawerThemeTokens, type MenuItemTokens, type MenuGroupTokens, type MenuThemeTokens, type ToggleButtonGroupSizeTokens, type ToggleButtonGroupThemeTokens, type AppBarSizeTokens, type AppBarThemeTokens, type SliderSizeTokens, type SliderThemeTokens, type SpeedDialSizeTokens, type SpeedDialThemeTokens, type TableSizeTokens, type TableThemeTokens, type DeepPartial, } from './theme';
|
|
3
|
+
export { useBreakpoint, useMinWidth, useResponsiveValue, } from './hooks';
|
|
3
4
|
export { Button, type ButtonProps, type ButtonSize } from './components/Button';
|
|
4
5
|
export { Icon, type IconProps } from './components/Icon';
|
|
5
6
|
export { Dialog, DialogContent, type DialogProps, type DialogContentProps, type DialogAction, type DialogSize, } from './components/Dialog';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.
|
|
3
|
+
exports.themes = exports.useMotion = exports.useTheme = exports.ThemeProvider = exports.resolveMotion = exports.motionTokens = exports.loop = exports.feedback = exports.transition = exports.spring = exports.easingBezier = exports.easing = exports.cycle = exports.duration = exports.resolveResponsiveValue = exports.resolveBreakpoint = exports.breakpointOrder = exports.breakpoints = exports.iconSize = exports.caption = exports.display = exports.heading = exports.body = exports.title = exports.label = exports.fontWeight = exports.fontFamily = exports.tableColors = exports.sliderColors = exports.skeletonColors = exports.avatarColors = exports.radioColors = exports.tabsColors = exports.progressColors = exports.toggleColors = exports.checkboxColors = exports.listColors = exports.errorTokens = exports.tagTokens = exports.menuColors = exports.selectColors = exports.overlayTokens = exports.textTokens = exports.surfaceTokens = exports.controlTokens = exports.disabledColors = exports.intentColors = exports.colorSchemes = exports.darkColors = exports.lightColors = void 0;
|
|
4
|
+
exports.Menu = exports.DrawerContent = exports.Drawer = exports.CodeBlock = exports.Breadcrumb = exports.Breadcrumbs = exports.Backdrop = exports.Link = exports.AccordionItem = exports.Accordion = exports.Tab = exports.Tabs = exports.BottomSheetContent = exports.BottomSheet = exports.Spinner = exports.Progress = exports.Text = exports.Tooltip = exports.Skeleton = exports.Popover = exports.Avatar = exports.Divider = exports.Chip = exports.Toast = exports.RadioGroup = exports.Radio = exports.Input = exports.Badge = exports.Card = exports.Toggle = exports.Alert = exports.Checkbox = exports.ListDivider = exports.ListSubheader = exports.ListItem = exports.List = exports.SelectDropdown = exports.SelectTag = exports.SelectSeparator = exports.SelectGroup = exports.SelectOption = exports.Select = exports.DialogContent = exports.Dialog = exports.Icon = exports.Button = exports.useResponsiveValue = exports.useMinWidth = exports.useBreakpoint = exports.applyCastTheme = void 0;
|
|
5
|
+
exports.Autocomplete = exports.TableCell = exports.TableRow = exports.TableBody = exports.TableHead = exports.Table = exports.SpeedDialAction = exports.SpeedDial = exports.Slider = exports.AppBar = exports.ToggleButton = exports.ToggleButtonGroup = exports.MenuContent = exports.MenuLabel = exports.MenuDivider = exports.MenuItem = void 0;
|
|
5
6
|
// Cast UI — Cross-platform design system component library
|
|
6
7
|
//
|
|
7
8
|
// Tokens
|
|
@@ -38,12 +39,32 @@ Object.defineProperty(exports, "heading", { enumerable: true, get: function () {
|
|
|
38
39
|
Object.defineProperty(exports, "display", { enumerable: true, get: function () { return tokens_1.display; } });
|
|
39
40
|
Object.defineProperty(exports, "caption", { enumerable: true, get: function () { return tokens_1.caption; } });
|
|
40
41
|
Object.defineProperty(exports, "iconSize", { enumerable: true, get: function () { return tokens_1.iconSize; } });
|
|
42
|
+
Object.defineProperty(exports, "breakpoints", { enumerable: true, get: function () { return tokens_1.breakpoints; } });
|
|
43
|
+
Object.defineProperty(exports, "breakpointOrder", { enumerable: true, get: function () { return tokens_1.breakpointOrder; } });
|
|
44
|
+
Object.defineProperty(exports, "resolveBreakpoint", { enumerable: true, get: function () { return tokens_1.resolveBreakpoint; } });
|
|
45
|
+
Object.defineProperty(exports, "resolveResponsiveValue", { enumerable: true, get: function () { return tokens_1.resolveResponsiveValue; } });
|
|
46
|
+
Object.defineProperty(exports, "duration", { enumerable: true, get: function () { return tokens_1.duration; } });
|
|
47
|
+
Object.defineProperty(exports, "cycle", { enumerable: true, get: function () { return tokens_1.cycle; } });
|
|
48
|
+
Object.defineProperty(exports, "easing", { enumerable: true, get: function () { return tokens_1.easing; } });
|
|
49
|
+
Object.defineProperty(exports, "easingBezier", { enumerable: true, get: function () { return tokens_1.easingBezier; } });
|
|
50
|
+
Object.defineProperty(exports, "spring", { enumerable: true, get: function () { return tokens_1.spring; } });
|
|
51
|
+
Object.defineProperty(exports, "transition", { enumerable: true, get: function () { return tokens_1.transition; } });
|
|
52
|
+
Object.defineProperty(exports, "feedback", { enumerable: true, get: function () { return tokens_1.feedback; } });
|
|
53
|
+
Object.defineProperty(exports, "loop", { enumerable: true, get: function () { return tokens_1.loop; } });
|
|
54
|
+
Object.defineProperty(exports, "motionTokens", { enumerable: true, get: function () { return tokens_1.motionTokens; } });
|
|
55
|
+
Object.defineProperty(exports, "resolveMotion", { enumerable: true, get: function () { return tokens_1.resolveMotion; } });
|
|
41
56
|
// Theme
|
|
42
57
|
var theme_1 = require("./theme");
|
|
43
58
|
Object.defineProperty(exports, "ThemeProvider", { enumerable: true, get: function () { return theme_1.ThemeProvider; } });
|
|
44
59
|
Object.defineProperty(exports, "useTheme", { enumerable: true, get: function () { return theme_1.useTheme; } });
|
|
60
|
+
Object.defineProperty(exports, "useMotion", { enumerable: true, get: function () { return theme_1.useMotion; } });
|
|
45
61
|
Object.defineProperty(exports, "themes", { enumerable: true, get: function () { return theme_1.themes; } });
|
|
46
62
|
Object.defineProperty(exports, "applyCastTheme", { enumerable: true, get: function () { return theme_1.applyCastTheme; } });
|
|
63
|
+
// Hooks
|
|
64
|
+
var hooks_1 = require("./hooks");
|
|
65
|
+
Object.defineProperty(exports, "useBreakpoint", { enumerable: true, get: function () { return hooks_1.useBreakpoint; } });
|
|
66
|
+
Object.defineProperty(exports, "useMinWidth", { enumerable: true, get: function () { return hooks_1.useMinWidth; } });
|
|
67
|
+
Object.defineProperty(exports, "useResponsiveValue", { enumerable: true, get: function () { return hooks_1.useResponsiveValue; } });
|
|
47
68
|
// Components
|
|
48
69
|
var Button_1 = require("./components/Button");
|
|
49
70
|
Object.defineProperty(exports, "Button", { enumerable: true, get: function () { return Button_1.Button; } });
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
import React from 'react';
|
|
40
40
|
import { intentColors as defaultIntentColors } from '../tokens/colors';
|
|
41
41
|
import type { ColorMode, ColorScheme, IntentName } from '../tokens/colors';
|
|
42
|
+
import type { MotionTokens, MotionOverrides } from '../tokens/motion';
|
|
42
43
|
import type { DensityTheme, ComponentTokens, DeepPartial } from './types';
|
|
43
44
|
type IntentColorMap = typeof defaultIntentColors;
|
|
44
45
|
export type Theme = {
|
|
@@ -52,6 +53,8 @@ export type Theme = {
|
|
|
52
53
|
colors: IntentColorMap;
|
|
53
54
|
/** Disabled colours of the active scheme — kept for backwards compatibility. */
|
|
54
55
|
disabledColors: ColorScheme['disabled'];
|
|
56
|
+
/** Motion tokens — animation durations, easings, springs. Constant across density and colour mode. */
|
|
57
|
+
motion: MotionTokens;
|
|
55
58
|
};
|
|
56
59
|
export type ThemeProviderProps = {
|
|
57
60
|
/** Density theme — controls spacing and padding across all components. */
|
|
@@ -71,9 +74,17 @@ export type ThemeProviderProps = {
|
|
|
71
74
|
* Usually you don't set this by hand — `applyCastTheme` builds it for you.
|
|
72
75
|
*/
|
|
73
76
|
scheme?: DeepPartial<ColorScheme>;
|
|
77
|
+
/**
|
|
78
|
+
* Primitive-level motion overrides — durations, cycle lengths, easing
|
|
79
|
+
* beziers, springs. Semantic roles (transition/feedback/loop) are rebuilt
|
|
80
|
+
* from these, so one duration change flows into every role that uses it.
|
|
81
|
+
* Usually you don't set this by hand — `applyCastTheme` maps a
|
|
82
|
+
* cast-theme.json `motion` block onto it.
|
|
83
|
+
*/
|
|
84
|
+
motion?: MotionOverrides;
|
|
74
85
|
children: React.ReactNode;
|
|
75
86
|
};
|
|
76
|
-
export declare function ThemeProvider({ density, colorMode, colors, scheme: schemeOverride, children, }: ThemeProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
87
|
+
export declare function ThemeProvider({ density, colorMode, colors, scheme: schemeOverride, motion: motionOverrides, children, }: ThemeProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
77
88
|
/**
|
|
78
89
|
* Access the current theme — density tokens, intent colours, and component tokens.
|
|
79
90
|
* Must be called within a ThemeProvider; falls back to the "default" density if not.
|