@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
|
@@ -42,6 +42,16 @@ import {
|
|
|
42
42
|
} from "./style-utils.js";
|
|
43
43
|
import { widgetForType } from "./style-inputs.js";
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {{ name: string; entry: Record<string, unknown> }} CssLonghand
|
|
47
|
+
*
|
|
48
|
+
* @typedef {Record<string, unknown>} CssPropertyEntry
|
|
49
|
+
*
|
|
50
|
+
* @typedef {import("../tabs/tab.js").Tab} Tab
|
|
51
|
+
*
|
|
52
|
+
* @typedef {import("../state.js").JxPath} JxPath
|
|
53
|
+
*/
|
|
54
|
+
|
|
45
55
|
/**
|
|
46
56
|
* Check if a selector is a stylebook tag path (e.g., "table" or "table th"). Tag paths don't start
|
|
47
57
|
* with selector prefixes (`:`, `.`, `&`, `[`, `@`).
|
|
@@ -56,31 +66,32 @@ function isTagPath(selector) {
|
|
|
56
66
|
/**
|
|
57
67
|
* Resolve a style object by traversing a nested tag path. e.g., "table th" → style["table"]["th"]
|
|
58
68
|
*
|
|
59
|
-
* @param {Record<string,
|
|
69
|
+
* @param {Record<string, unknown>} style
|
|
60
70
|
* @param {string} tagPath
|
|
61
|
-
* @returns {Record<string,
|
|
71
|
+
* @returns {Record<string, unknown>}
|
|
62
72
|
*/
|
|
63
73
|
function resolveNestedTagStyle(style, tagPath) {
|
|
64
74
|
const parts = tagPath.split(" ");
|
|
75
|
+
/** @type {unknown} */
|
|
65
76
|
let obj = style;
|
|
66
77
|
for (const part of parts) {
|
|
67
78
|
if (!obj || typeof obj !== "object") return {};
|
|
68
|
-
obj = obj[part];
|
|
79
|
+
obj = /** @type {Record<string, unknown>} */ (obj)[part];
|
|
69
80
|
}
|
|
70
|
-
return obj && typeof obj === "object" ? obj : {};
|
|
81
|
+
return obj && typeof obj === "object" ? /** @type {Record<string, unknown>} */ (obj) : {};
|
|
71
82
|
}
|
|
72
83
|
|
|
73
84
|
// ─── Row renderers ──────────────────────────────────────────────────────────
|
|
74
85
|
|
|
75
86
|
function renderStyleRow(
|
|
76
|
-
/** @type {
|
|
77
|
-
/** @type {
|
|
78
|
-
/** @type {
|
|
79
|
-
/** @type {
|
|
80
|
-
/** @type {
|
|
81
|
-
/** @type {
|
|
82
|
-
/** @type {
|
|
83
|
-
/** @type {
|
|
87
|
+
/** @type {CssPropertyEntry} */ entry,
|
|
88
|
+
/** @type {string} */ prop,
|
|
89
|
+
/** @type {string} */ value,
|
|
90
|
+
/** @type {(v: string | undefined) => void} */ onCommit,
|
|
91
|
+
/** @type {() => void} */ onDelete,
|
|
92
|
+
/** @type {boolean} */ isWarning,
|
|
93
|
+
/** @type {boolean} */ gridMode,
|
|
94
|
+
/** @type {string | undefined} */ inheritedValue,
|
|
84
95
|
) {
|
|
85
96
|
const type = inferInputType(entry);
|
|
86
97
|
const hasVal = value !== undefined && value !== "";
|
|
@@ -91,23 +102,30 @@ function renderStyleRow(
|
|
|
91
102
|
hasValue: hasVal,
|
|
92
103
|
onClear: onDelete,
|
|
93
104
|
widget: widgetForType(type, entry, prop, value, onCommit, { placeholder }),
|
|
94
|
-
span: gridMode && entry.$span === 2 ? 2 : undefined,
|
|
105
|
+
span: gridMode && /** @type {Record<string, unknown>} */ (entry).$span === 2 ? 2 : undefined,
|
|
95
106
|
warning: isWarning,
|
|
96
107
|
});
|
|
97
108
|
}
|
|
98
109
|
|
|
99
110
|
/**
|
|
100
|
-
* @param {
|
|
101
|
-
* @param {
|
|
111
|
+
* @param {string} shortProp
|
|
112
|
+
* @param {CssPropertyEntry} entry
|
|
113
|
+
* @param {Record<string, unknown>} style
|
|
114
|
+
* @param {(t: Tab, prop: string, val: string | Record<string, unknown> | undefined) => void} mutateFn
|
|
115
|
+
* @param {() => void} _deleteFn
|
|
116
|
+
* @param {Record<string, string | number>} inherited
|
|
102
117
|
*/
|
|
103
118
|
function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inherited = {}) {
|
|
104
119
|
const tab = activeTab.value;
|
|
105
|
-
const longhands = getLonghands(shortProp);
|
|
120
|
+
const longhands = /** @type {CssLonghand[]} */ (getLonghands(shortProp));
|
|
106
121
|
const shortVal = style[shortProp];
|
|
107
|
-
const hasLonghands = longhands.some(
|
|
122
|
+
const hasLonghands = longhands.some(
|
|
123
|
+
(/** @type {CssLonghand} */ l) => style[l.name] !== undefined,
|
|
124
|
+
);
|
|
108
125
|
const isExpanded = tab.session.ui.styleShorthands[shortProp] ?? hasLonghands;
|
|
109
126
|
const hasAnyVal =
|
|
110
|
-
shortVal !== undefined ||
|
|
127
|
+
shortVal !== undefined ||
|
|
128
|
+
longhands.some((/** @type {CssLonghand} */ l) => style[l.name] !== undefined);
|
|
111
129
|
|
|
112
130
|
return html`
|
|
113
131
|
<div class="style-row" data-prop=${shortProp}>
|
|
@@ -116,7 +134,7 @@ function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inheri
|
|
|
116
134
|
? html`<span
|
|
117
135
|
class="set-dot"
|
|
118
136
|
title="Clear ${shortProp}"
|
|
119
|
-
@click=${(/** @type {
|
|
137
|
+
@click=${(/** @type {Event} */ e) => {
|
|
120
138
|
e.stopPropagation();
|
|
121
139
|
transactDoc(activeTab.value, (t) => {
|
|
122
140
|
if (shortVal !== undefined) mutateFn(t, shortProp, undefined);
|
|
@@ -134,25 +152,27 @@ function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inheri
|
|
|
134
152
|
size="s"
|
|
135
153
|
.value=${live(shortVal || "")}
|
|
136
154
|
placeholder=${!shortVal && hasLonghands
|
|
137
|
-
? longhands.map((/** @type {
|
|
155
|
+
? longhands.map((/** @type {CssLonghand} */ l) => style[l.name] || "0").join(" ")
|
|
138
156
|
: !shortVal && inherited[shortProp]
|
|
139
157
|
? inherited[shortProp]
|
|
140
|
-
: !shortVal && longhands.some((/** @type {
|
|
141
|
-
? longhands
|
|
158
|
+
: !shortVal && longhands.some((/** @type {CssLonghand} */ l) => inherited[l.name])
|
|
159
|
+
? longhands
|
|
160
|
+
.map((/** @type {CssLonghand} */ l) => inherited[l.name] || "0")
|
|
161
|
+
.join(" ")
|
|
142
162
|
: ""}
|
|
143
|
-
@input=${debouncedStyleCommit(`short:${shortProp}`, 400, (/** @type {
|
|
163
|
+
@input=${debouncedStyleCommit(`short:${shortProp}`, 400, (/** @type {Event} */ e) => {
|
|
144
164
|
transactDoc(activeTab.value, (t) => {
|
|
145
165
|
for (const l of longhands) {
|
|
146
166
|
if (style[l.name] !== undefined) mutateFn(t, l.name, undefined);
|
|
147
167
|
}
|
|
148
|
-
mutateFn(t, shortProp, e.target.value || undefined);
|
|
168
|
+
mutateFn(t, shortProp, /** @type {HTMLInputElement} */ (e.target).value || undefined);
|
|
149
169
|
});
|
|
150
170
|
})}
|
|
151
171
|
></sp-textfield>
|
|
152
172
|
<sp-action-button
|
|
153
173
|
size="xs"
|
|
154
174
|
quiet
|
|
155
|
-
@click=${(/** @type {
|
|
175
|
+
@click=${(/** @type {Event} */ e) => {
|
|
156
176
|
e.stopPropagation();
|
|
157
177
|
activeTab.value.session.ui.styleShorthands = {
|
|
158
178
|
...activeTab.value.session.ui.styleShorthands,
|
|
@@ -168,16 +188,17 @@ function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inheri
|
|
|
168
188
|
</div>
|
|
169
189
|
${isExpanded
|
|
170
190
|
? (() => {
|
|
171
|
-
const isBorderSide = entry.$shorthandType ===
|
|
191
|
+
const isBorderSide = /** @type {Record<string, unknown>} */ (entry).$shorthandType ===
|
|
192
|
+
"border-side";
|
|
172
193
|
const expanded = shortVal
|
|
173
194
|
? isBorderSide
|
|
174
|
-
? expandBorderSide(shortVal)
|
|
175
|
-
: expandShorthand(shortVal, longhands.length)
|
|
195
|
+
? expandBorderSide(/** @type {string} */ (shortVal))
|
|
196
|
+
: expandShorthand(/** @type {string} */ (shortVal), longhands.length)
|
|
176
197
|
: null;
|
|
177
198
|
const compress = isBorderSide ? compressBorderSide : compressShorthand;
|
|
178
199
|
const emptyVal = isBorderSide ? "" : "0";
|
|
179
200
|
return longhands.map(
|
|
180
|
-
(/** @type {
|
|
201
|
+
(/** @type {CssLonghand} */ { name, entry: lEntry }, /** @type {number} */ idx) => {
|
|
181
202
|
const lVal = style[name] ?? (expanded ? expanded[idx] : "");
|
|
182
203
|
return html`
|
|
183
204
|
<div class="style-row style-row--child" data-prop=${name}>
|
|
@@ -186,10 +207,10 @@ function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inheri
|
|
|
186
207
|
? html`<span
|
|
187
208
|
class="set-dot"
|
|
188
209
|
title="Clear ${name}"
|
|
189
|
-
@click=${(/** @type {
|
|
210
|
+
@click=${(/** @type {Event} */ e) => {
|
|
190
211
|
e.stopPropagation();
|
|
191
212
|
const vals = longhands.map(
|
|
192
|
-
(/** @type {
|
|
213
|
+
(/** @type {CssLonghand} */ l, /** @type {number} */ i) =>
|
|
193
214
|
i === idx
|
|
194
215
|
? emptyVal
|
|
195
216
|
: (style[l.name] ?? (expanded ? expanded[i] : emptyVal)),
|
|
@@ -198,7 +219,7 @@ function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inheri
|
|
|
198
219
|
for (const l of longhands) {
|
|
199
220
|
if (style[l.name] !== undefined) mutateFn(t, l.name, undefined);
|
|
200
221
|
}
|
|
201
|
-
mutateFn(t, shortProp, compress(vals));
|
|
222
|
+
mutateFn(t, shortProp, compress(/** @type {string[]} */ (vals)));
|
|
202
223
|
});
|
|
203
224
|
}}
|
|
204
225
|
></span>`
|
|
@@ -212,17 +233,18 @@ function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inheri
|
|
|
212
233
|
lEntry,
|
|
213
234
|
name,
|
|
214
235
|
lVal,
|
|
215
|
-
(/** @type {
|
|
216
|
-
const vals = longhands.map(
|
|
217
|
-
i
|
|
218
|
-
|
|
219
|
-
|
|
236
|
+
(/** @type {string} */ newVal) => {
|
|
237
|
+
const vals = longhands.map(
|
|
238
|
+
(/** @type {CssLonghand} */ l, /** @type {number} */ i) =>
|
|
239
|
+
i === idx
|
|
240
|
+
? newVal || emptyVal
|
|
241
|
+
: (style[l.name] ?? (expanded ? expanded[i] : emptyVal)),
|
|
220
242
|
);
|
|
221
243
|
transactDoc(activeTab.value, (t) => {
|
|
222
244
|
for (const l of longhands) {
|
|
223
245
|
if (style[l.name] !== undefined) mutateFn(t, l.name, undefined);
|
|
224
246
|
}
|
|
225
|
-
mutateFn(t, shortProp, compress(vals));
|
|
247
|
+
mutateFn(t, shortProp, compress(/** @type {string[]} */ (vals)));
|
|
226
248
|
});
|
|
227
249
|
},
|
|
228
250
|
{ placeholder: !lVal && inherited[name] ? String(inherited[name]) : "" },
|
|
@@ -239,14 +261,14 @@ function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inheri
|
|
|
239
261
|
// ─── Main template ──────────────────────────────────────────────────────────
|
|
240
262
|
|
|
241
263
|
/**
|
|
242
|
-
* @param {
|
|
243
|
-
* @param {
|
|
244
|
-
* @param {
|
|
245
|
-
* @param {Record<string,
|
|
264
|
+
* @param {JxMutableNode} node
|
|
265
|
+
* @param {string | null} activeMediaTab
|
|
266
|
+
* @param {string | null} activeSelector
|
|
267
|
+
* @param {Record<string, unknown>} [effectiveStyle]
|
|
246
268
|
*/
|
|
247
269
|
function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveStyle) {
|
|
248
270
|
const tab = activeTab.value;
|
|
249
|
-
const sel = /** @type {
|
|
271
|
+
const sel = /** @type {JxPath} */ (tab.session.selection);
|
|
250
272
|
const style = effectiveStyle || node.style || {};
|
|
251
273
|
const { sizeBreakpoints } = parseMediaEntries(getEffectiveMedia(tab.doc.document.$media));
|
|
252
274
|
const mediaNames = sizeBreakpoints.map((bp) => bp.name);
|
|
@@ -259,8 +281,8 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
259
281
|
<sp-tabs
|
|
260
282
|
size="s"
|
|
261
283
|
selected=${mediaTab || "base"}
|
|
262
|
-
@change=${(/** @type {
|
|
263
|
-
const val = e.target.selected;
|
|
284
|
+
@change=${(/** @type {Event} */ e) => {
|
|
285
|
+
const val = /** @type {HTMLElement & { selected: string }} */ (e.target).selected;
|
|
264
286
|
const newMedia = val === "base" ? null : val;
|
|
265
287
|
if (newMedia !== tab.session.ui.activeMedia) {
|
|
266
288
|
tab.session.ui.activeMedia = newMedia;
|
|
@@ -276,7 +298,9 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
276
298
|
: nothing;
|
|
277
299
|
|
|
278
300
|
// ── Selector dropdown ──────────────────────────────────────────────────────
|
|
279
|
-
const contextStyle = mediaTab
|
|
301
|
+
const contextStyle = mediaTab
|
|
302
|
+
? /** @type {Record<string, unknown>} */ (style[`@${mediaTab}`]) || {}
|
|
303
|
+
: style;
|
|
280
304
|
const existingSelectors = Object.keys(contextStyle).filter(isNestedSelector);
|
|
281
305
|
const existingSet = new Set(existingSelectors);
|
|
282
306
|
const commonSet = new Set(COMMON_SELECTORS);
|
|
@@ -292,14 +316,15 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
292
316
|
class="selector-select"
|
|
293
317
|
quiet
|
|
294
318
|
.value=${live(_selectorVal)}
|
|
295
|
-
@change=${(/** @type {
|
|
296
|
-
const val = e.target.value;
|
|
319
|
+
@change=${(/** @type {Event} */ e) => {
|
|
320
|
+
const val = /** @type {HTMLElement & { value: string }} */ (e.target).value;
|
|
297
321
|
if (val === "__add_custom__") {
|
|
298
322
|
requestAnimationFrame(() => {
|
|
299
|
-
e.target.value =
|
|
323
|
+
/** @type {HTMLElement & { value: string }} */ (e.target).value =
|
|
324
|
+
activeSelector || "__base__";
|
|
300
325
|
});
|
|
301
|
-
const picker = e.target;
|
|
302
|
-
const bar = picker.closest(".style-toolbar");
|
|
326
|
+
const picker = /** @type {HTMLElement} */ (e.target);
|
|
327
|
+
const bar = /** @type {HTMLElement} */ (picker.closest(".style-toolbar"));
|
|
303
328
|
picker.style.display = "none";
|
|
304
329
|
const inp = document.createElement("input");
|
|
305
330
|
inp.type = "text";
|
|
@@ -308,7 +333,7 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
308
333
|
bar.appendChild(inp);
|
|
309
334
|
inp.focus();
|
|
310
335
|
let done = false;
|
|
311
|
-
const finish = (/** @type {
|
|
336
|
+
const finish = (/** @type {boolean} */ accept) => {
|
|
312
337
|
if (done) return;
|
|
313
338
|
done = true;
|
|
314
339
|
const v = inp.value.trim();
|
|
@@ -363,8 +388,8 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
363
388
|
class="style-filter-input"
|
|
364
389
|
placeholder="Filter properties…"
|
|
365
390
|
.value=${live(tab.session.ui.styleFilter || "")}
|
|
366
|
-
@input=${(/** @type {
|
|
367
|
-
activeTab.value.session.ui.styleFilter = e.target.value;
|
|
391
|
+
@input=${(/** @type {Event} */ e) => {
|
|
392
|
+
activeTab.value.session.ui.styleFilter = /** @type {HTMLInputElement} */ (e.target).value;
|
|
368
393
|
}}
|
|
369
394
|
></sp-textfield>
|
|
370
395
|
<sp-action-button
|
|
@@ -382,61 +407,119 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
382
407
|
`;
|
|
383
408
|
|
|
384
409
|
// ── Determine the active style object ──────────────────────────────────────
|
|
385
|
-
/** @type {Record<string,
|
|
410
|
+
/** @type {Record<string, unknown>} */
|
|
386
411
|
let activeStyle;
|
|
387
|
-
/** @type {(prop:
|
|
412
|
+
/** @type {(prop: string, val: string | Record<string, unknown> | undefined) => void} */
|
|
388
413
|
let commitStyle;
|
|
389
|
-
/** @type {(t:
|
|
414
|
+
/** @type {(t: Tab, prop: string, val: string | Record<string, unknown> | undefined) => void} */
|
|
390
415
|
let commitMutate;
|
|
391
416
|
if (activeSelector && isTagPath(activeSelector) && mediaTab && mediaNames.length > 0) {
|
|
392
|
-
const mediaObj = style[`@${mediaTab}`] || {};
|
|
417
|
+
const mediaObj = /** @type {Record<string, unknown>} */ (style[`@${mediaTab}`]) || {};
|
|
393
418
|
activeStyle = resolveNestedTagStyle(mediaObj, activeSelector);
|
|
394
419
|
const stylePath = activeSelector.split(" ");
|
|
395
|
-
commitMutate = (
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
420
|
+
commitMutate = (
|
|
421
|
+
/** @type {Tab} */ t,
|
|
422
|
+
/** @type {string} */ prop,
|
|
423
|
+
/** @type {string | Record<string, unknown> | undefined} */ val,
|
|
424
|
+
) =>
|
|
425
|
+
mutateUpdateMediaNestedStylePath(
|
|
426
|
+
t,
|
|
427
|
+
sel,
|
|
428
|
+
mediaTab,
|
|
429
|
+
stylePath,
|
|
430
|
+
prop,
|
|
431
|
+
/** @type {string | undefined} */ (val),
|
|
432
|
+
);
|
|
433
|
+
commitStyle = (
|
|
434
|
+
/** @type {string} */ prop,
|
|
435
|
+
/** @type {string | Record<string, unknown> | undefined} */ val,
|
|
436
|
+
) => transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
|
|
399
437
|
} else if (activeSelector && isTagPath(activeSelector)) {
|
|
400
438
|
activeStyle = resolveNestedTagStyle(style, activeSelector);
|
|
401
439
|
const stylePath = activeSelector.split(" ");
|
|
402
|
-
commitMutate = (
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
440
|
+
commitMutate = (
|
|
441
|
+
/** @type {Tab} */ t,
|
|
442
|
+
/** @type {string} */ prop,
|
|
443
|
+
/** @type {string | Record<string, unknown> | undefined} */ val,
|
|
444
|
+
) =>
|
|
445
|
+
mutateUpdateNestedStylePath(t, sel, stylePath, prop, /** @type {string | undefined} */ (val));
|
|
446
|
+
commitStyle = (
|
|
447
|
+
/** @type {string} */ prop,
|
|
448
|
+
/** @type {string | Record<string, unknown> | undefined} */ val,
|
|
449
|
+
) => transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
|
|
406
450
|
} else if (activeSelector && mediaTab && mediaNames.length > 0) {
|
|
407
|
-
activeStyle =
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
451
|
+
activeStyle = /** @type {Record<string, unknown>} */ (
|
|
452
|
+
/** @type {Record<string, unknown>} */ (style[`@${mediaTab}`] || {})[activeSelector]
|
|
453
|
+
) || {};
|
|
454
|
+
commitMutate = (
|
|
455
|
+
/** @type {Tab} */ t,
|
|
456
|
+
/** @type {string} */ prop,
|
|
457
|
+
/** @type {string | Record<string, unknown> | undefined} */ val,
|
|
458
|
+
) =>
|
|
459
|
+
mutateUpdateMediaNestedStyle(
|
|
460
|
+
t,
|
|
461
|
+
sel,
|
|
462
|
+
mediaTab,
|
|
463
|
+
activeSelector,
|
|
464
|
+
prop,
|
|
465
|
+
/** @type {string | undefined} */ (val),
|
|
466
|
+
);
|
|
467
|
+
commitStyle = (
|
|
468
|
+
/** @type {string} */ prop,
|
|
469
|
+
/** @type {string | Record<string, unknown> | undefined} */ val,
|
|
470
|
+
) => transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
|
|
412
471
|
} else if (activeSelector) {
|
|
413
|
-
activeStyle = style[activeSelector] || {};
|
|
414
|
-
commitMutate = (
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
472
|
+
activeStyle = /** @type {Record<string, unknown>} */ (style[activeSelector]) || {};
|
|
473
|
+
commitMutate = (
|
|
474
|
+
/** @type {Tab} */ t,
|
|
475
|
+
/** @type {string} */ prop,
|
|
476
|
+
/** @type {string | Record<string, unknown> | undefined} */ val,
|
|
477
|
+
) =>
|
|
478
|
+
mutateUpdateNestedStyle(
|
|
479
|
+
t,
|
|
480
|
+
sel,
|
|
481
|
+
activeSelector,
|
|
482
|
+
prop,
|
|
483
|
+
/** @type {string | undefined} */ (val),
|
|
484
|
+
);
|
|
485
|
+
commitStyle = (
|
|
486
|
+
/** @type {string} */ prop,
|
|
487
|
+
/** @type {string | Record<string, unknown> | undefined} */ val,
|
|
488
|
+
) => transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
|
|
418
489
|
} else if (mediaTab !== null && mediaNames.length > 0) {
|
|
419
490
|
activeStyle = {};
|
|
420
|
-
for (const [p, v] of Object.entries(
|
|
491
|
+
for (const [p, v] of Object.entries(
|
|
492
|
+
/** @type {Record<string, unknown>} */ (style[`@${mediaTab}`]) || {},
|
|
493
|
+
)) {
|
|
421
494
|
if (typeof v !== "object") activeStyle[p] = v;
|
|
422
495
|
}
|
|
423
|
-
commitMutate = (
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
496
|
+
commitMutate = (
|
|
497
|
+
/** @type {Tab} */ t,
|
|
498
|
+
/** @type {string} */ prop,
|
|
499
|
+
/** @type {string | Record<string, unknown> | undefined} */ val,
|
|
500
|
+
) => mutateUpdateMediaStyle(t, sel, mediaTab, prop, /** @type {string | undefined} */ (val));
|
|
501
|
+
commitStyle = (
|
|
502
|
+
/** @type {string} */ prop,
|
|
503
|
+
/** @type {string | Record<string, unknown> | undefined} */ val,
|
|
504
|
+
) => transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
|
|
427
505
|
} else {
|
|
428
506
|
activeStyle = {};
|
|
429
507
|
for (const [p, v] of Object.entries(style)) {
|
|
430
508
|
if (typeof v !== "object") activeStyle[p] = v;
|
|
431
509
|
}
|
|
432
|
-
commitMutate = (
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
510
|
+
commitMutate = (
|
|
511
|
+
/** @type {Tab} */ t,
|
|
512
|
+
/** @type {string} */ prop,
|
|
513
|
+
/** @type {string | Record<string, unknown> | undefined} */ val,
|
|
514
|
+
) => mutateUpdateStyle(t, sel, prop, /** @type {string | undefined} */ (val));
|
|
515
|
+
commitStyle = (
|
|
516
|
+
/** @type {string} */ prop,
|
|
517
|
+
/** @type {string | Record<string, unknown> | undefined} */ val,
|
|
518
|
+
) => transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
|
|
436
519
|
}
|
|
437
520
|
|
|
438
521
|
// ── Compute inherited style from higher breakpoints ──────────────────────
|
|
439
|
-
/** @type {Record<string,
|
|
522
|
+
/** @type {Record<string, string | number>} */
|
|
440
523
|
const inheritedStyle = computeInheritedStyle(style, mediaNames, mediaTab, activeSelector);
|
|
441
524
|
|
|
442
525
|
// Auto-open sections that have properties
|
|
@@ -446,23 +529,32 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
446
529
|
}
|
|
447
530
|
|
|
448
531
|
// Partition properties into sections
|
|
449
|
-
const sectionProps =
|
|
532
|
+
const sectionProps =
|
|
533
|
+
/** @type {Record<string, { prop: string; entry: CssPropertyEntry }[]>} */ ({});
|
|
450
534
|
for (const sec of cssMeta.$sections) sectionProps[sec.key] = [];
|
|
451
535
|
|
|
452
|
-
for (const [prop, entry] of /** @type {[string,
|
|
453
|
-
|
|
454
|
-
|
|
536
|
+
for (const [prop, entry] of /** @type {[string, CssPropertyEntry][]} */ (
|
|
537
|
+
Object.entries(cssMeta.$defs)
|
|
538
|
+
)) {
|
|
539
|
+
if (typeof (/** @type {Record<string, unknown>} */ (entry).$shorthand) === "string") continue;
|
|
540
|
+
const sec = /** @type {string} */ (/** @type {Record<string, unknown>} */ (entry).$section) ||
|
|
541
|
+
"other";
|
|
455
542
|
sectionProps[sec].push({ prop, entry });
|
|
456
543
|
}
|
|
457
544
|
for (const sec of cssMeta.$sections) {
|
|
458
545
|
sectionProps[sec.key].sort(
|
|
459
|
-
(
|
|
546
|
+
(
|
|
547
|
+
/** @type {{ prop: string; entry: CssPropertyEntry }} */ a,
|
|
548
|
+
/** @type {{ prop: string; entry: CssPropertyEntry }} */ b,
|
|
549
|
+
) =>
|
|
550
|
+
/** @type {number} */ (/** @type {Record<string, unknown>} */ (a.entry).$order) -
|
|
551
|
+
/** @type {number} */ (/** @type {Record<string, unknown>} */ (b.entry).$order),
|
|
460
552
|
);
|
|
461
553
|
}
|
|
462
554
|
|
|
463
555
|
const otherProps = [];
|
|
464
556
|
for (const prop of Object.keys(activeStyle)) {
|
|
465
|
-
if (!(/** @type {Record<string,
|
|
557
|
+
if (!(/** @type {Record<string, unknown>} */ (cssMeta.$defs)[prop])) {
|
|
466
558
|
const val = activeStyle[prop];
|
|
467
559
|
if (val !== null && typeof val === "object") continue;
|
|
468
560
|
otherProps.push(prop);
|
|
@@ -488,15 +580,17 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
488
580
|
.map((sec) => {
|
|
489
581
|
const entries = sectionProps[sec.key];
|
|
490
582
|
|
|
491
|
-
const sectionActiveProps = entries.filter(
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
583
|
+
const sectionActiveProps = entries.filter(
|
|
584
|
+
(/** @type {{ prop: string; entry: CssPropertyEntry }} */ { prop, entry }) => {
|
|
585
|
+
if (activeStyle[prop] !== undefined) return true;
|
|
586
|
+
if (inferInputType(entry) === "shorthand") {
|
|
587
|
+
return /** @type {CssLonghand[]} */ (getLonghands(prop)).some(
|
|
588
|
+
(/** @type {CssLonghand} */ l) => activeStyle[l.name] !== undefined,
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
return false;
|
|
592
|
+
},
|
|
593
|
+
);
|
|
500
594
|
|
|
501
595
|
const rows = [];
|
|
502
596
|
for (const { prop, entry } of entries) {
|
|
@@ -512,17 +606,19 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
512
606
|
}
|
|
513
607
|
if (filterActive) {
|
|
514
608
|
if (type === "shorthand") {
|
|
515
|
-
const longhands = getLonghands(prop);
|
|
609
|
+
const longhands = /** @type {CssLonghand[]} */ (getLonghands(prop));
|
|
516
610
|
const hasAnySet =
|
|
517
|
-
hasVal ||
|
|
611
|
+
hasVal ||
|
|
612
|
+
longhands.some((/** @type {CssLonghand} */ l) => activeStyle[l.name] !== undefined);
|
|
518
613
|
if (!hasAnySet) continue;
|
|
519
614
|
} else if (!hasVal) continue;
|
|
520
615
|
}
|
|
521
616
|
|
|
522
617
|
if (type === "shorthand") {
|
|
523
|
-
const longhands = getLonghands(prop);
|
|
618
|
+
const longhands = /** @type {CssLonghand[]} */ (getLonghands(prop));
|
|
524
619
|
const hasAny =
|
|
525
|
-
hasVal ||
|
|
620
|
+
hasVal ||
|
|
621
|
+
longhands.some((/** @type {CssLonghand} */ l) => activeStyle[l.name] !== undefined);
|
|
526
622
|
if (!hasAny && !condMet) continue;
|
|
527
623
|
rows.push(
|
|
528
624
|
renderShorthandRow(prop, entry, activeStyle, commitMutate, () => {}, inheritedStyle),
|
|
@@ -534,12 +630,13 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
534
630
|
renderStyleRow(
|
|
535
631
|
entry,
|
|
536
632
|
prop,
|
|
537
|
-
val ?? "",
|
|
538
|
-
(/** @type {
|
|
633
|
+
/** @type {string} */ (val) ?? "",
|
|
634
|
+
(/** @type {string | undefined} */ newVal) =>
|
|
635
|
+
commitStyle(prop, newVal || undefined),
|
|
539
636
|
() => commitStyle(prop, undefined),
|
|
540
637
|
isWarning,
|
|
541
638
|
sec.$layout === "grid",
|
|
542
|
-
inheritedStyle[prop],
|
|
639
|
+
/** @type {string | undefined} */ (inheritedStyle[prop]),
|
|
543
640
|
),
|
|
544
641
|
);
|
|
545
642
|
}
|
|
@@ -553,10 +650,10 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
553
650
|
<sp-accordion-item
|
|
554
651
|
label=${sec.label}
|
|
555
652
|
.open=${isOpen}
|
|
556
|
-
@sp-accordion-item-toggle=${(/** @type {
|
|
653
|
+
@sp-accordion-item-toggle=${(/** @type {Event} */ e) => {
|
|
557
654
|
activeTab.value.session.ui.styleSections = {
|
|
558
655
|
...activeTab.value.session.ui.styleSections,
|
|
559
|
-
[sec.key]: e.target.open,
|
|
656
|
+
[sec.key]: /** @type {HTMLElement & { open: boolean }} */ (e.target).open,
|
|
560
657
|
};
|
|
561
658
|
}}
|
|
562
659
|
>
|
|
@@ -567,14 +664,14 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
567
664
|
<span
|
|
568
665
|
class="set-dot set-dot--section"
|
|
569
666
|
title="Clear all ${sec.label.toLowerCase()} properties"
|
|
570
|
-
@click=${(/** @type {
|
|
667
|
+
@click=${(/** @type {Event} */ e) => {
|
|
571
668
|
e.stopPropagation();
|
|
572
669
|
e.preventDefault();
|
|
573
670
|
transactDoc(activeTab.value, (t) => {
|
|
574
671
|
for (const { prop, entry } of sectionActiveProps) {
|
|
575
672
|
if (activeStyle[prop] !== undefined) commitMutate(t, prop, undefined);
|
|
576
673
|
if (inferInputType(entry) === "shorthand") {
|
|
577
|
-
for (const l of getLonghands(prop)) {
|
|
674
|
+
for (const l of /** @type {CssLonghand[]} */ (getLonghands(prop))) {
|
|
578
675
|
if (activeStyle[l.name] !== undefined)
|
|
579
676
|
commitMutate(t, l.name, undefined);
|
|
580
677
|
}
|
|
@@ -598,10 +695,10 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
598
695
|
<sp-accordion-item
|
|
599
696
|
label="Custom"
|
|
600
697
|
.open=${customIsOpen}
|
|
601
|
-
@sp-accordion-item-toggle=${(/** @type {
|
|
698
|
+
@sp-accordion-item-toggle=${(/** @type {Event} */ e) => {
|
|
602
699
|
activeTab.value.session.ui.styleSections = {
|
|
603
700
|
...activeTab.value.session.ui.styleSections,
|
|
604
|
-
other: e.target.open,
|
|
701
|
+
other: /** @type {HTMLElement & { open: boolean }} */ (e.target).open,
|
|
605
702
|
};
|
|
606
703
|
}}
|
|
607
704
|
>
|
|
@@ -613,8 +710,8 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
613
710
|
size="s"
|
|
614
711
|
class="kv-key"
|
|
615
712
|
.value=${live(prop)}
|
|
616
|
-
@change=${(/** @type {
|
|
617
|
-
const newProp = e.target.value.trim();
|
|
713
|
+
@change=${(/** @type {Event} */ e) => {
|
|
714
|
+
const newProp = /** @type {HTMLInputElement} */ (e.target).value.trim();
|
|
618
715
|
if (newProp && newProp !== prop) {
|
|
619
716
|
transactDoc(activeTab.value, (t) => {
|
|
620
717
|
commitMutate(t, prop, undefined);
|
|
@@ -628,8 +725,8 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
628
725
|
class="kv-val"
|
|
629
726
|
.value=${live(String(activeStyle[prop]))}
|
|
630
727
|
placeholder=${ifDefined(cssInitialMap.get(prop))}
|
|
631
|
-
@input=${debouncedStyleCommit(`custom:${prop}`, 400, (/** @type {
|
|
632
|
-
commitStyle(prop, e.target.value);
|
|
728
|
+
@input=${debouncedStyleCommit(`custom:${prop}`, 400, (/** @type {Event} */ e) => {
|
|
729
|
+
commitStyle(prop, /** @type {HTMLInputElement} */ (e.target).value);
|
|
633
730
|
})}
|
|
634
731
|
></sp-textfield>
|
|
635
732
|
<sp-action-button size="xs" quiet @click=${() => commitStyle(prop, undefined)}>
|
|
@@ -643,14 +740,14 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
643
740
|
size="s"
|
|
644
741
|
placeholder="Property name…"
|
|
645
742
|
style="flex:1"
|
|
646
|
-
@keydown=${(/** @type {
|
|
743
|
+
@keydown=${(/** @type {KeyboardEvent} */ e) => {
|
|
647
744
|
if (e.key === "Enter") {
|
|
648
745
|
e.preventDefault();
|
|
649
|
-
const prop = e.target.value.trim();
|
|
746
|
+
const prop = /** @type {HTMLInputElement} */ (e.target).value.trim();
|
|
650
747
|
if (prop) {
|
|
651
748
|
const initial = cssInitialMap.get(prop) || "";
|
|
652
749
|
commitStyle(prop, initial || "");
|
|
653
|
-
e.target.value = "";
|
|
750
|
+
/** @type {HTMLInputElement} */ (e.target).value = "";
|
|
654
751
|
}
|
|
655
752
|
}
|
|
656
753
|
}}
|
|
@@ -668,10 +765,10 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector, effectiveSty
|
|
|
668
765
|
<sp-accordion-item
|
|
669
766
|
label="Relative Styling"
|
|
670
767
|
.open=${nestedIsOpen}
|
|
671
|
-
@sp-accordion-item-toggle=${(/** @type {
|
|
768
|
+
@sp-accordion-item-toggle=${(/** @type {Event} */ e) => {
|
|
672
769
|
activeTab.value.session.ui.styleSections = {
|
|
673
770
|
...activeTab.value.session.ui.styleSections,
|
|
674
|
-
nested: e.target.open,
|
|
771
|
+
nested: /** @type {HTMLElement & { open: boolean }} */ (e.target).open,
|
|
675
772
|
};
|
|
676
773
|
}}
|
|
677
774
|
>
|
|
@@ -755,17 +852,20 @@ export function renderStylePanelTemplate(ctx) {
|
|
|
755
852
|
|
|
756
853
|
/** Single property input row (generic field row helper) */
|
|
757
854
|
export function _fieldRow(
|
|
758
|
-
/** @type {
|
|
759
|
-
/** @type {
|
|
760
|
-
/** @type {
|
|
761
|
-
/** @type {
|
|
762
|
-
/** @type {
|
|
855
|
+
/** @type {string} */ label,
|
|
856
|
+
/** @type {string} */ type,
|
|
857
|
+
/** @type {string} */ value,
|
|
858
|
+
/** @type {(v: string | boolean) => void} */ onChange,
|
|
859
|
+
/** @type {string | undefined} */ _datalistId,
|
|
763
860
|
) {
|
|
764
|
-
/** @type {
|
|
861
|
+
/** @type {ReturnType<typeof setTimeout> | undefined} */
|
|
765
862
|
let debounceTimer;
|
|
766
|
-
const onInput = (/** @type {
|
|
863
|
+
const onInput = (/** @type {Event} */ e) => {
|
|
767
864
|
clearTimeout(debounceTimer);
|
|
768
|
-
debounceTimer = setTimeout(
|
|
865
|
+
debounceTimer = setTimeout(
|
|
866
|
+
() => onChange(/** @type {HTMLInputElement} */ (e.target).value),
|
|
867
|
+
400,
|
|
868
|
+
);
|
|
769
869
|
};
|
|
770
870
|
const inputTpl =
|
|
771
871
|
type === "textarea"
|
|
@@ -778,7 +878,8 @@ export function _fieldRow(
|
|
|
778
878
|
: type === "checkbox"
|
|
779
879
|
? html`<sp-checkbox
|
|
780
880
|
?checked=${!!value}
|
|
781
|
-
@change=${(/** @type {
|
|
881
|
+
@change=${(/** @type {Event} */ e) =>
|
|
882
|
+
onChange(/** @type {HTMLInputElement} */ (e.target).checked)}
|
|
782
883
|
></sp-checkbox>`
|
|
783
884
|
: html`<sp-textfield size="s" value=${value ?? ""} @input=${onInput}></sp-textfield>`;
|
|
784
885
|
return html`
|