@lattice-php/lattice 0.3.0 → 0.5.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 (195) hide show
  1. package/dist/action/components/action-form.js +3 -3
  2. package/dist/action/components/action-group.js +1 -1
  3. package/dist/action/components/action.js +10 -16
  4. package/dist/action/components/action.js.map +1 -1
  5. package/dist/action/run-action.d.ts +9 -0
  6. package/dist/action/run-action.js +22 -0
  7. package/dist/action/run-action.js.map +1 -0
  8. package/dist/chat/components/chat-box.js +31 -14
  9. package/dist/chat/components/chat-box.js.map +1 -1
  10. package/dist/chat/components/message.js +2 -2
  11. package/dist/chat/components/message.js.map +1 -1
  12. package/dist/chat/components/prompt-input.js +1 -1
  13. package/dist/chat/index.js +2 -0
  14. package/dist/chat/index.js.map +1 -1
  15. package/dist/chat/transport.d.ts +2 -0
  16. package/dist/chat/transport.js +23 -3
  17. package/dist/chat/transport.js.map +1 -1
  18. package/dist/core/api.d.ts +14 -7
  19. package/dist/core/api.js +77 -1
  20. package/dist/core/api.js.map +1 -1
  21. package/dist/core/components/badge.js +4 -4
  22. package/dist/core/components/badge.js.map +1 -1
  23. package/dist/core/components/button.js +31 -19
  24. package/dist/core/components/button.js.map +1 -1
  25. package/dist/core/components/card.js +3 -2
  26. package/dist/core/components/card.js.map +1 -1
  27. package/dist/core/components/chart.js +3 -2
  28. package/dist/core/components/chart.js.map +1 -1
  29. package/dist/core/components/checkbox.js +1 -1
  30. package/dist/core/components/checkbox.js.map +1 -1
  31. package/dist/core/components/control.js +4 -4
  32. package/dist/core/components/control.js.map +1 -1
  33. package/dist/core/components/dialog.d.ts +1 -1
  34. package/dist/core/components/dialog.js +3 -2
  35. package/dist/core/components/dialog.js.map +1 -1
  36. package/dist/core/components/floating-panel.js +5 -4
  37. package/dist/core/components/floating-panel.js.map +1 -1
  38. package/dist/core/components/fragment.js +3 -6
  39. package/dist/core/components/fragment.js.map +1 -1
  40. package/dist/core/components/grid.js +2 -1
  41. package/dist/core/components/grid.js.map +1 -1
  42. package/dist/core/components/heading.js +1 -1
  43. package/dist/core/components/heading.js.map +1 -1
  44. package/dist/core/components/index.js +0 -2
  45. package/dist/core/components/index.js.map +1 -1
  46. package/dist/core/components/popover.d.ts +1 -1
  47. package/dist/core/components/popover.js +1 -1
  48. package/dist/core/components/popover.js.map +1 -1
  49. package/dist/core/components/section.js +7 -11
  50. package/dist/core/components/section.js.map +1 -1
  51. package/dist/core/components/segmented-pills.js +1 -1
  52. package/dist/core/components/segmented-pills.js.map +1 -1
  53. package/dist/core/components/stack.js +9 -2
  54. package/dist/core/components/stack.js.map +1 -1
  55. package/dist/core/components/tabs.js +2 -2
  56. package/dist/core/components/tabs.js.map +1 -1
  57. package/dist/core/nodes.d.ts +6 -0
  58. package/dist/core/nodes.js +13 -0
  59. package/dist/core/nodes.js.map +1 -0
  60. package/dist/core/renderer.js +1 -1
  61. package/dist/core/types.d.ts +3 -1
  62. package/dist/create-app.d.ts +21 -0
  63. package/dist/create-app.js +31 -0
  64. package/dist/create-app.js.map +1 -0
  65. package/dist/effects/registry.js +2 -1
  66. package/dist/effects/registry.js.map +1 -1
  67. package/dist/effects/use-effect-dispatcher.js +1 -1
  68. package/dist/effects/use-flash-effects.js +1 -1
  69. package/dist/events/event-names.d.ts +3 -2
  70. package/dist/events/event-names.js +3 -2
  71. package/dist/events/event-names.js.map +1 -1
  72. package/dist/form/components/base/affix-group.d.ts +17 -0
  73. package/dist/form/components/base/affix-group.js +49 -0
  74. package/dist/form/components/base/affix-group.js.map +1 -0
  75. package/dist/form/components/base/input-otp.d.ts +7 -0
  76. package/dist/form/components/base/input-otp.js +30 -0
  77. package/dist/form/components/base/input-otp.js.map +1 -0
  78. package/dist/form/components/base/label.js +1 -1
  79. package/dist/form/components/base/label.js.map +1 -1
  80. package/dist/form/components/base/submit-button.js +1 -1
  81. package/dist/form/components/base/submit-button.js.map +1 -1
  82. package/dist/form/components/base/textarea.js +1 -1
  83. package/dist/form/components/base/textarea.js.map +1 -1
  84. package/dist/form/components/conditions.js +1 -1
  85. package/dist/form/components/conditions.js.map +1 -1
  86. package/dist/form/components/fields/file-upload.js +35 -3
  87. package/dist/form/components/fields/file-upload.js.map +1 -1
  88. package/dist/form/components/fields/number-input.js +21 -15
  89. package/dist/form/components/fields/number-input.js.map +1 -1
  90. package/dist/form/components/fields/otp-input.d.ts +2 -0
  91. package/dist/form/components/fields/otp-input.js +25 -0
  92. package/dist/form/components/fields/otp-input.js.map +1 -0
  93. package/dist/form/components/fields/password-input.js +21 -15
  94. package/dist/form/components/fields/password-input.js.map +1 -1
  95. package/dist/form/components/fields/rich-editor.js +2 -2
  96. package/dist/form/components/fields/rich-editor.js.map +1 -1
  97. package/dist/form/components/fields/row-item.js +1 -1
  98. package/dist/form/components/fields/table-rows.js +1 -1
  99. package/dist/form/components/fields/text-input.js +19 -13
  100. package/dist/form/components/fields/text-input.js.map +1 -1
  101. package/dist/form/components/form.js +2 -2
  102. package/dist/form/components/form.js.map +1 -1
  103. package/dist/form/components/index.d.ts +1 -0
  104. package/dist/form/components/index.js +2 -1
  105. package/dist/form/components/prefill-targets.d.ts +0 -1
  106. package/dist/form/components/prefill-targets.js +1 -4
  107. package/dist/form/components/prefill-targets.js.map +1 -1
  108. package/dist/form/components/use-form-resolver.js +2 -2
  109. package/dist/form/components/use-form-resolver.js.map +1 -1
  110. package/dist/form/index.js +1 -0
  111. package/dist/form/index.js.map +1 -1
  112. package/dist/form/skeleton.js +1 -1
  113. package/dist/form/skeleton.js.map +1 -1
  114. package/dist/i18n/locale-reload.js +1 -1
  115. package/dist/i18n/translatable.d.ts +4 -0
  116. package/dist/i18n/translatable.js +24 -0
  117. package/dist/i18n/translatable.js.map +1 -0
  118. package/dist/index.d.ts +3 -0
  119. package/dist/index.js +15 -13
  120. package/dist/lattice.css +99 -6
  121. package/dist/layout/breadcrumbs.js +3 -2
  122. package/dist/layout/breadcrumbs.js.map +1 -1
  123. package/dist/layout/callouts.js +2 -2
  124. package/dist/layout/callouts.js.map +1 -1
  125. package/dist/layout/components.js +4 -2
  126. package/dist/layout/components.js.map +1 -1
  127. package/dist/layout/dropdown.js +2 -1
  128. package/dist/layout/dropdown.js.map +1 -1
  129. package/dist/layout/menu-item.js +33 -12
  130. package/dist/layout/menu-item.js.map +1 -1
  131. package/dist/layout/menu.js +2 -1
  132. package/dist/layout/menu.js.map +1 -1
  133. package/dist/layout/popover.js +1 -1
  134. package/dist/layout/sidebar.js +69 -29
  135. package/dist/layout/sidebar.js.map +1 -1
  136. package/dist/layout/topbar.d.ts +3 -0
  137. package/dist/layout/topbar.js +16 -0
  138. package/dist/layout/topbar.js.map +1 -0
  139. package/dist/page.js +15 -10
  140. package/dist/page.js.map +1 -1
  141. package/dist/realtime/build-effects.d.ts +4 -0
  142. package/dist/realtime/build-effects.js +23 -0
  143. package/dist/realtime/build-effects.js.map +1 -0
  144. package/dist/realtime/listeners.d.ts +8 -0
  145. package/dist/realtime/listeners.js +31 -0
  146. package/dist/realtime/listeners.js.map +1 -0
  147. package/dist/realtime/subscriptions.d.ts +4 -0
  148. package/dist/realtime/subscriptions.js +45 -0
  149. package/dist/realtime/subscriptions.js.map +1 -0
  150. package/dist/registry.js +3 -2
  151. package/dist/registry.js.map +1 -1
  152. package/dist/remote/components/data-list.d.ts +10 -0
  153. package/dist/remote/components/data-list.js +106 -0
  154. package/dist/remote/components/data-list.js.map +1 -0
  155. package/dist/remote/index.d.ts +1 -0
  156. package/dist/remote/index.js +11 -0
  157. package/dist/remote/index.js.map +1 -0
  158. package/dist/table/components/bulk-bar.js +10 -15
  159. package/dist/table/components/bulk-bar.js.map +1 -1
  160. package/dist/table/components/cells/text-cell.js +38 -3
  161. package/dist/table/components/cells/text-cell.js.map +1 -1
  162. package/dist/table/components/column-filter-control.d.ts +2 -1
  163. package/dist/table/components/column-filter-control.js +28 -19
  164. package/dist/table/components/column-filter-control.js.map +1 -1
  165. package/dist/table/components/column-header.js +2 -2
  166. package/dist/table/components/column-header.js.map +1 -1
  167. package/dist/table/components/filter-bar.d.ts +7 -6
  168. package/dist/table/components/filter-bar.js +51 -28
  169. package/dist/table/components/filter-bar.js.map +1 -1
  170. package/dist/table/components/filter-controls.js +2 -2
  171. package/dist/table/components/filter-controls.js.map +1 -1
  172. package/dist/table/components/filter-stack-bar.js +4 -3
  173. package/dist/table/components/filter-stack-bar.js.map +1 -1
  174. package/dist/table/components/filter-value-input.js +1 -1
  175. package/dist/table/components/filter-value-input.js.map +1 -1
  176. package/dist/table/components/sort-bar.js +1 -1
  177. package/dist/table/components/sort-bar.js.map +1 -1
  178. package/dist/table/components/table.js +26 -17
  179. package/dist/table/components/table.js.map +1 -1
  180. package/dist/table/filter-values.d.ts +5 -0
  181. package/dist/table/filter-values.js +10 -3
  182. package/dist/table/filter-values.js.map +1 -1
  183. package/dist/table/query.js +5 -4
  184. package/dist/table/query.js.map +1 -1
  185. package/dist/table/use-table.d.ts +1 -0
  186. package/dist/table/use-table.js +4 -0
  187. package/dist/table/use-table.js.map +1 -1
  188. package/dist/toast/toast.js +5 -2
  189. package/dist/toast/toast.js.map +1 -1
  190. package/dist/toast/toaster.js +6 -5
  191. package/dist/toast/toaster.js.map +1 -1
  192. package/dist/types/generated.d.ts +122 -14
  193. package/dist/vite.js +32 -1
  194. package/dist/vite.js.map +1 -1
  195. package/package.json +33 -3
@@ -1,5 +1,6 @@
1
1
  import { Input } from "../base/input.js";
2
2
  import { SimpleField } from "./simple-field.js";
3
+ import { AffixGroup } from "../base/affix-group.js";
3
4
  import { jsx } from "react/jsx-runtime";
4
5
  //#region resources/js/form/components/fields/text-input.tsx
5
6
  var TextInputComponent = ({ node }) => {
@@ -7,19 +8,24 @@ var TextInputComponent = ({ node }) => {
7
8
  return /* @__PURE__ */ jsx(SimpleField, {
8
9
  node,
9
10
  label: props.label ?? "",
10
- children: ({ name, testId, value, readOnly, disabled, commit }) => /* @__PURE__ */ jsx(Input, {
11
- autoComplete: props.autoComplete ?? "",
12
- autoFocus: props.autoFocus ?? false,
13
- "data-test": testId,
14
- disabled,
15
- id: name,
16
- name,
17
- onChange: (event) => commit(event.target.value),
18
- placeholder: props.placeholder ?? "",
19
- readOnly,
20
- tabIndex: props.tabIndex ?? void 0,
21
- type: props.type ?? "text",
22
- value
11
+ children: ({ name, testId, value, readOnly, disabled, commit }) => /* @__PURE__ */ jsx(AffixGroup, {
12
+ prefix: props.prefix,
13
+ suffix: props.suffix,
14
+ children: (controlClassName) => /* @__PURE__ */ jsx(Input, {
15
+ autoComplete: props.autoComplete ?? "",
16
+ autoFocus: props.autoFocus ?? false,
17
+ className: controlClassName,
18
+ "data-test": testId,
19
+ disabled,
20
+ id: name,
21
+ name,
22
+ onChange: (event) => commit(event.target.value),
23
+ placeholder: props.placeholder ?? "",
24
+ readOnly,
25
+ tabIndex: props.tabIndex ?? void 0,
26
+ type: props.type ?? "text",
27
+ value
28
+ })
23
29
  })
24
30
  });
25
31
  };
@@ -1 +1 @@
1
- {"version":3,"file":"text-input.js","names":[],"sources":["../../../../resources/js/form/components/fields/text-input.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { Input } from \"../base/input\";\nimport { SimpleField } from \"./simple-field\";\n\nexport const TextInputComponent: RendererComponent<\"field.text-input\"> = ({ node }) => {\n const props = node.props;\n\n return (\n <SimpleField node={node} label={props.label ?? \"\"}>\n {({ name, testId, value, readOnly, disabled, commit }) => (\n <Input\n autoComplete={props.autoComplete ?? \"\"}\n autoFocus={props.autoFocus ?? false}\n data-test={testId}\n disabled={disabled}\n id={name}\n name={name}\n onChange={(event) => commit(event.target.value)}\n placeholder={props.placeholder ?? \"\"}\n readOnly={readOnly}\n tabIndex={props.tabIndex ?? undefined}\n type={props.type ?? \"text\"}\n value={value}\n />\n )}\n </SimpleField>\n );\n};\n"],"mappings":";;;;AAIA,IAAa,sBAA6D,EAAE,WAAW;CACrF,MAAM,QAAQ,KAAK;CAEnB,OACE,oBAAC,aAAD;EAAmB;EAAM,OAAO,MAAM,SAAS;aAC3C,EAAE,MAAM,QAAQ,OAAO,UAAU,UAAU,aAC3C,oBAAC,OAAD;GACE,cAAc,MAAM,gBAAgB;GACpC,WAAW,MAAM,aAAa;GAC9B,aAAW;GACD;GACV,IAAI;GACE;GACN,WAAW,UAAU,OAAO,MAAM,OAAO,KAAK;GAC9C,aAAa,MAAM,eAAe;GACxB;GACV,UAAU,MAAM,YAAY,KAAA;GAC5B,MAAM,MAAM,QAAQ;GACb;EACR,CAAA;CAEQ,CAAA;AAEjB"}
1
+ {"version":3,"file":"text-input.js","names":[],"sources":["../../../../resources/js/form/components/fields/text-input.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { AffixGroup } from \"../base/affix-group\";\nimport { Input } from \"../base/input\";\nimport { SimpleField } from \"./simple-field\";\n\nexport const TextInputComponent: RendererComponent<\"field.text-input\"> = ({ node }) => {\n const props = node.props;\n\n return (\n <SimpleField node={node} label={props.label ?? \"\"}>\n {({ name, testId, value, readOnly, disabled, commit }) => (\n <AffixGroup prefix={props.prefix} suffix={props.suffix}>\n {(controlClassName) => (\n <Input\n autoComplete={props.autoComplete ?? \"\"}\n autoFocus={props.autoFocus ?? false}\n className={controlClassName}\n data-test={testId}\n disabled={disabled}\n id={name}\n name={name}\n onChange={(event) => commit(event.target.value)}\n placeholder={props.placeholder ?? \"\"}\n readOnly={readOnly}\n tabIndex={props.tabIndex ?? undefined}\n type={props.type ?? \"text\"}\n value={value}\n />\n )}\n </AffixGroup>\n )}\n </SimpleField>\n );\n};\n"],"mappings":";;;;;AAKA,IAAa,sBAA6D,EAAE,WAAW;CACrF,MAAM,QAAQ,KAAK;CAEnB,OACE,oBAAC,aAAD;EAAmB;EAAM,OAAO,MAAM,SAAS;aAC3C,EAAE,MAAM,QAAQ,OAAO,UAAU,UAAU,aAC3C,oBAAC,YAAD;GAAY,QAAQ,MAAM;GAAQ,QAAQ,MAAM;cAC5C,qBACA,oBAAC,OAAD;IACE,cAAc,MAAM,gBAAgB;IACpC,WAAW,MAAM,aAAa;IAC9B,WAAW;IACX,aAAW;IACD;IACV,IAAI;IACE;IACN,WAAW,UAAU,OAAO,MAAM,OAAO,KAAK;IAC9C,aAAa,MAAM,eAAe;IACxB;IACV,UAAU,MAAM,YAAY,KAAA;IAC5B,MAAM,MAAM,QAAQ;IACb;GACR,CAAA;EAEO,CAAA;CAEH,CAAA;AAEjB"}
@@ -7,8 +7,8 @@ import { ResolvedNodesProvider } from "./resolved-nodes.js";
7
7
  import { FormValuesProvider } from "./values.js";
8
8
  import { useFormResolver } from "./use-form-resolver.js";
9
9
  import { FormSubmitButton } from "./base/submit-button.js";
10
- import { useEffect, useMemo } from "react";
11
10
  import { Form } from "@inertiajs/react";
11
+ import { useEffect, useMemo } from "react";
12
12
  import { jsx, jsxs } from "react/jsx-runtime";
13
13
  //#region resources/js/form/components/form.tsx
14
14
  function collectFields(nodes) {
@@ -43,7 +43,7 @@ function FormBody({ action, children, componentRef, nodes, shouldRenderSubmitBut
43
43
  children: /* @__PURE__ */ jsxs("div", {
44
44
  className: "flex flex-col gap-6",
45
45
  children: [children, shouldRenderSubmitButton && /* @__PURE__ */ jsx("div", {
46
- className: "flex justify-end rounded-lt border border-lt-border bg-lt-surface px-6 py-4 shadow-xs",
46
+ className: "flex justify-end rounded-lt border border-lt-border bg-lt-surface px-6 py-4 shadow-lt-sm",
47
47
  children: /* @__PURE__ */ jsx(FormSubmitButton, {
48
48
  label: submitLabel,
49
49
  summaryLabel
@@ -1 +1 @@
1
- {"version":3,"file":"form.js","names":[],"sources":["../../../resources/js/form/components/form.tsx"],"sourcesContent":["import { Form as InertiaForm } from \"@inertiajs/react\";\nimport { withHeaders } from \"@lattice-php/lattice/core/headers\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/events/event-names\";\nimport type { Node, RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { useEffect, useMemo } from \"react\";\nimport { FormSubmitButton } from \"./base/submit-button\";\nimport { FormProvider } from \"./context\";\nimport { walkFields } from \"./field-props\";\nimport { PrefillProvider } from \"./prefill-context\";\nimport { ResolvedNodesProvider } from \"./resolved-nodes\";\nimport { useFormResolver } from \"./use-form-resolver\";\nimport { FormValuesProvider } from \"./values\";\n\ntype CollectedFields = {\n labels: Record<string, string>;\n values: Record<string, unknown>;\n};\n\nfunction collectFields(nodes: Node[] | undefined): CollectedFields {\n const collected: CollectedFields = { labels: {}, values: {} };\n\n walkFields(nodes, (props) => {\n if (!props.name) {\n return;\n }\n if (props.label) {\n collected.labels[props.name] = props.label;\n }\n if (props.value !== undefined) {\n collected.values[props.name] = props.value;\n }\n });\n\n return collected;\n}\n\nfunction FormResetListener({\n componentId,\n reset,\n}: {\n componentId?: string;\n reset: (...fields: string[]) => void;\n}) {\n useEffect(() => {\n const handler = (event: Event) => {\n const detail = (event as CustomEvent<{ form: string | null }>).detail;\n\n if (!detail?.form || detail.form === componentId) {\n reset();\n }\n };\n\n window.addEventListener(LATTICE_EVENT.resetForm, handler);\n\n return () => window.removeEventListener(LATTICE_EVENT.resetForm, handler);\n }, [componentId, reset]);\n\n return null;\n}\n\nfunction FormBody({\n action,\n children,\n componentRef,\n nodes,\n shouldRenderSubmitButton,\n submitLabel,\n summaryLabel,\n}: {\n action: string;\n children: React.ReactNode;\n componentRef: string;\n nodes: Node[] | undefined;\n shouldRenderSubmitButton: boolean;\n submitLabel: string;\n summaryLabel: string;\n}) {\n const { nodes: resolvedNodes, markUserEdit } = useFormResolver(action, componentRef, nodes);\n\n return (\n <PrefillProvider value={{ markUserEdit }}>\n <ResolvedNodesProvider nodes={resolvedNodes}>\n <div className=\"flex flex-col gap-6\">\n {children}\n\n {shouldRenderSubmitButton && (\n <div className=\"flex justify-end rounded-lt border border-lt-border bg-lt-surface px-6 py-4 shadow-xs\">\n <FormSubmitButton label={submitLabel} summaryLabel={summaryLabel} />\n </div>\n )}\n </div>\n </ResolvedNodesProvider>\n </PrefillProvider>\n );\n}\n\nexport const FormComponent: RendererComponent<\"form\"> = ({ children, node }) => {\n const props = node.props;\n const action = props.action ?? \"#\";\n const errorBag = props.errorBag;\n const componentRef = props.ref ?? \"\";\n const method = props.method ?? \"post\";\n const precognitive = props.precognitive;\n const resetOnError = props.resetOnError ?? false;\n const resetOnSuccess = props.resetOnSuccess ?? [];\n const state = props.state;\n const { labels: fieldLabels, values: fieldValues } = useMemo(\n () => collectFields(node.schema),\n [node.schema],\n );\n const initialValues = useMemo(() => ({ ...fieldValues, ...state }), [fieldValues, state]);\n const shouldRenderSubmitButton = props.submitButton;\n const submitLabel = props.submitLabel ?? \"Submit\";\n const summaryLabel = props.validationSummaryLabel;\n const validationTimeout = props.validationTimeout ?? undefined;\n\n return (\n <InertiaForm\n action={action}\n data-lattice-component={node.id}\n errorBag={errorBag}\n method={method}\n resetOnError={resetOnError}\n resetOnSuccess={resetOnSuccess}\n validationTimeout={precognitive ? validationTimeout : undefined}\n headers={withHeaders(componentRef)}\n className=\"mx-auto flex w-full max-w-2xl flex-col gap-6\"\n >\n {({ clearErrors, errors, processing, reset, validate }) => (\n <FormProvider\n value={{\n action,\n clearErrors: (field) => clearErrors(field),\n componentId: node.id,\n componentRef,\n errors: errors as Record<string, string | undefined>,\n fieldLabels,\n precognitive,\n processing,\n validate: (field) => validate(field),\n }}\n >\n <FormResetListener componentId={node.id} reset={reset} />\n\n {props.status && (\n <div className=\"text-center text-sm font-medium text-lt-success\">{props.status}</div>\n )}\n\n <FormValuesProvider initial={initialValues}>\n <FormBody\n action={action}\n componentRef={componentRef}\n nodes={node.schema}\n shouldRenderSubmitButton={shouldRenderSubmitButton}\n submitLabel={submitLabel}\n summaryLabel={summaryLabel}\n >\n {children}\n </FormBody>\n </FormValuesProvider>\n </FormProvider>\n )}\n </InertiaForm>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AAkBA,SAAS,cAAc,OAA4C;CACjE,MAAM,YAA6B;EAAE,QAAQ,CAAC;EAAG,QAAQ,CAAC;CAAE;CAE5D,WAAW,QAAQ,UAAU;EAC3B,IAAI,CAAC,MAAM,MACT;EAEF,IAAI,MAAM,OACR,UAAU,OAAO,MAAM,QAAQ,MAAM;EAEvC,IAAI,MAAM,UAAU,KAAA,GAClB,UAAU,OAAO,MAAM,QAAQ,MAAM;CAEzC,CAAC;CAED,OAAO;AACT;AAEA,SAAS,kBAAkB,EACzB,aACA,SAIC;CACD,gBAAgB;EACd,MAAM,WAAW,UAAiB;GAChC,MAAM,SAAU,MAA+C;GAE/D,IAAI,CAAC,QAAQ,QAAQ,OAAO,SAAS,aACnC,MAAM;EAEV;EAEA,OAAO,iBAAiB,cAAc,WAAW,OAAO;EAExD,aAAa,OAAO,oBAAoB,cAAc,WAAW,OAAO;CAC1E,GAAG,CAAC,aAAa,KAAK,CAAC;CAEvB,OAAO;AACT;AAEA,SAAS,SAAS,EAChB,QACA,UACA,cACA,OACA,0BACA,aACA,gBASC;CACD,MAAM,EAAE,OAAO,eAAe,iBAAiB,gBAAgB,QAAQ,cAAc,KAAK;CAE1F,OACE,oBAAC,iBAAD;EAAiB,OAAO,EAAE,aAAa;YACrC,oBAAC,uBAAD;GAAuB,OAAO;aAC5B,qBAAC,OAAD;IAAK,WAAU;cAAf,CACG,UAEA,4BACC,oBAAC,OAAD;KAAK,WAAU;eACb,oBAAC,kBAAD;MAAkB,OAAO;MAA2B;KAAe,CAAA;IAChE,CAAA,CAEJ;;EACgB,CAAA;CACR,CAAA;AAErB;AAEA,IAAa,iBAA4C,EAAE,UAAU,WAAW;CAC9E,MAAM,QAAQ,KAAK;CACnB,MAAM,SAAS,MAAM,UAAU;CAC/B,MAAM,WAAW,MAAM;CACvB,MAAM,eAAe,MAAM,OAAO;CAClC,MAAM,SAAS,MAAM,UAAU;CAC/B,MAAM,eAAe,MAAM;CAC3B,MAAM,eAAe,MAAM,gBAAgB;CAC3C,MAAM,iBAAiB,MAAM,kBAAkB,CAAC;CAChD,MAAM,QAAQ,MAAM;CACpB,MAAM,EAAE,QAAQ,aAAa,QAAQ,gBAAgB,cAC7C,cAAc,KAAK,MAAM,GAC/B,CAAC,KAAK,MAAM,CACd;CACA,MAAM,gBAAgB,eAAe;EAAE,GAAG;EAAa,GAAG;CAAM,IAAI,CAAC,aAAa,KAAK,CAAC;CACxF,MAAM,2BAA2B,MAAM;CACvC,MAAM,cAAc,MAAM,eAAe;CACzC,MAAM,eAAe,MAAM;CAC3B,MAAM,oBAAoB,MAAM,qBAAqB,KAAA;CAErD,OACE,oBAAC,MAAD;EACU;EACR,0BAAwB,KAAK;EACnB;EACF;EACM;EACE;EAChB,mBAAmB,eAAe,oBAAoB,KAAA;EACtD,SAAS,YAAY,YAAY;EACjC,WAAU;aAER,EAAE,aAAa,QAAQ,YAAY,OAAO,eAC1C,qBAAC,cAAD;GACE,OAAO;IACL;IACA,cAAc,UAAU,YAAY,KAAK;IACzC,aAAa,KAAK;IAClB;IACQ;IACR;IACA;IACA;IACA,WAAW,UAAU,SAAS,KAAK;GACrC;aAXF;IAaE,oBAAC,mBAAD;KAAmB,aAAa,KAAK;KAAW;IAAQ,CAAA;IAEvD,MAAM,UACL,oBAAC,OAAD;KAAK,WAAU;eAAmD,MAAM;IAAY,CAAA;IAGtF,oBAAC,oBAAD;KAAoB,SAAS;eAC3B,oBAAC,UAAD;MACU;MACM;MACd,OAAO,KAAK;MACc;MACb;MACC;MAEb;KACO,CAAA;IACQ,CAAA;GACR;;CAEL,CAAA;AAEjB"}
1
+ {"version":3,"file":"form.js","names":[],"sources":["../../../resources/js/form/components/form.tsx"],"sourcesContent":["import { Form as InertiaForm } from \"@inertiajs/react\";\nimport { withHeaders } from \"@lattice-php/lattice/core/headers\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/events/event-names\";\nimport type { Node, RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { useEffect, useMemo } from \"react\";\nimport { FormSubmitButton } from \"./base/submit-button\";\nimport { FormProvider } from \"./context\";\nimport { walkFields } from \"./field-props\";\nimport { PrefillProvider } from \"./prefill-context\";\nimport { ResolvedNodesProvider } from \"./resolved-nodes\";\nimport { useFormResolver } from \"./use-form-resolver\";\nimport { FormValuesProvider } from \"./values\";\n\ntype CollectedFields = {\n labels: Record<string, string>;\n values: Record<string, unknown>;\n};\n\nfunction collectFields(nodes: Node[] | undefined): CollectedFields {\n const collected: CollectedFields = { labels: {}, values: {} };\n\n walkFields(nodes, (props) => {\n if (!props.name) {\n return;\n }\n if (props.label) {\n collected.labels[props.name] = props.label;\n }\n if (props.value !== undefined) {\n collected.values[props.name] = props.value;\n }\n });\n\n return collected;\n}\n\nfunction FormResetListener({\n componentId,\n reset,\n}: {\n componentId?: string;\n reset: (...fields: string[]) => void;\n}) {\n useEffect(() => {\n const handler = (event: Event) => {\n const detail = (event as CustomEvent<{ form: string | null }>).detail;\n\n if (!detail?.form || detail.form === componentId) {\n reset();\n }\n };\n\n window.addEventListener(LATTICE_EVENT.resetForm, handler);\n\n return () => window.removeEventListener(LATTICE_EVENT.resetForm, handler);\n }, [componentId, reset]);\n\n return null;\n}\n\nfunction FormBody({\n action,\n children,\n componentRef,\n nodes,\n shouldRenderSubmitButton,\n submitLabel,\n summaryLabel,\n}: {\n action: string;\n children: React.ReactNode;\n componentRef: string;\n nodes: Node[] | undefined;\n shouldRenderSubmitButton: boolean;\n submitLabel: string;\n summaryLabel: string;\n}) {\n const { nodes: resolvedNodes, markUserEdit } = useFormResolver(action, componentRef, nodes);\n\n return (\n <PrefillProvider value={{ markUserEdit }}>\n <ResolvedNodesProvider nodes={resolvedNodes}>\n <div className=\"flex flex-col gap-6\">\n {children}\n\n {shouldRenderSubmitButton && (\n <div className=\"flex justify-end rounded-lt border border-lt-border bg-lt-surface px-6 py-4 shadow-lt-sm\">\n <FormSubmitButton label={submitLabel} summaryLabel={summaryLabel} />\n </div>\n )}\n </div>\n </ResolvedNodesProvider>\n </PrefillProvider>\n );\n}\n\nexport const FormComponent: RendererComponent<\"form\"> = ({ children, node }) => {\n const props = node.props;\n const action = props.action ?? \"#\";\n const errorBag = props.errorBag;\n const componentRef = props.ref ?? \"\";\n const method = props.method ?? \"post\";\n const precognitive = props.precognitive;\n const resetOnError = props.resetOnError ?? false;\n const resetOnSuccess = props.resetOnSuccess ?? [];\n const state = props.state;\n const { labels: fieldLabels, values: fieldValues } = useMemo(\n () => collectFields(node.schema),\n [node.schema],\n );\n const initialValues = useMemo(() => ({ ...fieldValues, ...state }), [fieldValues, state]);\n const shouldRenderSubmitButton = props.submitButton;\n const submitLabel = props.submitLabel ?? \"Submit\";\n const summaryLabel = props.validationSummaryLabel;\n const validationTimeout = props.validationTimeout ?? undefined;\n\n return (\n <InertiaForm\n action={action}\n data-lattice-component={node.id}\n errorBag={errorBag}\n method={method}\n resetOnError={resetOnError}\n resetOnSuccess={resetOnSuccess}\n validationTimeout={precognitive ? validationTimeout : undefined}\n headers={withHeaders(componentRef)}\n className=\"mx-auto flex w-full max-w-2xl flex-col gap-6\"\n >\n {({ clearErrors, errors, processing, reset, validate }) => (\n <FormProvider\n value={{\n action,\n clearErrors: (field) => clearErrors(field),\n componentId: node.id,\n componentRef,\n errors: errors as Record<string, string | undefined>,\n fieldLabels,\n precognitive,\n processing,\n validate: (field) => validate(field),\n }}\n >\n <FormResetListener componentId={node.id} reset={reset} />\n\n {props.status && (\n <div className=\"text-center text-sm font-medium text-lt-success\">{props.status}</div>\n )}\n\n <FormValuesProvider initial={initialValues}>\n <FormBody\n action={action}\n componentRef={componentRef}\n nodes={node.schema}\n shouldRenderSubmitButton={shouldRenderSubmitButton}\n submitLabel={submitLabel}\n summaryLabel={summaryLabel}\n >\n {children}\n </FormBody>\n </FormValuesProvider>\n </FormProvider>\n )}\n </InertiaForm>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AAkBA,SAAS,cAAc,OAA4C;CACjE,MAAM,YAA6B;EAAE,QAAQ,CAAC;EAAG,QAAQ,CAAC;CAAE;CAE5D,WAAW,QAAQ,UAAU;EAC3B,IAAI,CAAC,MAAM,MACT;EAEF,IAAI,MAAM,OACR,UAAU,OAAO,MAAM,QAAQ,MAAM;EAEvC,IAAI,MAAM,UAAU,KAAA,GAClB,UAAU,OAAO,MAAM,QAAQ,MAAM;CAEzC,CAAC;CAED,OAAO;AACT;AAEA,SAAS,kBAAkB,EACzB,aACA,SAIC;CACD,gBAAgB;EACd,MAAM,WAAW,UAAiB;GAChC,MAAM,SAAU,MAA+C;GAE/D,IAAI,CAAC,QAAQ,QAAQ,OAAO,SAAS,aACnC,MAAM;EAEV;EAEA,OAAO,iBAAiB,cAAc,WAAW,OAAO;EAExD,aAAa,OAAO,oBAAoB,cAAc,WAAW,OAAO;CAC1E,GAAG,CAAC,aAAa,KAAK,CAAC;CAEvB,OAAO;AACT;AAEA,SAAS,SAAS,EAChB,QACA,UACA,cACA,OACA,0BACA,aACA,gBASC;CACD,MAAM,EAAE,OAAO,eAAe,iBAAiB,gBAAgB,QAAQ,cAAc,KAAK;CAE1F,OACE,oBAAC,iBAAD;EAAiB,OAAO,EAAE,aAAa;YACrC,oBAAC,uBAAD;GAAuB,OAAO;aAC5B,qBAAC,OAAD;IAAK,WAAU;cAAf,CACG,UAEA,4BACC,oBAAC,OAAD;KAAK,WAAU;eACb,oBAAC,kBAAD;MAAkB,OAAO;MAA2B;KAAe,CAAA;IAChE,CAAA,CAEJ;;EACgB,CAAA;CACR,CAAA;AAErB;AAEA,IAAa,iBAA4C,EAAE,UAAU,WAAW;CAC9E,MAAM,QAAQ,KAAK;CACnB,MAAM,SAAS,MAAM,UAAU;CAC/B,MAAM,WAAW,MAAM;CACvB,MAAM,eAAe,MAAM,OAAO;CAClC,MAAM,SAAS,MAAM,UAAU;CAC/B,MAAM,eAAe,MAAM;CAC3B,MAAM,eAAe,MAAM,gBAAgB;CAC3C,MAAM,iBAAiB,MAAM,kBAAkB,CAAC;CAChD,MAAM,QAAQ,MAAM;CACpB,MAAM,EAAE,QAAQ,aAAa,QAAQ,gBAAgB,cAC7C,cAAc,KAAK,MAAM,GAC/B,CAAC,KAAK,MAAM,CACd;CACA,MAAM,gBAAgB,eAAe;EAAE,GAAG;EAAa,GAAG;CAAM,IAAI,CAAC,aAAa,KAAK,CAAC;CACxF,MAAM,2BAA2B,MAAM;CACvC,MAAM,cAAc,MAAM,eAAe;CACzC,MAAM,eAAe,MAAM;CAC3B,MAAM,oBAAoB,MAAM,qBAAqB,KAAA;CAErD,OACE,oBAAC,MAAD;EACU;EACR,0BAAwB,KAAK;EACnB;EACF;EACM;EACE;EAChB,mBAAmB,eAAe,oBAAoB,KAAA;EACtD,SAAS,YAAY,YAAY;EACjC,WAAU;aAER,EAAE,aAAa,QAAQ,YAAY,OAAO,eAC1C,qBAAC,cAAD;GACE,OAAO;IACL;IACA,cAAc,UAAU,YAAY,KAAK;IACzC,aAAa,KAAK;IAClB;IACQ;IACR;IACA;IACA;IACA,WAAW,UAAU,SAAS,KAAK;GACrC;aAXF;IAaE,oBAAC,mBAAD;KAAmB,aAAa,KAAK;KAAW;IAAQ,CAAA;IAEvD,MAAM,UACL,oBAAC,OAAD;KAAK,WAAU;eAAmD,MAAM;IAAY,CAAA;IAGtF,oBAAC,oBAAD;KAAoB,SAAS;eAC3B,oBAAC,UAAD;MACU;MACM;MACd,OAAO,KAAK;MACc;MACb;MACC;MAEb;KACO,CAAA;IACQ,CAAA;GACR;;CAEL,CAAA;AAEjB"}
@@ -6,6 +6,7 @@ export { FileUploadComponent } from './fields/file-upload';
6
6
  export { FormComponent } from './form';
7
7
  export { HiddenInputComponent } from './fields/hidden-input';
8
8
  export { NumberInputComponent } from './fields/number-input';
9
+ export { OtpInputComponent } from './fields/otp-input';
9
10
  export { PasswordInputComponent } from './fields/password-input';
10
11
  export { RepeaterComponent } from './fields/repeater';
11
12
  export { SelectComponent } from './fields/select';
@@ -6,9 +6,10 @@ import { DateInputComponent } from "./fields/date-input.js";
6
6
  import { FileUploadComponent } from "./fields/file-upload.js";
7
7
  import { HiddenInputComponent } from "./fields/hidden-input.js";
8
8
  import { NumberInputComponent } from "./fields/number-input.js";
9
+ import { OtpInputComponent } from "./fields/otp-input.js";
9
10
  import { PasswordInputComponent } from "./fields/password-input.js";
10
11
  import { RepeaterComponent } from "./fields/repeater.js";
11
12
  import { SelectComponent } from "./fields/select.js";
12
13
  import { TextareaComponent } from "./fields/textarea.js";
13
14
  import { TextInputComponent } from "./fields/text-input.js";
14
- export { BuilderComponent, CheckboxComponent, ChoiceComponent, DateInputComponent, FileUploadComponent, FormComponent, HiddenInputComponent, NumberInputComponent, PasswordInputComponent, RepeaterComponent, SelectComponent, TextInputComponent, TextareaComponent };
15
+ export { BuilderComponent, CheckboxComponent, ChoiceComponent, DateInputComponent, FileUploadComponent, FormComponent, HiddenInputComponent, NumberInputComponent, OtpInputComponent, PasswordInputComponent, RepeaterComponent, SelectComponent, TextInputComponent, TextareaComponent };
@@ -11,7 +11,6 @@ type PrefillSnapshot = {
11
11
  };
12
12
  export { getPath } from './form-path';
13
13
  export declare function collectPrefillTargets(nodes: Node[] | undefined, values: Record<string, unknown>): PrefillTarget[];
14
- export declare function applyPrefillValue(setValue: (name: string, value: unknown) => void, path: string, value: unknown): void;
15
14
  export declare function pathsToClear(previous: PrefillSnapshot, next: PrefillSnapshot): string[];
16
15
  export declare function seededOverrides(targets: PrefillTarget[], values: Record<string, unknown>): string[];
17
16
  export declare function pruneOverrides(overrides: Set<string>, targets: PrefillTarget[]): Set<string>;
@@ -44,9 +44,6 @@ function collectPrefillTargets(nodes, values) {
44
44
  walk(nodes);
45
45
  return targets;
46
46
  }
47
- function applyPrefillValue(setValue, path, value) {
48
- setValue(path, value);
49
- }
50
47
  function pathsToClear(previous, next) {
51
48
  const previousByKey = new Map(previous.targets.map((target) => [target.overrideKey, target]));
52
49
  return next.targets.filter((target) => {
@@ -68,6 +65,6 @@ function pruneOverrides(overrides, targets) {
68
65
  return new Set([...overrides].filter((key) => liveKeys.has(key)));
69
66
  }
70
67
  //#endregion
71
- export { applyPrefillValue, collectPrefillTargets, getPath, pathsToClear, pruneOverrides, seededOverrides };
68
+ export { collectPrefillTargets, getPath, pathsToClear, pruneOverrides, seededOverrides };
72
69
 
73
70
  //# sourceMappingURL=prefill-targets.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prefill-targets.js","names":[],"sources":["../../../resources/js/form/components/prefill-targets.ts"],"sourcesContent":["import type { Node } from \"@lattice-php/lattice/core/types\";\nimport { fieldProps } from \"./field-props\";\nimport { appendPath, getPath } from \"./form-path\";\nimport { buildOverrideKey, rowIdFrom } from \"./override-keys\";\n\nexport type PrefillTarget = {\n path: string;\n overrideKey: string;\n resetOn: string[];\n refreshOn: string[];\n};\n\ntype PrefillSnapshot = {\n targets: PrefillTarget[];\n values: Record<string, unknown>;\n};\n\ntype Block = { type: string; label: string; schema: Node[] };\n\nconst ROW_COLLECTION_TYPES = new Set([\"field.builder\", \"field.repeater\"]);\n\nexport { getPath } from \"./form-path\";\n\nfunction mapDep(dep: string, rowPath: string | null): string {\n if (dep.startsWith(\"@\")) {\n return dep.slice(1);\n }\n\n return rowPath === null ? dep : appendPath(rowPath, dep);\n}\n\nfunction targetFor(\n node: Node,\n rowPath: string | null,\n identityCollectionPath: string | null,\n index = 0,\n row: Record<string, unknown> = {},\n): PrefillTarget | null {\n const props = fieldProps(node);\n\n if (!props.editablePrefill || !props.name) {\n return null;\n }\n\n const rowId = rowIdFrom(row);\n\n return {\n path: rowPath === null ? props.name : appendPath(rowPath, props.name),\n overrideKey:\n identityCollectionPath === null\n ? props.name\n : buildOverrideKey(identityCollectionPath, rowId, index, props.name),\n resetOn: (props.prefillResetOn ?? []).map((dep) => mapDep(dep, rowPath)),\n refreshOn: (props.prefillRefreshOn ?? []).map((dep) => mapDep(dep, rowPath)),\n };\n}\n\nexport function collectPrefillTargets(\n nodes: Node[] | undefined,\n values: Record<string, unknown>,\n): PrefillTarget[] {\n const targets: PrefillTarget[] = [];\n\n const walk = (\n list: Node[] | undefined,\n rowPath: string | null = null,\n identityRowPath: string | null = null,\n identityCollectionPath: string | null = null,\n index = 0,\n row: Record<string, unknown> = {},\n ): void => {\n for (const node of list ?? []) {\n if (ROW_COLLECTION_TYPES.has(node.type)) {\n const name = fieldProps(node).name;\n if (name) {\n const childCollectionPath = appendPath(rowPath, name);\n const childIdentityCollectionPath = appendPath(identityRowPath, name);\n const storedRows = getPath(values, childCollectionPath);\n const rows = Array.isArray(storedRows)\n ? (storedRows as Array<Record<string, unknown>>)\n : [];\n const blocks = (node as unknown as { blocks?: Block[] }).blocks;\n\n rows.forEach((childRow, childIndex) => {\n const template = blocks\n ? (blocks.find((block) => block.type === childRow.type)?.schema ?? [])\n : (node.schema ?? []);\n\n walk(\n template,\n appendPath(childCollectionPath, childIndex),\n appendPath(childIdentityCollectionPath, rowIdFrom(childRow) ?? childIndex),\n childIdentityCollectionPath,\n childIndex,\n childRow,\n );\n });\n }\n\n continue;\n }\n\n const target = targetFor(node, rowPath, identityCollectionPath, index, row);\n if (target) {\n targets.push(target);\n }\n\n walk(node.schema, rowPath, identityRowPath, identityCollectionPath, index, row);\n }\n };\n\n walk(nodes);\n\n return targets;\n}\n\nexport function applyPrefillValue(\n setValue: (name: string, value: unknown) => void,\n path: string,\n value: unknown,\n): void {\n setValue(path, value);\n}\n\nexport function pathsToClear(previous: PrefillSnapshot, next: PrefillSnapshot): string[] {\n const previousByKey = new Map(\n previous.targets.map((target) => [target.overrideKey, target] as const),\n );\n\n return next.targets\n .filter((target) => {\n const previousTarget = previousByKey.get(target.overrideKey);\n\n return target.resetOn.some((dep, index) => {\n const previousDep = previousTarget?.resetOn[index] ?? dep;\n\n return !Object.is(getPath(previous.values, previousDep), getPath(next.values, dep));\n });\n })\n .map((target) => target.overrideKey);\n}\n\nexport function seededOverrides(\n targets: PrefillTarget[],\n values: Record<string, unknown>,\n): string[] {\n return targets\n .filter((target) => {\n const value = getPath(values, target.path);\n\n return value !== undefined && value !== null && value !== \"\";\n })\n .map((target) => target.overrideKey);\n}\n\nexport function pruneOverrides(overrides: Set<string>, targets: PrefillTarget[]): Set<string> {\n const liveKeys = new Set(targets.map((target) => target.overrideKey));\n\n return new Set([...overrides].filter((key) => liveKeys.has(key)));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"prefill-targets.js","names":[],"sources":["../../../resources/js/form/components/prefill-targets.ts"],"sourcesContent":["import type { Node } from \"@lattice-php/lattice/core/types\";\nimport { fieldProps } from \"./field-props\";\nimport { appendPath, getPath } from \"./form-path\";\nimport { buildOverrideKey, rowIdFrom } from \"./override-keys\";\n\nexport type PrefillTarget = {\n path: string;\n overrideKey: string;\n resetOn: string[];\n refreshOn: string[];\n};\n\ntype PrefillSnapshot = {\n targets: PrefillTarget[];\n values: Record<string, unknown>;\n};\n\ntype Block = { type: string; label: string; schema: Node[] };\n\nconst ROW_COLLECTION_TYPES = new Set([\"field.builder\", \"field.repeater\"]);\n\nexport { getPath } from \"./form-path\";\n\nfunction mapDep(dep: string, rowPath: string | null): string {\n if (dep.startsWith(\"@\")) {\n return dep.slice(1);\n }\n\n return rowPath === null ? dep : appendPath(rowPath, dep);\n}\n\nfunction targetFor(\n node: Node,\n rowPath: string | null,\n identityCollectionPath: string | null,\n index = 0,\n row: Record<string, unknown> = {},\n): PrefillTarget | null {\n const props = fieldProps(node);\n\n if (!props.editablePrefill || !props.name) {\n return null;\n }\n\n const rowId = rowIdFrom(row);\n\n return {\n path: rowPath === null ? props.name : appendPath(rowPath, props.name),\n overrideKey:\n identityCollectionPath === null\n ? props.name\n : buildOverrideKey(identityCollectionPath, rowId, index, props.name),\n resetOn: (props.prefillResetOn ?? []).map((dep) => mapDep(dep, rowPath)),\n refreshOn: (props.prefillRefreshOn ?? []).map((dep) => mapDep(dep, rowPath)),\n };\n}\n\nexport function collectPrefillTargets(\n nodes: Node[] | undefined,\n values: Record<string, unknown>,\n): PrefillTarget[] {\n const targets: PrefillTarget[] = [];\n\n const walk = (\n list: Node[] | undefined,\n rowPath: string | null = null,\n identityRowPath: string | null = null,\n identityCollectionPath: string | null = null,\n index = 0,\n row: Record<string, unknown> = {},\n ): void => {\n for (const node of list ?? []) {\n if (ROW_COLLECTION_TYPES.has(node.type)) {\n const name = fieldProps(node).name;\n if (name) {\n const childCollectionPath = appendPath(rowPath, name);\n const childIdentityCollectionPath = appendPath(identityRowPath, name);\n const storedRows = getPath(values, childCollectionPath);\n const rows = Array.isArray(storedRows)\n ? (storedRows as Array<Record<string, unknown>>)\n : [];\n const blocks = (node as unknown as { blocks?: Block[] }).blocks;\n\n rows.forEach((childRow, childIndex) => {\n const template = blocks\n ? (blocks.find((block) => block.type === childRow.type)?.schema ?? [])\n : (node.schema ?? []);\n\n walk(\n template,\n appendPath(childCollectionPath, childIndex),\n appendPath(childIdentityCollectionPath, rowIdFrom(childRow) ?? childIndex),\n childIdentityCollectionPath,\n childIndex,\n childRow,\n );\n });\n }\n\n continue;\n }\n\n const target = targetFor(node, rowPath, identityCollectionPath, index, row);\n if (target) {\n targets.push(target);\n }\n\n walk(node.schema, rowPath, identityRowPath, identityCollectionPath, index, row);\n }\n };\n\n walk(nodes);\n\n return targets;\n}\n\nexport function pathsToClear(previous: PrefillSnapshot, next: PrefillSnapshot): string[] {\n const previousByKey = new Map(\n previous.targets.map((target) => [target.overrideKey, target] as const),\n );\n\n return next.targets\n .filter((target) => {\n const previousTarget = previousByKey.get(target.overrideKey);\n\n return target.resetOn.some((dep, index) => {\n const previousDep = previousTarget?.resetOn[index] ?? dep;\n\n return !Object.is(getPath(previous.values, previousDep), getPath(next.values, dep));\n });\n })\n .map((target) => target.overrideKey);\n}\n\nexport function seededOverrides(\n targets: PrefillTarget[],\n values: Record<string, unknown>,\n): string[] {\n return targets\n .filter((target) => {\n const value = getPath(values, target.path);\n\n return value !== undefined && value !== null && value !== \"\";\n })\n .map((target) => target.overrideKey);\n}\n\nexport function pruneOverrides(overrides: Set<string>, targets: PrefillTarget[]): Set<string> {\n const liveKeys = new Set(targets.map((target) => target.overrideKey));\n\n return new Set([...overrides].filter((key) => liveKeys.has(key)));\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { walkFields } from "./field-props.js";
2
2
  import { postFormAction } from "./form-transport.js";
3
3
  import { getPath } from "./form-path.js";
4
- import { applyPrefillValue, collectPrefillTargets, pathsToClear, pruneOverrides, seededOverrides } from "./prefill-targets.js";
4
+ import { collectPrefillTargets, pathsToClear, pruneOverrides, seededOverrides } from "./prefill-targets.js";
5
5
  import { useFormValues, useSetFormValue } from "./values.js";
6
6
  import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
7
7
  //#region resources/js/form/components/use-form-resolver.ts
@@ -70,7 +70,7 @@ function useFormResolver(action, componentRef, nodes) {
70
70
  const targetsByPath = new Map(targetsRef.current.map((target) => [target.path, target]));
71
71
  for (const [path, value] of Object.entries(response.prefill ?? {})) {
72
72
  const target = targetsByPath.get(path);
73
- if (target && !overrides.current.has(target.overrideKey)) applyPrefillValue(setValue, path, value);
73
+ if (target && !overrides.current.has(target.overrideKey)) setValue(path, value);
74
74
  }
75
75
  if (response.fields) setResolved(response.fields);
76
76
  }).catch(() => {});
@@ -1 +1 @@
1
- {"version":3,"file":"use-form-resolver.js","names":[],"sources":["../../../resources/js/form/components/use-form-resolver.ts"],"sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport { walkFields } from \"./field-props\";\nimport { FORM_DEBOUNCE_MS, postFormAction } from \"./form-transport\";\nimport {\n applyPrefillValue,\n collectPrefillTargets,\n getPath,\n pathsToClear,\n pruneOverrides,\n seededOverrides,\n} from \"./prefill-targets\";\nimport type { PrefillController } from \"./prefill-context\";\nimport { useFormValues, useSetFormValue } from \"./values\";\n\ntype ResolveResponse = {\n fields?: Record<string, Node>;\n values?: Record<string, unknown>;\n prefill?: Record<string, unknown>;\n};\n\nexport type FormResolver = {\n nodes: Record<string, Node>;\n markUserEdit: PrefillController[\"markUserEdit\"];\n};\n\nexport function useFormResolver(\n action: string,\n componentRef: string,\n nodes: Node[] | undefined,\n): FormResolver {\n const values = useFormValues();\n const setValue = useSetFormValue();\n const [resolved, setResolved] = useState<Record<string, Node>>({});\n\n const targets = useMemo(() => collectPrefillTargets(nodes, values), [nodes, values]);\n\n const overrides = useRef<Set<string>>(new Set());\n const seededOverrideKeys = useRef<Set<string>>(new Set());\n const previousValues = useRef<Record<string, unknown>>(values);\n const previousTargets = useRef(targets);\n\n // Read targets via a ref inside the effect: `targets` changes identity on every\n // value change, but the effect must fire only when `watchSignature` changes\n // (which already reflects added/removed target paths). Keeping `targets` out of\n // the dep array preserves the targeted watch in non-\"any\" mode.\n const targetsRef = useRef(targets);\n targetsRef.current = targets;\n\n useLayoutEffect(() => {\n const liveOverrideKeys = new Set(targets.map((target) => target.overrideKey));\n seededOverrideKeys.current = new Set(\n [...seededOverrideKeys.current].filter((overrideKey) => liveOverrideKeys.has(overrideKey)),\n );\n const freshTargets = targets.filter(\n (target) => !seededOverrideKeys.current.has(target.overrideKey),\n );\n\n // Layout ordering seeds loaded values before the first resolve effect can apply prefill.\n for (const overrideKey of seededOverrides(freshTargets, values)) {\n overrides.current.add(overrideKey);\n }\n\n for (const target of freshTargets) {\n seededOverrideKeys.current.add(target.overrideKey);\n }\n }, [targets, values]);\n\n const markUserEdit = useCallback((overrideKey: string) => {\n overrides.current.add(overrideKey);\n }, []);\n\n const watch = useMemo(() => {\n const keys = new Set<string>();\n let any = false;\n walkFields(nodes, (props) => {\n if (Array.isArray(props.dependsOnKeys)) {\n for (const key of props.dependsOnKeys) {\n keys.add(String(key));\n }\n }\n if (props.dependsOnAny) {\n any = true;\n }\n });\n return { keys: [...keys], any };\n }, [nodes]);\n\n const watchPaths = useMemo(() => {\n const set = new Set<string>(watch.keys);\n for (const target of targets) {\n for (const dep of target.resetOn) {\n set.add(dep);\n }\n for (const dep of target.refreshOn) {\n set.add(dep);\n }\n }\n return [...set];\n }, [watch.keys, targets]);\n\n // In \"any\" mode the values store keeps a stable reference until something\n // actually changes, so its identity is the change signal. Otherwise hash the\n // watched paths (form-level and per-row).\n const watchSignature = watch.any\n ? values\n : JSON.stringify(watchPaths.map((path) => getPath(values, path)));\n\n useEffect(() => {\n if (watchPaths.length === 0 && !watch.any) {\n return;\n }\n\n const previous = previousValues.current;\n previousValues.current = values;\n\n // `resetOn` deps unlock a path (a fresh product gives a fresh price); `refreshOn`\n // deps only re-ask the server and never clear an override (a customer change\n // re-prices untouched rows but leaves user-edited ones alone).\n for (const overrideKey of pathsToClear(\n { targets: previousTargets.current, values: previous },\n { targets: targetsRef.current, values },\n )) {\n overrides.current.delete(overrideKey);\n }\n previousTargets.current = targetsRef.current;\n\n // Override ownership is row-id keyed, so pruning removes departed rows without reindex drift.\n overrides.current = pruneOverrides(overrides.current, targetsRef.current);\n\n const controller = new AbortController();\n\n const timer = window.setTimeout(() => {\n void postFormAction<ResolveResponse>(\n action,\n componentRef,\n { _resolve: true, ...values },\n controller.signal,\n )\n .then((response) => {\n if (!response) {\n return;\n }\n for (const [name, value] of Object.entries(response.values ?? {})) {\n setValue(name, value);\n }\n const targetsByPath = new Map(\n targetsRef.current.map((target) => [target.path, target] as const),\n );\n for (const [path, value] of Object.entries(response.prefill ?? {})) {\n const target = targetsByPath.get(path);\n\n if (target && !overrides.current.has(target.overrideKey)) {\n applyPrefillValue(setValue, path, value);\n }\n }\n if (response.fields) {\n setResolved(response.fields);\n }\n })\n .catch(() => {});\n }, FORM_DEBOUNCE_MS);\n\n return () => {\n window.clearTimeout(timer);\n controller.abort();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [watchSignature, action, componentRef, watch.any, setValue]);\n\n return { nodes: resolved, markUserEdit };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-form-resolver.js","names":[],"sources":["../../../resources/js/form/components/use-form-resolver.ts"],"sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport { walkFields } from \"./field-props\";\nimport { FORM_DEBOUNCE_MS, postFormAction } from \"./form-transport\";\nimport {\n collectPrefillTargets,\n getPath,\n pathsToClear,\n pruneOverrides,\n seededOverrides,\n} from \"./prefill-targets\";\nimport type { PrefillController } from \"./prefill-context\";\nimport { useFormValues, useSetFormValue } from \"./values\";\n\ntype ResolveResponse = {\n fields?: Record<string, Node>;\n values?: Record<string, unknown>;\n prefill?: Record<string, unknown>;\n};\n\nexport type FormResolver = {\n nodes: Record<string, Node>;\n markUserEdit: PrefillController[\"markUserEdit\"];\n};\n\nexport function useFormResolver(\n action: string,\n componentRef: string,\n nodes: Node[] | undefined,\n): FormResolver {\n const values = useFormValues();\n const setValue = useSetFormValue();\n const [resolved, setResolved] = useState<Record<string, Node>>({});\n\n const targets = useMemo(() => collectPrefillTargets(nodes, values), [nodes, values]);\n\n const overrides = useRef<Set<string>>(new Set());\n const seededOverrideKeys = useRef<Set<string>>(new Set());\n const previousValues = useRef<Record<string, unknown>>(values);\n const previousTargets = useRef(targets);\n\n // Read targets via a ref inside the effect: `targets` changes identity on every\n // value change, but the effect must fire only when `watchSignature` changes\n // (which already reflects added/removed target paths). Keeping `targets` out of\n // the dep array preserves the targeted watch in non-\"any\" mode.\n const targetsRef = useRef(targets);\n targetsRef.current = targets;\n\n useLayoutEffect(() => {\n const liveOverrideKeys = new Set(targets.map((target) => target.overrideKey));\n seededOverrideKeys.current = new Set(\n [...seededOverrideKeys.current].filter((overrideKey) => liveOverrideKeys.has(overrideKey)),\n );\n const freshTargets = targets.filter(\n (target) => !seededOverrideKeys.current.has(target.overrideKey),\n );\n\n // Layout ordering seeds loaded values before the first resolve effect can apply prefill.\n for (const overrideKey of seededOverrides(freshTargets, values)) {\n overrides.current.add(overrideKey);\n }\n\n for (const target of freshTargets) {\n seededOverrideKeys.current.add(target.overrideKey);\n }\n }, [targets, values]);\n\n const markUserEdit = useCallback((overrideKey: string) => {\n overrides.current.add(overrideKey);\n }, []);\n\n const watch = useMemo(() => {\n const keys = new Set<string>();\n let any = false;\n walkFields(nodes, (props) => {\n if (Array.isArray(props.dependsOnKeys)) {\n for (const key of props.dependsOnKeys) {\n keys.add(String(key));\n }\n }\n if (props.dependsOnAny) {\n any = true;\n }\n });\n return { keys: [...keys], any };\n }, [nodes]);\n\n const watchPaths = useMemo(() => {\n const set = new Set<string>(watch.keys);\n for (const target of targets) {\n for (const dep of target.resetOn) {\n set.add(dep);\n }\n for (const dep of target.refreshOn) {\n set.add(dep);\n }\n }\n return [...set];\n }, [watch.keys, targets]);\n\n // In \"any\" mode the values store keeps a stable reference until something\n // actually changes, so its identity is the change signal. Otherwise hash the\n // watched paths (form-level and per-row).\n const watchSignature = watch.any\n ? values\n : JSON.stringify(watchPaths.map((path) => getPath(values, path)));\n\n useEffect(() => {\n if (watchPaths.length === 0 && !watch.any) {\n return;\n }\n\n const previous = previousValues.current;\n previousValues.current = values;\n\n // `resetOn` deps unlock a path (a fresh product gives a fresh price); `refreshOn`\n // deps only re-ask the server and never clear an override (a customer change\n // re-prices untouched rows but leaves user-edited ones alone).\n for (const overrideKey of pathsToClear(\n { targets: previousTargets.current, values: previous },\n { targets: targetsRef.current, values },\n )) {\n overrides.current.delete(overrideKey);\n }\n previousTargets.current = targetsRef.current;\n\n // Override ownership is row-id keyed, so pruning removes departed rows without reindex drift.\n overrides.current = pruneOverrides(overrides.current, targetsRef.current);\n\n const controller = new AbortController();\n\n const timer = window.setTimeout(() => {\n void postFormAction<ResolveResponse>(\n action,\n componentRef,\n { _resolve: true, ...values },\n controller.signal,\n )\n .then((response) => {\n if (!response) {\n return;\n }\n for (const [name, value] of Object.entries(response.values ?? {})) {\n setValue(name, value);\n }\n const targetsByPath = new Map(\n targetsRef.current.map((target) => [target.path, target] as const),\n );\n for (const [path, value] of Object.entries(response.prefill ?? {})) {\n const target = targetsByPath.get(path);\n\n if (target && !overrides.current.has(target.overrideKey)) {\n setValue(path, value);\n }\n }\n if (response.fields) {\n setResolved(response.fields);\n }\n })\n .catch(() => {});\n }, FORM_DEBOUNCE_MS);\n\n return () => {\n window.clearTimeout(timer);\n controller.abort();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [watchSignature, action, componentRef, watch.any, setValue]);\n\n return { nodes: resolved, markUserEdit };\n}\n"],"mappings":"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"}
@@ -17,6 +17,7 @@ var formComponents = createPlugin({
17
17
  "field.file-upload": lazyComponent(loadFormComponent("FileUploadComponent")),
18
18
  "field.hidden-input": lazyComponent(loadFormComponent("HiddenInputComponent")),
19
19
  "field.number-input": lazyComponent(loadFormComponent("NumberInputComponent")),
20
+ "field.otp": lazyComponent(loadFormComponent("OtpInputComponent")),
20
21
  "field.password-input": lazyComponent(loadFormComponent("PasswordInputComponent")),
21
22
  "field.repeater": lazyComponent(loadFormComponent("RepeaterComponent")),
22
23
  "field.rich-editor": lazyComponent(async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../resources/js/form/index.ts"],"sourcesContent":["import { createPlugin, lazyComponent } from \"@lattice-php/lattice/core/registry\";\nimport type { RendererComponent, RendererComponentModule } from \"@lattice-php/lattice/core/types\";\nimport { FormSkeletonComponent } from \"./skeleton\";\n\ntype FormComponentName =\n | \"BuilderComponent\"\n | \"CheckboxComponent\"\n | \"ChoiceComponent\"\n | \"DateInputComponent\"\n | \"FileUploadComponent\"\n | \"FormComponent\"\n | \"HiddenInputComponent\"\n | \"NumberInputComponent\"\n | \"PasswordInputComponent\"\n | \"RepeaterComponent\"\n | \"SelectComponent\"\n | \"TextareaComponent\"\n | \"TextInputComponent\";\n\nconst loadFormComponents = () => import(\"./components\");\n\nfunction loadFormComponent<TType extends string>(\n name: FormComponentName,\n): () => Promise<RendererComponentModule<TType>> {\n return async () => {\n const components = await loadFormComponents();\n\n return {\n default: components[name] as unknown as RendererComponent<TType>,\n };\n };\n}\n\nexport const formComponents = createPlugin({\n components: {\n form: lazyComponent(loadFormComponent(\"FormComponent\"), {\n fallback: FormSkeletonComponent,\n }),\n \"field.builder\": lazyComponent(loadFormComponent(\"BuilderComponent\")),\n \"field.checkbox\": lazyComponent(loadFormComponent(\"CheckboxComponent\")),\n \"field.choice\": lazyComponent(loadFormComponent(\"ChoiceComponent\")),\n \"field.date-input\": lazyComponent(loadFormComponent(\"DateInputComponent\")),\n \"field.file-upload\": lazyComponent(loadFormComponent(\"FileUploadComponent\")),\n \"field.hidden-input\": lazyComponent(loadFormComponent(\"HiddenInputComponent\")),\n \"field.number-input\": lazyComponent(loadFormComponent(\"NumberInputComponent\")),\n \"field.password-input\": lazyComponent(loadFormComponent(\"PasswordInputComponent\")),\n \"field.repeater\": lazyComponent(loadFormComponent(\"RepeaterComponent\")),\n // Loaded from its own module so TipTap is split into a separate chunk,\n // only fetched on pages that actually render a rich editor.\n \"field.rich-editor\": lazyComponent<\"field.rich-editor\">(async () => {\n const { RichEditorComponent } = await import(\"./components/fields/rich-editor\");\n\n return { default: RichEditorComponent };\n }),\n \"field.select\": lazyComponent(loadFormComponent(\"SelectComponent\")),\n \"field.textarea\": lazyComponent(loadFormComponent(\"TextareaComponent\")),\n \"field.text-input\": lazyComponent(loadFormComponent(\"TextInputComponent\")),\n },\n name: \"lattice/form\",\n});\n"],"mappings":";;;AAmBA,IAAM,2BAA2B,OAAO;AAExC,SAAS,kBACP,MAC+C;CAC/C,OAAO,YAAY;EAGjB,OAAO,EACL,UAAS,MAHc,mBAAmB,GAGtB,MACtB;CACF;AACF;AAEA,IAAa,iBAAiB,aAAa;CACzC,YAAY;EACV,MAAM,cAAc,kBAAkB,eAAe,GAAG,EACtD,UAAU,sBACZ,CAAC;EACD,iBAAiB,cAAc,kBAAkB,kBAAkB,CAAC;EACpE,kBAAkB,cAAc,kBAAkB,mBAAmB,CAAC;EACtE,gBAAgB,cAAc,kBAAkB,iBAAiB,CAAC;EAClE,oBAAoB,cAAc,kBAAkB,oBAAoB,CAAC;EACzE,qBAAqB,cAAc,kBAAkB,qBAAqB,CAAC;EAC3E,sBAAsB,cAAc,kBAAkB,sBAAsB,CAAC;EAC7E,sBAAsB,cAAc,kBAAkB,sBAAsB,CAAC;EAC7E,wBAAwB,cAAc,kBAAkB,wBAAwB,CAAC;EACjF,kBAAkB,cAAc,kBAAkB,mBAAmB,CAAC;EAGtE,qBAAqB,cAAmC,YAAY;GAClE,MAAM,EAAE,wBAAwB,MAAM,OAAO;GAE7C,OAAO,EAAE,SAAS,oBAAoB;EACxC,CAAC;EACD,gBAAgB,cAAc,kBAAkB,iBAAiB,CAAC;EAClE,kBAAkB,cAAc,kBAAkB,mBAAmB,CAAC;EACtE,oBAAoB,cAAc,kBAAkB,oBAAoB,CAAC;CAC3E;CACA,MAAM;AACR,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../resources/js/form/index.ts"],"sourcesContent":["import { createPlugin, lazyComponent } from \"@lattice-php/lattice/core/registry\";\nimport type { RendererComponent, RendererComponentModule } from \"@lattice-php/lattice/core/types\";\nimport { FormSkeletonComponent } from \"./skeleton\";\n\ntype FormComponentName =\n | \"BuilderComponent\"\n | \"CheckboxComponent\"\n | \"ChoiceComponent\"\n | \"DateInputComponent\"\n | \"FileUploadComponent\"\n | \"FormComponent\"\n | \"HiddenInputComponent\"\n | \"NumberInputComponent\"\n | \"OtpInputComponent\"\n | \"PasswordInputComponent\"\n | \"RepeaterComponent\"\n | \"SelectComponent\"\n | \"TextareaComponent\"\n | \"TextInputComponent\";\n\nconst loadFormComponents = () => import(\"./components\");\n\nfunction loadFormComponent<TType extends string>(\n name: FormComponentName,\n): () => Promise<RendererComponentModule<TType>> {\n return async () => {\n const components = await loadFormComponents();\n\n return {\n default: components[name] as unknown as RendererComponent<TType>,\n };\n };\n}\n\nexport const formComponents = createPlugin({\n components: {\n form: lazyComponent(loadFormComponent(\"FormComponent\"), {\n fallback: FormSkeletonComponent,\n }),\n \"field.builder\": lazyComponent(loadFormComponent(\"BuilderComponent\")),\n \"field.checkbox\": lazyComponent(loadFormComponent(\"CheckboxComponent\")),\n \"field.choice\": lazyComponent(loadFormComponent(\"ChoiceComponent\")),\n \"field.date-input\": lazyComponent(loadFormComponent(\"DateInputComponent\")),\n \"field.file-upload\": lazyComponent(loadFormComponent(\"FileUploadComponent\")),\n \"field.hidden-input\": lazyComponent(loadFormComponent(\"HiddenInputComponent\")),\n \"field.number-input\": lazyComponent(loadFormComponent(\"NumberInputComponent\")),\n \"field.otp\": lazyComponent(loadFormComponent(\"OtpInputComponent\")),\n \"field.password-input\": lazyComponent(loadFormComponent(\"PasswordInputComponent\")),\n \"field.repeater\": lazyComponent(loadFormComponent(\"RepeaterComponent\")),\n // Loaded from its own module so TipTap is split into a separate chunk,\n // only fetched on pages that actually render a rich editor.\n \"field.rich-editor\": lazyComponent<\"field.rich-editor\">(async () => {\n const { RichEditorComponent } = await import(\"./components/fields/rich-editor\");\n\n return { default: RichEditorComponent };\n }),\n \"field.select\": lazyComponent(loadFormComponent(\"SelectComponent\")),\n \"field.textarea\": lazyComponent(loadFormComponent(\"TextareaComponent\")),\n \"field.text-input\": lazyComponent(loadFormComponent(\"TextInputComponent\")),\n },\n name: \"lattice/form\",\n});\n"],"mappings":";;;AAoBA,IAAM,2BAA2B,OAAO;AAExC,SAAS,kBACP,MAC+C;CAC/C,OAAO,YAAY;EAGjB,OAAO,EACL,UAAS,MAHc,mBAAmB,GAGtB,MACtB;CACF;AACF;AAEA,IAAa,iBAAiB,aAAa;CACzC,YAAY;EACV,MAAM,cAAc,kBAAkB,eAAe,GAAG,EACtD,UAAU,sBACZ,CAAC;EACD,iBAAiB,cAAc,kBAAkB,kBAAkB,CAAC;EACpE,kBAAkB,cAAc,kBAAkB,mBAAmB,CAAC;EACtE,gBAAgB,cAAc,kBAAkB,iBAAiB,CAAC;EAClE,oBAAoB,cAAc,kBAAkB,oBAAoB,CAAC;EACzE,qBAAqB,cAAc,kBAAkB,qBAAqB,CAAC;EAC3E,sBAAsB,cAAc,kBAAkB,sBAAsB,CAAC;EAC7E,sBAAsB,cAAc,kBAAkB,sBAAsB,CAAC;EAC7E,aAAa,cAAc,kBAAkB,mBAAmB,CAAC;EACjE,wBAAwB,cAAc,kBAAkB,wBAAwB,CAAC;EACjF,kBAAkB,cAAc,kBAAkB,mBAAmB,CAAC;EAGtE,qBAAqB,cAAmC,YAAY;GAClE,MAAM,EAAE,wBAAwB,MAAM,OAAO;GAE7C,OAAO,EAAE,SAAS,oBAAoB;EACxC,CAAC;EACD,gBAAgB,cAAc,kBAAkB,iBAAiB,CAAC;EAClE,kBAAkB,cAAc,kBAAkB,mBAAmB,CAAC;EACtE,oBAAoB,cAAc,kBAAkB,oBAAoB,CAAC;CAC3E;CACA,MAAM;AACR,CAAC"}
@@ -5,7 +5,7 @@ var FormSkeletonComponent = ({ node }) => {
5
5
  const fieldCount = Math.max(1, Math.min(4, node.schema?.find((child) => child.type === "grid")?.schema?.filter((child) => child.type !== "field.hidden-input").length ?? 2));
6
6
  return /* @__PURE__ */ jsx("div", {
7
7
  "aria-hidden": "true",
8
- className: "mx-auto flex w-full max-w-md flex-col gap-6 rounded-lt border border-lt-border bg-lt-surface p-6 shadow-xs",
8
+ className: "mx-auto flex w-full max-w-md flex-col gap-6 rounded-lt border border-lt-border bg-lt-surface p-6 shadow-lt-sm",
9
9
  "data-lattice-skeleton": node.id ?? node.type,
10
10
  children: /* @__PURE__ */ jsxs("div", {
11
11
  className: "grid gap-6",
@@ -1 +1 @@
1
- {"version":3,"file":"skeleton.js","names":[],"sources":["../../resources/js/form/skeleton.tsx"],"sourcesContent":["import { Skeleton } from \"@lattice-php/lattice/core/components/skeleton\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\n\nexport const FormSkeletonComponent: RendererComponent<\"form\"> = ({ node }) => {\n const fieldCount = Math.max(\n 1,\n Math.min(\n 4,\n node.schema\n ?.find((child) => child.type === \"grid\")\n ?.schema?.filter((child) => child.type !== \"field.hidden-input\").length ?? 2,\n ),\n );\n\n return (\n <div\n aria-hidden=\"true\"\n className=\"mx-auto flex w-full max-w-md flex-col gap-6 rounded-lt border border-lt-border bg-lt-surface p-6 shadow-xs\"\n data-lattice-skeleton={node.id ?? node.type}\n >\n <div className=\"grid gap-6\">\n {Array.from({ length: fieldCount }).map((_, index) => (\n <div className=\"grid gap-2\" key={index}>\n <Skeleton className=\"h-4 w-28\" />\n <Skeleton className=\"h-10\" />\n </div>\n ))}\n\n <Skeleton className=\"mt-4 h-10\" />\n </div>\n </div>\n );\n};\n"],"mappings":";;;AAGA,IAAa,yBAAoD,EAAE,WAAW;CAC5E,MAAM,aAAa,KAAK,IACtB,GACA,KAAK,IACH,GACA,KAAK,QACD,MAAM,UAAU,MAAM,SAAS,MAAM,GACrC,QAAQ,QAAQ,UAAU,MAAM,SAAS,oBAAoB,EAAE,UAAU,CAC/E,CACF;CAEA,OACE,oBAAC,OAAD;EACE,eAAY;EACZ,WAAU;EACV,yBAAuB,KAAK,MAAM,KAAK;YAEvC,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,MAAM,KAAK,EAAE,QAAQ,WAAW,CAAC,EAAE,KAAK,GAAG,UAC1C,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,UAAD,EAAU,WAAU,WAAY,CAAA,GAChC,oBAAC,UAAD,EAAU,WAAU,OAAQ,CAAA,CACzB;MAH4B,KAG5B,CACN,GAED,oBAAC,UAAD,EAAU,WAAU,YAAa,CAAA,CAC9B;;CACF,CAAA;AAET"}
1
+ {"version":3,"file":"skeleton.js","names":[],"sources":["../../resources/js/form/skeleton.tsx"],"sourcesContent":["import { Skeleton } from \"@lattice-php/lattice/core/components/skeleton\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\n\nexport const FormSkeletonComponent: RendererComponent<\"form\"> = ({ node }) => {\n const fieldCount = Math.max(\n 1,\n Math.min(\n 4,\n node.schema\n ?.find((child) => child.type === \"grid\")\n ?.schema?.filter((child) => child.type !== \"field.hidden-input\").length ?? 2,\n ),\n );\n\n return (\n <div\n aria-hidden=\"true\"\n className=\"mx-auto flex w-full max-w-md flex-col gap-6 rounded-lt border border-lt-border bg-lt-surface p-6 shadow-lt-sm\"\n data-lattice-skeleton={node.id ?? node.type}\n >\n <div className=\"grid gap-6\">\n {Array.from({ length: fieldCount }).map((_, index) => (\n <div className=\"grid gap-2\" key={index}>\n <Skeleton className=\"h-4 w-28\" />\n <Skeleton className=\"h-10\" />\n </div>\n ))}\n\n <Skeleton className=\"mt-4 h-10\" />\n </div>\n </div>\n );\n};\n"],"mappings":";;;AAGA,IAAa,yBAAoD,EAAE,WAAW;CAC5E,MAAM,aAAa,KAAK,IACtB,GACA,KAAK,IACH,GACA,KAAK,QACD,MAAM,UAAU,MAAM,SAAS,MAAM,GACrC,QAAQ,QAAQ,UAAU,MAAM,SAAS,oBAAoB,EAAE,UAAU,CAC/E,CACF;CAEA,OACE,oBAAC,OAAD;EACE,eAAY;EACZ,WAAU;EACV,yBAAuB,KAAK,MAAM,KAAK;YAEvC,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,MAAM,KAAK,EAAE,QAAQ,WAAW,CAAC,EAAE,KAAK,GAAG,UAC1C,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,UAAD,EAAU,WAAU,WAAY,CAAA,GAChC,oBAAC,UAAD,EAAU,WAAU,OAAQ,CAAA,CACzB;MAH4B,KAG5B,CACN,GAED,oBAAC,UAAD,EAAU,WAAU,YAAa,CAAA,CAC9B;;CACF,CAAA;AAET"}
@@ -1,6 +1,6 @@
1
1
  import { EventBridge } from "../events/event-bridge.js";
2
- import { useCallback } from "react";
3
2
  import { router } from "@inertiajs/react";
3
+ import { useCallback } from "react";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region resources/js/i18n/locale-reload.tsx
6
6
  function LocaleReload({ preserveScroll = true, preserveState = true }) {
@@ -0,0 +1,4 @@
1
+ import { Translatable } from '../types/generated';
2
+ export type Translate = (key: string, defaultValue?: string, options?: Record<string, unknown>) => string;
3
+ export declare function isTranslatable(value: unknown): value is Translatable;
4
+ export declare function resolveTranslatable(value: Translatable, payload: Record<string, unknown>, t: Translate): string;
@@ -0,0 +1,24 @@
1
+ //#region resources/js/i18n/translatable.ts
2
+ function isTranslatable(value) {
3
+ return typeof value === "object" && value !== null && typeof value.key === "string";
4
+ }
5
+ function readPath(payload, path) {
6
+ return path.split(".").reduce((node, segment) => {
7
+ if (typeof node === "object" && node !== null) return node[segment];
8
+ }, payload);
9
+ }
10
+ function resolveTranslatable(value, payload, t) {
11
+ const fromPayload = {};
12
+ for (const [name, path] of Object.entries(value.payload)) {
13
+ const read = readPath(payload, path);
14
+ fromPayload[name] = read === void 0 ? "" : read;
15
+ }
16
+ return t(value.key, value.key, {
17
+ ...value.replacements,
18
+ ...fromPayload
19
+ });
20
+ }
21
+ //#endregion
22
+ export { isTranslatable, resolveTranslatable };
23
+
24
+ //# sourceMappingURL=translatable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translatable.js","names":[],"sources":["../../resources/js/i18n/translatable.ts"],"sourcesContent":["import type { Translatable } from \"@lattice-php/lattice/types/generated\";\n\nexport type Translate = (\n key: string,\n defaultValue?: string,\n options?: Record<string, unknown>,\n) => string;\n\nexport function isTranslatable(value: unknown): value is Translatable {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { key?: unknown }).key === \"string\"\n );\n}\n\nfunction readPath(payload: Record<string, unknown>, path: string): unknown {\n return path.split(\".\").reduce<unknown>((node, segment) => {\n if (typeof node === \"object\" && node !== null) {\n return (node as Record<string, unknown>)[segment];\n }\n\n return undefined;\n }, payload);\n}\n\nexport function resolveTranslatable(\n value: Translatable,\n payload: Record<string, unknown>,\n t: Translate,\n): string {\n const fromPayload: Record<string, unknown> = {};\n\n for (const [name, path] of Object.entries(value.payload)) {\n const read = readPath(payload, path);\n fromPayload[name] = read === undefined ? \"\" : read;\n }\n\n return t(value.key, value.key, { ...value.replacements, ...fromPayload });\n}\n"],"mappings":";AAQA,SAAgB,eAAe,OAAuC;CACpE,OACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA4B,QAAQ;AAEhD;AAEA,SAAS,SAAS,SAAkC,MAAuB;CACzE,OAAO,KAAK,MAAM,GAAG,EAAE,QAAiB,MAAM,YAAY;EACxD,IAAI,OAAO,SAAS,YAAY,SAAS,MACvC,OAAQ,KAAiC;CAI7C,GAAG,OAAO;AACZ;AAEA,SAAgB,oBACd,OACA,SACA,GACQ;CACR,MAAM,cAAuC,CAAC;CAE9C,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,MAAM,OAAO,GAAG;EACxD,MAAM,OAAO,SAAS,SAAS,IAAI;EACnC,YAAY,QAAQ,SAAS,KAAA,IAAY,KAAK;CAChD;CAEA,OAAO,EAAE,MAAM,KAAK,MAAM,KAAK;EAAE,GAAG,MAAM;EAAc,GAAG;CAAY,CAAC;AAC1E"}
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export { useEffectDispatcher } from './effects/use-effect-dispatcher';
6
6
  export { builtinEffectHandlers, effectHandler, mergeEffectHandlers } from './effects/registry';
7
7
  export { initializeTheme, useAppearance } from './appearance';
8
8
  export { copyToClipboard, useClipboard } from './clipboard';
9
+ export { createLatticeApp, type CreateLatticeAppOptions } from './create-app';
9
10
  export { EventBridge } from './events/event-bridge';
10
11
  export { Icon, IconRenderer, IconRendererProvider, SpriteProvider } from './icons';
11
12
  export { createLayoutResolver, createPageResolver, pageComponentName, withVisitHeaders, } from './inertia';
@@ -23,6 +24,8 @@ export { LATTICE_EVENT } from './events/event-names';
23
24
  export type { ButtonVariant } from './core/components/button';
24
25
  export type { ReloadComponentEvent } from './events/event-names';
25
26
  export type { ComponentProps, KnownPageContainer, LayoutPayload, Node, NodeOfType, NodeProps, NodeType, PageContainer, PageBreadcrumb, PagePayload, PropsOf, RendererComponent, RendererComponentModule, RendererComponentProps, Schema, UnknownComponent, WireNode, } from './core/types';
27
+ export { RealtimeListeners } from './realtime/listeners';
28
+ export type { ChannelVisibility, ListenerPayload } from './types/generated';
26
29
  export { columnCell } from './table/registry';
27
30
  export type { ColumnCellArgs, ColumnCellComponent, ColumnRegistry } from './table/registry';
28
31
  export type { ColumnProps, ColumnPropsOf } from './table/types';
package/dist/index.js CHANGED
@@ -1,29 +1,31 @@
1
- import { createPlugin, createRegistry, eagerComponent, extendRegistry, lazyComponent } from "./core/registry.js";
1
+ import { initializeTheme, useAppearance } from "./appearance/index.js";
2
2
  import { LATTICE_EVENT } from "./events/event-names.js";
3
3
  import { LATTICE_REF_HEADER, withRefHeader } from "./core/component-ref.js";
4
4
  import { withHeaders } from "./core/headers.js";
5
5
  import { Icon, SpriteProvider } from "./icons/sprite.js";
6
6
  import { IconRenderer, IconRendererProvider } from "./icons/icon-renderer.js";
7
- import { onToast, showToast } from "./toast/toast.js";
8
- import { EventBridge } from "./events/event-bridge.js";
9
- import { builtinEffectHandlers, effectHandler, mergeEffectHandlers } from "./effects/registry.js";
10
- import { dispatchActionError, dispatchEffects, getActionEffects, isActionEffect } from "./effects/dispatch.js";
11
- import { useColumnRegistry, useComponentRegistry } from "./core/registry-context.js";
12
- import { useEffectDispatcher } from "./effects/use-effect-dispatcher.js";
13
7
  import { CollapsedContext, useCollapsed } from "./core/collapsed-context.js";
8
+ import { useColumnRegistry, useComponentRegistry } from "./core/registry-context.js";
14
9
  import { Renderer } from "./core/renderer.js";
15
- import { chatPlugin } from "./chat/index.js";
16
- import { useChat } from "./chat/use-chat.js";
10
+ import { onToast, showToast } from "./toast/toast.js";
17
11
  import { onCallout } from "./toast/callout.js";
12
+ import { EventBridge } from "./events/event-bridge.js";
18
13
  import Callouts from "./layout/callouts.js";
14
+ import { createPlugin, createRegistry, eagerComponent, extendRegistry, lazyComponent } from "./core/registry.js";
19
15
  import { OutletContext } from "./layout/context.js";
20
16
  import { layoutComponents } from "./layout/components.js";
21
- import { registry } from "./registry.js";
22
- import { initializeTheme, useAppearance } from "./appearance/index.js";
23
- import { copyToClipboard, useClipboard } from "./clipboard/index.js";
24
17
  import SchemaLayout from "./layout/schema-layout.js";
18
+ import { RealtimeListeners } from "./realtime/listeners.js";
25
19
  import { createLayoutResolver, createPageResolver, pageComponentName, withVisitHeaders } from "./inertia.js";
20
+ import { builtinEffectHandlers, effectHandler, mergeEffectHandlers } from "./effects/registry.js";
21
+ import { dispatchActionError, dispatchEffects, getActionEffects, isActionEffect } from "./effects/dispatch.js";
22
+ import { useEffectDispatcher } from "./effects/use-effect-dispatcher.js";
23
+ import { useChat } from "./chat/use-chat.js";
24
+ import { chatPlugin } from "./chat/index.js";
25
+ import { registry } from "./registry.js";
26
26
  import { Toaster } from "./toast/toaster.js";
27
27
  import { Provider } from "./provider.js";
28
+ import { createLatticeApp } from "./create-app.js";
29
+ import { copyToClipboard, useClipboard } from "./clipboard/index.js";
28
30
  import { columnCell } from "./table/registry.js";
29
- export { Callouts, EventBridge, Icon, IconRenderer, IconRendererProvider, LATTICE_EVENT, LATTICE_REF_HEADER, OutletContext, Provider, Renderer, SchemaLayout, CollapsedContext as SidebarCollapsedContext, SpriteProvider, Toaster, builtinEffectHandlers, chatPlugin, columnCell, copyToClipboard, createLayoutResolver, createPageResolver, createPlugin, createRegistry, dispatchActionError, dispatchEffects, eagerComponent, effectHandler, extendRegistry, getActionEffects, initializeTheme, isActionEffect, layoutComponents, lazyComponent, mergeEffectHandlers, onCallout, onToast, pageComponentName, registry, showToast, useAppearance, useChat, useClipboard, useColumnRegistry, useComponentRegistry, useEffectDispatcher, useCollapsed as useSidebarCollapsed, withHeaders, withRefHeader, withVisitHeaders };
31
+ export { Callouts, EventBridge, Icon, IconRenderer, IconRendererProvider, LATTICE_EVENT, LATTICE_REF_HEADER, OutletContext, Provider, RealtimeListeners, Renderer, SchemaLayout, CollapsedContext as SidebarCollapsedContext, SpriteProvider, Toaster, builtinEffectHandlers, chatPlugin, columnCell, copyToClipboard, createLatticeApp, createLayoutResolver, createPageResolver, createPlugin, createRegistry, dispatchActionError, dispatchEffects, eagerComponent, effectHandler, extendRegistry, getActionEffects, initializeTheme, isActionEffect, layoutComponents, lazyComponent, mergeEffectHandlers, onCallout, onToast, pageComponentName, registry, showToast, useAppearance, useChat, useClipboard, useColumnRegistry, useComponentRegistry, useEffectDispatcher, useCollapsed as useSidebarCollapsed, withHeaders, withRefHeader, withVisitHeaders };
package/dist/lattice.css CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  :root,
4
4
  [data-theme="light"] {
5
- --lt-bg: var(--background, oklch(1 0 0));
5
+ --lt-bg: var(--background, oklch(0.97 0 0));
6
6
  --lt-fg: var(--foreground, oklch(0.145 0 0));
7
7
  --lt-surface: var(--card, oklch(1 0 0));
8
8
  --lt-surface-fg: var(--card-foreground, oklch(0.145 0 0));
@@ -27,15 +27,72 @@
27
27
  --lt-success-fg: var(--success-foreground, oklch(0.985 0 0));
28
28
  --lt-info: var(--info, oklch(0.62 0.14 240));
29
29
  --lt-info-fg: var(--info-foreground, oklch(0.985 0 0));
30
- --lt-radius: var(--radius, 0.625rem);
31
- --lt-radius-sm: calc(var(--lt-radius) - 2px);
32
- --lt-radius-xs: calc(var(--lt-radius) - 4px);
30
+ /* Interaction states (hover / active / disabled) */
31
+ --lt-primary-hover: var(--primary-hover, oklch(0.43 0.092 182));
32
+ --lt-primary-active: var(--primary-active, oklch(0.39 0.092 182));
33
+ --lt-danger-hover: var(--destructive-hover, oklch(0.53 0.21 27.3));
34
+ --lt-danger-active: var(--destructive-active, oklch(0.48 0.21 27.3));
35
+ --lt-success-hover: var(--success-hover, oklch(0.57 0.125 160));
36
+ --lt-success-active: var(--success-active, oklch(0.52 0.125 160));
37
+ --lt-info-hover: var(--info-hover, oklch(0.57 0.14 240));
38
+ --lt-info-active: var(--info-active, oklch(0.52 0.14 240));
39
+ --lt-secondary-hover: var(--secondary-hover, oklch(0.93 0 0));
40
+ --lt-secondary-active: var(--secondary-active, oklch(0.9 0 0));
41
+ --lt-disabled: var(--disabled, oklch(0.95 0 0));
42
+ --lt-disabled-fg: var(--disabled-foreground, oklch(0.7 0 0));
43
+
44
+ /* Radius: base = cards, -sm = controls, -xs = chips */
45
+ --lt-radius: var(--radius, 0.5rem);
46
+ --lt-radius-sm: calc(var(--lt-radius) - 0.125rem);
47
+ --lt-radius-xs: calc(var(--lt-radius) - 0.25rem);
33
48
 
34
49
  --lt-icon-xs: 0.75rem;
35
50
  --lt-icon-sm: 0.875rem;
36
51
  --lt-icon-md: 1rem;
37
52
  --lt-icon-lg: 1.25rem;
38
53
  --lt-icon-xl: 1.5rem;
54
+ --lt-icon-stroke: 1.5;
55
+
56
+ /* Control heights — density lever */
57
+ --lt-control-h-sm: 2rem;
58
+ --lt-control-h-md: 2.25rem;
59
+ --lt-control-h-lg: 2.5rem;
60
+
61
+ --lt-font-weight-normal: 400;
62
+ --lt-font-weight-medium: 500;
63
+ --lt-font-weight-semibold: 600;
64
+ --lt-font-weight-bold: 700;
65
+
66
+ /* Focus-ring size (colour: --lt-ring) */
67
+ --lt-ring-width: 0.125rem;
68
+ --lt-ring-offset: 0.125rem;
69
+
70
+ /* Elevation */
71
+ --lt-shadow-xs: 0 1px 1px 0 oklch(0 0 0 / 0.04);
72
+ --lt-shadow-sm: 0 1px 2px 0 oklch(0 0 0 / 0.05);
73
+ --lt-shadow-md:
74
+ 0 2px 4px -1px oklch(0 0 0 / 0.08), 0 1px 2px -1px oklch(0 0 0 / 0.06);
75
+ --lt-shadow-lg:
76
+ 0 8px 16px -4px oklch(0 0 0 / 0.12), 0 2px 4px -2px oklch(0 0 0 / 0.08);
77
+
78
+ /* Z-index scale */
79
+ --lt-z-dropdown: 1000;
80
+ --lt-z-sticky: 1100;
81
+ --lt-z-overlay: 1200;
82
+ --lt-z-modal: 1300;
83
+ --lt-z-popover: 1400;
84
+ --lt-z-toast: 1500;
85
+
86
+ /* Motion */
87
+ --lt-duration-fast: 120ms;
88
+ --lt-duration-base: 160ms;
89
+ --lt-duration-slow: 240ms;
90
+ --lt-ease: cubic-bezier(0.2, 0, 0, 1);
91
+
92
+ /* Layout dimensions */
93
+ --lt-sidebar-w: 16rem;
94
+ --lt-sidebar-w-collapsed: 4rem;
95
+ --lt-container-max: 80rem;
39
96
 
40
97
  color-scheme: light;
41
98
  }
@@ -68,6 +125,20 @@
68
125
  --lt-info: var(--info, oklch(0.7 0.14 240));
69
126
  --lt-info-fg: var(--info-foreground, oklch(0.205 0 0));
70
127
 
128
+ /* Interaction states (dark) */
129
+ --lt-primary-hover: var(--primary-hover, oklch(0.79 0.105 182));
130
+ --lt-primary-active: var(--primary-active, oklch(0.84 0.105 182));
131
+ --lt-danger-hover: var(--destructive-hover, oklch(0.47 0.13 26));
132
+ --lt-danger-active: var(--destructive-active, oklch(0.52 0.13 26));
133
+ --lt-success-hover: var(--success-hover, oklch(0.75 0.15 162));
134
+ --lt-success-active: var(--success-active, oklch(0.8 0.15 162));
135
+ --lt-info-hover: var(--info-hover, oklch(0.75 0.14 240));
136
+ --lt-info-active: var(--info-active, oklch(0.8 0.14 240));
137
+ --lt-secondary-hover: var(--secondary-hover, oklch(0.32 0 0));
138
+ --lt-secondary-active: var(--secondary-active, oklch(0.36 0 0));
139
+ --lt-disabled: var(--disabled, oklch(0.32 0 0));
140
+ --lt-disabled-fg: var(--disabled-foreground, oklch(0.55 0 0));
141
+
71
142
  color-scheme: dark;
72
143
  }
73
144
 
@@ -97,16 +168,35 @@
97
168
  --color-lt-success-fg: var(--lt-success-fg);
98
169
  --color-lt-info: var(--lt-info);
99
170
  --color-lt-info-fg: var(--lt-info-fg);
171
+ --color-lt-primary-hover: var(--lt-primary-hover);
172
+ --color-lt-primary-active: var(--lt-primary-active);
173
+ --color-lt-danger-hover: var(--lt-danger-hover);
174
+ --color-lt-danger-active: var(--lt-danger-active);
175
+ --color-lt-success-hover: var(--lt-success-hover);
176
+ --color-lt-success-active: var(--lt-success-active);
177
+ --color-lt-info-hover: var(--lt-info-hover);
178
+ --color-lt-info-active: var(--lt-info-active);
179
+ --color-lt-secondary-hover: var(--lt-secondary-hover);
180
+ --color-lt-secondary-active: var(--lt-secondary-active);
181
+ --color-lt-disabled: var(--lt-disabled);
182
+ --color-lt-disabled-fg: var(--lt-disabled-fg);
100
183
  --radius-lt: var(--lt-radius);
101
184
  --radius-lt-sm: var(--lt-radius-sm);
102
185
  --radius-lt-xs: var(--lt-radius-xs);
186
+ --shadow-lt-xs: var(--lt-shadow-xs);
187
+ --shadow-lt-sm: var(--lt-shadow-sm);
188
+ --shadow-lt-md: var(--lt-shadow-md);
189
+ --shadow-lt-lg: var(--lt-shadow-lg);
103
190
  --spacing-lt-icon-xs: var(--lt-icon-xs);
104
191
  --spacing-lt-icon-sm: var(--lt-icon-sm);
105
192
  --spacing-lt-icon-md: var(--lt-icon-md);
106
193
  --spacing-lt-icon-lg: var(--lt-icon-lg);
107
194
  --spacing-lt-icon-xl: var(--lt-icon-xl);
108
- --animate-lt-toast-in: lt-toast-in 160ms ease-out;
109
- --animate-lt-toast-out: lt-toast-out 120ms ease-in;
195
+ --spacing-lt-control-sm: var(--lt-control-h-sm);
196
+ --spacing-lt-control-md: var(--lt-control-h-md);
197
+ --spacing-lt-control-lg: var(--lt-control-h-lg);
198
+ --animate-lt-toast-in: lt-toast-in var(--lt-duration-base) ease-out;
199
+ --animate-lt-toast-out: lt-toast-out var(--lt-duration-fast) ease-in;
110
200
  }
111
201
 
112
202
  @theme {
@@ -115,6 +205,9 @@
115
205
  "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
116
206
  "Segoe UI Symbol", "Noto Color Emoji";
117
207
 
208
+ /* Base spacing unit */
209
+ --spacing: 0.25rem;
210
+
118
211
  --text-xs: 0.625rem;
119
212
  --text-xs--line-height: 1.6;
120
213
  --text-sm: 0.6875rem;
@@ -1,5 +1,6 @@
1
- import { Fragment } from "react";
1
+ import { nodeIdentity } from "../core/test-id.js";
2
2
  import { Link, usePage } from "@inertiajs/react";
3
+ import { Fragment } from "react";
3
4
  import { jsx, jsxs } from "react/jsx-runtime";
4
5
  //#region resources/js/layout/breadcrumbs.tsx
5
6
  var BreadcrumbsComponent = ({ node }) => {
@@ -7,7 +8,7 @@ var BreadcrumbsComponent = ({ node }) => {
7
8
  if (crumbs.length === 0) return null;
8
9
  return /* @__PURE__ */ jsx("nav", {
9
10
  "aria-label": "Breadcrumb",
10
- "data-lattice-component": node.id,
11
+ "data-lattice-component": nodeIdentity(node),
11
12
  children: /* @__PURE__ */ jsx("ol", {
12
13
  className: "flex items-center gap-2 text-sm text-lt-muted-fg",
13
14
  children: crumbs.map((crumb, index) => {