@lattice-php/lattice 0.30.0 → 0.31.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 (311) hide show
  1. package/dist/action/components/action-form.js +26 -25
  2. package/dist/action/components/action-form.js.map +1 -1
  3. package/dist/action/components/action-group.js.map +1 -1
  4. package/dist/action/components/action-trigger-provider.js.map +1 -1
  5. package/dist/action/components/action.js.map +1 -1
  6. package/dist/action/lib/run-action.js +2 -1
  7. package/dist/action/lib/run-action.js.map +1 -1
  8. package/dist/appearance/index.js.map +1 -1
  9. package/dist/chat/components/chat-box.js +4 -2
  10. package/dist/chat/components/chat-box.js.map +1 -1
  11. package/dist/chat/components/message-list.js.map +1 -1
  12. package/dist/chat/components/message.js.map +1 -1
  13. package/dist/chat/components/prompt-input.js.map +1 -1
  14. package/dist/chat/hooks/use-chat.js +2 -1
  15. package/dist/chat/hooks/use-chat.js.map +1 -1
  16. package/dist/chat/lib/transport.js.map +1 -1
  17. package/dist/chat/parts/text.js.map +1 -1
  18. package/dist/chat/parts/tool-call.js.map +1 -1
  19. package/dist/core/api.js.map +1 -1
  20. package/dist/core/event-names.d.ts +1 -0
  21. package/dist/core/event-names.js +1 -0
  22. package/dist/core/event-names.js.map +1 -1
  23. package/dist/core/hooks/column-sizing.js.map +1 -1
  24. package/dist/core/hooks/use-column-resizing.js.map +1 -1
  25. package/dist/core/materialize.js.map +1 -1
  26. package/dist/core/registry-context.js.map +1 -1
  27. package/dist/core/renderer.js +2 -1
  28. package/dist/core/renderer.js.map +1 -1
  29. package/dist/core/test-id.js.map +1 -1
  30. package/dist/core/upload.js.map +1 -1
  31. package/dist/create-app.js +1 -1
  32. package/dist/create-app.js.map +1 -1
  33. package/dist/effects/registry.js +2 -1
  34. package/dist/effects/registry.js.map +1 -1
  35. package/dist/form/components/base/field.js.map +1 -1
  36. package/dist/form/components/base/submit-button.js.map +1 -1
  37. package/dist/form/components/fields/add-row-menu.js.map +1 -1
  38. package/dist/form/components/fields/builder.js +2 -1
  39. package/dist/form/components/fields/builder.js.map +1 -1
  40. package/dist/form/components/fields/checkbox.js.map +1 -1
  41. package/dist/form/components/fields/choice.js.map +1 -1
  42. package/dist/form/components/fields/color-picker-field.js.map +1 -1
  43. package/dist/form/components/fields/date-input.js.map +1 -1
  44. package/dist/form/components/fields/date-picker-field.js.map +1 -1
  45. package/dist/form/components/fields/date-picker-value.js.map +1 -1
  46. package/dist/form/components/fields/date-picker.js.map +1 -1
  47. package/dist/form/components/fields/date-time-input.js.map +1 -1
  48. package/dist/form/components/fields/file-upload.js +3 -2
  49. package/dist/form/components/fields/file-upload.js.map +1 -1
  50. package/dist/form/components/fields/number-input.js.map +1 -1
  51. package/dist/form/components/fields/otp-input.js.map +1 -1
  52. package/dist/form/components/fields/password-input.js.map +1 -1
  53. package/dist/form/components/fields/repeater.js +2 -1
  54. package/dist/form/components/fields/repeater.js.map +1 -1
  55. package/dist/form/components/fields/rich-editor-field.js +6 -5
  56. package/dist/form/components/fields/rich-editor-field.js.map +1 -1
  57. package/dist/form/components/fields/rich-editor.js.map +1 -1
  58. package/dist/form/components/fields/row-action-menu.js.map +1 -1
  59. package/dist/form/components/fields/row-actions.js.map +1 -1
  60. package/dist/form/components/fields/row-item.js.map +1 -1
  61. package/dist/form/components/fields/row-templates.js.map +1 -1
  62. package/dist/form/components/fields/select.js.map +1 -1
  63. package/dist/form/components/fields/simple-field.js.map +1 -1
  64. package/dist/form/components/fields/table-rows.js.map +1 -1
  65. package/dist/form/components/fields/text-input.js.map +1 -1
  66. package/dist/form/components/fields/textarea.js.map +1 -1
  67. package/dist/form/components/fields/time-input.js.map +1 -1
  68. package/dist/form/components/fields/time-picker-columns.js.map +1 -1
  69. package/dist/form/components/fields/time-picker.js +0 -1
  70. package/dist/form/components/fields/time-picker.js.map +1 -1
  71. package/dist/form/components/fields/toggle.js.map +1 -1
  72. package/dist/form/components/fields/use-flip-reorder.js.map +1 -1
  73. package/dist/form/components/form.js +7 -5
  74. package/dist/form/components/form.js.map +1 -1
  75. package/dist/form/components/wizard.js +1 -1
  76. package/dist/form/components/wizard.js.map +1 -1
  77. package/dist/form/hooks/resolved-nodes.js.map +1 -1
  78. package/dist/form/hooks/use-dependent-field.js +2 -1
  79. package/dist/form/hooks/use-dependent-field.js.map +1 -1
  80. package/dist/form/hooks/use-form-resolver.js +2 -1
  81. package/dist/form/hooks/use-form-resolver.js.map +1 -1
  82. package/dist/form/hooks/values.js +2 -1
  83. package/dist/form/hooks/values.js.map +1 -1
  84. package/dist/form/lib/conditions.js +1 -1
  85. package/dist/form/lib/conditions.js.map +1 -1
  86. package/dist/form/lib/field-dom-name.js.map +1 -1
  87. package/dist/form/lib/field-props.js +1 -1
  88. package/dist/form/lib/field-props.js.map +1 -1
  89. package/dist/form/lib/form-path.js.map +1 -1
  90. package/dist/form/lib/form-transport.js.map +1 -1
  91. package/dist/form/lib/prefill-targets.js.map +1 -1
  92. package/dist/form/lib/wizard-steps.js.map +1 -1
  93. package/dist/form/rich-editor/builtins.js.map +1 -1
  94. package/dist/format/date-time.d.ts +1 -0
  95. package/dist/format/date-time.js +5 -1
  96. package/dist/format/date-time.js.map +1 -1
  97. package/dist/format/number.js.map +1 -1
  98. package/dist/i18n/config.js.map +1 -1
  99. package/dist/i18n/date-time-formatter.d.ts +13 -0
  100. package/dist/i18n/date-time-formatter.js +28 -0
  101. package/dist/i18n/date-time-formatter.js.map +1 -0
  102. package/dist/i18n/date-time.js +1 -1
  103. package/dist/i18n/date-time.js.map +1 -1
  104. package/dist/i18n/index.js +1 -1
  105. package/dist/i18n/instance.js +2 -0
  106. package/dist/i18n/instance.js.map +1 -1
  107. package/dist/i18n/locale.js +1 -1
  108. package/dist/i18n/locale.js.map +1 -1
  109. package/dist/i18n/timezone.js.map +1 -1
  110. package/dist/i18n/translatable.js.map +1 -1
  111. package/dist/icons/icon-renderer.js.map +1 -1
  112. package/dist/icons/sprite.js.map +1 -1
  113. package/dist/layout/components/breadcrumbs.js.map +1 -1
  114. package/dist/layout/components/callouts.d.ts +2 -1
  115. package/dist/layout/components/callouts.js +6 -2
  116. package/dist/layout/components/callouts.js.map +1 -1
  117. package/dist/layout/components/dropdown.js.map +1 -1
  118. package/dist/layout/components/menu-item.js.map +1 -1
  119. package/dist/layout/components/menu.js.map +1 -1
  120. package/dist/layout/components/popover.js.map +1 -1
  121. package/dist/layout/components/schema-layout.js.map +1 -1
  122. package/dist/layout/components/sidebar.js.map +1 -1
  123. package/dist/lib/color.js +1 -1
  124. package/dist/lib/color.js.map +1 -1
  125. package/dist/lib/use-media-query.js.map +1 -1
  126. package/dist/notifications/components/empty-state.js.map +1 -1
  127. package/dist/notifications/components/notification-item.js.map +1 -1
  128. package/dist/notifications/components/notification-list.js.map +1 -1
  129. package/dist/notifications/components/notifications.js.map +1 -1
  130. package/dist/notifications/store.js.map +1 -1
  131. package/dist/page.js.map +1 -1
  132. package/dist/provider-base.js.map +1 -1
  133. package/dist/realtime/listeners.js.map +1 -1
  134. package/dist/remote/components/data-list.js.map +1 -1
  135. package/dist/table/components/bulk-bar.js.map +1 -1
  136. package/dist/table/components/cells/badge-cell.js.map +1 -1
  137. package/dist/table/components/cells/boolean-cell.js.map +1 -1
  138. package/dist/table/components/cells/icon-cell.js.map +1 -1
  139. package/dist/table/components/cells/numeric-cell.js.map +1 -1
  140. package/dist/table/components/cells/stack-cell.js.map +1 -1
  141. package/dist/table/components/cells/text-cell.js.map +1 -1
  142. package/dist/table/components/column-filter-control.js.map +1 -1
  143. package/dist/table/components/column-header.js.map +1 -1
  144. package/dist/table/components/column-visibility-menu.js +2 -1
  145. package/dist/table/components/column-visibility-menu.js.map +1 -1
  146. package/dist/table/components/filter-bar.js +2 -1
  147. package/dist/table/components/filter-bar.js.map +1 -1
  148. package/dist/table/components/filter-controls.js +6 -5
  149. package/dist/table/components/filter-controls.js.map +1 -1
  150. package/dist/table/components/filter-value-input.js.map +1 -1
  151. package/dist/table/components/pagination.d.ts +5 -1
  152. package/dist/table/components/pagination.js +25 -6
  153. package/dist/table/components/pagination.js.map +1 -1
  154. package/dist/table/components/sort-bar.js.map +1 -1
  155. package/dist/table/components/table-cell.js +2 -1
  156. package/dist/table/components/table-cell.js.map +1 -1
  157. package/dist/table/components/table-search.js.map +1 -1
  158. package/dist/table/components/table.js +6 -2
  159. package/dist/table/components/table.js.map +1 -1
  160. package/dist/table/hooks/use-column-visibility.js.map +1 -1
  161. package/dist/table/hooks/use-table-selection.js +2 -1
  162. package/dist/table/hooks/use-table-selection.js.map +1 -1
  163. package/dist/table/hooks/use-table.d.ts +2 -0
  164. package/dist/table/hooks/use-table.js +12 -0
  165. package/dist/table/hooks/use-table.js.map +1 -1
  166. package/dist/table/lib/bulk.js.map +1 -1
  167. package/dist/table/lib/filter-values.js.map +1 -1
  168. package/dist/table/lib/format.js.map +1 -1
  169. package/dist/table/lib/payload.d.ts +2 -0
  170. package/dist/table/lib/payload.js +18 -3
  171. package/dist/table/lib/payload.js.map +1 -1
  172. package/dist/table/lib/query.js +2 -1
  173. package/dist/table/lib/query.js.map +1 -1
  174. package/dist/toast/callout.d.ts +1 -0
  175. package/dist/toast/callout.js +9 -1
  176. package/dist/toast/callout.js.map +1 -1
  177. package/dist/toast/index.d.ts +1 -1
  178. package/dist/toast/index.js +2 -2
  179. package/dist/toast/toaster.js.map +1 -1
  180. package/dist/types/generated.d.ts +9 -1
  181. package/dist/ui/affix-group.js.map +1 -1
  182. package/dist/ui/avatar.js.map +1 -1
  183. package/dist/ui/badge.js.map +1 -1
  184. package/dist/ui/button.js.map +1 -1
  185. package/dist/ui/card.js.map +1 -1
  186. package/dist/ui/chart-view.js.map +1 -1
  187. package/dist/ui/chart.js.map +1 -1
  188. package/dist/ui/checkbox.js.map +1 -1
  189. package/dist/ui/collapsible.js.map +1 -1
  190. package/dist/ui/color-picker.js.map +1 -1
  191. package/dist/ui/combobox.js.map +1 -1
  192. package/dist/ui/confirm-dialog.js.map +1 -1
  193. package/dist/ui/copyable-text.js.map +1 -1
  194. package/dist/ui/dialog.js.map +1 -1
  195. package/dist/ui/dropdown-menu.js.map +1 -1
  196. package/dist/ui/floating-panel.js.map +1 -1
  197. package/dist/ui/fragment.js +2 -1
  198. package/dist/ui/fragment.js.map +1 -1
  199. package/dist/ui/heading.js.map +1 -1
  200. package/dist/ui/icon-button.js.map +1 -1
  201. package/dist/ui/icon.js.map +1 -1
  202. package/dist/ui/image-preview.js.map +1 -1
  203. package/dist/ui/info-tooltip.js.map +1 -1
  204. package/dist/ui/input-error.js.map +1 -1
  205. package/dist/ui/input-otp.js.map +1 -1
  206. package/dist/ui/link.js.map +1 -1
  207. package/dist/ui/modal.js.map +1 -1
  208. package/dist/ui/password-input.js.map +1 -1
  209. package/dist/ui/progress.js.map +1 -1
  210. package/dist/ui/section.js +4 -2
  211. package/dist/ui/section.js.map +1 -1
  212. package/dist/ui/segmented-control.js.map +1 -1
  213. package/dist/ui/segmented-pills.js.map +1 -1
  214. package/dist/ui/tabs.js.map +1 -1
  215. package/dist/ui/text.js.map +1 -1
  216. package/dist/ui/tooltip.js.map +1 -1
  217. package/dist/vite-typescript-refresh.js.map +1 -1
  218. package/dist/vite.js.map +1 -1
  219. package/dist-standalone/chunks/chart-view-ZCTbih8A.js +59 -0
  220. package/dist-standalone/chunks/date-picker-field-CCnkf4SC.js +1 -0
  221. package/dist-standalone/chunks/dist-CK9gk4iB.js +1 -0
  222. package/dist-standalone/chunks/dist-DqlcM-vw.js +2 -0
  223. package/dist-standalone/chunks/icon-button-DcP25cn4.js +41 -0
  224. package/dist-standalone/chunks/instance-t58Awajh.js +1 -0
  225. package/dist-standalone/chunks/jsx-runtime-CN5ucv1d.js +1 -0
  226. package/dist-standalone/chunks/locale-CgtcxthU.js +1 -0
  227. package/dist-standalone/chunks/materialize-CWqnSEHR.js +1 -0
  228. package/dist-standalone/chunks/{notifications-echo-DsfNc4Ge.js → notifications-echo-uU87iJAV.js} +1 -1
  229. package/dist-standalone/chunks/number-BvVKQqOG.js +1 -0
  230. package/dist-standalone/chunks/page-props-B4GewgLI.js +1 -0
  231. package/dist-standalone/chunks/react-SIfiwpqq.js +1 -0
  232. package/dist-standalone/chunks/rich-editor-field-BtbU5XFS.js +158 -0
  233. package/dist-standalone/chunks/shared-props-B_wT1GcG.js +1 -0
  234. package/dist-standalone/chunks/subscriptions-BdHDnVA3.js +1 -0
  235. package/dist-standalone/chunks/time-picker-DSs9fDWe.js +1 -0
  236. package/dist-standalone/chunks/timezone-BukvzD0d.js +1 -0
  237. package/dist-standalone/chunks/translatable-DLw4h3x8.js +1 -0
  238. package/dist-standalone/chunks/use-effect-dispatcher-CiWYRvsF.js +97 -0
  239. package/dist-standalone/chunks/{with-selector-BwDoCrJJ.js → with-selector-zFUY-tQj.js} +1 -1
  240. package/dist-standalone/lattice.css +2 -2
  241. package/dist-standalone/lattice.js +4 -48
  242. package/dist-standalone/manifest.json +24 -15
  243. package/dist-standalone/sprite.svg +1 -1
  244. package/package.json +6 -6
  245. package/resources/icons/align-center.svg +1 -1
  246. package/resources/icons/align-justify.svg +1 -1
  247. package/resources/icons/align-left.svg +1 -1
  248. package/resources/icons/align-right.svg +1 -1
  249. package/resources/icons/arrow-down.svg +1 -1
  250. package/resources/icons/arrow-up.svg +1 -1
  251. package/resources/icons/bell.svg +1 -1
  252. package/resources/icons/bold.svg +1 -1
  253. package/resources/icons/calendar.svg +5 -5
  254. package/resources/icons/check.svg +1 -1
  255. package/resources/icons/chevron-down.svg +1 -1
  256. package/resources/icons/chevron-left.svg +1 -1
  257. package/resources/icons/chevron-right.svg +1 -1
  258. package/resources/icons/chevrons-up-down.svg +1 -1
  259. package/resources/icons/circle-alert.svg +1 -1
  260. package/resources/icons/circle-check.svg +1 -1
  261. package/resources/icons/circle-help.svg +1 -1
  262. package/resources/icons/circle-x.svg +1 -1
  263. package/resources/icons/clock.svg +1 -1
  264. package/resources/icons/code-xml.svg +1 -1
  265. package/resources/icons/code.svg +1 -1
  266. package/resources/icons/columns-3.svg +1 -1
  267. package/resources/icons/copy.svg +1 -1
  268. package/resources/icons/external-link.svg +1 -1
  269. package/resources/icons/eye-off.svg +1 -1
  270. package/resources/icons/eye.svg +1 -1
  271. package/resources/icons/filter.svg +1 -1
  272. package/resources/icons/heading-1.svg +1 -1
  273. package/resources/icons/heading-2.svg +1 -1
  274. package/resources/icons/heading-3.svg +1 -1
  275. package/resources/icons/heading.svg +1 -1
  276. package/resources/icons/highlighter.svg +1 -1
  277. package/resources/icons/info.svg +1 -1
  278. package/resources/icons/italic.svg +1 -1
  279. package/resources/icons/layout-dashboard.svg +1 -1
  280. package/resources/icons/link.svg +1 -1
  281. package/resources/icons/list-ordered.svg +1 -1
  282. package/resources/icons/list.svg +1 -1
  283. package/resources/icons/loader-2.svg +1 -1
  284. package/resources/icons/minus.svg +1 -1
  285. package/resources/icons/more-horizontal.svg +1 -1
  286. package/resources/icons/panel-left.svg +1 -1
  287. package/resources/icons/pencil-line.svg +1 -1
  288. package/resources/icons/pencil.svg +1 -1
  289. package/resources/icons/plus.svg +1 -1
  290. package/resources/icons/quote.svg +1 -1
  291. package/resources/icons/rotate-ccw.svg +1 -1
  292. package/resources/icons/rows-3.svg +1 -1
  293. package/resources/icons/search.svg +1 -1
  294. package/resources/icons/send.svg +1 -1
  295. package/resources/icons/settings.svg +1 -1
  296. package/resources/icons/smile.svg +1 -1
  297. package/resources/icons/strikethrough.svg +1 -1
  298. package/resources/icons/table.svg +1 -1
  299. package/resources/icons/trash-2.svg +1 -1
  300. package/resources/icons/underline.svg +1 -1
  301. package/resources/icons/x.svg +1 -1
  302. package/dist-standalone/chunks/chart-view-CpYsLvzR.js +0 -36
  303. package/dist-standalone/chunks/date-picker-field-lX8N40Ii.js +0 -1
  304. package/dist-standalone/chunks/dist-PaT1gYi_.js +0 -2
  305. package/dist-standalone/chunks/jsx-runtime-Drll_-DD.js +0 -1
  306. package/dist-standalone/chunks/locale-CXqr7LPR.js +0 -1
  307. package/dist-standalone/chunks/page-props-ClpVjpOc.js +0 -1
  308. package/dist-standalone/chunks/react-B8IZ02wI.js +0 -1
  309. package/dist-standalone/chunks/rich-editor-field-C71csVJv.js +0 -154
  310. package/dist-standalone/chunks/subscriptions-B1STcwPE.js +0 -1
  311. package/dist-standalone/chunks/use-effect-dispatcher-I8So0l_I.js +0 -97
@@ -1 +1 @@
1
- {"version":3,"file":"table-rows.js","names":[],"sources":["../../../../resources/js/form/components/fields/table-rows.tsx"],"sourcesContent":["import type { Node } from \"@lattice-php/lattice/core/types\";\nimport { nodeKey } from \"@lattice-php/lattice/core/nodes\";\nimport { RenderNode } from \"@lattice-php/lattice/core/renderer\";\nimport {\n DEFAULT_COLUMN_WIDTH,\n type SizableColumn,\n} from \"@lattice-php/lattice/core/hooks/column-sizing\";\nimport { useColumnResizing } from \"@lattice-php/lattice/core/hooks/use-column-resizing\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { memo, useMemo } from \"react\";\nimport { useMediaQuery } from \"@lattice-php/lattice/lib/use-media-query\";\nimport type { ColumnWidth, RowAction as WireRowAction } from \"@lattice-php/lattice/types/generated\";\nimport { FieldScopeProvider } from \"@lattice-php/lattice/form/hooks/field-scope\";\nimport { TableCellProvider } from \"@lattice-php/lattice/form/hooks/row-layout-context\";\nimport { buildRowActions } from \"./row-action-menu\";\nimport { RowActions } from \"./row-actions\";\nimport { RowButton, RowItem } from \"./row-item\";\nimport type { RepeaterRow } from \"./repeater-rows\";\n\nconst rowControlTrack = \"3rem\";\nconst rowActionTrack = \"3rem\";\nconst rowControlTracks = [rowControlTrack];\nconst rowActionTracks = [rowActionTrack];\nconst tableViewportQuery = \"(min-width: 768px)\";\n\nexport type TableColumn = { name: string; label: string; columnWidth: ColumnWidth };\n\nfunction useTableViewport(): boolean {\n return useMediaQuery(tableViewportQuery, true);\n}\n\ntype TableRowModel = {\n key: string;\n index: number;\n row: RepeaterRow;\n template: Node[];\n span: boolean;\n heading?: string;\n};\n\nexport function columnsFromSchema(nodes: Node[]): TableColumn[] {\n return nodes.map((node) => {\n const props = node.props as {\n name: unknown;\n label?: unknown;\n columnWidth?: ColumnWidth | null;\n };\n return {\n name: String(props.name),\n label: String(props.label ?? props.name),\n columnWidth: props.columnWidth ?? DEFAULT_COLUMN_WIDTH,\n };\n });\n}\n\ntype TableRowItemProps = {\n base: string;\n index: number;\n row: RepeaterRow;\n template: Node[];\n span: boolean;\n isFirst: boolean;\n isLast: boolean;\n columnCount: number;\n flipKey: string;\n reorderable: boolean;\n removable: boolean;\n rowActions: WireRowAction[] | null;\n onField: (index: number, field: string, value: unknown) => void;\n onMove: (index: number, delta: number) => void;\n onRemove: (index: number) => void;\n onDuplicate: (index: number) => void;\n registerRow?: (key: string, el: HTMLElement | null) => void;\n};\n\nconst TableRowItem = memo(function TableRowItem({\n base,\n index,\n row,\n template,\n span,\n isFirst,\n isLast,\n columnCount,\n flipKey,\n reorderable,\n removable,\n rowActions,\n onField,\n onMove,\n onRemove,\n onDuplicate,\n registerRow,\n}: TableRowItemProps) {\n const { t } = useT(\"lattice\");\n\n return (\n <div\n ref={(el) => registerRow?.(flipKey, el)}\n data-flip-key={flipKey}\n data-test={`table-row-${base}-${index}`}\n className=\"grid grid-cols-[var(--lattice-table-columns)] items-start gap-x-3\"\n >\n <div className=\"flex items-center gap-1 [&_svg]:size-lt-icon-sm\">\n {reorderable && !isFirst && (\n <RowButton\n label=\"Move up\"\n testId={`table-${base}-up-${index}`}\n onClick={() => onMove(index, -1)}\n >\n <Icon name=\"arrow-up\" />\n </RowButton>\n )}\n {reorderable && !isLast && (\n <RowButton\n label=\"Move down\"\n testId={`table-${base}-down-${index}`}\n onClick={() => onMove(index, 1)}\n >\n <Icon name=\"arrow-down\" />\n </RowButton>\n )}\n </div>\n\n <FieldScopeProvider\n base={base}\n index={index}\n row={row}\n onChange={(field, value) => onField(index, field, value)}\n >\n <TableCellProvider>\n {span ? (\n <div\n data-test={`table-row-${base}-${index}-span`}\n className=\"flex flex-col gap-2\"\n style={{ gridColumn: `span ${columnCount}` }}\n >\n {template.map((child, childIndex) => (\n <RenderNode key={nodeKey(child, childIndex)} node={child} />\n ))}\n </div>\n ) : (\n template.map((child, childIndex) => (\n <div key={nodeKey(child, childIndex)}>\n <RenderNode node={child} />\n </div>\n ))\n )}\n </TableCellProvider>\n </FieldScopeProvider>\n\n <div className=\"flex items-center\">\n <RowActions\n actions={buildRowActions(rowActions, { index, removable, onRemove, onDuplicate, t })}\n />\n </div>\n </div>\n );\n});\n\nexport function TableRows({\n base,\n columns,\n rows,\n reorderable,\n removable,\n rowActions,\n onField,\n onMove,\n onRemove,\n onDuplicate,\n registerRow,\n resizableColumns = false,\n resizeIndicator = false,\n}: {\n base: string;\n columns: TableColumn[];\n rows: TableRowModel[];\n reorderable: boolean;\n removable: (index: number) => boolean;\n rowActions: WireRowAction[] | null;\n onField: (index: number, field: string, value: unknown) => void;\n onMove: (index: number, delta: number) => void;\n onRemove: (index: number) => void;\n onDuplicate: (index: number) => void;\n registerRow?: (key: string, el: HTMLElement | null) => void;\n resizableColumns?: boolean;\n resizeIndicator?: boolean;\n}) {\n const { t } = useT(\"lattice\");\n const isTableViewport = useTableViewport();\n const sizingColumns = useMemo<SizableColumn[]>(\n () =>\n columns.map((column) => ({\n key: column.name,\n label: column.label,\n width: column.columnWidth,\n })),\n [columns],\n );\n const { getResizeHandleProps, gridTemplateColumns, hasOverrides, resizeRootRef, resetColumns } =\n useColumnResizing({\n columns: sizingColumns,\n enabled: resizableColumns,\n columnGapPx: 12,\n leadingTracks: rowControlTracks,\n showIndicator: resizeIndicator,\n storageKey: resizableColumns ? `lattice:table-columns:form:${base}` : undefined,\n trailingTracks: rowActionTracks,\n });\n\n if (!isTableViewport) {\n return (\n <div className=\"flex flex-col gap-3\">\n {rows.map((row) => (\n <div key={row.key} ref={(el) => registerRow?.(row.key, el)} data-flip-key={row.key}>\n <RowItem\n base={base}\n index={row.index}\n row={row.row}\n template={row.template}\n heading={row.heading ?? `#${row.index + 1}`}\n reorderable={reorderable}\n isFirst={row.index === 0}\n isLast={row.index === rows.length - 1}\n removable={removable(row.index)}\n rowActions={rowActions}\n onField={onField}\n onRemove={onRemove}\n onMove={onMove}\n onDuplicate={onDuplicate}\n />\n </div>\n ))}\n </div>\n );\n }\n\n return (\n <div className=\"relative\">\n {hasOverrides && (\n <button\n aria-label={t(\"table.reset-column-widths\", \"Reset column widths\")}\n className=\"absolute right-1 top-1 z-10 hidden rounded-lt-sm p-1 text-lt-muted-fg hover:text-lt-fg md:inline-flex\"\n data-test=\"table-reset-columns\"\n onClick={resetColumns}\n title={t(\"table.reset-column-widths\", \"Reset column widths\")}\n type=\"button\"\n >\n <Icon name=\"rotate-ccw\" className=\"size-lt-icon-sm\" />\n </button>\n )}\n <div className=\"overflow-x-auto\">\n <div\n ref={resizeRootRef}\n className=\"flex min-w-max flex-col gap-2\"\n style={{ \"--lattice-table-columns\": gridTemplateColumns } as never}\n >\n <div className=\"grid grid-cols-[var(--lattice-table-columns)] items-center gap-x-3\">\n <div />\n {columns.map((column, index) => (\n <div\n key={column.name}\n className=\"relative min-w-0 pr-3 text-xs font-medium text-lt-muted-fg\"\n >\n {column.label}\n {resizableColumns && <div {...getResizeHandleProps(sizingColumns[index])} />}\n </div>\n ))}\n <div />\n </div>\n\n {rows.map((row) => (\n <TableRowItem\n key={row.key}\n base={base}\n index={row.index}\n row={row.row}\n template={row.template}\n span={row.span}\n isFirst={row.index === 0}\n isLast={row.index === rows.length - 1}\n columnCount={columns.length}\n flipKey={row.key}\n reorderable={reorderable}\n removable={removable(row.index)}\n rowActions={rowActions}\n onField={onField}\n onMove={onMove}\n onRemove={onRemove}\n onDuplicate={onDuplicate}\n registerRow={registerRow}\n />\n ))}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"table-rows.js","names":[],"sources":["../../../../resources/js/form/components/fields/table-rows.tsx"],"sourcesContent":["import type { Node } from \"@lattice-php/lattice/core/types\";\nimport { nodeKey } from \"@lattice-php/lattice/core/nodes\";\nimport { RenderNode } from \"@lattice-php/lattice/core/renderer\";\nimport {\n DEFAULT_COLUMN_WIDTH,\n type SizableColumn,\n} from \"@lattice-php/lattice/core/hooks/column-sizing\";\nimport { useColumnResizing } from \"@lattice-php/lattice/core/hooks/use-column-resizing\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { memo, useMemo } from \"react\";\nimport { useMediaQuery } from \"@lattice-php/lattice/lib/use-media-query\";\nimport type { ColumnWidth, RowAction as WireRowAction } from \"@lattice-php/lattice/types/generated\";\nimport { FieldScopeProvider } from \"@lattice-php/lattice/form/hooks/field-scope\";\nimport { TableCellProvider } from \"@lattice-php/lattice/form/hooks/row-layout-context\";\nimport { buildRowActions } from \"./row-action-menu\";\nimport { RowActions } from \"./row-actions\";\nimport { RowButton, RowItem } from \"./row-item\";\nimport type { RepeaterRow } from \"./repeater-rows\";\n\nconst rowControlTrack = \"3rem\";\nconst rowActionTrack = \"3rem\";\nconst rowControlTracks = [rowControlTrack];\nconst rowActionTracks = [rowActionTrack];\nconst tableViewportQuery = \"(min-width: 768px)\";\n\nexport type TableColumn = { name: string; label: string; columnWidth: ColumnWidth };\n\nfunction useTableViewport(): boolean {\n return useMediaQuery(tableViewportQuery, true);\n}\n\ntype TableRowModel = {\n key: string;\n index: number;\n row: RepeaterRow;\n template: Node[];\n span: boolean;\n heading?: string;\n};\n\nexport function columnsFromSchema(nodes: Node[]): TableColumn[] {\n return nodes.map((node) => {\n const props = node.props as {\n name: unknown;\n label?: unknown;\n columnWidth?: ColumnWidth | null;\n };\n return {\n name: String(props.name),\n label: String(props.label ?? props.name),\n columnWidth: props.columnWidth ?? DEFAULT_COLUMN_WIDTH,\n };\n });\n}\n\ntype TableRowItemProps = {\n base: string;\n index: number;\n row: RepeaterRow;\n template: Node[];\n span: boolean;\n isFirst: boolean;\n isLast: boolean;\n columnCount: number;\n flipKey: string;\n reorderable: boolean;\n removable: boolean;\n rowActions: WireRowAction[] | null;\n onField: (index: number, field: string, value: unknown) => void;\n onMove: (index: number, delta: number) => void;\n onRemove: (index: number) => void;\n onDuplicate: (index: number) => void;\n registerRow?: (key: string, el: HTMLElement | null) => void;\n};\n\nconst TableRowItem = memo(function TableRowItem({\n base,\n index,\n row,\n template,\n span,\n isFirst,\n isLast,\n columnCount,\n flipKey,\n reorderable,\n removable,\n rowActions,\n onField,\n onMove,\n onRemove,\n onDuplicate,\n registerRow,\n}: TableRowItemProps) {\n const { t } = useT(\"lattice\");\n\n return (\n <div\n ref={(el) => registerRow?.(flipKey, el)}\n data-flip-key={flipKey}\n data-test={`table-row-${base}-${index}`}\n className=\"grid grid-cols-[var(--lattice-table-columns)] items-start gap-x-3\"\n >\n <div className=\"flex items-center gap-1 [&_svg]:size-lt-icon-sm\">\n {reorderable && !isFirst && (\n <RowButton\n label=\"Move up\"\n testId={`table-${base}-up-${index}`}\n onClick={() => onMove(index, -1)}\n >\n <Icon name=\"arrow-up\" />\n </RowButton>\n )}\n {reorderable && !isLast && (\n <RowButton\n label=\"Move down\"\n testId={`table-${base}-down-${index}`}\n onClick={() => onMove(index, 1)}\n >\n <Icon name=\"arrow-down\" />\n </RowButton>\n )}\n </div>\n\n <FieldScopeProvider\n base={base}\n index={index}\n row={row}\n onChange={(field, value) => onField(index, field, value)}\n >\n <TableCellProvider>\n {span ? (\n <div\n data-test={`table-row-${base}-${index}-span`}\n className=\"flex flex-col gap-2\"\n style={{ gridColumn: `span ${columnCount}` }}\n >\n {template.map((child, childIndex) => (\n <RenderNode key={nodeKey(child, childIndex)} node={child} />\n ))}\n </div>\n ) : (\n template.map((child, childIndex) => (\n <div key={nodeKey(child, childIndex)}>\n <RenderNode node={child} />\n </div>\n ))\n )}\n </TableCellProvider>\n </FieldScopeProvider>\n\n <div className=\"flex items-center\">\n <RowActions\n actions={buildRowActions(rowActions, { index, removable, onRemove, onDuplicate, t })}\n />\n </div>\n </div>\n );\n});\n\nexport function TableRows({\n base,\n columns,\n rows,\n reorderable,\n removable,\n rowActions,\n onField,\n onMove,\n onRemove,\n onDuplicate,\n registerRow,\n resizableColumns = false,\n resizeIndicator = false,\n}: {\n base: string;\n columns: TableColumn[];\n rows: TableRowModel[];\n reorderable: boolean;\n removable: (index: number) => boolean;\n rowActions: WireRowAction[] | null;\n onField: (index: number, field: string, value: unknown) => void;\n onMove: (index: number, delta: number) => void;\n onRemove: (index: number) => void;\n onDuplicate: (index: number) => void;\n registerRow?: (key: string, el: HTMLElement | null) => void;\n resizableColumns?: boolean;\n resizeIndicator?: boolean;\n}) {\n const { t } = useT(\"lattice\");\n const isTableViewport = useTableViewport();\n const sizingColumns = useMemo<SizableColumn[]>(\n () =>\n columns.map((column) => ({\n key: column.name,\n label: column.label,\n width: column.columnWidth,\n })),\n [columns],\n );\n const { getResizeHandleProps, gridTemplateColumns, hasOverrides, resizeRootRef, resetColumns } =\n useColumnResizing({\n columns: sizingColumns,\n enabled: resizableColumns,\n columnGapPx: 12,\n leadingTracks: rowControlTracks,\n showIndicator: resizeIndicator,\n storageKey: resizableColumns ? `lattice:table-columns:form:${base}` : undefined,\n trailingTracks: rowActionTracks,\n });\n\n if (!isTableViewport) {\n return (\n <div className=\"flex flex-col gap-3\">\n {rows.map((row) => (\n <div key={row.key} ref={(el) => registerRow?.(row.key, el)} data-flip-key={row.key}>\n <RowItem\n base={base}\n index={row.index}\n row={row.row}\n template={row.template}\n heading={row.heading ?? `#${row.index + 1}`}\n reorderable={reorderable}\n isFirst={row.index === 0}\n isLast={row.index === rows.length - 1}\n removable={removable(row.index)}\n rowActions={rowActions}\n onField={onField}\n onRemove={onRemove}\n onMove={onMove}\n onDuplicate={onDuplicate}\n />\n </div>\n ))}\n </div>\n );\n }\n\n return (\n <div className=\"relative\">\n {hasOverrides && (\n <button\n aria-label={t(\"table.reset-column-widths\", \"Reset column widths\")}\n className=\"absolute right-1 top-1 z-10 hidden rounded-lt-sm p-1 text-lt-muted-fg hover:text-lt-fg md:inline-flex\"\n data-test=\"table-reset-columns\"\n onClick={resetColumns}\n title={t(\"table.reset-column-widths\", \"Reset column widths\")}\n type=\"button\"\n >\n <Icon name=\"rotate-ccw\" className=\"size-lt-icon-sm\" />\n </button>\n )}\n <div className=\"overflow-x-auto\">\n <div\n ref={resizeRootRef}\n className=\"flex min-w-max flex-col gap-2\"\n style={{ \"--lattice-table-columns\": gridTemplateColumns } as never}\n >\n <div className=\"grid grid-cols-[var(--lattice-table-columns)] items-center gap-x-3\">\n <div />\n {columns.map((column, index) => (\n <div\n key={column.name}\n className=\"relative min-w-0 pr-3 text-xs font-medium text-lt-muted-fg\"\n >\n {column.label}\n {resizableColumns && <div {...getResizeHandleProps(sizingColumns[index])} />}\n </div>\n ))}\n <div />\n </div>\n\n {rows.map((row) => (\n <TableRowItem\n key={row.key}\n base={base}\n index={row.index}\n row={row.row}\n template={row.template}\n span={row.span}\n isFirst={row.index === 0}\n isLast={row.index === rows.length - 1}\n columnCount={columns.length}\n flipKey={row.key}\n reorderable={reorderable}\n removable={removable(row.index)}\n rowActions={rowActions}\n onField={onField}\n onMove={onMove}\n onRemove={onRemove}\n onDuplicate={onDuplicate}\n registerRow={registerRow}\n />\n ))}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -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 { AffixGroup } from \"@lattice-php/lattice/ui/affix-group\";\nimport { CopyButton } from \"@lattice-php/lattice/ui/copyable-text\";\nimport { Input } from \"@lattice-php/lattice/ui/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\n prefix={props.prefix}\n suffix={props.suffix}\n end={\n props.copyable ? (\n <CopyButton\n className=\"h-lt-control-md gap-1.5 rounded-l-none rounded-r-lt-sm border-l-0 border-lt-input px-3 group-has-[:focus-visible]:border-lt-ring\"\n label={props.label ?? name}\n testId={`${testId}-copy`}\n value={value}\n />\n ) : null\n }\n >\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":";;;;;;AAMA,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;GACE,QAAQ,MAAM;GACd,QAAQ,MAAM;GACd,KACE,MAAM,WACJ,oBAAC,YAAD;IACE,WAAU;IACV,OAAO,MAAM,SAAS;IACtB,QAAQ,GAAG,OAAO;IACX;GACR,CAAA,IACC;cAGJ,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"}
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 \"@lattice-php/lattice/ui/affix-group\";\nimport { CopyButton } from \"@lattice-php/lattice/ui/copyable-text\";\nimport { Input } from \"@lattice-php/lattice/ui/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\n prefix={props.prefix}\n suffix={props.suffix}\n end={\n props.copyable ? (\n <CopyButton\n className=\"h-lt-control-md gap-1.5 rounded-l-none rounded-r-lt-sm border-l-0 border-lt-input px-3 group-has-[:focus-visible]:border-lt-ring\"\n label={props.label ?? name}\n testId={`${testId}-copy`}\n value={value}\n />\n ) : null\n }\n >\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":";;;;;;AAMA,IAAa,sBAA6D,EAAE,WAAW;CACrF,MAAM,QAAQ,KAAK;CAEnB,OACE,oBAAC,aAAD;EAAmB;EAAM,OAAO,MAAM,SAAS;EAC3C,WAAA,EAAE,MAAM,QAAQ,OAAO,UAAU,UAAU,aAC3C,oBAAC,YAAD;GACE,QAAQ,MAAM;GACd,QAAQ,MAAM;GACd,KACE,MAAM,WACJ,oBAAC,YAAD;IACE,WAAU;IACV,OAAO,MAAM,SAAS;IACtB,QAAQ,GAAG,OAAO;IACX;GACR,CAAA,IACC;GAGJ,WAAA,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"}
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.js","names":[],"sources":["../../../../resources/js/form/components/fields/textarea.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { Textarea } from \"@lattice-php/lattice/ui/textarea\";\nimport { SimpleField } from \"./simple-field\";\n\nexport const TextareaComponent: RendererComponent<\"field.textarea\"> = ({ node }) => {\n const props = node.props;\n\n return (\n <SimpleField node={node} label={props.label ?? \"\"}>\n {({ name, testId, value, readOnly, disabled, commit }) => (\n <Textarea\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 rows={props.rows ?? undefined}\n tabIndex={props.tabIndex ?? undefined}\n value={value}\n />\n )}\n </SimpleField>\n );\n};\n"],"mappings":";;;;AAIA,IAAa,qBAA0D,EAAE,WAAW;CAClF,MAAM,QAAQ,KAAK;CAEnB,OACE,oBAAC,aAAD;EAAmB;EAAM,OAAO,MAAM,SAAS;aAC3C,EAAE,MAAM,QAAQ,OAAO,UAAU,UAAU,aAC3C,oBAAC,UAAD;GACE,WAAW,MAAM,aAAa;GAC9B,aAAW;GACD;GACV,IAAI;GACE;GACN,WAAW,UAAU,OAAO,MAAM,OAAO,KAAK;GAC9C,aAAa,MAAM,eAAe;GACxB;GACV,MAAM,MAAM,QAAQ,KAAA;GACpB,UAAU,MAAM,YAAY,KAAA;GACrB;EACR,CAAA;CAEQ,CAAA;AAEjB"}
1
+ {"version":3,"file":"textarea.js","names":[],"sources":["../../../../resources/js/form/components/fields/textarea.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { Textarea } from \"@lattice-php/lattice/ui/textarea\";\nimport { SimpleField } from \"./simple-field\";\n\nexport const TextareaComponent: RendererComponent<\"field.textarea\"> = ({ node }) => {\n const props = node.props;\n\n return (\n <SimpleField node={node} label={props.label ?? \"\"}>\n {({ name, testId, value, readOnly, disabled, commit }) => (\n <Textarea\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 rows={props.rows ?? undefined}\n tabIndex={props.tabIndex ?? undefined}\n value={value}\n />\n )}\n </SimpleField>\n );\n};\n"],"mappings":";;;;AAIA,IAAa,qBAA0D,EAAE,WAAW;CAClF,MAAM,QAAQ,KAAK;CAEnB,OACE,oBAAC,aAAD;EAAmB;EAAM,OAAO,MAAM,SAAS;EAC3C,WAAA,EAAE,MAAM,QAAQ,OAAO,UAAU,UAAU,aAC3C,oBAAC,UAAD;GACE,WAAW,MAAM,aAAa;GAC9B,aAAW;GACD;GACV,IAAI;GACE;GACN,WAAW,UAAU,OAAO,MAAM,OAAO,KAAK;GAC9C,aAAa,MAAM,eAAe;GACxB;GACV,MAAM,MAAM,QAAQ,KAAA;GACpB,UAAU,MAAM,YAAY,KAAA;GACrB;EACR,CAAA;CAEQ,CAAA;AAEjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"time-input.js","names":[],"sources":["../../../../resources/js/form/components/fields/time-input.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { Button } from \"@lattice-php/lattice/ui/button\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@lattice-php/lattice/ui/popover\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { Input } from \"@lattice-php/lattice/ui/input\";\nimport { SimpleField } from \"./simple-field\";\nimport { TimePicker } from \"./time-picker\";\nimport { formatTimeValue, parseTimeString, secondsEnabled } from \"./time-picker-columns\";\n\nexport const TimeInputComponent: RendererComponent<\"field.time-input\"> = ({ node }) => {\n const props = node.props;\n const withSeconds = secondsEnabled(props.step);\n const triggerLabel = props.label ?? props.name;\n\n return (\n <SimpleField node={node} label={props.label ?? \"\"}>\n {({ name, testId, value, readOnly, disabled, commit, blur }) => (\n <div className=\"flex gap-2\">\n <Input\n aria-label={triggerLabel}\n autoFocus={props.autoFocus ?? false}\n data-test={testId}\n disabled={disabled}\n id={name}\n name={name}\n onBlur={() => {\n const parsed = parseTimeString(value);\n\n if (parsed) {\n commit(formatTimeValue(parsed, withSeconds));\n }\n\n blur();\n }}\n onChange={(event) => commit(event.target.value)}\n readOnly={readOnly}\n tabIndex={props.tabIndex ?? undefined}\n type=\"text\"\n value={value}\n />\n <Popover>\n <PopoverTrigger asChild>\n <Button\n aria-label={`Open ${triggerLabel} time picker`}\n disabled={disabled || readOnly}\n size=\"icon\"\n type=\"button\"\n variant=\"secondary\"\n >\n <Icon name=\"clock\" className=\"size-lt-icon-md\" aria-hidden=\"true\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"p-2\">\n <TimePicker\n value={parseTimeString(value)}\n onChange={(next) => commit(formatTimeValue(next, withSeconds))}\n step={props.step}\n min={props.min}\n max={props.max}\n disabled={disabled}\n readOnly={readOnly}\n testId={`${testId}-picker`}\n />\n </PopoverContent>\n </Popover>\n </div>\n )}\n </SimpleField>\n );\n};\n"],"mappings":";;;;;;;;;AASA,IAAa,sBAA6D,EAAE,WAAW;CACrF,MAAM,QAAQ,KAAK;CACnB,MAAM,cAAc,eAAe,MAAM,IAAI;CAC7C,MAAM,eAAe,MAAM,SAAS,MAAM;CAE1C,OACE,oBAAC,aAAD;EAAmB;EAAM,OAAO,MAAM,SAAS;aAC3C,EAAE,MAAM,QAAQ,OAAO,UAAU,UAAU,QAAQ,WACnD,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,OAAD;IACE,cAAY;IACZ,WAAW,MAAM,aAAa;IAC9B,aAAW;IACD;IACV,IAAI;IACE;IACN,cAAc;KACZ,MAAM,SAAS,gBAAgB,KAAK;KAEpC,IAAI,QACF,OAAO,gBAAgB,QAAQ,WAAW,CAAC;KAG7C,KAAK;IACP;IACA,WAAW,UAAU,OAAO,MAAM,OAAO,KAAK;IACpC;IACV,UAAU,MAAM,YAAY,KAAA;IAC5B,MAAK;IACE;GACR,CAAA,GACD,qBAAC,SAAD,EAAA,UAAA,CACE,oBAAC,gBAAD;IAAgB,SAAA;cACd,oBAAC,QAAD;KACE,cAAY,QAAQ,aAAa;KACjC,UAAU,YAAY;KACtB,MAAK;KACL,MAAK;KACL,SAAQ;eAER,oBAAC,MAAD;MAAM,MAAK;MAAQ,WAAU;MAAkB,eAAY;KAAQ,CAAA;IAC7D,CAAA;GACM,CAAA,GAChB,oBAAC,gBAAD;IAAgB,WAAU;cACxB,oBAAC,YAAD;KACE,OAAO,gBAAgB,KAAK;KAC5B,WAAW,SAAS,OAAO,gBAAgB,MAAM,WAAW,CAAC;KAC7D,MAAM,MAAM;KACZ,KAAK,MAAM;KACX,KAAK,MAAM;KACD;KACA;KACV,QAAQ,GAAG,OAAO;IACnB,CAAA;GACa,CAAA,CACT,EAAA,CAAA,CACN;;CAEI,CAAA;AAEjB"}
1
+ {"version":3,"file":"time-input.js","names":[],"sources":["../../../../resources/js/form/components/fields/time-input.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { Button } from \"@lattice-php/lattice/ui/button\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@lattice-php/lattice/ui/popover\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { Input } from \"@lattice-php/lattice/ui/input\";\nimport { SimpleField } from \"./simple-field\";\nimport { TimePicker } from \"./time-picker\";\nimport { formatTimeValue, parseTimeString, secondsEnabled } from \"./time-picker-columns\";\n\nexport const TimeInputComponent: RendererComponent<\"field.time-input\"> = ({ node }) => {\n const props = node.props;\n const withSeconds = secondsEnabled(props.step);\n const triggerLabel = props.label ?? props.name;\n\n return (\n <SimpleField node={node} label={props.label ?? \"\"}>\n {({ name, testId, value, readOnly, disabled, commit, blur }) => (\n <div className=\"flex gap-2\">\n <Input\n aria-label={triggerLabel}\n autoFocus={props.autoFocus ?? false}\n data-test={testId}\n disabled={disabled}\n id={name}\n name={name}\n onBlur={() => {\n const parsed = parseTimeString(value);\n\n if (parsed) {\n commit(formatTimeValue(parsed, withSeconds));\n }\n\n blur();\n }}\n onChange={(event) => commit(event.target.value)}\n readOnly={readOnly}\n tabIndex={props.tabIndex ?? undefined}\n type=\"text\"\n value={value}\n />\n <Popover>\n <PopoverTrigger asChild>\n <Button\n aria-label={`Open ${triggerLabel} time picker`}\n disabled={disabled || readOnly}\n size=\"icon\"\n type=\"button\"\n variant=\"secondary\"\n >\n <Icon name=\"clock\" className=\"size-lt-icon-md\" aria-hidden=\"true\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"p-2\">\n <TimePicker\n value={parseTimeString(value)}\n onChange={(next) => commit(formatTimeValue(next, withSeconds))}\n step={props.step}\n min={props.min}\n max={props.max}\n disabled={disabled}\n readOnly={readOnly}\n testId={`${testId}-picker`}\n />\n </PopoverContent>\n </Popover>\n </div>\n )}\n </SimpleField>\n );\n};\n"],"mappings":";;;;;;;;;AASA,IAAa,sBAA6D,EAAE,WAAW;CACrF,MAAM,QAAQ,KAAK;CACnB,MAAM,cAAc,eAAe,MAAM,IAAI;CAC7C,MAAM,eAAe,MAAM,SAAS,MAAM;CAE1C,OACE,oBAAC,aAAD;EAAmB;EAAM,OAAO,MAAM,SAAS;EAC3C,WAAA,EAAE,MAAM,QAAQ,OAAO,UAAU,UAAU,QAAQ,WACnD,qBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,oBAAC,OAAD;IACE,cAAY;IACZ,WAAW,MAAM,aAAa;IAC9B,aAAW;IACD;IACV,IAAI;IACE;IACN,cAAc;KACZ,MAAM,SAAS,gBAAgB,KAAK;KAEpC,IAAI,QACF,OAAO,gBAAgB,QAAQ,WAAW,CAAC;KAG7C,KAAK;IACP;IACA,WAAW,UAAU,OAAO,MAAM,OAAO,KAAK;IACpC;IACV,UAAU,MAAM,YAAY,KAAA;IAC5B,MAAK;IACE;GACR,CAAA,GACD,qBAAC,SAAD,EAAA,UAAA,CACE,oBAAC,gBAAD;IAAgB,SAAA;IACd,UAAA,oBAAC,QAAD;KACE,cAAY,QAAQ,aAAa;KACjC,UAAU,YAAY;KACtB,MAAK;KACL,MAAK;KACL,SAAQ;KAER,UAAA,oBAAC,MAAD;MAAM,MAAK;MAAQ,WAAU;MAAkB,eAAY;KAAQ,CAAA;IAC7D,CAAA;GACM,CAAA,GAChB,oBAAC,gBAAD;IAAgB,WAAU;IACxB,UAAA,oBAAC,YAAD;KACE,OAAO,gBAAgB,KAAK;KAC5B,WAAW,SAAS,OAAO,gBAAgB,MAAM,WAAW,CAAC;KAC7D,MAAM,MAAM;KACZ,KAAK,MAAM;KACX,KAAK,MAAM;KACD;KACA;KACV,QAAQ,GAAG,OAAO;IACnB,CAAA;GACa,CAAA,CACT,EAAA,CAAA,CACN;;CAEI,CAAA;AAEjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"time-picker-columns.js","names":[],"sources":["../../../../resources/js/form/components/fields/time-picker-columns.ts"],"sourcesContent":["export type TimeValue = { hour: number; minute: number; second: number };\n\nexport type TimeColumnOption = { value: number; label: string; disabled: boolean };\n\ntype TimeColumns = {\n hours: TimeColumnOption[];\n minutes: TimeColumnOption[];\n seconds: TimeColumnOption[] | null;\n};\n\ntype Bound = { hour: number; minute: number };\n\nconst timePattern = /^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?$/;\n\nexport function parseTimeString(value: string | null | undefined): TimeValue | null {\n if (typeof value !== \"string\") {\n return null;\n }\n\n const match = timePattern.exec(value.trim());\n\n if (!match) {\n return null;\n }\n\n const hour = Number(match[1]);\n const minute = Number(match[2]);\n const second = match[3] ? Number(match[3]) : 0;\n\n if (hour > 23 || minute > 59 || second > 59) {\n return null;\n }\n\n return { hour, minute, second };\n}\n\nexport function formatTimeValue(value: TimeValue, withSeconds: boolean): string {\n const parts = [pad(value.hour), pad(value.minute)];\n\n if (withSeconds) {\n parts.push(pad(value.second));\n }\n\n return parts.join(\":\");\n}\n\nexport function secondsEnabled(step: number | null | undefined): boolean {\n return step != null && step < 60;\n}\n\nexport function buildTimeColumns(\n step: number | null | undefined,\n options: { min?: string | null; max?: string | null; current?: TimeValue | null } = {},\n): TimeColumns {\n const { min, max, current } = options;\n const minBound = toBound(min);\n const maxBound = toBound(max);\n const minuteStep = step == null || step < 60 ? 1 : step % 60 === 0 ? step / 60 : 1;\n\n const hours = range(0, 23, 1).map((value) => ({\n value,\n label: pad(value),\n disabled:\n (minBound != null && value < minBound.hour) || (maxBound != null && value > maxBound.hour),\n }));\n\n const minuteValues = withValue(range(0, 59, minuteStep), current?.minute);\n const minutes = minuteValues.map((value) => ({\n value,\n label: pad(value),\n disabled: minuteDisabled(value, current, minBound, maxBound),\n }));\n\n const seconds = secondsEnabled(step)\n ? range(0, 59, 1).map((value) => ({ value, label: pad(value), disabled: false }))\n : null;\n\n return { hours, minutes, seconds };\n}\n\nfunction minuteDisabled(\n minute: number,\n current: TimeValue | null | undefined,\n minBound: Bound | null,\n maxBound: Bound | null,\n): boolean {\n if (!current) {\n return false;\n }\n\n if (minBound != null && current.hour === minBound.hour && minute < minBound.minute) {\n return true;\n }\n\n if (maxBound != null && current.hour === maxBound.hour && minute > maxBound.minute) {\n return true;\n }\n\n return false;\n}\n\nfunction toBound(value: string | null | undefined): Bound | null {\n const parsed = parseTimeString(value);\n\n return parsed ? { hour: parsed.hour, minute: parsed.minute } : null;\n}\n\nfunction withValue(values: number[], extra: number | undefined): number[] {\n if (extra == null || values.includes(extra)) {\n return values;\n }\n\n return [...values, extra].sort((left, right) => left - right);\n}\n\nfunction range(start: number, end: number, step: number): number[] {\n const values: number[] = [];\n\n for (let value = start; value <= end; value += step) {\n values.push(value);\n }\n\n return values;\n}\n\nfunction pad(value: number): string {\n return String(value).padStart(2, \"0\");\n}\n"],"mappings":";AAYA,IAAM,cAAc;AAEpB,SAAgB,gBAAgB,OAAoD;CAClF,IAAI,OAAO,UAAU,UACnB,OAAO;CAGT,MAAM,QAAQ,YAAY,KAAK,MAAM,KAAK,CAAC;CAE3C,IAAI,CAAC,OACH,OAAO;CAGT,MAAM,OAAO,OAAO,MAAM,EAAE;CAC5B,MAAM,SAAS,OAAO,MAAM,EAAE;CAC9B,MAAM,SAAS,MAAM,KAAK,OAAO,MAAM,EAAE,IAAI;CAE7C,IAAI,OAAO,MAAM,SAAS,MAAM,SAAS,IACvC,OAAO;CAGT,OAAO;EAAE;EAAM;EAAQ;CAAO;AAChC;AAEA,SAAgB,gBAAgB,OAAkB,aAA8B;CAC9E,MAAM,QAAQ,CAAC,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,MAAM,CAAC;CAEjD,IAAI,aACF,MAAM,KAAK,IAAI,MAAM,MAAM,CAAC;CAG9B,OAAO,MAAM,KAAK,GAAG;AACvB;AAEA,SAAgB,eAAe,MAA0C;CACvE,OAAO,QAAQ,QAAQ,OAAO;AAChC;AAEA,SAAgB,iBACd,MACA,UAAoF,CAAC,GACxE;CACb,MAAM,EAAE,KAAK,KAAK,YAAY;CAC9B,MAAM,WAAW,QAAQ,GAAG;CAC5B,MAAM,WAAW,QAAQ,GAAG;CAC5B,MAAM,aAAa,QAAQ,QAAQ,OAAO,KAAK,IAAI,OAAO,OAAO,IAAI,OAAO,KAAK;CAoBjF,OAAO;EAAE,OAlBK,MAAM,GAAG,IAAI,CAAC,EAAE,KAAK,WAAW;GAC5C;GACA,OAAO,IAAI,KAAK;GAChB,UACG,YAAY,QAAQ,QAAQ,SAAS,QAAU,YAAY,QAAQ,QAAQ,SAAS;EACzF,EAaS;EAAO,SAXK,UAAU,MAAM,GAAG,IAAI,UAAU,GAAG,SAAS,MAClD,EAAa,KAAK,WAAW;GAC3C;GACA,OAAO,IAAI,KAAK;GAChB,UAAU,eAAe,OAAO,SAAS,UAAU,QAAQ;EAC7D,EAMgB;EAAS,SAJT,eAAe,IAAI,IAC/B,MAAM,GAAG,IAAI,CAAC,EAAE,KAAK,WAAW;GAAE;GAAO,OAAO,IAAI,KAAK;GAAG,UAAU;EAAM,EAAE,IAC9E;CAE6B;AACnC;AAEA,SAAS,eACP,QACA,SACA,UACA,UACS;CACT,IAAI,CAAC,SACH,OAAO;CAGT,IAAI,YAAY,QAAQ,QAAQ,SAAS,SAAS,QAAQ,SAAS,SAAS,QAC1E,OAAO;CAGT,IAAI,YAAY,QAAQ,QAAQ,SAAS,SAAS,QAAQ,SAAS,SAAS,QAC1E,OAAO;CAGT,OAAO;AACT;AAEA,SAAS,QAAQ,OAAgD;CAC/D,MAAM,SAAS,gBAAgB,KAAK;CAEpC,OAAO,SAAS;EAAE,MAAM,OAAO;EAAM,QAAQ,OAAO;CAAO,IAAI;AACjE;AAEA,SAAS,UAAU,QAAkB,OAAqC;CACxE,IAAI,SAAS,QAAQ,OAAO,SAAS,KAAK,GACxC,OAAO;CAGT,OAAO,CAAC,GAAG,QAAQ,KAAK,EAAE,MAAM,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEA,SAAS,MAAM,OAAe,KAAa,MAAwB;CACjE,MAAM,SAAmB,CAAC;CAE1B,KAAK,IAAI,QAAQ,OAAO,SAAS,KAAK,SAAS,MAC7C,OAAO,KAAK,KAAK;CAGnB,OAAO;AACT;AAEA,SAAS,IAAI,OAAuB;CAClC,OAAO,OAAO,KAAK,EAAE,SAAS,GAAG,GAAG;AACtC"}
1
+ {"version":3,"file":"time-picker-columns.js","names":[],"sources":["../../../../resources/js/form/components/fields/time-picker-columns.ts"],"sourcesContent":["export type TimeValue = { hour: number; minute: number; second: number };\n\nexport type TimeColumnOption = { value: number; label: string; disabled: boolean };\n\ntype TimeColumns = {\n hours: TimeColumnOption[];\n minutes: TimeColumnOption[];\n seconds: TimeColumnOption[] | null;\n};\n\ntype Bound = { hour: number; minute: number };\n\nconst timePattern = /^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?$/;\n\nexport function parseTimeString(value: string | null | undefined): TimeValue | null {\n if (typeof value !== \"string\") {\n return null;\n }\n\n const match = timePattern.exec(value.trim());\n\n if (!match) {\n return null;\n }\n\n const hour = Number(match[1]);\n const minute = Number(match[2]);\n const second = match[3] ? Number(match[3]) : 0;\n\n if (hour > 23 || minute > 59 || second > 59) {\n return null;\n }\n\n return { hour, minute, second };\n}\n\nexport function formatTimeValue(value: TimeValue, withSeconds: boolean): string {\n const parts = [pad(value.hour), pad(value.minute)];\n\n if (withSeconds) {\n parts.push(pad(value.second));\n }\n\n return parts.join(\":\");\n}\n\nexport function secondsEnabled(step: number | null | undefined): boolean {\n return step != null && step < 60;\n}\n\nexport function buildTimeColumns(\n step: number | null | undefined,\n options: { min?: string | null; max?: string | null; current?: TimeValue | null } = {},\n): TimeColumns {\n const { min, max, current } = options;\n const minBound = toBound(min);\n const maxBound = toBound(max);\n const minuteStep = step == null || step < 60 ? 1 : step % 60 === 0 ? step / 60 : 1;\n\n const hours = range(0, 23, 1).map((value) => ({\n value,\n label: pad(value),\n disabled:\n (minBound != null && value < minBound.hour) || (maxBound != null && value > maxBound.hour),\n }));\n\n const minuteValues = withValue(range(0, 59, minuteStep), current?.minute);\n const minutes = minuteValues.map((value) => ({\n value,\n label: pad(value),\n disabled: minuteDisabled(value, current, minBound, maxBound),\n }));\n\n const seconds = secondsEnabled(step)\n ? range(0, 59, 1).map((value) => ({ value, label: pad(value), disabled: false }))\n : null;\n\n return { hours, minutes, seconds };\n}\n\nfunction minuteDisabled(\n minute: number,\n current: TimeValue | null | undefined,\n minBound: Bound | null,\n maxBound: Bound | null,\n): boolean {\n if (!current) {\n return false;\n }\n\n if (minBound != null && current.hour === minBound.hour && minute < minBound.minute) {\n return true;\n }\n\n if (maxBound != null && current.hour === maxBound.hour && minute > maxBound.minute) {\n return true;\n }\n\n return false;\n}\n\nfunction toBound(value: string | null | undefined): Bound | null {\n const parsed = parseTimeString(value);\n\n return parsed ? { hour: parsed.hour, minute: parsed.minute } : null;\n}\n\nfunction withValue(values: number[], extra: number | undefined): number[] {\n if (extra == null || values.includes(extra)) {\n return values;\n }\n\n return [...values, extra].sort((left, right) => left - right);\n}\n\nfunction range(start: number, end: number, step: number): number[] {\n const values: number[] = [];\n\n for (let value = start; value <= end; value += step) {\n values.push(value);\n }\n\n return values;\n}\n\nfunction pad(value: number): string {\n return String(value).padStart(2, \"0\");\n}\n"],"mappings":";AAYA,IAAM,cAAc;AAEpB,SAAgB,gBAAgB,OAAoD;CAClF,IAAI,OAAO,UAAU,UACnB,OAAO;CAGT,MAAM,QAAQ,YAAY,KAAK,MAAM,KAAK,CAAC;CAE3C,IAAI,CAAC,OACH,OAAO;CAGT,MAAM,OAAO,OAAO,MAAM,EAAE;CAC5B,MAAM,SAAS,OAAO,MAAM,EAAE;CAC9B,MAAM,SAAS,MAAM,KAAK,OAAO,MAAM,EAAE,IAAI;CAE7C,IAAI,OAAO,MAAM,SAAS,MAAM,SAAS,IACvC,OAAO;CAGT,OAAO;EAAE;EAAM;EAAQ;CAAO;AAChC;AAEA,SAAgB,gBAAgB,OAAkB,aAA8B;CAC9E,MAAM,QAAQ,CAAC,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,MAAM,CAAC;CAEjD,IAAI,aACF,MAAM,KAAK,IAAI,MAAM,MAAM,CAAC;CAG9B,OAAO,MAAM,KAAK,GAAG;AACvB;AAEA,SAAgB,eAAe,MAA0C;CACvE,OAAO,QAAQ,QAAQ,OAAO;AAChC;AAEA,SAAgB,iBACd,MACA,UAAoF,CAAC,GACxE;CACb,MAAM,EAAE,KAAK,KAAK,YAAY;CAC9B,MAAM,WAAW,QAAQ,GAAG;CAC5B,MAAM,WAAW,QAAQ,GAAG;CAC5B,MAAM,aAAa,QAAQ,QAAQ,OAAO,KAAK,IAAI,OAAO,OAAO,IAAI,OAAO,KAAK;CAoBjF,OAAO;EAAE,OAlBK,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,WAAW;GAC5C;GACA,OAAO,IAAI,KAAK;GAChB,UACG,YAAY,QAAQ,QAAQ,SAAS,QAAU,YAAY,QAAQ,QAAQ,SAAS;EACzF,EAaS;EAAO,SAXK,UAAU,MAAM,GAAG,IAAI,UAAU,GAAG,SAAS,MAClD,CAAA,CAAa,KAAK,WAAW;GAC3C;GACA,OAAO,IAAI,KAAK;GAChB,UAAU,eAAe,OAAO,SAAS,UAAU,QAAQ;EAC7D,EAMgB;EAAS,SAJT,eAAe,IAAI,IAC/B,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,WAAW;GAAE;GAAO,OAAO,IAAI,KAAK;GAAG,UAAU;EAAM,EAAE,IAC9E;CAE6B;AACnC;AAEA,SAAS,eACP,QACA,SACA,UACA,UACS;CACT,IAAI,CAAC,SACH,OAAO;CAGT,IAAI,YAAY,QAAQ,QAAQ,SAAS,SAAS,QAAQ,SAAS,SAAS,QAC1E,OAAO;CAGT,IAAI,YAAY,QAAQ,QAAQ,SAAS,SAAS,QAAQ,SAAS,SAAS,QAC1E,OAAO;CAGT,OAAO;AACT;AAEA,SAAS,QAAQ,OAAgD;CAC/D,MAAM,SAAS,gBAAgB,KAAK;CAEpC,OAAO,SAAS;EAAE,MAAM,OAAO;EAAM,QAAQ,OAAO;CAAO,IAAI;AACjE;AAEA,SAAS,UAAU,QAAkB,OAAqC;CACxE,IAAI,SAAS,QAAQ,OAAO,SAAS,KAAK,GACxC,OAAO;CAGT,OAAO,CAAC,GAAG,QAAQ,KAAK,CAAC,CAAC,MAAM,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEA,SAAS,MAAM,OAAe,KAAa,MAAwB;CACjE,MAAM,SAAmB,CAAC;CAE1B,KAAK,IAAI,QAAQ,OAAO,SAAS,KAAK,SAAS,MAC7C,OAAO,KAAK,KAAK;CAGnB,OAAO;AACT;AAEA,SAAS,IAAI,OAAuB;CAClC,OAAO,OAAO,KAAK,CAAC,CAAC,SAAS,GAAG,GAAG;AACtC"}
@@ -97,7 +97,6 @@ function TimeColumn({ label, options, selected, disabled, onSelect, onHorizontal
97
97
  case "ArrowLeft":
98
98
  event.preventDefault();
99
99
  onHorizontal(-1);
100
- break;
101
100
  }
102
101
  }
103
102
  return /* @__PURE__ */ jsx("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"time-picker.js","names":[],"sources":["../../../../resources/js/form/components/fields/time-picker.tsx"],"sourcesContent":["import { type KeyboardEvent, useEffect, useRef } from \"react\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { buildTimeColumns, type TimeColumnOption, type TimeValue } from \"./time-picker-columns\";\n\ntype TimePickerProps = {\n value: TimeValue | null;\n onChange: (next: TimeValue) => void;\n step?: number | null;\n min?: string | null;\n max?: string | null;\n disabled?: boolean;\n readOnly?: boolean;\n labels?: { hour?: string; minute?: string; second?: string };\n testId?: string;\n};\n\nexport function TimePicker({\n value,\n onChange,\n step,\n min,\n max,\n disabled = false,\n readOnly = false,\n labels,\n testId,\n}: TimePickerProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const columns = buildTimeColumns(step, { min, max, current: value });\n const current: TimeValue = value ?? { hour: 0, minute: 0, second: 0 };\n const interactive = !disabled && !readOnly;\n\n const columnList = [\n {\n key: \"hour\" as const,\n label: labels?.hour ?? \"Hour\",\n options: columns.hours,\n selected: current.hour,\n },\n {\n key: \"minute\" as const,\n label: labels?.minute ?? \"Minute\",\n options: columns.minutes,\n selected: current.minute,\n },\n ...(columns.seconds\n ? [\n {\n key: \"second\" as const,\n label: labels?.second ?? \"Second\",\n options: columns.seconds,\n selected: current.second,\n },\n ]\n : []),\n ];\n\n function focusColumn(index: number) {\n const listboxes = containerRef.current?.querySelectorAll<HTMLElement>('[role=\"listbox\"]');\n const target = listboxes?.[index];\n\n if (!target) {\n return;\n }\n\n const active =\n target.querySelector<HTMLElement>('[data-active=\"true\"]') ??\n target.querySelector<HTMLElement>('[role=\"option\"]');\n\n active?.focus();\n }\n\n return (\n <div ref={containerRef} className=\"flex gap-1\" data-test={testId}>\n {columnList.map((column, index) => (\n <TimeColumn\n key={column.key}\n label={column.label}\n options={column.options}\n selected={value ? column.selected : null}\n disabled={!interactive}\n onSelect={(optionValue) =>\n interactive && onChange({ ...current, [column.key]: optionValue })\n }\n onHorizontal={(direction) => focusColumn(index + direction)}\n />\n ))}\n </div>\n );\n}\n\nfunction TimeColumn({\n label,\n options,\n selected,\n disabled,\n onSelect,\n onHorizontal,\n}: {\n label: string;\n options: TimeColumnOption[];\n selected: number | null;\n disabled: boolean;\n onSelect: (value: number) => void;\n onHorizontal: (direction: 1 | -1) => void;\n}) {\n const listRef = useRef<HTMLDivElement>(null);\n const enabledValues = options.filter((option) => !option.disabled).map((option) => option.value);\n const activeValue = selected ?? enabledValues[0] ?? options[0]?.value ?? 0;\n\n useEffect(() => {\n const active = listRef.current?.querySelector<HTMLElement>('[data-active=\"true\"]');\n\n active?.scrollIntoView?.({ block: \"nearest\" });\n }, [selected]);\n\n function moveTo(nextValue: number | undefined) {\n if (nextValue == null) {\n return;\n }\n\n listRef.current?.querySelector<HTMLElement>(`[data-value=\"${nextValue}\"]`)?.focus();\n onSelect(nextValue);\n }\n\n function handleKeyDown(event: KeyboardEvent) {\n if (disabled) {\n return;\n }\n\n const index = enabledValues.indexOf(activeValue);\n\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n moveTo(enabledValues[Math.min(index + 1, enabledValues.length - 1)]);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n moveTo(enabledValues[Math.max(index - 1, 0)]);\n break;\n case \"Home\":\n event.preventDefault();\n moveTo(enabledValues[0]);\n break;\n case \"End\":\n event.preventDefault();\n moveTo(enabledValues[enabledValues.length - 1]);\n break;\n case \"ArrowRight\":\n event.preventDefault();\n onHorizontal(1);\n break;\n case \"ArrowLeft\":\n event.preventDefault();\n onHorizontal(-1);\n break;\n }\n }\n\n return (\n <div\n ref={listRef}\n role=\"listbox\"\n aria-label={label}\n aria-orientation=\"vertical\"\n tabIndex={-1}\n className=\"flex max-h-40 w-14 flex-col overflow-y-auto\"\n onKeyDown={handleKeyDown}\n >\n {options.map((option) => {\n const isSelected = selected === option.value;\n\n return (\n <button\n key={option.value}\n type=\"button\"\n role=\"option\"\n aria-selected={isSelected}\n aria-label={`${label} ${option.label}`}\n data-value={option.value}\n data-active={activeValue === option.value}\n disabled={disabled || option.disabled}\n tabIndex={activeValue === option.value ? 0 : -1}\n onClick={() => onSelect(option.value)}\n className={cn(\n \"shrink-0 rounded-lt-sm px-2 py-1 text-sm text-lt-fg hover:bg-lt-muted\",\n isSelected && \"bg-lt-primary text-lt-primary-fg\",\n (disabled || option.disabled) && \"cursor-not-allowed opacity-40\",\n )}\n >\n {option.label}\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"time-picker.js","names":[],"sources":["../../../../resources/js/form/components/fields/time-picker.tsx"],"sourcesContent":["import { type KeyboardEvent, useEffect, useRef } from \"react\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { buildTimeColumns, type TimeColumnOption, type TimeValue } from \"./time-picker-columns\";\n\ntype TimePickerProps = {\n value: TimeValue | null;\n onChange: (next: TimeValue) => void;\n step?: number | null;\n min?: string | null;\n max?: string | null;\n disabled?: boolean;\n readOnly?: boolean;\n labels?: { hour?: string; minute?: string; second?: string };\n testId?: string;\n};\n\nexport function TimePicker({\n value,\n onChange,\n step,\n min,\n max,\n disabled = false,\n readOnly = false,\n labels,\n testId,\n}: TimePickerProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const columns = buildTimeColumns(step, { min, max, current: value });\n const current: TimeValue = value ?? { hour: 0, minute: 0, second: 0 };\n const interactive = !disabled && !readOnly;\n\n const columnList = [\n {\n key: \"hour\" as const,\n label: labels?.hour ?? \"Hour\",\n options: columns.hours,\n selected: current.hour,\n },\n {\n key: \"minute\" as const,\n label: labels?.minute ?? \"Minute\",\n options: columns.minutes,\n selected: current.minute,\n },\n ...(columns.seconds\n ? [\n {\n key: \"second\" as const,\n label: labels?.second ?? \"Second\",\n options: columns.seconds,\n selected: current.second,\n },\n ]\n : []),\n ];\n\n function focusColumn(index: number) {\n const listboxes = containerRef.current?.querySelectorAll<HTMLElement>('[role=\"listbox\"]');\n const target = listboxes?.[index];\n\n if (!target) {\n return;\n }\n\n const active =\n target.querySelector<HTMLElement>('[data-active=\"true\"]') ??\n target.querySelector<HTMLElement>('[role=\"option\"]');\n\n active?.focus();\n }\n\n return (\n <div ref={containerRef} className=\"flex gap-1\" data-test={testId}>\n {columnList.map((column, index) => (\n <TimeColumn\n key={column.key}\n label={column.label}\n options={column.options}\n selected={value ? column.selected : null}\n disabled={!interactive}\n onSelect={(optionValue) =>\n interactive && onChange({ ...current, [column.key]: optionValue })\n }\n onHorizontal={(direction) => focusColumn(index + direction)}\n />\n ))}\n </div>\n );\n}\n\nfunction TimeColumn({\n label,\n options,\n selected,\n disabled,\n onSelect,\n onHorizontal,\n}: {\n label: string;\n options: TimeColumnOption[];\n selected: number | null;\n disabled: boolean;\n onSelect: (value: number) => void;\n onHorizontal: (direction: 1 | -1) => void;\n}) {\n const listRef = useRef<HTMLDivElement>(null);\n const enabledValues = options.filter((option) => !option.disabled).map((option) => option.value);\n const activeValue = selected ?? enabledValues[0] ?? options[0]?.value ?? 0;\n\n useEffect(() => {\n const active = listRef.current?.querySelector<HTMLElement>('[data-active=\"true\"]');\n\n active?.scrollIntoView?.({ block: \"nearest\" });\n }, [selected]);\n\n function moveTo(nextValue: number | undefined) {\n if (nextValue == null) {\n return;\n }\n\n listRef.current?.querySelector<HTMLElement>(`[data-value=\"${nextValue}\"]`)?.focus();\n onSelect(nextValue);\n }\n\n function handleKeyDown(event: KeyboardEvent) {\n if (disabled) {\n return;\n }\n\n const index = enabledValues.indexOf(activeValue);\n\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n moveTo(enabledValues[Math.min(index + 1, enabledValues.length - 1)]);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n moveTo(enabledValues[Math.max(index - 1, 0)]);\n break;\n case \"Home\":\n event.preventDefault();\n moveTo(enabledValues[0]);\n break;\n case \"End\":\n event.preventDefault();\n moveTo(enabledValues[enabledValues.length - 1]);\n break;\n case \"ArrowRight\":\n event.preventDefault();\n onHorizontal(1);\n break;\n case \"ArrowLeft\":\n event.preventDefault();\n onHorizontal(-1);\n break;\n }\n }\n\n return (\n <div\n ref={listRef}\n role=\"listbox\"\n aria-label={label}\n aria-orientation=\"vertical\"\n tabIndex={-1}\n className=\"flex max-h-40 w-14 flex-col overflow-y-auto\"\n onKeyDown={handleKeyDown}\n >\n {options.map((option) => {\n const isSelected = selected === option.value;\n\n return (\n <button\n key={option.value}\n type=\"button\"\n role=\"option\"\n aria-selected={isSelected}\n aria-label={`${label} ${option.label}`}\n data-value={option.value}\n data-active={activeValue === option.value}\n disabled={disabled || option.disabled}\n tabIndex={activeValue === option.value ? 0 : -1}\n onClick={() => onSelect(option.value)}\n className={cn(\n \"shrink-0 rounded-lt-sm px-2 py-1 text-sm text-lt-fg hover:bg-lt-muted\",\n isSelected && \"bg-lt-primary text-lt-primary-fg\",\n (disabled || option.disabled) && \"cursor-not-allowed opacity-40\",\n )}\n >\n {option.label}\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"toggle.js","names":[],"sources":["../../../../resources/js/form/components/fields/toggle.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { FormFieldFrame } from \"@lattice-php/lattice/form/components/base/field\";\nimport { toBoolean } from \"@lattice-php/lattice/form/lib/conditions\";\nimport { useFormContext } from \"@lattice-php/lattice/form/hooks/context\";\nimport { fieldDomName } from \"@lattice-php/lattice/form/lib/field-dom-name\";\nimport { useFieldScope } from \"@lattice-php/lattice/form/hooks/field-scope\";\nimport { useDependentField } from \"@lattice-php/lattice/form/hooks/use-dependent-field\";\nimport { useFieldCommit } from \"@lattice-php/lattice/form/hooks/use-field-commit\";\nimport { useSeedDefault } from \"@lattice-php/lattice/form/hooks/use-seed-default\";\nimport { useFormValue } from \"@lattice-php/lattice/form/hooks/values\";\n\nexport const ToggleComponent: RendererComponent<\"field.toggle\"> = ({ node }) => {\n const { hidden, required, readOnly, disabled } = useDependentField(node);\n const props = node.props;\n const localName = props.name;\n const scope = useFieldScope();\n const { errors, fieldIdPrefix } = useFormContext();\n const name = fieldDomName(scope ? scope.scopedName(localName) : localName, fieldIdPrefix);\n const errorKey = scope ? scope.errorKey(localName) : localName;\n const globalValue = useFormValue(localName);\n const storedValue = scope ? scope.getValue(localName) : globalValue;\n const defaultChecked = toBoolean(props.value);\n const checked = storedValue !== undefined ? toBoolean(storedValue) : defaultChecked;\n const locked = readOnly || disabled;\n const { commit } = useFieldCommit();\n\n useSeedDefault(localName, defaultChecked);\n\n if (hidden) {\n return null;\n }\n\n return (\n <FormFieldFrame\n error={errors[errorKey]}\n helperText={props.helperText ?? undefined}\n tooltip={props.tooltip ?? undefined}\n label={props.label ?? \"\"}\n name={name}\n required={required}\n >\n <input disabled={locked} name={name} type=\"hidden\" value={checked ? \"1\" : \"0\"} />\n <button\n aria-checked={checked}\n aria-label={props.label ?? localName}\n autoFocus={props.autoFocus ?? false}\n className={cn(\n \"inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border border-transparent bg-lt-muted p-0.5 shadow-lt-xs transition-colors outline-none focus-visible:border-lt-ring focus-visible:ring-[length:var(--lt-ring-width)] focus-visible:ring-lt-ring/50 disabled:cursor-not-allowed disabled:bg-lt-disabled data-[state=checked]:bg-lt-primary disabled:data-[state=checked]:bg-lt-disabled\",\n )}\n data-state={checked ? \"checked\" : \"unchecked\"}\n data-test={testIdentity(localName)}\n disabled={locked}\n id={name}\n name={name}\n onClick={() => commit(localName, !checked)}\n role=\"switch\"\n tabIndex={props.tabIndex ?? undefined}\n type=\"button\"\n >\n <span\n className=\"size-5 rounded-full bg-lt-bg shadow-lt-sm transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0\"\n data-state={checked ? \"checked\" : \"unchecked\"}\n />\n </button>\n </FormFieldFrame>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AAaA,IAAa,mBAAsD,EAAE,WAAW;CAC9E,MAAM,EAAE,QAAQ,UAAU,UAAU,aAAa,kBAAkB,IAAI;CACvE,MAAM,QAAQ,KAAK;CACnB,MAAM,YAAY,MAAM;CACxB,MAAM,QAAQ,cAAc;CAC5B,MAAM,EAAE,QAAQ,kBAAkB,eAAe;CACjD,MAAM,OAAO,aAAa,QAAQ,MAAM,WAAW,SAAS,IAAI,WAAW,aAAa;CACxF,MAAM,WAAW,QAAQ,MAAM,SAAS,SAAS,IAAI;CACrD,MAAM,cAAc,aAAa,SAAS;CAC1C,MAAM,cAAc,QAAQ,MAAM,SAAS,SAAS,IAAI;CACxD,MAAM,iBAAiB,UAAU,MAAM,KAAK;CAC5C,MAAM,UAAU,gBAAgB,KAAA,IAAY,UAAU,WAAW,IAAI;CACrE,MAAM,SAAS,YAAY;CAC3B,MAAM,EAAE,WAAW,eAAe;CAElC,eAAe,WAAW,cAAc;CAExC,IAAI,QACF,OAAO;CAGT,OACE,qBAAC,gBAAD;EACE,OAAO,OAAO;EACd,YAAY,MAAM,cAAc,KAAA;EAChC,SAAS,MAAM,WAAW,KAAA;EAC1B,OAAO,MAAM,SAAS;EAChB;EACI;YANZ,CAQE,oBAAC,SAAD;GAAO,UAAU;GAAc;GAAM,MAAK;GAAS,OAAO,UAAU,MAAM;EAAM,CAAA,GAChF,oBAAC,UAAD;GACE,gBAAc;GACd,cAAY,MAAM,SAAS;GAC3B,WAAW,MAAM,aAAa;GAC9B,WAAW,GACT,gZACF;GACA,cAAY,UAAU,YAAY;GAClC,aAAW,aAAa,SAAS;GACjC,UAAU;GACV,IAAI;GACE;GACN,eAAe,OAAO,WAAW,CAAC,OAAO;GACzC,MAAK;GACL,UAAU,MAAM,YAAY,KAAA;GAC5B,MAAK;aAEL,oBAAC,QAAD;IACE,WAAU;IACV,cAAY,UAAU,YAAY;GACnC,CAAA;EACK,CAAA,CACM;;AAEpB"}
1
+ {"version":3,"file":"toggle.js","names":[],"sources":["../../../../resources/js/form/components/fields/toggle.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { testIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { FormFieldFrame } from \"@lattice-php/lattice/form/components/base/field\";\nimport { toBoolean } from \"@lattice-php/lattice/form/lib/conditions\";\nimport { useFormContext } from \"@lattice-php/lattice/form/hooks/context\";\nimport { fieldDomName } from \"@lattice-php/lattice/form/lib/field-dom-name\";\nimport { useFieldScope } from \"@lattice-php/lattice/form/hooks/field-scope\";\nimport { useDependentField } from \"@lattice-php/lattice/form/hooks/use-dependent-field\";\nimport { useFieldCommit } from \"@lattice-php/lattice/form/hooks/use-field-commit\";\nimport { useSeedDefault } from \"@lattice-php/lattice/form/hooks/use-seed-default\";\nimport { useFormValue } from \"@lattice-php/lattice/form/hooks/values\";\n\nexport const ToggleComponent: RendererComponent<\"field.toggle\"> = ({ node }) => {\n const { hidden, required, readOnly, disabled } = useDependentField(node);\n const props = node.props;\n const localName = props.name;\n const scope = useFieldScope();\n const { errors, fieldIdPrefix } = useFormContext();\n const name = fieldDomName(scope ? scope.scopedName(localName) : localName, fieldIdPrefix);\n const errorKey = scope ? scope.errorKey(localName) : localName;\n const globalValue = useFormValue(localName);\n const storedValue = scope ? scope.getValue(localName) : globalValue;\n const defaultChecked = toBoolean(props.value);\n const checked = storedValue !== undefined ? toBoolean(storedValue) : defaultChecked;\n const locked = readOnly || disabled;\n const { commit } = useFieldCommit();\n\n useSeedDefault(localName, defaultChecked);\n\n if (hidden) {\n return null;\n }\n\n return (\n <FormFieldFrame\n error={errors[errorKey]}\n helperText={props.helperText ?? undefined}\n tooltip={props.tooltip ?? undefined}\n label={props.label ?? \"\"}\n name={name}\n required={required}\n >\n <input disabled={locked} name={name} type=\"hidden\" value={checked ? \"1\" : \"0\"} />\n <button\n aria-checked={checked}\n aria-label={props.label ?? localName}\n autoFocus={props.autoFocus ?? false}\n className={cn(\n \"inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border border-transparent bg-lt-muted p-0.5 shadow-lt-xs transition-colors outline-none focus-visible:border-lt-ring focus-visible:ring-[length:var(--lt-ring-width)] focus-visible:ring-lt-ring/50 disabled:cursor-not-allowed disabled:bg-lt-disabled data-[state=checked]:bg-lt-primary disabled:data-[state=checked]:bg-lt-disabled\",\n )}\n data-state={checked ? \"checked\" : \"unchecked\"}\n data-test={testIdentity(localName)}\n disabled={locked}\n id={name}\n name={name}\n onClick={() => commit(localName, !checked)}\n role=\"switch\"\n tabIndex={props.tabIndex ?? undefined}\n type=\"button\"\n >\n <span\n className=\"size-5 rounded-full bg-lt-bg shadow-lt-sm transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0\"\n data-state={checked ? \"checked\" : \"unchecked\"}\n />\n </button>\n </FormFieldFrame>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AAaA,IAAa,mBAAsD,EAAE,WAAW;CAC9E,MAAM,EAAE,QAAQ,UAAU,UAAU,aAAa,kBAAkB,IAAI;CACvE,MAAM,QAAQ,KAAK;CACnB,MAAM,YAAY,MAAM;CACxB,MAAM,QAAQ,cAAc;CAC5B,MAAM,EAAE,QAAQ,kBAAkB,eAAe;CACjD,MAAM,OAAO,aAAa,QAAQ,MAAM,WAAW,SAAS,IAAI,WAAW,aAAa;CACxF,MAAM,WAAW,QAAQ,MAAM,SAAS,SAAS,IAAI;CACrD,MAAM,cAAc,aAAa,SAAS;CAC1C,MAAM,cAAc,QAAQ,MAAM,SAAS,SAAS,IAAI;CACxD,MAAM,iBAAiB,UAAU,MAAM,KAAK;CAC5C,MAAM,UAAU,gBAAgB,KAAA,IAAY,UAAU,WAAW,IAAI;CACrE,MAAM,SAAS,YAAY;CAC3B,MAAM,EAAE,WAAW,eAAe;CAElC,eAAe,WAAW,cAAc;CAExC,IAAI,QACF,OAAO;CAGT,OACE,qBAAC,gBAAD;EACE,OAAO,OAAO;EACd,YAAY,MAAM,cAAc,KAAA;EAChC,SAAS,MAAM,WAAW,KAAA;EAC1B,OAAO,MAAM,SAAS;EAChB;EACI;EANZ,UAAA,CAQE,oBAAC,SAAD;GAAO,UAAU;GAAc;GAAM,MAAK;GAAS,OAAO,UAAU,MAAM;EAAM,CAAA,GAChF,oBAAC,UAAD;GACE,gBAAc;GACd,cAAY,MAAM,SAAS;GAC3B,WAAW,MAAM,aAAa;GAC9B,WAAW,GACT,gZACF;GACA,cAAY,UAAU,YAAY;GAClC,aAAW,aAAa,SAAS;GACjC,UAAU;GACV,IAAI;GACE;GACN,eAAe,OAAO,WAAW,CAAC,OAAO;GACzC,MAAK;GACL,UAAU,MAAM,YAAY,KAAA;GAC5B,MAAK;GAEL,UAAA,oBAAC,QAAD;IACE,WAAU;IACV,cAAY,UAAU,YAAY;GACnC,CAAA;EACK,CAAA,CACM;;AAEpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-flip-reorder.js","names":[],"sources":["../../../../resources/js/form/components/fields/use-flip-reorder.ts"],"sourcesContent":["import { useCallback, useLayoutEffect, useRef } from \"react\";\n\nconst DURATION_MS = 180;\n\nfunction prefersReducedMotion(): boolean {\n return typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n\n/**\n * FLIP reorder: animates registered elements from their previous to current\n * position whenever `orderSignature` changes. Imperative style writes only, so\n * it does not affect React render or the memoised rows.\n */\nexport function useFlipReorder(\n orderSignature: string,\n): (key: string, el: HTMLElement | null) => void {\n const elements = useRef(new Map<string, HTMLElement>());\n const previous = useRef(new Map<string, DOMRect>());\n\n // register is the FLIP measurement target; the element must be a zero-overhead positioning wrapper (no padding/margin) for the delta to match the visible row.\n const register = useCallback((key: string, el: HTMLElement | null): void => {\n if (el) {\n elements.current.set(key, el);\n } else {\n elements.current.delete(key);\n }\n }, []);\n\n useLayoutEffect(() => {\n const handles: number[] = [];\n const next = new Map<string, DOMRect>();\n elements.current.forEach((el, key) => {\n el.style.transition = \"\";\n el.style.transform = \"\";\n next.set(key, el.getBoundingClientRect());\n });\n\n if (!prefersReducedMotion()) {\n elements.current.forEach((el, key) => {\n const before = previous.current.get(key);\n const after = next.get(key);\n if (before && after) {\n const dy = before.top - after.top;\n if (dy) {\n el.style.transform = `translateY(${dy}px)`;\n handles.push(\n requestAnimationFrame(() => {\n el.style.transition = `transform ${DURATION_MS}ms ease-out`;\n el.style.transform = \"\";\n }),\n );\n }\n }\n });\n }\n\n previous.current = next;\n\n return () => {\n for (const handle of handles) {\n cancelAnimationFrame(handle);\n }\n };\n }, [orderSignature]);\n\n return register;\n}\n"],"mappings":";;AAEA,IAAM,cAAc;AAEpB,SAAS,uBAAgC;CACvC,OAAO,OAAO,eAAe,cAAc,WAAW,kCAAkC,EAAE;AAC5F;;;;;;AAOA,SAAgB,eACd,gBAC+C;CAC/C,MAAM,WAAW,uBAAO,IAAI,IAAyB,CAAC;CACtD,MAAM,WAAW,uBAAO,IAAI,IAAqB,CAAC;CAGlD,MAAM,WAAW,aAAa,KAAa,OAAiC;EAC1E,IAAI,IACF,SAAS,QAAQ,IAAI,KAAK,EAAE;OAE5B,SAAS,QAAQ,OAAO,GAAG;CAE/B,GAAG,CAAC,CAAC;CAEL,sBAAsB;EACpB,MAAM,UAAoB,CAAC;EAC3B,MAAM,uBAAO,IAAI,IAAqB;EACtC,SAAS,QAAQ,SAAS,IAAI,QAAQ;GACpC,GAAG,MAAM,aAAa;GACtB,GAAG,MAAM,YAAY;GACrB,KAAK,IAAI,KAAK,GAAG,sBAAsB,CAAC;EAC1C,CAAC;EAED,IAAI,CAAC,qBAAqB,GACxB,SAAS,QAAQ,SAAS,IAAI,QAAQ;GACpC,MAAM,SAAS,SAAS,QAAQ,IAAI,GAAG;GACvC,MAAM,QAAQ,KAAK,IAAI,GAAG;GAC1B,IAAI,UAAU,OAAO;IACnB,MAAM,KAAK,OAAO,MAAM,MAAM;IAC9B,IAAI,IAAI;KACN,GAAG,MAAM,YAAY,cAAc,GAAG;KACtC,QAAQ,KACN,4BAA4B;MAC1B,GAAG,MAAM,aAAa,aAAa,YAAY;MAC/C,GAAG,MAAM,YAAY;KACvB,CAAC,CACH;IACF;GACF;EACF,CAAC;EAGH,SAAS,UAAU;EAEnB,aAAa;GACX,KAAK,MAAM,UAAU,SACnB,qBAAqB,MAAM;EAE/B;CACF,GAAG,CAAC,cAAc,CAAC;CAEnB,OAAO;AACT"}
1
+ {"version":3,"file":"use-flip-reorder.js","names":[],"sources":["../../../../resources/js/form/components/fields/use-flip-reorder.ts"],"sourcesContent":["import { useCallback, useLayoutEffect, useRef } from \"react\";\n\nconst DURATION_MS = 180;\n\nfunction prefersReducedMotion(): boolean {\n return typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n\n/**\n * FLIP reorder: animates registered elements from their previous to current\n * position whenever `orderSignature` changes. Imperative style writes only, so\n * it does not affect React render or the memoised rows.\n */\nexport function useFlipReorder(\n orderSignature: string,\n): (key: string, el: HTMLElement | null) => void {\n const elements = useRef(new Map<string, HTMLElement>());\n const previous = useRef(new Map<string, DOMRect>());\n\n // register is the FLIP measurement target; the element must be a zero-overhead positioning wrapper (no padding/margin) for the delta to match the visible row.\n const register = useCallback((key: string, el: HTMLElement | null): void => {\n if (el) {\n elements.current.set(key, el);\n } else {\n elements.current.delete(key);\n }\n }, []);\n\n useLayoutEffect(() => {\n const handles: number[] = [];\n const next = new Map<string, DOMRect>();\n elements.current.forEach((el, key) => {\n el.style.transition = \"\";\n el.style.transform = \"\";\n next.set(key, el.getBoundingClientRect());\n });\n\n if (!prefersReducedMotion()) {\n elements.current.forEach((el, key) => {\n const before = previous.current.get(key);\n const after = next.get(key);\n if (before && after) {\n const dy = before.top - after.top;\n if (dy) {\n el.style.transform = `translateY(${dy}px)`;\n handles.push(\n requestAnimationFrame(() => {\n el.style.transition = `transform ${DURATION_MS}ms ease-out`;\n el.style.transform = \"\";\n }),\n );\n }\n }\n });\n }\n\n previous.current = next;\n\n return () => {\n for (const handle of handles) {\n cancelAnimationFrame(handle);\n }\n };\n }, [orderSignature]);\n\n return register;\n}\n"],"mappings":";;AAEA,IAAM,cAAc;AAEpB,SAAS,uBAAgC;CACvC,OAAO,OAAO,eAAe,cAAc,WAAW,kCAAkC,CAAC,CAAC;AAC5F;;;;;;AAOA,SAAgB,eACd,gBAC+C;CAC/C,MAAM,WAAW,uBAAO,IAAI,IAAyB,CAAC;CACtD,MAAM,WAAW,uBAAO,IAAI,IAAqB,CAAC;CAGlD,MAAM,WAAW,aAAa,KAAa,OAAiC;EAC1E,IAAI,IACF,SAAS,QAAQ,IAAI,KAAK,EAAE;OAE5B,SAAS,QAAQ,OAAO,GAAG;CAE/B,GAAG,CAAC,CAAC;CAEL,sBAAsB;EACpB,MAAM,UAAoB,CAAC;EAC3B,MAAM,uBAAO,IAAI,IAAqB;EACtC,SAAS,QAAQ,SAAS,IAAI,QAAQ;GACpC,GAAG,MAAM,aAAa;GACtB,GAAG,MAAM,YAAY;GACrB,KAAK,IAAI,KAAK,GAAG,sBAAsB,CAAC;EAC1C,CAAC;EAED,IAAI,CAAC,qBAAqB,GACxB,SAAS,QAAQ,SAAS,IAAI,QAAQ;GACpC,MAAM,SAAS,SAAS,QAAQ,IAAI,GAAG;GACvC,MAAM,QAAQ,KAAK,IAAI,GAAG;GAC1B,IAAI,UAAU,OAAO;IACnB,MAAM,KAAK,OAAO,MAAM,MAAM;IAC9B,IAAI,IAAI;KACN,GAAG,MAAM,YAAY,cAAc,GAAG;KACtC,QAAQ,KACN,4BAA4B;MAC1B,GAAG,MAAM,aAAa,aAAa,YAAY;MAC/C,GAAG,MAAM,YAAY;KACvB,CAAC,CACH;IACF;GACF;EACF,CAAC;EAGH,SAAS,UAAU;EAEnB,aAAa;GACX,KAAK,MAAM,UAAU,SACnB,qBAAqB,MAAM;EAE/B;CACF,GAAG,CAAC,cAAc,CAAC;CAEnB,OAAO;AACT"}
@@ -37,8 +37,9 @@ function FormResetListener({ componentId, reset }) {
37
37
  }
38
38
  function FormBody({ action, children, componentRef, nodes, shouldRenderSubmitButton, submitButtons, submitEmphasis, submitJustify, submitLabel, submitVariant, summaryLabel }) {
39
39
  const { nodes: resolvedNodes, markUserEdit } = useFormResolver(action, componentRef, nodes);
40
+ const prefill = useMemo(() => ({ markUserEdit }), [markUserEdit]);
40
41
  return /* @__PURE__ */ jsx(PrefillProvider, {
41
- value: useMemo(() => ({ markUserEdit }), [markUserEdit]),
42
+ value: prefill,
42
43
  children: /* @__PURE__ */ jsx(ResolvedNodesProvider, {
43
44
  nodes: resolvedNodes,
44
45
  children: /* @__PURE__ */ jsxs("div", {
@@ -66,11 +67,12 @@ function configuredResetFields(configured) {
66
67
  return Array.isArray(configured) ? configured : void 0;
67
68
  }
68
69
  var FormComponent = ({ children, node }) => {
70
+ const initialValues = useMemo(() => ({
71
+ ...collectFields(node.schema).values,
72
+ ...node.props.state
73
+ }), [node.schema, node.props.state]);
69
74
  return /* @__PURE__ */ jsx(FormValuesProvider, {
70
- initial: useMemo(() => ({
71
- ...collectFields(node.schema).values,
72
- ...node.props.state
73
- }), [node.schema, node.props.state]),
75
+ initial: initialValues,
74
76
  children: /* @__PURE__ */ jsx(FormShell, {
75
77
  node,
76
78
  children
@@ -1 +1 @@
1
- {"version":3,"file":"form.js","names":[],"sources":["../../../resources/js/form/components/form.tsx"],"sourcesContent":["import type { FormComponentRef, HttpExceptionResponse } from \"@inertiajs/core\";\nimport { Form as InertiaForm } from \"@inertiajs/react\";\nimport { refreshRef } from \"@lattice-php/lattice/core/api\";\nimport { withHeaders } from \"@lattice-php/lattice/core/headers\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/core/event-names\";\nimport { useWindowEvent } from \"@lattice-php/lattice/core/hooks/use-window-event\";\nimport { nodeKey } from \"@lattice-php/lattice/core/nodes\";\nimport { RenderNode } from \"@lattice-php/lattice/core/renderer\";\nimport type { Node, RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport type { Emphasis, Justify, Variant } from \"@lattice-php/lattice/types/generated\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { FormSubmitButton } from \"./base/submit-button\";\nimport { FormProvider } from \"@lattice-php/lattice/form/hooks/context\";\nimport { collectFields } from \"@lattice-php/lattice/form/lib/collect-fields\";\nimport { PrefillProvider } from \"@lattice-php/lattice/form/hooks/prefill-context\";\nimport { ResolvedNodesProvider } from \"@lattice-php/lattice/form/hooks/resolved-nodes\";\nimport { useFormResolver } from \"@lattice-php/lattice/form/hooks/use-form-resolver\";\nimport { FormValuesProvider, useResetFormValues } from \"@lattice-php/lattice/form/hooks/values\";\n\nconst JUSTIFY_CLASS: Record<Justify, string> = {\n start: \"justify-start\",\n center: \"justify-center\",\n end: \"justify-end\",\n between: \"justify-between\",\n around: \"justify-around\",\n evenly: \"justify-evenly\",\n};\n\nfunction FormResetListener({\n componentId,\n reset,\n}: {\n componentId?: string;\n reset: (...fields: string[]) => void;\n}) {\n const resetValues = useResetFormValues();\n\n useWindowEvent(LATTICE_EVENT.resetForm, (event) => {\n const detail = (event as CustomEvent<{ form: string | null }>).detail;\n\n if (!detail?.form || detail.form === componentId) {\n reset();\n resetValues();\n }\n });\n\n return null;\n}\n\nfunction FormBody({\n action,\n children,\n componentRef,\n nodes,\n shouldRenderSubmitButton,\n submitButtons,\n submitEmphasis,\n submitJustify,\n submitLabel,\n submitVariant,\n summaryLabel,\n}: {\n action: string;\n children: React.ReactNode;\n componentRef: string;\n nodes: Node[] | undefined;\n shouldRenderSubmitButton: boolean;\n submitButtons: Node<\"button\">[] | undefined;\n submitEmphasis: Emphasis | undefined;\n submitJustify: Justify | undefined;\n submitLabel: string;\n submitVariant: Variant | undefined;\n summaryLabel: string;\n}) {\n const { nodes: resolvedNodes, markUserEdit } = useFormResolver(action, componentRef, nodes);\n const prefill = useMemo(() => ({ markUserEdit }), [markUserEdit]);\n\n return (\n <PrefillProvider value={prefill}>\n <ResolvedNodesProvider nodes={resolvedNodes}>\n <div className=\"flex flex-col gap-6\">\n {children}\n\n {shouldRenderSubmitButton && (\n <div className={`flex gap-3 ${JUSTIFY_CLASS[submitJustify ?? \"end\"]}`}>\n {submitButtons?.length ? (\n submitButtons.map((button, index) =>\n button.props.buttonType === \"submit\" ? (\n <FormSubmitButton\n key={nodeKey(button, index)}\n emphasis={button.props.emphasis ?? submitEmphasis}\n label={button.props.label ?? submitLabel}\n summaryLabel={summaryLabel}\n variant={button.props.variant ?? submitVariant}\n />\n ) : (\n <RenderNode key={nodeKey(button, index)} node={button} />\n ),\n )\n ) : (\n <FormSubmitButton\n emphasis={submitEmphasis}\n label={submitLabel}\n summaryLabel={summaryLabel}\n variant={submitVariant}\n />\n )}\n </div>\n )}\n </div>\n </ResolvedNodesProvider>\n </PrefillProvider>\n );\n}\n\nfunction configuredResetFields(\n configured: string[] | boolean | null | undefined,\n): string[] | undefined | false {\n if (!configured || (Array.isArray(configured) && configured.length === 0)) {\n return false;\n }\n\n return Array.isArray(configured) ? configured : undefined;\n}\n\nexport const FormComponent: RendererComponent<\"form\"> = ({ children, node }) => {\n const initialValues = useMemo(\n () => ({ ...collectFields(node.schema).values, ...node.props.state }),\n [node.schema, node.props.state],\n );\n\n return (\n <FormValuesProvider initial={initialValues}>\n <FormShell node={node}>{children}</FormShell>\n </FormValuesProvider>\n );\n};\n\nfunction FormShell({ children, node }: { children: React.ReactNode; node: Node<\"form\"> }) {\n const { t } = useT(\"lattice\");\n const props = node.props;\n const action = props.action ?? \"#\";\n const errorBag = props.errorBag;\n const componentRef = props.ref ?? \"\";\n const method = props.method ?? \"post\";\n const precognitive = props.precognitive;\n const resetOnError = props.resetOnError ?? false;\n const resetOnSuccess = props.resetOnSuccess ?? [];\n const fieldLabels = useMemo(() => collectFields(node.schema).labels, [node.schema]);\n const shouldRenderSubmitButton = props.submitButton;\n const submitButtons = props.submitButtons ?? undefined;\n const submitJustify = props.submitJustify ?? undefined;\n const submitLabel = props.submitLabel ?? t(\"form.submit\", \"Submit\");\n const submitVariant = props.submitVariant ?? undefined;\n const submitEmphasis = props.submitEmphasis ?? undefined;\n const summaryLabel = props.validationSummaryLabel;\n const validationTimeout = props.validationTimeout ?? undefined;\n\n const resetValues = useResetFormValues();\n const resetConfigured = (configured: string[] | boolean | null | undefined): void => {\n const fields = configuredResetFields(configured);\n\n if (fields !== false) {\n resetValues(fields);\n }\n };\n\n const formRef = useRef<FormComponentRef | null>(null);\n // One renewal attempt per user-initiated submit: 'renewing' marks the window\n // between the 403 and our programmatic retry, 'retried' lets the second\n // failure surface normally.\n const retryPhase = useRef<\"idle\" | \"renewing\" | \"retried\">(\"idle\");\n const [renewedSubmitTick, setRenewedSubmitTick] = useState(0);\n\n useEffect(() => {\n // The state bump forces this render first, so the headers prop below has\n // re-resolved the renewed token before the retry goes out.\n if (renewedSubmitTick > 0 && retryPhase.current === \"renewing\") {\n formRef.current?.submit();\n }\n }, [renewedSubmitTick]);\n\n return (\n <InertiaForm\n ref={formRef}\n action={action}\n data-slot=\"form\"\n data-lattice-component={node.id}\n errorBag={errorBag}\n method={method}\n resetOnError={resetOnError}\n resetOnSuccess={resetOnSuccess}\n validationTimeout={precognitive ? validationTimeout : undefined}\n headers={withHeaders(componentRef)}\n className=\"mx-auto flex w-full max-w-2xl flex-col gap-6\"\n onStart={() => {\n retryPhase.current = retryPhase.current === \"renewing\" ? \"retried\" : \"idle\";\n }}\n onSuccess={() => resetConfigured(resetOnSuccess)}\n onError={() => resetConfigured(resetOnError)}\n onHttpException={(response: HttpExceptionResponse) => {\n if (response.status !== 403 || componentRef === \"\" || retryPhase.current === \"retried\") {\n return undefined;\n }\n\n retryPhase.current = \"renewing\";\n // Retry even when the renewal failed: a genuine 403 then surfaces\n // through the second attempt instead of being swallowed here.\n void refreshRef(componentRef).finally(() => setRenewedSubmitTick((tick) => tick + 1));\n\n return false;\n }}\n >\n {({ clearErrors, errors, processing, reset, touch, validate, validating }) => (\n <FormProvider\n value={{\n action,\n clearErrors: (field) => clearErrors(field),\n componentId: node.id,\n componentRef,\n errors: errors as Record<string, string | undefined>,\n fieldLabels,\n precognitive,\n processing,\n touch: (fields) => touch(...fields),\n validate: (field) => validate(field),\n validateFields: (fields, options) => validate({ only: fields, ...options }),\n validating,\n }}\n >\n <FormResetListener componentId={node.id} reset={reset} />\n\n {props.status && (\n <div className=\"text-center text-sm font-medium text-lt-success\">{props.status}</div>\n )}\n\n <FormBody\n action={action}\n componentRef={componentRef}\n nodes={node.schema}\n shouldRenderSubmitButton={shouldRenderSubmitButton}\n submitButtons={submitButtons}\n submitEmphasis={submitEmphasis}\n submitJustify={submitJustify}\n submitLabel={submitLabel}\n submitVariant={submitVariant}\n summaryLabel={summaryLabel}\n >\n {children}\n </FormBody>\n </FormProvider>\n )}\n </InertiaForm>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"form.js","names":[],"sources":["../../../resources/js/form/components/form.tsx"],"sourcesContent":["import type { FormComponentRef, HttpExceptionResponse } from \"@inertiajs/core\";\nimport { Form as InertiaForm } from \"@inertiajs/react\";\nimport { refreshRef } from \"@lattice-php/lattice/core/api\";\nimport { withHeaders } from \"@lattice-php/lattice/core/headers\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/core/event-names\";\nimport { useWindowEvent } from \"@lattice-php/lattice/core/hooks/use-window-event\";\nimport { nodeKey } from \"@lattice-php/lattice/core/nodes\";\nimport { RenderNode } from \"@lattice-php/lattice/core/renderer\";\nimport type { Node, RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport type { Emphasis, Justify, Variant } from \"@lattice-php/lattice/types/generated\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { FormSubmitButton } from \"./base/submit-button\";\nimport { FormProvider } from \"@lattice-php/lattice/form/hooks/context\";\nimport { collectFields } from \"@lattice-php/lattice/form/lib/collect-fields\";\nimport { PrefillProvider } from \"@lattice-php/lattice/form/hooks/prefill-context\";\nimport { ResolvedNodesProvider } from \"@lattice-php/lattice/form/hooks/resolved-nodes\";\nimport { useFormResolver } from \"@lattice-php/lattice/form/hooks/use-form-resolver\";\nimport { FormValuesProvider, useResetFormValues } from \"@lattice-php/lattice/form/hooks/values\";\n\nconst JUSTIFY_CLASS: Record<Justify, string> = {\n start: \"justify-start\",\n center: \"justify-center\",\n end: \"justify-end\",\n between: \"justify-between\",\n around: \"justify-around\",\n evenly: \"justify-evenly\",\n};\n\nfunction FormResetListener({\n componentId,\n reset,\n}: {\n componentId?: string;\n reset: (...fields: string[]) => void;\n}) {\n const resetValues = useResetFormValues();\n\n useWindowEvent(LATTICE_EVENT.resetForm, (event) => {\n const detail = (event as CustomEvent<{ form: string | null }>).detail;\n\n if (!detail?.form || detail.form === componentId) {\n reset();\n resetValues();\n }\n });\n\n return null;\n}\n\nfunction FormBody({\n action,\n children,\n componentRef,\n nodes,\n shouldRenderSubmitButton,\n submitButtons,\n submitEmphasis,\n submitJustify,\n submitLabel,\n submitVariant,\n summaryLabel,\n}: {\n action: string;\n children: React.ReactNode;\n componentRef: string;\n nodes: Node[] | undefined;\n shouldRenderSubmitButton: boolean;\n submitButtons: Node<\"button\">[] | undefined;\n submitEmphasis: Emphasis | undefined;\n submitJustify: Justify | undefined;\n submitLabel: string;\n submitVariant: Variant | undefined;\n summaryLabel: string;\n}) {\n const { nodes: resolvedNodes, markUserEdit } = useFormResolver(action, componentRef, nodes);\n const prefill = useMemo(() => ({ markUserEdit }), [markUserEdit]);\n\n return (\n <PrefillProvider value={prefill}>\n <ResolvedNodesProvider nodes={resolvedNodes}>\n <div className=\"flex flex-col gap-6\">\n {children}\n\n {shouldRenderSubmitButton && (\n <div className={`flex gap-3 ${JUSTIFY_CLASS[submitJustify ?? \"end\"]}`}>\n {submitButtons?.length ? (\n submitButtons.map((button, index) =>\n button.props.buttonType === \"submit\" ? (\n <FormSubmitButton\n key={nodeKey(button, index)}\n emphasis={button.props.emphasis ?? submitEmphasis}\n label={button.props.label ?? submitLabel}\n summaryLabel={summaryLabel}\n variant={button.props.variant ?? submitVariant}\n />\n ) : (\n <RenderNode key={nodeKey(button, index)} node={button} />\n ),\n )\n ) : (\n <FormSubmitButton\n emphasis={submitEmphasis}\n label={submitLabel}\n summaryLabel={summaryLabel}\n variant={submitVariant}\n />\n )}\n </div>\n )}\n </div>\n </ResolvedNodesProvider>\n </PrefillProvider>\n );\n}\n\nfunction configuredResetFields(\n configured: string[] | boolean | null | undefined,\n): string[] | undefined | false {\n if (!configured || (Array.isArray(configured) && configured.length === 0)) {\n return false;\n }\n\n return Array.isArray(configured) ? configured : undefined;\n}\n\nexport const FormComponent: RendererComponent<\"form\"> = ({ children, node }) => {\n const initialValues = useMemo(\n () => ({ ...collectFields(node.schema).values, ...node.props.state }),\n [node.schema, node.props.state],\n );\n\n return (\n <FormValuesProvider initial={initialValues}>\n <FormShell node={node}>{children}</FormShell>\n </FormValuesProvider>\n );\n};\n\nfunction FormShell({ children, node }: { children: React.ReactNode; node: Node<\"form\"> }) {\n const { t } = useT(\"lattice\");\n const props = node.props;\n const action = props.action ?? \"#\";\n const errorBag = props.errorBag;\n const componentRef = props.ref ?? \"\";\n const method = props.method ?? \"post\";\n const precognitive = props.precognitive;\n const resetOnError = props.resetOnError ?? false;\n const resetOnSuccess = props.resetOnSuccess ?? [];\n const fieldLabels = useMemo(() => collectFields(node.schema).labels, [node.schema]);\n const shouldRenderSubmitButton = props.submitButton;\n const submitButtons = props.submitButtons ?? undefined;\n const submitJustify = props.submitJustify ?? undefined;\n const submitLabel = props.submitLabel ?? t(\"form.submit\", \"Submit\");\n const submitVariant = props.submitVariant ?? undefined;\n const submitEmphasis = props.submitEmphasis ?? undefined;\n const summaryLabel = props.validationSummaryLabel;\n const validationTimeout = props.validationTimeout ?? undefined;\n\n const resetValues = useResetFormValues();\n const resetConfigured = (configured: string[] | boolean | null | undefined): void => {\n const fields = configuredResetFields(configured);\n\n if (fields !== false) {\n resetValues(fields);\n }\n };\n\n const formRef = useRef<FormComponentRef | null>(null);\n // One renewal attempt per user-initiated submit: 'renewing' marks the window\n // between the 403 and our programmatic retry, 'retried' lets the second\n // failure surface normally.\n const retryPhase = useRef<\"idle\" | \"renewing\" | \"retried\">(\"idle\");\n const [renewedSubmitTick, setRenewedSubmitTick] = useState(0);\n\n useEffect(() => {\n // The state bump forces this render first, so the headers prop below has\n // re-resolved the renewed token before the retry goes out.\n if (renewedSubmitTick > 0 && retryPhase.current === \"renewing\") {\n formRef.current?.submit();\n }\n }, [renewedSubmitTick]);\n\n return (\n <InertiaForm\n ref={formRef}\n action={action}\n data-slot=\"form\"\n data-lattice-component={node.id}\n errorBag={errorBag}\n method={method}\n resetOnError={resetOnError}\n resetOnSuccess={resetOnSuccess}\n validationTimeout={precognitive ? validationTimeout : undefined}\n headers={withHeaders(componentRef)}\n className=\"mx-auto flex w-full max-w-2xl flex-col gap-6\"\n onStart={() => {\n retryPhase.current = retryPhase.current === \"renewing\" ? \"retried\" : \"idle\";\n }}\n onSuccess={() => resetConfigured(resetOnSuccess)}\n onError={() => resetConfigured(resetOnError)}\n onHttpException={(response: HttpExceptionResponse) => {\n if (response.status !== 403 || componentRef === \"\" || retryPhase.current === \"retried\") {\n return undefined;\n }\n\n retryPhase.current = \"renewing\";\n // Retry even when the renewal failed: a genuine 403 then surfaces\n // through the second attempt instead of being swallowed here.\n void refreshRef(componentRef).finally(() => setRenewedSubmitTick((tick) => tick + 1));\n\n return false;\n }}\n >\n {({ clearErrors, errors, processing, reset, touch, validate, validating }) => (\n <FormProvider\n value={{\n action,\n clearErrors: (field) => clearErrors(field),\n componentId: node.id,\n componentRef,\n errors: errors as Record<string, string | undefined>,\n fieldLabels,\n precognitive,\n processing,\n touch: (fields) => touch(...fields),\n validate: (field) => validate(field),\n validateFields: (fields, options) => validate({ only: fields, ...options }),\n validating,\n }}\n >\n <FormResetListener componentId={node.id} reset={reset} />\n\n {props.status && (\n <div className=\"text-center text-sm font-medium text-lt-success\">{props.status}</div>\n )}\n\n <FormBody\n action={action}\n componentRef={componentRef}\n nodes={node.schema}\n shouldRenderSubmitButton={shouldRenderSubmitButton}\n submitButtons={submitButtons}\n submitEmphasis={submitEmphasis}\n submitJustify={submitJustify}\n submitLabel={submitLabel}\n submitVariant={submitVariant}\n summaryLabel={summaryLabel}\n >\n {children}\n </FormBody>\n </FormProvider>\n )}\n </InertiaForm>\n );\n}\n"],"mappings":"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"}
@@ -23,7 +23,7 @@ var WizardComponent = ({ children, node }) => {
23
23
  const stepNames = useMemo(() => nodes.map((step) => stepFieldNames(step)), [nodes]);
24
24
  const isVertical = node.props.orientation === "vertical";
25
25
  const [activeIndex, setActiveIndex] = useState(0);
26
- const [visited, setVisited] = useState(() => new Set([0]));
26
+ const [visited, setVisited] = useState(() => /* @__PURE__ */ new Set([0]));
27
27
  const [completed, setCompleted] = useState(() => /* @__PURE__ */ new Set());
28
28
  const erroredSteps = useMemo(() => stepsWithErrors(stepNames, errors), [stepNames, errors]);
29
29
  const isLast = activeIndex === items.length - 1;
@@ -1 +1 @@
1
- {"version":3,"file":"wizard.js","names":[],"sources":["../../../resources/js/form/components/wizard.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { Node, RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport type { WizardStep } from \"@lattice-php/lattice/types/generated\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { Button } from \"@lattice-php/lattice/ui/button\";\nimport { Spinner } from \"@lattice-php/lattice/ui/spinner\";\nimport { useFormContext } from \"@lattice-php/lattice/form/hooks/context\";\nimport {\n firstErroredStep,\n stepFieldNames,\n stepsWithErrors,\n stepValidationPaths,\n} from \"@lattice-php/lattice/form/lib/wizard-steps\";\n\ntype WizardContextValue = { activeName: string };\n\nconst WizardContext = createContext<WizardContextValue>({ activeName: \"\" });\n\nfunction getSteps(node: Node<\"wizard\">): { items: WizardStep[]; nodes: Node[] } {\n const nodes = (node.schema ?? []).filter((child) => child.type === \"wizard-step\");\n const items = nodes.map((child) => child.props as unknown as WizardStep);\n\n return { items, nodes };\n}\n\nexport const WizardComponent: RendererComponent<\"wizard\"> = ({ children, node }) => {\n const { t } = useT(\"lattice\");\n const { errors, processing, touch, validateFields, validating } = useFormContext();\n const { items, nodes } = useMemo(() => getSteps(node), [node]);\n const stepNames = useMemo(() => nodes.map((step) => stepFieldNames(step)), [nodes]);\n const isVertical = node.props.orientation === \"vertical\";\n\n const [activeIndex, setActiveIndex] = useState(0);\n const [visited, setVisited] = useState<Set<number>>(() => new Set([0]));\n const [completed, setCompleted] = useState<Set<number>>(() => new Set());\n const erroredSteps = useMemo(() => stepsWithErrors(stepNames, errors), [stepNames, errors]);\n const isLast = activeIndex === items.length - 1;\n\n const goTo = (index: number): void => {\n setActiveIndex(index);\n setVisited((previous) => new Set(previous).add(index));\n };\n\n const advance = (): void => {\n setCompleted((previous) => new Set(previous).add(activeIndex));\n\n if (!isLast) {\n goTo(activeIndex + 1);\n }\n };\n\n const onNext = (): void => {\n const step = nodes[activeIndex];\n const paths = step ? stepValidationPaths(step) : [];\n\n if (paths.length === 0) {\n advance();\n\n return;\n }\n\n touch(paths);\n validateFields(paths, { onSuccess: advance });\n };\n\n const wasProcessing = useRef(false);\n useEffect(() => {\n if (wasProcessing.current && !processing) {\n const target = firstErroredStep(stepNames, errors);\n\n if (target !== null && target !== activeIndex) {\n goTo(target);\n }\n }\n\n wasProcessing.current = processing;\n }, [processing, errors, stepNames, activeIndex]);\n\n const contextValue = useMemo(\n () => ({ activeName: items[activeIndex]?.name ?? \"\" }),\n [items, activeIndex],\n );\n\n return (\n <WizardContext.Provider value={contextValue}>\n <div className={cn(\"gap-6\", isVertical ? \"flex\" : \"grid\")} data-slot=\"wizard\">\n <ol\n aria-label={t(\"form.wizard.steps\", \"Steps\")}\n className={cn(\"gap-1\", isVertical ? \"flex w-56 shrink-0 flex-col\" : \"flex flex-wrap\")}\n >\n {items.map((step, index) => {\n const isActive = index === activeIndex;\n const isDone = completed.has(index);\n const hasError = erroredSteps.has(index);\n\n return (\n <li key={step.name}>\n <button\n aria-current={isActive ? \"step\" : undefined}\n className={cn(\n \"flex w-full items-center gap-2 rounded-lt px-3 py-2 text-left text-sm\",\n isActive ? \"bg-lt-muted font-medium text-lt-fg\" : \"text-lt-muted-fg\",\n !visited.has(index) && \"cursor-not-allowed opacity-60\",\n )}\n data-error={hasError || undefined}\n data-test={`wizard-rail-${step.name}`}\n disabled={!visited.has(index)}\n id={`wizard-step-${step.name}-trigger`}\n onClick={() => goTo(index)}\n type=\"button\"\n >\n <span\n className={cn(\n \"flex size-5 shrink-0 items-center justify-center rounded-full border text-xs\",\n hasError\n ? \"border-lt-danger text-lt-danger\"\n : isDone\n ? \"border-lt-primary bg-lt-primary text-lt-primary-fg\"\n : \"border-lt-border\",\n )}\n >\n {isDone && !hasError ? (\n <Icon className=\"size-lt-icon-sm\" name=\"check\" />\n ) : (\n index + 1\n )}\n </span>\n <span className=\"min-w-0\">\n <span className=\"block truncate\">{step.label}</span>\n {isVertical && step.description && (\n <span className=\"block truncate text-xs text-lt-muted-fg\">\n {step.description}\n </span>\n )}\n </span>\n </button>\n </li>\n );\n })}\n </ol>\n\n <div className=\"min-w-0 flex-1 space-y-6\">\n {children}\n\n <div className=\"flex items-center justify-between gap-3\">\n <Button\n data-test=\"wizard-back\"\n disabled={activeIndex === 0 || processing}\n onClick={() => goTo(activeIndex - 1)}\n type=\"button\"\n emphasis=\"outline\"\n >\n {t(\"form.wizard.back\", \"Back\")}\n </Button>\n\n {isLast ? (\n <Button data-test=\"wizard-finish\" disabled={processing} type=\"submit\">\n {processing && <Spinner />}\n {t(\"form.wizard.finish\", \"Finish\")}\n </Button>\n ) : (\n <Button\n data-test=\"wizard-next\"\n disabled={processing || validating}\n onClick={onNext}\n type=\"button\"\n >\n {validating && <Spinner />}\n {t(\"form.wizard.next\", \"Next\")}\n </Button>\n )}\n </div>\n </div>\n </div>\n </WizardContext.Provider>\n );\n};\n\nexport const WizardStepComponent: RendererComponent<\"wizard-step\"> = ({ children, node }) => {\n const { activeName } = useContext(WizardContext);\n const name = node.props.name;\n const isActive = activeName === name;\n const [hasOpened, setHasOpened] = useState(isActive);\n\n useEffect(() => {\n if (isActive) {\n setHasOpened(true);\n }\n }, [isActive]);\n\n return (\n <section\n aria-labelledby={`wizard-step-${name}-trigger`}\n className={cn(\"space-y-8\", !isActive && \"hidden\")}\n hidden={!isActive}\n id={`wizard-step-${name}-panel`}\n >\n {hasOpened ? (children as ReactNode) : null}\n </section>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"wizard.js","names":[],"sources":["../../../resources/js/form/components/wizard.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { Node, RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport type { WizardStep } from \"@lattice-php/lattice/types/generated\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { Button } from \"@lattice-php/lattice/ui/button\";\nimport { Spinner } from \"@lattice-php/lattice/ui/spinner\";\nimport { useFormContext } from \"@lattice-php/lattice/form/hooks/context\";\nimport {\n firstErroredStep,\n stepFieldNames,\n stepsWithErrors,\n stepValidationPaths,\n} from \"@lattice-php/lattice/form/lib/wizard-steps\";\n\ntype WizardContextValue = { activeName: string };\n\nconst WizardContext = createContext<WizardContextValue>({ activeName: \"\" });\n\nfunction getSteps(node: Node<\"wizard\">): { items: WizardStep[]; nodes: Node[] } {\n const nodes = (node.schema ?? []).filter((child) => child.type === \"wizard-step\");\n const items = nodes.map((child) => child.props as unknown as WizardStep);\n\n return { items, nodes };\n}\n\nexport const WizardComponent: RendererComponent<\"wizard\"> = ({ children, node }) => {\n const { t } = useT(\"lattice\");\n const { errors, processing, touch, validateFields, validating } = useFormContext();\n const { items, nodes } = useMemo(() => getSteps(node), [node]);\n const stepNames = useMemo(() => nodes.map((step) => stepFieldNames(step)), [nodes]);\n const isVertical = node.props.orientation === \"vertical\";\n\n const [activeIndex, setActiveIndex] = useState(0);\n const [visited, setVisited] = useState<Set<number>>(() => new Set([0]));\n const [completed, setCompleted] = useState<Set<number>>(() => new Set());\n const erroredSteps = useMemo(() => stepsWithErrors(stepNames, errors), [stepNames, errors]);\n const isLast = activeIndex === items.length - 1;\n\n const goTo = (index: number): void => {\n setActiveIndex(index);\n setVisited((previous) => new Set(previous).add(index));\n };\n\n const advance = (): void => {\n setCompleted((previous) => new Set(previous).add(activeIndex));\n\n if (!isLast) {\n goTo(activeIndex + 1);\n }\n };\n\n const onNext = (): void => {\n const step = nodes[activeIndex];\n const paths = step ? stepValidationPaths(step) : [];\n\n if (paths.length === 0) {\n advance();\n\n return;\n }\n\n touch(paths);\n validateFields(paths, { onSuccess: advance });\n };\n\n const wasProcessing = useRef(false);\n useEffect(() => {\n if (wasProcessing.current && !processing) {\n const target = firstErroredStep(stepNames, errors);\n\n if (target !== null && target !== activeIndex) {\n goTo(target);\n }\n }\n\n wasProcessing.current = processing;\n }, [processing, errors, stepNames, activeIndex]);\n\n const contextValue = useMemo(\n () => ({ activeName: items[activeIndex]?.name ?? \"\" }),\n [items, activeIndex],\n );\n\n return (\n <WizardContext.Provider value={contextValue}>\n <div className={cn(\"gap-6\", isVertical ? \"flex\" : \"grid\")} data-slot=\"wizard\">\n <ol\n aria-label={t(\"form.wizard.steps\", \"Steps\")}\n className={cn(\"gap-1\", isVertical ? \"flex w-56 shrink-0 flex-col\" : \"flex flex-wrap\")}\n >\n {items.map((step, index) => {\n const isActive = index === activeIndex;\n const isDone = completed.has(index);\n const hasError = erroredSteps.has(index);\n\n return (\n <li key={step.name}>\n <button\n aria-current={isActive ? \"step\" : undefined}\n className={cn(\n \"flex w-full items-center gap-2 rounded-lt px-3 py-2 text-left text-sm\",\n isActive ? \"bg-lt-muted font-medium text-lt-fg\" : \"text-lt-muted-fg\",\n !visited.has(index) && \"cursor-not-allowed opacity-60\",\n )}\n data-error={hasError || undefined}\n data-test={`wizard-rail-${step.name}`}\n disabled={!visited.has(index)}\n id={`wizard-step-${step.name}-trigger`}\n onClick={() => goTo(index)}\n type=\"button\"\n >\n <span\n className={cn(\n \"flex size-5 shrink-0 items-center justify-center rounded-full border text-xs\",\n hasError\n ? \"border-lt-danger text-lt-danger\"\n : isDone\n ? \"border-lt-primary bg-lt-primary text-lt-primary-fg\"\n : \"border-lt-border\",\n )}\n >\n {isDone && !hasError ? (\n <Icon className=\"size-lt-icon-sm\" name=\"check\" />\n ) : (\n index + 1\n )}\n </span>\n <span className=\"min-w-0\">\n <span className=\"block truncate\">{step.label}</span>\n {isVertical && step.description && (\n <span className=\"block truncate text-xs text-lt-muted-fg\">\n {step.description}\n </span>\n )}\n </span>\n </button>\n </li>\n );\n })}\n </ol>\n\n <div className=\"min-w-0 flex-1 space-y-6\">\n {children}\n\n <div className=\"flex items-center justify-between gap-3\">\n <Button\n data-test=\"wizard-back\"\n disabled={activeIndex === 0 || processing}\n onClick={() => goTo(activeIndex - 1)}\n type=\"button\"\n emphasis=\"outline\"\n >\n {t(\"form.wizard.back\", \"Back\")}\n </Button>\n\n {isLast ? (\n <Button data-test=\"wizard-finish\" disabled={processing} type=\"submit\">\n {processing && <Spinner />}\n {t(\"form.wizard.finish\", \"Finish\")}\n </Button>\n ) : (\n <Button\n data-test=\"wizard-next\"\n disabled={processing || validating}\n onClick={onNext}\n type=\"button\"\n >\n {validating && <Spinner />}\n {t(\"form.wizard.next\", \"Next\")}\n </Button>\n )}\n </div>\n </div>\n </div>\n </WizardContext.Provider>\n );\n};\n\nexport const WizardStepComponent: RendererComponent<\"wizard-step\"> = ({ children, node }) => {\n const { activeName } = useContext(WizardContext);\n const name = node.props.name;\n const isActive = activeName === name;\n const [hasOpened, setHasOpened] = useState(isActive);\n\n useEffect(() => {\n if (isActive) {\n setHasOpened(true);\n }\n }, [isActive]);\n\n return (\n <section\n aria-labelledby={`wizard-step-${name}-trigger`}\n className={cn(\"space-y-8\", !isActive && \"hidden\")}\n hidden={!isActive}\n id={`wizard-step-${name}-panel`}\n >\n {hasOpened ? (children as ReactNode) : null}\n </section>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolved-nodes.js","names":[],"sources":["../../../resources/js/form/hooks/resolved-nodes.tsx"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport { fieldProps } from \"@lattice-php/lattice/form/lib/field-props\";\nimport { useFieldScope } from \"./field-scope\";\n\nconst ResolvedNodesContext = createContext<Record<string, Node>>({});\n\nexport function ResolvedNodesProvider({\n nodes,\n children,\n}: {\n nodes: Record<string, Node>;\n children: React.ReactNode;\n}) {\n return <ResolvedNodesContext.Provider value={nodes}>{children}</ResolvedNodesContext.Provider>;\n}\n\nexport function useResolvedNode(node: Node): Node {\n const nodes = useContext(ResolvedNodesContext);\n const name = fieldProps(node).name ?? \"\";\n const scope = useFieldScope();\n const path = name && scope ? scope.errorKey(name) : name;\n\n return (path && nodes[path]) || (name && nodes[name]) || node;\n}\n"],"mappings":";;;;;AAKA,IAAM,uBAAuB,cAAoC,CAAC,CAAC;AAEnE,SAAgB,sBAAsB,EACpC,OACA,YAIC;CACD,OAAO,oBAAC,qBAAqB,UAAtB;EAA+B,OAAO;EAAQ;CAAwC,CAAA;AAC/F;AAEA,SAAgB,gBAAgB,MAAkB;CAChD,MAAM,QAAQ,WAAW,oBAAoB;CAC7C,MAAM,OAAO,WAAW,IAAI,EAAE,QAAQ;CACtC,MAAM,QAAQ,cAAc;CAC5B,MAAM,OAAO,QAAQ,QAAQ,MAAM,SAAS,IAAI,IAAI;CAEpD,OAAQ,QAAQ,MAAM,SAAW,QAAQ,MAAM,SAAU;AAC3D"}
1
+ {"version":3,"file":"resolved-nodes.js","names":[],"sources":["../../../resources/js/form/hooks/resolved-nodes.tsx"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport { fieldProps } from \"@lattice-php/lattice/form/lib/field-props\";\nimport { useFieldScope } from \"./field-scope\";\n\nconst ResolvedNodesContext = createContext<Record<string, Node>>({});\n\nexport function ResolvedNodesProvider({\n nodes,\n children,\n}: {\n nodes: Record<string, Node>;\n children: React.ReactNode;\n}) {\n return <ResolvedNodesContext.Provider value={nodes}>{children}</ResolvedNodesContext.Provider>;\n}\n\nexport function useResolvedNode(node: Node): Node {\n const nodes = useContext(ResolvedNodesContext);\n const name = fieldProps(node).name ?? \"\";\n const scope = useFieldScope();\n const path = name && scope ? scope.errorKey(name) : name;\n\n return (path && nodes[path]) || (name && nodes[name]) || node;\n}\n"],"mappings":";;;;;AAKA,IAAM,uBAAuB,cAAoC,CAAC,CAAC;AAEnE,SAAgB,sBAAsB,EACpC,OACA,YAIC;CACD,OAAO,oBAAC,qBAAqB,UAAtB;EAA+B,OAAO;EAAQ;CAAwC,CAAA;AAC/F;AAEA,SAAgB,gBAAgB,MAAkB;CAChD,MAAM,QAAQ,WAAW,oBAAoB;CAC7C,MAAM,OAAO,WAAW,IAAI,CAAC,CAAC,QAAQ;CACtC,MAAM,QAAQ,cAAc;CAC5B,MAAM,OAAO,QAAQ,QAAQ,MAAM,SAAS,IAAI,IAAI;CAEpD,OAAQ,QAAQ,MAAM,SAAW,QAAQ,MAAM,SAAU;AAC3D"}
@@ -7,7 +7,8 @@ import { useMemo } from "react";
7
7
  function useDependentField(node) {
8
8
  const scope = useFieldScope();
9
9
  const props = fieldProps(node);
10
- const values = useFormValuesFor(useMemo(() => conditionFields(props.conditions), [props.conditions]));
10
+ const fields = useMemo(() => conditionFields(props.conditions), [props.conditions]);
11
+ const values = useFormValuesFor(fields);
11
12
  const conditionValues = scope ? {
12
13
  ...values,
13
14
  ...scope.values
@@ -1 +1 @@
1
- {"version":3,"file":"use-dependent-field.js","names":[],"sources":["../../../resources/js/form/hooks/use-dependent-field.ts"],"sourcesContent":["import type { Node } from \"@lattice-php/lattice/core/types\";\nimport { useMemo } from \"react\";\nimport {\n conditionFields,\n type FieldState,\n evaluateConditions,\n} from \"@lattice-php/lattice/form/lib/conditions\";\nimport { fieldProps } from \"@lattice-php/lattice/form/lib/field-props\";\nimport { useFieldScope } from \"./field-scope\";\nimport { useFormValuesFor } from \"./values\";\n\nexport function useDependentField(node: Node): FieldState {\n const scope = useFieldScope();\n const props = fieldProps(node);\n const fields = useMemo(() => conditionFields(props.conditions), [props.conditions]);\n const values = useFormValuesFor(fields);\n // Bare condition names resolve to row siblings first; form values remain the fallback.\n const conditionValues = scope ? { ...values, ...scope.values } : values;\n\n return evaluateConditions(props.conditions ?? undefined, conditionValues, {\n required: props.required ?? false,\n readOnly: props.readOnly ?? false,\n disabled: props.disabled ?? false,\n });\n}\n"],"mappings":";;;;;;AAWA,SAAgB,kBAAkB,MAAwB;CACxD,MAAM,QAAQ,cAAc;CAC5B,MAAM,QAAQ,WAAW,IAAI;CAE7B,MAAM,SAAS,iBADA,cAAc,gBAAgB,MAAM,UAAU,GAAG,CAAC,MAAM,UAAU,CACjD,CAAM;CAEtC,MAAM,kBAAkB,QAAQ;EAAE,GAAG;EAAQ,GAAG,MAAM;CAAO,IAAI;CAEjE,OAAO,mBAAmB,MAAM,cAAc,KAAA,GAAW,iBAAiB;EACxE,UAAU,MAAM,YAAY;EAC5B,UAAU,MAAM,YAAY;EAC5B,UAAU,MAAM,YAAY;CAC9B,CAAC;AACH"}
1
+ {"version":3,"file":"use-dependent-field.js","names":[],"sources":["../../../resources/js/form/hooks/use-dependent-field.ts"],"sourcesContent":["import type { Node } from \"@lattice-php/lattice/core/types\";\nimport { useMemo } from \"react\";\nimport {\n conditionFields,\n type FieldState,\n evaluateConditions,\n} from \"@lattice-php/lattice/form/lib/conditions\";\nimport { fieldProps } from \"@lattice-php/lattice/form/lib/field-props\";\nimport { useFieldScope } from \"./field-scope\";\nimport { useFormValuesFor } from \"./values\";\n\nexport function useDependentField(node: Node): FieldState {\n const scope = useFieldScope();\n const props = fieldProps(node);\n const fields = useMemo(() => conditionFields(props.conditions), [props.conditions]);\n const values = useFormValuesFor(fields);\n // Bare condition names resolve to row siblings first; form values remain the fallback.\n const conditionValues = scope ? { ...values, ...scope.values } : values;\n\n return evaluateConditions(props.conditions ?? undefined, conditionValues, {\n required: props.required ?? false,\n readOnly: props.readOnly ?? false,\n disabled: props.disabled ?? false,\n });\n}\n"],"mappings":";;;;;;AAWA,SAAgB,kBAAkB,MAAwB;CACxD,MAAM,QAAQ,cAAc;CAC5B,MAAM,QAAQ,WAAW,IAAI;CAC7B,MAAM,SAAS,cAAc,gBAAgB,MAAM,UAAU,GAAG,CAAC,MAAM,UAAU,CAAC;CAClF,MAAM,SAAS,iBAAiB,MAAM;CAEtC,MAAM,kBAAkB,QAAQ;EAAE,GAAG;EAAQ,GAAG,MAAM;CAAO,IAAI;CAEjE,OAAO,mBAAmB,MAAM,cAAc,KAAA,GAAW,iBAAiB;EACxE,UAAU,MAAM,YAAY;EAC5B,UAAU,MAAM,YAAY;EAC5B,UAAU,MAAM,YAAY;CAC9B,CAAC;AACH"}
@@ -46,6 +46,7 @@ function useFormResolver(action, componentRef, nodes) {
46
46
  }
47
47
  return [...set];
48
48
  }, [watch.keys, targets]);
49
+ const watchSignature = watch.any ? values : JSON.stringify(watchPaths.map((path) => getPath(values, path)));
49
50
  useEffect(() => {
50
51
  if (watchPaths.length === 0 && !watch.any) return;
51
52
  const previous = previousValues.current;
@@ -80,7 +81,7 @@ function useFormResolver(action, componentRef, nodes) {
80
81
  controller.abort();
81
82
  };
82
83
  }, [
83
- watch.any ? values : JSON.stringify(watchPaths.map((path) => getPath(values, path))),
84
+ watchSignature,
84
85
  action,
85
86
  componentRef,
86
87
  watch.any,
@@ -1 +1 @@
1
- {"version":3,"file":"use-form-resolver.js","names":[],"sources":["../../../resources/js/form/hooks/use-form-resolver.ts"],"sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport type { ResolveResponse } from \"@lattice-php/lattice/types/generated\";\nimport { walkFields } from \"@lattice-php/lattice/form/lib/field-props\";\nimport { FORM_DEBOUNCE_MS, postFormAction } from \"@lattice-php/lattice/form/lib/form-transport\";\nimport {\n collectPrefillTargets,\n getPath,\n pathsToClear,\n pruneOverrides,\n seededOverrides,\n} from \"@lattice-php/lattice/form/lib/prefill-targets\";\nimport type { PrefillController } from \"./prefill-context\";\nimport { useFormValues, useSetFormValue } from \"./values\";\n\ntype FormResolver = {\n nodes: Record<string, Node>;\n markUserEdit: PrefillController[\"markUserEdit\"];\n};\n\nexport function useFormResolver(\n action: string,\n componentRef: string,\n nodes: Node[] | undefined,\n): FormResolver {\n const values = useFormValues();\n const setValue = useSetFormValue();\n const [resolved, setResolved] = useState<Record<string, Node>>({});\n\n const targets = useMemo(() => collectPrefillTargets(nodes, values), [nodes, values]);\n\n const overrides = useRef<Set<string>>(new Set());\n const seededOverrideKeys = useRef<Set<string>>(new Set());\n const previousValues = useRef<Record<string, unknown>>(values);\n const previousTargets = useRef(targets);\n\n // Read targets via a ref inside the effect: `targets` changes identity on every\n // value change, but the effect must fire only when `watchSignature` changes\n // (which already reflects added/removed target paths). Keeping `targets` out of\n // the dep array preserves the targeted watch in non-\"any\" mode.\n const targetsRef = useRef(targets);\n targetsRef.current = targets;\n\n useLayoutEffect(() => {\n const liveOverrideKeys = new Set(targets.map((target) => target.overrideKey));\n seededOverrideKeys.current = new Set(\n [...seededOverrideKeys.current].filter((overrideKey) => liveOverrideKeys.has(overrideKey)),\n );\n const freshTargets = targets.filter(\n (target) => !seededOverrideKeys.current.has(target.overrideKey),\n );\n\n // Layout ordering seeds loaded values before the first resolve effect can apply prefill.\n for (const overrideKey of seededOverrides(freshTargets, values)) {\n overrides.current.add(overrideKey);\n }\n\n for (const target of freshTargets) {\n seededOverrideKeys.current.add(target.overrideKey);\n }\n }, [targets, values]);\n\n const markUserEdit = useCallback((overrideKey: string) => {\n overrides.current.add(overrideKey);\n }, []);\n\n const watch = useMemo(() => {\n const keys = new Set<string>();\n let any = false;\n walkFields(nodes, (props) => {\n if (Array.isArray(props.dependsOnKeys)) {\n for (const key of props.dependsOnKeys) {\n keys.add(String(key));\n }\n }\n if (props.dependsOnAny) {\n any = true;\n }\n });\n return { keys: [...keys], any };\n }, [nodes]);\n\n const watchPaths = useMemo(() => {\n const set = new Set<string>(watch.keys);\n for (const target of targets) {\n for (const dep of target.resetOn) {\n set.add(dep);\n }\n for (const dep of target.refreshOn) {\n set.add(dep);\n }\n }\n return [...set];\n }, [watch.keys, targets]);\n\n // In \"any\" mode the values store keeps a stable reference until something\n // actually changes, so its identity is the change signal. Otherwise hash the\n // watched paths (form-level and per-row).\n const watchSignature = watch.any\n ? values\n : JSON.stringify(watchPaths.map((path) => getPath(values, path)));\n\n useEffect(() => {\n if (watchPaths.length === 0 && !watch.any) {\n return;\n }\n\n const previous = previousValues.current;\n previousValues.current = values;\n\n // `resetOn` deps unlock a path (a fresh product gives a fresh price); `refreshOn`\n // deps only re-ask the server and never clear an override (a customer change\n // re-prices untouched rows but leaves user-edited ones alone).\n for (const overrideKey of pathsToClear(\n { targets: previousTargets.current, values: previous },\n { targets: targetsRef.current, values },\n )) {\n overrides.current.delete(overrideKey);\n }\n previousTargets.current = targetsRef.current;\n\n // Override ownership is row-id keyed, so pruning removes departed rows without reindex drift.\n overrides.current = pruneOverrides(overrides.current, targetsRef.current);\n\n const controller = new AbortController();\n\n const timer = window.setTimeout(() => {\n void postFormAction<ResolveResponse>(\n action,\n componentRef,\n { _sub: \"resolve\", ...values },\n controller.signal,\n )\n .then((response) => {\n if (!response) {\n return;\n }\n for (const [name, value] of Object.entries(response.values ?? {})) {\n setValue(name, value);\n }\n const targetsByPath = new Map(\n targetsRef.current.map((target) => [target.path, target] as const),\n );\n for (const [path, value] of Object.entries(response.prefill ?? {})) {\n const target = targetsByPath.get(path);\n\n if (target && !overrides.current.has(target.overrideKey)) {\n setValue(path, value);\n }\n }\n if (response.fields) {\n setResolved(response.fields);\n }\n })\n .catch(() => {});\n }, FORM_DEBOUNCE_MS);\n\n return () => {\n window.clearTimeout(timer);\n controller.abort();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [watchSignature, action, componentRef, watch.any, setValue]);\n\n return { nodes: resolved, markUserEdit };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-form-resolver.js","names":[],"sources":["../../../resources/js/form/hooks/use-form-resolver.ts"],"sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport type { ResolveResponse } from \"@lattice-php/lattice/types/generated\";\nimport { walkFields } from \"@lattice-php/lattice/form/lib/field-props\";\nimport { FORM_DEBOUNCE_MS, postFormAction } from \"@lattice-php/lattice/form/lib/form-transport\";\nimport {\n collectPrefillTargets,\n getPath,\n pathsToClear,\n pruneOverrides,\n seededOverrides,\n} from \"@lattice-php/lattice/form/lib/prefill-targets\";\nimport type { PrefillController } from \"./prefill-context\";\nimport { useFormValues, useSetFormValue } from \"./values\";\n\ntype FormResolver = {\n nodes: Record<string, Node>;\n markUserEdit: PrefillController[\"markUserEdit\"];\n};\n\nexport function useFormResolver(\n action: string,\n componentRef: string,\n nodes: Node[] | undefined,\n): FormResolver {\n const values = useFormValues();\n const setValue = useSetFormValue();\n const [resolved, setResolved] = useState<Record<string, Node>>({});\n\n const targets = useMemo(() => collectPrefillTargets(nodes, values), [nodes, values]);\n\n const overrides = useRef<Set<string>>(new Set());\n const seededOverrideKeys = useRef<Set<string>>(new Set());\n const previousValues = useRef<Record<string, unknown>>(values);\n const previousTargets = useRef(targets);\n\n // Read targets via a ref inside the effect: `targets` changes identity on every\n // value change, but the effect must fire only when `watchSignature` changes\n // (which already reflects added/removed target paths). Keeping `targets` out of\n // the dep array preserves the targeted watch in non-\"any\" mode.\n const targetsRef = useRef(targets);\n targetsRef.current = targets;\n\n useLayoutEffect(() => {\n const liveOverrideKeys = new Set(targets.map((target) => target.overrideKey));\n seededOverrideKeys.current = new Set(\n [...seededOverrideKeys.current].filter((overrideKey) => liveOverrideKeys.has(overrideKey)),\n );\n const freshTargets = targets.filter(\n (target) => !seededOverrideKeys.current.has(target.overrideKey),\n );\n\n // Layout ordering seeds loaded values before the first resolve effect can apply prefill.\n for (const overrideKey of seededOverrides(freshTargets, values)) {\n overrides.current.add(overrideKey);\n }\n\n for (const target of freshTargets) {\n seededOverrideKeys.current.add(target.overrideKey);\n }\n }, [targets, values]);\n\n const markUserEdit = useCallback((overrideKey: string) => {\n overrides.current.add(overrideKey);\n }, []);\n\n const watch = useMemo(() => {\n const keys = new Set<string>();\n let any = false;\n walkFields(nodes, (props) => {\n if (Array.isArray(props.dependsOnKeys)) {\n for (const key of props.dependsOnKeys) {\n keys.add(String(key));\n }\n }\n if (props.dependsOnAny) {\n any = true;\n }\n });\n return { keys: [...keys], any };\n }, [nodes]);\n\n const watchPaths = useMemo(() => {\n const set = new Set<string>(watch.keys);\n for (const target of targets) {\n for (const dep of target.resetOn) {\n set.add(dep);\n }\n for (const dep of target.refreshOn) {\n set.add(dep);\n }\n }\n return [...set];\n }, [watch.keys, targets]);\n\n // In \"any\" mode the values store keeps a stable reference until something\n // actually changes, so its identity is the change signal. Otherwise hash the\n // watched paths (form-level and per-row).\n const watchSignature = watch.any\n ? values\n : JSON.stringify(watchPaths.map((path) => getPath(values, path)));\n\n useEffect(() => {\n if (watchPaths.length === 0 && !watch.any) {\n return;\n }\n\n const previous = previousValues.current;\n previousValues.current = values;\n\n // `resetOn` deps unlock a path (a fresh product gives a fresh price); `refreshOn`\n // deps only re-ask the server and never clear an override (a customer change\n // re-prices untouched rows but leaves user-edited ones alone).\n for (const overrideKey of pathsToClear(\n { targets: previousTargets.current, values: previous },\n { targets: targetsRef.current, values },\n )) {\n overrides.current.delete(overrideKey);\n }\n previousTargets.current = targetsRef.current;\n\n // Override ownership is row-id keyed, so pruning removes departed rows without reindex drift.\n overrides.current = pruneOverrides(overrides.current, targetsRef.current);\n\n const controller = new AbortController();\n\n const timer = window.setTimeout(() => {\n void postFormAction<ResolveResponse>(\n action,\n componentRef,\n { _sub: \"resolve\", ...values },\n controller.signal,\n )\n .then((response) => {\n if (!response) {\n return;\n }\n for (const [name, value] of Object.entries(response.values ?? {})) {\n setValue(name, value);\n }\n const targetsByPath = new Map(\n targetsRef.current.map((target) => [target.path, target] as const),\n );\n for (const [path, value] of Object.entries(response.prefill ?? {})) {\n const target = targetsByPath.get(path);\n\n if (target && !overrides.current.has(target.overrideKey)) {\n setValue(path, value);\n }\n }\n if (response.fields) {\n setResolved(response.fields);\n }\n })\n .catch(() => {});\n }, FORM_DEBOUNCE_MS);\n\n return () => {\n window.clearTimeout(timer);\n controller.abort();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [watchSignature, action, componentRef, watch.any, setValue]);\n\n return { nodes: resolved, markUserEdit };\n}\n"],"mappings":"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"}
@@ -77,7 +77,8 @@ function createFormValuesStore(initial) {
77
77
  updateValues(initialValues, "");
78
78
  return;
79
79
  }
80
- updateValues(fields.reduce((accumulated, field) => setPath(accumulated, field, getPath(initialValues, field)), values), "");
80
+ const next = fields.reduce((accumulated, field) => setPath(accumulated, field, getPath(initialValues, field)), values);
81
+ updateValues(next, "");
81
82
  },
82
83
  setValue: (name, value) => {
83
84
  const previous = getPath(values, name);
@@ -1 +1 @@
1
- {"version":3,"file":"values.js","names":[],"sources":["../../../resources/js/form/hooks/values.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport { getPath, setPath } from \"@lattice-php/lattice/form/lib/form-path\";\n\nexport type SetFormValue = (\n name: string,\n value: unknown | ((previous: unknown) => unknown),\n) => void;\n\nexport type ResetFormValues = (fields?: string[]) => void;\n\ntype FormValuesStore = {\n getPathSnapshot: (path: string) => unknown;\n getPathsSnapshot: (paths: string[]) => Record<string, unknown>;\n getSnapshot: () => Record<string, unknown>;\n replaceInitial: (initial: Record<string, unknown>) => void;\n reset: ResetFormValues;\n setValue: SetFormValue;\n subscribe: (listener: () => void) => () => void;\n subscribePath: (path: string, listener: () => void) => () => void;\n subscribePaths: (paths: string[], listener: () => void) => () => void;\n};\n\nconst emptyValues: Record<string, unknown> = {};\nconst emptySelectedValues: Record<string, unknown> = {};\n\nconst FormValuesStoreContext = createContext<FormValuesStore | null>(null);\nconst SetFormValueContext = createContext<SetFormValue>(() => {});\n\nfunction valuesEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) {\n return true;\n }\n\n if (!a || !b || typeof a !== \"object\" || typeof b !== \"object\") {\n return false;\n }\n\n if (Array.isArray(a) || Array.isArray(b)) {\n return (\n Array.isArray(a) &&\n Array.isArray(b) &&\n a.length === b.length &&\n a.every((value, index) => valuesEqual(value, b[index]))\n );\n }\n\n const aEntries = Object.entries(a);\n const bObject = b as Record<string, unknown>;\n\n return (\n aEntries.length === Object.keys(bObject).length &&\n aEntries.every(([key, value]) => valuesEqual(value, bObject[key]))\n );\n}\n\nfunction normalizePath(path: string): string {\n return path\n .split(\".\")\n .filter((part) => part !== \"\")\n .join(\".\");\n}\n\nfunction pathsOverlap(left: string, right: string): boolean {\n return (\n left === \"\" ||\n right === \"\" ||\n left === right ||\n left.startsWith(`${right}.`) ||\n right.startsWith(`${left}.`)\n );\n}\n\nfunction createFormValuesStore(initial: Record<string, unknown>): FormValuesStore {\n let initialValues = initial;\n let values = initial;\n const listeners = new Set<() => void>();\n const pathListeners = new Map<string, Set<() => void>>();\n const selectedCache = new Map<string, { snapshot: Record<string, unknown>; values: unknown[] }>();\n\n const notify = (path: string): void => {\n for (const listener of listeners) {\n listener();\n }\n\n const normalizedPath = normalizePath(path);\n\n for (const [subscribedPath, subscribedListeners] of pathListeners) {\n if (!pathsOverlap(subscribedPath, normalizedPath)) {\n continue;\n }\n\n for (const listener of subscribedListeners) {\n listener();\n }\n }\n };\n\n const updateValues = (next: Record<string, unknown>, path: string): void => {\n if (Object.is(values, next)) {\n return;\n }\n\n values = next;\n selectedCache.clear();\n notify(path);\n };\n\n const subscribePath = (path: string, listener: () => void): (() => void) => {\n const normalizedPath = normalizePath(path);\n const listenersForPath = pathListeners.get(normalizedPath) ?? new Set<() => void>();\n\n listenersForPath.add(listener);\n pathListeners.set(normalizedPath, listenersForPath);\n\n return () => {\n listenersForPath.delete(listener);\n\n if (listenersForPath.size === 0) {\n pathListeners.delete(normalizedPath);\n }\n };\n };\n\n return {\n getPathSnapshot: (path: string): unknown => getPath(values, path),\n getPathsSnapshot: (paths: string[]): Record<string, unknown> => {\n if (paths.length === 0) {\n return emptySelectedValues;\n }\n\n const normalizedPaths = paths.map(normalizePath);\n const key = JSON.stringify(paths);\n const selectedValues = normalizedPaths.map((path) => getPath(values, path));\n const cached = selectedCache.get(key);\n\n if (\n cached &&\n cached.values.length === selectedValues.length &&\n selectedValues.every((value, index) => Object.is(value, cached.values[index]))\n ) {\n return cached.snapshot;\n }\n\n const snapshot = Object.fromEntries(\n paths.map((path, index) => [path, selectedValues[index]]),\n );\n\n selectedCache.set(key, { snapshot, values: selectedValues });\n\n return snapshot;\n },\n getSnapshot: () => values,\n replaceInitial: (nextInitial: Record<string, unknown>): void => {\n if (valuesEqual(initialValues, nextInitial)) {\n return;\n }\n\n initialValues = nextInitial;\n updateValues(nextInitial, \"\");\n },\n reset: (fields?: string[]): void => {\n if (fields === undefined) {\n updateValues(initialValues, \"\");\n\n return;\n }\n\n const next = fields.reduce(\n (accumulated, field) => setPath(accumulated, field, getPath(initialValues, field)),\n values,\n );\n\n updateValues(next, \"\");\n },\n setValue: (name: string, value: unknown | ((previous: unknown) => unknown)): void => {\n const previous = getPath(values, name);\n const next = typeof value === \"function\" ? value(previous) : value;\n\n if (Object.is(previous, next)) {\n return;\n }\n\n updateValues(setPath(values, name, next), name);\n },\n subscribe: (listener: () => void): (() => void) => {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n subscribePath,\n subscribePaths: (paths: string[], listener: () => void): (() => void) => {\n const unsubscribers = Array.from(new Set(paths.map(normalizePath))).map((path) =>\n subscribePath(path, listener),\n );\n\n return () => {\n for (const unsubscribe of unsubscribers) {\n unsubscribe();\n }\n };\n },\n };\n}\n\nconst fallbackFormValuesStore = createFormValuesStore(emptyValues);\n\nexport function FormValuesProvider({\n initial,\n children,\n}: {\n initial: Record<string, unknown>;\n children: React.ReactNode;\n}) {\n const storeRef = useRef<FormValuesStore>(null);\n\n if (storeRef.current === null) {\n storeRef.current = createFormValuesStore(initial);\n }\n\n useEffect(() => {\n storeRef.current?.replaceInitial(initial);\n }, [initial]);\n\n return (\n <FormValuesStoreContext.Provider value={storeRef.current}>\n <SetFormValueContext.Provider value={storeRef.current.setValue}>\n {children}\n </SetFormValueContext.Provider>\n </FormValuesStoreContext.Provider>\n );\n}\n\nfunction useFormValuesStore(): FormValuesStore {\n return useContext(FormValuesStoreContext) ?? fallbackFormValuesStore;\n}\n\nexport function useFormValues(): Record<string, unknown> {\n const store = useFormValuesStore();\n\n return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n}\n\nexport function useFormValue(name: string): unknown {\n const store = useFormValuesStore();\n const subscribe = useCallback(\n (listener: () => void) => store.subscribePath(name, listener),\n [name, store],\n );\n const getSnapshot = useCallback(() => store.getPathSnapshot(name), [name, store]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nexport function useFormValuesFor(paths: string[]): Record<string, unknown> {\n const store = useFormValuesStore();\n const pathsKey = JSON.stringify(paths);\n const selectedPaths = useMemo(() => JSON.parse(pathsKey) as string[], [pathsKey]);\n const subscribe = useCallback(\n (listener: () => void) => store.subscribePaths(selectedPaths, listener),\n [selectedPaths, store],\n );\n const getSnapshot = useCallback(\n () => store.getPathsSnapshot(selectedPaths),\n [selectedPaths, store],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nexport function useSetFormValue(): SetFormValue {\n return useContext(SetFormValueContext);\n}\n\n/**\n * Reset values to the store's initial snapshot — all of them, or just the\n * given field paths. This is the reset that actually clears controlled\n * fields; Inertia's own form reset only touches its internal data and the\n * DOM, which store-driven inputs ignore.\n */\nexport function useResetFormValues(): ResetFormValues {\n const store = useFormValuesStore();\n\n return useCallback((fields?: string[]) => store.reset(fields), [store]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"values.js","names":[],"sources":["../../../resources/js/form/hooks/values.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport { getPath, setPath } from \"@lattice-php/lattice/form/lib/form-path\";\n\nexport type SetFormValue = (\n name: string,\n value: unknown | ((previous: unknown) => unknown),\n) => void;\n\nexport type ResetFormValues = (fields?: string[]) => void;\n\ntype FormValuesStore = {\n getPathSnapshot: (path: string) => unknown;\n getPathsSnapshot: (paths: string[]) => Record<string, unknown>;\n getSnapshot: () => Record<string, unknown>;\n replaceInitial: (initial: Record<string, unknown>) => void;\n reset: ResetFormValues;\n setValue: SetFormValue;\n subscribe: (listener: () => void) => () => void;\n subscribePath: (path: string, listener: () => void) => () => void;\n subscribePaths: (paths: string[], listener: () => void) => () => void;\n};\n\nconst emptyValues: Record<string, unknown> = {};\nconst emptySelectedValues: Record<string, unknown> = {};\n\nconst FormValuesStoreContext = createContext<FormValuesStore | null>(null);\nconst SetFormValueContext = createContext<SetFormValue>(() => {});\n\nfunction valuesEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) {\n return true;\n }\n\n if (!a || !b || typeof a !== \"object\" || typeof b !== \"object\") {\n return false;\n }\n\n if (Array.isArray(a) || Array.isArray(b)) {\n return (\n Array.isArray(a) &&\n Array.isArray(b) &&\n a.length === b.length &&\n a.every((value, index) => valuesEqual(value, b[index]))\n );\n }\n\n const aEntries = Object.entries(a);\n const bObject = b as Record<string, unknown>;\n\n return (\n aEntries.length === Object.keys(bObject).length &&\n aEntries.every(([key, value]) => valuesEqual(value, bObject[key]))\n );\n}\n\nfunction normalizePath(path: string): string {\n return path\n .split(\".\")\n .filter((part) => part !== \"\")\n .join(\".\");\n}\n\nfunction pathsOverlap(left: string, right: string): boolean {\n return (\n left === \"\" ||\n right === \"\" ||\n left === right ||\n left.startsWith(`${right}.`) ||\n right.startsWith(`${left}.`)\n );\n}\n\nfunction createFormValuesStore(initial: Record<string, unknown>): FormValuesStore {\n let initialValues = initial;\n let values = initial;\n const listeners = new Set<() => void>();\n const pathListeners = new Map<string, Set<() => void>>();\n const selectedCache = new Map<string, { snapshot: Record<string, unknown>; values: unknown[] }>();\n\n const notify = (path: string): void => {\n for (const listener of listeners) {\n listener();\n }\n\n const normalizedPath = normalizePath(path);\n\n for (const [subscribedPath, subscribedListeners] of pathListeners) {\n if (!pathsOverlap(subscribedPath, normalizedPath)) {\n continue;\n }\n\n for (const listener of subscribedListeners) {\n listener();\n }\n }\n };\n\n const updateValues = (next: Record<string, unknown>, path: string): void => {\n if (Object.is(values, next)) {\n return;\n }\n\n values = next;\n selectedCache.clear();\n notify(path);\n };\n\n const subscribePath = (path: string, listener: () => void): (() => void) => {\n const normalizedPath = normalizePath(path);\n const listenersForPath = pathListeners.get(normalizedPath) ?? new Set<() => void>();\n\n listenersForPath.add(listener);\n pathListeners.set(normalizedPath, listenersForPath);\n\n return () => {\n listenersForPath.delete(listener);\n\n if (listenersForPath.size === 0) {\n pathListeners.delete(normalizedPath);\n }\n };\n };\n\n return {\n getPathSnapshot: (path: string): unknown => getPath(values, path),\n getPathsSnapshot: (paths: string[]): Record<string, unknown> => {\n if (paths.length === 0) {\n return emptySelectedValues;\n }\n\n const normalizedPaths = paths.map(normalizePath);\n const key = JSON.stringify(paths);\n const selectedValues = normalizedPaths.map((path) => getPath(values, path));\n const cached = selectedCache.get(key);\n\n if (\n cached &&\n cached.values.length === selectedValues.length &&\n selectedValues.every((value, index) => Object.is(value, cached.values[index]))\n ) {\n return cached.snapshot;\n }\n\n const snapshot = Object.fromEntries(\n paths.map((path, index) => [path, selectedValues[index]]),\n );\n\n selectedCache.set(key, { snapshot, values: selectedValues });\n\n return snapshot;\n },\n getSnapshot: () => values,\n replaceInitial: (nextInitial: Record<string, unknown>): void => {\n if (valuesEqual(initialValues, nextInitial)) {\n return;\n }\n\n initialValues = nextInitial;\n updateValues(nextInitial, \"\");\n },\n reset: (fields?: string[]): void => {\n if (fields === undefined) {\n updateValues(initialValues, \"\");\n\n return;\n }\n\n const next = fields.reduce(\n (accumulated, field) => setPath(accumulated, field, getPath(initialValues, field)),\n values,\n );\n\n updateValues(next, \"\");\n },\n setValue: (name: string, value: unknown | ((previous: unknown) => unknown)): void => {\n const previous = getPath(values, name);\n const next = typeof value === \"function\" ? value(previous) : value;\n\n if (Object.is(previous, next)) {\n return;\n }\n\n updateValues(setPath(values, name, next), name);\n },\n subscribe: (listener: () => void): (() => void) => {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n subscribePath,\n subscribePaths: (paths: string[], listener: () => void): (() => void) => {\n const unsubscribers = Array.from(new Set(paths.map(normalizePath))).map((path) =>\n subscribePath(path, listener),\n );\n\n return () => {\n for (const unsubscribe of unsubscribers) {\n unsubscribe();\n }\n };\n },\n };\n}\n\nconst fallbackFormValuesStore = createFormValuesStore(emptyValues);\n\nexport function FormValuesProvider({\n initial,\n children,\n}: {\n initial: Record<string, unknown>;\n children: React.ReactNode;\n}) {\n const storeRef = useRef<FormValuesStore>(null);\n\n if (storeRef.current === null) {\n storeRef.current = createFormValuesStore(initial);\n }\n\n useEffect(() => {\n storeRef.current?.replaceInitial(initial);\n }, [initial]);\n\n return (\n <FormValuesStoreContext.Provider value={storeRef.current}>\n <SetFormValueContext.Provider value={storeRef.current.setValue}>\n {children}\n </SetFormValueContext.Provider>\n </FormValuesStoreContext.Provider>\n );\n}\n\nfunction useFormValuesStore(): FormValuesStore {\n return useContext(FormValuesStoreContext) ?? fallbackFormValuesStore;\n}\n\nexport function useFormValues(): Record<string, unknown> {\n const store = useFormValuesStore();\n\n return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n}\n\nexport function useFormValue(name: string): unknown {\n const store = useFormValuesStore();\n const subscribe = useCallback(\n (listener: () => void) => store.subscribePath(name, listener),\n [name, store],\n );\n const getSnapshot = useCallback(() => store.getPathSnapshot(name), [name, store]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nexport function useFormValuesFor(paths: string[]): Record<string, unknown> {\n const store = useFormValuesStore();\n const pathsKey = JSON.stringify(paths);\n const selectedPaths = useMemo(() => JSON.parse(pathsKey) as string[], [pathsKey]);\n const subscribe = useCallback(\n (listener: () => void) => store.subscribePaths(selectedPaths, listener),\n [selectedPaths, store],\n );\n const getSnapshot = useCallback(\n () => store.getPathsSnapshot(selectedPaths),\n [selectedPaths, store],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nexport function useSetFormValue(): SetFormValue {\n return useContext(SetFormValueContext);\n}\n\n/**\n * Reset values to the store's initial snapshot — all of them, or just the\n * given field paths. This is the reset that actually clears controlled\n * fields; Inertia's own form reset only touches its internal data and the\n * DOM, which store-driven inputs ignore.\n */\nexport function useResetFormValues(): ResetFormValues {\n const store = useFormValuesStore();\n\n return useCallback((fields?: string[]) => store.reset(fields), [store]);\n}\n"],"mappings":"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"}