@btndprojects/loam-ui-native 0.1.1 → 0.2.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 (262) hide show
  1. package/dist/accordion/index.js +5 -3
  2. package/dist/adaptive/index.js +6 -2
  3. package/dist/alert/index.js +5 -2
  4. package/dist/analyzer/index.js +9 -6
  5. package/dist/announcement/index.js +8 -5
  6. package/dist/audio-player/index.js +5 -3
  7. package/dist/avatar/index.js +5 -2
  8. package/dist/avatar-group/index.js +7 -3
  9. package/dist/badge/index.js +7 -4
  10. package/dist/banner/index.js +5 -2
  11. package/dist/board/index.js +11 -6
  12. package/dist/button/index.js +6 -3
  13. package/dist/camera-permission/index.js +15 -12
  14. package/dist/capture-result/index.js +19 -16
  15. package/dist/card/index.js +6 -3
  16. package/dist/card-form/index.js +12 -8
  17. package/dist/carousel/index.js +4 -2
  18. package/dist/chart/index.js +8 -5
  19. package/dist/charts/index.js +8 -6
  20. package/dist/chat/index.js +16 -0
  21. package/dist/checkbox/index.js +6 -4
  22. package/dist/checklist/index.js +11 -8
  23. package/dist/checkout/index.js +16 -11
  24. package/dist/chip/index.js +7 -4
  25. package/dist/{chunk-XNUVB5OL.js → chunk-2OLKPG4E.js} +92 -28
  26. package/dist/{chunk-RUZHTGKB.js → chunk-2PKYGLQ6.js} +71 -40
  27. package/dist/{chunk-UC2WGUP3.js → chunk-2SWKL3AJ.js} +91 -252
  28. package/dist/{chunk-AI4DWZCD.js → chunk-2SY63FCH.js} +63 -16
  29. package/dist/{chunk-F5XNPWS7.js → chunk-2TXERVVA.js} +92 -35
  30. package/dist/chunk-36EC2T6P.js +581 -0
  31. package/dist/chunk-3DUWIYV5.js +437 -0
  32. package/dist/{chunk-JNY4ZHAR.js → chunk-3QMEOFW5.js} +275 -71
  33. package/dist/chunk-3WEZCGHX.js +265 -0
  34. package/dist/chunk-46MMSDGL.js +194 -0
  35. package/dist/{chunk-7O2FMUPT.js → chunk-4IZXHVFJ.js} +297 -92
  36. package/dist/{chunk-LAECSR4H.js → chunk-5LAAAXFP.js} +14 -8
  37. package/dist/{chunk-2A5NIQAK.js → chunk-5RENVKBQ.js} +134 -46
  38. package/dist/{chunk-2ZZ7TDJ4.js → chunk-5UTRIXQ6.js} +37 -31
  39. package/dist/{chunk-LOE5YNN3.js → chunk-5VFZRGVD.js} +37 -10
  40. package/dist/{chunk-52X2VYTD.js → chunk-67IZZ2MJ.js} +115 -60
  41. package/dist/{chunk-UYG5L34B.js → chunk-6YAQBXB3.js} +10 -4
  42. package/dist/{chunk-BTTFCMKG.js → chunk-74FLKD24.js} +94 -23
  43. package/dist/{chunk-ITTYJZWX.js → chunk-7BUIKDD6.js} +73 -18
  44. package/dist/{chunk-423B7SPN.js → chunk-7OVVSVEZ.js} +273 -109
  45. package/dist/chunk-AEB7KCHQ.js +287 -0
  46. package/dist/chunk-APCWFOZW.js +28 -0
  47. package/dist/chunk-AWKQIE5I.js +139 -0
  48. package/dist/{chunk-VCDBXW5H.js → chunk-B7N4ASYE.js} +86 -42
  49. package/dist/{chunk-IVNRBPGC.js → chunk-C4GTDLRC.js} +104 -20
  50. package/dist/{chunk-4ZAHCY44.js → chunk-C6APNDVA.js} +12 -5
  51. package/dist/{chunk-4SZYY6AN.js → chunk-CMTVNF3D.js} +18 -13
  52. package/dist/{chunk-HZ6CNMAF.js → chunk-CPTOADB2.js} +151 -96
  53. package/dist/chunk-D5N5QWI7.js +1325 -0
  54. package/dist/{chunk-IIQTAY3J.js → chunk-DN7MNQIP.js} +113 -29
  55. package/dist/{chunk-47FWHGUN.js → chunk-DVGBZ3TX.js} +84 -23
  56. package/dist/chunk-DW3L54DQ.js +305 -0
  57. package/dist/{chunk-3ARE4TES.js → chunk-E2E3I5W2.js} +69 -18
  58. package/dist/{chunk-DHWADYZX.js → chunk-EDCBJV7J.js} +41 -28
  59. package/dist/{chunk-WSW6ZFAQ.js → chunk-ENP3BMZU.js} +4 -4
  60. package/dist/{chunk-OOPPQH27.js → chunk-EVOIGUBI.js} +43 -19
  61. package/dist/{chunk-VEAYTRPX.js → chunk-EY4DD67D.js} +165 -97
  62. package/dist/{chunk-223Q2T2Q.js → chunk-F3H33DQX.js} +132 -61
  63. package/dist/{chunk-CYRBHQQZ.js → chunk-F5B3UINI.js} +68 -35
  64. package/dist/{chunk-6X6PQ443.js → chunk-FAJI7BSC.js} +20 -9
  65. package/dist/{chunk-MOUJOP55.js → chunk-FUMHAAF3.js} +96 -29
  66. package/dist/{chunk-6HT2LP62.js → chunk-FV6HMP7W.js} +192 -73
  67. package/dist/{chunk-S7F6XLTK.js → chunk-FYWNRX4F.js} +76 -47
  68. package/dist/{chunk-YG3ZJHGU.js → chunk-FZGAAOKS.js} +30 -8
  69. package/dist/{chunk-YDGVJETP.js → chunk-G3AFC24V.js} +1 -1
  70. package/dist/{chunk-EJBDRVIH.js → chunk-G6UESEVN.js} +15 -8
  71. package/dist/chunk-GDKVKNR2.js +339 -0
  72. package/dist/chunk-GEIGMWNF.js +10 -0
  73. package/dist/{chunk-JUAMLEHX.js → chunk-H7DWSL4O.js} +24 -12
  74. package/dist/chunk-HJFFOOJ4.js +1 -0
  75. package/dist/{chunk-RW6ZL2FQ.js → chunk-HJLPZA4X.js} +106 -21
  76. package/dist/chunk-HYEOQWT7.js +647 -0
  77. package/dist/chunk-I2KLQ2HA.js +19 -0
  78. package/dist/{chunk-CJ5EOYZE.js → chunk-I4FHVKEC.js} +11 -5
  79. package/dist/{chunk-JNH2TYIN.js → chunk-IDM7JJMO.js} +19 -5
  80. package/dist/{chunk-O4AEHWII.js → chunk-IPUNJMAQ.js} +141 -49
  81. package/dist/{chunk-J7Q7VCVC.js → chunk-IT6FUXXG.js} +13 -6
  82. package/dist/{chunk-QJJ6MADV.js → chunk-IVKMZ7BB.js} +80 -28
  83. package/dist/{chunk-Z7H7VSFZ.js → chunk-IXKWIFWZ.js} +17 -6
  84. package/dist/chunk-J64KGCGX.js +55 -0
  85. package/dist/chunk-JBMLWFSJ.js +270 -0
  86. package/dist/{chunk-4BXMXBJO.js → chunk-JJLPBRVZ.js} +22 -11
  87. package/dist/chunk-JLC6PM5S.js +406 -0
  88. package/dist/chunk-JUYWSSKW.js +335 -0
  89. package/dist/{chunk-7CFF4HXS.js → chunk-JV5YOTOB.js} +49 -11
  90. package/dist/{chunk-LZLHELAG.js → chunk-KFJGFLMW.js} +29 -8
  91. package/dist/{chunk-O7AE6DAF.js → chunk-KQZ3GLHR.js} +18 -8
  92. package/dist/{chunk-N32RGOA5.js → chunk-LKFC4CS7.js} +80 -24
  93. package/dist/{chunk-YC6WORFL.js → chunk-LMPHCVJJ.js} +82 -50
  94. package/dist/{chunk-3WPW4KUN.js → chunk-LQP4KEXZ.js} +45 -13
  95. package/dist/{chunk-TKQYWZ5V.js → chunk-LUMVGO2K.js} +47 -31
  96. package/dist/{chunk-AWJOCGFZ.js → chunk-M656FYNH.js} +25 -13
  97. package/dist/{chunk-35OFI3JG.js → chunk-MKSNFG7H.js} +73 -20
  98. package/dist/{chunk-4GBOGOTV.js → chunk-NBEF4EPJ.js} +16 -6
  99. package/dist/chunk-NNCOFLNE.js +24 -0
  100. package/dist/{chunk-4YQOR7JM.js → chunk-NTQ2F3AU.js} +73 -20
  101. package/dist/{chunk-QCDXZBC5.js → chunk-O5V4TG3V.js} +126 -44
  102. package/dist/{chunk-U7TFM2JA.js → chunk-OJQUBQWA.js} +3 -3
  103. package/dist/{chunk-VQHPQEUH.js → chunk-OWAMM2DK.js} +13 -6
  104. package/dist/chunk-PID3NJB7.js +405 -0
  105. package/dist/{chunk-HET4LLMX.js → chunk-PMMTGBBS.js} +27 -17
  106. package/dist/{chunk-RIBDXGUG.js → chunk-PNF3NKB5.js} +49 -16
  107. package/dist/{chunk-JXJB2X6M.js → chunk-Q6NKEPRE.js} +81 -32
  108. package/dist/chunk-QDZGFXQD.js +176 -0
  109. package/dist/{chunk-SYNOJOQO.js → chunk-QEYFDRHR.js} +60 -26
  110. package/dist/{chunk-VBOXAEPI.js → chunk-QHAITQIF.js} +97 -22
  111. package/dist/{chunk-VXDZFJGP.js → chunk-QHTVYA65.js} +230 -76
  112. package/dist/{chunk-TUTI5HOZ.js → chunk-QIKT7LIB.js} +14 -8
  113. package/dist/{chunk-3ENTMPQY.js → chunk-QP3KMOVF.js} +82 -42
  114. package/dist/{chunk-GNNJ4GCC.js → chunk-QPWDDJJ4.js} +62 -148
  115. package/dist/{chunk-QWCMU5B2.js → chunk-QRUMA22E.js} +205 -80
  116. package/dist/{chunk-ZVFE5QGU.js → chunk-QYPE5JX3.js} +50 -10
  117. package/dist/{chunk-PUPM2YGF.js → chunk-ROCUG24E.js} +76 -36
  118. package/dist/{chunk-NQYBUO3K.js → chunk-RU3ZPNL6.js} +333 -142
  119. package/dist/{chunk-TOXAGRDQ.js → chunk-RXSCIO6W.js} +14 -5
  120. package/dist/{chunk-FMYDDECK.js → chunk-SATMR6SG.js} +631 -203
  121. package/dist/{chunk-4YIP3CWS.js → chunk-SHHY5OMK.js} +7 -5
  122. package/dist/chunk-SKWO3SZF.js +2284 -0
  123. package/dist/{chunk-YF4NSSMT.js → chunk-SWS5JQIF.js} +41 -13
  124. package/dist/{chunk-CUMUNPXY.js → chunk-T5LTPKX6.js} +15 -7
  125. package/dist/{chunk-H6NIOQHK.js → chunk-TDMG26JI.js} +26 -6
  126. package/dist/{chunk-UWFLYMAR.js → chunk-TLCIR6RG.js} +267 -126
  127. package/dist/{chunk-SBMNWWBV.js → chunk-TN2ZNJUI.js} +47 -9
  128. package/dist/{chunk-5YNTNN5A.js → chunk-TQVAIKFD.js} +106 -41
  129. package/dist/chunk-TRGFFU6A.js +1456 -0
  130. package/dist/{chunk-2GK6PHGE.js → chunk-TY4MTKQJ.js} +100 -22
  131. package/dist/{chunk-AZRX6SAC.js → chunk-U23FYWME.js} +15 -7
  132. package/dist/{chunk-PZXZJBMP.js → chunk-U5NLHYV6.js} +26 -12
  133. package/dist/chunk-UQEN55EY.js +1 -0
  134. package/dist/chunk-UQGBTPXH.js +218 -0
  135. package/dist/chunk-UXGIW7OK.js +116 -0
  136. package/dist/{chunk-PJXUJYS6.js → chunk-UYGDJWMN.js} +55 -30
  137. package/dist/{chunk-QDYDBCFP.js → chunk-V2OEB75Z.js} +50 -51
  138. package/dist/{chunk-LSS54GLJ.js → chunk-VAOAKWUK.js} +34 -13
  139. package/dist/{chunk-DM5QUKKO.js → chunk-VDPK5CY4.js} +18 -6
  140. package/dist/chunk-W23G6JLF.js +37 -0
  141. package/dist/{chunk-CDRZHT42.js → chunk-W63AJTM7.js} +276 -12
  142. package/dist/{chunk-XXXEFXY5.js → chunk-W74TXYFB.js} +13 -5
  143. package/dist/{chunk-LRJNDEIA.js → chunk-WRDSPUE7.js} +144 -29
  144. package/dist/{chunk-C2NTXVWM.js → chunk-XBZGDDMR.js} +11 -4
  145. package/dist/{chunk-BV3B2HYZ.js → chunk-XIDTBCBA.js} +189 -131
  146. package/dist/{chunk-YTFKPRUK.js → chunk-XN4KG3P4.js} +348 -153
  147. package/dist/chunk-XQXMLMH3.js +211 -0
  148. package/dist/{chunk-HPPW424V.js → chunk-XTORZWRF.js} +19 -8
  149. package/dist/{chunk-P23EQNRQ.js → chunk-YAAHPATV.js} +181 -30
  150. package/dist/{chunk-KMI5ML5I.js → chunk-YXOAPXMU.js} +57 -18
  151. package/dist/{chunk-RJQOOHT2.js → chunk-ZECZOOSB.js} +59 -28
  152. package/dist/{chunk-QKXCPTFS.js → chunk-ZLTO5VQI.js} +121 -31
  153. package/dist/{chunk-BEDBFJND.js → chunk-ZVATDZOM.js} +16 -18
  154. package/dist/coachmark/index.js +6 -3
  155. package/dist/code-block/index.js +5 -3
  156. package/dist/color-field/index.js +6 -4
  157. package/dist/commerce/index.js +19 -16
  158. package/dist/connectivity/index.js +5 -2
  159. package/dist/context-menu/index.js +5 -3
  160. package/dist/copy-button/index.js +4 -2
  161. package/dist/data-grid/index.js +21 -16
  162. package/dist/date-range-picker/index.js +11 -8
  163. package/dist/description-list/index.js +3 -1
  164. package/dist/dialog/index.js +4 -2
  165. package/dist/empty-state/index.js +4 -2
  166. package/dist/error-summary/index.js +4 -2
  167. package/dist/fab/index.js +5 -2
  168. package/dist/file-manager/index.js +28 -0
  169. package/dist/file-upload/index.js +10 -7
  170. package/dist/filter-bar/index.js +9 -6
  171. package/dist/flow-run/index.js +15 -12
  172. package/dist/form/index.js +5 -3
  173. package/dist/gallery/index.js +5 -2
  174. package/dist/hold-to-confirm/index.js +4 -2
  175. package/dist/index.d.ts +9557 -2985
  176. package/dist/index.js +355 -122
  177. package/dist/infinite-list/index.js +4 -2
  178. package/dist/inline-edit/index.js +6 -4
  179. package/dist/input/index.js +5 -3
  180. package/dist/interactions/index.js +2 -1
  181. package/dist/invoice/index.js +13 -9
  182. package/dist/kbd/index.js +5 -1
  183. package/dist/keyboard/index.js +4 -2
  184. package/dist/list-row/index.js +6 -4
  185. package/dist/log-viewer/index.js +4 -2
  186. package/dist/map-view/index.js +5 -3
  187. package/dist/markdown/index.js +9 -6
  188. package/dist/metric/index.js +7 -4
  189. package/dist/money/index.js +9 -3
  190. package/dist/nav-drawer/index.js +4 -2
  191. package/dist/nav-header/index.js +9 -0
  192. package/dist/navigation/index.js +5 -2
  193. package/dist/notification-center/index.js +12 -10
  194. package/dist/number-roll/index.js +5 -2
  195. package/dist/one-handed/index.js +5 -3
  196. package/dist/os-surfaces/index.js +6 -3
  197. package/dist/otp/index.js +7 -4
  198. package/dist/page-header/index.js +1 -0
  199. package/dist/pagination/index.js +6 -0
  200. package/dist/payment-method/index.js +14 -10
  201. package/dist/payment-status/index.js +17 -14
  202. package/dist/permission/index.js +5 -2
  203. package/dist/picker/index.js +11 -6
  204. package/dist/progress/index.js +6 -3
  205. package/dist/pull-to-refresh/index.js +4 -2
  206. package/dist/radio-group/index.js +6 -4
  207. package/dist/rating/index.js +5 -2
  208. package/dist/receivables/index.js +21 -16
  209. package/dist/refund/index.js +18 -14
  210. package/dist/save-status/index.js +5 -3
  211. package/dist/scroll-area/index.js +3 -1
  212. package/dist/search-field/index.js +8 -0
  213. package/dist/segmented-control/index.js +4 -3
  214. package/dist/sheet/index.js +5 -3
  215. package/dist/skeleton/index.js +3 -1
  216. package/dist/slider/index.js +4 -2
  217. package/dist/sortable/index.js +7 -0
  218. package/dist/spinner/index.js +3 -1
  219. package/dist/split-button/index.js +9 -6
  220. package/dist/spotlight/index.js +3 -1
  221. package/dist/stage-timeline/index.js +5 -3
  222. package/dist/status-dot/index.js +4 -2
  223. package/dist/stepper/index.js +7 -4
  224. package/dist/streaming-text/index.js +3 -2
  225. package/dist/survey/index.js +18 -15
  226. package/dist/switch/index.js +5 -3
  227. package/dist/system-state/index.js +7 -4
  228. package/dist/tab-bar/index.js +5 -2
  229. package/dist/tabs/index.js +4 -2
  230. package/dist/tags-input/index.js +10 -0
  231. package/dist/textarea/index.js +6 -4
  232. package/dist/thinking/index.js +3 -1
  233. package/dist/time-field/index.js +12 -0
  234. package/dist/time-range-control/index.js +22 -0
  235. package/dist/timeline/index.js +4 -2
  236. package/dist/toast/index.js +4 -2
  237. package/dist/toolbar/index.js +4 -2
  238. package/dist/tour/index.js +12 -9
  239. package/dist/truncation-notice/index.js +5 -2
  240. package/dist/update-notice/index.js +12 -9
  241. package/dist/video-player/index.js +5 -2
  242. package/dist/viewfinder/index.js +6 -4
  243. package/dist/widget/index.js +7 -4
  244. package/dist/wizard/index.js +7 -4
  245. package/package.json +13 -12
  246. package/dist/chunk-6N33LFQA.js +0 -35
  247. package/dist/chunk-GDKZTW34.js +0 -395
  248. package/dist/chunk-GL7B2DOW.js +0 -134
  249. package/dist/chunk-NKB7BZYK.js +0 -167
  250. package/dist/chunk-OETCUXQP.js +0 -134
  251. package/dist/chunk-OPXFZ4Y5.js +0 -204
  252. package/dist/chunk-Q72LJOVE.js +0 -169
  253. package/dist/chunk-RNGLIHFA.js +0 -191
  254. package/dist/chunk-RQ3HASCC.js +0 -6
  255. package/dist/chunk-RSOJT5YW.js +0 -124
  256. package/dist/chunk-TKVFVF5D.js +0 -95
  257. package/dist/chunk-XOSCY2CW.js +0 -242
  258. package/dist/header/index.js +0 -5
  259. package/dist/search-bar/index.js +0 -5
  260. /package/dist/{chunk-3ECPOSKO.js → chunk-3UG6JUCV.js} +0 -0
  261. /package/dist/{chunk-JRLTGLN4.js → chunk-5WWRTEPS.js} +0 -0
  262. /package/dist/{chunk-RTIKSY6A.js → chunk-5Z5ZYMOJ.js} +0 -0
@@ -1,48 +1,69 @@
1
- import { Thinking } from './chunk-XXXEFXY5.js';
2
- import { Icon } from './chunk-TKVFVF5D.js';
1
+ import { Thinking } from './chunk-W74TXYFB.js';
2
+ import { useLabels } from './chunk-APCWFOZW.js';
3
+ import { Icon } from './chunk-UXGIW7OK.js';
3
4
  import * as React from 'react';
4
5
  import { Animated, Easing, View, Text, Pressable } from 'react-native';
5
6
  import { createStyleSheet, useStyles, useLoamTheme } from '@btndprojects/loam-theme-native';
6
7
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
8
 
8
9
  var STATE_META = {
9
- idle: { icon: "check-circle", text: "Up to date", tone: "muted" },
10
- typing: { icon: "edit", text: "Editing\u2026", tone: "muted" },
11
- saving: { icon: "loader", text: "Saving\u2026", tone: "muted", spin: true },
12
- saved: { icon: "check", text: "Saved", tone: "good" },
13
- offline: { icon: "cloud-off", text: "Offline \u2014 changes queued", tone: "warn" },
14
- error: { icon: "alert-triangle", text: "Couldn't save", tone: "danger" }
10
+ idle: { icon: "check-circle", tone: "muted" },
11
+ typing: { icon: "edit", tone: "muted" },
12
+ saving: { icon: "loader", tone: "muted", spin: true },
13
+ saved: { icon: "check", tone: "good" },
14
+ offline: { icon: "cloud-off", tone: "warn" },
15
+ error: { icon: "alert-triangle", tone: "danger" }
15
16
  };
16
- function formatSavedAt(value) {
17
+ var DEFAULT_LABELS = {
18
+ states: {
19
+ idle: "Up to date",
20
+ typing: "Editing\u2026",
21
+ saving: "Saving\u2026",
22
+ saved: "Saved",
23
+ offline: "Offline \u2014 changes queued",
24
+ error: "Couldn't save"
25
+ },
26
+ savedAt: (ago) => `Saved \xB7 ${ago}`,
27
+ offlineQueued: (count) => `Offline \u2014 ${count} ${count === 1 ? "change" : "changes"} queued`,
28
+ justNow: "just now",
29
+ minutesAgo: (n) => `${n}m ago`,
30
+ hoursAgo: (n) => `${n}h ago`,
31
+ daysAgo: (n) => `${n}d ago`,
32
+ retry: "Retry"
33
+ };
34
+ function formatSavedAt(value, labels) {
17
35
  const then = typeof value === "number" ? value : value.getTime();
18
36
  const diff = Date.now() - then;
19
- if (!Number.isFinite(then) || diff < 0) return "just now";
37
+ if (!Number.isFinite(then) || diff < 0) return labels.justNow;
20
38
  const sec = Math.floor(diff / 1e3);
21
- if (sec < 45) return "just now";
39
+ if (sec < 45) return labels.justNow;
22
40
  const min = Math.floor(sec / 60);
23
- if (min < 60) return `${min}m ago`;
41
+ if (min < 60) return labels.minutesAgo(min);
24
42
  const hr = Math.floor(min / 60);
25
- if (hr < 24) return `${hr}h ago`;
26
- return `${Math.floor(hr / 24)}d ago`;
43
+ if (hr < 24) return labels.hoursAgo(hr);
44
+ return labels.daysAgo(Math.floor(hr / 24));
27
45
  }
28
46
  function SaveStatus({
29
47
  status,
30
48
  savedAt,
49
+ queued,
31
50
  onRetry,
32
51
  variant = "inline",
33
52
  size = "md",
34
53
  motion = "full",
35
54
  accessibilityLabel,
55
+ labels: labelsProp,
36
56
  style,
37
57
  testID
38
58
  }) {
59
+ const labels = useLabels("saveStatus", DEFAULT_LABELS, labelsProp);
39
60
  const { styles } = useStyles(sheet, { variant, size, tone: STATE_META[status].tone });
40
- const theme = useLoamTheme();
61
+ const theme = useLoamTheme("save-status");
41
62
  const meta = STATE_META[status];
42
63
  const tone = meta.tone === "good" ? theme.color.good : meta.tone === "warn" ? theme.color.warn : meta.tone === "danger" ? theme.color.danger : theme.color.text2;
43
64
  const iconSize = size === "sm" ? theme.icon.xs : theme.icon.sm;
44
65
  const animate = motion === "full" && !theme.reduceMotion;
45
- const label = status === "saved" && savedAt != null ? `Saved \xB7 ${formatSavedAt(savedAt)}` : meta.text;
66
+ const label = status === "saved" && savedAt != null ? labels.savedAt(formatSavedAt(savedAt, labels)) : status === "offline" && queued != null && queued > 0 ? labels.offlineQueued(queued) : labels.states[status];
46
67
  const chipPad = variant === "toolbar" ? {
47
68
  paddingHorizontal: size === "sm" ? theme.space.s2 : theme.space.s3,
48
69
  paddingVertical: size === "sm" ? theme.space.s1 : theme.space.s2
@@ -102,13 +123,16 @@ function SaveStatus({
102
123
  Pressable,
103
124
  {
104
125
  accessibilityRole: "button",
105
- accessibilityLabel: "Retry",
126
+ accessibilityLabel: labels.retry,
106
127
  onPress: onRetry,
107
128
  hitSlop: 8,
108
- style: ({ pressed }) => [styles.retry, pressed ? { opacity: theme.state.pressedOpacity } : null],
129
+ style: ({ pressed }) => [
130
+ styles.retry,
131
+ pressed ? { opacity: theme.state.pressedOpacity } : null
132
+ ],
109
133
  children: [
110
134
  /* @__PURE__ */ jsx(Icon, { name: "refresh", size: iconSize, color: theme.color.danger }),
111
- /* @__PURE__ */ jsx(Text, { style: styles.retryLabel, children: "Retry" })
135
+ /* @__PURE__ */ jsx(Text, { style: styles.retryLabel, children: labels.retry })
112
136
  ]
113
137
  }
114
138
  ) : null
@@ -1,11 +1,32 @@
1
- import { Card, CardContent } from './chunk-KMI5ML5I.js';
2
- import { expandedProps } from './chunk-RQ3HASCC.js';
3
- import { Icon } from './chunk-TKVFVF5D.js';
1
+ import { useReorder, ROOT_SCROLLER } from './chunk-36EC2T6P.js';
2
+ import { Card, CardContent } from './chunk-YXOAPXMU.js';
3
+ import { expandedProps } from './chunk-W23G6JLF.js';
4
+ import { useLabels } from './chunk-APCWFOZW.js';
5
+ import { Icon } from './chunk-UXGIW7OK.js';
4
6
  import * as React from 'react';
5
7
  import { View, ScrollView, Text, Pressable, Animated } from 'react-native';
6
8
  import { createStyleSheet, useStyles, useLoamTheme } from '@btndprojects/loam-theme-native';
7
- import { jsxs, jsx } from 'react/jsx-runtime';
9
+ import { jsx, jsxs } from 'react/jsx-runtime';
8
10
 
11
+ var DEFAULT_LABELS = {
12
+ board: "Board",
13
+ noCards: "No cards",
14
+ moveUp: "Move up",
15
+ moveDown: "Move down",
16
+ movePrev: "Move to previous column",
17
+ moveNext: "Move to next column",
18
+ expand: (column, count) => `Expand ${column}, ${count} cards`,
19
+ collapse: (column) => `Collapse ${column}`,
20
+ columnName: (column, count) => `${column}, ${count} cards`,
21
+ cardList: (column) => `${column} cards`,
22
+ cardHint: (position, total, column) => `Position ${position} of ${total} in ${column}. Press and hold to drag, or use the arrow keys or actions to move it.`,
23
+ moved: (card, column, position, total) => `Moved ${card} to ${column}, position ${position} of ${total}.`,
24
+ overLimit: (column, count, limit) => ` ${column} is over its limit, ${count} of ${limit}.`,
25
+ movedNotice: (card) => `Moved ${card}`,
26
+ restored: (card, column) => `Restored ${card} to ${column}.`,
27
+ pickedUp: (card) => `Picked up ${card}.`,
28
+ droppedBack: (card) => `${card} dropped where it started.`
29
+ };
9
30
  function clamp(value, min, max) {
10
31
  return Math.min(max, Math.max(min, value));
11
32
  }
@@ -14,12 +35,16 @@ function Board({
14
35
  renderCard,
15
36
  onMoveCard,
16
37
  onToggleColumn,
17
- label = "Board",
38
+ label: labelProp,
18
39
  motion = "full",
19
- style
40
+ onNotify,
41
+ style,
42
+ labels: labelsProp
20
43
  }) {
44
+ const labels = useLabels("board", DEFAULT_LABELS, labelsProp);
45
+ const label = labelProp ?? labels.board;
21
46
  const { styles } = useStyles(sheet);
22
- const theme = useLoamTheme();
47
+ const theme = useLoamTheme("board");
23
48
  const [cols, setCols] = React.useState(columns);
24
49
  React.useEffect(() => setCols(columns), [columns]);
25
50
  const [collapsed, setCollapsed] = React.useState(
@@ -38,7 +63,6 @@ function Board({
38
63
  if (w > 0) setPageW(w);
39
64
  };
40
65
  const [message, setMessage] = React.useState("");
41
- const lift = motion === "full" && !theme.reduceMotion;
42
66
  const locate = (cardId) => {
43
67
  for (let c = 0; c < cols.length; c += 1) {
44
68
  const r = cols[c].items.findIndex((it) => it.id === cardId);
@@ -62,10 +86,64 @@ function Board({
62
86
  setCols(next);
63
87
  onMoveCard?.(cardId, fromColumnId, toColumnId, target);
64
88
  const destCol = next[toCol];
65
- const over = destCol.wipLimit !== void 0 && destCol.items.length > destCol.wipLimit ? ` ${destCol.title} is over its limit, ${destCol.items.length} of ${destCol.wipLimit}.` : "";
89
+ const over = destCol.wipLimit !== void 0 && destCol.items.length > destCol.wipLimit ? labels.overLimit(destCol.title, destCol.items.length, destCol.wipLimit) : "";
66
90
  setMessage(
67
- `Moved ${card.title} to ${destCol.title}, position ${target + 1} of ${destCol.items.length}.${over}`
91
+ labels.moved(card.title, destCol.title, target + 1, destCol.items.length) + over
68
92
  );
93
+ if (onNotify) {
94
+ const before = cols;
95
+ onNotify({
96
+ text: labels.movedNotice(card.title),
97
+ undo: () => {
98
+ setCols(before);
99
+ onMoveCard?.(cardId, toColumnId, fromColumnId, from.row);
100
+ setMessage(labels.restored(card.title, before[from.col].title));
101
+ }
102
+ });
103
+ }
104
+ };
105
+ const findCard = (cardId) => {
106
+ const at = locate(cardId);
107
+ return at ? { card: cols[at.col].items[at.row], column: cols[at.col] } : null;
108
+ };
109
+ const reorder = useReorder({
110
+ motion,
111
+ tilt: false,
112
+ // The default face is a `Card`, cornered at `radius.xl`.
113
+ radius: theme.radius.xl,
114
+ renderGhost: (cardId) => {
115
+ const found = findCard(cardId);
116
+ if (!found) return null;
117
+ return renderCard ? renderCard(found.card, found.column) : /* @__PURE__ */ jsx(CardBody, { card: found.card, lifted: true });
118
+ },
119
+ onLift: (cardId) => {
120
+ const found = findCard(cardId);
121
+ if (found) setMessage(labels.pickedUp(found.card.title));
122
+ },
123
+ // `to` counts without the card; within a column `moveCard` expects the
124
+ // index before it was taken out.
125
+ onMove: ({ id, fromGroup, toGroup, from, to }) => moveCard(id, toGroup, fromGroup === toGroup && to > from ? to + 1 : to),
126
+ onCancel: (cardId) => {
127
+ const found = findCard(cardId);
128
+ if (found) setMessage(labels.droppedBack(found.card.title));
129
+ }
130
+ });
131
+ const step = (cardId, dir) => {
132
+ const at = locate(cardId);
133
+ if (!at) return false;
134
+ const { col, row } = at;
135
+ if (dir === "up" || dir === "down") {
136
+ const to = dir === "up" ? row - 1 : row + 2;
137
+ if (to < 0 || to > cols[col].items.length) return false;
138
+ reorder.animate(() => moveCard(cardId, cols[col].id, to));
139
+ return true;
140
+ }
141
+ const dest = adjacentColumn(col, dir === "prev" ? -1 : 1);
142
+ if (dest < 0) return false;
143
+ reorder.animate(
144
+ () => moveCard(cardId, cols[dest].id, Math.min(row, cols[dest].items.length))
145
+ );
146
+ return true;
69
147
  };
70
148
  const toggleCollapsed = (columnId) => {
71
149
  setCollapsed((prev) => {
@@ -81,27 +159,14 @@ function Board({
81
159
  return -1;
82
160
  };
83
161
  const handleCardKey = (event, cardId) => {
84
- const at = locate(cardId);
85
- if (!at) return;
86
- const { col, row } = at;
87
- if (event.key === "ArrowUp") {
88
- event.preventDefault?.();
89
- moveCard(cardId, cols[col].id, row - 1);
90
- } else if (event.key === "ArrowDown") {
91
- event.preventDefault?.();
92
- moveCard(cardId, cols[col].id, row + 1);
93
- } else if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
94
- const dir = event.key === "ArrowLeft" !== theme.rtl ? -1 : 1;
95
- const dest = adjacentColumn(col, dir);
96
- if (dest < 0) return;
97
- event.preventDefault?.();
98
- moveCard(cardId, cols[dest].id, Math.min(row, cols[dest].items.length));
99
- }
162
+ const dir = event.key === "ArrowUp" ? "up" : event.key === "ArrowDown" ? "down" : event.key === "ArrowLeft" || event.key === "ArrowRight" ? event.key === "ArrowLeft" !== theme.rtl ? "prev" : "next" : null;
163
+ if (dir && step(cardId, dir)) event.preventDefault?.();
100
164
  };
101
165
  return /* @__PURE__ */ jsxs(View, { role: "group", accessibilityLabel: label, onLayout, style: [styles.root, style], children: [
102
166
  /* @__PURE__ */ jsx(
103
167
  ScrollView,
104
168
  {
169
+ ...reorder.scroller(ROOT_SCROLLER, { horizontal: true, page: pageW + theme.space.s3 }),
105
170
  horizontal: true,
106
171
  pagingEnabled: true,
107
172
  showsHorizontalScrollIndicator: false,
@@ -114,15 +179,18 @@ function Board({
114
179
  column,
115
180
  width: pageW,
116
181
  collapsed: collapsed[column.id] ?? false,
117
- lift,
182
+ reorder,
118
183
  renderCard,
119
184
  onToggle: () => toggleCollapsed(column.id),
120
- onCardKey: handleCardKey
185
+ onCardKey: handleCardKey,
186
+ onStep: step,
187
+ labels: labelsProp
121
188
  },
122
189
  column.id
123
190
  ))
124
191
  }
125
192
  ),
193
+ reorder.overlay,
126
194
  /* @__PURE__ */ jsx(
127
195
  View,
128
196
  {
@@ -139,13 +207,16 @@ function Column({
139
207
  column,
140
208
  width,
141
209
  collapsed,
142
- lift,
210
+ reorder,
143
211
  renderCard,
144
212
  onToggle,
145
- onCardKey
213
+ onCardKey,
214
+ onStep,
215
+ labels: labelsProp
146
216
  }) {
147
217
  const { styles } = useStyles(sheet);
148
- const theme = useLoamTheme();
218
+ const theme = useLoamTheme("board");
219
+ const labels = useLabels("board", DEFAULT_LABELS, labelsProp);
149
220
  const count = column.items.length;
150
221
  const over = column.wipLimit !== void 0 && count > column.wipLimit;
151
222
  const countLabel = column.wipLimit !== void 0 ? `${count} / ${column.wipLimit}` : `${count}`;
@@ -154,7 +225,7 @@ function Column({
154
225
  Pressable,
155
226
  {
156
227
  accessibilityRole: "button",
157
- accessibilityLabel: `Expand ${column.title}, ${count} cards`,
228
+ accessibilityLabel: labels.expand(column.title, count),
158
229
  accessibilityState: { expanded: false },
159
230
  ...expandedProps(false),
160
231
  onPress: onToggle,
@@ -174,7 +245,7 @@ function Column({
174
245
  View,
175
246
  {
176
247
  role: "group",
177
- accessibilityLabel: `${column.title}, ${count} cards`,
248
+ accessibilityLabel: labels.columnName(column.title, count),
178
249
  style: styles.column,
179
250
  children: [
180
251
  /* @__PURE__ */ jsxs(View, { style: styles.header, children: [
@@ -182,7 +253,7 @@ function Column({
182
253
  Pressable,
183
254
  {
184
255
  accessibilityRole: "button",
185
- accessibilityLabel: `Collapse ${column.title}`,
256
+ accessibilityLabel: labels.collapse(column.title),
186
257
  accessibilityState: { expanded: true },
187
258
  ...expandedProps(true),
188
259
  onPress: onToggle,
@@ -203,14 +274,24 @@ function Column({
203
274
  /* @__PURE__ */ jsx(
204
275
  ScrollView,
205
276
  {
277
+ ...reorder.scroller(column.id),
206
278
  role: "list",
207
- contentContainerStyle: styles.list,
279
+ style: styles.listScroll,
280
+ contentContainerStyle: styles.listPad,
208
281
  showsVerticalScrollIndicator: false,
209
- ...{ "aria-label": `${column.title} cards` },
210
- children: count === 0 ? (
211
- // The empty placeholder is the sole child of the `role="list"`, so it
212
- // is a `listitem` too (a list may contain only listitems).
213
- /* @__PURE__ */ jsx(View, { role: "listitem", style: styles.empty, children: /* @__PURE__ */ jsx(Text, { style: styles.emptyText, children: "No cards" }) })
282
+ ...{ "aria-label": labels.cardList(column.title) },
283
+ children: /* @__PURE__ */ jsx(View, { ...reorder.group(column.id), style: styles.list, children: count === 0 ? (
284
+ // The empty placeholder is the sole item of the `role="list"`, so it
285
+ // is a `listitem` too (a list may contain only listitems). While a
286
+ // card hovers this column the slot takes its place.
287
+ /* @__PURE__ */ jsx(
288
+ View,
289
+ {
290
+ role: "listitem",
291
+ style: [styles.empty, reorder.over === column.id && styles.emptyHidden],
292
+ children: /* @__PURE__ */ jsx(Text, { style: styles.emptyText, children: labels.noCards })
293
+ }
294
+ )
214
295
  ) : column.items.map((card, row) => /* @__PURE__ */ jsx(
215
296
  CardCell,
216
297
  {
@@ -218,79 +299,69 @@ function Column({
218
299
  column,
219
300
  index: row,
220
301
  total: count,
221
- lift,
302
+ reorder,
222
303
  renderCard,
223
- onCardKey
304
+ onCardKey,
305
+ onStep,
306
+ labels: labelsProp
224
307
  },
225
308
  card.id
226
- ))
309
+ )) })
227
310
  }
228
311
  )
229
312
  ]
230
313
  }
231
314
  ) });
232
315
  }
316
+ var CARD_ACTION_NAMES = ["moveUp", "moveDown", "movePrev", "moveNext"];
317
+ var ACTION_DIR = { moveUp: "up", moveDown: "down", movePrev: "prev", moveNext: "next" };
318
+ function CardBody({
319
+ card,
320
+ lifted = false
321
+ }) {
322
+ const { styles } = useStyles(sheet);
323
+ return /* @__PURE__ */ jsx(Card, { variant: "default", dragging: lifted, motion: "none", children: /* @__PURE__ */ jsxs(CardContent, { style: styles.cardBody, children: [
324
+ /* @__PURE__ */ jsx(Text, { style: styles.cardTitle, children: card.title }),
325
+ card.description ? /* @__PURE__ */ jsx(Text, { style: styles.cardDesc, children: card.description }) : null
326
+ ] }) });
327
+ }
233
328
  function CardCell({
234
329
  card,
235
330
  column,
236
331
  index,
237
332
  total,
238
- lift,
333
+ reorder,
239
334
  renderCard,
240
- onCardKey
335
+ onCardKey,
336
+ onStep,
337
+ labels: labelsProp
241
338
  }) {
242
339
  const { styles } = useStyles(sheet);
243
- const theme = useLoamTheme();
244
- const [grabbed, setGrabbed] = React.useState(false);
245
- const progress = React.useRef(new Animated.Value(0)).current;
246
- const spring = (to) => {
247
- Animated.spring(progress, {
248
- toValue: to,
249
- useNativeDriver: true,
250
- speed: 40,
251
- bounciness: 6
252
- }).start();
253
- };
254
- const scale = lift ? progress.interpolate({ inputRange: [0, 1], outputRange: [1, 1.022] }) : 1;
255
- const rotate = lift ? progress.interpolate({
256
- inputRange: [0, 1],
257
- // `--mo-tilt` is 1.2deg; mirror its sign under RTL.
258
- outputRange: ["0deg", theme.rtl ? "-1.2deg" : "1.2deg"]
259
- }) : "0deg";
340
+ const theme = useLoamTheme("board");
341
+ const labels = useLabels("board", DEFAULT_LABELS, labelsProp);
342
+ const cardActions = CARD_ACTION_NAMES.map((name) => ({ name, label: labels[name] }));
260
343
  const keyProps = {
261
344
  onKeyDown: (event) => onCardKey(event, card.id)
262
345
  };
346
+ const onAction = (event) => {
347
+ const dir = ACTION_DIR[event.nativeEvent.actionName];
348
+ if (dir) onStep(card.id, dir);
349
+ };
263
350
  return (
264
351
  // The card cell is the list item — the column body is a `role="list"`, so its
265
352
  // direct children must each be a `listitem` (aria-required-children).
266
- /* @__PURE__ */ jsx(Animated.View, { role: "listitem", style: { transform: [{ scale }, { rotate }] }, children: /* @__PURE__ */ jsx(
353
+ /* @__PURE__ */ jsx(Animated.View, { role: "listitem", ...reorder.item(card.id, column.id, index), children: /* @__PURE__ */ jsx(
267
354
  Pressable,
268
355
  {
356
+ ...reorder.press(card.id),
269
357
  accessibilityRole: "button",
270
358
  accessibilityLabel: card.title,
271
- accessibilityHint: "Use the arrow keys to move this card between and within columns",
272
- onPressIn: () => {
273
- setGrabbed(true);
274
- spring(1);
275
- },
276
- onPressOut: () => {
277
- setGrabbed(false);
278
- spring(0);
279
- },
280
- style: [styles.card, grabbed && styles.cardGrabbed],
359
+ accessibilityHint: labels.cardHint(index + 1, total, column.title),
360
+ accessibilityActions: cardActions,
361
+ onAccessibilityAction: onAction,
362
+ style: ({ pressed }) => [styles.card, pressed && { opacity: theme.state.pressedOpacity }],
281
363
  ...keyProps,
282
- children: renderCard ? renderCard(card, column) : (
283
- // `dragging` is what actually paints the grabbed state: `Card` renders
284
- // `theme.state.dragged` as an `absoluteFill` overlay ON TOP of its own
285
- // opaque surface, which a `backgroundColor` on this wrapper could never
286
- // do (the Card paints straight over it) — the same reason the web twin
287
- // uses an overlay span. `motion="none"` because the lift/tilt is already
288
- // animated by the `Animated.View` above; `Card`'s own would double it.
289
- /* @__PURE__ */ jsx(Card, { variant: "default", dragging: grabbed, motion: "none", children: /* @__PURE__ */ jsxs(CardContent, { style: styles.cardBody, children: [
290
- /* @__PURE__ */ jsx(Text, { style: styles.cardTitle, children: card.title }),
291
- card.description ? /* @__PURE__ */ jsx(Text, { style: styles.cardDesc, children: card.description }) : null
292
- ] }) })
293
- )
364
+ children: renderCard ? renderCard(card, column) : /* @__PURE__ */ jsx(CardBody, { card })
294
365
  }
295
366
  ) })
296
367
  );
@@ -347,7 +418,13 @@ var sheet = createStyleSheet((theme) => ({
347
418
  fontSize: theme.type.caption1.fontSize,
348
419
  color: theme.color.warnOnSoft
349
420
  },
350
- list: { gap: theme.space.s2, paddingBottom: theme.space.s1 },
421
+ // A scroller clips its content, and a card's shadow reaches ~14px past it.
422
+ // Bleed the scroller out over the column's padding (and the header gap) and
423
+ // pad it back in, so the shadows fade out inside the clip instead of being
424
+ // cut into a hard-edged box.
425
+ listScroll: { margin: -theme.space.s3 },
426
+ listPad: { padding: theme.space.s3 },
427
+ list: { gap: theme.space.s2 },
351
428
  empty: {
352
429
  borderRadius: theme.radius.lg,
353
430
  borderWidth: theme.borderWidth.base,
@@ -356,22 +433,13 @@ var sheet = createStyleSheet((theme) => ({
356
433
  paddingVertical: theme.space.s5,
357
434
  alignItems: "center"
358
435
  },
436
+ emptyHidden: { opacity: 0 },
359
437
  emptyText: {
360
438
  fontFamily: theme.font.sans,
361
439
  fontSize: theme.type.footnote.fontSize,
362
440
  color: theme.color.text3
363
441
  },
364
- card: { borderRadius: theme.radius.lg },
365
- // The grabbed card raises to the stronger shadow — the "picked up" read. The
366
- // `--state-dragged` tint is NOT set here: this wrapper sits behind the card,
367
- // whose surface is opaque, so a `backgroundColor` on it was never visible.
368
- // `Card`'s `dragging` prop composites the tint over the surface instead.
369
- // Under reduced motion there is no scale/tilt, but the state still changes so
370
- // the gesture is not silent.
371
- cardGrabbed: {
372
- ...theme.shadow.lg,
373
- borderRadius: theme.radius.lg
374
- },
442
+ card: { borderRadius: theme.radius.xl },
375
443
  cardBody: { padding: theme.space.s3, gap: theme.space.s1 },
376
444
  cardTitle: {
377
445
  fontFamily: theme.font.sans,