@lattice-php/lattice 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action/components/action-form.js +3 -3
- package/dist/action/components/action-group.js +1 -1
- package/dist/action/components/action.js +10 -16
- package/dist/action/components/action.js.map +1 -1
- package/dist/action/run-action.d.ts +9 -0
- package/dist/action/run-action.js +22 -0
- package/dist/action/run-action.js.map +1 -0
- package/dist/chat/components/chat-box.js +31 -14
- package/dist/chat/components/chat-box.js.map +1 -1
- package/dist/chat/components/message.js +2 -2
- package/dist/chat/components/message.js.map +1 -1
- package/dist/chat/components/prompt-input.js +1 -1
- package/dist/chat/index.js +2 -0
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/transport.d.ts +2 -0
- package/dist/chat/transport.js +23 -3
- package/dist/chat/transport.js.map +1 -1
- package/dist/core/api.d.ts +14 -7
- package/dist/core/api.js +77 -1
- package/dist/core/api.js.map +1 -1
- package/dist/core/components/badge.js +4 -4
- package/dist/core/components/badge.js.map +1 -1
- package/dist/core/components/button.js +31 -19
- package/dist/core/components/button.js.map +1 -1
- package/dist/core/components/card.js +3 -2
- package/dist/core/components/card.js.map +1 -1
- package/dist/core/components/chart.js +3 -2
- package/dist/core/components/chart.js.map +1 -1
- package/dist/core/components/checkbox.js +1 -1
- package/dist/core/components/checkbox.js.map +1 -1
- package/dist/core/components/control.js +4 -4
- package/dist/core/components/control.js.map +1 -1
- package/dist/core/components/dialog.d.ts +1 -1
- package/dist/core/components/dialog.js +3 -2
- package/dist/core/components/dialog.js.map +1 -1
- package/dist/core/components/floating-panel.js +5 -4
- package/dist/core/components/floating-panel.js.map +1 -1
- package/dist/core/components/fragment.js +3 -6
- package/dist/core/components/fragment.js.map +1 -1
- package/dist/core/components/grid.js +2 -1
- package/dist/core/components/grid.js.map +1 -1
- package/dist/core/components/heading.js +1 -1
- package/dist/core/components/heading.js.map +1 -1
- package/dist/core/components/index.js +0 -2
- package/dist/core/components/index.js.map +1 -1
- package/dist/core/components/popover.d.ts +1 -1
- package/dist/core/components/popover.js +1 -1
- package/dist/core/components/popover.js.map +1 -1
- package/dist/core/components/section.js +7 -11
- package/dist/core/components/section.js.map +1 -1
- package/dist/core/components/segmented-pills.js +1 -1
- package/dist/core/components/segmented-pills.js.map +1 -1
- package/dist/core/components/stack.js +9 -2
- package/dist/core/components/stack.js.map +1 -1
- package/dist/core/components/tabs.js +2 -2
- package/dist/core/components/tabs.js.map +1 -1
- package/dist/core/nodes.d.ts +6 -0
- package/dist/core/nodes.js +13 -0
- package/dist/core/nodes.js.map +1 -0
- package/dist/core/renderer.js +1 -1
- package/dist/core/types.d.ts +3 -1
- package/dist/create-app.d.ts +21 -0
- package/dist/create-app.js +31 -0
- package/dist/create-app.js.map +1 -0
- package/dist/effects/registry.js +2 -1
- package/dist/effects/registry.js.map +1 -1
- package/dist/effects/use-effect-dispatcher.js +1 -1
- package/dist/effects/use-flash-effects.js +1 -1
- package/dist/events/event-names.d.ts +3 -2
- package/dist/events/event-names.js +3 -2
- package/dist/events/event-names.js.map +1 -1
- package/dist/form/components/base/affix-group.d.ts +17 -0
- package/dist/form/components/base/affix-group.js +49 -0
- package/dist/form/components/base/affix-group.js.map +1 -0
- package/dist/form/components/base/input-otp.d.ts +7 -0
- package/dist/form/components/base/input-otp.js +30 -0
- package/dist/form/components/base/input-otp.js.map +1 -0
- package/dist/form/components/base/label.js +1 -1
- package/dist/form/components/base/label.js.map +1 -1
- package/dist/form/components/base/submit-button.js +1 -1
- package/dist/form/components/base/submit-button.js.map +1 -1
- package/dist/form/components/base/textarea.js +1 -1
- package/dist/form/components/base/textarea.js.map +1 -1
- package/dist/form/components/conditions.js +1 -1
- package/dist/form/components/conditions.js.map +1 -1
- package/dist/form/components/fields/file-upload.js +35 -3
- package/dist/form/components/fields/file-upload.js.map +1 -1
- package/dist/form/components/fields/number-input.js +21 -15
- package/dist/form/components/fields/number-input.js.map +1 -1
- package/dist/form/components/fields/otp-input.d.ts +2 -0
- package/dist/form/components/fields/otp-input.js +25 -0
- package/dist/form/components/fields/otp-input.js.map +1 -0
- package/dist/form/components/fields/password-input.js +21 -15
- package/dist/form/components/fields/password-input.js.map +1 -1
- package/dist/form/components/fields/rich-editor.js +2 -2
- package/dist/form/components/fields/rich-editor.js.map +1 -1
- package/dist/form/components/fields/row-item.js +1 -1
- package/dist/form/components/fields/table-rows.js +1 -1
- package/dist/form/components/fields/text-input.js +19 -13
- package/dist/form/components/fields/text-input.js.map +1 -1
- package/dist/form/components/form.js +2 -2
- package/dist/form/components/form.js.map +1 -1
- package/dist/form/components/index.d.ts +1 -0
- package/dist/form/components/index.js +2 -1
- package/dist/form/components/prefill-targets.d.ts +0 -1
- package/dist/form/components/prefill-targets.js +1 -4
- package/dist/form/components/prefill-targets.js.map +1 -1
- package/dist/form/components/use-form-resolver.js +2 -2
- package/dist/form/components/use-form-resolver.js.map +1 -1
- package/dist/form/index.js +1 -0
- package/dist/form/index.js.map +1 -1
- package/dist/form/skeleton.js +1 -1
- package/dist/form/skeleton.js.map +1 -1
- package/dist/i18n/locale-reload.js +1 -1
- package/dist/i18n/translatable.d.ts +4 -0
- package/dist/i18n/translatable.js +24 -0
- package/dist/i18n/translatable.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +15 -13
- package/dist/lattice.css +99 -6
- package/dist/layout/breadcrumbs.js +3 -2
- package/dist/layout/breadcrumbs.js.map +1 -1
- package/dist/layout/callouts.js +2 -2
- package/dist/layout/callouts.js.map +1 -1
- package/dist/layout/components.js +4 -2
- package/dist/layout/components.js.map +1 -1
- package/dist/layout/dropdown.js +2 -1
- package/dist/layout/dropdown.js.map +1 -1
- package/dist/layout/menu-item.js +33 -12
- package/dist/layout/menu-item.js.map +1 -1
- package/dist/layout/menu.js +2 -1
- package/dist/layout/menu.js.map +1 -1
- package/dist/layout/popover.js +1 -1
- package/dist/layout/sidebar.js +69 -29
- package/dist/layout/sidebar.js.map +1 -1
- package/dist/layout/topbar.d.ts +3 -0
- package/dist/layout/topbar.js +16 -0
- package/dist/layout/topbar.js.map +1 -0
- package/dist/page.js +15 -10
- package/dist/page.js.map +1 -1
- package/dist/realtime/build-effects.d.ts +4 -0
- package/dist/realtime/build-effects.js +23 -0
- package/dist/realtime/build-effects.js.map +1 -0
- package/dist/realtime/listeners.d.ts +8 -0
- package/dist/realtime/listeners.js +31 -0
- package/dist/realtime/listeners.js.map +1 -0
- package/dist/realtime/subscriptions.d.ts +4 -0
- package/dist/realtime/subscriptions.js +45 -0
- package/dist/realtime/subscriptions.js.map +1 -0
- package/dist/registry.js +3 -2
- package/dist/registry.js.map +1 -1
- package/dist/remote/components/data-list.d.ts +10 -0
- package/dist/remote/components/data-list.js +106 -0
- package/dist/remote/components/data-list.js.map +1 -0
- package/dist/remote/index.d.ts +1 -0
- package/dist/remote/index.js +11 -0
- package/dist/remote/index.js.map +1 -0
- package/dist/table/components/bulk-bar.js +10 -15
- package/dist/table/components/bulk-bar.js.map +1 -1
- package/dist/table/components/cells/text-cell.js +38 -3
- package/dist/table/components/cells/text-cell.js.map +1 -1
- package/dist/table/components/column-filter-control.d.ts +2 -1
- package/dist/table/components/column-filter-control.js +28 -19
- package/dist/table/components/column-filter-control.js.map +1 -1
- package/dist/table/components/column-header.js +2 -2
- package/dist/table/components/column-header.js.map +1 -1
- package/dist/table/components/filter-bar.d.ts +7 -6
- package/dist/table/components/filter-bar.js +51 -28
- package/dist/table/components/filter-bar.js.map +1 -1
- package/dist/table/components/filter-controls.js +2 -2
- package/dist/table/components/filter-controls.js.map +1 -1
- package/dist/table/components/filter-stack-bar.js +4 -3
- package/dist/table/components/filter-stack-bar.js.map +1 -1
- package/dist/table/components/filter-value-input.js +1 -1
- package/dist/table/components/filter-value-input.js.map +1 -1
- package/dist/table/components/sort-bar.js +1 -1
- package/dist/table/components/sort-bar.js.map +1 -1
- package/dist/table/components/table.js +26 -17
- package/dist/table/components/table.js.map +1 -1
- package/dist/table/filter-values.d.ts +5 -0
- package/dist/table/filter-values.js +10 -3
- package/dist/table/filter-values.js.map +1 -1
- package/dist/table/query.js +5 -4
- package/dist/table/query.js.map +1 -1
- package/dist/table/use-table.d.ts +1 -0
- package/dist/table/use-table.js +4 -0
- package/dist/table/use-table.js.map +1 -1
- package/dist/toast/toast.js +5 -2
- package/dist/toast/toast.js.map +1 -1
- package/dist/toast/toaster.js +6 -5
- package/dist/toast/toaster.js.map +1 -1
- package/dist/types/generated.d.ts +122 -14
- package/dist/vite.js +32 -1
- package/dist/vite.js.map +1 -1
- package/package.json +33 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.js","names":[],"sources":["../../../resources/js/table/components/table.tsx"],"sourcesContent":["import { type ReactNode, useMemo } from \"react\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useColumnResizing } from \"@lattice-php/lattice/core/use-column-resizing\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { Checkbox } from \"@lattice-php/lattice/core/components/checkbox\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { alignJustifyItems, alignText } from \"../align\";\nimport type { TableNode } from \"../types\";\nimport { getBulkActions } from \"../bulk\";\nimport { flattenColumns, getRowActions, getRowKey } from \"../payload\";\nimport {\n getQueryParams,\n getTableSizingColumns,\n getTableUtilityTracks,\n getVisiblePages,\n} from \"../query\";\nimport { useTable } from \"../use-table\";\nimport { useTableSelection } from \"../use-table-selection\";\nimport { BulkBar } from \"./bulk-bar\";\nimport { ColumnFilterControl } from \"./column-filter-control\";\nimport { ColumnHeader } from \"./column-header\";\nimport { FilterBar } from \"./filter-bar\";\nimport { FilterStackBar } from \"./filter-stack-bar\";\nimport { TablePagination } from \"./pagination\";\nimport { SortBar } from \"./sort-bar\";\nimport { TableActionNode } from \"./table-action-node\";\nimport { ColumnCell } from \"./table-cell\";\n\nconst TableComponent = ({ node }: { children?: ReactNode; node: TableNode }) => {\n const { t } = useT(\"lattice\");\n const {\n columns,\n rows,\n pagination,\n state,\n filters,\n tableFilters,\n addFilter,\n updateFilter,\n removeFilter,\n setTableFilter,\n resetFilters,\n searchFilterOptions,\n processing,\n hasLoaded,\n infiniteLoaderRef,\n sort,\n clearSort,\n goToPage,\n loadMore,\n } = useTable(node);\n\n const bulkActions = useMemo(\n () => getBulkActions(node.props?.bulkActions),\n [node.props?.bulkActions],\n );\n const hasBulkActions = bulkActions.length > 0;\n const rowEntries = useMemo(\n () =>\n rows.map((row, index) => ({ row, actions: getRowActions(row), key: getRowKey(row, index) })),\n [rows],\n );\n const selection = useTableSelection(rowEntries.map((entry) => entry.key));\n\n const columnsByKey = useMemo(\n () => new Map(flattenColumns(columns).map((column) => [column.key, column])),\n [columns],\n );\n const currentPage = pagination.currentPage ?? state.page;\n const lastPage = pagination.lastPage ?? currentPage;\n const mode = pagination.mode ?? \"table\";\n const visiblePages = getVisiblePages(currentPage, lastPage);\n const hasNextPage = pagination.hasMore ?? currentPage < lastPage;\n const hasActions = rowEntries.some((entry) => entry.actions.length > 0);\n const striped = node.props?.striped === true;\n const hasFilters = columns.some((column) => column.filter?.enabled);\n const filterEntries = filters.map((clause, index) => ({ clause, index }));\n const filterDefinitions = useMemo(\n () => (Array.isArray(node.props?.filters) ? node.props.filters : []),\n [node.props?.filters],\n );\n const hasActiveFilters = filters.length > 0 || Object.keys(tableFilters).length > 0;\n const sizingColumns = useMemo(() => getTableSizingColumns(columns), [columns]);\n const utilityTracks = useMemo(\n () => getTableUtilityTracks(hasActions, hasBulkActions),\n [hasActions, hasBulkActions],\n );\n const resizingEnabled = node.props?.resizableColumns === true;\n const resizeStorageIdentity = nodeIdentity(node);\n const { getResizeHandleProps, gridTemplateColumns, hasOverrides, resetColumns } =\n useColumnResizing({\n columns: sizingColumns,\n enabled: resizingEnabled,\n leadingTracks: utilityTracks.leadingTracks,\n showIndicator: node.props?.resizeIndicator === true,\n storageKey:\n resizingEnabled && resizeStorageIdentity\n ? `lattice:table-columns:${resizeStorageIdentity}`\n : undefined,\n trailingTracks: utilityTracks.trailingTracks,\n });\n\n return (\n <div data-lattice-component={node.id} className=\"relative\">\n {hasOverrides && (\n <button\n aria-label={t(\"a11y.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(\"a11y.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 rounded-lt-sm border border-lt-border\">\n {filterDefinitions.length > 0 && (\n <FilterBar\n filters={filterDefinitions}\n values={tableFilters}\n processing={processing}\n hasActiveFilters={hasActiveFilters}\n onChange={setTableFilter}\n onReset={resetFilters}\n onSearch={searchFilterOptions}\n />\n )}\n {hasBulkActions && selection.active && (\n <BulkBar\n actions={bulkActions}\n selectedKeys={selection.selectedKeys}\n allMatching={selection.allMatching}\n total={pagination.total ?? undefined}\n query={getQueryParams(state)}\n canSelectAllMatching={\n selection.allVisibleSelected &&\n !selection.allMatching &&\n pagination.total != null &&\n pagination.total > selection.selectedKeys.length\n }\n onSelectAllMatching={selection.selectAllMatching}\n onCompleted={selection.clear}\n />\n )}\n {filters.length > 0 && (\n <FilterStackBar\n filters={filters}\n columnsByKey={columnsByKey}\n processing={processing}\n onRemove={removeFilter}\n />\n )}\n {state.sorts.length > 0 && (\n <SortBar\n columnsByKey={columnsByKey}\n state={state}\n processing={processing}\n onClear={clearSort}\n />\n )}\n <div className=\"min-w-full text-sm\" role=\"table\">\n <div className=\"border-b border-lt-border bg-lt-muted/50\" role=\"rowgroup\">\n <div\n className=\"hidden min-w-full md:grid md:grid-cols-[var(--lattice-table-columns)]\"\n role=\"row\"\n style={{ \"--lattice-table-columns\": gridTemplateColumns } as never}\n >\n {hasBulkActions && (\n <div className=\"flex items-center px-4 py-3\" role=\"columnheader\">\n <Checkbox\n aria-label={t(\"a11y.selectAllRows\", \"Select all rows\")}\n data-test=\"select-all\"\n checked={selection.allSelected}\n onCheckedChange={() => selection.toggleAll()}\n />\n </div>\n )}\n {columns.map((column, index) => (\n <ColumnHeader\n column={column}\n key={column.key}\n processing={processing}\n resizeHandleProps={\n resizingEnabled ? getResizeHandleProps(sizingColumns[index]) : undefined\n }\n sort={sort}\n state={state}\n />\n ))}\n {hasActions && (\n <div\n className=\"px-4 py-3 text-right align-middle font-medium text-lt-muted-fg\"\n role=\"columnheader\"\n >\n <span className=\"sr-only\">{node.props?.actionsLabel}</span>\n </div>\n )}\n </div>\n {hasFilters && (\n <div\n className=\"hidden min-w-full border-t border-lt-border md:grid md:grid-cols-[var(--lattice-table-columns)]\"\n role=\"row\"\n style={{ \"--lattice-table-columns\": gridTemplateColumns } as never}\n >\n {hasBulkActions && <div className=\"px-4 py-2\" role=\"cell\" />}\n {columns.map((column) => (\n <div key={column.key} className=\"min-w-0 px-2 py-2\" role=\"cell\">\n {column.filter?.enabled && (\n <ColumnFilterControl\n column={column}\n clauses={filterEntries.filter((entry) => entry.clause.field === column.key)}\n processing={processing}\n onAdd={addFilter}\n onUpdate={updateFilter}\n onRemove={removeFilter}\n onSearch={(query) => searchFilterOptions(column.key, query)}\n />\n )}\n </div>\n ))}\n {hasActions && <div className=\"px-4 py-2\" role=\"cell\" />}\n </div>\n )}\n </div>\n <div role=\"rowgroup\">\n {!hasLoaded ? (\n <div className=\"p-4 text-lt-muted-fg\" role=\"row\">\n <div role=\"cell\">{t(\"table.loading\", \"Loading rows...\")}</div>\n </div>\n ) : rowEntries.length === 0 ? (\n <div className=\"p-8 text-center text-lt-muted-fg\" role=\"row\">\n <div role=\"cell\">{node.props?.emptyLabel}</div>\n </div>\n ) : (\n rowEntries.map(({ row, actions, key }) => {\n return (\n <div\n key={key}\n className={`grid grid-cols-1 border-b border-lt-border last:border-b-0 md:grid-cols-[var(--lattice-table-columns)] ${\n striped ? \"odd:bg-lt-muted/30\" : \"\"\n }`}\n role=\"row\"\n style={{ \"--lattice-table-columns\": gridTemplateColumns } as never}\n >\n {hasBulkActions && (\n <div className=\"flex items-center p-4\" role=\"cell\">\n <Checkbox\n aria-label={t(\"a11y.selectRow\", \"Select row {{key}}\", { key })}\n data-test={`select-row-${key}`}\n checked={selection.isSelected(key)}\n onCheckedChange={() => selection.toggle(key)}\n />\n </div>\n )}\n {columns.map((column) => (\n <div\n key={column.key}\n className={cn(\n \"grid min-w-0 gap-1 p-4 align-middle\",\n alignText(column.align),\n alignJustifyItems(column.align),\n )}\n role=\"cell\"\n >\n <span\n aria-hidden=\"true\"\n className=\"text-xs font-medium text-lt-muted-fg md:hidden\"\n >\n {column.label}\n </span>\n <div className=\"min-w-0 truncate\">\n <ColumnCell column={column} row={row} />\n </div>\n </div>\n ))}\n {actions.length > 0 && (\n <div\n className=\"flex items-center justify-start gap-2 p-4 md:justify-end\"\n role=\"cell\"\n >\n {actions.map((action, actionIndex) => (\n <TableActionNode\n key={action.key ?? action.id ?? actionIndex}\n node={action}\n />\n ))}\n </div>\n )}\n </div>\n );\n })\n )}\n </div>\n </div>\n {hasLoaded && (\n <TablePagination\n pagination={pagination}\n currentPage={currentPage}\n processing={processing}\n mode={mode}\n hasNextPage={hasNextPage}\n visiblePages={visiblePages}\n infiniteLoaderRef={infiniteLoaderRef}\n onPage={goToPage}\n onLoadMore={loadMore}\n />\n )}\n </div>\n </div>\n );\n};\n\nexport default TableComponent;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"table.js","names":[],"sources":["../../../resources/js/table/components/table.tsx"],"sourcesContent":["import { type ReactNode, useMemo } from \"react\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useColumnResizing } from \"@lattice-php/lattice/core/use-column-resizing\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { Checkbox } from \"@lattice-php/lattice/core/components/checkbox\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { alignJustifyItems, alignText } from \"../align\";\nimport type { TableNode } from \"../types\";\nimport { getBulkActions } from \"../bulk\";\nimport { flattenColumns, getRowActions, getRowKey } from \"../payload\";\nimport {\n getQueryParams,\n getTableSizingColumns,\n getTableUtilityTracks,\n getVisiblePages,\n} from \"../query\";\nimport { useTable } from \"../use-table\";\nimport { useTableSelection } from \"../use-table-selection\";\nimport { BulkBar } from \"./bulk-bar\";\nimport { ColumnFilterControl } from \"./column-filter-control\";\nimport { ColumnHeader } from \"./column-header\";\nimport { FilterBar, FilterMenu } from \"./filter-bar\";\nimport { FilterStackBar } from \"./filter-stack-bar\";\nimport { TablePagination } from \"./pagination\";\nimport { SortBar } from \"./sort-bar\";\nimport { TableActionNode } from \"./table-action-node\";\nimport { ColumnCell } from \"./table-cell\";\n\nconst TableComponent = ({ node }: { children?: ReactNode; node: TableNode }) => {\n const { t } = useT(\"lattice\");\n const {\n columns,\n rows,\n pagination,\n state,\n filters,\n tableFilters,\n addFilter,\n updateFilter,\n removeFilter,\n replaceColumnFilters,\n setTableFilter,\n resetFilters,\n searchFilterOptions,\n processing,\n hasLoaded,\n infiniteLoaderRef,\n sort,\n clearSort,\n goToPage,\n loadMore,\n } = useTable(node);\n\n const bulkActions = useMemo(\n () => getBulkActions(node.props?.bulkActions),\n [node.props?.bulkActions],\n );\n const hasBulkActions = bulkActions.length > 0;\n const rowEntries = useMemo(\n () =>\n rows.map((row, index) => ({ row, actions: getRowActions(row), key: getRowKey(row, index) })),\n [rows],\n );\n const selection = useTableSelection(rowEntries.map((entry) => entry.key));\n\n const columnsByKey = useMemo(\n () => new Map(flattenColumns(columns).map((column) => [column.key, column])),\n [columns],\n );\n const currentPage = pagination.currentPage ?? state.page;\n const lastPage = pagination.lastPage ?? currentPage;\n const mode = pagination.mode ?? \"table\";\n const visiblePages = getVisiblePages(currentPage, lastPage);\n const hasNextPage = pagination.hasMore ?? currentPage < lastPage;\n const hasActions = rowEntries.some((entry) => entry.actions.length > 0);\n const striped = node.props?.striped === true;\n const hasFilters = columns.some((column) => column.filter?.enabled);\n const filterEntries = filters.map((clause, index) => ({ clause, index }));\n const filterDefinitions = useMemo(\n () => (Array.isArray(node.props?.filters) ? node.props.filters : []),\n [node.props?.filters],\n );\n const hasDedicatedFilters = filterDefinitions.length > 0;\n const hasActiveFilters = filters.length > 0 || Object.keys(tableFilters).length > 0;\n const hasTrailingUtility = hasActions || hasDedicatedFilters;\n const sizingColumns = useMemo(() => getTableSizingColumns(columns), [columns]);\n const utilityTracks = useMemo(\n () => getTableUtilityTracks(hasTrailingUtility, hasBulkActions),\n [hasTrailingUtility, hasBulkActions],\n );\n const resizingEnabled = node.props?.resizableColumns === true;\n const resizeStorageIdentity = nodeIdentity(node);\n const { getResizeHandleProps, gridTemplateColumns, hasOverrides, resetColumns } =\n useColumnResizing({\n columns: sizingColumns,\n enabled: resizingEnabled,\n leadingTracks: utilityTracks.leadingTracks,\n showIndicator: node.props?.resizeIndicator === true,\n storageKey:\n resizingEnabled && resizeStorageIdentity\n ? `lattice:table-columns:${resizeStorageIdentity}`\n : undefined,\n trailingTracks: utilityTracks.trailingTracks,\n });\n\n return (\n <div data-lattice-component={node.id} className=\"relative\">\n {hasOverrides && (\n <button\n aria-label={t(\"a11y.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(\"a11y.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 rounded-lt-sm border border-lt-border\">\n {hasDedicatedFilters && hasActiveFilters && (\n <FilterBar\n filters={filterDefinitions}\n values={tableFilters}\n processing={processing}\n hasActiveFilters={hasActiveFilters}\n onChange={setTableFilter}\n onReset={resetFilters}\n />\n )}\n {hasBulkActions && selection.active && (\n <BulkBar\n actions={bulkActions}\n selectedKeys={selection.selectedKeys}\n allMatching={selection.allMatching}\n total={pagination.total ?? undefined}\n query={getQueryParams(state)}\n canSelectAllMatching={\n selection.allVisibleSelected &&\n !selection.allMatching &&\n pagination.total != null &&\n pagination.total > selection.selectedKeys.length\n }\n onSelectAllMatching={selection.selectAllMatching}\n onCompleted={selection.clear}\n />\n )}\n {filters.length > 0 && (\n <FilterStackBar\n filters={filters}\n columnsByKey={columnsByKey}\n processing={processing}\n onRemove={removeFilter}\n />\n )}\n {state.sorts.length > 0 && (\n <SortBar\n columnsByKey={columnsByKey}\n state={state}\n processing={processing}\n onClear={clearSort}\n />\n )}\n <div className=\"min-w-full text-base\" role=\"table\">\n <div className=\"border-b border-lt-border bg-lt-muted/50\" role=\"rowgroup\">\n <div\n className=\"hidden min-w-full md:grid md:grid-cols-[var(--lattice-table-columns)]\"\n role=\"row\"\n style={{ \"--lattice-table-columns\": gridTemplateColumns } as never}\n >\n {hasBulkActions && (\n <div className=\"flex items-center px-4 py-3\" role=\"columnheader\">\n <Checkbox\n aria-label={t(\"a11y.selectAllRows\", \"Select all rows\")}\n data-test=\"select-all\"\n checked={selection.allSelected}\n onCheckedChange={() => selection.toggleAll()}\n />\n </div>\n )}\n {columns.map((column, index) => (\n <ColumnHeader\n column={column}\n key={column.key}\n processing={processing}\n resizeHandleProps={\n resizingEnabled ? getResizeHandleProps(sizingColumns[index]) : undefined\n }\n sort={sort}\n state={state}\n />\n ))}\n {hasTrailingUtility && (\n <div\n className=\"flex items-center justify-end gap-2 px-4 py-2 align-middle font-semibold text-lt-fg\"\n role=\"columnheader\"\n >\n {hasDedicatedFilters && (\n <FilterMenu\n filters={filterDefinitions}\n values={tableFilters}\n processing={processing}\n onChange={setTableFilter}\n onSearch={searchFilterOptions}\n />\n )}\n {hasActions && <span className=\"sr-only\">{node.props?.actionsLabel}</span>}\n </div>\n )}\n </div>\n {hasFilters && (\n <div\n className=\"hidden min-w-full border-t border-lt-border md:grid md:grid-cols-[var(--lattice-table-columns)]\"\n role=\"row\"\n style={{ \"--lattice-table-columns\": gridTemplateColumns } as never}\n >\n {hasBulkActions && <div className=\"px-4 py-2\" role=\"cell\" />}\n {columns.map((column) => (\n <div key={column.key} className=\"min-w-0 px-2 py-2\" role=\"cell\">\n {column.filter?.enabled && (\n <ColumnFilterControl\n column={column}\n clauses={filterEntries.filter((entry) => entry.clause.field === column.key)}\n processing={processing}\n onAdd={addFilter}\n onUpdate={updateFilter}\n onRemove={removeFilter}\n onReplace={replaceColumnFilters}\n onSearch={(query) => searchFilterOptions(column.key, query)}\n />\n )}\n </div>\n ))}\n {hasTrailingUtility && <div className=\"px-4 py-2\" role=\"cell\" />}\n </div>\n )}\n </div>\n <div role=\"rowgroup\">\n {!hasLoaded ? (\n <div className=\"p-4 text-lt-muted-fg\" role=\"row\">\n <div role=\"cell\">{t(\"table.loading\", \"Loading rows...\")}</div>\n </div>\n ) : rowEntries.length === 0 ? (\n <div className=\"p-8 text-center text-lt-muted-fg\" role=\"row\">\n <div role=\"cell\">{node.props?.emptyLabel}</div>\n </div>\n ) : (\n rowEntries.map(({ row, actions, key }) => {\n return (\n <div\n key={key}\n className={`grid grid-cols-1 border-b border-lt-border last:border-b-0 md:grid-cols-[var(--lattice-table-columns)] ${\n striped ? \"odd:bg-lt-muted/30\" : \"\"\n }`}\n role=\"row\"\n style={{ \"--lattice-table-columns\": gridTemplateColumns } as never}\n >\n {hasBulkActions && (\n <div className=\"flex items-center p-4\" role=\"cell\">\n <Checkbox\n aria-label={t(\"a11y.selectRow\", \"Select row {{key}}\", { key })}\n data-test={`select-row-${key}`}\n checked={selection.isSelected(key)}\n onCheckedChange={() => selection.toggle(key)}\n />\n </div>\n )}\n {columns.map((column) => (\n <div\n key={column.key}\n className={cn(\n \"grid min-w-0 gap-1 overflow-hidden p-4 align-middle\",\n alignText(column.align),\n alignJustifyItems(column.align),\n )}\n role=\"cell\"\n >\n <span\n aria-hidden=\"true\"\n className=\"text-xs font-medium text-lt-muted-fg md:hidden\"\n >\n {column.label}\n </span>\n <div\n data-slot=\"table-cell-content\"\n className=\"min-w-0 max-w-full overflow-hidden truncate\"\n >\n <ColumnCell column={column} row={row} />\n </div>\n </div>\n ))}\n {hasTrailingUtility && (\n <div\n className={cn(\n \"items-center justify-start gap-2 p-4 md:justify-end\",\n actions.length > 0 ? \"flex\" : \"hidden md:flex\",\n )}\n role=\"cell\"\n >\n {actions.map((action, actionIndex) => (\n <TableActionNode\n key={action.key ?? action.id ?? actionIndex}\n node={action}\n />\n ))}\n </div>\n )}\n </div>\n );\n })\n )}\n </div>\n </div>\n {hasLoaded && (\n <TablePagination\n pagination={pagination}\n currentPage={currentPage}\n processing={processing}\n mode={mode}\n hasNextPage={hasNextPage}\n visiblePages={visiblePages}\n infiniteLoaderRef={infiniteLoaderRef}\n onPage={goToPage}\n onLoadMore={loadMore}\n />\n )}\n </div>\n </div>\n );\n};\n\nexport default TableComponent;\n"],"mappings":"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"}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { FilterData, Option } from '../types/generated';
|
|
2
|
+
/**
|
|
3
|
+
* Whether a single scalar filter member is absent — the atomic rule the
|
|
4
|
+
* filter-emptiness and query-serialization logic both build on.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isEmptyMember(value: unknown): value is null | undefined | "";
|
|
2
7
|
/**
|
|
3
8
|
* Whether a table-filter value should clear the filter rather than apply it —
|
|
4
9
|
* an empty string, empty list, or an object whose every member is empty.
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
//#region resources/js/table/filter-values.ts
|
|
2
2
|
/**
|
|
3
|
+
* Whether a single scalar filter member is absent — the atomic rule the
|
|
4
|
+
* filter-emptiness and query-serialization logic both build on.
|
|
5
|
+
*/
|
|
6
|
+
function isEmptyMember(value) {
|
|
7
|
+
return value == null || value === "";
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
3
10
|
* Whether a table-filter value should clear the filter rather than apply it —
|
|
4
11
|
* an empty string, empty list, or an object whose every member is empty.
|
|
5
12
|
*/
|
|
6
13
|
function isEmptyFilterValue(value) {
|
|
7
|
-
if (value
|
|
14
|
+
if (isEmptyMember(value)) return true;
|
|
8
15
|
if (Array.isArray(value)) return value.length === 0;
|
|
9
|
-
if (typeof value === "object") return Object.values(value).every(
|
|
16
|
+
if (typeof value === "object") return Object.values(value).every(isEmptyMember);
|
|
10
17
|
return false;
|
|
11
18
|
}
|
|
12
19
|
function isActiveFilterValue(value) {
|
|
@@ -25,6 +32,6 @@ function filterOptions(filter) {
|
|
|
25
32
|
return Array.isArray(value) ? value : [];
|
|
26
33
|
}
|
|
27
34
|
//#endregion
|
|
28
|
-
export { filterOptions, isActiveFilterValue, isEmptyFilterValue, stringProp };
|
|
35
|
+
export { filterOptions, isActiveFilterValue, isEmptyFilterValue, isEmptyMember, stringProp };
|
|
29
36
|
|
|
30
37
|
//# sourceMappingURL=filter-values.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-values.js","names":[],"sources":["../../resources/js/table/filter-values.ts"],"sourcesContent":["import type { FilterData, Option } from \"@lattice-php/lattice/types/generated\";\n\n/**\n * Whether a table-filter value should clear the filter rather than apply it —\n * an empty string, empty list, or an object whose every member is empty.\n */\nexport function isEmptyFilterValue(value: unknown): boolean {\n if (value
|
|
1
|
+
{"version":3,"file":"filter-values.js","names":[],"sources":["../../resources/js/table/filter-values.ts"],"sourcesContent":["import type { FilterData, Option } from \"@lattice-php/lattice/types/generated\";\n\n/**\n * Whether a single scalar filter member is absent — the atomic rule the\n * filter-emptiness and query-serialization logic both build on.\n */\nexport function isEmptyMember(value: unknown): value is null | undefined | \"\" {\n return value == null || value === \"\";\n}\n\n/**\n * Whether a table-filter value should clear the filter rather than apply it —\n * an empty string, empty list, or an object whose every member is empty.\n */\nexport function isEmptyFilterValue(value: unknown): boolean {\n if (isEmptyMember(value)) {\n return true;\n }\n\n if (Array.isArray(value)) {\n return value.length === 0;\n }\n\n if (typeof value === \"object\") {\n return Object.values(value).every(isEmptyMember);\n }\n\n return false;\n}\n\nexport function isActiveFilterValue(value: unknown): boolean {\n return !isEmptyFilterValue(value);\n}\n\n/**\n * Read a string entry from a filter's loose `props` bag, falling back when the\n * key is absent or not a string.\n */\nexport function stringProp(filter: FilterData, key: string, fallback: string): string {\n const value = filter.props[key];\n\n return typeof value === \"string\" ? value : fallback;\n}\n\nexport function filterOptions(filter: FilterData): Option[] {\n const value = filter.props.options;\n\n return Array.isArray(value) ? (value as Option[]) : [];\n}\n"],"mappings":";;;;;AAMA,SAAgB,cAAc,OAAgD;CAC5E,OAAO,SAAS,QAAQ,UAAU;AACpC;;;;;AAMA,SAAgB,mBAAmB,OAAyB;CAC1D,IAAI,cAAc,KAAK,GACrB,OAAO;CAGT,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,MAAM,WAAW;CAG1B,IAAI,OAAO,UAAU,UACnB,OAAO,OAAO,OAAO,KAAK,EAAE,MAAM,aAAa;CAGjD,OAAO;AACT;AAEA,SAAgB,oBAAoB,OAAyB;CAC3D,OAAO,CAAC,mBAAmB,KAAK;AAClC;;;;;AAMA,SAAgB,WAAW,QAAoB,KAAa,UAA0B;CACpF,MAAM,QAAQ,OAAO,MAAM;CAE3B,OAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;AAEA,SAAgB,cAAc,QAA8B;CAC1D,MAAM,QAAQ,OAAO,MAAM;CAE3B,OAAO,MAAM,QAAQ,KAAK,IAAK,QAAqB,CAAC;AACvD"}
|
package/dist/table/query.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { translate } from "../i18n/instance.js";
|
|
2
2
|
import "../core/column-sizing.js";
|
|
3
|
+
import { isEmptyMember } from "./filter-values.js";
|
|
3
4
|
//#region resources/js/table/query.ts
|
|
4
5
|
function getColumnSort(state, column) {
|
|
5
6
|
return state.sorts.find((currentSort) => currentSort.key === column.key);
|
|
@@ -30,7 +31,7 @@ function appendTableFilters(url, tableFilters) {
|
|
|
30
31
|
continue;
|
|
31
32
|
}
|
|
32
33
|
if (Array.isArray(value)) {
|
|
33
|
-
for (const item of value) if (item
|
|
34
|
+
for (const item of value) if (!isEmptyMember(item)) url.searchParams.append(`tf[${key}][]`, String(item));
|
|
34
35
|
continue;
|
|
35
36
|
}
|
|
36
37
|
if (typeof value === "object" && value !== null) for (const [subKey, subValue] of Object.entries(value)) url.searchParams.set(`tf[${key}][${subKey}]`, String(subValue));
|
|
@@ -45,14 +46,14 @@ function getTableFilterParams(tableFilters) {
|
|
|
45
46
|
return params;
|
|
46
47
|
}
|
|
47
48
|
function normalizeTableFilterValue(value) {
|
|
48
|
-
if (value
|
|
49
|
+
if (isEmptyMember(value)) return;
|
|
49
50
|
if (typeof value === "string") return value;
|
|
50
51
|
if (Array.isArray(value)) {
|
|
51
|
-
const items = value.filter((item) => item
|
|
52
|
+
const items = value.filter((item) => !isEmptyMember(item)).map(String);
|
|
52
53
|
return items.length > 0 ? items : void 0;
|
|
53
54
|
}
|
|
54
55
|
if (typeof value === "object") {
|
|
55
|
-
const entries = Object.entries(value).filter(([, subValue]) => subValue
|
|
56
|
+
const entries = Object.entries(value).filter(([, subValue]) => !isEmptyMember(subValue)).map(([subKey, subValue]) => [subKey, String(subValue)]);
|
|
56
57
|
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
|
|
57
58
|
}
|
|
58
59
|
return String(value);
|
package/dist/table/query.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.js","names":[],"sources":["../../resources/js/table/query.ts"],"sourcesContent":["import { translate } from \"@lattice-php/lattice/i18n\";\nimport { DEFAULT_COLUMN_WIDTH } from \"@lattice-php/lattice/core/column-sizing\";\nimport type { ColumnWidth } from \"@lattice-php/lattice/types/generated\";\nimport type { TableColumn, TableSort, TableState } from \"./types\";\n\nexport function getColumnSort(state: TableState, column: TableColumn): TableSort | undefined {\n return state.sorts.find((currentSort) => currentSort.key === column.key);\n}\n\nexport function getColumnAriaSort(\n sort: TableSort | undefined,\n): \"ascending\" | \"descending\" | undefined {\n if (sort?.direction === \"asc\") {\n return \"ascending\";\n }\n\n if (sort?.direction === \"desc\") {\n return \"descending\";\n }\n\n return undefined;\n}\n\nexport function buildEndpoint(endpoint: string, state: TableState): string {\n const url = new URL(endpoint, window.location.origin);\n\n if (state.filters.length > 0) {\n url.searchParams.set(\"filter\", serializeFilters(state));\n }\n\n if (state.sorts.length > 0) {\n url.searchParams.set(\"sort\", serializeSorts(state));\n }\n\n appendTableFilters(url, state.tableFilters);\n\n url.searchParams.set(\"page\", String(state.page));\n url.searchParams.set(\"per_page\", String(state.perPage));\n\n return `${url.pathname}${url.search}`;\n}\n\n/**\n * Serialize the dedicated table filters as Laravel-native bracket params:\n * `tf[key]=v`, repeated `tf[key][]=v` for multi-value, and `tf[key][sub]=v`\n * for structured values (e.g. a date range's from/until). Empty values are\n * dropped so an unset filter never reaches the server.\n */\nfunction appendTableFilters(url: URL, tableFilters: Record<string, unknown>): void {\n for (const [key, value] of Object.entries(getTableFilterParams(tableFilters))) {\n if (typeof value === \"string\") {\n url.searchParams.set(`tf[${key}]`, value);\n\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (item != null && item !== \"\") {\n url.searchParams.append(`tf[${key}][]`, String(item));\n }\n }\n\n continue;\n }\n\n if (typeof value === \"object\" && value !== null) {\n for (const [subKey, subValue] of Object.entries(value as Record<string, unknown>)) {\n url.searchParams.set(`tf[${key}][${subKey}]`, String(subValue));\n }\n }\n }\n}\n\nfunction getTableFilterParams(tableFilters: Record<string, unknown>): Record<string, unknown> {\n const params: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(tableFilters)) {\n const normalized = normalizeTableFilterValue(value);\n\n if (normalized !== undefined) {\n params[key] = normalized;\n }\n }\n\n return params;\n}\n\nfunction normalizeTableFilterValue(value: unknown): unknown | undefined {\n if (value == null || value === \"\") {\n return undefined;\n }\n\n if (typeof value === \"string\") {\n return value;\n }\n\n if (Array.isArray(value)) {\n const items = value.filter((item) => item != null && item !== \"\").map(String);\n\n return items.length > 0 ? items : undefined;\n }\n\n if (typeof value === \"object\") {\n const entries = Object.entries(value)\n .filter(([, subValue]) => subValue != null && subValue !== \"\")\n .map(([subKey, subValue]) => [subKey, String(subValue)]);\n\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n }\n\n return String(value);\n}\n\nexport function getQueryParams(state: TableState): Record<string, unknown> {\n const params: Record<string, unknown> = {};\n\n if (state.filters.length > 0) {\n params.filter = serializeFilters(state);\n }\n\n if (state.sorts.length > 0) {\n params.sort = serializeSorts(state);\n }\n\n const tableFilters = getTableFilterParams(state.tableFilters);\n\n if (Object.keys(tableFilters).length > 0) {\n params.tf = tableFilters;\n }\n\n return params;\n}\n\nfunction serializeFilters(state: TableState): string {\n return state.filters\n .map((clause) => `${clause.field}:${clause.operator}:${encodeURIComponent(clause.value)}`)\n .join(\",\");\n}\n\nfunction serializeSorts(state: TableState): string {\n return state.sorts\n .map((sort) => (sort.direction === \"desc\" ? `-${sort.key}` : sort.key))\n .join(\",\");\n}\n\nconst operatorLabels: Record<string, string> = {\n contains: \"contains\",\n starts_with: \"starts with\",\n ends_with: \"ends with\",\n eq: \"equals\",\n neq: \"not equals\",\n gt: \">\",\n gte: \"≥\",\n lt: \"<\",\n lte: \"≤\",\n in: \"in\",\n not_in: \"not in\",\n before: \"before\",\n after: \"after\",\n empty: \"is empty\",\n filled: \"is not empty\",\n};\n\nexport const VALUELESS_FILTER_OPERATORS = new Set<string>([\"empty\", \"filled\"]);\n\nexport function operatorLabel(operator: string): string {\n return translate(\"lattice\", `operators.${operator}`, operatorLabels[operator] ?? operator);\n}\n\nexport function getSortDirectionLabel(direction: string): string {\n return direction === \"desc\" ? \"descending\" : \"ascending\";\n}\n\nexport function nextSort(sorts: TableSort[], column: TableColumn): TableSort[] {\n if (!column.sortable) {\n return sorts;\n }\n\n const currentSort = sorts.find((sort) => sort.key === column.key);\n const remainingSorts = sorts.filter((sort) => sort.key !== column.key);\n\n if (!currentSort) {\n return [...sorts, { key: column.key, direction: \"asc\" }];\n }\n\n if (currentSort.direction === \"asc\") {\n return [...remainingSorts, { key: column.key, direction: \"desc\" }];\n }\n\n return remainingSorts;\n}\n\nexport function getVisiblePages(currentPage: number, lastPage: number): number[] {\n if (lastPage <= 5) {\n return Array.from({ length: lastPage }, (_, index) => index + 1);\n }\n\n const start = Math.max(1, Math.min(currentPage - 2, lastPage - 4));\n\n return Array.from({ length: 5 }, (_, index) => start + index);\n}\n\nexport function getTableSizingColumns(columns: TableColumn[]) {\n return columns.map((column) => ({\n key: column.key,\n label: column.label,\n width: columnWidthOrDefault(column),\n }));\n}\n\nfunction columnWidthOrDefault(column: TableColumn): ColumnWidth {\n return (column.width ??\n (column.type === \"column.stack\" ? \"xl\" : DEFAULT_COLUMN_WIDTH)) as ColumnWidth;\n}\n\nexport function getTableUtilityTracks(hasActions: boolean, hasSelection: boolean) {\n // Utility tracks are fixed because the independent header/filter/body grids would drift with content-sized tracks.\n return {\n leadingTracks: hasSelection ? [\"3rem\"] : [],\n trailingTracks: hasActions ? [\"10rem\"] : [],\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"query.js","names":[],"sources":["../../resources/js/table/query.ts"],"sourcesContent":["import { translate } from \"@lattice-php/lattice/i18n\";\nimport { DEFAULT_COLUMN_WIDTH } from \"@lattice-php/lattice/core/column-sizing\";\nimport type { ColumnWidth } from \"@lattice-php/lattice/types/generated\";\nimport { isEmptyMember } from \"./filter-values\";\nimport type { TableColumn, TableSort, TableState } from \"./types\";\n\nexport function getColumnSort(state: TableState, column: TableColumn): TableSort | undefined {\n return state.sorts.find((currentSort) => currentSort.key === column.key);\n}\n\nexport function getColumnAriaSort(\n sort: TableSort | undefined,\n): \"ascending\" | \"descending\" | undefined {\n if (sort?.direction === \"asc\") {\n return \"ascending\";\n }\n\n if (sort?.direction === \"desc\") {\n return \"descending\";\n }\n\n return undefined;\n}\n\nexport function buildEndpoint(endpoint: string, state: TableState): string {\n const url = new URL(endpoint, window.location.origin);\n\n if (state.filters.length > 0) {\n url.searchParams.set(\"filter\", serializeFilters(state));\n }\n\n if (state.sorts.length > 0) {\n url.searchParams.set(\"sort\", serializeSorts(state));\n }\n\n appendTableFilters(url, state.tableFilters);\n\n url.searchParams.set(\"page\", String(state.page));\n url.searchParams.set(\"per_page\", String(state.perPage));\n\n return `${url.pathname}${url.search}`;\n}\n\n/**\n * Serialize the dedicated table filters as Laravel-native bracket params:\n * `tf[key]=v`, repeated `tf[key][]=v` for multi-value, and `tf[key][sub]=v`\n * for structured values (e.g. a date range's from/until). Empty values are\n * dropped so an unset filter never reaches the server.\n */\nfunction appendTableFilters(url: URL, tableFilters: Record<string, unknown>): void {\n for (const [key, value] of Object.entries(getTableFilterParams(tableFilters))) {\n if (typeof value === \"string\") {\n url.searchParams.set(`tf[${key}]`, value);\n\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (!isEmptyMember(item)) {\n url.searchParams.append(`tf[${key}][]`, String(item));\n }\n }\n\n continue;\n }\n\n if (typeof value === \"object\" && value !== null) {\n for (const [subKey, subValue] of Object.entries(value as Record<string, unknown>)) {\n url.searchParams.set(`tf[${key}][${subKey}]`, String(subValue));\n }\n }\n }\n}\n\nfunction getTableFilterParams(tableFilters: Record<string, unknown>): Record<string, unknown> {\n const params: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(tableFilters)) {\n const normalized = normalizeTableFilterValue(value);\n\n if (normalized !== undefined) {\n params[key] = normalized;\n }\n }\n\n return params;\n}\n\nfunction normalizeTableFilterValue(value: unknown): unknown | undefined {\n if (isEmptyMember(value)) {\n return undefined;\n }\n\n if (typeof value === \"string\") {\n return value;\n }\n\n if (Array.isArray(value)) {\n const items = value.filter((item) => !isEmptyMember(item)).map(String);\n\n return items.length > 0 ? items : undefined;\n }\n\n if (typeof value === \"object\") {\n const entries = Object.entries(value)\n .filter(([, subValue]) => !isEmptyMember(subValue))\n .map(([subKey, subValue]) => [subKey, String(subValue)]);\n\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n }\n\n return String(value);\n}\n\nexport function getQueryParams(state: TableState): Record<string, unknown> {\n const params: Record<string, unknown> = {};\n\n if (state.filters.length > 0) {\n params.filter = serializeFilters(state);\n }\n\n if (state.sorts.length > 0) {\n params.sort = serializeSorts(state);\n }\n\n const tableFilters = getTableFilterParams(state.tableFilters);\n\n if (Object.keys(tableFilters).length > 0) {\n params.tf = tableFilters;\n }\n\n return params;\n}\n\nfunction serializeFilters(state: TableState): string {\n return state.filters\n .map((clause) => `${clause.field}:${clause.operator}:${encodeURIComponent(clause.value)}`)\n .join(\",\");\n}\n\nfunction serializeSorts(state: TableState): string {\n return state.sorts\n .map((sort) => (sort.direction === \"desc\" ? `-${sort.key}` : sort.key))\n .join(\",\");\n}\n\nconst operatorLabels: Record<string, string> = {\n contains: \"contains\",\n starts_with: \"starts with\",\n ends_with: \"ends with\",\n eq: \"equals\",\n neq: \"not equals\",\n gt: \">\",\n gte: \"≥\",\n lt: \"<\",\n lte: \"≤\",\n in: \"in\",\n not_in: \"not in\",\n before: \"before\",\n after: \"after\",\n empty: \"is empty\",\n filled: \"is not empty\",\n};\n\nexport const VALUELESS_FILTER_OPERATORS = new Set<string>([\"empty\", \"filled\"]);\n\nexport function operatorLabel(operator: string): string {\n return translate(\"lattice\", `operators.${operator}`, operatorLabels[operator] ?? operator);\n}\n\nexport function getSortDirectionLabel(direction: string): string {\n return direction === \"desc\" ? \"descending\" : \"ascending\";\n}\n\nexport function nextSort(sorts: TableSort[], column: TableColumn): TableSort[] {\n if (!column.sortable) {\n return sorts;\n }\n\n const currentSort = sorts.find((sort) => sort.key === column.key);\n const remainingSorts = sorts.filter((sort) => sort.key !== column.key);\n\n if (!currentSort) {\n return [...sorts, { key: column.key, direction: \"asc\" }];\n }\n\n if (currentSort.direction === \"asc\") {\n return [...remainingSorts, { key: column.key, direction: \"desc\" }];\n }\n\n return remainingSorts;\n}\n\nexport function getVisiblePages(currentPage: number, lastPage: number): number[] {\n if (lastPage <= 5) {\n return Array.from({ length: lastPage }, (_, index) => index + 1);\n }\n\n const start = Math.max(1, Math.min(currentPage - 2, lastPage - 4));\n\n return Array.from({ length: 5 }, (_, index) => start + index);\n}\n\nexport function getTableSizingColumns(columns: TableColumn[]) {\n return columns.map((column) => ({\n key: column.key,\n label: column.label,\n width: columnWidthOrDefault(column),\n }));\n}\n\nfunction columnWidthOrDefault(column: TableColumn): ColumnWidth {\n return (column.width ??\n (column.type === \"column.stack\" ? \"xl\" : DEFAULT_COLUMN_WIDTH)) as ColumnWidth;\n}\n\nexport function getTableUtilityTracks(hasActions: boolean, hasSelection: boolean) {\n // Utility tracks are fixed because the independent header/filter/body grids would drift with content-sized tracks.\n return {\n leadingTracks: hasSelection ? [\"3rem\"] : [],\n trailingTracks: hasActions ? [\"10rem\"] : [],\n };\n}\n"],"mappings":"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"}
|
|
@@ -10,6 +10,7 @@ export declare function useTable(node: TableNode): {
|
|
|
10
10
|
addFilter: (clause: FilterClause) => void;
|
|
11
11
|
updateFilter: (index: number, clause: FilterClause) => void;
|
|
12
12
|
removeFilter: (index: number) => void;
|
|
13
|
+
replaceColumnFilters: (field: string, clauses: FilterClause[]) => void;
|
|
13
14
|
setTableFilter: (key: string, value: unknown) => void;
|
|
14
15
|
resetFilters: () => void;
|
|
15
16
|
searchFilterOptions: (filterKey: string, query: string) => Promise<Option[]>;
|
package/dist/table/use-table.js
CHANGED
|
@@ -68,6 +68,9 @@ function useTable(node) {
|
|
|
68
68
|
function removeFilter(index) {
|
|
69
69
|
applyFilters(state.filters.filter((_, current) => current !== index));
|
|
70
70
|
}
|
|
71
|
+
function replaceColumnFilters(field, clauses) {
|
|
72
|
+
applyFilters([...state.filters.filter((clause) => clause.field !== field), ...clauses]);
|
|
73
|
+
}
|
|
71
74
|
function setTableFilter(key, value) {
|
|
72
75
|
const next = { ...state.tableFilters };
|
|
73
76
|
if (isEmptyFilterValue(value)) delete next[key];
|
|
@@ -178,6 +181,7 @@ function useTable(node) {
|
|
|
178
181
|
addFilter,
|
|
179
182
|
updateFilter,
|
|
180
183
|
removeFilter,
|
|
184
|
+
replaceColumnFilters,
|
|
181
185
|
setTableFilter,
|
|
182
186
|
resetFilters,
|
|
183
187
|
searchFilterOptions,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-table.js","names":[],"sources":["../../resources/js/table/use-table.ts"],"sourcesContent":["import { apiFetch, apiJson } from \"@lattice-php/lattice/core/api\";\nimport { LATTICE_EVENT, type ReloadComponentEvent } from \"@lattice-php/lattice/events/event-names\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { Option } from \"@lattice-php/lattice/types/generated\";\nimport { isEmptyFilterValue } from \"./filter-values\";\nimport { getColumns, getPagination, getRows, getState } from \"./payload\";\nimport { buildEndpoint, nextSort } from \"./query\";\nimport type {\n FilterClause,\n TableColumn,\n TableNode,\n TableResponse,\n TableSort,\n TableState,\n} from \"./types\";\n\nexport function useTable(node: TableNode) {\n const columns = useMemo(() => getColumns(node.props?.columns), [node.props?.columns]);\n const endpoint = typeof node.props?.endpoint === \"string\" ? node.props.endpoint : null;\n const componentRef = typeof node.props?.ref === \"string\" ? node.props.ref : \"\";\n const isLazy = node.props?.lazy === true;\n const initialState = useMemo(() => getState(node.props?.state), [node.props?.state]);\n const initialRows = useMemo(() => getRows(node.props?.data), [node.props?.data]);\n const initialPagination = useMemo(\n () => getPagination(node.props?.pagination),\n [node.props?.pagination],\n );\n const [rows, setRows] = useState(initialRows);\n const [pagination, setPagination] = useState(initialPagination);\n const [state, setState] = useState(initialState);\n const [processing, setProcessing] = useState(isLazy);\n const [hasLoaded, setHasLoaded] = useState(!isLazy);\n const infiniteLoaderRef = useRef<HTMLDivElement | null>(null);\n const currentPage = pagination.currentPage ?? state.page;\n const isInfinite = (pagination.mode ?? \"table\") === \"infinite\";\n\n const load = useCallback(\n async (nextState: TableState, append = false): Promise<void> => {\n if (!endpoint) {\n return;\n }\n\n setProcessing(true);\n\n try {\n const result = await apiJson<TableResponse>(buildEndpoint(endpoint, nextState), {\n ref: componentRef,\n });\n const resultState = getState(result.state);\n const resultRows = getRows(result.data);\n\n setRows((currentRows) => (append ? [...currentRows, ...resultRows] : resultRows));\n setPagination(getPagination(result.pagination));\n setState(resultState);\n setHasLoaded(true);\n } finally {\n setProcessing(false);\n }\n },\n [endpoint, componentRef],\n );\n\n function sort(column: TableColumn): void {\n void load({\n ...state,\n page: 1,\n sorts: nextSort(state.sorts, column),\n });\n }\n\n function clearSort(sort: TableSort): void {\n void load({\n ...state,\n page: 1,\n sorts: state.sorts.filter((currentSort) => currentSort.key !== sort.key),\n });\n }\n\n function applyFilters(next: FilterClause[]): void {\n const nextState = { ...state, filters: next, page: 1 };\n\n setState(nextState);\n void load(nextState);\n }\n\n function addFilter(clause: FilterClause): void {\n applyFilters([...state.filters, clause]);\n }\n\n function updateFilter(index: number, clause: FilterClause): void {\n applyFilters(state.filters.map((current, position) => (position === index ? clause : current)));\n }\n\n function removeFilter(index: number): void {\n applyFilters(state.filters.filter((_, current) => current !== index));\n }\n\n function setTableFilter(key: string, value: unknown): void {\n const next = { ...state.tableFilters };\n\n if (isEmptyFilterValue(value)) {\n delete next[key];\n } else {\n next[key] = value;\n }\n\n const nextState = { ...state, tableFilters: next, page: 1 };\n\n setState(nextState);\n void load(nextState);\n }\n\n function resetFilters(): void {\n const nextState = { ...state, filters: [], tableFilters: {}, page: 1 };\n\n setState(nextState);\n void load(nextState);\n }\n\n const searchFilterOptions = useCallback(\n async (filterKey: string, query: string): Promise<Option[]> => {\n if (!endpoint) {\n return [];\n }\n\n const url = new URL(endpoint, window.location.origin);\n url.searchParams.set(\"_search\", filterKey);\n url.searchParams.set(\"q\", query);\n\n const response = await apiFetch(`${url.pathname}${url.search}`, {\n ref: componentRef,\n throwOnError: false,\n });\n\n if (!response.ok) {\n return [];\n }\n\n const result = (await response.json()) as { options?: Option[] };\n\n return result.options ?? [];\n },\n [endpoint, componentRef],\n );\n\n function goToPage(page: number): void {\n void load({\n ...state,\n page,\n });\n }\n\n const loadMore = useCallback((): void => {\n if (processing || !pagination.hasMore) {\n return;\n }\n\n void load(\n {\n ...state,\n page: pagination.nextPage ?? currentPage + 1,\n },\n true,\n );\n }, [currentPage, load, pagination.hasMore, pagination.nextPage, processing, state]);\n\n useEffect(() => {\n setRows(initialRows);\n setPagination(initialPagination);\n setState(initialState);\n setProcessing(isLazy);\n setHasLoaded(!isLazy);\n }, [initialRows, initialPagination, initialState, isLazy]);\n\n useEffect(() => {\n if (!isLazy || hasLoaded) {\n return;\n }\n\n void load(state);\n }, [hasLoaded, isLazy, load, state]);\n\n useEffect(() => {\n function reload(event: Event): void {\n const detail = (event as ReloadComponentEvent).detail;\n\n if (detail?.component !== node.id) {\n return;\n }\n\n void load(state);\n }\n\n window.addEventListener(LATTICE_EVENT.reloadComponent, reload);\n\n return () => window.removeEventListener(LATTICE_EVENT.reloadComponent, reload);\n }, [load, node.id, state]);\n\n useEffect(() => {\n if (\n !isInfinite ||\n !pagination.hasMore ||\n processing ||\n !infiniteLoaderRef.current ||\n typeof IntersectionObserver === \"undefined\"\n ) {\n return;\n }\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries.some((entry) => entry.isIntersecting)) {\n loadMore();\n }\n },\n {\n rootMargin: \"240px\",\n },\n );\n\n observer.observe(infiniteLoaderRef.current);\n\n return () => observer.disconnect();\n }, [isInfinite, loadMore, pagination.hasMore, processing]);\n\n return {\n columns,\n rows,\n pagination,\n state,\n filters: state.filters,\n tableFilters: state.tableFilters,\n addFilter,\n updateFilter,\n removeFilter,\n setTableFilter,\n resetFilters,\n searchFilterOptions,\n processing,\n hasLoaded,\n infiniteLoaderRef,\n sort,\n clearSort,\n goToPage,\n loadMore,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-table.js","names":[],"sources":["../../resources/js/table/use-table.ts"],"sourcesContent":["import { apiFetch, apiJson } from \"@lattice-php/lattice/core/api\";\nimport { LATTICE_EVENT, type ReloadComponentEvent } from \"@lattice-php/lattice/events/event-names\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { Option } from \"@lattice-php/lattice/types/generated\";\nimport { isEmptyFilterValue } from \"./filter-values\";\nimport { getColumns, getPagination, getRows, getState } from \"./payload\";\nimport { buildEndpoint, nextSort } from \"./query\";\nimport type {\n FilterClause,\n TableColumn,\n TableNode,\n TableResponse,\n TableSort,\n TableState,\n} from \"./types\";\n\nexport function useTable(node: TableNode) {\n const columns = useMemo(() => getColumns(node.props?.columns), [node.props?.columns]);\n const endpoint = typeof node.props?.endpoint === \"string\" ? node.props.endpoint : null;\n const componentRef = typeof node.props?.ref === \"string\" ? node.props.ref : \"\";\n const isLazy = node.props?.lazy === true;\n const initialState = useMemo(() => getState(node.props?.state), [node.props?.state]);\n const initialRows = useMemo(() => getRows(node.props?.data), [node.props?.data]);\n const initialPagination = useMemo(\n () => getPagination(node.props?.pagination),\n [node.props?.pagination],\n );\n const [rows, setRows] = useState(initialRows);\n const [pagination, setPagination] = useState(initialPagination);\n const [state, setState] = useState(initialState);\n const [processing, setProcessing] = useState(isLazy);\n const [hasLoaded, setHasLoaded] = useState(!isLazy);\n const infiniteLoaderRef = useRef<HTMLDivElement | null>(null);\n const currentPage = pagination.currentPage ?? state.page;\n const isInfinite = (pagination.mode ?? \"table\") === \"infinite\";\n\n const load = useCallback(\n async (nextState: TableState, append = false): Promise<void> => {\n if (!endpoint) {\n return;\n }\n\n setProcessing(true);\n\n try {\n const result = await apiJson<TableResponse>(buildEndpoint(endpoint, nextState), {\n ref: componentRef,\n });\n const resultState = getState(result.state);\n const resultRows = getRows(result.data);\n\n setRows((currentRows) => (append ? [...currentRows, ...resultRows] : resultRows));\n setPagination(getPagination(result.pagination));\n setState(resultState);\n setHasLoaded(true);\n } finally {\n setProcessing(false);\n }\n },\n [endpoint, componentRef],\n );\n\n function sort(column: TableColumn): void {\n void load({\n ...state,\n page: 1,\n sorts: nextSort(state.sorts, column),\n });\n }\n\n function clearSort(sort: TableSort): void {\n void load({\n ...state,\n page: 1,\n sorts: state.sorts.filter((currentSort) => currentSort.key !== sort.key),\n });\n }\n\n function applyFilters(next: FilterClause[]): void {\n const nextState = { ...state, filters: next, page: 1 };\n\n setState(nextState);\n void load(nextState);\n }\n\n function addFilter(clause: FilterClause): void {\n applyFilters([...state.filters, clause]);\n }\n\n function updateFilter(index: number, clause: FilterClause): void {\n applyFilters(state.filters.map((current, position) => (position === index ? clause : current)));\n }\n\n function removeFilter(index: number): void {\n applyFilters(state.filters.filter((_, current) => current !== index));\n }\n\n function replaceColumnFilters(field: string, clauses: FilterClause[]): void {\n applyFilters([...state.filters.filter((clause) => clause.field !== field), ...clauses]);\n }\n\n function setTableFilter(key: string, value: unknown): void {\n const next = { ...state.tableFilters };\n\n if (isEmptyFilterValue(value)) {\n delete next[key];\n } else {\n next[key] = value;\n }\n\n const nextState = { ...state, tableFilters: next, page: 1 };\n\n setState(nextState);\n void load(nextState);\n }\n\n function resetFilters(): void {\n const nextState = { ...state, filters: [], tableFilters: {}, page: 1 };\n\n setState(nextState);\n void load(nextState);\n }\n\n const searchFilterOptions = useCallback(\n async (filterKey: string, query: string): Promise<Option[]> => {\n if (!endpoint) {\n return [];\n }\n\n const url = new URL(endpoint, window.location.origin);\n url.searchParams.set(\"_search\", filterKey);\n url.searchParams.set(\"q\", query);\n\n const response = await apiFetch(`${url.pathname}${url.search}`, {\n ref: componentRef,\n throwOnError: false,\n });\n\n if (!response.ok) {\n return [];\n }\n\n const result = (await response.json()) as { options?: Option[] };\n\n return result.options ?? [];\n },\n [endpoint, componentRef],\n );\n\n function goToPage(page: number): void {\n void load({\n ...state,\n page,\n });\n }\n\n const loadMore = useCallback((): void => {\n if (processing || !pagination.hasMore) {\n return;\n }\n\n void load(\n {\n ...state,\n page: pagination.nextPage ?? currentPage + 1,\n },\n true,\n );\n }, [currentPage, load, pagination.hasMore, pagination.nextPage, processing, state]);\n\n useEffect(() => {\n setRows(initialRows);\n setPagination(initialPagination);\n setState(initialState);\n setProcessing(isLazy);\n setHasLoaded(!isLazy);\n }, [initialRows, initialPagination, initialState, isLazy]);\n\n useEffect(() => {\n if (!isLazy || hasLoaded) {\n return;\n }\n\n void load(state);\n }, [hasLoaded, isLazy, load, state]);\n\n useEffect(() => {\n function reload(event: Event): void {\n const detail = (event as ReloadComponentEvent).detail;\n\n if (detail?.component !== node.id) {\n return;\n }\n\n void load(state);\n }\n\n window.addEventListener(LATTICE_EVENT.reloadComponent, reload);\n\n return () => window.removeEventListener(LATTICE_EVENT.reloadComponent, reload);\n }, [load, node.id, state]);\n\n useEffect(() => {\n if (\n !isInfinite ||\n !pagination.hasMore ||\n processing ||\n !infiniteLoaderRef.current ||\n typeof IntersectionObserver === \"undefined\"\n ) {\n return;\n }\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries.some((entry) => entry.isIntersecting)) {\n loadMore();\n }\n },\n {\n rootMargin: \"240px\",\n },\n );\n\n observer.observe(infiniteLoaderRef.current);\n\n return () => observer.disconnect();\n }, [isInfinite, loadMore, pagination.hasMore, processing]);\n\n return {\n columns,\n rows,\n pagination,\n state,\n filters: state.filters,\n tableFilters: state.tableFilters,\n addFilter,\n updateFilter,\n removeFilter,\n replaceColumnFilters,\n setTableFilter,\n resetFilters,\n searchFilterOptions,\n processing,\n hasLoaded,\n infiniteLoaderRef,\n sort,\n clearSort,\n goToPage,\n loadMore,\n };\n}\n"],"mappings":"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"}
|
package/dist/toast/toast.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LATTICE_EVENT } from "../events/event-names.js";
|
|
2
|
+
import { isTranslatable } from "../i18n/translatable.js";
|
|
2
3
|
//#region resources/js/toast/toast.ts
|
|
3
4
|
var variants = [
|
|
4
5
|
"success",
|
|
@@ -16,12 +17,14 @@ function isVariant(value) {
|
|
|
16
17
|
function normalizeToastMessage(value) {
|
|
17
18
|
if (typeof value !== "object" || value === null) return null;
|
|
18
19
|
const toast = value;
|
|
19
|
-
|
|
20
|
+
const rawMessage = toast.message;
|
|
21
|
+
const message = typeof rawMessage === "string" ? rawMessage : isTranslatable(rawMessage) ? rawMessage : null;
|
|
22
|
+
if (message === null || message === "") return null;
|
|
20
23
|
return {
|
|
21
24
|
action: toast.action ?? null,
|
|
22
25
|
dismissible: toast.dismissible !== false,
|
|
23
26
|
duration: typeof toast.duration === "number" ? toast.duration : null,
|
|
24
|
-
message
|
|
27
|
+
message,
|
|
25
28
|
persistent: toast.persistent === true,
|
|
26
29
|
variant: isVariant(toast.variant) ? toast.variant : "success"
|
|
27
30
|
};
|
package/dist/toast/toast.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.js","names":[],"sources":["../../resources/js/toast/toast.ts"],"sourcesContent":["import type { ToastMessage, Variant } from \"@lattice-php/lattice/types/generated\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/events/event-names\";\n\nexport type { ToastMessage, Variant };\n\nconst variants = [\"success\", \"info\", \"warning\", \"error\"] as const satisfies readonly Variant[];\n\nexport function isVariant(value: unknown): value is Variant {\n return variants.some((variant) => variant === value);\n}\n\n/**\n * Coerce a raw toast value (server flash payload or event detail) into a\n * ToastMessage, defaulting optional fields, or null when it is malformed.\n */\nexport function normalizeToastMessage(value: unknown): ToastMessage | null {\n if (typeof value !== \"object\" || value === null) {\n return null;\n }\n\n const toast = value as Record<string, unknown>;\n\n
|
|
1
|
+
{"version":3,"file":"toast.js","names":[],"sources":["../../resources/js/toast/toast.ts"],"sourcesContent":["import type { ToastMessage, Variant } from \"@lattice-php/lattice/types/generated\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/events/event-names\";\nimport { isTranslatable } from \"@lattice-php/lattice/i18n/translatable\";\n\nexport type { ToastMessage, Variant };\n\nconst variants = [\"success\", \"info\", \"warning\", \"error\"] as const satisfies readonly Variant[];\n\nexport function isVariant(value: unknown): value is Variant {\n return variants.some((variant) => variant === value);\n}\n\n/**\n * Coerce a raw toast value (server flash payload or event detail) into a\n * ToastMessage, defaulting optional fields, or null when it is malformed.\n */\nexport function normalizeToastMessage(value: unknown): ToastMessage | null {\n if (typeof value !== \"object\" || value === null) {\n return null;\n }\n\n const toast = value as Record<string, unknown>;\n\n const rawMessage = toast.message;\n const message =\n typeof rawMessage === \"string\" ? rawMessage : isTranslatable(rawMessage) ? rawMessage : null;\n\n if (message === null || message === \"\") {\n return null;\n }\n\n return {\n action: (toast.action as ToastMessage[\"action\"]) ?? null,\n dismissible: toast.dismissible !== false,\n duration: typeof toast.duration === \"number\" ? toast.duration : null,\n message,\n persistent: toast.persistent === true,\n variant: isVariant(toast.variant) ? toast.variant : \"success\",\n };\n}\n\n/** Coerce a `lattice:toast` event detail (`{ toast: {...} }`) into a ToastMessage. */\nexport function normalizeToast(detail: unknown): ToastMessage | null {\n if (typeof detail !== \"object\" || detail === null) {\n return null;\n }\n\n return normalizeToastMessage((detail as { toast?: unknown }).toast);\n}\n\n/** Emit a toast onto the shared `lattice:toast` bus. */\nexport function showToast(toast: ToastMessage): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n window.dispatchEvent(new CustomEvent(LATTICE_EVENT.toast, { detail: { toast } }));\n}\n\n/** Subscribe to the shared toast bus. Returns an unsubscribe function. */\nexport function onToast(callback: (toast: ToastMessage) => void): () => void {\n const listener = (event: Event): void => {\n const toast = normalizeToast((event as CustomEvent).detail);\n\n if (toast) {\n callback(toast);\n }\n };\n\n window.addEventListener(LATTICE_EVENT.toast, listener);\n\n return () => window.removeEventListener(LATTICE_EVENT.toast, listener);\n}\n"],"mappings":";;;AAMA,IAAM,WAAW;CAAC;CAAW;CAAQ;CAAW;AAAO;AAEvD,SAAgB,UAAU,OAAkC;CAC1D,OAAO,SAAS,MAAM,YAAY,YAAY,KAAK;AACrD;;;;;AAMA,SAAgB,sBAAsB,OAAqC;CACzE,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO;CAGT,MAAM,QAAQ;CAEd,MAAM,aAAa,MAAM;CACzB,MAAM,UACJ,OAAO,eAAe,WAAW,aAAa,eAAe,UAAU,IAAI,aAAa;CAE1F,IAAI,YAAY,QAAQ,YAAY,IAClC,OAAO;CAGT,OAAO;EACL,QAAS,MAAM,UAAqC;EACpD,aAAa,MAAM,gBAAgB;EACnC,UAAU,OAAO,MAAM,aAAa,WAAW,MAAM,WAAW;EAChE;EACA,YAAY,MAAM,eAAe;EACjC,SAAS,UAAU,MAAM,OAAO,IAAI,MAAM,UAAU;CACtD;AACF;;AAGA,SAAgB,eAAe,QAAsC;CACnE,IAAI,OAAO,WAAW,YAAY,WAAW,MAC3C,OAAO;CAGT,OAAO,sBAAuB,OAA+B,KAAK;AACpE;;AAGA,SAAgB,UAAU,OAA2B;CACnD,IAAI,OAAO,WAAW,aACpB;CAGF,OAAO,cAAc,IAAI,YAAY,cAAc,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;AAClF;;AAGA,SAAgB,QAAQ,UAAqD;CAC3E,MAAM,YAAY,UAAuB;EACvC,MAAM,QAAQ,eAAgB,MAAsB,MAAM;EAE1D,IAAI,OACF,SAAS,KAAK;CAElB;CAEA,OAAO,iBAAiB,cAAc,OAAO,QAAQ;CAErD,aAAa,OAAO,oBAAoB,cAAc,OAAO,QAAQ;AACvE"}
|
package/dist/toast/toaster.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { cn } from "../lib/utils.js";
|
|
2
2
|
import { Icon } from "../icons/sprite.js";
|
|
3
|
-
import { useT } from "../i18n/instance.js";
|
|
4
|
-
import { onToast } from "./toast.js";
|
|
5
3
|
import { RenderNode } from "../core/renderer.js";
|
|
4
|
+
import { resolveTranslatable } from "../i18n/translatable.js";
|
|
5
|
+
import { onToast } from "./toast.js";
|
|
6
|
+
import { useT } from "../i18n/instance.js";
|
|
6
7
|
import { variantStyles } from "./variant-styles.js";
|
|
7
8
|
import { useEffect, useState } from "react";
|
|
8
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -25,7 +26,7 @@ function Toaster({ duration = 4e3 }) {
|
|
|
25
26
|
duration,
|
|
26
27
|
swipeDirection: "down",
|
|
27
28
|
children: [toasts.map((toast) => /* @__PURE__ */ jsxs(Toast.Root, {
|
|
28
|
-
className: cn("flex items-start gap-3 rounded-lt border border-l-4 border-lt-border bg-lt-popover p-4 text-lt-popover-fg shadow-lg", variantStyles[toast.variant].accent, "data-[state=open]:animate-lt-toast-in data-[state=closed]:animate-lt-toast-out", "data-[swipe=move]:translate-y-[var(--radix-toast-swipe-move-y)] data-[swipe=cancel]:translate-y-0 data-[swipe=cancel]:transition-transform"),
|
|
29
|
+
className: cn("flex items-start gap-3 rounded-lt border border-l-4 border-lt-border bg-lt-popover p-4 text-lt-popover-fg shadow-lt-lg", variantStyles[toast.variant].accent, "data-[state=open]:animate-lt-toast-in data-[state=closed]:animate-lt-toast-out", "data-[swipe=move]:translate-y-[var(--radix-toast-swipe-move-y)] data-[swipe=cancel]:translate-y-0 data-[swipe=cancel]:transition-transform"),
|
|
29
30
|
"data-test": `toast-${toast.variant}`,
|
|
30
31
|
duration: toast.persistent ? Infinity : toast.duration ?? duration,
|
|
31
32
|
onOpenChange: (open) => {
|
|
@@ -37,7 +38,7 @@ function Toaster({ duration = 4e3 }) {
|
|
|
37
38
|
className: "flex min-w-0 flex-1 flex-col gap-2",
|
|
38
39
|
children: [/* @__PURE__ */ jsx(Toast.Title, {
|
|
39
40
|
className: "text-sm text-lt-fg",
|
|
40
|
-
children: toast.message
|
|
41
|
+
children: typeof toast.message === "string" ? toast.message : resolveTranslatable(toast.message, {}, t)
|
|
41
42
|
}), toast.action ? /* @__PURE__ */ jsx("div", {
|
|
42
43
|
className: "flex flex-wrap gap-2",
|
|
43
44
|
children: /* @__PURE__ */ jsx(RenderNode, { node: toast.action })
|
|
@@ -45,7 +46,7 @@ function Toaster({ duration = 4e3 }) {
|
|
|
45
46
|
}),
|
|
46
47
|
toast.dismissible ? /* @__PURE__ */ jsx(Toast.Close, {
|
|
47
48
|
"aria-label": t("a11y.dismiss", "Dismiss"),
|
|
48
|
-
className: "shrink-0 rounded-
|
|
49
|
+
className: "shrink-0 rounded-lt-sm p-1 text-lt-muted-fg transition-colors hover:bg-lt-muted hover:text-lt-fg",
|
|
49
50
|
"data-test": "toast-dismiss",
|
|
50
51
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
51
52
|
name: "x",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toaster.js","names":[],"sources":["../../resources/js/toast/toaster.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport * as Toast from \"@radix-ui/react-toast\";\nimport { useEffect, useState } from \"react\";\nimport { RenderNode } from \"@lattice-php/lattice/core/renderer\";\nimport type { ToastMessage } from \"@lattice-php/lattice/types/generated\";\nimport { onToast } from \"@lattice-php/lattice/toast/toast\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { variantStyles } from \"@lattice-php/lattice/toast/variant-styles\";\n\ntype ToastItem = ToastMessage & { id: number };\n\nlet nextId = 0;\n\nexport function Toaster({ duration = 4000 }: { duration?: number }) {\n const { t } = useT(\"lattice\");\n const [toasts, setToasts] = useState<ToastItem[]>([]);\n\n function dismiss(id: number): void {\n setToasts((current) => current.filter((toast) => toast.id !== id));\n }\n\n useEffect(\n () =>\n onToast((toast) => {\n setToasts((current) => [...current, { ...toast, id: nextId++ }]);\n }),\n [],\n );\n\n return (\n <Toast.Provider duration={duration} swipeDirection=\"down\">\n {toasts.map((toast) => (\n <Toast.Root\n key={toast.id}\n className={cn(\n \"flex items-start gap-3 rounded-lt border border-l-4 border-lt-border bg-lt-popover p-4 text-lt-popover-fg shadow-lg\",\n variantStyles[toast.variant].accent,\n \"data-[state=open]:animate-lt-toast-in data-[state=closed]:animate-lt-toast-out\",\n \"data-[swipe=move]:translate-y-[var(--radix-toast-swipe-move-y)] data-[swipe=cancel]:translate-y-0 data-[swipe=cancel]:transition-transform\",\n )}\n data-test={`toast-${toast.variant}`}\n duration={toast.persistent ? Infinity : (toast.duration ?? duration)}\n onOpenChange={(open) => {\n if (!open) {\n dismiss(toast.id);\n }\n }}\n >\n {variantStyles[toast.variant].icon}\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n <Toast.Title className=\"text-sm text-lt-fg\"
|
|
1
|
+
{"version":3,"file":"toaster.js","names":[],"sources":["../../resources/js/toast/toaster.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport * as Toast from \"@radix-ui/react-toast\";\nimport { useEffect, useState } from \"react\";\nimport { RenderNode } from \"@lattice-php/lattice/core/renderer\";\nimport type { ToastMessage } from \"@lattice-php/lattice/types/generated\";\nimport { onToast } from \"@lattice-php/lattice/toast/toast\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { variantStyles } from \"@lattice-php/lattice/toast/variant-styles\";\nimport { resolveTranslatable } from \"@lattice-php/lattice/i18n/translatable\";\n\ntype ToastItem = ToastMessage & { id: number };\n\nlet nextId = 0;\n\nexport function Toaster({ duration = 4000 }: { duration?: number }) {\n const { t } = useT(\"lattice\");\n const [toasts, setToasts] = useState<ToastItem[]>([]);\n\n function dismiss(id: number): void {\n setToasts((current) => current.filter((toast) => toast.id !== id));\n }\n\n useEffect(\n () =>\n onToast((toast) => {\n setToasts((current) => [...current, { ...toast, id: nextId++ }]);\n }),\n [],\n );\n\n return (\n <Toast.Provider duration={duration} swipeDirection=\"down\">\n {toasts.map((toast) => (\n <Toast.Root\n key={toast.id}\n className={cn(\n \"flex items-start gap-3 rounded-lt border border-l-4 border-lt-border bg-lt-popover p-4 text-lt-popover-fg shadow-lt-lg\",\n variantStyles[toast.variant].accent,\n \"data-[state=open]:animate-lt-toast-in data-[state=closed]:animate-lt-toast-out\",\n \"data-[swipe=move]:translate-y-[var(--radix-toast-swipe-move-y)] data-[swipe=cancel]:translate-y-0 data-[swipe=cancel]:transition-transform\",\n )}\n data-test={`toast-${toast.variant}`}\n duration={toast.persistent ? Infinity : (toast.duration ?? duration)}\n onOpenChange={(open) => {\n if (!open) {\n dismiss(toast.id);\n }\n }}\n >\n {variantStyles[toast.variant].icon}\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n <Toast.Title className=\"text-sm text-lt-fg\">\n {typeof toast.message === \"string\"\n ? toast.message\n : resolveTranslatable(toast.message, {}, t)}\n </Toast.Title>\n {toast.action ? (\n <div className=\"flex flex-wrap gap-2\">\n <RenderNode node={toast.action} />\n </div>\n ) : null}\n </div>\n {toast.dismissible ? (\n <Toast.Close\n aria-label={t(\"a11y.dismiss\", \"Dismiss\")}\n className=\"shrink-0 rounded-lt-sm p-1 text-lt-muted-fg transition-colors hover:bg-lt-muted hover:text-lt-fg\"\n data-test=\"toast-dismiss\"\n >\n <Icon name=\"x\" className=\"size-lt-icon-md\" />\n </Toast.Close>\n ) : null}\n </Toast.Root>\n ))}\n <Toast.Viewport className=\"fixed inset-x-0 bottom-0 z-[100] mx-auto flex w-full max-w-sm flex-col gap-2 p-4 outline-none\" />\n </Toast.Provider>\n );\n}\n"],"mappings":";;;;;;;;;;;AAaA,IAAI,SAAS;AAEb,SAAgB,QAAQ,EAAE,WAAW,OAA+B;CAClE,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,CAAC,QAAQ,aAAa,SAAsB,CAAC,CAAC;CAEpD,SAAS,QAAQ,IAAkB;EACjC,WAAW,YAAY,QAAQ,QAAQ,UAAU,MAAM,OAAO,EAAE,CAAC;CACnE;CAEA,gBAEI,SAAS,UAAU;EACjB,WAAW,YAAY,CAAC,GAAG,SAAS;GAAE,GAAG;GAAO,IAAI;EAAS,CAAC,CAAC;CACjE,CAAC,GACH,CAAC,CACH;CAEA,OACE,qBAAC,MAAM,UAAP;EAA0B;EAAU,gBAAe;YAAnD,CACG,OAAO,KAAK,UACX,qBAAC,MAAM,MAAP;GAEE,WAAW,GACT,0HACA,cAAc,MAAM,SAAS,QAC7B,kFACA,4IACF;GACA,aAAW,SAAS,MAAM;GAC1B,UAAU,MAAM,aAAa,WAAY,MAAM,YAAY;GAC3D,eAAe,SAAS;IACtB,IAAI,CAAC,MACH,QAAQ,MAAM,EAAE;GAEpB;aAdF;IAgBG,cAAc,MAAM,SAAS;IAC9B,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,MAAM,OAAP;MAAa,WAAU;gBACpB,OAAO,MAAM,YAAY,WACtB,MAAM,UACN,oBAAoB,MAAM,SAAS,CAAC,GAAG,CAAC;KACjC,CAAA,GACZ,MAAM,SACL,oBAAC,OAAD;MAAK,WAAU;gBACb,oBAAC,YAAD,EAAY,MAAM,MAAM,OAAS,CAAA;KAC9B,CAAA,IACH,IACD;;IACJ,MAAM,cACL,oBAAC,MAAM,OAAP;KACE,cAAY,EAAE,gBAAgB,SAAS;KACvC,WAAU;KACV,aAAU;eAEV,oBAAC,MAAD;MAAM,MAAK;MAAI,WAAU;KAAmB,CAAA;IACjC,CAAA,IACX;GACM;KArCL,MAAM,EAqCD,CACb,GACD,oBAAC,MAAM,UAAP,EAAgB,WAAU,gGAAiG,CAAA,CAC7G;;AAEpB"}
|