@braedonsaunders/appkit-forms 0.2.2
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/LICENSE +661 -0
- package/README.md +74 -0
- package/canvas-editor.d.ts +24 -0
- package/canvas-editor.d.ts.map +1 -0
- package/canvas-editor.js +115 -0
- package/canvas-editor.js.map +1 -0
- package/canvas-runtime.d.ts +12 -0
- package/canvas-runtime.d.ts.map +1 -0
- package/canvas-runtime.js +54 -0
- package/canvas-runtime.js.map +1 -0
- package/element-preview.d.ts +9 -0
- package/element-preview.d.ts.map +1 -0
- package/element-preview.js +167 -0
- package/element-preview.js.map +1 -0
- package/form-designer.d.ts +29 -0
- package/form-designer.d.ts.map +1 -0
- package/form-designer.js +214 -0
- package/form-designer.js.map +1 -0
- package/form-flow-validation.d.ts +5 -0
- package/form-flow-validation.d.ts.map +1 -0
- package/form-flow-validation.js +154 -0
- package/form-flow-validation.js.map +1 -0
- package/form-renderer.d.ts +37 -0
- package/form-renderer.d.ts.map +1 -0
- package/form-renderer.js +282 -0
- package/form-renderer.js.map +1 -0
- package/formula-builder.d.ts +25 -0
- package/formula-builder.d.ts.map +1 -0
- package/formula-builder.js +310 -0
- package/formula-builder.js.map +1 -0
- package/generated-copy.d.ts +17 -0
- package/generated-copy.d.ts.map +1 -0
- package/generated-copy.js +396 -0
- package/generated-copy.js.map +1 -0
- package/index.d.ts +27 -0
- package/index.d.ts.map +1 -0
- package/index.js +25 -0
- package/index.js.map +1 -0
- package/logic-builder.d.ts +27 -0
- package/logic-builder.d.ts.map +1 -0
- package/logic-builder.js +106 -0
- package/logic-builder.js.map +1 -0
- package/package.json +54 -0
- package/photo-field-state.d.ts +9 -0
- package/photo-field-state.d.ts.map +1 -0
- package/photo-field-state.js +11 -0
- package/photo-field-state.js.map +1 -0
- package/production-designer-adapter.d.ts +100 -0
- package/production-designer-adapter.d.ts.map +1 -0
- package/production-designer-adapter.js +2 -0
- package/production-designer-adapter.js.map +1 -0
- package/production-file-upload.d.ts +27 -0
- package/production-file-upload.d.ts.map +1 -0
- package/production-file-upload.js +105 -0
- package/production-file-upload.js.map +1 -0
- package/production-form-designer.d.ts +3 -0
- package/production-form-designer.d.ts.map +1 -0
- package/production-form-designer.js +1124 -0
- package/production-form-designer.js.map +1 -0
- package/production-form-renderer.d.ts +16 -0
- package/production-form-renderer.d.ts.map +1 -0
- package/production-form-renderer.js +2134 -0
- package/production-form-renderer.js.map +1 -0
- package/production-runtime-adapter.d.ts +187 -0
- package/production-runtime-adapter.d.ts.map +1 -0
- package/production-runtime-adapter.js +2 -0
- package/production-runtime-adapter.js.map +1 -0
- package/production-runtime-context.d.ts +29 -0
- package/production-runtime-context.d.ts.map +1 -0
- package/production-runtime-context.js +35 -0
- package/production-runtime-context.js.map +1 -0
- package/production-runtime-layout.d.ts +21 -0
- package/production-runtime-layout.d.ts.map +1 -0
- package/production-runtime-layout.js +33 -0
- package/production-runtime-layout.js.map +1 -0
- package/properties.d.ts +36 -0
- package/properties.d.ts.map +1 -0
- package/properties.js +133 -0
- package/properties.js.map +1 -0
- package/record-actions-panel.d.ts +28 -0
- package/record-actions-panel.d.ts.map +1 -0
- package/record-actions-panel.js +136 -0
- package/record-actions-panel.js.map +1 -0
- package/record-behavior-panel.d.ts +15 -0
- package/record-behavior-panel.d.ts.map +1 -0
- package/record-behavior-panel.js +59 -0
- package/record-behavior-panel.js.map +1 -0
- package/record-config.d.ts +44 -0
- package/record-config.d.ts.map +1 -0
- package/record-config.js +2 -0
- package/record-config.js.map +1 -0
- package/record-list-panel.d.ts +16 -0
- package/record-list-panel.d.ts.map +1 -0
- package/record-list-panel.js +86 -0
- package/record-list-panel.js.map +1 -0
- package/risk-matrix.d.ts +93 -0
- package/risk-matrix.d.ts.map +1 -0
- package/risk-matrix.js +239 -0
- package/risk-matrix.js.map +1 -0
- package/runtime-controls.d.ts +19 -0
- package/runtime-controls.d.ts.map +1 -0
- package/runtime-controls.js +61 -0
- package/runtime-controls.js.map +1 -0
- package/sketch-pad-client.d.ts +4 -0
- package/sketch-pad-client.d.ts.map +1 -0
- package/sketch-pad-client.js +54 -0
- package/sketch-pad-client.js.map +1 -0
- package/sketch-pad.d.ts +13 -0
- package/sketch-pad.d.ts.map +1 -0
- package/sketch-pad.js +17 -0
- package/sketch-pad.js.map +1 -0
- package/styles.css +7 -0
- package/text.d.ts +5 -0
- package/text.d.ts.map +1 -0
- package/text.js +10 -0
- package/text.js.map +1 -0
- package/workflow-properties.d.ts +16 -0
- package/workflow-properties.d.ts.map +1 -0
- package/workflow-properties.js +31 -0
- package/workflow-properties.js.map +1 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { MapPin, Plus, Trash2 } from 'lucide-react';
|
|
4
|
+
import { Button, Checkbox, Input } from '@braedonsaunders/appkit-ui';
|
|
5
|
+
import { SignaturePad } from '@braedonsaunders/appkit-ui';
|
|
6
|
+
import { RiskMatrixField, RiskMatrixProvider } from './risk-matrix.js';
|
|
7
|
+
import { SketchPad } from './sketch-pad.js';
|
|
8
|
+
export function GpsField({ value, onChange, disabled }) {
|
|
9
|
+
const location = isRecord(value) && typeof value.lat === 'number' && typeof value.lng === 'number'
|
|
10
|
+
? value
|
|
11
|
+
: null;
|
|
12
|
+
return _jsxs("div", { className: "space-y-1.5", children: [_jsxs(Button, { type: "button", variant: "outline", size: "sm", disabled: disabled, onClick: () => navigator.geolocation?.getCurrentPosition((position) => onChange({ lat: position.coords.latitude, lng: position.coords.longitude, accuracy: position.coords.accuracy, capturedAt: new Date().toISOString() }), undefined, { enableHighAccuracy: true, timeout: 10_000 }), children: [_jsx(MapPin, { size: 14 }), location ? 'Update location' : 'Capture location'] }), location ? _jsxs("p", { className: "text-xs text-fg-muted", children: [location.lat.toFixed(5), ", ", location.lng.toFixed(5), location.accuracy ? ` · ±${Math.round(location.accuracy)} m` : ''] }) : null] });
|
|
13
|
+
}
|
|
14
|
+
export function MatrixField({ field, value, onChange, disabled }) {
|
|
15
|
+
const config = (field.config ?? {});
|
|
16
|
+
const rows = config.rows ?? [];
|
|
17
|
+
const scale = config.scale ?? [];
|
|
18
|
+
const current = isRecord(value) ? value : {};
|
|
19
|
+
return _jsx("div", { className: "overflow-x-auto rounded-md border border-border", children: _jsxs("table", { className: "w-full border-collapse text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "bg-surface-subtle", children: [_jsx("th", { className: "p-2" }), scale.map((item) => _jsx("th", { className: "p-2 text-center text-xs font-medium text-fg-muted", children: item.label }, item.value))] }) }), _jsx("tbody", { children: rows.map((row) => _jsxs("tr", { className: "border-t border-border", children: [_jsx("td", { className: "px-2 py-2 font-medium text-fg", children: row.label }), scale.map((item) => _jsx("td", { className: "p-2 text-center", children: _jsx("input", { type: "radio", name: `${field.id}_${row.key}`, disabled: disabled, checked: current[row.key] === item.value, onChange: () => onChange({ ...current, [row.key]: item.value }), "aria-label": `${row.label}: ${item.label}` }) }, item.value))] }, row.key)) })] }) });
|
|
20
|
+
}
|
|
21
|
+
export function TableField({ field, value, onChange, disabled }) {
|
|
22
|
+
const config = (field.config ?? {});
|
|
23
|
+
const columns = (config.columns ?? []);
|
|
24
|
+
const fixed = config.rowMode === 'fixed';
|
|
25
|
+
const stored = Array.isArray(value) ? value.filter(isRecord) : [];
|
|
26
|
+
const rows = fixed ? (config.rows ?? []).map((_, index) => stored[index] ?? {}) : stored;
|
|
27
|
+
const minRows = config.minRows ?? 0;
|
|
28
|
+
function setCell(rowIndex, key, next) { onChange(rows.map((row, index) => index === rowIndex ? { ...row, [key]: next } : row)); }
|
|
29
|
+
function addRow() { if (config.maxRows == null || rows.length < config.maxRows)
|
|
30
|
+
onChange([...rows, {}]); }
|
|
31
|
+
function removeRow(rowIndex) { if (rows.length > minRows)
|
|
32
|
+
onChange(rows.filter((_, index) => index !== rowIndex)); }
|
|
33
|
+
return _jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "overflow-x-auto rounded-md border border-border", children: _jsxs("table", { className: "w-full border-collapse text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "bg-surface-subtle", children: [fixed ? _jsx("th", { className: "border-b border-border p-2" }) : null, columns.map((column) => _jsx("th", { className: "border-b border-border p-2 text-left text-xs font-semibold text-fg-muted", children: column.label }, column.key)), !fixed ? _jsx("th", { className: "w-10 border-b border-border" }) : null] }) }), _jsx("tbody", { children: rows.length === 0 ? _jsx("tr", { children: _jsx("td", { colSpan: columns.length + 1, className: "p-4 text-center text-xs text-fg-muted", children: "No rows yet." }) }) : rows.map((row, rowIndex) => _jsxs("tr", { className: "border-b border-border last:border-b-0", children: [fixed ? _jsx("td", { className: "p-2 text-xs font-medium text-fg", children: config.rows?.[rowIndex]?.label }) : null, columns.map((column) => _jsx("td", { className: "min-w-32 p-1.5", children: _jsx(TableCell, { column: column, value: row[column.key], disabled: disabled, onChange: (next) => setCell(rowIndex, column.key, next) }) }, column.key)), !fixed ? _jsx("td", { className: "p-1", children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", disabled: disabled || rows.length <= minRows, "aria-label": `Remove row ${rowIndex + 1}`, onClick: () => removeRow(rowIndex), children: _jsx(Trash2, { size: 14 }) }) }) : null] }, rowIndex)) })] }) }), !fixed ? _jsxs(Button, { type: "button", variant: "outline", size: "sm", disabled: disabled || (config.maxRows != null && rows.length >= config.maxRows), onClick: addRow, children: [_jsx(Plus, { size: 14 }), "Add row"] }) : null] });
|
|
34
|
+
}
|
|
35
|
+
function TableCell({ column, value, disabled, onChange }) {
|
|
36
|
+
if (column.type === 'checkbox')
|
|
37
|
+
return _jsx(Checkbox, { checked: value === true, disabled: disabled, onChange: (event) => onChange(event.currentTarget.checked), "aria-label": column.label });
|
|
38
|
+
if (column.type === 'select')
|
|
39
|
+
return _jsxs("select", { className: "h-9 w-full rounded-md border border-border bg-surface px-2 text-sm text-fg", value: typeof value === 'string' ? value : '', disabled: disabled, onChange: (event) => onChange(event.currentTarget.value || undefined), children: [_jsx("option", { value: "", children: "Select\u2026" }), column.options?.map((option) => _jsx("option", { value: option.value, children: option.label }, option.value))] });
|
|
40
|
+
return _jsx(Input, { className: "h-9", type: column.type === 'number' ? 'number' : column.type === 'date' ? 'date' : 'text', value: column.type === 'number' ? typeof value === 'number' ? value : '' : typeof value === 'string' ? value : '', disabled: disabled, onChange: (event) => onChange(column.type === 'number' ? event.currentTarget.value === '' ? undefined : Number(event.currentTarget.value) : event.currentTarget.value) });
|
|
41
|
+
}
|
|
42
|
+
export function AddressField({ value, onChange, disabled }) {
|
|
43
|
+
const current = isRecord(value) ? value : {};
|
|
44
|
+
const set = (patch) => onChange({ ...current, ...patch });
|
|
45
|
+
return _jsxs("div", { className: "grid gap-2 sm:grid-cols-2", children: [_jsx(Input, { className: "sm:col-span-2", value: stringValue(current.line1), placeholder: "Street address", disabled: disabled, onChange: (event) => set({ line1: event.currentTarget.value }) }), _jsx(Input, { value: stringValue(current.city), placeholder: "City", disabled: disabled, onChange: (event) => set({ city: event.currentTarget.value }) }), _jsx(Input, { value: stringValue(current.region), placeholder: "State / province", disabled: disabled, onChange: (event) => set({ region: event.currentTarget.value }) }), _jsx(Input, { value: stringValue(current.postal), placeholder: "Postal code", disabled: disabled, onChange: (event) => set({ postal: event.currentTarget.value }) }), _jsx(Input, { value: stringValue(current.country), placeholder: "Country", disabled: disabled, onChange: (event) => set({ country: event.currentTarget.value }) })] });
|
|
46
|
+
}
|
|
47
|
+
export function SignatureField({ value, onChange, disabled }) {
|
|
48
|
+
const stored = typeof value === 'string' ? value : isRecord(value) && typeof value.url === 'string' ? value.url : null;
|
|
49
|
+
return _jsx(SignaturePad, { value: stored, onChange: onChange, disabled: disabled });
|
|
50
|
+
}
|
|
51
|
+
export function SketchField({ value, onChange, disabled }) {
|
|
52
|
+
const stored = isRecord(value) ? value : {};
|
|
53
|
+
return _jsx(SketchPad, { initialScene: stored.scene ?? null, readOnly: disabled, onChange: (dataUrl, scene) => onChange(dataUrl ? { dataUrl, scene } : null) });
|
|
54
|
+
}
|
|
55
|
+
export function RiskField({ field, value, onChange, disabled, matrix }) {
|
|
56
|
+
const current = isRecord(value) ? value : {};
|
|
57
|
+
return _jsx(RiskMatrixProvider, { matrix: matrix, children: _jsx(RiskMatrixField, { label: typeof field.label === 'string' ? field.label : 'Risk', likelihoodName: `${field.id}.likelihood`, severityName: `${field.id}.severity`, defaultLikelihood: typeof current.likelihood === 'number' ? current.likelihood : undefined, defaultSeverity: typeof current.severity === 'number' ? current.severity : undefined, disabled: disabled, onChange: (next) => onChange(next.likelihood == null || next.severity == null || next.score == null ? {} : next) }) });
|
|
58
|
+
}
|
|
59
|
+
function isRecord(value) { return typeof value === 'object' && value !== null && !Array.isArray(value); }
|
|
60
|
+
function stringValue(value) { return typeof value === 'string' ? value : ''; }
|
|
61
|
+
//# sourceMappingURL=runtime-controls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-controls.js","sourceRoot":"","sources":["../src/runtime-controls.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport { MapPin, Plus, Trash2 } from 'lucide-react'\nimport type { FormField, TableColumn, TableConfig } from '@braedonsaunders/appkit-forms-core'\nimport { Button, Checkbox, Input } from '@braedonsaunders/appkit-ui'\nimport { SignaturePad } from '@braedonsaunders/appkit-ui'\nimport { RiskMatrixField, RiskMatrixProvider, type RiskMatrixConfig } from './risk-matrix'\nimport { SketchPad, type SketchScene } from './sketch-pad'\n\nexport function GpsField({ value, onChange, disabled }: ControlProps) {\n const location = isRecord(value) && typeof value.lat === 'number' && typeof value.lng === 'number'\n ? value as { lat: number; lng: number; accuracy?: number }\n : null\n return <div className=\"space-y-1.5\"><Button type=\"button\" variant=\"outline\" size=\"sm\" disabled={disabled} onClick={() => navigator.geolocation?.getCurrentPosition((position) => onChange({ lat: position.coords.latitude, lng: position.coords.longitude, accuracy: position.coords.accuracy, capturedAt: new Date().toISOString() }), undefined, { enableHighAccuracy: true, timeout: 10_000 })}><MapPin size={14} />{location ? 'Update location' : 'Capture location'}</Button>{location ? <p className=\"text-xs text-fg-muted\">{location.lat.toFixed(5)}, {location.lng.toFixed(5)}{location.accuracy ? ` · ±${Math.round(location.accuracy)} m` : ''}</p> : null}</div>\n}\n\nexport function MatrixField({ field, value, onChange, disabled }: ControlProps) {\n const config = (field.config ?? {}) as { rows?: { key: string; label: string }[]; scale?: { value: string; label: string }[] }\n const rows = config.rows ?? []\n const scale = config.scale ?? []\n const current = isRecord(value) ? value : {}\n return <div className=\"overflow-x-auto rounded-md border border-border\"><table className=\"w-full border-collapse text-sm\"><thead><tr className=\"bg-surface-subtle\"><th className=\"p-2\" />{scale.map((item) => <th key={item.value} className=\"p-2 text-center text-xs font-medium text-fg-muted\">{item.label}</th>)}</tr></thead><tbody>{rows.map((row) => <tr key={row.key} className=\"border-t border-border\"><td className=\"px-2 py-2 font-medium text-fg\">{row.label}</td>{scale.map((item) => <td key={item.value} className=\"p-2 text-center\"><input type=\"radio\" name={`${field.id}_${row.key}`} disabled={disabled} checked={current[row.key] === item.value} onChange={() => onChange({ ...current, [row.key]: item.value })} aria-label={`${row.label}: ${item.label}`} /></td>)}</tr>)}</tbody></table></div>\n}\n\nexport function TableField({ field, value, onChange, disabled }: ControlProps) {\n const config = (field.config ?? {}) as Partial<TableConfig>\n const columns = (config.columns ?? []) as TableColumn[]\n const fixed = config.rowMode === 'fixed'\n const stored = Array.isArray(value) ? value.filter(isRecord) : []\n const rows = fixed ? (config.rows ?? []).map((_, index) => stored[index] ?? {}) : stored\n const minRows = config.minRows ?? 0\n function setCell(rowIndex: number, key: string, next: unknown) { onChange(rows.map((row, index) => index === rowIndex ? { ...row, [key]: next } : row)) }\n function addRow() { if (config.maxRows == null || rows.length < config.maxRows) onChange([...rows, {}]) }\n function removeRow(rowIndex: number) { if (rows.length > minRows) onChange(rows.filter((_, index) => index !== rowIndex)) }\n return <div className=\"space-y-2\"><div className=\"overflow-x-auto rounded-md border border-border\"><table className=\"w-full border-collapse text-sm\"><thead><tr className=\"bg-surface-subtle\">{fixed ? <th className=\"border-b border-border p-2\" /> : null}{columns.map((column) => <th key={column.key} className=\"border-b border-border p-2 text-left text-xs font-semibold text-fg-muted\">{column.label}</th>)}{!fixed ? <th className=\"w-10 border-b border-border\" /> : null}</tr></thead><tbody>{rows.length === 0 ? <tr><td colSpan={columns.length + 1} className=\"p-4 text-center text-xs text-fg-muted\">No rows yet.</td></tr> : rows.map((row, rowIndex) => <tr key={rowIndex} className=\"border-b border-border last:border-b-0\">{fixed ? <td className=\"p-2 text-xs font-medium text-fg\">{config.rows?.[rowIndex]?.label}</td> : null}{columns.map((column) => <td key={column.key} className=\"min-w-32 p-1.5\"><TableCell column={column} value={row[column.key]} disabled={disabled} onChange={(next) => setCell(rowIndex, column.key, next)} /></td>)}{!fixed ? <td className=\"p-1\"><Button type=\"button\" variant=\"ghost\" size=\"icon\" disabled={disabled || rows.length <= minRows} aria-label={`Remove row ${rowIndex + 1}`} onClick={() => removeRow(rowIndex)}><Trash2 size={14} /></Button></td> : null}</tr>)}</tbody></table></div>{!fixed ? <Button type=\"button\" variant=\"outline\" size=\"sm\" disabled={disabled || (config.maxRows != null && rows.length >= config.maxRows)} onClick={addRow}><Plus size={14} />Add row</Button> : null}</div>\n}\n\nfunction TableCell({ column, value, disabled, onChange }: { column: TableColumn; value: unknown; disabled?: boolean; onChange: (value: unknown) => void }) {\n if (column.type === 'checkbox') return <Checkbox checked={value === true} disabled={disabled} onChange={(event) => onChange(event.currentTarget.checked)} aria-label={column.label} />\n if (column.type === 'select') return <select className=\"h-9 w-full rounded-md border border-border bg-surface px-2 text-sm text-fg\" value={typeof value === 'string' ? value : ''} disabled={disabled} onChange={(event) => onChange(event.currentTarget.value || undefined)}><option value=\"\">Select…</option>{column.options?.map((option) => <option key={option.value} value={option.value}>{option.label}</option>)}</select>\n return <Input className=\"h-9\" type={column.type === 'number' ? 'number' : column.type === 'date' ? 'date' : 'text'} value={column.type === 'number' ? typeof value === 'number' ? value : '' : typeof value === 'string' ? value : ''} disabled={disabled} onChange={(event) => onChange(column.type === 'number' ? event.currentTarget.value === '' ? undefined : Number(event.currentTarget.value) : event.currentTarget.value)} />\n}\n\nexport function AddressField({ value, onChange, disabled }: ControlProps) {\n const current = isRecord(value) ? value : {}\n const set = (patch: Record<string, string>) => onChange({ ...current, ...patch })\n return <div className=\"grid gap-2 sm:grid-cols-2\"><Input className=\"sm:col-span-2\" value={stringValue(current.line1)} placeholder=\"Street address\" disabled={disabled} onChange={(event) => set({ line1: event.currentTarget.value })} /><Input value={stringValue(current.city)} placeholder=\"City\" disabled={disabled} onChange={(event) => set({ city: event.currentTarget.value })} /><Input value={stringValue(current.region)} placeholder=\"State / province\" disabled={disabled} onChange={(event) => set({ region: event.currentTarget.value })} /><Input value={stringValue(current.postal)} placeholder=\"Postal code\" disabled={disabled} onChange={(event) => set({ postal: event.currentTarget.value })} /><Input value={stringValue(current.country)} placeholder=\"Country\" disabled={disabled} onChange={(event) => set({ country: event.currentTarget.value })} /></div>\n}\n\nexport function SignatureField({ value, onChange, disabled }: ControlProps) {\n const stored = typeof value === 'string' ? value : isRecord(value) && typeof value.url === 'string' ? value.url : null\n return <SignaturePad value={stored} onChange={onChange} disabled={disabled} />\n}\n\nexport function SketchField({ value, onChange, disabled }: ControlProps) {\n const stored = isRecord(value) ? value as { dataUrl?: string | null; scene?: SketchScene } : {}\n return <SketchPad initialScene={stored.scene ?? null} readOnly={disabled} onChange={(dataUrl, scene) => onChange(dataUrl ? { dataUrl, scene } : null)} />\n}\n\nexport function RiskField({ field, value, onChange, disabled, matrix }: ControlProps & { matrix?: RiskMatrixConfig }) {\n const current = isRecord(value) ? value : {}\n return <RiskMatrixProvider matrix={matrix}><RiskMatrixField label={typeof field.label === 'string' ? field.label : 'Risk'} likelihoodName={`${field.id}.likelihood`} severityName={`${field.id}.severity`} defaultLikelihood={typeof current.likelihood === 'number' ? current.likelihood : undefined} defaultSeverity={typeof current.severity === 'number' ? current.severity : undefined} disabled={disabled} onChange={(next) => onChange(next.likelihood == null || next.severity == null || next.score == null ? {} : next)} /></RiskMatrixProvider>\n}\n\ntype ControlProps = { field: FormField; value: unknown; onChange: (value: unknown) => void; disabled?: boolean }\nfunction isRecord(value: unknown): value is Record<string, unknown> { return typeof value === 'object' && value !== null && !Array.isArray(value) }\nfunction stringValue(value: unknown): string { return typeof value === 'string' ? value : '' }\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import '@excalidraw/excalidraw/index.css';
|
|
2
|
+
import type { SketchPadProps } from './sketch-pad.js';
|
|
3
|
+
export default function SketchPadClient({ initialScene, onChange, readOnly, }: SketchPadProps): import("react").JSX.Element;
|
|
4
|
+
//# sourceMappingURL=sketch-pad-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sketch-pad-client.d.ts","sourceRoot":"","sources":["../src/sketch-pad-client.tsx"],"names":[],"mappings":"AAEA,OAAO,kCAAkC,CAAA;AAazC,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,cAAc,CAAA;AAW/D,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EACtC,YAAY,EACZ,QAAQ,EACR,QAAgB,GACjB,EAAE,cAAc,+BAuChB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import '@excalidraw/excalidraw/index.css';
|
|
4
|
+
// SketchPad — a comprehensive freehand drawing / diagram canvas built on
|
|
5
|
+
// Excalidraw (shapes, arrows, text, freehand, images). Used by the form
|
|
6
|
+
// `sketch` element (e.g. a lift-plan diagram). Outputs a PNG data-url plus the
|
|
7
|
+
// editable Excalidraw scene so the drawing can be re-opened and amended.
|
|
8
|
+
//
|
|
9
|
+
// Excalidraw touches `window` at module scope, so it is mounted lazily on the
|
|
10
|
+
// client. The heavy bundle only downloads when a sketch element renders.
|
|
11
|
+
import { useCallback, useRef } from 'react';
|
|
12
|
+
import { Excalidraw, exportToBlob } from '@excalidraw/excalidraw';
|
|
13
|
+
function blobToDataUrl(blob) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const reader = new FileReader();
|
|
16
|
+
reader.onload = () => resolve(reader.result);
|
|
17
|
+
reader.onerror = reject;
|
|
18
|
+
reader.readAsDataURL(blob);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export default function SketchPadClient({ initialScene, onChange, readOnly = false, }) {
|
|
22
|
+
const debounceRef = useRef(null);
|
|
23
|
+
const handleChange = useCallback((elements, appState, files) => {
|
|
24
|
+
if (debounceRef.current)
|
|
25
|
+
clearTimeout(debounceRef.current);
|
|
26
|
+
debounceRef.current = setTimeout(async () => {
|
|
27
|
+
const live = elements.filter((el) => !el.isDeleted);
|
|
28
|
+
const scene = {
|
|
29
|
+
elements: live,
|
|
30
|
+
appState: { viewBackgroundColor: appState.viewBackgroundColor },
|
|
31
|
+
files,
|
|
32
|
+
};
|
|
33
|
+
if (live.length === 0) {
|
|
34
|
+
onChange(null, scene);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const blob = await exportToBlob({
|
|
39
|
+
elements: live,
|
|
40
|
+
appState: { ...appState, exportBackground: true, exportWithDarkMode: false },
|
|
41
|
+
files,
|
|
42
|
+
mimeType: 'image/png',
|
|
43
|
+
quality: 0.92,
|
|
44
|
+
});
|
|
45
|
+
onChange(await blobToDataUrl(blob), scene);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
console.warn('[sketch] export failed', err);
|
|
49
|
+
}
|
|
50
|
+
}, 700);
|
|
51
|
+
}, [onChange]);
|
|
52
|
+
return _jsx(Excalidraw, { initialData: initialScene ? initialScene : undefined, viewModeEnabled: readOnly, onChange: handleChange, UIOptions: { canvasActions: { loadScene: false, saveToActiveFile: false, export: false, saveAsImage: false } } });
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=sketch-pad-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sketch-pad-client.js","sourceRoot":"","sources":["../src/sketch-pad-client.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,kCAAkC,CAAA;AAEzC,yEAAyE;AACzE,wEAAwE;AACxE,+EAA+E;AAC/E,yEAAyE;AACzE,EAAE;AACF,8EAA8E;AAC9E,yEAAyE;AAEzE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAIjE,SAAS,aAAa,CAAC,IAAU;IAC/B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAA;QAC/B,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAgB,CAAC,CAAA;QACtD,MAAM,CAAC,OAAO,GAAG,MAAM,CAAA;QACvB,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EACtC,YAAY,EACZ,QAAQ,EACR,QAAQ,GAAG,KAAK,GACD;IACf,MAAM,WAAW,GAAG,MAAM,CAAuC,IAAI,CAAC,CAAA;IACtE,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;QAC5B,IAAI,WAAW,CAAC,OAAO;YAAE,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAA;QAC1D,WAAW,CAAC,OAAO,GAAG,UAAU,CAAC,KAAK,IAAI,EAAE;YAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAA;YACnD,MAAM,KAAK,GAAgB;gBACzB,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,EAAE,mBAAmB,EAAE,QAAQ,CAAC,mBAAmB,EAAE;gBAC/D,KAAK;aACN,CAAA;YACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACtB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;gBACrB,OAAM;YACR,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC;oBAC9B,QAAQ,EAAE,IAAI;oBACd,QAAQ,EAAE,EAAE,GAAG,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE;oBAC5E,KAAK;oBACL,QAAQ,EAAE,WAAW;oBACrB,OAAO,EAAE,IAAI;iBACd,CAAC,CAAA;gBACF,QAAQ,CAAC,MAAM,aAAa,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAA;YAC5C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAA;YAC7C,CAAC;QACH,CAAC,EAAE,GAAG,CAAC,CAAA;IACT,CAAC,EACD,CAAC,QAAQ,CAAC,CACX,CAAA;IAED,OAAO,KAAC,UAAU,IAChB,WAAW,EAAE,YAAY,CAAC,CAAC,CAAE,YAAsD,CAAC,CAAC,CAAC,SAAS,EAC/F,eAAe,EAAE,QAAQ,EACzB,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,EAAE,aAAa,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,GAC9G,CAAA;AACJ,CAAC","sourcesContent":["'use client'\n\nimport '@excalidraw/excalidraw/index.css'\n\n// SketchPad — a comprehensive freehand drawing / diagram canvas built on\n// Excalidraw (shapes, arrows, text, freehand, images). Used by the form\n// `sketch` element (e.g. a lift-plan diagram). Outputs a PNG data-url plus the\n// editable Excalidraw scene so the drawing can be re-opened and amended.\n//\n// Excalidraw touches `window` at module scope, so it is mounted lazily on the\n// client. The heavy bundle only downloads when a sketch element renders.\n\nimport { useCallback, useRef } from 'react'\nimport { Excalidraw, exportToBlob } from '@excalidraw/excalidraw'\nimport type { ExcalidrawInitialDataState, ExcalidrawProps } from '@excalidraw/excalidraw/types'\nimport type { SketchPadProps, SketchScene } from './sketch-pad'\n\nfunction blobToDataUrl(blob: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = reject\n reader.readAsDataURL(blob)\n })\n}\n\nexport default function SketchPadClient({\n initialScene,\n onChange,\n readOnly = false,\n}: SketchPadProps) {\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const handleChange = useCallback<NonNullable<ExcalidrawProps['onChange']>>(\n (elements, appState, files) => {\n if (debounceRef.current) clearTimeout(debounceRef.current)\n debounceRef.current = setTimeout(async () => {\n const live = elements.filter((el) => !el.isDeleted)\n const scene: SketchScene = {\n elements: live,\n appState: { viewBackgroundColor: appState.viewBackgroundColor },\n files,\n }\n if (live.length === 0) {\n onChange(null, scene)\n return\n }\n try {\n const blob = await exportToBlob({\n elements: live,\n appState: { ...appState, exportBackground: true, exportWithDarkMode: false },\n files,\n mimeType: 'image/png',\n quality: 0.92,\n })\n onChange(await blobToDataUrl(blob), scene)\n } catch (err) {\n console.warn('[sketch] export failed', err)\n }\n }, 700)\n },\n [onChange],\n )\n\n return <Excalidraw\n initialData={initialScene ? (initialScene as unknown as ExcalidrawInitialDataState) : undefined}\n viewModeEnabled={readOnly}\n onChange={handleChange}\n UIOptions={{ canvasActions: { loadScene: false, saveToActiveFile: false, export: false, saveAsImage: false } }}\n />\n}\n"]}
|
package/sketch-pad.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type SketchScene = {
|
|
2
|
+
elements?: readonly unknown[];
|
|
3
|
+
appState?: Record<string, unknown>;
|
|
4
|
+
files?: Record<string, unknown>;
|
|
5
|
+
};
|
|
6
|
+
export type SketchPadProps = {
|
|
7
|
+
initialScene?: SketchScene | null;
|
|
8
|
+
onChange: (dataUrl: string | null, scene: SketchScene) => void;
|
|
9
|
+
height?: number;
|
|
10
|
+
readOnly?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare function SketchPad(props: SketchPadProps): import("react").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=sketch-pad.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sketch-pad.d.ts","sourceRoot":"","sources":["../src/sketch-pad.tsx"],"names":[],"mappings":"AAOA,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IACjC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAOD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+BAgB9C"}
|
package/sketch-pad.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { lazy, Suspense, useEffect, useState } from 'react';
|
|
4
|
+
import { GeneratedText } from './generated-copy.js';
|
|
5
|
+
// Keep both the browser-only drawing engine and its package stylesheet behind
|
|
6
|
+
// the mounted boundary. Importing @braedonsaunders/appkit-forms therefore stays safe in Node,
|
|
7
|
+
// while applications that actually render a sketch receive the complete UI.
|
|
8
|
+
const SketchPadClient = lazy(() => import('./sketch-pad-client.js'));
|
|
9
|
+
export function SketchPad(props) {
|
|
10
|
+
const [mounted, setMounted] = useState(false);
|
|
11
|
+
useEffect(() => setMounted(true), []);
|
|
12
|
+
return (_jsx("div", { style: { height: props.height ?? 440 }, className: "overflow-hidden rounded-md border border-border", children: mounted ? (_jsx(Suspense, { fallback: _jsx(SketchLoading, {}), children: _jsx(SketchPadClient, { ...props }) })) : _jsx(SketchLoading, {}) }));
|
|
13
|
+
}
|
|
14
|
+
function SketchLoading() {
|
|
15
|
+
return _jsx("div", { className: "flex h-full w-full items-center justify-center text-sm text-fg-muted", children: _jsx(GeneratedText, { id: "m_0839e554b28c58" }) });
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=sketch-pad.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sketch-pad.js","sourceRoot":"","sources":["../src/sketch-pad.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAiBhD,8EAA8E;AAC9E,8FAA8F;AAC9F,4EAA4E;AAC5E,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAAA;AAEjE,MAAM,UAAU,SAAS,CAAC,KAAqB;IAC7C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC7C,SAAS,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAA;IAErC,OAAO,CACL,cACE,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG,EAAE,EACtC,SAAS,EAAC,iDAAiD,YAE1D,OAAO,CAAC,CAAC,CAAC,CACT,KAAC,QAAQ,IAAC,QAAQ,EAAE,KAAC,aAAa,KAAG,YACnC,KAAC,eAAe,OAAK,KAAK,GAAI,GACrB,CACZ,CAAC,CAAC,CAAC,KAAC,aAAa,KAAG,GACjB,CACP,CAAA;AACH,CAAC;AAED,SAAS,aAAa;IACpB,OAAO,cAAK,SAAS,EAAC,sEAAsE,YAAC,KAAC,aAAa,IAAC,EAAE,EAAC,kBAAkB,GAAG,GAAM,CAAA;AAC5I,CAAC","sourcesContent":["'use client'\n\nimport { lazy, Suspense, useEffect, useState } from 'react'\nimport { GeneratedText } from './generated-copy'\n\n// The editable scene persisted alongside the rendered PNG. It remains opaque\n// to AppKit and is handed back to the drawing engine when the field reopens.\nexport type SketchScene = {\n elements?: readonly unknown[]\n appState?: Record<string, unknown>\n files?: Record<string, unknown>\n}\n\nexport type SketchPadProps = {\n initialScene?: SketchScene | null\n onChange: (dataUrl: string | null, scene: SketchScene) => void\n height?: number\n readOnly?: boolean\n}\n\n// Keep both the browser-only drawing engine and its package stylesheet behind\n// the mounted boundary. Importing @braedonsaunders/appkit-forms therefore stays safe in Node,\n// while applications that actually render a sketch receive the complete UI.\nconst SketchPadClient = lazy(() => import('./sketch-pad-client'))\n\nexport function SketchPad(props: SketchPadProps) {\n const [mounted, setMounted] = useState(false)\n useEffect(() => setMounted(true), [])\n\n return (\n <div\n style={{ height: props.height ?? 440 }}\n className=\"overflow-hidden rounded-md border border-border\"\n >\n {mounted ? (\n <Suspense fallback={<SketchLoading />}>\n <SketchPadClient {...props} />\n </Suspense>\n ) : <SketchLoading />}\n </div>\n )\n}\n\nfunction SketchLoading() {\n return <div className=\"flex h-full w-full items-center justify-center text-sm text-fg-muted\"><GeneratedText id=\"m_0839e554b28c58\" /></div>\n}\n"]}
|
package/styles.css
ADDED
package/text.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type AppLocale } from '@braedonsaunders/appkit-i18n';
|
|
2
|
+
import type { I18nString } from '@braedonsaunders/appkit-forms-core';
|
|
3
|
+
export declare function readText(value: I18nString | undefined, locale?: AppLocale, fallback?: string): string;
|
|
4
|
+
export declare function writeText(previous: I18nString | undefined, value: string, locale: AppLocale): I18nString;
|
|
5
|
+
//# sourceMappingURL=text.d.ts.map
|
package/text.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../src/text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAC3F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AAEpE,wBAAgB,QAAQ,CACtB,KAAK,EAAE,UAAU,GAAG,SAAS,EAC7B,MAAM,GAAE,SAA0B,EAClC,QAAQ,SAAK,GACZ,MAAM,CAER;AAED,wBAAgB,SAAS,CACvB,QAAQ,EAAE,UAAU,GAAG,SAAS,EAChC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,SAAS,GAChB,UAAU,CAIZ"}
|
package/text.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DEFAULT_LOCALE, localizeText } from '@braedonsaunders/appkit-i18n';
|
|
2
|
+
export function readText(value, locale = DEFAULT_LOCALE, fallback = '') {
|
|
3
|
+
return localizeText(value, locale, fallback);
|
|
4
|
+
}
|
|
5
|
+
export function writeText(previous, value, locale) {
|
|
6
|
+
return typeof previous === 'string' || previous === undefined
|
|
7
|
+
? value
|
|
8
|
+
: { ...previous, [locale]: value };
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=text.js.map
|
package/text.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../src/text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAkB,MAAM,8BAA8B,CAAA;AAG3F,MAAM,UAAU,QAAQ,CACtB,KAA6B,EAC7B,SAAoB,cAAc,EAClC,QAAQ,GAAG,EAAE;IAEb,OAAO,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAA;AAC9C,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,QAAgC,EAChC,KAAa,EACb,MAAiB;IAEjB,OAAO,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,SAAS;QAC3D,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAA;AACtC,CAAC","sourcesContent":["import { DEFAULT_LOCALE, localizeText, type AppLocale } from '@braedonsaunders/appkit-i18n'\nimport type { I18nString } from '@braedonsaunders/appkit-forms-core'\n\nexport function readText(\n value: I18nString | undefined,\n locale: AppLocale = DEFAULT_LOCALE,\n fallback = '',\n): string {\n return localizeText(value, locale, fallback)\n}\n\nexport function writeText(\n previous: I18nString | undefined,\n value: string,\n locale: AppLocale,\n): I18nString {\n return typeof previous === 'string' || previous === undefined\n ? value\n : { ...previous, [locale]: value }\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { FormSchemaV1, FormWorkflowStep } from '@braedonsaunders/appkit-forms-core';
|
|
3
|
+
import type { AppLocale } from '@braedonsaunders/appkit-i18n';
|
|
4
|
+
export declare function FormWorkflowEditor({ schema, locale, readOnly, onChange }: {
|
|
5
|
+
schema: FormSchemaV1;
|
|
6
|
+
locale: AppLocale;
|
|
7
|
+
readOnly?: boolean;
|
|
8
|
+
onChange: (steps: FormWorkflowStep[]) => void;
|
|
9
|
+
}): React.JSX.Element;
|
|
10
|
+
export declare function FormTabsEditor({ schema, locale, readOnly, onChange }: {
|
|
11
|
+
schema: FormSchemaV1;
|
|
12
|
+
locale: AppLocale;
|
|
13
|
+
readOnly?: boolean;
|
|
14
|
+
onChange: (tabs: NonNullable<FormSchemaV1['tabs']> | undefined, sectionTabs: Record<string, string | undefined>) => void;
|
|
15
|
+
}): React.JSX.Element;
|
|
16
|
+
//# sourceMappingURL=workflow-properties.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-properties.d.ts","sourceRoot":"","sources":["../src/workflow-properties.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAA;AACxF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAc7D,wBAAgB,kBAAkB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAgB,EAAE,QAAQ,EAAE,EAAE;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAA;CAAE,qBAKhM;AAED,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAgB,EAAE,QAAQ,EAAE,EAAE;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KAAK,IAAI,CAAA;CAAE,qBAKvQ"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { ArrowDown, ArrowUp, Plus, Trash2 } from 'lucide-react';
|
|
5
|
+
import { Button, Checkbox, Input, Label, Select } from '@braedonsaunders/appkit-ui';
|
|
6
|
+
import { readText, writeText } from './text.js';
|
|
7
|
+
function uniqueId(prefix, values) {
|
|
8
|
+
let index = values.size + 1;
|
|
9
|
+
while (values.has(`${prefix}_${index}`))
|
|
10
|
+
index += 1;
|
|
11
|
+
return `${prefix}_${index}`;
|
|
12
|
+
}
|
|
13
|
+
function emptyStep(existing) {
|
|
14
|
+
return { key: uniqueId('step', new Set(existing.map((step) => step.key))), title: { en: 'New step' }, assignee: { type: 'expression', expr: '$submitter' } };
|
|
15
|
+
}
|
|
16
|
+
export function FormWorkflowEditor({ schema, locale, readOnly = false, onChange }) {
|
|
17
|
+
const steps = schema.workflow?.steps ?? [];
|
|
18
|
+
const setStep = (index, patch) => onChange(steps.map((step, candidate) => candidate === index ? { ...step, ...patch } : step));
|
|
19
|
+
const move = (index, delta) => { const target = index + delta; if (target < 0 || target >= steps.length)
|
|
20
|
+
return; const next = [...steps]; [next[index], next[target]] = [next[target], next[index]]; onChange(next); };
|
|
21
|
+
return _jsxs("section", { className: "space-y-2", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-fg", children: "Guided workflow" }), _jsx("p", { className: "mt-1 text-xs text-fg-muted", children: "Build the ordered fill, review, and sign-off steps. Sections are assigned to these steps from section settings." })] }), _jsx("ul", { className: "space-y-2", children: steps.map((step, index) => _jsx("li", { className: "rounded-md border border-border bg-surface p-2", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-1 grid size-6 shrink-0 place-items-center rounded-full bg-primary-subtle text-xs font-semibold text-primary", children: index + 1 }), _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsx(Field, { label: "Title", children: _jsx(Input, { className: "h-8 text-xs", value: readText(step.title, locale, step.key), disabled: readOnly, onChange: (event) => setStep(index, { title: writeText(step.title, event.target.value, locale) }) }) }), _jsx(Field, { label: "Step key", children: _jsx(Input, { className: "h-8 font-mono text-xs", value: step.key, disabled: true }) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsx(Field, { label: "Assignee", children: _jsxs(Select, { value: step.assignee.type, disabled: readOnly, onChange: (event) => { const type = event.target.value; setStep(index, { assignee: type === 'literal' ? { type: 'literal', userId: '' } : type === 'role' ? { type: 'role', role: '' } : { type: 'expression', expr: '$submitter' } }); }, children: [_jsx("option", { value: "expression", children: "Expression" }), _jsx("option", { value: "role", children: "Role" }), _jsx("option", { value: "literal", children: "Specific user" })] }) }), _jsx(Field, { label: "Value", children: _jsx(Input, { className: "h-8 text-xs", disabled: readOnly, value: step.assignee.type === 'literal' ? step.assignee.userId : step.assignee.type === 'role' ? step.assignee.role : step.assignee.expr, onChange: (event) => { const value = event.target.value; setStep(index, { assignee: step.assignee.type === 'literal' ? { type: 'literal', userId: value } : step.assignee.type === 'role' ? { type: 'role', role: value } : { type: 'expression', expr: value } }); } }) })] }), _jsxs("label", { className: "flex items-center gap-2 text-xs", children: [_jsx(Checkbox, { checked: step.signatureRequired ?? false, disabled: readOnly, onChange: (event) => setStep(index, { signatureRequired: event.currentTarget.checked }) }), "Require signature"] })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Icon, { label: "Move step up", disabled: readOnly || index === 0, onClick: () => move(index, -1), children: _jsx(ArrowUp, { size: 12 }) }), _jsx(Icon, { label: "Move step down", disabled: readOnly || index === steps.length - 1, onClick: () => move(index, 1), children: _jsx(ArrowDown, { size: 12 }) }), _jsx(Icon, { label: "Delete step", disabled: readOnly || steps.length === 1, onClick: () => onChange(steps.filter((_, candidate) => candidate !== index)), children: _jsx(Trash2, { size: 12 }) })] })] }) }, step.key)) }), _jsxs(Button, { variant: "outline", disabled: readOnly, onClick: () => onChange([...steps, emptyStep(steps)]), className: "w-full", children: [_jsx(Plus, { size: 14 }), "Add workflow step"] })] });
|
|
22
|
+
}
|
|
23
|
+
export function FormTabsEditor({ schema, locale, readOnly = false, onChange }) {
|
|
24
|
+
const tabs = schema.tabs ?? [];
|
|
25
|
+
function commit(next, assignments = Object.fromEntries(schema.sections.map((section) => [section.id, section.tabId]))) { onChange(next.length ? next : undefined, assignments); }
|
|
26
|
+
function add() { const id = uniqueId('tab', new Set(tabs.map((tab) => tab.id))); const next = [...tabs, { id, title: writeText(undefined, `Tab ${tabs.length + 1}`, locale) }]; const assignments = Object.fromEntries(schema.sections.map((section) => [section.id, section.tabId ?? (tabs.length === 0 ? id : undefined)])); commit(next, assignments); }
|
|
27
|
+
return _jsxs("section", { className: "space-y-2 border-b border-border pb-4", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-fg", children: "App pages" }), _jsx("p", { className: "mt-1 text-xs text-fg-muted", children: "Optional top-level tabs organize sections into pages without changing the workflow." })] }), tabs.map((tab, index) => _jsxs("div", { className: "flex gap-1", children: [_jsx(Input, { value: readText(tab.title, locale, tab.id), disabled: readOnly, onChange: (event) => commit(tabs.map((candidate) => candidate.id === tab.id ? { ...candidate, title: writeText(candidate.title, event.target.value, locale) } : candidate)) }), _jsx(Icon, { label: "Delete tab", disabled: readOnly, onClick: () => { const next = tabs.filter((candidate) => candidate.id !== tab.id); const fallback = next[0]?.id; commit(next, Object.fromEntries(schema.sections.map((section) => [section.id, section.tabId === tab.id ? fallback : section.tabId]))); }, children: _jsx(Trash2, { size: 13 }) }), index === 0 ? null : _jsxs("span", { className: "sr-only", children: ["Tab ", index + 1] })] }, tab.id)), _jsxs(Button, { variant: "outline", size: "sm", disabled: readOnly, onClick: add, children: [_jsx(Plus, { size: 12 }), "Add page"] })] });
|
|
28
|
+
}
|
|
29
|
+
function Field({ label, children }) { return _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: label }), children] }); }
|
|
30
|
+
function Icon({ label, disabled, onClick, children }) { return _jsx("button", { type: "button", "aria-label": label, title: label, disabled: disabled, onClick: onClick, className: "grid size-7 place-items-center rounded text-fg-subtle hover:bg-surface-hover hover:text-fg disabled:opacity-30", children: children }); }
|
|
31
|
+
//# sourceMappingURL=workflow-properties.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-properties.js","sourceRoot":"","sources":["../src/workflow-properties.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { ArrowDown, ArrowUp, Plus, Trash2 } from 'lucide-react'\nimport type { FormSchemaV1, FormWorkflowStep } from '@braedonsaunders/appkit-forms-core'\nimport type { AppLocale } from '@braedonsaunders/appkit-i18n'\nimport { Button, Checkbox, Input, Label, Select } from '@braedonsaunders/appkit-ui'\nimport { readText, writeText } from './text'\n\nfunction uniqueId(prefix: string, values: ReadonlySet<string>): string {\n let index = values.size + 1\n while (values.has(`${prefix}_${index}`)) index += 1\n return `${prefix}_${index}`\n}\n\nfunction emptyStep(existing: readonly FormWorkflowStep[]): FormWorkflowStep {\n return { key: uniqueId('step', new Set(existing.map((step) => step.key))), title: { en: 'New step' }, assignee: { type: 'expression', expr: '$submitter' } }\n}\n\nexport function FormWorkflowEditor({ schema, locale, readOnly = false, onChange }: { schema: FormSchemaV1; locale: AppLocale; readOnly?: boolean; onChange: (steps: FormWorkflowStep[]) => void }) {\n const steps = schema.workflow?.steps ?? []\n const setStep = (index: number, patch: Partial<FormWorkflowStep>) => onChange(steps.map((step, candidate) => candidate === index ? { ...step, ...patch } : step))\n const move = (index: number, delta: -1 | 1) => { const target = index + delta; if (target < 0 || target >= steps.length) return; const next = [...steps]; [next[index], next[target]] = [next[target]!, next[index]!]; onChange(next) }\n return <section className=\"space-y-2\"><div><h3 className=\"text-sm font-semibold text-fg\">Guided workflow</h3><p className=\"mt-1 text-xs text-fg-muted\">Build the ordered fill, review, and sign-off steps. Sections are assigned to these steps from section settings.</p></div><ul className=\"space-y-2\">{steps.map((step, index) => <li key={step.key} className=\"rounded-md border border-border bg-surface p-2\"><div className=\"flex items-start gap-2\"><span className=\"mt-1 grid size-6 shrink-0 place-items-center rounded-full bg-primary-subtle text-xs font-semibold text-primary\">{index + 1}</span><div className=\"min-w-0 flex-1 space-y-2\"><div className=\"grid grid-cols-2 gap-2\"><Field label=\"Title\"><Input className=\"h-8 text-xs\" value={readText(step.title, locale, step.key)} disabled={readOnly} onChange={(event) => setStep(index, { title: writeText(step.title, event.target.value, locale) })} /></Field><Field label=\"Step key\"><Input className=\"h-8 font-mono text-xs\" value={step.key} disabled /></Field></div><div className=\"grid grid-cols-2 gap-2\"><Field label=\"Assignee\"><Select value={step.assignee.type} disabled={readOnly} onChange={(event) => { const type = event.target.value; setStep(index, { assignee: type === 'literal' ? { type: 'literal', userId: '' } : type === 'role' ? { type: 'role', role: '' } : { type: 'expression', expr: '$submitter' } }) }}><option value=\"expression\">Expression</option><option value=\"role\">Role</option><option value=\"literal\">Specific user</option></Select></Field><Field label=\"Value\"><Input className=\"h-8 text-xs\" disabled={readOnly} value={step.assignee.type === 'literal' ? step.assignee.userId : step.assignee.type === 'role' ? step.assignee.role : step.assignee.expr} onChange={(event) => { const value = event.target.value; setStep(index, { assignee: step.assignee.type === 'literal' ? { type: 'literal', userId: value } : step.assignee.type === 'role' ? { type: 'role', role: value } : { type: 'expression', expr: value } }) }} /></Field></div><label className=\"flex items-center gap-2 text-xs\"><Checkbox checked={step.signatureRequired ?? false} disabled={readOnly} onChange={(event) => setStep(index, { signatureRequired: event.currentTarget.checked })} />Require signature</label></div><div className=\"flex flex-col gap-1\"><Icon label=\"Move step up\" disabled={readOnly || index === 0} onClick={() => move(index, -1)}><ArrowUp size={12} /></Icon><Icon label=\"Move step down\" disabled={readOnly || index === steps.length - 1} onClick={() => move(index, 1)}><ArrowDown size={12} /></Icon><Icon label=\"Delete step\" disabled={readOnly || steps.length === 1} onClick={() => onChange(steps.filter((_, candidate) => candidate !== index))}><Trash2 size={12} /></Icon></div></div></li>)}</ul><Button variant=\"outline\" disabled={readOnly} onClick={() => onChange([...steps, emptyStep(steps)])} className=\"w-full\"><Plus size={14} />Add workflow step</Button></section>\n}\n\nexport function FormTabsEditor({ schema, locale, readOnly = false, onChange }: { schema: FormSchemaV1; locale: AppLocale; readOnly?: boolean; onChange: (tabs: NonNullable<FormSchemaV1['tabs']> | undefined, sectionTabs: Record<string, string | undefined>) => void }) {\n const tabs = schema.tabs ?? []\n function commit(next: typeof tabs, assignments: Record<string, string | undefined> = Object.fromEntries(schema.sections.map((section) => [section.id, section.tabId]))) { onChange(next.length ? next : undefined, assignments) }\n function add() { const id = uniqueId('tab', new Set(tabs.map((tab) => tab.id))); const next = [...tabs, { id, title: writeText(undefined, `Tab ${tabs.length + 1}`, locale) }]; const assignments = Object.fromEntries(schema.sections.map((section) => [section.id, section.tabId ?? (tabs.length === 0 ? id : undefined)])); commit(next, assignments) }\n return <section className=\"space-y-2 border-b border-border pb-4\"><div><h3 className=\"text-sm font-semibold text-fg\">App pages</h3><p className=\"mt-1 text-xs text-fg-muted\">Optional top-level tabs organize sections into pages without changing the workflow.</p></div>{tabs.map((tab, index) => <div key={tab.id} className=\"flex gap-1\"><Input value={readText(tab.title, locale, tab.id)} disabled={readOnly} onChange={(event) => commit(tabs.map((candidate) => candidate.id === tab.id ? { ...candidate, title: writeText(candidate.title, event.target.value, locale) } : candidate))} /><Icon label=\"Delete tab\" disabled={readOnly} onClick={() => { const next = tabs.filter((candidate) => candidate.id !== tab.id); const fallback = next[0]?.id; commit(next, Object.fromEntries(schema.sections.map((section) => [section.id, section.tabId === tab.id ? fallback : section.tabId]))) }}><Trash2 size={13} /></Icon>{index === 0 ? null : <span className=\"sr-only\">Tab {index + 1}</span>}</div>)}<Button variant=\"outline\" size=\"sm\" disabled={readOnly} onClick={add}><Plus size={12} />Add page</Button></section>\n}\n\nfunction Field({ label, children }: { label: string; children: React.ReactNode }) { return <div className=\"space-y-1\"><Label className=\"text-[10px]\">{label}</Label>{children}</div> }\nfunction Icon({ label, disabled, onClick, children }: { label: string; disabled: boolean; onClick: () => void; children: React.ReactNode }) { return <button type=\"button\" aria-label={label} title={label} disabled={disabled} onClick={onClick} className=\"grid size-7 place-items-center rounded text-fg-subtle hover:bg-surface-hover hover:text-fg disabled:opacity-30\">{children}</button> }\n"]}
|