smoodly 0.0.16 → 0.0.17
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 +156 -21
- package/dist/admin/client-ops.js +1 -1
- package/dist/admin/custom-section.d.ts +10 -0
- package/dist/admin/custom-section.js +10 -0
- package/dist/admin/editor/CanvasOverlay.d.ts +4 -4
- package/dist/admin/editor/CanvasOverlay.js +5 -5
- package/dist/admin/editor/EditorView.js +140 -108
- package/dist/admin/editor/GlobalInspector.d.ts +24 -0
- package/dist/admin/editor/{SharedInspector.js → GlobalInspector.js} +4 -4
- package/dist/admin/editor/InsertPicker.d.ts +6 -5
- package/dist/admin/editor/InsertPicker.js +6 -4
- package/dist/admin/editor/InspectorFields.d.ts +21 -0
- package/dist/admin/editor/InspectorFields.js +22 -0
- package/dist/admin/editor/Outline.d.ts +0 -1
- package/dist/admin/editor/Outline.js +3 -3
- package/dist/admin/editor/{shared-edit.d.ts → global-edit.d.ts} +10 -10
- package/dist/admin/editor/{shared-edit.js → global-edit.js} +4 -4
- package/dist/admin/editor/useGlobalEdit.d.ts +6 -0
- package/dist/admin/editor/{useSharedEdit.js → useGlobalEdit.js} +4 -4
- package/dist/admin/forms/FieldWidget.d.ts +14 -1
- package/dist/admin/forms/FieldWidget.js +38 -11
- package/dist/admin/forms/ListField.d.ts +9 -1
- package/dist/admin/forms/ListField.js +16 -8
- package/dist/admin/forms/RichtextElementCard.js +11 -5
- package/dist/admin/forms/RowsWidget.d.ts +13 -0
- package/dist/admin/forms/RowsWidget.js +26 -0
- package/dist/admin/forms/SpanBar.d.ts +5 -0
- package/dist/admin/forms/SpanBar.js +37 -0
- package/dist/admin/forms/richtext-insert.d.ts +2 -2
- package/dist/admin/forms/richtext-insert.js +3 -3
- package/dist/admin/forms/richtext-nodes.js +1 -1
- package/dist/admin/forms/tabs.d.ts +15 -9
- package/dist/admin/forms/tabs.js +19 -23
- package/dist/admin/forms/useRichtextEditor.js +8 -1
- package/dist/admin/global-items.d.ts +9 -0
- package/dist/admin/{shared-items.js → global-items.js} +17 -16
- package/dist/admin/index.d.ts +3 -3
- package/dist/admin/index.js +1 -1
- package/dist/admin/ops-impl.js +65 -54
- package/dist/admin/ops.d.ts +6 -6
- package/dist/admin/serialize.d.ts +18 -6
- package/dist/admin/serialize.js +27 -4
- package/dist/admin/shell/AdminApp.js +6 -6
- package/dist/admin/shell/GlobalFields.d.ts +24 -0
- package/dist/admin/shell/GlobalFields.js +19 -0
- package/dist/admin/shell/{SharedForm.d.ts → GlobalForm.d.ts} +1 -1
- package/dist/admin/shell/{SharedForm.js → GlobalForm.js} +36 -26
- package/dist/admin/shell/{SharedList.d.ts → GlobalList.d.ts} +1 -1
- package/dist/admin/shell/{SharedList.js → GlobalList.js} +9 -9
- package/dist/admin/shell/IconRail.js +13 -6
- package/dist/admin/shell/PagesList.js +1 -6
- package/dist/admin/shell/{shared-list.d.ts → global-list.d.ts} +3 -3
- package/dist/admin/shell/{shared-list.js → global-list.js} +2 -2
- package/dist/admin/tree-ops.d.ts +10 -8
- package/dist/admin/tree-ops.js +28 -16
- package/dist/admin/ui/HistoryModal.js +1 -1
- package/dist/admin/ui/primitives.d.ts +3 -1
- package/dist/admin/ui/primitives.js +2 -2
- package/dist/admin/ui/theme.js +54 -14
- package/dist/admin/validate.d.ts +27 -2
- package/dist/admin/validate.js +47 -10
- package/dist/collections.d.ts +3 -3
- package/dist/collections.js +1 -1
- package/dist/conditions.d.ts +33 -0
- package/dist/conditions.js +79 -0
- package/dist/config.d.ts +26 -19
- package/dist/config.js +26 -25
- package/dist/css.d.ts +7 -0
- package/dist/css.js +57 -0
- package/dist/custom/CustomSection.d.ts +26 -0
- package/dist/custom/CustomSection.js +20 -0
- package/dist/custom/css.d.ts +3 -0
- package/dist/custom/css.js +28 -0
- package/dist/custom/index.d.ts +26 -0
- package/dist/custom/index.js +16 -0
- package/dist/custom/rows.d.ts +28 -0
- package/dist/custom/rows.js +129 -0
- package/dist/custom/schema.d.ts +32 -0
- package/dist/custom/schema.js +39 -0
- package/dist/entry-store.d.ts +4 -4
- package/dist/entry-store.js +17 -17
- package/dist/fields.d.ts +5 -2
- package/dist/fields.js +4 -2
- package/dist/{shared-id.d.ts → global-id.d.ts} +1 -1
- package/dist/{shared-id.js → global-id.js} +4 -4
- package/dist/global.d.ts +57 -0
- package/dist/global.js +51 -0
- package/dist/index.d.ts +28 -18
- package/dist/index.js +10 -5
- package/dist/media.js +1 -1
- package/dist/next/index.d.ts +1 -1
- package/dist/next/page-renderer.d.ts +4 -2
- package/dist/next/page-renderer.js +9 -9
- package/dist/next/queries.d.ts +6 -6
- package/dist/next/queries.js +7 -7
- package/dist/page.js +2 -2
- package/dist/pairing.d.ts +13 -7
- package/dist/pairing.js +4 -4
- package/dist/refs.d.ts +2 -2
- package/dist/refs.js +4 -4
- package/dist/render.d.ts +13 -1
- package/dist/render.js +10 -5
- package/dist/resolve.d.ts +11 -6
- package/dist/resolve.js +22 -12
- package/dist/revalidate.d.ts +4 -4
- package/dist/revalidate.js +4 -4
- package/dist/richtext-render.js +2 -2
- package/dist/richtext-walk.d.ts +5 -5
- package/dist/richtext-walk.js +4 -4
- package/dist/schema.d.ts +15 -10
- package/dist/schema.js +19 -7
- package/dist/section-wrapper.d.ts +18 -6
- package/dist/section-wrapper.js +24 -17
- package/dist/site.d.ts +15 -5
- package/dist/site.js +24 -10
- package/dist/sql-space.js +1 -1
- package/dist/sql.js +2 -2
- package/dist/store.d.ts +7 -0
- package/dist/store.js +6 -0
- package/dist/styles.d.ts +27 -0
- package/dist/styles.js +60 -0
- package/dist/supabase-entry-store.js +14 -14
- package/dist/supabase-store.js +2 -1
- package/dist/zones.d.ts +9 -7
- package/dist/zones.js +5 -5
- package/package.json +1 -1
- package/dist/admin/editor/SharedInspector.d.ts +0 -23
- package/dist/admin/editor/useSharedEdit.d.ts +0 -6
- package/dist/admin/shared-items.d.ts +0 -9
- package/dist/admin/shell/SharedFields.d.ts +0 -21
- package/dist/admin/shell/SharedFields.js +0 -15
- package/dist/shared.d.ts +0 -53
- package/dist/shared.js +0 -45
package/dist/section-wrapper.js
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import { createElement } from "react";
|
|
2
2
|
import { NODE_ATTR } from "./marks.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
3
|
+
import { DEFAULT_STYLE_VALUES, inlineStyles } from "./styles.js";
|
|
4
|
+
import { safeCss, scopedCss } from "./css.js";
|
|
5
|
+
// The root wrapper every section renders through (spec 2026-09-14 §2, §4).
|
|
6
|
+
// Owns the real root element, no extra div: element choice via `as`,
|
|
7
|
+
// Smoodly's styles set rendered INLINE (an editor's step beats the
|
|
8
|
+
// section's own class rule with no ordering and no !important), the
|
|
9
|
+
// node id as data-smd-node always (the custom-CSS scope needs it on the
|
|
10
|
+
// live site), the scoped <style> child, and every other prop spread onto
|
|
11
|
+
// the root — settings never become attributes; the view writes its own.
|
|
12
|
+
/** The reserved key `<Zone>` and `getGlobal` ride on the styles object. */
|
|
13
|
+
export const SMD_KEY = "__smd";
|
|
14
|
+
export function Section({ as = "section", styles, className, style, children, ...rest }) {
|
|
15
|
+
const { [SMD_KEY]: smd, ...steps } = (styles ?? {});
|
|
16
|
+
const inline = inlineStyles(steps, smd?.values ?? DEFAULT_STYLE_VALUES);
|
|
17
|
+
const merged = { ...(style ?? {}), ...inline };
|
|
18
|
+
const attrs = { ...rest };
|
|
19
|
+
if (smd?.node)
|
|
20
|
+
attrs[NODE_ATTR] = smd.node;
|
|
21
|
+
if (className !== undefined)
|
|
22
|
+
attrs.className = className;
|
|
23
|
+
if (Object.keys(merged).length > 0)
|
|
24
|
+
attrs.style = merged;
|
|
25
|
+
const css = smd?.node ? safeCss(smd.css) : "";
|
|
26
|
+
return createElement(as, attrs, smd?.layoutCss ? createElement("style", { key: "smd-layout" }, smd.layoutCss) : null, css ? createElement("style", { key: "smd-css" }, scopedCss(smd.node, css)) : null, children);
|
|
20
27
|
}
|
package/dist/site.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ import { type EntryRecord, type EntryStore, type Where } from "./entry-store.ts"
|
|
|
5
5
|
import { type LocaleSet, type PathTarget } from "./paths.ts";
|
|
6
6
|
import type { SectionLike } from "./refs.ts";
|
|
7
7
|
import type { PageContext, PageLink, PageTree } from "./render.tsx";
|
|
8
|
-
import { type
|
|
8
|
+
import { type GlobalSchema } from "./global.ts";
|
|
9
|
+
import { type StyleValues, type StyleValuesInput } from "./styles.ts";
|
|
10
|
+
import { type CustomConfigInput, type ResolvedCustom } from "./custom/index.ts";
|
|
9
11
|
import type { PageRecord, PageStore } from "./store.ts";
|
|
10
12
|
export type SiteOptions = {
|
|
11
13
|
stores: () => {
|
|
@@ -17,9 +19,13 @@ export type SiteOptions = {
|
|
|
17
19
|
sections: SectionLike[];
|
|
18
20
|
/** Registered Elements a richtext document may embed (spec 2026-09-08 block nodes §2). */
|
|
19
21
|
elements?: SectionLike[];
|
|
20
|
-
|
|
22
|
+
globals?: GlobalSchema[];
|
|
21
23
|
locales: LocaleSet;
|
|
22
24
|
homePageSlug?: string;
|
|
25
|
+
/** The CSS values behind Smoodly's styles set (spec 2026-09-14 §2); the package fallbacks fill the rest. */
|
|
26
|
+
styles?: StyleValuesInput;
|
|
27
|
+
/** The custom section's options (spec 2026-09-14 §6); the defaults when absent. */
|
|
28
|
+
custom?: CustomConfigInput;
|
|
23
29
|
/** Page shapes (entry pages are ignored): the source of each template's page-field descriptors (spec 2026-09-10 page fields §3). */
|
|
24
30
|
pages?: ReadonlyArray<SitePageSchema>;
|
|
25
31
|
};
|
|
@@ -90,6 +96,10 @@ export type LoadOptions = {
|
|
|
90
96
|
export interface SmoodlySite {
|
|
91
97
|
readonly locales: LocaleSet;
|
|
92
98
|
readonly homePageSlug: string;
|
|
99
|
+
/** The resolved styles values the renderer and `getGlobal` hand the wrapper. */
|
|
100
|
+
readonly styles: StyleValues;
|
|
101
|
+
/** The custom section's resolved options the renderer hands renderPage. */
|
|
102
|
+
readonly custom: ResolvedCustom;
|
|
93
103
|
/** One index hit: what lives at (locale, path). Uncached by design — the id it returns is the cache tag. */
|
|
94
104
|
locate(locale: string, path: string): Promise<PathTarget | null>;
|
|
95
105
|
/** Everything the renderer needs for one target: null when it is a folder, unpublished (outside draft), or gone. */
|
|
@@ -112,11 +122,11 @@ export interface SmoodlySite {
|
|
|
112
122
|
locale?: string;
|
|
113
123
|
draft?: boolean;
|
|
114
124
|
}): Promise<EntryTreeItem[]>;
|
|
115
|
-
/** A
|
|
125
|
+
/** A global item's content in a locale (spec 2026-09-07 §4): an object
|
|
116
126
|
* item's merged fields, or a visual item's section props (fields plus
|
|
117
|
-
* `
|
|
127
|
+
* `settings`), refs hydrated; null when the item has no row there or is
|
|
118
128
|
* unpublished (outside draft mode). */
|
|
119
|
-
|
|
129
|
+
getGlobal<V>(item: GlobalSchema<V>, options?: {
|
|
120
130
|
locale?: string;
|
|
121
131
|
draft?: boolean;
|
|
122
132
|
}): Promise<V | null>;
|
package/dist/site.js
CHANGED
|
@@ -2,7 +2,10 @@ import { ARRAY_FIELD_TYPES, entryFieldsIn, localizedKeys, schemaOf } from "./ent
|
|
|
2
2
|
import { buildPageTree, chainOf, entryPathRows, entrySegment, pagePath, } from "./paths.js";
|
|
3
3
|
import { resolveFields, resolveMedia, resolveMediaSets, resolveTree } from "./resolve.js";
|
|
4
4
|
import { elementSchemas } from "./richtext-walk.js";
|
|
5
|
-
import {
|
|
5
|
+
import { globalNodeId, SETTINGS_KEY, STYLES_KEY, CSS_KEY } from "./global.js";
|
|
6
|
+
import { SMD_KEY } from "./section-wrapper.js";
|
|
7
|
+
import { resolveStyleValues } from "./styles.js";
|
|
8
|
+
import { resolveCustom } from "./custom/index.js";
|
|
6
9
|
import { withDefaults } from "./field-defaults.js";
|
|
7
10
|
/** The `page` prop for a loaded page. `toUrl` is the app's `href` seam
|
|
8
11
|
* bound to the render locale; the default keeps CMS paths as URLs. */
|
|
@@ -21,6 +24,8 @@ export function pageContextOf(hit, toUrl = (p) => p) {
|
|
|
21
24
|
export function createSmoodlySite(options) {
|
|
22
25
|
const locales = options.locales;
|
|
23
26
|
const homePageSlug = options.homePageSlug ?? "home";
|
|
27
|
+
const styles = resolveStyleValues(options.styles);
|
|
28
|
+
const custom = resolveCustom(options.custom);
|
|
24
29
|
const elements = elementSchemas(options.elements);
|
|
25
30
|
const pathOpts = { locales, homePageSlug };
|
|
26
31
|
const mounted = options.collections.filter((c) => c.path !== undefined);
|
|
@@ -33,9 +38,9 @@ export function createSmoodlySite(options) {
|
|
|
33
38
|
const assets = options.stores().assets;
|
|
34
39
|
return assets ? (ids) => assets.getMany(ids) : undefined;
|
|
35
40
|
};
|
|
36
|
-
const
|
|
41
|
+
const sectionSettings = (name) => {
|
|
37
42
|
const s = options.sections.find((x) => ("schema" in x ? x.schema : x).name === name);
|
|
38
|
-
return (s && ("schema" in s ? s.schema : s).
|
|
43
|
+
return (s && ("schema" in s ? s.schema : s).settings) ?? {};
|
|
39
44
|
};
|
|
40
45
|
/** Visible to the public in a locale: present there, and a folder or a
|
|
41
46
|
* published page. Draft mode sees every present node. Presence is the
|
|
@@ -108,11 +113,12 @@ export function createSmoodlySite(options) {
|
|
|
108
113
|
const tree = await resolveTree(raw, {
|
|
109
114
|
sections: options.sections,
|
|
110
115
|
collections: options.collections,
|
|
111
|
-
|
|
116
|
+
globals: options.globals,
|
|
112
117
|
elements: options.elements,
|
|
113
118
|
fetch: fetcher(draft, locale),
|
|
114
119
|
assets: assetFetcher(),
|
|
115
120
|
page: { fields: pageFields(record.template) },
|
|
121
|
+
custom,
|
|
116
122
|
});
|
|
117
123
|
const all = await pages.listPages();
|
|
118
124
|
const index = new Map(all.map((n) => [n.id, n]));
|
|
@@ -267,7 +273,7 @@ export function createSmoodlySite(options) {
|
|
|
267
273
|
const build = (parentId) => items.filter((i) => i.entry.parentId === parentId).map((i) => ({ ...i, children: build(i.entry.id) }));
|
|
268
274
|
return build(null);
|
|
269
275
|
},
|
|
270
|
-
async
|
|
276
|
+
async getGlobal(item, o) {
|
|
271
277
|
const locale = localeOf(o);
|
|
272
278
|
const draft = o?.draft ?? false;
|
|
273
279
|
const { entries } = options.stores();
|
|
@@ -285,18 +291,26 @@ export function createSmoodlySite(options) {
|
|
|
285
291
|
const fields = await resolveFields(raw, item.fields, { collections: options.collections, elements: options.elements, fetch: fetcher(draft, locale), assets: assetFetcher() });
|
|
286
292
|
if (!item.section)
|
|
287
293
|
return fields;
|
|
288
|
-
const { [STYLES_KEY]:
|
|
294
|
+
const { [SETTINGS_KEY]: settings, [STYLES_KEY]: steps, [CSS_KEY]: css, ...rest } = fields;
|
|
289
295
|
const fetch = assetFetcher();
|
|
290
|
-
const own =
|
|
296
|
+
const own = settings !== null && typeof settings === "object" ? settings : {};
|
|
291
297
|
if (fetch)
|
|
292
|
-
await resolveMedia(own,
|
|
293
|
-
// The layout node id
|
|
294
|
-
|
|
298
|
+
await resolveMedia(own, sectionSettings(item.section), fetch, elements);
|
|
299
|
+
// The layout node id, the css and the values ride the styles under the reserved key (spec 2026-09-13 §1, 2026-09-14 §2, §4):
|
|
300
|
+
// the wrapper strips it and emits data-smd-node, so the canvas selects the item and the css reaches it.
|
|
301
|
+
const stepsObject = steps !== null && typeof steps === "object" ? steps : {};
|
|
302
|
+
return {
|
|
303
|
+
...rest,
|
|
304
|
+
settings: own,
|
|
305
|
+
styles: { ...stepsObject, [SMD_KEY]: { node: globalNodeId(item.name), css: typeof css === "string" ? css : undefined, values: styles } },
|
|
306
|
+
};
|
|
295
307
|
},
|
|
296
308
|
};
|
|
297
309
|
const site = {
|
|
298
310
|
locales,
|
|
299
311
|
homePageSlug,
|
|
312
|
+
styles,
|
|
313
|
+
custom,
|
|
300
314
|
locate: (locale, path) => options.stores().pages.resolvePath(locale, path),
|
|
301
315
|
load(target, locale, path, opts = {}) {
|
|
302
316
|
const draft = opts.draft ?? false;
|
package/dist/sql-space.js
CHANGED
|
@@ -31,7 +31,7 @@ $$;`.trim();
|
|
|
31
31
|
}
|
|
32
32
|
/** Which rows a read key (or the anon key, which has no claim) may see.
|
|
33
33
|
* null = write keys only — refs is editor tooling; editors is admission
|
|
34
|
-
* data; nothing a browser needs.
|
|
34
|
+
* data; nothing a browser needs. Global content (spec 2026-09-07) rides
|
|
35
35
|
* the three entry predicates: it is stored as entries. */
|
|
36
36
|
const PUBLISHED_READ = {
|
|
37
37
|
pages: `exists (select 1 from "page_locales" l where l."page_id" = "pages"."id" and l."status" = 'published')`,
|
package/dist/sql.js
CHANGED
|
@@ -31,7 +31,7 @@ function fieldExpr(key, d, source) {
|
|
|
31
31
|
case "date":
|
|
32
32
|
return `(${source}->>'${key}')::timestamptz`;
|
|
33
33
|
default:
|
|
34
|
-
return `${source}->'${key}'`; // image, video, file, object, list, richtext,
|
|
34
|
+
return `${source}->'${key}'`; // image, video, file, object, list, richtext, refList
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
export function collectionSQL(collection) {
|
|
@@ -179,7 +179,7 @@ create index if not exists "entry_locales_status_idx"
|
|
|
179
179
|
-- A version belongs to ONE locale of one entry and stores the MERGED
|
|
180
180
|
-- fields (shared + localized + slug) as they were at that save, so
|
|
181
181
|
-- restoring an old Finnish version restores the shared fields too.
|
|
182
|
-
-- Rows for every collection and every
|
|
182
|
+
-- Rows for every collection and every global item (spec 2026-09-14 §1).
|
|
183
183
|
-- One row per publish plus the live draft (spec 2026-09-13 history §1).
|
|
184
184
|
create table if not exists "entry_versions" (
|
|
185
185
|
"id" uuid primary key default gen_random_uuid(),
|
package/dist/store.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PageTree } from "./render.tsx";
|
|
2
2
|
import type { EntryStore } from "./entry-store.ts";
|
|
3
3
|
import { type RefEdge, type SectionLike } from "./refs.ts";
|
|
4
|
+
import type { Toolset } from "./toolset.ts";
|
|
4
5
|
import type { MemoryRefIndex } from "./ref-index.ts";
|
|
5
6
|
import { type PathIndex } from "./path-index.ts";
|
|
6
7
|
import { type LocaleSet, type PagePathOptions, type PathTarget } from "./paths.ts";
|
|
@@ -107,6 +108,10 @@ export type PageStoreOptions = {
|
|
|
107
108
|
sections?: SectionLike[];
|
|
108
109
|
/** Registered Elements a richtext document may embed (spec 2026-09-08 block nodes §2). */
|
|
109
110
|
elements?: SectionLike[];
|
|
111
|
+
/** The custom section's columns toolset (spec 2026-09-14 §6); with `sections` given, the custom schema always joins the ref walk. */
|
|
112
|
+
custom?: {
|
|
113
|
+
toolset?: Toolset;
|
|
114
|
+
};
|
|
110
115
|
entries?: EntryStore;
|
|
111
116
|
/** The shared URL index (one instance for pages AND entries). */
|
|
112
117
|
paths?: PathIndex;
|
|
@@ -117,6 +122,8 @@ export type PageStoreOptions = {
|
|
|
117
122
|
};
|
|
118
123
|
};
|
|
119
124
|
export declare const DEFAULT_LOCALES: LocaleSet;
|
|
125
|
+
/** The section list every adapter walks refs with: the registered ones plus the custom section's schema (spec 2026-09-14 §6). */
|
|
126
|
+
export declare function refSections(options: PageStoreOptions): SectionLike[] | undefined;
|
|
120
127
|
/** The options every adapter resolves the same way. */
|
|
121
128
|
export declare function pathOptions(options: PageStoreOptions): PagePathOptions & {
|
|
122
129
|
depth: number;
|
package/dist/store.js
CHANGED
|
@@ -11,9 +11,14 @@
|
|
|
11
11
|
// locale — presence IS the row.
|
|
12
12
|
import { ASSET_COLLECTION } from "./media.js";
|
|
13
13
|
import { collectTreeRefs } from "./refs.js";
|
|
14
|
+
import { customToolset, withCustomSchema } from "./custom/index.js";
|
|
14
15
|
import { MemoryPathIndex } from "./path-index.js";
|
|
15
16
|
import { assertDepth, assertSegment, assertSupportedLocale, chainOf, isHomeNode, pagePathRows, unsupportedLocale, } from "./paths.js";
|
|
16
17
|
export const DEFAULT_LOCALES = { default: "en", supported: ["en"] };
|
|
18
|
+
/** The section list every adapter walks refs with: the registered ones plus the custom section's schema (spec 2026-09-14 §6). */
|
|
19
|
+
export function refSections(options) {
|
|
20
|
+
return options.sections ? withCustomSchema(options.sections, customToolset(options.custom?.toolset)) : undefined;
|
|
21
|
+
}
|
|
17
22
|
/** The options every adapter resolves the same way. */
|
|
18
23
|
export function pathOptions(options) {
|
|
19
24
|
return {
|
|
@@ -98,6 +103,7 @@ export class MemoryPageStore {
|
|
|
98
103
|
/** Append-only creation-order tie-break; `size + 1` stays fresh only because entries are never deleted. */
|
|
99
104
|
this.seq = new Map();
|
|
100
105
|
this.byId = (id) => this.pages.get(id);
|
|
106
|
+
this.options = { ...options, sections: refSections(options) };
|
|
101
107
|
this.paths = options.paths ?? new MemoryPathIndex();
|
|
102
108
|
this.opts = pathOptions(options);
|
|
103
109
|
}
|
package/dist/styles.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Descriptor } from "./fields.ts";
|
|
2
|
+
export type StyleValues = {
|
|
3
|
+
spacing: Record<string, string>;
|
|
4
|
+
width: Record<string, string>;
|
|
5
|
+
};
|
|
6
|
+
export type StyleValuesInput = {
|
|
7
|
+
spacing?: Record<string, string>;
|
|
8
|
+
width?: Record<string, string>;
|
|
9
|
+
};
|
|
10
|
+
export declare const DEFAULT_STYLE_VALUES: StyleValues;
|
|
11
|
+
/** The "0" step: always present, always `0`, never a config key. */
|
|
12
|
+
export declare const SPACING_NONE = "none";
|
|
13
|
+
export declare const ALIGN_STEPS: readonly ["left", "center", "right"];
|
|
14
|
+
export declare const STYLE_KEYS: readonly ["spacingTop", "spacingBottom", "width", "align"];
|
|
15
|
+
export type StyleKey = (typeof STYLE_KEYS)[number];
|
|
16
|
+
/** What a view receives: steps, not CSS. A config may add steps, so the strings stay open. */
|
|
17
|
+
export type StyleProps = {
|
|
18
|
+
spacingTop?: string;
|
|
19
|
+
spacingBottom?: string;
|
|
20
|
+
width?: string;
|
|
21
|
+
align?: (typeof ALIGN_STEPS)[number];
|
|
22
|
+
};
|
|
23
|
+
export declare function resolveStyleValues(input?: StyleValuesInput): StyleValues;
|
|
24
|
+
/** The set as admin descriptors: optional radios (the empty pill is Default), product labels. */
|
|
25
|
+
export declare function styleDescriptors(values: StyleValues, omit?: readonly string[]): Record<string, Descriptor>;
|
|
26
|
+
/** The inline CSS for a node's steps. Unknown steps and non-strings render nothing. */
|
|
27
|
+
export declare function inlineStyles(steps: Record<string, unknown> | undefined, values: StyleValues): Record<string, string | number>;
|
package/dist/styles.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export const DEFAULT_STYLE_VALUES = {
|
|
2
|
+
spacing: { sm: "1.5rem", md: "3.5rem", lg: "5.5rem" },
|
|
3
|
+
width: { narrow: "40rem", wide: "90rem", full: "none" },
|
|
4
|
+
};
|
|
5
|
+
/** The "0" step: always present, always `0`, never a config key. */
|
|
6
|
+
export const SPACING_NONE = "none";
|
|
7
|
+
export const ALIGN_STEPS = ["left", "center", "right"];
|
|
8
|
+
export const STYLE_KEYS = ["spacingTop", "spacingBottom", "width", "align"];
|
|
9
|
+
export function resolveStyleValues(input) {
|
|
10
|
+
const spacing = { ...DEFAULT_STYLE_VALUES.spacing, ...(input?.spacing ?? {}) };
|
|
11
|
+
if (SPACING_NONE in spacing)
|
|
12
|
+
throw new Error(`smoodly: styles.spacing cannot define "${SPACING_NONE}" — the 0 step is always 0.`);
|
|
13
|
+
const width = { ...DEFAULT_STYLE_VALUES.width, ...(input?.width ?? {}) };
|
|
14
|
+
for (const [group, map] of Object.entries({ spacing, width })) {
|
|
15
|
+
for (const [k, v] of Object.entries(map)) {
|
|
16
|
+
if (typeof v !== "string" || v === "")
|
|
17
|
+
throw new Error(`smoodly: styles.${group}.${k} must be a CSS value.`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return { spacing, width };
|
|
21
|
+
}
|
|
22
|
+
const SPACING_LABELS = { [SPACING_NONE]: "0", sm: "S", md: "M", lg: "L" };
|
|
23
|
+
const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
24
|
+
const radio = (options) => ({ type: "radio", optional: true, options });
|
|
25
|
+
/** The set as admin descriptors: optional radios (the empty pill is Default), product labels. */
|
|
26
|
+
export function styleDescriptors(values, omit = []) {
|
|
27
|
+
const spacingOptions = () => [
|
|
28
|
+
{ value: SPACING_NONE, label: "0" },
|
|
29
|
+
...Object.keys(values.spacing).map((k) => ({ value: k, label: SPACING_LABELS[k] ?? k.toUpperCase() })),
|
|
30
|
+
];
|
|
31
|
+
const all = {
|
|
32
|
+
spacingTop: radio(spacingOptions()),
|
|
33
|
+
spacingBottom: radio(spacingOptions()),
|
|
34
|
+
width: radio(Object.keys(values.width).map((k) => ({ value: k, label: capitalize(k) }))),
|
|
35
|
+
align: radio(ALIGN_STEPS.map((k) => ({ value: k, label: capitalize(k) }))),
|
|
36
|
+
};
|
|
37
|
+
const out = {};
|
|
38
|
+
for (const key of STYLE_KEYS)
|
|
39
|
+
if (!omit.includes(key))
|
|
40
|
+
out[key] = all[key];
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
/** The inline CSS for a node's steps. Unknown steps and non-strings render nothing. */
|
|
44
|
+
export function inlineStyles(steps, values) {
|
|
45
|
+
const out = {};
|
|
46
|
+
if (!steps)
|
|
47
|
+
return out;
|
|
48
|
+
const spacing = (v) => v === SPACING_NONE ? 0 : typeof v === "string" && v in values.spacing ? values.spacing[v] : undefined;
|
|
49
|
+
const top = spacing(steps.spacingTop);
|
|
50
|
+
if (top !== undefined)
|
|
51
|
+
out.paddingTop = top;
|
|
52
|
+
const bottom = spacing(steps.spacingBottom);
|
|
53
|
+
if (bottom !== undefined)
|
|
54
|
+
out.paddingBottom = bottom;
|
|
55
|
+
if (typeof steps.width === "string" && steps.width in values.width)
|
|
56
|
+
out["--smd-width"] = values.width[steps.width];
|
|
57
|
+
if (typeof steps.align === "string" && ALIGN_STEPS.includes(steps.align))
|
|
58
|
+
out.textAlign = steps.align;
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
// calls the smoodly_reorder SQL function: one statement, updated_at
|
|
8
8
|
// untouched.
|
|
9
9
|
import { collectionDepth, collectionOrder } from "./collections.js";
|
|
10
|
-
import { ARRAY_FIELD_TYPES, DEFAULT_ENTRY_LOCALES, entryErrors, entryFieldsIn, entryLocaleRow, entryRefEdges, entryTitleIn,
|
|
11
|
-
import {
|
|
10
|
+
import { ARRAY_FIELD_TYPES, DEFAULT_ENTRY_LOCALES, entryErrors, entryFieldsIn, entryLocaleRow, entryRefEdges, entryTitleIn, isGlobalSchema, schemaOf, splitFields, } from "./entry-store.js";
|
|
11
|
+
import { globalEntryId } from "./global-id.js";
|
|
12
12
|
import { SupabasePathIndex } from "./supabase-path-index.js";
|
|
13
13
|
import { assertDepth, assertSupportedLocale, entryPathRows, slugOf } from "./paths.js";
|
|
14
14
|
// entries.id is a Postgres uuid column: filtering on a non-UUID string
|
|
@@ -48,7 +48,7 @@ export class SupabaseEntryStore {
|
|
|
48
48
|
this.db = db;
|
|
49
49
|
this.paths = options.paths ?? new SupabasePathIndex(db);
|
|
50
50
|
this.locales = options.locales ?? DEFAULT_ENTRY_LOCALES;
|
|
51
|
-
this.schemas = [...collections, ...(options.
|
|
51
|
+
this.schemas = [...collections, ...(options.globals ?? [])];
|
|
52
52
|
this.elements = options.elements;
|
|
53
53
|
}
|
|
54
54
|
fail(message) {
|
|
@@ -293,7 +293,7 @@ export class SupabaseEntryStore {
|
|
|
293
293
|
const schema = this.schema(collection);
|
|
294
294
|
assertSupportedLocale(options.locale, this.locales);
|
|
295
295
|
const parentId = options.parentId ?? null;
|
|
296
|
-
if (
|
|
296
|
+
if (isGlobalSchema(schema)) {
|
|
297
297
|
const existing = await this.db.from("entries").select("id").eq("collection", collection).limit(1);
|
|
298
298
|
if (existing.error)
|
|
299
299
|
this.fail(existing.error.message);
|
|
@@ -306,17 +306,17 @@ export class SupabaseEntryStore {
|
|
|
306
306
|
this.assertParentHasLocale(parent, options.locale);
|
|
307
307
|
const { shared, localized, slug } = splitFields(fields, schema);
|
|
308
308
|
await this.assertSiblingSlug(collection, parentId, options.locale, slug);
|
|
309
|
-
// A
|
|
309
|
+
// A global item's row id is DERIVED from (space, name), not generated
|
|
310
310
|
// (spec 2026-09-07 §10): the SELECT above is a fast path two racing
|
|
311
311
|
// materializations can both pass, so the primary key is the backstop
|
|
312
312
|
// — a duplicate would be unremovable, since `delete` refuses every
|
|
313
|
-
//
|
|
313
|
+
// global row.
|
|
314
314
|
const row = { collection, parent_id: parentId, fields: shared };
|
|
315
|
-
if (
|
|
316
|
-
row.id = await
|
|
315
|
+
if (isGlobalSchema(schema))
|
|
316
|
+
row.id = await globalEntryId(await this.space(), collection);
|
|
317
317
|
const inserted = await this.db.from("entries").insert(row).select("id").single();
|
|
318
318
|
if (inserted.error) {
|
|
319
|
-
if (
|
|
319
|
+
if (isGlobalSchema(schema) && inserted.error.code === UNIQUE_VIOLATION)
|
|
320
320
|
throw entryErrors.singleton(collection);
|
|
321
321
|
this.fail(inserted.error.message);
|
|
322
322
|
}
|
|
@@ -423,7 +423,7 @@ export class SupabaseEntryStore {
|
|
|
423
423
|
const row = entryLocaleRow(before, locale);
|
|
424
424
|
await this.ensureLiveSnapshot(before, locale);
|
|
425
425
|
const { shared, localized, slug } = splitFields(fields, schema);
|
|
426
|
-
if (
|
|
426
|
+
if (isGlobalSchema(schema) && slug !== null)
|
|
427
427
|
throw entryErrors.noSlug(collection);
|
|
428
428
|
await this.assertSiblingSlug(collection, before.parentId, locale, slug, id);
|
|
429
429
|
await this.updateNode(id, { fields: shared });
|
|
@@ -511,7 +511,7 @@ export class SupabaseEntryStore {
|
|
|
511
511
|
}
|
|
512
512
|
async removeLocale(collection, id, locale) {
|
|
513
513
|
const schema = this.schema(collection);
|
|
514
|
-
if (
|
|
514
|
+
if (isGlobalSchema(schema))
|
|
515
515
|
throw entryErrors.everyLocale(collection);
|
|
516
516
|
const entry = await this.must(collection, id);
|
|
517
517
|
entryLocaleRow(entry, locale);
|
|
@@ -575,7 +575,7 @@ export class SupabaseEntryStore {
|
|
|
575
575
|
return res.data ? toVersion(res.data) : null;
|
|
576
576
|
}
|
|
577
577
|
async move(collection, id, to) {
|
|
578
|
-
if (
|
|
578
|
+
if (isGlobalSchema(this.schema(collection)))
|
|
579
579
|
throw entryErrors.owned(collection, "it has no tree position");
|
|
580
580
|
const entry = await this.must(collection, id);
|
|
581
581
|
const subtree = await this.subtree(entry);
|
|
@@ -622,7 +622,7 @@ export class SupabaseEntryStore {
|
|
|
622
622
|
const entry = await this.fetch(collection, id);
|
|
623
623
|
if (!entry)
|
|
624
624
|
return;
|
|
625
|
-
if (
|
|
625
|
+
if (isGlobalSchema(this.schema(collection)))
|
|
626
626
|
throw entryErrors.owned(collection, "remove the registration to remove it");
|
|
627
627
|
const children = await this.db.from("entries").select("id").eq("parent_id", id).limit(1);
|
|
628
628
|
if (children.error)
|
|
@@ -649,7 +649,7 @@ export class SupabaseEntryStore {
|
|
|
649
649
|
return (await this.fetch(collection, id)) ? this.paths.pathsOf({ kind: "entry", id }) : {};
|
|
650
650
|
}
|
|
651
651
|
async reorder(collection, orderedIds) {
|
|
652
|
-
if (
|
|
652
|
+
if (isGlobalSchema(this.schema(collection)))
|
|
653
653
|
throw entryErrors.owned(collection, "it has no order");
|
|
654
654
|
const res = await this.db.rpc("smoodly_reorder", { p_collection: collection, p_ids: orderedIds });
|
|
655
655
|
if (res.error)
|
package/dist/supabase-store.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// are two indexed lookups. Every read embeds the locale rows
|
|
7
7
|
// (`*, page_locales(*)`) so a record arrives whole. Path rows are
|
|
8
8
|
// rewritten through ONE RPC per write, so a collision rolls back.
|
|
9
|
-
import { assertPublishable, assertSupportedLocale, localeRow, pageErrors, pageRefEdges, pathOptions, } from "./store.js";
|
|
9
|
+
import { assertPublishable, assertSupportedLocale, localeRow, pageErrors, pageRefEdges, pathOptions, refSections, } from "./store.js";
|
|
10
10
|
import { SupabasePathIndex } from "./supabase-path-index.js";
|
|
11
11
|
import { assertDepth, assertSegment, isHomeNode, pagePathRows } from "./paths.js";
|
|
12
12
|
const SELECT = "*, page_locales(*)";
|
|
@@ -40,6 +40,7 @@ export class SupabasePageStore {
|
|
|
40
40
|
constructor(db, options = {}) {
|
|
41
41
|
this.db = db;
|
|
42
42
|
this.options = options;
|
|
43
|
+
this.options = { ...options, sections: refSections(options) };
|
|
43
44
|
this.paths = options.paths ?? new SupabasePathIndex(db);
|
|
44
45
|
this.opts = pathOptions(options);
|
|
45
46
|
}
|
package/dist/zones.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { GlobalSchema } from "./global.ts";
|
|
2
2
|
type WithSchema = {
|
|
3
3
|
schema: {
|
|
4
4
|
name: string;
|
|
@@ -8,16 +8,18 @@ export type ZonePolicy = {
|
|
|
8
8
|
kind: "sections";
|
|
9
9
|
allow: string[];
|
|
10
10
|
max?: number;
|
|
11
|
-
}
|
|
11
|
+
}
|
|
12
|
+
/** `custom`: whether editors may add a custom section here (spec 2026-09-14 §6). */
|
|
13
|
+
| {
|
|
12
14
|
kind: "freeform";
|
|
13
15
|
allow: string[] | "*";
|
|
14
|
-
|
|
16
|
+
custom: boolean;
|
|
15
17
|
}
|
|
16
|
-
/** `
|
|
18
|
+
/** `global` names the item a bound zone shows (spec 2026-09-07 §4); `component` is its section either way. */
|
|
17
19
|
| {
|
|
18
20
|
kind: "locked";
|
|
19
21
|
component: string;
|
|
20
|
-
|
|
22
|
+
global?: string;
|
|
21
23
|
};
|
|
22
24
|
export declare const z: {
|
|
23
25
|
sections(components: WithSchema[]): {
|
|
@@ -28,8 +30,8 @@ export declare const z: {
|
|
|
28
30
|
};
|
|
29
31
|
freeform(opts: {
|
|
30
32
|
sections: WithSchema[] | "*";
|
|
31
|
-
|
|
33
|
+
custom?: boolean;
|
|
32
34
|
}): ZonePolicy;
|
|
33
|
-
locked(target: WithSchema |
|
|
35
|
+
locked(target: WithSchema | GlobalSchema): ZonePolicy;
|
|
34
36
|
};
|
|
35
37
|
export {};
|
package/dist/zones.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Zone policies — the freedom dial. Policies reference paired components
|
|
2
2
|
// directly; only their registered names are stored (plain data).
|
|
3
|
-
const
|
|
3
|
+
const isGlobal = (target) => "kind" in target && target.kind === "global";
|
|
4
4
|
const names = (components) => components.map((c) => c.schema.name);
|
|
5
5
|
export const z = {
|
|
6
6
|
sections(components) {
|
|
@@ -15,14 +15,14 @@ export const z = {
|
|
|
15
15
|
return {
|
|
16
16
|
kind: "freeform",
|
|
17
17
|
allow: opts.sections === "*" ? "*" : names(opts.sections),
|
|
18
|
-
|
|
18
|
+
custom: opts.custom ?? false,
|
|
19
19
|
};
|
|
20
20
|
},
|
|
21
21
|
locked(target) {
|
|
22
|
-
if (
|
|
22
|
+
if (isGlobal(target)) {
|
|
23
23
|
if (!target.section)
|
|
24
|
-
throw new Error(`smoodly:
|
|
25
|
-
return { kind: "locked", component: target.section,
|
|
24
|
+
throw new Error(`smoodly: global item "${target.name}" has no section — a locked zone needs a visual item.`);
|
|
25
|
+
return { kind: "locked", component: target.section, global: target.name };
|
|
26
26
|
}
|
|
27
27
|
return { kind: "locked", component: target.schema.name };
|
|
28
28
|
},
|
package/package.json
CHANGED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { AdminShared } from "../serialize.ts";
|
|
2
|
-
import type { TabEntry } from "../forms/tabs.ts";
|
|
3
|
-
import type { SharedEditItem } from "./shared-edit.ts";
|
|
4
|
-
import { type RefOption } from "../shell/SharedFields.tsx";
|
|
5
|
-
import { type PublishBusy } from "../ui/PublishControl.tsx";
|
|
6
|
-
export declare function SharedInspector({ item, edit, locale, entries, multilingual, refOptions, inlinePath, onEditHere, onChange, pending, busy, onPublish, onDiscard, onUnpublish, }: {
|
|
7
|
-
item: AdminShared;
|
|
8
|
-
/** null while the item loads. */
|
|
9
|
-
edit: SharedEditItem | null;
|
|
10
|
-
locale: string;
|
|
11
|
-
entries: TabEntry[];
|
|
12
|
-
multilingual: boolean;
|
|
13
|
-
refOptions: Record<string, RefOption[]>;
|
|
14
|
-
inlinePath?: string;
|
|
15
|
-
onEditHere?(): void;
|
|
16
|
-
onChange(ns: "fields" | "styles", key: string, value: unknown): void;
|
|
17
|
-
/** An edit the store has not confirmed (dirty, or the item's autosave not in `saved`). */
|
|
18
|
-
pending: boolean;
|
|
19
|
-
busy: PublishBusy;
|
|
20
|
-
onPublish(): void;
|
|
21
|
-
onDiscard(): void;
|
|
22
|
-
onUnpublish(): void;
|
|
23
|
-
}): import("react").JSX.Element;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { AdminOps } from "../ops.ts";
|
|
2
|
-
import { type SharedEdit, type SharedEditState } from "./shared-edit.ts";
|
|
3
|
-
export declare function useSharedEdit({ ops, onSaved }: {
|
|
4
|
-
ops: Pick<AdminOps, "shared">;
|
|
5
|
-
onSaved?: () => void;
|
|
6
|
-
}): SharedEditState & Pick<SharedEdit, "load" | "patch" | "flush" | "setStatus" | "reset">;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { ResolvedConfig } from "../config.ts";
|
|
2
|
-
import type { Descriptor } from "../fields.ts";
|
|
3
|
-
import type { EntryRecord, EntryStore } from "../entry-store.ts";
|
|
4
|
-
import { type SharedSchema } from "../shared.ts";
|
|
5
|
-
/** The merged fields a fresh item starts from: sampled field values, and
|
|
6
|
-
* for a visual item `$styles` = the resolved style defaults with the
|
|
7
|
-
* sample's style values on top. Unsampled fields stay absent. */
|
|
8
|
-
export declare function initialSharedFields(item: SharedSchema, resolvedStyles: Record<string, Descriptor>): Record<string, unknown>;
|
|
9
|
-
export declare function ensureSharedItems(entries: EntryStore, config: ResolvedConfig): Promise<Record<string, EntryRecord>>;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { AdminShared } from "../serialize.ts";
|
|
2
|
-
import type { TabEntry } from "../forms/tabs.ts";
|
|
3
|
-
export type RefOption = {
|
|
4
|
-
id: string;
|
|
5
|
-
title: string;
|
|
6
|
-
status: string;
|
|
7
|
-
};
|
|
8
|
-
export declare function SharedFields({ item, entries, fields, errors, refOptions, multilingual, inlinePath, onEditHere, onChange }: {
|
|
9
|
-
item: AdminShared;
|
|
10
|
-
entries: TabEntry[];
|
|
11
|
-
/** The merged draft fields; a visual item's styles under `$styles`. */
|
|
12
|
-
fields: Record<string, unknown>;
|
|
13
|
-
errors?: Record<string, string>;
|
|
14
|
-
/** Loaded ref options by target collection. */
|
|
15
|
-
refOptions: Record<string, RefOption[]>;
|
|
16
|
-
multilingual: boolean;
|
|
17
|
-
/** The content field with an active inline session: its richtext widget yields to a note. */
|
|
18
|
-
inlinePath?: string;
|
|
19
|
-
onEditHere?(): void;
|
|
20
|
-
onChange(ns: "fields" | "styles", key: string, value: unknown): void;
|
|
21
|
-
}): import("react").JSX.Element;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { STYLES_KEY } from "../../shared.js";
|
|
4
|
-
import { FieldWidget } from "../forms/FieldWidget.js";
|
|
5
|
-
import { UnlocalizedMark } from "../ui/primitives.js";
|
|
6
|
-
export function SharedFields({ item, entries, fields, errors, refOptions, multilingual, inlinePath, onEditHere, onChange }) {
|
|
7
|
-
const styles = (fields[STYLES_KEY] ?? {});
|
|
8
|
-
return (_jsx(_Fragment, { children: entries.map(({ key, ns, descriptor }) => {
|
|
9
|
-
if (ns === "meta")
|
|
10
|
-
return null; // a shared item has no meta namespace
|
|
11
|
-
const target = (descriptor.type === "ref" || descriptor.type === "refList") && typeof descriptor.target === "string" ? descriptor.target : null;
|
|
12
|
-
const localized = ns === "fields" && item.fields[key]?.localized === true;
|
|
13
|
-
return (_jsx(FieldWidget, { fieldKey: key, descriptor: descriptor, value: ns === "fields" ? fields[key] : styles[key], errors: errors, refOptions: target ? refOptions[target] ?? [] : undefined, mark: ns === "fields" && multilingual && !localized ? _jsx(UnlocalizedMark, {}) : undefined, inlinePath: ns === "fields" ? inlinePath : undefined, onEditHere: onEditHere, onChange: (value) => onChange(ns, key, value) }, `${ns}.${key}`));
|
|
14
|
-
}) }));
|
|
15
|
-
}
|