@castui/cast-ui 4.1.1 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -19
- package/dist/components/Alert/Alert.d.ts +40 -0
- package/dist/components/Alert/Alert.js +71 -0
- package/dist/components/Alert/index.d.ts +1 -0
- package/dist/components/Alert/index.js +5 -0
- package/dist/components/Avatar/Avatar.d.ts +31 -0
- package/dist/components/Avatar/Avatar.js +55 -0
- package/dist/components/Avatar/index.d.ts +1 -0
- package/dist/components/Avatar/index.js +5 -0
- package/dist/components/Badge/Badge.d.ts +41 -0
- package/dist/components/Badge/Badge.js +71 -0
- package/dist/components/Badge/index.d.ts +1 -0
- package/dist/components/Badge/index.js +5 -0
- package/dist/components/Button/Button.d.ts +0 -1
- package/dist/components/Button/Button.js +21 -19
- package/dist/components/Button/index.d.ts +0 -1
- package/dist/components/Button/index.js +5 -2
- package/dist/components/Card/Card.d.ts +41 -0
- package/dist/components/Card/Card.js +91 -0
- package/dist/components/Card/index.d.ts +1 -0
- package/dist/components/Card/index.js +5 -0
- package/dist/components/Checkbox/Checkbox.d.ts +32 -0
- package/dist/components/Checkbox/Checkbox.js +107 -0
- package/dist/components/Checkbox/index.d.ts +1 -0
- package/dist/components/Checkbox/index.js +5 -0
- package/dist/components/Chip/Chip.d.ts +46 -0
- package/dist/components/Chip/Chip.js +82 -0
- package/dist/components/Chip/index.d.ts +1 -0
- package/dist/components/Chip/index.js +5 -0
- package/dist/components/Dialog/Dialog.d.ts +0 -1
- package/dist/components/Dialog/Dialog.js +33 -26
- package/dist/components/Dialog/index.d.ts +0 -1
- package/dist/components/Dialog/index.js +6 -2
- package/dist/components/Divider/Divider.d.ts +22 -0
- package/dist/components/Divider/Divider.js +19 -0
- package/dist/components/Divider/index.d.ts +1 -0
- package/dist/components/Divider/index.js +5 -0
- package/dist/components/Icon/Icon.d.ts +26 -9
- package/dist/components/Icon/Icon.js +16 -6
- package/dist/components/Icon/index.d.ts +0 -1
- package/dist/components/Icon/index.js +5 -2
- package/dist/components/Input/Input.d.ts +62 -0
- package/dist/components/Input/Input.js +141 -0
- package/dist/components/Input/index.d.ts +1 -0
- package/dist/components/Input/index.js +5 -0
- package/dist/components/List/List.d.ts +58 -0
- package/dist/components/List/List.js +116 -0
- package/dist/components/List/index.d.ts +1 -0
- package/dist/components/List/index.js +8 -0
- package/dist/components/Popover/Popover.d.ts +34 -0
- package/dist/components/Popover/Popover.js +62 -0
- package/dist/components/Popover/index.d.ts +1 -0
- package/dist/components/Popover/index.js +5 -0
- package/dist/components/Radio/Radio.d.ts +52 -0
- package/dist/components/Radio/Radio.js +127 -0
- package/dist/components/Radio/index.d.ts +1 -0
- package/dist/components/Radio/index.js +6 -0
- package/dist/components/Select/Select.d.ts +0 -1
- package/dist/components/Select/Select.js +114 -96
- package/dist/components/Select/index.d.ts +0 -1
- package/dist/components/Select/index.js +10 -2
- package/dist/components/Skeleton/Skeleton.d.ts +33 -0
- package/dist/components/Skeleton/Skeleton.js +66 -0
- package/dist/components/Skeleton/index.d.ts +1 -0
- package/dist/components/Skeleton/index.js +5 -0
- package/dist/components/Toast/Toast.d.ts +35 -0
- package/dist/components/Toast/Toast.js +79 -0
- package/dist/components/Toast/index.d.ts +1 -0
- package/dist/components/Toast/index.js +5 -0
- package/dist/components/Toggle/Toggle.d.ts +31 -0
- package/dist/components/Toggle/Toggle.js +91 -0
- package/dist/components/Toggle/index.d.ts +1 -0
- package/dist/components/Toggle/index.js +5 -0
- package/dist/components/Tooltip/Tooltip.d.ts +34 -0
- package/dist/components/Tooltip/Tooltip.js +67 -0
- package/dist/components/Tooltip/index.d.ts +1 -0
- package/dist/components/Tooltip/index.js +5 -0
- package/dist/index.d.ts +17 -3
- package/dist/index.js +81 -7
- package/dist/theme/ThemeContext.d.ts +24 -8
- package/dist/theme/ThemeContext.js +41 -22
- package/dist/theme/index.d.ts +1 -2
- package/dist/theme/index.js +8 -3
- package/dist/theme/themes.d.ts +0 -1
- package/dist/theme/themes.js +214 -2
- package/dist/theme/types.d.ts +183 -1
- package/dist/theme/types.js +2 -2
- package/dist/tokens/colors.d.ts +294 -26
- package/dist/tokens/colors.js +324 -99
- package/dist/tokens/index.d.ts +1 -2
- package/dist/tokens/index.js +29 -3
- package/dist/tokens/typography.d.ts +0 -1
- package/dist/tokens/typography.js +13 -11
- package/package.json +13 -2
- package/dist/components/Button/Button.d.ts.map +0 -1
- package/dist/components/Button/Button.js.map +0 -1
- package/dist/components/Button/index.d.ts.map +0 -1
- package/dist/components/Button/index.js.map +0 -1
- package/dist/components/Dialog/Dialog.d.ts.map +0 -1
- package/dist/components/Dialog/Dialog.js.map +0 -1
- package/dist/components/Dialog/index.d.ts.map +0 -1
- package/dist/components/Dialog/index.js.map +0 -1
- package/dist/components/Icon/Icon.d.ts.map +0 -1
- package/dist/components/Icon/Icon.js.map +0 -1
- package/dist/components/Icon/index.d.ts.map +0 -1
- package/dist/components/Icon/index.js.map +0 -1
- package/dist/components/Select/Select.d.ts.map +0 -1
- package/dist/components/Select/Select.js.map +0 -1
- package/dist/components/Select/index.d.ts.map +0 -1
- package/dist/components/Select/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/theme/ThemeContext.d.ts.map +0 -1
- package/dist/theme/ThemeContext.js.map +0 -1
- package/dist/theme/index.d.ts.map +0 -1
- package/dist/theme/index.js.map +0 -1
- package/dist/theme/themes.d.ts.map +0 -1
- package/dist/theme/themes.js.map +0 -1
- package/dist/theme/types.d.ts.map +0 -1
- package/dist/theme/types.js.map +0 -1
- package/dist/tokens/colors.d.ts.map +0 -1
- package/dist/tokens/colors.js.map +0 -1
- package/dist/tokens/index.d.ts.map +0 -1
- package/dist/tokens/index.js.map +0 -1
- package/dist/tokens/typography.d.ts.map +0 -1
- package/dist/tokens/typography.js.map +0 -1
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Icon — renders a Material Symbols Outlined icon by name.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Why Material Symbols Outlined?
|
|
5
|
+
* - It's the icon set the Cast UI Figma uses (star, check, close,
|
|
6
|
+
* keyboard_arrow_down, …), so names map 1:1 to the design with zero drift.
|
|
7
|
+
* - Apache-2.0 licensed and free for commercial use.
|
|
8
|
+
* - It's a single *variable* font, so one asset covers every icon plus the
|
|
9
|
+
* fill / weight / grade / optical-size axes — keeping Cast UI's zero
|
|
10
|
+
* runtime dependencies (no per-icon SVG packages, no vector-icons dep).
|
|
7
11
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* -
|
|
11
|
-
*
|
|
12
|
+
* Rendering uses font ligatures: the `name` text (e.g. "chevron_right") is
|
|
13
|
+
* shaped into the glyph by the font. Requires the font to be loaded:
|
|
14
|
+
* - Web: Google Fonts CSS import (see .storybook/preview-head.html), or
|
|
15
|
+
* self-host the same `Material Symbols Outlined` family.
|
|
16
|
+
* - Expo: load `MaterialSymbolsOutlined` via expo-font / useFonts.
|
|
17
|
+
* - Bare RN: link the .ttf as a font asset (react-native.config.js / Xcode).
|
|
18
|
+
*
|
|
19
|
+
* The fill / weight / grade / opticalSize axes are applied via CSS
|
|
20
|
+
* `fontVariationSettings` and therefore take effect on web (where the variable
|
|
21
|
+
* font is loaded). On native they require a matching static/variable font cut.
|
|
12
22
|
*/
|
|
13
23
|
import { type TextStyle, type StyleProp } from 'react-native';
|
|
14
24
|
export type IconProps = {
|
|
@@ -18,8 +28,15 @@ export type IconProps = {
|
|
|
18
28
|
size?: number;
|
|
19
29
|
/** Icon colour. Defaults to "#374151" (neutral fg). */
|
|
20
30
|
color?: string;
|
|
31
|
+
/** Filled vs outlined glyph (FILL axis, 0–1). Defaults to false (outlined). */
|
|
32
|
+
fill?: boolean;
|
|
33
|
+
/** Stroke weight (wght axis, 100–700). Defaults to 400. */
|
|
34
|
+
weight?: 100 | 200 | 300 | 400 | 500 | 600 | 700;
|
|
35
|
+
/** Emphasis grade (GRAD axis, -25–200). Defaults to 0. */
|
|
36
|
+
grade?: number;
|
|
37
|
+
/** Optical size (opsz axis, 20–48). Defaults to the icon `size`. */
|
|
38
|
+
opticalSize?: number;
|
|
21
39
|
/** Additional style overrides. */
|
|
22
40
|
style?: StyleProp<TextStyle>;
|
|
23
41
|
};
|
|
24
|
-
export declare function Icon({ name, size, color, style }: IconProps): import("react/jsx-runtime").JSX.Element;
|
|
25
|
-
//# sourceMappingURL=Icon.d.ts.map
|
|
42
|
+
export declare function Icon({ name, size, color, fill, weight, grade, opticalSize, style, }: IconProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Icon = Icon;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_native_1 = require("react-native");
|
|
6
|
+
const FONT_FAMILY = react_native_1.Platform.select({
|
|
4
7
|
web: '"Material Symbols Outlined", sans-serif',
|
|
5
8
|
default: 'MaterialSymbolsOutlined',
|
|
6
9
|
});
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
function Icon({ name, size = 20, color = '#374151', fill = false, weight = 400, grade = 0, opticalSize, style, }) {
|
|
11
|
+
// Material Symbols variable-font axes — applied on web via fontVariationSettings.
|
|
12
|
+
const opsz = Math.min(48, Math.max(20, opticalSize ?? size));
|
|
13
|
+
const variationStyle = react_native_1.Platform.OS === 'web'
|
|
14
|
+
? {
|
|
15
|
+
fontVariationSettings: `'FILL' ${fill ? 1 : 0}, 'wght' ${weight}, 'GRAD' ${grade}, 'opsz' ${opsz}`,
|
|
16
|
+
}
|
|
17
|
+
: null;
|
|
18
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.Text, { selectable: false, accessibilityElementsHidden: true, importantForAccessibility: "no", style: [
|
|
9
19
|
{
|
|
10
20
|
fontFamily: FONT_FAMILY,
|
|
11
21
|
fontSize: size,
|
|
@@ -22,7 +32,7 @@ export function Icon({ name, size = 20, color = '#374151', style }) {
|
|
|
22
32
|
textTransform: 'none',
|
|
23
33
|
textDecorationLine: 'none',
|
|
24
34
|
},
|
|
35
|
+
variationStyle,
|
|
25
36
|
style,
|
|
26
37
|
], children: name }));
|
|
27
38
|
}
|
|
28
|
-
//# sourceMappingURL=Icon.js.map
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Icon = void 0;
|
|
4
|
+
var Icon_1 = require("./Icon");
|
|
5
|
+
Object.defineProperty(exports, "Icon", { enumerable: true, get: function () { return Icon_1.Icon; } });
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input — single-line text field with label and helper/error text.
|
|
3
|
+
*
|
|
4
|
+
* Maps to the Figma <Input> component (node 307:3476):
|
|
5
|
+
* size → small | default | large
|
|
6
|
+
* state → default | hover | focus | error | disabled
|
|
7
|
+
*
|
|
8
|
+
* `error` and `disabled` are props; `hover` and `focus` are interaction-driven.
|
|
9
|
+
* Field spacing comes from the density theme's `input` tokens (shared with the
|
|
10
|
+
* Select trigger). Colours come from the semantic intent system (neutral):
|
|
11
|
+
* default → neutral/default/default hover → neutral/default/hover
|
|
12
|
+
* focus → focus-ring border (2px) error → danger border + red helper
|
|
13
|
+
* disabled→ shared disabled colours
|
|
14
|
+
* Label uses the label scale, the value/placeholder the body scale (both
|
|
15
|
+
* matched to size); helper text uses the caption scale.
|
|
16
|
+
*/
|
|
17
|
+
import React from 'react';
|
|
18
|
+
import { type ViewStyle, type StyleProp, type KeyboardTypeOptions, type ReturnKeyTypeOptions } from 'react-native';
|
|
19
|
+
export type InputSize = 'small' | 'default' | 'large';
|
|
20
|
+
export type InputProps = {
|
|
21
|
+
/** Form label above the field. */
|
|
22
|
+
label?: string;
|
|
23
|
+
/** Helper text below the field — turns red when `error` is set. */
|
|
24
|
+
helperText?: string;
|
|
25
|
+
/** Placeholder shown when the field is empty. */
|
|
26
|
+
placeholder?: string;
|
|
27
|
+
/** Controlled value. */
|
|
28
|
+
value?: string;
|
|
29
|
+
/** Uncontrolled initial value. */
|
|
30
|
+
defaultValue?: string;
|
|
31
|
+
/** Change handler — receives the new text. */
|
|
32
|
+
onChangeText?: (text: string) => void;
|
|
33
|
+
/** Size variant — controls padding, gap, and typography scale. */
|
|
34
|
+
size?: InputSize;
|
|
35
|
+
/** Error state — danger border and red helper text. */
|
|
36
|
+
error?: boolean;
|
|
37
|
+
/** Disables interaction and applies muted styling. */
|
|
38
|
+
disabled?: boolean;
|
|
39
|
+
/** Leading icon — Material Symbols name string or a ReactNode. */
|
|
40
|
+
leadingIcon?: string | React.ReactNode;
|
|
41
|
+
/** Trailing icon — Material Symbols name string or a ReactNode. */
|
|
42
|
+
trailingIcon?: string | React.ReactNode;
|
|
43
|
+
/** Masks the text (passwords). */
|
|
44
|
+
secureTextEntry?: boolean;
|
|
45
|
+
/** Keyboard type for native platforms. */
|
|
46
|
+
keyboardType?: KeyboardTypeOptions;
|
|
47
|
+
/** Auto-capitalisation behaviour. */
|
|
48
|
+
autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters';
|
|
49
|
+
/** Return key label for native platforms. */
|
|
50
|
+
returnKeyType?: ReturnKeyTypeOptions;
|
|
51
|
+
/** Submit (return key) handler. */
|
|
52
|
+
onSubmitEditing?: () => void;
|
|
53
|
+
/** Focus handler. */
|
|
54
|
+
onFocus?: () => void;
|
|
55
|
+
/** Blur handler. */
|
|
56
|
+
onBlur?: () => void;
|
|
57
|
+
/** Style override for the outer container. */
|
|
58
|
+
style?: StyleProp<ViewStyle>;
|
|
59
|
+
/** Accessibility label — falls back to the label prop. */
|
|
60
|
+
accessibilityLabel?: string;
|
|
61
|
+
};
|
|
62
|
+
export declare function Input({ label: formLabel, helperText, placeholder, value, defaultValue, onChangeText, size, error, disabled, leadingIcon, trailingIcon, secureTextEntry, keyboardType, autoCapitalize, returnKeyType, onSubmitEditing, onFocus, onBlur, style, accessibilityLabel, }: InputProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Input = Input;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
/**
|
|
6
|
+
* Input — single-line text field with label and helper/error text.
|
|
7
|
+
*
|
|
8
|
+
* Maps to the Figma <Input> component (node 307:3476):
|
|
9
|
+
* size → small | default | large
|
|
10
|
+
* state → default | hover | focus | error | disabled
|
|
11
|
+
*
|
|
12
|
+
* `error` and `disabled` are props; `hover` and `focus` are interaction-driven.
|
|
13
|
+
* Field spacing comes from the density theme's `input` tokens (shared with the
|
|
14
|
+
* Select trigger). Colours come from the semantic intent system (neutral):
|
|
15
|
+
* default → neutral/default/default hover → neutral/default/hover
|
|
16
|
+
* focus → focus-ring border (2px) error → danger border + red helper
|
|
17
|
+
* disabled→ shared disabled colours
|
|
18
|
+
* Label uses the label scale, the value/placeholder the body scale (both
|
|
19
|
+
* matched to size); helper text uses the caption scale.
|
|
20
|
+
*/
|
|
21
|
+
const react_1 = require("react");
|
|
22
|
+
const react_native_1 = require("react-native");
|
|
23
|
+
const theme_1 = require("../../theme");
|
|
24
|
+
const Icon_1 = require("../Icon");
|
|
25
|
+
const tokens_1 = require("../../tokens");
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Constants
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
const ICON_SIZE = 16;
|
|
30
|
+
/** Maps input size → label typography scale (form label text) */
|
|
31
|
+
const LABEL_SCALE = {
|
|
32
|
+
small: 'sm',
|
|
33
|
+
default: 'md',
|
|
34
|
+
large: 'lg',
|
|
35
|
+
};
|
|
36
|
+
/** Maps input size → body typography scale (value + placeholder text) */
|
|
37
|
+
const BODY_SCALE = {
|
|
38
|
+
small: 'sm',
|
|
39
|
+
default: 'md',
|
|
40
|
+
large: 'lg',
|
|
41
|
+
};
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Component
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
function Input({ label: formLabel, helperText, placeholder, value, defaultValue, onChangeText, size = 'default', error = false, disabled = false, leadingIcon, trailingIcon, secureTextEntry, keyboardType, autoCapitalize, returnKeyType, onSubmitEditing, onFocus, onBlur, style, accessibilityLabel, }) {
|
|
46
|
+
const { components, scheme } = (0, theme_1.useTheme)();
|
|
47
|
+
const sizeTokens = components.input[size];
|
|
48
|
+
const labelTypo = tokens_1.label[LABEL_SCALE[size]];
|
|
49
|
+
const bodyTypo = tokens_1.body[BODY_SCALE[size]];
|
|
50
|
+
const [isHovered, setIsHovered] = (0, react_1.useState)(false);
|
|
51
|
+
const [isFocused, setIsFocused] = (0, react_1.useState)(false);
|
|
52
|
+
const disabledColors = scheme.disabled;
|
|
53
|
+
const errorTokens = scheme.error;
|
|
54
|
+
const textTokens = scheme.text;
|
|
55
|
+
const neutral = scheme.intents.neutral.default;
|
|
56
|
+
// Resolve field colours — state priority: disabled > error > focus > hover > default
|
|
57
|
+
let bg;
|
|
58
|
+
let borderColor;
|
|
59
|
+
let borderWidth;
|
|
60
|
+
if (disabled) {
|
|
61
|
+
bg = disabledColors.bg;
|
|
62
|
+
borderColor = disabledColors.border;
|
|
63
|
+
borderWidth = tokens_1.controlTokens.borderWidth;
|
|
64
|
+
}
|
|
65
|
+
else if (isFocused) {
|
|
66
|
+
bg = neutral.default.bg;
|
|
67
|
+
borderColor = scheme.focusRing.color;
|
|
68
|
+
borderWidth = tokens_1.controlTokens.focusRingWidth;
|
|
69
|
+
}
|
|
70
|
+
else if (error) {
|
|
71
|
+
bg = neutral.default.bg;
|
|
72
|
+
borderColor = errorTokens.border;
|
|
73
|
+
borderWidth = tokens_1.controlTokens.borderWidth;
|
|
74
|
+
}
|
|
75
|
+
else if (isHovered) {
|
|
76
|
+
bg = neutral.hover.bg;
|
|
77
|
+
borderColor = neutral.hover.border;
|
|
78
|
+
borderWidth = tokens_1.controlTokens.borderWidth;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
bg = neutral.default.bg;
|
|
82
|
+
borderColor = neutral.default.border;
|
|
83
|
+
borderWidth = tokens_1.controlTokens.borderWidth;
|
|
84
|
+
}
|
|
85
|
+
const textColor = disabled ? disabledColors.fg : neutral.default.fg;
|
|
86
|
+
const labelColor = disabled ? disabledColors.fg : neutral.default.fg;
|
|
87
|
+
const helperColor = disabled
|
|
88
|
+
? disabledColors.fg
|
|
89
|
+
: error
|
|
90
|
+
? errorTokens.fg
|
|
91
|
+
: textTokens.description;
|
|
92
|
+
const iconColor = disabled ? disabledColors.fg : neutral.default.fg;
|
|
93
|
+
const resolvedLeading = typeof leadingIcon === 'string' ? ((0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: leadingIcon, size: ICON_SIZE, color: iconColor })) : (leadingIcon);
|
|
94
|
+
const resolvedTrailing = typeof trailingIcon === 'string' ? ((0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: trailingIcon, size: ICON_SIZE, color: iconColor })) : (trailingIcon);
|
|
95
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
|
|
96
|
+
{ alignSelf: 'stretch', gap: components.input.fieldGap },
|
|
97
|
+
style,
|
|
98
|
+
], children: [formLabel ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: {
|
|
99
|
+
fontFamily: tokens_1.fontFamily.sans,
|
|
100
|
+
fontWeight: tokens_1.fontWeight.medium,
|
|
101
|
+
fontSize: labelTypo.fontSize,
|
|
102
|
+
lineHeight: labelTypo.lineHeight,
|
|
103
|
+
letterSpacing: labelTypo.letterSpacing,
|
|
104
|
+
color: labelColor,
|
|
105
|
+
}, selectable: false, children: formLabel })) : null, (0, jsx_runtime_1.jsxs)(react_native_1.View, { onPointerEnter: disabled ? undefined : () => setIsHovered(true), onPointerLeave: disabled ? undefined : () => setIsHovered(false), style: {
|
|
106
|
+
flexDirection: 'row',
|
|
107
|
+
alignItems: 'center',
|
|
108
|
+
gap: sizeTokens.gap,
|
|
109
|
+
paddingHorizontal: sizeTokens.paddingX,
|
|
110
|
+
paddingVertical: sizeTokens.paddingY,
|
|
111
|
+
borderRadius: sizeTokens.borderRadius,
|
|
112
|
+
borderWidth,
|
|
113
|
+
borderColor,
|
|
114
|
+
backgroundColor: bg,
|
|
115
|
+
}, children: [resolvedLeading ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { accessibilityElementsHidden: true, importantForAccessibility: "no", style: { width: ICON_SIZE, height: ICON_SIZE }, children: resolvedLeading })) : null, (0, jsx_runtime_1.jsx)(react_native_1.TextInput, { value: value, defaultValue: defaultValue, onChangeText: onChangeText, placeholder: placeholder, placeholderTextColor: textTokens.placeholder, editable: !disabled, secureTextEntry: secureTextEntry, keyboardType: keyboardType, autoCapitalize: autoCapitalize, returnKeyType: returnKeyType, onSubmitEditing: onSubmitEditing, onFocus: () => {
|
|
116
|
+
setIsFocused(true);
|
|
117
|
+
onFocus?.();
|
|
118
|
+
}, onBlur: () => {
|
|
119
|
+
setIsFocused(false);
|
|
120
|
+
onBlur?.();
|
|
121
|
+
}, accessibilityLabel: accessibilityLabel || formLabel, style: {
|
|
122
|
+
flex: 1,
|
|
123
|
+
fontFamily: tokens_1.fontFamily.sans,
|
|
124
|
+
fontWeight: tokens_1.fontWeight.regular,
|
|
125
|
+
fontSize: bodyTypo.fontSize,
|
|
126
|
+
lineHeight: bodyTypo.lineHeight,
|
|
127
|
+
letterSpacing: bodyTypo.letterSpacing,
|
|
128
|
+
color: textColor,
|
|
129
|
+
padding: 0,
|
|
130
|
+
...(react_native_1.Platform.OS === 'web'
|
|
131
|
+
? { outlineWidth: 0 }
|
|
132
|
+
: {}),
|
|
133
|
+
} }), resolvedTrailing ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { accessibilityElementsHidden: true, importantForAccessibility: "no", style: { width: ICON_SIZE, height: ICON_SIZE }, children: resolvedTrailing })) : null] }), helperText ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: {
|
|
134
|
+
fontFamily: tokens_1.fontFamily.sans,
|
|
135
|
+
fontWeight: tokens_1.fontWeight.regular,
|
|
136
|
+
fontSize: tokens_1.caption.fontSize,
|
|
137
|
+
lineHeight: tokens_1.caption.lineHeight,
|
|
138
|
+
letterSpacing: tokens_1.caption.letterSpacing,
|
|
139
|
+
color: helperColor,
|
|
140
|
+
}, selectable: false, children: helperText })) : null] }));
|
|
141
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Input, type InputProps, type InputSize } from './Input';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List — vertical collection of items with optional section subheaders.
|
|
3
|
+
*
|
|
4
|
+
* Maps to the Figma <List> component (node 452:3401):
|
|
5
|
+
* List — the container surface (white bg, 1px section gap)
|
|
6
|
+
* ListSubheader — muted caption label for a section ("Inbox", "Labels")
|
|
7
|
+
* ListItem — icon + label + optional description, with
|
|
8
|
+
* default / selected / disabled states
|
|
9
|
+
* ListDivider — horizontal rule between sections
|
|
10
|
+
*
|
|
11
|
+
* Density (compact/default/comfortable) drives item padding, gap, and
|
|
12
|
+
* icon size via the theme's `list` tokens. Colours come from the theme's
|
|
13
|
+
* colour scheme (`scheme.list`, constant across densities).
|
|
14
|
+
*/
|
|
15
|
+
import React from 'react';
|
|
16
|
+
import { type ViewStyle, type StyleProp, type GestureResponderEvent } from 'react-native';
|
|
17
|
+
export type ListProps = {
|
|
18
|
+
/** ListItem / ListSubheader / ListDivider children. */
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
/** Style override for the outer container. */
|
|
21
|
+
style?: StyleProp<ViewStyle>;
|
|
22
|
+
/** Accessibility label for the list region. */
|
|
23
|
+
accessibilityLabel?: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function List({ children, style, accessibilityLabel }: ListProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export type ListSubheaderProps = {
|
|
27
|
+
/** Subheader label text. */
|
|
28
|
+
children: string;
|
|
29
|
+
/** Style override. */
|
|
30
|
+
style?: StyleProp<ViewStyle>;
|
|
31
|
+
};
|
|
32
|
+
export declare function ListSubheader({ children, style }: ListSubheaderProps): import("react/jsx-runtime").JSX.Element;
|
|
33
|
+
export type ListDividerProps = {
|
|
34
|
+
/** Style override for the divider line. */
|
|
35
|
+
style?: StyleProp<ViewStyle>;
|
|
36
|
+
};
|
|
37
|
+
export declare function ListDivider({ style }: ListDividerProps): import("react/jsx-runtime").JSX.Element;
|
|
38
|
+
export type ListItemProps = {
|
|
39
|
+
/** Primary label text. */
|
|
40
|
+
children: string;
|
|
41
|
+
/** Supporting description text below the label. */
|
|
42
|
+
description?: string;
|
|
43
|
+
/** Leading icon — Material Symbols name string or a ReactNode. */
|
|
44
|
+
icon?: string | React.ReactNode;
|
|
45
|
+
/** Trailing icon — Material Symbols name string or a ReactNode. */
|
|
46
|
+
trailingIcon?: string | React.ReactNode;
|
|
47
|
+
/** Marks the item as selected (highlighted). */
|
|
48
|
+
selected?: boolean;
|
|
49
|
+
/** Disables interaction and applies muted styling. */
|
|
50
|
+
disabled?: boolean;
|
|
51
|
+
/** Press handler. */
|
|
52
|
+
onPress?: (e: GestureResponderEvent) => void;
|
|
53
|
+
/** Style override for the item row. */
|
|
54
|
+
style?: StyleProp<ViewStyle>;
|
|
55
|
+
/** Accessibility label — falls back to the label text. */
|
|
56
|
+
accessibilityLabel?: string;
|
|
57
|
+
};
|
|
58
|
+
export declare function ListItem({ children, description, icon, trailingIcon, selected, disabled, onPress, style, accessibilityLabel, }: ListItemProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.List = List;
|
|
4
|
+
exports.ListSubheader = ListSubheader;
|
|
5
|
+
exports.ListDivider = ListDivider;
|
|
6
|
+
exports.ListItem = ListItem;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
/**
|
|
9
|
+
* List — vertical collection of items with optional section subheaders.
|
|
10
|
+
*
|
|
11
|
+
* Maps to the Figma <List> component (node 452:3401):
|
|
12
|
+
* List — the container surface (white bg, 1px section gap)
|
|
13
|
+
* ListSubheader — muted caption label for a section ("Inbox", "Labels")
|
|
14
|
+
* ListItem — icon + label + optional description, with
|
|
15
|
+
* default / selected / disabled states
|
|
16
|
+
* ListDivider — horizontal rule between sections
|
|
17
|
+
*
|
|
18
|
+
* Density (compact/default/comfortable) drives item padding, gap, and
|
|
19
|
+
* icon size via the theme's `list` tokens. Colours come from the theme's
|
|
20
|
+
* colour scheme (`scheme.list`, constant across densities).
|
|
21
|
+
*/
|
|
22
|
+
const react_1 = require("react");
|
|
23
|
+
const react_native_1 = require("react-native");
|
|
24
|
+
const theme_1 = require("../../theme");
|
|
25
|
+
const Icon_1 = require("../Icon");
|
|
26
|
+
const tokens_1 = require("../../tokens");
|
|
27
|
+
function List({ children, style, accessibilityLabel }) {
|
|
28
|
+
const { components, scheme } = (0, theme_1.useTheme)();
|
|
29
|
+
const listColors = scheme.list;
|
|
30
|
+
const tokens = components.list;
|
|
31
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { accessibilityRole: "list", accessibilityLabel: accessibilityLabel, style: [
|
|
32
|
+
{
|
|
33
|
+
backgroundColor: listColors.containerBg,
|
|
34
|
+
gap: tokens.sectionGap,
|
|
35
|
+
alignSelf: 'stretch',
|
|
36
|
+
},
|
|
37
|
+
style,
|
|
38
|
+
], children: children }));
|
|
39
|
+
}
|
|
40
|
+
function ListSubheader({ children, style }) {
|
|
41
|
+
const { components, scheme } = (0, theme_1.useTheme)();
|
|
42
|
+
const listColors = scheme.list;
|
|
43
|
+
const tokens = components.list.subheader;
|
|
44
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
45
|
+
{
|
|
46
|
+
paddingHorizontal: tokens.paddingX,
|
|
47
|
+
paddingVertical: tokens.paddingY,
|
|
48
|
+
},
|
|
49
|
+
style,
|
|
50
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: {
|
|
51
|
+
fontFamily: tokens_1.fontFamily.sans,
|
|
52
|
+
fontWeight: tokens_1.fontWeight.regular,
|
|
53
|
+
fontSize: tokens_1.caption.fontSize,
|
|
54
|
+
lineHeight: tokens_1.caption.lineHeight,
|
|
55
|
+
letterSpacing: tokens_1.caption.letterSpacing,
|
|
56
|
+
color: listColors.subheaderFg,
|
|
57
|
+
}, selectable: false, children: children }) }));
|
|
58
|
+
}
|
|
59
|
+
function ListDivider({ style }) {
|
|
60
|
+
const { scheme } = (0, theme_1.useTheme)();
|
|
61
|
+
const listColors = scheme.list;
|
|
62
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { accessibilityRole: "none", style: [
|
|
63
|
+
{
|
|
64
|
+
height: 1,
|
|
65
|
+
backgroundColor: listColors.separator,
|
|
66
|
+
},
|
|
67
|
+
style,
|
|
68
|
+
] }));
|
|
69
|
+
}
|
|
70
|
+
function ListItem({ children, description, icon, trailingIcon, selected = false, disabled = false, onPress, style, accessibilityLabel, }) {
|
|
71
|
+
const { components, scheme } = (0, theme_1.useTheme)();
|
|
72
|
+
const listColors = scheme.list;
|
|
73
|
+
const tokens = components.list.item;
|
|
74
|
+
const [isHovered, setIsHovered] = (0, react_1.useState)(false);
|
|
75
|
+
// State priority: disabled > selected+hover > selected > hover > default
|
|
76
|
+
const colors = disabled
|
|
77
|
+
? listColors.item.disabled
|
|
78
|
+
: selected && isHovered
|
|
79
|
+
? listColors.item.selectedHover
|
|
80
|
+
: selected
|
|
81
|
+
? listColors.item.selected
|
|
82
|
+
: isHovered
|
|
83
|
+
? listColors.item.hover
|
|
84
|
+
: listColors.item.default;
|
|
85
|
+
const resolvedIcon = typeof icon === 'string' ? ((0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: icon, size: tokens.iconSize, color: colors.fg })) : (icon);
|
|
86
|
+
const resolvedTrailing = typeof trailingIcon === 'string' ? ((0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: trailingIcon, size: tokens.iconSize, color: colors.fg })) : (trailingIcon);
|
|
87
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.Pressable, { onPress: (e) => {
|
|
88
|
+
if (!disabled)
|
|
89
|
+
onPress?.(e);
|
|
90
|
+
}, onHoverIn: () => setIsHovered(true), onHoverOut: () => setIsHovered(false), disabled: disabled || !onPress, accessibilityRole: "menuitem", accessibilityLabel: accessibilityLabel || children, accessibilityState: { selected, disabled }, style: [
|
|
91
|
+
{
|
|
92
|
+
flexDirection: 'row',
|
|
93
|
+
alignItems: 'flex-start',
|
|
94
|
+
gap: tokens.gap,
|
|
95
|
+
paddingHorizontal: tokens.paddingX,
|
|
96
|
+
paddingVertical: tokens.paddingY,
|
|
97
|
+
borderRadius: tokens.borderRadius,
|
|
98
|
+
backgroundColor: colors.bg,
|
|
99
|
+
},
|
|
100
|
+
style,
|
|
101
|
+
], children: [resolvedIcon ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { accessibilityElementsHidden: true, importantForAccessibility: "no", style: { width: tokens.iconSize, height: tokens.iconSize }, children: resolvedIcon })) : null, (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: { flex: 1, justifyContent: 'center' }, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: {
|
|
102
|
+
fontFamily: tokens_1.fontFamily.sans,
|
|
103
|
+
fontWeight: tokens_1.fontWeight.medium,
|
|
104
|
+
fontSize: tokens_1.label.md.fontSize,
|
|
105
|
+
lineHeight: tokens_1.label.md.lineHeight,
|
|
106
|
+
letterSpacing: tokens_1.label.md.letterSpacing,
|
|
107
|
+
color: colors.fg,
|
|
108
|
+
}, selectable: false, children: children }), description ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: {
|
|
109
|
+
fontFamily: tokens_1.fontFamily.sans,
|
|
110
|
+
fontWeight: tokens_1.fontWeight.regular,
|
|
111
|
+
fontSize: tokens_1.body.md.fontSize,
|
|
112
|
+
lineHeight: tokens_1.body.md.lineHeight,
|
|
113
|
+
letterSpacing: tokens_1.body.md.letterSpacing,
|
|
114
|
+
color: disabled ? colors.fg : listColors.descriptionFg,
|
|
115
|
+
}, selectable: false, children: description })) : null] }), resolvedTrailing ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { accessibilityElementsHidden: true, importantForAccessibility: "no", style: { width: tokens.iconSize, height: tokens.iconSize }, children: resolvedTrailing })) : null] }));
|
|
116
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { List, ListItem, ListSubheader, ListDivider, type ListProps, type ListItemProps, type ListSubheaderProps, type ListDividerProps, } from './List';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ListDivider = exports.ListSubheader = exports.ListItem = exports.List = void 0;
|
|
4
|
+
var List_1 = require("./List");
|
|
5
|
+
Object.defineProperty(exports, "List", { enumerable: true, get: function () { return List_1.List; } });
|
|
6
|
+
Object.defineProperty(exports, "ListItem", { enumerable: true, get: function () { return List_1.ListItem; } });
|
|
7
|
+
Object.defineProperty(exports, "ListSubheader", { enumerable: true, get: function () { return List_1.ListSubheader; } });
|
|
8
|
+
Object.defineProperty(exports, "ListDivider", { enumerable: true, get: function () { return List_1.ListDivider; } });
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Popover — a floating surface that points at an anchor with an arrow.
|
|
3
|
+
*
|
|
4
|
+
* Maps to the Figma <Popover> component (node 307:4149):
|
|
5
|
+
* direction → top | bottom | left | right (the edge the arrow sits on)
|
|
6
|
+
* size → small | default | large (drives content padding)
|
|
7
|
+
*
|
|
8
|
+
* This is the presentational bubble only: it renders the overlay surface
|
|
9
|
+
* (white background, 8px radius, shadow/md) with a directional arrow and your
|
|
10
|
+
* `children` inside. Positioning relative to a trigger is the caller's job —
|
|
11
|
+
* wrap it in a positioned container or pass a `style` with absolute coords.
|
|
12
|
+
*
|
|
13
|
+
* Padding comes from the `popover` density theme (size × density); the radius
|
|
14
|
+
* and arrow size are constant across densities.
|
|
15
|
+
*/
|
|
16
|
+
import React from 'react';
|
|
17
|
+
import { type ViewStyle, type StyleProp } from 'react-native';
|
|
18
|
+
export type PopoverSize = 'small' | 'default' | 'large';
|
|
19
|
+
export type PopoverDirection = 'top' | 'bottom' | 'left' | 'right';
|
|
20
|
+
export type PopoverProps = {
|
|
21
|
+
/** Content rendered inside the bubble. */
|
|
22
|
+
children: React.ReactNode;
|
|
23
|
+
/** The edge the arrow sits on (points away from the bubble). */
|
|
24
|
+
direction?: PopoverDirection;
|
|
25
|
+
/** Size variant — controls content padding. */
|
|
26
|
+
size?: PopoverSize;
|
|
27
|
+
/** Hide the directional arrow. */
|
|
28
|
+
hideArrow?: boolean;
|
|
29
|
+
/** Outer style — use for positioning (absolute coords, margin, width). */
|
|
30
|
+
style?: StyleProp<ViewStyle>;
|
|
31
|
+
/** Accessibility label for the popover surface. */
|
|
32
|
+
accessibilityLabel?: string;
|
|
33
|
+
};
|
|
34
|
+
export declare function Popover({ children, direction, size, hideArrow, style, accessibilityLabel, }: PopoverProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Popover = Popover;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_native_1 = require("react-native");
|
|
6
|
+
const theme_1 = require("../../theme");
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Constants
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
/** shadow/md — overlay drop shadow (two stacked layers). */
|
|
11
|
+
const SHADOW_WEB = {
|
|
12
|
+
boxShadow: '0px 4px 6px -1px rgba(0,0,0,0.07), 0px 2px 4px -2px rgba(0,0,0,0.05)',
|
|
13
|
+
};
|
|
14
|
+
const SHADOW_NATIVE = {
|
|
15
|
+
shadowColor: '#000000',
|
|
16
|
+
shadowOffset: { width: 0, height: 4 },
|
|
17
|
+
shadowOpacity: 0.07,
|
|
18
|
+
shadowRadius: 6,
|
|
19
|
+
elevation: 4,
|
|
20
|
+
};
|
|
21
|
+
const SHADOW = react_native_1.Platform.OS === 'web' ? SHADOW_WEB : SHADOW_NATIVE;
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Component
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
/** Positions the rotated-square arrow on the requested edge, centred. */
|
|
26
|
+
function arrowStyle(direction, arrowSize, backgroundColor) {
|
|
27
|
+
const half = arrowSize / 2;
|
|
28
|
+
const base = {
|
|
29
|
+
position: 'absolute',
|
|
30
|
+
width: arrowSize,
|
|
31
|
+
height: arrowSize,
|
|
32
|
+
backgroundColor,
|
|
33
|
+
transform: [{ rotate: '45deg' }],
|
|
34
|
+
};
|
|
35
|
+
switch (direction) {
|
|
36
|
+
case 'top':
|
|
37
|
+
return { ...base, top: -half, left: '50%', marginLeft: -half };
|
|
38
|
+
case 'left':
|
|
39
|
+
return { ...base, left: -half, top: '50%', marginTop: -half };
|
|
40
|
+
case 'right':
|
|
41
|
+
return { ...base, right: -half, top: '50%', marginTop: -half };
|
|
42
|
+
case 'bottom':
|
|
43
|
+
default:
|
|
44
|
+
return { ...base, bottom: -half, left: '50%', marginLeft: -half };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function Popover({ children, direction = 'bottom', size = 'default', hideArrow = false, style, accessibilityLabel, }) {
|
|
48
|
+
const { components, scheme } = (0, theme_1.useTheme)();
|
|
49
|
+
const surfaceTokens = scheme.surface;
|
|
50
|
+
const tokens = components.popover;
|
|
51
|
+
const sizeTokens = tokens[size];
|
|
52
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { accessibilityLabel: accessibilityLabel, style: [
|
|
53
|
+
{
|
|
54
|
+
alignSelf: 'flex-start',
|
|
55
|
+
backgroundColor: surfaceTokens.overlay.bg,
|
|
56
|
+
borderRadius: tokens.borderRadius,
|
|
57
|
+
padding: sizeTokens.padding,
|
|
58
|
+
...SHADOW,
|
|
59
|
+
},
|
|
60
|
+
style,
|
|
61
|
+
], children: [!hideArrow ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { accessibilityElementsHidden: true, importantForAccessibility: "no", style: arrowStyle(direction, tokens.arrowSize, surfaceTokens.overlay.bg) })) : null, children] }));
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Popover, type PopoverProps, type PopoverSize, type PopoverDirection, } from './Popover';
|