@lattice-php/lattice 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action/components/action-form.js +1 -1
- package/dist/action/use-action.js +1 -1
- package/dist/chat/types.d.ts +2 -10
- package/dist/core/components/button.js +34 -18
- package/dist/core/components/button.js.map +1 -1
- package/dist/core/components/chart.js +49 -2
- package/dist/core/components/chart.js.map +1 -1
- package/dist/core/components/control.d.ts +1 -3
- package/dist/core/components/control.js.map +1 -1
- package/dist/core/components/link.js +25 -29
- package/dist/core/components/link.js.map +1 -1
- package/dist/core/types.d.ts +22 -42
- package/dist/core/use-click-behavior.d.ts +42 -0
- package/dist/core/use-click-behavior.js +46 -0
- package/dist/core/use-click-behavior.js.map +1 -0
- package/dist/create-app.d.ts +8 -2
- package/dist/create-app.js +4 -2
- package/dist/create-app.js.map +1 -1
- package/dist/effects/registry.d.ts +9 -20
- package/dist/effects/registry.js +4 -6
- package/dist/effects/registry.js.map +1 -1
- package/dist/events/event-bridge.d.ts +1 -1
- package/dist/events/event-bridge.js +1 -1
- package/dist/events/event-bridge.js.map +1 -1
- package/dist/form/components/base/input-otp.d.ts +2 -1
- package/dist/form/components/base/input-otp.js.map +1 -1
- package/dist/form/components/conditions.d.ts +1 -1
- package/dist/form/components/conditions.js.map +1 -1
- package/dist/form/components/field-props.d.ts +2 -1
- package/dist/form/components/field-props.js.map +1 -1
- package/dist/form/components/field-scope.d.ts +2 -1
- package/dist/form/components/field-scope.js.map +1 -1
- package/dist/form/components/fields/date-picker-control.js +11 -13
- package/dist/form/components/fields/date-picker-control.js.map +1 -1
- package/dist/form/components/fields/row-action-menu.d.ts +2 -1
- package/dist/form/components/fields/row-action-menu.js.map +1 -1
- package/dist/form/components/fields/row-item.d.ts +2 -1
- package/dist/form/components/fields/row-item.js.map +1 -1
- package/dist/form/components/fields/table-rows.d.ts +2 -1
- package/dist/form/components/fields/table-rows.js.map +1 -1
- package/dist/form/components/fields/time-input.js +54 -15
- package/dist/form/components/fields/time-input.js.map +1 -1
- package/dist/form/components/fields/time-picker-columns.d.ts +24 -0
- package/dist/form/components/fields/time-picker-columns.js +76 -0
- package/dist/form/components/fields/time-picker-columns.js.map +1 -0
- package/dist/form/components/fields/time-picker.d.ts +18 -0
- package/dist/form/components/fields/time-picker.js +132 -0
- package/dist/form/components/fields/time-picker.js.map +1 -0
- package/dist/form/components/fields/use-row-collection.d.ts +2 -1
- package/dist/form/components/fields/use-row-collection.js.map +1 -1
- package/dist/form/components/form-path.d.ts +0 -1
- package/dist/form/components/form-path.js +1 -1
- package/dist/form/components/form-path.js.map +1 -1
- package/dist/form/components/prefill-targets.d.ts +1 -1
- package/dist/form/components/prefill-targets.js.map +1 -1
- package/dist/form/components/types.d.ts +5 -1
- package/dist/form/components/use-field-commit.d.ts +2 -1
- package/dist/form/components/use-field-commit.js.map +1 -1
- package/dist/form/components/use-form-resolver.d.ts +2 -1
- package/dist/form/components/use-form-resolver.js.map +1 -1
- package/dist/format/date-time.d.ts +12 -0
- package/dist/format/date-time.js +30 -0
- package/dist/format/date-time.js.map +1 -0
- package/dist/format/number.d.ts +2 -0
- package/dist/format/number.js +23 -0
- package/dist/format/number.js.map +1 -0
- package/dist/{table/components/cells → format}/numeric.js +1 -1
- package/dist/format/numeric.js.map +1 -0
- package/dist/format/value.d.ts +6 -0
- package/dist/format/value.js +17 -0
- package/dist/format/value.js.map +1 -0
- package/dist/i18n/config.d.ts +2 -1
- package/dist/i18n/config.js.map +1 -1
- package/dist/i18n/date-time.js +1 -1
- package/dist/i18n/date-time.js.map +1 -1
- package/dist/i18n/instance.d.ts +1 -1
- package/dist/i18n/instance.js.map +1 -1
- package/dist/i18n/locale-reload.d.ts +2 -1
- package/dist/i18n/locale-reload.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/layout/components.js +1 -1
- package/dist/layout/menu-item.js +43 -49
- package/dist/layout/menu-item.js.map +1 -1
- package/dist/notifications/api.d.ts +6 -0
- package/dist/notifications/api.js +21 -0
- package/dist/notifications/api.js.map +1 -0
- package/dist/notifications/components/empty-state.d.ts +1 -0
- package/dist/notifications/components/empty-state.js +21 -0
- package/dist/notifications/components/empty-state.js.map +1 -0
- package/dist/notifications/components/notification-item.d.ts +6 -0
- package/dist/notifications/components/notification-item.js +65 -0
- package/dist/notifications/components/notification-item.js.map +1 -0
- package/dist/notifications/components/notification-list.d.ts +10 -0
- package/dist/notifications/components/notification-list.js +38 -0
- package/dist/notifications/components/notification-list.js.map +1 -0
- package/dist/notifications/components/notifications-echo.d.ts +5 -0
- package/dist/notifications/components/notifications-echo.js +22 -0
- package/dist/notifications/components/notifications-echo.js.map +1 -0
- package/dist/notifications/components/notifications.d.ts +3 -0
- package/dist/notifications/components/notifications.js +109 -0
- package/dist/notifications/components/notifications.js.map +1 -0
- package/dist/notifications/index.d.ts +1 -0
- package/dist/notifications/index.js +11 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/store.d.ts +25 -0
- package/dist/notifications/store.js +100 -0
- package/dist/notifications/store.js.map +1 -0
- package/dist/notifications/types.d.ts +10 -0
- package/dist/notifications/types.js +0 -0
- package/dist/realtime/build-effects.d.ts +2 -1
- package/dist/realtime/build-effects.js.map +1 -1
- package/dist/realtime/subscriptions.js.map +1 -1
- package/dist/registry/eager.js +2 -1
- package/dist/registry/eager.js.map +1 -1
- package/dist/registry/lazy.js +2 -1
- package/dist/registry/lazy.js.map +1 -1
- package/dist/table/bulk.d.ts +2 -1
- package/dist/table/bulk.js.map +1 -1
- package/dist/table/components/bulk-bar.js +2 -2
- package/dist/table/components/cells/money-cell.js +11 -12
- package/dist/table/components/cells/money-cell.js.map +1 -1
- package/dist/table/components/cells/number-cell.js +11 -11
- package/dist/table/components/cells/number-cell.js.map +1 -1
- package/dist/table/components/cells/text-cell.js +1 -1
- package/dist/table/components/filter-controls.d.ts +0 -4
- package/dist/table/components/filter-controls.js.map +1 -1
- package/dist/table/components/table-cell.js.map +1 -1
- package/dist/table/format.d.ts +1 -10
- package/dist/table/format.js +2 -24
- package/dist/table/format.js.map +1 -1
- package/dist/table/types.d.ts +7 -17
- package/dist/types/generated.d.ts +137 -290
- package/dist/types/index.d.ts +1 -1
- package/dist/vite-client.d.ts +8 -0
- package/dist/vite.d.ts +35 -1
- package/dist/vite.js +60 -2
- package/dist/vite.js.map +1 -1
- package/package.json +13 -2
- package/resources/icons/bell.svg +16 -0
- package/resources/icons/clock.svg +16 -0
- package/dist/table/components/cells/numeric.js.map +0 -1
- /package/dist/{table/components/cells → format}/numeric.d.ts +0 -0
|
@@ -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 { RenderNode } from \"@lattice-php/lattice/core/renderer\";\nimport { DEFAULT_COLUMN_WIDTH, type SizableColumn } from \"@lattice-php/lattice/core/column-sizing\";\nimport { useColumnResizing } from \"@lattice-php/lattice/core/use-column-resizing\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { memo, useEffect, useMemo, useState } from \"react\";\nimport type { ColumnWidth, RowAction as WireRowAction } from \"@lattice-php/lattice/types/generated\";\nimport { FieldScopeProvider } from \"../field-scope\";\nimport { TableCellProvider } from \"../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\nexport type TableRowModel = {\n key: string;\n index: number;\n row: RepeaterRow;\n template: Node[];\n span: boolean;\n heading?: string;\n};\n\nfunction tableViewportMatches(): boolean {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return true;\n }\n\n return window.matchMedia(tableViewportQuery).matches;\n}\n\nfunction useTableViewport(): boolean {\n const [matches, setMatches] = useState(tableViewportMatches);\n\n useEffect(() => {\n if (typeof window.matchMedia !== \"function\") {\n return;\n }\n\n const query = window.matchMedia(tableViewportQuery);\n const update = () => setMatches(query.matches);\n\n update();\n query.addEventListener(\"change\", update);\n\n return () => query.removeEventListener(\"change\", update);\n }, []);\n\n return matches;\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 gridTemplateColumns: string;\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 gridTemplateColumns,\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 items-start gap-x-3\"\n style={{ gridTemplateColumns }}\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) => (\n <RenderNode key={child.key ?? child.id} node={child} />\n ))}\n </div>\n ) : (\n template.map((child) => (\n <div key={child.key ?? child.id}>\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, 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.resetColumnWidths\", \"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.resetColumnWidths\", \"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 className=\"flex min-w-max flex-col gap-2\">\n <div className=\"grid items-center gap-x-3\" style={{ gridTemplateColumns }}>\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 gridTemplateColumns={gridTemplateColumns}\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":";;;;;;;;;;;;;AAeA,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AACvB,IAAM,mBAAmB,CAAC,eAAe;AACzC,IAAM,kBAAkB,CAAC,cAAc;AACvC,IAAM,qBAAqB;AAa3B,SAAS,uBAAgC;CACvC,IAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAChE,OAAO;CAGT,OAAO,OAAO,WAAW,kBAAkB,EAAE;AAC/C;AAEA,SAAS,mBAA4B;CACnC,MAAM,CAAC,SAAS,cAAc,SAAS,oBAAoB;CAE3D,gBAAgB;EACd,IAAI,OAAO,OAAO,eAAe,YAC/B;EAGF,MAAM,QAAQ,OAAO,WAAW,kBAAkB;EAClD,MAAM,eAAe,WAAW,MAAM,OAAO;EAE7C,OAAO;EACP,MAAM,iBAAiB,UAAU,MAAM;EAEvC,aAAa,MAAM,oBAAoB,UAAU,MAAM;CACzD,GAAG,CAAC,CAAC;CAEL,OAAO;AACT;AAEA,SAAgB,kBAAkB,OAA8B;CAC9D,OAAO,MAAM,KAAK,SAAS;EACzB,MAAM,QAAQ,KAAK;EAKnB,OAAO;GACL,MAAM,OAAO,MAAM,IAAI;GACvB,OAAO,OAAO,MAAM,SAAS,MAAM,IAAI;GACvC,aAAa,MAAM,eAAA;EACrB;CACF,CAAC;AACH;AAuBA,IAAM,eAAe,KAAK,SAAS,aAAa,EAC9C,MACA,OACA,KACA,UACA,MACA,SACA,QACA,aACA,qBACA,SACA,aACA,WACA,YACA,SACA,QACA,UACA,aACA,eACoB;CACpB,MAAM,EAAE,MAAM,KAAK,SAAS;CAE5B,OACE,qBAAC,OAAD;EACE,MAAM,OAAO,cAAc,SAAS,EAAE;EACtC,iBAAe;EACf,aAAW,aAAa,KAAK,GAAG;EAChC,WAAU;EACV,OAAO,EAAE,oBAAoB;YAL/B;GAOE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACG,eAAe,CAAC,WACf,oBAAC,WAAD;KACE,OAAM;KACN,QAAQ,SAAS,KAAK,MAAM;KAC5B,eAAe,OAAO,OAAO,EAAE;eAE/B,oBAAC,MAAD,EAAM,MAAK,WAAY,CAAA;IACd,CAAA,GAEZ,eAAe,CAAC,UACf,oBAAC,WAAD;KACE,OAAM;KACN,QAAQ,SAAS,KAAK,QAAQ;KAC9B,eAAe,OAAO,OAAO,CAAC;eAE9B,oBAAC,MAAD,EAAM,MAAK,aAAc,CAAA;IAChB,CAAA,CAEV;;GAEL,oBAAC,oBAAD;IACQ;IACC;IACF;IACL,WAAW,OAAO,UAAU,QAAQ,OAAO,OAAO,KAAK;cAEvD,oBAAC,mBAAD,EAAA,UACG,OACC,oBAAC,OAAD;KACE,aAAW,aAAa,KAAK,GAAG,MAAM;KACtC,WAAU;KACV,OAAO,EAAE,YAAY,QAAQ,cAAc;eAE1C,SAAS,KAAK,UACb,oBAAC,YAAD,EAAwC,MAAM,MAAQ,GAArC,MAAM,OAAO,MAAM,EAAkB,CACvD;IACE,CAAA,IAEL,SAAS,KAAK,UACZ,oBAAC,OAAD,EAAA,UACE,oBAAC,YAAD,EAAY,MAAM,MAAQ,CAAA,EACvB,GAFK,MAAM,OAAO,MAAM,EAExB,CACN,EAEc,CAAA;GACD,CAAA;GAEpB,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,YAAD,EACE,SAAS,gBAAgB,YAAY;KAAE;KAAO;KAAW;KAAU;KAAa;IAAE,CAAC,EACpF,CAAA;GACE,CAAA;EACF;;AAET,CAAC;AAED,SAAgB,UAAU,EACxB,MACA,SACA,MACA,aACA,WACA,YACA,SACA,QACA,UACA,aACA,aACA,mBAAmB,OACnB,kBAAkB,SAejB;CACD,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,kBAAkB,iBAAiB;CACzC,MAAM,gBAAgB,cAElB,QAAQ,KAAK,YAAY;EACvB,KAAK,OAAO;EACZ,OAAO,OAAO;EACd,OAAO,OAAO;CAChB,EAAE,GACJ,CAAC,OAAO,CACV;CACA,MAAM,EAAE,sBAAsB,qBAAqB,cAAc,iBAC/D,kBAAkB;EAChB,SAAS;EACT,SAAS;EACT,aAAa;EACb,eAAe;EACf,eAAe;EACf,YAAY,mBAAmB,8BAA8B,SAAS,KAAA;EACtE,gBAAgB;CAClB,CAAC;CAEH,IAAI,CAAC,iBACH,OACE,oBAAC,OAAD;EAAK,WAAU;YACZ,KAAK,KAAK,QACT,oBAAC,OAAD;GAAmB,MAAM,OAAO,cAAc,IAAI,KAAK,EAAE;GAAG,iBAAe,IAAI;aAC7E,oBAAC,SAAD;IACQ;IACN,OAAO,IAAI;IACX,KAAK,IAAI;IACT,UAAU,IAAI;IACd,SAAS,IAAI,WAAW,IAAI,IAAI,QAAQ;IAC3B;IACb,SAAS,IAAI,UAAU;IACvB,QAAQ,IAAI,UAAU,KAAK,SAAS;IACpC,WAAW,UAAU,IAAI,KAAK;IAClB;IACH;IACC;IACF;IACK;GACd,CAAA;EACE,GAjBK,IAAI,GAiBT,CACN;CACE,CAAA;CAIT,OACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACG,gBACC,oBAAC,UAAD;GACE,cAAY,EAAE,2BAA2B,qBAAqB;GAC9D,WAAU;GACV,aAAU;GACV,SAAS;GACT,OAAO,EAAE,2BAA2B,qBAAqB;GACzD,MAAK;aAEL,oBAAC,MAAD;IAAM,MAAK;IAAa,WAAU;GAAmB,CAAA;EAC/C,CAAA,GAEV,oBAAC,OAAD;GAAK,WAAU;aACb,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;KAA4B,OAAO,EAAE,oBAAoB;eAAxE;MACE,oBAAC,OAAD,CAAM,CAAA;MACL,QAAQ,KAAK,QAAQ,UACpB,qBAAC,OAAD;OAEE,WAAU;iBAFZ,CAIG,OAAO,OACP,oBAAoB,oBAAC,OAAD,EAAK,GAAI,qBAAqB,cAAc,MAAM,EAAI,CAAA,CACxE;SALE,OAAO,IAKT,CACN;MACD,oBAAC,OAAD,CAAM,CAAA;KACH;QAEJ,KAAK,KAAK,QACT,oBAAC,cAAD;KAEQ;KACN,OAAO,IAAI;KACX,KAAK,IAAI;KACT,UAAU,IAAI;KACd,MAAM,IAAI;KACV,SAAS,IAAI,UAAU;KACvB,QAAQ,IAAI,UAAU,KAAK,SAAS;KACpC,aAAa,QAAQ;KACA;KACrB,SAAS,IAAI;KACA;KACb,WAAW,UAAU,IAAI,KAAK;KAClB;KACH;KACD;KACE;KACG;KACA;IACd,GAnBM,IAAI,GAmBV,CACF,CACE;;EACF,CAAA,CACF;;AAET"}
|
|
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 { RenderNode } from \"@lattice-php/lattice/core/renderer\";\nimport { DEFAULT_COLUMN_WIDTH, type SizableColumn } from \"@lattice-php/lattice/core/column-sizing\";\nimport { useColumnResizing } from \"@lattice-php/lattice/core/use-column-resizing\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { memo, useEffect, useMemo, useState } from \"react\";\nimport type { ColumnWidth, RowAction as WireRowAction } from \"@lattice-php/lattice/types/generated\";\nimport { FieldScopeProvider } from \"../field-scope\";\nimport { TableCellProvider } from \"../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\ntype TableRowModel = {\n key: string;\n index: number;\n row: RepeaterRow;\n template: Node[];\n span: boolean;\n heading?: string;\n};\n\nfunction tableViewportMatches(): boolean {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return true;\n }\n\n return window.matchMedia(tableViewportQuery).matches;\n}\n\nfunction useTableViewport(): boolean {\n const [matches, setMatches] = useState(tableViewportMatches);\n\n useEffect(() => {\n if (typeof window.matchMedia !== \"function\") {\n return;\n }\n\n const query = window.matchMedia(tableViewportQuery);\n const update = () => setMatches(query.matches);\n\n update();\n query.addEventListener(\"change\", update);\n\n return () => query.removeEventListener(\"change\", update);\n }, []);\n\n return matches;\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 gridTemplateColumns: string;\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 gridTemplateColumns,\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 items-start gap-x-3\"\n style={{ gridTemplateColumns }}\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) => (\n <RenderNode key={child.key ?? child.id} node={child} />\n ))}\n </div>\n ) : (\n template.map((child) => (\n <div key={child.key ?? child.id}>\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, 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.resetColumnWidths\", \"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.resetColumnWidths\", \"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 className=\"flex min-w-max flex-col gap-2\">\n <div className=\"grid items-center gap-x-3\" style={{ gridTemplateColumns }}>\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 gridTemplateColumns={gridTemplateColumns}\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,26 +1,65 @@
|
|
|
1
|
+
import { Icon } from "../../../icons/sprite.js";
|
|
2
|
+
import { Popover, PopoverContent, PopoverTrigger } from "../../../core/components/popover.js";
|
|
3
|
+
import { Button } from "../../../core/components/button.js";
|
|
1
4
|
import { SimpleField } from "./simple-field.js";
|
|
2
5
|
import { Input } from "../base/input.js";
|
|
3
|
-
import {
|
|
6
|
+
import { formatTimeValue, parseTimeString, secondsEnabled } from "./time-picker-columns.js";
|
|
7
|
+
import { TimePicker } from "./time-picker.js";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
9
|
//#region resources/js/form/components/fields/time-input.tsx
|
|
5
10
|
var TimeInputComponent = ({ node }) => {
|
|
6
11
|
const props = node.props;
|
|
12
|
+
const withSeconds = secondsEnabled(props.step);
|
|
13
|
+
const triggerLabel = props.label ?? props.name;
|
|
7
14
|
return /* @__PURE__ */ jsx(SimpleField, {
|
|
8
15
|
node,
|
|
9
16
|
label: props.label ?? "",
|
|
10
|
-
children: ({ name, testId, value, readOnly, disabled, commit }) => /* @__PURE__ */
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
children: ({ name, testId, value, readOnly, disabled, commit, blur }) => /* @__PURE__ */ jsxs("div", {
|
|
18
|
+
className: "flex gap-2",
|
|
19
|
+
children: [/* @__PURE__ */ jsx(Input, {
|
|
20
|
+
"aria-label": triggerLabel,
|
|
21
|
+
autoFocus: props.autoFocus ?? false,
|
|
22
|
+
"data-test": testId,
|
|
23
|
+
disabled,
|
|
24
|
+
id: name,
|
|
25
|
+
name,
|
|
26
|
+
onBlur: () => {
|
|
27
|
+
const parsed = parseTimeString(value);
|
|
28
|
+
if (parsed) commit(formatTimeValue(parsed, withSeconds));
|
|
29
|
+
blur();
|
|
30
|
+
},
|
|
31
|
+
onChange: (event) => commit(event.target.value),
|
|
32
|
+
readOnly,
|
|
33
|
+
tabIndex: props.tabIndex ?? void 0,
|
|
34
|
+
type: "text",
|
|
35
|
+
value
|
|
36
|
+
}), /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
37
|
+
asChild: true,
|
|
38
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
39
|
+
"aria-label": `Open ${triggerLabel} time picker`,
|
|
40
|
+
disabled: disabled || readOnly,
|
|
41
|
+
size: "icon",
|
|
42
|
+
type: "button",
|
|
43
|
+
variant: "secondary",
|
|
44
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
45
|
+
name: "clock",
|
|
46
|
+
className: "size-lt-icon-md",
|
|
47
|
+
"aria-hidden": "true"
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
}), /* @__PURE__ */ jsx(PopoverContent, {
|
|
51
|
+
className: "p-2",
|
|
52
|
+
children: /* @__PURE__ */ jsx(TimePicker, {
|
|
53
|
+
value: parseTimeString(value),
|
|
54
|
+
onChange: (next) => commit(formatTimeValue(next, withSeconds)),
|
|
55
|
+
step: props.step,
|
|
56
|
+
min: props.min,
|
|
57
|
+
max: props.max,
|
|
58
|
+
disabled,
|
|
59
|
+
readOnly,
|
|
60
|
+
testId: `${testId}-picker`
|
|
61
|
+
})
|
|
62
|
+
})] })]
|
|
24
63
|
})
|
|
25
64
|
});
|
|
26
65
|
};
|
|
@@ -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 { Input } from \"../base/input\";\nimport { SimpleField } from \"./simple-field\";\n\nexport const TimeInputComponent: RendererComponent<\"field.time-input\"> = ({ node }) => {\n const props = node.props;\n\n return (\n <SimpleField node={node} label={props.label ?? \"\"}>\n {({ name, testId, value, readOnly, disabled, commit }) => (\n <Input\n
|
|
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/core/components/button\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@lattice-php/lattice/core/components/popover\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { Input } from \"../base/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":";;;;;;;;;AAaA,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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type TimeValue = {
|
|
2
|
+
hour: number;
|
|
3
|
+
minute: number;
|
|
4
|
+
second: number;
|
|
5
|
+
};
|
|
6
|
+
export type TimeColumnOption = {
|
|
7
|
+
value: number;
|
|
8
|
+
label: string;
|
|
9
|
+
disabled: boolean;
|
|
10
|
+
};
|
|
11
|
+
type TimeColumns = {
|
|
12
|
+
hours: TimeColumnOption[];
|
|
13
|
+
minutes: TimeColumnOption[];
|
|
14
|
+
seconds: TimeColumnOption[] | null;
|
|
15
|
+
};
|
|
16
|
+
export declare function parseTimeString(value: string | null | undefined): TimeValue | null;
|
|
17
|
+
export declare function formatTimeValue(value: TimeValue, withSeconds: boolean): string;
|
|
18
|
+
export declare function secondsEnabled(step: number | null | undefined): boolean;
|
|
19
|
+
export declare function buildTimeColumns(step: number | null | undefined, options?: {
|
|
20
|
+
min?: string | null;
|
|
21
|
+
max?: string | null;
|
|
22
|
+
current?: TimeValue | null;
|
|
23
|
+
}): TimeColumns;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//#region resources/js/form/components/fields/time-picker-columns.ts
|
|
2
|
+
var timePattern = /^(\d{1,2}):(\d{2})(?::(\d{2}))?$/;
|
|
3
|
+
function parseTimeString(value) {
|
|
4
|
+
if (typeof value !== "string") return null;
|
|
5
|
+
const match = timePattern.exec(value.trim());
|
|
6
|
+
if (!match) return null;
|
|
7
|
+
const hour = Number(match[1]);
|
|
8
|
+
const minute = Number(match[2]);
|
|
9
|
+
const second = match[3] ? Number(match[3]) : 0;
|
|
10
|
+
if (hour > 23 || minute > 59 || second > 59) return null;
|
|
11
|
+
return {
|
|
12
|
+
hour,
|
|
13
|
+
minute,
|
|
14
|
+
second
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function formatTimeValue(value, withSeconds) {
|
|
18
|
+
const parts = [pad(value.hour), pad(value.minute)];
|
|
19
|
+
if (withSeconds) parts.push(pad(value.second));
|
|
20
|
+
return parts.join(":");
|
|
21
|
+
}
|
|
22
|
+
function secondsEnabled(step) {
|
|
23
|
+
return step != null && step < 60;
|
|
24
|
+
}
|
|
25
|
+
function buildTimeColumns(step, options = {}) {
|
|
26
|
+
const { min, max, current } = options;
|
|
27
|
+
const minBound = toBound(min);
|
|
28
|
+
const maxBound = toBound(max);
|
|
29
|
+
const minuteStep = step == null || step < 60 ? 1 : step % 60 === 0 ? step / 60 : 1;
|
|
30
|
+
return {
|
|
31
|
+
hours: range(0, 23, 1).map((value) => ({
|
|
32
|
+
value,
|
|
33
|
+
label: pad(value),
|
|
34
|
+
disabled: minBound != null && value < minBound.hour || maxBound != null && value > maxBound.hour
|
|
35
|
+
})),
|
|
36
|
+
minutes: withValue(range(0, 59, minuteStep), current?.minute).map((value) => ({
|
|
37
|
+
value,
|
|
38
|
+
label: pad(value),
|
|
39
|
+
disabled: minuteDisabled(value, current, minBound, maxBound)
|
|
40
|
+
})),
|
|
41
|
+
seconds: secondsEnabled(step) ? range(0, 59, 1).map((value) => ({
|
|
42
|
+
value,
|
|
43
|
+
label: pad(value),
|
|
44
|
+
disabled: false
|
|
45
|
+
})) : null
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function minuteDisabled(minute, current, minBound, maxBound) {
|
|
49
|
+
if (!current) return false;
|
|
50
|
+
if (minBound != null && current.hour === minBound.hour && minute < minBound.minute) return true;
|
|
51
|
+
if (maxBound != null && current.hour === maxBound.hour && minute > maxBound.minute) return true;
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
function toBound(value) {
|
|
55
|
+
const parsed = parseTimeString(value);
|
|
56
|
+
return parsed ? {
|
|
57
|
+
hour: parsed.hour,
|
|
58
|
+
minute: parsed.minute
|
|
59
|
+
} : null;
|
|
60
|
+
}
|
|
61
|
+
function withValue(values, extra) {
|
|
62
|
+
if (extra == null || values.includes(extra)) return values;
|
|
63
|
+
return [...values, extra].sort((left, right) => left - right);
|
|
64
|
+
}
|
|
65
|
+
function range(start, end, step) {
|
|
66
|
+
const values = [];
|
|
67
|
+
for (let value = start; value <= end; value += step) values.push(value);
|
|
68
|
+
return values;
|
|
69
|
+
}
|
|
70
|
+
function pad(value) {
|
|
71
|
+
return String(value).padStart(2, "0");
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { buildTimeColumns, formatTimeValue, parseTimeString, secondsEnabled };
|
|
75
|
+
|
|
76
|
+
//# sourceMappingURL=time-picker-columns.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { TimeValue } from './time-picker-columns';
|
|
2
|
+
type TimePickerProps = {
|
|
3
|
+
value: TimeValue | null;
|
|
4
|
+
onChange: (next: TimeValue) => void;
|
|
5
|
+
step?: number | null;
|
|
6
|
+
min?: string | null;
|
|
7
|
+
max?: string | null;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
readOnly?: boolean;
|
|
10
|
+
labels?: {
|
|
11
|
+
hour?: string;
|
|
12
|
+
minute?: string;
|
|
13
|
+
second?: string;
|
|
14
|
+
};
|
|
15
|
+
testId?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function TimePicker({ value, onChange, step, min, max, disabled, readOnly, labels, testId, }: TimePickerProps): import("react").JSX.Element;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { buildTimeColumns } from "./time-picker-columns.js";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region resources/js/form/components/fields/time-picker.tsx
|
|
6
|
+
function TimePicker({ value, onChange, step, min, max, disabled = false, readOnly = false, labels, testId }) {
|
|
7
|
+
const containerRef = useRef(null);
|
|
8
|
+
const columns = buildTimeColumns(step, {
|
|
9
|
+
min,
|
|
10
|
+
max,
|
|
11
|
+
current: value
|
|
12
|
+
});
|
|
13
|
+
const current = value ?? {
|
|
14
|
+
hour: 0,
|
|
15
|
+
minute: 0,
|
|
16
|
+
second: 0
|
|
17
|
+
};
|
|
18
|
+
const interactive = !disabled && !readOnly;
|
|
19
|
+
const columnList = [
|
|
20
|
+
{
|
|
21
|
+
key: "hour",
|
|
22
|
+
label: labels?.hour ?? "Hour",
|
|
23
|
+
options: columns.hours,
|
|
24
|
+
selected: current.hour
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
key: "minute",
|
|
28
|
+
label: labels?.minute ?? "Minute",
|
|
29
|
+
options: columns.minutes,
|
|
30
|
+
selected: current.minute
|
|
31
|
+
},
|
|
32
|
+
...columns.seconds ? [{
|
|
33
|
+
key: "second",
|
|
34
|
+
label: labels?.second ?? "Second",
|
|
35
|
+
options: columns.seconds,
|
|
36
|
+
selected: current.second
|
|
37
|
+
}] : []
|
|
38
|
+
];
|
|
39
|
+
function focusColumn(index) {
|
|
40
|
+
const target = (containerRef.current?.querySelectorAll("[role=\"listbox\"]"))?.[index];
|
|
41
|
+
if (!target) return;
|
|
42
|
+
(target.querySelector("[data-active=\"true\"]") ?? target.querySelector("[role=\"option\"]"))?.focus();
|
|
43
|
+
}
|
|
44
|
+
return /* @__PURE__ */ jsx("div", {
|
|
45
|
+
ref: containerRef,
|
|
46
|
+
className: "flex gap-1",
|
|
47
|
+
"data-test": testId,
|
|
48
|
+
children: columnList.map((column, index) => /* @__PURE__ */ jsx(TimeColumn, {
|
|
49
|
+
label: column.label,
|
|
50
|
+
options: column.options,
|
|
51
|
+
selected: value ? column.selected : null,
|
|
52
|
+
disabled: !interactive,
|
|
53
|
+
onSelect: (optionValue) => interactive && onChange({
|
|
54
|
+
...current,
|
|
55
|
+
[column.key]: optionValue
|
|
56
|
+
}),
|
|
57
|
+
onHorizontal: (direction) => focusColumn(index + direction)
|
|
58
|
+
}, column.key))
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function TimeColumn({ label, options, selected, disabled, onSelect, onHorizontal }) {
|
|
62
|
+
const listRef = useRef(null);
|
|
63
|
+
const enabledValues = options.filter((option) => !option.disabled).map((option) => option.value);
|
|
64
|
+
const activeValue = selected ?? enabledValues[0] ?? options[0]?.value ?? 0;
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
(listRef.current?.querySelector("[data-active=\"true\"]"))?.scrollIntoView?.({ block: "nearest" });
|
|
67
|
+
}, [selected]);
|
|
68
|
+
function moveTo(nextValue) {
|
|
69
|
+
if (nextValue == null) return;
|
|
70
|
+
listRef.current?.querySelector(`[data-value="${nextValue}"]`)?.focus();
|
|
71
|
+
onSelect(nextValue);
|
|
72
|
+
}
|
|
73
|
+
function handleKeyDown(event) {
|
|
74
|
+
if (disabled) return;
|
|
75
|
+
const index = enabledValues.indexOf(activeValue);
|
|
76
|
+
switch (event.key) {
|
|
77
|
+
case "ArrowDown":
|
|
78
|
+
event.preventDefault();
|
|
79
|
+
moveTo(enabledValues[Math.min(index + 1, enabledValues.length - 1)]);
|
|
80
|
+
break;
|
|
81
|
+
case "ArrowUp":
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
moveTo(enabledValues[Math.max(index - 1, 0)]);
|
|
84
|
+
break;
|
|
85
|
+
case "Home":
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
moveTo(enabledValues[0]);
|
|
88
|
+
break;
|
|
89
|
+
case "End":
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
moveTo(enabledValues[enabledValues.length - 1]);
|
|
92
|
+
break;
|
|
93
|
+
case "ArrowRight":
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
onHorizontal(1);
|
|
96
|
+
break;
|
|
97
|
+
case "ArrowLeft":
|
|
98
|
+
event.preventDefault();
|
|
99
|
+
onHorizontal(-1);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return /* @__PURE__ */ jsx("div", {
|
|
104
|
+
ref: listRef,
|
|
105
|
+
role: "listbox",
|
|
106
|
+
"aria-label": label,
|
|
107
|
+
"aria-orientation": "vertical",
|
|
108
|
+
tabIndex: -1,
|
|
109
|
+
className: "flex max-h-40 w-14 flex-col overflow-y-auto",
|
|
110
|
+
onKeyDown: handleKeyDown,
|
|
111
|
+
children: options.map((option) => {
|
|
112
|
+
const isSelected = selected === option.value;
|
|
113
|
+
return /* @__PURE__ */ jsx("button", {
|
|
114
|
+
type: "button",
|
|
115
|
+
role: "option",
|
|
116
|
+
"aria-selected": isSelected,
|
|
117
|
+
"aria-label": `${label} ${option.label}`,
|
|
118
|
+
"data-value": option.value,
|
|
119
|
+
"data-active": activeValue === option.value,
|
|
120
|
+
disabled: disabled || option.disabled,
|
|
121
|
+
tabIndex: activeValue === option.value ? 0 : -1,
|
|
122
|
+
onClick: () => onSelect(option.value),
|
|
123
|
+
className: cn("shrink-0 rounded-lt-sm px-2 py-1 text-sm text-lt-fg hover:bg-lt-muted", isSelected && "bg-lt-primary text-lt-primary-fg", (disabled || option.disabled) && "cursor-not-allowed opacity-40"),
|
|
124
|
+
children: option.label
|
|
125
|
+
}, option.value);
|
|
126
|
+
})
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
export { TimePicker };
|
|
131
|
+
|
|
132
|
+
//# sourceMappingURL=time-picker.js.map
|
|
@@ -0,0 +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,5 +1,5 @@
|
|
|
1
1
|
import { RepeaterRow } from './repeater-rows';
|
|
2
|
-
|
|
2
|
+
type RowCollection = {
|
|
3
3
|
path: string;
|
|
4
4
|
rows: RepeaterRow[];
|
|
5
5
|
onField: (index: number, field: string, value: unknown) => void;
|
|
@@ -9,3 +9,4 @@ export type RowCollection = {
|
|
|
9
9
|
append: (row: RepeaterRow) => void;
|
|
10
10
|
};
|
|
11
11
|
export declare function useRowCollection(name: string, defaultItems: number): RowCollection;
|
|
12
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-row-collection.js","names":[],"sources":["../../../../resources/js/form/components/fields/use-row-collection.ts"],"sourcesContent":["import { useCallback, useLayoutEffect } from \"react\";\nimport { useFieldScope } from \"../field-scope\";\nimport { useFormValue, useSetFormValue } from \"../values\";\nimport {\n duplicateRow,\n ensureRowIds,\n moveRow,\n removeRow,\n seedRows,\n withRowId,\n type RepeaterRow,\n} from \"./repeater-rows\";\n\
|
|
1
|
+
{"version":3,"file":"use-row-collection.js","names":[],"sources":["../../../../resources/js/form/components/fields/use-row-collection.ts"],"sourcesContent":["import { useCallback, useLayoutEffect } from \"react\";\nimport { useFieldScope } from \"../field-scope\";\nimport { useFormValue, useSetFormValue } from \"../values\";\nimport {\n duplicateRow,\n ensureRowIds,\n moveRow,\n removeRow,\n seedRows,\n withRowId,\n type RepeaterRow,\n} from \"./repeater-rows\";\n\ntype RowCollection = {\n path: string;\n rows: RepeaterRow[];\n onField: (index: number, field: string, value: unknown) => void;\n onRemove: (index: number) => void;\n onMove: (index: number, delta: number) => void;\n onDuplicate: (index: number) => void;\n append: (row: RepeaterRow) => void;\n};\n\nexport function useRowCollection(name: string, defaultItems: number): RowCollection {\n const scope = useFieldScope();\n const path = scope ? scope.errorKey(name) : name;\n const setValue = useSetFormValue();\n const stored = useFormValue(path);\n const raw: RepeaterRow[] = Array.isArray(stored) ? stored : seedRows(stored, defaultItems);\n const rows = ensureRowIds(raw);\n\n useLayoutEffect(() => {\n if (rows !== raw) {\n setValue(path, rows);\n }\n }, [raw, rows, setValue, path]);\n\n // Functional store updates preserve the identity of untouched rows, which lets\n // the memoised RowItem skip re-rendering siblings on a single-row edit.\n const mutate = useCallback(\n (fn: (rows: RepeaterRow[]) => RepeaterRow[]): void => {\n setValue(path, (prev: unknown) =>\n fn(Array.isArray(prev) ? (prev as RepeaterRow[]) : seedRows(prev, defaultItems)),\n );\n },\n [setValue, path, defaultItems],\n );\n\n const onField = useCallback(\n (index: number, field: string, value: unknown): void =>\n mutate((current) => current.map((r, i) => (i === index ? { ...r, [field]: value } : r))),\n [mutate],\n );\n const onRemove = useCallback(\n (index: number): void => mutate((current) => removeRow(current, index)),\n [mutate],\n );\n const onMove = useCallback(\n (index: number, delta: number): void =>\n mutate((current) => moveRow(current, index, index + delta)),\n [mutate],\n );\n const onDuplicate = useCallback(\n (index: number): void => mutate((current) => duplicateRow(current, index)),\n [mutate],\n );\n const append = useCallback(\n (row: RepeaterRow): void => mutate((current) => [...current, withRowId(row)]),\n [mutate],\n );\n\n return { path, rows, onField, onRemove, onMove, onDuplicate, append };\n}\n"],"mappings":";;;;;AAuBA,SAAgB,iBAAiB,MAAc,cAAqC;CAClF,MAAM,QAAQ,cAAc;CAC5B,MAAM,OAAO,QAAQ,MAAM,SAAS,IAAI,IAAI;CAC5C,MAAM,WAAW,gBAAgB;CACjC,MAAM,SAAS,aAAa,IAAI;CAChC,MAAM,MAAqB,MAAM,QAAQ,MAAM,IAAI,SAAS,SAAS,QAAQ,YAAY;CACzF,MAAM,OAAO,aAAa,GAAG;CAE7B,sBAAsB;EACpB,IAAI,SAAS,KACX,SAAS,MAAM,IAAI;CAEvB,GAAG;EAAC;EAAK;EAAM;EAAU;CAAI,CAAC;CAI9B,MAAM,SAAS,aACZ,OAAqD;EACpD,SAAS,OAAO,SACd,GAAG,MAAM,QAAQ,IAAI,IAAK,OAAyB,SAAS,MAAM,YAAY,CAAC,CACjF;CACF,GACA;EAAC;EAAU;EAAM;CAAY,CAC/B;CAyBA,OAAO;EAAE;EAAM;EAAM,SAvBL,aACb,OAAe,OAAe,UAC7B,QAAQ,YAAY,QAAQ,KAAK,GAAG,MAAO,MAAM,QAAQ;GAAE,GAAG;IAAI,QAAQ;EAAM,IAAI,CAAE,CAAC,GACzF,CAAC,MAAM,CAoBY;EAAS,UAlBb,aACd,UAAwB,QAAQ,YAAY,UAAU,SAAS,KAAK,CAAC,GACtE,CAAC,MAAM,CAgBqB;EAAU,QAdzB,aACZ,OAAe,UACd,QAAQ,YAAY,QAAQ,SAAS,OAAO,QAAQ,KAAK,CAAC,GAC5D,CAAC,MAAM,CAW+B;EAAQ,aAT5B,aACjB,UAAwB,QAAQ,YAAY,aAAa,SAAS,KAAK,CAAC,GACzE,CAAC,MAAM,CAOuC;EAAa,QAL9C,aACZ,QAA2B,QAAQ,YAAY,CAAC,GAAG,SAAS,UAAU,GAAG,CAAC,CAAC,GAC5E,CAAC,MAAM,CAGoD;CAAO;AACtE"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export declare function pathParts(path: string): string[];
|
|
2
1
|
export declare function appendPath(base: string | null | undefined, ...parts: Array<string | number>): string;
|
|
3
2
|
export declare function toHtmlName(path: string): string;
|
|
4
3
|
export declare function getPath(values: Record<string, unknown>, path: string): unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-path.js","names":[],"sources":["../../../resources/js/form/components/form-path.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"form-path.js","names":[],"sources":["../../../resources/js/form/components/form-path.ts"],"sourcesContent":["function pathParts(path: string): string[] {\n return path.split(\".\").filter((part) => part !== \"\");\n}\n\nexport function appendPath(\n base: string | null | undefined,\n ...parts: Array<string | number>\n): string {\n const suffix = parts.map(String).filter((part) => part !== \"\");\n\n return [base ?? \"\", ...suffix].filter((part) => part !== \"\").join(\".\");\n}\n\nexport function toHtmlName(path: string): string {\n const [head, ...tail] = pathParts(path);\n\n return tail.reduce((name, part) => `${name}[${part}]`, head ?? \"\");\n}\n\nexport function getPath(values: Record<string, unknown>, path: string): unknown {\n let current: unknown = values;\n\n for (const part of pathParts(path)) {\n if (current == null) {\n return undefined;\n }\n\n current = (current as Record<string, unknown>)[part];\n }\n\n return current;\n}\n\nfunction emptyContainer(nextPart: string | undefined): Record<string, unknown> | unknown[] {\n return nextPart !== undefined && /^\\d+$/.test(nextPart) ? [] : {};\n}\n\nexport function setPath(\n values: Record<string, unknown>,\n path: string,\n value: unknown,\n): Record<string, unknown> {\n const parts = pathParts(path);\n\n if (parts.length === 0) {\n return values;\n }\n\n const write = (current: unknown, index: number): unknown => {\n const part = parts[index];\n const last = index === parts.length - 1;\n const source =\n current && typeof current === \"object\" ? current : emptyContainer(parts[index + 1]);\n const next = Array.isArray(source) ? [...source] : { ...(source as Record<string, unknown>) };\n\n (next as Record<string, unknown>)[part] = last\n ? value\n : write((source as Record<string, unknown>)[part], index + 1);\n\n return next;\n };\n\n return write(values, 0) as Record<string, unknown>;\n}\n"],"mappings":";AAAA,SAAS,UAAU,MAAwB;CACzC,OAAO,KAAK,MAAM,GAAG,EAAE,QAAQ,SAAS,SAAS,EAAE;AACrD;AAEA,SAAgB,WACd,MACA,GAAG,OACK;CACR,MAAM,SAAS,MAAM,IAAI,MAAM,EAAE,QAAQ,SAAS,SAAS,EAAE;CAE7D,OAAO,CAAC,QAAQ,IAAI,GAAG,MAAM,EAAE,QAAQ,SAAS,SAAS,EAAE,EAAE,KAAK,GAAG;AACvE;AAEA,SAAgB,WAAW,MAAsB;CAC/C,MAAM,CAAC,MAAM,GAAG,QAAQ,UAAU,IAAI;CAEtC,OAAO,KAAK,QAAQ,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,IAAI,QAAQ,EAAE;AACnE;AAEA,SAAgB,QAAQ,QAAiC,MAAuB;CAC9E,IAAI,UAAmB;CAEvB,KAAK,MAAM,QAAQ,UAAU,IAAI,GAAG;EAClC,IAAI,WAAW,MACb;EAGF,UAAW,QAAoC;CACjD;CAEA,OAAO;AACT;AAEA,SAAS,eAAe,UAAmE;CACzF,OAAO,aAAa,KAAA,KAAa,QAAQ,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC;AAClE;AAEA,SAAgB,QACd,QACA,MACA,OACyB;CACzB,MAAM,QAAQ,UAAU,IAAI;CAE5B,IAAI,MAAM,WAAW,GACnB,OAAO;CAGT,MAAM,SAAS,SAAkB,UAA2B;EAC1D,MAAM,OAAO,MAAM;EACnB,MAAM,OAAO,UAAU,MAAM,SAAS;EACtC,MAAM,SACJ,WAAW,OAAO,YAAY,WAAW,UAAU,eAAe,MAAM,QAAQ,EAAE;EACpF,MAAM,OAAO,MAAM,QAAQ,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,EAAE,GAAI,OAAmC;EAE5F,KAAkC,QAAQ,OACtC,QACA,MAAO,OAAmC,OAAO,QAAQ,CAAC;EAE9D,OAAO;CACT;CAEA,OAAO,MAAM,QAAQ,CAAC;AACxB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefill-targets.js","names":[],"sources":["../../../resources/js/form/components/prefill-targets.ts"],"sourcesContent":["import type { Node } from \"@lattice-php/lattice/core/types\";\nimport { fieldProps } from \"./field-props\";\nimport { appendPath, getPath } from \"./form-path\";\nimport { buildOverrideKey, rowIdFrom } from \"./override-keys\";\n\
|
|
1
|
+
{"version":3,"file":"prefill-targets.js","names":[],"sources":["../../../resources/js/form/components/prefill-targets.ts"],"sourcesContent":["import type { Node } from \"@lattice-php/lattice/core/types\";\nimport { fieldProps } from \"./field-props\";\nimport { appendPath, getPath } from \"./form-path\";\nimport { buildOverrideKey, rowIdFrom } from \"./override-keys\";\n\ntype PrefillTarget = {\n path: string;\n overrideKey: string;\n resetOn: string[];\n refreshOn: string[];\n};\n\ntype PrefillSnapshot = {\n targets: PrefillTarget[];\n values: Record<string, unknown>;\n};\n\ntype Block = { type: string; label: string; schema: Node[] };\n\nconst ROW_COLLECTION_TYPES = new Set([\"field.builder\", \"field.repeater\"]);\n\nexport { getPath } from \"./form-path\";\n\nfunction mapDep(dep: string, rowPath: string | null): string {\n if (dep.startsWith(\"@\")) {\n return dep.slice(1);\n }\n\n return rowPath === null ? dep : appendPath(rowPath, dep);\n}\n\nfunction targetFor(\n node: Node,\n rowPath: string | null,\n identityCollectionPath: string | null,\n index = 0,\n row: Record<string, unknown> = {},\n): PrefillTarget | null {\n const props = fieldProps(node);\n\n if (!props.editablePrefill || !props.name) {\n return null;\n }\n\n const rowId = rowIdFrom(row);\n\n return {\n path: rowPath === null ? props.name : appendPath(rowPath, props.name),\n overrideKey:\n identityCollectionPath === null\n ? props.name\n : buildOverrideKey(identityCollectionPath, rowId, index, props.name),\n resetOn: (props.prefillResetOn ?? []).map((dep) => mapDep(dep, rowPath)),\n refreshOn: (props.prefillRefreshOn ?? []).map((dep) => mapDep(dep, rowPath)),\n };\n}\n\nexport function collectPrefillTargets(\n nodes: Node[] | undefined,\n values: Record<string, unknown>,\n): PrefillTarget[] {\n const targets: PrefillTarget[] = [];\n\n const walk = (\n list: Node[] | undefined,\n rowPath: string | null = null,\n identityRowPath: string | null = null,\n identityCollectionPath: string | null = null,\n index = 0,\n row: Record<string, unknown> = {},\n ): void => {\n for (const node of list ?? []) {\n if (ROW_COLLECTION_TYPES.has(node.type)) {\n const name = fieldProps(node).name;\n if (name) {\n const childCollectionPath = appendPath(rowPath, name);\n const childIdentityCollectionPath = appendPath(identityRowPath, name);\n const storedRows = getPath(values, childCollectionPath);\n const rows = Array.isArray(storedRows)\n ? (storedRows as Array<Record<string, unknown>>)\n : [];\n const blocks = (node as unknown as { blocks?: Block[] }).blocks;\n\n rows.forEach((childRow, childIndex) => {\n const template = blocks\n ? (blocks.find((block) => block.type === childRow.type)?.schema ?? [])\n : (node.schema ?? []);\n\n walk(\n template,\n appendPath(childCollectionPath, childIndex),\n appendPath(childIdentityCollectionPath, rowIdFrom(childRow) ?? childIndex),\n childIdentityCollectionPath,\n childIndex,\n childRow,\n );\n });\n }\n\n continue;\n }\n\n const target = targetFor(node, rowPath, identityCollectionPath, index, row);\n if (target) {\n targets.push(target);\n }\n\n walk(node.schema, rowPath, identityRowPath, identityCollectionPath, index, row);\n }\n };\n\n walk(nodes);\n\n return targets;\n}\n\nexport function pathsToClear(previous: PrefillSnapshot, next: PrefillSnapshot): string[] {\n const previousByKey = new Map(\n previous.targets.map((target) => [target.overrideKey, target] as const),\n );\n\n return next.targets\n .filter((target) => {\n const previousTarget = previousByKey.get(target.overrideKey);\n\n return target.resetOn.some((dep, index) => {\n const previousDep = previousTarget?.resetOn[index] ?? dep;\n\n return !Object.is(getPath(previous.values, previousDep), getPath(next.values, dep));\n });\n })\n .map((target) => target.overrideKey);\n}\n\nexport function seededOverrides(\n targets: PrefillTarget[],\n values: Record<string, unknown>,\n): string[] {\n return targets\n .filter((target) => {\n const value = getPath(values, target.path);\n\n return value !== undefined && value !== null && value !== \"\";\n })\n .map((target) => target.overrideKey);\n}\n\nexport function pruneOverrides(overrides: Set<string>, targets: PrefillTarget[]): Set<string> {\n const liveKeys = new Set(targets.map((target) => target.overrideKey));\n\n return new Set([...overrides].filter((key) => liveKeys.has(key)));\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Method } from '@inertiajs/core';
|
|
2
|
-
|
|
2
|
+
import { NodeUnionOf } from '../../core/types';
|
|
3
|
+
import { FormFieldNodeType, FormNodeType } from '../../types/generated';
|
|
4
|
+
export type { Checkbox, Choice, DateInput, DateTimeInput, Form, FormNodeType, HiddenInput, NumberInput, PasswordInput, RichEditor, Select, Textarea, TextInput, TimeInput, } from '../../types/generated';
|
|
5
|
+
export type FormFieldNode = NodeUnionOf<FormFieldNodeType>;
|
|
6
|
+
export type FormNode = NodeUnionOf<FormNodeType>;
|
|
3
7
|
export type FormMethod = Method;
|
|
4
8
|
export type FormLabelAction = {
|
|
5
9
|
href: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
type FieldCommit = {
|
|
2
2
|
/** Write the value and validate immediately (precognitive) or clear errors. */
|
|
3
3
|
commit: (name: string, value: unknown) => void;
|
|
4
4
|
/** Write the value and clear errors, deferring validation to a later `blur`. */
|
|
@@ -16,3 +16,4 @@ export type FieldCommit = {
|
|
|
16
16
|
* behavior is identical to before.
|
|
17
17
|
*/
|
|
18
18
|
export declare function useFieldCommit(): FieldCommit;
|
|
19
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-field-commit.js","names":[],"sources":["../../../resources/js/form/components/use-field-commit.ts"],"sourcesContent":["import { useFieldScope } from \"./field-scope\";\nimport { useFormContext } from \"./context\";\nimport { usePrefillController } from \"./prefill-context\";\nimport { useSetFormValue } from \"./values\";\n\
|
|
1
|
+
{"version":3,"file":"use-field-commit.js","names":[],"sources":["../../../resources/js/form/components/use-field-commit.ts"],"sourcesContent":["import { useFieldScope } from \"./field-scope\";\nimport { useFormContext } from \"./context\";\nimport { usePrefillController } from \"./prefill-context\";\nimport { useSetFormValue } from \"./values\";\n\ntype FieldCommit = {\n /** Write the value and validate immediately (precognitive) or clear errors. */\n commit: (name: string, value: unknown) => void;\n /** Write the value and clear errors, deferring validation to a later `blur`. */\n change: (name: string, value: unknown) => void;\n /** Validate the field now if precognitive (e.g. on blur or popover close). */\n blur: (name: string) => void;\n};\n\n/**\n * The shared field-mutation contract every form field uses to write its value\n * and drive precognition. Fields that validate on change call `commit`; fields\n * that validate on blur/close (rich editor, select) call `change` then `blur`.\n *\n * When called inside a `FieldScopeProvider`, writes go through the scope's\n * `setValue` and error paths use the scoped dot-key; outside a scope the\n * behavior is identical to before.\n */\nexport function useFieldCommit(): FieldCommit {\n const { clearErrors, precognitive, validate } = useFormContext();\n const setGlobal = useSetFormValue();\n const scope = useFieldScope();\n const prefill = usePrefillController();\n\n const write = (name: string, value: unknown): void => {\n if (scope) {\n scope.setValue(name, value);\n } else {\n setGlobal(name, value);\n }\n prefill?.markUserEdit(scope ? scope.overrideKey(name) : name);\n };\n const errorPath = (name: string): string => (scope ? scope.errorKey(name) : name);\n\n return {\n commit(name, value) {\n write(name, value);\n if (precognitive) {\n validate(errorPath(name));\n } else {\n clearErrors(errorPath(name));\n }\n },\n change(name, value) {\n write(name, value);\n clearErrors(errorPath(name));\n },\n blur(name) {\n if (precognitive) {\n validate(errorPath(name));\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AAuBA,SAAgB,iBAA8B;CAC5C,MAAM,EAAE,aAAa,cAAc,aAAa,eAAe;CAC/D,MAAM,YAAY,gBAAgB;CAClC,MAAM,QAAQ,cAAc;CAC5B,MAAM,UAAU,qBAAqB;CAErC,MAAM,SAAS,MAAc,UAAyB;EACpD,IAAI,OACF,MAAM,SAAS,MAAM,KAAK;OAE1B,UAAU,MAAM,KAAK;EAEvB,SAAS,aAAa,QAAQ,MAAM,YAAY,IAAI,IAAI,IAAI;CAC9D;CACA,MAAM,aAAa,SAA0B,QAAQ,MAAM,SAAS,IAAI,IAAI;CAE5E,OAAO;EACL,OAAO,MAAM,OAAO;GAClB,MAAM,MAAM,KAAK;GACjB,IAAI,cACF,SAAS,UAAU,IAAI,CAAC;QAExB,YAAY,UAAU,IAAI,CAAC;EAE/B;EACA,OAAO,MAAM,OAAO;GAClB,MAAM,MAAM,KAAK;GACjB,YAAY,UAAU,IAAI,CAAC;EAC7B;EACA,KAAK,MAAM;GACT,IAAI,cACF,SAAS,UAAU,IAAI,CAAC;EAE5B;CACF;AACF"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Node } from '../../core/types';
|
|
2
2
|
import { PrefillController } from './prefill-context';
|
|
3
|
-
|
|
3
|
+
type FormResolver = {
|
|
4
4
|
nodes: Record<string, Node>;
|
|
5
5
|
markUserEdit: PrefillController["markUserEdit"];
|
|
6
6
|
};
|
|
7
7
|
export declare function useFormResolver(action: string, componentRef: string, nodes: Node[] | undefined): FormResolver;
|
|
8
|
+
export {};
|