@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
|
@@ -3,32 +3,33 @@
|
|
|
3
3
|
|
|
4
4
|
import { html, nothing } from "lit-html";
|
|
5
5
|
import { live } from "lit-html/directives/live.js";
|
|
6
|
-
import { getNodeAtPath,
|
|
7
|
-
import
|
|
6
|
+
import { debouncedStyleCommit, getNodeAtPath, projectState, renderOnly } from "../store";
|
|
7
|
+
import { isRef } from "@jxsuite/schema/guards";
|
|
8
|
+
import type { DirEntry, JsonValue } from "../types";
|
|
8
9
|
import {
|
|
9
|
-
transactDoc,
|
|
10
|
-
mutateUpdateProperty,
|
|
11
|
-
mutateUpdateAttribute,
|
|
12
|
-
mutateUpdateProp,
|
|
13
|
-
mutateUpdateMedia,
|
|
14
10
|
mutateAddSwitchCase,
|
|
15
11
|
mutateRemoveSwitchCase,
|
|
16
12
|
mutateRenameSwitchCase,
|
|
13
|
+
mutateUpdateAttribute,
|
|
14
|
+
mutateUpdateMedia,
|
|
15
|
+
mutateUpdateProp,
|
|
16
|
+
mutateUpdateProperty,
|
|
17
|
+
transactDoc,
|
|
17
18
|
} from "../tabs/transact";
|
|
18
19
|
import { activeTab } from "../workspace/workspace";
|
|
19
20
|
import { view } from "../view";
|
|
20
21
|
import { componentRegistry } from "../files/components";
|
|
21
22
|
import { widgetForType } from "./style-inputs";
|
|
22
23
|
import { renderFieldRow } from "../ui/field-row";
|
|
23
|
-
import {
|
|
24
|
+
import { spTextArea, spTextField } from "../ui/field-input";
|
|
24
25
|
import {
|
|
25
26
|
attrLabel,
|
|
26
|
-
inferInputType,
|
|
27
|
-
friendlyNameToVar,
|
|
28
27
|
camelToLabel,
|
|
28
|
+
friendlyNameToVar,
|
|
29
|
+
inferInputType,
|
|
29
30
|
parseCemType,
|
|
30
31
|
} from "../utils/studio-utils";
|
|
31
|
-
import {
|
|
32
|
+
import { collectCssParts, isCustomElementDoc } from "./signals-panel";
|
|
32
33
|
import { mediaDisplayName } from "./shared";
|
|
33
34
|
import { getCssInitialMap } from "./style-utils";
|
|
34
35
|
import { renderMediaPicker } from "../ui/media-picker";
|
|
@@ -37,8 +38,14 @@ import { getEffectiveLayoutPath, invalidateLayoutCache } from "../site-context";
|
|
|
37
38
|
import { getPlatform } from "../platform";
|
|
38
39
|
import htmlMeta from "../../data/html-meta.json";
|
|
39
40
|
|
|
40
|
-
import type {
|
|
41
|
+
import type {
|
|
42
|
+
JxMutableNode,
|
|
43
|
+
JxPrototypeDef,
|
|
44
|
+
JxStateDefinition,
|
|
45
|
+
JxStateObject,
|
|
46
|
+
} from "@jxsuite/schema/types";
|
|
41
47
|
import type { JxPath } from "../state";
|
|
48
|
+
import type { SignalDef } from "./signals-panel.js";
|
|
42
49
|
|
|
43
50
|
interface SignalOption {
|
|
44
51
|
value: string;
|
|
@@ -67,32 +74,54 @@ function friendlyNameToMedia(name: string) {
|
|
|
67
74
|
|
|
68
75
|
/** Check if a selection path is inside a $map template (contains [..., "children", "map", ...]). */
|
|
69
76
|
function isInsideMapTemplate(path: JxPath | null) {
|
|
70
|
-
if (!path)
|
|
77
|
+
if (!path) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
71
80
|
for (let i = 0; i < path.length - 1; i++) {
|
|
72
|
-
if (path[i] === "children" && path[i + 1] === "map")
|
|
81
|
+
if (path[i] === "children" && path[i + 1] === "map") {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
73
84
|
}
|
|
74
85
|
return false;
|
|
75
86
|
}
|
|
76
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Render a state entry's default value as the static input text when unbinding.
|
|
90
|
+
*
|
|
91
|
+
* @param {import("@jxsuite/schema/types").JxStateDefinition | undefined} def
|
|
92
|
+
* @returns {string}
|
|
93
|
+
*/
|
|
94
|
+
function defaultAsString(def: JxStateDefinition | undefined) {
|
|
95
|
+
if (!def || typeof def !== "object" || Array.isArray(def)) {
|
|
96
|
+
return "";
|
|
97
|
+
}
|
|
98
|
+
const dv = (def as JxStateObject).default;
|
|
99
|
+
if (dv === undefined) {
|
|
100
|
+
return "";
|
|
101
|
+
}
|
|
102
|
+
return typeof dv === "object" ? JSON.stringify(dv) : String(dv);
|
|
103
|
+
}
|
|
104
|
+
|
|
77
105
|
/**
|
|
78
106
|
* Field row with binding toggle — allows switching between static value and signal binding.
|
|
79
|
-
* rawValue can be a
|
|
107
|
+
* rawValue can be a JSON literal (static) or { $ref: "..." } (bound).
|
|
80
108
|
*/
|
|
81
109
|
function bindableFieldRow(
|
|
82
110
|
label: string,
|
|
83
111
|
type: string,
|
|
84
|
-
rawValue:
|
|
85
|
-
onChange: (v
|
|
86
|
-
filterFn: ((d:
|
|
112
|
+
rawValue: unknown,
|
|
113
|
+
onChange: (v?: JsonValue) => void,
|
|
114
|
+
filterFn: ((d: SignalDef) => boolean) | null = null,
|
|
87
115
|
extraSignals: SignalOption[] | null = null,
|
|
88
116
|
) {
|
|
89
117
|
const tab = activeTab.value;
|
|
90
118
|
const defs = tab!.doc.document.state || {};
|
|
91
|
-
const
|
|
119
|
+
const boundRef = isRef(rawValue) ? rawValue.$ref : null;
|
|
120
|
+
const isBound = boundRef !== null;
|
|
92
121
|
|
|
93
122
|
const signalDefs = Object.entries(defs).filter(([, d]) =>
|
|
94
123
|
filterFn
|
|
95
|
-
? filterFn(d as
|
|
124
|
+
? filterFn(d as SignalDef)
|
|
96
125
|
: !(d as Record<string, unknown>)?.$handler &&
|
|
97
126
|
(d as JxPrototypeDef)?.$prototype !== "Function",
|
|
98
127
|
);
|
|
@@ -104,7 +133,7 @@ function bindableFieldRow(
|
|
|
104
133
|
? spTextArea(fieldKey, String(staticVal), (v: string) => onChange(v))
|
|
105
134
|
: type === "checkbox"
|
|
106
135
|
? html`<sp-checkbox
|
|
107
|
-
?checked=${
|
|
136
|
+
?checked=${Boolean(staticVal)}
|
|
108
137
|
@change=${(e: Event) => onChange((e.target as HTMLInputElement).checked)}
|
|
109
138
|
></sp-checkbox>`
|
|
110
139
|
: spTextField(fieldKey, String(staticVal), (v: string) => onChange(v));
|
|
@@ -114,11 +143,13 @@ function bindableFieldRow(
|
|
|
114
143
|
size="s"
|
|
115
144
|
quiet
|
|
116
145
|
placeholder="— select signal —"
|
|
117
|
-
value=${
|
|
146
|
+
value=${boundRef ?? nothing}
|
|
118
147
|
@change=${(e: Event) => {
|
|
119
|
-
if ((e.target as HTMLInputElement).value)
|
|
148
|
+
if ((e.target as HTMLInputElement).value) {
|
|
120
149
|
onChange({ $ref: (e.target as HTMLInputElement).value });
|
|
121
|
-
else
|
|
150
|
+
} else {
|
|
151
|
+
onChange();
|
|
152
|
+
}
|
|
122
153
|
}}
|
|
123
154
|
>
|
|
124
155
|
${signalDefs.map(
|
|
@@ -137,21 +168,14 @@ function bindableFieldRow(
|
|
|
137
168
|
`;
|
|
138
169
|
|
|
139
170
|
const onToggle = () => {
|
|
140
|
-
if (
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
onChange(staticVal || undefined);
|
|
149
|
-
} else {
|
|
150
|
-
if (signalDefs.length > 0) {
|
|
151
|
-
onChange({ $ref: `#/state/${signalDefs[0][0]}` });
|
|
152
|
-
} else if (extraSignals && extraSignals.length > 0) {
|
|
153
|
-
onChange({ $ref: extraSignals[0].value });
|
|
154
|
-
}
|
|
171
|
+
if (boundRef !== null) {
|
|
172
|
+
const defName = boundRef.startsWith("#/state/") ? boundRef.slice(8) : boundRef;
|
|
173
|
+
const staticDefault = defaultAsString(defs[defName]);
|
|
174
|
+
onChange(staticDefault || undefined);
|
|
175
|
+
} else if (signalDefs.length > 0) {
|
|
176
|
+
onChange({ $ref: `#/state/${signalDefs[0][0]}` });
|
|
177
|
+
} else if (extraSignals && extraSignals.length > 0) {
|
|
178
|
+
onChange({ $ref: extraSignals[0].value });
|
|
155
179
|
}
|
|
156
180
|
};
|
|
157
181
|
|
|
@@ -202,11 +226,12 @@ function kvRow(
|
|
|
202
226
|
const el = (e.target as HTMLInputElement)
|
|
203
227
|
.closest(".kv-row")
|
|
204
228
|
?.querySelector(".kv-val");
|
|
205
|
-
if (el)
|
|
229
|
+
if (el) {
|
|
206
230
|
el.setAttribute(
|
|
207
231
|
"placeholder",
|
|
208
232
|
getCssInitialMap().get((e.target as HTMLInputElement).value) || "",
|
|
209
233
|
);
|
|
234
|
+
}
|
|
210
235
|
}
|
|
211
236
|
: nothing}
|
|
212
237
|
></sp-textfield>
|
|
@@ -327,10 +352,11 @@ function renderSwitchFieldsTemplate(
|
|
|
327
352
|
if (
|
|
328
353
|
(e.target as HTMLInputElement).value &&
|
|
329
354
|
(e.target as HTMLInputElement).value !== caseName
|
|
330
|
-
)
|
|
355
|
+
) {
|
|
331
356
|
transactDoc(activeTab.value, (t) =>
|
|
332
357
|
mutateRenameSwitchCase(t, path, caseName, (e.target as HTMLInputElement).value),
|
|
333
358
|
);
|
|
359
|
+
}
|
|
334
360
|
}, 500);
|
|
335
361
|
}}
|
|
336
362
|
/>
|
|
@@ -376,15 +402,17 @@ function renderComponentPropsFieldsTemplate(
|
|
|
376
402
|
) {
|
|
377
403
|
const tab = activeTab.value;
|
|
378
404
|
const comp = componentRegistry.find((c) => c.tagName === node.tagName);
|
|
379
|
-
if (!comp || !comp.props)
|
|
405
|
+
if (!comp || !comp.props) {
|
|
406
|
+
return html`<div class="empty-state">Component not found</div>`;
|
|
407
|
+
}
|
|
380
408
|
const isNpm = comp.source === "npm";
|
|
381
409
|
const currentVals = isNpm ? node.attributes || {} : node.$props || {};
|
|
382
410
|
const updateFn = isNpm
|
|
383
|
-
? (name: string, v
|
|
411
|
+
? (name: string, v?: JsonValue) =>
|
|
384
412
|
transactDoc(activeTab.value, (t) =>
|
|
385
413
|
mutateUpdateAttribute(t, path, name, v === "" ? undefined : (v as string | undefined)),
|
|
386
414
|
)
|
|
387
|
-
: (name: string, v
|
|
415
|
+
: (name: string, v?: JsonValue) =>
|
|
388
416
|
transactDoc(activeTab.value, (t) => mutateUpdateProp(t, path, name, v));
|
|
389
417
|
|
|
390
418
|
const defs = tab!.doc.document.state || {};
|
|
@@ -400,33 +428,27 @@ function renderComponentPropsFieldsTemplate(
|
|
|
400
428
|
/** @type {{ name: string; type?: string; format?: string; description?: string }} */ prop,
|
|
401
429
|
) => {
|
|
402
430
|
const rawValue = currentVals[prop.name];
|
|
403
|
-
const
|
|
431
|
+
const boundRef = isRef(rawValue) ? rawValue.$ref : null;
|
|
432
|
+
const isBound = boundRef !== null;
|
|
404
433
|
const hasVal = rawValue !== undefined && rawValue !== null;
|
|
405
434
|
const parsed = parseCemType(prop.type);
|
|
406
|
-
const onChange = (v
|
|
435
|
+
const onChange = (v?: JsonValue) => updateFn(prop.name, v);
|
|
407
436
|
const staticVal = isBound ? "" : String(rawValue ?? "");
|
|
408
437
|
|
|
409
438
|
const clearProp = (e: Event) => {
|
|
410
439
|
e.stopPropagation();
|
|
411
|
-
updateFn(prop.name
|
|
440
|
+
updateFn(prop.name);
|
|
412
441
|
};
|
|
413
442
|
|
|
414
443
|
const onToggleBind = () => {
|
|
415
|
-
if (
|
|
416
|
-
const
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
onChange(staticVal || undefined);
|
|
424
|
-
} else {
|
|
425
|
-
if (signalDefs.length > 0) {
|
|
426
|
-
onChange({ $ref: `#/state/${signalDefs[0][0]}` });
|
|
427
|
-
} else if (extraSignals && extraSignals.length > 0) {
|
|
428
|
-
onChange({ $ref: extraSignals[0].value });
|
|
429
|
-
}
|
|
444
|
+
if (boundRef !== null) {
|
|
445
|
+
const defName = boundRef.startsWith("#/state/") ? boundRef.slice(8) : boundRef;
|
|
446
|
+
const staticDefault = defaultAsString(defs[defName]);
|
|
447
|
+
onChange(staticDefault || undefined);
|
|
448
|
+
} else if (signalDefs.length > 0) {
|
|
449
|
+
onChange({ $ref: `#/state/${signalDefs[0][0]}` });
|
|
450
|
+
} else if (extraSignals && extraSignals.length > 0) {
|
|
451
|
+
onChange({ $ref: extraSignals[0].value });
|
|
430
452
|
}
|
|
431
453
|
};
|
|
432
454
|
|
|
@@ -435,11 +457,13 @@ function renderComponentPropsFieldsTemplate(
|
|
|
435
457
|
size="s"
|
|
436
458
|
quiet
|
|
437
459
|
placeholder="— select signal —"
|
|
438
|
-
value=${
|
|
460
|
+
value=${boundRef ?? nothing}
|
|
439
461
|
@change=${(e: Event) => {
|
|
440
|
-
if ((e.target as HTMLInputElement).value)
|
|
462
|
+
if ((e.target as HTMLInputElement).value) {
|
|
441
463
|
onChange({ $ref: (e.target as HTMLInputElement).value });
|
|
442
|
-
else
|
|
464
|
+
} else {
|
|
465
|
+
onChange();
|
|
466
|
+
}
|
|
443
467
|
}}
|
|
444
468
|
>
|
|
445
469
|
${signalDefs.map(
|
|
@@ -477,7 +501,7 @@ function renderComponentPropsFieldsTemplate(
|
|
|
477
501
|
} else if (parsed.kind === "boolean") {
|
|
478
502
|
widgetTpl = html`<sp-checkbox
|
|
479
503
|
size="s"
|
|
480
|
-
.checked=${live(
|
|
504
|
+
.checked=${live(Boolean(staticVal))}
|
|
481
505
|
@change=${(e: Event) => onChange((e.target as HTMLInputElement).checked || undefined)}
|
|
482
506
|
></sp-checkbox>`;
|
|
483
507
|
} else if (parsed.kind === "number") {
|
|
@@ -496,8 +520,8 @@ function renderComponentPropsFieldsTemplate(
|
|
|
496
520
|
size="s"
|
|
497
521
|
placeholder="—"
|
|
498
522
|
.options=${options.map((o) => ({
|
|
499
|
-
value: o,
|
|
500
523
|
label: camelToLabel(o),
|
|
524
|
+
value: o,
|
|
501
525
|
}))}
|
|
502
526
|
@change=${(e: Event & { detail?: { value?: string } }) =>
|
|
503
527
|
onChange(e.detail?.value ?? (e.target as HTMLInputElement).value)}
|
|
@@ -532,7 +556,7 @@ function renderComponentPropsFieldsTemplate(
|
|
|
532
556
|
)}
|
|
533
557
|
${comp.props.length === 0 ? html`<div class="empty-state">No props defined</div>` : nothing}
|
|
534
558
|
${comp.path
|
|
535
|
-
? html`<span class="kv-add" @click=${() => navigateToComponent(comp.path)}
|
|
559
|
+
? html`<span class="kv-add" @click=${() => navigateToComponent(comp.path!)}
|
|
536
560
|
>→ Edit definition</span
|
|
537
561
|
>`
|
|
538
562
|
: nothing}
|
|
@@ -555,14 +579,14 @@ function renderCustomAttrsFieldsTemplate(
|
|
|
555
579
|
(newAttr: string, newVal: string) => {
|
|
556
580
|
if (newAttr !== attr) {
|
|
557
581
|
transactDoc(activeTab.value, (t) => {
|
|
558
|
-
mutateUpdateAttribute(t, path, attr
|
|
582
|
+
mutateUpdateAttribute(t, path, attr);
|
|
559
583
|
mutateUpdateAttribute(t, path, newAttr, newVal);
|
|
560
584
|
});
|
|
561
585
|
} else {
|
|
562
586
|
transactDoc(activeTab.value, (t) => mutateUpdateAttribute(t, path, attr, newVal));
|
|
563
587
|
}
|
|
564
588
|
},
|
|
565
|
-
() => transactDoc(activeTab.value, (t) => mutateUpdateAttribute(t, path, attr
|
|
589
|
+
() => transactDoc(activeTab.value, (t) => mutateUpdateAttribute(t, path, attr)),
|
|
566
590
|
),
|
|
567
591
|
)}
|
|
568
592
|
<span
|
|
@@ -602,8 +626,7 @@ function renderMediaFieldsTemplate(node: JxMutableNode) {
|
|
|
602
626
|
${media["--"]
|
|
603
627
|
? html`<span
|
|
604
628
|
class="kv-del"
|
|
605
|
-
@click=${() =>
|
|
606
|
-
transactDoc(activeTab.value, (t) => mutateUpdateMedia(t, "--", undefined))}
|
|
629
|
+
@click=${() => transactDoc(activeTab.value, (t) => mutateUpdateMedia(t, "--"))}
|
|
607
630
|
>✕</span
|
|
608
631
|
>`
|
|
609
632
|
: nothing}
|
|
@@ -698,7 +721,9 @@ function mediaBreakpointRowTemplate(name: string, query: string) {
|
|
|
698
721
|
const newKey = friendlyNameToMedia((e.target as HTMLInputElement).value);
|
|
699
722
|
currentRawLabel = newKey || "";
|
|
700
723
|
const rawEl = (e.target as HTMLElement).parentElement?.querySelector(".bp-raw-label");
|
|
701
|
-
if (rawEl)
|
|
724
|
+
if (rawEl) {
|
|
725
|
+
rawEl.textContent = currentRawLabel;
|
|
726
|
+
}
|
|
702
727
|
clearTimeout(debounceTimer);
|
|
703
728
|
debounceTimer = setTimeout(() => {
|
|
704
729
|
if (newKey && newKey !== name) {
|
|
@@ -706,7 +731,7 @@ function mediaBreakpointRowTemplate(name: string, query: string) {
|
|
|
706
731
|
.closest("div[style]")
|
|
707
732
|
?.parentElement?.querySelector(".bp-query-input") as HTMLInputElement | null;
|
|
708
733
|
transactDoc(activeTab.value, (t) => {
|
|
709
|
-
mutateUpdateMedia(t, name
|
|
734
|
+
mutateUpdateMedia(t, name);
|
|
710
735
|
mutateUpdateMedia(t, newKey, queryEl?.value || query);
|
|
711
736
|
});
|
|
712
737
|
}
|
|
@@ -720,7 +745,7 @@ function mediaBreakpointRowTemplate(name: string, query: string) {
|
|
|
720
745
|
>
|
|
721
746
|
<span
|
|
722
747
|
class="kv-del"
|
|
723
|
-
@click=${() => transactDoc(activeTab.value, (t) => mutateUpdateMedia(t, name
|
|
748
|
+
@click=${() => transactDoc(activeTab.value, (t) => mutateUpdateMedia(t, name))}
|
|
724
749
|
>✕</span
|
|
725
750
|
>
|
|
726
751
|
</div>
|
|
@@ -771,13 +796,17 @@ export function invalidateLayoutPickerCache() {
|
|
|
771
796
|
}
|
|
772
797
|
|
|
773
798
|
function isPageDocument(documentPath: string | undefined | null) {
|
|
774
|
-
if (!documentPath || !projectState?.isSiteProject)
|
|
799
|
+
if (!documentPath || !projectState?.isSiteProject) {
|
|
800
|
+
return false;
|
|
801
|
+
}
|
|
775
802
|
return documentPath.startsWith("pages/") || documentPath.startsWith("./pages/");
|
|
776
803
|
}
|
|
777
804
|
|
|
778
805
|
function renderPageSection(node: JxMutableNode) {
|
|
779
806
|
const tab = activeTab.value;
|
|
780
|
-
if (!isPageDocument(tab!.documentPath))
|
|
807
|
+
if (!isPageDocument(tab!.documentPath)) {
|
|
808
|
+
return nothing;
|
|
809
|
+
}
|
|
781
810
|
|
|
782
811
|
if (layoutEntries === null) {
|
|
783
812
|
loadLayoutEntries();
|
|
@@ -787,8 +816,7 @@ function renderPageSection(node: JxMutableNode) {
|
|
|
787
816
|
const currentLayout = node.$layout;
|
|
788
817
|
const defaultLayout = projectState?.projectConfig?.defaults?.layout;
|
|
789
818
|
const effectivePath = getEffectiveLayoutPath(currentLayout);
|
|
790
|
-
const displayValue =
|
|
791
|
-
currentLayout === false ? "__none__" : currentLayout ? currentLayout : "__default__";
|
|
819
|
+
const displayValue = currentLayout === false ? "__none__" : currentLayout || "__default__";
|
|
792
820
|
|
|
793
821
|
return html`
|
|
794
822
|
<sp-accordion-item label="Page" open>
|
|
@@ -801,9 +829,7 @@ function renderPageSection(node: JxMutableNode) {
|
|
|
801
829
|
title="Reset to default"
|
|
802
830
|
@click=${(e: Event) => {
|
|
803
831
|
e.stopPropagation();
|
|
804
|
-
transactDoc(activeTab.value, (t) =>
|
|
805
|
-
mutateUpdateProperty(t, [], "$layout", undefined),
|
|
806
|
-
);
|
|
832
|
+
transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, [], "$layout"));
|
|
807
833
|
}}
|
|
808
834
|
></span>`
|
|
809
835
|
: nothing}
|
|
@@ -815,9 +841,7 @@ function renderPageSection(node: JxMutableNode) {
|
|
|
815
841
|
@change=${(e: Event) => {
|
|
816
842
|
const val = (e.target as HTMLInputElement).value;
|
|
817
843
|
if (val === "__default__") {
|
|
818
|
-
transactDoc(activeTab.value, (t) =>
|
|
819
|
-
mutateUpdateProperty(t, [], "$layout", undefined),
|
|
820
|
-
);
|
|
844
|
+
transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, [], "$layout"));
|
|
821
845
|
} else if (val === "__none__") {
|
|
822
846
|
transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, [], "$layout", false));
|
|
823
847
|
} else {
|
|
@@ -906,7 +930,9 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
906
930
|
navigateToComponent: (path: string) => void;
|
|
907
931
|
}) {
|
|
908
932
|
const tab = activeTab.value;
|
|
909
|
-
if (!tab)
|
|
933
|
+
if (!tab) {
|
|
934
|
+
return html`<div class="empty-state">No document loaded</div>`;
|
|
935
|
+
}
|
|
910
936
|
|
|
911
937
|
// Layout element selected — show read-only info with link to open layout
|
|
912
938
|
if (view.layoutSelection) {
|
|
@@ -917,7 +943,9 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
917
943
|
return html`<div class="empty-state">Select an element to inspect</div>`;
|
|
918
944
|
}
|
|
919
945
|
const node = getNodeAtPath(tab.doc.document, tab.session.selection);
|
|
920
|
-
if (!node)
|
|
946
|
+
if (!node) {
|
|
947
|
+
return html`<div class="empty-state">Node not found</div>`;
|
|
948
|
+
}
|
|
921
949
|
|
|
922
950
|
const path = tab.session.selection;
|
|
923
951
|
const isMapNode = node.$prototype === "Array";
|
|
@@ -925,7 +953,7 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
925
953
|
node.children &&
|
|
926
954
|
typeof node.children === "object" &&
|
|
927
955
|
(node.children as unknown as { $prototype?: string }).$prototype === "Array";
|
|
928
|
-
const isSwitchNode =
|
|
956
|
+
const isSwitchNode = Boolean(node.$switch);
|
|
929
957
|
const isCustomInstance = (node.tagName || "").includes("-");
|
|
930
958
|
const isRoot = path.length === 0;
|
|
931
959
|
const tagName = node.tagName || "div";
|
|
@@ -933,8 +961,8 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
933
961
|
|
|
934
962
|
const mapSignals = isInsideMapTemplate(path)
|
|
935
963
|
? [
|
|
936
|
-
{
|
|
937
|
-
{
|
|
964
|
+
{ label: "$map/item", value: "$map/item" },
|
|
965
|
+
{ label: "$map/index", value: "$map/index" },
|
|
938
966
|
]
|
|
939
967
|
: null;
|
|
940
968
|
|
|
@@ -944,15 +972,14 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
944
972
|
|
|
945
973
|
if (entry.type === "boolean") {
|
|
946
974
|
return renderFieldRow({
|
|
947
|
-
prop: attr,
|
|
948
|
-
label: attrLabel(entry, attr),
|
|
949
975
|
hasValue: hasVal,
|
|
950
|
-
|
|
951
|
-
|
|
976
|
+
label: attrLabel(entry, attr),
|
|
977
|
+
onClear: () => transactDoc(activeTab.value, (t) => mutateUpdateAttribute(t, path, attr)),
|
|
978
|
+
prop: attr,
|
|
952
979
|
widget: html`
|
|
953
980
|
<sp-checkbox
|
|
954
981
|
size="s"
|
|
955
|
-
.checked=${live(
|
|
982
|
+
.checked=${live(Boolean(value))}
|
|
956
983
|
@change=${(e: Event) =>
|
|
957
984
|
transactDoc(activeTab.value, (t) =>
|
|
958
985
|
mutateUpdateAttribute(
|
|
@@ -969,11 +996,10 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
969
996
|
}
|
|
970
997
|
|
|
971
998
|
return renderFieldRow({
|
|
972
|
-
prop: attr,
|
|
973
|
-
label: attrLabel(entry, attr),
|
|
974
999
|
hasValue: hasVal,
|
|
975
|
-
|
|
976
|
-
|
|
1000
|
+
label: attrLabel(entry, attr),
|
|
1001
|
+
onClear: () => transactDoc(activeTab.value, (t) => mutateUpdateAttribute(t, path, attr)),
|
|
1002
|
+
prop: attr,
|
|
977
1003
|
widget: widgetForType(type, entry, attr, String(value || ""), (v: string) =>
|
|
978
1004
|
transactDoc(activeTab.value!, (t) => mutateUpdateAttribute(t, path, attr, v || undefined)),
|
|
979
1005
|
),
|
|
@@ -989,10 +1015,14 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
989
1015
|
}
|
|
990
1016
|
|
|
991
1017
|
const attrSections: Record<string, { name: string; entry: HtmlMetaEntry }[]> = {};
|
|
992
|
-
for (const sec of htmlMeta.$sections)
|
|
1018
|
+
for (const sec of htmlMeta.$sections) {
|
|
1019
|
+
attrSections[sec.key] = [];
|
|
1020
|
+
}
|
|
993
1021
|
for (const [attr, entry] of Object.entries(applicableAttrs)) {
|
|
994
1022
|
const secKey = entry.$section;
|
|
995
|
-
if (attrSections[secKey])
|
|
1023
|
+
if (attrSections[secKey]) {
|
|
1024
|
+
attrSections[secKey].push({ entry, name: attr });
|
|
1025
|
+
}
|
|
996
1026
|
}
|
|
997
1027
|
for (const sec of htmlMeta.$sections) {
|
|
998
1028
|
attrSections[sec.key].sort(
|
|
@@ -1004,20 +1034,29 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
1004
1034
|
const knownAttrNames = new Set(Object.keys(applicableAttrs));
|
|
1005
1035
|
if (isCustomInstance) {
|
|
1006
1036
|
const comp = componentRegistry.find((c) => c.tagName === node.tagName);
|
|
1007
|
-
if (comp?.props)
|
|
1037
|
+
if (comp?.props) {
|
|
1038
|
+
for (const p of comp.props) {
|
|
1039
|
+
knownAttrNames.add(p.name);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1008
1042
|
}
|
|
1009
1043
|
const customAttrs = Object.entries(attrs).filter(([k]) => !knownAttrNames.has(k));
|
|
1010
1044
|
|
|
1011
1045
|
const autoOpen = new Set();
|
|
1012
1046
|
for (const [attr] of Object.entries(attrs)) {
|
|
1013
1047
|
const entry = applicableAttrs[attr];
|
|
1014
|
-
if (entry)
|
|
1048
|
+
if (entry) {
|
|
1049
|
+
autoOpen.add(entry.$section);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
if (customAttrs.length > 0) {
|
|
1053
|
+
autoOpen.add("__custom");
|
|
1015
1054
|
}
|
|
1016
|
-
if (customAttrs.length > 0) autoOpen.add("__custom");
|
|
1017
1055
|
|
|
1018
1056
|
function isSectionOpen(key: string) {
|
|
1019
|
-
if (tab!.session.ui.inspectorSections[key] !== undefined)
|
|
1057
|
+
if (tab!.session.ui.inspectorSections[key] !== undefined) {
|
|
1020
1058
|
return tab!.session.ui.inspectorSections[key];
|
|
1059
|
+
}
|
|
1021
1060
|
return autoOpen.has(key);
|
|
1022
1061
|
}
|
|
1023
1062
|
|
|
@@ -1067,9 +1106,7 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
1067
1106
|
title="Clear $id"
|
|
1068
1107
|
@click=${(e: Event) => {
|
|
1069
1108
|
e.stopPropagation();
|
|
1070
|
-
transactDoc(activeTab.value, (t) =>
|
|
1071
|
-
mutateUpdateProperty(t, path, "$id", undefined),
|
|
1072
|
-
);
|
|
1109
|
+
transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, path, "$id"));
|
|
1073
1110
|
}}
|
|
1074
1111
|
></span>`
|
|
1075
1112
|
: nothing}
|
|
@@ -1089,9 +1126,7 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
1089
1126
|
title="Clear class"
|
|
1090
1127
|
@click=${(e: Event) => {
|
|
1091
1128
|
e.stopPropagation();
|
|
1092
|
-
transactDoc(activeTab.value, (t) =>
|
|
1093
|
-
mutateUpdateProperty(t, path, "className", undefined),
|
|
1094
|
-
);
|
|
1129
|
+
transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, path, "className"));
|
|
1095
1130
|
}}
|
|
1096
1131
|
></span>`
|
|
1097
1132
|
: nothing}
|
|
@@ -1114,7 +1149,7 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
1114
1149
|
@click=${(e: Event) => {
|
|
1115
1150
|
e.stopPropagation();
|
|
1116
1151
|
transactDoc(activeTab.value, (t) =>
|
|
1117
|
-
mutateUpdateProperty(t, path, "textContent"
|
|
1152
|
+
mutateUpdateProperty(t, path, "textContent"),
|
|
1118
1153
|
);
|
|
1119
1154
|
}}
|
|
1120
1155
|
></span>`
|
|
@@ -1140,9 +1175,7 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
1140
1175
|
title="Clear hidden"
|
|
1141
1176
|
@click=${(e: Event) => {
|
|
1142
1177
|
e.stopPropagation();
|
|
1143
|
-
transactDoc(activeTab.value, (t) =>
|
|
1144
|
-
mutateUpdateProperty(t, path, "hidden", undefined),
|
|
1145
|
-
);
|
|
1178
|
+
transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, path, "hidden"));
|
|
1146
1179
|
}}
|
|
1147
1180
|
></span>`
|
|
1148
1181
|
: nothing}
|
|
@@ -1150,7 +1183,7 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
1150
1183
|
</div>
|
|
1151
1184
|
<sp-checkbox
|
|
1152
1185
|
size="s"
|
|
1153
|
-
.checked=${live(
|
|
1186
|
+
.checked=${live(Boolean(node.hidden))}
|
|
1154
1187
|
@change=${(e: Event) =>
|
|
1155
1188
|
transactDoc(activeTab.value, (t) =>
|
|
1156
1189
|
mutateUpdateProperty(
|
|
@@ -1309,7 +1342,9 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
1309
1342
|
? (() => {
|
|
1310
1343
|
const style = node.style || {};
|
|
1311
1344
|
const cssProps = Object.entries(style).filter(([k]) => k.startsWith("--"));
|
|
1312
|
-
if (cssProps.length === 0)
|
|
1345
|
+
if (cssProps.length === 0) {
|
|
1346
|
+
return nothing;
|
|
1347
|
+
}
|
|
1313
1348
|
return html`
|
|
1314
1349
|
<sp-accordion-item
|
|
1315
1350
|
label="CSS Properties"
|
|
@@ -1337,7 +1372,9 @@ export function renderPropertiesPanelTemplate(ctx: {
|
|
|
1337
1372
|
isCustomElementDoc({ document: tab.doc.document }) && isRoot
|
|
1338
1373
|
? (() => {
|
|
1339
1374
|
const parts = collectCssParts(tab.doc.document);
|
|
1340
|
-
if (parts.length === 0)
|
|
1375
|
+
if (parts.length === 0) {
|
|
1376
|
+
return nothing;
|
|
1377
|
+
}
|
|
1341
1378
|
return html`
|
|
1342
1379
|
<sp-accordion-item
|
|
1343
1380
|
label="CSS Parts"
|