@jxsuite/studio 0.11.0 → 0.14.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 +6517 -5870
- package/dist/studio.js.map +57 -51
- 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 +28 -2
- package/src/canvas/canvas-render.js +170 -20
- package/src/canvas/canvas-utils.js +22 -25
- package/src/editor/component-inline-edit.js +55 -44
- package/src/editor/content-inline-edit.js +47 -50
- package/src/editor/context-menu.js +78 -53
- package/src/editor/convert-to-component.js +11 -14
- package/src/editor/insertion-helper.js +8 -11
- package/src/editor/shortcuts.js +88 -64
- package/src/files/components.js +15 -4
- package/src/files/file-ops.js +57 -108
- package/src/files/files.js +81 -28
- package/src/panels/activity-bar.js +31 -7
- package/src/panels/block-action-bar.js +104 -80
- package/src/panels/canvas-dnd.js +4 -10
- package/src/panels/dnd.js +77 -35
- package/src/panels/editors.js +17 -26
- package/src/panels/elements-panel.js +17 -11
- package/src/panels/events-panel.js +44 -39
- package/src/panels/git-panel.js +47 -4
- package/src/panels/layers-panel.js +25 -21
- package/src/panels/left-panel.js +109 -44
- package/src/panels/overlays.js +91 -41
- package/src/panels/panel-events.js +28 -37
- package/src/panels/preview-render.js +7 -3
- package/src/panels/properties-panel.js +179 -104
- package/src/panels/pseudo-preview.js +9 -8
- 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 +26 -19
- package/src/panels/style-inputs.js +5 -4
- package/src/panels/style-panel.js +128 -105
- package/src/panels/style-utils.js +8 -6
- package/src/panels/stylebook-layers-panel.js +5 -5
- package/src/panels/stylebook-panel.js +27 -31
- package/src/panels/tab-strip.js +124 -0
- package/src/panels/toolbar.js +40 -10
- 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 +2 -459
- package/src/store.js +61 -219
- package/src/studio.js +163 -300
- package/src/tabs/tab.js +168 -0
- package/src/tabs/transact.js +406 -0
- package/src/ui/color-selector.js +4 -5
- package/src/view.js +3 -0
- package/src/workspace/workspace.js +90 -0
package/src/panels/editors.js
CHANGED
|
@@ -7,33 +7,25 @@ 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 { 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
|
|
|
23
16
|
function getFunctionBody(/** @type {any} */ editing) {
|
|
24
|
-
const
|
|
17
|
+
const document = activeTab.value?.doc.document;
|
|
25
18
|
if (editing.type === "def") {
|
|
26
|
-
return
|
|
19
|
+
return document?.state?.[editing.defName]?.body || "";
|
|
27
20
|
} else if (editing.type === "event") {
|
|
28
|
-
const node = getNodeAtPath(
|
|
21
|
+
const node = getNodeAtPath(document, editing.path);
|
|
29
22
|
return node?.[editing.eventKey]?.body || "";
|
|
30
23
|
}
|
|
31
24
|
return "";
|
|
32
25
|
}
|
|
33
26
|
|
|
34
27
|
export function renderFunctionEditor() {
|
|
35
|
-
const
|
|
36
|
-
const editing = S.ui.editingFunction;
|
|
28
|
+
const editing = activeTab.value?.session.ui.editingFunction;
|
|
37
29
|
|
|
38
30
|
// If editor already exists and matches current target, just sync value
|
|
39
31
|
if (view.functionEditor && view.functionEditor._editingTarget === JSON.stringify(editing)) {
|
|
@@ -83,7 +75,7 @@ export function renderFunctionEditor() {
|
|
|
83
75
|
);
|
|
84
76
|
|
|
85
77
|
const body = getFunctionBody(editing);
|
|
86
|
-
const args = getFunctionArgs(editing,
|
|
78
|
+
const args = getFunctionArgs(editing, activeTab.value?.doc.document);
|
|
87
79
|
|
|
88
80
|
view.functionEditor = monaco.editor.create(/** @type {any} */ (editorContainer), {
|
|
89
81
|
value: body,
|
|
@@ -126,15 +118,15 @@ export function renderFunctionEditor() {
|
|
|
126
118
|
|
|
127
119
|
clearTimeout(syncDebounce);
|
|
128
120
|
syncDebounce = setTimeout(() => {
|
|
129
|
-
const S = getState();
|
|
130
121
|
const newBody = view.functionEditor.getValue();
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
122
|
+
const ed = /** @type {any} */ (editing);
|
|
123
|
+
if (ed.type === "def") {
|
|
124
|
+
transactDoc(activeTab.value, (t) => mutateUpdateDef(t, ed.defName, { body: newBody }));
|
|
125
|
+
} else if (ed.type === "event") {
|
|
126
|
+
const node = getNodeAtPath(activeTab.value?.doc.document, ed.path);
|
|
127
|
+
const current = node?.[ed.eventKey] || {};
|
|
128
|
+
transactDoc(activeTab.value, (t) =>
|
|
129
|
+
mutateUpdateProperty(t, ed.path, ed.eventKey, {
|
|
138
130
|
...current,
|
|
139
131
|
$prototype: "Function",
|
|
140
132
|
body: newBody,
|
|
@@ -164,8 +156,7 @@ export function registerFunctionCompletions() {
|
|
|
164
156
|
monaco.languages.registerCompletionItemProvider("javascript", {
|
|
165
157
|
triggerCharacters: ["."],
|
|
166
158
|
provideCompletionItems(model, position) {
|
|
167
|
-
const
|
|
168
|
-
const defs = S?.document?.state || {};
|
|
159
|
+
const defs = activeTab.value?.doc.document?.state || {};
|
|
169
160
|
const word = model.getWordUntilPosition(position);
|
|
170
161
|
const range = {
|
|
171
162
|
startLineNumber: position.lineNumber,
|
|
@@ -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 {
|
|
4
|
+
import { 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";
|
|
@@ -11,7 +13,7 @@ import { componentRegistry } from "../files/components.js";
|
|
|
11
13
|
* @returns {import("lit-html").TemplateResult}
|
|
12
14
|
*/
|
|
13
15
|
export function renderElementsTemplate(ctx) {
|
|
14
|
-
const
|
|
16
|
+
const tab = activeTab.value;
|
|
15
17
|
|
|
16
18
|
const categories = Object.entries(ctx.webdata.elements).map(
|
|
17
19
|
(/** @type {any} */ [category, elements]) => {
|
|
@@ -36,11 +38,13 @@ export function renderElementsTemplate(ctx) {
|
|
|
36
38
|
class="element-card"
|
|
37
39
|
data-block-tag=${tag}
|
|
38
40
|
@click=${() => {
|
|
39
|
-
const
|
|
40
|
-
const parentPath =
|
|
41
|
-
const parent = getNodeAtPath(
|
|
41
|
+
const t = activeTab.value;
|
|
42
|
+
const parentPath = t?.session.selection || [];
|
|
43
|
+
const parent = getNodeAtPath(t?.doc.document, parentPath);
|
|
42
44
|
const idx = parent?.children ? parent.children.length : 0;
|
|
43
|
-
|
|
45
|
+
transactDoc(t, (tr) =>
|
|
46
|
+
mutateInsertNode(tr, parentPath, idx, structuredClone(def)),
|
|
47
|
+
);
|
|
44
48
|
}}
|
|
45
49
|
>
|
|
46
50
|
<div class="element-card-preview"></div>
|
|
@@ -53,7 +57,7 @@ export function renderElementsTemplate(ctx) {
|
|
|
53
57
|
},
|
|
54
58
|
);
|
|
55
59
|
|
|
56
|
-
const effectiveEls = getEffectiveElements(
|
|
60
|
+
const effectiveEls = getEffectiveElements(tab?.doc.document?.$elements);
|
|
57
61
|
/** @type {Set<string>} */
|
|
58
62
|
const enabledTags = new Set();
|
|
59
63
|
for (const entry of effectiveEls) {
|
|
@@ -101,9 +105,9 @@ export function renderElementsTemplate(ctx) {
|
|
|
101
105
|
? `${comp.package}: <${comp.tagName}>`
|
|
102
106
|
: comp.path}
|
|
103
107
|
@click=${() => {
|
|
104
|
-
const
|
|
105
|
-
const parentPath =
|
|
106
|
-
const parent = getNodeAtPath(
|
|
108
|
+
const t = activeTab.value;
|
|
109
|
+
const parentPath = t?.session.selection || [];
|
|
110
|
+
const parent = getNodeAtPath(t?.doc.document, parentPath);
|
|
107
111
|
const idx = parent?.children ? parent.children.length : 0;
|
|
108
112
|
const instanceDef = {
|
|
109
113
|
tagName: comp.tagName,
|
|
@@ -114,7 +118,9 @@ export function renderElementsTemplate(ctx) {
|
|
|
114
118
|
]),
|
|
115
119
|
),
|
|
116
120
|
};
|
|
117
|
-
|
|
121
|
+
transactDoc(t, (tr) =>
|
|
122
|
+
mutateInsertNode(tr, parentPath, idx, structuredClone(instanceDef)),
|
|
123
|
+
);
|
|
118
124
|
}}
|
|
119
125
|
>
|
|
120
126
|
<div class="element-card-preview">
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { getNodeAtPath
|
|
1
|
+
import { getNodeAtPath } from "../store.js";
|
|
2
2
|
import { html, nothing } from "lit-html";
|
|
3
3
|
import { live } from "lit-html/directives/live.js";
|
|
4
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
5
|
+
import { transactDoc, mutateUpdateProperty } from "../tabs/transact.js";
|
|
4
6
|
|
|
5
7
|
export const EVENT_NAMES = [
|
|
6
8
|
"onclick",
|
|
@@ -15,17 +17,17 @@ export const EVENT_NAMES = [
|
|
|
15
17
|
"onmouseleave",
|
|
16
18
|
];
|
|
17
19
|
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
if (!
|
|
25
|
-
const node = getNodeAtPath(
|
|
20
|
+
/** @param {{ isCustomElementDoc: () => boolean }} helpers */
|
|
21
|
+
export function eventsSidebarTemplate(helpers) {
|
|
22
|
+
const { isCustomElementDoc } = helpers;
|
|
23
|
+
const tab = activeTab.value;
|
|
24
|
+
const selection = tab?.session.selection;
|
|
25
|
+
const document = tab?.doc.document;
|
|
26
|
+
if (!selection) return html`<div class="empty-state">Select an element to edit events</div>`;
|
|
27
|
+
const node = getNodeAtPath(document, selection);
|
|
26
28
|
if (!node) return html`<div class="empty-state">Node not found</div>`;
|
|
27
29
|
|
|
28
|
-
const defs =
|
|
30
|
+
const defs = document.state || {};
|
|
29
31
|
const functionDefs = Object.entries(defs).filter(
|
|
30
32
|
([, d]) => d.$prototype === "Function" || d.$handler,
|
|
31
33
|
);
|
|
@@ -87,9 +89,10 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
87
89
|
@change=${(/** @type {any} */ e) => {
|
|
88
90
|
const newKey = e.target.value;
|
|
89
91
|
if (newKey && newKey !== evKey) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
transactDoc(activeTab.value, (t) => {
|
|
93
|
+
mutateUpdateProperty(t, selection, evKey, undefined);
|
|
94
|
+
mutateUpdateProperty(t, selection, newKey, node[evKey]);
|
|
95
|
+
});
|
|
93
96
|
}
|
|
94
97
|
}}
|
|
95
98
|
>
|
|
@@ -103,8 +106,8 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
103
106
|
.value=${live(isInline ? "inline" : "ref")}
|
|
104
107
|
@change=${(/** @type {any} */ e) => {
|
|
105
108
|
if (e.target.value === "inline") {
|
|
106
|
-
|
|
107
|
-
|
|
109
|
+
transactDoc(activeTab.value, (t) =>
|
|
110
|
+
mutateUpdateProperty(t, selection, evKey, {
|
|
108
111
|
$prototype: "Function",
|
|
109
112
|
body: "",
|
|
110
113
|
parameters: [],
|
|
@@ -112,10 +115,10 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
112
115
|
);
|
|
113
116
|
} else {
|
|
114
117
|
const firstFn = functionDefs[0];
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
transactDoc(activeTab.value, (t) =>
|
|
119
|
+
mutateUpdateProperty(
|
|
120
|
+
t,
|
|
121
|
+
selection,
|
|
119
122
|
evKey,
|
|
120
123
|
firstFn ? { $ref: `#/state/${firstFn[0]}` } : { $ref: "" },
|
|
121
124
|
),
|
|
@@ -129,7 +132,10 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
129
132
|
<sp-action-button
|
|
130
133
|
size="xs"
|
|
131
134
|
quiet
|
|
132
|
-
@click=${() =>
|
|
135
|
+
@click=${() =>
|
|
136
|
+
transactDoc(activeTab.value, (t) =>
|
|
137
|
+
mutateUpdateProperty(t, selection, evKey, undefined),
|
|
138
|
+
)}
|
|
133
139
|
>
|
|
134
140
|
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
135
141
|
</sp-action-button>
|
|
@@ -144,8 +150,8 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
144
150
|
placeholder="// handler body"
|
|
145
151
|
.value=${live(evVal.body || "")}
|
|
146
152
|
@input=${(/** @type {any} */ e) => {
|
|
147
|
-
|
|
148
|
-
|
|
153
|
+
transactDoc(activeTab.value, (t) =>
|
|
154
|
+
mutateUpdateProperty(t, selection, evKey, {
|
|
149
155
|
$prototype: "Function",
|
|
150
156
|
body: e.target.value,
|
|
151
157
|
parameters: evVal.parameters || [],
|
|
@@ -159,18 +165,11 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
159
165
|
quiet
|
|
160
166
|
title="Open in editor"
|
|
161
167
|
@click=${() => {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
editingFunction: {
|
|
167
|
-
type: "event",
|
|
168
|
-
path: S.selection,
|
|
169
|
-
eventKey: evKey,
|
|
170
|
-
},
|
|
171
|
-
},
|
|
168
|
+
tab.session.ui.editingFunction = {
|
|
169
|
+
type: "event",
|
|
170
|
+
path: selection,
|
|
171
|
+
eventKey: evKey,
|
|
172
172
|
};
|
|
173
|
-
renderCanvas();
|
|
174
173
|
}}
|
|
175
174
|
>
|
|
176
175
|
<sp-icon-code slot="icon"></sp-icon-code>
|
|
@@ -184,9 +183,13 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
184
183
|
.value=${live(evVal.$ref || "__none__")}
|
|
185
184
|
@change=${(/** @type {any} */ e) => {
|
|
186
185
|
if (e.target.value && e.target.value !== "__none__") {
|
|
187
|
-
|
|
186
|
+
transactDoc(activeTab.value, (t) =>
|
|
187
|
+
mutateUpdateProperty(t, selection, evKey, { $ref: e.target.value }),
|
|
188
|
+
);
|
|
188
189
|
} else {
|
|
189
|
-
|
|
190
|
+
transactDoc(activeTab.value, (t) =>
|
|
191
|
+
mutateUpdateProperty(t, selection, evKey, undefined),
|
|
192
|
+
);
|
|
190
193
|
}
|
|
191
194
|
}}
|
|
192
195
|
>
|
|
@@ -212,12 +215,14 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
212
215
|
}
|
|
213
216
|
}
|
|
214
217
|
if (functionDefs.length > 0) {
|
|
215
|
-
|
|
216
|
-
|
|
218
|
+
transactDoc(activeTab.value, (t) =>
|
|
219
|
+
mutateUpdateProperty(t, selection, evName, {
|
|
220
|
+
$ref: `#/state/${functionDefs[0][0]}`,
|
|
221
|
+
}),
|
|
217
222
|
);
|
|
218
223
|
} else {
|
|
219
|
-
|
|
220
|
-
|
|
224
|
+
transactDoc(activeTab.value, (t) =>
|
|
225
|
+
mutateUpdateProperty(t, selection, evName, {
|
|
221
226
|
$prototype: "Function",
|
|
222
227
|
body: "",
|
|
223
228
|
parameters: [],
|
package/src/panels/git-panel.js
CHANGED
|
@@ -7,6 +7,7 @@ import { html, nothing } from "lit-html";
|
|
|
7
7
|
import { live } from "lit-html/directives/live.js";
|
|
8
8
|
import { getPlatform } from "../platform.js";
|
|
9
9
|
import { updateUi, renderOnly } from "../store.js";
|
|
10
|
+
import { view } from "../view.js";
|
|
10
11
|
|
|
11
12
|
async function refreshGitStatus() {
|
|
12
13
|
const plat = getPlatform();
|
|
@@ -44,8 +45,11 @@ async function gitAction(action, body) {
|
|
|
44
45
|
|
|
45
46
|
let _pollTimer = /** @type {any} */ (null);
|
|
46
47
|
|
|
47
|
-
/**
|
|
48
|
-
|
|
48
|
+
/**
|
|
49
|
+
* @param {any} S
|
|
50
|
+
* @param {any} ctx
|
|
51
|
+
*/
|
|
52
|
+
export function renderGitPanel(S, ctx) {
|
|
49
53
|
const status = S.ui.gitStatus;
|
|
50
54
|
const branches = S.ui.gitBranches;
|
|
51
55
|
const loading = S.ui.gitLoading;
|
|
@@ -57,7 +61,7 @@ export function renderGitPanel(S) {
|
|
|
57
61
|
|
|
58
62
|
if (!_pollTimer) {
|
|
59
63
|
_pollTimer = setInterval(() => {
|
|
60
|
-
if (
|
|
64
|
+
if (view.leftTab === "git" && !S.ui.gitLoading) refreshGitStatus();
|
|
61
65
|
}, 30000);
|
|
62
66
|
}
|
|
63
67
|
|
|
@@ -156,9 +160,48 @@ export function renderGitPanel(S) {
|
|
|
156
160
|
const parts = file.path.split("/");
|
|
157
161
|
const name = parts.pop();
|
|
158
162
|
const dir = parts.join("/");
|
|
163
|
+
|
|
164
|
+
const onFileClick = async () => {
|
|
165
|
+
// Only support diff for modified/added files
|
|
166
|
+
if (file.status !== "M" && file.status !== "A") return;
|
|
167
|
+
if (!file.path.endsWith(".md") && !file.path.endsWith(".json")) return;
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
const plat = getPlatform();
|
|
171
|
+
updateUi("gitLoading", true);
|
|
172
|
+
const originalContent = await plat.gitShow({ path: file.path, ref: "HEAD" });
|
|
173
|
+
|
|
174
|
+
// Determine if it's markdown or JSON
|
|
175
|
+
const isMarkdown = file.path.endsWith(".md");
|
|
176
|
+
|
|
177
|
+
// For now, we'll need to pass the original content to the canvas
|
|
178
|
+
// This requires state management which we'll add to studio.js
|
|
179
|
+
updateUi("gitDiffState", {
|
|
180
|
+
filePath: file.path,
|
|
181
|
+
originalContent,
|
|
182
|
+
isMarkdown,
|
|
183
|
+
fileStatus: file.status,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Trigger diff mode via context
|
|
187
|
+
if (ctx?.setCanvasMode) {
|
|
188
|
+
ctx.setCanvasMode("git-diff");
|
|
189
|
+
}
|
|
190
|
+
} catch (/** @type {any} */ e) {
|
|
191
|
+
updateUi("gitError", `Failed to load diff: ${e.message}`);
|
|
192
|
+
} finally {
|
|
193
|
+
updateUi("gitLoading", false);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
159
197
|
return html`
|
|
160
198
|
<div class="git-file-row">
|
|
161
|
-
<span
|
|
199
|
+
<span
|
|
200
|
+
class="git-file-info"
|
|
201
|
+
style="cursor: pointer; flex: 1;"
|
|
202
|
+
title="Click to view diff"
|
|
203
|
+
@click=${onFileClick}
|
|
204
|
+
>
|
|
162
205
|
<span class="git-file-name" title=${file.path}>${name}</span>
|
|
163
206
|
${dir ? html`<span class="git-file-dir">${dir}</span>` : nothing}
|
|
164
207
|
</span>
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
import { html, nothing } from "lit-html";
|
|
7
7
|
import {
|
|
8
|
-
getState,
|
|
9
|
-
update,
|
|
10
8
|
flattenTree,
|
|
11
9
|
getNodeAtPath,
|
|
12
10
|
pathKey,
|
|
@@ -14,11 +12,10 @@ import {
|
|
|
14
12
|
parentElementPath,
|
|
15
13
|
childIndex,
|
|
16
14
|
nodeLabel,
|
|
17
|
-
selectNode,
|
|
18
|
-
moveNode,
|
|
19
|
-
removeNode,
|
|
20
15
|
VOID_ELEMENTS,
|
|
21
16
|
} from "../store.js";
|
|
17
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
18
|
+
import { transactDoc, mutateMoveNode, mutateRemoveNode } from "../tabs/transact.js";
|
|
22
19
|
import { view } from "../view.js";
|
|
23
20
|
import { isInlineElement } from "../editor/inline-edit.js";
|
|
24
21
|
import { showContextMenu } from "../editor/context-menu.js";
|
|
@@ -28,13 +25,13 @@ import { showContextMenu } from "../editor/context-menu.js";
|
|
|
28
25
|
* @returns {import("lit-html").TemplateResult}
|
|
29
26
|
*/
|
|
30
27
|
export function renderLayersTemplate(ctx) {
|
|
31
|
-
const
|
|
28
|
+
const tab = activeTab.value;
|
|
32
29
|
|
|
33
30
|
for (const fn of view.dndCleanups) fn();
|
|
34
31
|
view.dndCleanups = [];
|
|
35
32
|
|
|
36
|
-
const rows = flattenTree(
|
|
37
|
-
const collapsed =
|
|
33
|
+
const rows = flattenTree(tab?.doc.document);
|
|
34
|
+
const collapsed = view._layersCollapsed || (view._layersCollapsed = new Set());
|
|
38
35
|
|
|
39
36
|
/** @type {any[]} */
|
|
40
37
|
const layerRows = [];
|
|
@@ -49,7 +46,7 @@ export function renderLayersTemplate(ctx) {
|
|
|
49
46
|
}
|
|
50
47
|
if (hidden) continue;
|
|
51
48
|
|
|
52
|
-
if (
|
|
49
|
+
if (tab?.doc.mode === "content" && path.length === 0) continue;
|
|
53
50
|
|
|
54
51
|
if (nodeType === "text") {
|
|
55
52
|
const textPreview = String(node).length > 40 ? String(node).slice(0, 40) + "…" : String(node);
|
|
@@ -67,12 +64,12 @@ export function renderLayersTemplate(ctx) {
|
|
|
67
64
|
|
|
68
65
|
if (path.length >= 2 && nodeType === "element") {
|
|
69
66
|
const pPath = parentElementPath(path);
|
|
70
|
-
const parentNode = pPath ? getNodeAtPath(
|
|
67
|
+
const parentNode = pPath ? getNodeAtPath(tab?.doc.document, pPath) : null;
|
|
71
68
|
if (parentNode && isInlineElement(node, parentNode)) continue;
|
|
72
69
|
}
|
|
73
70
|
|
|
74
71
|
const key = pathKey(path);
|
|
75
|
-
const isSelected = pathsEqual(path,
|
|
72
|
+
const isSelected = pathsEqual(path, tab?.session.selection);
|
|
76
73
|
const hasChildren = Array.isArray(node.children) && node.children.length > 0;
|
|
77
74
|
const hasMapChildren =
|
|
78
75
|
node.children && typeof node.children === "object" && node.children.$prototype === "Array";
|
|
@@ -116,10 +113,10 @@ export function renderLayersTemplate(ctx) {
|
|
|
116
113
|
}
|
|
117
114
|
|
|
118
115
|
const isElement = nodeType === "element";
|
|
119
|
-
const isRoot =
|
|
116
|
+
const isRoot = tab?.doc.mode === "content" ? path.length === 0 : path.length < 2;
|
|
120
117
|
const idx = isElement ? /** @type {number} */ (childIndex(path)) : 0;
|
|
121
118
|
const parentPath = isElement && !isRoot ? /** @type {any} */ (parentElementPath(path)) : null;
|
|
122
|
-
const parentNode = parentPath ? getNodeAtPath(
|
|
119
|
+
const parentNode = parentPath ? getNodeAtPath(tab?.doc.document, parentPath) : null;
|
|
123
120
|
const siblingCount = parentNode?.children?.length || 0;
|
|
124
121
|
const canMoveUp = isElement && !isRoot && idx > 0;
|
|
125
122
|
const canMoveDown = isElement && !isRoot && idx < siblingCount - 1;
|
|
@@ -142,10 +139,11 @@ export function renderLayersTemplate(ctx) {
|
|
|
142
139
|
data-dnd-row=${isElement ? key : nothing}
|
|
143
140
|
data-dnd-depth=${isElement ? depth : nothing}
|
|
144
141
|
data-dnd-void=${isElement && isVoidEl ? "" : nothing}
|
|
145
|
-
|
|
142
|
+
data-dnd-expanded=${isElement && isExpandable && !collapsed.has(key) ? "" : nothing}
|
|
143
|
+
@click=${() => (activeTab.value.session.selection = path)}
|
|
146
144
|
@contextmenu=${isElement
|
|
147
145
|
? (/** @type {any} */ e) =>
|
|
148
|
-
showContextMenu(e, path,
|
|
146
|
+
showContextMenu(e, path, {
|
|
149
147
|
onEditComponent: ctx.navigateToComponent,
|
|
150
148
|
})
|
|
151
149
|
: nothing}
|
|
@@ -175,7 +173,9 @@ export function renderLayersTemplate(ctx) {
|
|
|
175
173
|
@click=${(/** @type {any} */ e) => {
|
|
176
174
|
e.stopPropagation();
|
|
177
175
|
/** @type {HTMLElement} */ (e.currentTarget).blur();
|
|
178
|
-
|
|
176
|
+
transactDoc(activeTab.value, (t) =>
|
|
177
|
+
mutateMoveNode(t, path, parentPath, idx - 1),
|
|
178
|
+
);
|
|
179
179
|
}}
|
|
180
180
|
>
|
|
181
181
|
<sp-icon-arrow-up slot="icon"></sp-icon-arrow-up>
|
|
@@ -189,7 +189,9 @@ export function renderLayersTemplate(ctx) {
|
|
|
189
189
|
@click=${(/** @type {any} */ e) => {
|
|
190
190
|
e.stopPropagation();
|
|
191
191
|
/** @type {HTMLElement} */ (e.currentTarget).blur();
|
|
192
|
-
|
|
192
|
+
transactDoc(activeTab.value, (t) =>
|
|
193
|
+
mutateMoveNode(t, path, parentPath, idx + 2),
|
|
194
|
+
);
|
|
193
195
|
}}
|
|
194
196
|
>
|
|
195
197
|
<sp-icon-arrow-down slot="icon"></sp-icon-arrow-down>
|
|
@@ -204,9 +206,9 @@ export function renderLayersTemplate(ctx) {
|
|
|
204
206
|
e.stopPropagation();
|
|
205
207
|
/** @type {HTMLElement} */ (e.currentTarget).blur();
|
|
206
208
|
const prevPath = [...parentPath, idx - 1];
|
|
207
|
-
const prev = getNodeAtPath(
|
|
209
|
+
const prev = getNodeAtPath(activeTab.value?.doc.document, prevPath);
|
|
208
210
|
const len = prev?.children?.length || 0;
|
|
209
|
-
|
|
211
|
+
transactDoc(activeTab.value, (t) => mutateMoveNode(t, path, prevPath, len));
|
|
210
212
|
}}
|
|
211
213
|
>
|
|
212
214
|
<sp-icon-arrow-right slot="icon"></sp-icon-arrow-right>
|
|
@@ -221,7 +223,9 @@ export function renderLayersTemplate(ctx) {
|
|
|
221
223
|
e.stopPropagation();
|
|
222
224
|
/** @type {HTMLElement} */ (e.currentTarget).blur();
|
|
223
225
|
const parentIdx = /** @type {number} */ (childIndex(parentPath));
|
|
224
|
-
|
|
226
|
+
transactDoc(activeTab.value, (t) =>
|
|
227
|
+
mutateMoveNode(t, path, grandparentPath, parentIdx + 1),
|
|
228
|
+
);
|
|
225
229
|
}}
|
|
226
230
|
>
|
|
227
231
|
<sp-icon-arrow-left slot="icon"></sp-icon-arrow-left>
|
|
@@ -234,7 +238,7 @@ export function renderLayersTemplate(ctx) {
|
|
|
234
238
|
title="Delete"
|
|
235
239
|
@click=${(/** @type {any} */ e) => {
|
|
236
240
|
e.stopPropagation();
|
|
237
|
-
|
|
241
|
+
transactDoc(activeTab.value, (t) => mutateRemoveNode(t, path));
|
|
238
242
|
}}
|
|
239
243
|
>
|
|
240
244
|
<sp-icon-close slot="icon"></sp-icon-close>
|