@jxsuite/studio 0.10.2 → 0.11.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/README.md +82 -0
- package/dist/studio.js +1001 -147
- package/dist/studio.js.map +28 -18
- package/package.json +3 -2
- package/src/browse/browse.js +27 -3
- package/src/canvas/canvas-live-render.js +133 -15
- package/src/canvas/canvas-render.js +2 -1
- package/src/panels/canvas-dnd.js +132 -50
- package/src/panels/overlays.js +15 -3
- package/src/panels/panel-events.js +10 -0
- package/src/panels/properties-panel.js +299 -5
- package/src/panels/stylebook-panel.js +31 -2
- package/src/settings/content-types-editor.js +22 -1
- package/src/settings/schema-field-ui.js +57 -4
- package/src/site-context.js +105 -0
- package/src/store.js +9 -1
- package/src/ui/spectrum.js +2 -0
- package/src/view.js +3 -0
package/src/site-context.js
CHANGED
|
@@ -109,6 +109,111 @@ export function getEffectiveHead(docHead) {
|
|
|
109
109
|
return merged;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
// ─── Layout resolution ──────────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
/** @type {Map<string, any>} */
|
|
115
|
+
const layoutCache = new Map();
|
|
116
|
+
|
|
117
|
+
export function invalidateLayoutCache() {
|
|
118
|
+
layoutCache.clear();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Determine the effective layout path for a document.
|
|
123
|
+
*
|
|
124
|
+
* @param {any} docLayout - The document's $layout value (string path, false, or undefined)
|
|
125
|
+
* @returns {string | null} The layout path, or null if no layout applies
|
|
126
|
+
*/
|
|
127
|
+
export function getEffectiveLayoutPath(docLayout) {
|
|
128
|
+
if (docLayout === false) return null;
|
|
129
|
+
const defaultLayout = projectState?.projectConfig?.defaults?.layout;
|
|
130
|
+
return docLayout || defaultLayout || null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Resolve a layout document by path. Fetches and caches the parsed JSON.
|
|
135
|
+
*
|
|
136
|
+
* @param {string} layoutPath - Relative path to the layout file (e.g., "./layouts/base.json")
|
|
137
|
+
* @returns {Promise<any | null>} The parsed layout document, or null on failure
|
|
138
|
+
*/
|
|
139
|
+
export async function resolveLayoutDoc(layoutPath) {
|
|
140
|
+
const normalized = layoutPath.replace(/^\.\//, "");
|
|
141
|
+
if (layoutCache.has(normalized)) return structuredClone(layoutCache.get(normalized));
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const platform = getPlatform();
|
|
145
|
+
const content = await platform.readFile(normalized);
|
|
146
|
+
const doc = JSON.parse(content);
|
|
147
|
+
layoutCache.set(normalized, doc);
|
|
148
|
+
return structuredClone(doc);
|
|
149
|
+
} catch {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Distribute page children into a layout document's <slot> elements. Returns the merged document
|
|
156
|
+
* with page content injected into slots.
|
|
157
|
+
*
|
|
158
|
+
* @param {any} layoutDoc - Deep-cloned layout document
|
|
159
|
+
* @param {any} pageDoc - The page document
|
|
160
|
+
* @returns {any} The merged document
|
|
161
|
+
*/
|
|
162
|
+
export function distributePageIntoLayout(layoutDoc, pageDoc) {
|
|
163
|
+
const pageChildren = pageDoc.children ?? [];
|
|
164
|
+
const children = typeof pageChildren === "string" ? [pageChildren] : pageChildren;
|
|
165
|
+
|
|
166
|
+
const named = new Map();
|
|
167
|
+
const defaults = [];
|
|
168
|
+
|
|
169
|
+
for (const child of children) {
|
|
170
|
+
if (child && typeof child === "object" && child.attributes?.slot) {
|
|
171
|
+
const slotName = child.attributes.slot;
|
|
172
|
+
if (!named.has(slotName)) named.set(slotName, []);
|
|
173
|
+
named.get(slotName).push(child);
|
|
174
|
+
} else {
|
|
175
|
+
defaults.push(child);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
fillSlots(layoutDoc, named, defaults);
|
|
180
|
+
|
|
181
|
+
if (pageDoc.state) layoutDoc.state = { ...layoutDoc.state, ...pageDoc.state };
|
|
182
|
+
if (pageDoc.$media) layoutDoc.$media = { ...layoutDoc.$media, ...pageDoc.$media };
|
|
183
|
+
if (pageDoc.style) layoutDoc.style = { ...layoutDoc.style, ...pageDoc.style };
|
|
184
|
+
if (pageDoc.attributes) layoutDoc.attributes = { ...layoutDoc.attributes, ...pageDoc.attributes };
|
|
185
|
+
|
|
186
|
+
return layoutDoc;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function fillSlots(
|
|
190
|
+
/** @type {any} */ node,
|
|
191
|
+
/** @type {Map<string, any[]>} */ named,
|
|
192
|
+
/** @type {any[]} */ defaults,
|
|
193
|
+
) {
|
|
194
|
+
if (!node || typeof node !== "object") return;
|
|
195
|
+
if (!Array.isArray(node.children)) return;
|
|
196
|
+
|
|
197
|
+
const newChildren = [];
|
|
198
|
+
for (const child of node.children) {
|
|
199
|
+
if (child && typeof child === "object" && child.tagName === "slot") {
|
|
200
|
+
const slotName = child.attributes?.name;
|
|
201
|
+
if (slotName && named.has(slotName)) {
|
|
202
|
+
newChildren.push(.../** @type {any[]} */ (named.get(slotName)));
|
|
203
|
+
named.delete(slotName);
|
|
204
|
+
} else if (!slotName && defaults.length > 0) {
|
|
205
|
+
newChildren.push(...defaults);
|
|
206
|
+
} else if (child.children) {
|
|
207
|
+
newChildren.push(...child.children);
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
fillSlots(child, named, defaults);
|
|
211
|
+
newChildren.push(child);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
node.children = newChildren;
|
|
215
|
+
}
|
|
216
|
+
|
|
112
217
|
/**
|
|
113
218
|
* Update the project's project.json with a partial patch and persist to disk.
|
|
114
219
|
*
|
package/src/store.js
CHANGED
|
@@ -164,7 +164,15 @@ export function stripEventHandlers(node) {
|
|
|
164
164
|
const cases = {};
|
|
165
165
|
for (const [ck, cv] of Object.entries(v)) cases[ck] = stripEventHandlers(cv);
|
|
166
166
|
out.cases = cases;
|
|
167
|
-
} else if (k === "state"
|
|
167
|
+
} else if (k === "state" && typeof v === "object" && v !== null) {
|
|
168
|
+
/** @type {Record<string, any>} */
|
|
169
|
+
const state = {};
|
|
170
|
+
for (const [sk, sv] of Object.entries(v)) {
|
|
171
|
+
if (sv && typeof sv === "object" && sv.timing === "server") continue;
|
|
172
|
+
state[sk] = sv;
|
|
173
|
+
}
|
|
174
|
+
out.state = state;
|
|
175
|
+
} else if (k === "style" || k === "attributes" || k === "$media") {
|
|
168
176
|
out[k] = v;
|
|
169
177
|
} else {
|
|
170
178
|
out[k] = v;
|
package/src/ui/spectrum.js
CHANGED
|
@@ -47,6 +47,7 @@ import { PickerButton } from "@spectrum-web-components/picker-button/src/PickerB
|
|
|
47
47
|
import { Accordion } from "@spectrum-web-components/accordion/src/Accordion.js";
|
|
48
48
|
import { AccordionItem } from "@spectrum-web-components/accordion/src/AccordionItem.js";
|
|
49
49
|
import { ActionBar } from "@spectrum-web-components/action-bar/src/ActionBar.js";
|
|
50
|
+
import { Toast } from "@spectrum-web-components/toast/src/Toast.js";
|
|
50
51
|
import { Table } from "@spectrum-web-components/table/src/Table.js";
|
|
51
52
|
import { TableHead } from "@spectrum-web-components/table/src/TableHead.js";
|
|
52
53
|
import { TableHeadCell } from "@spectrum-web-components/table/src/TableHeadCell.js";
|
|
@@ -180,6 +181,7 @@ const components = [
|
|
|
180
181
|
["sp-accordion", Accordion],
|
|
181
182
|
["sp-accordion-item", AccordionItem],
|
|
182
183
|
["sp-action-bar", ActionBar],
|
|
184
|
+
["sp-toast", Toast],
|
|
183
185
|
["sp-table", Table],
|
|
184
186
|
["sp-table-head", TableHead],
|
|
185
187
|
["sp-table-head-cell", TableHeadCell],
|