@ecoma-io/loom 0.1.0 → 0.2.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 (495) hide show
  1. package/README.md +54 -57
  2. package/dist/blocks/AppHeader/AppHeader.js.map +1 -1
  3. package/dist/blocks/AppHeader/AppHeader.vue.d.ts +5 -6
  4. package/dist/blocks/AppHeader/AppHeader.vue.d.ts.map +1 -1
  5. package/dist/blocks/AppHeader/AppHeader.vue_vue_type_script_setup_true_lang.js +6 -7
  6. package/dist/blocks/AppHeader/AppHeader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  7. package/dist/blocks/DashboardGrid/DashboardGrid.vue.d.ts +1 -1
  8. package/dist/blocks/DashboardGrid/DashboardGrid.vue.d.ts.map +1 -1
  9. package/dist/blocks/DesktopAppShell/DesktopAppShell.js +7 -0
  10. package/dist/blocks/DesktopAppShell/DesktopAppShell.js.map +1 -0
  11. package/dist/blocks/DesktopAppShell/DesktopAppShell.vue.d.ts +102 -0
  12. package/dist/blocks/DesktopAppShell/DesktopAppShell.vue.d.ts.map +1 -0
  13. package/dist/blocks/DesktopAppShell/DesktopAppShell.vue_vue_type_script_setup_true_lang.js +74 -0
  14. package/dist/blocks/DesktopAppShell/DesktopAppShell.vue_vue_type_script_setup_true_lang.js.map +1 -0
  15. package/dist/blocks/ErrorState/ErrorState.js +7 -0
  16. package/dist/blocks/ErrorState/ErrorState.js.map +1 -0
  17. package/dist/blocks/ErrorState/ErrorState.vue.d.ts +44 -0
  18. package/dist/blocks/ErrorState/ErrorState.vue.d.ts.map +1 -0
  19. package/dist/blocks/ErrorState/ErrorState.vue_vue_type_script_setup_true_lang.js +60 -0
  20. package/dist/blocks/ErrorState/ErrorState.vue_vue_type_script_setup_true_lang.js.map +1 -0
  21. package/dist/blocks/FormActions/FormActions.js +7 -0
  22. package/dist/blocks/FormActions/FormActions.js.map +1 -0
  23. package/dist/blocks/FormActions/FormActions.vue.d.ts +51 -0
  24. package/dist/blocks/FormActions/FormActions.vue.d.ts.map +1 -0
  25. package/dist/blocks/FormActions/FormActions.vue_vue_type_script_setup_true_lang.js +21 -0
  26. package/dist/blocks/FormActions/FormActions.vue_vue_type_script_setup_true_lang.js.map +1 -0
  27. package/dist/blocks/FormSection/FormSection.js +7 -0
  28. package/dist/blocks/FormSection/FormSection.js.map +1 -0
  29. package/dist/blocks/FormSection/FormSection.vue.d.ts +85 -0
  30. package/dist/blocks/FormSection/FormSection.vue.d.ts.map +1 -0
  31. package/dist/blocks/FormSection/FormSection.vue_vue_type_script_setup_true_lang.js +62 -0
  32. package/dist/blocks/FormSection/FormSection.vue_vue_type_script_setup_true_lang.js.map +1 -0
  33. package/dist/blocks/LoadingState/LoadingState.js +7 -0
  34. package/dist/blocks/LoadingState/LoadingState.js.map +1 -0
  35. package/dist/blocks/LoadingState/LoadingState.vue.d.ts +22 -0
  36. package/dist/blocks/LoadingState/LoadingState.vue.d.ts.map +1 -0
  37. package/dist/blocks/LoadingState/LoadingState.vue_vue_type_script_setup_true_lang.js +57 -0
  38. package/dist/blocks/LoadingState/LoadingState.vue_vue_type_script_setup_true_lang.js.map +1 -0
  39. package/dist/blocks/MetricCard/MetricCard.js +7 -0
  40. package/dist/blocks/MetricCard/MetricCard.js.map +1 -0
  41. package/dist/blocks/MetricCard/MetricCard.vue.d.ts +59 -0
  42. package/dist/blocks/MetricCard/MetricCard.vue.d.ts.map +1 -0
  43. package/dist/blocks/MetricCard/MetricCard.vue_vue_type_script_setup_true_lang.js +63 -0
  44. package/dist/blocks/MetricCard/MetricCard.vue_vue_type_script_setup_true_lang.js.map +1 -0
  45. package/dist/blocks/SidebarNav/SidebarNav.js.map +1 -1
  46. package/dist/blocks/SidebarNav/SidebarNav.vue_vue_type_script_setup_true_lang.js.map +1 -1
  47. package/dist/blocks/TitleBar/TitleBar.js.map +1 -1
  48. package/dist/blocks/TitleBar/TitleBar.vue.d.ts +37 -3
  49. package/dist/blocks/TitleBar/TitleBar.vue.d.ts.map +1 -1
  50. package/dist/blocks/TitleBar/TitleBar.vue_vue_type_script_setup_true_lang.js +14 -14
  51. package/dist/blocks/TitleBar/TitleBar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  52. package/dist/blocks/ToastStack/ToastStack.js.map +1 -1
  53. package/dist/blocks/ToastStack/ToastStack.vue.d.ts +20 -0
  54. package/dist/blocks/ToastStack/ToastStack.vue.d.ts.map +1 -1
  55. package/dist/blocks/ToastStack/ToastStack.vue_vue_type_script_setup_true_lang.js +16 -5
  56. package/dist/blocks/ToastStack/ToastStack.vue_vue_type_script_setup_true_lang.js.map +1 -1
  57. package/dist/composition/Center/Center.js +7 -0
  58. package/dist/composition/Center/Center.js.map +1 -0
  59. package/dist/composition/Center/Center.vue.d.ts +40 -0
  60. package/dist/composition/Center/Center.vue.d.ts.map +1 -0
  61. package/dist/composition/Center/Center.vue_vue_type_script_setup_true_lang.js +29 -0
  62. package/dist/composition/Center/Center.vue_vue_type_script_setup_true_lang.js.map +1 -0
  63. package/dist/composition/Frame/Frame.js +7 -0
  64. package/dist/composition/Frame/Frame.js.map +1 -0
  65. package/dist/composition/Frame/Frame.vue.d.ts +35 -0
  66. package/dist/composition/Frame/Frame.vue.d.ts.map +1 -0
  67. package/dist/composition/Frame/Frame.vue_vue_type_script_setup_true_lang.js +29 -0
  68. package/dist/composition/Frame/Frame.vue_vue_type_script_setup_true_lang.js.map +1 -0
  69. package/dist/composition/Grid/Grid.js +7 -0
  70. package/dist/composition/Grid/Grid.js.map +1 -0
  71. package/dist/composition/Grid/Grid.vue.d.ts +41 -0
  72. package/dist/composition/Grid/Grid.vue.d.ts.map +1 -0
  73. package/dist/composition/Grid/Grid.vue_vue_type_script_setup_true_lang.js +27 -0
  74. package/dist/composition/Grid/Grid.vue_vue_type_script_setup_true_lang.js.map +1 -0
  75. package/dist/composition/Inline/Inline.js +7 -0
  76. package/dist/composition/Inline/Inline.js.map +1 -0
  77. package/dist/composition/Inline/Inline.vue.d.ts +46 -0
  78. package/dist/composition/Inline/Inline.vue.d.ts.map +1 -0
  79. package/dist/composition/Inline/Inline.vue_vue_type_script_setup_true_lang.js +35 -0
  80. package/dist/composition/Inline/Inline.vue_vue_type_script_setup_true_lang.js.map +1 -0
  81. package/dist/composition/ScrollReel/ScrollReel.js +7 -0
  82. package/dist/composition/ScrollReel/ScrollReel.js.map +1 -0
  83. package/dist/composition/ScrollReel/ScrollReel.vue.d.ts +42 -0
  84. package/dist/composition/ScrollReel/ScrollReel.vue.d.ts.map +1 -0
  85. package/dist/composition/ScrollReel/ScrollReel.vue_vue_type_script_setup_true_lang.js +79 -0
  86. package/dist/composition/ScrollReel/ScrollReel.vue_vue_type_script_setup_true_lang.js.map +1 -0
  87. package/dist/composition/Sidebar/Sidebar.js +7 -0
  88. package/dist/composition/Sidebar/Sidebar.js.map +1 -0
  89. package/dist/composition/Sidebar/Sidebar.vue.d.ts +52 -0
  90. package/dist/composition/Sidebar/Sidebar.vue.d.ts.map +1 -0
  91. package/dist/composition/Sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +36 -0
  92. package/dist/composition/Sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js.map +1 -0
  93. package/dist/composition/Split/Split.js +7 -0
  94. package/dist/composition/Split/Split.js.map +1 -0
  95. package/dist/composition/Split/Split.vue.d.ts +58 -0
  96. package/dist/composition/Split/Split.vue.d.ts.map +1 -0
  97. package/dist/composition/Split/Split.vue_vue_type_script_setup_true_lang.js +40 -0
  98. package/dist/composition/Split/Split.vue_vue_type_script_setup_true_lang.js.map +1 -0
  99. package/dist/composition/Stack/Stack.js +7 -0
  100. package/dist/composition/Stack/Stack.js.map +1 -0
  101. package/dist/composition/Stack/Stack.vue.d.ts +43 -0
  102. package/dist/composition/Stack/Stack.vue.d.ts.map +1 -0
  103. package/dist/composition/Stack/Stack.vue_vue_type_script_setup_true_lang.js +30 -0
  104. package/dist/composition/Stack/Stack.vue_vue_type_script_setup_true_lang.js.map +1 -0
  105. package/dist/index.d.ts +119 -11
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +83 -5
  108. package/dist/layouts/AppShell/AppShell.js +7 -0
  109. package/dist/layouts/AppShell/AppShell.js.map +1 -0
  110. package/dist/layouts/AppShell/AppShell.vue.d.ts +51 -0
  111. package/dist/layouts/AppShell/AppShell.vue.d.ts.map +1 -0
  112. package/dist/layouts/AppShell/AppShell.vue_vue_type_script_setup_true_lang.js +47 -0
  113. package/dist/layouts/AppShell/AppShell.vue_vue_type_script_setup_true_lang.js.map +1 -0
  114. package/dist/layouts/Centered/Centered.js +7 -0
  115. package/dist/layouts/Centered/Centered.js.map +1 -0
  116. package/dist/layouts/Centered/Centered.vue.d.ts +48 -0
  117. package/dist/layouts/Centered/Centered.vue.d.ts.map +1 -0
  118. package/dist/layouts/Centered/Centered.vue_vue_type_script_setup_true_lang.js +35 -0
  119. package/dist/layouts/Centered/Centered.vue_vue_type_script_setup_true_lang.js.map +1 -0
  120. package/dist/layouts/Dashboard/Dashboard.js +7 -0
  121. package/dist/layouts/Dashboard/Dashboard.js.map +1 -0
  122. package/dist/layouts/Dashboard/Dashboard.vue.d.ts +60 -0
  123. package/dist/layouts/Dashboard/Dashboard.vue.d.ts.map +1 -0
  124. package/dist/layouts/Dashboard/Dashboard.vue_vue_type_script_setup_true_lang.js +64 -0
  125. package/dist/layouts/Dashboard/Dashboard.vue_vue_type_script_setup_true_lang.js.map +1 -0
  126. package/dist/layouts/FormLayout/FormLayout.js +7 -0
  127. package/dist/layouts/FormLayout/FormLayout.js.map +1 -0
  128. package/dist/layouts/FormLayout/FormLayout.vue.d.ts +46 -0
  129. package/dist/layouts/FormLayout/FormLayout.vue.d.ts.map +1 -0
  130. package/dist/layouts/FormLayout/FormLayout.vue_vue_type_script_setup_true_lang.js +28 -0
  131. package/dist/layouts/FormLayout/FormLayout.vue_vue_type_script_setup_true_lang.js.map +1 -0
  132. package/dist/layouts/MasterDetail/MasterDetail.js +7 -0
  133. package/dist/layouts/MasterDetail/MasterDetail.js.map +1 -0
  134. package/dist/layouts/MasterDetail/MasterDetail.vue.d.ts +49 -0
  135. package/dist/layouts/MasterDetail/MasterDetail.vue.d.ts.map +1 -0
  136. package/dist/layouts/MasterDetail/MasterDetail.vue_vue_type_script_setup_true_lang.js +42 -0
  137. package/dist/layouts/MasterDetail/MasterDetail.vue_vue_type_script_setup_true_lang.js.map +1 -0
  138. package/dist/layouts/Reading/Reading.js +7 -0
  139. package/dist/layouts/Reading/Reading.js.map +1 -0
  140. package/dist/layouts/Reading/Reading.vue.d.ts +24 -0
  141. package/dist/layouts/Reading/Reading.vue.d.ts.map +1 -0
  142. package/dist/layouts/Reading/Reading.vue_vue_type_script_setup_true_lang.js +29 -0
  143. package/dist/layouts/Reading/Reading.vue_vue_type_script_setup_true_lang.js.map +1 -0
  144. package/dist/layouts/Settings/Settings.js +7 -0
  145. package/dist/layouts/Settings/Settings.js.map +1 -0
  146. package/dist/layouts/Settings/Settings.vue.d.ts +39 -0
  147. package/dist/layouts/Settings/Settings.vue.d.ts.map +1 -0
  148. package/dist/layouts/Settings/Settings.vue_vue_type_script_setup_true_lang.js +35 -0
  149. package/dist/layouts/Settings/Settings.vue_vue_type_script_setup_true_lang.js.map +1 -0
  150. package/dist/layouts/SplitLayout/SplitLayout.js +7 -0
  151. package/dist/layouts/SplitLayout/SplitLayout.js.map +1 -0
  152. package/dist/layouts/SplitLayout/SplitLayout.vue.d.ts +63 -0
  153. package/dist/layouts/SplitLayout/SplitLayout.vue.d.ts.map +1 -0
  154. package/dist/layouts/SplitLayout/SplitLayout.vue_vue_type_script_setup_true_lang.js +46 -0
  155. package/dist/layouts/SplitLayout/SplitLayout.vue_vue_type_script_setup_true_lang.js.map +1 -0
  156. package/dist/lib/ancestor-disabled.d.ts +35 -0
  157. package/dist/lib/ancestor-disabled.d.ts.map +1 -0
  158. package/dist/lib/ancestor-disabled.js +86 -0
  159. package/dist/lib/ancestor-disabled.js.map +1 -0
  160. package/dist/lib/cn.d.ts.map +1 -1
  161. package/dist/lib/cn.js +46 -1
  162. package/dist/lib/cn.js.map +1 -1
  163. package/dist/lib/count-labels.d.ts +78 -0
  164. package/dist/lib/count-labels.d.ts.map +1 -0
  165. package/dist/lib/count-labels.js +29 -0
  166. package/dist/lib/count-labels.js.map +1 -0
  167. package/dist/lib/date-labels.d.ts +278 -0
  168. package/dist/lib/date-labels.d.ts.map +1 -0
  169. package/dist/lib/date-labels.js +157 -0
  170. package/dist/lib/date-labels.js.map +1 -0
  171. package/dist/lib/field-context.d.ts +178 -0
  172. package/dist/lib/field-context.d.ts.map +1 -0
  173. package/dist/lib/field-context.js +106 -0
  174. package/dist/lib/field-context.js.map +1 -0
  175. package/dist/lib/label-registry.d.ts +75 -0
  176. package/dist/lib/label-registry.d.ts.map +1 -0
  177. package/dist/lib/labels.d.ts +116 -0
  178. package/dist/lib/labels.d.ts.map +1 -0
  179. package/dist/lib/labels.js +84 -0
  180. package/dist/lib/labels.js.map +1 -0
  181. package/dist/lib/theme.d.ts +68 -0
  182. package/dist/lib/theme.d.ts.map +1 -0
  183. package/dist/node_modules/.pnpm/@internationalized_date@3.12.3/node_modules/@internationalized/date/dist/private/CalendarDate.js +236 -0
  184. package/dist/node_modules/.pnpm/@internationalized_date@3.12.3/node_modules/@internationalized/date/dist/private/CalendarDate.js.map +1 -0
  185. package/dist/node_modules/.pnpm/@internationalized_date@3.12.3/node_modules/@internationalized/date/dist/private/calendars/GregorianCalendar.js +117 -0
  186. package/dist/node_modules/.pnpm/@internationalized_date@3.12.3/node_modules/@internationalized/date/dist/private/calendars/GregorianCalendar.js.map +1 -0
  187. package/dist/node_modules/.pnpm/@internationalized_date@3.12.3/node_modules/@internationalized/date/dist/private/conversion.js +142 -0
  188. package/dist/node_modules/.pnpm/@internationalized_date@3.12.3/node_modules/@internationalized/date/dist/private/conversion.js.map +1 -0
  189. package/dist/node_modules/.pnpm/@internationalized_date@3.12.3/node_modules/@internationalized/date/dist/private/manipulation.js +280 -0
  190. package/dist/node_modules/.pnpm/@internationalized_date@3.12.3/node_modules/@internationalized/date/dist/private/manipulation.js.map +1 -0
  191. package/dist/node_modules/.pnpm/@internationalized_date@3.12.3/node_modules/@internationalized/date/dist/private/queries.js +40 -0
  192. package/dist/node_modules/.pnpm/@internationalized_date@3.12.3/node_modules/@internationalized/date/dist/private/queries.js.map +1 -0
  193. package/dist/node_modules/.pnpm/@internationalized_date@3.12.3/node_modules/@internationalized/date/dist/private/string.js +69 -0
  194. package/dist/node_modules/.pnpm/@internationalized_date@3.12.3/node_modules/@internationalized/date/dist/private/string.js.map +1 -0
  195. package/dist/node_modules/.pnpm/@internationalized_date@3.12.3/node_modules/@internationalized/date/dist/private/utils.js +8 -0
  196. package/dist/node_modules/.pnpm/@internationalized_date@3.12.3/node_modules/@internationalized/date/dist/private/utils.js.map +1 -0
  197. package/dist/primitives/Accordion/Accordion.js +7 -0
  198. package/dist/primitives/Accordion/Accordion.js.map +1 -0
  199. package/dist/primitives/Accordion/Accordion.vue.d.ts +68 -0
  200. package/dist/primitives/Accordion/Accordion.vue.d.ts.map +1 -0
  201. package/dist/primitives/Accordion/Accordion.vue_vue_type_script_setup_true_lang.js +67 -0
  202. package/dist/primitives/Accordion/Accordion.vue_vue_type_script_setup_true_lang.js.map +1 -0
  203. package/dist/primitives/AlertDialog/AlertDialog.js +7 -0
  204. package/dist/primitives/AlertDialog/AlertDialog.js.map +1 -0
  205. package/dist/primitives/AlertDialog/AlertDialog.vue.d.ts +94 -0
  206. package/dist/primitives/AlertDialog/AlertDialog.vue.d.ts.map +1 -0
  207. package/dist/primitives/AlertDialog/AlertDialog.vue_vue_type_script_setup_true_lang.js +99 -0
  208. package/dist/primitives/AlertDialog/AlertDialog.vue_vue_type_script_setup_true_lang.js.map +1 -0
  209. package/dist/primitives/Avatar/Avatar.js +2 -2
  210. package/dist/primitives/Avatar/Avatar.js.map +1 -1
  211. package/dist/primitives/Avatar/Avatar.vue.d.ts +30 -3
  212. package/dist/primitives/Avatar/Avatar.vue.d.ts.map +1 -1
  213. package/dist/primitives/Avatar/Avatar.vue_vue_type_script_setup_true_lang.js +95 -14
  214. package/dist/primitives/Avatar/Avatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  215. package/dist/primitives/AvatarGroup/AvatarGroup.js +7 -0
  216. package/dist/primitives/AvatarGroup/AvatarGroup.js.map +1 -0
  217. package/dist/primitives/AvatarGroup/AvatarGroup.vue.d.ts +104 -0
  218. package/dist/primitives/AvatarGroup/AvatarGroup.vue.d.ts.map +1 -0
  219. package/dist/primitives/AvatarGroup/AvatarGroup.vue_vue_type_script_setup_true_lang.js +139 -0
  220. package/dist/primitives/AvatarGroup/AvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -0
  221. package/dist/primitives/Badge/Badge.js.map +1 -1
  222. package/dist/primitives/Badge/Badge.vue.d.ts +6 -6
  223. package/dist/primitives/Badge/Badge.vue.d.ts.map +1 -1
  224. package/dist/primitives/Badge/Badge.vue_vue_type_script_setup_true_lang.js +1 -1
  225. package/dist/primitives/Badge/Badge.vue_vue_type_script_setup_true_lang.js.map +1 -1
  226. package/dist/primitives/Breadcrumb/Breadcrumb.js +7 -0
  227. package/dist/primitives/Breadcrumb/Breadcrumb.js.map +1 -0
  228. package/dist/primitives/Breadcrumb/Breadcrumb.vue.d.ts +25 -0
  229. package/dist/primitives/Breadcrumb/Breadcrumb.vue.d.ts.map +1 -0
  230. package/dist/primitives/Breadcrumb/Breadcrumb.vue_vue_type_script_setup_true_lang.js +48 -0
  231. package/dist/primitives/Breadcrumb/Breadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -0
  232. package/dist/primitives/Button/Button.js +2 -2
  233. package/dist/primitives/Button/Button.js.map +1 -1
  234. package/dist/primitives/Button/Button.vue.d.ts +31 -8
  235. package/dist/primitives/Button/Button.vue.d.ts.map +1 -1
  236. package/dist/primitives/Button/Button.vue_vue_type_script_setup_true_lang.js +32 -16
  237. package/dist/primitives/Button/Button.vue_vue_type_script_setup_true_lang.js.map +1 -1
  238. package/dist/primitives/Checkbox/Checkbox.js.map +1 -1
  239. package/dist/primitives/Checkbox/Checkbox.vue.d.ts +18 -3
  240. package/dist/primitives/Checkbox/Checkbox.vue.d.ts.map +1 -1
  241. package/dist/primitives/Checkbox/Checkbox.vue_vue_type_script_setup_true_lang.js +46 -7
  242. package/dist/primitives/Checkbox/Checkbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  243. package/dist/primitives/Chip/Chip.js +7 -0
  244. package/dist/primitives/Chip/Chip.js.map +1 -0
  245. package/dist/primitives/Chip/Chip.vue.d.ts +79 -0
  246. package/dist/primitives/Chip/Chip.vue.d.ts.map +1 -0
  247. package/dist/primitives/Chip/Chip.vue_vue_type_script_setup_true_lang.js +139 -0
  248. package/dist/primitives/Chip/Chip.vue_vue_type_script_setup_true_lang.js.map +1 -0
  249. package/dist/primitives/ColorPicker/ColorPicker.js +7 -0
  250. package/dist/primitives/ColorPicker/ColorPicker.js.map +1 -0
  251. package/dist/primitives/ColorPicker/ColorPicker.vue.d.ts +125 -0
  252. package/dist/primitives/ColorPicker/ColorPicker.vue.d.ts.map +1 -0
  253. package/dist/primitives/ColorPicker/ColorPicker.vue_vue_type_script_setup_true_lang.js +301 -0
  254. package/dist/primitives/ColorPicker/ColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -0
  255. package/dist/primitives/Combobox/Combobox.js +7 -0
  256. package/dist/primitives/Combobox/Combobox.js.map +1 -0
  257. package/dist/primitives/Combobox/Combobox.vue.d.ts +205 -0
  258. package/dist/primitives/Combobox/Combobox.vue.d.ts.map +1 -0
  259. package/dist/primitives/Combobox/Combobox.vue_vue_type_script_setup_true_lang.js +345 -0
  260. package/dist/primitives/Combobox/Combobox.vue_vue_type_script_setup_true_lang.js.map +1 -0
  261. package/dist/primitives/ContextMenu/ContextMenu.js +7 -0
  262. package/dist/primitives/ContextMenu/ContextMenu.js.map +1 -0
  263. package/dist/primitives/ContextMenu/ContextMenu.vue.d.ts +55 -0
  264. package/dist/primitives/ContextMenu/ContextMenu.vue.d.ts.map +1 -0
  265. package/dist/primitives/ContextMenu/ContextMenu.vue_vue_type_script_setup_true_lang.js +82 -0
  266. package/dist/primitives/ContextMenu/ContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -0
  267. package/dist/primitives/DatePicker/DatePicker.js +7 -0
  268. package/dist/primitives/DatePicker/DatePicker.js.map +1 -0
  269. package/dist/primitives/DatePicker/DatePicker.vue.d.ts +102 -0
  270. package/dist/primitives/DatePicker/DatePicker.vue.d.ts.map +1 -0
  271. package/dist/primitives/DatePicker/DatePicker.vue_vue_type_script_setup_true_lang.js +292 -0
  272. package/dist/primitives/DatePicker/DatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -0
  273. package/dist/primitives/DateRangePicker/DateRangePicker.js +7 -0
  274. package/dist/primitives/DateRangePicker/DateRangePicker.js.map +1 -0
  275. package/dist/primitives/DateRangePicker/DateRangePicker.vue.d.ts +173 -0
  276. package/dist/primitives/DateRangePicker/DateRangePicker.vue.d.ts.map +1 -0
  277. package/dist/primitives/DateRangePicker/DateRangePicker.vue_vue_type_script_setup_true_lang.js +412 -0
  278. package/dist/primitives/DateRangePicker/DateRangePicker.vue_vue_type_script_setup_true_lang.js.map +1 -0
  279. package/dist/primitives/DateTimePicker/DateTimePicker.js +7 -0
  280. package/dist/primitives/DateTimePicker/DateTimePicker.js.map +1 -0
  281. package/dist/primitives/DateTimePicker/DateTimePicker.vue.d.ts +113 -0
  282. package/dist/primitives/DateTimePicker/DateTimePicker.vue.d.ts.map +1 -0
  283. package/dist/primitives/DateTimePicker/DateTimePicker.vue_vue_type_script_setup_true_lang.js +358 -0
  284. package/dist/primitives/DateTimePicker/DateTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -0
  285. package/dist/primitives/DateTimeRangePicker/DateTimeRangePicker.js +7 -0
  286. package/dist/primitives/DateTimeRangePicker/DateTimeRangePicker.js.map +1 -0
  287. package/dist/primitives/DateTimeRangePicker/DateTimeRangePicker.vue.d.ts +181 -0
  288. package/dist/primitives/DateTimeRangePicker/DateTimeRangePicker.vue.d.ts.map +1 -0
  289. package/dist/primitives/DateTimeRangePicker/DateTimeRangePicker.vue_vue_type_script_setup_true_lang.js +562 -0
  290. package/dist/primitives/DateTimeRangePicker/DateTimeRangePicker.vue_vue_type_script_setup_true_lang.js.map +1 -0
  291. package/dist/primitives/Dialog/Dialog.js +2 -2
  292. package/dist/primitives/Dialog/Dialog.js.map +1 -1
  293. package/dist/primitives/Dialog/Dialog.vue.d.ts +39 -0
  294. package/dist/primitives/Dialog/Dialog.vue.d.ts.map +1 -1
  295. package/dist/primitives/Dialog/Dialog.vue_vue_type_script_setup_true_lang.js +14 -5
  296. package/dist/primitives/Dialog/Dialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  297. package/dist/primitives/Drawer/Drawer.js +7 -0
  298. package/dist/primitives/Drawer/Drawer.js.map +1 -0
  299. package/dist/primitives/Drawer/Drawer.vue.d.ts +110 -0
  300. package/dist/primitives/Drawer/Drawer.vue.d.ts.map +1 -0
  301. package/dist/primitives/Drawer/Drawer.vue_vue_type_script_setup_true_lang.js +223 -0
  302. package/dist/primitives/Drawer/Drawer.vue_vue_type_script_setup_true_lang.js.map +1 -0
  303. package/dist/primitives/DropdownMenu/DropdownMenu.js.map +1 -1
  304. package/dist/primitives/DropdownMenu/DropdownMenu.vue.d.ts.map +1 -1
  305. package/dist/primitives/DropdownMenu/DropdownMenu.vue_vue_type_script_setup_true_lang.js +2 -2
  306. package/dist/primitives/DropdownMenu/DropdownMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  307. package/dist/primitives/Editable/Editable.js +7 -0
  308. package/dist/primitives/Editable/Editable.js.map +1 -0
  309. package/dist/primitives/Editable/Editable.vue.d.ts +200 -0
  310. package/dist/primitives/Editable/Editable.vue.d.ts.map +1 -0
  311. package/dist/primitives/Editable/Editable.vue_vue_type_script_setup_true_lang.js +275 -0
  312. package/dist/primitives/Editable/Editable.vue_vue_type_script_setup_true_lang.js.map +1 -0
  313. package/dist/primitives/Field/Field.js.map +1 -1
  314. package/dist/primitives/Field/Field.vue.d.ts +32 -17
  315. package/dist/primitives/Field/Field.vue.d.ts.map +1 -1
  316. package/dist/primitives/Field/Field.vue_vue_type_script_setup_true_lang.js +51 -18
  317. package/dist/primitives/Field/Field.vue_vue_type_script_setup_true_lang.js.map +1 -1
  318. package/dist/primitives/Fieldset/Fieldset.js +7 -0
  319. package/dist/primitives/Fieldset/Fieldset.js.map +1 -0
  320. package/dist/primitives/Fieldset/Fieldset.vue.d.ts +91 -0
  321. package/dist/primitives/Fieldset/Fieldset.vue.d.ts.map +1 -0
  322. package/dist/primitives/Fieldset/Fieldset.vue_vue_type_script_setup_true_lang.js +138 -0
  323. package/dist/primitives/Fieldset/Fieldset.vue_vue_type_script_setup_true_lang.js.map +1 -0
  324. package/dist/primitives/FileUpload/FileUpload.js +7 -0
  325. package/dist/primitives/FileUpload/FileUpload.js.map +1 -0
  326. package/dist/primitives/FileUpload/FileUpload.vue.d.ts +132 -0
  327. package/dist/primitives/FileUpload/FileUpload.vue.d.ts.map +1 -0
  328. package/dist/primitives/FileUpload/FileUpload.vue_vue_type_script_setup_true_lang.js +359 -0
  329. package/dist/primitives/FileUpload/FileUpload.vue_vue_type_script_setup_true_lang.js.map +1 -0
  330. package/dist/primitives/HoverCard/HoverCard.js +7 -0
  331. package/dist/primitives/HoverCard/HoverCard.js.map +1 -0
  332. package/dist/primitives/HoverCard/HoverCard.vue.d.ts +68 -0
  333. package/dist/primitives/HoverCard/HoverCard.vue.d.ts.map +1 -0
  334. package/dist/primitives/HoverCard/HoverCard.vue_vue_type_script_setup_true_lang.js +62 -0
  335. package/dist/primitives/HoverCard/HoverCard.vue_vue_type_script_setup_true_lang.js.map +1 -0
  336. package/dist/primitives/IconButton/IconButton.js +7 -0
  337. package/dist/primitives/IconButton/IconButton.js.map +1 -0
  338. package/dist/primitives/IconButton/IconButton.vue.d.ts +45 -0
  339. package/dist/primitives/IconButton/IconButton.vue.d.ts.map +1 -0
  340. package/dist/primitives/IconButton/IconButton.vue_vue_type_script_setup_true_lang.js +58 -0
  341. package/dist/primitives/IconButton/IconButton.vue_vue_type_script_setup_true_lang.js.map +1 -0
  342. package/dist/primitives/Indicator/Indicator.js +7 -0
  343. package/dist/primitives/Indicator/Indicator.js.map +1 -0
  344. package/dist/primitives/Indicator/Indicator.vue.d.ts +144 -0
  345. package/dist/primitives/Indicator/Indicator.vue.d.ts.map +1 -0
  346. package/dist/primitives/Indicator/Indicator.vue_vue_type_script_setup_true_lang.js +198 -0
  347. package/dist/primitives/Indicator/Indicator.vue_vue_type_script_setup_true_lang.js.map +1 -0
  348. package/dist/primitives/Link/Link.js +7 -0
  349. package/dist/primitives/Link/Link.js.map +1 -0
  350. package/dist/primitives/Link/Link.vue.d.ts +46 -0
  351. package/dist/primitives/Link/Link.vue.d.ts.map +1 -0
  352. package/dist/primitives/Link/Link.vue_vue_type_script_setup_true_lang.js +85 -0
  353. package/dist/primitives/Link/Link.vue_vue_type_script_setup_true_lang.js.map +1 -0
  354. package/dist/primitives/Menubar/Menubar.js.map +1 -1
  355. package/dist/primitives/Menubar/Menubar.vue.d.ts.map +1 -1
  356. package/dist/primitives/Menubar/Menubar.vue_vue_type_script_setup_true_lang.js +1 -1
  357. package/dist/primitives/Menubar/Menubar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  358. package/dist/primitives/NumberField/NumberField.js +2 -2
  359. package/dist/primitives/NumberField/NumberField.js.map +1 -1
  360. package/dist/primitives/NumberField/NumberField.vue.d.ts +86 -25
  361. package/dist/primitives/NumberField/NumberField.vue.d.ts.map +1 -1
  362. package/dist/primitives/NumberField/NumberField.vue_vue_type_script_setup_true_lang.js +83 -39
  363. package/dist/primitives/NumberField/NumberField.vue_vue_type_script_setup_true_lang.js.map +1 -1
  364. package/dist/primitives/OtpInput/OtpInput.js +7 -0
  365. package/dist/primitives/OtpInput/OtpInput.js.map +1 -0
  366. package/dist/primitives/OtpInput/OtpInput.vue.d.ts +110 -0
  367. package/dist/primitives/OtpInput/OtpInput.vue.d.ts.map +1 -0
  368. package/dist/primitives/OtpInput/OtpInput.vue_vue_type_script_setup_true_lang.js +130 -0
  369. package/dist/primitives/OtpInput/OtpInput.vue_vue_type_script_setup_true_lang.js.map +1 -0
  370. package/dist/primitives/Pagination/Pagination.js +7 -0
  371. package/dist/primitives/Pagination/Pagination.js.map +1 -0
  372. package/dist/primitives/Pagination/Pagination.vue.d.ts +143 -0
  373. package/dist/primitives/Pagination/Pagination.vue.d.ts.map +1 -0
  374. package/dist/primitives/Pagination/Pagination.vue_vue_type_script_setup_true_lang.js +231 -0
  375. package/dist/primitives/Pagination/Pagination.vue_vue_type_script_setup_true_lang.js.map +1 -0
  376. package/dist/primitives/Progress/Progress.js +2 -2
  377. package/dist/primitives/Progress/Progress.js.map +1 -1
  378. package/dist/primitives/Progress/Progress.vue.d.ts +81 -15
  379. package/dist/primitives/Progress/Progress.vue.d.ts.map +1 -1
  380. package/dist/primitives/Progress/Progress.vue_vue_type_script_setup_true_lang.js +88 -22
  381. package/dist/primitives/Progress/Progress.vue_vue_type_script_setup_true_lang.js.map +1 -1
  382. package/dist/primitives/RadialProgress/RadialProgress.js +7 -0
  383. package/dist/primitives/RadialProgress/RadialProgress.js.map +1 -0
  384. package/dist/primitives/RadialProgress/RadialProgress.vue.d.ts +96 -0
  385. package/dist/primitives/RadialProgress/RadialProgress.vue.d.ts.map +1 -0
  386. package/dist/primitives/RadialProgress/RadialProgress.vue_vue_type_script_setup_true_lang.js +189 -0
  387. package/dist/primitives/RadialProgress/RadialProgress.vue_vue_type_script_setup_true_lang.js.map +1 -0
  388. package/dist/primitives/RadioGroup/RadioGroup.js.map +1 -1
  389. package/dist/primitives/RadioGroup/RadioGroup.vue.d.ts +7 -7
  390. package/dist/primitives/RadioGroup/RadioGroup.vue.d.ts.map +1 -1
  391. package/dist/primitives/RadioGroup/RadioGroup.vue_vue_type_script_setup_true_lang.js +26 -9
  392. package/dist/primitives/RadioGroup/RadioGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  393. package/dist/primitives/Rating/Rating.js +7 -0
  394. package/dist/primitives/Rating/Rating.js.map +1 -0
  395. package/dist/primitives/Rating/Rating.vue.d.ts +113 -0
  396. package/dist/primitives/Rating/Rating.vue.d.ts.map +1 -0
  397. package/dist/primitives/Rating/Rating.vue_vue_type_script_setup_true_lang.js +282 -0
  398. package/dist/primitives/Rating/Rating.vue_vue_type_script_setup_true_lang.js.map +1 -0
  399. package/dist/primitives/ScrollArea/ScrollArea.js +7 -0
  400. package/dist/primitives/ScrollArea/ScrollArea.js.map +1 -0
  401. package/dist/primitives/ScrollArea/ScrollArea.vue.d.ts +25 -0
  402. package/dist/primitives/ScrollArea/ScrollArea.vue.d.ts.map +1 -0
  403. package/dist/primitives/ScrollArea/ScrollArea.vue_vue_type_script_setup_true_lang.js +45 -0
  404. package/dist/primitives/ScrollArea/ScrollArea.vue_vue_type_script_setup_true_lang.js.map +1 -0
  405. package/dist/primitives/SegmentedControl/SegmentedControl.js.map +1 -1
  406. package/dist/primitives/SegmentedControl/SegmentedControl.vue.d.ts +5 -5
  407. package/dist/primitives/SegmentedControl/SegmentedControl.vue.d.ts.map +1 -1
  408. package/dist/primitives/SegmentedControl/SegmentedControl.vue_vue_type_script_setup_true_lang.js +51 -8
  409. package/dist/primitives/SegmentedControl/SegmentedControl.vue_vue_type_script_setup_true_lang.js.map +1 -1
  410. package/dist/primitives/Select/Select.js.map +1 -1
  411. package/dist/primitives/Select/Select.vue.d.ts +10 -10
  412. package/dist/primitives/Select/Select.vue.d.ts.map +1 -1
  413. package/dist/primitives/Select/Select.vue_vue_type_script_setup_true_lang.js +105 -52
  414. package/dist/primitives/Select/Select.vue_vue_type_script_setup_true_lang.js.map +1 -1
  415. package/dist/primitives/Skeleton/Skeleton.vue.d.ts +1 -1
  416. package/dist/primitives/Slider/Slider.js.map +1 -1
  417. package/dist/primitives/Slider/Slider.vue.d.ts +24 -4
  418. package/dist/primitives/Slider/Slider.vue.d.ts.map +1 -1
  419. package/dist/primitives/Slider/Slider.vue_vue_type_script_setup_true_lang.js +79 -9
  420. package/dist/primitives/Slider/Slider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  421. package/dist/primitives/SpeedDial/SpeedDial.js +7 -0
  422. package/dist/primitives/SpeedDial/SpeedDial.js.map +1 -0
  423. package/dist/primitives/SpeedDial/SpeedDial.vue.d.ts +58 -0
  424. package/dist/primitives/SpeedDial/SpeedDial.vue.d.ts.map +1 -0
  425. package/dist/primitives/SpeedDial/SpeedDial.vue_vue_type_script_setup_true_lang.js +175 -0
  426. package/dist/primitives/SpeedDial/SpeedDial.vue_vue_type_script_setup_true_lang.js.map +1 -0
  427. package/dist/primitives/Stepper/Stepper.js +7 -0
  428. package/dist/primitives/Stepper/Stepper.js.map +1 -0
  429. package/dist/primitives/Stepper/Stepper.vue.d.ts +119 -0
  430. package/dist/primitives/Stepper/Stepper.vue.d.ts.map +1 -0
  431. package/dist/primitives/Stepper/Stepper.vue_vue_type_script_setup_true_lang.js +202 -0
  432. package/dist/primitives/Stepper/Stepper.vue_vue_type_script_setup_true_lang.js.map +1 -0
  433. package/dist/primitives/Switch/Switch.js.map +1 -1
  434. package/dist/primitives/Switch/Switch.vue.d.ts +13 -3
  435. package/dist/primitives/Switch/Switch.vue.d.ts.map +1 -1
  436. package/dist/primitives/Switch/Switch.vue_vue_type_script_setup_true_lang.js +34 -5
  437. package/dist/primitives/Switch/Switch.vue_vue_type_script_setup_true_lang.js.map +1 -1
  438. package/dist/primitives/Tabs/Tabs.js.map +1 -1
  439. package/dist/primitives/Tabs/Tabs.vue.d.ts.map +1 -1
  440. package/dist/primitives/Tabs/Tabs.vue_vue_type_script_setup_true_lang.js +1 -1
  441. package/dist/primitives/Tabs/Tabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  442. package/dist/primitives/TagsInput/TagsInput.js +7 -0
  443. package/dist/primitives/TagsInput/TagsInput.js.map +1 -0
  444. package/dist/primitives/TagsInput/TagsInput.vue.d.ts +156 -0
  445. package/dist/primitives/TagsInput/TagsInput.vue.d.ts.map +1 -0
  446. package/dist/primitives/TagsInput/TagsInput.vue_vue_type_script_setup_true_lang.js +387 -0
  447. package/dist/primitives/TagsInput/TagsInput.vue_vue_type_script_setup_true_lang.js.map +1 -0
  448. package/dist/primitives/TextField/TextField.js +2 -2
  449. package/dist/primitives/TextField/TextField.js.map +1 -1
  450. package/dist/primitives/TextField/TextField.vue.d.ts +119 -10
  451. package/dist/primitives/TextField/TextField.vue.d.ts.map +1 -1
  452. package/dist/primitives/TextField/TextField.vue_vue_type_script_setup_true_lang.js +270 -21
  453. package/dist/primitives/TextField/TextField.vue_vue_type_script_setup_true_lang.js.map +1 -1
  454. package/dist/primitives/Textarea/Textarea.js +2 -2
  455. package/dist/primitives/Textarea/Textarea.js.map +1 -1
  456. package/dist/primitives/Textarea/Textarea.vue.d.ts +114 -14
  457. package/dist/primitives/Textarea/Textarea.vue.d.ts.map +1 -1
  458. package/dist/primitives/Textarea/Textarea.vue_vue_type_script_setup_true_lang.js +216 -20
  459. package/dist/primitives/Textarea/Textarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  460. package/dist/primitives/TimePicker/TimePicker.js +7 -0
  461. package/dist/primitives/TimePicker/TimePicker.js.map +1 -0
  462. package/dist/primitives/TimePicker/TimePicker.vue.d.ts +111 -0
  463. package/dist/primitives/TimePicker/TimePicker.vue.d.ts.map +1 -0
  464. package/dist/primitives/TimePicker/TimePicker.vue_vue_type_script_setup_true_lang.js +185 -0
  465. package/dist/primitives/TimePicker/TimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -0
  466. package/dist/primitives/Toast/Toast.js +2 -1
  467. package/dist/primitives/Toast/Toast.js.map +1 -1
  468. package/dist/primitives/Toast/Toast.vue.d.ts +29 -3
  469. package/dist/primitives/Toast/Toast.vue.d.ts.map +1 -1
  470. package/dist/primitives/Toast/Toast.vue_vue_type_script_setup_true_lang.js +17 -7
  471. package/dist/primitives/Toast/Toast.vue_vue_type_script_setup_true_lang.js.map +1 -1
  472. package/dist/primitives/Toast/ToastItem.js +2 -2
  473. package/dist/primitives/Toast/ToastItem.js.map +1 -1
  474. package/dist/primitives/Toast/ToastItem.vue.d.ts +69 -12
  475. package/dist/primitives/Toast/ToastItem.vue.d.ts.map +1 -1
  476. package/dist/primitives/Toast/ToastItem.vue_vue_type_script_setup_true_lang.js +23 -23
  477. package/dist/primitives/Toast/ToastItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  478. package/dist/primitives/Tooltip/Tooltip.vue.d.ts +1 -1
  479. package/dist/primitives/Tooltip/Tooltip.vue.d.ts.map +1 -1
  480. package/dist/primitives/WindowControls/WindowControls.js +2 -2
  481. package/dist/primitives/WindowControls/WindowControls.js.map +1 -1
  482. package/dist/primitives/WindowControls/WindowControls.vue.d.ts +66 -10
  483. package/dist/primitives/WindowControls/WindowControls.vue.d.ts.map +1 -1
  484. package/dist/primitives/WindowControls/WindowControls.vue_vue_type_script_setup_true_lang.js +23 -13
  485. package/dist/primitives/WindowControls/WindowControls.vue_vue_type_script_setup_true_lang.js.map +1 -1
  486. package/dist/styles/global.css +116 -17
  487. package/dist/styles/theme.contrast.test.ts +460 -0
  488. package/dist/styles/theme.css +239 -41
  489. package/dist/theme.js +171 -0
  490. package/dist/theme.js.map +1 -0
  491. package/package.json +14 -4
  492. package/dist/icons/BrandMark.d.ts +0 -18
  493. package/dist/icons/BrandMark.d.ts.map +0 -1
  494. package/dist/icons/BrandMark.js +0 -47
  495. package/dist/icons/BrandMark.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"NumberField.js","names":[],"sources":["../../../src/primitives/NumberField/NumberField.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onUnmounted, ref, watch } from \"vue\";\nimport {\n NumberFieldRoot,\n NumberFieldInput,\n NumberFieldIncrement,\n NumberFieldDecrement,\n} from \"reka-ui\";\nimport { ChevronUp, ChevronDown } from \"@lucide/vue\";\nimport { cn } from \"../../lib/cn\";\nimport { useSplitAttrs } from \"../../lib/attrs\";\nimport { optional } from \"../../lib/props\";\n\n/**\n * NumberField — a constrained number input you can also *scrub*: drag\n * horizontally anywhere on the field and the value runs with the pointer, the\n * gesture professional editors trained everyone to expect on a numeric row.\n *\n * Built on Reka UI's NumberField, which supplies the spinbutton semantics and\n * the typed-value parsing. Two things are added here because Reka has no\n * equivalent: the scrub-drag gesture, and a Shift multiplier on the arrow\n * keys.\n *\n * The event contract is what makes it usable behind an undo stack.\n * `update:modelValue` is transient — it fires on every drag tick, every arrow\n * tick and every applied edit, so a host can paint a live preview. `commit`\n * fires once per gesture boundary — drag release, Enter, or focus leaving the\n * field. One long drag is therefore one undo checkpoint rather than one per\n * pixel, and a gesture that ends where it started produces none at all.\n */\nconst props = withDefaults(\n defineProps<{\n /** The current value. Transient edits are reported before it changes. */\n modelValue?: number;\n /** Lower bound. Every path — typing, arrows, scrub — is clamped to it. */\n min?: number;\n /** Upper bound, clamped the same way. It wins over step alignment. */\n max?: number;\n /** The granularity of one arrow tick or one scrub step. Shift multiplies it by ten. */\n step?: number;\n /** A presentational suffix such as `px` or `deg`. It is never part of the value. */\n unit?: string;\n /** Unavailable: dims the field and refuses every edit path. */\n disabled?: boolean;\n /** Error state: paints the destructive border and ring, and sets `aria-invalid`. */\n invalid?: boolean;\n }>(),\n { step: 1, disabled: false, invalid: false },\n);\n\nconst emit = defineEmits<{\n /** Transient: every drag tick, arrow tick, or applied edit. Not a checkpoint. */\n \"update:modelValue\": [value: number];\n /** Committed: once per gesture boundary — drag release, Enter, or focus leaving. */\n commit: [value: number];\n}>();\n\n// The rendered root is a non-focusable `role=\"group\"` wrapper, so fallthrough\n// attributes that describe the *control* — `aria-labelledby`, `data-testid` —\n// belong on the spinbutton inside it, not on the group. `class` is the one\n// exception and goes the other way: a caller's sizing or layout class has to\n// land on the element that participates in the parent's layout, merged through\n// `cn()` so a caller's `w-24` actually beats the root's own `w-full` instead of\n// losing to whichever utility Tailwind happened to emit last.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: inputAttrs } = useSplitAttrs();\n\n// `optional()` drops the absent bounds rather than forwarding `undefined`,\n// which Reka would read as \"a bound that is undefined\" rather than \"no bound\".\nconst rootBounds = computed(() =>\n optional({ modelValue: lastValue.value, min: props.min, max: props.max }),\n);\n\nfunction clampValue(value: number): number {\n const step = props.step || 1;\n const stepped = Math.round(value / step) * step;\n const withMin = props.min !== undefined ? Math.max(props.min, stepped) : stepped;\n return props.max !== undefined ? Math.min(props.max, withMin) : withMin;\n}\n\n// The single source of truth for \"the last value we know about\", updated the\n// moment it changes — never re-derived from `props.modelValue` at commit time,\n// because that prop only reflects a new value after the host's v-model\n// round-trip re-renders us, which is a tick later than the blur or keydown\n// being handled.\n//\n// It is also what the Reka root binds as its own `model-value`, rather than\n// `props.modelValue`. Given a defined model value Reka runs fully controlled:\n// its increment reads back through that prop, so a host that correctly\n// withholds transient echoes until commit would freeze every arrow tick at\n// start + 1 and freeze the readout at the gesture's start value. Feeding it\n// `lastValue` keeps the ticks accumulating and the readout live inside a\n// gesture, while the host still sees nothing but transient emits until the\n// boundary.\nconst lastValue = ref(props.modelValue);\n\n// The value the host last acknowledged. `commit` only fires when `lastValue`\n// differs from it, so overlapping boundaries (Enter then blur, drag release\n// then blur) and no-edit focus passes (Tab in, Tab out) produce no extra\n// checkpoints.\nconst lastCommittedValue = ref(props.modelValue);\n\n// True while an uncommitted edit is in flight. An external prop change\n// re-baselines `lastCommittedValue` only when nothing is pending — otherwise\n// the host echoing our own transient emits back down would make the pending\n// edit look already committed and swallow its `commit`.\nlet editPending = false;\n\nwatch(\n () => props.modelValue,\n (value) => {\n lastValue.value = value;\n if (!editPending) lastCommittedValue.value = value;\n },\n);\n\nfunction onUpdate(value: number | undefined) {\n // Reka clears its model to `undefined` when the text in the field does not\n // parse — an emptied field, most reachably. The contract here is a number,\n // so the field falls back to its own floor instead of forwarding a hole,\n // which is also what Reka itself does when a stepper is pressed on\n // unparseable text.\n const next = value ?? props.min ?? 0;\n editPending = true;\n lastValue.value = next;\n emit(\"update:modelValue\", next);\n}\n\nfunction commitLastValue() {\n // The boundary ends the edit session even when the value ended up back where\n // it started — a drag that returned to its origin still ends the gesture.\n editPending = false;\n if (lastValue.value === lastCommittedValue.value) return;\n lastCommittedValue.value = lastValue.value;\n emit(\"commit\", lastValue.value ?? props.min ?? 0);\n}\n\n// Shift = ×10. Reka's own arrow handling is bound on the input and ignores\n// `shiftKey`, so this intercepts in the capture phase — strictly before the\n// event reaches the input — and fully replaces the default ×1 tick for this\n// one case. Letting both run would double-step.\nfunction onKeydownCapture(event: KeyboardEvent) {\n if (props.disabled) return;\n if (!event.shiftKey || (event.key !== \"ArrowUp\" && event.key !== \"ArrowDown\")) return;\n event.preventDefault();\n event.stopPropagation();\n const tick = (props.step || 1) * 10;\n const current = lastValue.value ?? props.min ?? 0;\n onUpdate(clampValue(event.key === \"ArrowUp\" ? current + tick : current - tick));\n}\n\n// Bubble phase, not capture: by the time this fires, the input's own Enter\n// handling has already applied the typed text, because bubbling visits the\n// target before its ancestors.\nfunction onKeydownCommit(event: KeyboardEvent) {\n if (event.key !== \"Enter\") return;\n commitLastValue();\n}\n\n// A held arrow key is one gesture, exactly like a pointer drag: every keydown,\n// including the operating system's auto-repeats, only ticks the transient\n// value, and the keyup ends the gesture and commits the summed delta once. A\n// single tap is a keydown plus a keyup, so it still commits once.\nfunction onKeyupCommit(event: KeyboardEvent) {\n if (event.key !== \"ArrowUp\" && event.key !== \"ArrowDown\") return;\n commitLastValue();\n}\n\n// `focusout` bubbles where `blur` does not, so this reliably runs after the\n// input's own blur handling — but it only commits once focus has actually left\n// the field, not when it shifts internally to the hover-revealed stepper.\nfunction onFocusOut(event: FocusEvent) {\n const root = event.currentTarget as HTMLElement;\n if (event.relatedTarget instanceof Node && root.contains(event.relatedTarget)) return;\n commitLastValue();\n}\n\n// Horizontal scrub. A plain click must still place the caret for typing, so\n// the gesture does not take over until the pointer has moved past a small\n// threshold.\nconst DRAG_THRESHOLD_PX = 3;\nconst PIXELS_PER_STEP = 4;\n\n// Set while a drag's window listeners are attached, so unmounting mid-drag\n// detaches them instead of leaking handlers onto `window`.\nlet removeDragListeners: (() => void) | undefined;\nonUnmounted(() => removeDragListeners?.());\n\nfunction onPointerDown(event: PointerEvent) {\n if (props.disabled || event.button !== 0) return;\n const startX = event.clientX;\n const startValue = lastValue.value ?? props.min ?? 0;\n const step = props.step || 1;\n let dragging = false;\n\n function onPointerMove(moveEvent: PointerEvent) {\n const deltaX = moveEvent.clientX - startX;\n if (!dragging) {\n if (Math.abs(deltaX) < DRAG_THRESHOLD_PX) return;\n dragging = true;\n window.getSelection()?.removeAllRanges();\n }\n moveEvent.preventDefault();\n const deltaSteps = Math.round(deltaX / PIXELS_PER_STEP);\n const next = clampValue(startValue + deltaSteps * step);\n if (next !== lastValue.value) onUpdate(next);\n }\n\n function onPointerUp() {\n removeDragListeners?.();\n if (dragging) commitLastValue();\n }\n\n // Bail-out (Escape mid-scrub, or a browser-issued pointercancel): the\n // gesture is discarded. Nothing commits, and one last transient update\n // carrying the start value lets the host restore the pre-drag preview.\n function abortDrag() {\n removeDragListeners?.();\n if (!dragging) return;\n if (lastValue.value !== startValue) onUpdate(startValue);\n editPending = false;\n }\n\n function onDragKeydown(keyEvent: KeyboardEvent) {\n if (keyEvent.key !== \"Escape\" || !dragging) return;\n keyEvent.preventDefault();\n keyEvent.stopPropagation();\n abortDrag();\n }\n\n window.addEventListener(\"pointermove\", onPointerMove);\n window.addEventListener(\"pointerup\", onPointerUp);\n window.addEventListener(\"pointercancel\", abortDrag);\n window.addEventListener(\"keydown\", onDragKeydown, true);\n removeDragListeners = () => {\n window.removeEventListener(\"pointermove\", onPointerMove);\n window.removeEventListener(\"pointerup\", onPointerUp);\n window.removeEventListener(\"pointercancel\", abortDrag);\n window.removeEventListener(\"keydown\", onDragKeydown, true);\n removeDragListeners = undefined;\n };\n}\n</script>\n\n<template>\n <NumberFieldRoot\n v-bind=\"rootBounds\"\n :step=\"step\"\n :disabled=\"disabled\"\n :aria-disabled=\"disabled || undefined\"\n :data-invalid=\"invalid || undefined\"\n :class=\"\n cn(\n 'group relative inline-flex h-9 w-full items-center rounded-md border border-input bg-background',\n 'transition-[color,background-color,box-shadow] duration-fast ease-out',\n // Rim-lit at rest; the ring blooms on focus.\n 'focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-ring',\n !invalid && 'focus-within:shadow-halo',\n invalid && 'border-destructive focus-within:outline-destructive',\n 'data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50',\n attrs.class as string,\n )\n \"\n @update:model-value=\"onUpdate\"\n @keydown.capture=\"onKeydownCapture\"\n @keydown=\"onKeydownCommit\"\n @keyup=\"onKeyupCommit\"\n @focusout=\"onFocusOut\"\n @pointerdown=\"onPointerDown\"\n >\n <NumberFieldInput\n v-bind=\"inputAttrs\"\n :aria-invalid=\"invalid || undefined\"\n :class=\"\n cn(\n 'tabular h-full w-full flex-1 rounded-md bg-transparent px-3 text-sm text-foreground outline-none',\n unit ? 'pr-9' : 'pr-3',\n disabled ? 'cursor-not-allowed' : 'cursor-ew-resize',\n )\n \"\n />\n <!-- The unit is presentation, so it is hidden from assistive technology\n and it clears out on hover to make room for the stepper. -->\n <span\n v-if=\"unit\"\n aria-hidden=\"true\"\n class=\"pointer-events-none absolute right-2 text-xs text-muted-foreground transition-opacity duration-fast group-hover:opacity-0\"\n >\n {{ unit }}\n </span>\n <!-- The stepper is hidden until hover: a column of these is read as a\n column of numbers, and a permanent pair of chevrons on every row turns\n that into a column of controls. -->\n <div\n class=\"absolute right-1 flex flex-col opacity-0 transition-opacity duration-fast group-hover:opacity-100\"\n >\n <NumberFieldIncrement\n class=\"flex h-4 w-4 items-center justify-center rounded-sm text-muted-foreground [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out)] hover:bg-subtle hover:text-foreground active:scale-[0.97] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50\"\n >\n <!-- Stroke lives on the 24 grid and scales with size, so the inherited\n 1.5 would render 0.75 device pixels at this 12px box and all but\n disappear. 2.5 restores the optical weight. -->\n <ChevronUp class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </NumberFieldIncrement>\n <NumberFieldDecrement\n class=\"flex h-4 w-4 items-center justify-center rounded-sm text-muted-foreground [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out)] hover:bg-subtle hover:text-foreground active:scale-[0.97] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50\"\n >\n <ChevronDown class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </NumberFieldDecrement>\n </div>\n </NumberFieldRoot>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"NumberField.js","names":[],"sources":["../../../src/primitives/NumberField/NumberField.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Everything this control publishes to assistive technology — and all three of\n * these are names Reka UI would otherwise supply in English of its own accord.\n *\n * `increment` and `decrement` are the stepper buttons, which Reka labels\n * `Increase` and `Decrease` inside its own render function with no prop to\n * reach them by. `roleDescription` replaces the `aria-roledescription=\"Number\n * field\"` Reka writes on the spinbutton itself — the phrase a screen reader\n * announces *in place of* \"spin button\", so leaving it unreachable means a\n * fully localised form still names its own control in English.\n *\n * A binding on the Loom side arrives as a fallthrough attribute, which Vue\n * merges onto the root vnode after Reka's render function produced it, and\n * `mergeProps` is last-write-wins for everything that is not `class`, `style`\n * or `on*`. So these replace Reka's rather than competing with it, and\n * `NumberField.test.ts` pins that by overriding each one and reading the DOM\n * back — the check the wording alone cannot make, since Loom's English for the\n * role description is the same phrase Reka chose.\n */\nexport interface NumberFieldLabels {\n /** The stepper's up button. */\n readonly increment: string;\n /** The stepper's down button. */\n readonly decrement: string;\n /**\n * What the field calls itself, announced in place of \"spin button\". Set it\n * to the kind of number the field holds — \"Rotation, in degrees\" — where\n * that is more use to a reader than the control's generic name.\n */\n readonly roleDescription: string;\n}\n\n/**\n * Loom's English, co-located with the component so it tree-shakes with it, and\n * exported so a host can build a partial vocabulary against the real thing.\n */\nexport const NUMBER_FIELD_LABELS: NumberFieldLabels = {\n increment: \"Increase value\",\n decrement: \"Decrease value\",\n roleDescription: \"Number field\",\n};\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, onUnmounted, ref, useTemplateRef, watch } from \"vue\";\nimport {\n NumberFieldRoot,\n NumberFieldInput,\n NumberFieldIncrement,\n NumberFieldDecrement,\n} from \"reka-ui\";\nimport { ChevronUp, ChevronDown } from \"@lucide/vue\";\nimport { cn } from \"../../lib/cn\";\nimport { useSplitAttrs } from \"../../lib/attrs\";\nimport { useAncestorDisabled } from \"../../lib/ancestor-disabled\";\nimport { useFieldControl } from \"../../lib/field-context\";\nimport { useLabels, type LabelOverrides } from \"../../lib/labels\";\nimport { optional } from \"../../lib/props\";\n\n/**\n * NumberField — a constrained number input you can also *scrub*: drag\n * horizontally anywhere on the field and the value runs with the pointer, the\n * gesture professional editors trained everyone to expect on a numeric row.\n *\n * Built on Reka UI's NumberField, which supplies the spinbutton semantics and\n * the typed-value parsing. Two things are added here because Reka has no\n * equivalent: the scrub-drag gesture, and a Shift multiplier on the arrow\n * keys.\n *\n * The event contract is what makes it usable behind an undo stack.\n * `update:modelValue` is transient — it fires on every drag tick, every arrow\n * tick and every applied edit, so a host can paint a live preview. `commit`\n * fires once per gesture boundary — drag release, Enter, or focus leaving the\n * field. One long drag is therefore one undo checkpoint rather than one per\n * pixel, and a gesture that ends where it started produces none at all.\n *\n * Inside a [Field](../Field/Field.vue) it wires itself: the row's id, the id of\n * its hint or error line, `required`, `invalid`, `disabled`, `readonly` and the\n * name the value is submitted under all arrive through `useFieldControl()`, so\n * `<Field label=\"Rotation\" error=\"…\"><NumberField … /></Field>` needs no\n * attributes at the call site. Every prop below still wins over the row when it\n * is set, in both directions — which is why the three booleans default to\n * `undefined` rather than `false`.\n *\n * `readonly` and `disabled` are different states here for the same reason they\n * are on a text input, and a number is the case that makes it obvious: a rate,\n * a computed total or a locked coordinate is a value on show. It stays a Tab\n * stop, stays in the form's submitted data, and takes a lifted fill with the\n * number at full strength, one step short of the disabled state's drained fill,\n * muted number and slackened rim — and every path that would change it is\n * closed, the stepper included, because a control offering a gesture that does\n * nothing is worse than one that offers none.\n */\nconst props = withDefaults(\n defineProps<{\n /** The current value. Transient edits are reported before it changes. */\n modelValue?: number;\n /** Lower bound. Every path — typing, arrows, scrub — is clamped to it. */\n min?: number;\n /** Upper bound, clamped the same way. It wins over step alignment. */\n max?: number;\n /** The granularity of one arrow tick or one scrub step. Shift multiplies it by ten. */\n step?: number;\n /** A presentational suffix such as `px` or `deg`. It is never part of the value. */\n unit?: string;\n /** Unavailable: dims the field and refuses every edit path. Unset defers to a wrapping Field. */\n disabled?: boolean | undefined;\n /** Shows the value without allowing an edit — typing, arrows, stepper and scrub all closed — while staying focusable and still submitted. Unset defers to a wrapping Field. */\n readonly?: boolean | undefined;\n /** Error state: paints the destructive border and ring, and sets `aria-invalid`. Unset defers to a wrapping Field's `error`. */\n invalid?: boolean | undefined;\n /**\n * Names for everything this control says out loud, as any subset of\n * `NumberFieldLabels` — the rest stay as the host's `provideLoomLabels`\n * vocabulary left them, and then as Loom's English.\n *\n * This is the per-instance correction, not the place to localise an\n * application: the case it exists for is `roleDescription`, where a field\n * holding a rotation or a price is more useful named as that than as a\n * number field.\n */\n labels?: LabelOverrides<NumberFieldLabels>;\n }>(),\n {\n step: 1,\n // Not `false`, for any of the three. `false` is a caller saying \"this field\n // is fine / enabled / editable even though its row is not\"; absent is a\n // caller saying nothing, and only `undefined` survives to mean that past\n // Vue's absent-Boolean-casts-to-false rule — see TextField.vue, which\n // carries the full reasoning.\n disabled: undefined,\n readonly: undefined,\n invalid: undefined,\n },\n);\n\nconst emit = defineEmits<{\n /** Transient: every drag tick, arrow tick, or applied edit. Not a checkpoint. */\n \"update:modelValue\": [value: number];\n /** Committed: once per gesture boundary — drag release, Enter, or focus leaving. */\n commit: [value: number];\n}>();\n\n// `text`, not `labels`: the prop of that name is one of the three sources this\n// resolves, and a template reading the raw prop would be reading the overrides\n// rather than the answer.\nconst text = useLabels(\"numberField\", NUMBER_FIELD_LABELS, () => props.labels);\n\n// The rendered root is a non-focusable `role=\"group\"` wrapper, so fallthrough\n// attributes that describe the *control* — `aria-labelledby`, `data-testid` —\n// belong on the spinbutton inside it, not on the group. `class` is the one\n// exception and goes the other way: a caller's sizing or layout class has to\n// land on the element that participates in the parent's layout, merged through\n// `cn()` so a caller's `w-24` actually beats the root's own `w-full` instead of\n// losing to whichever utility Tailwind happened to emit last.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: inputAttrs } = useSplitAttrs();\n\n/**\n * The scrub is the reason this control cannot be left to the platform.\n *\n * `<fieldset disabled>` makes the inner `<input>` and the two stepper buttons\n * inert on its own, which covers the keyboard and the two clicks — but the\n * drag-to-change gesture is a `pointerdown` on the **root**, and a `<div>` is\n * not a form control, so nothing native stops it. `onPointerDown` bails on\n * `field.disabled`, and that is exactly the value a disabled fieldset does not\n * reach. Without this, a field inside a disabled group looked unavailable,\n * refused the keyboard, and still changed its number under a drag.\n *\n * Read off the DOM rather than taken from the Field context, which publishes no\n * `disabled` on purpose — see `../../lib/ancestor-disabled.ts`.\n */\nconst root = useTemplateRef<{ $el?: Element }>(\"root\");\nconst groupDisabled = useAncestorDisabled(() => root.value?.$el);\n\n// `id` and `aria-describedby` reach this control as fallthrough attrs rather\n// than props, so they are read off `attrs` and handed in as this caller's own\n// values — a caller who sets either still beats the row, and the row's\n// description is merged into theirs rather than replacing it. `name` and\n// `required` are absent from this object because there is no prop to oppose\n// the row with; the row's answer stands unopposed, which is what is wanted.\nconst field = useFieldControl(() => ({\n id: attrs.id as string | undefined,\n describedBy: attrs[\"aria-describedby\"] as string | undefined,\n // The fieldset beats the prop in both directions, exactly as it does for the\n // `<input>` inside this very control.\n disabled: groupDisabled.value ? true : props.disabled,\n readonly: props.readonly,\n invalid: props.invalid,\n}));\n\n// `name` is the one resolved key that does not travel with the bag, and here\n// it is a defect rather than a nicety: the bag lands on the spinbutton, and\n// what the spinbutton holds is the *formatted* number — `1,234` for 1234. A\n// `name` on it would post the grouping separators along with the digits. Reka\n// submits the model value itself through a hidden input it renders from the\n// root's own `name`, so that is where the row's name goes. Everything else in\n// the bag — the id, the merged description, `aria-required`, `aria-invalid` —\n// belongs on the `role=\"spinbutton\"` node, which is that input.\nconst inputFieldAttrs = computed(() => {\n // Removed from a copy rather than listed positively, so a key added to the\n // resolved bag later reaches this input without anyone remembering to come\n // back here.\n const aria: Record<string, unknown> = { ...field.attrs };\n delete aria.name;\n return aria;\n});\n\n// `optional()` drops the absent bounds and an absent name rather than\n// forwarding `undefined`, which Reka would read as \"a bound that is undefined\"\n// rather than \"no bound\" — and, for the name, would have it render a hidden\n// form input for a field nothing is submitting.\nconst rootProps = computed(() =>\n optional({\n modelValue: lastValue.value,\n min: props.min,\n max: props.max,\n name: field.name,\n }),\n);\n\nfunction clampValue(value: number): number {\n const step = props.step || 1;\n const stepped = Math.round(value / step) * step;\n const withMin = props.min !== undefined ? Math.max(props.min, stepped) : stepped;\n return props.max !== undefined ? Math.min(props.max, withMin) : withMin;\n}\n\n// The single source of truth for \"the last value we know about\", updated the\n// moment it changes — never re-derived from `props.modelValue` at commit time,\n// because that prop only reflects a new value after the host's v-model\n// round-trip re-renders us, which is a tick later than the blur or keydown\n// being handled.\n//\n// It is also what the Reka root binds as its own `model-value`, rather than\n// `props.modelValue`. Given a defined model value Reka runs fully controlled:\n// its increment reads back through that prop, so a host that correctly\n// withholds transient echoes until commit would freeze every arrow tick at\n// start + 1 and freeze the readout at the gesture's start value. Feeding it\n// `lastValue` keeps the ticks accumulating and the readout live inside a\n// gesture, while the host still sees nothing but transient emits until the\n// boundary.\nconst lastValue = ref(props.modelValue);\n\n// The value the host last acknowledged. `commit` only fires when `lastValue`\n// differs from it, so overlapping boundaries (Enter then blur, drag release\n// then blur) and no-edit focus passes (Tab in, Tab out) produce no extra\n// checkpoints.\nconst lastCommittedValue = ref(props.modelValue);\n\n// True while an uncommitted edit is in flight. An external prop change\n// re-baselines `lastCommittedValue` only when nothing is pending — otherwise\n// the host echoing our own transient emits back down would make the pending\n// edit look already committed and swallow its `commit`.\nlet editPending = false;\n\nwatch(\n () => props.modelValue,\n (value) => {\n lastValue.value = value;\n if (!editPending) lastCommittedValue.value = value;\n },\n);\n\nfunction onUpdate(value: number | undefined) {\n // Reka clears its model to `undefined` when the text in the field does not\n // parse — an emptied field, most reachably. The contract here is a number,\n // so the field falls back to its own floor instead of forwarding a hole,\n // which is also what Reka itself does when a stepper is pressed on\n // unparseable text.\n const next = value ?? props.min ?? 0;\n editPending = true;\n lastValue.value = next;\n emit(\"update:modelValue\", next);\n}\n\nfunction commitLastValue() {\n // The boundary ends the edit session even when the value ended up back where\n // it started — a drag that returned to its origin still ends the gesture.\n editPending = false;\n if (lastValue.value === lastCommittedValue.value) return;\n lastCommittedValue.value = lastValue.value;\n emit(\"commit\", lastValue.value ?? props.min ?? 0);\n}\n\n// Shift = ×10. Reka's own arrow handling is bound on the input and ignores\n// `shiftKey`, so this intercepts in the capture phase — strictly before the\n// event reaches the input — and fully replaces the default ×1 tick for this\n// one case. Letting both run would double-step.\nfunction onKeydownCapture(event: KeyboardEvent) {\n // Read-only closes this path as firmly as disabled does. Reka's own arrow\n // handling already bails on `readonly`, and this override replaces it — so\n // without the guard Shift+Arrow would be the one way to edit a field that\n // says it cannot be edited.\n if (field.disabled || field.readonly) return;\n if (!event.shiftKey || (event.key !== \"ArrowUp\" && event.key !== \"ArrowDown\")) return;\n event.preventDefault();\n event.stopPropagation();\n const tick = (props.step || 1) * 10;\n const current = lastValue.value ?? props.min ?? 0;\n onUpdate(clampValue(event.key === \"ArrowUp\" ? current + tick : current - tick));\n}\n\n// Bubble phase, not capture: by the time this fires, the input's own Enter\n// handling has already applied the typed text, because bubbling visits the\n// target before its ancestors.\nfunction onKeydownCommit(event: KeyboardEvent) {\n if (event.key !== \"Enter\") return;\n commitLastValue();\n}\n\n// A held arrow key is one gesture, exactly like a pointer drag: every keydown,\n// including the operating system's auto-repeats, only ticks the transient\n// value, and the keyup ends the gesture and commits the summed delta once. A\n// single tap is a keydown plus a keyup, so it still commits once.\nfunction onKeyupCommit(event: KeyboardEvent) {\n if (event.key !== \"ArrowUp\" && event.key !== \"ArrowDown\") return;\n commitLastValue();\n}\n\n// `focusout` bubbles where `blur` does not, so this reliably runs after the\n// input's own blur handling — but it only commits once focus has actually left\n// the field, not when it shifts internally to the hover-revealed stepper.\nfunction onFocusOut(event: FocusEvent) {\n const root = event.currentTarget as HTMLElement;\n if (event.relatedTarget instanceof Node && root.contains(event.relatedTarget)) return;\n commitLastValue();\n}\n\n// Horizontal scrub. A plain click must still place the caret for typing, so\n// the gesture does not take over until the pointer has moved past a small\n// threshold.\nconst DRAG_THRESHOLD_PX = 3;\nconst PIXELS_PER_STEP = 4;\n\n// Set while a drag's window listeners are attached, so unmounting mid-drag\n// detaches them instead of leaking handlers onto `window`.\nlet removeDragListeners: (() => void) | undefined;\nonUnmounted(() => removeDragListeners?.());\n\nfunction onPointerDown(event: PointerEvent) {\n // A read-only field still places the caret and still selects text, which is\n // the whole point of it — it simply never starts a scrub.\n if (field.disabled || field.readonly || event.button !== 0) return;\n const startX = event.clientX;\n const startValue = lastValue.value ?? props.min ?? 0;\n const step = props.step || 1;\n let dragging = false;\n\n function onPointerMove(moveEvent: PointerEvent) {\n const deltaX = moveEvent.clientX - startX;\n if (!dragging) {\n if (Math.abs(deltaX) < DRAG_THRESHOLD_PX) return;\n dragging = true;\n window.getSelection()?.removeAllRanges();\n }\n moveEvent.preventDefault();\n const deltaSteps = Math.round(deltaX / PIXELS_PER_STEP);\n const next = clampValue(startValue + deltaSteps * step);\n if (next !== lastValue.value) onUpdate(next);\n }\n\n function onPointerUp() {\n removeDragListeners?.();\n if (dragging) commitLastValue();\n }\n\n // Bail-out (Escape mid-scrub, or a browser-issued pointercancel): the\n // gesture is discarded. Nothing commits, and one last transient update\n // carrying the start value lets the host restore the pre-drag preview.\n function abortDrag() {\n removeDragListeners?.();\n if (!dragging) return;\n if (lastValue.value !== startValue) onUpdate(startValue);\n editPending = false;\n }\n\n function onDragKeydown(keyEvent: KeyboardEvent) {\n if (keyEvent.key !== \"Escape\" || !dragging) return;\n keyEvent.preventDefault();\n keyEvent.stopPropagation();\n abortDrag();\n }\n\n window.addEventListener(\"pointermove\", onPointerMove);\n window.addEventListener(\"pointerup\", onPointerUp);\n window.addEventListener(\"pointercancel\", abortDrag);\n window.addEventListener(\"keydown\", onDragKeydown, true);\n removeDragListeners = () => {\n window.removeEventListener(\"pointermove\", onPointerMove);\n window.removeEventListener(\"pointerup\", onPointerUp);\n window.removeEventListener(\"pointercancel\", abortDrag);\n window.removeEventListener(\"keydown\", onDragKeydown, true);\n removeDragListeners = undefined;\n };\n}\n</script>\n\n<template>\n <NumberFieldRoot\n ref=\"root\"\n v-bind=\"rootProps\"\n :step=\"step\"\n :disabled=\"field.disabled\"\n :readonly=\"field.readonly\"\n :aria-disabled=\"field.disabled || undefined\"\n :data-invalid=\"field.invalid || undefined\"\n :class=\"\n cn(\n 'group relative inline-flex h-9 w-full items-center rounded-md border border-input bg-background',\n 'transition-[color,background-color,box-shadow] duration-fast ease-out',\n // Rim-lit at rest; the ring blooms on focus.\n 'focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-ring',\n !field.invalid && 'focus-within:shadow-halo',\n // Three resting appearances, three channels — the same treatment\n // TextField carries at length. Read-only and disabled used to share one\n // fill and part only on the text colour, which is a distinction made in\n // hue alone; the fill and the border weight now part them twice more.\n //\n // Read-only keeps its focus ring and its full-strength number: a\n // read-only value is on show, not an unavailable control. The fill lifts\n // to `subtle`, the rim stays `input` because the field's reach has not\n // changed, and Reka's native `readonly` on the input carries the state\n // to assistive tech, so nothing here rests on colour.\n field.readonly && 'bg-subtle',\n // Unavailable is a *colour* and a *weight*, never an opacity.\n // `opacity-50` on this box faded the value inside it:\n // `--color-foreground` measures 14.09:1 on the resting fill and 2.99:1\n // once composited at half alpha, and the unit suffix beside it — muted,\n // and smaller — fell to 2.02:1. The fill drains a step past read-only\n // and the rim slackens to `border`; the input below takes the matching\n // text colour, which has to be declared there rather than inherited from\n // here because the input names `text-foreground` itself.\n field.disabled && 'border-border bg-muted',\n // Last of the rules that name a border colour: `cn()` resolves one by\n // whichever class it saw last, so a field that is both in error and\n // unavailable would otherwise lose its destructive rim to the disabled\n // one.\n field.invalid && 'border-destructive focus-within:outline-destructive',\n 'data-[disabled]:cursor-not-allowed',\n attrs.class as string,\n )\n \"\n @update:model-value=\"onUpdate\"\n @keydown.capture=\"onKeydownCapture\"\n @keydown=\"onKeydownCommit\"\n @keyup=\"onKeyupCommit\"\n @focusout=\"onFocusOut\"\n @pointerdown=\"onPointerDown\"\n >\n <!-- `aria-roledescription` is written before the `v-bind` rather than after\n it, which is the opposite of the field bag below and deliberate: it is\n replacing Reka's own literal, not a caller's, so a caller who passes\n the attribute directly should still beat it. Reka's is merged inside\n its render function and Vue applies everything from here afterwards,\n so both orders defeat Reka and only this one leaves the caller a way\n past Loom. -->\n <NumberFieldInput\n :aria-roledescription=\"text.roleDescription\"\n v-bind=\"{ ...inputAttrs, ...inputFieldAttrs }\"\n :class=\"\n cn(\n 'tabular h-full w-full flex-1 rounded-md bg-transparent px-3 text-sm text-foreground outline-none',\n unit ? 'pr-9' : 'pr-3',\n // 4.68:1 against the drained fill the root takes, and two steps back\n // from the black an editable value keeps — the state stays plain to\n // see and the number stays plain to read. `cn()` is what makes the\n // pair work: it drops the `text-foreground` above rather than leaving\n // two colours for the stylesheet to order.\n field.disabled && 'cursor-not-allowed text-muted-foreground',\n // The scrub cursor is a promise the field makes about the gesture it\n // takes. Read-only takes no gesture, so it must not make the promise\n // — the caret cursor is the honest one for a value you may select\n // and copy but not change.\n !field.disabled && !field.readonly && 'cursor-ew-resize',\n )\n \"\n />\n <!-- The unit is presentation, so it is hidden from assistive technology\n and it clears out on hover to make room for the stepper. -->\n <span\n v-if=\"unit\"\n aria-hidden=\"true\"\n class=\"pointer-events-none absolute right-2 text-xs text-muted-foreground transition-opacity duration-fast group-hover:opacity-0\"\n >\n {{ unit }}\n </span>\n <!-- The stepper is hidden until hover: a column of these is read as a\n column of numbers, and a permanent pair of chevrons on every row turns\n that into a column of controls.\n\n It is not rendered at all on a read-only field. Reka's own\n `handleChangingValue` bails on `readonly` *after* focusing the input,\n so a rendered stepper would be two named buttons that a pointer or a\n screen reader can still reach and press, and that answer by moving\n focus and changing nothing. Absent is the honest state. -->\n <div\n v-if=\"!field.readonly\"\n class=\"absolute right-1 flex flex-col opacity-0 transition-opacity duration-fast group-hover:opacity-100\"\n >\n <!-- Both `:aria-label`s replace one Reka writes in English inside its own\n render function — \"Increase\" and \"Decrease\". They reach the DOM node\n as fallthrough attributes, which Vue merges last, so each is a\n replacement rather than a second name. Removing one does not fall\n back to nothing; it falls back to Reka's English.\n\n The two keep `data-[disabled]:opacity-50` where the box around them\n gave it up, and the difference is that each holds a chevron and\n nothing else. Fading a glyph costs a reader nothing; their names are\n in `aria-label`, not in dimmed text. -->\n <NumberFieldIncrement\n :aria-label=\"text.increment\"\n class=\"flex h-4 w-4 items-center justify-center rounded-sm text-muted-foreground [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out)] hover:bg-subtle hover:text-foreground active:scale-press focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50\"\n >\n <!-- Stroke lives on the 24 grid and scales with size, so the inherited\n 1.5 would render 0.75 device pixels at this 12px box and all but\n disappear. 2.5 restores the optical weight. -->\n <ChevronUp class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </NumberFieldIncrement>\n <NumberFieldDecrement\n :aria-label=\"text.decrement\"\n class=\"flex h-4 w-4 items-center justify-center rounded-sm text-muted-foreground [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out)] hover:bg-subtle hover:text-foreground active:scale-press focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50\"\n >\n <ChevronDown class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </NumberFieldDecrement>\n </div>\n </NumberFieldRoot>\n</template>\n"],"mappings":""}
@@ -1,21 +1,43 @@
1
+ import { type LabelOverrides } from "../../lib/labels";
1
2
  /**
2
- * NumberField a constrained number input you can also *scrub*: drag
3
- * horizontally anywhere on the field and the value runs with the pointer, the
4
- * gesture professional editors trained everyone to expect on a numeric row.
3
+ * Everything this control publishes to assistive technology and all three of
4
+ * these are names Reka UI would otherwise supply in English of its own accord.
5
5
  *
6
- * Built on Reka UI's NumberField, which supplies the spinbutton semantics and
7
- * the typed-value parsing. Two things are added here because Reka has no
8
- * equivalent: the scrub-drag gesture, and a Shift multiplier on the arrow
9
- * keys.
6
+ * `increment` and `decrement` are the stepper buttons, which Reka labels
7
+ * `Increase` and `Decrease` inside its own render function with no prop to
8
+ * reach them by. `roleDescription` replaces the `aria-roledescription="Number
9
+ * field"` Reka writes on the spinbutton itself — the phrase a screen reader
10
+ * announces *in place of* "spin button", so leaving it unreachable means a
11
+ * fully localised form still names its own control in English.
10
12
  *
11
- * The event contract is what makes it usable behind an undo stack.
12
- * `update:modelValue` is transient it fires on every drag tick, every arrow
13
- * tick and every applied edit, so a host can paint a live preview. `commit`
14
- * fires once per gesture boundary drag release, Enter, or focus leaving the
15
- * field. One long drag is therefore one undo checkpoint rather than one per
16
- * pixel, and a gesture that ends where it started produces none at all.
13
+ * A binding on the Loom side arrives as a fallthrough attribute, which Vue
14
+ * merges onto the root vnode after Reka's render function produced it, and
15
+ * `mergeProps` is last-write-wins for everything that is not `class`, `style`
16
+ * or `on*`. So these replace Reka's rather than competing with it, and
17
+ * `NumberField.test.ts` pins that by overriding each one and reading the DOM
18
+ * back the check the wording alone cannot make, since Loom's English for the
19
+ * role description is the same phrase Reka chose.
17
20
  */
18
- type __VLS_Props = {
21
+ export interface NumberFieldLabels {
22
+ /** The stepper's up button. */
23
+ readonly increment: string;
24
+ /** The stepper's down button. */
25
+ readonly decrement: string;
26
+ /**
27
+ * What the field calls itself, announced in place of "spin button". Set it
28
+ * to the kind of number the field holds — "Rotation, in degrees" — where
29
+ * that is more use to a reader than the control's generic name.
30
+ */
31
+ readonly roleDescription: string;
32
+ }
33
+ /**
34
+ * Loom's English, co-located with the component so it tree-shakes with it, and
35
+ * exported so a host can build a partial vocabulary against the real thing.
36
+ */
37
+ export declare const NUMBER_FIELD_LABELS: NumberFieldLabels;
38
+ declare const _default: typeof __VLS_export;
39
+ export default _default;
40
+ declare const __VLS_export: import("vue").DefineComponent<{
19
41
  /** The current value. Transient edits are reported before it changes. */
20
42
  modelValue?: number;
21
43
  /** Lower bound. Every path — typing, arrows, scrub — is clamped to it. */
@@ -26,22 +48,61 @@ type __VLS_Props = {
26
48
  step?: number;
27
49
  /** A presentational suffix such as `px` or `deg`. It is never part of the value. */
28
50
  unit?: string;
29
- /** Unavailable: dims the field and refuses every edit path. */
30
- disabled?: boolean;
31
- /** Error state: paints the destructive border and ring, and sets `aria-invalid`. */
32
- invalid?: boolean;
33
- };
34
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
51
+ /** Unavailable: dims the field and refuses every edit path. Unset defers to a wrapping Field. */
52
+ disabled?: boolean | undefined;
53
+ /** Shows the value without allowing an edit — typing, arrows, stepper and scrub all closed — while staying focusable and still submitted. Unset defers to a wrapping Field. */
54
+ readonly?: boolean | undefined;
55
+ /** Error state: paints the destructive border and ring, and sets `aria-invalid`. Unset defers to a wrapping Field's `error`. */
56
+ invalid?: boolean | undefined;
57
+ /**
58
+ * Names for everything this control says out loud, as any subset of
59
+ * `NumberFieldLabels` — the rest stay as the host's `provideLoomLabels`
60
+ * vocabulary left them, and then as Loom's English.
61
+ *
62
+ * This is the per-instance correction, not the place to localise an
63
+ * application: the case it exists for is `roleDescription`, where a field
64
+ * holding a rotation or a price is more useful named as that than as a
65
+ * number field.
66
+ */
67
+ labels?: LabelOverrides<NumberFieldLabels>;
68
+ }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
35
69
  "update:modelValue": (value: number) => any;
36
70
  commit: (value: number) => any;
37
- }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
71
+ }, string, import("vue").PublicProps, Readonly<{
72
+ /** The current value. Transient edits are reported before it changes. */
73
+ modelValue?: number;
74
+ /** Lower bound. Every path — typing, arrows, scrub — is clamped to it. */
75
+ min?: number;
76
+ /** Upper bound, clamped the same way. It wins over step alignment. */
77
+ max?: number;
78
+ /** The granularity of one arrow tick or one scrub step. Shift multiplies it by ten. */
79
+ step?: number;
80
+ /** A presentational suffix such as `px` or `deg`. It is never part of the value. */
81
+ unit?: string;
82
+ /** Unavailable: dims the field and refuses every edit path. Unset defers to a wrapping Field. */
83
+ disabled?: boolean | undefined;
84
+ /** Shows the value without allowing an edit — typing, arrows, stepper and scrub all closed — while staying focusable and still submitted. Unset defers to a wrapping Field. */
85
+ readonly?: boolean | undefined;
86
+ /** Error state: paints the destructive border and ring, and sets `aria-invalid`. Unset defers to a wrapping Field's `error`. */
87
+ invalid?: boolean | undefined;
88
+ /**
89
+ * Names for everything this control says out loud, as any subset of
90
+ * `NumberFieldLabels` — the rest stay as the host's `provideLoomLabels`
91
+ * vocabulary left them, and then as Loom's English.
92
+ *
93
+ * This is the per-instance correction, not the place to localise an
94
+ * application: the case it exists for is `roleDescription`, where a field
95
+ * holding a rotation or a price is more useful named as that than as a
96
+ * number field.
97
+ */
98
+ labels?: LabelOverrides<NumberFieldLabels>;
99
+ }> & Readonly<{
38
100
  "onUpdate:modelValue"?: (value: number) => any;
39
101
  onCommit?: (value: number) => any;
40
102
  }>, {
41
- disabled: boolean;
42
- invalid: boolean;
103
+ disabled: boolean | undefined;
104
+ invalid: boolean | undefined;
105
+ readonly: boolean | undefined;
43
106
  step: number;
44
107
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
45
- declare const _default: typeof __VLS_export;
46
- export default _default;
47
108
  //# sourceMappingURL=NumberField.vue.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NumberField.vue.d.ts","sourceRoot":"","sources":["../../../src/primitives/NumberField/NumberField.vue"],"names":[],"mappings":"AAuUA;;;;;;;;;;;;;;;;GAgBG;AACH,KAAK,WAAW,GAAG;IACf,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oFAAoF;IACpF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAgdJ,QAAA,MAAM,YAAY;;;;;;;cAndH,OAAO;aAER,OAAO;UANV,MAAM;6EA2df,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
1
+ {"version":3,"file":"NumberField.vue.d.ts","sourceRoot":"","sources":["../../../src/primitives/NumberField/NumberField.vue"],"names":[],"mappings":"AAgiBA,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIlE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,iBAAiB;IAChC,+BAA+B;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iCAAiC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,iBAIjC,CAAC;wBACmB,OAAO,YAAY;AAAxC,wBAAyC;AACzC,QAAA,MAAM,YAAY;IAoCd,yEAAyE;iBAC5D,MAAM;IACnB,0EAA0E;UACpE,MAAM;IACZ,sEAAsE;UAChE,MAAM;IACZ,uFAAuF;WAChF,MAAM;IACb,oFAAoF;WAC7E,MAAM;IACb,iGAAiG;eACtF,OAAO,GAAG,SAAS;IAC9B,+KAA+K;eACpK,OAAO,GAAG,SAAS;IAC9B,gIAAgI;cACtH,OAAO,GAAG,SAAS;IAC7B;;;;;;;;;OASG;aACM,cAAc,CAAC,iBAAiB,CAAC;;;;;IA1B1C,yEAAyE;iBAC5D,MAAM;IACnB,0EAA0E;UACpE,MAAM;IACZ,sEAAsE;UAChE,MAAM;IACZ,uFAAuF;WAChF,MAAM;IACb,oFAAoF;WAC7E,MAAM;IACb,iGAAiG;eACtF,OAAO,GAAG,SAAS;IAC9B,+KAA+K;eACpK,OAAO,GAAG,SAAS;IAC9B,gIAAgI;cACtH,OAAO,GAAG,SAAS;IAC7B;;;;;;;;;OASG;aACM,cAAc,CAAC,iBAAiB,CAAC;;;;;cAf/B,OAAO,GAAG,SAAS;aAIpB,OAAO,GAAG,SAAS;cAFlB,OAAO,GAAG,SAAS;UANvB,MAAM;6EAspBb,CAAC"}
@@ -1,8 +1,11 @@
1
1
  import { cn } from "../../lib/cn.js";
2
2
  import { optional } from "../../lib/props.js";
3
+ import { useLabels } from "../../lib/labels.js";
3
4
  import { useSplitAttrs } from "../../lib/attrs.js";
5
+ import { useFieldControl } from "../../lib/field-context.js";
6
+ import { useAncestorDisabled } from "../../lib/ancestor-disabled.js";
4
7
  import { ChevronDown, ChevronUp } from "@lucide/vue";
5
- import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, mergeProps, onUnmounted, openBlock, ref, toDisplayString, unref, watch, withCtx } from "vue";
8
+ import { computed, createBlock, createCommentVNode, createElementBlock, createVNode, defineComponent, mergeProps, onUnmounted, openBlock, ref, toDisplayString, unref, useTemplateRef, watch, withCtx } from "vue";
6
9
  import { NumberFieldDecrement, NumberFieldIncrement, NumberFieldInput, NumberFieldRoot } from "reka-ui";
7
10
  //#region src/primitives/NumberField/NumberField.vue?vue&type=script&setup=true&lang.ts
8
11
  var _hoisted_1 = {
@@ -10,26 +13,19 @@ var _hoisted_1 = {
10
13
  "aria-hidden": "true",
11
14
  class: "pointer-events-none absolute right-2 text-xs text-muted-foreground transition-opacity duration-fast group-hover:opacity-0"
12
15
  };
13
- var _hoisted_2 = { class: "absolute right-1 flex flex-col opacity-0 transition-opacity duration-fast group-hover:opacity-100" };
16
+ var _hoisted_2 = {
17
+ key: 1,
18
+ class: "absolute right-1 flex flex-col opacity-0 transition-opacity duration-fast group-hover:opacity-100"
19
+ };
14
20
  /**
15
- * NumberField a constrained number input you can also *scrub*: drag
16
- * horizontally anywhere on the field and the value runs with the pointer, the
17
- * gesture professional editors trained everyone to expect on a numeric row.
18
- *
19
- * Built on Reka UI's NumberField, which supplies the spinbutton semantics and
20
- * the typed-value parsing. Two things are added here because Reka has no
21
- * equivalent: the scrub-drag gesture, and a Shift multiplier on the arrow
22
- * keys.
23
- *
24
- * The event contract is what makes it usable behind an undo stack.
25
- * `update:modelValue` is transient — it fires on every drag tick, every arrow
26
- * tick and every applied edit, so a host can paint a live preview. `commit`
27
- * fires once per gesture boundary — drag release, Enter, or focus leaving the
28
- * field. One long drag is therefore one undo checkpoint rather than one per
29
- * pixel, and a gesture that ends where it started produces none at all.
21
+ * Loom's English, co-located with the component so it tree-shakes with it, and
22
+ * exported so a host can build a partial vocabulary against the real thing.
30
23
  */
31
- var DRAG_THRESHOLD_PX = 3;
32
- var PIXELS_PER_STEP = 4;
24
+ var NUMBER_FIELD_LABELS = {
25
+ increment: "Increase value",
26
+ decrement: "Decrease value",
27
+ roleDescription: "Number field"
28
+ };
33
29
  var NumberField_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
34
30
  inheritAttrs: false,
35
31
  __name: "NumberField",
@@ -41,22 +37,57 @@ var NumberField_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
41
37
  unit: {},
42
38
  disabled: {
43
39
  type: Boolean,
44
- default: false
40
+ default: void 0
41
+ },
42
+ readonly: {
43
+ type: Boolean,
44
+ default: void 0
45
45
  },
46
46
  invalid: {
47
47
  type: Boolean,
48
- default: false
49
- }
48
+ default: void 0
49
+ },
50
+ labels: {}
50
51
  },
51
52
  emits: ["update:modelValue", "commit"],
52
53
  setup(__props, { emit: __emit }) {
53
54
  const props = __props;
54
55
  const emit = __emit;
56
+ const text = useLabels("numberField", NUMBER_FIELD_LABELS, () => props.labels);
55
57
  const { attrs, rest: inputAttrs } = useSplitAttrs();
56
- const rootBounds = computed(() => optional({
58
+ /**
59
+ * The scrub is the reason this control cannot be left to the platform.
60
+ *
61
+ * `<fieldset disabled>` makes the inner `<input>` and the two stepper buttons
62
+ * inert on its own, which covers the keyboard and the two clicks — but the
63
+ * drag-to-change gesture is a `pointerdown` on the **root**, and a `<div>` is
64
+ * not a form control, so nothing native stops it. `onPointerDown` bails on
65
+ * `field.disabled`, and that is exactly the value a disabled fieldset does not
66
+ * reach. Without this, a field inside a disabled group looked unavailable,
67
+ * refused the keyboard, and still changed its number under a drag.
68
+ *
69
+ * Read off the DOM rather than taken from the Field context, which publishes no
70
+ * `disabled` on purpose — see `../../lib/ancestor-disabled.ts`.
71
+ */
72
+ const root = useTemplateRef("root");
73
+ const groupDisabled = useAncestorDisabled(() => root.value?.$el);
74
+ const field = useFieldControl(() => ({
75
+ id: attrs.id,
76
+ describedBy: attrs["aria-describedby"],
77
+ disabled: groupDisabled.value ? true : props.disabled,
78
+ readonly: props.readonly,
79
+ invalid: props.invalid
80
+ }));
81
+ const inputFieldAttrs = computed(() => {
82
+ const aria = { ...field.attrs };
83
+ delete aria.name;
84
+ return aria;
85
+ });
86
+ const rootProps = computed(() => optional({
57
87
  modelValue: lastValue.value,
58
88
  min: props.min,
59
- max: props.max
89
+ max: props.max,
90
+ name: field.name
60
91
  }));
61
92
  function clampValue(value) {
62
93
  const step = props.step || 1;
@@ -84,7 +115,7 @@ var NumberField_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
84
115
  emit("commit", lastValue.value ?? props.min ?? 0);
85
116
  }
86
117
  function onKeydownCapture(event) {
87
- if (props.disabled) return;
118
+ if (field.disabled || field.readonly) return;
88
119
  if (!event.shiftKey || event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
89
120
  event.preventDefault();
90
121
  event.stopPropagation();
@@ -105,10 +136,12 @@ var NumberField_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
105
136
  if (event.relatedTarget instanceof Node && root.contains(event.relatedTarget)) return;
106
137
  commitLastValue();
107
138
  }
139
+ const DRAG_THRESHOLD_PX = 3;
140
+ const PIXELS_PER_STEP = 4;
108
141
  let removeDragListeners;
109
142
  onUnmounted(() => removeDragListeners?.());
110
143
  function onPointerDown(event) {
111
- if (props.disabled || event.button !== 0) return;
144
+ if (field.disabled || field.readonly || event.button !== 0) return;
112
145
  const startX = event.clientX;
113
146
  const startValue = lastValue.value ?? props.min ?? 0;
114
147
  const step = props.step || 1;
@@ -154,12 +187,16 @@ var NumberField_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
154
187
  };
155
188
  }
156
189
  return (_ctx, _cache) => {
157
- return openBlock(), createBlock(unref(NumberFieldRoot), mergeProps(rootBounds.value, {
190
+ return openBlock(), createBlock(unref(NumberFieldRoot), mergeProps({
191
+ ref_key: "root",
192
+ ref: root
193
+ }, rootProps.value, {
158
194
  step: __props.step,
159
- disabled: __props.disabled,
160
- "aria-disabled": __props.disabled || void 0,
161
- "data-invalid": __props.invalid || void 0,
162
- class: unref(cn)("group relative inline-flex h-9 w-full items-center rounded-md border border-input bg-background", "transition-[color,background-color,box-shadow] duration-fast ease-out", "focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-ring", !__props.invalid && "focus-within:shadow-halo", __props.invalid && "border-destructive focus-within:outline-destructive", "data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50", unref(attrs).class),
195
+ disabled: unref(field).disabled,
196
+ readonly: unref(field).readonly,
197
+ "aria-disabled": unref(field).disabled || void 0,
198
+ "data-invalid": unref(field).invalid || void 0,
199
+ class: unref(cn)("group relative inline-flex h-9 w-full items-center rounded-md border border-input bg-background", "transition-[color,background-color,box-shadow] duration-fast ease-out", "focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-ring", !unref(field).invalid && "focus-within:shadow-halo", unref(field).readonly && "bg-subtle", unref(field).disabled && "border-border bg-muted", unref(field).invalid && "border-destructive focus-within:outline-destructive", "data-[disabled]:cursor-not-allowed", unref(attrs).class),
163
200
  "onUpdate:modelValue": onUpdate,
164
201
  onKeydownCapture,
165
202
  onKeydown: onKeydownCommit,
@@ -168,29 +205,36 @@ var NumberField_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
168
205
  onPointerdown: onPointerDown
169
206
  }), {
170
207
  default: withCtx(() => [
171
- createVNode(unref(NumberFieldInput), mergeProps(unref(inputAttrs), {
172
- "aria-invalid": __props.invalid || void 0,
173
- class: unref(cn)("tabular h-full w-full flex-1 rounded-md bg-transparent px-3 text-sm text-foreground outline-none", __props.unit ? "pr-9" : "pr-3", __props.disabled ? "cursor-not-allowed" : "cursor-ew-resize")
174
- }), null, 16, ["aria-invalid", "class"]),
208
+ createVNode(unref(NumberFieldInput), mergeProps({ "aria-roledescription": unref(text).roleDescription }, {
209
+ ...unref(inputAttrs),
210
+ ...inputFieldAttrs.value
211
+ }, { class: unref(cn)("tabular h-full w-full flex-1 rounded-md bg-transparent px-3 text-sm text-foreground outline-none", __props.unit ? "pr-9" : "pr-3", unref(field).disabled && "cursor-not-allowed text-muted-foreground", !unref(field).disabled && !unref(field).readonly && "cursor-ew-resize") }), null, 16, ["aria-roledescription", "class"]),
175
212
  __props.unit ? (openBlock(), createElementBlock("span", _hoisted_1, toDisplayString(__props.unit), 1)) : createCommentVNode("", true),
176
- createElementVNode("div", _hoisted_2, [createVNode(unref(NumberFieldIncrement), { class: "flex h-4 w-4 items-center justify-center rounded-sm text-muted-foreground [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out)] hover:bg-subtle hover:text-foreground active:scale-[0.97] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50" }, {
213
+ !unref(field).readonly ? (openBlock(), createElementBlock("div", _hoisted_2, [createVNode(unref(NumberFieldIncrement), {
214
+ "aria-label": unref(text).increment,
215
+ class: "flex h-4 w-4 items-center justify-center rounded-sm text-muted-foreground [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out)] hover:bg-subtle hover:text-foreground active:scale-press focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
216
+ }, {
177
217
  default: withCtx(() => [createVNode(unref(ChevronUp), {
178
218
  class: "h-3 w-3",
179
219
  "stroke-width": 2.5
180
220
  })]),
181
221
  _: 1
182
- }), createVNode(unref(NumberFieldDecrement), { class: "flex h-4 w-4 items-center justify-center rounded-sm text-muted-foreground [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out)] hover:bg-subtle hover:text-foreground active:scale-[0.97] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50" }, {
222
+ }, 8, ["aria-label"]), createVNode(unref(NumberFieldDecrement), {
223
+ "aria-label": unref(text).decrement,
224
+ class: "flex h-4 w-4 items-center justify-center rounded-sm text-muted-foreground [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out)] hover:bg-subtle hover:text-foreground active:scale-press focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
225
+ }, {
183
226
  default: withCtx(() => [createVNode(unref(ChevronDown), {
184
227
  class: "h-3 w-3",
185
228
  "stroke-width": 2.5
186
229
  })]),
187
230
  _: 1
188
- })])
231
+ }, 8, ["aria-label"])])) : createCommentVNode("", true)
189
232
  ]),
190
233
  _: 1
191
234
  }, 16, [
192
235
  "step",
193
236
  "disabled",
237
+ "readonly",
194
238
  "aria-disabled",
195
239
  "data-invalid",
196
240
  "class"
@@ -199,6 +243,6 @@ var NumberField_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
199
243
  }
200
244
  });
201
245
  //#endregion
202
- export { NumberField_vue_vue_type_script_setup_true_lang_default as default };
246
+ export { NUMBER_FIELD_LABELS, NumberField_vue_vue_type_script_setup_true_lang_default as default };
203
247
 
204
248
  //# sourceMappingURL=NumberField.vue_vue_type_script_setup_true_lang.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"NumberField.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/primitives/NumberField/NumberField.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onUnmounted, ref, watch } from \"vue\";\nimport {\n NumberFieldRoot,\n NumberFieldInput,\n NumberFieldIncrement,\n NumberFieldDecrement,\n} from \"reka-ui\";\nimport { ChevronUp, ChevronDown } from \"@lucide/vue\";\nimport { cn } from \"../../lib/cn\";\nimport { useSplitAttrs } from \"../../lib/attrs\";\nimport { optional } from \"../../lib/props\";\n\n/**\n * NumberField — a constrained number input you can also *scrub*: drag\n * horizontally anywhere on the field and the value runs with the pointer, the\n * gesture professional editors trained everyone to expect on a numeric row.\n *\n * Built on Reka UI's NumberField, which supplies the spinbutton semantics and\n * the typed-value parsing. Two things are added here because Reka has no\n * equivalent: the scrub-drag gesture, and a Shift multiplier on the arrow\n * keys.\n *\n * The event contract is what makes it usable behind an undo stack.\n * `update:modelValue` is transient — it fires on every drag tick, every arrow\n * tick and every applied edit, so a host can paint a live preview. `commit`\n * fires once per gesture boundary — drag release, Enter, or focus leaving the\n * field. One long drag is therefore one undo checkpoint rather than one per\n * pixel, and a gesture that ends where it started produces none at all.\n */\nconst props = withDefaults(\n defineProps<{\n /** The current value. Transient edits are reported before it changes. */\n modelValue?: number;\n /** Lower bound. Every path — typing, arrows, scrub — is clamped to it. */\n min?: number;\n /** Upper bound, clamped the same way. It wins over step alignment. */\n max?: number;\n /** The granularity of one arrow tick or one scrub step. Shift multiplies it by ten. */\n step?: number;\n /** A presentational suffix such as `px` or `deg`. It is never part of the value. */\n unit?: string;\n /** Unavailable: dims the field and refuses every edit path. */\n disabled?: boolean;\n /** Error state: paints the destructive border and ring, and sets `aria-invalid`. */\n invalid?: boolean;\n }>(),\n { step: 1, disabled: false, invalid: false },\n);\n\nconst emit = defineEmits<{\n /** Transient: every drag tick, arrow tick, or applied edit. Not a checkpoint. */\n \"update:modelValue\": [value: number];\n /** Committed: once per gesture boundary — drag release, Enter, or focus leaving. */\n commit: [value: number];\n}>();\n\n// The rendered root is a non-focusable `role=\"group\"` wrapper, so fallthrough\n// attributes that describe the *control* — `aria-labelledby`, `data-testid` —\n// belong on the spinbutton inside it, not on the group. `class` is the one\n// exception and goes the other way: a caller's sizing or layout class has to\n// land on the element that participates in the parent's layout, merged through\n// `cn()` so a caller's `w-24` actually beats the root's own `w-full` instead of\n// losing to whichever utility Tailwind happened to emit last.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: inputAttrs } = useSplitAttrs();\n\n// `optional()` drops the absent bounds rather than forwarding `undefined`,\n// which Reka would read as \"a bound that is undefined\" rather than \"no bound\".\nconst rootBounds = computed(() =>\n optional({ modelValue: lastValue.value, min: props.min, max: props.max }),\n);\n\nfunction clampValue(value: number): number {\n const step = props.step || 1;\n const stepped = Math.round(value / step) * step;\n const withMin = props.min !== undefined ? Math.max(props.min, stepped) : stepped;\n return props.max !== undefined ? Math.min(props.max, withMin) : withMin;\n}\n\n// The single source of truth for \"the last value we know about\", updated the\n// moment it changes — never re-derived from `props.modelValue` at commit time,\n// because that prop only reflects a new value after the host's v-model\n// round-trip re-renders us, which is a tick later than the blur or keydown\n// being handled.\n//\n// It is also what the Reka root binds as its own `model-value`, rather than\n// `props.modelValue`. Given a defined model value Reka runs fully controlled:\n// its increment reads back through that prop, so a host that correctly\n// withholds transient echoes until commit would freeze every arrow tick at\n// start + 1 and freeze the readout at the gesture's start value. Feeding it\n// `lastValue` keeps the ticks accumulating and the readout live inside a\n// gesture, while the host still sees nothing but transient emits until the\n// boundary.\nconst lastValue = ref(props.modelValue);\n\n// The value the host last acknowledged. `commit` only fires when `lastValue`\n// differs from it, so overlapping boundaries (Enter then blur, drag release\n// then blur) and no-edit focus passes (Tab in, Tab out) produce no extra\n// checkpoints.\nconst lastCommittedValue = ref(props.modelValue);\n\n// True while an uncommitted edit is in flight. An external prop change\n// re-baselines `lastCommittedValue` only when nothing is pending — otherwise\n// the host echoing our own transient emits back down would make the pending\n// edit look already committed and swallow its `commit`.\nlet editPending = false;\n\nwatch(\n () => props.modelValue,\n (value) => {\n lastValue.value = value;\n if (!editPending) lastCommittedValue.value = value;\n },\n);\n\nfunction onUpdate(value: number | undefined) {\n // Reka clears its model to `undefined` when the text in the field does not\n // parse — an emptied field, most reachably. The contract here is a number,\n // so the field falls back to its own floor instead of forwarding a hole,\n // which is also what Reka itself does when a stepper is pressed on\n // unparseable text.\n const next = value ?? props.min ?? 0;\n editPending = true;\n lastValue.value = next;\n emit(\"update:modelValue\", next);\n}\n\nfunction commitLastValue() {\n // The boundary ends the edit session even when the value ended up back where\n // it started — a drag that returned to its origin still ends the gesture.\n editPending = false;\n if (lastValue.value === lastCommittedValue.value) return;\n lastCommittedValue.value = lastValue.value;\n emit(\"commit\", lastValue.value ?? props.min ?? 0);\n}\n\n// Shift = ×10. Reka's own arrow handling is bound on the input and ignores\n// `shiftKey`, so this intercepts in the capture phase — strictly before the\n// event reaches the input — and fully replaces the default ×1 tick for this\n// one case. Letting both run would double-step.\nfunction onKeydownCapture(event: KeyboardEvent) {\n if (props.disabled) return;\n if (!event.shiftKey || (event.key !== \"ArrowUp\" && event.key !== \"ArrowDown\")) return;\n event.preventDefault();\n event.stopPropagation();\n const tick = (props.step || 1) * 10;\n const current = lastValue.value ?? props.min ?? 0;\n onUpdate(clampValue(event.key === \"ArrowUp\" ? current + tick : current - tick));\n}\n\n// Bubble phase, not capture: by the time this fires, the input's own Enter\n// handling has already applied the typed text, because bubbling visits the\n// target before its ancestors.\nfunction onKeydownCommit(event: KeyboardEvent) {\n if (event.key !== \"Enter\") return;\n commitLastValue();\n}\n\n// A held arrow key is one gesture, exactly like a pointer drag: every keydown,\n// including the operating system's auto-repeats, only ticks the transient\n// value, and the keyup ends the gesture and commits the summed delta once. A\n// single tap is a keydown plus a keyup, so it still commits once.\nfunction onKeyupCommit(event: KeyboardEvent) {\n if (event.key !== \"ArrowUp\" && event.key !== \"ArrowDown\") return;\n commitLastValue();\n}\n\n// `focusout` bubbles where `blur` does not, so this reliably runs after the\n// input's own blur handling — but it only commits once focus has actually left\n// the field, not when it shifts internally to the hover-revealed stepper.\nfunction onFocusOut(event: FocusEvent) {\n const root = event.currentTarget as HTMLElement;\n if (event.relatedTarget instanceof Node && root.contains(event.relatedTarget)) return;\n commitLastValue();\n}\n\n// Horizontal scrub. A plain click must still place the caret for typing, so\n// the gesture does not take over until the pointer has moved past a small\n// threshold.\nconst DRAG_THRESHOLD_PX = 3;\nconst PIXELS_PER_STEP = 4;\n\n// Set while a drag's window listeners are attached, so unmounting mid-drag\n// detaches them instead of leaking handlers onto `window`.\nlet removeDragListeners: (() => void) | undefined;\nonUnmounted(() => removeDragListeners?.());\n\nfunction onPointerDown(event: PointerEvent) {\n if (props.disabled || event.button !== 0) return;\n const startX = event.clientX;\n const startValue = lastValue.value ?? props.min ?? 0;\n const step = props.step || 1;\n let dragging = false;\n\n function onPointerMove(moveEvent: PointerEvent) {\n const deltaX = moveEvent.clientX - startX;\n if (!dragging) {\n if (Math.abs(deltaX) < DRAG_THRESHOLD_PX) return;\n dragging = true;\n window.getSelection()?.removeAllRanges();\n }\n moveEvent.preventDefault();\n const deltaSteps = Math.round(deltaX / PIXELS_PER_STEP);\n const next = clampValue(startValue + deltaSteps * step);\n if (next !== lastValue.value) onUpdate(next);\n }\n\n function onPointerUp() {\n removeDragListeners?.();\n if (dragging) commitLastValue();\n }\n\n // Bail-out (Escape mid-scrub, or a browser-issued pointercancel): the\n // gesture is discarded. Nothing commits, and one last transient update\n // carrying the start value lets the host restore the pre-drag preview.\n function abortDrag() {\n removeDragListeners?.();\n if (!dragging) return;\n if (lastValue.value !== startValue) onUpdate(startValue);\n editPending = false;\n }\n\n function onDragKeydown(keyEvent: KeyboardEvent) {\n if (keyEvent.key !== \"Escape\" || !dragging) return;\n keyEvent.preventDefault();\n keyEvent.stopPropagation();\n abortDrag();\n }\n\n window.addEventListener(\"pointermove\", onPointerMove);\n window.addEventListener(\"pointerup\", onPointerUp);\n window.addEventListener(\"pointercancel\", abortDrag);\n window.addEventListener(\"keydown\", onDragKeydown, true);\n removeDragListeners = () => {\n window.removeEventListener(\"pointermove\", onPointerMove);\n window.removeEventListener(\"pointerup\", onPointerUp);\n window.removeEventListener(\"pointercancel\", abortDrag);\n window.removeEventListener(\"keydown\", onDragKeydown, true);\n removeDragListeners = undefined;\n };\n}\n</script>\n\n<template>\n <NumberFieldRoot\n v-bind=\"rootBounds\"\n :step=\"step\"\n :disabled=\"disabled\"\n :aria-disabled=\"disabled || undefined\"\n :data-invalid=\"invalid || undefined\"\n :class=\"\n cn(\n 'group relative inline-flex h-9 w-full items-center rounded-md border border-input bg-background',\n 'transition-[color,background-color,box-shadow] duration-fast ease-out',\n // Rim-lit at rest; the ring blooms on focus.\n 'focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-ring',\n !invalid && 'focus-within:shadow-halo',\n invalid && 'border-destructive focus-within:outline-destructive',\n 'data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50',\n attrs.class as string,\n )\n \"\n @update:model-value=\"onUpdate\"\n @keydown.capture=\"onKeydownCapture\"\n @keydown=\"onKeydownCommit\"\n @keyup=\"onKeyupCommit\"\n @focusout=\"onFocusOut\"\n @pointerdown=\"onPointerDown\"\n >\n <NumberFieldInput\n v-bind=\"inputAttrs\"\n :aria-invalid=\"invalid || undefined\"\n :class=\"\n cn(\n 'tabular h-full w-full flex-1 rounded-md bg-transparent px-3 text-sm text-foreground outline-none',\n unit ? 'pr-9' : 'pr-3',\n disabled ? 'cursor-not-allowed' : 'cursor-ew-resize',\n )\n \"\n />\n <!-- The unit is presentation, so it is hidden from assistive technology\n and it clears out on hover to make room for the stepper. -->\n <span\n v-if=\"unit\"\n aria-hidden=\"true\"\n class=\"pointer-events-none absolute right-2 text-xs text-muted-foreground transition-opacity duration-fast group-hover:opacity-0\"\n >\n {{ unit }}\n </span>\n <!-- The stepper is hidden until hover: a column of these is read as a\n column of numbers, and a permanent pair of chevrons on every row turns\n that into a column of controls. -->\n <div\n class=\"absolute right-1 flex flex-col opacity-0 transition-opacity duration-fast group-hover:opacity-100\"\n >\n <NumberFieldIncrement\n class=\"flex h-4 w-4 items-center justify-center rounded-sm text-muted-foreground [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out)] hover:bg-subtle hover:text-foreground active:scale-[0.97] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50\"\n >\n <!-- Stroke lives on the 24 grid and scales with size, so the inherited\n 1.5 would render 0.75 device pixels at this 12px box and all but\n disappear. 2.5 restores the optical weight. -->\n <ChevronUp class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </NumberFieldIncrement>\n <NumberFieldDecrement\n class=\"flex h-4 w-4 items-center justify-center rounded-sm text-muted-foreground [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out)] hover:bg-subtle hover:text-foreground active:scale-[0.97] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50\"\n >\n <ChevronDown class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </NumberFieldDecrement>\n </div>\n </NumberFieldRoot>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"NumberField.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/primitives/NumberField/NumberField.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Everything this control publishes to assistive technology — and all three of\n * these are names Reka UI would otherwise supply in English of its own accord.\n *\n * `increment` and `decrement` are the stepper buttons, which Reka labels\n * `Increase` and `Decrease` inside its own render function with no prop to\n * reach them by. `roleDescription` replaces the `aria-roledescription=\"Number\n * field\"` Reka writes on the spinbutton itself — the phrase a screen reader\n * announces *in place of* \"spin button\", so leaving it unreachable means a\n * fully localised form still names its own control in English.\n *\n * A binding on the Loom side arrives as a fallthrough attribute, which Vue\n * merges onto the root vnode after Reka's render function produced it, and\n * `mergeProps` is last-write-wins for everything that is not `class`, `style`\n * or `on*`. So these replace Reka's rather than competing with it, and\n * `NumberField.test.ts` pins that by overriding each one and reading the DOM\n * back — the check the wording alone cannot make, since Loom's English for the\n * role description is the same phrase Reka chose.\n */\nexport interface NumberFieldLabels {\n /** The stepper's up button. */\n readonly increment: string;\n /** The stepper's down button. */\n readonly decrement: string;\n /**\n * What the field calls itself, announced in place of \"spin button\". Set it\n * to the kind of number the field holds — \"Rotation, in degrees\" — where\n * that is more use to a reader than the control's generic name.\n */\n readonly roleDescription: string;\n}\n\n/**\n * Loom's English, co-located with the component so it tree-shakes with it, and\n * exported so a host can build a partial vocabulary against the real thing.\n */\nexport const NUMBER_FIELD_LABELS: NumberFieldLabels = {\n increment: \"Increase value\",\n decrement: \"Decrease value\",\n roleDescription: \"Number field\",\n};\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, onUnmounted, ref, useTemplateRef, watch } from \"vue\";\nimport {\n NumberFieldRoot,\n NumberFieldInput,\n NumberFieldIncrement,\n NumberFieldDecrement,\n} from \"reka-ui\";\nimport { ChevronUp, ChevronDown } from \"@lucide/vue\";\nimport { cn } from \"../../lib/cn\";\nimport { useSplitAttrs } from \"../../lib/attrs\";\nimport { useAncestorDisabled } from \"../../lib/ancestor-disabled\";\nimport { useFieldControl } from \"../../lib/field-context\";\nimport { useLabels, type LabelOverrides } from \"../../lib/labels\";\nimport { optional } from \"../../lib/props\";\n\n/**\n * NumberField — a constrained number input you can also *scrub*: drag\n * horizontally anywhere on the field and the value runs with the pointer, the\n * gesture professional editors trained everyone to expect on a numeric row.\n *\n * Built on Reka UI's NumberField, which supplies the spinbutton semantics and\n * the typed-value parsing. Two things are added here because Reka has no\n * equivalent: the scrub-drag gesture, and a Shift multiplier on the arrow\n * keys.\n *\n * The event contract is what makes it usable behind an undo stack.\n * `update:modelValue` is transient — it fires on every drag tick, every arrow\n * tick and every applied edit, so a host can paint a live preview. `commit`\n * fires once per gesture boundary — drag release, Enter, or focus leaving the\n * field. One long drag is therefore one undo checkpoint rather than one per\n * pixel, and a gesture that ends where it started produces none at all.\n *\n * Inside a [Field](../Field/Field.vue) it wires itself: the row's id, the id of\n * its hint or error line, `required`, `invalid`, `disabled`, `readonly` and the\n * name the value is submitted under all arrive through `useFieldControl()`, so\n * `<Field label=\"Rotation\" error=\"…\"><NumberField … /></Field>` needs no\n * attributes at the call site. Every prop below still wins over the row when it\n * is set, in both directions — which is why the three booleans default to\n * `undefined` rather than `false`.\n *\n * `readonly` and `disabled` are different states here for the same reason they\n * are on a text input, and a number is the case that makes it obvious: a rate,\n * a computed total or a locked coordinate is a value on show. It stays a Tab\n * stop, stays in the form's submitted data, and takes a lifted fill with the\n * number at full strength, one step short of the disabled state's drained fill,\n * muted number and slackened rim — and every path that would change it is\n * closed, the stepper included, because a control offering a gesture that does\n * nothing is worse than one that offers none.\n */\nconst props = withDefaults(\n defineProps<{\n /** The current value. Transient edits are reported before it changes. */\n modelValue?: number;\n /** Lower bound. Every path — typing, arrows, scrub — is clamped to it. */\n min?: number;\n /** Upper bound, clamped the same way. It wins over step alignment. */\n max?: number;\n /** The granularity of one arrow tick or one scrub step. Shift multiplies it by ten. */\n step?: number;\n /** A presentational suffix such as `px` or `deg`. It is never part of the value. */\n unit?: string;\n /** Unavailable: dims the field and refuses every edit path. Unset defers to a wrapping Field. */\n disabled?: boolean | undefined;\n /** Shows the value without allowing an edit — typing, arrows, stepper and scrub all closed — while staying focusable and still submitted. Unset defers to a wrapping Field. */\n readonly?: boolean | undefined;\n /** Error state: paints the destructive border and ring, and sets `aria-invalid`. Unset defers to a wrapping Field's `error`. */\n invalid?: boolean | undefined;\n /**\n * Names for everything this control says out loud, as any subset of\n * `NumberFieldLabels` — the rest stay as the host's `provideLoomLabels`\n * vocabulary left them, and then as Loom's English.\n *\n * This is the per-instance correction, not the place to localise an\n * application: the case it exists for is `roleDescription`, where a field\n * holding a rotation or a price is more useful named as that than as a\n * number field.\n */\n labels?: LabelOverrides<NumberFieldLabels>;\n }>(),\n {\n step: 1,\n // Not `false`, for any of the three. `false` is a caller saying \"this field\n // is fine / enabled / editable even though its row is not\"; absent is a\n // caller saying nothing, and only `undefined` survives to mean that past\n // Vue's absent-Boolean-casts-to-false rule — see TextField.vue, which\n // carries the full reasoning.\n disabled: undefined,\n readonly: undefined,\n invalid: undefined,\n },\n);\n\nconst emit = defineEmits<{\n /** Transient: every drag tick, arrow tick, or applied edit. Not a checkpoint. */\n \"update:modelValue\": [value: number];\n /** Committed: once per gesture boundary — drag release, Enter, or focus leaving. */\n commit: [value: number];\n}>();\n\n// `text`, not `labels`: the prop of that name is one of the three sources this\n// resolves, and a template reading the raw prop would be reading the overrides\n// rather than the answer.\nconst text = useLabels(\"numberField\", NUMBER_FIELD_LABELS, () => props.labels);\n\n// The rendered root is a non-focusable `role=\"group\"` wrapper, so fallthrough\n// attributes that describe the *control* — `aria-labelledby`, `data-testid` —\n// belong on the spinbutton inside it, not on the group. `class` is the one\n// exception and goes the other way: a caller's sizing or layout class has to\n// land on the element that participates in the parent's layout, merged through\n// `cn()` so a caller's `w-24` actually beats the root's own `w-full` instead of\n// losing to whichever utility Tailwind happened to emit last.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: inputAttrs } = useSplitAttrs();\n\n/**\n * The scrub is the reason this control cannot be left to the platform.\n *\n * `<fieldset disabled>` makes the inner `<input>` and the two stepper buttons\n * inert on its own, which covers the keyboard and the two clicks — but the\n * drag-to-change gesture is a `pointerdown` on the **root**, and a `<div>` is\n * not a form control, so nothing native stops it. `onPointerDown` bails on\n * `field.disabled`, and that is exactly the value a disabled fieldset does not\n * reach. Without this, a field inside a disabled group looked unavailable,\n * refused the keyboard, and still changed its number under a drag.\n *\n * Read off the DOM rather than taken from the Field context, which publishes no\n * `disabled` on purpose — see `../../lib/ancestor-disabled.ts`.\n */\nconst root = useTemplateRef<{ $el?: Element }>(\"root\");\nconst groupDisabled = useAncestorDisabled(() => root.value?.$el);\n\n// `id` and `aria-describedby` reach this control as fallthrough attrs rather\n// than props, so they are read off `attrs` and handed in as this caller's own\n// values — a caller who sets either still beats the row, and the row's\n// description is merged into theirs rather than replacing it. `name` and\n// `required` are absent from this object because there is no prop to oppose\n// the row with; the row's answer stands unopposed, which is what is wanted.\nconst field = useFieldControl(() => ({\n id: attrs.id as string | undefined,\n describedBy: attrs[\"aria-describedby\"] as string | undefined,\n // The fieldset beats the prop in both directions, exactly as it does for the\n // `<input>` inside this very control.\n disabled: groupDisabled.value ? true : props.disabled,\n readonly: props.readonly,\n invalid: props.invalid,\n}));\n\n// `name` is the one resolved key that does not travel with the bag, and here\n// it is a defect rather than a nicety: the bag lands on the spinbutton, and\n// what the spinbutton holds is the *formatted* number — `1,234` for 1234. A\n// `name` on it would post the grouping separators along with the digits. Reka\n// submits the model value itself through a hidden input it renders from the\n// root's own `name`, so that is where the row's name goes. Everything else in\n// the bag — the id, the merged description, `aria-required`, `aria-invalid` —\n// belongs on the `role=\"spinbutton\"` node, which is that input.\nconst inputFieldAttrs = computed(() => {\n // Removed from a copy rather than listed positively, so a key added to the\n // resolved bag later reaches this input without anyone remembering to come\n // back here.\n const aria: Record<string, unknown> = { ...field.attrs };\n delete aria.name;\n return aria;\n});\n\n// `optional()` drops the absent bounds and an absent name rather than\n// forwarding `undefined`, which Reka would read as \"a bound that is undefined\"\n// rather than \"no bound\" — and, for the name, would have it render a hidden\n// form input for a field nothing is submitting.\nconst rootProps = computed(() =>\n optional({\n modelValue: lastValue.value,\n min: props.min,\n max: props.max,\n name: field.name,\n }),\n);\n\nfunction clampValue(value: number): number {\n const step = props.step || 1;\n const stepped = Math.round(value / step) * step;\n const withMin = props.min !== undefined ? Math.max(props.min, stepped) : stepped;\n return props.max !== undefined ? Math.min(props.max, withMin) : withMin;\n}\n\n// The single source of truth for \"the last value we know about\", updated the\n// moment it changes — never re-derived from `props.modelValue` at commit time,\n// because that prop only reflects a new value after the host's v-model\n// round-trip re-renders us, which is a tick later than the blur or keydown\n// being handled.\n//\n// It is also what the Reka root binds as its own `model-value`, rather than\n// `props.modelValue`. Given a defined model value Reka runs fully controlled:\n// its increment reads back through that prop, so a host that correctly\n// withholds transient echoes until commit would freeze every arrow tick at\n// start + 1 and freeze the readout at the gesture's start value. Feeding it\n// `lastValue` keeps the ticks accumulating and the readout live inside a\n// gesture, while the host still sees nothing but transient emits until the\n// boundary.\nconst lastValue = ref(props.modelValue);\n\n// The value the host last acknowledged. `commit` only fires when `lastValue`\n// differs from it, so overlapping boundaries (Enter then blur, drag release\n// then blur) and no-edit focus passes (Tab in, Tab out) produce no extra\n// checkpoints.\nconst lastCommittedValue = ref(props.modelValue);\n\n// True while an uncommitted edit is in flight. An external prop change\n// re-baselines `lastCommittedValue` only when nothing is pending — otherwise\n// the host echoing our own transient emits back down would make the pending\n// edit look already committed and swallow its `commit`.\nlet editPending = false;\n\nwatch(\n () => props.modelValue,\n (value) => {\n lastValue.value = value;\n if (!editPending) lastCommittedValue.value = value;\n },\n);\n\nfunction onUpdate(value: number | undefined) {\n // Reka clears its model to `undefined` when the text in the field does not\n // parse — an emptied field, most reachably. The contract here is a number,\n // so the field falls back to its own floor instead of forwarding a hole,\n // which is also what Reka itself does when a stepper is pressed on\n // unparseable text.\n const next = value ?? props.min ?? 0;\n editPending = true;\n lastValue.value = next;\n emit(\"update:modelValue\", next);\n}\n\nfunction commitLastValue() {\n // The boundary ends the edit session even when the value ended up back where\n // it started — a drag that returned to its origin still ends the gesture.\n editPending = false;\n if (lastValue.value === lastCommittedValue.value) return;\n lastCommittedValue.value = lastValue.value;\n emit(\"commit\", lastValue.value ?? props.min ?? 0);\n}\n\n// Shift = ×10. Reka's own arrow handling is bound on the input and ignores\n// `shiftKey`, so this intercepts in the capture phase — strictly before the\n// event reaches the input — and fully replaces the default ×1 tick for this\n// one case. Letting both run would double-step.\nfunction onKeydownCapture(event: KeyboardEvent) {\n // Read-only closes this path as firmly as disabled does. Reka's own arrow\n // handling already bails on `readonly`, and this override replaces it — so\n // without the guard Shift+Arrow would be the one way to edit a field that\n // says it cannot be edited.\n if (field.disabled || field.readonly) return;\n if (!event.shiftKey || (event.key !== \"ArrowUp\" && event.key !== \"ArrowDown\")) return;\n event.preventDefault();\n event.stopPropagation();\n const tick = (props.step || 1) * 10;\n const current = lastValue.value ?? props.min ?? 0;\n onUpdate(clampValue(event.key === \"ArrowUp\" ? current + tick : current - tick));\n}\n\n// Bubble phase, not capture: by the time this fires, the input's own Enter\n// handling has already applied the typed text, because bubbling visits the\n// target before its ancestors.\nfunction onKeydownCommit(event: KeyboardEvent) {\n if (event.key !== \"Enter\") return;\n commitLastValue();\n}\n\n// A held arrow key is one gesture, exactly like a pointer drag: every keydown,\n// including the operating system's auto-repeats, only ticks the transient\n// value, and the keyup ends the gesture and commits the summed delta once. A\n// single tap is a keydown plus a keyup, so it still commits once.\nfunction onKeyupCommit(event: KeyboardEvent) {\n if (event.key !== \"ArrowUp\" && event.key !== \"ArrowDown\") return;\n commitLastValue();\n}\n\n// `focusout` bubbles where `blur` does not, so this reliably runs after the\n// input's own blur handling — but it only commits once focus has actually left\n// the field, not when it shifts internally to the hover-revealed stepper.\nfunction onFocusOut(event: FocusEvent) {\n const root = event.currentTarget as HTMLElement;\n if (event.relatedTarget instanceof Node && root.contains(event.relatedTarget)) return;\n commitLastValue();\n}\n\n// Horizontal scrub. A plain click must still place the caret for typing, so\n// the gesture does not take over until the pointer has moved past a small\n// threshold.\nconst DRAG_THRESHOLD_PX = 3;\nconst PIXELS_PER_STEP = 4;\n\n// Set while a drag's window listeners are attached, so unmounting mid-drag\n// detaches them instead of leaking handlers onto `window`.\nlet removeDragListeners: (() => void) | undefined;\nonUnmounted(() => removeDragListeners?.());\n\nfunction onPointerDown(event: PointerEvent) {\n // A read-only field still places the caret and still selects text, which is\n // the whole point of it — it simply never starts a scrub.\n if (field.disabled || field.readonly || event.button !== 0) return;\n const startX = event.clientX;\n const startValue = lastValue.value ?? props.min ?? 0;\n const step = props.step || 1;\n let dragging = false;\n\n function onPointerMove(moveEvent: PointerEvent) {\n const deltaX = moveEvent.clientX - startX;\n if (!dragging) {\n if (Math.abs(deltaX) < DRAG_THRESHOLD_PX) return;\n dragging = true;\n window.getSelection()?.removeAllRanges();\n }\n moveEvent.preventDefault();\n const deltaSteps = Math.round(deltaX / PIXELS_PER_STEP);\n const next = clampValue(startValue + deltaSteps * step);\n if (next !== lastValue.value) onUpdate(next);\n }\n\n function onPointerUp() {\n removeDragListeners?.();\n if (dragging) commitLastValue();\n }\n\n // Bail-out (Escape mid-scrub, or a browser-issued pointercancel): the\n // gesture is discarded. Nothing commits, and one last transient update\n // carrying the start value lets the host restore the pre-drag preview.\n function abortDrag() {\n removeDragListeners?.();\n if (!dragging) return;\n if (lastValue.value !== startValue) onUpdate(startValue);\n editPending = false;\n }\n\n function onDragKeydown(keyEvent: KeyboardEvent) {\n if (keyEvent.key !== \"Escape\" || !dragging) return;\n keyEvent.preventDefault();\n keyEvent.stopPropagation();\n abortDrag();\n }\n\n window.addEventListener(\"pointermove\", onPointerMove);\n window.addEventListener(\"pointerup\", onPointerUp);\n window.addEventListener(\"pointercancel\", abortDrag);\n window.addEventListener(\"keydown\", onDragKeydown, true);\n removeDragListeners = () => {\n window.removeEventListener(\"pointermove\", onPointerMove);\n window.removeEventListener(\"pointerup\", onPointerUp);\n window.removeEventListener(\"pointercancel\", abortDrag);\n window.removeEventListener(\"keydown\", onDragKeydown, true);\n removeDragListeners = undefined;\n };\n}\n</script>\n\n<template>\n <NumberFieldRoot\n ref=\"root\"\n v-bind=\"rootProps\"\n :step=\"step\"\n :disabled=\"field.disabled\"\n :readonly=\"field.readonly\"\n :aria-disabled=\"field.disabled || undefined\"\n :data-invalid=\"field.invalid || undefined\"\n :class=\"\n cn(\n 'group relative inline-flex h-9 w-full items-center rounded-md border border-input bg-background',\n 'transition-[color,background-color,box-shadow] duration-fast ease-out',\n // Rim-lit at rest; the ring blooms on focus.\n 'focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-ring',\n !field.invalid && 'focus-within:shadow-halo',\n // Three resting appearances, three channels — the same treatment\n // TextField carries at length. Read-only and disabled used to share one\n // fill and part only on the text colour, which is a distinction made in\n // hue alone; the fill and the border weight now part them twice more.\n //\n // Read-only keeps its focus ring and its full-strength number: a\n // read-only value is on show, not an unavailable control. The fill lifts\n // to `subtle`, the rim stays `input` because the field's reach has not\n // changed, and Reka's native `readonly` on the input carries the state\n // to assistive tech, so nothing here rests on colour.\n field.readonly && 'bg-subtle',\n // Unavailable is a *colour* and a *weight*, never an opacity.\n // `opacity-50` on this box faded the value inside it:\n // `--color-foreground` measures 14.09:1 on the resting fill and 2.99:1\n // once composited at half alpha, and the unit suffix beside it — muted,\n // and smaller — fell to 2.02:1. The fill drains a step past read-only\n // and the rim slackens to `border`; the input below takes the matching\n // text colour, which has to be declared there rather than inherited from\n // here because the input names `text-foreground` itself.\n field.disabled && 'border-border bg-muted',\n // Last of the rules that name a border colour: `cn()` resolves one by\n // whichever class it saw last, so a field that is both in error and\n // unavailable would otherwise lose its destructive rim to the disabled\n // one.\n field.invalid && 'border-destructive focus-within:outline-destructive',\n 'data-[disabled]:cursor-not-allowed',\n attrs.class as string,\n )\n \"\n @update:model-value=\"onUpdate\"\n @keydown.capture=\"onKeydownCapture\"\n @keydown=\"onKeydownCommit\"\n @keyup=\"onKeyupCommit\"\n @focusout=\"onFocusOut\"\n @pointerdown=\"onPointerDown\"\n >\n <!-- `aria-roledescription` is written before the `v-bind` rather than after\n it, which is the opposite of the field bag below and deliberate: it is\n replacing Reka's own literal, not a caller's, so a caller who passes\n the attribute directly should still beat it. Reka's is merged inside\n its render function and Vue applies everything from here afterwards,\n so both orders defeat Reka and only this one leaves the caller a way\n past Loom. -->\n <NumberFieldInput\n :aria-roledescription=\"text.roleDescription\"\n v-bind=\"{ ...inputAttrs, ...inputFieldAttrs }\"\n :class=\"\n cn(\n 'tabular h-full w-full flex-1 rounded-md bg-transparent px-3 text-sm text-foreground outline-none',\n unit ? 'pr-9' : 'pr-3',\n // 4.68:1 against the drained fill the root takes, and two steps back\n // from the black an editable value keeps — the state stays plain to\n // see and the number stays plain to read. `cn()` is what makes the\n // pair work: it drops the `text-foreground` above rather than leaving\n // two colours for the stylesheet to order.\n field.disabled && 'cursor-not-allowed text-muted-foreground',\n // The scrub cursor is a promise the field makes about the gesture it\n // takes. Read-only takes no gesture, so it must not make the promise\n // — the caret cursor is the honest one for a value you may select\n // and copy but not change.\n !field.disabled && !field.readonly && 'cursor-ew-resize',\n )\n \"\n />\n <!-- The unit is presentation, so it is hidden from assistive technology\n and it clears out on hover to make room for the stepper. -->\n <span\n v-if=\"unit\"\n aria-hidden=\"true\"\n class=\"pointer-events-none absolute right-2 text-xs text-muted-foreground transition-opacity duration-fast group-hover:opacity-0\"\n >\n {{ unit }}\n </span>\n <!-- The stepper is hidden until hover: a column of these is read as a\n column of numbers, and a permanent pair of chevrons on every row turns\n that into a column of controls.\n\n It is not rendered at all on a read-only field. Reka's own\n `handleChangingValue` bails on `readonly` *after* focusing the input,\n so a rendered stepper would be two named buttons that a pointer or a\n screen reader can still reach and press, and that answer by moving\n focus and changing nothing. Absent is the honest state. -->\n <div\n v-if=\"!field.readonly\"\n class=\"absolute right-1 flex flex-col opacity-0 transition-opacity duration-fast group-hover:opacity-100\"\n >\n <!-- Both `:aria-label`s replace one Reka writes in English inside its own\n render function — \"Increase\" and \"Decrease\". They reach the DOM node\n as fallthrough attributes, which Vue merges last, so each is a\n replacement rather than a second name. Removing one does not fall\n back to nothing; it falls back to Reka's English.\n\n The two keep `data-[disabled]:opacity-50` where the box around them\n gave it up, and the difference is that each holds a chevron and\n nothing else. Fading a glyph costs a reader nothing; their names are\n in `aria-label`, not in dimmed text. -->\n <NumberFieldIncrement\n :aria-label=\"text.increment\"\n class=\"flex h-4 w-4 items-center justify-center rounded-sm text-muted-foreground [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out)] hover:bg-subtle hover:text-foreground active:scale-press focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50\"\n >\n <!-- Stroke lives on the 24 grid and scales with size, so the inherited\n 1.5 would render 0.75 device pixels at this 12px box and all but\n disappear. 2.5 restores the optical weight. -->\n <ChevronUp class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </NumberFieldIncrement>\n <NumberFieldDecrement\n :aria-label=\"text.decrement\"\n class=\"flex h-4 w-4 items-center justify-center rounded-sm text-muted-foreground [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out)] hover:bg-subtle hover:text-foreground active:scale-press focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50\"\n >\n <ChevronDown class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </NumberFieldDecrement>\n </div>\n </NumberFieldRoot>\n</template>\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import OtpInput_vue_vue_type_script_setup_true_lang_default, { OTP_INPUT_LABELS } from "./OtpInput.vue_vue_type_script_setup_true_lang.js";
2
+ //#region src/primitives/OtpInput/OtpInput.vue
3
+ var OtpInput_default = OtpInput_vue_vue_type_script_setup_true_lang_default;
4
+ //#endregion
5
+ export { OTP_INPUT_LABELS, OtpInput_default as default };
6
+
7
+ //# sourceMappingURL=OtpInput.js.map