@jxsuite/studio 0.11.0 → 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/dist/studio.js +4248 -3412
- package/dist/studio.js.map +49 -43
- package/package.json +5 -3
- package/src/canvas/canvas-diff.js +184 -0
- package/src/canvas/canvas-helpers.js +10 -14
- package/src/canvas/canvas-live-render.js +3 -2
- package/src/canvas/canvas-render.js +152 -20
- 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/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 +91 -41
- package/src/panels/panel-events.js +9 -12
- package/src/panels/properties-panel.js +179 -104
- 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 +11 -15
- 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 +56 -7
- package/src/settings/defs-editor.js +56 -7
- package/src/settings/schema-field-ui.js +60 -25
- package/src/state.js +0 -456
- package/src/store.js +97 -124
- package/src/studio.js +112 -121
- package/src/tabs/tab.js +153 -0
- package/src/tabs/transact.js +406 -0
- package/src/workspace/workspace.js +89 -0
|
@@ -6,18 +6,9 @@
|
|
|
6
6
|
import { html, render as litRender, nothing } from "lit-html";
|
|
7
7
|
import { draggable } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
updateSession,
|
|
13
|
-
renderOnly,
|
|
14
|
-
selectNode,
|
|
15
|
-
moveNode,
|
|
16
|
-
getNodeAtPath,
|
|
17
|
-
nodeLabel,
|
|
18
|
-
parentElementPath,
|
|
19
|
-
childIndex,
|
|
20
|
-
} from "../store.js";
|
|
9
|
+
import { getNodeAtPath, nodeLabel, parentElementPath, childIndex } from "../store.js";
|
|
10
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
11
|
+
import { transactDoc, mutateMoveNode } from "../tabs/transact.js";
|
|
21
12
|
import { view } from "../view.js";
|
|
22
13
|
import { isEditing, getActiveElement, getInlineActions } from "../editor/inline-edit.js";
|
|
23
14
|
import { toggleInlineFormat, isTagActiveInSelection } from "../editor/inline-format.js";
|
|
@@ -25,7 +16,21 @@ import { componentRegistry } from "../files/components.js";
|
|
|
25
16
|
import { convertToComponent } from "../editor/convert-to-component.js";
|
|
26
17
|
import { findCanvasElement, getActivePanel } from "../canvas/canvas-helpers.js";
|
|
27
18
|
|
|
28
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {import("../state.js").StudioState} StudioState
|
|
21
|
+
*
|
|
22
|
+
* @typedef {import("../state.js").JxPath} JxPath
|
|
23
|
+
*
|
|
24
|
+
* @typedef {{ command: string; tag: string; label: string; icon: string; shortcut?: string }} InlineAction
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @type {{
|
|
29
|
+
* getCanvasMode: () => string;
|
|
30
|
+
* navigateToComponent: (path: string) => void;
|
|
31
|
+
* createFloatingContainer: () => HTMLElement;
|
|
32
|
+
* } | null}
|
|
33
|
+
*/
|
|
29
34
|
let _ctx = null;
|
|
30
35
|
|
|
31
36
|
/**
|
|
@@ -33,8 +38,8 @@ let _ctx = null;
|
|
|
33
38
|
*
|
|
34
39
|
* @param {{
|
|
35
40
|
* getCanvasMode: () => string;
|
|
36
|
-
* navigateToComponent:
|
|
37
|
-
* createFloatingContainer:
|
|
41
|
+
* navigateToComponent: (path: string) => void;
|
|
42
|
+
* createFloatingContainer: () => HTMLElement;
|
|
38
43
|
* }} ctx
|
|
39
44
|
*/
|
|
40
45
|
export function initBlockActionBar(ctx) {
|
|
@@ -45,7 +50,7 @@ export function initBlockActionBar(ctx) {
|
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
/** Pre-built icon templates for inline format buttons (avoids unsafeStatic) */
|
|
48
|
-
const formatIconMap = /** @type {Record<string,
|
|
53
|
+
const formatIconMap = /** @type {Record<string, import("lit-html").TemplateResult>} */ ({
|
|
49
54
|
"sp-icon-text-bold": html`<sp-icon-text-bold slot="icon"></sp-icon-text-bold>`,
|
|
50
55
|
"sp-icon-text-italic": html`<sp-icon-text-italic slot="icon"></sp-icon-text-italic>`,
|
|
51
56
|
"sp-icon-text-underline": html`<sp-icon-text-underline slot="icon"></sp-icon-text-underline>`,
|
|
@@ -63,11 +68,11 @@ const formatIconMap = /** @type {Record<string, any>} */ ({
|
|
|
63
68
|
/**
|
|
64
69
|
* Prevent the bar from stealing focus from contenteditable
|
|
65
70
|
*
|
|
66
|
-
* @param {
|
|
71
|
+
* @param {MouseEvent} e
|
|
67
72
|
*/
|
|
68
73
|
function onBarMousedown(e) {
|
|
69
|
-
if (e.target.closest("sp-textfield")) return;
|
|
70
|
-
if (e.target.closest(".bar-drag-handle")) return;
|
|
74
|
+
if (/** @type {HTMLElement} */ (e.target).closest("sp-textfield")) return;
|
|
75
|
+
if (/** @type {HTMLElement} */ (e.target).closest(".bar-drag-handle")) return;
|
|
71
76
|
e.preventDefault();
|
|
72
77
|
}
|
|
73
78
|
|
|
@@ -78,42 +83,48 @@ function captureSelectionRange() {
|
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
/**
|
|
81
|
-
* @param {
|
|
82
|
-
* @param {
|
|
86
|
+
* @param {MouseEvent} e
|
|
87
|
+
* @param {InlineAction} action
|
|
83
88
|
*/
|
|
84
89
|
function onFormatClick(e, action) {
|
|
85
90
|
e.stopPropagation();
|
|
86
91
|
if (action.command === "link") {
|
|
87
|
-
showLinkPopover(
|
|
92
|
+
showLinkPopover(
|
|
93
|
+
/** @type {HTMLElement} */ (
|
|
94
|
+
/** @type {HTMLElement} */ (e.target).closest("sp-action-button")
|
|
95
|
+
),
|
|
96
|
+
);
|
|
88
97
|
} else if (view.savedRange) {
|
|
89
|
-
const sel =
|
|
98
|
+
const sel = window.getSelection();
|
|
90
99
|
const anchor = view.savedRange.startContainer;
|
|
91
100
|
const editableRoot = (
|
|
92
|
-
anchor?.nodeType === Node.ELEMENT_NODE
|
|
101
|
+
anchor?.nodeType === Node.ELEMENT_NODE
|
|
102
|
+
? /** @type {Element} */ (anchor)
|
|
103
|
+
: anchor?.parentElement
|
|
93
104
|
)?.closest("[contenteditable]");
|
|
94
105
|
if (editableRoot) {
|
|
95
|
-
editableRoot.focus();
|
|
96
|
-
sel
|
|
97
|
-
sel
|
|
106
|
+
/** @type {HTMLElement} */ (editableRoot).focus();
|
|
107
|
+
sel?.removeAllRanges();
|
|
108
|
+
sel?.addRange(view.savedRange);
|
|
98
109
|
applyInlineFormat(action);
|
|
99
110
|
}
|
|
100
111
|
}
|
|
101
112
|
}
|
|
102
113
|
|
|
103
114
|
function renderParentSelector() {
|
|
104
|
-
const
|
|
105
|
-
|
|
115
|
+
const tab = activeTab.value;
|
|
116
|
+
if (!tab?.session.selection) return nothing;
|
|
117
|
+
const pPath = parentElementPath(tab.session.selection);
|
|
106
118
|
if (!pPath) return nothing;
|
|
107
|
-
const parentNode = getNodeAtPath(
|
|
119
|
+
const parentNode = getNodeAtPath(tab.doc.document, pPath);
|
|
108
120
|
return html`
|
|
109
121
|
<sp-action-button
|
|
110
122
|
size="xs"
|
|
111
123
|
quiet
|
|
112
124
|
title="Select parent: ${nodeLabel(parentNode)}"
|
|
113
|
-
@click=${(/** @type {
|
|
125
|
+
@click=${(/** @type {MouseEvent} */ e) => {
|
|
114
126
|
e.stopPropagation();
|
|
115
|
-
|
|
116
|
-
update(selectNode(S, pPath));
|
|
127
|
+
activeTab.value.session.selection = pPath;
|
|
117
128
|
}}
|
|
118
129
|
>
|
|
119
130
|
<sp-icon-back slot="icon"></sp-icon-back>
|
|
@@ -122,10 +133,12 @@ function renderParentSelector() {
|
|
|
122
133
|
}
|
|
123
134
|
|
|
124
135
|
function renderMoveArrows() {
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
const
|
|
136
|
+
const tab = activeTab.value;
|
|
137
|
+
if (!tab?.session.selection) return nothing;
|
|
138
|
+
const sel = tab.session.selection;
|
|
139
|
+
const idx = /** @type {number} */ (childIndex(sel));
|
|
140
|
+
const pPath = parentElementPath(sel);
|
|
141
|
+
const parentNode = getNodeAtPath(tab.doc.document, /** @type {JxPath} */ (pPath));
|
|
129
142
|
const siblings = parentNode?.children;
|
|
130
143
|
return html`
|
|
131
144
|
<sp-action-button
|
|
@@ -133,7 +146,7 @@ function renderMoveArrows() {
|
|
|
133
146
|
quiet
|
|
134
147
|
title="Move up"
|
|
135
148
|
?disabled=${idx <= 0}
|
|
136
|
-
@click=${(/** @type {
|
|
149
|
+
@click=${(/** @type {MouseEvent} */ e) => {
|
|
137
150
|
e.stopPropagation();
|
|
138
151
|
moveSelectionUp();
|
|
139
152
|
}}
|
|
@@ -145,7 +158,7 @@ function renderMoveArrows() {
|
|
|
145
158
|
quiet
|
|
146
159
|
title="Move down"
|
|
147
160
|
?disabled=${!siblings || idx >= siblings.length - 1}
|
|
148
|
-
@click=${(/** @type {
|
|
161
|
+
@click=${(/** @type {MouseEvent} */ e) => {
|
|
149
162
|
e.stopPropagation();
|
|
150
163
|
moveSelectionDown();
|
|
151
164
|
}}
|
|
@@ -158,10 +171,10 @@ function renderMoveArrows() {
|
|
|
158
171
|
/**
|
|
159
172
|
* Apply an inline format action.
|
|
160
173
|
*
|
|
161
|
-
* @param {
|
|
174
|
+
* @param {InlineAction} action
|
|
162
175
|
*/
|
|
163
176
|
function applyInlineFormat(action) {
|
|
164
|
-
/** @type {Record<string,
|
|
177
|
+
/** @type {Record<string, string>} */
|
|
165
178
|
const cmdToTag = {
|
|
166
179
|
bold: "strong",
|
|
167
180
|
italic: "em",
|
|
@@ -190,19 +203,22 @@ export function dismissBlockActionBar() {
|
|
|
190
203
|
if (view.blockActionBarEl) litRender(nothing, view.blockActionBarEl);
|
|
191
204
|
}
|
|
192
205
|
|
|
193
|
-
/** @param {
|
|
206
|
+
/** @param {HTMLElement} anchorBtn */
|
|
194
207
|
function showLinkPopover(anchorBtn) {
|
|
195
208
|
litRender(nothing, view.linkPopoverHost);
|
|
196
209
|
|
|
197
210
|
const sel = window.getSelection();
|
|
198
|
-
/** @type {
|
|
211
|
+
/** @type {HTMLAnchorElement | null} */
|
|
199
212
|
let existingLink = null;
|
|
200
213
|
if (sel?.rangeCount) {
|
|
201
|
-
/** @type {
|
|
214
|
+
/** @type {Node | null} */
|
|
202
215
|
let node = sel.anchorNode;
|
|
203
216
|
while (node && node !== document.body) {
|
|
204
|
-
if (
|
|
205
|
-
|
|
217
|
+
if (
|
|
218
|
+
node.nodeType === Node.ELEMENT_NODE &&
|
|
219
|
+
/** @type {Element} */ (node).tagName.toLowerCase() === "a"
|
|
220
|
+
) {
|
|
221
|
+
existingLink = /** @type {HTMLAnchorElement} */ (node);
|
|
206
222
|
break;
|
|
207
223
|
}
|
|
208
224
|
node = node.parentNode;
|
|
@@ -212,8 +228,10 @@ function showLinkPopover(anchorBtn) {
|
|
|
212
228
|
const rect = anchorBtn.getBoundingClientRect();
|
|
213
229
|
|
|
214
230
|
const onApply = () => {
|
|
215
|
-
const field =
|
|
216
|
-
|
|
231
|
+
const field = /** @type {HTMLInputElement | null} */ (
|
|
232
|
+
view.linkPopoverHost.querySelector("sp-textfield")
|
|
233
|
+
);
|
|
234
|
+
const url = field?.value || "";
|
|
217
235
|
if (existingLink) {
|
|
218
236
|
existingLink.setAttribute("href", url);
|
|
219
237
|
} else if (url) {
|
|
@@ -224,6 +242,7 @@ function showLinkPopover(anchorBtn) {
|
|
|
224
242
|
};
|
|
225
243
|
|
|
226
244
|
const onRemove = () => {
|
|
245
|
+
if (!existingLink?.parentNode) return;
|
|
227
246
|
const frag = document.createDocumentFragment();
|
|
228
247
|
while (existingLink.firstChild) frag.appendChild(existingLink.firstChild);
|
|
229
248
|
existingLink.parentNode.replaceChild(frag, existingLink);
|
|
@@ -231,7 +250,7 @@ function showLinkPopover(anchorBtn) {
|
|
|
231
250
|
renderBlockActionBar();
|
|
232
251
|
};
|
|
233
252
|
|
|
234
|
-
const onKeydown = (/** @type {
|
|
253
|
+
const onKeydown = (/** @type {KeyboardEvent} */ e) => {
|
|
235
254
|
if (e.key === "Enter") onApply();
|
|
236
255
|
else if (e.key === "Escape") {
|
|
237
256
|
litRender(nothing, view.linkPopoverHost);
|
|
@@ -273,32 +292,33 @@ function showLinkPopover(anchorBtn) {
|
|
|
273
292
|
|
|
274
293
|
/** Move the selected node up (swap with previous sibling). */
|
|
275
294
|
function moveSelectionUp() {
|
|
276
|
-
const
|
|
277
|
-
if (!
|
|
278
|
-
const
|
|
295
|
+
const tab = activeTab.value;
|
|
296
|
+
if (!tab?.session.selection || tab.session.selection.length < 2) return;
|
|
297
|
+
const sel = tab.session.selection;
|
|
298
|
+
const idx = /** @type {number} */ (childIndex(sel));
|
|
279
299
|
if (idx <= 0) return;
|
|
280
|
-
const pPath = /** @type {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
renderOnly("overlays");
|
|
300
|
+
const pPath = /** @type {JxPath} */ (parentElementPath(sel));
|
|
301
|
+
transactDoc(tab, (t) => mutateMoveNode(t, sel, pPath, idx - 1));
|
|
302
|
+
tab.session.selection = [...pPath, "children", idx - 1];
|
|
284
303
|
}
|
|
285
304
|
|
|
286
305
|
/** Move the selected node down (swap with next sibling). */
|
|
287
306
|
function moveSelectionDown() {
|
|
288
|
-
const
|
|
289
|
-
if (!
|
|
290
|
-
const
|
|
291
|
-
const
|
|
292
|
-
const
|
|
307
|
+
const tab = activeTab.value;
|
|
308
|
+
if (!tab?.session.selection || tab.session.selection.length < 2) return;
|
|
309
|
+
const sel = tab.session.selection;
|
|
310
|
+
const idx = /** @type {number} */ (childIndex(sel));
|
|
311
|
+
const pPath = /** @type {JxPath} */ (parentElementPath(sel));
|
|
312
|
+
const parentNode = getNodeAtPath(tab.doc.document, pPath);
|
|
293
313
|
const siblings = parentNode?.children;
|
|
294
314
|
if (!siblings || idx >= siblings.length - 1) return;
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
renderOnly("overlays");
|
|
315
|
+
transactDoc(tab, (t) => mutateMoveNode(t, sel, pPath, idx + 2));
|
|
316
|
+
tab.session.selection = [...pPath, "children", idx + 1];
|
|
298
317
|
}
|
|
299
318
|
|
|
300
319
|
/** Render the unified block action bar above the selected element. */
|
|
301
320
|
export function renderBlockActionBar() {
|
|
321
|
+
if (!_ctx) return;
|
|
302
322
|
if (!view.blockActionBarEl) {
|
|
303
323
|
view.blockActionBarEl = _ctx.createFloatingContainer();
|
|
304
324
|
}
|
|
@@ -308,21 +328,22 @@ export function renderBlockActionBar() {
|
|
|
308
328
|
view.selDragCleanup = null;
|
|
309
329
|
}
|
|
310
330
|
|
|
311
|
-
const
|
|
331
|
+
const tab = activeTab.value;
|
|
312
332
|
const canvasMode = _ctx.getCanvasMode();
|
|
313
333
|
|
|
314
|
-
if (!
|
|
334
|
+
if (!tab?.session.selection || (canvasMode !== "design" && canvasMode !== "edit")) {
|
|
315
335
|
litRender(nothing, view.blockActionBarEl);
|
|
316
336
|
return;
|
|
317
337
|
}
|
|
318
338
|
|
|
339
|
+
const selection = tab.session.selection;
|
|
319
340
|
const activePanel = getActivePanel();
|
|
320
341
|
if (!activePanel) {
|
|
321
342
|
litRender(nothing, view.blockActionBarEl);
|
|
322
343
|
return;
|
|
323
344
|
}
|
|
324
|
-
const el = findCanvasElement(
|
|
325
|
-
const node = el && getNodeAtPath(
|
|
345
|
+
const el = findCanvasElement(selection, activePanel.canvas);
|
|
346
|
+
const node = el && getNodeAtPath(tab.doc.document, selection);
|
|
326
347
|
if (!el || !node) {
|
|
327
348
|
litRender(nothing, view.blockActionBarEl);
|
|
328
349
|
return;
|
|
@@ -347,28 +368,31 @@ export function renderBlockActionBar() {
|
|
|
347
368
|
style="left:${elRect.left}px; top:${topPos}px"
|
|
348
369
|
@mousedown=${onBarMousedown}
|
|
349
370
|
>
|
|
350
|
-
${
|
|
371
|
+
${selection.length >= 2 ? renderParentSelector() : nothing}
|
|
351
372
|
|
|
352
373
|
<span class="bar-tag">${node.$id || (node.tagName ?? "div")}</span>
|
|
353
374
|
|
|
354
|
-
${
|
|
375
|
+
${selection.length >= 2
|
|
355
376
|
? html`<span class="bar-drag-handle" title="Drag to reorder">⡇</span>`
|
|
356
377
|
: nothing}
|
|
357
|
-
${
|
|
358
|
-
${
|
|
378
|
+
${selection.length >= 2 ? renderMoveArrows() : nothing}
|
|
379
|
+
${selection.length >= 2 && node.tagName
|
|
359
380
|
? (() => {
|
|
360
381
|
const isComp =
|
|
361
382
|
node.tagName.includes("-") &&
|
|
362
|
-
componentRegistry.some(
|
|
383
|
+
componentRegistry.some(
|
|
384
|
+
(/** @type {{ tagName: string }} */ c) => c.tagName === node.tagName,
|
|
385
|
+
);
|
|
363
386
|
if (isComp) {
|
|
364
387
|
const comp = componentRegistry.find(
|
|
365
|
-
(/** @type {
|
|
388
|
+
(/** @type {{ tagName: string; path: string }} */ c) =>
|
|
389
|
+
c.tagName === node.tagName,
|
|
366
390
|
);
|
|
367
391
|
return html`<sp-action-button
|
|
368
392
|
size="xs"
|
|
369
393
|
quiet
|
|
370
394
|
title="Edit Component"
|
|
371
|
-
@click=${() => _ctx
|
|
395
|
+
@click=${() => _ctx?.navigateToComponent(/** @type {string} */ (comp?.path))}
|
|
372
396
|
><sp-icon-edit slot="icon" size="xs"></sp-icon-edit
|
|
373
397
|
></sp-action-button>`;
|
|
374
398
|
}
|
|
@@ -376,7 +400,7 @@ export function renderBlockActionBar() {
|
|
|
376
400
|
size="xs"
|
|
377
401
|
quiet
|
|
378
402
|
title="Convert to Component"
|
|
379
|
-
@click=${() => convertToComponent(
|
|
403
|
+
@click=${() => convertToComponent()}
|
|
380
404
|
><sp-icon-box slot="icon" size="xs"></sp-icon-box
|
|
381
405
|
></sp-action-button>`;
|
|
382
406
|
})()
|
|
@@ -398,7 +422,7 @@ export function renderBlockActionBar() {
|
|
|
398
422
|
value=${action.tag}
|
|
399
423
|
title="${action.label}${action.shortcut ? ` (${action.shortcut})` : ""}"
|
|
400
424
|
@mousedown=${captureSelectionRange}
|
|
401
|
-
@click=${(/** @type {
|
|
425
|
+
@click=${(/** @type {MouseEvent} */ e) => onFormatClick(e, action)}
|
|
402
426
|
>
|
|
403
427
|
${formatIconMap[action.icon] ?? nothing}
|
|
404
428
|
</sp-action-button>
|
|
@@ -422,8 +446,8 @@ export function renderBlockActionBar() {
|
|
|
422
446
|
bar.style.left = `${Math.max(0, window.innerWidth - barRect.width)}px`;
|
|
423
447
|
}
|
|
424
448
|
// Attach drag handle
|
|
425
|
-
const
|
|
426
|
-
if (
|
|
449
|
+
const currentTab = activeTab.value;
|
|
450
|
+
if (currentTab?.session.selection && currentTab.session.selection.length >= 2) {
|
|
427
451
|
const handle = bar.querySelector(".bar-drag-handle");
|
|
428
452
|
if (handle) {
|
|
429
453
|
if (view.selDragCleanup) {
|
|
@@ -432,7 +456,7 @@ export function renderBlockActionBar() {
|
|
|
432
456
|
}
|
|
433
457
|
view.selDragCleanup = draggable({
|
|
434
458
|
element: handle,
|
|
435
|
-
getInitialData: () => ({ type: "tree-node", path:
|
|
459
|
+
getInitialData: () => ({ type: "tree-node", path: activeTab.value?.session.selection }),
|
|
436
460
|
});
|
|
437
461
|
}
|
|
438
462
|
}
|
package/src/panels/dnd.js
CHANGED
|
@@ -16,16 +16,14 @@ import {
|
|
|
16
16
|
|
|
17
17
|
import {
|
|
18
18
|
getState,
|
|
19
|
-
update,
|
|
20
19
|
leftPanel,
|
|
21
|
-
moveNode,
|
|
22
|
-
insertNode,
|
|
23
|
-
applyMutation,
|
|
24
20
|
getNodeAtPath,
|
|
25
21
|
parentElementPath,
|
|
26
22
|
childIndex,
|
|
27
23
|
isAncestor,
|
|
28
24
|
} from "../store.js";
|
|
25
|
+
import { transact, transactDoc, mutateMoveNode, mutateInsertNode } from "../tabs/transact.js";
|
|
26
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
29
27
|
import { view } from "../view.js";
|
|
30
28
|
import { componentRegistry, computeRelativePath } from "../files/components.js";
|
|
31
29
|
import { renderComponentPreview } from "./stylebook-panel.js";
|
|
@@ -144,12 +142,14 @@ export function registerComponentsDnD() {
|
|
|
144
142
|
|
|
145
143
|
const instanceDef = {
|
|
146
144
|
tagName: comp.tagName,
|
|
147
|
-
$props:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
145
|
+
$props: comp.props
|
|
146
|
+
? Object.fromEntries(
|
|
147
|
+
comp.props.map((/** @type {any} */ p) => [
|
|
148
|
+
p.name,
|
|
149
|
+
p.default !== undefined ? p.default : "",
|
|
150
|
+
]),
|
|
151
|
+
)
|
|
152
|
+
: {},
|
|
153
153
|
};
|
|
154
154
|
const cleanup = draggable({
|
|
155
155
|
element: row,
|
|
@@ -259,15 +259,17 @@ export function applyDropInstruction(instruction, srcData, targetPath) {
|
|
|
259
259
|
|
|
260
260
|
switch (instruction.type) {
|
|
261
261
|
case "reorder-above":
|
|
262
|
-
|
|
262
|
+
transactDoc(activeTab.value, (t) => mutateMoveNode(t, fromPath, targetParent, targetIdx));
|
|
263
263
|
break;
|
|
264
264
|
case "reorder-below":
|
|
265
|
-
|
|
265
|
+
transactDoc(activeTab.value, (t) =>
|
|
266
|
+
mutateMoveNode(t, fromPath, targetParent, targetIdx + 1),
|
|
267
|
+
);
|
|
266
268
|
break;
|
|
267
269
|
case "make-child": {
|
|
268
270
|
const target = getNodeAtPath(S.document, targetPath);
|
|
269
271
|
const len = target?.children?.length || 0;
|
|
270
|
-
|
|
272
|
+
transactDoc(activeTab.value, (t) => mutateMoveNode(t, fromPath, targetPath, len));
|
|
271
273
|
break;
|
|
272
274
|
}
|
|
273
275
|
}
|
|
@@ -277,15 +279,21 @@ export function applyDropInstruction(instruction, srcData, targetPath) {
|
|
|
277
279
|
|
|
278
280
|
switch (instruction.type) {
|
|
279
281
|
case "reorder-above":
|
|
280
|
-
|
|
282
|
+
transactDoc(activeTab.value, (t) =>
|
|
283
|
+
mutateInsertNode(t, targetParent, targetIdx, structuredClone(srcData.fragment)),
|
|
284
|
+
);
|
|
281
285
|
break;
|
|
282
286
|
case "reorder-below":
|
|
283
|
-
|
|
287
|
+
transactDoc(activeTab.value, (t) =>
|
|
288
|
+
mutateInsertNode(t, targetParent, targetIdx + 1, structuredClone(srcData.fragment)),
|
|
289
|
+
);
|
|
284
290
|
break;
|
|
285
291
|
case "make-child": {
|
|
286
292
|
const target = getNodeAtPath(S.document, targetPath);
|
|
287
293
|
const len = target?.children?.length || 0;
|
|
288
|
-
|
|
294
|
+
transactDoc(activeTab.value, (t) =>
|
|
295
|
+
mutateInsertNode(t, targetPath, len, structuredClone(srcData.fragment)),
|
|
296
|
+
);
|
|
289
297
|
break;
|
|
290
298
|
}
|
|
291
299
|
}
|
|
@@ -303,12 +311,10 @@ export function applyDropInstruction(instruction, srcData, targetPath) {
|
|
|
303
311
|
(/** @type {any} */ e) => e === specifier || e === comp.package,
|
|
304
312
|
);
|
|
305
313
|
if (!alreadyImported) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}),
|
|
311
|
-
);
|
|
314
|
+
transact(activeTab.value, (/** @type {any} */ doc) => {
|
|
315
|
+
if (!doc.$elements) doc.$elements = [];
|
|
316
|
+
doc.$elements.push(specifier);
|
|
317
|
+
});
|
|
312
318
|
}
|
|
313
319
|
} else {
|
|
314
320
|
const alreadyImported = elements.some(
|
|
@@ -318,12 +324,10 @@ export function applyDropInstruction(instruction, srcData, targetPath) {
|
|
|
318
324
|
);
|
|
319
325
|
if (!alreadyImported) {
|
|
320
326
|
const relPath = computeRelativePath(currentS.documentPath, comp.path);
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}),
|
|
326
|
-
);
|
|
327
|
+
transact(activeTab.value, (/** @type {any} */ doc) => {
|
|
328
|
+
if (!doc.$elements) doc.$elements = [];
|
|
329
|
+
doc.$elements.push({ $ref: relPath });
|
|
330
|
+
});
|
|
327
331
|
}
|
|
328
332
|
}
|
|
329
333
|
}
|
package/src/panels/editors.js
CHANGED
|
@@ -7,16 +7,9 @@ import * as monaco from "monaco-editor/esm/vs/editor/editor.api.js";
|
|
|
7
7
|
import { html, render as litRender, nothing } from "lit-html";
|
|
8
8
|
import { ref } from "lit-html/directives/ref.js";
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
renderOnly,
|
|
14
|
-
canvasWrap,
|
|
15
|
-
canvasPanels,
|
|
16
|
-
updateDef,
|
|
17
|
-
updateProperty,
|
|
18
|
-
getNodeAtPath,
|
|
19
|
-
} from "../store.js";
|
|
10
|
+
import { getState, renderOnly, canvasWrap, canvasPanels, getNodeAtPath } from "../store.js";
|
|
11
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
12
|
+
import { transactDoc, mutateUpdateDef, mutateUpdateProperty } from "../tabs/transact.js";
|
|
20
13
|
import { view } from "../view.js";
|
|
21
14
|
import { codeService, setLintMarkers, getFunctionArgs } from "../services/code-services.js";
|
|
22
15
|
|
|
@@ -126,15 +119,15 @@ export function renderFunctionEditor() {
|
|
|
126
119
|
|
|
127
120
|
clearTimeout(syncDebounce);
|
|
128
121
|
syncDebounce = setTimeout(() => {
|
|
129
|
-
const S = getState();
|
|
130
122
|
const newBody = view.functionEditor.getValue();
|
|
131
123
|
if (editing.type === "def") {
|
|
132
|
-
|
|
124
|
+
transactDoc(activeTab.value, (t) => mutateUpdateDef(t, editing.defName, { body: newBody }));
|
|
133
125
|
} else if (editing.type === "event") {
|
|
126
|
+
const S = getState();
|
|
134
127
|
const node = getNodeAtPath(S.document, editing.path);
|
|
135
128
|
const current = node?.[editing.eventKey] || {};
|
|
136
|
-
|
|
137
|
-
|
|
129
|
+
transactDoc(activeTab.value, (t) =>
|
|
130
|
+
mutateUpdateProperty(t, editing.path, editing.eventKey, {
|
|
138
131
|
...current,
|
|
139
132
|
$prototype: "Function",
|
|
140
133
|
body: newBody,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/** Elements panel — block/component palette with categorized accordion and search filter. */
|
|
2
2
|
|
|
3
3
|
import { html, nothing } from "lit-html";
|
|
4
|
-
import { getState,
|
|
4
|
+
import { getState, getNodeAtPath } from "../store.js";
|
|
5
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
6
|
+
import { transactDoc, mutateInsertNode } from "../tabs/transact.js";
|
|
5
7
|
import { view } from "../view.js";
|
|
6
8
|
import { getEffectiveElements } from "../site-context.js";
|
|
7
9
|
import { componentRegistry } from "../files/components.js";
|
|
@@ -40,7 +42,9 @@ export function renderElementsTemplate(ctx) {
|
|
|
40
42
|
const parentPath = s.selection || [];
|
|
41
43
|
const parent = getNodeAtPath(s.document, parentPath);
|
|
42
44
|
const idx = parent?.children ? parent.children.length : 0;
|
|
43
|
-
|
|
45
|
+
transactDoc(activeTab.value, (t) =>
|
|
46
|
+
mutateInsertNode(t, parentPath, idx, structuredClone(def)),
|
|
47
|
+
);
|
|
44
48
|
}}
|
|
45
49
|
>
|
|
46
50
|
<div class="element-card-preview"></div>
|
|
@@ -114,7 +118,9 @@ export function renderElementsTemplate(ctx) {
|
|
|
114
118
|
]),
|
|
115
119
|
),
|
|
116
120
|
};
|
|
117
|
-
|
|
121
|
+
transactDoc(activeTab.value, (t) =>
|
|
122
|
+
mutateInsertNode(t, parentPath, idx, structuredClone(instanceDef)),
|
|
123
|
+
);
|
|
118
124
|
}}
|
|
119
125
|
>
|
|
120
126
|
<div class="element-card-preview">
|