@ecoma-io/loom 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +180 -0
  3. package/dist/a11y.d.ts +2 -0
  4. package/dist/a11y.d.ts.map +1 -0
  5. package/dist/a11y.js +2 -0
  6. package/dist/blocks/AppHeader/AppHeader.js +7 -0
  7. package/dist/blocks/AppHeader/AppHeader.js.map +1 -0
  8. package/dist/blocks/AppHeader/AppHeader.vue.d.ts +83 -0
  9. package/dist/blocks/AppHeader/AppHeader.vue.d.ts.map +1 -0
  10. package/dist/blocks/AppHeader/AppHeader.vue_vue_type_script_setup_true_lang.js +96 -0
  11. package/dist/blocks/AppHeader/AppHeader.vue_vue_type_script_setup_true_lang.js.map +1 -0
  12. package/dist/blocks/DashboardGrid/DashboardGrid.js +7 -0
  13. package/dist/blocks/DashboardGrid/DashboardGrid.js.map +1 -0
  14. package/dist/blocks/DashboardGrid/DashboardGrid.vue.d.ts +42 -0
  15. package/dist/blocks/DashboardGrid/DashboardGrid.vue.d.ts.map +1 -0
  16. package/dist/blocks/DashboardGrid/DashboardGrid.vue_vue_type_script_setup_true_lang.js +45 -0
  17. package/dist/blocks/DashboardGrid/DashboardGrid.vue_vue_type_script_setup_true_lang.js.map +1 -0
  18. package/dist/blocks/EmptyState/EmptyState.js +7 -0
  19. package/dist/blocks/EmptyState/EmptyState.js.map +1 -0
  20. package/dist/blocks/EmptyState/EmptyState.vue.d.ts +50 -0
  21. package/dist/blocks/EmptyState/EmptyState.vue.d.ts.map +1 -0
  22. package/dist/blocks/EmptyState/EmptyState.vue_vue_type_script_setup_true_lang.js +71 -0
  23. package/dist/blocks/EmptyState/EmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -0
  24. package/dist/blocks/PageHeader/PageHeader.js +7 -0
  25. package/dist/blocks/PageHeader/PageHeader.js.map +1 -0
  26. package/dist/blocks/PageHeader/PageHeader.vue.d.ts +54 -0
  27. package/dist/blocks/PageHeader/PageHeader.vue.d.ts.map +1 -0
  28. package/dist/blocks/PageHeader/PageHeader.vue_vue_type_script_setup_true_lang.js +68 -0
  29. package/dist/blocks/PageHeader/PageHeader.vue_vue_type_script_setup_true_lang.js.map +1 -0
  30. package/dist/blocks/RowActions/RowActions.js +7 -0
  31. package/dist/blocks/RowActions/RowActions.js.map +1 -0
  32. package/dist/blocks/RowActions/RowActions.vue.d.ts +14 -0
  33. package/dist/blocks/RowActions/RowActions.vue.d.ts.map +1 -0
  34. package/dist/blocks/RowActions/RowActions.vue_vue_type_script_setup_true_lang.js +47 -0
  35. package/dist/blocks/RowActions/RowActions.vue_vue_type_script_setup_true_lang.js.map +1 -0
  36. package/dist/blocks/SidebarNav/SidebarNav.js +7 -0
  37. package/dist/blocks/SidebarNav/SidebarNav.js.map +1 -0
  38. package/dist/blocks/SidebarNav/SidebarNav.vue.d.ts +40 -0
  39. package/dist/blocks/SidebarNav/SidebarNav.vue.d.ts.map +1 -0
  40. package/dist/blocks/SidebarNav/SidebarNav.vue_vue_type_script_setup_true_lang.js +125 -0
  41. package/dist/blocks/SidebarNav/SidebarNav.vue_vue_type_script_setup_true_lang.js.map +1 -0
  42. package/dist/blocks/TitleBar/TitleBar.js +7 -0
  43. package/dist/blocks/TitleBar/TitleBar.js.map +1 -0
  44. package/dist/blocks/TitleBar/TitleBar.vue.d.ts +49 -0
  45. package/dist/blocks/TitleBar/TitleBar.vue.d.ts.map +1 -0
  46. package/dist/blocks/TitleBar/TitleBar.vue_vue_type_script_setup_true_lang.js +66 -0
  47. package/dist/blocks/TitleBar/TitleBar.vue_vue_type_script_setup_true_lang.js.map +1 -0
  48. package/dist/blocks/ToastStack/ToastStack.js +7 -0
  49. package/dist/blocks/ToastStack/ToastStack.js.map +1 -0
  50. package/dist/blocks/ToastStack/ToastStack.vue.d.ts +24 -0
  51. package/dist/blocks/ToastStack/ToastStack.vue.d.ts.map +1 -0
  52. package/dist/blocks/ToastStack/ToastStack.vue_vue_type_script_setup_true_lang.js +38 -0
  53. package/dist/blocks/ToastStack/ToastStack.vue_vue_type_script_setup_true_lang.js.map +1 -0
  54. package/dist/icons/BrandMark.d.ts +18 -0
  55. package/dist/icons/BrandMark.d.ts.map +1 -0
  56. package/dist/icons/BrandMark.js +47 -0
  57. package/dist/icons/BrandMark.js.map +1 -0
  58. package/dist/index.d.ts +75 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +46 -0
  61. package/dist/lib/a11y-scope.d.ts +19 -0
  62. package/dist/lib/a11y-scope.d.ts.map +1 -0
  63. package/dist/lib/a11y-scope.js +28 -0
  64. package/dist/lib/a11y-scope.js.map +1 -0
  65. package/dist/lib/attrs.d.ts +18 -0
  66. package/dist/lib/attrs.d.ts.map +1 -0
  67. package/dist/lib/attrs.js +28 -0
  68. package/dist/lib/attrs.js.map +1 -0
  69. package/dist/lib/cn.d.ts +9 -0
  70. package/dist/lib/cn.d.ts.map +1 -0
  71. package/dist/lib/cn.js +16 -0
  72. package/dist/lib/cn.js.map +1 -0
  73. package/dist/lib/icon-defaults.d.ts +22 -0
  74. package/dist/lib/icon-defaults.d.ts.map +1 -0
  75. package/dist/lib/icon-defaults.js +32 -0
  76. package/dist/lib/icon-defaults.js.map +1 -0
  77. package/dist/lib/motion.d.ts +14 -0
  78. package/dist/lib/motion.d.ts.map +1 -0
  79. package/dist/lib/motion.js +20 -0
  80. package/dist/lib/motion.js.map +1 -0
  81. package/dist/lib/props.d.ts +31 -0
  82. package/dist/lib/props.d.ts.map +1 -0
  83. package/dist/lib/props.js +37 -0
  84. package/dist/lib/props.js.map +1 -0
  85. package/dist/primitives/Avatar/Avatar.js +7 -0
  86. package/dist/primitives/Avatar/Avatar.js.map +1 -0
  87. package/dist/primitives/Avatar/Avatar.vue.d.ts +27 -0
  88. package/dist/primitives/Avatar/Avatar.vue.d.ts.map +1 -0
  89. package/dist/primitives/Avatar/Avatar.vue_vue_type_script_setup_true_lang.js +33 -0
  90. package/dist/primitives/Avatar/Avatar.vue_vue_type_script_setup_true_lang.js.map +1 -0
  91. package/dist/primitives/Badge/Badge.js +7 -0
  92. package/dist/primitives/Badge/Badge.js.map +1 -0
  93. package/dist/primitives/Badge/Badge.vue.d.ts +27 -0
  94. package/dist/primitives/Badge/Badge.vue.d.ts.map +1 -0
  95. package/dist/primitives/Badge/Badge.vue_vue_type_script_setup_true_lang.js +30 -0
  96. package/dist/primitives/Badge/Badge.vue_vue_type_script_setup_true_lang.js.map +1 -0
  97. package/dist/primitives/Button/Button.js +7 -0
  98. package/dist/primitives/Button/Button.js.map +1 -0
  99. package/dist/primitives/Button/Button.vue.d.ts +70 -0
  100. package/dist/primitives/Button/Button.vue.d.ts.map +1 -0
  101. package/dist/primitives/Button/Button.vue_vue_type_script_setup_true_lang.js +122 -0
  102. package/dist/primitives/Button/Button.vue_vue_type_script_setup_true_lang.js.map +1 -0
  103. package/dist/primitives/Checkbox/Checkbox.js +7 -0
  104. package/dist/primitives/Checkbox/Checkbox.js.map +1 -0
  105. package/dist/primitives/Checkbox/Checkbox.vue.d.ts +34 -0
  106. package/dist/primitives/Checkbox/Checkbox.vue.d.ts.map +1 -0
  107. package/dist/primitives/Checkbox/Checkbox.vue_vue_type_script_setup_true_lang.js +94 -0
  108. package/dist/primitives/Checkbox/Checkbox.vue_vue_type_script_setup_true_lang.js.map +1 -0
  109. package/dist/primitives/Dialog/Dialog.js +7 -0
  110. package/dist/primitives/Dialog/Dialog.js.map +1 -0
  111. package/dist/primitives/Dialog/Dialog.vue.d.ts +58 -0
  112. package/dist/primitives/Dialog/Dialog.vue.d.ts.map +1 -0
  113. package/dist/primitives/Dialog/Dialog.vue_vue_type_script_setup_true_lang.js +88 -0
  114. package/dist/primitives/Dialog/Dialog.vue_vue_type_script_setup_true_lang.js.map +1 -0
  115. package/dist/primitives/DropdownMenu/DropdownMenu.js +7 -0
  116. package/dist/primitives/DropdownMenu/DropdownMenu.js.map +1 -0
  117. package/dist/primitives/DropdownMenu/DropdownMenu.vue.d.ts +55 -0
  118. package/dist/primitives/DropdownMenu/DropdownMenu.vue.d.ts.map +1 -0
  119. package/dist/primitives/DropdownMenu/DropdownMenu.vue_vue_type_script_setup_true_lang.js +82 -0
  120. package/dist/primitives/DropdownMenu/DropdownMenu.vue_vue_type_script_setup_true_lang.js.map +1 -0
  121. package/dist/primitives/Field/Field.js +7 -0
  122. package/dist/primitives/Field/Field.js.map +1 -0
  123. package/dist/primitives/Field/Field.vue.d.ts +53 -0
  124. package/dist/primitives/Field/Field.vue.d.ts.map +1 -0
  125. package/dist/primitives/Field/Field.vue_vue_type_script_setup_true_lang.js +84 -0
  126. package/dist/primitives/Field/Field.vue_vue_type_script_setup_true_lang.js.map +1 -0
  127. package/dist/primitives/InlineError/InlineError.js +7 -0
  128. package/dist/primitives/InlineError/InlineError.js.map +1 -0
  129. package/dist/primitives/InlineError/InlineError.vue.d.ts +32 -0
  130. package/dist/primitives/InlineError/InlineError.vue.d.ts.map +1 -0
  131. package/dist/primitives/InlineError/InlineError.vue_vue_type_script_setup_true_lang.js +44 -0
  132. package/dist/primitives/InlineError/InlineError.vue_vue_type_script_setup_true_lang.js.map +1 -0
  133. package/dist/primitives/Menubar/Menubar.js +7 -0
  134. package/dist/primitives/Menubar/Menubar.js.map +1 -0
  135. package/dist/primitives/Menubar/Menubar.vue.d.ts +41 -0
  136. package/dist/primitives/Menubar/Menubar.vue.d.ts.map +1 -0
  137. package/dist/primitives/Menubar/Menubar.vue_vue_type_script_setup_true_lang.js +184 -0
  138. package/dist/primitives/Menubar/Menubar.vue_vue_type_script_setup_true_lang.js.map +1 -0
  139. package/dist/primitives/NumberField/NumberField.js +7 -0
  140. package/dist/primitives/NumberField/NumberField.js.map +1 -0
  141. package/dist/primitives/NumberField/NumberField.vue.d.ts +47 -0
  142. package/dist/primitives/NumberField/NumberField.vue.d.ts.map +1 -0
  143. package/dist/primitives/NumberField/NumberField.vue_vue_type_script_setup_true_lang.js +204 -0
  144. package/dist/primitives/NumberField/NumberField.vue_vue_type_script_setup_true_lang.js.map +1 -0
  145. package/dist/primitives/Popover/Popover.js +7 -0
  146. package/dist/primitives/Popover/Popover.js.map +1 -0
  147. package/dist/primitives/Popover/Popover.vue.d.ts +61 -0
  148. package/dist/primitives/Popover/Popover.vue.d.ts.map +1 -0
  149. package/dist/primitives/Popover/Popover.vue_vue_type_script_setup_true_lang.js +59 -0
  150. package/dist/primitives/Popover/Popover.vue_vue_type_script_setup_true_lang.js.map +1 -0
  151. package/dist/primitives/Progress/Progress.js +7 -0
  152. package/dist/primitives/Progress/Progress.js.map +1 -0
  153. package/dist/primitives/Progress/Progress.vue.d.ts +30 -0
  154. package/dist/primitives/Progress/Progress.vue.d.ts.map +1 -0
  155. package/dist/primitives/Progress/Progress.vue_vue_type_script_setup_true_lang.js +67 -0
  156. package/dist/primitives/Progress/Progress.vue_vue_type_script_setup_true_lang.js.map +1 -0
  157. package/dist/primitives/RadioGroup/RadioGroup.js +7 -0
  158. package/dist/primitives/RadioGroup/RadioGroup.js.map +1 -0
  159. package/dist/primitives/RadioGroup/RadioGroup.vue.d.ts +39 -0
  160. package/dist/primitives/RadioGroup/RadioGroup.vue.d.ts.map +1 -0
  161. package/dist/primitives/RadioGroup/RadioGroup.vue_vue_type_script_setup_true_lang.js +63 -0
  162. package/dist/primitives/RadioGroup/RadioGroup.vue_vue_type_script_setup_true_lang.js.map +1 -0
  163. package/dist/primitives/SegmentedControl/SegmentedControl.js +7 -0
  164. package/dist/primitives/SegmentedControl/SegmentedControl.js.map +1 -0
  165. package/dist/primitives/SegmentedControl/SegmentedControl.vue.d.ts +40 -0
  166. package/dist/primitives/SegmentedControl/SegmentedControl.vue.d.ts.map +1 -0
  167. package/dist/primitives/SegmentedControl/SegmentedControl.vue_vue_type_script_setup_true_lang.js +89 -0
  168. package/dist/primitives/SegmentedControl/SegmentedControl.vue_vue_type_script_setup_true_lang.js.map +1 -0
  169. package/dist/primitives/Select/Select.js +7 -0
  170. package/dist/primitives/Select/Select.js.map +1 -0
  171. package/dist/primitives/Select/Select.vue.d.ts +56 -0
  172. package/dist/primitives/Select/Select.vue.d.ts.map +1 -0
  173. package/dist/primitives/Select/Select.vue_vue_type_script_setup_true_lang.js +122 -0
  174. package/dist/primitives/Select/Select.vue_vue_type_script_setup_true_lang.js.map +1 -0
  175. package/dist/primitives/Separator/Separator.js +7 -0
  176. package/dist/primitives/Separator/Separator.js.map +1 -0
  177. package/dist/primitives/Separator/Separator.vue.d.ts +19 -0
  178. package/dist/primitives/Separator/Separator.vue.d.ts.map +1 -0
  179. package/dist/primitives/Separator/Separator.vue_vue_type_script_setup_true_lang.js +31 -0
  180. package/dist/primitives/Separator/Separator.vue_vue_type_script_setup_true_lang.js.map +1 -0
  181. package/dist/primitives/Skeleton/Skeleton.js +7 -0
  182. package/dist/primitives/Skeleton/Skeleton.js.map +1 -0
  183. package/dist/primitives/Skeleton/Skeleton.vue.d.ts +16 -0
  184. package/dist/primitives/Skeleton/Skeleton.vue.d.ts.map +1 -0
  185. package/dist/primitives/Skeleton/Skeleton.vue_vue_type_script_setup_true_lang.js +37 -0
  186. package/dist/primitives/Skeleton/Skeleton.vue_vue_type_script_setup_true_lang.js.map +1 -0
  187. package/dist/primitives/Slider/Slider.js +7 -0
  188. package/dist/primitives/Slider/Slider.js.map +1 -0
  189. package/dist/primitives/Slider/Slider.vue.d.ts +58 -0
  190. package/dist/primitives/Slider/Slider.vue.d.ts.map +1 -0
  191. package/dist/primitives/Slider/Slider.vue_vue_type_script_setup_true_lang.js +137 -0
  192. package/dist/primitives/Slider/Slider.vue_vue_type_script_setup_true_lang.js.map +1 -0
  193. package/dist/primitives/Spinner/Spinner.js +7 -0
  194. package/dist/primitives/Spinner/Spinner.js.map +1 -0
  195. package/dist/primitives/Spinner/Spinner.vue.d.ts +21 -0
  196. package/dist/primitives/Spinner/Spinner.vue.d.ts.map +1 -0
  197. package/dist/primitives/Spinner/Spinner.vue_vue_type_script_setup_true_lang.js +63 -0
  198. package/dist/primitives/Spinner/Spinner.vue_vue_type_script_setup_true_lang.js.map +1 -0
  199. package/dist/primitives/Surface/Surface.js +7 -0
  200. package/dist/primitives/Surface/Surface.js.map +1 -0
  201. package/dist/primitives/Surface/Surface.vue.d.ts +42 -0
  202. package/dist/primitives/Surface/Surface.vue.d.ts.map +1 -0
  203. package/dist/primitives/Surface/Surface.vue_vue_type_script_setup_true_lang.js +48 -0
  204. package/dist/primitives/Surface/Surface.vue_vue_type_script_setup_true_lang.js.map +1 -0
  205. package/dist/primitives/Switch/Switch.js +7 -0
  206. package/dist/primitives/Switch/Switch.js.map +1 -0
  207. package/dist/primitives/Switch/Switch.vue.d.ts +24 -0
  208. package/dist/primitives/Switch/Switch.vue.d.ts.map +1 -0
  209. package/dist/primitives/Switch/Switch.vue_vue_type_script_setup_true_lang.js +44 -0
  210. package/dist/primitives/Switch/Switch.vue_vue_type_script_setup_true_lang.js.map +1 -0
  211. package/dist/primitives/Tabs/Tabs.js +7 -0
  212. package/dist/primitives/Tabs/Tabs.js.map +1 -0
  213. package/dist/primitives/Tabs/Tabs.vue.d.ts +33 -0
  214. package/dist/primitives/Tabs/Tabs.vue.d.ts.map +1 -0
  215. package/dist/primitives/Tabs/Tabs.vue_vue_type_script_setup_true_lang.js +54 -0
  216. package/dist/primitives/Tabs/Tabs.vue_vue_type_script_setup_true_lang.js.map +1 -0
  217. package/dist/primitives/TextField/TextField.js +7 -0
  218. package/dist/primitives/TextField/TextField.js.map +1 -0
  219. package/dist/primitives/TextField/TextField.vue.d.ts +58 -0
  220. package/dist/primitives/TextField/TextField.vue.d.ts.map +1 -0
  221. package/dist/primitives/TextField/TextField.vue_vue_type_script_setup_true_lang.js +71 -0
  222. package/dist/primitives/TextField/TextField.vue_vue_type_script_setup_true_lang.js.map +1 -0
  223. package/dist/primitives/Textarea/Textarea.js +7 -0
  224. package/dist/primitives/Textarea/Textarea.js.map +1 -0
  225. package/dist/primitives/Textarea/Textarea.vue.d.ts +48 -0
  226. package/dist/primitives/Textarea/Textarea.vue.d.ts.map +1 -0
  227. package/dist/primitives/Textarea/Textarea.vue_vue_type_script_setup_true_lang.js +54 -0
  228. package/dist/primitives/Textarea/Textarea.vue_vue_type_script_setup_true_lang.js.map +1 -0
  229. package/dist/primitives/Toast/Toast.js +7 -0
  230. package/dist/primitives/Toast/Toast.js.map +1 -0
  231. package/dist/primitives/Toast/Toast.vue.d.ts +46 -0
  232. package/dist/primitives/Toast/Toast.vue.d.ts.map +1 -0
  233. package/dist/primitives/Toast/Toast.vue_vue_type_script_setup_true_lang.js +52 -0
  234. package/dist/primitives/Toast/Toast.vue_vue_type_script_setup_true_lang.js.map +1 -0
  235. package/dist/primitives/Toast/ToastItem.js +7 -0
  236. package/dist/primitives/Toast/ToastItem.js.map +1 -0
  237. package/dist/primitives/Toast/ToastItem.vue.d.ts +38 -0
  238. package/dist/primitives/Toast/ToastItem.vue.d.ts.map +1 -0
  239. package/dist/primitives/Toast/ToastItem.vue_vue_type_script_setup_true_lang.js +114 -0
  240. package/dist/primitives/Toast/ToastItem.vue_vue_type_script_setup_true_lang.js.map +1 -0
  241. package/dist/primitives/Tooltip/Tooltip.js +7 -0
  242. package/dist/primitives/Tooltip/Tooltip.js.map +1 -0
  243. package/dist/primitives/Tooltip/Tooltip.vue.d.ts +52 -0
  244. package/dist/primitives/Tooltip/Tooltip.vue.d.ts.map +1 -0
  245. package/dist/primitives/Tooltip/Tooltip.vue_vue_type_script_setup_true_lang.js +56 -0
  246. package/dist/primitives/Tooltip/Tooltip.vue_vue_type_script_setup_true_lang.js.map +1 -0
  247. package/dist/primitives/WindowControls/WindowControls.js +7 -0
  248. package/dist/primitives/WindowControls/WindowControls.js.map +1 -0
  249. package/dist/primitives/WindowControls/WindowControls.vue.d.ts +41 -0
  250. package/dist/primitives/WindowControls/WindowControls.vue.d.ts.map +1 -0
  251. package/dist/primitives/WindowControls/WindowControls.vue_vue_type_script_setup_true_lang.js +111 -0
  252. package/dist/primitives/WindowControls/WindowControls.vue_vue_type_script_setup_true_lang.js.map +1 -0
  253. package/dist/styles/fonts/Geist-Variable.woff2 +0 -0
  254. package/dist/styles/fonts/GeistMono-Variable.woff2 +0 -0
  255. package/dist/styles/fonts/OFL.txt +92 -0
  256. package/dist/styles/fonts.css +30 -0
  257. package/dist/styles/global.css +181 -0
  258. package/dist/styles/theme.css +212 -0
  259. package/package.json +123 -0
@@ -0,0 +1,184 @@
1
+ import { listStaggerDelay } from "../../lib/motion.js";
2
+ import { Fragment, createCommentVNode, createElementBlock, createElementVNode, defineComponent, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, renderList, toDisplayString, unref, withModifiers } from "vue";
3
+ //#region src/primitives/Menubar/Menubar.vue?vue&type=script&setup=true&lang.ts
4
+ var _hoisted_1 = [
5
+ "id",
6
+ "aria-expanded",
7
+ "onClick",
8
+ "onMouseenter",
9
+ "onFocus",
10
+ "onKeydown"
11
+ ];
12
+ var _hoisted_2 = ["aria-label", "onKeydown"];
13
+ var _hoisted_3 = {
14
+ key: 0,
15
+ role: "separator",
16
+ class: "my-1 h-px bg-border"
17
+ };
18
+ var _hoisted_4 = [
19
+ "disabled",
20
+ "data-highlighted",
21
+ "onClick",
22
+ "onMouseenter",
23
+ "onFocus"
24
+ ];
25
+ var _hoisted_5 = {
26
+ key: 0,
27
+ class: "tabular text-[0.6875rem] text-muted-foreground"
28
+ };
29
+ var Menubar_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
30
+ __name: "Menubar",
31
+ props: { menus: {} },
32
+ emits: ["select"],
33
+ setup(__props, { emit: __emit }) {
34
+ const props = __props;
35
+ const emit = __emit;
36
+ const openId = ref(null);
37
+ const activeIndex = ref(-1);
38
+ const openMenu = (menu) => menu?.items ?? [];
39
+ const firstEnabled = (items, from = 0, dir = 1) => {
40
+ for (let i = from; i >= 0 && i < items.length; i += dir) {
41
+ const item = items[i];
42
+ if (item && !item.separator && !item.disabled) return i;
43
+ }
44
+ return -1;
45
+ };
46
+ function toggle(id) {
47
+ openId.value = openId.value === id ? null : id;
48
+ activeIndex.value = -1;
49
+ }
50
+ function hover(id) {
51
+ if (openId.value !== null && openId.value !== id) {
52
+ openId.value = id;
53
+ activeIndex.value = -1;
54
+ }
55
+ }
56
+ function choose(item) {
57
+ if (item.separator || item.disabled) return;
58
+ close();
59
+ if (item.command) emit("select", item.command);
60
+ }
61
+ function close() {
62
+ openId.value = null;
63
+ activeIndex.value = -1;
64
+ }
65
+ function moveMenu(dir) {
66
+ const ids = props.menus.map((m) => m.id);
67
+ const next = ((openId.value ? ids.indexOf(openId.value) : 0) + dir + ids.length) % ids.length;
68
+ openId.value = ids[next] ?? null;
69
+ activeIndex.value = -1;
70
+ }
71
+ async function onTriggerKeydown(e, menu) {
72
+ switch (e.key) {
73
+ case "ArrowDown":
74
+ case "Enter":
75
+ case " ":
76
+ e.preventDefault();
77
+ openId.value = menu.id;
78
+ await nextTick();
79
+ activeIndex.value = firstEnabled(menu.items);
80
+ break;
81
+ case "ArrowRight":
82
+ e.preventDefault();
83
+ if (openId.value) moveMenu(1);
84
+ break;
85
+ case "ArrowLeft":
86
+ e.preventDefault();
87
+ if (openId.value) moveMenu(-1);
88
+ break;
89
+ case "Escape": close();
90
+ }
91
+ }
92
+ function onMenuKeydown(e, menu) {
93
+ const items = menu.items;
94
+ switch (e.key) {
95
+ case "ArrowDown":
96
+ e.preventDefault();
97
+ activeIndex.value = firstEnabled(items, Math.max(activeIndex.value, -1) + 1, 1);
98
+ if (activeIndex.value === -1) activeIndex.value = firstEnabled(items, 0, 1);
99
+ break;
100
+ case "ArrowUp":
101
+ e.preventDefault();
102
+ activeIndex.value = activeIndex.value <= 0 ? firstEnabled(items, items.length - 1, -1) : firstEnabled(items, activeIndex.value - 1, -1);
103
+ break;
104
+ case "Home":
105
+ e.preventDefault();
106
+ activeIndex.value = firstEnabled(items, 0, 1);
107
+ break;
108
+ case "End":
109
+ e.preventDefault();
110
+ activeIndex.value = firstEnabled(items, items.length - 1, -1);
111
+ break;
112
+ case "ArrowRight":
113
+ e.preventDefault();
114
+ moveMenu(1);
115
+ break;
116
+ case "ArrowLeft":
117
+ e.preventDefault();
118
+ moveMenu(-1);
119
+ break;
120
+ case "Enter":
121
+ case " ": {
122
+ e.preventDefault();
123
+ const item = activeIndex.value >= 0 ? items[activeIndex.value] : void 0;
124
+ if (item) choose(item);
125
+ break;
126
+ }
127
+ case "Escape":
128
+ e.preventDefault();
129
+ close();
130
+ }
131
+ }
132
+ onMounted(() => window.addEventListener("click", close));
133
+ onBeforeUnmount(() => window.removeEventListener("click", close));
134
+ return (_ctx, _cache) => {
135
+ return openBlock(), createElementBlock("div", {
136
+ role: "menubar",
137
+ class: "flex items-center gap-0.5",
138
+ style: { "-webkit-app-region": "no-drag" },
139
+ onClick: _cache[1] || (_cache[1] = withModifiers(() => {}, ["stop"]))
140
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.menus, (menu) => {
141
+ return openBlock(), createElementBlock("div", {
142
+ key: menu.id,
143
+ class: "relative"
144
+ }, [createElementVNode("button", {
145
+ id: `menubar-trigger-${menu.id}`,
146
+ type: "button",
147
+ role: "menuitem",
148
+ "aria-haspopup": true,
149
+ "aria-expanded": openId.value === menu.id,
150
+ class: normalizeClass(["rounded-sm px-2 py-1 text-xs text-muted-foreground transition-colors duration-fast ease-out hover:bg-subtle hover:text-foreground focus-visible:bg-subtle focus-visible:text-foreground", openId.value === menu.id && "bg-subtle text-foreground"]),
151
+ onClick: withModifiers(($event) => toggle(menu.id), ["stop"]),
152
+ onMouseenter: ($event) => hover(menu.id),
153
+ onFocus: ($event) => hover(menu.id),
154
+ onKeydown: ($event) => onTriggerKeydown($event, menu)
155
+ }, toDisplayString(menu.label), 43, _hoisted_1), openId.value === menu.id ? (openBlock(), createElementBlock("div", {
156
+ key: 0,
157
+ role: "menu",
158
+ "aria-label": menu.label,
159
+ tabindex: "-1",
160
+ class: "absolute left-0 top-full z-50 mt-1 min-w-[13rem] origin-top-left rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md animate-scale-in",
161
+ onClick: _cache[0] || (_cache[0] = withModifiers(() => {}, ["stop"])),
162
+ onKeydown: ($event) => onMenuKeydown($event, menu)
163
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(openMenu(menu), (item, i) => {
164
+ return openBlock(), createElementBlock(Fragment, { key: i }, [item.separator ? (openBlock(), createElementBlock("div", _hoisted_3)) : (openBlock(), createElementBlock("button", {
165
+ key: 1,
166
+ type: "button",
167
+ role: "menuitem",
168
+ disabled: item.disabled,
169
+ "data-highlighted": activeIndex.value === i || void 0,
170
+ style: normalizeStyle({ animationDelay: unref(listStaggerDelay)(i) }),
171
+ class: "flex w-full items-center justify-between gap-6 rounded-sm px-2 py-1.5 text-left text-xs text-foreground transition-colors duration-fast ease-out hover:bg-subtle disabled:pointer-events-none disabled:opacity-40 data-[highlighted]:bg-subtle animate-fade-rise",
172
+ onClick: ($event) => choose(item),
173
+ onMouseenter: ($event) => activeIndex.value = i,
174
+ onFocus: ($event) => activeIndex.value = i
175
+ }, [createElementVNode("span", null, toDisplayString(item.label), 1), item.shortcut ? (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString(item.shortcut), 1)) : createCommentVNode("", true)], 44, _hoisted_4))], 64);
176
+ }), 128))], 40, _hoisted_2)) : createCommentVNode("", true)]);
177
+ }), 128))]);
178
+ };
179
+ }
180
+ });
181
+ //#endregion
182
+ export { Menubar_vue_vue_type_script_setup_true_lang_default as default };
183
+
184
+ //# sourceMappingURL=Menubar.vue_vue_type_script_setup_true_lang.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Menubar.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/primitives/Menubar/Menubar.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Menubar — the classic desktop-app menu strip (File · View · Help …).\n *\n * Data-driven: pass a `menus` array; the component owns open/close, hover-to-switch,\n * keyboard nav and click-outside. Selecting an item emits `select` with its `command`\n * id — the host maps that id to an action (keeps the primitive free of app logic).\n *\n * A11y: WAI-ARIA menubar semantics (roles + aria-expanded/haspopup) with Escape,\n * Arrow, Home/End and Enter/Space handling. When richer focus-trapping is needed we\n * can later delegate to Reka UI without changing this call-site contract.\n */\nexport interface MenubarItem {\n /** Visible label. */\n label: string;\n /** Command id emitted on select. Omit for a non-actionable row. */\n command?: string;\n /** Right-aligned accelerator hint, e.g. \"Ctrl+S\". */\n shortcut?: string;\n /** Render a divider instead of an item (label ignored). */\n separator?: boolean;\n disabled?: boolean;\n}\n\nexport interface MenubarMenu {\n id: string;\n label: string;\n items: MenubarItem[];\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { onMounted, onBeforeUnmount, ref, nextTick } from \"vue\";\nimport { listStaggerDelay } from \"../../lib/motion\";\n\nconst props = defineProps<{\n /** The top-level menus, left to right, each carrying its own item list. */\n menus: MenubarMenu[];\n}>();\nconst emit = defineEmits<{ (e: \"select\", command: string): void }>();\n\nconst openId = ref<string | null>(null);\nconst activeIndex = ref(-1); // highlighted item within the open menu\n\nconst openMenu = (menu: MenubarMenu | undefined) => menu?.items ?? [];\nconst firstEnabled = (items: MenubarItem[], from = 0, dir = 1): number => {\n for (let i = from; i >= 0 && i < items.length; i += dir) {\n const item = items[i];\n if (item && !item.separator && !item.disabled) return i;\n }\n return -1;\n};\n\nfunction toggle(id: string): void {\n openId.value = openId.value === id ? null : id;\n activeIndex.value = -1;\n}\nfunction hover(id: string): void {\n if (openId.value !== null && openId.value !== id) {\n openId.value = id;\n activeIndex.value = -1;\n }\n}\nfunction choose(item: MenubarItem): void {\n if (item.separator || item.disabled) return;\n close();\n if (item.command) emit(\"select\", item.command);\n}\nfunction close(): void {\n openId.value = null;\n activeIndex.value = -1;\n}\n\nfunction moveMenu(dir: 1 | -1): void {\n const ids = props.menus.map((m) => m.id);\n const cur = openId.value ? ids.indexOf(openId.value) : 0;\n const next = (cur + dir + ids.length) % ids.length;\n openId.value = ids[next] ?? null;\n activeIndex.value = -1;\n}\n\nasync function onTriggerKeydown(e: KeyboardEvent, menu: MenubarMenu): Promise<void> {\n switch (e.key) {\n case \"ArrowDown\":\n case \"Enter\":\n case \" \":\n e.preventDefault();\n openId.value = menu.id;\n await nextTick();\n activeIndex.value = firstEnabled(menu.items);\n break;\n case \"ArrowRight\":\n e.preventDefault();\n if (openId.value) moveMenu(1);\n break;\n case \"ArrowLeft\":\n e.preventDefault();\n if (openId.value) moveMenu(-1);\n break;\n case \"Escape\":\n close();\n break;\n }\n}\n\nfunction onMenuKeydown(e: KeyboardEvent, menu: MenubarMenu): void {\n const items = menu.items;\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n activeIndex.value = firstEnabled(items, Math.max(activeIndex.value, -1) + 1, 1);\n if (activeIndex.value === -1) activeIndex.value = firstEnabled(items, 0, 1);\n break;\n case \"ArrowUp\":\n e.preventDefault();\n activeIndex.value =\n activeIndex.value <= 0\n ? firstEnabled(items, items.length - 1, -1)\n : firstEnabled(items, activeIndex.value - 1, -1);\n break;\n case \"Home\":\n e.preventDefault();\n activeIndex.value = firstEnabled(items, 0, 1);\n break;\n case \"End\":\n e.preventDefault();\n activeIndex.value = firstEnabled(items, items.length - 1, -1);\n break;\n case \"ArrowRight\":\n e.preventDefault();\n moveMenu(1);\n break;\n case \"ArrowLeft\":\n e.preventDefault();\n moveMenu(-1);\n break;\n case \"Enter\":\n case \" \": {\n e.preventDefault();\n const item = activeIndex.value >= 0 ? items[activeIndex.value] : undefined;\n if (item) choose(item);\n break;\n }\n case \"Escape\":\n e.preventDefault();\n close();\n break;\n }\n}\n\nonMounted(() => window.addEventListener(\"click\", close));\nonBeforeUnmount(() => window.removeEventListener(\"click\", close));\n</script>\n\n<template>\n <div\n role=\"menubar\"\n class=\"flex items-center gap-0.5\"\n style=\"-webkit-app-region: no-drag\"\n @click.stop\n >\n <div v-for=\"menu in menus\" :key=\"menu.id\" class=\"relative\">\n <button\n :id=\"`menubar-trigger-${menu.id}`\"\n type=\"button\"\n role=\"menuitem\"\n :aria-haspopup=\"true\"\n :aria-expanded=\"openId === menu.id\"\n class=\"rounded-sm px-2 py-1 text-xs text-muted-foreground transition-colors duration-fast ease-out hover:bg-subtle hover:text-foreground focus-visible:bg-subtle focus-visible:text-foreground\"\n :class=\"openId === menu.id && 'bg-subtle text-foreground'\"\n @click.stop=\"toggle(menu.id)\"\n @mouseenter=\"hover(menu.id)\"\n @focus=\"hover(menu.id)\"\n @keydown=\"onTriggerKeydown($event, menu)\"\n >\n {{ menu.label }}\n </button>\n\n <div\n v-if=\"openId === menu.id\"\n role=\"menu\"\n :aria-label=\"menu.label\"\n tabindex=\"-1\"\n class=\"absolute left-0 top-full z-50 mt-1 min-w-[13rem] origin-top-left rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md animate-scale-in\"\n @click.stop\n @keydown=\"onMenuKeydown($event, menu)\"\n >\n <template v-for=\"(item, i) in openMenu(menu)\" :key=\"i\">\n <div v-if=\"item.separator\" role=\"separator\" class=\"my-1 h-px bg-border\" />\n <button\n v-else\n type=\"button\"\n role=\"menuitem\"\n :disabled=\"item.disabled\"\n :data-highlighted=\"activeIndex === i || undefined\"\n :style=\"{ animationDelay: listStaggerDelay(i) }\"\n class=\"flex w-full items-center justify-between gap-6 rounded-sm px-2 py-1.5 text-left text-xs text-foreground transition-colors duration-fast ease-out hover:bg-subtle disabled:pointer-events-none disabled:opacity-40 data-[highlighted]:bg-subtle animate-fade-rise\"\n @click=\"choose(item)\"\n @mouseenter=\"activeIndex = i\"\n @focus=\"activeIndex = i\"\n >\n <span>{{ item.label }}</span>\n <span v-if=\"item.shortcut\" class=\"tabular text-[0.6875rem] text-muted-foreground\">\n {{ item.shortcut }}\n </span>\n </button>\n </template>\n </div>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import NumberField_vue_vue_type_script_setup_true_lang_default from "./NumberField.vue_vue_type_script_setup_true_lang.js";
2
+ //#region src/primitives/NumberField/NumberField.vue
3
+ var NumberField_default = NumberField_vue_vue_type_script_setup_true_lang_default;
4
+ //#endregion
5
+ export { NumberField_default as default };
6
+
7
+ //# sourceMappingURL=NumberField.js.map
@@ -0,0 +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":""}
@@ -0,0 +1,47 @@
1
+ /**
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.
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.
10
+ *
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.
17
+ */
18
+ type __VLS_Props = {
19
+ /** The current value. Transient edits are reported before it changes. */
20
+ modelValue?: number;
21
+ /** Lower bound. Every path — typing, arrows, scrub — is clamped to it. */
22
+ min?: number;
23
+ /** Upper bound, clamped the same way. It wins over step alignment. */
24
+ max?: number;
25
+ /** The granularity of one arrow tick or one scrub step. Shift multiplies it by ten. */
26
+ step?: number;
27
+ /** A presentational suffix such as `px` or `deg`. It is never part of the value. */
28
+ 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, {
35
+ "update:modelValue": (value: number) => any;
36
+ commit: (value: number) => any;
37
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
38
+ "onUpdate:modelValue"?: (value: number) => any;
39
+ onCommit?: (value: number) => any;
40
+ }>, {
41
+ disabled: boolean;
42
+ invalid: boolean;
43
+ step: number;
44
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
45
+ declare const _default: typeof __VLS_export;
46
+ export default _default;
47
+ //# sourceMappingURL=NumberField.vue.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,204 @@
1
+ import { cn } from "../../lib/cn.js";
2
+ import { optional } from "../../lib/props.js";
3
+ import { useSplitAttrs } from "../../lib/attrs.js";
4
+ 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";
6
+ import { NumberFieldDecrement, NumberFieldIncrement, NumberFieldInput, NumberFieldRoot } from "reka-ui";
7
+ //#region src/primitives/NumberField/NumberField.vue?vue&type=script&setup=true&lang.ts
8
+ var _hoisted_1 = {
9
+ key: 0,
10
+ "aria-hidden": "true",
11
+ class: "pointer-events-none absolute right-2 text-xs text-muted-foreground transition-opacity duration-fast group-hover:opacity-0"
12
+ };
13
+ var _hoisted_2 = { class: "absolute right-1 flex flex-col opacity-0 transition-opacity duration-fast group-hover:opacity-100" };
14
+ /**
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.
30
+ */
31
+ var DRAG_THRESHOLD_PX = 3;
32
+ var PIXELS_PER_STEP = 4;
33
+ var NumberField_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
34
+ inheritAttrs: false,
35
+ __name: "NumberField",
36
+ props: {
37
+ modelValue: {},
38
+ min: {},
39
+ max: {},
40
+ step: { default: 1 },
41
+ unit: {},
42
+ disabled: {
43
+ type: Boolean,
44
+ default: false
45
+ },
46
+ invalid: {
47
+ type: Boolean,
48
+ default: false
49
+ }
50
+ },
51
+ emits: ["update:modelValue", "commit"],
52
+ setup(__props, { emit: __emit }) {
53
+ const props = __props;
54
+ const emit = __emit;
55
+ const { attrs, rest: inputAttrs } = useSplitAttrs();
56
+ const rootBounds = computed(() => optional({
57
+ modelValue: lastValue.value,
58
+ min: props.min,
59
+ max: props.max
60
+ }));
61
+ function clampValue(value) {
62
+ const step = props.step || 1;
63
+ const stepped = Math.round(value / step) * step;
64
+ const withMin = props.min !== void 0 ? Math.max(props.min, stepped) : stepped;
65
+ return props.max !== void 0 ? Math.min(props.max, withMin) : withMin;
66
+ }
67
+ const lastValue = ref(props.modelValue);
68
+ const lastCommittedValue = ref(props.modelValue);
69
+ let editPending = false;
70
+ watch(() => props.modelValue, (value) => {
71
+ lastValue.value = value;
72
+ if (!editPending) lastCommittedValue.value = value;
73
+ });
74
+ function onUpdate(value) {
75
+ const next = value ?? props.min ?? 0;
76
+ editPending = true;
77
+ lastValue.value = next;
78
+ emit("update:modelValue", next);
79
+ }
80
+ function commitLastValue() {
81
+ editPending = false;
82
+ if (lastValue.value === lastCommittedValue.value) return;
83
+ lastCommittedValue.value = lastValue.value;
84
+ emit("commit", lastValue.value ?? props.min ?? 0);
85
+ }
86
+ function onKeydownCapture(event) {
87
+ if (props.disabled) return;
88
+ if (!event.shiftKey || event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
89
+ event.preventDefault();
90
+ event.stopPropagation();
91
+ const tick = (props.step || 1) * 10;
92
+ const current = lastValue.value ?? props.min ?? 0;
93
+ onUpdate(clampValue(event.key === "ArrowUp" ? current + tick : current - tick));
94
+ }
95
+ function onKeydownCommit(event) {
96
+ if (event.key !== "Enter") return;
97
+ commitLastValue();
98
+ }
99
+ function onKeyupCommit(event) {
100
+ if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
101
+ commitLastValue();
102
+ }
103
+ function onFocusOut(event) {
104
+ const root = event.currentTarget;
105
+ if (event.relatedTarget instanceof Node && root.contains(event.relatedTarget)) return;
106
+ commitLastValue();
107
+ }
108
+ let removeDragListeners;
109
+ onUnmounted(() => removeDragListeners?.());
110
+ function onPointerDown(event) {
111
+ if (props.disabled || event.button !== 0) return;
112
+ const startX = event.clientX;
113
+ const startValue = lastValue.value ?? props.min ?? 0;
114
+ const step = props.step || 1;
115
+ let dragging = false;
116
+ function onPointerMove(moveEvent) {
117
+ const deltaX = moveEvent.clientX - startX;
118
+ if (!dragging) {
119
+ if (Math.abs(deltaX) < DRAG_THRESHOLD_PX) return;
120
+ dragging = true;
121
+ window.getSelection()?.removeAllRanges();
122
+ }
123
+ moveEvent.preventDefault();
124
+ const deltaSteps = Math.round(deltaX / PIXELS_PER_STEP);
125
+ const next = clampValue(startValue + deltaSteps * step);
126
+ if (next !== lastValue.value) onUpdate(next);
127
+ }
128
+ function onPointerUp() {
129
+ removeDragListeners?.();
130
+ if (dragging) commitLastValue();
131
+ }
132
+ function abortDrag() {
133
+ removeDragListeners?.();
134
+ if (!dragging) return;
135
+ if (lastValue.value !== startValue) onUpdate(startValue);
136
+ editPending = false;
137
+ }
138
+ function onDragKeydown(keyEvent) {
139
+ if (keyEvent.key !== "Escape" || !dragging) return;
140
+ keyEvent.preventDefault();
141
+ keyEvent.stopPropagation();
142
+ abortDrag();
143
+ }
144
+ window.addEventListener("pointermove", onPointerMove);
145
+ window.addEventListener("pointerup", onPointerUp);
146
+ window.addEventListener("pointercancel", abortDrag);
147
+ window.addEventListener("keydown", onDragKeydown, true);
148
+ removeDragListeners = () => {
149
+ window.removeEventListener("pointermove", onPointerMove);
150
+ window.removeEventListener("pointerup", onPointerUp);
151
+ window.removeEventListener("pointercancel", abortDrag);
152
+ window.removeEventListener("keydown", onDragKeydown, true);
153
+ removeDragListeners = void 0;
154
+ };
155
+ }
156
+ return (_ctx, _cache) => {
157
+ return openBlock(), createBlock(unref(NumberFieldRoot), mergeProps(rootBounds.value, {
158
+ 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),
163
+ "onUpdate:modelValue": onUpdate,
164
+ onKeydownCapture,
165
+ onKeydown: onKeydownCommit,
166
+ onKeyup: onKeyupCommit,
167
+ onFocusout: onFocusOut,
168
+ onPointerdown: onPointerDown
169
+ }), {
170
+ 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"]),
175
+ __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" }, {
177
+ default: withCtx(() => [createVNode(unref(ChevronUp), {
178
+ class: "h-3 w-3",
179
+ "stroke-width": 2.5
180
+ })]),
181
+ _: 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" }, {
183
+ default: withCtx(() => [createVNode(unref(ChevronDown), {
184
+ class: "h-3 w-3",
185
+ "stroke-width": 2.5
186
+ })]),
187
+ _: 1
188
+ })])
189
+ ]),
190
+ _: 1
191
+ }, 16, [
192
+ "step",
193
+ "disabled",
194
+ "aria-disabled",
195
+ "data-invalid",
196
+ "class"
197
+ ]);
198
+ };
199
+ }
200
+ });
201
+ //#endregion
202
+ export { NumberField_vue_vue_type_script_setup_true_lang_default as default };
203
+
204
+ //# sourceMappingURL=NumberField.vue_vue_type_script_setup_true_lang.js.map
@@ -0,0 +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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"}
@@ -0,0 +1,7 @@
1
+ import Popover_vue_vue_type_script_setup_true_lang_default from "./Popover.vue_vue_type_script_setup_true_lang.js";
2
+ //#region src/primitives/Popover/Popover.vue
3
+ var Popover_default = Popover_vue_vue_type_script_setup_true_lang_default;
4
+ //#endregion
5
+ export { Popover_default as default };
6
+
7
+ //# sourceMappingURL=Popover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Popover.js","names":[],"sources":["../../../src/primitives/Popover/Popover.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Which edge of the trigger the panel prefers. It is a preference and not a\n * guarantee: Reka flips the panel to the opposite edge when it would collide\n * with the viewport, which is why a caller sets the side it *wants* rather\n * than the side it will get.\n *\n * The members mirror Reka's own `Side`. Nothing enforces that in this\n * declaration — the template binding does: give this union a member Reka does\n * not accept and `:side=\"side\"` stops type-checking. Declaring it here rather\n * than aliasing `PopoverContentProps[\"side\"]` is what puts the four values in\n * the generated props table, because the documentation reads syntax and an\n * indexed access type has none to read.\n */\nexport type PopoverSide = \"top\" | \"right\" | \"bottom\" | \"left\";\n\n/** Where the panel sits along that edge. */\nexport type PopoverAlign = \"start\" | \"center\" | \"end\";\n</script>\n\n<script setup lang=\"ts\">\nimport { PopoverRoot, PopoverTrigger, PopoverPortal, PopoverContent, PopoverArrow } from \"reka-ui\";\nimport { cn } from \"../../lib/cn\";\nimport { optional } from \"../../lib/props\";\n\n/**\n * Popover — a floating panel anchored to a trigger, carrying secondary content\n * the user opts into: a filter set, a colour picker, a details card.\n *\n * The escape contract, which is the whole reason to reach for an overlay\n * primitive rather than an absolutely positioned div: opening moves focus into\n * the panel, Esc and an outside click both close it, closing returns focus to\n * the trigger, and the page behind keeps scrolling because a popover is not\n * modal. Focus is *not* trapped — Tab leaves the panel, which is correct for\n * content the user can walk past. `Dialog` is the one that traps.\n *\n * Reach for `DropdownMenu` when the panel is a list of commands, `Tooltip` for\n * a hint that must not take focus at all, and `Dialog` when the task has to\n * block the rest of the UI.\n */\nwithDefaults(\n defineProps<{\n /** Drive the panel from the host with `v-model:open`; omit it and the popover owns its own state. */\n open?: boolean | undefined;\n /** The edge of the trigger to anchor against, before collision flipping. */\n side?: PopoverSide;\n /** Where the panel sits along that edge — `start` keeps its leading edge flush with the trigger's. */\n align?: PopoverAlign;\n /** Gap in pixels between the trigger and the panel. */\n sideOffset?: number;\n /** Draw the pointer notch. Turn it off where the panel reads better flush against its trigger. */\n arrow?: boolean;\n }>(),\n // `open: undefined` is load-bearing, not a redundant default — it is what\n // keeps the uncontrolled third state reachable past Vue's absent-Boolean\n // casting. `optional()` in the template is the other half; its docblock\n // carries the reasoning for both.\n { open: undefined, side: \"bottom\", align: \"center\", sideOffset: 6, arrow: true },\n);\n\ndefineEmits<{ \"update:open\": [value: boolean] }>();\n</script>\n\n<template>\n <PopoverRoot v-bind=\"optional({ open })\" @update:open=\"$emit('update:open', $event)\">\n <!-- @slot The control that opens the panel. Rendered `as-child`, so the\n caller's own element *is* the trigger and no wrapper swallows its\n accessible name. -->\n <PopoverTrigger as-child>\n <slot name=\"trigger\" />\n </PopoverTrigger>\n\n <PopoverPortal>\n <PopoverContent\n :side=\"side\"\n :align=\"align\"\n :side-offset=\"sideOffset\"\n :class=\"\n cn(\n 'z-50 min-w-[12rem] max-w-[min(90vw,22rem)] rounded-md border border-border bg-popover p-3 text-sm text-popover-foreground shadow-md outline-none',\n // Scoped to the open state, never unconditional. Reka's Presence\n // keeps closed content mounted until an animationend arrives, and a\n // mount-only animation never fires a second one — so an\n // unconditional animation class strands an invisible panel over the\n // page, swallowing clicks. Scoped, the closed element computes\n // `animation-name: none` and Presence takes its immediate-unmount\n // branch instead. The origin below is what makes that scale grow\n // out of the trigger rather than out of the panel's own centre.\n 'data-[state=open]:animate-scale-in',\n )\n \"\n style=\"transform-origin: var(--reka-popper-transform-origin)\"\n >\n <!-- @slot The panel body. -->\n <slot />\n <PopoverArrow v-if=\"arrow\" class=\"fill-popover\" :width=\"12\" :height=\"6\" />\n </PopoverContent>\n </PopoverPortal>\n </PopoverRoot>\n</template>\n"],"mappings":""}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Which edge of the trigger the panel prefers. It is a preference and not a
3
+ * guarantee: Reka flips the panel to the opposite edge when it would collide
4
+ * with the viewport, which is why a caller sets the side it *wants* rather
5
+ * than the side it will get.
6
+ *
7
+ * The members mirror Reka's own `Side`. Nothing enforces that in this
8
+ * declaration — the template binding does: give this union a member Reka does
9
+ * not accept and `:side="side"` stops type-checking. Declaring it here rather
10
+ * than aliasing `PopoverContentProps["side"]` is what puts the four values in
11
+ * the generated props table, because the documentation reads syntax and an
12
+ * indexed access type has none to read.
13
+ */
14
+ export type PopoverSide = "top" | "right" | "bottom" | "left";
15
+ /** Where the panel sits along that edge. */
16
+ export type PopoverAlign = "start" | "center" | "end";
17
+ declare const _default: typeof __VLS_export;
18
+ export default _default;
19
+ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
20
+ /** Drive the panel from the host with `v-model:open`; omit it and the popover owns its own state. */
21
+ open?: boolean | undefined;
22
+ /** The edge of the trigger to anchor against, before collision flipping. */
23
+ side?: PopoverSide;
24
+ /** Where the panel sits along that edge — `start` keeps its leading edge flush with the trigger's. */
25
+ align?: PopoverAlign;
26
+ /** Gap in pixels between the trigger and the panel. */
27
+ sideOffset?: number;
28
+ /** Draw the pointer notch. Turn it off where the panel reads better flush against its trigger. */
29
+ arrow?: boolean;
30
+ }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
31
+ "update:open": (value: boolean) => any;
32
+ }, string, import("vue").PublicProps, Readonly<{
33
+ /** Drive the panel from the host with `v-model:open`; omit it and the popover owns its own state. */
34
+ open?: boolean | undefined;
35
+ /** The edge of the trigger to anchor against, before collision flipping. */
36
+ side?: PopoverSide;
37
+ /** Where the panel sits along that edge — `start` keeps its leading edge flush with the trigger's. */
38
+ align?: PopoverAlign;
39
+ /** Gap in pixels between the trigger and the panel. */
40
+ sideOffset?: number;
41
+ /** Draw the pointer notch. Turn it off where the panel reads better flush against its trigger. */
42
+ arrow?: boolean;
43
+ }> & Readonly<{
44
+ "onUpdate:open"?: (value: boolean) => any;
45
+ }>, {
46
+ open: boolean | undefined;
47
+ side: PopoverSide;
48
+ sideOffset: number;
49
+ align: PopoverAlign;
50
+ arrow: boolean;
51
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
52
+ trigger?: (props: {}) => any;
53
+ } & {
54
+ default?: (props: {}) => any;
55
+ }>;
56
+ type __VLS_WithSlots<T, S> = T & {
57
+ new (): {
58
+ $slots: S;
59
+ };
60
+ };
61
+ //# sourceMappingURL=Popover.vue.d.ts.map