@jxsuite/studio 0.23.1 → 0.24.0
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 +2231 -715
- package/dist/studio.js.map +121 -104
- package/package.json +8 -7
- package/src/browse/{browse-modal.js → browse-modal.ts} +9 -10
- package/src/browse/{browse.js → browse.ts} +110 -85
- package/src/canvas/{canvas-diff.js → canvas-diff.ts} +42 -35
- package/src/canvas/{canvas-helpers.js → canvas-helpers.ts} +25 -19
- package/src/canvas/{canvas-live-render.js → canvas-live-render.ts} +53 -63
- package/src/canvas/{canvas-render.js → canvas-render.ts} +110 -121
- package/src/canvas/{canvas-utils.js → canvas-utils.ts} +48 -44
- package/src/canvas/{nested-site-style.js → nested-site-style.ts} +3 -3
- package/src/editor/{component-inline-edit.js → component-inline-edit.ts} +45 -33
- package/src/editor/{content-inline-edit.js → content-inline-edit.ts} +27 -45
- package/src/editor/{context-menu.js → context-menu.ts} +38 -36
- package/src/editor/{convert-targets.js → convert-targets.ts} +15 -12
- package/src/editor/{convert-to-component.js → convert-to-component.ts} +25 -26
- package/src/editor/{convert-to-repeater.js → convert-to-repeater.ts} +60 -40
- package/src/editor/{inline-edit.js → inline-edit.ts} +91 -55
- package/src/editor/{inline-format.js → inline-format.ts} +46 -51
- package/src/editor/{insertion-helper.js → insertion-helper.ts} +47 -63
- package/src/editor/{shortcuts.js → shortcuts.ts} +45 -31
- package/src/editor/{slash-menu.js → slash-menu.ts} +34 -35
- package/src/files/{components.js → components.ts} +12 -15
- package/src/files/{file-ops.js → file-ops.ts} +36 -29
- package/src/files/{files.js → files.ts} +102 -84
- package/src/github/{github-auth.js → github-auth.ts} +3 -3
- package/src/github/{github-publish.js → github-publish.ts} +20 -18
- package/src/markdown/{md-allowlist.js → md-allowlist.ts} +5 -2
- package/src/markdown/{md-convert.js → md-convert.ts} +146 -169
- package/src/new-project/{new-project-modal.js → new-project-modal.ts} +16 -18
- package/src/panels/{activity-bar.js → activity-bar.ts} +26 -34
- package/src/panels/{ai-panel.js → ai-panel.ts} +30 -30
- package/src/panels/{block-action-bar.js → block-action-bar.ts} +64 -73
- package/src/panels/{canvas-dnd.js → canvas-dnd.ts} +44 -49
- package/src/panels/{data-explorer.js → data-explorer.ts} +21 -15
- package/src/panels/dnd.ts +417 -0
- package/src/panels/{editors.js → editors.ts} +43 -50
- package/src/panels/{elements-panel.js → elements-panel.ts} +30 -27
- package/src/panels/{events-panel.js → events-panel.ts} +33 -25
- package/src/panels/{git-panel.js → git-panel.ts} +97 -83
- package/src/panels/{head-panel.js → head-panel.ts} +117 -139
- package/src/panels/{imports-panel.js → imports-panel.ts} +69 -89
- package/src/panels/{layers-panel.js → layers-panel.ts} +61 -57
- package/src/panels/{left-panel.js → left-panel.ts} +56 -63
- package/src/panels/{overlays.js → overlays.ts} +28 -36
- package/src/panels/{panel-events.js → panel-events.ts} +53 -51
- package/src/panels/{preview-render.js → preview-render.ts} +20 -15
- package/src/panels/{properties-panel.js → properties-panel.ts} +212 -246
- package/src/panels/{pseudo-preview.js → pseudo-preview.ts} +9 -8
- package/src/panels/{quick-search.js → quick-search.ts} +16 -17
- package/src/panels/{right-panel.js → right-panel.ts} +41 -59
- package/src/panels/{shared.js → shared.ts} +7 -5
- package/src/panels/{signals-panel.js → signals-panel.ts} +322 -362
- package/src/panels/{statusbar.js → statusbar.ts} +14 -14
- package/src/panels/{style-inputs.js → style-inputs.ts} +66 -48
- package/src/panels/{style-panel.js → style-panel.ts} +235 -290
- package/src/panels/{style-utils.js → style-utils.ts} +32 -31
- package/src/panels/{stylebook-layers-panel.js → stylebook-layers-panel.ts} +22 -14
- package/src/panels/{stylebook-panel.js → stylebook-panel.ts} +127 -140
- package/src/panels/{tab-strip.js → tab-strip.ts} +12 -14
- package/src/panels/{toolbar.js → toolbar.ts} +68 -70
- package/src/panels/{welcome-screen.js → welcome-screen.ts} +14 -16
- package/src/{platform.js → platform.ts} +5 -3
- package/src/platforms/{devserver.js → devserver.ts} +53 -44
- package/src/{recent-projects.js → recent-projects.ts} +17 -12
- package/src/{resize-edges.js → resize-edges.ts} +18 -13
- package/src/services/{cem-export.js → cem-export.ts} +14 -7
- package/src/services/{code-services.js → code-services.ts} +29 -20
- package/src/services/{monaco-setup.js → monaco-setup.ts} +2 -3
- package/src/settings/{content-types-editor.js → content-types-editor.ts} +88 -67
- package/src/settings/{css-vars-editor.js → css-vars-editor.ts} +78 -51
- package/src/settings/{defs-editor.js → defs-editor.ts} +70 -56
- package/src/settings/{general-settings.js → general-settings.ts} +29 -28
- package/src/settings/{head-editor.js → head-editor.ts} +37 -35
- package/src/settings/{schema-field-ui.js → schema-field-ui.ts} +86 -76
- package/src/settings/{settings-modal.js → settings-modal.ts} +14 -16
- package/src/{site-context.js → site-context.ts} +35 -32
- package/src/{state.js → state.ts} +95 -78
- package/src/{store.js → store.ts} +60 -63
- package/src/{studio.js → studio.ts} +117 -119
- package/src/tabs/tab.ts +196 -0
- package/src/tabs/{transact.js → transact.ts} +107 -60
- package/src/types.ts +185 -0
- package/src/ui/{button-group.js → button-group.ts} +21 -21
- package/src/ui/{color-selector.js → color-selector.ts} +44 -37
- package/src/ui/{expression-editor.js → expression-editor.ts} +65 -40
- package/src/ui/{field-row.js → field-row.ts} +19 -2
- package/src/ui/{icons.js → icons.ts} +2 -2
- package/src/ui/{layers.js → layers.ts} +33 -24
- package/src/ui/{media-picker.js → media-picker.ts} +13 -9
- package/src/ui/{panel-resize.js → panel-resize.ts} +9 -4
- package/src/ui/{spectrum.js → spectrum.ts} +4 -4
- package/src/ui/{unit-selector.js → unit-selector.ts} +16 -19
- package/src/ui/{value-selector.js → value-selector.ts} +28 -28
- package/src/ui/{widgets.js → widgets.ts} +62 -15
- package/src/utils/{canvas-media.js → canvas-media.ts} +29 -14
- package/src/utils/{edit-display.js → edit-display.ts} +23 -20
- package/src/utils/{google-fonts.js → google-fonts.ts} +14 -13
- package/src/utils/{inherited-style.js → inherited-style.ts} +15 -10
- package/src/utils/{studio-utils.js → studio-utils.ts} +38 -26
- package/src/{view.js → view.ts} +53 -55
- package/src/workspace/{workspace.js → workspace.ts} +44 -33
- package/src/panels/dnd.js +0 -426
- package/src/tabs/tab.js +0 -192
- /package/src/{reactivity.js → reactivity.ts} +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/** Properties panel — inspector for element attributes, component props, media, and frontmatter. */
|
|
2
3
|
|
|
3
4
|
import { html, nothing } from "lit-html";
|
|
4
5
|
import { live } from "lit-html/directives/live.js";
|
|
5
|
-
import { getNodeAtPath, debouncedStyleCommit, renderOnly, projectState } from "../store
|
|
6
|
+
import { getNodeAtPath, debouncedStyleCommit, renderOnly, projectState } from "../store";
|
|
7
|
+
import type { JsonValue, DirEntry } from "../types";
|
|
6
8
|
import {
|
|
7
9
|
transactDoc,
|
|
8
10
|
mutateUpdateProperty,
|
|
@@ -12,39 +14,58 @@ import {
|
|
|
12
14
|
mutateAddSwitchCase,
|
|
13
15
|
mutateRemoveSwitchCase,
|
|
14
16
|
mutateRenameSwitchCase,
|
|
15
|
-
} from "../tabs/transact
|
|
16
|
-
import { activeTab } from "../workspace/workspace
|
|
17
|
-
import { view } from "../view
|
|
18
|
-
import { componentRegistry } from "../files/components
|
|
19
|
-
import { widgetForType } from "./style-inputs
|
|
20
|
-
import { renderFieldRow } from "../ui/field-row
|
|
17
|
+
} from "../tabs/transact";
|
|
18
|
+
import { activeTab } from "../workspace/workspace";
|
|
19
|
+
import { view } from "../view";
|
|
20
|
+
import { componentRegistry } from "../files/components";
|
|
21
|
+
import { widgetForType } from "./style-inputs";
|
|
22
|
+
import { renderFieldRow } from "../ui/field-row";
|
|
21
23
|
import {
|
|
22
24
|
attrLabel,
|
|
23
25
|
inferInputType,
|
|
24
26
|
friendlyNameToVar,
|
|
25
27
|
camelToLabel,
|
|
26
28
|
parseCemType,
|
|
27
|
-
} from "../utils/studio-utils
|
|
28
|
-
import { isCustomElementDoc, collectCssParts } from "./signals-panel
|
|
29
|
-
import { mediaDisplayName } from "./shared
|
|
30
|
-
import { getCssInitialMap } from "./style-utils
|
|
31
|
-
import { renderMediaPicker } from "../ui/media-picker
|
|
32
|
-
import { renderColorSelector } from "../ui/color-selector
|
|
33
|
-
import { getEffectiveLayoutPath, invalidateLayoutCache } from "../site-context
|
|
34
|
-
import { getPlatform } from "../platform
|
|
29
|
+
} from "../utils/studio-utils";
|
|
30
|
+
import { isCustomElementDoc, collectCssParts } from "./signals-panel";
|
|
31
|
+
import { mediaDisplayName } from "./shared";
|
|
32
|
+
import { getCssInitialMap } from "./style-utils";
|
|
33
|
+
import { renderMediaPicker } from "../ui/media-picker";
|
|
34
|
+
import { renderColorSelector } from "../ui/color-selector";
|
|
35
|
+
import { getEffectiveLayoutPath, invalidateLayoutCache } from "../site-context";
|
|
36
|
+
import { getPlatform } from "../platform";
|
|
35
37
|
import htmlMeta from "../../data/html-meta.json";
|
|
36
38
|
|
|
39
|
+
import type { JxPrototypeDef, JxMutableNode } from "@jxsuite/schema/types";
|
|
40
|
+
import type { JxPath } from "../state";
|
|
41
|
+
|
|
42
|
+
interface SignalOption {
|
|
43
|
+
value: string;
|
|
44
|
+
label: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface HtmlMetaEntry {
|
|
48
|
+
$section: string;
|
|
49
|
+
$order: number;
|
|
50
|
+
$elements?: string[];
|
|
51
|
+
$label?: string;
|
|
52
|
+
$input?: string;
|
|
53
|
+
$shorthand?: boolean;
|
|
54
|
+
type?: string;
|
|
55
|
+
[key: string]: unknown;
|
|
56
|
+
}
|
|
57
|
+
|
|
37
58
|
/**
|
|
38
59
|
* Convert a human-friendly name like "Tablet" to a $media key "--tablet"
|
|
39
60
|
*
|
|
40
61
|
* @param {string} name
|
|
41
62
|
*/
|
|
42
|
-
function friendlyNameToMedia(name) {
|
|
63
|
+
function friendlyNameToMedia(name: string) {
|
|
43
64
|
return friendlyNameToVar(name, "--");
|
|
44
65
|
}
|
|
45
66
|
|
|
46
67
|
/** Check if a selection path is inside a $map template (contains [..., "children", "map", ...]). */
|
|
47
|
-
function isInsideMapTemplate(
|
|
68
|
+
function isInsideMapTemplate(path: JxPath | null) {
|
|
48
69
|
if (!path) return false;
|
|
49
70
|
for (let i = 0; i < path.length - 1; i++) {
|
|
50
71
|
if (path[i] === "children" && path[i + 1] === "map") return true;
|
|
@@ -56,42 +77,30 @@ function isInsideMapTemplate(/** @type {JxPath | null} */ path) {
|
|
|
56
77
|
* Field row with binding toggle — allows switching between static value and signal binding.
|
|
57
78
|
* rawValue can be a string/bool (static) or { $ref: "..." } (bound).
|
|
58
79
|
*/
|
|
59
|
-
/** @typedef {{ value: string; label: string }} SignalOption */
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* @typedef {{
|
|
63
|
-
* $section: string;
|
|
64
|
-
* $order: number;
|
|
65
|
-
* $elements?: string[];
|
|
66
|
-
* $label?: string;
|
|
67
|
-
* $input?: string;
|
|
68
|
-
* $shorthand?: boolean;
|
|
69
|
-
* type?: string;
|
|
70
|
-
* [key: string]: unknown;
|
|
71
|
-
* }} HtmlMetaEntry
|
|
72
|
-
*/
|
|
73
|
-
|
|
74
80
|
function bindableFieldRow(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
label: string,
|
|
82
|
+
type: string,
|
|
83
|
+
rawValue: string | number | boolean | { $ref: string } | null | undefined,
|
|
84
|
+
onChange: (v: JsonValue) => void,
|
|
85
|
+
filterFn: ((d: import("./signals-panel.js").SignalDef) => boolean) | null = null,
|
|
86
|
+
extraSignals: SignalOption[] | null = null,
|
|
81
87
|
) {
|
|
82
88
|
const tab = activeTab.value;
|
|
83
|
-
const defs = tab
|
|
89
|
+
const defs = tab!.doc.document.state || {};
|
|
84
90
|
const isBound = typeof rawValue === "object" && rawValue !== null && rawValue.$ref;
|
|
85
91
|
|
|
86
92
|
const signalDefs = Object.entries(defs).filter(([, d]) =>
|
|
87
|
-
filterFn
|
|
93
|
+
filterFn
|
|
94
|
+
? filterFn(d as import("./signals-panel.js").SignalDef)
|
|
95
|
+
: !(d as Record<string, unknown>)?.$handler &&
|
|
96
|
+
(d as JxPrototypeDef)?.$prototype !== "Function",
|
|
88
97
|
);
|
|
89
98
|
|
|
90
99
|
/** @type {ReturnType<typeof setTimeout> | undefined} */
|
|
91
|
-
let debounce;
|
|
92
|
-
const onInput = (
|
|
100
|
+
let debounce: ReturnType<typeof setTimeout> | undefined;
|
|
101
|
+
const onInput = (e: Event) => {
|
|
93
102
|
clearTimeout(debounce);
|
|
94
|
-
debounce = setTimeout(() => onChange(
|
|
103
|
+
debounce = setTimeout(() => onChange((e.target as HTMLInputElement).value), 400);
|
|
95
104
|
};
|
|
96
105
|
|
|
97
106
|
const staticVal = isBound ? "" : (rawValue ?? "");
|
|
@@ -106,8 +115,7 @@ function bindableFieldRow(
|
|
|
106
115
|
: type === "checkbox"
|
|
107
116
|
? html`<sp-checkbox
|
|
108
117
|
?checked=${!!staticVal}
|
|
109
|
-
@change=${(
|
|
110
|
-
onChange(/** @type {HTMLInputElement} */ (e.target).checked)}
|
|
118
|
+
@change=${(e: Event) => onChange((e.target as HTMLInputElement).checked)}
|
|
111
119
|
></sp-checkbox>`
|
|
112
120
|
: html`<sp-textfield size="s" .value=${live(staticVal)} @input=${onInput}></sp-textfield>`;
|
|
113
121
|
|
|
@@ -117,9 +125,9 @@ function bindableFieldRow(
|
|
|
117
125
|
quiet
|
|
118
126
|
placeholder="— select signal —"
|
|
119
127
|
value=${isBound && rawValue.$ref ? rawValue.$ref : nothing}
|
|
120
|
-
@change=${(
|
|
121
|
-
if (
|
|
122
|
-
onChange({ $ref:
|
|
128
|
+
@change=${(e: Event) => {
|
|
129
|
+
if ((e.target as HTMLInputElement).value)
|
|
130
|
+
onChange({ $ref: (e.target as HTMLInputElement).value });
|
|
123
131
|
else onChange(undefined);
|
|
124
132
|
}}
|
|
125
133
|
>
|
|
@@ -130,7 +138,7 @@ function bindableFieldRow(
|
|
|
130
138
|
? html`
|
|
131
139
|
<sp-menu-divider></sp-menu-divider>
|
|
132
140
|
${extraSignals.map(
|
|
133
|
-
(
|
|
141
|
+
(sig: SignalOption) =>
|
|
134
142
|
html`<sp-menu-item value=${sig.value}>${sig.label}</sp-menu-item>`,
|
|
135
143
|
)}
|
|
136
144
|
`
|
|
@@ -174,14 +182,14 @@ function bindableFieldRow(
|
|
|
174
182
|
|
|
175
183
|
/** Key-value pair row for styles / attributes */
|
|
176
184
|
function kvRow(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
185
|
+
key: string,
|
|
186
|
+
value: string,
|
|
187
|
+
onChange: (newKey: string, newVal: string) => void,
|
|
188
|
+
onDelete: () => void,
|
|
181
189
|
/** @type {string | null} */ datalistId = null,
|
|
182
190
|
) {
|
|
183
191
|
/** @type {ReturnType<typeof setTimeout> | undefined} */
|
|
184
|
-
let debounceTimer;
|
|
192
|
+
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
|
185
193
|
let currentKey = key;
|
|
186
194
|
let currentVal = value;
|
|
187
195
|
const commit = () => {
|
|
@@ -195,19 +203,19 @@ function kvRow(
|
|
|
195
203
|
size="s"
|
|
196
204
|
class="kv-key"
|
|
197
205
|
.value=${live(key)}
|
|
198
|
-
@input=${(
|
|
199
|
-
currentKey =
|
|
206
|
+
@input=${(e: Event) => {
|
|
207
|
+
currentKey = (e.target as HTMLInputElement).value;
|
|
200
208
|
commit();
|
|
201
209
|
}}
|
|
202
210
|
@change=${datalistId === "css-props"
|
|
203
|
-
? (
|
|
204
|
-
const el =
|
|
211
|
+
? (e: Event) => {
|
|
212
|
+
const el = (e.target as HTMLInputElement)
|
|
205
213
|
.closest(".kv-row")
|
|
206
214
|
?.querySelector(".kv-val");
|
|
207
215
|
if (el)
|
|
208
216
|
el.setAttribute(
|
|
209
217
|
"placeholder",
|
|
210
|
-
getCssInitialMap().get(
|
|
218
|
+
getCssInitialMap().get((e.target as HTMLInputElement).value) || "",
|
|
211
219
|
);
|
|
212
220
|
}
|
|
213
221
|
: nothing}
|
|
@@ -217,8 +225,8 @@ function kvRow(
|
|
|
217
225
|
class="kv-val"
|
|
218
226
|
.value=${live(value)}
|
|
219
227
|
placeholder=${placeholder}
|
|
220
|
-
@input=${(
|
|
221
|
-
currentVal =
|
|
228
|
+
@input=${(e: Event) => {
|
|
229
|
+
currentVal = (e.target as HTMLInputElement).value;
|
|
222
230
|
commit();
|
|
223
231
|
}}
|
|
224
232
|
></sp-textfield>
|
|
@@ -233,23 +241,23 @@ function kvRow(
|
|
|
233
241
|
|
|
234
242
|
/** Repeater fields template */
|
|
235
243
|
function renderRepeaterFieldsTemplate(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
244
|
+
node: JxMutableNode,
|
|
245
|
+
path: JxPath,
|
|
246
|
+
_mapSignals: SignalOption[] | null,
|
|
239
247
|
) {
|
|
240
248
|
return html`
|
|
241
|
-
${bindableFieldRow("Items", "text", node.items, (
|
|
249
|
+
${bindableFieldRow("Items", "text", node.items, (v: JsonValue) =>
|
|
242
250
|
transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, path, "items", v)),
|
|
243
251
|
)}
|
|
244
252
|
${node.filter
|
|
245
|
-
? bindableFieldRow("Filter", "text", node.filter, (
|
|
253
|
+
? bindableFieldRow("Filter", "text", node.filter, (v: JsonValue) =>
|
|
246
254
|
transactDoc(activeTab.value, (t) =>
|
|
247
255
|
mutateUpdateProperty(t, path, "filter", v || undefined),
|
|
248
256
|
),
|
|
249
257
|
)
|
|
250
258
|
: nothing}
|
|
251
259
|
${node.sort
|
|
252
|
-
? bindableFieldRow("Sort", "text", node.sort, (
|
|
260
|
+
? bindableFieldRow("Sort", "text", node.sort, (v: JsonValue) =>
|
|
253
261
|
transactDoc(activeTab.value, (t) =>
|
|
254
262
|
mutateUpdateProperty(t, path, "sort", v || undefined),
|
|
255
263
|
),
|
|
@@ -283,7 +291,7 @@ function renderRepeaterFieldsTemplate(
|
|
|
283
291
|
size="s"
|
|
284
292
|
style="margin-top:8px;width:100%"
|
|
285
293
|
@click=${() => {
|
|
286
|
-
activeTab.value
|
|
294
|
+
activeTab.value!.session.selection = [...path, "map"];
|
|
287
295
|
}}
|
|
288
296
|
>Edit template →</sp-action-button
|
|
289
297
|
>
|
|
@@ -294,9 +302,9 @@ function renderRepeaterFieldsTemplate(
|
|
|
294
302
|
|
|
295
303
|
/** Switch fields template */
|
|
296
304
|
function renderSwitchFieldsTemplate(
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
305
|
+
node: JxMutableNode,
|
|
306
|
+
path: JxPath,
|
|
307
|
+
mapSignals: SignalOption[] | null,
|
|
300
308
|
) {
|
|
301
309
|
const caseNames = Object.keys(node.cases || {});
|
|
302
310
|
return html`
|
|
@@ -304,7 +312,7 @@ function renderSwitchFieldsTemplate(
|
|
|
304
312
|
"Expression",
|
|
305
313
|
"text",
|
|
306
314
|
node.$switch,
|
|
307
|
-
(
|
|
315
|
+
(v: JsonValue) =>
|
|
308
316
|
transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, path, "$switch", v)),
|
|
309
317
|
null,
|
|
310
318
|
mapSignals,
|
|
@@ -316,27 +324,22 @@ function renderSwitchFieldsTemplate(
|
|
|
316
324
|
</div>
|
|
317
325
|
${caseNames.map((caseName) => {
|
|
318
326
|
/** @type {ReturnType<typeof setTimeout> | undefined} */
|
|
319
|
-
let debounce;
|
|
327
|
+
let debounce: ReturnType<typeof setTimeout> | undefined;
|
|
320
328
|
return html`
|
|
321
329
|
<div class="field-row" style="display:flex;align-items:center;gap:4px;margin-bottom:3px">
|
|
322
330
|
<input
|
|
323
331
|
class="field-input"
|
|
324
332
|
.value=${live(caseName)}
|
|
325
333
|
style="flex:1"
|
|
326
|
-
@input=${(
|
|
334
|
+
@input=${(e: Event) => {
|
|
327
335
|
clearTimeout(debounce);
|
|
328
336
|
debounce = setTimeout(() => {
|
|
329
337
|
if (
|
|
330
|
-
|
|
331
|
-
|
|
338
|
+
(e.target as HTMLInputElement).value &&
|
|
339
|
+
(e.target as HTMLInputElement).value !== caseName
|
|
332
340
|
)
|
|
333
341
|
transactDoc(activeTab.value, (t) =>
|
|
334
|
-
mutateRenameSwitchCase(
|
|
335
|
-
t,
|
|
336
|
-
path,
|
|
337
|
-
caseName,
|
|
338
|
-
/** @type {HTMLInputElement} */ (e.target).value,
|
|
339
|
-
),
|
|
342
|
+
mutateRenameSwitchCase(t, path, caseName, (e.target as HTMLInputElement).value),
|
|
340
343
|
);
|
|
341
344
|
}, 500);
|
|
342
345
|
}}
|
|
@@ -345,17 +348,17 @@ function renderSwitchFieldsTemplate(
|
|
|
345
348
|
class="bind-toggle"
|
|
346
349
|
title="Edit case"
|
|
347
350
|
style="cursor:pointer"
|
|
348
|
-
@click=${(
|
|
351
|
+
@click=${(e: Event) => {
|
|
349
352
|
e.stopPropagation();
|
|
350
|
-
activeTab.value
|
|
353
|
+
activeTab.value!.session.selection = [...path, "cases", caseName];
|
|
351
354
|
}}
|
|
352
355
|
>→</span
|
|
353
356
|
>
|
|
354
357
|
<span
|
|
355
358
|
style="cursor:pointer;color:var(--danger);font-size:11px"
|
|
356
|
-
@click=${(
|
|
359
|
+
@click=${(e: Event) => {
|
|
357
360
|
e.stopPropagation();
|
|
358
|
-
transactDoc(activeTab.value
|
|
361
|
+
transactDoc(activeTab.value!, (t) => mutateRemoveSwitchCase(t, path, caseName));
|
|
359
362
|
}}
|
|
360
363
|
>✕</span
|
|
361
364
|
>
|
|
@@ -376,10 +379,10 @@ function renderSwitchFieldsTemplate(
|
|
|
376
379
|
|
|
377
380
|
/** Component props fields template */
|
|
378
381
|
function renderComponentPropsFieldsTemplate(
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
382
|
+
node: JxMutableNode,
|
|
383
|
+
path: JxPath,
|
|
384
|
+
mapSignals: SignalOption[] | null,
|
|
385
|
+
navigateToComponent: (path: string) => void,
|
|
383
386
|
) {
|
|
384
387
|
const tab = activeTab.value;
|
|
385
388
|
const comp = componentRegistry.find((c) => c.tagName === node.tagName);
|
|
@@ -387,21 +390,17 @@ function renderComponentPropsFieldsTemplate(
|
|
|
387
390
|
const isNpm = comp.source === "npm";
|
|
388
391
|
const currentVals = isNpm ? node.attributes || {} : node.$props || {};
|
|
389
392
|
const updateFn = isNpm
|
|
390
|
-
? (
|
|
393
|
+
? (name: string, v: JsonValue) =>
|
|
391
394
|
transactDoc(activeTab.value, (t) =>
|
|
392
|
-
mutateUpdateAttribute(
|
|
393
|
-
t,
|
|
394
|
-
path,
|
|
395
|
-
name,
|
|
396
|
-
v === "" ? undefined : /** @type {string | undefined} */ (v),
|
|
397
|
-
),
|
|
395
|
+
mutateUpdateAttribute(t, path, name, v === "" ? undefined : (v as string | undefined)),
|
|
398
396
|
)
|
|
399
|
-
: (
|
|
397
|
+
: (name: string, v: JsonValue) =>
|
|
400
398
|
transactDoc(activeTab.value, (t) => mutateUpdateProp(t, path, name, v));
|
|
401
399
|
|
|
402
|
-
const defs = tab
|
|
400
|
+
const defs = tab!.doc.document.state || {};
|
|
403
401
|
const signalDefs = Object.entries(defs).filter(
|
|
404
|
-
([, d]) =>
|
|
402
|
+
([, d]) =>
|
|
403
|
+
!(d as Record<string, unknown>)?.$handler && (d as JxPrototypeDef)?.$prototype !== "Function",
|
|
405
404
|
);
|
|
406
405
|
const extraSignals = mapSignals;
|
|
407
406
|
|
|
@@ -414,9 +413,10 @@ function renderComponentPropsFieldsTemplate(
|
|
|
414
413
|
const isBound = typeof rawValue === "object" && rawValue !== null && rawValue.$ref;
|
|
415
414
|
const hasVal = rawValue !== undefined && rawValue !== null;
|
|
416
415
|
const parsed = parseCemType(prop.type);
|
|
417
|
-
const onChange = (
|
|
416
|
+
const onChange = (v: JsonValue) => updateFn(prop.name, v);
|
|
417
|
+
const staticVal = isBound ? "" : String(rawValue ?? "");
|
|
418
418
|
|
|
419
|
-
const clearProp = (
|
|
419
|
+
const clearProp = (e: Event) => {
|
|
420
420
|
e.stopPropagation();
|
|
421
421
|
updateFn(prop.name, undefined);
|
|
422
422
|
};
|
|
@@ -446,9 +446,9 @@ function renderComponentPropsFieldsTemplate(
|
|
|
446
446
|
quiet
|
|
447
447
|
placeholder="— select signal —"
|
|
448
448
|
value=${isBound && rawValue.$ref ? rawValue.$ref : nothing}
|
|
449
|
-
@change=${(
|
|
450
|
-
if (
|
|
451
|
-
onChange({ $ref:
|
|
449
|
+
@change=${(e: Event) => {
|
|
450
|
+
if ((e.target as HTMLInputElement).value)
|
|
451
|
+
onChange({ $ref: (e.target as HTMLInputElement).value });
|
|
452
452
|
else onChange(undefined);
|
|
453
453
|
}}
|
|
454
454
|
>
|
|
@@ -460,7 +460,7 @@ function renderComponentPropsFieldsTemplate(
|
|
|
460
460
|
? html`
|
|
461
461
|
<sp-menu-divider></sp-menu-divider>
|
|
462
462
|
${extraSignals.map(
|
|
463
|
-
(
|
|
463
|
+
(sig: SignalOption) =>
|
|
464
464
|
html`<sp-menu-item value=${sig.value}>${sig.label}</sp-menu-item>`,
|
|
465
465
|
)}
|
|
466
466
|
`
|
|
@@ -469,9 +469,7 @@ function renderComponentPropsFieldsTemplate(
|
|
|
469
469
|
`;
|
|
470
470
|
|
|
471
471
|
/** @type {ReturnType<typeof setTimeout> | undefined} */
|
|
472
|
-
let debounce;
|
|
473
|
-
const staticVal = isBound ? "" : (rawValue ?? "");
|
|
474
|
-
/** @type {import("lit-html").TemplateResult | undefined} */
|
|
472
|
+
let debounce: ReturnType<typeof setTimeout> | undefined;
|
|
475
473
|
let widgetTpl;
|
|
476
474
|
if (prop.format === "image") {
|
|
477
475
|
widgetTpl = renderMediaPicker(prop.name, staticVal, onChange);
|
|
@@ -482,55 +480,43 @@ function renderComponentPropsFieldsTemplate(
|
|
|
482
480
|
size="s"
|
|
483
481
|
placeholder="YYYY-MM-DD"
|
|
484
482
|
.value=${live(staticVal)}
|
|
485
|
-
@input=${(
|
|
483
|
+
@input=${(e: Event) => {
|
|
486
484
|
clearTimeout(debounce);
|
|
487
|
-
debounce = setTimeout(
|
|
488
|
-
() => onChange(/** @type {HTMLInputElement} */ (e.target).value),
|
|
489
|
-
400,
|
|
490
|
-
);
|
|
485
|
+
debounce = setTimeout(() => onChange((e.target as HTMLInputElement).value), 400);
|
|
491
486
|
}}
|
|
492
487
|
></sp-textfield>`;
|
|
493
488
|
} else if (parsed.kind === "boolean") {
|
|
494
489
|
widgetTpl = html`<sp-checkbox
|
|
495
490
|
size="s"
|
|
496
491
|
.checked=${live(!!staticVal)}
|
|
497
|
-
@change=${(
|
|
498
|
-
onChange(/** @type {HTMLInputElement} */ (e.target).checked || undefined)}
|
|
492
|
+
@change=${(e: Event) => onChange((e.target as HTMLInputElement).checked || undefined)}
|
|
499
493
|
></sp-checkbox>`;
|
|
500
494
|
} else if (parsed.kind === "number") {
|
|
501
495
|
widgetTpl = html`<sp-number-field
|
|
502
496
|
size="s"
|
|
503
497
|
.value=${live(staticVal)}
|
|
504
|
-
@input=${(
|
|
498
|
+
@input=${(e: Event) => {
|
|
505
499
|
clearTimeout(debounce);
|
|
506
|
-
debounce = setTimeout(
|
|
507
|
-
() => onChange(/** @type {HTMLInputElement} */ (e.target).value),
|
|
508
|
-
400,
|
|
509
|
-
);
|
|
500
|
+
debounce = setTimeout(() => onChange((e.target as HTMLInputElement).value), 400);
|
|
510
501
|
}}
|
|
511
502
|
></sp-number-field>`;
|
|
512
503
|
} else if (parsed.kind === "combobox") {
|
|
513
|
-
const options = /** @type {string[]} */ (
|
|
514
|
-
/** @type {{ options?: string[] }} */ (parsed).options
|
|
515
|
-
);
|
|
504
|
+
const options = /** @type {{ options?: string[] }} */ (parsed).options as string[];
|
|
516
505
|
widgetTpl = html`<jx-value-selector
|
|
517
506
|
.value=${String(staticVal)}
|
|
518
507
|
size="s"
|
|
519
508
|
placeholder="—"
|
|
520
509
|
.options=${options.map((o) => ({ value: o, label: camelToLabel(o) }))}
|
|
521
|
-
@change=${(
|
|
522
|
-
onChange(e.detail?.value ??
|
|
510
|
+
@change=${(e: Event & { detail?: { value?: string } }) =>
|
|
511
|
+
onChange(e.detail?.value ?? (e.target as HTMLInputElement).value)}
|
|
523
512
|
></jx-value-selector>`;
|
|
524
513
|
} else {
|
|
525
514
|
widgetTpl = html`<sp-textfield
|
|
526
515
|
size="s"
|
|
527
516
|
.value=${live(staticVal)}
|
|
528
|
-
@input=${(
|
|
517
|
+
@input=${(e: Event) => {
|
|
529
518
|
clearTimeout(debounce);
|
|
530
|
-
debounce = setTimeout(
|
|
531
|
-
() => onChange(/** @type {HTMLInputElement} */ (e.target).value),
|
|
532
|
-
400,
|
|
533
|
-
);
|
|
519
|
+
debounce = setTimeout(() => onChange((e.target as HTMLInputElement).value), 400);
|
|
534
520
|
}}
|
|
535
521
|
></sp-textfield>`;
|
|
536
522
|
}
|
|
@@ -568,10 +554,10 @@ function renderComponentPropsFieldsTemplate(
|
|
|
568
554
|
|
|
569
555
|
/** Custom attrs fields template */
|
|
570
556
|
function renderCustomAttrsFieldsTemplate(
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
557
|
+
node: JxMutableNode,
|
|
558
|
+
path: JxPath,
|
|
559
|
+
attrs: Record<string, unknown>,
|
|
560
|
+
knownAttrNames: Set<string>,
|
|
575
561
|
) {
|
|
576
562
|
const customAttrs = Object.entries(attrs).filter(([k]) => !knownAttrNames.has(k));
|
|
577
563
|
return html`
|
|
@@ -579,7 +565,7 @@ function renderCustomAttrsFieldsTemplate(
|
|
|
579
565
|
kvRow(
|
|
580
566
|
attr,
|
|
581
567
|
String(val),
|
|
582
|
-
(
|
|
568
|
+
(newAttr: string, newVal: string) => {
|
|
583
569
|
if (newAttr !== attr) {
|
|
584
570
|
transactDoc(activeTab.value, (t) => {
|
|
585
571
|
mutateUpdateAttribute(t, path, attr, undefined);
|
|
@@ -604,10 +590,10 @@ function renderCustomAttrsFieldsTemplate(
|
|
|
604
590
|
// ─── Media breakpoints ──────────────────────────────────────────────────────
|
|
605
591
|
|
|
606
592
|
/** Media breakpoint fields template */
|
|
607
|
-
function renderMediaFieldsTemplate(
|
|
593
|
+
function renderMediaFieldsTemplate(node: JxMutableNode) {
|
|
608
594
|
const media = node.$media || {};
|
|
609
595
|
/** @type {ReturnType<typeof setTimeout> | undefined} */
|
|
610
|
-
let baseDebounce;
|
|
596
|
+
let baseDebounce: ReturnType<typeof setTimeout> | undefined;
|
|
611
597
|
const breakpoints = Object.entries(media).filter(([k]) => k !== "--");
|
|
612
598
|
|
|
613
599
|
return html`
|
|
@@ -618,10 +604,10 @@ function renderMediaFieldsTemplate(/** @type {JxMutableNode} */ node) {
|
|
|
618
604
|
style="width:70px;flex:none"
|
|
619
605
|
placeholder="320px"
|
|
620
606
|
.value=${live(media["--"] || "")}
|
|
621
|
-
@input=${(
|
|
607
|
+
@input=${(e: Event) => {
|
|
622
608
|
clearTimeout(baseDebounce);
|
|
623
609
|
baseDebounce = setTimeout(() => {
|
|
624
|
-
const val =
|
|
610
|
+
const val = (e.target as HTMLInputElement).value.trim();
|
|
625
611
|
transactDoc(activeTab.value, (t) => mutateUpdateMedia(t, "--", val || undefined));
|
|
626
612
|
}, 400);
|
|
627
613
|
}}
|
|
@@ -642,7 +628,7 @@ function renderMediaFieldsTemplate(/** @type {JxMutableNode} */ node) {
|
|
|
642
628
|
<span
|
|
643
629
|
class="kv-add"
|
|
644
630
|
style=${view.showAddBreakpointForm ? "display:none" : ""}
|
|
645
|
-
@click=${(
|
|
631
|
+
@click=${(_e: Event) => {
|
|
646
632
|
view.showAddBreakpointForm = true;
|
|
647
633
|
renderOnly("rightPanel");
|
|
648
634
|
}}
|
|
@@ -656,9 +642,9 @@ function renderMediaFieldsTemplate(/** @type {JxMutableNode} */ node) {
|
|
|
656
642
|
class="field-input"
|
|
657
643
|
placeholder="Name (e.g. Tablet)"
|
|
658
644
|
style="flex:1"
|
|
659
|
-
@input=${(
|
|
645
|
+
@input=${(e: Event) => {
|
|
660
646
|
view.addBreakpointPreview =
|
|
661
|
-
friendlyNameToMedia(
|
|
647
|
+
friendlyNameToMedia((e.target as HTMLInputElement).value) || "";
|
|
662
648
|
renderOnly("rightPanel");
|
|
663
649
|
}}
|
|
664
650
|
/>
|
|
@@ -674,13 +660,11 @@ function renderMediaFieldsTemplate(/** @type {JxMutableNode} */ node) {
|
|
|
674
660
|
<button
|
|
675
661
|
class="kv-add"
|
|
676
662
|
style="padding:2px 10px;cursor:pointer"
|
|
677
|
-
@click=${(
|
|
678
|
-
const wrap =
|
|
679
|
-
"div",
|
|
680
|
-
)?.parentElement;
|
|
663
|
+
@click=${(e: Event) => {
|
|
664
|
+
const wrap = (e.target as HTMLElement).closest("div")?.parentElement;
|
|
681
665
|
const nameVal = wrap?.querySelector("input")?.value;
|
|
682
|
-
const queryVal =
|
|
683
|
-
wrap?.querySelector(".add-bp-query")
|
|
666
|
+
const queryVal = (
|
|
667
|
+
wrap?.querySelector(".add-bp-query") as HTMLInputElement | null
|
|
684
668
|
)?.value?.trim();
|
|
685
669
|
const key = friendlyNameToMedia(nameVal || "");
|
|
686
670
|
if (key && queryVal) {
|
|
@@ -712,9 +696,9 @@ function renderMediaFieldsTemplate(/** @type {JxMutableNode} */ node) {
|
|
|
712
696
|
}
|
|
713
697
|
|
|
714
698
|
/** Single media breakpoint row template */
|
|
715
|
-
function mediaBreakpointRowTemplate(
|
|
699
|
+
function mediaBreakpointRowTemplate(name: string, query: string) {
|
|
716
700
|
/** @type {ReturnType<typeof setTimeout> | undefined} */
|
|
717
|
-
let debounceTimer;
|
|
701
|
+
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
|
718
702
|
let currentRawLabel = name;
|
|
719
703
|
return html`
|
|
720
704
|
<div style="margin-bottom:6px;padding:4px 0;border-bottom:1px solid var(--border)">
|
|
@@ -723,21 +707,17 @@ function mediaBreakpointRowTemplate(/** @type {string} */ name, /** @type {strin
|
|
|
723
707
|
class="field-input"
|
|
724
708
|
.value=${live(mediaDisplayName(name))}
|
|
725
709
|
style="flex:1;font-weight:600;font-size:12px"
|
|
726
|
-
@input=${(
|
|
727
|
-
const newKey = friendlyNameToMedia(
|
|
710
|
+
@input=${(e: Event) => {
|
|
711
|
+
const newKey = friendlyNameToMedia((e.target as HTMLInputElement).value);
|
|
728
712
|
currentRawLabel = newKey || "";
|
|
729
|
-
const rawEl =
|
|
730
|
-
".bp-raw-label",
|
|
731
|
-
);
|
|
713
|
+
const rawEl = (e.target as HTMLElement).parentElement?.querySelector(".bp-raw-label");
|
|
732
714
|
if (rawEl) rawEl.textContent = currentRawLabel;
|
|
733
715
|
clearTimeout(debounceTimer);
|
|
734
716
|
debounceTimer = setTimeout(() => {
|
|
735
717
|
if (newKey && newKey !== name) {
|
|
736
|
-
const queryEl =
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
?.parentElement?.querySelector(".bp-query-input")
|
|
740
|
-
);
|
|
718
|
+
const queryEl = (e.target as HTMLElement)
|
|
719
|
+
.closest("div[style]")
|
|
720
|
+
?.parentElement?.querySelector(".bp-query-input") as HTMLInputElement | null;
|
|
741
721
|
transactDoc(activeTab.value, (t) => {
|
|
742
722
|
mutateUpdateMedia(t, name, undefined);
|
|
743
723
|
mutateUpdateMedia(t, newKey, queryEl?.value || query);
|
|
@@ -762,12 +742,12 @@ function mediaBreakpointRowTemplate(/** @type {string} */ name, /** @type {strin
|
|
|
762
742
|
class="field-input bp-query-input"
|
|
763
743
|
.value=${live(query)}
|
|
764
744
|
style="flex:1"
|
|
765
|
-
@input=${(
|
|
745
|
+
@input=${(e: Event) => {
|
|
766
746
|
clearTimeout(debounceTimer);
|
|
767
747
|
debounceTimer = setTimeout(
|
|
768
748
|
() =>
|
|
769
749
|
transactDoc(activeTab.value, (t) =>
|
|
770
|
-
mutateUpdateMedia(t, name,
|
|
750
|
+
mutateUpdateMedia(t, name, (e.target as HTMLInputElement).value),
|
|
771
751
|
),
|
|
772
752
|
400,
|
|
773
753
|
);
|
|
@@ -781,18 +761,15 @@ function mediaBreakpointRowTemplate(/** @type {string} */ name, /** @type {strin
|
|
|
781
761
|
// ─── Layout picker ──────────────────────────────────────────────────────────
|
|
782
762
|
|
|
783
763
|
/** @type {{ name: string; path: string }[] | null} */
|
|
784
|
-
let layoutEntries = null;
|
|
764
|
+
let layoutEntries: { name: string; path: string }[] | null = null;
|
|
785
765
|
|
|
786
766
|
async function loadLayoutEntries() {
|
|
787
767
|
try {
|
|
788
768
|
const platform = getPlatform();
|
|
789
769
|
const listing = await platform.listDirectory("layouts");
|
|
790
770
|
layoutEntries = listing
|
|
791
|
-
.filter(
|
|
792
|
-
|
|
793
|
-
f.type === "file" && f.name.endsWith(".json"),
|
|
794
|
-
)
|
|
795
|
-
.map((/** @type {{ type: string; name: string }} */ f) => ({
|
|
771
|
+
.filter((f: DirEntry) => f.type === "file" && f.name.endsWith(".json"))
|
|
772
|
+
.map((f: DirEntry) => ({
|
|
796
773
|
name: f.name.replace(/\.json$/, ""),
|
|
797
774
|
path: `./layouts/${f.name}`,
|
|
798
775
|
}));
|
|
@@ -806,14 +783,14 @@ export function invalidateLayoutPickerCache() {
|
|
|
806
783
|
layoutEntries = null;
|
|
807
784
|
}
|
|
808
785
|
|
|
809
|
-
function isPageDocument(
|
|
786
|
+
function isPageDocument(documentPath: string | undefined | null) {
|
|
810
787
|
if (!documentPath || !projectState?.isSiteProject) return false;
|
|
811
788
|
return documentPath.startsWith("pages/") || documentPath.startsWith("./pages/");
|
|
812
789
|
}
|
|
813
790
|
|
|
814
|
-
function renderPageSection(
|
|
791
|
+
function renderPageSection(node: JxMutableNode) {
|
|
815
792
|
const tab = activeTab.value;
|
|
816
|
-
if (!isPageDocument(tab
|
|
793
|
+
if (!isPageDocument(tab!.documentPath)) return nothing;
|
|
817
794
|
|
|
818
795
|
if (layoutEntries === null) {
|
|
819
796
|
loadLayoutEntries();
|
|
@@ -835,7 +812,7 @@ function renderPageSection(/** @type {JxMutableNode} */ node) {
|
|
|
835
812
|
? html`<span
|
|
836
813
|
class="set-dot"
|
|
837
814
|
title="Reset to default"
|
|
838
|
-
@click=${(
|
|
815
|
+
@click=${(e: Event) => {
|
|
839
816
|
e.stopPropagation();
|
|
840
817
|
transactDoc(activeTab.value, (t) =>
|
|
841
818
|
mutateUpdateProperty(t, [], "$layout", undefined),
|
|
@@ -848,8 +825,8 @@ function renderPageSection(/** @type {JxMutableNode} */ node) {
|
|
|
848
825
|
<sp-picker
|
|
849
826
|
size="s"
|
|
850
827
|
value=${displayValue}
|
|
851
|
-
@change=${(
|
|
852
|
-
const val =
|
|
828
|
+
@change=${(e: Event) => {
|
|
829
|
+
const val = (e.target as HTMLInputElement).value;
|
|
853
830
|
if (val === "__default__") {
|
|
854
831
|
transactDoc(activeTab.value, (t) =>
|
|
855
832
|
mutateUpdateProperty(t, [], "$layout", undefined),
|
|
@@ -869,8 +846,8 @@ function renderPageSection(/** @type {JxMutableNode} */ node) {
|
|
|
869
846
|
>
|
|
870
847
|
<sp-menu-item value="__none__">None</sp-menu-item>
|
|
871
848
|
<sp-menu-divider></sp-menu-divider>
|
|
872
|
-
${layoutEntries
|
|
873
|
-
(
|
|
849
|
+
${layoutEntries!.map(
|
|
850
|
+
(l: { name: string; path: string }) =>
|
|
874
851
|
html`<sp-menu-item value=${l.path}>${l.name}</sp-menu-item>`,
|
|
875
852
|
)}
|
|
876
853
|
</sp-picker>
|
|
@@ -887,12 +864,8 @@ function renderPageSection(/** @type {JxMutableNode} */ node) {
|
|
|
887
864
|
|
|
888
865
|
// ─── Layout selection panel ─────────────────────────────────────────────────
|
|
889
866
|
|
|
890
|
-
function renderLayoutSelectionPanel(
|
|
891
|
-
|
|
892
|
-
) {
|
|
893
|
-
const { el, layoutPath } = /** @type {{ el: HTMLElement; layoutPath: string }} */ (
|
|
894
|
-
view.layoutSelection
|
|
895
|
-
);
|
|
867
|
+
function renderLayoutSelectionPanel(ctx: { navigateToComponent: (path: string) => void }) {
|
|
868
|
+
const { el, layoutPath } = view.layoutSelection as { el: HTMLElement; layoutPath: string };
|
|
896
869
|
const tagName = el?.tagName?.toLowerCase() || "element";
|
|
897
870
|
const className = el?.className || "";
|
|
898
871
|
const displayPath = layoutPath || "layout";
|
|
@@ -939,7 +912,9 @@ function renderLayoutSelectionPanel(
|
|
|
939
912
|
*
|
|
940
913
|
* @param {{ navigateToComponent: (path: string) => void }} ctx
|
|
941
914
|
*/
|
|
942
|
-
export function renderPropertiesPanelTemplate(ctx
|
|
915
|
+
export function renderPropertiesPanelTemplate(ctx: {
|
|
916
|
+
navigateToComponent: (path: string) => void;
|
|
917
|
+
}) {
|
|
943
918
|
const tab = activeTab.value;
|
|
944
919
|
if (!tab) return html`<div class="empty-state">No document loaded</div>`;
|
|
945
920
|
|
|
@@ -959,7 +934,7 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
959
934
|
const isMapParent =
|
|
960
935
|
node.children &&
|
|
961
936
|
typeof node.children === "object" &&
|
|
962
|
-
|
|
937
|
+
(node.children as unknown as { $prototype?: string }).$prototype === "Array";
|
|
963
938
|
const isSwitchNode = !!node.$switch;
|
|
964
939
|
const isCustomInstance = (node.tagName || "").includes("-");
|
|
965
940
|
const isRoot = path.length === 0;
|
|
@@ -973,11 +948,7 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
973
948
|
]
|
|
974
949
|
: null;
|
|
975
950
|
|
|
976
|
-
function renderAttrRow(
|
|
977
|
-
/** @type {string} */ attr,
|
|
978
|
-
/** @type {HtmlMetaEntry} */ entry,
|
|
979
|
-
/** @type {unknown} */ value,
|
|
980
|
-
) {
|
|
951
|
+
function renderAttrRow(attr: string, entry: HtmlMetaEntry, value: unknown) {
|
|
981
952
|
const type = inferInputType(entry);
|
|
982
953
|
const hasVal = value !== undefined && value !== "";
|
|
983
954
|
|
|
@@ -992,13 +963,13 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
992
963
|
<sp-checkbox
|
|
993
964
|
size="s"
|
|
994
965
|
.checked=${live(!!value)}
|
|
995
|
-
@change=${(
|
|
966
|
+
@change=${(e: Event) =>
|
|
996
967
|
transactDoc(activeTab.value, (t) =>
|
|
997
968
|
mutateUpdateAttribute(
|
|
998
969
|
t,
|
|
999
970
|
path,
|
|
1000
971
|
attr,
|
|
1001
|
-
|
|
972
|
+
(e.target as HTMLInputElement).checked ? "" : undefined,
|
|
1002
973
|
),
|
|
1003
974
|
)}
|
|
1004
975
|
>
|
|
@@ -1013,23 +984,21 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1013
984
|
hasValue: hasVal,
|
|
1014
985
|
onClear: () =>
|
|
1015
986
|
transactDoc(activeTab.value, (t) => mutateUpdateAttribute(t, path, attr, undefined)),
|
|
1016
|
-
widget: widgetForType(type, entry, attr, String(value || ""), (
|
|
1017
|
-
transactDoc(activeTab.value
|
|
987
|
+
widget: widgetForType(type, entry, attr, String(value || ""), (v: string) =>
|
|
988
|
+
transactDoc(activeTab.value!, (t) => mutateUpdateAttribute(t, path, attr, v || undefined)),
|
|
1018
989
|
),
|
|
1019
990
|
});
|
|
1020
991
|
}
|
|
1021
992
|
|
|
1022
993
|
// ── Collect applicable attributes from html-meta ──
|
|
1023
|
-
const applicableAttrs =
|
|
1024
|
-
for (const [attr, entry] of
|
|
1025
|
-
Object.entries(htmlMeta.$defs)
|
|
1026
|
-
)) {
|
|
994
|
+
const applicableAttrs = {} as Record<string, HtmlMetaEntry>;
|
|
995
|
+
for (const [attr, entry] of Object.entries(htmlMeta.$defs) as [string, HtmlMetaEntry][]) {
|
|
1027
996
|
if (!entry.$elements || entry.$elements.includes(tagName)) {
|
|
1028
997
|
applicableAttrs[attr] = entry;
|
|
1029
998
|
}
|
|
1030
999
|
}
|
|
1031
1000
|
|
|
1032
|
-
const attrSections
|
|
1001
|
+
const attrSections: Record<string, { name: string; entry: HtmlMetaEntry }[]> = {};
|
|
1033
1002
|
for (const sec of htmlMeta.$sections) attrSections[sec.key] = [];
|
|
1034
1003
|
for (const [attr, entry] of Object.entries(applicableAttrs)) {
|
|
1035
1004
|
const secKey = entry.$section;
|
|
@@ -1037,10 +1006,8 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1037
1006
|
}
|
|
1038
1007
|
for (const sec of htmlMeta.$sections) {
|
|
1039
1008
|
attrSections[sec.key].sort(
|
|
1040
|
-
(
|
|
1041
|
-
|
|
1042
|
-
/** @type {{ name: string; entry: HtmlMetaEntry }} */ b,
|
|
1043
|
-
) => a.entry.$order - b.entry.$order,
|
|
1009
|
+
(a: { name: string; entry: HtmlMetaEntry }, b: { name: string; entry: HtmlMetaEntry }) =>
|
|
1010
|
+
a.entry.$order - b.entry.$order,
|
|
1044
1011
|
);
|
|
1045
1012
|
}
|
|
1046
1013
|
|
|
@@ -1058,16 +1025,16 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1058
1025
|
}
|
|
1059
1026
|
if (customAttrs.length > 0) autoOpen.add("__custom");
|
|
1060
1027
|
|
|
1061
|
-
function isSectionOpen(
|
|
1062
|
-
if (tab
|
|
1063
|
-
return tab
|
|
1028
|
+
function isSectionOpen(key: string) {
|
|
1029
|
+
if (tab!.session.ui.inspectorSections[key] !== undefined)
|
|
1030
|
+
return tab!.session.ui.inspectorSections[key];
|
|
1064
1031
|
return autoOpen.has(key);
|
|
1065
1032
|
}
|
|
1066
1033
|
|
|
1067
|
-
function toggleSection(
|
|
1034
|
+
function toggleSection(key: string) {
|
|
1068
1035
|
const current = isSectionOpen(key);
|
|
1069
|
-
activeTab.value
|
|
1070
|
-
...activeTab.value
|
|
1036
|
+
activeTab.value!.session.ui.inspectorSections = {
|
|
1037
|
+
...activeTab.value!.session.ui.inspectorSections,
|
|
1071
1038
|
[key]: !current,
|
|
1072
1039
|
};
|
|
1073
1040
|
}
|
|
@@ -1090,13 +1057,13 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1090
1057
|
.value=${live(tagName)}
|
|
1091
1058
|
autocomplete="off"
|
|
1092
1059
|
list="tag-names"
|
|
1093
|
-
@input=${debouncedStyleCommit("prop:tagName", 400, (
|
|
1060
|
+
@input=${debouncedStyleCommit("prop:tagName", 400, (e: Event) => {
|
|
1094
1061
|
transactDoc(activeTab.value, (t) =>
|
|
1095
1062
|
mutateUpdateProperty(
|
|
1096
1063
|
t,
|
|
1097
1064
|
path,
|
|
1098
1065
|
"tagName",
|
|
1099
|
-
|
|
1066
|
+
(e.target as HTMLInputElement).value || undefined,
|
|
1100
1067
|
),
|
|
1101
1068
|
);
|
|
1102
1069
|
})}
|
|
@@ -1108,7 +1075,7 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1108
1075
|
? html`<span
|
|
1109
1076
|
class="set-dot"
|
|
1110
1077
|
title="Clear $id"
|
|
1111
|
-
@click=${(
|
|
1078
|
+
@click=${(e: Event) => {
|
|
1112
1079
|
e.stopPropagation();
|
|
1113
1080
|
transactDoc(activeTab.value, (t) =>
|
|
1114
1081
|
mutateUpdateProperty(t, path, "$id", undefined),
|
|
@@ -1121,13 +1088,13 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1121
1088
|
<sp-textfield
|
|
1122
1089
|
size="s"
|
|
1123
1090
|
.value=${live(node.$id || "")}
|
|
1124
|
-
@input=${debouncedStyleCommit("prop:$id", 400, (
|
|
1091
|
+
@input=${debouncedStyleCommit("prop:$id", 400, (e: Event) => {
|
|
1125
1092
|
transactDoc(activeTab.value, (t) =>
|
|
1126
1093
|
mutateUpdateProperty(
|
|
1127
1094
|
t,
|
|
1128
1095
|
path,
|
|
1129
1096
|
"$id",
|
|
1130
|
-
|
|
1097
|
+
(e.target as HTMLInputElement).value || undefined,
|
|
1131
1098
|
),
|
|
1132
1099
|
);
|
|
1133
1100
|
})}
|
|
@@ -1139,7 +1106,7 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1139
1106
|
? html`<span
|
|
1140
1107
|
class="set-dot"
|
|
1141
1108
|
title="Clear class"
|
|
1142
|
-
@click=${(
|
|
1109
|
+
@click=${(e: Event) => {
|
|
1143
1110
|
e.stopPropagation();
|
|
1144
1111
|
transactDoc(activeTab.value, (t) =>
|
|
1145
1112
|
mutateUpdateProperty(t, path, "className", undefined),
|
|
@@ -1152,13 +1119,13 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1152
1119
|
<sp-textfield
|
|
1153
1120
|
size="s"
|
|
1154
1121
|
.value=${live(node.className || "")}
|
|
1155
|
-
@input=${debouncedStyleCommit("prop:className", 400, (
|
|
1122
|
+
@input=${debouncedStyleCommit("prop:className", 400, (e: Event) => {
|
|
1156
1123
|
transactDoc(activeTab.value, (t) =>
|
|
1157
1124
|
mutateUpdateProperty(
|
|
1158
1125
|
t,
|
|
1159
1126
|
path,
|
|
1160
1127
|
"className",
|
|
1161
|
-
|
|
1128
|
+
(e.target as HTMLInputElement).value || undefined,
|
|
1162
1129
|
),
|
|
1163
1130
|
);
|
|
1164
1131
|
})}
|
|
@@ -1172,7 +1139,7 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1172
1139
|
? html`<span
|
|
1173
1140
|
class="set-dot"
|
|
1174
1141
|
title="Clear text"
|
|
1175
|
-
@click=${(
|
|
1142
|
+
@click=${(e: Event) => {
|
|
1176
1143
|
e.stopPropagation();
|
|
1177
1144
|
transactDoc(activeTab.value, (t) =>
|
|
1178
1145
|
mutateUpdateProperty(t, path, "textContent", undefined),
|
|
@@ -1190,20 +1157,16 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1190
1157
|
? node.textContent
|
|
1191
1158
|
: (node.textContent ?? ""),
|
|
1192
1159
|
)}
|
|
1193
|
-
@input=${debouncedStyleCommit(
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
),
|
|
1204
|
-
);
|
|
1205
|
-
},
|
|
1206
|
-
)}
|
|
1160
|
+
@input=${debouncedStyleCommit("prop:textContent", 400, (e: Event) => {
|
|
1161
|
+
transactDoc(activeTab.value, (t) =>
|
|
1162
|
+
mutateUpdateProperty(
|
|
1163
|
+
t,
|
|
1164
|
+
path,
|
|
1165
|
+
"textContent",
|
|
1166
|
+
(e.target as HTMLInputElement).value || undefined,
|
|
1167
|
+
),
|
|
1168
|
+
);
|
|
1169
|
+
})}
|
|
1207
1170
|
></sp-textfield>
|
|
1208
1171
|
</div>
|
|
1209
1172
|
`
|
|
@@ -1214,7 +1177,7 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1214
1177
|
? html`<span
|
|
1215
1178
|
class="set-dot"
|
|
1216
1179
|
title="Clear hidden"
|
|
1217
|
-
@click=${(
|
|
1180
|
+
@click=${(e: Event) => {
|
|
1218
1181
|
e.stopPropagation();
|
|
1219
1182
|
transactDoc(activeTab.value, (t) =>
|
|
1220
1183
|
mutateUpdateProperty(t, path, "hidden", undefined),
|
|
@@ -1227,13 +1190,13 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1227
1190
|
<sp-checkbox
|
|
1228
1191
|
size="s"
|
|
1229
1192
|
.checked=${live(!!node.hidden)}
|
|
1230
|
-
@change=${(
|
|
1193
|
+
@change=${(e: Event) =>
|
|
1231
1194
|
transactDoc(activeTab.value, (t) =>
|
|
1232
1195
|
mutateUpdateProperty(
|
|
1233
1196
|
t,
|
|
1234
1197
|
path,
|
|
1235
1198
|
"hidden",
|
|
1236
|
-
|
|
1199
|
+
(e.target as HTMLInputElement).checked || undefined,
|
|
1237
1200
|
),
|
|
1238
1201
|
)}
|
|
1239
1202
|
>
|
|
@@ -1274,7 +1237,9 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1274
1237
|
isCustomElementDoc({ document: tab.doc.document }) && isRoot
|
|
1275
1238
|
? (() => {
|
|
1276
1239
|
const state = tab.doc.document.state || {};
|
|
1277
|
-
const entries = Object.entries(state).filter(
|
|
1240
|
+
const entries = Object.entries(state).filter(
|
|
1241
|
+
([, d]) => (d as Record<string, unknown>).attribute,
|
|
1242
|
+
);
|
|
1278
1243
|
return html`
|
|
1279
1244
|
<sp-accordion-item label="Observed Attributes" ?open=${isSectionOpen("__observed")}>
|
|
1280
1245
|
<div class="style-section-body">
|
|
@@ -1282,30 +1247,31 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1282
1247
|
? html`<div class="empty-state">
|
|
1283
1248
|
No attributes declared. Set "attribute" on a state entry.
|
|
1284
1249
|
</div>`
|
|
1285
|
-
: entries.map(
|
|
1286
|
-
|
|
1250
|
+
: entries.map(([key, d]) => {
|
|
1251
|
+
const def = d as Record<string, unknown>;
|
|
1252
|
+
return html`
|
|
1287
1253
|
<div
|
|
1288
1254
|
style="display:flex;gap:6px;align-items:center;padding:2px 0;font-size:11px"
|
|
1289
1255
|
>
|
|
1290
1256
|
<code style="font-family:monospace;color:var(--accent)"
|
|
1291
|
-
>${
|
|
1257
|
+
>${def.attribute}</code
|
|
1292
1258
|
>
|
|
1293
1259
|
<span style="color:var(--fg-dim)"> → </span>
|
|
1294
1260
|
<span>${key}</span>
|
|
1295
|
-
${
|
|
1261
|
+
${def.type
|
|
1296
1262
|
? html`<span style="margin-left:auto;color:var(--fg-dim);font-size:10px"
|
|
1297
|
-
>${
|
|
1263
|
+
>${def.type}</span
|
|
1298
1264
|
>`
|
|
1299
1265
|
: nothing}
|
|
1300
|
-
${
|
|
1266
|
+
${def.reflects
|
|
1301
1267
|
? html`<span
|
|
1302
1268
|
style="font-size:9px;background:var(--bg-hover);padding:1px 4px;border-radius:3px"
|
|
1303
1269
|
>reflects</span
|
|
1304
1270
|
>`
|
|
1305
1271
|
: nothing}
|
|
1306
1272
|
</div>
|
|
1307
|
-
|
|
1308
|
-
)}
|
|
1273
|
+
`;
|
|
1274
|
+
})}
|
|
1309
1275
|
</div>
|
|
1310
1276
|
</sp-accordion-item>
|
|
1311
1277
|
`;
|
|
@@ -1327,7 +1293,7 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1327
1293
|
.map((sec) => {
|
|
1328
1294
|
const sectionAttrs = attrSections[sec.key];
|
|
1329
1295
|
const hasAnySet = sectionAttrs.some(
|
|
1330
|
-
(
|
|
1296
|
+
(a: { name: string; entry: HtmlMetaEntry }) => attrs[a.name] !== undefined,
|
|
1331
1297
|
);
|
|
1332
1298
|
return html`
|
|
1333
1299
|
<sp-accordion-item
|
|
@@ -1339,7 +1305,7 @@ export function renderPropertiesPanelTemplate(ctx) {
|
|
|
1339
1305
|
? html`<span slot="heading" class="set-dot set-dot--section"></span>`
|
|
1340
1306
|
: nothing}
|
|
1341
1307
|
<div class="style-section-body">
|
|
1342
|
-
${sectionAttrs.map((
|
|
1308
|
+
${sectionAttrs.map((a: { name: string; entry: HtmlMetaEntry }) =>
|
|
1343
1309
|
renderAttrRow(a.name, a.entry, attrs[a.name]),
|
|
1344
1310
|
)}
|
|
1345
1311
|
</div>
|