@btndprojects/loam-ui-native 0.0.0-stage → 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 (269) hide show
  1. package/LICENSE +21 -0
  2. package/dist/accordion/index.js +3 -0
  3. package/dist/adaptive/index.js +2 -0
  4. package/dist/alert/index.js +3 -0
  5. package/dist/analyzer/index.js +6 -0
  6. package/dist/announcement/index.js +6 -0
  7. package/dist/audio-player/index.js +3 -0
  8. package/dist/avatar/index.js +3 -0
  9. package/dist/avatar-group/index.js +3 -0
  10. package/dist/badge/index.js +5 -0
  11. package/dist/banner/index.js +2 -0
  12. package/dist/board/index.js +7 -0
  13. package/dist/button/index.js +5 -0
  14. package/dist/camera-permission/index.js +13 -0
  15. package/dist/capture-result/index.js +18 -0
  16. package/dist/card/index.js +4 -0
  17. package/dist/card-form/index.js +10 -0
  18. package/dist/carousel/index.js +2 -0
  19. package/dist/chart/index.js +6 -0
  20. package/dist/charts/index.js +7 -0
  21. package/dist/checkbox/index.js +7 -0
  22. package/dist/checklist/index.js +10 -0
  23. package/dist/checkout/index.js +13 -0
  24. package/dist/chip/index.js +7 -0
  25. package/dist/chunk-223Q2T2Q.js +229 -0
  26. package/dist/chunk-2A5NIQAK.js +254 -0
  27. package/dist/chunk-2GK6PHGE.js +363 -0
  28. package/dist/chunk-2WUTXHMU.js +1 -0
  29. package/dist/chunk-2ZZ7TDJ4.js +324 -0
  30. package/dist/chunk-35OFI3JG.js +499 -0
  31. package/dist/chunk-3ARE4TES.js +195 -0
  32. package/dist/chunk-3ECPOSKO.js +1 -0
  33. package/dist/chunk-3ENTMPQY.js +229 -0
  34. package/dist/chunk-3HJH6FF4.js +98 -0
  35. package/dist/chunk-3NMD74ZU.js +4 -0
  36. package/dist/chunk-3WPW4KUN.js +182 -0
  37. package/dist/chunk-47FWHGUN.js +394 -0
  38. package/dist/chunk-4BXMXBJO.js +141 -0
  39. package/dist/chunk-4DS5ZWVC.js +83 -0
  40. package/dist/chunk-4GBOGOTV.js +295 -0
  41. package/dist/chunk-4SZYY6AN.js +99 -0
  42. package/dist/chunk-4YIP3CWS.js +237 -0
  43. package/dist/chunk-4YQOR7JM.js +204 -0
  44. package/dist/chunk-4ZAHCY44.js +177 -0
  45. package/dist/chunk-52X2VYTD.js +228 -0
  46. package/dist/chunk-5P26LBDL.js +1 -0
  47. package/dist/chunk-5XLRYDAX.js +1 -0
  48. package/dist/chunk-5YNTNN5A.js +177 -0
  49. package/dist/chunk-66SG2H23.js +129 -0
  50. package/dist/chunk-6HT2LP62.js +504 -0
  51. package/dist/chunk-6N33LFQA.js +35 -0
  52. package/dist/chunk-6X6PQ443.js +432 -0
  53. package/dist/chunk-7CFF4HXS.js +126 -0
  54. package/dist/chunk-7O2FMUPT.js +755 -0
  55. package/dist/chunk-AI4DWZCD.js +162 -0
  56. package/dist/chunk-AWJOCGFZ.js +213 -0
  57. package/dist/chunk-AZRX6SAC.js +285 -0
  58. package/dist/chunk-B54QE2FN.js +1 -0
  59. package/dist/chunk-BC475SJH.js +1 -0
  60. package/dist/chunk-BEDBFJND.js +202 -0
  61. package/dist/chunk-BTTFCMKG.js +166 -0
  62. package/dist/chunk-BV3B2HYZ.js +1635 -0
  63. package/dist/chunk-C2NTXVWM.js +98 -0
  64. package/dist/chunk-CDRZHT42.js +424 -0
  65. package/dist/chunk-CJ5EOYZE.js +62 -0
  66. package/dist/chunk-CUMUNPXY.js +128 -0
  67. package/dist/chunk-CUS3ZSCU.js +1 -0
  68. package/dist/chunk-CYRBHQQZ.js +411 -0
  69. package/dist/chunk-DHWADYZX.js +144 -0
  70. package/dist/chunk-DKCSPATL.js +1 -0
  71. package/dist/chunk-DM5QUKKO.js +326 -0
  72. package/dist/chunk-DSJBXLUG.js +845 -0
  73. package/dist/chunk-DYBH2I6W.js +1 -0
  74. package/dist/chunk-DZ255O2F.js +1 -0
  75. package/dist/chunk-E35BPSGW.js +1 -0
  76. package/dist/chunk-EJBDRVIH.js +166 -0
  77. package/dist/chunk-F43NP42L.js +1 -0
  78. package/dist/chunk-F5XNPWS7.js +1024 -0
  79. package/dist/chunk-FIF2M24N.js +1521 -0
  80. package/dist/chunk-FOKG57CR.js +993 -0
  81. package/dist/chunk-G3HOYSF7.js +1 -0
  82. package/dist/chunk-GDKZTW34.js +395 -0
  83. package/dist/chunk-GL7B2DOW.js +134 -0
  84. package/dist/chunk-GNNJ4GCC.js +471 -0
  85. package/dist/chunk-GNZ56EUI.js +1 -0
  86. package/dist/chunk-H6NIOQHK.js +346 -0
  87. package/dist/chunk-H6PC7RDA.js +1 -0
  88. package/dist/chunk-HPPW424V.js +334 -0
  89. package/dist/chunk-HQOQIBJI.js +1 -0
  90. package/dist/chunk-HU2EQNRZ.js +44 -0
  91. package/dist/chunk-IIQTAY3J.js +715 -0
  92. package/dist/chunk-ITTYJZWX.js +271 -0
  93. package/dist/chunk-IVNRBPGC.js +1018 -0
  94. package/dist/chunk-J4KLDGI4.js +1 -0
  95. package/dist/chunk-J7Q7VCVC.js +254 -0
  96. package/dist/chunk-JDEOITQ2.js +1 -0
  97. package/dist/chunk-JNH2TYIN.js +96 -0
  98. package/dist/chunk-JRLTGLN4.js +1 -0
  99. package/dist/chunk-JTRJJLKL.js +1 -0
  100. package/dist/chunk-JUAMLEHX.js +379 -0
  101. package/dist/chunk-JXJB2X6M.js +131 -0
  102. package/dist/chunk-KMI5ML5I.js +262 -0
  103. package/dist/chunk-KTAC4MGE.js +4 -0
  104. package/dist/chunk-LAECSR4H.js +178 -0
  105. package/dist/chunk-LK4BCSGX.js +1 -0
  106. package/dist/chunk-LOE5YNN3.js +470 -0
  107. package/dist/chunk-LRJNDEIA.js +302 -0
  108. package/dist/chunk-LSS54GLJ.js +116 -0
  109. package/dist/chunk-LZLHELAG.js +137 -0
  110. package/dist/chunk-M2LUXGKZ.js +1 -0
  111. package/dist/chunk-MOUJOP55.js +425 -0
  112. package/dist/chunk-N32RGOA5.js +159 -0
  113. package/dist/chunk-N5ZNE2YE.js +1 -0
  114. package/dist/chunk-NBNCBML6.js +1 -0
  115. package/dist/chunk-NKB7BZYK.js +167 -0
  116. package/dist/chunk-NQYBUO3K.js +1795 -0
  117. package/dist/chunk-NY2U3PBI.js +59 -0
  118. package/dist/chunk-O4AEHWII.js +464 -0
  119. package/dist/chunk-O7AE6DAF.js +146 -0
  120. package/dist/chunk-OETCUXQP.js +134 -0
  121. package/dist/chunk-OOPPQH27.js +179 -0
  122. package/dist/chunk-OPXFZ4Y5.js +204 -0
  123. package/dist/chunk-P23EQNRQ.js +338 -0
  124. package/dist/chunk-PJXUJYS6.js +248 -0
  125. package/dist/chunk-PNWUWOBF.js +1 -0
  126. package/dist/chunk-PUPM2YGF.js +376 -0
  127. package/dist/chunk-PZXZJBMP.js +620 -0
  128. package/dist/chunk-Q72LJOVE.js +169 -0
  129. package/dist/chunk-QCDXZBC5.js +247 -0
  130. package/dist/chunk-QDYDBCFP.js +197 -0
  131. package/dist/chunk-QJJ6MADV.js +274 -0
  132. package/dist/chunk-QKXCPTFS.js +349 -0
  133. package/dist/chunk-QWCMU5B2.js +860 -0
  134. package/dist/chunk-RGQWWKLA.js +634 -0
  135. package/dist/chunk-RIBDXGUG.js +108 -0
  136. package/dist/chunk-RJQOOHT2.js +214 -0
  137. package/dist/chunk-RNGLIHFA.js +191 -0
  138. package/dist/chunk-RQ3HASCC.js +6 -0
  139. package/dist/chunk-RSOJT5YW.js +124 -0
  140. package/dist/chunk-RTIKSY6A.js +1 -0
  141. package/dist/chunk-RUZHTGKB.js +237 -0
  142. package/dist/chunk-RW6ZL2FQ.js +284 -0
  143. package/dist/chunk-S7F6XLTK.js +540 -0
  144. package/dist/chunk-SBMNWWBV.js +155 -0
  145. package/dist/chunk-SDTQPRRB.js +1 -0
  146. package/dist/chunk-SYNOJOQO.js +377 -0
  147. package/dist/chunk-T753XT36.js +1 -0
  148. package/dist/chunk-TKQYWZ5V.js +196 -0
  149. package/dist/chunk-TKVFVF5D.js +95 -0
  150. package/dist/chunk-TOXAGRDQ.js +144 -0
  151. package/dist/chunk-TUTI5HOZ.js +112 -0
  152. package/dist/chunk-U7TFM2JA.js +140 -0
  153. package/dist/chunk-UC2WGUP3.js +627 -0
  154. package/dist/chunk-UWFLYMAR.js +1126 -0
  155. package/dist/chunk-UWSZB7BJ.js +1 -0
  156. package/dist/chunk-UYG5L34B.js +87 -0
  157. package/dist/chunk-VBOXAEPI.js +336 -0
  158. package/dist/chunk-VCDBXW5H.js +188 -0
  159. package/dist/chunk-VCXPHHDS.js +1 -0
  160. package/dist/chunk-VEAYTRPX.js +414 -0
  161. package/dist/chunk-VFCUIDRT.js +1 -0
  162. package/dist/chunk-VQHPQEUH.js +273 -0
  163. package/dist/chunk-VXDZFJGP.js +874 -0
  164. package/dist/chunk-WS56BL3D.js +1 -0
  165. package/dist/chunk-WSW6ZFAQ.js +98 -0
  166. package/dist/chunk-WVKKJA6U.js +870 -0
  167. package/dist/chunk-XNUVB5OL.js +375 -0
  168. package/dist/chunk-XOSCY2CW.js +242 -0
  169. package/dist/chunk-XXXEFXY5.js +99 -0
  170. package/dist/chunk-Y5CSSPV4.js +1 -0
  171. package/dist/chunk-Y6DU4J47.js +1 -0
  172. package/dist/chunk-YC6WORFL.js +535 -0
  173. package/dist/chunk-YDGVJETP.js +60 -0
  174. package/dist/chunk-YF4NSSMT.js +366 -0
  175. package/dist/chunk-YG3ZJHGU.js +223 -0
  176. package/dist/chunk-YIJ55TBC.js +1 -0
  177. package/dist/chunk-YKZ4RZZF.js +1 -0
  178. package/dist/chunk-YLLCXTMC.js +37 -0
  179. package/dist/chunk-YTA74UZR.js +1 -0
  180. package/dist/chunk-Z7H7VSFZ.js +323 -0
  181. package/dist/chunk-ZK2H4O5P.js +1 -0
  182. package/dist/chunk-ZVFE5QGU.js +524 -0
  183. package/dist/coachmark/index.js +4 -0
  184. package/dist/code-block/index.js +4 -0
  185. package/dist/color-field/index.js +7 -0
  186. package/dist/commerce/index.js +20 -0
  187. package/dist/connectivity/index.js +2 -0
  188. package/dist/context-menu/index.js +5 -0
  189. package/dist/copy-button/index.js +3 -0
  190. package/dist/data-grid/index.js +24 -0
  191. package/dist/date-range-picker/index.js +13 -0
  192. package/dist/description-list/index.js +2 -0
  193. package/dist/dialog/index.js +3 -0
  194. package/dist/empty-state/index.js +3 -0
  195. package/dist/error-summary/index.js +3 -0
  196. package/dist/fab/index.js +2 -0
  197. package/dist/file-upload/index.js +9 -0
  198. package/dist/filter-bar/index.js +8 -0
  199. package/dist/flow-run/index.js +14 -0
  200. package/dist/form/index.js +3 -0
  201. package/dist/gallery/index.js +2 -0
  202. package/dist/header/index.js +5 -0
  203. package/dist/hold-to-confirm/index.js +4 -0
  204. package/dist/index.d.ts +22883 -0
  205. package/dist/index.js +158 -0
  206. package/dist/infinite-list/index.js +2 -0
  207. package/dist/inline-edit/index.js +5 -0
  208. package/dist/input/index.js +5 -0
  209. package/dist/interactions/index.js +2 -0
  210. package/dist/invoice/index.js +10 -0
  211. package/dist/kbd/index.js +1 -0
  212. package/dist/keyboard/index.js +3 -0
  213. package/dist/list-row/index.js +7 -0
  214. package/dist/log-viewer/index.js +2 -0
  215. package/dist/map-view/index.js +3 -0
  216. package/dist/markdown/index.js +6 -0
  217. package/dist/metric/index.js +4 -0
  218. package/dist/money/index.js +3 -0
  219. package/dist/nav-drawer/index.js +2 -0
  220. package/dist/navigation/index.js +2 -0
  221. package/dist/notification-center/index.js +16 -0
  222. package/dist/number-roll/index.js +3 -0
  223. package/dist/one-handed/index.js +5 -0
  224. package/dist/os-surfaces/index.js +3 -0
  225. package/dist/otp/index.js +7 -0
  226. package/dist/page-header/index.js +1 -0
  227. package/dist/payment-method/index.js +12 -0
  228. package/dist/payment-status/index.js +16 -0
  229. package/dist/permission/index.js +2 -0
  230. package/dist/picker/index.js +7 -0
  231. package/dist/progress/index.js +5 -0
  232. package/dist/pull-to-refresh/index.js +4 -0
  233. package/dist/radio-group/index.js +6 -0
  234. package/dist/rating/index.js +2 -0
  235. package/dist/receivables/index.js +20 -0
  236. package/dist/refund/index.js +16 -0
  237. package/dist/save-status/index.js +3 -0
  238. package/dist/scroll-area/index.js +1 -0
  239. package/dist/search-bar/index.js +5 -0
  240. package/dist/segmented-control/index.js +5 -0
  241. package/dist/sheet/index.js +4 -0
  242. package/dist/skeleton/index.js +2 -0
  243. package/dist/slider/index.js +5 -0
  244. package/dist/spinner/index.js +2 -0
  245. package/dist/split-button/index.js +7 -0
  246. package/dist/spotlight/index.js +2 -0
  247. package/dist/stage-timeline/index.js +4 -0
  248. package/dist/status-dot/index.js +2 -0
  249. package/dist/stepper/index.js +6 -0
  250. package/dist/streaming-text/index.js +3 -0
  251. package/dist/survey/index.js +19 -0
  252. package/dist/switch/index.js +5 -0
  253. package/dist/system-state/index.js +4 -0
  254. package/dist/tab-bar/index.js +2 -0
  255. package/dist/tabs/index.js +2 -0
  256. package/dist/textarea/index.js +6 -0
  257. package/dist/thinking/index.js +2 -0
  258. package/dist/timeline/index.js +4 -0
  259. package/dist/toast/index.js +2 -0
  260. package/dist/toolbar/index.js +2 -0
  261. package/dist/tour/index.js +11 -0
  262. package/dist/truncation-notice/index.js +2 -0
  263. package/dist/update-notice/index.js +11 -0
  264. package/dist/video-player/index.js +2 -0
  265. package/dist/viewfinder/index.js +5 -0
  266. package/dist/widget/index.js +4 -0
  267. package/dist/wizard/index.js +6 -0
  268. package/package.json +76 -3
  269. package/README.md +0 -3
@@ -0,0 +1,346 @@
1
+ import { DRAG_CURSOR } from './chunk-KTAC4MGE.js';
2
+ import { haptics } from './chunk-NY2U3PBI.js';
3
+ import { useResolvedMotion } from './chunk-6N33LFQA.js';
4
+ import * as React from 'react';
5
+ import { Animated, Easing, PanResponder, View, Text } from 'react-native';
6
+ import { createStyleSheet, useStyles, useLoamTheme } from '@btndprojects/loam-theme-native';
7
+ import { jsxs, jsx } from 'react/jsx-runtime';
8
+
9
+ var GEOMETRY = {
10
+ sm: { thumb: 18, track: 4, height: 32 },
11
+ // loam-allow-raw: ramp keys, read via theme.space
12
+ md: { thumb: 24, track: 6, height: 40 }
13
+ // loam-allow-raw: ramp keys, read via theme.space
14
+ };
15
+ var THUMB_ACTIVE_SCALE = 1.18;
16
+ var MAX_TICKS = 20;
17
+ var BUBBLE_BOTTOM = 28;
18
+ function clamp(n, lo, hi) {
19
+ return Math.max(lo, Math.min(hi, n));
20
+ }
21
+ function Slider({
22
+ min = 0,
23
+ max = 100,
24
+ step = 1,
25
+ value,
26
+ defaultValue,
27
+ onValueChange,
28
+ disabled = false,
29
+ size = "md",
30
+ motion = "full",
31
+ showValue = false,
32
+ showReadout = false,
33
+ showTicks = false,
34
+ accessibilityLabel
35
+ }) {
36
+ const animateScale = useResolvedMotion(motion) === "full";
37
+ const isControlled = value !== void 0;
38
+ const [internal, setInternal] = React.useState(defaultValue ?? min);
39
+ const current = clamp(isControlled ? value : internal, min, max);
40
+ const { styles } = useStyles(sheet, { size, disabled });
41
+ const theme = useLoamTheme();
42
+ const fraction = max > min ? (current - min) / (max - min) : 0;
43
+ const pct = `${fraction * 100}%`;
44
+ const [trackWidth, setTrackWidth] = React.useState(0);
45
+ const containerRef = React.useRef(null);
46
+ const widthRef = React.useRef(0);
47
+ const leftRef = React.useRef(0);
48
+ const currentRef = React.useRef(current);
49
+ currentRef.current = current;
50
+ const disabledRef = React.useRef(disabled);
51
+ disabledRef.current = disabled;
52
+ const setValue = React.useCallback(
53
+ (next) => {
54
+ if (next === currentRef.current) return;
55
+ currentRef.current = next;
56
+ if (!isControlled) setInternal(next);
57
+ onValueChange?.(next);
58
+ },
59
+ [isControlled, onValueChange]
60
+ );
61
+ const handleRef = React.useRef(() => {
62
+ });
63
+ handleRef.current = (pageX) => {
64
+ const width = widthRef.current;
65
+ if (!width) return;
66
+ const frac = clamp((pageX - leftRef.current) / width, 0, 1);
67
+ const raw = min + frac * (max - min);
68
+ const stepped = step > 0 ? Math.round(raw / step) * step : raw;
69
+ const next = clamp(Number(stepped.toFixed(6)), min, max);
70
+ setValue(next);
71
+ };
72
+ const thumbScale = React.useRef(new Animated.Value(1)).current;
73
+ const swellRef = React.useRef(() => {
74
+ });
75
+ swellRef.current = (to) => {
76
+ if (!animateScale) {
77
+ thumbScale.setValue(to);
78
+ return;
79
+ }
80
+ Animated.timing(thumbScale, {
81
+ toValue: to,
82
+ duration: theme.duration.base,
83
+ easing: Easing.bezier(...theme.easing.spring),
84
+ useNativeDriver: true
85
+ }).start();
86
+ };
87
+ const bubbleScale = React.useRef(new Animated.Value(0)).current;
88
+ const bubbleRef = React.useRef(() => {
89
+ });
90
+ bubbleRef.current = (to) => {
91
+ if (!animateScale) {
92
+ bubbleScale.setValue(to);
93
+ return;
94
+ }
95
+ Animated.timing(bubbleScale, {
96
+ toValue: to,
97
+ duration: theme.duration.base,
98
+ easing: Easing.bezier(...theme.easing.spring),
99
+ useNativeDriver: true
100
+ }).start();
101
+ };
102
+ const steppedRef = React.useRef(step > 0);
103
+ steppedRef.current = step > 0;
104
+ const pan = React.useRef(
105
+ PanResponder.create({
106
+ onStartShouldSetPanResponder: () => !disabledRef.current,
107
+ onMoveShouldSetPanResponder: () => !disabledRef.current,
108
+ onPanResponderGrant: (e) => {
109
+ swellRef.current(THUMB_ACTIVE_SCALE);
110
+ bubbleRef.current(1);
111
+ haptics.impact("light");
112
+ handleRef.current(e.nativeEvent.pageX);
113
+ },
114
+ onPanResponderMove: (e) => {
115
+ const before = currentRef.current;
116
+ handleRef.current(e.nativeEvent.pageX);
117
+ if (steppedRef.current && currentRef.current !== before) haptics.impact("light");
118
+ },
119
+ onPanResponderRelease: () => {
120
+ swellRef.current(1);
121
+ bubbleRef.current(0);
122
+ if (steppedRef.current) haptics.impact("medium");
123
+ },
124
+ onPanResponderTerminate: () => {
125
+ swellRef.current(1);
126
+ bubbleRef.current(0);
127
+ }
128
+ })
129
+ ).current;
130
+ const onLayout = (e) => {
131
+ const w = e.nativeEvent.layout.width;
132
+ widthRef.current = w;
133
+ setTrackWidth(w);
134
+ containerRef.current?.measureInWindow?.((x) => {
135
+ leftRef.current = x;
136
+ });
137
+ };
138
+ const thumbSize = theme.space[GEOMETRY[size].thumb];
139
+ const thumbTranslateX = fraction * trackWidth - thumbSize / 2;
140
+ const stepCount = step > 0 && max > min ? Math.round((max - min) / step) : 0;
141
+ const showTickMarks = showTicks && stepCount > 0 && stepCount <= MAX_TICKS;
142
+ const tickIndices = showTickMarks ? Array.from({ length: stepCount + 1 }, (_, i) => i) : [];
143
+ const bubbleLabel = step > 0 ? String(current) : `${Math.round(fraction * 100)}%`;
144
+ const readoutText = `${min} \xB7 ${Math.round(fraction * 100)}% \xB7 ${max}`;
145
+ return /* @__PURE__ */ jsxs(View, { style: styles.wrap, children: [
146
+ /* @__PURE__ */ jsxs(View, { style: styles.trackRow, children: [
147
+ /* @__PURE__ */ jsxs(
148
+ View,
149
+ {
150
+ ref: containerRef,
151
+ accessibilityRole: "adjustable",
152
+ accessibilityLabel,
153
+ accessibilityValue: { min, max, now: current },
154
+ accessibilityState: { disabled },
155
+ "aria-valuemin": min,
156
+ "aria-valuemax": max,
157
+ "aria-valuenow": current,
158
+ onLayout,
159
+ style: [styles.container, !disabled && DRAG_CURSOR],
160
+ ...disabled ? {} : pan.panHandlers,
161
+ children: [
162
+ /* @__PURE__ */ jsxs(View, { style: styles.track, children: [
163
+ /* @__PURE__ */ jsx(View, { style: [styles.fill, { width: pct }] }),
164
+ showTickMarks && /* @__PURE__ */ jsx(View, { style: styles.ticks, children: tickIndices.map((i) => /* @__PURE__ */ jsx(View, { testID: `slider-tick-${i}`, style: styles.tick }, i)) })
165
+ ] }),
166
+ /* @__PURE__ */ jsx(
167
+ View,
168
+ {
169
+ style: [styles.bubbleAnchor, { transform: [{ translateX: fraction * trackWidth }] }],
170
+ children: /* @__PURE__ */ jsx(
171
+ Animated.View,
172
+ {
173
+ testID: "slider-bubble",
174
+ style: [styles.bubbleCard, { transform: [{ scale: bubbleScale }] }],
175
+ children: /* @__PURE__ */ jsx(Text, { style: styles.bubbleText, children: bubbleLabel })
176
+ }
177
+ )
178
+ }
179
+ ),
180
+ /* @__PURE__ */ jsx(
181
+ Animated.View,
182
+ {
183
+ testID: "slider-thumb",
184
+ style: [
185
+ styles.thumb,
186
+ {
187
+ transform: [{ translateX: thumbTranslateX }, { scale: thumbScale }]
188
+ }
189
+ ]
190
+ }
191
+ )
192
+ ]
193
+ }
194
+ ),
195
+ showValue ? /* @__PURE__ */ jsx(Text, { style: styles.value, children: current }) : null
196
+ ] }),
197
+ showReadout ? /* @__PURE__ */ jsx(Text, { style: styles.readout, children: readoutText }) : null
198
+ ] });
199
+ }
200
+ var sheet = createStyleSheet((theme) => ({
201
+ // Outer column: stacks the track row and (optional) read-out below it.
202
+ wrap: {
203
+ flexDirection: "column",
204
+ alignSelf: "stretch",
205
+ gap: theme.space.s2
206
+ },
207
+ // Track row: slider container + optional side-label.
208
+ trackRow: {
209
+ flexDirection: "row",
210
+ alignItems: "center",
211
+ gap: theme.space.s3
212
+ },
213
+ container: {
214
+ flex: 1,
215
+ justifyContent: "center",
216
+ variants: {
217
+ size: {
218
+ sm: { height: theme.space[GEOMETRY.sm.height] },
219
+ md: { height: theme.space[GEOMETRY.md.height] }
220
+ },
221
+ disabled: {
222
+ true: { opacity: theme.state.disabledOpacity },
223
+ false: {}
224
+ }
225
+ }
226
+ },
227
+ track: {
228
+ width: "100%",
229
+ borderRadius: theme.radius.full,
230
+ backgroundColor: theme.color.surface3,
231
+ overflow: "hidden",
232
+ variants: {
233
+ size: {
234
+ sm: { height: theme.space[GEOMETRY.sm.track] },
235
+ md: { height: theme.space[GEOMETRY.md.track] }
236
+ },
237
+ disabled: {}
238
+ }
239
+ },
240
+ fill: {
241
+ position: "absolute",
242
+ left: 0,
243
+ top: 0,
244
+ bottom: 0,
245
+ borderRadius: theme.radius.full,
246
+ backgroundColor: theme.color.accent,
247
+ variants: { size: {}, disabled: {} }
248
+ },
249
+ // Tick marks overlaid inside the track (reference sliderCtl line 2294).
250
+ // `overflow: 'hidden'` on `track` keeps them clipped to the track bounds.
251
+ ticks: {
252
+ position: "absolute",
253
+ left: 0,
254
+ right: 0,
255
+ top: 0,
256
+ bottom: 0,
257
+ flexDirection: "row",
258
+ justifyContent: "space-between",
259
+ alignItems: "center"
260
+ },
261
+ tick: {
262
+ width: theme.space[4],
263
+ height: theme.space[4],
264
+ borderRadius: theme.radius.full,
265
+ backgroundColor: theme.color.surface,
266
+ borderWidth: theme.borderWidth.thin,
267
+ borderColor: theme.color.border
268
+ },
269
+ // Bubble anchor: zero-width column pinned to the bottom of the drag zone.
270
+ // `alignItems: 'center'` centres the bubble card over the zero-width axis
271
+ // without knowing the card's intrinsic width.
272
+ bubbleAnchor: {
273
+ position: "absolute",
274
+ bottom: BUBBLE_BOTTOM,
275
+ width: 0,
276
+ alignItems: "center"
277
+ },
278
+ // The pill-shaped card that floats above the thumb during drag.
279
+ // Reference sliderCtl line 2292: background var(--text), color var(--bg),
280
+ // font-size 12px, font-weight 700, padding 4px 9px, border-radius 8px.
281
+ bubbleCard: {
282
+ backgroundColor: theme.color.text,
283
+ borderRadius: theme.radius.sm,
284
+ paddingVertical: theme.space.s1,
285
+ paddingHorizontal: theme.space[9]
286
+ },
287
+ bubbleText: {
288
+ color: theme.color.bg,
289
+ fontFamily: theme.font.sans,
290
+ fontSize: theme.type.caption1.fontSize,
291
+ fontWeight: theme.fontWeight.bold,
292
+ lineHeight: theme.type.caption1.lineHeight
293
+ },
294
+ thumb: {
295
+ position: "absolute",
296
+ top: "50%",
297
+ backgroundColor: theme.color.surface,
298
+ borderRadius: theme.radius.full,
299
+ // `0 0 0 1px var(--border)` — a 1px ring that is part of the drawing, so it
300
+ // uses the static `w1` weight rather than the contrast-reactive hairline.
301
+ borderWidth: theme.borderWidth.thin,
302
+ borderColor: theme.color.border,
303
+ // The reference asks for `0 2px 8px rgba(…,.25)` (`sliderCtl()`, reference
304
+ // line 2290). That is `shadow.knob` with a wider blur, so only the radius
305
+ // is overridden — and the *colour* now comes from the theme, which carries
306
+ // the accent tint `--sh-*` carries on the web. A hardcoded `#000000` here
307
+ // made the thumb one of the only shadows in the system to ignore the tint.
308
+ ...theme.shadow.knob,
309
+ shadowRadius: theme.space[8],
310
+ elevation: 3,
311
+ // loam-allow-raw: Android depth between shadow.knob (2) and shadow.md (4).
312
+ variants: {
313
+ size: {
314
+ sm: {
315
+ width: theme.space[GEOMETRY.sm.thumb],
316
+ height: theme.space[GEOMETRY.sm.thumb],
317
+ // Vertical centring only — horizontal offset comes from translateX.
318
+ marginTop: -theme.space[GEOMETRY.sm.thumb] / 2
319
+ },
320
+ md: {
321
+ width: theme.space[GEOMETRY.md.thumb],
322
+ height: theme.space[GEOMETRY.md.thumb],
323
+ // Vertical centring only — horizontal offset comes from translateX.
324
+ marginTop: -theme.space[GEOMETRY.md.thumb] / 2
325
+ }
326
+ },
327
+ disabled: {}
328
+ }
329
+ },
330
+ value: {
331
+ minWidth: theme.space[36],
332
+ textAlign: "right",
333
+ fontFamily: theme.font.mono,
334
+ fontSize: theme.fontSize[14],
335
+ color: theme.color.text2
336
+ },
337
+ // Read-out row: mono `min · value% · max` below the track.
338
+ readout: {
339
+ fontFamily: theme.font.mono,
340
+ fontSize: theme.fontSize[12],
341
+ color: theme.color.text3,
342
+ textAlign: "center"
343
+ }
344
+ }));
345
+
346
+ export { Slider };
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,334 @@
1
+ import { Icon } from './chunk-TKVFVF5D.js';
2
+ import * as React2 from 'react';
3
+ import { Platform, Easing, Keyboard, Animated, View, Pressable, Text, useWindowDimensions, ScrollView } from 'react-native';
4
+ import { createStyleSheet, useLoamTheme, useStyles } from '@btndprojects/loam-theme-native';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
+
7
+ function keyboardEventNames(os = Platform.OS) {
8
+ return os === "ios" ? { show: "keyboardWillShow", hide: "keyboardWillHide" } : { show: "keyboardDidShow", hide: "keyboardDidHide" };
9
+ }
10
+ function eventDuration(event, fallback) {
11
+ const reported = event?.duration;
12
+ return typeof reported === "number" && reported > 0 ? reported : fallback;
13
+ }
14
+ function eventEasing(event) {
15
+ const reported = event?.easing;
16
+ return reported && reported !== "keyboard" ? reported : void 0;
17
+ }
18
+ function keyboardEasing(easing, fallback) {
19
+ switch (easing) {
20
+ case "linear":
21
+ return Easing.linear;
22
+ case "easeIn":
23
+ return Easing.in(Easing.ease);
24
+ case "easeOut":
25
+ return Easing.out(Easing.ease);
26
+ case "easeInEaseOut":
27
+ return Easing.inOut(Easing.ease);
28
+ default:
29
+ return Easing.bezier(...fallback);
30
+ }
31
+ }
32
+ function useKeyboardInset() {
33
+ const theme = useLoamTheme();
34
+ const enterDuration = theme.motion.enter.duration;
35
+ const exitDuration = theme.motion.exit.duration;
36
+ const [state, setState] = React2.useState(() => ({
37
+ height: 0,
38
+ visible: false,
39
+ duration: enterDuration,
40
+ easing: void 0
41
+ }));
42
+ React2.useEffect(() => {
43
+ const { show, hide } = keyboardEventNames();
44
+ const onShow = (event) => setState({
45
+ height: event?.endCoordinates?.height ?? 0,
46
+ visible: true,
47
+ duration: eventDuration(event, enterDuration),
48
+ easing: eventEasing(event)
49
+ });
50
+ const onHide = (event) => setState({
51
+ height: 0,
52
+ visible: false,
53
+ // Closing is an exit: faster than the open, per the motion contract.
54
+ duration: eventDuration(event, exitDuration),
55
+ easing: eventEasing(event)
56
+ });
57
+ const subscriptions = [Keyboard.addListener(show, onShow), Keyboard.addListener(hide, onHide)];
58
+ return () => {
59
+ for (const subscription of subscriptions) subscription.remove();
60
+ };
61
+ }, [enterDuration, exitDuration]);
62
+ return state;
63
+ }
64
+ function useAnimatedKeyboardInset(extra = 0) {
65
+ const theme = useLoamTheme();
66
+ const state = useKeyboardInset();
67
+ const inset = React2.useRef(new Animated.Value(0)).current;
68
+ const fallbackCurve = state.visible ? theme.motion.enter.easing : theme.motion.exit.easing;
69
+ const target = state.visible ? state.height + extra : 0;
70
+ React2.useEffect(() => {
71
+ Animated.timing(inset, {
72
+ toValue: target,
73
+ duration: state.duration,
74
+ easing: keyboardEasing(state.easing, fallbackCurve),
75
+ // A reserved gap is layout, so this one cannot ride the native driver; it
76
+ // is a single value per keyboard transition, not a per-frame loop.
77
+ useNativeDriver: false
78
+ }).start();
79
+ }, [inset, target, state.duration, state.easing, fallbackCurve]);
80
+ return { inset, state };
81
+ }
82
+ var PREVIOUS_ICON = "arrow-up";
83
+ var NEXT_ICON = "arrow-down";
84
+ var ACCESSORY_NAV_MIN_FIELDS = 3;
85
+ function KeyboardSpacer({
86
+ extra = 0,
87
+ enabled = true,
88
+ testID
89
+ }) {
90
+ const { inset } = useAnimatedKeyboardInset(extra);
91
+ return /* @__PURE__ */ jsx(
92
+ Animated.View,
93
+ {
94
+ accessibilityElementsHidden: true,
95
+ importantForAccessibility: "no-hide-descendants",
96
+ style: { height: enabled ? inset : 0 },
97
+ testID
98
+ }
99
+ );
100
+ }
101
+ function KeyboardStickyView({
102
+ children,
103
+ offset = 0,
104
+ enabled = true,
105
+ style,
106
+ testID
107
+ }) {
108
+ const { styles } = useStyles(sheet);
109
+ const { inset } = useAnimatedKeyboardInset(offset);
110
+ const lift = inset.interpolate({ inputRange: [0, 1], outputRange: [0, -1] });
111
+ return /* @__PURE__ */ jsx(
112
+ Animated.View,
113
+ {
114
+ style: [styles.stickyBar, { transform: [{ translateY: enabled ? lift : 0 }] }, style],
115
+ testID,
116
+ children
117
+ }
118
+ );
119
+ }
120
+ function KeyboardAccessoryBar({
121
+ navigable = true,
122
+ fieldCount,
123
+ canGoPrevious = true,
124
+ canGoNext = true,
125
+ onPrevious,
126
+ onNext,
127
+ onDone,
128
+ doneLabel = "Done",
129
+ previousLabel = "Previous field",
130
+ nextLabel = "Next field",
131
+ children,
132
+ sticky = false,
133
+ style,
134
+ testID
135
+ }) {
136
+ const { styles } = useStyles(sheet);
137
+ const showNav = navigable && (fieldCount == null || fieldCount > ACCESSORY_NAV_MIN_FIELDS);
138
+ const bar = /* @__PURE__ */ jsxs(View, { accessibilityRole: "toolbar", style: [styles.accessoryBar, style], testID, children: [
139
+ showNav ? /* @__PURE__ */ jsxs(View, { style: styles.accessoryNav, children: [
140
+ /* @__PURE__ */ jsx(
141
+ AccessoryButton,
142
+ {
143
+ icon: PREVIOUS_ICON,
144
+ label: previousLabel,
145
+ disabled: !canGoPrevious,
146
+ onPress: onPrevious
147
+ }
148
+ ),
149
+ /* @__PURE__ */ jsx(
150
+ AccessoryButton,
151
+ {
152
+ icon: NEXT_ICON,
153
+ label: nextLabel,
154
+ disabled: !canGoNext,
155
+ onPress: onNext
156
+ }
157
+ )
158
+ ] }) : /* @__PURE__ */ jsx(View, {}),
159
+ children ? /* @__PURE__ */ jsx(View, { style: styles.accessoryMiddle, children }) : null,
160
+ /* @__PURE__ */ jsx(
161
+ Pressable,
162
+ {
163
+ accessibilityRole: "button",
164
+ accessibilityLabel: doneLabel,
165
+ onPress: onDone,
166
+ style: styles.accessoryDone,
167
+ children: /* @__PURE__ */ jsx(Text, { style: styles.accessoryDoneLabel, children: doneLabel })
168
+ }
169
+ )
170
+ ] });
171
+ return sticky ? /* @__PURE__ */ jsx(KeyboardStickyView, { children: bar }) : bar;
172
+ }
173
+ function AccessoryButton({
174
+ icon,
175
+ label,
176
+ disabled,
177
+ onPress
178
+ }) {
179
+ const theme = useLoamTheme();
180
+ const { styles } = useStyles(sheet, { disabled });
181
+ return /* @__PURE__ */ jsx(
182
+ Pressable,
183
+ {
184
+ accessibilityRole: "button",
185
+ accessibilityLabel: label,
186
+ accessibilityState: { disabled },
187
+ disabled,
188
+ onPress: disabled ? void 0 : onPress,
189
+ style: ({ pressed }) => [
190
+ styles.accessoryButton,
191
+ pressed && !disabled ? { backgroundColor: theme.state.pressed } : null
192
+ ],
193
+ children: /* @__PURE__ */ jsx(Icon, { name: icon, size: theme.icon.md, color: disabled ? theme.color.text3 : theme.color.text2 })
194
+ }
195
+ );
196
+ }
197
+ var KeyboardScrollContext = React2.createContext(null);
198
+ function KeyboardAvoidingScrollView({
199
+ children,
200
+ extraBottom,
201
+ keyboardDismissMode = "on-drag",
202
+ contentContainerStyle,
203
+ onScroll,
204
+ testID,
205
+ ...rest
206
+ }) {
207
+ const theme = useLoamTheme();
208
+ const inset = useKeyboardInset();
209
+ const { height: windowHeight } = useWindowDimensions();
210
+ const clearance = extraBottom ?? theme.layout.screenPad;
211
+ const scrollRef = React2.useRef(null);
212
+ const offset = React2.useRef(0);
213
+ const restoreTo = React2.useRef(null);
214
+ const pendingNode = React2.useRef(null);
215
+ const latest = React2.useRef({ inset, windowHeight, clearance });
216
+ latest.current = { inset, windowHeight, clearance };
217
+ const handleScroll = React2.useCallback(
218
+ (event) => {
219
+ offset.current = event.nativeEvent.contentOffset.y;
220
+ onScroll?.(event);
221
+ },
222
+ [onScroll]
223
+ );
224
+ const bringIntoView = React2.useCallback((node) => {
225
+ if (!node || typeof node.measureInWindow !== "function") return;
226
+ const { inset: kbd, windowHeight: winH, clearance: pad } = latest.current;
227
+ if (!kbd.visible) {
228
+ pendingNode.current = node;
229
+ return;
230
+ }
231
+ node.measureInWindow((_x, y, _width, height) => {
232
+ const visibleBottom = winH - kbd.height - pad;
233
+ const covered = y + height - visibleBottom;
234
+ if (covered > 0) {
235
+ scrollRef.current?.scrollTo({ y: offset.current + covered, animated: true });
236
+ }
237
+ });
238
+ }, []);
239
+ React2.useEffect(() => {
240
+ if (inset.visible) {
241
+ restoreTo.current = offset.current;
242
+ const node = pendingNode.current;
243
+ pendingNode.current = null;
244
+ if (node) bringIntoView(node);
245
+ return;
246
+ }
247
+ pendingNode.current = null;
248
+ const target = restoreTo.current;
249
+ restoreTo.current = null;
250
+ if (target != null) scrollRef.current?.scrollTo({ y: target, animated: true });
251
+ }, [inset.visible, bringIntoView]);
252
+ const context = React2.useMemo(
253
+ () => ({ scrollIntoView: bringIntoView }),
254
+ [bringIntoView]
255
+ );
256
+ return /* @__PURE__ */ jsx(KeyboardScrollContext.Provider, { value: context, children: /* @__PURE__ */ jsx(
257
+ ScrollView,
258
+ {
259
+ ref: scrollRef,
260
+ keyboardDismissMode,
261
+ keyboardShouldPersistTaps: "handled",
262
+ scrollEventThrottle: 16,
263
+ onScroll: handleScroll,
264
+ contentContainerStyle: [{ paddingBottom: inset.height + clearance }, contentContainerStyle],
265
+ testID,
266
+ ...rest,
267
+ children
268
+ }
269
+ ) });
270
+ }
271
+ function useScrollIntoView() {
272
+ const context = React2.useContext(KeyboardScrollContext);
273
+ const ref = React2.useRef(null);
274
+ const onFocus = React2.useCallback(() => {
275
+ context?.scrollIntoView(ref.current);
276
+ }, [context]);
277
+ return { ref, onFocus };
278
+ }
279
+ var sheet = createStyleSheet((theme) => ({
280
+ // The reference draws the accessory bar 42 dp tall; `toolbarH` (44) is the
281
+ // token for exactly this surface and also clears `tapMin`.
282
+ accessoryBar: {
283
+ minHeight: theme.layout.toolbarH,
284
+ flexDirection: "row",
285
+ alignItems: "center",
286
+ justifyContent: "space-between",
287
+ paddingHorizontal: theme.space.s3,
288
+ backgroundColor: theme.color.surface2,
289
+ borderTopWidth: theme.borderWidth.base,
290
+ borderTopColor: theme.color.border
291
+ },
292
+ accessoryNav: {
293
+ flexDirection: "row",
294
+ alignItems: "center",
295
+ gap: theme.space.s2
296
+ },
297
+ accessoryMiddle: { flex: 1, alignItems: "center" },
298
+ accessoryButton: {
299
+ minWidth: theme.tapMin,
300
+ minHeight: theme.tapMin,
301
+ alignItems: "center",
302
+ justifyContent: "center",
303
+ borderRadius: theme.radius.sm,
304
+ variants: {
305
+ disabled: {
306
+ true: { opacity: theme.state.disabledOpacity },
307
+ false: {}
308
+ }
309
+ }
310
+ },
311
+ accessoryDone: {
312
+ minHeight: theme.tapMin,
313
+ minWidth: theme.tapMin,
314
+ alignItems: "flex-end",
315
+ justifyContent: "center",
316
+ paddingHorizontal: theme.space.s1
317
+ },
318
+ accessoryDoneLabel: {
319
+ fontFamily: theme.font.sans,
320
+ fontWeight: theme.fontWeight.bold,
321
+ fontSize: theme.type.footnote.fontSize,
322
+ lineHeight: theme.type.footnote.lineHeight,
323
+ letterSpacing: theme.type.footnote.letterSpacing,
324
+ color: theme.color.accentStrong
325
+ },
326
+ stickyBar: {
327
+ flexDirection: "column",
328
+ backgroundColor: theme.color.surface,
329
+ borderTopWidth: theme.borderWidth.base,
330
+ borderTopColor: theme.color.border
331
+ }
332
+ }));
333
+
334
+ export { ACCESSORY_NAV_MIN_FIELDS, KeyboardAccessoryBar, KeyboardAvoidingScrollView, KeyboardSpacer, KeyboardStickyView, keyboardEasing, keyboardEventNames, useAnimatedKeyboardInset, useKeyboardInset, useScrollIntoView };
@@ -0,0 +1 @@
1
+