@entifix/react-controls 0.19.0 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/README.md +8 -0
  2. package/dist/i18n/catalog/en.d.ts.map +1 -1
  3. package/dist/i18n/catalog/en.js +55 -9
  4. package/dist/i18n/catalog/en.js.map +1 -1
  5. package/dist/i18n/catalog/es.d.ts +64 -5
  6. package/dist/i18n/catalog/es.d.ts.map +1 -1
  7. package/dist/i18n/catalog/es.js +61 -9
  8. package/dist/i18n/catalog/es.js.map +1 -1
  9. package/dist/i18n/catalog/index.d.ts +102 -10
  10. package/dist/i18n/catalog/index.d.ts.map +1 -1
  11. package/dist/i18n/entity-labels.d.ts +14 -0
  12. package/dist/i18n/entity-labels.d.ts.map +1 -1
  13. package/dist/i18n/entity-labels.js +15 -0
  14. package/dist/i18n/entity-labels.js.map +1 -1
  15. package/dist/i18n/i18n-context.js +2 -2
  16. package/dist/i18n/i18n-context.js.map +1 -1
  17. package/dist/index.d.ts +6 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +6 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/primitives.d.ts +0 -1
  22. package/dist/primitives.d.ts.map +1 -1
  23. package/dist/primitives.js +0 -1
  24. package/dist/primitives.js.map +1 -1
  25. package/dist/theme/index.d.ts +1 -0
  26. package/dist/theme/index.d.ts.map +1 -1
  27. package/dist/theme/index.js +1 -0
  28. package/dist/theme/index.js.map +1 -1
  29. package/dist/theme/theme-context.d.ts +15 -3
  30. package/dist/theme/theme-context.d.ts.map +1 -1
  31. package/dist/theme/theme-context.js +49 -23
  32. package/dist/theme/theme-context.js.map +1 -1
  33. package/dist/theme/theme-cookie.d.ts +9 -0
  34. package/dist/theme/theme-cookie.d.ts.map +1 -0
  35. package/dist/theme/theme-cookie.js +8 -0
  36. package/dist/theme/theme-cookie.js.map +1 -0
  37. package/dist/theme/theme-script.d.ts +8 -6
  38. package/dist/theme/theme-script.d.ts.map +1 -1
  39. package/dist/theme/theme-script.js +21 -12
  40. package/dist/theme/theme-script.js.map +1 -1
  41. package/dist/ui/actions/action-surfaces.d.ts +1 -1
  42. package/dist/ui/actions/action-surfaces.js +1 -1
  43. package/dist/ui/actions/action-surfaces.js.map +1 -1
  44. package/dist/ui/actions/error-actions.d.ts +32 -0
  45. package/dist/ui/actions/error-actions.d.ts.map +1 -0
  46. package/dist/ui/actions/error-actions.js +40 -0
  47. package/dist/ui/actions/error-actions.js.map +1 -0
  48. package/dist/ui/actions/index.d.ts +3 -0
  49. package/dist/ui/actions/index.d.ts.map +1 -1
  50. package/dist/ui/actions/index.js +3 -0
  51. package/dist/ui/actions/index.js.map +1 -1
  52. package/dist/ui/actions/shortcut.d.ts +13 -0
  53. package/dist/ui/actions/shortcut.d.ts.map +1 -0
  54. package/dist/ui/actions/shortcut.js +18 -0
  55. package/dist/ui/actions/shortcut.js.map +1 -0
  56. package/dist/ui/actions/use-verb-invoker.d.ts +55 -0
  57. package/dist/ui/actions/use-verb-invoker.d.ts.map +1 -0
  58. package/dist/ui/actions/use-verb-invoker.js +98 -0
  59. package/dist/ui/actions/use-verb-invoker.js.map +1 -0
  60. package/dist/ui/atoms/cell-value/cell-value.d.ts.map +1 -1
  61. package/dist/ui/atoms/cell-value/cell-value.js +28 -14
  62. package/dist/ui/atoms/cell-value/cell-value.js.map +1 -1
  63. package/dist/ui/atoms/field-control/field-control.d.ts +1 -0
  64. package/dist/ui/atoms/field-control/field-control.d.ts.map +1 -1
  65. package/dist/ui/atoms/field-control/field-control.js +2 -0
  66. package/dist/ui/atoms/field-control/field-control.js.map +1 -1
  67. package/dist/ui/atoms/number-input/quantity-input.d.ts +8 -1
  68. package/dist/ui/atoms/number-input/quantity-input.d.ts.map +1 -1
  69. package/dist/ui/atoms/number-input/quantity-input.js +2 -1
  70. package/dist/ui/atoms/number-input/quantity-input.js.map +1 -1
  71. package/dist/ui/atoms/table/table.d.ts +12 -3
  72. package/dist/ui/atoms/table/table.d.ts.map +1 -1
  73. package/dist/ui/atoms/table/table.js +59 -10
  74. package/dist/ui/atoms/table/table.js.map +1 -1
  75. package/dist/ui/atoms/text/text.d.ts +2 -2
  76. package/dist/ui/atoms/text/text.d.ts.map +1 -1
  77. package/dist/ui/atoms/text/text.js.map +1 -1
  78. package/dist/ui/molecules/action-bar/action-bar.d.ts +76 -0
  79. package/dist/ui/molecules/action-bar/action-bar.d.ts.map +1 -0
  80. package/dist/ui/molecules/action-bar/action-bar.js +218 -0
  81. package/dist/ui/molecules/action-bar/action-bar.js.map +1 -0
  82. package/dist/ui/molecules/action-bar/index.d.ts +2 -0
  83. package/dist/ui/molecules/action-bar/index.d.ts.map +1 -0
  84. package/dist/ui/molecules/action-bar/index.js +3 -0
  85. package/dist/ui/molecules/action-bar/index.js.map +1 -0
  86. package/dist/ui/molecules/bulk-action-bar/bulk-action-bar.d.ts.map +1 -1
  87. package/dist/ui/molecules/bulk-action-bar/bulk-action-bar.js +8 -10
  88. package/dist/ui/molecules/bulk-action-bar/bulk-action-bar.js.map +1 -1
  89. package/dist/ui/molecules/bulk-result/bulk-result.d.ts +3 -3
  90. package/dist/ui/molecules/bulk-result/bulk-result.js +4 -4
  91. package/dist/ui/molecules/bulk-result/bulk-result.js.map +1 -1
  92. package/dist/ui/molecules/confirm-dialog/confirm-dialog.d.ts +24 -9
  93. package/dist/ui/molecules/confirm-dialog/confirm-dialog.d.ts.map +1 -1
  94. package/dist/ui/molecules/confirm-dialog/confirm-dialog.js +36 -11
  95. package/dist/ui/molecules/confirm-dialog/confirm-dialog.js.map +1 -1
  96. package/dist/ui/molecules/entity-record-card/entity-record-card.d.ts +33 -20
  97. package/dist/ui/molecules/entity-record-card/entity-record-card.d.ts.map +1 -1
  98. package/dist/ui/molecules/entity-record-card/entity-record-card.js +53 -20
  99. package/dist/ui/molecules/entity-record-card/entity-record-card.js.map +1 -1
  100. package/dist/ui/molecules/filter-builder/filter-builder.d.ts +8 -2
  101. package/dist/ui/molecules/filter-builder/filter-builder.d.ts.map +1 -1
  102. package/dist/ui/molecules/filter-builder/filter-builder.js +14 -5
  103. package/dist/ui/molecules/filter-builder/filter-builder.js.map +1 -1
  104. package/dist/ui/molecules/filter-chips/filter-chips.d.ts +16 -0
  105. package/dist/ui/molecules/filter-chips/filter-chips.d.ts.map +1 -0
  106. package/dist/ui/molecules/filter-chips/filter-chips.js +73 -0
  107. package/dist/ui/molecules/filter-chips/filter-chips.js.map +1 -0
  108. package/dist/ui/molecules/filter-chips/index.d.ts +2 -0
  109. package/dist/ui/molecules/filter-chips/index.d.ts.map +1 -0
  110. package/dist/ui/molecules/filter-chips/index.js +3 -0
  111. package/dist/ui/molecules/filter-chips/index.js.map +1 -0
  112. package/dist/ui/molecules/menu/menu.d.ts +11 -1
  113. package/dist/ui/molecules/menu/menu.d.ts.map +1 -1
  114. package/dist/ui/molecules/menu/menu.js +62 -2
  115. package/dist/ui/molecules/menu/menu.js.map +1 -1
  116. package/dist/ui/molecules/pagination/pagination.d.ts +5 -0
  117. package/dist/ui/molecules/pagination/pagination.d.ts.map +1 -1
  118. package/dist/ui/molecules/pagination/pagination.js +35 -18
  119. package/dist/ui/molecules/pagination/pagination.js.map +1 -1
  120. package/dist/ui/molecules/region-state/index.d.ts +2 -0
  121. package/dist/ui/molecules/region-state/index.d.ts.map +1 -0
  122. package/dist/ui/molecules/region-state/index.js +3 -0
  123. package/dist/ui/molecules/region-state/index.js.map +1 -0
  124. package/dist/ui/molecules/region-state/region-state.d.ts +42 -0
  125. package/dist/ui/molecules/region-state/region-state.d.ts.map +1 -0
  126. package/dist/ui/molecules/region-state/region-state.js +83 -0
  127. package/dist/ui/molecules/region-state/region-state.js.map +1 -0
  128. package/dist/ui/molecules/table-toolbar/table-toolbar.d.ts +3 -1
  129. package/dist/ui/molecules/table-toolbar/table-toolbar.d.ts.map +1 -1
  130. package/dist/ui/molecules/table-toolbar/table-toolbar.js +3 -2
  131. package/dist/ui/molecules/table-toolbar/table-toolbar.js.map +1 -1
  132. package/dist/ui/molecules/toast-region/toast-region.d.ts +63 -19
  133. package/dist/ui/molecules/toast-region/toast-region.d.ts.map +1 -1
  134. package/dist/ui/molecules/toast-region/toast-region.js +129 -42
  135. package/dist/ui/molecules/toast-region/toast-region.js.map +1 -1
  136. package/dist/ui/organisms/command-palette/command-palette.d.ts.map +1 -1
  137. package/dist/ui/organisms/command-palette/command-palette.js +19 -12
  138. package/dist/ui/organisms/command-palette/command-palette.js.map +1 -1
  139. package/dist/ui/organisms/entity-detail-grid/entity-detail-grid.d.ts.map +1 -1
  140. package/dist/ui/organisms/entity-detail-grid/entity-detail-grid.js +0 -1
  141. package/dist/ui/organisms/entity-detail-grid/entity-detail-grid.js.map +1 -1
  142. package/dist/ui/organisms/entity-form/entity-form.d.ts +1 -1
  143. package/dist/ui/organisms/entity-form/entity-form.d.ts.map +1 -1
  144. package/dist/ui/organisms/entity-form/entity-form.js +322 -214
  145. package/dist/ui/organisms/entity-form/entity-form.js.map +1 -1
  146. package/dist/ui/organisms/entity-form/entity-form.types.d.ts +16 -0
  147. package/dist/ui/organisms/entity-form/entity-form.types.d.ts.map +1 -1
  148. package/dist/ui/organisms/entity-form/entity-form.types.js.map +1 -1
  149. package/dist/ui/organisms/entity-table/entity-table.d.ts +1 -1
  150. package/dist/ui/organisms/entity-table/entity-table.d.ts.map +1 -1
  151. package/dist/ui/organisms/entity-table/entity-table.js +332 -197
  152. package/dist/ui/organisms/entity-table/entity-table.js.map +1 -1
  153. package/dist/ui/organisms/entity-table/entity-table.types.d.ts +19 -4
  154. package/dist/ui/organisms/entity-table/entity-table.types.d.ts.map +1 -1
  155. package/dist/ui/organisms/entity-table/entity-table.types.js.map +1 -1
  156. package/dist/ui/organisms/entity-table/header-sort.d.ts +18 -0
  157. package/dist/ui/organisms/entity-table/header-sort.d.ts.map +1 -0
  158. package/dist/ui/organisms/entity-table/header-sort.js +54 -0
  159. package/dist/ui/organisms/entity-table/header-sort.js.map +1 -0
  160. package/dist/ui/organisms/entity-table/pivot.d.ts +25 -0
  161. package/dist/ui/organisms/entity-table/pivot.d.ts.map +1 -0
  162. package/dist/ui/organisms/entity-table/pivot.js +106 -0
  163. package/dist/ui/organisms/entity-table/pivot.js.map +1 -0
  164. package/dist/ui/organisms/entity-table/use-result-announcement.d.ts +13 -0
  165. package/dist/ui/organisms/entity-table/use-result-announcement.d.ts.map +1 -0
  166. package/dist/ui/organisms/entity-table/use-result-announcement.js +31 -0
  167. package/dist/ui/organisms/entity-table/use-result-announcement.js.map +1 -0
  168. package/dist/ui/organisms/top-bar/live-indicator.d.ts +11 -0
  169. package/dist/ui/organisms/top-bar/live-indicator.d.ts.map +1 -0
  170. package/dist/ui/organisms/top-bar/live-indicator.js +46 -0
  171. package/dist/ui/organisms/top-bar/live-indicator.js.map +1 -0
  172. package/dist/ui/organisms/top-bar/top-bar.d.ts +11 -2
  173. package/dist/ui/organisms/top-bar/top-bar.d.ts.map +1 -1
  174. package/dist/ui/organisms/top-bar/top-bar.js +14 -5
  175. package/dist/ui/organisms/top-bar/top-bar.js.map +1 -1
  176. package/dist/ui/organisms/wizard/wizard.d.ts +1 -1
  177. package/dist/ui/organisms/wizard/wizard.d.ts.map +1 -1
  178. package/dist/ui/organisms/wizard/wizard.js +7 -1
  179. package/dist/ui/organisms/wizard/wizard.js.map +1 -1
  180. package/dist/ui/organisms/wizard/wizard.types.d.ts +8 -0
  181. package/dist/ui/organisms/wizard/wizard.types.d.ts.map +1 -1
  182. package/dist/ui/organisms/wizard/wizard.types.js.map +1 -1
  183. package/dist/ui/utils/live-updates.d.ts +24 -0
  184. package/dist/ui/utils/live-updates.d.ts.map +1 -0
  185. package/dist/ui/utils/live-updates.js +20 -0
  186. package/dist/ui/utils/live-updates.js.map +1 -0
  187. package/dist/ui/utils/use-focus-on-arrival.d.ts +14 -0
  188. package/dist/ui/utils/use-focus-on-arrival.d.ts.map +1 -0
  189. package/dist/ui/utils/use-focus-on-arrival.js +42 -0
  190. package/dist/ui/utils/use-focus-on-arrival.js.map +1 -0
  191. package/dist/ui/utils/use-hotkey.d.ts +44 -25
  192. package/dist/ui/utils/use-hotkey.d.ts.map +1 -1
  193. package/dist/ui/utils/use-hotkey.js +80 -48
  194. package/dist/ui/utils/use-hotkey.js.map +1 -1
  195. package/package.json +5 -5
  196. package/dist/ui/molecules/split-button/index.d.ts +0 -2
  197. package/dist/ui/molecules/split-button/index.d.ts.map +0 -1
  198. package/dist/ui/molecules/split-button/index.js +0 -3
  199. package/dist/ui/molecules/split-button/index.js.map +0 -1
  200. package/dist/ui/molecules/split-button/split-button.d.ts +0 -21
  201. package/dist/ui/molecules/split-button/split-button.d.ts.map +0 -1
  202. package/dist/ui/molecules/split-button/split-button.js +0 -35
  203. package/dist/ui/molecules/split-button/split-button.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/ui/organisms/entity-table/entity-table.tsx"],"sourcesContent":["'use client';\n\nimport {\n emptySelection,\n type Entity,\n type EntityId,\n type EntitySelection,\n type EntitySorting,\n extractMetaEntity,\n type FilterGroup,\n hierarchyOf,\n isSelected,\n selectionSize,\n toggleSelected,\n type UseCaseDescriptor,\n} from '@entifix/core';\nimport { Fragment, type ReactNode, useMemo, useRef, useState } from 'react';\n\nimport { useErrorMessage, useT, useTranslateKey } from '../../../i18n/index.js';\nimport {\n OVERFLOW_GLYPH,\n renderAnchor,\n useCasesForSurface,\n} from '../../actions/index.js';\nimport { Button } from '../../atoms/button/index.js';\nimport { CellValue } from '../../atoms/cell-value/index.js';\nimport { Checkbox } from '../../atoms/field/index.js';\nimport { Skeleton } from '../../atoms/skeleton/index.js';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeaderCell,\n TableMessageRow,\n TableRow,\n} from '../../atoms/table/index.js';\nimport { linkClassName } from '../../atoms/text/index.js';\nimport { BulkActionBar } from '../../molecules/bulk-action-bar/index.js';\nimport { BulkResult } from '../../molecules/bulk-result/index.js';\nimport { ColumnSettings } from '../../molecules/column-settings/index.js';\nimport { ConfirmDialog } from '../../molecules/confirm-dialog/index.js';\nimport { EntityRecordCard } from '../../molecules/entity-record-card/index.js';\nimport { FilterBuilder } from '../../molecules/filter-builder/index.js';\nimport { LoadingBoundary } from '../../molecules/loading-boundary/index.js';\nimport { Menu } from '../../molecules/menu/index.js';\nimport { Pagination } from '../../molecules/pagination/index.js';\nimport { SortBuilder } from '../../molecules/sort-builder/index.js';\nimport { TableToolbar } from '../../molecules/table-toolbar/index.js';\nimport type {\n EntityTableColumn,\n EntityTablePivotBreakpoint,\n EntityTableProps,\n} from './entity-table.types.js';\nimport { readEntityTableSlots } from './entity-table-slots.js';\nimport { shownTreeRows, TreeCell, treeRows } from './entity-table-tree.js';\nimport { useEntityTableColumns } from './use-entity-table-columns.js';\n\n/**\n * The grid and the card list are both rendered, and CSS picks one. A JS\n * breakpoint hook would have to guess during SSR and correct after mount —\n * a hydration mismatch on every page load. Classes are written out in full\n * because Tailwind cannot see a class name that is concatenated at runtime.\n */\nconst PIVOT_CLASS: Record<\n EntityTablePivotBreakpoint,\n { grid: string; cards: string }\n> = {\n sm: { grid: 'hidden sm:block', cards: 'sm:hidden' },\n md: { grid: 'hidden md:block', cards: 'md:hidden' },\n lg: { grid: 'hidden lg:block', cards: 'lg:hidden' },\n};\n\ntype Panel = 'none' | 'filters' | 'sorting';\n\n/**\n * How many placeholder rows the built-in skeleton draws.\n *\n * The column count is taken from the real geometry, because that is the axis a\n * mismatch shifts; the row count is capped instead of tracking `pageSize`, since\n * a fifty-row page would otherwise paint fifty shimmer rows to stand in for\n * content that arrives below the fold anyway.\n */\nconst SKELETON_ROW_CAP = 5;\n\n/**\n * A table that builds itself from an entity's metadata: columns, labels, value\n * formatting and the filter/sort controls all come from `@accessor()`\n * declarations, so listing a new entity needs no bespoke table.\n *\n * Three things layer on top of that default:\n * - **personalization** — column order and visibility, persisted through the\n * UI-preferences port (so it survives reloads and can later move server-side);\n * - **responsiveness** — below `pivotBreakpoint` rows pivot into label/value\n * cards instead of scrolling sideways;\n * - **slots** — `<EntityColumn>` / `<EntityTableHeader>` / `<EntityTableRow>` /\n * `<EntityTableToolbar>` children override any part of the default rendering.\n */\n/**\n * What a pending verb will act on once it is confirmed.\n *\n * A form has one record; a table has a row or a selection, and those reach two\n * different handlers with two different payloads. Carrying the selection itself\n * rather than reading it back at confirm time also removes a branch that could\n * not happen: the bulk bar only renders inside a truthy selection.\n */\ntype PendingTarget<TEntity extends Entity> =\n | { kind: 'row'; item: TEntity }\n | { kind: 'selection'; selection: EntitySelection<TEntity> };\n\nexport function EntityTable<TEntity extends Entity>({\n entityConstructor,\n isLoading,\n skeleton = true,\n error,\n items,\n totalItems,\n currentPage,\n pageSize,\n onPageChange,\n onPageSizeChange,\n hrefFor,\n newHref,\n renderLink = renderAnchor,\n onSelect,\n preferencesKey,\n showControls = true,\n pivotBreakpoint = 'md',\n filtering,\n sorting,\n onFilteringChange,\n onSortingChange,\n selection,\n onSelectionChange,\n metadata,\n isMetadataLoading = false,\n onBulkUseCase,\n onUseCase,\n bulkOutcomes,\n onBulkDismiss,\n onBulkRetry,\n isBulkRunning = false,\n children,\n}: EntityTableProps<TEntity>) {\n const t = useT();\n // A descriptor's label is a runtime catalog key — the typed gate cannot see\n // it, which is why a host's catalog check is what catches a typo.\n const translateKey = useTranslateKey();\n const errorMessage = useErrorMessage();\n const metaEntity = extractMetaEntity(entityConstructor);\n const slots = readEntityTableSlots<TEntity>(children);\n const [panel, setPanel] = useState<Panel>('none');\n\n /**\n * A verb waiting to be confirmed, and what it will act on.\n *\n * ⚠️ **The table had no confirmation flow at all** until a change moved a\n * destructive verb onto it. It read `confirm.tone` — only to pick a tone for\n * a button or a menu item — and then fired on the first click. `unpublish` is\n * declared `destructive`, so a verb reachable from a row menu without this\n * would have been a one-click unpublish with a red label explaining what had\n * already happened.\n *\n * The state holds the confirmation itself rather than the descriptor, so the\n * dialog cannot open for a verb that never asked for one — the same shape\n * `EntityForm` uses, and for the same reason.\n *\n * It carries the **target** too, which the form's does not need: a form has\n * one record and a table has a row or a selection, and those run through two\n * different handlers with two different payloads.\n */\n const [pending, setPending] = useState<\n | {\n key: string;\n labelKey: string;\n confirm: NonNullable<UseCaseDescriptor['confirm']>;\n target: PendingTarget<TEntity>;\n }\n | undefined\n >(undefined);\n\n const { columns, visibleColumns, hidden, setPersonalization } =\n useEntityTableColumns<TEntity>(\n entityConstructor,\n slots.columns,\n items[0],\n `entity-table:${preferencesKey ?? metaEntity.key ?? metaEntity.name}`,\n );\n\n const pivot = PIVOT_CLASS[pivotBreakpoint];\n\n // A picker's table is choosing *one* value; a multi-selection there would\n // offer a set to a form member that holds a single reference, and the two\n // controls would fight over the same row. This is a wiring mistake rather\n // than a state, so it costs the render — the same call `EntityForm` makes\n // for a link source aimed at a member no picker can edit.\n if (onSelect !== undefined && selection !== undefined) {\n throw new Error(\n 'EntityTable was given both `onSelect` and `selection`. A picker picks one ' +\n 'row and a selection accumulates many, so the row control cannot be both. ' +\n 'Drop `selection` inside a picker, or drop `onSelect` outside one.',\n );\n }\n\n /**\n * The selection, or `undefined` when the table is not selectable at all.\n *\n * One value rather than a `selection` prop beside a `selectable` boolean:\n * two of them means every render site has to re-test both, and the second\n * test is unreachable — which reads as a defensive check and is really dead\n * code the coverage gate then cannot close.\n */\n const selectionState =\n selection !== undefined && onSelectionChange !== undefined\n ? selection\n : undefined;\n const selectable = selectionState !== undefined;\n const rowUseCases = useCasesForSurface('row-menu', metadata?.useCases);\n const bulkUseCases = useCasesForSurface('bulk-bar', metadata?.useCases);\n const toolbarUseCases = useCasesForSurface(\n 'table-toolbar',\n metadata?.useCases,\n );\n const hasRowMenu = rowUseCases.length > 0 && onUseCase !== undefined;\n\n /**\n * What a `context-independent` collection verb acts on: everything the\n * current filter matches.\n *\n * Not an empty `ids` selection, which would say \"no rows\" — the verb is\n * available *without* a selection precisely because its subject is the\n * collection, so the honest payload is the same filter the listing is\n * showing, and the same one a \"select all matching\" escalation would send.\n */\n const everythingMatching: EntitySelection<TEntity> = {\n mode: 'matching',\n filtering,\n total: totalItems,\n excluded: new Set<EntityId>(),\n };\n\n const hasRowAction =\n onSelect !== undefined || hrefFor !== undefined || hasRowMenu;\n const columnCount =\n visibleColumns.length + (hasRowAction ? 1 : 0) + (selectable ? 1 : 0);\n\n /**\n * The first load has nothing to show and is held with a skeleton; a refetch\n * already has rows, so it keeps them and only marks the region busy. Splitting\n * the two is what stops every pagination click flashing grey.\n */\n const isFirstLoad = isLoading && items.length === 0;\n const isRefetching = isLoading && items.length > 0;\n\n /** `true`/omitted → the built-in default; a node → that node; `false` → none. */\n const customSkeleton = typeof skeleton === 'boolean' ? undefined : skeleton;\n const showSkeleton = isFirstLoad && skeleton !== false;\n const skeletonRows = Array.from(\n { length: Math.min(pageSize, SKELETON_ROW_CAP) },\n (_, index) => index,\n );\n\n /**\n * The refetch hint, and it is deliberately the only one: rows stay readable\n * and simply dim, because a second skeleton over live data would be two\n * loading signals in one region.\n */\n const busyClass = isRefetching\n ? 'opacity-60 transition-opacity duration-200 ease-smooth'\n : '';\n\n const renderCell = (\n column: EntityTableColumn<TEntity>,\n item: TEntity,\n ): ReactNode =>\n column.render ? (\n column.render(item)\n ) : (\n <CellValue\n value={(item as Record<string, unknown>)[column.name]}\n descriptor={column}\n record={item as Record<string, unknown>}\n />\n );\n\n /**\n * The row a shift-click ranges *from* — the last box the user actually\n * toggled. Held in a ref rather than state because it changes nothing on\n * screen, and re-rendering the whole table on every click to remember an\n * anchor is a cost with no visible return.\n */\n const anchorIndex = useRef<number | undefined>(undefined);\n\n /**\n * An entity that declares a parent renders as a tree: the page arranged\n * depth first, its first column indented, and a subtree hidden while its\n * record is collapsed. Collapsing is view state, so it lives here and is\n * forgotten with the table, like the shift-click anchor above.\n */\n const treeKeys = useMemo(\n () => hierarchyOf(entityConstructor),\n [entityConstructor],\n );\n const [collapsed, setCollapsed] = useState<ReadonlySet<string>>(\n () => new Set(),\n );\n const rows = useMemo(() => treeRows(items, treeKeys), [items, treeKeys]);\n const shown = shownTreeRows(rows, collapsed);\n const shownItems = shown.map(row => row.item);\n const toggleCollapsed = (key: string) =>\n setCollapsed(current => {\n const next = new Set(current);\n if (next.has(key)) next.delete(key);\n else next.add(key);\n return next;\n });\n\n const setSelection = (next: EntitySelection<TEntity>) =>\n onSelectionChange?.(next);\n\n const toggleRow = (\n index: number,\n checked: boolean,\n shiftKey: boolean,\n current: EntitySelection<TEntity>,\n ) => {\n // A shift-click extends the last toggle across the rows *on this page*, in\n // the direction the user dragged, and applies the new state to all of them.\n const from = shiftKey ? (anchorIndex.current ?? index) : index;\n const [start, end] = from <= index ? [from, index] : [index, from];\n\n let next = current;\n // The rows as shown: in a tree the order on screen is not the load's.\n for (const row of shownItems.slice(start, end + 1)) {\n next = toggleSelected(next, row.id, checked);\n }\n\n anchorIndex.current = index;\n setSelection(next);\n };\n\n /**\n * The header box, and it has three states rather than two: none of this\n * page's rows selected, all of them, or some. Without the third the box\n * reads as \"nothing is selected\" while a bulk bar sits below it saying\n * otherwise.\n */\n const pageIds = items.map(item => item.id);\n const selectedOnPage = selectionState\n ? pageIds.filter(id => isSelected(selectionState, id)).length\n : 0;\n const allOnPageSelected =\n pageIds.length > 0 && selectedOnPage === pageIds.length;\n const someOnPageSelected = selectedOnPage > 0 && !allOnPageSelected;\n\n const toggleAllOnPage = (\n checked: boolean,\n current: EntitySelection<TEntity>,\n ) => {\n let next = current;\n for (const id of pageIds) next = toggleSelected(next, id, checked);\n anchorIndex.current = undefined;\n setSelection(next);\n };\n\n /**\n * How a row names itself to a screen reader.\n *\n * The first visible column's value, because that is what the user reads the\n * row *as* — \"Seleccionar Acme\" rather than \"Seleccionar fila 3\", which tells\n * them the position they can already see and not the thing they cannot.\n */\n const rowLabel = (item: TEntity): string => {\n // The first non-identifier column **that this row actually filled in**.\n //\n // An id names a row the way \"row 3\" does: it repeats what the user can\n // already see. But skipping only the id is not enough — measured against\n // the seeded catalog, where `code` is the first non-id column and is empty\n // on every brand, so the label fell straight through to the id and never\n // reached `name`. A column that is blank on this row names it no better\n // than its position does, so the search continues past it.\n //\n // The id remains the last resort, because a row with nothing rendered\n // still has to announce itself.\n const named = visibleColumns.find(column => {\n if (column.type === 'id') return false;\n const value = (item as Record<string, unknown>)[column.name];\n return value != null && String(value) !== '';\n });\n\n return named\n ? String((item as Record<string, unknown>)[named.name])\n : String(item.id);\n };\n\n const selectionCell = (\n item: TEntity,\n index: number,\n // Passed in rather than closed over: this is only ever called under\n // `selectable`, which already proves the selection exists, and reading the\n // optional prop here would add a fallback arm that nothing can reach.\n current: EntitySelection<TEntity>,\n ) => (\n <Checkbox\n // Deliberately no `id`: both pivots are always in the DOM, so an id here\n // would be emitted twice per row. `aria-label` names the control instead,\n // and it names the *record* rather than the position.\n aria-label={t('table.selectRow', { record: rowLabel(item) })}\n checked={isSelected(current, item.id)}\n disabled={isBulkRunning}\n onChange={event =>\n toggleRow(\n index,\n event.currentTarget.checked,\n (event.nativeEvent as MouseEvent).shiftKey === true,\n current,\n )\n }\n />\n );\n\n const runTarget = (key: string, target: PendingTarget<TEntity>) => {\n if (target.kind === 'row') onUseCase?.(key, target.item);\n else onBulkUseCase?.(key, target.selection);\n };\n\n const invoke = (\n useCase: UseCaseDescriptor,\n target: PendingTarget<TEntity>,\n ) => {\n const confirm = useCase.confirm;\n if (confirm) {\n setPending({\n key: useCase.key,\n labelKey: useCase.labelKey,\n confirm,\n target,\n });\n return;\n }\n runTarget(useCase.key, target);\n };\n\n const rowMenu = (item: TEntity) =>\n hasRowMenu ? (\n <Menu>\n <Menu.Trigger aria-label={t('table.actions')}>\n {OVERFLOW_GLYPH}\n </Menu.Trigger>\n <Menu.Items>\n {rowUseCases.map(useCase => (\n <Menu.Item\n key={useCase.key}\n tone={\n useCase.confirm?.tone === 'destructive'\n ? 'destructive'\n : 'neutral'\n }\n onClick={() => invoke(useCase, { kind: 'row', item })}\n >\n {translateKey(useCase.labelKey)}\n </Menu.Item>\n ))}\n </Menu.Items>\n </Menu>\n ) : undefined;\n\n /**\n * Selection wins over navigation: a picker's row must set the value, not send\n * the user away from the form they came from.\n *\n * The row menu sits *beside* whichever of the two applies rather than\n * replacing it — \"open this record\" and \"run a verb on it\" are both true at\n * once, and folding the first into the menu would bury the action every row\n * of a list exists for.\n */\n const recordAction = (item: TEntity) => {\n const primary = onSelect ? (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={() => onSelect(item)}\n >\n {t('table.select')}\n </Button>\n ) : hrefFor ? (\n renderLink({\n href: hrefFor(item.id),\n className: linkClassName,\n children: t('table.open'),\n intent: { kind: 'open', id: item.id },\n })\n ) : undefined;\n\n const menu = rowMenu(item);\n if (!menu) return primary;\n\n return (\n <span className=\"flex items-center gap-2xs\">\n {primary}\n {menu}\n </span>\n );\n };\n\n /**\n * \"No records\" is a statement about the data; after a failed load it would be\n * a lie about it. The distinction matters most to the user who is deciding\n * whether their catalog is empty or their backend is down.\n */\n const emptyMessage = error ? t('table.error') : t('table.empty');\n\n return (\n <div className=\"flex flex-col gap-s\">\n {/*\n One announcement for the whole table, not one per layout. Both the grid\n and the card list are always in the DOM — CSS picks which is visible —\n so a live region inside each meant assistive tech was told twice that\n the table was loading. The shimmer itself is aria-hidden, so this is the\n only thing that carries the news.\n */}\n <span role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {isFirstLoad ? t('table.loading') : ''}\n </span>\n\n {showControls && (\n <TableToolbar\n start={\n <>\n {newHref &&\n renderLink({\n href: newHref,\n className: linkClassName,\n children: t('table.new'),\n intent: { kind: 'new' },\n })}\n {/* Collection-bound verbs that need no selection. They sit with\n `New` rather than in the bulk bar, because the bulk bar only\n exists while rows are ticked and these are always available. */}\n <LoadingBoundary isLoading={isMetadataLoading} lines={0}>\n <>\n {toolbarUseCases.map(useCase => (\n <Button\n key={useCase.key}\n type=\"button\"\n variant={\n useCase.confirm?.tone === 'destructive'\n ? 'destructive'\n : 'secondary'\n }\n size=\"sm\"\n disabled={isBulkRunning}\n onClick={() =>\n onBulkUseCase?.(useCase.key, everythingMatching)\n }\n >\n {translateKey(useCase.labelKey)}\n </Button>\n ))}\n </>\n </LoadingBoundary>\n {slots.toolbar}\n </>\n }\n end={\n <>\n <Button\n type=\"button\"\n variant={panel === 'filters' ? 'primary' : 'secondary'}\n size=\"sm\"\n onClick={() =>\n setPanel(current =>\n current === 'filters' ? 'none' : 'filters',\n )\n }\n >\n {t('table.filters')}\n </Button>\n <Button\n type=\"button\"\n variant={panel === 'sorting' ? 'primary' : 'secondary'}\n size=\"sm\"\n onClick={() =>\n setPanel(current =>\n current === 'sorting' ? 'none' : 'sorting',\n )\n }\n >\n {t('table.sorting')}\n </Button>\n <ColumnSettings\n columns={columns}\n hidden={hidden}\n onChange={setPersonalization}\n />\n </>\n }\n panel={\n panel === 'filters' ? (\n <FilterBuilder<TEntity>\n descriptors={columns.filter(column => column.filterable)}\n value={filtering}\n onChange={(applied: FilterGroup<TEntity>) =>\n onFilteringChange?.(applied)\n }\n />\n ) : panel === 'sorting' ? (\n <SortBuilder<TEntity>\n descriptors={columns.filter(column => column.sortable)}\n value={sorting}\n onChange={(applied: EntitySorting<TEntity>) =>\n onSortingChange?.(applied)\n }\n />\n ) : undefined\n }\n />\n )}\n\n {/*\n The bar appears when there is something selected and something to do\n with it. It sits above the rows rather than floating over them: a fixed\n overlay covers the last row of a short page, which is the row the user\n most likely just ticked.\n */}\n {selectionState && selectionSize(selectionState) > 0 && (\n <BulkActionBar\n count={selectionSize(selectionState)}\n useCases={bulkUseCases}\n busy={isBulkRunning}\n // Offered only when this page is fully ticked and the store holds\n // more than the page — the escalation has somewhere to escalate to.\n matchingTotal={\n selectionState.mode === 'ids' &&\n allOnPageSelected &&\n totalItems > items.length\n ? totalItems\n : undefined\n }\n onSelectAllMatching={() => setSelection(everythingMatching)}\n onClear={() => setSelection(emptySelection<TEntity>())}\n onUseCase={useCase =>\n invoke(useCase, { kind: 'selection', selection: selectionState })\n }\n />\n )}\n\n {pending && (\n <ConfirmDialog\n open\n tone={pending.confirm.tone}\n title={translateKey(pending.labelKey)}\n message={translateKey(pending.confirm.messageKey)}\n busy={isBulkRunning}\n onCancel={() => setPending(undefined)}\n onConfirm={() => {\n const { key, target } = pending;\n setPending(undefined);\n runTarget(key, target);\n }}\n />\n )}\n\n {bulkOutcomes && bulkOutcomes.length > 0 && onBulkDismiss && (\n <BulkResult\n outcomes={bulkOutcomes}\n busy={isBulkRunning}\n labelFor={id => {\n const row = items.find(item => item.id === id);\n return row ? rowLabel(row) : undefined;\n }}\n onRetryFailed={onBulkRetry}\n onDismiss={onBulkDismiss}\n />\n )}\n\n {/*\n Rendered once, outside both layouts, and independently of whether there\n are rows: a refetch that fails leaves the previous page on screen, and\n without this the user would read stale data as current.\n */}\n {error && (\n <p\n role=\"alert\"\n data-testid=\"entity-table-error\"\n className=\"rounded-sm border border-danger bg-danger-subtle px-s py-2xs text-step-sm text-danger\"\n >\n {errorMessage(error)}\n </p>\n )}\n\n {/* Wide viewports: a grid. */}\n <div className={`${pivot.grid} ${busyClass}`}>\n <Table>\n <TableHead>\n {slots.header ? (\n slots.header.render(visibleColumns)\n ) : (\n <tr>\n {selectable && (\n <TableHeaderCell>\n <Checkbox\n aria-label={t('table.selectAllOnPage')}\n checked={allOnPageSelected}\n indeterminate={someOnPageSelected}\n disabled={isBulkRunning || items.length === 0}\n onChange={event =>\n toggleAllOnPage(\n event.currentTarget.checked,\n selectionState,\n )\n }\n />\n </TableHeaderCell>\n )}\n {visibleColumns.map(column => (\n <TableHeaderCell key={column.name}>\n {column.header ?? column.label}\n </TableHeaderCell>\n ))}\n {hasRowAction && (\n <TableHeaderCell>{t('table.actions')}</TableHeaderCell>\n )}\n </tr>\n )}\n </TableHead>\n <TableBody aria-busy={isLoading || undefined}>\n {/* Skeleton rows rather than the word \"Loading\", and one per column\n rather than a single full-width blob: the placeholder occupies\n the real grid, so the swap to rows shifts nothing sideways.\n `TableMessageRow` is deliberately bypassed — it is one centred\n full-span cell and cannot express the geometry. */}\n {showSkeleton &&\n (customSkeleton ? (\n <TableMessageRow colSpan={columnCount}>\n {customSkeleton}\n </TableMessageRow>\n ) : (\n skeletonRows.map(row => (\n <TableRow key={row}>\n {selectable && (\n <TableCell>\n <Skeleton shape=\"line\" className=\"w-4\" />\n </TableCell>\n )}\n {visibleColumns.map(column => (\n <TableCell key={column.name}>\n <Skeleton shape=\"line\" className=\"w-full\" />\n </TableCell>\n ))}\n {hasRowAction && (\n <TableCell>\n <Skeleton shape=\"line\" className=\"w-16\" />\n </TableCell>\n )}\n </TableRow>\n ))\n ))}\n {!isLoading && items.length === 0 && (\n <TableMessageRow colSpan={columnCount}>\n {emptyMessage}\n </TableMessageRow>\n )}\n {shown.map(({ item, depth, hasChildren }, index) =>\n slots.row ? (\n // A Fragment carries the key without wrapping the custom row in\n // an element `<tbody>` would reject.\n <Fragment key={String(item.id ?? index)}>\n {slots.row.render(item, visibleColumns)}\n </Fragment>\n ) : (\n <TableRow key={String(item.id ?? index)}>\n {selectionState && (\n <TableCell>\n {selectionCell(item, index, selectionState)}\n </TableCell>\n )}\n {visibleColumns.map((column, columnIndex) => (\n <TableCell key={column.name}>\n {treeKeys !== undefined && columnIndex === 0 ? (\n <TreeCell\n depth={depth}\n hasChildren={hasChildren}\n expanded={!collapsed.has(treeKeys.idOf(item))}\n label={t(\n collapsed.has(treeKeys.idOf(item))\n ? 'table.expandRow'\n : 'table.collapseRow',\n { record: rowLabel(item) },\n )}\n onToggle={() => toggleCollapsed(treeKeys.idOf(item))}\n >\n {renderCell(column, item)}\n </TreeCell>\n ) : (\n renderCell(column, item)\n )}\n </TableCell>\n ))}\n {hasRowAction && <TableCell>{recordAction(item)}</TableCell>}\n </TableRow>\n ),\n )}\n </TableBody>\n </Table>\n </div>\n\n {/* Narrow viewports: the same columns pivoted into cards. */}\n <div\n className={`${pivot.cards} flex flex-col gap-2xs ${busyClass}`}\n aria-busy={isLoading || undefined}\n >\n {/* The card pivot gets card-shaped placeholders, not a bare shimmer\n block: the border, radius and padding are the geometry here. */}\n {showSkeleton &&\n (customSkeleton ??\n skeletonRows.map(row => (\n <EntityRecordCard\n key={row}\n columns={visibleColumns}\n renderCell={() => <Skeleton shape=\"line\" className=\"w-24\" />}\n />\n )))}\n {!isLoading && items.length === 0 && (\n <p className=\"text-step-sm text-content-muted\">{emptyMessage}</p>\n )}\n {shown.map(({ item }, index) => (\n <EntityRecordCard\n key={String(item.id ?? index)}\n columns={visibleColumns}\n // The card is handed the resolved columns themselves, so the\n // narrow layout goes through the exact same renderers as the grid.\n renderCell={column =>\n renderCell(column as EntityTableColumn<TEntity>, item)\n }\n leading={\n selectionState\n ? selectionCell(item, index, selectionState)\n : undefined\n }\n actions={recordAction(item)}\n />\n ))}\n </div>\n\n <Pagination\n currentPage={currentPage}\n pageSize={pageSize}\n totalItems={totalItems}\n onPageChange={onPageChange}\n onPageSizeChange={onPageSizeChange}\n />\n\n {slots.rest}\n </div>\n );\n}\n"],"names":["emptySelection","extractMetaEntity","hierarchyOf","isSelected","selectionSize","toggleSelected","Fragment","useMemo","useRef","useState","useErrorMessage","useT","useTranslateKey","OVERFLOW_GLYPH","renderAnchor","useCasesForSurface","Button","CellValue","Checkbox","Skeleton","Table","TableBody","TableCell","TableHead","TableHeaderCell","TableMessageRow","TableRow","linkClassName","BulkActionBar","BulkResult","ColumnSettings","ConfirmDialog","EntityRecordCard","FilterBuilder","LoadingBoundary","Menu","Pagination","SortBuilder","TableToolbar","readEntityTableSlots","shownTreeRows","TreeCell","treeRows","useEntityTableColumns","PIVOT_CLASS","sm","grid","cards","md","lg","SKELETON_ROW_CAP","EntityTable","entityConstructor","isLoading","skeleton","error","items","totalItems","currentPage","pageSize","onPageChange","onPageSizeChange","hrefFor","newHref","renderLink","onSelect","preferencesKey","showControls","pivotBreakpoint","filtering","sorting","onFilteringChange","onSortingChange","selection","onSelectionChange","metadata","isMetadataLoading","onBulkUseCase","onUseCase","bulkOutcomes","onBulkDismiss","onBulkRetry","isBulkRunning","children","t","translateKey","errorMessage","metaEntity","slots","panel","setPanel","pending","setPending","undefined","columns","visibleColumns","hidden","setPersonalization","key","name","pivot","Error","selectionState","selectable","rowUseCases","useCases","bulkUseCases","toolbarUseCases","hasRowMenu","length","everythingMatching","mode","total","excluded","Set","hasRowAction","columnCount","isFirstLoad","isRefetching","customSkeleton","showSkeleton","skeletonRows","Array","from","Math","min","_","index","busyClass","renderCell","column","item","render","value","descriptor","record","anchorIndex","treeKeys","collapsed","setCollapsed","rows","shown","shownItems","map","row","toggleCollapsed","current","next","has","delete","add","setSelection","toggleRow","checked","shiftKey","start","end","slice","id","pageIds","selectedOnPage","filter","allOnPageSelected","someOnPageSelected","toggleAllOnPage","rowLabel","named","find","type","String","selectionCell","aria-label","disabled","onChange","event","currentTarget","nativeEvent","runTarget","target","kind","invoke","useCase","confirm","labelKey","rowMenu","Trigger","Items","Item","tone","onClick","recordAction","primary","variant","size","href","className","intent","menu","span","emptyMessage","div","role","aria-live","lines","toolbar","descriptors","filterable","applied","sortable","count","busy","matchingTotal","onSelectAllMatching","onClear","open","title","message","messageKey","onCancel","onConfirm","outcomes","labelFor","onRetryFailed","onDismiss","p","data-testid","header","tr","indeterminate","label","aria-busy","colSpan","shape","depth","hasChildren","columnIndex","expanded","idOf","onToggle","leading","actions","rest"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/ui/organisms/entity-table/entity-table.tsx"],"sourcesContent":["'use client';\n\nimport {\n emptySelection,\n type Entity,\n type EntityId,\n type EntitySelection,\n type EntitySorting,\n extractMetaEntity,\n type FilterGroup,\n hierarchyOf,\n isSelected,\n queryPropertyFor,\n selectionSize,\n toggleSelected,\n type UseCaseDescriptor,\n} from '@entifix/core';\nimport {\n Fragment,\n type ReactNode,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport {\n useEntityNoun,\n useLocale,\n useT,\n useTranslateKey,\n} from '../../../i18n/index.js';\nimport { renderAnchor, useCasesForSurface } from '../../actions/index.js';\nimport { useVerbInvoker } from '../../actions/use-verb-invoker.js';\nimport { Button, button } from '../../atoms/button/index.js';\nimport { CellValue } from '../../atoms/cell-value/index.js';\nimport { Checkbox } from '../../atoms/field/index.js';\nimport { Skeleton } from '../../atoms/skeleton/index.js';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeaderCell,\n TableMessageRow,\n TableRow,\n} from '../../atoms/table/index.js';\nimport { linkClassName } from '../../atoms/text/index.js';\nimport {\n ActionBar,\n type ActionBarItem,\n itemsForUseCases,\n} from '../../molecules/action-bar/index.js';\nimport { BulkActionBar } from '../../molecules/bulk-action-bar/index.js';\nimport { BulkResult } from '../../molecules/bulk-result/index.js';\nimport { ColumnSettings } from '../../molecules/column-settings/index.js';\nimport { EntityRecordCard } from '../../molecules/entity-record-card/index.js';\nimport { FilterBuilder } from '../../molecules/filter-builder/index.js';\nimport { FilterChips } from '../../molecules/filter-chips/index.js';\nimport { LoadingBoundary } from '../../molecules/loading-boundary/index.js';\nimport { Pagination } from '../../molecules/pagination/index.js';\nimport { RegionState } from '../../molecules/region-state/index.js';\nimport { SortBuilder } from '../../molecules/sort-builder/index.js';\nimport { TableToolbar } from '../../molecules/table-toolbar/index.js';\nimport type {\n EntityTableColumn,\n EntityTableProps,\n} from './entity-table.types.js';\nimport { readEntityTableSlots } from './entity-table-slots.js';\nimport { shownTreeRows, TreeCell, treeRows } from './entity-table-tree.js';\nimport { nextHeaderSorting, sortKeys } from './header-sort.js';\nimport {\n ACTIONS_WIDTH,\n pivotClasses,\n pivotWidth,\n SELECTION_WIDTH,\n} from './pivot.js';\nimport { useEntityTableColumns } from './use-entity-table-columns.js';\nimport { useResultAnnouncement } from './use-result-announcement.js';\n\ntype Panel = 'none' | 'filters' | 'sorting';\n\n/**\n * How many placeholder rows the built-in skeleton draws.\n *\n * The column count is taken from the real geometry, because that is the axis a\n * mismatch shifts; the row count is capped instead of tracking `pageSize`, since\n * a fifty-row page would otherwise paint fifty shimmer rows to stand in for\n * content that arrives below the fold anyway.\n */\nconst SKELETON_ROW_CAP = 5;\n\n/** How many members a card shows as meta when none declares a `card` role. */\nconst UNDECLARED_META_CAP = 3;\n\n/**\n * A table that builds itself from an entity's metadata: columns, labels, value\n * formatting and the filter/sort controls all come from `@accessor()`\n * declarations, so listing a new entity needs no bespoke table.\n *\n * Three things layer on top of that default:\n * - **personalization** — column order and visibility, persisted through the\n * UI-preferences port (so it survives reloads and can later move server-side);\n * - **responsiveness** — below `pivotBreakpoint` rows pivot into label/value\n * cards instead of scrolling sideways;\n * - **slots** — `<EntityColumn>` / `<EntityTableHeader>` / `<EntityTableRow>` /\n * `<EntityTableToolbar>` children override any part of the default rendering.\n */\n/**\n * What a pending verb will act on once it is confirmed.\n *\n * A form has one record; a table has a row or a selection, and those reach two\n * different handlers with two different payloads. Carrying the selection itself\n * rather than reading it back at confirm time also removes a branch that could\n * not happen: the bulk bar only renders inside a truthy selection.\n */\ntype PendingTarget<TEntity extends Entity> =\n | { kind: 'row'; item: TEntity }\n | { kind: 'selection'; selection: EntitySelection<TEntity> };\n\nexport function EntityTable<TEntity extends Entity>({\n entityConstructor,\n isLoading,\n skeleton = true,\n error,\n items,\n totalItems,\n currentPage,\n pageSize,\n onPageChange,\n onPageSizeChange,\n hrefFor,\n newHref,\n renderLink = renderAnchor,\n onSelect,\n preferencesKey,\n showControls = true,\n pivotBreakpoint,\n filtering,\n sorting,\n onFilteringChange,\n onSortingChange,\n facets,\n selection,\n onSelectionChange,\n metadata,\n isMetadataLoading = false,\n onBulkUseCase,\n onUseCase,\n onRetry,\n bulkOutcomes,\n onBulkDismiss,\n onBulkRetry,\n isBulkRunning = false,\n children,\n}: EntityTableProps<TEntity>) {\n const t = useT();\n // A descriptor's label is a runtime catalog key — the typed gate cannot see\n // it, which is why a host's catalog check is what catches a typo.\n const translateKey = useTranslateKey();\n const entityNoun = useEntityNoun();\n const metaEntity = extractMetaEntity(entityConstructor);\n const slots = readEntityTableSlots<TEntity>(children);\n const [panel, setPanel] = useState<Panel>('none');\n\n const { columns, visibleColumns, hidden, setPersonalization } =\n useEntityTableColumns<TEntity>(\n entityConstructor,\n slots.columns,\n items[0],\n `entity-table:${preferencesKey ?? metaEntity.key ?? metaEntity.name}`,\n );\n\n // A picker's table is choosing *one* value; a multi-selection there would\n // offer a set to a form member that holds a single reference, and the two\n // controls would fight over the same row. This is a wiring mistake rather\n // than a state, so it costs the render — the same call `EntityForm` makes\n // for a link source aimed at a member no picker can edit.\n if (onSelect !== undefined && selection !== undefined) {\n throw new Error(\n 'EntityTable was given both `onSelect` and `selection`. A picker picks one ' +\n 'row and a selection accumulates many, so the row control cannot be both. ' +\n 'Drop `selection` inside a picker, or drop `onSelect` outside one.',\n );\n }\n\n /**\n * The selection, or `undefined` when the table is not selectable at all.\n *\n * One value rather than a `selection` prop beside a `selectable` boolean:\n * two of them means every render site has to re-test both, and the second\n * test is unreachable — which reads as a defensive check and is really dead\n * code the coverage gate then cannot close.\n */\n const selectionState =\n selection !== undefined && onSelectionChange !== undefined\n ? selection\n : undefined;\n const selectable = selectionState !== undefined;\n const rowUseCases = useCasesForSurface('row-menu', metadata?.useCases);\n const bulkUseCases = useCasesForSurface('bulk-bar', metadata?.useCases);\n const toolbarUseCases = useCasesForSurface(\n 'table-toolbar',\n metadata?.useCases,\n );\n const hasRowMenu = rowUseCases.length > 0 && onUseCase !== undefined;\n\n /**\n * What a `context-independent` collection verb acts on: everything the\n * current filter matches.\n *\n * Not an empty `ids` selection, which would say \"no rows\" — the verb is\n * available *without* a selection precisely because its subject is the\n * collection, so the honest payload is the same filter the listing is\n * showing, and the same one a \"select all matching\" escalation would send.\n */\n const everythingMatching: EntitySelection<TEntity> = {\n mode: 'matching',\n filtering,\n total: totalItems,\n excluded: new Set<EntityId>(),\n };\n\n // Opening a record is its title's link, so a row needs an actions column\n // only to pick it or to run a verb on it.\n const hasRowAction = onSelect !== undefined || hasRowMenu;\n const columnCount =\n visibleColumns.length + (hasRowAction ? 1 : 0) + (selectable ? 1 : 0);\n\n /**\n * The grid and the cards are both rendered, and a container query on the\n * table's own box picks one — at the width its visible columns need. A\n * viewport breakpoint was wrong inside a split view, a drawer or a wizard\n * step, and a JS measurement would guess during SSR and correct after mount.\n */\n const pivot = pivotClasses(\n pivotWidth(\n visibleColumns,\n (selectable ? SELECTION_WIDTH : 0) + (hasRowAction ? ACTIONS_WIDTH : 0),\n pivotBreakpoint,\n ),\n );\n /**\n * The first load has nothing to show and is held with a skeleton; a refetch\n * already has rows, so it keeps them and only marks the region busy. Splitting\n * the two is what stops every pagination click flashing grey.\n */\n const isFirstLoad = isLoading && items.length === 0;\n\n /** `true`/omitted → the built-in default; a node → that node; `false` → none. */\n const customSkeleton = typeof skeleton === 'boolean' ? undefined : skeleton;\n const showSkeleton = isFirstLoad && skeleton !== false;\n const skeletonRows = Array.from(\n { length: Math.min(pageSize, SKELETON_ROW_CAP) },\n (_, index) => index,\n );\n\n /**\n * A refetch keeps its rows at full contrast and marks them `aria-busy`:\n * dimming them through opacity took the text under the contrast a reader\n * needs, on every page change.\n */\n const busy = isLoading || undefined;\n\n /** The column a row reads *as*: the first non-identifier one it filled in. */\n const titleColumn = (item: TEntity) =>\n visibleColumns.find(column => {\n if (column.type === 'id') return false;\n const value = (item as Record<string, unknown>)[column.name];\n return value != null && String(value) !== '';\n });\n\n /** The column that links to the record: one declared `card: 'title'`, else the row's title. */\n const declaredTitle = visibleColumns.find(column => column.card === 'title');\n const linkColumn = (item: TEntity) => declaredTitle ?? titleColumn(item);\n\n /**\n * What a record's card shows, by each member's declared `card` role or the\n * default: an id is hidden, the first enum is the badge, and up to\n * {@link UNDECLARED_META_CAP} more members are meta — a card is a summary,\n * and the grid holds the rest.\n */\n const firstEnum = visibleColumns.find(\n column => column.card === undefined && column.type === 'enum',\n );\n const roleOf = (column: EntityTableColumn<TEntity>) =>\n column.card ??\n (column.type === 'id' ? 'hidden' : column === firstEnum ? 'badge' : 'meta');\n const cardParts = (item: TEntity) => {\n const title = linkColumn(item);\n const rest = visibleColumns.filter(column => column !== title);\n let undeclared = 0;\n return {\n title,\n subtitle: rest.find(column => roleOf(column) === 'subtitle'),\n badges: rest.filter(column => roleOf(column) === 'badge'),\n meta: rest.filter(column => {\n if (roleOf(column) !== 'meta') return false;\n if (column.card !== undefined) return true;\n undeclared += 1;\n return undeclared <= UNDECLARED_META_CAP;\n }),\n };\n };\n\n const renderValue = (\n column: EntityTableColumn<TEntity>,\n item: TEntity,\n ): ReactNode =>\n column.render ? (\n column.render(item)\n ) : (\n <CellValue\n value={(item as Record<string, unknown>)[column.name]}\n descriptor={column}\n record={item as Record<string, unknown>}\n />\n );\n\n /**\n * A cell, and the row's title cell is the record's link\n * ([ADR 0026](../../../../../../../docs/adr/0026-actions-hierarchy-overflow-and-destructive-verbs.md)).\n * A column of identical \"Open\" links named nothing, and a reader tabbing\n * down it heard \"Open\" once per row; the title says which record it opens.\n * A picker's rows are chosen, not opened, so they stay plain.\n */\n const renderCell = (\n column: EntityTableColumn<TEntity>,\n item: TEntity,\n ): ReactNode => {\n const value = renderValue(column, item);\n if (hrefFor === undefined || onSelect !== undefined) return value;\n if (linkColumn(item)?.name !== column.name) return value;\n return renderLink({\n href: hrefFor(item.id),\n className: linkClassName,\n children: value,\n intent: { kind: 'open', id: item.id },\n });\n };\n\n /**\n * The row a shift-click ranges *from* — the last box the user actually\n * toggled. Held in a ref rather than state because it changes nothing on\n * screen, and re-rendering the whole table on every click to remember an\n * anchor is a cost with no visible return.\n */\n const anchorIndex = useRef<number | undefined>(undefined);\n\n /**\n * An entity that declares a parent renders as a tree: the page arranged\n * depth first, its first column indented, and a subtree hidden while its\n * record is collapsed. Collapsing is view state, so it lives here and is\n * forgotten with the table, like the shift-click anchor above.\n */\n const treeKeys = useMemo(\n () => hierarchyOf(entityConstructor),\n [entityConstructor],\n );\n const [collapsed, setCollapsed] = useState<ReadonlySet<string>>(\n () => new Set(),\n );\n const rows = useMemo(() => treeRows(items, treeKeys), [items, treeKeys]);\n const shown = shownTreeRows(rows, collapsed);\n const shownItems = shown.map(row => row.item);\n const toggleCollapsed = (key: string) =>\n setCollapsed(current => {\n const next = new Set(current);\n if (next.has(key)) next.delete(key);\n else next.add(key);\n return next;\n });\n\n const setSelection = (next: EntitySelection<TEntity>) =>\n onSelectionChange?.(next);\n\n const toggleRow = (\n index: number,\n checked: boolean,\n shiftKey: boolean,\n current: EntitySelection<TEntity>,\n ) => {\n // A shift-click extends the last toggle across the rows *on this page*, in\n // the direction the user dragged, and applies the new state to all of them.\n const from = shiftKey ? (anchorIndex.current ?? index) : index;\n const [start, end] = from <= index ? [from, index] : [index, from];\n\n let next = current;\n // The rows as shown: in a tree the order on screen is not the load's.\n for (const row of shownItems.slice(start, end + 1)) {\n next = toggleSelected(next, row.id, checked);\n }\n\n anchorIndex.current = index;\n setSelection(next);\n };\n\n /**\n * The header box, and it has three states rather than two: none of this\n * page's rows selected, all of them, or some. Without the third the box\n * reads as \"nothing is selected\" while a bulk bar sits below it saying\n * otherwise.\n */\n const pageIds = items.map(item => item.id);\n const selectedOnPage = selectionState\n ? pageIds.filter(id => isSelected(selectionState, id)).length\n : 0;\n const allOnPageSelected =\n pageIds.length > 0 && selectedOnPage === pageIds.length;\n const someOnPageSelected = selectedOnPage > 0 && !allOnPageSelected;\n\n const toggleAllOnPage = (\n checked: boolean,\n current: EntitySelection<TEntity>,\n ) => {\n let next = current;\n for (const id of pageIds) next = toggleSelected(next, id, checked);\n anchorIndex.current = undefined;\n setSelection(next);\n };\n\n /**\n * How a row names itself to a screen reader.\n *\n * The first visible column's value, because that is what the user reads the\n * row *as* — \"Seleccionar Acme\" rather than \"Seleccionar fila 3\", which tells\n * them the position they can already see and not the thing they cannot.\n */\n const rowLabel = (item: TEntity): string => {\n // The first non-identifier column **that this row actually filled in**.\n //\n // An id names a row the way \"row 3\" does: it repeats what the user can\n // already see. But skipping only the id is not enough — measured against\n // the seeded catalog, where `code` is the first non-id column and is empty\n // on every brand, so the label fell straight through to the id and never\n // reached `name`. A column that is blank on this row names it no better\n // than its position does, so the search continues past it.\n //\n // The id remains the last resort, because a row with nothing rendered\n // still has to announce itself.\n const named = titleColumn(item);\n\n return named\n ? String((item as Record<string, unknown>)[named.name])\n : String(item.id);\n };\n\n const selectionCell = (\n item: TEntity,\n index: number,\n // Passed in rather than closed over: this is only ever called under\n // `selectable`, which already proves the selection exists, and reading the\n // optional prop here would add a fallback arm that nothing can reach.\n current: EntitySelection<TEntity>,\n ) => (\n <Checkbox\n // Deliberately no `id`: both pivots are always in the DOM, so an id here\n // would be emitted twice per row. `aria-label` names the control instead,\n // and it names the *record* rather than the position.\n aria-label={t('table.selectRow', { record: rowLabel(item) })}\n checked={isSelected(current, item.id)}\n disabled={isBulkRunning}\n onChange={event =>\n toggleRow(\n index,\n event.currentTarget.checked,\n (event.nativeEvent as MouseEvent).shiftKey === true,\n current,\n )\n }\n />\n );\n\n const runTarget = (key: string, target: PendingTarget<TEntity>) => {\n if (target.kind === 'row') onUseCase?.(key, target.item);\n else onBulkUseCase?.(key, target.selection);\n };\n\n /**\n * Every verb on the table goes through here — the row menu, the bulk bar and\n * the toolbar — so none reaches a destructive verb around its confirmation,\n * its typed check or its Undo.\n */\n const verbs = useVerbInvoker<PendingTarget<TEntity>>({\n run: runTarget,\n busy: isBulkRunning,\n served: metadata?.useCases,\n });\n\n const invoke = (\n useCase: UseCaseDescriptor,\n target: PendingTarget<TEntity>,\n ) => {\n if (target.kind === 'row') {\n verbs.invoke({\n descriptor: useCase,\n target,\n object: rowLabel(target.item),\n count: 1,\n });\n return;\n }\n const count = selectionSize(target.selection);\n verbs.invoke({\n descriptor: useCase,\n target,\n object: t('confirm.selection', {\n count,\n label: entityNoun(metaEntity),\n plural: entityNoun(metaEntity, 'plural'),\n }),\n count,\n matching: target.selection.mode === 'matching',\n });\n };\n\n const rowMenu = (item: TEntity) =>\n hasRowMenu ? (\n <ActionBar\n surface=\"row-menu\"\n menuLabel={t('actions.forRecord', { record: rowLabel(item) })}\n items={itemsForUseCases(rowUseCases, translateKey, useCase =>\n invoke(useCase, { kind: 'row', item }),\n )}\n />\n ) : undefined;\n\n /**\n * A picker's Select, then the row's verbs. Opening the record is not here:\n * it is the title's link.\n */\n const recordAction = (item: TEntity) => {\n const pick = onSelect ? (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={() => onSelect(item)}\n >\n {t('table.select')}\n </Button>\n ) : undefined;\n\n const menu = rowMenu(item);\n if (!menu) return pick;\n\n return (\n <span className=\"flex items-center gap-2xs\">\n {pick}\n {menu}\n </span>\n );\n };\n\n /**\n * \"New\" is the list's primary, and only where something can be created\n * here: the entity is not one only a flow writes (`creatable: false`), and\n * the caller may write it. A list that offered \"New\" to a reader who could\n * never save opened a form with no Save on it.\n */\n const mayCreate =\n newHref !== undefined &&\n metaEntity.creatable &&\n (metadata === undefined || metadata.actions.includes('write'));\n const toolbarItems: ActionBarItem[] = itemsForUseCases(\n toolbarUseCases,\n translateKey,\n useCase =>\n invoke(useCase, { kind: 'selection', selection: everythingMatching }),\n isBulkRunning,\n );\n const declaresToolbarPrimary = toolbarItems.some(\n item => item.emphasis === 'primary',\n );\n const toolbarActions: ActionBarItem[] = [\n ...(mayCreate\n ? [\n {\n key: 'new',\n label: t('table.new'),\n emphasis: declaresToolbarPrimary\n ? ('default' as const)\n : ('primary' as const),\n href: newHref,\n intent: { kind: 'new' as const },\n },\n ]\n : []),\n ...toolbarItems,\n ];\n\n const locale = useLocale();\n const panelId = useId();\n const keys = sortKeys(sorting, columns);\n const activeFilters = filtering?.values.length ?? 0;\n const results = t('table.results', { count: totalItems });\n\n /**\n * What the status region says once a filter, a sort or a page has settled —\n * debounced, and silent on the first load.\n */\n const announcement = useResultAnnouncement(\n !isLoading,\n totalItems === 0 && activeFilters > 0\n ? t('table.noResultsFiltered')\n : results,\n JSON.stringify([filtering, sorting, currentPage, pageSize]),\n );\n\n /** The caption: what the table holds, how many, and in what order. */\n const primaryKey = keys[0];\n const primaryColumn =\n primaryKey === undefined\n ? undefined\n : columns.find(column => column.name === primaryKey.member);\n const caption =\n primaryKey === undefined || primaryColumn === undefined\n ? t('table.caption', {\n entity: entityNoun(metaEntity, 'plural'),\n results,\n })\n : t('table.captionSorted', {\n entity: entityNoun(metaEntity, 'plural'),\n results,\n column: primaryColumn.label,\n direction: t(\n primaryKey.type === 'asc' ? 'sort.ascending' : 'sort.descending',\n ),\n });\n\n /**\n * A column's header: its name, and — for a sortable one — a button that\n * cycles it ascending → descending → unsorted, with Shift adding it as the\n * next key. Only the primary key carries `aria-sort`.\n */\n const headerCell = (column: EntityTableColumn<TEntity>) => {\n const label = column.header ?? column.label;\n if (!column.sortable || onSortingChange === undefined)\n return <TableHeaderCell key={column.name}>{label}</TableHeaderCell>;\n const position = keys.findIndex(key => key.member === column.name);\n const type = keys[position]?.type;\n return (\n <TableHeaderCell\n key={column.name}\n aria-sort={\n position === 0\n ? type === 'asc'\n ? 'ascending'\n : 'descending'\n : undefined\n }\n >\n <button\n type=\"button\"\n className=\"inline-flex min-h-(--target-min) items-center gap-3xs rounded-sm focus-ring hover:text-content\"\n onClick={event =>\n onSortingChange(\n nextHeaderSorting<TEntity>(\n keys,\n {\n member: column.name,\n property: queryPropertyFor(column, locale),\n },\n event.shiftKey,\n ),\n )\n }\n >\n {label}\n <span aria-hidden=\"true\">\n {type === 'asc' ? '▲' : type === 'desc' ? '▼' : '↕'}\n </span>\n {keys.length > 1 && position >= 0 && (\n <span aria-hidden=\"true\" className=\"text-step-sm\">\n {position + 1}\n </span>\n )}\n </button>\n </TableHeaderCell>\n );\n };\n\n /** A panel toggle: its state said by `aria-expanded`, and its count shown. */\n const panelToggle = (which: Exclude<Panel, 'none'>, count: number) => (\n <Button\n type=\"button\"\n variant={panel === which ? 'primary' : 'secondary'}\n size=\"sm\"\n aria-expanded={panel === which}\n aria-controls={panelId}\n onClick={() => setPanel(current => (current === which ? 'none' : which))}\n >\n {t(which === 'filters' ? 'table.filters' : 'table.sorting')}\n {count > 0 && (\n // Its name stays \"Filters\": the chips say which filters, and a count\n // in the name would change it under the reader with every filter.\n <span\n aria-hidden=\"true\"\n className=\"ms-3xs rounded-full bg-surface px-3xs text-step-sm text-content\"\n >\n {count}\n </span>\n )}\n </Button>\n );\n\n return (\n <div className=\"@container/entity-table flex flex-col gap-s\">\n {/*\n One announcement for the whole table, not one per layout. Both the grid\n and the card list are always in the DOM — CSS picks which is visible —\n so a live region inside each meant assistive tech was told twice that\n the table was loading. The shimmer itself is aria-hidden, so this is the\n only thing that carries the news.\n */}\n <span role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {isFirstLoad ? t('table.loading') : announcement}\n </span>\n\n {showControls && (\n <TableToolbar\n start={\n <>\n {/* \"New\" and the collection-bound verbs that need no\n selection. They sit here rather than in the bulk bar,\n because the bulk bar only exists while rows are ticked and\n these are always available. */}\n <LoadingBoundary isLoading={isMetadataLoading} lines={0}>\n <>\n {toolbarActions.length > 0 && (\n <ActionBar\n surface=\"table-toolbar\"\n items={toolbarActions}\n renderLink={renderLink}\n />\n )}\n </>\n </LoadingBoundary>\n {slots.toolbar}\n </>\n }\n end={\n <>\n {panelToggle('filters', activeFilters)}\n {panelToggle('sorting', keys.length)}\n <ColumnSettings\n columns={columns}\n hidden={hidden}\n onChange={setPersonalization}\n />\n </>\n }\n panelId={panelId}\n panel={\n panel === 'filters' ? (\n <FilterBuilder<TEntity>\n // Remounted when the filtering changes from outside — a chip\n // removed — so its rows show what is in effect.\n key={JSON.stringify(filtering ?? null)}\n descriptors={columns.filter(column => column.filterable)}\n facets={facets}\n value={filtering}\n onChange={(applied: FilterGroup<TEntity>) =>\n onFilteringChange?.(applied)\n }\n />\n ) : panel === 'sorting' ? (\n <SortBuilder<TEntity>\n descriptors={columns.filter(column => column.sortable)}\n value={sorting}\n onChange={(applied: EntitySorting<TEntity>) =>\n onSortingChange?.(applied)\n }\n />\n ) : undefined\n }\n />\n )}\n\n {showControls && onFilteringChange !== undefined && (\n <FilterChips<TEntity>\n descriptors={columns.filter(column => column.filterable)}\n value={filtering}\n onChange={onFilteringChange}\n />\n )}\n\n {/*\n The bar appears when there is something selected and something to do\n with it. It sits above the rows rather than floating over them: a fixed\n overlay covers the last row of a short page, which is the row the user\n most likely just ticked.\n */}\n {selectionState && selectionSize(selectionState) > 0 && (\n <BulkActionBar\n count={selectionSize(selectionState)}\n useCases={bulkUseCases}\n busy={isBulkRunning}\n // Offered only when this page is fully ticked and the store holds\n // more than the page — the escalation has somewhere to escalate to.\n matchingTotal={\n selectionState.mode === 'ids' &&\n allOnPageSelected &&\n totalItems > items.length\n ? totalItems\n : undefined\n }\n onSelectAllMatching={() => setSelection(everythingMatching)}\n onClear={() => setSelection(emptySelection<TEntity>())}\n onUseCase={useCase =>\n invoke(useCase, { kind: 'selection', selection: selectionState })\n }\n />\n )}\n\n {verbs.dialog}\n\n {bulkOutcomes && bulkOutcomes.length > 0 && onBulkDismiss && (\n <BulkResult\n outcomes={bulkOutcomes}\n busy={isBulkRunning}\n labelFor={id => {\n const row = items.find(item => item.id === id);\n return row ? rowLabel(row) : undefined;\n }}\n onRetryFailed={onBulkRetry}\n onDismiss={onBulkDismiss}\n />\n )}\n\n {/*\n Rendered once, outside both layouts, and independently of whether there\n are rows: a refetch that fails leaves the previous page on screen, and\n without this the user would read stale data as current.\n */}\n {/*\n One state for the region, outside both layouts (ADR 0028): a failed\n load with Retry — and the rows from before it marked as possibly out of\n date — or an empty answer, which says whether a filter emptied it.\n */}\n <RegionState\n plural={entityNoun(metaEntity, 'plural')}\n isLoading={isLoading}\n error={error}\n onRetry={onRetry}\n hasStaleData={items.length > 0}\n isEmpty={items.length === 0}\n isFiltered={activeFilters > 0}\n onClearFilters={\n onFilteringChange === undefined\n ? undefined\n : () =>\n onFilteringChange({\n // Only offered under a filter, so there is one to keep the\n // logic of.\n operator: (filtering as FilterGroup<TEntity>).operator,\n values: [],\n })\n }\n emptyAction={\n mayCreate\n ? renderLink({\n href: newHref as string,\n className: button({ variant: 'primary' }),\n children: t('table.new'),\n intent: { kind: 'new' },\n })\n : undefined\n }\n />\n\n {/* Wide viewports: a grid. */}\n <div className={pivot.grid} aria-busy={busy}>\n {/* Held to most of the viewport so the header can stick above the\n rows; the scroll padding keeps a focused row out from under it. */}\n <Table\n caption={caption}\n containerClassName=\"max-h-(--table-max-block) scroll-pt-12\"\n >\n <TableHead>\n {slots.header ? (\n slots.header.render(visibleColumns)\n ) : (\n <tr>\n {selectable && (\n <TableHeaderCell>\n <Checkbox\n aria-label={t('table.selectAllOnPage')}\n checked={allOnPageSelected}\n indeterminate={someOnPageSelected}\n disabled={isBulkRunning || items.length === 0}\n onChange={event =>\n toggleAllOnPage(\n event.currentTarget.checked,\n selectionState,\n )\n }\n />\n </TableHeaderCell>\n )}\n {visibleColumns.map(headerCell)}\n {hasRowAction && (\n <TableHeaderCell>{t('table.actions')}</TableHeaderCell>\n )}\n </tr>\n )}\n </TableHead>\n <TableBody>\n {/* Skeleton rows rather than the word \"Loading\", and one per column\n rather than a single full-width blob: the placeholder occupies\n the real grid, so the swap to rows shifts nothing sideways.\n `TableMessageRow` is deliberately bypassed — it is one centred\n full-span cell and cannot express the geometry. */}\n {showSkeleton &&\n (customSkeleton ? (\n <TableMessageRow colSpan={columnCount}>\n {customSkeleton}\n </TableMessageRow>\n ) : (\n skeletonRows.map(row => (\n <TableRow key={row}>\n {selectable && (\n <TableCell>\n <Skeleton shape=\"line\" className=\"w-4\" />\n </TableCell>\n )}\n {visibleColumns.map(column => (\n <TableCell key={column.name}>\n <Skeleton shape=\"line\" className=\"w-full\" />\n </TableCell>\n ))}\n {hasRowAction && (\n <TableCell>\n <Skeleton shape=\"line\" className=\"w-16\" />\n </TableCell>\n )}\n </TableRow>\n ))\n ))}\n {shown.map(({ item, depth, hasChildren }, index) =>\n slots.row ? (\n // A Fragment carries the key without wrapping the custom row in\n // an element `<tbody>` would reject.\n <Fragment key={String(item.id ?? index)}>\n {slots.row.render(item, visibleColumns)}\n </Fragment>\n ) : (\n <TableRow key={String(item.id ?? index)}>\n {selectionState && (\n <TableCell>\n {selectionCell(item, index, selectionState)}\n </TableCell>\n )}\n {visibleColumns.map((column, columnIndex) => (\n <TableCell key={column.name}>\n {treeKeys !== undefined && columnIndex === 0 ? (\n <TreeCell\n depth={depth}\n hasChildren={hasChildren}\n expanded={!collapsed.has(treeKeys.idOf(item))}\n label={t(\n collapsed.has(treeKeys.idOf(item))\n ? 'table.expandRow'\n : 'table.collapseRow',\n { record: rowLabel(item) },\n )}\n onToggle={() => toggleCollapsed(treeKeys.idOf(item))}\n >\n {renderCell(column, item)}\n </TreeCell>\n ) : (\n renderCell(column, item)\n )}\n </TableCell>\n ))}\n {hasRowAction && <TableCell>{recordAction(item)}</TableCell>}\n </TableRow>\n ),\n )}\n </TableBody>\n </Table>\n </div>\n\n {/* Narrow containers: the same columns pivoted into a list of cards. */}\n <div className={pivot.cards} aria-busy={busy}>\n {/* The card pivot gets card-shaped placeholders, not a bare shimmer\n block: the border, radius and padding are the geometry here. */}\n {showSkeleton &&\n (customSkeleton ?? (\n <ul role=\"list\" className=\"m-0 p-0 flex list-none flex-col gap-2xs\">\n {skeletonRows.map(row => (\n <li key={row}>\n <EntityRecordCard\n title={<Skeleton shape=\"line\" className=\"w-32\" />}\n meta={visibleColumns.slice(1).map(column => ({\n key: column.name,\n label: column.label,\n value: <Skeleton shape=\"line\" className=\"w-24\" />,\n }))}\n />\n </li>\n ))}\n </ul>\n ))}\n {shown.length > 0 && (\n <ul role=\"list\" className=\"m-0 p-0 flex list-none flex-col gap-2xs\">\n {shown.map(({ item, depth }, index) => {\n const parts = cardParts(item);\n return (\n <li\n key={String(item.id ?? index)}\n // A tree's card says how deep it sits, as its row does.\n aria-level={treeKeys === undefined ? undefined : depth + 1}\n style={\n treeKeys === undefined\n ? undefined\n : { marginInlineStart: `${depth * 1.5}rem` }\n }\n >\n <EntityRecordCard\n // The same renderers as the grid, so the two layouts agree.\n title={\n parts.title === undefined\n ? rowLabel(item)\n : renderCell(parts.title, item)\n }\n subtitle={\n parts.subtitle === undefined\n ? undefined\n : renderCell(parts.subtitle, item)\n }\n badges={parts.badges.map(column => ({\n key: column.name,\n value: renderCell(column, item),\n }))}\n meta={parts.meta.map(column => ({\n key: column.name,\n label: column.header ?? column.label,\n value: renderCell(column, item),\n }))}\n leading={\n selectionState\n ? selectionCell(item, index, selectionState)\n : undefined\n }\n actions={recordAction(item)}\n />\n </li>\n );\n })}\n </ul>\n )}\n </div>\n\n <Pagination\n currentPage={currentPage}\n pageSize={pageSize}\n totalItems={totalItems}\n onPageChange={onPageChange}\n onPageSizeChange={onPageSizeChange}\n />\n\n {slots.rest}\n </div>\n );\n}\n"],"names":["emptySelection","extractMetaEntity","hierarchyOf","isSelected","queryPropertyFor","selectionSize","toggleSelected","Fragment","useId","useMemo","useRef","useState","useEntityNoun","useLocale","useT","useTranslateKey","renderAnchor","useCasesForSurface","useVerbInvoker","Button","button","CellValue","Checkbox","Skeleton","Table","TableBody","TableCell","TableHead","TableHeaderCell","TableMessageRow","TableRow","linkClassName","ActionBar","itemsForUseCases","BulkActionBar","BulkResult","ColumnSettings","EntityRecordCard","FilterBuilder","FilterChips","LoadingBoundary","Pagination","RegionState","SortBuilder","TableToolbar","readEntityTableSlots","shownTreeRows","TreeCell","treeRows","nextHeaderSorting","sortKeys","ACTIONS_WIDTH","pivotClasses","pivotWidth","SELECTION_WIDTH","useEntityTableColumns","useResultAnnouncement","SKELETON_ROW_CAP","UNDECLARED_META_CAP","EntityTable","entityConstructor","isLoading","skeleton","error","items","totalItems","currentPage","pageSize","onPageChange","onPageSizeChange","hrefFor","newHref","renderLink","onSelect","preferencesKey","showControls","pivotBreakpoint","filtering","sorting","onFilteringChange","onSortingChange","facets","selection","onSelectionChange","metadata","isMetadataLoading","onBulkUseCase","onUseCase","onRetry","bulkOutcomes","onBulkDismiss","onBulkRetry","isBulkRunning","children","t","translateKey","entityNoun","metaEntity","slots","panel","setPanel","columns","visibleColumns","hidden","setPersonalization","key","name","undefined","Error","selectionState","selectable","rowUseCases","useCases","bulkUseCases","toolbarUseCases","hasRowMenu","length","everythingMatching","mode","total","excluded","Set","hasRowAction","columnCount","pivot","isFirstLoad","customSkeleton","showSkeleton","skeletonRows","Array","from","Math","min","_","index","busy","titleColumn","item","find","column","type","value","String","declaredTitle","card","linkColumn","firstEnum","roleOf","cardParts","title","rest","filter","undeclared","subtitle","badges","meta","renderValue","render","descriptor","record","renderCell","href","id","className","intent","kind","anchorIndex","treeKeys","collapsed","setCollapsed","rows","shown","shownItems","map","row","toggleCollapsed","current","next","has","delete","add","setSelection","toggleRow","checked","shiftKey","start","end","slice","pageIds","selectedOnPage","allOnPageSelected","someOnPageSelected","toggleAllOnPage","rowLabel","named","selectionCell","aria-label","disabled","onChange","event","currentTarget","nativeEvent","runTarget","target","verbs","run","served","invoke","useCase","object","count","label","plural","matching","rowMenu","surface","menuLabel","recordAction","pick","variant","size","onClick","menu","span","mayCreate","creatable","actions","includes","toolbarItems","declaresToolbarPrimary","some","emphasis","toolbarActions","locale","panelId","keys","activeFilters","values","results","announcement","JSON","stringify","primaryKey","primaryColumn","member","caption","entity","direction","headerCell","header","sortable","position","findIndex","aria-sort","property","aria-hidden","panelToggle","which","aria-expanded","aria-controls","div","role","aria-live","lines","toolbar","descriptors","filterable","applied","matchingTotal","onSelectAllMatching","onClear","dialog","outcomes","labelFor","onRetryFailed","onDismiss","hasStaleData","isEmpty","isFiltered","onClearFilters","operator","emptyAction","grid","aria-busy","containerClassName","tr","indeterminate","colSpan","shape","depth","hasChildren","columnIndex","expanded","idOf","onToggle","cards","ul","li","parts","aria-level","style","marginInlineStart","leading"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { BulkOutcome, EntifixFailure, Entity, EntityConstructor, EntityFieldDescriptor, EntityId, EntityMetadataDocument, EntitySelection, EntitySorting, FilterGroup } from '@entifix/core';
1
+ import type { BulkOutcome, EntifixFailure, Entity, EntityConstructor, EntityFacetCounts, EntityFieldDescriptor, EntityId, EntityMetadataDocument, EntitySelection, EntitySorting, FilterGroup } from '@entifix/core';
2
2
  import type { ReactNode } from 'react';
3
3
  import type { RenderLink } from '../../actions/index.js';
4
4
  /**
@@ -13,7 +13,10 @@ export interface EntityTableColumn<TEntity extends Entity> extends EntityFieldDe
13
13
  /** True when the column has no backing entity member (slot-only column). */
14
14
  virtual?: boolean;
15
15
  }
16
- /** Breakpoint at and above which the table renders as a grid, not as cards. */
16
+ /**
17
+ * A fixed width, read against the table's own container, at and above which
18
+ * it renders as a grid rather than as cards: `sm` 40rem, `md` 48rem, `lg` 64rem.
19
+ */
17
20
  export type EntityTablePivotBreakpoint = 'sm' | 'md' | 'lg';
18
21
  export interface EntityTableProps<TEntity extends Entity> {
19
22
  /** Metadata source. Columns, labels, types and the filter/sort controls all
@@ -79,7 +82,12 @@ export interface EntityTableProps<TEntity extends Entity> {
79
82
  preferencesKey?: string;
80
83
  /** Hide the toolbar (columns, filters, sorting) — a bare listing. */
81
84
  showControls?: boolean;
82
- /** Viewport width at which rows stop pivoting into cards. Default `md`. */
85
+ /**
86
+ * Overrides where rows stop pivoting into cards. By default that is where
87
+ * the visible columns fit side by side in the table's own container — the
88
+ * sum of each member's `minWidth`, or its type's — so a table in a split
89
+ * view, a drawer or a wizard step switches by the room it actually has.
90
+ */
83
91
  pivotBreakpoint?: EntityTablePivotBreakpoint;
84
92
  /** The filtering currently applied, seeding the filter panel so it shows
85
93
  * what is in effect rather than an empty form. */
@@ -89,8 +97,15 @@ export interface EntityTableProps<TEntity extends Entity> {
89
97
  /** Notified when the filter panel is **applied** — not while it is edited, so
90
98
  * the value can feed a load request directly. */
91
99
  onFilteringChange?: (filtering: FilterGroup<TEntity>) => void;
92
- /** Notified when the sort panel is applied. */
100
+ /** Loads the page again — what a failed load offers as Retry. */
101
+ onRetry?: () => void;
102
+ /** Notified when the sort panel is applied, or a column header is clicked. */
93
103
  onSortingChange?: (sorting: EntitySorting<TEntity>) => void;
104
+ /**
105
+ * How many records hold each value, by member, for the filter panel to show
106
+ * beside an enum's or a boolean's choices.
107
+ */
108
+ facets?: EntityFacetCounts;
94
109
  /**
95
110
  * The rows a collection-bound action would act on.
96
111
  *
@@ -1 +1 @@
1
- {"version":3,"file":"entity-table.types.d.ts","sourceRoot":"","sources":["../../../../src/ui/organisms/entity-table/entity-table.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,MAAM,EACN,iBAAiB,EACjB,qBAAqB,EACrB,QAAQ,EACR,sBAAsB,EACtB,eAAe,EACf,aAAa,EACb,WAAW,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD;;;GAGG;AACH,MAAM,WAAW,iBAAiB,CAChC,OAAO,SAAS,MAAM,CACtB,SAAQ,qBAAqB;IAC7B,wCAAwC;IACxC,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,kEAAkE;IAClE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,SAAS,CAAC;IACtC,4EAA4E;IAC5E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,+EAA+E;AAC/E,MAAM,MAAM,0BAA0B,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5D,MAAM,WAAW,gBAAgB,CAAC,OAAO,SAAS,MAAM;IACtD;oDACgD;IAChD,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAE9C,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,gBAAgB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAEjD;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,MAAM,CAAC;IACnC,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAEnC,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,0BAA0B,CAAC;IAE7C;uDACmD;IACnD,SAAS,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACjC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAEjC;sDACkD;IAClD,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAC9D,+CAA+C;IAC/C,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAE5D;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;IACrC,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAElE;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAClC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,6DAA6D;IAC7D,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAC3E,mEAAmE;IACnE,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAEjD,0EAA0E;IAC1E,YAAY,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;IACxC,oEAAoE;IACpE,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB"}
1
+ {"version":3,"file":"entity-table.types.d.ts","sourceRoot":"","sources":["../../../../src/ui/organisms/entity-table/entity-table.types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/ui/organisms/entity-table/entity-table.types.ts"],"sourcesContent":["import type {\n BulkOutcome,\n EntifixFailure,\n Entity,\n EntityConstructor,\n EntityFieldDescriptor,\n EntityId,\n EntityMetadataDocument,\n EntitySelection,\n EntitySorting,\n FilterGroup,\n} from '@entifix/core';\nimport type { ReactNode } from 'react';\n\nimport type { RenderLink } from '../../actions/index.js';\n\n/**\n * A column as the table finally renders it: the metadata descriptor plus the\n * per-column overrides a caller supplied through an `<EntityColumn>` slot.\n */\nexport interface EntityTableColumn<\n TEntity extends Entity,\n> extends EntityFieldDescriptor {\n /** Replaces the default header text. */\n header?: ReactNode;\n /** Replaces the default `CellValue` rendering for this column. */\n render?: (item: TEntity) => ReactNode;\n /** True when the column has no backing entity member (slot-only column). */\n virtual?: boolean;\n}\n\n/** Breakpoint at and above which the table renders as a grid, not as cards. */\nexport type EntityTablePivotBreakpoint = 'sm' | 'md' | 'lg';\n\nexport interface EntityTableProps<TEntity extends Entity> {\n /** Metadata source. Columns, labels, types and the filter/sort controls all\n * derive from this constructor's accessors. */\n entityConstructor: EntityConstructor<TEntity>;\n\n isLoading: boolean;\n /**\n * What holds the table's shape during the first load.\n *\n * `true` (the default) renders the built-in placeholder, which is derived from\n * the table's own resolved geometry — one shimmer cell per visible column, so\n * the swap to real rows shifts nothing. A node replaces that default; `false`\n * renders no placeholder at all.\n *\n * Only the *first* load is held this way. A refetch that already has rows\n * keeps them and marks the region busy instead — replacing populated rows with\n * shimmer on every pagination click is a grey flash, not a loading state.\n */\n skeleton?: boolean | ReactNode;\n /**\n * The failure of the last load, if it failed.\n *\n * A listing that renders an empty table when the service is unreachable tells\n * the user their catalog is empty — the one thing they must not conclude. So\n * this is rendered as an alert, and it also changes the empty message.\n *\n * It arrives for free from `useDataLoading`, whose whole state is spread into\n * this component.\n */\n error?: EntifixFailure;\n items: ReadonlyArray<TEntity>;\n totalItems: number;\n currentPage: number;\n pageSize: number;\n onPageChange: (newPage: number) => void;\n onPageSizeChange?: (newPageSize: number) => void;\n\n /**\n * Builds the link to a row's record. Supplied as a function rather than the\n * table routing by itself: this package must not depend on a router, so the\n * caller (a shell page, which does know one) owns the URL shape.\n *\n * Omit it and the actions column is not rendered at all.\n */\n hrefFor?: (id: EntityId) => string;\n /** Link to the create form. Omit and no `New` action is rendered. */\n newHref?: string;\n /**\n * Renders the `Open` and `New` links. Defaults to a plain anchor, which a\n * client-routed host experiences as a full document load — pass one built on\n * the host's own link component. See {@link RenderLink}.\n */\n renderLink?: RenderLink;\n\n /**\n * Picks a row instead of navigating to it — how a link picker reuses this\n * table as its browse dialog.\n *\n * Takes precedence over {@link hrefFor}: inside a picker the user is choosing\n * a value, and offering to navigate away from the form they are editing is the\n * wrong affordance. Either one renders the actions column.\n */\n onSelect?: (item: TEntity) => void;\n\n /** Scope for persisted personalization. Defaults to the entity's key. */\n preferencesKey?: string;\n /** Hide the toolbar (columns, filters, sorting) — a bare listing. */\n showControls?: boolean;\n /** Viewport width at which rows stop pivoting into cards. Default `md`. */\n pivotBreakpoint?: EntityTablePivotBreakpoint;\n\n /** The filtering currently applied, seeding the filter panel so it shows\n * what is in effect rather than an empty form. */\n filtering?: FilterGroup<TEntity>;\n /** The sorting currently applied, seeding the sort panel. */\n sorting?: EntitySorting<TEntity>;\n\n /** Notified when the filter panel is **applied** — not while it is edited, so\n * the value can feed a load request directly. */\n onFilteringChange?: (filtering: FilterGroup<TEntity>) => void;\n /** Notified when the sort panel is applied. */\n onSortingChange?: (sorting: EntitySorting<TEntity>) => void;\n\n /**\n * The rows a collection-bound action would act on.\n *\n * **Controlled from above the table**, exactly as `filtering` and `sorting`\n * are, and for the same reason plus one more: a selection has to survive\n * pagination, and the page owns the pager. A selection held inside the table\n * would be reset by the very navigation the user performs to add to it.\n *\n * Omit it and no selection column is rendered — a listing that is only read\n * gains nothing from a column of empty boxes.\n */\n selection?: EntitySelection<TEntity>;\n onSelectionChange?: (selection: EntitySelection<TEntity>) => void;\n\n /**\n * What this caller may do with the entity, as the service decided it.\n *\n * The same document `EntityForm` takes. The table reads only the\n * `collection`-bound half of `useCases` — the entity-bound verbs belong to a\n * record, and a row's own overflow menu is fed from the\n * `context-dependent` ones.\n *\n * Absent keeps the pre-ADR-0026 behaviour: no bulk bar and no row menu, which\n * is what every un-migrated call site renders today.\n */\n metadata?: EntityMetadataDocument;\n isMetadataLoading?: boolean;\n\n /** Runs a `collection`-bound verb over {@link selection}. */\n onBulkUseCase?: (key: string, selection: EntitySelection<TEntity>) => void;\n /** Runs an `entity`-bound, `context-dependent` verb on one row. */\n onUseCase?: (key: string, item: TEntity) => void;\n\n /** The outcome of the last bulk run, rendered per row until dismissed. */\n bulkOutcomes?: readonly BulkOutcome[];\n onBulkDismiss?: () => void;\n onBulkRetry?: (ids: EntityId[]) => void;\n /** A bulk run is in flight — every verb is disabled while it is. */\n isBulkRunning?: boolean;\n\n /** Customization slots — see `entity-table-slots`. */\n children?: ReactNode;\n}\n"],"names":[],"mappings":"AAkCA,WA6HC"}
1
+ {"version":3,"sources":["../../../../src/ui/organisms/entity-table/entity-table.types.ts"],"sourcesContent":["import type {\n BulkOutcome,\n EntifixFailure,\n Entity,\n EntityConstructor,\n EntityFacetCounts,\n EntityFieldDescriptor,\n EntityId,\n EntityMetadataDocument,\n EntitySelection,\n EntitySorting,\n FilterGroup,\n} from '@entifix/core';\nimport type { ReactNode } from 'react';\n\nimport type { RenderLink } from '../../actions/index.js';\n\n/**\n * A column as the table finally renders it: the metadata descriptor plus the\n * per-column overrides a caller supplied through an `<EntityColumn>` slot.\n */\nexport interface EntityTableColumn<\n TEntity extends Entity,\n> extends EntityFieldDescriptor {\n /** Replaces the default header text. */\n header?: ReactNode;\n /** Replaces the default `CellValue` rendering for this column. */\n render?: (item: TEntity) => ReactNode;\n /** True when the column has no backing entity member (slot-only column). */\n virtual?: boolean;\n}\n\n/**\n * A fixed width, read against the table's own container, at and above which\n * it renders as a grid rather than as cards: `sm` 40rem, `md` 48rem, `lg` 64rem.\n */\nexport type EntityTablePivotBreakpoint = 'sm' | 'md' | 'lg';\n\nexport interface EntityTableProps<TEntity extends Entity> {\n /** Metadata source. Columns, labels, types and the filter/sort controls all\n * derive from this constructor's accessors. */\n entityConstructor: EntityConstructor<TEntity>;\n\n isLoading: boolean;\n /**\n * What holds the table's shape during the first load.\n *\n * `true` (the default) renders the built-in placeholder, which is derived from\n * the table's own resolved geometry — one shimmer cell per visible column, so\n * the swap to real rows shifts nothing. A node replaces that default; `false`\n * renders no placeholder at all.\n *\n * Only the *first* load is held this way. A refetch that already has rows\n * keeps them and marks the region busy instead — replacing populated rows with\n * shimmer on every pagination click is a grey flash, not a loading state.\n */\n skeleton?: boolean | ReactNode;\n /**\n * The failure of the last load, if it failed.\n *\n * A listing that renders an empty table when the service is unreachable tells\n * the user their catalog is empty — the one thing they must not conclude. So\n * this is rendered as an alert, and it also changes the empty message.\n *\n * It arrives for free from `useDataLoading`, whose whole state is spread into\n * this component.\n */\n error?: EntifixFailure;\n items: ReadonlyArray<TEntity>;\n totalItems: number;\n currentPage: number;\n pageSize: number;\n onPageChange: (newPage: number) => void;\n onPageSizeChange?: (newPageSize: number) => void;\n\n /**\n * Builds the link to a row's record. Supplied as a function rather than the\n * table routing by itself: this package must not depend on a router, so the\n * caller (a shell page, which does know one) owns the URL shape.\n *\n * Omit it and the actions column is not rendered at all.\n */\n hrefFor?: (id: EntityId) => string;\n /** Link to the create form. Omit and no `New` action is rendered. */\n newHref?: string;\n /**\n * Renders the `Open` and `New` links. Defaults to a plain anchor, which a\n * client-routed host experiences as a full document load — pass one built on\n * the host's own link component. See {@link RenderLink}.\n */\n renderLink?: RenderLink;\n\n /**\n * Picks a row instead of navigating to it — how a link picker reuses this\n * table as its browse dialog.\n *\n * Takes precedence over {@link hrefFor}: inside a picker the user is choosing\n * a value, and offering to navigate away from the form they are editing is the\n * wrong affordance. Either one renders the actions column.\n */\n onSelect?: (item: TEntity) => void;\n\n /** Scope for persisted personalization. Defaults to the entity's key. */\n preferencesKey?: string;\n /** Hide the toolbar (columns, filters, sorting) — a bare listing. */\n showControls?: boolean;\n /**\n * Overrides where rows stop pivoting into cards. By default that is where\n * the visible columns fit side by side in the table's own container — the\n * sum of each member's `minWidth`, or its type's — so a table in a split\n * view, a drawer or a wizard step switches by the room it actually has.\n */\n pivotBreakpoint?: EntityTablePivotBreakpoint;\n\n /** The filtering currently applied, seeding the filter panel so it shows\n * what is in effect rather than an empty form. */\n filtering?: FilterGroup<TEntity>;\n /** The sorting currently applied, seeding the sort panel. */\n sorting?: EntitySorting<TEntity>;\n\n /** Notified when the filter panel is **applied** — not while it is edited, so\n * the value can feed a load request directly. */\n onFilteringChange?: (filtering: FilterGroup<TEntity>) => void;\n /** Loads the page again — what a failed load offers as Retry. */\n onRetry?: () => void;\n /** Notified when the sort panel is applied, or a column header is clicked. */\n\n onSortingChange?: (sorting: EntitySorting<TEntity>) => void;\n /**\n * How many records hold each value, by member, for the filter panel to show\n * beside an enum's or a boolean's choices.\n */\n facets?: EntityFacetCounts;\n\n /**\n * The rows a collection-bound action would act on.\n *\n * **Controlled from above the table**, exactly as `filtering` and `sorting`\n * are, and for the same reason plus one more: a selection has to survive\n * pagination, and the page owns the pager. A selection held inside the table\n * would be reset by the very navigation the user performs to add to it.\n *\n * Omit it and no selection column is rendered — a listing that is only read\n * gains nothing from a column of empty boxes.\n */\n selection?: EntitySelection<TEntity>;\n onSelectionChange?: (selection: EntitySelection<TEntity>) => void;\n\n /**\n * What this caller may do with the entity, as the service decided it.\n *\n * The same document `EntityForm` takes. The table reads only the\n * `collection`-bound half of `useCases` — the entity-bound verbs belong to a\n * record, and a row's own overflow menu is fed from the\n * `context-dependent` ones.\n *\n * Absent keeps the pre-ADR-0026 behaviour: no bulk bar and no row menu, which\n * is what every un-migrated call site renders today.\n */\n metadata?: EntityMetadataDocument;\n isMetadataLoading?: boolean;\n\n /** Runs a `collection`-bound verb over {@link selection}. */\n onBulkUseCase?: (key: string, selection: EntitySelection<TEntity>) => void;\n /** Runs an `entity`-bound, `context-dependent` verb on one row. */\n onUseCase?: (key: string, item: TEntity) => void;\n\n /** The outcome of the last bulk run, rendered per row until dismissed. */\n bulkOutcomes?: readonly BulkOutcome[];\n onBulkDismiss?: () => void;\n onBulkRetry?: (ids: EntityId[]) => void;\n /** A bulk run is in flight — every verb is disabled while it is. */\n isBulkRunning?: boolean;\n\n /** Customization slots — see `entity-table-slots`. */\n children?: ReactNode;\n}\n"],"names":[],"mappings":"AAsCA,WA0IC"}
@@ -0,0 +1,18 @@
1
+ import type { Entity, EntityFieldDescriptor, EntitySorting, EntitySortType } from '@entifix/core';
2
+ /** One key of a sorting, by the member it sorts, in precedence order. */
3
+ export interface SortKey {
4
+ /** The member's accessor name — what a column is addressed by. */
5
+ readonly member: string;
6
+ /** The wire key it sorts by: a locale path for a localized member. */
7
+ readonly property: string;
8
+ readonly type: EntitySortType;
9
+ }
10
+ /** A sorting's keys in precedence order, each resolved to its member. */
11
+ export declare function sortKeys<TEntity extends Entity>(sorting: EntitySorting<TEntity> | undefined, descriptors: readonly EntityFieldDescriptor[]): SortKey[];
12
+ /**
13
+ * The sorting after a header click: the member cycles ascending → descending →
14
+ * unsorted. A plain click makes it the only key; with Shift it is added as the
15
+ * next key, or cycled where it already stands, and the others are kept.
16
+ */
17
+ export declare function nextHeaderSorting<TEntity extends Entity>(keys: readonly SortKey[], clicked: Omit<SortKey, 'type'>, additive: boolean): EntitySorting<TEntity>;
18
+ //# sourceMappingURL=header-sort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"header-sort.d.ts","sourceRoot":"","sources":["../../../../src/ui/organisms/entity-table/header-sort.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,qBAAqB,EACrB,aAAa,EACb,cAAc,EACf,MAAM,eAAe,CAAC;AAGvB,yEAAyE;AACzE,MAAM,WAAW,OAAO;IACtB,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;CAC/B;AAED,yEAAyE;AACzE,wBAAgB,QAAQ,CAAC,OAAO,SAAS,MAAM,EAC7C,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,SAAS,EAC3C,WAAW,EAAE,SAAS,qBAAqB,EAAE,GAC5C,OAAO,EAAE,CAeX;AAQD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,SAAS,MAAM,EACtD,IAAI,EAAE,SAAS,OAAO,EAAE,EACxB,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,EAC9B,QAAQ,EAAE,OAAO,GAChB,aAAa,CAAC,OAAO,CAAC,CAsBxB"}
@@ -0,0 +1,54 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ import { findQueriedDescriptor } from '@entifix/core';
3
+ /** A sorting's keys in precedence order, each resolved to its member. */ export function sortKeys(sorting, descriptors) {
4
+ if (sorting === undefined) return [];
5
+ return Object.keys(sorting).map(Number).sort((left, right)=>left - right).flatMap((priority)=>{
6
+ const entry = sorting[priority];
7
+ const property = String(entry.property);
8
+ const descriptor = findQueriedDescriptor(descriptors, property);
9
+ return descriptor === undefined ? [] : [
10
+ {
11
+ member: descriptor.name,
12
+ property,
13
+ type: entry.type
14
+ }
15
+ ];
16
+ });
17
+ }
18
+ const NEXT = {
19
+ none: 'asc',
20
+ asc: 'desc',
21
+ desc: undefined
22
+ };
23
+ /**
24
+ * The sorting after a header click: the member cycles ascending → descending →
25
+ * unsorted. A plain click makes it the only key; with Shift it is added as the
26
+ * next key, or cycled where it already stands, and the others are kept.
27
+ */ export function nextHeaderSorting(keys, clicked, additive) {
28
+ const position = keys.findIndex((key)=>key.member === clicked.member);
29
+ // A plain click on a secondary key starts it over as the primary one.
30
+ const from = position === -1 || !additive && position > 0 ? 'none' : keys[position].type;
31
+ const type = NEXT[from];
32
+ const cycled = type === undefined ? [] : [
33
+ _extends({}, clicked, {
34
+ type
35
+ })
36
+ ];
37
+ const next = !additive ? cycled : position === -1 ? [
38
+ ...keys,
39
+ ...cycled
40
+ ] : [
41
+ ...keys.slice(0, position),
42
+ ...cycled,
43
+ ...keys.slice(position + 1)
44
+ ];
45
+ return Object.fromEntries(next.map((key, index)=>[
46
+ index,
47
+ {
48
+ property: key.property,
49
+ type: key.type
50
+ }
51
+ ]));
52
+ }
53
+
54
+ //# sourceMappingURL=header-sort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/ui/organisms/entity-table/header-sort.ts"],"sourcesContent":["import type {\n Entity,\n EntityFieldDescriptor,\n EntitySorting,\n EntitySortType,\n} from '@entifix/core';\nimport { findQueriedDescriptor } from '@entifix/core';\n\n/** One key of a sorting, by the member it sorts, in precedence order. */\nexport interface SortKey {\n /** The member's accessor name — what a column is addressed by. */\n readonly member: string;\n /** The wire key it sorts by: a locale path for a localized member. */\n readonly property: string;\n readonly type: EntitySortType;\n}\n\n/** A sorting's keys in precedence order, each resolved to its member. */\nexport function sortKeys<TEntity extends Entity>(\n sorting: EntitySorting<TEntity> | undefined,\n descriptors: readonly EntityFieldDescriptor[],\n): SortKey[] {\n if (sorting === undefined) return [];\n return Object.keys(sorting)\n .map(Number)\n .sort((left, right) => left - right)\n .flatMap(priority => {\n const entry = sorting[priority] as NonNullable<\n EntitySorting<TEntity>[number]\n >;\n const property = String(entry.property);\n const descriptor = findQueriedDescriptor(descriptors, property);\n return descriptor === undefined\n ? []\n : [{ member: descriptor.name, property, type: entry.type }];\n });\n}\n\nconst NEXT: Record<'none' | EntitySortType, EntitySortType | undefined> = {\n none: 'asc',\n asc: 'desc',\n desc: undefined,\n};\n\n/**\n * The sorting after a header click: the member cycles ascending → descending →\n * unsorted. A plain click makes it the only key; with Shift it is added as the\n * next key, or cycled where it already stands, and the others are kept.\n */\nexport function nextHeaderSorting<TEntity extends Entity>(\n keys: readonly SortKey[],\n clicked: Omit<SortKey, 'type'>,\n additive: boolean,\n): EntitySorting<TEntity> {\n const position = keys.findIndex(key => key.member === clicked.member);\n // A plain click on a secondary key starts it over as the primary one.\n const from =\n position === -1 || (!additive && position > 0)\n ? 'none'\n : (keys[position] as SortKey).type;\n const type = NEXT[from];\n const cycled = type === undefined ? [] : [{ ...clicked, type }];\n\n const next = !additive\n ? cycled\n : position === -1\n ? [...keys, ...cycled]\n : [...keys.slice(0, position), ...cycled, ...keys.slice(position + 1)];\n\n return Object.fromEntries(\n next.map((key, index) => [\n index,\n { property: key.property as keyof TEntity, type: key.type },\n ]),\n ) as EntitySorting<TEntity>;\n}\n"],"names":["findQueriedDescriptor","sortKeys","sorting","descriptors","undefined","Object","keys","map","Number","sort","left","right","flatMap","priority","entry","property","String","descriptor","member","name","type","NEXT","none","asc","desc","nextHeaderSorting","clicked","additive","position","findIndex","key","from","cycled","next","slice","fromEntries","index"],"mappings":";AAMA,SAASA,qBAAqB,QAAQ,gBAAgB;AAWtD,uEAAuE,GACvE,OAAO,SAASC,SACdC,OAA2C,EAC3CC,WAA6C;IAE7C,IAAID,YAAYE,WAAW,OAAO,EAAE;IACpC,OAAOC,OAAOC,IAAI,CAACJ,SAChBK,GAAG,CAACC,QACJC,IAAI,CAAC,CAACC,MAAMC,QAAUD,OAAOC,OAC7BC,OAAO,CAACC,CAAAA;QACP,MAAMC,QAAQZ,OAAO,CAACW,SAAS;QAG/B,MAAME,WAAWC,OAAOF,MAAMC,QAAQ;QACtC,MAAME,aAAajB,sBAAsBG,aAAaY;QACtD,OAAOE,eAAeb,YAClB,EAAE,GACF;YAAC;gBAAEc,QAAQD,WAAWE,IAAI;gBAAEJ;gBAAUK,MAAMN,MAAMM,IAAI;YAAC;SAAE;IAC/D;AACJ;AAEA,MAAMC,OAAoE;IACxEC,MAAM;IACNC,KAAK;IACLC,MAAMpB;AACR;AAEA;;;;CAIC,GACD,OAAO,SAASqB,kBACdnB,IAAwB,EACxBoB,OAA8B,EAC9BC,QAAiB;IAEjB,MAAMC,WAAWtB,KAAKuB,SAAS,CAACC,CAAAA,MAAOA,IAAIZ,MAAM,KAAKQ,QAAQR,MAAM;IACpE,sEAAsE;IACtE,MAAMa,OACJH,aAAa,CAAC,KAAM,CAACD,YAAYC,WAAW,IACxC,SACA,AAACtB,IAAI,CAACsB,SAAS,CAAaR,IAAI;IACtC,MAAMA,OAAOC,IAAI,CAACU,KAAK;IACvB,MAAMC,SAASZ,SAAShB,YAAY,EAAE,GAAG;QAAC,aAAKsB;YAASN;;KAAO;IAE/D,MAAMa,OAAO,CAACN,WACVK,SACAJ,aAAa,CAAC,IACZ;WAAItB;WAAS0B;KAAO,GACpB;WAAI1B,KAAK4B,KAAK,CAAC,GAAGN;WAAcI;WAAW1B,KAAK4B,KAAK,CAACN,WAAW;KAAG;IAE1E,OAAOvB,OAAO8B,WAAW,CACvBF,KAAK1B,GAAG,CAAC,CAACuB,KAAKM,QAAU;YACvBA;YACA;gBAAErB,UAAUe,IAAIf,QAAQ;gBAAmBK,MAAMU,IAAIV,IAAI;YAAC;SAC3D;AAEL"}
@@ -0,0 +1,25 @@
1
+ import type { Entity, MetaAccessorType } from '@entifix/core';
2
+ import type { EntityTableColumn, EntityTablePivotBreakpoint } from './entity-table.types.js';
3
+ /**
4
+ * The narrowest a column of each type reads at, in `rem`, when its member
5
+ * declares no `minWidth`.
6
+ */
7
+ export declare const TYPE_MIN_WIDTH: Partial<Record<MetaAccessorType, number>>;
8
+ /** The selection column, and the row's actions. */
9
+ export declare const SELECTION_WIDTH = 3;
10
+ export declare const ACTIONS_WIDTH = 5;
11
+ /**
12
+ * The container width, in `rem`, at which a table's visible columns fit side
13
+ * by side: the sum of their widths and of the selection and actions columns —
14
+ * or a declared breakpoint's, which overrides it.
15
+ */
16
+ export declare function pivotWidth<TEntity extends Entity>(columns: readonly EntityTableColumn<TEntity>[], extra: number, breakpoint?: EntityTablePivotBreakpoint): number;
17
+ /**
18
+ * The classes that show the grid at and above `width`, and the cards below:
19
+ * the first named size that holds it, or the widest.
20
+ */
21
+ export declare function pivotClasses(width: number): {
22
+ grid: string;
23
+ cards: string;
24
+ };
25
+ //# sourceMappingURL=pivot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pivot.d.ts","sourceRoot":"","sources":["../../../../src/ui/organisms/entity-table/pivot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAE9D,OAAO,KAAK,EACV,iBAAiB,EACjB,0BAA0B,EAC3B,MAAM,yBAAyB,CAAC;AAEjC;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAQpE,CAAC;AAIF,mDAAmD;AACnD,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,eAAO,MAAM,aAAa,IAAI,CAAC;AA6B/B;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAS,MAAM,EAC/C,OAAO,EAAE,SAAS,iBAAiB,CAAC,OAAO,CAAC,EAAE,EAC9C,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,0BAA0B,GACtC,MAAM,CAQR;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAI3E"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * The narrowest a column of each type reads at, in `rem`, when its member
3
+ * declares no `minWidth`.
4
+ */ export const TYPE_MIN_WIDTH = {
5
+ id: 6,
6
+ string: 10,
7
+ number: 6,
8
+ boolean: 4,
9
+ date: 8,
10
+ enum: 8,
11
+ link: 10
12
+ };
13
+ /** Any other type — a collection, a composition. */ const DEFAULT_MIN_WIDTH = 8;
14
+ /** The selection column, and the row's actions. */ export const SELECTION_WIDTH = 3;
15
+ export const ACTIONS_WIDTH = 5;
16
+ /** A breakpoint's width, now read against the table's own container. */ const BREAKPOINT_WIDTH = {
17
+ sm: 40,
18
+ md: 48,
19
+ lg: 64
20
+ };
21
+ /**
22
+ * The container widths a table can switch at — Tailwind's named container
23
+ * sizes, in `rem` — each with its classes written out in full: Tailwind sees
24
+ * only a class it can read in the source, never one built at runtime. The grid
25
+ * is `display: none` below the width and the cards at and above it.
26
+ */ const STEPS = [
27
+ [
28
+ 20,
29
+ 'hidden @xs/entity-table:block',
30
+ '@xs/entity-table:hidden'
31
+ ],
32
+ [
33
+ 24,
34
+ 'hidden @sm/entity-table:block',
35
+ '@sm/entity-table:hidden'
36
+ ],
37
+ [
38
+ 28,
39
+ 'hidden @md/entity-table:block',
40
+ '@md/entity-table:hidden'
41
+ ],
42
+ [
43
+ 32,
44
+ 'hidden @lg/entity-table:block',
45
+ '@lg/entity-table:hidden'
46
+ ],
47
+ [
48
+ 36,
49
+ 'hidden @xl/entity-table:block',
50
+ '@xl/entity-table:hidden'
51
+ ],
52
+ [
53
+ 42,
54
+ 'hidden @2xl/entity-table:block',
55
+ '@2xl/entity-table:hidden'
56
+ ],
57
+ [
58
+ 48,
59
+ 'hidden @3xl/entity-table:block',
60
+ '@3xl/entity-table:hidden'
61
+ ],
62
+ [
63
+ 56,
64
+ 'hidden @4xl/entity-table:block',
65
+ '@4xl/entity-table:hidden'
66
+ ],
67
+ [
68
+ 64,
69
+ 'hidden @5xl/entity-table:block',
70
+ '@5xl/entity-table:hidden'
71
+ ],
72
+ [
73
+ 72,
74
+ 'hidden @6xl/entity-table:block',
75
+ '@6xl/entity-table:hidden'
76
+ ],
77
+ [
78
+ 80,
79
+ 'hidden @7xl/entity-table:block',
80
+ '@7xl/entity-table:hidden'
81
+ ]
82
+ ];
83
+ /**
84
+ * The container width, in `rem`, at which a table's visible columns fit side
85
+ * by side: the sum of their widths and of the selection and actions columns —
86
+ * or a declared breakpoint's, which overrides it.
87
+ */ export function pivotWidth(columns, extra, breakpoint) {
88
+ if (breakpoint !== undefined) return BREAKPOINT_WIDTH[breakpoint];
89
+ return columns.reduce((sum, column)=>{
90
+ var _ref, _column_minWidth;
91
+ return sum + ((_ref = (_column_minWidth = column.minWidth) != null ? _column_minWidth : TYPE_MIN_WIDTH[column.type]) != null ? _ref : DEFAULT_MIN_WIDTH);
92
+ }, extra);
93
+ }
94
+ /**
95
+ * The classes that show the grid at and above `width`, and the cards below:
96
+ * the first named size that holds it, or the widest.
97
+ */ export function pivotClasses(width) {
98
+ var _STEPS_find;
99
+ const [, grid, cards] = (_STEPS_find = STEPS.find(([size])=>size >= width)) != null ? _STEPS_find : STEPS[STEPS.length - 1];
100
+ return {
101
+ grid,
102
+ cards
103
+ };
104
+ }
105
+
106
+ //# sourceMappingURL=pivot.js.map