@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
|
@@ -23,7 +23,7 @@ import { getLayerSlot } from "../ui/layers.js";
|
|
|
23
23
|
*
|
|
24
24
|
* @typedef {import("../state.js").JxPath} JxPath
|
|
25
25
|
*
|
|
26
|
-
* @typedef {
|
|
26
|
+
* @typedef {import("../editor/inline-edit.js").InlineAction} InlineAction
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -182,7 +182,7 @@ function applyInlineFormat(action) {
|
|
|
182
182
|
code: "code",
|
|
183
183
|
};
|
|
184
184
|
|
|
185
|
-
const tag = cmdToTag[action.command];
|
|
185
|
+
const tag = action.command ? cmdToTag[action.command] : undefined;
|
|
186
186
|
if (tag) {
|
|
187
187
|
const editableRoot = getActiveElement();
|
|
188
188
|
toggleInlineFormat(tag, editableRoot);
|
|
@@ -423,7 +423,7 @@ export function renderBlockActionBar() {
|
|
|
423
423
|
@mousedown=${captureSelectionRange}
|
|
424
424
|
@click=${(/** @type {MouseEvent} */ e) => onFormatClick(e, action)}
|
|
425
425
|
>
|
|
426
|
-
${formatIconMap[action.icon] ?? nothing}
|
|
426
|
+
${action.icon ? (formatIconMap[action.icon] ?? nothing) : nothing}
|
|
427
427
|
</sp-action-button>
|
|
428
428
|
`,
|
|
429
429
|
)}
|
|
@@ -437,7 +437,7 @@ export function renderBlockActionBar() {
|
|
|
437
437
|
|
|
438
438
|
// Post-render side effects
|
|
439
439
|
requestAnimationFrame(() => {
|
|
440
|
-
const bar = view.blockActionBarEl?.firstElementChild;
|
|
440
|
+
const bar = /** @type {HTMLElement | null} */ (view.blockActionBarEl?.firstElementChild);
|
|
441
441
|
if (!bar) return;
|
|
442
442
|
// Clamp to window
|
|
443
443
|
const barRect = bar.getBoundingClientRect();
|
|
@@ -447,7 +447,7 @@ export function renderBlockActionBar() {
|
|
|
447
447
|
// Attach drag handle
|
|
448
448
|
const currentTab = activeTab.value;
|
|
449
449
|
if (currentTab?.session.selection && currentTab.session.selection.length >= 2) {
|
|
450
|
-
const handle = bar.querySelector(".bar-drag-handle");
|
|
450
|
+
const handle = /** @type {HTMLElement | null} */ (bar.querySelector(".bar-drag-handle"));
|
|
451
451
|
if (handle) {
|
|
452
452
|
if (view.selDragCleanup) {
|
|
453
453
|
view.selDragCleanup();
|
package/src/panels/canvas-dnd.js
CHANGED
|
@@ -16,11 +16,15 @@ import { effectiveZoom } from "../canvas/canvas-helpers.js";
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* @typedef {{
|
|
19
|
+
* mediaName: string | null;
|
|
20
|
+
* element: HTMLElement | null;
|
|
19
21
|
* canvas: HTMLElement;
|
|
20
|
-
* overlayClk: HTMLElement;
|
|
21
22
|
* overlay: HTMLElement;
|
|
23
|
+
* overlayClk: HTMLElement;
|
|
22
24
|
* viewport: HTMLElement;
|
|
25
|
+
* scrollContainer: HTMLElement | null;
|
|
23
26
|
* dropLine: HTMLElement;
|
|
27
|
+
* _width: number | null;
|
|
24
28
|
* }} CanvasPanel
|
|
25
29
|
*
|
|
26
30
|
* @typedef {(string | number)[]} JxPath
|
|
@@ -56,7 +60,9 @@ export function registerPanelDnD(panel) {
|
|
|
56
60
|
onDrop() {
|
|
57
61
|
_activeDropEl?.classList.remove("canvas-drop-target");
|
|
58
62
|
_activeDropEl = null;
|
|
59
|
-
for (const p of canvasPanels)
|
|
63
|
+
for (const p of canvasPanels) {
|
|
64
|
+
if (p.dropLine) p.dropLine.style.display = "none";
|
|
65
|
+
}
|
|
60
66
|
view.lastDragInput = null;
|
|
61
67
|
for (const el of canvas.querySelectorAll("*")) {
|
|
62
68
|
/** @type {HTMLElement} */ (el).style.pointerEvents = "none";
|
|
@@ -7,13 +7,18 @@ import { classMap } from "lit-html/directives/class-map.js";
|
|
|
7
7
|
const expandedDataKeys = new Set();
|
|
8
8
|
|
|
9
9
|
/** Unwrap a Vue ref (has .value and .__v_isRef) to get the underlying value. */
|
|
10
|
-
function unwrapSignal(/** @type {
|
|
11
|
-
if (
|
|
10
|
+
function unwrapSignal(/** @type {unknown} */ value) {
|
|
11
|
+
if (
|
|
12
|
+
value &&
|
|
13
|
+
typeof value === "object" &&
|
|
14
|
+
/** @type {Record<string, unknown>} */ (value).__v_isRef
|
|
15
|
+
)
|
|
16
|
+
return /** @type {Record<string, unknown>} */ (value).value;
|
|
12
17
|
return value;
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
/** Type label for a signal value in the data explorer. */
|
|
16
|
-
function dataTypeLabel(/** @type {
|
|
21
|
+
function dataTypeLabel(/** @type {unknown} */ value) {
|
|
17
22
|
const v = unwrapSignal(value);
|
|
18
23
|
if (v === null) return "null";
|
|
19
24
|
if (v === undefined) return "pending";
|
|
@@ -25,15 +30,15 @@ function dataTypeLabel(/** @type {any} */ value) {
|
|
|
25
30
|
/**
|
|
26
31
|
* Render the data explorer tab showing live resolved values.
|
|
27
32
|
*
|
|
28
|
-
* @param {Record<string,
|
|
29
|
-
* @param {Record<string,
|
|
33
|
+
* @param {Record<string, unknown>} state - S.document.state (the $defs definitions)
|
|
34
|
+
* @param {Record<string, unknown> | null} liveScope - Cached live scope from runtime rendering
|
|
30
35
|
* @param {{
|
|
31
36
|
* renderCanvas: () => void;
|
|
32
37
|
* renderLeftPanel: () => void;
|
|
33
|
-
* defCategory: (def:
|
|
34
|
-
* defBadgeLabel: (def:
|
|
38
|
+
* defCategory: (def: unknown) => string;
|
|
39
|
+
* defBadgeLabel: (def: unknown) => string;
|
|
35
40
|
* }} callbacks
|
|
36
|
-
* @returns {
|
|
41
|
+
* @returns {import("lit-html").TemplateResult}
|
|
37
42
|
*/
|
|
38
43
|
export function renderDataExplorerTemplate(state, liveScope, callbacks) {
|
|
39
44
|
const { renderCanvas, renderLeftPanel, defCategory, defBadgeLabel } = callbacks;
|
|
@@ -91,11 +96,11 @@ export function renderDataExplorerTemplate(state, liveScope, callbacks) {
|
|
|
91
96
|
/**
|
|
92
97
|
* Recursively render a JSON value as a tree view (Lit template).
|
|
93
98
|
*
|
|
94
|
-
* @returns {
|
|
99
|
+
* @returns {import("lit-html").TemplateResult}
|
|
95
100
|
*/
|
|
96
101
|
export function renderDataTreeTemplate(
|
|
97
|
-
/** @type {
|
|
98
|
-
/** @type {
|
|
102
|
+
/** @type {unknown} */ value,
|
|
103
|
+
/** @type {number} */ depth,
|
|
99
104
|
maxDepth = 5,
|
|
100
105
|
) {
|
|
101
106
|
const indent = `${(depth + 1) * 12}px`;
|
|
@@ -150,10 +155,11 @@ export function renderDataTreeTemplate(
|
|
|
150
155
|
}
|
|
151
156
|
|
|
152
157
|
// Object
|
|
153
|
-
const
|
|
158
|
+
const obj = /** @type {Record<string, unknown>} */ (value);
|
|
159
|
+
const keys = Object.keys(obj);
|
|
154
160
|
const cap = 30;
|
|
155
161
|
const items = keys.slice(0, cap).map((key) => {
|
|
156
|
-
const v =
|
|
162
|
+
const v = obj[key];
|
|
157
163
|
if (v === null || v === undefined || typeof v !== "object") {
|
|
158
164
|
const valText =
|
|
159
165
|
typeof v === "string" && v.length > 80 ? `"${v.slice(0, 80)}\u2026"` : JSON.stringify(v);
|
package/src/panels/dnd.js
CHANGED
|
@@ -3,6 +3,19 @@
|
|
|
3
3
|
* on layer rows, component cards, and element cards.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {{ element: HTMLElement; input: { clientX: number; clientY: number } }} DragCanDragArgs
|
|
8
|
+
*
|
|
9
|
+
* @typedef {{ source: { data: Record<string, unknown>; element: HTMLElement } }} DragDropSourceArgs
|
|
10
|
+
*
|
|
11
|
+
* @typedef {{ self: { data: Record<string, unknown> } }} DragSelfArgs
|
|
12
|
+
*
|
|
13
|
+
* @typedef {{
|
|
14
|
+
* source: { data: Record<string, unknown>; element: HTMLElement };
|
|
15
|
+
* location: { current: { dropTargets: { data: Record<string, unknown> }[] } };
|
|
16
|
+
* }} DragMonitorDropArgs
|
|
17
|
+
*/
|
|
18
|
+
|
|
6
19
|
import {
|
|
7
20
|
draggable,
|
|
8
21
|
dropTargetForElements,
|
|
@@ -32,14 +45,18 @@ import { defaultDef, unsafeTags } from "./shared.js";
|
|
|
32
45
|
/** Register DnD on layer rows — called from left-panel.js after render */
|
|
33
46
|
export function registerLayersDnD() {
|
|
34
47
|
requestAnimationFrame(() => {
|
|
35
|
-
const container = /** @type {
|
|
48
|
+
const container = /** @type {HTMLElement | null} */ (
|
|
49
|
+
leftPanel?.querySelector(".layers-container")
|
|
50
|
+
);
|
|
36
51
|
if (!container) return;
|
|
37
52
|
|
|
38
|
-
container.querySelectorAll("[data-dnd-row]").forEach(
|
|
39
|
-
|
|
40
|
-
const rowPath = /** @type {
|
|
41
|
-
.
|
|
42
|
-
|
|
53
|
+
/** @type {NodeListOf<HTMLElement>} */ (container.querySelectorAll("[data-dnd-row]")).forEach(
|
|
54
|
+
(row) => {
|
|
55
|
+
const rowPath = /** @type {JxPath} */ (
|
|
56
|
+
/** @type {string} */ (row.dataset.path)
|
|
57
|
+
.split("/")
|
|
58
|
+
.map((/** @type {string} */ s) => (/^\d+$/.test(s) ? parseInt(s) : s))
|
|
59
|
+
);
|
|
43
60
|
const rowDepth = parseInt(/** @type {string} */ (row.dataset.dndDepth)) || 0;
|
|
44
61
|
const isVoid = row.hasAttribute("data-dnd-void");
|
|
45
62
|
const isExpanded = row.hasAttribute("data-dnd-expanded");
|
|
@@ -47,7 +64,7 @@ export function registerLayersDnD() {
|
|
|
47
64
|
const cleanup = combine(
|
|
48
65
|
draggable({
|
|
49
66
|
element: row,
|
|
50
|
-
canDrag(/** @type {
|
|
67
|
+
canDrag(/** @type {DragCanDragArgs} */ { element: _el, input }) {
|
|
51
68
|
const target = /** @type {HTMLElement} */ (
|
|
52
69
|
document.elementFromPoint(input.clientX, input.clientY)
|
|
53
70
|
);
|
|
@@ -73,28 +90,30 @@ export function registerLayersDnD() {
|
|
|
73
90
|
}),
|
|
74
91
|
dropTargetForElements({
|
|
75
92
|
element: row,
|
|
76
|
-
canDrop(/** @type {
|
|
77
|
-
const srcPath = source.data.path;
|
|
93
|
+
canDrop(/** @type {DragDropSourceArgs} */ { source }) {
|
|
94
|
+
const srcPath = /** @type {JxPath | undefined} */ (source.data.path);
|
|
78
95
|
if (srcPath && isAncestor(srcPath, rowPath)) return false;
|
|
79
96
|
return true;
|
|
80
97
|
},
|
|
81
98
|
getData(/** @type {any} */ { input, element }) {
|
|
82
|
-
return
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
99
|
+
return /** @type {Record<string | symbol, unknown>} */ (
|
|
100
|
+
attachInstruction(
|
|
101
|
+
{ path: rowPath },
|
|
102
|
+
{
|
|
103
|
+
input,
|
|
104
|
+
element,
|
|
105
|
+
currentLevel: rowDepth,
|
|
106
|
+
indentPerLevel: 16,
|
|
107
|
+
mode: isExpanded ? "expanded" : "standard",
|
|
108
|
+
block: isVoid ? ["make-child"] : [],
|
|
109
|
+
},
|
|
110
|
+
)
|
|
92
111
|
);
|
|
93
112
|
},
|
|
94
|
-
onDragEnter(/** @type {
|
|
113
|
+
onDragEnter(/** @type {DragSelfArgs} */ { self }) {
|
|
95
114
|
showLayerDropGap(row, self.data, container);
|
|
96
115
|
},
|
|
97
|
-
onDrag(/** @type {
|
|
116
|
+
onDrag(/** @type {DragSelfArgs} */ { self }) {
|
|
98
117
|
showLayerDropGap(row, self.data, container);
|
|
99
118
|
},
|
|
100
119
|
onDragLeave() {
|
|
@@ -111,14 +130,14 @@ export function registerLayersDnD() {
|
|
|
111
130
|
|
|
112
131
|
// Global monitor
|
|
113
132
|
const monitorCleanup = monitorForElements({
|
|
114
|
-
onDrop(/** @type {
|
|
133
|
+
onDrop(/** @type {DragMonitorDropArgs} */ { source, location }) {
|
|
115
134
|
clearLayerDropGap(container);
|
|
116
135
|
const target = location.current.dropTargets[0];
|
|
117
136
|
if (!target) return;
|
|
118
137
|
const instruction = extractInstruction(target.data);
|
|
119
138
|
if (!instruction || instruction.type === "instruction-blocked") return;
|
|
120
139
|
const srcData = source.data;
|
|
121
|
-
const targetPath = target.data.path;
|
|
140
|
+
const targetPath = /** @type {JxPath} */ (target.data.path);
|
|
122
141
|
|
|
123
142
|
// If the source had children, persist collapse at the new location
|
|
124
143
|
const srcRow = srcData.type === "tree-node" && source.element;
|
|
@@ -143,56 +162,62 @@ export function registerLayersDnD() {
|
|
|
143
162
|
/** Register DnD on component rows — called from renderLeftPanel when tab=components */
|
|
144
163
|
export function registerComponentsDnD() {
|
|
145
164
|
requestAnimationFrame(() => {
|
|
146
|
-
const container = /** @type {
|
|
165
|
+
const container = /** @type {HTMLElement | null} */ (
|
|
166
|
+
leftPanel?.querySelector(".components-section")
|
|
167
|
+
);
|
|
147
168
|
if (!container) return;
|
|
148
169
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
170
|
+
/** @type {NodeListOf<HTMLElement>} */ (
|
|
171
|
+
container.querySelectorAll("[data-component-tag]")
|
|
172
|
+
).forEach((row) => {
|
|
173
|
+
const tagName = row.dataset.componentTag;
|
|
174
|
+
if (!tagName) return;
|
|
175
|
+
const comp = componentRegistry.find(
|
|
176
|
+
(/** @type {import("../files/components.js").ComponentEntry} */ c) => c.tagName === tagName,
|
|
177
|
+
);
|
|
178
|
+
if (!comp) return;
|
|
155
179
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
180
|
+
// Fill preview with live rendered component
|
|
181
|
+
const preview = row.querySelector(".element-card-preview");
|
|
182
|
+
if (preview && !preview.querySelector(tagName)) {
|
|
183
|
+
renderComponentPreview(comp).then((/** @type {HTMLElement} */ el) => {
|
|
184
|
+
preview.textContent = "";
|
|
185
|
+
preview.appendChild(el);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
164
188
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
189
|
+
const instanceDef = {
|
|
190
|
+
tagName: comp.tagName,
|
|
191
|
+
$props: comp.props
|
|
192
|
+
? Object.fromEntries(
|
|
193
|
+
comp.props.map(
|
|
194
|
+
(/** @type {{ name: string; default?: unknown; [k: string]: unknown }} */ p) => [
|
|
170
195
|
p.name,
|
|
171
196
|
p.default !== undefined ? p.default : "",
|
|
172
|
-
]
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
);
|
|
197
|
+
],
|
|
198
|
+
),
|
|
199
|
+
)
|
|
200
|
+
: {},
|
|
201
|
+
};
|
|
202
|
+
const cleanup = draggable({
|
|
203
|
+
element: row,
|
|
204
|
+
getInitialData() {
|
|
205
|
+
return { type: "block", fragment: structuredClone(instanceDef) };
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
view.dndCleanups.push(cleanup);
|
|
209
|
+
});
|
|
185
210
|
});
|
|
186
211
|
}
|
|
187
212
|
|
|
188
213
|
/** Register DnD on element (HTML block) rows */
|
|
189
214
|
export function registerElementsDnD() {
|
|
190
215
|
requestAnimationFrame(() => {
|
|
191
|
-
const container = /** @type {
|
|
216
|
+
const container = /** @type {HTMLElement | null} */ (leftPanel?.querySelector(".panel-body"));
|
|
192
217
|
if (!container) return;
|
|
193
|
-
container.querySelectorAll("[data-block-tag]").forEach(
|
|
194
|
-
|
|
195
|
-
const tag = row.dataset.blockTag;
|
|
218
|
+
/** @type {NodeListOf<HTMLElement>} */ (container.querySelectorAll("[data-block-tag]")).forEach(
|
|
219
|
+
(row) => {
|
|
220
|
+
const tag = /** @type {string} */ (row.dataset.blockTag);
|
|
196
221
|
const preview = row.querySelector(".element-card-preview");
|
|
197
222
|
if (preview && !preview.firstChild) {
|
|
198
223
|
const el = document.createElement(unsafeTags.has(tag) ? "span" : tag);
|
|
@@ -215,23 +240,23 @@ export function registerElementsDnD() {
|
|
|
215
240
|
/**
|
|
216
241
|
* Hide descendant rows of the dragged item so it appears collapsed during drag.
|
|
217
242
|
*
|
|
218
|
-
* @param {
|
|
219
|
-
* @param {
|
|
243
|
+
* @param {HTMLElement} parentRow
|
|
244
|
+
* @param {HTMLElement} container
|
|
220
245
|
*/
|
|
221
246
|
function hideDescendantRows(parentRow, container) {
|
|
222
247
|
const prefix = parentRow.dataset.path + "/";
|
|
223
248
|
const rows = container.querySelectorAll(".layers-tree .layer-row");
|
|
224
249
|
for (const r of rows) {
|
|
225
|
-
if (/** @type {
|
|
226
|
-
/** @type {
|
|
250
|
+
if (/** @type {HTMLElement} */ (r).dataset.path?.startsWith(prefix)) {
|
|
251
|
+
/** @type {HTMLElement} */ (r).style.display = "none";
|
|
227
252
|
}
|
|
228
253
|
}
|
|
229
254
|
}
|
|
230
255
|
|
|
231
256
|
/**
|
|
232
|
-
* @param {
|
|
233
|
-
* @param {
|
|
234
|
-
* @param {
|
|
257
|
+
* @param {HTMLElement} rowEl
|
|
258
|
+
* @param {Record<string, unknown>} data
|
|
259
|
+
* @param {HTMLElement} container
|
|
235
260
|
*/
|
|
236
261
|
export function showLayerDropGap(rowEl, data, container) {
|
|
237
262
|
const instruction = extractInstruction(data);
|
|
@@ -262,37 +287,39 @@ export function showLayerDropGap(rowEl, data, container) {
|
|
|
262
287
|
const gap = view.layerDragSourceHeight;
|
|
263
288
|
|
|
264
289
|
for (let i = 0; i < rows.length; i++) {
|
|
265
|
-
if (/** @type {
|
|
290
|
+
if (/** @type {HTMLElement} */ (rows[i]).classList.contains("dragging")) continue;
|
|
266
291
|
if (instruction.type === "reorder-above") {
|
|
267
|
-
/** @type {
|
|
292
|
+
/** @type {HTMLElement} */ (rows[i]).style.transform =
|
|
293
|
+
i >= targetIdx ? `translateY(${gap}px)` : "";
|
|
268
294
|
} else {
|
|
269
|
-
/** @type {
|
|
295
|
+
/** @type {HTMLElement} */ (rows[i]).style.transform =
|
|
296
|
+
i > targetIdx ? `translateY(${gap}px)` : "";
|
|
270
297
|
}
|
|
271
298
|
}
|
|
272
299
|
}
|
|
273
300
|
|
|
274
|
-
/** @param {
|
|
301
|
+
/** @param {HTMLElement} container */
|
|
275
302
|
export function clearLayerDropGap(container) {
|
|
276
303
|
if (view._currentDropTargetRow) {
|
|
277
304
|
view._currentDropTargetRow.classList.remove("drop-target");
|
|
278
305
|
view._currentDropTargetRow = null;
|
|
279
306
|
}
|
|
280
307
|
const rows = container.querySelectorAll(".layers-tree .layer-row");
|
|
281
|
-
for (const r of rows) /** @type {
|
|
308
|
+
for (const r of rows) /** @type {HTMLElement} */ (r).style.transform = "";
|
|
282
309
|
}
|
|
283
310
|
|
|
284
311
|
/**
|
|
285
312
|
* Apply a DnD instruction to the state
|
|
286
313
|
*
|
|
287
|
-
* @param {
|
|
288
|
-
* @param {
|
|
289
|
-
* @param {
|
|
314
|
+
* @param {{ type: string }} instruction
|
|
315
|
+
* @param {Record<string, unknown>} srcData
|
|
316
|
+
* @param {JxPath} targetPath
|
|
290
317
|
*/
|
|
291
318
|
export function applyDropInstruction(instruction, srcData, targetPath) {
|
|
292
|
-
const
|
|
319
|
+
const doc = /** @type {JxMutableNode} */ (activeTab.value?.doc.document);
|
|
293
320
|
if (srcData.type === "tree-node") {
|
|
294
|
-
const fromPath = srcData.path;
|
|
295
|
-
const targetParent = /** @type {
|
|
321
|
+
const fromPath = /** @type {JxPath} */ (srcData.path);
|
|
322
|
+
const targetParent = /** @type {JxPath} */ (parentElementPath(targetPath));
|
|
296
323
|
const targetIdx = /** @type {number} */ (childIndex(targetPath));
|
|
297
324
|
|
|
298
325
|
switch (instruction.type) {
|
|
@@ -305,64 +332,89 @@ export function applyDropInstruction(instruction, srcData, targetPath) {
|
|
|
305
332
|
);
|
|
306
333
|
break;
|
|
307
334
|
case "make-child": {
|
|
308
|
-
const target = getNodeAtPath(
|
|
335
|
+
const target = getNodeAtPath(doc, targetPath);
|
|
309
336
|
const len = target?.children?.length || 0;
|
|
310
337
|
transactDoc(activeTab.value, (t) => mutateMoveNode(t, fromPath, targetPath, len));
|
|
311
338
|
break;
|
|
312
339
|
}
|
|
313
340
|
}
|
|
314
341
|
} else if (srcData.type === "block") {
|
|
315
|
-
const targetParent = /** @type {
|
|
342
|
+
const targetParent = /** @type {JxPath} */ (parentElementPath(targetPath));
|
|
316
343
|
const targetIdx = /** @type {number} */ (childIndex(targetPath));
|
|
317
344
|
|
|
318
345
|
switch (instruction.type) {
|
|
319
346
|
case "reorder-above":
|
|
320
347
|
transactDoc(activeTab.value, (t) =>
|
|
321
|
-
mutateInsertNode(
|
|
348
|
+
mutateInsertNode(
|
|
349
|
+
t,
|
|
350
|
+
targetParent,
|
|
351
|
+
targetIdx,
|
|
352
|
+
structuredClone(/** @type {JxMutableNode} */ (srcData.fragment)),
|
|
353
|
+
),
|
|
322
354
|
);
|
|
323
355
|
break;
|
|
324
356
|
case "reorder-below":
|
|
325
357
|
transactDoc(activeTab.value, (t) =>
|
|
326
|
-
mutateInsertNode(
|
|
358
|
+
mutateInsertNode(
|
|
359
|
+
t,
|
|
360
|
+
targetParent,
|
|
361
|
+
targetIdx + 1,
|
|
362
|
+
structuredClone(/** @type {JxMutableNode} */ (srcData.fragment)),
|
|
363
|
+
),
|
|
327
364
|
);
|
|
328
365
|
break;
|
|
329
366
|
case "make-child": {
|
|
330
|
-
const target = getNodeAtPath(
|
|
367
|
+
const target = getNodeAtPath(doc, targetPath);
|
|
331
368
|
const len = target?.children?.length || 0;
|
|
332
369
|
transactDoc(activeTab.value, (t) =>
|
|
333
|
-
mutateInsertNode(
|
|
370
|
+
mutateInsertNode(
|
|
371
|
+
t,
|
|
372
|
+
targetPath,
|
|
373
|
+
len,
|
|
374
|
+
structuredClone(/** @type {JxMutableNode} */ (srcData.fragment)),
|
|
375
|
+
),
|
|
334
376
|
);
|
|
335
377
|
break;
|
|
336
378
|
}
|
|
337
379
|
}
|
|
338
380
|
|
|
339
381
|
// Auto-import to $elements if the dropped block is a custom component
|
|
340
|
-
const
|
|
382
|
+
const fragment = /** @type {JxMutableNode | undefined} */ (srcData.fragment);
|
|
383
|
+
const tag = fragment?.tagName;
|
|
341
384
|
if (tag && tag.includes("-")) {
|
|
342
|
-
const comp = componentRegistry.find(
|
|
385
|
+
const comp = componentRegistry.find(
|
|
386
|
+
(/** @type {import("../files/components.js").ComponentEntry} */ c) => c.tagName === tag,
|
|
387
|
+
);
|
|
343
388
|
if (comp) {
|
|
344
389
|
const tab = activeTab.value;
|
|
345
390
|
const elements = tab?.doc.document?.$elements || [];
|
|
346
391
|
if (comp.source === "npm") {
|
|
347
392
|
const specifier = comp.modulePath ? `${comp.package}/${comp.modulePath}` : comp.package;
|
|
393
|
+
if (!specifier) return;
|
|
348
394
|
const alreadyImported = elements.some(
|
|
349
|
-
(/** @type {
|
|
395
|
+
(/** @type {JxMutableNode | string | { $ref: string }} */ e) =>
|
|
396
|
+
e === specifier || e === comp.package,
|
|
350
397
|
);
|
|
351
398
|
if (!alreadyImported) {
|
|
352
|
-
transact(activeTab.value, (/** @type {
|
|
399
|
+
transact(activeTab.value, (/** @type {JxMutableNode} */ doc) => {
|
|
353
400
|
if (!doc.$elements) doc.$elements = [];
|
|
354
401
|
doc.$elements.push(specifier);
|
|
355
402
|
});
|
|
356
403
|
}
|
|
357
404
|
} else {
|
|
358
405
|
const alreadyImported = elements.some(
|
|
359
|
-
(/** @type {
|
|
360
|
-
e.$ref
|
|
361
|
-
|
|
406
|
+
(/** @type {JxMutableNode | string | { $ref: string }} */ e) => {
|
|
407
|
+
const ref = typeof e === "object" && e !== null ? e.$ref : undefined;
|
|
408
|
+
return (
|
|
409
|
+
ref &&
|
|
410
|
+
(ref === `./${comp.path}` ||
|
|
411
|
+
ref.endsWith(/** @type {string} */ (comp.path.split("/").pop())))
|
|
412
|
+
);
|
|
413
|
+
},
|
|
362
414
|
);
|
|
363
415
|
if (!alreadyImported) {
|
|
364
416
|
const relPath = computeRelativePath(tab?.documentPath, comp.path);
|
|
365
|
-
transact(activeTab.value, (/** @type {
|
|
417
|
+
transact(activeTab.value, (/** @type {JxMutableNode} */ doc) => {
|
|
366
418
|
if (!doc.$elements) doc.$elements = [];
|
|
367
419
|
doc.$elements.push({ $ref: relPath });
|
|
368
420
|
});
|