@lattice-php/lattice 0.24.0 → 0.26.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.
Files changed (154) hide show
  1. package/dist/action/components/action-form.js +37 -5
  2. package/dist/action/components/action-form.js.map +1 -1
  3. package/dist/action/components/action-group.js +1 -1
  4. package/dist/action/components/action-group.js.map +1 -1
  5. package/dist/action/components/action.js +3 -2
  6. package/dist/action/components/action.js.map +1 -1
  7. package/dist/action/hooks/use-action.js +2 -1
  8. package/dist/action/hooks/use-action.js.map +1 -1
  9. package/dist/appearance/index.d.ts +1 -1
  10. package/dist/appearance/index.js +8 -8
  11. package/dist/appearance/index.js.map +1 -1
  12. package/dist/chat/components/prompt-input.js +1 -2
  13. package/dist/chat/components/prompt-input.js.map +1 -1
  14. package/dist/create-app.js +2 -2
  15. package/dist/create-app.js.map +1 -1
  16. package/dist/form/components/base/submit-button.d.ts +4 -3
  17. package/dist/form/components/base/submit-button.js +2 -1
  18. package/dist/form/components/base/submit-button.js.map +1 -1
  19. package/dist/form/components/fields/date-picker-field.js +3 -3
  20. package/dist/form/components/fields/date-picker-field.js.map +1 -1
  21. package/dist/form/components/fields/number-input.js +1 -1
  22. package/dist/form/components/fields/number-input.js.map +1 -1
  23. package/dist/form/components/fields/rich-editor-field.js +1 -1
  24. package/dist/form/components/fields/rich-editor-field.js.map +1 -1
  25. package/dist/form/components/fields/row-action-menu.js +3 -3
  26. package/dist/form/components/fields/row-action-menu.js.map +1 -1
  27. package/dist/form/components/fields/row-actions.d.ts +1 -1
  28. package/dist/form/components/fields/row-actions.js +4 -4
  29. package/dist/form/components/fields/row-actions.js.map +1 -1
  30. package/dist/form/components/fields/toggle.js +1 -1
  31. package/dist/form/components/fields/toggle.js.map +1 -1
  32. package/dist/form/components/form.js +15 -5
  33. package/dist/form/components/form.js.map +1 -1
  34. package/dist/form/components/index.d.ts +1 -0
  35. package/dist/form/components/index.js +2 -1
  36. package/dist/form/components/wizard.d.ts +3 -0
  37. package/dist/form/components/wizard.js +148 -0
  38. package/dist/form/components/wizard.js.map +1 -0
  39. package/dist/form/embed.d.ts +1 -1
  40. package/dist/form/embed.js +2 -2
  41. package/dist/form/hooks/context.d.ts +7 -2
  42. package/dist/form/hooks/context.js +4 -1
  43. package/dist/form/hooks/context.js.map +1 -1
  44. package/dist/form/index.js +2 -1
  45. package/dist/form/lib/field-errors.d.ts +2 -0
  46. package/dist/form/lib/field-errors.js +5 -1
  47. package/dist/form/lib/field-errors.js.map +1 -1
  48. package/dist/form/lib/field-props.d.ts +6 -0
  49. package/dist/form/lib/field-props.js +7 -1
  50. package/dist/form/lib/field-props.js.map +1 -1
  51. package/dist/form/lib/prefill-targets.js +2 -3
  52. package/dist/form/lib/prefill-targets.js.map +1 -1
  53. package/dist/form/lib/wizard-steps.d.ts +5 -0
  54. package/dist/form/lib/wizard-steps.js +44 -0
  55. package/dist/form/lib/wizard-steps.js.map +1 -0
  56. package/dist/form/plugin.js +4 -1
  57. package/dist/form/plugin.js.map +1 -1
  58. package/dist/i18n/translatable.d.ts +1 -0
  59. package/dist/i18n/translatable.js +4 -1
  60. package/dist/i18n/translatable.js.map +1 -1
  61. package/dist/index.d.ts +2 -2
  62. package/dist/index.js +2 -2
  63. package/dist/lattice.css +183 -115
  64. package/dist/layout/components/callouts.js +3 -2
  65. package/dist/layout/components/callouts.js.map +1 -1
  66. package/dist/notifications/components/notification-item.js +10 -5
  67. package/dist/notifications/components/notification-item.js.map +1 -1
  68. package/dist/notifications/components/notifications.js +2 -4
  69. package/dist/notifications/components/notifications.js.map +1 -1
  70. package/dist/table/components/bulk-bar.js +2 -0
  71. package/dist/table/components/bulk-bar.js.map +1 -1
  72. package/dist/table/components/cells/badge-cell.js +3 -6
  73. package/dist/table/components/cells/badge-cell.js.map +1 -1
  74. package/dist/table/components/cells/text-cell.js +6 -9
  75. package/dist/table/components/cells/text-cell.js.map +1 -1
  76. package/dist/table/components/column-filter-control.js +2 -2
  77. package/dist/table/components/column-filter-control.js.map +1 -1
  78. package/dist/table/components/filter-bar.js +1 -1
  79. package/dist/table/components/filter-bar.js.map +1 -1
  80. package/dist/table/components/filter-controls.js +4 -1
  81. package/dist/table/components/filter-controls.js.map +1 -1
  82. package/dist/table/components/pagination.js +6 -6
  83. package/dist/table/components/pagination.js.map +1 -1
  84. package/dist/table/lib/bulk.d.ts +3 -2
  85. package/dist/table/lib/bulk.js +2 -1
  86. package/dist/table/lib/bulk.js.map +1 -1
  87. package/dist/toast/callout.js +6 -3
  88. package/dist/toast/callout.js.map +1 -1
  89. package/dist/toast/toast.js +3 -1
  90. package/dist/toast/toast.js.map +1 -1
  91. package/dist/toast/toaster.js +2 -2
  92. package/dist/toast/toaster.js.map +1 -1
  93. package/dist/toast/variant-styles.js +15 -1
  94. package/dist/toast/variant-styles.js.map +1 -1
  95. package/dist/types/generated.d.ts +31 -16
  96. package/dist/ui/action-menu-context.d.ts +1 -1
  97. package/dist/ui/action-menu-context.js +1 -1
  98. package/dist/ui/action-menu-context.js.map +1 -1
  99. package/dist/ui/affix-group.js +1 -1
  100. package/dist/ui/affix-group.js.map +1 -1
  101. package/dist/ui/badge.d.ts +4 -7
  102. package/dist/ui/badge.js +11 -17
  103. package/dist/ui/badge.js.map +1 -1
  104. package/dist/ui/button.d.ts +6 -5
  105. package/dist/ui/button.js +154 -15
  106. package/dist/ui/button.js.map +1 -1
  107. package/dist/ui/chart-view.js +9 -7
  108. package/dist/ui/chart-view.js.map +1 -1
  109. package/dist/ui/checkbox.js +1 -1
  110. package/dist/ui/checkbox.js.map +1 -1
  111. package/dist/ui/confirm-dialog.d.ts +4 -3
  112. package/dist/ui/confirm-dialog.js +3 -2
  113. package/dist/ui/confirm-dialog.js.map +1 -1
  114. package/dist/ui/control.d.ts +1 -1
  115. package/dist/ui/control.js +1 -1
  116. package/dist/ui/control.js.map +1 -1
  117. package/dist/ui/dialog.js +1 -1
  118. package/dist/ui/dialog.js.map +1 -1
  119. package/dist/ui/dropdown-menu.d.ts +2 -2
  120. package/dist/ui/dropdown-menu.js +2 -2
  121. package/dist/ui/dropdown-menu.js.map +1 -1
  122. package/dist/ui/icon-button.d.ts +3 -3
  123. package/dist/ui/icon-button.js +6 -6
  124. package/dist/ui/icon-button.js.map +1 -1
  125. package/dist/ui/image-preview.js +1 -1
  126. package/dist/ui/image-preview.js.map +1 -1
  127. package/dist/ui/index.d.ts +2 -2
  128. package/dist/ui/index.js +2 -2
  129. package/dist/ui/info-tooltip.js +1 -1
  130. package/dist/ui/info-tooltip.js.map +1 -1
  131. package/dist/ui/input-otp.js +1 -1
  132. package/dist/ui/input-otp.js.map +1 -1
  133. package/dist/ui/label.js +1 -1
  134. package/dist/ui/label.js.map +1 -1
  135. package/dist/ui/password-input.js +1 -1
  136. package/dist/ui/password-input.js.map +1 -1
  137. package/dist/ui/progress.js +2 -2
  138. package/dist/ui/progress.js.map +1 -1
  139. package/dist/ui/text.js +1 -1
  140. package/dist/ui/tooltip.js +1 -1
  141. package/dist/ui/tooltip.js.map +1 -1
  142. package/dist/vite-typescript-refresh.d.ts +9 -0
  143. package/dist/vite-typescript-refresh.js +47 -0
  144. package/dist/vite-typescript-refresh.js.map +1 -0
  145. package/dist/vite.d.ts +22 -1
  146. package/dist/vite.js +60 -7
  147. package/dist/vite.js.map +1 -1
  148. package/dist-standalone/chunks/{chart-view-DEwGTt9d.js → chart-view-8xEKTUJV.js} +1 -1
  149. package/dist-standalone/chunks/{date-picker-field-CCQ9nWx0.js → date-picker-field-cdhxtVSG.js} +1 -1
  150. package/dist-standalone/chunks/{rich-editor-field-B9Nv1qia.js → rich-editor-field-47ov3Bny.js} +1 -1
  151. package/dist-standalone/lattice.css +1 -1
  152. package/dist-standalone/lattice.js +15 -15
  153. package/dist-standalone/manifest.json +5 -5
  154. package/package.json +1 -1
@@ -14,7 +14,7 @@ import { FormValuesProvider, useFormValues } from "../../form/hooks/values.js";
14
14
  import "../../form/lib/form-transport.js";
15
15
  import { useFormResolver } from "../../form/hooks/use-form-resolver.js";
16
16
  import { collectFields } from "../../form/lib/collect-fields.js";
17
- import { firstErrors } from "../../form/lib/field-errors.js";
17
+ import { errorKeyBelongsTo, firstErrors } from "../../form/lib/field-errors.js";
18
18
  import { useDebouncedCallback } from "../../lib/use-debounced-callback.js";
19
19
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
20
20
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -67,6 +67,7 @@ function ActionFormBody({ cancelLabel, componentRef, endpoint, extraData, fieldL
67
67
  const dispatch = useEffectDispatcher();
68
68
  const [errors, setErrors] = useState({});
69
69
  const [processing, setProcessing] = useState(false);
70
+ const [validating, setValidating] = useState(false);
70
71
  const request = useCallback((extraHeaders) => apiFetch(endpoint, {
71
72
  body: JSON.stringify({
72
73
  ...valuesRef.current,
@@ -106,6 +107,31 @@ function ActionFormBody({ cancelLabel, componentRef, endpoint, extraData, fieldL
106
107
  const validate = useCallback((field) => {
107
108
  if (precognitive) runValidation(field);
108
109
  }, [precognitive, runValidation]);
110
+ const touch = useCallback(() => {}, []);
111
+ const validateFields = useCallback((fields, options) => {
112
+ setValidating(true);
113
+ request({
114
+ Precognition: "true",
115
+ "Precognition-Validate-Only": fields.join(",")
116
+ }).then(async (response) => {
117
+ if (response.status === 422) {
118
+ const body = await response.json();
119
+ setErrors((current) => ({
120
+ ...current,
121
+ ...firstErrors(body.errors)
122
+ }));
123
+ options?.onValidationError?.();
124
+ return;
125
+ }
126
+ if (!response.ok) {
127
+ options?.onValidationError?.();
128
+ return;
129
+ }
130
+ const cleared = fields.filter((field) => !field.includes("*"));
131
+ setErrors((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !cleared.some((name) => errorKeyBelongsTo(key, name)))));
132
+ options?.onSuccess?.();
133
+ }).catch(() => options?.onValidationError?.()).finally(() => setValidating(false));
134
+ }, [request]);
109
135
  const submit = useCallback(() => {
110
136
  setProcessing(true);
111
137
  request().then(async (response) => {
@@ -132,7 +158,10 @@ function ActionFormBody({ cancelLabel, componentRef, endpoint, extraData, fieldL
132
158
  fieldLabels,
133
159
  precognitive,
134
160
  processing,
135
- validate
161
+ touch,
162
+ validate,
163
+ validateFields,
164
+ validating
136
165
  }), [
137
166
  clearErrors,
138
167
  componentRef,
@@ -141,7 +170,10 @@ function ActionFormBody({ cancelLabel, componentRef, endpoint, extraData, fieldL
141
170
  fieldLabels,
142
171
  precognitive,
143
172
  processing,
144
- validate
173
+ touch,
174
+ validate,
175
+ validateFields,
176
+ validating
145
177
  ]),
146
178
  children: /* @__PURE__ */ jsxs("form", {
147
179
  className: "flex flex-col gap-6",
@@ -162,9 +194,9 @@ function ActionFormBody({ cancelLabel, componentRef, endpoint, extraData, fieldL
162
194
  disabled: processing,
163
195
  onClick: onClose,
164
196
  type: "button",
165
- variant: "ghost",
197
+ emphasis: "ghost",
166
198
  children: cancelLabel
167
- }), /* @__PURE__ */ jsxs(Button, {
199
+ }), formNode.props?.submitButton !== false && /* @__PURE__ */ jsxs(Button, {
168
200
  "data-test": "action-form-submit",
169
201
  disabled: processing,
170
202
  type: "submit",
@@ -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, 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
+ {"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 errorKeyBelongsTo,\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 const [validating, setValidating] = 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 touch = useCallback(() => {}, []);\n\n const validateFields = useCallback(\n (fields: string[], options?: { onSuccess?: () => void; onValidationError?: () => void }) => {\n setValidating(true);\n\n void request({ Precognition: \"true\", \"Precognition-Validate-Only\": fields.join(\",\") })\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 options?.onValidationError?.();\n\n return;\n }\n\n if (!response.ok) {\n options?.onValidationError?.();\n\n return;\n }\n\n const cleared = fields.filter((field) => !field.includes(\"*\"));\n setErrors((current) =>\n Object.fromEntries(\n Object.entries(current).filter(\n ([key]) => !cleared.some((name) => errorKeyBelongsTo(key, name)),\n ),\n ),\n );\n options?.onSuccess?.();\n })\n .catch(() => options?.onValidationError?.())\n .finally(() => setValidating(false));\n },\n [request],\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 touch,\n validate,\n validateFields,\n validating,\n }),\n [\n clearErrors,\n componentRef,\n endpoint,\n errors,\n fieldLabels,\n precognitive,\n processing,\n touch,\n validate,\n validateFields,\n validating,\n ],\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 emphasis=\"ghost\"\n >\n {cancelLabel}\n </Button>\n\n {formNode.props?.submitButton !== false && (\n <Button data-test=\"action-form-submit\" disabled={processing} type=\"submit\">\n {processing && <Spinner />}\n {submitLabel}\n </Button>\n )}\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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"}
@@ -29,7 +29,7 @@ var ActionGroupComponent = ({ children, node }) => {
29
29
  "data-test": nodeIdentity(node),
30
30
  size: "icon",
31
31
  type: "button",
32
- variant: "ghost",
32
+ emphasis: "ghost",
33
33
  children: /* @__PURE__ */ jsx(Icon, {
34
34
  name: "more-horizontal",
35
35
  "aria-hidden": "true",
@@ -1 +1 @@
1
- {"version":3,"file":"action-group.js","names":[],"sources":["../../../resources/js/action/components/action-group.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport { Button } from \"@lattice-php/lattice/ui/button\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuTrigger,\n} from \"@lattice-php/lattice/ui/dropdown-menu\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { ActionMenuProvider } from \"@lattice-php/lattice/ui/action-menu-context\";\n\nconst ActionGroupComponent: RendererComponent<\"action.group\"> = ({ children, node }) => {\n const { t } = useT(\"lattice\");\n const label = node.props.label ?? t(\"common.action-group.label\", \"Actions\");\n const orientation = node.props.orientation;\n\n if (orientation) {\n return (\n <div\n aria-label={label}\n className={cn(\n \"inline-flex max-w-full gap-1\",\n orientation === \"vertical\" ? \"flex-col items-stretch\" : \"flex-row flex-wrap items-center\",\n )}\n data-lattice-component={node.id}\n role=\"group\"\n >\n {children}\n </div>\n );\n }\n\n return (\n <div className=\"inline-flex\" data-lattice-component={node.id}>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n aria-label={label}\n className=\"size-lt-control-sm text-lt-muted-fg shadow-none hover:text-lt-fg\"\n data-test={nodeIdentity(node)}\n size=\"icon\"\n type=\"button\"\n variant=\"ghost\"\n >\n <Icon name=\"more-horizontal\" aria-hidden=\"true\" className=\"size-lt-icon-md\" />\n </Button>\n </DropdownMenuTrigger>\n\n <DropdownMenuContent\n align=\"end\"\n aria-label={label}\n className=\"min-w-44 gap-0.5 p-1.5\"\n sideOffset={8}\n >\n <ActionMenuProvider>{children}</ActionMenuProvider>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n );\n};\n\nexport default ActionGroupComponent;\n"],"mappings":";;;;;;;;;AAaA,IAAM,wBAA2D,EAAE,UAAU,WAAW;CACtF,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,QAAQ,KAAK,MAAM,SAAS,EAAE,6BAA6B,SAAS;CAC1E,MAAM,cAAc,KAAK,MAAM;CAE/B,IAAI,aACF,OACE,oBAAC,OAAD;EACE,cAAY;EACZ,WAAW,GACT,gCACA,gBAAgB,aAAa,2BAA2B,iCAC1D;EACA,0BAAwB,KAAK;EAC7B,MAAK;EAEJ;CACE,CAAA;CAIT,OACE,oBAAC,OAAD;EAAK,WAAU;EAAc,0BAAwB,KAAK;YACxD,qBAAC,cAAD,EAAA,UAAA,CACE,oBAAC,qBAAD;GAAqB,SAAA;aACnB,oBAAC,QAAD;IACE,cAAY;IACZ,WAAU;IACV,aAAW,aAAa,IAAI;IAC5B,MAAK;IACL,MAAK;IACL,SAAQ;cAER,oBAAC,MAAD;KAAM,MAAK;KAAkB,eAAY;KAAO,WAAU;IAAmB,CAAA;GACvE,CAAA;EACW,CAAA,GAErB,oBAAC,qBAAD;GACE,OAAM;GACN,cAAY;GACZ,WAAU;GACV,YAAY;aAEZ,oBAAC,oBAAD,EAAqB,SAA6B,CAAA;EAC/B,CAAA,CACT,EAAA,CAAA;CACX,CAAA;AAET"}
1
+ {"version":3,"file":"action-group.js","names":[],"sources":["../../../resources/js/action/components/action-group.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport { Button } from \"@lattice-php/lattice/ui/button\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuTrigger,\n} from \"@lattice-php/lattice/ui/dropdown-menu\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { ActionMenuProvider } from \"@lattice-php/lattice/ui/action-menu-context\";\n\nconst ActionGroupComponent: RendererComponent<\"action.group\"> = ({ children, node }) => {\n const { t } = useT(\"lattice\");\n const label = node.props.label ?? t(\"common.action-group.label\", \"Actions\");\n const orientation = node.props.orientation;\n\n if (orientation) {\n return (\n <div\n aria-label={label}\n className={cn(\n \"inline-flex max-w-full gap-1\",\n orientation === \"vertical\" ? \"flex-col items-stretch\" : \"flex-row flex-wrap items-center\",\n )}\n data-lattice-component={node.id}\n role=\"group\"\n >\n {children}\n </div>\n );\n }\n\n return (\n <div className=\"inline-flex\" data-lattice-component={node.id}>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n aria-label={label}\n className=\"size-lt-control-sm text-lt-muted-fg shadow-none hover:text-lt-fg\"\n data-test={nodeIdentity(node)}\n size=\"icon\"\n type=\"button\"\n emphasis=\"ghost\"\n >\n <Icon name=\"more-horizontal\" aria-hidden=\"true\" className=\"size-lt-icon-md\" />\n </Button>\n </DropdownMenuTrigger>\n\n <DropdownMenuContent\n align=\"end\"\n aria-label={label}\n className=\"min-w-44 gap-0.5 p-1.5\"\n sideOffset={8}\n >\n <ActionMenuProvider>{children}</ActionMenuProvider>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n );\n};\n\nexport default ActionGroupComponent;\n"],"mappings":";;;;;;;;;AAaA,IAAM,wBAA2D,EAAE,UAAU,WAAW;CACtF,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,QAAQ,KAAK,MAAM,SAAS,EAAE,6BAA6B,SAAS;CAC1E,MAAM,cAAc,KAAK,MAAM;CAE/B,IAAI,aACF,OACE,oBAAC,OAAD;EACE,cAAY;EACZ,WAAW,GACT,gCACA,gBAAgB,aAAa,2BAA2B,iCAC1D;EACA,0BAAwB,KAAK;EAC7B,MAAK;EAEJ;CACE,CAAA;CAIT,OACE,oBAAC,OAAD;EAAK,WAAU;EAAc,0BAAwB,KAAK;YACxD,qBAAC,cAAD,EAAA,UAAA,CACE,oBAAC,qBAAD;GAAqB,SAAA;aACnB,oBAAC,QAAD;IACE,cAAY;IACZ,WAAU;IACV,aAAW,aAAa,IAAI;IAC5B,MAAK;IACL,MAAK;IACL,UAAS;cAET,oBAAC,MAAD;KAAM,MAAK;KAAkB,eAAY;KAAO,WAAU;IAAmB,CAAA;GACvE,CAAA;EACW,CAAA,GAErB,oBAAC,qBAAD;GACE,OAAM;GACN,cAAY;GACZ,WAAU;GACV,YAAY;aAEZ,oBAAC,oBAAD,EAAqB,SAA6B,CAAA;EAC/B,CAAA,CACT,EAAA,CAAA;CACX,CAAA;AAET"}
@@ -12,7 +12,7 @@ var ActionComponent = ({ node }) => {
12
12
  const icon = node.props.icon;
13
13
  const label = actionLabel(node);
14
14
  const isMenuItem = useActionMenu();
15
- const variant = node.props.variant ?? "default";
15
+ const { variant, emphasis } = node.props;
16
16
  const { processing, requestSubmit, overlays } = useAction(node);
17
17
  const testId = node.key ?? prefixedTestId("action", node.id);
18
18
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Button, {
@@ -22,7 +22,8 @@ var ActionComponent = ({ node }) => {
22
22
  disabled: processing || !endpoint,
23
23
  onClick: requestSubmit,
24
24
  type: "button",
25
- variant: isMenuItem ? "ghost" : variant,
25
+ emphasis: isMenuItem ? "ghost" : emphasis,
26
+ variant: isMenuItem ? null : variant,
26
27
  children: [processing ? /* @__PURE__ */ jsx(Spinner, { className: isMenuItem ? "size-lt-icon-sm" : void 0 }) : icon && /* @__PURE__ */ jsx(IconRenderer, {
27
28
  className: isMenuItem ? "size-lt-icon-sm" : "size-lt-icon-md",
28
29
  icon
@@ -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\";\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
+ {"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, emphasis } = node.props;\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 emphasis={isMenuItem ? \"ghost\" : emphasis}\n variant={isMenuItem ? null : 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,EAAE,SAAS,aAAa,KAAK;CACnC,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,UAAU,aAAa,UAAU;EACjC,SAAS,aAAa,OAAO;YAR/B,CAUG,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"}
@@ -21,7 +21,7 @@ function useAction(node) {
21
21
  const componentRef = node.props.ref ?? "";
22
22
  const method = node.props.method ?? "post";
23
23
  const label = actionLabel(node);
24
- const variant = node.props.variant ?? "default";
24
+ const { variant, emphasis } = node.props;
25
25
  const confirmation = node.props.confirmation;
26
26
  const inlineForm = node.props.form;
27
27
  const lazyForm = node.props.lazyForm === true;
@@ -71,6 +71,7 @@ function useAction(node) {
71
71
  confirmLabel: confirmationConfirmLabel,
72
72
  cancelLabel: confirmationCancelLabel,
73
73
  confirmVariant: variant,
74
+ confirmEmphasis: emphasis,
74
75
  processing,
75
76
  confirmDisabled: !endpoint,
76
77
  onConfirm: () => void submit(),
@@ -1 +1 @@
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"}
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, emphasis } = node.props;\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 confirmEmphasis={emphasis}\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,EAAE,SAAS,aAAa,KAAK;CACnC,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;CAwC7E,OAAO;EAAE;EAAY;EAAe,UArClC,qBAAA,YAAA,EAAA,UAAA,CACG,gBAAgB,gBACf,oBAAC,eAAD;GACE,OAAO;GACP,aAAa,aAAa,eAAe,KAAA;GACzC,cAAc;GACd,aAAa;GACb,gBAAgB;GAChB,iBAAiB;GACL;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"}
@@ -8,6 +8,6 @@ export type UseAppearanceReturn = {
8
8
  };
9
9
  export declare function isAppearance(value: unknown): value is Appearance;
10
10
  export declare function updateAppearance(mode: Appearance): void;
11
- export declare function initializeTheme(): void;
11
+ export declare function initializeAppearance(): void;
12
12
  export declare function useAppearance(): UseAppearanceReturn;
13
13
  export {};
@@ -28,7 +28,7 @@ var isDarkMode = (appearance) => {
28
28
  return appearance === "dark" || appearance === "system" && prefersDark();
29
29
  };
30
30
  var getSystemAppearance = () => prefersDark() ? "dark" : "light";
31
- var applyTheme = (appearance) => {
31
+ var applyAppearance = (appearance) => {
32
32
  if (typeof document === "undefined") return;
33
33
  const isDark = isDarkMode(appearance);
34
34
  document.documentElement.classList.toggle("dark", isDark);
@@ -43,26 +43,26 @@ var mediaQuery = () => {
43
43
  if (typeof window === "undefined") return null;
44
44
  return window.matchMedia("(prefers-color-scheme: dark)");
45
45
  };
46
- var handleSystemThemeChange = () => {
47
- applyTheme(currentAppearance);
46
+ var handleSystemAppearanceChange = () => {
47
+ applyAppearance(currentAppearance);
48
48
  notify();
49
49
  };
50
50
  function updateAppearance(mode) {
51
51
  currentAppearance = mode;
52
52
  localStorage.setItem("appearance", mode);
53
53
  setCookie("appearance", mode);
54
- applyTheme(mode);
54
+ applyAppearance(mode);
55
55
  notify();
56
56
  }
57
- function initializeTheme() {
57
+ function initializeAppearance() {
58
58
  if (typeof window === "undefined") return;
59
59
  if (!localStorage.getItem("appearance")) {
60
60
  localStorage.setItem("appearance", "system");
61
61
  setCookie("appearance", "system");
62
62
  }
63
63
  currentAppearance = getStoredAppearance();
64
- applyTheme(currentAppearance);
65
- mediaQuery()?.addEventListener("change", handleSystemThemeChange);
64
+ applyAppearance(currentAppearance);
65
+ mediaQuery()?.addEventListener("change", handleSystemAppearanceChange);
66
66
  }
67
67
  function useAppearance() {
68
68
  const appearance = useSyncExternalStore(subscribe, () => currentAppearance, () => "system");
@@ -74,6 +74,6 @@ function useAppearance() {
74
74
  };
75
75
  }
76
76
  //#endregion
77
- export { initializeTheme, isAppearance, updateAppearance, useAppearance };
77
+ export { initializeAppearance, isAppearance, updateAppearance, useAppearance };
78
78
 
79
79
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../resources/js/appearance/index.ts"],"sourcesContent":["import { useSyncExternalStore } from \"react\";\n\nconst appearances = [\"light\", \"dark\", \"system\"] as const;\n\nexport type Appearance = (typeof appearances)[number];\n\nexport type ResolvedAppearance = \"light\" | \"dark\";\n\nexport type UseAppearanceReturn = {\n readonly appearance: Appearance;\n readonly resolvedAppearance: ResolvedAppearance;\n readonly updateAppearance: (mode: Appearance) => void;\n};\n\nconst listeners = new Set<() => void>();\nlet currentAppearance: Appearance = \"system\";\n\nexport function isAppearance(value: unknown): value is Appearance {\n return appearances.some((appearance) => appearance === value);\n}\n\nconst prefersDark = (): boolean => {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n};\n\nconst setCookie = (name: string, value: string, days = 365): void => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n const maxAge = days * 24 * 60 * 60;\n document.cookie = `${name}=${value};path=/;max-age=${maxAge};SameSite=Lax`;\n};\n\nconst getStoredAppearance = (): Appearance => {\n if (typeof window === \"undefined\") {\n return \"system\";\n }\n\n const stored = localStorage.getItem(\"appearance\");\n\n return isAppearance(stored) ? stored : \"system\";\n};\n\nconst isDarkMode = (appearance: Appearance): boolean => {\n return appearance === \"dark\" || (appearance === \"system\" && prefersDark());\n};\n\nconst getSystemAppearance = (): ResolvedAppearance => (prefersDark() ? \"dark\" : \"light\");\n\nconst applyTheme = (appearance: Appearance): void => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n const isDark = isDarkMode(appearance);\n\n document.documentElement.classList.toggle(\"dark\", isDark);\n document.documentElement.style.colorScheme = isDark ? \"dark\" : \"light\";\n};\n\nconst subscribe = (callback: () => void) => {\n listeners.add(callback);\n\n return () => listeners.delete(callback);\n};\n\nconst notify = (): void => listeners.forEach((listener) => listener());\n\nconst mediaQuery = (): MediaQueryList | null => {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n return window.matchMedia(\"(prefers-color-scheme: dark)\");\n};\n\nconst handleSystemThemeChange = (): void => {\n applyTheme(currentAppearance);\n notify();\n};\n\nexport function updateAppearance(mode: Appearance): void {\n currentAppearance = mode;\n\n localStorage.setItem(\"appearance\", mode);\n\n setCookie(\"appearance\", mode);\n\n applyTheme(mode);\n notify();\n}\n\nexport function initializeTheme(): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n if (!localStorage.getItem(\"appearance\")) {\n localStorage.setItem(\"appearance\", \"system\");\n setCookie(\"appearance\", \"system\");\n }\n\n currentAppearance = getStoredAppearance();\n applyTheme(currentAppearance);\n\n mediaQuery()?.addEventListener(\"change\", handleSystemThemeChange);\n}\n\nexport function useAppearance(): UseAppearanceReturn {\n const appearance: Appearance = useSyncExternalStore(\n subscribe,\n () => currentAppearance,\n () => \"system\",\n );\n const systemAppearance: ResolvedAppearance = useSyncExternalStore(\n subscribe,\n getSystemAppearance,\n () => \"light\",\n );\n\n const resolvedAppearance: ResolvedAppearance =\n appearance === \"system\" ? systemAppearance : appearance;\n\n return { appearance, resolvedAppearance, updateAppearance } as const;\n}\n"],"mappings":";;AAEA,IAAM,cAAc;CAAC;CAAS;CAAQ;AAAQ;AAY9C,IAAM,4BAAY,IAAI,IAAgB;AACtC,IAAI,oBAAgC;AAEpC,SAAgB,aAAa,OAAqC;CAChE,OAAO,YAAY,MAAM,eAAe,eAAe,KAAK;AAC9D;AAEA,IAAM,oBAA6B;CACjC,IAAI,OAAO,WAAW,aACpB,OAAO;CAGT,OAAO,OAAO,WAAW,8BAA8B,EAAE;AAC3D;AAEA,IAAM,aAAa,MAAc,OAAe,OAAO,QAAc;CACnE,IAAI,OAAO,aAAa,aACtB;CAGF,MAAM,SAAS,OAAO,KAAK,KAAK;CAChC,SAAS,SAAS,GAAG,KAAK,GAAG,MAAM,kBAAkB,OAAO;AAC9D;AAEA,IAAM,4BAAwC;CAC5C,IAAI,OAAO,WAAW,aACpB,OAAO;CAGT,MAAM,SAAS,aAAa,QAAQ,YAAY;CAEhD,OAAO,aAAa,MAAM,IAAI,SAAS;AACzC;AAEA,IAAM,cAAc,eAAoC;CACtD,OAAO,eAAe,UAAW,eAAe,YAAY,YAAY;AAC1E;AAEA,IAAM,4BAAiD,YAAY,IAAI,SAAS;AAEhF,IAAM,cAAc,eAAiC;CACnD,IAAI,OAAO,aAAa,aACtB;CAGF,MAAM,SAAS,WAAW,UAAU;CAEpC,SAAS,gBAAgB,UAAU,OAAO,QAAQ,MAAM;CACxD,SAAS,gBAAgB,MAAM,cAAc,SAAS,SAAS;AACjE;AAEA,IAAM,aAAa,aAAyB;CAC1C,UAAU,IAAI,QAAQ;CAEtB,aAAa,UAAU,OAAO,QAAQ;AACxC;AAEA,IAAM,eAAqB,UAAU,SAAS,aAAa,SAAS,CAAC;AAErE,IAAM,mBAA0C;CAC9C,IAAI,OAAO,WAAW,aACpB,OAAO;CAGT,OAAO,OAAO,WAAW,8BAA8B;AACzD;AAEA,IAAM,gCAAsC;CAC1C,WAAW,iBAAiB;CAC5B,OAAO;AACT;AAEA,SAAgB,iBAAiB,MAAwB;CACvD,oBAAoB;CAEpB,aAAa,QAAQ,cAAc,IAAI;CAEvC,UAAU,cAAc,IAAI;CAE5B,WAAW,IAAI;CACf,OAAO;AACT;AAEA,SAAgB,kBAAwB;CACtC,IAAI,OAAO,WAAW,aACpB;CAGF,IAAI,CAAC,aAAa,QAAQ,YAAY,GAAG;EACvC,aAAa,QAAQ,cAAc,QAAQ;EAC3C,UAAU,cAAc,QAAQ;CAClC;CAEA,oBAAoB,oBAAoB;CACxC,WAAW,iBAAiB;CAE5B,WAAW,GAAG,iBAAiB,UAAU,uBAAuB;AAClE;AAEA,SAAgB,gBAAqC;CACnD,MAAM,aAAyB,qBAC7B,iBACM,yBACA,QACR;CACA,MAAM,mBAAuC,qBAC3C,WACA,2BACM,OACR;CAKA,OAAO;EAAE;EAAY,oBAFnB,eAAe,WAAW,mBAAmB;EAEN;CAAiB;AAC5D"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../resources/js/appearance/index.ts"],"sourcesContent":["import { useSyncExternalStore } from \"react\";\n\nconst appearances = [\"light\", \"dark\", \"system\"] as const;\n\nexport type Appearance = (typeof appearances)[number];\n\nexport type ResolvedAppearance = \"light\" | \"dark\";\n\nexport type UseAppearanceReturn = {\n readonly appearance: Appearance;\n readonly resolvedAppearance: ResolvedAppearance;\n readonly updateAppearance: (mode: Appearance) => void;\n};\n\nconst listeners = new Set<() => void>();\nlet currentAppearance: Appearance = \"system\";\n\nexport function isAppearance(value: unknown): value is Appearance {\n return appearances.some((appearance) => appearance === value);\n}\n\nconst prefersDark = (): boolean => {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n};\n\nconst setCookie = (name: string, value: string, days = 365): void => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n const maxAge = days * 24 * 60 * 60;\n document.cookie = `${name}=${value};path=/;max-age=${maxAge};SameSite=Lax`;\n};\n\nconst getStoredAppearance = (): Appearance => {\n if (typeof window === \"undefined\") {\n return \"system\";\n }\n\n const stored = localStorage.getItem(\"appearance\");\n\n return isAppearance(stored) ? stored : \"system\";\n};\n\nconst isDarkMode = (appearance: Appearance): boolean => {\n return appearance === \"dark\" || (appearance === \"system\" && prefersDark());\n};\n\nconst getSystemAppearance = (): ResolvedAppearance => (prefersDark() ? \"dark\" : \"light\");\n\nconst applyAppearance = (appearance: Appearance): void => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n const isDark = isDarkMode(appearance);\n\n document.documentElement.classList.toggle(\"dark\", isDark);\n document.documentElement.style.colorScheme = isDark ? \"dark\" : \"light\";\n};\n\nconst subscribe = (callback: () => void) => {\n listeners.add(callback);\n\n return () => listeners.delete(callback);\n};\n\nconst notify = (): void => listeners.forEach((listener) => listener());\n\nconst mediaQuery = (): MediaQueryList | null => {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n return window.matchMedia(\"(prefers-color-scheme: dark)\");\n};\n\nconst handleSystemAppearanceChange = (): void => {\n applyAppearance(currentAppearance);\n notify();\n};\n\nexport function updateAppearance(mode: Appearance): void {\n currentAppearance = mode;\n\n localStorage.setItem(\"appearance\", mode);\n\n setCookie(\"appearance\", mode);\n\n applyAppearance(mode);\n notify();\n}\n\nexport function initializeAppearance(): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n if (!localStorage.getItem(\"appearance\")) {\n localStorage.setItem(\"appearance\", \"system\");\n setCookie(\"appearance\", \"system\");\n }\n\n currentAppearance = getStoredAppearance();\n applyAppearance(currentAppearance);\n\n mediaQuery()?.addEventListener(\"change\", handleSystemAppearanceChange);\n}\n\nexport function useAppearance(): UseAppearanceReturn {\n const appearance: Appearance = useSyncExternalStore(\n subscribe,\n () => currentAppearance,\n () => \"system\",\n );\n const systemAppearance: ResolvedAppearance = useSyncExternalStore(\n subscribe,\n getSystemAppearance,\n () => \"light\",\n );\n\n const resolvedAppearance: ResolvedAppearance =\n appearance === \"system\" ? systemAppearance : appearance;\n\n return { appearance, resolvedAppearance, updateAppearance } as const;\n}\n"],"mappings":";;AAEA,IAAM,cAAc;CAAC;CAAS;CAAQ;AAAQ;AAY9C,IAAM,4BAAY,IAAI,IAAgB;AACtC,IAAI,oBAAgC;AAEpC,SAAgB,aAAa,OAAqC;CAChE,OAAO,YAAY,MAAM,eAAe,eAAe,KAAK;AAC9D;AAEA,IAAM,oBAA6B;CACjC,IAAI,OAAO,WAAW,aACpB,OAAO;CAGT,OAAO,OAAO,WAAW,8BAA8B,EAAE;AAC3D;AAEA,IAAM,aAAa,MAAc,OAAe,OAAO,QAAc;CACnE,IAAI,OAAO,aAAa,aACtB;CAGF,MAAM,SAAS,OAAO,KAAK,KAAK;CAChC,SAAS,SAAS,GAAG,KAAK,GAAG,MAAM,kBAAkB,OAAO;AAC9D;AAEA,IAAM,4BAAwC;CAC5C,IAAI,OAAO,WAAW,aACpB,OAAO;CAGT,MAAM,SAAS,aAAa,QAAQ,YAAY;CAEhD,OAAO,aAAa,MAAM,IAAI,SAAS;AACzC;AAEA,IAAM,cAAc,eAAoC;CACtD,OAAO,eAAe,UAAW,eAAe,YAAY,YAAY;AAC1E;AAEA,IAAM,4BAAiD,YAAY,IAAI,SAAS;AAEhF,IAAM,mBAAmB,eAAiC;CACxD,IAAI,OAAO,aAAa,aACtB;CAGF,MAAM,SAAS,WAAW,UAAU;CAEpC,SAAS,gBAAgB,UAAU,OAAO,QAAQ,MAAM;CACxD,SAAS,gBAAgB,MAAM,cAAc,SAAS,SAAS;AACjE;AAEA,IAAM,aAAa,aAAyB;CAC1C,UAAU,IAAI,QAAQ;CAEtB,aAAa,UAAU,OAAO,QAAQ;AACxC;AAEA,IAAM,eAAqB,UAAU,SAAS,aAAa,SAAS,CAAC;AAErE,IAAM,mBAA0C;CAC9C,IAAI,OAAO,WAAW,aACpB,OAAO;CAGT,OAAO,OAAO,WAAW,8BAA8B;AACzD;AAEA,IAAM,qCAA2C;CAC/C,gBAAgB,iBAAiB;CACjC,OAAO;AACT;AAEA,SAAgB,iBAAiB,MAAwB;CACvD,oBAAoB;CAEpB,aAAa,QAAQ,cAAc,IAAI;CAEvC,UAAU,cAAc,IAAI;CAE5B,gBAAgB,IAAI;CACpB,OAAO;AACT;AAEA,SAAgB,uBAA6B;CAC3C,IAAI,OAAO,WAAW,aACpB;CAGF,IAAI,CAAC,aAAa,QAAQ,YAAY,GAAG;EACvC,aAAa,QAAQ,cAAc,QAAQ;EAC3C,UAAU,cAAc,QAAQ;CAClC;CAEA,oBAAoB,oBAAoB;CACxC,gBAAgB,iBAAiB;CAEjC,WAAW,GAAG,iBAAiB,UAAU,4BAA4B;AACvE;AAEA,SAAgB,gBAAqC;CACnD,MAAM,aAAyB,qBAC7B,iBACM,yBACA,QACR;CACA,MAAM,mBAAuC,qBAC3C,WACA,2BACM,OACR;CAKA,OAAO;EAAE;EAAY,oBAFnB,eAAe,WAAW,mBAAmB;EAEN;CAAiB;AAC5D"}
@@ -24,7 +24,7 @@ function PromptInput({ onSubmit, disabled = false, placeholder }) {
24
24
  className: "flex items-end gap-2 border-t border-lt-border p-3",
25
25
  children: [/* @__PURE__ */ jsx("textarea", {
26
26
  "aria-label": t("common.chat.input-label", "Message input"),
27
- className: cn("min-h-[2.5rem] flex-1 resize-none rounded-lt-sm border border-lt-input bg-lt-bg px-3 py-2 text-sm text-lt-fg placeholder:text-lt-muted-fg focus:outline-none focus:ring-1 focus:ring-lt-ring disabled:opacity-50"),
27
+ className: cn("min-h-[2.5rem] flex-1 resize-none rounded-lt-sm border border-lt-input bg-lt-bg px-3 py-2 text-sm text-lt-fg placeholder:text-lt-muted-fg focus:outline-none focus:ring-[length:var(--lt-ring-width)] focus:ring-lt-ring/50 disabled:bg-lt-disabled disabled:text-lt-disabled-fg"),
28
28
  "data-test": testIdentity("chat-input"),
29
29
  disabled,
30
30
  onChange: (e) => setValue(e.target.value),
@@ -38,7 +38,6 @@ function PromptInput({ onSubmit, disabled = false, placeholder }) {
38
38
  onClick: submit,
39
39
  size: "sm",
40
40
  type: "button",
41
- variant: "default",
42
41
  children: t("common.chat.send", "Send")
43
42
  })]
44
43
  });
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-input.js","names":[],"sources":["../../../resources/js/chat/components/prompt-input.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport type { KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { Button } from \"@lattice-php/lattice/ui/button\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\n\ntype PromptInputProps = {\n onSubmit: (text: string) => void;\n disabled?: boolean;\n placeholder?: string;\n};\n\nexport function PromptInput({\n onSubmit,\n disabled = false,\n placeholder,\n}: PromptInputProps): ReactNode {\n const { t } = useT(\"lattice\");\n const [value, setValue] = useState(\"\");\n\n function submit(): void {\n const trimmed = value.trim();\n if (trimmed === \"\" || disabled) {\n return;\n }\n\n onSubmit(trimmed);\n setValue(\"\");\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLTextAreaElement>): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n submit();\n }\n }\n\n return (\n <div className=\"flex items-end gap-2 border-t border-lt-border p-3\">\n <textarea\n aria-label={t(\"common.chat.input-label\", \"Message input\")}\n className={cn(\n \"min-h-[2.5rem] flex-1 resize-none rounded-lt-sm border border-lt-input bg-lt-bg px-3 py-2 text-sm text-lt-fg placeholder:text-lt-muted-fg focus:outline-none focus:ring-1 focus:ring-lt-ring disabled:opacity-50\",\n )}\n data-test={testIdentity(\"chat-input\")}\n disabled={disabled}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n rows={1}\n value={value}\n />\n <Button\n data-test={testIdentity(\"chat-send\")}\n disabled={disabled}\n onClick={submit}\n size=\"sm\"\n type=\"button\"\n variant=\"default\"\n >\n {t(\"common.chat.send\", \"Send\")}\n </Button>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAaA,SAAgB,YAAY,EAC1B,UACA,WAAW,OACX,eAC8B;CAC9B,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CAErC,SAAS,SAAe;EACtB,MAAM,UAAU,MAAM,KAAK;EAC3B,IAAI,YAAY,MAAM,UACpB;EAGF,SAAS,OAAO;EAChB,SAAS,EAAE;CACb;CAEA,SAAS,cAAc,OAAiD;EACtE,IAAI,MAAM,QAAQ,WAAW,CAAC,MAAM,UAAU;GAC5C,MAAM,eAAe;GACrB,OAAO;EACT;CACF;CAEA,OACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,YAAD;GACE,cAAY,EAAE,2BAA2B,eAAe;GACxD,WAAW,GACT,kNACF;GACA,aAAW,aAAa,YAAY;GAC1B;GACV,WAAW,MAAM,SAAS,EAAE,OAAO,KAAK;GACxC,WAAW;GACE;GACb,MAAM;GACC;EACR,CAAA,GACD,oBAAC,QAAD;GACE,aAAW,aAAa,WAAW;GACzB;GACV,SAAS;GACT,MAAK;GACL,MAAK;GACL,SAAQ;aAEP,EAAE,oBAAoB,MAAM;EACvB,CAAA,CACL;;AAET"}
1
+ {"version":3,"file":"prompt-input.js","names":[],"sources":["../../../resources/js/chat/components/prompt-input.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport type { KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { Button } from \"@lattice-php/lattice/ui/button\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\n\ntype PromptInputProps = {\n onSubmit: (text: string) => void;\n disabled?: boolean;\n placeholder?: string;\n};\n\nexport function PromptInput({\n onSubmit,\n disabled = false,\n placeholder,\n}: PromptInputProps): ReactNode {\n const { t } = useT(\"lattice\");\n const [value, setValue] = useState(\"\");\n\n function submit(): void {\n const trimmed = value.trim();\n if (trimmed === \"\" || disabled) {\n return;\n }\n\n onSubmit(trimmed);\n setValue(\"\");\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLTextAreaElement>): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n submit();\n }\n }\n\n return (\n <div className=\"flex items-end gap-2 border-t border-lt-border p-3\">\n <textarea\n aria-label={t(\"common.chat.input-label\", \"Message input\")}\n className={cn(\n \"min-h-[2.5rem] flex-1 resize-none rounded-lt-sm border border-lt-input bg-lt-bg px-3 py-2 text-sm text-lt-fg placeholder:text-lt-muted-fg focus:outline-none focus:ring-[length:var(--lt-ring-width)] focus:ring-lt-ring/50 disabled:bg-lt-disabled disabled:text-lt-disabled-fg\",\n )}\n data-test={testIdentity(\"chat-input\")}\n disabled={disabled}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n rows={1}\n value={value}\n />\n <Button\n data-test={testIdentity(\"chat-send\")}\n disabled={disabled}\n onClick={submit}\n size=\"sm\"\n type=\"button\"\n >\n {t(\"common.chat.send\", \"Send\")}\n </Button>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAaA,SAAgB,YAAY,EAC1B,UACA,WAAW,OACX,eAC8B;CAC9B,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CAErC,SAAS,SAAe;EACtB,MAAM,UAAU,MAAM,KAAK;EAC3B,IAAI,YAAY,MAAM,UACpB;EAGF,SAAS,OAAO;EAChB,SAAS,EAAE;CACb;CAEA,SAAS,cAAc,OAAiD;EACtE,IAAI,MAAM,QAAQ,WAAW,CAAC,MAAM,UAAU;GAC5C,MAAM,eAAe;GACrB,OAAO;EACT;CACF;CAEA,OACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,YAAD;GACE,cAAY,EAAE,2BAA2B,eAAe;GACxD,WAAW,GACT,kRACF;GACA,aAAW,aAAa,YAAY;GAC1B;GACV,WAAW,MAAM,SAAS,EAAE,OAAO,KAAK;GACxC,WAAW;GACE;GACb,MAAM;GACC;EACR,CAAA,GACD,oBAAC,QAAD;GACE,aAAW,aAAa,WAAW;GACzB;GACV,SAAS;GACT,MAAK;GACL,MAAK;aAEJ,EAAE,oBAAoB,MAAM;EACvB,CAAA,CACL;;AAET"}
@@ -1,4 +1,4 @@
1
- import { initializeTheme } from "./appearance/index.js";
1
+ import { initializeAppearance } from "./appearance/index.js";
2
2
  import { extendRegistry } from "./core/registry.js";
3
3
  import { setDefaultRegistry } from "./core/registry-context.js";
4
4
  import "./i18n/instance.js";
@@ -77,7 +77,7 @@ function createLatticeApp({ registry: registry$1, plugins, sprite, pages = {}, d
77
77
  }) : shell;
78
78
  }
79
79
  });
80
- initializeTheme();
80
+ initializeAppearance();
81
81
  return app;
82
82
  }
83
83
  //#endregion
@@ -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 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
+ {"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 { initializeAppearance } 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 initializeAppearance();\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,qBAAqB;CAErB,OAAO;AACT"}
@@ -1,6 +1,7 @@
1
- import { ButtonVariant } from '../../../types/generated.js';
2
- export declare function FormSubmitButton({ label, summaryLabel, variant, }: {
1
+ import { Emphasis, Variant } from '../../../types/generated.js';
2
+ export declare function FormSubmitButton({ label, summaryLabel, variant, emphasis, }: {
3
3
  label: string;
4
4
  summaryLabel: string;
5
- variant?: ButtonVariant;
5
+ variant?: Variant | null;
6
+ emphasis?: Emphasis | null;
6
7
  }): import("react").JSX.Element;
@@ -3,7 +3,7 @@ import { Spinner } from "../../../ui/spinner.js";
3
3
  import { useFormContext } from "../../hooks/context.js";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  //#region resources/js/form/components/base/submit-button.tsx
6
- function FormSubmitButton({ label, summaryLabel, variant = "default" }) {
6
+ function FormSubmitButton({ label, summaryLabel, variant, emphasis }) {
7
7
  const { componentId, errors, fieldLabels, processing } = useFormContext();
8
8
  const invalidFields = Object.entries(errors).filter(([, message]) => Boolean(message)).map(([name, message]) => ({
9
9
  label: fieldLabels[name] ?? name,
@@ -16,6 +16,7 @@ function FormSubmitButton({ label, summaryLabel, variant = "default" }) {
16
16
  "data-lattice-form": componentId,
17
17
  "data-test": "form-submit",
18
18
  disabled: processing || hasErrors,
19
+ emphasis,
19
20
  type: "submit",
20
21
  variant,
21
22
  children: [processing && /* @__PURE__ */ jsx(Spinner, {}), label]
@@ -1 +1 @@
1
- {"version":3,"file":"submit-button.js","names":[],"sources":["../../../../resources/js/form/components/base/submit-button.tsx"],"sourcesContent":["import { Button } from \"@lattice-php/lattice/ui/button\";\nimport { Spinner } from \"@lattice-php/lattice/ui/spinner\";\nimport type { ButtonVariant } from \"@lattice-php/lattice/types/generated\";\nimport { useFormContext } from \"@lattice-php/lattice/form/hooks/context\";\n\nexport function FormSubmitButton({\n label,\n summaryLabel,\n variant = \"default\",\n}: {\n label: string;\n summaryLabel: string;\n variant?: ButtonVariant;\n}) {\n const { componentId, errors, fieldLabels, processing } = useFormContext();\n\n const invalidFields = Object.entries(errors)\n .filter(([, message]) => Boolean(message))\n .map(([name, message]) => ({ label: fieldLabels[name] ?? name, message: message as string }));\n const hasErrors = invalidFields.length > 0;\n\n return (\n <span className=\"group relative inline-flex flex-col\">\n <Button\n data-lattice-form={componentId}\n data-test=\"form-submit\"\n disabled={processing || hasErrors}\n type=\"submit\"\n variant={variant}\n >\n {processing && <Spinner />}\n {label}\n </Button>\n\n {hasErrors && (\n <div\n className=\"pointer-events-none absolute bottom-full left-1/2 z-lt-popover mb-2 w-max max-w-xs -translate-x-1/2 rounded-lt border border-lt-border bg-lt-surface p-3 text-left text-sm opacity-0 shadow-lt-md transition-opacity group-hover:opacity-100\"\n role=\"tooltip\"\n >\n <p className=\"mb-1 font-medium text-lt-fg\">{summaryLabel}</p>\n <ul className=\"space-y-1\">\n {invalidFields.map((field) => (\n <li className=\"text-lt-muted-fg\" key={field.label}>\n <span className=\"font-medium text-lt-fg\">{field.label}</span> — {field.message}\n </li>\n ))}\n </ul>\n </div>\n )}\n </span>\n );\n}\n"],"mappings":";;;;;AAKA,SAAgB,iBAAiB,EAC/B,OACA,cACA,UAAU,aAKT;CACD,MAAM,EAAE,aAAa,QAAQ,aAAa,eAAe,eAAe;CAExE,MAAM,gBAAgB,OAAO,QAAQ,MAAM,EACxC,QAAQ,GAAG,aAAa,QAAQ,OAAO,CAAC,EACxC,KAAK,CAAC,MAAM,cAAc;EAAE,OAAO,YAAY,SAAS;EAAe;CAAkB,EAAE;CAC9F,MAAM,YAAY,cAAc,SAAS;CAEzC,OACE,qBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,qBAAC,QAAD;GACE,qBAAmB;GACnB,aAAU;GACV,UAAU,cAAc;GACxB,MAAK;GACI;aALX,CAOG,cAAc,oBAAC,SAAD,CAAU,CAAA,GACxB,KACK;MAEP,aACC,qBAAC,OAAD;GACE,WAAU;GACV,MAAK;aAFP,CAIE,oBAAC,KAAD;IAAG,WAAU;cAA+B;GAAgB,CAAA,GAC5D,oBAAC,MAAD;IAAI,WAAU;cACX,cAAc,KAAK,UAClB,qBAAC,MAAD;KAAI,WAAU;eAAd;MACE,oBAAC,QAAD;OAAM,WAAU;iBAA0B,MAAM;MAAY,CAAA;MAAC;MAAI,MAAM;KACrE;OAFkC,MAAM,KAExC,CACL;GACC,CAAA,CACD;IAEH;;AAEV"}
1
+ {"version":3,"file":"submit-button.js","names":[],"sources":["../../../../resources/js/form/components/base/submit-button.tsx"],"sourcesContent":["import { Button } from \"@lattice-php/lattice/ui/button\";\nimport { Spinner } from \"@lattice-php/lattice/ui/spinner\";\nimport type { Emphasis, Variant } from \"@lattice-php/lattice/types/generated\";\nimport { useFormContext } from \"@lattice-php/lattice/form/hooks/context\";\n\nexport function FormSubmitButton({\n label,\n summaryLabel,\n variant,\n emphasis,\n}: {\n label: string;\n summaryLabel: string;\n variant?: Variant | null;\n emphasis?: Emphasis | null;\n}) {\n const { componentId, errors, fieldLabels, processing } = useFormContext();\n\n const invalidFields = Object.entries(errors)\n .filter(([, message]) => Boolean(message))\n .map(([name, message]) => ({ label: fieldLabels[name] ?? name, message: message as string }));\n const hasErrors = invalidFields.length > 0;\n\n return (\n <span className=\"group relative inline-flex flex-col\">\n <Button\n data-lattice-form={componentId}\n data-test=\"form-submit\"\n disabled={processing || hasErrors}\n emphasis={emphasis}\n type=\"submit\"\n variant={variant}\n >\n {processing && <Spinner />}\n {label}\n </Button>\n\n {hasErrors && (\n <div\n className=\"pointer-events-none absolute bottom-full left-1/2 z-lt-popover mb-2 w-max max-w-xs -translate-x-1/2 rounded-lt border border-lt-border bg-lt-surface p-3 text-left text-sm opacity-0 shadow-lt-md transition-opacity group-hover:opacity-100\"\n role=\"tooltip\"\n >\n <p className=\"mb-1 font-medium text-lt-fg\">{summaryLabel}</p>\n <ul className=\"space-y-1\">\n {invalidFields.map((field) => (\n <li className=\"text-lt-muted-fg\" key={field.label}>\n <span className=\"font-medium text-lt-fg\">{field.label}</span> — {field.message}\n </li>\n ))}\n </ul>\n </div>\n )}\n </span>\n );\n}\n"],"mappings":";;;;;AAKA,SAAgB,iBAAiB,EAC/B,OACA,cACA,SACA,YAMC;CACD,MAAM,EAAE,aAAa,QAAQ,aAAa,eAAe,eAAe;CAExE,MAAM,gBAAgB,OAAO,QAAQ,MAAM,EACxC,QAAQ,GAAG,aAAa,QAAQ,OAAO,CAAC,EACxC,KAAK,CAAC,MAAM,cAAc;EAAE,OAAO,YAAY,SAAS;EAAe;CAAkB,EAAE;CAC9F,MAAM,YAAY,cAAc,SAAS;CAEzC,OACE,qBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,qBAAC,QAAD;GACE,qBAAmB;GACnB,aAAU;GACV,UAAU,cAAc;GACd;GACV,MAAK;GACI;aANX,CAQG,cAAc,oBAAC,SAAD,CAAU,CAAA,GACxB,KACK;MAEP,aACC,qBAAC,OAAD;GACE,WAAU;GACV,MAAK;aAFP,CAIE,oBAAC,KAAD;IAAG,WAAU;cAA+B;GAAgB,CAAA,GAC5D,oBAAC,MAAD;IAAI,WAAU;cACX,cAAc,KAAK,UAClB,qBAAC,MAAD;KAAI,WAAU;eAAd;MACE,oBAAC,QAAD;OAAM,WAAU;iBAA0B,MAAM;MAAY,CAAA;MAAC;MAAI,MAAM;KACrE;OAFkC,MAAM,KAExC,CACL;GACC,CAAA,CACD;IAEH;;AAEV"}
@@ -88,7 +88,7 @@ function DatePickerField({ mode, label, name, testId, value, min, max, step, dis
88
88
  disabled: disabled || readOnly,
89
89
  size: "icon",
90
90
  type: "button",
91
- variant: "secondary",
91
+ color: "secondary",
92
92
  children: /* @__PURE__ */ jsx(Icon, {
93
93
  name: "calendar",
94
94
  className: "size-lt-icon-md",
@@ -108,9 +108,9 @@ function DatePickerField({ mode, label, name, testId, value, min, max, step, dis
108
108
  children: [
109
109
  /* @__PURE__ */ jsx(Button, {
110
110
  ...api.getPrevTriggerProps(),
111
+ emphasis: "ghost",
111
112
  size: "icon",
112
113
  type: "button",
113
- variant: "ghost",
114
114
  children: /* @__PURE__ */ jsx(Icon, {
115
115
  name: "chevron-left",
116
116
  className: "size-lt-icon-md",
@@ -123,9 +123,9 @@ function DatePickerField({ mode, label, name, testId, value, min, max, step, dis
123
123
  }),
124
124
  /* @__PURE__ */ jsx(Button, {
125
125
  ...api.getNextTriggerProps(),
126
+ emphasis: "ghost",
126
127
  size: "icon",
127
128
  type: "button",
128
- variant: "ghost",
129
129
  children: /* @__PURE__ */ jsx(Icon, {
130
130
  name: "chevron-right",
131
131
  className: "size-lt-icon-md",
@@ -1 +1 @@
1
- {"version":3,"file":"date-picker-field.js","names":[],"sources":["../../../../resources/js/form/components/fields/date-picker-field.tsx"],"sourcesContent":["import type { DateValue } from \"@internationalized/date\";\nimport * as datePicker from \"@zag-js/date-picker\";\nimport { normalizeProps, useMachine } from \"@zag-js/react\";\nimport { useId, useMemo } from \"react\";\nimport { Button } from \"@lattice-php/lattice/ui/button\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { useLocale } from \"@lattice-php/lattice/i18n\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { Input } from \"@lattice-php/lattice/ui/input\";\nimport {\n formatDateDisplayValue,\n formatDateTimeDisplayValue,\n formatDateTimeValue,\n formatDateValue,\n formatTimeInputValue,\n parseDateDisplayValue,\n parseDateTimeDisplayValue,\n parseDateTimeValue,\n parseDateValue,\n} from \"./date-picker-value\";\nimport { TimePicker } from \"./time-picker\";\nimport { parseTimeString } from \"./time-picker-columns\";\n\nexport type DatePickerFieldProps = {\n mode: \"date\" | \"date-time\";\n label: string;\n name: string;\n testId: string;\n value: unknown;\n min?: string | null;\n max?: string | null;\n step?: number | null;\n disabled: boolean;\n readOnly: boolean;\n autoFocus?: boolean;\n tabIndex?: number | null;\n timezone?: string;\n onChange: (value: string) => void;\n onBlur?: () => void;\n};\n\nexport function DatePickerField({\n mode,\n label,\n name,\n testId,\n value,\n min,\n max,\n step,\n disabled,\n readOnly,\n autoFocus = false,\n tabIndex,\n timezone = \"UTC\",\n onChange,\n onBlur,\n}: DatePickerFieldProps) {\n const id = useId();\n const { locale } = useLocale();\n const selected = useMemo(\n () =>\n [mode === \"date\" ? parseDateValue(value) : parseDateTimeValue(value, timezone)].filter(\n Boolean,\n ) as DateValue[],\n [mode, timezone, value],\n );\n const service = useMachine(datePicker.machine, {\n id,\n name,\n value: selected.length > 0 ? selected : undefined,\n min: min ? parseDateValue(min) : undefined,\n max: max ? parseDateValue(max) : undefined,\n disabled,\n readOnly,\n locale,\n selectionMode: \"single\",\n timeZone: timezone,\n closeOnSelect: mode === \"date\",\n format(date) {\n return mode === \"date\"\n ? formatDateDisplayValue(date, locale)\n : formatDateTimeDisplayValue(date, locale, timezone);\n },\n parse(text) {\n return mode === \"date\"\n ? parseDateDisplayValue(text, locale)\n : parseDateTimeDisplayValue(text, locale, timezone);\n },\n onValueChange(details) {\n const next = details.value[0];\n\n onChange(mode === \"date\" ? formatDateValue(next) : formatDateTimeValue(next, timezone));\n },\n onOpenChange(details) {\n if (!details.open) {\n onBlur?.();\n }\n },\n });\n const api = datePicker.connect(service, normalizeProps);\n const { name: _inputName, onInput, ...inputProps } = api.getInputProps();\n const submittedValue =\n mode === \"date\" ? formatDateValue(selected[0]) : formatDateTimeValue(selected[0], timezone);\n\n return (\n <div {...api.getRootProps()} className={cn(\"relative\", api.open && \"z-lt-popover\")}>\n <input type=\"hidden\" name={name} value={submittedValue} data-test={`${testId}-value`} />\n <div {...api.getControlProps()} className=\"flex gap-2\">\n <Input\n {...inputProps}\n aria-label={label}\n autoFocus={autoFocus}\n data-test={testId}\n disabled={disabled}\n id={name}\n onInput={(event) => {\n onInput?.(event);\n\n if (mode !== \"date\") {\n return;\n }\n\n const normalized = normalizeDateInputValue(event.currentTarget.value);\n\n if (!normalized) {\n return;\n }\n\n const next = parseDateValue(normalized);\n\n if (!next) {\n return;\n }\n\n event.currentTarget.value = normalized;\n api.setValue([next]);\n event.currentTarget.value = formatDateDisplayValue(next, locale);\n onChange(formatDateValue(next));\n }}\n readOnly={readOnly}\n tabIndex={tabIndex ?? undefined}\n />\n <Button\n {...api.getTriggerProps()}\n aria-label={`Open ${label || name} calendar`}\n disabled={disabled || readOnly}\n size=\"icon\"\n type=\"button\"\n variant=\"secondary\"\n >\n <Icon name=\"calendar\" className=\"size-lt-icon-md\" aria-hidden=\"true\" />\n </Button>\n </div>\n {api.open ? (\n <div\n {...api.getPositionerProps()}\n className=\"absolute z-lt-popover mt-2 rounded-lt-sm border border-lt-border bg-lt-popover p-3 text-lt-popover-fg shadow-lt-md\"\n >\n <div {...api.getContentProps()} className=\"grid gap-3\">\n <div className=\"flex items-center justify-between gap-2\">\n <Button {...api.getPrevTriggerProps()} size=\"icon\" type=\"button\" variant=\"ghost\">\n <Icon name=\"chevron-left\" className=\"size-lt-icon-md\" aria-hidden=\"true\" />\n </Button>\n <div {...api.getRangeTextProps()} className=\"text-sm font-medium text-lt-fg\" />\n <Button {...api.getNextTriggerProps()} size=\"icon\" type=\"button\" variant=\"ghost\">\n <Icon name=\"chevron-right\" className=\"size-lt-icon-md\" aria-hidden=\"true\" />\n </Button>\n </div>\n <table {...api.getTableProps()} className=\"w-full border-collapse text-sm\">\n <thead {...api.getTableHeadProps()}>\n <tr {...api.getTableRowProps()}>\n {api.weekDays.map((day) => (\n <th\n {...api.getTableHeaderProps()}\n aria-label={day.long}\n key={day.value.toString()}\n className=\"size-8 text-center text-xs font-medium text-lt-muted-fg\"\n >\n {day.narrow}\n </th>\n ))}\n </tr>\n </thead>\n <tbody {...api.getTableBodyProps()}>\n {api.weeks.map((week, weekIndex) => (\n <tr {...api.getTableRowProps()} key={weekIndex}>\n {week.map((day) => {\n const state = api.getDayTableCellState({ value: day });\n\n return (\n <td\n {...api.getDayTableCellProps({ value: day })}\n key={day.toString()}\n className=\"p-0 text-center\"\n >\n <button\n {...api.getDayTableCellTriggerProps({ value: day })}\n type=\"button\"\n className={cn(\n \"size-8 rounded-lt-sm text-sm text-lt-fg hover:bg-lt-muted\",\n state.selected && \"bg-lt-primary text-lt-primary-fg\",\n state.outsideRange && \"text-lt-muted-fg\",\n state.disabled && \"cursor-not-allowed opacity-40\",\n )}\n >\n {day.day}\n </button>\n </td>\n );\n })}\n </tr>\n ))}\n </tbody>\n </table>\n {mode === \"date-time\" ? (\n <TimePicker\n value={parseTimeString(formatTimeInputValue(selected[0], timezone))}\n onChange={(next) =>\n api.setTime({ hour: next.hour, minute: next.minute, second: next.second })\n }\n step={step}\n disabled={disabled}\n readOnly={readOnly}\n testId={`${testId}-time`}\n />\n ) : null}\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction normalizeDateInputValue(value: string): string | undefined {\n const compact = value.replace(/\\D/g, \"\");\n\n if (compact.length !== 8) {\n return undefined;\n }\n\n return `${compact.slice(0, 4)}-${compact.slice(4, 6)}-${compact.slice(6, 8)}`;\n}\n"],"mappings":";;;;;;;;;;;;;AAyCA,SAAgB,gBAAgB,EAC9B,MACA,OACA,MACA,QACA,OACA,KACA,KACA,MACA,UACA,UACA,YAAY,OACZ,UACA,WAAW,OACX,UACA,UACuB;CACvB,MAAM,KAAK,MAAM;CACjB,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,WAAW,cAEb,CAAC,SAAS,SAAS,eAAe,KAAK,IAAI,mBAAmB,OAAO,QAAQ,CAAC,EAAE,OAC9E,OACF,GACF;EAAC;EAAM;EAAU;CAAK,CACxB;CACA,MAAM,UAAU,WAAW,WAAW,SAAS;EAC7C;EACA;EACA,OAAO,SAAS,SAAS,IAAI,WAAW,KAAA;EACxC,KAAK,MAAM,eAAe,GAAG,IAAI,KAAA;EACjC,KAAK,MAAM,eAAe,GAAG,IAAI,KAAA;EACjC;EACA;EACA;EACA,eAAe;EACf,UAAU;EACV,eAAe,SAAS;EACxB,OAAO,MAAM;GACX,OAAO,SAAS,SACZ,uBAAuB,MAAM,MAAM,IACnC,2BAA2B,MAAM,QAAQ,QAAQ;EACvD;EACA,MAAM,MAAM;GACV,OAAO,SAAS,SACZ,sBAAsB,MAAM,MAAM,IAClC,0BAA0B,MAAM,QAAQ,QAAQ;EACtD;EACA,cAAc,SAAS;GACrB,MAAM,OAAO,QAAQ,MAAM;GAE3B,SAAS,SAAS,SAAS,gBAAgB,IAAI,IAAI,oBAAoB,MAAM,QAAQ,CAAC;EACxF;EACA,aAAa,SAAS;GACpB,IAAI,CAAC,QAAQ,MACX,SAAS;EAEb;CACF,CAAC;CACD,MAAM,MAAM,WAAW,QAAQ,SAAS,cAAc;CACtD,MAAM,EAAE,MAAM,YAAY,SAAS,GAAG,eAAe,IAAI,cAAc;CACvE,MAAM,iBACJ,SAAS,SAAS,gBAAgB,SAAS,EAAE,IAAI,oBAAoB,SAAS,IAAI,QAAQ;CAE5F,OACE,qBAAC,OAAD;EAAK,GAAI,IAAI,aAAa;EAAG,WAAW,GAAG,YAAY,IAAI,QAAQ,cAAc;YAAjF;GACE,oBAAC,SAAD;IAAO,MAAK;IAAe;IAAM,OAAO;IAAgB,aAAW,GAAG,OAAO;GAAU,CAAA;GACvF,qBAAC,OAAD;IAAK,GAAI,IAAI,gBAAgB;IAAG,WAAU;cAA1C,CACE,oBAAC,OAAD;KACE,GAAI;KACJ,cAAY;KACD;KACX,aAAW;KACD;KACV,IAAI;KACJ,UAAU,UAAU;MAClB,UAAU,KAAK;MAEf,IAAI,SAAS,QACX;MAGF,MAAM,aAAa,wBAAwB,MAAM,cAAc,KAAK;MAEpE,IAAI,CAAC,YACH;MAGF,MAAM,OAAO,eAAe,UAAU;MAEtC,IAAI,CAAC,MACH;MAGF,MAAM,cAAc,QAAQ;MAC5B,IAAI,SAAS,CAAC,IAAI,CAAC;MACnB,MAAM,cAAc,QAAQ,uBAAuB,MAAM,MAAM;MAC/D,SAAS,gBAAgB,IAAI,CAAC;KAChC;KACU;KACV,UAAU,YAAY,KAAA;IACvB,CAAA,GACD,oBAAC,QAAD;KACE,GAAI,IAAI,gBAAgB;KACxB,cAAY,QAAQ,SAAS,KAAK;KAClC,UAAU,YAAY;KACtB,MAAK;KACL,MAAK;KACL,SAAQ;eAER,oBAAC,MAAD;MAAM,MAAK;MAAW,WAAU;MAAkB,eAAY;KAAQ,CAAA;IAChE,CAAA,CACL;;GACJ,IAAI,OACH,oBAAC,OAAD;IACE,GAAI,IAAI,mBAAmB;IAC3B,WAAU;cAEV,qBAAC,OAAD;KAAK,GAAI,IAAI,gBAAgB;KAAG,WAAU;eAA1C;MACE,qBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,oBAAC,QAAD;SAAQ,GAAI,IAAI,oBAAoB;SAAG,MAAK;SAAO,MAAK;SAAS,SAAQ;mBACvE,oBAAC,MAAD;UAAM,MAAK;UAAe,WAAU;UAAkB,eAAY;SAAQ,CAAA;QACpE,CAAA;QACR,oBAAC,OAAD;SAAK,GAAI,IAAI,kBAAkB;SAAG,WAAU;QAAkC,CAAA;QAC9E,oBAAC,QAAD;SAAQ,GAAI,IAAI,oBAAoB;SAAG,MAAK;SAAO,MAAK;SAAS,SAAQ;mBACvE,oBAAC,MAAD;UAAM,MAAK;UAAgB,WAAU;UAAkB,eAAY;SAAQ,CAAA;QACrE,CAAA;OACL;;MACL,qBAAC,SAAD;OAAO,GAAI,IAAI,cAAc;OAAG,WAAU;iBAA1C,CACE,oBAAC,SAAD;QAAO,GAAI,IAAI,kBAAkB;kBAC/B,oBAAC,MAAD;SAAI,GAAI,IAAI,iBAAiB;mBAC1B,IAAI,SAAS,KAAK,QACjB,8BAAC,MAAD;UACE,GAAI,IAAI,oBAAoB;UAC5B,cAAY,IAAI;UAChB,KAAK,IAAI,MAAM,SAAS;UACxB,WAAU;SAGR,GADD,IAAI,MACH,CACL;QACC,CAAA;OACC,CAAA,GACP,oBAAC,SAAD;QAAO,GAAI,IAAI,kBAAkB;kBAC9B,IAAI,MAAM,KAAK,MAAM,cACpB,8BAAC,MAAD;SAAI,GAAI,IAAI,iBAAiB;SAAG,KAAK;QAyBjC,GAxBD,KAAK,KAAK,QAAQ;SACjB,MAAM,QAAQ,IAAI,qBAAqB,EAAE,OAAO,IAAI,CAAC;SAErD,OACE,8BAAC,MAAD;UACE,GAAI,IAAI,qBAAqB,EAAE,OAAO,IAAI,CAAC;UAC3C,KAAK,IAAI,SAAS;UAClB,WAAU;SAcR,GAZF,oBAAC,UAAD;UACE,GAAI,IAAI,4BAA4B,EAAE,OAAO,IAAI,CAAC;UAClD,MAAK;UACL,WAAW,GACT,6DACA,MAAM,YAAY,oCAClB,MAAM,gBAAgB,oBACtB,MAAM,YAAY,+BACpB;oBAEC,IAAI;SACC,CAAA,CACN;QAER,CAAC,CACC,CACL;OACI,CAAA,CACF;;MACN,SAAS,cACR,oBAAC,YAAD;OACE,OAAO,gBAAgB,qBAAqB,SAAS,IAAI,QAAQ,CAAC;OAClE,WAAW,SACT,IAAI,QAAQ;QAAE,MAAM,KAAK;QAAM,QAAQ,KAAK;QAAQ,QAAQ,KAAK;OAAO,CAAC;OAErE;OACI;OACA;OACV,QAAQ,GAAG,OAAO;MACnB,CAAA,IACC;KACD;;GACF,CAAA,IACH;EACD;;AAET;AAEA,SAAS,wBAAwB,OAAmC;CAClE,MAAM,UAAU,MAAM,QAAQ,OAAO,EAAE;CAEvC,IAAI,QAAQ,WAAW,GACrB;CAGF,OAAO,GAAG,QAAQ,MAAM,GAAG,CAAC,EAAE,GAAG,QAAQ,MAAM,GAAG,CAAC,EAAE,GAAG,QAAQ,MAAM,GAAG,CAAC;AAC5E"}
1
+ {"version":3,"file":"date-picker-field.js","names":[],"sources":["../../../../resources/js/form/components/fields/date-picker-field.tsx"],"sourcesContent":["import type { DateValue } from \"@internationalized/date\";\nimport * as datePicker from \"@zag-js/date-picker\";\nimport { normalizeProps, useMachine } from \"@zag-js/react\";\nimport { useId, useMemo } from \"react\";\nimport { Button } from \"@lattice-php/lattice/ui/button\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { useLocale } from \"@lattice-php/lattice/i18n\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { Input } from \"@lattice-php/lattice/ui/input\";\nimport {\n formatDateDisplayValue,\n formatDateTimeDisplayValue,\n formatDateTimeValue,\n formatDateValue,\n formatTimeInputValue,\n parseDateDisplayValue,\n parseDateTimeDisplayValue,\n parseDateTimeValue,\n parseDateValue,\n} from \"./date-picker-value\";\nimport { TimePicker } from \"./time-picker\";\nimport { parseTimeString } from \"./time-picker-columns\";\n\nexport type DatePickerFieldProps = {\n mode: \"date\" | \"date-time\";\n label: string;\n name: string;\n testId: string;\n value: unknown;\n min?: string | null;\n max?: string | null;\n step?: number | null;\n disabled: boolean;\n readOnly: boolean;\n autoFocus?: boolean;\n tabIndex?: number | null;\n timezone?: string;\n onChange: (value: string) => void;\n onBlur?: () => void;\n};\n\nexport function DatePickerField({\n mode,\n label,\n name,\n testId,\n value,\n min,\n max,\n step,\n disabled,\n readOnly,\n autoFocus = false,\n tabIndex,\n timezone = \"UTC\",\n onChange,\n onBlur,\n}: DatePickerFieldProps) {\n const id = useId();\n const { locale } = useLocale();\n const selected = useMemo(\n () =>\n [mode === \"date\" ? parseDateValue(value) : parseDateTimeValue(value, timezone)].filter(\n Boolean,\n ) as DateValue[],\n [mode, timezone, value],\n );\n const service = useMachine(datePicker.machine, {\n id,\n name,\n value: selected.length > 0 ? selected : undefined,\n min: min ? parseDateValue(min) : undefined,\n max: max ? parseDateValue(max) : undefined,\n disabled,\n readOnly,\n locale,\n selectionMode: \"single\",\n timeZone: timezone,\n closeOnSelect: mode === \"date\",\n format(date) {\n return mode === \"date\"\n ? formatDateDisplayValue(date, locale)\n : formatDateTimeDisplayValue(date, locale, timezone);\n },\n parse(text) {\n return mode === \"date\"\n ? parseDateDisplayValue(text, locale)\n : parseDateTimeDisplayValue(text, locale, timezone);\n },\n onValueChange(details) {\n const next = details.value[0];\n\n onChange(mode === \"date\" ? formatDateValue(next) : formatDateTimeValue(next, timezone));\n },\n onOpenChange(details) {\n if (!details.open) {\n onBlur?.();\n }\n },\n });\n const api = datePicker.connect(service, normalizeProps);\n const { name: _inputName, onInput, ...inputProps } = api.getInputProps();\n const submittedValue =\n mode === \"date\" ? formatDateValue(selected[0]) : formatDateTimeValue(selected[0], timezone);\n\n return (\n <div {...api.getRootProps()} className={cn(\"relative\", api.open && \"z-lt-popover\")}>\n <input type=\"hidden\" name={name} value={submittedValue} data-test={`${testId}-value`} />\n <div {...api.getControlProps()} className=\"flex gap-2\">\n <Input\n {...inputProps}\n aria-label={label}\n autoFocus={autoFocus}\n data-test={testId}\n disabled={disabled}\n id={name}\n onInput={(event) => {\n onInput?.(event);\n\n if (mode !== \"date\") {\n return;\n }\n\n const normalized = normalizeDateInputValue(event.currentTarget.value);\n\n if (!normalized) {\n return;\n }\n\n const next = parseDateValue(normalized);\n\n if (!next) {\n return;\n }\n\n event.currentTarget.value = normalized;\n api.setValue([next]);\n event.currentTarget.value = formatDateDisplayValue(next, locale);\n onChange(formatDateValue(next));\n }}\n readOnly={readOnly}\n tabIndex={tabIndex ?? undefined}\n />\n <Button\n {...api.getTriggerProps()}\n aria-label={`Open ${label || name} calendar`}\n disabled={disabled || readOnly}\n size=\"icon\"\n type=\"button\"\n color=\"secondary\"\n >\n <Icon name=\"calendar\" className=\"size-lt-icon-md\" aria-hidden=\"true\" />\n </Button>\n </div>\n {api.open ? (\n <div\n {...api.getPositionerProps()}\n className=\"absolute z-lt-popover mt-2 rounded-lt-sm border border-lt-border bg-lt-popover p-3 text-lt-popover-fg shadow-lt-md\"\n >\n <div {...api.getContentProps()} className=\"grid gap-3\">\n <div className=\"flex items-center justify-between gap-2\">\n <Button {...api.getPrevTriggerProps()} emphasis=\"ghost\" size=\"icon\" type=\"button\">\n <Icon name=\"chevron-left\" className=\"size-lt-icon-md\" aria-hidden=\"true\" />\n </Button>\n <div {...api.getRangeTextProps()} className=\"text-sm font-medium text-lt-fg\" />\n <Button {...api.getNextTriggerProps()} emphasis=\"ghost\" size=\"icon\" type=\"button\">\n <Icon name=\"chevron-right\" className=\"size-lt-icon-md\" aria-hidden=\"true\" />\n </Button>\n </div>\n <table {...api.getTableProps()} className=\"w-full border-collapse text-sm\">\n <thead {...api.getTableHeadProps()}>\n <tr {...api.getTableRowProps()}>\n {api.weekDays.map((day) => (\n <th\n {...api.getTableHeaderProps()}\n aria-label={day.long}\n key={day.value.toString()}\n className=\"size-8 text-center text-xs font-medium text-lt-muted-fg\"\n >\n {day.narrow}\n </th>\n ))}\n </tr>\n </thead>\n <tbody {...api.getTableBodyProps()}>\n {api.weeks.map((week, weekIndex) => (\n <tr {...api.getTableRowProps()} key={weekIndex}>\n {week.map((day) => {\n const state = api.getDayTableCellState({ value: day });\n\n return (\n <td\n {...api.getDayTableCellProps({ value: day })}\n key={day.toString()}\n className=\"p-0 text-center\"\n >\n <button\n {...api.getDayTableCellTriggerProps({ value: day })}\n type=\"button\"\n className={cn(\n \"size-8 rounded-lt-sm text-sm text-lt-fg hover:bg-lt-muted\",\n state.selected && \"bg-lt-primary text-lt-primary-fg\",\n state.outsideRange && \"text-lt-muted-fg\",\n state.disabled && \"cursor-not-allowed opacity-40\",\n )}\n >\n {day.day}\n </button>\n </td>\n );\n })}\n </tr>\n ))}\n </tbody>\n </table>\n {mode === \"date-time\" ? (\n <TimePicker\n value={parseTimeString(formatTimeInputValue(selected[0], timezone))}\n onChange={(next) =>\n api.setTime({ hour: next.hour, minute: next.minute, second: next.second })\n }\n step={step}\n disabled={disabled}\n readOnly={readOnly}\n testId={`${testId}-time`}\n />\n ) : null}\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction normalizeDateInputValue(value: string): string | undefined {\n const compact = value.replace(/\\D/g, \"\");\n\n if (compact.length !== 8) {\n return undefined;\n }\n\n return `${compact.slice(0, 4)}-${compact.slice(4, 6)}-${compact.slice(6, 8)}`;\n}\n"],"mappings":"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"}