@castui/cast-ui 4.6.0 → 4.8.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.
Files changed (78) hide show
  1. package/README.md +6 -0
  2. package/dist/components/Accordion/Accordion.d.ts +80 -0
  3. package/dist/components/Accordion/Accordion.js +157 -0
  4. package/dist/components/Accordion/index.d.ts +1 -0
  5. package/dist/components/Accordion/index.js +6 -0
  6. package/dist/components/AppBar/AppBar.d.ts +47 -0
  7. package/dist/components/AppBar/AppBar.js +47 -0
  8. package/dist/components/AppBar/index.d.ts +1 -0
  9. package/dist/components/AppBar/index.js +5 -0
  10. package/dist/components/Autocomplete/Autocomplete.d.ts +70 -0
  11. package/dist/components/Autocomplete/Autocomplete.js +249 -0
  12. package/dist/components/Autocomplete/index.d.ts +1 -0
  13. package/dist/components/Autocomplete/index.js +5 -0
  14. package/dist/components/Backdrop/Backdrop.d.ts +32 -0
  15. package/dist/components/Backdrop/Backdrop.js +74 -0
  16. package/dist/components/Backdrop/index.d.ts +1 -0
  17. package/dist/components/Backdrop/index.js +5 -0
  18. package/dist/components/BottomSheet/BottomSheet.d.ts +50 -0
  19. package/dist/components/BottomSheet/BottomSheet.js +159 -0
  20. package/dist/components/BottomSheet/index.d.ts +1 -0
  21. package/dist/components/BottomSheet/index.js +6 -0
  22. package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +63 -0
  23. package/dist/components/Breadcrumbs/Breadcrumbs.js +143 -0
  24. package/dist/components/Breadcrumbs/index.d.ts +1 -0
  25. package/dist/components/Breadcrumbs/index.js +6 -0
  26. package/dist/components/CodeBlock/CodeBlock.d.ts +42 -0
  27. package/dist/components/CodeBlock/CodeBlock.js +110 -0
  28. package/dist/components/CodeBlock/index.d.ts +1 -0
  29. package/dist/components/CodeBlock/index.js +5 -0
  30. package/dist/components/Drawer/Drawer.d.ts +51 -0
  31. package/dist/components/Drawer/Drawer.js +168 -0
  32. package/dist/components/Drawer/index.d.ts +1 -0
  33. package/dist/components/Drawer/index.js +6 -0
  34. package/dist/components/Link/Link.d.ts +51 -0
  35. package/dist/components/Link/Link.js +73 -0
  36. package/dist/components/Link/index.d.ts +1 -0
  37. package/dist/components/Link/index.js +5 -0
  38. package/dist/components/Menu/Menu.d.ts +91 -0
  39. package/dist/components/Menu/Menu.js +211 -0
  40. package/dist/components/Menu/index.d.ts +1 -0
  41. package/dist/components/Menu/index.js +9 -0
  42. package/dist/components/Slider/Slider.d.ts +47 -0
  43. package/dist/components/Slider/Slider.js +132 -0
  44. package/dist/components/Slider/index.d.ts +1 -0
  45. package/dist/components/Slider/index.js +5 -0
  46. package/dist/components/SpeedDial/SpeedDial.d.ts +72 -0
  47. package/dist/components/SpeedDial/SpeedDial.js +189 -0
  48. package/dist/components/SpeedDial/index.d.ts +1 -0
  49. package/dist/components/SpeedDial/index.js +6 -0
  50. package/dist/components/Spinner/Spinner.d.ts +42 -0
  51. package/dist/components/Spinner/Spinner.js +77 -0
  52. package/dist/components/Spinner/index.d.ts +1 -0
  53. package/dist/components/Spinner/index.js +5 -0
  54. package/dist/components/Table/Table.d.ts +74 -0
  55. package/dist/components/Table/Table.js +176 -0
  56. package/dist/components/Table/index.d.ts +1 -0
  57. package/dist/components/Table/index.js +9 -0
  58. package/dist/components/Tabs/Tabs.d.ts +1 -1
  59. package/dist/components/Tabs/Tabs.js +5 -2
  60. package/dist/components/ToggleButtonGroup/ToggleButtonGroup.d.ts +69 -0
  61. package/dist/components/ToggleButtonGroup/ToggleButtonGroup.js +158 -0
  62. package/dist/components/ToggleButtonGroup/index.d.ts +1 -0
  63. package/dist/components/ToggleButtonGroup/index.js +6 -0
  64. package/dist/index.d.ts +17 -2
  65. package/dist/index.js +51 -2
  66. package/dist/theme/ThemeContext.d.ts +8 -1
  67. package/dist/theme/ThemeContext.js +7 -4
  68. package/dist/theme/applyCastTheme.d.ts +75 -0
  69. package/dist/theme/applyCastTheme.js +95 -0
  70. package/dist/theme/index.d.ts +2 -1
  71. package/dist/theme/index.js +3 -1
  72. package/dist/theme/themes.js +192 -0
  73. package/dist/theme/types.d.ts +192 -0
  74. package/dist/tokens/colors.d.ts +48 -0
  75. package/dist/tokens/colors.js +49 -1
  76. package/dist/tokens/index.d.ts +1 -1
  77. package/dist/tokens/index.js +4 -1
  78. package/package.json +2 -1
@@ -0,0 +1,72 @@
1
+ /**
2
+ * SpeedDial — a floating action button that expands into a set of actions.
3
+ *
4
+ * Compound component. <SpeedDial> is the round FAB; <SpeedDialAction> is one
5
+ * action that fans out when the FAB is pressed.
6
+ *
7
+ * <SpeedDial icon="add">
8
+ * <SpeedDialAction icon="edit" label="Edit" onPress={edit} />
9
+ * <SpeedDialAction icon="share" label="Share" onPress={share} />
10
+ * </SpeedDial>
11
+ *
12
+ * Maps 1:1 to the Figma <Speed Dial> component:
13
+ * intent → neutral | brand | danger (FAB fill)
14
+ * size → small | default | large (FAB + action sizes)
15
+ * direction → up | down | left | right (which way actions fan out)
16
+ *
17
+ * The FAB fills with the intent bold colour; actions are neutral surface
18
+ * buttons with a tag-styled label. An optional scrim dims the background and
19
+ * closes on press. open is controlled with open/onOpenChange or uncontrolled.
20
+ *
21
+ * Tokens: speed-dial/{size}/fab-size and action-size are constant per size;
22
+ * speed-dial/{size}/gap is density-varying. The FAB icon and action icons use
23
+ * the named Icon scale. Place the SpeedDial in a positioned container (e.g.
24
+ * absolute bottom-right) so it floats. Fonts are consumer-loaded.
25
+ *
26
+ * Exports:
27
+ * SpeedDial — the FAB + expanding actions
28
+ * SpeedDialAction — one action button with a label
29
+ */
30
+ import React from 'react';
31
+ import { type StyleProp, type ViewStyle } from 'react-native';
32
+ import type { IntentName } from '../../tokens';
33
+ export type SpeedDialSize = 'small' | 'default' | 'large';
34
+ export type SpeedDialDirection = 'up' | 'down' | 'left' | 'right';
35
+ export type SpeedDialProps = {
36
+ /** `<SpeedDialAction>` children. */
37
+ children: React.ReactNode;
38
+ /** FAB icon when closed. Defaults to "add". */
39
+ icon?: string;
40
+ /** FAB icon when open. Defaults to "close". */
41
+ openIcon?: string;
42
+ /** Controlled open state. */
43
+ open?: boolean;
44
+ /** Open-state change handler. */
45
+ onOpenChange?: (open: boolean) => void;
46
+ /** Initial open state (uncontrolled). */
47
+ defaultOpen?: boolean;
48
+ /** Which way actions fan out. Defaults to "up". */
49
+ direction?: SpeedDialDirection;
50
+ /** Semantic intent — drives the FAB fill. */
51
+ intent?: IntentName;
52
+ /** Size variant — FAB and action sizes. */
53
+ size?: SpeedDialSize;
54
+ /** Show a dimming scrim behind the open actions. Defaults to true. */
55
+ backdrop?: boolean;
56
+ /** Outer style for the FAB container (use for absolute positioning). */
57
+ style?: StyleProp<ViewStyle>;
58
+ /** Accessibility label for the FAB. */
59
+ accessibilityLabel?: string;
60
+ };
61
+ export type SpeedDialActionProps = {
62
+ /** Action icon — Material Symbols name. */
63
+ icon: string;
64
+ /** Action label, shown as a tag chip beside the button. */
65
+ label?: string;
66
+ /** Press handler. */
67
+ onPress?: () => void;
68
+ /** Disables this action. */
69
+ disabled?: boolean;
70
+ };
71
+ export declare function SpeedDialAction({ icon, label, onPress, disabled }: SpeedDialActionProps): import("react/jsx-runtime").JSX.Element;
72
+ export declare function SpeedDial({ children, icon, openIcon, open: controlledOpen, onOpenChange, defaultOpen, direction, intent, size, backdrop, style, accessibilityLabel, }: SpeedDialProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SpeedDialAction = SpeedDialAction;
4
+ exports.SpeedDial = SpeedDial;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ /**
7
+ * SpeedDial — a floating action button that expands into a set of actions.
8
+ *
9
+ * Compound component. <SpeedDial> is the round FAB; <SpeedDialAction> is one
10
+ * action that fans out when the FAB is pressed.
11
+ *
12
+ * <SpeedDial icon="add">
13
+ * <SpeedDialAction icon="edit" label="Edit" onPress={edit} />
14
+ * <SpeedDialAction icon="share" label="Share" onPress={share} />
15
+ * </SpeedDial>
16
+ *
17
+ * Maps 1:1 to the Figma <Speed Dial> component:
18
+ * intent → neutral | brand | danger (FAB fill)
19
+ * size → small | default | large (FAB + action sizes)
20
+ * direction → up | down | left | right (which way actions fan out)
21
+ *
22
+ * The FAB fills with the intent bold colour; actions are neutral surface
23
+ * buttons with a tag-styled label. An optional scrim dims the background and
24
+ * closes on press. open is controlled with open/onOpenChange or uncontrolled.
25
+ *
26
+ * Tokens: speed-dial/{size}/fab-size and action-size are constant per size;
27
+ * speed-dial/{size}/gap is density-varying. The FAB icon and action icons use
28
+ * the named Icon scale. Place the SpeedDial in a positioned container (e.g.
29
+ * absolute bottom-right) so it floats. Fonts are consumer-loaded.
30
+ *
31
+ * Exports:
32
+ * SpeedDial — the FAB + expanding actions
33
+ * SpeedDialAction — one action button with a label
34
+ */
35
+ const react_1 = require("react");
36
+ const react_native_1 = require("react-native");
37
+ const theme_1 = require("../../theme");
38
+ const tokens_1 = require("../../tokens");
39
+ const Text_1 = require("../Text");
40
+ const Icon_1 = require("../Icon");
41
+ const SpeedDialCtx = (0, react_1.createContext)(null);
42
+ function useSpeedDialContext() {
43
+ const ctx = (0, react_1.useContext)(SpeedDialCtx);
44
+ if (!ctx)
45
+ throw new Error('<SpeedDialAction> must be used within <SpeedDial>');
46
+ return ctx;
47
+ }
48
+ // ---------------------------------------------------------------------------
49
+ // Constants
50
+ // ---------------------------------------------------------------------------
51
+ const FAB_ICON = {
52
+ small: 'default',
53
+ default: 'large',
54
+ large: 'large',
55
+ };
56
+ const ACTION_ICON = {
57
+ small: 'small',
58
+ default: 'default',
59
+ large: 'default',
60
+ };
61
+ const DURATION_IN = 160;
62
+ const DURATION_OUT = 140;
63
+ const USE_NATIVE_DRIVER = react_native_1.Platform.OS !== 'web';
64
+ const SHADOW_WEB = {
65
+ boxShadow: '0px 4px 6px -1px rgba(0,0,0,0.12), 0px 2px 4px -2px rgba(0,0,0,0.1)',
66
+ };
67
+ const SHADOW_NATIVE = {
68
+ shadowColor: '#000000',
69
+ shadowOffset: { width: 0, height: 4 },
70
+ shadowOpacity: 0.18,
71
+ shadowRadius: 6,
72
+ elevation: 6,
73
+ };
74
+ const SHADOW = react_native_1.Platform.OS === 'web' ? SHADOW_WEB : SHADOW_NATIVE;
75
+ // ---------------------------------------------------------------------------
76
+ // SpeedDialAction
77
+ // ---------------------------------------------------------------------------
78
+ function SpeedDialAction({ icon, label, onPress, disabled = false }) {
79
+ const { size, gap, orientation, onClose } = useSpeedDialContext();
80
+ const { components, scheme } = (0, theme_1.useTheme)();
81
+ const actionSize = components.speedDial[size].actionSize;
82
+ const button = ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: () => {
83
+ if (disabled)
84
+ return;
85
+ onPress?.();
86
+ onClose();
87
+ }, disabled: disabled, accessibilityRole: "button", accessibilityLabel: label || icon, style: {
88
+ width: actionSize,
89
+ height: actionSize,
90
+ borderRadius: actionSize / 2,
91
+ alignItems: 'center',
92
+ justifyContent: 'center',
93
+ backgroundColor: scheme.surface.overlay.bg,
94
+ borderWidth: tokens_1.controlTokens.borderWidth,
95
+ borderColor: scheme.surface.overlay.border,
96
+ ...SHADOW,
97
+ }, children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: icon, size: ACTION_ICON[size], color: disabled ? scheme.disabled.fg : scheme.text.primary }) }));
98
+ const labelChip = label ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { pointerEvents: "none", style: {
99
+ backgroundColor: scheme.tag.bg,
100
+ borderRadius: scheme.tag.borderRadius,
101
+ paddingHorizontal: scheme.tag.paddingX,
102
+ paddingVertical: scheme.tag.paddingY,
103
+ }, children: (0, jsx_runtime_1.jsx)(Text_1.Text, { type: "label-sm", color: scheme.tag.fg, selectable: false, children: label }) })) : null;
104
+ if (orientation === 'vertical') {
105
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: { flexDirection: 'row', alignItems: 'center', gap }, children: [labelChip, button] }));
106
+ }
107
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: { alignItems: 'center', gap }, children: [button, labelChip] }));
108
+ }
109
+ // ---------------------------------------------------------------------------
110
+ // SpeedDial
111
+ // ---------------------------------------------------------------------------
112
+ function SpeedDial({ children, icon = 'add', openIcon = 'close', open: controlledOpen, onOpenChange, defaultOpen = false, direction = 'up', intent = 'brand', size = 'default', backdrop = true, style, accessibilityLabel, }) {
113
+ const { components, colors, scheme } = (0, theme_1.useTheme)();
114
+ const { fabSize, actionSize, gap } = components.speedDial[size];
115
+ const fabColors = colors[intent].bold.default;
116
+ const isControlled = controlledOpen !== undefined;
117
+ const [internalOpen, setInternalOpen] = (0, react_1.useState)(defaultOpen);
118
+ const open = isControlled ? controlledOpen : internalOpen;
119
+ const setOpen = (next) => {
120
+ if (!isControlled)
121
+ setInternalOpen(next);
122
+ onOpenChange?.(next);
123
+ };
124
+ const orientation = direction === 'up' || direction === 'down' ? 'vertical' : 'horizontal';
125
+ // Mount + animate the actions group.
126
+ const anim = (0, react_1.useRef)(new react_native_1.Animated.Value(open ? 1 : 0)).current;
127
+ const [actionsMounted, setActionsMounted] = (0, react_1.useState)(open);
128
+ (0, react_1.useEffect)(() => {
129
+ if (open) {
130
+ setActionsMounted(true);
131
+ react_native_1.Animated.timing(anim, { toValue: 1, duration: DURATION_IN, useNativeDriver: USE_NATIVE_DRIVER }).start();
132
+ }
133
+ else if (actionsMounted) {
134
+ react_native_1.Animated.timing(anim, { toValue: 0, duration: DURATION_OUT, useNativeDriver: USE_NATIVE_DRIVER }).start(({ finished }) => {
135
+ if (finished)
136
+ setActionsMounted(false);
137
+ });
138
+ }
139
+ // eslint-disable-next-line react-hooks/exhaustive-deps
140
+ }, [open]);
141
+ const inset = (fabSize - actionSize) / 2;
142
+ const groupBase = { position: 'absolute', gap };
143
+ const groupStyle = direction === 'up'
144
+ ? { ...groupBase, bottom: '100%', right: inset, marginBottom: gap, alignItems: 'flex-end' }
145
+ : direction === 'down'
146
+ ? { ...groupBase, top: '100%', right: inset, marginTop: gap, alignItems: 'flex-end' }
147
+ : direction === 'left'
148
+ ? { ...groupBase, right: '100%', top: inset, marginRight: gap, alignItems: 'center' }
149
+ : { ...groupBase, left: '100%', top: inset, marginLeft: gap, alignItems: 'center' };
150
+ const groupFlex = direction === 'up' ? 'column-reverse' : direction === 'down' ? 'column' : direction === 'left' ? 'row-reverse' : 'row';
151
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [{ alignSelf: 'flex-start', position: 'relative', zIndex: open ? 1000 : 0 }, style], children: [open && backdrop ? ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: () => setOpen(false), accessibilityRole: "button", accessibilityLabel: "Close menu", style: react_native_1.Platform.select({
152
+ web: {
153
+ position: 'fixed',
154
+ top: 0,
155
+ left: 0,
156
+ right: 0,
157
+ bottom: 0,
158
+ backgroundColor: '#000000',
159
+ opacity: scheme.overlay.scrimOpacity,
160
+ zIndex: 0,
161
+ },
162
+ default: {
163
+ position: 'absolute',
164
+ top: -9999,
165
+ left: -9999,
166
+ width: 99999,
167
+ height: 99999,
168
+ backgroundColor: '#000000',
169
+ opacity: scheme.overlay.scrimOpacity,
170
+ },
171
+ }) })) : null, actionsMounted ? ((0, jsx_runtime_1.jsx)(react_native_1.Animated.View, { style: [
172
+ groupStyle,
173
+ {
174
+ flexDirection: groupFlex,
175
+ zIndex: 1,
176
+ opacity: anim,
177
+ transform: [{ scale: anim.interpolate({ inputRange: [0, 1], outputRange: [0.8, 1] }) }],
178
+ },
179
+ ], children: (0, jsx_runtime_1.jsx)(SpeedDialCtx.Provider, { value: { size, gap, orientation, onClose: () => setOpen(false) }, children: children }) })) : null, (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: () => setOpen(!open), accessibilityRole: "button", accessibilityLabel: accessibilityLabel || 'Actions', accessibilityState: { expanded: open }, style: {
180
+ width: fabSize,
181
+ height: fabSize,
182
+ borderRadius: fabSize / 2,
183
+ alignItems: 'center',
184
+ justifyContent: 'center',
185
+ backgroundColor: fabColors.bg,
186
+ zIndex: 2,
187
+ ...SHADOW,
188
+ }, children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: open ? openIcon : icon, size: FAB_ICON[size], color: fabColors.fg }) })] }));
189
+ }
@@ -0,0 +1 @@
1
+ export { SpeedDial, SpeedDialAction, type SpeedDialProps, type SpeedDialActionProps, type SpeedDialSize, type SpeedDialDirection, } from './SpeedDial';
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SpeedDialAction = exports.SpeedDial = void 0;
4
+ var SpeedDial_1 = require("./SpeedDial");
5
+ Object.defineProperty(exports, "SpeedDial", { enumerable: true, get: function () { return SpeedDial_1.SpeedDial; } });
6
+ Object.defineProperty(exports, "SpeedDialAction", { enumerable: true, get: function () { return SpeedDial_1.SpeedDialAction; } });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Spinner — indeterminate circular loading indicator.
3
+ *
4
+ * Maps 1:1 to the Figma <Spinner> component:
5
+ * intent → neutral | brand | danger (arc colour)
6
+ * size → small | default | large (diameter + ring stroke)
7
+ *
8
+ * A Spinner is always indeterminate: it shows that work is happening, not how
9
+ * much is left. For a known percentage use <Progress> instead.
10
+ *
11
+ * Geometry. The diameter and ring stroke are the two tokens this component
12
+ * introduces (`spinner/{size}/diameter`, `spinner/{size}/stroke`). Both are
13
+ * keyed by the `size` prop and constant across the three densities, like
14
+ * <Progress> track-height. So Spinner reads no density-varying spacing. The
15
+ * ring radius is half the diameter, computed here.
16
+ *
17
+ * The ring is drawn with borders: all four sides take the faint track colour,
18
+ * then the top border takes the intent colour to form one visible arc. The
19
+ * whole ring rotates, so the arc sweeps around. This needs no SVG and keeps the
20
+ * zero-dependency contract.
21
+ *
22
+ * Colours. The arc binds to the intent system
23
+ * (`colors[intent].bold.default.bg`) so it tracks the host intent and any
24
+ * ThemeProvider colour overrides. The track ring is the dedicated
25
+ * `control/spinner/track/bg` semantic (cool-grey/200 light, cool-grey/700
26
+ * dark, matching the <Progress> track), mirrored as `scheme.spinner.track`, so
27
+ * it follows light/dark colour mode.
28
+ */
29
+ import { type StyleProp, type ViewStyle } from 'react-native';
30
+ import type { IntentName } from '../../tokens';
31
+ export type SpinnerSize = 'small' | 'default' | 'large';
32
+ export type SpinnerProps = {
33
+ /** Semantic intent — drives the arc colour. */
34
+ intent?: IntentName;
35
+ /** Size variant — controls diameter and ring stroke. */
36
+ size?: SpinnerSize;
37
+ /** Outer style — use for positioning (margin, alignSelf). */
38
+ style?: StyleProp<ViewStyle>;
39
+ /** Accessibility label — describes what is loading. */
40
+ accessibilityLabel?: string;
41
+ };
42
+ export declare function Spinner({ intent, size, style, accessibilityLabel, }: SpinnerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Spinner = Spinner;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ /**
6
+ * Spinner — indeterminate circular loading indicator.
7
+ *
8
+ * Maps 1:1 to the Figma <Spinner> component:
9
+ * intent → neutral | brand | danger (arc colour)
10
+ * size → small | default | large (diameter + ring stroke)
11
+ *
12
+ * A Spinner is always indeterminate: it shows that work is happening, not how
13
+ * much is left. For a known percentage use <Progress> instead.
14
+ *
15
+ * Geometry. The diameter and ring stroke are the two tokens this component
16
+ * introduces (`spinner/{size}/diameter`, `spinner/{size}/stroke`). Both are
17
+ * keyed by the `size` prop and constant across the three densities, like
18
+ * <Progress> track-height. So Spinner reads no density-varying spacing. The
19
+ * ring radius is half the diameter, computed here.
20
+ *
21
+ * The ring is drawn with borders: all four sides take the faint track colour,
22
+ * then the top border takes the intent colour to form one visible arc. The
23
+ * whole ring rotates, so the arc sweeps around. This needs no SVG and keeps the
24
+ * zero-dependency contract.
25
+ *
26
+ * Colours. The arc binds to the intent system
27
+ * (`colors[intent].bold.default.bg`) so it tracks the host intent and any
28
+ * ThemeProvider colour overrides. The track ring is the dedicated
29
+ * `control/spinner/track/bg` semantic (cool-grey/200 light, cool-grey/700
30
+ * dark, matching the <Progress> track), mirrored as `scheme.spinner.track`, so
31
+ * it follows light/dark colour mode.
32
+ */
33
+ const react_1 = require("react");
34
+ const react_native_1 = require("react-native");
35
+ const theme_1 = require("../../theme");
36
+ // ---------------------------------------------------------------------------
37
+ // Constants
38
+ // ---------------------------------------------------------------------------
39
+ /** One full rotation, in milliseconds. */
40
+ const ROTATION_DURATION = 800;
41
+ // ---------------------------------------------------------------------------
42
+ // Component
43
+ // ---------------------------------------------------------------------------
44
+ function Spinner({ intent = 'brand', size = 'default', style, accessibilityLabel = 'Loading', }) {
45
+ const { components, colors, scheme } = (0, theme_1.useTheme)();
46
+ const { diameter, stroke } = components.spinner[size];
47
+ const arc = colors[intent].bold.default.bg;
48
+ const trackColor = scheme.spinner.track;
49
+ const spin = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
50
+ (0, react_1.useEffect)(() => {
51
+ spin.setValue(0);
52
+ const loop = react_native_1.Animated.loop(react_native_1.Animated.timing(spin, {
53
+ toValue: 1,
54
+ duration: ROTATION_DURATION,
55
+ easing: react_native_1.Easing.linear,
56
+ useNativeDriver: true,
57
+ }));
58
+ loop.start();
59
+ return () => loop.stop();
60
+ }, [spin]);
61
+ const rotate = spin.interpolate({
62
+ inputRange: [0, 1],
63
+ outputRange: ['0deg', '360deg'],
64
+ });
65
+ return ((0, jsx_runtime_1.jsx)(react_native_1.Animated.View, { accessibilityRole: "progressbar", accessibilityLabel: accessibilityLabel, accessibilityState: { busy: true }, style: [
66
+ {
67
+ width: diameter,
68
+ height: diameter,
69
+ borderRadius: diameter / 2,
70
+ borderWidth: stroke,
71
+ borderColor: trackColor,
72
+ borderTopColor: arc,
73
+ transform: [{ rotate }],
74
+ },
75
+ style,
76
+ ] }));
77
+ }
@@ -0,0 +1 @@
1
+ export { Spinner, type SpinnerProps, type SpinnerSize } from './Spinner';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Spinner = void 0;
4
+ var Spinner_1 = require("./Spinner");
5
+ Object.defineProperty(exports, "Spinner", { enumerable: true, get: function () { return Spinner_1.Spinner; } });
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Table — rows and columns of data.
3
+ *
4
+ * Compound component:
5
+ * <Table>
6
+ * <TableHead><TableRow><TableCell>Name</TableCell>…</TableRow></TableHead>
7
+ * <TableBody>
8
+ * <TableRow><TableCell>Ada</TableCell>…</TableRow>
9
+ * </TableBody>
10
+ * </Table>
11
+ *
12
+ * Maps 1:1 to the Figma <Table> component:
13
+ * size → small | default | large (cell padding + typography)
14
+ * row state → default | hover | selected | disabled
15
+ *
16
+ * React Native has no <table>, so the table is built from Views: each row is a
17
+ * flex row of cells. Keep the cell widths (flex or width) consistent across rows
18
+ * so columns line up. striped shades alternate body rows; hoverable highlights a
19
+ * row on hover; a row with onPress is pressable and can be selected.
20
+ *
21
+ * Colours come from the dedicated scheme.table slice (header, borders, row
22
+ * states), which reuses existing semantic values (surface, neutral, brand
23
+ * subtle), so no new semantic variables. Cell padding varies by density; the
24
+ * size prop drives typography. Fonts are consumer-loaded.
25
+ *
26
+ * Exports: Table, TableHead, TableBody, TableRow, TableCell.
27
+ */
28
+ import React from 'react';
29
+ import { type StyleProp, type ViewStyle, type GestureResponderEvent } from 'react-native';
30
+ export type TableSize = 'small' | 'default' | 'large';
31
+ export type TableCellAlign = 'left' | 'center' | 'right';
32
+ export type TableProps = {
33
+ children: React.ReactNode;
34
+ /** Size variant — cell padding and typography. */
35
+ size?: TableSize;
36
+ /** Shade alternate body rows. */
37
+ striped?: boolean;
38
+ /** Highlight a row on hover. */
39
+ hoverable?: boolean;
40
+ /** Outer style. */
41
+ style?: StyleProp<ViewStyle>;
42
+ /** Accessibility label for the table. */
43
+ accessibilityLabel?: string;
44
+ };
45
+ export type TableSectionProps = {
46
+ children: React.ReactNode;
47
+ };
48
+ export type TableRowProps = {
49
+ children: React.ReactNode;
50
+ /** Press handler — makes the row pressable. */
51
+ onPress?: (e: GestureResponderEvent) => void;
52
+ /** Selected (active) row styling. */
53
+ selected?: boolean;
54
+ /** Disabled row styling. */
55
+ disabled?: boolean;
56
+ /** Internal: row index, set by <TableBody> for striping. */
57
+ __index?: number;
58
+ };
59
+ export type TableCellProps = {
60
+ children?: React.ReactNode;
61
+ /** Text alignment. Defaults to left (right when numeric). */
62
+ align?: TableCellAlign;
63
+ /** Right-align for numbers. */
64
+ numeric?: boolean;
65
+ /** Flex grow factor. Defaults to 1. */
66
+ flex?: number;
67
+ /** Fixed width (overrides flex). */
68
+ width?: number;
69
+ };
70
+ export declare function TableCell({ children, align, numeric, flex, width }: TableCellProps): import("react/jsx-runtime").JSX.Element;
71
+ export declare function TableRow({ children, onPress, selected, disabled, __index, }: TableRowProps): import("react/jsx-runtime").JSX.Element;
72
+ export declare function TableHead({ children }: TableSectionProps): import("react/jsx-runtime").JSX.Element;
73
+ export declare function TableBody({ children }: TableSectionProps): import("react/jsx-runtime").JSX.Element;
74
+ export declare function Table({ children, size, striped, hoverable, style, accessibilityLabel, }: TableProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.TableCell = TableCell;
37
+ exports.TableRow = TableRow;
38
+ exports.TableHead = TableHead;
39
+ exports.TableBody = TableBody;
40
+ exports.Table = Table;
41
+ const jsx_runtime_1 = require("react/jsx-runtime");
42
+ /**
43
+ * Table — rows and columns of data.
44
+ *
45
+ * Compound component:
46
+ * <Table>
47
+ * <TableHead><TableRow><TableCell>Name</TableCell>…</TableRow></TableHead>
48
+ * <TableBody>
49
+ * <TableRow><TableCell>Ada</TableCell>…</TableRow>
50
+ * </TableBody>
51
+ * </Table>
52
+ *
53
+ * Maps 1:1 to the Figma <Table> component:
54
+ * size → small | default | large (cell padding + typography)
55
+ * row state → default | hover | selected | disabled
56
+ *
57
+ * React Native has no <table>, so the table is built from Views: each row is a
58
+ * flex row of cells. Keep the cell widths (flex or width) consistent across rows
59
+ * so columns line up. striped shades alternate body rows; hoverable highlights a
60
+ * row on hover; a row with onPress is pressable and can be selected.
61
+ *
62
+ * Colours come from the dedicated scheme.table slice (header, borders, row
63
+ * states), which reuses existing semantic values (surface, neutral, brand
64
+ * subtle), so no new semantic variables. Cell padding varies by density; the
65
+ * size prop drives typography. Fonts are consumer-loaded.
66
+ *
67
+ * Exports: Table, TableHead, TableBody, TableRow, TableCell.
68
+ */
69
+ const react_1 = __importStar(require("react"));
70
+ const react_native_1 = require("react-native");
71
+ const theme_1 = require("../../theme");
72
+ const tokens_1 = require("../../tokens");
73
+ const Text_1 = require("../Text");
74
+ const TableCtx = (0, react_1.createContext)(null);
75
+ function useTableContext(c) {
76
+ const ctx = (0, react_1.useContext)(TableCtx);
77
+ if (!ctx)
78
+ throw new Error(`<${c}> must be used within <Table>`);
79
+ return ctx;
80
+ }
81
+ /** Set by TableHead / TableBody so cells know whether they are headers. */
82
+ const SectionCtx = (0, react_1.createContext)({ isHeader: false });
83
+ // ---------------------------------------------------------------------------
84
+ // Constants
85
+ // ---------------------------------------------------------------------------
86
+ const HEADER_TYPE = {
87
+ small: 'label-sm',
88
+ default: 'label-md',
89
+ large: 'label-lg',
90
+ };
91
+ const BODY_TYPE = {
92
+ small: 'body-sm',
93
+ default: 'body-md',
94
+ large: 'body-lg',
95
+ };
96
+ // ---------------------------------------------------------------------------
97
+ // TableCell
98
+ // ---------------------------------------------------------------------------
99
+ function TableCell({ children, align, numeric = false, flex = 1, width }) {
100
+ const { size } = useTableContext('TableCell');
101
+ const { isHeader } = (0, react_1.useContext)(SectionCtx);
102
+ const { components, scheme } = (0, theme_1.useTheme)();
103
+ const tokens = components.table[size];
104
+ const effectiveAlign = align ?? (numeric ? 'right' : 'left');
105
+ const justify = effectiveAlign === 'right' ? 'flex-end' : effectiveAlign === 'center' ? 'center' : 'flex-start';
106
+ return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: {
107
+ ...(width != null ? { width } : { flex }),
108
+ paddingHorizontal: tokens.cellPaddingX,
109
+ paddingVertical: tokens.cellPaddingY,
110
+ justifyContent: 'center',
111
+ alignItems: justify,
112
+ }, children: typeof children === 'string' ? ((0, jsx_runtime_1.jsx)(Text_1.Text, { type: isHeader ? HEADER_TYPE[size] : BODY_TYPE[size], color: scheme.text.primary, numberOfLines: 1, style: { textAlign: effectiveAlign }, children: children })) : (children) }));
113
+ }
114
+ // ---------------------------------------------------------------------------
115
+ // TableRow
116
+ // ---------------------------------------------------------------------------
117
+ function TableRow({ children, onPress, selected = false, disabled = false, __index = 0, }) {
118
+ const { striped, hoverable } = useTableContext('TableRow');
119
+ const { isHeader } = (0, react_1.useContext)(SectionCtx);
120
+ const { scheme } = (0, theme_1.useTheme)();
121
+ const table = scheme.table;
122
+ const [isHovered, setIsHovered] = (0, react_1.useState)(false);
123
+ const interactive = !isHeader && !disabled && (hoverable || Boolean(onPress));
124
+ const backgroundColor = isHeader
125
+ ? table.headerBg
126
+ : selected
127
+ ? isHovered
128
+ ? table.selectedHoverBg
129
+ : table.selectedBg
130
+ : isHovered
131
+ ? table.rowHover
132
+ : striped && __index % 2 === 1
133
+ ? table.stripe
134
+ : 'transparent';
135
+ const rowStyle = {
136
+ flexDirection: 'row',
137
+ alignItems: 'stretch',
138
+ backgroundColor,
139
+ borderBottomWidth: tokens_1.controlTokens.borderWidth,
140
+ borderBottomColor: table.border,
141
+ opacity: disabled ? 0.5 : 1,
142
+ };
143
+ if (interactive) {
144
+ return ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: onPress, onHoverIn: () => setIsHovered(true), onHoverOut: () => setIsHovered(false), accessibilityRole: onPress ? 'button' : undefined, accessibilityState: { selected, disabled }, style: rowStyle, children: children }));
145
+ }
146
+ return (0, jsx_runtime_1.jsx)(react_native_1.View, { style: rowStyle, children: children });
147
+ }
148
+ // ---------------------------------------------------------------------------
149
+ // TableHead / TableBody
150
+ // ---------------------------------------------------------------------------
151
+ function TableHead({ children }) {
152
+ return (0, jsx_runtime_1.jsx)(SectionCtx.Provider, { value: { isHeader: true }, children: children });
153
+ }
154
+ function TableBody({ children }) {
155
+ const rows = react_1.default.Children.toArray(children).filter(Boolean);
156
+ return ((0, jsx_runtime_1.jsx)(SectionCtx.Provider, { value: { isHeader: false }, children: rows.map((child, i) => react_1.default.cloneElement(child, {
157
+ key: `row-${i}`,
158
+ __index: i,
159
+ })) }));
160
+ }
161
+ // ---------------------------------------------------------------------------
162
+ // Table
163
+ // ---------------------------------------------------------------------------
164
+ function Table({ children, size = 'default', striped = false, hoverable = false, style, accessibilityLabel, }) {
165
+ const { scheme } = (0, theme_1.useTheme)();
166
+ return ((0, jsx_runtime_1.jsx)(TableCtx.Provider, { value: { size, striped, hoverable }, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { accessibilityLabel: accessibilityLabel, style: [
167
+ {
168
+ borderWidth: tokens_1.controlTokens.borderWidth,
169
+ borderColor: scheme.table.border,
170
+ borderRadius: scheme.surface.overlay.borderRadius,
171
+ overflow: 'hidden',
172
+ backgroundColor: scheme.surface.overlay.bg,
173
+ },
174
+ style,
175
+ ], children: children }) }));
176
+ }
@@ -0,0 +1 @@
1
+ export { Table, TableHead, TableBody, TableRow, TableCell, type TableProps, type TableSectionProps, type TableRowProps, type TableCellProps, type TableSize, type TableCellAlign, } from './Table';