@threadlabs/looma 0.14.8 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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-cluster/ui-cluster.html +7 -0
- package/components/ui-combobox/ui-combobox.html +46 -10
- package/components/ui-combobox/ui-combobox.js +5 -5
- 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-cluster.css +3 -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/UiCluster.d.ts +1 -0
- package/vanilla/UiCluster.js +2 -0
- package/vanilla/UiCombobox.d.ts +3 -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/UiCluster.d.ts +1 -0
- package/vue/UiCluster.vue +5 -0
- package/vue/UiCombobox.d.ts +11 -3
- package/vue/UiCombobox.vue +60 -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/UiCluster.js +5 -3
- package/vue/chunks/UiCombobox.js +29 -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 +1856 -864
- package/vue/editor/index.js +263 -2
- package/vue/index.d.ts +6 -0
- package/vue/index.js +9 -3
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
<prop name="checked" type="boolean" default="false"
|
|
4
4
|
>Whether the checkbox is checked initially or when set externally. A form reset returns it to this.</prop>
|
|
5
5
|
<prop name="disabled" type="boolean" default="false">Disables the native checkbox, so it cannot be toggled or focused.</prop>
|
|
6
|
+
<prop name="invalid" type="boolean" default="false">Exposes an externally validated error and styles the checkbox with the field danger colour.</prop>
|
|
6
7
|
<prop name="indeterminate" type="boolean" default="false"
|
|
7
8
|
>Shows the mixed state, for a checkbox that summarises a group whose items are partly checked. It sets the native indeterminate property, which is
|
|
8
9
|
independent of checked, so update it as the group changes.</prop>
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
.checked="internalChecked"
|
|
29
30
|
.indeterminate="indeterminate"
|
|
30
31
|
:disabled="disabled"
|
|
32
|
+
:aria-invalid="invalid"
|
|
31
33
|
:required="required"
|
|
32
34
|
:name="name"
|
|
33
35
|
:value="value"
|
|
@@ -43,6 +45,10 @@
|
|
|
43
45
|
syntax: "*";
|
|
44
46
|
inherits: false;
|
|
45
47
|
}
|
|
48
|
+
@property --ui-checkbox-invalid-border {
|
|
49
|
+
syntax: "*";
|
|
50
|
+
inherits: false;
|
|
51
|
+
}
|
|
46
52
|
@property --ui-checkbox-size {
|
|
47
53
|
syntax: "*";
|
|
48
54
|
inherits: false;
|
|
@@ -51,6 +57,7 @@
|
|
|
51
57
|
:host {
|
|
52
58
|
/* Hooks do not inherit, so the parts below read them from the root. */
|
|
53
59
|
--_ui-checkbox-size: var(--ui-checkbox-size);
|
|
60
|
+
--_ui-checkbox-invalid-border: var(--ui-checkbox-invalid-border, var(--ui-field-danger, var(--ui-danger-solid)));
|
|
54
61
|
|
|
55
62
|
display: inline-flex;
|
|
56
63
|
align-items: flex-start;
|
|
@@ -148,6 +155,10 @@
|
|
|
148
155
|
outline-offset: 2px;
|
|
149
156
|
}
|
|
150
157
|
|
|
158
|
+
:host-state([invalid]) input {
|
|
159
|
+
border-color: var(--_ui-checkbox-invalid-border);
|
|
160
|
+
}
|
|
161
|
+
|
|
151
162
|
:host-state([disabled]) {
|
|
152
163
|
color: var(--ui-disabled-text);
|
|
153
164
|
cursor: not-allowed;
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
<prop name="justify" type="start | center | end | between"
|
|
10
10
|
>Where items sit along each row: start (the default), center, end, or between, which pushes the first and last items to the ends of the row and spreads the
|
|
11
11
|
spare space between them, as for a title with its actions.</prop>
|
|
12
|
+
<prop name="wrap" type="wrap | nowrap"
|
|
13
|
+
>Whether items move onto a new row when they run out of space: wrap (the default) or nowrap, which keeps them on one row at their own sizes. Use nowrap for
|
|
14
|
+
a small group that must read as one unit, such as a meter and its value in a table cell, so the cell grows to fit it; a whole row of actions should keep
|
|
15
|
+
wrapping so it fits narrow screens.</prop>
|
|
12
16
|
</defs>
|
|
13
17
|
<div><slot></slot></div>
|
|
14
18
|
<style>
|
|
@@ -62,6 +66,9 @@
|
|
|
62
66
|
:host-state([justify="between"]) {
|
|
63
67
|
justify-content: space-between;
|
|
64
68
|
}
|
|
69
|
+
:host-state([wrap="nowrap"]) {
|
|
70
|
+
flex-wrap: nowrap;
|
|
71
|
+
}
|
|
65
72
|
|
|
66
73
|
/* A component that sets its display still honours the hidden attribute on its root. */
|
|
67
74
|
:host[hidden] {
|
|
@@ -50,6 +50,11 @@
|
|
|
50
50
|
<prop name="required" type="boolean" default="false"
|
|
51
51
|
>Requires a value: adds an asterisk to the label, marks the input required for native form validation, and makes validation report "A value is required."
|
|
52
52
|
when the field is left empty. In multiple mode a chosen item satisfies it, whatever is left in the input.</prop>
|
|
53
|
+
<prop name="invalid" type="boolean" default="false"
|
|
54
|
+
>Exposes an externally validated error on the input and field. It remains until the consumer clears it, independently of native validation.</prop>
|
|
55
|
+
<prop name="openOnFocus" type="boolean" default="false">Opens suggestions when the input receives focus, before typing.</prop>
|
|
56
|
+
<prop name="loading" type="boolean" default="false"
|
|
57
|
+
>Marks the suggestion list busy while the consumer loads options. It announces loading and suppresses the empty state and create offer until cleared.</prop>
|
|
53
58
|
<prop name="size" type="sm | md | lg" default="md"
|
|
54
59
|
>Field height, padding, and type size, matching Button's sizes so a field and a button of one size share a row. sm is the small control height (2rem by
|
|
55
60
|
default) with smaller text, for compact rows such as a table row or a filter bar; md is the standard control height; lg is taller with roomier padding.
|
|
@@ -81,7 +86,6 @@
|
|
|
81
86
|
:value="[]"
|
|
82
87
|
></state>
|
|
83
88
|
<state name="active" :value="-1"></state>
|
|
84
|
-
<state name="loading" :value="false"></state>
|
|
85
89
|
<state name="helpOpen" :value="false"></state>
|
|
86
90
|
<state name="lookupError" :value="''"></state>
|
|
87
91
|
<state
|
|
@@ -163,8 +167,8 @@
|
|
|
163
167
|
autocomplete="off"
|
|
164
168
|
:aria-controls="format('%s-listbox', uid)"
|
|
165
169
|
:aria-expanded="expanded"
|
|
166
|
-
:aria-invalid="validationStatus = 'error'"
|
|
167
|
-
:aria-busy="validationStatus = 'pending'"
|
|
170
|
+
:aria-invalid="invalid or validationStatus = 'error'"
|
|
171
|
+
:aria-busy="loading or validationStatus = 'pending'"
|
|
168
172
|
.value="display"
|
|
169
173
|
:placeholder="placeholder"
|
|
170
174
|
:disabled="disabled"
|
|
@@ -262,6 +266,7 @@
|
|
|
262
266
|
>
|
|
263
267
|
</div>
|
|
264
268
|
<div $if="message" class="message" $value="message"></div>
|
|
269
|
+
<div $if="not loading and not rows[0] and not creatable and not lookupError" class="message"><slot name="empty">No suggestions.</slot></div>
|
|
265
270
|
</div>
|
|
266
271
|
<slot name="footer"></slot>
|
|
267
272
|
</div>
|
|
@@ -313,6 +318,30 @@
|
|
|
313
318
|
syntax: "*";
|
|
314
319
|
inherits: false;
|
|
315
320
|
}
|
|
321
|
+
@property --ui-combobox-radius {
|
|
322
|
+
syntax: "*";
|
|
323
|
+
inherits: false;
|
|
324
|
+
}
|
|
325
|
+
@property --ui-combobox-invalid-border {
|
|
326
|
+
syntax: "*";
|
|
327
|
+
inherits: false;
|
|
328
|
+
}
|
|
329
|
+
@property --ui-combobox-popup-surface {
|
|
330
|
+
syntax: "*";
|
|
331
|
+
inherits: false;
|
|
332
|
+
}
|
|
333
|
+
@property --ui-combobox-popup-border {
|
|
334
|
+
syntax: "*";
|
|
335
|
+
inherits: false;
|
|
336
|
+
}
|
|
337
|
+
@property --ui-combobox-popup-radius {
|
|
338
|
+
syntax: "*";
|
|
339
|
+
inherits: false;
|
|
340
|
+
}
|
|
341
|
+
@property --ui-combobox-popup-shadow {
|
|
342
|
+
syntax: "*";
|
|
343
|
+
inherits: false;
|
|
344
|
+
}
|
|
316
345
|
|
|
317
346
|
:host {
|
|
318
347
|
/* Hooks do not inherit, so the parts below read them from the root. */
|
|
@@ -326,6 +355,12 @@
|
|
|
326
355
|
--_ui-combobox-item-min-block-size: var(--ui-combobox-item-min-block-size);
|
|
327
356
|
--_ui-combobox-item-input-min-inline-size: var(--ui-combobox-item-input-min-inline-size);
|
|
328
357
|
--_ui-combobox-item-item-max-inline-size: var(--ui-combobox-item-item-max-inline-size);
|
|
358
|
+
--_ui-combobox-radius: var(--ui-combobox-radius);
|
|
359
|
+
--_ui-combobox-invalid-border: var(--ui-combobox-invalid-border);
|
|
360
|
+
--_ui-combobox-popup-surface: var(--ui-combobox-popup-surface);
|
|
361
|
+
--_ui-combobox-popup-border: var(--ui-combobox-popup-border);
|
|
362
|
+
--_ui-combobox-popup-radius: var(--ui-combobox-popup-radius);
|
|
363
|
+
--_ui-combobox-popup-shadow: var(--ui-combobox-popup-shadow);
|
|
329
364
|
|
|
330
365
|
display: grid;
|
|
331
366
|
gap: var(--ui-space-1);
|
|
@@ -352,7 +387,7 @@
|
|
|
352
387
|
display: flex;
|
|
353
388
|
min-inline-size: 0;
|
|
354
389
|
border: 1px solid var(--ui-control-border, var(--ui-border-default));
|
|
355
|
-
border-radius: var(--ui-radius-md);
|
|
390
|
+
border-radius: var(--_ui-combobox-radius, var(--ui-field-radius, var(--ui-radius-md)));
|
|
356
391
|
background: var(--ui-control-surface, var(--ui-surface-default));
|
|
357
392
|
box-shadow: var(--ui-control-inset);
|
|
358
393
|
transition: border-color var(--ui-motion-fast) var(--ui-motion-ease), box-shadow var(--ui-motion-fast) var(--ui-motion-ease);
|
|
@@ -470,8 +505,9 @@
|
|
|
470
505
|
.field:has(input:disabled) {
|
|
471
506
|
background: var(--ui-control-surface-disabled, var(--ui-surface-muted));
|
|
472
507
|
}
|
|
473
|
-
:host-state([validationStatus="error"]) .field
|
|
474
|
-
|
|
508
|
+
:host-state([validationStatus="error"]) .field,
|
|
509
|
+
:host-state([invalid]) .field {
|
|
510
|
+
border-color: var(--_ui-combobox-invalid-border, var(--ui-field-danger, var(--ui-danger-solid, var(--ui-accent-active))));
|
|
475
511
|
}
|
|
476
512
|
.popup {
|
|
477
513
|
box-sizing: border-box;
|
|
@@ -481,11 +517,11 @@
|
|
|
481
517
|
overflow: auto;
|
|
482
518
|
margin: 0;
|
|
483
519
|
padding: var(--ui-space-1);
|
|
484
|
-
border: 1px solid var(--ui-border-default);
|
|
485
|
-
border-radius: var(--ui-radius-md);
|
|
486
|
-
background: var(--ui-surface-elevated);
|
|
520
|
+
border: 1px solid var(--_ui-combobox-popup-border, var(--ui-overlay-border, var(--ui-border-default)));
|
|
521
|
+
border-radius: var(--_ui-combobox-popup-radius, var(--ui-overlay-radius, var(--ui-radius-md)));
|
|
522
|
+
background: var(--_ui-combobox-popup-surface, var(--ui-overlay-surface, var(--ui-surface-elevated)));
|
|
487
523
|
color: var(--ui-text-primary);
|
|
488
|
-
box-shadow: var(--ui-elevation-overlay, var(--ui-shadow-lg));
|
|
524
|
+
box-shadow: var(--_ui-combobox-popup-shadow, var(--ui-overlay-shadow, var(--ui-elevation-overlay, var(--ui-shadow-lg))));
|
|
489
525
|
}
|
|
490
526
|
[hidden] {
|
|
491
527
|
display: none;
|
|
@@ -110,7 +110,6 @@ export default function controller(host) {
|
|
|
110
110
|
const cancelLookup = () => {
|
|
111
111
|
lookup?.abort();
|
|
112
112
|
lookup = undefined;
|
|
113
|
-
host.state.loading = false;
|
|
114
113
|
};
|
|
115
114
|
const close = () => {
|
|
116
115
|
host.state.expanded = false;
|
|
@@ -167,7 +166,6 @@ export default function controller(host) {
|
|
|
167
166
|
// the group-name list below. Concatenating a Map iterator produces one
|
|
168
167
|
// iterator row instead of the option objects it contains.
|
|
169
168
|
host.state.rows = Array.from(groups.values()).flat();
|
|
170
|
-
host.state.loading = false;
|
|
171
169
|
// When nothing matches what was typed, the offer to create it is the only choice, so it is
|
|
172
170
|
// the one Enter makes, and it is highlighted as such.
|
|
173
171
|
if (query && !host.state.rows.some((row) => !row.disabled) && canCreate()) host.state.active = host.state.rows.length;
|
|
@@ -385,8 +383,7 @@ export default function controller(host) {
|
|
|
385
383
|
host.state.createIndex = rows.length;
|
|
386
384
|
// What a named combobox submits in single mode: the value, never the label the field shows.
|
|
387
385
|
host.state.submitted = String(host.state.selected ?? (host.state.allowFreeText ? host.state.raw ?? "" : ""));
|
|
388
|
-
host.state.message = host.state.loading ? "Loading suggestions…"
|
|
389
|
-
: host.state.lookupError || (!rows.length && !host.state.creatable ? "No suggestions." : "");
|
|
386
|
+
host.state.message = host.state.loading ? "Loading suggestions…" : host.state.lookupError || "";
|
|
390
387
|
const validation = host.state.validation ?? { status: "pristine", issues: [] };
|
|
391
388
|
host.state.validationStatus = validation.status;
|
|
392
389
|
const status = host.state.loading ? "Loading suggestions…" : host.state.lookupError || (host.state.expanded ? `${rows.length} suggestions available.` : "");
|
|
@@ -455,6 +452,9 @@ export default function controller(host) {
|
|
|
455
452
|
if (!host.state.multiple && config().allowFreeText && host.state.selected === null) commit(null, host.state.raw, null, "free-entry", "keyboard");
|
|
456
453
|
void validateCurrent();
|
|
457
454
|
};
|
|
455
|
+
const onFocusin = (event) => {
|
|
456
|
+
if (event.target === input && host.state.openOnFocus && !host.state.expanded) open();
|
|
457
|
+
};
|
|
458
458
|
const onCompositionstart = (event) => { if (event.target === input) composing = true; };
|
|
459
459
|
const onCompositionend = (event) => { if (event.target === input) { composing = false; onInput(new InputEvent("input")); } };
|
|
460
460
|
const onTooltipOpen = (event) => { if (event.target.closest?.('[role="tooltip"]')) host.state.helpOpen = true; };
|
|
@@ -482,7 +482,7 @@ export default function controller(host) {
|
|
|
482
482
|
|
|
483
483
|
const api = { get input() { return input; }, validate: validateCurrent };
|
|
484
484
|
instances.set(element, api);
|
|
485
|
-
const listeners = { input: onInput, keydown: onKeydown, click: onClick, pointerdown: onPointerdown, focusout: onFocusout, compositionstart: onCompositionstart, compositionend: onCompositionend, open: onTooltipOpen, close: onTooltipClose };
|
|
485
|
+
const listeners = { input: onInput, keydown: onKeydown, click: onClick, pointerdown: onPointerdown, focusin: onFocusin, focusout: onFocusout, compositionstart: onCompositionstart, compositionend: onCompositionend, open: onTooltipOpen, close: onTooltipClose };
|
|
486
486
|
for (const [name, listener] of Object.entries(listeners)) element.addEventListener(name, listener);
|
|
487
487
|
// Authored options can change after mount (renamed, replaced, or arriving late). A selected value
|
|
488
488
|
// then shows its current label, unless the user is editing the text.
|
|
@@ -11,11 +11,27 @@
|
|
|
11
11
|
name="close"
|
|
12
12
|
type="object({ open: boolean, reason: action | programmatic | light-dismiss | escape, trigger: keyboard | pointer | programmatic })"
|
|
13
13
|
></event>
|
|
14
|
-
<event name="select" type="object({ value: string, trigger: keyboard | pointer | programmatic })"></event>
|
|
14
|
+
<event name="select" type="object({ value: string, checked?: boolean, trigger: keyboard | pointer | programmatic })"></event>
|
|
15
15
|
</defs>
|
|
16
16
|
<span><div class="menu" $ref="menu" role="menu" aria-orientation="vertical"><slot></slot></div></span>
|
|
17
17
|
<style>
|
|
18
18
|
/* This component's hooks style the element they are set on, and never a nested one. */
|
|
19
|
+
@property --ui-context-menu-surface {
|
|
20
|
+
syntax: "*";
|
|
21
|
+
inherits: false;
|
|
22
|
+
}
|
|
23
|
+
@property --ui-context-menu-border {
|
|
24
|
+
syntax: "*";
|
|
25
|
+
inherits: false;
|
|
26
|
+
}
|
|
27
|
+
@property --ui-context-menu-radius {
|
|
28
|
+
syntax: "*";
|
|
29
|
+
inherits: false;
|
|
30
|
+
}
|
|
31
|
+
@property --ui-context-menu-shadow {
|
|
32
|
+
syntax: "*";
|
|
33
|
+
inherits: false;
|
|
34
|
+
}
|
|
19
35
|
@property --ui-context-menu-z-index {
|
|
20
36
|
syntax: "*";
|
|
21
37
|
inherits: false;
|
|
@@ -23,6 +39,10 @@
|
|
|
23
39
|
|
|
24
40
|
:host {
|
|
25
41
|
/* Hooks do not inherit, so the parts below read them from the root. */
|
|
42
|
+
--_ui-context-menu-surface: var(--ui-context-menu-surface);
|
|
43
|
+
--_ui-context-menu-border: var(--ui-context-menu-border);
|
|
44
|
+
--_ui-context-menu-radius: var(--ui-context-menu-radius);
|
|
45
|
+
--_ui-context-menu-shadow: var(--ui-context-menu-shadow);
|
|
26
46
|
--_ui-context-menu-z-index: var(--ui-context-menu-z-index);
|
|
27
47
|
|
|
28
48
|
display: contents;
|
|
@@ -36,10 +56,10 @@
|
|
|
36
56
|
flex-direction: column;
|
|
37
57
|
box-sizing: border-box;
|
|
38
58
|
padding: var(--ui-space-1);
|
|
39
|
-
background: var(--ui-surface-elevated);
|
|
40
|
-
border: 1px solid var(--ui-border-default);
|
|
41
|
-
border-radius: var(--ui-radius-lg);
|
|
42
|
-
box-shadow: var(--ui-elevation-overlay, var(--ui-shadow-lg));
|
|
59
|
+
background: var(--_ui-context-menu-surface, var(--ui-overlay-surface, var(--ui-surface-elevated)));
|
|
60
|
+
border: 1px solid var(--_ui-context-menu-border, var(--ui-overlay-border, var(--ui-border-default)));
|
|
61
|
+
border-radius: var(--_ui-context-menu-radius, var(--ui-overlay-radius, var(--ui-radius-lg)));
|
|
62
|
+
box-shadow: var(--_ui-context-menu-shadow, var(--ui-overlay-shadow, var(--ui-elevation-overlay, var(--ui-shadow-lg))));
|
|
43
63
|
outline: 0;
|
|
44
64
|
overflow: auto;
|
|
45
65
|
z-index: var(--_ui-context-menu-z-index, 1000);
|
|
@@ -53,6 +73,14 @@
|
|
|
53
73
|
overlay var(--ui-motion-fast) allow-discrete;
|
|
54
74
|
}
|
|
55
75
|
|
|
76
|
+
.menu hr {
|
|
77
|
+
align-self: stretch;
|
|
78
|
+
block-size: 0;
|
|
79
|
+
margin: var(--ui-space-1) 0;
|
|
80
|
+
border: 0;
|
|
81
|
+
border-block-start: 1px solid var(--ui-border-default);
|
|
82
|
+
}
|
|
83
|
+
|
|
56
84
|
.menu:popover-open {
|
|
57
85
|
opacity: 1;
|
|
58
86
|
transform: translateY(0) scale(1);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { closeOverlay, createAnchoredSurface, createIdResolver, openOverlay, requestTopOverlayClose } from "../shared/overlay.js";
|
|
2
|
+
import { menuItemFrom, menuItems, navigateMenu } from "../shared/menu-navigation.js";
|
|
2
3
|
|
|
3
4
|
function disabled(item) {
|
|
4
5
|
return item.getAttribute("aria-disabled") === "true" || item.hasAttribute("disabled") || item.getAttribute("disabled") === "true";
|
|
@@ -17,7 +18,7 @@ export default function controller(host) {
|
|
|
17
18
|
let lastOpenProp = Boolean(host.state.open);
|
|
18
19
|
host.state.internalOpen = lastOpenProp;
|
|
19
20
|
|
|
20
|
-
const items = () =>
|
|
21
|
+
const items = () => menuItems(element);
|
|
21
22
|
const focusFirst = () => requestAnimationFrame(() => items()[0]?.focus());
|
|
22
23
|
const targetEvents = { contextmenu: onContextMenu, keydown: onTargetKeydown, pointerdown: onPointerdown, pointerup: endPress, pointercancel: endPress };
|
|
23
24
|
const detach = () => {
|
|
@@ -101,33 +102,41 @@ export default function controller(host) {
|
|
|
101
102
|
clearTimeout(press?.timer);
|
|
102
103
|
press = null;
|
|
103
104
|
}
|
|
105
|
+
const select = (item, trigger) => {
|
|
106
|
+
if (!item || disabled(item)) return;
|
|
107
|
+
const value = item.getAttribute("data-value") ?? item.getAttribute("value") ?? "";
|
|
108
|
+
if (item.getAttribute("role") !== "menuitem") {
|
|
109
|
+
const detail = { trigger, checked: undefined };
|
|
110
|
+
item.dispatchEvent(new CustomEvent("ui-menu-item-activate", { detail }));
|
|
111
|
+
host.dispatch("select", { value, checked: detail.checked, trigger });
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
host.dispatch("select", { value, trigger });
|
|
115
|
+
close("action", trigger);
|
|
116
|
+
};
|
|
104
117
|
const onKeydown = (event) => {
|
|
105
118
|
if (!host.state.internalOpen) return;
|
|
106
119
|
if (event.key === "Escape") {
|
|
107
120
|
event.preventDefault();
|
|
108
121
|
requestTopOverlayClose(document, "escape", "keyboard");
|
|
109
122
|
} else if (["Enter", " "].includes(event.key)) {
|
|
110
|
-
const item = event.target
|
|
123
|
+
const item = menuItemFrom(event.target);
|
|
111
124
|
if (!item || disabled(item)) return;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const next = event.key === "ArrowDown"
|
|
121
|
-
? (index < 0 ? 0 : Math.min(index + 1, enabled.length - 1))
|
|
122
|
-
: (index <= 0 ? enabled.length - 1 : index - 1);
|
|
123
|
-
enabled[next].focus();
|
|
125
|
+
if (item.localName === "a") {
|
|
126
|
+
if (event.key === " ") { event.preventDefault(); item.click(); }
|
|
127
|
+
} else {
|
|
128
|
+
event.preventDefault();
|
|
129
|
+
select(item, "keyboard");
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
navigateMenu(event, element);
|
|
124
133
|
}
|
|
125
134
|
};
|
|
126
135
|
const onClick = (event) => {
|
|
127
|
-
const item = event.target
|
|
136
|
+
const item = menuItemFrom(event.target);
|
|
128
137
|
if (!item || disabled(item)) return;
|
|
129
|
-
|
|
130
|
-
|
|
138
|
+
const trigger = event.detail === 0 ? "keyboard" : "pointer";
|
|
139
|
+
select(item, trigger);
|
|
131
140
|
};
|
|
132
141
|
resolveTargets();
|
|
133
142
|
if (menuSurface) surface = createAnchoredSurface(menuSurface, { anchor: target, placement: "bottom-start" });
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
<template component="ui-dialog" controller="./ui-dialog.js">
|
|
2
2
|
<defs>
|
|
3
3
|
<prop name="open" type="boolean" default="false">Whether the dialog is open initially or when set externally.</prop>
|
|
4
|
-
<prop name="
|
|
5
|
-
<prop name="
|
|
6
|
-
>
|
|
4
|
+
<prop name="modeless" type="boolean" default="false">Open without a backdrop or making the rest of the page inert; dialogs are modal by default.</prop>
|
|
5
|
+
<prop name="closedby" type="any | closerequest | none" default="closerequest"
|
|
6
|
+
>Which user close requests work: any includes outside presses, closerequest includes Escape, and none requires an action.</prop>
|
|
7
|
+
<prop name="alert" type="boolean" default="false">Use alert dialog semantics and require an explicit action instead of the header close button.</prop>
|
|
8
|
+
<prop name="size" type="sm | md | lg | fullscreen" default="md">Choose a compact, standard, wide, or viewport-filling dialog.</prop>
|
|
7
9
|
<prop name="label" type="string">Visible title in the dialog header; also its accessible name.</prop>
|
|
8
10
|
<prop name="for" type="string" default="">ID of the control that opens the dialog.</prop>
|
|
9
11
|
<state name="internalOpen" :value="false"></state>
|
|
10
12
|
<state name="accessibleLabel" :value="'Dialog'"></state>
|
|
13
|
+
<event
|
|
14
|
+
name="open"
|
|
15
|
+
type="object({ open: boolean, reason: action | programmatic, trigger: keyboard | pointer | programmatic })"
|
|
16
|
+
></event>
|
|
11
17
|
<event
|
|
12
18
|
name="close"
|
|
13
19
|
type="object({ open: boolean, reason: action | programmatic | light-dismiss | escape, trigger: keyboard | pointer | programmatic })"
|
|
@@ -27,6 +33,22 @@
|
|
|
27
33
|
syntax: "*";
|
|
28
34
|
inherits: false;
|
|
29
35
|
}
|
|
36
|
+
@property --ui-dialog-surface {
|
|
37
|
+
syntax: "*";
|
|
38
|
+
inherits: false;
|
|
39
|
+
}
|
|
40
|
+
@property --ui-dialog-border {
|
|
41
|
+
syntax: "*";
|
|
42
|
+
inherits: false;
|
|
43
|
+
}
|
|
44
|
+
@property --ui-dialog-radius {
|
|
45
|
+
syntax: "*";
|
|
46
|
+
inherits: false;
|
|
47
|
+
}
|
|
48
|
+
@property --ui-dialog-shadow {
|
|
49
|
+
syntax: "*";
|
|
50
|
+
inherits: false;
|
|
51
|
+
}
|
|
30
52
|
@property --ui-dialog-max-width {
|
|
31
53
|
syntax: "*";
|
|
32
54
|
inherits: false;
|
|
@@ -45,7 +67,7 @@
|
|
|
45
67
|
box-sizing: border-box;
|
|
46
68
|
inline-size: min(
|
|
47
69
|
calc(100% - var(--ui-dialog-viewport-gap, var(--ui-space-4)) * 2),
|
|
48
|
-
var(--ui-dialog-max-width, 440px)
|
|
70
|
+
var(--ui-dialog-max-width, var(--_ui-dialog-size, 440px))
|
|
49
71
|
);
|
|
50
72
|
max-inline-size: none;
|
|
51
73
|
max-block-size: calc(100dvh - var(--ui-dialog-viewport-gap, var(--ui-space-4)) * 2);
|
|
@@ -53,10 +75,10 @@
|
|
|
53
75
|
padding: 0;
|
|
54
76
|
overflow: hidden;
|
|
55
77
|
flex-direction: column;
|
|
56
|
-
background: var(--ui-surface-elevated);
|
|
57
|
-
border: 1px solid var(--ui-border-default);
|
|
58
|
-
border-radius: var(--ui-radius-dialog);
|
|
59
|
-
box-shadow: var(--ui-elevation-dialog, var(--ui-shadow-xl));
|
|
78
|
+
background: var(--ui-dialog-surface, var(--ui-overlay-surface, var(--ui-surface-elevated)));
|
|
79
|
+
border: 1px solid var(--ui-dialog-border, var(--ui-overlay-border, var(--ui-border-default)));
|
|
80
|
+
border-radius: var(--ui-dialog-radius, var(--ui-overlay-radius, var(--ui-radius-dialog)));
|
|
81
|
+
box-shadow: var(--ui-dialog-shadow, var(--ui-overlay-shadow, var(--ui-elevation-dialog, var(--ui-shadow-xl))));
|
|
60
82
|
visibility: hidden;
|
|
61
83
|
pointer-events: none;
|
|
62
84
|
opacity: 0;
|
|
@@ -78,6 +100,27 @@
|
|
|
78
100
|
animation: ui-dialog-enter var(--ui-dialog-duration, var(--ui-motion-base)) var(--ui-motion-ease);
|
|
79
101
|
}
|
|
80
102
|
|
|
103
|
+
:host-state([size="sm"]) {
|
|
104
|
+
--_ui-dialog-size: 360px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
:host-state([size="lg"]) {
|
|
108
|
+
--_ui-dialog-size: 640px;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
:host-state([size="fullscreen"]) {
|
|
112
|
+
inset: 0;
|
|
113
|
+
inline-size: 100%;
|
|
114
|
+
block-size: 100dvh;
|
|
115
|
+
max-block-size: none;
|
|
116
|
+
margin: 0;
|
|
117
|
+
border-radius: var(--ui-dialog-radius, 0);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
:host-state([alert]) .close {
|
|
121
|
+
display: none;
|
|
122
|
+
}
|
|
123
|
+
|
|
81
124
|
/* Header and actions stay pinned; only the body scrolls when content is long. */
|
|
82
125
|
header {
|
|
83
126
|
display: flex;
|
|
@@ -13,16 +13,21 @@ export default function controller(host) {
|
|
|
13
13
|
let trigger = null;
|
|
14
14
|
let lastFor = "";
|
|
15
15
|
let lastExternalOpen = Boolean(host.state.open);
|
|
16
|
+
let lastOpen;
|
|
16
17
|
host.state.internalOpen = lastExternalOpen;
|
|
17
18
|
|
|
18
19
|
const requestClose = (reason, input) => {
|
|
19
20
|
host.state.internalOpen = false;
|
|
21
|
+
lastOpen = false;
|
|
20
22
|
host.dispatch("close", { open: false, reason, trigger: input });
|
|
21
23
|
};
|
|
22
24
|
const closeButton = host.refs.close;
|
|
23
25
|
const onCloseClick = (event) => requestClose("action", event.detail === 0 ? "keyboard" : "pointer");
|
|
24
|
-
const onTriggerClick = () => {
|
|
26
|
+
const onTriggerClick = (event) => {
|
|
27
|
+
if (host.state.internalOpen) return;
|
|
25
28
|
host.state.internalOpen = true;
|
|
29
|
+
lastOpen = true;
|
|
30
|
+
host.dispatch("open", { open: true, reason: "action", trigger: event.detail === 0 ? "keyboard" : "pointer" });
|
|
26
31
|
};
|
|
27
32
|
const ids = createIdResolver(document, () => {
|
|
28
33
|
lastFor = null;
|
|
@@ -49,30 +54,46 @@ export default function controller(host) {
|
|
|
49
54
|
}
|
|
50
55
|
host.state.accessibleLabel = inferredLabel(element, host.state.label);
|
|
51
56
|
dialog?.setAttribute("aria-label", String(host.state.accessibleLabel));
|
|
57
|
+
if (host.state.alert) dialog?.setAttribute("role", "alertdialog");
|
|
58
|
+
else dialog?.removeAttribute("role");
|
|
52
59
|
syncTrigger();
|
|
53
60
|
const open = Boolean(host.state.internalOpen);
|
|
61
|
+
const modal = !host.state.modeless;
|
|
62
|
+
const requestedClosedBy = String(host.state.closedby ?? "closerequest");
|
|
63
|
+
const closedBy = host.state.alert && requestedClosedBy === "any" ? "closerequest" : requestedClosedBy;
|
|
54
64
|
trigger?.setAttribute("aria-expanded", String(open));
|
|
55
65
|
if (!dialog) return;
|
|
66
|
+
dialog.setAttribute("closedby", closedBy);
|
|
56
67
|
if (open) {
|
|
68
|
+
if (dialog.open && dialog.matches(":modal") !== modal) dialog.close();
|
|
57
69
|
if (!dialog.open) {
|
|
58
|
-
if (
|
|
70
|
+
if (modal) dialog.showModal();
|
|
59
71
|
else dialog.show();
|
|
60
72
|
}
|
|
61
|
-
openOverlay({ id: overlayId, modal
|
|
73
|
+
openOverlay({ id: overlayId, modal, element, dismissible: true, canClose: (reason) => reason === "light-dismiss" ? closedBy === "any" : reason !== "escape" || closedBy !== "none", requestClose });
|
|
62
74
|
} else {
|
|
63
75
|
if (dialog.open) dialog.close();
|
|
64
76
|
closeOverlay(document, overlayId);
|
|
65
77
|
}
|
|
78
|
+
if (lastOpen !== undefined && lastOpen !== open) {
|
|
79
|
+
host.dispatch(open ? "open" : "close", { open, reason: "programmatic", trigger: "programmatic" });
|
|
80
|
+
}
|
|
81
|
+
lastOpen = open;
|
|
66
82
|
};
|
|
67
|
-
const onClose = () => {
|
|
68
|
-
if (
|
|
83
|
+
const onClose = (event) => {
|
|
84
|
+
if (event instanceof CustomEvent) return;
|
|
85
|
+
// Consumers receive one Looma close event with a reason, not an additional native close event.
|
|
86
|
+
event.stopImmediatePropagation();
|
|
87
|
+
if (dialog.open || !host.state.internalOpen) return;
|
|
69
88
|
host.state.internalOpen = false;
|
|
89
|
+
lastOpen = false;
|
|
70
90
|
closeOverlay(document, overlayId);
|
|
71
91
|
host.dispatch("close", { open: false, reason: "programmatic", trigger: "programmatic" });
|
|
72
92
|
};
|
|
73
93
|
const onKeydown = (event) => {
|
|
74
94
|
if (event.key === "Escape") {
|
|
75
95
|
event.preventDefault();
|
|
96
|
+
event.stopPropagation();
|
|
76
97
|
requestTopOverlayClose(document, "escape", "keyboard");
|
|
77
98
|
}
|
|
78
99
|
};
|
|
@@ -85,7 +106,7 @@ export default function controller(host) {
|
|
|
85
106
|
if (label !== host.state.accessibleLabel) host.state.accessibleLabel = label;
|
|
86
107
|
});
|
|
87
108
|
observer.observe(element, { childList: true, subtree: true, characterData: true });
|
|
88
|
-
dialog?.addEventListener("close", onClose);
|
|
109
|
+
dialog?.addEventListener("close", onClose, true);
|
|
89
110
|
dialog?.addEventListener("cancel", onCancel);
|
|
90
111
|
closeButton?.addEventListener("click", onCloseClick);
|
|
91
112
|
element.addEventListener("keydown", onKeydown);
|
|
@@ -95,7 +116,7 @@ export default function controller(host) {
|
|
|
95
116
|
stop();
|
|
96
117
|
ids.stop();
|
|
97
118
|
observer.disconnect();
|
|
98
|
-
dialog?.removeEventListener("close", onClose);
|
|
119
|
+
dialog?.removeEventListener("close", onClose, true);
|
|
99
120
|
dialog?.removeEventListener("cancel", onCancel);
|
|
100
121
|
closeButton?.removeEventListener("click", onCloseClick);
|
|
101
122
|
trigger?.removeEventListener("click", onTriggerClick);
|
|
@@ -7,8 +7,15 @@
|
|
|
7
7
|
>Whether the disclosure is open initially or when set externally. The trigger toggles it from there, reporting each change as an open or close event.</prop>
|
|
8
8
|
<prop name="disabled" type="boolean" default="false"
|
|
9
9
|
>Disables the trigger and mutes its text, so the disclosure keeps its current state and the user cannot toggle it.</prop>
|
|
10
|
+
<prop name="name" type="string" default=""
|
|
11
|
+
>Disclosures with the same nonempty name form an exclusive group in one document: opening one closes the others. An empty name allows multiple open
|
|
12
|
+
items.</prop>
|
|
13
|
+
<prop name="headingLevel" type="integer" default="0"
|
|
14
|
+
>Adds heading semantics around the trigger at level 2 through 6. Zero leaves it a plain disclosure; choose a level that matches the page outline.</prop>
|
|
10
15
|
<state name="internalOpen" :value="false"></state>
|
|
11
16
|
<state name="contentId" :value="''"></state>
|
|
17
|
+
<state name="headingRole" :value="null"></state>
|
|
18
|
+
<state name="headingAriaLevel" :value="null"></state>
|
|
12
19
|
<event
|
|
13
20
|
name="open"
|
|
14
21
|
type="object({ open: boolean, reason: action | programmatic | light-dismiss | escape, trigger: keyboard | pointer | programmatic })"
|
|
@@ -19,11 +26,13 @@
|
|
|
19
26
|
></event>
|
|
20
27
|
</defs>
|
|
21
28
|
<div>
|
|
22
|
-
<
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
<div class="heading" :role="headingRole" :aria-level="headingAriaLevel">
|
|
30
|
+
<button class="trigger" type="button" $ref="trigger" :aria-controls="contentId" :aria-expanded="internalOpen" :disabled="disabled">
|
|
31
|
+
<span class="summary"><slot name="summary"><template $value="summary"></template></slot></span>
|
|
32
|
+
<span class="chevron" aria-hidden="true"></span>
|
|
33
|
+
</button>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="panel" $ref="panel" :id="contentId"><div class="panel-inner"><slot></slot></div></div>
|
|
27
36
|
</div>
|
|
28
37
|
<style>
|
|
29
38
|
/* This component's hooks style the element they are set on, and never a nested one. */
|