@threadlabs/looma 0.14.8 → 0.15.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 (336) hide show
  1. package/components/shared/icons.js +5 -0
  2. package/components/shared/menu-navigation.js +49 -0
  3. package/components/shared/overlay.js +33 -60
  4. package/components/ui-avatar/ui-avatar.html +19 -2
  5. package/components/ui-avatar/ui-avatar.js +7 -1
  6. package/components/ui-avatar-group/ui-avatar-group.html +8 -3
  7. package/components/ui-avatar-group/ui-avatar-group.js +6 -2
  8. package/components/ui-badge/ui-badge.html +70 -4
  9. package/components/ui-button/ui-button.html +21 -5
  10. package/components/ui-button/ui-button.js +10 -0
  11. package/components/ui-callout/ui-callout.html +13 -5
  12. package/components/ui-checkbox/ui-checkbox.html +11 -0
  13. package/components/ui-combobox/ui-combobox.html +46 -10
  14. package/components/ui-combobox/ui-combobox.js +5 -5
  15. package/components/ui-context-menu/ui-context-menu.html +33 -5
  16. package/components/ui-context-menu/ui-context-menu.js +26 -17
  17. package/components/ui-dialog/ui-dialog.html +51 -8
  18. package/components/ui-dialog/ui-dialog.js +28 -7
  19. package/components/ui-disclosure/ui-disclosure.html +14 -5
  20. package/components/ui-disclosure/ui-disclosure.js +51 -4
  21. package/components/ui-editable/ui-editable.html +17 -3
  22. package/components/ui-editable/ui-editable.js +7 -0
  23. package/components/ui-editor-insert-table-grid/ui-editor-insert-table-grid.html +48 -14
  24. package/components/ui-editor-insert-table-grid/ui-editor-insert-table-grid.js +40 -6
  25. package/components/ui-editor-mention-menu/ui-editor-mention-menu.html +22 -9
  26. package/components/ui-editor-mention-menu/ui-editor-mention-menu.js +33 -11
  27. package/components/ui-editor-mention-menu-item/ui-editor-mention-menu-item.html +67 -0
  28. package/components/ui-editor-slash-menu/ui-editor-slash-menu.html +33 -30
  29. package/components/ui-editor-slash-menu/ui-editor-slash-menu.js +42 -8
  30. package/components/ui-editor-slash-menu-group/ui-editor-slash-menu-group.html +31 -0
  31. package/components/ui-editor-slash-menu-item/ui-editor-slash-menu-item.html +75 -0
  32. package/components/ui-editor-table-context-menu/ui-editor-table-context-menu.html +16 -5
  33. package/components/ui-editor-table-context-menu/ui-editor-table-context-menu.js +10 -2
  34. package/components/ui-editor-table-overlay/ui-editor-table-overlay.html +5 -5
  35. package/components/ui-editor-table-overlay/ui-editor-table-overlay.js +5 -2
  36. package/components/ui-editor-table-toolbar/ui-editor-table-toolbar.html +14 -4
  37. package/components/ui-editor-table-toolbar/ui-editor-table-toolbar.js +2 -2
  38. package/components/ui-editor-toolbar/ui-editor-toolbar.html +1 -1
  39. package/components/ui-editor-toolbar/ui-editor-toolbar.js +46 -0
  40. package/components/ui-floating-action-button/ui-floating-action-button.html +1 -1
  41. package/components/ui-form-field/ui-form-field.html +16 -3
  42. package/components/ui-form-field/ui-form-field.js +1 -1
  43. package/components/ui-grid/ui-grid.html +17 -2
  44. package/components/ui-icon/ui-icon.html +5 -0
  45. package/components/ui-icon-button/ui-icon-button.html +25 -6
  46. package/components/ui-icon-button/ui-icon-button.js +11 -1
  47. package/components/ui-input/ui-input.html +3 -3
  48. package/components/ui-input-group/ui-input-group.html +18 -4
  49. package/components/ui-listbox/ui-listbox.html +81 -0
  50. package/components/ui-listbox/ui-listbox.js +27 -0
  51. package/components/ui-menu/ui-menu.html +33 -5
  52. package/components/ui-menu/ui-menu.js +18 -12
  53. package/components/ui-menu-group/ui-menu-group.html +27 -0
  54. package/components/ui-menu-item/ui-menu-item.html +80 -8
  55. package/components/ui-menu-item/ui-menu-item.js +47 -0
  56. package/components/ui-popover/ui-popover.html +33 -5
  57. package/components/ui-popover/ui-popover.js +9 -1
  58. package/components/ui-radio-group/ui-radio-group.html +13 -1
  59. package/components/ui-search-shell/ui-search-shell.html +6 -6
  60. package/components/ui-search-shell/ui-search-shell.js +21 -5
  61. package/components/ui-select/ui-select.html +14 -4
  62. package/components/ui-switch/ui-switch.html +2 -2
  63. package/components/ui-switcher/ui-switcher.html +8 -1
  64. package/components/ui-switcher/ui-switcher.js +15 -0
  65. package/components/ui-tabs/ui-tabs.html +17 -12
  66. package/components/ui-tabs/ui-tabs.js +43 -7
  67. package/components/ui-textarea/ui-textarea.html +28 -5
  68. package/components/ui-toast/ui-toast.html +88 -0
  69. package/components/ui-toast/ui-toast.js +68 -0
  70. package/components/ui-toast-region/ui-toast-region.html +102 -19
  71. package/components/ui-toast-region/ui-toast-region.js +10 -18
  72. package/components/ui-tooltip/ui-tooltip.html +51 -7
  73. package/components/ui-tooltip/ui-tooltip.js +29 -3
  74. package/components/ui-tree/ui-tree.html +12 -0
  75. package/components/ui-tree/ui-tree.js +191 -12
  76. package/components/ui-tree-item/ui-tree-item.html +87 -8
  77. package/components/ui-tree-item/ui-tree-item.js +3 -1
  78. package/dist/index.js +38 -32
  79. package/editor/chunks/extensions.js +82 -2
  80. package/editor/extensions/table-commands.d.ts +8 -0
  81. package/editor/icons.d.ts +5 -0
  82. package/editor/index.js +7 -2
  83. package/editor/insert-table-grid.d.ts +1 -1
  84. package/editor/mention-menu.d.ts +2 -0
  85. package/editor/slash-menu.d.ts +3 -0
  86. package/editor/table-context-menu.d.ts +1 -1
  87. package/editor/table-overlay.d.ts +2 -2
  88. package/package.json +1 -1
  89. package/styles/ui-avatar-group.css +1 -2
  90. package/styles/ui-avatar.css +11 -1
  91. package/styles/ui-badge.css +73 -1
  92. package/styles/ui-button.css +9 -1
  93. package/styles/ui-callout.css +1 -1
  94. package/styles/ui-checkbox.css +9 -0
  95. package/styles/ui-combobox.css +38 -7
  96. package/styles/ui-context-menu.css +32 -4
  97. package/styles/ui-dialog.css +42 -5
  98. package/styles/ui-editor-insert-table-grid.css +48 -11
  99. package/styles/ui-editor-mention-menu-item.css +57 -0
  100. package/styles/ui-editor-mention-menu.css +11 -1
  101. package/styles/ui-editor-slash-menu-group.css +21 -0
  102. package/styles/ui-editor-slash-menu-item.css +62 -0
  103. package/styles/ui-editor-slash-menu.css +10 -21
  104. package/styles/ui-editor-table-context-menu.css +1 -1
  105. package/styles/ui-floating-action-button.css +1 -1
  106. package/styles/ui-form-field.css +14 -1
  107. package/styles/ui-grid.css +14 -1
  108. package/styles/ui-icon-button.css +17 -5
  109. package/styles/ui-input-group.css +18 -4
  110. package/styles/ui-input.css +3 -3
  111. package/styles/ui-listbox.css +69 -0
  112. package/styles/ui-menu-group.css +19 -0
  113. package/styles/ui-menu-item.css +48 -9
  114. package/styles/ui-menu.css +32 -4
  115. package/styles/ui-popover.css +28 -4
  116. package/styles/ui-radio-group.css +9 -0
  117. package/styles/ui-search-shell.css +5 -5
  118. package/styles/ui-select.css +14 -4
  119. package/styles/ui-switch.css +2 -2
  120. package/styles/ui-switcher.css +4 -0
  121. package/styles/ui-textarea.css +27 -4
  122. package/styles/ui-toast-region.css +88 -4
  123. package/styles/ui-toast.css +74 -0
  124. package/styles/ui-tooltip.css +46 -6
  125. package/styles/ui-tree-item.css +72 -4
  126. package/styles/ui-tree.css +8 -0
  127. package/tokens.css +3 -0
  128. package/vanilla/UiAvatar.d.ts +2 -0
  129. package/vanilla/UiAvatar.js +8 -1
  130. package/vanilla/UiAvatarGroup.d.ts +2 -0
  131. package/vanilla/UiAvatarGroup.js +1 -1
  132. package/vanilla/UiBadge.d.ts +1 -1
  133. package/vanilla/UiBadge.js +1 -1
  134. package/vanilla/UiButton.d.ts +1 -0
  135. package/vanilla/UiButton.js +22 -6
  136. package/vanilla/UiCallout.d.ts +1 -1
  137. package/vanilla/UiCallout.js +1 -1
  138. package/vanilla/UiCheckbox.d.ts +1 -0
  139. package/vanilla/UiCheckbox.js +12 -9
  140. package/vanilla/UiCombobox.d.ts +3 -0
  141. package/vanilla/UiCombobox.js +30 -15
  142. package/vanilla/UiContextMenu.d.ts +1 -1
  143. package/vanilla/UiContextMenu.js +1 -1
  144. package/vanilla/UiDialog.d.ts +5 -2
  145. package/vanilla/UiDialog.js +1 -1
  146. package/vanilla/UiDisclosure.d.ts +2 -0
  147. package/vanilla/UiDisclosure.js +32 -18
  148. package/vanilla/UiEditable.d.ts +5 -0
  149. package/vanilla/UiEditable.js +7 -3
  150. package/vanilla/UiEditorInsertTableGrid.js +1 -6
  151. package/vanilla/UiEditorMentionMenu.d.ts +4 -2
  152. package/vanilla/UiEditorMentionMenu.js +49 -31
  153. package/vanilla/UiEditorMentionMenuItem.d.ts +16 -0
  154. package/vanilla/UiEditorMentionMenuItem.js +56 -0
  155. package/vanilla/UiEditorSlashMenu.d.ts +4 -3
  156. package/vanilla/UiEditorSlashMenu.js +59 -46
  157. package/vanilla/UiEditorSlashMenuGroup.d.ts +14 -0
  158. package/vanilla/UiEditorSlashMenuGroup.js +48 -0
  159. package/vanilla/UiEditorSlashMenuItem.d.ts +18 -0
  160. package/vanilla/UiEditorSlashMenuItem.js +57 -0
  161. package/vanilla/UiEditorTableContextMenu.d.ts +5 -2
  162. package/vanilla/UiEditorTableContextMenu.js +1 -2
  163. package/vanilla/UiEditorTableOverlay.d.ts +1 -1
  164. package/vanilla/UiEditorTableOverlay.js +5 -5
  165. package/vanilla/UiEditorTableToolbar.d.ts +4 -2
  166. package/vanilla/UiEditorTableToolbar.js +1 -2
  167. package/vanilla/UiEditorToolbar.js +2 -0
  168. package/vanilla/UiGrid.d.ts +1 -0
  169. package/vanilla/UiGrid.js +8 -7
  170. package/vanilla/UiIcon.js +1 -1
  171. package/vanilla/UiIconButton.d.ts +1 -0
  172. package/vanilla/UiIconButton.js +11 -2
  173. package/vanilla/UiListbox.d.ts +20 -0
  174. package/vanilla/UiListbox.js +45 -0
  175. package/vanilla/UiMenu.d.ts +1 -1
  176. package/vanilla/UiMenu.js +1 -1
  177. package/vanilla/UiMenuGroup.d.ts +14 -0
  178. package/vanilla/UiMenuGroup.js +35 -0
  179. package/vanilla/UiMenuItem.d.ts +5 -1
  180. package/vanilla/UiMenuItem.js +91 -30
  181. package/vanilla/UiPopover.d.ts +1 -1
  182. package/vanilla/UiPopover.js +1 -1
  183. package/vanilla/UiRadioGroup.d.ts +1 -0
  184. package/vanilla/UiRadioGroup.js +6 -3
  185. package/vanilla/UiSwitcher.d.ts +1 -0
  186. package/vanilla/UiSwitcher.js +7 -9
  187. package/vanilla/UiTabs.d.ts +1 -0
  188. package/vanilla/UiTabs.js +9 -1
  189. package/vanilla/UiTextarea.d.ts +1 -0
  190. package/vanilla/UiTextarea.js +4 -1
  191. package/vanilla/UiToast.d.ts +16 -0
  192. package/vanilla/UiToast.js +53 -0
  193. package/vanilla/UiToastRegion.d.ts +1 -4
  194. package/vanilla/UiToastRegion.js +3 -11
  195. package/vanilla/UiTooltip.d.ts +1 -1
  196. package/vanilla/UiTooltip.js +1 -1
  197. package/vanilla/UiTree.d.ts +2 -0
  198. package/vanilla/UiTree.js +1 -1
  199. package/vanilla/UiTreeItem.d.ts +1 -0
  200. package/vanilla/UiTreeItem.js +45 -31
  201. package/vanilla/index.js +6 -0
  202. package/vue/UiAvatar.d.ts +4 -0
  203. package/vue/UiAvatar.vue +23 -3
  204. package/vue/UiAvatarGroup.d.ts +3 -0
  205. package/vue/UiAvatarGroup.vue +7 -4
  206. package/vue/UiBadge.d.ts +2 -2
  207. package/vue/UiBadge.vue +74 -2
  208. package/vue/UiButton.d.ts +5 -3
  209. package/vue/UiButton.vue +41 -7
  210. package/vue/UiCallout.d.ts +2 -2
  211. package/vue/UiCallout.vue +3 -2
  212. package/vue/UiCheckbox.d.ts +2 -0
  213. package/vue/UiCheckbox.vue +16 -0
  214. package/vue/UiCombobox.d.ts +11 -3
  215. package/vue/UiCombobox.vue +60 -11
  216. package/vue/UiContextMenu.d.ts +2 -0
  217. package/vue/UiContextMenu.vue +47 -6
  218. package/vue/UiDialog.d.ts +18 -4
  219. package/vue/UiDialog.vue +86 -11
  220. package/vue/UiDisclosure.d.ts +8 -2
  221. package/vue/UiDisclosure.vue +38 -14
  222. package/vue/UiEditable.d.ts +10 -0
  223. package/vue/UiEditable.vue +14 -2
  224. package/vue/UiEditorInsertTableGrid.vue +52 -15
  225. package/vue/UiEditorMentionMenu.d.ts +24 -2
  226. package/vue/UiEditorMentionMenu.vue +27 -11
  227. package/vue/UiEditorMentionMenuItem.d.ts +24 -0
  228. package/vue/UiEditorMentionMenuItem.js +2 -0
  229. package/vue/UiEditorMentionMenuItem.vue +88 -0
  230. package/vue/UiEditorSlashMenu.d.ts +24 -1
  231. package/vue/UiEditorSlashMenu.vue +33 -33
  232. package/vue/UiEditorSlashMenuGroup.d.ts +18 -0
  233. package/vue/UiEditorSlashMenuGroup.js +2 -0
  234. package/vue/UiEditorSlashMenuGroup.vue +43 -0
  235. package/vue/UiEditorSlashMenuItem.d.ts +26 -0
  236. package/vue/UiEditorSlashMenuItem.js +2 -0
  237. package/vue/UiEditorSlashMenuItem.vue +101 -0
  238. package/vue/UiEditorTableContextMenu.d.ts +9 -3
  239. package/vue/UiEditorTableContextMenu.vue +53 -3
  240. package/vue/UiEditorTableOverlay.d.ts +2 -2
  241. package/vue/UiEditorTableOverlay.vue +8 -6
  242. package/vue/UiEditorTableToolbar.d.ts +7 -3
  243. package/vue/UiEditorTableToolbar.vue +50 -2
  244. package/vue/UiEditorToolbar.vue +14 -1
  245. package/vue/UiFloatingActionButton.vue +1 -1
  246. package/vue/UiFormField.vue +28 -3
  247. package/vue/UiGrid.d.ts +1 -0
  248. package/vue/UiGrid.vue +26 -2
  249. package/vue/UiIcon.vue +16 -0
  250. package/vue/UiIconButton.d.ts +5 -3
  251. package/vue/UiIconButton.vue +40 -6
  252. package/vue/UiInput.vue +10 -3
  253. package/vue/UiInputGroup.vue +32 -11
  254. package/vue/UiListbox.d.ts +33 -0
  255. package/vue/UiListbox.js +2 -0
  256. package/vue/UiListbox.vue +131 -0
  257. package/vue/UiMenu.d.ts +2 -0
  258. package/vue/UiMenu.vue +43 -6
  259. package/vue/UiMenuGroup.d.ts +18 -0
  260. package/vue/UiMenuGroup.js +2 -0
  261. package/vue/UiMenuGroup.vue +39 -0
  262. package/vue/UiMenuItem.d.ts +34 -2
  263. package/vue/UiMenuItem.vue +145 -9
  264. package/vue/UiPopover.d.ts +2 -2
  265. package/vue/UiPopover.vue +44 -5
  266. package/vue/UiRadioGroup.d.ts +2 -0
  267. package/vue/UiRadioGroup.vue +17 -0
  268. package/vue/UiSearchShell.vue +12 -5
  269. package/vue/UiSelect.vue +18 -4
  270. package/vue/UiSwitch.vue +2 -2
  271. package/vue/UiSwitcher.d.ts +1 -0
  272. package/vue/UiSwitcher.vue +22 -1
  273. package/vue/UiTabs.d.ts +6 -2
  274. package/vue/UiTabs.vue +14 -12
  275. package/vue/UiTextarea.d.ts +2 -0
  276. package/vue/UiTextarea.vue +32 -4
  277. package/vue/UiToast.d.ts +34 -0
  278. package/vue/UiToast.js +2 -0
  279. package/vue/UiToast.vue +173 -0
  280. package/vue/UiToastRegion.d.ts +2 -18
  281. package/vue/UiToastRegion.vue +123 -48
  282. package/vue/UiTooltip.d.ts +2 -2
  283. package/vue/UiTooltip.vue +67 -12
  284. package/vue/UiTree.d.ts +10 -0
  285. package/vue/UiTree.vue +27 -0
  286. package/vue/UiTreeItem.d.ts +2 -0
  287. package/vue/UiTreeItem.vue +96 -4
  288. package/vue/chunks/UiAvatar.js +19 -5
  289. package/vue/chunks/UiAvatarGroup.js +10 -4
  290. package/vue/chunks/UiBadge.js +1 -1
  291. package/vue/chunks/UiButton.js +58 -11
  292. package/vue/chunks/UiCallout.js +2 -2
  293. package/vue/chunks/UiCheckbox.js +12 -2
  294. package/vue/chunks/UiCombobox.js +29 -13
  295. package/vue/chunks/UiContextMenu.js +6 -2
  296. package/vue/chunks/UiDialog.js +25 -8
  297. package/vue/chunks/UiDisclosure.js +36 -12
  298. package/vue/chunks/UiEditable.js +22 -8
  299. package/vue/chunks/UiEditorInsertTableGrid.js +11 -14
  300. package/vue/chunks/UiEditorMentionMenu.js +30 -19
  301. package/vue/chunks/UiEditorMentionMenuItem.js +36 -0
  302. package/vue/chunks/UiEditorSlashMenu.js +33 -35
  303. package/vue/chunks/UiEditorSlashMenuGroup.js +26 -0
  304. package/vue/chunks/UiEditorSlashMenuItem.js +51 -0
  305. package/vue/chunks/UiEditorTableContextMenu.js +39 -5
  306. package/vue/chunks/UiEditorTableOverlay.js +6 -6
  307. package/vue/chunks/UiEditorTableToolbar.js +37 -4
  308. package/vue/chunks/UiEditorToolbar.js +18 -4
  309. package/vue/chunks/UiFloatingActionButton.js +1 -1
  310. package/vue/chunks/UiFormField.js +13 -9
  311. package/vue/chunks/UiGrid.js +6 -2
  312. package/vue/chunks/UiIcon.js +40 -2
  313. package/vue/chunks/UiIconButton.js +35 -4
  314. package/vue/chunks/UiInput.js +1 -1
  315. package/vue/chunks/UiInputGroup.js +1 -1
  316. package/vue/chunks/UiListbox.js +70 -0
  317. package/vue/chunks/UiMenu.js +6 -2
  318. package/vue/chunks/UiMenuGroup.js +25 -0
  319. package/vue/chunks/UiMenuItem.js +128 -7
  320. package/vue/chunks/UiPopover.js +1 -1
  321. package/vue/chunks/UiRadioGroup.js +8 -2
  322. package/vue/chunks/UiSearchShell.js +1 -1
  323. package/vue/chunks/UiSelect.js +1 -1
  324. package/vue/chunks/UiSwitch.js +1 -1
  325. package/vue/chunks/UiSwitcher.js +20 -3
  326. package/vue/chunks/UiTabs.js +4 -3
  327. package/vue/chunks/UiTextarea.js +7 -2
  328. package/vue/chunks/UiToast.js +81 -0
  329. package/vue/chunks/UiToastRegion.js +25 -49
  330. package/vue/chunks/UiTooltip.js +3 -7
  331. package/vue/chunks/UiTree.js +8 -4
  332. package/vue/chunks/UiTreeItem.js +28 -10
  333. package/vue/components.css +1840 -851
  334. package/vue/editor/index.js +263 -2
  335. package/vue/index.d.ts +6 -0
  336. package/vue/index.js +9 -3
@@ -0,0 +1,68 @@
1
+ let toastSequence = 0;
2
+
3
+ function inputFor(event) {
4
+ return event.detail === 0 ? "keyboard" : "pointer";
5
+ }
6
+
7
+ /** Authored toasts request dismissal; their consumer owns removal from the rendered list. */
8
+ export default function controller(host) {
9
+ const element = host.element;
10
+ const { action } = host.refs;
11
+ if (!element.id) element.id = `ui-toast-authored-${++toastSequence}`;
12
+ let remaining = Math.max(0, Number(host.state.duration) || 0);
13
+ let started = 0;
14
+ let timer = null;
15
+ let paused = false;
16
+ let dismissed = false;
17
+
18
+ const dismiss = (reason, trigger) => {
19
+ if (dismissed) return;
20
+ dismissed = true;
21
+ clearTimeout(timer);
22
+ host.dispatch("dismiss", { id: element.id, reason, trigger });
23
+ };
24
+ const start = () => {
25
+ clearTimeout(timer);
26
+ if (paused || dismissed || remaining <= 0) return;
27
+ started = Date.now();
28
+ timer = setTimeout(() => dismiss("timeout", "programmatic"), remaining);
29
+ };
30
+ const pause = () => {
31
+ if (paused) return;
32
+ paused = true;
33
+ if (timer !== null) remaining = Math.max(0, remaining - (Date.now() - started));
34
+ clearTimeout(timer);
35
+ timer = null;
36
+ };
37
+ const resume = () => {
38
+ if (!paused || element.matches(":hover, :focus-within")) return;
39
+ paused = false;
40
+ if (host.state.duration > 0 && remaining <= 0) { dismiss("timeout", "programmatic"); return; }
41
+ start();
42
+ };
43
+ const onFocusOut = () => queueMicrotask(resume);
44
+ const onClick = (event) => {
45
+ if (event.target.closest?.(".dismiss") || action?.contains(event.target)) dismiss("action", inputFor(event));
46
+ };
47
+ const stop = host.effect(() => {
48
+ const duration = Math.max(0, Number(host.state.duration) || 0);
49
+ if (duration === remaining || dismissed) return;
50
+ remaining = duration;
51
+ start();
52
+ });
53
+ element.addEventListener("click", onClick);
54
+ element.addEventListener("pointerenter", pause);
55
+ element.addEventListener("pointerleave", resume);
56
+ element.addEventListener("focusin", pause);
57
+ element.addEventListener("focusout", onFocusOut);
58
+ start();
59
+ return () => {
60
+ stop();
61
+ clearTimeout(timer);
62
+ element.removeEventListener("click", onClick);
63
+ element.removeEventListener("pointerenter", pause);
64
+ element.removeEventListener("pointerleave", resume);
65
+ element.removeEventListener("focusin", pause);
66
+ element.removeEventListener("focusout", onFocusOut);
67
+ };
68
+ }
@@ -1,35 +1,48 @@
1
1
  <link rel="component" href="../ui-icon-button/ui-icon-button.html">
2
+ <link rel="component" href="../ui-icon/ui-icon.html">
2
3
  <template component="ui-toast-region" controller="./ui-toast-region.js">
3
4
  <defs>
4
- <prop name="open" type="boolean" default="false">Whether the messages authored inside the region are visible.</prop>
5
- <prop name="message" type="string" default="Notification">Fallback message used by the declarative show command.</prop>
6
- <prop name="auto" type="boolean" default="false"
7
- >Dismiss each message automatically after its duration. Timers pause while the pointer or focus is on a message.</prop>
8
- <prop name="duration" type="number" default="5000">Milliseconds an automatically dismissed message stays visible.</prop>
9
- <state name="internalOpen" :value="false"></state>
10
- <state name="toasts" type="list(object({ id: string, message: string, role: alert | status, closing: boolean }))" :value="[]"></state>
11
- <event
12
- name="close"
13
- type="object({ open: boolean, reason: action | timeout | programmatic | light-dismiss | escape, trigger: keyboard | pointer | programmatic })"
14
- ></event>
5
+ <prop name="duration" type="number" default="0"
6
+ >Default milliseconds for toasts added with show() or the show command; 0 keeps each until dismissed. Hover or focus pauses a timer.</prop>
7
+ <prop name="placement" type="top | top-start | top-end | bottom | bottom-start | bottom-end" default="top"
8
+ >Screen edge where the stack appears. Start and end follow the writing direction.</prop>
9
+ <state
10
+ name="toasts"
11
+ type="list(object({ id: string, message: string, tone: neutral | info | success | warning | danger, role: alert | status, closing: boolean }))"
12
+ :value="[]"
13
+ ></state>
15
14
  <event name="dismiss" type="object({ id: string, reason: action | timeout, trigger: keyboard | pointer | programmatic })"></event>
16
15
  <method name="show" export="show" returns="promise(string)"></method>
17
16
  </defs>
18
17
  <div role="region" aria-label="Notifications" aria-live="polite" popover="manual">
19
18
  <slot></slot>
20
- <div $each="toast of toasts" $key="toast.id" class="toast" class:closing="toast.closing" :id="toast.id" :role="toast.role">
19
+ <div $each="toast of toasts" $key="toast.id" class="toast" class:closing="toast.closing" :id="toast.id" :role="toast.role" :data-tone="toast.tone">
21
20
  <span class="message" $value="toast.message"></span>
22
21
  <span class="dismiss" :data-toast="toast.id">
23
22
  <ui-icon-button size="sm" variant="ghost" :round="true" :label="format('Dismiss %s', toast.message)">
24
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">
25
- <path d="M18 6 6 18M6 6l12 12"></path>
26
- </svg>
23
+ <ui-icon name="x"></ui-icon>
27
24
  </ui-icon-button>
28
25
  </span>
29
26
  </div>
30
27
  </div>
31
28
  <style>
32
29
  /* This component's hooks style the element they are set on, and never a nested one. */
30
+ @property --ui-toast-region-surface {
31
+ syntax: "*";
32
+ inherits: false;
33
+ }
34
+ @property --ui-toast-region-border {
35
+ syntax: "*";
36
+ inherits: false;
37
+ }
38
+ @property --ui-toast-region-radius {
39
+ syntax: "*";
40
+ inherits: false;
41
+ }
42
+ @property --ui-toast-region-shadow {
43
+ syntax: "*";
44
+ inherits: false;
45
+ }
33
46
  @property --ui-toast-region-enter-duration {
34
47
  syntax: "*";
35
48
  inherits: false;
@@ -41,6 +54,10 @@
41
54
 
42
55
  :host {
43
56
  /* Hooks do not inherit, so the parts below read them from the root. */
57
+ --_ui-toast-region-surface: var(--ui-toast-region-surface);
58
+ --_ui-toast-region-border: var(--ui-toast-region-border);
59
+ --_ui-toast-region-radius: var(--ui-toast-region-radius);
60
+ --_ui-toast-region-shadow: var(--ui-toast-region-shadow);
44
61
  --_ui-toast-region-enter-duration: var(--ui-toast-region-enter-duration);
45
62
  --_ui-toast-region-exit-duration: var(--ui-toast-region-exit-duration);
46
63
 
@@ -70,6 +87,29 @@
70
87
  display: none;
71
88
  }
72
89
 
90
+ :host-state([placement="top-start"]),
91
+ :host-state([placement="bottom-start"]) {
92
+ left: auto;
93
+ right: auto;
94
+ inset-inline-start: calc(var(--ui-space-4) + env(safe-area-inset-left));
95
+ transform: none;
96
+ }
97
+
98
+ :host-state([placement="top-end"]),
99
+ :host-state([placement="bottom-end"]) {
100
+ left: auto;
101
+ right: auto;
102
+ inset-inline-end: calc(var(--ui-space-4) + env(safe-area-inset-right));
103
+ transform: none;
104
+ }
105
+
106
+ :host-state([placement="bottom"]),
107
+ :host-state([placement="bottom-start"]),
108
+ :host-state([placement="bottom-end"]) {
109
+ top: auto;
110
+ bottom: calc(var(--ui-space-4) + env(safe-area-inset-bottom));
111
+ }
112
+
73
113
  :host > .toast,
74
114
  :host > :slotted(*) {
75
115
  pointer-events: auto;
@@ -80,21 +120,42 @@
80
120
  grid-template-columns: 1fr auto;
81
121
  gap: var(--ui-space-2);
82
122
  align-items: start;
83
- border: 1px solid var(--ui-border-default);
84
- border-radius: var(--ui-radius-lg);
123
+ border: 1px solid var(--_ui-toast-region-border, var(--ui-overlay-border, var(--ui-border-default)));
124
+ border-radius: var(--_ui-toast-region-radius, var(--ui-overlay-radius, var(--ui-radius-lg)));
85
125
  padding: var(--ui-space-3) var(--ui-space-4);
86
- background: var(--ui-surface-elevated);
126
+ background: var(--_ui-toast-region-surface, var(--ui-overlay-surface, var(--ui-surface-elevated)));
87
127
  color: var(--ui-text-primary);
88
- box-shadow: var(--ui-elevation-overlay, var(--ui-shadow-lg));
128
+ box-shadow: var(--_ui-toast-region-shadow, var(--ui-overlay-shadow, var(--ui-elevation-overlay, var(--ui-shadow-lg))));
89
129
  animation: ui-toast-enter var(--_ui-toast-region-enter-duration, 0.32s) var(--ui-motion-ease-out, cubic-bezier(0.16, 1, 0.3, 1)) both;
90
130
  }
91
131
 
132
+ .toast[data-tone="info"] {
133
+ border-inline-start: 3px solid var(--ui-info-solid);
134
+ }
135
+ .toast[data-tone="success"] {
136
+ border-inline-start: 3px solid var(--ui-success-solid);
137
+ }
138
+ .toast[data-tone="warning"] {
139
+ border-inline-start: 3px solid var(--ui-warning-solid);
140
+ }
141
+ .toast[data-tone="danger"] {
142
+ border-inline-start: 3px solid var(--ui-danger-solid);
143
+ }
144
+
92
145
  /* Leaving slides on down and fades; the controller removes the toast when this ends. */
93
146
  .toast.closing {
94
147
  animation: ui-toast-exit var(--_ui-toast-region-exit-duration, 0.22s) var(--ui-motion-ease) both;
95
148
  pointer-events: none;
96
149
  }
97
150
 
151
+ :is(:host-state([placement="bottom"]), :host-state([placement="bottom-start"]), :host-state([placement="bottom-end"])) .toast {
152
+ animation-name: ui-toast-enter-bottom;
153
+ }
154
+
155
+ :is(:host-state([placement="bottom"]), :host-state([placement="bottom-start"]), :host-state([placement="bottom-end"])) .toast.closing {
156
+ animation-name: ui-toast-exit-bottom;
157
+ }
158
+
98
159
  @keyframes ui-toast-enter {
99
160
  from {
100
161
  opacity: 0;
@@ -117,6 +178,28 @@
117
178
  }
118
179
  }
119
180
 
181
+ @keyframes ui-toast-enter-bottom {
182
+ from {
183
+ opacity: 0;
184
+ transform: translateY(1rem);
185
+ }
186
+ to {
187
+ opacity: 1;
188
+ transform: translateY(0);
189
+ }
190
+ }
191
+
192
+ @keyframes ui-toast-exit-bottom {
193
+ from {
194
+ opacity: 1;
195
+ transform: translateY(0);
196
+ }
197
+ to {
198
+ opacity: 0;
199
+ transform: translateY(-0.75rem);
200
+ }
201
+ }
202
+
120
203
  @media (prefers-reduced-motion: reduce) {
121
204
  .toast, .toast.closing {
122
205
  animation: none;
@@ -8,19 +8,16 @@ export async function show(host, message, options = {}) {
8
8
  }
9
9
 
10
10
  /**
11
- * Shows the region while `open` is set and it has authored messages, or while any message added
12
- * with show() or the --show-toast command remains. Added messages dismiss by action or timeout.
11
+ * Shows the region while it has authored or generated messages. Generated messages dismiss by
12
+ * action or timeout; authored ui-toast children own their removal through the dismiss event.
13
13
  */
14
14
  export default function controller(host) {
15
15
  const element = host.element;
16
16
  const [trigger, stopTracking] = trackTrigger(host);
17
- let external = host.state.open;
18
- host.state.internalOpen = Boolean(external);
19
-
20
17
  const toasts = () => host.state.toasts ?? [];
21
18
  const authored = () => Array.from(element.children).some((child) => !child.classList.contains("toast"));
22
19
  const sync = () => {
23
- const visible = (host.state.internalOpen && authored()) || toasts().length > 0;
20
+ const visible = authored() || toasts().length > 0;
24
21
  if (visible && !element.matches(":popover-open")) element.showPopover();
25
22
  else if (!visible && element.matches(":popover-open")) element.hidePopover();
26
23
  };
@@ -59,7 +56,6 @@ export default function controller(host) {
59
56
  if (!toast || toast.closing) return;
60
57
  host.state.toasts = toasts().map((candidate) => candidate.id === id ? { ...candidate, closing: true } : candidate);
61
58
  host.dispatch("dismiss", { id, reason, trigger: how });
62
- if (toasts().every((candidate) => candidate.closing)) host.dispatch("close", { open: false, reason, trigger: how });
63
59
  // Removed once the exit animation ends; reduced motion has none, and the timeout guards a missed end.
64
60
  const node = element.ownerDocument.getElementById(id);
65
61
  if (!node || getComputedStyle(node).animationName === "none") remove(id);
@@ -70,9 +66,11 @@ export default function controller(host) {
70
66
  };
71
67
  const add = (message, options = {}) => {
72
68
  const id = String(options.id || `ui-toast-${++toastIds}`);
73
- host.state.toasts = [...toasts(), { id, message: String(message), role: options.tone === "danger" ? "alert" : "status", closing: false }];
74
- if (options.auto ?? host.state.auto) {
75
- timers.set(id, { remaining: Math.max(0, Number(options.duration ?? host.state.duration ?? 5000)), started: 0, handle: 0 });
69
+ const tone = ["neutral", "info", "success", "warning", "danger"].includes(options.tone) ? options.tone : "neutral";
70
+ host.state.toasts = [...toasts(), { id, message: String(message), tone, role: tone === "danger" ? "alert" : "status", closing: false }];
71
+ const duration = Math.max(0, Number(options.duration ?? host.state.duration ?? 0));
72
+ if (duration > 0) {
73
+ timers.set(id, { remaining: duration, started: 0, handle: 0 });
76
74
  startTimer(id);
77
75
  }
78
76
  return id;
@@ -83,15 +81,9 @@ export default function controller(host) {
83
81
  if (id) dismiss(id, "action", trigger());
84
82
  };
85
83
  const onCommand = (event) => {
86
- if (event.command === "--show-toast") add(event.source?.value || host.state.message || "Notification");
84
+ if (event.command === "--show-toast" && event.source?.value) add(event.source.value);
87
85
  };
88
- const stop = host.effect(() => {
89
- if (host.state.open !== external) {
90
- external = host.state.open;
91
- host.state.internalOpen = Boolean(external);
92
- }
93
- sync();
94
- });
86
+ const stop = host.effect(sync);
95
87
  const observer = new MutationObserver(sync);
96
88
  observer.observe(element, { childList: true });
97
89
  element.addEventListener("click", onClick);
@@ -2,7 +2,11 @@
2
2
  <defs>
3
3
  <prop name="for" type="string" default="">ID of the control described and visually anchored by the tooltip.</prop>
4
4
  <prop name="open" type="boolean" default="false">Whether the tooltip is open initially or when set externally.</prop>
5
- <prop name="placement" type="string" default="top-start">Positions the description relative to its trigger.</prop>
5
+ <prop
6
+ name="placement"
7
+ type="top | top-start | top-end | bottom | bottom-start | bottom-end | left | left-start | left-end | right | right-start | right-end"
8
+ default="top"
9
+ >Positions the description around its trigger. Start and end on top or bottom follow the writing direction.</prop>
6
10
  <prop name="trigger" type="hover | click | focus" default="hover"
7
11
  >How the trigger opens it. hover also opens on keyboard focus; click toggles it and suits a help button, where hovering a question mark says nothing; focus
8
12
  opens only while the trigger holds focus.</prop>
@@ -42,6 +46,10 @@
42
46
  syntax: "*";
43
47
  inherits: false;
44
48
  }
49
+ @property --ui-tooltip-offset {
50
+ syntax: "*";
51
+ inherits: false;
52
+ }
45
53
  @property --ui-tooltip-radius {
46
54
  syntax: "*";
47
55
  inherits: false;
@@ -93,19 +101,19 @@
93
101
  }
94
102
 
95
103
  .surface {
96
- --_tooltip-line: var(--_ui-tooltip-border, var(--ui-border-default));
104
+ --_tooltip-line: var(--_ui-tooltip-border, var(--ui-overlay-border, var(--ui-border-default)));
97
105
  position: relative;
98
106
  box-sizing: border-box;
99
107
  max-inline-size: var(--_ui-tooltip-max-inline-size, 18rem);
100
108
  border: 1px solid var(--_tooltip-line);
101
- border-radius: var(--_ui-tooltip-radius, var(--ui-radius-sm));
102
- background: var(--_ui-tooltip-surface, var(--ui-surface-elevated));
109
+ border-radius: var(--_ui-tooltip-radius, var(--ui-overlay-radius, var(--ui-radius-sm)));
110
+ background: var(--_ui-tooltip-surface, var(--ui-overlay-surface, var(--ui-surface-elevated)));
103
111
  color: var(--_ui-tooltip-text, var(--ui-text-primary));
104
112
  padding: var(--ui-space-1) var(--ui-space-2);
105
113
  font-family: var(--ui-font-family-sans);
106
114
  font-size: var(--ui-font-size-sm);
107
115
  line-height: var(--ui-line-height-tight);
108
- box-shadow: var(--_ui-tooltip-shadow, var(--ui-elevation-tooltip, var(--ui-shadow-md)));
116
+ box-shadow: var(--_ui-tooltip-shadow, var(--ui-overlay-shadow, var(--ui-elevation-tooltip, var(--ui-shadow-md))));
109
117
  }
110
118
 
111
119
  :host-state([inverse]) .surface {
@@ -133,7 +141,7 @@
133
141
  transform: rotate(45deg);
134
142
  }
135
143
 
136
- :is(:host-state([placement="bottom"]), :host-state([placement="bottom-start"]), :host-state([placement="bottom-end"])) .surface::after {
144
+ :host[data-ui-actual-placement="bottom"] .surface::after {
137
145
  inset-block-start: calc(var(--_ui-tooltip-arrow-size, 0.5rem) / -2 - 0.5px);
138
146
  inset-block-end: auto;
139
147
  border-inline-end-width: 0;
@@ -144,11 +152,47 @@
144
152
  border-start-start-radius: 1px;
145
153
  }
146
154
 
147
- :is(:host-state([placement="top-end"]), :host-state([placement="bottom-end"])) .surface::after {
155
+ :host[data-ui-actual-align="center"] .surface::after {
156
+ inset-inline-start: calc(50% - var(--_ui-tooltip-arrow-size, 0.5rem) / 2);
157
+ }
158
+
159
+ :host[data-ui-actual-align="end"] .surface::after {
148
160
  inset-inline-start: auto;
149
161
  inset-inline-end: var(--ui-space-3);
150
162
  }
151
163
 
164
+ :host[data-ui-actual-placement="left"] .surface::after,
165
+ :host[data-ui-actual-placement="right"] .surface::after {
166
+ inset-block-start: calc(50% - var(--_ui-tooltip-arrow-size, 0.5rem) / 2);
167
+ inset-block-end: auto;
168
+ border: 0 solid var(--_tooltip-line);
169
+ }
170
+
171
+ :host[data-ui-actual-placement="left"] .surface::after {
172
+ inset-inline-start: auto;
173
+ inset-inline-end: calc(var(--_ui-tooltip-arrow-size, 0.5rem) / -2 - 0.5px);
174
+ border-inline-end-width: 1px;
175
+ border-block-end-width: 1px;
176
+ }
177
+
178
+ :host[data-ui-actual-placement="right"] .surface::after {
179
+ inset-inline-start: calc(var(--_ui-tooltip-arrow-size, 0.5rem) / -2 - 0.5px);
180
+ inset-inline-end: auto;
181
+ border-inline-start-width: 1px;
182
+ border-block-start-width: 1px;
183
+ }
184
+
185
+ :host[data-ui-actual-placement="left"][data-ui-actual-align="start"] .surface::after,
186
+ :host[data-ui-actual-placement="right"][data-ui-actual-align="start"] .surface::after {
187
+ inset-block-start: var(--ui-space-3);
188
+ }
189
+
190
+ :host[data-ui-actual-placement="left"][data-ui-actual-align="end"] .surface::after,
191
+ :host[data-ui-actual-placement="right"][data-ui-actual-align="end"] .surface::after {
192
+ inset-block-start: auto;
193
+ inset-block-end: var(--ui-space-3);
194
+ }
195
+
152
196
  :host:popover-open {
153
197
  opacity: 1;
154
198
  transform: translateY(0);
@@ -1,8 +1,30 @@
1
1
  import { closeOverlay, createAnchoredSurface, createIdResolver, openOverlay } from "../shared/overlay.js";
2
2
 
3
+ const warmTooltips = new WeakMap();
4
+
3
5
  export default function controller(host) {
4
6
  const element = host.element;
5
7
  const document = element.ownerDocument;
8
+ const warm = warmTooltips.get(document) ?? { active: 0, until: 0 };
9
+ warmTooltips.set(document, warm);
10
+ let counted = false;
11
+ const trackVisible = (visible) => {
12
+ if (counted === visible) return;
13
+ counted = visible;
14
+ if (visible) warm.active += 1;
15
+ else {
16
+ warm.active = Math.max(0, warm.active - 1);
17
+ warm.until = Date.now() + 400;
18
+ }
19
+ };
20
+ const offset = () => {
21
+ const value = getComputedStyle(element).getPropertyValue("--ui-tooltip-offset").trim();
22
+ const amount = Number.parseFloat(value);
23
+ if (!Number.isFinite(amount)) return 4;
24
+ if (value.endsWith("rem")) return amount * Number.parseFloat(getComputedStyle(document.documentElement).fontSize);
25
+ if (value.endsWith("em")) return amount * Number.parseFloat(getComputedStyle(element).fontSize);
26
+ return amount;
27
+ };
6
28
  const overlayId = element.id || `ui-tooltip-${Math.random().toString(36).slice(2, 11)}`;
7
29
  if (!element.id) element.id = overlayId;
8
30
  let focused = false;
@@ -21,6 +43,7 @@ export default function controller(host) {
21
43
  const setOpen = (open, input) => {
22
44
  if (Boolean(host.state.internalOpen) === open) return;
23
45
  host.state.internalOpen = open;
46
+ trackVisible(open);
24
47
  if (trigger && host.state.trigger === "click") trigger.setAttribute("aria-expanded", String(open));
25
48
  host.dispatch(open ? "open" : "close", { open, reason: "action", trigger: input });
26
49
  };
@@ -37,7 +60,7 @@ export default function controller(host) {
37
60
  showTimer = setTimeout(() => {
38
61
  showTimer = null;
39
62
  setOpen(true, "pointer");
40
- }, Math.max(0, Number(host.state.showDelay ?? 500)));
63
+ }, warm.active > 0 || warm.until > Date.now() ? 0 : Math.max(0, Number(host.state.showDelay ?? 500)));
41
64
  };
42
65
  const onLeave = () => {
43
66
  clearShow();
@@ -101,7 +124,7 @@ export default function controller(host) {
101
124
  });
102
125
  const setup = () => {
103
126
  const nextFor = String(host.state.for ?? "");
104
- const nextPlacement = String(host.state.placement ?? "top-start");
127
+ const nextPlacement = String(host.state.placement ?? "top");
105
128
  if (surface && nextFor === lastFor && nextPlacement === lastPlacement) return;
106
129
  lastFor = nextFor;
107
130
  lastPlacement = nextPlacement;
@@ -112,12 +135,13 @@ export default function controller(host) {
112
135
  attach();
113
136
  }
114
137
  surface?.destroy();
115
- surface = createAnchoredSurface(element, { anchor: trigger, placement: nextPlacement });
138
+ surface = createAnchoredSurface(element, { anchor: trigger, placement: nextPlacement, gap: offset });
116
139
  };
117
140
  const close = (reason, input) => {
118
141
  if (reason !== "escape" && reason !== "light-dismiss") return;
119
142
  clearTimers();
120
143
  host.state.internalOpen = false;
144
+ trackVisible(false);
121
145
  host.dispatch("close", { open: false, reason, trigger: input });
122
146
  };
123
147
  const apply = () => {
@@ -128,6 +152,7 @@ export default function controller(host) {
128
152
  }
129
153
  setup();
130
154
  const open = Boolean(host.state.internalOpen);
155
+ trackVisible(open);
131
156
  element.hidden = !open;
132
157
  if (open) {
133
158
  surface?.show();
@@ -146,6 +171,7 @@ export default function controller(host) {
146
171
  stop();
147
172
  ids.stop();
148
173
  clearTimers();
174
+ trackVisible(false);
149
175
  detach();
150
176
  surface?.destroy();
151
177
  element.removeEventListener("pointerenter", onSurfaceEnter);
@@ -3,6 +3,9 @@
3
3
  <defs>
4
4
  <prop name="density" type="comfortable | compact" default="comfortable">Compact trades padding and type size for rows that fit more on screen.</prop>
5
5
  <prop name="label" type="string" default="Tree">The tree's accessible name, announced when focus enters it. Name what the tree holds.</prop>
6
+ <prop name="selection" type="none | single | multiple" default="none"
7
+ >How selection works. None keeps rows for navigation and expansion; single requests one selected item; multiple shows checkboxes and requests an item with
8
+ its descendants. The application owns each Tree Item's selected state and responds to select.</prop>
6
9
  <prop name="hoverExpandDelay" type="number" default="700"
7
10
  >How long, in milliseconds, a dragged item must hover over the middle of a collapsed branch before the branch opens to receive it. 0 opens it at
8
11
  once.</prop>
@@ -20,6 +23,7 @@
20
23
  name="reorder-rejected"
21
24
  type="object({ sourceId: string, targetId: string, position: before | inside | after, reason: max-depth, maxDepth: integer, resultingDepth: integer, trigger: keyboard | pointer | programmatic })"
22
25
  ></event>
26
+ <event name="select" type="object({ ids: list(string), trigger: keyboard | pointer | programmatic })"></event>
23
27
  </defs>
24
28
  <div role="tree"><slot></slot></div>
25
29
  <style>
@@ -64,6 +68,14 @@
64
68
  padding-inline-start: var(--ui-tree-gutter, 0px);
65
69
  }
66
70
 
71
+ :host[data-move-mode]::after {
72
+ content: "Choose a destination row, or use Up and Down; Right nests inside a branch. Enter places it, Escape cancels.";
73
+ display: block;
74
+ padding: var(--ui-space-2);
75
+ color: var(--ui-text-secondary);
76
+ font-size: var(--ui-font-size-sm);
77
+ }
78
+
67
79
  /* A component that sets its display still honours the hidden attribute on its root. */
68
80
  :host[hidden] {
69
81
  display: none;