@threadlabs/looma 0.14.7 → 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.
- package/components/shared/icons.js +5 -0
- package/components/shared/menu-navigation.js +49 -0
- package/components/shared/overlay.js +33 -60
- package/components/ui-avatar/ui-avatar.html +19 -2
- package/components/ui-avatar/ui-avatar.js +7 -1
- package/components/ui-avatar-group/ui-avatar-group.html +8 -3
- package/components/ui-avatar-group/ui-avatar-group.js +6 -2
- package/components/ui-badge/ui-badge.html +70 -4
- package/components/ui-button/ui-button.html +21 -5
- package/components/ui-button/ui-button.js +10 -0
- package/components/ui-callout/ui-callout.html +13 -5
- package/components/ui-checkbox/ui-checkbox.html +11 -0
- package/components/ui-combobox/ui-combobox.html +50 -10
- package/components/ui-combobox/ui-combobox.js +11 -8
- package/components/ui-context-menu/ui-context-menu.html +33 -5
- package/components/ui-context-menu/ui-context-menu.js +26 -17
- package/components/ui-dialog/ui-dialog.html +51 -8
- package/components/ui-dialog/ui-dialog.js +28 -7
- package/components/ui-disclosure/ui-disclosure.html +14 -5
- package/components/ui-disclosure/ui-disclosure.js +51 -4
- package/components/ui-editable/ui-editable.html +17 -3
- package/components/ui-editable/ui-editable.js +7 -0
- package/components/ui-editor-insert-table-grid/ui-editor-insert-table-grid.html +48 -14
- package/components/ui-editor-insert-table-grid/ui-editor-insert-table-grid.js +40 -6
- package/components/ui-editor-mention-menu/ui-editor-mention-menu.html +22 -9
- package/components/ui-editor-mention-menu/ui-editor-mention-menu.js +33 -11
- package/components/ui-editor-mention-menu-item/ui-editor-mention-menu-item.html +67 -0
- package/components/ui-editor-slash-menu/ui-editor-slash-menu.html +33 -30
- package/components/ui-editor-slash-menu/ui-editor-slash-menu.js +42 -8
- package/components/ui-editor-slash-menu-group/ui-editor-slash-menu-group.html +31 -0
- package/components/ui-editor-slash-menu-item/ui-editor-slash-menu-item.html +75 -0
- package/components/ui-editor-table-context-menu/ui-editor-table-context-menu.html +16 -5
- package/components/ui-editor-table-context-menu/ui-editor-table-context-menu.js +10 -2
- package/components/ui-editor-table-overlay/ui-editor-table-overlay.html +5 -5
- package/components/ui-editor-table-overlay/ui-editor-table-overlay.js +5 -2
- package/components/ui-editor-table-toolbar/ui-editor-table-toolbar.html +14 -4
- package/components/ui-editor-table-toolbar/ui-editor-table-toolbar.js +2 -2
- package/components/ui-editor-toolbar/ui-editor-toolbar.html +1 -1
- package/components/ui-editor-toolbar/ui-editor-toolbar.js +46 -0
- package/components/ui-floating-action-button/ui-floating-action-button.html +1 -1
- package/components/ui-form-field/ui-form-field.html +16 -3
- package/components/ui-form-field/ui-form-field.js +1 -1
- package/components/ui-grid/ui-grid.html +17 -2
- package/components/ui-icon/ui-icon.html +5 -0
- package/components/ui-icon-button/ui-icon-button.html +25 -6
- package/components/ui-icon-button/ui-icon-button.js +11 -1
- package/components/ui-input/ui-input.html +3 -3
- package/components/ui-input-group/ui-input-group.html +18 -4
- package/components/ui-listbox/ui-listbox.html +81 -0
- package/components/ui-listbox/ui-listbox.js +27 -0
- package/components/ui-menu/ui-menu.html +33 -5
- package/components/ui-menu/ui-menu.js +18 -12
- package/components/ui-menu-group/ui-menu-group.html +27 -0
- package/components/ui-menu-item/ui-menu-item.html +80 -8
- package/components/ui-menu-item/ui-menu-item.js +47 -0
- package/components/ui-popover/ui-popover.html +33 -5
- package/components/ui-popover/ui-popover.js +9 -1
- package/components/ui-radio-group/ui-radio-group.html +13 -1
- package/components/ui-search-shell/ui-search-shell.html +6 -6
- package/components/ui-search-shell/ui-search-shell.js +21 -5
- package/components/ui-select/ui-select.html +14 -4
- package/components/ui-switch/ui-switch.html +2 -2
- package/components/ui-switcher/ui-switcher.html +8 -1
- package/components/ui-switcher/ui-switcher.js +15 -0
- package/components/ui-tabs/ui-tabs.html +17 -12
- package/components/ui-tabs/ui-tabs.js +43 -7
- package/components/ui-textarea/ui-textarea.html +28 -5
- package/components/ui-toast/ui-toast.html +88 -0
- package/components/ui-toast/ui-toast.js +68 -0
- package/components/ui-toast-region/ui-toast-region.html +102 -19
- package/components/ui-toast-region/ui-toast-region.js +10 -18
- package/components/ui-tooltip/ui-tooltip.html +51 -7
- package/components/ui-tooltip/ui-tooltip.js +29 -3
- package/components/ui-tree/ui-tree.html +12 -0
- package/components/ui-tree/ui-tree.js +191 -12
- package/components/ui-tree-item/ui-tree-item.html +87 -8
- package/components/ui-tree-item/ui-tree-item.js +3 -1
- package/dist/index.js +38 -32
- package/editor/chunks/extensions.js +82 -2
- package/editor/extensions/table-commands.d.ts +8 -0
- package/editor/icons.d.ts +5 -0
- package/editor/index.js +7 -2
- package/editor/insert-table-grid.d.ts +1 -1
- package/editor/mention-menu.d.ts +2 -0
- package/editor/slash-menu.d.ts +3 -0
- package/editor/table-context-menu.d.ts +1 -1
- package/editor/table-overlay.d.ts +2 -2
- package/package.json +1 -1
- package/styles/ui-avatar-group.css +1 -2
- package/styles/ui-avatar.css +11 -1
- package/styles/ui-badge.css +73 -1
- package/styles/ui-button.css +9 -1
- package/styles/ui-callout.css +1 -1
- package/styles/ui-checkbox.css +9 -0
- package/styles/ui-combobox.css +38 -7
- package/styles/ui-context-menu.css +32 -4
- package/styles/ui-dialog.css +42 -5
- package/styles/ui-editor-insert-table-grid.css +48 -11
- package/styles/ui-editor-mention-menu-item.css +57 -0
- package/styles/ui-editor-mention-menu.css +11 -1
- package/styles/ui-editor-slash-menu-group.css +21 -0
- package/styles/ui-editor-slash-menu-item.css +62 -0
- package/styles/ui-editor-slash-menu.css +10 -21
- package/styles/ui-editor-table-context-menu.css +1 -1
- package/styles/ui-floating-action-button.css +1 -1
- package/styles/ui-form-field.css +14 -1
- package/styles/ui-grid.css +14 -1
- package/styles/ui-icon-button.css +17 -5
- package/styles/ui-input-group.css +18 -4
- package/styles/ui-input.css +3 -3
- package/styles/ui-listbox.css +69 -0
- package/styles/ui-menu-group.css +19 -0
- package/styles/ui-menu-item.css +48 -9
- package/styles/ui-menu.css +32 -4
- package/styles/ui-popover.css +28 -4
- package/styles/ui-radio-group.css +9 -0
- package/styles/ui-search-shell.css +5 -5
- package/styles/ui-select.css +14 -4
- package/styles/ui-switch.css +2 -2
- package/styles/ui-switcher.css +4 -0
- package/styles/ui-textarea.css +27 -4
- package/styles/ui-toast-region.css +88 -4
- package/styles/ui-toast.css +74 -0
- package/styles/ui-tooltip.css +46 -6
- package/styles/ui-tree-item.css +72 -4
- package/styles/ui-tree.css +8 -0
- package/tokens.css +3 -0
- package/vanilla/UiAvatar.d.ts +2 -0
- package/vanilla/UiAvatar.js +8 -1
- package/vanilla/UiAvatarGroup.d.ts +2 -0
- package/vanilla/UiAvatarGroup.js +1 -1
- package/vanilla/UiBadge.d.ts +1 -1
- package/vanilla/UiBadge.js +1 -1
- package/vanilla/UiButton.d.ts +1 -0
- package/vanilla/UiButton.js +22 -6
- package/vanilla/UiCallout.d.ts +1 -1
- package/vanilla/UiCallout.js +1 -1
- package/vanilla/UiCheckbox.d.ts +1 -0
- package/vanilla/UiCheckbox.js +12 -9
- package/vanilla/UiCombobox.d.ts +4 -0
- package/vanilla/UiCombobox.js +30 -15
- package/vanilla/UiContextMenu.d.ts +1 -1
- package/vanilla/UiContextMenu.js +1 -1
- package/vanilla/UiDialog.d.ts +5 -2
- package/vanilla/UiDialog.js +1 -1
- package/vanilla/UiDisclosure.d.ts +2 -0
- package/vanilla/UiDisclosure.js +32 -18
- package/vanilla/UiEditable.d.ts +5 -0
- package/vanilla/UiEditable.js +7 -3
- package/vanilla/UiEditorInsertTableGrid.js +1 -6
- package/vanilla/UiEditorMentionMenu.d.ts +4 -2
- package/vanilla/UiEditorMentionMenu.js +49 -31
- package/vanilla/UiEditorMentionMenuItem.d.ts +16 -0
- package/vanilla/UiEditorMentionMenuItem.js +56 -0
- package/vanilla/UiEditorSlashMenu.d.ts +4 -3
- package/vanilla/UiEditorSlashMenu.js +59 -46
- package/vanilla/UiEditorSlashMenuGroup.d.ts +14 -0
- package/vanilla/UiEditorSlashMenuGroup.js +48 -0
- package/vanilla/UiEditorSlashMenuItem.d.ts +18 -0
- package/vanilla/UiEditorSlashMenuItem.js +57 -0
- package/vanilla/UiEditorTableContextMenu.d.ts +5 -2
- package/vanilla/UiEditorTableContextMenu.js +1 -2
- package/vanilla/UiEditorTableOverlay.d.ts +1 -1
- package/vanilla/UiEditorTableOverlay.js +5 -5
- package/vanilla/UiEditorTableToolbar.d.ts +4 -2
- package/vanilla/UiEditorTableToolbar.js +1 -2
- package/vanilla/UiEditorToolbar.js +2 -0
- package/vanilla/UiGrid.d.ts +1 -0
- package/vanilla/UiGrid.js +8 -7
- package/vanilla/UiIcon.js +1 -1
- package/vanilla/UiIconButton.d.ts +1 -0
- package/vanilla/UiIconButton.js +11 -2
- package/vanilla/UiListbox.d.ts +20 -0
- package/vanilla/UiListbox.js +45 -0
- package/vanilla/UiMenu.d.ts +1 -1
- package/vanilla/UiMenu.js +1 -1
- package/vanilla/UiMenuGroup.d.ts +14 -0
- package/vanilla/UiMenuGroup.js +35 -0
- package/vanilla/UiMenuItem.d.ts +5 -1
- package/vanilla/UiMenuItem.js +91 -30
- package/vanilla/UiPopover.d.ts +1 -1
- package/vanilla/UiPopover.js +1 -1
- package/vanilla/UiRadioGroup.d.ts +1 -0
- package/vanilla/UiRadioGroup.js +6 -3
- package/vanilla/UiSwitcher.d.ts +1 -0
- package/vanilla/UiSwitcher.js +7 -9
- package/vanilla/UiTabs.d.ts +1 -0
- package/vanilla/UiTabs.js +9 -1
- package/vanilla/UiTextarea.d.ts +1 -0
- package/vanilla/UiTextarea.js +4 -1
- package/vanilla/UiToast.d.ts +16 -0
- package/vanilla/UiToast.js +53 -0
- package/vanilla/UiToastRegion.d.ts +1 -4
- package/vanilla/UiToastRegion.js +3 -11
- package/vanilla/UiTooltip.d.ts +1 -1
- package/vanilla/UiTooltip.js +1 -1
- package/vanilla/UiTree.d.ts +2 -0
- package/vanilla/UiTree.js +1 -1
- package/vanilla/UiTreeItem.d.ts +1 -0
- package/vanilla/UiTreeItem.js +45 -31
- package/vanilla/index.js +6 -0
- package/vue/UiAvatar.d.ts +4 -0
- package/vue/UiAvatar.vue +23 -3
- package/vue/UiAvatarGroup.d.ts +3 -0
- package/vue/UiAvatarGroup.vue +7 -4
- package/vue/UiBadge.d.ts +2 -2
- package/vue/UiBadge.vue +74 -2
- package/vue/UiButton.d.ts +5 -3
- package/vue/UiButton.vue +41 -7
- package/vue/UiCallout.d.ts +2 -2
- package/vue/UiCallout.vue +3 -2
- package/vue/UiCheckbox.d.ts +2 -0
- package/vue/UiCheckbox.vue +16 -0
- package/vue/UiCombobox.d.ts +13 -3
- package/vue/UiCombobox.vue +62 -11
- package/vue/UiContextMenu.d.ts +2 -0
- package/vue/UiContextMenu.vue +47 -6
- package/vue/UiDialog.d.ts +18 -4
- package/vue/UiDialog.vue +86 -11
- package/vue/UiDisclosure.d.ts +8 -2
- package/vue/UiDisclosure.vue +38 -14
- package/vue/UiEditable.d.ts +10 -0
- package/vue/UiEditable.vue +14 -2
- package/vue/UiEditorInsertTableGrid.vue +52 -15
- package/vue/UiEditorMentionMenu.d.ts +24 -2
- package/vue/UiEditorMentionMenu.vue +27 -11
- package/vue/UiEditorMentionMenuItem.d.ts +24 -0
- package/vue/UiEditorMentionMenuItem.js +2 -0
- package/vue/UiEditorMentionMenuItem.vue +88 -0
- package/vue/UiEditorSlashMenu.d.ts +24 -1
- package/vue/UiEditorSlashMenu.vue +33 -33
- package/vue/UiEditorSlashMenuGroup.d.ts +18 -0
- package/vue/UiEditorSlashMenuGroup.js +2 -0
- package/vue/UiEditorSlashMenuGroup.vue +43 -0
- package/vue/UiEditorSlashMenuItem.d.ts +26 -0
- package/vue/UiEditorSlashMenuItem.js +2 -0
- package/vue/UiEditorSlashMenuItem.vue +101 -0
- package/vue/UiEditorTableContextMenu.d.ts +9 -3
- package/vue/UiEditorTableContextMenu.vue +53 -3
- package/vue/UiEditorTableOverlay.d.ts +2 -2
- package/vue/UiEditorTableOverlay.vue +8 -6
- package/vue/UiEditorTableToolbar.d.ts +7 -3
- package/vue/UiEditorTableToolbar.vue +50 -2
- package/vue/UiEditorToolbar.vue +14 -1
- package/vue/UiFloatingActionButton.vue +1 -1
- package/vue/UiFormField.vue +28 -3
- package/vue/UiGrid.d.ts +1 -0
- package/vue/UiGrid.vue +26 -2
- package/vue/UiIcon.vue +16 -0
- package/vue/UiIconButton.d.ts +5 -3
- package/vue/UiIconButton.vue +40 -6
- package/vue/UiInput.vue +10 -3
- package/vue/UiInputGroup.vue +32 -11
- package/vue/UiListbox.d.ts +33 -0
- package/vue/UiListbox.js +2 -0
- package/vue/UiListbox.vue +131 -0
- package/vue/UiMenu.d.ts +2 -0
- package/vue/UiMenu.vue +43 -6
- package/vue/UiMenuGroup.d.ts +18 -0
- package/vue/UiMenuGroup.js +2 -0
- package/vue/UiMenuGroup.vue +39 -0
- package/vue/UiMenuItem.d.ts +34 -2
- package/vue/UiMenuItem.vue +145 -9
- package/vue/UiPopover.d.ts +2 -2
- package/vue/UiPopover.vue +44 -5
- package/vue/UiRadioGroup.d.ts +2 -0
- package/vue/UiRadioGroup.vue +17 -0
- package/vue/UiSearchShell.vue +12 -5
- package/vue/UiSelect.vue +18 -4
- package/vue/UiSwitch.vue +2 -2
- package/vue/UiSwitcher.d.ts +1 -0
- package/vue/UiSwitcher.vue +22 -1
- package/vue/UiTabs.d.ts +6 -2
- package/vue/UiTabs.vue +14 -12
- package/vue/UiTextarea.d.ts +2 -0
- package/vue/UiTextarea.vue +32 -4
- package/vue/UiToast.d.ts +34 -0
- package/vue/UiToast.js +2 -0
- package/vue/UiToast.vue +173 -0
- package/vue/UiToastRegion.d.ts +2 -18
- package/vue/UiToastRegion.vue +123 -48
- package/vue/UiTooltip.d.ts +2 -2
- package/vue/UiTooltip.vue +67 -12
- package/vue/UiTree.d.ts +10 -0
- package/vue/UiTree.vue +27 -0
- package/vue/UiTreeItem.d.ts +2 -0
- package/vue/UiTreeItem.vue +96 -4
- package/vue/chunks/UiAvatar.js +19 -5
- package/vue/chunks/UiAvatarGroup.js +10 -4
- package/vue/chunks/UiBadge.js +1 -1
- package/vue/chunks/UiButton.js +58 -11
- package/vue/chunks/UiCallout.js +2 -2
- package/vue/chunks/UiCheckbox.js +12 -2
- package/vue/chunks/UiCombobox.js +30 -13
- package/vue/chunks/UiContextMenu.js +6 -2
- package/vue/chunks/UiDialog.js +25 -8
- package/vue/chunks/UiDisclosure.js +36 -12
- package/vue/chunks/UiEditable.js +22 -8
- package/vue/chunks/UiEditorInsertTableGrid.js +11 -14
- package/vue/chunks/UiEditorMentionMenu.js +30 -19
- package/vue/chunks/UiEditorMentionMenuItem.js +36 -0
- package/vue/chunks/UiEditorSlashMenu.js +33 -35
- package/vue/chunks/UiEditorSlashMenuGroup.js +26 -0
- package/vue/chunks/UiEditorSlashMenuItem.js +51 -0
- package/vue/chunks/UiEditorTableContextMenu.js +39 -5
- package/vue/chunks/UiEditorTableOverlay.js +6 -6
- package/vue/chunks/UiEditorTableToolbar.js +37 -4
- package/vue/chunks/UiEditorToolbar.js +18 -4
- package/vue/chunks/UiFloatingActionButton.js +1 -1
- package/vue/chunks/UiFormField.js +13 -9
- package/vue/chunks/UiGrid.js +6 -2
- package/vue/chunks/UiIcon.js +40 -2
- package/vue/chunks/UiIconButton.js +35 -4
- package/vue/chunks/UiInput.js +1 -1
- package/vue/chunks/UiInputGroup.js +1 -1
- package/vue/chunks/UiListbox.js +70 -0
- package/vue/chunks/UiMenu.js +6 -2
- package/vue/chunks/UiMenuGroup.js +25 -0
- package/vue/chunks/UiMenuItem.js +128 -7
- package/vue/chunks/UiPopover.js +1 -1
- package/vue/chunks/UiRadioGroup.js +8 -2
- package/vue/chunks/UiSearchShell.js +1 -1
- package/vue/chunks/UiSelect.js +1 -1
- package/vue/chunks/UiSwitch.js +1 -1
- package/vue/chunks/UiSwitcher.js +20 -3
- package/vue/chunks/UiTabs.js +4 -3
- package/vue/chunks/UiTextarea.js +7 -2
- package/vue/chunks/UiToast.js +81 -0
- package/vue/chunks/UiToastRegion.js +25 -49
- package/vue/chunks/UiTooltip.js +3 -7
- package/vue/chunks/UiTree.js +8 -4
- package/vue/chunks/UiTreeItem.js +28 -10
- package/vue/components.css +1840 -851
- package/vue/editor/index.js +263 -2
- package/vue/index.d.ts +6 -0
- package/vue/index.js +9 -3
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<link rel="component" href="../ui-icon-button/ui-icon-button.html">
|
|
2
|
+
<link rel="component" href="../ui-icon/ui-icon.html">
|
|
3
|
+
<template component="ui-toast" controller="./ui-toast.js">
|
|
4
|
+
<defs>
|
|
5
|
+
<prop name="tone" type="neutral | info | success | warning | danger" default="neutral"
|
|
6
|
+
>Semantic accent for this message. Danger uses an assertive alert role; the other tones use polite status semantics.</prop>
|
|
7
|
+
<prop name="duration" type="number" default="0"
|
|
8
|
+
>Milliseconds before requesting dismissal; 0 keeps the toast until an action dismisses it. The timer pauses on hover or focus.</prop>
|
|
9
|
+
<event name="dismiss" type="object({ id: string, reason: action | timeout, trigger: keyboard | pointer | programmatic })"></event>
|
|
10
|
+
</defs>
|
|
11
|
+
<div :role="{ neutral: 'status', info: 'status', success: 'status', warning: 'status', danger: 'alert' }[tone]">
|
|
12
|
+
<div class="message"><slot></slot></div>
|
|
13
|
+
<div class="action" $ref="action"><slot name="action"></slot></div>
|
|
14
|
+
<ui-icon-button class="dismiss" size="sm" variant="ghost" :round="true" label="Dismiss notification">
|
|
15
|
+
<ui-icon name="x"></ui-icon>
|
|
16
|
+
</ui-icon-button>
|
|
17
|
+
</div>
|
|
18
|
+
<style>
|
|
19
|
+
/* Component hooks stay on this toast; group overlay values may inherit from the region. */
|
|
20
|
+
@property --ui-toast-surface {
|
|
21
|
+
syntax: "*";
|
|
22
|
+
inherits: false;
|
|
23
|
+
}
|
|
24
|
+
@property --ui-toast-border {
|
|
25
|
+
syntax: "*";
|
|
26
|
+
inherits: false;
|
|
27
|
+
}
|
|
28
|
+
@property --ui-toast-radius {
|
|
29
|
+
syntax: "*";
|
|
30
|
+
inherits: false;
|
|
31
|
+
}
|
|
32
|
+
@property --ui-toast-shadow {
|
|
33
|
+
syntax: "*";
|
|
34
|
+
inherits: false;
|
|
35
|
+
}
|
|
36
|
+
@property --ui-toast-accent {
|
|
37
|
+
syntax: "*";
|
|
38
|
+
inherits: false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
:host {
|
|
42
|
+
--_ui-toast-accent: var(--ui-border-strong);
|
|
43
|
+
|
|
44
|
+
display: flex;
|
|
45
|
+
align-items: center;
|
|
46
|
+
gap: var(--ui-space-2);
|
|
47
|
+
box-sizing: border-box;
|
|
48
|
+
inline-size: 100%;
|
|
49
|
+
margin: 0;
|
|
50
|
+
padding: var(--ui-space-3) var(--ui-space-4);
|
|
51
|
+
border: 1px solid var(--ui-toast-border, var(--ui-overlay-border, var(--ui-border-default)));
|
|
52
|
+
border-inline-start: 3px solid var(--ui-toast-accent, var(--_ui-toast-accent));
|
|
53
|
+
border-radius: var(--ui-toast-radius, var(--ui-overlay-radius, var(--ui-radius-lg)));
|
|
54
|
+
background: var(--ui-toast-surface, var(--ui-overlay-surface, var(--ui-surface-elevated)));
|
|
55
|
+
color: var(--ui-text-primary);
|
|
56
|
+
box-shadow: var(--ui-toast-shadow, var(--ui-overlay-shadow, var(--ui-elevation-overlay, var(--ui-shadow-lg))));
|
|
57
|
+
pointer-events: auto;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
:host-state([tone="info"]) {
|
|
61
|
+
--_ui-toast-accent: var(--ui-info-solid);
|
|
62
|
+
}
|
|
63
|
+
:host-state([tone="success"]) {
|
|
64
|
+
--_ui-toast-accent: var(--ui-success-solid);
|
|
65
|
+
}
|
|
66
|
+
:host-state([tone="warning"]) {
|
|
67
|
+
--_ui-toast-accent: var(--ui-warning-solid);
|
|
68
|
+
}
|
|
69
|
+
:host-state([tone="danger"]) {
|
|
70
|
+
--_ui-toast-accent: var(--ui-danger-solid);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.message {
|
|
74
|
+
min-inline-size: 0;
|
|
75
|
+
flex: 1;
|
|
76
|
+
}
|
|
77
|
+
.action:not(:has(> *)) {
|
|
78
|
+
display: none;
|
|
79
|
+
}
|
|
80
|
+
.dismiss {
|
|
81
|
+
margin-inline-end: calc(var(--ui-space-2) * -1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
:host[hidden] {
|
|
85
|
+
display: none;
|
|
86
|
+
}
|
|
87
|
+
</style>
|
|
88
|
+
</template>
|
|
@@ -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="
|
|
5
|
-
|
|
6
|
-
<prop name="
|
|
7
|
-
>
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
<
|
|
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
|
|
12
|
-
*
|
|
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 =
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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"
|
|
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
|
|
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
|
-
:
|
|
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
|
-
:
|
|
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
|
|
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;
|