@jxsuite/studio 0.28.2 → 0.28.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/studio.js +86144 -83971
- package/dist/studio.js.map +229 -219
- package/package.json +5 -5
- package/src/browse/browse-modal.ts +9 -3
- package/src/browse/browse.ts +139 -82
- package/src/canvas/canvas-diff.ts +35 -18
- package/src/canvas/canvas-helpers.ts +55 -21
- package/src/canvas/canvas-live-render.ts +140 -89
- package/src/canvas/canvas-render.ts +127 -82
- package/src/canvas/canvas-utils.ts +68 -42
- package/src/canvas/nested-site-style.ts +15 -7
- package/src/editor/component-inline-edit.ts +90 -51
- package/src/editor/content-inline-edit.ts +75 -72
- package/src/editor/context-menu.ts +125 -71
- package/src/editor/convert-targets.ts +17 -13
- package/src/editor/convert-to-component.ts +51 -28
- package/src/editor/convert-to-repeater.ts +44 -19
- package/src/editor/inline-edit.ts +107 -52
- package/src/editor/inline-format.ts +128 -60
- package/src/editor/insertion-helper.ts +26 -14
- package/src/editor/shortcuts.ts +89 -43
- package/src/editor/slash-menu.ts +41 -26
- package/src/files/components.ts +9 -7
- package/src/files/file-ops.ts +53 -33
- package/src/files/files.ts +246 -143
- package/src/format/constraints.ts +25 -15
- package/src/format/format-host.ts +33 -10
- package/src/github/github-auth.ts +24 -14
- package/src/github/github-publish.ts +20 -14
- package/src/new-project/new-project-modal.ts +27 -18
- package/src/panels/activity-bar.ts +27 -26
- package/src/panels/ai-panel.ts +100 -44
- package/src/panels/block-action-bar.ts +84 -52
- package/src/panels/canvas-dnd.ts +47 -28
- package/src/panels/data-explorer.ts +24 -11
- package/src/panels/dnd.ts +148 -100
- package/src/panels/editors.ts +54 -37
- package/src/panels/elements-panel.ts +23 -13
- package/src/panels/events-panel.ts +48 -38
- package/src/panels/git-panel.ts +95 -60
- package/src/panels/head-panel.ts +138 -84
- package/src/panels/imports-panel.ts +66 -29
- package/src/panels/layers-panel.ts +80 -36
- package/src/panels/left-panel.ts +86 -53
- package/src/panels/overlays.ts +36 -19
- package/src/panels/panel-events.ts +48 -27
- package/src/panels/panel-scheduler.ts +41 -23
- package/src/panels/preview-render.ts +26 -26
- package/src/panels/properties-panel.ts +155 -118
- package/src/panels/pseudo-preview.ts +31 -15
- package/src/panels/quick-search.ts +28 -13
- package/src/panels/right-panel.ts +35 -24
- package/src/panels/shared.ts +46 -29
- package/src/panels/signals-panel.ts +303 -186
- package/src/panels/statusbar.ts +27 -12
- package/src/panels/style-inputs.ts +30 -24
- package/src/panels/style-panel.ts +130 -90
- package/src/panels/style-utils.ts +63 -23
- package/src/panels/stylebook-layers-panel.ts +27 -30
- package/src/panels/stylebook-panel.ts +140 -72
- package/src/panels/tab-strip.ts +21 -10
- package/src/panels/toolbar.ts +52 -40
- package/src/panels/welcome-screen.ts +1 -1
- package/src/platform.ts +2 -1
- package/src/platforms/devserver.ts +221 -105
- package/src/recent-projects.ts +15 -7
- package/src/resize-edges.ts +7 -5
- package/src/services/cem-export.ts +73 -57
- package/src/services/code-services.ts +52 -25
- package/src/services/monaco-setup.ts +5 -5
- package/src/settings/content-types-editor.ts +133 -62
- package/src/settings/css-vars-editor.ts +31 -14
- package/src/settings/defs-editor.ts +125 -63
- package/src/settings/general-settings.ts +11 -6
- package/src/settings/head-editor.ts +30 -15
- package/src/settings/schema-field-ui.ts +75 -35
- package/src/settings/settings-modal.ts +35 -17
- package/src/site-context.ts +80 -47
- package/src/state.ts +98 -65
- package/src/store.ts +42 -23
- package/src/studio.ts +165 -144
- package/src/tabs/tab.ts +51 -45
- package/src/tabs/transact.ts +230 -109
- package/src/types.ts +60 -51
- package/src/ui/button-group.ts +3 -2
- package/src/ui/color-selector.ts +33 -15
- package/src/ui/expression-editor.ts +139 -86
- package/src/ui/field-input.ts +35 -18
- package/src/ui/field-row.ts +1 -1
- package/src/ui/icons.ts +5 -6
- package/src/ui/layers.ts +33 -26
- package/src/ui/media-picker.ts +13 -7
- package/src/ui/panel-resize.ts +39 -20
- package/src/ui/spectrum.ts +4 -2
- package/src/ui/unit-selector.ts +20 -11
- package/src/ui/value-selector.ts +13 -7
- package/src/ui/widgets.ts +34 -18
- package/src/utils/canvas-media.ts +66 -33
- package/src/utils/edit-display.ts +43 -44
- package/src/utils/google-fonts.ts +10 -6
- package/src/utils/inherited-style.ts +22 -8
- package/src/utils/studio-utils.ts +88 -45
- package/src/view.ts +9 -5
- package/src/workspace/workspace.ts +49 -29
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
import { html, nothing } from "lit-html";
|
|
3
3
|
import { live } from "lit-html/directives/live.js";
|
|
4
|
+
import { isJsonObject, isRef } from "@jxsuite/schema/guards";
|
|
4
5
|
import { renderFieldRow } from "./field-row";
|
|
5
6
|
|
|
7
|
+
import type { JxExpressionNode, JxExpressionOperand } from "@jxsuite/schema/types";
|
|
8
|
+
import type { TemplateResult } from "lit-html";
|
|
9
|
+
|
|
6
10
|
// ─── Operator Categories ────────────────────────────────────────────────────
|
|
7
11
|
|
|
8
12
|
const UNARY_OPS = new Set(["!", "-"]);
|
|
@@ -49,75 +53,83 @@ const OPERATOR_GROUPS = [
|
|
|
49
53
|
* }}
|
|
50
54
|
*/
|
|
51
55
|
function operatorInfo(op: string) {
|
|
52
|
-
if (UNARY_OPS.has(op))
|
|
56
|
+
if (UNARY_OPS.has(op)) {
|
|
53
57
|
return {
|
|
54
|
-
needsValue: false,
|
|
55
58
|
needsInitial: false,
|
|
56
|
-
|
|
59
|
+
needsValue: false,
|
|
57
60
|
spliceArray: false,
|
|
61
|
+
targetMustBeRef: false,
|
|
58
62
|
valueIsNode: false,
|
|
59
63
|
};
|
|
60
|
-
|
|
64
|
+
}
|
|
65
|
+
if (BINARY_OPS.has(op)) {
|
|
61
66
|
return {
|
|
62
|
-
needsValue: true,
|
|
63
67
|
needsInitial: false,
|
|
64
|
-
|
|
68
|
+
needsValue: true,
|
|
65
69
|
spliceArray: false,
|
|
70
|
+
targetMustBeRef: false,
|
|
66
71
|
valueIsNode: false,
|
|
67
72
|
};
|
|
68
|
-
|
|
73
|
+
}
|
|
74
|
+
if (ASSIGN_OPS.has(op)) {
|
|
69
75
|
return {
|
|
70
|
-
needsValue: true,
|
|
71
76
|
needsInitial: false,
|
|
72
|
-
|
|
77
|
+
needsValue: true,
|
|
73
78
|
spliceArray: false,
|
|
79
|
+
targetMustBeRef: true,
|
|
74
80
|
valueIsNode: false,
|
|
75
81
|
};
|
|
76
|
-
|
|
82
|
+
}
|
|
83
|
+
if (NO_ARG_OPS.has(op)) {
|
|
77
84
|
return {
|
|
78
|
-
needsValue: false,
|
|
79
85
|
needsInitial: false,
|
|
80
|
-
|
|
86
|
+
needsValue: false,
|
|
81
87
|
spliceArray: false,
|
|
88
|
+
targetMustBeRef: true,
|
|
82
89
|
valueIsNode: false,
|
|
83
90
|
};
|
|
84
|
-
|
|
91
|
+
}
|
|
92
|
+
if (ONE_ARG_OPS.has(op)) {
|
|
85
93
|
return {
|
|
86
|
-
needsValue: true,
|
|
87
94
|
needsInitial: false,
|
|
88
|
-
|
|
95
|
+
needsValue: true,
|
|
89
96
|
spliceArray: false,
|
|
97
|
+
targetMustBeRef: true,
|
|
90
98
|
valueIsNode: false,
|
|
91
99
|
};
|
|
92
|
-
|
|
100
|
+
}
|
|
101
|
+
if (op === "splice") {
|
|
93
102
|
return {
|
|
94
|
-
needsValue: true,
|
|
95
103
|
needsInitial: false,
|
|
96
|
-
|
|
104
|
+
needsValue: true,
|
|
97
105
|
spliceArray: true,
|
|
106
|
+
targetMustBeRef: true,
|
|
98
107
|
valueIsNode: false,
|
|
99
108
|
};
|
|
100
|
-
|
|
109
|
+
}
|
|
110
|
+
if (op === "reduce") {
|
|
101
111
|
return {
|
|
102
|
-
needsValue: true,
|
|
103
112
|
needsInitial: true,
|
|
104
|
-
|
|
113
|
+
needsValue: true,
|
|
105
114
|
spliceArray: false,
|
|
115
|
+
targetMustBeRef: true,
|
|
106
116
|
valueIsNode: true,
|
|
107
117
|
};
|
|
108
|
-
|
|
118
|
+
}
|
|
119
|
+
if (op === "map" || op === "filter") {
|
|
109
120
|
return {
|
|
110
|
-
needsValue: true,
|
|
111
121
|
needsInitial: false,
|
|
112
|
-
|
|
122
|
+
needsValue: true,
|
|
113
123
|
spliceArray: false,
|
|
124
|
+
targetMustBeRef: true,
|
|
114
125
|
valueIsNode: true,
|
|
115
126
|
};
|
|
127
|
+
}
|
|
116
128
|
return {
|
|
117
|
-
needsValue: false,
|
|
118
129
|
needsInitial: false,
|
|
119
|
-
|
|
130
|
+
needsValue: false,
|
|
120
131
|
spliceArray: false,
|
|
132
|
+
targetMustBeRef: false,
|
|
121
133
|
valueIsNode: false,
|
|
122
134
|
};
|
|
123
135
|
}
|
|
@@ -125,71 +137,105 @@ function operatorInfo(op: string) {
|
|
|
125
137
|
// ─── Operand Mode Detection ─────────────────────────────────────────────────
|
|
126
138
|
|
|
127
139
|
/**
|
|
128
|
-
* @param {
|
|
140
|
+
* @param {unknown} operand
|
|
129
141
|
* @returns {"ref" | "expression" | "literal"}
|
|
130
142
|
*/
|
|
131
|
-
function operandMode(operand:
|
|
143
|
+
function operandMode(operand: unknown) {
|
|
132
144
|
if (operand && typeof operand === "object") {
|
|
133
|
-
if ("$ref" in operand)
|
|
134
|
-
|
|
145
|
+
if ("$ref" in operand) {
|
|
146
|
+
return "ref";
|
|
147
|
+
}
|
|
148
|
+
if ("operator" in operand) {
|
|
149
|
+
return "expression";
|
|
150
|
+
}
|
|
135
151
|
}
|
|
136
152
|
return "literal";
|
|
137
153
|
}
|
|
138
154
|
|
|
139
155
|
/**
|
|
140
156
|
* @param {string} mode
|
|
141
|
-
* @returns {
|
|
157
|
+
* @returns {JxExpressionOperand}
|
|
142
158
|
*/
|
|
143
|
-
function defaultForMode(mode: string) {
|
|
144
|
-
if (mode === "ref")
|
|
145
|
-
|
|
159
|
+
function defaultForMode(mode: string): JxExpressionOperand {
|
|
160
|
+
if (mode === "ref") {
|
|
161
|
+
return { $ref: "" };
|
|
162
|
+
}
|
|
163
|
+
if (mode === "expression") {
|
|
164
|
+
return { operator: "!", target: null };
|
|
165
|
+
}
|
|
146
166
|
return null;
|
|
147
167
|
}
|
|
148
168
|
|
|
149
169
|
// ─── Literal Type Detection ─────────────────────────────────────────────────
|
|
150
170
|
|
|
151
171
|
/**
|
|
152
|
-
* @param {
|
|
172
|
+
* @param {unknown} val
|
|
153
173
|
* @returns {"string" | "number" | "boolean" | "null"}
|
|
154
174
|
*/
|
|
155
|
-
function literalType(val:
|
|
156
|
-
if (val === null || val === undefined)
|
|
157
|
-
|
|
158
|
-
|
|
175
|
+
function literalType(val: unknown) {
|
|
176
|
+
if (val === null || val === undefined) {
|
|
177
|
+
return "null";
|
|
178
|
+
}
|
|
179
|
+
if (typeof val === "boolean") {
|
|
180
|
+
return "boolean";
|
|
181
|
+
}
|
|
182
|
+
if (typeof val === "number") {
|
|
183
|
+
return "number";
|
|
184
|
+
}
|
|
159
185
|
return "string";
|
|
160
186
|
}
|
|
161
187
|
|
|
162
188
|
/**
|
|
163
189
|
* @param {string} type
|
|
164
|
-
* @returns {
|
|
190
|
+
* @returns {JxExpressionOperand}
|
|
165
191
|
*/
|
|
166
|
-
function defaultForLiteralType(type: string) {
|
|
167
|
-
if (type === "number")
|
|
168
|
-
|
|
169
|
-
|
|
192
|
+
function defaultForLiteralType(type: string): JxExpressionOperand {
|
|
193
|
+
if (type === "number") {
|
|
194
|
+
return 0;
|
|
195
|
+
}
|
|
196
|
+
if (type === "boolean") {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
if (type === "null") {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
170
202
|
return "";
|
|
171
203
|
}
|
|
172
204
|
|
|
173
205
|
// ─── Hint (one-line summary for signal rows) ────────────────────────────────
|
|
174
206
|
|
|
175
207
|
/**
|
|
176
|
-
* @param {
|
|
208
|
+
* @param {unknown} node
|
|
177
209
|
* @returns {string}
|
|
178
210
|
*/
|
|
179
|
-
export function expressionHint(node:
|
|
180
|
-
if (!node ||
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
211
|
+
export function expressionHint(node: unknown) {
|
|
212
|
+
if (!isJsonObject(node) || typeof node.operator !== "string") {
|
|
213
|
+
return "$expression";
|
|
214
|
+
}
|
|
215
|
+
const expr = node as unknown as JxExpressionNode;
|
|
216
|
+
const op = expr.operator;
|
|
217
|
+
const { target } = expr;
|
|
218
|
+
const targetLabel = isRef(target)
|
|
219
|
+
? target.$ref.replace("#/state/", "")
|
|
220
|
+
: isJsonObject(target) && typeof target.operator === "string"
|
|
221
|
+
? `(${target.operator}…)`
|
|
222
|
+
: String(target ?? "?");
|
|
187
223
|
|
|
188
|
-
if (ASSIGN_OPS.has(op) || ONE_ARG_OPS.has(op))
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (op
|
|
192
|
-
|
|
224
|
+
if (ASSIGN_OPS.has(op) || ONE_ARG_OPS.has(op)) {
|
|
225
|
+
return `${op} ${targetLabel}`;
|
|
226
|
+
}
|
|
227
|
+
if (NO_ARG_OPS.has(op)) {
|
|
228
|
+
return `${op}(${targetLabel})`;
|
|
229
|
+
}
|
|
230
|
+
if (op === "splice") {
|
|
231
|
+
return `splice(${targetLabel})`;
|
|
232
|
+
}
|
|
233
|
+
if (op === "reduce" || op === "map" || op === "filter") {
|
|
234
|
+
return `${op}(${targetLabel})`;
|
|
235
|
+
}
|
|
236
|
+
if (UNARY_OPS.has(op)) {
|
|
237
|
+
return `${op}${targetLabel}`;
|
|
238
|
+
}
|
|
193
239
|
return `${targetLabel} ${op} …`;
|
|
194
240
|
}
|
|
195
241
|
|
|
@@ -219,7 +265,9 @@ function renderRefPicker(
|
|
|
219
265
|
.value=${live(isCustom ? "__custom__" : refVal || "")}
|
|
220
266
|
@change=${(e: Event) => {
|
|
221
267
|
const val = (e.target as HTMLInputElement).value;
|
|
222
|
-
if (val === "__custom__")
|
|
268
|
+
if (val === "__custom__") {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
223
271
|
onRefChange(val);
|
|
224
272
|
}}
|
|
225
273
|
>
|
|
@@ -241,11 +289,11 @@ function renderRefPicker(
|
|
|
241
289
|
// ─── Literal Editor ─────────────────────────────────────────────────────────
|
|
242
290
|
|
|
243
291
|
/**
|
|
244
|
-
* @param {
|
|
245
|
-
* @param {(newVal:
|
|
292
|
+
* @param {unknown} operand
|
|
293
|
+
* @param {(newVal: JxExpressionOperand) => void} onChange
|
|
246
294
|
* @returns {import("lit-html").TemplateResult}
|
|
247
295
|
*/
|
|
248
|
-
function renderLiteralEditor(operand:
|
|
296
|
+
function renderLiteralEditor(operand: unknown, onChange: (newVal: JxExpressionOperand) => void) {
|
|
249
297
|
const type = literalType(operand);
|
|
250
298
|
return html`
|
|
251
299
|
<div style="display:flex;gap:4px;align-items:center;flex:1">
|
|
@@ -280,7 +328,7 @@ function renderLiteralEditor(operand: any, onChange: (newVal: any) => void) {
|
|
|
280
328
|
: type === "boolean"
|
|
281
329
|
? html`<sp-checkbox
|
|
282
330
|
size="s"
|
|
283
|
-
?checked=${
|
|
331
|
+
?checked=${Boolean(operand)}
|
|
284
332
|
@change=${(e: Event) => onChange((e.target as HTMLInputElement).checked)}
|
|
285
333
|
>true</sp-checkbox
|
|
286
334
|
>`
|
|
@@ -294,8 +342,8 @@ function renderLiteralEditor(operand: any, onChange: (newVal: any) => void) {
|
|
|
294
342
|
// ─── Operand Editor ─────────────────────────────────────────────────────────
|
|
295
343
|
|
|
296
344
|
/**
|
|
297
|
-
* @param {
|
|
298
|
-
* @param {(newOperand:
|
|
345
|
+
* @param {unknown} operand
|
|
346
|
+
* @param {(newOperand: unknown) => void} onChange
|
|
299
347
|
* @param {{
|
|
300
348
|
* stateDefs: string[];
|
|
301
349
|
* allowEventRef: boolean;
|
|
@@ -313,7 +361,7 @@ function renderOperandEditor(
|
|
|
313
361
|
depth: number;
|
|
314
362
|
mustBeRef?: boolean;
|
|
315
363
|
},
|
|
316
|
-
):
|
|
364
|
+
): TemplateResult {
|
|
317
365
|
if (opts.mustBeRef) {
|
|
318
366
|
const refVal = ((operand as Record<string, unknown> | null)?.$ref as string) ?? "";
|
|
319
367
|
return html`
|
|
@@ -356,8 +404,8 @@ function renderOperandEditor(
|
|
|
356
404
|
// ─── Splice Args Editor ─────────────────────────────────────────────────────
|
|
357
405
|
|
|
358
406
|
/**
|
|
359
|
-
* @param {
|
|
360
|
-
* @param {(newArgs:
|
|
407
|
+
* @param {unknown[]} args
|
|
408
|
+
* @param {(newArgs: unknown[]) => void} onChange
|
|
361
409
|
* @param {{ stateDefs: string[]; allowEventRef: boolean; depth: number }} opts
|
|
362
410
|
* @returns {import("lit-html").TemplateResult}
|
|
363
411
|
*/
|
|
@@ -365,7 +413,7 @@ function renderSpliceArgsEditor(
|
|
|
365
413
|
args: unknown[],
|
|
366
414
|
onChange: (newArgs: unknown[]) => void,
|
|
367
415
|
opts: { stateDefs: string[]; allowEventRef: boolean; depth: number },
|
|
368
|
-
):
|
|
416
|
+
): TemplateResult {
|
|
369
417
|
const safeArgs = Array.isArray(args) ? args : [];
|
|
370
418
|
const labels = ["start", "del", "item"];
|
|
371
419
|
|
|
@@ -424,8 +472,8 @@ const _operatorMenuCache = OPERATOR_GROUPS.map(
|
|
|
424
472
|
// ─── Main Expression Editor ─────────────────────────────────────────────────
|
|
425
473
|
|
|
426
474
|
/**
|
|
427
|
-
* @param {
|
|
428
|
-
* @param {(node:
|
|
475
|
+
* @param {unknown} node
|
|
476
|
+
* @param {(node: unknown) => void} onChange
|
|
429
477
|
* @param {{
|
|
430
478
|
* stateDefs: string[];
|
|
431
479
|
* allowEventRef: boolean;
|
|
@@ -437,7 +485,7 @@ export function renderExpressionEditor(
|
|
|
437
485
|
node: unknown,
|
|
438
486
|
onChange: (node: unknown) => void,
|
|
439
487
|
opts: { stateDefs: string[]; allowEventRef: boolean; depth?: number },
|
|
440
|
-
):
|
|
488
|
+
): TemplateResult {
|
|
441
489
|
const depth = opts.depth ?? 0;
|
|
442
490
|
const safeNode: Record<string, unknown> =
|
|
443
491
|
node && typeof node === "object"
|
|
@@ -454,9 +502,9 @@ export function renderExpressionEditor(
|
|
|
454
502
|
return html`
|
|
455
503
|
<div class="expression-editor" style=${nestStyle}>
|
|
456
504
|
${renderFieldRow({
|
|
457
|
-
prop: "operator",
|
|
458
|
-
label: "Operator",
|
|
459
505
|
hasValue: false,
|
|
506
|
+
label: "Operator",
|
|
507
|
+
prop: "operator",
|
|
460
508
|
widget: html`
|
|
461
509
|
<sp-picker
|
|
462
510
|
size="s"
|
|
@@ -464,7 +512,10 @@ export function renderExpressionEditor(
|
|
|
464
512
|
@change=${(e: Event) => {
|
|
465
513
|
const newOp = (e.target as HTMLInputElement).value;
|
|
466
514
|
const newInfo = operatorInfo(newOp);
|
|
467
|
-
const updated:
|
|
515
|
+
const updated: Record<string, unknown> = {
|
|
516
|
+
operator: newOp,
|
|
517
|
+
target: safeNode.target,
|
|
518
|
+
};
|
|
468
519
|
if (newInfo.targetMustBeRef && operandMode(safeNode.target) !== "ref") {
|
|
469
520
|
updated.target = { $ref: "" };
|
|
470
521
|
}
|
|
@@ -478,7 +529,9 @@ export function renderExpressionEditor(
|
|
|
478
529
|
updated.value = safeNode.value ?? null;
|
|
479
530
|
}
|
|
480
531
|
}
|
|
481
|
-
if (newInfo.needsInitial)
|
|
532
|
+
if (newInfo.needsInitial) {
|
|
533
|
+
updated.initial = safeNode.initial ?? 0;
|
|
534
|
+
}
|
|
482
535
|
onChange(updated);
|
|
483
536
|
}}
|
|
484
537
|
>
|
|
@@ -487,9 +540,9 @@ export function renderExpressionEditor(
|
|
|
487
540
|
`,
|
|
488
541
|
})}
|
|
489
542
|
${renderFieldRow({
|
|
490
|
-
prop: "target",
|
|
491
|
-
label: "Target",
|
|
492
543
|
hasValue: false,
|
|
544
|
+
label: "Target",
|
|
545
|
+
prop: "target",
|
|
493
546
|
widget: renderOperandEditor(safeNode.target, (t) => onChange({ ...safeNode, target: t }), {
|
|
494
547
|
...opts,
|
|
495
548
|
depth,
|
|
@@ -498,9 +551,9 @@ export function renderExpressionEditor(
|
|
|
498
551
|
})}
|
|
499
552
|
${info.needsValue && !info.valueIsNode && !info.spliceArray
|
|
500
553
|
? renderFieldRow({
|
|
501
|
-
prop: "value",
|
|
502
|
-
label: "Value",
|
|
503
554
|
hasValue: false,
|
|
555
|
+
label: "Value",
|
|
556
|
+
prop: "value",
|
|
504
557
|
widget: renderOperandEditor(
|
|
505
558
|
safeNode.value,
|
|
506
559
|
(v) => onChange({ ...safeNode, value: v }),
|
|
@@ -512,9 +565,9 @@ export function renderExpressionEditor(
|
|
|
512
565
|
? html`
|
|
513
566
|
<div style="margin-top:4px">
|
|
514
567
|
${renderFieldRow({
|
|
515
|
-
prop: "value",
|
|
516
|
-
label: "Per-item",
|
|
517
568
|
hasValue: false,
|
|
569
|
+
label: "Per-item",
|
|
570
|
+
prop: "value",
|
|
518
571
|
widget: nothing,
|
|
519
572
|
})}
|
|
520
573
|
${renderExpressionEditor(
|
|
@@ -531,9 +584,9 @@ export function renderExpressionEditor(
|
|
|
531
584
|
? html`
|
|
532
585
|
<div style="margin-top:4px">
|
|
533
586
|
${renderFieldRow({
|
|
534
|
-
prop: "value",
|
|
535
|
-
label: "Args",
|
|
536
587
|
hasValue: false,
|
|
588
|
+
label: "Args",
|
|
589
|
+
prop: "value",
|
|
537
590
|
widget: nothing,
|
|
538
591
|
})}
|
|
539
592
|
${renderSpliceArgsEditor(
|
|
@@ -549,9 +602,9 @@ export function renderExpressionEditor(
|
|
|
549
602
|
: nothing}
|
|
550
603
|
${info.needsInitial
|
|
551
604
|
? renderFieldRow({
|
|
552
|
-
prop: "initial",
|
|
553
|
-
label: "Initial",
|
|
554
605
|
hasValue: false,
|
|
606
|
+
label: "Initial",
|
|
607
|
+
prop: "initial",
|
|
555
608
|
widget: renderOperandEditor(
|
|
556
609
|
safeNode.initial,
|
|
557
610
|
(v) => onChange({ ...safeNode, initial: v }),
|
package/src/ui/field-input.ts
CHANGED
|
@@ -49,14 +49,19 @@ export function hasDraft(key: string): boolean {
|
|
|
49
49
|
/** Record the in-progress value for a field without committing it. */
|
|
50
50
|
export function setDraft(key: string, value: string): void {
|
|
51
51
|
const d = _drafts.get(key);
|
|
52
|
-
if (d)
|
|
53
|
-
|
|
52
|
+
if (d) {
|
|
53
|
+
d.value = value;
|
|
54
|
+
} else {
|
|
55
|
+
_drafts.set(key, { value });
|
|
56
|
+
}
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
/** Discard a field's draft (and cancel any pending debounced commit). */
|
|
57
60
|
export function clearDraft(key: string): void {
|
|
58
61
|
const d = _drafts.get(key);
|
|
59
|
-
if (d?.timer)
|
|
62
|
+
if (d?.timer) {
|
|
63
|
+
clearTimeout(d.timer);
|
|
64
|
+
}
|
|
60
65
|
_drafts.delete(key);
|
|
61
66
|
}
|
|
62
67
|
|
|
@@ -67,11 +72,17 @@ export function clearDraft(key: string): void {
|
|
|
67
72
|
*/
|
|
68
73
|
export function scheduleDraftCommit(key: string, ms: number, commit: (v: string) => void): void {
|
|
69
74
|
const d = _drafts.get(key);
|
|
70
|
-
if (!d)
|
|
71
|
-
|
|
75
|
+
if (!d) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (d.timer) {
|
|
79
|
+
clearTimeout(d.timer);
|
|
80
|
+
}
|
|
72
81
|
d.timer = setTimeout(() => {
|
|
73
82
|
const cur = _drafts.get(key);
|
|
74
|
-
if (!cur)
|
|
83
|
+
if (!cur) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
75
86
|
delete cur.timer;
|
|
76
87
|
commit(cur.value);
|
|
77
88
|
}, ms);
|
|
@@ -83,9 +94,13 @@ export function scheduleDraftCommit(key: string, ms: number, commit: (v: string)
|
|
|
83
94
|
*/
|
|
84
95
|
export function commitField(key: string, commit: (v: string) => void): void {
|
|
85
96
|
const d = _drafts.get(key);
|
|
86
|
-
if (!d)
|
|
87
|
-
|
|
88
|
-
|
|
97
|
+
if (!d) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (d.timer) {
|
|
101
|
+
clearTimeout(d.timer);
|
|
102
|
+
}
|
|
103
|
+
const { value } = d;
|
|
89
104
|
_drafts.delete(key);
|
|
90
105
|
commit(value);
|
|
91
106
|
}
|
|
@@ -119,15 +134,17 @@ function makeHandlers(
|
|
|
119
134
|
commitMode: "live" | "blur" = "live",
|
|
120
135
|
) {
|
|
121
136
|
return {
|
|
122
|
-
|
|
137
|
+
onCommit: (e: Event) => {
|
|
123
138
|
const v = (e.target as HTMLInputElement).value;
|
|
124
139
|
setDraft(key, v);
|
|
125
|
-
|
|
140
|
+
commitField(key, commit);
|
|
126
141
|
},
|
|
127
|
-
|
|
142
|
+
onInput: (e: Event) => {
|
|
128
143
|
const v = (e.target as HTMLInputElement).value;
|
|
129
144
|
setDraft(key, v);
|
|
130
|
-
|
|
145
|
+
if (commitMode === "live") {
|
|
146
|
+
scheduleDraftCommit(key, ms, commit);
|
|
147
|
+
}
|
|
131
148
|
},
|
|
132
149
|
onKeydown: (e: KeyboardEvent, multiline: boolean) => {
|
|
133
150
|
if (e.key === "Enter" && !multiline) {
|
|
@@ -158,7 +175,7 @@ export function spTextField(
|
|
|
158
175
|
<sp-textfield
|
|
159
176
|
size=${opts.size ?? "s"}
|
|
160
177
|
placeholder=${opts.placeholder ?? ""}
|
|
161
|
-
?disabled=${
|
|
178
|
+
?disabled=${Boolean(opts.disabled)}
|
|
162
179
|
style=${opts.style ?? ""}
|
|
163
180
|
.value=${live(getFieldValue(key, value))}
|
|
164
181
|
@input=${onInput}
|
|
@@ -185,7 +202,7 @@ export function spTextArea(
|
|
|
185
202
|
multiline
|
|
186
203
|
size=${opts.size ?? "s"}
|
|
187
204
|
placeholder=${opts.placeholder ?? ""}
|
|
188
|
-
?disabled=${
|
|
205
|
+
?disabled=${Boolean(opts.disabled)}
|
|
189
206
|
style=${opts.style ?? ""}
|
|
190
207
|
.value=${live(getFieldValue(key, value))}
|
|
191
208
|
@input=${onInput}
|
|
@@ -218,7 +235,7 @@ export function rawTextArea(
|
|
|
218
235
|
class="field-input"
|
|
219
236
|
style=${style}
|
|
220
237
|
placeholder=${opts.placeholder ?? ""}
|
|
221
|
-
?disabled=${
|
|
238
|
+
?disabled=${Boolean(opts.disabled)}
|
|
222
239
|
.value=${live(getFieldValue(key, value))}
|
|
223
240
|
@input=${onInput}
|
|
224
241
|
@change=${onCommit}
|
|
@@ -241,13 +258,13 @@ export function spNumberField(
|
|
|
241
258
|
<sp-number-field
|
|
242
259
|
size=${opts.size ?? "s"}
|
|
243
260
|
?hide-stepper=${opts.hideStepper ?? true}
|
|
244
|
-
?disabled=${
|
|
261
|
+
?disabled=${Boolean(opts.disabled)}
|
|
245
262
|
style=${opts.style ?? ""}
|
|
246
263
|
.value=${live(value !== undefined ? Number(value) : undefined)}
|
|
247
264
|
@change=${(e: Event) => {
|
|
248
265
|
const raw = (e.target as HTMLInputElement).value;
|
|
249
266
|
const n = Number(raw);
|
|
250
|
-
commit(raw === "" || isNaN(n) ? undefined : n);
|
|
267
|
+
commit(raw === "" || Number.isNaN(n) ? undefined : n);
|
|
251
268
|
}}
|
|
252
269
|
></sp-number-field>
|
|
253
270
|
`;
|
package/src/ui/field-row.ts
CHANGED
|
@@ -42,7 +42,7 @@ export function renderFieldRow({
|
|
|
42
42
|
}) {
|
|
43
43
|
return html`
|
|
44
44
|
<div
|
|
45
|
-
class=${classMap({ "style-row": true, "style-row--warning":
|
|
45
|
+
class=${classMap({ "style-row": true, "style-row--warning": Boolean(warning) })}
|
|
46
46
|
data-prop=${prop}
|
|
47
47
|
style=${span === 2 ? "grid-column: 1 / -1" : ""}
|
|
48
48
|
>
|
package/src/ui/icons.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Packages/studio/icons.js
|
|
2
2
|
// Icon templates for style sidebar button groups.
|
|
3
3
|
// Uses Spectrum workflow icons where available; custom SVGs for flex-specific concepts.
|
|
4
4
|
|
|
5
5
|
import { html } from "lit";
|
|
6
|
+
import type { TemplateResult } from "lit";
|
|
6
7
|
|
|
7
8
|
// Helper for custom filled-rect icons (alignment/justify diagrams) where no Spectrum match exists
|
|
8
|
-
const _R = (d:
|
|
9
|
+
const _R = (d: TemplateResult) =>
|
|
9
10
|
html`<svg
|
|
10
11
|
xmlns="http://www.w3.org/2000/svg"
|
|
11
12
|
width="16"
|
|
@@ -18,7 +19,7 @@ const _R = (d: import("lit").TemplateResult) =>
|
|
|
18
19
|
</svg>`;
|
|
19
20
|
|
|
20
21
|
// Helper for custom stroke icons
|
|
21
|
-
const _S = (d:
|
|
22
|
+
const _S = (d: TemplateResult) =>
|
|
22
23
|
html`<svg
|
|
23
24
|
xmlns="http://www.w3.org/2000/svg"
|
|
24
25
|
width="16"
|
|
@@ -33,7 +34,7 @@ const _S = (d: import("lit").TemplateResult) =>
|
|
|
33
34
|
${d}
|
|
34
35
|
</svg>`;
|
|
35
36
|
|
|
36
|
-
const icons = {
|
|
37
|
+
export const icons = {
|
|
37
38
|
// ─── Arrows — flexDirection ───
|
|
38
39
|
"arrow-right": html`<sp-icon-arrow-right slot="icon"></sp-icon-arrow-right>`,
|
|
39
40
|
"arrow-left": html`<sp-icon-arrow-left slot="icon"></sp-icon-arrow-left>`,
|
|
@@ -79,5 +80,3 @@ const icons = {
|
|
|
79
80
|
"display-inline": html`<sp-icon-remove slot="icon"></sp-icon-remove>`,
|
|
80
81
|
"display-none": html`<sp-icon-visibility-off slot="icon"></sp-icon-visibility-off>`,
|
|
81
82
|
};
|
|
82
|
-
|
|
83
|
-
export default icons;
|