@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/left-panel.js
CHANGED
|
@@ -7,40 +7,94 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { html, render as litRender, nothing } from "lit-html";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
updateFrontmatter,
|
|
17
|
-
} from "../store.js";
|
|
18
|
-
import { ensureLitState } from "./shared.js";
|
|
10
|
+
import { leftPanel, updateSession } from "../store.js";
|
|
11
|
+
import { effect, effectScope } from "../reactivity.js";
|
|
12
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
13
|
+
import { view } from "../view.js";
|
|
14
|
+
import { transact, mutateUpdateFrontmatter } from "../tabs/transact.js";
|
|
15
|
+
|
|
19
16
|
import { renderLayersTemplate } from "./layers-panel.js";
|
|
20
17
|
import { renderStylebookLayersTemplate } from "./stylebook-layers-panel.js";
|
|
21
18
|
import { renderElementsTemplate } from "./elements-panel.js";
|
|
22
19
|
import { selectStylebookTag, stylebookMeta } from "./stylebook-panel.js";
|
|
23
20
|
|
|
24
|
-
/** @
|
|
21
|
+
/** @typedef {import("lit-html").TemplateResult} TemplateResult */
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {{
|
|
25
|
+
* getCanvasMode: () => string;
|
|
26
|
+
* setCanvasMode: (mode: string) => void;
|
|
27
|
+
* renderImportsTemplate: (...args: any[]) => TemplateResult;
|
|
28
|
+
* renderFilesTemplate: () => TemplateResult;
|
|
29
|
+
* renderSignalsTemplate: (...args: any[]) => TemplateResult;
|
|
30
|
+
* renderDataExplorerTemplate: (...args: any[]) => TemplateResult;
|
|
31
|
+
* renderHeadTemplate: (...args: any[]) => TemplateResult;
|
|
32
|
+
* renderGitPanel: (...args: any[]) => TemplateResult;
|
|
33
|
+
* renderCanvas: () => void;
|
|
34
|
+
* defCategory: (tag: string) => string;
|
|
35
|
+
* defBadgeLabel: (tag: string) => string;
|
|
36
|
+
* navigateToComponent: (path: string) => void;
|
|
37
|
+
* webdata: object;
|
|
38
|
+
* defaultDef: (tag: string) => object;
|
|
39
|
+
* registerLayersDnD: () => void;
|
|
40
|
+
* registerElementsDnD: () => void;
|
|
41
|
+
* registerComponentsDnD: () => void;
|
|
42
|
+
* setupTreeKeyboard: (tree: HTMLElement) => void;
|
|
43
|
+
* }} LeftPanelCtx
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/** @type {LeftPanelCtx | null} */
|
|
25
47
|
let _ctx = null;
|
|
26
48
|
|
|
49
|
+
/** @type {import("@vue/reactivity").EffectScope | null} */
|
|
50
|
+
let _scope = null;
|
|
51
|
+
|
|
52
|
+
let _rendering = false;
|
|
53
|
+
let _scheduled = false;
|
|
54
|
+
|
|
27
55
|
/**
|
|
28
56
|
* Mount the left panel orchestrator.
|
|
29
57
|
*
|
|
30
|
-
* @param {
|
|
58
|
+
* @param {LeftPanelCtx} ctx
|
|
31
59
|
*/
|
|
32
60
|
export function mount(ctx) {
|
|
33
61
|
_ctx = ctx;
|
|
62
|
+
_scope = effectScope();
|
|
63
|
+
_scope.run(() => {
|
|
64
|
+
effect(() => {
|
|
65
|
+
const tab = activeTab.value;
|
|
66
|
+
if (!tab) return;
|
|
67
|
+
// Track properties the left panel reads
|
|
68
|
+
void tab.doc.document;
|
|
69
|
+
void tab.doc.mode;
|
|
70
|
+
void tab.session.selection;
|
|
71
|
+
void tab.session.ui.settingsTab;
|
|
72
|
+
render();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
34
75
|
}
|
|
35
76
|
|
|
36
77
|
export function unmount() {
|
|
78
|
+
_scope?.stop();
|
|
79
|
+
_scope = null;
|
|
37
80
|
_ctx = null;
|
|
38
81
|
}
|
|
39
82
|
|
|
40
83
|
export function render() {
|
|
41
84
|
if (!_ctx) return;
|
|
85
|
+
if (_rendering) return;
|
|
86
|
+
if (!_scheduled) {
|
|
87
|
+
_scheduled = true;
|
|
88
|
+
queueMicrotask(_flush);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function _flush() {
|
|
93
|
+
_scheduled = false;
|
|
94
|
+
if (!_ctx) return;
|
|
95
|
+
if (_rendering) return;
|
|
96
|
+
_rendering = true;
|
|
42
97
|
try {
|
|
43
|
-
ensureLitState(leftPanel);
|
|
44
98
|
_render();
|
|
45
99
|
} catch (e) {
|
|
46
100
|
console.error("left-panel render error:", e);
|
|
@@ -52,90 +106,101 @@ export function render() {
|
|
|
52
106
|
} catch (e2) {
|
|
53
107
|
console.error("left-panel retry failed:", e2);
|
|
54
108
|
}
|
|
109
|
+
} finally {
|
|
110
|
+
_rendering = false;
|
|
55
111
|
}
|
|
56
112
|
}
|
|
57
113
|
|
|
58
114
|
function _render() {
|
|
59
|
-
const
|
|
60
|
-
const
|
|
115
|
+
const ctx = /** @type {LeftPanelCtx} */ (_ctx);
|
|
116
|
+
const aTab = activeTab.value;
|
|
117
|
+
if (!aTab) return;
|
|
118
|
+
const S = /** @type {any} */ ({
|
|
119
|
+
ui: aTab.session.ui,
|
|
120
|
+
document: aTab.doc.document,
|
|
121
|
+
mode: aTab.doc.mode,
|
|
122
|
+
selection: aTab.session.selection,
|
|
123
|
+
});
|
|
124
|
+
const tab = view.leftTab;
|
|
61
125
|
|
|
62
|
-
/** @type {
|
|
126
|
+
/** @type {TemplateResult | typeof nothing} */
|
|
63
127
|
let content;
|
|
64
128
|
if (tab === "layers")
|
|
65
129
|
content =
|
|
66
|
-
|
|
130
|
+
ctx.getCanvasMode() === "settings"
|
|
67
131
|
? renderStylebookLayersTemplate({
|
|
68
132
|
selectStylebookTag,
|
|
69
133
|
stylebookMeta,
|
|
70
134
|
})
|
|
71
135
|
: renderLayersTemplate({
|
|
72
|
-
navigateToComponent:
|
|
136
|
+
navigateToComponent: ctx.navigateToComponent,
|
|
73
137
|
rerender: render,
|
|
74
138
|
});
|
|
75
139
|
else if (tab === "imports")
|
|
76
|
-
content =
|
|
140
|
+
content = ctx.renderImportsTemplate({
|
|
77
141
|
renderLeftPanel: render,
|
|
78
142
|
documentPath: S.documentPath,
|
|
79
143
|
documentElements: S.document.$elements || [],
|
|
80
|
-
applyMutation: (/** @type {
|
|
81
|
-
|
|
144
|
+
applyMutation: (/** @type {(doc: object) => void} */ fn) => {
|
|
145
|
+
transact(activeTab.value, fn);
|
|
82
146
|
},
|
|
83
147
|
});
|
|
84
|
-
else if (tab === "files") content =
|
|
148
|
+
else if (tab === "files") content = ctx.renderFilesTemplate();
|
|
85
149
|
else if (tab === "blocks")
|
|
86
150
|
content = renderElementsTemplate({
|
|
87
|
-
webdata:
|
|
88
|
-
defaultDef:
|
|
151
|
+
webdata: ctx.webdata,
|
|
152
|
+
defaultDef: ctx.defaultDef,
|
|
89
153
|
rerender: render,
|
|
90
154
|
});
|
|
91
155
|
else if (tab === "state")
|
|
92
|
-
content =
|
|
156
|
+
content = ctx.renderSignalsTemplate(S, {
|
|
93
157
|
renderLeftPanel: render,
|
|
94
|
-
renderCanvas:
|
|
158
|
+
renderCanvas: ctx.renderCanvas,
|
|
95
159
|
updateSession,
|
|
96
160
|
});
|
|
97
161
|
else if (tab === "data")
|
|
98
|
-
content =
|
|
99
|
-
renderCanvas:
|
|
162
|
+
content = ctx.renderDataExplorerTemplate(S.document.state, S.canvas?.scope ?? null, {
|
|
163
|
+
renderCanvas: ctx.renderCanvas,
|
|
100
164
|
renderLeftPanel: render,
|
|
101
|
-
defCategory:
|
|
102
|
-
defBadgeLabel:
|
|
165
|
+
defCategory: ctx.defCategory,
|
|
166
|
+
defBadgeLabel: ctx.defBadgeLabel,
|
|
103
167
|
});
|
|
104
168
|
else if (tab === "head") {
|
|
105
169
|
const isContent = S.mode === "content";
|
|
106
170
|
const fm = S.content?.frontmatter ?? {};
|
|
107
171
|
const headDoc = isContent ? { ...S.document, title: fm.title, $head: fm.$head } : S.document;
|
|
108
|
-
content =
|
|
172
|
+
content = ctx.renderHeadTemplate({
|
|
109
173
|
document: headDoc,
|
|
110
174
|
applyMutation: isContent
|
|
111
|
-
? (/** @type {
|
|
175
|
+
? (/** @type {(doc: object) => void} */ fn) => {
|
|
176
|
+
const tab = activeTab.value;
|
|
177
|
+
const fm = /** @type {Record<string, any>} */ (tab.doc.content?.frontmatter ?? {});
|
|
112
178
|
const tmp = { title: fm.title, $head: fm.$head ? [...fm.$head] : undefined };
|
|
113
179
|
fn(tmp);
|
|
114
|
-
|
|
115
|
-
|
|
180
|
+
if (tmp.title !== fm.title)
|
|
181
|
+
mutateUpdateFrontmatter(tab, "title", /** @type {any} */ (tmp.title));
|
|
116
182
|
const newHead = tmp.$head && tmp.$head.length > 0 ? tmp.$head : undefined;
|
|
117
|
-
|
|
118
|
-
update(s);
|
|
183
|
+
mutateUpdateFrontmatter(tab, "$head", newHead);
|
|
119
184
|
}
|
|
120
|
-
: (/** @type {
|
|
121
|
-
|
|
185
|
+
: (/** @type {(doc: object) => void} */ fn) => {
|
|
186
|
+
transact(activeTab.value, fn);
|
|
122
187
|
},
|
|
123
188
|
renderLeftPanel: render,
|
|
124
189
|
});
|
|
125
|
-
} else if (tab === "git") content =
|
|
190
|
+
} else if (tab === "git") content = ctx.renderGitPanel(S, ctx);
|
|
126
191
|
else content = nothing;
|
|
127
192
|
|
|
128
|
-
litRender(html`<div class="panel-body">${content}</div>`,
|
|
193
|
+
litRender(html`<div class="panel-body">${content}</div>`, leftPanel);
|
|
129
194
|
|
|
130
195
|
// Post-render side effects
|
|
131
|
-
if (tab === "layers" &&
|
|
196
|
+
if (tab === "layers" && ctx.getCanvasMode() !== "settings") ctx.registerLayersDnD();
|
|
132
197
|
else if (tab === "imports") {
|
|
133
198
|
/* no post-render DnD needed */
|
|
134
199
|
} else if (tab === "blocks") {
|
|
135
|
-
|
|
136
|
-
|
|
200
|
+
ctx.registerElementsDnD();
|
|
201
|
+
ctx.registerComponentsDnD();
|
|
137
202
|
} else if (tab === "files") {
|
|
138
|
-
const tree = /** @type {
|
|
139
|
-
if (tree)
|
|
203
|
+
const tree = /** @type {HTMLElement | null} */ (leftPanel.querySelector(".file-tree"));
|
|
204
|
+
if (tree) ctx.setupTreeKeyboard(tree);
|
|
140
205
|
}
|
|
141
206
|
}
|
package/src/panels/overlays.js
CHANGED
|
@@ -4,42 +4,83 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { html, render as litRender, nothing } from "lit-html";
|
|
7
|
-
import {
|
|
7
|
+
import { canvasPanels, pathsEqual } from "../store.js";
|
|
8
|
+
import { effect, effectScope } from "../reactivity.js";
|
|
9
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
8
10
|
import { view } from "../view.js";
|
|
9
|
-
import {
|
|
10
|
-
findCanvasElement,
|
|
11
|
-
getActivePanel,
|
|
12
|
-
overlayBoxDescriptor,
|
|
13
|
-
} from "../canvas/canvas-helpers.js";
|
|
11
|
+
import { findCanvasElement, getActivePanel, effectiveZoom } from "../canvas/canvas-helpers.js";
|
|
14
12
|
import { layoutElements } from "../canvas/canvas-live-render.js";
|
|
15
13
|
|
|
16
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {{
|
|
16
|
+
* cls: string;
|
|
17
|
+
* top: string;
|
|
18
|
+
* left: string;
|
|
19
|
+
* width: string;
|
|
20
|
+
* height: string;
|
|
21
|
+
* border?: string;
|
|
22
|
+
* isLayout?: boolean;
|
|
23
|
+
* }} OverlayBox
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {{
|
|
28
|
+
* getCanvasMode: () => string;
|
|
29
|
+
* isEditing: () => boolean;
|
|
30
|
+
* renderBlockActionBar: () => void;
|
|
31
|
+
* }} OverlaysCtx
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/** @type {OverlaysCtx | null} */
|
|
17
35
|
let _ctx = null;
|
|
18
36
|
|
|
19
|
-
/** @type {(
|
|
20
|
-
let
|
|
37
|
+
/** @type {import("@vue/reactivity").EffectScope | null} */
|
|
38
|
+
let _scope = null;
|
|
39
|
+
|
|
40
|
+
let _scheduled = false;
|
|
21
41
|
|
|
22
42
|
/**
|
|
23
43
|
* Mount the overlays panel.
|
|
24
44
|
*
|
|
25
|
-
* @param {
|
|
45
|
+
* @param {OverlaysCtx} ctx
|
|
26
46
|
*/
|
|
27
47
|
export function mount(ctx) {
|
|
28
48
|
_ctx = ctx;
|
|
29
|
-
|
|
30
|
-
|
|
49
|
+
_scope = effectScope();
|
|
50
|
+
_scope.run(() => {
|
|
51
|
+
effect(() => {
|
|
52
|
+
const tab = activeTab.value;
|
|
53
|
+
if (!tab) return;
|
|
54
|
+
// Track selection, hover, and mode
|
|
55
|
+
void tab.session.selection;
|
|
56
|
+
void tab.session.hover;
|
|
57
|
+
void tab.doc.mode;
|
|
58
|
+
render();
|
|
59
|
+
});
|
|
31
60
|
});
|
|
32
61
|
}
|
|
33
62
|
|
|
34
63
|
export function unmount() {
|
|
35
|
-
|
|
36
|
-
|
|
64
|
+
_scope?.stop();
|
|
65
|
+
_scope = null;
|
|
37
66
|
_ctx = null;
|
|
38
67
|
}
|
|
39
68
|
|
|
40
69
|
export function render() {
|
|
41
70
|
if (!_ctx) return;
|
|
42
|
-
|
|
71
|
+
if (!_scheduled) {
|
|
72
|
+
_scheduled = true;
|
|
73
|
+
queueMicrotask(_flush);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function _flush() {
|
|
78
|
+
_scheduled = false;
|
|
79
|
+
if (!_ctx) return;
|
|
80
|
+
const tab = activeTab.value;
|
|
81
|
+
if (!tab) return;
|
|
82
|
+
const { selection, hover } = tab.session;
|
|
83
|
+
const { stylebookTab } = tab.session.ui;
|
|
43
84
|
const canvasMode = _ctx.getCanvasMode();
|
|
44
85
|
|
|
45
86
|
if (canvasMode !== "design" && canvasMode !== "edit" && canvasMode !== "settings") {
|
|
@@ -55,7 +96,7 @@ export function render() {
|
|
|
55
96
|
}
|
|
56
97
|
|
|
57
98
|
if (canvasMode === "settings") {
|
|
58
|
-
const enable =
|
|
99
|
+
const enable = stylebookTab === "elements";
|
|
59
100
|
for (const p of canvasPanels) {
|
|
60
101
|
p.overlayClk.style.pointerEvents = enable ? "" : "none";
|
|
61
102
|
}
|
|
@@ -72,33 +113,46 @@ export function render() {
|
|
|
72
113
|
}
|
|
73
114
|
|
|
74
115
|
for (const p of canvasPanels) {
|
|
75
|
-
/**
|
|
76
|
-
* @type {{
|
|
77
|
-
* cls: string;
|
|
78
|
-
* top: string;
|
|
79
|
-
* left: string;
|
|
80
|
-
* width: string;
|
|
81
|
-
* height: string;
|
|
82
|
-
* border?: string;
|
|
83
|
-
* }[]}
|
|
84
|
-
*/
|
|
116
|
+
/** @type {OverlayBox[]} */
|
|
85
117
|
const boxes = [];
|
|
86
118
|
|
|
87
|
-
|
|
88
|
-
|
|
119
|
+
// Batch layout reads: read viewport geometry once per panel
|
|
120
|
+
const vpRect = p.viewport.getBoundingClientRect();
|
|
121
|
+
const scrollTop = p.viewport.scrollTop;
|
|
122
|
+
const scrollLeft = p.viewport.scrollLeft;
|
|
123
|
+
const scale = effectiveZoom();
|
|
124
|
+
|
|
125
|
+
if (hover && !pathsEqual(hover, selection)) {
|
|
126
|
+
const el = findCanvasElement(hover, p.canvas);
|
|
89
127
|
if (el) {
|
|
90
|
-
const
|
|
91
|
-
|
|
128
|
+
const elRect = el.getBoundingClientRect();
|
|
129
|
+
/** @type {OverlayBox} */
|
|
130
|
+
const desc = {
|
|
131
|
+
cls: "overlay-box overlay-hover",
|
|
132
|
+
top: `${(elRect.top - vpRect.top + scrollTop) / scale}px`,
|
|
133
|
+
left: `${(elRect.left - vpRect.left + scrollLeft) / scale}px`,
|
|
134
|
+
width: `${elRect.width / scale}px`,
|
|
135
|
+
height: `${elRect.height / scale}px`,
|
|
136
|
+
};
|
|
137
|
+
if (layoutElements.has(el)) desc.isLayout = true;
|
|
92
138
|
boxes.push(desc);
|
|
93
139
|
}
|
|
94
140
|
}
|
|
95
141
|
|
|
96
|
-
if (
|
|
97
|
-
const el = findCanvasElement(
|
|
142
|
+
if (selection && p === getActivePanel()) {
|
|
143
|
+
const el = findCanvasElement(selection, p.canvas);
|
|
98
144
|
if (el) {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
145
|
+
const elRect = el.getBoundingClientRect();
|
|
146
|
+
/** @type {OverlayBox} */
|
|
147
|
+
const desc = {
|
|
148
|
+
cls: "overlay-box overlay-selection",
|
|
149
|
+
top: `${(elRect.top - vpRect.top + scrollTop) / scale}px`,
|
|
150
|
+
left: `${(elRect.left - vpRect.left + scrollLeft) / scale}px`,
|
|
151
|
+
width: `${elRect.width / scale}px`,
|
|
152
|
+
height: `${elRect.height / scale}px`,
|
|
153
|
+
};
|
|
154
|
+
if (view.componentInlineEdit || _ctx.isEditing()) desc.border = "none";
|
|
155
|
+
if (layoutElements.has(el)) desc.isLayout = true;
|
|
102
156
|
boxes.push(desc);
|
|
103
157
|
}
|
|
104
158
|
}
|
|
@@ -109,16 +163,12 @@ export function render() {
|
|
|
109
163
|
${boxes.map(
|
|
110
164
|
(b) => html`
|
|
111
165
|
<div
|
|
112
|
-
class="${b.cls}${
|
|
166
|
+
class="${b.cls}${b.isLayout ? " overlay-layout" : ""}"
|
|
113
167
|
style="top:${b.top};left:${b.left};width:${b.width};height:${b.height}${b.border
|
|
114
168
|
? `;border:${b.border}`
|
|
115
169
|
: ""}"
|
|
116
170
|
>
|
|
117
|
-
${
|
|
118
|
-
/** @type {any} */ (b).isLayout
|
|
119
|
-
? html`<span class="overlay-layout-badge">Layout</span>`
|
|
120
|
-
: nothing
|
|
121
|
-
}
|
|
171
|
+
${b.isLayout ? html`<span class="overlay-layout-badge">Layout</span>` : nothing}
|
|
122
172
|
</div>
|
|
123
173
|
`,
|
|
124
174
|
)}
|
|
@@ -5,18 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
-
update,
|
|
9
8
|
updateUi,
|
|
10
|
-
selectNode,
|
|
11
|
-
hoverNode,
|
|
12
9
|
elToPath,
|
|
13
10
|
pathsEqual,
|
|
14
|
-
insertNode,
|
|
15
11
|
parentElementPath,
|
|
16
12
|
childIndex,
|
|
17
13
|
getNodeAtPath,
|
|
18
14
|
renderOnly,
|
|
19
15
|
} from "../store.js";
|
|
16
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
20
17
|
import { view } from "../view.js";
|
|
21
18
|
import { stopEditing, isEditing, isEditableBlock } from "../editor/inline-edit.js";
|
|
22
19
|
import { showContextMenu } from "../editor/context-menu.js";
|
|
@@ -32,8 +29,6 @@ let _ctx = null;
|
|
|
32
29
|
* Initialize the panel events module.
|
|
33
30
|
*
|
|
34
31
|
* @param {{
|
|
35
|
-
* getState: () => any;
|
|
36
|
-
* setState: (s: any) => void;
|
|
37
32
|
* getCanvasMode: () => string;
|
|
38
33
|
* enterInlineEdit: (el: any, path: any) => void;
|
|
39
34
|
* navigateToComponent: (path: any) => void;
|
|
@@ -79,7 +74,7 @@ export function registerPanelEvents(panel) {
|
|
|
79
74
|
stopEditing();
|
|
80
75
|
}
|
|
81
76
|
|
|
82
|
-
const
|
|
77
|
+
const tab = activeTab.value;
|
|
83
78
|
const canvasMode = _ctx.getCanvasMode();
|
|
84
79
|
|
|
85
80
|
const elements = withPanelPointerEvents(() =>
|
|
@@ -91,7 +86,7 @@ export function registerPanelEvents(panel) {
|
|
|
91
86
|
// Layout element clicked — show layout info instead of selecting in page doc
|
|
92
87
|
if (layoutElements.has(el)) {
|
|
93
88
|
view.layoutSelection = { el, layoutPath: activeLayoutPath };
|
|
94
|
-
|
|
89
|
+
activeTab.value.session.selection = null;
|
|
95
90
|
renderOnly("rightPanel");
|
|
96
91
|
return;
|
|
97
92
|
}
|
|
@@ -99,34 +94,35 @@ export function registerPanelEvents(panel) {
|
|
|
99
94
|
|
|
100
95
|
const originalPath = elToPath.get(el);
|
|
101
96
|
if (originalPath) {
|
|
102
|
-
let path = bubbleInlinePath(
|
|
97
|
+
let path = bubbleInlinePath(tab?.doc.document, originalPath);
|
|
103
98
|
const newMedia = mediaName === "base" ? null : (mediaName ?? null);
|
|
104
|
-
const withMedia = { ...S, ui: { ...S.ui, activeMedia: newMedia } };
|
|
105
99
|
|
|
106
100
|
const resolvedEl = path === originalPath ? el : findCanvasElement(path, canvas) || el;
|
|
107
101
|
|
|
108
102
|
if (
|
|
109
|
-
pathsEqual(path,
|
|
103
|
+
pathsEqual(path, tab?.session.selection) &&
|
|
110
104
|
isEditableBlock(resolvedEl) &&
|
|
111
|
-
(canvasMode === "edit" ||
|
|
105
|
+
(canvasMode === "edit" || tab?.doc.mode === "content")
|
|
112
106
|
) {
|
|
113
|
-
|
|
107
|
+
activeTab.value.session.ui.activeMedia = newMedia;
|
|
114
108
|
_ctx.enterInlineEdit(resolvedEl, path);
|
|
115
109
|
return;
|
|
116
110
|
}
|
|
117
111
|
|
|
118
|
-
if (canvasMode === "design" &&
|
|
112
|
+
if (canvasMode === "design" && tab?.doc.mode !== "content") {
|
|
119
113
|
updateUi("pendingInlineEdit", { path, mediaName });
|
|
120
|
-
|
|
114
|
+
activeTab.value.session.ui.activeMedia = newMedia;
|
|
115
|
+
activeTab.value.session.selection = path;
|
|
121
116
|
return;
|
|
122
117
|
}
|
|
123
118
|
|
|
124
|
-
|
|
119
|
+
activeTab.value.session.ui.activeMedia = newMedia;
|
|
120
|
+
activeTab.value.session.selection = path;
|
|
125
121
|
return;
|
|
126
122
|
}
|
|
127
123
|
}
|
|
128
124
|
}
|
|
129
|
-
|
|
125
|
+
activeTab.value.session.selection = null;
|
|
130
126
|
},
|
|
131
127
|
opts,
|
|
132
128
|
);
|
|
@@ -148,7 +144,7 @@ export function registerPanelEvents(panel) {
|
|
|
148
144
|
const canvasMode = _ctx.getCanvasMode();
|
|
149
145
|
if (canvasMode !== "edit" && canvasMode !== "design") return;
|
|
150
146
|
|
|
151
|
-
const
|
|
147
|
+
const tab = activeTab.value;
|
|
152
148
|
const elements = withPanelPointerEvents(() =>
|
|
153
149
|
document.elementsFromPoint(e.clientX, e.clientY),
|
|
154
150
|
);
|
|
@@ -157,12 +153,12 @@ export function registerPanelEvents(panel) {
|
|
|
157
153
|
if (canvas.contains(el) && el !== canvas) {
|
|
158
154
|
const originalPath = elToPath.get(el);
|
|
159
155
|
if (originalPath) {
|
|
160
|
-
const path = bubbleInlinePath(
|
|
156
|
+
const path = bubbleInlinePath(tab?.doc.document, originalPath);
|
|
161
157
|
const resolvedEl = path === originalPath ? el : findCanvasElement(path, canvas) || el;
|
|
162
158
|
if (isEditableBlock(resolvedEl)) {
|
|
163
159
|
const newMedia = mediaName === "base" ? null : (mediaName ?? null);
|
|
164
|
-
|
|
165
|
-
|
|
160
|
+
activeTab.value.session.ui.activeMedia = newMedia;
|
|
161
|
+
activeTab.value.session.selection = path;
|
|
166
162
|
_ctx.enterInlineEdit(resolvedEl, path);
|
|
167
163
|
return;
|
|
168
164
|
}
|
|
@@ -187,7 +183,7 @@ export function registerPanelEvents(panel) {
|
|
|
187
183
|
)
|
|
188
184
|
return;
|
|
189
185
|
}
|
|
190
|
-
const
|
|
186
|
+
const tab = activeTab.value;
|
|
191
187
|
const elements = withPanelPointerEvents(() =>
|
|
192
188
|
document.elementsFromPoint(e.clientX, e.clientY),
|
|
193
189
|
);
|
|
@@ -195,8 +191,8 @@ export function registerPanelEvents(panel) {
|
|
|
195
191
|
if (canvas.contains(el) && el !== canvas) {
|
|
196
192
|
let path = elToPath.get(el);
|
|
197
193
|
if (path) {
|
|
198
|
-
path = bubbleInlinePath(
|
|
199
|
-
showContextMenu(e, path,
|
|
194
|
+
path = bubbleInlinePath(tab?.doc.document, path);
|
|
195
|
+
showContextMenu(e, path, { onEditComponent: _ctx.navigateToComponent });
|
|
200
196
|
return;
|
|
201
197
|
}
|
|
202
198
|
}
|
|
@@ -220,19 +216,19 @@ export function registerPanelEvents(panel) {
|
|
|
220
216
|
)
|
|
221
217
|
return;
|
|
222
218
|
}
|
|
223
|
-
|
|
219
|
+
const tab = activeTab.value;
|
|
224
220
|
const el = withPanelPointerEvents(() => document.elementFromPoint(e.clientX, e.clientY));
|
|
225
221
|
if (el && canvas.contains(el) && el !== canvas) {
|
|
226
222
|
let path = elToPath.get(el);
|
|
227
223
|
if (path) {
|
|
228
|
-
path = bubbleInlinePath(
|
|
229
|
-
if (!pathsEqual(path,
|
|
230
|
-
|
|
224
|
+
path = bubbleInlinePath(tab?.doc.document, path);
|
|
225
|
+
if (!pathsEqual(path, tab?.session.hover)) {
|
|
226
|
+
activeTab.value.session.hover = path;
|
|
231
227
|
renderOnly("overlays");
|
|
232
228
|
}
|
|
233
229
|
}
|
|
234
|
-
} else if (
|
|
235
|
-
|
|
230
|
+
} else if (tab?.session.hover) {
|
|
231
|
+
activeTab.value.session.hover = null;
|
|
236
232
|
renderOnly("overlays");
|
|
237
233
|
}
|
|
238
234
|
},
|
|
@@ -242,9 +238,8 @@ export function registerPanelEvents(panel) {
|
|
|
242
238
|
overlayClk.addEventListener(
|
|
243
239
|
"mouseleave",
|
|
244
240
|
() => {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
_ctx.setState(hoverNode(S, null));
|
|
241
|
+
if (activeTab.value?.session.hover) {
|
|
242
|
+
activeTab.value.session.hover = null;
|
|
248
243
|
renderOnly("overlays");
|
|
249
244
|
}
|
|
250
245
|
},
|
|
@@ -252,14 +247,10 @@ export function registerPanelEvents(panel) {
|
|
|
252
247
|
);
|
|
253
248
|
|
|
254
249
|
insertionHelper.mount({
|
|
255
|
-
getState: _ctx.getState,
|
|
256
|
-
update,
|
|
257
250
|
getCanvasMode: _ctx.getCanvasMode,
|
|
258
251
|
withPanelPointerEvents,
|
|
259
252
|
effectiveZoom: effectiveZoom,
|
|
260
253
|
defaultDef,
|
|
261
|
-
insertNode,
|
|
262
|
-
selectNode,
|
|
263
254
|
parentElementPath,
|
|
264
255
|
childIndex,
|
|
265
256
|
getNodeAtPath,
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* DOM from Jx node trees as a fallback when runtime rendering fails.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { elToPath } from "../store.js";
|
|
7
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
7
8
|
import { applyCanvasStyle } from "../utils/canvas-media.js";
|
|
8
9
|
import { resolveDefaultForCanvas } from "../panels/signals-panel.js";
|
|
9
10
|
|
|
@@ -32,7 +33,10 @@ export function renderCanvasNode(node, path, parent, activeBreakpoints, featureT
|
|
|
32
33
|
if (typeof node.textContent === "string") {
|
|
33
34
|
el.textContent = node.textContent;
|
|
34
35
|
} else if (typeof node.textContent === "object" && node.textContent?.$ref) {
|
|
35
|
-
const resolved = resolveDefaultForCanvas(
|
|
36
|
+
const resolved = resolveDefaultForCanvas(
|
|
37
|
+
node.textContent,
|
|
38
|
+
activeTab.value?.doc.document?.state,
|
|
39
|
+
);
|
|
36
40
|
el.textContent = resolved;
|
|
37
41
|
el.style.opacity = "0.7";
|
|
38
42
|
el.style.fontStyle = "italic";
|
|
@@ -48,7 +52,7 @@ export function renderCanvasNode(node, path, parent, activeBreakpoints, featureT
|
|
|
48
52
|
for (const [attr, val] of Object.entries(node.attributes)) {
|
|
49
53
|
try {
|
|
50
54
|
if (typeof val === "object" && val?.$ref) {
|
|
51
|
-
const resolved = resolveDefaultForCanvas(val,
|
|
55
|
+
const resolved = resolveDefaultForCanvas(val, activeTab.value?.doc.document?.state);
|
|
52
56
|
el.setAttribute(attr, resolved);
|
|
53
57
|
} else {
|
|
54
58
|
el.setAttribute(attr, val);
|