@gryt/ui-native 0.1.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 (137) hide show
  1. package/README.md +86 -0
  2. package/dist/components/Accordion/Accordion.d.ts +35 -0
  3. package/dist/components/Accordion/Accordion.d.ts.map +1 -0
  4. package/dist/components/Accordion/Accordion.js +66 -0
  5. package/dist/components/Alert/Alert.d.ts +21 -0
  6. package/dist/components/Alert/Alert.d.ts.map +1 -0
  7. package/dist/components/Alert/Alert.js +35 -0
  8. package/dist/components/AlertDialog/AlertDialog.d.ts +38 -0
  9. package/dist/components/AlertDialog/AlertDialog.d.ts.map +1 -0
  10. package/dist/components/AlertDialog/AlertDialog.js +15 -0
  11. package/dist/components/Avatar/Avatar.d.ts +10 -0
  12. package/dist/components/Avatar/Avatar.d.ts.map +1 -0
  13. package/dist/components/Avatar/Avatar.js +57 -0
  14. package/dist/components/Badge/Badge.d.ts +13 -0
  15. package/dist/components/Badge/Badge.d.ts.map +1 -0
  16. package/dist/components/Badge/Badge.js +28 -0
  17. package/dist/components/Button/Button.d.ts +24 -0
  18. package/dist/components/Button/Button.d.ts.map +1 -0
  19. package/dist/components/Button/Button.js +61 -0
  20. package/dist/components/Card/Card.d.ts +19 -0
  21. package/dist/components/Card/Card.d.ts.map +1 -0
  22. package/dist/components/Card/Card.js +45 -0
  23. package/dist/components/Checkbox/Checkbox.d.ts +24 -0
  24. package/dist/components/Checkbox/Checkbox.d.ts.map +1 -0
  25. package/dist/components/Checkbox/Checkbox.js +43 -0
  26. package/dist/components/Chip/Chip.d.ts +17 -0
  27. package/dist/components/Chip/Chip.d.ts.map +1 -0
  28. package/dist/components/Chip/Chip.js +38 -0
  29. package/dist/components/Collapsible/Collapsible.d.ts +31 -0
  30. package/dist/components/Collapsible/Collapsible.d.ts.map +1 -0
  31. package/dist/components/Collapsible/Collapsible.js +34 -0
  32. package/dist/components/Dialog/Dialog.d.ts +67 -0
  33. package/dist/components/Dialog/Dialog.d.ts.map +1 -0
  34. package/dist/components/Dialog/Dialog.js +112 -0
  35. package/dist/components/Divider/Divider.d.ts +16 -0
  36. package/dist/components/Divider/Divider.d.ts.map +1 -0
  37. package/dist/components/Divider/Divider.js +21 -0
  38. package/dist/components/Drawer/Drawer.d.ts +44 -0
  39. package/dist/components/Drawer/Drawer.d.ts.map +1 -0
  40. package/dist/components/Drawer/Drawer.js +83 -0
  41. package/dist/components/Menu/Menu.d.ts +56 -0
  42. package/dist/components/Menu/Menu.d.ts.map +1 -0
  43. package/dist/components/Menu/Menu.js +76 -0
  44. package/dist/components/Meter/Meter.d.ts +21 -0
  45. package/dist/components/Meter/Meter.d.ts.map +1 -0
  46. package/dist/components/Meter/Meter.js +32 -0
  47. package/dist/components/NumberField/NumberField.d.ts +23 -0
  48. package/dist/components/NumberField/NumberField.d.ts.map +1 -0
  49. package/dist/components/NumberField/NumberField.js +56 -0
  50. package/dist/components/OtpField/OtpField.d.ts +24 -0
  51. package/dist/components/OtpField/OtpField.d.ts.map +1 -0
  52. package/dist/components/OtpField/OtpField.js +49 -0
  53. package/dist/components/Popover/Popover.d.ts +48 -0
  54. package/dist/components/Popover/Popover.d.ts.map +1 -0
  55. package/dist/components/Popover/Popover.js +54 -0
  56. package/dist/components/Progress/Progress.d.ts +18 -0
  57. package/dist/components/Progress/Progress.d.ts.map +1 -0
  58. package/dist/components/Progress/Progress.js +30 -0
  59. package/dist/components/Radio/Radio.d.ts +28 -0
  60. package/dist/components/Radio/Radio.d.ts.map +1 -0
  61. package/dist/components/Radio/Radio.js +53 -0
  62. package/dist/components/ScrollArea/ScrollArea.d.ts +22 -0
  63. package/dist/components/ScrollArea/ScrollArea.d.ts.map +1 -0
  64. package/dist/components/ScrollArea/ScrollArea.js +17 -0
  65. package/dist/components/Select/Select.d.ts +34 -0
  66. package/dist/components/Select/Select.d.ts.map +1 -0
  67. package/dist/components/Select/Select.js +71 -0
  68. package/dist/components/Skeleton/Skeleton.d.ts +18 -0
  69. package/dist/components/Skeleton/Skeleton.d.ts.map +1 -0
  70. package/dist/components/Skeleton/Skeleton.js +28 -0
  71. package/dist/components/Slider/Slider.d.ts +29 -0
  72. package/dist/components/Slider/Slider.d.ts.map +1 -0
  73. package/dist/components/Slider/Slider.js +94 -0
  74. package/dist/components/Spinner/Spinner.d.ts +20 -0
  75. package/dist/components/Spinner/Spinner.d.ts.map +1 -0
  76. package/dist/components/Spinner/Spinner.js +18 -0
  77. package/dist/components/Surface/Surface.d.ts +17 -0
  78. package/dist/components/Surface/Surface.d.ts.map +1 -0
  79. package/dist/components/Surface/Surface.js +22 -0
  80. package/dist/components/Switch/Switch.d.ts +25 -0
  81. package/dist/components/Switch/Switch.d.ts.map +1 -0
  82. package/dist/components/Switch/Switch.js +65 -0
  83. package/dist/components/Tabs/Tabs.d.ts +51 -0
  84. package/dist/components/Tabs/Tabs.d.ts.map +1 -0
  85. package/dist/components/Tabs/Tabs.js +82 -0
  86. package/dist/components/TextField/TextField.d.ts +15 -0
  87. package/dist/components/TextField/TextField.d.ts.map +1 -0
  88. package/dist/components/TextField/TextField.js +47 -0
  89. package/dist/components/Toast/Toast.d.ts +31 -0
  90. package/dist/components/Toast/Toast.d.ts.map +1 -0
  91. package/dist/components/Toast/Toast.js +86 -0
  92. package/dist/components/Toggle/Toggle.d.ts +19 -0
  93. package/dist/components/Toggle/Toggle.d.ts.map +1 -0
  94. package/dist/components/Toggle/Toggle.js +41 -0
  95. package/dist/components/Toolbar/Toolbar.d.ts +25 -0
  96. package/dist/components/Toolbar/Toolbar.d.ts.map +1 -0
  97. package/dist/components/Toolbar/Toolbar.js +35 -0
  98. package/dist/components/Tooltip/Tooltip.d.ts +40 -0
  99. package/dist/components/Tooltip/Tooltip.d.ts.map +1 -0
  100. package/dist/components/Tooltip/Tooltip.js +52 -0
  101. package/dist/hooks/useReducedMotion.d.ts +10 -0
  102. package/dist/hooks/useReducedMotion.d.ts.map +1 -0
  103. package/dist/hooks/useReducedMotion.js +26 -0
  104. package/dist/index.d.ts +39 -0
  105. package/dist/index.d.ts.map +1 -0
  106. package/dist/index.js +38 -0
  107. package/dist/overlay/AnchoredPopup.d.ts +26 -0
  108. package/dist/overlay/AnchoredPopup.d.ts.map +1 -0
  109. package/dist/overlay/AnchoredPopup.js +36 -0
  110. package/dist/overlay/placePopup.d.ts +51 -0
  111. package/dist/overlay/placePopup.d.ts.map +1 -0
  112. package/dist/overlay/placePopup.js +63 -0
  113. package/dist/overlay/useAnchoredPosition.d.ts +18 -0
  114. package/dist/overlay/useAnchoredPosition.d.ts.map +1 -0
  115. package/dist/overlay/useAnchoredPosition.js +25 -0
  116. package/dist/overlay/useOpenState.d.ts +17 -0
  117. package/dist/overlay/useOpenState.d.ts.map +1 -0
  118. package/dist/overlay/useOpenState.js +12 -0
  119. package/dist/theme/ThemeProvider.d.ts +17 -0
  120. package/dist/theme/ThemeProvider.d.ts.map +1 -0
  121. package/dist/theme/ThemeProvider.js +17 -0
  122. package/dist/theme/createNativeTheme.d.ts +82 -0
  123. package/dist/theme/createNativeTheme.d.ts.map +1 -0
  124. package/dist/theme/createNativeTheme.js +65 -0
  125. package/dist/theme/index.d.ts +6 -0
  126. package/dist/theme/index.d.ts.map +1 -0
  127. package/dist/theme/index.js +5 -0
  128. package/dist/theme/themeContext.d.ts +9 -0
  129. package/dist/theme/themeContext.d.ts.map +1 -0
  130. package/dist/theme/themeContext.js +9 -0
  131. package/dist/theme/tones.d.ts +15 -0
  132. package/dist/theme/tones.d.ts.map +1 -0
  133. package/dist/theme/tones.js +11 -0
  134. package/dist/theme/useTheme.d.ts +8 -0
  135. package/dist/theme/useTheme.d.ts.map +1 -0
  136. package/dist/theme/useTheme.js +10 -0
  137. package/package.json +46 -0
@@ -0,0 +1,76 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { createContext, useCallback, useContext, useRef } from "react";
3
+ import { Pressable, Text, View } from "react-native";
4
+ import { AnchoredPopup } from "../../overlay/AnchoredPopup.js";
5
+ import { useAnchorMeasure } from "../../overlay/useAnchoredPosition.js";
6
+ import { useOpenState } from "../../overlay/useOpenState.js";
7
+ import { useTheme } from "../../theme/index.js";
8
+ const MenuContext = createContext(null);
9
+ function useMenu(part) {
10
+ const value = useContext(MenuContext);
11
+ if (!value)
12
+ throw new Error(`Menu.${part} must be rendered inside Menu.Root.`);
13
+ return value;
14
+ }
15
+ function Root({ children, ...openProps }) {
16
+ const { open, setOpen } = useOpenState(openProps);
17
+ const { anchor, measure } = useAnchorMeasure();
18
+ return (_jsx(MenuContext.Provider, { value: { open, setOpen, anchor, measure }, children: children }));
19
+ }
20
+ function Trigger({ children, style }) {
21
+ const { setOpen, measure } = useMenu("Trigger");
22
+ const ref = useRef(null);
23
+ const onPress = useCallback(() => {
24
+ measure(ref.current);
25
+ setOpen(true);
26
+ }, [measure, setOpen]);
27
+ return (_jsx(Pressable, { ref: ref, accessibilityRole: "button", onPress: onPress, style: style, children: children }));
28
+ }
29
+ function Portal({ children }) {
30
+ return _jsx(_Fragment, { children: children });
31
+ }
32
+ /**
33
+ * Kept as a passthrough for shape.
34
+ *
35
+ * Base UI splits Positioner from Popup so the positioned box and the styled box
36
+ * are separate elements. Here AnchoredPopup is both, because there is no
37
+ * stacking context to escape and nothing gained by the extra view.
38
+ */
39
+ function Positioner({ children }) {
40
+ return _jsx(_Fragment, { children: children });
41
+ }
42
+ function Popup({ children, style, ...options }) {
43
+ const { open, setOpen, anchor } = useMenu("Popup");
44
+ const theme = useTheme();
45
+ return (_jsx(AnchoredPopup, { open: open, anchor: anchor, onDismiss: () => setOpen(false), align: "start", style: [{ paddingVertical: theme.space(1), minWidth: 180 }, style], ...options, children: _jsx(View, { accessibilityRole: "menu", children: children }) }));
46
+ }
47
+ function Item({ children, onPress, disabled, destructive, style }) {
48
+ const { setOpen } = useMenu("Item");
49
+ const theme = useTheme();
50
+ return (_jsx(Pressable, { accessibilityRole: "menuitem", accessibilityState: { disabled: !!disabled }, disabled: disabled,
51
+ // Choosing something closes the menu, which is what Base UI does unless
52
+ // an item opts out. Nothing here opts out yet.
53
+ onPress: () => {
54
+ onPress?.();
55
+ setOpen(false);
56
+ }, style: ({ pressed }) => [
57
+ {
58
+ paddingHorizontal: theme.space(4),
59
+ paddingVertical: theme.space(2.5),
60
+ opacity: disabled ? 0.5 : 1,
61
+ backgroundColor: pressed ? theme.color.surfaceHover : "transparent",
62
+ },
63
+ style,
64
+ ], children: typeof children === "string" ? (_jsx(Text, { style: {
65
+ color: destructive ? theme.scales.danger[10] : theme.color.text,
66
+ fontSize: 14,
67
+ }, children: children })) : (children) }));
68
+ }
69
+ function Separator({ style }) {
70
+ const theme = useTheme();
71
+ return (_jsx(View, { style: [
72
+ { height: 1, backgroundColor: theme.color.border, marginVertical: theme.space(1) },
73
+ style,
74
+ ] }));
75
+ }
76
+ export const Menu = { Root, Trigger, Portal, Positioner, Popup, Item, Separator };
@@ -0,0 +1,21 @@
1
+ import type { ReactNode } from "react";
2
+ import { type StyleProp, type ViewStyle } from "react-native";
3
+ export interface MeterProps {
4
+ value: number;
5
+ min?: number;
6
+ max?: number;
7
+ label?: ReactNode;
8
+ /** Shows the formatted value at the end of the label row. */
9
+ showValue?: boolean;
10
+ format?: (value: number) => string;
11
+ style?: StyleProp<ViewStyle>;
12
+ }
13
+ /**
14
+ * A reading, not a progress bar.
15
+ *
16
+ * Progress goes one way and finishes. A meter is a level that moves in both
17
+ * directions: microphone input, disk use, how full a channel is. `@gryt/ui`
18
+ * keeps them separate for that reason and so does this.
19
+ */
20
+ export declare function Meter({ value, min, max, label, showValue, format, style, }: MeterProps): import("react").JSX.Element;
21
+ //# sourceMappingURL=Meter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Meter.d.ts","sourceRoot":"","sources":["../../../src/components/Meter/Meter.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAc,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAI1E,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACnC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,EACpB,KAAK,EACL,GAAO,EACP,GAAS,EACT,KAAK,EACL,SAAiB,EACjB,MAAM,EACN,KAAK,GACN,EAAE,UAAU,+BAmDZ"}
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Text, View } from "react-native";
3
+ import { useTheme } from "../../theme/index.js";
4
+ /**
5
+ * A reading, not a progress bar.
6
+ *
7
+ * Progress goes one way and finishes. A meter is a level that moves in both
8
+ * directions: microphone input, disk use, how full a channel is. `@gryt/ui`
9
+ * keeps them separate for that reason and so does this.
10
+ */
11
+ export function Meter({ value, min = 0, max = 100, label, showValue = false, format, style, }) {
12
+ const theme = useTheme();
13
+ const ratio = Math.max(0, Math.min(1, (value - min) / (max - min || 1)));
14
+ // Green until it is worth noticing, amber, then red. The thresholds match the
15
+ // web's: two thirds and nine tenths.
16
+ const ramp = ratio >= 0.9
17
+ ? theme.scales.danger
18
+ : ratio >= 0.66
19
+ ? theme.scales.warning
20
+ : theme.scales.success;
21
+ return (_jsxs(View, { style: [{ gap: theme.space(1.5) }, style], children: [label || showValue ? (_jsxs(View, { style: { flexDirection: "row", justifyContent: "space-between" }, children: [typeof label === "string" ? (_jsx(Text, { style: { color: theme.color.muted, fontSize: 13 }, children: label })) : (label), showValue ? (_jsx(Text, { style: { color: theme.color.muted, fontSize: 13 }, children: format ? format(value) : String(Math.round(value)) })) : null] })) : null, _jsx(View, { accessibilityRole: "progressbar", accessibilityValue: { min, max, now: value }, style: {
22
+ height: 6,
23
+ borderRadius: theme.radius.full,
24
+ backgroundColor: theme.scales.neutral[3],
25
+ overflow: "hidden",
26
+ }, children: _jsx(View, { style: {
27
+ width: `${ratio * 100}%`,
28
+ height: "100%",
29
+ borderRadius: theme.radius.full,
30
+ backgroundColor: ramp[8],
31
+ } }) })] }));
32
+ }
@@ -0,0 +1,23 @@
1
+ import { type ReactNode } from "react";
2
+ import { type StyleProp, type ViewStyle } from "react-native";
3
+ export interface NumberFieldProps {
4
+ value?: number;
5
+ defaultValue?: number;
6
+ onValueChange?: (value: number) => void;
7
+ min?: number;
8
+ max?: number;
9
+ step?: number;
10
+ label?: ReactNode;
11
+ disabled?: boolean;
12
+ style?: StyleProp<ViewStyle>;
13
+ }
14
+ /**
15
+ * Typed or stepped, with the keyboard set to numeric.
16
+ *
17
+ * The web version also scrubs: press the label and drag sideways to change the
18
+ * value. That gesture is not reproduced. It competes with scrolling on a phone,
19
+ * and the same interaction already exists here as Slider, which is a better fit
20
+ * for a screen you touch.
21
+ */
22
+ export declare function NumberField({ value: controlled, defaultValue, onValueChange, min, max, step, label, disabled, style, }: NumberFieldProps): import("react").JSX.Element;
23
+ //# sourceMappingURL=NumberField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NumberField.d.ts","sourceRoot":"","sources":["../../../src/components/NumberField/NumberField.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,EAAoC,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAuChG,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EAAE,UAAU,EACjB,YAAgB,EAChB,aAAa,EACb,GAA8B,EAC9B,GAA8B,EAC9B,IAAQ,EACR,KAAK,EACL,QAAQ,EACR,KAAK,GACN,EAAE,gBAAgB,+BAgElB"}
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { Pressable, Text, TextInput, View } from "react-native";
4
+ import { useTheme } from "../../theme/index.js";
5
+ /**
6
+ * Declared here rather than inside NumberField.
7
+ *
8
+ * A component defined in another component's body is a new type on every
9
+ * render, so React unmounts and remounts it each time and any state inside is
10
+ * lost. Harmless for a button with no state, and still the kind of thing that
11
+ * bites later.
12
+ */
13
+ function StepButton({ glyph, label, disabled, onPress, }) {
14
+ const theme = useTheme();
15
+ return (_jsx(Pressable, { accessibilityRole: "button", accessibilityLabel: label, disabled: disabled, onPress: onPress, style: { paddingHorizontal: theme.space(3), paddingVertical: theme.space(2) }, children: _jsx(Text, { style: { color: theme.color.muted, fontSize: 16, fontWeight: "600" }, children: glyph }) }));
16
+ }
17
+ /**
18
+ * Typed or stepped, with the keyboard set to numeric.
19
+ *
20
+ * The web version also scrubs: press the label and drag sideways to change the
21
+ * value. That gesture is not reproduced. It competes with scrolling on a phone,
22
+ * and the same interaction already exists here as Slider, which is a better fit
23
+ * for a screen you touch.
24
+ */
25
+ export function NumberField({ value: controlled, defaultValue = 0, onValueChange, min = Number.NEGATIVE_INFINITY, max = Number.POSITIVE_INFINITY, step = 1, label, disabled, style, }) {
26
+ const theme = useTheme();
27
+ const [uncontrolled, setUncontrolled] = useState(defaultValue);
28
+ const value = controlled ?? uncontrolled;
29
+ // Kept separately so a half-typed "-" or "" does not become 0 mid-edit.
30
+ const [text, setText] = useState(String(defaultValue));
31
+ const commit = (next) => {
32
+ const clamped = Math.max(min, Math.min(max, next));
33
+ if (controlled === undefined)
34
+ setUncontrolled(clamped);
35
+ setText(String(clamped));
36
+ onValueChange?.(clamped);
37
+ };
38
+ return (_jsxs(View, { style: [{ gap: theme.space(1.5) }, style], children: [label ? (_jsx(Text, { style: { color: theme.color.muted, fontSize: 13, fontWeight: "500" }, children: label })) : null, _jsxs(View, { style: {
39
+ flexDirection: "row",
40
+ alignItems: "center",
41
+ borderWidth: 1,
42
+ borderColor: theme.color.border,
43
+ borderRadius: theme.radius.md,
44
+ backgroundColor: theme.color.surface,
45
+ opacity: disabled ? 0.5 : 1,
46
+ }, children: [_jsx(StepButton, { glyph: "\u2212", label: "Decrease", disabled: disabled || value <= min, onPress: () => commit(value - step) }), _jsx(TextInput, { editable: !disabled, keyboardType: "numeric", value: text, onChangeText: setText, onBlur: () => {
47
+ const parsed = Number(text);
48
+ commit(Number.isFinite(parsed) ? parsed : value);
49
+ }, style: {
50
+ flex: 1,
51
+ textAlign: "center",
52
+ color: theme.color.text,
53
+ fontSize: 14,
54
+ paddingVertical: theme.space(2),
55
+ } }), _jsx(StepButton, { glyph: "+", label: "Increase", disabled: disabled || value >= max, onPress: () => commit(value + step) })] })] }));
56
+ }
@@ -0,0 +1,24 @@
1
+ import { type StyleProp, type ViewStyle } from "react-native";
2
+ export interface OtpFieldProps {
3
+ length?: number;
4
+ value?: string;
5
+ onValueChange?: (value: string) => void;
6
+ onComplete?: (value: string) => void;
7
+ disabled?: boolean;
8
+ autoFocus?: boolean;
9
+ style?: StyleProp<ViewStyle>;
10
+ }
11
+ /**
12
+ * One hidden input behind a row of boxes.
13
+ *
14
+ * The obvious build is one TextInput per digit, and it is a trap: backspace
15
+ * across an empty box, paste, and autofill all have to be threaded between
16
+ * inputs by hand, and every platform disagrees about the events. A single
17
+ * invisible input holding the whole code gets all three for free, and the boxes
18
+ * become presentation.
19
+ *
20
+ * `textContentType="oneTimeCode"` is what makes iOS offer the code from
21
+ * Messages. Android reads `autoComplete="sms-otp"`.
22
+ */
23
+ export declare function OtpField({ length, value: controlled, onValueChange, onComplete, disabled, autoFocus, style, }: OtpFieldProps): import("react").JSX.Element;
24
+ //# sourceMappingURL=OtpField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OtpField.d.ts","sourceRoot":"","sources":["../../../src/components/OtpField/OtpField.tsx"],"names":[],"mappings":"AACA,OAAO,EAAoC,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAIhG,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,EACvB,MAAU,EACV,KAAK,EAAE,UAAU,EACjB,aAAa,EACb,UAAU,EACV,QAAQ,EACR,SAAS,EACT,KAAK,GACN,EAAE,aAAa,+BAgEf"}
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef, useState } from "react";
3
+ import { Pressable, Text, TextInput, View } from "react-native";
4
+ import { useTheme } from "../../theme/index.js";
5
+ /**
6
+ * One hidden input behind a row of boxes.
7
+ *
8
+ * The obvious build is one TextInput per digit, and it is a trap: backspace
9
+ * across an empty box, paste, and autofill all have to be threaded between
10
+ * inputs by hand, and every platform disagrees about the events. A single
11
+ * invisible input holding the whole code gets all three for free, and the boxes
12
+ * become presentation.
13
+ *
14
+ * `textContentType="oneTimeCode"` is what makes iOS offer the code from
15
+ * Messages. Android reads `autoComplete="sms-otp"`.
16
+ */
17
+ export function OtpField({ length = 6, value: controlled, onValueChange, onComplete, disabled, autoFocus, style, }) {
18
+ const theme = useTheme();
19
+ const [uncontrolled, setUncontrolled] = useState("");
20
+ const value = controlled ?? uncontrolled;
21
+ const [focused, setFocused] = useState(false);
22
+ const input = useRef(null);
23
+ const setValue = (next) => {
24
+ const digits = next.replace(/\D/g, "").slice(0, length);
25
+ if (controlled === undefined)
26
+ setUncontrolled(digits);
27
+ onValueChange?.(digits);
28
+ if (digits.length === length)
29
+ onComplete?.(digits);
30
+ };
31
+ return (_jsxs(Pressable, { accessibilityRole: "none", disabled: disabled, onPress: () => input.current?.focus(), style: [{ flexDirection: "row", gap: theme.space(2) }, style], children: [Array.from({ length }).map((_, i) => {
32
+ const filled = i < value.length;
33
+ const active = focused && i === value.length;
34
+ return (_jsx(View, { style: {
35
+ flex: 1,
36
+ aspectRatio: 0.8,
37
+ alignItems: "center",
38
+ justifyContent: "center",
39
+ borderWidth: 1,
40
+ borderColor: active ? theme.color.accent : theme.color.border,
41
+ borderRadius: theme.radius.md,
42
+ backgroundColor: theme.color.surface,
43
+ opacity: disabled ? 0.5 : 1,
44
+ }, children: _jsx(Text, { style: { color: theme.color.text, fontSize: 20, fontWeight: "600" }, children: filled ? value[i] : "" }) }, i));
45
+ }), _jsx(TextInput, { ref: input, value: value, onChangeText: setValue, onFocus: () => setFocused(true), onBlur: () => setFocused(false), editable: !disabled, autoFocus: autoFocus, keyboardType: "number-pad", textContentType: "oneTimeCode", autoComplete: "sms-otp", maxLength: length,
46
+ // Off-screen rather than hidden: opacity 0 still takes the caret on
47
+ // some Android builds, and display:none stops autofill reaching it.
48
+ style: { position: "absolute", top: -9999, left: -9999, width: 1, height: 1 } })] }));
49
+ }
@@ -0,0 +1,48 @@
1
+ import { type ReactNode } from "react";
2
+ import { type StyleProp, type ViewStyle } from "react-native";
3
+ import type { Align, Side } from "../../overlay/placePopup.js";
4
+ import { type OpenStateProps } from "../../overlay/useOpenState.js";
5
+ export interface PopoverRootProps extends OpenStateProps {
6
+ children?: ReactNode;
7
+ }
8
+ declare function Root({ children, ...openProps }: PopoverRootProps): import("react").JSX.Element;
9
+ declare function Trigger({ children, style }: {
10
+ children?: ReactNode;
11
+ style?: StyleProp<ViewStyle>;
12
+ }): import("react").JSX.Element;
13
+ /** A passthrough. React Native's Modal already renders above everything. */
14
+ declare function Portal({ children }: {
15
+ children?: ReactNode;
16
+ }): import("react").JSX.Element;
17
+ export interface PopoverPopupProps {
18
+ children?: ReactNode;
19
+ side?: Side;
20
+ align?: Align;
21
+ sideOffset?: number;
22
+ style?: StyleProp<ViewStyle>;
23
+ }
24
+ declare function Popup({ children, style, ...options }: PopoverPopupProps): import("react").JSX.Element;
25
+ declare function Close({ children, style }: {
26
+ children?: ReactNode;
27
+ style?: StyleProp<ViewStyle>;
28
+ }): import("react").JSX.Element;
29
+ /**
30
+ * Not drawn.
31
+ *
32
+ * Base UI's Arrow is an element positioned against the popup's edge once
33
+ * Floating UI has settled. Doing it here means a rotated square, a border that
34
+ * only shows on two sides, and knowing which side the popup ended up on. It is
35
+ * doable and it is not free, so it is an exception rather than a silent
36
+ * omission. Kept as a no-op so call sites match.
37
+ */
38
+ declare function Arrow(): null;
39
+ export declare const Popover: {
40
+ Root: typeof Root;
41
+ Trigger: typeof Trigger;
42
+ Portal: typeof Portal;
43
+ Popup: typeof Popup;
44
+ Close: typeof Close;
45
+ Arrow: typeof Arrow;
46
+ };
47
+ export {};
48
+ //# sourceMappingURL=Popover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Popover.d.ts","sourceRoot":"","sources":["../../../src/components/Popover/Popover.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACvF,OAAO,EAAmB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAG/E,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAE5D,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAkB/E,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,iBAAS,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,SAAS,EAAE,EAAE,gBAAgB,+BAQzD;AAED,iBAAS,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAA;CAAE,+BAc3F;AAED,4EAA4E;AAC5E,iBAAS,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,+BAErD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,iBAAS,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,EAAE,iBAAiB,+BAchE;AAED,iBAAS,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAA;CAAE,+BAOzF;AAED;;;;;;;;GAQG;AACH,iBAAS,KAAK,SAEb;AAED,eAAO,MAAM,OAAO;;;;;;;CAAiD,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { createContext, useCallback, useContext, useRef } from "react";
3
+ import { Pressable } from "react-native";
4
+ import { AnchoredPopup } from "../../overlay/AnchoredPopup.js";
5
+ import { useAnchorMeasure } from "../../overlay/useAnchoredPosition.js";
6
+ import { useOpenState } from "../../overlay/useOpenState.js";
7
+ import { useTheme } from "../../theme/index.js";
8
+ const PopoverContext = createContext(null);
9
+ function usePopover(part) {
10
+ const value = useContext(PopoverContext);
11
+ if (!value)
12
+ throw new Error(`Popover.${part} must be rendered inside Popover.Root.`);
13
+ return value;
14
+ }
15
+ function Root({ children, ...openProps }) {
16
+ const { open, setOpen } = useOpenState(openProps);
17
+ const { anchor, measure } = useAnchorMeasure();
18
+ return (_jsx(PopoverContext.Provider, { value: { open, setOpen, anchor, measure }, children: children }));
19
+ }
20
+ function Trigger({ children, style }) {
21
+ const { setOpen, measure } = usePopover("Trigger");
22
+ const ref = useRef(null);
23
+ const onPress = useCallback(() => {
24
+ measure(ref.current);
25
+ setOpen(true);
26
+ }, [measure, setOpen]);
27
+ return (_jsx(Pressable, { ref: ref, accessibilityRole: "button", onPress: onPress, style: style, children: children }));
28
+ }
29
+ /** A passthrough. React Native's Modal already renders above everything. */
30
+ function Portal({ children }) {
31
+ return _jsx(_Fragment, { children: children });
32
+ }
33
+ function Popup({ children, style, ...options }) {
34
+ const { open, setOpen, anchor } = usePopover("Popup");
35
+ const theme = useTheme();
36
+ return (_jsx(AnchoredPopup, { open: open, anchor: anchor, onDismiss: () => setOpen(false), style: [{ padding: theme.space(3), minWidth: 160 }, style], ...options, children: children }));
37
+ }
38
+ function Close({ children, style }) {
39
+ const { setOpen } = usePopover("Close");
40
+ return (_jsx(Pressable, { accessibilityRole: "button", onPress: () => setOpen(false), style: style, children: children }));
41
+ }
42
+ /**
43
+ * Not drawn.
44
+ *
45
+ * Base UI's Arrow is an element positioned against the popup's edge once
46
+ * Floating UI has settled. Doing it here means a rotated square, a border that
47
+ * only shows on two sides, and knowing which side the popup ended up on. It is
48
+ * doable and it is not free, so it is an exception rather than a silent
49
+ * omission. Kept as a no-op so call sites match.
50
+ */
51
+ function Arrow() {
52
+ return null;
53
+ }
54
+ export const Popover = { Root, Trigger, Portal, Popup, Close, Arrow };
@@ -0,0 +1,18 @@
1
+ import { type StyleProp, type ViewStyle } from "react-native";
2
+ export interface ProgressProps {
3
+ /** 0 to 100. Leave undefined for an indeterminate bar. */
4
+ value?: number;
5
+ tone?: "accent" | "success" | "danger" | "warning";
6
+ style?: StyleProp<ViewStyle>;
7
+ }
8
+ /**
9
+ * Determinate only for now.
10
+ *
11
+ * The web's indeterminate state is a CSS keyframe sweeping a partial bar across
12
+ * the track. Reproducing it here needs an Animated loop and a reduce-motion
13
+ * check, which is worth doing properly rather than as a footnote on this one, so
14
+ * an undefined value currently renders an empty track. Recorded in the parity
15
+ * exceptions table.
16
+ */
17
+ export declare function Progress({ value, tone, style }: ProgressProps): import("react").JSX.Element;
18
+ //# sourceMappingURL=Progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Progress.d.ts","sourceRoot":"","sources":["../../../src/components/Progress/Progress.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAIpE,MAAM,WAAW,aAAa;IAC5B,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IACnD,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAe,EAAE,KAAK,EAAE,EAAE,aAAa,+BA4BxE"}
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { View } from "react-native";
3
+ import { useTheme } from "../../theme/index.js";
4
+ /**
5
+ * Determinate only for now.
6
+ *
7
+ * The web's indeterminate state is a CSS keyframe sweeping a partial bar across
8
+ * the track. Reproducing it here needs an Animated loop and a reduce-motion
9
+ * check, which is worth doing properly rather than as a footnote on this one, so
10
+ * an undefined value currently renders an empty track. Recorded in the parity
11
+ * exceptions table.
12
+ */
13
+ export function Progress({ value, tone = "accent", style }) {
14
+ const theme = useTheme();
15
+ const clamped = value === undefined ? 0 : Math.max(0, Math.min(100, value));
16
+ return (_jsx(View, { accessibilityRole: "progressbar", accessibilityValue: value === undefined ? undefined : { now: clamped, min: 0, max: 100 }, style: [
17
+ {
18
+ height: 6,
19
+ borderRadius: theme.radius.full,
20
+ backgroundColor: theme.scales.neutral[3],
21
+ overflow: "hidden",
22
+ },
23
+ style,
24
+ ], children: _jsx(View, { style: {
25
+ width: `${clamped}%`,
26
+ height: "100%",
27
+ borderRadius: theme.radius.full,
28
+ backgroundColor: theme.scales[tone][8],
29
+ } }) }));
30
+ }
@@ -0,0 +1,28 @@
1
+ import { type ReactNode } from "react";
2
+ import { type StyleProp, type ViewStyle } from "react-native";
3
+ export type RadioTone = "primary" | "secondary" | "neutral" | "danger";
4
+ export interface RadioGroupProps {
5
+ value?: string | number;
6
+ defaultValue?: string | number;
7
+ onValueChange?: (value: string | number) => void;
8
+ disabled?: boolean;
9
+ children?: ReactNode;
10
+ style?: StyleProp<ViewStyle>;
11
+ }
12
+ /**
13
+ * The group holds the value, which is the part a radio cannot do alone.
14
+ *
15
+ * The web gets this from Base UI's RadioGroup, and a lone Radio outside one is
16
+ * a mistake there too. Here it throws, because a radio that silently never
17
+ * checks is worse than one that says why.
18
+ */
19
+ export declare function RadioGroup({ value: controlled, defaultValue, onValueChange, disabled, children, style, }: RadioGroupProps): import("react").JSX.Element;
20
+ export interface RadioProps {
21
+ value: string | number;
22
+ disabled?: boolean;
23
+ tone?: RadioTone;
24
+ style?: StyleProp<ViewStyle>;
25
+ accessibilityLabel?: string;
26
+ }
27
+ export declare function Radio({ value, disabled, tone, style, accessibilityLabel, }: RadioProps): import("react").JSX.Element;
28
+ //# sourceMappingURL=Radio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Radio.d.ts","sourceRoot":"","sources":["../../../src/components/Radio/Radio.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5E,OAAO,EAAmB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAI/E,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAUvE,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;IACjD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EAAE,UAAU,EACjB,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,KAAK,GACN,EAAE,eAAe,+BAsBjB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAID,wBAAgB,KAAK,CAAC,EACpB,KAAK,EACL,QAAQ,EACR,IAAgB,EAChB,KAAK,EACL,kBAAkB,GACnB,EAAE,UAAU,+BA0CZ"}
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext, useState } from "react";
3
+ import { Pressable, View } from "react-native";
4
+ import { toneRamp, useTheme } from "../../theme/index.js";
5
+ const RadioGroupContext = createContext(null);
6
+ /**
7
+ * The group holds the value, which is the part a radio cannot do alone.
8
+ *
9
+ * The web gets this from Base UI's RadioGroup, and a lone Radio outside one is
10
+ * a mistake there too. Here it throws, because a radio that silently never
11
+ * checks is worse than one that says why.
12
+ */
13
+ export function RadioGroup({ value: controlled, defaultValue, onValueChange, disabled, children, style, }) {
14
+ const [uncontrolled, setUncontrolled] = useState(defaultValue ?? null);
15
+ const value = controlled ?? uncontrolled;
16
+ return (_jsx(RadioGroupContext.Provider, { value: {
17
+ value,
18
+ disabled,
19
+ setValue: (next) => {
20
+ if (controlled === undefined)
21
+ setUncontrolled(next);
22
+ onValueChange?.(next);
23
+ },
24
+ }, children: _jsx(View, { accessibilityRole: "radiogroup", style: style, children: children }) }));
25
+ }
26
+ const SIZE = 20;
27
+ export function Radio({ value, disabled, tone = "primary", style, accessibilityLabel, }) {
28
+ const theme = useTheme();
29
+ const group = useContext(RadioGroupContext);
30
+ if (!group)
31
+ throw new Error("Radio must be rendered inside a RadioGroup.");
32
+ const selected = group.value === value;
33
+ const isDisabled = disabled || group.disabled;
34
+ const ramp = toneRamp(theme, tone);
35
+ return (_jsx(Pressable, { accessibilityRole: "radio", accessibilityState: { checked: selected, disabled: !!isDisabled }, accessibilityLabel: accessibilityLabel, disabled: isDisabled, onPress: () => group.setValue(value), style: [
36
+ {
37
+ width: SIZE,
38
+ height: SIZE,
39
+ borderRadius: SIZE / 2,
40
+ alignItems: "center",
41
+ justifyContent: "center",
42
+ borderWidth: 1.5,
43
+ borderColor: selected ? ramp[8] : theme.color.border,
44
+ opacity: isDisabled ? 0.5 : 1,
45
+ },
46
+ style,
47
+ ], children: selected ? (_jsx(View, { style: {
48
+ width: SIZE / 2,
49
+ height: SIZE / 2,
50
+ borderRadius: SIZE / 4,
51
+ backgroundColor: ramp[8],
52
+ } })) : null }));
53
+ }
@@ -0,0 +1,22 @@
1
+ import type { ReactNode } from "react";
2
+ import { type ScrollViewProps, type StyleProp, type ViewStyle } from "react-native";
3
+ export interface ScrollAreaProps extends Omit<ScrollViewProps, "style"> {
4
+ children?: ReactNode;
5
+ horizontal?: boolean;
6
+ style?: StyleProp<ViewStyle>;
7
+ contentStyle?: StyleProp<ViewStyle>;
8
+ }
9
+ /**
10
+ * A thin pass over ScrollView.
11
+ *
12
+ * The web's ScrollArea exists to replace scrollbars the browser draws badly and
13
+ * inconsistently. A phone has no scrollbars to replace: the indicator is drawn
14
+ * by the OS, it fades on its own, and it already matches everything else on the
15
+ * device. So the component stays for call-site parity and does almost nothing,
16
+ * which is the honest version.
17
+ *
18
+ * Its one job is defaulting the indicator off, since the web version hides the
19
+ * native scrollbar too.
20
+ */
21
+ export declare function ScrollArea({ children, horizontal, style, contentStyle, showsVerticalScrollIndicator, showsHorizontalScrollIndicator, ...rest }: ScrollAreaProps): import("react").JSX.Element;
22
+ //# sourceMappingURL=ScrollArea.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScrollArea.d.ts","sourceRoot":"","sources":["../../../src/components/ScrollArea/ScrollArea.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAc,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAEhG,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC;IACrE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACrC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,UAAkB,EAClB,KAAK,EACL,YAAY,EACZ,4BAAoC,EACpC,8BAAsC,EACtC,GAAG,IAAI,EACR,EAAE,eAAe,+BAajB"}
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { ScrollView } from "react-native";
3
+ /**
4
+ * A thin pass over ScrollView.
5
+ *
6
+ * The web's ScrollArea exists to replace scrollbars the browser draws badly and
7
+ * inconsistently. A phone has no scrollbars to replace: the indicator is drawn
8
+ * by the OS, it fades on its own, and it already matches everything else on the
9
+ * device. So the component stays for call-site parity and does almost nothing,
10
+ * which is the honest version.
11
+ *
12
+ * Its one job is defaulting the indicator off, since the web version hides the
13
+ * native scrollbar too.
14
+ */
15
+ export function ScrollArea({ children, horizontal = false, style, contentStyle, showsVerticalScrollIndicator = false, showsHorizontalScrollIndicator = false, ...rest }) {
16
+ return (_jsx(ScrollView, { horizontal: horizontal, style: style, contentContainerStyle: contentStyle, showsVerticalScrollIndicator: showsVerticalScrollIndicator, showsHorizontalScrollIndicator: showsHorizontalScrollIndicator, ...rest, children: children }));
17
+ }
@@ -0,0 +1,34 @@
1
+ import { type ReactNode } from "react";
2
+ import { type StyleProp, type ViewStyle } from "react-native";
3
+ export interface SelectOption {
4
+ label: ReactNode;
5
+ value: string | number;
6
+ disabled?: boolean;
7
+ }
8
+ export type SelectSize = "small" | "medium";
9
+ export interface SelectProps {
10
+ options?: SelectOption[];
11
+ value?: string | number | null;
12
+ defaultValue?: string | number | null;
13
+ onValueChange?: (value: string | number) => void;
14
+ label?: ReactNode;
15
+ placeholder?: string;
16
+ size?: SelectSize;
17
+ disabled?: boolean;
18
+ style?: StyleProp<ViewStyle>;
19
+ }
20
+ /**
21
+ * A single component, like the web's, rather than a namespace.
22
+ *
23
+ * `@gryt/ui`'s Select takes an `options` array and renders the trigger, the
24
+ * list, the checkmark and the positioning itself, so this matches that shape
25
+ * instead of exposing Root/Trigger/Popup.
26
+ *
27
+ * Underneath it is the same anchored popup Menu uses. The one thing worth
28
+ * knowing: the platform pickers are not used. iOS would give a wheel and Android
29
+ * a dialog, neither takes the Gryt palette, and the two look nothing like each
30
+ * other. A list matching the rest of the library is more use than two native
31
+ * controls that match neither.
32
+ */
33
+ export declare function Select({ options, value: controlled, defaultValue, onValueChange, label, placeholder, size, disabled, style, }: SelectProps): import("react").JSX.Element;
34
+ //# sourceMappingURL=Select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../../src/components/Select/Select.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACtE,OAAO,EAAyB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAMrF,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;AAO5C,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACtC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;IACjD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CAAC,EACrB,OAAY,EACZ,KAAK,EAAE,UAAU,EACjB,YAAmB,EACnB,aAAa,EACb,KAAK,EACL,WAAuB,EACvB,IAAe,EACf,QAAQ,EACR,KAAK,GACN,EAAE,WAAW,+BAgHb"}