@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,105 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Camera, FileUp, Loader2, Trash2 } from 'lucide-react';
|
|
5
|
+
import { Button, uploadReservedFile, } from '@braedonsaunders/appkit-ui';
|
|
6
|
+
function kindFromType(mime) {
|
|
7
|
+
if (mime.startsWith('image/'))
|
|
8
|
+
return 'image';
|
|
9
|
+
if (mime.startsWith('video/'))
|
|
10
|
+
return 'video';
|
|
11
|
+
if (mime.startsWith('audio/'))
|
|
12
|
+
return 'audio';
|
|
13
|
+
if (mime === 'application/pdf' || mime.includes('document') || mime.includes('sheet')) {
|
|
14
|
+
return 'document';
|
|
15
|
+
}
|
|
16
|
+
return 'other';
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Production form-field uploader: controlled attachments, environment camera
|
|
20
|
+
* capture, bounded counts, reserved upload/finalize protocol, previews, and
|
|
21
|
+
* removal. Storage policy remains an injected application boundary.
|
|
22
|
+
*/
|
|
23
|
+
export function ProductionFileUpload({ value, onChange, requestUpload, finalizeUpload, accept, multiple = true, maxFiles, onUploadingChange, variant = 'file', }) {
|
|
24
|
+
const inputRef = React.useRef(null);
|
|
25
|
+
const [pending, startTransition] = React.useTransition();
|
|
26
|
+
const [error, setError] = React.useState(null);
|
|
27
|
+
const uploadingCallback = React.useRef(onUploadingChange);
|
|
28
|
+
React.useEffect(() => {
|
|
29
|
+
uploadingCallback.current = onUploadingChange;
|
|
30
|
+
}, [onUploadingChange]);
|
|
31
|
+
React.useEffect(() => {
|
|
32
|
+
uploadingCallback.current?.(pending);
|
|
33
|
+
}, [pending]);
|
|
34
|
+
React.useEffect(() => () => uploadingCallback.current?.(false), []);
|
|
35
|
+
async function uploadOne(file) {
|
|
36
|
+
const request = await requestUpload({
|
|
37
|
+
kind: kindFromType(file.type),
|
|
38
|
+
filename: file.name,
|
|
39
|
+
contentType: file.type || 'application/octet-stream',
|
|
40
|
+
sizeBytes: file.size,
|
|
41
|
+
});
|
|
42
|
+
if (!request.ok) {
|
|
43
|
+
setError(request.error);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
let finalizeInput;
|
|
47
|
+
try {
|
|
48
|
+
finalizeInput = await uploadReservedFile(request, file);
|
|
49
|
+
}
|
|
50
|
+
catch (cause) {
|
|
51
|
+
setError(cause instanceof Error ? cause.message : 'Upload failed.');
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const finalized = await finalizeUpload(finalizeInput);
|
|
55
|
+
if (!finalized.ok) {
|
|
56
|
+
setError(finalized.error);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
attachmentId: finalized.attachmentId,
|
|
61
|
+
filename: file.name,
|
|
62
|
+
contentType: file.type || 'application/octet-stream',
|
|
63
|
+
url: finalized.url,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function onFiles(files) {
|
|
67
|
+
if (!files || files.length === 0)
|
|
68
|
+
return;
|
|
69
|
+
const remaining = maxFiles === undefined ? files.length : Math.max(0, maxFiles - value.length);
|
|
70
|
+
if (remaining === 0) {
|
|
71
|
+
setError(`Maximum ${maxFiles} file${maxFiles === 1 ? '' : 's'}.`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
setError(null);
|
|
75
|
+
startTransition(async () => {
|
|
76
|
+
const uploaded = [];
|
|
77
|
+
for (const file of Array.from(files).slice(0, remaining)) {
|
|
78
|
+
const result = await uploadOne(file);
|
|
79
|
+
if (result)
|
|
80
|
+
uploaded.push(result);
|
|
81
|
+
}
|
|
82
|
+
onChange([...value, ...uploaded]);
|
|
83
|
+
if (inputRef.current)
|
|
84
|
+
inputRef.current.value = '';
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const acceptValue = accept ?? (variant === 'photo' ? 'image/*'
|
|
88
|
+
: variant === 'video' ? 'video/*'
|
|
89
|
+
: variant === 'audio' ? 'audio/*'
|
|
90
|
+
: undefined);
|
|
91
|
+
const atLimit = maxFiles !== undefined && value.length >= maxFiles;
|
|
92
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsx("input", { ref: inputRef, type: "file", accept: acceptValue, multiple: multiple, capture: variant === 'photo' ? 'environment' : undefined, className: "hidden", onChange: (event) => onFiles(event.target.files) }), _jsxs("button", { type: "button", onClick: () => inputRef.current?.click(), disabled: pending || atLimit, className: "flex w-full items-center justify-center gap-2 rounded-md border border-dashed border-border-strong bg-bg-subtle px-3 py-6 text-sm text-fg-muted transition-colors hover:border-primary hover:bg-primary-subtle disabled:opacity-50", children: [pending ? _jsx(Loader2, { size: 16, className: "animate-spin" }) : variant === 'photo' ? _jsx(Camera, { size: 16 }) : _jsx(FileUp, { size: 16 }), atLimit ? `Maximum ${maxFiles} files` : pending ? 'Uploading…' : variant === 'photo' ? 'Add photo' : variant === 'video' ? 'Add video' : variant === 'audio' ? 'Add audio' : 'Add file'] }), error ? _jsx("p", { role: "alert", className: "text-xs text-danger", children: error }) : null, value.length > 0 ? (_jsx("ul", { className: "space-y-1.5", children: value.map((file) => (_jsxs("li", { className: "flex items-center justify-between gap-2 rounded-md border border-border bg-surface px-2 py-1.5 text-xs", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [file.contentType.startsWith('image/') ? (
|
|
93
|
+
// The host supplies access-controlled attachment URLs.
|
|
94
|
+
_jsx("img", { src: file.url, alt: "", className: "size-10 shrink-0 rounded object-cover" })) : (_jsx("span", { className: "grid size-10 shrink-0 place-items-center rounded bg-bg-subtle text-fg-subtle", children: _jsx(FileUp, { size: 14 }) })), _jsx("span", { className: "truncate font-medium text-fg", children: file.filename })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onChange(value.filter((candidate) => candidate.attachmentId !== file.attachmentId)), children: _jsx(Trash2, { size: 12, className: "text-danger" }) })] }, file.attachmentId))) })) : null] }));
|
|
95
|
+
}
|
|
96
|
+
export function dataUrlToFile(dataUrl, filename) {
|
|
97
|
+
const [metadata, encoded] = dataUrl.split(',');
|
|
98
|
+
const mime = metadata?.match(/data:([^;]+)/)?.[1] ?? 'application/octet-stream';
|
|
99
|
+
const binary = atob(encoded ?? '');
|
|
100
|
+
const bytes = new Uint8Array(binary.length);
|
|
101
|
+
for (let index = 0; index < binary.length; index += 1)
|
|
102
|
+
bytes[index] = binary.charCodeAt(index);
|
|
103
|
+
return new File([bytes], filename, { type: mime });
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=production-file-upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"production-file-upload.js","sourceRoot":"","sources":["../src/production-file-upload.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Camera, FileUp, Loader2, Trash2 } from 'lucide-react'\nimport {\n Button,\n uploadReservedFile,\n type FinalizeUploadAction,\n type RequestUploadAction,\n} from '@braedonsaunders/appkit-ui'\n\nexport type ProductionAttachedFile = {\n attachmentId: string\n filename: string\n contentType: string\n url: string\n}\n\nfunction kindFromType(mime: string): 'image' | 'document' | 'video' | 'audio' | 'other' {\n if (mime.startsWith('image/')) return 'image'\n if (mime.startsWith('video/')) return 'video'\n if (mime.startsWith('audio/')) return 'audio'\n if (mime === 'application/pdf' || mime.includes('document') || mime.includes('sheet')) {\n return 'document'\n }\n return 'other'\n}\n\nexport type ProductionFileUploadProps = {\n value: ProductionAttachedFile[]\n onChange: (files: ProductionAttachedFile[]) => void\n requestUpload: RequestUploadAction\n finalizeUpload: FinalizeUploadAction\n accept?: string\n multiple?: boolean\n maxFiles?: number\n onUploadingChange?: (uploading: boolean) => void\n variant?: 'photo' | 'file' | 'video' | 'audio'\n}\n\n/**\n * Production form-field uploader: controlled attachments, environment camera\n * capture, bounded counts, reserved upload/finalize protocol, previews, and\n * removal. Storage policy remains an injected application boundary.\n */\nexport function ProductionFileUpload({\n value,\n onChange,\n requestUpload,\n finalizeUpload,\n accept,\n multiple = true,\n maxFiles,\n onUploadingChange,\n variant = 'file',\n}: ProductionFileUploadProps) {\n const inputRef = React.useRef<HTMLInputElement>(null)\n const [pending, startTransition] = React.useTransition()\n const [error, setError] = React.useState<string | null>(null)\n const uploadingCallback = React.useRef(onUploadingChange)\n\n React.useEffect(() => {\n uploadingCallback.current = onUploadingChange\n }, [onUploadingChange])\n\n React.useEffect(() => {\n uploadingCallback.current?.(pending)\n }, [pending])\n\n React.useEffect(() => () => uploadingCallback.current?.(false), [])\n\n async function uploadOne(file: File): Promise<ProductionAttachedFile | null> {\n const request = await requestUpload({\n kind: kindFromType(file.type),\n filename: file.name,\n contentType: file.type || 'application/octet-stream',\n sizeBytes: file.size,\n })\n if (!request.ok) {\n setError(request.error)\n return null\n }\n let finalizeInput\n try {\n finalizeInput = await uploadReservedFile(request, file)\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : 'Upload failed.')\n return null\n }\n const finalized = await finalizeUpload(finalizeInput)\n if (!finalized.ok) {\n setError(finalized.error)\n return null\n }\n return {\n attachmentId: finalized.attachmentId,\n filename: file.name,\n contentType: file.type || 'application/octet-stream',\n url: finalized.url,\n }\n }\n\n function onFiles(files: FileList | null) {\n if (!files || files.length === 0) return\n const remaining = maxFiles === undefined ? files.length : Math.max(0, maxFiles - value.length)\n if (remaining === 0) {\n setError(`Maximum ${maxFiles} file${maxFiles === 1 ? '' : 's'}.`)\n return\n }\n setError(null)\n startTransition(async () => {\n const uploaded: ProductionAttachedFile[] = []\n for (const file of Array.from(files).slice(0, remaining)) {\n const result = await uploadOne(file)\n if (result) uploaded.push(result)\n }\n onChange([...value, ...uploaded])\n if (inputRef.current) inputRef.current.value = ''\n })\n }\n\n const acceptValue = accept ?? (\n variant === 'photo' ? 'image/*'\n : variant === 'video' ? 'video/*'\n : variant === 'audio' ? 'audio/*'\n : undefined\n )\n const atLimit = maxFiles !== undefined && value.length >= maxFiles\n\n return (\n <div className=\"space-y-2\">\n <input\n ref={inputRef}\n type=\"file\"\n accept={acceptValue}\n multiple={multiple}\n capture={variant === 'photo' ? 'environment' : undefined}\n className=\"hidden\"\n onChange={(event) => onFiles(event.target.files)}\n />\n <button\n type=\"button\"\n onClick={() => inputRef.current?.click()}\n disabled={pending || atLimit}\n className=\"flex w-full items-center justify-center gap-2 rounded-md border border-dashed border-border-strong bg-bg-subtle px-3 py-6 text-sm text-fg-muted transition-colors hover:border-primary hover:bg-primary-subtle disabled:opacity-50\"\n >\n {pending ? <Loader2 size={16} className=\"animate-spin\" /> : variant === 'photo' ? <Camera size={16} /> : <FileUp size={16} />}\n {atLimit ? `Maximum ${maxFiles} files` : pending ? 'Uploading…' : variant === 'photo' ? 'Add photo' : variant === 'video' ? 'Add video' : variant === 'audio' ? 'Add audio' : 'Add file'}\n </button>\n {error ? <p role=\"alert\" className=\"text-xs text-danger\">{error}</p> : null}\n {value.length > 0 ? (\n <ul className=\"space-y-1.5\">\n {value.map((file) => (\n <li key={file.attachmentId} className=\"flex items-center justify-between gap-2 rounded-md border border-border bg-surface px-2 py-1.5 text-xs\">\n <div className=\"flex min-w-0 items-center gap-2\">\n {file.contentType.startsWith('image/') ? (\n // The host supplies access-controlled attachment URLs.\n <img src={file.url} alt=\"\" className=\"size-10 shrink-0 rounded object-cover\" />\n ) : (\n <span className=\"grid size-10 shrink-0 place-items-center rounded bg-bg-subtle text-fg-subtle\"><FileUp size={14} /></span>\n )}\n <span className=\"truncate font-medium text-fg\">{file.filename}</span>\n </div>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => onChange(value.filter((candidate) => candidate.attachmentId !== file.attachmentId))}>\n <Trash2 size={12} className=\"text-danger\" />\n </Button>\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n )\n}\n\nexport function dataUrlToFile(dataUrl: string, filename: string): File {\n const [metadata, encoded] = dataUrl.split(',')\n const mime = metadata?.match(/data:([^;]+)/)?.[1] ?? 'application/octet-stream'\n const binary = atob(encoded ?? '')\n const bytes = new Uint8Array(binary.length)\n for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index)\n return new File([bytes], filename, { type: mime })\n}\n"]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ProductionFormDesignerProps } from './production-designer-adapter.js';
|
|
2
|
+
export declare function ProductionFormDesigner({ adapter, templateId, templateName, templateKind, initialSchema, currentVersion, initialSurface, overview, recordConfig, allowedRoles, roles, flows, recordActionAdapter, dataSources, dataSourcesLoading, onRefreshDataSources, renderFlows, renderAssistant, backHref, recordsHref, assignmentCreateHref, assignmentsHref, dataSourcesHref, publishedHref, canPin, pinned, onSchemaChange, locale, defaultLocale, enabledLocales, localeLabels, className, }: ProductionFormDesignerProps): import("react").JSX.Element;
|
|
3
|
+
//# sourceMappingURL=production-form-designer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"production-form-designer.d.ts","sourceRoot":"","sources":["../src/production-form-designer.tsx"],"names":[],"mappings":"AAgIA,OAAO,KAAK,EAEV,2BAA2B,EAC5B,MAAM,+BAA+B,CAAA;AA0KtC,wBAAgB,sBAAsB,CAAC,EACrC,OAAO,EACP,UAAU,EACV,YAAY,EACZ,YAAqB,EACrB,aAAa,EACb,cAAc,EACd,cAAwB,EACxB,QAAQ,EACR,YAAY,EACZ,YAAiB,EACjB,KAAU,EACV,KAAU,EACV,mBAAmB,EACnB,WAAgB,EAChB,kBAA0B,EAC1B,oBAAoB,EACpB,WAAW,EACX,eAAe,EACf,QAAQ,EACR,WAAW,EACX,oBAAoB,EACpB,eAAe,EACf,eAAe,EACf,aAAa,EACb,MAAc,EACd,MAAc,EACd,cAAc,EACd,MAAM,EACN,aAAa,EACb,cAAc,EACd,YAAY,EACZ,SAAS,GACV,EAAE,2BAA2B,+BA+gC7B"}
|