@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
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
<prop name="min" type="sm | md | lg"
|
|
7
7
|
>The narrowest a column may get before the grid drops to fewer columns: sm is 12rem (also the default), md is 16rem, and lg is 20rem. The grid fits as many
|
|
8
8
|
equal columns as its width allows, and a single column never overflows a narrower container.</prop>
|
|
9
|
+
<prop name="columns" type="integer"
|
|
10
|
+
>Maximum columns when there is room. Omit for no cap. The grid still drops columns as its container narrows; it never forces a fixed column count.</prop>
|
|
9
11
|
</defs>
|
|
10
|
-
<div><slot></slot></div>
|
|
12
|
+
<div style:--_grid-column-cap="columns"><slot></slot></div>
|
|
11
13
|
<style>
|
|
12
14
|
/* This component's hooks style the element they are set on, and never a nested one. */
|
|
13
15
|
@property --ui-grid-gap {
|
|
@@ -24,7 +26,20 @@
|
|
|
24
26
|
--_grid-min: 12rem;
|
|
25
27
|
display: grid;
|
|
26
28
|
gap: var(--ui-grid-gap, var(--_grid-gap));
|
|
27
|
-
grid-template-columns:
|
|
29
|
+
grid-template-columns:
|
|
30
|
+
repeat(
|
|
31
|
+
auto-fit,
|
|
32
|
+
minmax(
|
|
33
|
+
min(
|
|
34
|
+
max(
|
|
35
|
+
var(--ui-grid-min, var(--_grid-min)),
|
|
36
|
+
calc((100% - (max(1, var(--_grid-column-cap, 9999)) - 1) * var(--ui-grid-gap, var(--_grid-gap))) / max(1, var(--_grid-column-cap, 9999)))
|
|
37
|
+
),
|
|
38
|
+
100%
|
|
39
|
+
),
|
|
40
|
+
1fr
|
|
41
|
+
)
|
|
42
|
+
);
|
|
28
43
|
min-inline-size: 0;
|
|
29
44
|
max-inline-size: 100%;
|
|
30
45
|
margin: 0;
|
|
@@ -15,10 +15,12 @@
|
|
|
15
15
|
'book-user': [{ tag: 'path', d: 'M15 13a3 3 0 1 0-6 0' }, { tag: 'path', d: 'M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20' }, { tag: 'circle', cx: '12', cy: '8', r: '2' }],
|
|
16
16
|
'braces': [{ tag: 'path', d: 'M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1' }, { tag: 'path', d: 'M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1' }],
|
|
17
17
|
'calculator': [{ tag: 'rect', width: '16', height: '20', x: '4', y: '2', rx: '2' }, { tag: 'line', x1: '8', x2: '16', y1: '6', y2: '6' }, { tag: 'line', x1: '16', x2: '16', y1: '14', y2: '18' }, { tag: 'path', d: 'M16 10h.01' }, { tag: 'path', d: 'M12 10h.01' }, { tag: 'path', d: 'M8 10h.01' }, { tag: 'path', d: 'M12 14h.01' }, { tag: 'path', d: 'M8 14h.01' }, { tag: 'path', d: 'M12 18h.01' }, { tag: 'path', d: 'M8 18h.01' }],
|
|
18
|
+
'check': [{ tag: 'path', d: 'M20 6 9 17l-5-5' }],
|
|
18
19
|
'chevron-down': [{ tag: 'path', d: 'm6 9 6 6 6-6' }],
|
|
19
20
|
'chevron-left': [{ tag: 'path', d: 'm15 18-6-6 6-6' }],
|
|
20
21
|
'chevron-right': [{ tag: 'path', d: 'm9 18 6-6-6-6' }],
|
|
21
22
|
'circle-check': [{ tag: 'circle', cx: '12', cy: '12', r: '10' }, { tag: 'path', d: 'm9 12 2 2 4-4' }],
|
|
23
|
+
'circle-dot': [{ tag: 'circle', cx: '12', cy: '12', r: '10' }, { tag: 'circle', cx: '12', cy: '12', r: '1' }],
|
|
22
24
|
'circle-x': [{ tag: 'circle', cx: '12', cy: '12', r: '10' }, { tag: 'path', d: 'm15 9-6 6' }, { tag: 'path', d: 'm9 9 6 6' }],
|
|
23
25
|
'code-xml': [{ tag: 'path', d: 'm18 16 4-4-4-4' }, { tag: 'path', d: 'm6 8-4 4 4 4' }, { tag: 'path', d: 'm14.5 4-5 16' }],
|
|
24
26
|
'columns': [{ tag: 'rect', width: '18', height: '18', x: '3', y: '3', rx: '2' }, { tag: 'path', d: 'M9 3v18' }, { tag: 'path', d: 'M15 3v18' }],
|
|
@@ -38,6 +40,7 @@
|
|
|
38
40
|
'info': [{ tag: 'circle', cx: '12', cy: '12', r: '10' }, { tag: 'path', d: 'M12 16v-4' }, { tag: 'path', d: 'M12 8h.01' }],
|
|
39
41
|
'italic': [{ tag: 'line', x1: '19', x2: '10', y1: '4', y2: '4' }, { tag: 'line', x1: '14', x2: '5', y1: '20', y2: '20' }, { tag: 'line', x1: '15', x2: '9', y1: '4', y2: '20' }],
|
|
40
42
|
'layout-dashboard': [{ tag: 'rect', width: '7', height: '9', x: '3', y: '3', rx: '1' }, { tag: 'rect', width: '7', height: '5', x: '14', y: '3', rx: '1' }, { tag: 'rect', width: '7', height: '9', x: '14', y: '12', rx: '1' }, { tag: 'rect', width: '7', height: '5', x: '3', y: '16', rx: '1' }],
|
|
43
|
+
'link': [{ tag: 'path', d: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71' }, { tag: 'path', d: 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71' }],
|
|
41
44
|
'list': [{ tag: 'path', d: 'M3 5h.01' }, { tag: 'path', d: 'M3 12h.01' }, { tag: 'path', d: 'M3 19h.01' }, { tag: 'path', d: 'M8 5h13' }, { tag: 'path', d: 'M8 12h13' }, { tag: 'path', d: 'M8 19h13' }],
|
|
42
45
|
'list-ordered': [{ tag: 'path', d: 'M11 5h10' }, { tag: 'path', d: 'M11 12h10' }, { tag: 'path', d: 'M11 19h10' }, { tag: 'path', d: 'M4 4h1v5' }, { tag: 'path', d: 'M4 9h2' }, { tag: 'path', d: 'M6.5 20H3.4c0-1 2.6-1.925 2.6-3.5a1.5 1.5 0 0 0-2.6-1.02' }],
|
|
43
46
|
'list-todo': [{ tag: 'path', d: 'M13 5h8' }, { tag: 'path', d: 'M13 12h8' }, { tag: 'path', d: 'M13 19h8' }, { tag: 'path', d: 'm3 17 2 2 4-4' }, { tag: 'rect', x: '3', y: '4', width: '6', height: '6', rx: '1' }],
|
|
@@ -65,7 +68,9 @@
|
|
|
65
68
|
'truck': [{ tag: 'path', d: 'M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2' }, { tag: 'path', d: 'M15 18H9' }, { tag: 'path', d: 'M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.624l-3.48-4.35A1 1 0 0 0 17.52 8H14' }, { tag: 'circle', cx: '17', cy: '18', r: '2' }, { tag: 'circle', cx: '7', cy: '18', r: '2' }],
|
|
66
69
|
'underline': [{ tag: 'path', d: 'M6 4v6a6 6 0 0 0 12 0V4' }, { tag: 'line', x1: '4', x2: '20', y1: '20', y2: '20' }],
|
|
67
70
|
'undo': [{ tag: 'path', d: 'M9 14 4 9l5-5' }, { tag: 'path', d: 'M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11' }],
|
|
71
|
+
'user-round': [{ tag: 'circle', cx: '12', cy: '8', r: '5' }, { tag: 'path', d: 'M20 21a8 8 0 0 0-16 0' }],
|
|
68
72
|
'users': [{ tag: 'path', d: 'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2' }, { tag: 'path', d: 'M16 3.128a4 4 0 0 1 0 7.744' }, { tag: 'path', d: 'M22 21v-2a4 4 0 0 0-3-3.87' }, { tag: 'circle', cx: '9', cy: '7', r: '4' }],
|
|
73
|
+
'x': [{ tag: 'path', d: 'M18 6 6 18' }, { tag: 'path', d: 'm6 6 12 12' }],
|
|
69
74
|
}[name]"
|
|
70
75
|
></computed>
|
|
71
76
|
</defs>
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
<link rel="component" href="../ui-spinner/ui-spinner.html">
|
|
1
2
|
<template component="ui-icon-button" controller="./ui-icon-button.js">
|
|
2
3
|
<defs>
|
|
3
4
|
<prop name="disabled" type="boolean" default="false"
|
|
4
5
|
>Disables the button, so it cannot be pressed or focused, and washes it out: the same shape, with less colour and contrast (--ui-disabled-filter).</prop>
|
|
6
|
+
<prop name="pending" type="boolean" default="false"
|
|
7
|
+
>Keeps the button focusable and named while work is in progress. It shows a spinner, states aria-disabled and aria-busy, and prevents activation until
|
|
8
|
+
cleared.</prop>
|
|
5
9
|
<prop name="label" type="string">Accessible name for the icon.</prop>
|
|
6
10
|
<prop name="size" type="sm | md | lg" default="md"
|
|
7
11
|
>Button and icon size together: sm is 1.75rem with a small icon, md is 2rem, and lg is 2.5rem with a large icon. On touch, every size gets an invisible hit
|
|
@@ -14,7 +18,10 @@
|
|
|
14
18
|
the pointer comes near inside a ui-affordance-scope. Touch devices and the solid variant always show it.</prop>
|
|
15
19
|
<prop name="round" type="boolean" default="false">Use a fully circular shape.</prop>
|
|
16
20
|
</defs>
|
|
17
|
-
<button :disabled="disabled" type="button" :aria-label="label"
|
|
21
|
+
<button :disabled="disabled" type="button" :aria-label="label" :aria-disabled="{ true: 'true', false: null }[format('%s', pending)]" :aria-busy="pending">
|
|
22
|
+
<ui-spinner $if="pending" size="sm"></ui-spinner>
|
|
23
|
+
<span class="content"><slot></slot></span>
|
|
24
|
+
</button>
|
|
18
25
|
<style>
|
|
19
26
|
/* This component's hooks style the element they are set on, and never a nested one. */
|
|
20
27
|
@property --ui-icon-button-active-scale {
|
|
@@ -145,7 +152,7 @@
|
|
|
145
152
|
padding: 0;
|
|
146
153
|
margin: 0;
|
|
147
154
|
border: 1px solid transparent;
|
|
148
|
-
border-radius: var(--ui-icon-button-radius, var(--ui-radius-md));
|
|
155
|
+
border-radius: var(--ui-icon-button-radius, var(--ui-action-radius, var(--ui-radius-md)));
|
|
149
156
|
background: var(--ui-icon-button-surface, transparent);
|
|
150
157
|
color: inherit;
|
|
151
158
|
display: inline-flex;
|
|
@@ -231,24 +238,36 @@
|
|
|
231
238
|
);
|
|
232
239
|
}
|
|
233
240
|
|
|
234
|
-
:host:hover:enabled {
|
|
241
|
+
:host:hover:enabled:not([aria-disabled="true"]) {
|
|
235
242
|
/* Derived from the text colour so the ghost hover shows on any background. */
|
|
236
243
|
background: var(--ui-icon-button-hover-surface, color-mix(in srgb, currentColor 8%, transparent));
|
|
237
244
|
color: var(--ui-icon-button-hover-text, var(--ui-text-primary));
|
|
238
245
|
}
|
|
239
246
|
|
|
240
|
-
:host-state([variant="outline"]):hover:enabled {
|
|
247
|
+
:host-state([variant="outline"]):hover:enabled:not([aria-disabled="true"]) {
|
|
241
248
|
border-color: var(--ui-icon-button-hover-border, var(--ui-border-strong));
|
|
242
249
|
}
|
|
243
250
|
|
|
244
|
-
:host-state([variant="solid"]):hover:enabled {
|
|
251
|
+
:host-state([variant="solid"]):hover:enabled:not([aria-disabled="true"]) {
|
|
245
252
|
background: var(--ui-icon-button-solid-hover-surface, var(--ui-accent-hover));
|
|
246
253
|
}
|
|
247
254
|
|
|
248
|
-
:host:active:enabled {
|
|
255
|
+
:host:active:enabled:not([aria-disabled="true"]) {
|
|
249
256
|
transform: scale(var(--ui-icon-button-active-scale, 0.96));
|
|
250
257
|
}
|
|
251
258
|
|
|
259
|
+
:host-state([pending]) {
|
|
260
|
+
cursor: progress;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.content {
|
|
264
|
+
display: contents;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
:host-state([pending]) .content {
|
|
268
|
+
display: none;
|
|
269
|
+
}
|
|
270
|
+
|
|
252
271
|
:host:focus-visible {
|
|
253
272
|
outline: none;
|
|
254
273
|
box-shadow: var(--ui-focus-halo);
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
// Marks an anticipatory button for the enclosing ui-affordance-scope, which reveals it on proximity.
|
|
2
2
|
export default function controller(host) {
|
|
3
|
-
|
|
3
|
+
const stop = host.effect(() => {
|
|
4
4
|
if (host.state.anticipatory) host.element.setAttribute("data-ui-affordance", "button");
|
|
5
5
|
else host.element.removeAttribute("data-ui-affordance");
|
|
6
6
|
});
|
|
7
|
+
const onClick = (event) => {
|
|
8
|
+
if (!host.state.pending) return;
|
|
9
|
+
event.preventDefault();
|
|
10
|
+
event.stopImmediatePropagation();
|
|
11
|
+
};
|
|
12
|
+
host.element.addEventListener("click", onClick, true);
|
|
13
|
+
return () => {
|
|
14
|
+
stop();
|
|
15
|
+
host.element.removeEventListener("click", onClick, true);
|
|
16
|
+
};
|
|
7
17
|
}
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
appearance: none;
|
|
78
78
|
border: var(--ui-input-border-width, var(--_ui-default-input-border-width, 1px)) solid
|
|
79
79
|
var(--ui-input-border, var(--_ui-default-input-border, var(--ui-control-border, var(--ui-border-strong))));
|
|
80
|
-
border-radius: var(--ui-input-radius, var(--_ui-default-input-radius, var(--ui-radius-md)));
|
|
80
|
+
border-radius: var(--ui-input-radius, var(--_ui-default-input-radius, var(--ui-field-radius, var(--ui-radius-md))));
|
|
81
81
|
background: var(--ui-input-surface, var(--_ui-default-input-surface, var(--ui-control-surface, var(--ui-surface-elevated))));
|
|
82
82
|
color: var(--ui-text-primary);
|
|
83
83
|
font: inherit;
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
/* The app's invalid flag, or the browser's own verdict once the user has left an invalid field. */
|
|
136
136
|
:host[aria-invalid="true"],
|
|
137
137
|
:host:user-invalid {
|
|
138
|
-
border-color: var(--ui-input-invalid-border, var(--_ui-default-input-invalid-border, var(--ui-danger-solid)));
|
|
138
|
+
border-color: var(--ui-input-invalid-border, var(--_ui-default-input-invalid-border, var(--ui-field-danger, var(--ui-danger-solid))));
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
:host:is([aria-invalid="true"], :user-invalid):focus-visible {
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
var(
|
|
145
145
|
--_ui-default-input-invalid-focus-shadow,
|
|
146
146
|
var(--ui-control-inset),
|
|
147
|
-
0 0 0 3px color-mix(in srgb, var(--ui-danger-solid) 22%, transparent)
|
|
147
|
+
0 0 0 3px color-mix(in srgb, var(--ui-input-invalid-border, var(--ui-field-danger, var(--ui-danger-solid))) 22%, transparent)
|
|
148
148
|
)
|
|
149
149
|
);
|
|
150
150
|
}
|
|
@@ -9,6 +9,18 @@
|
|
|
9
9
|
<span class="action"><slot name="action"></slot></span>
|
|
10
10
|
</div>
|
|
11
11
|
<style>
|
|
12
|
+
@property --ui-input-group-radius {
|
|
13
|
+
syntax: "*";
|
|
14
|
+
inherits: false;
|
|
15
|
+
}
|
|
16
|
+
@property --ui-input-group-invalid-border {
|
|
17
|
+
syntax: "*";
|
|
18
|
+
inherits: false;
|
|
19
|
+
}
|
|
20
|
+
@property --ui-input-group-invalid-focus-shadow {
|
|
21
|
+
syntax: "*";
|
|
22
|
+
inherits: false;
|
|
23
|
+
}
|
|
12
24
|
/* An input with fixed text before or after it, such as a domain after a site's name
|
|
13
25
|
("acme" + ".example.com") or slashes around a path. The group draws the field's border and
|
|
14
26
|
focus ring around the input and its affixes together; the input inside drops its own through
|
|
@@ -41,7 +53,7 @@
|
|
|
41
53
|
min-block-size: var(--ui-control-size-md);
|
|
42
54
|
/* The frame reads the shared control tokens with a lone Input's fallbacks, so the two match. */
|
|
43
55
|
border: 1px solid var(--ui-control-border, var(--ui-border-strong));
|
|
44
|
-
border-radius: var(--ui-radius-md);
|
|
56
|
+
border-radius: var(--ui-input-group-radius, var(--ui-field-radius, var(--ui-radius-md)));
|
|
45
57
|
background: var(--ui-control-surface, var(--ui-surface-elevated));
|
|
46
58
|
box-shadow: var(--ui-control-inset);
|
|
47
59
|
overflow: hidden;
|
|
@@ -79,13 +91,15 @@
|
|
|
79
91
|
}
|
|
80
92
|
|
|
81
93
|
:host:has([aria-invalid="true"], :user-invalid) {
|
|
82
|
-
border-color: var(--ui-danger-solid);
|
|
94
|
+
border-color: var(--ui-input-group-invalid-border, var(--ui-field-danger, var(--ui-danger-solid)));
|
|
83
95
|
}
|
|
84
96
|
|
|
85
97
|
:host:has(input:is([aria-invalid="true"], :user-invalid):focus-visible) {
|
|
86
|
-
box-shadow:
|
|
98
|
+
box-shadow: var(
|
|
99
|
+
--ui-input-group-invalid-focus-shadow,
|
|
87
100
|
var(--ui-control-inset),
|
|
88
|
-
0 0 0 3px color-mix(in srgb, var(--ui-danger-solid) 22%, transparent)
|
|
101
|
+
0 0 0 3px color-mix(in srgb, var(--ui-input-group-invalid-border, var(--ui-field-danger, var(--ui-danger-solid))) 22%, transparent)
|
|
102
|
+
);
|
|
89
103
|
}
|
|
90
104
|
|
|
91
105
|
/* Forced colors drop box shadows, so the focus ring becomes an outline. */
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<template component="ui-listbox" controller="./ui-listbox.js">
|
|
2
|
+
<defs>
|
|
3
|
+
<prop name="rows" type="number" default="5">The number of option rows visible at once. Use at least two.</prop>
|
|
4
|
+
<prop name="multiple" type="boolean" default="false">Allows several options to be selected with native keyboard and pointer behavior.</prop>
|
|
5
|
+
<prop name="value" type="string">The initial or externally set single selection. Authored selected options work when it is omitted.</prop>
|
|
6
|
+
<prop name="values" type="list(string)">The initial or externally set selections in multiple mode. Authored selected options work when it is omitted.</prop>
|
|
7
|
+
<prop name="disabled" type="boolean" default="false">Prevents selection and form submission.</prop>
|
|
8
|
+
<prop name="required" type="boolean" default="false">Requires at least one selection for form validation.</prop>
|
|
9
|
+
<prop name="invalid" type="boolean" default="false">Exposes invalid state to assistive technology and styling.</prop>
|
|
10
|
+
</defs>
|
|
11
|
+
<select :size="rows" :multiple="multiple" :disabled="disabled" :required="required" :aria-invalid="invalid"><slot></slot></select>
|
|
12
|
+
<style>
|
|
13
|
+
@property --ui-listbox-radius {
|
|
14
|
+
syntax: "*";
|
|
15
|
+
inherits: false;
|
|
16
|
+
}
|
|
17
|
+
@property --ui-listbox-border {
|
|
18
|
+
syntax: "*";
|
|
19
|
+
inherits: false;
|
|
20
|
+
}
|
|
21
|
+
@property --ui-listbox-surface {
|
|
22
|
+
syntax: "*";
|
|
23
|
+
inherits: false;
|
|
24
|
+
}
|
|
25
|
+
@property --ui-listbox-focus-shadow {
|
|
26
|
+
syntax: "*";
|
|
27
|
+
inherits: false;
|
|
28
|
+
}
|
|
29
|
+
@property --ui-listbox-invalid-border {
|
|
30
|
+
syntax: "*";
|
|
31
|
+
inherits: false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
:host {
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
display: block;
|
|
37
|
+
inline-size: 100%;
|
|
38
|
+
min-inline-size: 0;
|
|
39
|
+
min-block-size: var(--ui-control-min-block-size);
|
|
40
|
+
margin: 0;
|
|
41
|
+
padding: var(--ui-space-1);
|
|
42
|
+
border: 1px solid var(--ui-listbox-border, var(--ui-control-border));
|
|
43
|
+
border-radius: var(--ui-listbox-radius, var(--ui-field-radius, var(--ui-radius-md)));
|
|
44
|
+
background: var(--ui-listbox-surface, var(--ui-control-surface));
|
|
45
|
+
color: var(--ui-text-primary);
|
|
46
|
+
color-scheme: light dark;
|
|
47
|
+
font: inherit;
|
|
48
|
+
line-height: var(--ui-line-height-md);
|
|
49
|
+
box-shadow: var(--ui-control-inset);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
:host option,
|
|
53
|
+
:host optgroup {
|
|
54
|
+
padding: var(--ui-space-2);
|
|
55
|
+
font: inherit;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
:host:focus-visible {
|
|
59
|
+
border-color: var(--ui-control-focus);
|
|
60
|
+
outline: 2px solid var(--ui-focus-ring);
|
|
61
|
+
outline-offset: 2px;
|
|
62
|
+
box-shadow: var(--ui-listbox-focus-shadow, var(--ui-focus-halo));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
:host[aria-invalid="true"],
|
|
66
|
+
:host:user-invalid {
|
|
67
|
+
border-color: var(--ui-listbox-invalid-border, var(--ui-field-danger, var(--ui-danger)));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
:host:disabled {
|
|
71
|
+
background: var(--ui-control-surface-disabled);
|
|
72
|
+
color: var(--ui-disabled-text);
|
|
73
|
+
box-shadow: none;
|
|
74
|
+
cursor: not-allowed;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
:host[hidden] {
|
|
78
|
+
display: none;
|
|
79
|
+
}
|
|
80
|
+
</style>
|
|
81
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Keep externally supplied selections in sync when options arrive after the listbox upgrades.
|
|
2
|
+
// Without value or values, the native select owns selection, keyboard behavior, and form reset.
|
|
3
|
+
export default function controller(host) {
|
|
4
|
+
const select = host.element;
|
|
5
|
+
const apply = () => {
|
|
6
|
+
if (host.state.multiple) {
|
|
7
|
+
if (host.state.values === undefined || host.state.values === null) return;
|
|
8
|
+
const values = new Set(host.state.values.map(String));
|
|
9
|
+
for (const option of select.options) {
|
|
10
|
+
option.defaultSelected = values.has(option.value);
|
|
11
|
+
option.selected = values.has(option.value);
|
|
12
|
+
}
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (host.state.value === undefined || host.state.value === null) return;
|
|
16
|
+
const value = String(host.state.value);
|
|
17
|
+
for (const option of select.options) option.defaultSelected = option.value === value;
|
|
18
|
+
if (select.value !== value) select.value = value;
|
|
19
|
+
};
|
|
20
|
+
const observer = new MutationObserver(apply);
|
|
21
|
+
observer.observe(select, { childList: true, subtree: true });
|
|
22
|
+
const stop = host.effect(apply);
|
|
23
|
+
return () => {
|
|
24
|
+
stop();
|
|
25
|
+
observer.disconnect();
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<prop name="for" type="string" default="">ID of the control that toggles and anchors the menu.</prop>
|
|
6
6
|
<prop name="placement" type="string" default="bottom-start">Positions an associated menu relative to its trigger.</prop>
|
|
7
7
|
<state name="internalOpen" :value="false"></state>
|
|
8
|
-
<event name="select" type="object({ value: string, trigger: keyboard | pointer | programmatic })"></event>
|
|
8
|
+
<event name="select" type="object({ value: string, checked?: boolean, trigger: keyboard | pointer | programmatic })"></event>
|
|
9
9
|
<event name="open" type="object({ open: boolean, reason: action | programmatic, trigger: keyboard | pointer | programmatic })"></event>
|
|
10
10
|
<event
|
|
11
11
|
name="close"
|
|
@@ -15,6 +15,22 @@
|
|
|
15
15
|
<div role="menu" aria-orientation="vertical"><div class="surface"><slot></slot></div></div>
|
|
16
16
|
<style>
|
|
17
17
|
/* This component's hooks style the element they are set on, and never a nested one. */
|
|
18
|
+
@property --ui-menu-surface {
|
|
19
|
+
syntax: "*";
|
|
20
|
+
inherits: false;
|
|
21
|
+
}
|
|
22
|
+
@property --ui-menu-border {
|
|
23
|
+
syntax: "*";
|
|
24
|
+
inherits: false;
|
|
25
|
+
}
|
|
26
|
+
@property --ui-menu-radius {
|
|
27
|
+
syntax: "*";
|
|
28
|
+
inherits: false;
|
|
29
|
+
}
|
|
30
|
+
@property --ui-menu-shadow {
|
|
31
|
+
syntax: "*";
|
|
32
|
+
inherits: false;
|
|
33
|
+
}
|
|
18
34
|
@property --ui-menu-viewport-gap {
|
|
19
35
|
syntax: "*";
|
|
20
36
|
inherits: false;
|
|
@@ -32,6 +48,10 @@
|
|
|
32
48
|
}
|
|
33
49
|
|
|
34
50
|
:host {
|
|
51
|
+
--_ui-menu-surface: var(--ui-menu-surface);
|
|
52
|
+
--_ui-menu-border: var(--ui-menu-border);
|
|
53
|
+
--_ui-menu-radius: var(--ui-menu-radius);
|
|
54
|
+
--_ui-menu-shadow: var(--ui-menu-shadow);
|
|
35
55
|
/* A menu nested in a compact one starts again from its own density. */
|
|
36
56
|
--_ui-default-menu-item-padding-block: initial;
|
|
37
57
|
--_ui-default-menu-item-padding-inline: initial;
|
|
@@ -58,14 +78,22 @@
|
|
|
58
78
|
box-sizing: border-box;
|
|
59
79
|
max-inline-size: inherit;
|
|
60
80
|
padding: var(--ui-space-1);
|
|
61
|
-
background: var(--ui-surface-elevated);
|
|
62
|
-
border: 1px solid var(--ui-border-default);
|
|
63
|
-
border-radius: var(--ui-radius-lg);
|
|
64
|
-
box-shadow: var(--ui-elevation-overlay, var(--ui-shadow-lg));
|
|
81
|
+
background: var(--_ui-menu-surface, var(--ui-overlay-surface, var(--ui-surface-elevated)));
|
|
82
|
+
border: 1px solid var(--_ui-menu-border, var(--ui-overlay-border, var(--ui-border-default)));
|
|
83
|
+
border-radius: var(--_ui-menu-radius, var(--ui-overlay-radius, var(--ui-radius-lg)));
|
|
84
|
+
box-shadow: var(--_ui-menu-shadow, var(--ui-overlay-shadow, var(--ui-elevation-overlay, var(--ui-shadow-lg))));
|
|
65
85
|
max-block-size: inherit;
|
|
66
86
|
overflow: auto;
|
|
67
87
|
}
|
|
68
88
|
|
|
89
|
+
.surface hr {
|
|
90
|
+
align-self: stretch;
|
|
91
|
+
block-size: 0;
|
|
92
|
+
margin: var(--ui-space-1) 0;
|
|
93
|
+
border: 0;
|
|
94
|
+
border-block-start: 1px solid var(--ui-border-default);
|
|
95
|
+
}
|
|
96
|
+
|
|
69
97
|
:host:not(:host-state([internalOpen])) {
|
|
70
98
|
display: none;
|
|
71
99
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
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 triggerFor(event) {
|
|
4
5
|
if (event instanceof KeyboardEvent) return "keyboard";
|
|
5
|
-
if (event instanceof MouseEvent || event instanceof PointerEvent) return "pointer";
|
|
6
|
+
if (event instanceof MouseEvent || event instanceof PointerEvent) return event.detail === 0 ? "keyboard" : "pointer";
|
|
6
7
|
return "programmatic";
|
|
7
8
|
}
|
|
8
9
|
|
|
@@ -14,7 +15,7 @@ export default function controller(host) {
|
|
|
14
15
|
const element = host.element;
|
|
15
16
|
const document = element.ownerDocument;
|
|
16
17
|
const overlayId = `ui-menu-${Math.random().toString(36).slice(2, 11)}`;
|
|
17
|
-
const items = () =>
|
|
18
|
+
const items = () => menuItems(element);
|
|
18
19
|
let anchor = null;
|
|
19
20
|
let surface = null;
|
|
20
21
|
let lastFor;
|
|
@@ -28,7 +29,7 @@ export default function controller(host) {
|
|
|
28
29
|
else {
|
|
29
30
|
host.state.internalOpen = true;
|
|
30
31
|
host.dispatch("open", { open: true, reason: "action", trigger });
|
|
31
|
-
requestAnimationFrame(() => items()
|
|
32
|
+
requestAnimationFrame(() => items()[0]?.focus());
|
|
32
33
|
}
|
|
33
34
|
};
|
|
34
35
|
const close = (reason, trigger) => {
|
|
@@ -73,29 +74,34 @@ export default function controller(host) {
|
|
|
73
74
|
const select = (item, trigger) => {
|
|
74
75
|
if (!item || disabled(item)) return;
|
|
75
76
|
const value = item.getAttribute("data-value") ?? item.getAttribute("value") ?? "";
|
|
77
|
+
if (item.getAttribute("role") !== "menuitem") {
|
|
78
|
+
const detail = { trigger, checked: undefined };
|
|
79
|
+
item.dispatchEvent(new CustomEvent("ui-menu-item-activate", { detail }));
|
|
80
|
+
host.dispatch("select", { value, checked: detail.checked, trigger });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
76
83
|
host.dispatch("select", { value, trigger });
|
|
77
84
|
host.dispatch("close", { open: false, reason: "action", trigger });
|
|
78
85
|
host.state.internalOpen = false;
|
|
79
86
|
};
|
|
80
|
-
const onClick = (event) => select(event.target
|
|
87
|
+
const onClick = (event) => select(menuItemFrom(event.target), triggerFor(event));
|
|
81
88
|
const onKeydown = (event) => {
|
|
82
89
|
if (event.key === "Escape") {
|
|
83
90
|
event.preventDefault();
|
|
84
91
|
requestTopOverlayClose(document, "escape", "keyboard");
|
|
85
92
|
return;
|
|
86
93
|
}
|
|
87
|
-
const
|
|
88
|
-
const target = event.target.closest?.('[role="menuitem"]');
|
|
89
|
-
const index = target ? enabled.indexOf(target) : -1;
|
|
94
|
+
const target = menuItemFrom(event.target);
|
|
90
95
|
if (["Enter", " "].includes(event.key)) {
|
|
91
|
-
if (
|
|
96
|
+
if (!target || disabled(target)) return;
|
|
97
|
+
if (target.localName === "a") {
|
|
98
|
+
if (event.key === " ") { event.preventDefault(); target.click(); }
|
|
99
|
+
} else {
|
|
92
100
|
event.preventDefault();
|
|
93
101
|
select(target, "keyboard");
|
|
94
102
|
}
|
|
95
|
-
} else
|
|
96
|
-
event
|
|
97
|
-
const next = event.key === "ArrowDown" ? (index < 0 ? 0 : Math.min(index + 1, enabled.length - 1)) : (index <= 0 ? enabled.length - 1 : index - 1);
|
|
98
|
-
enabled[next]?.focus();
|
|
103
|
+
} else {
|
|
104
|
+
navigateMenu(event, element);
|
|
99
105
|
}
|
|
100
106
|
};
|
|
101
107
|
element.addEventListener("click", onClick);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<template component="ui-menu-group">
|
|
2
|
+
<defs>
|
|
3
|
+
<prop name="label" type="string" default="">Visible heading and accessible name for this group of menu items.</prop>
|
|
4
|
+
</defs>
|
|
5
|
+
<div role="group" :aria-label="label">
|
|
6
|
+
<div class="heading" $if="label" $value="label"></div>
|
|
7
|
+
<slot></slot>
|
|
8
|
+
</div>
|
|
9
|
+
<style>
|
|
10
|
+
:host {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
min-inline-size: 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.heading {
|
|
17
|
+
padding: var(--ui-space-2) var(--ui-space-3) var(--ui-space-1);
|
|
18
|
+
color: var(--ui-text-muted);
|
|
19
|
+
font-size: var(--ui-font-size-sm);
|
|
20
|
+
font-weight: var(--ui-font-semibold);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
:host[hidden] {
|
|
24
|
+
display: none;
|
|
25
|
+
}
|
|
26
|
+
</style>
|
|
27
|
+
</template>
|
|
@@ -1,12 +1,46 @@
|
|
|
1
|
-
<
|
|
1
|
+
<link rel="component" href="../ui-icon/ui-icon.html">
|
|
2
|
+
<template component="ui-menu-item" controller="./ui-menu-item.js">
|
|
2
3
|
<defs>
|
|
3
4
|
<prop name="disabled" type="boolean" default="false"
|
|
4
5
|
>Makes the item unavailable: it is faded, skipped by the arrow keys, and choosing it does nothing. It stays visible and is announced as disabled.</prop>
|
|
5
6
|
<prop name="value" type="string" default=""
|
|
6
7
|
>The value the enclosing menu reports in its select event when this item is chosen. Give each item a distinct value so one handler can tell them
|
|
7
8
|
apart.</prop>
|
|
9
|
+
<prop name="href" type="string" default="">A destination makes an action item a real link, with native navigation and link context actions.</prop>
|
|
10
|
+
<prop name="type" type="action | checkbox | radio" default="action"
|
|
11
|
+
>An action closes its menu; a checkbox toggles, and a radio chooses one item within its nearest Menu Group. Checkable items keep the menu open.</prop>
|
|
12
|
+
<prop name="checked" type="boolean" default="false">Initial or externally controlled checked state of a checkbox or radio item.</prop>
|
|
13
|
+
<state name="internalChecked" :value="false"></state>
|
|
14
|
+
<event name="change" type="object({ checked: boolean, value: string, type: checkbox | radio, trigger: keyboard | pointer | programmatic })"></event>
|
|
8
15
|
</defs>
|
|
9
|
-
<
|
|
16
|
+
<template $match>
|
|
17
|
+
<a
|
|
18
|
+
$when="href and type = 'action'"
|
|
19
|
+
role="menuitem"
|
|
20
|
+
tabindex="-1"
|
|
21
|
+
:aria-disabled="disabled"
|
|
22
|
+
:data-value="value"
|
|
23
|
+
:href="{ true: null, false: href }[format('%s', disabled)]"
|
|
24
|
+
>
|
|
25
|
+
<span class="label"><slot></slot></span>
|
|
26
|
+
<span class="shortcut" aria-hidden="true"><slot name="shortcut"></slot></span>
|
|
27
|
+
</a>
|
|
28
|
+
<div
|
|
29
|
+
$else
|
|
30
|
+
:role="{ action: 'menuitem', checkbox: 'menuitemcheckbox', radio: 'menuitemradio' }[type]"
|
|
31
|
+
:aria-checked="{ action: null, checkbox: internalChecked, radio: internalChecked }[type]"
|
|
32
|
+
tabindex="-1"
|
|
33
|
+
:aria-disabled="disabled"
|
|
34
|
+
:data-value="value"
|
|
35
|
+
>
|
|
36
|
+
<span class="indicator" aria-hidden="true">
|
|
37
|
+
<ui-icon $if="type = 'checkbox' and internalChecked" name="check"></ui-icon>
|
|
38
|
+
<ui-icon $if="type = 'radio' and internalChecked" name="circle-dot"></ui-icon>
|
|
39
|
+
</span>
|
|
40
|
+
<span class="label"><slot></slot></span>
|
|
41
|
+
<span class="shortcut" aria-hidden="true"><slot name="shortcut"></slot></span>
|
|
42
|
+
</div>
|
|
43
|
+
</template>
|
|
10
44
|
<style>
|
|
11
45
|
/* This component's hooks style the element they are set on, and never a nested one. */
|
|
12
46
|
@property --ui-menu-item-font-size {
|
|
@@ -35,7 +69,9 @@
|
|
|
35
69
|
}
|
|
36
70
|
|
|
37
71
|
:host {
|
|
38
|
-
display:
|
|
72
|
+
display: flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
gap: var(--ui-space-5);
|
|
39
75
|
box-sizing: border-box;
|
|
40
76
|
width: 100%;
|
|
41
77
|
padding: var(--ui-menu-item-padding-block, var(--_ui-default-menu-item-padding-block, var(--ui-space-2)))
|
|
@@ -45,19 +81,44 @@
|
|
|
45
81
|
color: var(--ui-text-primary);
|
|
46
82
|
font-size: var(--ui-menu-item-font-size, var(--_ui-default-menu-item-font-size, var(--ui-font-size-md)));
|
|
47
83
|
font-weight: var(--ui-font-medium);
|
|
84
|
+
text-decoration: none;
|
|
48
85
|
cursor: pointer;
|
|
49
86
|
outline: none;
|
|
50
87
|
transition: background-color var(--ui-motion-fast) var(--ui-motion-ease), color var(--ui-motion-fast) var(--ui-motion-ease);
|
|
51
88
|
}
|
|
52
89
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
90
|
+
.label {
|
|
91
|
+
flex: 1;
|
|
92
|
+
min-inline-size: 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.indicator {
|
|
96
|
+
display: inline-flex;
|
|
97
|
+
place-items: center;
|
|
98
|
+
flex: none;
|
|
99
|
+
inline-size: var(--ui-icon-size-md);
|
|
100
|
+
color: var(--ui-accent-solid);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
:host-state([type="action"]) .indicator {
|
|
104
|
+
display: none;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.shortcut {
|
|
108
|
+
margin-inline-start: auto;
|
|
109
|
+
color: var(--ui-text-secondary);
|
|
110
|
+
font-size: var(--ui-font-size-sm);
|
|
111
|
+
font-weight: var(--ui-font-normal);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.shortcut:empty {
|
|
115
|
+
display: none;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Keep the text row at the shared control height where cap trimming is available. */
|
|
56
119
|
@supports (text-box-trim: trim-both) {
|
|
57
120
|
:host {
|
|
58
|
-
display: block;
|
|
59
121
|
min-block-size: var(--ui-menu-item-min-block-size, var(--ui-control-size-md, 2.5rem));
|
|
60
|
-
align-content: center;
|
|
61
122
|
text-box-trim: trim-both;
|
|
62
123
|
text-box-edge: cap alphabetic;
|
|
63
124
|
}
|
|
@@ -67,11 +128,22 @@
|
|
|
67
128
|
background: var(--ui-menu-item-hover-surface, var(--ui-surface-subtle));
|
|
68
129
|
}
|
|
69
130
|
|
|
131
|
+
:host:focus-visible {
|
|
132
|
+
outline: 2px solid var(--ui-focus-ring);
|
|
133
|
+
outline-offset: -2px;
|
|
134
|
+
}
|
|
135
|
+
|
|
70
136
|
:host[aria-disabled="true"] {
|
|
71
137
|
opacity: 0.6;
|
|
72
138
|
cursor: not-allowed;
|
|
73
139
|
}
|
|
74
140
|
|
|
141
|
+
@media (forced-colors: active) {
|
|
142
|
+
:host:focus-visible {
|
|
143
|
+
outline-color: Highlight;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
75
147
|
/* A component that sets its display still honours the hidden attribute on its root. */
|
|
76
148
|
:host[hidden] {
|
|
77
149
|
display: none;
|