@jxsuite/studio 0.28.2 → 0.28.4
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/dist/studio.js +86144 -83971
- package/dist/studio.js.map +229 -219
- package/package.json +5 -5
- package/src/browse/browse-modal.ts +9 -3
- package/src/browse/browse.ts +139 -82
- package/src/canvas/canvas-diff.ts +35 -18
- package/src/canvas/canvas-helpers.ts +55 -21
- package/src/canvas/canvas-live-render.ts +140 -89
- package/src/canvas/canvas-render.ts +127 -82
- package/src/canvas/canvas-utils.ts +68 -42
- package/src/canvas/nested-site-style.ts +15 -7
- package/src/editor/component-inline-edit.ts +90 -51
- package/src/editor/content-inline-edit.ts +75 -72
- package/src/editor/context-menu.ts +125 -71
- package/src/editor/convert-targets.ts +17 -13
- package/src/editor/convert-to-component.ts +51 -28
- package/src/editor/convert-to-repeater.ts +44 -19
- package/src/editor/inline-edit.ts +107 -52
- package/src/editor/inline-format.ts +128 -60
- package/src/editor/insertion-helper.ts +26 -14
- package/src/editor/shortcuts.ts +89 -43
- package/src/editor/slash-menu.ts +41 -26
- package/src/files/components.ts +9 -7
- package/src/files/file-ops.ts +53 -33
- package/src/files/files.ts +246 -143
- package/src/format/constraints.ts +25 -15
- package/src/format/format-host.ts +33 -10
- package/src/github/github-auth.ts +24 -14
- package/src/github/github-publish.ts +20 -14
- package/src/new-project/new-project-modal.ts +27 -18
- package/src/panels/activity-bar.ts +27 -26
- package/src/panels/ai-panel.ts +100 -44
- package/src/panels/block-action-bar.ts +84 -52
- package/src/panels/canvas-dnd.ts +47 -28
- package/src/panels/data-explorer.ts +24 -11
- package/src/panels/dnd.ts +148 -100
- package/src/panels/editors.ts +54 -37
- package/src/panels/elements-panel.ts +23 -13
- package/src/panels/events-panel.ts +48 -38
- package/src/panels/git-panel.ts +95 -60
- package/src/panels/head-panel.ts +138 -84
- package/src/panels/imports-panel.ts +66 -29
- package/src/panels/layers-panel.ts +80 -36
- package/src/panels/left-panel.ts +86 -53
- package/src/panels/overlays.ts +36 -19
- package/src/panels/panel-events.ts +48 -27
- package/src/panels/panel-scheduler.ts +41 -23
- package/src/panels/preview-render.ts +26 -26
- package/src/panels/properties-panel.ts +155 -118
- package/src/panels/pseudo-preview.ts +31 -15
- package/src/panels/quick-search.ts +28 -13
- package/src/panels/right-panel.ts +35 -24
- package/src/panels/shared.ts +46 -29
- package/src/panels/signals-panel.ts +303 -186
- package/src/panels/statusbar.ts +27 -12
- package/src/panels/style-inputs.ts +30 -24
- package/src/panels/style-panel.ts +130 -90
- package/src/panels/style-utils.ts +63 -23
- package/src/panels/stylebook-layers-panel.ts +27 -30
- package/src/panels/stylebook-panel.ts +140 -72
- package/src/panels/tab-strip.ts +21 -10
- package/src/panels/toolbar.ts +52 -40
- package/src/panels/welcome-screen.ts +1 -1
- package/src/platform.ts +2 -1
- package/src/platforms/devserver.ts +221 -105
- package/src/recent-projects.ts +15 -7
- package/src/resize-edges.ts +7 -5
- package/src/services/cem-export.ts +73 -57
- package/src/services/code-services.ts +52 -25
- package/src/services/monaco-setup.ts +5 -5
- package/src/settings/content-types-editor.ts +133 -62
- package/src/settings/css-vars-editor.ts +31 -14
- package/src/settings/defs-editor.ts +125 -63
- package/src/settings/general-settings.ts +11 -6
- package/src/settings/head-editor.ts +30 -15
- package/src/settings/schema-field-ui.ts +75 -35
- package/src/settings/settings-modal.ts +35 -17
- package/src/site-context.ts +80 -47
- package/src/state.ts +98 -65
- package/src/store.ts +42 -23
- package/src/studio.ts +165 -144
- package/src/tabs/tab.ts +51 -45
- package/src/tabs/transact.ts +230 -109
- package/src/types.ts +60 -51
- package/src/ui/button-group.ts +3 -2
- package/src/ui/color-selector.ts +33 -15
- package/src/ui/expression-editor.ts +139 -86
- package/src/ui/field-input.ts +35 -18
- package/src/ui/field-row.ts +1 -1
- package/src/ui/icons.ts +5 -6
- package/src/ui/layers.ts +33 -26
- package/src/ui/media-picker.ts +13 -7
- package/src/ui/panel-resize.ts +39 -20
- package/src/ui/spectrum.ts +4 -2
- package/src/ui/unit-selector.ts +20 -11
- package/src/ui/value-selector.ts +13 -7
- package/src/ui/widgets.ts +34 -18
- package/src/utils/canvas-media.ts +66 -33
- package/src/utils/edit-display.ts +43 -44
- package/src/utils/google-fonts.ts +10 -6
- package/src/utils/inherited-style.ts +22 -8
- package/src/utils/studio-utils.ts +88 -45
- package/src/view.ts +9 -5
- package/src/workspace/workspace.ts +49 -29
|
@@ -7,25 +7,25 @@ export interface SlashCommand {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const TAG_LABELS = {
|
|
10
|
-
|
|
10
|
+
article: "Article",
|
|
11
|
+
aside: "Aside",
|
|
12
|
+
blockquote: "Blockquote",
|
|
13
|
+
div: "Div",
|
|
14
|
+
footer: "Footer",
|
|
11
15
|
h1: "Heading 1",
|
|
12
16
|
h2: "Heading 2",
|
|
13
17
|
h3: "Heading 3",
|
|
14
18
|
h4: "Heading 4",
|
|
15
19
|
h5: "Heading 5",
|
|
16
20
|
h6: "Heading 6",
|
|
17
|
-
blockquote: "Blockquote",
|
|
18
|
-
div: "Div",
|
|
19
|
-
section: "Section",
|
|
20
|
-
article: "Article",
|
|
21
|
-
aside: "Aside",
|
|
22
|
-
main: "Main",
|
|
23
21
|
header: "Header",
|
|
24
|
-
|
|
22
|
+
main: "Main",
|
|
25
23
|
nav: "Nav",
|
|
24
|
+
ol: "Numbered List",
|
|
25
|
+
p: "Paragraph",
|
|
26
26
|
search: "Search",
|
|
27
|
+
section: "Section",
|
|
27
28
|
ul: "Bulleted List",
|
|
28
|
-
ol: "Numbered List",
|
|
29
29
|
} as Record<string, string>;
|
|
30
30
|
|
|
31
31
|
const groups = (elementsMeta.$convertGroups || {}) as Record<string, string[]>;
|
|
@@ -41,23 +41,27 @@ export function getConvertTargets(currentTag: string, isEmpty: boolean) {
|
|
|
41
41
|
const def = (elementsMeta.$defs as Record<string, Record<string, unknown>>)?.[currentTag] as
|
|
42
42
|
| Record<string, unknown>
|
|
43
43
|
| undefined;
|
|
44
|
-
if (!def?.$convertTo)
|
|
44
|
+
if (!def?.$convertTo) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
45
47
|
|
|
46
48
|
const groupNames =
|
|
47
49
|
isEmpty && def.$convertToWhenEmpty
|
|
48
50
|
? (def.$convertToWhenEmpty as string[])
|
|
49
51
|
: [def.$convertTo as string];
|
|
50
52
|
|
|
51
|
-
const tags
|
|
53
|
+
const tags = new Set<string>();
|
|
52
54
|
for (const name of groupNames) {
|
|
53
55
|
for (const tag of groups[name] || []) {
|
|
54
|
-
if (tag !== currentTag)
|
|
56
|
+
if (tag !== currentTag) {
|
|
57
|
+
tags.add(tag);
|
|
58
|
+
}
|
|
55
59
|
}
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
return [...tags].map((tag) => ({
|
|
63
|
+
description: "",
|
|
59
64
|
label: TAG_LABELS[tag] || tag.charAt(0).toUpperCase() + tag.slice(1),
|
|
60
65
|
tag,
|
|
61
|
-
description: "",
|
|
62
66
|
}));
|
|
63
67
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
// ─── Convert to Component ─────────────────────────────────────────────────────
|
|
3
3
|
import { html, render as litRender } from "lit-html";
|
|
4
|
+
import { errorMessage } from "@jxsuite/schema/parse";
|
|
4
5
|
import { ref } from "lit-html/directives/ref.js";
|
|
5
|
-
import { getNodeAtPath, parentElementPath
|
|
6
|
+
import { childIndex, getNodeAtPath, parentElementPath } from "../store";
|
|
6
7
|
import { activeTab } from "../workspace/workspace";
|
|
7
8
|
import { transact } from "../tabs/transact";
|
|
8
|
-
import { computeRelativePath, loadComponentRegistry
|
|
9
|
+
import { componentRegistry, computeRelativePath, loadComponentRegistry } from "../files/components";
|
|
9
10
|
import { getPlatform } from "../platform";
|
|
10
11
|
import { statusMessage } from "../panels/statusbar";
|
|
11
12
|
import { showDialog } from "../ui/layers";
|
|
@@ -17,21 +18,27 @@ const VALID_NAME = /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/;
|
|
|
17
18
|
/** Convert the currently selected element into a reusable component. */
|
|
18
19
|
export async function convertToComponent() {
|
|
19
20
|
const tab = activeTab.value;
|
|
20
|
-
if (!tab?.session.selection || tab.session.selection.length < 2)
|
|
21
|
+
if (!tab?.session.selection || tab.session.selection.length < 2) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
21
24
|
|
|
22
25
|
const node = getNodeAtPath(tab.doc.document, tab.session.selection);
|
|
23
|
-
if (!node || !node.tagName)
|
|
26
|
+
if (!node || !node.tagName) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
24
29
|
|
|
25
30
|
const defaultName = deriveDefaultName(node);
|
|
26
31
|
const name = await promptComponentName(defaultName);
|
|
27
|
-
if (!name)
|
|
32
|
+
if (!name) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
28
35
|
|
|
29
36
|
// Extract component definition
|
|
30
37
|
const componentDef = extractComponentDef(node);
|
|
31
38
|
componentDef.tagName = name;
|
|
32
39
|
|
|
33
40
|
// Compute paths
|
|
34
|
-
const componentFile =
|
|
41
|
+
const componentFile = `components/${name}.json`;
|
|
35
42
|
const refPath = computeRelativePath(tab.documentPath, componentFile);
|
|
36
43
|
|
|
37
44
|
// Single atomic mutation: replace node + add $elements ref
|
|
@@ -41,12 +48,19 @@ export async function convertToComponent() {
|
|
|
41
48
|
const pp = parentElementPath(selectionPath) ?? [];
|
|
42
49
|
const idx = childIndex(selectionPath) as number;
|
|
43
50
|
let parent = doc;
|
|
44
|
-
|
|
45
|
-
|
|
51
|
+
// Paths address nodes by construction (same contract as getNodeAtPath).
|
|
52
|
+
for (const seg of pp) {
|
|
53
|
+
parent = parent[seg] as JxMutableNode;
|
|
54
|
+
}
|
|
55
|
+
if (!Array.isArray(parent.children)) {
|
|
56
|
+
parent.children = [];
|
|
57
|
+
}
|
|
46
58
|
parent.children[idx] = { tagName: name };
|
|
47
59
|
|
|
48
60
|
// Ensure $elements exists and add the $ref
|
|
49
|
-
if (!doc.$elements)
|
|
61
|
+
if (!doc.$elements) {
|
|
62
|
+
doc.$elements = [];
|
|
63
|
+
}
|
|
50
64
|
const alreadyReferenced = doc.$elements.some(
|
|
51
65
|
(/** @type {JxMutableNode | string | { $ref: string }} */ el) =>
|
|
52
66
|
el && typeof el === "object" && "$ref" in el && el.$ref === refPath,
|
|
@@ -62,8 +76,8 @@ export async function convertToComponent() {
|
|
|
62
76
|
await platform.writeFile(componentFile, JSON.stringify(componentDef, null, 2));
|
|
63
77
|
await loadComponentRegistry();
|
|
64
78
|
statusMessage(`Converted to <${name}>`);
|
|
65
|
-
} catch (
|
|
66
|
-
statusMessage(`Error saving component: ${(
|
|
79
|
+
} catch (error) {
|
|
80
|
+
statusMessage(`Error saving component: ${errorMessage(error)}`);
|
|
67
81
|
}
|
|
68
82
|
}
|
|
69
83
|
|
|
@@ -74,9 +88,11 @@ export async function convertToComponent() {
|
|
|
74
88
|
* @returns {string}
|
|
75
89
|
*/
|
|
76
90
|
function deriveDefaultName(node: JxMutableNode) {
|
|
77
|
-
if (node.$id && node.$id.includes("-"))
|
|
91
|
+
if (node.$id && node.$id.includes("-")) {
|
|
92
|
+
return node.$id.toLowerCase();
|
|
93
|
+
}
|
|
78
94
|
const tag = (node.tagName ?? "div").toLowerCase();
|
|
79
|
-
return tag.includes("-") ? tag :
|
|
95
|
+
return tag.includes("-") ? tag : `jx-${tag}`;
|
|
80
96
|
}
|
|
81
97
|
|
|
82
98
|
/**
|
|
@@ -100,24 +116,24 @@ function extractComponentDef(node: JxMutableNode) {
|
|
|
100
116
|
* @returns {{ valid: boolean; error: string }}
|
|
101
117
|
*/
|
|
102
118
|
function validateName(val: string) {
|
|
103
|
-
|
|
104
|
-
if (!
|
|
119
|
+
const name = val.trim().toLowerCase();
|
|
120
|
+
if (!name.includes("-")) {
|
|
105
121
|
return {
|
|
106
|
-
valid: false,
|
|
107
122
|
error: "Name must contain a hyphen (e.g. my-component)",
|
|
123
|
+
valid: false,
|
|
108
124
|
};
|
|
109
125
|
}
|
|
110
|
-
if (!VALID_NAME.test(
|
|
126
|
+
if (!VALID_NAME.test(name)) {
|
|
111
127
|
return {
|
|
112
|
-
valid: false,
|
|
113
128
|
error: "Lowercase letters, digits, and hyphens only",
|
|
129
|
+
valid: false,
|
|
114
130
|
};
|
|
115
131
|
}
|
|
116
|
-
const exists = componentRegistry.some((c
|
|
132
|
+
const exists = componentRegistry.some((c) => c.tagName === name);
|
|
117
133
|
if (exists) {
|
|
118
|
-
return {
|
|
134
|
+
return { error: `Component <${name}> already exists`, valid: false };
|
|
119
135
|
}
|
|
120
|
-
return {
|
|
136
|
+
return { error: "", valid: true };
|
|
121
137
|
}
|
|
122
138
|
|
|
123
139
|
/**
|
|
@@ -134,7 +150,7 @@ function promptComponentName(defaultName: string) {
|
|
|
134
150
|
function confirm() {
|
|
135
151
|
const result = validateName(value);
|
|
136
152
|
if (!result.valid) {
|
|
137
|
-
error = result
|
|
153
|
+
({ error } = result);
|
|
138
154
|
rerender();
|
|
139
155
|
return;
|
|
140
156
|
}
|
|
@@ -149,13 +165,17 @@ function promptComponentName(defaultName: string) {
|
|
|
149
165
|
}
|
|
150
166
|
|
|
151
167
|
function onKeydown(e: KeyboardEvent) {
|
|
152
|
-
if (e.key === "Enter")
|
|
168
|
+
if (e.key === "Enter") {
|
|
169
|
+
confirm();
|
|
170
|
+
}
|
|
153
171
|
}
|
|
154
172
|
|
|
155
173
|
function rerender() {
|
|
156
|
-
const layer = document.
|
|
174
|
+
const layer = document.querySelector("#layer-dialog");
|
|
157
175
|
const slot = layer?.lastElementChild;
|
|
158
|
-
if (slot)
|
|
176
|
+
if (slot) {
|
|
177
|
+
litRender(buildTpl(), slot as HTMLElement);
|
|
178
|
+
}
|
|
159
179
|
}
|
|
160
180
|
|
|
161
181
|
function buildTpl() {
|
|
@@ -175,16 +195,19 @@ function promptComponentName(defaultName: string) {
|
|
|
175
195
|
<sp-textfield
|
|
176
196
|
placeholder="my-component"
|
|
177
197
|
value=${value}
|
|
178
|
-
?negative=${
|
|
198
|
+
?negative=${Boolean(error)}
|
|
179
199
|
@input=${onInput}
|
|
180
200
|
@keydown=${onKeydown}
|
|
181
201
|
${ref((el) => {
|
|
182
|
-
if (el)
|
|
202
|
+
if (el) {
|
|
183
203
|
requestAnimationFrame(() => {
|
|
184
204
|
(el as HTMLElement).focus();
|
|
185
205
|
const input = (el as HTMLElement).shadowRoot?.querySelector("input");
|
|
186
|
-
if (input)
|
|
206
|
+
if (input) {
|
|
207
|
+
input.select();
|
|
208
|
+
}
|
|
187
209
|
});
|
|
210
|
+
}
|
|
188
211
|
})}
|
|
189
212
|
>
|
|
190
213
|
<sp-help-text slot="negative-help-text">${error}</sp-help-text>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// ─── Convert to Repeater ──────────────────────────────────────────────────────
|
|
3
3
|
import { html, render as litRender, nothing } from "lit-html";
|
|
4
4
|
import { ref } from "lit-html/directives/ref.js";
|
|
5
|
-
import { getNodeAtPath, parentElementPath
|
|
5
|
+
import { childIndex, childList, getNodeAtPath, parentElementPath } from "../store";
|
|
6
6
|
import { activeTab } from "../workspace/workspace";
|
|
7
7
|
import { transactDoc } from "../tabs/transact";
|
|
8
8
|
import { showDialog } from "../ui/layers";
|
|
@@ -21,40 +21,56 @@ interface RepeaterConfig {
|
|
|
21
21
|
/** Convert the currently selected element into a repeater template. */
|
|
22
22
|
export async function convertToRepeater() {
|
|
23
23
|
const tab = activeTab.value;
|
|
24
|
-
if (!tab?.session.selection || tab.session.selection.length < 2)
|
|
24
|
+
if (!tab?.session.selection || tab.session.selection.length < 2) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
25
27
|
|
|
26
28
|
const path = tab.session.selection;
|
|
27
29
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
28
|
-
if (!node)
|
|
30
|
+
if (!node) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
29
33
|
|
|
30
34
|
const defs = tab.doc.document.state || {};
|
|
31
35
|
const config = await promptRepeaterConfig(defs);
|
|
32
|
-
if (!config)
|
|
36
|
+
if (!config) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
33
39
|
|
|
34
40
|
transactDoc(tab, (t) => {
|
|
35
41
|
const doc = t.doc.document;
|
|
36
42
|
if (config.newDef) {
|
|
37
|
-
if (!doc.state)
|
|
38
|
-
|
|
43
|
+
if (!doc.state) {
|
|
44
|
+
doc.state = {};
|
|
45
|
+
}
|
|
46
|
+
doc.state[config.newDef.name] = { default: [], type: "array" };
|
|
39
47
|
}
|
|
40
48
|
const pp = parentElementPath(path);
|
|
41
|
-
if (!pp)
|
|
49
|
+
if (!pp) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
42
52
|
const idx = childIndex(path) as number;
|
|
43
53
|
const parent = getNodeAtPath(doc, pp);
|
|
44
|
-
if (!parent?.children)
|
|
45
|
-
|
|
54
|
+
if (!parent?.children) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const element = childList(parent)[idx];
|
|
46
58
|
|
|
47
59
|
const repeater: Record<string, unknown> = {
|
|
48
60
|
$prototype: "Array",
|
|
49
61
|
items: config.items,
|
|
50
62
|
map: element,
|
|
51
63
|
};
|
|
52
|
-
if (config.filter)
|
|
53
|
-
|
|
64
|
+
if (config.filter) {
|
|
65
|
+
repeater.filter = config.filter;
|
|
66
|
+
}
|
|
67
|
+
if (config.sort) {
|
|
68
|
+
repeater.sort = config.sort;
|
|
69
|
+
}
|
|
54
70
|
|
|
55
71
|
(parent.children as (string | JxMutableNode)[])[idx] = {
|
|
56
|
-
tagName: "div",
|
|
57
72
|
children: repeater as unknown as (string | JxMutableNode)[],
|
|
73
|
+
tagName: "div",
|
|
58
74
|
};
|
|
59
75
|
});
|
|
60
76
|
}
|
|
@@ -75,8 +91,12 @@ async function promptRepeaterConfig(defs: Record<string, unknown>) {
|
|
|
75
91
|
const docPath = tab?.documentPath;
|
|
76
92
|
for (const [name, d] of Object.entries(defs)) {
|
|
77
93
|
const def = d as Record<string, unknown> | null;
|
|
78
|
-
if (!def?.$prototype || def.$prototype === "Function" || def.$prototype === "Array")
|
|
79
|
-
|
|
94
|
+
if (!def?.$prototype || def.$prototype === "Function" || def.$prototype === "Array") {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (arrayDefs.some(([n]) => n === name)) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
80
100
|
const schema = await fetchPluginSchema(
|
|
81
101
|
{
|
|
82
102
|
...(def.$src != null && { $src: def.$src as string }),
|
|
@@ -132,9 +152,11 @@ async function promptRepeaterConfig(defs: Record<string, unknown>) {
|
|
|
132
152
|
}
|
|
133
153
|
|
|
134
154
|
function rerender() {
|
|
135
|
-
const layer = document.
|
|
155
|
+
const layer = document.querySelector("#layer-dialog");
|
|
136
156
|
const slot = layer?.lastElementChild;
|
|
137
|
-
if (slot)
|
|
157
|
+
if (slot) {
|
|
158
|
+
litRender(buildTpl(), slot as HTMLElement);
|
|
159
|
+
}
|
|
138
160
|
}
|
|
139
161
|
|
|
140
162
|
function buildTpl() {
|
|
@@ -179,18 +201,21 @@ async function promptRepeaterConfig(defs: Record<string, unknown>) {
|
|
|
179
201
|
size="s"
|
|
180
202
|
placeholder="myItems"
|
|
181
203
|
.value=${newDefName}
|
|
182
|
-
?negative=${
|
|
204
|
+
?negative=${Boolean(error)}
|
|
183
205
|
@input=${(e: Event) => {
|
|
184
206
|
newDefName = (e.target as HTMLInputElement).value || "";
|
|
185
207
|
error = "";
|
|
186
208
|
rerender();
|
|
187
209
|
}}
|
|
188
210
|
@keydown=${(e: KeyboardEvent) => {
|
|
189
|
-
if (e.key === "Enter")
|
|
211
|
+
if (e.key === "Enter") {
|
|
212
|
+
confirm();
|
|
213
|
+
}
|
|
190
214
|
}}
|
|
191
215
|
${ref((el) => {
|
|
192
|
-
if (el && source === "__new__")
|
|
216
|
+
if (el && source === "__new__") {
|
|
193
217
|
requestAnimationFrame(() => (el as HTMLElement).focus());
|
|
218
|
+
}
|
|
194
219
|
})}
|
|
195
220
|
>
|
|
196
221
|
<sp-help-text slot="negative-help-text">${error}</sp-help-text>
|