@elytracms/studio 0.0.57 → 0.0.59
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/components/ai/ai-chat-panel.js +44 -6
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/ai/chat-ui.d.ts +1 -1
- package/dist/components/ai/chat-ui.js +11 -2
- package/dist/components/ai/chat-ui.js.map +1 -1
- package/dist/components/ai/proposal-card.d.ts +30 -7
- package/dist/components/ai/proposal-card.js +81 -21
- package/dist/components/ai/proposal-card.js.map +1 -1
- package/dist/components/builder/blocks-model.js +12 -7
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/canvas-dnd.d.ts +59 -0
- package/dist/components/builder/canvas-dnd.js +207 -0
- package/dist/components/builder/canvas-dnd.js.map +1 -0
- package/dist/components/builder/canvas-dom.d.ts +11 -0
- package/dist/components/builder/canvas-dom.js +17 -0
- package/dist/components/builder/canvas-dom.js.map +1 -0
- package/dist/components/builder/canvas-inline-editing.d.ts +91 -0
- package/dist/components/builder/canvas-inline-editing.js +242 -0
- package/dist/components/builder/canvas-inline-editing.js.map +1 -0
- package/dist/components/builder/canvas-render-context.d.ts +83 -0
- package/dist/components/builder/canvas-render-context.js +196 -0
- package/dist/components/builder/canvas-render-context.js.map +1 -0
- package/dist/components/builder/canvas.d.ts +4 -1
- package/dist/components/builder/canvas.js +32 -567
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/composition-field-editor.js +14 -4
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/insert-context.d.ts +5 -2
- package/dist/components/builder/insert-context.js +1 -3
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/node-factory.d.ts +12 -0
- package/dist/components/builder/node-factory.js +18 -1
- package/dist/components/builder/node-factory.js.map +1 -1
- package/dist/components/cms/asset-field.d.ts +22 -0
- package/dist/components/cms/asset-field.js +300 -0
- package/dist/components/cms/asset-field.js.map +1 -0
- package/dist/components/cms/detail-editor.js +12 -0
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/document-diff-view.d.ts +12 -1
- package/dist/components/cms/document-diff-view.js +119 -42
- package/dist/components/cms/document-diff-view.js.map +1 -1
- package/dist/components/cms/field-control-lib.d.ts +43 -0
- package/dist/components/cms/field-control-lib.js +98 -0
- package/dist/components/cms/field-control-lib.js.map +1 -0
- package/dist/components/cms/field-control.d.ts +7 -35
- package/dist/components/cms/field-control.js +13 -556
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/object-array-field.d.ts +29 -0
- package/dist/components/cms/object-array-field.js +205 -0
- package/dist/components/cms/object-array-field.js.map +1 -0
- package/dist/components/cms/rich-text-preview.d.ts +15 -0
- package/dist/components/cms/rich-text-preview.js +81 -0
- package/dist/components/cms/rich-text-preview.js.map +1 -0
- package/dist/components/cms/version-compare-text.d.ts +26 -0
- package/dist/components/cms/version-compare-text.js +60 -0
- package/dist/components/cms/version-compare-text.js.map +1 -1
- package/dist/components/project-session.d.ts +19 -0
- package/dist/components/project-session.js +29 -2
- package/dist/components/project-session.js.map +1 -1
- package/dist/components/use-hydration-phase.d.ts +6 -4
- package/dist/components/use-hydration-phase.js.map +1 -1
- package/dist/lib/ai/apply-content.js +52 -10
- package/dist/lib/ai/apply-content.js.map +1 -1
- package/dist/lib/ai/chat-context.d.ts +19 -3
- package/dist/lib/ai/chat-context.js +33 -4
- package/dist/lib/ai/chat-context.js.map +1 -1
- package/dist/lib/ai/chat-state.d.ts +6 -3
- package/dist/lib/ai/chat-state.js.map +1 -1
- package/dist/lib/ai/fake-provider.d.ts +60 -5
- package/dist/lib/ai/fake-provider.js +74 -6
- package/dist/lib/ai/fake-provider.js.map +1 -1
- package/dist/lib/ai/index.d.ts +2 -0
- package/dist/lib/ai/index.js +2 -0
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/rich-text-input.d.ts +27 -0
- package/dist/lib/ai/rich-text-input.js +25 -0
- package/dist/lib/ai/rich-text-input.js.map +1 -0
- package/dist/lib/ai/shape.d.ts +12 -1
- package/dist/lib/ai/shape.js +46 -20
- package/dist/lib/ai/shape.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +88 -13
- package/dist/lib/ai/tools.js +253 -14
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/ai/types.d.ts +20 -0
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/ai/update-fields.d.ts +68 -0
- package/dist/lib/ai/update-fields.js +296 -0
- package/dist/lib/ai/update-fields.js.map +1 -0
- package/dist/lib/ai-current-document.d.ts +53 -0
- package/dist/lib/ai-current-document.js +54 -0
- package/dist/lib/ai-current-document.js.map +1 -0
- package/dist/lib/cms-views/view-model.d.ts +6 -6
- package/dist/lib/data/collection-content.d.ts +12 -7
- package/dist/lib/data/collection-content.js +6 -4
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/i18n/messages.en.js +10 -1
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/session-hydration.d.ts +4 -1
- package/dist/lib/operations/session-hydration.js +4 -1
- package/dist/lib/operations/session-hydration.js.map +1 -1
- package/dist/lib/persistence/index.js +13 -1
- package/dist/lib/persistence/index.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +1 -1
- package/dist/lib/workspace/index.js +1 -1
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/lib/workspace/workspace-source.d.ts +17 -22
- package/dist/lib/workspace/workspace-source.js +1 -49
- package/dist/lib/workspace/workspace-source.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ObjectFieldDef, SelectFieldDef } from '@elytracms/core/cms-core';
|
|
2
|
+
/**
|
|
3
|
+
* Pure companions of the document-editor field controls (EC-330, split from
|
|
4
|
+
* `field-control.tsx`): the repeater reorder/summary/shape decisions and the
|
|
5
|
+
* depth-encoding palette the object-array editors render from, plus the
|
|
6
|
+
* `options` segmented control. Small and dependency-light so the behavioural
|
|
7
|
+
* pins in `field-control.test.ts` reach them without pulling the whole
|
|
8
|
+
* field-control dispatcher.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Move `from` → `to`, returning a NEW array (all elements preserved). The repeater
|
|
12
|
+
* applies the same `(from, to)` to its value array AND its parallel stable-key array,
|
|
13
|
+
* so the two stay aligned through a reorder — a row's child/DOM state (an open
|
|
14
|
+
* relation/asset picker, focus) never mis-associates (EC-253 C2, EC-294).
|
|
15
|
+
*/
|
|
16
|
+
export declare function reorder<T>(list: readonly T[], from: number, to: number): T[];
|
|
17
|
+
/**
|
|
18
|
+
* A scannable one-line summary for a collapsed repeater item (EC-294): the first
|
|
19
|
+
* meaningful scalar subfield value — preferring title-ish field names — else `#n`.
|
|
20
|
+
* Pure (no store): summarises from the item's own scalar values, so it works for
|
|
21
|
+
* any object shape without resolving relations/assets.
|
|
22
|
+
*/
|
|
23
|
+
export declare function objectItemSummary(fields: readonly {
|
|
24
|
+
name: string;
|
|
25
|
+
}[], item: Record<string, unknown>, index: number): string;
|
|
26
|
+
export declare const depthRail: (d: number) => string;
|
|
27
|
+
export declare const depthTint: (d: number) => string;
|
|
28
|
+
export declare function isTabularObjectType(field: ObjectFieldDef): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* EC-295: the `options` control — a `select` shown as inline segmented buttons
|
|
31
|
+
* (`[[Page]] [URL] [File]`) instead of a dropdown. Engine built-in (no consumer
|
|
32
|
+
* registration); single-select only. Each option shows `label ?? value` and a
|
|
33
|
+
* `title` tooltip from the option's `description`; the field `default` + `readOnly`
|
|
34
|
+
* flow through the shared form. Picks one — to allow an empty/unset value use the
|
|
35
|
+
* default dropdown control.
|
|
36
|
+
*/
|
|
37
|
+
export declare function SegmentedOptions({ field, value, onChange, readOnly, id, }: {
|
|
38
|
+
field: SelectFieldDef;
|
|
39
|
+
value: unknown;
|
|
40
|
+
onChange: (value: unknown) => void;
|
|
41
|
+
readOnly: boolean;
|
|
42
|
+
id: string;
|
|
43
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Row } from '@elytracms/studio/ui';
|
|
3
|
+
import { localizedLabel } from '@elytracms/studio/lib/i18n';
|
|
4
|
+
import { fieldLabel } from './cms-labels.js';
|
|
5
|
+
/**
|
|
6
|
+
* Pure companions of the document-editor field controls (EC-330, split from
|
|
7
|
+
* `field-control.tsx`): the repeater reorder/summary/shape decisions and the
|
|
8
|
+
* depth-encoding palette the object-array editors render from, plus the
|
|
9
|
+
* `options` segmented control. Small and dependency-light so the behavioural
|
|
10
|
+
* pins in `field-control.test.ts` reach them without pulling the whole
|
|
11
|
+
* field-control dispatcher.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Move `from` → `to`, returning a NEW array (all elements preserved). The repeater
|
|
15
|
+
* applies the same `(from, to)` to its value array AND its parallel stable-key array,
|
|
16
|
+
* so the two stay aligned through a reorder — a row's child/DOM state (an open
|
|
17
|
+
* relation/asset picker, focus) never mis-associates (EC-253 C2, EC-294).
|
|
18
|
+
*/
|
|
19
|
+
export function reorder(list, from, to) {
|
|
20
|
+
const next = list.slice();
|
|
21
|
+
const [moved] = next.splice(from, 1);
|
|
22
|
+
if (moved !== undefined)
|
|
23
|
+
next.splice(to, 0, moved);
|
|
24
|
+
return next;
|
|
25
|
+
}
|
|
26
|
+
const SUMMARY_PREFERRED_FIELDS = ['title', 'label', 'name', 'heading', 'text', 'linkType'];
|
|
27
|
+
/**
|
|
28
|
+
* A scannable one-line summary for a collapsed repeater item (EC-294): the first
|
|
29
|
+
* meaningful scalar subfield value — preferring title-ish field names — else `#n`.
|
|
30
|
+
* Pure (no store): summarises from the item's own scalar values, so it works for
|
|
31
|
+
* any object shape without resolving relations/assets.
|
|
32
|
+
*/
|
|
33
|
+
export function objectItemSummary(fields, item, index) {
|
|
34
|
+
const scalar = (name) => {
|
|
35
|
+
const v = item[name];
|
|
36
|
+
if (typeof v === 'string' && v.trim())
|
|
37
|
+
return v.trim();
|
|
38
|
+
if (typeof v === 'number' || typeof v === 'boolean')
|
|
39
|
+
return String(v);
|
|
40
|
+
return null;
|
|
41
|
+
};
|
|
42
|
+
const names = fields.map((f) => f.name);
|
|
43
|
+
for (const name of SUMMARY_PREFERRED_FIELDS) {
|
|
44
|
+
if (names.includes(name)) {
|
|
45
|
+
const s = scalar(name);
|
|
46
|
+
if (s)
|
|
47
|
+
return truncateSummary(s);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const name of names) {
|
|
51
|
+
const s = scalar(name);
|
|
52
|
+
if (s)
|
|
53
|
+
return truncateSummary(s);
|
|
54
|
+
}
|
|
55
|
+
return `#${index + 1}`;
|
|
56
|
+
}
|
|
57
|
+
function truncateSummary(s, max = 60) {
|
|
58
|
+
return s.length > max ? `${s.slice(0, max - 1)}…` : s;
|
|
59
|
+
}
|
|
60
|
+
// (B) Depth-keyed visuals: a coloured left rail + a matching faint tint, DISTINCT
|
|
61
|
+
// per level so nesting reads at a glance. The outermost array (depth 0) starts at
|
|
62
|
+
// the brand primary — no more neutral, un-encoded first level — then a fixed
|
|
63
|
+
// high-contrast rotation whose adjacent hues never collide (primary→amber→green→red;
|
|
64
|
+
// note `info` is a near-primary blue, so it is deliberately NOT adjacent to primary).
|
|
65
|
+
// It cycles (mod) so pathological depth still renders. Literal classes so Tailwind emits them.
|
|
66
|
+
const DEPTH_RAIL = ['border-l-primary', 'border-l-warning', 'border-l-success', 'border-l-destructive'];
|
|
67
|
+
const DEPTH_TINT = ['bg-primary/5', 'bg-warning/5', 'bg-success/5', 'bg-destructive/5'];
|
|
68
|
+
// `d` is always ≥ 0, so `d % length` is in-bounds — the assertion is sound and gives
|
|
69
|
+
// callers a definite `string` (cn tolerates undefined, but tests read the value).
|
|
70
|
+
export const depthRail = (d) => DEPTH_RAIL[d % DEPTH_RAIL.length];
|
|
71
|
+
export const depthTint = (d) => DEPTH_TINT[d % DEPTH_TINT.length];
|
|
72
|
+
// (C) An object array renders as a compact TABLE when every subfield is a scalar (a
|
|
73
|
+
// flat row) and there are few enough columns to read; richer/nested object types stay
|
|
74
|
+
// as cards with depth encoding (B). Conditional (function-hidden) subfields force the
|
|
75
|
+
// card path — a table has no place to show/hide a column per row.
|
|
76
|
+
const TABULAR_FIELD_TYPES = new Set(['text', 'number', 'boolean', 'select', 'date']);
|
|
77
|
+
const MAX_TABLE_COLUMNS = 4;
|
|
78
|
+
export function isTabularObjectType(field) {
|
|
79
|
+
return (field.fields.length > 0 &&
|
|
80
|
+
field.fields.length <= MAX_TABLE_COLUMNS &&
|
|
81
|
+
field.fields.every((f) => TABULAR_FIELD_TYPES.has(f.type) && typeof f.form?.hidden !== 'function'));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* EC-295: the `options` control — a `select` shown as inline segmented buttons
|
|
85
|
+
* (`[[Page]] [URL] [File]`) instead of a dropdown. Engine built-in (no consumer
|
|
86
|
+
* registration); single-select only. Each option shows `label ?? value` and a
|
|
87
|
+
* `title` tooltip from the option's `description`; the field `default` + `readOnly`
|
|
88
|
+
* flow through the shared form. Picks one — to allow an empty/unset value use the
|
|
89
|
+
* default dropdown control.
|
|
90
|
+
*/
|
|
91
|
+
export function SegmentedOptions({ field, value, onChange, readOnly, id, }) {
|
|
92
|
+
const current = typeof value === 'string' ? value : '';
|
|
93
|
+
return (_jsx(Row, { gap: "1", wrap: true, role: "radiogroup", id: id, "aria-label": fieldLabel(field), children: field.options.map((opt) => {
|
|
94
|
+
const active = current === opt.value;
|
|
95
|
+
return (_jsx(Button, { type: "button", variant: active ? 'default' : 'outline', size: "sm", role: "radio", "aria-checked": active, title: localizedLabel(opt.description) || undefined, disabled: readOnly, onClick: () => onChange(opt.value), children: localizedLabel(opt.label, opt.value) }, opt.value));
|
|
96
|
+
}) }));
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=field-control-lib.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-control-lib.js","sourceRoot":"","sources":["../../../src/components/cms/field-control-lib.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ObjectFieldDef, SelectFieldDef } from '@elytracms/core/cms-core'\nimport { Button, Row } from '@elytracms/studio/ui'\nimport { localizedLabel } from '@elytracms/studio/lib/i18n'\nimport { fieldLabel } from './cms-labels'\n\n/**\n * Pure companions of the document-editor field controls (EC-330, split from\n * `field-control.tsx`): the repeater reorder/summary/shape decisions and the\n * depth-encoding palette the object-array editors render from, plus the\n * `options` segmented control. Small and dependency-light so the behavioural\n * pins in `field-control.test.ts` reach them without pulling the whole\n * field-control dispatcher.\n */\n\n/**\n * Move `from` → `to`, returning a NEW array (all elements preserved). The repeater\n * applies the same `(from, to)` to its value array AND its parallel stable-key array,\n * so the two stay aligned through a reorder — a row's child/DOM state (an open\n * relation/asset picker, focus) never mis-associates (EC-253 C2, EC-294).\n */\nexport function reorder<T>(list: readonly T[], from: number, to: number): T[] {\n const next = list.slice()\n const [moved] = next.splice(from, 1)\n if (moved !== undefined) next.splice(to, 0, moved)\n return next\n}\n\nconst SUMMARY_PREFERRED_FIELDS = ['title', 'label', 'name', 'heading', 'text', 'linkType']\n\n/**\n * A scannable one-line summary for a collapsed repeater item (EC-294): the first\n * meaningful scalar subfield value — preferring title-ish field names — else `#n`.\n * Pure (no store): summarises from the item's own scalar values, so it works for\n * any object shape without resolving relations/assets.\n */\nexport function objectItemSummary(\n fields: readonly { name: string }[],\n item: Record<string, unknown>,\n index: number,\n): string {\n const scalar = (name: string): string | null => {\n const v = item[name]\n if (typeof v === 'string' && v.trim()) return v.trim()\n if (typeof v === 'number' || typeof v === 'boolean') return String(v)\n return null\n }\n const names = fields.map((f) => f.name)\n for (const name of SUMMARY_PREFERRED_FIELDS) {\n if (names.includes(name)) {\n const s = scalar(name)\n if (s) return truncateSummary(s)\n }\n }\n for (const name of names) {\n const s = scalar(name)\n if (s) return truncateSummary(s)\n }\n return `#${index + 1}`\n}\n\nfunction truncateSummary(s: string, max = 60): string {\n return s.length > max ? `${s.slice(0, max - 1)}…` : s\n}\n\n// (B) Depth-keyed visuals: a coloured left rail + a matching faint tint, DISTINCT\n// per level so nesting reads at a glance. The outermost array (depth 0) starts at\n// the brand primary — no more neutral, un-encoded first level — then a fixed\n// high-contrast rotation whose adjacent hues never collide (primary→amber→green→red;\n// note `info` is a near-primary blue, so it is deliberately NOT adjacent to primary).\n// It cycles (mod) so pathological depth still renders. Literal classes so Tailwind emits them.\nconst DEPTH_RAIL = ['border-l-primary', 'border-l-warning', 'border-l-success', 'border-l-destructive']\nconst DEPTH_TINT = ['bg-primary/5', 'bg-warning/5', 'bg-success/5', 'bg-destructive/5']\n// `d` is always ≥ 0, so `d % length` is in-bounds — the assertion is sound and gives\n// callers a definite `string` (cn tolerates undefined, but tests read the value).\nexport const depthRail = (d: number) => DEPTH_RAIL[d % DEPTH_RAIL.length]!\nexport const depthTint = (d: number) => DEPTH_TINT[d % DEPTH_TINT.length]!\n\n// (C) An object array renders as a compact TABLE when every subfield is a scalar (a\n// flat row) and there are few enough columns to read; richer/nested object types stay\n// as cards with depth encoding (B). Conditional (function-hidden) subfields force the\n// card path — a table has no place to show/hide a column per row.\nconst TABULAR_FIELD_TYPES = new Set(['text', 'number', 'boolean', 'select', 'date'])\nconst MAX_TABLE_COLUMNS = 4\nexport function isTabularObjectType(field: ObjectFieldDef): boolean {\n return (\n field.fields.length > 0 &&\n field.fields.length <= MAX_TABLE_COLUMNS &&\n field.fields.every(\n (f) => TABULAR_FIELD_TYPES.has(f.type) && typeof f.form?.hidden !== 'function',\n )\n )\n}\n\n/**\n * EC-295: the `options` control — a `select` shown as inline segmented buttons\n * (`[[Page]] [URL] [File]`) instead of a dropdown. Engine built-in (no consumer\n * registration); single-select only. Each option shows `label ?? value` and a\n * `title` tooltip from the option's `description`; the field `default` + `readOnly`\n * flow through the shared form. Picks one — to allow an empty/unset value use the\n * default dropdown control.\n */\nexport function SegmentedOptions({\n field,\n value,\n onChange,\n readOnly,\n id,\n}: {\n field: SelectFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}) {\n const current = typeof value === 'string' ? value : ''\n return (\n <Row gap=\"1\" wrap role=\"radiogroup\" id={id} aria-label={fieldLabel(field)}>\n {field.options.map((opt) => {\n const active = current === opt.value\n return (\n <Button\n key={opt.value}\n type=\"button\"\n variant={active ? 'default' : 'outline'}\n size=\"sm\"\n role=\"radio\"\n aria-checked={active}\n title={localizedLabel(opt.description) || undefined}\n disabled={readOnly}\n onClick={() => onChange(opt.value)}\n >\n {localizedLabel(opt.label, opt.value)}\n </Button>\n )\n })}\n </Row>\n )\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FieldDef
|
|
1
|
+
import type { FieldDef } from '@elytracms/core/cms-core';
|
|
2
2
|
interface FieldControlProps {
|
|
3
3
|
field: FieldDef;
|
|
4
4
|
value: unknown;
|
|
@@ -15,40 +15,12 @@ interface FieldControlProps {
|
|
|
15
15
|
* naming a control with NO renderer and NO built-in name (a typo) shows the
|
|
16
16
|
* built-in with a loud issue — never a silent fall-through; otherwise the built-in
|
|
17
17
|
* renders alone.
|
|
18
|
+
*
|
|
19
|
+
* EC-330 split: the asset-field family lives in `asset-field.tsx`, the object /
|
|
20
|
+
* repeater editors in `object-array-field.tsx`, and their pure companions
|
|
21
|
+
* (reorder/summary/depth/table decisions, the `options` segmented control) in
|
|
22
|
+
* `field-control-lib.tsx` — this file keeps the dispatcher plus the slug and
|
|
23
|
+
* relation controls.
|
|
18
24
|
*/
|
|
19
25
|
export declare function FieldControl(props: FieldControlProps): import("react").JSX.Element;
|
|
20
|
-
/**
|
|
21
|
-
* EC-295: the `options` control — a `select` shown as inline segmented buttons
|
|
22
|
-
* (`[[Page]] [URL] [File]`) instead of a dropdown. Engine built-in (no consumer
|
|
23
|
-
* registration); single-select only. Each option shows `label ?? value` and a
|
|
24
|
-
* `title` tooltip from the option's `description`; the field `default` + `readOnly`
|
|
25
|
-
* flow through the shared form. Picks one — to allow an empty/unset value use the
|
|
26
|
-
* default dropdown control.
|
|
27
|
-
*/
|
|
28
|
-
export declare function SegmentedOptions({ field, value, onChange, readOnly, id, }: {
|
|
29
|
-
field: SelectFieldDef;
|
|
30
|
-
value: unknown;
|
|
31
|
-
onChange: (value: unknown) => void;
|
|
32
|
-
readOnly: boolean;
|
|
33
|
-
id: string;
|
|
34
|
-
}): import("react").JSX.Element;
|
|
35
|
-
/**
|
|
36
|
-
* Move `from` → `to`, returning a NEW array (all elements preserved). The repeater
|
|
37
|
-
* applies the same `(from, to)` to its value array AND its parallel stable-key array,
|
|
38
|
-
* so the two stay aligned through a reorder — a row's child/DOM state (an open
|
|
39
|
-
* relation/asset picker, focus) never mis-associates (EC-253 C2, EC-294).
|
|
40
|
-
*/
|
|
41
|
-
export declare function reorder<T>(list: readonly T[], from: number, to: number): T[];
|
|
42
|
-
/**
|
|
43
|
-
* A scannable one-line summary for a collapsed repeater item (EC-294): the first
|
|
44
|
-
* meaningful scalar subfield value — preferring title-ish field names — else `#n`.
|
|
45
|
-
* Pure (no store): summarises from the item's own scalar values, so it works for
|
|
46
|
-
* any object shape without resolving relations/assets.
|
|
47
|
-
*/
|
|
48
|
-
export declare function objectItemSummary(fields: readonly {
|
|
49
|
-
name: string;
|
|
50
|
-
}[], item: Record<string, unknown>, index: number): string;
|
|
51
|
-
export declare const depthRail: (d: number) => string;
|
|
52
|
-
export declare const depthTint: (d: number) => string;
|
|
53
|
-
export declare function isTabularObjectType(field: ObjectFieldDef): boolean;
|
|
54
26
|
export {};
|