@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.
- package/README.md +6 -0
- package/dist/components/Accordion/Accordion.d.ts +80 -0
- package/dist/components/Accordion/Accordion.js +157 -0
- package/dist/components/Accordion/index.d.ts +1 -0
- package/dist/components/Accordion/index.js +6 -0
- package/dist/components/AppBar/AppBar.d.ts +47 -0
- package/dist/components/AppBar/AppBar.js +47 -0
- package/dist/components/AppBar/index.d.ts +1 -0
- package/dist/components/AppBar/index.js +5 -0
- package/dist/components/Autocomplete/Autocomplete.d.ts +70 -0
- package/dist/components/Autocomplete/Autocomplete.js +249 -0
- package/dist/components/Autocomplete/index.d.ts +1 -0
- package/dist/components/Autocomplete/index.js +5 -0
- package/dist/components/Backdrop/Backdrop.d.ts +32 -0
- package/dist/components/Backdrop/Backdrop.js +74 -0
- package/dist/components/Backdrop/index.d.ts +1 -0
- package/dist/components/Backdrop/index.js +5 -0
- package/dist/components/BottomSheet/BottomSheet.d.ts +50 -0
- package/dist/components/BottomSheet/BottomSheet.js +159 -0
- package/dist/components/BottomSheet/index.d.ts +1 -0
- package/dist/components/BottomSheet/index.js +6 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +63 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.js +143 -0
- package/dist/components/Breadcrumbs/index.d.ts +1 -0
- package/dist/components/Breadcrumbs/index.js +6 -0
- package/dist/components/CodeBlock/CodeBlock.d.ts +42 -0
- package/dist/components/CodeBlock/CodeBlock.js +110 -0
- package/dist/components/CodeBlock/index.d.ts +1 -0
- package/dist/components/CodeBlock/index.js +5 -0
- package/dist/components/Drawer/Drawer.d.ts +51 -0
- package/dist/components/Drawer/Drawer.js +168 -0
- package/dist/components/Drawer/index.d.ts +1 -0
- package/dist/components/Drawer/index.js +6 -0
- package/dist/components/Link/Link.d.ts +51 -0
- package/dist/components/Link/Link.js +73 -0
- package/dist/components/Link/index.d.ts +1 -0
- package/dist/components/Link/index.js +5 -0
- package/dist/components/Menu/Menu.d.ts +91 -0
- package/dist/components/Menu/Menu.js +211 -0
- package/dist/components/Menu/index.d.ts +1 -0
- package/dist/components/Menu/index.js +9 -0
- package/dist/components/Slider/Slider.d.ts +47 -0
- package/dist/components/Slider/Slider.js +132 -0
- package/dist/components/Slider/index.d.ts +1 -0
- package/dist/components/Slider/index.js +5 -0
- package/dist/components/SpeedDial/SpeedDial.d.ts +72 -0
- package/dist/components/SpeedDial/SpeedDial.js +189 -0
- package/dist/components/SpeedDial/index.d.ts +1 -0
- package/dist/components/SpeedDial/index.js +6 -0
- package/dist/components/Spinner/Spinner.d.ts +42 -0
- package/dist/components/Spinner/Spinner.js +77 -0
- package/dist/components/Spinner/index.d.ts +1 -0
- package/dist/components/Spinner/index.js +5 -0
- package/dist/components/Table/Table.d.ts +74 -0
- package/dist/components/Table/Table.js +176 -0
- package/dist/components/Table/index.d.ts +1 -0
- package/dist/components/Table/index.js +9 -0
- package/dist/components/Tabs/Tabs.d.ts +1 -1
- package/dist/components/Tabs/Tabs.js +5 -2
- package/dist/components/ToggleButtonGroup/ToggleButtonGroup.d.ts +69 -0
- package/dist/components/ToggleButtonGroup/ToggleButtonGroup.js +158 -0
- package/dist/components/ToggleButtonGroup/index.d.ts +1 -0
- package/dist/components/ToggleButtonGroup/index.js +6 -0
- package/dist/index.d.ts +17 -2
- package/dist/index.js +51 -2
- package/dist/theme/ThemeContext.d.ts +8 -1
- package/dist/theme/ThemeContext.js +7 -4
- package/dist/theme/applyCastTheme.d.ts +75 -0
- package/dist/theme/applyCastTheme.js +95 -0
- package/dist/theme/index.d.ts +2 -1
- package/dist/theme/index.js +3 -1
- package/dist/theme/themes.js +192 -0
- package/dist/theme/types.d.ts +192 -0
- package/dist/tokens/colors.d.ts +48 -0
- package/dist/tokens/colors.js +49 -1
- package/dist/tokens/index.d.ts +1 -1
- package/dist/tokens/index.js +4 -1
- package/package.json +2 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Link — an inline or standalone text link.
|
|
3
|
+
*
|
|
4
|
+
* Maps 1:1 to the Figma <Link> component:
|
|
5
|
+
* intent → neutral | brand | danger (link colour)
|
|
6
|
+
* prominence is fixed to the subtle scheme (coloured text, no fill)
|
|
7
|
+
* size → small | default | large (typography + icon size + gap)
|
|
8
|
+
* underline → none | hover | always
|
|
9
|
+
*
|
|
10
|
+
* Colours come from the intent system's subtle prominence fg
|
|
11
|
+
* (colors[intent].subtle.{state}.fg). Brand is the default, so a link reads
|
|
12
|
+
* blue by default and the darker blue on hover; disabled uses the shared
|
|
13
|
+
* disabled fg. The gap between an icon and the label is the one spacing token
|
|
14
|
+
* (link/{size}/gap), density-varying. Typography is the label scale matched to
|
|
15
|
+
* size; the icon size follows the named Icon scale 1:1 (small→16, default→20,
|
|
16
|
+
* large→24), like Button.
|
|
17
|
+
*
|
|
18
|
+
* Links navigate. On web, passing `href` renders a real anchor through
|
|
19
|
+
* react-native-web; on native it is informational and `onPress` drives
|
|
20
|
+
* navigation. Fonts are consumer-loaded (Inter).
|
|
21
|
+
*/
|
|
22
|
+
import React from 'react';
|
|
23
|
+
import { type ViewStyle, type StyleProp, type GestureResponderEvent } from 'react-native';
|
|
24
|
+
import type { IntentName } from '../../tokens';
|
|
25
|
+
export type LinkSize = 'small' | 'default' | 'large';
|
|
26
|
+
export type LinkUnderline = 'none' | 'hover' | 'always';
|
|
27
|
+
export type LinkProps = {
|
|
28
|
+
/** The link text. */
|
|
29
|
+
children: string;
|
|
30
|
+
/** Semantic intent — drives the link colour. Defaults to brand. */
|
|
31
|
+
intent?: IntentName;
|
|
32
|
+
/** Size variant — controls typography scale, icon size, and gap. */
|
|
33
|
+
size?: LinkSize;
|
|
34
|
+
/** When the underline shows. Defaults to "hover". */
|
|
35
|
+
underline?: LinkUnderline;
|
|
36
|
+
/** Disables interaction and applies muted styling. */
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
/** Icon before the label — Material Symbols name string or a ReactNode. */
|
|
39
|
+
leadingIcon?: string | React.ReactNode;
|
|
40
|
+
/** Icon after the label — Material Symbols name string or a ReactNode. */
|
|
41
|
+
trailingIcon?: string | React.ReactNode;
|
|
42
|
+
/** Destination URL. Renders a real anchor on web; informational on native. */
|
|
43
|
+
href?: string;
|
|
44
|
+
/** Press handler. */
|
|
45
|
+
onPress?: (e: GestureResponderEvent) => void;
|
|
46
|
+
/** Outer style — use for positioning (margin, flex, alignSelf). */
|
|
47
|
+
style?: StyleProp<ViewStyle>;
|
|
48
|
+
/** Accessibility label — falls back to the link text. */
|
|
49
|
+
accessibilityLabel?: string;
|
|
50
|
+
};
|
|
51
|
+
export declare function Link({ children, intent, size, underline, disabled, leadingIcon, trailingIcon, href, onPress, style, accessibilityLabel, }: LinkProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Link = Link;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
/**
|
|
6
|
+
* Link — an inline or standalone text link.
|
|
7
|
+
*
|
|
8
|
+
* Maps 1:1 to the Figma <Link> component:
|
|
9
|
+
* intent → neutral | brand | danger (link colour)
|
|
10
|
+
* prominence is fixed to the subtle scheme (coloured text, no fill)
|
|
11
|
+
* size → small | default | large (typography + icon size + gap)
|
|
12
|
+
* underline → none | hover | always
|
|
13
|
+
*
|
|
14
|
+
* Colours come from the intent system's subtle prominence fg
|
|
15
|
+
* (colors[intent].subtle.{state}.fg). Brand is the default, so a link reads
|
|
16
|
+
* blue by default and the darker blue on hover; disabled uses the shared
|
|
17
|
+
* disabled fg. The gap between an icon and the label is the one spacing token
|
|
18
|
+
* (link/{size}/gap), density-varying. Typography is the label scale matched to
|
|
19
|
+
* size; the icon size follows the named Icon scale 1:1 (small→16, default→20,
|
|
20
|
+
* large→24), like Button.
|
|
21
|
+
*
|
|
22
|
+
* Links navigate. On web, passing `href` renders a real anchor through
|
|
23
|
+
* react-native-web; on native it is informational and `onPress` drives
|
|
24
|
+
* navigation. Fonts are consumer-loaded (Inter).
|
|
25
|
+
*/
|
|
26
|
+
const react_1 = require("react");
|
|
27
|
+
const react_native_1 = require("react-native");
|
|
28
|
+
const theme_1 = require("../../theme");
|
|
29
|
+
const tokens_1 = require("../../tokens");
|
|
30
|
+
const Icon_1 = require("../Icon");
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Constants
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
/** Maps link size → label typography scale. */
|
|
35
|
+
const LABEL_SCALE = {
|
|
36
|
+
small: 'sm',
|
|
37
|
+
default: 'md',
|
|
38
|
+
large: 'lg',
|
|
39
|
+
};
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Component
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
function Link({ children, intent = 'brand', size = 'default', underline = 'hover', disabled = false, leadingIcon, trailingIcon, href, onPress, style, accessibilityLabel, }) {
|
|
44
|
+
const { components, colors, scheme } = (0, theme_1.useTheme)();
|
|
45
|
+
const [isHovered, setIsHovered] = (0, react_1.useState)(false);
|
|
46
|
+
const { gap } = components.link[size];
|
|
47
|
+
const labelTokens = tokens_1.label[LABEL_SCALE[size]];
|
|
48
|
+
const subtle = colors[intent].subtle;
|
|
49
|
+
const fg = disabled
|
|
50
|
+
? scheme.disabled.fg
|
|
51
|
+
: isHovered
|
|
52
|
+
? subtle.hover.fg
|
|
53
|
+
: subtle.default.fg;
|
|
54
|
+
const showUnderline = underline === 'always' || (underline === 'hover' && isHovered && !disabled);
|
|
55
|
+
const resolvedLeading = typeof leadingIcon === 'string' ? ((0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: leadingIcon, size: size, color: fg })) : (leadingIcon);
|
|
56
|
+
const resolvedTrailing = typeof trailingIcon === 'string' ? ((0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: trailingIcon, size: size, color: fg })) : (trailingIcon);
|
|
57
|
+
// react-native-web forwards `href` to render an <a>; native ignores it.
|
|
58
|
+
const hrefProps = href ? { href } : {};
|
|
59
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { ...hrefProps, onPress: disabled ? undefined : onPress, disabled: disabled, onHoverIn: () => setIsHovered(true), onHoverOut: () => setIsHovered(false), accessibilityRole: "link", accessibilityLabel: accessibilityLabel || children, accessibilityState: { disabled }, style: style, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
|
|
60
|
+
flexDirection: 'row',
|
|
61
|
+
alignItems: 'center',
|
|
62
|
+
alignSelf: 'flex-start',
|
|
63
|
+
gap,
|
|
64
|
+
}, children: [resolvedLeading, (0, jsx_runtime_1.jsx)(react_native_1.Text, { selectable: false, style: {
|
|
65
|
+
fontFamily: tokens_1.fontFamily.sans,
|
|
66
|
+
fontWeight: tokens_1.fontWeight.medium,
|
|
67
|
+
fontSize: labelTokens.fontSize,
|
|
68
|
+
lineHeight: labelTokens.lineHeight,
|
|
69
|
+
letterSpacing: labelTokens.letterSpacing,
|
|
70
|
+
color: fg,
|
|
71
|
+
textDecorationLine: showUnderline ? 'underline' : 'none',
|
|
72
|
+
}, children: children }), resolvedTrailing] }) }));
|
|
73
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Link, type LinkProps, type LinkSize, type LinkUnderline } from './Link';
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Menu — a transient overlay of actions, anchored to a trigger.
|
|
3
|
+
*
|
|
4
|
+
* Compound component. <Menu> owns the open state and the anchored overlay;
|
|
5
|
+
* <MenuItem> is one action row; <MenuDivider> separates groups; <MenuLabel> is
|
|
6
|
+
* a section heading.
|
|
7
|
+
*
|
|
8
|
+
* <Menu trigger={<Icon name="more_vert" />}>
|
|
9
|
+
* <MenuItem leadingIcon="edit" onPress={edit}>Edit</MenuItem>
|
|
10
|
+
* <MenuItem leadingIcon="content_copy" onPress={dupe}>Duplicate</MenuItem>
|
|
11
|
+
* <MenuDivider />
|
|
12
|
+
* <MenuItem leadingIcon="delete" intent="danger" onPress={remove}>Delete</MenuItem>
|
|
13
|
+
* </Menu>
|
|
14
|
+
*
|
|
15
|
+
* Maps 1:1 to the Figma <Menu> component:
|
|
16
|
+
* size → small | default | large (item typography + icon size)
|
|
17
|
+
* item state → default | hover | selected | disabled
|
|
18
|
+
*
|
|
19
|
+
* A Menu is not a Select. It fires actions; it does not hold a form value. So it
|
|
20
|
+
* has its own dedicated tokens (menu/*) and its own colour slice
|
|
21
|
+
* (scheme.menu.item), mirroring the select shape but namespaced to menu, so the
|
|
22
|
+
* Figma <Menu> set binds to menu variables, not select ones. Item spacing varies
|
|
23
|
+
* by density; the size prop drives typography (like Select). Surface reuses the
|
|
24
|
+
* shared overlay tokens. Fonts are consumer-loaded.
|
|
25
|
+
*
|
|
26
|
+
* Exports:
|
|
27
|
+
* Menu — trigger + anchored overlay
|
|
28
|
+
* MenuItem — one action row
|
|
29
|
+
* MenuDivider — a separator line
|
|
30
|
+
* MenuLabel — a section heading
|
|
31
|
+
* MenuContent — the floating card, for custom overlay/anchoring
|
|
32
|
+
*/
|
|
33
|
+
import React from 'react';
|
|
34
|
+
import { type ViewStyle, type StyleProp, type GestureResponderEvent } from 'react-native';
|
|
35
|
+
import type { IntentName } from '../../tokens';
|
|
36
|
+
export type MenuSize = 'small' | 'default' | 'large';
|
|
37
|
+
export type MenuPlacement = 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end';
|
|
38
|
+
export type MenuProps = {
|
|
39
|
+
/** The element that opens the menu. Menu owns the press. */
|
|
40
|
+
trigger: React.ReactNode;
|
|
41
|
+
/** Menu rows (MenuItem / MenuDivider / MenuLabel). */
|
|
42
|
+
children: React.ReactNode;
|
|
43
|
+
/** Size variant — item typography and icon size. */
|
|
44
|
+
size?: MenuSize;
|
|
45
|
+
/** Where the overlay opens relative to the trigger. */
|
|
46
|
+
placement?: MenuPlacement;
|
|
47
|
+
/** Controlled open state. Omit for uncontrolled. */
|
|
48
|
+
open?: boolean;
|
|
49
|
+
/** Open-state change handler. */
|
|
50
|
+
onOpenChange?: (open: boolean) => void;
|
|
51
|
+
/** Initial open state when uncontrolled. */
|
|
52
|
+
defaultOpen?: boolean;
|
|
53
|
+
/** Close the menu when an item is pressed. Defaults to true. */
|
|
54
|
+
closeOnSelect?: boolean;
|
|
55
|
+
/** Outer style for the anchor wrapper. */
|
|
56
|
+
style?: StyleProp<ViewStyle>;
|
|
57
|
+
/** Accessibility label for the trigger. */
|
|
58
|
+
accessibilityLabel?: string;
|
|
59
|
+
};
|
|
60
|
+
export type MenuItemProps = {
|
|
61
|
+
/** The item label. */
|
|
62
|
+
children: string;
|
|
63
|
+
/** Press handler. */
|
|
64
|
+
onPress?: (e: GestureResponderEvent) => void;
|
|
65
|
+
/** Leading icon — Material Symbols name string or a ReactNode. */
|
|
66
|
+
leadingIcon?: string | React.ReactNode;
|
|
67
|
+
/** Trailing icon — Material Symbols name string or a ReactNode. */
|
|
68
|
+
trailingIcon?: string | React.ReactNode;
|
|
69
|
+
/** Keyboard shortcut hint shown at the end of the row. */
|
|
70
|
+
shortcut?: string;
|
|
71
|
+
/** Marks the item as active. Shows the selected colours and a check. */
|
|
72
|
+
selected?: boolean;
|
|
73
|
+
/** Intent — neutral (default) or danger for destructive actions. */
|
|
74
|
+
intent?: Extract<IntentName, 'neutral' | 'danger'>;
|
|
75
|
+
/** Disables the item. */
|
|
76
|
+
disabled?: boolean;
|
|
77
|
+
/** Accessibility label — falls back to the label text. */
|
|
78
|
+
accessibilityLabel?: string;
|
|
79
|
+
};
|
|
80
|
+
export type MenuLabelProps = {
|
|
81
|
+
children: string;
|
|
82
|
+
};
|
|
83
|
+
export declare function MenuItem({ children, onPress, leadingIcon, trailingIcon, shortcut, selected, intent, disabled, accessibilityLabel, }: MenuItemProps): import("react/jsx-runtime").JSX.Element;
|
|
84
|
+
export declare function MenuLabel({ children }: MenuLabelProps): import("react/jsx-runtime").JSX.Element;
|
|
85
|
+
export declare function MenuDivider(): import("react/jsx-runtime").JSX.Element;
|
|
86
|
+
export type MenuContentProps = {
|
|
87
|
+
children: React.ReactNode;
|
|
88
|
+
style?: StyleProp<ViewStyle>;
|
|
89
|
+
};
|
|
90
|
+
export declare function MenuContent({ children, style }: MenuContentProps): import("react/jsx-runtime").JSX.Element;
|
|
91
|
+
export declare function Menu({ trigger, children, size, placement, open: controlledOpen, onOpenChange, defaultOpen, closeOnSelect, style, accessibilityLabel, }: MenuProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MenuItem = MenuItem;
|
|
4
|
+
exports.MenuLabel = MenuLabel;
|
|
5
|
+
exports.MenuDivider = MenuDivider;
|
|
6
|
+
exports.MenuContent = MenuContent;
|
|
7
|
+
exports.Menu = Menu;
|
|
8
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
9
|
+
/**
|
|
10
|
+
* Menu — a transient overlay of actions, anchored to a trigger.
|
|
11
|
+
*
|
|
12
|
+
* Compound component. <Menu> owns the open state and the anchored overlay;
|
|
13
|
+
* <MenuItem> is one action row; <MenuDivider> separates groups; <MenuLabel> is
|
|
14
|
+
* a section heading.
|
|
15
|
+
*
|
|
16
|
+
* <Menu trigger={<Icon name="more_vert" />}>
|
|
17
|
+
* <MenuItem leadingIcon="edit" onPress={edit}>Edit</MenuItem>
|
|
18
|
+
* <MenuItem leadingIcon="content_copy" onPress={dupe}>Duplicate</MenuItem>
|
|
19
|
+
* <MenuDivider />
|
|
20
|
+
* <MenuItem leadingIcon="delete" intent="danger" onPress={remove}>Delete</MenuItem>
|
|
21
|
+
* </Menu>
|
|
22
|
+
*
|
|
23
|
+
* Maps 1:1 to the Figma <Menu> component:
|
|
24
|
+
* size → small | default | large (item typography + icon size)
|
|
25
|
+
* item state → default | hover | selected | disabled
|
|
26
|
+
*
|
|
27
|
+
* A Menu is not a Select. It fires actions; it does not hold a form value. So it
|
|
28
|
+
* has its own dedicated tokens (menu/*) and its own colour slice
|
|
29
|
+
* (scheme.menu.item), mirroring the select shape but namespaced to menu, so the
|
|
30
|
+
* Figma <Menu> set binds to menu variables, not select ones. Item spacing varies
|
|
31
|
+
* by density; the size prop drives typography (like Select). Surface reuses the
|
|
32
|
+
* shared overlay tokens. Fonts are consumer-loaded.
|
|
33
|
+
*
|
|
34
|
+
* Exports:
|
|
35
|
+
* Menu — trigger + anchored overlay
|
|
36
|
+
* MenuItem — one action row
|
|
37
|
+
* MenuDivider — a separator line
|
|
38
|
+
* MenuLabel — a section heading
|
|
39
|
+
* MenuContent — the floating card, for custom overlay/anchoring
|
|
40
|
+
*/
|
|
41
|
+
const react_1 = require("react");
|
|
42
|
+
const react_native_1 = require("react-native");
|
|
43
|
+
const theme_1 = require("../../theme");
|
|
44
|
+
const tokens_1 = require("../../tokens");
|
|
45
|
+
const Text_1 = require("../Text");
|
|
46
|
+
const Icon_1 = require("../Icon");
|
|
47
|
+
const MenuCtx = (0, react_1.createContext)(null);
|
|
48
|
+
function useMenuContext(component) {
|
|
49
|
+
const ctx = (0, react_1.useContext)(MenuCtx);
|
|
50
|
+
if (!ctx)
|
|
51
|
+
throw new Error(`<${component}> must be used within <Menu>`);
|
|
52
|
+
return ctx;
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Constants
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
/** Maps menu size → label typography scale (Text component `type`). */
|
|
58
|
+
const LABEL_TYPE = {
|
|
59
|
+
small: 'label-sm',
|
|
60
|
+
default: 'label-md',
|
|
61
|
+
large: 'label-lg',
|
|
62
|
+
};
|
|
63
|
+
/** Minimum width of the floating card. Layout default, not a token. */
|
|
64
|
+
const MIN_WIDTH = 180;
|
|
65
|
+
/** The card never grows past this height before it scrolls. */
|
|
66
|
+
const CONTENT_MAX_HEIGHT = 320;
|
|
67
|
+
const SHADOW_WEB = {
|
|
68
|
+
boxShadow: '0px 2px 4px -2px rgba(0,0,0,0.05), 0px 4px 6px -1px rgba(0,0,0,0.07)',
|
|
69
|
+
};
|
|
70
|
+
const SHADOW_NATIVE = {
|
|
71
|
+
shadowColor: '#000000',
|
|
72
|
+
shadowOffset: { width: 0, height: 4 },
|
|
73
|
+
shadowOpacity: 0.07,
|
|
74
|
+
shadowRadius: 6,
|
|
75
|
+
elevation: 4,
|
|
76
|
+
};
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// MenuItem
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
function MenuItem({ children, onPress, leadingIcon, trailingIcon, shortcut, selected = false, intent = 'neutral', disabled = false, accessibilityLabel, }) {
|
|
81
|
+
const { size, onClose, closeOnSelect } = useMenuContext('MenuItem');
|
|
82
|
+
const { components, colors, scheme } = (0, theme_1.useTheme)();
|
|
83
|
+
const tokens = components.menu.item;
|
|
84
|
+
const [isHovered, setIsHovered] = (0, react_1.useState)(false);
|
|
85
|
+
// Danger items use the intent system; neutral items use scheme.menu.item.
|
|
86
|
+
const danger = intent === 'danger';
|
|
87
|
+
const state = disabled
|
|
88
|
+
? scheme.menu.item.disabled
|
|
89
|
+
: selected && isHovered
|
|
90
|
+
? scheme.menu.item.selectedHover
|
|
91
|
+
: selected
|
|
92
|
+
? scheme.menu.item.selected
|
|
93
|
+
: isHovered
|
|
94
|
+
? scheme.menu.item.hover
|
|
95
|
+
: scheme.menu.item.default;
|
|
96
|
+
const fg = disabled
|
|
97
|
+
? scheme.menu.item.disabled.fg
|
|
98
|
+
: danger
|
|
99
|
+
? isHovered
|
|
100
|
+
? colors.danger.subtle.hover.fg
|
|
101
|
+
: colors.danger.subtle.default.fg
|
|
102
|
+
: state.fg;
|
|
103
|
+
const bg = danger && isHovered && !disabled ? colors.danger.subtle.hover.bg : state.bg;
|
|
104
|
+
const resolvedLeading = typeof leadingIcon === 'string' ? ((0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: leadingIcon, size: size, color: fg })) : (leadingIcon);
|
|
105
|
+
const resolvedTrailing = typeof trailingIcon === 'string' ? ((0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: trailingIcon, size: size, color: fg })) : (trailingIcon);
|
|
106
|
+
const handlePress = (e) => {
|
|
107
|
+
if (disabled)
|
|
108
|
+
return;
|
|
109
|
+
onPress?.(e);
|
|
110
|
+
if (closeOnSelect)
|
|
111
|
+
onClose();
|
|
112
|
+
};
|
|
113
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.Pressable, { onPress: handlePress, disabled: disabled, onHoverIn: () => setIsHovered(true), onHoverOut: () => setIsHovered(false), accessibilityRole: "menuitem", accessibilityLabel: accessibilityLabel || children, accessibilityState: { disabled, selected }, style: {
|
|
114
|
+
flexDirection: 'row',
|
|
115
|
+
alignItems: 'center',
|
|
116
|
+
gap: tokens.gap,
|
|
117
|
+
paddingHorizontal: tokens.paddingX,
|
|
118
|
+
paddingVertical: tokens.paddingY,
|
|
119
|
+
borderRadius: tokens.borderRadius,
|
|
120
|
+
backgroundColor: bg,
|
|
121
|
+
}, children: [resolvedLeading, (0, jsx_runtime_1.jsx)(Text_1.Text, { type: LABEL_TYPE[size], color: fg, selectable: false, style: { flex: 1 }, children: children }), shortcut ? ((0, jsx_runtime_1.jsx)(Text_1.Text, { type: "caption", color: scheme.text.description, selectable: false, children: shortcut })) : null, selected && !disabled ? (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "check", size: size, color: fg }) : resolvedTrailing] }));
|
|
122
|
+
}
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// MenuLabel + MenuDivider
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
function MenuLabel({ children }) {
|
|
127
|
+
const { components, scheme } = (0, theme_1.useTheme)();
|
|
128
|
+
const tokens = components.menu.group;
|
|
129
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: { paddingHorizontal: tokens.paddingX, paddingVertical: tokens.labelPaddingY }, children: (0, jsx_runtime_1.jsx)(Text_1.Text, { type: "caption", color: scheme.text.description, selectable: false, style: { textTransform: 'uppercase' }, children: children }) }));
|
|
130
|
+
}
|
|
131
|
+
function MenuDivider() {
|
|
132
|
+
const { components, scheme } = (0, theme_1.useTheme)();
|
|
133
|
+
const tokens = components.menu.separator;
|
|
134
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: { paddingVertical: tokens.marginY }, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: { height: 1, backgroundColor: scheme.menu.separator } }) }));
|
|
135
|
+
}
|
|
136
|
+
function MenuContent({ children, style }) {
|
|
137
|
+
const { components, scheme } = (0, theme_1.useTheme)();
|
|
138
|
+
const tokens = components.menu.content;
|
|
139
|
+
const surface = scheme.surface;
|
|
140
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
141
|
+
{
|
|
142
|
+
minWidth: MIN_WIDTH,
|
|
143
|
+
backgroundColor: surface.overlay.bg,
|
|
144
|
+
borderWidth: tokens_1.controlTokens.borderWidth,
|
|
145
|
+
borderColor: surface.overlay.border,
|
|
146
|
+
borderRadius: surface.overlay.borderRadius,
|
|
147
|
+
paddingVertical: tokens.paddingY,
|
|
148
|
+
maxHeight: CONTENT_MAX_HEIGHT,
|
|
149
|
+
...(react_native_1.Platform.OS === 'web' ? SHADOW_WEB : SHADOW_NATIVE),
|
|
150
|
+
},
|
|
151
|
+
style,
|
|
152
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.ScrollView, { nestedScrollEnabled: true, keyboardShouldPersistTaps: "handled", children: children }) }));
|
|
153
|
+
}
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// Menu — trigger + anchored overlay
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
/** Absolute-position styles for the four placements. */
|
|
158
|
+
function placementStyle(placement) {
|
|
159
|
+
switch (placement) {
|
|
160
|
+
case 'bottom-end':
|
|
161
|
+
return { position: 'absolute', top: '100%', right: 0, paddingTop: 4 };
|
|
162
|
+
case 'top-start':
|
|
163
|
+
return { position: 'absolute', bottom: '100%', left: 0, paddingBottom: 4 };
|
|
164
|
+
case 'top-end':
|
|
165
|
+
return { position: 'absolute', bottom: '100%', right: 0, paddingBottom: 4 };
|
|
166
|
+
case 'bottom-start':
|
|
167
|
+
default:
|
|
168
|
+
return { position: 'absolute', top: '100%', left: 0, paddingTop: 4 };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function Menu({ trigger, children, size = 'default', placement = 'bottom-start', open: controlledOpen, onOpenChange, defaultOpen = false, closeOnSelect = true, style, accessibilityLabel, }) {
|
|
172
|
+
const [internalOpen, setInternalOpen] = (0, react_1.useState)(defaultOpen);
|
|
173
|
+
const isControlled = controlledOpen !== undefined;
|
|
174
|
+
const open = isControlled ? controlledOpen : internalOpen;
|
|
175
|
+
const setOpen = (next) => {
|
|
176
|
+
if (!isControlled)
|
|
177
|
+
setInternalOpen(next);
|
|
178
|
+
onOpenChange?.(next);
|
|
179
|
+
};
|
|
180
|
+
// Escape closes the menu on web.
|
|
181
|
+
(0, react_1.useEffect)(() => {
|
|
182
|
+
if (!open || react_native_1.Platform.OS !== 'web')
|
|
183
|
+
return;
|
|
184
|
+
const onKey = (e) => {
|
|
185
|
+
if (e.key === 'Escape') {
|
|
186
|
+
e.stopPropagation();
|
|
187
|
+
setOpen(false);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
document.addEventListener('keydown', onKey);
|
|
191
|
+
return () => document.removeEventListener('keydown', onKey);
|
|
192
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
193
|
+
}, [open]);
|
|
194
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [{ alignSelf: 'flex-start', position: 'relative', zIndex: open ? 1000 : 0 }, style], children: [(0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: () => setOpen(!open), accessibilityRole: "button", accessibilityLabel: accessibilityLabel || 'Open menu', accessibilityState: { expanded: open }, children: trigger }), open ? ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: () => setOpen(false), accessibilityRole: "button", accessibilityLabel: "Close menu", style: react_native_1.Platform.select({
|
|
195
|
+
web: {
|
|
196
|
+
position: 'fixed',
|
|
197
|
+
top: 0,
|
|
198
|
+
left: 0,
|
|
199
|
+
right: 0,
|
|
200
|
+
bottom: 0,
|
|
201
|
+
zIndex: 0,
|
|
202
|
+
},
|
|
203
|
+
default: {
|
|
204
|
+
position: 'absolute',
|
|
205
|
+
top: -9999,
|
|
206
|
+
left: -9999,
|
|
207
|
+
width: 99999,
|
|
208
|
+
height: 99999,
|
|
209
|
+
},
|
|
210
|
+
}) })) : null, open ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [placementStyle(placement), { zIndex: 1 }], children: (0, jsx_runtime_1.jsx)(MenuCtx.Provider, { value: { size, onClose: () => setOpen(false), closeOnSelect }, children: (0, jsx_runtime_1.jsx)(MenuContent, { children: children }) }) })) : null] }));
|
|
211
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Menu, MenuItem, MenuDivider, MenuLabel, MenuContent, type MenuProps, type MenuItemProps, type MenuLabelProps, type MenuContentProps, type MenuSize, type MenuPlacement, } from './Menu';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MenuContent = exports.MenuLabel = exports.MenuDivider = exports.MenuItem = exports.Menu = void 0;
|
|
4
|
+
var Menu_1 = require("./Menu");
|
|
5
|
+
Object.defineProperty(exports, "Menu", { enumerable: true, get: function () { return Menu_1.Menu; } });
|
|
6
|
+
Object.defineProperty(exports, "MenuItem", { enumerable: true, get: function () { return Menu_1.MenuItem; } });
|
|
7
|
+
Object.defineProperty(exports, "MenuDivider", { enumerable: true, get: function () { return Menu_1.MenuDivider; } });
|
|
8
|
+
Object.defineProperty(exports, "MenuLabel", { enumerable: true, get: function () { return Menu_1.MenuLabel; } });
|
|
9
|
+
Object.defineProperty(exports, "MenuContent", { enumerable: true, get: function () { return Menu_1.MenuContent; } });
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slider — drag a thumb along a track to pick a number.
|
|
3
|
+
*
|
|
4
|
+
* Maps 1:1 to the Figma <Slider> component:
|
|
5
|
+
* intent → neutral | brand | danger (filled portion + thumb ring)
|
|
6
|
+
* size → small | default | large (track thickness + thumb size)
|
|
7
|
+
* value → min..max (single value)
|
|
8
|
+
*
|
|
9
|
+
* Drag the thumb or tap the track to set the value. Built on PanResponder, so
|
|
10
|
+
* it works the same on web and native with zero dependencies. value is
|
|
11
|
+
* controlled with value/onValueChange, or uncontrolled with defaultValue.
|
|
12
|
+
*
|
|
13
|
+
* Tokens: slider/{size}/track-height and slider/{size}/thumb-size are keyed by
|
|
14
|
+
* the size prop and constant across density (like Progress's track-height);
|
|
15
|
+
* slider/border-radius is the pill radius. The track background is the dedicated
|
|
16
|
+
* control/slider/track/bg semantic (scheme.slider.track, cool-grey/200 light,
|
|
17
|
+
* cool-grey/700 dark); the filled portion reuses the intent system
|
|
18
|
+
* (colors[intent].bold.default.bg). No density-varying spacing.
|
|
19
|
+
*/
|
|
20
|
+
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
21
|
+
import type { IntentName } from '../../tokens';
|
|
22
|
+
export type SliderSize = 'small' | 'default' | 'large';
|
|
23
|
+
export type SliderProps = {
|
|
24
|
+
/** Current value (controlled). */
|
|
25
|
+
value?: number;
|
|
26
|
+
/** Initial value (uncontrolled). */
|
|
27
|
+
defaultValue?: number;
|
|
28
|
+
/** Called with the new value while dragging or on tap. */
|
|
29
|
+
onValueChange?: (value: number) => void;
|
|
30
|
+
/** Minimum value. Defaults to 0. */
|
|
31
|
+
min?: number;
|
|
32
|
+
/** Maximum value. Defaults to 100. */
|
|
33
|
+
max?: number;
|
|
34
|
+
/** Step increment. Defaults to 1. */
|
|
35
|
+
step?: number;
|
|
36
|
+
/** Semantic intent — drives the fill and thumb-ring colour. */
|
|
37
|
+
intent?: IntentName;
|
|
38
|
+
/** Size variant — track thickness and thumb size. */
|
|
39
|
+
size?: SliderSize;
|
|
40
|
+
/** Disables interaction and applies muted styling. */
|
|
41
|
+
disabled?: boolean;
|
|
42
|
+
/** Outer style — use for positioning (margin, width, alignSelf). */
|
|
43
|
+
style?: StyleProp<ViewStyle>;
|
|
44
|
+
/** Accessibility label — describes what the slider controls. */
|
|
45
|
+
accessibilityLabel?: string;
|
|
46
|
+
};
|
|
47
|
+
export declare function Slider({ value: controlledValue, defaultValue, onValueChange, min, max, step, intent, size, disabled, style, accessibilityLabel, }: SliderProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Slider = Slider;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
/**
|
|
6
|
+
* Slider — drag a thumb along a track to pick a number.
|
|
7
|
+
*
|
|
8
|
+
* Maps 1:1 to the Figma <Slider> component:
|
|
9
|
+
* intent → neutral | brand | danger (filled portion + thumb ring)
|
|
10
|
+
* size → small | default | large (track thickness + thumb size)
|
|
11
|
+
* value → min..max (single value)
|
|
12
|
+
*
|
|
13
|
+
* Drag the thumb or tap the track to set the value. Built on PanResponder, so
|
|
14
|
+
* it works the same on web and native with zero dependencies. value is
|
|
15
|
+
* controlled with value/onValueChange, or uncontrolled with defaultValue.
|
|
16
|
+
*
|
|
17
|
+
* Tokens: slider/{size}/track-height and slider/{size}/thumb-size are keyed by
|
|
18
|
+
* the size prop and constant across density (like Progress's track-height);
|
|
19
|
+
* slider/border-radius is the pill radius. The track background is the dedicated
|
|
20
|
+
* control/slider/track/bg semantic (scheme.slider.track, cool-grey/200 light,
|
|
21
|
+
* cool-grey/700 dark); the filled portion reuses the intent system
|
|
22
|
+
* (colors[intent].bold.default.bg). No density-varying spacing.
|
|
23
|
+
*/
|
|
24
|
+
const react_1 = require("react");
|
|
25
|
+
const react_native_1 = require("react-native");
|
|
26
|
+
const theme_1 = require("../../theme");
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Constants
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
/** Thumb ring width. A small visual constant, not a spacing token. */
|
|
31
|
+
const THUMB_RING = 2;
|
|
32
|
+
const SHADOW_WEB = { boxShadow: '0px 1px 3px rgba(0,0,0,0.2)' };
|
|
33
|
+
const SHADOW_NATIVE = {
|
|
34
|
+
shadowColor: '#000000',
|
|
35
|
+
shadowOffset: { width: 0, height: 1 },
|
|
36
|
+
shadowOpacity: 0.2,
|
|
37
|
+
shadowRadius: 2,
|
|
38
|
+
elevation: 2,
|
|
39
|
+
};
|
|
40
|
+
const SHADOW = react_native_1.Platform.OS === 'web' ? SHADOW_WEB : SHADOW_NATIVE;
|
|
41
|
+
const clampFrac = (f) => Math.max(0, Math.min(1, f));
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Component
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
function Slider({ value: controlledValue, defaultValue = 0, onValueChange, min = 0, max = 100, step = 1, intent = 'brand', size = 'default', disabled = false, style, accessibilityLabel, }) {
|
|
46
|
+
const { components, colors, scheme } = (0, theme_1.useTheme)();
|
|
47
|
+
const { trackHeight, thumbSize } = components.slider[size];
|
|
48
|
+
const borderRadius = components.slider.borderRadius;
|
|
49
|
+
const isControlled = controlledValue !== undefined;
|
|
50
|
+
const [internalValue, setInternalValue] = (0, react_1.useState)(defaultValue);
|
|
51
|
+
const value = isControlled ? controlledValue : internalValue;
|
|
52
|
+
const [trackWidth, setTrackWidth] = (0, react_1.useState)(0);
|
|
53
|
+
// Live state for the PanResponder closures (created once).
|
|
54
|
+
const live = (0, react_1.useRef)({
|
|
55
|
+
trackWidth,
|
|
56
|
+
disabled,
|
|
57
|
+
min,
|
|
58
|
+
max,
|
|
59
|
+
step,
|
|
60
|
+
isControlled,
|
|
61
|
+
onValueChange,
|
|
62
|
+
});
|
|
63
|
+
live.current = { trackWidth, disabled, min, max, step, isControlled, onValueChange };
|
|
64
|
+
const startFrac = (0, react_1.useRef)(0);
|
|
65
|
+
const fracToValue = (f) => {
|
|
66
|
+
const { min: lo, max: hi, step: st } = live.current;
|
|
67
|
+
const raw = lo + clampFrac(f) * (hi - lo);
|
|
68
|
+
const snapped = Math.round((raw - lo) / st) * st + lo;
|
|
69
|
+
return Math.max(lo, Math.min(hi, snapped));
|
|
70
|
+
};
|
|
71
|
+
const commit = (v) => {
|
|
72
|
+
if (!live.current.isControlled)
|
|
73
|
+
setInternalValue(v);
|
|
74
|
+
live.current.onValueChange?.(v);
|
|
75
|
+
};
|
|
76
|
+
const pan = (0, react_1.useRef)(react_native_1.PanResponder.create({
|
|
77
|
+
onStartShouldSetPanResponder: () => !live.current.disabled,
|
|
78
|
+
onMoveShouldSetPanResponder: () => !live.current.disabled,
|
|
79
|
+
onPanResponderGrant: (evt) => {
|
|
80
|
+
const w = live.current.trackWidth;
|
|
81
|
+
if (live.current.disabled || w === 0)
|
|
82
|
+
return;
|
|
83
|
+
const f = clampFrac(evt.nativeEvent.locationX / w);
|
|
84
|
+
startFrac.current = f;
|
|
85
|
+
commit(fracToValue(f));
|
|
86
|
+
},
|
|
87
|
+
onPanResponderMove: (_evt, g) => {
|
|
88
|
+
const w = live.current.trackWidth;
|
|
89
|
+
if (live.current.disabled || w === 0)
|
|
90
|
+
return;
|
|
91
|
+
const f = clampFrac(startFrac.current + g.dx / w);
|
|
92
|
+
commit(fracToValue(f));
|
|
93
|
+
},
|
|
94
|
+
})).current;
|
|
95
|
+
const range = max - min;
|
|
96
|
+
const fraction = range > 0 ? clampFrac((value - min) / range) : 0;
|
|
97
|
+
const onLayout = (e) => setTrackWidth(e.nativeEvent.layout.width);
|
|
98
|
+
const trackBg = disabled ? scheme.disabled.bg : scheme.slider.track;
|
|
99
|
+
const fillColor = disabled ? scheme.disabled.fg : colors[intent].bold.default.bg;
|
|
100
|
+
const thumbBorder = disabled ? scheme.disabled.border : colors[intent].bold.default.bg;
|
|
101
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { accessibilityRole: "adjustable", accessibilityLabel: accessibilityLabel, accessibilityValue: { min, max, now: Math.round(value) }, accessibilityState: { disabled }, accessibilityActions: [{ name: 'increment' }, { name: 'decrement' }], onAccessibilityAction: (e) => {
|
|
102
|
+
if (disabled)
|
|
103
|
+
return;
|
|
104
|
+
if (e.nativeEvent.actionName === 'increment')
|
|
105
|
+
commit(fracToValue((value - min + step) / (range || 1)));
|
|
106
|
+
if (e.nativeEvent.actionName === 'decrement')
|
|
107
|
+
commit(fracToValue((value - min - step) / (range || 1)));
|
|
108
|
+
}, style: [{ justifyContent: 'center', paddingHorizontal: thumbSize / 2 }, style], children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { ...pan.panHandlers, onLayout: onLayout, style: { height: thumbSize, justifyContent: 'center' }, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: {
|
|
109
|
+
height: trackHeight,
|
|
110
|
+
borderRadius,
|
|
111
|
+
backgroundColor: trackBg,
|
|
112
|
+
overflow: 'hidden',
|
|
113
|
+
}, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: {
|
|
114
|
+
position: 'absolute',
|
|
115
|
+
left: 0,
|
|
116
|
+
top: 0,
|
|
117
|
+
bottom: 0,
|
|
118
|
+
width: trackWidth * fraction,
|
|
119
|
+
backgroundColor: fillColor,
|
|
120
|
+
borderRadius,
|
|
121
|
+
} }) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { pointerEvents: "none", style: {
|
|
122
|
+
position: 'absolute',
|
|
123
|
+
left: fraction * trackWidth - thumbSize / 2,
|
|
124
|
+
width: thumbSize,
|
|
125
|
+
height: thumbSize,
|
|
126
|
+
borderRadius: thumbSize / 2,
|
|
127
|
+
backgroundColor: scheme.surface.overlay.bg,
|
|
128
|
+
borderWidth: THUMB_RING,
|
|
129
|
+
borderColor: thumbBorder,
|
|
130
|
+
...SHADOW,
|
|
131
|
+
} })] }) }));
|
|
132
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Slider, type SliderProps, type SliderSize } from './Slider';
|