@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,3 +1,4 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/**
|
|
2
3
|
* Media Picker — combobox-style widget for selecting project media files.
|
|
3
4
|
*
|
|
@@ -7,8 +8,8 @@
|
|
|
7
8
|
|
|
8
9
|
import { html, nothing } from "lit-html";
|
|
9
10
|
import { live } from "lit-html/directives/live.js";
|
|
10
|
-
import { getPlatform } from "../platform
|
|
11
|
-
import { debouncedStyleCommit } from "../store
|
|
11
|
+
import { getPlatform } from "../platform";
|
|
12
|
+
import { debouncedStyleCommit } from "../store";
|
|
12
13
|
|
|
13
14
|
// ─── Media file cache ────────────────────────────────────────────────────────
|
|
14
15
|
|
|
@@ -34,7 +35,7 @@ const MEDIA_EXTENSIONS = new Set([
|
|
|
34
35
|
]);
|
|
35
36
|
|
|
36
37
|
/** @type {{ path: string; name: string; isImage: boolean }[]} */
|
|
37
|
-
let mediaCache = [];
|
|
38
|
+
let mediaCache: { path: string; name: string; isImage: boolean }[] = [];
|
|
38
39
|
let mediaCacheLoaded = false;
|
|
39
40
|
|
|
40
41
|
/**
|
|
@@ -44,7 +45,10 @@ let mediaCacheLoaded = false;
|
|
|
44
45
|
* @param {ReturnType<typeof getPlatform>} platform
|
|
45
46
|
* @returns {Promise<{ path: string; name: string; isImage: boolean }[]>}
|
|
46
47
|
*/
|
|
47
|
-
async function collectMedia(
|
|
48
|
+
async function collectMedia(
|
|
49
|
+
dir: string,
|
|
50
|
+
platform: ReturnType<typeof getPlatform>,
|
|
51
|
+
): Promise<{ path: string; name: string; isImage: boolean }[]> {
|
|
48
52
|
/** @type {{ path: string; name: string; isImage: boolean }[]} */
|
|
49
53
|
const results = [];
|
|
50
54
|
try {
|
|
@@ -98,7 +102,7 @@ export function invalidateMediaCache() {
|
|
|
98
102
|
* @param {(val: string) => void} onCommit — commit callback
|
|
99
103
|
* @returns {import("lit-html").TemplateResult}
|
|
100
104
|
*/
|
|
101
|
-
export function renderMediaPicker(prop, value, onCommit) {
|
|
105
|
+
export function renderMediaPicker(prop: string, value: string, onCommit: (val: string) => void) {
|
|
102
106
|
// Kick off async load (won't block render)
|
|
103
107
|
loadMediaCache();
|
|
104
108
|
|
|
@@ -127,8 +131,8 @@ export function renderMediaPicker(prop, value, onCommit) {
|
|
|
127
131
|
size="s"
|
|
128
132
|
placeholder="/image.jpg"
|
|
129
133
|
.value=${live(currentValue)}
|
|
130
|
-
@input=${debouncedStyleCommit(`media:${prop}`, 400, (
|
|
131
|
-
onCommit(
|
|
134
|
+
@input=${debouncedStyleCommit(`media:${prop}`, 400, (e: Event) =>
|
|
135
|
+
onCommit((e.target as HTMLInputElement).value),
|
|
132
136
|
)}
|
|
133
137
|
@focus=${() => loadMediaCache()}
|
|
134
138
|
></sp-textfield>
|
|
@@ -140,8 +144,8 @@ export function renderMediaPicker(prop, value, onCommit) {
|
|
|
140
144
|
</sp-action-button>
|
|
141
145
|
<sp-popover slot="click-content" class="media-picker-popover">
|
|
142
146
|
<sp-menu
|
|
143
|
-
@change=${(
|
|
144
|
-
onCommit(
|
|
147
|
+
@change=${(e: Event) => {
|
|
148
|
+
onCommit((e.target as HTMLInputElement).value);
|
|
145
149
|
}}
|
|
146
150
|
>
|
|
147
151
|
${options.map(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/**
|
|
2
3
|
* Panel-resize.js — Draggable resize handles for left and right sidebars.
|
|
3
4
|
*
|
|
@@ -5,7 +6,7 @@
|
|
|
5
6
|
* localStorage so they survive page reloads.
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
|
-
import { view, applyPanelCollapse } from "../view
|
|
9
|
+
import { view, applyPanelCollapse } from "../view";
|
|
9
10
|
|
|
10
11
|
const STORAGE_KEY = "jx-studio-panel-widths";
|
|
11
12
|
const MIN_WIDTH = 160;
|
|
@@ -36,9 +37,13 @@ try {
|
|
|
36
37
|
* @param {"left" | "right"} side
|
|
37
38
|
* @param {number} defaultWidth
|
|
38
39
|
*/
|
|
39
|
-
function setupHandle(
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
function setupHandle(
|
|
41
|
+
handle: HTMLElement,
|
|
42
|
+
cssVar: string,
|
|
43
|
+
side: "left" | "right",
|
|
44
|
+
defaultWidth: number,
|
|
45
|
+
) {
|
|
46
|
+
let drag: { startX: number; startWidth: number } | null = null;
|
|
42
47
|
|
|
43
48
|
handle.addEventListener("pointerdown", (e) => {
|
|
44
49
|
e.preventDefault();
|
|
@@ -141,8 +141,8 @@ import { IconDownload } from "@spectrum-web-components/icons-workflow/src/elemen
|
|
|
141
141
|
import { IconCheckmark } from "@spectrum-web-components/icons-workflow/src/elements/IconCheckmark.js";
|
|
142
142
|
|
|
143
143
|
// Custom studio components
|
|
144
|
-
import { JxValueSelector } from "./value-selector
|
|
145
|
-
import { JxColorPopover } from "./color-selector
|
|
144
|
+
import { JxValueSelector } from "./value-selector";
|
|
145
|
+
import { JxColorPopover } from "./color-selector";
|
|
146
146
|
|
|
147
147
|
// UI icons (used internally by Spectrum components like accordion, picker, combobox)
|
|
148
148
|
import { IconChevron100 } from "@spectrum-web-components/icons-ui/src/elements/IconChevron100.js";
|
|
@@ -282,8 +282,8 @@ const components = [
|
|
|
282
282
|
["jx-color-popover", JxColorPopover],
|
|
283
283
|
];
|
|
284
284
|
|
|
285
|
-
for (const [tag, ctor] of
|
|
286
|
-
if (!customElements.get(tag)) defineElement(tag,
|
|
285
|
+
for (const [tag, ctor] of components as [string, CustomElementConstructor][]) {
|
|
286
|
+
if (!customElements.get(tag)) defineElement(tag, ctor);
|
|
287
287
|
}
|
|
288
288
|
|
|
289
289
|
// Register theme fragments (these are also side-effect-only in the original modules)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/**
|
|
2
3
|
* Unit-selector.js — Number + unit picker widget.
|
|
3
4
|
*
|
|
@@ -8,7 +9,7 @@
|
|
|
8
9
|
import { html, nothing } from "lit-html";
|
|
9
10
|
import { live } from "lit-html/directives/live.js";
|
|
10
11
|
import { classMap } from "lit-html/directives/class-map.js";
|
|
11
|
-
import { debouncedStyleCommit } from "../store
|
|
12
|
+
import { debouncedStyleCommit } from "../store";
|
|
12
13
|
|
|
13
14
|
export const UNIT_RE = /^(-?[\d.]+)(px|rem|em|%|vw|vh|svw|svh|dvh|ms|s|fr|ch|ex|deg)?$/;
|
|
14
15
|
|
|
@@ -23,18 +24,18 @@ export const UNIT_RE = /^(-?[\d.]+)(px|rem|em|%|vw|vh|svw|svh|dvh|ms|s|fr|ch|ex|
|
|
|
23
24
|
* @returns {import("lit-html").TemplateResult}
|
|
24
25
|
*/
|
|
25
26
|
export function renderUnitSelector(
|
|
26
|
-
/** @type {Record<string, unknown>} */ entry,
|
|
27
|
-
/** @type {string} */ prop,
|
|
28
|
-
/** @type {string | number | undefined} */ value,
|
|
29
|
-
/** @type {(val: string) => void} */ onChange,
|
|
30
|
-
/** @type {string} */ placeholder = "",
|
|
27
|
+
/** @type {Record<string, unknown>} */ entry: Record<string, unknown>,
|
|
28
|
+
/** @type {string} */ prop: string,
|
|
29
|
+
/** @type {string | number | undefined} */ value: string | number | undefined,
|
|
30
|
+
/** @type {(val: string) => void} */ onChange: (val: string) => void,
|
|
31
|
+
/** @type {string} */ placeholder: string = "",
|
|
31
32
|
) {
|
|
32
|
-
const units =
|
|
33
|
-
const keywords =
|
|
33
|
+
const units = (entry.$units || []) as string[];
|
|
34
|
+
const keywords = (entry.$keywords || []) as string[];
|
|
34
35
|
const strVal = String(value ?? "");
|
|
35
36
|
const match = strVal.match(UNIT_RE);
|
|
36
37
|
const isKeyword = !match && strVal !== "" && keywords.includes(strVal);
|
|
37
|
-
const isNumericVal = (
|
|
38
|
+
const isNumericVal = (v: string) => /^-?\d*\.?\d*$/.test(v);
|
|
38
39
|
|
|
39
40
|
const currentUnit = isKeyword ? units[0] || "" : match ? match[2] || "" : units[0] || "";
|
|
40
41
|
let displayValue;
|
|
@@ -56,8 +57,8 @@ export function renderUnitSelector(
|
|
|
56
57
|
size="s"
|
|
57
58
|
placeholder=${placeholder || "0"}
|
|
58
59
|
.value=${live(displayValue)}
|
|
59
|
-
@input=${debouncedStyleCommit(`nui:${prop}`, 400, (
|
|
60
|
-
const val =
|
|
60
|
+
@input=${debouncedStyleCommit(`nui:${prop}`, 400, (e: Event) => {
|
|
61
|
+
const val = (e.target as HTMLInputElement).value.trim();
|
|
61
62
|
if (val === "") {
|
|
62
63
|
onChange("");
|
|
63
64
|
return;
|
|
@@ -75,8 +76,8 @@ export function renderUnitSelector(
|
|
|
75
76
|
<sp-popover style="min-width: var(--spectrum-component-width-900, 64px)">
|
|
76
77
|
<sp-menu
|
|
77
78
|
label="CSS unit"
|
|
78
|
-
@change=${(
|
|
79
|
-
const chosen =
|
|
79
|
+
@change=${(e: Event) => {
|
|
80
|
+
const chosen = (e.target as HTMLInputElement).value;
|
|
80
81
|
if (keywords.includes(chosen)) {
|
|
81
82
|
onChange(chosen);
|
|
82
83
|
} else if (units.includes(chosen)) {
|
|
@@ -86,16 +87,12 @@ export function renderUnitSelector(
|
|
|
86
87
|
}
|
|
87
88
|
}}
|
|
88
89
|
>
|
|
89
|
-
${units.map(
|
|
90
|
-
(/** @type {string} */ u) =>
|
|
91
|
-
html`<sp-menu-item value=${u}>${u}</sp-menu-item>`,
|
|
92
|
-
)}
|
|
90
|
+
${units.map((u: string) => html`<sp-menu-item value=${u}>${u}</sp-menu-item>`)}
|
|
93
91
|
${keywords.length > 0 && units.length > 0
|
|
94
92
|
? html`<sp-menu-divider></sp-menu-divider>`
|
|
95
93
|
: nothing}
|
|
96
94
|
${keywords.map(
|
|
97
|
-
(
|
|
98
|
-
html`<sp-menu-item value=${kw}>${kw}</sp-menu-item>`,
|
|
95
|
+
(kw: string) => html`<sp-menu-item value=${kw}>${kw}</sp-menu-item>`,
|
|
99
96
|
)}
|
|
100
97
|
</sp-menu>
|
|
101
98
|
</sp-popover>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/**
|
|
2
3
|
* Value Selector — Dual-mode styled combobox custom element.
|
|
3
4
|
*
|
|
@@ -18,7 +19,7 @@
|
|
|
18
19
|
import { LitElement, html } from "lit";
|
|
19
20
|
import { live } from "lit/directives/live.js";
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
type ComboOption = { value: string; label: string; style?: string } | { divider: true };
|
|
22
23
|
|
|
23
24
|
export class JxValueSelector extends LitElement {
|
|
24
25
|
static properties = {
|
|
@@ -28,13 +29,18 @@ export class JxValueSelector extends LitElement {
|
|
|
28
29
|
options: { attribute: false },
|
|
29
30
|
};
|
|
30
31
|
|
|
32
|
+
declare value: string;
|
|
33
|
+
declare placeholder: string;
|
|
34
|
+
declare size: string;
|
|
35
|
+
declare options: ComboOption[];
|
|
36
|
+
declare _menuId: string;
|
|
37
|
+
|
|
31
38
|
constructor() {
|
|
32
39
|
super();
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/** @type {string} */
|
|
40
|
+
this.value = "";
|
|
41
|
+
this.placeholder = "";
|
|
42
|
+
this.size = "s";
|
|
43
|
+
this.options = [];
|
|
38
44
|
this._menuId = "jx-combo-" + Math.random().toString(36).slice(2, 8);
|
|
39
45
|
}
|
|
40
46
|
|
|
@@ -47,26 +53,20 @@ export class JxValueSelector extends LitElement {
|
|
|
47
53
|
get _isPicker() {
|
|
48
54
|
return (
|
|
49
55
|
!!this.value &&
|
|
50
|
-
this.options.some(
|
|
51
|
-
(/** @type {ComboOption} */ o) => !("divider" in o) && o.value === this.value,
|
|
52
|
-
)
|
|
56
|
+
this.options.some((o: ComboOption) => !("divider" in o) && o.value === this.value)
|
|
53
57
|
);
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
/** Get the selected option's style string for the picker button preview */
|
|
57
61
|
get _selectedStyle() {
|
|
58
62
|
if (!this._isPicker) return "";
|
|
59
|
-
const opt = this.options.find(
|
|
60
|
-
|
|
61
|
-
);
|
|
62
|
-
return (
|
|
63
|
-
/** @type {{ value: string; label: string; style?: string } | undefined} */ (opt)?.style || ""
|
|
64
|
-
);
|
|
63
|
+
const opt = this.options.find((o: ComboOption) => !("divider" in o) && o.value === this.value);
|
|
64
|
+
return (opt as { value: string; label: string; style?: string } | undefined)?.style || "";
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/** Render menu items from options array */
|
|
68
68
|
_renderMenuItems() {
|
|
69
|
-
return this.options.map((
|
|
69
|
+
return this.options.map((opt: ComboOption) =>
|
|
70
70
|
"divider" in opt
|
|
71
71
|
? html`<sp-menu-divider></sp-menu-divider>`
|
|
72
72
|
: html`<sp-menu-item value=${opt.value} style=${opt.style || ""}
|
|
@@ -76,33 +76,33 @@ export class JxValueSelector extends LitElement {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/** Picker mode: sp-picker @change handler */
|
|
79
|
-
_handlePickerChange(
|
|
79
|
+
_handlePickerChange(e: Event) {
|
|
80
80
|
e.stopPropagation();
|
|
81
|
-
this.value =
|
|
81
|
+
this.value = (e.target as HTMLInputElement).value;
|
|
82
82
|
this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/** Combobox mode: sp-menu @change handler */
|
|
86
|
-
_handleMenuChange(
|
|
86
|
+
_handleMenuChange(e: Event) {
|
|
87
87
|
e.stopPropagation();
|
|
88
|
-
if (!(
|
|
89
|
-
this.value =
|
|
88
|
+
if (!(e.target as HTMLInputElement).value) return;
|
|
89
|
+
this.value = (e.target as HTMLInputElement).value;
|
|
90
90
|
this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
/** Combobox mode: textfield @input handler */
|
|
94
|
-
_handleInput(
|
|
94
|
+
_handleInput(e: Event) {
|
|
95
95
|
e.stopPropagation();
|
|
96
|
-
this.value =
|
|
96
|
+
this.value = (e.target as HTMLInputElement).value;
|
|
97
97
|
this.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
/** Set popover min-width to match trigger width (replicates sp-picker behavior) */
|
|
101
|
-
_setPopoverWidth(
|
|
101
|
+
_setPopoverWidth(e: Event) {
|
|
102
102
|
const group = this.querySelector(".jx-combobox-group");
|
|
103
|
-
const w = group ?
|
|
104
|
-
const popover =
|
|
105
|
-
if (popover && w)
|
|
103
|
+
const w = group ? (group as HTMLElement).offsetWidth : 0;
|
|
104
|
+
const popover = (e.target as HTMLElement).querySelector("sp-popover");
|
|
105
|
+
if (popover && w) (popover as HTMLElement).style.minWidth = `${w}px`;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
render() {
|
|
@@ -127,7 +127,7 @@ export class JxValueSelector extends LitElement {
|
|
|
127
127
|
placeholder=${this.placeholder}
|
|
128
128
|
.value=${live(this.value || "")}
|
|
129
129
|
@input=${this._handleInput}
|
|
130
|
-
@click=${(
|
|
130
|
+
@click=${(e: Event) => e.stopPropagation()}
|
|
131
131
|
></sp-textfield>
|
|
132
132
|
<sp-picker-button size=${this.size}></sp-picker-button>
|
|
133
133
|
<sp-overlay
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/**
|
|
2
3
|
* Widgets.js — Widget type dispatcher and simple widget renderers.
|
|
3
4
|
*
|
|
@@ -13,11 +14,11 @@
|
|
|
13
14
|
import { html } from "lit-html";
|
|
14
15
|
import { live } from "lit-html/directives/live.js";
|
|
15
16
|
import { ifDefined } from "lit-html/directives/if-defined.js";
|
|
16
|
-
import { debouncedStyleCommit } from "../store
|
|
17
|
-
import { renderColorSelector } from "./color-selector
|
|
18
|
-
import { renderUnitSelector } from "./unit-selector
|
|
19
|
-
import { renderButtonGroup } from "./button-group
|
|
20
|
-
import { renderMediaPicker } from "./media-picker
|
|
17
|
+
import { debouncedStyleCommit } from "../store";
|
|
18
|
+
import { renderColorSelector } from "./color-selector";
|
|
19
|
+
import { renderUnitSelector } from "./unit-selector";
|
|
20
|
+
import { renderButtonGroup } from "./button-group";
|
|
21
|
+
import { renderMediaPicker } from "./media-picker";
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Render a plain text input widget.
|
|
@@ -28,14 +29,19 @@ import { renderMediaPicker } from "./media-picker.js";
|
|
|
28
29
|
* @param {string} [placeholder]
|
|
29
30
|
* @returns {import("lit-html").TemplateResult}
|
|
30
31
|
*/
|
|
31
|
-
export function renderTextInput(
|
|
32
|
+
export function renderTextInput(
|
|
33
|
+
prop: string,
|
|
34
|
+
value: string | number | undefined,
|
|
35
|
+
onChange: (val: string) => void,
|
|
36
|
+
placeholder: string = "",
|
|
37
|
+
) {
|
|
32
38
|
return html`
|
|
33
39
|
<sp-textfield
|
|
34
40
|
size="s"
|
|
35
41
|
placeholder=${placeholder}
|
|
36
42
|
.value=${live(String(value ?? ""))}
|
|
37
|
-
@input=${debouncedStyleCommit(`text:${prop}`, 400, (
|
|
38
|
-
onChange(
|
|
43
|
+
@input=${debouncedStyleCommit(`text:${prop}`, 400, (e: Event) =>
|
|
44
|
+
onChange((e.target as HTMLInputElement).value),
|
|
39
45
|
)}
|
|
40
46
|
></sp-textfield>
|
|
41
47
|
`;
|
|
@@ -51,9 +57,15 @@ export function renderTextInput(prop, value, onChange, placeholder = "") {
|
|
|
51
57
|
* @param {string} [placeholder]
|
|
52
58
|
* @returns {import("lit-html").TemplateResult}
|
|
53
59
|
*/
|
|
54
|
-
export function renderNumberInput(
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
export function renderNumberInput(
|
|
61
|
+
entry: Record<string, unknown>,
|
|
62
|
+
prop: string,
|
|
63
|
+
value: string | number | undefined,
|
|
64
|
+
onChange: (val: string | number) => void,
|
|
65
|
+
placeholder: string = "",
|
|
66
|
+
) {
|
|
67
|
+
const minimum = entry.minimum as number | undefined;
|
|
68
|
+
const maximum = entry.maximum as number | undefined;
|
|
57
69
|
return html`
|
|
58
70
|
<sp-number-field
|
|
59
71
|
size="s"
|
|
@@ -63,8 +75,8 @@ export function renderNumberInput(entry, prop, value, onChange, placeholder = ""
|
|
|
63
75
|
min=${ifDefined(minimum)}
|
|
64
76
|
max=${ifDefined(maximum)}
|
|
65
77
|
step=${ifDefined(maximum !== undefined && maximum <= 1 ? 0.1 : undefined)}
|
|
66
|
-
@change=${debouncedStyleCommit(`num:${prop}`, 400, (
|
|
67
|
-
const v =
|
|
78
|
+
@change=${debouncedStyleCommit(`num:${prop}`, 400, (e: Event) => {
|
|
79
|
+
const v = (e.target as HTMLInputElement & { value: number | undefined }).value;
|
|
68
80
|
if (v === undefined || isNaN(v)) onChange("");
|
|
69
81
|
else onChange(Number(v));
|
|
70
82
|
})}
|
|
@@ -80,10 +92,45 @@ export function renderNumberInput(entry, prop, value, onChange, placeholder = ""
|
|
|
80
92
|
* @param {string} prop — property key
|
|
81
93
|
* @param {string | number | undefined} value — current value
|
|
82
94
|
* @param {(val: string | number) => void} onCommit — commit callback
|
|
83
|
-
* @param {{
|
|
95
|
+
* @param {{
|
|
96
|
+
* placeholder?: string;
|
|
97
|
+
* renderSelect?: (
|
|
98
|
+
* entry: Record<string, unknown>,
|
|
99
|
+
* prop: string,
|
|
100
|
+
* value: string | number | undefined,
|
|
101
|
+
* onCommit: (val: string | number) => void,
|
|
102
|
+
* ) => import("lit-html").TemplateResult;
|
|
103
|
+
* renderCombobox?: (
|
|
104
|
+
* entry: Record<string, unknown>,
|
|
105
|
+
* prop: string,
|
|
106
|
+
* value: string | number | undefined,
|
|
107
|
+
* onCommit: (val: string | number) => void,
|
|
108
|
+
* ) => import("lit-html").TemplateResult;
|
|
109
|
+
* }} [opts]
|
|
84
110
|
* @returns {import("lit-html").TemplateResult}
|
|
85
111
|
*/
|
|
86
|
-
export function widgetForType(
|
|
112
|
+
export function widgetForType(
|
|
113
|
+
type: string,
|
|
114
|
+
entry: Record<string, unknown>,
|
|
115
|
+
prop: string,
|
|
116
|
+
value: string | number | undefined,
|
|
117
|
+
onCommit: (val: string | number) => void,
|
|
118
|
+
opts: {
|
|
119
|
+
placeholder?: string;
|
|
120
|
+
renderSelect?: (
|
|
121
|
+
entry: Record<string, unknown>,
|
|
122
|
+
prop: string,
|
|
123
|
+
value: any,
|
|
124
|
+
onCommit: (val: string | number) => void,
|
|
125
|
+
) => import("lit-html").TemplateResult;
|
|
126
|
+
renderCombobox?: (
|
|
127
|
+
entry: Record<string, unknown>,
|
|
128
|
+
prop: string,
|
|
129
|
+
value: any,
|
|
130
|
+
onCommit: (val: string | number) => void,
|
|
131
|
+
) => import("lit-html").TemplateResult;
|
|
132
|
+
} = {},
|
|
133
|
+
) {
|
|
87
134
|
switch (type) {
|
|
88
135
|
case "button-group":
|
|
89
136
|
return renderButtonGroup(entry, prop, value, onCommit);
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/** Canvas media/breakpoint utilities — pure functions extracted for testability. */
|
|
2
3
|
|
|
4
|
+
import type { JxStyle } from "@jxsuite/schema/types";
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* Classify $media entries into size breakpoints (get a canvas each) and feature queries (rendered
|
|
5
8
|
* as toolbar toggles).
|
|
@@ -11,7 +14,7 @@
|
|
|
11
14
|
* baseWidth: number;
|
|
12
15
|
* }}
|
|
13
16
|
*/
|
|
14
|
-
export function parseMediaEntries(mediaDef) {
|
|
17
|
+
export function parseMediaEntries(mediaDef: Record<string, string> | null | undefined) {
|
|
15
18
|
if (!mediaDef) return { sizeBreakpoints: [], featureQueries: [], baseWidth: 320 };
|
|
16
19
|
const sizes = [],
|
|
17
20
|
features = [];
|
|
@@ -39,8 +42,11 @@ export function parseMediaEntries(mediaDef) {
|
|
|
39
42
|
* @param {number} canvasWidth
|
|
40
43
|
* @returns {Set<string>}
|
|
41
44
|
*/
|
|
42
|
-
export function activeBreakpointsForWidth(
|
|
43
|
-
|
|
45
|
+
export function activeBreakpointsForWidth(
|
|
46
|
+
sizeBreakpoints: { name: string; width: number; type: string }[],
|
|
47
|
+
canvasWidth: number,
|
|
48
|
+
) {
|
|
49
|
+
const active = new Set<string>();
|
|
44
50
|
for (const bp of sizeBreakpoints) {
|
|
45
51
|
if (bp.type === "min" && canvasWidth >= bp.width) active.add(bp.name);
|
|
46
52
|
else if (bp.type === "max" && canvasWidth <= bp.width) active.add(bp.name);
|
|
@@ -57,18 +63,23 @@ export function activeBreakpointsForWidth(sizeBreakpoints, canvasWidth) {
|
|
|
57
63
|
* @param {Set<string>} activeBreakpoints
|
|
58
64
|
* @param {Record<string, boolean>} featureToggles
|
|
59
65
|
*/
|
|
60
|
-
export function applyCanvasStyle(
|
|
66
|
+
export function applyCanvasStyle(
|
|
67
|
+
el: HTMLElement,
|
|
68
|
+
styleDef: JxStyle | null | undefined,
|
|
69
|
+
activeBreakpoints: Set<string>,
|
|
70
|
+
featureToggles: Record<string, boolean>,
|
|
71
|
+
) {
|
|
61
72
|
if (!styleDef || typeof styleDef !== "object") return;
|
|
62
73
|
for (const [prop, val] of Object.entries(styleDef)) {
|
|
63
74
|
if (typeof val === "string" || typeof val === "number") {
|
|
64
75
|
try {
|
|
65
76
|
if (prop.startsWith("--")) el.style.setProperty(prop, String(val));
|
|
66
|
-
else
|
|
77
|
+
else (el.style as unknown as Record<string, unknown>)[prop] = val;
|
|
67
78
|
} catch {}
|
|
68
79
|
}
|
|
69
80
|
}
|
|
70
81
|
for (const [key, val] of Object.entries(styleDef)) {
|
|
71
|
-
if (!key.startsWith("@") || typeof val !== "object") continue;
|
|
82
|
+
if (!key.startsWith("@") || typeof val !== "object" || val === null) continue;
|
|
72
83
|
const mediaName = key.slice(1);
|
|
73
84
|
if (mediaName === "--") continue;
|
|
74
85
|
if (activeBreakpoints.has(mediaName) || featureToggles[mediaName]) {
|
|
@@ -76,8 +87,7 @@ export function applyCanvasStyle(el, styleDef, activeBreakpoints, featureToggles
|
|
|
76
87
|
if (typeof v === "string" || typeof v === "number") {
|
|
77
88
|
try {
|
|
78
89
|
if (prop.startsWith("--")) el.style.setProperty(prop, String(v));
|
|
79
|
-
else
|
|
80
|
-
/** @type {Record<string, unknown>} */ (/** @type {unknown} */ (el.style))[prop] = v;
|
|
90
|
+
else (el.style as unknown as Record<string, unknown>)[prop] = v;
|
|
81
91
|
} catch {}
|
|
82
92
|
}
|
|
83
93
|
}
|
|
@@ -95,9 +105,11 @@ export function applyCanvasStyle(el, styleDef, activeBreakpoints, featureToggles
|
|
|
95
105
|
* @param {Set<string>} activeBreakpoints
|
|
96
106
|
* @returns {Map<string, Map<string, string>>}
|
|
97
107
|
*/
|
|
98
|
-
export function collectMediaOverrides(
|
|
99
|
-
|
|
100
|
-
|
|
108
|
+
export function collectMediaOverrides(
|
|
109
|
+
styleSheets: Iterable<CSSStyleSheet>,
|
|
110
|
+
activeBreakpoints: Set<string>,
|
|
111
|
+
) {
|
|
112
|
+
const overrides: Map<string, Map<string, string>> = new Map();
|
|
101
113
|
if (!activeBreakpoints.size) return overrides;
|
|
102
114
|
|
|
103
115
|
for (const sheet of styleSheets) {
|
|
@@ -121,7 +133,7 @@ export function collectMediaOverrides(styleSheets, activeBreakpoints) {
|
|
|
121
133
|
if (!jxMatch) continue;
|
|
122
134
|
const uid = jxMatch[1];
|
|
123
135
|
if (!overrides.has(uid)) overrides.set(uid, new Map());
|
|
124
|
-
const props =
|
|
136
|
+
const props = overrides.get(uid) as Map<string, string>;
|
|
125
137
|
for (let i = 0; i < mediaRule.style.length; i++) {
|
|
126
138
|
const prop = mediaRule.style[i];
|
|
127
139
|
props.set(prop, mediaRule.style.getPropertyValue(prop));
|
|
@@ -138,13 +150,16 @@ export function collectMediaOverrides(styleSheets, activeBreakpoints) {
|
|
|
138
150
|
* @param {Element} canvasEl
|
|
139
151
|
* @param {Map<string, Map<string, string>>} overrides
|
|
140
152
|
*/
|
|
141
|
-
export function applyOverridesToCanvas(
|
|
153
|
+
export function applyOverridesToCanvas(
|
|
154
|
+
canvasEl: Element,
|
|
155
|
+
overrides: Map<string, Map<string, string>>,
|
|
156
|
+
) {
|
|
142
157
|
for (const [uid, props] of overrides) {
|
|
143
158
|
const els = canvasEl.querySelectorAll(`[data-jx="${uid}"]`);
|
|
144
159
|
for (const el of els) {
|
|
145
160
|
for (const [prop, val] of props) {
|
|
146
161
|
try {
|
|
147
|
-
|
|
162
|
+
(el as HTMLElement).style.setProperty(prop, val);
|
|
148
163
|
} catch {}
|
|
149
164
|
}
|
|
150
165
|
}
|