@threadlabs/looma 0.5.1 → 0.6.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/README.md +1 -1
  2. package/components/shared/overlay.js +3 -0
  3. package/components/ui-button/ui-button.html +27 -14
  4. package/components/ui-callout/ui-callout.html +1 -1
  5. package/components/ui-checkbox/ui-checkbox.html +1 -1
  6. package/components/ui-cluster/ui-cluster.html +6 -6
  7. package/components/ui-combobox/ui-combobox.html +35 -27
  8. package/components/ui-container/ui-container.html +4 -4
  9. package/components/ui-context-menu/ui-context-menu.html +2 -2
  10. package/components/ui-dialog/ui-dialog.html +21 -8
  11. package/components/ui-disclosure/ui-disclosure.html +19 -4
  12. package/components/ui-editable/ui-editable.html +6 -6
  13. package/components/ui-editor-insert-table-grid/ui-editor-insert-table-grid.html +21 -21
  14. package/components/ui-editor-mention-menu/ui-editor-mention-menu.html +26 -26
  15. package/components/ui-editor-slash-menu/ui-editor-slash-menu.html +39 -39
  16. package/components/ui-editor-table-context-menu/ui-editor-table-context-menu.html +26 -22
  17. package/components/ui-editor-table-overlay/ui-editor-table-overlay.html +68 -52
  18. package/components/ui-editor-table-toolbar/ui-editor-table-toolbar.html +42 -38
  19. package/components/ui-editor-toolbar/ui-editor-toolbar.html +9 -9
  20. package/components/ui-floating-action-button/ui-floating-action-button.html +16 -22
  21. package/components/ui-form-field/ui-form-field.html +18 -7
  22. package/components/ui-grid/ui-grid.html +6 -6
  23. package/components/ui-icon/ui-icon.html +5 -1
  24. package/components/ui-icon-button/ui-icon-button.html +68 -34
  25. package/components/ui-input/ui-input.html +2 -2
  26. package/components/ui-menu/ui-menu.html +15 -4
  27. package/components/ui-menu-item/ui-menu-item.html +5 -5
  28. package/components/ui-popover/ui-popover.html +3 -3
  29. package/components/ui-popover/ui-popover.js +10 -2
  30. package/components/ui-reel/ui-reel.html +6 -6
  31. package/components/ui-search-result-row/ui-search-result-row.html +9 -9
  32. package/components/ui-search-shell/ui-search-shell.html +10 -3
  33. package/components/ui-search-shell/ui-search-shell.js +9 -0
  34. package/components/ui-select/ui-select.html +2 -2
  35. package/components/ui-sidebar/ui-sidebar.html +11 -11
  36. package/components/ui-stack/ui-stack.html +6 -6
  37. package/components/ui-switcher/ui-switcher.html +6 -6
  38. package/components/ui-tabs/ui-tabs.html +19 -5
  39. package/components/ui-textarea/ui-textarea.html +2 -2
  40. package/components/ui-toast-region/ui-toast-region.html +3 -3
  41. package/components/ui-tooltip/ui-tooltip.html +1 -1
  42. package/components/ui-top-bar/ui-top-bar.html +5 -5
  43. package/components/ui-tree/ui-tree.html +7 -0
  44. package/components/ui-tree-item/ui-tree-item.html +20 -7
  45. package/dist/index.d.ts +1 -1
  46. package/dist/index.js +2 -2
  47. package/package.json +2 -2
  48. package/styles/ui-affordance-scope.css +1 -1
  49. package/styles/ui-avatar-group.css +1 -1
  50. package/styles/ui-avatar.css +1 -1
  51. package/styles/ui-badge.css +1 -1
  52. package/styles/ui-button.css +25 -15
  53. package/styles/ui-callout.css +2 -2
  54. package/styles/ui-checkbox.css +2 -2
  55. package/styles/ui-cluster.css +7 -7
  56. package/styles/ui-combobox.css +24 -24
  57. package/styles/ui-container.css +5 -5
  58. package/styles/ui-context-menu.css +3 -3
  59. package/styles/ui-dialog.css +26 -9
  60. package/styles/ui-disclosure.css +23 -7
  61. package/styles/ui-editable.css +7 -7
  62. package/styles/ui-editor-insert-table-grid.css +21 -21
  63. package/styles/ui-editor-mention-menu.css +26 -26
  64. package/styles/ui-editor-slash-menu.css +40 -40
  65. package/styles/ui-editor-table-context-menu.css +21 -21
  66. package/styles/ui-editor-table-overlay.css +49 -49
  67. package/styles/ui-editor-table-toolbar.css +35 -35
  68. package/styles/ui-editor-toolbar.css +10 -10
  69. package/styles/ui-floating-action-button.css +17 -23
  70. package/styles/ui-form-field.css +24 -10
  71. package/styles/ui-grid.css +7 -7
  72. package/styles/ui-icon-button.css +74 -35
  73. package/styles/ui-icon.css +1 -1
  74. package/styles/ui-input.css +3 -3
  75. package/styles/ui-menu-item.css +6 -6
  76. package/styles/ui-menu.css +19 -7
  77. package/styles/ui-popover.css +4 -4
  78. package/styles/ui-radio-group.css +1 -1
  79. package/styles/ui-radio.css +1 -1
  80. package/styles/ui-reel.css +7 -7
  81. package/styles/ui-search-result-row.css +10 -10
  82. package/styles/ui-search-shell.css +14 -5
  83. package/styles/ui-select.css +3 -3
  84. package/styles/ui-separator.css +1 -1
  85. package/styles/ui-sidebar.css +12 -12
  86. package/styles/ui-stack.css +7 -7
  87. package/styles/ui-switch.css +1 -1
  88. package/styles/ui-switcher.css +7 -7
  89. package/styles/ui-tabs.css +21 -7
  90. package/styles/ui-textarea.css +3 -3
  91. package/styles/ui-toast-region.css +3 -3
  92. package/styles/ui-tooltip.css +2 -2
  93. package/styles/ui-top-bar.css +6 -6
  94. package/styles/ui-tree-item.css +30 -9
  95. package/styles/ui-tree.css +8 -2
  96. package/theme-dark.css +42 -103
  97. package/theme-high-contrast.css +18 -22
  98. package/theme-light.css +15 -41
  99. package/tokens.css +149 -201
  100. package/vanilla/UiAffordanceScope.d.ts +1 -1
  101. package/vanilla/UiAffordanceScope.js +2 -2
  102. package/vanilla/UiAvatar.d.ts +1 -1
  103. package/vanilla/UiAvatar.js +2 -2
  104. package/vanilla/UiAvatarGroup.d.ts +1 -1
  105. package/vanilla/UiAvatarGroup.js +2 -2
  106. package/vanilla/UiBadge.d.ts +1 -1
  107. package/vanilla/UiBadge.js +2 -2
  108. package/vanilla/UiButton.d.ts +3 -1
  109. package/vanilla/UiButton.js +3 -3
  110. package/vanilla/UiCallout.d.ts +1 -1
  111. package/vanilla/UiCallout.js +2 -2
  112. package/vanilla/UiCheckbox.d.ts +1 -1
  113. package/vanilla/UiCheckbox.js +2 -2
  114. package/vanilla/UiCluster.d.ts +1 -1
  115. package/vanilla/UiCluster.js +2 -2
  116. package/vanilla/UiCombobox.d.ts +1 -1
  117. package/vanilla/UiCombobox.js +3 -3
  118. package/vanilla/UiContainer.d.ts +1 -1
  119. package/vanilla/UiContainer.js +2 -2
  120. package/vanilla/UiContextMenu.d.ts +1 -1
  121. package/vanilla/UiContextMenu.js +2 -2
  122. package/vanilla/UiDialog.d.ts +1 -1
  123. package/vanilla/UiDialog.js +2 -2
  124. package/vanilla/UiDisclosure.d.ts +2 -1
  125. package/vanilla/UiDisclosure.js +3 -3
  126. package/vanilla/UiEditable.d.ts +1 -1
  127. package/vanilla/UiEditable.js +2 -2
  128. package/vanilla/UiEditorInsertTableGrid.d.ts +1 -1
  129. package/vanilla/UiEditorInsertTableGrid.js +3 -3
  130. package/vanilla/UiEditorMentionMenu.d.ts +1 -1
  131. package/vanilla/UiEditorMentionMenu.js +3 -3
  132. package/vanilla/UiEditorSlashMenu.d.ts +1 -1
  133. package/vanilla/UiEditorSlashMenu.js +2 -2
  134. package/vanilla/UiEditorTableContextMenu.d.ts +1 -1
  135. package/vanilla/UiEditorTableContextMenu.js +3 -3
  136. package/vanilla/UiEditorTableOverlay.d.ts +1 -1
  137. package/vanilla/UiEditorTableOverlay.js +3 -3
  138. package/vanilla/UiEditorTableToolbar.d.ts +1 -1
  139. package/vanilla/UiEditorTableToolbar.js +3 -3
  140. package/vanilla/UiEditorToolbar.d.ts +1 -1
  141. package/vanilla/UiEditorToolbar.js +2 -2
  142. package/vanilla/UiFloatingActionButton.d.ts +1 -1
  143. package/vanilla/UiFloatingActionButton.js +2 -2
  144. package/vanilla/UiFormField.d.ts +1 -1
  145. package/vanilla/UiFormField.js +15 -6
  146. package/vanilla/UiGrid.d.ts +1 -1
  147. package/vanilla/UiGrid.js +2 -2
  148. package/vanilla/UiIcon.d.ts +1 -1
  149. package/vanilla/UiIcon.js +3 -3
  150. package/vanilla/UiIconButton.d.ts +1 -1
  151. package/vanilla/UiIconButton.js +2 -2
  152. package/vanilla/UiInput.d.ts +1 -1
  153. package/vanilla/UiInput.js +2 -2
  154. package/vanilla/UiMenu.d.ts +2 -1
  155. package/vanilla/UiMenu.js +3 -3
  156. package/vanilla/UiMenuItem.d.ts +1 -1
  157. package/vanilla/UiMenuItem.js +2 -2
  158. package/vanilla/UiPopover.d.ts +1 -1
  159. package/vanilla/UiPopover.js +2 -2
  160. package/vanilla/UiRadio.d.ts +1 -1
  161. package/vanilla/UiRadio.js +2 -2
  162. package/vanilla/UiRadioGroup.d.ts +1 -1
  163. package/vanilla/UiRadioGroup.js +2 -2
  164. package/vanilla/UiReel.d.ts +1 -1
  165. package/vanilla/UiReel.js +2 -2
  166. package/vanilla/UiSearchResultRow.d.ts +1 -1
  167. package/vanilla/UiSearchResultRow.js +2 -2
  168. package/vanilla/UiSearchShell.d.ts +1 -1
  169. package/vanilla/UiSearchShell.js +2 -2
  170. package/vanilla/UiSelect.d.ts +1 -1
  171. package/vanilla/UiSelect.js +2 -2
  172. package/vanilla/UiSeparator.d.ts +1 -1
  173. package/vanilla/UiSeparator.js +2 -2
  174. package/vanilla/UiSidebar.d.ts +1 -1
  175. package/vanilla/UiSidebar.js +2 -2
  176. package/vanilla/UiStack.d.ts +1 -1
  177. package/vanilla/UiStack.js +2 -2
  178. package/vanilla/UiSwitch.d.ts +1 -1
  179. package/vanilla/UiSwitch.js +2 -2
  180. package/vanilla/UiSwitcher.d.ts +1 -1
  181. package/vanilla/UiSwitcher.js +2 -2
  182. package/vanilla/UiTabs.d.ts +2 -1
  183. package/vanilla/UiTabs.js +3 -3
  184. package/vanilla/UiTextarea.d.ts +1 -1
  185. package/vanilla/UiTextarea.js +2 -2
  186. package/vanilla/UiToastRegion.d.ts +1 -1
  187. package/vanilla/UiToastRegion.js +3 -3
  188. package/vanilla/UiTooltip.d.ts +1 -1
  189. package/vanilla/UiTooltip.js +2 -2
  190. package/vanilla/UiTopBar.d.ts +1 -1
  191. package/vanilla/UiTopBar.js +1 -1
  192. package/vanilla/UiTree.d.ts +2 -1
  193. package/vanilla/UiTree.js +3 -3
  194. package/vanilla/UiTreeItem.d.ts +1 -1
  195. package/vanilla/UiTreeItem.js +2 -2
  196. package/vue/UiAffordanceScope.d.ts +2 -2
  197. package/vue/UiAffordanceScope.vue +92 -73
  198. package/vue/UiAvatar.d.ts +7 -7
  199. package/vue/UiAvatar.vue +145 -105
  200. package/vue/UiAvatarGroup.d.ts +4 -4
  201. package/vue/UiAvatarGroup.vue +134 -118
  202. package/vue/UiBadge.d.ts +4 -4
  203. package/vue/UiBadge.vue +113 -114
  204. package/vue/UiButton.d.ts +10 -6
  205. package/vue/UiButton.vue +295 -248
  206. package/vue/UiCallout.d.ts +2 -2
  207. package/vue/UiCallout.vue +154 -87
  208. package/vue/UiCheckbox.d.ts +14 -10
  209. package/vue/UiCheckbox.vue +228 -162
  210. package/vue/UiCluster.d.ts +2 -2
  211. package/vue/UiCluster.vue +53 -60
  212. package/vue/UiCombobox.d.ts +36 -32
  213. package/vue/UiCombobox.vue +1005 -416
  214. package/vue/UiContainer.d.ts +2 -2
  215. package/vue/UiContainer.vue +48 -51
  216. package/vue/UiContextMenu.d.ts +6 -4
  217. package/vue/UiContextMenu.vue +205 -116
  218. package/vue/UiDialog.d.ts +11 -9
  219. package/vue/UiDialog.vue +287 -212
  220. package/vue/UiDisclosure.d.ts +10 -6
  221. package/vue/UiDisclosure.vue +265 -159
  222. package/vue/UiEditable.d.ts +16 -12
  223. package/vue/UiEditable.vue +325 -205
  224. package/vue/UiEditorInsertTableGrid.d.ts +8 -8
  225. package/vue/UiEditorInsertTableGrid.vue +216 -157
  226. package/vue/UiEditorMentionMenu.d.ts +9 -9
  227. package/vue/UiEditorMentionMenu.vue +265 -222
  228. package/vue/UiEditorSlashMenu.d.ts +7 -7
  229. package/vue/UiEditorSlashMenu.vue +286 -240
  230. package/vue/UiEditorTableContextMenu.d.ts +5 -5
  231. package/vue/UiEditorTableContextMenu.vue +306 -193
  232. package/vue/UiEditorTableOverlay.d.ts +2 -2
  233. package/vue/UiEditorTableOverlay.vue +600 -369
  234. package/vue/UiEditorTableToolbar.d.ts +7 -7
  235. package/vue/UiEditorTableToolbar.vue +443 -268
  236. package/vue/UiEditorToolbar.d.ts +2 -2
  237. package/vue/UiEditorToolbar.vue +134 -121
  238. package/vue/UiFloatingActionButton.d.ts +6 -6
  239. package/vue/UiFloatingActionButton.vue +95 -99
  240. package/vue/UiFormField.d.ts +6 -6
  241. package/vue/UiFormField.vue +112 -80
  242. package/vue/UiGrid.d.ts +2 -2
  243. package/vue/UiGrid.vue +51 -57
  244. package/vue/UiIcon.d.ts +2 -2
  245. package/vue/UiIcon.vue +118 -89
  246. package/vue/UiIconButton.d.ts +11 -11
  247. package/vue/UiIconButton.vue +287 -217
  248. package/vue/UiInput.d.ts +9 -9
  249. package/vue/UiInput.vue +153 -127
  250. package/vue/UiMenu.d.ts +10 -6
  251. package/vue/UiMenu.vue +227 -143
  252. package/vue/UiMenuItem.d.ts +4 -4
  253. package/vue/UiMenuItem.vue +53 -46
  254. package/vue/UiPopover.d.ts +8 -6
  255. package/vue/UiPopover.vue +197 -137
  256. package/vue/UiRadio.d.ts +14 -10
  257. package/vue/UiRadio.vue +183 -117
  258. package/vue/UiRadioGroup.d.ts +14 -12
  259. package/vue/UiRadioGroup.vue +172 -111
  260. package/vue/UiReel.d.ts +3 -3
  261. package/vue/UiReel.vue +77 -77
  262. package/vue/UiSearchResultRow.d.ts +4 -4
  263. package/vue/UiSearchResultRow.vue +121 -115
  264. package/vue/UiSearchShell.d.ts +10 -8
  265. package/vue/UiSearchShell.vue +245 -176
  266. package/vue/UiSelect.d.ts +9 -9
  267. package/vue/UiSelect.vue +162 -134
  268. package/vue/UiSeparator.d.ts +2 -2
  269. package/vue/UiSeparator.vue +22 -27
  270. package/vue/UiSidebar.d.ts +20 -18
  271. package/vue/UiSidebar.vue +307 -224
  272. package/vue/UiStack.d.ts +3 -3
  273. package/vue/UiStack.vue +64 -70
  274. package/vue/UiSwitch.d.ts +12 -8
  275. package/vue/UiSwitch.vue +218 -150
  276. package/vue/UiSwitcher.d.ts +3 -3
  277. package/vue/UiSwitcher.vue +73 -75
  278. package/vue/UiTabs.d.ts +12 -8
  279. package/vue/UiTabs.vue +268 -210
  280. package/vue/UiTextarea.d.ts +11 -11
  281. package/vue/UiTextarea.vue +155 -128
  282. package/vue/UiToastRegion.d.ts +10 -8
  283. package/vue/UiToastRegion.vue +261 -198
  284. package/vue/UiTooltip.d.ts +14 -12
  285. package/vue/UiTooltip.vue +241 -177
  286. package/vue/UiTopBar.vue +51 -42
  287. package/vue/UiTree.d.ts +10 -6
  288. package/vue/UiTree.vue +180 -70
  289. package/vue/UiTreeItem.d.ts +24 -22
  290. package/vue/UiTreeItem.vue +464 -339
  291. package/vue/chunks/UiAffordanceScope.js +9 -16
  292. package/vue/chunks/UiAvatar.js +23 -29
  293. package/vue/chunks/UiAvatarGroup.js +16 -39
  294. package/vue/chunks/UiBadge.js +3 -12
  295. package/vue/chunks/UiButton.js +15 -18
  296. package/vue/chunks/UiCallout.js +8 -17
  297. package/vue/chunks/UiCheckbox.js +37 -40
  298. package/vue/chunks/UiCluster.js +3 -12
  299. package/vue/chunks/UiCombobox.js +224 -266
  300. package/vue/chunks/UiContainer.js +3 -12
  301. package/vue/chunks/UiContextMenu.js +39 -34
  302. package/vue/chunks/UiDialog.js +36 -39
  303. package/vue/chunks/UiDisclosure.js +44 -48
  304. package/vue/chunks/UiEditable.js +55 -57
  305. package/vue/chunks/UiEditorInsertTableGrid.js +55 -73
  306. package/vue/chunks/UiEditorMentionMenu.js +40 -81
  307. package/vue/chunks/UiEditorSlashMenu.js +28 -52
  308. package/vue/chunks/UiEditorTableContextMenu.js +32 -56
  309. package/vue/chunks/UiEditorTableOverlay.js +58 -82
  310. package/vue/chunks/UiEditorTableToolbar.js +61 -85
  311. package/vue/chunks/UiEditorToolbar.js +4 -13
  312. package/vue/chunks/UiFloatingActionButton.js +7 -21
  313. package/vue/chunks/UiFormField.js +15 -12
  314. package/vue/chunks/UiGrid.js +3 -12
  315. package/vue/chunks/UiIcon.js +20 -43
  316. package/vue/chunks/UiIconButton.js +16 -28
  317. package/vue/chunks/UiInput.js +20 -25
  318. package/vue/chunks/UiMenu.js +30 -33
  319. package/vue/chunks/UiMenuItem.js +4 -10
  320. package/vue/chunks/UiPopover.js +28 -29
  321. package/vue/chunks/UiRadio.js +36 -39
  322. package/vue/chunks/UiRadioGroup.js +30 -34
  323. package/vue/chunks/UiReel.js +6 -15
  324. package/vue/chunks/UiSearchResultRow.js +6 -19
  325. package/vue/chunks/UiSearchShell.js +25 -28
  326. package/vue/chunks/UiSelect.js +20 -23
  327. package/vue/chunks/UiSeparator.js +2 -8
  328. package/vue/chunks/UiSidebar.js +39 -44
  329. package/vue/chunks/UiStack.js +6 -15
  330. package/vue/chunks/UiSwitch.js +35 -38
  331. package/vue/chunks/UiSwitcher.js +6 -15
  332. package/vue/chunks/UiTabs.js +52 -85
  333. package/vue/chunks/UiTextarea.js +21 -26
  334. package/vue/chunks/UiToastRegion.js +38 -72
  335. package/vue/chunks/UiTooltip.js +34 -35
  336. package/vue/chunks/UiTopBar.js +1 -1
  337. package/vue/chunks/UiTree.js +27 -16
  338. package/vue/chunks/UiTreeItem.js +85 -102
  339. package/vue/components.css +4027 -3800
  340. package/vue/editor/LoomaEditor.d.ts +11 -0
  341. package/vue/editor/index.js +9 -0
@@ -1,418 +1,543 @@
1
- <!-- Generated by HTML Next 1.0.0-alpha.1 for Vue 3.5. Do not edit. -->
1
+ <!-- Generated by HTML Next 1.0.0-alpha.2 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
- import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watchEffect } from "vue";
4
- import * as controllerModule from "../components/ui-tree-item/ui-tree-item.js";
5
-
6
- defineOptions({ inheritAttrs: false });
7
-
8
- const hn = {
9
- t: (v: unknown): boolean => v === true || (typeof v === "string" ? v.length > 0 : typeof v === "number" ? v !== 0 && v === v : Array.isArray(v) ? v.length > 0 : v !== null && typeof v === "object" ? Object.keys(v).length > 0 : false),
10
- n: (v: unknown): number | undefined => typeof v === "number" && v === v ? v : undefined,
11
- text: (v: unknown): string => v === undefined || v === null ? "" : Array.isArray(v) ? v.map(hn.text).join(" ") : typeof v === "object" ? "" : String(v),
12
- attr: (v: unknown): any => v === undefined || v === null || v === false ? undefined : v === true ? "" : Array.isArray(v) ? v.map(hn.text).join(" ") : typeof v === "object" ? undefined : String(v),
13
- enumerated: (v: unknown): any => typeof v === "boolean" ? String(v) : hn.attr(v),
14
- call: (fn: string, ...args: unknown[]): any => {
15
- if (fn === "format") {
16
- let index = 1;
17
- return typeof args[0] === "string" ? args[0].replace(/%s/g, () => index < args.length ? hn.text(args[index++]) : "%s") : undefined;
18
- }
19
- const n = args.map(hn.n);
20
- if (n.some((value) => value === undefined)) return undefined;
21
- const v = n as number[];
22
- switch (fn) {
23
- case "abs": return v.length === 1 ? Math.abs(v[0]!) : undefined;
24
- case "round": return v.length === 1 ? Math.round(v[0]!) : undefined;
25
- case "min": return v.length > 0 ? Math.min(...v) : undefined;
26
- case "max": return v.length > 0 ? Math.max(...v) : undefined;
27
- case "clamp": return v.length === 3 ? Math.min(Math.max(v[0]!, v[1]!), v[2]!) : undefined;
28
- }
29
- return undefined;
3
+ import {
4
+ computed,
5
+ onBeforeUnmount,
6
+ onMounted,
7
+ ref,
8
+ shallowRef,
9
+ useTemplateRef,
10
+ watchEffect,
11
+ } from 'vue'
12
+ import * as controllerModule from '../components/ui-tree-item/ui-tree-item.js'
13
+
14
+ defineOptions({ inheritAttrs: false })
15
+
16
+ const props = withDefaults(
17
+ defineProps<{
18
+ accepts?: string
19
+ container?: boolean
20
+ disabled?: boolean
21
+ dragType?: string
22
+ dropDepth?: number
23
+ dropScope?: string
24
+ expanded?: boolean
25
+ itemId?: string
26
+ label?: string
27
+ selected?: boolean
28
+ sortable?: boolean
29
+ subtreeDepth?: number
30
+ }>(),
31
+ {
32
+ accepts: '',
33
+ container: false,
34
+ disabled: false,
35
+ dragType: 'item',
36
+ dropScope: '',
37
+ expanded: false,
38
+ itemId: '',
39
+ label: '',
40
+ selected: false,
41
+ sortable: false,
30
42
  },
31
- };
32
-
33
- const props = withDefaults(defineProps<{
34
- accepts?: string | null;
35
- container?: boolean | null;
36
- disabled?: boolean | null;
37
- dragType?: string | null;
38
- dropDepth?: number | null;
39
- dropScope?: string | null;
40
- expanded?: boolean | null;
41
- itemId?: string | null;
42
- label?: string | null;
43
- selected?: boolean | null;
44
- sortable?: boolean | null;
45
- subtreeDepth?: number | null;
46
- }>(), {
47
- accepts: "",
48
- container: false,
49
- disabled: false,
50
- dragType: "item",
51
- dropScope: "",
52
- expanded: false,
53
- itemId: "",
54
- label: "",
55
- selected: false,
56
- sortable: false,
57
- });
43
+ )
58
44
  const emit = defineEmits<{
59
- "expand": [detail: { readonly id: string; readonly expanded: boolean; readonly trigger: "keyboard" | "pointer" | "programmatic" }];
60
- }>();
61
- const root = ref<HTMLElement | null>(null);
62
- const refs: Record<string, Element | undefined> = {};
63
- const state_internalExpanded = ref<any>(false);
64
- const state_structuralLevel = ref<any>(1);
65
- const state_tabStop = ref<any>(false);
66
- const state_isContainer = ref<any>(false);
45
+ 'update:expanded': [value: boolean]
46
+ expand: [
47
+ detail: {
48
+ readonly id: string
49
+ readonly expanded: boolean
50
+ readonly trigger: 'keyboard' | 'pointer' | 'programmatic'
51
+ },
52
+ ]
53
+ }>()
54
+
55
+ const root = ref<HTMLElement | null>(null)
56
+ const rowElement = useTemplateRef<HTMLElement>('row')
57
+ const dragHandleElement = useTemplateRef<HTMLElement>('dragHandle')
58
+ const disclosureElement = useTemplateRef<HTMLElement>('disclosure')
59
+ const childrenElement = useTemplateRef<HTMLElement>('children')
60
+ const internalExpanded = ref(false)
61
+ const structuralLevel = ref(1)
62
+ const tabStop = ref(false)
63
+ const isContainer = ref(false)
64
+
65
+ /** The values the styles' :host-state() rules test. */
66
+ const hostState = computed(() =>
67
+ [
68
+ props.selected && 'selected',
69
+ props.disabled && 'disabled',
70
+ ].filter(Boolean).join(' ')
71
+ )
67
72
 
68
73
  /** Dispatches a component event to Vue listeners and, for controllers and page code, on the root. */
69
- const dispatch = (name: string, detail?: unknown): boolean => {
70
- const declared = {"expand":{"bubbles":false,"composed":true,"cancelable":false}} as Record<string, EventInit | undefined>;
74
+ function dispatch(name: string, detail?: unknown): boolean {
75
+ const declared: Record<string, EventInit | undefined> = {
76
+ expand: { bubbles: false, composed: true, cancelable: false },
77
+ }
71
78
  const checks: Record<string, (detail: unknown) => boolean> = {
72
- "expand": (detail: unknown) => (detail !== null && typeof detail === "object" && !Array.isArray(detail) && typeof (detail as Record<string, unknown>)["id"] === "string" && typeof (detail as Record<string, unknown>)["expanded"] === "boolean" && ((detail as Record<string, unknown>)["trigger"] === "keyboard" || (detail as Record<string, unknown>)["trigger"] === "pointer" || (detail as Record<string, unknown>)["trigger"] === "programmatic") && Object.keys(detail as object).every((key) => ["id","expanded","trigger"].includes(key))),
73
- };
79
+ expand: (detail) =>
80
+ detail !== null && typeof detail === 'object' && !Array.isArray(detail)
81
+ && typeof (detail as Record<string, unknown>)['id'] === 'string'
82
+ && typeof (detail as Record<string, unknown>)['expanded'] === 'boolean'
83
+ && ((detail as Record<string, unknown>)['trigger'] === 'keyboard'
84
+ || (detail as Record<string, unknown>)['trigger'] === 'pointer'
85
+ || (detail as Record<string, unknown>)['trigger'] === 'programmatic')
86
+ && Object.keys(detail as object).every((key) => ['id', 'expanded', 'trigger'].includes(key)),
87
+ }
74
88
  if (detail !== undefined && checks[name] !== undefined && !checks[name]!(detail)) {
75
- throw new TypeError(`HR002: Event \`${name}\` detail does not satisfy its declared type.`);
89
+ throw new TypeError(`HR002: Event \`${name}\` detail does not satisfy its declared type.`)
90
+ }
91
+ const emitEvent = emit as (name: string, detail: unknown) => void
92
+ emitEvent(name, detail)
93
+ if (detail !== null && typeof detail === 'object' && 'expanded' in detail) {
94
+ emitEvent('update:expanded', (detail as Record<string, unknown>)['expanded'])
76
95
  }
77
- (emit as (name: string, detail: unknown) => void)(name, detail);
78
- return root.value?.dispatchEvent(new CustomEvent(name, { bubbles: true, composed: true, ...declared[name], detail })) ?? true;
79
- };
80
-
81
- /** The resolved values the styles' :host-state() rules test. */
82
- const hostState = computed(() => (["selected","disabled"] as const).flatMap((name) => {
83
- const value = read(name);
84
- const tokens: string[] = hn.t(value) ? [name] : [];
85
- if (typeof value === "string" || typeof value === "number") tokens.push(`${name}=${encodeURIComponent(String(value))}`);
86
- return tokens;
87
- }).join(" "));
96
+ return root.value?.dispatchEvent(
97
+ new CustomEvent(name, { bubbles: true, composed: true, ...declared[name], detail }),
98
+ ) ?? true
99
+ }
88
100
 
89
101
  function read(name: string): unknown {
90
- if (name === "internalExpanded") return state_internalExpanded.value;
91
- if (name === "structuralLevel") return state_structuralLevel.value;
92
- if (name === "tabStop") return state_tabStop.value;
93
- if (name === "isContainer") return state_isContainer.value;
94
- return (props as Record<string, unknown>)[name];
102
+ if (name === 'internalExpanded') return internalExpanded.value
103
+ if (name === 'structuralLevel') return structuralLevel.value
104
+ if (name === 'tabStop') return tabStop.value
105
+ if (name === 'isContainer') return isContainer.value
106
+ return (props as Record<string, unknown>)[name]
95
107
  }
96
108
 
97
109
  function write(name: string, value: unknown): boolean {
98
- if (name === "internalExpanded") { state_internalExpanded.value = value; return true; }
99
- if (name === "structuralLevel") { state_structuralLevel.value = value; return true; }
100
- if (name === "tabStop") { state_tabStop.value = value; return true; }
101
- if (name === "isContainer") { state_isContainer.value = value; return true; }
102
- throw new TypeError(`Only declared state is writable; \`${name}\` is not.`);
110
+ if (name === 'internalExpanded') {
111
+ internalExpanded.value = value as never
112
+ return true
113
+ }
114
+ if (name === 'structuralLevel') {
115
+ structuralLevel.value = value as never
116
+ return true
117
+ }
118
+ if (name === 'tabStop') {
119
+ tabStop.value = value as never
120
+ return true
121
+ }
122
+ if (name === 'isContainer') {
123
+ isContainer.value = value as never
124
+ return true
125
+ }
126
+ throw new TypeError(`Only declared state is writable; \`${name}\` is not.`)
103
127
  }
104
128
 
105
- const stops: Array<() => void> = [];
129
+ const stops: Array<() => void> = []
106
130
  const host = {
107
- get element(): Element { return root.value as Element; },
131
+ get element(): Element {
132
+ return root.value as Element
133
+ },
108
134
  state: new Proxy({} as Record<string, unknown>, {
109
- get: (_target, name) => typeof name === "string" ? read(name) : undefined,
110
- set: (_target, name, value) => typeof name === "string" && write(name, value),
135
+ get: (_target, name) => typeof name === 'string' ? read(name) : undefined,
136
+ set: (_target, name, value) => typeof name === 'string' && write(name, value),
111
137
  }),
112
- refs: refs as Readonly<Record<string, Element>>,
138
+ refs: {
139
+ get row(): Element {
140
+ return rowElement.value as Element
141
+ },
142
+ get dragHandle(): Element {
143
+ return dragHandleElement.value as Element
144
+ },
145
+ get disclosure(): Element {
146
+ return disclosureElement.value as Element
147
+ },
148
+ get children(): Element {
149
+ return childrenElement.value as Element
150
+ },
151
+ } as Readonly<Record<string, Element>>,
113
152
  elements: new Proxy({} as Record<string, Element | RadioNodeList | undefined>, {
114
153
  get: (_target, name) => {
115
- if (typeof name !== "string" || root.value === null) return undefined;
116
- const form = root.value instanceof HTMLFormElement ? root.value : root.value.querySelector("form");
117
- return form?.elements.namedItem(name) ?? root.value.querySelector(`[name="${CSS.escape(name)}"]`) ?? undefined;
154
+ if (typeof name !== 'string' || root.value === null) return undefined
155
+ const form = root.value instanceof HTMLFormElement
156
+ ? root.value
157
+ : root.value.querySelector('form')
158
+ return form?.elements.namedItem(name)
159
+ ?? root.value.querySelector(`[name="${CSS.escape(name)}"]`) ?? undefined
118
160
  },
119
161
  }),
120
162
  signal<T>(initialValue: T) {
121
- const value = shallowRef(initialValue);
163
+ const value = shallowRef(initialValue)
122
164
  return {
123
165
  get: (): T => value.value,
124
- set: (next: T): void => { if (!Object.is(value.value, next)) value.value = next; },
125
- update: (next: (current: T) => T): void => { const updated = next(value.value); if (!Object.is(value.value, updated)) value.value = updated; },
126
- };
166
+ set: (next: T): void => {
167
+ if (!Object.is(value.value, next)) value.value = next
168
+ },
169
+ update: (next: (current: T) => T): void => {
170
+ const updated = next(value.value)
171
+ if (!Object.is(value.value, updated)) value.value = updated
172
+ },
173
+ }
127
174
  },
128
175
  computed<T>(compute: () => T) {
129
- const value = computed(compute);
130
- return { get: (): T => value.value };
176
+ const value = computed(compute)
177
+ return { get: (): T => value.value }
131
178
  },
132
179
  effect(run: () => void | (() => void)): () => void {
133
180
  const stop = watchEffect((onCleanup) => {
134
- const cleanup = run();
135
- if (typeof cleanup === "function") onCleanup(cleanup);
136
- }, { flush: "post" });
137
- stops.push(stop);
138
- return stop;
181
+ const cleanup = run()
182
+ if (typeof cleanup === 'function') onCleanup(cleanup)
183
+ }, { flush: 'post' })
184
+ stops.push(stop)
185
+ return stop
139
186
  },
140
187
  on(event: string, listener: EventListener): () => void {
141
- const element = root.value;
142
- element?.addEventListener(event, listener);
143
- const off = (): void => element?.removeEventListener(event, listener);
144
- stops.push(off);
145
- return off;
188
+ const element = root.value
189
+ element?.addEventListener(event, listener)
190
+ const off = (): void => element?.removeEventListener(event, listener)
191
+ stops.push(off)
192
+ return off
146
193
  },
147
194
  dispatch,
148
- };
195
+ }
149
196
 
150
- let cleanup: void | (() => void);
151
- let ready: Promise<void> | undefined;
197
+ let cleanup: void | (() => void)
198
+ let ready: Promise<void> | undefined
152
199
  onMounted(() => {
153
- ready = Promise.resolve(controllerModule.default(host as never)).then((result) => { cleanup = result; });
154
- });
200
+ ready = Promise.resolve(controllerModule.default(host as never)).then((result) => {
201
+ cleanup = result
202
+ })
203
+ })
155
204
  onBeforeUnmount(() => {
156
- for (const stop of stops.splice(0)) stop();
157
- if (typeof cleanup === "function") cleanup();
158
- });
205
+ for (const stop of stops.splice(0)) stop()
206
+ if (typeof cleanup === 'function') cleanup()
207
+ })
159
208
  </script>
160
209
 
161
210
  <template>
162
- <div data-component="ui-tree-item" role="treeitem" v-bind="$attrs" :aria-label="hn.enumerated(props.label)" :aria-level="hn.enumerated(state_structuralLevel)" :aria-selected="hn.enumerated(props.selected)" :aria-disabled="hn.enumerated(props.disabled)" :data-item-id="hn.attr(props.itemId)" :data-drag-type="hn.attr(props.dragType)" :data-drop-scope="hn.attr(props.dropScope)" :data-accepts="hn.attr(props.accepts)" :data-drop-depth="hn.attr(props.dropDepth)" :data-subtree-depth="hn.attr(props.subtreeDepth)" :data-ui-tree-item-state="hostState || undefined" ref="root"><div class="row" :ref="(element) => { refs['row'] = element as Element }"><template v-if="hn.t((hn.t(props.sortable) && hn.t(!hn.t(props.disabled))))"><button class="drag-handle" type="button" draggable="true" :aria-label="hn.enumerated(hn.call('format', 'Drag %s to reorder', props.label))" :ref="(element) => { refs['dragHandle'] = element as Element }"><svg aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="12" r="1"></circle><circle cx="9" cy="5" r="1"></circle><circle cx="9" cy="19" r="1"></circle><circle cx="15" cy="12" r="1"></circle><circle cx="15" cy="5" r="1"></circle><circle cx="15" cy="19" r="1"></circle></svg></button></template><button class="disclosure" type="button" :disabled="hn.attr(props.disabled)" :hidden="hn.attr(!hn.t(state_isContainer))" :ref="(element) => { refs['disclosure'] = element as Element }"><span class="disclosure-icon" aria-hidden="true"></span></button><span class="leading"><slot name="leading"></slot></span><span class="label"><slot name="label">{{ hn.text(props.label) }}</slot></span><span class="actions"><slot name="actions"></slot></span><span class="drop-indicator row-drop-indicator" aria-hidden="true"></span></div><div class="children" role="group" :hidden="hn.attr(!hn.t((hn.t(state_isContainer) && hn.t(state_internalExpanded))))" :ref="(element) => { refs['children'] = element as Element }"><slot></slot></div><span class="drop-indicator subtree-drop-indicator" aria-hidden="true"></span></div>
211
+ <div
212
+ data-component="ui-tree-item"
213
+ role="treeitem"
214
+ v-bind="$attrs"
215
+ :aria-label="label"
216
+ :aria-level="structuralLevel"
217
+ :aria-selected="selected"
218
+ :aria-disabled="disabled"
219
+ :data-item-id="itemId"
220
+ :data-drag-type="dragType"
221
+ :data-drop-scope="dropScope"
222
+ :data-accepts="accepts"
223
+ :data-drop-depth="dropDepth"
224
+ :data-subtree-depth="subtreeDepth"
225
+ :data-ui-tree-item-state="hostState || undefined"
226
+ ref="root"
227
+ >
228
+ <div class="row" ref="row">
229
+ <button
230
+ v-if="sortable && !disabled"
231
+ class="drag-handle"
232
+ type="button"
233
+ draggable="true"
234
+ :aria-label="`Drag ${label} to reorder`"
235
+ ref="dragHandle"
236
+ >
237
+ <svg
238
+ aria-hidden="true"
239
+ focusable="false"
240
+ viewBox="0 0 24 24"
241
+ fill="none"
242
+ stroke="currentColor"
243
+ stroke-width="2"
244
+ stroke-linecap="round"
245
+ stroke-linejoin="round"
246
+ >
247
+ <circle cx="9" cy="12" r="1"></circle>
248
+ <circle cx="9" cy="5" r="1"></circle>
249
+ <circle cx="9" cy="19" r="1"></circle>
250
+ <circle cx="15" cy="12" r="1"></circle>
251
+ <circle cx="15" cy="5" r="1"></circle>
252
+ <circle cx="15" cy="19" r="1"></circle>
253
+ </svg>
254
+ </button>
255
+ <button
256
+ class="disclosure"
257
+ type="button"
258
+ :disabled="disabled"
259
+ :hidden="!isContainer"
260
+ ref="disclosure"
261
+ >
262
+ <span class="disclosure-icon" aria-hidden="true"></span>
263
+ </button>
264
+ <span class="leading"><slot name="leading" /></span>
265
+ <span class="label"><slot name="label">{{ label }}</slot></span>
266
+ <span class="actions"><slot name="actions" /></span>
267
+ <span class="drop-indicator row-drop-indicator" aria-hidden="true"></span>
268
+ </div>
269
+ <div class="children" role="group" :hidden="!(isContainer && internalExpanded)" ref="children">
270
+ <slot />
271
+ </div>
272
+ <span class="drop-indicator subtree-drop-indicator" aria-hidden="true"></span>
273
+ </div>
163
274
  </template>
164
275
 
165
276
  <style scoped>
166
277
  [data-component~="ui-tree-item"] {
167
- --_tree-row-min-height: 32px;
278
+ --_tree-row-min-height: 32px;
168
279
 
169
- position: relative;
170
- display: block;
171
- min-inline-size: 0;
172
- margin-inline-start: calc(var(--ui-tree-item-depth, 0) * var(--ui-tree-indent, 16px));
173
- }
280
+ position: relative;
281
+ display: block;
282
+ min-inline-size: 0;
283
+ margin-inline-start: calc(var(--ui-tree-item-depth, 0) * var(--ui-tree-indent, 16px));
284
+ }
174
285
 
175
- .row {
176
- position: relative;
177
- display: grid;
178
- grid-template-columns: 20px auto minmax(0, 1fr) auto;
179
- align-items: center;
180
- min-block-size: var(--ui-tree-row-min-height, var(--_tree-row-min-height));
181
- min-inline-size: 0;
182
- font-size: var(--ui-tree-font-size, var(--ui-font-size-ui, 0.9375rem));
183
- border-radius: var(--ui-radius-md);
184
- color: var(--ui-text-secondary);
185
- transition:
186
- background-color var(--ui-motion-fast) var(--ui-motion-ease),
187
- color var(--ui-motion-fast) var(--ui-motion-ease),
188
- opacity var(--ui-motion-fast) var(--ui-motion-ease),
189
- box-shadow var(--ui-motion-fast) var(--ui-motion-ease),
190
- min-block-size var(--ui-motion-base) var(--ui-motion-ease);
191
- }
286
+ .row {
287
+ position: relative;
288
+ display: grid;
289
+ grid-template-columns: 20px auto minmax(0, 1fr) auto;
290
+ align-items: center;
291
+ min-block-size: var(--ui-tree-item-min-block-size, var(--_tree-row-min-height));
292
+ min-inline-size: 0;
293
+ font-size: var(--ui-tree-item-font-size, var(--ui-font-size-ui));
294
+ border-radius: var(--ui-radius-md);
295
+ color: var(--ui-text-secondary);
296
+ transition:
297
+ background-color var(--ui-motion-fast) var(--ui-motion-ease),
298
+ color var(--ui-motion-fast) var(--ui-motion-ease),
299
+ opacity var(--ui-motion-fast) var(--ui-motion-ease),
300
+ box-shadow var(--ui-motion-fast) var(--ui-motion-ease),
301
+ min-block-size var(--ui-motion-base) var(--ui-motion-ease);
302
+ }
192
303
 
193
- .row:hover,
194
- [data-component~="ui-tree-item"][data-ui-tree-item-state~="selected"] .row,
195
- .row:focus-within {
196
- color: var(--ui-text-primary);
197
- background: var(--ui-surface-muted);
198
- }
304
+ .row:hover,
305
+ [data-component~="ui-tree-item"][data-ui-tree-item-state~="selected"] .row,
306
+ .row:focus-within {
307
+ color: var(--ui-text-primary);
308
+ background: var(--ui-surface-muted);
309
+ }
199
310
 
200
- [data-component~="ui-tree-item"][data-ui-tree-item-state~="disabled"] .row {
201
- opacity: 0.55;
202
- }
311
+ [data-component~="ui-tree-item"][data-ui-tree-item-state~="disabled"] .row {
312
+ opacity: 0.55;
313
+ }
203
314
 
204
- [data-component~="ui-tree-item"][data-dragging] .row {
205
- opacity: 0.42;
206
- }
315
+ [data-component~="ui-tree-item"][data-dragging] .row {
316
+ opacity: 0.42;
317
+ }
207
318
 
208
- [data-component~="ui-tree-item"]:focus {
209
- outline: none;
210
- }
319
+ [data-component~="ui-tree-item"]:focus {
320
+ outline: none;
321
+ }
211
322
 
212
- [data-component~="ui-tree-item"]:focus-visible .row {
213
- box-shadow: 0 0 0 2px var(--ui-focus-ring);
214
- }
323
+ [data-component~="ui-tree-item"]:focus-visible .row {
324
+ box-shadow: 0 0 0 2px var(--ui-focus-ring);
325
+ }
215
326
 
216
- [data-component~="ui-tree-item"][data-drop-position="inside"] .row {
217
- background: var(--ui-accent-subtle);
218
- box-shadow: inset 0 0 0 1px var(--ui-tree-drop-color, var(--ui-accent-solid));
219
- }
327
+ /* Child combinators: drop feedback belongs to this item's own row, not the rows nested in it. */
328
+ [data-component~="ui-tree-item"][data-drop-position="inside"] > .row {
329
+ background: var(--ui-accent-subtle);
330
+ box-shadow: inset 0 0 0 1px var(--ui-tree-drop-color, var(--ui-accent-solid));
331
+ }
220
332
 
221
- .disclosure,
222
- .disclosure-spacer {
223
- grid-column: 1;
224
- grid-row: 1;
225
- inline-size: 20px;
226
- block-size: 28px;
227
- }
333
+ .disclosure,
334
+ .disclosure-spacer {
335
+ grid-column: 1;
336
+ grid-row: 1;
337
+ inline-size: 20px;
338
+ block-size: 28px;
339
+ }
228
340
 
229
- .disclosure {
230
- display: grid;
231
- place-items: center;
232
- padding: 0;
233
- color: var(--ui-text-muted);
234
- background: transparent;
235
- border: 0;
236
- border-radius: var(--ui-radius-sm);
237
- cursor: pointer;
238
- }
341
+ .disclosure {
342
+ display: grid;
343
+ place-items: center;
344
+ padding: 0;
345
+ color: var(--ui-text-muted);
346
+ background: transparent;
347
+ border: 0;
348
+ border-radius: var(--ui-radius-sm);
349
+ cursor: pointer;
350
+ }
239
351
 
240
- .disclosure[hidden] {
241
- display: none;
242
- }
352
+ .disclosure[hidden] {
353
+ display: none;
354
+ }
243
355
 
244
- .disclosure:hover {
245
- color: var(--ui-text-primary);
246
- background: var(--ui-surface-elevated);
247
- }
356
+ .disclosure:hover {
357
+ color: var(--ui-text-primary);
358
+ background: var(--ui-surface-elevated);
359
+ }
248
360
 
249
- .disclosure-icon {
250
- display: grid;
251
- place-items: center;
252
- inline-size: 14px;
253
- block-size: 14px;
254
- transform: rotate(-90deg);
255
- transition: transform var(--ui-motion-fast) var(--ui-motion-ease);
256
- }
361
+ .disclosure-icon {
362
+ display: grid;
363
+ place-items: center;
364
+ inline-size: 14px;
365
+ block-size: 14px;
366
+ transform: rotate(-90deg);
367
+ transition: transform var(--ui-motion-fast) var(--ui-motion-ease);
368
+ }
257
369
 
258
- .disclosure-icon::before {
259
- display: block;
260
- inline-size: 0.45rem;
261
- block-size: 0.45rem;
262
- border-inline-end: 2px solid currentColor;
263
- border-block-end: 2px solid currentColor;
264
- content: "";
265
- /* A chevron's ink sits below its box centre; lift it so it centres optically (in either rotation). */
266
- transform: translateY(-20%) rotate(45deg);
267
- }
370
+ .disclosure-icon::before {
371
+ display: block;
372
+ inline-size: 0.45rem;
373
+ block-size: 0.45rem;
374
+ border-inline-end: 2px solid currentColor;
375
+ border-block-end: 2px solid currentColor;
376
+ content: "";
377
+ /* A chevron's ink sits below its box centre; lift it so it centres optically (in either rotation). */
378
+ transform: translateY(-20%) rotate(45deg);
379
+ }
268
380
 
269
- [data-component~="ui-tree-item"][aria-expanded="true"] .disclosure-icon {
270
- transform: rotate(0deg);
271
- }
381
+ [data-component~="ui-tree-item"][aria-expanded="true"] .disclosure-icon {
382
+ transform: rotate(0deg);
383
+ }
272
384
 
273
- /* Lucide grip-vertical, filling the handle as in Looma 0.2. */
274
- .drag-handle svg {
275
- display: block;
276
- inline-size: 100%;
277
- block-size: 100%;
278
- }
385
+ /* Lucide grip-vertical, filling the handle as in Looma 0.2. */
386
+ .drag-handle svg {
387
+ display: block;
388
+ inline-size: 100%;
389
+ block-size: 100%;
390
+ }
279
391
 
280
- /* Slotted content is outside this scope, so :has() cannot see it; hide only a truly empty slot. */
281
- .leading {
282
- display: inline-grid;
283
- place-items: center;
284
- grid-column: 2;
285
- grid-row: 1;
286
- inline-size: 1em;
287
- block-size: 1em;
288
- margin-inline-start: var(--ui-space-1);
289
- color: var(--ui-text-muted);
290
- }
392
+ /* Slotted content is outside this scope, so :has() cannot see it; hide only a truly empty slot. */
393
+ .leading {
394
+ display: inline-grid;
395
+ place-items: center;
396
+ grid-column: 2;
397
+ grid-row: 1;
398
+ inline-size: 1em;
399
+ block-size: 1em;
400
+ margin-inline-start: var(--ui-space-1);
401
+ color: var(--ui-text-muted);
402
+ }
291
403
 
292
- .leading:empty {
293
- display: none;
294
- }
404
+ .leading:empty {
405
+ display: none;
406
+ }
295
407
 
296
- /* Icons match the label's size (as in Knit's tree): an unsized SVG would otherwise render 300×150. */
297
- :slotted(svg),
298
- :slotted(img) {
299
- display: block;
300
- inline-size: 1em;
301
- block-size: 1em;
302
- flex: none;
303
- }
408
+ /* Icons match the label's size (as in Knit's tree): an unsized SVG would otherwise render 300×150. */
409
+ :slotted(svg),
410
+ :slotted(img) {
411
+ display: block;
412
+ inline-size: 1em;
413
+ block-size: 1em;
414
+ flex: none;
415
+ }
304
416
 
305
- .label {
306
- grid-column: 3;
307
- grid-row: 1;
308
- min-inline-size: 0;
309
- padding: var(--ui-space-1) var(--ui-space-2);
310
- }
417
+ /* The label spans the row's height and centres its content, so slotted content (a link) can
418
+ stretch to fill the row as its own hit area; the padding tokens remove the inset. */
419
+ .label {
420
+ grid-column: 3;
421
+ grid-row: 1;
422
+ align-self: stretch;
423
+ display: flex;
424
+ align-items: center;
425
+ min-inline-size: 0;
426
+ padding: var(--ui-tree-item-label-padding-block, var(--ui-space-1))
427
+ var(--ui-tree-item-label-padding-inline, var(--ui-space-2));
428
+ }
311
429
 
312
- .actions {
313
- grid-column: 4;
314
- grid-row: 1;
315
- display: inline-flex;
316
- align-items: center;
317
- min-inline-size: 0;
318
- }
430
+ /* Slotted label content fills the cell, so a link is the row's hit area rather than sizing to
431
+ its own text and reading as overflow. */
432
+ .label > :slotted(*) {
433
+ flex: 1 1 auto;
434
+ min-inline-size: 0;
435
+ }
319
436
 
320
- .drag-handle {
321
- position: absolute;
322
- z-index: 2;
323
- inset-block-start: 50%;
324
- inset-inline-start: -22px;
325
- inline-size: 20px;
326
- block-size: 32px;
327
- display: grid;
328
- place-items: center;
329
- padding: 7px 3px;
330
- color: var(--ui-text-muted);
331
- background: var(--ui-surface-elevated);
332
- border: 1px solid transparent;
333
- border-radius: var(--ui-radius-sm);
334
- cursor: grab;
335
- /* Hidden until the row is hovered, but always hittable: the handle sits outside the row, and moving
336
- the pointer onto it must not hide it on the way. */
337
- opacity: 0;
338
- transform: translateY(-50%);
339
- transition: opacity var(--ui-motion-fast) var(--ui-motion-ease);
340
- }
437
+ .actions {
438
+ grid-column: 4;
439
+ grid-row: 1;
440
+ display: inline-flex;
441
+ align-items: center;
442
+ min-inline-size: 0;
443
+ }
341
444
 
342
- .row:hover .drag-handle,
343
- .drag-handle:hover,
344
- .drag-handle:focus-visible,
345
- [data-component~="ui-tree-item"][data-dragging] .drag-handle {
346
- opacity: 1;
347
- }
445
+ .drag-handle {
446
+ position: absolute;
447
+ z-index: 2;
448
+ inset-block-start: 50%;
449
+ inset-inline-start: -22px;
450
+ inline-size: 20px;
451
+ block-size: 32px;
452
+ display: grid;
453
+ place-items: center;
454
+ padding: 7px 3px;
455
+ color: var(--ui-text-muted);
456
+ background: var(--ui-surface-elevated);
457
+ border: 1px solid transparent;
458
+ border-radius: var(--ui-radius-sm);
459
+ cursor: grab;
460
+ /* Hidden until the row is hovered, but always hittable: the handle sits outside the row, and moving
461
+ the pointer onto it must not hide it on the way. */
462
+ opacity: 0;
463
+ transform: translateY(-50%);
464
+ transition: opacity var(--ui-motion-fast) var(--ui-motion-ease);
465
+ }
348
466
 
349
- .drag-handle:active {
350
- cursor: grabbing;
351
- }
467
+ .row:hover .drag-handle,
468
+ .drag-handle:hover,
469
+ .drag-handle:focus-visible,
470
+ [data-component~="ui-tree-item"][data-dragging] > .row > .drag-handle {
471
+ opacity: 1;
472
+ }
352
473
 
353
- .drop-indicator {
354
- position: absolute;
355
- z-index: 3;
356
- inset-inline: 1px;
357
- display: none;
358
- block-size: 2px;
359
- border-radius: 999px;
360
- background: var(--ui-tree-drop-color, var(--ui-accent-solid));
361
- pointer-events: none;
362
- }
474
+ .drag-handle:active {
475
+ cursor: grabbing;
476
+ }
363
477
 
364
- .drop-indicator::before,
365
- .drop-indicator::after {
366
- content: "";
367
- position: absolute;
368
- inset-block-start: 50%;
369
- inline-size: 7px;
370
- block-size: 7px;
371
- border: 2px solid var(--ui-tree-drop-color, var(--ui-accent-solid));
372
- border-radius: 999px;
373
- background: var(--ui-surface-elevated);
374
- transform: translateY(-50%);
375
- }
478
+ .drop-indicator {
479
+ position: absolute;
480
+ z-index: 3;
481
+ inset-inline: 1px;
482
+ display: none;
483
+ block-size: 2px;
484
+ border-radius: 999px;
485
+ background: var(--ui-tree-drop-color, var(--ui-accent-solid));
486
+ pointer-events: none;
487
+ }
376
488
 
377
- .drop-indicator::before {
378
- inset-inline-start: -1px;
379
- }
380
- .drop-indicator::after {
381
- inset-inline-end: -1px;
382
- }
489
+ .drop-indicator::before,
490
+ .drop-indicator::after {
491
+ content: "";
492
+ position: absolute;
493
+ inset-block-start: 50%;
494
+ inline-size: 7px;
495
+ block-size: 7px;
496
+ border: 2px solid var(--ui-tree-drop-color, var(--ui-accent-solid));
497
+ border-radius: 999px;
498
+ background: var(--ui-surface-elevated);
499
+ transform: translateY(-50%);
500
+ }
383
501
 
384
- [data-component~="ui-tree-item"][data-drop-position="before"] .row-drop-indicator {
385
- display: block;
386
- inset-block-start: -1px;
387
- }
502
+ .drop-indicator::before {
503
+ inset-inline-start: -1px;
504
+ }
505
+ .drop-indicator::after {
506
+ inset-inline-end: -1px;
507
+ }
388
508
 
389
- [data-component~="ui-tree-item"][data-drop-position="after"] .subtree-drop-indicator {
390
- display: block;
391
- inset-block-end: -1px;
392
- }
509
+ [data-component~="ui-tree-item"][data-drop-position="before"] > .row > .row-drop-indicator {
510
+ display: block;
511
+ inset-block-start: -1px;
512
+ }
393
513
 
394
- .children[hidden] {
395
- display: none;
396
- }
514
+ [data-component~="ui-tree-item"][data-drop-position="after"] > .subtree-drop-indicator {
515
+ display: block;
516
+ inset-block-end: -1px;
517
+ }
397
518
 
398
- html[data-ui-input-modality="touch"] [data-component~="ui-tree-item"] {
399
- --_tree-row-min-height: 44px;
400
- }
519
+ .children[hidden] {
520
+ display: none;
521
+ }
401
522
 
402
- html[data-ui-input-modality="touch"] [data-component~="ui-tree-item"] .drag-handle {
403
- display: none;
404
- }
523
+ html[data-ui-input-modality="touch"] [data-component~="ui-tree-item"] {
524
+ --_tree-row-min-height: 44px;
525
+ }
405
526
 
406
- @media (prefers-reduced-motion: reduce) {
407
- .row,
408
- .disclosure-icon,
409
- .drag-handle {
410
- transition: none;
411
- }
412
- }
527
+ html[data-ui-input-modality="touch"] [data-component~="ui-tree-item"] .drag-handle {
528
+ display: none;
529
+ }
413
530
 
414
- /* A component that sets its display still honours the hidden attribute on its root. */
415
- [data-component~="ui-tree-item"][hidden] {
416
- display: none;
417
- }
531
+ @media (prefers-reduced-motion: reduce) {
532
+ .row,
533
+ .disclosure-icon,
534
+ .drag-handle {
535
+ transition: none;
536
+ }
537
+ }
538
+
539
+ /* A component that sets its display still honours the hidden attribute on its root. */
540
+ [data-component~="ui-tree-item"][hidden] {
541
+ display: none;
542
+ }
418
543
  </style>