@lattice-php/lattice 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/action/components/action-form.js +15 -10
- package/dist/action/components/action-form.js.map +1 -1
- package/dist/action/components/action-group.js +1 -1
- package/dist/action/components/action.js +2 -1
- package/dist/action/components/action.js.map +1 -1
- package/dist/action/hooks/use-action.js +42 -35
- package/dist/action/hooks/use-action.js.map +1 -1
- package/dist/action/lib/action-label.d.ts +2 -0
- package/dist/action/lib/action-label.js +9 -0
- package/dist/action/lib/action-label.js.map +1 -0
- package/dist/action/lib/run-action.d.ts +7 -7
- package/dist/action/lib/run-action.js +8 -7
- package/dist/action/lib/run-action.js.map +1 -1
- package/dist/chat/components/chat-box.js +1 -1
- package/dist/chat/components/prompt-input.js +1 -1
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +4 -1
- package/dist/core/registry.d.ts +5 -0
- package/dist/core/registry.js.map +1 -1
- package/dist/create-app.d.ts +6 -2
- package/dist/create-app.js +12 -1
- package/dist/create-app.js.map +1 -1
- package/dist/form/components/fields/rich-editor-field.js +1 -1
- package/dist/form/components/fields/row-actions.js +1 -1
- package/dist/form/components/fields/row-item.js +1 -1
- package/dist/form/components/fields/select.js +1 -1
- package/dist/form/components/fields/table-rows.js +1 -1
- package/dist/form/components/form.js +28 -5
- package/dist/form/components/form.js.map +1 -1
- package/dist/form/rich-editor/builtins.js +1 -1
- package/dist/format/number.d.ts +1 -1
- package/dist/format/number.js.map +1 -1
- package/dist/format/value.d.ts +1 -1
- package/dist/format/value.js.map +1 -1
- package/dist/i18n/backend.js +1 -1
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/instance.d.ts +1 -0
- package/dist/i18n/instance.js +5 -5
- package/dist/i18n/instance.js.map +1 -1
- package/dist/i18n/timezone.js +1 -1
- package/dist/layout/components/callouts.js +1 -1
- package/dist/notifications/components/empty-state.js +1 -1
- package/dist/notifications/components/notification-item.js +1 -1
- package/dist/notifications/components/notifications.js +2 -1
- package/dist/notifications/components/notifications.js.map +1 -1
- package/dist/remote/components/data-list.js +1 -1
- package/dist/table/components/bulk-bar.js +15 -14
- package/dist/table/components/bulk-bar.js.map +1 -1
- package/dist/table/components/cells/boolean-cell.js +1 -1
- package/dist/table/components/cells/numeric-cell.d.ts +1 -1
- package/dist/table/components/cells/numeric-cell.js.map +1 -1
- package/dist/table/components/column-header.js +1 -1
- package/dist/table/components/filter-controls.js +1 -1
- package/dist/table/components/filter-value-input.js +1 -1
- package/dist/table/components/sort-bar.js +1 -1
- package/dist/table/components/table-search.js +1 -1
- package/dist/table/components/table.js +1 -1
- package/dist/table/lib/bulk.js +2 -1
- package/dist/table/lib/bulk.js.map +1 -1
- package/dist/toast/index.d.ts +1 -1
- package/dist/toast/index.js +2 -2
- package/dist/toast/toaster.js +1 -1
- package/dist/types/generated.d.ts +4 -1
- package/dist/ui/combobox.js +1 -1
- package/dist/ui/copyable-text.js +1 -1
- package/dist/ui/info-tooltip.js +1 -1
- package/dist/ui/modal.js +3 -1
- package/dist/ui/modal.js.map +1 -1
- package/dist/ui/spinner.js +1 -1
- package/dist/ui/tabs.js +1 -1
- package/dist/ui/tree.js +1 -1
- package/dist/vite.d.ts +3 -2
- package/dist/vite.js +5 -2
- package/dist/vite.js.map +1 -1
- package/dist-standalone/chunks/{chart-view-C6R5iPra.js → chart-view-DEwGTt9d.js} +2 -2
- package/dist-standalone/chunks/{date-picker-field-DhhEB-Mh.js → date-picker-field-CCQ9nWx0.js} +1 -1
- package/dist-standalone/chunks/{page-props-DOfg30l5.js → page-props-D3FnL614.js} +1 -1
- package/dist-standalone/chunks/{rich-editor-field-DORXTHPw.js → rich-editor-field-B9Nv1qia.js} +2 -2
- package/dist-standalone/chunks/{subscriptions-B_ISgA7V.js → subscriptions-C-0lSr7X.js} +1 -1
- package/dist-standalone/chunks/use-effect-dispatcher-EVZxmfoo.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 +4 -4
- package/dist-standalone/chunks/use-effect-dispatcher-D1wPPwHj.js +0 -97
package/README.md
CHANGED
|
@@ -36,6 +36,16 @@ See [Installation](https://latticephp.com/introduction/installation/) for the fu
|
|
|
36
36
|
|
|
37
37
|
Full documentation, guides, and examples live at **[latticephp.com](https://latticephp.com)**.
|
|
38
38
|
|
|
39
|
+
## Generating types for custom wire classes
|
|
40
|
+
|
|
41
|
+
If you define your own Lattice components, fields, columns, filters, or effects in PHP, regenerate the matching TypeScript so the React side stays in lockstep:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
php artisan lattice:typescript
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Projects that use only Lattice's bundled components never need this — the command detects it and no-ops. Generating custom types requires the dev dependency `spatie/laravel-typescript-transformer`.
|
|
48
|
+
|
|
39
49
|
## License
|
|
40
50
|
|
|
41
51
|
The MIT License (MIT). See [LICENSE.md](LICENSE.md) for details.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Renderer } from "../../core/renderer.js";
|
|
2
1
|
import { useT } from "../../i18n/instance.js";
|
|
3
|
-
import {
|
|
2
|
+
import { Renderer } from "../../core/renderer.js";
|
|
3
|
+
import { dispatchActionError, getActionEffects } from "../../effects/dispatch.js";
|
|
4
|
+
import { useEffectDispatcher } from "../../effects/use-effect-dispatcher.js";
|
|
4
5
|
import { Button } from "../../ui/button.js";
|
|
5
6
|
import { Dialog, DialogContent, DialogHeader } from "../../ui/dialog.js";
|
|
6
7
|
import { Spinner } from "../../ui/spinner.js";
|
|
@@ -63,6 +64,7 @@ function ActionFormBody({ cancelLabel, componentRef, endpoint, extraData, fieldL
|
|
|
63
64
|
const extraDataRef = useRef(extraData);
|
|
64
65
|
extraDataRef.current = extraData;
|
|
65
66
|
const { nodes: resolvedNodes, markUserEdit } = useFormResolver(endpoint, componentRef, formNode.schema);
|
|
67
|
+
const dispatch = useEffectDispatcher();
|
|
66
68
|
const [errors, setErrors] = useState({});
|
|
67
69
|
const [processing, setProcessing] = useState(false);
|
|
68
70
|
const request = useCallback((extraHeaders) => apiFetch(endpoint, {
|
|
@@ -107,17 +109,20 @@ function ActionFormBody({ cancelLabel, componentRef, endpoint, extraData, fieldL
|
|
|
107
109
|
const submit = useCallback(() => {
|
|
108
110
|
setProcessing(true);
|
|
109
111
|
request().then(async (response) => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
const body = await response.json().catch(() => ({}));
|
|
113
|
+
dispatch(getActionEffects(body.effects));
|
|
114
|
+
if (response.status === 422 && body.errors) {
|
|
115
|
+
setErrors(firstErrors(body.errors));
|
|
112
116
|
return;
|
|
113
117
|
}
|
|
114
|
-
if (!response.ok)
|
|
115
|
-
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
onSuccess(await response.json());
|
|
118
|
+
if (!response.ok) return;
|
|
119
|
+
onSuccess(body);
|
|
119
120
|
}).catch((error) => dispatchActionError(error)).finally(() => setProcessing(false));
|
|
120
|
-
}, [
|
|
121
|
+
}, [
|
|
122
|
+
dispatch,
|
|
123
|
+
onSuccess,
|
|
124
|
+
request
|
|
125
|
+
]);
|
|
121
126
|
return /* @__PURE__ */ jsx(FormProvider, {
|
|
122
127
|
value: useMemo(() => ({
|
|
123
128
|
action: endpoint,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-form.js","names":[],"sources":["../../../resources/js/action/components/action-form.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { apiFetch } from \"@lattice-php/lattice/core/api\";\nimport { Button } from \"@lattice-php/lattice/ui/button\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n type DialogPlacement,\n} from \"@lattice-php/lattice/ui/dialog\";\nimport { Skeleton } from \"@lattice-php/lattice/ui/skeleton\";\nimport { Spinner } from \"@lattice-php/lattice/ui/spinner\";\nimport { Renderer } from \"@lattice-php/lattice/core/renderer\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport type { ModalWidth } from \"@lattice-php/lattice/types/generated\";\nimport {\n collectFields,\n FORM_DEBOUNCE_MS,\n FormProvider,\n FormValuesProvider,\n firstErrors,\n PrefillProvider,\n ResolvedNodesProvider,\n useFormResolver,\n useFormValues,\n} from \"@lattice-php/lattice/form/embed\";\nimport type { FieldErrors } from \"@lattice-php/lattice/form/embed\";\nimport { useDebouncedCallback } from \"@lattice-php/lattice/lib/use-debounced-callback\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { dispatchActionError } from \"@lattice-php/lattice/effects/dispatch\";\nimport type { ActionResponse } from \"@lattice-php/lattice/effects/dispatch\";\n\ntype ActionFormProps = {\n cancelLabel: string;\n componentRef: string;\n description?: string;\n endpoint: string;\n /** Extra payload merged into every request, e.g. a bulk action's selection. */\n extraData?: Record<string, unknown>;\n /** The form to render; null while a lazy schema is still being fetched. */\n formNode: Node | null;\n method: string;\n onClose: () => void;\n onSuccess: (response: ActionResponse) => void;\n /** Dialog placement for the form modal; sheets dock to a viewport edge. */\n placement?: DialogPlacement;\n submitLabel: string;\n title: string;\n width?: ModalWidth;\n};\n\n/**\n * Fetch a lazily-served form schema from the action endpoint while `enabled`,\n * so it can be prefilled per record. Returns null until it arrives.\n */\nexport function useLazyActionForm(\n endpoint: string,\n componentRef: string,\n enabled: boolean,\n): Node | null {\n const [node, setNode] = useState<Node | null>(null);\n\n useEffect(() => {\n if (!enabled) {\n setNode(null);\n\n return;\n }\n\n const controller = new AbortController();\n\n void apiFetch(endpoint, {\n body: JSON.stringify({ _form: true }),\n ref: componentRef,\n method: \"POST\",\n signal: controller.signal,\n throwOnError: false,\n })\n .then((response) => (response.ok ? (response.json() as Promise<Node>) : null))\n .then((fetched) => setNode(fetched))\n .catch(() => {});\n\n return () => controller.abort();\n }, [enabled, endpoint, componentRef]);\n\n return node;\n}\n\nfunction ActionFormSkeleton() {\n return (\n <div className=\"space-y-4\" data-lattice-action-form-loading>\n <Skeleton className=\"h-4 w-24\" />\n <Skeleton className=\"h-10 w-full\" />\n <Skeleton className=\"h-10 w-full\" />\n </div>\n );\n}\n\nfunction ActionFormBody({\n cancelLabel,\n componentRef,\n endpoint,\n extraData,\n fieldLabels,\n formNode,\n method,\n onClose,\n onSuccess,\n precognitive,\n submitLabel,\n}: Omit<ActionFormProps, \"description\" | \"title\"> & {\n fieldLabels: Record<string, string>;\n formNode: Node;\n precognitive: boolean;\n}) {\n const values = useFormValues();\n const valuesRef = useRef(values);\n valuesRef.current = values;\n const extraDataRef = useRef(extraData);\n extraDataRef.current = extraData;\n const { nodes: resolvedNodes, markUserEdit } = useFormResolver(\n endpoint,\n componentRef,\n formNode.schema,\n );\n\n const [errors, setErrors] = useState<FieldErrors>({});\n const [processing, setProcessing] = useState(false);\n\n const request = useCallback(\n (extraHeaders?: Record<string, string>): Promise<Response> =>\n apiFetch(endpoint, {\n body: JSON.stringify({ ...valuesRef.current, ...extraDataRef.current }),\n method,\n ref: componentRef,\n headers: extraHeaders,\n throwOnError: false,\n }),\n [componentRef, endpoint, method],\n );\n\n const clearErrors = useCallback((field: string) => {\n setErrors((current) =>\n current[field] === undefined ? current : { ...current, [field]: undefined },\n );\n }, []);\n\n const runValidation = useDebouncedCallback((field: string) => {\n void request({ Precognition: \"true\", \"Precognition-Validate-Only\": field })\n .then(async (response) => {\n if (response.status === 422) {\n const body = (await response.json()) as { errors?: Record<string, string[]> };\n setErrors((current) => ({ ...current, ...firstErrors(body.errors) }));\n\n return;\n }\n\n clearErrors(field);\n })\n .catch(() => {});\n }, FORM_DEBOUNCE_MS);\n\n const validate = useCallback(\n (field: string) => {\n if (precognitive) {\n runValidation(field);\n }\n },\n [precognitive, runValidation],\n );\n\n const submit = useCallback(() => {\n setProcessing(true);\n\n void request()\n .then(async (response) => {\n if (response.status === 422) {\n const body = (await response.json()) as { errors?: Record<string, string[]> };\n setErrors(firstErrors(body.errors));\n\n return;\n }\n\n if (!response.ok) {\n dispatchActionError(new Error(`Action request failed with status ${response.status}`));\n\n return;\n }\n\n onSuccess((await response.json()) as ActionResponse);\n })\n .catch((error: unknown) => dispatchActionError(error))\n .finally(() => setProcessing(false));\n }, [onSuccess, request]);\n\n const context = useMemo(\n () => ({\n action: endpoint,\n clearErrors,\n componentRef,\n errors,\n fieldLabels,\n precognitive,\n processing,\n validate,\n }),\n [clearErrors, componentRef, endpoint, errors, fieldLabels, precognitive, processing, validate],\n );\n\n return (\n <FormProvider value={context}>\n <form\n className=\"flex flex-col gap-6\"\n onSubmit={(event) => {\n event.preventDefault();\n submit();\n }}\n >\n <PrefillProvider value={{ markUserEdit }}>\n <ResolvedNodesProvider nodes={resolvedNodes}>\n <Renderer nodes={formNode.schema ?? []} />\n </ResolvedNodesProvider>\n </PrefillProvider>\n\n <div className=\"flex justify-end gap-3\">\n <Button\n data-test=\"action-form-cancel\"\n disabled={processing}\n onClick={onClose}\n type=\"button\"\n variant=\"ghost\"\n >\n {cancelLabel}\n </Button>\n\n <Button data-test=\"action-form-submit\" disabled={processing} type=\"submit\">\n {processing && <Spinner />}\n {submitLabel}\n </Button>\n </div>\n </form>\n </FormProvider>\n );\n}\n\nfunction ActionFormContent({\n formNode,\n ...rest\n}: Omit<ActionFormProps, \"description\" | \"title\"> & { formNode: Node }) {\n const precognitive = Boolean(formNode.props?.precognitive);\n const { labels: fieldLabels, values: initialValues } = useMemo(() => {\n const { labels, values } = collectFields(formNode.schema);\n\n return {\n labels,\n values: { ...values, ...(formNode.props?.state as Record<string, unknown> | undefined) },\n };\n }, [formNode]);\n\n return (\n <FormValuesProvider initial={initialValues}>\n <ActionFormBody\n fieldLabels={fieldLabels}\n formNode={formNode}\n precognitive={precognitive}\n {...rest}\n />\n </FormValuesProvider>\n );\n}\n\nexport function ActionForm({\n description,\n formNode,\n onClose,\n placement,\n title,\n width,\n ...rest\n}: ActionFormProps) {\n const { t } = useT(\"lattice\");\n\n return (\n <Dialog\n open\n onOpenChange={(open) => {\n if (!open) {\n onClose();\n }\n }}\n >\n <DialogContent\n {...(description ? {} : { \"aria-describedby\": undefined })}\n placement={placement}\n width={width}\n >\n <DialogHeader\n closeLabel={t(\"common.close\", \"Close\")}\n description={description}\n title={title}\n />\n\n <div className=\"mt-6\">\n {formNode ? (\n <ActionFormContent formNode={formNode} onClose={onClose} {...rest} />\n ) : (\n <ActionFormSkeleton />\n )}\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"action-form.js","names":[],"sources":["../../../resources/js/action/components/action-form.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { apiFetch } from \"@lattice-php/lattice/core/api\";\nimport { Button } from \"@lattice-php/lattice/ui/button\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n type DialogPlacement,\n} from \"@lattice-php/lattice/ui/dialog\";\nimport { Skeleton } from \"@lattice-php/lattice/ui/skeleton\";\nimport { Spinner } from \"@lattice-php/lattice/ui/spinner\";\nimport { Renderer } from \"@lattice-php/lattice/core/renderer\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport type { ModalWidth } from \"@lattice-php/lattice/types/generated\";\nimport {\n collectFields,\n FORM_DEBOUNCE_MS,\n FormProvider,\n FormValuesProvider,\n firstErrors,\n PrefillProvider,\n ResolvedNodesProvider,\n useFormResolver,\n useFormValues,\n} from \"@lattice-php/lattice/form/embed\";\nimport type { FieldErrors } from \"@lattice-php/lattice/form/embed\";\nimport { useDebouncedCallback } from \"@lattice-php/lattice/lib/use-debounced-callback\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { dispatchActionError, getActionEffects } from \"@lattice-php/lattice/effects/dispatch\";\nimport type { ActionResponse } from \"@lattice-php/lattice/effects/dispatch\";\nimport { useEffectDispatcher } from \"@lattice-php/lattice/effects/use-effect-dispatcher\";\n\ntype ActionFormProps = {\n cancelLabel: string;\n componentRef: string;\n description?: string;\n endpoint: string;\n /** Extra payload merged into every request, e.g. a bulk action's selection. */\n extraData?: Record<string, unknown>;\n /** The form to render; null while a lazy schema is still being fetched. */\n formNode: Node | null;\n method: string;\n onClose: () => void;\n onSuccess: (response: ActionResponse) => void;\n /** Dialog placement for the form modal; sheets dock to a viewport edge. */\n placement?: DialogPlacement;\n submitLabel: string;\n title: string;\n width?: ModalWidth;\n};\n\n/**\n * Fetch a lazily-served form schema from the action endpoint while `enabled`,\n * so it can be prefilled per record. Returns null until it arrives.\n */\nexport function useLazyActionForm(\n endpoint: string,\n componentRef: string,\n enabled: boolean,\n): Node | null {\n const [node, setNode] = useState<Node | null>(null);\n\n useEffect(() => {\n if (!enabled) {\n setNode(null);\n\n return;\n }\n\n const controller = new AbortController();\n\n void apiFetch(endpoint, {\n body: JSON.stringify({ _form: true }),\n ref: componentRef,\n method: \"POST\",\n signal: controller.signal,\n throwOnError: false,\n })\n .then((response) => (response.ok ? (response.json() as Promise<Node>) : null))\n .then((fetched) => setNode(fetched))\n .catch(() => {});\n\n return () => controller.abort();\n }, [enabled, endpoint, componentRef]);\n\n return node;\n}\n\nfunction ActionFormSkeleton() {\n return (\n <div className=\"space-y-4\" data-lattice-action-form-loading>\n <Skeleton className=\"h-4 w-24\" />\n <Skeleton className=\"h-10 w-full\" />\n <Skeleton className=\"h-10 w-full\" />\n </div>\n );\n}\n\nfunction ActionFormBody({\n cancelLabel,\n componentRef,\n endpoint,\n extraData,\n fieldLabels,\n formNode,\n method,\n onClose,\n onSuccess,\n precognitive,\n submitLabel,\n}: Omit<ActionFormProps, \"description\" | \"title\"> & {\n fieldLabels: Record<string, string>;\n formNode: Node;\n precognitive: boolean;\n}) {\n const values = useFormValues();\n const valuesRef = useRef(values);\n valuesRef.current = values;\n const extraDataRef = useRef(extraData);\n extraDataRef.current = extraData;\n const { nodes: resolvedNodes, markUserEdit } = useFormResolver(\n endpoint,\n componentRef,\n formNode.schema,\n );\n\n const dispatch = useEffectDispatcher();\n const [errors, setErrors] = useState<FieldErrors>({});\n const [processing, setProcessing] = useState(false);\n\n const request = useCallback(\n (extraHeaders?: Record<string, string>): Promise<Response> =>\n apiFetch(endpoint, {\n body: JSON.stringify({ ...valuesRef.current, ...extraDataRef.current }),\n method,\n ref: componentRef,\n headers: extraHeaders,\n throwOnError: false,\n }),\n [componentRef, endpoint, method],\n );\n\n const clearErrors = useCallback((field: string) => {\n setErrors((current) =>\n current[field] === undefined ? current : { ...current, [field]: undefined },\n );\n }, []);\n\n const runValidation = useDebouncedCallback((field: string) => {\n void request({ Precognition: \"true\", \"Precognition-Validate-Only\": field })\n .then(async (response) => {\n if (response.status === 422) {\n const body = (await response.json()) as { errors?: Record<string, string[]> };\n setErrors((current) => ({ ...current, ...firstErrors(body.errors) }));\n\n return;\n }\n\n clearErrors(field);\n })\n .catch(() => {});\n }, FORM_DEBOUNCE_MS);\n\n const validate = useCallback(\n (field: string) => {\n if (precognitive) {\n runValidation(field);\n }\n },\n [precognitive, runValidation],\n );\n\n const submit = useCallback(() => {\n setProcessing(true);\n\n void request()\n .then(async (response) => {\n const body = (await response.json().catch(() => ({}))) as ActionResponse & {\n errors?: Record<string, string[]>;\n };\n\n dispatch(getActionEffects(body.effects));\n\n if (response.status === 422 && body.errors) {\n setErrors(firstErrors(body.errors));\n\n return;\n }\n\n if (!response.ok) {\n return;\n }\n\n onSuccess(body);\n })\n .catch((error: unknown) => dispatchActionError(error))\n .finally(() => setProcessing(false));\n }, [dispatch, onSuccess, request]);\n\n const context = useMemo(\n () => ({\n action: endpoint,\n clearErrors,\n componentRef,\n errors,\n fieldLabels,\n precognitive,\n processing,\n validate,\n }),\n [clearErrors, componentRef, endpoint, errors, fieldLabels, precognitive, processing, validate],\n );\n\n return (\n <FormProvider value={context}>\n <form\n className=\"flex flex-col gap-6\"\n onSubmit={(event) => {\n event.preventDefault();\n submit();\n }}\n >\n <PrefillProvider value={{ markUserEdit }}>\n <ResolvedNodesProvider nodes={resolvedNodes}>\n <Renderer nodes={formNode.schema ?? []} />\n </ResolvedNodesProvider>\n </PrefillProvider>\n\n <div className=\"flex justify-end gap-3\">\n <Button\n data-test=\"action-form-cancel\"\n disabled={processing}\n onClick={onClose}\n type=\"button\"\n variant=\"ghost\"\n >\n {cancelLabel}\n </Button>\n\n <Button data-test=\"action-form-submit\" disabled={processing} type=\"submit\">\n {processing && <Spinner />}\n {submitLabel}\n </Button>\n </div>\n </form>\n </FormProvider>\n );\n}\n\nfunction ActionFormContent({\n formNode,\n ...rest\n}: Omit<ActionFormProps, \"description\" | \"title\"> & { formNode: Node }) {\n const precognitive = Boolean(formNode.props?.precognitive);\n const { labels: fieldLabels, values: initialValues } = useMemo(() => {\n const { labels, values } = collectFields(formNode.schema);\n\n return {\n labels,\n values: { ...values, ...(formNode.props?.state as Record<string, unknown> | undefined) },\n };\n }, [formNode]);\n\n return (\n <FormValuesProvider initial={initialValues}>\n <ActionFormBody\n fieldLabels={fieldLabels}\n formNode={formNode}\n precognitive={precognitive}\n {...rest}\n />\n </FormValuesProvider>\n );\n}\n\nexport function ActionForm({\n description,\n formNode,\n onClose,\n placement,\n title,\n width,\n ...rest\n}: ActionFormProps) {\n const { t } = useT(\"lattice\");\n\n return (\n <Dialog\n open\n onOpenChange={(open) => {\n if (!open) {\n onClose();\n }\n }}\n >\n <DialogContent\n {...(description ? {} : { \"aria-describedby\": undefined })}\n placement={placement}\n width={width}\n >\n <DialogHeader\n closeLabel={t(\"common.close\", \"Close\")}\n description={description}\n title={title}\n />\n\n <div className=\"mt-6\">\n {formNode ? (\n <ActionFormContent formNode={formNode} onClose={onClose} {...rest} />\n ) : (\n <ActionFormSkeleton />\n )}\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { useT } from "../../i18n/instance.js";
|
|
1
2
|
import { cn } from "../../lib/utils.js";
|
|
2
3
|
import { Icon } from "../../icons/sprite.js";
|
|
3
|
-
import { useT } from "../../i18n/instance.js";
|
|
4
4
|
import { nodeIdentity } from "../../core/test-id.js";
|
|
5
5
|
import { Button } from "../../ui/button.js";
|
|
6
6
|
import { ActionMenuProvider } from "../../ui/action-menu-context.js";
|
|
@@ -2,6 +2,7 @@ import { IconRenderer } from "../../icons/icon-renderer.js";
|
|
|
2
2
|
import { prefixedTestId } from "../../core/test-id.js";
|
|
3
3
|
import { Button } from "../../ui/button.js";
|
|
4
4
|
import { Spinner } from "../../ui/spinner.js";
|
|
5
|
+
import { actionLabel } from "../lib/action-label.js";
|
|
5
6
|
import { useAction } from "../hooks/use-action.js";
|
|
6
7
|
import { actionMenuItemClassName, useActionMenu } from "../../ui/action-menu-context.js";
|
|
7
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -9,7 +10,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
9
10
|
var ActionComponent = ({ node }) => {
|
|
10
11
|
const endpoint = node.props.endpoint ?? "";
|
|
11
12
|
const icon = node.props.icon;
|
|
12
|
-
const label = node
|
|
13
|
+
const label = actionLabel(node);
|
|
13
14
|
const isMenuItem = useActionMenu();
|
|
14
15
|
const variant = node.props.variant ?? "default";
|
|
15
16
|
const { processing, requestSubmit, overlays } = useAction(node);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action.js","names":[],"sources":["../../../resources/js/action/components/action.tsx"],"sourcesContent":["import { Button } from \"@lattice-php/lattice/ui/button\";\nimport { Spinner } from \"@lattice-php/lattice/ui/spinner\";\nimport { prefixedTestId } from \"@lattice-php/lattice/core/test-id\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { IconRenderer } from \"@lattice-php/lattice/icons\";\nimport {\n actionMenuItemClassName,\n useActionMenu,\n} from \"@lattice-php/lattice/ui/action-menu-context\";\nimport { useAction } from \"@lattice-php/lattice/action/hooks/use-action\";\n\nconst ActionComponent: RendererComponent<\"action\"> = ({ node }) => {\n const endpoint = node.props.endpoint ?? \"\";\n const icon = node.props.icon;\n const label = node
|
|
1
|
+
{"version":3,"file":"action.js","names":[],"sources":["../../../resources/js/action/components/action.tsx"],"sourcesContent":["import { Button } from \"@lattice-php/lattice/ui/button\";\nimport { Spinner } from \"@lattice-php/lattice/ui/spinner\";\nimport { prefixedTestId } from \"@lattice-php/lattice/core/test-id\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { IconRenderer } from \"@lattice-php/lattice/icons\";\nimport {\n actionMenuItemClassName,\n useActionMenu,\n} from \"@lattice-php/lattice/ui/action-menu-context\";\nimport { useAction } from \"@lattice-php/lattice/action/hooks/use-action\";\nimport { actionLabel } from \"@lattice-php/lattice/action/lib/action-label\";\n\nconst ActionComponent: RendererComponent<\"action\"> = ({ node }) => {\n const endpoint = node.props.endpoint ?? \"\";\n const icon = node.props.icon;\n const label = actionLabel(node);\n const isMenuItem = useActionMenu();\n const variant = node.props.variant ?? \"default\";\n const { processing, requestSubmit, overlays } = useAction(node);\n const testId = node.key ?? prefixedTestId(\"action\", node.id);\n\n return (\n <>\n <Button\n className={isMenuItem ? actionMenuItemClassName : undefined}\n data-lattice-component={node.id}\n data-test={testId}\n disabled={processing || !endpoint}\n onClick={requestSubmit}\n type=\"button\"\n variant={isMenuItem ? \"ghost\" : variant}\n >\n {processing ? (\n <Spinner className={isMenuItem ? \"size-lt-icon-sm\" : undefined} />\n ) : (\n icon && (\n <IconRenderer\n className={isMenuItem ? \"size-lt-icon-sm\" : \"size-lt-icon-md\"}\n icon={icon}\n />\n )\n )}\n {label}\n </Button>\n\n {overlays}\n </>\n );\n};\n\nexport default ActionComponent;\n"],"mappings":";;;;;;;;;AAYA,IAAM,mBAAgD,EAAE,WAAW;CACjE,MAAM,WAAW,KAAK,MAAM,YAAY;CACxC,MAAM,OAAO,KAAK,MAAM;CACxB,MAAM,QAAQ,YAAY,IAAI;CAC9B,MAAM,aAAa,cAAc;CACjC,MAAM,UAAU,KAAK,MAAM,WAAW;CACtC,MAAM,EAAE,YAAY,eAAe,aAAa,UAAU,IAAI;CAC9D,MAAM,SAAS,KAAK,OAAO,eAAe,UAAU,KAAK,EAAE;CAE3D,OACE,qBAAA,UAAA,EAAA,UAAA,CACE,qBAAC,QAAD;EACE,WAAW,aAAa,0BAA0B,KAAA;EAClD,0BAAwB,KAAK;EAC7B,aAAW;EACX,UAAU,cAAc,CAAC;EACzB,SAAS;EACT,MAAK;EACL,SAAS,aAAa,UAAU;YAPlC,CASG,aACC,oBAAC,SAAD,EAAS,WAAW,aAAa,oBAAoB,KAAA,EAAY,CAAA,IAEjE,QACE,oBAAC,cAAD;GACE,WAAW,aAAa,oBAAoB;GACtC;EACP,CAAA,GAGJ,KACK;KAEP,QACD,EAAA,CAAA;AAEN"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { translate } from "../../i18n/instance.js";
|
|
1
2
|
import { withHeaders } from "../../core/headers.js";
|
|
2
|
-
import { getActionEffects } from "../../effects/dispatch.js";
|
|
3
3
|
import { useEffectDispatcher } from "../../effects/use-effect-dispatcher.js";
|
|
4
4
|
import { ConfirmDialog } from "../../ui/confirm-dialog.js";
|
|
5
|
+
import { apiFetch } from "../../core/api.js";
|
|
5
6
|
import { runAction } from "../lib/run-action.js";
|
|
6
7
|
import { ActionForm, useLazyActionForm } from "../components/action-form.js";
|
|
7
|
-
import {
|
|
8
|
+
import { actionLabel } from "../lib/action-label.js";
|
|
9
|
+
import { router } from "@inertiajs/react";
|
|
8
10
|
import { useState } from "react";
|
|
9
11
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
10
12
|
//#region resources/js/action/hooks/use-action.tsx
|
|
@@ -18,13 +20,13 @@ function useAction(node) {
|
|
|
18
20
|
const endpoint = node.props.endpoint ?? "";
|
|
19
21
|
const componentRef = node.props.ref ?? "";
|
|
20
22
|
const method = node.props.method ?? "post";
|
|
21
|
-
const label = node
|
|
23
|
+
const label = actionLabel(node);
|
|
22
24
|
const variant = node.props.variant ?? "default";
|
|
23
25
|
const confirmation = node.props.confirmation;
|
|
24
26
|
const inlineForm = node.props.form;
|
|
25
27
|
const lazyForm = node.props.lazyForm === true;
|
|
26
28
|
const hasForm = Boolean(inlineForm) || lazyForm;
|
|
27
|
-
const
|
|
29
|
+
const [processing, setProcessing] = useState(false);
|
|
28
30
|
const dispatch = useEffectDispatcher();
|
|
29
31
|
const [isConfirming, setIsConfirming] = useState(false);
|
|
30
32
|
const [isFilling, setIsFilling] = useState(false);
|
|
@@ -37,7 +39,14 @@ function useAction(node) {
|
|
|
37
39
|
setIsConfirming(false);
|
|
38
40
|
return;
|
|
39
41
|
}
|
|
40
|
-
|
|
42
|
+
setProcessing(true);
|
|
43
|
+
const ok = await runAction(() => apiFetch(endpoint, {
|
|
44
|
+
method,
|
|
45
|
+
ref: componentRef,
|
|
46
|
+
throwOnError: false
|
|
47
|
+
}), dispatch);
|
|
48
|
+
setProcessing(false);
|
|
49
|
+
if (ok) setIsConfirming(false);
|
|
41
50
|
};
|
|
42
51
|
const requestSubmit = () => {
|
|
43
52
|
if (hasForm) {
|
|
@@ -52,38 +61,36 @@ function useAction(node) {
|
|
|
52
61
|
};
|
|
53
62
|
const confirmationTitle = confirmation?.title ?? label;
|
|
54
63
|
const confirmationConfirmLabel = confirmation?.confirmLabel ?? label;
|
|
55
|
-
const confirmationCancelLabel = confirmation?.cancelLabel ?? "Cancel";
|
|
56
|
-
const overlays = /* @__PURE__ */ jsxs(Fragment$1, { children: [isConfirming && confirmation && /* @__PURE__ */ jsx(ConfirmDialog, {
|
|
57
|
-
title: confirmationTitle,
|
|
58
|
-
description: confirmation.description ?? void 0,
|
|
59
|
-
confirmLabel: confirmationConfirmLabel,
|
|
60
|
-
cancelLabel: confirmationCancelLabel,
|
|
61
|
-
confirmVariant: variant,
|
|
62
|
-
processing: http.processing,
|
|
63
|
-
confirmDisabled: !endpoint,
|
|
64
|
-
onConfirm: () => void submit(),
|
|
65
|
-
onCancel: () => setIsConfirming(false)
|
|
66
|
-
}), isFilling && hasForm && /* @__PURE__ */ jsx(ActionForm, {
|
|
67
|
-
cancelLabel: confirmationCancelLabel,
|
|
68
|
-
componentRef,
|
|
69
|
-
description: confirmation?.description ?? void 0,
|
|
70
|
-
endpoint,
|
|
71
|
-
formNode,
|
|
72
|
-
method,
|
|
73
|
-
onClose: () => setIsFilling(false),
|
|
74
|
-
onSuccess: (response) => {
|
|
75
|
-
dispatch(getActionEffects(response.effects));
|
|
76
|
-
setIsFilling(false);
|
|
77
|
-
},
|
|
78
|
-
placement: node.props.modalSide ?? "center",
|
|
79
|
-
submitLabel: confirmationConfirmLabel,
|
|
80
|
-
title: confirmationTitle,
|
|
81
|
-
width: node.props.modalWidth ?? void 0
|
|
82
|
-
})] });
|
|
64
|
+
const confirmationCancelLabel = confirmation?.cancelLabel ?? translate("lattice", "common.cancel", "Cancel");
|
|
83
65
|
return {
|
|
84
|
-
processing
|
|
66
|
+
processing,
|
|
85
67
|
requestSubmit,
|
|
86
|
-
overlays
|
|
68
|
+
overlays: /* @__PURE__ */ jsxs(Fragment$1, { children: [isConfirming && confirmation && /* @__PURE__ */ jsx(ConfirmDialog, {
|
|
69
|
+
title: confirmationTitle,
|
|
70
|
+
description: confirmation.description ?? void 0,
|
|
71
|
+
confirmLabel: confirmationConfirmLabel,
|
|
72
|
+
cancelLabel: confirmationCancelLabel,
|
|
73
|
+
confirmVariant: variant,
|
|
74
|
+
processing,
|
|
75
|
+
confirmDisabled: !endpoint,
|
|
76
|
+
onConfirm: () => void submit(),
|
|
77
|
+
onCancel: () => setIsConfirming(false)
|
|
78
|
+
}), isFilling && hasForm && /* @__PURE__ */ jsx(ActionForm, {
|
|
79
|
+
cancelLabel: confirmationCancelLabel,
|
|
80
|
+
componentRef,
|
|
81
|
+
description: confirmation?.description ?? void 0,
|
|
82
|
+
endpoint,
|
|
83
|
+
formNode,
|
|
84
|
+
method,
|
|
85
|
+
onClose: () => setIsFilling(false),
|
|
86
|
+
onSuccess: () => {
|
|
87
|
+
setIsFilling(false);
|
|
88
|
+
},
|
|
89
|
+
placement: node.props.modalSide ?? "center",
|
|
90
|
+
submitLabel: confirmationConfirmLabel,
|
|
91
|
+
title: confirmationTitle,
|
|
92
|
+
width: node.props.modalWidth ?? void 0
|
|
93
|
+
})] })
|
|
87
94
|
};
|
|
88
95
|
}
|
|
89
96
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-action.js","names":[],"sources":["../../../resources/js/action/hooks/use-action.tsx"],"sourcesContent":["import { router
|
|
1
|
+
{"version":3,"file":"use-action.js","names":[],"sources":["../../../resources/js/action/hooks/use-action.tsx"],"sourcesContent":["import { router } from \"@inertiajs/react\";\nimport type { Method } from \"@inertiajs/core\";\nimport { useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { ConfirmDialog } from \"@lattice-php/lattice/ui/confirm-dialog\";\nimport { apiFetch } from \"@lattice-php/lattice/core/api\";\nimport { withHeaders } from \"@lattice-php/lattice/core/headers\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport { translate } from \"@lattice-php/lattice/i18n\";\nimport { useEffectDispatcher } from \"@lattice-php/lattice/effects/use-effect-dispatcher\";\nimport { runAction } from \"@lattice-php/lattice/action/lib/run-action\";\nimport { ActionForm, useLazyActionForm } from \"@lattice-php/lattice/action/components/action-form\";\nimport { actionLabel } from \"@lattice-php/lattice/action/lib/action-label\";\n\ntype UseAction = {\n /** Whether the action request is in flight. */\n processing: boolean;\n /** Gate then run the action: open the form, confirm, or dispatch directly. */\n requestSubmit: () => void;\n /** The confirm dialog and action form rendered next to the trigger. */\n overlays: ReactNode;\n};\n\n/**\n * The shared action machinery behind the Action button, action menu items, and\n * action links: it gates submission (form → modal, confirmation → confirm,\n * otherwise dispatch) and renders the matching overlays. The host owns the\n * trigger element so each surface keeps its own styling.\n */\nexport function useAction(node: Node<\"action\" | \"action.bulk\">): UseAction {\n const endpoint = node.props.endpoint ?? \"\";\n const componentRef = node.props.ref ?? \"\";\n const method: Method = node.props.method ?? \"post\";\n const label = actionLabel(node);\n const variant = node.props.variant ?? \"default\";\n const confirmation = node.props.confirmation;\n const inlineForm = node.props.form;\n const lazyForm = node.props.lazyForm === true;\n const hasForm = Boolean(inlineForm) || lazyForm;\n\n const [processing, setProcessing] = useState(false);\n const dispatch = useEffectDispatcher();\n const [isConfirming, setIsConfirming] = useState(false);\n const [isFilling, setIsFilling] = useState(false);\n const lazyNode = useLazyActionForm(endpoint, componentRef, isFilling && lazyForm);\n const formNode = lazyForm ? lazyNode : inlineForm;\n\n const submit = async (): Promise<void> => {\n if (!endpoint) {\n return;\n }\n\n if (method === \"get\") {\n router.visit(endpoint, { headers: withHeaders(componentRef) });\n setIsConfirming(false);\n\n return;\n }\n\n setProcessing(true);\n\n const ok = await runAction(\n () => apiFetch(endpoint, { method, ref: componentRef, throwOnError: false }),\n dispatch,\n );\n\n setProcessing(false);\n\n if (ok) {\n setIsConfirming(false);\n }\n };\n\n const requestSubmit = (): void => {\n if (hasForm) {\n setIsFilling(true);\n\n return;\n }\n\n if (confirmation) {\n setIsConfirming(true);\n\n return;\n }\n\n void submit();\n };\n\n const confirmationTitle = confirmation?.title ?? label;\n const confirmationConfirmLabel = confirmation?.confirmLabel ?? label;\n const confirmationCancelLabel =\n confirmation?.cancelLabel ?? translate(\"lattice\", \"common.cancel\", \"Cancel\");\n\n const overlays = (\n <>\n {isConfirming && confirmation && (\n <ConfirmDialog\n title={confirmationTitle}\n description={confirmation.description ?? undefined}\n confirmLabel={confirmationConfirmLabel}\n cancelLabel={confirmationCancelLabel}\n confirmVariant={variant}\n processing={processing}\n confirmDisabled={!endpoint}\n onConfirm={() => void submit()}\n onCancel={() => setIsConfirming(false)}\n />\n )}\n\n {isFilling && hasForm && (\n <ActionForm\n cancelLabel={confirmationCancelLabel}\n componentRef={componentRef}\n description={confirmation?.description ?? undefined}\n endpoint={endpoint}\n formNode={formNode}\n method={method}\n onClose={() => setIsFilling(false)}\n onSuccess={() => {\n setIsFilling(false);\n }}\n placement={node.props.modalSide ?? \"center\"}\n submitLabel={confirmationConfirmLabel}\n title={confirmationTitle}\n width={node.props.modalWidth ?? undefined}\n />\n )}\n </>\n );\n\n return { processing, requestSubmit, overlays };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA6BA,SAAgB,UAAU,MAAiD;CACzE,MAAM,WAAW,KAAK,MAAM,YAAY;CACxC,MAAM,eAAe,KAAK,MAAM,OAAO;CACvC,MAAM,SAAiB,KAAK,MAAM,UAAU;CAC5C,MAAM,QAAQ,YAAY,IAAI;CAC9B,MAAM,UAAU,KAAK,MAAM,WAAW;CACtC,MAAM,eAAe,KAAK,MAAM;CAChC,MAAM,aAAa,KAAK,MAAM;CAC9B,MAAM,WAAW,KAAK,MAAM,aAAa;CACzC,MAAM,UAAU,QAAQ,UAAU,KAAK;CAEvC,MAAM,CAAC,YAAY,iBAAiB,SAAS,KAAK;CAClD,MAAM,WAAW,oBAAoB;CACrC,MAAM,CAAC,cAAc,mBAAmB,SAAS,KAAK;CACtD,MAAM,CAAC,WAAW,gBAAgB,SAAS,KAAK;CAChD,MAAM,WAAW,kBAAkB,UAAU,cAAc,aAAa,QAAQ;CAChF,MAAM,WAAW,WAAW,WAAW;CAEvC,MAAM,SAAS,YAA2B;EACxC,IAAI,CAAC,UACH;EAGF,IAAI,WAAW,OAAO;GACpB,OAAO,MAAM,UAAU,EAAE,SAAS,YAAY,YAAY,EAAE,CAAC;GAC7D,gBAAgB,KAAK;GAErB;EACF;EAEA,cAAc,IAAI;EAElB,MAAM,KAAK,MAAM,gBACT,SAAS,UAAU;GAAE;GAAQ,KAAK;GAAc,cAAc;EAAM,CAAC,GAC3E,QACF;EAEA,cAAc,KAAK;EAEnB,IAAI,IACF,gBAAgB,KAAK;CAEzB;CAEA,MAAM,sBAA4B;EAChC,IAAI,SAAS;GACX,aAAa,IAAI;GAEjB;EACF;EAEA,IAAI,cAAc;GAChB,gBAAgB,IAAI;GAEpB;EACF;EAEA,OAAY;CACd;CAEA,MAAM,oBAAoB,cAAc,SAAS;CACjD,MAAM,2BAA2B,cAAc,gBAAgB;CAC/D,MAAM,0BACJ,cAAc,eAAe,UAAU,WAAW,iBAAiB,QAAQ;CAuC7E,OAAO;EAAE;EAAY;EAAe,UApClC,qBAAA,YAAA,EAAA,UAAA,CACG,gBAAgB,gBACf,oBAAC,eAAD;GACE,OAAO;GACP,aAAa,aAAa,eAAe,KAAA;GACzC,cAAc;GACd,aAAa;GACb,gBAAgB;GACJ;GACZ,iBAAiB,CAAC;GAClB,iBAAiB,KAAK,OAAO;GAC7B,gBAAgB,gBAAgB,KAAK;EACtC,CAAA,GAGF,aAAa,WACZ,oBAAC,YAAD;GACE,aAAa;GACC;GACd,aAAa,cAAc,eAAe,KAAA;GAChC;GACA;GACF;GACR,eAAe,aAAa,KAAK;GACjC,iBAAiB;IACf,aAAa,KAAK;GACpB;GACA,WAAW,KAAK,MAAM,aAAa;GACnC,aAAa;GACb,OAAO;GACP,OAAO,KAAK,MAAM,cAAc,KAAA;EACjC,CAAA,CAEH,EAAA,CAGgC;CAAS;AAC/C"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { translate } from "../../i18n/instance.js";
|
|
2
|
+
//#region resources/js/action/lib/action-label.ts
|
|
3
|
+
function actionLabel(node) {
|
|
4
|
+
return node.props.label ?? translate("lattice", "common.action.run", "Run action");
|
|
5
|
+
}
|
|
6
|
+
//#endregion
|
|
7
|
+
export { actionLabel };
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=action-label.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-label.js","names":[],"sources":["../../../resources/js/action/lib/action-label.ts"],"sourcesContent":["import { translate } from \"@lattice-php/lattice/i18n\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\n\nexport function actionLabel(node: Node<\"action\" | \"action.bulk\">): string {\n return node.props.label ?? translate(\"lattice\", \"common.action.run\", \"Run action\");\n}\n"],"mappings":";;AAGA,SAAgB,YAAY,MAA8C;CACxE,OAAO,KAAK,MAAM,SAAS,UAAU,WAAW,qBAAqB,YAAY;AACnF"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ActionEffect
|
|
1
|
+
import { ActionEffect } from '../../effects/dispatch.js';
|
|
2
2
|
/**
|
|
3
|
-
* Runs an action request and dispatches the effects from its response,
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* reloading a table)
|
|
7
|
-
*
|
|
3
|
+
* Runs an action request and dispatches the effects from its response body,
|
|
4
|
+
* whether the action succeeded or was rejected (non-2xx). Returns whether the
|
|
5
|
+
* response was ok so callers run their own post-success cleanup (closing a
|
|
6
|
+
* dialog, reloading a table) only on success; a rejected action leaves the
|
|
7
|
+
* dialog open. A thrown/network error routes through dispatchActionError.
|
|
8
8
|
*/
|
|
9
|
-
export declare function runAction(request: () => Promise<
|
|
9
|
+
export declare function runAction(request: () => Promise<Response>, dispatch: (effects: ActionEffect[]) => void): Promise<boolean>;
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { dispatchActionError, getActionEffects } from "../../effects/dispatch.js";
|
|
2
2
|
//#region resources/js/action/lib/run-action.ts
|
|
3
3
|
/**
|
|
4
|
-
* Runs an action request and dispatches the effects from its response,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* reloading a table)
|
|
8
|
-
*
|
|
4
|
+
* Runs an action request and dispatches the effects from its response body,
|
|
5
|
+
* whether the action succeeded or was rejected (non-2xx). Returns whether the
|
|
6
|
+
* response was ok so callers run their own post-success cleanup (closing a
|
|
7
|
+
* dialog, reloading a table) only on success; a rejected action leaves the
|
|
8
|
+
* dialog open. A thrown/network error routes through dispatchActionError.
|
|
9
9
|
*/
|
|
10
10
|
async function runAction(request, dispatch) {
|
|
11
11
|
try {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const response = await request();
|
|
13
|
+
dispatch(getActionEffects((await response.json().catch(() => ({}))).effects));
|
|
14
|
+
return response.ok;
|
|
14
15
|
} catch (error) {
|
|
15
16
|
dispatchActionError(error);
|
|
16
17
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-action.js","names":[],"sources":["../../../resources/js/action/lib/run-action.ts"],"sourcesContent":["import {\n type ActionEffect,\n type ActionResponse,\n dispatchActionError,\n getActionEffects,\n} from \"@lattice-php/lattice/effects/dispatch\";\n\n/**\n * Runs an action request and dispatches the effects from its response
|
|
1
|
+
{"version":3,"file":"run-action.js","names":[],"sources":["../../../resources/js/action/lib/run-action.ts"],"sourcesContent":["import {\n type ActionEffect,\n type ActionResponse,\n dispatchActionError,\n getActionEffects,\n} from \"@lattice-php/lattice/effects/dispatch\";\n\n/**\n * Runs an action request and dispatches the effects from its response body,\n * whether the action succeeded or was rejected (non-2xx). Returns whether the\n * response was ok so callers run their own post-success cleanup (closing a\n * dialog, reloading a table) only on success; a rejected action leaves the\n * dialog open. A thrown/network error routes through dispatchActionError.\n */\nexport async function runAction(\n request: () => Promise<Response>,\n dispatch: (effects: ActionEffect[]) => void,\n): Promise<boolean> {\n try {\n const response = await request();\n const body = (await response.json().catch(() => ({}))) as ActionResponse;\n dispatch(getActionEffects(body.effects));\n\n return response.ok;\n } catch (error) {\n dispatchActionError(error);\n\n return false;\n }\n}\n"],"mappings":";;;;;;;;;AAcA,eAAsB,UACpB,SACA,UACkB;CAClB,IAAI;EACF,MAAM,WAAW,MAAM,QAAQ;EAE/B,SAAS,kBAAiB,MADN,SAAS,KAAK,EAAE,aAAa,CAAC,EAAE,GACrB,OAAO,CAAC;EAEvC,OAAO,SAAS;CAClB,SAAS,OAAO;EACd,oBAAoB,KAAK;EAEzB,OAAO;CACT;AACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { cn } from "../../lib/utils.js";
|
|
2
1
|
import { useT } from "../../i18n/instance.js";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
3
|
import { testIdentity } from "../../core/test-id.js";
|
|
4
4
|
import { apiFetch, remoteJson } from "../../core/api.js";
|
|
5
5
|
import { createRemoteNdjsonChatTransport, ndjsonChatTransport } from "../lib/transport.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { cn } from "../../lib/utils.js";
|
|
2
1
|
import { useT } from "../../i18n/instance.js";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
3
|
import { testIdentity } from "../../core/test-id.js";
|
|
4
4
|
import { Button } from "../../ui/button.js";
|
|
5
5
|
import { useState } from "react";
|
package/dist/core/index.d.ts
CHANGED
|
@@ -10,4 +10,8 @@ export { createPlugin, createRegistry, eagerComponent, extendRegistry, lazyCompo
|
|
|
10
10
|
export type { Plugin, Registry } from './registry.js';
|
|
11
11
|
export { RegistryContext, setDefaultRegistry, useColumnRegistry, useComponentRegistry, useEffectHandlerRegistry, } from './registry-context.js';
|
|
12
12
|
export { Renderer, RenderNode } from './renderer.js';
|
|
13
|
+
export { nodeIdentity } from './test-id.js';
|
|
14
|
+
export { usePersistentState } from '../lib/use-persistent-state.js';
|
|
15
|
+
export type { PersistentStateOptions } from '../lib/use-persistent-state.js';
|
|
16
|
+
export { cn } from '../lib/utils.js';
|
|
13
17
|
export type * from './types.js';
|
package/dist/core/index.js
CHANGED
|
@@ -3,8 +3,11 @@ import { RegistryContext, setDefaultRegistry, useColumnRegistry, useComponentReg
|
|
|
3
3
|
import { dataBindings, isRecord, materializeNode, materializeProps, materializeSchema, rowValue } from "./materialize.js";
|
|
4
4
|
import { LATTICE_REF_HEADER, withRefHeader } from "./component-ref.js";
|
|
5
5
|
import { withHeaders } from "./headers.js";
|
|
6
|
+
import { cn } from "../lib/utils.js";
|
|
6
7
|
import { toNodes } from "./nodes.js";
|
|
7
8
|
import { CollapsedContext, useCollapsed } from "./collapsed-context.js";
|
|
8
9
|
import { RenderNode, Renderer } from "./renderer.js";
|
|
10
|
+
import { nodeIdentity } from "./test-id.js";
|
|
11
|
+
import { usePersistentState } from "../lib/use-persistent-state.js";
|
|
9
12
|
import { ApiError, apiFetch, apiJson, clearRemoteTokenCache, invalidateRemoteToken, remoteFetch, remoteJson, remoteToken } from "./api.js";
|
|
10
|
-
export { ApiError, CollapsedContext, LATTICE_REF_HEADER, RegistryContext, RenderNode, Renderer, apiFetch, apiJson, clearRemoteTokenCache, createPlugin, createRegistry, dataBindings, eagerComponent, extendRegistry, invalidateRemoteToken, isRecord, lazyComponent, materializeNode, materializeProps, materializeSchema, remoteFetch, remoteJson, remoteToken, rowValue, setDefaultRegistry, toNodes, useCollapsed, useColumnRegistry, useComponentRegistry, useEffectHandlerRegistry, withHeaders, withRefHeader };
|
|
13
|
+
export { ApiError, CollapsedContext, LATTICE_REF_HEADER, RegistryContext, RenderNode, Renderer, apiFetch, apiJson, clearRemoteTokenCache, cn, createPlugin, createRegistry, dataBindings, eagerComponent, extendRegistry, invalidateRemoteToken, isRecord, lazyComponent, materializeNode, materializeProps, materializeSchema, nodeIdentity, remoteFetch, remoteJson, remoteToken, rowValue, setDefaultRegistry, toNodes, useCollapsed, useColumnRegistry, useComponentRegistry, useEffectHandlerRegistry, usePersistentState, withHeaders, withRefHeader };
|
package/dist/core/registry.d.ts
CHANGED
|
@@ -16,11 +16,16 @@ export type LazyComponentRegistration = {
|
|
|
16
16
|
export type ComponentRegistration = EagerComponentRegistration | LazyComponentRegistration;
|
|
17
17
|
export type ComponentRegistry = Record<string, ComponentRegistration>;
|
|
18
18
|
export type ComponentRegistryFor<TTypes extends keyof ComponentPropsMap & string> = Record<TTypes, ComponentRegistration>;
|
|
19
|
+
export type PluginI18n = {
|
|
20
|
+
/** i18next namespace the plugin's components translate under. */
|
|
21
|
+
namespace: string;
|
|
22
|
+
};
|
|
19
23
|
export type Plugin = {
|
|
20
24
|
name: string;
|
|
21
25
|
components?: ComponentRegistry;
|
|
22
26
|
columns?: ColumnRegistry;
|
|
23
27
|
effects?: EffectHandlerRegistry;
|
|
28
|
+
i18n?: PluginI18n;
|
|
24
29
|
};
|
|
25
30
|
export type Registry = {
|
|
26
31
|
components: ComponentRegistry;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","names":[],"sources":["../../resources/js/core/registry.ts"],"sourcesContent":["import { lazy } from \"react\";\nimport type { LazyExoticComponent } from \"react\";\nimport type { RendererComponent, RendererComponentModule } from \"./types\";\nimport type { EffectHandlerRegistry } from \"@lattice-php/lattice/effects/registry\";\nimport type { ColumnRegistry } from \"@lattice-php/lattice/table/registry\";\nimport type { ComponentPropsMap } from \"@lattice-php/lattice/types/generated\";\n\nexport type EagerComponentRegistration = {\n component: RendererComponent;\n mode: \"eager\";\n};\n\nexport type LazyComponentRegistration = {\n component: LazyExoticComponent<RendererComponent>;\n fallback?: RendererComponent;\n load: () => Promise<RendererComponentModule>;\n mode: \"lazy\";\n};\n\nexport type ComponentRegistration = EagerComponentRegistration | LazyComponentRegistration;\n\nexport type ComponentRegistry = Record<string, ComponentRegistration>;\n\nexport type ComponentRegistryFor<TTypes extends keyof ComponentPropsMap & string> = Record<\n TTypes,\n ComponentRegistration\n>;\n\nexport type Plugin = {\n name: string;\n components?: ComponentRegistry;\n columns?: ColumnRegistry;\n effects?: EffectHandlerRegistry;\n};\n\nexport type Registry = {\n components: ComponentRegistry;\n columns: ColumnRegistry;\n effects: EffectHandlerRegistry;\n};\n\nexport type LazyComponentOptions<TType extends string> = {\n fallback?: RendererComponent<TType>;\n};\n\nexport function eagerComponent<TType extends string>(\n component: RendererComponent<TType>,\n): EagerComponentRegistration {\n return {\n component: component as unknown as RendererComponent,\n mode: \"eager\",\n };\n}\n\nexport function lazyComponent<TType extends string>(\n load: () => Promise<RendererComponentModule<TType>>,\n options: LazyComponentOptions<TType> = {},\n): LazyComponentRegistration {\n const erasedLoader = load as unknown as () => Promise<RendererComponentModule>;\n\n return {\n component: lazy(erasedLoader),\n fallback: options.fallback as RendererComponent | undefined,\n load: erasedLoader,\n mode: \"lazy\",\n };\n}\n\nexport function createPlugin(plugin: Plugin): Plugin {\n return plugin;\n}\n\nexport function createRegistry(...plugins: Plugin[]): Registry {\n return plugins.reduce<Registry>(\n (registry, plugin) => ({\n components: { ...registry.components, ...plugin.components },\n columns: { ...registry.columns, ...plugin.columns },\n effects: { ...registry.effects, ...plugin.effects },\n }),\n { components: {}, columns: {}, effects: {} },\n );\n}\n\nexport function extendRegistry(registry: Registry, ...plugins: Plugin[]): Registry {\n const merged = createRegistry(...plugins);\n\n return {\n components: { ...registry.components, ...merged.components },\n columns: { ...registry.columns, ...merged.columns },\n effects: { ...registry.effects, ...merged.effects },\n };\n}\n"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"registry.js","names":[],"sources":["../../resources/js/core/registry.ts"],"sourcesContent":["import { lazy } from \"react\";\nimport type { LazyExoticComponent } from \"react\";\nimport type { RendererComponent, RendererComponentModule } from \"./types\";\nimport type { EffectHandlerRegistry } from \"@lattice-php/lattice/effects/registry\";\nimport type { ColumnRegistry } from \"@lattice-php/lattice/table/registry\";\nimport type { ComponentPropsMap } from \"@lattice-php/lattice/types/generated\";\n\nexport type EagerComponentRegistration = {\n component: RendererComponent;\n mode: \"eager\";\n};\n\nexport type LazyComponentRegistration = {\n component: LazyExoticComponent<RendererComponent>;\n fallback?: RendererComponent;\n load: () => Promise<RendererComponentModule>;\n mode: \"lazy\";\n};\n\nexport type ComponentRegistration = EagerComponentRegistration | LazyComponentRegistration;\n\nexport type ComponentRegistry = Record<string, ComponentRegistration>;\n\nexport type ComponentRegistryFor<TTypes extends keyof ComponentPropsMap & string> = Record<\n TTypes,\n ComponentRegistration\n>;\n\nexport type PluginI18n = {\n /** i18next namespace the plugin's components translate under. */\n namespace: string;\n};\n\nexport type Plugin = {\n name: string;\n components?: ComponentRegistry;\n columns?: ColumnRegistry;\n effects?: EffectHandlerRegistry;\n i18n?: PluginI18n;\n};\n\nexport type Registry = {\n components: ComponentRegistry;\n columns: ColumnRegistry;\n effects: EffectHandlerRegistry;\n};\n\nexport type LazyComponentOptions<TType extends string> = {\n fallback?: RendererComponent<TType>;\n};\n\nexport function eagerComponent<TType extends string>(\n component: RendererComponent<TType>,\n): EagerComponentRegistration {\n return {\n component: component as unknown as RendererComponent,\n mode: \"eager\",\n };\n}\n\nexport function lazyComponent<TType extends string>(\n load: () => Promise<RendererComponentModule<TType>>,\n options: LazyComponentOptions<TType> = {},\n): LazyComponentRegistration {\n const erasedLoader = load as unknown as () => Promise<RendererComponentModule>;\n\n return {\n component: lazy(erasedLoader),\n fallback: options.fallback as RendererComponent | undefined,\n load: erasedLoader,\n mode: \"lazy\",\n };\n}\n\nexport function createPlugin(plugin: Plugin): Plugin {\n return plugin;\n}\n\nexport function createRegistry(...plugins: Plugin[]): Registry {\n return plugins.reduce<Registry>(\n (registry, plugin) => ({\n components: { ...registry.components, ...plugin.components },\n columns: { ...registry.columns, ...plugin.columns },\n effects: { ...registry.effects, ...plugin.effects },\n }),\n { components: {}, columns: {}, effects: {} },\n );\n}\n\nexport function extendRegistry(registry: Registry, ...plugins: Plugin[]): Registry {\n const merged = createRegistry(...plugins);\n\n return {\n components: { ...registry.components, ...merged.components },\n columns: { ...registry.columns, ...merged.columns },\n effects: { ...registry.effects, ...merged.effects },\n };\n}\n"],"mappings":";;AAmDA,SAAgB,eACd,WAC4B;CAC5B,OAAO;EACM;EACX,MAAM;CACR;AACF;AAEA,SAAgB,cACd,MACA,UAAuC,CAAC,GACb;CAC3B,MAAM,eAAe;CAErB,OAAO;EACL,WAAW,KAAK,YAAY;EAC5B,UAAU,QAAQ;EAClB,MAAM;EACN,MAAM;CACR;AACF;AAEA,SAAgB,aAAa,QAAwB;CACnD,OAAO;AACT;AAEA,SAAgB,eAAe,GAAG,SAA6B;CAC7D,OAAO,QAAQ,QACZ,UAAU,YAAY;EACrB,YAAY;GAAE,GAAG,SAAS;GAAY,GAAG,OAAO;EAAW;EAC3D,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,OAAO;EAAQ;EAClD,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,OAAO;EAAQ;CACpD,IACA;EAAE,YAAY,CAAC;EAAG,SAAS,CAAC;EAAG,SAAS,CAAC;CAAE,CAC7C;AACF;AAEA,SAAgB,eAAe,UAAoB,GAAG,SAA6B;CACjF,MAAM,SAAS,eAAe,GAAG,OAAO;CAExC,OAAO;EACL,YAAY;GAAE,GAAG,SAAS;GAAY,GAAG,OAAO;EAAW;EAC3D,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,OAAO;EAAQ;EAClD,SAAS;GAAE,GAAG,SAAS;GAAS,GAAG,OAAO;EAAQ;CACpD;AACF"}
|
package/dist/create-app.d.ts
CHANGED
|
@@ -6,7 +6,10 @@ import { SpriteValue } from './icons/sprite.js';
|
|
|
6
6
|
import { CreateLayoutResolverOptions, PageModules } from './inertia.js';
|
|
7
7
|
type InertiaAppOptions = NonNullable<Parameters<typeof createInertiaApp>[0]>;
|
|
8
8
|
export type CreateLatticeAppI18nOptions = {
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* i18next namespaces to load, e.g. `["lattice", "app"]`. Defaults to the
|
|
11
|
+
* package's own. Namespaces declared by plugins are merged in on top.
|
|
12
|
+
*/
|
|
10
13
|
namespaces?: string[];
|
|
11
14
|
};
|
|
12
15
|
export type CreateLatticeAppOptions = Omit<InertiaAppOptions, "resolve" | "layout" | "setup" | "withApp" | "pages"> & {
|
|
@@ -14,7 +17,8 @@ export type CreateLatticeAppOptions = Omit<InertiaAppOptions, "resolve" | "layou
|
|
|
14
17
|
/**
|
|
15
18
|
* Component-package plugins to merge onto the registry — pass the
|
|
16
19
|
* Composer-discovered `virtual:lattice/plugins` here to register vendor
|
|
17
|
-
* components with no manual `extendRegistry` call.
|
|
20
|
+
* components with no manual `extendRegistry` call. A plugin's declared
|
|
21
|
+
* i18n namespace joins the i18n bootstrap automatically.
|
|
18
22
|
*/
|
|
19
23
|
plugins?: Plugin[];
|
|
20
24
|
sprite?: SpriteValue;
|
package/dist/create-app.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { initializeTheme } from "./appearance/index.js";
|
|
2
2
|
import { extendRegistry } from "./core/registry.js";
|
|
3
3
|
import { setDefaultRegistry } from "./core/registry-context.js";
|
|
4
|
+
import "./i18n/instance.js";
|
|
4
5
|
import { LocaleReload } from "./i18n/locale-reload.js";
|
|
5
6
|
import { i18nConfigFromPageProps } from "./i18n/shared-props.js";
|
|
6
7
|
import { createLayoutResolver, createPageResolver, withVisitHeaders } from "./inertia.js";
|
|
@@ -10,6 +11,14 @@ import { createInertiaApp } from "@inertiajs/react";
|
|
|
10
11
|
import { useEffect, useState } from "react";
|
|
11
12
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
12
13
|
//#region resources/js/create-app.tsx
|
|
14
|
+
function withPluginNamespaces(options, entries) {
|
|
15
|
+
if (entries.length === 0) return options;
|
|
16
|
+
const namespaces = new Set([...options.namespaces ?? ["lattice"], ...entries.map((entry) => entry.namespace)]);
|
|
17
|
+
return {
|
|
18
|
+
...options,
|
|
19
|
+
namespaces: [...namespaces]
|
|
20
|
+
};
|
|
21
|
+
}
|
|
13
22
|
function AwaitReady({ ready, children }) {
|
|
14
23
|
const [isReady, setReady] = useState(false);
|
|
15
24
|
useEffect(() => {
|
|
@@ -36,6 +45,8 @@ function createLatticeApp({ registry: registry$1, plugins, sprite, pages = {}, d
|
|
|
36
45
|
const activeRegistry = plugins && plugins.length > 0 ? extendRegistry(baseRegistry, ...plugins) : baseRegistry;
|
|
37
46
|
setDefaultRegistry(activeRegistry);
|
|
38
47
|
const i18nEnabled = i18n !== false;
|
|
48
|
+
const pluginI18n = plugins?.flatMap((plugin) => plugin.i18n ? [plugin.i18n] : []) ?? [];
|
|
49
|
+
const i18nOptions = i18nEnabled ? withPluginNamespaces(i18n ?? {}, pluginI18n) : {};
|
|
39
50
|
const userVisitOptions = inertiaOptions.defaults?.visitOptions;
|
|
40
51
|
const defaults = i18nEnabled ? {
|
|
41
52
|
...inertiaOptions.defaults,
|
|
@@ -50,7 +61,7 @@ function createLatticeApp({ registry: registry$1, plugins, sprite, pages = {}, d
|
|
|
50
61
|
withApp: (node, { ssr, page }) => {
|
|
51
62
|
const pending = [];
|
|
52
63
|
if (!ssr) {
|
|
53
|
-
if (i18nEnabled && i18nConfigFromPageProps(page.props) !== void 0) pending.push(import("./i18n/page-props.js").then((module) => module.configureI18nFromPageProps(page.props,
|
|
64
|
+
if (i18nEnabled && i18nConfigFromPageProps(page.props) !== void 0) pending.push(import("./i18n/page-props.js").then((module) => module.configureI18nFromPageProps(page.props, i18nOptions)));
|
|
54
65
|
const booted = boot?.({ page });
|
|
55
66
|
if (booted) pending.push(booted);
|
|
56
67
|
}
|
package/dist/create-app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-app.js","names":[],"sources":["../resources/js/create-app.tsx"],"sourcesContent":["import type { Page as InertiaPage, VisitOptions } from \"@inertiajs/core\";\nimport { createInertiaApp } from \"@inertiajs/react\";\nimport { useEffect, useState, type ReactElement, type ReactNode } from \"react\";\nimport { initializeTheme } from \"./appearance\";\nimport { extendRegistry, type Plugin, type Registry } from \"./core/registry\";\nimport { setDefaultRegistry } from \"./core/registry-context\";\nimport type { SpriteValue } from \"./icons/sprite\";\nimport { LocaleReload } from \"./i18n/locale-reload\";\nimport { i18nConfigFromPageProps } from \"./i18n/shared-props\";\nimport {\n createLayoutResolver,\n createPageResolver,\n withVisitHeaders,\n type CreateLayoutResolverOptions,\n type PageModules,\n} from \"./inertia\";\nimport { ProviderBase } from \"./provider-base\";\nimport { registry as defaultRegistry } from \"./registry\";\n\ntype InertiaAppOptions = NonNullable<Parameters<typeof createInertiaApp>[0]>;\n\nexport type CreateLatticeAppI18nOptions = {\n
|
|
1
|
+
{"version":3,"file":"create-app.js","names":[],"sources":["../resources/js/create-app.tsx"],"sourcesContent":["import type { Page as InertiaPage, VisitOptions } from \"@inertiajs/core\";\nimport { createInertiaApp } from \"@inertiajs/react\";\nimport { useEffect, useState, type ReactElement, type ReactNode } from \"react\";\nimport { initializeTheme } from \"./appearance\";\nimport { extendRegistry, type Plugin, type PluginI18n, type Registry } from \"./core/registry\";\nimport { setDefaultRegistry } from \"./core/registry-context\";\nimport type { SpriteValue } from \"./icons/sprite\";\nimport { DEFAULT_NAMESPACE } from \"./i18n/instance\";\nimport { LocaleReload } from \"./i18n/locale-reload\";\nimport { i18nConfigFromPageProps } from \"./i18n/shared-props\";\nimport {\n createLayoutResolver,\n createPageResolver,\n withVisitHeaders,\n type CreateLayoutResolverOptions,\n type PageModules,\n} from \"./inertia\";\nimport { ProviderBase } from \"./provider-base\";\nimport { registry as defaultRegistry } from \"./registry\";\n\ntype InertiaAppOptions = NonNullable<Parameters<typeof createInertiaApp>[0]>;\n\nexport type CreateLatticeAppI18nOptions = {\n /**\n * i18next namespaces to load, e.g. `[\"lattice\", \"app\"]`. Defaults to the\n * package's own. Namespaces declared by plugins are merged in on top.\n */\n namespaces?: string[];\n};\n\nexport type CreateLatticeAppOptions = Omit<\n InertiaAppOptions,\n \"resolve\" | \"layout\" | \"setup\" | \"withApp\" | \"pages\"\n> & {\n registry?: Registry;\n /**\n * Component-package plugins to merge onto the registry — pass the\n * Composer-discovered `virtual:lattice/plugins` here to register vendor\n * components with no manual `extendRegistry` call. A plugin's declared\n * i18n namespace joins the i18n bootstrap automatically.\n */\n plugins?: Plugin[];\n sprite?: SpriteValue;\n /** Normal Inertia page modules, e.g. `import.meta.glob('./pages/**\\/*.tsx')`. */\n pages?: PageModules;\n defaultLayout?: CreateLayoutResolverOptions[\"defaultLayout\"];\n /**\n * Lattice's i18n bootstrap, on by default: when the backend shares the\n * `lattice.i18n` prop, the first render waits for the translation setup (no\n * flash of untranslated fallbacks), `LocaleReload` re-fetches the page after\n * a locale switch, and every visit carries the locale/timezone headers. The\n * i18next chunk only loads when the backend actually shares the prop. Pass\n * `false` to opt out entirely.\n */\n i18n?: CreateLatticeAppI18nOptions | false;\n /**\n * App bootstrap that needs the initial page — e.g. `configureEcho` from a\n * shared connection prop. Runs on the client before the first render; a\n * returned promise delays that render until it resolves.\n */\n boot?: (context: { page: InertiaPage }) => void | Promise<void>;\n /**\n * Compose providers or siblings around the app. Applied inside the Lattice\n * Provider, so the registry, sprite, and toaster contexts are available.\n */\n wrap?: (app: ReactElement) => ReactElement;\n};\n\nfunction withPluginNamespaces(\n options: CreateLatticeAppI18nOptions,\n entries: PluginI18n[],\n): CreateLatticeAppI18nOptions {\n if (entries.length === 0) {\n return options;\n }\n\n const namespaces = new Set([\n ...(options.namespaces ?? [DEFAULT_NAMESPACE]),\n ...entries.map((entry) => entry.namespace),\n ]);\n\n return { ...options, namespaces: [...namespaces] };\n}\n\n// Fail-open: a failed bootstrap renders the app anyway (with fallback strings)\n// rather than a blank page.\nfunction AwaitReady({ ready, children }: { ready: Promise<unknown>; children: ReactNode }) {\n const [isReady, setReady] = useState(false);\n\n useEffect(() => {\n let active = true;\n const reveal = (): void => {\n if (active) {\n setReady(true);\n }\n };\n\n void ready.then(reveal, reveal);\n\n return () => {\n active = false;\n };\n }, [ready]);\n\n return isReady ? children : null;\n}\n\n/**\n * Bootstrap an Inertia app with the Lattice shell: the page/layout resolvers\n * (server-driven Lattice pages and normal Inertia pages alike), the Provider —\n * registry, sprite, flash toasts via Lattice's own Toaster — theme\n * initialization, and the i18n bootstrap. Forwards any other createInertiaApp\n * option.\n */\nexport function createLatticeApp({\n registry,\n plugins,\n sprite,\n pages = {},\n defaultLayout,\n strictMode = true,\n i18n,\n boot,\n wrap,\n ...inertiaOptions\n}: CreateLatticeAppOptions = {}) {\n const baseRegistry = registry ?? defaultRegistry;\n const activeRegistry =\n plugins && plugins.length > 0 ? extendRegistry(baseRegistry, ...plugins) : baseRegistry;\n\n setDefaultRegistry(activeRegistry);\n\n const i18nEnabled = i18n !== false;\n const pluginI18n = plugins?.flatMap((plugin) => (plugin.i18n ? [plugin.i18n] : [])) ?? [];\n const i18nOptions = i18nEnabled ? withPluginNamespaces(i18n ?? {}, pluginI18n) : {};\n const userVisitOptions = inertiaOptions.defaults?.visitOptions;\n const defaults = i18nEnabled\n ? {\n ...inertiaOptions.defaults,\n visitOptions: (href: string, options: VisitOptions): VisitOptions =>\n withVisitHeaders(href, userVisitOptions?.(href, options) ?? options),\n }\n : inertiaOptions.defaults;\n\n const app = createInertiaApp({\n ...inertiaOptions,\n defaults,\n strictMode,\n resolve: createPageResolver(pages),\n layout: createLayoutResolver({ defaultLayout }),\n withApp: (node: ReactElement, { ssr, page }: { ssr: boolean; page: InertiaPage }) => {\n const pending: Promise<unknown>[] = [];\n\n if (!ssr) {\n // Loaded on demand so apps without the shared i18n prop never ship the\n // i18next backend; the disabled-config call still stores the timezone.\n if (i18nEnabled && i18nConfigFromPageProps(page.props) !== undefined) {\n pending.push(\n import(\"./i18n/page-props\").then((module) =>\n module.configureI18nFromPageProps(page.props, i18nOptions),\n ),\n );\n }\n\n const booted = boot?.({ page });\n\n if (booted) {\n pending.push(booted);\n }\n }\n\n const inner = (\n <>\n {node}\n {i18nEnabled ? <LocaleReload /> : null}\n </>\n );\n\n // The whole shell waits, not just the page: anything rendering a\n // translated string (the Toaster included) would otherwise initialize\n // i18next before the backend can register — init runs exactly once,\n // first caller wins.\n const shell = (\n <ProviderBase registry={activeRegistry} sprite={sprite}>\n {wrap ? wrap(inner) : inner}\n </ProviderBase>\n );\n\n return pending.length > 0 ? (\n <AwaitReady ready={Promise.all(pending)}>{shell}</AwaitReady>\n ) : (\n shell\n );\n },\n });\n\n initializeTheme();\n\n return app;\n}\n"],"mappings":";;;;;;;;;;;;;AAoEA,SAAS,qBACP,SACA,SAC6B;CAC7B,IAAI,QAAQ,WAAW,GACrB,OAAO;CAGT,MAAM,aAAa,IAAI,IAAI,CACzB,GAAI,QAAQ,cAAc,CAAA,SAAkB,GAC5C,GAAG,QAAQ,KAAK,UAAU,MAAM,SAAS,CAC3C,CAAC;CAED,OAAO;EAAE,GAAG;EAAS,YAAY,CAAC,GAAG,UAAU;CAAE;AACnD;AAIA,SAAS,WAAW,EAAE,OAAO,YAA8D;CACzF,MAAM,CAAC,SAAS,YAAY,SAAS,KAAK;CAE1C,gBAAgB;EACd,IAAI,SAAS;EACb,MAAM,eAAqB;GACzB,IAAI,QACF,SAAS,IAAI;EAEjB;EAEA,MAAW,KAAK,QAAQ,MAAM;EAE9B,aAAa;GACX,SAAS;EACX;CACF,GAAG,CAAC,KAAK,CAAC;CAEV,OAAO,UAAU,WAAW;AAC9B;;;;;;;;AASA,SAAgB,iBAAiB,EAC/B,UAAA,YACA,SACA,QACA,QAAQ,CAAC,GACT,eACA,aAAa,MACb,MACA,MACA,MACA,GAAG,mBACwB,CAAC,GAAG;CAC/B,MAAM,eAAe,cAAY;CACjC,MAAM,iBACJ,WAAW,QAAQ,SAAS,IAAI,eAAe,cAAc,GAAG,OAAO,IAAI;CAE7E,mBAAmB,cAAc;CAEjC,MAAM,cAAc,SAAS;CAC7B,MAAM,aAAa,SAAS,SAAS,WAAY,OAAO,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,CAAE,KAAK,CAAC;CACxF,MAAM,cAAc,cAAc,qBAAqB,QAAQ,CAAC,GAAG,UAAU,IAAI,CAAC;CAClF,MAAM,mBAAmB,eAAe,UAAU;CAClD,MAAM,WAAW,cACb;EACE,GAAG,eAAe;EAClB,eAAe,MAAc,YAC3B,iBAAiB,MAAM,mBAAmB,MAAM,OAAO,KAAK,OAAO;CACvE,IACA,eAAe;CAEnB,MAAM,MAAM,iBAAiB;EAC3B,GAAG;EACH;EACA;EACA,SAAS,mBAAmB,KAAK;EACjC,QAAQ,qBAAqB,EAAE,cAAc,CAAC;EAC9C,UAAU,MAAoB,EAAE,KAAK,WAAgD;GACnF,MAAM,UAA8B,CAAC;GAErC,IAAI,CAAC,KAAK;IAGR,IAAI,eAAe,wBAAwB,KAAK,KAAK,MAAM,KAAA,GACzD,QAAQ,KACN,OAAO,wBAAqB,MAAM,WAChC,OAAO,2BAA2B,KAAK,OAAO,WAAW,CAC3D,CACF;IAGF,MAAM,SAAS,OAAO,EAAE,KAAK,CAAC;IAE9B,IAAI,QACF,QAAQ,KAAK,MAAM;GAEvB;GAEA,MAAM,QACJ,qBAAA,YAAA,EAAA,UAAA,CACG,MACA,cAAc,oBAAC,cAAD,CAAe,CAAA,IAAI,IAClC,EAAA,CAAA;GAOJ,MAAM,QACJ,oBAAC,cAAD;IAAc,UAAU;IAAwB;cAC7C,OAAO,KAAK,KAAK,IAAI;GACV,CAAA;GAGhB,OAAO,QAAQ,SAAS,IACtB,oBAAC,YAAD;IAAY,OAAO,QAAQ,IAAI,OAAO;cAAI;GAAkB,CAAA,IAE5D;EAEJ;CACF,CAAC;CAED,gBAAgB;CAEhB,OAAO;AACT"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { cn } from "../../../lib/utils.js";
|
|
2
1
|
import { useT } from "../../../i18n/instance.js";
|
|
2
|
+
import { cn } from "../../../lib/utils.js";
|
|
3
3
|
import { useFormContext } from "../../hooks/context.js";
|
|
4
4
|
import { useFieldScope } from "../../hooks/field-scope.js";
|
|
5
5
|
import { useFormValue } from "../../hooks/values.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Icon } from "../../../icons/sprite.js";
|
|
2
1
|
import { useT } from "../../../i18n/instance.js";
|
|
2
|
+
import { Icon } from "../../../icons/sprite.js";
|
|
3
3
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../../../ui/dropdown-menu.js";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region resources/js/form/components/fields/row-actions.tsx
|