@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
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
<link rel="component" href="../ui-icon/ui-icon.html">
|
|
2
|
+
<link rel="component" href="../ui-editor-slash-menu-item/ui-editor-slash-menu-item.html">
|
|
3
|
+
<link rel="component" href="../ui-editor-slash-menu-group/ui-editor-slash-menu-group.html">
|
|
2
4
|
<template component="ui-editor-slash-menu" controller="./ui-editor-slash-menu.js">
|
|
3
5
|
<defs>
|
|
4
6
|
<prop name="open" type="boolean" default="false">Whether the menu is open.</prop>
|
|
5
7
|
<prop name="query" type="string" default="">The text typed after the slash.</prop>
|
|
6
|
-
<prop name="items" type="list(object({ title: string, description: string, icon: string }))"
|
|
8
|
+
<prop name="items" type="list(object({ value?: string, title: string, description: string, icon: string }))"
|
|
9
|
+
>Integration-supplied commands. Authored Slash Menu Item children are the markup alternative.</prop>
|
|
10
|
+
<prop name="label" type="string" default="Insert block"
|
|
11
|
+
>Accessible name of the suggestion list, used unless an aria-label or aria-labelledby is authored.</prop>
|
|
7
12
|
<prop name="selectedIndex" type="integer" default="0"
|
|
8
13
|
>The zero-based index of the highlighted item. The menu does not handle arrow keys itself: the editor sets this as the user moves, while hovering an item
|
|
9
14
|
highlights it and reports a highlight event.</prop>
|
|
@@ -13,23 +18,29 @@
|
|
|
13
18
|
>Where the slash was typed, in viewport coordinates.</prop>
|
|
14
19
|
<state name="visible" :value="false"></state>
|
|
15
20
|
<state name="active" :value="0"></state>
|
|
16
|
-
<
|
|
17
|
-
<event name="
|
|
21
|
+
<state name="rows" type="list(object({ value: string, title: string, description: string, icon: string }))" :value="[]"></state>
|
|
22
|
+
<event name="highlight" type="object({ index: integer, value: string })"></event>
|
|
23
|
+
<event name="select" type="object({ index: integer, value: string })"></event>
|
|
18
24
|
</defs>
|
|
19
|
-
<div role="listbox"
|
|
20
|
-
<div class="header"
|
|
21
|
-
<span class="header-label">Insert</span>
|
|
22
|
-
<span $if="query" class="header-query" $value="query"></span>
|
|
23
|
-
</div>
|
|
25
|
+
<div role="listbox" :hidden="not visible">
|
|
26
|
+
<div class="header"><slot name="header"></slot></div>
|
|
24
27
|
<ul class="list" role="presentation">
|
|
25
|
-
<li
|
|
28
|
+
<li
|
|
29
|
+
$each="item, index of rows"
|
|
30
|
+
class="item"
|
|
31
|
+
class:active="index = active"
|
|
32
|
+
role="option"
|
|
33
|
+
:aria-selected="index = active"
|
|
34
|
+
:data-index="index"
|
|
35
|
+
:data-value="item.value"
|
|
36
|
+
>
|
|
26
37
|
<span class="icon" aria-hidden="true"><ui-icon :name="item.icon"></ui-icon></span>
|
|
27
38
|
<span class="body"><span class="title" $value="item.title"></span><span class="description" $value="item.description"></span></span>
|
|
28
39
|
</li>
|
|
40
|
+
<slot></slot>
|
|
29
41
|
</ul>
|
|
30
|
-
<div class="
|
|
31
|
-
|
|
32
|
-
</div>
|
|
42
|
+
<div class="empty" $ref="empty"><slot name="empty"></slot></div>
|
|
43
|
+
<div class="footer"><slot name="footer"></slot></div>
|
|
33
44
|
</div>
|
|
34
45
|
<style>
|
|
35
46
|
:host {
|
|
@@ -66,23 +77,22 @@
|
|
|
66
77
|
letter-spacing: 0.05em;
|
|
67
78
|
}
|
|
68
79
|
|
|
80
|
+
.header:not(:has(> *)), .footer:not(:has(> *)), .empty:not(:has(> *)) {
|
|
81
|
+
display: none;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.empty {
|
|
85
|
+
padding: var(--ui-space-3);
|
|
86
|
+
color: var(--ui-text-muted);
|
|
87
|
+
font-size: var(--ui-font-size-sm);
|
|
88
|
+
}
|
|
89
|
+
|
|
69
90
|
@media (max-width: 767px) {
|
|
70
91
|
.header {
|
|
71
92
|
display: flex;
|
|
72
93
|
}
|
|
73
94
|
}
|
|
74
95
|
|
|
75
|
-
.header-query {
|
|
76
|
-
background: var(--ui-surface-hover);
|
|
77
|
-
border-radius: var(--ui-radius-sm);
|
|
78
|
-
padding: 0.1em 0.4em;
|
|
79
|
-
font-family: var(--ui-font-family-mono, ui-monospace, monospace);
|
|
80
|
-
font-size: var(--ui-font-size-xs);
|
|
81
|
-
color: var(--ui-accent);
|
|
82
|
-
text-transform: none;
|
|
83
|
-
letter-spacing: 0;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
96
|
.list {
|
|
87
97
|
list-style: none;
|
|
88
98
|
margin: 0;
|
|
@@ -183,19 +193,12 @@
|
|
|
183
193
|
}
|
|
184
194
|
|
|
185
195
|
.footer {
|
|
186
|
-
display: none;
|
|
187
196
|
padding: var(--ui-space-2) var(--ui-space-3);
|
|
188
197
|
border-top: 1px solid var(--ui-border);
|
|
189
198
|
font-size: var(--ui-font-size-xs);
|
|
190
199
|
color: var(--ui-text-muted);
|
|
191
200
|
}
|
|
192
201
|
|
|
193
|
-
@media (min-width: 768px) {
|
|
194
|
-
.footer {
|
|
195
|
-
display: block;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
202
|
:host kbd {
|
|
200
203
|
display: inline-flex;
|
|
201
204
|
align-items: center;
|
|
@@ -3,27 +3,60 @@ import { normalizeAnchor, positionMenu } from "../shared/editor.js";
|
|
|
3
3
|
// Places the menu at the slash and tracks the highlighted item; the template renders the items.
|
|
4
4
|
export default function controller(host) {
|
|
5
5
|
const element = host.element;
|
|
6
|
+
const authoredLabel = element.hasAttribute("aria-label") || element.hasAttribute("aria-labelledby");
|
|
6
7
|
const place = () => {
|
|
7
8
|
if (host.state.visible) positionMenu(element, normalizeAnchor(host.state.anchorRect), 280);
|
|
8
9
|
};
|
|
10
|
+
const options = () => Array.from(element.querySelectorAll('[role="option"]'));
|
|
11
|
+
const syncOptions = () => {
|
|
12
|
+
const query = String(host.state.query || "").trim().toLocaleLowerCase();
|
|
13
|
+
const generated = host.state.rows.length > 0;
|
|
14
|
+
for (const row of options()) {
|
|
15
|
+
if (generated) break;
|
|
16
|
+
const title = row.querySelector(".title")?.textContent ?? row.textContent ?? "";
|
|
17
|
+
const terms = `${title} ${row.dataset.keywords ?? ""}`.toLocaleLowerCase();
|
|
18
|
+
row.hidden = Boolean(query && !terms.includes(query));
|
|
19
|
+
}
|
|
20
|
+
for (const group of element.querySelectorAll('[role="group"]')) {
|
|
21
|
+
group.closest('[role="presentation"]')?.toggleAttribute("hidden", !group.querySelector('[role="option"]:not([hidden])'));
|
|
22
|
+
}
|
|
23
|
+
const rows = options().filter((row) => !row.hidden && !row.closest('[role="presentation"][hidden]')
|
|
24
|
+
&& row.getAttribute("aria-disabled") !== "true");
|
|
25
|
+
const active = Math.min(Math.max(0, Number(host.state.active) || 0), Math.max(0, rows.length - 1));
|
|
26
|
+
rows.forEach((row, index) => {
|
|
27
|
+
row.dataset.index = String(index);
|
|
28
|
+
row.setAttribute("aria-selected", String(index === active));
|
|
29
|
+
});
|
|
30
|
+
host.refs.empty.hidden = rows.length > 0;
|
|
31
|
+
const visible = Boolean(host.state.open && normalizeAnchor(host.state.anchorRect)
|
|
32
|
+
&& (rows.length > 0 || host.refs.empty.children.length));
|
|
33
|
+
if (host.state.visible !== visible) host.state.visible = visible;
|
|
34
|
+
place();
|
|
35
|
+
};
|
|
9
36
|
const stop = host.effect(() => {
|
|
10
37
|
const items = Array.isArray(host.state.items) ? host.state.items : [];
|
|
38
|
+
if (!authoredLabel) element.setAttribute("aria-label", String(host.state.label || "Insert block"));
|
|
39
|
+
host.state.rows = items.map((item) => ({ ...item, value: String(item.value ?? item.title) }));
|
|
11
40
|
host.state.active = Number(host.state.selectedIndex ?? 0);
|
|
12
|
-
|
|
13
|
-
place();
|
|
41
|
+
queueMicrotask(syncOptions);
|
|
14
42
|
});
|
|
15
|
-
const
|
|
43
|
+
const rowOf = (event) => event.target.closest?.('[role="option"]');
|
|
16
44
|
const onMousedown = (event) => event.preventDefault();
|
|
17
45
|
const onClick = (event) => {
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
46
|
+
const row = rowOf(event);
|
|
47
|
+
if (row && !row.hidden && row.getAttribute("aria-disabled") !== "true") host.dispatch("select", { index: Number(row.dataset.index), value: row.dataset.value ?? "" });
|
|
20
48
|
};
|
|
21
49
|
const onMouseover = (event) => {
|
|
22
|
-
const
|
|
23
|
-
if (!
|
|
50
|
+
const row = rowOf(event);
|
|
51
|
+
if (!row || row.hidden || row.getAttribute("aria-disabled") === "true") return;
|
|
52
|
+
const index = Number(row.dataset.index);
|
|
53
|
+
if (index === host.state.active) return;
|
|
24
54
|
host.state.active = index;
|
|
25
|
-
|
|
55
|
+
syncOptions();
|
|
56
|
+
host.dispatch("highlight", { index, value: row.dataset.value ?? "" });
|
|
26
57
|
};
|
|
58
|
+
const observer = new MutationObserver(syncOptions);
|
|
59
|
+
observer.observe(element, { childList: true, subtree: true });
|
|
27
60
|
element.addEventListener("mousedown", onMousedown);
|
|
28
61
|
element.addEventListener("click", onClick);
|
|
29
62
|
element.addEventListener("mouseover", onMouseover);
|
|
@@ -33,6 +66,7 @@ export default function controller(host) {
|
|
|
33
66
|
window.visualViewport?.addEventListener("scroll", place);
|
|
34
67
|
return () => {
|
|
35
68
|
stop();
|
|
69
|
+
observer.disconnect();
|
|
36
70
|
element.removeEventListener("mousedown", onMousedown);
|
|
37
71
|
element.removeEventListener("click", onClick);
|
|
38
72
|
element.removeEventListener("mouseover", onMouseover);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<template component="ui-editor-slash-menu-group">
|
|
2
|
+
<defs>
|
|
3
|
+
<prop name="label" type="string" default="">Names this group of related slash commands.</prop>
|
|
4
|
+
</defs>
|
|
5
|
+
<li role="presentation">
|
|
6
|
+
<div role="group" :aria-label="label">
|
|
7
|
+
<div class="heading" $value="label"></div>
|
|
8
|
+
<ul role="presentation"><slot></slot></ul>
|
|
9
|
+
</div>
|
|
10
|
+
</li>
|
|
11
|
+
<style>
|
|
12
|
+
:host {
|
|
13
|
+
display: block;
|
|
14
|
+
list-style: none;
|
|
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-xs);
|
|
20
|
+
font-weight: var(--ui-font-semibold);
|
|
21
|
+
}
|
|
22
|
+
ul {
|
|
23
|
+
margin: 0;
|
|
24
|
+
padding: 0;
|
|
25
|
+
list-style: none;
|
|
26
|
+
}
|
|
27
|
+
:host[hidden] {
|
|
28
|
+
display: none;
|
|
29
|
+
}
|
|
30
|
+
</style>
|
|
31
|
+
</template>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<link rel="component" href="../ui-icon/ui-icon.html">
|
|
2
|
+
<template component="ui-editor-slash-menu-item">
|
|
3
|
+
<defs>
|
|
4
|
+
<prop name="value" type="string" default="">Stable identifier reported by the containing Slash Menu when this command is chosen.</prop>
|
|
5
|
+
<prop name="icon" type="string" default="">A Lucide icon name for the leading symbol.</prop>
|
|
6
|
+
<prop name="description" type="string" default="">One line explaining the command.</prop>
|
|
7
|
+
<prop name="keywords" type="string" default="">Additional space-separated search terms matched by the containing Slash Menu.</prop>
|
|
8
|
+
<prop name="disabled" type="boolean" default="false">Keeps the command visible but unavailable for selection.</prop>
|
|
9
|
+
</defs>
|
|
10
|
+
<li role="option" tabindex="-1" :aria-disabled="disabled" :data-value="value" :data-keywords="keywords">
|
|
11
|
+
<span $if="icon" class="icon" aria-hidden="true"><ui-icon :name="icon"></ui-icon></span>
|
|
12
|
+
<span class="body"><span class="title"><slot></slot></span><span $if="description" class="description" $value="description"></span></span>
|
|
13
|
+
</li>
|
|
14
|
+
<style>
|
|
15
|
+
:host {
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: var(--ui-space-3);
|
|
19
|
+
min-block-size: 44px;
|
|
20
|
+
padding: var(--ui-space-2) var(--ui-space-3);
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
list-style: none;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
:host[aria-selected="true"] {
|
|
26
|
+
background: var(--ui-accent-subtle);
|
|
27
|
+
}
|
|
28
|
+
:host[aria-disabled="true"] {
|
|
29
|
+
opacity: 0.6;
|
|
30
|
+
cursor: not-allowed;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.icon {
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
inline-size: 32px;
|
|
38
|
+
block-size: 32px;
|
|
39
|
+
flex: 0 0 auto;
|
|
40
|
+
color: var(--ui-text-secondary);
|
|
41
|
+
background: var(--ui-surface-hover);
|
|
42
|
+
border: 1px solid var(--ui-border);
|
|
43
|
+
border-radius: var(--ui-radius-md);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.icon {
|
|
47
|
+
--_ui-default-icon-size: 1rem;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.body {
|
|
51
|
+
display: flex;
|
|
52
|
+
min-inline-size: 0;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
gap: 1px;
|
|
55
|
+
}
|
|
56
|
+
.title, .description {
|
|
57
|
+
overflow: hidden;
|
|
58
|
+
text-overflow: ellipsis;
|
|
59
|
+
white-space: nowrap;
|
|
60
|
+
}
|
|
61
|
+
.title {
|
|
62
|
+
color: var(--ui-text-primary);
|
|
63
|
+
font-size: var(--ui-font-size-sm);
|
|
64
|
+
font-weight: var(--ui-font-medium);
|
|
65
|
+
}
|
|
66
|
+
.description {
|
|
67
|
+
color: var(--ui-text-muted);
|
|
68
|
+
font-size: var(--ui-font-size-xs);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
:host[hidden] {
|
|
72
|
+
display: none;
|
|
73
|
+
}
|
|
74
|
+
</style>
|
|
75
|
+
</template>
|
|
@@ -2,20 +2,23 @@
|
|
|
2
2
|
<template component="ui-editor-table-context-menu" controller="./ui-editor-table-context-menu.js">
|
|
3
3
|
<defs>
|
|
4
4
|
<prop name="open" type="boolean" default="false">Shows the menu at the position owned by the editor integration.</prop>
|
|
5
|
+
<prop name="scope" type="cell | row | column" default="cell">Keeps structure actions relevant to a cell, row handle, or column handle.</prop>
|
|
5
6
|
<prop name="cellBackground" type="string" default="">Identifies the background choice that is active for the current cell.</prop>
|
|
7
|
+
<prop name="headerRow" type="boolean" default="false">Whether the table's first row currently uses header cells.</prop>
|
|
8
|
+
<prop name="headerColumn" type="boolean" default="false">Whether the table's first column currently uses header cells.</prop>
|
|
6
9
|
<prop
|
|
7
10
|
name="actions"
|
|
8
|
-
type="list(align-left | align-center | align-right | background-none | background-gray | background-yellow | background-blue | background-green | background-red | add-row-before | add-row-after | add-column-before | add-column-after | clear-cells | merge-cells | split-cell | delete-row | delete-column | delete-table)"
|
|
11
|
+
type="list(align-left | align-center | align-right | background-none | background-gray | background-yellow | background-blue | background-green | background-red | add-row-before | add-row-after | add-column-before | add-column-after | toggle-header-row | toggle-header-column | move-row-up | move-row-down | move-column-left | move-column-right | clear-cells | merge-cells | split-cell | delete-row | delete-column | delete-table)"
|
|
9
12
|
>The complete set of commands the current selection permits.</prop>
|
|
10
13
|
<state name="swatches" type="list(object({ action: string, label: string, color: string, selected: boolean })) | null" :value="[]"></state>
|
|
11
14
|
<state
|
|
12
15
|
name="sections"
|
|
13
|
-
type="list(object({ heading: string, items: list(object({ action: string, label: string, icon: string, danger: boolean })) }))"
|
|
16
|
+
type="list(object({ heading: string, items: list(object({ action: string, label: string, icon: string, danger: boolean, checkable: boolean, checked: boolean })) }))"
|
|
14
17
|
:value="[]"
|
|
15
18
|
></state>
|
|
16
19
|
<event
|
|
17
20
|
name="action"
|
|
18
|
-
type="object({ action: align-left | align-center | align-right | background-none | background-gray | background-yellow | background-blue | background-green | background-red | add-row-before | add-row-after | add-column-before | add-column-after | clear-cells | merge-cells | split-cell | delete-row | delete-column | delete-table })"
|
|
21
|
+
type="object({ action: align-left | align-center | align-right | background-none | background-gray | background-yellow | background-blue | background-green | background-red | add-row-before | add-row-after | add-column-before | add-column-after | toggle-header-row | toggle-header-column | move-row-up | move-row-down | move-column-left | move-column-right | clear-cells | merge-cells | split-cell | delete-row | delete-column | delete-table })"
|
|
19
22
|
></event>
|
|
20
23
|
</defs>
|
|
21
24
|
<div role="menu" :hidden="not open">
|
|
@@ -41,7 +44,15 @@
|
|
|
41
44
|
<div class="sep"></div>
|
|
42
45
|
<div class="section" role="none">
|
|
43
46
|
<div class="heading" role="presentation" $value="section.heading"></div>
|
|
44
|
-
<button
|
|
47
|
+
<button
|
|
48
|
+
$each="item of section.items"
|
|
49
|
+
$key="item.action"
|
|
50
|
+
type="button"
|
|
51
|
+
:role="{ true: 'menuitemcheckbox', false: 'menuitem' }[format('%s', item.checkable)]"
|
|
52
|
+
:aria-checked="{ true: item.checked, false: null }[format('%s', item.checkable)]"
|
|
53
|
+
class:danger="item.danger"
|
|
54
|
+
:data-action="item.action"
|
|
55
|
+
>
|
|
45
56
|
<ui-icon :name="item.icon"></ui-icon>
|
|
46
57
|
<span $value="item.label"></span>
|
|
47
58
|
</button>
|
|
@@ -123,7 +134,7 @@
|
|
|
123
134
|
display: inline-flex;
|
|
124
135
|
width: 1rem;
|
|
125
136
|
height: 1rem;
|
|
126
|
-
border-radius:
|
|
137
|
+
border-radius: var(--ui-radius-round);
|
|
127
138
|
border: 1px solid var(--ui-border);
|
|
128
139
|
background: var(--_swatch, #fff);
|
|
129
140
|
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.35);
|
|
@@ -6,6 +6,12 @@ const sections = [
|
|
|
6
6
|
["add-row-after", "Add row below", "panel-bottom"],
|
|
7
7
|
["add-column-before", "Add column left", "panel-left"],
|
|
8
8
|
["add-column-after", "Add column right", "panel-right"],
|
|
9
|
+
["toggle-header-row", "Header row", "rows"],
|
|
10
|
+
["toggle-header-column", "Header column", "columns"],
|
|
11
|
+
["move-row-up", "Move row up", "rows"],
|
|
12
|
+
["move-row-down", "Move row down", "rows"],
|
|
13
|
+
["move-column-left", "Move column left", "columns"],
|
|
14
|
+
["move-column-right", "Move column right", "columns"],
|
|
9
15
|
]],
|
|
10
16
|
["Cells", [
|
|
11
17
|
["clear-cells", "Clear selected cells", "eraser"],
|
|
@@ -42,7 +48,9 @@ export default function controller(host) {
|
|
|
42
48
|
frame = requestAnimationFrame(nudge);
|
|
43
49
|
};
|
|
44
50
|
const stop = host.effect(() => {
|
|
45
|
-
const
|
|
51
|
+
const scope = String(host.state.scope || "cell");
|
|
52
|
+
const enabled = new Set((Array.isArray(host.state.actions) ? host.state.actions : []).filter((action) =>
|
|
53
|
+
scope === "cell" || (scope === "row" ? !action.includes("column") : !action.includes("row"))));
|
|
46
54
|
const background = String(host.state.cellBackground ?? "");
|
|
47
55
|
const swatches = backgrounds.filter(([action]) => enabled.has(action))
|
|
48
56
|
.map(([action, label, color]) => ({ action, label, color, selected: color === background }));
|
|
@@ -51,7 +59,7 @@ export default function controller(host) {
|
|
|
51
59
|
.map(([heading, items]) => ({
|
|
52
60
|
heading,
|
|
53
61
|
items: items.filter(([action]) => enabled.has(action))
|
|
54
|
-
.map(([action, label, icon, tone]) => ({ action, label, icon, danger: tone === "danger" })),
|
|
62
|
+
.map(([action, label, icon, tone]) => ({ action, label, icon, danger: tone === "danger", checkable: action.startsWith("toggle-header-"), checked: action === "toggle-header-row" ? Boolean(host.state.headerRow) : action === "toggle-header-column" ? Boolean(host.state.headerColumn) : false })),
|
|
55
63
|
}))
|
|
56
64
|
.filter((section) => section.items.length);
|
|
57
65
|
if (host.state.open) schedule();
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<state name="activeKey" :value="''"></state>
|
|
30
30
|
<event
|
|
31
31
|
name="action"
|
|
32
|
-
type="object({ action: add-row-before | add-row-after | add-column-before | add-column-after, boundaryIndex: integer }) | object({ action: select-row | select-column, rowIndex: integer, columnIndex: integer }) | object({ action: open-cell-menu, rowIndex: integer, columnIndex: integer, anchor: object({ left: number, top: number, right: number, bottom: number }) })"
|
|
32
|
+
type="object({ action: add-row-before | add-row-after | add-column-before | add-column-after, boundaryIndex: integer }) | object({ action: select-row | select-column, rowIndex: integer, columnIndex: integer }) | object({ action: open-cell-menu | open-row-menu | open-column-menu, rowIndex: integer, columnIndex: integer, anchor: object({ left: number, top: number, right: number, bottom: number }) })"
|
|
33
33
|
></event>
|
|
34
34
|
</defs>
|
|
35
35
|
<div aria-label="Table controls" :hidden="not open">
|
|
@@ -89,8 +89,8 @@
|
|
|
89
89
|
data-action="select-row"
|
|
90
90
|
:data-row-index="hovered.rowIndex"
|
|
91
91
|
:data-column-index="hovered.columnIndex"
|
|
92
|
-
title="
|
|
93
|
-
aria-label="
|
|
92
|
+
title="Row actions"
|
|
93
|
+
aria-label="Row actions"
|
|
94
94
|
>
|
|
95
95
|
<span class="glyph"><ui-icon name="grip-vertical"></ui-icon></span>
|
|
96
96
|
</button>
|
|
@@ -101,8 +101,8 @@
|
|
|
101
101
|
data-action="select-column"
|
|
102
102
|
:data-row-index="hovered.rowIndex"
|
|
103
103
|
:data-column-index="hovered.columnIndex"
|
|
104
|
-
title="
|
|
105
|
-
aria-label="
|
|
104
|
+
title="Column actions"
|
|
105
|
+
aria-label="Column actions"
|
|
106
106
|
>
|
|
107
107
|
<span class="glyph"><ui-icon name="grip-horizontal"></ui-icon></span>
|
|
108
108
|
</button>
|
|
@@ -93,8 +93,11 @@ export default function controller(host) {
|
|
|
93
93
|
if (!button) return;
|
|
94
94
|
const action = button.dataset.action;
|
|
95
95
|
const cell = { rowIndex: Number(button.dataset.rowIndex), columnIndex: Number(button.dataset.columnIndex) };
|
|
96
|
-
if (action === "select-row" || action === "select-column")
|
|
97
|
-
|
|
96
|
+
if (action === "select-row" || action === "select-column") {
|
|
97
|
+
host.dispatch("action", { action, ...cell });
|
|
98
|
+
const rect = button.getBoundingClientRect();
|
|
99
|
+
host.dispatch("action", { action: action === "select-row" ? "open-row-menu" : "open-column-menu", ...cell, anchor: { left: rect.left, top: rect.top, right: rect.right, bottom: rect.bottom } });
|
|
100
|
+
} else if (action === "open-cell-menu") {
|
|
98
101
|
const rect = button.getBoundingClientRect();
|
|
99
102
|
host.dispatch("action", { action, ...cell, anchor: { left: rect.left, top: rect.top, right: rect.right, bottom: rect.bottom } });
|
|
100
103
|
} else host.dispatch("action", { action, boundaryIndex: Number(button.dataset.boundaryIndex) });
|
|
@@ -4,23 +4,25 @@
|
|
|
4
4
|
<prop name="open" type="boolean" default="false">Shows the toolbar while a table selection is active.</prop>
|
|
5
5
|
<prop name="cellAlignment" type="left | center | right" default="left">Marks the alignment command that is active for the current cell.</prop>
|
|
6
6
|
<prop name="cellBackground" type="string" default="">Identifies the background choice that is active for the current cell.</prop>
|
|
7
|
+
<prop name="headerRow" type="boolean" default="false">Whether the table's first row currently uses header cells.</prop>
|
|
8
|
+
<prop name="headerColumn" type="boolean" default="false">Whether the table's first column currently uses header cells.</prop>
|
|
7
9
|
<prop
|
|
8
10
|
name="actions"
|
|
9
|
-
type="list(align-left | align-center | align-right | background-none | background-gray | background-yellow | background-blue | background-green | background-red | add-row-before | add-row-after | add-column-before | add-column-after | clear-cells | merge-cells | split-cell | delete-row | delete-column | delete-table)"
|
|
11
|
+
type="list(align-left | align-center | align-right | background-none | background-gray | background-yellow | background-blue | background-green | background-red | add-row-before | add-row-after | add-column-before | add-column-after | toggle-header-row | toggle-header-column | move-row-up | move-row-down | move-column-left | move-column-right | clear-cells | merge-cells | split-cell | delete-row | delete-column | delete-table)"
|
|
10
12
|
>The complete set of commands the current selection permits.</prop>
|
|
11
13
|
<state name="alignments" type="list(object({ action: align-left | align-center | align-right, label: string, active: boolean }))" :value="[]"></state>
|
|
12
14
|
<state name="structure" type="list(object({ action: add-row-after | add-column-after, label: string, icon: string }))" :value="[]"></state>
|
|
13
15
|
<state name="swatches" type="list(object({ action: string, label: string, color: string, selected: boolean }))" :value="[]"></state>
|
|
14
16
|
<state
|
|
15
17
|
name="sections"
|
|
16
|
-
type="list(object({ heading: string, items: list(object({ action: string, label: string, icon: string, danger: boolean })) }))"
|
|
18
|
+
type="list(object({ heading: string, items: list(object({ action: string, label: string, icon: string, danger: boolean, checkable: boolean, checked: boolean })) }))"
|
|
17
19
|
:value="[]"
|
|
18
20
|
></state>
|
|
19
21
|
<state name="hasOverflow" :value="false"></state>
|
|
20
22
|
<state name="overflowOpen" :value="false"></state>
|
|
21
23
|
<event
|
|
22
24
|
name="action"
|
|
23
|
-
type="object({ action: align-left | align-center | align-right | background-none | background-gray | background-yellow | background-blue | background-green | background-red | add-row-before | add-row-after | add-column-before | add-column-after | clear-cells | merge-cells | split-cell | delete-row | delete-column | delete-table })"
|
|
25
|
+
type="object({ action: align-left | align-center | align-right | background-none | background-gray | background-yellow | background-blue | background-green | background-red | add-row-before | add-row-after | add-column-before | add-column-after | toggle-header-row | toggle-header-column | move-row-up | move-row-down | move-column-left | move-column-right | clear-cells | merge-cells | split-cell | delete-row | delete-column | delete-table })"
|
|
24
26
|
></event>
|
|
25
27
|
</defs>
|
|
26
28
|
<div role="toolbar" aria-label="Table actions" :hidden="not open">
|
|
@@ -74,7 +76,15 @@
|
|
|
74
76
|
</div>
|
|
75
77
|
<div $each="section of sections" $key="section.heading" class="menu-section divided" role="none">
|
|
76
78
|
<div class="menu-heading" role="presentation" $value="section.heading"></div>
|
|
77
|
-
<button
|
|
79
|
+
<button
|
|
80
|
+
$each="item of section.items"
|
|
81
|
+
$key="item.action"
|
|
82
|
+
type="button"
|
|
83
|
+
:role="{ true: 'menuitemcheckbox', false: 'menuitem' }[format('%s', item.checkable)]"
|
|
84
|
+
:aria-checked="{ true: item.checked, false: null }[format('%s', item.checkable)]"
|
|
85
|
+
class:danger="item.danger"
|
|
86
|
+
:data-action="item.action"
|
|
87
|
+
>
|
|
78
88
|
<ui-icon :name="item.icon"></ui-icon>
|
|
79
89
|
<span $value="item.label"></span>
|
|
80
90
|
</button>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { backgrounds } from "../shared/editor.js";
|
|
2
2
|
|
|
3
3
|
const overflowSections = [
|
|
4
|
-
["Structure", [["add-row-before", "Add row above", "panel-top"], ["add-column-before", "Add column left", "panel-left"]]],
|
|
4
|
+
["Structure", [["add-row-before", "Add row above", "panel-top"], ["add-column-before", "Add column left", "panel-left"], ["toggle-header-row", "Header row", "rows"], ["toggle-header-column", "Header column", "columns"], ["move-row-up", "Move row up", "rows"], ["move-row-down", "Move row down", "rows"], ["move-column-left", "Move column left", "columns"], ["move-column-right", "Move column right", "columns"]]],
|
|
5
5
|
["Cells", [["clear-cells", "Clear selected cells", "eraser"], ["merge-cells", "Merge selected cells", "merge"], ["split-cell", "Split merged cell", "split"]]],
|
|
6
6
|
["Table", [["delete-row", "Delete row", "trash", "danger"], ["delete-column", "Delete column", "trash", "danger"], ["delete-table", "Delete table", "trash", "danger"]]],
|
|
7
7
|
];
|
|
@@ -25,7 +25,7 @@ export default function controller(host) {
|
|
|
25
25
|
.map(([heading, items]) => ({
|
|
26
26
|
heading,
|
|
27
27
|
items: items.filter(([action]) => enabled.has(action))
|
|
28
|
-
.map(([action, label, icon, tone]) => ({ action, label, icon, danger: tone === "danger" })),
|
|
28
|
+
.map(([action, label, icon, tone]) => ({ action, label, icon, danger: tone === "danger", checkable: action.startsWith("toggle-header-"), checked: action === "toggle-header-row" ? Boolean(host.state.headerRow) : action === "toggle-header-column" ? Boolean(host.state.headerColumn) : false })),
|
|
29
29
|
}))
|
|
30
30
|
.filter((section) => section.items.length);
|
|
31
31
|
host.state.hasOverflow = host.state.swatches.length > 0 || host.state.sections.length > 0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<template component="ui-editor-toolbar">
|
|
1
|
+
<template component="ui-editor-toolbar" controller="./ui-editor-toolbar.js">
|
|
2
2
|
<defs>
|
|
3
3
|
<prop name="floating" type="boolean" default="false">Shows the toolbar as a floating bar beside the selection rather than pinned to the editor.</prop>
|
|
4
4
|
</defs>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const selector = 'button, a[href], [role="button"], select, input:not([type="hidden"])';
|
|
2
|
+
|
|
3
|
+
/** Keep one Tab stop in the toolbar; arrow keys move among its enabled authored controls. */
|
|
4
|
+
export default function controller(host) {
|
|
5
|
+
const strip = host.refs.strip;
|
|
6
|
+
let current = null;
|
|
7
|
+
const controls = () => Array.from(strip.querySelectorAll(selector)).filter((element) =>
|
|
8
|
+
!element.disabled && element.getAttribute("aria-disabled") !== "true"
|
|
9
|
+
&& !element.closest("[hidden], [inert], [role='menu'], [role='listbox']")
|
|
10
|
+
&& element.getClientRects().length > 0);
|
|
11
|
+
const sync = () => {
|
|
12
|
+
const items = controls();
|
|
13
|
+
if (!items.includes(current)) current = items.find((item) => item.tabIndex === 0) ?? items[0] ?? null;
|
|
14
|
+
for (const item of items) item.tabIndex = item === current ? 0 : -1;
|
|
15
|
+
};
|
|
16
|
+
const onFocusin = (event) => {
|
|
17
|
+
const item = event.target.closest?.(selector);
|
|
18
|
+
if (!controls().includes(item)) return;
|
|
19
|
+
current = item;
|
|
20
|
+
sync();
|
|
21
|
+
};
|
|
22
|
+
const onKeydown = (event) => {
|
|
23
|
+
const items = controls();
|
|
24
|
+
const index = items.indexOf(event.target.closest?.(selector));
|
|
25
|
+
if (index < 0) return;
|
|
26
|
+
const rtl = getComputedStyle(strip).direction === "rtl";
|
|
27
|
+
let next;
|
|
28
|
+
if (event.key === "ArrowRight") next = (index + (rtl ? -1 : 1) + items.length) % items.length;
|
|
29
|
+
else if (event.key === "ArrowLeft") next = (index + (rtl ? 1 : -1) + items.length) % items.length;
|
|
30
|
+
else if (event.key === "Home") next = 0;
|
|
31
|
+
else if (event.key === "End") next = items.length - 1;
|
|
32
|
+
else return;
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
items[next]?.focus();
|
|
35
|
+
};
|
|
36
|
+
const observer = new MutationObserver(sync);
|
|
37
|
+
observer.observe(strip, { childList: true, subtree: true, attributes: true, attributeFilter: ["disabled", "hidden", "aria-disabled"] });
|
|
38
|
+
strip.addEventListener("focusin", onFocusin);
|
|
39
|
+
strip.addEventListener("keydown", onKeydown);
|
|
40
|
+
sync();
|
|
41
|
+
return () => {
|
|
42
|
+
observer.disconnect();
|
|
43
|
+
strip.removeEventListener("focusin", onFocusin);
|
|
44
|
+
strip.removeEventListener("keydown", onKeydown);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template component="ui-form-field" controller="./ui-form-field.js">
|
|
2
2
|
<defs>
|
|
3
3
|
<prop name="invalid" type="boolean" default="false"
|
|
4
|
-
>Marks the field's control invalid by setting aria-invalid on it.
|
|
5
|
-
|
|
4
|
+
>Marks the field's control invalid by setting aria-invalid on it. Only while invalid, the error slot appears and becomes part of the control's
|
|
5
|
+
description.</prop>
|
|
6
6
|
<prop name="disabled" type="boolean" default="false"
|
|
7
7
|
>Disables the field's control: the first input, textarea, or select inside it. The field writes this to the control whenever it updates, so set it here
|
|
8
8
|
rather than on the control.</prop>
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
</div>
|
|
19
19
|
<style>
|
|
20
20
|
/* This component's hooks style the element they are set on, and never a nested one. */
|
|
21
|
+
@property --ui-form-field-danger {
|
|
22
|
+
syntax: "*";
|
|
23
|
+
inherits: false;
|
|
24
|
+
}
|
|
21
25
|
@property --ui-form-field-help-min-block-size {
|
|
22
26
|
syntax: "*";
|
|
23
27
|
inherits: false;
|
|
@@ -25,6 +29,7 @@
|
|
|
25
29
|
|
|
26
30
|
:host {
|
|
27
31
|
/* Hooks do not inherit, so the parts below read them from the root. */
|
|
32
|
+
--_ui-form-field-danger: var(--ui-form-field-danger);
|
|
28
33
|
--_ui-form-field-help-min-block-size: var(--ui-form-field-help-min-block-size);
|
|
29
34
|
|
|
30
35
|
display: grid;
|
|
@@ -57,10 +62,18 @@
|
|
|
57
62
|
min-block-size: var(--_ui-form-field-help-min-block-size, 0);
|
|
58
63
|
}
|
|
59
64
|
|
|
65
|
+
.error {
|
|
66
|
+
display: none;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
:host-state([invalid]) .error {
|
|
70
|
+
display: contents;
|
|
71
|
+
}
|
|
72
|
+
|
|
60
73
|
/* An error reads in the danger colour, the one the invalid control's border takes. */
|
|
61
74
|
.error,
|
|
62
75
|
:slotted([role="alert"]) {
|
|
63
|
-
color: var(--ui-danger);
|
|
76
|
+
color: var(--_ui-form-field-danger, var(--ui-field-danger, var(--ui-danger)));
|
|
64
77
|
font-size: var(--ui-font-size-sm);
|
|
65
78
|
}
|
|
66
79
|
|
|
@@ -44,7 +44,7 @@ export default function controller(host) {
|
|
|
44
44
|
if (!help.id) help.id = id("form-field-help");
|
|
45
45
|
described.push(help.id);
|
|
46
46
|
}
|
|
47
|
-
if (error) {
|
|
47
|
+
if (error && host.state.invalid) {
|
|
48
48
|
if (!error.id) error.id = id("form-field-error");
|
|
49
49
|
described.push(error.id);
|
|
50
50
|
}
|