@threadlabs/looma 0.4.0 → 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 (490) 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 -60
  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/styles.css +0 -940
  490. package/vue/chunk-OX3EACZ7.js +0 -4369
@@ -1,2592 +0,0 @@
1
- import {
2
- __export
3
- } from "./chunk-JK53ENSG.js";
4
-
5
- // src/ui.ts
6
- import { registerLoomaPackage } from "@threadlabs/looma/core/declarative";
7
-
8
- // src/declarative/components/controllers/ui-editor-insert-table-grid.js
9
- var ui_editor_insert_table_grid_exports = {};
10
- __export(ui_editor_insert_table_grid_exports, {
11
- default: () => controller
12
- });
13
- function bounded(value, fallback) {
14
- const parsed = Number.parseInt(String(value ?? fallback), 10);
15
- return Math.min(10, Math.max(1, Number.isFinite(parsed) ? parsed : fallback));
16
- }
17
- function controller(host) {
18
- const element = host.element;
19
- let selectedRows = 3;
20
- let selectedCols = 3;
21
- let previewRows = null;
22
- let previewCols = null;
23
- let lastHeaderRow = Boolean(host.state.headerRow);
24
- let withHeaderRow = lastHeaderRow;
25
- let selectionCommitted = false;
26
- const updateSelection = () => {
27
- const activeRows = previewRows ?? selectedRows;
28
- const activeCols = previewCols ?? selectedCols;
29
- const hint = element.querySelector(".ui-editor-insert-table-grid__hint");
30
- if (hint) hint.textContent = previewRows !== null && previewCols !== null ? `${activeRows} \xD7 ${activeCols}` : `${selectedRows} \xD7 ${selectedCols}${selectionCommitted ? " selected" : ""}`;
31
- for (const cell of element.querySelectorAll("[data-row][data-col]")) {
32
- const selected = Number(cell.dataset.row) <= activeRows && Number(cell.dataset.col) <= activeCols;
33
- cell.classList.toggle("ui-editor-insert-table-grid__cell--selected", selected);
34
- cell.setAttribute("aria-pressed", String(selected));
35
- }
36
- };
37
- const render = () => {
38
- const configuredHeaderRow = Boolean(host.state.headerRow);
39
- if (configuredHeaderRow !== lastHeaderRow) {
40
- lastHeaderRow = configuredHeaderRow;
41
- withHeaderRow = configuredHeaderRow;
42
- }
43
- element.hidden = !host.state.open;
44
- if (!host.state.open) return;
45
- const maxRows = bounded(host.state.maxRows, 8);
46
- const maxCols = bounded(host.state.maxCols, 8);
47
- const cells = [];
48
- for (let row = 1; row <= maxRows; row += 1) for (let col = 1; col <= maxCols; col += 1) {
49
- const selected = row <= selectedRows && col <= selectedCols;
50
- cells.push(`<button type="button" data-row="${row}" data-col="${col}" aria-label="${row} rows by ${col} columns" aria-pressed="${selected}" class="ui-editor-insert-table-grid__cell${selected ? " ui-editor-insert-table-grid__cell--selected" : ""}"></button>`);
51
- }
52
- element.innerHTML = `<div class="ui-editor-insert-table-grid"><p class="ui-editor-insert-table-grid__hint">${selectedRows} \xD7 ${selectedCols}</p><div class="ui-editor-insert-table-grid__grid" role="group" aria-label="Table dimensions" style="--ui-editor-table-grid-rows:${maxRows};--ui-editor-table-grid-cols:${maxCols}">${cells.join("")}</div><label class="ui-editor-insert-table-grid__header"><input type="checkbox" checked> Header row</label><button type="button" data-insert-table>Insert table</button></div>`;
53
- const checkbox = element.querySelector('input[type="checkbox"]');
54
- if (checkbox) {
55
- checkbox.checked = withHeaderRow;
56
- checkbox.addEventListener("change", () => {
57
- withHeaderRow = checkbox.checked;
58
- });
59
- }
60
- updateSelection();
61
- };
62
- const onClick = (event) => {
63
- const cell = event.target.closest?.("[data-row][data-col]");
64
- if (cell) {
65
- selectedRows = Number(cell.dataset.row);
66
- selectedCols = Number(cell.dataset.col);
67
- previewRows = null;
68
- previewCols = null;
69
- selectionCommitted = true;
70
- updateSelection();
71
- return;
72
- }
73
- if (event.target.closest?.("[data-insert-table]")) {
74
- host.dispatch("insert", { rows: selectedRows, cols: selectedCols, withHeaderRow });
75
- }
76
- };
77
- const onMouseover = (event) => {
78
- const cell = event.target.closest?.("[data-row][data-col]");
79
- if (!cell) {
80
- previewRows = null;
81
- previewCols = null;
82
- updateSelection();
83
- return;
84
- }
85
- previewRows = Number(cell.dataset.row);
86
- previewCols = Number(cell.dataset.col);
87
- updateSelection();
88
- };
89
- const onMouseleave = () => {
90
- previewRows = null;
91
- previewCols = null;
92
- updateSelection();
93
- };
94
- element.addEventListener("click", onClick);
95
- element.addEventListener("mouseover", onMouseover);
96
- element.addEventListener("mouseleave", onMouseleave);
97
- const stop = host.effect(render);
98
- render();
99
- return () => {
100
- stop();
101
- element.removeEventListener("click", onClick);
102
- element.removeEventListener("mouseover", onMouseover);
103
- element.removeEventListener("mouseleave", onMouseleave);
104
- };
105
- }
106
-
107
- // src/declarative/components/controllers/ui-editor-mention-menu.js
108
- var ui_editor_mention_menu_exports = {};
109
- __export(ui_editor_mention_menu_exports, {
110
- default: () => controller2
111
- });
112
-
113
- // src/declarative/components/controllers/shared/editor.js
114
- var backgrounds = [
115
- ["background-none", "Default", ""],
116
- ["background-gray", "Gray", "#f3f4f6"],
117
- ["background-yellow", "Yellow", "#fef3c7"],
118
- ["background-blue", "Blue", "#dbeafe"],
119
- ["background-green", "Green", "#dcfce7"],
120
- ["background-red", "Red", "#fee2e2"]
121
- ];
122
- function escapeHtml(value) {
123
- return String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
124
- }
125
- var icons = {
126
- "align-left": [["path", { d: "M21 5H3" }], ["path", { d: "M15 12H3" }], ["path", { d: "M17 19H3" }]],
127
- "align-center": [["path", { d: "M21 5H3" }], ["path", { d: "M17 12H7" }], ["path", { d: "M19 19H5" }]],
128
- "align-right": [["path", { d: "M21 5H3" }], ["path", { d: "M21 12H9" }], ["path", { d: "M21 19H7" }]],
129
- rows: [["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }], ["path", { d: "M21 9H3" }], ["path", { d: "M21 15H3" }]],
130
- columns: [["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }], ["path", { d: "M9 3v18" }], ["path", { d: "M15 3v18" }]],
131
- 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" }]],
132
- "chevron-down": [["path", { d: "m6 9 6 6 6-6" }]],
133
- "panel-top": [["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }], ["path", { d: "M3 9h18" }]],
134
- "panel-bottom": [["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }], ["path", { d: "M3 15h18" }]],
135
- "panel-left": [["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }], ["path", { d: "M9 3v18" }]],
136
- "panel-right": [["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }], ["path", { d: "M15 3v18" }]],
137
- 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" }]],
138
- 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" }]],
139
- 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" }]],
140
- 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" }]],
141
- plus: [["path", { d: "M5 12h14" }], ["path", { d: "M12 5v14" }]],
142
- "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" }]],
143
- "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" }]],
144
- "heading-1": [["path", { d: "M4 12h8" }], ["path", { d: "M4 18V6" }], ["path", { d: "M12 18V6" }], ["path", { d: "m17 12 3-2v8" }]],
145
- pilcrow: [["path", { d: "M13 4v16" }], ["path", { d: "M17 4v16" }], ["path", { d: "M19 4H9.5a4.5 4.5 0 0 0 0 9H13" }]],
146
- 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" }]]
147
- };
148
- function icon(name, className = "looma-icon") {
149
- const nodes = (icons[name] ?? []).map(([tag, attributes]) => {
150
- const serialized = Object.entries(attributes).map(([key, value]) => ` ${key}="${value}"`).join("");
151
- return `<${tag}${serialized}></${tag}>`;
152
- }).join("");
153
- return `<svg class="${className}" data-looma-icon="${name}" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${nodes}</svg>`;
154
- }
155
- function normalizeAnchor(value) {
156
- if (!value || typeof value !== "object") return null;
157
- const finite = (candidate) => typeof candidate === "number" && Number.isFinite(candidate) ? candidate : null;
158
- const x = finite(value.x);
159
- const y = finite(value.y);
160
- const width = finite(value.width);
161
- const height = finite(value.height);
162
- const left = finite(value.left) ?? x;
163
- const top = finite(value.top) ?? y;
164
- const right = finite(value.right) ?? (left !== null && width !== null ? left + width : null);
165
- const bottom = finite(value.bottom) ?? (top !== null && height !== null ? top + height : null);
166
- return left === null || top === null || right === null || bottom === null ? null : { left, top, right, bottom };
167
- }
168
- function viewport() {
169
- const visual = window.visualViewport;
170
- const left = visual?.offsetLeft ?? 0;
171
- const top = visual?.offsetTop ?? 0;
172
- const width = visual?.width ?? window.innerWidth;
173
- const height = visual?.height ?? window.innerHeight;
174
- return { left, top, width, height, right: left + width, bottom: top + height };
175
- }
176
-
177
- // src/declarative/components/controllers/ui-editor-mention-menu.js
178
- function controller2(host) {
179
- const element = host.element;
180
- let selectedIndex = Number(host.state.selectedIndex ?? 0);
181
- const syncSelection = () => {
182
- const prefix = element.id || "ui-editor-mention-menu";
183
- for (const option of element.querySelectorAll("[data-index]")) {
184
- const active = Number(option.dataset.index) === selectedIndex;
185
- option.classList.toggle("ui-editor-mention-menu__item--active", active);
186
- option.setAttribute("aria-selected", String(active));
187
- }
188
- if (element.querySelector("[data-index]")) element.setAttribute("aria-activedescendant", `${prefix}-option-${selectedIndex}`);
189
- };
190
- const position = () => {
191
- const rect = normalizeAnchor(host.state.anchorRect);
192
- if (!rect) {
193
- element.style.display = "none";
194
- return;
195
- }
196
- const view = viewport();
197
- element.style.display = "block";
198
- element.style.position = "fixed";
199
- element.style.zIndex = "500";
200
- if (window.innerWidth < 768) {
201
- const height = Math.min(320, Math.max(0, view.height - 56));
202
- Object.assign(element.style, { left: `${view.left}px`, top: `${view.top + view.height - height - 56}px`, right: "", bottom: "", width: `${view.width}px`, maxHeight: `${height}px` });
203
- return;
204
- }
205
- const below = view.bottom - rect.bottom - 8;
206
- const above = rect.top - view.top - 8;
207
- const top = below >= 320 || below >= above ? rect.bottom + 8 : rect.top - 328;
208
- const left = Math.max(view.left + 8, Math.min(rect.left, view.right - 328));
209
- Object.assign(element.style, { top: `${Math.max(view.top + 8, top)}px`, left: `${left}px`, right: "", bottom: "", width: "320px", maxHeight: "" });
210
- };
211
- const render = () => {
212
- const items = (Array.isArray(host.state.items) ? host.state.items : []).slice(0, 20);
213
- selectedIndex = Math.min(Number(host.state.selectedIndex ?? selectedIndex), Math.max(0, items.length - 1));
214
- const visible = host.state.open && (host.state.loading || items.length > 0) && normalizeAnchor(host.state.anchorRect);
215
- element.hidden = !visible;
216
- if (!visible) return;
217
- position();
218
- const prefix = element.id || "ui-editor-mention-menu";
219
- const selectedId = `${prefix}-option-${selectedIndex}`;
220
- element.setAttribute("aria-busy", String(Boolean(host.state.loading)));
221
- if (items.length) element.setAttribute("aria-activedescendant", selectedId);
222
- else element.removeAttribute("aria-activedescendant");
223
- const rows = items.map((item, index) => `<li id="${prefix}-option-${index}" class="ui-editor-mention-menu__item${index === selectedIndex ? " ui-editor-mention-menu__item--active" : ""}" role="option" aria-selected="${index === selectedIndex}" data-index="${index}"><span class="ui-editor-mention-menu__avatar" aria-hidden="true">${escapeHtml(item.initials ?? String(item.label).slice(0, 2).toUpperCase())}</span><span class="ui-editor-mention-menu__body"><span class="ui-editor-mention-menu__label">${escapeHtml(item.label)}</span>${item.detail ? `<span class="ui-editor-mention-menu__detail">${escapeHtml(item.detail)}</span>` : ""}</span></li>`).join("");
224
- element.innerHTML = `<div class="ui-editor-mention-menu"><div class="ui-editor-mention-menu__header"><span>People</span><span class="ui-editor-mention-menu__hint">Type after @ to search</span></div>${host.state.loading && !items.length ? '<div class="ui-editor-mention-menu__loading" role="status">Searching\u2026</div>' : `<ul class="ui-editor-mention-menu__list" role="presentation">${rows}</ul>`}</div>`;
225
- };
226
- const onMousedown = (event) => event.preventDefault();
227
- const onClick = (event) => {
228
- const option = event.target.closest?.("[data-index]");
229
- if (option) host.dispatch("select", { index: Number(option.dataset.index) });
230
- };
231
- const onMouseover = (event) => {
232
- const option = event.target.closest?.("[data-index]");
233
- const index = Number(option?.dataset.index);
234
- if (!Number.isFinite(index) || index === selectedIndex) return;
235
- selectedIndex = index;
236
- syncSelection();
237
- host.dispatch("highlight", { index });
238
- };
239
- const onViewport = () => {
240
- if (host.state.open) position();
241
- };
242
- element.setAttribute("role", "listbox");
243
- element.setAttribute("aria-label", "Mention a person");
244
- element.addEventListener("mousedown", onMousedown);
245
- element.addEventListener("click", onClick);
246
- element.addEventListener("mouseover", onMouseover);
247
- window.addEventListener("resize", onViewport);
248
- window.addEventListener("scroll", onViewport, true);
249
- window.visualViewport?.addEventListener("resize", onViewport);
250
- window.visualViewport?.addEventListener("scroll", onViewport);
251
- const stop = host.effect(render);
252
- render();
253
- return () => {
254
- stop();
255
- element.removeEventListener("mousedown", onMousedown);
256
- element.removeEventListener("click", onClick);
257
- element.removeEventListener("mouseover", onMouseover);
258
- window.removeEventListener("resize", onViewport);
259
- window.removeEventListener("scroll", onViewport, true);
260
- window.visualViewport?.removeEventListener("resize", onViewport);
261
- window.visualViewport?.removeEventListener("scroll", onViewport);
262
- };
263
- }
264
-
265
- // src/declarative/components/controllers/ui-editor-slash-menu.js
266
- var ui_editor_slash_menu_exports = {};
267
- __export(ui_editor_slash_menu_exports, {
268
- default: () => controller3
269
- });
270
- function controller3(host) {
271
- const element = host.element;
272
- let selectedIndex = Number(host.state.selectedIndex ?? 0);
273
- const position = () => {
274
- const rect = normalizeAnchor(host.state.anchorRect);
275
- if (!rect) {
276
- element.style.display = "none";
277
- return;
278
- }
279
- const view = viewport();
280
- element.style.display = "block";
281
- element.style.position = "fixed";
282
- element.style.zIndex = "500";
283
- if (window.innerWidth < 768) {
284
- const height = Math.min(320, Math.max(0, view.height - 56));
285
- 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` });
286
- return;
287
- }
288
- const below = view.bottom - rect.bottom - 8;
289
- const above = rect.top - view.top - 8;
290
- const top = below >= 320 || below >= above ? rect.bottom + 8 : rect.top - 328;
291
- const left = Math.max(view.left + 8, Math.min(rect.left, view.right - 288));
292
- Object.assign(element.style, { top: `${Math.max(view.top + 8, top)}px`, left: `${left}px`, right: "", bottom: "", width: "280px", maxHeight: "" });
293
- };
294
- const syncSelection = (ensureVisible = false) => {
295
- let selected;
296
- for (const option of element.querySelectorAll("[data-index]")) {
297
- const active = Number(option.dataset.index) === selectedIndex;
298
- option.classList.toggle("ui-editor-slash-menu__item--active", active);
299
- option.setAttribute("aria-selected", String(active));
300
- if (active) selected = option;
301
- }
302
- if (ensureVisible) selected?.scrollIntoView?.({ block: "nearest" });
303
- };
304
- const render = () => {
305
- selectedIndex = Number(host.state.selectedIndex ?? selectedIndex);
306
- const items = Array.isArray(host.state.items) ? host.state.items : [];
307
- const visible = host.state.open && items.length > 0 && normalizeAnchor(host.state.anchorRect);
308
- element.hidden = !visible;
309
- if (!visible) return;
310
- position();
311
- const query = String(host.state.query ?? "").trim();
312
- element.innerHTML = `<div class="ui-editor-slash-menu"><div class="ui-editor-slash-menu__header" aria-hidden="true"><span class="ui-editor-slash-menu__header-label">Insert</span>${query ? `<span class="ui-editor-slash-menu__header-query">${escapeHtml(query)}</span>` : ""}</div><ul class="ui-editor-slash-menu__list" role="presentation">${items.map((item, index) => `<li class="ui-editor-slash-menu__item${index === selectedIndex ? " ui-editor-slash-menu__item--active" : ""}" role="option" aria-selected="${index === selectedIndex}" data-index="${index}"><span class="ui-editor-slash-menu__icon" aria-hidden="true">${icon(item.icon)}</span><span class="ui-editor-slash-menu__body"><span class="ui-editor-slash-menu__title">${escapeHtml(item.title)}</span><span class="ui-editor-slash-menu__description">${escapeHtml(item.description)}</span></span></li>`).join("")}</ul><div class="ui-editor-slash-menu__footer" aria-hidden="true"><kbd>\u2191\u2193</kbd> navigate &nbsp;\xB7&nbsp; <kbd>\u21B5</kbd> insert &nbsp;\xB7&nbsp; <kbd>Esc</kbd> close</div></div>`;
313
- };
314
- const onMousedown = (event) => event.preventDefault();
315
- const onClick = (event) => {
316
- const option = event.target.closest?.("[data-index]");
317
- if (option) host.dispatch("select", { index: Number(option.dataset.index) });
318
- };
319
- const onMouseover = (event) => {
320
- const option = event.target.closest?.("[data-index]");
321
- const index = Number(option?.dataset.index);
322
- if (!Number.isFinite(index) || index === selectedIndex) return;
323
- selectedIndex = index;
324
- syncSelection();
325
- host.dispatch("highlight", { index });
326
- };
327
- const onViewport = () => {
328
- if (host.state.open) position();
329
- };
330
- element.setAttribute("role", "listbox");
331
- element.setAttribute("aria-label", "Insert block");
332
- element.addEventListener("mousedown", onMousedown);
333
- element.addEventListener("click", onClick);
334
- element.addEventListener("mouseover", onMouseover);
335
- window.addEventListener("resize", onViewport);
336
- window.addEventListener("scroll", onViewport, true);
337
- window.visualViewport?.addEventListener("resize", onViewport);
338
- window.visualViewport?.addEventListener("scroll", onViewport);
339
- const stop = host.effect(render);
340
- render();
341
- return () => {
342
- stop();
343
- element.removeEventListener("mousedown", onMousedown);
344
- element.removeEventListener("click", onClick);
345
- element.removeEventListener("mouseover", onMouseover);
346
- window.removeEventListener("resize", onViewport);
347
- window.removeEventListener("scroll", onViewport, true);
348
- window.visualViewport?.removeEventListener("resize", onViewport);
349
- window.visualViewport?.removeEventListener("scroll", onViewport);
350
- };
351
- }
352
-
353
- // src/declarative/components/controllers/ui-editor-table-context-menu.js
354
- var ui_editor_table_context_menu_exports = {};
355
- __export(ui_editor_table_context_menu_exports, {
356
- default: () => controller4
357
- });
358
- var sections = [
359
- ["Structure", [
360
- ["add-row-before", "Add row above", "panel-top"],
361
- ["add-row-after", "Add row below", "panel-bottom"],
362
- ["add-column-before", "Add column left", "panel-left"],
363
- ["add-column-after", "Add column right", "panel-right"]
364
- ]],
365
- ["Cells", [
366
- ["clear-cells", "Clear selected cells", "eraser"],
367
- ["merge-cells", "Merge cells", "merge"],
368
- ["split-cell", "Split cell", "split"]
369
- ]],
370
- ["Table", [
371
- ["delete-row", "Delete row", "trash", "danger"],
372
- ["delete-column", "Delete column", "trash", "danger"],
373
- ["delete-table", "Delete table", "trash", "danger"]
374
- ]]
375
- ];
376
- function controller4(host) {
377
- const element = host.element;
378
- let positionFrame;
379
- const position = () => {
380
- positionFrame = void 0;
381
- const menu = element.querySelector(".ui-editor-table-context-menu");
382
- if (!menu) return;
383
- menu.style.translate = "";
384
- const rect = menu.getBoundingClientRect();
385
- const view = viewport();
386
- const inset = 12;
387
- let x = 0;
388
- let y = 0;
389
- if (rect.left < view.left + inset) x = view.left + inset - rect.left;
390
- else if (rect.right > view.right - inset) x = view.right - inset - rect.right;
391
- if (rect.top < view.top + inset) y = view.top + inset - rect.top;
392
- else if (rect.bottom > view.bottom - inset) y = view.bottom - inset - rect.bottom;
393
- if (x || y) menu.style.translate = `${x}px ${y}px`;
394
- };
395
- const schedule = () => {
396
- if (positionFrame) cancelAnimationFrame(positionFrame);
397
- positionFrame = requestAnimationFrame(position);
398
- };
399
- const render = () => {
400
- element.hidden = !host.state.open;
401
- if (!host.state.open) return;
402
- const enabled = new Set(Array.isArray(host.state.actions) ? host.state.actions : []);
403
- const background = String(host.state.cellBackground ?? "");
404
- const rendered = [];
405
- const swatches = backgrounds.filter(([action]) => enabled.has(action)).map(([action, label, swatch]) => {
406
- const selected = swatch === background;
407
- return `<button type="button" class="ui-editor-table-context-menu__swatch-button" role="menuitemradio" aria-checked="${selected}" data-action="${action}"${selected ? ' data-selected="true"' : ""}><span class="ui-editor-table-context-menu__swatch${swatch ? "" : " ui-editor-table-context-menu__swatch--default"}"${swatch ? ` style="--ui-editor-table-context-swatch:${swatch}"` : ""}></span><span class="ui-editor-table-context-menu__swatch-label">${label}</span></button>`;
408
- }).join("");
409
- if (swatches) rendered.push(`<div class="ui-editor-table-context-menu__section" role="none"><div class="ui-editor-table-context-menu__heading" role="presentation">Cell background</div><div class="ui-editor-table-context-menu__swatches" role="group" aria-label="Cell background">${swatches}</div></div>`);
410
- for (const [heading, actions] of sections) {
411
- const available = actions.filter(([action]) => enabled.has(action));
412
- if (!available.length) continue;
413
- rendered.push(`<div class="ui-editor-table-context-menu__sep"></div><div class="ui-editor-table-context-menu__section" role="none"><div class="ui-editor-table-context-menu__heading" role="presentation">${heading}</div>${available.map(([action, label, iconName, tone]) => `<button type="button" role="menuitem" data-action="${action}"${tone ? ` data-tone="${tone}"` : ""}>${icon(iconName)}<span>${label}</span></button>`).join("")}</div>`);
414
- }
415
- element.innerHTML = `<div class="ui-editor-table-context-menu" role="menu">${rendered.join("")}</div>`;
416
- schedule();
417
- };
418
- const onClick = (event) => {
419
- const action = event.target.closest?.("[data-action]")?.dataset.action;
420
- if (action) host.dispatch("action", { action });
421
- };
422
- element.addEventListener("click", onClick);
423
- window.addEventListener("resize", schedule);
424
- window.visualViewport?.addEventListener("resize", schedule);
425
- window.visualViewport?.addEventListener("scroll", schedule);
426
- const stop = host.effect(render);
427
- render();
428
- return () => {
429
- stop();
430
- element.removeEventListener("click", onClick);
431
- window.removeEventListener("resize", schedule);
432
- window.visualViewport?.removeEventListener("resize", schedule);
433
- window.visualViewport?.removeEventListener("scroll", schedule);
434
- if (positionFrame) cancelAnimationFrame(positionFrame);
435
- };
436
- }
437
-
438
- // src/declarative/components/controllers/ui-editor-table-overlay.js
439
- var ui_editor_table_overlay_exports = {};
440
- __export(ui_editor_table_overlay_exports, {
441
- default: () => controller5
442
- });
443
- function fallbackBoundaries(segments, length) {
444
- return Array.from({ length: segments + 1 }, (_, index) => index * length / segments);
445
- }
446
- function createProximity(scope, selector = ".ui-editor-table-overlay__handle[data-ui-affordance]", radius = 16) {
447
- const owner = scope.ownerDocument.defaultView;
448
- const pointerTarget = scope.ownerDocument;
449
- let anchors = [];
450
- let frame = null;
451
- let point = null;
452
- let dirty = false;
453
- const distance = (position, rect) => Math.hypot(
454
- Math.max(rect.left - position.x, 0, position.x - rect.right),
455
- Math.max(rect.top - position.y, 0, position.y - rect.bottom)
456
- );
457
- const clear = () => {
458
- for (const anchor of anchors) anchor.element.removeAttribute("data-ui-proximity");
459
- scope.removeAttribute("data-ui-interaction");
460
- };
461
- const measure = () => {
462
- anchors = Array.from(scope.querySelectorAll(selector)).filter((element) => !element.disabled && element.getAttribute("aria-disabled") !== "true").map((element) => ({ element, rect: element.getBoundingClientRect() }));
463
- dirty = false;
464
- };
465
- const update = () => {
466
- frame = null;
467
- if (dirty) measure();
468
- if (!point) {
469
- clear();
470
- return;
471
- }
472
- let engaged = false;
473
- for (const anchor of anchors) {
474
- const near = distance(point, anchor.rect) <= radius;
475
- if (near) anchor.element.setAttribute("data-ui-proximity", "near");
476
- else anchor.element.removeAttribute("data-ui-proximity");
477
- engaged ||= near;
478
- }
479
- if (engaged) scope.setAttribute("data-ui-interaction", "engaged");
480
- else scope.removeAttribute("data-ui-interaction");
481
- };
482
- const schedule = () => {
483
- if (frame === null) frame = owner.requestAnimationFrame(update);
484
- };
485
- const refresh = () => {
486
- dirty = true;
487
- schedule();
488
- };
489
- const onPointermove = (event) => {
490
- point = event.pointerType === "touch" ? null : { x: event.clientX, y: event.clientY };
491
- schedule();
492
- };
493
- pointerTarget.addEventListener("pointermove", onPointermove, { passive: true });
494
- owner.addEventListener("resize", refresh, { passive: true });
495
- owner.addEventListener("scroll", refresh, { passive: true, capture: true });
496
- owner.visualViewport?.addEventListener("resize", refresh, { passive: true });
497
- owner.visualViewport?.addEventListener("scroll", refresh, { passive: true });
498
- measure();
499
- return { refresh, destroy() {
500
- pointerTarget.removeEventListener("pointermove", onPointermove);
501
- owner.removeEventListener("resize", refresh);
502
- owner.removeEventListener("scroll", refresh, true);
503
- owner.visualViewport?.removeEventListener("resize", refresh);
504
- owner.visualViewport?.removeEventListener("scroll", refresh);
505
- if (frame !== null) owner.cancelAnimationFrame(frame);
506
- clear();
507
- } };
508
- }
509
- function controller5(host) {
510
- const element = host.element;
511
- let activeControlKey = null;
512
- const proximity = createProximity(element);
513
- const boundaries = (axis) => {
514
- const geometry = host.state.geometry;
515
- const values = geometry?.[axis === "row" ? "rowBoundaries" : "columnBoundaries"];
516
- if (Array.isArray(values) && values.length >= 2 && values.every(Number.isFinite)) return values;
517
- const rect = element.getBoundingClientRect();
518
- return fallbackBoundaries(3, axis === "row" ? rect.height : rect.width);
519
- };
520
- const insertion = (axis, index, position) => {
521
- const first = index === 0;
522
- const noun = axis === "row" ? "row" : "column";
523
- const action = axis === "row" ? first ? "add-row-before" : "add-row-after" : first ? "add-column-before" : "add-column-after";
524
- const relation = first ? axis === "row" ? "above" : "left" : axis === "row" ? "below" : "right";
525
- const label = `Insert ${noun} ${relation}`;
526
- const key = `${axis}:${index}`;
527
- return `<div class="ui-editor-table-overlay__control ui-editor-table-overlay__control--${axis}" style="${axis === "row" ? "top" : "left"}:${position}px" data-control-key="${key}" data-active="${activeControlKey === key}"><span class="ui-editor-table-overlay__line" aria-hidden="true"></span><span class="ui-editor-table-overlay__guide" data-ui-guide aria-hidden="true"></span><button type="button" class="ui-editor-table-overlay__handle" data-ui-affordance="insert-${noun}" data-action="${action}" data-boundary-index="${index}" aria-label="${label}">${icon("plus")}<span class="ui-editor-table-overlay__tooltip" role="tooltip">${label}</span></button></div>`;
528
- };
529
- const selectors = (cell) => {
530
- const indexes = `data-row-index="${cell.rowIndex}" data-column-index="${cell.columnIndex}"`;
531
- return `<button type="button" class="ui-editor-table-overlay__selector ui-editor-table-overlay__selector--row" style="top:${cell.top + cell.height / 2}px" data-action="select-row" ${indexes} title="Select row" aria-label="Select row">${icon("grip-vertical")}</button><button type="button" class="ui-editor-table-overlay__selector ui-editor-table-overlay__selector--column" style="left:${cell.left + cell.width / 2}px" data-action="select-column" ${indexes} title="Select column" aria-label="Select column">${icon("grip-horizontal")}</button>`;
532
- };
533
- const menu = (cell) => `<button type="button" class="ui-editor-table-overlay__cell-menu" style="left:${cell.left + cell.width - 30}px;top:${cell.top + 6}px" data-action="open-cell-menu" data-row-index="${cell.rowIndex}" data-column-index="${cell.columnIndex}" title="Cell actions" aria-label="Cell actions">${icon("chevron-down")}</button>`;
534
- const render = () => {
535
- element.hidden = !host.state.open;
536
- if (!host.state.open) {
537
- activeControlKey = null;
538
- return;
539
- }
540
- const rowBoundaries = boundaries("row");
541
- const columnBoundaries = boundaries("column");
542
- const geometry = host.state.geometry;
543
- const active = geometry?.activeCell ?? null;
544
- const hovered = geometry?.hoveredCell ?? null;
545
- element.innerHTML = `<div class="ui-editor-table-overlay" aria-label="Table controls"><div class="ui-editor-table-overlay__rows">${rowBoundaries.map((position, index) => insertion("row", index, position)).join("")}</div><div class="ui-editor-table-overlay__cols">${columnBoundaries.map((position, index) => insertion("col", index, position)).join("")}</div>${hovered ? selectors(hovered) : ""}${active ? menu(active) : ""}</div>`;
546
- proximity.refresh();
547
- };
548
- const setActive = (key) => {
549
- if (activeControlKey === key) return;
550
- activeControlKey = key;
551
- for (const control of element.querySelectorAll("[data-control-key]")) control.dataset.active = String(control.dataset.controlKey === key);
552
- };
553
- const onClick = (event) => {
554
- const button = event.target.closest?.("button[data-action]");
555
- if (!button) return;
556
- const action = button.dataset.action;
557
- if (action === "select-row" || action === "select-column") {
558
- host.dispatch("action", { action, rowIndex: Number(button.dataset.rowIndex), columnIndex: Number(button.dataset.columnIndex) });
559
- return;
560
- }
561
- if (action === "open-cell-menu") {
562
- const rect = button.getBoundingClientRect();
563
- host.dispatch("action", { action, rowIndex: Number(button.dataset.rowIndex), columnIndex: Number(button.dataset.columnIndex), anchor: { left: rect.left, top: rect.top, right: rect.right, bottom: rect.bottom } });
564
- return;
565
- }
566
- host.dispatch("action", { action, boundaryIndex: Number(button.dataset.boundaryIndex) });
567
- };
568
- const enter = (event) => setActive(event.target.closest?.("[data-control-key]")?.dataset.controlKey ?? null);
569
- const leave = (event) => setActive(event.relatedTarget instanceof Element ? event.relatedTarget.closest?.("[data-control-key]")?.dataset.controlKey ?? null : null);
570
- element.addEventListener("click", onClick);
571
- element.addEventListener("pointerover", enter);
572
- element.addEventListener("pointerout", leave);
573
- element.addEventListener("focusin", enter);
574
- element.addEventListener("focusout", leave);
575
- const stop = host.effect(render);
576
- render();
577
- return () => {
578
- stop();
579
- proximity.destroy();
580
- element.removeEventListener("click", onClick);
581
- element.removeEventListener("pointerover", enter);
582
- element.removeEventListener("pointerout", leave);
583
- element.removeEventListener("focusin", enter);
584
- element.removeEventListener("focusout", leave);
585
- };
586
- }
587
-
588
- // src/declarative/components/controllers/ui-editor-table-toolbar.js
589
- var ui_editor_table_toolbar_exports = {};
590
- __export(ui_editor_table_toolbar_exports, {
591
- default: () => controller6
592
- });
593
- var overflowSections = [
594
- ["Structure", [["add-row-before", "Add row above", "panel-top"], ["add-column-before", "Add column left", "panel-left"]]],
595
- ["Cells", [["clear-cells", "Clear selected cells", "eraser"], ["merge-cells", "Merge selected cells", "merge"], ["split-cell", "Split merged cell", "split"]]],
596
- ["Table", [["delete-row", "Delete row", "trash", "danger"], ["delete-column", "Delete column", "trash", "danger"], ["delete-table", "Delete table", "trash", "danger"]]]
597
- ];
598
- function controller6(host) {
599
- const element = host.element;
600
- let overflowOpen = false;
601
- const render = () => {
602
- element.hidden = !host.state.open;
603
- if (!host.state.open) {
604
- overflowOpen = false;
605
- return;
606
- }
607
- const enabled = new Set(Array.isArray(host.state.actions) ? host.state.actions : []);
608
- const active = `align-${host.state.cellAlignment === "center" || host.state.cellAlignment === "right" ? host.state.cellAlignment : "left"}`;
609
- const alignments = [["align-left", "Align left"], ["align-center", "Align center"], ["align-right", "Align right"]].filter(([action]) => enabled.has(action));
610
- const structure = [["add-row-after", "Add row", "rows"], ["add-column-after", "Add column", "columns"]].filter(([action]) => enabled.has(action));
611
- const background = String(host.state.cellBackground ?? "");
612
- const backgroundActions = backgrounds.filter(([action]) => enabled.has(action));
613
- const overflowGroups = overflowSections.map(([heading, actions]) => [heading, actions.filter(([action]) => enabled.has(action))]);
614
- const hasOverflow = backgroundActions.length > 0 || overflowGroups.some(([, actions]) => actions.length > 0);
615
- if (!hasOverflow) overflowOpen = false;
616
- const menu = overflowOpen ? `<div class="ui-editor-table-toolbar__menu" role="menu" aria-label="More table actions">${backgroundActions.length ? `<div class="ui-editor-table-toolbar__menu-section" role="none"><div class="ui-editor-table-toolbar__menu-heading" role="presentation">Background</div><div class="ui-editor-table-toolbar__swatches" role="group" aria-label="Cell background">${backgroundActions.map(([action, label, swatch]) => {
617
- const selected = swatch === background;
618
- return `<button type="button" class="ui-editor-table-toolbar__swatch-button" role="menuitemradio" aria-checked="${selected}" data-action="${action}"${selected ? ' data-selected="true"' : ""}><span class="ui-editor-table-toolbar__swatch${swatch ? "" : " ui-editor-table-toolbar__swatch--default"}"${swatch ? ` style="--ui-editor-table-toolbar-swatch:${swatch}"` : ""}></span><span>${label}</span></button>`;
619
- }).join("")}</div></div>` : ""}${overflowGroups.map(([heading, actions]) => actions.length ? `<div class="ui-editor-table-toolbar__menu-section ui-editor-table-toolbar__menu-section--divided" role="none"><div class="ui-editor-table-toolbar__menu-heading" role="presentation">${heading}</div>${actions.map(([action, label, iconName, tone]) => `<button type="button" role="menuitem" data-action="${action}"${tone ? ` data-tone="${tone}"` : ""}>${icon(iconName)}<span>${label}</span></button>`).join("")}</div>` : "").join("")}</div>` : "";
620
- const groups = [];
621
- if (alignments.length) groups.push(`<div class="ui-editor-table-toolbar__group" role="group" aria-label="Cell alignment">${alignments.map(([action, label]) => `<button type="button" class="ui-editor-table-toolbar__icon-button" data-action="${action}" aria-label="${label}" title="${label}" aria-pressed="${action === active}" data-active="${action === active}">${icon(action)}</button>`).join("")}</div>`);
622
- if (structure.length) groups.push(`<div class="ui-editor-table-toolbar__group" role="group" aria-label="Table structure">${structure.map(([action, label, iconName]) => `<button type="button" data-action="${action}">${icon(iconName)}<span>${label}</span></button>`).join("")}</div>`);
623
- if (hasOverflow) groups.push(`<div class="ui-editor-table-toolbar__overflow"><button type="button" class="ui-editor-table-toolbar__more" data-action="toggle-overflow" aria-haspopup="menu" aria-expanded="${overflowOpen}">${icon("table")}<span>Table options</span>${icon("chevron-down", "looma-icon looma-icon--chevron")}</button>${menu}</div>`);
624
- element.innerHTML = `<div class="ui-editor-table-toolbar" role="toolbar" aria-label="Table actions">${groups.join('<div class="ui-editor-table-toolbar__sep" aria-hidden="true"></div>')}</div>`;
625
- };
626
- const onClick = (event) => {
627
- const action = event.target.closest?.("[data-action]")?.dataset.action;
628
- if (!action) return;
629
- if (action === "toggle-overflow") {
630
- overflowOpen = !overflowOpen;
631
- render();
632
- return;
633
- }
634
- overflowOpen = false;
635
- render();
636
- host.dispatch("action", { action });
637
- };
638
- const onOutside = (event) => {
639
- if (!overflowOpen || event.composedPath().includes(element)) return;
640
- overflowOpen = false;
641
- render();
642
- };
643
- element.addEventListener("click", onClick);
644
- document.addEventListener("pointerdown", onOutside, true);
645
- const stop = host.effect(render);
646
- render();
647
- return () => {
648
- stop();
649
- element.removeEventListener("click", onClick);
650
- document.removeEventListener("pointerdown", onOutside, true);
651
- };
652
- }
653
-
654
- // src/declarative/components/controllers/ui-editor-toolbar.js
655
- var ui_editor_toolbar_exports = {};
656
- __export(ui_editor_toolbar_exports, {
657
- default: () => controller7
658
- });
659
- function controller7(host) {
660
- const element = host.element;
661
- if (!element.hasAttribute("role")) element.setAttribute("role", "toolbar");
662
- if (!element.hasAttribute("aria-label")) element.setAttribute("aria-label", "Editor toolbar");
663
- }
664
-
665
- // src/declarative/registry.js
666
- var records = [
667
- { tag: "ui-editor-insert-table-grid", source: '<template component="ui-editor-insert-table-grid" status="early" summary="Looma ui-editor-insert-table-grid editor surface." controller="./controllers/ui-editor-insert-table-grid.js">\n <defs>\n <prop name="open" type="boolean" default="false">open value.</prop>\n <prop name="maxRows" type="integer" default="8">maxRows value.</prop>\n <prop name="maxCols" type="integer" default="8">maxCols value.</prop>\n <prop name="headerRow" type="boolean" default="false">Whether table insertion initially includes a header row.</prop>\n <event name="insert" type="object({ rows: integer, cols: integer, withHeaderRow: boolean })"></event>\n </defs>\n <div></div>\n</template>\n', controller: ui_editor_insert_table_grid_exports },
668
- { tag: "ui-editor-mention-menu", source: '<template component="ui-editor-mention-menu" status="early" summary="Looma ui-editor-mention-menu editor surface." controller="./controllers/ui-editor-mention-menu.js">\n <defs>\n <prop name="open" type="boolean" default="false">open value.</prop>\n <prop name="query" type="string" default="">query value.</prop>\n <prop name="items" type="list(object({ id: string, label: string, detail?: string, initials?: string }))">items value.</prop>\n <prop name="selectedIndex" type="integer" default="0">selectedIndex value.</prop>\n <prop name="loading" type="boolean" default="false">loading value.</prop>\n <prop name="anchorRect" type="object({ left?: number, top?: number, right?: number, bottom?: number, x?: number, y?: number, width?: number, height?: number }) | null">anchorRect value.</prop>\n <event name="highlight" type="object({ index: integer })"></event>\n <event name="select" type="object({ index: integer })"></event>\n </defs>\n <div></div>\n</template>\n', controller: ui_editor_mention_menu_exports },
669
- { tag: "ui-editor-slash-menu", source: '<template component="ui-editor-slash-menu" status="early" summary="Looma ui-editor-slash-menu editor surface." controller="./controllers/ui-editor-slash-menu.js">\n <defs>\n <prop name="open" type="boolean" default="false">open value.</prop>\n <prop name="query" type="string" default="">query value.</prop>\n <prop name="items" type="list(object({ title: string, description: string, icon: string }))">items value.</prop>\n <prop name="selectedIndex" type="integer" default="0">selectedIndex value.</prop>\n <prop name="anchorRect" type="object({ left?: number, top?: number, right?: number, bottom?: number, x?: number, y?: number, width?: number, height?: number }) | null">anchorRect value.</prop>\n <event name="highlight" type="object({ index: integer })"></event>\n <event name="select" type="object({ index: integer })"></event>\n </defs>\n <div></div>\n</template>\n', controller: ui_editor_slash_menu_exports },
670
- { tag: "ui-editor-table-context-menu", source: '<template component="ui-editor-table-context-menu" status="early" summary="Presents the table commands an editor explicitly enables for the current cell selection." controller="./controllers/ui-editor-table-context-menu.js">\n <defs>\n <prop name="open" type="boolean" default="false">Shows the menu at the position owned by the editor integration.</prop>\n <prop name="cellBackground" type="string" default="">Identifies the background choice that is active for the current cell.</prop>\n <prop name="actions" type="list(align-left | align-center | align-right | background-none | background-gray | background-yellow | background-blue | background-green | background-red | add-row-before | add-row-after | add-column-before | add-column-after | clear-cells | merge-cells | split-cell | delete-row | delete-column | delete-table)">The complete set of commands the current selection permits; omitted commands are not rendered.</prop>\n <event name="action" type="object({ action: string })"></event>\n </defs>\n <div></div>\n</template>\n', controller: ui_editor_table_context_menu_exports },
671
- { tag: "ui-editor-table-overlay", source: '<template component="ui-editor-table-overlay" status="early" summary="Projects editor-measured table geometry into insertion, selection, and cell-action controls without owning document mutations." controller="./controllers/ui-editor-table-overlay.js">\n <defs>\n <prop name="open" type="boolean" default="false">Shows the controls after the editor has supplied current geometry.</prop>\n <prop name="geometry" type="object({ rowBoundaries: list(number), columnBoundaries: list(number), activeCell: object({ left: number, top: number, width: number, height: number, rowIndex: integer, columnIndex: integer }) | null, hoveredCell?: object({ left: number, top: number, width: number, height: number, rowIndex: integer, columnIndex: integer }) | null }) | null">The authoritative editor-measured table boundaries and active or hovered cell rectangles.</prop>\n <event name="action" type="object({ action: string, boundaryIndex?: integer, rowIndex?: integer, columnIndex?: integer, anchor?: object({ left: number, top: number, right: number, bottom: number }) })"></event>\n </defs>\n <div></div>\n</template>\n', controller: ui_editor_table_overlay_exports },
672
- { tag: "ui-editor-table-toolbar", source: '<template component="ui-editor-table-toolbar" status="early" summary="Offers selection-aware table commands without deciding which mutations the editor performs." controller="./controllers/ui-editor-table-toolbar.js">\n <defs>\n <prop name="open" type="boolean" default="false">Shows the toolbar while a table selection is active.</prop>\n <prop name="cellAlignment" type="left | center | right" default="left">Marks the alignment command that is active for the current cell.</prop>\n <prop name="cellBackground" type="string" default="">Identifies the background choice that is active for the current cell.</prop>\n <prop name="actions" type="list(align-left | align-center | align-right | background-none | background-gray | background-yellow | background-blue | background-green | background-red | add-row-before | add-row-after | add-column-before | add-column-after | clear-cells | merge-cells | split-cell | delete-row | delete-column | delete-table)">The complete set of commands the current selection permits; omitted commands are not rendered.</prop>\n <event name="action" type="object({ action: string })"></event>\n </defs>\n <div></div>\n</template>\n', controller: ui_editor_table_toolbar_exports },
673
- { tag: "ui-editor-toolbar", source: '<template component="ui-editor-toolbar" status="early" summary="Looma ui-editor-toolbar editor surface." controller="./controllers/ui-editor-toolbar.js">\n <defs>\n\n </defs>\n <div><slot></slot></div>\n</template>\n', controller: ui_editor_toolbar_exports }
674
- ];
675
- var styles = `/**
676
- * Looma editor styles (tokens-based).
677
- * Tables, context menu, insert-table grid, slash menu. Use with ProseMirror/Tiptap content.
678
- */
679
-
680
- /* Turnkey editor */
681
- .looma-editor {
682
- position: relative;
683
- min-width: 0;
684
- color: var(--ui-text-primary);
685
- font-family: var(--ui-font-family-sans);
686
- }
687
-
688
- .looma-editor .ProseMirror {
689
- min-height: var(--ui-editor-content-min-height, 300px);
690
- padding:
691
- var(--ui-editor-content-padding-block, var(--ui-space-4))
692
- var(--ui-editor-content-padding-inline, var(--ui-space-6));
693
- outline: none;
694
- color: var(--ui-editor-content-color, var(--ui-text-primary));
695
- caret-color: var(--ui-editor-caret-color, var(--ui-accent-solid));
696
- font-family: var(--ui-editor-font-family, var(--ui-font-family-sans));
697
- font-size: var(--ui-editor-font-size, var(--ui-font-size-md));
698
- line-height: var(--ui-editor-line-height, var(--ui-line-height-relaxed));
699
- }
700
-
701
- @media (max-width: 767px) {
702
- .looma-editor .ProseMirror {
703
- padding-bottom: var(--ui-editor-mobile-bottom-space, 80px);
704
- }
705
- }
706
-
707
- .looma-editor .ProseMirror p {
708
- margin: 0 0 var(--ui-editor-paragraph-gap, var(--ui-space-3));
709
- }
710
-
711
- .looma-editor .ProseMirror h1,
712
- .looma-editor .ProseMirror h2,
713
- .looma-editor .ProseMirror h3 {
714
- color: inherit;
715
- line-height: var(--ui-line-height-tight);
716
- }
717
-
718
- .looma-editor .ProseMirror h1 {
719
- margin: var(--ui-space-6) 0 var(--ui-space-3);
720
- font-size: var(--ui-font-size-2xl);
721
- font-weight: var(--ui-font-bold);
722
- }
723
-
724
- .looma-editor .ProseMirror h2 {
725
- margin: var(--ui-space-5) 0 var(--ui-space-2);
726
- font-size: var(--ui-font-size-xl);
727
- font-weight: var(--ui-font-semibold);
728
- }
729
-
730
- .looma-editor .ProseMirror h3 {
731
- margin: var(--ui-space-4) 0 var(--ui-space-2);
732
- font-size: var(--ui-font-size-lg);
733
- font-weight: var(--ui-font-semibold);
734
- }
735
-
736
- .looma-editor .ProseMirror ul,
737
- .looma-editor .ProseMirror ol {
738
- margin: 0 0 var(--ui-space-3);
739
- /* Nested lists retain the browser's hierarchy; this is only the root inset. */
740
- padding-inline-start: var(--ui-editor-list-inset, var(--ui-space-5));
741
- }
742
-
743
- .looma-editor .ProseMirror li {
744
- margin-bottom: var(--ui-space-1);
745
- }
746
-
747
- .looma-editor .ProseMirror blockquote {
748
- margin: var(--ui-space-4) 0;
749
- padding-left: var(--ui-space-4);
750
- border-left: 3px solid var(--ui-accent-solid);
751
- color: var(--ui-text-secondary);
752
- font-style: italic;
753
- }
754
-
755
- .looma-editor .ProseMirror aside[data-looma-callout][data-tone] {
756
- --ui-callout-padding-block: var(--ui-space-2);
757
- --ui-callout-padding-inline: var(--ui-space-3);
758
- --ui-callout-icon-column: 1rem;
759
- --ui-callout-gap: var(--ui-space-2);
760
- --ui-callout-icon-size: 1rem;
761
- --ui-callout-border-width: 1px;
762
- --ui-callout-leading-border-width: 4px;
763
- --ui-callout-surface: var(--ui-accent-soft);
764
- --ui-callout-text: var(--ui-text-primary);
765
- --ui-callout-border: var(--ui-accent-solid);
766
- --ui-callout-icon: var(--ui-accent-solid);
767
- --ui-editor-callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M13.4 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-7.4'/%3E%3Cpath d='M2 6h4'/%3E%3Cpath d='M2 10h4'/%3E%3Cpath d='M2 14h4'/%3E%3Cpath d='M2 18h4'/%3E%3Cpath d='M21.378 5.626a1 1 0 1 0-3.004-3.004l-5.01 5.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z'/%3E%3C/svg%3E");
768
-
769
- display: grid;
770
- grid-template-columns: var(--ui-callout-icon-column) minmax(0, 1fr);
771
- align-items: start;
772
- column-gap: var(--ui-callout-gap);
773
- margin: var(--ui-space-4) 0;
774
- padding: var(--ui-callout-padding-block) var(--ui-callout-padding-inline);
775
- border: var(--ui-callout-border-width) solid color-mix(in srgb, var(--ui-callout-border) 35%, transparent);
776
- border-inline-start: var(--ui-callout-leading-border-width) solid var(--ui-callout-border);
777
- border-radius: var(--ui-radius-2);
778
- background: var(--ui-callout-surface);
779
- color: var(--ui-callout-text);
780
- }
781
-
782
- .looma-editor .ProseMirror aside[data-looma-callout][data-tone]::before {
783
- width: var(--ui-callout-icon-size);
784
- height: var(--ui-callout-icon-size);
785
- margin-top: 0.2em;
786
- background: var(--ui-callout-icon);
787
- content: "";
788
- mask: var(--ui-editor-callout-icon) center / contain no-repeat;
789
- -webkit-mask: var(--ui-editor-callout-icon) center / contain no-repeat;
790
- }
791
-
792
- .looma-editor .ProseMirror aside[data-looma-callout][data-tone] > * {
793
- grid-column: 2;
794
- }
795
-
796
- .looma-editor .ProseMirror aside[data-looma-callout][data-tone] > :last-child {
797
- margin-bottom: 0;
798
- }
799
-
800
- .looma-editor .ProseMirror aside[data-looma-callout][data-tone="info"] {
801
- --ui-callout-surface: var(--ui-info-soft);
802
- --ui-callout-border: var(--ui-info-solid);
803
- --ui-callout-icon: var(--ui-info-solid);
804
- --ui-editor-callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 16v-4'/%3E%3Cpath d='M12 8h.01'/%3E%3C/svg%3E");
805
- }
806
-
807
- .looma-editor .ProseMirror aside[data-looma-callout][data-tone="note"] {
808
- --ui-callout-surface: var(--ui-accent-soft);
809
- --ui-callout-border: var(--ui-accent-solid);
810
- --ui-callout-icon: var(--ui-accent-solid);
811
- }
812
-
813
- .looma-editor .ProseMirror aside[data-looma-callout][data-tone="warning"] {
814
- --ui-callout-surface: var(--ui-warning-soft);
815
- --ui-callout-border: var(--ui-warning-solid);
816
- --ui-callout-icon: var(--ui-warning-solid);
817
- --ui-editor-callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m21.73 18-8-14a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3'/%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E%3C/svg%3E");
818
- }
819
-
820
- .looma-editor .ProseMirror pre {
821
- margin: var(--ui-space-4) 0;
822
- padding: var(--ui-space-4);
823
- overflow-x: auto;
824
- border: 1px solid var(--ui-border-default);
825
- border-radius: var(--ui-radius-2);
826
- background: var(--ui-surface-muted);
827
- font-family: var(--ui-font-family-mono, ui-monospace, monospace);
828
- font-size: var(--ui-font-size-sm);
829
- }
830
-
831
- .looma-editor .ProseMirror code {
832
- padding: 0.1em 0.3em;
833
- border-radius: var(--ui-radius-1);
834
- background: var(--ui-surface-subtle);
835
- font-family: var(--ui-font-family-mono, ui-monospace, monospace);
836
- font-size: 0.9em;
837
- }
838
-
839
- .looma-editor .ProseMirror pre code {
840
- padding: 0;
841
- background: none;
842
- }
843
-
844
- .looma-editor .ProseMirror hr {
845
- margin: var(--ui-space-6) 0;
846
- border: 0;
847
- border-top: 1px solid var(--ui-border-default);
848
- }
849
-
850
- .looma-editor .ProseMirror mark {
851
- border-radius: var(--ui-radius-1);
852
- background: var(--ui-editor-highlight-bg, var(--ui-warning-soft));
853
- }
854
-
855
- .looma-editor .ProseMirror a {
856
- color: var(--ui-editor-link-color, var(--ui-accent-solid));
857
- text-decoration: underline;
858
- }
859
-
860
- .looma-editor .ProseMirror ul[data-type="taskList"] {
861
- padding-left: var(--ui-space-2);
862
- list-style: none;
863
- }
864
-
865
- .looma-editor .ProseMirror ul[data-type="taskList"] li {
866
- display: flex;
867
- align-items: flex-start;
868
- gap: var(--ui-space-2);
869
- }
870
-
871
- .looma-editor .ProseMirror ul[data-type="taskList"] li > label {
872
- margin-top: 3px;
873
- }
874
-
875
- .looma-editor .ProseMirror ul[data-type="taskList"] input[type="checkbox"] {
876
- width: 16px;
877
- height: 16px;
878
- accent-color: var(--ui-accent-solid);
879
- cursor: pointer;
880
- }
881
-
882
- .looma-editor .ProseMirror .is-editor-empty:first-child::before {
883
- float: left;
884
- height: 0;
885
- color: var(--ui-text-muted);
886
- content: attr(data-placeholder);
887
- pointer-events: none;
888
- }
889
-
890
- .looma-editor .ProseMirror img {
891
- max-width: 100%;
892
- height: auto;
893
- margin: var(--ui-space-3) 0;
894
- border-radius: var(--ui-radius-2);
895
- }
896
-
897
- .looma-editor .ProseMirror img[data-looma-image]:focus-visible {
898
- outline: 2px solid var(--ui-focus-ring);
899
- outline-offset: 3px;
900
- }
901
-
902
- .looma-editor__file-input {
903
- position: absolute;
904
- width: 0;
905
- height: 0;
906
- opacity: 0;
907
- pointer-events: none;
908
- }
909
-
910
- .looma-editor__upload-error {
911
- display: flex;
912
- align-items: center;
913
- justify-content: space-between;
914
- gap: var(--ui-space-3);
915
- margin-top: var(--ui-space-3);
916
- padding: var(--ui-space-2) var(--ui-space-3);
917
- border: 1px solid var(--ui-danger-solid);
918
- border-radius: var(--ui-radius-2);
919
- background: var(--ui-danger-soft);
920
- color: var(--ui-text);
921
- font-size: var(--ui-font-size-sm);
922
- }
923
-
924
- .looma-editor__upload-error button {
925
- min-height: 2rem;
926
- padding: 0 var(--ui-space-3);
927
- border: 0;
928
- border-radius: var(--ui-radius-1);
929
- background: var(--ui-danger-solid);
930
- color: var(--ui-text-on-accent);
931
- cursor: pointer;
932
- font: inherit;
933
- font-weight: var(--ui-font-semibold);
934
- }
935
-
936
- .looma-editor__upload-error button:focus-visible {
937
- outline: 2px solid var(--ui-focus-ring);
938
- outline-offset: 2px;
939
- }
940
-
941
- .looma-editor__upload-error button:disabled {
942
- cursor: wait;
943
- opacity: 0.65;
944
- }
945
-
946
- .looma-editor__drop-overlay {
947
- position: absolute;
948
- z-index: var(--ui-editor-drop-overlay-z-index, 10);
949
- inset: 0;
950
- display: flex;
951
- flex-direction: column;
952
- align-items: center;
953
- justify-content: center;
954
- gap: var(--ui-space-3);
955
- border: 2px dashed var(--ui-accent-solid);
956
- border-radius: var(--ui-radius-2);
957
- background: color-mix(in srgb, var(--ui-accent-soft) 92%, transparent);
958
- color: var(--ui-accent-active);
959
- font-size: var(--ui-font-size-sm);
960
- font-weight: var(--ui-font-semibold);
961
- pointer-events: none;
962
- }
963
-
964
- .looma-editor__drop-overlay > .looma-icon {
965
- width: var(--ui-font-size-2xl);
966
- height: var(--ui-font-size-2xl);
967
- stroke-width: var(--ui-editor-toolbar-icon-stroke-width, 1.75);
968
- }
969
-
970
- .looma-editor__toolbar-button {
971
- --ui-icon-button-size-sm: var(--ui-editor-toolbar-button-size, 2rem);
972
- --ui-icon-button-radius: var(--ui-editor-toolbar-button-radius, var(--ui-radius-1));
973
- --ui-icon-button-icon-size: var(--ui-editor-toolbar-icon-size, 1rem);
974
- }
975
-
976
- .looma-editor__toolbar-button > .looma-icon {
977
- width: var(--ui-editor-toolbar-icon-size, 1rem);
978
- height: var(--ui-editor-toolbar-icon-size, 1rem);
979
- stroke-width: var(--ui-editor-toolbar-icon-stroke-width, 1.75);
980
- }
981
-
982
- .looma-editor__sticky-toolbar-shell {
983
- position: sticky;
984
- top: var(--ui-editor-sticky-toolbar-top, 0);
985
- z-index: var(--ui-editor-toolbar-z-index, 10);
986
- min-width: 0;
987
- background: var(--ui-editor-toolbar-bg, var(--ui-surface, #fff));
988
- border-bottom: 1px solid var(--ui-editor-toolbar-border, var(--ui-border, #e5e7eb));
989
- }
990
-
991
- .looma-editor__sticky-toolbar-shell [data-component-root~="ui-editor-toolbar"] {
992
- display: flex;
993
- align-items: center;
994
- justify-content: flex-start;
995
- gap: var(--ui-editor-toolbar-gap, 2px);
996
- min-height: var(--ui-editor-toolbar-min-height, 48px);
997
- padding:
998
- var(--ui-editor-toolbar-padding-block, 6px)
999
- var(--ui-editor-toolbar-padding-inline, 12px);
1000
- overflow-x: auto;
1001
- overscroll-behavior-inline: contain;
1002
- scrollbar-width: none;
1003
- white-space: nowrap;
1004
- }
1005
-
1006
- .looma-editor__sticky-toolbar-shell [data-component-root~="ui-editor-toolbar"]::-webkit-scrollbar {
1007
- display: none;
1008
- }
1009
-
1010
- .looma-editor__sticky-toolbar-shell [data-component-root~="ui-editor-toolbar"] > * {
1011
- flex: 0 0 auto;
1012
- }
1013
-
1014
- .looma-editor__table-picker-popover,
1015
- .looma-editor__table-toolbar-shell,
1016
- .looma-editor__table-menu-shell {
1017
- z-index: var(--ui-editor-popover-z-index, 300);
1018
- }
1019
-
1020
- .looma-editor__table-picker-popover {
1021
- min-width: 220px;
1022
- padding: 0;
1023
- border: 0;
1024
- background: transparent;
1025
- }
1026
-
1027
- .looma-editor__table-toolbar-shell,
1028
- .looma-editor__table-menu-shell,
1029
- .looma-editor__table-overlay-shell {
1030
- position: fixed;
1031
- }
1032
-
1033
- .looma-editor__mobile-toolbar-shell {
1034
- position: fixed;
1035
- z-index: var(--ui-editor-mobile-toolbar-z-index, var(--ui-editor-popover-z-index, 300));
1036
- display: flex;
1037
- align-items: stretch;
1038
- min-height: var(--ui-editor-toolbar-mobile-min-height, 56px);
1039
- padding:
1040
- var(--ui-editor-mobile-toolbar-padding-block, 4px)
1041
- max(var(--ui-editor-mobile-toolbar-padding-inline, 8px), env(safe-area-inset-left));
1042
- overflow-x: auto;
1043
- overflow-y: hidden;
1044
- overscroll-behavior-inline: contain;
1045
- -webkit-overflow-scrolling: touch;
1046
- scroll-snap-type: x mandatory;
1047
- scrollbar-width: none;
1048
- background: var(--ui-editor-toolbar-bg, var(--ui-surface, #fff));
1049
- border-top: 1px solid var(--ui-editor-toolbar-border, var(--ui-border, #e5e7eb));
1050
- box-shadow: var(--ui-editor-toolbar-mobile-shadow, 0 -2px 8px rgb(0 0 0 / 0.06));
1051
- }
1052
-
1053
- .looma-editor__mobile-toolbar-shell::-webkit-scrollbar {
1054
- display: none;
1055
- }
1056
-
1057
- .looma-editor__mobile-toolbar-shell > *,
1058
- .looma-editor__mobile-toolbar-shell button,
1059
- .looma-editor__mobile-toolbar-shell [data-component-root~="ui-icon-button"] {
1060
- flex: 0 0 auto;
1061
- scroll-snap-align: start;
1062
- }
1063
-
1064
- .looma-editor__mobile-toolbar-shell [data-component-root~="ui-editor-toolbar"][floating] {
1065
- width: 100%;
1066
- max-width: none;
1067
- min-height: 100%;
1068
- padding: 0;
1069
- overflow-x: auto;
1070
- overflow-y: hidden;
1071
- overscroll-behavior-inline: contain;
1072
- -webkit-overflow-scrolling: touch;
1073
- scroll-snap-type: x mandatory;
1074
- scrollbar-width: none;
1075
- background: transparent;
1076
- border: 0;
1077
- border-radius: 0;
1078
- box-shadow: none;
1079
- backdrop-filter: none;
1080
- }
1081
-
1082
- .looma-editor__mobile-toolbar-shell [data-component-root~="ui-editor-toolbar"][floating]::-webkit-scrollbar {
1083
- display: none;
1084
- }
1085
-
1086
- .looma-editor__mobile-toolbar-shell .looma-editor__toolbar-button {
1087
- --ui-icon-button-size-sm: var(--ui-editor-toolbar-mobile-button-size, 44px);
1088
- }
1089
-
1090
- .looma-editor__mobile-toolbar-back {
1091
- display: inline-flex;
1092
- align-items: center;
1093
- justify-content: center;
1094
- gap: var(--ui-space-1-5, 6px);
1095
- min-width: 44px;
1096
- min-height: 44px;
1097
- padding: 0 var(--ui-space-3, 12px);
1098
- border: 0;
1099
- border-radius: var(--ui-radius-md, 6px);
1100
- background: transparent;
1101
- color: var(--ui-text-secondary, #4b5563);
1102
- cursor: pointer;
1103
- font: inherit;
1104
- white-space: nowrap;
1105
- }
1106
-
1107
- .looma-editor__mobile-toolbar-back .looma-icon {
1108
- width: 1rem;
1109
- height: 1rem;
1110
- stroke-width: var(--ui-editor-toolbar-icon-stroke-width, 1.75);
1111
- }
1112
-
1113
- .looma-editor__mobile-toolbar-back:is(:hover, :focus-visible) {
1114
- background: var(--ui-surface-hover, #f3f4f6);
1115
- color: var(--ui-text-primary, #111827);
1116
- }
1117
-
1118
- .looma-editor__mobile-toolbar-shell .ui-editor-table-toolbar {
1119
- min-width: max-content;
1120
- min-height: 100%;
1121
- padding: 0;
1122
- background: transparent;
1123
- border: 0;
1124
- border-radius: 0;
1125
- box-shadow: none;
1126
- backdrop-filter: none;
1127
- }
1128
-
1129
- .looma-editor__mobile-toolbar-shell .ui-editor-table-toolbar button {
1130
- min-height: 44px;
1131
- background: transparent;
1132
- }
1133
-
1134
- .looma-editor__mobile-toolbar-shell .ui-editor-table-toolbar button[data-active="true"],
1135
- .looma-editor__mobile-toolbar-shell .ui-editor-table-toolbar button[data-selected="true"] {
1136
- background: var(--ui-accent-soft, var(--ui-accent-subtle, #e8f0fb));
1137
- }
1138
-
1139
- .looma-editor__mobile-toolbar-shell .ui-editor-table-toolbar__menu {
1140
- top: auto;
1141
- right: 0;
1142
- bottom: calc(100% + var(--ui-space-2, 8px));
1143
- max-height: min(50dvh, 22rem);
1144
- overflow-y: auto;
1145
- overscroll-behavior: contain;
1146
- }
1147
-
1148
- .looma-editor__table-overlay-shell {
1149
- z-index: var(--ui-editor-overlay-z-index, 299);
1150
- overflow: visible;
1151
- pointer-events: none;
1152
- }
1153
-
1154
- /* Slash menu */
1155
- [data-component-root~="ui-editor-slash-menu"] {
1156
- user-select: none;
1157
- }
1158
-
1159
- /* Mention menu */
1160
- [data-component-root~="ui-editor-mention-menu"] {
1161
- user-select: none;
1162
- }
1163
-
1164
- .ui-editor-mention-menu {
1165
- display: flex;
1166
- overflow: hidden;
1167
- flex-direction: column;
1168
- background: var(--ui-surface, #fff);
1169
- border: 1px solid var(--ui-border, #e5e7eb);
1170
- border-radius: var(--ui-radius-xl, 1rem);
1171
- box-shadow: var(--ui-shadow-lg, 0 8px 24px rgb(0 0 0 / 0.12));
1172
- }
1173
-
1174
- .ui-editor-mention-menu__header {
1175
- display: flex;
1176
- align-items: center;
1177
- justify-content: space-between;
1178
- gap: var(--ui-space-3, 12px);
1179
- padding: var(--ui-space-2, 8px) var(--ui-space-3, 12px);
1180
- color: var(--ui-text-secondary, #4b5563);
1181
- border-bottom: 1px solid var(--ui-border, #e5e7eb);
1182
- font-size: var(--ui-font-size-xs, 0.75rem);
1183
- font-weight: var(--ui-font-semibold, 600);
1184
- }
1185
-
1186
- .ui-editor-mention-menu__hint {
1187
- overflow: hidden;
1188
- color: var(--ui-text-muted, #6b7280);
1189
- font-weight: var(--ui-font-normal, 400);
1190
- text-overflow: ellipsis;
1191
- white-space: nowrap;
1192
- }
1193
-
1194
- .ui-editor-mention-menu__list {
1195
- max-height: 280px;
1196
- padding: var(--ui-space-1, 4px) 0;
1197
- margin: 0;
1198
- overflow-y: auto;
1199
- overscroll-behavior: contain;
1200
- list-style: none;
1201
- }
1202
-
1203
- .ui-editor-mention-menu__item {
1204
- display: flex;
1205
- align-items: center;
1206
- min-height: 44px;
1207
- padding: var(--ui-space-1-5, 6px) var(--ui-space-3, 12px);
1208
- gap: var(--ui-space-3, 12px);
1209
- cursor: pointer;
1210
- }
1211
-
1212
- .ui-editor-mention-menu__item--active {
1213
- background: var(--ui-accent-subtle, rgb(59 130 246 / 0.1));
1214
- }
1215
-
1216
- .ui-editor-mention-menu__avatar {
1217
- display: inline-flex;
1218
- align-items: center;
1219
- justify-content: center;
1220
- width: 32px;
1221
- height: 32px;
1222
- flex: 0 0 auto;
1223
- color: var(--ui-accent-active, var(--ui-accent-solid, #2563eb));
1224
- background: var(--ui-accent-soft, var(--ui-accent-subtle, #eff6ff));
1225
- border-radius: 999px;
1226
- font-size: var(--ui-font-size-xs, 0.75rem);
1227
- font-weight: var(--ui-font-semibold, 600);
1228
- }
1229
-
1230
- .ui-editor-mention-menu__body {
1231
- display: flex;
1232
- min-width: 0;
1233
- flex-direction: column;
1234
- }
1235
-
1236
- .ui-editor-mention-menu__label,
1237
- .ui-editor-mention-menu__detail {
1238
- overflow: hidden;
1239
- text-overflow: ellipsis;
1240
- white-space: nowrap;
1241
- }
1242
-
1243
- .ui-editor-mention-menu__label {
1244
- color: var(--ui-text-primary, #111827);
1245
- font-size: var(--ui-font-size-sm, 0.875rem);
1246
- font-weight: var(--ui-font-medium, 500);
1247
- }
1248
-
1249
- .ui-editor-mention-menu__detail,
1250
- .ui-editor-mention-menu__loading {
1251
- color: var(--ui-text-muted, #6b7280);
1252
- font-size: var(--ui-font-size-xs, 0.75rem);
1253
- }
1254
-
1255
- .ui-editor-mention-menu__loading {
1256
- min-height: 44px;
1257
- padding: var(--ui-space-3, 12px);
1258
- }
1259
-
1260
- .looma-editor .ProseMirror .looma-mention {
1261
- padding: 0.08em 0.28em;
1262
- color: var(--ui-editor-mention-color, var(--ui-accent-active, var(--ui-accent-solid)));
1263
- background: var(--ui-editor-mention-bg, var(--ui-accent-soft, var(--ui-accent-subtle)));
1264
- border-radius: var(--ui-radius-sm, 4px);
1265
- font-weight: var(--ui-font-medium, 500);
1266
- white-space: nowrap;
1267
- }
1268
-
1269
- @media (max-width: 767px) {
1270
- .ui-editor-mention-menu {
1271
- border-bottom: 0;
1272
- border-radius: var(--ui-radius-xl, 1rem) var(--ui-radius-xl, 1rem) 0 0;
1273
- }
1274
-
1275
- .ui-editor-mention-menu__list {
1276
- max-height: calc(60dvh - 48px);
1277
- }
1278
- }
1279
-
1280
- /* Toolbar shell */
1281
- [data-component-root~="ui-editor-toolbar"] {
1282
- position: sticky;
1283
- top: 0;
1284
- z-index: var(--ui-editor-toolbar-z-index, 10);
1285
- display: flex;
1286
- align-items: center;
1287
- justify-content: flex-start;
1288
- gap: var(--ui-editor-toolbar-gap, 2px);
1289
- padding:
1290
- var(--ui-editor-toolbar-padding-block, 6px)
1291
- var(--ui-editor-toolbar-padding-inline, 12px);
1292
- min-height: var(--ui-editor-toolbar-min-height, 48px);
1293
- overflow-x: auto;
1294
- -webkit-overflow-scrolling: touch;
1295
- scrollbar-width: none;
1296
- white-space: nowrap;
1297
- background: var(--ui-editor-toolbar-bg, var(--ui-surface, #fff));
1298
- border-bottom: 1px solid var(--ui-editor-toolbar-border, var(--ui-border, #e5e7eb));
1299
- }
1300
-
1301
- [data-component-root~="ui-editor-toolbar"] > * {
1302
- flex: 0 0 auto;
1303
- }
1304
-
1305
- [data-component-root~="ui-editor-toolbar"]::-webkit-scrollbar {
1306
- display: none;
1307
- }
1308
-
1309
- [data-component-root~="ui-editor-toolbar"][floating] {
1310
- position: static;
1311
- top: auto;
1312
- width: max-content;
1313
- max-width: min(calc(100vw - 24px), 100%);
1314
- min-height: auto;
1315
- padding: var(--ui-editor-toolbar-floating-padding, 4px);
1316
- overflow-x: visible;
1317
- background: color-mix(in srgb, var(--ui-editor-toolbar-bg, var(--ui-surface, #fff)) 96%, transparent);
1318
- border: 1px solid var(--ui-editor-toolbar-border, var(--ui-border, #e5e7eb));
1319
- border-radius: var(--ui-editor-toolbar-floating-radius, var(--ui-radius-lg, 10px));
1320
- box-shadow: var(--ui-editor-toolbar-floating-shadow, var(--ui-shadow-lg, 0 10px 24px rgb(0 0 0 / 0.12)));
1321
- backdrop-filter: blur(10px);
1322
- }
1323
-
1324
- @media (max-width: 767px) {
1325
- [data-component-root~="ui-editor-toolbar"] {
1326
- position: fixed;
1327
- top: auto;
1328
- bottom: env(safe-area-inset-bottom);
1329
- left: 0;
1330
- right: 0;
1331
- min-height: var(--ui-editor-toolbar-mobile-min-height, 56px);
1332
- border-top: 1px solid var(--ui-editor-toolbar-border, var(--ui-border, #e5e7eb));
1333
- border-bottom: none;
1334
- box-shadow: var(--ui-editor-toolbar-mobile-shadow, 0 -2px 8px rgb(0 0 0 / 0.06));
1335
- }
1336
-
1337
- [data-component-root~="ui-editor-toolbar"][floating] {
1338
- position: static;
1339
- inset: auto;
1340
- min-height: auto;
1341
- border: 1px solid var(--ui-editor-toolbar-border, var(--ui-border, #e5e7eb));
1342
- border-radius: var(--ui-editor-toolbar-floating-radius, var(--ui-radius-lg, 10px));
1343
- box-shadow: var(--ui-editor-toolbar-floating-shadow, var(--ui-shadow-lg, 0 10px 24px rgb(0 0 0 / 0.12)));
1344
- }
1345
- }
1346
-
1347
- .ui-editor-toolbar__divider {
1348
- width: 1px;
1349
- height: 20px;
1350
- background: var(--ui-editor-toolbar-divider, var(--ui-border, #e5e7eb));
1351
- flex-shrink: 0;
1352
- margin: 0 var(--ui-editor-toolbar-divider-margin, 4px);
1353
- }
1354
-
1355
- .ui-editor-slash-menu {
1356
- background: var(--ui-surface, #fff);
1357
- border: 1px solid var(--ui-border, #e5e7eb);
1358
- border-radius: var(--ui-radius-xl, 1rem);
1359
- box-shadow: var(--ui-shadow-lg, 0 8px 24px rgb(0 0 0 / 0.12));
1360
- overflow: hidden;
1361
- display: flex;
1362
- flex-direction: column;
1363
- }
1364
-
1365
- @media (max-width: 767px) {
1366
- .ui-editor-slash-menu {
1367
- border-radius: var(--ui-radius-xl, 1rem) var(--ui-radius-xl, 1rem) 0 0;
1368
- border-bottom: none;
1369
- }
1370
- }
1371
-
1372
- .ui-editor-slash-menu__header {
1373
- display: none;
1374
- align-items: center;
1375
- gap: var(--ui-space-2, 8px);
1376
- padding: var(--ui-space-3, 12px) var(--ui-space-4, 16px);
1377
- border-bottom: 1px solid var(--ui-border, #e5e7eb);
1378
- font-size: var(--ui-font-size-xs, 0.75rem);
1379
- color: var(--ui-text-muted, #6b7280);
1380
- font-weight: var(--ui-font-semibold, 600);
1381
- text-transform: uppercase;
1382
- letter-spacing: 0.05em;
1383
- }
1384
-
1385
- @media (max-width: 767px) {
1386
- .ui-editor-slash-menu__header {
1387
- display: flex;
1388
- }
1389
- }
1390
-
1391
- .ui-editor-slash-menu__header-query {
1392
- background: var(--ui-surface-hover, #f3f4f6);
1393
- border-radius: var(--ui-radius-sm, 4px);
1394
- padding: 0.1em 0.4em;
1395
- font-family: var(--ui-font-family-mono, ui-monospace, monospace);
1396
- font-size: var(--ui-font-size-xs, 0.75rem);
1397
- color: var(--ui-accent, #3b82f6);
1398
- text-transform: none;
1399
- letter-spacing: 0;
1400
- }
1401
-
1402
- .ui-editor-slash-menu__list {
1403
- list-style: none;
1404
- margin: 0;
1405
- padding: var(--ui-space-1, 4px) 0;
1406
- overflow-y: auto;
1407
- max-height: 280px;
1408
- overscroll-behavior: contain;
1409
- }
1410
-
1411
- @media (max-width: 767px) {
1412
- .ui-editor-slash-menu__list {
1413
- max-height: calc(60dvh - 80px);
1414
- padding: var(--ui-space-1, 4px) 0 var(--ui-space-2, 8px);
1415
- }
1416
- }
1417
-
1418
- .ui-editor-slash-menu__item {
1419
- display: flex;
1420
- align-items: center;
1421
- gap: var(--ui-space-3, 12px);
1422
- padding: var(--ui-space-2, 8px) var(--ui-space-3, 12px);
1423
- min-height: 44px;
1424
- cursor: pointer;
1425
- transition: background-color 120ms ease;
1426
- }
1427
-
1428
- .ui-editor-slash-menu__item--active {
1429
- background: var(--ui-accent-subtle, rgb(59 130 246 / 0.1));
1430
- }
1431
-
1432
- .ui-editor-slash-menu__icon {
1433
- display: flex;
1434
- align-items: center;
1435
- justify-content: center;
1436
- width: 32px;
1437
- height: 32px;
1438
- background: var(--ui-surface-hover, #f3f4f6);
1439
- border: 1px solid var(--ui-border, #e5e7eb);
1440
- border-radius: var(--ui-radius-md, 6px);
1441
- font-size: var(--ui-font-size-sm, 0.875rem);
1442
- font-weight: var(--ui-font-bold, 700);
1443
- color: var(--ui-text-secondary, #4b5563);
1444
- font-family: var(--ui-font-family-mono, ui-monospace, monospace);
1445
- flex-shrink: 0;
1446
- line-height: 1;
1447
- }
1448
-
1449
- .ui-editor-slash-menu__item--active .ui-editor-slash-menu__icon {
1450
- background: var(--ui-accent, #3b82f6);
1451
- border-color: var(--ui-accent, #3b82f6);
1452
- color: var(--ui-accent-contrast, #fff);
1453
- }
1454
-
1455
- .ui-editor-slash-menu__icon .looma-icon {
1456
- width: 1rem;
1457
- height: 1rem;
1458
- stroke-width: var(--ui-editor-toolbar-icon-stroke-width, 1.75);
1459
- }
1460
-
1461
- .ui-editor-slash-menu__body {
1462
- display: flex;
1463
- min-width: 0;
1464
- flex-direction: column;
1465
- gap: 1px;
1466
- }
1467
-
1468
- .ui-editor-slash-menu__title {
1469
- font-size: var(--ui-font-size-sm, 0.875rem);
1470
- font-weight: var(--ui-font-medium, 500);
1471
- color: var(--ui-text, #111827);
1472
- white-space: nowrap;
1473
- overflow: hidden;
1474
- text-overflow: ellipsis;
1475
- }
1476
-
1477
- .ui-editor-slash-menu__description {
1478
- font-size: var(--ui-font-size-xs, 0.75rem);
1479
- color: var(--ui-text-muted, #6b7280);
1480
- white-space: nowrap;
1481
- overflow: hidden;
1482
- text-overflow: ellipsis;
1483
- }
1484
-
1485
- .ui-editor-slash-menu__footer {
1486
- display: none;
1487
- padding: var(--ui-space-2, 8px) var(--ui-space-3, 12px);
1488
- border-top: 1px solid var(--ui-border, #e5e7eb);
1489
- font-size: var(--ui-font-size-xs, 0.75rem);
1490
- color: var(--ui-text-muted, #6b7280);
1491
- }
1492
-
1493
- @media (min-width: 768px) {
1494
- .ui-editor-slash-menu__footer {
1495
- display: block;
1496
- }
1497
- }
1498
-
1499
- .ui-editor-slash-menu kbd {
1500
- display: inline-flex;
1501
- align-items: center;
1502
- justify-content: center;
1503
- font-family: var(--ui-font-family-mono, ui-monospace, monospace);
1504
- font-size: 10px;
1505
- background: var(--ui-surface-hover, #f3f4f6);
1506
- border: 1px solid var(--ui-border, #e5e7eb);
1507
- border-radius: 3px;
1508
- padding: 0 3px;
1509
- color: var(--ui-text-secondary, #4b5563);
1510
- }
1511
-
1512
- /* Table context menu */
1513
- .ui-editor-table-context-menu {
1514
- min-width: 14rem;
1515
- max-width: min(17rem, calc(100vw - 24px));
1516
- max-height: min(28rem, calc(100vh - 24px));
1517
- padding: var(--ui-space-1-5, 6px);
1518
- background: var(--ui-surface, #fff);
1519
- border: 1px solid var(--ui-border, #e5e7eb);
1520
- border-radius: var(--ui-radius-lg, 10px);
1521
- box-shadow: var(--ui-shadow-lg, 0 10px 15px -3px rgb(0 0 0 / 0.1));
1522
- display: flex;
1523
- flex-direction: column;
1524
- gap: 2px;
1525
- overflow-y: auto;
1526
- overscroll-behavior: contain;
1527
- scrollbar-width: thin;
1528
- }
1529
-
1530
- .ui-editor-table-context-menu__section {
1531
- display: flex;
1532
- flex-direction: column;
1533
- gap: 3px;
1534
- }
1535
-
1536
- .ui-editor-table-context-menu button {
1537
- display: flex;
1538
- align-items: center;
1539
- gap: var(--ui-space-2, 8px);
1540
- width: 100%;
1541
- text-align: left;
1542
- min-height: 32px;
1543
- padding: var(--ui-space-1-5, 6px) var(--ui-space-2, 8px);
1544
- border: none;
1545
- border-radius: var(--ui-radius-sm, 4px);
1546
- background: none;
1547
- font-size: var(--ui-font-size-sm, 0.875rem);
1548
- cursor: pointer;
1549
- color: var(--ui-text, #111);
1550
- }
1551
-
1552
- .ui-editor-table-context-menu button > .looma-icon {
1553
- width: 1rem;
1554
- height: 1rem;
1555
- flex: 0 0 auto;
1556
- stroke-width: var(--ui-editor-toolbar-icon-stroke-width, 1.75);
1557
- }
1558
-
1559
- .ui-editor-table-context-menu button:hover:not(:disabled) {
1560
- background: var(--ui-surface-hover, #f3f4f6);
1561
- }
1562
-
1563
- .ui-editor-table-context-menu button[data-selected="true"] {
1564
- background: var(--ui-accent-subtle, rgb(59 130 246 / 0.1));
1565
- }
1566
-
1567
- .ui-editor-table-context-menu button[data-tone="danger"] {
1568
- color: var(--ui-danger, #dc2626);
1569
- }
1570
-
1571
- .ui-editor-table-context-menu__swatches {
1572
- display: grid;
1573
- grid-template-columns: repeat(3, minmax(0, 1fr));
1574
- gap: 3px;
1575
- }
1576
-
1577
- .ui-editor-table-context-menu__swatch-button {
1578
- display: inline-flex;
1579
- align-items: center;
1580
- gap: var(--ui-space-2, 8px);
1581
- }
1582
-
1583
- .ui-editor-table-context-menu__swatch {
1584
- display: inline-flex;
1585
- width: 1rem;
1586
- height: 1rem;
1587
- border-radius: 999px;
1588
- border: 1px solid var(--ui-border, #d1d5db);
1589
- background: var(--ui-editor-table-context-swatch, #fff);
1590
- box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.35);
1591
- flex-shrink: 0;
1592
- }
1593
-
1594
- .ui-editor-table-context-menu__swatch--default {
1595
- position: relative;
1596
- background:
1597
- linear-gradient(
1598
- 135deg,
1599
- transparent calc(50% - 1px),
1600
- var(--ui-text-muted, #6b7280) calc(50% - 1px),
1601
- var(--ui-text-muted, #6b7280) calc(50% + 1px),
1602
- transparent calc(50% + 1px)
1603
- ),
1604
- var(--ui-surface, #fff);
1605
- }
1606
-
1607
- .ui-editor-table-context-menu__swatch-label {
1608
- min-width: 0;
1609
- white-space: nowrap;
1610
- }
1611
-
1612
- .ui-editor-table-context-menu__heading {
1613
- padding: var(--ui-space-1, 4px) var(--ui-space-2, 8px);
1614
- font-size: 0.6875rem;
1615
- font-weight: 700;
1616
- letter-spacing: 0.05em;
1617
- text-transform: uppercase;
1618
- color: var(--ui-text-muted, #6b7280);
1619
- }
1620
-
1621
- .ui-editor-table-context-menu__sep {
1622
- height: 1px;
1623
- background: var(--ui-border, #e5e7eb);
1624
- margin: var(--ui-space-1, 4px) 0;
1625
- }
1626
-
1627
- /* Table toolbar */
1628
- .ui-editor-table-toolbar {
1629
- display: inline-flex;
1630
- align-items: center;
1631
- gap: var(--ui-space-1, 4px);
1632
- padding: var(--ui-space-1, 4px);
1633
- background: color-mix(in srgb, var(--ui-surface, #fff) 94%, transparent);
1634
- border: 1px solid var(--ui-border, #e5e7eb);
1635
- border-radius: calc(var(--ui-radius-md, 6px) + 2px);
1636
- box-shadow: var(--ui-shadow-lg, 0 10px 15px -3px rgb(0 0 0 / 0.1));
1637
- backdrop-filter: blur(10px);
1638
- }
1639
-
1640
- .ui-editor-table-toolbar__group {
1641
- display: inline-flex;
1642
- align-items: center;
1643
- gap: 2px;
1644
- overflow: hidden;
1645
- }
1646
-
1647
- .ui-editor-table-toolbar__sep {
1648
- width: 1px;
1649
- align-self: stretch;
1650
- background: var(--ui-border, #e5e7eb);
1651
- }
1652
-
1653
- .ui-editor-table-toolbar__overflow {
1654
- position: relative;
1655
- }
1656
-
1657
- .ui-editor-table-toolbar button {
1658
- display: inline-flex;
1659
- align-items: center;
1660
- justify-content: center;
1661
- gap: var(--ui-space-1-5, 6px);
1662
- min-height: 32px;
1663
- border: 0;
1664
- border-radius: var(--ui-radius-sm, 4px);
1665
- background: transparent;
1666
- color: var(--ui-text-primary, #111827);
1667
- cursor: pointer;
1668
- font-size: var(--ui-text-sm, 0.875rem);
1669
- font-weight: 500;
1670
- line-height: 1;
1671
- padding: var(--ui-space-1-5, 6px) var(--ui-space-2, 8px);
1672
- white-space: nowrap;
1673
- }
1674
-
1675
- .ui-editor-table-toolbar button:hover {
1676
- background: var(--ui-surface-hover, #f3f4f6);
1677
- }
1678
-
1679
- .ui-editor-table-toolbar button[data-active="true"] {
1680
- background: var(--ui-accent-soft, var(--ui-accent-subtle, #e8f0fb));
1681
- color: var(--ui-accent-active, #003d7a);
1682
- }
1683
-
1684
- .ui-editor-table-toolbar__group button {
1685
- border-radius: var(--ui-radius-sm, 4px);
1686
- }
1687
-
1688
- .ui-editor-table-toolbar__group button + button {
1689
- border-left: 0;
1690
- }
1691
-
1692
- .ui-editor-table-toolbar__more {
1693
- border: 0 !important;
1694
- border-radius: var(--ui-radius-sm, 4px) !important;
1695
- }
1696
-
1697
- .ui-editor-table-toolbar button[data-tone="danger"] {
1698
- color: var(--ui-danger, #dc2626);
1699
- }
1700
-
1701
- .ui-editor-table-toolbar .looma-icon {
1702
- width: var(--ui-editor-toolbar-icon-size, 1rem);
1703
- height: var(--ui-editor-toolbar-icon-size, 1rem);
1704
- flex: 0 0 auto;
1705
- stroke-width: var(--ui-editor-toolbar-icon-stroke-width, 1.75);
1706
- }
1707
-
1708
- .ui-editor-table-toolbar .looma-icon--chevron {
1709
- width: 0.875rem;
1710
- height: 0.875rem;
1711
- }
1712
-
1713
- .ui-editor-table-toolbar__icon-button {
1714
- width: 32px;
1715
- padding-inline: 0 !important;
1716
- }
1717
-
1718
- .ui-editor-table-toolbar__menu {
1719
- position: absolute;
1720
- top: calc(100% + var(--ui-space-2, 8px));
1721
- right: 0;
1722
- min-width: 12rem;
1723
- padding: var(--ui-space-1, 4px);
1724
- background: var(--ui-surface, #fff);
1725
- border: 1px solid var(--ui-border, #e5e7eb);
1726
- border-radius: var(--ui-radius-md, 6px);
1727
- box-shadow: var(--ui-shadow-lg, 0 10px 15px -3px rgb(0 0 0 / 0.1));
1728
- display: flex;
1729
- flex-direction: column;
1730
- gap: 2px;
1731
- z-index: 2;
1732
- }
1733
-
1734
- .ui-editor-table-toolbar__menu-section + .ui-editor-table-toolbar__menu-section {
1735
- margin-top: var(--ui-space-1, 4px);
1736
- padding-top: var(--ui-space-1, 4px);
1737
- border-top: 1px solid color-mix(in srgb, var(--ui-border, #e5e7eb) 80%, transparent);
1738
- }
1739
-
1740
- .ui-editor-table-toolbar__menu-heading {
1741
- padding: var(--ui-space-1, 4px) var(--ui-space-2, 8px);
1742
- font-size: 0.6875rem;
1743
- font-weight: 700;
1744
- letter-spacing: 0.05em;
1745
- text-transform: uppercase;
1746
- color: var(--ui-text-muted, #6b7280);
1747
- }
1748
-
1749
- .ui-editor-table-toolbar__menu button {
1750
- display: flex;
1751
- justify-content: flex-start;
1752
- width: 100%;
1753
- text-align: left;
1754
- }
1755
-
1756
- .ui-editor-table-toolbar__swatches {
1757
- display: grid;
1758
- grid-template-columns: repeat(3, minmax(0, 1fr));
1759
- gap: 2px;
1760
- }
1761
-
1762
- .ui-editor-table-toolbar__swatch-button {
1763
- gap: var(--ui-space-1, 4px);
1764
- justify-content: flex-start !important;
1765
- }
1766
-
1767
- .ui-editor-table-toolbar__swatch {
1768
- width: 14px;
1769
- height: 14px;
1770
- flex: 0 0 14px;
1771
- border: 1px solid color-mix(in srgb, var(--ui-border, #e5e7eb) 80%, #000);
1772
- border-radius: 3px;
1773
- background: var(--ui-editor-table-toolbar-swatch);
1774
- }
1775
-
1776
- .ui-editor-table-toolbar__swatch--default {
1777
- background:
1778
- linear-gradient(135deg, transparent 44%, var(--ui-danger, #dc2626) 46% 54%, transparent 56%),
1779
- var(--ui-surface, #fff);
1780
- }
1781
-
1782
- .ui-editor-table-toolbar__swatch-button[data-selected="true"] {
1783
- background: var(--ui-accent-soft, var(--ui-accent-subtle, #e8f0fb));
1784
- }
1785
-
1786
- /* Insert table grid */
1787
- .ui-editor-insert-table-grid {
1788
- padding: var(--ui-space-3, 12px);
1789
- background: var(--ui-surface, #fff);
1790
- border: 1px solid var(--ui-border, #e5e7eb);
1791
- border-radius: var(--ui-radius-md, 6px);
1792
- box-shadow: var(--ui-shadow-lg, 0 10px 15px -3px rgb(0 0 0 / 0.1));
1793
- }
1794
-
1795
- .ui-editor-insert-table-grid__hint {
1796
- margin: 0 0 var(--ui-space-2, 8px);
1797
- font-size: var(--ui-text-sm, 0.875rem);
1798
- color: var(--ui-text-muted, #6b7280);
1799
- }
1800
-
1801
- .ui-editor-insert-table-grid__grid {
1802
- display: grid;
1803
- grid-template-columns: repeat(var(--ui-editor-table-grid-cols, 8), 1.25rem);
1804
- grid-template-rows: repeat(var(--ui-editor-table-grid-rows, 8), 1.25rem);
1805
- gap: 2px;
1806
- margin-bottom: var(--ui-space-2, 8px);
1807
- }
1808
-
1809
- .ui-editor-insert-table-grid__cell {
1810
- width: 1.25rem;
1811
- height: 1.25rem;
1812
- padding: 0;
1813
- border: 1px solid var(--ui-border, #e5e7eb);
1814
- border-radius: 2px;
1815
- background: var(--ui-surface, #fff);
1816
- cursor: pointer;
1817
- }
1818
-
1819
- .ui-editor-insert-table-grid__cell--selected {
1820
- background: var(--ui-accent-solid, var(--ui-accent, #0066cc));
1821
- border-color: var(--ui-accent-solid, var(--ui-accent, #0066cc));
1822
- }
1823
-
1824
- .ui-editor-insert-table-grid__header {
1825
- display: block;
1826
- margin-bottom: var(--ui-space-2, 8px);
1827
- font-size: var(--ui-text-sm, 0.875rem);
1828
- cursor: pointer;
1829
- }
1830
-
1831
- .ui-editor-insert-table-grid [data-insert-table] {
1832
- width: 100%;
1833
- padding: var(--ui-space-2, 8px) var(--ui-space-3, 12px);
1834
- border: 1px solid var(--ui-border, #e5e7eb);
1835
- border-radius: var(--ui-radius-sm, 4px);
1836
- background: var(--ui-accent-solid, var(--ui-accent, #0066cc));
1837
- color: var(--ui-accent-contrast, #fff);
1838
- font-size: var(--ui-text-sm, 0.875rem);
1839
- cursor: pointer;
1840
- }
1841
-
1842
- .ui-editor-insert-table-grid [data-insert-table]:hover {
1843
- filter: brightness(1.05);
1844
- }
1845
-
1846
- /* Table overlay controls */
1847
- [data-component-root~="ui-editor-table-overlay"] {
1848
- display: block;
1849
- width: 100%;
1850
- height: 100%;
1851
- overflow: visible;
1852
- pointer-events: none;
1853
- }
1854
-
1855
- .ui-editor-table-overlay {
1856
- position: relative;
1857
- width: 100%;
1858
- height: 100%;
1859
- overflow: visible;
1860
- pointer-events: none;
1861
- }
1862
-
1863
- .ui-editor-table-overlay__rows,
1864
- .ui-editor-table-overlay__cols {
1865
- pointer-events: none;
1866
- position: absolute;
1867
- }
1868
-
1869
- .ui-editor-table-overlay__rows {
1870
- inset: 0;
1871
- width: 100%;
1872
- }
1873
-
1874
- .ui-editor-table-overlay__cols {
1875
- inset: 0;
1876
- height: 100%;
1877
- }
1878
-
1879
- .ui-editor-table-overlay__control {
1880
- position: absolute;
1881
- z-index: 1;
1882
- pointer-events: none;
1883
- }
1884
-
1885
- .ui-editor-table-overlay__handle {
1886
- position: absolute;
1887
- display: inline-flex;
1888
- align-items: center;
1889
- justify-content: center;
1890
- width: 24px;
1891
- height: 24px;
1892
- padding: 0;
1893
- border: 1px solid var(--ui-affordance-near-color, var(--ui-border-strong, #c8c8c4));
1894
- border-radius: var(--ui-radius-round, 999px);
1895
- background: color-mix(in srgb, var(--ui-surface-elevated, #fff) 96%, transparent);
1896
- color: var(--ui-affordance-near-color, var(--ui-text-secondary, #5c5c5c));
1897
- font: inherit;
1898
- font-size: 16px;
1899
- line-height: 1;
1900
- box-shadow: var(--ui-shadow-xs, 0 1px 2px rgb(0 0 0 / 0.05));
1901
- opacity: 0;
1902
- scale: 0.72;
1903
- transition:
1904
- opacity var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease),
1905
- scale var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease),
1906
- color var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease),
1907
- background-color var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease),
1908
- border-color var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease);
1909
- pointer-events: auto;
1910
- cursor: pointer;
1911
- }
1912
-
1913
- .ui-editor-table-overlay__handle > .looma-icon {
1914
- width: 14px;
1915
- height: 14px;
1916
- stroke-width: var(--ui-editor-table-affordance-icon-stroke-width, 2);
1917
- }
1918
-
1919
- .ui-editor-table-overlay__handle[data-ui-proximity="near"],
1920
- .ui-editor-table-overlay__control[data-active="true"] .ui-editor-table-overlay__handle,
1921
- .ui-editor-table-overlay__handle:focus-visible {
1922
- opacity: 1;
1923
- scale: 1;
1924
- }
1925
-
1926
- .ui-editor-table-overlay__control[data-active="true"] .ui-editor-table-overlay__handle,
1927
- .ui-editor-table-overlay__handle:focus-visible {
1928
- border-color: var(--ui-affordance-direct-color, var(--ui-accent-solid, #5b55d6));
1929
- background: var(--ui-affordance-direct-color, var(--ui-accent-solid, #5b55d6));
1930
- color: var(--ui-text-on-accent, #fff);
1931
- box-shadow: var(--ui-shadow-sm, 0 1px 3px rgb(0 0 0 / 0.1));
1932
- }
1933
-
1934
- .ui-editor-table-overlay__handle:focus-visible,
1935
- .ui-editor-table-overlay__selector:focus-visible,
1936
- .ui-editor-table-overlay__cell-menu:focus-visible {
1937
- outline: 2px solid var(--ui-focus-ring, #5b55d6);
1938
- outline-offset: 2px;
1939
- }
1940
-
1941
- .ui-editor-table-overlay__control--row {
1942
- left: 0;
1943
- width: 100%;
1944
- height: 0;
1945
- }
1946
-
1947
- .ui-editor-table-overlay__control--row .ui-editor-table-overlay__handle {
1948
- top: -12px;
1949
- left: -30px;
1950
- }
1951
-
1952
- .ui-editor-table-overlay__control--col {
1953
- top: 0;
1954
- width: 0;
1955
- height: 100%;
1956
- }
1957
-
1958
- .ui-editor-table-overlay__control--col .ui-editor-table-overlay__handle {
1959
- top: -30px;
1960
- left: -12px;
1961
- }
1962
-
1963
- .ui-editor-table-overlay__line {
1964
- position: absolute;
1965
- background: var(--ui-affordance-direct-color, var(--ui-accent-solid, #5b55d6));
1966
- opacity: 0;
1967
- transition: opacity var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease);
1968
- }
1969
-
1970
- .ui-editor-table-overlay__control--row .ui-editor-table-overlay__line {
1971
- inset: -1px 0 auto;
1972
- height: 2px;
1973
- }
1974
-
1975
- .ui-editor-table-overlay__control--col .ui-editor-table-overlay__line {
1976
- inset: 0 auto 0 -1px;
1977
- width: 2px;
1978
- }
1979
-
1980
- .ui-editor-table-overlay__control[data-active="true"] .ui-editor-table-overlay__line {
1981
- opacity: 0.8;
1982
- }
1983
-
1984
- .ui-editor-table-overlay__guide {
1985
- position: absolute;
1986
- width: var(--ui-affordance-guide-size, 4px);
1987
- height: var(--ui-affordance-guide-size, 4px);
1988
- border-radius: var(--ui-radius-round, 999px);
1989
- background: var(--ui-affordance-guide-color, var(--ui-border-strong, #c8c8c4));
1990
- opacity: var(--ui-affordance-guide-opacity, 0.42);
1991
- transition: opacity var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease);
1992
- }
1993
-
1994
- .ui-editor-table-overlay__control--row .ui-editor-table-overlay__guide {
1995
- left: -10px;
1996
- top: -2px;
1997
- }
1998
-
1999
- .ui-editor-table-overlay__control--col .ui-editor-table-overlay__guide {
2000
- left: -2px;
2001
- top: -10px;
2002
- }
2003
-
2004
- .ui-editor-table-overlay__control:has(.ui-editor-table-overlay__handle[data-ui-proximity="near"]) .ui-editor-table-overlay__guide,
2005
- .ui-editor-table-overlay__control[data-active="true"] .ui-editor-table-overlay__guide {
2006
- opacity: 0;
2007
- }
2008
-
2009
- .ui-editor-table-overlay__tooltip {
2010
- position: absolute;
2011
- z-index: 2;
2012
- top: calc(100% + 6px);
2013
- left: 50%;
2014
- width: max-content;
2015
- max-width: 14rem;
2016
- padding: var(--ui-space-1, 4px) var(--ui-space-2, 8px);
2017
- border-radius: var(--ui-radius-1, 4px);
2018
- background: var(--ui-text-primary, #1a1a1a);
2019
- color: var(--ui-surface-elevated, #fff);
2020
- font-size: var(--ui-font-size-xs, 0.75rem);
2021
- font-weight: var(--ui-font-medium, 500);
2022
- line-height: var(--ui-line-height-md, 1.4);
2023
- opacity: 0;
2024
- transform: translateX(-50%);
2025
- transition: opacity var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease);
2026
- pointer-events: none;
2027
- }
2028
-
2029
- .ui-editor-table-overlay__control--row .ui-editor-table-overlay__tooltip {
2030
- top: 50%;
2031
- left: auto;
2032
- right: calc(100% + 8px);
2033
- transform: translateY(-50%);
2034
- }
2035
-
2036
- .ui-editor-table-overlay__control[data-active="true"] .ui-editor-table-overlay__tooltip,
2037
- .ui-editor-table-overlay__handle:focus-visible .ui-editor-table-overlay__tooltip {
2038
- opacity: 1;
2039
- }
2040
-
2041
- .ui-editor-table-overlay__selector,
2042
- .ui-editor-table-overlay__cell-menu {
2043
- position: absolute;
2044
- z-index: 2;
2045
- display: inline-flex;
2046
- align-items: center;
2047
- justify-content: center;
2048
- padding: 0;
2049
- border: 1px solid transparent;
2050
- background: transparent;
2051
- color: var(--ui-text-secondary, #5c5c5c);
2052
- box-shadow: none;
2053
- pointer-events: auto;
2054
- cursor: pointer;
2055
- transition:
2056
- color var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease),
2057
- background-color var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease),
2058
- border-color var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease),
2059
- box-shadow var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease);
2060
- }
2061
-
2062
- .ui-editor-table-overlay__selector:hover,
2063
- .ui-editor-table-overlay__cell-menu:hover {
2064
- background: var(--ui-icon-button-hover-bg, var(--ui-surface-muted, #f3f4f6));
2065
- color: var(--ui-icon-button-hover-color, var(--ui-text-primary, #1a1a1a));
2066
- box-shadow: var(--ui-shadow-xs, 0 1px 2px rgb(0 0 0 / 0.05));
2067
- }
2068
-
2069
- .ui-editor-table-overlay__selector {
2070
- border-radius: var(--ui-radius-round, 999px);
2071
- }
2072
-
2073
- .ui-editor-table-overlay__selector::before {
2074
- content: "";
2075
- position: absolute;
2076
- inset: 50% auto auto 50%;
2077
- border-radius: var(--ui-radius-round, 999px);
2078
- background: var(--ui-affordance-guide-color, var(--ui-border-strong, #c8c8c4));
2079
- opacity: var(--ui-affordance-guide-opacity, 0.42);
2080
- transform: translate(-50%, -50%);
2081
- transition: opacity var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease);
2082
- pointer-events: none;
2083
- }
2084
-
2085
- .ui-editor-table-overlay__selector .looma-icon {
2086
- display: block;
2087
- width: 14px;
2088
- height: 14px;
2089
- stroke-width: var(--ui-editor-table-affordance-icon-stroke-width, 2);
2090
- opacity: 0;
2091
- transform: scale(0.72);
2092
- transition:
2093
- opacity var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease),
2094
- transform var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease);
2095
- }
2096
-
2097
- .ui-editor-table-overlay__selector:hover::before,
2098
- .ui-editor-table-overlay__selector:focus-visible::before {
2099
- opacity: 0;
2100
- }
2101
-
2102
- .ui-editor-table-overlay__selector:hover .looma-icon,
2103
- .ui-editor-table-overlay__selector:focus-visible .looma-icon {
2104
- opacity: 1;
2105
- transform: scale(1);
2106
- }
2107
-
2108
- .ui-editor-table-overlay__selector--row {
2109
- left: -9px;
2110
- width: 18px;
2111
- height: 32px;
2112
- transform: translateY(-50%);
2113
- }
2114
-
2115
- .ui-editor-table-overlay__selector--row::before {
2116
- width: 2px;
2117
- height: 16px;
2118
- }
2119
-
2120
- .ui-editor-table-overlay__selector--column {
2121
- top: -9px;
2122
- width: 32px;
2123
- height: 18px;
2124
- transform: translateX(-50%);
2125
- }
2126
-
2127
- .ui-editor-table-overlay__selector--column::before {
2128
- width: 16px;
2129
- height: 2px;
2130
- }
2131
-
2132
- @media (hover: none), (pointer: coarse) {
2133
- .ui-editor-table-overlay__selector::before {
2134
- opacity: 0;
2135
- }
2136
-
2137
- .ui-editor-table-overlay__selector .looma-icon {
2138
- opacity: 1;
2139
- transform: none;
2140
- }
2141
- }
2142
-
2143
- .ui-editor-table-overlay__cell-menu {
2144
- width: 24px;
2145
- height: 24px;
2146
- border-radius: var(--ui-radius-1, 4px);
2147
- background: color-mix(in srgb, var(--ui-surface-muted, #f3f4f6) 78%, transparent);
2148
- }
2149
-
2150
- .ui-editor-table-overlay__cell-menu .looma-icon {
2151
- width: 15px;
2152
- height: 15px;
2153
- stroke-width: var(--ui-editor-table-affordance-icon-stroke-width, 2);
2154
- }
2155
-
2156
- .ProseMirror .tableWrapper {
2157
- max-width: 100%;
2158
- overflow-x: auto;
2159
- overflow-y: visible;
2160
- overscroll-behavior-inline: contain;
2161
- -webkit-overflow-scrolling: touch;
2162
- }
2163
-
2164
- /* ProseMirror table (for when Looma editor content is rendered) */
2165
- .ProseMirror:focus-visible {
2166
- outline: none;
2167
- box-shadow: none;
2168
- }
2169
-
2170
- .ProseMirror table {
2171
- border-collapse: collapse;
2172
- margin: 0;
2173
- width: max-content !important;
2174
- min-width: 100%;
2175
- max-width: none !important;
2176
- table-layout: fixed;
2177
- }
2178
-
2179
- .ProseMirror table.ProseMirror-selectednode,
2180
- .ProseMirror .tableWrapper.ProseMirror-selectednode {
2181
- outline: none;
2182
- }
2183
-
2184
- .ProseMirror th,
2185
- .ProseMirror td {
2186
- position: relative;
2187
- border: 1px solid var(--ui-border, #e5e7eb);
2188
- padding: var(--ui-space-2, 8px) var(--ui-space-3, 12px);
2189
- vertical-align: top;
2190
- min-width: var(--ui-editor-table-cell-min-width, 112px);
2191
- text-align: left;
2192
- overflow-wrap: anywhere;
2193
- }
2194
-
2195
- .ProseMirror :is(th, td) > p,
2196
- .looma-editor .ProseMirror :is(th, td) > p {
2197
- margin: 0;
2198
- }
2199
-
2200
- .ProseMirror :is(th, td) > p + p,
2201
- .looma-editor .ProseMirror :is(th, td) > p + p {
2202
- margin-top: var(--ui-editor-table-paragraph-gap, var(--ui-space-2, 8px));
2203
- }
2204
-
2205
- .ProseMirror th {
2206
- font-weight: 600;
2207
- background: var(--ui-surface-hover, #f3f4f6);
2208
- }
2209
-
2210
- .ProseMirror .selectedCell {
2211
- background: var(--ui-accent-subtle, rgba(59, 130, 246, 0.1));
2212
- }
2213
-
2214
- .ProseMirror .column-resize-handle {
2215
- position: absolute;
2216
- z-index: 3;
2217
- right: -5px;
2218
- top: 0;
2219
- bottom: 0;
2220
- width: 10px;
2221
- cursor: col-resize;
2222
- background: transparent;
2223
- touch-action: none;
2224
- }
2225
-
2226
- .ProseMirror .column-resize-handle::after {
2227
- content: "";
2228
- position: absolute;
2229
- inset-block: 0;
2230
- inset-inline-start: 4px;
2231
- width: 2px;
2232
- border-radius: var(--ui-radius-round, 999px);
2233
- background: color-mix(in srgb, var(--ui-affordance-near-color, var(--ui-border-strong, #c8c8c4)) 72%, transparent);
2234
- transition:
2235
- width var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease),
2236
- background-color var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease);
2237
- }
2238
-
2239
- .ProseMirror .column-resize-handle:hover::after,
2240
- .ProseMirror.resize-cursor .column-resize-handle::after {
2241
- width: 3px;
2242
- background: var(--ui-affordance-direct-color, var(--ui-accent-solid, #5b55d6));
2243
- }
2244
-
2245
- .ProseMirror .column-resize-handle::before {
2246
- content: "Drag to resize column";
2247
- position: absolute;
2248
- z-index: 4;
2249
- top: 4px;
2250
- left: calc(100% + 6px);
2251
- width: max-content;
2252
- padding: var(--ui-space-1, 4px) var(--ui-space-2, 8px);
2253
- border-radius: var(--ui-radius-1, 4px);
2254
- background: var(--ui-text-primary, #1a1a1a);
2255
- color: var(--ui-surface-elevated, #fff);
2256
- font-size: var(--ui-font-size-xs, 0.75rem);
2257
- line-height: var(--ui-line-height-md, 1.4);
2258
- opacity: 0;
2259
- transform: none;
2260
- transition: opacity var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease);
2261
- pointer-events: none;
2262
- }
2263
-
2264
- .ProseMirror .column-resize-handle:hover::before {
2265
- opacity: 1;
2266
- }
2267
- `;
2268
-
2269
- // src/table-overlay.ts
2270
- import {
2271
- createProximityCoordinator,
2272
- loomaIconMarkup
2273
- } from "@threadlabs/looma/core";
2274
- var DEFAULT_ROWS = 3;
2275
- var DEFAULT_COLS = 3;
2276
- function sameNumbers(left, right) {
2277
- return left.length === right.length && left.every((value, index) => value === right[index]);
2278
- }
2279
- function sameActiveCell(left, right) {
2280
- if (left === right) return true;
2281
- if (!left || !right) return false;
2282
- return left.left === right.left && left.top === right.top && left.width === right.width && left.height === right.height && left.rowIndex === right.rowIndex && left.columnIndex === right.columnIndex;
2283
- }
2284
- function sameGeometry(left, right) {
2285
- if (left === right) return true;
2286
- if (!left || !right) return false;
2287
- return sameNumbers(left.rowBoundaries, right.rowBoundaries) && sameNumbers(left.columnBoundaries, right.columnBoundaries) && sameActiveCell(left.activeCell, right.activeCell) && sameActiveCell(left.hoveredCell ?? null, right.hoveredCell ?? null);
2288
- }
2289
- function mapTableCells(table) {
2290
- const rows = Array.from(table.rows);
2291
- const grid = [];
2292
- const coordinates = /* @__PURE__ */ new Map();
2293
- rows.forEach((row, rowIndex) => {
2294
- grid[rowIndex] ??= [];
2295
- let columnIndex = 0;
2296
- for (const cell of Array.from(row.cells)) {
2297
- while (grid[rowIndex]?.[columnIndex]) columnIndex += 1;
2298
- coordinates.set(cell, { rowIndex, columnIndex });
2299
- const rowSpan = cell.rowSpan === 0 ? rows.length - rowIndex : Math.max(1, cell.rowSpan);
2300
- const columnSpan = Math.max(1, cell.colSpan);
2301
- for (let rowOffset = 0; rowOffset < rowSpan; rowOffset += 1) {
2302
- const gridRow = grid[rowIndex + rowOffset] ??= [];
2303
- for (let columnOffset = 0; columnOffset < columnSpan; columnOffset += 1) {
2304
- gridRow[columnIndex + columnOffset] = cell;
2305
- }
2306
- }
2307
- columnIndex += columnSpan;
2308
- }
2309
- });
2310
- return { grid, coordinates };
2311
- }
2312
- function resolveTableCellAt(table, rowIndex, columnIndex) {
2313
- return mapTableCells(table).grid[rowIndex]?.[columnIndex] ?? null;
2314
- }
2315
- function uniqueSorted(values) {
2316
- return values.sort((left, right) => left - right).filter((value, index, all) => index === 0 || Math.abs(value - (all[index - 1] ?? value)) >= 0.5).map((value) => Math.round(value * 100) / 100);
2317
- }
2318
- function measureTableOverlayGeometry(table, activeCell, hoveredCell = null) {
2319
- const tableRect = table.getBoundingClientRect();
2320
- const rows = Array.from(table.rows);
2321
- const cells = rows.flatMap((row) => Array.from(row.cells));
2322
- const { coordinates } = mapTableCells(table);
2323
- const rowBoundaries = uniqueSorted(rows.flatMap((row) => {
2324
- const rect = row.getBoundingClientRect();
2325
- return [rect.top - tableRect.top, rect.bottom - tableRect.top];
2326
- }));
2327
- const columnBoundaries = uniqueSorted(cells.flatMap((cell) => {
2328
- const rect = cell.getBoundingClientRect();
2329
- return [rect.left - tableRect.left, rect.right - tableRect.left];
2330
- }));
2331
- if (!activeCell || !table.contains(activeCell)) {
2332
- return {
2333
- rowBoundaries,
2334
- columnBoundaries,
2335
- activeCell: null,
2336
- hoveredCell: measureCellRect(table, hoveredCell, coordinates)
2337
- };
2338
- }
2339
- return {
2340
- rowBoundaries,
2341
- columnBoundaries,
2342
- activeCell: measureCellRect(table, activeCell, coordinates),
2343
- hoveredCell: measureCellRect(table, hoveredCell, coordinates)
2344
- };
2345
- }
2346
- function measureCellRect(table, cell, coordinates) {
2347
- if (!cell || !table.contains(cell)) return null;
2348
- const tableRect = table.getBoundingClientRect();
2349
- const cellRect = cell.getBoundingClientRect();
2350
- const coordinate = coordinates.get(cell);
2351
- return {
2352
- left: Math.round((cellRect.left - tableRect.left) * 100) / 100,
2353
- top: Math.round((cellRect.top - tableRect.top) * 100) / 100,
2354
- width: Math.round(cellRect.width * 100) / 100,
2355
- height: Math.round(cellRect.height * 100) / 100,
2356
- rowIndex: coordinate?.rowIndex ?? 0,
2357
- columnIndex: coordinate?.columnIndex ?? 0
2358
- };
2359
- }
2360
- function finiteNumbers(value) {
2361
- if (!value) return [];
2362
- const values = value.split(",").map((part) => Number(part.trim()));
2363
- return values.every(Number.isFinite) ? values : [];
2364
- }
2365
- function fallbackBoundaries2(segments, length) {
2366
- return Array.from({ length: segments + 1 }, (_, index) => index * length / segments);
2367
- }
2368
- if (typeof HTMLElement !== "undefined") {
2369
- class UIEditorTableOverlayElement extends HTMLElement {
2370
- static get observedAttributes() {
2371
- return ["open", "rows", "cols", "row-boundaries", "column-boundaries", "active-cell", "hovered-cell"];
2372
- }
2373
- #rows = DEFAULT_ROWS;
2374
- #cols = DEFAULT_COLS;
2375
- #activeControlKey = null;
2376
- #proximity = null;
2377
- #geometry = null;
2378
- #renderPending = false;
2379
- get geometry() {
2380
- return this.#geometry;
2381
- }
2382
- set geometry(value) {
2383
- if (sameGeometry(this.#geometry, value)) return;
2384
- this.#geometry = value;
2385
- this.#scheduleRender();
2386
- }
2387
- connectedCallback() {
2388
- this.#rows = this.#readBoundedNumber("rows", DEFAULT_ROWS);
2389
- this.#cols = this.#readBoundedNumber("cols", DEFAULT_COLS);
2390
- this.render();
2391
- this.addEventListener("mousedown", this.onMouseDown);
2392
- this.addEventListener("click", this.onClick);
2393
- this.addEventListener("pointerover", this.onPointerOver);
2394
- this.addEventListener("pointerout", this.onPointerOut);
2395
- this.addEventListener("focusin", this.onFocusIn);
2396
- this.addEventListener("focusout", this.onFocusOut);
2397
- this.#proximity = createProximityCoordinator(this, {
2398
- pointerTarget: this.ownerDocument,
2399
- anchorSelector: ".ui-editor-table-overlay__handle[data-ui-affordance]"
2400
- });
2401
- }
2402
- disconnectedCallback() {
2403
- this.removeEventListener("mousedown", this.onMouseDown);
2404
- this.removeEventListener("click", this.onClick);
2405
- this.removeEventListener("pointerover", this.onPointerOver);
2406
- this.removeEventListener("pointerout", this.onPointerOut);
2407
- this.removeEventListener("focusin", this.onFocusIn);
2408
- this.removeEventListener("focusout", this.onFocusOut);
2409
- this.#proximity?.destroy();
2410
- this.#proximity = null;
2411
- }
2412
- attributeChangedCallback(name) {
2413
- if (name === "rows") this.#rows = this.#readBoundedNumber("rows", DEFAULT_ROWS);
2414
- if (name === "cols") this.#cols = this.#readBoundedNumber("cols", DEFAULT_COLS);
2415
- this.#scheduleRender();
2416
- }
2417
- #scheduleRender() {
2418
- if (!this.isConnected || this.#renderPending) return;
2419
- this.#renderPending = true;
2420
- queueMicrotask(() => {
2421
- this.#renderPending = false;
2422
- if (this.isConnected) this.render();
2423
- });
2424
- }
2425
- #readBoundedNumber(name, fallback) {
2426
- const parsed = Number.parseInt(this.getAttribute(name) ?? String(fallback), 10);
2427
- return Number.isFinite(parsed) ? Math.min(100, Math.max(1, parsed)) : fallback;
2428
- }
2429
- #boundaries(name, segments) {
2430
- if (this.#geometry) {
2431
- return name === "row-boundaries" ? this.#geometry.rowBoundaries : this.#geometry.columnBoundaries;
2432
- }
2433
- const explicit = finiteNumbers(this.getAttribute(name));
2434
- if (explicit.length === segments + 1) return explicit;
2435
- const length = name === "row-boundaries" ? this.getBoundingClientRect().height : this.getBoundingClientRect().width;
2436
- return fallbackBoundaries2(segments, length);
2437
- }
2438
- #activeCell() {
2439
- if (this.#geometry) return this.#geometry.activeCell;
2440
- const values = finiteNumbers(this.getAttribute("active-cell"));
2441
- if (values.length !== 6) return null;
2442
- const [left, top, width, height, rowIndex, columnIndex] = values;
2443
- if (left === void 0 || top === void 0 || width === void 0 || height === void 0 || rowIndex === void 0 || columnIndex === void 0) return null;
2444
- return { left, top, width, height, rowIndex, columnIndex };
2445
- }
2446
- #hoveredCell() {
2447
- if (this.#geometry) return this.#geometry.hoveredCell ?? null;
2448
- const values = finiteNumbers(this.getAttribute("hovered-cell"));
2449
- if (values.length !== 6) return null;
2450
- const [left, top, width, height, rowIndex, columnIndex] = values;
2451
- if (left === void 0 || top === void 0 || width === void 0 || height === void 0 || rowIndex === void 0 || columnIndex === void 0) return null;
2452
- return { left, top, width, height, rowIndex, columnIndex };
2453
- }
2454
- dispatch(detail) {
2455
- this.dispatchEvent(new CustomEvent(
2456
- "action",
2457
- { detail, bubbles: true, composed: true }
2458
- ));
2459
- }
2460
- onMouseDown = (event) => {
2461
- const target = event.target instanceof Element ? event.target.closest("button[data-action]") : null;
2462
- if (target) event.preventDefault();
2463
- };
2464
- onClick = (event) => {
2465
- const target = event.target.closest("button[data-action]");
2466
- if (!target) return;
2467
- const action = target.dataset.action;
2468
- if (!action) return;
2469
- if (action === "select-row" || action === "select-column") {
2470
- const rowIndex = Number.parseInt(target.dataset.rowIndex ?? "", 10);
2471
- const columnIndex = Number.parseInt(target.dataset.columnIndex ?? "", 10);
2472
- if (Number.isFinite(rowIndex) && Number.isFinite(columnIndex)) {
2473
- this.dispatch({ action, rowIndex, columnIndex });
2474
- }
2475
- return;
2476
- }
2477
- if (action === "open-cell-menu") {
2478
- const rowIndex = Number.parseInt(target.dataset.rowIndex ?? "", 10);
2479
- const columnIndex = Number.parseInt(target.dataset.columnIndex ?? "", 10);
2480
- if (!Number.isFinite(rowIndex) || !Number.isFinite(columnIndex)) return;
2481
- const rect = target.getBoundingClientRect();
2482
- this.dispatch({
2483
- action,
2484
- rowIndex,
2485
- columnIndex,
2486
- anchor: { left: rect.left, top: rect.top, right: rect.right, bottom: rect.bottom }
2487
- });
2488
- return;
2489
- }
2490
- const boundaryIndex = Number.parseInt(target.dataset.boundaryIndex ?? "", 10);
2491
- if (!Number.isFinite(boundaryIndex) || boundaryIndex < 0) return;
2492
- this.dispatch({ action, boundaryIndex });
2493
- };
2494
- onPointerOver = (event) => {
2495
- const control = event.target.closest("[data-control-key]");
2496
- this.#setActiveControl(control?.dataset.controlKey ?? null);
2497
- };
2498
- onPointerOut = (event) => {
2499
- const related = event.relatedTarget;
2500
- const next = related instanceof Element ? related.closest("[data-control-key]") : null;
2501
- this.#setActiveControl(next?.dataset.controlKey ?? null);
2502
- };
2503
- onFocusIn = (event) => {
2504
- const control = event.target.closest("[data-control-key]");
2505
- this.#setActiveControl(control?.dataset.controlKey ?? null);
2506
- };
2507
- onFocusOut = (event) => {
2508
- const related = event.relatedTarget;
2509
- const next = related instanceof Element ? related.closest("[data-control-key]") : null;
2510
- this.#setActiveControl(next?.dataset.controlKey ?? null);
2511
- };
2512
- #setActiveControl(controlKey) {
2513
- if (this.#activeControlKey === controlKey) return;
2514
- this.#activeControlKey = controlKey;
2515
- this.#syncActiveControl();
2516
- }
2517
- #syncActiveControl() {
2518
- this.querySelectorAll("[data-control-key]").forEach((control) => {
2519
- control.dataset.active = this.#activeControlKey === control.dataset.controlKey ? "true" : "false";
2520
- });
2521
- }
2522
- #insertionControl(axis, index, position) {
2523
- const first = index === 0;
2524
- const action = axis === "row" ? first ? "add-row-before" : "add-row-after" : first ? "add-column-before" : "add-column-after";
2525
- const noun = axis === "row" ? "row" : "column";
2526
- const relation = first ? axis === "row" ? "above" : "left" : axis === "row" ? "below" : "right";
2527
- const label = `Insert ${noun} ${relation}`;
2528
- const controlKey = `${axis}:${index}`;
2529
- const positionStyle = axis === "row" ? `top:${position}px;` : `left:${position}px;`;
2530
- return [
2531
- `<div class="ui-editor-table-overlay__control ui-editor-table-overlay__control--${axis}"`,
2532
- ` style="${positionStyle}" data-control-key="${controlKey}"`,
2533
- ` data-active="${this.#activeControlKey === controlKey ? "true" : "false"}">`,
2534
- '<span class="ui-editor-table-overlay__line" aria-hidden="true"></span>',
2535
- '<span class="ui-editor-table-overlay__guide" data-ui-guide aria-hidden="true"></span>',
2536
- `<button type="button" class="ui-editor-table-overlay__handle" data-ui-affordance="insert-${noun}"`,
2537
- ` data-action="${action}" data-boundary-index="${index}" aria-label="${label}">`,
2538
- loomaIconMarkup("plus"),
2539
- `<span class="ui-editor-table-overlay__tooltip" role="tooltip">${label}</span>`,
2540
- "</button>",
2541
- "</div>"
2542
- ].join("");
2543
- }
2544
- #cellSelectors(cell) {
2545
- const centerX = cell.left + cell.width / 2;
2546
- const centerY = cell.top + cell.height / 2;
2547
- const indexes = `data-row-index="${cell.rowIndex}" data-column-index="${cell.columnIndex}"`;
2548
- return [
2549
- `<button type="button" class="ui-editor-table-overlay__selector ui-editor-table-overlay__selector--row" style="top:${centerY}px" data-action="select-row" ${indexes} title="Select row" aria-label="Select row">${loomaIconMarkup("grip-vertical")}</button>`,
2550
- `<button type="button" class="ui-editor-table-overlay__selector ui-editor-table-overlay__selector--column" style="left:${centerX}px" data-action="select-column" ${indexes} title="Select column" aria-label="Select column">${loomaIconMarkup("grip-horizontal")}</button>`
2551
- ].join("");
2552
- }
2553
- #cellMenu(cell) {
2554
- const indexes = `data-row-index="${cell.rowIndex}" data-column-index="${cell.columnIndex}"`;
2555
- return `<button type="button" class="ui-editor-table-overlay__cell-menu" style="left:${cell.left + cell.width - 30}px;top:${cell.top + 6}px" data-action="open-cell-menu" ${indexes} title="Cell actions" aria-label="Cell actions">${loomaIconMarkup("chevron-down")}</button>`;
2556
- }
2557
- render() {
2558
- const open = this.hasAttribute("open") && this.getAttribute("open") !== "false";
2559
- this.hidden = !open;
2560
- if (!open) {
2561
- this.#setActiveControl(null);
2562
- return;
2563
- }
2564
- const rows = this.#boundaries("row-boundaries", this.#rows);
2565
- const columns = this.#boundaries("column-boundaries", this.#cols);
2566
- const activeCell = this.#activeCell();
2567
- const hoveredCell = this.#hoveredCell();
2568
- this.innerHTML = [
2569
- '<div class="ui-editor-table-overlay" aria-label="Table controls">',
2570
- '<div class="ui-editor-table-overlay__rows">',
2571
- rows.map((position, index) => this.#insertionControl("row", index, position)).join(""),
2572
- "</div>",
2573
- '<div class="ui-editor-table-overlay__cols">',
2574
- columns.map((position, index) => this.#insertionControl("col", index, position)).join(""),
2575
- "</div>",
2576
- hoveredCell ? this.#cellSelectors(hoveredCell) : "",
2577
- activeCell ? this.#cellMenu(activeCell) : "",
2578
- "</div>"
2579
- ].join("");
2580
- this.#syncActiveControl();
2581
- this.#proximity?.refresh();
2582
- }
2583
- }
2584
- }
2585
-
2586
- // src/ui.ts
2587
- registerLoomaPackage("editor", records, styles);
2588
-
2589
- export {
2590
- resolveTableCellAt,
2591
- measureTableOverlayGeometry
2592
- };