@jxsuite/studio 0.23.2 → 0.24.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 +2230 -714
- package/dist/studio.js.map +121 -104
- package/package.json +8 -7
- package/src/browse/{browse-modal.js → browse-modal.ts} +9 -10
- package/src/browse/{browse.js → browse.ts} +109 -84
- package/src/canvas/{canvas-diff.js → canvas-diff.ts} +42 -35
- package/src/canvas/{canvas-helpers.js → canvas-helpers.ts} +25 -19
- package/src/canvas/{canvas-live-render.js → canvas-live-render.ts} +53 -63
- package/src/canvas/{canvas-render.js → canvas-render.ts} +110 -121
- package/src/canvas/{canvas-utils.js → canvas-utils.ts} +48 -44
- package/src/canvas/{nested-site-style.js → nested-site-style.ts} +3 -3
- package/src/editor/{component-inline-edit.js → component-inline-edit.ts} +45 -33
- package/src/editor/{content-inline-edit.js → content-inline-edit.ts} +27 -45
- package/src/editor/{context-menu.js → context-menu.ts} +38 -36
- package/src/editor/{convert-targets.js → convert-targets.ts} +15 -12
- package/src/editor/{convert-to-component.js → convert-to-component.ts} +25 -26
- package/src/editor/{convert-to-repeater.js → convert-to-repeater.ts} +60 -40
- package/src/editor/{inline-edit.js → inline-edit.ts} +91 -55
- package/src/editor/{inline-format.js → inline-format.ts} +46 -51
- package/src/editor/{insertion-helper.js → insertion-helper.ts} +47 -63
- package/src/editor/{shortcuts.js → shortcuts.ts} +45 -31
- package/src/editor/{slash-menu.js → slash-menu.ts} +34 -35
- package/src/files/{components.js → components.ts} +12 -15
- package/src/files/{file-ops.js → file-ops.ts} +36 -29
- package/src/files/{files.js → files.ts} +102 -84
- package/src/github/{github-auth.js → github-auth.ts} +3 -3
- package/src/github/{github-publish.js → github-publish.ts} +20 -18
- package/src/markdown/{md-allowlist.js → md-allowlist.ts} +5 -2
- package/src/markdown/{md-convert.js → md-convert.ts} +146 -169
- package/src/new-project/{new-project-modal.js → new-project-modal.ts} +16 -18
- package/src/panels/{activity-bar.js → activity-bar.ts} +26 -34
- package/src/panels/{ai-panel.js → ai-panel.ts} +30 -30
- package/src/panels/{block-action-bar.js → block-action-bar.ts} +64 -73
- package/src/panels/{canvas-dnd.js → canvas-dnd.ts} +44 -49
- package/src/panels/{data-explorer.js → data-explorer.ts} +21 -15
- package/src/panels/dnd.ts +417 -0
- package/src/panels/{editors.js → editors.ts} +43 -50
- package/src/panels/{elements-panel.js → elements-panel.ts} +30 -27
- package/src/panels/{events-panel.js → events-panel.ts} +33 -25
- package/src/panels/{git-panel.js → git-panel.ts} +97 -83
- package/src/panels/{head-panel.js → head-panel.ts} +117 -139
- package/src/panels/{imports-panel.js → imports-panel.ts} +69 -89
- package/src/panels/{layers-panel.js → layers-panel.ts} +61 -57
- package/src/panels/{left-panel.js → left-panel.ts} +56 -63
- package/src/panels/{overlays.js → overlays.ts} +28 -36
- package/src/panels/{panel-events.js → panel-events.ts} +53 -51
- package/src/panels/{preview-render.js → preview-render.ts} +20 -15
- package/src/panels/{properties-panel.js → properties-panel.ts} +212 -246
- package/src/panels/{pseudo-preview.js → pseudo-preview.ts} +9 -8
- package/src/panels/{quick-search.js → quick-search.ts} +16 -17
- package/src/panels/{right-panel.js → right-panel.ts} +41 -59
- package/src/panels/{shared.js → shared.ts} +7 -5
- package/src/panels/{signals-panel.js → signals-panel.ts} +322 -362
- package/src/panels/{statusbar.js → statusbar.ts} +14 -14
- package/src/panels/{style-inputs.js → style-inputs.ts} +66 -48
- package/src/panels/{style-panel.js → style-panel.ts} +235 -290
- package/src/panels/{style-utils.js → style-utils.ts} +32 -31
- package/src/panels/{stylebook-layers-panel.js → stylebook-layers-panel.ts} +22 -14
- package/src/panels/{stylebook-panel.js → stylebook-panel.ts} +127 -140
- package/src/panels/{tab-strip.js → tab-strip.ts} +12 -14
- package/src/panels/{toolbar.js → toolbar.ts} +68 -70
- package/src/panels/{welcome-screen.js → welcome-screen.ts} +14 -16
- package/src/{platform.js → platform.ts} +5 -3
- package/src/platforms/{devserver.js → devserver.ts} +53 -44
- package/src/{recent-projects.js → recent-projects.ts} +17 -12
- package/src/{resize-edges.js → resize-edges.ts} +18 -13
- package/src/services/{cem-export.js → cem-export.ts} +14 -7
- package/src/services/{code-services.js → code-services.ts} +29 -20
- package/src/services/{monaco-setup.js → monaco-setup.ts} +2 -3
- package/src/settings/{content-types-editor.js → content-types-editor.ts} +88 -67
- package/src/settings/{css-vars-editor.js → css-vars-editor.ts} +78 -51
- package/src/settings/{defs-editor.js → defs-editor.ts} +70 -56
- package/src/settings/{general-settings.js → general-settings.ts} +29 -28
- package/src/settings/{head-editor.js → head-editor.ts} +37 -35
- package/src/settings/{schema-field-ui.js → schema-field-ui.ts} +86 -76
- package/src/settings/{settings-modal.js → settings-modal.ts} +14 -16
- package/src/{site-context.js → site-context.ts} +35 -32
- package/src/{state.js → state.ts} +95 -78
- package/src/{store.js → store.ts} +60 -63
- package/src/{studio.js → studio.ts} +117 -119
- package/src/tabs/tab.ts +196 -0
- package/src/tabs/{transact.js → transact.ts} +107 -60
- package/src/types.ts +185 -0
- package/src/ui/{button-group.js → button-group.ts} +21 -21
- package/src/ui/{color-selector.js → color-selector.ts} +44 -37
- package/src/ui/{expression-editor.js → expression-editor.ts} +65 -40
- package/src/ui/{field-row.js → field-row.ts} +19 -2
- package/src/ui/{icons.js → icons.ts} +2 -2
- package/src/ui/{layers.js → layers.ts} +33 -24
- package/src/ui/{media-picker.js → media-picker.ts} +13 -9
- package/src/ui/{panel-resize.js → panel-resize.ts} +9 -4
- package/src/ui/{spectrum.js → spectrum.ts} +4 -4
- package/src/ui/{unit-selector.js → unit-selector.ts} +16 -19
- package/src/ui/{value-selector.js → value-selector.ts} +28 -28
- package/src/ui/{widgets.js → widgets.ts} +62 -15
- package/src/utils/{canvas-media.js → canvas-media.ts} +29 -14
- package/src/utils/{edit-display.js → edit-display.ts} +23 -20
- package/src/utils/{google-fonts.js → google-fonts.ts} +14 -13
- package/src/utils/{inherited-style.js → inherited-style.ts} +15 -10
- package/src/utils/{studio-utils.js → studio-utils.ts} +35 -23
- package/src/{view.js → view.ts} +53 -55
- package/src/workspace/{workspace.js → workspace.ts} +44 -33
- package/src/panels/dnd.js +0 -426
- package/src/tabs/tab.js +0 -192
- /package/src/{reactivity.js → reactivity.ts} +0 -0
|
@@ -1,53 +1,50 @@
|
|
|
1
|
-
import { reactive, computed } from "../reactivity
|
|
2
|
-
import { createTab, disposeTab } from "../tabs/tab
|
|
1
|
+
import { reactive, computed } from "../reactivity";
|
|
2
|
+
import { createTab, disposeTab } from "../tabs/tab";
|
|
3
|
+
import type { Tab } from "../tabs/tab";
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
import type { ComponentEntry } from "../files/components";
|
|
6
|
+
import type { JxStyle, JxMutableNode } from "@jxsuite/schema/types";
|
|
7
|
+
|
|
8
|
+
interface FileEntry {
|
|
9
|
+
name: string;
|
|
10
|
+
path: string;
|
|
11
|
+
type: string;
|
|
12
|
+
}
|
|
11
13
|
|
|
12
14
|
export const workspace = reactive({
|
|
13
15
|
/** @type {string | null} */
|
|
14
|
-
projectRoot: null,
|
|
16
|
+
projectRoot: null as string | null,
|
|
15
17
|
/** @type {object | null} */
|
|
16
|
-
projectConfig: null,
|
|
18
|
+
projectConfig: null as object | null,
|
|
17
19
|
/** @type {ComponentEntry[]} */
|
|
18
|
-
componentRegistry: [],
|
|
20
|
+
componentRegistry: [] as ComponentEntry[],
|
|
19
21
|
/** @type {JxMutableNode | null} */
|
|
20
|
-
clipboard: null,
|
|
22
|
+
clipboard: null as JxMutableNode | null,
|
|
21
23
|
/** @type {JxStyle | null} */
|
|
22
|
-
styleClipboard: null,
|
|
24
|
+
styleClipboard: null as JxStyle | null,
|
|
23
25
|
fileTree: {
|
|
24
26
|
/** @type {Map<string, FileEntry[]>} */
|
|
25
|
-
dirs: new Map(),
|
|
27
|
+
dirs: new Map<string, FileEntry[]>(),
|
|
26
28
|
/** @type {Set<string>} */
|
|
27
|
-
expanded: new Set(),
|
|
29
|
+
expanded: new Set<string>(),
|
|
28
30
|
/** @type {string | null} */
|
|
29
|
-
selectedPath: null,
|
|
31
|
+
selectedPath: null as string | null,
|
|
30
32
|
searchQuery: "",
|
|
31
33
|
},
|
|
32
34
|
/** @type {Map<string, Tab>} */
|
|
33
|
-
tabs: new Map(),
|
|
35
|
+
tabs: new Map<string, Tab>(),
|
|
34
36
|
/** @type {string[]} */
|
|
35
|
-
tabOrder: [],
|
|
37
|
+
tabOrder: [] as string[],
|
|
36
38
|
/** @type {string | null} */
|
|
37
|
-
activeTabId: null,
|
|
39
|
+
activeTabId: null as string | null,
|
|
38
40
|
ui: {
|
|
39
41
|
activityBar: "files",
|
|
40
42
|
},
|
|
41
43
|
});
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
computed(() =>
|
|
47
|
-
workspace.activeTabId ? (workspace.tabs.get(workspace.activeTabId) ?? null) : null,
|
|
48
|
-
)
|
|
49
|
-
)
|
|
50
|
-
);
|
|
45
|
+
export const activeTab = computed(() =>
|
|
46
|
+
workspace.activeTabId ? (workspace.tabs.get(workspace.activeTabId) ?? null) : null,
|
|
47
|
+
) as unknown as import("@vue/reactivity").ComputedRef<Tab | null>;
|
|
51
48
|
|
|
52
49
|
/**
|
|
53
50
|
* Open a new tab and make it active.
|
|
@@ -63,7 +60,15 @@ export const activeTab = /** @type {import("@vue/reactivity").ComputedRef<Tab>}
|
|
|
63
60
|
* }} opts
|
|
64
61
|
* @returns {Tab}
|
|
65
62
|
*/
|
|
66
|
-
export function openTab(opts
|
|
63
|
+
export function openTab(opts: {
|
|
64
|
+
id: string;
|
|
65
|
+
documentPath?: string | null;
|
|
66
|
+
fileHandle?: FileSystemFileHandle | null;
|
|
67
|
+
document: Record<string, unknown>;
|
|
68
|
+
frontmatter?: Record<string, unknown>;
|
|
69
|
+
sourceFormat?: string | null;
|
|
70
|
+
capabilities?: { modes?: string[] };
|
|
71
|
+
}) {
|
|
67
72
|
const tab = createTab(opts);
|
|
68
73
|
workspace.tabs.set(tab.id, tab);
|
|
69
74
|
workspace.tabOrder.push(tab.id);
|
|
@@ -76,7 +81,7 @@ export function openTab(opts) {
|
|
|
76
81
|
*
|
|
77
82
|
* @param {string} tabId
|
|
78
83
|
*/
|
|
79
|
-
export function closeTab(tabId) {
|
|
84
|
+
export function closeTab(tabId: string) {
|
|
80
85
|
const tab = workspace.tabs.get(tabId);
|
|
81
86
|
if (!tab) return;
|
|
82
87
|
disposeTab(tab);
|
|
@@ -111,7 +116,13 @@ export function closeAllTabs() {
|
|
|
111
116
|
* }} newTabOpts
|
|
112
117
|
* @returns {import("../tabs/tab.js").Tab}
|
|
113
118
|
*/
|
|
114
|
-
export function replaceAllTabs(newTabOpts
|
|
119
|
+
export function replaceAllTabs(newTabOpts: {
|
|
120
|
+
id: string;
|
|
121
|
+
documentPath?: string | null;
|
|
122
|
+
document: Record<string, unknown>;
|
|
123
|
+
frontmatter?: Record<string, unknown>;
|
|
124
|
+
sourceFormat?: string | null;
|
|
125
|
+
}) {
|
|
115
126
|
const oldIds = [...workspace.tabs.keys()];
|
|
116
127
|
const oldTabs = [...workspace.tabs.values()];
|
|
117
128
|
|
|
@@ -136,7 +147,7 @@ export function replaceAllTabs(newTabOpts) {
|
|
|
136
147
|
*
|
|
137
148
|
* @param {string} tabId
|
|
138
149
|
*/
|
|
139
|
-
export function activateTab(tabId) {
|
|
150
|
+
export function activateTab(tabId: string) {
|
|
140
151
|
if (workspace.tabs.has(tabId)) workspace.activeTabId = tabId;
|
|
141
152
|
}
|
|
142
153
|
|
|
@@ -148,7 +159,7 @@ export function activateTab(tabId) {
|
|
|
148
159
|
* @param {string} newId
|
|
149
160
|
* @param {string} newDocumentPath
|
|
150
161
|
*/
|
|
151
|
-
export function renameTab(oldId, newId, newDocumentPath) {
|
|
162
|
+
export function renameTab(oldId: string, newId: string, newDocumentPath: string) {
|
|
152
163
|
const tab = workspace.tabs.get(oldId);
|
|
153
164
|
if (!tab) return;
|
|
154
165
|
tab.id = newId;
|
package/src/panels/dnd.js
DELETED
|
@@ -1,426 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DnD registration functions — extracted from studio.js (Phase 4). Registers drag-and-drop behavior
|
|
3
|
-
* on layer rows, component cards, and element cards.
|
|
4
|
-
*/
|
|
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
|
-
|
|
19
|
-
import {
|
|
20
|
-
draggable,
|
|
21
|
-
dropTargetForElements,
|
|
22
|
-
monitorForElements,
|
|
23
|
-
} from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
|
24
|
-
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
|
|
25
|
-
import {
|
|
26
|
-
attachInstruction,
|
|
27
|
-
extractInstruction,
|
|
28
|
-
} from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item";
|
|
29
|
-
|
|
30
|
-
import {
|
|
31
|
-
leftPanel,
|
|
32
|
-
getNodeAtPath,
|
|
33
|
-
parentElementPath,
|
|
34
|
-
childIndex,
|
|
35
|
-
isAncestor,
|
|
36
|
-
renderOnly,
|
|
37
|
-
} from "../store.js";
|
|
38
|
-
import { transact, transactDoc, mutateMoveNode, mutateInsertNode } from "../tabs/transact.js";
|
|
39
|
-
import { activeTab } from "../workspace/workspace.js";
|
|
40
|
-
import { view } from "../view.js";
|
|
41
|
-
import { componentRegistry, computeRelativePath } from "../files/components.js";
|
|
42
|
-
import { renderComponentPreview } from "./stylebook-panel.js";
|
|
43
|
-
import { defaultDef, unsafeTags } from "./shared.js";
|
|
44
|
-
|
|
45
|
-
/** Register DnD on layer rows — called from left-panel.js after render */
|
|
46
|
-
export function registerLayersDnD() {
|
|
47
|
-
requestAnimationFrame(() => {
|
|
48
|
-
const container = /** @type {HTMLElement | null} */ (
|
|
49
|
-
leftPanel?.querySelector(".layers-container")
|
|
50
|
-
);
|
|
51
|
-
if (!container) return;
|
|
52
|
-
|
|
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
|
-
);
|
|
60
|
-
const rowDepth = parseInt(/** @type {string} */ (row.dataset.dndDepth)) || 0;
|
|
61
|
-
const isVoid = row.hasAttribute("data-dnd-void");
|
|
62
|
-
const isExpanded = row.hasAttribute("data-dnd-expanded");
|
|
63
|
-
|
|
64
|
-
const cleanup = combine(
|
|
65
|
-
draggable({
|
|
66
|
-
element: row,
|
|
67
|
-
canDrag(/** @type {DragCanDragArgs} */ { element: _el, input }) {
|
|
68
|
-
const target = /** @type {HTMLElement} */ (
|
|
69
|
-
document.elementFromPoint(input.clientX, input.clientY)
|
|
70
|
-
);
|
|
71
|
-
if (target?.closest(".layer-actions")) return false;
|
|
72
|
-
return true;
|
|
73
|
-
},
|
|
74
|
-
getInitialData() {
|
|
75
|
-
return { type: "tree-node", path: rowPath };
|
|
76
|
-
},
|
|
77
|
-
onDragStart() {
|
|
78
|
-
row.classList.add("dragging");
|
|
79
|
-
view.layerDragSourceHeight = row.offsetHeight;
|
|
80
|
-
if (isExpanded) {
|
|
81
|
-
hideDescendantRows(row, container);
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
onDrop() {
|
|
85
|
-
row.classList.remove("dragging");
|
|
86
|
-
if (isExpanded) {
|
|
87
|
-
renderOnly("leftPanel");
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
}),
|
|
91
|
-
dropTargetForElements({
|
|
92
|
-
element: row,
|
|
93
|
-
canDrop(/** @type {DragDropSourceArgs} */ { source }) {
|
|
94
|
-
const srcPath = /** @type {JxPath | undefined} */ (source.data.path);
|
|
95
|
-
if (srcPath && isAncestor(srcPath, rowPath)) return false;
|
|
96
|
-
return true;
|
|
97
|
-
},
|
|
98
|
-
getData(/** @type {any} */ { input, element }) {
|
|
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
|
-
)
|
|
111
|
-
);
|
|
112
|
-
},
|
|
113
|
-
onDragEnter(/** @type {DragSelfArgs} */ { self }) {
|
|
114
|
-
showLayerDropGap(row, self.data, container);
|
|
115
|
-
},
|
|
116
|
-
onDrag(/** @type {DragSelfArgs} */ { self }) {
|
|
117
|
-
showLayerDropGap(row, self.data, container);
|
|
118
|
-
},
|
|
119
|
-
onDragLeave() {
|
|
120
|
-
clearLayerDropGap(container);
|
|
121
|
-
},
|
|
122
|
-
onDrop() {
|
|
123
|
-
clearLayerDropGap(container);
|
|
124
|
-
},
|
|
125
|
-
}),
|
|
126
|
-
);
|
|
127
|
-
view.dndCleanups.push(cleanup);
|
|
128
|
-
},
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
// Global monitor
|
|
132
|
-
const monitorCleanup = monitorForElements({
|
|
133
|
-
onDrop(/** @type {DragMonitorDropArgs} */ { source, location }) {
|
|
134
|
-
clearLayerDropGap(container);
|
|
135
|
-
const target = location.current.dropTargets[0];
|
|
136
|
-
if (!target) return;
|
|
137
|
-
const instruction = extractInstruction(target.data);
|
|
138
|
-
if (!instruction || instruction.type === "instruction-blocked") return;
|
|
139
|
-
const srcData = source.data;
|
|
140
|
-
const targetPath = /** @type {JxPath} */ (target.data.path);
|
|
141
|
-
|
|
142
|
-
// If the source had children, persist collapse at the new location
|
|
143
|
-
const srcRow = srcData.type === "tree-node" && source.element;
|
|
144
|
-
const wasExpanded = srcRow && srcRow.hasAttribute("data-dnd-expanded");
|
|
145
|
-
|
|
146
|
-
applyDropInstruction(instruction, srcData, targetPath);
|
|
147
|
-
|
|
148
|
-
if (wasExpanded) {
|
|
149
|
-
const tab = activeTab.value;
|
|
150
|
-
const newPath = tab?.session.selection;
|
|
151
|
-
if (newPath) {
|
|
152
|
-
const collapsed = view._layersCollapsed || (view._layersCollapsed = new Set());
|
|
153
|
-
collapsed.add(newPath.join("/"));
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
});
|
|
158
|
-
view.dndCleanups.push(monitorCleanup);
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/** Register DnD on component rows — called from renderLeftPanel when tab=components */
|
|
163
|
-
export function registerComponentsDnD() {
|
|
164
|
-
requestAnimationFrame(() => {
|
|
165
|
-
const container = /** @type {HTMLElement | null} */ (
|
|
166
|
-
leftPanel?.querySelector(".components-section")
|
|
167
|
-
);
|
|
168
|
-
if (!container) return;
|
|
169
|
-
|
|
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;
|
|
179
|
-
|
|
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
|
-
}
|
|
188
|
-
|
|
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) => [
|
|
195
|
-
p.name,
|
|
196
|
-
p.default !== undefined ? p.default : "",
|
|
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
|
-
});
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/** Register DnD on element (HTML block) rows */
|
|
214
|
-
export function registerElementsDnD() {
|
|
215
|
-
requestAnimationFrame(() => {
|
|
216
|
-
const container = /** @type {HTMLElement | null} */ (leftPanel?.querySelector(".panel-body"));
|
|
217
|
-
if (!container) return;
|
|
218
|
-
/** @type {NodeListOf<HTMLElement>} */ (container.querySelectorAll("[data-block-tag]")).forEach(
|
|
219
|
-
(row) => {
|
|
220
|
-
const tag = /** @type {string} */ (row.dataset.blockTag);
|
|
221
|
-
const preview = row.querySelector(".element-card-preview");
|
|
222
|
-
if (preview && !preview.firstChild) {
|
|
223
|
-
const el = document.createElement(unsafeTags.has(tag) ? "span" : tag);
|
|
224
|
-
el.textContent = tag;
|
|
225
|
-
preview.appendChild(el);
|
|
226
|
-
}
|
|
227
|
-
const def = defaultDef(tag);
|
|
228
|
-
const cleanup = draggable({
|
|
229
|
-
element: row,
|
|
230
|
-
getInitialData() {
|
|
231
|
-
return { type: "block", fragment: structuredClone(def) };
|
|
232
|
-
},
|
|
233
|
-
});
|
|
234
|
-
view.dndCleanups.push(cleanup);
|
|
235
|
-
},
|
|
236
|
-
);
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Hide descendant rows of the dragged item so it appears collapsed during drag.
|
|
242
|
-
*
|
|
243
|
-
* @param {HTMLElement} parentRow
|
|
244
|
-
* @param {HTMLElement} container
|
|
245
|
-
*/
|
|
246
|
-
function hideDescendantRows(parentRow, container) {
|
|
247
|
-
const prefix = parentRow.dataset.path + "/";
|
|
248
|
-
const rows = container.querySelectorAll(".layers-tree .layer-row");
|
|
249
|
-
for (const r of rows) {
|
|
250
|
-
if (/** @type {HTMLElement} */ (r).dataset.path?.startsWith(prefix)) {
|
|
251
|
-
/** @type {HTMLElement} */ (r).style.display = "none";
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* @param {HTMLElement} rowEl
|
|
258
|
-
* @param {Record<string, unknown>} data
|
|
259
|
-
* @param {HTMLElement} container
|
|
260
|
-
*/
|
|
261
|
-
export function showLayerDropGap(rowEl, data, container) {
|
|
262
|
-
const instruction = extractInstruction(data);
|
|
263
|
-
|
|
264
|
-
// Clear previous drop-target highlight
|
|
265
|
-
if (view._currentDropTargetRow && view._currentDropTargetRow !== rowEl) {
|
|
266
|
-
view._currentDropTargetRow.classList.remove("drop-target");
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (!instruction || instruction.type === "instruction-blocked") {
|
|
270
|
-
clearLayerDropGap(container);
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (instruction.type === "make-child") {
|
|
275
|
-
clearLayerDropGap(container);
|
|
276
|
-
rowEl.classList.add("drop-target");
|
|
277
|
-
view._currentDropTargetRow = rowEl;
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
rowEl.classList.remove("drop-target");
|
|
282
|
-
view._currentDropTargetRow = rowEl;
|
|
283
|
-
|
|
284
|
-
// Shift rows to create gap
|
|
285
|
-
const rows = Array.from(container.querySelectorAll(".layers-tree .layer-row"));
|
|
286
|
-
const targetIdx = rows.indexOf(rowEl);
|
|
287
|
-
const gap = view.layerDragSourceHeight;
|
|
288
|
-
|
|
289
|
-
for (let i = 0; i < rows.length; i++) {
|
|
290
|
-
if (/** @type {HTMLElement} */ (rows[i]).classList.contains("dragging")) continue;
|
|
291
|
-
if (instruction.type === "reorder-above") {
|
|
292
|
-
/** @type {HTMLElement} */ (rows[i]).style.transform =
|
|
293
|
-
i >= targetIdx ? `translateY(${gap}px)` : "";
|
|
294
|
-
} else {
|
|
295
|
-
/** @type {HTMLElement} */ (rows[i]).style.transform =
|
|
296
|
-
i > targetIdx ? `translateY(${gap}px)` : "";
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/** @param {HTMLElement} container */
|
|
302
|
-
export function clearLayerDropGap(container) {
|
|
303
|
-
if (view._currentDropTargetRow) {
|
|
304
|
-
view._currentDropTargetRow.classList.remove("drop-target");
|
|
305
|
-
view._currentDropTargetRow = null;
|
|
306
|
-
}
|
|
307
|
-
const rows = container.querySelectorAll(".layers-tree .layer-row");
|
|
308
|
-
for (const r of rows) /** @type {HTMLElement} */ (r).style.transform = "";
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Apply a DnD instruction to the state
|
|
313
|
-
*
|
|
314
|
-
* @param {{ type: string }} instruction
|
|
315
|
-
* @param {Record<string, unknown>} srcData
|
|
316
|
-
* @param {JxPath} targetPath
|
|
317
|
-
*/
|
|
318
|
-
export function applyDropInstruction(instruction, srcData, targetPath) {
|
|
319
|
-
const doc = /** @type {JxMutableNode} */ (activeTab.value?.doc.document);
|
|
320
|
-
if (srcData.type === "tree-node") {
|
|
321
|
-
const fromPath = /** @type {JxPath} */ (srcData.path);
|
|
322
|
-
const targetParent = /** @type {JxPath} */ (parentElementPath(targetPath));
|
|
323
|
-
const targetIdx = /** @type {number} */ (childIndex(targetPath));
|
|
324
|
-
|
|
325
|
-
switch (instruction.type) {
|
|
326
|
-
case "reorder-above":
|
|
327
|
-
transactDoc(activeTab.value, (t) => mutateMoveNode(t, fromPath, targetParent, targetIdx));
|
|
328
|
-
break;
|
|
329
|
-
case "reorder-below":
|
|
330
|
-
transactDoc(activeTab.value, (t) =>
|
|
331
|
-
mutateMoveNode(t, fromPath, targetParent, targetIdx + 1),
|
|
332
|
-
);
|
|
333
|
-
break;
|
|
334
|
-
case "make-child": {
|
|
335
|
-
const target = getNodeAtPath(doc, targetPath);
|
|
336
|
-
const len = target?.children?.length || 0;
|
|
337
|
-
transactDoc(activeTab.value, (t) => mutateMoveNode(t, fromPath, targetPath, len));
|
|
338
|
-
break;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
} else if (srcData.type === "block") {
|
|
342
|
-
const targetParent = /** @type {JxPath} */ (parentElementPath(targetPath));
|
|
343
|
-
const targetIdx = /** @type {number} */ (childIndex(targetPath));
|
|
344
|
-
|
|
345
|
-
switch (instruction.type) {
|
|
346
|
-
case "reorder-above":
|
|
347
|
-
transactDoc(activeTab.value, (t) =>
|
|
348
|
-
mutateInsertNode(
|
|
349
|
-
t,
|
|
350
|
-
targetParent,
|
|
351
|
-
targetIdx,
|
|
352
|
-
structuredClone(/** @type {JxMutableNode} */ (srcData.fragment)),
|
|
353
|
-
),
|
|
354
|
-
);
|
|
355
|
-
break;
|
|
356
|
-
case "reorder-below":
|
|
357
|
-
transactDoc(activeTab.value, (t) =>
|
|
358
|
-
mutateInsertNode(
|
|
359
|
-
t,
|
|
360
|
-
targetParent,
|
|
361
|
-
targetIdx + 1,
|
|
362
|
-
structuredClone(/** @type {JxMutableNode} */ (srcData.fragment)),
|
|
363
|
-
),
|
|
364
|
-
);
|
|
365
|
-
break;
|
|
366
|
-
case "make-child": {
|
|
367
|
-
const target = getNodeAtPath(doc, targetPath);
|
|
368
|
-
const len = target?.children?.length || 0;
|
|
369
|
-
transactDoc(activeTab.value, (t) =>
|
|
370
|
-
mutateInsertNode(
|
|
371
|
-
t,
|
|
372
|
-
targetPath,
|
|
373
|
-
len,
|
|
374
|
-
structuredClone(/** @type {JxMutableNode} */ (srcData.fragment)),
|
|
375
|
-
),
|
|
376
|
-
);
|
|
377
|
-
break;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// Auto-import to $elements if the dropped block is a custom component
|
|
382
|
-
const fragment = /** @type {JxMutableNode | undefined} */ (srcData.fragment);
|
|
383
|
-
const tag = fragment?.tagName;
|
|
384
|
-
if (tag && tag.includes("-")) {
|
|
385
|
-
const comp = componentRegistry.find(
|
|
386
|
-
(/** @type {import("../files/components.js").ComponentEntry} */ c) => c.tagName === tag,
|
|
387
|
-
);
|
|
388
|
-
if (comp) {
|
|
389
|
-
const tab = activeTab.value;
|
|
390
|
-
const elements = tab?.doc.document?.$elements || [];
|
|
391
|
-
if (comp.source === "npm") {
|
|
392
|
-
const specifier = comp.modulePath ? `${comp.package}/${comp.modulePath}` : comp.package;
|
|
393
|
-
if (!specifier) return;
|
|
394
|
-
const alreadyImported = elements.some(
|
|
395
|
-
(/** @type {JxMutableNode | string | { $ref: string }} */ e) =>
|
|
396
|
-
e === specifier || e === comp.package,
|
|
397
|
-
);
|
|
398
|
-
if (!alreadyImported) {
|
|
399
|
-
transact(activeTab.value, (/** @type {JxMutableNode} */ doc) => {
|
|
400
|
-
if (!doc.$elements) doc.$elements = [];
|
|
401
|
-
doc.$elements.push(specifier);
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
} else {
|
|
405
|
-
const alreadyImported = elements.some(
|
|
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
|
-
},
|
|
414
|
-
);
|
|
415
|
-
if (!alreadyImported) {
|
|
416
|
-
const relPath = computeRelativePath(tab?.documentPath, comp.path);
|
|
417
|
-
transact(activeTab.value, (/** @type {JxMutableNode} */ doc) => {
|
|
418
|
-
if (!doc.$elements) doc.$elements = [];
|
|
419
|
-
doc.$elements.push({ $ref: relPath });
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|