@jxsuite/studio 0.23.2 → 0.25.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 +2311 -718
- 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} +8 -6
- 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.ts +298 -0
- 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/utils/edit-display.js +0 -203
- /package/src/{reactivity.js → reactivity.ts} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/**
|
|
2
3
|
* Imports panel — context-aware import manager with cherry-pick component selection.
|
|
3
4
|
*
|
|
@@ -8,24 +9,23 @@
|
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
import { html, nothing } from "lit-html";
|
|
11
|
-
import { componentRegistry, computeRelativePath } from "../files/components
|
|
12
|
-
import { projectState } from "../store
|
|
13
|
-
import { updateSiteConfig } from "../site-context
|
|
14
|
-
import { getPlatform } from "../platform
|
|
15
|
-
import { showConfirmDialog } from "../ui/layers
|
|
12
|
+
import { componentRegistry, computeRelativePath } from "../files/components";
|
|
13
|
+
import { projectState } from "../store";
|
|
14
|
+
import { updateSiteConfig } from "../site-context";
|
|
15
|
+
import { getPlatform } from "../platform";
|
|
16
|
+
import { showConfirmDialog } from "../ui/layers";
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
import type { ComponentEntry } from "../files/components";
|
|
19
|
+
import type { JxElement, JxMutableNode } from "@jxsuite/schema/types";
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
interface ImportsContext {
|
|
22
|
+
renderLeftPanel: () => void;
|
|
23
|
+
documentPath: string | null;
|
|
24
|
+
documentElements: ElementsEntry[];
|
|
25
|
+
applyMutation: (fn: (doc: JxMutableNode) => void) => void;
|
|
26
|
+
}
|
|
20
27
|
|
|
21
|
-
|
|
22
|
-
* @typedef {{
|
|
23
|
-
* renderLeftPanel: () => void;
|
|
24
|
-
* documentPath: string | null;
|
|
25
|
-
* documentElements: ElementsEntry[];
|
|
26
|
-
* applyMutation: (fn: (doc: JxMutableNode) => void) => void;
|
|
27
|
-
* }} ImportsContext
|
|
28
|
-
*/
|
|
28
|
+
export type ElementsEntry = string | JxMutableNode | { $ref: string };
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Build the subpath specifier for a component: `<package>/<modulePath>`
|
|
@@ -33,7 +33,7 @@ import { showConfirmDialog } from "../ui/layers.js";
|
|
|
33
33
|
* @param {ComponentEntry} comp
|
|
34
34
|
* @returns {string}
|
|
35
35
|
*/
|
|
36
|
-
function componentSpecifier(comp) {
|
|
36
|
+
function componentSpecifier(comp: ComponentEntry) {
|
|
37
37
|
return `${comp.package}/${comp.modulePath}`;
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -45,7 +45,7 @@ function componentSpecifier(comp) {
|
|
|
45
45
|
* @param {ElementsEntry[]} elements
|
|
46
46
|
* @returns {boolean}
|
|
47
47
|
*/
|
|
48
|
-
function isComponentEnabled(comp, elements) {
|
|
48
|
+
function isComponentEnabled(comp: ComponentEntry, elements: ElementsEntry[]) {
|
|
49
49
|
if (!elements?.length) return false;
|
|
50
50
|
const specifier = componentSpecifier(comp);
|
|
51
51
|
for (const entry of elements) {
|
|
@@ -64,8 +64,7 @@ function isComponentEnabled(comp, elements) {
|
|
|
64
64
|
* @returns {Map<string, ComponentEntry[]>}
|
|
65
65
|
*/
|
|
66
66
|
function groupByPackage() {
|
|
67
|
-
|
|
68
|
-
const groups = new Map();
|
|
67
|
+
const groups: Map<string, ComponentEntry[]> = new Map();
|
|
69
68
|
for (const comp of componentRegistry) {
|
|
70
69
|
if (comp.source !== "npm" || !comp.package || !comp.modulePath) continue;
|
|
71
70
|
if (!groups.has(comp.package)) groups.set(comp.package, []);
|
|
@@ -83,7 +82,7 @@ export function renderImportsTemplate({
|
|
|
83
82
|
documentPath,
|
|
84
83
|
documentElements,
|
|
85
84
|
applyMutation,
|
|
86
|
-
}) {
|
|
85
|
+
}: ImportsContext) {
|
|
87
86
|
const isSiteLevel = documentPath?.endsWith("project.json");
|
|
88
87
|
|
|
89
88
|
if (isSiteLevel) {
|
|
@@ -101,12 +100,10 @@ export function renderImportsTemplate({
|
|
|
101
100
|
// ─── Site-level: Class Imports + Dependencies + Component Cherry-pick ─────────
|
|
102
101
|
|
|
103
102
|
/** @param {() => void} renderLeftPanel */
|
|
104
|
-
function renderSiteLevelImports(renderLeftPanel) {
|
|
103
|
+
function renderSiteLevelImports(renderLeftPanel: () => void) {
|
|
105
104
|
const siteImports = projectState?.projectConfig?.imports || {};
|
|
106
105
|
const entries = Object.entries(siteImports);
|
|
107
|
-
const siteElements =
|
|
108
|
-
projectState?.projectConfig?.$elements || []
|
|
109
|
-
);
|
|
106
|
+
const siteElements = (projectState?.projectConfig?.$elements || []) as ElementsEntry[];
|
|
110
107
|
|
|
111
108
|
const packageGroups = groupByPackage();
|
|
112
109
|
|
|
@@ -124,7 +121,7 @@ function renderSiteLevelImports(renderLeftPanel) {
|
|
|
124
121
|
${entries.map(
|
|
125
122
|
([name, path]) => html`
|
|
126
123
|
<div class="import-row">
|
|
127
|
-
<span class="import-name" title=${
|
|
124
|
+
<span class="import-name" title=${path as string}>${name}</span>
|
|
128
125
|
<span class="import-path">${path}</span>
|
|
129
126
|
<sp-action-button
|
|
130
127
|
quiet
|
|
@@ -152,14 +149,10 @@ function renderSiteLevelImports(renderLeftPanel) {
|
|
|
152
149
|
quiet
|
|
153
150
|
size="xs"
|
|
154
151
|
title="Add import"
|
|
155
|
-
@click=${async (
|
|
156
|
-
const form =
|
|
157
|
-
const nameField =
|
|
158
|
-
|
|
159
|
-
);
|
|
160
|
-
const pathField = /** @type {HTMLInputElement} */ (
|
|
161
|
-
form?.querySelector(".import-add-path")
|
|
162
|
-
);
|
|
152
|
+
@click=${async (e: Event) => {
|
|
153
|
+
const form = (e.target as HTMLElement).closest(".import-add-form");
|
|
154
|
+
const nameField = form?.querySelector(".import-add-name") as HTMLInputElement;
|
|
155
|
+
const pathField = form?.querySelector(".import-add-path") as HTMLInputElement;
|
|
163
156
|
const name = nameField?.value?.trim();
|
|
164
157
|
const path = pathField?.value?.trim();
|
|
165
158
|
if (!name || !path) return;
|
|
@@ -196,16 +189,15 @@ function renderSiteLevelImports(renderLeftPanel) {
|
|
|
196
189
|
await platform.removePackage(pkg);
|
|
197
190
|
// Also remove all cherry-picked elements for this package
|
|
198
191
|
const updatedElements = siteElements.filter(
|
|
199
|
-
(
|
|
200
|
-
typeof e !== "string" || !e.startsWith(pkg + "/"),
|
|
192
|
+
(e: ElementsEntry) => typeof e !== "string" || !e.startsWith(pkg + "/"),
|
|
201
193
|
);
|
|
202
194
|
const { loadComponentRegistry } = await import("../files/components.js");
|
|
203
195
|
await loadComponentRegistry();
|
|
204
196
|
await updateSiteConfig({
|
|
205
|
-
$elements:
|
|
197
|
+
$elements: updatedElements as (string | JxElement)[],
|
|
206
198
|
});
|
|
207
199
|
renderLeftPanel();
|
|
208
|
-
} catch (
|
|
200
|
+
} catch (e) {
|
|
209
201
|
console.error("Failed to remove package:", e);
|
|
210
202
|
}
|
|
211
203
|
}}
|
|
@@ -214,7 +206,7 @@ function renderSiteLevelImports(renderLeftPanel) {
|
|
|
214
206
|
</sp-action-button>
|
|
215
207
|
</div>
|
|
216
208
|
<div class="imports-list imports-component-list">
|
|
217
|
-
${comps.map((
|
|
209
|
+
${comps.map((comp: ComponentEntry) => {
|
|
218
210
|
const enabled = isComponentEnabled(comp, siteElements);
|
|
219
211
|
const specifier = componentSpecifier(comp);
|
|
220
212
|
return html`
|
|
@@ -222,19 +214,17 @@ function renderSiteLevelImports(renderLeftPanel) {
|
|
|
222
214
|
<sp-checkbox
|
|
223
215
|
size="s"
|
|
224
216
|
.checked=${enabled}
|
|
225
|
-
@change=${async (
|
|
226
|
-
let updated = [...siteElements];
|
|
217
|
+
@change=${async (e: Event) => {
|
|
218
|
+
let updated: ElementsEntry[] = [...siteElements];
|
|
227
219
|
// Remove legacy full-package import if present
|
|
228
|
-
updated = updated.filter((
|
|
229
|
-
if (
|
|
220
|
+
updated = updated.filter((el: ElementsEntry) => el !== pkg);
|
|
221
|
+
if ((e.target as HTMLInputElement).checked) {
|
|
230
222
|
if (!updated.includes(specifier)) updated.push(specifier);
|
|
231
223
|
} else {
|
|
232
|
-
updated = updated.filter(
|
|
233
|
-
(/** @type {ElementsEntry} */ el) => el !== specifier,
|
|
234
|
-
);
|
|
224
|
+
updated = updated.filter((el: ElementsEntry) => el !== specifier);
|
|
235
225
|
}
|
|
236
226
|
await updateSiteConfig({
|
|
237
|
-
$elements:
|
|
227
|
+
$elements: updated as (string | JxElement)[],
|
|
238
228
|
});
|
|
239
229
|
renderLeftPanel();
|
|
240
230
|
}}
|
|
@@ -259,18 +249,18 @@ function renderSiteLevelImports(renderLeftPanel) {
|
|
|
259
249
|
placeholder="Package name…"
|
|
260
250
|
size="s"
|
|
261
251
|
style="flex:1"
|
|
262
|
-
@keydown=${async (
|
|
252
|
+
@keydown=${async (e: KeyboardEvent) => {
|
|
263
253
|
if (e.key !== "Enter") return;
|
|
264
|
-
const name =
|
|
254
|
+
const name = (e.target as HTMLInputElement).value?.trim();
|
|
265
255
|
if (!name) return;
|
|
266
|
-
|
|
256
|
+
(e.target as HTMLInputElement).value = "";
|
|
267
257
|
try {
|
|
268
258
|
const platform = getPlatform();
|
|
269
259
|
await platform.addPackage(name);
|
|
270
260
|
const { loadComponentRegistry } = await import("../files/components.js");
|
|
271
261
|
await loadComponentRegistry();
|
|
272
262
|
renderLeftPanel();
|
|
273
|
-
} catch (
|
|
263
|
+
} catch (err) {
|
|
274
264
|
console.error("Failed to add package:", err);
|
|
275
265
|
}
|
|
276
266
|
}}
|
|
@@ -279,20 +269,20 @@ function renderSiteLevelImports(renderLeftPanel) {
|
|
|
279
269
|
quiet
|
|
280
270
|
size="xs"
|
|
281
271
|
title="Add package"
|
|
282
|
-
@click=${async (
|
|
283
|
-
const input =
|
|
272
|
+
@click=${async (e: Event) => {
|
|
273
|
+
const input = (e.target as HTMLElement)
|
|
284
274
|
.closest(".import-add-form")
|
|
285
275
|
?.querySelector("sp-textfield");
|
|
286
|
-
const name =
|
|
276
|
+
const name = (input as HTMLInputElement | null)?.value?.trim();
|
|
287
277
|
if (!name) return;
|
|
288
|
-
|
|
278
|
+
(input as HTMLInputElement).value = "";
|
|
289
279
|
try {
|
|
290
280
|
const platform = getPlatform();
|
|
291
281
|
await platform.addPackage(name);
|
|
292
282
|
const { loadComponentRegistry } = await import("../files/components.js");
|
|
293
283
|
await loadComponentRegistry();
|
|
294
284
|
renderLeftPanel();
|
|
295
|
-
} catch (
|
|
285
|
+
} catch (err) {
|
|
296
286
|
console.error("Failed to add package:", err);
|
|
297
287
|
}
|
|
298
288
|
}}
|
|
@@ -313,30 +303,26 @@ function renderDocumentLevelImports({
|
|
|
313
303
|
documentPath,
|
|
314
304
|
documentElements,
|
|
315
305
|
applyMutation,
|
|
316
|
-
}) {
|
|
306
|
+
}: ImportsContext) {
|
|
317
307
|
const refEntries = documentElements.filter(
|
|
318
|
-
(
|
|
319
|
-
);
|
|
320
|
-
const npmEntries = documentElements.filter(
|
|
321
|
-
(/** @type {ElementsEntry} */ e) => typeof e === "string",
|
|
308
|
+
(e: ElementsEntry) => e && typeof e === "object" && e.$ref,
|
|
322
309
|
);
|
|
310
|
+
const npmEntries = documentElements.filter((e: ElementsEntry) => typeof e === "string");
|
|
323
311
|
|
|
324
312
|
// Available JX components not yet imported
|
|
325
|
-
const importedRefs = new Set(
|
|
326
|
-
refEntries.map((/** @type {ElementsEntry} */ e) => /** @type {{ $ref: string }} */ (e).$ref),
|
|
327
|
-
);
|
|
313
|
+
const importedRefs = new Set(refEntries.map((e: ElementsEntry) => (e as { $ref: string }).$ref));
|
|
328
314
|
const availableComponents = componentRegistry.filter(
|
|
329
|
-
(
|
|
315
|
+
(c: ComponentEntry) =>
|
|
330
316
|
c.source !== "npm" && !importedRefs.has(`./${c.path}`) && !importedRefs.has(c.path),
|
|
331
317
|
);
|
|
332
318
|
|
|
333
319
|
const packageGroups = groupByPackage();
|
|
334
320
|
|
|
335
321
|
/** @param {string} ref */
|
|
336
|
-
const removeRef = (ref) => {
|
|
337
|
-
applyMutation((
|
|
322
|
+
const removeRef = (ref: string) => {
|
|
323
|
+
applyMutation((doc: JxMutableNode) => {
|
|
338
324
|
doc.$elements = (doc.$elements || []).filter(
|
|
339
|
-
(
|
|
325
|
+
(e: ElementsEntry) => !(e && typeof e === "object" && e.$ref === ref),
|
|
340
326
|
);
|
|
341
327
|
});
|
|
342
328
|
renderLeftPanel();
|
|
@@ -354,18 +340,16 @@ function renderDocumentLevelImports({
|
|
|
354
340
|
? html`
|
|
355
341
|
<div class="imports-list">
|
|
356
342
|
${refEntries.map(
|
|
357
|
-
(
|
|
343
|
+
(entry: ElementsEntry) => html`
|
|
358
344
|
<div class="import-row">
|
|
359
|
-
<span
|
|
360
|
-
|
|
361
|
-
title=${/** @type {{ $ref: string }} */ (entry).$ref}
|
|
362
|
-
>${/** @type {{ $ref: string }} */ (entry).$ref}</span
|
|
345
|
+
<span class="import-path" title=${(entry as { $ref: string }).$ref}
|
|
346
|
+
>${(entry as { $ref: string }).$ref}</span
|
|
363
347
|
>
|
|
364
348
|
<sp-action-button
|
|
365
349
|
quiet
|
|
366
350
|
size="xs"
|
|
367
351
|
title="Remove"
|
|
368
|
-
@click=${() => removeRef(
|
|
352
|
+
@click=${() => removeRef((entry as { $ref: string }).$ref)}
|
|
369
353
|
>
|
|
370
354
|
<sp-icon-close slot="icon" size="xs"></sp-icon-close>
|
|
371
355
|
</sp-action-button>
|
|
@@ -382,16 +366,14 @@ function renderDocumentLevelImports({
|
|
|
382
366
|
size="s"
|
|
383
367
|
label="Add component…"
|
|
384
368
|
class="import-picker"
|
|
385
|
-
@change=${(
|
|
386
|
-
const tag =
|
|
369
|
+
@change=${(e: Event) => {
|
|
370
|
+
const tag = (e.target as HTMLInputElement).value;
|
|
387
371
|
if (!tag) return;
|
|
388
|
-
|
|
389
|
-
const comp = componentRegistry.find(
|
|
390
|
-
(/** @type {ComponentEntry} */ c) => c.tagName === tag,
|
|
391
|
-
);
|
|
372
|
+
(e.target as HTMLInputElement).value = "";
|
|
373
|
+
const comp = componentRegistry.find((c: ComponentEntry) => c.tagName === tag);
|
|
392
374
|
if (!comp) return;
|
|
393
375
|
const relPath = computeRelativePath(documentPath, comp.path);
|
|
394
|
-
applyMutation((
|
|
376
|
+
applyMutation((doc: JxMutableNode) => {
|
|
395
377
|
if (!doc.$elements) doc.$elements = [];
|
|
396
378
|
doc.$elements.push({ $ref: relPath });
|
|
397
379
|
});
|
|
@@ -399,7 +381,7 @@ function renderDocumentLevelImports({
|
|
|
399
381
|
}}
|
|
400
382
|
>
|
|
401
383
|
${availableComponents.map(
|
|
402
|
-
(
|
|
384
|
+
(c: ComponentEntry) =>
|
|
403
385
|
html`<sp-menu-item value=${c.tagName}><${c.tagName}></sp-menu-item>`,
|
|
404
386
|
)}
|
|
405
387
|
</sp-picker>
|
|
@@ -416,7 +398,7 @@ function renderDocumentLevelImports({
|
|
|
416
398
|
<span class="imports-section-title import-mono">${pkg}</span>
|
|
417
399
|
</div>
|
|
418
400
|
<div class="imports-list imports-component-list">
|
|
419
|
-
${comps.map((
|
|
401
|
+
${comps.map((comp: ComponentEntry) => {
|
|
420
402
|
const enabled = isComponentEnabled(comp, npmEntries);
|
|
421
403
|
const specifier = componentSpecifier(comp);
|
|
422
404
|
return html`
|
|
@@ -424,18 +406,16 @@ function renderDocumentLevelImports({
|
|
|
424
406
|
<sp-checkbox
|
|
425
407
|
size="s"
|
|
426
408
|
.checked=${enabled}
|
|
427
|
-
@change=${(
|
|
428
|
-
applyMutation((
|
|
409
|
+
@change=${(e: Event) => {
|
|
410
|
+
applyMutation((doc: JxMutableNode) => {
|
|
429
411
|
if (!doc.$elements) doc.$elements = [];
|
|
430
412
|
// Remove legacy full-package import if present
|
|
431
|
-
doc.$elements = doc.$elements.filter(
|
|
432
|
-
|
|
433
|
-
);
|
|
434
|
-
if (/** @type {HTMLInputElement} */ (e.target).checked) {
|
|
413
|
+
doc.$elements = doc.$elements.filter((el: ElementsEntry) => el !== pkg);
|
|
414
|
+
if ((e.target as HTMLInputElement).checked) {
|
|
435
415
|
if (!doc.$elements.includes(specifier)) doc.$elements.push(specifier);
|
|
436
416
|
} else {
|
|
437
417
|
doc.$elements = doc.$elements.filter(
|
|
438
|
-
(
|
|
418
|
+
(el: ElementsEntry) => el !== specifier,
|
|
439
419
|
);
|
|
440
420
|
}
|
|
441
421
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/**
|
|
2
3
|
* Layers panel — document tree view showing element hierarchy with collapse, selection, move
|
|
3
4
|
* actions, and drag-and-drop reordering.
|
|
@@ -15,18 +16,20 @@ import {
|
|
|
15
16
|
childIndex,
|
|
16
17
|
nodeLabel,
|
|
17
18
|
VOID_ELEMENTS,
|
|
18
|
-
} from "../store
|
|
19
|
-
import { activeTab } from "../workspace/workspace
|
|
19
|
+
} from "../store";
|
|
20
|
+
import { activeTab } from "../workspace/workspace";
|
|
21
|
+
import type { JxPath } from "../state";
|
|
22
|
+
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
20
23
|
import {
|
|
21
24
|
transactDoc,
|
|
22
25
|
mutateMoveNode,
|
|
23
26
|
mutateRemoveNode,
|
|
24
27
|
mutateUpdateProperty,
|
|
25
|
-
} from "../tabs/transact
|
|
26
|
-
import { view } from "../view
|
|
27
|
-
import { isInlineElement } from "../editor/inline-edit
|
|
28
|
-
import { showContextMenu } from "../editor/context-menu
|
|
29
|
-
import { panToElement } from "../canvas/canvas-utils
|
|
28
|
+
} from "../tabs/transact";
|
|
29
|
+
import { view } from "../view";
|
|
30
|
+
import { isInlineElement } from "../editor/inline-edit";
|
|
31
|
+
import { showContextMenu } from "../editor/context-menu";
|
|
32
|
+
import { panToElement } from "../canvas/canvas-utils";
|
|
30
33
|
|
|
31
34
|
/**
|
|
32
35
|
* Start inline title editing on a layer row.
|
|
@@ -34,22 +37,24 @@ import { panToElement } from "../canvas/canvas-utils.js";
|
|
|
34
37
|
* @param {JxPath} path
|
|
35
38
|
* @param {() => void} rerender
|
|
36
39
|
*/
|
|
37
|
-
export function startLayerTitleEdit(path, rerender) {
|
|
40
|
+
export function startLayerTitleEdit(path: JxPath, rerender: () => void) {
|
|
38
41
|
const key = pathKey(path);
|
|
39
42
|
const row = document.querySelector(`.layer-row[data-path="${key}"]`);
|
|
40
43
|
if (!row) return;
|
|
41
|
-
const label =
|
|
44
|
+
const label = row.querySelector(".layer-label") as HTMLElement | null;
|
|
42
45
|
if (!label) return;
|
|
43
46
|
|
|
44
47
|
const tab = activeTab.value;
|
|
45
|
-
|
|
48
|
+
if (!tab) return;
|
|
49
|
+
const node = getNodeAtPath(tab.doc.document, path);
|
|
46
50
|
if (!node) return;
|
|
47
51
|
|
|
48
52
|
label.style.display = "none";
|
|
49
53
|
const input = document.createElement("input");
|
|
50
54
|
input.className = "layer-title-input";
|
|
51
55
|
input.value = node.$title || "";
|
|
52
|
-
|
|
56
|
+
const { $title: _, ...nodeWithoutTitle } = node;
|
|
57
|
+
input.placeholder = nodeLabel(nodeWithoutTitle);
|
|
53
58
|
label.after(input);
|
|
54
59
|
input.focus();
|
|
55
60
|
input.select();
|
|
@@ -64,7 +69,7 @@ export function startLayerTitleEdit(path, rerender) {
|
|
|
64
69
|
committed = true;
|
|
65
70
|
cleanup();
|
|
66
71
|
const val = input.value.trim();
|
|
67
|
-
transactDoc(
|
|
72
|
+
transactDoc(tab, (t) => mutateUpdateProperty(t, path, "$title", val || undefined));
|
|
68
73
|
rerender();
|
|
69
74
|
};
|
|
70
75
|
const cancel = () => {
|
|
@@ -74,7 +79,7 @@ export function startLayerTitleEdit(path, rerender) {
|
|
|
74
79
|
rerender();
|
|
75
80
|
};
|
|
76
81
|
input.addEventListener("blur", commit);
|
|
77
|
-
input.addEventListener("keydown", (
|
|
82
|
+
input.addEventListener("keydown", (e: KeyboardEvent) => {
|
|
78
83
|
if (e.key === "Enter") {
|
|
79
84
|
e.preventDefault();
|
|
80
85
|
input.blur();
|
|
@@ -90,17 +95,19 @@ export function startLayerTitleEdit(path, rerender) {
|
|
|
90
95
|
* @param {{ navigateToComponent: (path: string) => void; rerender: () => void }} ctx
|
|
91
96
|
* @returns {import("lit-html").TemplateResult}
|
|
92
97
|
*/
|
|
93
|
-
export function renderLayersTemplate(ctx
|
|
98
|
+
export function renderLayersTemplate(ctx: {
|
|
99
|
+
navigateToComponent: (path: string) => void;
|
|
100
|
+
rerender: () => void;
|
|
101
|
+
}) {
|
|
94
102
|
const tab = activeTab.value;
|
|
95
103
|
|
|
96
104
|
for (const fn of view.dndCleanups) fn();
|
|
97
105
|
view.dndCleanups = [];
|
|
98
106
|
|
|
99
|
-
const rows = flattenTree(tab
|
|
107
|
+
const rows = flattenTree(tab!.doc.document);
|
|
100
108
|
const collapsed = view._layersCollapsed || (view._layersCollapsed = new Set());
|
|
101
109
|
|
|
102
|
-
|
|
103
|
-
const layerRows = [];
|
|
110
|
+
const layerRows: import("lit-html").TemplateResult[] = [];
|
|
104
111
|
for (const { node, path, depth, nodeType } of rows) {
|
|
105
112
|
let hidden = false;
|
|
106
113
|
for (let d = 1; d <= path.length; d++) {
|
|
@@ -130,23 +137,21 @@ export function renderLayersTemplate(ctx) {
|
|
|
130
137
|
|
|
131
138
|
// After text-node skip, node is guaranteed to be JxMutableNode (not a primitive)
|
|
132
139
|
if (typeof node !== "object" || node === null) continue;
|
|
133
|
-
|
|
134
|
-
const jxNode = /** @type {JxMutableNode} */ (node);
|
|
140
|
+
const jxNode: JxMutableNode = node as JxMutableNode;
|
|
135
141
|
|
|
136
142
|
if (path.length >= 2 && nodeType === "element") {
|
|
137
143
|
const pPath = parentElementPath(path);
|
|
138
|
-
const parentNode = pPath ? getNodeAtPath(tab
|
|
144
|
+
const parentNode = pPath ? getNodeAtPath(tab!.doc.document, pPath) : null;
|
|
139
145
|
if (parentNode && isInlineElement(jxNode, parentNode)) continue;
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
const key = pathKey(path);
|
|
143
|
-
const isSelected = pathsEqual(path, tab
|
|
149
|
+
const isSelected = pathsEqual(path, tab!.session.selection);
|
|
144
150
|
const hasChildren = Array.isArray(jxNode.children) && jxNode.children.length > 0;
|
|
145
151
|
const hasMapChildren =
|
|
146
152
|
jxNode.children &&
|
|
147
153
|
typeof jxNode.children === "object" &&
|
|
148
|
-
|
|
149
|
-
.$prototype === "Array";
|
|
154
|
+
(jxNode.children as unknown as Record<string, unknown>).$prototype === "Array";
|
|
150
155
|
const hasCases =
|
|
151
156
|
jxNode.$switch &&
|
|
152
157
|
jxNode.cases &&
|
|
@@ -194,10 +199,9 @@ export function renderLayersTemplate(ctx) {
|
|
|
194
199
|
|
|
195
200
|
const isElement = nodeType === "element";
|
|
196
201
|
const isRoot = tab?.doc.mode === "content" ? path.length === 0 : path.length < 2;
|
|
197
|
-
const idx = isElement ?
|
|
198
|
-
const parentPath =
|
|
199
|
-
|
|
200
|
-
const parentNode = parentPath ? getNodeAtPath(tab?.doc.document, parentPath) : null;
|
|
202
|
+
const idx = isElement ? (childIndex(path) as number) : 0;
|
|
203
|
+
const parentPath = isElement && !isRoot ? (parentElementPath(path) as JxPath) : null;
|
|
204
|
+
const parentNode = parentPath ? getNodeAtPath(tab!.doc.document, parentPath) : null;
|
|
201
205
|
const siblingCount = parentNode?.children?.length || 0;
|
|
202
206
|
const canMoveUp = isElement && !isRoot && idx > 0;
|
|
203
207
|
const canMoveDown = isElement && !isRoot && idx < siblingCount - 1;
|
|
@@ -209,7 +213,7 @@ export function renderLayersTemplate(ctx) {
|
|
|
209
213
|
if (!ch) return false;
|
|
210
214
|
if (
|
|
211
215
|
typeof ch === "object" &&
|
|
212
|
-
|
|
216
|
+
(ch as unknown as Record<string, unknown>).$prototype === "Array"
|
|
213
217
|
)
|
|
214
218
|
return true;
|
|
215
219
|
if (!Array.isArray(ch)) return false;
|
|
@@ -221,7 +225,7 @@ export function renderLayersTemplate(ctx) {
|
|
|
221
225
|
const canMoveIn = isElement && !isRoot && prevIsContainer;
|
|
222
226
|
const grandparentPath =
|
|
223
227
|
isElement && parentPath && parentPath.length >= 2
|
|
224
|
-
?
|
|
228
|
+
? (parentElementPath(parentPath) as JxPath)
|
|
225
229
|
: null;
|
|
226
230
|
const canMoveOut = isElement && !isRoot && !!grandparentPath;
|
|
227
231
|
|
|
@@ -234,17 +238,17 @@ export function renderLayersTemplate(ctx) {
|
|
|
234
238
|
data-dnd-void=${isElement && isVoidEl ? "" : nothing}
|
|
235
239
|
data-dnd-expanded=${isElement && isExpandable && !collapsed.has(key) ? "" : nothing}
|
|
236
240
|
@click=${() => {
|
|
237
|
-
activeTab.value
|
|
241
|
+
activeTab.value!.session.selection = path;
|
|
238
242
|
panToElement(path);
|
|
239
243
|
}}
|
|
240
244
|
@dblclick=${isElement
|
|
241
|
-
? (
|
|
245
|
+
? (e: MouseEvent) => {
|
|
242
246
|
e.stopPropagation();
|
|
243
247
|
startLayerTitleEdit(path, ctx.rerender);
|
|
244
248
|
}
|
|
245
249
|
: nothing}
|
|
246
250
|
@contextmenu=${isElement
|
|
247
|
-
? (
|
|
251
|
+
? (e: MouseEvent) =>
|
|
248
252
|
showContextMenu(e, path, {
|
|
249
253
|
onEditComponent: ctx.navigateToComponent,
|
|
250
254
|
rerender: ctx.rerender,
|
|
@@ -274,11 +278,11 @@ export function renderLayersTemplate(ctx) {
|
|
|
274
278
|
quiet
|
|
275
279
|
size="xs"
|
|
276
280
|
title="Move up"
|
|
277
|
-
@click=${(
|
|
281
|
+
@click=${(e: MouseEvent) => {
|
|
278
282
|
e.stopPropagation();
|
|
279
|
-
|
|
280
|
-
const pp =
|
|
281
|
-
transactDoc(activeTab.value
|
|
283
|
+
(e.currentTarget as HTMLElement).blur();
|
|
284
|
+
const pp = parentPath as JxPath;
|
|
285
|
+
transactDoc(activeTab.value!, (t) => mutateMoveNode(t, path, pp, idx - 1));
|
|
282
286
|
}}
|
|
283
287
|
>
|
|
284
288
|
<sp-icon-arrow-up slot="icon"></sp-icon-arrow-up>
|
|
@@ -289,11 +293,11 @@ export function renderLayersTemplate(ctx) {
|
|
|
289
293
|
quiet
|
|
290
294
|
size="xs"
|
|
291
295
|
title="Move down"
|
|
292
|
-
@click=${(
|
|
296
|
+
@click=${(e: MouseEvent) => {
|
|
293
297
|
e.stopPropagation();
|
|
294
|
-
|
|
295
|
-
const pp =
|
|
296
|
-
transactDoc(activeTab.value
|
|
298
|
+
(e.currentTarget as HTMLElement).blur();
|
|
299
|
+
const pp = parentPath as JxPath;
|
|
300
|
+
transactDoc(activeTab.value!, (t) => mutateMoveNode(t, path, pp, idx + 2));
|
|
297
301
|
}}
|
|
298
302
|
>
|
|
299
303
|
<sp-icon-arrow-down slot="icon"></sp-icon-arrow-down>
|
|
@@ -304,14 +308,16 @@ export function renderLayersTemplate(ctx) {
|
|
|
304
308
|
quiet
|
|
305
309
|
size="xs"
|
|
306
310
|
title="Move into previous sibling"
|
|
307
|
-
@click=${(
|
|
311
|
+
@click=${(e: MouseEvent) => {
|
|
308
312
|
e.stopPropagation();
|
|
309
|
-
|
|
310
|
-
const pp =
|
|
313
|
+
(e.currentTarget as HTMLElement).blur();
|
|
314
|
+
const pp = parentPath as JxPath;
|
|
311
315
|
const prevPath = [...pp, idx - 1];
|
|
312
|
-
const prev = getNodeAtPath(activeTab.value
|
|
316
|
+
const prev = getNodeAtPath(activeTab.value!.doc.document, prevPath);
|
|
313
317
|
const len = prev?.children?.length || 0;
|
|
314
|
-
transactDoc(activeTab.value
|
|
318
|
+
transactDoc(activeTab.value!, (t) =>
|
|
319
|
+
mutateMoveNode(t, path, prevPath, len),
|
|
320
|
+
);
|
|
315
321
|
}}
|
|
316
322
|
>
|
|
317
323
|
<sp-icon-arrow-right slot="icon"></sp-icon-arrow-right>
|
|
@@ -322,14 +328,12 @@ export function renderLayersTemplate(ctx) {
|
|
|
322
328
|
quiet
|
|
323
329
|
size="xs"
|
|
324
330
|
title="Move out of parent"
|
|
325
|
-
@click=${(
|
|
331
|
+
@click=${(e: MouseEvent) => {
|
|
326
332
|
e.stopPropagation();
|
|
327
|
-
|
|
328
|
-
const gp =
|
|
329
|
-
const parentIdx =
|
|
330
|
-
|
|
331
|
-
);
|
|
332
|
-
transactDoc(activeTab.value, (t) =>
|
|
333
|
+
(e.currentTarget as HTMLElement).blur();
|
|
334
|
+
const gp = grandparentPath as JxPath;
|
|
335
|
+
const parentIdx = childIndex(parentPath!) as number;
|
|
336
|
+
transactDoc(activeTab.value!, (t) =>
|
|
333
337
|
mutateMoveNode(t, path, gp, parentIdx + 1),
|
|
334
338
|
);
|
|
335
339
|
}}
|
|
@@ -342,9 +346,9 @@ export function renderLayersTemplate(ctx) {
|
|
|
342
346
|
size="xs"
|
|
343
347
|
class="layer-delete"
|
|
344
348
|
title="Delete"
|
|
345
|
-
@click=${(
|
|
349
|
+
@click=${(e: MouseEvent) => {
|
|
346
350
|
e.stopPropagation();
|
|
347
|
-
transactDoc(activeTab.value
|
|
351
|
+
transactDoc(activeTab.value!, (t) => mutateRemoveNode(t, path));
|
|
348
352
|
}}
|
|
349
353
|
>
|
|
350
354
|
<sp-icon-close slot="icon"></sp-icon-close>
|
|
@@ -360,13 +364,13 @@ export function renderLayersTemplate(ctx) {
|
|
|
360
364
|
<div class="layers-container" style="position:relative">
|
|
361
365
|
<div
|
|
362
366
|
class="layers-tree"
|
|
363
|
-
@click=${(
|
|
364
|
-
const toggle =
|
|
367
|
+
@click=${(e: MouseEvent) => {
|
|
368
|
+
const toggle = (e.target as HTMLElement).closest(".layer-toggle");
|
|
365
369
|
if (!toggle) return;
|
|
366
370
|
e.stopPropagation();
|
|
367
371
|
const row = toggle.closest(".layer-row");
|
|
368
372
|
if (!row) return;
|
|
369
|
-
const key =
|
|
373
|
+
const key = (row as HTMLElement).dataset.path;
|
|
370
374
|
if (!key) return;
|
|
371
375
|
if (collapsed.has(key)) collapsed.delete(key);
|
|
372
376
|
else collapsed.add(key);
|