@delacour/react-native-ui 0.1.0-alpha.3

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 (273) hide show
  1. package/README.md +113 -0
  2. package/package.json +125 -0
  3. package/src/components/accordion/AGENTS.md +192 -0
  4. package/src/components/accordion/accordion-content.tsx +125 -0
  5. package/src/components/accordion/accordion-description.tsx +17 -0
  6. package/src/components/accordion/accordion-indicator.tsx +104 -0
  7. package/src/components/accordion/accordion-item.tsx +78 -0
  8. package/src/components/accordion/accordion-title.tsx +18 -0
  9. package/src/components/accordion/accordion-trigger.tsx +148 -0
  10. package/src/components/accordion/accordion.context.tsx +153 -0
  11. package/src/components/accordion/accordion.tsx +253 -0
  12. package/src/components/accordion/accordion.types.ts +11 -0
  13. package/src/components/accordion/accordion.variants.test.ts +435 -0
  14. package/src/components/accordion/accordion.variants.ts +358 -0
  15. package/src/components/accordion/index.ts +45 -0
  16. package/src/components/badge/AGENTS.md +83 -0
  17. package/src/components/badge/badge-close-button.tsx +48 -0
  18. package/src/components/badge/badge-end-content.tsx +15 -0
  19. package/src/components/badge/badge-label.tsx +24 -0
  20. package/src/components/badge/badge-start-content.tsx +16 -0
  21. package/src/components/badge/badge.context.tsx +64 -0
  22. package/src/components/badge/badge.tsx +192 -0
  23. package/src/components/badge/badge.types.ts +10 -0
  24. package/src/components/badge/badge.variants.test.ts +269 -0
  25. package/src/components/badge/badge.variants.ts +238 -0
  26. package/src/components/badge/index.ts +17 -0
  27. package/src/components/bottom-sheet/AGENTS.md +232 -0
  28. package/src/components/bottom-sheet/bottom-sheet-background.tsx +35 -0
  29. package/src/components/bottom-sheet/bottom-sheet-close.tsx +60 -0
  30. package/src/components/bottom-sheet/bottom-sheet-container.tsx +245 -0
  31. package/src/components/bottom-sheet/bottom-sheet-content.tsx +66 -0
  32. package/src/components/bottom-sheet/bottom-sheet-description.tsx +29 -0
  33. package/src/components/bottom-sheet/bottom-sheet-footer.tsx +148 -0
  34. package/src/components/bottom-sheet/bottom-sheet-handle.tsx +47 -0
  35. package/src/components/bottom-sheet/bottom-sheet-overlay.tsx +180 -0
  36. package/src/components/bottom-sheet/bottom-sheet-portal.tsx +72 -0
  37. package/src/components/bottom-sheet/bottom-sheet-scroll-view.tsx +95 -0
  38. package/src/components/bottom-sheet/bottom-sheet-title.tsx +24 -0
  39. package/src/components/bottom-sheet/bottom-sheet-trigger.tsx +73 -0
  40. package/src/components/bottom-sheet/bottom-sheet.context.tsx +135 -0
  41. package/src/components/bottom-sheet/bottom-sheet.tsx +125 -0
  42. package/src/components/bottom-sheet/bottom-sheet.variants.test.ts +276 -0
  43. package/src/components/bottom-sheet/bottom-sheet.variants.ts +199 -0
  44. package/src/components/bottom-sheet/index.ts +40 -0
  45. package/src/components/bottom-sheet/use-bottom-sheet-input.ts +125 -0
  46. package/src/components/button/AGENTS.md +217 -0
  47. package/src/components/button/button-end-content.tsx +15 -0
  48. package/src/components/button/button-group-separator.tsx +37 -0
  49. package/src/components/button/button-group-text.tsx +101 -0
  50. package/src/components/button/button-group.tsx +158 -0
  51. package/src/components/button/button-label.tsx +24 -0
  52. package/src/components/button/button-start-content.tsx +15 -0
  53. package/src/components/button/button.context.tsx +187 -0
  54. package/src/components/button/button.tsx +273 -0
  55. package/src/components/button/button.types.ts +10 -0
  56. package/src/components/button/button.variants.test.ts +811 -0
  57. package/src/components/button/button.variants.ts +480 -0
  58. package/src/components/button/index.ts +50 -0
  59. package/src/components/chart/AGENTS.md +210 -0
  60. package/src/components/chart/chart-area.tsx +93 -0
  61. package/src/components/chart/chart-bar.tsx +103 -0
  62. package/src/components/chart/chart-bars.tsx +75 -0
  63. package/src/components/chart/chart-candlestick.tsx +60 -0
  64. package/src/components/chart/chart-grid.tsx +33 -0
  65. package/src/components/chart/chart-legend.tsx +41 -0
  66. package/src/components/chart/chart-line.tsx +50 -0
  67. package/src/components/chart/chart-scatter.tsx +35 -0
  68. package/src/components/chart/chart-tooltip-dot.tsx +70 -0
  69. package/src/components/chart/chart-tooltip-x.tsx +121 -0
  70. package/src/components/chart/chart-tooltip-y.tsx +55 -0
  71. package/src/components/chart/chart-tooltip.tsx +133 -0
  72. package/src/components/chart/chart-x-axis.tsx +27 -0
  73. package/src/components/chart/chart-y-axis.tsx +21 -0
  74. package/src/components/chart/chart.context.tsx +127 -0
  75. package/src/components/chart/chart.tsx +448 -0
  76. package/src/components/chart/chart.types.ts +108 -0
  77. package/src/components/chart/chart.variants.test.ts +893 -0
  78. package/src/components/chart/chart.variants.ts +726 -0
  79. package/src/components/chart/index.ts +103 -0
  80. package/src/components/chart/pie-chart-center.tsx +40 -0
  81. package/src/components/chart/pie-chart-label.tsx +56 -0
  82. package/src/components/chart/pie-chart-slice.tsx +45 -0
  83. package/src/components/chart/pie-chart-tooltip.tsx +85 -0
  84. package/src/components/chart/pie-chart.context.tsx +77 -0
  85. package/src/components/chart/pie-chart.tsx +247 -0
  86. package/src/components/chart/use-chart-font.ts +25 -0
  87. package/src/components/chart/use-chart-palette.ts +38 -0
  88. package/src/components/checkbox/AGENTS.md +185 -0
  89. package/src/components/checkbox/checkbox-box.tsx +152 -0
  90. package/src/components/checkbox/checkbox-group.tsx +107 -0
  91. package/src/components/checkbox/checkbox-label.tsx +34 -0
  92. package/src/components/checkbox/checkbox.context.tsx +136 -0
  93. package/src/components/checkbox/checkbox.tsx +258 -0
  94. package/src/components/checkbox/checkbox.types.ts +14 -0
  95. package/src/components/checkbox/checkbox.variants.test.ts +634 -0
  96. package/src/components/checkbox/checkbox.variants.ts +484 -0
  97. package/src/components/checkbox/index.ts +42 -0
  98. package/src/components/field/AGENTS.md +100 -0
  99. package/src/components/field/field-content.tsx +24 -0
  100. package/src/components/field/field-description.tsx +29 -0
  101. package/src/components/field/field-error.tsx +61 -0
  102. package/src/components/field/field-group.tsx +22 -0
  103. package/src/components/field/field-label.tsx +33 -0
  104. package/src/components/field/field-legend.tsx +34 -0
  105. package/src/components/field/field-separator.tsx +81 -0
  106. package/src/components/field/field-set.tsx +22 -0
  107. package/src/components/field/field.context.tsx +78 -0
  108. package/src/components/field/field.tsx +144 -0
  109. package/src/components/field/field.types.ts +15 -0
  110. package/src/components/field/field.variants.test.ts +216 -0
  111. package/src/components/field/field.variants.ts +136 -0
  112. package/src/components/field/index.ts +21 -0
  113. package/src/components/icon/AGENTS.md +104 -0
  114. package/src/components/icon/icon.context.tsx +29 -0
  115. package/src/components/icon/icon.tsx +92 -0
  116. package/src/components/icon/icon.variants.test.ts +111 -0
  117. package/src/components/icon/icon.variants.ts +79 -0
  118. package/src/components/icon/index.ts +11 -0
  119. package/src/components/input/AGENTS.md +126 -0
  120. package/src/components/input/index.ts +27 -0
  121. package/src/components/input/input-group-decorator.tsx +84 -0
  122. package/src/components/input/input-group-prefix.tsx +16 -0
  123. package/src/components/input/input-group-suffix.tsx +15 -0
  124. package/src/components/input/input-group.tsx +140 -0
  125. package/src/components/input/input.context.tsx +87 -0
  126. package/src/components/input/input.tsx +184 -0
  127. package/src/components/input/input.types.ts +10 -0
  128. package/src/components/input/input.variants.test.ts +369 -0
  129. package/src/components/input/input.variants.ts +329 -0
  130. package/src/components/list-group/AGENTS.md +67 -0
  131. package/src/components/list-group/index.ts +20 -0
  132. package/src/components/list-group/list-group-item-content.tsx +10 -0
  133. package/src/components/list-group/list-group-item-description.tsx +12 -0
  134. package/src/components/list-group/list-group-item-prefix.tsx +30 -0
  135. package/src/components/list-group/list-group-item-suffix.tsx +55 -0
  136. package/src/components/list-group/list-group-item-title.tsx +17 -0
  137. package/src/components/list-group/list-group-item.tsx +87 -0
  138. package/src/components/list-group/list-group.context.tsx +65 -0
  139. package/src/components/list-group/list-group.tsx +116 -0
  140. package/src/components/list-group/list-group.types.ts +14 -0
  141. package/src/components/list-group/list-group.variants.test.ts +223 -0
  142. package/src/components/list-group/list-group.variants.ts +107 -0
  143. package/src/components/pressable/AGENTS.md +64 -0
  144. package/src/components/pressable/index.ts +9 -0
  145. package/src/components/pressable/pressable.tsx +256 -0
  146. package/src/components/pressable/pressable.variants.test.ts +99 -0
  147. package/src/components/pressable/pressable.variants.ts +63 -0
  148. package/src/components/provider/AGENTS.md +90 -0
  149. package/src/components/provider/index.ts +1 -0
  150. package/src/components/provider/provider.tsx +105 -0
  151. package/src/components/radio/AGENTS.md +247 -0
  152. package/src/components/radio/index.ts +35 -0
  153. package/src/components/radio/radio-group.tsx +126 -0
  154. package/src/components/radio/radio-indicator.tsx +91 -0
  155. package/src/components/radio/radio-label.tsx +39 -0
  156. package/src/components/radio/radio.context.tsx +132 -0
  157. package/src/components/radio/radio.tsx +215 -0
  158. package/src/components/radio/radio.variants.test.ts +581 -0
  159. package/src/components/radio/radio.variants.ts +272 -0
  160. package/src/components/screen/AGENTS.md +287 -0
  161. package/src/components/screen/index.ts +85 -0
  162. package/src/components/screen/screen-chat-list.tsx +485 -0
  163. package/src/components/screen/screen-content.tsx +69 -0
  164. package/src/components/screen/screen-debug.ts +43 -0
  165. package/src/components/screen/screen-error.tsx +61 -0
  166. package/src/components/screen/screen-flat-list.tsx +88 -0
  167. package/src/components/screen/screen-footer-background.tsx +60 -0
  168. package/src/components/screen/screen-footer.tsx +170 -0
  169. package/src/components/screen/screen-header.tsx +36 -0
  170. package/src/components/screen/screen-legend-list.tsx +105 -0
  171. package/src/components/screen/screen-list-component.tsx +23 -0
  172. package/src/components/screen/screen-loading.tsx +50 -0
  173. package/src/components/screen/screen-navbar-back-button.tsx +79 -0
  174. package/src/components/screen/screen-navbar-background.tsx +45 -0
  175. package/src/components/screen/screen-navbar-subtitle.tsx +26 -0
  176. package/src/components/screen/screen-navbar-title.tsx +31 -0
  177. package/src/components/screen/screen-navbar.tsx +154 -0
  178. package/src/components/screen/screen-root.tsx +38 -0
  179. package/src/components/screen/screen-scroll-area.tsx +116 -0
  180. package/src/components/screen/screen-scroll-shadow.tsx +186 -0
  181. package/src/components/screen/screen-section-list.tsx +81 -0
  182. package/src/components/screen/screen-view.tsx +59 -0
  183. package/src/components/screen/screen.context.tsx +167 -0
  184. package/src/components/screen/screen.tsx +114 -0
  185. package/src/components/screen/screen.types.ts +66 -0
  186. package/src/components/screen/screen.variants.test.ts +602 -0
  187. package/src/components/screen/screen.variants.ts +475 -0
  188. package/src/components/screen/use-screen-scroll-insets.ts +218 -0
  189. package/src/components/separator/AGENTS.md +30 -0
  190. package/src/components/separator/index.ts +8 -0
  191. package/src/components/separator/separator.tsx +85 -0
  192. package/src/components/slider/AGENTS.md +274 -0
  193. package/src/components/slider/index.ts +48 -0
  194. package/src/components/slider/slider-fill.tsx +68 -0
  195. package/src/components/slider/slider-output.tsx +55 -0
  196. package/src/components/slider/slider-thumb.tsx +161 -0
  197. package/src/components/slider/slider-track.tsx +233 -0
  198. package/src/components/slider/slider.context.tsx +118 -0
  199. package/src/components/slider/slider.tsx +321 -0
  200. package/src/components/slider/slider.types.ts +26 -0
  201. package/src/components/slider/slider.variants.test.ts +804 -0
  202. package/src/components/slider/slider.variants.ts +607 -0
  203. package/src/components/spinner/AGENTS.md +73 -0
  204. package/src/components/spinner/index.ts +17 -0
  205. package/src/components/spinner/spinner-arc.tsx +90 -0
  206. package/src/components/spinner/spinner-content.tsx +63 -0
  207. package/src/components/spinner/spinner.context.tsx +52 -0
  208. package/src/components/spinner/spinner.tsx +128 -0
  209. package/src/components/spinner/spinner.variants.test.ts +273 -0
  210. package/src/components/spinner/spinner.variants.ts +187 -0
  211. package/src/components/switch/AGENTS.md +213 -0
  212. package/src/components/switch/index.ts +42 -0
  213. package/src/components/switch/switch-content.tsx +111 -0
  214. package/src/components/switch/switch-end-content.tsx +18 -0
  215. package/src/components/switch/switch-start-content.tsx +20 -0
  216. package/src/components/switch/switch-thumb.tsx +102 -0
  217. package/src/components/switch/switch.context.tsx +79 -0
  218. package/src/components/switch/switch.tsx +423 -0
  219. package/src/components/switch/switch.types.ts +14 -0
  220. package/src/components/switch/switch.variants.test.ts +570 -0
  221. package/src/components/switch/switch.variants.ts +511 -0
  222. package/src/components/tabs/AGENTS.md +286 -0
  223. package/src/components/tabs/index.ts +69 -0
  224. package/src/components/tabs/tabs-content.tsx +65 -0
  225. package/src/components/tabs/tabs-indicator.tsx +97 -0
  226. package/src/components/tabs/tabs-label.tsx +62 -0
  227. package/src/components/tabs/tabs-list.tsx +139 -0
  228. package/src/components/tabs/tabs-pager.tsx +59 -0
  229. package/src/components/tabs/tabs-scroll-view.tsx +171 -0
  230. package/src/components/tabs/tabs-separator.tsx +73 -0
  231. package/src/components/tabs/tabs-trigger.tsx +210 -0
  232. package/src/components/tabs/tabs.context.tsx +294 -0
  233. package/src/components/tabs/tabs.tsx +435 -0
  234. package/src/components/tabs/tabs.types.ts +13 -0
  235. package/src/components/tabs/tabs.variants.test.ts +1020 -0
  236. package/src/components/tabs/tabs.variants.ts +672 -0
  237. package/src/components/text/AGENTS.md +95 -0
  238. package/src/components/text/index.ts +25 -0
  239. package/src/components/text/text.context.tsx +60 -0
  240. package/src/components/text/text.tsx +251 -0
  241. package/src/components/text/text.variants.test.ts +422 -0
  242. package/src/components/text/text.variants.ts +282 -0
  243. package/src/display-name.test.ts +145 -0
  244. package/src/docs.test.ts +98 -0
  245. package/src/expo/navigation-theme.tsx +58 -0
  246. package/src/hooks/use-controllable-state.ts +45 -0
  247. package/src/hooks/use-keyboard-state-sync.tsx +147 -0
  248. package/src/hooks/use-navigation-theme.ts +78 -0
  249. package/src/hooks/use-theme-color.ts +44 -0
  250. package/src/icons/central.ts +1 -0
  251. package/src/lib/cn.test.ts +136 -0
  252. package/src/lib/cn.ts +29 -0
  253. package/src/lib/color.test.ts +80 -0
  254. package/src/lib/color.ts +79 -0
  255. package/src/lib/compose-refs.test.ts +64 -0
  256. package/src/lib/compose-refs.ts +37 -0
  257. package/src/lib/keyboard-animation.test.ts +33 -0
  258. package/src/lib/keyboard-animation.ts +28 -0
  259. package/src/lib/merge-props.test.ts +82 -0
  260. package/src/lib/merge-props.ts +47 -0
  261. package/src/lib/navigation-theme.test.ts +49 -0
  262. package/src/lib/navigation-theme.ts +53 -0
  263. package/src/lib/slot.tsx +45 -0
  264. package/src/lib/tv.ts +20 -0
  265. package/src/styles/base.css +2 -0
  266. package/src/styles/geometry.test.ts +117 -0
  267. package/src/styles/index.css +3 -0
  268. package/src/styles/theme-tokens.test.ts +252 -0
  269. package/src/styles/theme.css +447 -0
  270. package/src/styles/tokens.css +132 -0
  271. package/src/styles/tokens.test.ts +181 -0
  272. package/src/styles/tokens.ts +86 -0
  273. package/src/uniwind-env.d.ts +1 -0
@@ -0,0 +1,253 @@
1
+ import { Children, isValidElement, type ReactElement, type ReactNode, useCallback, useMemo, useRef } from "react";
2
+ import { View, type ViewProps } from "react-native";
3
+ import { useControllableState } from "../../hooks/use-controllable-state";
4
+ import { Separator } from "../separator";
5
+ import { type AccordionContextValue, AccordionProvider } from "./accordion.context";
6
+ import {
7
+ ACCORDION_DEFAULT_SIZE,
8
+ ACCORDION_DEFAULT_VARIANT,
9
+ type AccordionSelection,
10
+ type AccordionSelectionMode,
11
+ type AccordionSize,
12
+ type AccordionVariant,
13
+ accordionVariants,
14
+ toExpandedList,
15
+ toggleExpandedValue,
16
+ } from "./accordion.variants";
17
+ import { AccordionContent } from "./accordion-content";
18
+ import { AccordionDescription } from "./accordion-description";
19
+ import { AccordionIndicator } from "./accordion-indicator";
20
+ import { AccordionItem } from "./accordion-item";
21
+ import { AccordionTitle } from "./accordion-title";
22
+ import { AccordionTrigger } from "./accordion-trigger";
23
+
24
+ export type AccordionBaseProps = Omit<ViewProps, "children"> & {
25
+ variant?: AccordionVariant;
26
+ size?: AccordionSize;
27
+ /** Draw a divider between adjacent items. On by default. */
28
+ isDivided?: boolean;
29
+ /** Blocks every trigger and fades every row. An item's own prop still overrides it. */
30
+ isDisabled?: boolean;
31
+ /**
32
+ * Whether the last open item may be closed. On by default.
33
+ *
34
+ * It bounds the *set*, never a single item: in `multiple` mode an item still
35
+ * closes while another is open, and only the last one is refused.
36
+ */
37
+ isCollapsible?: boolean;
38
+ className?: string;
39
+ children?: ReactNode;
40
+ };
41
+
42
+ export type AccordionProps = AccordionBaseProps & AccordionSelection;
43
+
44
+ type AccordionBodyProps = AccordionBaseProps & {
45
+ selectionMode?: AccordionSelectionMode;
46
+ expanded?: readonly string[];
47
+ defaultExpanded: readonly string[];
48
+ onExpandedChange?: (expanded: readonly string[]) => void;
49
+ };
50
+
51
+ /**
52
+ * Everything the accordion does, over one internal list of open values.
53
+ *
54
+ * Split from the root so the root can be nothing but a narrow on `selectionMode`.
55
+ * `useControllableState` cannot be called conditionally, so the two shapes a
56
+ * caller can hold — a string or an array — are converted to this one list at the
57
+ * boundary and back again on the way out. Two hooks would be two pieces of state
58
+ * for one answer.
59
+ */
60
+ function AccordionBody({
61
+ selectionMode = "single",
62
+ variant = ACCORDION_DEFAULT_VARIANT,
63
+ size = ACCORDION_DEFAULT_SIZE,
64
+ isDivided = true,
65
+ isDisabled = false,
66
+ isCollapsible = true,
67
+ expanded: expandedProp,
68
+ defaultExpanded,
69
+ onExpandedChange,
70
+ className,
71
+ children,
72
+ ...props
73
+ }: AccordionBodyProps): ReactElement {
74
+ // A ref-backed trampoline, so the accordion's own context does not change
75
+ // identity whenever the caller passes a fresh arrow — which would re-render
76
+ // every item on every render of the screen above it. `Switch` registers its
77
+ // toggle with a `Field` the same way, for the same reason.
78
+ const changeRef = useRef(onExpandedChange);
79
+ changeRef.current = onExpandedChange;
80
+ const handleChange = useCallback((next: readonly string[]) => changeRef.current?.(next), []);
81
+
82
+ const [expanded, setExpanded] = useControllableState<readonly string[]>({
83
+ defaultValue: defaultExpanded,
84
+ onChange: handleChange,
85
+ value: expandedProp,
86
+ });
87
+
88
+ // Read by `toggle` rather than closed over, so one stable callback serves the
89
+ // accordion for its whole life. A `toggle` that changed on every expansion
90
+ // would be a new context value on every expansion, and therefore a re-render
91
+ // of every item rather than of the one that moved.
92
+ const expandedRef = useRef(expanded);
93
+ expandedRef.current = expanded;
94
+
95
+ const toggle = useCallback(
96
+ (value: string) => {
97
+ const next = toggleExpandedValue({ expanded: expandedRef.current, isCollapsible, selectionMode, value });
98
+ // Refused, by identity. A tap the rules reject must not re-render and
99
+ // must not report an `onValueChange` for a change that did not happen.
100
+ if (next === expandedRef.current) return;
101
+ setExpanded(next);
102
+ },
103
+ [isCollapsible, selectionMode, setExpanded]
104
+ );
105
+
106
+ const context = useMemo<AccordionContextValue>(
107
+ () => ({ expanded, isDisabled, size, toggle, variant }),
108
+ [expanded, isDisabled, size, toggle, variant]
109
+ );
110
+
111
+ const slots = accordionVariants({ size, variant });
112
+ const dividerClassName = slots.divider();
113
+
114
+ const content = useMemo(
115
+ () => (isDivided ? withDividers(children, dividerClassName) : children),
116
+ [children, dividerClassName, isDivided]
117
+ );
118
+
119
+ return (
120
+ <AccordionProvider value={context}>
121
+ <View className={slots.root({ className })} {...props}>
122
+ {content}
123
+ </View>
124
+ </AccordionProvider>
125
+ );
126
+ }
127
+ AccordionBody.displayName = "DelacourUI.Accordion.Body";
128
+
129
+ /**
130
+ * Inserts a divider between adjacent items.
131
+ *
132
+ * A pair is skipped when either side already is a `Separator`, so a hand-placed
133
+ * divider is never doubled. `Children.toArray` drops the nulls and booleans a
134
+ * conditional child leaves behind, so an item rendered only some of the time does
135
+ * not strand a divider where nothing follows it — which counting indices against
136
+ * the raw children cannot do.
137
+ */
138
+ function withDividers(children: ReactNode, dividerClassName: string): ReactNode {
139
+ const items = Children.toArray(children);
140
+ const output: ReactNode[] = [];
141
+
142
+ for (const [index, child] of items.entries()) {
143
+ if (index > 0 && !isSeparator(items[index - 1]) && !isSeparator(child)) {
144
+ output.push(<Separator className={dividerClassName} key={`divider-${index}`} />);
145
+ }
146
+ output.push(child);
147
+ }
148
+
149
+ return output;
150
+ }
151
+
152
+ function isSeparator(node: ReactNode): boolean {
153
+ return isValidElement(node) && node.type === Separator;
154
+ }
155
+
156
+ function AccordionRoot(props: AccordionProps): ReactElement {
157
+ // The one place the union is narrowed, and it happens before anything is
158
+ // destructured — which is what keeps the two shapes correlated. Destructure
159
+ // first and `onValueChange` widens to a union of two functions that cannot be
160
+ // called, which is what forces a cast or a `@ts-expect-error` on every other
161
+ // implementation of this component.
162
+ if (props.selectionMode === "multiple") {
163
+ const { value, defaultValue, onValueChange, ...rest } = props;
164
+ return (
165
+ <AccordionBody
166
+ {...rest}
167
+ defaultExpanded={toExpandedList(defaultValue)}
168
+ expanded={value === undefined ? undefined : toExpandedList(value)}
169
+ onExpandedChange={(next) => onValueChange?.([...next])}
170
+ />
171
+ );
172
+ }
173
+
174
+ const { value, defaultValue, onValueChange, ...rest } = props;
175
+ return (
176
+ <AccordionBody
177
+ {...rest}
178
+ defaultExpanded={toExpandedList(defaultValue)}
179
+ expanded={value === undefined ? undefined : toExpandedList(value)}
180
+ onExpandedChange={(next) => onValueChange?.(next[0] ?? null)}
181
+ />
182
+ );
183
+ }
184
+
185
+ /**
186
+ * A set of rows that each disclose a panel.
187
+ *
188
+ * `selectionMode` decides how many open at once: `single` (the default) keeps one
189
+ * open and reports a `string | null`, `multiple` keeps any number and reports a
190
+ * `string[]`. The two are a real discriminated union, so an array `defaultValue`
191
+ * on a single accordion is a compile error rather than a runtime surprise.
192
+ *
193
+ * **A panel's height is measured and animated**, so the animation lives on the
194
+ * panel's own node and whatever sits below simply follows it — no sibling on the
195
+ * screen has to opt into anything. Its
196
+ * height, its fade and the indicator's rotation all run off one shared value per
197
+ * item, so they cannot drift out of step by a frame, and a tap mid-travel reverses
198
+ * the spring rather than restarting it.
199
+ *
200
+ * **A panel mounts on first expand and stays mounted**, so a form inside one keeps
201
+ * what was typed, a list keeps where it was scrolled, and a video keeps playing
202
+ * position across every later collapse.
203
+ *
204
+ * Dividers are inserted between adjacent items rather than written out at every
205
+ * call site, inset to line up with the triggers' own padding. A `Separator` placed
206
+ * by hand is respected — no divider is added on either side of it — so a caller can
207
+ * control one gap without turning the feature off. `isDivided={false}` turns it off.
208
+ *
209
+ * `isCollapsible={false}` keeps one item open at all times. It bounds the set and
210
+ * never a single item, so in `multiple` mode a row still closes while another is
211
+ * open and only the last one is refused.
212
+ *
213
+ * **State works either way from one hook**: pass `value` to control it, or nothing
214
+ * and let it hold its own.
215
+ *
216
+ * @example
217
+ * <Accordion>
218
+ * <Accordion.Item value="shipping">
219
+ * <Accordion.Trigger>
220
+ * <Accordion.Title>Shipping</Accordion.Title>
221
+ * <Accordion.Description>2–5 business days</Accordion.Description>
222
+ * </Accordion.Trigger>
223
+ * <Accordion.Content>
224
+ * <Text.Paragraph>Tracked, and signed for over $200.</Text.Paragraph>
225
+ * </Accordion.Content>
226
+ * </Accordion.Item>
227
+ * </Accordion>
228
+ *
229
+ * @example
230
+ * <Accordion onValueChange={setOpen} selectionMode="multiple" value={open}>
231
+ * {sections.map((section) => (
232
+ * <Accordion.Item key={section.id} value={section.id}>
233
+ * <Accordion.Trigger>{section.title}</Accordion.Trigger>
234
+ * <Accordion.Content>{section.body}</Accordion.Content>
235
+ * </Accordion.Item>
236
+ * ))}
237
+ * </Accordion>
238
+ */
239
+ export const Accordion = Object.assign(AccordionRoot, {
240
+ /** One section: a trigger, and the panel it discloses. */
241
+ Item: AccordionItem,
242
+ /** The row that opens the item. A `Pressable`, so it inherits the whole vocabulary. */
243
+ Trigger: AccordionTrigger,
244
+ /** The trigger's primary line. Bare string children become one automatically. */
245
+ Title: AccordionTitle,
246
+ /** The trigger's secondary line, stacked under the title. */
247
+ Description: AccordionDescription,
248
+ /** The glyph that turns as the panel opens. Composed in when a trigger holds none. */
249
+ Indicator: AccordionIndicator,
250
+ /** The measured, clipped panel. Mounts on first expand and stays mounted. */
251
+ Content: AccordionContent,
252
+ displayName: "DelacourUI.Accordion",
253
+ });
@@ -0,0 +1,11 @@
1
+ import type { TextProps } from "react-native";
2
+
3
+ /**
4
+ * The shape of a trigger's two text lines, `Title` and `Description`.
5
+ *
6
+ * Shared by both parts, and by the root's trigger, which wraps bare string
7
+ * children in a `Accordion.Title` — three modules, so it lives in a leaf rather
8
+ * than in one of them arbitrarily. `ListGroupTextProps` is the same type for the
9
+ * same reason.
10
+ */
11
+ export type AccordionTextProps = TextProps & { className?: string };