@threadlabs/looma 0.3.1 → 0.5.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 (494) hide show
  1. package/README.md +43 -12
  2. package/components/shared/editor.js +69 -0
  3. package/components/shared/input-modality.js +20 -0
  4. package/components/shared/overlay.js +281 -0
  5. package/components/shared/trigger.js +17 -0
  6. package/components/ui-affordance-scope/ui-affordance-scope.html +22 -0
  7. package/components/ui-affordance-scope/ui-affordance-scope.js +76 -0
  8. package/components/ui-avatar/ui-avatar.html +59 -0
  9. package/components/ui-avatar/ui-avatar.js +47 -0
  10. package/components/ui-avatar-group/ui-avatar-group.html +62 -0
  11. package/components/ui-avatar-group/ui-avatar-group.js +27 -0
  12. package/components/ui-badge/ui-badge.html +94 -0
  13. package/components/ui-button/ui-button.html +198 -0
  14. package/components/ui-callout/ui-callout.html +104 -0
  15. package/components/ui-checkbox/ui-checkbox.html +107 -0
  16. package/components/ui-checkbox/ui-checkbox.js +24 -0
  17. package/components/ui-cluster/ui-cluster.html +49 -0
  18. package/components/ui-combobox/ui-combobox.html +377 -0
  19. package/components/ui-combobox/ui-combobox.js +432 -0
  20. package/components/ui-container/ui-container.html +39 -0
  21. package/components/ui-context-menu/ui-context-menu.html +75 -0
  22. package/components/ui-context-menu/ui-context-menu.js +143 -0
  23. package/components/ui-dialog/ui-dialog.html +178 -0
  24. package/components/ui-dialog/ui-dialog.js +105 -0
  25. package/components/ui-disclosure/ui-disclosure.html +109 -0
  26. package/components/ui-disclosure/ui-disclosure.js +28 -0
  27. package/components/ui-editable/ui-editable.html +163 -0
  28. package/components/ui-editable/ui-editable.js +113 -0
  29. package/components/ui-editor-insert-table-grid/ui-editor-insert-table-grid.html +97 -0
  30. package/components/ui-editor-insert-table-grid/ui-editor-insert-table-grid.js +71 -0
  31. package/components/ui-editor-mention-menu/ui-editor-mention-menu.html +157 -0
  32. package/components/ui-editor-mention-menu/ui-editor-mention-menu.js +50 -0
  33. package/components/ui-editor-slash-menu/ui-editor-slash-menu.html +197 -0
  34. package/components/ui-editor-slash-menu/ui-editor-slash-menu.js +44 -0
  35. package/components/ui-editor-table-context-menu/ui-editor-table-context-menu.html +164 -0
  36. package/components/ui-editor-table-context-menu/ui-editor-table-context-menu.js +75 -0
  37. package/components/ui-editor-table-overlay/ui-editor-table-overlay.html +422 -0
  38. package/components/ui-editor-table-overlay/ui-editor-table-overlay.js +119 -0
  39. package/components/ui-editor-table-toolbar/ui-editor-table-toolbar.html +271 -0
  40. package/components/ui-editor-table-toolbar/ui-editor-table-toolbar.js +54 -0
  41. package/components/ui-editor-toolbar/ui-editor-toolbar.html +110 -0
  42. package/components/ui-floating-action-button/ui-floating-action-button.html +83 -0
  43. package/components/ui-form-field/ui-form-field.html +45 -0
  44. package/components/ui-form-field/ui-form-field.js +71 -0
  45. package/components/ui-grid/ui-grid.html +46 -0
  46. package/components/ui-icon/ui-icon.html +34 -0
  47. package/components/ui-icon/ui-icon.js +8 -0
  48. package/components/ui-icon-button/ui-icon-button.html +167 -0
  49. package/components/ui-icon-button/ui-icon-button.js +7 -0
  50. package/components/ui-input/ui-input.html +81 -0
  51. package/components/ui-input/ui-input.js +7 -0
  52. package/components/ui-menu/ui-menu.html +87 -0
  53. package/components/ui-menu/ui-menu.js +114 -0
  54. package/components/ui-menu-item/ui-menu-item.html +37 -0
  55. package/components/ui-popover/ui-popover.html +85 -0
  56. package/components/ui-popover/ui-popover.js +70 -0
  57. package/components/ui-radio/ui-radio.html +57 -0
  58. package/components/ui-radio/ui-radio.js +25 -0
  59. package/components/ui-radio-group/ui-radio-group.html +51 -0
  60. package/components/ui-radio-group/ui-radio-group.js +42 -0
  61. package/components/ui-reel/ui-reel.html +67 -0
  62. package/components/ui-search-result-row/ui-search-result-row.html +101 -0
  63. package/components/ui-search-shell/ui-search-shell.html +142 -0
  64. package/components/ui-search-shell/ui-search-shell.js +60 -0
  65. package/components/ui-select/ui-select.html +87 -0
  66. package/components/ui-select/ui-select.js +16 -0
  67. package/components/ui-separator/ui-separator.html +22 -0
  68. package/components/ui-sidebar/ui-sidebar.html +183 -0
  69. package/components/ui-sidebar/ui-sidebar.js +178 -0
  70. package/components/ui-stack/ui-stack.html +59 -0
  71. package/components/ui-switch/ui-switch.html +95 -0
  72. package/components/ui-switch/ui-switch.js +24 -0
  73. package/components/ui-switcher/ui-switcher.html +65 -0
  74. package/components/ui-tabs/ui-tabs.html +134 -0
  75. package/components/ui-tabs/ui-tabs.js +81 -0
  76. package/components/ui-textarea/ui-textarea.html +80 -0
  77. package/components/ui-textarea/ui-textarea.js +7 -0
  78. package/components/ui-toast-region/ui-toast-region.html +127 -0
  79. package/components/ui-toast-region/ui-toast-region.js +119 -0
  80. package/components/ui-tooltip/ui-tooltip.html +122 -0
  81. package/components/ui-tooltip/ui-tooltip.js +134 -0
  82. package/components/ui-top-bar/ui-top-bar.html +53 -0
  83. package/components/ui-tree/ui-tree.html +29 -0
  84. package/components/ui-tree/ui-tree.js +250 -0
  85. package/components/ui-tree-item/ui-tree-item.html +326 -0
  86. package/components/ui-tree-item/ui-tree-item.js +94 -0
  87. package/dist/index.d.ts +4 -414
  88. package/dist/index.js +55 -657
  89. package/editor/chunks/extensions.js +7267 -0
  90. package/editor/chunks/table-backgrounds.js +51 -0
  91. package/editor/chunks/ui.js +92 -0
  92. package/editor/extensions/callout.d.ts +20 -0
  93. package/editor/extensions/index.d.ts +12 -441
  94. package/editor/extensions/index.js +3 -64
  95. package/editor/extensions/list-behavior.d.ts +7 -0
  96. package/editor/extensions/mention.d.ts +35 -0
  97. package/editor/extensions/preset.d.ts +42 -0
  98. package/editor/extensions/slash-command.d.ts +80 -0
  99. package/editor/extensions/smart-paste.d.ts +13 -0
  100. package/editor/extensions/table-commands.d.ts +114 -0
  101. package/editor/extensions/table-formatting.d.ts +60 -0
  102. package/editor/icons.d.ts +55 -0
  103. package/editor/index.d.ts +8 -6
  104. package/editor/index.js +96 -71
  105. package/editor/insert-table-grid.d.ts +7 -0
  106. package/editor/mention-contract.d.ts +78 -0
  107. package/editor/mention-menu.d.ts +9 -0
  108. package/editor/slash-menu.d.ts +29 -0
  109. package/editor/table-backgrounds.d.ts +43 -0
  110. package/editor/table-context-menu.d.ts +12 -0
  111. package/editor/table-overlay.d.ts +79 -0
  112. package/editor/ui.d.ts +10 -50
  113. package/editor/ui.js +3 -14
  114. package/editor/viewport.d.ts +15 -0
  115. package/package.json +84 -80
  116. package/styles/ui-affordance-scope.css +19 -0
  117. package/styles/ui-avatar-group.css +56 -0
  118. package/styles/ui-avatar.css +47 -0
  119. package/styles/ui-badge.css +91 -0
  120. package/styles/ui-button.css +206 -0
  121. package/styles/ui-callout.css +76 -0
  122. package/styles/ui-checkbox.css +98 -0
  123. package/styles/ui-cluster.css +46 -0
  124. package/styles/ui-combobox.css +213 -0
  125. package/styles/ui-container.css +36 -0
  126. package/styles/ui-context-menu.css +68 -0
  127. package/styles/ui-dialog.css +160 -0
  128. package/styles/ui-disclosure.css +95 -0
  129. package/styles/ui-editable.css +139 -0
  130. package/styles/ui-editor-insert-table-grid.css +65 -0
  131. package/styles/ui-editor-mention-menu.css +121 -0
  132. package/styles/ui-editor-slash-menu.css +179 -0
  133. package/styles/ui-editor-table-context-menu.css +118 -0
  134. package/styles/ui-editor-table-overlay.css +318 -0
  135. package/styles/ui-editor-table-toolbar.css +191 -0
  136. package/styles/ui-editor-toolbar.css +115 -0
  137. package/styles/ui-floating-action-button.css +82 -0
  138. package/styles/ui-form-field.css +36 -0
  139. package/styles/ui-grid.css +43 -0
  140. package/styles/ui-icon-button.css +169 -0
  141. package/styles/ui-icon.css +23 -0
  142. package/styles/ui-input.css +82 -0
  143. package/styles/ui-menu-item.css +34 -0
  144. package/styles/ui-menu.css +80 -0
  145. package/styles/ui-popover.css +76 -0
  146. package/styles/ui-radio-group.css +38 -0
  147. package/styles/ui-radio.css +46 -0
  148. package/styles/ui-reel.css +63 -0
  149. package/styles/ui-search-result-row.css +92 -0
  150. package/styles/ui-search-shell.css +128 -0
  151. package/styles/ui-select.css +86 -0
  152. package/styles/ui-separator.css +16 -0
  153. package/styles/ui-sidebar.css +166 -0
  154. package/styles/ui-stack.css +55 -0
  155. package/styles/ui-switch.css +88 -0
  156. package/styles/ui-switcher.css +61 -0
  157. package/styles/ui-tabs.css +114 -0
  158. package/styles/ui-textarea.css +77 -0
  159. package/styles/ui-toast-region.css +103 -0
  160. package/styles/ui-tooltip.css +113 -0
  161. package/styles/ui-top-bar.css +51 -0
  162. package/styles/ui-tree-item.css +267 -0
  163. package/styles/ui-tree.css +16 -0
  164. package/vanilla/UiAffordanceScope.d.ts +14 -0
  165. package/vanilla/UiAffordanceScope.js +30 -0
  166. package/vanilla/UiAvatar.d.ts +17 -0
  167. package/vanilla/UiAvatar.js +41 -0
  168. package/vanilla/UiAvatarGroup.d.ts +15 -0
  169. package/vanilla/UiAvatarGroup.js +40 -0
  170. package/vanilla/UiBadge.d.ts +15 -0
  171. package/vanilla/UiBadge.js +30 -0
  172. package/vanilla/UiButton.d.ts +16 -0
  173. package/vanilla/UiButton.js +32 -0
  174. package/vanilla/UiCallout.d.ts +14 -0
  175. package/vanilla/UiCallout.js +124 -0
  176. package/vanilla/UiCheckbox.d.ts +19 -0
  177. package/vanilla/UiCheckbox.js +47 -0
  178. package/vanilla/UiCluster.d.ts +15 -0
  179. package/vanilla/UiCluster.js +30 -0
  180. package/vanilla/UiCombobox.d.ts +42 -0
  181. package/vanilla/UiCombobox.js +194 -0
  182. package/vanilla/UiContainer.d.ts +15 -0
  183. package/vanilla/UiContainer.js +30 -0
  184. package/vanilla/UiContextMenu.d.ts +18 -0
  185. package/vanilla/UiContextMenu.js +35 -0
  186. package/vanilla/UiDialog.d.ts +19 -0
  187. package/vanilla/UiDialog.js +53 -0
  188. package/vanilla/UiDisclosure.d.ts +18 -0
  189. package/vanilla/UiDisclosure.js +49 -0
  190. package/vanilla/UiEditable.d.ts +22 -0
  191. package/vanilla/UiEditable.js +59 -0
  192. package/vanilla/UiEditorInsertTableGrid.d.ts +18 -0
  193. package/vanilla/UiEditorInsertTableGrid.js +46 -0
  194. package/vanilla/UiEditorMentionMenu.d.ts +21 -0
  195. package/vanilla/UiEditorMentionMenu.js +63 -0
  196. package/vanilla/UiEditorSlashMenu.d.ts +20 -0
  197. package/vanilla/UiEditorSlashMenu.js +74 -0
  198. package/vanilla/UiEditorTableContextMenu.d.ts +17 -0
  199. package/vanilla/UiEditorTableContextMenu.js +69 -0
  200. package/vanilla/UiEditorTableOverlay.d.ts +16 -0
  201. package/vanilla/UiEditorTableOverlay.js +113 -0
  202. package/vanilla/UiEditorTableToolbar.d.ts +18 -0
  203. package/vanilla/UiEditorTableToolbar.js +121 -0
  204. package/vanilla/UiEditorToolbar.d.ts +14 -0
  205. package/vanilla/UiEditorToolbar.js +30 -0
  206. package/vanilla/UiFloatingActionButton.d.ts +16 -0
  207. package/vanilla/UiFloatingActionButton.js +35 -0
  208. package/vanilla/UiFormField.d.ts +16 -0
  209. package/vanilla/UiFormField.js +54 -0
  210. package/vanilla/UiGrid.d.ts +15 -0
  211. package/vanilla/UiGrid.js +30 -0
  212. package/vanilla/UiIcon.d.ts +14 -0
  213. package/vanilla/UiIcon.js +40 -0
  214. package/vanilla/UiIconButton.d.ts +19 -0
  215. package/vanilla/UiIconButton.js +37 -0
  216. package/vanilla/UiInput.d.ts +18 -0
  217. package/vanilla/UiInput.js +37 -0
  218. package/vanilla/UiMenu.d.ts +19 -0
  219. package/vanilla/UiMenu.js +35 -0
  220. package/vanilla/UiMenuItem.d.ts +15 -0
  221. package/vanilla/UiMenuItem.js +43 -0
  222. package/vanilla/UiPopover.d.ts +18 -0
  223. package/vanilla/UiPopover.js +33 -0
  224. package/vanilla/UiRadio.d.ts +19 -0
  225. package/vanilla/UiRadio.js +48 -0
  226. package/vanilla/UiRadioGroup.d.ts +21 -0
  227. package/vanilla/UiRadioGroup.js +35 -0
  228. package/vanilla/UiReel.d.ts +16 -0
  229. package/vanilla/UiReel.js +34 -0
  230. package/vanilla/UiSearchResultRow.d.ts +15 -0
  231. package/vanilla/UiSearchResultRow.js +82 -0
  232. package/vanilla/UiSearchShell.d.ts +18 -0
  233. package/vanilla/UiSearchShell.js +74 -0
  234. package/vanilla/UiSelect.d.ts +18 -0
  235. package/vanilla/UiSelect.js +42 -0
  236. package/vanilla/UiSeparator.d.ts +14 -0
  237. package/vanilla/UiSeparator.js +28 -0
  238. package/vanilla/UiSidebar.d.ts +24 -0
  239. package/vanilla/UiSidebar.js +47 -0
  240. package/vanilla/UiStack.d.ts +16 -0
  241. package/vanilla/UiStack.js +32 -0
  242. package/vanilla/UiSwitch.d.ts +18 -0
  243. package/vanilla/UiSwitch.js +46 -0
  244. package/vanilla/UiSwitcher.d.ts +16 -0
  245. package/vanilla/UiSwitcher.js +32 -0
  246. package/vanilla/UiTabs.d.ts +18 -0
  247. package/vanilla/UiTabs.js +47 -0
  248. package/vanilla/UiTextarea.d.ts +19 -0
  249. package/vanilla/UiTextarea.js +40 -0
  250. package/vanilla/UiToastRegion.d.ts +20 -0
  251. package/vanilla/UiToastRegion.js +58 -0
  252. package/vanilla/UiTooltip.d.ts +21 -0
  253. package/vanilla/UiTooltip.js +34 -0
  254. package/vanilla/UiTopBar.d.ts +13 -0
  255. package/vanilla/UiTopBar.js +59 -0
  256. package/vanilla/UiTree.d.ts +18 -0
  257. package/vanilla/UiTree.js +31 -0
  258. package/vanilla/UiTreeItem.d.ts +26 -0
  259. package/vanilla/UiTreeItem.js +164 -0
  260. package/vanilla/index.js +48 -0
  261. package/vue/UiAffordanceScope.d.ts +18 -0
  262. package/vue/UiAffordanceScope.js +2 -0
  263. package/vue/UiAffordanceScope.vue +118 -0
  264. package/vue/UiAvatar.d.ts +23 -0
  265. package/vue/UiAvatar.js +2 -0
  266. package/vue/UiAvatar.vue +152 -0
  267. package/vue/UiAvatarGroup.d.ts +20 -0
  268. package/vue/UiAvatarGroup.js +2 -0
  269. package/vue/UiAvatarGroup.vue +167 -0
  270. package/vue/UiBadge.d.ts +20 -0
  271. package/vue/UiBadge.js +2 -0
  272. package/vue/UiBadge.vue +123 -0
  273. package/vue/UiButton.d.ts +22 -0
  274. package/vue/UiButton.js +2 -0
  275. package/vue/UiButton.vue +229 -0
  276. package/vue/UiCallout.d.ts +18 -0
  277. package/vue/UiCallout.js +2 -0
  278. package/vue/UiCallout.vue +104 -0
  279. package/vue/UiCheckbox.d.ts +38 -0
  280. package/vue/UiCheckbox.js +2 -0
  281. package/vue/UiCheckbox.vue +216 -0
  282. package/vue/UiCluster.d.ts +17 -0
  283. package/vue/UiCluster.js +2 -0
  284. package/vue/UiCluster.vue +75 -0
  285. package/vue/UiCombobox.d.ts +255 -0
  286. package/vue/UiCombobox.js +2 -0
  287. package/vue/UiCombobox.vue +466 -0
  288. package/vue/UiContainer.d.ts +17 -0
  289. package/vue/UiContainer.js +2 -0
  290. package/vue/UiContainer.vue +65 -0
  291. package/vue/UiContextMenu.d.ts +50 -0
  292. package/vue/UiContextMenu.js +2 -0
  293. package/vue/UiContextMenu.vue +166 -0
  294. package/vue/UiDialog.d.ts +39 -0
  295. package/vue/UiDialog.js +2 -0
  296. package/vue/UiDialog.vue +273 -0
  297. package/vue/UiDisclosure.d.ts +44 -0
  298. package/vue/UiDisclosure.js +2 -0
  299. package/vue/UiDisclosure.vue +211 -0
  300. package/vue/UiEditable.d.ts +48 -0
  301. package/vue/UiEditable.js +2 -0
  302. package/vue/UiEditable.vue +267 -0
  303. package/vue/UiEditorInsertTableGrid.d.ts +26 -0
  304. package/vue/UiEditorInsertTableGrid.js +2 -0
  305. package/vue/UiEditorInsertTableGrid.vue +210 -0
  306. package/vue/UiEditorMentionMenu.d.ts +44 -0
  307. package/vue/UiEditorMentionMenu.js +2 -0
  308. package/vue/UiEditorMentionMenu.vue +284 -0
  309. package/vue/UiEditorSlashMenu.d.ts +41 -0
  310. package/vue/UiEditorSlashMenu.js +2 -0
  311. package/vue/UiEditorSlashMenu.vue +306 -0
  312. package/vue/UiEditorTableContextMenu.d.ts +19 -0
  313. package/vue/UiEditorTableContextMenu.js +2 -0
  314. package/vue/UiEditorTableContextMenu.vue +252 -0
  315. package/vue/UiEditorTableOverlay.d.ts +66 -0
  316. package/vue/UiEditorTableOverlay.js +2 -0
  317. package/vue/UiEditorTableOverlay.vue +455 -0
  318. package/vue/UiEditorTableToolbar.d.ts +21 -0
  319. package/vue/UiEditorTableToolbar.js +2 -0
  320. package/vue/UiEditorTableToolbar.vue +339 -0
  321. package/vue/UiEditorToolbar.d.ts +18 -0
  322. package/vue/UiEditorToolbar.js +2 -0
  323. package/vue/UiEditorToolbar.vue +138 -0
  324. package/vue/UiFloatingActionButton.d.ts +22 -0
  325. package/vue/UiFloatingActionButton.js +2 -0
  326. package/vue/UiFloatingActionButton.vue +116 -0
  327. package/vue/UiFormField.d.ts +28 -0
  328. package/vue/UiFormField.js +2 -0
  329. package/vue/UiFormField.vue +124 -0
  330. package/vue/UiGrid.d.ts +17 -0
  331. package/vue/UiGrid.js +2 -0
  332. package/vue/UiGrid.vue +72 -0
  333. package/vue/UiIcon.d.ts +8 -0
  334. package/vue/UiIcon.js +2 -0
  335. package/vue/UiIcon.vue +135 -0
  336. package/vue/UiIconButton.d.ts +27 -0
  337. package/vue/UiIconButton.js +2 -0
  338. package/vue/UiIconButton.vue +268 -0
  339. package/vue/UiInput.d.ts +20 -0
  340. package/vue/UiInput.js +2 -0
  341. package/vue/UiInput.vue +177 -0
  342. package/vue/UiMenu.d.ts +52 -0
  343. package/vue/UiMenu.js +2 -0
  344. package/vue/UiMenu.vue +194 -0
  345. package/vue/UiMenuItem.d.ts +20 -0
  346. package/vue/UiMenuItem.js +2 -0
  347. package/vue/UiMenuItem.vue +55 -0
  348. package/vue/UiPopover.d.ts +44 -0
  349. package/vue/UiPopover.js +2 -0
  350. package/vue/UiPopover.vue +188 -0
  351. package/vue/UiRadio.d.ts +38 -0
  352. package/vue/UiRadio.js +2 -0
  353. package/vue/UiRadio.vue +166 -0
  354. package/vue/UiRadioGroup.d.ts +50 -0
  355. package/vue/UiRadioGroup.js +2 -0
  356. package/vue/UiRadioGroup.vue +161 -0
  357. package/vue/UiReel.d.ts +18 -0
  358. package/vue/UiReel.js +2 -0
  359. package/vue/UiReel.vue +93 -0
  360. package/vue/UiSearchResultRow.d.ts +28 -0
  361. package/vue/UiSearchResultRow.js +2 -0
  362. package/vue/UiSearchResultRow.vue +124 -0
  363. package/vue/UiSearchShell.d.ts +42 -0
  364. package/vue/UiSearchShell.js +2 -0
  365. package/vue/UiSearchShell.vue +233 -0
  366. package/vue/UiSelect.d.ts +30 -0
  367. package/vue/UiSelect.js +2 -0
  368. package/vue/UiSelect.vue +183 -0
  369. package/vue/UiSeparator.d.ts +8 -0
  370. package/vue/UiSeparator.js +2 -0
  371. package/vue/UiSeparator.vue +38 -0
  372. package/vue/UiSidebar.d.ts +54 -0
  373. package/vue/UiSidebar.js +2 -0
  374. package/vue/UiSidebar.vue +288 -0
  375. package/vue/UiStack.d.ts +18 -0
  376. package/vue/UiStack.js +2 -0
  377. package/vue/UiStack.vue +85 -0
  378. package/vue/UiSwitch.d.ts +36 -0
  379. package/vue/UiSwitch.js +2 -0
  380. package/vue/UiSwitch.vue +203 -0
  381. package/vue/UiSwitcher.d.ts +18 -0
  382. package/vue/UiSwitcher.js +2 -0
  383. package/vue/UiSwitcher.vue +91 -0
  384. package/vue/UiTabs.d.ts +36 -0
  385. package/vue/UiTabs.js +2 -0
  386. package/vue/UiTabs.vue +269 -0
  387. package/vue/UiTextarea.d.ts +22 -0
  388. package/vue/UiTextarea.js +2 -0
  389. package/vue/UiTextarea.vue +177 -0
  390. package/vue/UiToastRegion.d.ts +48 -0
  391. package/vue/UiToastRegion.js +2 -0
  392. package/vue/UiToastRegion.vue +256 -0
  393. package/vue/UiTooltip.d.ts +50 -0
  394. package/vue/UiTooltip.js +2 -0
  395. package/vue/UiTooltip.vue +230 -0
  396. package/vue/UiTopBar.d.ts +19 -0
  397. package/vue/UiTopBar.js +2 -0
  398. package/vue/UiTopBar.vue +56 -0
  399. package/vue/UiTree.d.ts +62 -0
  400. package/vue/UiTree.js +2 -0
  401. package/vue/UiTree.vue +117 -0
  402. package/vue/UiTreeItem.d.ts +56 -0
  403. package/vue/UiTreeItem.js +2 -0
  404. package/vue/UiTreeItem.vue +418 -0
  405. package/vue/chunks/UiAffordanceScope.js +112 -0
  406. package/vue/chunks/UiAvatar.js +144 -0
  407. package/vue/chunks/UiAvatarGroup.js +142 -0
  408. package/vue/chunks/UiBadge.js +33 -0
  409. package/vue/chunks/UiButton.js +47 -0
  410. package/vue/chunks/UiCallout.js +117 -0
  411. package/vue/chunks/UiCheckbox.js +167 -0
  412. package/vue/chunks/UiCluster.js +33 -0
  413. package/vue/chunks/UiCombobox.js +684 -0
  414. package/vue/chunks/UiContainer.js +33 -0
  415. package/vue/chunks/UiContextMenu.js +153 -0
  416. package/vue/chunks/UiDialog.js +162 -0
  417. package/vue/chunks/UiDisclosure.js +185 -0
  418. package/vue/chunks/UiEditable.js +223 -0
  419. package/vue/chunks/UiEditorInsertTableGrid.js +221 -0
  420. package/vue/chunks/UiEditorMentionMenu.js +245 -0
  421. package/vue/chunks/UiEditorSlashMenu.js +210 -0
  422. package/vue/chunks/UiEditorTableContextMenu.js +201 -0
  423. package/vue/chunks/UiEditorTableOverlay.js +301 -0
  424. package/vue/chunks/UiEditorTableToolbar.js +299 -0
  425. package/vue/chunks/UiEditorToolbar.js +37 -0
  426. package/vue/chunks/UiFloatingActionButton.js +56 -0
  427. package/vue/chunks/UiFormField.js +113 -0
  428. package/vue/chunks/UiGrid.js +33 -0
  429. package/vue/chunks/UiIcon.js +170 -0
  430. package/vue/chunks/UiIconButton.js +141 -0
  431. package/vue/chunks/UiInput.js +136 -0
  432. package/vue/chunks/UiMenu.js +163 -0
  433. package/vue/chunks/UiMenuItem.js +37 -0
  434. package/vue/chunks/UiPopover.js +149 -0
  435. package/vue/chunks/UiRadio.js +164 -0
  436. package/vue/chunks/UiRadioGroup.js +160 -0
  437. package/vue/chunks/UiReel.js +42 -0
  438. package/vue/chunks/UiSearchResultRow.js +63 -0
  439. package/vue/chunks/UiSearchShell.js +152 -0
  440. package/vue/chunks/UiSelect.js +134 -0
  441. package/vue/chunks/UiSeparator.js +24 -0
  442. package/vue/chunks/UiSidebar.js +180 -0
  443. package/vue/chunks/UiStack.js +38 -0
  444. package/vue/chunks/UiSwitch.js +162 -0
  445. package/vue/chunks/UiSwitcher.js +38 -0
  446. package/vue/chunks/UiTabs.js +210 -0
  447. package/vue/chunks/UiTextarea.js +139 -0
  448. package/vue/chunks/UiToastRegion.js +227 -0
  449. package/vue/chunks/UiTooltip.js +163 -0
  450. package/vue/chunks/UiTopBar.js +25 -0
  451. package/vue/chunks/UiTree.js +140 -0
  452. package/vue/chunks/UiTreeItem.js +315 -0
  453. package/vue/chunks/_plugin-vue_export-helper.js +8 -0
  454. package/vue/components.css +4405 -0
  455. package/vue/editor/LoomaEditor.d.ts +171 -0
  456. package/vue/editor/image-delivery.d.ts +61 -0
  457. package/vue/editor/index.d.ts +4 -386
  458. package/vue/editor/index.js +995 -1134
  459. package/vue/editor/primitives.d.ts +23 -0
  460. package/vue/index.d.ts +48 -604
  461. package/vue/index.js +49 -86
  462. package/dist/chunk-MLKGABMK.js +0 -9
  463. package/dist/chunk-RWLYVQY6.js +0 -5719
  464. package/dist/chunk-XDNZIKNI.js +0 -2721
  465. package/dist/declarative-generated.cjs +0 -149
  466. package/dist/declarative-generated.d.cts +0 -16
  467. package/dist/declarative-generated.d.ts +0 -16
  468. package/dist/declarative-generated.js +0 -124
  469. package/dist/declarative.cjs +0 -2750
  470. package/dist/declarative.d.cts +0 -101
  471. package/dist/declarative.d.ts +0 -101
  472. package/dist/declarative.js +0 -15
  473. package/dist/index.cjs +0 -9077
  474. package/dist/index.d.cts +0 -414
  475. package/dist/loader.cjs +0 -8442
  476. package/dist/loader.d.cts +0 -4
  477. package/dist/loader.d.ts +0 -4
  478. package/dist/loader.js +0 -16
  479. package/editor/chunk-EUIH3TBV.js +0 -6826
  480. package/editor/chunk-JK53ENSG.js +0 -29
  481. package/editor/chunk-U36XNFJN.js +0 -2592
  482. package/editor/table-overlay-DZFGHQDt.d.ts +0 -138
  483. package/editor.css +0 -1592
  484. package/layout/index.cjs +0 -356
  485. package/layout/index.d.cts +0 -18
  486. package/layout/index.d.ts +0 -18
  487. package/layout/index.js +0 -341
  488. package/layout.css +0 -191
  489. package/react/index.d.ts +0 -1276
  490. package/react/index.js +0 -1526
  491. package/styles.css +0 -940
  492. package/svelte/index.d.ts +0 -204
  493. package/svelte/index.js +0 -1892
  494. package/vue/chunk-OX3EACZ7.js +0 -4369
package/README.md CHANGED
@@ -1,23 +1,54 @@
1
1
  # @threadlabs/looma
2
2
 
3
- The public Looma package. Install it once, then import only the framework and
4
- feature subpaths your application uses.
3
+ Looma's components, written once as [Declarative HTML Components](https://nextwebwg.org/html-next/)
4
+ and shipped three ways: as HTML, as Vue components, and as plain DOM factories.
5
5
 
6
6
  ```sh
7
7
  pnpm add @threadlabs/looma
8
8
  ```
9
9
 
10
+ ## Vue
11
+
12
+ ```ts
13
+ import "@threadlabs/looma/tokens.css";
14
+ import "@threadlabs/looma/vue.css";
15
+ import { Button, TopBar } from "@threadlabs/looma/vue";
16
+ ```
17
+
18
+ The Vue components are ordinary Vue 3.5 single-file components, compiled to JavaScript with
19
+ declarations; the `.vue` sources ship beside them. They depend on Vue and Looma only.
20
+
21
+ The editor is Tiptap-based and needs `@tiptap/vue-3@^2.11.5`:
22
+
10
23
  ```ts
11
- import { openOverlay } from "@threadlabs/looma";
12
- import { TopBar } from "@threadlabs/looma/vue";
13
24
  import { LoomaEditor } from "@threadlabs/looma/vue/editor";
25
+ import { getDefaultEditorExtensions } from "@threadlabs/looma/editor/extensions";
14
26
  ```
15
27
 
16
- Vue and Tiptap are optional to the package as a whole. `/vue` needs only Vue 3.5
17
- or newer. Looma's `/editor` and `/vue/editor` surfaces are Tiptap-backed and
18
- ship Looma's concrete extension set within those subpaths. `/vue/editor` consumers
19
- must also install `@tiptap/vue-3@^2.11.5`; `LoomaEditor` owns the Tiptap lifecycle,
20
- formatting toolbar, slash menu, bounded mention suggestions, image insertion,
21
- and table editing. `/editor/ui`
22
- is the low-level UI-only boundary. See the
23
- repository getting-started guide for the full CSS and adapter setup.
28
+ The editor components' event and geometry types, without Tiptap, are in
29
+ `@threadlabs/looma/editor/ui`.
30
+
31
+ ## HTML
32
+
33
+ ```html
34
+ <link rel="stylesheet" href="…/@threadlabs/looma/tokens.css">
35
+ <script type="module">
36
+ import "@threadlabs/looma";
37
+ </script>
38
+
39
+ <ui-stack gap="m">
40
+ <ui-button variant="solid">Save</ui-button>
41
+ </ui-stack>
42
+ ```
43
+
44
+ Importing the package registers every component with the
45
+ [HTML Next runtime](https://www.npmjs.com/package/@nextwebwg/declarative-components), which turns
46
+ each `<ui-*>` element into its native root. Each component's styles are scoped to it; the token
47
+ stylesheet is the only stylesheet to include. Without a build, link components one by one from
48
+ `@threadlabs/looma/components/<tag>/<tag>.html`.
49
+
50
+ ## Theming
51
+
52
+ Components read design tokens (`--ui-*` custom properties) from `tokens.css` and a theme
53
+ (`theme-light.css`, `theme-dark.css`, `theme-high-contrast.css`), and each exposes its own
54
+ `--ui-<component>-*` properties. Set them on the component or any ancestor.
@@ -0,0 +1,69 @@
1
+ export const backgrounds = [
2
+ ["background-none", "Default", ""], ["background-gray", "Gray", "#f3f4f6"],
3
+ ["background-yellow", "Yellow", "#fef3c7"], ["background-blue", "Blue", "#dbeafe"],
4
+ ["background-green", "Green", "#dcfce7"], ["background-red", "Red", "#fee2e2"],
5
+ ];
6
+
7
+ // Serialized from Looma's framework-neutral icon catalog. Keeping the icon nodes here lets the
8
+ // migration controllers stay directly importable in a browser without introducing a bundler-only
9
+ // package resolution step.
10
+ export const icons = {
11
+ "align-left": [["path", { d: "M21 5H3" }], ["path", { d: "M15 12H3" }], ["path", { d: "M17 19H3" }]],
12
+ "align-center": [["path", { d: "M21 5H3" }], ["path", { d: "M17 12H7" }], ["path", { d: "M19 19H5" }]],
13
+ "align-right": [["path", { d: "M21 5H3" }], ["path", { d: "M21 12H9" }], ["path", { d: "M21 19H7" }]],
14
+ rows: [["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }], ["path", { d: "M21 9H3" }], ["path", { d: "M21 15H3" }]],
15
+ columns: [["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }], ["path", { d: "M9 3v18" }], ["path", { d: "M15 3v18" }]],
16
+ table: [["path", { d: "M9 3H5a2 2 0 0 0-2 2v4m6-6h10a2 2 0 0 1 2 2v4M9 3v18m0 0h10a2 2 0 0 0 2-2V9M9 21H5a2 2 0 0 1-2-2V9m0 0h18" }]],
17
+ "chevron-down": [["path", { d: "m6 9 6 6 6-6" }]],
18
+ "panel-top": [["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }], ["path", { d: "M3 9h18" }]],
19
+ "panel-bottom": [["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }], ["path", { d: "M3 15h18" }]],
20
+ "panel-left": [["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }], ["path", { d: "M9 3v18" }]],
21
+ "panel-right": [["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }], ["path", { d: "M15 3v18" }]],
22
+ eraser: [["path", { d: "M21 21H8a2 2 0 0 1-1.42-.587l-3.994-3.999a2 2 0 0 1 0-2.828l10-10a2 2 0 0 1 2.829 0l5.999 6a2 2 0 0 1 0 2.828L12.834 21" }], ["path", { d: "m5.082 11.09 8.828 8.828" }]],
23
+ merge: [["path", { d: "m8 6 4-4 4 4" }], ["path", { d: "M12 2v10.3a4 4 0 0 1-1.172 2.872L4 22" }], ["path", { d: "m20 22-5-5" }]],
24
+ split: [["path", { d: "M16 3h5v5" }], ["path", { d: "M8 3H3v5" }], ["path", { d: "M12 22v-8.3a4 4 0 0 0-1.172-2.872L3 3" }], ["path", { d: "m15 9 6-6" }]],
25
+ trash: [["path", { d: "M10 11v6" }], ["path", { d: "M14 11v6" }], ["path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }], ["path", { d: "M3 6h18" }], ["path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }]],
26
+ plus: [["path", { d: "M5 12h14" }], ["path", { d: "M12 5v14" }]],
27
+ "grip-vertical": [["circle", { cx: "9", cy: "12", r: "1" }], ["circle", { cx: "9", cy: "5", r: "1" }], ["circle", { cx: "9", cy: "19", r: "1" }], ["circle", { cx: "15", cy: "12", r: "1" }], ["circle", { cx: "15", cy: "5", r: "1" }], ["circle", { cx: "15", cy: "19", r: "1" }]],
28
+ "grip-horizontal": [["circle", { cx: "12", cy: "9", r: "1" }], ["circle", { cx: "19", cy: "9", r: "1" }], ["circle", { cx: "5", cy: "9", r: "1" }], ["circle", { cx: "12", cy: "15", r: "1" }], ["circle", { cx: "19", cy: "15", r: "1" }], ["circle", { cx: "5", cy: "15", r: "1" }]],
29
+ "heading-1": [["path", { d: "M4 12h8" }], ["path", { d: "M4 18V6" }], ["path", { d: "M12 18V6" }], ["path", { d: "m17 12 3-2v8" }]],
30
+ pilcrow: [["path", { d: "M13 4v16" }], ["path", { d: "M17 4v16" }], ["path", { d: "M19 4H9.5a4.5 4.5 0 0 0 0 9H13" }]],
31
+ quote: [["path", { d: "M16 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z" }], ["path", { d: "M5 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z" }]],
32
+ };
33
+
34
+ export function normalizeAnchor(value) {
35
+ if (!value || typeof value !== "object") return null;
36
+ const finite = (candidate) => typeof candidate === "number" && Number.isFinite(candidate) ? candidate : null;
37
+ const x = finite(value.x); const y = finite(value.y); const width = finite(value.width); const height = finite(value.height);
38
+ const left = finite(value.left) ?? x; const top = finite(value.top) ?? y;
39
+ const right = finite(value.right) ?? (left !== null && width !== null ? left + width : null);
40
+ const bottom = finite(value.bottom) ?? (top !== null && height !== null ? top + height : null);
41
+ return left === null || top === null || right === null || bottom === null ? null : { left, top, right, bottom };
42
+ }
43
+
44
+ export function viewport() {
45
+ const visual = window.visualViewport;
46
+ const left = visual?.offsetLeft ?? 0; const top = visual?.offsetTop ?? 0;
47
+ const width = visual?.width ?? window.innerWidth; const height = visual?.height ?? window.innerHeight;
48
+ return { left, top, width, height, right: left + width, bottom: top + height };
49
+ }
50
+
51
+ /**
52
+ * Places a floating menu below its anchor (or above, when there is more room), or as a bottom sheet on
53
+ * narrow screens.
54
+ */
55
+ export function positionMenu(element, rect, width) {
56
+ if (!rect) return;
57
+ const view = viewport();
58
+ Object.assign(element.style, { position: "fixed", zIndex: "500" });
59
+ if (window.innerWidth < 768) {
60
+ const height = Math.min(320, Math.max(0, view.height - 56));
61
+ Object.assign(element.style, { left: `${view.left}px`, right: "", bottom: "", top: `${view.top + view.height - height - 56}px`, width: `${view.width}px`, maxHeight: `${height}px` });
62
+ return;
63
+ }
64
+ const below = view.bottom - rect.bottom - 8;
65
+ const above = rect.top - view.top - 8;
66
+ const top = below >= 320 || below >= above ? rect.bottom + 8 : rect.top - 328;
67
+ const left = Math.max(view.left + 8, Math.min(rect.left, view.right - width - 8));
68
+ Object.assign(element.style, { top: `${Math.max(view.top + 8, top)}px`, left: `${left}px`, right: "", bottom: "", width: `${width}px`, maxHeight: "" });
69
+ }
@@ -0,0 +1,20 @@
1
+ const initialized = new WeakSet();
2
+
3
+ /**
4
+ * Marks the document `data-ui-input-modality="touch"` once the user actually touches it, rather than
5
+ * guessing from capability media queries: hybrid devices keep compact pointer affordances until then.
6
+ * Idempotent per document; touch is sticky once observed.
7
+ */
8
+ export function trackInputModality(document) {
9
+ if (initialized.has(document)) return;
10
+ initialized.add(document);
11
+ const markTouch = () => {
12
+ if (document.documentElement.getAttribute("data-ui-input-modality") === "touch") return;
13
+ document.documentElement.setAttribute("data-ui-input-modality", "touch");
14
+ document.dispatchEvent(new CustomEvent("ui-input-modality-change", { detail: { modality: "touch" } }));
15
+ };
16
+ document.addEventListener("pointerdown", (event) => {
17
+ if (event.pointerType === "touch") markTouch();
18
+ }, { capture: true, passive: true });
19
+ document.addEventListener("touchstart", markTouch, { capture: true, passive: true });
20
+ }
@@ -0,0 +1,281 @@
1
+ const stacks = new WeakMap();
2
+ let anchorSequence = 0;
3
+ const anchorBindings = new WeakMap();
4
+
5
+ function stateFor(document) {
6
+ let state = stacks.get(document);
7
+ if (state) return state;
8
+ state = { records: [], modalCount: 0, listening: false };
9
+ stacks.set(document, state);
10
+ return state;
11
+ }
12
+
13
+ function syncScrollLock(document, state) {
14
+ document.documentElement.toggleAttribute("data-ui-scroll-lock", state.modalCount > 0);
15
+ if (state.modalCount > 0) document.documentElement.style.overflow = "hidden";
16
+ else document.documentElement.style.removeProperty("overflow");
17
+ }
18
+
19
+ function requestClose(document, reason, trigger) {
20
+ const record = stateFor(document).records.at(-1);
21
+ if (!record || (record.dismissible === false && (reason === "escape" || reason === "light-dismiss"))) return false;
22
+ record.requestClose(reason, trigger);
23
+ return true;
24
+ }
25
+
26
+ function ensureListeners(document, state) {
27
+ if (state.listening) return;
28
+ state.onKeydown = (event) => {
29
+ if (event.key === "Escape") requestClose(document, "escape", "keyboard");
30
+ };
31
+ state.onPointerdown = (event) => {
32
+ const top = state.records.at(-1);
33
+ if (!top || top.dismissible === false) return;
34
+ const boundary = [top.element, ...(top.relatedElements ?? [])];
35
+ // A modal dialog's ::backdrop reports the dialog itself as the target; a press outside its box is outside.
36
+ const rect = top.element.getBoundingClientRect();
37
+ const onBackdrop = event.target === top.element
38
+ && (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom);
39
+ if (!onBackdrop && event.composedPath().some((target) => target instanceof Node && boundary.some((element) => element.contains(target)))) return;
40
+ requestClose(document, "light-dismiss", "pointer");
41
+ };
42
+ document.addEventListener("keydown", state.onKeydown);
43
+ document.addEventListener("pointerdown", state.onPointerdown, true);
44
+ state.listening = true;
45
+ }
46
+
47
+ function removeListeners(document, state) {
48
+ if (!state.listening || state.records.length) return;
49
+ document.removeEventListener("keydown", state.onKeydown);
50
+ document.removeEventListener("pointerdown", state.onPointerdown, true);
51
+ state.listening = false;
52
+ }
53
+
54
+ export function openOverlay(record) {
55
+ const document = record.element.ownerDocument;
56
+ const state = stateFor(document);
57
+ closeOverlay(document, record.id);
58
+ state.records.push(record);
59
+ if (record.modal) {
60
+ state.modalCount += 1;
61
+ syncScrollLock(document, state);
62
+ }
63
+ ensureListeners(document, state);
64
+ }
65
+
66
+ export function closeOverlay(document, id) {
67
+ const state = stateFor(document);
68
+ const index = state.records.findIndex((record) => record.id === id);
69
+ if (index >= 0) {
70
+ const [record] = state.records.splice(index, 1);
71
+ if (record?.modal) {
72
+ state.modalCount = Math.max(0, state.modalCount - 1);
73
+ syncScrollLock(document, state);
74
+ }
75
+ }
76
+ removeListeners(document, state);
77
+ }
78
+
79
+ export function requestTopOverlayClose(document, reason, trigger) {
80
+ return requestClose(document, reason, trigger);
81
+ }
82
+
83
+ function topLayerOpen(surface) {
84
+ try { return surface.matches(":popover-open"); } catch { return !surface.hidden; }
85
+ }
86
+
87
+ function show(surface) {
88
+ surface.setAttribute("popover", "manual");
89
+ surface.hidden = false;
90
+ if (typeof surface.showPopover === "function" && !topLayerOpen(surface)) {
91
+ try { surface.showPopover(); } catch {}
92
+ }
93
+ }
94
+
95
+ function hide(surface) {
96
+ if (typeof surface.hidePopover === "function" && topLayerOpen(surface)) {
97
+ try { surface.hidePopover(); } catch {}
98
+ }
99
+ surface.hidden = true;
100
+ }
101
+
102
+ function bindAnchor(anchor, name) {
103
+ const binding = anchorBindings.get(anchor) ?? { original: anchor.style.getPropertyValue("anchor-name"), names: new Set() };
104
+ binding.names.add(name);
105
+ anchorBindings.set(anchor, binding);
106
+ anchor.style.setProperty("anchor-name", [binding.original, ...binding.names].filter(Boolean).join(", "));
107
+ }
108
+
109
+ function unbindAnchor(anchor, name) {
110
+ const binding = anchorBindings.get(anchor);
111
+ if (!binding) return;
112
+ binding.names.delete(name);
113
+ if (binding.names.size) anchor.style.setProperty("anchor-name", [binding.original, ...binding.names].filter(Boolean).join(", "));
114
+ else {
115
+ if (binding.original) anchor.style.setProperty("anchor-name", binding.original);
116
+ else anchor.style.removeProperty("anchor-name");
117
+ anchorBindings.delete(anchor);
118
+ }
119
+ }
120
+
121
+ function viewport(owner) {
122
+ const visual = owner.visualViewport;
123
+ const left = visual?.offsetLeft ?? 0;
124
+ const top = visual?.offsetTop ?? 0;
125
+ const width = visual?.width ?? owner.innerWidth;
126
+ const height = visual?.height ?? owner.innerHeight;
127
+ return { left, top, right: left + width, bottom: top + height, width, height };
128
+ }
129
+
130
+ function clamp(rect, bounds, gutter) {
131
+ const availableWidth = Math.max(0, bounds.width - gutter * 2);
132
+ const availableHeight = Math.max(0, bounds.height - gutter * 2);
133
+ const left = rect.width > availableWidth ? bounds.left + gutter : Math.min(Math.max(rect.left, bounds.left + gutter), bounds.right - gutter - rect.width);
134
+ const top = rect.height > availableHeight ? bounds.top + gutter : Math.min(Math.max(rect.top, bounds.top + gutter), bounds.bottom - gutter - rect.height);
135
+ return { x: left - rect.left, y: top - rect.top };
136
+ }
137
+
138
+ // Entry transitions scale and nudge the surface; measure its untransformed layout box so a position
139
+ // computed mid-animation is not shrunk or offset (offsetWidth/Height ignore transforms).
140
+ function layoutRect(surface) {
141
+ const box = surface.getBoundingClientRect();
142
+ const width = surface.offsetWidth || box.width;
143
+ const height = surface.offsetHeight || box.height;
144
+ const left = box.left + (box.width - width) / 2;
145
+ const top = box.top + (box.height - height) / 2;
146
+ return { left, top, right: left + width, bottom: top + height, width, height };
147
+ }
148
+
149
+ function fallbackPosition(surface, anchor, placement, gap, viewportGap, surfaceRect = layoutRect(surface)) {
150
+ const bounds = viewport(surface.ownerDocument.defaultView);
151
+ const preferTop = placement.startsWith("top");
152
+ const preferEnd = placement.endsWith("end");
153
+ const below = bounds.bottom - anchor.bottom - viewportGap;
154
+ const above = anchor.top - bounds.top - viewportGap;
155
+ const useTop = preferTop ? !(above < surfaceRect.height + gap && below > above) : below < surfaceRect.height + gap && above > below;
156
+ const top = useTop ? anchor.top - surfaceRect.height - gap : anchor.bottom + gap;
157
+ const left = preferEnd ? anchor.right - surfaceRect.width : anchor.left;
158
+ const shift = clamp({ left, top, right: left + surfaceRect.width, bottom: top + surfaceRect.height, width: surfaceRect.width, height: surfaceRect.height }, bounds, viewportGap);
159
+ surface.style.left = `${Math.round(left + shift.x)}px`;
160
+ surface.style.top = `${Math.round(top + shift.y)}px`;
161
+ surface.style.right = "auto";
162
+ surface.style.bottom = "auto";
163
+ }
164
+
165
+ export function createAnchoredSurface(surface, options = {}) {
166
+ const owner = surface.ownerDocument.defaultView;
167
+ let placement = options.placement ?? "bottom-start";
168
+ const gap = Math.max(0, options.gap ?? 4);
169
+ const viewportGap = Math.max(0, options.viewportGap ?? 8);
170
+ const nativeAnchor = owner.CSS?.supports?.("anchor-name: --ui-anchor-test") && owner.CSS.supports("position-anchor: --ui-anchor-test") && owner.CSS.supports("top: anchor(bottom)");
171
+ const anchorName = `--ui-anchor-${++anchorSequence}`;
172
+ let anchor = options.anchor ?? null;
173
+ let point = null;
174
+ let open = false;
175
+ let frame = null;
176
+ let abort = null;
177
+ let sizeObserver = null;
178
+ surface.setAttribute("popover", "manual");
179
+ surface.dataset.uiPositioning = nativeAnchor ? "anchor" : "fallback";
180
+ surface.style.position = "fixed";
181
+ surface.style.margin = "0";
182
+ const bind = (next) => {
183
+ if (anchor) unbindAnchor(anchor, anchorName);
184
+ anchor = next;
185
+ if (anchor) {
186
+ bindAnchor(anchor, anchorName);
187
+ surface.style.setProperty("position-anchor", anchorName);
188
+ } else surface.style.removeProperty("position-anchor");
189
+ };
190
+ const position = () => {
191
+ frame = null;
192
+ if (!open) return;
193
+ if (point) {
194
+ fallbackPosition(surface, { left: point.x, right: point.x, top: point.y, bottom: point.y, width: 0, height: 0 }, "bottom-start", gap, viewportGap);
195
+ return;
196
+ }
197
+ if (!anchor) return;
198
+ let rect;
199
+ if (nativeAnchor) {
200
+ const blockEnd = placement.startsWith("bottom");
201
+ const inlineEnd = placement.endsWith("end");
202
+ surface.style.top = blockEnd ? `calc(anchor(bottom) + ${gap}px)` : "auto";
203
+ surface.style.bottom = blockEnd ? "auto" : `calc(anchor(top) + ${gap}px)`;
204
+ surface.style.left = inlineEnd ? "auto" : "anchor(left)";
205
+ surface.style.right = inlineEnd ? "anchor(right)" : "auto";
206
+ surface.style.setProperty("position-try-fallbacks", "flip-block, flip-inline");
207
+ rect = layoutRect(surface);
208
+ if (!rect.width || !rect.height) return;
209
+ const shift = clamp(rect, viewport(owner), viewportGap);
210
+ if (!shift.x && !shift.y) return;
211
+ }
212
+ fallbackPosition(surface, anchor.getBoundingClientRect(), placement, gap, viewportGap, rect);
213
+ };
214
+ const schedule = () => {
215
+ if (open && frame === null) frame = owner.requestAnimationFrame(position);
216
+ };
217
+ const syncListeners = () => {
218
+ const needed = open && (point || anchor);
219
+ if (!needed) {
220
+ abort?.abort();
221
+ abort = null;
222
+ return;
223
+ }
224
+ if (abort) return;
225
+ abort = new AbortController();
226
+ const signal = abort.signal;
227
+ owner.addEventListener("resize", schedule, { passive: true, signal });
228
+ owner.addEventListener("scroll", schedule, { passive: true, capture: true, signal });
229
+ owner.visualViewport?.addEventListener("resize", schedule, { passive: true, signal });
230
+ owner.visualViewport?.addEventListener("scroll", schedule, { passive: true, signal });
231
+ };
232
+ const observeSize = () => {
233
+ if (sizeObserver || !owner.ResizeObserver) return;
234
+ sizeObserver = new owner.ResizeObserver(schedule);
235
+ sizeObserver.observe(surface);
236
+ };
237
+ const stopSize = () => { sizeObserver?.disconnect(); sizeObserver = null; };
238
+ bind(anchor);
239
+ return {
240
+ setAnchor(next) { point = null; bind(next); syncListeners(); schedule(); },
241
+ setPlacement(next) { placement = next || "bottom-start"; schedule(); },
242
+ show() { point = null; open = true; syncListeners(); show(surface); position(); schedule(); observeSize(); },
243
+ showAtPoint(next) { point = next; open = true; syncListeners(); show(surface); position(); schedule(); observeSize(); },
244
+ hide() { open = false; point = null; syncListeners(); stopSize(); if (frame !== null) owner.cancelAnimationFrame(frame); frame = null; hide(surface); },
245
+ refresh: schedule,
246
+ destroy() { open = false; abort?.abort(); abort = null; stopSize(); if (frame !== null) owner.cancelAnimationFrame(frame); frame = null; hide(surface); bind(null); },
247
+ };
248
+ }
249
+
250
+ /**
251
+ * Resolves `for`-style ID references that may not exist yet. `get(id)` returns the element or, when
252
+ * it is missing, watches the document and calls `onLate` once an element with that ID appears
253
+ * (examples, templates, and frameworks often render the trigger after the overlay).
254
+ */
255
+ export function createIdResolver(document, onLate) {
256
+ let observer = null;
257
+ let waiting = "";
258
+ const stop = () => {
259
+ observer?.disconnect();
260
+ observer = null;
261
+ waiting = "";
262
+ };
263
+ const get = (id) => {
264
+ const found = id ? document.getElementById(id) : null;
265
+ if (found || !id) {
266
+ if (waiting) stop();
267
+ return found;
268
+ }
269
+ if (waiting === id) return null;
270
+ stop();
271
+ waiting = id;
272
+ observer = new MutationObserver(() => {
273
+ if (!document.getElementById(waiting)) return;
274
+ stop();
275
+ onLate();
276
+ });
277
+ observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ["id"] });
278
+ return null;
279
+ };
280
+ return { get, stop };
281
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Reports what caused the next change on a component: a `change` event is a plain Event, so the
3
+ * keyboard or pointer that preceded it is remembered here. Returns [take, stop].
4
+ */
5
+ export function trackTrigger(host) {
6
+ let trigger = "programmatic";
7
+ const offKey = host.on("keydown", (event) => {
8
+ if (event.key === " " || event.key === "Enter" || event.key.startsWith("Arrow")) trigger = "keyboard";
9
+ });
10
+ const offPointer = host.on("pointerdown", () => { trigger = "pointer"; });
11
+ const take = () => {
12
+ const current = trigger;
13
+ trigger = "programmatic";
14
+ return current;
15
+ };
16
+ return [take, () => { offKey(); offPointer(); }];
17
+ }
@@ -0,0 +1,22 @@
1
+ <template component="ui-affordance-scope" controller="./ui-affordance-scope.js">
2
+ <defs>
3
+ <prop name="nearRadius" type="number" default="16">How close, in pixels, the pointer must come to reveal an affordance.</prop>
4
+ <state name="engaged" :value="false"></state>
5
+ </defs>
6
+ <span><slot></slot></span>
7
+ <style>
8
+ :host {
9
+ display: contents;
10
+ --ui-affordance-scope-engaged: 0;
11
+ }
12
+
13
+ :host-state([engaged]) {
14
+ --ui-affordance-scope-engaged: 1;
15
+ }
16
+
17
+ /* A component that sets its display still honours the hidden attribute on its root. */
18
+ :host[hidden] {
19
+ display: none;
20
+ }
21
+ </style>
22
+ </template>
@@ -0,0 +1,76 @@
1
+ function distance(point, rect) {
2
+ const dx = Math.max(rect.left - point.x, 0, point.x - rect.right);
3
+ const dy = Math.max(rect.top - point.y, 0, point.y - rect.bottom);
4
+ return Math.hypot(dx, dy);
5
+ }
6
+
7
+ export default function controller(host) {
8
+ const element = host.element;
9
+ const owner = element.ownerDocument.defaultView;
10
+ let anchors = [];
11
+ let frame = null;
12
+ let point = null;
13
+ let dirty = false;
14
+ let nearRadius = Number(host.state.nearRadius ?? 16);
15
+ const clear = () => {
16
+ for (const anchor of anchors) anchor.element.removeAttribute("data-ui-proximity");
17
+ host.state.engaged = false;
18
+ };
19
+ const measure = () => {
20
+ anchors = Array.from(element.querySelectorAll("[data-ui-affordance]"))
21
+ .filter((anchor) => !anchor.hasAttribute("disabled") && anchor.getAttribute("aria-disabled") !== "true")
22
+ .map((anchor) => ({ element: anchor, rect: anchor.getBoundingClientRect() }));
23
+ dirty = false;
24
+ };
25
+ const update = () => {
26
+ frame = null;
27
+ if (dirty) measure();
28
+ if (!point) {
29
+ clear();
30
+ return;
31
+ }
32
+ let engaged = false;
33
+ for (const anchor of anchors) {
34
+ const near = distance(point, anchor.rect) <= nearRadius;
35
+ anchor.element.toggleAttribute("data-ui-proximity", near);
36
+ if (near) anchor.element.setAttribute("data-ui-proximity", "near");
37
+ engaged ||= near;
38
+ }
39
+ host.state.engaged = engaged;
40
+ };
41
+ const schedule = () => {
42
+ if (frame === null) frame = owner.requestAnimationFrame(update);
43
+ };
44
+ const invalidate = () => {
45
+ dirty = true;
46
+ schedule();
47
+ };
48
+ const onPointermove = (event) => {
49
+ point = event.pointerType === "touch" ? null : { x: event.clientX, y: event.clientY };
50
+ schedule();
51
+ };
52
+ const observer = new MutationObserver(invalidate);
53
+ observer.observe(element, { childList: true, subtree: true, attributes: true, attributeFilter: ["data-ui-affordance", "disabled", "aria-disabled"] });
54
+ element.ownerDocument.addEventListener("pointermove", onPointermove, { passive: true });
55
+ owner.addEventListener("resize", invalidate, { passive: true });
56
+ owner.addEventListener("scroll", invalidate, { passive: true, capture: true });
57
+ owner.visualViewport?.addEventListener("resize", invalidate, { passive: true });
58
+ owner.visualViewport?.addEventListener("scroll", invalidate, { passive: true });
59
+ const stop = host.effect(() => {
60
+ const configured = Number(host.state.nearRadius ?? 16);
61
+ nearRadius = Number.isFinite(configured) ? Math.max(0, configured) : 16;
62
+ invalidate();
63
+ });
64
+ measure();
65
+ return () => {
66
+ stop();
67
+ observer.disconnect();
68
+ element.ownerDocument.removeEventListener("pointermove", onPointermove);
69
+ owner.removeEventListener("resize", invalidate);
70
+ owner.removeEventListener("scroll", invalidate, true);
71
+ owner.visualViewport?.removeEventListener("resize", invalidate);
72
+ owner.visualViewport?.removeEventListener("scroll", invalidate);
73
+ if (frame !== null) owner.cancelAnimationFrame(frame);
74
+ clear();
75
+ };
76
+ }
@@ -0,0 +1,59 @@
1
+ <template component="ui-avatar" controller="./ui-avatar.js">
2
+ <defs>
3
+ <prop name="src" type="string">Image URL.</prop>
4
+ <prop name="alt" type="string" default="">alt token.</prop>
5
+ <prop name="name" type="string" default="">name token.</prop>
6
+ <prop name="fallback" type="string" default="">fallback token.</prop>
7
+ <state name="hasImage" :value="false"></state>
8
+ <state name="hasAuthoredImage" :value="false"></state>
9
+ <state name="initials" :value="'?'"></state>
10
+ </defs>
11
+ <span>
12
+ <slot></slot>
13
+ <img class="image" $ref="image" :src="src" alt="" aria-hidden="true" :hidden="hasAuthoredImage or not hasImage">
14
+ <span class="fallback" :aria-hidden="hasImage" :hidden="hasImage" $value="initials"></span>
15
+ </span>
16
+ <style>
17
+ :host {
18
+ display: inline-grid;
19
+ place-items: center;
20
+ overflow: hidden;
21
+ inline-size: 2.5rem;
22
+ block-size: 2.5rem;
23
+ border-radius: 999px;
24
+ border: 1px solid var(--ui-avatar-border, var(--ui-border-strong));
25
+ background: var(--ui-avatar-surface, var(--ui-accent-soft));
26
+ color: var(--ui-avatar-text, var(--ui-accent-solid));
27
+ font: inherit;
28
+ font-size: 0.875rem;
29
+ font-weight: 600;
30
+ text-transform: uppercase;
31
+ }
32
+
33
+ .image,
34
+ :slotted(img) {
35
+ grid-area: 1 / 1;
36
+ inline-size: 100%;
37
+ block-size: 100%;
38
+ object-fit: cover;
39
+ }
40
+
41
+ .fallback {
42
+ grid-area: 1 / 1;
43
+ display: inline-flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ inline-size: 100%;
47
+ block-size: 100%;
48
+ }
49
+
50
+ [hidden] {
51
+ display: none;
52
+ }
53
+
54
+ /* A component that sets its display still honours the hidden attribute on its root. */
55
+ :host[hidden] {
56
+ display: none;
57
+ }
58
+ </style>
59
+ </template>
@@ -0,0 +1,47 @@
1
+ function toInitials(value) {
2
+ const tokens = (value || "").trim().split(/\s+/).filter((token) => token.length > 0);
3
+ if (tokens.length === 0) return "?";
4
+ if (tokens.length === 1) return tokens[0].slice(0, 2).toUpperCase();
5
+ return `${tokens[0][0] ?? ""}${tokens[1][0] ?? ""}`.toUpperCase();
6
+ }
7
+
8
+ // Shows an authored <img>, else the `src` image, else initials, and tracks whether the image loaded.
9
+ export default function controller(host) {
10
+ const element = host.element;
11
+ const managed = host.refs.image;
12
+ let active;
13
+ const onLoad = () => { host.state.hasImage = true; };
14
+ const onError = () => { host.state.hasImage = false; };
15
+ const watch = (image) => {
16
+ if (image === active) return;
17
+ active?.removeEventListener("load", onLoad);
18
+ active?.removeEventListener("error", onError);
19
+ active = image;
20
+ active.addEventListener("load", onLoad);
21
+ active.addEventListener("error", onError);
22
+ host.state.hasImage = active.complete && active.naturalWidth > 0;
23
+ };
24
+ const apply = () => {
25
+ const authored = Array.from(element.querySelectorAll("img")).find((image) => image !== managed);
26
+ host.state.hasAuthoredImage = Boolean(authored);
27
+ watch(authored ?? managed);
28
+ if (authored) {
29
+ authored.hidden = !host.state.hasImage;
30
+ element.removeAttribute("role");
31
+ element.removeAttribute("aria-label");
32
+ } else {
33
+ element.setAttribute("role", "img");
34
+ element.setAttribute("aria-label", host.state.alt || host.state.name || "Avatar");
35
+ }
36
+ host.state.initials = host.state.fallback || toInitials(host.state.name || host.state.alt);
37
+ };
38
+ const observer = new MutationObserver(apply);
39
+ observer.observe(element, { childList: true });
40
+ const stop = host.effect(apply);
41
+ return () => {
42
+ stop();
43
+ observer.disconnect();
44
+ active?.removeEventListener("load", onLoad);
45
+ active?.removeEventListener("error", onError);
46
+ };
47
+ }