@jxsuite/studio 0.23.1 → 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 +2231 -715
- 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} +110 -85
- 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} +38 -26
- 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
|
* CSS Variables editor — form-based editor for managing design tokens. Colors and fonts are NOT
|
|
3
4
|
* media-aware; sizes/spacing are media-aware.
|
|
@@ -5,13 +6,15 @@
|
|
|
5
6
|
|
|
6
7
|
import { html, render as litRender, nothing } from "lit-html";
|
|
7
8
|
import { ref } from "lit-html/directives/ref.js";
|
|
8
|
-
import { projectState } from "../store
|
|
9
|
-
import { updateSiteConfig } from "../site-context
|
|
10
|
-
import { getEffectiveMedia } from "../site-context
|
|
11
|
-
import { friendlyNameToVar, varDisplayName } from "../utils/studio-utils
|
|
9
|
+
import { projectState } from "../store";
|
|
10
|
+
import { updateSiteConfig } from "../site-context";
|
|
11
|
+
import { getEffectiveMedia } from "../site-context";
|
|
12
|
+
import { friendlyNameToVar, varDisplayName } from "../utils/studio-utils";
|
|
13
|
+
|
|
14
|
+
import type { JxStyle } from "@jxsuite/schema/types";
|
|
12
15
|
|
|
13
16
|
/** @param {HTMLElement} container */
|
|
14
|
-
export function renderCssVarsEditor(container) {
|
|
17
|
+
export function renderCssVarsEditor(container: HTMLElement) {
|
|
15
18
|
const config = projectState?.projectConfig || {};
|
|
16
19
|
const rootStyle = config.style || {};
|
|
17
20
|
const media = getEffectiveMedia(config.$media);
|
|
@@ -24,7 +27,12 @@ export function renderCssVarsEditor(container) {
|
|
|
24
27
|
* other: [string, string | number][];
|
|
25
28
|
* }}
|
|
26
29
|
*/
|
|
27
|
-
const groups
|
|
30
|
+
const groups: {
|
|
31
|
+
color: [string, string | number][];
|
|
32
|
+
font: [string, string | number][];
|
|
33
|
+
size: [string, string | number][];
|
|
34
|
+
other: [string, string | number][];
|
|
35
|
+
} = { color: [], font: [], size: [], other: [] };
|
|
28
36
|
for (const [k, v] of Object.entries(rootStyle)) {
|
|
29
37
|
if (!k.startsWith("--")) continue;
|
|
30
38
|
if (typeof v !== "string" && typeof v !== "number") continue;
|
|
@@ -41,22 +49,18 @@ export function renderCssVarsEditor(container) {
|
|
|
41
49
|
updateSiteConfig({ style: { ...rootStyle } });
|
|
42
50
|
};
|
|
43
51
|
|
|
44
|
-
const updateVar = (
|
|
52
|
+
const updateVar = (name: string, val: string) => {
|
|
45
53
|
rootStyle[name] = val;
|
|
46
54
|
save();
|
|
47
55
|
};
|
|
48
56
|
|
|
49
|
-
const deleteVar = (
|
|
57
|
+
const deleteVar = (name: string) => {
|
|
50
58
|
delete rootStyle[name];
|
|
51
59
|
save();
|
|
52
60
|
renderCssVarsEditor(container);
|
|
53
61
|
};
|
|
54
62
|
|
|
55
|
-
const addVar = (
|
|
56
|
-
/** @type {string} */ prefix,
|
|
57
|
-
/** @type {string} */ friendlyName,
|
|
58
|
-
/** @type {string} */ val,
|
|
59
|
-
) => {
|
|
63
|
+
const addVar = (prefix: string, friendlyName: string, val: string) => {
|
|
60
64
|
const varName = friendlyNameToVar(friendlyName, prefix);
|
|
61
65
|
if (!varName || !val) return;
|
|
62
66
|
rootStyle[varName] = val;
|
|
@@ -82,11 +86,16 @@ export function renderCssVarsEditor(container) {
|
|
|
82
86
|
|
|
83
87
|
/**
|
|
84
88
|
* @param {[string, string | number][]} vars
|
|
85
|
-
* @param {
|
|
86
|
-
* @param {
|
|
87
|
-
* @param {
|
|
89
|
+
* @param {(name: string, val: string) => void} updateVar
|
|
90
|
+
* @param {(name: string) => void} deleteVar
|
|
91
|
+
* @param {(prefix: string, friendlyName: string, val: string) => void} addVar
|
|
88
92
|
*/
|
|
89
|
-
function renderColorSection(
|
|
93
|
+
function renderColorSection(
|
|
94
|
+
vars: [string, string | number][],
|
|
95
|
+
updateVar: (name: string, val: string) => void,
|
|
96
|
+
deleteVar: (name: string) => void,
|
|
97
|
+
addVar: (prefix: string, friendlyName: string, val: string) => void,
|
|
98
|
+
) {
|
|
90
99
|
return html`
|
|
91
100
|
<div class="css-vars-group">
|
|
92
101
|
<h4 class="css-vars-group-title">Colors</h4>
|
|
@@ -97,16 +106,14 @@ function renderColorSection(vars, updateVar, deleteVar, addVar) {
|
|
|
97
106
|
<input
|
|
98
107
|
type="color"
|
|
99
108
|
.value=${val && String(val).startsWith("#") ? val : "#007acc"}
|
|
100
|
-
@input=${(
|
|
101
|
-
updateVar(name, /** @type {HTMLInputElement} */ (e.target).value)}
|
|
109
|
+
@input=${(e: Event) => updateVar(name, (e.target as HTMLInputElement).value)}
|
|
102
110
|
/>
|
|
103
111
|
</div>
|
|
104
112
|
<span class="css-var-name">${varDisplayName(name, "--color-")}</span>
|
|
105
113
|
<sp-textfield
|
|
106
114
|
size="s"
|
|
107
115
|
.value=${String(val)}
|
|
108
|
-
@change=${(
|
|
109
|
-
updateVar(name, /** @type {HTMLInputElement} */ (e.target).value)}
|
|
116
|
+
@change=${(e: Event) => updateVar(name, (e.target as HTMLInputElement).value)}
|
|
110
117
|
style="flex:1;max-width:160px"
|
|
111
118
|
></sp-textfield>
|
|
112
119
|
<sp-action-button quiet size="s" @click=${() => deleteVar(name)}>
|
|
@@ -122,11 +129,16 @@ function renderColorSection(vars, updateVar, deleteVar, addVar) {
|
|
|
122
129
|
|
|
123
130
|
/**
|
|
124
131
|
* @param {[string, string | number][]} vars
|
|
125
|
-
* @param {
|
|
126
|
-
* @param {
|
|
127
|
-
* @param {
|
|
132
|
+
* @param {(name: string, val: string) => void} updateVar
|
|
133
|
+
* @param {(name: string) => void} deleteVar
|
|
134
|
+
* @param {(prefix: string, friendlyName: string, val: string) => void} addVar
|
|
128
135
|
*/
|
|
129
|
-
function renderFontSection(
|
|
136
|
+
function renderFontSection(
|
|
137
|
+
vars: [string, string | number][],
|
|
138
|
+
updateVar: (name: string, val: string) => void,
|
|
139
|
+
deleteVar: (name: string) => void,
|
|
140
|
+
addVar: (prefix: string, friendlyName: string, val: string) => void,
|
|
141
|
+
) {
|
|
130
142
|
return html`
|
|
131
143
|
<div class="css-vars-group">
|
|
132
144
|
<h4 class="css-vars-group-title">Fonts</h4>
|
|
@@ -137,8 +149,7 @@ function renderFontSection(vars, updateVar, deleteVar, addVar) {
|
|
|
137
149
|
<sp-textfield
|
|
138
150
|
size="s"
|
|
139
151
|
.value=${String(val)}
|
|
140
|
-
@change=${(
|
|
141
|
-
updateVar(name, /** @type {HTMLInputElement} */ (e.target).value)}
|
|
152
|
+
@change=${(e: Event) => updateVar(name, (e.target as HTMLInputElement).value)}
|
|
142
153
|
style="flex:1"
|
|
143
154
|
></sp-textfield>
|
|
144
155
|
<sp-action-button quiet size="s" @click=${() => deleteVar(name)}>
|
|
@@ -157,13 +168,20 @@ function renderFontSection(vars, updateVar, deleteVar, addVar) {
|
|
|
157
168
|
|
|
158
169
|
/**
|
|
159
170
|
* @param {[string, string | number][]} vars
|
|
160
|
-
* @param {
|
|
161
|
-
* @param {
|
|
162
|
-
* @param {
|
|
171
|
+
* @param {(name: string, val: string) => void} updateVar
|
|
172
|
+
* @param {(name: string) => void} deleteVar
|
|
173
|
+
* @param {(prefix: string, friendlyName: string, val: string) => void} addVar
|
|
163
174
|
* @param {JxStyle} rootStyle
|
|
164
175
|
* @param {string[]} mediaNames
|
|
165
176
|
*/
|
|
166
|
-
function renderSizeSection(
|
|
177
|
+
function renderSizeSection(
|
|
178
|
+
vars: [string, string | number][],
|
|
179
|
+
updateVar: (name: string, val: string) => void,
|
|
180
|
+
deleteVar: (name: string) => void,
|
|
181
|
+
addVar: (prefix: string, friendlyName: string, val: string) => void,
|
|
182
|
+
rootStyle: JxStyle,
|
|
183
|
+
mediaNames: string[],
|
|
184
|
+
) {
|
|
167
185
|
return html`
|
|
168
186
|
<div class="css-vars-group">
|
|
169
187
|
<h4 class="css-vars-group-title">Sizes & Spacing</h4>
|
|
@@ -179,8 +197,7 @@ function renderSizeSection(vars, updateVar, deleteVar, addVar, rootStyle, mediaN
|
|
|
179
197
|
<sp-textfield
|
|
180
198
|
size="s"
|
|
181
199
|
.value=${String(val)}
|
|
182
|
-
@change=${(
|
|
183
|
-
updateVar(name, /** @type {HTMLInputElement} */ (e.target).value)}
|
|
200
|
+
@change=${(e: Event) => updateVar(name, (e.target as HTMLInputElement).value)}
|
|
184
201
|
style="max-width:120px"
|
|
185
202
|
></sp-textfield>
|
|
186
203
|
<sp-action-button quiet size="s" @click=${() => deleteVar(name)}>
|
|
@@ -197,13 +214,20 @@ function renderSizeSection(vars, updateVar, deleteVar, addVar, rootStyle, mediaN
|
|
|
197
214
|
|
|
198
215
|
/**
|
|
199
216
|
* @param {[string, string | number][]} vars
|
|
200
|
-
* @param {
|
|
201
|
-
* @param {
|
|
202
|
-
* @param {
|
|
217
|
+
* @param {(name: string, val: string) => void} updateVar
|
|
218
|
+
* @param {(name: string) => void} deleteVar
|
|
219
|
+
* @param {(prefix: string, friendlyName: string, val: string) => void} addVar
|
|
203
220
|
* @param {JxStyle} rootStyle
|
|
204
221
|
* @param {string[]} mediaNames
|
|
205
222
|
*/
|
|
206
|
-
function renderOtherSection(
|
|
223
|
+
function renderOtherSection(
|
|
224
|
+
vars: [string, string | number][],
|
|
225
|
+
updateVar: (name: string, val: string) => void,
|
|
226
|
+
deleteVar: (name: string) => void,
|
|
227
|
+
addVar: (prefix: string, friendlyName: string, val: string) => void,
|
|
228
|
+
rootStyle: JxStyle,
|
|
229
|
+
mediaNames: string[],
|
|
230
|
+
) {
|
|
207
231
|
return html`
|
|
208
232
|
<div class="css-vars-group">
|
|
209
233
|
<h4 class="css-vars-group-title">Other</h4>
|
|
@@ -214,8 +238,7 @@ function renderOtherSection(vars, updateVar, deleteVar, addVar, rootStyle, media
|
|
|
214
238
|
<sp-textfield
|
|
215
239
|
size="s"
|
|
216
240
|
.value=${String(val)}
|
|
217
|
-
@change=${(
|
|
218
|
-
updateVar(name, /** @type {HTMLInputElement} */ (e.target).value)}
|
|
241
|
+
@change=${(e: Event) => updateVar(name, (e.target as HTMLInputElement).value)}
|
|
219
242
|
style="flex:1"
|
|
220
243
|
></sp-textfield>
|
|
221
244
|
<sp-action-button quiet size="s" @click=${() => deleteVar(name)}>
|
|
@@ -235,7 +258,7 @@ function renderOtherSection(vars, updateVar, deleteVar, addVar, rootStyle, media
|
|
|
235
258
|
* @param {JxStyle} rootStyle
|
|
236
259
|
* @param {string[]} mediaNames
|
|
237
260
|
*/
|
|
238
|
-
function renderMediaOverrides(varName, rootStyle, mediaNames) {
|
|
261
|
+
function renderMediaOverrides(varName: string, rootStyle: JxStyle, mediaNames: string[]) {
|
|
239
262
|
const overrides = [];
|
|
240
263
|
for (const mediaName of mediaNames) {
|
|
241
264
|
const mediaKey = `@${mediaName}`;
|
|
@@ -256,10 +279,11 @@ function renderMediaOverrides(varName, rootStyle, mediaNames) {
|
|
|
256
279
|
<sp-textfield
|
|
257
280
|
size="s"
|
|
258
281
|
.value=${String(o.value)}
|
|
259
|
-
@change=${(
|
|
282
|
+
@change=${(e: Event) => {
|
|
260
283
|
if (!rootStyle[`@${o.mediaName}`]) rootStyle[`@${o.mediaName}`] = {};
|
|
261
|
-
|
|
262
|
-
|
|
284
|
+
(rootStyle[`@${o.mediaName}`] as Record<string, unknown>)[varName] = (
|
|
285
|
+
e.target as HTMLInputElement
|
|
286
|
+
).value;
|
|
263
287
|
updateSiteConfig({ style: { ...rootStyle } });
|
|
264
288
|
}}
|
|
265
289
|
style="max-width:120px"
|
|
@@ -275,13 +299,16 @@ function renderMediaOverrides(varName, rootStyle, mediaNames) {
|
|
|
275
299
|
* @param {string} prefix
|
|
276
300
|
* @param {string} placeholder
|
|
277
301
|
* @param {string} valuePlaceholder
|
|
278
|
-
* @param {
|
|
302
|
+
* @param {(prefix: string, friendlyName: string, val: string) => void} addVar
|
|
279
303
|
*/
|
|
280
|
-
function renderAddRow(
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
304
|
+
function renderAddRow(
|
|
305
|
+
prefix: string,
|
|
306
|
+
placeholder: string,
|
|
307
|
+
valuePlaceholder: string,
|
|
308
|
+
addVar: (prefix: string, friendlyName: string, val: string) => void,
|
|
309
|
+
) {
|
|
310
|
+
let nameEl: HTMLInputElement | null = null;
|
|
311
|
+
let valEl: HTMLInputElement | null = null;
|
|
285
312
|
|
|
286
313
|
return html`
|
|
287
314
|
<div class="css-var-add-row">
|
|
@@ -289,14 +316,14 @@ function renderAddRow(prefix, placeholder, valuePlaceholder, addVar) {
|
|
|
289
316
|
size="s"
|
|
290
317
|
placeholder=${placeholder}
|
|
291
318
|
${ref((el) => {
|
|
292
|
-
if (el) nameEl =
|
|
319
|
+
if (el) nameEl = el as HTMLInputElement;
|
|
293
320
|
})}
|
|
294
321
|
></sp-textfield>
|
|
295
322
|
<sp-textfield
|
|
296
323
|
size="s"
|
|
297
324
|
placeholder=${valuePlaceholder}
|
|
298
325
|
${ref((el) => {
|
|
299
|
-
if (el) valEl =
|
|
326
|
+
if (el) valEl = el as HTMLInputElement;
|
|
300
327
|
})}
|
|
301
328
|
></sp-textfield>
|
|
302
329
|
<sp-action-button
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/**
|
|
2
3
|
* Definitions Editor — visual editor for project-level $defs (JSON Schema type definitions).
|
|
3
4
|
*
|
|
@@ -7,19 +8,15 @@
|
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import { html, render as litRender } from "lit-html";
|
|
10
|
-
import { getPlatform } from "../platform
|
|
11
|
-
import { projectState } from "../store
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
schemaForType,
|
|
16
|
-
detectFieldFormat,
|
|
17
|
-
} from "./schema-field-ui.js";
|
|
11
|
+
import { getPlatform } from "../platform";
|
|
12
|
+
import { projectState } from "../store";
|
|
13
|
+
import { fieldCardTpl, addFieldFormTpl, schemaForType, detectFieldFormat } from "./schema-field-ui";
|
|
14
|
+
|
|
15
|
+
import type { ProjectConfig, ContentTypeSchema } from "@jxsuite/schema/types";
|
|
18
16
|
|
|
19
17
|
// ─── Module state ─────────────────────────────────────────────────────────────
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
let selectedDef = null;
|
|
19
|
+
let selectedDef: string | null = null;
|
|
23
20
|
let showAddField = false;
|
|
24
21
|
let newFieldState = { name: "", type: "string", format: "", required: false };
|
|
25
22
|
let showNewDef = false;
|
|
@@ -29,22 +26,22 @@ let newDefName = "";
|
|
|
29
26
|
|
|
30
27
|
async function saveProjectConfig() {
|
|
31
28
|
const platform = getPlatform();
|
|
32
|
-
const config =
|
|
29
|
+
const config = (projectState as { projectConfig: ProjectConfig }).projectConfig;
|
|
33
30
|
await platform.writeFile("project.json", JSON.stringify(config, null, "\t"));
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
37
34
|
|
|
38
35
|
/** Get the selected $def schema object. */
|
|
39
|
-
function getSelectedDef() {
|
|
36
|
+
function getSelectedDef(): ContentTypeSchema | undefined {
|
|
40
37
|
const config = projectState?.projectConfig;
|
|
41
|
-
return config?.$defs?.[
|
|
38
|
+
return config?.$defs?.[selectedDef as string] as ContentTypeSchema | undefined;
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
// ─── Handlers ─────────────────────────────────────────────────────────────────
|
|
45
42
|
|
|
46
43
|
/** @param {() => void} rerender */
|
|
47
|
-
function handleNewDef(rerender) {
|
|
44
|
+
function handleNewDef(rerender: () => void) {
|
|
48
45
|
const name = newDefName.trim();
|
|
49
46
|
if (!name) return;
|
|
50
47
|
|
|
@@ -67,7 +64,7 @@ function handleNewDef(rerender) {
|
|
|
67
64
|
}
|
|
68
65
|
|
|
69
66
|
/** @param {() => void} rerender */
|
|
70
|
-
function handleAddField(rerender) {
|
|
67
|
+
function handleAddField(rerender: () => void) {
|
|
71
68
|
const name = newFieldState.name.trim();
|
|
72
69
|
if (!name || !selectedDef) return;
|
|
73
70
|
|
|
@@ -92,13 +89,13 @@ function handleAddField(rerender) {
|
|
|
92
89
|
* @param {string} fieldName
|
|
93
90
|
* @param {() => void} rerender
|
|
94
91
|
*/
|
|
95
|
-
function handleDeleteField(fieldName, rerender) {
|
|
92
|
+
function handleDeleteField(fieldName: string, rerender: () => void) {
|
|
96
93
|
const def = getSelectedDef();
|
|
97
94
|
if (!def?.properties) return;
|
|
98
95
|
|
|
99
96
|
delete def.properties[fieldName];
|
|
100
97
|
if (def.required) {
|
|
101
|
-
def.required = def.required.filter((
|
|
98
|
+
def.required = def.required.filter((r: string) => r !== fieldName);
|
|
102
99
|
}
|
|
103
100
|
|
|
104
101
|
rerender();
|
|
@@ -109,7 +106,7 @@ function handleDeleteField(fieldName, rerender) {
|
|
|
109
106
|
* @param {string} fieldName
|
|
110
107
|
* @param {() => void} rerender
|
|
111
108
|
*/
|
|
112
|
-
function handleToggleRequired(fieldName, rerender) {
|
|
109
|
+
function handleToggleRequired(fieldName: string, rerender: () => void) {
|
|
113
110
|
const def = getSelectedDef();
|
|
114
111
|
if (!def) return;
|
|
115
112
|
if (!def.required) def.required = [];
|
|
@@ -127,19 +124,18 @@ function handleToggleRequired(fieldName, rerender) {
|
|
|
127
124
|
* @param {string} newName
|
|
128
125
|
* @param {() => void} rerender
|
|
129
126
|
*/
|
|
130
|
-
function handleRenameField(oldName, newName, rerender) {
|
|
127
|
+
function handleRenameField(oldName: string, newName: string, rerender: () => void) {
|
|
131
128
|
const def = getSelectedDef();
|
|
132
129
|
if (!def?.properties || !newName || def.properties[newName]) return;
|
|
133
130
|
|
|
134
|
-
|
|
135
|
-
const newProps = {};
|
|
131
|
+
const newProps: Record<string, unknown> = {};
|
|
136
132
|
for (const [key, val] of Object.entries(def.properties)) {
|
|
137
133
|
newProps[key === oldName ? newName : key] = val;
|
|
138
134
|
}
|
|
139
135
|
def.properties = newProps;
|
|
140
136
|
|
|
141
137
|
if (def.required) {
|
|
142
|
-
def.required = def.required.map((
|
|
138
|
+
def.required = def.required.map((r: string) => (r === oldName ? newName : r));
|
|
143
139
|
}
|
|
144
140
|
|
|
145
141
|
rerender();
|
|
@@ -151,7 +147,7 @@ function handleRenameField(oldName, newName, rerender) {
|
|
|
151
147
|
* @param {string} newType
|
|
152
148
|
* @param {() => void} rerender
|
|
153
149
|
*/
|
|
154
|
-
function handleChangeType(fieldName, newType, rerender) {
|
|
150
|
+
function handleChangeType(fieldName: string, newType: string, rerender: () => void) {
|
|
155
151
|
const def = getSelectedDef();
|
|
156
152
|
if (!def?.properties?.[fieldName]) return;
|
|
157
153
|
|
|
@@ -169,7 +165,7 @@ function handleChangeType(fieldName, newType, rerender) {
|
|
|
169
165
|
* @param {string} format
|
|
170
166
|
* @param {() => void} rerender
|
|
171
167
|
*/
|
|
172
|
-
function handleChangeFormat(fieldName, format, rerender) {
|
|
168
|
+
function handleChangeFormat(fieldName: string, format: string, rerender: () => void) {
|
|
173
169
|
const def = getSelectedDef();
|
|
174
170
|
if (!def?.properties?.[fieldName]) return;
|
|
175
171
|
|
|
@@ -187,7 +183,11 @@ function handleChangeFormat(fieldName, format, rerender) {
|
|
|
187
183
|
* @param {{ name: string; type: string; required: boolean }} fieldState
|
|
188
184
|
* @param {() => void} rerender
|
|
189
185
|
*/
|
|
190
|
-
function handleAddNestedField(
|
|
186
|
+
function handleAddNestedField(
|
|
187
|
+
parentName: string,
|
|
188
|
+
fieldState: { name: string; type: string; required: boolean },
|
|
189
|
+
rerender: () => void,
|
|
190
|
+
) {
|
|
191
191
|
const def = getSelectedDef();
|
|
192
192
|
const parent = def?.properties?.[parentName];
|
|
193
193
|
if (!parent) return;
|
|
@@ -209,14 +209,14 @@ function handleAddNestedField(parentName, fieldState, rerender) {
|
|
|
209
209
|
* @param {string} childName
|
|
210
210
|
* @param {() => void} rerender
|
|
211
211
|
*/
|
|
212
|
-
function handleDeleteNested(parentName, childName, rerender) {
|
|
212
|
+
function handleDeleteNested(parentName: string, childName: string, rerender: () => void) {
|
|
213
213
|
const def = getSelectedDef();
|
|
214
214
|
const parent = def?.properties?.[parentName];
|
|
215
215
|
if (!parent?.properties) return;
|
|
216
216
|
|
|
217
217
|
delete parent.properties[childName];
|
|
218
218
|
if (parent.required) {
|
|
219
|
-
parent.required = parent.required.filter((
|
|
219
|
+
parent.required = parent.required.filter((r: string) => r !== childName);
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
rerender();
|
|
@@ -228,7 +228,7 @@ function handleDeleteNested(parentName, childName, rerender) {
|
|
|
228
228
|
* @param {string} childName
|
|
229
229
|
* @param {() => void} rerender
|
|
230
230
|
*/
|
|
231
|
-
function handleToggleNestedRequired(parentName, childName, rerender) {
|
|
231
|
+
function handleToggleNestedRequired(parentName: string, childName: string, rerender: () => void) {
|
|
232
232
|
const def = getSelectedDef();
|
|
233
233
|
const parent = def?.properties?.[parentName];
|
|
234
234
|
if (!parent) return;
|
|
@@ -248,22 +248,24 @@ function handleToggleNestedRequired(parentName, childName, rerender) {
|
|
|
248
248
|
* @param {string} newChild
|
|
249
249
|
* @param {() => void} rerender
|
|
250
250
|
*/
|
|
251
|
-
function handleRenameNested(
|
|
251
|
+
function handleRenameNested(
|
|
252
|
+
parentName: string,
|
|
253
|
+
oldChild: string,
|
|
254
|
+
newChild: string,
|
|
255
|
+
rerender: () => void,
|
|
256
|
+
) {
|
|
252
257
|
const def = getSelectedDef();
|
|
253
258
|
const parent = def?.properties?.[parentName];
|
|
254
259
|
if (!parent?.properties || !newChild || parent.properties[newChild]) return;
|
|
255
260
|
|
|
256
|
-
|
|
257
|
-
const newProps = {};
|
|
261
|
+
const newProps: Record<string, unknown> = {};
|
|
258
262
|
for (const [key, val] of Object.entries(parent.properties)) {
|
|
259
263
|
newProps[key === oldChild ? newChild : key] = val;
|
|
260
264
|
}
|
|
261
265
|
parent.properties = newProps;
|
|
262
266
|
|
|
263
267
|
if (parent.required) {
|
|
264
|
-
parent.required = parent.required.map((
|
|
265
|
-
r === oldChild ? newChild : r,
|
|
266
|
-
);
|
|
268
|
+
parent.required = parent.required.map((r: string) => (r === oldChild ? newChild : r));
|
|
267
269
|
}
|
|
268
270
|
|
|
269
271
|
rerender();
|
|
@@ -276,7 +278,12 @@ function handleRenameNested(parentName, oldChild, newChild, rerender) {
|
|
|
276
278
|
* @param {string} newType
|
|
277
279
|
* @param {() => void} rerender
|
|
278
280
|
*/
|
|
279
|
-
function handleChangeNestedType(
|
|
281
|
+
function handleChangeNestedType(
|
|
282
|
+
parentName: string,
|
|
283
|
+
childName: string,
|
|
284
|
+
newType: string,
|
|
285
|
+
rerender: () => void,
|
|
286
|
+
) {
|
|
280
287
|
const def = getSelectedDef();
|
|
281
288
|
const parent = def?.properties?.[parentName];
|
|
282
289
|
if (!parent?.properties?.[childName]) return;
|
|
@@ -296,7 +303,12 @@ function handleChangeNestedType(parentName, childName, newType, rerender) {
|
|
|
296
303
|
* @param {string} format
|
|
297
304
|
* @param {() => void} rerender
|
|
298
305
|
*/
|
|
299
|
-
function handleChangeNestedFormat(
|
|
306
|
+
function handleChangeNestedFormat(
|
|
307
|
+
parentName: string,
|
|
308
|
+
childName: string,
|
|
309
|
+
format: string,
|
|
310
|
+
rerender: () => void,
|
|
311
|
+
) {
|
|
300
312
|
const def = getSelectedDef();
|
|
301
313
|
const parent = def?.properties?.[parentName];
|
|
302
314
|
if (!parent?.properties?.[childName]) return;
|
|
@@ -309,7 +321,7 @@ function handleChangeNestedFormat(parentName, childName, format, rerender) {
|
|
|
309
321
|
}
|
|
310
322
|
|
|
311
323
|
/** @param {() => void} rerender */
|
|
312
|
-
function handleDeleteDef(rerender) {
|
|
324
|
+
function handleDeleteDef(rerender: () => void) {
|
|
313
325
|
if (!selectedDef) return;
|
|
314
326
|
const config = projectState?.projectConfig;
|
|
315
327
|
if (!config?.$defs?.[selectedDef]) return;
|
|
@@ -328,7 +340,7 @@ function handleDeleteDef(rerender) {
|
|
|
328
340
|
*
|
|
329
341
|
* @param {HTMLElement} container
|
|
330
342
|
*/
|
|
331
|
-
export function renderDefsEditor(container) {
|
|
343
|
+
export function renderDefsEditor(container: HTMLElement) {
|
|
332
344
|
const rerender = () => renderDefsEditor(container);
|
|
333
345
|
const config = projectState?.projectConfig;
|
|
334
346
|
const defs = config?.$defs || {};
|
|
@@ -359,10 +371,10 @@ export function renderDefsEditor(container) {
|
|
|
359
371
|
size="s"
|
|
360
372
|
placeholder="TypeName"
|
|
361
373
|
.value=${newDefName}
|
|
362
|
-
@input=${(
|
|
363
|
-
newDefName =
|
|
374
|
+
@input=${(e: Event) => {
|
|
375
|
+
newDefName = (e.target as HTMLInputElement).value;
|
|
364
376
|
}}
|
|
365
|
-
@keydown=${(
|
|
377
|
+
@keydown=${(e: KeyboardEvent) => {
|
|
366
378
|
if (e.key === "Enter") handleNewDef(rerender);
|
|
367
379
|
if (e.key === "Escape") {
|
|
368
380
|
showNewDef = false;
|
|
@@ -395,29 +407,31 @@ export function renderDefsEditor(container) {
|
|
|
395
407
|
if (!selectedDef || !defs[selectedDef]) {
|
|
396
408
|
editorTpl = html`<div class="settings-empty-state">Select or create a type definition</div>`;
|
|
397
409
|
} else {
|
|
398
|
-
const def = defs[selectedDef];
|
|
410
|
+
const def = defs[selectedDef] as ContentTypeSchema;
|
|
399
411
|
const properties = def.properties || {};
|
|
400
412
|
const required = def.required || [];
|
|
401
413
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
onDeleteNested: (p, c) => handleDeleteNested(p, c, rerender),
|
|
411
|
-
onToggleNestedRequired: (p, c) => handleToggleNestedRequired(p, c, rerender),
|
|
412
|
-
onRenameNested: (p, o, n) => handleRenameNested(p, o, n, rerender),
|
|
413
|
-
onChangeNestedType: (p, c, t) =>
|
|
414
|
-
|
|
414
|
+
const handlers: import("./schema-field-ui.js").FieldHandlers = {
|
|
415
|
+
onDelete: (n: string) => handleDeleteField(n, rerender),
|
|
416
|
+
onToggleRequired: (n: string) => handleToggleRequired(n, rerender),
|
|
417
|
+
onRename: (oldN: string, newN: string) => handleRenameField(oldN, newN, rerender),
|
|
418
|
+
onChangeType: (n: string, t: string) => handleChangeType(n, t, rerender),
|
|
419
|
+
onChangeFormat: (n: string, f: string) => handleChangeFormat(n, f, rerender),
|
|
420
|
+
onAddNestedField: (p: string, s: { name: string; type: string; required: boolean }) =>
|
|
421
|
+
handleAddNestedField(p, s, rerender),
|
|
422
|
+
onDeleteNested: (p: string, c: string) => handleDeleteNested(p, c, rerender),
|
|
423
|
+
onToggleNestedRequired: (p: string, c: string) => handleToggleNestedRequired(p, c, rerender),
|
|
424
|
+
onRenameNested: (p: string, o: string, n: string) => handleRenameNested(p, o, n, rerender),
|
|
425
|
+
onChangeNestedType: (p: string, c: string, t: string) =>
|
|
426
|
+
handleChangeNestedType(p, c, t, rerender),
|
|
427
|
+
onChangeNestedFormat: (p: string, c: string, f: string) =>
|
|
428
|
+
handleChangeNestedFormat(p, c, f, rerender),
|
|
415
429
|
};
|
|
416
430
|
|
|
417
431
|
const fieldCards = Object.entries(properties).map(([name, fieldDef]) =>
|
|
418
432
|
fieldCardTpl(
|
|
419
433
|
name,
|
|
420
|
-
|
|
434
|
+
fieldDef as import("./schema-field-ui.js").SchemaProperty,
|
|
421
435
|
required.includes(name),
|
|
422
436
|
handlers,
|
|
423
437
|
),
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/** General settings section — favicon, platform adapter, breakpoints, and other site-wide config. */
|
|
2
3
|
|
|
3
4
|
import { html, render as litRender, nothing } from "lit-html";
|
|
4
|
-
import { projectState } from "../store
|
|
5
|
-
import { updateSiteConfig } from "../site-context
|
|
6
|
-
import { getPlatform } from "../platform
|
|
7
|
-
import { openFileInTab } from "../files/files
|
|
8
|
-
import { closeSettingsModal } from "./settings-modal
|
|
5
|
+
import { projectState } from "../store";
|
|
6
|
+
import { updateSiteConfig } from "../site-context";
|
|
7
|
+
import { getPlatform } from "../platform";
|
|
8
|
+
import { openFileInTab } from "../files/files";
|
|
9
|
+
import { closeSettingsModal } from "./settings-modal";
|
|
10
|
+
|
|
11
|
+
import type { ProjectConfig } from "@jxsuite/schema/types";
|
|
9
12
|
|
|
10
13
|
/** @param {HTMLElement} container */
|
|
11
|
-
export function renderGeneralSettings(container) {
|
|
12
|
-
const config =
|
|
14
|
+
export function renderGeneralSettings(container: HTMLElement) {
|
|
15
|
+
const config = (projectState?.projectConfig || {}) as ProjectConfig;
|
|
13
16
|
|
|
14
17
|
const onFaviconUpload = async () => {
|
|
15
18
|
const input = document.createElement("input");
|
|
@@ -26,8 +29,8 @@ export function renderGeneralSettings(container) {
|
|
|
26
29
|
input.click();
|
|
27
30
|
};
|
|
28
31
|
|
|
29
|
-
const onAdapterChange = (
|
|
30
|
-
updateSiteConfig({ build: { ...config.build, adapter: e.target.value } });
|
|
32
|
+
const onAdapterChange = (e: Event) => {
|
|
33
|
+
updateSiteConfig({ build: { ...config.build, adapter: (e.target as HTMLInputElement).value } });
|
|
31
34
|
};
|
|
32
35
|
|
|
33
36
|
const onEditGlobalStyles = () => {
|
|
@@ -37,29 +40,27 @@ export function renderGeneralSettings(container) {
|
|
|
37
40
|
|
|
38
41
|
// ─── Breakpoints ($media) ───────────────────────────────────────────────────
|
|
39
42
|
|
|
40
|
-
const media =
|
|
43
|
+
const media = (config.$media || {}) as Record<string, string>;
|
|
41
44
|
const mediaEntries = Object.entries(media);
|
|
42
45
|
|
|
43
|
-
const onMediaValueChange =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
};
|
|
46
|
+
const onMediaValueChange = (key: string) => (e: Event) => {
|
|
47
|
+
const updated = { ...media, [key]: (e.target as HTMLInputElement).value };
|
|
48
|
+
updateSiteConfig({ $media: updated });
|
|
49
|
+
};
|
|
48
50
|
|
|
49
|
-
const onMediaNameChange =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
};
|
|
51
|
+
const onMediaNameChange = (oldKey: string) => (e: Event) => {
|
|
52
|
+
const rawName = (e.target as HTMLInputElement).value.trim();
|
|
53
|
+
const newKey = rawName.startsWith("--") ? rawName : `--${rawName}`;
|
|
54
|
+
if (newKey === oldKey) return;
|
|
55
|
+
const updated = {} as Record<string, string>;
|
|
56
|
+
for (const [k, v] of Object.entries(media)) {
|
|
57
|
+
updated[k === oldKey ? newKey : k] = v;
|
|
58
|
+
}
|
|
59
|
+
updateSiteConfig({ $media: updated });
|
|
60
|
+
renderGeneralSettings(container);
|
|
61
|
+
};
|
|
61
62
|
|
|
62
|
-
const onRemoveBreakpoint = (
|
|
63
|
+
const onRemoveBreakpoint = (key: string) => () => {
|
|
63
64
|
const updated = { ...media };
|
|
64
65
|
delete updated[key];
|
|
65
66
|
updateSiteConfig({ $media: updated });
|