@jxsuite/studio 0.10.2 → 0.13.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/README.md +82 -0
- package/dist/studio.js +5114 -3424
- package/dist/studio.js.map +65 -49
- package/package.json +6 -3
- package/src/browse/browse.js +27 -3
- package/src/canvas/canvas-diff.js +184 -0
- package/src/canvas/canvas-helpers.js +10 -14
- package/src/canvas/canvas-live-render.js +136 -17
- package/src/canvas/canvas-render.js +154 -21
- package/src/canvas/canvas-utils.js +21 -23
- package/src/editor/component-inline-edit.js +54 -41
- package/src/editor/content-inline-edit.js +46 -47
- package/src/editor/context-menu.js +63 -39
- package/src/editor/convert-to-component.js +11 -14
- package/src/editor/insertion-helper.js +8 -10
- package/src/editor/shortcuts.js +69 -39
- package/src/files/components.js +15 -4
- package/src/files/files.js +72 -24
- package/src/panels/activity-bar.js +29 -7
- package/src/panels/block-action-bar.js +104 -80
- package/src/panels/canvas-dnd.js +132 -50
- package/src/panels/dnd.js +32 -28
- package/src/panels/editors.js +7 -14
- package/src/panels/elements-panel.js +9 -3
- package/src/panels/events-panel.js +44 -39
- package/src/panels/git-panel.js +45 -3
- package/src/panels/layers-panel.js +16 -11
- package/src/panels/left-panel.js +108 -43
- package/src/panels/overlays.js +97 -35
- package/src/panels/panel-events.js +18 -11
- package/src/panels/properties-panel.js +467 -98
- package/src/panels/right-panel.js +85 -37
- package/src/panels/shared.js +0 -22
- package/src/panels/signals-panel.js +125 -54
- package/src/panels/statusbar.js +23 -8
- package/src/panels/style-inputs.js +4 -2
- package/src/panels/style-panel.js +128 -105
- package/src/panels/stylebook-panel.js +42 -17
- package/src/panels/tab-strip.js +124 -0
- package/src/panels/toolbar.js +39 -9
- package/src/reactivity.js +28 -0
- package/src/settings/content-types-editor.js +78 -8
- package/src/settings/defs-editor.js +56 -7
- package/src/settings/schema-field-ui.js +99 -11
- package/src/site-context.js +105 -0
- package/src/state.js +0 -456
- package/src/store.js +105 -124
- package/src/studio.js +112 -121
- package/src/tabs/tab.js +153 -0
- package/src/tabs/transact.js +406 -0
- package/src/ui/spectrum.js +2 -0
- package/src/view.js +3 -0
- package/src/workspace/workspace.js +89 -0
|
@@ -7,18 +7,19 @@ import { html, nothing } from "lit-html";
|
|
|
7
7
|
import { live } from "lit-html/directives/live.js";
|
|
8
8
|
import { ifDefined } from "lit-html/directives/if-defined.js";
|
|
9
9
|
import {
|
|
10
|
-
getState,
|
|
11
|
-
update,
|
|
12
|
-
updateUi,
|
|
13
10
|
getNodeAtPath,
|
|
14
|
-
updateStyle,
|
|
15
|
-
updateMediaStyle,
|
|
16
|
-
updateNestedStyle,
|
|
17
|
-
updateMediaNestedStyle,
|
|
18
11
|
COMMON_SELECTORS,
|
|
19
12
|
isNestedSelector,
|
|
20
13
|
debouncedStyleCommit,
|
|
21
14
|
} from "../store.js";
|
|
15
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
16
|
+
import {
|
|
17
|
+
transactDoc,
|
|
18
|
+
mutateUpdateStyle,
|
|
19
|
+
mutateUpdateMediaStyle,
|
|
20
|
+
mutateUpdateNestedStyle,
|
|
21
|
+
mutateUpdateMediaNestedStyle,
|
|
22
|
+
} from "../tabs/transact.js";
|
|
22
23
|
import { inferInputType, propLabel } from "../utils/studio-utils.js";
|
|
23
24
|
import { renderFieldRow } from "../ui/field-row.js";
|
|
24
25
|
import { parseMediaEntries } from "../utils/canvas-media.js";
|
|
@@ -65,15 +66,15 @@ function renderStyleRow(
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
/**
|
|
68
|
-
* @param {any} shortProp @param {any} entry @param {any} style @param {any}
|
|
69
|
+
* @param {any} shortProp @param {any} entry @param {any} style @param {any} mutateFn
|
|
69
70
|
* @param {any} _deleteFn @param {Record<string, any>} inherited
|
|
70
71
|
*/
|
|
71
|
-
function renderShorthandRow(shortProp, entry, style,
|
|
72
|
-
const
|
|
72
|
+
function renderShorthandRow(shortProp, entry, style, mutateFn, _deleteFn, inherited = {}) {
|
|
73
|
+
const tab = activeTab.value;
|
|
73
74
|
const longhands = getLonghands(shortProp);
|
|
74
75
|
const shortVal = style[shortProp];
|
|
75
76
|
const hasLonghands = longhands.some((/** @type {any} */ l) => style[l.name] !== undefined);
|
|
76
|
-
const isExpanded =
|
|
77
|
+
const isExpanded = tab.session.ui.styleShorthands[shortProp] ?? hasLonghands;
|
|
77
78
|
const hasAnyVal =
|
|
78
79
|
shortVal !== undefined || longhands.some((/** @type {any} */ l) => style[l.name] !== undefined);
|
|
79
80
|
|
|
@@ -86,12 +87,12 @@ function renderShorthandRow(shortProp, entry, style, commitFn, _deleteFn, inheri
|
|
|
86
87
|
title="Clear ${shortProp}"
|
|
87
88
|
@click=${(/** @type {any} */ e) => {
|
|
88
89
|
e.stopPropagation();
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
transactDoc(activeTab.value, (t) => {
|
|
91
|
+
if (shortVal !== undefined) mutateFn(t, shortProp, undefined);
|
|
92
|
+
for (const l of longhands) {
|
|
93
|
+
if (style[l.name] !== undefined) mutateFn(t, l.name, undefined);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
95
96
|
}}
|
|
96
97
|
></span>`
|
|
97
98
|
: nothing}
|
|
@@ -109,12 +110,12 @@ function renderShorthandRow(shortProp, entry, style, commitFn, _deleteFn, inheri
|
|
|
109
110
|
? longhands.map((/** @type {any} */ l) => inherited[l.name] || "0").join(" ")
|
|
110
111
|
: ""}
|
|
111
112
|
@input=${debouncedStyleCommit(`short:${shortProp}`, 400, (/** @type {any} */ e) => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
transactDoc(activeTab.value, (t) => {
|
|
114
|
+
for (const l of longhands) {
|
|
115
|
+
if (style[l.name] !== undefined) mutateFn(t, l.name, undefined);
|
|
116
|
+
}
|
|
117
|
+
mutateFn(t, shortProp, e.target.value || undefined);
|
|
118
|
+
});
|
|
118
119
|
})}
|
|
119
120
|
></sp-textfield>
|
|
120
121
|
<sp-action-button
|
|
@@ -122,10 +123,10 @@ function renderShorthandRow(shortProp, entry, style, commitFn, _deleteFn, inheri
|
|
|
122
123
|
quiet
|
|
123
124
|
@click=${(/** @type {any} */ e) => {
|
|
124
125
|
e.stopPropagation();
|
|
125
|
-
|
|
126
|
-
...
|
|
126
|
+
activeTab.value.session.ui.styleShorthands = {
|
|
127
|
+
...activeTab.value.session.ui.styleShorthands,
|
|
127
128
|
[shortProp]: !isExpanded,
|
|
128
|
-
}
|
|
129
|
+
};
|
|
129
130
|
}}
|
|
130
131
|
>
|
|
131
132
|
${isExpanded
|
|
@@ -162,12 +163,12 @@ function renderShorthandRow(shortProp, entry, style, commitFn, _deleteFn, inheri
|
|
|
162
163
|
? emptyVal
|
|
163
164
|
: (style[l.name] ?? (expanded ? expanded[i] : emptyVal)),
|
|
164
165
|
);
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
transactDoc(activeTab.value, (t) => {
|
|
167
|
+
for (const l of longhands) {
|
|
168
|
+
if (style[l.name] !== undefined) mutateFn(t, l.name, undefined);
|
|
169
|
+
}
|
|
170
|
+
mutateFn(t, shortProp, compress(vals));
|
|
171
|
+
});
|
|
171
172
|
}}
|
|
172
173
|
></span>`
|
|
173
174
|
: nothing}
|
|
@@ -186,12 +187,12 @@ function renderShorthandRow(shortProp, entry, style, commitFn, _deleteFn, inheri
|
|
|
186
187
|
? newVal || emptyVal
|
|
187
188
|
: (style[l.name] ?? (expanded ? expanded[i] : emptyVal)),
|
|
188
189
|
);
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
190
|
+
transactDoc(activeTab.value, (t) => {
|
|
191
|
+
for (const l of longhands) {
|
|
192
|
+
if (style[l.name] !== undefined) mutateFn(t, l.name, undefined);
|
|
193
|
+
}
|
|
194
|
+
mutateFn(t, shortProp, compress(vals));
|
|
195
|
+
});
|
|
195
196
|
},
|
|
196
197
|
{ placeholder: !lVal && inherited[name] ? String(inherited[name]) : "" },
|
|
197
198
|
)}
|
|
@@ -212,11 +213,12 @@ function renderShorthandRow(shortProp, entry, style, commitFn, _deleteFn, inheri
|
|
|
212
213
|
* @param {any} activeSelector
|
|
213
214
|
*/
|
|
214
215
|
function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
215
|
-
const
|
|
216
|
+
const tab = activeTab.value;
|
|
217
|
+
const sel = /** @type {import("../state.js").JxPath} */ (tab.session.selection);
|
|
216
218
|
const style = node.style || {};
|
|
217
|
-
const { sizeBreakpoints } = parseMediaEntries(getEffectiveMedia(
|
|
219
|
+
const { sizeBreakpoints } = parseMediaEntries(getEffectiveMedia(tab.doc.document.$media));
|
|
218
220
|
const mediaNames = sizeBreakpoints.map((bp) => bp.name);
|
|
219
|
-
const
|
|
221
|
+
const mediaTab = activeMediaTab;
|
|
220
222
|
|
|
221
223
|
// ── Media tabs template ──────────────────────────────────────────────────
|
|
222
224
|
const mediaTabsT =
|
|
@@ -224,12 +226,12 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
224
226
|
? html`
|
|
225
227
|
<sp-tabs
|
|
226
228
|
size="s"
|
|
227
|
-
selected=${
|
|
229
|
+
selected=${mediaTab || "base"}
|
|
228
230
|
@change=${(/** @type {any} */ e) => {
|
|
229
231
|
const val = e.target.selected;
|
|
230
232
|
const newMedia = val === "base" ? null : val;
|
|
231
|
-
if (newMedia !==
|
|
232
|
-
|
|
233
|
+
if (newMedia !== tab.session.ui.activeMedia) {
|
|
234
|
+
tab.session.ui.activeMedia = newMedia;
|
|
233
235
|
}
|
|
234
236
|
}}
|
|
235
237
|
>
|
|
@@ -242,7 +244,7 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
242
244
|
: nothing;
|
|
243
245
|
|
|
244
246
|
// ── Selector dropdown ──────────────────────────────────────────────────────
|
|
245
|
-
const contextStyle =
|
|
247
|
+
const contextStyle = mediaTab ? style[`@${mediaTab}`] || {} : style;
|
|
246
248
|
const existingSelectors = Object.keys(contextStyle).filter(isNestedSelector);
|
|
247
249
|
const existingSet = new Set(existingSelectors);
|
|
248
250
|
const commonSet = new Set(COMMON_SELECTORS);
|
|
@@ -281,7 +283,7 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
281
283
|
inp.remove();
|
|
282
284
|
picker.style.display = "";
|
|
283
285
|
if (accept && v && isNestedSelector(v)) {
|
|
284
|
-
|
|
286
|
+
activeTab.value.session.ui.activeSelector = v;
|
|
285
287
|
}
|
|
286
288
|
};
|
|
287
289
|
inp.addEventListener("keydown", (ev) => {
|
|
@@ -292,7 +294,7 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
292
294
|
return;
|
|
293
295
|
}
|
|
294
296
|
const newSelector = val === "__base__" ? null : val;
|
|
295
|
-
|
|
297
|
+
activeTab.value.session.ui.activeSelector = newSelector;
|
|
296
298
|
}}
|
|
297
299
|
>
|
|
298
300
|
<sp-menu-item value="__base__">(base)</sp-menu-item>
|
|
@@ -328,14 +330,19 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
328
330
|
size="s"
|
|
329
331
|
class="style-filter-input"
|
|
330
332
|
placeholder="Filter properties…"
|
|
331
|
-
.value=${live(
|
|
332
|
-
@input=${(/** @type {any} */ e) =>
|
|
333
|
+
.value=${live(tab.session.ui.styleFilter || "")}
|
|
334
|
+
@input=${(/** @type {any} */ e) => {
|
|
335
|
+
activeTab.value.session.ui.styleFilter = e.target.value;
|
|
336
|
+
}}
|
|
333
337
|
></sp-textfield>
|
|
334
338
|
<sp-action-button
|
|
335
339
|
size="xs"
|
|
336
340
|
class="style-filter-toggle"
|
|
337
|
-
?selected=${
|
|
338
|
-
@click=${() =>
|
|
341
|
+
?selected=${tab.session.ui.styleFilterActive}
|
|
342
|
+
@click=${() => {
|
|
343
|
+
activeTab.value.session.ui.styleFilterActive =
|
|
344
|
+
!activeTab.value.session.ui.styleFilterActive;
|
|
345
|
+
}}
|
|
339
346
|
>
|
|
340
347
|
Active
|
|
341
348
|
</sp-action-button>
|
|
@@ -345,40 +352,50 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
345
352
|
// ── Determine the active style object ──────────────────────────────────────
|
|
346
353
|
/** @type {Record<string, any>} */
|
|
347
354
|
let activeStyle;
|
|
348
|
-
/** @type {any} */
|
|
355
|
+
/** @type {(prop: any, val: any) => void} */
|
|
349
356
|
let commitStyle;
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
357
|
+
/** @type {(t: any, prop: any, val: any) => void} */
|
|
358
|
+
let commitMutate;
|
|
359
|
+
if (activeSelector && mediaTab && mediaNames.length > 0) {
|
|
360
|
+
activeStyle = (style[`@${mediaTab}`] || {})[activeSelector] || {};
|
|
361
|
+
commitMutate = (/** @type {any} */ t, /** @type {any} */ prop, /** @type {any} */ val) =>
|
|
362
|
+
mutateUpdateMediaNestedStyle(t, sel, mediaTab, activeSelector, prop, val);
|
|
363
|
+
commitStyle = (/** @type {any} */ prop, /** @type {any} */ val) =>
|
|
364
|
+
transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
|
|
354
365
|
} else if (activeSelector) {
|
|
355
366
|
activeStyle = style[activeSelector] || {};
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
367
|
+
commitMutate = (/** @type {any} */ t, /** @type {any} */ prop, /** @type {any} */ val) =>
|
|
368
|
+
mutateUpdateNestedStyle(t, sel, activeSelector, prop, val);
|
|
369
|
+
commitStyle = (/** @type {any} */ prop, /** @type {any} */ val) =>
|
|
370
|
+
transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
|
|
371
|
+
} else if (mediaTab !== null && mediaNames.length > 0) {
|
|
359
372
|
activeStyle = {};
|
|
360
|
-
for (const [p, v] of Object.entries(style[`@${
|
|
373
|
+
for (const [p, v] of Object.entries(style[`@${mediaTab}`] || {})) {
|
|
361
374
|
if (typeof v !== "object") activeStyle[p] = v;
|
|
362
375
|
}
|
|
363
|
-
|
|
364
|
-
|
|
376
|
+
commitMutate = (/** @type {any} */ t, /** @type {any} */ prop, /** @type {any} */ val) =>
|
|
377
|
+
mutateUpdateMediaStyle(t, sel, mediaTab, prop, val);
|
|
378
|
+
commitStyle = (/** @type {any} */ prop, /** @type {any} */ val) =>
|
|
379
|
+
transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
|
|
365
380
|
} else {
|
|
366
381
|
activeStyle = {};
|
|
367
382
|
for (const [p, v] of Object.entries(style)) {
|
|
368
383
|
if (typeof v !== "object") activeStyle[p] = v;
|
|
369
384
|
}
|
|
370
|
-
|
|
371
|
-
|
|
385
|
+
commitMutate = (/** @type {any} */ t, /** @type {any} */ prop, /** @type {any} */ val) =>
|
|
386
|
+
mutateUpdateStyle(t, sel, prop, val);
|
|
387
|
+
commitStyle = (/** @type {any} */ prop, /** @type {any} */ val) =>
|
|
388
|
+
transactDoc(activeTab.value, (t) => commitMutate(t, prop, val));
|
|
372
389
|
}
|
|
373
390
|
|
|
374
391
|
// ── Compute inherited style from higher breakpoints ──────────────────────
|
|
375
392
|
/** @type {Record<string, any>} */
|
|
376
|
-
const inheritedStyle = computeInheritedStyle(style, mediaNames,
|
|
393
|
+
const inheritedStyle = computeInheritedStyle(style, mediaNames, mediaTab, activeSelector);
|
|
377
394
|
|
|
378
395
|
// Auto-open sections that have properties
|
|
379
|
-
const newSections = autoOpenSections({ style: activeStyle },
|
|
380
|
-
if (JSON.stringify(newSections) !== JSON.stringify(
|
|
381
|
-
|
|
396
|
+
const newSections = autoOpenSections({ style: activeStyle }, tab.session.ui.styleSections);
|
|
397
|
+
if (JSON.stringify(newSections) !== JSON.stringify(tab.session.ui.styleSections)) {
|
|
398
|
+
tab.session.ui.styleSections = newSections;
|
|
382
399
|
}
|
|
383
400
|
|
|
384
401
|
// Partition properties into sections
|
|
@@ -402,8 +419,8 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
402
419
|
}
|
|
403
420
|
|
|
404
421
|
// ── Filter state ─────────────────────────────────────────────────────────
|
|
405
|
-
const filterText = (
|
|
406
|
-
const filterActive =
|
|
422
|
+
const filterText = (tab.session.ui.styleFilter || "").toLowerCase();
|
|
423
|
+
const filterActive = tab.session.ui.styleFilterActive;
|
|
407
424
|
const isFiltering = filterText.length > 0 || filterActive;
|
|
408
425
|
|
|
409
426
|
// ── Section templates ────────────────────────────────────────────────────
|
|
@@ -449,7 +466,7 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
449
466
|
hasVal || longhands.some((/** @type {any} */ l) => activeStyle[l.name] !== undefined);
|
|
450
467
|
if (!hasAny && !condMet) continue;
|
|
451
468
|
rows.push(
|
|
452
|
-
renderShorthandRow(prop, entry, activeStyle,
|
|
469
|
+
renderShorthandRow(prop, entry, activeStyle, commitMutate, () => {}, inheritedStyle),
|
|
453
470
|
);
|
|
454
471
|
} else {
|
|
455
472
|
const isWarning = hasVal && !condMet;
|
|
@@ -459,9 +476,8 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
459
476
|
entry,
|
|
460
477
|
prop,
|
|
461
478
|
val ?? "",
|
|
462
|
-
(/** @type {any} */ newVal) =>
|
|
463
|
-
|
|
464
|
-
() => update(commitStyle(getState(), prop, undefined)),
|
|
479
|
+
(/** @type {any} */ newVal) => commitStyle(prop, newVal || undefined),
|
|
480
|
+
() => commitStyle(prop, undefined),
|
|
465
481
|
isWarning,
|
|
466
482
|
sec.$layout === "grid",
|
|
467
483
|
inheritedStyle[prop],
|
|
@@ -472,14 +488,17 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
472
488
|
}
|
|
473
489
|
|
|
474
490
|
if (isFiltering && rows.length === 0) return nothing;
|
|
475
|
-
const isOpen = isFiltering ? true : (
|
|
491
|
+
const isOpen = isFiltering ? true : (tab.session.ui.styleSections[sec.key] ?? false);
|
|
476
492
|
|
|
477
493
|
return html`
|
|
478
494
|
<sp-accordion-item
|
|
479
495
|
label=${sec.label}
|
|
480
496
|
.open=${isOpen}
|
|
481
497
|
@sp-accordion-item-toggle=${(/** @type {any} */ e) => {
|
|
482
|
-
|
|
498
|
+
activeTab.value.session.ui.styleSections = {
|
|
499
|
+
...activeTab.value.session.ui.styleSections,
|
|
500
|
+
[sec.key]: e.target.open,
|
|
501
|
+
};
|
|
483
502
|
}}
|
|
484
503
|
>
|
|
485
504
|
${sectionActiveProps.length > 0
|
|
@@ -492,17 +511,17 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
492
511
|
@click=${(/** @type {any} */ e) => {
|
|
493
512
|
e.stopPropagation();
|
|
494
513
|
e.preventDefault();
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
514
|
+
transactDoc(activeTab.value, (t) => {
|
|
515
|
+
for (const { prop, entry } of sectionActiveProps) {
|
|
516
|
+
if (activeStyle[prop] !== undefined) commitMutate(t, prop, undefined);
|
|
517
|
+
if (inferInputType(entry) === "shorthand") {
|
|
518
|
+
for (const l of getLonghands(prop)) {
|
|
519
|
+
if (activeStyle[l.name] !== undefined)
|
|
520
|
+
commitMutate(t, l.name, undefined);
|
|
521
|
+
}
|
|
502
522
|
}
|
|
503
523
|
}
|
|
504
|
-
}
|
|
505
|
-
update(s);
|
|
524
|
+
});
|
|
506
525
|
}}
|
|
507
526
|
></span>
|
|
508
527
|
</span>
|
|
@@ -515,13 +534,16 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
515
534
|
|
|
516
535
|
// ── Custom section ─────────────────────────────────────────────────────────
|
|
517
536
|
const cssInitialMap = getCssInitialMap();
|
|
518
|
-
const customIsOpen =
|
|
537
|
+
const customIsOpen = tab.session.ui.styleSections.other ?? otherProps.length > 0;
|
|
519
538
|
const customSectionT = html`
|
|
520
539
|
<sp-accordion-item
|
|
521
540
|
label="Custom"
|
|
522
541
|
.open=${customIsOpen}
|
|
523
542
|
@sp-accordion-item-toggle=${(/** @type {any} */ e) => {
|
|
524
|
-
|
|
543
|
+
activeTab.value.session.ui.styleSections = {
|
|
544
|
+
...activeTab.value.session.ui.styleSections,
|
|
545
|
+
other: e.target.open,
|
|
546
|
+
};
|
|
525
547
|
}}
|
|
526
548
|
>
|
|
527
549
|
<div>
|
|
@@ -535,9 +557,10 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
535
557
|
@change=${(/** @type {any} */ e) => {
|
|
536
558
|
const newProp = e.target.value.trim();
|
|
537
559
|
if (newProp && newProp !== prop) {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
560
|
+
transactDoc(activeTab.value, (t) => {
|
|
561
|
+
commitMutate(t, prop, undefined);
|
|
562
|
+
commitMutate(t, newProp, String(activeStyle[prop]));
|
|
563
|
+
});
|
|
541
564
|
}
|
|
542
565
|
}}
|
|
543
566
|
></sp-textfield>
|
|
@@ -547,14 +570,10 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
547
570
|
.value=${live(String(activeStyle[prop]))}
|
|
548
571
|
placeholder=${ifDefined(cssInitialMap.get(prop))}
|
|
549
572
|
@input=${debouncedStyleCommit(`custom:${prop}`, 400, (/** @type {any} */ e) => {
|
|
550
|
-
|
|
573
|
+
commitStyle(prop, e.target.value);
|
|
551
574
|
})}
|
|
552
575
|
></sp-textfield>
|
|
553
|
-
<sp-action-button
|
|
554
|
-
size="xs"
|
|
555
|
-
quiet
|
|
556
|
-
@click=${() => update(commitStyle(getState(), prop, undefined))}
|
|
557
|
-
>
|
|
576
|
+
<sp-action-button size="xs" quiet @click=${() => commitStyle(prop, undefined)}>
|
|
558
577
|
<sp-icon-close slot="icon"></sp-icon-close>
|
|
559
578
|
</sp-action-button>
|
|
560
579
|
</div>
|
|
@@ -571,7 +590,7 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
571
590
|
const prop = e.target.value.trim();
|
|
572
591
|
if (prop) {
|
|
573
592
|
const initial = cssInitialMap.get(prop) || "";
|
|
574
|
-
|
|
593
|
+
commitStyle(prop, initial || "");
|
|
575
594
|
e.target.value = "";
|
|
576
595
|
}
|
|
577
596
|
}
|
|
@@ -599,19 +618,23 @@ function styleSidebarTemplate(node, activeMediaTab, activeSelector) {
|
|
|
599
618
|
* @returns {import("lit-html").TemplateResult}
|
|
600
619
|
*/
|
|
601
620
|
export function renderStylePanelTemplate(ctx) {
|
|
602
|
-
const
|
|
603
|
-
if (
|
|
604
|
-
|
|
621
|
+
const tab = activeTab.value;
|
|
622
|
+
if (!tab) return html`<div class="empty-state">No document loaded</div>`;
|
|
623
|
+
if (ctx.getCanvasMode() === "settings" && tab.session.ui.stylebookSelection) {
|
|
624
|
+
const node = tab.doc.document;
|
|
605
625
|
if (!node) return html`<div class="empty-state">No document loaded</div>`;
|
|
606
626
|
return html`
|
|
607
|
-
<div class="stylebook-style-header">
|
|
608
|
-
|
|
627
|
+
<div class="stylebook-style-header">
|
|
628
|
+
Styling: <${tab.session.ui.stylebookSelection}>
|
|
629
|
+
</div>
|
|
630
|
+
${styleSidebarTemplate(node, tab.session.ui.activeMedia, tab.session.ui.activeSelector)}
|
|
609
631
|
`;
|
|
610
632
|
}
|
|
611
|
-
if (!
|
|
612
|
-
|
|
633
|
+
if (!tab.session.selection)
|
|
634
|
+
return html`<div class="empty-state">Select an element to style</div>`;
|
|
635
|
+
const node = getNodeAtPath(tab.doc.document, tab.session.selection);
|
|
613
636
|
if (!node) return html`<div class="empty-state">Select an element to style</div>`;
|
|
614
|
-
return styleSidebarTemplate(node,
|
|
637
|
+
return styleSidebarTemplate(node, tab.session.ui.activeMedia, tab.session.ui.activeSelector);
|
|
615
638
|
}
|
|
616
639
|
|
|
617
640
|
/** Single property input row (generic field row helper) */
|
|
@@ -9,8 +9,6 @@ import { styleMap } from "lit-html/directives/style-map.js";
|
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
11
|
getState,
|
|
12
|
-
update,
|
|
13
|
-
updateStyle,
|
|
14
12
|
updateSession,
|
|
15
13
|
updateUi,
|
|
16
14
|
canvasWrap,
|
|
@@ -18,8 +16,10 @@ import {
|
|
|
18
16
|
elToPath,
|
|
19
17
|
projectState,
|
|
20
18
|
} from "../store.js";
|
|
19
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
20
|
+
import { transactDoc, mutateUpdateStyle } from "../tabs/transact.js";
|
|
21
21
|
import { view } from "../view.js";
|
|
22
|
-
import { defineElement } from "@jxsuite/runtime";
|
|
22
|
+
import { defineElement, setSkipServerFunctions } from "@jxsuite/runtime";
|
|
23
23
|
import { componentRegistry } from "../files/components.js";
|
|
24
24
|
import { getEffectiveStyle, getEffectiveMedia } from "../site-context.js";
|
|
25
25
|
import { parseMediaEntries, activeBreakpointsForWidth } from "../utils/canvas-media.js";
|
|
@@ -272,7 +272,7 @@ export function renderStylebookOverlays() {
|
|
|
272
272
|
const selectedTag = S.ui.stylebookSelection;
|
|
273
273
|
|
|
274
274
|
for (const panel of canvasPanels) {
|
|
275
|
-
const hoverTag = panel._lastHoverTag;
|
|
275
|
+
const hoverTag = /** @type {any} */ (panel)._lastHoverTag;
|
|
276
276
|
/**
|
|
277
277
|
* @type {{
|
|
278
278
|
* cls: string;
|
|
@@ -351,6 +351,7 @@ export function buildStylebookElement(entry, rootStyle, activeBreakpoints, paren
|
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
353
|
if (activeBreakpoints) {
|
|
354
|
+
// Check media overrides nested inside the tag style (selector wraps media)
|
|
354
355
|
for (const [key, val] of Object.entries(tagStyle)) {
|
|
355
356
|
if (!key.startsWith("@") || typeof val !== "object") continue;
|
|
356
357
|
const mediaName = key.slice(1);
|
|
@@ -367,6 +368,27 @@ export function buildStylebookElement(entry, rootStyle, activeBreakpoints, paren
|
|
|
367
368
|
}
|
|
368
369
|
}
|
|
369
370
|
}
|
|
371
|
+
// Check top-level @media keys for tag-specific overrides (media wraps selector)
|
|
372
|
+
if (activeBreakpoints) {
|
|
373
|
+
const selector = compoundSelector || `& ${entry.tag}`;
|
|
374
|
+
for (const [key, val] of Object.entries(rootStyle)) {
|
|
375
|
+
if (!key.startsWith("@") || typeof val !== "object") continue;
|
|
376
|
+
const mediaName = key.slice(1);
|
|
377
|
+
if (mediaName === "--") continue;
|
|
378
|
+
if (activeBreakpoints.has(mediaName)) {
|
|
379
|
+
const mediaTagStyle = /** @type {any} */ (val)[selector];
|
|
380
|
+
if (mediaTagStyle && typeof mediaTagStyle === "object") {
|
|
381
|
+
for (const [prop, v] of Object.entries(mediaTagStyle)) {
|
|
382
|
+
if (typeof v === "string" || typeof v === "number") {
|
|
383
|
+
try {
|
|
384
|
+
/** @type {any} */ (el.style)[prop] = v;
|
|
385
|
+
} catch {}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
370
392
|
if (entry.children) {
|
|
371
393
|
for (const child of entry.children) {
|
|
372
394
|
el.appendChild(buildStylebookElement(child, rootStyle, activeBreakpoints, entry.tag));
|
|
@@ -382,6 +404,7 @@ export function buildStylebookElement(entry, rootStyle, activeBreakpoints, paren
|
|
|
382
404
|
* @returns {Promise<HTMLElement>}
|
|
383
405
|
*/
|
|
384
406
|
export async function renderComponentPreview(comp) {
|
|
407
|
+
setSkipServerFunctions(true);
|
|
385
408
|
try {
|
|
386
409
|
if (comp.source === "npm") {
|
|
387
410
|
if (!customElements.get(comp.tagName)) {
|
|
@@ -416,7 +439,13 @@ export async function renderComponentPreview(comp) {
|
|
|
416
439
|
*/
|
|
417
440
|
function hasTagStyle(rootStyle, tag) {
|
|
418
441
|
const s = rootStyle[`& ${tag}`];
|
|
419
|
-
|
|
442
|
+
if (s && typeof s === "object" && Object.keys(s).length > 0) return true;
|
|
443
|
+
const selector = `& ${tag}`;
|
|
444
|
+
for (const [key, val] of Object.entries(rootStyle)) {
|
|
445
|
+
if (!key.startsWith("@") || typeof val !== "object") continue;
|
|
446
|
+
if (/** @type {any} */ (val)[selector]) return true;
|
|
447
|
+
}
|
|
448
|
+
return false;
|
|
420
449
|
}
|
|
421
450
|
|
|
422
451
|
/**
|
|
@@ -662,8 +691,9 @@ function renderVarRow(catKey, catMeta, varName, varVal, isNew) {
|
|
|
662
691
|
const swatch = /** @type {any} */ (row.querySelector(".sb-var-swatch"));
|
|
663
692
|
if (swatch) swatch.style.backgroundColor = colorPicker.value;
|
|
664
693
|
if (!isNew && varName) {
|
|
665
|
-
|
|
666
|
-
|
|
694
|
+
transactDoc(activeTab.value, (t) =>
|
|
695
|
+
mutateUpdateStyle(t, [], varName, colorPicker.value),
|
|
696
|
+
);
|
|
667
697
|
}
|
|
668
698
|
}}
|
|
669
699
|
/>
|
|
@@ -722,8 +752,7 @@ function renderVarRow(catKey, catMeta, varName, varVal, isNew) {
|
|
|
722
752
|
const swatch = /** @type {any} */ (row.querySelector(".sb-var-swatch"));
|
|
723
753
|
if (swatch) swatch.style.backgroundColor = v;
|
|
724
754
|
if (!isNew && varName) {
|
|
725
|
-
|
|
726
|
-
update(updateStyle(S, [], varName, v));
|
|
755
|
+
transactDoc(activeTab.value, (t) => mutateUpdateStyle(t, [], varName, v));
|
|
727
756
|
}
|
|
728
757
|
}, 400);
|
|
729
758
|
}}
|
|
@@ -736,8 +765,7 @@ function renderVarRow(catKey, catMeta, varName, varVal, isNew) {
|
|
|
736
765
|
const bar = /** @type {any} */ (row.querySelector(".sb-var-size-bar"));
|
|
737
766
|
if (bar) bar.style.width = newVal;
|
|
738
767
|
if (!isNew && varName) {
|
|
739
|
-
|
|
740
|
-
update(updateStyle(S, [], varName, newVal));
|
|
768
|
+
transactDoc(activeTab.value, (t) => mutateUpdateStyle(t, [], varName, newVal));
|
|
741
769
|
}
|
|
742
770
|
},
|
|
743
771
|
});
|
|
@@ -769,8 +797,7 @@ function renderVarRow(catKey, catMeta, varName, varVal, isNew) {
|
|
|
769
797
|
const v = textFieldEl.value;
|
|
770
798
|
const fontPrev = /** @type {any} */ (row.querySelector(".sb-var-font-preview"));
|
|
771
799
|
if (fontPrev) fontPrev.style.fontFamily = v;
|
|
772
|
-
|
|
773
|
-
update(updateStyle(S, [], varName, v));
|
|
800
|
+
transactDoc(activeTab.value, (t) => mutateUpdateStyle(t, [], varName, v));
|
|
774
801
|
}, 400);
|
|
775
802
|
}}
|
|
776
803
|
></sp-textfield>
|
|
@@ -795,8 +822,7 @@ function renderVarRow(catKey, catMeta, varName, varVal, isNew) {
|
|
|
795
822
|
const val = getValueFn();
|
|
796
823
|
const generatedVar = friendlyNameToVar(name, catMeta.prefix);
|
|
797
824
|
if (!generatedVar || !val) return;
|
|
798
|
-
|
|
799
|
-
update(updateStyle(S, [], generatedVar, val));
|
|
825
|
+
transactDoc(activeTab.value, (t) => mutateUpdateStyle(t, [], generatedVar, val));
|
|
800
826
|
}}
|
|
801
827
|
>Add</sp-action-button
|
|
802
828
|
>
|
|
@@ -829,8 +855,7 @@ function renderVarRow(catKey, catMeta, varName, varVal, isNew) {
|
|
|
829
855
|
class="sb-var-del"
|
|
830
856
|
style="pointer-events:auto"
|
|
831
857
|
@click=${() => {
|
|
832
|
-
|
|
833
|
-
update(updateStyle(S, [], varName, undefined));
|
|
858
|
+
transactDoc(activeTab.value, (t) => mutateUpdateStyle(t, [], varName, undefined));
|
|
834
859
|
}}
|
|
835
860
|
>
|
|
836
861
|
<sp-icon-delete slot="icon"></sp-icon-delete>
|