@entifix/react-controls 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/i18n/catalog/en.d.ts.map +1 -1
  2. package/dist/i18n/catalog/en.js +3 -0
  3. package/dist/i18n/catalog/en.js.map +1 -1
  4. package/dist/i18n/catalog/es.d.ts +3 -0
  5. package/dist/i18n/catalog/es.d.ts.map +1 -1
  6. package/dist/i18n/catalog/es.js +3 -0
  7. package/dist/i18n/catalog/es.js.map +1 -1
  8. package/dist/i18n/catalog/index.d.ts +6 -0
  9. package/dist/i18n/catalog/index.d.ts.map +1 -1
  10. package/dist/ui/actions/render-link.d.ts +8 -4
  11. package/dist/ui/actions/render-link.d.ts.map +1 -1
  12. package/dist/ui/actions/render-link.js.map +1 -1
  13. package/dist/ui/molecules/pagination/index.d.ts +2 -0
  14. package/dist/ui/molecules/pagination/index.d.ts.map +1 -1
  15. package/dist/ui/molecules/pagination/index.js +2 -0
  16. package/dist/ui/molecules/pagination/index.js.map +1 -1
  17. package/dist/ui/molecules/pagination/page-math.d.ts +23 -0
  18. package/dist/ui/molecules/pagination/page-math.d.ts.map +1 -0
  19. package/dist/ui/molecules/pagination/page-math.js +47 -0
  20. package/dist/ui/molecules/pagination/page-math.js.map +1 -0
  21. package/dist/ui/molecules/pagination/pagination-links.d.ts +33 -0
  22. package/dist/ui/molecules/pagination/pagination-links.d.ts.map +1 -0
  23. package/dist/ui/molecules/pagination/pagination-links.js +94 -0
  24. package/dist/ui/molecules/pagination/pagination-links.js.map +1 -0
  25. package/dist/ui/molecules/pagination/pagination.d.ts +6 -1
  26. package/dist/ui/molecules/pagination/pagination.d.ts.map +1 -1
  27. package/dist/ui/molecules/pagination/pagination.js +8 -2
  28. package/dist/ui/molecules/pagination/pagination.js.map +1 -1
  29. package/dist/ui/organisms/entity-table/entity-table-tree.d.ts +33 -0
  30. package/dist/ui/organisms/entity-table/entity-table-tree.d.ts.map +1 -0
  31. package/dist/ui/organisms/entity-table/entity-table-tree.js +80 -0
  32. package/dist/ui/organisms/entity-table/entity-table-tree.js.map +1 -0
  33. package/dist/ui/organisms/entity-table/entity-table.d.ts.map +1 -1
  34. package/dist/ui/organisms/entity-table/entity-table.js +39 -7
  35. package/dist/ui/organisms/entity-table/entity-table.js.map +1 -1
  36. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../src/i18n/catalog/en.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,KAAK,UAAU,GAAG,cAAc,SAAS,EAAE,QAAQ,CAAC;AAEpD,eAAO,MAAM,QAAQ,EAAE,UAmNtB,CAAC"}
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../src/i18n/catalog/en.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,KAAK,UAAU,GAAG,cAAc,SAAS,EAAE,QAAQ,CAAC;AAEpD,eAAO,MAAM,QAAQ,EAAE,UAsNtB,CAAC"}
@@ -16,6 +16,8 @@
16
16
  error: 'Could not load records',
17
17
  selection: 'Selection',
18
18
  selectRow: 'Select {{record}}',
19
+ expandRow: 'Expand {{record}}',
20
+ collapseRow: 'Collapse {{record}}',
19
21
  selectAllOnPage: 'Select all on this page',
20
22
  selectAllMatching_one: 'Select {{count}} match',
21
23
  selectAllMatching_other: 'Select all {{count}} matches',
@@ -49,6 +51,7 @@
49
51
  loading: 'Loading…'
50
52
  },
51
53
  pagination: {
54
+ label: 'Pages',
52
55
  summary: 'Page {{current}} of {{total}} · {{items}} items',
53
56
  rows: 'Rows',
54
57
  previous: 'Previous',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/i18n/catalog/en.ts"],"sourcesContent":["/**\n * The `es` shape is the contract: annotating with it makes a key that exists in\n * Spanish but not here a *compile* error, so a half-translated locale cannot\n * reach a build. `tools/check-i18n.mjs` covers what types cannot — empty values,\n * and keys present here but absent from `es`.\n */\ntype EsControls = typeof import('./es.js').controls;\n\nexport const controls: EsControls = {\n table: {\n open: 'Open',\n select: 'Select',\n actions: 'Actions',\n new: 'New',\n filters: 'Filters',\n sorting: 'Sorting',\n loading: 'Loading…',\n empty: 'No records',\n error: 'Could not load records',\n selection: 'Selection',\n selectRow: 'Select {{record}}',\n selectAllOnPage: 'Select all on this page',\n selectAllMatching_one: 'Select {{count}} match',\n selectAllMatching_other: 'Select all {{count}} matches',\n selectedCount_one: '{{count}} selected',\n selectedCount_other: '{{count}} selected',\n clearSelection: 'Clear selection',\n },\n bulk: {\n running: 'Running…',\n succeeded_one: '{{count}} record updated',\n succeeded_other: '{{count}} records updated',\n failed_one: '{{count}} record failed',\n failed_other: '{{count}} records failed',\n retryFailed: 'Retry the failures',\n dismiss: 'Dismiss result',\n resultLabel: 'Bulk action result',\n barLabel: 'Actions on the selection',\n },\n form: {\n details: 'Details',\n new: 'New',\n view: 'View',\n edit: 'Edit',\n save: 'Save',\n saving: 'Saving…',\n delete: 'Delete',\n deleting: 'Deleting…',\n clone: 'Duplicate',\n moreActions: 'More actions',\n back: 'Back',\n loading: 'Loading…',\n },\n pagination: {\n summary: 'Page {{current}} of {{total}} · {{items}} items',\n rows: 'Rows',\n previous: 'Previous',\n next: 'Next',\n },\n columns: {\n title: 'Columns',\n subtitle: 'Visibility and order',\n reset: 'Reset to default',\n moveUp: 'Move {{column}} up',\n moveDown: 'Move {{column}} down',\n },\n filter: {\n match: 'Match',\n all: 'all',\n any: 'any',\n add: 'Add filter',\n apply: 'Apply',\n clear: 'Clear',\n none: 'No filterable members on this entity.',\n listPlaceholder: 'comma, separated, values',\n matchAria: 'Match all or any filter',\n member: 'Filter member',\n operator: 'Filter operator',\n value: 'Filter value',\n rangeEnd: 'Filter range end',\n remove: 'Remove filter',\n applyAria: 'Apply filters',\n clearAria: 'Clear filters',\n },\n sort: {\n by: 'Sort by',\n then: 'then by',\n ascending: 'ascending',\n descending: 'descending',\n add: 'Add sort',\n apply: 'Apply',\n clear: 'Clear',\n none: 'No sortable members on this entity.',\n member: 'Sort member',\n direction: 'Sort direction',\n raisePriority: 'Raise sort priority',\n lowerPriority: 'Lower sort priority',\n remove: 'Remove sort',\n applyAria: 'Apply sorting',\n clearAria: 'Clear sorting',\n },\n tabs: {\n close: 'Close {{label}}',\n new: 'Open a new tab',\n },\n link: {\n search: 'Search {{field}}',\n suggestAria: 'Show {{field}} suggestions',\n browseAria: 'Browse {{field}}',\n browseTitle: 'Select {{field}}',\n clear: 'Clear {{field}}',\n empty: '— unassigned —',\n loading: 'Loading…',\n noResults: 'No matches',\n error: 'Could not load options',\n close: 'Close',\n },\n detail: {\n addRow: 'Add row',\n removeRow: 'Remove row {{row}}',\n rowActions: 'Row actions',\n empty: 'No rows yet.',\n errorSummary_one: '{{count}} cell has errors',\n errorSummary_other: '{{count}} cells have errors',\n },\n value: {\n none: '— none —',\n yes: 'Yes',\n no: 'No',\n /**\n * A `composition` in a table: its rows are edited on the master's own\n * screen rather than in the cell, so the cell says how many there are.\n */\n rowCount_one: '{{count}} row',\n rowCount_other: '{{count}} rows',\n },\n /**\n * `required`…`option` are the metadata-derived rules. The rest are the\n * vocabulary an entity's Standard Schema writes its messages in: a schema\n * carries the *key*, never a sentence, so a rule stays translatable.\n *\n * `field` is the only parameter available — a Standard Schema issue exposes a\n * message and a path, nothing else — so these stay unquantified. A rule that\n * has to state its bound writes its own key in its domain catalog and passes\n * it namespaced (`entity:product-specification.validation.codeLength`).\n */\n validation: {\n required: '{{field}} is required',\n number: '{{field}} must be a number',\n date: '{{field}} must be a date',\n option: '{{field}} is not a valid option',\n minLength: '{{field}} is too short',\n maxLength: '{{field}} is too long',\n min: '{{field}} is too small',\n max: '{{field}} is too large',\n pattern: '{{field}} has an invalid format',\n minItems_one: '{{field}} needs at least {{count}} item',\n minItems_other: '{{field}} needs at least {{count}} items',\n maxItems_one: '{{field}} takes at most {{count}} item',\n maxItems_other: '{{field}} takes at most {{count}} items',\n notBefore: '{{field}} cannot be before {{other}}',\n locale: '{{field}} has no {{locale}} text',\n },\n operator: {\n eq: 'is',\n ne: 'is not',\n gt: 'greater than',\n gte: 'greater or equal',\n lt: 'less than',\n lte: 'less or equal',\n in: 'is one of',\n nin: 'is none of',\n between: 'between',\n nbetween: 'not between',\n like: 'contains',\n nlike: 'does not contain',\n isNull: 'is empty',\n isNotNull: 'is not empty',\n },\n theme: {\n label: 'Theme',\n },\n themes: {\n aurora: 'Aurora',\n sunset: 'Sunset',\n midnight: 'Midnight',\n ocean: 'Ocean (runtime)',\n marketplace: 'Marketplace',\n marketplaceDark: 'Marketplace dark',\n auth: 'Identity',\n authDark: 'Identity dark',\n },\n confirm: {\n confirm: 'Confirm',\n cancel: 'Cancel',\n title: 'Confirm',\n },\n breadcrumbs: {\n label: 'Breadcrumb',\n },\n wizard: {\n steps: 'Steps',\n previous: 'Back',\n next: 'Continue',\n finish: 'Finish',\n submitting: 'Submitting…',\n stepOf: 'Step {{current}} of {{total}}',\n goToStep: 'Back to {{label}}',\n status: {\n pending: 'pending',\n active: 'in progress',\n complete: 'completed',\n error: 'has errors',\n },\n resume: {\n title: 'Picking up where you left off',\n dismiss: 'Got it',\n },\n },\n};\n"],"names":["controls","table","open","select","actions","new","filters","sorting","loading","empty","error","selection","selectRow","selectAllOnPage","selectAllMatching_one","selectAllMatching_other","selectedCount_one","selectedCount_other","clearSelection","bulk","running","succeeded_one","succeeded_other","failed_one","failed_other","retryFailed","dismiss","resultLabel","barLabel","form","details","view","edit","save","saving","delete","deleting","clone","moreActions","back","pagination","summary","rows","previous","next","columns","title","subtitle","reset","moveUp","moveDown","filter","match","all","any","add","apply","clear","none","listPlaceholder","matchAria","member","operator","value","rangeEnd","remove","applyAria","clearAria","sort","by","then","ascending","descending","direction","raisePriority","lowerPriority","tabs","close","link","search","suggestAria","browseAria","browseTitle","noResults","detail","addRow","removeRow","rowActions","errorSummary_one","errorSummary_other","yes","no","rowCount_one","rowCount_other","validation","required","number","date","option","minLength","maxLength","min","max","pattern","minItems_one","minItems_other","maxItems_one","maxItems_other","notBefore","locale","eq","ne","gt","gte","lt","lte","in","nin","between","nbetween","like","nlike","isNull","isNotNull","theme","label","themes","aurora","sunset","midnight","ocean","marketplace","marketplaceDark","auth","authDark","confirm","cancel","breadcrumbs","wizard","steps","finish","submitting","stepOf","goToStep","status","pending","active","complete","resume"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/i18n/catalog/en.ts"],"sourcesContent":["/**\n * The `es` shape is the contract: annotating with it makes a key that exists in\n * Spanish but not here a *compile* error, so a half-translated locale cannot\n * reach a build. `tools/check-i18n.mjs` covers what types cannot — empty values,\n * and keys present here but absent from `es`.\n */\ntype EsControls = typeof import('./es.js').controls;\n\nexport const controls: EsControls = {\n table: {\n open: 'Open',\n select: 'Select',\n actions: 'Actions',\n new: 'New',\n filters: 'Filters',\n sorting: 'Sorting',\n loading: 'Loading…',\n empty: 'No records',\n error: 'Could not load records',\n selection: 'Selection',\n selectRow: 'Select {{record}}',\n expandRow: 'Expand {{record}}',\n collapseRow: 'Collapse {{record}}',\n selectAllOnPage: 'Select all on this page',\n selectAllMatching_one: 'Select {{count}} match',\n selectAllMatching_other: 'Select all {{count}} matches',\n selectedCount_one: '{{count}} selected',\n selectedCount_other: '{{count}} selected',\n clearSelection: 'Clear selection',\n },\n bulk: {\n running: 'Running…',\n succeeded_one: '{{count}} record updated',\n succeeded_other: '{{count}} records updated',\n failed_one: '{{count}} record failed',\n failed_other: '{{count}} records failed',\n retryFailed: 'Retry the failures',\n dismiss: 'Dismiss result',\n resultLabel: 'Bulk action result',\n barLabel: 'Actions on the selection',\n },\n form: {\n details: 'Details',\n new: 'New',\n view: 'View',\n edit: 'Edit',\n save: 'Save',\n saving: 'Saving…',\n delete: 'Delete',\n deleting: 'Deleting…',\n clone: 'Duplicate',\n moreActions: 'More actions',\n back: 'Back',\n loading: 'Loading…',\n },\n pagination: {\n label: 'Pages',\n summary: 'Page {{current}} of {{total}} · {{items}} items',\n rows: 'Rows',\n previous: 'Previous',\n next: 'Next',\n },\n columns: {\n title: 'Columns',\n subtitle: 'Visibility and order',\n reset: 'Reset to default',\n moveUp: 'Move {{column}} up',\n moveDown: 'Move {{column}} down',\n },\n filter: {\n match: 'Match',\n all: 'all',\n any: 'any',\n add: 'Add filter',\n apply: 'Apply',\n clear: 'Clear',\n none: 'No filterable members on this entity.',\n listPlaceholder: 'comma, separated, values',\n matchAria: 'Match all or any filter',\n member: 'Filter member',\n operator: 'Filter operator',\n value: 'Filter value',\n rangeEnd: 'Filter range end',\n remove: 'Remove filter',\n applyAria: 'Apply filters',\n clearAria: 'Clear filters',\n },\n sort: {\n by: 'Sort by',\n then: 'then by',\n ascending: 'ascending',\n descending: 'descending',\n add: 'Add sort',\n apply: 'Apply',\n clear: 'Clear',\n none: 'No sortable members on this entity.',\n member: 'Sort member',\n direction: 'Sort direction',\n raisePriority: 'Raise sort priority',\n lowerPriority: 'Lower sort priority',\n remove: 'Remove sort',\n applyAria: 'Apply sorting',\n clearAria: 'Clear sorting',\n },\n tabs: {\n close: 'Close {{label}}',\n new: 'Open a new tab',\n },\n link: {\n search: 'Search {{field}}',\n suggestAria: 'Show {{field}} suggestions',\n browseAria: 'Browse {{field}}',\n browseTitle: 'Select {{field}}',\n clear: 'Clear {{field}}',\n empty: '— unassigned —',\n loading: 'Loading…',\n noResults: 'No matches',\n error: 'Could not load options',\n close: 'Close',\n },\n detail: {\n addRow: 'Add row',\n removeRow: 'Remove row {{row}}',\n rowActions: 'Row actions',\n empty: 'No rows yet.',\n errorSummary_one: '{{count}} cell has errors',\n errorSummary_other: '{{count}} cells have errors',\n },\n value: {\n none: '— none —',\n yes: 'Yes',\n no: 'No',\n /**\n * A `composition` in a table: its rows are edited on the master's own\n * screen rather than in the cell, so the cell says how many there are.\n */\n rowCount_one: '{{count}} row',\n rowCount_other: '{{count}} rows',\n },\n /**\n * `required`…`option` are the metadata-derived rules. The rest are the\n * vocabulary an entity's Standard Schema writes its messages in: a schema\n * carries the *key*, never a sentence, so a rule stays translatable.\n *\n * `field` is the only parameter available — a Standard Schema issue exposes a\n * message and a path, nothing else — so these stay unquantified. A rule that\n * has to state its bound writes its own key in its domain catalog and passes\n * it namespaced (`entity:product-specification.validation.codeLength`).\n */\n validation: {\n required: '{{field}} is required',\n number: '{{field}} must be a number',\n date: '{{field}} must be a date',\n option: '{{field}} is not a valid option',\n minLength: '{{field}} is too short',\n maxLength: '{{field}} is too long',\n min: '{{field}} is too small',\n max: '{{field}} is too large',\n pattern: '{{field}} has an invalid format',\n minItems_one: '{{field}} needs at least {{count}} item',\n minItems_other: '{{field}} needs at least {{count}} items',\n maxItems_one: '{{field}} takes at most {{count}} item',\n maxItems_other: '{{field}} takes at most {{count}} items',\n notBefore: '{{field}} cannot be before {{other}}',\n locale: '{{field}} has no {{locale}} text',\n },\n operator: {\n eq: 'is',\n ne: 'is not',\n gt: 'greater than',\n gte: 'greater or equal',\n lt: 'less than',\n lte: 'less or equal',\n in: 'is one of',\n nin: 'is none of',\n between: 'between',\n nbetween: 'not between',\n like: 'contains',\n nlike: 'does not contain',\n isNull: 'is empty',\n isNotNull: 'is not empty',\n },\n theme: {\n label: 'Theme',\n },\n themes: {\n aurora: 'Aurora',\n sunset: 'Sunset',\n midnight: 'Midnight',\n ocean: 'Ocean (runtime)',\n marketplace: 'Marketplace',\n marketplaceDark: 'Marketplace dark',\n auth: 'Identity',\n authDark: 'Identity dark',\n },\n confirm: {\n confirm: 'Confirm',\n cancel: 'Cancel',\n title: 'Confirm',\n },\n breadcrumbs: {\n label: 'Breadcrumb',\n },\n wizard: {\n steps: 'Steps',\n previous: 'Back',\n next: 'Continue',\n finish: 'Finish',\n submitting: 'Submitting…',\n stepOf: 'Step {{current}} of {{total}}',\n goToStep: 'Back to {{label}}',\n status: {\n pending: 'pending',\n active: 'in progress',\n complete: 'completed',\n error: 'has errors',\n },\n resume: {\n title: 'Picking up where you left off',\n dismiss: 'Got it',\n },\n },\n};\n"],"names":["controls","table","open","select","actions","new","filters","sorting","loading","empty","error","selection","selectRow","expandRow","collapseRow","selectAllOnPage","selectAllMatching_one","selectAllMatching_other","selectedCount_one","selectedCount_other","clearSelection","bulk","running","succeeded_one","succeeded_other","failed_one","failed_other","retryFailed","dismiss","resultLabel","barLabel","form","details","view","edit","save","saving","delete","deleting","clone","moreActions","back","pagination","label","summary","rows","previous","next","columns","title","subtitle","reset","moveUp","moveDown","filter","match","all","any","add","apply","clear","none","listPlaceholder","matchAria","member","operator","value","rangeEnd","remove","applyAria","clearAria","sort","by","then","ascending","descending","direction","raisePriority","lowerPriority","tabs","close","link","search","suggestAria","browseAria","browseTitle","noResults","detail","addRow","removeRow","rowActions","errorSummary_one","errorSummary_other","yes","no","rowCount_one","rowCount_other","validation","required","number","date","option","minLength","maxLength","min","max","pattern","minItems_one","minItems_other","maxItems_one","maxItems_other","notBefore","locale","eq","ne","gt","gte","lt","lte","in","nin","between","nbetween","like","nlike","isNull","isNotNull","theme","themes","aurora","sunset","midnight","ocean","marketplace","marketplaceDark","auth","authDark","confirm","cancel","breadcrumbs","wizard","steps","finish","submitting","stepOf","goToStep","status","pending","active","complete","resume"],"mappings":"AAAA;;;;;CAKC,GAGD,OAAO,MAAMA,WAAuB;IAClCC,OAAO;QACLC,MAAM;QACNC,QAAQ;QACRC,SAAS;QACTC,KAAK;QACLC,SAAS;QACTC,SAAS;QACTC,SAAS;QACTC,OAAO;QACPC,OAAO;QACPC,WAAW;QACXC,WAAW;QACXC,WAAW;QACXC,aAAa;QACbC,iBAAiB;QACjBC,uBAAuB;QACvBC,yBAAyB;QACzBC,mBAAmB;QACnBC,qBAAqB;QACrBC,gBAAgB;IAClB;IACAC,MAAM;QACJC,SAAS;QACTC,eAAe;QACfC,iBAAiB;QACjBC,YAAY;QACZC,cAAc;QACdC,aAAa;QACbC,SAAS;QACTC,aAAa;QACbC,UAAU;IACZ;IACAC,MAAM;QACJC,SAAS;QACT3B,KAAK;QACL4B,MAAM;QACNC,MAAM;QACNC,MAAM;QACNC,QAAQ;QACRC,QAAQ;QACRC,UAAU;QACVC,OAAO;QACPC,aAAa;QACbC,MAAM;QACNjC,SAAS;IACX;IACAkC,YAAY;QACVC,OAAO;QACPC,SAAS;QACTC,MAAM;QACNC,UAAU;QACVC,MAAM;IACR;IACAC,SAAS;QACPC,OAAO;QACPC,UAAU;QACVC,OAAO;QACPC,QAAQ;QACRC,UAAU;IACZ;IACAC,QAAQ;QACNC,OAAO;QACPC,KAAK;QACLC,KAAK;QACLC,KAAK;QACLC,OAAO;QACPC,OAAO;QACPC,MAAM;QACNC,iBAAiB;QACjBC,WAAW;QACXC,QAAQ;QACRC,UAAU;QACVC,OAAO;QACPC,UAAU;QACVC,QAAQ;QACRC,WAAW;QACXC,WAAW;IACb;IACAC,MAAM;QACJC,IAAI;QACJC,MAAM;QACNC,WAAW;QACXC,YAAY;QACZjB,KAAK;QACLC,OAAO;QACPC,OAAO;QACPC,MAAM;QACNG,QAAQ;QACRY,WAAW;QACXC,eAAe;QACfC,eAAe;QACfV,QAAQ;QACRC,WAAW;QACXC,WAAW;IACb;IACAS,MAAM;QACJC,OAAO;QACP3E,KAAK;IACP;IACA4E,MAAM;QACJC,QAAQ;QACRC,aAAa;QACbC,YAAY;QACZC,aAAa;QACbzB,OAAO;QACPnD,OAAO;QACPD,SAAS;QACT8E,WAAW;QACX5E,OAAO;QACPsE,OAAO;IACT;IACAO,QAAQ;QACNC,QAAQ;QACRC,WAAW;QACXC,YAAY;QACZjF,OAAO;QACPkF,kBAAkB;QAClBC,oBAAoB;IACtB;IACA1B,OAAO;QACLL,MAAM;QACNgC,KAAK;QACLC,IAAI;QACJ;;;KAGC,GACDC,cAAc;QACdC,gBAAgB;IAClB;IACA;;;;;;;;;GASC,GACDC,YAAY;QACVC,UAAU;QACVC,QAAQ;QACRC,MAAM;QACNC,QAAQ;QACRC,WAAW;QACXC,WAAW;QACXC,KAAK;QACLC,KAAK;QACLC,SAAS;QACTC,cAAc;QACdC,gBAAgB;QAChBC,cAAc;QACdC,gBAAgB;QAChBC,WAAW;QACXC,QAAQ;IACV;IACA/C,UAAU;QACRgD,IAAI;QACJC,IAAI;QACJC,IAAI;QACJC,KAAK;QACLC,IAAI;QACJC,KAAK;QACLC,IAAI;QACJC,KAAK;QACLC,SAAS;QACTC,UAAU;QACVC,MAAM;QACNC,OAAO;QACPC,QAAQ;QACRC,WAAW;IACb;IACAC,OAAO;QACLpF,OAAO;IACT;IACAqF,QAAQ;QACNC,QAAQ;QACRC,QAAQ;QACRC,UAAU;QACVC,OAAO;QACPC,aAAa;QACbC,iBAAiB;QACjBC,MAAM;QACNC,UAAU;IACZ;IACAC,SAAS;QACPA,SAAS;QACTC,QAAQ;QACRzF,OAAO;IACT;IACA0F,aAAa;QACXhG,OAAO;IACT;IACAiG,QAAQ;QACNC,OAAO;QACP/F,UAAU;QACVC,MAAM;QACN+F,QAAQ;QACRC,YAAY;QACZC,QAAQ;QACRC,UAAU;QACVC,QAAQ;YACNC,SAAS;YACTC,QAAQ;YACRC,UAAU;YACV3I,OAAO;QACT;QACA4I,QAAQ;YACNrG,OAAO;YACPrB,SAAS;QACX;IACF;AACF,EAAE"}
@@ -22,6 +22,8 @@ export declare const controls: {
22
22
  */
23
23
  selection: string;
24
24
  selectRow: string;
25
+ expandRow: string;
26
+ collapseRow: string;
25
27
  selectAllOnPage: string;
26
28
  /**
27
29
  * La segunda selección — la que el servidor evalúa — lleva el número
@@ -63,6 +65,7 @@ export declare const controls: {
63
65
  loading: string;
64
66
  };
65
67
  pagination: {
68
+ label: string;
66
69
  summary: string;
67
70
  rows: string;
68
71
  previous: string;
@@ -1 +1 @@
1
- {"version":3,"file":"es.d.ts","sourceRoot":"","sources":["../../../src/i18n/catalog/es.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;QAWjB;;;;WAIG;;;;QAIH;;;WAGG;;;;;;;IAOL;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA8EH;;;;OAIG;;;;;;;;;;;;;IAaH;;;;;;OAMG;;;;;;;;;;;;;QAaD;;;WAGG;;;;IAIL;;;;;;;;;;OAUG;;;;;;;;;;;;;;;;;;IAkBH;;;;OAIG;;;;;;;;;;;;;;;;;;;;IAoBH;;;;;OAKG;;;;;;;;;;;;;;;;;;;IAmBH;;;;OAIG;;;;QAID;;;;WAIG;;;QAGH;;;;;WAKG;;;;QAIH;;;WAGG;;;;;;;QAOH;;;;WAIG;;;;;;CAMN,CAAC"}
1
+ {"version":3,"file":"es.d.ts","sourceRoot":"","sources":["../../../src/i18n/catalog/es.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;QAWjB;;;;WAIG;;;;;;QAMH;;;WAGG;;;;;;;IAOL;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA+EH;;;;OAIG;;;;;;;;;;;;;IAaH;;;;;;OAMG;;;;;;;;;;;;;QAaD;;;WAGG;;;;IAIL;;;;;;;;;;OAUG;;;;;;;;;;;;;;;;;;IAkBH;;;;OAIG;;;;;;;;;;;;;;;;;;;;IAoBH;;;;;OAKG;;;;;;;;;;;;;;;;;;;IAmBH;;;;OAIG;;;;QAID;;;;WAIG;;;QAGH;;;;;WAKG;;;;QAIH;;;WAGG;;;;;;;QAOH;;;;WAIG;;;;;;CAMN,CAAC"}
@@ -20,6 +20,8 @@
20
20
  * encabezado en cada fila no diría cuál es.
21
21
  */ selection: 'Selección',
22
22
  selectRow: 'Seleccionar {{record}}',
23
+ expandRow: 'Expandir {{record}}',
24
+ collapseRow: 'Contraer {{record}}',
23
25
  selectAllOnPage: 'Seleccionar todo en esta página',
24
26
  /**
25
27
  * La segunda selección — la que el servidor evalúa — lleva el número
@@ -59,6 +61,7 @@
59
61
  loading: 'Cargando…'
60
62
  },
61
63
  pagination: {
64
+ label: 'Páginas',
62
65
  summary: 'Página {{current}} de {{total}} · {{items}} registros',
63
66
  rows: 'Filas',
64
67
  previous: 'Anterior',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/i18n/catalog/es.ts"],"sourcesContent":["/**\n * Copy owned by `@entifix/react-controls` — the agnostic entity UI. It lives\n * here rather than next to the components because the typed-key augmentation has\n * to see every namespace from one module, and `@entifix/tooling` (T0) may not\n * import a T3 package.\n */\nexport const controls = {\n table: {\n open: 'Abrir',\n select: 'Seleccionar',\n actions: 'Acciones',\n new: 'Nuevo',\n filters: 'Filtros',\n sorting: 'Orden',\n loading: 'Cargando…',\n empty: 'Sin registros',\n error: 'No se pudieron cargar los registros',\n /**\n * La columna de selección. `selectRow` nombra la fila porque una casilla\n * sin etiqueta visible sólo se anuncia por su `aria-label`, y repetir el\n * encabezado en cada fila no diría cuál es.\n */\n selection: 'Selección',\n selectRow: 'Seleccionar {{record}}',\n selectAllOnPage: 'Seleccionar todo en esta página',\n /**\n * La segunda selección — la que el servidor evalúa — lleva el número\n * siempre: es la diferencia entre actuar sobre 25 filas y sobre 3.200.\n */\n selectAllMatching_one: 'Seleccionar {{count}} coincidencia',\n selectAllMatching_other: 'Seleccionar las {{count}} coincidencias',\n selectedCount_one: '{{count}} seleccionado',\n selectedCount_other: '{{count}} seleccionados',\n clearSelection: 'Limpiar selección',\n },\n /**\n * Una acción sobre una selección. El resultado es por fila, nunca un aviso\n * único: 40 seleccionadas y 3 fallidas no es ni un éxito ni un error.\n */\n bulk: {\n running: 'Ejecutando…',\n succeeded_one: '{{count}} registro actualizado',\n succeeded_other: '{{count}} registros actualizados',\n failed_one: '{{count}} registro falló',\n failed_other: '{{count}} registros fallaron',\n retryFailed: 'Reintentar los fallidos',\n dismiss: 'Cerrar resultado',\n resultLabel: 'Resultado de la acción masiva',\n barLabel: 'Acciones sobre la selección',\n },\n form: {\n details: 'Detalles',\n new: 'Nuevo',\n view: 'Ver',\n edit: 'Editar',\n save: 'Guardar',\n saving: 'Guardando…',\n delete: 'Eliminar',\n deleting: 'Eliminando…',\n clone: 'Duplicar',\n moreActions: 'Más acciones',\n back: 'Volver',\n loading: 'Cargando…',\n },\n pagination: {\n summary: 'Página {{current}} de {{total}} · {{items}} registros',\n rows: 'Filas',\n previous: 'Anterior',\n next: 'Siguiente',\n },\n columns: {\n title: 'Columnas',\n subtitle: 'Visibilidad y orden',\n reset: 'Restablecer',\n moveUp: 'Subir {{column}}',\n moveDown: 'Bajar {{column}}',\n },\n filter: {\n match: 'Coincidir',\n all: 'todos',\n any: 'cualquiera',\n add: 'Añadir filtro',\n apply: 'Aplicar',\n clear: 'Limpiar',\n none: 'Esta entidad no tiene miembros filtrables.',\n listPlaceholder: 'valores, separados, por comas',\n matchAria: 'Coincidir todos o cualquiera de los filtros',\n member: 'Miembro del filtro',\n operator: 'Operador del filtro',\n value: 'Valor del filtro',\n rangeEnd: 'Fin del rango del filtro',\n remove: 'Quitar filtro',\n applyAria: 'Aplicar filtros',\n clearAria: 'Limpiar filtros',\n },\n sort: {\n by: 'Ordenar por',\n then: 'después por',\n ascending: 'ascendente',\n descending: 'descendente',\n add: 'Añadir orden',\n apply: 'Aplicar',\n clear: 'Limpiar',\n none: 'Esta entidad no tiene miembros ordenables.',\n member: 'Miembro de orden',\n direction: 'Dirección de orden',\n raisePriority: 'Subir prioridad de orden',\n lowerPriority: 'Bajar prioridad de orden',\n remove: 'Quitar orden',\n applyAria: 'Aplicar orden',\n clearAria: 'Limpiar orden',\n },\n tabs: {\n close: 'Cerrar {{label}}',\n new: 'Abrir una pestaña nueva',\n },\n /**\n * El editor de una relación. `search` es la búsqueda rápida y `browse` abre el\n * catálogo completo; ambos botones son iconos, así que sus etiquetas `aria` son\n * lo único que un lector de pantalla anuncia.\n */\n link: {\n search: 'Buscar {{field}}',\n suggestAria: 'Ver sugerencias de {{field}}',\n browseAria: 'Examinar {{field}}',\n browseTitle: 'Seleccionar {{field}}',\n clear: 'Quitar {{field}}',\n empty: '— sin asignar —',\n loading: 'Cargando…',\n noResults: 'Sin coincidencias',\n error: 'No se pudieron cargar las opciones',\n close: 'Cerrar',\n },\n /**\n * La rejilla de detalle: las filas que un registro **posee** — las líneas de\n * un pedido, los renglones de una factura. `errorSummary` es el único\n * `role=\"alert\"` del control: un aviso por celda anunciaría la rejilla entera\n * en cada pulsación, y un mensaje que sólo vive en la celda es inalcanzable\n * cuando la fila que falla está fuera de pantalla.\n */\n detail: {\n addRow: 'Añadir fila',\n removeRow: 'Quitar fila {{row}}',\n rowActions: 'Acciones de fila',\n empty: 'Todavía no hay filas.',\n errorSummary_one: '{{count}} celda con errores',\n errorSummary_other: '{{count}} celdas con errores',\n },\n value: {\n none: '— ninguno —',\n yes: 'Sí',\n no: 'No',\n /**\n * Una `composition` en una tabla: sus filas se editan en la pantalla del\n * maestro, no en la celda, así que la celda dice cuántas hay.\n */\n rowCount_one: '{{count}} fila',\n rowCount_other: '{{count}} filas',\n },\n /**\n * `required`…`option` son las reglas derivadas de la metadata. El resto es el\n * vocabulario en el que un Standard Schema de una entidad escribe sus\n * mensajes: el esquema lleva la *clave*, nunca una frase, para que la regla\n * siga siendo traducible.\n *\n * `field` es el único parámetro disponible — un issue de Standard Schema\n * expone un mensaje y una ruta, nada más — así que estos mensajes no indican\n * el límite. Una regla que deba enunciarlo escribe su propia clave en el\n * catálogo de su dominio (`entity:product-specification.validation.codeLength`).\n */\n validation: {\n required: '{{field}} es obligatorio',\n number: '{{field}} debe ser un número',\n date: '{{field}} debe ser una fecha',\n option: '{{field}} no es una opción válida',\n minLength: '{{field}} es demasiado corto',\n maxLength: '{{field}} es demasiado largo',\n min: '{{field}} es demasiado pequeño',\n max: '{{field}} es demasiado grande',\n pattern: '{{field}} tiene un formato inválido',\n minItems_one: '{{field}} necesita al menos {{count}} elemento',\n minItems_other: '{{field}} necesita al menos {{count}} elementos',\n maxItems_one: '{{field}} admite como máximo {{count}} elemento',\n maxItems_other: '{{field}} admite como máximo {{count}} elementos',\n notBefore: '{{field}} no puede ser anterior a {{other}}',\n locale: '{{field}} no tiene texto en {{locale}}',\n },\n /**\n * The filter vocabulary shown to a user. Deliberately distinct from\n * `RSQL_TOKENS` in `@entifix/core` — those are wire tokens (`==`, `=gt=`)\n * and must never be translated.\n */\n operator: {\n eq: 'es',\n ne: 'no es',\n gt: 'mayor que',\n gte: 'mayor o igual',\n lt: 'menor que',\n lte: 'menor o igual',\n in: 'es uno de',\n nin: 'no es ninguno de',\n between: 'entre',\n nbetween: 'fuera de',\n like: 'contiene',\n nlike: 'no contiene',\n isNull: 'está vacío',\n isNotNull: 'no está vacío',\n },\n theme: {\n label: 'Tema',\n },\n /**\n * Captions for the theme presets shipped by `@entifix/style`, plus the\n * per-app brand sets. They name design-system tokens, not app copy, so they\n * live beside the switcher that renders them and stay reachable from\n * Storybook, where no shell is mounted.\n */\n themes: {\n aurora: 'Aurora',\n sunset: 'Atardecer',\n midnight: 'Medianoche',\n ocean: 'Océano (en runtime)',\n marketplace: 'Marketplace',\n marketplaceDark: 'Marketplace oscuro',\n auth: 'Identidad',\n authDark: 'Identidad oscura',\n },\n confirm: {\n confirm: 'Confirmar',\n cancel: 'Cancelar',\n title: 'Confirmar',\n },\n breadcrumbs: {\n label: 'Ruta de navegación',\n },\n /**\n * El asistente. Las etiquetas de cada paso las traduce quien declara el\n * asistente, no el control: un paso es del dominio que lo definió, y su copy\n * vive en `shell:` junto al resto de sus nombres.\n */\n wizard: {\n steps: 'Pasos',\n previous: 'Atrás',\n /**\n * \"Continuar\" y no \"Siguiente\": una tabla dentro de un paso trae su propio\n * paginador, cuyo botón **es** \"Siguiente\". Dos controles con el mismo\n * nombre en la misma pantalla se anuncian igual y hacen cosas distintas.\n */\n next: 'Continuar',\n finish: 'Finalizar',\n /**\n * Un envío que **no** resuelve. El asistente entrega el trabajo y vuelve\n * antes de que la escritura termine, así que dice \"enviando\" y\n * nunca \"guardado\": lo segundo sería una afirmación que el control no puede\n * comprobar.\n */\n submitting: 'Enviando…',\n stepOf: 'Paso {{current}} de {{total}}',\n goToStep: 'Volver a {{label}}',\n /**\n * El estado de cada paso, anunciado además de mostrarse. El color por sí\n * solo no lo dice, y un lector de pantalla recorre la lista sin verlo.\n */\n status: {\n pending: 'pendiente',\n active: 'en curso',\n complete: 'completado',\n error: 'con errores',\n },\n /**\n * El recordatorio al retomar. La persistencia da el \"continuar\" gratis; lo\n * que no da es acordarse de lo ya decidido, que es la falla conocida de los\n * asistentes largos.\n */\n resume: {\n title: 'Retomando donde lo dejaste',\n dismiss: 'Entendido',\n },\n },\n};\n"],"names":["controls","table","open","select","actions","new","filters","sorting","loading","empty","error","selection","selectRow","selectAllOnPage","selectAllMatching_one","selectAllMatching_other","selectedCount_one","selectedCount_other","clearSelection","bulk","running","succeeded_one","succeeded_other","failed_one","failed_other","retryFailed","dismiss","resultLabel","barLabel","form","details","view","edit","save","saving","delete","deleting","clone","moreActions","back","pagination","summary","rows","previous","next","columns","title","subtitle","reset","moveUp","moveDown","filter","match","all","any","add","apply","clear","none","listPlaceholder","matchAria","member","operator","value","rangeEnd","remove","applyAria","clearAria","sort","by","then","ascending","descending","direction","raisePriority","lowerPriority","tabs","close","link","search","suggestAria","browseAria","browseTitle","noResults","detail","addRow","removeRow","rowActions","errorSummary_one","errorSummary_other","yes","no","rowCount_one","rowCount_other","validation","required","number","date","option","minLength","maxLength","min","max","pattern","minItems_one","minItems_other","maxItems_one","maxItems_other","notBefore","locale","eq","ne","gt","gte","lt","lte","in","nin","between","nbetween","like","nlike","isNull","isNotNull","theme","label","themes","aurora","sunset","midnight","ocean","marketplace","marketplaceDark","auth","authDark","confirm","cancel","breadcrumbs","wizard","steps","finish","submitting","stepOf","goToStep","status","pending","active","complete","resume"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/i18n/catalog/es.ts"],"sourcesContent":["/**\n * Copy owned by `@entifix/react-controls` — the agnostic entity UI. It lives\n * here rather than next to the components because the typed-key augmentation has\n * to see every namespace from one module, and `@entifix/tooling` (T0) may not\n * import a T3 package.\n */\nexport const controls = {\n table: {\n open: 'Abrir',\n select: 'Seleccionar',\n actions: 'Acciones',\n new: 'Nuevo',\n filters: 'Filtros',\n sorting: 'Orden',\n loading: 'Cargando…',\n empty: 'Sin registros',\n error: 'No se pudieron cargar los registros',\n /**\n * La columna de selección. `selectRow` nombra la fila porque una casilla\n * sin etiqueta visible sólo se anuncia por su `aria-label`, y repetir el\n * encabezado en cada fila no diría cuál es.\n */\n selection: 'Selección',\n selectRow: 'Seleccionar {{record}}',\n expandRow: 'Expandir {{record}}',\n collapseRow: 'Contraer {{record}}',\n selectAllOnPage: 'Seleccionar todo en esta página',\n /**\n * La segunda selección — la que el servidor evalúa — lleva el número\n * siempre: es la diferencia entre actuar sobre 25 filas y sobre 3.200.\n */\n selectAllMatching_one: 'Seleccionar {{count}} coincidencia',\n selectAllMatching_other: 'Seleccionar las {{count}} coincidencias',\n selectedCount_one: '{{count}} seleccionado',\n selectedCount_other: '{{count}} seleccionados',\n clearSelection: 'Limpiar selección',\n },\n /**\n * Una acción sobre una selección. El resultado es por fila, nunca un aviso\n * único: 40 seleccionadas y 3 fallidas no es ni un éxito ni un error.\n */\n bulk: {\n running: 'Ejecutando…',\n succeeded_one: '{{count}} registro actualizado',\n succeeded_other: '{{count}} registros actualizados',\n failed_one: '{{count}} registro falló',\n failed_other: '{{count}} registros fallaron',\n retryFailed: 'Reintentar los fallidos',\n dismiss: 'Cerrar resultado',\n resultLabel: 'Resultado de la acción masiva',\n barLabel: 'Acciones sobre la selección',\n },\n form: {\n details: 'Detalles',\n new: 'Nuevo',\n view: 'Ver',\n edit: 'Editar',\n save: 'Guardar',\n saving: 'Guardando…',\n delete: 'Eliminar',\n deleting: 'Eliminando…',\n clone: 'Duplicar',\n moreActions: 'Más acciones',\n back: 'Volver',\n loading: 'Cargando…',\n },\n pagination: {\n label: 'Páginas',\n summary: 'Página {{current}} de {{total}} · {{items}} registros',\n rows: 'Filas',\n previous: 'Anterior',\n next: 'Siguiente',\n },\n columns: {\n title: 'Columnas',\n subtitle: 'Visibilidad y orden',\n reset: 'Restablecer',\n moveUp: 'Subir {{column}}',\n moveDown: 'Bajar {{column}}',\n },\n filter: {\n match: 'Coincidir',\n all: 'todos',\n any: 'cualquiera',\n add: 'Añadir filtro',\n apply: 'Aplicar',\n clear: 'Limpiar',\n none: 'Esta entidad no tiene miembros filtrables.',\n listPlaceholder: 'valores, separados, por comas',\n matchAria: 'Coincidir todos o cualquiera de los filtros',\n member: 'Miembro del filtro',\n operator: 'Operador del filtro',\n value: 'Valor del filtro',\n rangeEnd: 'Fin del rango del filtro',\n remove: 'Quitar filtro',\n applyAria: 'Aplicar filtros',\n clearAria: 'Limpiar filtros',\n },\n sort: {\n by: 'Ordenar por',\n then: 'después por',\n ascending: 'ascendente',\n descending: 'descendente',\n add: 'Añadir orden',\n apply: 'Aplicar',\n clear: 'Limpiar',\n none: 'Esta entidad no tiene miembros ordenables.',\n member: 'Miembro de orden',\n direction: 'Dirección de orden',\n raisePriority: 'Subir prioridad de orden',\n lowerPriority: 'Bajar prioridad de orden',\n remove: 'Quitar orden',\n applyAria: 'Aplicar orden',\n clearAria: 'Limpiar orden',\n },\n tabs: {\n close: 'Cerrar {{label}}',\n new: 'Abrir una pestaña nueva',\n },\n /**\n * El editor de una relación. `search` es la búsqueda rápida y `browse` abre el\n * catálogo completo; ambos botones son iconos, así que sus etiquetas `aria` son\n * lo único que un lector de pantalla anuncia.\n */\n link: {\n search: 'Buscar {{field}}',\n suggestAria: 'Ver sugerencias de {{field}}',\n browseAria: 'Examinar {{field}}',\n browseTitle: 'Seleccionar {{field}}',\n clear: 'Quitar {{field}}',\n empty: '— sin asignar —',\n loading: 'Cargando…',\n noResults: 'Sin coincidencias',\n error: 'No se pudieron cargar las opciones',\n close: 'Cerrar',\n },\n /**\n * La rejilla de detalle: las filas que un registro **posee** — las líneas de\n * un pedido, los renglones de una factura. `errorSummary` es el único\n * `role=\"alert\"` del control: un aviso por celda anunciaría la rejilla entera\n * en cada pulsación, y un mensaje que sólo vive en la celda es inalcanzable\n * cuando la fila que falla está fuera de pantalla.\n */\n detail: {\n addRow: 'Añadir fila',\n removeRow: 'Quitar fila {{row}}',\n rowActions: 'Acciones de fila',\n empty: 'Todavía no hay filas.',\n errorSummary_one: '{{count}} celda con errores',\n errorSummary_other: '{{count}} celdas con errores',\n },\n value: {\n none: '— ninguno —',\n yes: 'Sí',\n no: 'No',\n /**\n * Una `composition` en una tabla: sus filas se editan en la pantalla del\n * maestro, no en la celda, así que la celda dice cuántas hay.\n */\n rowCount_one: '{{count}} fila',\n rowCount_other: '{{count}} filas',\n },\n /**\n * `required`…`option` son las reglas derivadas de la metadata. El resto es el\n * vocabulario en el que un Standard Schema de una entidad escribe sus\n * mensajes: el esquema lleva la *clave*, nunca una frase, para que la regla\n * siga siendo traducible.\n *\n * `field` es el único parámetro disponible — un issue de Standard Schema\n * expone un mensaje y una ruta, nada más — así que estos mensajes no indican\n * el límite. Una regla que deba enunciarlo escribe su propia clave en el\n * catálogo de su dominio (`entity:product-specification.validation.codeLength`).\n */\n validation: {\n required: '{{field}} es obligatorio',\n number: '{{field}} debe ser un número',\n date: '{{field}} debe ser una fecha',\n option: '{{field}} no es una opción válida',\n minLength: '{{field}} es demasiado corto',\n maxLength: '{{field}} es demasiado largo',\n min: '{{field}} es demasiado pequeño',\n max: '{{field}} es demasiado grande',\n pattern: '{{field}} tiene un formato inválido',\n minItems_one: '{{field}} necesita al menos {{count}} elemento',\n minItems_other: '{{field}} necesita al menos {{count}} elementos',\n maxItems_one: '{{field}} admite como máximo {{count}} elemento',\n maxItems_other: '{{field}} admite como máximo {{count}} elementos',\n notBefore: '{{field}} no puede ser anterior a {{other}}',\n locale: '{{field}} no tiene texto en {{locale}}',\n },\n /**\n * The filter vocabulary shown to a user. Deliberately distinct from\n * `RSQL_TOKENS` in `@entifix/core` — those are wire tokens (`==`, `=gt=`)\n * and must never be translated.\n */\n operator: {\n eq: 'es',\n ne: 'no es',\n gt: 'mayor que',\n gte: 'mayor o igual',\n lt: 'menor que',\n lte: 'menor o igual',\n in: 'es uno de',\n nin: 'no es ninguno de',\n between: 'entre',\n nbetween: 'fuera de',\n like: 'contiene',\n nlike: 'no contiene',\n isNull: 'está vacío',\n isNotNull: 'no está vacío',\n },\n theme: {\n label: 'Tema',\n },\n /**\n * Captions for the theme presets shipped by `@entifix/style`, plus the\n * per-app brand sets. They name design-system tokens, not app copy, so they\n * live beside the switcher that renders them and stay reachable from\n * Storybook, where no shell is mounted.\n */\n themes: {\n aurora: 'Aurora',\n sunset: 'Atardecer',\n midnight: 'Medianoche',\n ocean: 'Océano (en runtime)',\n marketplace: 'Marketplace',\n marketplaceDark: 'Marketplace oscuro',\n auth: 'Identidad',\n authDark: 'Identidad oscura',\n },\n confirm: {\n confirm: 'Confirmar',\n cancel: 'Cancelar',\n title: 'Confirmar',\n },\n breadcrumbs: {\n label: 'Ruta de navegación',\n },\n /**\n * El asistente. Las etiquetas de cada paso las traduce quien declara el\n * asistente, no el control: un paso es del dominio que lo definió, y su copy\n * vive en `shell:` junto al resto de sus nombres.\n */\n wizard: {\n steps: 'Pasos',\n previous: 'Atrás',\n /**\n * \"Continuar\" y no \"Siguiente\": una tabla dentro de un paso trae su propio\n * paginador, cuyo botón **es** \"Siguiente\". Dos controles con el mismo\n * nombre en la misma pantalla se anuncian igual y hacen cosas distintas.\n */\n next: 'Continuar',\n finish: 'Finalizar',\n /**\n * Un envío que **no** resuelve. El asistente entrega el trabajo y vuelve\n * antes de que la escritura termine, así que dice \"enviando\" y\n * nunca \"guardado\": lo segundo sería una afirmación que el control no puede\n * comprobar.\n */\n submitting: 'Enviando…',\n stepOf: 'Paso {{current}} de {{total}}',\n goToStep: 'Volver a {{label}}',\n /**\n * El estado de cada paso, anunciado además de mostrarse. El color por sí\n * solo no lo dice, y un lector de pantalla recorre la lista sin verlo.\n */\n status: {\n pending: 'pendiente',\n active: 'en curso',\n complete: 'completado',\n error: 'con errores',\n },\n /**\n * El recordatorio al retomar. La persistencia da el \"continuar\" gratis; lo\n * que no da es acordarse de lo ya decidido, que es la falla conocida de los\n * asistentes largos.\n */\n resume: {\n title: 'Retomando donde lo dejaste',\n dismiss: 'Entendido',\n },\n },\n};\n"],"names":["controls","table","open","select","actions","new","filters","sorting","loading","empty","error","selection","selectRow","expandRow","collapseRow","selectAllOnPage","selectAllMatching_one","selectAllMatching_other","selectedCount_one","selectedCount_other","clearSelection","bulk","running","succeeded_one","succeeded_other","failed_one","failed_other","retryFailed","dismiss","resultLabel","barLabel","form","details","view","edit","save","saving","delete","deleting","clone","moreActions","back","pagination","label","summary","rows","previous","next","columns","title","subtitle","reset","moveUp","moveDown","filter","match","all","any","add","apply","clear","none","listPlaceholder","matchAria","member","operator","value","rangeEnd","remove","applyAria","clearAria","sort","by","then","ascending","descending","direction","raisePriority","lowerPriority","tabs","close","link","search","suggestAria","browseAria","browseTitle","noResults","detail","addRow","removeRow","rowActions","errorSummary_one","errorSummary_other","yes","no","rowCount_one","rowCount_other","validation","required","number","date","option","minLength","maxLength","min","max","pattern","minItems_one","minItems_other","maxItems_one","maxItems_other","notBefore","locale","eq","ne","gt","gte","lt","lte","in","nin","between","nbetween","like","nlike","isNull","isNotNull","theme","themes","aurora","sunset","midnight","ocean","marketplace","marketplaceDark","auth","authDark","confirm","cancel","breadcrumbs","wizard","steps","finish","submitting","stepOf","goToStep","status","pending","active","complete","resume"],"mappings":"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"}
@@ -22,6 +22,8 @@ export declare const controlsCatalogs: {
22
22
  error: string;
23
23
  selection: string;
24
24
  selectRow: string;
25
+ expandRow: string;
26
+ collapseRow: string;
25
27
  selectAllOnPage: string;
26
28
  selectAllMatching_one: string;
27
29
  selectAllMatching_other: string;
@@ -55,6 +57,7 @@ export declare const controlsCatalogs: {
55
57
  loading: string;
56
58
  };
57
59
  pagination: {
60
+ label: string;
58
61
  summary: string;
59
62
  rows: string;
60
63
  previous: string;
@@ -222,6 +225,8 @@ export declare const controlsCatalogs: {
222
225
  error: string;
223
226
  selection: string;
224
227
  selectRow: string;
228
+ expandRow: string;
229
+ collapseRow: string;
225
230
  selectAllOnPage: string;
226
231
  selectAllMatching_one: string;
227
232
  selectAllMatching_other: string;
@@ -255,6 +260,7 @@ export declare const controlsCatalogs: {
255
260
  loading: string;
256
261
  };
257
262
  pagination: {
263
+ label: string;
258
264
  summary: string;
259
265
  rows: string;
260
266
  previous: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/i18n/catalog/index.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAGnB,CAAC;AAEX,qEAAqE;AACrE,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC;AAEhE,eAAO,MAAM,mBAAmB,uBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/i18n/catalog/index.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAGnB,CAAC;AAEX,qEAAqE;AACrE,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC;AAEhE,eAAO,MAAM,mBAAmB,uBAAwB,CAAC"}
@@ -4,10 +4,11 @@ import type { ReactNode } from 'react';
4
4
  * What a navigation control means, so a host can do something other than follow
5
5
  * the `href`.
6
6
  *
7
- * `open` goes to a record, `new` to its create form, `back` to the list. A
8
- * workspace tab needs to know which, because it answers each by opening a tab —
9
- * and a URL alone does not say what it points at without the host parsing its
10
- * own routes back.
7
+ * `open` goes to a record, `new` to its create form, `back` to the list, and
8
+ * `page` to another page of the listing already on screen. A workspace tab
9
+ * needs to know which, because it answers each by opening a tab — and a URL
10
+ * alone does not say what it points at without the host parsing its own routes
11
+ * back.
11
12
  */
12
13
  export type NavigationIntent = {
13
14
  readonly kind: 'open';
@@ -16,6 +17,9 @@ export type NavigationIntent = {
16
17
  readonly kind: 'new';
17
18
  } | {
18
19
  readonly kind: 'back';
20
+ } | {
21
+ readonly kind: 'page';
22
+ readonly page: number;
19
23
  };
20
24
  export interface RenderLinkProps {
21
25
  /** Always a real URL: middle-click and "open in new tab" must keep working. */
@@ -1 +1 @@
1
- {"version":3,"file":"render-link.d.ts","sourceRoot":"","sources":["../../../src/ui/actions/render-link.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAA;CAAE,GAChD;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CAAE,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9B,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;CACnC;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,SAAS,CAAC;AAE/D,oCAAoC;AACpC,eAAO,MAAM,YAAY,EAAE,UAI1B,CAAC"}
1
+ {"version":3,"file":"render-link.d.ts","sourceRoot":"","sources":["../../../src/ui/actions/render-link.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;;;;;;;;GASG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAA;CAAE,GAChD;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CAAE,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;CACnC;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,SAAS,CAAC;AAE/D,oCAAoC;AACpC,eAAO,MAAM,YAAY,EAAE,UAI1B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/actions/render-link.tsx"],"sourcesContent":["import type { EntityId } from '@entifix/core';\nimport type { ReactNode } from 'react';\n\n/**\n * What a navigation control means, so a host can do something other than follow\n * the `href`.\n *\n * `open` goes to a record, `new` to its create form, `back` to the list. A\n * workspace tab needs to know which, because it answers each by opening a tab —\n * and a URL alone does not say what it points at without the host parsing its\n * own routes back.\n */\nexport type NavigationIntent =\n | { readonly kind: 'open'; readonly id: EntityId }\n | { readonly kind: 'new' }\n | { readonly kind: 'back' };\n\nexport interface RenderLinkProps {\n /** Always a real URL: middle-click and \"open in new tab\" must keep working. */\n readonly href: string;\n /** The styling the control would carry as a plain anchor. */\n readonly className: string;\n readonly children: ReactNode;\n readonly intent: NavigationIntent;\n}\n\n/**\n * Renders a navigation control.\n *\n * A seam rather than a router dependency: this package runs under any router or\n * none, so a plain anchor is all it can render by itself — and a plain anchor in\n * a client-routed application is a full document load, which rebuilds the\n * bundle, empties every cache and, inside a workspace tab, leaves the workspace.\n * A host passes a renderer built on its own link component instead.\n */\nexport type RenderLink = (props: RenderLinkProps) => ReactNode;\n\n/** The fallback: a plain anchor. */\nexport const renderAnchor: RenderLink = ({ href, className, children }) => (\n <a href={href} className={className}>\n {children}\n </a>\n);\n"],"names":["renderAnchor","href","className","children","a"],"mappings":";AAqCA,kCAAkC,GAClC,OAAO,MAAMA,eAA2B,CAAC,EAAEC,IAAI,EAAEC,SAAS,EAAEC,QAAQ,EAAE,iBACpE,KAACC;QAAEH,MAAMA;QAAMC,WAAWA;kBACvBC;OAEH"}
1
+ {"version":3,"sources":["../../../src/ui/actions/render-link.tsx"],"sourcesContent":["import type { EntityId } from '@entifix/core';\nimport type { ReactNode } from 'react';\n\n/**\n * What a navigation control means, so a host can do something other than follow\n * the `href`.\n *\n * `open` goes to a record, `new` to its create form, `back` to the list, and\n * `page` to another page of the listing already on screen. A workspace tab\n * needs to know which, because it answers each by opening a tab — and a URL\n * alone does not say what it points at without the host parsing its own routes\n * back.\n */\nexport type NavigationIntent =\n | { readonly kind: 'open'; readonly id: EntityId }\n | { readonly kind: 'new' }\n | { readonly kind: 'back' }\n | { readonly kind: 'page'; readonly page: number };\n\nexport interface RenderLinkProps {\n /** Always a real URL: middle-click and \"open in new tab\" must keep working. */\n readonly href: string;\n /** The styling the control would carry as a plain anchor. */\n readonly className: string;\n readonly children: ReactNode;\n readonly intent: NavigationIntent;\n}\n\n/**\n * Renders a navigation control.\n *\n * A seam rather than a router dependency: this package runs under any router or\n * none, so a plain anchor is all it can render by itself — and a plain anchor in\n * a client-routed application is a full document load, which rebuilds the\n * bundle, empties every cache and, inside a workspace tab, leaves the workspace.\n * A host passes a renderer built on its own link component instead.\n */\nexport type RenderLink = (props: RenderLinkProps) => ReactNode;\n\n/** The fallback: a plain anchor. */\nexport const renderAnchor: RenderLink = ({ href, className, children }) => (\n <a href={href} className={className}>\n {children}\n </a>\n);\n"],"names":["renderAnchor","href","className","children","a"],"mappings":";AAuCA,kCAAkC,GAClC,OAAO,MAAMA,eAA2B,CAAC,EAAEC,IAAI,EAAEC,SAAS,EAAEC,QAAQ,EAAE,iBACpE,KAACC;QAAEH,MAAMA;QAAMC,WAAWA;kBACvBC;OAEH"}
@@ -1,2 +1,4 @@
1
+ export * from './page-math.js';
1
2
  export * from './pagination.js';
3
+ export * from './pagination-links.js';
2
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ui/molecules/pagination/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ui/molecules/pagination/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC"}
@@ -1,3 +1,5 @@
1
+ export * from './page-math.js';
1
2
  export * from './pagination.js';
3
+ export * from './pagination-links.js';
2
4
 
3
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/ui/molecules/pagination/index.ts"],"sourcesContent":["export * from './pagination.js';\n"],"names":[],"mappings":"AAAA,cAAc,kBAAkB"}
1
+ {"version":3,"sources":["../../../../src/ui/molecules/pagination/index.ts"],"sourcesContent":["export * from './page-math.js';\nexport * from './pagination.js';\nexport * from './pagination-links.js';\n"],"names":[],"mappings":"AAAA,cAAc,iBAAiB;AAC/B,cAAc,kBAAkB;AAChC,cAAc,wBAAwB"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * How many pages a listing spans. An empty listing is still one page: "page 1
3
+ * of 0" reads as broken.
4
+ */
5
+ export declare function pageCount(totalItems: number, pageSize: number): number;
6
+ /** A page number to link to, or a gap standing for the pages between two. */
7
+ export type PageWindowEntry = {
8
+ readonly kind: 'page';
9
+ readonly page: number;
10
+ } | {
11
+ readonly kind: 'gap';
12
+ readonly after: number;
13
+ };
14
+ /**
15
+ * The page numbers worth linking from `current`: the first, the last, and
16
+ * `radius` either side of the current one, with a gap wherever pages are
17
+ * skipped.
18
+ *
19
+ * A gap replaces two or more pages, never one: `1 … 3` hides a single page
20
+ * behind a glyph that is as wide as its number, so the page is shown instead.
21
+ */
22
+ export declare function pageWindow(current: number, total: number, radius?: number): PageWindowEntry[];
23
+ //# sourceMappingURL=page-math.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-math.d.ts","sourceRoot":"","sources":["../../../../src/ui/molecules/pagination/page-math.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,6EAA6E;AAC7E,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,SAAI,GACT,eAAe,EAAE,CAkBnB"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * How many pages a listing spans. An empty listing is still one page: "page 1
3
+ * of 0" reads as broken.
4
+ */ export function pageCount(totalItems, pageSize) {
5
+ return Math.max(1, Math.ceil(totalItems / pageSize));
6
+ }
7
+ /**
8
+ * The page numbers worth linking from `current`: the first, the last, and
9
+ * `radius` either side of the current one, with a gap wherever pages are
10
+ * skipped.
11
+ *
12
+ * A gap replaces two or more pages, never one: `1 … 3` hides a single page
13
+ * behind a glyph that is as wide as its number, so the page is shown instead.
14
+ */ export function pageWindow(current, total, radius = 1) {
15
+ const wanted = new Set([
16
+ 1,
17
+ total
18
+ ]);
19
+ for(let page = current - radius; page <= current + radius; page += 1){
20
+ if (page >= 1 && page <= total) wanted.add(page);
21
+ }
22
+ const pages = [
23
+ ...wanted
24
+ ].sort((a, b)=>a - b);
25
+ const entries = [];
26
+ pages.forEach((page, index)=>{
27
+ const previous = pages[index - 1];
28
+ if (previous !== undefined && page - previous === 2) {
29
+ entries.push({
30
+ kind: 'page',
31
+ page: previous + 1
32
+ });
33
+ } else if (previous !== undefined && page - previous > 2) {
34
+ entries.push({
35
+ kind: 'gap',
36
+ after: previous
37
+ });
38
+ }
39
+ entries.push({
40
+ kind: 'page',
41
+ page
42
+ });
43
+ });
44
+ return entries;
45
+ }
46
+
47
+ //# sourceMappingURL=page-math.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/ui/molecules/pagination/page-math.ts"],"sourcesContent":["/**\n * How many pages a listing spans. An empty listing is still one page: \"page 1\n * of 0\" reads as broken.\n */\nexport function pageCount(totalItems: number, pageSize: number): number {\n return Math.max(1, Math.ceil(totalItems / pageSize));\n}\n\n/** A page number to link to, or a gap standing for the pages between two. */\nexport type PageWindowEntry =\n | { readonly kind: 'page'; readonly page: number }\n | { readonly kind: 'gap'; readonly after: number };\n\n/**\n * The page numbers worth linking from `current`: the first, the last, and\n * `radius` either side of the current one, with a gap wherever pages are\n * skipped.\n *\n * A gap replaces two or more pages, never one: `1 … 3` hides a single page\n * behind a glyph that is as wide as its number, so the page is shown instead.\n */\nexport function pageWindow(\n current: number,\n total: number,\n radius = 1,\n): PageWindowEntry[] {\n const wanted = new Set<number>([1, total]);\n for (let page = current - radius; page <= current + radius; page += 1) {\n if (page >= 1 && page <= total) wanted.add(page);\n }\n const pages = [...wanted].sort((a, b) => a - b);\n\n const entries: PageWindowEntry[] = [];\n pages.forEach((page, index) => {\n const previous = pages[index - 1];\n if (previous !== undefined && page - previous === 2) {\n entries.push({ kind: 'page', page: previous + 1 });\n } else if (previous !== undefined && page - previous > 2) {\n entries.push({ kind: 'gap', after: previous });\n }\n entries.push({ kind: 'page', page });\n });\n return entries;\n}\n"],"names":["pageCount","totalItems","pageSize","Math","max","ceil","pageWindow","current","total","radius","wanted","Set","page","add","pages","sort","a","b","entries","forEach","index","previous","undefined","push","kind","after"],"mappings":"AAAA;;;CAGC,GACD,OAAO,SAASA,UAAUC,UAAkB,EAAEC,QAAgB;IAC5D,OAAOC,KAAKC,GAAG,CAAC,GAAGD,KAAKE,IAAI,CAACJ,aAAaC;AAC5C;AAOA;;;;;;;CAOC,GACD,OAAO,SAASI,WACdC,OAAe,EACfC,KAAa,EACbC,SAAS,CAAC;IAEV,MAAMC,SAAS,IAAIC,IAAY;QAAC;QAAGH;KAAM;IACzC,IAAK,IAAII,OAAOL,UAAUE,QAAQG,QAAQL,UAAUE,QAAQG,QAAQ,EAAG;QACrE,IAAIA,QAAQ,KAAKA,QAAQJ,OAAOE,OAAOG,GAAG,CAACD;IAC7C;IACA,MAAME,QAAQ;WAAIJ;KAAO,CAACK,IAAI,CAAC,CAACC,GAAGC,IAAMD,IAAIC;IAE7C,MAAMC,UAA6B,EAAE;IACrCJ,MAAMK,OAAO,CAAC,CAACP,MAAMQ;QACnB,MAAMC,WAAWP,KAAK,CAACM,QAAQ,EAAE;QACjC,IAAIC,aAAaC,aAAaV,OAAOS,aAAa,GAAG;YACnDH,QAAQK,IAAI,CAAC;gBAAEC,MAAM;gBAAQZ,MAAMS,WAAW;YAAE;QAClD,OAAO,IAAIA,aAAaC,aAAaV,OAAOS,WAAW,GAAG;YACxDH,QAAQK,IAAI,CAAC;gBAAEC,MAAM;gBAAOC,OAAOJ;YAAS;QAC9C;QACAH,QAAQK,IAAI,CAAC;YAAEC,MAAM;YAAQZ;QAAK;IACpC;IACA,OAAOM;AACT"}
@@ -0,0 +1,33 @@
1
+ import type { Translate } from '../../../i18n/index.js';
2
+ import { type RenderLink } from '../../actions/render-link.js';
3
+ export interface PaginationLinksProps {
4
+ currentPage: number;
5
+ pageSize: number;
6
+ totalItems: number;
7
+ /** The URL of a page. Page 1 is the host's to spell, usually without `?page`. */
8
+ hrefFor: (page: number) => string;
9
+ /**
10
+ * Resolves this control's keys (`pagination.*`). A server component has no
11
+ * context to read a translator from, so the host hands one over: in a Next
12
+ * host, the server translator for the controls' namespace.
13
+ */
14
+ t: Translate;
15
+ /** Defaults to a plain anchor; a host passes its own link component. */
16
+ renderLink?: RenderLink;
17
+ /** How many page numbers either side of the current one. Defaults to 1. */
18
+ radius?: number;
19
+ className?: string;
20
+ }
21
+ /**
22
+ * {@link Pagination} as links, and a **server** component.
23
+ *
24
+ * Every page is an address: a crawler follows it, a visitor middle-clicks it,
25
+ * and the page works with JavaScript off. `Pagination` steps through a
26
+ * callback and carries a `'use client'`, which a server-rendered listing
27
+ * cannot use without turning its pager into a client boundary for no gain.
28
+ *
29
+ * Previous and Next stay in the layout at the edges, as text marked
30
+ * `aria-disabled` rather than as links that go nowhere.
31
+ */
32
+ export declare function PaginationLinks({ currentPage, pageSize, totalItems, hrefFor, t, renderLink, radius, className, }: PaginationLinksProps): import("react").JSX.Element;
33
+ //# sourceMappingURL=pagination-links.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination-links.d.ts","sourceRoot":"","sources":["../../../../src/ui/molecules/pagination/pagination-links.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAS7E,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAClC;;;;OAIG;IACH,CAAC,EAAE,SAAS,CAAC;IACb,wEAAwE;IACxE,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,EAC9B,WAAW,EACX,QAAQ,EACR,UAAU,EACV,OAAO,EACP,CAAC,EACD,UAAyB,EACzB,MAAM,EACN,SAAS,GACV,EAAE,oBAAoB,+BA2EtB"}
@@ -0,0 +1,94 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { renderAnchor } from '../../actions/render-link.js';
3
+ import { button } from '../../atoms/button/button-recipe.js';
4
+ import { Text } from '../../atoms/text/index.js';
5
+ import { cn } from '../../utils/cn.js';
6
+ import { pageCount, pageWindow } from './page-math.js';
7
+ /** A glyph, not copy: it reads the same in every locale, and it is aria-hidden. */ const GAP = '…';
8
+ /**
9
+ * {@link Pagination} as links, and a **server** component.
10
+ *
11
+ * Every page is an address: a crawler follows it, a visitor middle-clicks it,
12
+ * and the page works with JavaScript off. `Pagination` steps through a
13
+ * callback and carries a `'use client'`, which a server-rendered listing
14
+ * cannot use without turning its pager into a client boundary for no gain.
15
+ *
16
+ * Previous and Next stay in the layout at the edges, as text marked
17
+ * `aria-disabled` rather than as links that go nowhere.
18
+ */ export function PaginationLinks({ currentPage, pageSize, totalItems, hrefFor, t, renderLink = renderAnchor, radius, className }) {
19
+ const totalPages = pageCount(totalItems, pageSize);
20
+ const step = button({
21
+ variant: 'secondary',
22
+ size: 'sm'
23
+ });
24
+ const number = button({
25
+ variant: 'ghost',
26
+ size: 'sm'
27
+ });
28
+ const unavailable = cn(step, 'pointer-events-none opacity-50');
29
+ const edge = (page, label, enabled)=>enabled ? renderLink({
30
+ href: hrefFor(page),
31
+ className: step,
32
+ children: label,
33
+ intent: {
34
+ kind: 'page',
35
+ page
36
+ }
37
+ }) : /*#__PURE__*/ _jsx("span", {
38
+ "aria-disabled": "true",
39
+ className: unavailable,
40
+ children: label
41
+ });
42
+ return /*#__PURE__*/ _jsxs("nav", {
43
+ "aria-label": t('pagination.label'),
44
+ className: cn('flex flex-wrap items-center justify-between gap-2xs', className),
45
+ children: [
46
+ /*#__PURE__*/ _jsx(Text, {
47
+ step: -1,
48
+ tone: "muted",
49
+ children: t('pagination.summary', {
50
+ current: currentPage,
51
+ total: totalPages,
52
+ items: totalItems
53
+ })
54
+ }),
55
+ /*#__PURE__*/ _jsxs("ol", {
56
+ className: "flex flex-wrap items-center gap-3xs",
57
+ children: [
58
+ /*#__PURE__*/ _jsx("li", {
59
+ children: edge(currentPage - 1, t('pagination.previous'), currentPage > 1)
60
+ }),
61
+ pageWindow(currentPage, totalPages, radius).map((entry)=>entry.kind === 'gap' ? /*#__PURE__*/ _jsx("li", {
62
+ "aria-hidden": "true",
63
+ children: /*#__PURE__*/ _jsx(Text, {
64
+ tone: "muted",
65
+ children: GAP
66
+ })
67
+ }, `gap-${entry.after}`) : /*#__PURE__*/ _jsx("li", {
68
+ children: entry.page === currentPage ? /*#__PURE__*/ _jsx("span", {
69
+ "aria-current": "page",
70
+ className: button({
71
+ variant: 'primary',
72
+ size: 'sm'
73
+ }),
74
+ children: entry.page
75
+ }) : renderLink({
76
+ href: hrefFor(entry.page),
77
+ className: number,
78
+ children: String(entry.page),
79
+ intent: {
80
+ kind: 'page',
81
+ page: entry.page
82
+ }
83
+ })
84
+ }, entry.page)),
85
+ /*#__PURE__*/ _jsx("li", {
86
+ children: edge(currentPage + 1, t('pagination.next'), currentPage < totalPages)
87
+ })
88
+ ]
89
+ })
90
+ ]
91
+ });
92
+ }
93
+
94
+ //# sourceMappingURL=pagination-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/ui/molecules/pagination/pagination-links.tsx"],"sourcesContent":["import type { Translate } from '../../../i18n/index.js';\nimport { renderAnchor, type RenderLink } from '../../actions/render-link.js';\nimport { button } from '../../atoms/button/button-recipe.js';\nimport { Text } from '../../atoms/text/index.js';\nimport { cn } from '../../utils/cn.js';\nimport { pageCount, pageWindow } from './page-math.js';\n\n/** A glyph, not copy: it reads the same in every locale, and it is aria-hidden. */\nconst GAP = '…';\n\nexport interface PaginationLinksProps {\n currentPage: number;\n pageSize: number;\n totalItems: number;\n /** The URL of a page. Page 1 is the host's to spell, usually without `?page`. */\n hrefFor: (page: number) => string;\n /**\n * Resolves this control's keys (`pagination.*`). A server component has no\n * context to read a translator from, so the host hands one over: in a Next\n * host, the server translator for the controls' namespace.\n */\n t: Translate;\n /** Defaults to a plain anchor; a host passes its own link component. */\n renderLink?: RenderLink;\n /** How many page numbers either side of the current one. Defaults to 1. */\n radius?: number;\n className?: string;\n}\n\n/**\n * {@link Pagination} as links, and a **server** component.\n *\n * Every page is an address: a crawler follows it, a visitor middle-clicks it,\n * and the page works with JavaScript off. `Pagination` steps through a\n * callback and carries a `'use client'`, which a server-rendered listing\n * cannot use without turning its pager into a client boundary for no gain.\n *\n * Previous and Next stay in the layout at the edges, as text marked\n * `aria-disabled` rather than as links that go nowhere.\n */\nexport function PaginationLinks({\n currentPage,\n pageSize,\n totalItems,\n hrefFor,\n t,\n renderLink = renderAnchor,\n radius,\n className,\n}: PaginationLinksProps) {\n const totalPages = pageCount(totalItems, pageSize);\n const step = button({ variant: 'secondary', size: 'sm' });\n const number = button({ variant: 'ghost', size: 'sm' });\n const unavailable = cn(step, 'pointer-events-none opacity-50');\n\n const edge = (page: number, label: string, enabled: boolean) =>\n enabled ? (\n renderLink({\n href: hrefFor(page),\n className: step,\n children: label,\n intent: { kind: 'page', page },\n })\n ) : (\n <span aria-disabled=\"true\" className={unavailable}>\n {label}\n </span>\n );\n\n return (\n <nav\n aria-label={t('pagination.label')}\n className={cn(\n 'flex flex-wrap items-center justify-between gap-2xs',\n className,\n )}\n >\n <Text step={-1} tone=\"muted\">\n {t('pagination.summary', {\n current: currentPage,\n total: totalPages,\n items: totalItems,\n })}\n </Text>\n\n <ol className=\"flex flex-wrap items-center gap-3xs\">\n <li>\n {edge(currentPage - 1, t('pagination.previous'), currentPage > 1)}\n </li>\n {pageWindow(currentPage, totalPages, radius).map(entry =>\n entry.kind === 'gap' ? (\n <li key={`gap-${entry.after}`} aria-hidden=\"true\">\n <Text tone=\"muted\">{GAP}</Text>\n </li>\n ) : (\n <li key={entry.page}>\n {entry.page === currentPage ? (\n <span\n aria-current=\"page\"\n className={button({ variant: 'primary', size: 'sm' })}\n >\n {entry.page}\n </span>\n ) : (\n renderLink({\n href: hrefFor(entry.page),\n className: number,\n children: String(entry.page),\n intent: { kind: 'page', page: entry.page },\n })\n )}\n </li>\n ),\n )}\n <li>\n {edge(\n currentPage + 1,\n t('pagination.next'),\n currentPage < totalPages,\n )}\n </li>\n </ol>\n </nav>\n );\n}\n"],"names":["renderAnchor","button","Text","cn","pageCount","pageWindow","GAP","PaginationLinks","currentPage","pageSize","totalItems","hrefFor","t","renderLink","radius","className","totalPages","step","variant","size","number","unavailable","edge","page","label","enabled","href","children","intent","kind","span","aria-disabled","nav","aria-label","tone","current","total","items","ol","li","map","entry","aria-hidden","after","aria-current","String"],"mappings":";AACA,SAASA,YAAY,QAAyB,+BAA+B;AAC7E,SAASC,MAAM,QAAQ,sCAAsC;AAC7D,SAASC,IAAI,QAAQ,4BAA4B;AACjD,SAASC,EAAE,QAAQ,oBAAoB;AACvC,SAASC,SAAS,EAAEC,UAAU,QAAQ,iBAAiB;AAEvD,iFAAiF,GACjF,MAAMC,MAAM;AAqBZ;;;;;;;;;;CAUC,GACD,OAAO,SAASC,gBAAgB,EAC9BC,WAAW,EACXC,QAAQ,EACRC,UAAU,EACVC,OAAO,EACPC,CAAC,EACDC,aAAab,YAAY,EACzBc,MAAM,EACNC,SAAS,EACY;IACrB,MAAMC,aAAaZ,UAAUM,YAAYD;IACzC,MAAMQ,OAAOhB,OAAO;QAAEiB,SAAS;QAAaC,MAAM;IAAK;IACvD,MAAMC,SAASnB,OAAO;QAAEiB,SAAS;QAASC,MAAM;IAAK;IACrD,MAAME,cAAclB,GAAGc,MAAM;IAE7B,MAAMK,OAAO,CAACC,MAAcC,OAAeC,UACzCA,UACEZ,WAAW;YACTa,MAAMf,QAAQY;YACdR,WAAWE;YACXU,UAAUH;YACVI,QAAQ;gBAAEC,MAAM;gBAAQN;YAAK;QAC/B,mBAEA,KAACO;YAAKC,iBAAc;YAAOhB,WAAWM;sBACnCG;;IAIP,qBACE,MAACQ;QACCC,cAAYrB,EAAE;QACdG,WAAWZ,GACT,uDACAY;;0BAGF,KAACb;gBAAKe,MAAM,CAAC;gBAAGiB,MAAK;0BAClBtB,EAAE,sBAAsB;oBACvBuB,SAAS3B;oBACT4B,OAAOpB;oBACPqB,OAAO3B;gBACT;;0BAGF,MAAC4B;gBAAGvB,WAAU;;kCACZ,KAACwB;kCACEjB,KAAKd,cAAc,GAAGI,EAAE,wBAAwBJ,cAAc;;oBAEhEH,WAAWG,aAAaQ,YAAYF,QAAQ0B,GAAG,CAACC,CAAAA,QAC/CA,MAAMZ,IAAI,KAAK,sBACb,KAACU;4BAA8BG,eAAY;sCACzC,cAAA,KAACxC;gCAAKgC,MAAK;0CAAS5B;;2BADb,CAAC,IAAI,EAAEmC,MAAME,KAAK,EAAE,kBAI7B,KAACJ;sCACEE,MAAMlB,IAAI,KAAKf,4BACd,KAACsB;gCACCc,gBAAa;gCACb7B,WAAWd,OAAO;oCAAEiB,SAAS;oCAAWC,MAAM;gCAAK;0CAElDsB,MAAMlB,IAAI;iCAGbV,WAAW;gCACTa,MAAMf,QAAQ8B,MAAMlB,IAAI;gCACxBR,WAAWK;gCACXO,UAAUkB,OAAOJ,MAAMlB,IAAI;gCAC3BK,QAAQ;oCAAEC,MAAM;oCAAQN,MAAMkB,MAAMlB,IAAI;gCAAC;4BAC3C;2BAdKkB,MAAMlB,IAAI;kCAmBvB,KAACgB;kCACEjB,KACCd,cAAc,GACdI,EAAE,oBACFJ,cAAcQ;;;;;;AAM1B"}
@@ -8,6 +8,11 @@ export interface PaginationProps {
8
8
  pageSizeOptions?: readonly number[];
9
9
  className?: string;
10
10
  }
11
- /** Page stepper plus an optional page-size selector. */
11
+ /**
12
+ * Page stepper plus an optional page-size selector, driven by callbacks.
13
+ *
14
+ * For a listing whose pages are addresses (server-rendered, crawlable, working
15
+ * without JavaScript) use {@link PaginationLinks} instead.
16
+ */
12
17
  export declare function Pagination({ currentPage, pageSize, totalItems, onPageChange, onPageSizeChange, pageSizeOptions, className, }: PaginationProps): import("react").JSX.Element;
13
18
  //# sourceMappingURL=pagination.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../../../src/ui/molecules/pagination/pagination.tsx"],"names":[],"mappings":"AAQA,eAAO,MAAM,yBAAyB,4BAA6B,CAAC;AAEpE,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,EACzB,WAAW,EACX,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,eAA2C,EAC3C,SAAS,GACV,EAAE,eAAe,+BAwDjB"}
1
+ {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../../../src/ui/molecules/pagination/pagination.tsx"],"names":[],"mappings":"AASA,eAAO,MAAM,yBAAyB,4BAA6B,CAAC;AAEpE,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,WAAW,EACX,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,eAA2C,EAC3C,SAAS,GACV,EAAE,eAAe,+BAwDjB"}
@@ -5,15 +5,21 @@ import { Button } from '../../atoms/button/index.js';
5
5
  import { Select } from '../../atoms/field/index.js';
6
6
  import { Text } from '../../atoms/text/index.js';
7
7
  import { cn } from '../../utils/cn.js';
8
+ import { pageCount } from './page-math.js';
8
9
  export const DEFAULT_PAGE_SIZE_OPTIONS = [
9
10
  10,
10
11
  25,
11
12
  50,
12
13
  100
13
14
  ];
14
- /** Page stepper plus an optional page-size selector. */ export function Pagination({ currentPage, pageSize, totalItems, onPageChange, onPageSizeChange, pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS, className }) {
15
+ /**
16
+ * Page stepper plus an optional page-size selector, driven by callbacks.
17
+ *
18
+ * For a listing whose pages are addresses (server-rendered, crawlable, working
19
+ * without JavaScript) use {@link PaginationLinks} instead.
20
+ */ export function Pagination({ currentPage, pageSize, totalItems, onPageChange, onPageSizeChange, pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS, className }) {
15
21
  const t = useT();
16
- const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
22
+ const totalPages = pageCount(totalItems, pageSize);
17
23
  return /*#__PURE__*/ _jsxs("div", {
18
24
  className: cn('flex flex-wrap items-center justify-between gap-2xs', className),
19
25
  children: [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/ui/molecules/pagination/pagination.tsx"],"sourcesContent":["'use client';\n\nimport { useT } from '../../../i18n/index.js';\nimport { Button } from '../../atoms/button/index.js';\nimport { Select } from '../../atoms/field/index.js';\nimport { Text } from '../../atoms/text/index.js';\nimport { cn } from '../../utils/cn.js';\n\nexport const DEFAULT_PAGE_SIZE_OPTIONS = [10, 25, 50, 100] as const;\n\nexport interface PaginationProps {\n currentPage: number;\n pageSize: number;\n totalItems: number;\n onPageChange: (page: number) => void;\n onPageSizeChange?: (pageSize: number) => void;\n pageSizeOptions?: readonly number[];\n className?: string;\n}\n\n/** Page stepper plus an optional page-size selector. */\nexport function Pagination({\n currentPage,\n pageSize,\n totalItems,\n onPageChange,\n onPageSizeChange,\n pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,\n className,\n}: PaginationProps) {\n const t = useT();\n const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));\n\n return (\n <div\n className={cn(\n 'flex flex-wrap items-center justify-between gap-2xs',\n className,\n )}\n >\n <Text step={-1} tone=\"muted\">\n {t('pagination.summary', {\n current: currentPage,\n total: totalPages,\n items: totalItems,\n })}\n </Text>\n\n <div className=\"flex items-center gap-2xs\">\n {onPageSizeChange && (\n <label className=\"flex items-center gap-3xs text-step-sm text-content-muted\">\n {t('pagination.rows')}\n <Select\n value={pageSize}\n onChange={event => onPageSizeChange(Number(event.target.value))}\n >\n {pageSizeOptions.map(option => (\n <option key={option} value={option}>\n {option}\n </option>\n ))}\n </Select>\n </label>\n )}\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={currentPage <= 1}\n onClick={() => onPageChange(currentPage - 1)}\n >\n {t('pagination.previous')}\n </Button>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={currentPage >= totalPages}\n onClick={() => onPageChange(currentPage + 1)}\n >\n {t('pagination.next')}\n </Button>\n </div>\n </div>\n );\n}\n"],"names":["useT","Button","Select","Text","cn","DEFAULT_PAGE_SIZE_OPTIONS","Pagination","currentPage","pageSize","totalItems","onPageChange","onPageSizeChange","pageSizeOptions","className","t","totalPages","Math","max","ceil","div","step","tone","current","total","items","label","value","onChange","event","Number","target","map","option","type","variant","size","disabled","onClick"],"mappings":"AAAA;;AAEA,SAASA,IAAI,QAAQ,yBAAyB;AAC9C,SAASC,MAAM,QAAQ,8BAA8B;AACrD,SAASC,MAAM,QAAQ,6BAA6B;AACpD,SAASC,IAAI,QAAQ,4BAA4B;AACjD,SAASC,EAAE,QAAQ,oBAAoB;AAEvC,OAAO,MAAMC,4BAA4B;IAAC;IAAI;IAAI;IAAI;CAAI,CAAU;AAYpE,sDAAsD,GACtD,OAAO,SAASC,WAAW,EACzBC,WAAW,EACXC,QAAQ,EACRC,UAAU,EACVC,YAAY,EACZC,gBAAgB,EAChBC,kBAAkBP,yBAAyB,EAC3CQ,SAAS,EACO;IAChB,MAAMC,IAAId;IACV,MAAMe,aAAaC,KAAKC,GAAG,CAAC,GAAGD,KAAKE,IAAI,CAACT,aAAaD;IAEtD,qBACE,MAACW;QACCN,WAAWT,GACT,uDACAS;;0BAGF,KAACV;gBAAKiB,MAAM,CAAC;gBAAGC,MAAK;0BAClBP,EAAE,sBAAsB;oBACvBQ,SAASf;oBACTgB,OAAOR;oBACPS,OAAOf;gBACT;;0BAGF,MAACU;gBAAIN,WAAU;;oBACZF,kCACC,MAACc;wBAAMZ,WAAU;;4BACdC,EAAE;0CACH,KAACZ;gCACCwB,OAAOlB;gCACPmB,UAAUC,CAAAA,QAASjB,iBAAiBkB,OAAOD,MAAME,MAAM,CAACJ,KAAK;0CAE5Dd,gBAAgBmB,GAAG,CAACC,CAAAA,uBACnB,KAACA;wCAAoBN,OAAOM;kDACzBA;uCADUA;;;;kCAOrB,KAAC/B;wBACCgC,MAAK;wBACLC,SAAQ;wBACRC,MAAK;wBACLC,UAAU7B,eAAe;wBACzB8B,SAAS,IAAM3B,aAAaH,cAAc;kCAEzCO,EAAE;;kCAEL,KAACb;wBACCgC,MAAK;wBACLC,SAAQ;wBACRC,MAAK;wBACLC,UAAU7B,eAAeQ;wBACzBsB,SAAS,IAAM3B,aAAaH,cAAc;kCAEzCO,EAAE;;;;;;AAKb"}
1
+ {"version":3,"sources":["../../../../src/ui/molecules/pagination/pagination.tsx"],"sourcesContent":["'use client';\n\nimport { useT } from '../../../i18n/index.js';\nimport { Button } from '../../atoms/button/index.js';\nimport { Select } from '../../atoms/field/index.js';\nimport { Text } from '../../atoms/text/index.js';\nimport { cn } from '../../utils/cn.js';\nimport { pageCount } from './page-math.js';\n\nexport const DEFAULT_PAGE_SIZE_OPTIONS = [10, 25, 50, 100] as const;\n\nexport interface PaginationProps {\n currentPage: number;\n pageSize: number;\n totalItems: number;\n onPageChange: (page: number) => void;\n onPageSizeChange?: (pageSize: number) => void;\n pageSizeOptions?: readonly number[];\n className?: string;\n}\n\n/**\n * Page stepper plus an optional page-size selector, driven by callbacks.\n *\n * For a listing whose pages are addresses (server-rendered, crawlable, working\n * without JavaScript) use {@link PaginationLinks} instead.\n */\nexport function Pagination({\n currentPage,\n pageSize,\n totalItems,\n onPageChange,\n onPageSizeChange,\n pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,\n className,\n}: PaginationProps) {\n const t = useT();\n const totalPages = pageCount(totalItems, pageSize);\n\n return (\n <div\n className={cn(\n 'flex flex-wrap items-center justify-between gap-2xs',\n className,\n )}\n >\n <Text step={-1} tone=\"muted\">\n {t('pagination.summary', {\n current: currentPage,\n total: totalPages,\n items: totalItems,\n })}\n </Text>\n\n <div className=\"flex items-center gap-2xs\">\n {onPageSizeChange && (\n <label className=\"flex items-center gap-3xs text-step-sm text-content-muted\">\n {t('pagination.rows')}\n <Select\n value={pageSize}\n onChange={event => onPageSizeChange(Number(event.target.value))}\n >\n {pageSizeOptions.map(option => (\n <option key={option} value={option}>\n {option}\n </option>\n ))}\n </Select>\n </label>\n )}\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={currentPage <= 1}\n onClick={() => onPageChange(currentPage - 1)}\n >\n {t('pagination.previous')}\n </Button>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={currentPage >= totalPages}\n onClick={() => onPageChange(currentPage + 1)}\n >\n {t('pagination.next')}\n </Button>\n </div>\n </div>\n );\n}\n"],"names":["useT","Button","Select","Text","cn","pageCount","DEFAULT_PAGE_SIZE_OPTIONS","Pagination","currentPage","pageSize","totalItems","onPageChange","onPageSizeChange","pageSizeOptions","className","t","totalPages","div","step","tone","current","total","items","label","value","onChange","event","Number","target","map","option","type","variant","size","disabled","onClick"],"mappings":"AAAA;;AAEA,SAASA,IAAI,QAAQ,yBAAyB;AAC9C,SAASC,MAAM,QAAQ,8BAA8B;AACrD,SAASC,MAAM,QAAQ,6BAA6B;AACpD,SAASC,IAAI,QAAQ,4BAA4B;AACjD,SAASC,EAAE,QAAQ,oBAAoB;AACvC,SAASC,SAAS,QAAQ,iBAAiB;AAE3C,OAAO,MAAMC,4BAA4B;IAAC;IAAI;IAAI;IAAI;CAAI,CAAU;AAYpE;;;;;CAKC,GACD,OAAO,SAASC,WAAW,EACzBC,WAAW,EACXC,QAAQ,EACRC,UAAU,EACVC,YAAY,EACZC,gBAAgB,EAChBC,kBAAkBP,yBAAyB,EAC3CQ,SAAS,EACO;IAChB,MAAMC,IAAIf;IACV,MAAMgB,aAAaX,UAAUK,YAAYD;IAEzC,qBACE,MAACQ;QACCH,WAAWV,GACT,uDACAU;;0BAGF,KAACX;gBAAKe,MAAM,CAAC;gBAAGC,MAAK;0BAClBJ,EAAE,sBAAsB;oBACvBK,SAASZ;oBACTa,OAAOL;oBACPM,OAAOZ;gBACT;;0BAGF,MAACO;gBAAIH,WAAU;;oBACZF,kCACC,MAACW;wBAAMT,WAAU;;4BACdC,EAAE;0CACH,KAACb;gCACCsB,OAAOf;gCACPgB,UAAUC,CAAAA,QAASd,iBAAiBe,OAAOD,MAAME,MAAM,CAACJ,KAAK;0CAE5DX,gBAAgBgB,GAAG,CAACC,CAAAA,uBACnB,KAACA;wCAAoBN,OAAOM;kDACzBA;uCADUA;;;;kCAOrB,KAAC7B;wBACC8B,MAAK;wBACLC,SAAQ;wBACRC,MAAK;wBACLC,UAAU1B,eAAe;wBACzB2B,SAAS,IAAMxB,aAAaH,cAAc;kCAEzCO,EAAE;;kCAEL,KAACd;wBACC8B,MAAK;wBACLC,SAAQ;wBACRC,MAAK;wBACLC,UAAU1B,eAAeQ;wBACzBmB,SAAS,IAAMxB,aAAaH,cAAc;kCAEzCO,EAAE;;;;;;AAKb"}
@@ -0,0 +1,33 @@
1
+ import { type Entity, type HierarchyKeys, type TreeRow } from '@entifix/core';
2
+ import type { ReactNode } from 'react';
3
+ /**
4
+ * The rows a table shows, in the order it shows them.
5
+ *
6
+ * For an entity that is not a tree, the page as it came. For one that is, the
7
+ * page arranged depth first, siblings in the order the load sorted them. A
8
+ * record whose parent is on another page is a root of this page, and records
9
+ * on a cycle come last at depth 0, so no row the load returned goes missing.
10
+ *
11
+ * ⚠️ **The tree is the page's, not the store's.** A table is paged, so a
12
+ * parent and its children only nest when they arrive together. A host that
13
+ * wants the whole tree loads it whole: a vocabulary of a few hundred records
14
+ * fits in one page.
15
+ */
16
+ export declare function treeRows<TEntity extends Entity>(items: readonly TEntity[], keys: HierarchyKeys<TEntity> | undefined): TreeRow<TEntity>[];
17
+ /** The rows left once every collapsed record's subtree is hidden. */
18
+ export declare function shownTreeRows<TEntity extends Entity>(rows: readonly TreeRow<TEntity>[], collapsed: ReadonlySet<string>): TreeRow<TEntity>[];
19
+ /**
20
+ * The first cell of a tree row: indented by depth, with a toggle when the
21
+ * record has children and a spacer the same width when it does not, so every
22
+ * label at one depth starts at the same x.
23
+ */
24
+ export declare function TreeCell({ depth, hasChildren, expanded, label, onToggle, children, }: {
25
+ depth: number;
26
+ hasChildren: boolean;
27
+ expanded: boolean;
28
+ /** Names the toggle: "Collapse Lighting", not "Collapse". */
29
+ label: string;
30
+ onToggle: () => void;
31
+ children: ReactNode;
32
+ }): import("react").JSX.Element;
33
+ //# sourceMappingURL=entity-table-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-table-tree.d.ts","sourceRoot":"","sources":["../../../../src/ui/organisms/entity-table/entity-table-tree.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,MAAM,EAEX,KAAK,aAAa,EAClB,KAAK,OAAO,EACb,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,OAAO,SAAS,MAAM,EAC7C,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,SAAS,GACvC,OAAO,CAAC,OAAO,CAAC,EAAE,CAmBpB;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,OAAO,SAAS,MAAM,EAClD,IAAI,EAAE,SAAS,OAAO,CAAC,OAAO,CAAC,EAAE,EACjC,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,GAC7B,OAAO,CAAC,OAAO,CAAC,EAAE,CAGpB;AAeD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,WAAW,EACX,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB,+BAsBA"}
@@ -0,0 +1,80 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { buildTree, flattenTree } from '@entifix/core';
3
+ /**
4
+ * The rows a table shows, in the order it shows them.
5
+ *
6
+ * For an entity that is not a tree, the page as it came. For one that is, the
7
+ * page arranged depth first, siblings in the order the load sorted them. A
8
+ * record whose parent is on another page is a root of this page, and records
9
+ * on a cycle come last at depth 0, so no row the load returned goes missing.
10
+ *
11
+ * ⚠️ **The tree is the page's, not the store's.** A table is paged, so a
12
+ * parent and its children only nest when they arrive together. A host that
13
+ * wants the whole tree loads it whole: a vocabulary of a few hundred records
14
+ * fits in one page.
15
+ */ export function treeRows(items, keys) {
16
+ if (keys === undefined) {
17
+ return items.map((item)=>({
18
+ item,
19
+ depth: 0,
20
+ hasChildren: false,
21
+ path: []
22
+ }));
23
+ }
24
+ const tree = buildTree(items, keys);
25
+ return [
26
+ ...flattenTree(tree, keys),
27
+ ...tree.detached.map((item)=>({
28
+ item,
29
+ depth: 0,
30
+ hasChildren: false,
31
+ path: []
32
+ }))
33
+ ];
34
+ }
35
+ /** The rows left once every collapsed record's subtree is hidden. */ export function shownTreeRows(rows, collapsed) {
36
+ if (collapsed.size === 0) return [
37
+ ...rows
38
+ ];
39
+ return rows.filter((row)=>!row.path.some((key)=>collapsed.has(key)));
40
+ }
41
+ /** Chevron — points right when collapsed, down when expanded. */ function TreeChevron({ expanded }) {
42
+ return /*#__PURE__*/ _jsx("svg", {
43
+ "aria-hidden": true,
44
+ viewBox: "0 0 20 20",
45
+ className: `size-4 fill-current transition-transform duration-200 ${expanded ? 'rotate-90' : ''}`,
46
+ children: /*#__PURE__*/ _jsx("path", {
47
+ d: "M7.5 5.5 12 10l-4.5 4.5z"
48
+ })
49
+ });
50
+ }
51
+ /**
52
+ * The first cell of a tree row: indented by depth, with a toggle when the
53
+ * record has children and a spacer the same width when it does not, so every
54
+ * label at one depth starts at the same x.
55
+ */ export function TreeCell({ depth, hasChildren, expanded, label, onToggle, children }) {
56
+ return /*#__PURE__*/ _jsxs("div", {
57
+ className: "flex items-center gap-3xs",
58
+ style: {
59
+ paddingInlineStart: `calc(${depth} * 1.25rem)`
60
+ },
61
+ children: [
62
+ hasChildren ? /*#__PURE__*/ _jsx("button", {
63
+ type: "button",
64
+ "aria-expanded": expanded,
65
+ "aria-label": label,
66
+ onClick: onToggle,
67
+ className: "size-5 inline-flex items-center justify-center rounded-sm text-content-muted focus-ring hover:text-content",
68
+ children: /*#__PURE__*/ _jsx(TreeChevron, {
69
+ expanded: expanded
70
+ })
71
+ }) : /*#__PURE__*/ _jsx("span", {
72
+ "aria-hidden": true,
73
+ className: "size-5 inline-block"
74
+ }),
75
+ children
76
+ ]
77
+ });
78
+ }
79
+
80
+ //# sourceMappingURL=entity-table-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/ui/organisms/entity-table/entity-table-tree.tsx"],"sourcesContent":["import {\n buildTree,\n type Entity,\n flattenTree,\n type HierarchyKeys,\n type TreeRow,\n} from '@entifix/core';\nimport type { ReactNode } from 'react';\n\n/**\n * The rows a table shows, in the order it shows them.\n *\n * For an entity that is not a tree, the page as it came. For one that is, the\n * page arranged depth first, siblings in the order the load sorted them. A\n * record whose parent is on another page is a root of this page, and records\n * on a cycle come last at depth 0, so no row the load returned goes missing.\n *\n * ⚠️ **The tree is the page's, not the store's.** A table is paged, so a\n * parent and its children only nest when they arrive together. A host that\n * wants the whole tree loads it whole: a vocabulary of a few hundred records\n * fits in one page.\n */\nexport function treeRows<TEntity extends Entity>(\n items: readonly TEntity[],\n keys: HierarchyKeys<TEntity> | undefined,\n): TreeRow<TEntity>[] {\n if (keys === undefined) {\n return items.map(item => ({\n item,\n depth: 0,\n hasChildren: false,\n path: [],\n }));\n }\n const tree = buildTree(items, keys);\n return [\n ...flattenTree(tree, keys),\n ...tree.detached.map(item => ({\n item,\n depth: 0,\n hasChildren: false,\n path: [],\n })),\n ];\n}\n\n/** The rows left once every collapsed record's subtree is hidden. */\nexport function shownTreeRows<TEntity extends Entity>(\n rows: readonly TreeRow<TEntity>[],\n collapsed: ReadonlySet<string>,\n): TreeRow<TEntity>[] {\n if (collapsed.size === 0) return [...rows];\n return rows.filter(row => !row.path.some(key => collapsed.has(key)));\n}\n\n/** Chevron — points right when collapsed, down when expanded. */\nfunction TreeChevron({ expanded }: { expanded: boolean }) {\n return (\n <svg\n aria-hidden\n viewBox=\"0 0 20 20\"\n className={`size-4 fill-current transition-transform duration-200 ${expanded ? 'rotate-90' : ''}`}\n >\n <path d=\"M7.5 5.5 12 10l-4.5 4.5z\" />\n </svg>\n );\n}\n\n/**\n * The first cell of a tree row: indented by depth, with a toggle when the\n * record has children and a spacer the same width when it does not, so every\n * label at one depth starts at the same x.\n */\nexport function TreeCell({\n depth,\n hasChildren,\n expanded,\n label,\n onToggle,\n children,\n}: {\n depth: number;\n hasChildren: boolean;\n expanded: boolean;\n /** Names the toggle: \"Collapse Lighting\", not \"Collapse\". */\n label: string;\n onToggle: () => void;\n children: ReactNode;\n}) {\n return (\n <div\n className=\"flex items-center gap-3xs\"\n style={{ paddingInlineStart: `calc(${depth} * 1.25rem)` }}\n >\n {hasChildren ? (\n <button\n type=\"button\"\n aria-expanded={expanded}\n aria-label={label}\n onClick={onToggle}\n className=\"size-5 inline-flex items-center justify-center rounded-sm text-content-muted focus-ring hover:text-content\"\n >\n <TreeChevron expanded={expanded} />\n </button>\n ) : (\n <span aria-hidden className=\"size-5 inline-block\" />\n )}\n {children}\n </div>\n );\n}\n"],"names":["buildTree","flattenTree","treeRows","items","keys","undefined","map","item","depth","hasChildren","path","tree","detached","shownTreeRows","rows","collapsed","size","filter","row","some","key","has","TreeChevron","expanded","svg","aria-hidden","viewBox","className","d","TreeCell","label","onToggle","children","div","style","paddingInlineStart","button","type","aria-expanded","aria-label","onClick","span"],"mappings":";AAAA,SACEA,SAAS,EAETC,WAAW,QAGN,gBAAgB;AAGvB;;;;;;;;;;;;CAYC,GACD,OAAO,SAASC,SACdC,KAAyB,EACzBC,IAAwC;IAExC,IAAIA,SAASC,WAAW;QACtB,OAAOF,MAAMG,GAAG,CAACC,CAAAA,OAAS,CAAA;gBACxBA;gBACAC,OAAO;gBACPC,aAAa;gBACbC,MAAM,EAAE;YACV,CAAA;IACF;IACA,MAAMC,OAAOX,UAAUG,OAAOC;IAC9B,OAAO;WACFH,YAAYU,MAAMP;WAClBO,KAAKC,QAAQ,CAACN,GAAG,CAACC,CAAAA,OAAS,CAAA;gBAC5BA;gBACAC,OAAO;gBACPC,aAAa;gBACbC,MAAM,EAAE;YACV,CAAA;KACD;AACH;AAEA,mEAAmE,GACnE,OAAO,SAASG,cACdC,IAAiC,EACjCC,SAA8B;IAE9B,IAAIA,UAAUC,IAAI,KAAK,GAAG,OAAO;WAAIF;KAAK;IAC1C,OAAOA,KAAKG,MAAM,CAACC,CAAAA,MAAO,CAACA,IAAIR,IAAI,CAACS,IAAI,CAACC,CAAAA,MAAOL,UAAUM,GAAG,CAACD;AAChE;AAEA,+DAA+D,GAC/D,SAASE,YAAY,EAAEC,QAAQ,EAAyB;IACtD,qBACE,KAACC;QACCC,aAAW;QACXC,SAAQ;QACRC,WAAW,CAAC,sDAAsD,EAAEJ,WAAW,cAAc,IAAI;kBAEjG,cAAA,KAACb;YAAKkB,GAAE;;;AAGd;AAEA;;;;CAIC,GACD,OAAO,SAASC,SAAS,EACvBrB,KAAK,EACLC,WAAW,EACXc,QAAQ,EACRO,KAAK,EACLC,QAAQ,EACRC,QAAQ,EAST;IACC,qBACE,MAACC;QACCN,WAAU;QACVO,OAAO;YAAEC,oBAAoB,CAAC,KAAK,EAAE3B,MAAM,WAAW,CAAC;QAAC;;YAEvDC,4BACC,KAAC2B;gBACCC,MAAK;gBACLC,iBAAef;gBACfgB,cAAYT;gBACZU,SAAST;gBACTJ,WAAU;0BAEV,cAAA,KAACL;oBAAYC,UAAUA;;+BAGzB,KAACkB;gBAAKhB,aAAW;gBAACE,WAAU;;YAE7BK;;;AAGP"}
@@ -1 +1 @@
1
- {"version":3,"file":"entity-table.d.ts","sourceRoot":"","sources":["../../../../src/ui/organisms/entity-table/entity-table.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,MAAM,EAUZ,MAAM,eAAe,CAAC;AAkCvB,OAAO,KAAK,EAGV,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AAwDjC,wBAAgB,WAAW,CAAC,OAAO,SAAS,MAAM,EAAE,EAClD,iBAAiB,EACjB,SAAS,EACT,QAAe,EACf,KAAK,EACL,KAAK,EACL,UAAU,EACV,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,gBAAgB,EAChB,OAAO,EACP,OAAO,EACP,UAAyB,EACzB,QAAQ,EACR,cAAc,EACd,YAAmB,EACnB,eAAsB,EACtB,SAAS,EACT,OAAO,EACP,iBAAiB,EACjB,eAAe,EACf,SAAS,EACT,iBAAiB,EACjB,QAAQ,EACR,iBAAyB,EACzB,aAAa,EACb,SAAS,EACT,YAAY,EACZ,aAAa,EACb,WAAW,EACX,aAAqB,EACrB,QAAQ,GACT,EAAE,gBAAgB,CAAC,OAAO,CAAC,+BA+pB3B"}
1
+ {"version":3,"file":"entity-table.d.ts","sourceRoot":"","sources":["../../../../src/ui/organisms/entity-table/entity-table.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,MAAM,EAWZ,MAAM,eAAe,CAAC;AAkCvB,OAAO,KAAK,EAGV,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AAyDjC,wBAAgB,WAAW,CAAC,OAAO,SAAS,MAAM,EAAE,EAClD,iBAAiB,EACjB,SAAS,EACT,QAAe,EACf,KAAK,EACL,KAAK,EACL,UAAU,EACV,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,gBAAgB,EAChB,OAAO,EACP,OAAO,EACP,UAAyB,EACzB,QAAQ,EACR,cAAc,EACd,YAAmB,EACnB,eAAsB,EACtB,SAAS,EACT,OAAO,EACP,iBAAiB,EACjB,eAAe,EACf,SAAS,EACT,iBAAiB,EACjB,QAAQ,EACR,iBAAyB,EACzB,aAAa,EACb,SAAS,EACT,YAAY,EACZ,aAAa,EACb,WAAW,EACX,aAAqB,EACrB,QAAQ,GACT,EAAE,gBAAgB,CAAC,OAAO,CAAC,+BAysB3B"}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { emptySelection, EntifixLogicError, extractMetaEntity, isSelected, selectionSize, toggleSelected } from '@entifix/core';
4
- import { Fragment, useRef, useState } from 'react';
3
+ import { emptySelection, EntifixLogicError, extractMetaEntity, hierarchyOf, isSelected, selectionSize, toggleSelected } from '@entifix/core';
4
+ import { Fragment, useMemo, useRef, useState } from 'react';
5
5
  import { useErrorMessage, useT, useTranslateKey } from '../../../i18n/index.js';
6
6
  import { OVERFLOW_GLYPH, renderAnchor, useCasesForSurface } from '../../actions/index.js';
7
7
  import { Button } from '../../atoms/button/index.js';
@@ -22,6 +22,7 @@ import { Pagination } from '../../molecules/pagination/index.js';
22
22
  import { SortBuilder } from '../../molecules/sort-builder/index.js';
23
23
  import { TableToolbar } from '../../molecules/table-toolbar/index.js';
24
24
  import { readEntityTableSlots } from './entity-table-slots.js';
25
+ import { shownTreeRows, TreeCell, treeRows } from './entity-table-tree.js';
25
26
  import { useEntityTableColumns } from './use-entity-table-columns.js';
26
27
  /**
27
28
  * The grid and the card list are both rendered, and CSS picks one. A JS
@@ -143,6 +144,27 @@ export function EntityTable({ entityConstructor, isLoading, skeleton = true, err
143
144
  * screen, and re-rendering the whole table on every click to remember an
144
145
  * anchor is a cost with no visible return.
145
146
  */ const anchorIndex = useRef(undefined);
147
+ /**
148
+ * An entity that declares a parent renders as a tree: the page arranged
149
+ * depth first, its first column indented, and a subtree hidden while its
150
+ * record is collapsed. Collapsing is view state, so it lives here and is
151
+ * forgotten with the table, like the shift-click anchor above.
152
+ */ const treeKeys = useMemo(()=>hierarchyOf(entityConstructor), [
153
+ entityConstructor
154
+ ]);
155
+ const [collapsed, setCollapsed] = useState(()=>new Set());
156
+ const rows = useMemo(()=>treeRows(items, treeKeys), [
157
+ items,
158
+ treeKeys
159
+ ]);
160
+ const shown = shownTreeRows(rows, collapsed);
161
+ const shownItems = shown.map((row)=>row.item);
162
+ const toggleCollapsed = (key)=>setCollapsed((current)=>{
163
+ const next = new Set(current);
164
+ if (next.has(key)) next.delete(key);
165
+ else next.add(key);
166
+ return next;
167
+ });
146
168
  const setSelection = (next)=>onSelectionChange == null ? void 0 : onSelectionChange(next);
147
169
  const toggleRow = (index, checked, shiftKey, current)=>{
148
170
  var _anchorIndex_current;
@@ -157,7 +179,8 @@ export function EntityTable({ entityConstructor, isLoading, skeleton = true, err
157
179
  from
158
180
  ];
159
181
  let next = current;
160
- for (const row of items.slice(start, end + 1)){
182
+ // The rows as shown: in a tree the order on screen is not the load's.
183
+ for (const row of shownItems.slice(start, end + 1)){
161
184
  next = toggleSelected(next, row.id, checked);
162
185
  }
163
186
  anchorIndex.current = index;
@@ -470,7 +493,7 @@ export function EntityTable({ entityConstructor, isLoading, skeleton = true, err
470
493
  colSpan: columnCount,
471
494
  children: emptyMessage
472
495
  }),
473
- items.map((item, index)=>{
496
+ shown.map(({ item, depth, hasChildren }, index)=>{
474
497
  var _item_id, _item_id1;
475
498
  return slots.row ? // A Fragment carries the key without wrapping the custom row in
476
499
  // an element `<tbody>` would reject.
@@ -481,8 +504,17 @@ export function EntityTable({ entityConstructor, isLoading, skeleton = true, err
481
504
  selectionState && /*#__PURE__*/ _jsx(TableCell, {
482
505
  children: selectionCell(item, index, selectionState)
483
506
  }),
484
- visibleColumns.map((column)=>/*#__PURE__*/ _jsx(TableCell, {
485
- children: renderCell(column, item)
507
+ visibleColumns.map((column, columnIndex)=>/*#__PURE__*/ _jsx(TableCell, {
508
+ children: treeKeys !== undefined && columnIndex === 0 ? /*#__PURE__*/ _jsx(TreeCell, {
509
+ depth: depth,
510
+ hasChildren: hasChildren,
511
+ expanded: !collapsed.has(treeKeys.idOf(item)),
512
+ label: t(collapsed.has(treeKeys.idOf(item)) ? 'table.expandRow' : 'table.collapseRow', {
513
+ record: rowLabel(item)
514
+ }),
515
+ onToggle: ()=>toggleCollapsed(treeKeys.idOf(item)),
516
+ children: renderCell(column, item)
517
+ }) : renderCell(column, item)
486
518
  }, column.name)),
487
519
  hasRowAction && /*#__PURE__*/ _jsx(TableCell, {
488
520
  children: recordAction(item)
@@ -510,7 +542,7 @@ export function EntityTable({ entityConstructor, isLoading, skeleton = true, err
510
542
  className: "text-step-sm text-content-muted",
511
543
  children: emptyMessage
512
544
  }),
513
- items.map((item, index)=>{
545
+ shown.map(({ item }, index)=>{
514
546
  var _item_id;
515
547
  return /*#__PURE__*/ _jsx(EntityRecordCard, {
516
548
  columns: visibleColumns,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/ui/organisms/entity-table/entity-table.tsx"],"sourcesContent":["'use client';\n\nimport {\n emptySelection,\n EntifixLogicError,\n type Entity,\n type EntityId,\n type EntitySelection,\n type EntitySorting,\n extractMetaEntity,\n type FilterGroup,\n isSelected,\n selectionSize,\n toggleSelected,\n type UseCaseDescriptor,\n} from '@entifix/core';\nimport { Fragment, type ReactNode, 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 { 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 EntifixLogicError(\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 />\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 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 for (const row of items.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 {items.map((item, 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 => (\n <TableCell key={column.name}>\n {renderCell(column, item)}\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 {items.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","EntifixLogicError","extractMetaEntity","isSelected","selectionSize","toggleSelected","Fragment","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","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","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","anchorIndex","setSelection","next","toggleRow","checked","shiftKey","current","start","end","row","slice","id","pageIds","map","selectedOnPage","filter","allOnPageSelected","someOnPageSelected","toggleAllOnPage","rowLabel","named","find","type","String","selectionCell","aria-label","record","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","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 EntifixLogicError,\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 EntifixLogicError(\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 />\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","EntifixLogicError","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","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","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","record","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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@entifix/react-controls",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Entity-agnostic React UI — table, form, filters, wizard, command palette — rendered from entity metadata, plus layout primitives.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -19,7 +19,7 @@
19
19
  "@headlessui/react": "^2.2.10",
20
20
  "class-variance-authority": "^0.7.1",
21
21
  "idb": "^8.0.4",
22
- "@entifix/core": "0.6.0"
22
+ "@entifix/core": "0.7.0"
23
23
  },
24
24
  "exports": {
25
25
  "./package.json": "./package.json",
@@ -53,8 +53,8 @@
53
53
  "react": "19.3.1",
54
54
  "react-i18next": "17.1.0",
55
55
  "storybook": "^10.6.2",
56
- "@entifix/i18n": "0.6.0",
57
- "@entifix/style": "0.6.0"
56
+ "@entifix/i18n": "0.7.0",
57
+ "@entifix/style": "0.7.0"
58
58
  },
59
59
  "nx": {
60
60
  "tags": [
@@ -97,7 +97,7 @@
97
97
  "i18next": ">=26 <27",
98
98
  "react": ">=19 <20",
99
99
  "react-i18next": ">=17 <18",
100
- "@entifix/i18n": "0.6.0"
100
+ "@entifix/i18n": "0.7.0"
101
101
  },
102
102
  "peerDependenciesMeta": {
103
103
  "@entifix/i18n": {