@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
package/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # @gryt/ui-native
2
+
3
+ Gryt's design system on React Native. The same tokens as `@gryt/ui`, a different
4
+ renderer.
5
+
6
+ ## Status
7
+
8
+ **Private, and not published.** Thirty-three components across the overlays, the
9
+ form controls, and the layout and feedback set. Flip `private` off once there is
10
+ a reason to install it.
11
+
12
+ Still missing, and all Gryt-specific or built on something not here yet:
13
+ Composer, ConversationItem, MessageBubble, NavigationMenu, ContextMenu, Drawer,
14
+ PreviewCard, Toolbar, Form, Toast, ScrollArea, Accordion, Autocomplete,
15
+ Combobox, NumberField and OtpField.
16
+
17
+ Tracked in GRYT-342. The goal is 1:1 with `@gryt/ui`: every component, matching
18
+ behaviour, no mobile-only limits.
19
+
20
+ ## The tokens are imported, not copied
21
+
22
+ ```ts
23
+ import { createNativeTheme } from "@gryt/ui-native";
24
+ ```
25
+
26
+ `createNativeTheme` builds its ramps by calling `neutralScale`, `hueScale` and
27
+ `alphaScale` from `@gryt/ui/theme`. There is one implementation of the OKLab
28
+ maths and both renderers use it, so a curve tuned on the web moves here too.
29
+
30
+ What it does **not** use is `createGrytTheme`, which returns `CSSProperties` — a
31
+ map of `--gryt-accent-9` custom properties. React Native has no custom
32
+ properties and no cascade, so components take a theme from context instead:
33
+
34
+ ```tsx
35
+ <GrytThemeProvider followSystemAppearance>
36
+ <Surface level="raised" bordered padding={4}>
37
+ <Chip label="Live" tone="success" />
38
+ </Surface>
39
+ </GrytThemeProvider>
40
+ ```
41
+
42
+ Rendered outside a provider, components get the dark theme rather than nothing,
43
+ which matches `@gryt/ui` shipping its dark tokens on `:root`.
44
+
45
+ ## Parity exceptions
46
+
47
+ Kept from the first component rather than the first argument. Full 1:1 is the
48
+ goal, so the honest list is worth more than a claim of parity that isn't one.
49
+
50
+ | Component | What differs | Why |
51
+ |---|---|---|
52
+ | `Spinner` | Uses the platform `ActivityIndicator` instead of the web's stroked, CSS-animated circle | It is what the OS draws for "working", it honours reduce-motion for free, and iOS and Android differ from each other deliberately. Reproducing the web drawing would look wrong on both. |
53
+ | *(all)* | No hover state | A phone has no pointer. The web's `scale-[1.03]` on hover has no equivalent and is deliberately not emulated on press, which would fire on every tap and make the whole UI feel loose. |
54
+ | `Button` | Press scale is kept, hover scale is dropped | The web scales to `0.96` on press and `1.03` on hover. Press maps directly onto `onPressIn`/`onPressOut`, so it is here, spring-animated and skipped when reduce-motion is on. |
55
+ | `Button` | `hasPopup` is a prop, not inferred | The web reads `aria-haspopup`, which Base UI sets on a trigger, and skips the scale so the trigger does not drag its own popup sideways while it is being measured. React Native has no such attribute, so a menu trigger has to say so. |
56
+ | `TextField` | Focus changes the border colour instead of drawing an outline | The web's `outline` sits outside the box and shifts nothing. React Native has no outline, and a second ring would move the layout on focus. |
57
+ | `Progress` | Indeterminate renders as an empty track | The web sweeps a partial bar with a CSS keyframe. Doing it here needs an `Animated` loop plus a reduce-motion check, which deserves its own pass rather than a footnote. |
58
+ | `Tooltip` | Opens on long press, closes on release, instead of appearing on hover after a delay | A phone has no hover. This is the same name on a different interaction, and it is the component where 1:1 is least achievable. An interface that needs tooltips to be usable will not survive the port. |
59
+ | *(positioned overlays)* | The popup does not follow a trigger that moves | Floating UI keeps watching the reference element. React Native measures on demand and reports nothing afterwards, so the position is taken once, when the popup opens. If a list scrolls underneath an open menu, the menu stays put. |
60
+ | `Popover` | `Arrow` renders nothing | Base UI positions an arrow against the popup's edge once Floating UI has settled. Here it means a rotated square, a border on two sides only, and knowing which side the popup landed on. Doable, not free, and not done. |
61
+ | `Menu` | `Positioner` is a passthrough | Base UI splits the positioned box from the styled box. There is no stacking context to escape here, so one view does both. |
62
+ | `Dialog` | `Portal` is a passthrough and `Backdrop` renders nothing | React Native's `Modal` already renders above everything and draws its own layer, so there is nothing for a portal to escape and nowhere for a separate backdrop to go. Both are kept so call sites match. |
63
+ | `Dialog` | Android's back button stands in for Escape | It is the only hardware dismiss a phone has. `AlertDialog` disables it along with the scrim, matching Base UI suppressing outside-press and Escape. |
64
+ | `Switch` | Drawn from tokens, not React Native's `Switch` | The platform control is green on iOS and Material on Android, and `thumbColor`/`trackColor` do not reach the whole shape. A design system that cannot colour its own switch is not one. |
65
+ | `Select` | A list, not the platform picker | iOS gives a wheel and Android a dialog. Neither takes the Gryt palette and they look nothing like each other. |
66
+ | `Checkbox` | The tick is a text glyph, not a Phosphor icon | An icon set would be this package's first runtime dependency, for one glyph, and `@phosphor-icons/react` renders SVG that React Native cannot mount without `react-native-svg`. That is a decision for whoever needs icons, not for a checkbox. |
67
+ | `Tabs` | Each tab draws its own underline; `Indicator` renders nothing | Base UI slides one indicator between tabs, measured against the active one. Sliding it here means measuring every tab and animating between them, which is real work for a decoration. The underline appears rather than slides. |
68
+ | `Tabs` | The panel has no `tabpanel` role | React Native has `tab` and `tablist` and no `tabpanel`. The panel is reachable, it just does not announce itself as belonging to the tab. |
69
+ | `Collapsible` | Unmounts when closed instead of animating height | The web animates height, which needs the content measured first. Doable with `onLayout` and `Animated`, at the cost of a frame at the wrong size. |
70
+ | `Alert` | Severity is colour plus a live region, with no icon | Colour alone does not tell anyone this is an error, which is why the web pairs it with an icon. `assertive` for error and warning, `polite` otherwise, reaches a screen reader, which an icon does not. A visible icon still wants an icon set. |
71
+ | `NumberField` | No scrub gesture on the label | The web lets you press the label and drag sideways to change the value. On a phone that competes with scrolling, and the same interaction already exists as `Slider`. |
72
+ | `ScrollArea` | Nearly a passthrough over `ScrollView` | The web version exists to replace scrollbars browsers draw badly. A phone's indicator is drawn by the OS, fades on its own, and already matches every other app. |
73
+ | `Toolbar` | No roving focus | The web's manages arrow-key movement between controls and one tab stop for the group. There is no focus to rove. The role is kept, since that is what tells a screen reader the controls belong together. |
74
+ | `Accordion` | Two glyphs instead of a rotating chevron | Same reasoning as the checkbox tick: rotating one needs a transform and a measurement, and there is no icon set here yet. |
75
+ | `Skeleton` | Does not pulse | A loop running for the length of a request costs battery, and it is the sort of motion reduce-motion users switch off first. A flat block still reads as loading. |
76
+
77
+ ## Not Node-importable
78
+
79
+ The build is unbundled `tsc` output with extensionless relative imports, which
80
+ is what Metro wants: it resolves `.native.ts` and `.web.ts` per file, and a
81
+ bundle would have no files left to pick between. That also means plain Node
82
+ cannot import `dist/index.js` — it would fail on `react-native` regardless, so
83
+ nothing is lost, but do not add a Node-side consumer expecting it to work.
84
+
85
+ The theme layer is testable without a native runtime, which is what
86
+ `createNativeTheme.test.ts` covers.
@@ -0,0 +1,35 @@
1
+ import { type ReactNode } from "react";
2
+ import { type StyleProp, type ViewStyle } from "react-native";
3
+ type ItemValue = string | number;
4
+ export interface AccordionRootProps {
5
+ /** `single` closes the others when one opens. */
6
+ type?: "single" | "multiple";
7
+ value?: ItemValue[];
8
+ defaultValue?: ItemValue[];
9
+ onValueChange?: (value: ItemValue[]) => void;
10
+ children?: ReactNode;
11
+ style?: StyleProp<ViewStyle>;
12
+ }
13
+ declare function Root({ type, value: controlled, defaultValue, onValueChange, children, style, }: AccordionRootProps): import("react").JSX.Element;
14
+ export interface AccordionItemProps {
15
+ value: ItemValue;
16
+ children?: ReactNode;
17
+ style?: StyleProp<ViewStyle>;
18
+ }
19
+ declare function Item({ value, children, style }: AccordionItemProps): import("react").JSX.Element;
20
+ declare function Trigger({ children, style }: {
21
+ children?: ReactNode;
22
+ style?: StyleProp<ViewStyle>;
23
+ }): import("react").JSX.Element;
24
+ declare function Panel({ children, style }: {
25
+ children?: ReactNode;
26
+ style?: StyleProp<ViewStyle>;
27
+ }): import("react").JSX.Element | null;
28
+ export declare const Accordion: {
29
+ Root: typeof Root;
30
+ Item: typeof Item;
31
+ Trigger: typeof Trigger;
32
+ Panel: typeof Panel;
33
+ };
34
+ export {};
35
+ //# sourceMappingURL=Accordion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Accordion.d.ts","sourceRoot":"","sources":["../../../src/components/Accordion/Accordion.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5E,OAAO,EAAyB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAIrF,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAsBjC,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC7B,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC;IAC3B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,iBAAS,IAAI,CAAC,EACZ,IAAe,EACf,KAAK,EAAE,UAAU,EACjB,YAAiB,EACjB,aAAa,EACb,QAAQ,EACR,KAAK,GACN,EAAE,kBAAkB,+BAoBpB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,iBAAS,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,kBAAkB,+BAc3D;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,+BAkC3F;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,sCAMzF;AAED,eAAO,MAAM,SAAS;;;;;CAAiC,CAAC"}
@@ -0,0 +1,66 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { createContext, useContext, useState } from "react";
3
+ import { Pressable, Text, View } from "react-native";
4
+ import { useTheme } from "../../theme/index.js";
5
+ const AccordionContext = createContext(null);
6
+ const ItemContext = createContext(null);
7
+ function useAccordion(part) {
8
+ const value = useContext(AccordionContext);
9
+ if (!value)
10
+ throw new Error(`Accordion.${part} must be rendered inside Accordion.Root.`);
11
+ return value;
12
+ }
13
+ function useItem(part) {
14
+ const value = useContext(ItemContext);
15
+ if (value === null)
16
+ throw new Error(`Accordion.${part} must be rendered inside Accordion.Item.`);
17
+ return value;
18
+ }
19
+ function Root({ type = "single", value: controlled, defaultValue = [], onValueChange, children, style, }) {
20
+ const [uncontrolled, setUncontrolled] = useState(defaultValue);
21
+ const open = controlled ?? uncontrolled;
22
+ const toggle = (item) => {
23
+ const isOpen = open.includes(item);
24
+ const next = isOpen
25
+ ? open.filter((v) => v !== item)
26
+ : type === "single"
27
+ ? [item]
28
+ : [...open, item];
29
+ if (controlled === undefined)
30
+ setUncontrolled(next);
31
+ onValueChange?.(next);
32
+ };
33
+ return (_jsx(AccordionContext.Provider, { value: { open, toggle }, children: _jsx(View, { style: style, children: children }) }));
34
+ }
35
+ function Item({ value, children, style }) {
36
+ const theme = useTheme();
37
+ return (_jsx(ItemContext.Provider, { value: value, children: _jsx(View, { style: [
38
+ { borderBottomWidth: 1, borderBottomColor: theme.color.border },
39
+ style,
40
+ ], children: children }) }));
41
+ }
42
+ function Trigger({ children, style }) {
43
+ const theme = useTheme();
44
+ const { open, toggle } = useAccordion("Trigger");
45
+ const value = useItem("Trigger");
46
+ const isOpen = open.includes(value);
47
+ return (_jsxs(Pressable, { accessibilityRole: "button", accessibilityState: { expanded: isOpen }, onPress: () => toggle(value), style: [
48
+ {
49
+ flexDirection: "row",
50
+ alignItems: "center",
51
+ justifyContent: "space-between",
52
+ gap: theme.space(3),
53
+ paddingVertical: theme.space(3.5),
54
+ },
55
+ style,
56
+ ], children: [typeof children === "string" ? (_jsx(Text, { style: { color: theme.color.text, fontSize: 15, fontWeight: "500", flex: 1 }, children: children })) : (_jsx(View, { style: { flex: 1 }, children: children })), _jsx(Text, { style: { color: theme.color.muted, fontSize: 11 }, children: isOpen ? "▾" : "▸" })] }));
57
+ }
58
+ function Panel({ children, style }) {
59
+ const theme = useTheme();
60
+ const { open } = useAccordion("Panel");
61
+ const value = useItem("Panel");
62
+ if (!open.includes(value))
63
+ return null;
64
+ return _jsx(View, { style: [{ paddingBottom: theme.space(3.5) }, style], children: children });
65
+ }
66
+ export const Accordion = { Root, Item, Trigger, Panel };
@@ -0,0 +1,21 @@
1
+ import type { ReactNode } from "react";
2
+ import { type StyleProp, type ViewStyle } from "react-native";
3
+ export type AlertSeverity = "info" | "success" | "warning" | "error";
4
+ export interface AlertProps {
5
+ severity?: AlertSeverity;
6
+ title?: ReactNode;
7
+ children?: ReactNode;
8
+ style?: StyleProp<ViewStyle>;
9
+ }
10
+ /**
11
+ * Severity is carried by colour and by the accessibility role, not by an icon.
12
+ *
13
+ * Colour alone is not enough to tell somebody an alert is an error, which is why
14
+ * the web pairs it with an icon. The equivalent here is the live region: an
15
+ * `error` or `warning` announces itself as an assertive alert, the other two
16
+ * politely. That reaches a screen reader, which an icon does not.
17
+ *
18
+ * A visible icon still wants an icon set. Noted in the exceptions table.
19
+ */
20
+ export declare function Alert({ severity, title, children, style }: AlertProps): import("react").JSX.Element;
21
+ //# sourceMappingURL=Alert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Alert.d.ts","sourceRoot":"","sources":["../../../src/components/Alert/Alert.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,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAErE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AASD;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,EAAE,QAAiB,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,UAAU,+BAmC9E"}
@@ -0,0 +1,35 @@
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
+ const RAMP = {
5
+ info: "accent",
6
+ success: "success",
7
+ warning: "warning",
8
+ error: "danger",
9
+ };
10
+ /**
11
+ * Severity is carried by colour and by the accessibility role, not by an icon.
12
+ *
13
+ * Colour alone is not enough to tell somebody an alert is an error, which is why
14
+ * the web pairs it with an icon. The equivalent here is the live region: an
15
+ * `error` or `warning` announces itself as an assertive alert, the other two
16
+ * politely. That reaches a screen reader, which an icon does not.
17
+ *
18
+ * A visible icon still wants an icon set. Noted in the exceptions table.
19
+ */
20
+ export function Alert({ severity = "info", title, children, style }) {
21
+ const theme = useTheme();
22
+ const ramp = theme.scales[RAMP[severity]];
23
+ const urgent = severity === "error" || severity === "warning";
24
+ return (_jsxs(View, { accessibilityRole: "alert", accessibilityLiveRegion: urgent ? "assertive" : "polite", style: [
25
+ {
26
+ borderRadius: theme.radius.md,
27
+ borderWidth: 1,
28
+ borderColor: ramp[6],
29
+ backgroundColor: ramp[2],
30
+ padding: theme.space(3),
31
+ gap: theme.space(1),
32
+ },
33
+ style,
34
+ ], children: [title ? (typeof title === "string" ? (_jsx(Text, { style: { color: ramp[11], fontSize: 14, fontWeight: "600" }, children: title })) : (title)) : null, typeof children === "string" ? (_jsx(Text, { style: { color: ramp[10], fontSize: 13, lineHeight: 18 }, children: children })) : (children)] }));
35
+ }
@@ -0,0 +1,38 @@
1
+ import { type DialogPopupProps, type DialogRootProps } from "../Dialog/Dialog.js";
2
+ /**
3
+ * A dialog that will not go away until it is answered.
4
+ *
5
+ * The only difference from Dialog, on the web and here: no dismissal by
6
+ * accident. Base UI drops the outside-press and Escape handling; this drops the
7
+ * outside-press and the Android back button, which are the two ways a phone
8
+ * offers to leave without choosing.
9
+ *
10
+ * There is no Footer here, matching the web, which does not export one for
11
+ * AlertDialog either.
12
+ */
13
+ export type AlertDialogPopupProps = Omit<DialogPopupProps, "dismissible">;
14
+ declare function Popup(props: AlertDialogPopupProps): import("react").JSX.Element;
15
+ export type AlertDialogRootProps = DialogRootProps;
16
+ export declare const AlertDialog: {
17
+ Root: ({ children, ...openProps }: DialogRootProps) => import("react").JSX.Element;
18
+ Trigger: ({ children, style }: import("../../index.js").DialogTriggerProps) => import("react").JSX.Element;
19
+ Portal: ({ children }: {
20
+ children?: import("react").ReactNode;
21
+ }) => import("react").JSX.Element;
22
+ Backdrop: () => null;
23
+ Popup: typeof Popup;
24
+ Title: ({ children, style }: {
25
+ children?: import("react").ReactNode;
26
+ style?: import("react-native").StyleProp<import("react-native").TextStyle>;
27
+ }) => import("react").JSX.Element;
28
+ Description: ({ children, style }: {
29
+ children?: import("react").ReactNode;
30
+ style?: import("react-native").StyleProp<import("react-native").TextStyle>;
31
+ }) => import("react").JSX.Element;
32
+ Close: ({ children, style }: {
33
+ children?: import("react").ReactNode;
34
+ style?: import("react-native").StyleProp<import("react-native").ViewStyle>;
35
+ }) => import("react").JSX.Element;
36
+ };
37
+ export {};
38
+ //# sourceMappingURL=AlertDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AlertDialog.d.ts","sourceRoot":"","sources":["../../../src/components/AlertDialog/AlertDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEvF;;;;;;;;;;GAUG;AAIH,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;AAE1E,iBAAS,KAAK,CAAC,KAAK,EAAE,qBAAqB,+BAE1C;AAED,MAAM,MAAM,oBAAoB,GAAG,eAAe,CAAC;AAEnD,eAAO,MAAM,WAAW;;;;gBAUw0B,CAAC;;;;;gBAAy/E,CAAC;aAAkB,CAAC;;;gBAAiS,CAAC;aAAkB,CAAC;;;gBAAkiB,CAAC;aAAkB,CAAC;;CADxtI,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Dialog } from "../Dialog/Dialog.js";
3
+ function Popup(props) {
4
+ return _jsx(Dialog.Popup, { ...props, dismissible: false });
5
+ }
6
+ export const AlertDialog = {
7
+ Root: Dialog.Root,
8
+ Trigger: Dialog.Trigger,
9
+ Portal: Dialog.Portal,
10
+ Backdrop: Dialog.Backdrop,
11
+ Popup,
12
+ Title: Dialog.Title,
13
+ Description: Dialog.Description,
14
+ Close: Dialog.Close,
15
+ };
@@ -0,0 +1,10 @@
1
+ import { type StyleProp, type ViewStyle } from "react-native";
2
+ export type AvatarSize = "xs" | "sm" | "md" | "lg" | "xl";
3
+ export interface AvatarProps {
4
+ name?: string;
5
+ source?: string;
6
+ size?: AvatarSize;
7
+ style?: StyleProp<ViewStyle>;
8
+ }
9
+ export declare function Avatar({ name, source, size, style }: AvatarProps): import("react").JSX.Element;
10
+ //# sourceMappingURL=Avatar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../../../src/components/Avatar/Avatar.tsx"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAIjF,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAU1D,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAuBD,wBAAgB,MAAM,CAAC,EAAE,IAAS,EAAE,MAAM,EAAE,IAAW,EAAE,KAAK,EAAE,EAAE,WAAW,+BA+C5E"}
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { Image, Text, View } from "react-native";
4
+ import { useTheme } from "../../theme/index.js";
5
+ const SIZES = {
6
+ xs: 20,
7
+ sm: 28,
8
+ md: 36,
9
+ lg: 48,
10
+ xl: 64,
11
+ };
12
+ /**
13
+ * Picks a ramp from the name, so the same person is the same colour everywhere.
14
+ *
15
+ * Deliberately the same arithmetic as the web: sum the code units and take the
16
+ * remainder. Anything cleverer would give a different colour on each platform
17
+ * for the same person, which is the one outcome to avoid.
18
+ */
19
+ function toneFor(name) {
20
+ const tones = ["accent", "secondary", "success", "danger", "warning"];
21
+ let sum = 0;
22
+ for (let i = 0; i < name.length; i += 1)
23
+ sum += name.charCodeAt(i);
24
+ return tones[sum % tones.length];
25
+ }
26
+ function initials(name) {
27
+ const parts = name.trim().split(/\s+/).filter(Boolean);
28
+ if (parts.length === 0)
29
+ return "?";
30
+ if (parts.length === 1)
31
+ return parts[0].slice(0, 2).toUpperCase();
32
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
33
+ }
34
+ export function Avatar({ name = "", source, size = "md", style }) {
35
+ const theme = useTheme();
36
+ const [failed, setFailed] = useState(false);
37
+ const px = SIZES[size];
38
+ const ramp = theme.scales[toneFor(name)];
39
+ const frame = {
40
+ width: px,
41
+ height: px,
42
+ borderRadius: px / 2,
43
+ alignItems: "center",
44
+ justifyContent: "center",
45
+ overflow: "hidden",
46
+ };
47
+ // A broken image URL falls back to initials rather than to a blank circle.
48
+ // The web gets this from <img onerror>; here it is onError plus state.
49
+ if (source && !failed) {
50
+ return (_jsx(View, { style: [frame, { backgroundColor: theme.color.surfaceRaised }, style], children: _jsx(Image, { source: { uri: source }, onError: () => setFailed(true), accessibilityLabel: name || undefined, style: { width: px, height: px } }) }));
51
+ }
52
+ return (_jsx(View, { accessibilityRole: "image", accessibilityLabel: name || undefined, style: [frame, { backgroundColor: ramp[2] }, style], children: _jsx(Text, { style: {
53
+ color: ramp[10],
54
+ fontSize: Math.round(px * 0.4),
55
+ fontWeight: "600",
56
+ }, children: initials(name) }) }));
57
+ }
@@ -0,0 +1,13 @@
1
+ import { type StyleProp, type ViewStyle } from "react-native";
2
+ export type BadgeTone = "neutral" | "accent" | "secondary" | "success" | "danger" | "warning";
3
+ export interface BadgeProps {
4
+ /** Rendered as a count. Values above `max` show as "max+". */
5
+ count?: number;
6
+ max?: number;
7
+ /** A dot with no number, for "something happened" without saying how much. */
8
+ dot?: boolean;
9
+ tone?: BadgeTone;
10
+ style?: StyleProp<ViewStyle>;
11
+ }
12
+ export declare function Badge({ count, max, dot, tone, style, }: BadgeProps): import("react").JSX.Element | null;
13
+ //# sourceMappingURL=Badge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../../src/components/Badge/Badge.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAI1E,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9F,MAAM,WAAW,UAAU;IACzB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,wBAAgB,KAAK,CAAC,EACpB,KAAK,EACL,GAAQ,EACR,GAAW,EACX,IAAe,EACf,KAAK,GACN,EAAE,UAAU,sCA0CZ"}
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Text, View } from "react-native";
3
+ import { useTheme } from "../../theme/index.js";
4
+ export function Badge({ count, max = 99, dot = false, tone = "danger", style, }) {
5
+ const theme = useTheme();
6
+ const ramp = theme.scales[tone];
7
+ if (dot) {
8
+ return (_jsx(View, { accessibilityRole: "image", accessibilityLabel: "Unread", style: [
9
+ { width: 8, height: 8, borderRadius: 4, backgroundColor: ramp[8] },
10
+ style,
11
+ ] }));
12
+ }
13
+ if (count === undefined || count <= 0)
14
+ return null;
15
+ const label = count > max ? `${max}+` : String(count);
16
+ return (_jsx(View, { accessibilityRole: "text", accessibilityLabel: `${count} unread`, style: [
17
+ {
18
+ minWidth: 18,
19
+ height: 18,
20
+ paddingHorizontal: 5,
21
+ borderRadius: 9,
22
+ alignItems: "center",
23
+ justifyContent: "center",
24
+ backgroundColor: ramp[8],
25
+ },
26
+ style,
27
+ ], children: _jsx(Text, { style: { color: theme.color.bg, fontSize: 11, fontWeight: "700" }, children: label }) }));
28
+ }
@@ -0,0 +1,24 @@
1
+ import { type ReactNode } from "react";
2
+ import { type PressableProps, type StyleProp, type ViewStyle } from "react-native";
3
+ export type ButtonTone = "primary" | "secondary" | "neutral" | "danger" | "ghost";
4
+ export type ButtonSize = "xsmall" | "small" | "medium" | "large";
5
+ export interface ButtonProps extends Omit<PressableProps, "style" | "children"> {
6
+ children?: ReactNode;
7
+ tone?: ButtonTone;
8
+ size?: ButtonSize;
9
+ startIcon?: ReactNode;
10
+ endIcon?: ReactNode;
11
+ style?: StyleProp<ViewStyle>;
12
+ /**
13
+ * This button opens something.
14
+ *
15
+ * The web reads `aria-haspopup`, which Base UI sets on a trigger, and skips
16
+ * the press scale for it: the popup is positioned against the trigger's
17
+ * measured box and keeps measuring while open, so a trigger that changes size
18
+ * drags its own menu sideways. React Native has no equivalent attribute, so
19
+ * the caller says it.
20
+ */
21
+ hasPopup?: boolean;
22
+ }
23
+ export declare function Button({ children, tone, size, startIcon, endIcon, style, hasPopup, disabled, ...rest }: ButtonProps): import("react").JSX.Element;
24
+ //# sourceMappingURL=Button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../src/components/Button/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAKtB,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAClF,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AASjE,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,UAAU,CAAC;IAC7E,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAKD,wBAAgB,MAAM,CAAC,EACrB,QAAQ,EACR,IAAgB,EAChB,IAAe,EACf,SAAS,EACT,OAAO,EACP,KAAK,EACL,QAAgB,EAChB,QAAQ,EACR,GAAG,IAAI,EACR,EAAE,WAAW,+BAgFb"}
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { Animated, Pressable, Text, View, } from "react-native";
4
+ import { useReducedMotion } from "../../hooks/useReducedMotion.js";
5
+ import { useTheme } from "../../theme/index.js";
6
+ const SIZES = {
7
+ xsmall: { minHeight: 32, paddingH: 12, fontSize: 12 },
8
+ small: { minHeight: 36, paddingH: 16, fontSize: 14 },
9
+ medium: { minHeight: 40, paddingH: 20, fontSize: 14 },
10
+ large: { minHeight: 48, paddingH: 24, fontSize: 16 },
11
+ };
12
+ const PRESSED_SCALE = 0.96;
13
+ const SPRING = { useNativeDriver: true, speed: 40, bounciness: 6 };
14
+ export function Button({ children, tone = "primary", size = "medium", startIcon, endIcon, style, hasPopup = false, disabled, ...rest }) {
15
+ const theme = useTheme();
16
+ const reducedMotion = useReducedMotion();
17
+ // useState with an initialiser rather than useRef(new Animated.Value(1)):
18
+ // reading .current during render is a lint error, and the useRef form also
19
+ // constructs a throwaway Animated.Value on every render.
20
+ const [scale] = useState(() => new Animated.Value(1));
21
+ const metrics = SIZES[size];
22
+ const animate = !reducedMotion && !hasPopup && !disabled;
23
+ const background = tone === "primary"
24
+ ? theme.color.accent
25
+ : tone === "secondary"
26
+ ? theme.color.secondary
27
+ : tone === "neutral"
28
+ ? theme.color.surfaceRaised
29
+ : tone === "danger"
30
+ ? theme.color.danger
31
+ : "transparent";
32
+ const foreground = tone === "primary"
33
+ ? theme.color.onAccent
34
+ : tone === "secondary"
35
+ ? theme.color.onSecondary
36
+ : tone === "danger"
37
+ ? theme.color.onDanger
38
+ : tone === "ghost"
39
+ ? theme.color.muted
40
+ : theme.color.text;
41
+ const springTo = (value) => {
42
+ if (!animate)
43
+ return;
44
+ Animated.spring(scale, { toValue: value, ...SPRING }).start();
45
+ };
46
+ return (_jsx(Animated.View, { style: [{ transform: [{ scale }] }, style], children: _jsxs(Pressable, { accessibilityRole: "button", accessibilityState: { disabled: !!disabled }, disabled: disabled, onPressIn: () => springTo(PRESSED_SCALE), onPressOut: () => springTo(1), style: {
47
+ minHeight: metrics.minHeight,
48
+ paddingHorizontal: metrics.paddingH,
49
+ flexDirection: "row",
50
+ alignItems: "center",
51
+ justifyContent: "center",
52
+ gap: theme.space(2),
53
+ borderRadius: theme.radius.full,
54
+ backgroundColor: background,
55
+ opacity: disabled ? 0.5 : 1,
56
+ }, ...rest, children: [startIcon, typeof children === "string" ? (_jsx(Text, { numberOfLines: 1, style: {
57
+ color: foreground,
58
+ fontSize: metrics.fontSize,
59
+ fontWeight: "600",
60
+ }, children: children })) : (_jsx(View, { style: { flexDirection: "row", alignItems: "center" }, children: children })), endIcon] }) }));
61
+ }
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from "react";
2
+ import { type StyleProp, type TextStyle, type ViewStyle } from "react-native";
3
+ export interface CardProps {
4
+ children?: ReactNode;
5
+ style?: StyleProp<ViewStyle>;
6
+ }
7
+ export declare function Card({ children, style }: CardProps): import("react").JSX.Element;
8
+ export interface CardHeaderProps {
9
+ title?: ReactNode;
10
+ subtitle?: ReactNode;
11
+ /** Sits at the end of the header row: an avatar, a menu, a badge. */
12
+ action?: ReactNode;
13
+ style?: StyleProp<ViewStyle>;
14
+ titleStyle?: StyleProp<TextStyle>;
15
+ }
16
+ export declare function CardHeader({ title, subtitle, action, style, titleStyle }: CardHeaderProps): import("react").JSX.Element;
17
+ export declare function CardContent({ children, style }: CardProps): import("react").JSX.Element;
18
+ export declare function CardActions({ children, style }: CardProps): import("react").JSX.Element;
19
+ //# sourceMappingURL=Card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../../src/components/Card/Card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAc,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAI1F,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,wBAAgB,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,SAAS,+BAkBlD;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,qEAAqE;IACrE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACnC;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,eAAe,+BAkCzF;AAED,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,SAAS,+BAOzD;AAED,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,SAAS,+BAkBzD"}
@@ -0,0 +1,45 @@
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
+ export function Card({ children, style }) {
5
+ const theme = useTheme();
6
+ return (_jsx(View, { style: [
7
+ {
8
+ backgroundColor: theme.color.surface,
9
+ borderRadius: theme.radius.lg,
10
+ borderWidth: 1,
11
+ borderColor: theme.color.border,
12
+ overflow: "hidden",
13
+ },
14
+ style,
15
+ ], children: children }));
16
+ }
17
+ export function CardHeader({ title, subtitle, action, style, titleStyle }) {
18
+ const theme = useTheme();
19
+ return (_jsxs(View, { style: [
20
+ {
21
+ flexDirection: "row",
22
+ alignItems: "center",
23
+ gap: theme.space(3),
24
+ padding: theme.space(4),
25
+ },
26
+ style,
27
+ ], children: [_jsxs(View, { style: { flex: 1, gap: theme.space(0.5) }, children: [typeof title === "string" ? (_jsx(Text, { accessibilityRole: "header", style: [{ color: theme.color.text, fontSize: 16, fontWeight: "600" }, titleStyle], children: title })) : (title), typeof subtitle === "string" ? (_jsx(Text, { style: { color: theme.color.muted, fontSize: 13 }, children: subtitle })) : (subtitle)] }), action] }));
28
+ }
29
+ export function CardContent({ children, style }) {
30
+ const theme = useTheme();
31
+ return (_jsx(View, { style: [{ paddingHorizontal: theme.space(4), paddingBottom: theme.space(4), gap: theme.space(2) }, style], children: children }));
32
+ }
33
+ export function CardActions({ children, style }) {
34
+ const theme = useTheme();
35
+ return (_jsx(View, { style: [
36
+ {
37
+ flexDirection: "row",
38
+ justifyContent: "flex-end",
39
+ gap: theme.space(2),
40
+ paddingHorizontal: theme.space(4),
41
+ paddingBottom: theme.space(4),
42
+ },
43
+ style,
44
+ ], children: children }));
45
+ }
@@ -0,0 +1,24 @@
1
+ import { type StyleProp, type ViewStyle } from "react-native";
2
+ export type CheckboxTone = "primary" | "secondary" | "neutral" | "danger";
3
+ export interface CheckboxProps {
4
+ checked?: boolean;
5
+ defaultChecked?: boolean;
6
+ /** Base UI's third state, for a parent of partly-checked children. */
7
+ indeterminate?: boolean;
8
+ onCheckedChange?: (checked: boolean) => void;
9
+ disabled?: boolean;
10
+ tone?: CheckboxTone;
11
+ style?: StyleProp<ViewStyle>;
12
+ accessibilityLabel?: string;
13
+ }
14
+ /**
15
+ * The tick is a text glyph rather than an icon.
16
+ *
17
+ * `@gryt/ui` renders a Phosphor `Check`. Pulling an icon library in here would
18
+ * be the first runtime dependency this package has, for one glyph, and
19
+ * `@phosphor-icons/react` renders SVG that React Native cannot mount without
20
+ * react-native-svg on top. Both are decisions for whoever needs a full icon set,
21
+ * not for a checkbox.
22
+ */
23
+ export declare function Checkbox({ checked: controlled, defaultChecked, indeterminate, onCheckedChange, disabled, tone, style, accessibilityLabel, }: CheckboxProps): import("react").JSX.Element;
24
+ //# sourceMappingURL=Checkbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../../src/components/Checkbox/Checkbox.tsx"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAI/E,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE1E,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sEAAsE;IACtE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAID;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EAAE,UAAU,EACnB,cAAsB,EACtB,aAAqB,EACrB,eAAe,EACf,QAAQ,EACR,IAAgB,EAChB,KAAK,EACL,kBAAkB,GACnB,EAAE,aAAa,+BA2Cf"}