panelui-native 0.22.6 → 0.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/README.md +1 -0
  2. package/lib/module/components/accordion/index.js +8 -8
  3. package/lib/module/components/accordion/index.js.map +1 -1
  4. package/lib/module/components/alert/index.js +2 -2
  5. package/lib/module/components/alert/index.js.map +1 -1
  6. package/lib/module/components/area-chart/index.js +958 -0
  7. package/lib/module/components/area-chart/index.js.map +1 -0
  8. package/lib/module/components/attachment/index.js +2 -1
  9. package/lib/module/components/attachment/index.js.map +1 -1
  10. package/lib/module/components/avatar/index.js +4 -4
  11. package/lib/module/components/avatar/index.js.map +1 -1
  12. package/lib/module/components/badge/index.js +4 -4
  13. package/lib/module/components/badge/index.js.map +1 -1
  14. package/lib/module/components/bar-chart/index.js +994 -0
  15. package/lib/module/components/bar-chart/index.js.map +1 -0
  16. package/lib/module/components/bottom-sheet/index.js +213 -24
  17. package/lib/module/components/bottom-sheet/index.js.map +1 -1
  18. package/lib/module/components/breadcrumb/index.js +275 -0
  19. package/lib/module/components/breadcrumb/index.js.map +1 -0
  20. package/lib/module/components/button/index.js +11 -6
  21. package/lib/module/components/button/index.js.map +1 -1
  22. package/lib/module/components/calendar/index.js +557 -0
  23. package/lib/module/components/calendar/index.js.map +1 -0
  24. package/lib/module/components/carousel/index.js +680 -0
  25. package/lib/module/components/carousel/index.js.map +1 -0
  26. package/lib/module/components/chip/index.js +6 -6
  27. package/lib/module/components/chip/index.js.map +1 -1
  28. package/lib/module/components/date-picker/index.js +158 -0
  29. package/lib/module/components/date-picker/index.js.map +1 -0
  30. package/lib/module/components/dialog/index.js +7 -2
  31. package/lib/module/components/dialog/index.js.map +1 -1
  32. package/lib/module/components/direction/index.js +15 -20
  33. package/lib/module/components/direction/index.js.map +1 -1
  34. package/lib/module/components/empty-state/index.js +2 -2
  35. package/lib/module/components/empty-state/index.js.map +1 -1
  36. package/lib/module/components/field/index.js +5 -5
  37. package/lib/module/components/field/index.js.map +1 -1
  38. package/lib/module/components/flow/index.js +3 -3
  39. package/lib/module/components/flow/index.js.map +1 -1
  40. package/lib/module/components/frame/index.js +22 -5
  41. package/lib/module/components/frame/index.js.map +1 -1
  42. package/lib/module/components/heatmap-chart/index.js +28 -8
  43. package/lib/module/components/heatmap-chart/index.js.map +1 -1
  44. package/lib/module/components/input-group/index.js +5 -4
  45. package/lib/module/components/input-group/index.js.map +1 -1
  46. package/lib/module/components/item/index.js +9 -9
  47. package/lib/module/components/item/index.js.map +1 -1
  48. package/lib/module/components/label/index.js +4 -4
  49. package/lib/module/components/label/index.js.map +1 -1
  50. package/lib/module/components/line-chart/index.js +165 -184
  51. package/lib/module/components/line-chart/index.js.map +1 -1
  52. package/lib/module/components/loader/index.js +758 -0
  53. package/lib/module/components/loader/index.js.map +1 -0
  54. package/lib/module/components/map/basemap.js +265 -0
  55. package/lib/module/components/map/basemap.js.map +1 -0
  56. package/lib/module/components/map/index.js +850 -0
  57. package/lib/module/components/map/index.js.map +1 -0
  58. package/lib/module/components/map/maplibre.js +87 -0
  59. package/lib/module/components/map/maplibre.js.map +1 -0
  60. package/lib/module/components/marker/index.js +3 -3
  61. package/lib/module/components/marker/index.js.map +1 -1
  62. package/lib/module/components/message/index.js +15 -14
  63. package/lib/module/components/message/index.js.map +1 -1
  64. package/lib/module/components/message-scroller/index.js +5 -4
  65. package/lib/module/components/message-scroller/index.js.map +1 -1
  66. package/lib/module/components/otp-input/index.js +2 -2
  67. package/lib/module/components/popover/index.js +39 -7
  68. package/lib/module/components/popover/index.js.map +1 -1
  69. package/lib/module/components/progress/index.js +8 -2
  70. package/lib/module/components/progress/index.js.map +1 -1
  71. package/lib/module/components/radio-group/index.js +2 -2
  72. package/lib/module/components/radio-group/index.js.map +1 -1
  73. package/lib/module/components/ring-chart/index.js +423 -0
  74. package/lib/module/components/ring-chart/index.js.map +1 -0
  75. package/lib/module/components/section-rail/index.js +6 -6
  76. package/lib/module/components/section-rail/index.js.map +1 -1
  77. package/lib/module/components/select/index.js +10 -4
  78. package/lib/module/components/select/index.js.map +1 -1
  79. package/lib/module/components/separator/index.js +76 -22
  80. package/lib/module/components/separator/index.js.map +1 -1
  81. package/lib/module/components/shimmer/index.js +10 -1
  82. package/lib/module/components/shimmer/index.js.map +1 -1
  83. package/lib/module/components/slider/index.js +19 -5
  84. package/lib/module/components/slider/index.js.map +1 -1
  85. package/lib/module/components/steps/index.js +5 -5
  86. package/lib/module/components/steps/index.js.map +1 -1
  87. package/lib/module/components/switch/index.js +6 -1
  88. package/lib/module/components/switch/index.js.map +1 -1
  89. package/lib/module/components/tabs/index.js +7 -7
  90. package/lib/module/components/tabs/index.js.map +1 -1
  91. package/lib/module/components/timeline/index.js +4 -4
  92. package/lib/module/components/timeline/index.js.map +1 -1
  93. package/lib/module/components/toast/index.js +2 -2
  94. package/lib/module/components/toast/index.js.map +1 -1
  95. package/lib/module/components/toggle-button/index.js +5 -5
  96. package/lib/module/components/toggle-button/index.js.map +1 -1
  97. package/lib/module/components/tooltip/index.js +492 -0
  98. package/lib/module/components/tooltip/index.js.map +1 -0
  99. package/lib/module/components/typography/index.js +7 -2
  100. package/lib/module/components/typography/index.js.map +1 -1
  101. package/lib/module/hooks/index.js +1 -0
  102. package/lib/module/hooks/index.js.map +1 -1
  103. package/lib/module/hooks/use-back-handler.js +37 -0
  104. package/lib/module/hooks/use-back-handler.js.map +1 -0
  105. package/lib/module/hooks/use-direction.js +43 -0
  106. package/lib/module/hooks/use-direction.js.map +1 -0
  107. package/lib/module/icons/index.js +180 -2
  108. package/lib/module/icons/index.js.map +1 -1
  109. package/lib/module/index.js +12 -2
  110. package/lib/module/index.js.map +1 -1
  111. package/lib/module/primitives/keyboard-avoider.js +2 -1
  112. package/lib/module/primitives/keyboard-avoider.js.map +1 -1
  113. package/lib/module/primitives/text.js +55 -11
  114. package/lib/module/primitives/text.js.map +1 -1
  115. package/lib/module/utils/chart.js +279 -0
  116. package/lib/module/utils/chart.js.map +1 -0
  117. package/lib/module/utils/date.js +386 -0
  118. package/lib/module/utils/date.js.map +1 -0
  119. package/lib/typescript/src/components/accordion/index.d.ts.map +1 -1
  120. package/lib/typescript/src/components/alert/index.d.ts.map +1 -1
  121. package/lib/typescript/src/components/area-chart/index.d.ts +237 -0
  122. package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -0
  123. package/lib/typescript/src/components/attachment/index.d.ts.map +1 -1
  124. package/lib/typescript/src/components/bar-chart/index.d.ts +270 -0
  125. package/lib/typescript/src/components/bar-chart/index.d.ts.map +1 -0
  126. package/lib/typescript/src/components/bottom-sheet/index.d.ts +75 -4
  127. package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
  128. package/lib/typescript/src/components/breadcrumb/index.d.ts +100 -0
  129. package/lib/typescript/src/components/breadcrumb/index.d.ts.map +1 -0
  130. package/lib/typescript/src/components/button/index.d.ts.map +1 -1
  131. package/lib/typescript/src/components/calendar/index.d.ts +108 -0
  132. package/lib/typescript/src/components/calendar/index.d.ts.map +1 -0
  133. package/lib/typescript/src/components/carousel/index.d.ts +179 -0
  134. package/lib/typescript/src/components/carousel/index.d.ts.map +1 -0
  135. package/lib/typescript/src/components/chip/index.d.ts.map +1 -1
  136. package/lib/typescript/src/components/date-picker/index.d.ts +91 -0
  137. package/lib/typescript/src/components/date-picker/index.d.ts.map +1 -0
  138. package/lib/typescript/src/components/dialog/index.d.ts.map +1 -1
  139. package/lib/typescript/src/components/direction/index.d.ts +11 -10
  140. package/lib/typescript/src/components/direction/index.d.ts.map +1 -1
  141. package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -1
  142. package/lib/typescript/src/components/field/index.d.ts.map +1 -1
  143. package/lib/typescript/src/components/frame/index.d.ts +17 -0
  144. package/lib/typescript/src/components/frame/index.d.ts.map +1 -1
  145. package/lib/typescript/src/components/heatmap-chart/index.d.ts +22 -3
  146. package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
  147. package/lib/typescript/src/components/input-group/index.d.ts.map +1 -1
  148. package/lib/typescript/src/components/item/index.d.ts.map +1 -1
  149. package/lib/typescript/src/components/label/index.d.ts.map +1 -1
  150. package/lib/typescript/src/components/line-chart/index.d.ts +42 -2
  151. package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
  152. package/lib/typescript/src/components/loader/index.d.ts +38 -0
  153. package/lib/typescript/src/components/loader/index.d.ts.map +1 -0
  154. package/lib/typescript/src/components/map/basemap.d.ts +76 -0
  155. package/lib/typescript/src/components/map/basemap.d.ts.map +1 -0
  156. package/lib/typescript/src/components/map/index.d.ts +367 -0
  157. package/lib/typescript/src/components/map/index.d.ts.map +1 -0
  158. package/lib/typescript/src/components/map/maplibre.d.ts +193 -0
  159. package/lib/typescript/src/components/map/maplibre.d.ts.map +1 -0
  160. package/lib/typescript/src/components/marker/index.d.ts.map +1 -1
  161. package/lib/typescript/src/components/message/index.d.ts.map +1 -1
  162. package/lib/typescript/src/components/message-scroller/index.d.ts.map +1 -1
  163. package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
  164. package/lib/typescript/src/components/progress/index.d.ts.map +1 -1
  165. package/lib/typescript/src/components/ring-chart/index.d.ts +156 -0
  166. package/lib/typescript/src/components/ring-chart/index.d.ts.map +1 -0
  167. package/lib/typescript/src/components/select/index.d.ts.map +1 -1
  168. package/lib/typescript/src/components/separator/index.d.ts +36 -1
  169. package/lib/typescript/src/components/separator/index.d.ts.map +1 -1
  170. package/lib/typescript/src/components/shimmer/index.d.ts.map +1 -1
  171. package/lib/typescript/src/components/slider/index.d.ts.map +1 -1
  172. package/lib/typescript/src/components/steps/index.d.ts.map +1 -1
  173. package/lib/typescript/src/components/switch/index.d.ts.map +1 -1
  174. package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
  175. package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
  176. package/lib/typescript/src/components/toast/index.d.ts.map +1 -1
  177. package/lib/typescript/src/components/toggle-button/index.d.ts.map +1 -1
  178. package/lib/typescript/src/components/tooltip/index.d.ts +125 -0
  179. package/lib/typescript/src/components/tooltip/index.d.ts.map +1 -0
  180. package/lib/typescript/src/components/typography/index.d.ts +6 -0
  181. package/lib/typescript/src/components/typography/index.d.ts.map +1 -1
  182. package/lib/typescript/src/hooks/index.d.ts +1 -0
  183. package/lib/typescript/src/hooks/index.d.ts.map +1 -1
  184. package/lib/typescript/src/hooks/use-back-handler.d.ts +19 -0
  185. package/lib/typescript/src/hooks/use-back-handler.d.ts.map +1 -0
  186. package/lib/typescript/src/hooks/use-direction.d.ts +20 -0
  187. package/lib/typescript/src/hooks/use-direction.d.ts.map +1 -0
  188. package/lib/typescript/src/icons/index.d.ts +8 -0
  189. package/lib/typescript/src/icons/index.d.ts.map +1 -1
  190. package/lib/typescript/src/index.d.ts +14 -4
  191. package/lib/typescript/src/index.d.ts.map +1 -1
  192. package/lib/typescript/src/primitives/keyboard-avoider.d.ts.map +1 -1
  193. package/lib/typescript/src/primitives/text.d.ts +20 -0
  194. package/lib/typescript/src/primitives/text.d.ts.map +1 -1
  195. package/lib/typescript/src/utils/chart.d.ts +96 -0
  196. package/lib/typescript/src/utils/chart.d.ts.map +1 -0
  197. package/lib/typescript/src/utils/date.d.ts +116 -0
  198. package/lib/typescript/src/utils/date.d.ts.map +1 -0
  199. package/package.json +9 -1
  200. package/src/components/accordion/index.tsx +8 -12
  201. package/src/components/alert/index.tsx +2 -2
  202. package/src/components/area-chart/index.tsx +1147 -0
  203. package/src/components/attachment/index.tsx +2 -2
  204. package/src/components/avatar/index.tsx +4 -4
  205. package/src/components/badge/index.tsx +6 -6
  206. package/src/components/bar-chart/index.tsx +1208 -0
  207. package/src/components/bottom-sheet/index.tsx +282 -21
  208. package/src/components/breadcrumb/index.tsx +363 -0
  209. package/src/components/button/index.tsx +12 -8
  210. package/src/components/calendar/index.tsx +703 -0
  211. package/src/components/carousel/index.tsx +788 -0
  212. package/src/components/chip/index.tsx +6 -8
  213. package/src/components/date-picker/index.tsx +237 -0
  214. package/src/components/dialog/index.tsx +7 -2
  215. package/src/components/direction/index.tsx +22 -22
  216. package/src/components/empty-state/index.tsx +2 -2
  217. package/src/components/field/index.tsx +3 -5
  218. package/src/components/flow/index.tsx +3 -3
  219. package/src/components/frame/index.tsx +22 -7
  220. package/src/components/heatmap-chart/index.tsx +58 -11
  221. package/src/components/input-group/index.tsx +5 -4
  222. package/src/components/item/index.tsx +9 -9
  223. package/src/components/label/index.tsx +2 -2
  224. package/src/components/line-chart/index.tsx +194 -223
  225. package/src/components/loader/index.tsx +812 -0
  226. package/src/components/map/basemap.ts +304 -0
  227. package/src/components/map/index.tsx +1116 -0
  228. package/src/components/map/maplibre.ts +201 -0
  229. package/src/components/marker/index.tsx +3 -3
  230. package/src/components/message/index.tsx +15 -14
  231. package/src/components/message-scroller/index.tsx +5 -4
  232. package/src/components/otp-input/index.tsx +2 -2
  233. package/src/components/popover/index.tsx +43 -7
  234. package/src/components/progress/index.tsx +10 -2
  235. package/src/components/radio-group/index.tsx +2 -2
  236. package/src/components/ring-chart/index.tsx +543 -0
  237. package/src/components/section-rail/index.tsx +6 -6
  238. package/src/components/select/index.tsx +10 -4
  239. package/src/components/separator/index.tsx +77 -19
  240. package/src/components/shimmer/index.tsx +10 -1
  241. package/src/components/slider/index.tsx +20 -5
  242. package/src/components/steps/index.tsx +5 -5
  243. package/src/components/switch/index.tsx +6 -1
  244. package/src/components/tabs/index.tsx +7 -9
  245. package/src/components/timeline/index.tsx +4 -4
  246. package/src/components/toast/index.tsx +2 -2
  247. package/src/components/toggle-button/index.tsx +5 -7
  248. package/src/components/tooltip/index.tsx +610 -0
  249. package/src/components/typography/index.tsx +7 -2
  250. package/src/hooks/index.ts +1 -0
  251. package/src/hooks/use-back-handler.ts +37 -0
  252. package/src/hooks/use-direction.ts +45 -0
  253. package/src/icons/index.tsx +111 -0
  254. package/src/index.ts +129 -0
  255. package/src/primitives/keyboard-avoider.tsx +2 -1
  256. package/src/primitives/text.tsx +53 -8
  257. package/src/utils/chart.ts +355 -0
  258. package/src/utils/date.ts +446 -0
@@ -9,16 +9,24 @@
9
9
  * ```tsx
10
10
  * <Separator />
11
11
  * <Separator orientation="vertical" />
12
+ * <Separator>or</Separator>
12
13
  * ```
13
14
  *
14
15
  * A vertical separator takes its length from the parent, so the parent needs a
15
16
  * height — inside a `flex-row` with `items-stretch`, or with an explicit `h-*`
16
17
  * on either the row or the separator itself. Without one it measures zero and
17
18
  * nothing draws.
19
+ *
20
+ * Passing children turns it into a *labelled* separator: the rule breaks around
21
+ * centred content — the "or continue with" divider in a sign-in form. Only the
22
+ * horizontal axis carries a label, because a stacked word is not a divider; a
23
+ * vertical separator ignores its children and stays a plain hairline.
18
24
  */
19
- import { forwardRef } from 'react';
25
+ import { forwardRef, isValidElement, type ReactNode } from 'react';
20
26
  import { View, type ViewProps } from 'react-native';
21
27
  import { tv, type VariantProps } from 'tailwind-variants';
28
+ import { Text } from '../../primitives/text';
29
+ import { cn } from '../../utils/cn';
22
30
 
23
31
  const separatorVariants = tv({
24
32
  base: 'bg-border',
@@ -58,40 +66,90 @@ export interface SeparatorProps
58
66
  /**
59
67
  * Whether the separator is only visual. A decorative separator is skipped by
60
68
  * screen readers; set false when the split itself carries meaning — between
61
- * two groups of menu items, say — and it is announced instead.
69
+ * two groups of menu items, say — and it is announced instead. A labelled
70
+ * separator is never decorative: its label is content, so it is always read.
62
71
  */
63
72
  decorative?: boolean;
73
+ /**
74
+ * Optional label sitting in the break of a horizontal rule — the "or"
75
+ * between two sign-in paths. A bare string is wrapped in the muted label
76
+ * style; an element is rendered as-is. Ignored on the vertical axis, where a
77
+ * label is not a divider.
78
+ */
79
+ children?: ReactNode;
80
+ /** Styles the label text of a labelled separator. */
81
+ labelClassName?: string;
64
82
  }
65
83
 
66
84
  export const Separator = forwardRef<View, SeparatorProps>(
67
85
  (
68
86
  {
69
87
  className,
88
+ labelClassName,
70
89
  orientation = 'horizontal',
71
90
  variant,
72
91
  thickness,
73
92
  decorative = true,
93
+ children,
74
94
  style,
75
95
  ...props
76
96
  },
77
97
  ref
78
- ) => (
79
- <View
80
- ref={ref}
81
- // `role` rather than `accessibilityRole` — React Native's older role list
82
- // has no separator, the ARIA-aligned one does.
83
- role={decorative ? 'none' : 'separator'}
84
- accessible={!decorative}
85
- aria-orientation={decorative ? undefined : orientation}
86
- className={separatorVariants({ orientation, variant, className })}
87
- style={
88
- thickness !== undefined
89
- ? [orientation === 'horizontal' ? { height: thickness } : { width: thickness }, style]
90
- : style
91
- }
92
- {...props}
93
- />
94
- )
98
+ ) => {
99
+ const hasLabel =
100
+ orientation === 'horizontal' && children != null && children !== false;
101
+
102
+ // Labelled: two rules flanking centred content. The label carries meaning,
103
+ // so the row announces itself as a separator regardless of `decorative`,
104
+ // and the flanking rules are hidden from the reader. Here `className`
105
+ // styles the wrapping row and `labelClassName` the text.
106
+ if (hasLabel) {
107
+ const rule = separatorVariants({ orientation, variant });
108
+ const ruleStyle =
109
+ thickness !== undefined ? { height: thickness } : undefined;
110
+
111
+ return (
112
+ <View
113
+ ref={ref}
114
+ role="separator"
115
+ aria-orientation="horizontal"
116
+ className={cn('w-full flex-row items-center gap-3', className)}
117
+ style={style}
118
+ {...props}
119
+ >
120
+ <View aria-hidden className={rule} style={[styles.flex, ruleStyle]} />
121
+ {isValidElement(children) ? (
122
+ children
123
+ ) : (
124
+ <Text size="xs" muted className={labelClassName}>
125
+ {children}
126
+ </Text>
127
+ )}
128
+ <View aria-hidden className={rule} style={[styles.flex, ruleStyle]} />
129
+ </View>
130
+ );
131
+ }
132
+
133
+ return (
134
+ <View
135
+ ref={ref}
136
+ // `role` rather than `accessibilityRole` — React Native's older role list
137
+ // has no separator, the ARIA-aligned one does.
138
+ role={decorative ? 'none' : 'separator'}
139
+ accessible={!decorative}
140
+ aria-orientation={decorative ? undefined : orientation}
141
+ className={separatorVariants({ orientation, variant, className })}
142
+ style={
143
+ thickness !== undefined
144
+ ? [orientation === 'horizontal' ? { height: thickness } : { width: thickness }, style]
145
+ : style
146
+ }
147
+ {...props}
148
+ />
149
+ );
150
+ }
95
151
  );
96
152
 
97
153
  Separator.displayName = 'Separator';
154
+
155
+ const styles = { flex: { flex: 1 } } as const;
@@ -34,6 +34,7 @@ import Animated, {
34
34
  withTiming,
35
35
  } from 'react-native-reanimated';
36
36
  import { useCSSVariable } from 'uniwind';
37
+ import { useDirectionSign } from '../../hooks/use-direction';
37
38
  import { Text } from '../../primitives/text';
38
39
  import { cn } from '../../utils/cn';
39
40
 
@@ -97,6 +98,8 @@ export function Shimmer({
97
98
  const [width, setWidth] = useState(0);
98
99
  const progress = useSharedValue(0);
99
100
  const reducedMotion = useReducedMotion();
101
+ // The sweep follows the reading direction; `reverse` then flips it again.
102
+ const sign = useDirectionSign();
100
103
 
101
104
  const themeMuted = useCSSVariable('--color-muted-foreground');
102
105
  const themeForeground = useCSSVariable('--color-foreground');
@@ -134,7 +137,13 @@ export function Shimmer({
134
137
  // Travel from fully off one edge to fully off the other.
135
138
  const from = -bandWidth;
136
139
  const to = width;
137
- const x = reverse
140
+ /*
141
+ * The sweep follows the reading direction. It is standing in for text
142
+ * arriving, and text arrives from the side you start reading on — a band
143
+ * travelling against the script reads as something leaving.
144
+ */
145
+ const backwards = reverse !== (sign === -1);
146
+ const x = backwards
138
147
  ? to - progress.value * (to - from)
139
148
  : from + progress.value * (to - from);
140
149
  return { transform: [{ translateX: x }] };
@@ -34,6 +34,7 @@ import Animated, {
34
34
  withSpring,
35
35
  } from 'react-native-reanimated';
36
36
  import { tv, type VariantProps } from 'tailwind-variants';
37
+ import { useDirectionSign } from '../../hooks/use-direction';
37
38
  import { getNativeUI } from '../../native';
38
39
  import { Text } from '../../primitives/text';
39
40
  import { selectionTick } from '../../utils/haptics';
@@ -60,8 +61,8 @@ const sliderVariants = tv({
60
61
  // The track is the full height of the control, so the thumb has somewhere
61
62
  // to sit rather than somewhere to stick out of.
62
63
  track: 'w-full justify-center rounded-full bg-muted',
63
- fill: 'absolute bottom-0 left-0 top-0 rounded-full',
64
- thumb: 'absolute left-0 rounded-full p-0.5',
64
+ fill: 'absolute bottom-0 start-0 top-0 rounded-full',
65
+ thumb: 'absolute start-0 rounded-full p-0.5',
65
66
  // `background`, not a per-colour on-token: the status foregrounds are the
66
67
  // darker text hues meant for soft fills, so a green-700 knob on a green-500
67
68
  // pill would barely show. The page background reads against every fill in
@@ -193,6 +194,9 @@ export const Slider = forwardRef<View, SliderProps>(
193
194
 
194
195
  // Measured on layout; the thumb travels across (trackWidth - thumbWidth) so
195
196
  // it stays inside the track at both ends.
197
+ // `1` left to right, `-1` right to left. Everything below that is a raw
198
+ // pixel offset rather than a laid-out box multiplies through it.
199
+ const sign = useDirectionSign();
196
200
  const trackWidth = useSharedValue(0);
197
201
  // Fraction 0..1 that drives the fill and the thumb, animated on the UI thread.
198
202
  const progress = useSharedValue(max > min ? (value - min) / (max - min) : 0);
@@ -253,7 +257,12 @@ export const Slider = forwardRef<View, SliderProps>(
253
257
  })
254
258
  .onUpdate((event) => {
255
259
  const travel = Math.max(trackWidth.value - thumbWidth, 1);
256
- const delta = event.translationX / travel;
260
+ /*
261
+ * Yoga mirrors the track but not a translation: a finger moving right
262
+ * in a right-to-left subtree is moving *back* along the scale, and
263
+ * without the sign the thumb runs away from the finger.
264
+ */
265
+ const delta = (event.translationX * sign) / travel;
257
266
  const next = Math.min(Math.max(startProgress.value + delta, 0), 1);
258
267
  progress.value = next;
259
268
  runOnJS(commitFromProgress)(next, false);
@@ -268,7 +277,11 @@ export const Slider = forwardRef<View, SliderProps>(
268
277
  .maxDuration(250)
269
278
  .onEnd((event) => {
270
279
  const travel = Math.max(trackWidth.value - thumbWidth, 1);
271
- const next = Math.min(Math.max((event.x - thumbWidth / 2) / travel, 0), 1);
280
+ // `event.x` is measured from the physical left edge either way, so
281
+ // right-to-left counts back from the far end of the track.
282
+ const along =
283
+ sign === 1 ? event.x : trackWidth.value - event.x;
284
+ const next = Math.min(Math.max((along - thumbWidth / 2) / travel, 0), 1);
272
285
  progress.value = withSpring(next, SPRING);
273
286
  runOnJS(commitFromProgress)(next, true);
274
287
  });
@@ -284,7 +297,9 @@ export const Slider = forwardRef<View, SliderProps>(
284
297
 
285
298
  const thumbStyle = useAnimatedStyle(() => {
286
299
  const travel = Math.max(trackWidth.value - thumbWidth, 0);
287
- return { transform: [{ translateX: progress.value * travel }] };
300
+ // A transform is not laid out, so Yoga does not mirror it — the thumb
301
+ // has to travel the other way itself.
302
+ return { transform: [{ translateX: progress.value * travel * sign }] };
288
303
  });
289
304
 
290
305
  const knobStyle = useAnimatedStyle(() => ({
@@ -26,7 +26,7 @@ import { Pressable, View, type Text as RNText, type ViewProps } from 'react-nati
26
26
  import { tv } from 'tailwind-variants';
27
27
  import { useCSSVariable } from 'uniwind';
28
28
  import { CheckIcon } from '../../icons';
29
- import { Text, type TextProps } from '../../primitives/text';
29
+ import { Text, type TextProps, textChildren } from '../../primitives/text';
30
30
  import { Spinner } from '../spinner';
31
31
 
32
32
  export type StepState = 'active' | 'completed' | 'inactive' | 'loading';
@@ -53,7 +53,7 @@ const stepsVariants = tv({
53
53
  vertical: {
54
54
  root: 'flex-col',
55
55
  item: 'flex-col items-start',
56
- separator: 'ml-3.5 h-8 w-0.5',
56
+ separator: 'ms-3.5 h-8 w-0.5',
57
57
  },
58
58
  },
59
59
  state: {
@@ -163,7 +163,7 @@ const StepsRoot = forwardRef<View, StepsProps>(
163
163
  return (
164
164
  <StepsContext.Provider value={context}>
165
165
  <View ref={ref} className={root({ className })} {...props}>
166
- {children}
166
+ {textChildren(children)}
167
167
  </View>
168
168
  </StepsContext.Provider>
169
169
  );
@@ -210,7 +210,7 @@ const StepsItem = forwardRef<View, StepsItemProps>(
210
210
  return (
211
211
  <StepItemContext.Provider value={context}>
212
212
  <View ref={ref} className={item({ className })} {...props}>
213
- {children}
213
+ {textChildren(children)}
214
214
  </View>
215
215
  </StepItemContext.Provider>
216
216
  );
@@ -240,7 +240,7 @@ const StepsTrigger = forwardRef<View, StepsTriggerProps>(
240
240
  className={trigger({ className })}
241
241
  {...props}
242
242
  >
243
- {children}
243
+ {textChildren(children)}
244
244
  </Pressable>
245
245
  );
246
246
  }
@@ -7,6 +7,7 @@ import Animated, {
7
7
  withSpring,
8
8
  } from 'react-native-reanimated';
9
9
  import { tv, type VariantProps } from 'tailwind-variants';
10
+ import { useDirectionSign } from '../../hooks/use-direction';
10
11
  import { getNativeUI } from '../../native';
11
12
  import { selectionTick } from '../../utils/haptics';
12
13
 
@@ -68,6 +69,10 @@ export const Switch = forwardRef<View, SwitchProps>(
68
69
  ref
69
70
  ) => {
70
71
  const progress = useSharedValue(value ? 1 : 0);
72
+ // The track is mirrored by Yoga, but a transform is not — so "on" would
73
+ // otherwise still be on the right in a right-to-left subtree, which reads
74
+ // as the toggle running backwards.
75
+ const sign = useDirectionSign();
71
76
  const nativeUI = native ? getNativeUI() : null;
72
77
  const slots = switchVariants({ size, disabled: !!disabled });
73
78
 
@@ -77,7 +82,7 @@ export const Switch = forwardRef<View, SwitchProps>(
77
82
 
78
83
  const thumbStyle = useAnimatedStyle(() => ({
79
84
  transform: [
80
- { translateX: interpolate(progress.value, [0, 1], [0, TRAVEL[size]]) },
85
+ { translateX: interpolate(progress.value, [0, 1], [0, TRAVEL[size] * sign]) },
81
86
  ],
82
87
  }));
83
88
 
@@ -40,7 +40,7 @@ import Animated, {
40
40
  withSpring,
41
41
  } from 'react-native-reanimated';
42
42
  import { tv } from 'tailwind-variants';
43
- import { Text } from '../../primitives/text';
43
+ import { Text, textChildren } from '../../primitives/text';
44
44
  import { cn } from '../../utils/cn';
45
45
 
46
46
  const SPRING = { damping: 24, stiffness: 300, mass: 0.7 } as const;
@@ -197,7 +197,7 @@ function TabsRoot({
197
197
  return (
198
198
  <TabsContext.Provider value={context}>
199
199
  <View className={cn('gap-3', className)} {...props}>
200
- {children}
200
+ {textChildren(children)}
201
201
  </View>
202
202
  </TabsContext.Provider>
203
203
  );
@@ -274,7 +274,7 @@ function TabsList({ className, scrollable = false, children, ...props }: TabsLis
274
274
  const row = (
275
275
  <View accessibilityRole="tablist" className={cn(list(), className)} {...props}>
276
276
  <TabsIndicator />
277
- {children}
277
+ {textChildren(children)}
278
278
  </View>
279
279
  );
280
280
 
@@ -341,13 +341,11 @@ function TabsTrigger({
341
341
  className={cn(slots.trigger(), (icon || badge) && 'flex-row gap-1.5', className)}
342
342
  >
343
343
  {icon}
344
- {typeof children === 'string' ? (
344
+ {textChildren(children, (text) => (
345
345
  <Text size="sm" weight="medium" className={slots.label()}>
346
- {children}
346
+ {text}
347
347
  </Text>
348
- ) : (
349
- children
350
- )}
348
+ ))}
351
349
  {badge}
352
350
  </Pressable>
353
351
  );
@@ -384,7 +382,7 @@ function TabsContent({ className, value, children, style, ...props }: TabsConten
384
382
  className={className}
385
383
  {...props}
386
384
  >
387
- {children}
385
+ {textChildren(children)}
388
386
  </Animated.View>
389
387
  );
390
388
  }
@@ -43,7 +43,7 @@ import { View, type Text as RNText, type ViewProps } from 'react-native';
43
43
  import { tv } from 'tailwind-variants';
44
44
  import { useCSSVariable } from 'uniwind';
45
45
  import { IconColorProvider } from '../../icons';
46
- import { Text, type TextProps } from '../../primitives/text';
46
+ import { Text, type TextProps, textChildren } from '../../primitives/text';
47
47
 
48
48
  export type TimelineVariant = 'dot' | 'icon' | 'numbered' | 'card' | 'compact';
49
49
  /** Semantic colour for a single event, independent of progress. */
@@ -209,7 +209,7 @@ const TimelineRoot = forwardRef<View, TimelineProps>(
209
209
  return (
210
210
  <TimelineContext.Provider value={context}>
211
211
  <View ref={ref} className={root({ className })} {...props}>
212
- {children}
212
+ {textChildren(children)}
213
213
  </View>
214
214
  </TimelineContext.Provider>
215
215
  );
@@ -247,7 +247,7 @@ const TimelineItem = forwardRef<View, TimelineItemProps>(
247
247
  return (
248
248
  <TimelineItemContext.Provider value={context}>
249
249
  <View ref={ref} className={item({ className })} {...props}>
250
- {children}
250
+ {textChildren(children)}
251
251
  </View>
252
252
  </TimelineItemContext.Provider>
253
253
  );
@@ -357,7 +357,7 @@ const TimelineContent = forwardRef<View, ViewProps & { className?: string }>(
357
357
 
358
358
  return (
359
359
  <View ref={ref} className={body({ className })} {...props}>
360
- {variant === 'card' ? <View className={panel()}>{children}</View> : children}
360
+ {variant === 'card' ? <View className={panel()}>{textChildren(children)}</View> : children}
361
361
  </View>
362
362
  );
363
363
  }
@@ -39,7 +39,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
39
39
  import { tv, type VariantProps } from 'tailwind-variants';
40
40
  import { useCSSVariable } from 'uniwind';
41
41
  import { Portal } from '../../primitives/portal';
42
- import { Text, type TextProps } from '../../primitives/text';
42
+ import { Text, type TextProps, textChildren } from '../../primitives/text';
43
43
  import { cn } from '../../utils/cn';
44
44
  import { AlertTriangleIcon, CheckCircleIcon, InfoIcon, XIcon } from '../../icons';
45
45
  import { Button, type ButtonProps } from '../button';
@@ -171,7 +171,7 @@ const ToastRoot = ({ className, variant = 'default', onHide, children, ...props
171
171
  return (
172
172
  <ToastContext.Provider value={context}>
173
173
  <View accessibilityRole="alert" accessibilityLiveRegion="polite" className={root({ className })} {...props}>
174
- {children}
174
+ {textChildren(children)}
175
175
  </View>
176
176
  </ToastContext.Provider>
177
177
  );
@@ -42,7 +42,7 @@ import {
42
42
  AnimatedPressable,
43
43
  type AnimatedPressableProps,
44
44
  } from '../../primitives/animated-pressable';
45
- import { Text } from '../../primitives/text';
45
+ import { Text, textChildren } from '../../primitives/text';
46
46
  import { cn } from '../../utils/cn';
47
47
  import { selectionTick } from '../../utils/haptics';
48
48
 
@@ -196,7 +196,7 @@ const ToggleButtonGroup = forwardRef<View, ToggleButtonGroupProps>(
196
196
  className={cn('flex-row items-center gap-1.5', className)}
197
197
  {...props}
198
198
  >
199
- {children}
199
+ {textChildren(children)}
200
200
  </View>
201
201
  </ToggleGroupContext.Provider>
202
202
  );
@@ -347,13 +347,11 @@ const ToggleButtonRoot = forwardRef<View, ToggleButtonProps>(
347
347
  )}
348
348
  {...props}
349
349
  >
350
- {typeof children === 'string' ? (
350
+ {textChildren(children, (text) => (
351
351
  <Text className={slots.label({ className: labelClassName })}>
352
- {children}
352
+ {text}
353
353
  </Text>
354
- ) : (
355
- children
356
- )}
354
+ ))}
357
355
  </AnimatedPressable>
358
356
  </IconColorProvider>
359
357
  </ToggleStateContext.Provider>