@lattice-php/lattice 0.26.0 → 0.27.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/dist/action/components/action-form.js +2 -2
- package/dist/action/components/action-form.js.map +1 -1
- package/dist/action/hooks/use-action.js +2 -2
- package/dist/chat/components/chat-box.js +1 -1
- package/dist/core/api.d.ts +13 -0
- package/dist/core/api.js +38 -4
- package/dist/core/api.js.map +1 -1
- package/dist/core/component-ref.d.ts +3 -0
- package/dist/core/component-ref.js +20 -2
- package/dist/core/component-ref.js.map +1 -1
- package/dist/core/headers.js +1 -1
- package/dist/core/index.js +4 -4
- package/dist/create-app.js +6 -1
- package/dist/create-app.js.map +1 -1
- package/dist/dnd/index.d.ts +16 -0
- package/dist/dnd/index.js +13 -0
- package/dist/form/components/fields/date-picker-field.js +1 -1
- package/dist/form/components/fields/date-picker-field.js.map +1 -1
- package/dist/form/components/fields/file-upload.js +3 -2
- package/dist/form/components/fields/file-upload.js.map +1 -1
- package/dist/form/components/fields/select.js +4 -3
- package/dist/form/components/fields/select.js.map +1 -1
- package/dist/form/components/form.js +63 -27
- package/dist/form/components/form.js.map +1 -1
- package/dist/form/hooks/use-form-resolver.js +1 -1
- package/dist/form/hooks/use-form-resolver.js.map +1 -1
- package/dist/form/hooks/values.d.ts +8 -0
- package/dist/form/hooks/values.js +18 -1
- package/dist/form/hooks/values.js.map +1 -1
- package/dist/i18n/backend.js +1 -1
- package/dist/i18n/instance.js +1 -1
- package/dist/i18n/timezone.js +1 -1
- package/dist/index.js +3 -3
- package/dist/inertia.d.ts +7 -0
- package/dist/inertia.js +19 -1
- package/dist/inertia.js.map +1 -1
- package/dist/lattice.css +38 -33
- package/dist/remote/components/data-list.js +2 -2
- package/dist/table/components/bulk-bar.js +1 -1
- package/dist/table/hooks/use-table.js +3 -2
- package/dist/table/hooks/use-table.js.map +1 -1
- package/dist/ui/fragment.js +1 -1
- package/dist-standalone/chunks/{chart-view-8xEKTUJV.js → chart-view-CpYsLvzR.js} +4 -4
- package/dist-standalone/chunks/date-picker-field-lX8N40Ii.js +1 -0
- package/dist-standalone/chunks/{page-props-D3FnL614.js → page-props-ClpVjpOc.js} +1 -1
- package/dist-standalone/chunks/{rich-editor-field-47ov3Bny.js → rich-editor-field-C71csVJv.js} +2 -2
- package/dist-standalone/chunks/{subscriptions-C-0lSr7X.js → subscriptions-Bt2s3IF3.js} +1 -1
- package/dist-standalone/chunks/use-effect-dispatcher-DGFXSzA6.js +97 -0
- package/dist-standalone/lattice.css +1 -1
- package/dist-standalone/lattice.js +15 -15
- package/dist-standalone/manifest.json +8 -8
- package/package.json +9 -1
- package/dist-standalone/chunks/date-picker-field-cdhxtVSG.js +0 -1
- package/dist-standalone/chunks/use-effect-dispatcher-EVZxmfoo.js +0 -97
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-upload.js","names":[],"sources":["../../../../resources/js/form/components/fields/file-upload.tsx"],"sourcesContent":["import { apiFetch } from \"@lattice-php/lattice/core/api\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport type { SignedUpload } from \"@lattice-php/lattice/types/generated\";\nimport { IconButton } from \"@lattice-php/lattice/ui/icon-button\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { FormFieldFrame } from \"@lattice-php/lattice/form/components/base/field\";\nimport { useFormContext } from \"@lattice-php/lattice/form/hooks/context\";\nimport { useDependentField } from \"@lattice-php/lattice/form/hooks/use-dependent-field\";\nimport { useFieldScope } from \"@lattice-php/lattice/form/hooks/field-scope\";\nimport { useFormValues, useSetFormValue } from \"@lattice-php/lattice/form/hooks/values\";\n\ntype Item = {\n id: string;\n name: string;\n size: number | null;\n status: \"ready\" | \"uploading\" | \"error\";\n progress: number;\n file?: File;\n key?: string;\n url?: string | null;\n token?: string;\n existing: boolean;\n};\n\nfunction uploadValueEquals(current: unknown, next: string[] | string): boolean {\n if (Array.isArray(next)) {\n return (\n Array.isArray(current) &&\n current.length === next.length &&\n current.every((value, index) => value === next[index])\n );\n }\n\n return current === next;\n}\n\nexport const FileUploadComponent: RendererComponent<\"field.file-upload\"> = ({ node }) => {\n const { t } = useT(\"lattice\");\n const props = node.props;\n const { hidden, required, readOnly, disabled } = useDependentField(node);\n const { action, componentRef, errors } = useFormContext();\n const name = props.name;\n const scope = useFieldScope();\n const domName = scope ? scope.scopedName(name) : name;\n const errorKey = scope ? scope.errorKey(name) : name;\n const uploadKey = errorKey;\n const values = useFormValues();\n const setValue = useSetFormValue();\n const inputId = useId();\n const fileInputRef = useRef<HTMLInputElement>(null);\n const previewUrlsRef = useRef<Set<string>>(new Set());\n const locked = readOnly || disabled;\n const signed = props.signed;\n const multiple = props.multiple;\n const fieldName = multiple ? `${domName}[]` : domName;\n\n const initial = useMemo<Item[]>(\n () =>\n (props.files ?? []).map((file) => ({\n id: crypto.randomUUID(),\n name: file.name,\n size: file.size,\n status: \"ready\" as const,\n progress: 100,\n key: file.key,\n url: file.url,\n token: file.token,\n existing: true,\n })),\n [props.files],\n );\n const [items, setItems] = useState<Item[]>(initial);\n const [removedTokens, setRemovedTokens] = useState<string[]>([]);\n\n useEffect(() => {\n if (!signed) {\n return;\n }\n\n const keys = items\n .filter((item) => !item.existing && item.key && item.status === \"ready\")\n .map((item) => item.key as string);\n const next = multiple ? keys : (keys[0] ?? \"\");\n\n if (scope) {\n if (!uploadValueEquals(scope.getValue(name), next)) {\n scope.setValue(name, next);\n }\n\n return;\n }\n\n setValue(name, next);\n }, [items, multiple, name, scope, setValue, signed]);\n\n useEffect(() => {\n if (!signed || scope) {\n return;\n }\n\n setValue(`${name}__removed`, removedTokens);\n }, [name, removedTokens, scope, setValue, signed]);\n\n useEffect(\n () => () => {\n previewUrlsRef.current.forEach((url) => URL.revokeObjectURL(url));\n previewUrlsRef.current.clear();\n },\n [],\n );\n\n const multipartFiles = useMemo(\n () => items.filter((item) => item.file && !item.existing).map((item) => item.file as File),\n [items],\n );\n useEffect(() => {\n if (signed || !fileInputRef.current) {\n return;\n }\n\n const transfer = new DataTransfer();\n multipartFiles.forEach((file) => transfer.items.add(file));\n fileInputRef.current.files = transfer.files;\n }, [multipartFiles, signed]);\n\n function createPreviewUrl(file: File): string | undefined {\n if (!props.image) {\n return undefined;\n }\n\n const url = URL.createObjectURL(file);\n previewUrlsRef.current.add(url);\n\n return url;\n }\n\n function revokePreviewUrl(item: Item): void {\n if (!item.url || !previewUrlsRef.current.has(item.url)) {\n return;\n }\n\n URL.revokeObjectURL(item.url);\n previewUrlsRef.current.delete(item.url);\n }\n\n async function signAndUpload(item: Item, file: File): Promise<void> {\n const response = await apiFetch(action, {\n method: \"POST\",\n ref: componentRef,\n body: JSON.stringify({\n ...values,\n _upload: uploadKey,\n filename: file.name,\n contentType: file.type,\n }),\n throwOnError: false,\n });\n\n if (!response.ok) {\n setItems((prev) =>\n prev.map((entry) => (entry.id === item.id ? { ...entry, status: \"error\" } : entry)),\n );\n\n return;\n }\n\n const sign = (await response.json()) as SignedUpload;\n\n await new Promise<void>((resolve) => {\n const request = new XMLHttpRequest();\n request.open(sign.method.toUpperCase(), sign.url, true);\n Object.entries(sign.headers).forEach(([key, value]) =>\n request.setRequestHeader(key, String(value)),\n );\n request.upload.onprogress = (event) => {\n if (event.lengthComputable) {\n const progress = Math.round((event.loaded / event.total) * 100);\n setItems((prev) =>\n prev.map((entry) => (entry.id === item.id ? { ...entry, progress } : entry)),\n );\n }\n };\n request.onload = () => {\n setItems((prev) =>\n prev.map((entry) =>\n entry.id === item.id\n ? {\n ...entry,\n status: request.status < 300 ? \"ready\" : \"error\",\n key: sign.key,\n progress: 100,\n }\n : entry,\n ),\n );\n resolve();\n };\n request.onerror = () => {\n setItems((prev) =>\n prev.map((entry) => (entry.id === item.id ? { ...entry, status: \"error\" } : entry)),\n );\n resolve();\n };\n request.send(file);\n });\n }\n\n function addFiles(fileList: FileList | null): void {\n if (!fileList || locked) {\n return;\n }\n\n const incoming = Array.from(fileList);\n const next = incoming.map<Item>((file) => ({\n id: crypto.randomUUID(),\n name: file.name,\n size: file.size,\n status: signed ? \"uploading\" : \"ready\",\n progress: 0,\n file,\n url: createPreviewUrl(file),\n existing: false,\n }));\n\n if (!multiple) {\n items.forEach(revokePreviewUrl);\n\n if (!scope) {\n const replacedTokens = items\n .filter((item) => item.existing && item.token)\n .map((item) => item.token as string);\n\n if (replacedTokens.length > 0) {\n setRemovedTokens((tokens) => [...tokens, ...replacedTokens]);\n }\n }\n }\n\n setItems((prev) => (multiple ? [...prev, ...next] : next));\n\n if (signed) {\n next.forEach((item, index) => void signAndUpload(item, incoming[index]));\n }\n }\n\n function removeItem(id: string): void {\n const target = items.find((item) => item.id === id);\n\n if (target) {\n revokePreviewUrl(target);\n }\n\n if (target?.existing && target.token && !scope) {\n setRemovedTokens((tokens) => [...tokens, target.token as string]);\n }\n\n setItems((prev) => prev.filter((i) => i.id !== id));\n }\n\n if (hidden) {\n return null;\n }\n\n return (\n <FormFieldFrame\n error={errors[errorKey]}\n helperText={props.helperText ?? undefined}\n tooltip={props.tooltip ?? undefined}\n label={props.label ?? \"\"}\n name={name}\n required={required}\n >\n <div\n className=\"flex flex-col gap-3 rounded-lt-sm border border-dashed border-lt-border bg-lt-surface px-4 py-6\"\n data-test={testIdentity(name)}\n onDragOver={(event) => event.preventDefault()}\n onDrop={(event) => {\n event.preventDefault();\n addFiles(event.dataTransfer.files);\n }}\n >\n <button\n className=\"text-sm text-lt-muted-fg\"\n disabled={locked}\n onClick={() => fileInputRef.current?.click()}\n type=\"button\"\n >\n {t(\"form.file-upload.dropzone\", \"Drop files here or click to browse\")}\n </button>\n\n <ul\n className={props.image ? \"grid grid-cols-1 gap-3 sm:grid-cols-2\" : \"flex flex-col gap-2\"}\n >\n {items.map((item) => (\n <li\n className={\n props.image\n ? \"flex min-w-0 items-center gap-3 rounded-lt-sm border border-lt-border bg-lt-bg p-2 text-sm\"\n : \"flex items-center justify-between gap-3 text-sm\"\n }\n key={item.id}\n >\n {props.image && item.url ? (\n <img\n alt={item.name}\n className=\"size-16 shrink-0 rounded-lt-sm border border-lt-border object-cover\"\n data-test={testIdentity(`${name}-preview`)}\n src={item.url}\n />\n ) : null}\n <div className=\"min-w-0 flex-1\">\n <span className=\"block truncate\" data-test={testIdentity(`${name}-item`)}>\n {item.name}\n </span>\n {item.status === \"uploading\" && (\n <span className=\"text-xs text-lt-muted-fg\">{item.progress}%</span>\n )}\n {item.status === \"error\" && (\n <span className=\"text-xs text-lt-danger\">\n {t(\"form.file-upload.failed\", \"Failed\")}\n </span>\n )}\n </div>\n {(!item.existing || !scope) && (\n <IconButton\n size=\"sm\"\n icon=\"x\"\n label={t(\"form.file-upload.remove\", \"Remove {{name}}\", { name: item.name })}\n data-test={testIdentity(\n item.existing ? `${name}-remove-existing` : `${name}-remove`,\n )}\n disabled={locked}\n onClick={() => removeItem(item.id)}\n />\n )}\n {signed && !item.existing && item.key && item.status === \"ready\" && (\n <input\n data-test={testIdentity(`${name}-uploaded`)}\n name={fieldName}\n type=\"hidden\"\n value={item.key}\n />\n )}\n </li>\n ))}\n </ul>\n\n {!scope &&\n removedTokens.map((token) => (\n <input key={token} name={`${name}__removed[]`} type=\"hidden\" value={token} />\n ))}\n\n <input\n accept={props.accept ?? undefined}\n aria-label={props.label ?? name}\n className=\"sr-only\"\n data-test={testIdentity(`${name}-input`)}\n id={inputId}\n multiple={multiple}\n name={signed ? undefined : fieldName}\n onChange={(event) => {\n addFiles(event.target.files);\n if (signed) {\n event.target.value = \"\";\n }\n }}\n ref={fileInputRef}\n type=\"file\"\n />\n </div>\n </FormFieldFrame>\n );\n};\n"],"mappings":";;;;;;;;;;;;AA0BA,SAAS,kBAAkB,SAAkB,MAAkC;CAC7E,IAAI,MAAM,QAAQ,IAAI,GACpB,OACE,MAAM,QAAQ,OAAO,KACrB,QAAQ,WAAW,KAAK,UACxB,QAAQ,OAAO,OAAO,UAAU,UAAU,KAAK,MAAM;CAIzD,OAAO,YAAY;AACrB;AAEA,IAAa,uBAA+D,EAAE,WAAW;CACvF,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,QAAQ,KAAK;CACnB,MAAM,EAAE,QAAQ,UAAU,UAAU,aAAa,kBAAkB,IAAI;CACvE,MAAM,EAAE,QAAQ,cAAc,WAAW,eAAe;CACxD,MAAM,OAAO,MAAM;CACnB,MAAM,QAAQ,cAAc;CAC5B,MAAM,UAAU,QAAQ,MAAM,WAAW,IAAI,IAAI;CACjD,MAAM,WAAW,QAAQ,MAAM,SAAS,IAAI,IAAI;CAChD,MAAM,YAAY;CAClB,MAAM,SAAS,cAAc;CAC7B,MAAM,WAAW,gBAAgB;CACjC,MAAM,UAAU,MAAM;CACtB,MAAM,eAAe,OAAyB,IAAI;CAClD,MAAM,iBAAiB,uBAAoB,IAAI,IAAI,CAAC;CACpD,MAAM,SAAS,YAAY;CAC3B,MAAM,SAAS,MAAM;CACrB,MAAM,WAAW,MAAM;CACvB,MAAM,YAAY,WAAW,GAAG,QAAQ,MAAM;CAiB9C,MAAM,CAAC,OAAO,YAAY,SAfV,eAEX,MAAM,SAAS,CAAC,GAAG,KAAK,UAAU;EACjC,IAAI,OAAO,WAAW;EACtB,MAAM,KAAK;EACX,MAAM,KAAK;EACX,QAAQ;EACR,UAAU;EACV,KAAK,KAAK;EACV,KAAK,KAAK;EACV,OAAO,KAAK;EACZ,UAAU;CACZ,EAAE,GACJ,CAAC,MAAM,KAAK,CAE6B,CAAO;CAClD,MAAM,CAAC,eAAe,oBAAoB,SAAmB,CAAC,CAAC;CAE/D,gBAAgB;EACd,IAAI,CAAC,QACH;EAGF,MAAM,OAAO,MACV,QAAQ,SAAS,CAAC,KAAK,YAAY,KAAK,OAAO,KAAK,WAAW,OAAO,EACtE,KAAK,SAAS,KAAK,GAAa;EACnC,MAAM,OAAO,WAAW,OAAQ,KAAK,MAAM;EAE3C,IAAI,OAAO;GACT,IAAI,CAAC,kBAAkB,MAAM,SAAS,IAAI,GAAG,IAAI,GAC/C,MAAM,SAAS,MAAM,IAAI;GAG3B;EACF;EAEA,SAAS,MAAM,IAAI;CACrB,GAAG;EAAC;EAAO;EAAU;EAAM;EAAO;EAAU;CAAM,CAAC;CAEnD,gBAAgB;EACd,IAAI,CAAC,UAAU,OACb;EAGF,SAAS,GAAG,KAAK,YAAY,aAAa;CAC5C,GAAG;EAAC;EAAM;EAAe;EAAO;EAAU;CAAM,CAAC;CAEjD,sBACc;EACV,eAAe,QAAQ,SAAS,QAAQ,IAAI,gBAAgB,GAAG,CAAC;EAChE,eAAe,QAAQ,MAAM;CAC/B,GACA,CAAC,CACH;CAEA,MAAM,iBAAiB,cACf,MAAM,QAAQ,SAAS,KAAK,QAAQ,CAAC,KAAK,QAAQ,EAAE,KAAK,SAAS,KAAK,IAAY,GACzF,CAAC,KAAK,CACR;CACA,gBAAgB;EACd,IAAI,UAAU,CAAC,aAAa,SAC1B;EAGF,MAAM,WAAW,IAAI,aAAa;EAClC,eAAe,SAAS,SAAS,SAAS,MAAM,IAAI,IAAI,CAAC;EACzD,aAAa,QAAQ,QAAQ,SAAS;CACxC,GAAG,CAAC,gBAAgB,MAAM,CAAC;CAE3B,SAAS,iBAAiB,MAAgC;EACxD,IAAI,CAAC,MAAM,OACT;EAGF,MAAM,MAAM,IAAI,gBAAgB,IAAI;EACpC,eAAe,QAAQ,IAAI,GAAG;EAE9B,OAAO;CACT;CAEA,SAAS,iBAAiB,MAAkB;EAC1C,IAAI,CAAC,KAAK,OAAO,CAAC,eAAe,QAAQ,IAAI,KAAK,GAAG,GACnD;EAGF,IAAI,gBAAgB,KAAK,GAAG;EAC5B,eAAe,QAAQ,OAAO,KAAK,GAAG;CACxC;CAEA,eAAe,cAAc,MAAY,MAA2B;EAClE,MAAM,WAAW,MAAM,SAAS,QAAQ;GACtC,QAAQ;GACR,KAAK;GACL,MAAM,KAAK,UAAU;IACnB,GAAG;IACH,SAAS;IACT,UAAU,KAAK;IACf,aAAa,KAAK;GACpB,CAAC;GACD,cAAc;EAChB,CAAC;EAED,IAAI,CAAC,SAAS,IAAI;GAChB,UAAU,SACR,KAAK,KAAK,UAAW,MAAM,OAAO,KAAK,KAAK;IAAE,GAAG;IAAO,QAAQ;GAAQ,IAAI,KAAM,CACpF;GAEA;EACF;EAEA,MAAM,OAAQ,MAAM,SAAS,KAAK;EAElC,MAAM,IAAI,SAAe,YAAY;GACnC,MAAM,UAAU,IAAI,eAAe;GACnC,QAAQ,KAAK,KAAK,OAAO,YAAY,GAAG,KAAK,KAAK,IAAI;GACtD,OAAO,QAAQ,KAAK,OAAO,EAAE,SAAS,CAAC,KAAK,WAC1C,QAAQ,iBAAiB,KAAK,OAAO,KAAK,CAAC,CAC7C;GACA,QAAQ,OAAO,cAAc,UAAU;IACrC,IAAI,MAAM,kBAAkB;KAC1B,MAAM,WAAW,KAAK,MAAO,MAAM,SAAS,MAAM,QAAS,GAAG;KAC9D,UAAU,SACR,KAAK,KAAK,UAAW,MAAM,OAAO,KAAK,KAAK;MAAE,GAAG;MAAO;KAAS,IAAI,KAAM,CAC7E;IACF;GACF;GACA,QAAQ,eAAe;IACrB,UAAU,SACR,KAAK,KAAK,UACR,MAAM,OAAO,KAAK,KACd;KACE,GAAG;KACH,QAAQ,QAAQ,SAAS,MAAM,UAAU;KACzC,KAAK,KAAK;KACV,UAAU;IACZ,IACA,KACN,CACF;IACA,QAAQ;GACV;GACA,QAAQ,gBAAgB;IACtB,UAAU,SACR,KAAK,KAAK,UAAW,MAAM,OAAO,KAAK,KAAK;KAAE,GAAG;KAAO,QAAQ;IAAQ,IAAI,KAAM,CACpF;IACA,QAAQ;GACV;GACA,QAAQ,KAAK,IAAI;EACnB,CAAC;CACH;CAEA,SAAS,SAAS,UAAiC;EACjD,IAAI,CAAC,YAAY,QACf;EAGF,MAAM,WAAW,MAAM,KAAK,QAAQ;EACpC,MAAM,OAAO,SAAS,KAAW,UAAU;GACzC,IAAI,OAAO,WAAW;GACtB,MAAM,KAAK;GACX,MAAM,KAAK;GACX,QAAQ,SAAS,cAAc;GAC/B,UAAU;GACV;GACA,KAAK,iBAAiB,IAAI;GAC1B,UAAU;EACZ,EAAE;EAEF,IAAI,CAAC,UAAU;GACb,MAAM,QAAQ,gBAAgB;GAE9B,IAAI,CAAC,OAAO;IACV,MAAM,iBAAiB,MACpB,QAAQ,SAAS,KAAK,YAAY,KAAK,KAAK,EAC5C,KAAK,SAAS,KAAK,KAAe;IAErC,IAAI,eAAe,SAAS,GAC1B,kBAAkB,WAAW,CAAC,GAAG,QAAQ,GAAG,cAAc,CAAC;GAE/D;EACF;EAEA,UAAU,SAAU,WAAW,CAAC,GAAG,MAAM,GAAG,IAAI,IAAI,IAAK;EAEzD,IAAI,QACF,KAAK,SAAS,MAAM,UAAU,KAAK,cAAc,MAAM,SAAS,MAAM,CAAC;CAE3E;CAEA,SAAS,WAAW,IAAkB;EACpC,MAAM,SAAS,MAAM,MAAM,SAAS,KAAK,OAAO,EAAE;EAElD,IAAI,QACF,iBAAiB,MAAM;EAGzB,IAAI,QAAQ,YAAY,OAAO,SAAS,CAAC,OACvC,kBAAkB,WAAW,CAAC,GAAG,QAAQ,OAAO,KAAe,CAAC;EAGlE,UAAU,SAAS,KAAK,QAAQ,MAAM,EAAE,OAAO,EAAE,CAAC;CACpD;CAEA,IAAI,QACF,OAAO;CAGT,OACE,oBAAC,gBAAD;EACE,OAAO,OAAO;EACd,YAAY,MAAM,cAAc,KAAA;EAChC,SAAS,MAAM,WAAW,KAAA;EAC1B,OAAO,MAAM,SAAS;EAChB;EACI;YAEV,qBAAC,OAAD;GACE,WAAU;GACV,aAAW,aAAa,IAAI;GAC5B,aAAa,UAAU,MAAM,eAAe;GAC5C,SAAS,UAAU;IACjB,MAAM,eAAe;IACrB,SAAS,MAAM,aAAa,KAAK;GACnC;aAPF;IASE,oBAAC,UAAD;KACE,WAAU;KACV,UAAU;KACV,eAAe,aAAa,SAAS,MAAM;KAC3C,MAAK;eAEJ,EAAE,6BAA6B,oCAAoC;IAC9D,CAAA;IAER,oBAAC,MAAD;KACE,WAAW,MAAM,QAAQ,0CAA0C;eAElE,MAAM,KAAK,SACV,qBAAC,MAAD;MACE,WACE,MAAM,QACF,+FACA;gBAJR;OAQG,MAAM,SAAS,KAAK,MACnB,oBAAC,OAAD;QACE,KAAK,KAAK;QACV,WAAU;QACV,aAAW,aAAa,GAAG,KAAK,SAAS;QACzC,KAAK,KAAK;OACX,CAAA,IACC;OACJ,qBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,oBAAC,QAAD;UAAM,WAAU;UAAiB,aAAW,aAAa,GAAG,KAAK,MAAM;oBACpE,KAAK;SACF,CAAA;SACL,KAAK,WAAW,eACf,qBAAC,QAAD;UAAM,WAAU;oBAAhB,CAA4C,KAAK,UAAS,GAAO;;SAElE,KAAK,WAAW,WACf,oBAAC,QAAD;UAAM,WAAU;oBACb,EAAE,2BAA2B,QAAQ;SAClC,CAAA;QAEL;;QACH,CAAC,KAAK,YAAY,CAAC,UACnB,oBAAC,YAAD;QACE,MAAK;QACL,MAAK;QACL,OAAO,EAAE,2BAA2B,mBAAmB,EAAE,MAAM,KAAK,KAAK,CAAC;QAC1E,aAAW,aACT,KAAK,WAAW,GAAG,KAAK,oBAAoB,GAAG,KAAK,QACtD;QACA,UAAU;QACV,eAAe,WAAW,KAAK,EAAE;OAClC,CAAA;OAEF,UAAU,CAAC,KAAK,YAAY,KAAK,OAAO,KAAK,WAAW,WACvD,oBAAC,SAAD;QACE,aAAW,aAAa,GAAG,KAAK,UAAU;QAC1C,MAAM;QACN,MAAK;QACL,OAAO,KAAK;OACb,CAAA;MAED;QA3CG,KAAK,EA2CR,CACL;IACC,CAAA;IAEH,CAAC,SACA,cAAc,KAAK,UACjB,oBAAC,SAAD;KAAmB,MAAM,GAAG,KAAK;KAAc,MAAK;KAAS,OAAO;IAAQ,GAAhE,KAAgE,CAC7E;IAEH,oBAAC,SAAD;KACE,QAAQ,MAAM,UAAU,KAAA;KACxB,cAAY,MAAM,SAAS;KAC3B,WAAU;KACV,aAAW,aAAa,GAAG,KAAK,OAAO;KACvC,IAAI;KACM;KACV,MAAM,SAAS,KAAA,IAAY;KAC3B,WAAW,UAAU;MACnB,SAAS,MAAM,OAAO,KAAK;MAC3B,IAAI,QACF,MAAM,OAAO,QAAQ;KAEzB;KACA,KAAK;KACL,MAAK;IACN,CAAA;GACE;;CACS,CAAA;AAEpB"}
|
|
1
|
+
{"version":3,"file":"file-upload.js","names":[],"sources":["../../../../resources/js/form/components/fields/file-upload.tsx"],"sourcesContent":["import { apiFetch } from \"@lattice-php/lattice/core/api\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport type { SignedUpload } from \"@lattice-php/lattice/types/generated\";\nimport { IconButton } from \"@lattice-php/lattice/ui/icon-button\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { FormFieldFrame } from \"@lattice-php/lattice/form/components/base/field\";\nimport { useFormContext } from \"@lattice-php/lattice/form/hooks/context\";\nimport { useDependentField } from \"@lattice-php/lattice/form/hooks/use-dependent-field\";\nimport { useFieldScope } from \"@lattice-php/lattice/form/hooks/field-scope\";\nimport { useFormValues, useSetFormValue } from \"@lattice-php/lattice/form/hooks/values\";\n\ntype Item = {\n id: string;\n name: string;\n size: number | null;\n status: \"ready\" | \"uploading\" | \"error\";\n progress: number;\n file?: File;\n key?: string;\n url?: string | null;\n token?: string;\n existing: boolean;\n};\n\nfunction uploadValueEquals(current: unknown, next: string[] | string): boolean {\n if (Array.isArray(next)) {\n return (\n Array.isArray(current) &&\n current.length === next.length &&\n current.every((value, index) => value === next[index])\n );\n }\n\n return current === next;\n}\n\nexport const FileUploadComponent: RendererComponent<\"field.file-upload\"> = ({ node }) => {\n const { t } = useT(\"lattice\");\n const props = node.props;\n const { hidden, required, readOnly, disabled } = useDependentField(node);\n const { action, componentRef, errors } = useFormContext();\n const name = props.name;\n const scope = useFieldScope();\n const domName = scope ? scope.scopedName(name) : name;\n const errorKey = scope ? scope.errorKey(name) : name;\n const uploadKey = errorKey;\n const values = useFormValues();\n const setValue = useSetFormValue();\n const inputId = useId();\n const fileInputRef = useRef<HTMLInputElement>(null);\n const previewUrlsRef = useRef<Set<string>>(new Set());\n const locked = readOnly || disabled;\n const signed = props.signed;\n const multiple = props.multiple;\n const fieldName = multiple ? `${domName}[]` : domName;\n\n const initial = useMemo<Item[]>(\n () =>\n (props.files ?? []).map((file) => ({\n id: crypto.randomUUID(),\n name: file.name,\n size: file.size,\n status: \"ready\" as const,\n progress: 100,\n key: file.key,\n url: file.url,\n token: file.token,\n existing: true,\n })),\n [props.files],\n );\n const [items, setItems] = useState<Item[]>(initial);\n const [removedTokens, setRemovedTokens] = useState<string[]>([]);\n\n useEffect(() => {\n if (!signed) {\n return;\n }\n\n const keys = items\n .filter((item) => !item.existing && item.key && item.status === \"ready\")\n .map((item) => item.key as string);\n const next = multiple ? keys : (keys[0] ?? \"\");\n\n if (scope) {\n if (!uploadValueEquals(scope.getValue(name), next)) {\n scope.setValue(name, next);\n }\n\n return;\n }\n\n setValue(name, next);\n }, [items, multiple, name, scope, setValue, signed]);\n\n useEffect(() => {\n if (!signed || scope) {\n return;\n }\n\n setValue(`${name}__removed`, removedTokens);\n }, [name, removedTokens, scope, setValue, signed]);\n\n useEffect(\n () => () => {\n previewUrlsRef.current.forEach((url) => URL.revokeObjectURL(url));\n previewUrlsRef.current.clear();\n },\n [],\n );\n\n const multipartFiles = useMemo(\n () => items.filter((item) => item.file && !item.existing).map((item) => item.file as File),\n [items],\n );\n useEffect(() => {\n if (signed || !fileInputRef.current) {\n return;\n }\n\n const transfer = new DataTransfer();\n multipartFiles.forEach((file) => transfer.items.add(file));\n fileInputRef.current.files = transfer.files;\n }, [multipartFiles, signed]);\n\n function createPreviewUrl(file: File): string | undefined {\n if (!props.image) {\n return undefined;\n }\n\n const url = URL.createObjectURL(file);\n previewUrlsRef.current.add(url);\n\n return url;\n }\n\n function revokePreviewUrl(item: Item): void {\n if (!item.url || !previewUrlsRef.current.has(item.url)) {\n return;\n }\n\n URL.revokeObjectURL(item.url);\n previewUrlsRef.current.delete(item.url);\n }\n\n async function signAndUpload(item: Item, file: File): Promise<void> {\n const response = await apiFetch(action, {\n method: \"POST\",\n ref: componentRef,\n body: JSON.stringify({\n ...values,\n _sub: \"upload\",\n _target: uploadKey,\n filename: file.name,\n contentType: file.type,\n }),\n throwOnError: false,\n });\n\n if (!response.ok) {\n setItems((prev) =>\n prev.map((entry) => (entry.id === item.id ? { ...entry, status: \"error\" } : entry)),\n );\n\n return;\n }\n\n const sign = (await response.json()) as SignedUpload;\n\n await new Promise<void>((resolve) => {\n const request = new XMLHttpRequest();\n request.open(sign.method.toUpperCase(), sign.url, true);\n Object.entries(sign.headers).forEach(([key, value]) =>\n request.setRequestHeader(key, String(value)),\n );\n request.upload.onprogress = (event) => {\n if (event.lengthComputable) {\n const progress = Math.round((event.loaded / event.total) * 100);\n setItems((prev) =>\n prev.map((entry) => (entry.id === item.id ? { ...entry, progress } : entry)),\n );\n }\n };\n request.onload = () => {\n setItems((prev) =>\n prev.map((entry) =>\n entry.id === item.id\n ? {\n ...entry,\n status: request.status < 300 ? \"ready\" : \"error\",\n key: sign.key,\n progress: 100,\n }\n : entry,\n ),\n );\n resolve();\n };\n request.onerror = () => {\n setItems((prev) =>\n prev.map((entry) => (entry.id === item.id ? { ...entry, status: \"error\" } : entry)),\n );\n resolve();\n };\n request.send(file);\n });\n }\n\n function addFiles(fileList: FileList | null): void {\n if (!fileList || locked) {\n return;\n }\n\n const incoming = Array.from(fileList);\n const next = incoming.map<Item>((file) => ({\n id: crypto.randomUUID(),\n name: file.name,\n size: file.size,\n status: signed ? \"uploading\" : \"ready\",\n progress: 0,\n file,\n url: createPreviewUrl(file),\n existing: false,\n }));\n\n if (!multiple) {\n items.forEach(revokePreviewUrl);\n\n if (!scope) {\n const replacedTokens = items\n .filter((item) => item.existing && item.token)\n .map((item) => item.token as string);\n\n if (replacedTokens.length > 0) {\n setRemovedTokens((tokens) => [...tokens, ...replacedTokens]);\n }\n }\n }\n\n setItems((prev) => (multiple ? [...prev, ...next] : next));\n\n if (signed) {\n next.forEach((item, index) => void signAndUpload(item, incoming[index]));\n }\n }\n\n function removeItem(id: string): void {\n const target = items.find((item) => item.id === id);\n\n if (target) {\n revokePreviewUrl(target);\n }\n\n if (target?.existing && target.token && !scope) {\n setRemovedTokens((tokens) => [...tokens, target.token as string]);\n }\n\n setItems((prev) => prev.filter((i) => i.id !== id));\n }\n\n if (hidden) {\n return null;\n }\n\n return (\n <FormFieldFrame\n error={errors[errorKey]}\n helperText={props.helperText ?? undefined}\n tooltip={props.tooltip ?? undefined}\n label={props.label ?? \"\"}\n name={name}\n required={required}\n >\n <div\n className=\"flex flex-col gap-3 rounded-lt-sm border border-dashed border-lt-border bg-lt-surface px-4 py-6\"\n data-test={testIdentity(name)}\n onDragOver={(event) => event.preventDefault()}\n onDrop={(event) => {\n event.preventDefault();\n addFiles(event.dataTransfer.files);\n }}\n >\n <button\n className=\"text-sm text-lt-muted-fg\"\n disabled={locked}\n onClick={() => fileInputRef.current?.click()}\n type=\"button\"\n >\n {t(\"form.file-upload.dropzone\", \"Drop files here or click to browse\")}\n </button>\n\n <ul\n className={props.image ? \"grid grid-cols-1 gap-3 sm:grid-cols-2\" : \"flex flex-col gap-2\"}\n >\n {items.map((item) => (\n <li\n className={\n props.image\n ? \"flex min-w-0 items-center gap-3 rounded-lt-sm border border-lt-border bg-lt-bg p-2 text-sm\"\n : \"flex items-center justify-between gap-3 text-sm\"\n }\n key={item.id}\n >\n {props.image && item.url ? (\n <img\n alt={item.name}\n className=\"size-16 shrink-0 rounded-lt-sm border border-lt-border object-cover\"\n data-test={testIdentity(`${name}-preview`)}\n src={item.url}\n />\n ) : null}\n <div className=\"min-w-0 flex-1\">\n <span className=\"block truncate\" data-test={testIdentity(`${name}-item`)}>\n {item.name}\n </span>\n {item.status === \"uploading\" && (\n <span className=\"text-xs text-lt-muted-fg\">{item.progress}%</span>\n )}\n {item.status === \"error\" && (\n <span className=\"text-xs text-lt-danger\">\n {t(\"form.file-upload.failed\", \"Failed\")}\n </span>\n )}\n </div>\n {(!item.existing || !scope) && (\n <IconButton\n size=\"sm\"\n icon=\"x\"\n label={t(\"form.file-upload.remove\", \"Remove {{name}}\", { name: item.name })}\n data-test={testIdentity(\n item.existing ? `${name}-remove-existing` : `${name}-remove`,\n )}\n disabled={locked}\n onClick={() => removeItem(item.id)}\n />\n )}\n {signed && !item.existing && item.key && item.status === \"ready\" && (\n <input\n data-test={testIdentity(`${name}-uploaded`)}\n name={fieldName}\n type=\"hidden\"\n value={item.key}\n />\n )}\n </li>\n ))}\n </ul>\n\n {!scope &&\n removedTokens.map((token) => (\n <input key={token} name={`${name}__removed[]`} type=\"hidden\" value={token} />\n ))}\n\n <input\n accept={props.accept ?? undefined}\n aria-label={props.label ?? name}\n className=\"sr-only\"\n data-test={testIdentity(`${name}-input`)}\n id={inputId}\n multiple={multiple}\n name={signed ? undefined : fieldName}\n onChange={(event) => {\n addFiles(event.target.files);\n if (signed) {\n event.target.value = \"\";\n }\n }}\n ref={fileInputRef}\n type=\"file\"\n />\n </div>\n </FormFieldFrame>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useT } from "../../../i18n/instance.js";
|
|
2
1
|
import { materializeSchema } from "../../../core/materialize.js";
|
|
2
|
+
import { useT } from "../../../i18n/instance.js";
|
|
3
3
|
import { cn } from "../../../lib/utils.js";
|
|
4
4
|
import { Icon } from "../../../icons/sprite.js";
|
|
5
5
|
import { Renderer } from "../../../core/renderer.js";
|
|
@@ -81,8 +81,9 @@ var SelectComponent = ({ node }) => {
|
|
|
81
81
|
}
|
|
82
82
|
postFormAction(action, componentRef, {
|
|
83
83
|
...valuesRef.current,
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
_sub: "search",
|
|
85
|
+
_target: searchKey,
|
|
86
|
+
_q: query
|
|
86
87
|
}, controller.signal).then((response) => {
|
|
87
88
|
setResults(response?.options ?? []);
|
|
88
89
|
setLoading(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.js","names":[],"sources":["../../../../resources/js/form/components/fields/select.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport { useCallback, useMemo, useRef, useState } from \"react\";\nimport { Combobox } from \"@lattice-php/lattice/ui/combobox\";\nimport { controlSurface } from \"@lattice-php/lattice/ui/control\";\nimport { coerceColor, colorValue } from \"@lattice-php/lattice/lib/color\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { Renderer } from \"@lattice-php/lattice/core/renderer\";\nimport { materializeSchema } from \"@lattice-php/lattice/core/materialize\";\nimport type { Node, Option, RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { FormFieldFrame } from \"@lattice-php/lattice/form/components/base/field\";\nimport { useFormContext } from \"@lattice-php/lattice/form/hooks/context\";\nimport { fieldDomName } from \"@lattice-php/lattice/form/lib/field-dom-name\";\nimport { postFormAction } from \"@lattice-php/lattice/form/lib/form-transport\";\nimport { useResolvedNode } from \"@lattice-php/lattice/form/hooks/resolved-nodes\";\nimport { useDependentField } from \"@lattice-php/lattice/form/hooks/use-dependent-field\";\nimport { useFieldCommit } from \"@lattice-php/lattice/form/hooks/use-field-commit\";\nimport { useFieldScope } from \"@lattice-php/lattice/form/hooks/field-scope\";\nimport { useFormValue, useFormValues } from \"@lattice-php/lattice/form/hooks/values\";\n\nfunction toValues(stored: unknown, fallback: unknown): string[] {\n const source = stored ?? fallback;\n\n if (Array.isArray(source)) {\n return source.map(String);\n }\n\n if (source === undefined || source === null || source === \"\") {\n return [];\n }\n\n return [String(source)];\n}\n\nexport const SelectComponent: RendererComponent<\"field.select\"> = ({ node }) => {\n const { t } = useT(\"lattice\");\n const props = node.props;\n const { action, componentRef, errors, fieldIdPrefix, searchOptions } = useFormContext();\n const { hidden, required, readOnly, disabled } = useDependentField(node);\n const { change, blur } = useFieldCommit();\n const resolvedNode = useResolvedNode(node);\n const name = props.name;\n const scope = useFieldScope();\n const domName = fieldDomName(scope ? scope.scopedName(name) : name, fieldIdPrefix);\n const errorKey = scope ? scope.errorKey(name) : name;\n const searchKey = errorKey;\n const placeholder = props.placeholder || \"Select…\";\n const multiple = props.multiple;\n const searchable = props.searchable;\n const creatable = props.creatable;\n const staticOptions = useMemo(\n () => (resolvedNode.props as { options?: Option[] }).options ?? [],\n [resolvedNode.props],\n );\n const optionSchema = (resolvedNode.props as { optionSchema?: Node[] }).optionSchema;\n\n const globalValue = useFormValue(name);\n const values = useFormValues();\n const valuesRef = useRef(values);\n valuesRef.current = values;\n const storedValue = scope ? scope.getValue(name) : globalValue;\n const selected = useMemo(() => toValues(storedValue, props.value), [storedValue, props.value]);\n const selectedRef = useRef(selected);\n selectedRef.current = selected;\n\n const [open, setOpen] = useState(false);\n const [results, setResults] = useState<Option[] | null>(null);\n const [loading, setLoading] = useState(false);\n const searchAbort = useRef<AbortController | null>(null);\n\n const optionsByValue = useMemo(() => {\n const map = new Map<string, Option>();\n\n for (const option of [...staticOptions, ...(results ?? [])]) {\n map.set(option.value, option);\n }\n\n return map;\n }, [staticOptions, results]);\n const labelFor = (value: string) => optionsByValue.get(value)?.label ?? value;\n const colorFor = (value: string) =>\n coerceColor((optionsByValue.get(value)?.data as { color?: unknown } | undefined)?.color);\n\n const locked = readOnly || disabled;\n\n const search = useCallback(\n (query: string) => {\n searchAbort.current?.abort();\n\n if (query.trim() === \"\") {\n setResults(null);\n setLoading(false);\n\n return;\n }\n\n const controller = new AbortController();\n searchAbort.current = controller;\n setLoading(true);\n\n if (searchOptions) {\n void searchOptions(searchKey, query, valuesRef.current, controller.signal)\n .then((options) => {\n setResults(options);\n setLoading(false);\n })\n .catch(() => {});\n\n return;\n }\n\n void postFormAction<{ options?: Option[] }>(\n action,\n componentRef,\n { ...valuesRef.current, _search: searchKey, q: query },\n controller.signal,\n )\n .then((response) => {\n setResults(response?.options ?? []);\n setLoading(false);\n })\n .catch(() => {});\n },\n [action, componentRef, searchKey, searchOptions],\n );\n\n function commit(next: string[]): void {\n change(name, multiple ? next : (next[0] ?? \"\"));\n }\n\n function select(value: string): void {\n if (multiple) {\n const next = selectedRef.current.includes(value)\n ? selectedRef.current.filter((item) => item !== value)\n : [...selectedRef.current, value];\n selectedRef.current = next;\n commit(next);\n\n return;\n }\n\n selectedRef.current = [value];\n commit([value]);\n }\n\n function remove(value: string): void {\n commit(selected.filter((item) => item !== value));\n }\n\n function applyCreated(value: string): void {\n if (!multiple) {\n selectedRef.current = [value];\n commit([value]);\n\n return;\n }\n\n if (!selectedRef.current.includes(value)) {\n const next = [...selectedRef.current, value];\n selectedRef.current = next;\n commit(next);\n }\n }\n\n if (hidden) {\n return null;\n }\n\n const options = searchable ? (results ?? staticOptions) : staticOptions;\n\n const renderOption = optionSchema?.length\n ? (option: Option) => (\n <Renderer\n nodes={materializeSchema(optionSchema, {\n ...option.data,\n label: option.label,\n value: option.value,\n })}\n />\n )\n : undefined;\n\n return (\n <FormFieldFrame\n error={errors[errorKey]}\n helperText={props.helperText ?? undefined}\n tooltip={props.tooltip ?? undefined}\n label={props.label ?? \"\"}\n name={domName}\n required={required}\n >\n {multiple ? (\n selected.map((value) => (\n <input key={value} name={`${domName}[]`} type=\"hidden\" value={value} />\n ))\n ) : (\n <input name={domName} type=\"hidden\" value={selected[0] ?? \"\"} />\n )}\n\n <div>\n {multiple && selected.length > 0 && (\n <div className=\"mb-1.5 flex flex-wrap gap-1\">\n {selected.map((value) => {\n const color = colorFor(value);\n\n return (\n <span\n className=\"inline-flex items-center gap-1 rounded-lt-sm bg-lt-muted px-2 py-0.5 text-xs\"\n key={value}\n >\n {color && (\n <span\n aria-hidden=\"true\"\n className=\"size-2 shrink-0 rounded-full\"\n style={{ background: colorValue(color) }}\n />\n )}\n {labelFor(value)}\n {!locked && (\n <button\n aria-label={t(\"form.remove-option\", \"Remove {{label}}\", {\n label: labelFor(value),\n })}\n data-test={`select-${name}-remove-${value}`}\n className=\"text-lt-muted-fg hover:text-lt-fg [&_svg]:size-lt-icon-xs\"\n onClick={() => remove(value)}\n type=\"button\"\n >\n <Icon name=\"x\" />\n </button>\n )}\n </span>\n );\n })}\n </div>\n )}\n\n <Combobox\n creatable={creatable}\n emptyLabel={props.emptyLabel ?? undefined}\n loading={loading}\n multiple={multiple}\n onCommit={applyCreated}\n onCreate={applyCreated}\n onSearch={searchable ? search : undefined}\n onSelect={select}\n open={open && !locked}\n onOpenChange={(next) => {\n setOpen(next);\n\n if (!next) {\n blur(name);\n }\n }}\n options={options}\n renderOption={renderOption}\n searchPlaceholder={props.searchPlaceholder ?? undefined}\n showSearch={Boolean(searchable || creatable)}\n selected={selected}\n testId={`select-${name}`}\n trigger={\n <>\n {!multiple && selected.length > 0 ? (\n <span>{labelFor(selected[0])}</span>\n ) : (\n <span className=\"text-lt-muted-fg\">{placeholder}</span>\n )}\n <Icon name=\"chevrons-up-down\" className=\"size-lt-icon-md shrink-0 text-lt-muted-fg\" />\n </>\n }\n triggerClassName={cn(\n controlSurface(),\n \"flex items-center justify-between gap-2 text-left\",\n locked && \"cursor-not-allowed opacity-60\",\n )}\n triggerProps={{\n \"aria-haspopup\": \"listbox\",\n autoFocus: props.autoFocus ?? undefined,\n \"data-test\": `select-${name}`,\n disabled: locked,\n id: domName,\n tabIndex: props.tabIndex ?? undefined,\n }}\n />\n </div>\n </FormFieldFrame>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"select.js","names":[],"sources":["../../../../resources/js/form/components/fields/select.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport { useCallback, useMemo, useRef, useState } from \"react\";\nimport { Combobox } from \"@lattice-php/lattice/ui/combobox\";\nimport { controlSurface } from \"@lattice-php/lattice/ui/control\";\nimport { coerceColor, colorValue } from \"@lattice-php/lattice/lib/color\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { Renderer } from \"@lattice-php/lattice/core/renderer\";\nimport { materializeSchema } from \"@lattice-php/lattice/core/materialize\";\nimport type { Node, Option, RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { FormFieldFrame } from \"@lattice-php/lattice/form/components/base/field\";\nimport { useFormContext } from \"@lattice-php/lattice/form/hooks/context\";\nimport { fieldDomName } from \"@lattice-php/lattice/form/lib/field-dom-name\";\nimport { postFormAction } from \"@lattice-php/lattice/form/lib/form-transport\";\nimport { useResolvedNode } from \"@lattice-php/lattice/form/hooks/resolved-nodes\";\nimport { useDependentField } from \"@lattice-php/lattice/form/hooks/use-dependent-field\";\nimport { useFieldCommit } from \"@lattice-php/lattice/form/hooks/use-field-commit\";\nimport { useFieldScope } from \"@lattice-php/lattice/form/hooks/field-scope\";\nimport { useFormValue, useFormValues } from \"@lattice-php/lattice/form/hooks/values\";\n\nfunction toValues(stored: unknown, fallback: unknown): string[] {\n const source = stored ?? fallback;\n\n if (Array.isArray(source)) {\n return source.map(String);\n }\n\n if (source === undefined || source === null || source === \"\") {\n return [];\n }\n\n return [String(source)];\n}\n\nexport const SelectComponent: RendererComponent<\"field.select\"> = ({ node }) => {\n const { t } = useT(\"lattice\");\n const props = node.props;\n const { action, componentRef, errors, fieldIdPrefix, searchOptions } = useFormContext();\n const { hidden, required, readOnly, disabled } = useDependentField(node);\n const { change, blur } = useFieldCommit();\n const resolvedNode = useResolvedNode(node);\n const name = props.name;\n const scope = useFieldScope();\n const domName = fieldDomName(scope ? scope.scopedName(name) : name, fieldIdPrefix);\n const errorKey = scope ? scope.errorKey(name) : name;\n const searchKey = errorKey;\n const placeholder = props.placeholder || \"Select…\";\n const multiple = props.multiple;\n const searchable = props.searchable;\n const creatable = props.creatable;\n const staticOptions = useMemo(\n () => (resolvedNode.props as { options?: Option[] }).options ?? [],\n [resolvedNode.props],\n );\n const optionSchema = (resolvedNode.props as { optionSchema?: Node[] }).optionSchema;\n\n const globalValue = useFormValue(name);\n const values = useFormValues();\n const valuesRef = useRef(values);\n valuesRef.current = values;\n const storedValue = scope ? scope.getValue(name) : globalValue;\n const selected = useMemo(() => toValues(storedValue, props.value), [storedValue, props.value]);\n const selectedRef = useRef(selected);\n selectedRef.current = selected;\n\n const [open, setOpen] = useState(false);\n const [results, setResults] = useState<Option[] | null>(null);\n const [loading, setLoading] = useState(false);\n const searchAbort = useRef<AbortController | null>(null);\n\n const optionsByValue = useMemo(() => {\n const map = new Map<string, Option>();\n\n for (const option of [...staticOptions, ...(results ?? [])]) {\n map.set(option.value, option);\n }\n\n return map;\n }, [staticOptions, results]);\n const labelFor = (value: string) => optionsByValue.get(value)?.label ?? value;\n const colorFor = (value: string) =>\n coerceColor((optionsByValue.get(value)?.data as { color?: unknown } | undefined)?.color);\n\n const locked = readOnly || disabled;\n\n const search = useCallback(\n (query: string) => {\n searchAbort.current?.abort();\n\n if (query.trim() === \"\") {\n setResults(null);\n setLoading(false);\n\n return;\n }\n\n const controller = new AbortController();\n searchAbort.current = controller;\n setLoading(true);\n\n if (searchOptions) {\n void searchOptions(searchKey, query, valuesRef.current, controller.signal)\n .then((options) => {\n setResults(options);\n setLoading(false);\n })\n .catch(() => {});\n\n return;\n }\n\n void postFormAction<{ options?: Option[] }>(\n action,\n componentRef,\n { ...valuesRef.current, _sub: \"search\", _target: searchKey, _q: query },\n controller.signal,\n )\n .then((response) => {\n setResults(response?.options ?? []);\n setLoading(false);\n })\n .catch(() => {});\n },\n [action, componentRef, searchKey, searchOptions],\n );\n\n function commit(next: string[]): void {\n change(name, multiple ? next : (next[0] ?? \"\"));\n }\n\n function select(value: string): void {\n if (multiple) {\n const next = selectedRef.current.includes(value)\n ? selectedRef.current.filter((item) => item !== value)\n : [...selectedRef.current, value];\n selectedRef.current = next;\n commit(next);\n\n return;\n }\n\n selectedRef.current = [value];\n commit([value]);\n }\n\n function remove(value: string): void {\n commit(selected.filter((item) => item !== value));\n }\n\n function applyCreated(value: string): void {\n if (!multiple) {\n selectedRef.current = [value];\n commit([value]);\n\n return;\n }\n\n if (!selectedRef.current.includes(value)) {\n const next = [...selectedRef.current, value];\n selectedRef.current = next;\n commit(next);\n }\n }\n\n if (hidden) {\n return null;\n }\n\n const options = searchable ? (results ?? staticOptions) : staticOptions;\n\n const renderOption = optionSchema?.length\n ? (option: Option) => (\n <Renderer\n nodes={materializeSchema(optionSchema, {\n ...option.data,\n label: option.label,\n value: option.value,\n })}\n />\n )\n : undefined;\n\n return (\n <FormFieldFrame\n error={errors[errorKey]}\n helperText={props.helperText ?? undefined}\n tooltip={props.tooltip ?? undefined}\n label={props.label ?? \"\"}\n name={domName}\n required={required}\n >\n {multiple ? (\n selected.map((value) => (\n <input key={value} name={`${domName}[]`} type=\"hidden\" value={value} />\n ))\n ) : (\n <input name={domName} type=\"hidden\" value={selected[0] ?? \"\"} />\n )}\n\n <div>\n {multiple && selected.length > 0 && (\n <div className=\"mb-1.5 flex flex-wrap gap-1\">\n {selected.map((value) => {\n const color = colorFor(value);\n\n return (\n <span\n className=\"inline-flex items-center gap-1 rounded-lt-sm bg-lt-muted px-2 py-0.5 text-xs\"\n key={value}\n >\n {color && (\n <span\n aria-hidden=\"true\"\n className=\"size-2 shrink-0 rounded-full\"\n style={{ background: colorValue(color) }}\n />\n )}\n {labelFor(value)}\n {!locked && (\n <button\n aria-label={t(\"form.remove-option\", \"Remove {{label}}\", {\n label: labelFor(value),\n })}\n data-test={`select-${name}-remove-${value}`}\n className=\"text-lt-muted-fg hover:text-lt-fg [&_svg]:size-lt-icon-xs\"\n onClick={() => remove(value)}\n type=\"button\"\n >\n <Icon name=\"x\" />\n </button>\n )}\n </span>\n );\n })}\n </div>\n )}\n\n <Combobox\n creatable={creatable}\n emptyLabel={props.emptyLabel ?? undefined}\n loading={loading}\n multiple={multiple}\n onCommit={applyCreated}\n onCreate={applyCreated}\n onSearch={searchable ? search : undefined}\n onSelect={select}\n open={open && !locked}\n onOpenChange={(next) => {\n setOpen(next);\n\n if (!next) {\n blur(name);\n }\n }}\n options={options}\n renderOption={renderOption}\n searchPlaceholder={props.searchPlaceholder ?? undefined}\n showSearch={Boolean(searchable || creatable)}\n selected={selected}\n testId={`select-${name}`}\n trigger={\n <>\n {!multiple && selected.length > 0 ? (\n <span>{labelFor(selected[0])}</span>\n ) : (\n <span className=\"text-lt-muted-fg\">{placeholder}</span>\n )}\n <Icon name=\"chevrons-up-down\" className=\"size-lt-icon-md shrink-0 text-lt-muted-fg\" />\n </>\n }\n triggerClassName={cn(\n controlSurface(),\n \"flex items-center justify-between gap-2 text-left\",\n locked && \"cursor-not-allowed opacity-60\",\n )}\n triggerProps={{\n \"aria-haspopup\": \"listbox\",\n autoFocus: props.autoFocus ?? undefined,\n \"data-test\": `select-${name}`,\n disabled: locked,\n id: domName,\n tabIndex: props.tabIndex ?? undefined,\n }}\n />\n </div>\n </FormFieldFrame>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { LATTICE_EVENT } from "../../core/event-names.js";
|
|
2
|
+
import { withHeaders } from "../../core/headers.js";
|
|
3
|
+
import { refreshRef } from "../../core/api.js";
|
|
2
4
|
import { useT } from "../../i18n/instance.js";
|
|
3
5
|
import { useWindowEvent } from "../../core/hooks/use-window-event.js";
|
|
4
|
-
import { withHeaders } from "../../core/headers.js";
|
|
5
6
|
import { nodeKey } from "../../core/nodes.js";
|
|
6
7
|
import { RenderNode } from "../../core/renderer.js";
|
|
7
8
|
import { FormProvider } from "../hooks/context.js";
|
|
8
9
|
import { PrefillProvider } from "../hooks/prefill-context.js";
|
|
9
10
|
import { ResolvedNodesProvider } from "../hooks/resolved-nodes.js";
|
|
10
|
-
import { FormValuesProvider } from "../hooks/values.js";
|
|
11
|
+
import { FormValuesProvider, useResetFormValues } from "../hooks/values.js";
|
|
11
12
|
import { useFormResolver } from "../hooks/use-form-resolver.js";
|
|
12
13
|
import { collectFields } from "../lib/collect-fields.js";
|
|
13
14
|
import { FormSubmitButton } from "./base/submit-button.js";
|
|
14
15
|
import { Form } from "@inertiajs/react";
|
|
15
|
-
import { useMemo } from "react";
|
|
16
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
16
17
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
17
18
|
//#region resources/js/form/components/form.tsx
|
|
18
19
|
var JUSTIFY_CLASS = {
|
|
@@ -24,16 +25,20 @@ var JUSTIFY_CLASS = {
|
|
|
24
25
|
evenly: "justify-evenly"
|
|
25
26
|
};
|
|
26
27
|
function FormResetListener({ componentId, reset }) {
|
|
28
|
+
const resetValues = useResetFormValues();
|
|
27
29
|
useWindowEvent(LATTICE_EVENT.resetForm, (event) => {
|
|
28
30
|
const detail = event.detail;
|
|
29
|
-
if (!detail?.form || detail.form === componentId)
|
|
31
|
+
if (!detail?.form || detail.form === componentId) {
|
|
32
|
+
reset();
|
|
33
|
+
resetValues();
|
|
34
|
+
}
|
|
30
35
|
});
|
|
31
36
|
return null;
|
|
32
37
|
}
|
|
33
38
|
function FormBody({ action, children, componentRef, nodes, shouldRenderSubmitButton, submitButtons, submitEmphasis, submitJustify, submitLabel, submitVariant, summaryLabel }) {
|
|
34
39
|
const { nodes: resolvedNodes, markUserEdit } = useFormResolver(action, componentRef, nodes);
|
|
35
40
|
return /* @__PURE__ */ jsx(PrefillProvider, {
|
|
36
|
-
value: { markUserEdit },
|
|
41
|
+
value: useMemo(() => ({ markUserEdit }), [markUserEdit]),
|
|
37
42
|
children: /* @__PURE__ */ jsx(ResolvedNodesProvider, {
|
|
38
43
|
nodes: resolvedNodes,
|
|
39
44
|
children: /* @__PURE__ */ jsxs("div", {
|
|
@@ -56,7 +61,23 @@ function FormBody({ action, children, componentRef, nodes, shouldRenderSubmitBut
|
|
|
56
61
|
})
|
|
57
62
|
});
|
|
58
63
|
}
|
|
64
|
+
function configuredResetFields(configured) {
|
|
65
|
+
if (!configured || Array.isArray(configured) && configured.length === 0) return false;
|
|
66
|
+
return Array.isArray(configured) ? configured : void 0;
|
|
67
|
+
}
|
|
59
68
|
var FormComponent = ({ children, node }) => {
|
|
69
|
+
return /* @__PURE__ */ jsx(FormValuesProvider, {
|
|
70
|
+
initial: useMemo(() => ({
|
|
71
|
+
...collectFields(node.schema).values,
|
|
72
|
+
...node.props.state
|
|
73
|
+
}), [node.schema, node.props.state]),
|
|
74
|
+
children: /* @__PURE__ */ jsx(FormShell, {
|
|
75
|
+
node,
|
|
76
|
+
children
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
function FormShell({ children, node }) {
|
|
60
81
|
const { t } = useT("lattice");
|
|
61
82
|
const props = node.props;
|
|
62
83
|
const action = props.action ?? "#";
|
|
@@ -66,12 +87,7 @@ var FormComponent = ({ children, node }) => {
|
|
|
66
87
|
const precognitive = props.precognitive;
|
|
67
88
|
const resetOnError = props.resetOnError ?? false;
|
|
68
89
|
const resetOnSuccess = props.resetOnSuccess ?? [];
|
|
69
|
-
const
|
|
70
|
-
const { labels: fieldLabels, values: fieldValues } = useMemo(() => collectFields(node.schema), [node.schema]);
|
|
71
|
-
const initialValues = useMemo(() => ({
|
|
72
|
-
...fieldValues,
|
|
73
|
-
...state
|
|
74
|
-
}), [fieldValues, state]);
|
|
90
|
+
const fieldLabels = useMemo(() => collectFields(node.schema).labels, [node.schema]);
|
|
75
91
|
const shouldRenderSubmitButton = props.submitButton;
|
|
76
92
|
const submitButtons = props.submitButtons ?? void 0;
|
|
77
93
|
const submitJustify = props.submitJustify ?? void 0;
|
|
@@ -80,7 +96,19 @@ var FormComponent = ({ children, node }) => {
|
|
|
80
96
|
const submitEmphasis = props.submitEmphasis ?? void 0;
|
|
81
97
|
const summaryLabel = props.validationSummaryLabel;
|
|
82
98
|
const validationTimeout = props.validationTimeout ?? void 0;
|
|
99
|
+
const resetValues = useResetFormValues();
|
|
100
|
+
const resetConfigured = (configured) => {
|
|
101
|
+
const fields = configuredResetFields(configured);
|
|
102
|
+
if (fields !== false) resetValues(fields);
|
|
103
|
+
};
|
|
104
|
+
const formRef = useRef(null);
|
|
105
|
+
const retryPhase = useRef("idle");
|
|
106
|
+
const [renewedSubmitTick, setRenewedSubmitTick] = useState(0);
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (renewedSubmitTick > 0 && retryPhase.current === "renewing") formRef.current?.submit();
|
|
109
|
+
}, [renewedSubmitTick]);
|
|
83
110
|
return /* @__PURE__ */ jsx(Form, {
|
|
111
|
+
ref: formRef,
|
|
84
112
|
action,
|
|
85
113
|
"data-slot": "form",
|
|
86
114
|
"data-lattice-component": node.id,
|
|
@@ -91,6 +119,17 @@ var FormComponent = ({ children, node }) => {
|
|
|
91
119
|
validationTimeout: precognitive ? validationTimeout : void 0,
|
|
92
120
|
headers: withHeaders(componentRef),
|
|
93
121
|
className: "mx-auto flex w-full max-w-2xl flex-col gap-6",
|
|
122
|
+
onStart: () => {
|
|
123
|
+
retryPhase.current = retryPhase.current === "renewing" ? "retried" : "idle";
|
|
124
|
+
},
|
|
125
|
+
onSuccess: () => resetConfigured(resetOnSuccess),
|
|
126
|
+
onError: () => resetConfigured(resetOnError),
|
|
127
|
+
onHttpException: (response) => {
|
|
128
|
+
if (response.status !== 403 || componentRef === "" || retryPhase.current === "retried") return;
|
|
129
|
+
retryPhase.current = "renewing";
|
|
130
|
+
refreshRef(componentRef).finally(() => setRenewedSubmitTick((tick) => tick + 1));
|
|
131
|
+
return false;
|
|
132
|
+
},
|
|
94
133
|
children: ({ clearErrors, errors, processing, reset, touch, validate, validating }) => /* @__PURE__ */ jsxs(FormProvider, {
|
|
95
134
|
value: {
|
|
96
135
|
action,
|
|
@@ -118,26 +157,23 @@ var FormComponent = ({ children, node }) => {
|
|
|
118
157
|
className: "text-center text-sm font-medium text-lt-success",
|
|
119
158
|
children: props.status
|
|
120
159
|
}),
|
|
121
|
-
/* @__PURE__ */ jsx(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
summaryLabel,
|
|
134
|
-
children
|
|
135
|
-
})
|
|
160
|
+
/* @__PURE__ */ jsx(FormBody, {
|
|
161
|
+
action,
|
|
162
|
+
componentRef,
|
|
163
|
+
nodes: node.schema,
|
|
164
|
+
shouldRenderSubmitButton,
|
|
165
|
+
submitButtons,
|
|
166
|
+
submitEmphasis,
|
|
167
|
+
submitJustify,
|
|
168
|
+
submitLabel,
|
|
169
|
+
submitVariant,
|
|
170
|
+
summaryLabel,
|
|
171
|
+
children
|
|
136
172
|
})
|
|
137
173
|
]
|
|
138
174
|
})
|
|
139
175
|
});
|
|
140
|
-
}
|
|
176
|
+
}
|
|
141
177
|
//#endregion
|
|
142
178
|
export { FormComponent };
|
|
143
179
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.js","names":[],"sources":["../../../resources/js/form/components/form.tsx"],"sourcesContent":["import { Form as InertiaForm } from \"@inertiajs/react\";\nimport { withHeaders } from \"@lattice-php/lattice/core/headers\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/core/event-names\";\nimport { useWindowEvent } from \"@lattice-php/lattice/core/hooks/use-window-event\";\nimport { nodeKey } from \"@lattice-php/lattice/core/nodes\";\nimport { RenderNode } from \"@lattice-php/lattice/core/renderer\";\nimport type { Node, RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport type { Emphasis, Justify, Variant } from \"@lattice-php/lattice/types/generated\";\nimport { useMemo } from \"react\";\nimport { FormSubmitButton } from \"./base/submit-button\";\nimport { FormProvider } from \"@lattice-php/lattice/form/hooks/context\";\nimport { collectFields } from \"@lattice-php/lattice/form/lib/collect-fields\";\nimport { PrefillProvider } from \"@lattice-php/lattice/form/hooks/prefill-context\";\nimport { ResolvedNodesProvider } from \"@lattice-php/lattice/form/hooks/resolved-nodes\";\nimport { useFormResolver } from \"@lattice-php/lattice/form/hooks/use-form-resolver\";\nimport { FormValuesProvider } from \"@lattice-php/lattice/form/hooks/values\";\n\nconst JUSTIFY_CLASS: Record<Justify, string> = {\n start: \"justify-start\",\n center: \"justify-center\",\n end: \"justify-end\",\n between: \"justify-between\",\n around: \"justify-around\",\n evenly: \"justify-evenly\",\n};\n\nfunction FormResetListener({\n componentId,\n reset,\n}: {\n componentId?: string;\n reset: (...fields: string[]) => void;\n}) {\n useWindowEvent(LATTICE_EVENT.resetForm, (event) => {\n const detail = (event as CustomEvent<{ form: string | null }>).detail;\n\n if (!detail?.form || detail.form === componentId) {\n reset();\n }\n });\n\n return null;\n}\n\nfunction FormBody({\n action,\n children,\n componentRef,\n nodes,\n shouldRenderSubmitButton,\n submitButtons,\n submitEmphasis,\n submitJustify,\n submitLabel,\n submitVariant,\n summaryLabel,\n}: {\n action: string;\n children: React.ReactNode;\n componentRef: string;\n nodes: Node[] | undefined;\n shouldRenderSubmitButton: boolean;\n submitButtons: Node<\"button\">[] | undefined;\n submitEmphasis: Emphasis | undefined;\n submitJustify: Justify | undefined;\n submitLabel: string;\n submitVariant: Variant | undefined;\n summaryLabel: string;\n}) {\n const { nodes: resolvedNodes, markUserEdit } = useFormResolver(action, componentRef, nodes);\n\n return (\n <PrefillProvider value={{ markUserEdit }}>\n <ResolvedNodesProvider nodes={resolvedNodes}>\n <div className=\"flex flex-col gap-6\">\n {children}\n\n {shouldRenderSubmitButton && (\n <div className={`flex gap-3 ${JUSTIFY_CLASS[submitJustify ?? \"end\"]}`}>\n {submitButtons?.length ? (\n submitButtons.map((button, index) =>\n button.props.buttonType === \"submit\" ? (\n <FormSubmitButton\n key={nodeKey(button, index)}\n emphasis={button.props.emphasis ?? submitEmphasis}\n label={button.props.label ?? submitLabel}\n summaryLabel={summaryLabel}\n variant={button.props.variant ?? submitVariant}\n />\n ) : (\n <RenderNode key={nodeKey(button, index)} node={button} />\n ),\n )\n ) : (\n <FormSubmitButton\n emphasis={submitEmphasis}\n label={submitLabel}\n summaryLabel={summaryLabel}\n variant={submitVariant}\n />\n )}\n </div>\n )}\n </div>\n </ResolvedNodesProvider>\n </PrefillProvider>\n );\n}\n\nexport const FormComponent: RendererComponent<\"form\"> = ({ children, node }) => {\n const { t } = useT(\"lattice\");\n const props = node.props;\n const action = props.action ?? \"#\";\n const errorBag = props.errorBag;\n const componentRef = props.ref ?? \"\";\n const method = props.method ?? \"post\";\n const precognitive = props.precognitive;\n const resetOnError = props.resetOnError ?? false;\n const resetOnSuccess = props.resetOnSuccess ?? [];\n const state = props.state;\n const { labels: fieldLabels, values: fieldValues } = useMemo(\n () => collectFields(node.schema),\n [node.schema],\n );\n const initialValues = useMemo(() => ({ ...fieldValues, ...state }), [fieldValues, state]);\n const shouldRenderSubmitButton = props.submitButton;\n const submitButtons = props.submitButtons ?? undefined;\n const submitJustify = props.submitJustify ?? undefined;\n const submitLabel = props.submitLabel ?? t(\"form.submit\", \"Submit\");\n const submitVariant = props.submitVariant ?? undefined;\n const submitEmphasis = props.submitEmphasis ?? undefined;\n const summaryLabel = props.validationSummaryLabel;\n const validationTimeout = props.validationTimeout ?? undefined;\n\n return (\n <InertiaForm\n action={action}\n data-slot=\"form\"\n data-lattice-component={node.id}\n errorBag={errorBag}\n method={method}\n resetOnError={resetOnError}\n resetOnSuccess={resetOnSuccess}\n validationTimeout={precognitive ? validationTimeout : undefined}\n headers={withHeaders(componentRef)}\n className=\"mx-auto flex w-full max-w-2xl flex-col gap-6\"\n >\n {({ clearErrors, errors, processing, reset, touch, validate, validating }) => (\n <FormProvider\n value={{\n action,\n clearErrors: (field) => clearErrors(field),\n componentId: node.id,\n componentRef,\n errors: errors as Record<string, string | undefined>,\n fieldLabels,\n precognitive,\n processing,\n touch: (fields) => touch(...fields),\n validate: (field) => validate(field),\n validateFields: (fields, options) => validate({ only: fields, ...options }),\n validating,\n }}\n >\n <FormResetListener componentId={node.id} reset={reset} />\n\n {props.status && (\n <div className=\"text-center text-sm font-medium text-lt-success\">{props.status}</div>\n )}\n\n <FormValuesProvider initial={initialValues}>\n <FormBody\n action={action}\n componentRef={componentRef}\n nodes={node.schema}\n shouldRenderSubmitButton={shouldRenderSubmitButton}\n submitButtons={submitButtons}\n submitEmphasis={submitEmphasis}\n submitJustify={submitJustify}\n submitLabel={submitLabel}\n submitVariant={submitVariant}\n summaryLabel={summaryLabel}\n >\n {children}\n </FormBody>\n </FormValuesProvider>\n </FormProvider>\n )}\n </InertiaForm>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"form.js","names":[],"sources":["../../../resources/js/form/components/form.tsx"],"sourcesContent":["import type { FormComponentRef, HttpExceptionResponse } from \"@inertiajs/core\";\nimport { Form as InertiaForm } from \"@inertiajs/react\";\nimport { refreshRef } from \"@lattice-php/lattice/core/api\";\nimport { withHeaders } from \"@lattice-php/lattice/core/headers\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/core/event-names\";\nimport { useWindowEvent } from \"@lattice-php/lattice/core/hooks/use-window-event\";\nimport { nodeKey } from \"@lattice-php/lattice/core/nodes\";\nimport { RenderNode } from \"@lattice-php/lattice/core/renderer\";\nimport type { Node, RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport type { Emphasis, Justify, Variant } from \"@lattice-php/lattice/types/generated\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { FormSubmitButton } from \"./base/submit-button\";\nimport { FormProvider } from \"@lattice-php/lattice/form/hooks/context\";\nimport { collectFields } from \"@lattice-php/lattice/form/lib/collect-fields\";\nimport { PrefillProvider } from \"@lattice-php/lattice/form/hooks/prefill-context\";\nimport { ResolvedNodesProvider } from \"@lattice-php/lattice/form/hooks/resolved-nodes\";\nimport { useFormResolver } from \"@lattice-php/lattice/form/hooks/use-form-resolver\";\nimport { FormValuesProvider, useResetFormValues } from \"@lattice-php/lattice/form/hooks/values\";\n\nconst JUSTIFY_CLASS: Record<Justify, string> = {\n start: \"justify-start\",\n center: \"justify-center\",\n end: \"justify-end\",\n between: \"justify-between\",\n around: \"justify-around\",\n evenly: \"justify-evenly\",\n};\n\nfunction FormResetListener({\n componentId,\n reset,\n}: {\n componentId?: string;\n reset: (...fields: string[]) => void;\n}) {\n const resetValues = useResetFormValues();\n\n useWindowEvent(LATTICE_EVENT.resetForm, (event) => {\n const detail = (event as CustomEvent<{ form: string | null }>).detail;\n\n if (!detail?.form || detail.form === componentId) {\n reset();\n resetValues();\n }\n });\n\n return null;\n}\n\nfunction FormBody({\n action,\n children,\n componentRef,\n nodes,\n shouldRenderSubmitButton,\n submitButtons,\n submitEmphasis,\n submitJustify,\n submitLabel,\n submitVariant,\n summaryLabel,\n}: {\n action: string;\n children: React.ReactNode;\n componentRef: string;\n nodes: Node[] | undefined;\n shouldRenderSubmitButton: boolean;\n submitButtons: Node<\"button\">[] | undefined;\n submitEmphasis: Emphasis | undefined;\n submitJustify: Justify | undefined;\n submitLabel: string;\n submitVariant: Variant | undefined;\n summaryLabel: string;\n}) {\n const { nodes: resolvedNodes, markUserEdit } = useFormResolver(action, componentRef, nodes);\n const prefill = useMemo(() => ({ markUserEdit }), [markUserEdit]);\n\n return (\n <PrefillProvider value={prefill}>\n <ResolvedNodesProvider nodes={resolvedNodes}>\n <div className=\"flex flex-col gap-6\">\n {children}\n\n {shouldRenderSubmitButton && (\n <div className={`flex gap-3 ${JUSTIFY_CLASS[submitJustify ?? \"end\"]}`}>\n {submitButtons?.length ? (\n submitButtons.map((button, index) =>\n button.props.buttonType === \"submit\" ? (\n <FormSubmitButton\n key={nodeKey(button, index)}\n emphasis={button.props.emphasis ?? submitEmphasis}\n label={button.props.label ?? submitLabel}\n summaryLabel={summaryLabel}\n variant={button.props.variant ?? submitVariant}\n />\n ) : (\n <RenderNode key={nodeKey(button, index)} node={button} />\n ),\n )\n ) : (\n <FormSubmitButton\n emphasis={submitEmphasis}\n label={submitLabel}\n summaryLabel={summaryLabel}\n variant={submitVariant}\n />\n )}\n </div>\n )}\n </div>\n </ResolvedNodesProvider>\n </PrefillProvider>\n );\n}\n\nfunction configuredResetFields(\n configured: string[] | boolean | null | undefined,\n): string[] | undefined | false {\n if (!configured || (Array.isArray(configured) && configured.length === 0)) {\n return false;\n }\n\n return Array.isArray(configured) ? configured : undefined;\n}\n\nexport const FormComponent: RendererComponent<\"form\"> = ({ children, node }) => {\n const initialValues = useMemo(\n () => ({ ...collectFields(node.schema).values, ...node.props.state }),\n [node.schema, node.props.state],\n );\n\n return (\n <FormValuesProvider initial={initialValues}>\n <FormShell node={node}>{children}</FormShell>\n </FormValuesProvider>\n );\n};\n\nfunction FormShell({ children, node }: { children: React.ReactNode; node: Node<\"form\"> }) {\n const { t } = useT(\"lattice\");\n const props = node.props;\n const action = props.action ?? \"#\";\n const errorBag = props.errorBag;\n const componentRef = props.ref ?? \"\";\n const method = props.method ?? \"post\";\n const precognitive = props.precognitive;\n const resetOnError = props.resetOnError ?? false;\n const resetOnSuccess = props.resetOnSuccess ?? [];\n const fieldLabels = useMemo(() => collectFields(node.schema).labels, [node.schema]);\n const shouldRenderSubmitButton = props.submitButton;\n const submitButtons = props.submitButtons ?? undefined;\n const submitJustify = props.submitJustify ?? undefined;\n const submitLabel = props.submitLabel ?? t(\"form.submit\", \"Submit\");\n const submitVariant = props.submitVariant ?? undefined;\n const submitEmphasis = props.submitEmphasis ?? undefined;\n const summaryLabel = props.validationSummaryLabel;\n const validationTimeout = props.validationTimeout ?? undefined;\n\n const resetValues = useResetFormValues();\n const resetConfigured = (configured: string[] | boolean | null | undefined): void => {\n const fields = configuredResetFields(configured);\n\n if (fields !== false) {\n resetValues(fields);\n }\n };\n\n const formRef = useRef<FormComponentRef | null>(null);\n // One renewal attempt per user-initiated submit: 'renewing' marks the window\n // between the 403 and our programmatic retry, 'retried' lets the second\n // failure surface normally.\n const retryPhase = useRef<\"idle\" | \"renewing\" | \"retried\">(\"idle\");\n const [renewedSubmitTick, setRenewedSubmitTick] = useState(0);\n\n useEffect(() => {\n // The state bump forces this render first, so the headers prop below has\n // re-resolved the renewed token before the retry goes out.\n if (renewedSubmitTick > 0 && retryPhase.current === \"renewing\") {\n formRef.current?.submit();\n }\n }, [renewedSubmitTick]);\n\n return (\n <InertiaForm\n ref={formRef}\n action={action}\n data-slot=\"form\"\n data-lattice-component={node.id}\n errorBag={errorBag}\n method={method}\n resetOnError={resetOnError}\n resetOnSuccess={resetOnSuccess}\n validationTimeout={precognitive ? validationTimeout : undefined}\n headers={withHeaders(componentRef)}\n className=\"mx-auto flex w-full max-w-2xl flex-col gap-6\"\n onStart={() => {\n retryPhase.current = retryPhase.current === \"renewing\" ? \"retried\" : \"idle\";\n }}\n onSuccess={() => resetConfigured(resetOnSuccess)}\n onError={() => resetConfigured(resetOnError)}\n onHttpException={(response: HttpExceptionResponse) => {\n if (response.status !== 403 || componentRef === \"\" || retryPhase.current === \"retried\") {\n return undefined;\n }\n\n retryPhase.current = \"renewing\";\n // Retry even when the renewal failed: a genuine 403 then surfaces\n // through the second attempt instead of being swallowed here.\n void refreshRef(componentRef).finally(() => setRenewedSubmitTick((tick) => tick + 1));\n\n return false;\n }}\n >\n {({ clearErrors, errors, processing, reset, touch, validate, validating }) => (\n <FormProvider\n value={{\n action,\n clearErrors: (field) => clearErrors(field),\n componentId: node.id,\n componentRef,\n errors: errors as Record<string, string | undefined>,\n fieldLabels,\n precognitive,\n processing,\n touch: (fields) => touch(...fields),\n validate: (field) => validate(field),\n validateFields: (fields, options) => validate({ only: fields, ...options }),\n validating,\n }}\n >\n <FormResetListener componentId={node.id} reset={reset} />\n\n {props.status && (\n <div className=\"text-center text-sm font-medium text-lt-success\">{props.status}</div>\n )}\n\n <FormBody\n action={action}\n componentRef={componentRef}\n nodes={node.schema}\n shouldRenderSubmitButton={shouldRenderSubmitButton}\n submitButtons={submitButtons}\n submitEmphasis={submitEmphasis}\n submitJustify={submitJustify}\n submitLabel={submitLabel}\n submitVariant={submitVariant}\n summaryLabel={summaryLabel}\n >\n {children}\n </FormBody>\n </FormProvider>\n )}\n </InertiaForm>\n );\n}\n"],"mappings":"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"}
|
|
@@ -62,7 +62,7 @@ function useFormResolver(action, componentRef, nodes) {
|
|
|
62
62
|
const controller = new AbortController();
|
|
63
63
|
const timer = window.setTimeout(() => {
|
|
64
64
|
postFormAction(action, componentRef, {
|
|
65
|
-
|
|
65
|
+
_sub: "resolve",
|
|
66
66
|
...values
|
|
67
67
|
}, controller.signal).then((response) => {
|
|
68
68
|
if (!response) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-form-resolver.js","names":[],"sources":["../../../resources/js/form/hooks/use-form-resolver.ts"],"sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport type { ResolveResponse } from \"@lattice-php/lattice/types/generated\";\nimport { walkFields } from \"@lattice-php/lattice/form/lib/field-props\";\nimport { FORM_DEBOUNCE_MS, postFormAction } from \"@lattice-php/lattice/form/lib/form-transport\";\nimport {\n collectPrefillTargets,\n getPath,\n pathsToClear,\n pruneOverrides,\n seededOverrides,\n} from \"@lattice-php/lattice/form/lib/prefill-targets\";\nimport type { PrefillController } from \"./prefill-context\";\nimport { useFormValues, useSetFormValue } from \"./values\";\n\ntype FormResolver = {\n nodes: Record<string, Node>;\n markUserEdit: PrefillController[\"markUserEdit\"];\n};\n\nexport function useFormResolver(\n action: string,\n componentRef: string,\n nodes: Node[] | undefined,\n): FormResolver {\n const values = useFormValues();\n const setValue = useSetFormValue();\n const [resolved, setResolved] = useState<Record<string, Node>>({});\n\n const targets = useMemo(() => collectPrefillTargets(nodes, values), [nodes, values]);\n\n const overrides = useRef<Set<string>>(new Set());\n const seededOverrideKeys = useRef<Set<string>>(new Set());\n const previousValues = useRef<Record<string, unknown>>(values);\n const previousTargets = useRef(targets);\n\n // Read targets via a ref inside the effect: `targets` changes identity on every\n // value change, but the effect must fire only when `watchSignature` changes\n // (which already reflects added/removed target paths). Keeping `targets` out of\n // the dep array preserves the targeted watch in non-\"any\" mode.\n const targetsRef = useRef(targets);\n targetsRef.current = targets;\n\n useLayoutEffect(() => {\n const liveOverrideKeys = new Set(targets.map((target) => target.overrideKey));\n seededOverrideKeys.current = new Set(\n [...seededOverrideKeys.current].filter((overrideKey) => liveOverrideKeys.has(overrideKey)),\n );\n const freshTargets = targets.filter(\n (target) => !seededOverrideKeys.current.has(target.overrideKey),\n );\n\n // Layout ordering seeds loaded values before the first resolve effect can apply prefill.\n for (const overrideKey of seededOverrides(freshTargets, values)) {\n overrides.current.add(overrideKey);\n }\n\n for (const target of freshTargets) {\n seededOverrideKeys.current.add(target.overrideKey);\n }\n }, [targets, values]);\n\n const markUserEdit = useCallback((overrideKey: string) => {\n overrides.current.add(overrideKey);\n }, []);\n\n const watch = useMemo(() => {\n const keys = new Set<string>();\n let any = false;\n walkFields(nodes, (props) => {\n if (Array.isArray(props.dependsOnKeys)) {\n for (const key of props.dependsOnKeys) {\n keys.add(String(key));\n }\n }\n if (props.dependsOnAny) {\n any = true;\n }\n });\n return { keys: [...keys], any };\n }, [nodes]);\n\n const watchPaths = useMemo(() => {\n const set = new Set<string>(watch.keys);\n for (const target of targets) {\n for (const dep of target.resetOn) {\n set.add(dep);\n }\n for (const dep of target.refreshOn) {\n set.add(dep);\n }\n }\n return [...set];\n }, [watch.keys, targets]);\n\n // In \"any\" mode the values store keeps a stable reference until something\n // actually changes, so its identity is the change signal. Otherwise hash the\n // watched paths (form-level and per-row).\n const watchSignature = watch.any\n ? values\n : JSON.stringify(watchPaths.map((path) => getPath(values, path)));\n\n useEffect(() => {\n if (watchPaths.length === 0 && !watch.any) {\n return;\n }\n\n const previous = previousValues.current;\n previousValues.current = values;\n\n // `resetOn` deps unlock a path (a fresh product gives a fresh price); `refreshOn`\n // deps only re-ask the server and never clear an override (a customer change\n // re-prices untouched rows but leaves user-edited ones alone).\n for (const overrideKey of pathsToClear(\n { targets: previousTargets.current, values: previous },\n { targets: targetsRef.current, values },\n )) {\n overrides.current.delete(overrideKey);\n }\n previousTargets.current = targetsRef.current;\n\n // Override ownership is row-id keyed, so pruning removes departed rows without reindex drift.\n overrides.current = pruneOverrides(overrides.current, targetsRef.current);\n\n const controller = new AbortController();\n\n const timer = window.setTimeout(() => {\n void postFormAction<ResolveResponse>(\n action,\n componentRef,\n {
|
|
1
|
+
{"version":3,"file":"use-form-resolver.js","names":[],"sources":["../../../resources/js/form/hooks/use-form-resolver.ts"],"sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport type { ResolveResponse } from \"@lattice-php/lattice/types/generated\";\nimport { walkFields } from \"@lattice-php/lattice/form/lib/field-props\";\nimport { FORM_DEBOUNCE_MS, postFormAction } from \"@lattice-php/lattice/form/lib/form-transport\";\nimport {\n collectPrefillTargets,\n getPath,\n pathsToClear,\n pruneOverrides,\n seededOverrides,\n} from \"@lattice-php/lattice/form/lib/prefill-targets\";\nimport type { PrefillController } from \"./prefill-context\";\nimport { useFormValues, useSetFormValue } from \"./values\";\n\ntype FormResolver = {\n nodes: Record<string, Node>;\n markUserEdit: PrefillController[\"markUserEdit\"];\n};\n\nexport function useFormResolver(\n action: string,\n componentRef: string,\n nodes: Node[] | undefined,\n): FormResolver {\n const values = useFormValues();\n const setValue = useSetFormValue();\n const [resolved, setResolved] = useState<Record<string, Node>>({});\n\n const targets = useMemo(() => collectPrefillTargets(nodes, values), [nodes, values]);\n\n const overrides = useRef<Set<string>>(new Set());\n const seededOverrideKeys = useRef<Set<string>>(new Set());\n const previousValues = useRef<Record<string, unknown>>(values);\n const previousTargets = useRef(targets);\n\n // Read targets via a ref inside the effect: `targets` changes identity on every\n // value change, but the effect must fire only when `watchSignature` changes\n // (which already reflects added/removed target paths). Keeping `targets` out of\n // the dep array preserves the targeted watch in non-\"any\" mode.\n const targetsRef = useRef(targets);\n targetsRef.current = targets;\n\n useLayoutEffect(() => {\n const liveOverrideKeys = new Set(targets.map((target) => target.overrideKey));\n seededOverrideKeys.current = new Set(\n [...seededOverrideKeys.current].filter((overrideKey) => liveOverrideKeys.has(overrideKey)),\n );\n const freshTargets = targets.filter(\n (target) => !seededOverrideKeys.current.has(target.overrideKey),\n );\n\n // Layout ordering seeds loaded values before the first resolve effect can apply prefill.\n for (const overrideKey of seededOverrides(freshTargets, values)) {\n overrides.current.add(overrideKey);\n }\n\n for (const target of freshTargets) {\n seededOverrideKeys.current.add(target.overrideKey);\n }\n }, [targets, values]);\n\n const markUserEdit = useCallback((overrideKey: string) => {\n overrides.current.add(overrideKey);\n }, []);\n\n const watch = useMemo(() => {\n const keys = new Set<string>();\n let any = false;\n walkFields(nodes, (props) => {\n if (Array.isArray(props.dependsOnKeys)) {\n for (const key of props.dependsOnKeys) {\n keys.add(String(key));\n }\n }\n if (props.dependsOnAny) {\n any = true;\n }\n });\n return { keys: [...keys], any };\n }, [nodes]);\n\n const watchPaths = useMemo(() => {\n const set = new Set<string>(watch.keys);\n for (const target of targets) {\n for (const dep of target.resetOn) {\n set.add(dep);\n }\n for (const dep of target.refreshOn) {\n set.add(dep);\n }\n }\n return [...set];\n }, [watch.keys, targets]);\n\n // In \"any\" mode the values store keeps a stable reference until something\n // actually changes, so its identity is the change signal. Otherwise hash the\n // watched paths (form-level and per-row).\n const watchSignature = watch.any\n ? values\n : JSON.stringify(watchPaths.map((path) => getPath(values, path)));\n\n useEffect(() => {\n if (watchPaths.length === 0 && !watch.any) {\n return;\n }\n\n const previous = previousValues.current;\n previousValues.current = values;\n\n // `resetOn` deps unlock a path (a fresh product gives a fresh price); `refreshOn`\n // deps only re-ask the server and never clear an override (a customer change\n // re-prices untouched rows but leaves user-edited ones alone).\n for (const overrideKey of pathsToClear(\n { targets: previousTargets.current, values: previous },\n { targets: targetsRef.current, values },\n )) {\n overrides.current.delete(overrideKey);\n }\n previousTargets.current = targetsRef.current;\n\n // Override ownership is row-id keyed, so pruning removes departed rows without reindex drift.\n overrides.current = pruneOverrides(overrides.current, targetsRef.current);\n\n const controller = new AbortController();\n\n const timer = window.setTimeout(() => {\n void postFormAction<ResolveResponse>(\n action,\n componentRef,\n { _sub: \"resolve\", ...values },\n controller.signal,\n )\n .then((response) => {\n if (!response) {\n return;\n }\n for (const [name, value] of Object.entries(response.values ?? {})) {\n setValue(name, value);\n }\n const targetsByPath = new Map(\n targetsRef.current.map((target) => [target.path, target] as const),\n );\n for (const [path, value] of Object.entries(response.prefill ?? {})) {\n const target = targetsByPath.get(path);\n\n if (target && !overrides.current.has(target.overrideKey)) {\n setValue(path, value);\n }\n }\n if (response.fields) {\n setResolved(response.fields);\n }\n })\n .catch(() => {});\n }, FORM_DEBOUNCE_MS);\n\n return () => {\n window.clearTimeout(timer);\n controller.abort();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [watchSignature, action, componentRef, watch.any, setValue]);\n\n return { nodes: resolved, markUserEdit };\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type SetFormValue = (name: string, value: unknown | ((previous: unknown) => unknown)) => void;
|
|
2
|
+
export type ResetFormValues = (fields?: string[]) => void;
|
|
2
3
|
export declare function FormValuesProvider({ initial, children, }: {
|
|
3
4
|
initial: Record<string, unknown>;
|
|
4
5
|
children: React.ReactNode;
|
|
@@ -7,3 +8,10 @@ export declare function useFormValues(): Record<string, unknown>;
|
|
|
7
8
|
export declare function useFormValue(name: string): unknown;
|
|
8
9
|
export declare function useFormValuesFor(paths: string[]): Record<string, unknown>;
|
|
9
10
|
export declare function useSetFormValue(): SetFormValue;
|
|
11
|
+
/**
|
|
12
|
+
* Reset values to the store's initial snapshot — all of them, or just the
|
|
13
|
+
* given field paths. This is the reset that actually clears controlled
|
|
14
|
+
* fields; Inertia's own form reset only touches its internal data and the
|
|
15
|
+
* DOM, which store-driven inputs ignore.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useResetFormValues(): ResetFormValues;
|
|
@@ -72,6 +72,13 @@ function createFormValuesStore(initial) {
|
|
|
72
72
|
initialValues = nextInitial;
|
|
73
73
|
updateValues(nextInitial, "");
|
|
74
74
|
},
|
|
75
|
+
reset: (fields) => {
|
|
76
|
+
if (fields === void 0) {
|
|
77
|
+
updateValues(initialValues, "");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
updateValues(fields.reduce((accumulated, field) => setPath(accumulated, field, getPath(initialValues, field)), values), "");
|
|
81
|
+
},
|
|
75
82
|
setValue: (name, value) => {
|
|
76
83
|
const previous = getPath(values, name);
|
|
77
84
|
const next = typeof value === "function" ? value(previous) : value;
|
|
@@ -130,7 +137,17 @@ function useFormValuesFor(paths) {
|
|
|
130
137
|
function useSetFormValue() {
|
|
131
138
|
return useContext(SetFormValueContext);
|
|
132
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Reset values to the store's initial snapshot — all of them, or just the
|
|
142
|
+
* given field paths. This is the reset that actually clears controlled
|
|
143
|
+
* fields; Inertia's own form reset only touches its internal data and the
|
|
144
|
+
* DOM, which store-driven inputs ignore.
|
|
145
|
+
*/
|
|
146
|
+
function useResetFormValues() {
|
|
147
|
+
const store = useFormValuesStore();
|
|
148
|
+
return useCallback((fields) => store.reset(fields), [store]);
|
|
149
|
+
}
|
|
133
150
|
//#endregion
|
|
134
|
-
export { FormValuesProvider, useFormValue, useFormValues, useFormValuesFor, useSetFormValue };
|
|
151
|
+
export { FormValuesProvider, useFormValue, useFormValues, useFormValuesFor, useResetFormValues, useSetFormValue };
|
|
135
152
|
|
|
136
153
|
//# sourceMappingURL=values.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"values.js","names":[],"sources":["../../../resources/js/form/hooks/values.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport { getPath, setPath } from \"@lattice-php/lattice/form/lib/form-path\";\n\nexport type SetFormValue = (\n name: string,\n value: unknown | ((previous: unknown) => unknown),\n) => void;\n\ntype FormValuesStore = {\n getPathSnapshot: (path: string) => unknown;\n getPathsSnapshot: (paths: string[]) => Record<string, unknown>;\n getSnapshot: () => Record<string, unknown>;\n replaceInitial: (initial: Record<string, unknown>) => void;\n setValue: SetFormValue;\n subscribe: (listener: () => void) => () => void;\n subscribePath: (path: string, listener: () => void) => () => void;\n subscribePaths: (paths: string[], listener: () => void) => () => void;\n};\n\nconst emptyValues: Record<string, unknown> = {};\nconst emptySelectedValues: Record<string, unknown> = {};\n\nconst FormValuesStoreContext = createContext<FormValuesStore | null>(null);\nconst SetFormValueContext = createContext<SetFormValue>(() => {});\n\nfunction valuesEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) {\n return true;\n }\n\n if (!a || !b || typeof a !== \"object\" || typeof b !== \"object\") {\n return false;\n }\n\n if (Array.isArray(a) || Array.isArray(b)) {\n return (\n Array.isArray(a) &&\n Array.isArray(b) &&\n a.length === b.length &&\n a.every((value, index) => valuesEqual(value, b[index]))\n );\n }\n\n const aEntries = Object.entries(a);\n const bObject = b as Record<string, unknown>;\n\n return (\n aEntries.length === Object.keys(bObject).length &&\n aEntries.every(([key, value]) => valuesEqual(value, bObject[key]))\n );\n}\n\nfunction normalizePath(path: string): string {\n return path\n .split(\".\")\n .filter((part) => part !== \"\")\n .join(\".\");\n}\n\nfunction pathsOverlap(left: string, right: string): boolean {\n return (\n left === \"\" ||\n right === \"\" ||\n left === right ||\n left.startsWith(`${right}.`) ||\n right.startsWith(`${left}.`)\n );\n}\n\nfunction createFormValuesStore(initial: Record<string, unknown>): FormValuesStore {\n let initialValues = initial;\n let values = initial;\n const listeners = new Set<() => void>();\n const pathListeners = new Map<string, Set<() => void>>();\n const selectedCache = new Map<string, { snapshot: Record<string, unknown>; values: unknown[] }>();\n\n const notify = (path: string): void => {\n for (const listener of listeners) {\n listener();\n }\n\n const normalizedPath = normalizePath(path);\n\n for (const [subscribedPath, subscribedListeners] of pathListeners) {\n if (!pathsOverlap(subscribedPath, normalizedPath)) {\n continue;\n }\n\n for (const listener of subscribedListeners) {\n listener();\n }\n }\n };\n\n const updateValues = (next: Record<string, unknown>, path: string): void => {\n if (Object.is(values, next)) {\n return;\n }\n\n values = next;\n selectedCache.clear();\n notify(path);\n };\n\n const subscribePath = (path: string, listener: () => void): (() => void) => {\n const normalizedPath = normalizePath(path);\n const listenersForPath = pathListeners.get(normalizedPath) ?? new Set<() => void>();\n\n listenersForPath.add(listener);\n pathListeners.set(normalizedPath, listenersForPath);\n\n return () => {\n listenersForPath.delete(listener);\n\n if (listenersForPath.size === 0) {\n pathListeners.delete(normalizedPath);\n }\n };\n };\n\n return {\n getPathSnapshot: (path: string): unknown => getPath(values, path),\n getPathsSnapshot: (paths: string[]): Record<string, unknown> => {\n if (paths.length === 0) {\n return emptySelectedValues;\n }\n\n const normalizedPaths = paths.map(normalizePath);\n const key = JSON.stringify(paths);\n const selectedValues = normalizedPaths.map((path) => getPath(values, path));\n const cached = selectedCache.get(key);\n\n if (\n cached &&\n cached.values.length === selectedValues.length &&\n selectedValues.every((value, index) => Object.is(value, cached.values[index]))\n ) {\n return cached.snapshot;\n }\n\n const snapshot = Object.fromEntries(\n paths.map((path, index) => [path, selectedValues[index]]),\n );\n\n selectedCache.set(key, { snapshot, values: selectedValues });\n\n return snapshot;\n },\n getSnapshot: () => values,\n replaceInitial: (nextInitial: Record<string, unknown>): void => {\n if (valuesEqual(initialValues, nextInitial)) {\n return;\n }\n\n initialValues = nextInitial;\n updateValues(nextInitial, \"\");\n },\n setValue: (name: string, value: unknown | ((previous: unknown) => unknown)): void => {\n const previous = getPath(values, name);\n const next = typeof value === \"function\" ? value(previous) : value;\n\n if (Object.is(previous, next)) {\n return;\n }\n\n updateValues(setPath(values, name, next), name);\n },\n subscribe: (listener: () => void): (() => void) => {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n subscribePath,\n subscribePaths: (paths: string[], listener: () => void): (() => void) => {\n const unsubscribers = Array.from(new Set(paths.map(normalizePath))).map((path) =>\n subscribePath(path, listener),\n );\n\n return () => {\n for (const unsubscribe of unsubscribers) {\n unsubscribe();\n }\n };\n },\n };\n}\n\nconst fallbackFormValuesStore = createFormValuesStore(emptyValues);\n\nexport function FormValuesProvider({\n initial,\n children,\n}: {\n initial: Record<string, unknown>;\n children: React.ReactNode;\n}) {\n const storeRef = useRef<FormValuesStore>(null);\n\n if (storeRef.current === null) {\n storeRef.current = createFormValuesStore(initial);\n }\n\n useEffect(() => {\n storeRef.current?.replaceInitial(initial);\n }, [initial]);\n\n return (\n <FormValuesStoreContext.Provider value={storeRef.current}>\n <SetFormValueContext.Provider value={storeRef.current.setValue}>\n {children}\n </SetFormValueContext.Provider>\n </FormValuesStoreContext.Provider>\n );\n}\n\nfunction useFormValuesStore(): FormValuesStore {\n return useContext(FormValuesStoreContext) ?? fallbackFormValuesStore;\n}\n\nexport function useFormValues(): Record<string, unknown> {\n const store = useFormValuesStore();\n\n return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n}\n\nexport function useFormValue(name: string): unknown {\n const store = useFormValuesStore();\n const subscribe = useCallback(\n (listener: () => void) => store.subscribePath(name, listener),\n [name, store],\n );\n const getSnapshot = useCallback(() => store.getPathSnapshot(name), [name, store]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nexport function useFormValuesFor(paths: string[]): Record<string, unknown> {\n const store = useFormValuesStore();\n const pathsKey = JSON.stringify(paths);\n const selectedPaths = useMemo(() => JSON.parse(pathsKey) as string[], [pathsKey]);\n const subscribe = useCallback(\n (listener: () => void) => store.subscribePaths(selectedPaths, listener),\n [selectedPaths, store],\n );\n const getSnapshot = useCallback(\n () => store.getPathsSnapshot(selectedPaths),\n [selectedPaths, store],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nexport function useSetFormValue(): SetFormValue {\n return useContext(SetFormValueContext);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"values.js","names":[],"sources":["../../../resources/js/form/hooks/values.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport { getPath, setPath } from \"@lattice-php/lattice/form/lib/form-path\";\n\nexport type SetFormValue = (\n name: string,\n value: unknown | ((previous: unknown) => unknown),\n) => void;\n\nexport type ResetFormValues = (fields?: string[]) => void;\n\ntype FormValuesStore = {\n getPathSnapshot: (path: string) => unknown;\n getPathsSnapshot: (paths: string[]) => Record<string, unknown>;\n getSnapshot: () => Record<string, unknown>;\n replaceInitial: (initial: Record<string, unknown>) => void;\n reset: ResetFormValues;\n setValue: SetFormValue;\n subscribe: (listener: () => void) => () => void;\n subscribePath: (path: string, listener: () => void) => () => void;\n subscribePaths: (paths: string[], listener: () => void) => () => void;\n};\n\nconst emptyValues: Record<string, unknown> = {};\nconst emptySelectedValues: Record<string, unknown> = {};\n\nconst FormValuesStoreContext = createContext<FormValuesStore | null>(null);\nconst SetFormValueContext = createContext<SetFormValue>(() => {});\n\nfunction valuesEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) {\n return true;\n }\n\n if (!a || !b || typeof a !== \"object\" || typeof b !== \"object\") {\n return false;\n }\n\n if (Array.isArray(a) || Array.isArray(b)) {\n return (\n Array.isArray(a) &&\n Array.isArray(b) &&\n a.length === b.length &&\n a.every((value, index) => valuesEqual(value, b[index]))\n );\n }\n\n const aEntries = Object.entries(a);\n const bObject = b as Record<string, unknown>;\n\n return (\n aEntries.length === Object.keys(bObject).length &&\n aEntries.every(([key, value]) => valuesEqual(value, bObject[key]))\n );\n}\n\nfunction normalizePath(path: string): string {\n return path\n .split(\".\")\n .filter((part) => part !== \"\")\n .join(\".\");\n}\n\nfunction pathsOverlap(left: string, right: string): boolean {\n return (\n left === \"\" ||\n right === \"\" ||\n left === right ||\n left.startsWith(`${right}.`) ||\n right.startsWith(`${left}.`)\n );\n}\n\nfunction createFormValuesStore(initial: Record<string, unknown>): FormValuesStore {\n let initialValues = initial;\n let values = initial;\n const listeners = new Set<() => void>();\n const pathListeners = new Map<string, Set<() => void>>();\n const selectedCache = new Map<string, { snapshot: Record<string, unknown>; values: unknown[] }>();\n\n const notify = (path: string): void => {\n for (const listener of listeners) {\n listener();\n }\n\n const normalizedPath = normalizePath(path);\n\n for (const [subscribedPath, subscribedListeners] of pathListeners) {\n if (!pathsOverlap(subscribedPath, normalizedPath)) {\n continue;\n }\n\n for (const listener of subscribedListeners) {\n listener();\n }\n }\n };\n\n const updateValues = (next: Record<string, unknown>, path: string): void => {\n if (Object.is(values, next)) {\n return;\n }\n\n values = next;\n selectedCache.clear();\n notify(path);\n };\n\n const subscribePath = (path: string, listener: () => void): (() => void) => {\n const normalizedPath = normalizePath(path);\n const listenersForPath = pathListeners.get(normalizedPath) ?? new Set<() => void>();\n\n listenersForPath.add(listener);\n pathListeners.set(normalizedPath, listenersForPath);\n\n return () => {\n listenersForPath.delete(listener);\n\n if (listenersForPath.size === 0) {\n pathListeners.delete(normalizedPath);\n }\n };\n };\n\n return {\n getPathSnapshot: (path: string): unknown => getPath(values, path),\n getPathsSnapshot: (paths: string[]): Record<string, unknown> => {\n if (paths.length === 0) {\n return emptySelectedValues;\n }\n\n const normalizedPaths = paths.map(normalizePath);\n const key = JSON.stringify(paths);\n const selectedValues = normalizedPaths.map((path) => getPath(values, path));\n const cached = selectedCache.get(key);\n\n if (\n cached &&\n cached.values.length === selectedValues.length &&\n selectedValues.every((value, index) => Object.is(value, cached.values[index]))\n ) {\n return cached.snapshot;\n }\n\n const snapshot = Object.fromEntries(\n paths.map((path, index) => [path, selectedValues[index]]),\n );\n\n selectedCache.set(key, { snapshot, values: selectedValues });\n\n return snapshot;\n },\n getSnapshot: () => values,\n replaceInitial: (nextInitial: Record<string, unknown>): void => {\n if (valuesEqual(initialValues, nextInitial)) {\n return;\n }\n\n initialValues = nextInitial;\n updateValues(nextInitial, \"\");\n },\n reset: (fields?: string[]): void => {\n if (fields === undefined) {\n updateValues(initialValues, \"\");\n\n return;\n }\n\n const next = fields.reduce(\n (accumulated, field) => setPath(accumulated, field, getPath(initialValues, field)),\n values,\n );\n\n updateValues(next, \"\");\n },\n setValue: (name: string, value: unknown | ((previous: unknown) => unknown)): void => {\n const previous = getPath(values, name);\n const next = typeof value === \"function\" ? value(previous) : value;\n\n if (Object.is(previous, next)) {\n return;\n }\n\n updateValues(setPath(values, name, next), name);\n },\n subscribe: (listener: () => void): (() => void) => {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n subscribePath,\n subscribePaths: (paths: string[], listener: () => void): (() => void) => {\n const unsubscribers = Array.from(new Set(paths.map(normalizePath))).map((path) =>\n subscribePath(path, listener),\n );\n\n return () => {\n for (const unsubscribe of unsubscribers) {\n unsubscribe();\n }\n };\n },\n };\n}\n\nconst fallbackFormValuesStore = createFormValuesStore(emptyValues);\n\nexport function FormValuesProvider({\n initial,\n children,\n}: {\n initial: Record<string, unknown>;\n children: React.ReactNode;\n}) {\n const storeRef = useRef<FormValuesStore>(null);\n\n if (storeRef.current === null) {\n storeRef.current = createFormValuesStore(initial);\n }\n\n useEffect(() => {\n storeRef.current?.replaceInitial(initial);\n }, [initial]);\n\n return (\n <FormValuesStoreContext.Provider value={storeRef.current}>\n <SetFormValueContext.Provider value={storeRef.current.setValue}>\n {children}\n </SetFormValueContext.Provider>\n </FormValuesStoreContext.Provider>\n );\n}\n\nfunction useFormValuesStore(): FormValuesStore {\n return useContext(FormValuesStoreContext) ?? fallbackFormValuesStore;\n}\n\nexport function useFormValues(): Record<string, unknown> {\n const store = useFormValuesStore();\n\n return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n}\n\nexport function useFormValue(name: string): unknown {\n const store = useFormValuesStore();\n const subscribe = useCallback(\n (listener: () => void) => store.subscribePath(name, listener),\n [name, store],\n );\n const getSnapshot = useCallback(() => store.getPathSnapshot(name), [name, store]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nexport function useFormValuesFor(paths: string[]): Record<string, unknown> {\n const store = useFormValuesStore();\n const pathsKey = JSON.stringify(paths);\n const selectedPaths = useMemo(() => JSON.parse(pathsKey) as string[], [pathsKey]);\n const subscribe = useCallback(\n (listener: () => void) => store.subscribePaths(selectedPaths, listener),\n [selectedPaths, store],\n );\n const getSnapshot = useCallback(\n () => store.getPathsSnapshot(selectedPaths),\n [selectedPaths, store],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nexport function useSetFormValue(): SetFormValue {\n return useContext(SetFormValueContext);\n}\n\n/**\n * Reset values to the store's initial snapshot — all of them, or just the\n * given field paths. This is the reset that actually clears controlled\n * fields; Inertia's own form reset only touches its internal data and the\n * DOM, which store-driven inputs ignore.\n */\nexport function useResetFormValues(): ResetFormValues {\n const store = useFormValuesStore();\n\n return useCallback((fields?: string[]) => store.reset(fields), [store]);\n}\n"],"mappings":"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"}
|
package/dist/i18n/backend.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { setConfig } from "./config.js";
|
|
2
1
|
import { localeHeader } from "./locale.js";
|
|
2
|
+
import { setConfig } from "./config.js";
|
|
3
3
|
import { ensureI18n, i18n, preloadLanguages } from "./instance.js";
|
|
4
4
|
import HttpBackend from "i18next-http-backend";
|
|
5
5
|
//#region resources/js/i18n/backend.ts
|
package/dist/i18n/instance.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useConfig } from "./config.js";
|
|
2
1
|
import { currentLocale, subscribeLocale, useLocale } from "./locale.js";
|
|
2
|
+
import { useConfig } from "./config.js";
|
|
3
3
|
import { useCallback, useSyncExternalStore } from "react";
|
|
4
4
|
import i18next from "i18next";
|
|
5
5
|
//#region resources/js/i18n/instance.ts
|
package/dist/i18n/timezone.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { configTimezone, subscribeConfig } from "./config.js";
|
|
2
1
|
import { LATTICE_EVENT } from "../core/event-names.js";
|
|
2
|
+
import { configTimezone, subscribeConfig } from "./config.js";
|
|
3
3
|
import { useSyncExternalStore } from "react";
|
|
4
4
|
//#region resources/js/i18n/timezone.ts
|
|
5
5
|
var fallback = "UTC";
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { initializeAppearance, updateAppearance, useAppearance } from "./appearance/index.js";
|
|
2
|
-
import { createPlugin, createRegistry, eagerComponent, extendRegistry, lazyComponent } from "./core/registry.js";
|
|
3
|
-
import { useColumnRegistry, useComponentRegistry } from "./core/registry-context.js";
|
|
4
|
-
import { LATTICE_EVENT } from "./core/event-names.js";
|
|
5
2
|
import { LATTICE_REF_HEADER, withRefHeader } from "./core/component-ref.js";
|
|
3
|
+
import { LATTICE_EVENT } from "./core/event-names.js";
|
|
6
4
|
import { withHeaders } from "./core/headers.js";
|
|
5
|
+
import { createPlugin, createRegistry, eagerComponent, extendRegistry, lazyComponent } from "./core/registry.js";
|
|
6
|
+
import { useColumnRegistry, useComponentRegistry } from "./core/registry-context.js";
|
|
7
7
|
import { Icon, SpriteProvider } from "./icons/sprite.js";
|
|
8
8
|
import { IconRenderer, IconRendererProvider } from "./icons/icon-renderer.js";
|
|
9
9
|
import { CollapsedContext, useCollapsed } from "./core/collapsed-context.js";
|