@threadlabs/looma 0.28.6 → 0.28.7
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/ui-editor-block-actions/ui-editor-block-actions.html +20 -0
- package/components/ui-editor-block-actions/ui-editor-block-actions.js +14 -0
- package/components/ui-editor-image-toolbar/ui-editor-image-toolbar.html +7 -6
- package/components/ui-editor-image-toolbar/ui-editor-image-toolbar.js +2 -1
- package/components/ui-editor-link-card/ui-editor-link-card.html +57 -0
- package/components/ui-editor-link-card/ui-editor-link-card.js +27 -0
- package/dist/index.js +36 -34
- package/package.json +1 -1
- package/styles/ui-editor-block-actions.css +1 -0
- package/styles/ui-editor-link-card.css +1 -0
- package/vanilla/UiEditorBlockActions.d.ts +16 -0
- package/vanilla/UiEditorBlockActions.js +41 -0
- package/vanilla/UiEditorImageToolbar.js +1 -1
- package/vanilla/UiEditorLinkCard.d.ts +20 -0
- package/vanilla/UiEditorLinkCard.js +153 -0
- package/vanilla/index.js +2 -0
- package/vue/UiEditorBlockActions.d.ts +33 -0
- package/vue/UiEditorBlockActions.js +2 -0
- package/vue/UiEditorBlockActions.vue +125 -0
- package/vue/UiEditorImageToolbar.vue +5 -6
- package/vue/UiEditorLinkCard.d.ts +58 -0
- package/vue/UiEditorLinkCard.js +2 -0
- package/vue/UiEditorLinkCard.vue +319 -0
- package/vue/chunks/UiAffordanceScope.js +1 -1
- package/vue/chunks/UiAvatar.js +1 -1
- package/vue/chunks/UiAvatarGroup.js +1 -1
- package/vue/chunks/UiBadge.js +1 -1
- package/vue/chunks/UiBreadcrumbItem.js +1 -1
- package/vue/chunks/UiBreadcrumbs.js +1 -1
- package/vue/chunks/UiButton.js +1 -1
- package/vue/chunks/UiCallout.js +1 -1
- package/vue/chunks/UiCard.js +1 -1
- package/vue/chunks/UiCheckbox.js +1 -1
- package/vue/chunks/UiCluster.js +1 -1
- package/vue/chunks/UiCombobox.js +1 -1
- package/vue/chunks/UiContainer.js +1 -1
- package/vue/chunks/UiContextMenu.js +1 -1
- package/vue/chunks/UiCover.js +1 -1
- package/vue/chunks/UiDescriptionItem.js +1 -1
- package/vue/chunks/UiDescriptionList.js +1 -1
- package/vue/chunks/UiDialog.js +1 -1
- package/vue/chunks/UiDisclosure.js +1 -1
- package/vue/chunks/UiEditable.js +1 -1
- package/vue/chunks/UiEditorBlockActions.js +127 -0
- package/vue/chunks/UiEditorImageToolbar.js +7 -25
- package/vue/chunks/UiEditorInsertTableGrid.js +1 -1
- package/vue/chunks/UiEditorLinkCard.js +356 -0
- package/vue/chunks/UiEditorMentionMenu.js +1 -1
- package/vue/chunks/UiEditorMentionMenuItem.js +1 -1
- package/vue/chunks/UiEditorSlashMenu.js +1 -1
- package/vue/chunks/UiEditorSlashMenuGroup.js +1 -1
- package/vue/chunks/UiEditorSlashMenuItem.js +1 -1
- package/vue/chunks/UiEditorSurface.js +1 -1
- package/vue/chunks/UiEditorTableContextMenu.js +1 -1
- package/vue/chunks/UiEditorTableOverlay.js +1 -1
- package/vue/chunks/UiEditorTableToolbar.js +1 -1
- package/vue/chunks/UiEditorToolbar.js +1 -1
- package/vue/chunks/UiFloatingActionButton.js +1 -1
- package/vue/chunks/UiFormField.js +1 -1
- package/vue/chunks/UiGrid.js +1 -1
- package/vue/chunks/UiIcon.js +1 -1
- package/vue/chunks/UiIconButton.js +1 -1
- package/vue/chunks/UiImage.js +1 -1
- package/vue/chunks/UiInput.js +1 -1
- package/vue/chunks/UiInputGroup.js +1 -1
- package/vue/chunks/UiList.js +1 -1
- package/vue/chunks/UiListItem.js +1 -1
- package/vue/chunks/UiListbox.js +1 -1
- package/vue/chunks/UiMenu.js +1 -1
- package/vue/chunks/UiMenuGroup.js +1 -1
- package/vue/chunks/UiMenuItem.js +1 -1
- package/vue/chunks/UiMeter.js +1 -1
- package/vue/chunks/UiNavItem.js +1 -1
- package/vue/chunks/UiPageHeader.js +1 -1
- package/vue/chunks/UiPopover.js +1 -1
- package/vue/chunks/UiRadio.js +1 -1
- package/vue/chunks/UiRadioGroup.js +1 -1
- package/vue/chunks/UiReel.js +1 -1
- package/vue/chunks/UiScrollArea.js +1 -1
- package/vue/chunks/UiSearchResultRow.js +1 -1
- package/vue/chunks/UiSearchShell.js +1 -1
- package/vue/chunks/UiSection.js +1 -1
- package/vue/chunks/UiSelect.js +1 -1
- package/vue/chunks/UiSeparator.js +1 -1
- package/vue/chunks/UiSidebar.js +1 -1
- package/vue/chunks/UiSpinner.js +1 -1
- package/vue/chunks/UiStack.js +1 -1
- package/vue/chunks/UiStatusMessage.js +1 -1
- package/vue/chunks/UiSwitch.js +1 -1
- package/vue/chunks/UiSwitcher.js +1 -1
- package/vue/chunks/UiTable.js +1 -1
- package/vue/chunks/UiTabs.js +1 -1
- package/vue/chunks/UiText.js +1 -1
- package/vue/chunks/UiTextarea.js +1 -1
- package/vue/chunks/UiToast.js +1 -1
- package/vue/chunks/UiToastRegion.js +1 -1
- package/vue/chunks/UiTooltip.js +1 -1
- package/vue/chunks/UiTree.js +1 -1
- package/vue/chunks/UiTreeItem.js +1 -1
- package/vue/chunks/_plugin-vue_export-helper.js +1 -1
- package/vue/components.css +1176 -1176
- package/vue/editor/index.js +29 -94
- package/vue/editor/primitives.d.ts +8 -2
- package/vue/index.d.ts +2 -0
- package/vue/index.js +5 -3
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<link rel="component" href="../ui-menu/ui-menu.html">
|
|
2
|
+
<link rel="component" href="../ui-menu-item/ui-menu-item.html">
|
|
3
|
+
<template component="ui-editor-block-actions" controller="./ui-editor-block-actions.js">
|
|
4
|
+
<defs>
|
|
5
|
+
<prop name="for" type="string" default="">The id of the button that opens the menu and anchors it.</prop>
|
|
6
|
+
<prop name="placement" type="keyword" values="bottom-start, top-start" default="bottom-start"
|
|
7
|
+
>Where the menu opens beside its button; top-start suits a toolbar docked at the bottom of the screen.</prop>
|
|
8
|
+
<event name="action" type="object">
|
|
9
|
+
<prop name="action" required type="keyword" values="insert-below, duplicate, delete"></prop>
|
|
10
|
+
Requests an action on the block that holds the caret.
|
|
11
|
+
</event>
|
|
12
|
+
</defs>
|
|
13
|
+
<div>
|
|
14
|
+
<ui-menu from:for="$for" from:placement="$placement" aria-label="Block actions">
|
|
15
|
+
<ui-menu-item value="insert-below">Insert paragraph below</ui-menu-item>
|
|
16
|
+
<ui-menu-item value="duplicate">Duplicate block</ui-menu-item>
|
|
17
|
+
<ui-menu-item value="delete">Delete block</ui-menu-item>
|
|
18
|
+
</ui-menu>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Reports the menu's choice as a block action.
|
|
2
|
+
function connect(host) {
|
|
3
|
+
const element = host.element;
|
|
4
|
+
const onSelect = (event) => {
|
|
5
|
+
if (event.target !== element && element.contains(event.target)) host.dispatch("action", { action: event.detail.value });
|
|
6
|
+
};
|
|
7
|
+
element.addEventListener("select", onSelect);
|
|
8
|
+
return () => element.removeEventListener("select", onSelect);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Keep DOM setup and its cleanup tied to each connection, including reconnects. */
|
|
12
|
+
export default function controller(host) {
|
|
13
|
+
host.on("connect", () => connect(host));
|
|
14
|
+
}
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
</defs>
|
|
32
32
|
<div>
|
|
33
33
|
<ui-text as="div" font="sans">
|
|
34
|
+
<!-- from:floating="true": a bare boolean converts to floating="" in Vue (HTML Next gap 2). data-image-*: the root reflects
|
|
35
|
+
props as data-<prop>, so data-placement would collide (gap 4). html-next #236 fixes both for the next alpha. -->
|
|
34
36
|
<ui-editor-toolbar from:floating="true" aria-label="Image actions">
|
|
35
37
|
<ui-icon-button
|
|
36
38
|
from:id="concat($base, '-block')"
|
|
@@ -39,7 +41,7 @@
|
|
|
39
41
|
data-image-placement="block"
|
|
40
42
|
from:variant="{ true: 'outline', false: 'ghost' }[concat($placement = 'block')]"
|
|
41
43
|
from:aria-pressed="concat($placement = 'block')"
|
|
42
|
-
><ui-icon name="panel-top"
|
|
44
|
+
><ui-icon name="panel-top"></ui-icon></ui-icon-button>
|
|
43
45
|
<ui-icon-button
|
|
44
46
|
from:id="concat($base, '-center')"
|
|
45
47
|
label="Center image"
|
|
@@ -47,7 +49,7 @@
|
|
|
47
49
|
data-image-placement="center"
|
|
48
50
|
from:variant="{ true: 'outline', false: 'ghost' }[concat($placement = 'center')]"
|
|
49
51
|
from:aria-pressed="concat($placement = 'center')"
|
|
50
|
-
><ui-icon name="align-center"
|
|
52
|
+
><ui-icon name="align-center"></ui-icon></ui-icon-button>
|
|
51
53
|
<ui-icon-button
|
|
52
54
|
from:id="concat($base, '-wrap-left')"
|
|
53
55
|
label="Wrap text to the right"
|
|
@@ -55,7 +57,7 @@
|
|
|
55
57
|
data-image-placement="wrap-left"
|
|
56
58
|
from:variant="{ true: 'outline', false: 'ghost' }[concat($placement = 'wrap-left')]"
|
|
57
59
|
from:aria-pressed="concat($placement = 'wrap-left')"
|
|
58
|
-
><ui-icon name="panel-left"
|
|
60
|
+
><ui-icon name="panel-left"></ui-icon></ui-icon-button>
|
|
59
61
|
<ui-icon-button
|
|
60
62
|
from:id="concat($base, '-wrap-right')"
|
|
61
63
|
label="Wrap text to the left"
|
|
@@ -63,11 +65,10 @@
|
|
|
63
65
|
data-image-placement="wrap-right"
|
|
64
66
|
from:variant="{ true: 'outline', false: 'ghost' }[concat($placement = 'wrap-right')]"
|
|
65
67
|
from:aria-pressed="concat($placement = 'wrap-right')"
|
|
66
|
-
><ui-icon name="panel-right"
|
|
67
|
-
<ui-icon-button from:id="$base" label="Image description" size="sm" variant="ghost"><ui-icon name="file-text"
|
|
68
|
+
><ui-icon name="panel-right"></ui-icon></ui-icon-button>
|
|
69
|
+
<ui-icon-button from:id="$base" label="Image description" size="sm" variant="ghost"><ui-icon name="file-text"></ui-icon></ui-icon-button>
|
|
68
70
|
<ui-icon-button from:id="concat($base, '-delete')" label="Delete image" size="sm" variant="ghost" data-image-action="delete"><ui-icon
|
|
69
71
|
name="trash"
|
|
70
|
-
size="sm"
|
|
71
72
|
></ui-icon></ui-icon-button>
|
|
72
73
|
</ui-editor-toolbar>
|
|
73
74
|
<ui-tooltip from:for="concat($base, '-block')">Image in line</ui-tooltip>
|
|
@@ -4,7 +4,8 @@ function connect(host) {
|
|
|
4
4
|
const element = host.element;
|
|
5
5
|
if (host.state.base === "ui-editor-image-toolbar") host.state.base = `ui-editor-image-toolbar-${Math.random().toString(36).slice(2, 11)}`;
|
|
6
6
|
let target = null;
|
|
7
|
-
// Marked by attribute: the converted Vue component's host.refs has no entry for a nested component
|
|
7
|
+
// Marked by attribute: the converted Vue component's host.refs has no entry for a nested component
|
|
8
|
+
// (HTML Next gap 1, fixed by html-next #236 for the next alpha).
|
|
8
9
|
const isDescription = (event) => event.target.hasAttribute?.("data-image-description") && element.contains(event.target);
|
|
9
10
|
const onClick = (event) => {
|
|
10
11
|
const control = event.target.closest?.("[data-image-placement], [data-image-action]");
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<link rel="component" href="../ui-card/ui-card.html">
|
|
2
|
+
<link rel="component" href="../ui-cluster/ui-cluster.html">
|
|
3
|
+
<link rel="component" href="../ui-stack/ui-stack.html">
|
|
4
|
+
<link rel="component" href="../ui-text/ui-text.html">
|
|
5
|
+
<link rel="component" href="../ui-icon/ui-icon.html">
|
|
6
|
+
<link rel="component" href="../ui-button/ui-button.html">
|
|
7
|
+
<link rel="component" href="../ui-icon-button/ui-icon-button.html">
|
|
8
|
+
<link rel="component" href="../ui-tooltip/ui-tooltip.html">
|
|
9
|
+
<template component="ui-editor-link-card" controller="./ui-editor-link-card.js">
|
|
10
|
+
<defs>
|
|
11
|
+
<prop name="href" type="string" default="">The link's destination, shown when there is no label.</prop>
|
|
12
|
+
<prop name="openHref" type="string" default=""
|
|
13
|
+
>The address Open link opens in a new tab; without it the card offers no Open link. The editor passes the destination only when it is an http, https,
|
|
14
|
+
mailto, tel, or relative address.</prop>
|
|
15
|
+
<prop name="label" type="string" default="">The resolved destination's name, such as a page title, shown in place of the address.</prop>
|
|
16
|
+
<prop name="detail" type="string" default="">A second line under the label, such as where the destination lives.</prop>
|
|
17
|
+
<prop name="targetLabel" type="string" default="Item">What a resolved destination is called, as in "Page: Guide" for a screen reader.</prop>
|
|
18
|
+
<state name="base" type="string" value="ui-editor-link-card"></state>
|
|
19
|
+
<computed name="resolved" from="$label != ''"></computed>
|
|
20
|
+
<computed name="shown" from="{ true: $label, false: $href }[concat($resolved)]"></computed>
|
|
21
|
+
<event name="edit" type="object">Requests the link editor for this link.</event>
|
|
22
|
+
<event name="remove" type="object">Requests removing this link.</event>
|
|
23
|
+
</defs>
|
|
24
|
+
<div>
|
|
25
|
+
<ui-card variant="elevated" padding="xs" size="content">
|
|
26
|
+
<ui-cluster gap="xs" wrap="nowrap" role="group" aria-label="Link actions">
|
|
27
|
+
<ui-icon from:name="{ true: 'file-text', false: 'link' }[concat($resolved)]"></ui-icon>
|
|
28
|
+
<ui-stack gap="none" from:title="$href" from:aria-label="concat({ true: $targetLabel, false: 'URL' }[concat($resolved)], ': ', $shown)">
|
|
29
|
+
<ui-text as="strong" size="sm" weight="medium" font="sans" wrap="anywhere">{$shown}</ui-text>
|
|
30
|
+
<ui-text $if="$detail != ''" as="small" size="xs" tone="secondary" font="sans">{$detail}</ui-text>
|
|
31
|
+
</ui-stack>
|
|
32
|
+
<template $if="$openHref != ''">
|
|
33
|
+
<ui-button
|
|
34
|
+
from:id="concat($base, '-open')"
|
|
35
|
+
as="a"
|
|
36
|
+
variant="ghost"
|
|
37
|
+
tone="neutral"
|
|
38
|
+
size="sm"
|
|
39
|
+
from:href="$openHref"
|
|
40
|
+
target="_blank"
|
|
41
|
+
rel="noopener noreferrer"
|
|
42
|
+
aria-label="Open link"
|
|
43
|
+
><ui-cluster gap="xs" wrap="nowrap"><ui-icon name="external-link"></ui-icon>Open link</ui-cluster></ui-button>
|
|
44
|
+
<ui-tooltip from:for="concat($base, '-open')">Open link</ui-tooltip>
|
|
45
|
+
</template>
|
|
46
|
+
<ui-icon-button from:id="concat($base, '-edit')" label="Edit link" variant="ghost" size="sm" data-link-action="edit"><ui-icon
|
|
47
|
+
name="pencil"
|
|
48
|
+
></ui-icon></ui-icon-button>
|
|
49
|
+
<ui-tooltip from:for="concat($base, '-edit')">Edit link</ui-tooltip>
|
|
50
|
+
<ui-icon-button from:id="concat($base, '-remove')" label="Remove link" variant="ghost" size="sm" data-link-action="remove"><ui-icon
|
|
51
|
+
name="unlink"
|
|
52
|
+
></ui-icon></ui-icon-button>
|
|
53
|
+
<ui-tooltip from:for="concat($base, '-remove')">Remove link</ui-tooltip>
|
|
54
|
+
</ui-cluster>
|
|
55
|
+
</ui-card>
|
|
56
|
+
</div>
|
|
57
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Gives each card its own tooltip ids and reports edit and remove requests without taking focus
|
|
2
|
+
// from the editor.
|
|
3
|
+
function connect(host) {
|
|
4
|
+
const element = host.element;
|
|
5
|
+
if (host.state.base === "ui-editor-link-card") host.state.base = `ui-editor-link-card-${Math.random().toString(36).slice(2, 11)}`;
|
|
6
|
+
const action = (event) => {
|
|
7
|
+
const control = event.target.closest?.("[data-link-action]");
|
|
8
|
+
return control && element.contains(control) ? control.dataset.linkAction : null;
|
|
9
|
+
};
|
|
10
|
+
// Keep the editor's selection while a command is pressed.
|
|
11
|
+
const onPointerDown = (event) => { if (action(event)) event.preventDefault(); };
|
|
12
|
+
const onClick = (event) => {
|
|
13
|
+
const name = action(event);
|
|
14
|
+
if (name) host.dispatch(name, {});
|
|
15
|
+
};
|
|
16
|
+
element.addEventListener("pointerdown", onPointerDown);
|
|
17
|
+
element.addEventListener("click", onClick);
|
|
18
|
+
return () => {
|
|
19
|
+
element.removeEventListener("pointerdown", onPointerDown);
|
|
20
|
+
element.removeEventListener("click", onClick);
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Keep DOM setup and its cleanup tied to each connection, including reconnects. */
|
|
25
|
+
export default function controller(host) {
|
|
26
|
+
host.on("connect", () => connect(host));
|
|
27
|
+
}
|