@memberjunction/ng-dashboards 5.36.0 → 5.38.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 +32 -0
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts +14 -0
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +450 -292
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -1
- package/dist/ComponentStudio/component-studio-dashboard.component.d.ts +73 -1
- package/dist/ComponentStudio/component-studio-dashboard.component.d.ts.map +1 -1
- package/dist/ComponentStudio/component-studio-dashboard.component.js +512 -127
- package/dist/ComponentStudio/component-studio-dashboard.component.js.map +1 -1
- package/dist/ComponentStudio/component-studio-resource.component.d.ts +22 -0
- package/dist/ComponentStudio/component-studio-resource.component.d.ts.map +1 -0
- package/dist/ComponentStudio/component-studio-resource.component.js +55 -0
- package/dist/ComponentStudio/component-studio-resource.component.js.map +1 -0
- package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.d.ts +104 -45
- package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.d.ts.map +1 -1
- package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.js +234 -331
- package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.js.map +1 -1
- package/dist/ComponentStudio/components/form-builder/form-builder-canvas.component.d.ts +54 -0
- package/dist/ComponentStudio/components/form-builder/form-builder-canvas.component.d.ts.map +1 -0
- package/dist/ComponentStudio/components/form-builder/form-builder-canvas.component.js +339 -0
- package/dist/ComponentStudio/components/form-builder/form-builder-canvas.component.js.map +1 -0
- package/dist/ComponentStudio/components/form-builder/form-builder-right-panel.component.d.ts +65 -0
- package/dist/ComponentStudio/components/form-builder/form-builder-right-panel.component.d.ts.map +1 -0
- package/dist/ComponentStudio/components/form-builder/form-builder-right-panel.component.js +492 -0
- package/dist/ComponentStudio/components/form-builder/form-builder-right-panel.component.js.map +1 -0
- package/dist/ComponentStudio/components/form-builder/form-builder-tab.component.d.ts +88 -0
- package/dist/ComponentStudio/components/form-builder/form-builder-tab.component.d.ts.map +1 -0
- package/dist/ComponentStudio/components/form-builder/form-builder-tab.component.js +457 -0
- package/dist/ComponentStudio/components/form-builder/form-builder-tab.component.js.map +1 -0
- package/dist/ComponentStudio/components/form-override-dialog.component.d.ts +106 -0
- package/dist/ComponentStudio/components/form-override-dialog.component.d.ts.map +1 -0
- package/dist/ComponentStudio/components/form-override-dialog.component.js +478 -0
- package/dist/ComponentStudio/components/form-override-dialog.component.js.map +1 -0
- package/dist/ComponentStudio/components/workspace/component-preview.component.d.ts +54 -0
- package/dist/ComponentStudio/components/workspace/component-preview.component.d.ts.map +1 -1
- package/dist/ComponentStudio/components/workspace/component-preview.component.js +361 -50
- package/dist/ComponentStudio/components/workspace/component-preview.component.js.map +1 -1
- package/dist/ComponentStudio/components/workspace/editor-tabs.component.d.ts +10 -0
- package/dist/ComponentStudio/components/workspace/editor-tabs.component.d.ts.map +1 -1
- package/dist/ComponentStudio/components/workspace/editor-tabs.component.js +114 -45
- package/dist/ComponentStudio/components/workspace/editor-tabs.component.js.map +1 -1
- package/dist/ComponentStudio/services/canvas-to-code.d.ts +32 -0
- package/dist/ComponentStudio/services/canvas-to-code.d.ts.map +1 -0
- package/dist/ComponentStudio/services/canvas-to-code.js +347 -0
- package/dist/ComponentStudio/services/canvas-to-code.js.map +1 -0
- package/dist/ComponentStudio/services/code-to-canvas.d.ts +32 -0
- package/dist/ComponentStudio/services/code-to-canvas.d.ts.map +1 -0
- package/dist/ComponentStudio/services/code-to-canvas.js +92 -0
- package/dist/ComponentStudio/services/code-to-canvas.js.map +1 -0
- package/dist/ComponentStudio/services/component-studio-state.service.d.ts +29 -0
- package/dist/ComponentStudio/services/component-studio-state.service.d.ts.map +1 -1
- package/dist/ComponentStudio/services/component-studio-state.service.js +76 -0
- package/dist/ComponentStudio/services/component-studio-state.service.js.map +1 -1
- package/dist/ComponentStudio/services/entity-form-override.service.d.ts +86 -0
- package/dist/ComponentStudio/services/entity-form-override.service.d.ts.map +1 -0
- package/dist/ComponentStudio/services/entity-form-override.service.js +246 -0
- package/dist/ComponentStudio/services/entity-form-override.service.js.map +1 -0
- package/dist/ComponentStudio/services/field-binding-scanner.d.ts +29 -0
- package/dist/ComponentStudio/services/field-binding-scanner.d.ts.map +1 -0
- package/dist/ComponentStudio/services/field-binding-scanner.js +110 -0
- package/dist/ComponentStudio/services/field-binding-scanner.js.map +1 -0
- package/dist/ComponentStudio/services/form-canvas-model.d.ts +56 -0
- package/dist/ComponentStudio/services/form-canvas-model.d.ts.map +1 -0
- package/dist/ComponentStudio/services/form-canvas-model.js +35 -0
- package/dist/ComponentStudio/services/form-canvas-model.js.map +1 -0
- package/dist/ComponentStudio/services/form-host-props-fixture.d.ts +10 -0
- package/dist/ComponentStudio/services/form-host-props-fixture.d.ts.map +1 -0
- package/dist/ComponentStudio/services/form-host-props-fixture.js +10 -0
- package/dist/ComponentStudio/services/form-host-props-fixture.js.map +1 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.js +2 -2
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/FormBuilder/form-builder-resource.component.d.ts +964 -0
- package/dist/FormBuilder/form-builder-resource.component.d.ts.map +1 -0
- package/dist/FormBuilder/form-builder-resource.component.js +4487 -0
- package/dist/FormBuilder/form-builder-resource.component.js.map +1 -0
- package/dist/FormBuilder/form-builder-version-rail.helpers.d.ts +55 -0
- package/dist/FormBuilder/form-builder-version-rail.helpers.d.ts.map +1 -0
- package/dist/FormBuilder/form-builder-version-rail.helpers.js +73 -0
- package/dist/FormBuilder/form-builder-version-rail.helpers.js.map +1 -0
- package/dist/Home/home-application.d.ts +21 -1
- package/dist/Home/home-application.d.ts.map +1 -1
- package/dist/Home/home-application.js +60 -8
- package/dist/Home/home-application.js.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.d.ts +14 -14
- package/dist/QueryBrowser/query-browser-resource.component.d.ts.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.js +11 -10
- package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
- package/dist/component-studio-dashboards.module.d.ts +34 -22
- package/dist/component-studio-dashboards.module.d.ts.map +1 -1
- package/dist/component-studio-dashboards.module.js +65 -9
- package/dist/component-studio-dashboards.module.js.map +1 -1
- package/package.json +54 -53
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a JS-safe identifier from a free-form component name. Caller is
|
|
3
|
+
* responsible for handling empty results.
|
|
4
|
+
*/
|
|
5
|
+
export function toComponentIdentifier(name) {
|
|
6
|
+
const cleaned = name.replace(/[^A-Za-z0-9]/g, '');
|
|
7
|
+
if (!cleaned)
|
|
8
|
+
return 'Form';
|
|
9
|
+
return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generate the full Component code string from a canvas model.
|
|
13
|
+
*
|
|
14
|
+
* @param canvas - the canvas model to render
|
|
15
|
+
* @param schema - the curated form schema for the canvas's entity (used to
|
|
16
|
+
* resolve field types / FK display fields / enum allowed values)
|
|
17
|
+
* @param componentName - human-readable component name (used for the JS
|
|
18
|
+
* function identifier)
|
|
19
|
+
*/
|
|
20
|
+
export function generateCodeFromCanvas(canvas, schema, componentName) {
|
|
21
|
+
const fnName = toComponentIdentifier(componentName);
|
|
22
|
+
const fieldsByName = new Map(schema.fields.map(f => [f.name, f]));
|
|
23
|
+
const sectionBlocks = canvas.sections
|
|
24
|
+
.map(section => renderSection(section, fieldsByName))
|
|
25
|
+
.filter(s => s.length > 0)
|
|
26
|
+
.join('\n\n');
|
|
27
|
+
const title = canvas.title?.trim() || schema.displayName;
|
|
28
|
+
return [
|
|
29
|
+
`function ${fnName}({`,
|
|
30
|
+
` entityName,`,
|
|
31
|
+
` primaryKey,`,
|
|
32
|
+
` record,`,
|
|
33
|
+
` entityMetadata,`,
|
|
34
|
+
` mode,`,
|
|
35
|
+
` canEdit,`,
|
|
36
|
+
` canDelete,`,
|
|
37
|
+
` canCreate,`,
|
|
38
|
+
` utilities,`,
|
|
39
|
+
` styles,`,
|
|
40
|
+
` components,`,
|
|
41
|
+
` callbacks,`,
|
|
42
|
+
` savedUserSettings,`,
|
|
43
|
+
` onSaveUserSettings,`,
|
|
44
|
+
`}) {`,
|
|
45
|
+
` const [draft, setDraft] = React.useState({});`,
|
|
46
|
+
` const [fkOptions, setFkOptions] = React.useState({});`,
|
|
47
|
+
``,
|
|
48
|
+
` // Track the latest draft in a ref so the registered methods always`,
|
|
49
|
+
` // see fresh values without re-registering on every keystroke. The`,
|
|
50
|
+
` // ref is updated SYNCHRONOUSLY inside setField below — relying on a`,
|
|
51
|
+
` // useEffect to sync would create a race where Save reads a stale`,
|
|
52
|
+
` // draft if it fires before the effect commits.`,
|
|
53
|
+
` const draftRef = React.useRef({});`,
|
|
54
|
+
``,
|
|
55
|
+
` const isCreate = mode === "create";`,
|
|
56
|
+
` const isEdit = mode === "edit";`,
|
|
57
|
+
` const isView = mode === "view";`,
|
|
58
|
+
` const editing = isEdit || isCreate;`,
|
|
59
|
+
``,
|
|
60
|
+
` // Reset draft on new record load. Keep the ref in sync.`,
|
|
61
|
+
` React.useEffect(() => {`,
|
|
62
|
+
` draftRef.current = {};`,
|
|
63
|
+
` setDraft({});`,
|
|
64
|
+
` }, [primaryKey && JSON.stringify(primaryKey)]);`,
|
|
65
|
+
``,
|
|
66
|
+
` // Register the host-callable methods exactly once.`,
|
|
67
|
+
` React.useEffect(() => {`,
|
|
68
|
+
` callbacks?.RegisterMethod?.("RequestSave", () => {`,
|
|
69
|
+
` callbacks?.NotifyEvent?.("BeforeSave", {`,
|
|
70
|
+
` dirtyFields: { ...draftRef.current },`,
|
|
71
|
+
` cancel: false,`,
|
|
72
|
+
` timestamp: new Date(),`,
|
|
73
|
+
` });`,
|
|
74
|
+
` });`,
|
|
75
|
+
` callbacks?.RegisterMethod?.("RequestCancel", () => {`,
|
|
76
|
+
` draftRef.current = {};`,
|
|
77
|
+
` setDraft({});`,
|
|
78
|
+
` });`,
|
|
79
|
+
` }, []);`,
|
|
80
|
+
``,
|
|
81
|
+
renderFkLoaders(canvas, fieldsByName),
|
|
82
|
+
``,
|
|
83
|
+
` const value = (f) => (f in draft ? draft[f] : record?.[f] ?? "");`,
|
|
84
|
+
``,
|
|
85
|
+
` const setField = (f, v) => {`,
|
|
86
|
+
` // Update the ref SYNCHRONOUSLY so RequestSave (which reads via`,
|
|
87
|
+
` // draftRef) always sees the latest values, even if Save fires`,
|
|
88
|
+
` // before React commits the next render.`,
|
|
89
|
+
` draftRef.current = { ...draftRef.current, [f]: v };`,
|
|
90
|
+
` setDraft(draftRef.current);`,
|
|
91
|
+
` callbacks?.NotifyEvent?.("FieldChanged", {`,
|
|
92
|
+
` fieldName: f,`,
|
|
93
|
+
` oldValue: record?.[f],`,
|
|
94
|
+
` newValue: v,`,
|
|
95
|
+
` timestamp: new Date(),`,
|
|
96
|
+
` });`,
|
|
97
|
+
` };`,
|
|
98
|
+
``,
|
|
99
|
+
` if (!record && !isCreate) {`,
|
|
100
|
+
` return <div style={{ padding: 24 }}>No record loaded.</div>;`,
|
|
101
|
+
` }`,
|
|
102
|
+
``,
|
|
103
|
+
` return (`,
|
|
104
|
+
` <div style={{`,
|
|
105
|
+
` padding: 24,`,
|
|
106
|
+
` background: "var(--mj-bg-surface)",`,
|
|
107
|
+
` color: "var(--mj-text-primary)",`,
|
|
108
|
+
` borderRadius: 8,`,
|
|
109
|
+
` border: "1px solid var(--mj-border-default)",`,
|
|
110
|
+
` display: "flex",`,
|
|
111
|
+
` flexDirection: "column",`,
|
|
112
|
+
` gap: 20,`,
|
|
113
|
+
` }}>`,
|
|
114
|
+
` <h2 style={{ margin: 0, color: "var(--mj-text-primary)" }}>${escapeJsxText(title)}</h2>`,
|
|
115
|
+
sectionBlocks ? indent(sectionBlocks, 6) : ' {/* No sections — add some in Form Studio. */}',
|
|
116
|
+
` </div>`,
|
|
117
|
+
` );`,
|
|
118
|
+
`}`,
|
|
119
|
+
].join('\n');
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Render a single section: header + grid of elements. Elements that
|
|
123
|
+
* reference fields not in the schema are skipped (linter would reject them).
|
|
124
|
+
*/
|
|
125
|
+
function renderSection(section, fieldsByName) {
|
|
126
|
+
const elements = section.elements
|
|
127
|
+
.map(el => renderElement(el, fieldsByName, section.columns))
|
|
128
|
+
.filter(s => s.length > 0);
|
|
129
|
+
if (elements.length === 0 && !section.title) {
|
|
130
|
+
return '';
|
|
131
|
+
}
|
|
132
|
+
const gridStyle = section.columns === 2
|
|
133
|
+
? '{{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}'
|
|
134
|
+
: '{{ display: "flex", flexDirection: "column", gap: 12 }}';
|
|
135
|
+
return [
|
|
136
|
+
`<section style={{ display: "flex", flexDirection: "column", gap: 12 }}>`,
|
|
137
|
+
section.title
|
|
138
|
+
? ` <h3 style={{ margin: 0, fontSize: 14, fontWeight: 600, color: "var(--mj-text-secondary)", textTransform: "uppercase", letterSpacing: "0.5px" }}>${escapeJsxText(section.title)}</h3>`
|
|
139
|
+
: '',
|
|
140
|
+
` <div style=${gridStyle}>`,
|
|
141
|
+
...elements.map(e => indent(e, 4)),
|
|
142
|
+
` </div>`,
|
|
143
|
+
`</section>`,
|
|
144
|
+
].filter(s => s.length > 0).join('\n');
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Render a single element. Routes to type-specific renderers and respects
|
|
148
|
+
* the per-element column span.
|
|
149
|
+
*/
|
|
150
|
+
function renderElement(el, fieldsByName, sectionColumns) {
|
|
151
|
+
const span = sectionColumns === 2 && el.span === 2 ? ' gridColumn: "span 2",' : '';
|
|
152
|
+
const wrapperStyleOpen = `<div style={{ display: "flex", flexDirection: "column", gap: 4,${span} }}>`;
|
|
153
|
+
const wrapperClose = `</div>`;
|
|
154
|
+
if (el.type === 'static-text') {
|
|
155
|
+
return [
|
|
156
|
+
wrapperStyleOpen,
|
|
157
|
+
` <div style={{ color: "var(--mj-text-secondary)", fontSize: 13 }}>${escapeJsxText(el.text ?? '')}</div>`,
|
|
158
|
+
wrapperClose,
|
|
159
|
+
].join('\n');
|
|
160
|
+
}
|
|
161
|
+
if (el.type === 'spacer') {
|
|
162
|
+
return `<div style={{ minHeight: 12,${span} }} />`;
|
|
163
|
+
}
|
|
164
|
+
if (el.type === 'computed') {
|
|
165
|
+
// Expression is rendered as a runtime expression against `record`.
|
|
166
|
+
// We don't validate it here — the linter / runtime will surface bugs.
|
|
167
|
+
const expr = (el.expression ?? '""').trim() || '""';
|
|
168
|
+
return [
|
|
169
|
+
wrapperStyleOpen,
|
|
170
|
+
el.label
|
|
171
|
+
? ` <label style={{ fontSize: 11, color: "var(--mj-text-muted)", textTransform: "uppercase", letterSpacing: "0.5px" }}>${escapeJsxText(el.label)}</label>`
|
|
172
|
+
: '',
|
|
173
|
+
` <div>{(() => { try { return ${expr}; } catch (e) { return "—"; } })()}</div>`,
|
|
174
|
+
el.helper ? ` <small style={{ color: "var(--mj-text-muted)" }}>${escapeJsxText(el.helper)}</small>` : '',
|
|
175
|
+
wrapperClose,
|
|
176
|
+
].filter(Boolean).join('\n');
|
|
177
|
+
}
|
|
178
|
+
// type === 'field'
|
|
179
|
+
if (!el.fieldName)
|
|
180
|
+
return '';
|
|
181
|
+
const field = fieldsByName.get(el.fieldName);
|
|
182
|
+
if (!field)
|
|
183
|
+
return '';
|
|
184
|
+
const label = el.label ?? field.displayName ?? field.name;
|
|
185
|
+
const required = el.required ?? field.required;
|
|
186
|
+
const labelMarkup = `<label style={{ fontSize: 11, color: "var(--mj-text-muted)", textTransform: "uppercase", letterSpacing: "0.5px" }}>${escapeJsxText(label)}${required ? ' <span style={{ color: "var(--mj-status-error)" }}>*</span>' : ''}</label>`;
|
|
187
|
+
const helperMarkup = el.helper
|
|
188
|
+
? `<small style={{ color: "var(--mj-text-muted)" }}>${escapeJsxText(el.helper)}</small>`
|
|
189
|
+
: '';
|
|
190
|
+
const inputMarkup = renderFieldInput(field);
|
|
191
|
+
return [
|
|
192
|
+
wrapperStyleOpen,
|
|
193
|
+
` ${labelMarkup}`,
|
|
194
|
+
` ${inputMarkup}`,
|
|
195
|
+
helperMarkup ? ` ${helperMarkup}` : '',
|
|
196
|
+
wrapperClose,
|
|
197
|
+
].filter(Boolean).join('\n');
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Type-appropriate input rendering. Returns a single JSX expression.
|
|
201
|
+
* Inputs are styled with token-backed CSS so they adapt to the host theme.
|
|
202
|
+
*/
|
|
203
|
+
function renderFieldInput(field) {
|
|
204
|
+
const baseStyle = 'padding: "8px 10px", border: "1px solid var(--mj-border-default)", borderRadius: 4, background: "var(--mj-bg-surface)", color: "var(--mj-text-primary)", fontFamily: "inherit", fontSize: 13';
|
|
205
|
+
const name = field.name;
|
|
206
|
+
switch (field.type) {
|
|
207
|
+
case 'boolean':
|
|
208
|
+
return [
|
|
209
|
+
`{editing`,
|
|
210
|
+
` ? <input type="checkbox" checked={!!value("${name}")} onChange={(e) => setField("${name}", e.target.checked)} />`,
|
|
211
|
+
` : <div>{value("${name}") ? "Yes" : "No"}</div>}`,
|
|
212
|
+
].join('\n ');
|
|
213
|
+
case 'datetime':
|
|
214
|
+
return [
|
|
215
|
+
`{editing`,
|
|
216
|
+
` ? <input type="datetime-local" style={{ ${baseStyle} }} value={value("${name}") ? new Date(value("${name}")).toISOString().slice(0, 16) : ""} onChange={(e) => setField("${name}", e.target.value ? new Date(e.target.value).toISOString() : null)} />`,
|
|
217
|
+
` : <div>{value("${name}") ? new Date(value("${name}")).toLocaleString() : "—"}</div>}`,
|
|
218
|
+
].join('\n ');
|
|
219
|
+
case 'number':
|
|
220
|
+
return [
|
|
221
|
+
`{editing`,
|
|
222
|
+
` ? <input type="number" style={{ ${baseStyle} }} value={value("${name}") ?? ""} onChange={(e) => setField("${name}", e.target.value === "" ? null : Number(e.target.value))} />`,
|
|
223
|
+
` : <div>{value("${name}") ?? "—"}</div>}`,
|
|
224
|
+
].join('\n ');
|
|
225
|
+
case 'enum': {
|
|
226
|
+
const opts = (field.allowedValues ?? [])
|
|
227
|
+
.map(v => ` <option value=${escapeJsxAttr(v)}>${escapeJsxText(v)}</option>`)
|
|
228
|
+
.join('\n');
|
|
229
|
+
return [
|
|
230
|
+
`{editing`,
|
|
231
|
+
` ? (<select style={{ ${baseStyle} }} value={value("${name}") ?? ""} onChange={(e) => setField("${name}", e.target.value)}>`,
|
|
232
|
+
` <option value="">—</option>`,
|
|
233
|
+
opts,
|
|
234
|
+
` </select>)`,
|
|
235
|
+
` : <div>{value("${name}") ?? "—"}</div>}`,
|
|
236
|
+
].join('\n ');
|
|
237
|
+
}
|
|
238
|
+
case 'foreign-key': {
|
|
239
|
+
const display = field.references?.displayField ?? 'ID';
|
|
240
|
+
return [
|
|
241
|
+
`{editing`,
|
|
242
|
+
` ? (<select style={{ ${baseStyle} }} value={(value("${name}")?.ID) ?? value("${name}") ?? ""} onChange={(e) => setField("${name}", e.target.value || null)}>`,
|
|
243
|
+
` <option value="">—</option>`,
|
|
244
|
+
` {(fkOptions["${name}"] || []).map((o) => <option key={o.ID} value={o.ID}>{o.${safeIdent(display)}}</option>)}`,
|
|
245
|
+
` </select>)`,
|
|
246
|
+
` : <div>{(value("${name}")?.${safeIdent(display)}) ?? value("${name}") ?? "—"}</div>}`,
|
|
247
|
+
].join('\n ');
|
|
248
|
+
}
|
|
249
|
+
case 'string':
|
|
250
|
+
default: {
|
|
251
|
+
const useTextarea = (field.maxLength ?? 0) === 0 || (field.maxLength ?? 0) > 200;
|
|
252
|
+
if (useTextarea) {
|
|
253
|
+
return [
|
|
254
|
+
`{editing`,
|
|
255
|
+
` ? <textarea style={{ ${baseStyle}, minHeight: 80, fontFamily: "inherit", resize: "vertical" }} value={value("${name}") ?? ""} onChange={(e) => setField("${name}", e.target.value)} />`,
|
|
256
|
+
` : <div style={{ whiteSpace: "pre-wrap" }}>{value("${name}") || "—"}</div>}`,
|
|
257
|
+
].join('\n ');
|
|
258
|
+
}
|
|
259
|
+
return [
|
|
260
|
+
`{editing`,
|
|
261
|
+
` ? <input type="text" style={{ ${baseStyle} }} value={value("${name}") ?? ""} onChange={(e) => setField("${name}", e.target.value)} />`,
|
|
262
|
+
` : <div>{value("${name}") || "—"}</div>}`,
|
|
263
|
+
].join('\n ');
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Emit useEffect blocks that lazy-load FK option lists via
|
|
269
|
+
* `utilities.rv.RunView`. Triggered only for foreign-key fields actually
|
|
270
|
+
* referenced on the canvas.
|
|
271
|
+
*
|
|
272
|
+
* Returned string is empty when no FK fields are present, so the output
|
|
273
|
+
* stays tight.
|
|
274
|
+
*/
|
|
275
|
+
function renderFkLoaders(canvas, fieldsByName) {
|
|
276
|
+
const fkNames = new Set();
|
|
277
|
+
for (const section of canvas.sections) {
|
|
278
|
+
for (const el of section.elements) {
|
|
279
|
+
if (el.type !== 'field' || !el.fieldName)
|
|
280
|
+
continue;
|
|
281
|
+
const field = fieldsByName.get(el.fieldName);
|
|
282
|
+
if (field?.type === 'foreign-key' && field.references?.entity) {
|
|
283
|
+
fkNames.add(field.name);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (fkNames.size === 0)
|
|
288
|
+
return '';
|
|
289
|
+
const loaders = Array.from(fkNames).map(n => {
|
|
290
|
+
const field = fieldsByName.get(n);
|
|
291
|
+
if (!field?.references?.entity)
|
|
292
|
+
return '';
|
|
293
|
+
const entity = field.references.entity;
|
|
294
|
+
const displayField = field.references.displayField ?? 'ID';
|
|
295
|
+
return [
|
|
296
|
+
` React.useEffect(() => {`,
|
|
297
|
+
` if (!editing) return;`,
|
|
298
|
+
` let cancelled = false;`,
|
|
299
|
+
` (async () => {`,
|
|
300
|
+
` const r = await utilities.rv.RunView({`,
|
|
301
|
+
` EntityName: ${JSON.stringify(entity)},`,
|
|
302
|
+
` Fields: ["ID", ${JSON.stringify(displayField)}],`,
|
|
303
|
+
` MaxRows: 200,`,
|
|
304
|
+
` ResultType: "simple",`,
|
|
305
|
+
` });`,
|
|
306
|
+
` if (cancelled) return;`,
|
|
307
|
+
` if (r?.Success && Array.isArray(r.Results)) {`,
|
|
308
|
+
` setFkOptions((m) => ({ ...m, ${JSON.stringify(n)}: r.Results }));`,
|
|
309
|
+
` }`,
|
|
310
|
+
` })();`,
|
|
311
|
+
` return () => { cancelled = true; };`,
|
|
312
|
+
` }, [editing]);`,
|
|
313
|
+
].join('\n');
|
|
314
|
+
}).filter(s => s.length > 0);
|
|
315
|
+
return loaders.join('\n\n');
|
|
316
|
+
}
|
|
317
|
+
/* ---------- string helpers ---------- */
|
|
318
|
+
function escapeJsxText(s) {
|
|
319
|
+
return s.replace(/[<>{}]/g, ch => {
|
|
320
|
+
switch (ch) {
|
|
321
|
+
case '<': return '<';
|
|
322
|
+
case '>': return '>';
|
|
323
|
+
case '{': return '{';
|
|
324
|
+
case '}': return '}';
|
|
325
|
+
}
|
|
326
|
+
return ch;
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
/** Stringify an attribute value as a JSX attribute (always wraps in {"…"}). */
|
|
330
|
+
function escapeJsxAttr(s) {
|
|
331
|
+
return `{${JSON.stringify(s)}}`;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Ensure a display field name is a safe JS identifier. Falls back to
|
|
335
|
+
* bracket access if not. Most MJ display fields are "Name" or "Title" so
|
|
336
|
+
* the simple path dominates.
|
|
337
|
+
*/
|
|
338
|
+
function safeIdent(name) {
|
|
339
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
|
|
340
|
+
return name;
|
|
341
|
+
return `["${name.replace(/"/g, '\\"')}"]`;
|
|
342
|
+
}
|
|
343
|
+
function indent(block, spaces) {
|
|
344
|
+
const pad = ' '.repeat(spaces);
|
|
345
|
+
return block.split('\n').map(line => line.length === 0 ? line : pad + line).join('\n');
|
|
346
|
+
}
|
|
347
|
+
//# sourceMappingURL=canvas-to-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-to-code.js","sourceRoot":"","sources":["../../../src/ComponentStudio/services/canvas-to-code.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;IAClD,IAAI,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC;IAC5B,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAClC,MAAuB,EACvB,MAAyB,EACzB,aAAqB;IAErB,MAAM,MAAM,GAAG,qBAAqB,CAAC,aAAa,CAAC,CAAC;IACpD,MAAM,YAAY,GAAG,IAAI,GAAG,CACxB,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CACtC,CAAC;IAEF,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ;SAChC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;SACpD,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;SACzB,IAAI,CAAC,MAAM,CAAC,CAAC;IAElB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,MAAM,CAAC,WAAW,CAAC;IAEzD,OAAO;QACH,YAAY,MAAM,IAAI;QACtB,eAAe;QACf,eAAe;QACf,WAAW;QACX,mBAAmB;QACnB,SAAS;QACT,YAAY;QACZ,cAAc;QACd,cAAc;QACd,cAAc;QACd,WAAW;QACX,eAAe;QACf,cAAc;QACd,sBAAsB;QACtB,uBAAuB;QACvB,MAAM;QACN,iDAAiD;QACjD,yDAAyD;QACzD,EAAE;QACF,uEAAuE;QACvE,sEAAsE;QACtE,wEAAwE;QACxE,qEAAqE;QACrE,mDAAmD;QACnD,sCAAsC;QACtC,EAAE;QACF,uCAAuC;QACvC,mCAAmC;QACnC,mCAAmC;QACnC,uCAAuC;QACvC,EAAE;QACF,4DAA4D;QAC5D,2BAA2B;QAC3B,4BAA4B;QAC5B,mBAAmB;QACnB,mDAAmD;QACnD,EAAE;QACF,uDAAuD;QACvD,2BAA2B;QAC3B,wDAAwD;QACxD,gDAAgD;QAChD,+CAA+C;QAC/C,wBAAwB;QACxB,gCAAgC;QAChC,WAAW;QACX,SAAS;QACT,0DAA0D;QAC1D,8BAA8B;QAC9B,qBAAqB;QACrB,SAAS;QACT,WAAW;QACX,EAAE;QACF,eAAe,CAAC,MAAM,EAAE,YAAY,CAAC;QACrC,EAAE;QACF,qEAAqE;QACrE,EAAE;QACF,gCAAgC;QAChC,qEAAqE;QACrE,oEAAoE;QACpE,8CAA8C;QAC9C,yDAAyD;QACzD,iCAAiC;QACjC,gDAAgD;QAChD,qBAAqB;QACrB,8BAA8B;QAC9B,oBAAoB;QACpB,8BAA8B;QAC9B,SAAS;QACT,MAAM;QACN,EAAE;QACF,+BAA+B;QAC/B,kEAAkE;QAClE,KAAK;QACL,EAAE;QACF,YAAY;QACZ,mBAAmB;QACnB,oBAAoB;QACpB,2CAA2C;QAC3C,wCAAwC;QACxC,wBAAwB;QACxB,qDAAqD;QACrD,wBAAwB;QACxB,gCAAgC;QAChC,gBAAgB;QAChB,SAAS;QACT,oEAAoE,aAAa,CAAC,KAAK,CAAC,OAAO;QAC/F,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,sDAAsD;QACjG,YAAY;QACZ,MAAM;QACN,GAAG;KACN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAClB,OAA0B,EAC1B,YAA2C;IAE3C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ;SAC5B,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,aAAa,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;SAC3D,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE/B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAC1C,OAAO,EAAE,CAAC;IACd,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,KAAK,CAAC;QACnC,CAAC,CAAC,gEAAgE;QAClE,CAAC,CAAC,yDAAyD,CAAC;IAEhE,OAAO;QACH,yEAAyE;QACzE,OAAO,CAAC,KAAK;YACT,CAAC,CAAC,qJAAqJ,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO;YAC1L,CAAC,CAAC,EAAE;QACR,gBAAgB,SAAS,GAAG;QAC5B,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAClC,UAAU;QACV,YAAY;KACf,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC3C,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAClB,EAAqB,EACrB,YAA2C,EAC3C,cAAqB;IAErB,MAAM,IAAI,GAAG,cAAc,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,EAAE,CAAC;IACnF,MAAM,gBAAgB,GAAG,kEAAkE,IAAI,MAAM,CAAC;IACtG,MAAM,YAAY,GAAG,QAAQ,CAAC;IAE9B,IAAI,EAAE,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAC5B,OAAO;YACH,gBAAgB;YAChB,sEAAsE,aAAa,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,QAAQ;YAC1G,YAAY;SACf,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IAED,IAAI,EAAE,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACvB,OAAO,+BAA+B,IAAI,QAAQ,CAAC;IACvD,CAAC;IAED,IAAI,EAAE,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACzB,mEAAmE;QACnE,sEAAsE;QACtE,MAAM,IAAI,GAAG,CAAC,EAAE,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC;QACpD,OAAO;YACH,gBAAgB;YAChB,EAAE,CAAC,KAAK;gBACJ,CAAC,CAAC,wHAAwH,aAAa,CAAC,EAAE,CAAC,KAAK,CAAC,UAAU;gBAC3J,CAAC,CAAC,EAAE;YACR,iCAAiC,IAAI,2CAA2C;YAChF,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,sDAAsD,aAAa,CAAC,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;YACzG,YAAY;SACf,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,mBAAmB;IACnB,IAAI,CAAC,EAAE,CAAC,SAAS;QAAE,OAAO,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAEtB,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC;IAC1D,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC;IAC/C,MAAM,WAAW,GAAG,sHAAsH,aAAa,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,6DAA6D,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC;IACzP,MAAM,YAAY,GAAG,EAAE,CAAC,MAAM;QAC1B,CAAC,CAAC,oDAAoD,aAAa,CAAC,EAAE,CAAC,MAAM,CAAC,UAAU;QACxF,CAAC,CAAC,EAAE,CAAC;IAET,MAAM,WAAW,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAE5C,OAAO;QACH,gBAAgB;QAChB,KAAK,WAAW,EAAE;QAClB,KAAK,WAAW,EAAE;QAClB,YAAY,CAAC,CAAC,CAAC,KAAK,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE;QACvC,YAAY;KACf,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,KAAuB;IAC7C,MAAM,SAAS,GAAG,8LAA8L,CAAC;IACjN,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IAExB,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACjB,KAAK,SAAS;YACV,OAAO;gBACH,UAAU;gBACV,kDAAkD,IAAI,kCAAkC,IAAI,0BAA0B;gBACtH,sBAAsB,IAAI,2BAA2B;aACxD,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEnB,KAAK,UAAU;YACX,OAAO;gBACH,UAAU;gBACV,+CAA+C,SAAS,qBAAqB,IAAI,wBAAwB,IAAI,mEAAmE,IAAI,wEAAwE;gBAC5P,sBAAsB,IAAI,wBAAwB,IAAI,oCAAoC;aAC7F,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEnB,KAAK,QAAQ;YACT,OAAO;gBACH,UAAU;gBACV,uCAAuC,SAAS,qBAAqB,IAAI,wCAAwC,IAAI,+DAA+D;gBACpL,sBAAsB,IAAI,mBAAmB;aAChD,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEnB,KAAK,MAAM,CAAC,CAAC,CAAC;YACV,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,aAAa,IAAI,EAAE,CAAC;iBACnC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,uBAAuB,aAAa,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC;iBAChF,IAAI,CAAC,IAAI,CAAC,CAAC;YAChB,OAAO;gBACH,UAAU;gBACV,2BAA2B,SAAS,qBAAqB,IAAI,wCAAwC,IAAI,sBAAsB;gBAC/H,qCAAqC;gBACrC,IAAI;gBACJ,kBAAkB;gBAClB,sBAAsB,IAAI,mBAAmB;aAChD,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC;QAED,KAAK,aAAa,CAAC,CAAC,CAAC;YACjB,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,EAAE,YAAY,IAAI,IAAI,CAAC;YACvD,OAAO;gBACH,UAAU;gBACV,2BAA2B,SAAS,sBAAsB,IAAI,qBAAqB,IAAI,wCAAwC,IAAI,8BAA8B;gBACjK,qCAAqC;gBACrC,wBAAwB,IAAI,2DAA2D,SAAS,CAAC,OAAO,CAAC,cAAc;gBACvH,kBAAkB;gBAClB,uBAAuB,IAAI,OAAO,SAAS,CAAC,OAAO,CAAC,eAAe,IAAI,mBAAmB;aAC7F,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC;QAED,KAAK,QAAQ,CAAC;QACd,OAAO,CAAC,CAAC,CAAC;YACN,MAAM,WAAW,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC;YACjF,IAAI,WAAW,EAAE,CAAC;gBACd,OAAO;oBACH,UAAU;oBACV,4BAA4B,SAAS,+EAA+E,IAAI,wCAAwC,IAAI,wBAAwB;oBAC5L,yDAAyD,IAAI,mBAAmB;iBACnF,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACnB,CAAC;YACD,OAAO;gBACH,UAAU;gBACV,qCAAqC,SAAS,qBAAqB,IAAI,wCAAwC,IAAI,wBAAwB;gBAC3I,sBAAsB,IAAI,mBAAmB;aAChD,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC;IACL,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,eAAe,CACpB,MAAuB,EACvB,YAA2C;IAE3C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpC,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YAChC,IAAI,EAAE,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,EAAE,CAAC,SAAS;gBAAE,SAAS;YACnD,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;YAC7C,IAAI,KAAK,EAAE,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;gBAC5D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;QACL,CAAC;IACL,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;QACxC,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM;YAAE,OAAO,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC;QACvC,MAAM,YAAY,GAAG,KAAK,CAAC,UAAU,CAAC,YAAY,IAAI,IAAI,CAAC;QAC3D,OAAO;YACH,2BAA2B;YAC3B,2BAA2B;YAC3B,4BAA4B;YAC5B,oBAAoB;YACpB,8CAA8C;YAC9C,uBAAuB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG;YAChD,0BAA0B,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI;YAC1D,uBAAuB;YACvB,+BAA+B;YAC/B,WAAW;YACX,8BAA8B;YAC9B,qDAAqD;YACrD,wCAAwC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,kBAAkB;YAC3E,SAAS;YACT,WAAW;YACX,yCAAyC;YACzC,kBAAkB;SACrB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE7B,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAED,0CAA0C;AAE1C,SAAS,aAAa,CAAC,CAAS;IAC5B,OAAO,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,EAAE;QAC7B,QAAQ,EAAE,EAAE,CAAC;YACT,KAAK,GAAG,CAAC,CAAC,OAAO,MAAM,CAAC;YACxB,KAAK,GAAG,CAAC,CAAC,OAAO,MAAM,CAAC;YACxB,KAAK,GAAG,CAAC,CAAC,OAAO,QAAQ,CAAC;YAC1B,KAAK,GAAG,CAAC,CAAC,OAAO,QAAQ,CAAC;QAC9B,CAAC;QACD,OAAO,EAAE,CAAC;IACd,CAAC,CAAC,CAAC;AACP,CAAC;AAED,+EAA+E;AAC/E,SAAS,aAAa,CAAC,CAAS;IAC5B,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;AACpC,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,IAAY;IAC3B,IAAI,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACvD,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC;AAC9C,CAAC;AAED,SAAS,MAAM,CAAC,KAAa,EAAE,MAAc;IACzC,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/B,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC3F,CAAC","sourcesContent":["/**\n * Pure-function code generator: FormCanvasModel → JSX string.\n *\n * The output conforms to the form-role component contract:\n * - Destructures FormHostProps (record, mode, entityMetadata, callbacks, ...)\n * - Maintains a local draft diff so edits don't mutate the BaseEntity snapshot\n * - Registers RequestSave / RequestCancel via callbacks.RegisterMethod\n * - Emits BeforeSave with dirtyFields when the host toolbar fires Save\n * - No Save / Cancel buttons in the body — the host toolbar owns them\n * - References only fields from the curated schema (linter requirement)\n *\n * This file is *intentionally* a pure function with no side effects so it can\n * be unit-tested without DOM / Angular wiring.\n */\nimport type { CuratedFormSchema, CuratedFormField } from '@memberjunction/interactive-component-types/forms';\nimport type { FormCanvasModel, FormCanvasSection, FormCanvasElement } from './form-canvas-model';\n\n/**\n * Build a JS-safe identifier from a free-form component name. Caller is\n * responsible for handling empty results.\n */\nexport function toComponentIdentifier(name: string): string {\n const cleaned = name.replace(/[^A-Za-z0-9]/g, '');\n if (!cleaned) return 'Form';\n return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);\n}\n\n/**\n * Generate the full Component code string from a canvas model.\n *\n * @param canvas - the canvas model to render\n * @param schema - the curated form schema for the canvas's entity (used to\n * resolve field types / FK display fields / enum allowed values)\n * @param componentName - human-readable component name (used for the JS\n * function identifier)\n */\nexport function generateCodeFromCanvas(\n canvas: FormCanvasModel,\n schema: CuratedFormSchema,\n componentName: string,\n): string {\n const fnName = toComponentIdentifier(componentName);\n const fieldsByName = new Map<string, CuratedFormField>(\n schema.fields.map(f => [f.name, f]),\n );\n\n const sectionBlocks = canvas.sections\n .map(section => renderSection(section, fieldsByName))\n .filter(s => s.length > 0)\n .join('\\n\\n');\n\n const title = canvas.title?.trim() || schema.displayName;\n\n return [\n `function ${fnName}({`,\n ` entityName,`,\n ` primaryKey,`,\n ` record,`,\n ` entityMetadata,`,\n ` mode,`,\n ` canEdit,`,\n ` canDelete,`,\n ` canCreate,`,\n ` utilities,`,\n ` styles,`,\n ` components,`,\n ` callbacks,`,\n ` savedUserSettings,`,\n ` onSaveUserSettings,`,\n `}) {`,\n ` const [draft, setDraft] = React.useState({});`,\n ` const [fkOptions, setFkOptions] = React.useState({});`,\n ``,\n ` // Track the latest draft in a ref so the registered methods always`,\n ` // see fresh values without re-registering on every keystroke. The`,\n ` // ref is updated SYNCHRONOUSLY inside setField below — relying on a`,\n ` // useEffect to sync would create a race where Save reads a stale`,\n ` // draft if it fires before the effect commits.`,\n ` const draftRef = React.useRef({});`,\n ``,\n ` const isCreate = mode === \"create\";`,\n ` const isEdit = mode === \"edit\";`,\n ` const isView = mode === \"view\";`,\n ` const editing = isEdit || isCreate;`,\n ``,\n ` // Reset draft on new record load. Keep the ref in sync.`,\n ` React.useEffect(() => {`,\n ` draftRef.current = {};`,\n ` setDraft({});`,\n ` }, [primaryKey && JSON.stringify(primaryKey)]);`,\n ``,\n ` // Register the host-callable methods exactly once.`,\n ` React.useEffect(() => {`,\n ` callbacks?.RegisterMethod?.(\"RequestSave\", () => {`,\n ` callbacks?.NotifyEvent?.(\"BeforeSave\", {`,\n ` dirtyFields: { ...draftRef.current },`,\n ` cancel: false,`,\n ` timestamp: new Date(),`,\n ` });`,\n ` });`,\n ` callbacks?.RegisterMethod?.(\"RequestCancel\", () => {`,\n ` draftRef.current = {};`,\n ` setDraft({});`,\n ` });`,\n ` }, []);`,\n ``,\n renderFkLoaders(canvas, fieldsByName),\n ``,\n ` const value = (f) => (f in draft ? draft[f] : record?.[f] ?? \"\");`,\n ``,\n ` const setField = (f, v) => {`,\n ` // Update the ref SYNCHRONOUSLY so RequestSave (which reads via`,\n ` // draftRef) always sees the latest values, even if Save fires`,\n ` // before React commits the next render.`,\n ` draftRef.current = { ...draftRef.current, [f]: v };`,\n ` setDraft(draftRef.current);`,\n ` callbacks?.NotifyEvent?.(\"FieldChanged\", {`,\n ` fieldName: f,`,\n ` oldValue: record?.[f],`,\n ` newValue: v,`,\n ` timestamp: new Date(),`,\n ` });`,\n ` };`,\n ``,\n ` if (!record && !isCreate) {`,\n ` return <div style={{ padding: 24 }}>No record loaded.</div>;`,\n ` }`,\n ``,\n ` return (`,\n ` <div style={{`,\n ` padding: 24,`,\n ` background: \"var(--mj-bg-surface)\",`,\n ` color: \"var(--mj-text-primary)\",`,\n ` borderRadius: 8,`,\n ` border: \"1px solid var(--mj-border-default)\",`,\n ` display: \"flex\",`,\n ` flexDirection: \"column\",`,\n ` gap: 20,`,\n ` }}>`,\n ` <h2 style={{ margin: 0, color: \"var(--mj-text-primary)\" }}>${escapeJsxText(title)}</h2>`,\n sectionBlocks ? indent(sectionBlocks, 6) : ' {/* No sections — add some in Form Studio. */}',\n ` </div>`,\n ` );`,\n `}`,\n ].join('\\n');\n}\n\n/**\n * Render a single section: header + grid of elements. Elements that\n * reference fields not in the schema are skipped (linter would reject them).\n */\nfunction renderSection(\n section: FormCanvasSection,\n fieldsByName: Map<string, CuratedFormField>,\n): string {\n const elements = section.elements\n .map(el => renderElement(el, fieldsByName, section.columns))\n .filter(s => s.length > 0);\n\n if (elements.length === 0 && !section.title) {\n return '';\n }\n\n const gridStyle = section.columns === 2\n ? '{{ display: \"grid\", gridTemplateColumns: \"1fr 1fr\", gap: 16 }}'\n : '{{ display: \"flex\", flexDirection: \"column\", gap: 12 }}';\n\n return [\n `<section style={{ display: \"flex\", flexDirection: \"column\", gap: 12 }}>`,\n section.title\n ? ` <h3 style={{ margin: 0, fontSize: 14, fontWeight: 600, color: \"var(--mj-text-secondary)\", textTransform: \"uppercase\", letterSpacing: \"0.5px\" }}>${escapeJsxText(section.title)}</h3>`\n : '',\n ` <div style=${gridStyle}>`,\n ...elements.map(e => indent(e, 4)),\n ` </div>`,\n `</section>`,\n ].filter(s => s.length > 0).join('\\n');\n}\n\n/**\n * Render a single element. Routes to type-specific renderers and respects\n * the per-element column span.\n */\nfunction renderElement(\n el: FormCanvasElement,\n fieldsByName: Map<string, CuratedFormField>,\n sectionColumns: 1 | 2,\n): string {\n const span = sectionColumns === 2 && el.span === 2 ? ' gridColumn: \"span 2\",' : '';\n const wrapperStyleOpen = `<div style={{ display: \"flex\", flexDirection: \"column\", gap: 4,${span} }}>`;\n const wrapperClose = `</div>`;\n\n if (el.type === 'static-text') {\n return [\n wrapperStyleOpen,\n ` <div style={{ color: \"var(--mj-text-secondary)\", fontSize: 13 }}>${escapeJsxText(el.text ?? '')}</div>`,\n wrapperClose,\n ].join('\\n');\n }\n\n if (el.type === 'spacer') {\n return `<div style={{ minHeight: 12,${span} }} />`;\n }\n\n if (el.type === 'computed') {\n // Expression is rendered as a runtime expression against `record`.\n // We don't validate it here — the linter / runtime will surface bugs.\n const expr = (el.expression ?? '\"\"').trim() || '\"\"';\n return [\n wrapperStyleOpen,\n el.label\n ? ` <label style={{ fontSize: 11, color: \"var(--mj-text-muted)\", textTransform: \"uppercase\", letterSpacing: \"0.5px\" }}>${escapeJsxText(el.label)}</label>`\n : '',\n ` <div>{(() => { try { return ${expr}; } catch (e) { return \"—\"; } })()}</div>`,\n el.helper ? ` <small style={{ color: \"var(--mj-text-muted)\" }}>${escapeJsxText(el.helper)}</small>` : '',\n wrapperClose,\n ].filter(Boolean).join('\\n');\n }\n\n // type === 'field'\n if (!el.fieldName) return '';\n const field = fieldsByName.get(el.fieldName);\n if (!field) return '';\n\n const label = el.label ?? field.displayName ?? field.name;\n const required = el.required ?? field.required;\n const labelMarkup = `<label style={{ fontSize: 11, color: \"var(--mj-text-muted)\", textTransform: \"uppercase\", letterSpacing: \"0.5px\" }}>${escapeJsxText(label)}${required ? ' <span style={{ color: \"var(--mj-status-error)\" }}>*</span>' : ''}</label>`;\n const helperMarkup = el.helper\n ? `<small style={{ color: \"var(--mj-text-muted)\" }}>${escapeJsxText(el.helper)}</small>`\n : '';\n\n const inputMarkup = renderFieldInput(field);\n\n return [\n wrapperStyleOpen,\n ` ${labelMarkup}`,\n ` ${inputMarkup}`,\n helperMarkup ? ` ${helperMarkup}` : '',\n wrapperClose,\n ].filter(Boolean).join('\\n');\n}\n\n/**\n * Type-appropriate input rendering. Returns a single JSX expression.\n * Inputs are styled with token-backed CSS so they adapt to the host theme.\n */\nfunction renderFieldInput(field: CuratedFormField): string {\n const baseStyle = 'padding: \"8px 10px\", border: \"1px solid var(--mj-border-default)\", borderRadius: 4, background: \"var(--mj-bg-surface)\", color: \"var(--mj-text-primary)\", fontFamily: \"inherit\", fontSize: 13';\n const name = field.name;\n\n switch (field.type) {\n case 'boolean':\n return [\n `{editing`,\n ` ? <input type=\"checkbox\" checked={!!value(\"${name}\")} onChange={(e) => setField(\"${name}\", e.target.checked)} />`,\n ` : <div>{value(\"${name}\") ? \"Yes\" : \"No\"}</div>}`,\n ].join('\\n ');\n\n case 'datetime':\n return [\n `{editing`,\n ` ? <input type=\"datetime-local\" style={{ ${baseStyle} }} value={value(\"${name}\") ? new Date(value(\"${name}\")).toISOString().slice(0, 16) : \"\"} onChange={(e) => setField(\"${name}\", e.target.value ? new Date(e.target.value).toISOString() : null)} />`,\n ` : <div>{value(\"${name}\") ? new Date(value(\"${name}\")).toLocaleString() : \"—\"}</div>}`,\n ].join('\\n ');\n\n case 'number':\n return [\n `{editing`,\n ` ? <input type=\"number\" style={{ ${baseStyle} }} value={value(\"${name}\") ?? \"\"} onChange={(e) => setField(\"${name}\", e.target.value === \"\" ? null : Number(e.target.value))} />`,\n ` : <div>{value(\"${name}\") ?? \"—\"}</div>}`,\n ].join('\\n ');\n\n case 'enum': {\n const opts = (field.allowedValues ?? [])\n .map(v => ` <option value=${escapeJsxAttr(v)}>${escapeJsxText(v)}</option>`)\n .join('\\n');\n return [\n `{editing`,\n ` ? (<select style={{ ${baseStyle} }} value={value(\"${name}\") ?? \"\"} onChange={(e) => setField(\"${name}\", e.target.value)}>`,\n ` <option value=\"\">—</option>`,\n opts,\n ` </select>)`,\n ` : <div>{value(\"${name}\") ?? \"—\"}</div>}`,\n ].join('\\n ');\n }\n\n case 'foreign-key': {\n const display = field.references?.displayField ?? 'ID';\n return [\n `{editing`,\n ` ? (<select style={{ ${baseStyle} }} value={(value(\"${name}\")?.ID) ?? value(\"${name}\") ?? \"\"} onChange={(e) => setField(\"${name}\", e.target.value || null)}>`,\n ` <option value=\"\">—</option>`,\n ` {(fkOptions[\"${name}\"] || []).map((o) => <option key={o.ID} value={o.ID}>{o.${safeIdent(display)}}</option>)}`,\n ` </select>)`,\n ` : <div>{(value(\"${name}\")?.${safeIdent(display)}) ?? value(\"${name}\") ?? \"—\"}</div>}`,\n ].join('\\n ');\n }\n\n case 'string':\n default: {\n const useTextarea = (field.maxLength ?? 0) === 0 || (field.maxLength ?? 0) > 200;\n if (useTextarea) {\n return [\n `{editing`,\n ` ? <textarea style={{ ${baseStyle}, minHeight: 80, fontFamily: \"inherit\", resize: \"vertical\" }} value={value(\"${name}\") ?? \"\"} onChange={(e) => setField(\"${name}\", e.target.value)} />`,\n ` : <div style={{ whiteSpace: \"pre-wrap\" }}>{value(\"${name}\") || \"—\"}</div>}`,\n ].join('\\n ');\n }\n return [\n `{editing`,\n ` ? <input type=\"text\" style={{ ${baseStyle} }} value={value(\"${name}\") ?? \"\"} onChange={(e) => setField(\"${name}\", e.target.value)} />`,\n ` : <div>{value(\"${name}\") || \"—\"}</div>}`,\n ].join('\\n ');\n }\n }\n}\n\n/**\n * Emit useEffect blocks that lazy-load FK option lists via\n * `utilities.rv.RunView`. Triggered only for foreign-key fields actually\n * referenced on the canvas.\n *\n * Returned string is empty when no FK fields are present, so the output\n * stays tight.\n */\nfunction renderFkLoaders(\n canvas: FormCanvasModel,\n fieldsByName: Map<string, CuratedFormField>,\n): string {\n const fkNames = new Set<string>();\n for (const section of canvas.sections) {\n for (const el of section.elements) {\n if (el.type !== 'field' || !el.fieldName) continue;\n const field = fieldsByName.get(el.fieldName);\n if (field?.type === 'foreign-key' && field.references?.entity) {\n fkNames.add(field.name);\n }\n }\n }\n if (fkNames.size === 0) return '';\n\n const loaders = Array.from(fkNames).map(n => {\n const field = fieldsByName.get(n);\n if (!field?.references?.entity) return '';\n const entity = field.references.entity;\n const displayField = field.references.displayField ?? 'ID';\n return [\n ` React.useEffect(() => {`,\n ` if (!editing) return;`,\n ` let cancelled = false;`,\n ` (async () => {`,\n ` const r = await utilities.rv.RunView({`,\n ` EntityName: ${JSON.stringify(entity)},`,\n ` Fields: [\"ID\", ${JSON.stringify(displayField)}],`,\n ` MaxRows: 200,`,\n ` ResultType: \"simple\",`,\n ` });`,\n ` if (cancelled) return;`,\n ` if (r?.Success && Array.isArray(r.Results)) {`,\n ` setFkOptions((m) => ({ ...m, ${JSON.stringify(n)}: r.Results }));`,\n ` }`,\n ` })();`,\n ` return () => { cancelled = true; };`,\n ` }, [editing]);`,\n ].join('\\n');\n }).filter(s => s.length > 0);\n\n return loaders.join('\\n\\n');\n}\n\n/* ---------- string helpers ---------- */\n\nfunction escapeJsxText(s: string): string {\n return s.replace(/[<>{}]/g, ch => {\n switch (ch) {\n case '<': return '<';\n case '>': return '>';\n case '{': return '{';\n case '}': return '}';\n }\n return ch;\n });\n}\n\n/** Stringify an attribute value as a JSX attribute (always wraps in {\"…\"}). */\nfunction escapeJsxAttr(s: string): string {\n return `{${JSON.stringify(s)}}`;\n}\n\n/**\n * Ensure a display field name is a safe JS identifier. Falls back to\n * bracket access if not. Most MJ display fields are \"Name\" or \"Title\" so\n * the simple path dominates.\n */\nfunction safeIdent(name: string): string {\n if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) return name;\n return `[\"${name.replace(/\"/g, '\\\\\"')}\"]`;\n}\n\nfunction indent(block: string, spaces: number): string {\n const pad = ' '.repeat(spaces);\n return block.split('\\n').map(line => line.length === 0 ? line : pad + line).join('\\n');\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort round-trip: parse Component JSX code back into a
|
|
3
|
+
* FormCanvasModel. Lossy — anything we don't recognise falls into a single
|
|
4
|
+
* "raw" static-text element so the form remains visible in code-mode even
|
|
5
|
+
* when the canvas can't fully represent it.
|
|
6
|
+
*
|
|
7
|
+
* This is intentionally a *regex* scan, not an AST walk: pulling in
|
|
8
|
+
* `@babel/parser` (or a runtime JSX parser) would substantially increase
|
|
9
|
+
* the dashboards bundle size, and the canvas already has a fallback path
|
|
10
|
+
* ("View Code" is the primary editing surface when round-trip fails).
|
|
11
|
+
*
|
|
12
|
+
* Heuristics:
|
|
13
|
+
* - <h3> heading text → new section title
|
|
14
|
+
* - value("FieldName") OR record.FieldName references → field element
|
|
15
|
+
* - If we find fields the schema doesn't know about, we drop them
|
|
16
|
+
* - If we find zero fields, we return `null` and the caller falls back to
|
|
17
|
+
* code-only mode
|
|
18
|
+
*/
|
|
19
|
+
import type { CuratedFormSchema } from '@memberjunction/interactive-component-types/forms';
|
|
20
|
+
import type { FormCanvasModel } from './form-canvas-model';
|
|
21
|
+
export interface ParseResult {
|
|
22
|
+
/** The reconstructed canvas, or null if too lossy to use. */
|
|
23
|
+
canvas: FormCanvasModel | null;
|
|
24
|
+
/** True iff we used the AST fallback path. (Reserved — currently regex only.) */
|
|
25
|
+
usedAst: false;
|
|
26
|
+
/** True iff the parser detected JSX patterns it couldn't represent. */
|
|
27
|
+
hasUnknownConstructs: boolean;
|
|
28
|
+
/** Optional debug breadcrumbs. */
|
|
29
|
+
notes: string[];
|
|
30
|
+
}
|
|
31
|
+
export declare function parseCanvasFromCode(code: string, schema: CuratedFormSchema): ParseResult;
|
|
32
|
+
//# sourceMappingURL=code-to-canvas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-to-canvas.d.ts","sourceRoot":"","sources":["../../../src/ComponentStudio/services/code-to-canvas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mDAAmD,CAAC;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAwC,MAAM,qBAAqB,CAAC;AAGjG,MAAM,WAAW,WAAW;IACxB,6DAA6D;IAC7D,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,iFAAiF;IACjF,OAAO,EAAE,KAAK,CAAC;IACf,uEAAuE;IACvE,oBAAoB,EAAE,OAAO,CAAC;IAC9B,kCAAkC;IAClC,KAAK,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAgB,mBAAmB,CAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,iBAAiB,GAC1B,WAAW,CAiFb"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { buildEmptySection, generateCanvasId } from './form-canvas-model';
|
|
2
|
+
export function parseCanvasFromCode(code, schema) {
|
|
3
|
+
const notes = [];
|
|
4
|
+
const fieldNamesInSchema = new Set(schema.fields.map(f => f.name));
|
|
5
|
+
// 1) Find all <h3>...</h3> headings → section titles
|
|
6
|
+
const headings = Array.from(code.matchAll(/<h3[^>]*>([^<]+)<\/h3>/g))
|
|
7
|
+
.map(m => ({ index: m.index ?? 0, title: stripJsx(m[1]) }));
|
|
8
|
+
// 2) Find all field references via value("FieldName") OR record.FieldName
|
|
9
|
+
const fieldRefRegex = /(?:value\("([A-Za-z_][A-Za-z0-9_]*)"\)|record\.([A-Za-z_][A-Za-z0-9_]*))/g;
|
|
10
|
+
const fieldMatches = Array.from(code.matchAll(fieldRefRegex))
|
|
11
|
+
.map(m => ({ index: m.index ?? 0, name: m[1] ?? m[2] }));
|
|
12
|
+
if (fieldMatches.length === 0) {
|
|
13
|
+
notes.push('No field references detected; canvas not reconstructible from code.');
|
|
14
|
+
return { canvas: null, usedAst: false, hasUnknownConstructs: true, notes };
|
|
15
|
+
}
|
|
16
|
+
// 3) Bucket fields into sections by position. A field whose position is
|
|
17
|
+
// >= heading[i].index and < heading[i+1].index belongs to section i.
|
|
18
|
+
// If there are no headings, everything goes into a default "Details"
|
|
19
|
+
// section.
|
|
20
|
+
const sections = headings.length === 0
|
|
21
|
+
? [buildEmptySection('Details')]
|
|
22
|
+
: headings.map(h => ({ ...buildEmptySection(h.title), title: h.title }));
|
|
23
|
+
const seen = new Set();
|
|
24
|
+
let unknown = false;
|
|
25
|
+
for (const ref of fieldMatches) {
|
|
26
|
+
if (seen.has(ref.name))
|
|
27
|
+
continue;
|
|
28
|
+
seen.add(ref.name);
|
|
29
|
+
if (!fieldNamesInSchema.has(ref.name)) {
|
|
30
|
+
// Field referenced but not in the curated schema — likely a
|
|
31
|
+
// foreign-key sub-attribute access (e.g. value("Account")?.Name).
|
|
32
|
+
// Skip silently; the static-text fallback below will catch the
|
|
33
|
+
// visual content.
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
// Find the section this reference falls into.
|
|
37
|
+
const sectionIndex = headings.length === 0
|
|
38
|
+
? 0
|
|
39
|
+
: findSectionIndex(ref.index, headings);
|
|
40
|
+
const section = sections[sectionIndex];
|
|
41
|
+
const element = {
|
|
42
|
+
id: generateCanvasId('field'),
|
|
43
|
+
type: 'field',
|
|
44
|
+
fieldName: ref.name,
|
|
45
|
+
span: 1,
|
|
46
|
+
};
|
|
47
|
+
section.elements.push(element);
|
|
48
|
+
}
|
|
49
|
+
// 4) Look for obviously-non-representable JSX (state hooks beyond draft,
|
|
50
|
+
// custom components, inline subcomponents) — surface to the UI so we
|
|
51
|
+
// can show the "advanced" badge.
|
|
52
|
+
if (/<components\.|useReducer|useMemo|new Map\(|new Set\(/.test(code)) {
|
|
53
|
+
unknown = true;
|
|
54
|
+
notes.push('Detected advanced JSX constructs; canvas may be incomplete.');
|
|
55
|
+
}
|
|
56
|
+
// Drop empty sections (no fields landed in them).
|
|
57
|
+
const nonEmpty = sections.filter(s => s.elements.length > 0);
|
|
58
|
+
if (nonEmpty.length === 0) {
|
|
59
|
+
return { canvas: null, usedAst: false, hasUnknownConstructs: true, notes };
|
|
60
|
+
}
|
|
61
|
+
// 5) Try to find the form title from <h2>...</h2>.
|
|
62
|
+
const titleMatch = code.match(/<h2[^>]*>([^<{}]+)<\/h2>/);
|
|
63
|
+
const title = titleMatch ? stripJsx(titleMatch[1]) : undefined;
|
|
64
|
+
return {
|
|
65
|
+
canvas: {
|
|
66
|
+
entityName: schema.entityName,
|
|
67
|
+
title,
|
|
68
|
+
sections: nonEmpty,
|
|
69
|
+
},
|
|
70
|
+
usedAst: false,
|
|
71
|
+
hasUnknownConstructs: unknown,
|
|
72
|
+
notes,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function findSectionIndex(charIndex, headings) {
|
|
76
|
+
for (let i = headings.length - 1; i >= 0; i--) {
|
|
77
|
+
if (charIndex >= headings[i].index)
|
|
78
|
+
return i;
|
|
79
|
+
}
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
/** Strip simple JSX/HTML escapes so titles render cleanly. */
|
|
83
|
+
function stripJsx(s) {
|
|
84
|
+
return s
|
|
85
|
+
.replace(/</g, '<')
|
|
86
|
+
.replace(/>/g, '>')
|
|
87
|
+
.replace(/&/g, '&')
|
|
88
|
+
.replace(/{/g, '{')
|
|
89
|
+
.replace(/}/g, '}')
|
|
90
|
+
.trim();
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=code-to-canvas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-to-canvas.js","sourceRoot":"","sources":["../../../src/ComponentStudio/services/code-to-canvas.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Best-effort round-trip: parse Component JSX code back into a\n * FormCanvasModel. Lossy — anything we don't recognise falls into a single\n * \"raw\" static-text element so the form remains visible in code-mode even\n * when the canvas can't fully represent it.\n *\n * This is intentionally a *regex* scan, not an AST walk: pulling in\n * `@babel/parser` (or a runtime JSX parser) would substantially increase\n * the dashboards bundle size, and the canvas already has a fallback path\n * (\"View Code\" is the primary editing surface when round-trip fails).\n *\n * Heuristics:\n * - <h3> heading text → new section title\n * - value(\"FieldName\") OR record.FieldName references → field element\n * - If we find fields the schema doesn't know about, we drop them\n * - If we find zero fields, we return `null` and the caller falls back to\n * code-only mode\n */\nimport type { CuratedFormSchema } from '@memberjunction/interactive-component-types/forms';\nimport type { FormCanvasModel, FormCanvasSection, FormCanvasElement } from './form-canvas-model';\nimport { buildEmptySection, generateCanvasId } from './form-canvas-model';\n\nexport interface ParseResult {\n /** The reconstructed canvas, or null if too lossy to use. */\n canvas: FormCanvasModel | null;\n /** True iff we used the AST fallback path. (Reserved — currently regex only.) */\n usedAst: false;\n /** True iff the parser detected JSX patterns it couldn't represent. */\n hasUnknownConstructs: boolean;\n /** Optional debug breadcrumbs. */\n notes: string[];\n}\n\nexport function parseCanvasFromCode(\n code: string,\n schema: CuratedFormSchema,\n): ParseResult {\n const notes: string[] = [];\n const fieldNamesInSchema = new Set(schema.fields.map(f => f.name));\n\n // 1) Find all <h3>...</h3> headings → section titles\n const headings = Array.from(code.matchAll(/<h3[^>]*>([^<]+)<\\/h3>/g))\n .map(m => ({ index: m.index ?? 0, title: stripJsx(m[1]) }));\n\n // 2) Find all field references via value(\"FieldName\") OR record.FieldName\n const fieldRefRegex = /(?:value\\(\"([A-Za-z_][A-Za-z0-9_]*)\"\\)|record\\.([A-Za-z_][A-Za-z0-9_]*))/g;\n const fieldMatches = Array.from(code.matchAll(fieldRefRegex))\n .map(m => ({ index: m.index ?? 0, name: m[1] ?? m[2] }));\n\n if (fieldMatches.length === 0) {\n notes.push('No field references detected; canvas not reconstructible from code.');\n return { canvas: null, usedAst: false, hasUnknownConstructs: true, notes };\n }\n\n // 3) Bucket fields into sections by position. A field whose position is\n // >= heading[i].index and < heading[i+1].index belongs to section i.\n // If there are no headings, everything goes into a default \"Details\"\n // section.\n const sections: FormCanvasSection[] = headings.length === 0\n ? [buildEmptySection('Details')]\n : headings.map(h => ({ ...buildEmptySection(h.title), title: h.title }));\n\n const seen = new Set<string>();\n let unknown = false;\n for (const ref of fieldMatches) {\n if (seen.has(ref.name)) continue;\n seen.add(ref.name);\n if (!fieldNamesInSchema.has(ref.name)) {\n // Field referenced but not in the curated schema — likely a\n // foreign-key sub-attribute access (e.g. value(\"Account\")?.Name).\n // Skip silently; the static-text fallback below will catch the\n // visual content.\n continue;\n }\n\n // Find the section this reference falls into.\n const sectionIndex = headings.length === 0\n ? 0\n : findSectionIndex(ref.index, headings);\n const section = sections[sectionIndex];\n const element: FormCanvasElement = {\n id: generateCanvasId('field'),\n type: 'field',\n fieldName: ref.name,\n span: 1,\n };\n section.elements.push(element);\n }\n\n // 4) Look for obviously-non-representable JSX (state hooks beyond draft,\n // custom components, inline subcomponents) — surface to the UI so we\n // can show the \"advanced\" badge.\n if (/<components\\.|useReducer|useMemo|new Map\\(|new Set\\(/.test(code)) {\n unknown = true;\n notes.push('Detected advanced JSX constructs; canvas may be incomplete.');\n }\n\n // Drop empty sections (no fields landed in them).\n const nonEmpty = sections.filter(s => s.elements.length > 0);\n if (nonEmpty.length === 0) {\n return { canvas: null, usedAst: false, hasUnknownConstructs: true, notes };\n }\n\n // 5) Try to find the form title from <h2>...</h2>.\n const titleMatch = code.match(/<h2[^>]*>([^<{}]+)<\\/h2>/);\n const title = titleMatch ? stripJsx(titleMatch[1]) : undefined;\n\n return {\n canvas: {\n entityName: schema.entityName,\n title,\n sections: nonEmpty,\n },\n usedAst: false,\n hasUnknownConstructs: unknown,\n notes,\n };\n}\n\nfunction findSectionIndex(charIndex: number, headings: Array<{ index: number; title: string }>): number {\n for (let i = headings.length - 1; i >= 0; i--) {\n if (charIndex >= headings[i].index) return i;\n }\n return 0;\n}\n\n/** Strip simple JSX/HTML escapes so titles render cleanly. */\nfunction stripJsx(s: string): string {\n return s\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/&/g, '&')\n .replace(/{/g, '{')\n .replace(/}/g, '}')\n .trim();\n}\n"]}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { EventEmitter } from '@angular/core';
|
|
2
2
|
import { IMetadataProvider } from '@memberjunction/core';
|
|
3
3
|
import { ComponentSpec } from '@memberjunction/interactive-component-types';
|
|
4
|
+
import { CuratedFormSchema, FormMode } from '@memberjunction/interactive-component-types/forms';
|
|
5
|
+
import { FormCanvasModel } from './form-canvas-model';
|
|
4
6
|
import * as i0 from "@angular/core";
|
|
5
7
|
/**
|
|
6
8
|
* Lightweight summary of a database component for the browser list.
|
|
@@ -90,6 +92,30 @@ export declare class ComponentStudioStateService {
|
|
|
90
92
|
set ComponentSpec(value: ComponentSpec | null);
|
|
91
93
|
get IsRunning(): boolean;
|
|
92
94
|
set IsRunning(value: boolean);
|
|
95
|
+
private _formTargetEntityName;
|
|
96
|
+
get FormTargetEntityName(): string | null;
|
|
97
|
+
set FormTargetEntityName(value: string | null);
|
|
98
|
+
private _formCanvas;
|
|
99
|
+
get FormCanvas(): FormCanvasModel | null;
|
|
100
|
+
set FormCanvas(value: FormCanvasModel | null);
|
|
101
|
+
private _formSchema;
|
|
102
|
+
get FormSchema(): CuratedFormSchema | null;
|
|
103
|
+
private _formSelectedElementId;
|
|
104
|
+
get FormSelectedElementId(): string | null;
|
|
105
|
+
set FormSelectedElementId(value: string | null);
|
|
106
|
+
private _formSelectedSectionId;
|
|
107
|
+
get FormSelectedSectionId(): string | null;
|
|
108
|
+
set FormSelectedSectionId(value: string | null);
|
|
109
|
+
/** True when the parser couldn't fully round-trip code → canvas. UI shows a banner. */
|
|
110
|
+
private _formCodeOnlySectionsDetected;
|
|
111
|
+
get FormCodeOnlySectionsDetected(): boolean;
|
|
112
|
+
set FormCodeOnlySectionsDetected(value: boolean);
|
|
113
|
+
/** Preview mode for the Form Builder tab — drives the toolbar pills AND the preview pane. */
|
|
114
|
+
private _formPreviewMode;
|
|
115
|
+
get FormPreviewMode(): FormMode;
|
|
116
|
+
set FormPreviewMode(value: FormMode);
|
|
117
|
+
/** Build (and cache) the curated schema for the given entity. */
|
|
118
|
+
BuildFormSchema(entityName: string | null): CuratedFormSchema | null;
|
|
93
119
|
private _isLoading;
|
|
94
120
|
get IsLoading(): boolean;
|
|
95
121
|
private _currentError;
|
|
@@ -147,6 +173,9 @@ export declare class ComponentStudioStateService {
|
|
|
147
173
|
SendErrorToAI: EventEmitter<ComponentError>;
|
|
148
174
|
/** Emitted when a component spec is updated (e.g. by AI) */
|
|
149
175
|
SpecUpdated: EventEmitter<ComponentSpec>;
|
|
176
|
+
/** Emitted when the Form Builder tab's "Open in Chat" button is clicked.
|
|
177
|
+
* The dashboard subscribes and forwards to NavigationService.SetAgentContext. */
|
|
178
|
+
OpenInChatRequested: EventEmitter<void>;
|
|
150
179
|
private _provider;
|
|
151
180
|
/** Set the metadata provider this service should use. Components should call this after injection. */
|
|
152
181
|
set Provider(value: IMetadataProvider | null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-studio-state.service.d.ts","sourceRoot":"","sources":["../../../src/ComponentStudio/services/component-studio-state.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,YAAY,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAmC,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE1F,OAAO,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAC;;
|
|
1
|
+
{"version":3,"file":"component-studio-state.service.d.ts","sourceRoot":"","sources":["../../../src/ComponentStudio/services/component-studio-state.service.ts"],"names":[],"mappings":"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"}
|