@jxsuite/studio 0.19.0 → 0.20.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.css +98 -98
- package/dist/studio.js +12577 -12183
- package/dist/studio.js.map +146 -144
- package/dist/workers/editor.worker.js +79 -79
- package/dist/workers/json.worker.js +109 -109
- package/dist/workers/ts.worker.js +82 -82
- package/package.json +17 -6
- package/src/browse/browse.js +25 -19
- package/src/canvas/canvas-diff.js +24 -16
- package/src/canvas/canvas-helpers.js +24 -19
- package/src/canvas/canvas-live-render.js +94 -39
- package/src/canvas/canvas-render.js +119 -73
- package/src/canvas/canvas-utils.js +38 -15
- package/src/canvas/nested-site-style.js +50 -0
- package/src/editor/component-inline-edit.js +14 -13
- package/src/editor/content-inline-edit.js +22 -10
- package/src/editor/context-menu.js +1 -1
- package/src/editor/convert-to-component.js +12 -10
- package/src/editor/inline-edit.js +66 -28
- package/src/editor/insertion-helper.js +16 -12
- package/src/editor/shortcuts.js +4 -2
- package/src/editor/slash-menu.js +6 -4
- package/src/files/components.js +1 -1
- package/src/files/file-ops.js +37 -16
- package/src/files/files.js +78 -52
- package/src/github/github-auth.js +122 -0
- package/src/github/github-publish.js +115 -0
- package/src/markdown/md-convert.js +172 -107
- package/src/new-project/new-project-modal.js +204 -0
- package/src/panels/activity-bar.js +22 -20
- package/src/panels/ai-panel.js +399 -0
- package/src/panels/block-action-bar.js +5 -5
- package/src/panels/canvas-dnd.js +8 -2
- package/src/panels/data-explorer.js +19 -13
- package/src/panels/dnd.js +144 -92
- package/src/panels/editors.js +55 -35
- package/src/panels/elements-panel.js +33 -19
- package/src/panels/events-panel.js +15 -10
- package/src/panels/git-panel.js +469 -98
- package/src/panels/head-panel.js +121 -81
- package/src/panels/imports-panel.js +88 -64
- package/src/panels/layers-panel.js +64 -43
- package/src/panels/left-panel.js +85 -47
- package/src/panels/overlays.js +1 -0
- package/src/panels/panel-events.js +45 -30
- package/src/panels/preview-render.js +23 -18
- package/src/panels/properties-panel.js +365 -249
- package/src/panels/pseudo-preview.js +2 -2
- package/src/panels/quick-search.js +7 -5
- package/src/panels/right-panel.js +26 -2
- package/src/panels/shared.js +4 -4
- package/src/panels/signals-panel.js +295 -166
- package/src/panels/statusbar.js +4 -4
- package/src/panels/style-inputs.js +67 -35
- package/src/panels/style-panel.js +233 -132
- package/src/panels/style-utils.js +34 -16
- package/src/panels/stylebook-layers-panel.js +12 -10
- package/src/panels/stylebook-panel.js +134 -66
- package/src/panels/toolbar.js +333 -109
- package/src/panels/welcome-screen.js +121 -0
- package/src/platform.js +2 -4
- package/src/platforms/devserver.js +113 -7
- package/src/resize-edges.js +98 -0
- package/src/services/cem-export.js +12 -7
- package/src/services/code-services.js +30 -12
- package/src/settings/content-types-editor.js +7 -7
- package/src/settings/css-vars-editor.js +30 -24
- package/src/settings/defs-editor.js +12 -7
- package/src/settings/general-settings.js +85 -3
- package/src/settings/head-editor.js +30 -24
- package/src/settings/schema-field-ui.js +58 -39
- package/src/site-context.js +41 -31
- package/src/state.js +70 -29
- package/src/store.js +21 -26
- package/src/studio.js +103 -81
- package/src/tabs/tab.js +67 -43
- package/src/tabs/transact.js +23 -13
- package/src/ui/button-group.js +24 -24
- package/src/ui/color-selector.js +28 -23
- package/src/ui/field-row.js +3 -3
- package/src/ui/icons.js +2 -2
- package/src/ui/media-picker.js +7 -7
- package/src/ui/spectrum.js +5 -1
- package/src/ui/unit-selector.js +18 -16
- package/src/ui/value-selector.js +21 -15
- package/src/ui/widgets.js +22 -19
- package/src/utils/canvas-media.js +5 -4
- package/src/utils/edit-display.js +31 -20
- package/src/utils/google-fonts.js +11 -11
- package/src/utils/inherited-style.js +9 -8
- package/src/utils/studio-utils.js +7 -7
- package/src/view.js +58 -1
- package/src/workspace/workspace.js +10 -7
package/src/tabs/transact.js
CHANGED
|
@@ -6,8 +6,6 @@ import { getNodeAtPath, parentElementPath, childIndex, pathsEqual, isAncestor }
|
|
|
6
6
|
*
|
|
7
7
|
* @typedef {import("../state.js").JxPath} JxPath
|
|
8
8
|
*
|
|
9
|
-
* @typedef {import("../state.js").JxNode} JxNode
|
|
10
|
-
*
|
|
11
9
|
* @typedef {string | number | boolean | object | null | undefined} JsonValue
|
|
12
10
|
*/
|
|
13
11
|
|
|
@@ -19,11 +17,12 @@ const HISTORY_LIMIT = 100;
|
|
|
19
17
|
* Apply a document mutation transactionally: push to history and mark dirty. The mutationFn
|
|
20
18
|
* receives the tab and should mutate tab.doc.document in place.
|
|
21
19
|
*
|
|
22
|
-
* @param {Tab} tab
|
|
20
|
+
* @param {Tab | null} tab
|
|
23
21
|
* @param {(tab: Tab) => void} mutationFn
|
|
24
22
|
* @param {{ skipHistory?: boolean }} [opts]
|
|
25
23
|
*/
|
|
26
24
|
export function transactDoc(tab, mutationFn, { skipHistory = false } = {}) {
|
|
25
|
+
if (!tab) return;
|
|
27
26
|
mutationFn(tab);
|
|
28
27
|
|
|
29
28
|
// Replace the document root reference so effects tracking tab.doc.document re-trigger.
|
|
@@ -49,8 +48,8 @@ export function transactDoc(tab, mutationFn, { skipHistory = false } = {}) {
|
|
|
49
48
|
/**
|
|
50
49
|
* Convenience: transact with a mutation fn that receives the document directly.
|
|
51
50
|
*
|
|
52
|
-
* @param {Tab} tab
|
|
53
|
-
* @param {(doc:
|
|
51
|
+
* @param {Tab | null} tab
|
|
52
|
+
* @param {(doc: JxMutableNode) => void} fn
|
|
54
53
|
* @param {{ skipHistory?: boolean }} [opts]
|
|
55
54
|
*/
|
|
56
55
|
export function transact(tab, fn, opts) {
|
|
@@ -85,7 +84,7 @@ export function redo(tab) {
|
|
|
85
84
|
* @param {Tab} tab
|
|
86
85
|
* @param {JxPath} parentPath
|
|
87
86
|
* @param {number} index
|
|
88
|
-
* @param {
|
|
87
|
+
* @param {JxMutableNode} nodeDef
|
|
89
88
|
*/
|
|
90
89
|
export function mutateInsertNode(tab, parentPath, index, nodeDef) {
|
|
91
90
|
const parent = getNodeAtPath(tab.doc.document, parentPath);
|
|
@@ -101,7 +100,10 @@ export function mutateRemoveNode(tab, path) {
|
|
|
101
100
|
if (!path || path.length < 2) return;
|
|
102
101
|
const elemPath = /** @type {JxPath} */ (parentElementPath(path));
|
|
103
102
|
const idx = /** @type {number} */ (childIndex(path));
|
|
104
|
-
getNodeAtPath(tab.doc.document, elemPath).children.splice(
|
|
103
|
+
/** @type {JxMutableNode[]} */ (getNodeAtPath(tab.doc.document, elemPath).children).splice(
|
|
104
|
+
idx,
|
|
105
|
+
1,
|
|
106
|
+
);
|
|
105
107
|
if (tab.session.selection && isAncestor(path, tab.session.selection)) {
|
|
106
108
|
tab.session.selection = null;
|
|
107
109
|
}
|
|
@@ -118,7 +120,11 @@ export function mutateDuplicateNode(tab, path) {
|
|
|
118
120
|
const elemPath = /** @type {JxPath} */ (parentElementPath(path));
|
|
119
121
|
const idx = /** @type {number} */ (childIndex(path));
|
|
120
122
|
const clone = structuredClone(toRaw(node));
|
|
121
|
-
getNodeAtPath(tab.doc.document, elemPath).children.splice(
|
|
123
|
+
/** @type {JxMutableNode[]} */ (getNodeAtPath(tab.doc.document, elemPath).children).splice(
|
|
124
|
+
idx + 1,
|
|
125
|
+
0,
|
|
126
|
+
clone,
|
|
127
|
+
);
|
|
122
128
|
tab.session.selection = [...elemPath, "children", idx + 1];
|
|
123
129
|
}
|
|
124
130
|
|
|
@@ -134,7 +140,11 @@ export function mutateWrapNode(tab, path, wrapperTag = "div") {
|
|
|
134
140
|
const elemPath = /** @type {JxPath} */ (parentElementPath(path));
|
|
135
141
|
const idx = /** @type {number} */ (childIndex(path));
|
|
136
142
|
const wrapper = { tagName: wrapperTag, children: [structuredClone(toRaw(node))] };
|
|
137
|
-
getNodeAtPath(tab.doc.document, elemPath).children.splice(
|
|
143
|
+
/** @type {JxMutableNode[]} */ (getNodeAtPath(tab.doc.document, elemPath).children).splice(
|
|
144
|
+
idx,
|
|
145
|
+
1,
|
|
146
|
+
wrapper,
|
|
147
|
+
);
|
|
138
148
|
tab.session.selection = [...elemPath, "children", idx];
|
|
139
149
|
}
|
|
140
150
|
|
|
@@ -149,7 +159,7 @@ export function mutateMoveNode(tab, fromPath, toParentPath, toIndex) {
|
|
|
149
159
|
const fromParentPath = /** @type {JxPath} */ (parentElementPath(fromPath));
|
|
150
160
|
const fromIdx = /** @type {number} */ (childIndex(fromPath));
|
|
151
161
|
const fromParent = getNodeAtPath(doc, fromParentPath);
|
|
152
|
-
const [node] = fromParent.children.splice(fromIdx, 1);
|
|
162
|
+
const [node] = /** @type {JxMutableNode[]} */ (fromParent.children).splice(fromIdx, 1);
|
|
153
163
|
const toParent = getNodeAtPath(doc, toParentPath);
|
|
154
164
|
if (!toParent.children) toParent.children = [];
|
|
155
165
|
let adjustedIndex = toIndex;
|
|
@@ -351,7 +361,7 @@ export function mutateUpdateMediaNestedStylePath(tab, path, mediaName, stylePath
|
|
|
351
361
|
/**
|
|
352
362
|
* @param {Tab} tab
|
|
353
363
|
* @param {JxPath} path
|
|
354
|
-
* @param {Record<string,
|
|
364
|
+
* @param {Record<string, string | undefined> | undefined} style
|
|
355
365
|
*/
|
|
356
366
|
export function mutateReplaceStyle(tab, path, style) {
|
|
357
367
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
@@ -388,7 +398,7 @@ export function mutateRemoveDef(tab, name) {
|
|
|
388
398
|
/**
|
|
389
399
|
* @param {Tab} tab
|
|
390
400
|
* @param {string} name
|
|
391
|
-
* @param {Record<string,
|
|
401
|
+
* @param {Record<string, JsonValue>} updates
|
|
392
402
|
*/
|
|
393
403
|
export function mutateUpdateDef(tab, name, updates) {
|
|
394
404
|
const doc = tab.doc.document;
|
|
@@ -452,7 +462,7 @@ export function mutateUpdateProp(tab, path, propName, value) {
|
|
|
452
462
|
* @param {Tab} tab
|
|
453
463
|
* @param {JxPath} path
|
|
454
464
|
* @param {string} caseName
|
|
455
|
-
* @param {
|
|
465
|
+
* @param {JxMutableNode} [caseDef]
|
|
456
466
|
*/
|
|
457
467
|
export function mutateAddSwitchCase(tab, path, caseName, caseDef) {
|
|
458
468
|
const node = getNodeAtPath(tab.doc.document, path);
|
package/src/ui/button-group.js
CHANGED
|
@@ -13,35 +13,37 @@ import icons from "./icons.js";
|
|
|
13
13
|
/**
|
|
14
14
|
* Render a button group widget with optional overflow menu.
|
|
15
15
|
*
|
|
16
|
-
* @param {
|
|
16
|
+
* @param {Record<string, unknown>} entry — css-meta entry with $buttonValues, enum, $icons
|
|
17
17
|
* @param {string} prop — property key (for menu ID namespace)
|
|
18
|
-
* @param {
|
|
18
|
+
* @param {string | number | undefined} value — current value
|
|
19
19
|
* @param {(val: string) => void} onChange — commit callback
|
|
20
|
-
* @returns {
|
|
20
|
+
* @returns {import("lit-html").TemplateResult}
|
|
21
21
|
*/
|
|
22
22
|
export function renderButtonGroup(
|
|
23
|
-
/** @type {
|
|
24
|
-
/** @type {
|
|
25
|
-
/** @type {
|
|
26
|
-
/** @type {
|
|
23
|
+
/** @type {Record<string, unknown>} */ entry,
|
|
24
|
+
/** @type {string} */ prop,
|
|
25
|
+
/** @type {string | number | undefined} */ value,
|
|
26
|
+
/** @type {(val: string) => void} */ onChange,
|
|
27
27
|
) {
|
|
28
|
-
const values = entry.$buttonValues || entry.enum || [];
|
|
29
|
-
/** @type {Record<string,
|
|
30
|
-
const iconMap = entry.$icons || {};
|
|
28
|
+
const values = /** @type {string[]} */ (entry.$buttonValues || entry.enum || []);
|
|
29
|
+
/** @type {Record<string, string>} */
|
|
30
|
+
const iconMap = /** @type {Record<string, string>} */ (entry.$icons || {});
|
|
31
|
+
const buttonValues = /** @type {string[] | undefined} */ (entry.$buttonValues);
|
|
32
|
+
const enumValues = /** @type {string[] | undefined} */ (entry.enum);
|
|
31
33
|
const extra =
|
|
32
|
-
|
|
33
|
-
?
|
|
34
|
+
buttonValues && enumValues && enumValues.length > buttonValues.length
|
|
35
|
+
? enumValues.filter((/** @type {string} */ v) => !buttonValues.includes(v))
|
|
34
36
|
: [];
|
|
35
37
|
|
|
36
38
|
const menuId = `style-btngrp-${prop}`;
|
|
37
39
|
const hasExtra = extra.length > 0;
|
|
38
|
-
const extraSelected = hasExtra && extra.includes(value);
|
|
40
|
+
const extraSelected = hasExtra && extra.includes(/** @type {string} */ (value));
|
|
39
41
|
|
|
40
42
|
return html`
|
|
41
43
|
<div class=${classMap({ "button-group-combo": true, "has-overflow": hasExtra })}>
|
|
42
44
|
<sp-action-group size="s" compact>
|
|
43
45
|
${values.map(
|
|
44
|
-
(/** @type {
|
|
46
|
+
(/** @type {string} */ v) => html`
|
|
45
47
|
<sp-action-button
|
|
46
48
|
size="s"
|
|
47
49
|
value=${v}
|
|
@@ -49,12 +51,9 @@ export function renderButtonGroup(
|
|
|
49
51
|
?selected=${v === value}
|
|
50
52
|
@click=${() => onChange(v === value ? "" : v)}
|
|
51
53
|
>
|
|
52
|
-
${
|
|
53
|
-
/** @type {
|
|
54
|
-
|
|
55
|
-
? /** @type {any} */ (icons)[/** @type {any} */ (iconMap)[v]]
|
|
56
|
-
: abbreviateValue(v)
|
|
57
|
-
}
|
|
54
|
+
${iconMap[v] && /** @type {Record<string, unknown>} */ (icons)[iconMap[v]]
|
|
55
|
+
? /** @type {Record<string, unknown>} */ (icons)[iconMap[v]]
|
|
56
|
+
: abbreviateValue(v)}
|
|
58
57
|
</sp-action-button>
|
|
59
58
|
`,
|
|
60
59
|
)}
|
|
@@ -69,15 +68,16 @@ export function renderButtonGroup(
|
|
|
69
68
|
<sp-overlay trigger="${menuId}@click" placement="bottom-end" type="auto">
|
|
70
69
|
<sp-popover>
|
|
71
70
|
<sp-menu
|
|
72
|
-
@change=${(/** @type {
|
|
73
|
-
if (
|
|
71
|
+
@change=${(/** @type {Event} */ e) => {
|
|
72
|
+
if (/** @type {HTMLInputElement} */ (e.target).value)
|
|
73
|
+
onChange(/** @type {HTMLInputElement} */ (e.target).value);
|
|
74
74
|
}}
|
|
75
75
|
>
|
|
76
76
|
<sp-menu-item value="__none__">—</sp-menu-item>
|
|
77
|
-
${extra.map((/** @type {
|
|
77
|
+
${extra.map((/** @type {string} */ v) => {
|
|
78
78
|
const label = v.includes("-")
|
|
79
79
|
? kebabToLabel(v)
|
|
80
|
-
: v.replace(/^./, (/** @type {
|
|
80
|
+
: v.replace(/^./, (/** @type {string} */ c) => c.toUpperCase());
|
|
81
81
|
return html`<sp-menu-item value=${v} ?selected=${v === value}
|
|
82
82
|
>${label}</sp-menu-item
|
|
83
83
|
>`;
|
package/src/ui/color-selector.js
CHANGED
|
@@ -42,19 +42,20 @@ function varToLabel(/** @type {string} */ name) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/** Resolve a color value for display — if it's a var() reference, look up the actual color. */
|
|
45
|
-
function resolveColorForDisplay(/** @type {
|
|
45
|
+
function resolveColorForDisplay(/** @type {string | number | undefined} */ val) {
|
|
46
46
|
if (!val) return "transparent";
|
|
47
|
-
const
|
|
47
|
+
const s = String(val);
|
|
48
|
+
const m = s.match(/^var\((--[^)]+)\)$/);
|
|
48
49
|
if (m) {
|
|
49
50
|
const style = getEffectiveStyle(activeTab.value?.doc.document?.style);
|
|
50
51
|
const resolved = style?.[m[1]];
|
|
51
52
|
if (typeof resolved === "string") return resolved;
|
|
52
53
|
return "transparent";
|
|
53
54
|
}
|
|
54
|
-
return
|
|
55
|
+
return s;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
function safeColor(/** @type {
|
|
58
|
+
function safeColor(/** @type {string | number | undefined} */ val) {
|
|
58
59
|
if (!val) return "transparent";
|
|
59
60
|
return resolveColorForDisplay(val);
|
|
60
61
|
}
|
|
@@ -69,7 +70,7 @@ function normalizeHex(/** @type {string} */ c) {
|
|
|
69
70
|
/**
|
|
70
71
|
* Check if a value is a var() reference that matches a defined color variable.
|
|
71
72
|
*
|
|
72
|
-
* @param {
|
|
73
|
+
* @param {string | number | undefined} value
|
|
73
74
|
* @param {{ name: string; value: string }[]} colorVars
|
|
74
75
|
*/
|
|
75
76
|
function matchesColorVar(value, colorVars) {
|
|
@@ -102,7 +103,7 @@ export class JxColorPopover extends LitElement {
|
|
|
102
103
|
return this;
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
/** @param {Map<string,
|
|
106
|
+
/** @param {Map<string, unknown>} changed */
|
|
106
107
|
willUpdate(changed) {
|
|
107
108
|
if (changed.has("color")) {
|
|
108
109
|
const raw = resolveColorForDisplay(this.color);
|
|
@@ -116,29 +117,33 @@ export class JxColorPopover extends LitElement {
|
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
|
|
119
|
-
_handleArea(/** @type {
|
|
120
|
-
const color = normalizeHex(
|
|
120
|
+
_handleArea(/** @type {Event} */ e) {
|
|
121
|
+
const color = normalizeHex(
|
|
122
|
+
String(/** @type {HTMLElement & { color: string }} */ (e.target).color),
|
|
123
|
+
);
|
|
121
124
|
this.displayColor = color;
|
|
122
125
|
this.color = color;
|
|
123
126
|
this.dispatchEvent(new CustomEvent("color-change", { detail: color, bubbles: true }));
|
|
124
127
|
}
|
|
125
128
|
|
|
126
|
-
_handleSlider(/** @type {
|
|
127
|
-
const color = normalizeHex(
|
|
129
|
+
_handleSlider(/** @type {Event} */ e) {
|
|
130
|
+
const color = normalizeHex(
|
|
131
|
+
String(/** @type {HTMLElement & { color: string }} */ (e.target).color),
|
|
132
|
+
);
|
|
128
133
|
this.displayColor = color;
|
|
129
134
|
this.color = color;
|
|
130
135
|
this.dispatchEvent(new CustomEvent("color-change", { detail: color, bubbles: true }));
|
|
131
136
|
}
|
|
132
137
|
|
|
133
|
-
_handleText(/** @type {
|
|
134
|
-
const val = e.target.value.trim();
|
|
138
|
+
_handleText(/** @type {Event} */ e) {
|
|
139
|
+
const val = /** @type {HTMLInputElement} */ (e.target).value.trim();
|
|
135
140
|
if (!val) return;
|
|
136
141
|
this.displayColor = val;
|
|
137
142
|
this.color = val;
|
|
138
143
|
this.dispatchEvent(new CustomEvent("color-change", { detail: val, bubbles: true }));
|
|
139
144
|
}
|
|
140
145
|
|
|
141
|
-
_handleSwatch(/** @type {
|
|
146
|
+
_handleSwatch(/** @type {Event} */ e, /** @type {string} */ varName) {
|
|
142
147
|
e.stopPropagation();
|
|
143
148
|
const varRef = `var(${varName})`;
|
|
144
149
|
this.color = varRef;
|
|
@@ -176,7 +181,7 @@ export class JxColorPopover extends LitElement {
|
|
|
176
181
|
color=${cv.value}
|
|
177
182
|
.value=${cv.name}
|
|
178
183
|
title=${cv.name}
|
|
179
|
-
@click=${(/** @type {
|
|
184
|
+
@click=${(/** @type {Event} */ e) => this._handleSwatch(e, cv.name)}
|
|
180
185
|
></sp-swatch>
|
|
181
186
|
`,
|
|
182
187
|
)}
|
|
@@ -198,14 +203,14 @@ export class JxColorPopover extends LitElement {
|
|
|
198
203
|
* title-cased label with swatch (e.g. "Primary Blue").
|
|
199
204
|
*
|
|
200
205
|
* @param {string} prop — property key (for debounce namespace)
|
|
201
|
-
* @param {
|
|
206
|
+
* @param {string | number | undefined} value — current color value
|
|
202
207
|
* @param {(color: string) => void} onChange — commit callback
|
|
203
|
-
* @returns {
|
|
208
|
+
* @returns {import("lit-html").TemplateResult}
|
|
204
209
|
*/
|
|
205
210
|
export function renderColorSelector(
|
|
206
|
-
/** @type {
|
|
207
|
-
/** @type {
|
|
208
|
-
/** @type {
|
|
211
|
+
/** @type {string} */ prop,
|
|
212
|
+
/** @type {string | number | undefined} */ value,
|
|
213
|
+
/** @type {(color: string) => void} */ onChange,
|
|
209
214
|
) {
|
|
210
215
|
const colorVars = getColorVars();
|
|
211
216
|
const matchedVar = matchesColorVar(value, colorVars);
|
|
@@ -237,9 +242,9 @@ export function renderColorSelector(
|
|
|
237
242
|
size="s"
|
|
238
243
|
style="flex:1; min-width:0"
|
|
239
244
|
.value=${`var(${matchedVar.name})`}
|
|
240
|
-
@change=${(/** @type {
|
|
245
|
+
@change=${(/** @type {Event} */ e) => {
|
|
241
246
|
e.stopPropagation();
|
|
242
|
-
onChange(e.target.value);
|
|
247
|
+
onChange(/** @type {HTMLInputElement} */ (e.target).value);
|
|
243
248
|
}}
|
|
244
249
|
>
|
|
245
250
|
${colorVars.map(
|
|
@@ -275,8 +280,8 @@ export function renderColorSelector(
|
|
|
275
280
|
style="flex:1; min-width:0"
|
|
276
281
|
.value=${live(value || "")}
|
|
277
282
|
@click=${(/** @type {Event} */ e) => e.stopPropagation()}
|
|
278
|
-
@input=${debouncedStyleCommit(`color:${prop}`, 400, (/** @type {
|
|
279
|
-
onChange(e.target.value.trim());
|
|
283
|
+
@input=${debouncedStyleCommit(`color:${prop}`, 400, (/** @type {Event} */ e) => {
|
|
284
|
+
onChange(/** @type {HTMLInputElement} */ (e.target).value.trim());
|
|
280
285
|
})}
|
|
281
286
|
></sp-textfield>
|
|
282
287
|
<sp-overlay trigger="${triggerId}@click" placement="bottom-start" type="auto">
|
package/src/ui/field-row.js
CHANGED
|
@@ -16,11 +16,11 @@ import { classMap } from "lit-html/directives/class-map.js";
|
|
|
16
16
|
* label: string;
|
|
17
17
|
* hasValue: boolean;
|
|
18
18
|
* onClear?: () => void;
|
|
19
|
-
* widget:
|
|
19
|
+
* widget: unknown;
|
|
20
20
|
* span?: number;
|
|
21
21
|
* warning?: boolean;
|
|
22
22
|
* }} opts
|
|
23
|
-
* @returns {
|
|
23
|
+
* @returns {import("lit-html").TemplateResult}
|
|
24
24
|
*/
|
|
25
25
|
export function renderFieldRow({ prop, label, hasValue, onClear, widget, span, warning }) {
|
|
26
26
|
return html`
|
|
@@ -34,7 +34,7 @@ export function renderFieldRow({ prop, label, hasValue, onClear, widget, span, w
|
|
|
34
34
|
? html`<span
|
|
35
35
|
class="set-dot"
|
|
36
36
|
title="Clear ${prop}"
|
|
37
|
-
@click=${(/** @type {
|
|
37
|
+
@click=${(/** @type {Event} */ e) => {
|
|
38
38
|
e.stopPropagation();
|
|
39
39
|
onClear();
|
|
40
40
|
}}
|
package/src/ui/icons.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { html } from "lit";
|
|
6
6
|
|
|
7
7
|
// Helper for custom filled-rect icons (alignment/justify diagrams) where no Spectrum match exists
|
|
8
|
-
const _R = (/** @type {
|
|
8
|
+
const _R = (/** @type {import("lit").TemplateResult} */ d) =>
|
|
9
9
|
html`<svg
|
|
10
10
|
xmlns="http://www.w3.org/2000/svg"
|
|
11
11
|
width="16"
|
|
@@ -18,7 +18,7 @@ const _R = (/** @type {any} */ d) =>
|
|
|
18
18
|
</svg>`;
|
|
19
19
|
|
|
20
20
|
// Helper for custom stroke icons
|
|
21
|
-
const _S = (/** @type {
|
|
21
|
+
const _S = (/** @type {import("lit").TemplateResult} */ d) =>
|
|
22
22
|
html`<svg
|
|
23
23
|
xmlns="http://www.w3.org/2000/svg"
|
|
24
24
|
width="16"
|
package/src/ui/media-picker.js
CHANGED
|
@@ -94,9 +94,9 @@ export function invalidateMediaCache() {
|
|
|
94
94
|
* Render the media picker widget for src-type attributes.
|
|
95
95
|
*
|
|
96
96
|
* @param {string} prop — attribute name (e.g. "src")
|
|
97
|
-
* @param {
|
|
98
|
-
* @param {(val:
|
|
99
|
-
* @returns {
|
|
97
|
+
* @param {string} value — current attribute value
|
|
98
|
+
* @param {(val: string) => void} onCommit — commit callback
|
|
99
|
+
* @returns {import("lit-html").TemplateResult}
|
|
100
100
|
*/
|
|
101
101
|
export function renderMediaPicker(prop, value, onCommit) {
|
|
102
102
|
// Kick off async load (won't block render)
|
|
@@ -127,8 +127,8 @@ export function renderMediaPicker(prop, value, onCommit) {
|
|
|
127
127
|
size="s"
|
|
128
128
|
placeholder="/image.jpg"
|
|
129
129
|
.value=${live(currentValue)}
|
|
130
|
-
@input=${debouncedStyleCommit(`media:${prop}`, 400, (/** @type {
|
|
131
|
-
onCommit(e.target.value),
|
|
130
|
+
@input=${debouncedStyleCommit(`media:${prop}`, 400, (/** @type {Event} */ e) =>
|
|
131
|
+
onCommit(/** @type {HTMLInputElement} */ (e.target).value),
|
|
132
132
|
)}
|
|
133
133
|
@focus=${() => loadMediaCache()}
|
|
134
134
|
></sp-textfield>
|
|
@@ -140,8 +140,8 @@ export function renderMediaPicker(prop, value, onCommit) {
|
|
|
140
140
|
</sp-action-button>
|
|
141
141
|
<sp-popover slot="click-content" class="media-picker-popover">
|
|
142
142
|
<sp-menu
|
|
143
|
-
@change=${(/** @type {
|
|
144
|
-
onCommit(e.target.value);
|
|
143
|
+
@change=${(/** @type {Event} */ e) => {
|
|
144
|
+
onCommit(/** @type {HTMLInputElement} */ (e.target).value);
|
|
145
145
|
}}
|
|
146
146
|
>
|
|
147
147
|
${options.map(
|
package/src/ui/spectrum.js
CHANGED
|
@@ -90,6 +90,7 @@ import { IconSettings } from "@spectrum-web-components/icons-workflow/src/elemen
|
|
|
90
90
|
import { IconBack } from "@spectrum-web-components/icons-workflow/src/elements/IconBack.js";
|
|
91
91
|
import { IconProperties } from "@spectrum-web-components/icons-workflow/src/elements/IconProperties.js";
|
|
92
92
|
import { IconEvent } from "@spectrum-web-components/icons-workflow/src/elements/IconEvent.js";
|
|
93
|
+
import { IconMore } from "@spectrum-web-components/icons-workflow/src/elements/IconMore.js";
|
|
93
94
|
|
|
94
95
|
// Layout / alignment icons
|
|
95
96
|
import { IconArrowRight } from "@spectrum-web-components/icons-workflow/src/elements/IconArrowRight.js";
|
|
@@ -122,6 +123,7 @@ import { IconBox } from "@spectrum-web-components/icons-workflow/src/elements/Ic
|
|
|
122
123
|
import { IconVisibility } from "@spectrum-web-components/icons-workflow/src/elements/IconVisibility.js";
|
|
123
124
|
import { IconVisibilityOff } from "@spectrum-web-components/icons-workflow/src/elements/IconVisibilityOff.js";
|
|
124
125
|
import { IconArtboard } from "@spectrum-web-components/icons-workflow/src/elements/IconArtboard.js";
|
|
126
|
+
import { IconChat } from "@spectrum-web-components/icons-workflow/src/elements/IconChat.js";
|
|
125
127
|
import { IconViewList } from "@spectrum-web-components/icons-workflow/src/elements/IconViewList.js";
|
|
126
128
|
import { IconRailRightClose } from "@spectrum-web-components/icons-workflow/src/elements/IconRailRightClose.js";
|
|
127
129
|
import { IconRailRightOpen } from "@spectrum-web-components/icons-workflow/src/elements/IconRailRightOpen.js";
|
|
@@ -228,6 +230,7 @@ const components = [
|
|
|
228
230
|
["sp-icon-back", IconBack],
|
|
229
231
|
["sp-icon-properties", IconProperties],
|
|
230
232
|
["sp-icon-event", IconEvent],
|
|
233
|
+
["sp-icon-more", IconMore],
|
|
231
234
|
["sp-icon-arrow-right", IconArrowRight],
|
|
232
235
|
["sp-icon-arrow-left", IconArrowLeft],
|
|
233
236
|
["sp-icon-arrow-down", IconArrowDown],
|
|
@@ -263,6 +266,7 @@ const components = [
|
|
|
263
266
|
["sp-icon-visibility", IconVisibility],
|
|
264
267
|
["sp-icon-visibility-off", IconVisibilityOff],
|
|
265
268
|
["sp-icon-artboard", IconArtboard],
|
|
269
|
+
["sp-icon-chat", IconChat],
|
|
266
270
|
["sp-icon-view-list", IconViewList],
|
|
267
271
|
["sp-icon-text-bold", IconTextBold],
|
|
268
272
|
["sp-icon-text-italic", IconTextItalic],
|
|
@@ -279,7 +283,7 @@ const components = [
|
|
|
279
283
|
];
|
|
280
284
|
|
|
281
285
|
for (const [tag, ctor] of /** @type {[string, CustomElementConstructor][]} */ (components)) {
|
|
282
|
-
if (!customElements.get(tag)) defineElement(tag, /** @type {
|
|
286
|
+
if (!customElements.get(tag)) defineElement(tag, /** @type {CustomElementConstructor} */ (ctor));
|
|
283
287
|
}
|
|
284
288
|
|
|
285
289
|
// Register theme fragments (these are also side-effect-only in the original modules)
|
package/src/ui/unit-selector.js
CHANGED
|
@@ -15,25 +15,26 @@ export const UNIT_RE = /^(-?[\d.]+)(px|rem|em|%|vw|vh|svw|svh|dvh|ms|s|fr|ch|ex|
|
|
|
15
15
|
/**
|
|
16
16
|
* Render a number + unit selector widget.
|
|
17
17
|
*
|
|
18
|
-
* @param {
|
|
18
|
+
* @param {Record<string, unknown>} entry — css-meta entry with $units and $keywords arrays
|
|
19
19
|
* @param {string} prop — property key (for debounce namespace)
|
|
20
|
-
* @param {
|
|
20
|
+
* @param {string | number | undefined} value — current value (e.g. "12px", "auto", "")
|
|
21
21
|
* @param {(val: string) => void} onChange — commit callback
|
|
22
|
-
* @
|
|
22
|
+
* @param {string} [placeholder]
|
|
23
|
+
* @returns {import("lit-html").TemplateResult}
|
|
23
24
|
*/
|
|
24
25
|
export function renderUnitSelector(
|
|
25
|
-
/** @type {
|
|
26
|
-
/** @type {
|
|
27
|
-
/** @type {
|
|
28
|
-
/** @type {
|
|
26
|
+
/** @type {Record<string, unknown>} */ entry,
|
|
27
|
+
/** @type {string} */ prop,
|
|
28
|
+
/** @type {string | number | undefined} */ value,
|
|
29
|
+
/** @type {(val: string) => void} */ onChange,
|
|
29
30
|
/** @type {string} */ placeholder = "",
|
|
30
31
|
) {
|
|
31
|
-
const units = entry.$units || [];
|
|
32
|
-
const keywords = entry.$keywords || [];
|
|
32
|
+
const units = /** @type {string[]} */ (entry.$units || []);
|
|
33
|
+
const keywords = /** @type {string[]} */ (entry.$keywords || []);
|
|
33
34
|
const strVal = String(value ?? "");
|
|
34
35
|
const match = strVal.match(UNIT_RE);
|
|
35
36
|
const isKeyword = !match && strVal !== "" && keywords.includes(strVal);
|
|
36
|
-
const isNumericVal = (/** @type {
|
|
37
|
+
const isNumericVal = (/** @type {string} */ v) => /^-?\d*\.?\d*$/.test(v);
|
|
37
38
|
|
|
38
39
|
const currentUnit = isKeyword ? units[0] || "" : match ? match[2] || "" : units[0] || "";
|
|
39
40
|
let displayValue;
|
|
@@ -55,8 +56,8 @@ export function renderUnitSelector(
|
|
|
55
56
|
size="s"
|
|
56
57
|
placeholder=${placeholder || "0"}
|
|
57
58
|
.value=${live(displayValue)}
|
|
58
|
-
@input=${debouncedStyleCommit(`nui:${prop}`, 400, (/** @type {
|
|
59
|
-
const val = (e.target.value
|
|
59
|
+
@input=${debouncedStyleCommit(`nui:${prop}`, 400, (/** @type {Event} */ e) => {
|
|
60
|
+
const val = /** @type {HTMLInputElement} */ (e.target).value.trim();
|
|
60
61
|
if (val === "") {
|
|
61
62
|
onChange("");
|
|
62
63
|
return;
|
|
@@ -74,8 +75,8 @@ export function renderUnitSelector(
|
|
|
74
75
|
<sp-popover style="min-width: var(--spectrum-component-width-900, 64px)">
|
|
75
76
|
<sp-menu
|
|
76
77
|
label="CSS unit"
|
|
77
|
-
@change=${(/** @type {
|
|
78
|
-
const chosen = e.target.value;
|
|
78
|
+
@change=${(/** @type {Event} */ e) => {
|
|
79
|
+
const chosen = /** @type {HTMLInputElement} */ (e.target).value;
|
|
79
80
|
if (keywords.includes(chosen)) {
|
|
80
81
|
onChange(chosen);
|
|
81
82
|
} else if (units.includes(chosen)) {
|
|
@@ -86,13 +87,14 @@ export function renderUnitSelector(
|
|
|
86
87
|
}}
|
|
87
88
|
>
|
|
88
89
|
${units.map(
|
|
89
|
-
(/** @type {
|
|
90
|
+
(/** @type {string} */ u) =>
|
|
91
|
+
html`<sp-menu-item value=${u}>${u}</sp-menu-item>`,
|
|
90
92
|
)}
|
|
91
93
|
${keywords.length > 0 && units.length > 0
|
|
92
94
|
? html`<sp-menu-divider></sp-menu-divider>`
|
|
93
95
|
: nothing}
|
|
94
96
|
${keywords.map(
|
|
95
|
-
(/** @type {
|
|
97
|
+
(/** @type {string} */ kw) =>
|
|
96
98
|
html`<sp-menu-item value=${kw}>${kw}</sp-menu-item>`,
|
|
97
99
|
)}
|
|
98
100
|
</sp-menu>
|
package/src/ui/value-selector.js
CHANGED
|
@@ -47,21 +47,27 @@ export class JxValueSelector extends LitElement {
|
|
|
47
47
|
get _isPicker() {
|
|
48
48
|
return (
|
|
49
49
|
!!this.value &&
|
|
50
|
-
this.options.some(
|
|
50
|
+
this.options.some(
|
|
51
|
+
(/** @type {ComboOption} */ o) => !("divider" in o) && o.value === this.value,
|
|
52
|
+
)
|
|
51
53
|
);
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
/** Get the selected option's style string for the picker button preview */
|
|
55
57
|
get _selectedStyle() {
|
|
56
58
|
if (!this._isPicker) return "";
|
|
57
|
-
const opt = this.options.find(
|
|
58
|
-
|
|
59
|
+
const opt = this.options.find(
|
|
60
|
+
(/** @type {ComboOption} */ o) => !("divider" in o) && o.value === this.value,
|
|
61
|
+
);
|
|
62
|
+
return (
|
|
63
|
+
/** @type {{ value: string; label: string; style?: string } | undefined} */ (opt)?.style || ""
|
|
64
|
+
);
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
/** Render menu items from options array */
|
|
62
68
|
_renderMenuItems() {
|
|
63
|
-
return this.options.map((/** @type {
|
|
64
|
-
opt
|
|
69
|
+
return this.options.map((/** @type {ComboOption} */ opt) =>
|
|
70
|
+
"divider" in opt
|
|
65
71
|
? html`<sp-menu-divider></sp-menu-divider>`
|
|
66
72
|
: html`<sp-menu-item value=${opt.value} style=${opt.style || ""}
|
|
67
73
|
>${opt.label}</sp-menu-item
|
|
@@ -70,33 +76,33 @@ export class JxValueSelector extends LitElement {
|
|
|
70
76
|
}
|
|
71
77
|
|
|
72
78
|
/** Picker mode: sp-picker @change handler */
|
|
73
|
-
_handlePickerChange(/** @type {
|
|
79
|
+
_handlePickerChange(/** @type {Event} */ e) {
|
|
74
80
|
e.stopPropagation();
|
|
75
|
-
this.value = e.target.value;
|
|
81
|
+
this.value = /** @type {HTMLInputElement} */ (e.target).value;
|
|
76
82
|
this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
|
|
77
83
|
}
|
|
78
84
|
|
|
79
85
|
/** Combobox mode: sp-menu @change handler */
|
|
80
|
-
_handleMenuChange(/** @type {
|
|
86
|
+
_handleMenuChange(/** @type {Event} */ e) {
|
|
81
87
|
e.stopPropagation();
|
|
82
|
-
if (!e.target.value) return;
|
|
83
|
-
this.value = e.target.value;
|
|
88
|
+
if (!(/** @type {HTMLInputElement} */ (e.target).value)) return;
|
|
89
|
+
this.value = /** @type {HTMLInputElement} */ (e.target).value;
|
|
84
90
|
this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
|
|
85
91
|
}
|
|
86
92
|
|
|
87
93
|
/** Combobox mode: textfield @input handler */
|
|
88
|
-
_handleInput(/** @type {
|
|
94
|
+
_handleInput(/** @type {Event} */ e) {
|
|
89
95
|
e.stopPropagation();
|
|
90
|
-
this.value = e.target.value;
|
|
96
|
+
this.value = /** @type {HTMLInputElement} */ (e.target).value;
|
|
91
97
|
this.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
/** Set popover min-width to match trigger width (replicates sp-picker behavior) */
|
|
95
|
-
_setPopoverWidth(/** @type {
|
|
101
|
+
_setPopoverWidth(/** @type {Event} */ e) {
|
|
96
102
|
const group = this.querySelector(".jx-combobox-group");
|
|
97
103
|
const w = group ? /** @type {HTMLElement} */ (group).offsetWidth : 0;
|
|
98
|
-
const popover = e.target.querySelector("sp-popover");
|
|
99
|
-
if (popover && w) popover.style.minWidth = `${w}px`;
|
|
104
|
+
const popover = /** @type {HTMLElement} */ (e.target).querySelector("sp-popover");
|
|
105
|
+
if (popover && w) /** @type {HTMLElement} */ (popover).style.minWidth = `${w}px`;
|
|
100
106
|
}
|
|
101
107
|
|
|
102
108
|
render() {
|