@entifix/react-controls 0.12.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/i18n/catalog/en.d.ts.map +1 -1
- package/dist/i18n/catalog/en.js +6 -1
- package/dist/i18n/catalog/en.js.map +1 -1
- package/dist/i18n/catalog/es.d.ts +5 -0
- package/dist/i18n/catalog/es.d.ts.map +1 -1
- package/dist/i18n/catalog/es.js +6 -1
- package/dist/i18n/catalog/es.js.map +1 -1
- package/dist/i18n/catalog/index.d.ts +10 -0
- package/dist/i18n/catalog/index.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/ui/molecules/toast-region/index.d.ts +2 -0
- package/dist/ui/molecules/toast-region/index.d.ts.map +1 -0
- package/dist/ui/molecules/toast-region/index.js +3 -0
- package/dist/ui/molecules/toast-region/index.js.map +1 -0
- package/dist/ui/molecules/toast-region/toast-region.d.ts +43 -0
- package/dist/ui/molecules/toast-region/toast-region.d.ts.map +1 -0
- package/dist/ui/molecules/toast-region/toast-region.js +82 -0
- package/dist/ui/molecules/toast-region/toast-region.js.map +1 -0
- package/dist/ui/organisms/entity-form/entity-form.d.ts +1 -1
- package/dist/ui/organisms/entity-form/entity-form.d.ts.map +1 -1
- package/dist/ui/organisms/entity-form/entity-form.js +3 -3
- package/dist/ui/organisms/entity-form/entity-form.js.map +1 -1
- package/dist/ui/organisms/entity-form/entity-form.types.d.ts +7 -2
- package/dist/ui/organisms/entity-form/entity-form.types.d.ts.map +1 -1
- package/dist/ui/organisms/entity-form/entity-form.types.js.map +1 -1
- package/dist/ui/organisms/entity-table/entity-table.types.d.ts +2 -2
- package/dist/ui/organisms/entity-table/entity-table.types.d.ts.map +1 -1
- package/dist/ui/organisms/entity-table/entity-table.types.js.map +1 -1
- package/package.json +13 -13
|
@@ -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,
|
|
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,UA2NtB,CAAC"}
|
package/dist/i18n/catalog/en.js
CHANGED
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
clone: 'Duplicate',
|
|
49
49
|
moreActions: 'More actions',
|
|
50
50
|
back: 'Back',
|
|
51
|
-
loading: 'Loading…'
|
|
51
|
+
loading: 'Loading…',
|
|
52
|
+
pending: 'Waiting for the server…'
|
|
52
53
|
},
|
|
53
54
|
pagination: {
|
|
54
55
|
label: 'Pages',
|
|
@@ -193,6 +194,10 @@
|
|
|
193
194
|
cancel: 'Cancel',
|
|
194
195
|
title: 'Confirm'
|
|
195
196
|
},
|
|
197
|
+
toast: {
|
|
198
|
+
region: 'Notifications',
|
|
199
|
+
dismiss: 'Dismiss'
|
|
200
|
+
},
|
|
196
201
|
breadcrumbs: {
|
|
197
202
|
label: 'Breadcrumb'
|
|
198
203
|
},
|
|
@@ -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 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"}
|
|
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 pending: 'Waiting for the server…',\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 toast: {\n region: 'Notifications',\n dismiss: 'Dismiss',\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","pending","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","toast","region","breadcrumbs","wizard","steps","finish","submitting","stepOf","goToStep","status","active","complete","resume"],"mappings":"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"}
|
|
@@ -63,6 +63,7 @@ export declare const controls: {
|
|
|
63
63
|
moreActions: string;
|
|
64
64
|
back: string;
|
|
65
65
|
loading: string;
|
|
66
|
+
pending: string;
|
|
66
67
|
};
|
|
67
68
|
pagination: {
|
|
68
69
|
label: string;
|
|
@@ -233,6 +234,10 @@ export declare const controls: {
|
|
|
233
234
|
cancel: string;
|
|
234
235
|
title: string;
|
|
235
236
|
};
|
|
237
|
+
toast: {
|
|
238
|
+
region: string;
|
|
239
|
+
dismiss: string;
|
|
240
|
+
};
|
|
236
241
|
breadcrumbs: {
|
|
237
242
|
label: string;
|
|
238
243
|
};
|
|
@@ -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;;;;;;QAMH;;;WAGG;;;;;;;IAOL;;;OAGG
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAgFH;;;;OAIG;;;;;;;;;;;;;IAaH;;;;;;OAMG;;;;;;;;;;;;;QAaD;;;WAGG;;;;IAIL;;;;;;;;;;OAUG;;;;;;;;;;;;;;;;;;IAkBH;;;;OAIG;;;;;;;;;;;;;;;;;;;;IAoBH;;;;;OAKG;;;;;;;;;;;;;;;;;;;;;;;IAuBH;;;;OAIG;;;;QAID;;;;WAIG;;;QAGH;;;;;WAKG;;;;QAIH;;;WAGG;;;;;;;QAOH;;;;WAIG;;;;;;CAMN,CAAC"}
|
package/dist/i18n/catalog/es.js
CHANGED
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
clone: 'Duplicar',
|
|
59
59
|
moreActions: 'Más acciones',
|
|
60
60
|
back: 'Volver',
|
|
61
|
-
loading: 'Cargando…'
|
|
61
|
+
loading: 'Cargando…',
|
|
62
|
+
pending: 'Esperando al servidor…'
|
|
62
63
|
},
|
|
63
64
|
pagination: {
|
|
64
65
|
label: 'Páginas',
|
|
@@ -223,6 +224,10 @@
|
|
|
223
224
|
cancel: 'Cancelar',
|
|
224
225
|
title: 'Confirmar'
|
|
225
226
|
},
|
|
227
|
+
toast: {
|
|
228
|
+
region: 'Notificaciones',
|
|
229
|
+
dismiss: 'Descartar'
|
|
230
|
+
},
|
|
226
231
|
breadcrumbs: {
|
|
227
232
|
label: 'Ruta de navegación'
|
|
228
233
|
},
|
|
@@ -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 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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"}
|
|
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 pending: 'Esperando al servidor…',\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 toast: {\n region: 'Notificaciones',\n dismiss: 'Descartar',\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","pending","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","toast","region","breadcrumbs","wizard","steps","finish","submitting","stepOf","goToStep","status","active","complete","resume"],"mappings":"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"}
|
|
@@ -55,6 +55,7 @@ export declare const controlsCatalogs: {
|
|
|
55
55
|
moreActions: string;
|
|
56
56
|
back: string;
|
|
57
57
|
loading: string;
|
|
58
|
+
pending: string;
|
|
58
59
|
};
|
|
59
60
|
pagination: {
|
|
60
61
|
label: string;
|
|
@@ -187,6 +188,10 @@ export declare const controlsCatalogs: {
|
|
|
187
188
|
cancel: string;
|
|
188
189
|
title: string;
|
|
189
190
|
};
|
|
191
|
+
toast: {
|
|
192
|
+
region: string;
|
|
193
|
+
dismiss: string;
|
|
194
|
+
};
|
|
190
195
|
breadcrumbs: {
|
|
191
196
|
label: string;
|
|
192
197
|
};
|
|
@@ -258,6 +263,7 @@ export declare const controlsCatalogs: {
|
|
|
258
263
|
moreActions: string;
|
|
259
264
|
back: string;
|
|
260
265
|
loading: string;
|
|
266
|
+
pending: string;
|
|
261
267
|
};
|
|
262
268
|
pagination: {
|
|
263
269
|
label: string;
|
|
@@ -390,6 +396,10 @@ export declare const controlsCatalogs: {
|
|
|
390
396
|
cancel: string;
|
|
391
397
|
title: string;
|
|
392
398
|
};
|
|
399
|
+
toast: {
|
|
400
|
+
region: string;
|
|
401
|
+
dismiss: string;
|
|
402
|
+
};
|
|
393
403
|
breadcrumbs: {
|
|
394
404
|
label: string;
|
|
395
405
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/i18n/catalog/index.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB
|
|
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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ export * from './ui/molecules/stack/index.js';
|
|
|
43
43
|
export * from './ui/molecules/tab-strip/index.js';
|
|
44
44
|
export * from './ui/molecules/table-toolbar/index.js';
|
|
45
45
|
export * from './ui/molecules/theme-switcher/index.js';
|
|
46
|
+
export * from './ui/molecules/toast-region/index.js';
|
|
46
47
|
export * from './ui/organisms/command-palette/index.js';
|
|
47
48
|
export * from './ui/organisms/entity-detail-grid/index.js';
|
|
48
49
|
export * from './ui/organisms/entity-form/index.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC;AACpD,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,yCAAyC,CAAC;AACxD,cAAc,wCAAwC,CAAC;AACvD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,4CAA4C,CAAC;AAC3D,cAAc,4CAA4C,CAAC;AAC3D,cAAc,wCAAwC,CAAC;AACvD,cAAc,0CAA0C,CAAC;AACzD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,oCAAoC,CAAC;AACnD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,uCAAuC,CAAC;AACtD,cAAc,sCAAsC,CAAC;AACrD,cAAc,sCAAsC,CAAC;AACrD,cAAc,oCAAoC,CAAC;AACnD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,uCAAuC,CAAC;AACtD,cAAc,wCAAwC,CAAC;AACvD,cAAc,yCAAyC,CAAC;AACxD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,iCAAiC,CAAC;AAChD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,oCAAoC,CAAC;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,kBAAkB,CAAC;AACjC,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC;AACpD,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,yCAAyC,CAAC;AACxD,cAAc,wCAAwC,CAAC;AACvD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,4CAA4C,CAAC;AAC3D,cAAc,4CAA4C,CAAC;AAC3D,cAAc,wCAAwC,CAAC;AACvD,cAAc,0CAA0C,CAAC;AACzD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,oCAAoC,CAAC;AACnD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,uCAAuC,CAAC;AACtD,cAAc,sCAAsC,CAAC;AACrD,cAAc,sCAAsC,CAAC;AACrD,cAAc,oCAAoC,CAAC;AACnD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,uCAAuC,CAAC;AACtD,cAAc,wCAAwC,CAAC;AACvD,cAAc,sCAAsC,CAAC;AACrD,cAAc,yCAAyC,CAAC;AACxD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,iCAAiC,CAAC;AAChD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,oCAAoC,CAAC;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,kBAAkB,CAAC;AACjC,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,0BAA0B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -43,6 +43,7 @@ export * from './ui/molecules/stack/index.js';
|
|
|
43
43
|
export * from './ui/molecules/tab-strip/index.js';
|
|
44
44
|
export * from './ui/molecules/table-toolbar/index.js';
|
|
45
45
|
export * from './ui/molecules/theme-switcher/index.js';
|
|
46
|
+
export * from './ui/molecules/toast-region/index.js';
|
|
46
47
|
export * from './ui/organisms/command-palette/index.js';
|
|
47
48
|
export * from './ui/organisms/entity-detail-grid/index.js';
|
|
48
49
|
export * from './ui/organisms/entity-form/index.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './i18n/index.js';\nexport * from './preferences/index.js';\nexport * from './theme/index.js';\nexport * from './ui/actions/index.js';\nexport * from './ui/atoms/button/index.js';\nexport * from './ui/atoms/cell-value/index.js';\nexport * from './ui/atoms/external-link/index.js';\nexport * from './ui/atoms/field/index.js';\nexport * from './ui/atoms/field-control/index.js';\nexport * from './ui/atoms/localized-field-control/index.js';\nexport * from './ui/atoms/skeleton/index.js';\nexport * from './ui/atoms/switch/index.js';\nexport * from './ui/atoms/table/index.js';\nexport * from './ui/atoms/text/index.js';\nexport * from './ui/layout/box/index.js';\nexport * from './ui/layout/center/index.js';\nexport * from './ui/layout/cluster/index.js';\nexport * from './ui/layout/cover/index.js';\nexport * from './ui/layout/grid/index.js';\nexport * from './ui/layout/sidebar/index.js';\nexport * from './ui/layout/switcher/index.js';\nexport * from './ui/molecules/breadcrumbs/index.js';\nexport * from './ui/molecules/bulk-action-bar/index.js';\nexport * from './ui/molecules/bulk-result/index.js';\nexport * from './ui/molecules/card/index.js';\nexport * from './ui/molecules/column-settings/index.js';\nexport * from './ui/molecules/confirm-dialog/index.js';\nexport * from './ui/molecules/drawer/index.js';\nexport * from './ui/molecules/entity-link-input/index.js';\nexport * from './ui/molecules/entity-link-picker/index.js';\nexport * from './ui/molecules/entity-record-card/index.js';\nexport * from './ui/molecules/filter-builder/index.js';\nexport * from './ui/molecules/loading-boundary/index.js';\nexport * from './ui/molecules/menu/index.js';\nexport * from './ui/molecules/nav-menu/index.js';\nexport * from './ui/molecules/pagination/index.js';\nexport * from './ui/molecules/segmented-control/index.js';\nexport * from './ui/molecules/segmented-nav/index.js';\nexport * from './ui/molecules/sort-builder/index.js';\nexport * from './ui/molecules/split-button/index.js';\nexport * from './ui/molecules/split-view/index.js';\nexport * from './ui/molecules/stack/index.js';\nexport * from './ui/molecules/tab-strip/index.js';\nexport * from './ui/molecules/table-toolbar/index.js';\nexport * from './ui/molecules/theme-switcher/index.js';\nexport * from './ui/organisms/command-palette/index.js';\nexport * from './ui/organisms/entity-detail-grid/index.js';\nexport * from './ui/organisms/entity-form/index.js';\nexport * from './ui/organisms/entity-table/index.js';\nexport * from './ui/organisms/top-bar/index.js';\nexport * from './ui/organisms/wizard/index.js';\nexport * from './ui/utils/active-heading/index.js';\nexport * from './ui/utils/active-section/index.js';\nexport * from './ui/utils/cn.js';\nexport * from './ui/utils/fold-script/index.js';\nexport * from './ui/utils/keep-section/index.js';\nexport * from './ui/utils/media-query/index.js';\nexport * from './ui/utils/use-hotkey.js';\n"],"names":[],"mappings":"AAAA,cAAc,kBAAkB;AAChC,cAAc,yBAAyB;AACvC,cAAc,mBAAmB;AACjC,cAAc,wBAAwB;AACtC,cAAc,6BAA6B;AAC3C,cAAc,iCAAiC;AAC/C,cAAc,oCAAoC;AAClD,cAAc,4BAA4B;AAC1C,cAAc,oCAAoC;AAClD,cAAc,8CAA8C;AAC5D,cAAc,+BAA+B;AAC7C,cAAc,6BAA6B;AAC3C,cAAc,4BAA4B;AAC1C,cAAc,2BAA2B;AACzC,cAAc,2BAA2B;AACzC,cAAc,8BAA8B;AAC5C,cAAc,+BAA+B;AAC7C,cAAc,6BAA6B;AAC3C,cAAc,4BAA4B;AAC1C,cAAc,+BAA+B;AAC7C,cAAc,gCAAgC;AAC9C,cAAc,sCAAsC;AACpD,cAAc,0CAA0C;AACxD,cAAc,sCAAsC;AACpD,cAAc,+BAA+B;AAC7C,cAAc,0CAA0C;AACxD,cAAc,yCAAyC;AACvD,cAAc,iCAAiC;AAC/C,cAAc,4CAA4C;AAC1D,cAAc,6CAA6C;AAC3D,cAAc,6CAA6C;AAC3D,cAAc,yCAAyC;AACvD,cAAc,2CAA2C;AACzD,cAAc,+BAA+B;AAC7C,cAAc,mCAAmC;AACjD,cAAc,qCAAqC;AACnD,cAAc,4CAA4C;AAC1D,cAAc,wCAAwC;AACtD,cAAc,uCAAuC;AACrD,cAAc,uCAAuC;AACrD,cAAc,qCAAqC;AACnD,cAAc,gCAAgC;AAC9C,cAAc,oCAAoC;AAClD,cAAc,wCAAwC;AACtD,cAAc,yCAAyC;AACvD,cAAc,0CAA0C;AACxD,cAAc,6CAA6C;AAC3D,cAAc,sCAAsC;AACpD,cAAc,uCAAuC;AACrD,cAAc,kCAAkC;AAChD,cAAc,iCAAiC;AAC/C,cAAc,qCAAqC;AACnD,cAAc,qCAAqC;AACnD,cAAc,mBAAmB;AACjC,cAAc,kCAAkC;AAChD,cAAc,mCAAmC;AACjD,cAAc,kCAAkC;AAChD,cAAc,2BAA2B"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './i18n/index.js';\nexport * from './preferences/index.js';\nexport * from './theme/index.js';\nexport * from './ui/actions/index.js';\nexport * from './ui/atoms/button/index.js';\nexport * from './ui/atoms/cell-value/index.js';\nexport * from './ui/atoms/external-link/index.js';\nexport * from './ui/atoms/field/index.js';\nexport * from './ui/atoms/field-control/index.js';\nexport * from './ui/atoms/localized-field-control/index.js';\nexport * from './ui/atoms/skeleton/index.js';\nexport * from './ui/atoms/switch/index.js';\nexport * from './ui/atoms/table/index.js';\nexport * from './ui/atoms/text/index.js';\nexport * from './ui/layout/box/index.js';\nexport * from './ui/layout/center/index.js';\nexport * from './ui/layout/cluster/index.js';\nexport * from './ui/layout/cover/index.js';\nexport * from './ui/layout/grid/index.js';\nexport * from './ui/layout/sidebar/index.js';\nexport * from './ui/layout/switcher/index.js';\nexport * from './ui/molecules/breadcrumbs/index.js';\nexport * from './ui/molecules/bulk-action-bar/index.js';\nexport * from './ui/molecules/bulk-result/index.js';\nexport * from './ui/molecules/card/index.js';\nexport * from './ui/molecules/column-settings/index.js';\nexport * from './ui/molecules/confirm-dialog/index.js';\nexport * from './ui/molecules/drawer/index.js';\nexport * from './ui/molecules/entity-link-input/index.js';\nexport * from './ui/molecules/entity-link-picker/index.js';\nexport * from './ui/molecules/entity-record-card/index.js';\nexport * from './ui/molecules/filter-builder/index.js';\nexport * from './ui/molecules/loading-boundary/index.js';\nexport * from './ui/molecules/menu/index.js';\nexport * from './ui/molecules/nav-menu/index.js';\nexport * from './ui/molecules/pagination/index.js';\nexport * from './ui/molecules/segmented-control/index.js';\nexport * from './ui/molecules/segmented-nav/index.js';\nexport * from './ui/molecules/sort-builder/index.js';\nexport * from './ui/molecules/split-button/index.js';\nexport * from './ui/molecules/split-view/index.js';\nexport * from './ui/molecules/stack/index.js';\nexport * from './ui/molecules/tab-strip/index.js';\nexport * from './ui/molecules/table-toolbar/index.js';\nexport * from './ui/molecules/theme-switcher/index.js';\nexport * from './ui/molecules/toast-region/index.js';\nexport * from './ui/organisms/command-palette/index.js';\nexport * from './ui/organisms/entity-detail-grid/index.js';\nexport * from './ui/organisms/entity-form/index.js';\nexport * from './ui/organisms/entity-table/index.js';\nexport * from './ui/organisms/top-bar/index.js';\nexport * from './ui/organisms/wizard/index.js';\nexport * from './ui/utils/active-heading/index.js';\nexport * from './ui/utils/active-section/index.js';\nexport * from './ui/utils/cn.js';\nexport * from './ui/utils/fold-script/index.js';\nexport * from './ui/utils/keep-section/index.js';\nexport * from './ui/utils/media-query/index.js';\nexport * from './ui/utils/use-hotkey.js';\n"],"names":[],"mappings":"AAAA,cAAc,kBAAkB;AAChC,cAAc,yBAAyB;AACvC,cAAc,mBAAmB;AACjC,cAAc,wBAAwB;AACtC,cAAc,6BAA6B;AAC3C,cAAc,iCAAiC;AAC/C,cAAc,oCAAoC;AAClD,cAAc,4BAA4B;AAC1C,cAAc,oCAAoC;AAClD,cAAc,8CAA8C;AAC5D,cAAc,+BAA+B;AAC7C,cAAc,6BAA6B;AAC3C,cAAc,4BAA4B;AAC1C,cAAc,2BAA2B;AACzC,cAAc,2BAA2B;AACzC,cAAc,8BAA8B;AAC5C,cAAc,+BAA+B;AAC7C,cAAc,6BAA6B;AAC3C,cAAc,4BAA4B;AAC1C,cAAc,+BAA+B;AAC7C,cAAc,gCAAgC;AAC9C,cAAc,sCAAsC;AACpD,cAAc,0CAA0C;AACxD,cAAc,sCAAsC;AACpD,cAAc,+BAA+B;AAC7C,cAAc,0CAA0C;AACxD,cAAc,yCAAyC;AACvD,cAAc,iCAAiC;AAC/C,cAAc,4CAA4C;AAC1D,cAAc,6CAA6C;AAC3D,cAAc,6CAA6C;AAC3D,cAAc,yCAAyC;AACvD,cAAc,2CAA2C;AACzD,cAAc,+BAA+B;AAC7C,cAAc,mCAAmC;AACjD,cAAc,qCAAqC;AACnD,cAAc,4CAA4C;AAC1D,cAAc,wCAAwC;AACtD,cAAc,uCAAuC;AACrD,cAAc,uCAAuC;AACrD,cAAc,qCAAqC;AACnD,cAAc,gCAAgC;AAC9C,cAAc,oCAAoC;AAClD,cAAc,wCAAwC;AACtD,cAAc,yCAAyC;AACvD,cAAc,uCAAuC;AACrD,cAAc,0CAA0C;AACxD,cAAc,6CAA6C;AAC3D,cAAc,sCAAsC;AACpD,cAAc,uCAAuC;AACrD,cAAc,kCAAkC;AAChD,cAAc,iCAAiC;AAC/C,cAAc,qCAAqC;AACnD,cAAc,qCAAqC;AACnD,cAAc,mBAAmB;AACjC,cAAc,kCAAkC;AAChD,cAAc,mCAAmC;AACjD,cAAc,kCAAkC;AAChD,cAAc,2BAA2B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ui/molecules/toast-region/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/ui/molecules/toast-region/index.ts"],"sourcesContent":["export * from './toast-region.js';\n"],"names":[],"mappings":"AAAA,cAAc,oBAAoB"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { useErrorMessage } from '../../../i18n/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* A failure as {@link useErrorMessage} renders it: a typed failure carries its
|
|
5
|
+
* `code`, and anything else that reached the user keeps its `message`.
|
|
6
|
+
*/
|
|
7
|
+
export type ToastFailure = Parameters<ReturnType<typeof useErrorMessage>>[0];
|
|
8
|
+
/** One failure waiting in the region, under the id it was shown with. */
|
|
9
|
+
export interface FailureToast {
|
|
10
|
+
readonly id: number;
|
|
11
|
+
readonly failure: ToastFailure;
|
|
12
|
+
}
|
|
13
|
+
export interface Toasts {
|
|
14
|
+
/** The failures shown, oldest first. */
|
|
15
|
+
readonly failures: readonly FailureToast[];
|
|
16
|
+
/** Shows a failure until it is dismissed, and answers its id. */
|
|
17
|
+
readonly showFailure: (failure: ToastFailure) => number;
|
|
18
|
+
readonly dismiss: (id: number) => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Holds the toasts one host shows. Mounted once, above everything that can
|
|
22
|
+
* fail asynchronously, with a {@link ToastRegion} somewhere inside it.
|
|
23
|
+
*/
|
|
24
|
+
export declare function ToastProvider({ children }: {
|
|
25
|
+
readonly children: ReactNode;
|
|
26
|
+
}): import("react").JSX.Element;
|
|
27
|
+
/**
|
|
28
|
+
* The {@link ToastProvider} above, or `undefined` outside one: a caller with
|
|
29
|
+
* no region to show a failure in keeps it where it can, rather than having it
|
|
30
|
+
* vanish.
|
|
31
|
+
*/
|
|
32
|
+
export declare const useToasts: () => Toasts | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Where a host's toasts render: each failure in the reader's language,
|
|
35
|
+
* through the same {@link useErrorMessage} a form's error slot uses, so an
|
|
36
|
+
* asynchronous failure reads exactly as a synchronous one would.
|
|
37
|
+
*
|
|
38
|
+
* A failure stays until it is dismissed. It reports something that already
|
|
39
|
+
* happened, often after the screen that started it was left, and a message
|
|
40
|
+
* that times out is one a slow reader never gets.
|
|
41
|
+
*/
|
|
42
|
+
export declare function ToastRegion(): import("react").JSX.Element | null;
|
|
43
|
+
//# sourceMappingURL=toast-region.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toast-region.d.ts","sourceRoot":"","sources":["../../../../src/ui/molecules/toast-region/toast-region.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,eAAe,EAAQ,MAAM,wBAAwB,CAAC;AAG/D;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE7E,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;CAChC;AAED,MAAM,WAAW,MAAM;IACrB,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,iEAAiE;IACjE,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,MAAM,CAAC;IACxD,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AAID;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAqB3E;AAED;;;;GAIG;AACH,eAAO,MAAM,SAAS,QAAO,MAAM,GAAG,SAAqC,CAAC;AAE5E;;;;;;;;GAQG;AACH,wBAAgB,WAAW,uCA6B1B"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useCallback, useContext, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { useErrorMessage, useT } from '../../../i18n/index.js';
|
|
5
|
+
import { Button } from '../../atoms/button/index.js';
|
|
6
|
+
const ToastContext = /*#__PURE__*/ createContext(undefined);
|
|
7
|
+
/**
|
|
8
|
+
* Holds the toasts one host shows. Mounted once, above everything that can
|
|
9
|
+
* fail asynchronously, with a {@link ToastRegion} somewhere inside it.
|
|
10
|
+
*/ export function ToastProvider({ children }) {
|
|
11
|
+
const [failures, setFailures] = useState([]);
|
|
12
|
+
const next = useRef(0);
|
|
13
|
+
const showFailure = useCallback((failure)=>{
|
|
14
|
+
next.current += 1;
|
|
15
|
+
const id = next.current;
|
|
16
|
+
setFailures((shown)=>[
|
|
17
|
+
...shown,
|
|
18
|
+
{
|
|
19
|
+
id,
|
|
20
|
+
failure
|
|
21
|
+
}
|
|
22
|
+
]);
|
|
23
|
+
return id;
|
|
24
|
+
}, []);
|
|
25
|
+
const dismiss = useCallback((id)=>{
|
|
26
|
+
setFailures((shown)=>shown.filter((toast)=>toast.id !== id));
|
|
27
|
+
}, []);
|
|
28
|
+
const value = useMemo(()=>({
|
|
29
|
+
failures,
|
|
30
|
+
showFailure,
|
|
31
|
+
dismiss
|
|
32
|
+
}), [
|
|
33
|
+
failures,
|
|
34
|
+
showFailure,
|
|
35
|
+
dismiss
|
|
36
|
+
]);
|
|
37
|
+
return /*#__PURE__*/ _jsx(ToastContext.Provider, {
|
|
38
|
+
value: value,
|
|
39
|
+
children: children
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The {@link ToastProvider} above, or `undefined` outside one: a caller with
|
|
44
|
+
* no region to show a failure in keeps it where it can, rather than having it
|
|
45
|
+
* vanish.
|
|
46
|
+
*/ export const useToasts = ()=>useContext(ToastContext);
|
|
47
|
+
/**
|
|
48
|
+
* Where a host's toasts render: each failure in the reader's language,
|
|
49
|
+
* through the same {@link useErrorMessage} a form's error slot uses, so an
|
|
50
|
+
* asynchronous failure reads exactly as a synchronous one would.
|
|
51
|
+
*
|
|
52
|
+
* A failure stays until it is dismissed. It reports something that already
|
|
53
|
+
* happened, often after the screen that started it was left, and a message
|
|
54
|
+
* that times out is one a slow reader never gets.
|
|
55
|
+
*/ export function ToastRegion() {
|
|
56
|
+
const toasts = useToasts();
|
|
57
|
+
const t = useT();
|
|
58
|
+
const message = useErrorMessage();
|
|
59
|
+
if (toasts === undefined) return null;
|
|
60
|
+
return /*#__PURE__*/ _jsx("section", {
|
|
61
|
+
"aria-label": t('toast.region'),
|
|
62
|
+
className: "bottom-0 right-0 max-w-md fixed z-50 flex w-full flex-col gap-xs p-s",
|
|
63
|
+
children: toasts.failures.map(({ id, failure })=>/*#__PURE__*/ _jsxs("div", {
|
|
64
|
+
role: "alert",
|
|
65
|
+
className: "flex items-start gap-xs rounded-lg border border-danger bg-surface p-xs shadow-overlay",
|
|
66
|
+
children: [
|
|
67
|
+
/*#__PURE__*/ _jsx("p", {
|
|
68
|
+
className: "flex-1 text-step-sm text-danger",
|
|
69
|
+
children: message(failure)
|
|
70
|
+
}),
|
|
71
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
72
|
+
type: "button",
|
|
73
|
+
variant: "ghost",
|
|
74
|
+
onClick: ()=>toasts.dismiss(id),
|
|
75
|
+
children: t('toast.dismiss')
|
|
76
|
+
})
|
|
77
|
+
]
|
|
78
|
+
}, id))
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
//# sourceMappingURL=toast-region.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/ui/molecules/toast-region/toast-region.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport { useErrorMessage, useT } from '../../../i18n/index.js';\nimport { Button } from '../../atoms/button/index.js';\n\n/**\n * A failure as {@link useErrorMessage} renders it: a typed failure carries its\n * `code`, and anything else that reached the user keeps its `message`.\n */\nexport type ToastFailure = Parameters<ReturnType<typeof useErrorMessage>>[0];\n\n/** One failure waiting in the region, under the id it was shown with. */\nexport interface FailureToast {\n readonly id: number;\n readonly failure: ToastFailure;\n}\n\nexport interface Toasts {\n /** The failures shown, oldest first. */\n readonly failures: readonly FailureToast[];\n /** Shows a failure until it is dismissed, and answers its id. */\n readonly showFailure: (failure: ToastFailure) => number;\n readonly dismiss: (id: number) => void;\n}\n\nconst ToastContext = createContext<Toasts | undefined>(undefined);\n\n/**\n * Holds the toasts one host shows. Mounted once, above everything that can\n * fail asynchronously, with a {@link ToastRegion} somewhere inside it.\n */\nexport function ToastProvider({ children }: { readonly children: ReactNode }) {\n const [failures, setFailures] = useState<readonly FailureToast[]>([]);\n const next = useRef(0);\n\n const showFailure = useCallback((failure: ToastFailure) => {\n next.current += 1;\n const id = next.current;\n setFailures(shown => [...shown, { id, failure }]);\n return id;\n }, []);\n const dismiss = useCallback((id: number) => {\n setFailures(shown => shown.filter(toast => toast.id !== id));\n }, []);\n\n const value = useMemo(\n () => ({ failures, showFailure, dismiss }),\n [failures, showFailure, dismiss],\n );\n return (\n <ToastContext.Provider value={value}>{children}</ToastContext.Provider>\n );\n}\n\n/**\n * The {@link ToastProvider} above, or `undefined` outside one: a caller with\n * no region to show a failure in keeps it where it can, rather than having it\n * vanish.\n */\nexport const useToasts = (): Toasts | undefined => useContext(ToastContext);\n\n/**\n * Where a host's toasts render: each failure in the reader's language,\n * through the same {@link useErrorMessage} a form's error slot uses, so an\n * asynchronous failure reads exactly as a synchronous one would.\n *\n * A failure stays until it is dismissed. It reports something that already\n * happened, often after the screen that started it was left, and a message\n * that times out is one a slow reader never gets.\n */\nexport function ToastRegion() {\n const toasts = useToasts();\n const t = useT();\n const message = useErrorMessage();\n\n if (toasts === undefined) return null;\n return (\n <section\n aria-label={t('toast.region')}\n className=\"bottom-0 right-0 max-w-md fixed z-50 flex w-full flex-col gap-xs p-s\"\n >\n {toasts.failures.map(({ id, failure }) => (\n <div\n key={id}\n role=\"alert\"\n className=\"flex items-start gap-xs rounded-lg border border-danger bg-surface p-xs shadow-overlay\"\n >\n <p className=\"flex-1 text-step-sm text-danger\">{message(failure)}</p>\n <Button\n type=\"button\"\n variant=\"ghost\"\n onClick={() => toasts.dismiss(id)}\n >\n {t('toast.dismiss')}\n </Button>\n </div>\n ))}\n </section>\n );\n}\n"],"names":["createContext","useCallback","useContext","useMemo","useRef","useState","useErrorMessage","useT","Button","ToastContext","undefined","ToastProvider","children","failures","setFailures","next","showFailure","failure","current","id","shown","dismiss","filter","toast","value","Provider","useToasts","ToastRegion","toasts","t","message","section","aria-label","className","map","div","role","p","type","variant","onClick"],"mappings":"AAAA;;AAEA,SACEA,aAAa,EAEbC,WAAW,EACXC,UAAU,EACVC,OAAO,EACPC,MAAM,EACNC,QAAQ,QACH,QAAQ;AAEf,SAASC,eAAe,EAAEC,IAAI,QAAQ,yBAAyB;AAC/D,SAASC,MAAM,QAAQ,8BAA8B;AAsBrD,MAAMC,6BAAeT,cAAkCU;AAEvD;;;CAGC,GACD,OAAO,SAASC,cAAc,EAAEC,QAAQ,EAAoC;IAC1E,MAAM,CAACC,UAAUC,YAAY,GAAGT,SAAkC,EAAE;IACpE,MAAMU,OAAOX,OAAO;IAEpB,MAAMY,cAAcf,YAAY,CAACgB;QAC/BF,KAAKG,OAAO,IAAI;QAChB,MAAMC,KAAKJ,KAAKG,OAAO;QACvBJ,YAAYM,CAAAA,QAAS;mBAAIA;gBAAO;oBAAED;oBAAIF;gBAAQ;aAAE;QAChD,OAAOE;IACT,GAAG,EAAE;IACL,MAAME,UAAUpB,YAAY,CAACkB;QAC3BL,YAAYM,CAAAA,QAASA,MAAME,MAAM,CAACC,CAAAA,QAASA,MAAMJ,EAAE,KAAKA;IAC1D,GAAG,EAAE;IAEL,MAAMK,QAAQrB,QACZ,IAAO,CAAA;YAAEU;YAAUG;YAAaK;QAAQ,CAAA,GACxC;QAACR;QAAUG;QAAaK;KAAQ;IAElC,qBACE,KAACZ,aAAagB,QAAQ;QAACD,OAAOA;kBAAQZ;;AAE1C;AAEA;;;;CAIC,GACD,OAAO,MAAMc,YAAY,IAA0BxB,WAAWO,cAAc;AAE5E;;;;;;;;CAQC,GACD,OAAO,SAASkB;IACd,MAAMC,SAASF;IACf,MAAMG,IAAItB;IACV,MAAMuB,UAAUxB;IAEhB,IAAIsB,WAAWlB,WAAW,OAAO;IACjC,qBACE,KAACqB;QACCC,cAAYH,EAAE;QACdI,WAAU;kBAETL,OAAOf,QAAQ,CAACqB,GAAG,CAAC,CAAC,EAAEf,EAAE,EAAEF,OAAO,EAAE,iBACnC,MAACkB;gBAECC,MAAK;gBACLH,WAAU;;kCAEV,KAACI;wBAAEJ,WAAU;kCAAmCH,QAAQb;;kCACxD,KAACT;wBACC8B,MAAK;wBACLC,SAAQ;wBACRC,SAAS,IAAMZ,OAAOP,OAAO,CAACF;kCAE7BU,EAAE;;;eAVAV;;AAgBf"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { type Entity } from '@entifix/core';
|
|
2
2
|
import type { EntityFormProps } from './entity-form.types.js';
|
|
3
|
-
export declare function EntityForm<TEntity extends Entity>({ entityConstructor, entity, values, onFieldChange, linkSources, onLinkChange, mode: modeProp, defaultMode, onModeChange, errors, formError, onSubmit, onDelete, embedded, isLoading, skeleton, isSaving, isDeleting, error, backHref, renderLink, title, metadata, isMetadataLoading, onUseCase, onClone, children, }: EntityFormProps<TEntity>): import("react").JSX.Element;
|
|
3
|
+
export declare function EntityForm<TEntity extends Entity>({ entityConstructor, entity, values, onFieldChange, linkSources, onLinkChange, mode: modeProp, defaultMode, onModeChange, errors, formError, onSubmit, onDelete, embedded, isLoading, skeleton, isSaving, isPending, isDeleting, error, backHref, renderLink, title, metadata, isMetadataLoading, onUseCase, onClone, children, }: EntityFormProps<TEntity>): import("react").JSX.Element;
|
|
4
4
|
//# sourceMappingURL=entity-form.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-form.d.ts","sourceRoot":"","sources":["../../../../src/ui/organisms/entity-form/entity-form.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,MAAM,EAUZ,MAAM,eAAe,CAAC;AA4BvB,OAAO,KAAK,EAGV,eAAe,EAChB,MAAM,wBAAwB,CAAC;AA6DhC,wBAAgB,UAAU,CAAC,OAAO,SAAS,MAAM,EAAE,EACjD,iBAAiB,EACjB,MAAM,EACN,MAAM,EACN,aAAa,EACb,WAAW,EACX,YAAY,EACZ,IAAI,EAAE,QAAQ,EACd,WAAW,EACX,YAAY,EACZ,MAAM,EACN,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,QAAgB,EAChB,SAAiB,EACjB,QAAe,EACf,QAAgB,EAChB,UAAkB,EAClB,KAAK,EACL,QAAQ,EACR,UAAyB,EACzB,KAAK,EACL,QAAQ,EACR,iBAAyB,EACzB,SAAS,EACT,OAAO,EACP,QAAQ,GACT,EAAE,eAAe,CAAC,OAAO,CAAC,+
|
|
1
|
+
{"version":3,"file":"entity-form.d.ts","sourceRoot":"","sources":["../../../../src/ui/organisms/entity-form/entity-form.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,MAAM,EAUZ,MAAM,eAAe,CAAC;AA4BvB,OAAO,KAAK,EAGV,eAAe,EAChB,MAAM,wBAAwB,CAAC;AA6DhC,wBAAgB,UAAU,CAAC,OAAO,SAAS,MAAM,EAAE,EACjD,iBAAiB,EACjB,MAAM,EACN,MAAM,EACN,aAAa,EACb,WAAW,EACX,YAAY,EACZ,IAAI,EAAE,QAAQ,EACd,WAAW,EACX,YAAY,EACZ,MAAM,EACN,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,QAAgB,EAChB,SAAiB,EACjB,QAAe,EACf,QAAgB,EAChB,SAAiB,EACjB,UAAkB,EAClB,KAAK,EACL,QAAQ,EACR,UAAyB,EACzB,KAAK,EACL,QAAQ,EACR,iBAAyB,EACzB,SAAS,EACT,OAAO,EACP,QAAQ,GACT,EAAE,eAAe,CAAC,OAAO,CAAC,+BAkX1B"}
|
|
@@ -66,7 +66,7 @@ import { resolveEntityFormFields } from './use-entity-form-fields.js';
|
|
|
66
66
|
* without the row wrapping — the same number Fiori's object-page header settles
|
|
67
67
|
* on. It is a layout constant, not a policy: raising it does not grant anything.
|
|
68
68
|
*/ const VISIBLE_ACTION_LIMIT = 4;
|
|
69
|
-
export function EntityForm({ entityConstructor, entity, values, onFieldChange, linkSources, onLinkChange, mode: modeProp, defaultMode, onModeChange, errors, formError, onSubmit, onDelete, embedded = false, isLoading = false, skeleton = true, isSaving = false, isDeleting = false, error, backHref, renderLink = renderAnchor, title, metadata, isMetadataLoading = false, onUseCase, onClone, children }) {
|
|
69
|
+
export function EntityForm({ entityConstructor, entity, values, onFieldChange, linkSources, onLinkChange, mode: modeProp, defaultMode, onModeChange, errors, formError, onSubmit, onDelete, embedded = false, isLoading = false, skeleton = true, isSaving = false, isPending = false, isDeleting = false, error, backHref, renderLink = renderAnchor, title, metadata, isMetadataLoading = false, onUseCase, onClone, children }) {
|
|
70
70
|
const formId = useId();
|
|
71
71
|
const slots = readEntityFormFields(children);
|
|
72
72
|
// The *field* descriptors, which stay local and synchronous — a column is a
|
|
@@ -106,7 +106,7 @@ export function EntityForm({ entityConstructor, entity, values, onFieldChange, l
|
|
|
106
106
|
const t = useT();
|
|
107
107
|
const translateKey = useTranslateKey();
|
|
108
108
|
const errorMessage = useErrorMessage();
|
|
109
|
-
const busy = isSaving || isDeleting;
|
|
109
|
+
const busy = isSaving || isPending || isDeleting;
|
|
110
110
|
// A draft restored from storage holds relation ids and no instances, so every
|
|
111
111
|
// picker is resolving its target for a moment after mount. Saving inside that
|
|
112
112
|
// window is exactly the case `applyEntityLinks` refuses: an `embedded` member
|
|
@@ -303,7 +303,7 @@ export function EntityForm({ entityConstructor, entity, values, onFieldChange, l
|
|
|
303
303
|
type: "button",
|
|
304
304
|
onClick: ()=>onSubmit == null ? void 0 : onSubmit(draft),
|
|
305
305
|
disabled: busy || resolvingLinks,
|
|
306
|
-
children: isSaving ? t('form.saving') : t('form.save')
|
|
306
|
+
children: isPending ? t('form.pending') : isSaving ? t('form.saving') : t('form.save')
|
|
307
307
|
}),
|
|
308
308
|
onDelete && may('delete') && /*#__PURE__*/ _jsx(Button, {
|
|
309
309
|
type: "button",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/ui/organisms/entity-form/entity-form.tsx"],"sourcesContent":["'use client';\n\nimport {\n cloneEntityDraft,\n describeEntityColumns,\n type Entity,\n type EntityAction,\n type EntityDraft,\n type EntityDraftValue,\n type EntityLinkSource,\n type MetaAccessorType,\n readDraftString,\n readLocalizedDraft,\n readRowDrafts,\n type UseCaseDescriptor,\n} from '@entifix/core';\nimport { type ReactNode, useId, useState } from 'react';\n\nimport {\n useErrorMessage,\n useLocalizedDescriptors,\n useT,\n useTranslateKey,\n} from '../../../i18n/index.js';\nimport {\n OVERFLOW_GLYPH,\n renderAnchor,\n useCasesForSurface,\n} from '../../actions/index.js';\nimport { Button, button } from '../../atoms/button/index.js';\nimport { CellValue } from '../../atoms/cell-value/index.js';\nimport { FieldControl } from '../../atoms/field-control/index.js';\nimport { LocalizedFieldControl } from '../../atoms/localized-field-control/index.js';\nimport { Skeleton } from '../../atoms/skeleton/index.js';\nimport { Text } from '../../atoms/text/index.js';\nimport { Card } from '../../molecules/card/index.js';\nimport { ConfirmDialog } from '../../molecules/confirm-dialog/index.js';\nimport { EntityLinkInput } from '../../molecules/entity-link-input/index.js';\nimport { LoadingBoundary } from '../../molecules/loading-boundary/index.js';\nimport { Menu } from '../../molecules/menu/index.js';\nimport { Stack } from '../../molecules/stack/index.js';\nimport { cn } from '../../utils/cn.js';\nimport { EntityDetailGrid } from '../entity-detail-grid/index.js';\nimport type {\n EntityFormField,\n EntityFormMode,\n EntityFormProps,\n} from './entity-form.types.js';\nimport { readEntityFormFields } from './entity-form-slots.js';\nimport { resolveEntityFormFields } from './use-entity-form-fields.js';\n\n/**\n * The member types a `linkSources` entry can actually produce an editor for.\n *\n * `link` is the obvious one. `string` is here because a foreign key does not\n * have to be a typed relation: when the target lives in **another slice's\n * store**, an `EntityLink` would be an illegal edge and a storage-layer join\n * across a store boundary, so the member is a plain id and the relation exists\n * only in the operator's head — a cross-store reference is an id, never a\n * link. Nothing about the editor cares which of the two it is —\n * `EntityLinkInput` writes `String(target.id)` into the draft either way, and\n * the *only* place `link`-ness still matters is `applyEntityLinks` at submit,\n * which skips a non-`link` descriptor and so leaves the id as the truth.\n *\n * Everything else is excluded on purpose rather than by omission: a `boolean`,\n * `enum`, `number` or `date` member cannot name another record, so a source\n * pointed at one is a wiring mistake — see {@link assertLinkSourcesAreEditable}.\n */\nconst PICKABLE_TYPES: ReadonlySet<MetaAccessorType> = new Set<MetaAccessorType>(\n ['link', 'string'],\n);\n\n/**\n * A form that builds itself from an entity's metadata: fields, labels and value\n * formatting all come from `@accessor()` declarations, so editing a new entity\n * needs no bespoke form. The read/edit toggle is the whole point — the same\n * component shows a record as text or as inputs.\n *\n * Two things layer on top of that default:\n * - **modes** — `read` renders each member through `CellValue`; `edit` renders\n * the matching `FieldControl`. A member in {@link PICKABLE_TYPES} with an\n * entry in `linkSources` gets the full picker instead — a to-one `link`, or a\n * `string` holding a foreign key into another slice's store. Without a source,\n * a relation stays read-only, and a `linkCollection` always does, the to-many\n * editor being a follow-up. A source aimed at a member that can render no\n * picker is a wiring mistake rather than a no-op, and throws.\n * - **slots** — `<EntityField>` children override one field's label, control or\n * read display, or add a computed field.\n *\n * It is presentational: the draft, its errors and the save/delete actions are\n * all props, so the same form hosts on a plain route and inside a workspace tab.\n */\n/**\n * How many verbs a surface shows before the rest fold into an overflow menu.\n *\n * Four, because that is roughly what fits beside a title at a laptop width\n * without the row wrapping — the same number Fiori's object-page header settles\n * on. It is a layout constant, not a policy: raising it does not grant anything.\n */\nconst VISIBLE_ACTION_LIMIT = 4;\n\n/** A verb waiting on its confirmation — narrowed so `confirm` is not optional. */\ninterface PendingConfirmation {\n key: string;\n labelKey: string;\n confirm: NonNullable<UseCaseDescriptor['confirm']>;\n}\n\nexport function EntityForm<TEntity extends Entity>({\n entityConstructor,\n entity,\n values,\n onFieldChange,\n linkSources,\n onLinkChange,\n mode: modeProp,\n defaultMode,\n onModeChange,\n errors,\n formError,\n onSubmit,\n onDelete,\n embedded = false,\n isLoading = false,\n skeleton = true,\n isSaving = false,\n isDeleting = false,\n error,\n backHref,\n renderLink = renderAnchor,\n title,\n metadata,\n isMetadataLoading = false,\n onUseCase,\n onClone,\n children,\n}: EntityFormProps<TEntity>) {\n const formId = useId();\n const slots = readEntityFormFields<TEntity>(children);\n // The *field* descriptors, which stay local and synchronous — a column is a\n // property of the class every caller holds. Only an action's availability is\n // a property of the caller, which is why that half arrives as `metadata`.\n const columnDescriptors = describeEntityColumns(\n entityConstructor,\n entity,\n ) as Array<EntityFormField<TEntity>>;\n // Before slots resolve, so an `<EntityField label>` override still wins.\n const described = useLocalizedDescriptors(columnDescriptors);\n const resolved = resolveEntityFormFields(described, slots.fields);\n assertLinkSourcesAreEditable(resolved, linkSources);\n\n // **Form above, table below**, and it is a partition rather than an ordering\n // hint. A grid rendered between two labelled inputs reads as a field, which\n // it is not — it is a second record list — so every owned collection is\n // pulled out of the field stack whatever `order` it declared, and rendered\n // full width beneath it. A composition that named no child has nothing to\n // render and stays where it was, showing its row count as before.\n const fields = resolved.filter(field => !isOwnedCollection(field));\n const ownedCollections = resolved.filter(isOwnedCollection);\n\n /**\n * `true`/omitted → the built-in default, which draws one label+control pair\n * per resolved field; a node → that node; `false` → nothing. The field list\n * comes from the constructor's metadata, not from the record, so it is already\n * complete while the record is still in flight — which is what lets the\n * placeholder match the form it is standing in for.\n */\n const customSkeleton = typeof skeleton === 'boolean' ? undefined : skeleton;\n const showSkeleton = isLoading && skeleton !== false;\n\n const [internalMode, setInternalMode] = useState<EntityFormMode>(\n defaultMode ?? (entity ? 'read' : 'edit'),\n );\n const mode = modeProp ?? internalMode;\n const editing = mode === 'edit';\n\n const draft: EntityDraft = values ?? {};\n const setField = (name: string, value: EntityDraftValue) =>\n onFieldChange?.(name, value);\n\n const toggleMode = () => {\n const next: EntityFormMode = editing ? 'read' : 'edit';\n setInternalMode(next);\n onModeChange?.(next);\n };\n\n const t = useT();\n const translateKey = useTranslateKey();\n const errorMessage = useErrorMessage();\n const busy = isSaving || isDeleting;\n\n // A draft restored from storage holds relation ids and no instances, so every\n // picker is resolving its target for a moment after mount. Saving inside that\n // window is exactly the case `applyEntityLinks` refuses: an `embedded` member\n // with an id and nothing to inline. Gating the save is the honest answer —\n // the form is still assembling itself, and the wait is one lookup long.\n //\n // Deliberately not narrowed to `embedded` members: a descriptor's\n // serialization is not what makes an in-flight lookup a bad moment to submit,\n // and a form that saves half-resolved is confusing whichever shape it writes.\n const resolvingLinks = Object.values(linkSources ?? {}).some(\n source => source.selected.isLoading,\n );\n\n // Absent metadata keeps the pre-ADR-0026 behaviour, so an un-migrated call\n // site renders exactly as before. Present metadata is authoritative: it is\n // what the service already decided this caller may do.\n const may = (action: EntityAction) =>\n metadata === undefined || metadata.actions.includes(action);\n\n // A single-record form shows the verbs that act on *this* record. Which cell\n // lands where is `action-surfaces`' business and nobody else's — a\n // `context-dependent` entity verb belongs to a row's overflow menu and a\n // `collection`-bound one to the bulk bar, and resolving that here as well is\n // how the two surfaces drifted apart in the first place.\n //\n // ⚠️ **Only when there is somewhere for them to go.** Every cell these two\n // surfaces render is `binding: 'entity'`, so without an `onUseCase` the\n // button has no handler and clicking it does nothing at all. That is not a\n // hypothetical: `makeEntityCrud` withholds the handler on a create, because\n // there is no record for a verb to act on — and Publish and Unpublish\n // rendered on a new `ProductOffering` and were inert, which reads as a broken\n // feature rather than an inapplicable one.\n //\n // The hooks are called unconditionally and their *results* are gated: React's\n // hook count must stay fixed across renders.\n const declaredHeaderUseCases = useCasesForSurface(\n 'form-header',\n metadata?.useCases,\n );\n const declaredFooterUseCases = useCasesForSurface(\n 'form-footer',\n metadata?.useCases,\n );\n const headerUseCases = onUseCase ? declaredHeaderUseCases : [];\n const footerUseCases = onUseCase ? declaredFooterUseCases : [];\n\n // A descriptor carrying `confirm` must be asked about before it fires;\n // `revoke-sessions` ends every session a user holds. The state holds the\n // confirmation itself rather than the descriptor, so the dialog cannot be\n // opened for a verb that never asked for one.\n const [pending, setPending] = useState<PendingConfirmation | undefined>(\n undefined,\n );\n const invoke = (descriptor: UseCaseDescriptor) => {\n const confirm = descriptor.confirm;\n if (confirm) {\n setPending({\n key: descriptor.key,\n labelKey: descriptor.labelKey,\n confirm,\n });\n return;\n }\n onUseCase?.(descriptor.key);\n };\n\n const useCaseButton = (descriptor: UseCaseDescriptor) => (\n <Button\n key={descriptor.key}\n type=\"button\"\n variant={\n descriptor.confirm?.tone === 'destructive' ? 'destructive' : 'secondary'\n }\n size={descriptor.placement === 'context-independent' ? 'sm' : 'md'}\n disabled={busy}\n onClick={() => invoke(descriptor)}\n >\n {/* A descriptor's labels are *runtime* catalog keys — they never resolve\n at compile time, which is why a host's catalog check is what catches a\n typo here rather than the type system. */}\n {translateKey(descriptor.labelKey)}\n </Button>\n );\n\n const useCaseMenuItem = (descriptor: UseCaseDescriptor) => (\n <Menu.Item\n key={descriptor.key}\n tone={\n descriptor.confirm?.tone === 'destructive' ? 'destructive' : 'neutral'\n }\n disabled={busy}\n onClick={() => invoke(descriptor)}\n >\n {translateKey(descriptor.labelKey)}\n </Menu.Item>\n );\n\n /**\n * Four actions fit a row; twelve do not.\n *\n * So a surface renders its first {@link VISIBLE_ACTION_LIMIT} verbs as\n * buttons and folds the rest behind one overflow trigger, in **declaration\n * order** — the entity's author decided which verbs matter by writing them\n * first, and a rule that sorted by anything else (alphabetical, destructive\n * last) would reorder the row every time a verb was added.\n *\n * The split is only applied when there is something to fold: a fifth action\n * is the point at which a menu is cheaper than a wider row, and a menu\n * holding one entry is strictly worse than the button it replaced.\n */\n const withOverflow = (descriptors: UseCaseDescriptor[]) => {\n if (descriptors.length <= VISIBLE_ACTION_LIMIT) {\n return descriptors.map(useCaseButton);\n }\n\n const visible = descriptors.slice(0, VISIBLE_ACTION_LIMIT);\n const folded = descriptors.slice(VISIBLE_ACTION_LIMIT);\n\n return [\n ...visible.map(useCaseButton),\n <Menu key=\"overflow\">\n <Menu.Trigger aria-label={t('form.moreActions')}>\n {OVERFLOW_GLYPH}\n </Menu.Trigger>\n <Menu.Items>{folded.map(useCaseMenuItem)}</Menu.Items>\n </Menu>,\n ];\n };\n\n return (\n <Card>\n <Stack gap=\"s\">\n {/* An embedded form brings only its fields: the heading and the action\n row belong to whatever is hosting it — a wizard step sits under the\n wizard's own heading and advances with the wizard's own footer, so\n rendering a second of each is two controls for one job. */}\n {!embedded && (\n <Stack direction=\"row\" gap=\"xs\" align=\"center\">\n <Text as=\"h2\" step={1} weight=\"semibold\">\n {/* `entity` is undefined while the record is still in flight, so\n testing it alone made a loading edit form announce itself as\n \"New\" for as long as the fetch took, then relabel when the\n record landed. A form that is loading is never a create. */}\n {title ??\n (entity || isLoading ? t('form.details') : t('form.new'))}\n </Text>\n {/* The built-in toggle only appears when the form owns its mode and\n there is a record to view — a create form has nothing to read. */}\n {modeProp === undefined && entity && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={toggleMode}\n >\n {editing ? t('form.view') : t('form.edit')}\n </Button>\n )}\n <LoadingBoundary isLoading={isMetadataLoading} lines={0}>\n <>\n {withOverflow(headerUseCases)}\n {slots.headerActions}\n </>\n </LoadingBoundary>\n </Stack>\n )}\n\n {/* One announcement for the form; the shimmer below is aria-hidden. */}\n <span role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {isLoading ? t('form.loading') : ''}\n </span>\n\n {error && (\n <p\n role=\"alert\"\n data-testid=\"entity-form-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 {/* The placeholder *replaces* the rows rather than sitting above them.\n Rendering both meant a shimmer stacked on a full set of empty fields,\n which is two loading signals for one region and twice the height the\n loaded form settles at. */}\n {showSkeleton\n ? (customSkeleton ??\n fields.map(field => (\n <Stack key={field.name} gap=\"3xs\">\n <Skeleton shape=\"line\" className=\"w-24\" />\n <Skeleton className=\"h-9 w-full\" />\n </Stack>\n )))\n : fields.map(field => (\n <FieldRow\n key={field.name}\n field={field}\n entity={entity}\n draft={draft}\n value={readDraftString(draft, field.name)}\n editing={editing}\n error={editing ? errors?.[field.name] : undefined}\n setField={setField}\n linkSource={linkSources?.[field.name]}\n onLinkChange={onLinkChange}\n id={`${formId}-${field.name}`}\n />\n ))}\n\n {/* The rows the record owns, full width and below every field. Each\n grid is handed the *whole* error map and picks out the keys naming\n its own member, so a record owning two collections needs no\n bookkeeping here. */}\n {ownedCollections.map(field => (\n <EntityDetailGrid\n key={field.name}\n descriptor={field}\n rows={readRowDrafts(draft[field.name])}\n onRowsChange={rows => setField(field.name, rows)}\n errors={editing ? errors : undefined}\n editing={editing}\n isLoading={isLoading}\n skeleton={skeleton}\n />\n ))}\n\n {/* A cross-field rule has no row to sit under, so it sits with the\n actions it blocks. */}\n {editing && formError && (\n <span role=\"alert\" className=\"text-step-sm text-danger\">\n {formError}\n </span>\n )}\n\n {editing && !embedded && (\n <Stack direction=\"row\" gap=\"xs\">\n {may('write') && (\n <Button\n type=\"button\"\n onClick={() => onSubmit?.(draft)}\n disabled={busy || resolvingLinks}\n >\n {isSaving ? t('form.saving') : t('form.save')}\n </Button>\n )}\n {onDelete && may('delete') && (\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={onDelete}\n disabled={busy}\n >\n {isDeleting ? t('form.deleting') : t('form.delete')}\n </Button>\n )}\n {withOverflow(footerUseCases)}\n {/* Clone is the CRUD triple's, not a declared verb's: every entity\n can be copied, so giving it a `@useCase()` would mean declaring\n the same verb on all 28 of them. It needs `write`, because what\n it produces is a create. */}\n {onClone && entity && may('write') && (\n <Button\n type=\"button\"\n variant=\"secondary\"\n disabled={busy}\n onClick={() =>\n onClone(cloneEntityDraft(entityConstructor, draft))\n }\n >\n {t('form.clone')}\n </Button>\n )}\n {slots.footerActions}\n {/* A link styled as a button, not a button inside a link: the old\n nesting was two interactive elements for one action, and its\n `disabled` stopped nothing, because the anchor around it still\n navigated. While busy the link is inert instead. */}\n {backHref &&\n renderLink({\n href: backHref,\n className: cn(\n button({ variant: 'ghost' }),\n busy && 'pointer-events-none opacity-50',\n ),\n children: t('form.back'),\n intent: { kind: 'back' },\n })}\n </Stack>\n )}\n\n {slots.rest}\n </Stack>\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={busy}\n onCancel={() => setPending(undefined)}\n onConfirm={() => {\n const key = pending.key;\n setPending(undefined);\n onUseCase?.(key);\n }}\n />\n )}\n </Card>\n );\n}\n\n/**\n * A member the detail grid renders rather than the field stack.\n *\n * The `childType` half is not defensive: a `composition` that declared no child\n * has no columns, so there is nothing for a grid to draw. It keeps its place in\n * the field stack and reads as its row count, which is what it did before this\n * control existed — an honest \"some rows\" rather than an empty table implying\n * there are none.\n */\nfunction isOwnedCollection<TEntity extends Entity>(\n field: EntityFormField<TEntity>,\n): boolean {\n return field.type === 'composition' && field.childType !== undefined;\n}\n\n/**\n * Refuse a `linkSources` entry that names a member no picker can be built for.\n *\n * `FieldRow` asks for {@link PICKABLE_TYPES} before it builds an editor, so a\n * source handed to anything else used to fall through to the read-only display\n * or a plain `FieldControl` with nothing said: the caller wired a picker, no\n * picker appeared, and the field was indistinguishable from one the entity had\n * declared read-only. That is a wiring mistake rather than a state — the entry\n * is dead in *every* shape such a field can render, since an\n * `<EntityField render>` slot is handed the draft and never the source — so it\n * costs the render instead of a warning nobody reads. `useEntityLinkSource`\n * already throws at render for the sibling mistake (a search\n * property the target does not declare `filterable`); this is the same fault\n * caught one level up.\n *\n * Two messages, because the two faults are not the same thing. A\n * `linkCollection` is a member the editor has simply not been built for yet\n * (#26), so the fix is to wait or to point at the to-one you meant. Any other\n * type is a member that can never name another record at all, so the fix is to\n * remove the entry.\n *\n * Here rather than inside `FieldRow`, and that is the whole reason it is a\n * separate pass: a row only reaches its editor branch in **edit** mode, so a\n * per-row check would stay silent on a form that opens in `read` — the default\n * whenever there is a record — and then throw on a click, minutes later. One\n * pass over the registry fires on the first render either way.\n *\n * Against the resolved fields rather than the raw metadata: a slot may drop a\n * member, and a source left behind for a field that no longer renders is not\n * worth a page.\n */\nfunction assertLinkSourcesAreEditable<TEntity extends Entity>(\n fields: Array<EntityFormField<TEntity>>,\n // Same reason as the prop's own declaration: a source's target is another\n // entity entirely and cannot be expressed in terms of `TEntity`.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n linkSources: Record<string, EntityLinkSource<any>> | undefined,\n): void {\n if (linkSources === undefined) return;\n\n const sourced = fields.filter(\n field =>\n linkSources[field.name] !== undefined && !PICKABLE_TYPES.has(field.type),\n );\n if (sourced.length === 0) return;\n\n const toMany = sourced\n .filter(field => field.type === 'linkCollection')\n .map(field => field.name);\n if (toMany.length > 0) {\n throw new Error(\n `EntityForm was given a link source for a to-many member: ${toMany.join(', ')}. ` +\n 'A `linkCollection` has no editor yet, so the source can only be dropped ' +\n 'and the field would render read-only with no sign anything was wired. ' +\n 'Remove the entry, or point it at the to-one `link` you meant.',\n );\n }\n\n const owned = sourced\n .filter(field => field.type === 'composition')\n .map(field => field.name);\n if (owned.length > 0) {\n throw new Error(\n `EntityForm was given a link source for an owned collection: ${owned.join(', ')}. ` +\n 'A `composition` is not a reference — its rows have no life outside this ' +\n 'record and are never picked from existing ones, so there is nothing for a ' +\n 'picker to look up. The detail grid is what edits them.',\n );\n }\n\n const unpickable = sourced.map(field => `${field.name} (${field.type})`);\n throw new Error(\n `EntityForm was given a link source for a member that cannot hold one: ${unpickable.join(', ')}. ` +\n 'A picker writes the target’s id into the draft, so the member has to be a ' +\n '`link` or the `string` that carries a foreign key. Remove the entry, or ' +\n 'point it at the member that actually holds the reference.',\n );\n}\n\ninterface FieldRowProps<TEntity extends Entity> {\n field: EntityFormField<TEntity>;\n entity: TEntity | undefined;\n draft: EntityDraft;\n value: string;\n editing: boolean;\n error: string | undefined;\n setField: (name: string, value: EntityDraftValue) => void;\n // The target of a relation is a different entity than the form's own.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n linkSource: EntityLinkSource<any> | undefined;\n onLinkChange:\n ((name: string, entity: Entity | undefined) => void) | undefined;\n id: string;\n}\n\n/**\n * One label + control (or value) row. A `readonly` member renders its read\n * display even in edit mode, and so does a relation with no source; everything\n * else edits through `FieldControl` — or, when a source was supplied for a\n * member in {@link PICKABLE_TYPES}, through `EntityLinkInput`. A boolean edited\n * inline carries its own label, so the row does not add a second one.\n *\n * The source branch sits above the `virtual || isRelation` fallback and below\n * `field.render`, which is what lets a slot still override a picked field.\n */\nfunction FieldRow<TEntity extends Entity>({\n field,\n entity,\n draft,\n value,\n editing,\n error,\n setField,\n linkSource,\n onLinkChange,\n id,\n}: FieldRowProps<TEntity>) {\n const readNode: ReactNode = field.readRender ? (\n field.readRender(entity)\n ) : (\n <CellValue\n value={\n entity ? (entity as Record<string, unknown>)[field.name] : undefined\n }\n descriptor={field}\n />\n );\n\n // Members with no editor of their own here: a to-one `link` whose picker was\n // never wired, a `linkCollection` (#26) and a `composition`. All three\n // fall back to the read display, which is honest — a disabled text box\n // holding `[object Object]` is not.\n const isRelation =\n field.type === 'link' ||\n field.type === 'linkCollection' ||\n field.type === 'composition';\n\n let control: ReactNode;\n if (!editing) {\n control = readNode;\n } else if (field.render) {\n control = field.render({ draft, value, setField, id });\n } else if (linkSource && PICKABLE_TYPES.has(field.type)) {\n control = (\n <EntityLinkInput\n descriptor={field}\n value={value}\n source={linkSource}\n id={id}\n disabled={field.readonly}\n onSelect={target => {\n setField(field.name, target.id == null ? '' : String(target.id));\n onLinkChange?.(field.name, target);\n }}\n onClear={() => {\n setField(field.name, '');\n onLinkChange?.(field.name, undefined);\n }}\n />\n );\n } else if (field.type === 'localized' && !field.virtual) {\n control = (\n <LocalizedFieldControl\n descriptor={field}\n value={readLocalizedDraft(draft, field.name)}\n onChange={next => setField(field.name, next)}\n id={id}\n />\n );\n } else if (field.virtual || isRelation) {\n // A relation with no source has no input to offer, and a virtual field has\n // no member to edit unless a `render` was supplied — both fall back to the\n // read display.\n control = readNode;\n } else {\n control = (\n <FieldControl\n descriptor={field}\n value={value}\n onChange={next => setField(field.name, next)}\n id={id}\n />\n );\n }\n\n const ownsLabel =\n editing && field.type === 'boolean' && !field.render && !field.virtual;\n\n return (\n <Stack gap=\"3xs\">\n {!ownsLabel && (\n <label htmlFor={id} className=\"text-step-sm text-content-muted\">\n {field.label}\n </label>\n )}\n {control}\n {error && (\n <span role=\"alert\" className=\"text-step-sm text-danger\">\n {error}\n </span>\n )}\n </Stack>\n );\n}\n"],"names":["cloneEntityDraft","describeEntityColumns","readDraftString","readLocalizedDraft","readRowDrafts","useId","useState","useErrorMessage","useLocalizedDescriptors","useT","useTranslateKey","OVERFLOW_GLYPH","renderAnchor","useCasesForSurface","Button","button","CellValue","FieldControl","LocalizedFieldControl","Skeleton","Text","Card","ConfirmDialog","EntityLinkInput","LoadingBoundary","Menu","Stack","cn","EntityDetailGrid","readEntityFormFields","resolveEntityFormFields","PICKABLE_TYPES","Set","VISIBLE_ACTION_LIMIT","EntityForm","entityConstructor","entity","values","onFieldChange","linkSources","onLinkChange","mode","modeProp","defaultMode","onModeChange","errors","formError","onSubmit","onDelete","embedded","isLoading","skeleton","isSaving","isDeleting","error","backHref","renderLink","title","metadata","isMetadataLoading","onUseCase","onClone","children","formId","slots","columnDescriptors","described","resolved","fields","assertLinkSourcesAreEditable","filter","field","isOwnedCollection","ownedCollections","customSkeleton","undefined","showSkeleton","internalMode","setInternalMode","editing","draft","setField","name","value","toggleMode","next","t","translateKey","errorMessage","busy","resolvingLinks","Object","some","source","selected","may","action","actions","includes","declaredHeaderUseCases","useCases","declaredFooterUseCases","headerUseCases","footerUseCases","pending","setPending","invoke","descriptor","confirm","key","labelKey","useCaseButton","type","variant","tone","size","placement","disabled","onClick","useCaseMenuItem","Item","withOverflow","descriptors","length","map","visible","slice","folded","Trigger","aria-label","Items","gap","direction","align","as","step","weight","lines","headerActions","span","role","aria-live","className","p","data-testid","shape","FieldRow","linkSource","id","rows","onRowsChange","footerActions","href","intent","kind","rest","open","message","messageKey","onCancel","onConfirm","childType","sourced","has","toMany","Error","join","owned","unpickable","readNode","readRender","isRelation","control","render","readonly","onSelect","target","String","onClear","virtual","onChange","ownsLabel","label","htmlFor"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/ui/organisms/entity-form/entity-form.tsx"],"sourcesContent":["'use client';\n\nimport {\n cloneEntityDraft,\n describeEntityColumns,\n type Entity,\n type EntityAction,\n type EntityDraft,\n type EntityDraftValue,\n type EntityLinkSource,\n type MetaAccessorType,\n readDraftString,\n readLocalizedDraft,\n readRowDrafts,\n type UseCaseDescriptor,\n} from '@entifix/core';\nimport { type ReactNode, useId, useState } from 'react';\n\nimport {\n useErrorMessage,\n useLocalizedDescriptors,\n useT,\n useTranslateKey,\n} from '../../../i18n/index.js';\nimport {\n OVERFLOW_GLYPH,\n renderAnchor,\n useCasesForSurface,\n} from '../../actions/index.js';\nimport { Button, button } from '../../atoms/button/index.js';\nimport { CellValue } from '../../atoms/cell-value/index.js';\nimport { FieldControl } from '../../atoms/field-control/index.js';\nimport { LocalizedFieldControl } from '../../atoms/localized-field-control/index.js';\nimport { Skeleton } from '../../atoms/skeleton/index.js';\nimport { Text } from '../../atoms/text/index.js';\nimport { Card } from '../../molecules/card/index.js';\nimport { ConfirmDialog } from '../../molecules/confirm-dialog/index.js';\nimport { EntityLinkInput } from '../../molecules/entity-link-input/index.js';\nimport { LoadingBoundary } from '../../molecules/loading-boundary/index.js';\nimport { Menu } from '../../molecules/menu/index.js';\nimport { Stack } from '../../molecules/stack/index.js';\nimport { cn } from '../../utils/cn.js';\nimport { EntityDetailGrid } from '../entity-detail-grid/index.js';\nimport type {\n EntityFormField,\n EntityFormMode,\n EntityFormProps,\n} from './entity-form.types.js';\nimport { readEntityFormFields } from './entity-form-slots.js';\nimport { resolveEntityFormFields } from './use-entity-form-fields.js';\n\n/**\n * The member types a `linkSources` entry can actually produce an editor for.\n *\n * `link` is the obvious one. `string` is here because a foreign key does not\n * have to be a typed relation: when the target lives in **another slice's\n * store**, an `EntityLink` would be an illegal edge and a storage-layer join\n * across a store boundary, so the member is a plain id and the relation exists\n * only in the operator's head — a cross-store reference is an id, never a\n * link. Nothing about the editor cares which of the two it is —\n * `EntityLinkInput` writes `String(target.id)` into the draft either way, and\n * the *only* place `link`-ness still matters is `applyEntityLinks` at submit,\n * which skips a non-`link` descriptor and so leaves the id as the truth.\n *\n * Everything else is excluded on purpose rather than by omission: a `boolean`,\n * `enum`, `number` or `date` member cannot name another record, so a source\n * pointed at one is a wiring mistake — see {@link assertLinkSourcesAreEditable}.\n */\nconst PICKABLE_TYPES: ReadonlySet<MetaAccessorType> = new Set<MetaAccessorType>(\n ['link', 'string'],\n);\n\n/**\n * A form that builds itself from an entity's metadata: fields, labels and value\n * formatting all come from `@accessor()` declarations, so editing a new entity\n * needs no bespoke form. The read/edit toggle is the whole point — the same\n * component shows a record as text or as inputs.\n *\n * Two things layer on top of that default:\n * - **modes** — `read` renders each member through `CellValue`; `edit` renders\n * the matching `FieldControl`. A member in {@link PICKABLE_TYPES} with an\n * entry in `linkSources` gets the full picker instead — a to-one `link`, or a\n * `string` holding a foreign key into another slice's store. Without a source,\n * a relation stays read-only, and a `linkCollection` always does, the to-many\n * editor being a follow-up. A source aimed at a member that can render no\n * picker is a wiring mistake rather than a no-op, and throws.\n * - **slots** — `<EntityField>` children override one field's label, control or\n * read display, or add a computed field.\n *\n * It is presentational: the draft, its errors and the save/delete actions are\n * all props, so the same form hosts on a plain route and inside a workspace tab.\n */\n/**\n * How many verbs a surface shows before the rest fold into an overflow menu.\n *\n * Four, because that is roughly what fits beside a title at a laptop width\n * without the row wrapping — the same number Fiori's object-page header settles\n * on. It is a layout constant, not a policy: raising it does not grant anything.\n */\nconst VISIBLE_ACTION_LIMIT = 4;\n\n/** A verb waiting on its confirmation — narrowed so `confirm` is not optional. */\ninterface PendingConfirmation {\n key: string;\n labelKey: string;\n confirm: NonNullable<UseCaseDescriptor['confirm']>;\n}\n\nexport function EntityForm<TEntity extends Entity>({\n entityConstructor,\n entity,\n values,\n onFieldChange,\n linkSources,\n onLinkChange,\n mode: modeProp,\n defaultMode,\n onModeChange,\n errors,\n formError,\n onSubmit,\n onDelete,\n embedded = false,\n isLoading = false,\n skeleton = true,\n isSaving = false,\n isPending = false,\n isDeleting = false,\n error,\n backHref,\n renderLink = renderAnchor,\n title,\n metadata,\n isMetadataLoading = false,\n onUseCase,\n onClone,\n children,\n}: EntityFormProps<TEntity>) {\n const formId = useId();\n const slots = readEntityFormFields<TEntity>(children);\n // The *field* descriptors, which stay local and synchronous — a column is a\n // property of the class every caller holds. Only an action's availability is\n // a property of the caller, which is why that half arrives as `metadata`.\n const columnDescriptors = describeEntityColumns(\n entityConstructor,\n entity,\n ) as Array<EntityFormField<TEntity>>;\n // Before slots resolve, so an `<EntityField label>` override still wins.\n const described = useLocalizedDescriptors(columnDescriptors);\n const resolved = resolveEntityFormFields(described, slots.fields);\n assertLinkSourcesAreEditable(resolved, linkSources);\n\n // **Form above, table below**, and it is a partition rather than an ordering\n // hint. A grid rendered between two labelled inputs reads as a field, which\n // it is not — it is a second record list — so every owned collection is\n // pulled out of the field stack whatever `order` it declared, and rendered\n // full width beneath it. A composition that named no child has nothing to\n // render and stays where it was, showing its row count as before.\n const fields = resolved.filter(field => !isOwnedCollection(field));\n const ownedCollections = resolved.filter(isOwnedCollection);\n\n /**\n * `true`/omitted → the built-in default, which draws one label+control pair\n * per resolved field; a node → that node; `false` → nothing. The field list\n * comes from the constructor's metadata, not from the record, so it is already\n * complete while the record is still in flight — which is what lets the\n * placeholder match the form it is standing in for.\n */\n const customSkeleton = typeof skeleton === 'boolean' ? undefined : skeleton;\n const showSkeleton = isLoading && skeleton !== false;\n\n const [internalMode, setInternalMode] = useState<EntityFormMode>(\n defaultMode ?? (entity ? 'read' : 'edit'),\n );\n const mode = modeProp ?? internalMode;\n const editing = mode === 'edit';\n\n const draft: EntityDraft = values ?? {};\n const setField = (name: string, value: EntityDraftValue) =>\n onFieldChange?.(name, value);\n\n const toggleMode = () => {\n const next: EntityFormMode = editing ? 'read' : 'edit';\n setInternalMode(next);\n onModeChange?.(next);\n };\n\n const t = useT();\n const translateKey = useTranslateKey();\n const errorMessage = useErrorMessage();\n const busy = isSaving || isPending || isDeleting;\n\n // A draft restored from storage holds relation ids and no instances, so every\n // picker is resolving its target for a moment after mount. Saving inside that\n // window is exactly the case `applyEntityLinks` refuses: an `embedded` member\n // with an id and nothing to inline. Gating the save is the honest answer —\n // the form is still assembling itself, and the wait is one lookup long.\n //\n // Deliberately not narrowed to `embedded` members: a descriptor's\n // serialization is not what makes an in-flight lookup a bad moment to submit,\n // and a form that saves half-resolved is confusing whichever shape it writes.\n const resolvingLinks = Object.values(linkSources ?? {}).some(\n source => source.selected.isLoading,\n );\n\n // Absent metadata keeps the pre-ADR-0026 behaviour, so an un-migrated call\n // site renders exactly as before. Present metadata is authoritative: it is\n // what the service already decided this caller may do.\n const may = (action: EntityAction) =>\n metadata === undefined || metadata.actions.includes(action);\n\n // A single-record form shows the verbs that act on *this* record. Which cell\n // lands where is `action-surfaces`' business and nobody else's — a\n // `context-dependent` entity verb belongs to a row's overflow menu and a\n // `collection`-bound one to the bulk bar, and resolving that here as well is\n // how the two surfaces drifted apart in the first place.\n //\n // ⚠️ **Only when there is somewhere for them to go.** Every cell these two\n // surfaces render is `binding: 'entity'`, so without an `onUseCase` the\n // button has no handler and clicking it does nothing at all. That is not a\n // hypothetical: `makeEntityCrud` withholds the handler on a create, because\n // there is no record for a verb to act on — and Publish and Unpublish\n // rendered on a new `ProductOffering` and were inert, which reads as a broken\n // feature rather than an inapplicable one.\n //\n // The hooks are called unconditionally and their *results* are gated: React's\n // hook count must stay fixed across renders.\n const declaredHeaderUseCases = useCasesForSurface(\n 'form-header',\n metadata?.useCases,\n );\n const declaredFooterUseCases = useCasesForSurface(\n 'form-footer',\n metadata?.useCases,\n );\n const headerUseCases = onUseCase ? declaredHeaderUseCases : [];\n const footerUseCases = onUseCase ? declaredFooterUseCases : [];\n\n // A descriptor carrying `confirm` must be asked about before it fires;\n // `revoke-sessions` ends every session a user holds. The state holds the\n // confirmation itself rather than the descriptor, so the dialog cannot be\n // opened for a verb that never asked for one.\n const [pending, setPending] = useState<PendingConfirmation | undefined>(\n undefined,\n );\n const invoke = (descriptor: UseCaseDescriptor) => {\n const confirm = descriptor.confirm;\n if (confirm) {\n setPending({\n key: descriptor.key,\n labelKey: descriptor.labelKey,\n confirm,\n });\n return;\n }\n onUseCase?.(descriptor.key);\n };\n\n const useCaseButton = (descriptor: UseCaseDescriptor) => (\n <Button\n key={descriptor.key}\n type=\"button\"\n variant={\n descriptor.confirm?.tone === 'destructive' ? 'destructive' : 'secondary'\n }\n size={descriptor.placement === 'context-independent' ? 'sm' : 'md'}\n disabled={busy}\n onClick={() => invoke(descriptor)}\n >\n {/* A descriptor's labels are *runtime* catalog keys — they never resolve\n at compile time, which is why a host's catalog check is what catches a\n typo here rather than the type system. */}\n {translateKey(descriptor.labelKey)}\n </Button>\n );\n\n const useCaseMenuItem = (descriptor: UseCaseDescriptor) => (\n <Menu.Item\n key={descriptor.key}\n tone={\n descriptor.confirm?.tone === 'destructive' ? 'destructive' : 'neutral'\n }\n disabled={busy}\n onClick={() => invoke(descriptor)}\n >\n {translateKey(descriptor.labelKey)}\n </Menu.Item>\n );\n\n /**\n * Four actions fit a row; twelve do not.\n *\n * So a surface renders its first {@link VISIBLE_ACTION_LIMIT} verbs as\n * buttons and folds the rest behind one overflow trigger, in **declaration\n * order** — the entity's author decided which verbs matter by writing them\n * first, and a rule that sorted by anything else (alphabetical, destructive\n * last) would reorder the row every time a verb was added.\n *\n * The split is only applied when there is something to fold: a fifth action\n * is the point at which a menu is cheaper than a wider row, and a menu\n * holding one entry is strictly worse than the button it replaced.\n */\n const withOverflow = (descriptors: UseCaseDescriptor[]) => {\n if (descriptors.length <= VISIBLE_ACTION_LIMIT) {\n return descriptors.map(useCaseButton);\n }\n\n const visible = descriptors.slice(0, VISIBLE_ACTION_LIMIT);\n const folded = descriptors.slice(VISIBLE_ACTION_LIMIT);\n\n return [\n ...visible.map(useCaseButton),\n <Menu key=\"overflow\">\n <Menu.Trigger aria-label={t('form.moreActions')}>\n {OVERFLOW_GLYPH}\n </Menu.Trigger>\n <Menu.Items>{folded.map(useCaseMenuItem)}</Menu.Items>\n </Menu>,\n ];\n };\n\n return (\n <Card>\n <Stack gap=\"s\">\n {/* An embedded form brings only its fields: the heading and the action\n row belong to whatever is hosting it — a wizard step sits under the\n wizard's own heading and advances with the wizard's own footer, so\n rendering a second of each is two controls for one job. */}\n {!embedded && (\n <Stack direction=\"row\" gap=\"xs\" align=\"center\">\n <Text as=\"h2\" step={1} weight=\"semibold\">\n {/* `entity` is undefined while the record is still in flight, so\n testing it alone made a loading edit form announce itself as\n \"New\" for as long as the fetch took, then relabel when the\n record landed. A form that is loading is never a create. */}\n {title ??\n (entity || isLoading ? t('form.details') : t('form.new'))}\n </Text>\n {/* The built-in toggle only appears when the form owns its mode and\n there is a record to view — a create form has nothing to read. */}\n {modeProp === undefined && entity && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={toggleMode}\n >\n {editing ? t('form.view') : t('form.edit')}\n </Button>\n )}\n <LoadingBoundary isLoading={isMetadataLoading} lines={0}>\n <>\n {withOverflow(headerUseCases)}\n {slots.headerActions}\n </>\n </LoadingBoundary>\n </Stack>\n )}\n\n {/* One announcement for the form; the shimmer below is aria-hidden. */}\n <span role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {isLoading ? t('form.loading') : ''}\n </span>\n\n {error && (\n <p\n role=\"alert\"\n data-testid=\"entity-form-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 {/* The placeholder *replaces* the rows rather than sitting above them.\n Rendering both meant a shimmer stacked on a full set of empty fields,\n which is two loading signals for one region and twice the height the\n loaded form settles at. */}\n {showSkeleton\n ? (customSkeleton ??\n fields.map(field => (\n <Stack key={field.name} gap=\"3xs\">\n <Skeleton shape=\"line\" className=\"w-24\" />\n <Skeleton className=\"h-9 w-full\" />\n </Stack>\n )))\n : fields.map(field => (\n <FieldRow\n key={field.name}\n field={field}\n entity={entity}\n draft={draft}\n value={readDraftString(draft, field.name)}\n editing={editing}\n error={editing ? errors?.[field.name] : undefined}\n setField={setField}\n linkSource={linkSources?.[field.name]}\n onLinkChange={onLinkChange}\n id={`${formId}-${field.name}`}\n />\n ))}\n\n {/* The rows the record owns, full width and below every field. Each\n grid is handed the *whole* error map and picks out the keys naming\n its own member, so a record owning two collections needs no\n bookkeeping here. */}\n {ownedCollections.map(field => (\n <EntityDetailGrid\n key={field.name}\n descriptor={field}\n rows={readRowDrafts(draft[field.name])}\n onRowsChange={rows => setField(field.name, rows)}\n errors={editing ? errors : undefined}\n editing={editing}\n isLoading={isLoading}\n skeleton={skeleton}\n />\n ))}\n\n {/* A cross-field rule has no row to sit under, so it sits with the\n actions it blocks. */}\n {editing && formError && (\n <span role=\"alert\" className=\"text-step-sm text-danger\">\n {formError}\n </span>\n )}\n\n {editing && !embedded && (\n <Stack direction=\"row\" gap=\"xs\">\n {may('write') && (\n <Button\n type=\"button\"\n onClick={() => onSubmit?.(draft)}\n disabled={busy || resolvingLinks}\n >\n {isPending\n ? t('form.pending')\n : isSaving\n ? t('form.saving')\n : t('form.save')}\n </Button>\n )}\n {onDelete && may('delete') && (\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={onDelete}\n disabled={busy}\n >\n {isDeleting ? t('form.deleting') : t('form.delete')}\n </Button>\n )}\n {withOverflow(footerUseCases)}\n {/* Clone is the CRUD triple's, not a declared verb's: every entity\n can be copied, so giving it a `@useCase()` would mean declaring\n the same verb on all 28 of them. It needs `write`, because what\n it produces is a create. */}\n {onClone && entity && may('write') && (\n <Button\n type=\"button\"\n variant=\"secondary\"\n disabled={busy}\n onClick={() =>\n onClone(cloneEntityDraft(entityConstructor, draft))\n }\n >\n {t('form.clone')}\n </Button>\n )}\n {slots.footerActions}\n {/* A link styled as a button, not a button inside a link: the old\n nesting was two interactive elements for one action, and its\n `disabled` stopped nothing, because the anchor around it still\n navigated. While busy the link is inert instead. */}\n {backHref &&\n renderLink({\n href: backHref,\n className: cn(\n button({ variant: 'ghost' }),\n busy && 'pointer-events-none opacity-50',\n ),\n children: t('form.back'),\n intent: { kind: 'back' },\n })}\n </Stack>\n )}\n\n {slots.rest}\n </Stack>\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={busy}\n onCancel={() => setPending(undefined)}\n onConfirm={() => {\n const key = pending.key;\n setPending(undefined);\n onUseCase?.(key);\n }}\n />\n )}\n </Card>\n );\n}\n\n/**\n * A member the detail grid renders rather than the field stack.\n *\n * The `childType` half is not defensive: a `composition` that declared no child\n * has no columns, so there is nothing for a grid to draw. It keeps its place in\n * the field stack and reads as its row count, which is what it did before this\n * control existed — an honest \"some rows\" rather than an empty table implying\n * there are none.\n */\nfunction isOwnedCollection<TEntity extends Entity>(\n field: EntityFormField<TEntity>,\n): boolean {\n return field.type === 'composition' && field.childType !== undefined;\n}\n\n/**\n * Refuse a `linkSources` entry that names a member no picker can be built for.\n *\n * `FieldRow` asks for {@link PICKABLE_TYPES} before it builds an editor, so a\n * source handed to anything else used to fall through to the read-only display\n * or a plain `FieldControl` with nothing said: the caller wired a picker, no\n * picker appeared, and the field was indistinguishable from one the entity had\n * declared read-only. That is a wiring mistake rather than a state — the entry\n * is dead in *every* shape such a field can render, since an\n * `<EntityField render>` slot is handed the draft and never the source — so it\n * costs the render instead of a warning nobody reads. `useEntityLinkSource`\n * already throws at render for the sibling mistake (a search\n * property the target does not declare `filterable`); this is the same fault\n * caught one level up.\n *\n * Two messages, because the two faults are not the same thing. A\n * `linkCollection` is a member the editor has simply not been built for yet\n * (#26), so the fix is to wait or to point at the to-one you meant. Any other\n * type is a member that can never name another record at all, so the fix is to\n * remove the entry.\n *\n * Here rather than inside `FieldRow`, and that is the whole reason it is a\n * separate pass: a row only reaches its editor branch in **edit** mode, so a\n * per-row check would stay silent on a form that opens in `read` — the default\n * whenever there is a record — and then throw on a click, minutes later. One\n * pass over the registry fires on the first render either way.\n *\n * Against the resolved fields rather than the raw metadata: a slot may drop a\n * member, and a source left behind for a field that no longer renders is not\n * worth a page.\n */\nfunction assertLinkSourcesAreEditable<TEntity extends Entity>(\n fields: Array<EntityFormField<TEntity>>,\n // Same reason as the prop's own declaration: a source's target is another\n // entity entirely and cannot be expressed in terms of `TEntity`.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n linkSources: Record<string, EntityLinkSource<any>> | undefined,\n): void {\n if (linkSources === undefined) return;\n\n const sourced = fields.filter(\n field =>\n linkSources[field.name] !== undefined && !PICKABLE_TYPES.has(field.type),\n );\n if (sourced.length === 0) return;\n\n const toMany = sourced\n .filter(field => field.type === 'linkCollection')\n .map(field => field.name);\n if (toMany.length > 0) {\n throw new Error(\n `EntityForm was given a link source for a to-many member: ${toMany.join(', ')}. ` +\n 'A `linkCollection` has no editor yet, so the source can only be dropped ' +\n 'and the field would render read-only with no sign anything was wired. ' +\n 'Remove the entry, or point it at the to-one `link` you meant.',\n );\n }\n\n const owned = sourced\n .filter(field => field.type === 'composition')\n .map(field => field.name);\n if (owned.length > 0) {\n throw new Error(\n `EntityForm was given a link source for an owned collection: ${owned.join(', ')}. ` +\n 'A `composition` is not a reference — its rows have no life outside this ' +\n 'record and are never picked from existing ones, so there is nothing for a ' +\n 'picker to look up. The detail grid is what edits them.',\n );\n }\n\n const unpickable = sourced.map(field => `${field.name} (${field.type})`);\n throw new Error(\n `EntityForm was given a link source for a member that cannot hold one: ${unpickable.join(', ')}. ` +\n 'A picker writes the target’s id into the draft, so the member has to be a ' +\n '`link` or the `string` that carries a foreign key. Remove the entry, or ' +\n 'point it at the member that actually holds the reference.',\n );\n}\n\ninterface FieldRowProps<TEntity extends Entity> {\n field: EntityFormField<TEntity>;\n entity: TEntity | undefined;\n draft: EntityDraft;\n value: string;\n editing: boolean;\n error: string | undefined;\n setField: (name: string, value: EntityDraftValue) => void;\n // The target of a relation is a different entity than the form's own.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n linkSource: EntityLinkSource<any> | undefined;\n onLinkChange:\n ((name: string, entity: Entity | undefined) => void) | undefined;\n id: string;\n}\n\n/**\n * One label + control (or value) row. A `readonly` member renders its read\n * display even in edit mode, and so does a relation with no source; everything\n * else edits through `FieldControl` — or, when a source was supplied for a\n * member in {@link PICKABLE_TYPES}, through `EntityLinkInput`. A boolean edited\n * inline carries its own label, so the row does not add a second one.\n *\n * The source branch sits above the `virtual || isRelation` fallback and below\n * `field.render`, which is what lets a slot still override a picked field.\n */\nfunction FieldRow<TEntity extends Entity>({\n field,\n entity,\n draft,\n value,\n editing,\n error,\n setField,\n linkSource,\n onLinkChange,\n id,\n}: FieldRowProps<TEntity>) {\n const readNode: ReactNode = field.readRender ? (\n field.readRender(entity)\n ) : (\n <CellValue\n value={\n entity ? (entity as Record<string, unknown>)[field.name] : undefined\n }\n descriptor={field}\n />\n );\n\n // Members with no editor of their own here: a to-one `link` whose picker was\n // never wired, a `linkCollection` (#26) and a `composition`. All three\n // fall back to the read display, which is honest — a disabled text box\n // holding `[object Object]` is not.\n const isRelation =\n field.type === 'link' ||\n field.type === 'linkCollection' ||\n field.type === 'composition';\n\n let control: ReactNode;\n if (!editing) {\n control = readNode;\n } else if (field.render) {\n control = field.render({ draft, value, setField, id });\n } else if (linkSource && PICKABLE_TYPES.has(field.type)) {\n control = (\n <EntityLinkInput\n descriptor={field}\n value={value}\n source={linkSource}\n id={id}\n disabled={field.readonly}\n onSelect={target => {\n setField(field.name, target.id == null ? '' : String(target.id));\n onLinkChange?.(field.name, target);\n }}\n onClear={() => {\n setField(field.name, '');\n onLinkChange?.(field.name, undefined);\n }}\n />\n );\n } else if (field.type === 'localized' && !field.virtual) {\n control = (\n <LocalizedFieldControl\n descriptor={field}\n value={readLocalizedDraft(draft, field.name)}\n onChange={next => setField(field.name, next)}\n id={id}\n />\n );\n } else if (field.virtual || isRelation) {\n // A relation with no source has no input to offer, and a virtual field has\n // no member to edit unless a `render` was supplied — both fall back to the\n // read display.\n control = readNode;\n } else {\n control = (\n <FieldControl\n descriptor={field}\n value={value}\n onChange={next => setField(field.name, next)}\n id={id}\n />\n );\n }\n\n const ownsLabel =\n editing && field.type === 'boolean' && !field.render && !field.virtual;\n\n return (\n <Stack gap=\"3xs\">\n {!ownsLabel && (\n <label htmlFor={id} className=\"text-step-sm text-content-muted\">\n {field.label}\n </label>\n )}\n {control}\n {error && (\n <span role=\"alert\" className=\"text-step-sm text-danger\">\n {error}\n </span>\n )}\n </Stack>\n );\n}\n"],"names":["cloneEntityDraft","describeEntityColumns","readDraftString","readLocalizedDraft","readRowDrafts","useId","useState","useErrorMessage","useLocalizedDescriptors","useT","useTranslateKey","OVERFLOW_GLYPH","renderAnchor","useCasesForSurface","Button","button","CellValue","FieldControl","LocalizedFieldControl","Skeleton","Text","Card","ConfirmDialog","EntityLinkInput","LoadingBoundary","Menu","Stack","cn","EntityDetailGrid","readEntityFormFields","resolveEntityFormFields","PICKABLE_TYPES","Set","VISIBLE_ACTION_LIMIT","EntityForm","entityConstructor","entity","values","onFieldChange","linkSources","onLinkChange","mode","modeProp","defaultMode","onModeChange","errors","formError","onSubmit","onDelete","embedded","isLoading","skeleton","isSaving","isPending","isDeleting","error","backHref","renderLink","title","metadata","isMetadataLoading","onUseCase","onClone","children","formId","slots","columnDescriptors","described","resolved","fields","assertLinkSourcesAreEditable","filter","field","isOwnedCollection","ownedCollections","customSkeleton","undefined","showSkeleton","internalMode","setInternalMode","editing","draft","setField","name","value","toggleMode","next","t","translateKey","errorMessage","busy","resolvingLinks","Object","some","source","selected","may","action","actions","includes","declaredHeaderUseCases","useCases","declaredFooterUseCases","headerUseCases","footerUseCases","pending","setPending","invoke","descriptor","confirm","key","labelKey","useCaseButton","type","variant","tone","size","placement","disabled","onClick","useCaseMenuItem","Item","withOverflow","descriptors","length","map","visible","slice","folded","Trigger","aria-label","Items","gap","direction","align","as","step","weight","lines","headerActions","span","role","aria-live","className","p","data-testid","shape","FieldRow","linkSource","id","rows","onRowsChange","footerActions","href","intent","kind","rest","open","message","messageKey","onCancel","onConfirm","childType","sourced","has","toMany","Error","join","owned","unpickable","readNode","readRender","isRelation","control","render","readonly","onSelect","target","String","onClear","virtual","onChange","ownsLabel","label","htmlFor"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Entity, EntityConstructor, EntityDraft, EntityDraftValue, EntityFieldDescriptor, EntityLinkSource, EntityMetadataDocument
|
|
1
|
+
import type { EntifixFailure, Entity, EntityConstructor, EntityDraft, EntityDraftValue, EntityFieldDescriptor, EntityLinkSource, EntityMetadataDocument } from '@entifix/core';
|
|
2
2
|
import type { ReactNode } from 'react';
|
|
3
3
|
import type { RenderLink } from '../../actions/index.js';
|
|
4
4
|
/** Whether the form shows values as text (`read`) or as inputs (`edit`). */
|
|
@@ -139,9 +139,14 @@ export interface EntityFormProps<TEntity extends Entity> {
|
|
|
139
139
|
*/
|
|
140
140
|
skeleton?: boolean | ReactNode;
|
|
141
141
|
isSaving?: boolean;
|
|
142
|
+
/**
|
|
143
|
+
* A write the server handed to a flow that has not settled yet. The fields
|
|
144
|
+
* keep what was typed, and Save waits, until the flow's outcome arrives.
|
|
145
|
+
*/
|
|
146
|
+
isPending?: boolean;
|
|
142
147
|
isDeleting?: boolean;
|
|
143
148
|
/** The failure of the last load/save, shown as an alert. */
|
|
144
|
-
error?:
|
|
149
|
+
error?: EntifixFailure;
|
|
145
150
|
/** Link to return to, rendered as a `Back` action. Omit to hide it. */
|
|
146
151
|
backHref?: string;
|
|
147
152
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-form.types.d.ts","sourceRoot":"","sources":["../../../../src/ui/organisms/entity-form/entity-form.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,iBAAiB,EACjB,WAAW,EACX,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,
|
|
1
|
+
{"version":3,"file":"entity-form.types.d.ts","sourceRoot":"","sources":["../../../../src/ui/organisms/entity-form/entity-form.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/ui/organisms/entity-form/entity-form.types.ts"],"sourcesContent":["import type {\n Entity,\n EntityConstructor,\n EntityDraft,\n EntityDraftValue,\n EntityFieldDescriptor,\n EntityLinkSource,\n EntityMetadataDocument,\n
|
|
1
|
+
{"version":3,"sources":["../../../../src/ui/organisms/entity-form/entity-form.types.ts"],"sourcesContent":["import type {\n EntifixFailure,\n Entity,\n EntityConstructor,\n EntityDraft,\n EntityDraftValue,\n EntityFieldDescriptor,\n EntityLinkSource,\n EntityMetadataDocument,\n} from '@entifix/core';\nimport type { ReactNode } from 'react';\n\nimport type { RenderLink } from '../../actions/index.js';\n\n/** Whether the form shows values as text (`read`) or as inputs (`edit`). */\nexport type EntityFormMode = 'read' | 'edit';\n\n/** What a custom edit control for one field is handed. */\nexport interface EntityFieldRenderContext {\n draft: EntityDraft;\n value: string;\n setField: (name: string, value: EntityDraftValue) => void;\n /** The id the field's label points at — put it on your control. */\n id: string;\n}\n\n/**\n * A field as the form finally renders it: the metadata descriptor plus the\n * per-field overrides a caller supplied through an `<EntityField>` slot.\n */\nexport interface EntityFormField<\n TEntity extends Entity,\n> extends EntityFieldDescriptor {\n /** Replaces the default edit control. */\n render?: (context: EntityFieldRenderContext) => ReactNode;\n /** Replaces the default read display. */\n readRender?: (entity: TEntity | undefined) => ReactNode;\n /** True when the field has no backing entity member (slot-only field). */\n virtual?: boolean;\n}\n\nexport interface EntityFormProps<TEntity extends Entity> {\n /** Metadata source. Fields, labels and types derive from its accessors. */\n entityConstructor: EntityConstructor<TEntity>;\n /** The record being viewed/edited. The read display and, in a wrapper, the\n * initial draft both come from it; omit for a create form. */\n entity?: TEntity;\n\n /** The current draft. The form is controlled — a host (the `useEntityForm`\n * hook or a `useState`) owns this and updates it via `onFieldChange`. */\n values?: EntityDraft;\n /**\n * Called when one field changes.\n *\n * The value is a union rather than a string because a `composition`\n * member's is its list of rows — the one member with no lossless string form.\n * Every scalar editor still writes a string, so a host that only edits\n * scalars is unaffected.\n */\n onFieldChange?: (name: string, value: EntityDraftValue) => void;\n\n /**\n * A search/browse source per reference, keyed by accessor name. A `link` field\n * with a source gets the full editor; one without keeps its read-only display,\n * which is what a form that only shows a relation still wants.\n *\n * A **`string`** field with a source gets the same editor, because a foreign\n * key whose target lives in another slice's store cannot be a typed `link` at\n * all and is a plain id member. The editor does not know the\n * difference — it writes the target's id into the draft either way.\n *\n * A key naming any other member throws: `linkCollection` has no editor\n * anywhere yet, and a `boolean`/`enum`/`number`/`date` can never name another\n * record, so the entry could only be dropped — and a dropped source looks\n * exactly like a member the entity declared read-only.\n *\n * A registry rather than a slot per relation: the whole point is that an entity\n * declaring a reference needs no bespoke control. The sources are built by the\n * caller because fetching them is the integration layer's job, and this package\n * cannot reach it.\n */\n // A source's target is another entity entirely, so it cannot be expressed in\n // terms of `TEntity`; the input it feeds is instantiated per field anyway.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n linkSources?: Record<string, EntityLinkSource<any>>;\n /**\n * A relation was set or cleared. The instance travels alongside the id the\n * draft holds, so a host can remember it — an id alone cannot be written as an\n * embedded relation.\n */\n onLinkChange?: (name: string, entity: Entity | undefined) => void;\n\n /** Force a mode. Omit to let the form own it (with the built-in toggle). */\n mode?: EntityFormMode;\n /** Initial mode when uncontrolled. Defaults to `read` with an `entity`, else\n * `edit` (a create form opens ready to type). */\n defaultMode?: EntityFormMode;\n onModeChange?: (mode: EntityFormMode) => void;\n\n /** Per-field validation messages, keyed by field name. */\n errors?: Record<string, string>;\n /**\n * A validation message that belongs to no single field — a rule spanning two\n * of them. Rendered above the actions, since no row can host it.\n */\n formError?: string;\n\n onSubmit?: (draft: EntityDraft) => void;\n onDelete?: () => void;\n\n /**\n * What this caller may do with the entity, as served by\n * `GET /api/<entity>/$metadata` and already filtered against the verified\n * principal.\n *\n * **Optional, and absent means \"as before\".** Without it the form renders Save\n * and Delete exactly as it always has, so a call site that has not migrated is\n * unaffected. This is not a security control — hiding a button protects\n * nothing; the route guard does. It is what stops a screen offering an action\n * the service will refuse.\n */\n metadata?: EntityMetadataDocument;\n /** The document is still in flight; the action row renders as a skeleton. */\n isMetadataLoading?: boolean;\n /**\n * A declared use case was invoked, by its `key`. The form renders the button\n * and asks for confirmation where the descriptor says to; performing the act\n * is the caller's — the browser must never import a use-case class.\n */\n onUseCase?: (key: string) => void;\n\n /**\n * Copies the record into a new draft, dropping the members declared\n * `@accessor({ resetOnClone: true })` and always the id.\n *\n * A prop rather than something the form does by itself, because the *result*\n * of a clone is a navigation — a create form holding the copy — and this\n * package knows no router. The form supplies the cleaned draft\n * (`cloneEntityDraft`); where it lands is the caller's.\n *\n * Absent renders no Clone action.\n */\n onClone?: (draft: EntityDraft) => void;\n\n /**\n * Render the **fields and nothing else** — no heading, no action row.\n *\n * For a form hosted inside something that already owns those controls: a\n * wizard step sits under the wizard's own heading and advances with the\n * wizard's own footer, so a second heading and a Save button beside Siguiente\n * would be two controls for one job, and the Save would commit a partial\n * record mid-flow.\n *\n * Opt-in and defaulted off, so every existing form is untouched.\n */\n embedded?: boolean;\n isLoading?: boolean;\n /**\n * What holds the form's shape while the record is in flight.\n *\n * `true` (the default) renders the built-in placeholder — one label+control\n * pair per resolved field, so the swap to the real rows shifts nothing. A node\n * replaces that default; `false` renders no placeholder at all.\n *\n * The placeholder *replaces* the field rows; it never stacks above them.\n */\n skeleton?: boolean | ReactNode;\n isSaving?: boolean;\n /**\n * A write the server handed to a flow that has not settled yet. The fields\n * keep what was typed, and Save waits, until the flow's outcome arrives.\n */\n isPending?: boolean;\n isDeleting?: boolean;\n /** The failure of the last load/save, shown as an alert. */\n error?: EntifixFailure;\n\n /** Link to return to, rendered as a `Back` action. Omit to hide it. */\n backHref?: string;\n /**\n * Renders the `Back` link. Defaults to a plain anchor, which a client-routed\n * host experiences as a full document load. See {@link RenderLink}.\n */\n renderLink?: RenderLink;\n /** Heading for the card. Defaults to a generic label. */\n title?: ReactNode;\n\n /** Customization slots — see `entity-form-slots`. */\n children?: ReactNode;\n}\n"],"names":[],"mappings":"AAyCA,WAoJC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BulkOutcome, Entity, EntityConstructor, EntityFieldDescriptor, EntityId, EntityMetadataDocument, EntitySelection, EntitySorting, FilterGroup
|
|
1
|
+
import type { BulkOutcome, EntifixFailure, Entity, EntityConstructor, EntityFieldDescriptor, EntityId, EntityMetadataDocument, EntitySelection, EntitySorting, FilterGroup } from '@entifix/core';
|
|
2
2
|
import type { ReactNode } from 'react';
|
|
3
3
|
import type { RenderLink } from '../../actions/index.js';
|
|
4
4
|
/**
|
|
@@ -43,7 +43,7 @@ export interface EntityTableProps<TEntity extends Entity> {
|
|
|
43
43
|
* It arrives for free from `useDataLoading`, whose whole state is spread into
|
|
44
44
|
* this component.
|
|
45
45
|
*/
|
|
46
|
-
error?:
|
|
46
|
+
error?: EntifixFailure;
|
|
47
47
|
items: ReadonlyArray<TEntity>;
|
|
48
48
|
totalItems: number;
|
|
49
49
|
currentPage: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-table.types.d.ts","sourceRoot":"","sources":["../../../../src/ui/organisms/entity-table/entity-table.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,MAAM,EACN,iBAAiB,EACjB,qBAAqB,EACrB,QAAQ,EACR,sBAAsB,EACtB,eAAe,EACf,aAAa,EACb,WAAW,
|
|
1
|
+
{"version":3,"file":"entity-table.types.d.ts","sourceRoot":"","sources":["../../../../src/ui/organisms/entity-table/entity-table.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/ui/organisms/entity-table/entity-table.types.ts"],"sourcesContent":["import type {\n BulkOutcome,\n Entity,\n EntityConstructor,\n EntityFieldDescriptor,\n EntityId,\n EntityMetadataDocument,\n EntitySelection,\n EntitySorting,\n FilterGroup,\n
|
|
1
|
+
{"version":3,"sources":["../../../../src/ui/organisms/entity-table/entity-table.types.ts"],"sourcesContent":["import type {\n BulkOutcome,\n EntifixFailure,\n Entity,\n EntityConstructor,\n EntityFieldDescriptor,\n EntityId,\n EntityMetadataDocument,\n EntitySelection,\n EntitySorting,\n FilterGroup,\n} from '@entifix/core';\nimport type { ReactNode } from 'react';\n\nimport type { RenderLink } from '../../actions/index.js';\n\n/**\n * A column as the table finally renders it: the metadata descriptor plus the\n * per-column overrides a caller supplied through an `<EntityColumn>` slot.\n */\nexport interface EntityTableColumn<\n TEntity extends Entity,\n> extends EntityFieldDescriptor {\n /** Replaces the default header text. */\n header?: ReactNode;\n /** Replaces the default `CellValue` rendering for this column. */\n render?: (item: TEntity) => ReactNode;\n /** True when the column has no backing entity member (slot-only column). */\n virtual?: boolean;\n}\n\n/** Breakpoint at and above which the table renders as a grid, not as cards. */\nexport type EntityTablePivotBreakpoint = 'sm' | 'md' | 'lg';\n\nexport interface EntityTableProps<TEntity extends Entity> {\n /** Metadata source. Columns, labels, types and the filter/sort controls all\n * derive from this constructor's accessors. */\n entityConstructor: EntityConstructor<TEntity>;\n\n isLoading: boolean;\n /**\n * What holds the table's shape during the first load.\n *\n * `true` (the default) renders the built-in placeholder, which is derived from\n * the table's own resolved geometry — one shimmer cell per visible column, so\n * the swap to real rows shifts nothing. A node replaces that default; `false`\n * renders no placeholder at all.\n *\n * Only the *first* load is held this way. A refetch that already has rows\n * keeps them and marks the region busy instead — replacing populated rows with\n * shimmer on every pagination click is a grey flash, not a loading state.\n */\n skeleton?: boolean | ReactNode;\n /**\n * The failure of the last load, if it failed.\n *\n * A listing that renders an empty table when the service is unreachable tells\n * the user their catalog is empty — the one thing they must not conclude. So\n * this is rendered as an alert, and it also changes the empty message.\n *\n * It arrives for free from `useDataLoading`, whose whole state is spread into\n * this component.\n */\n error?: EntifixFailure;\n items: ReadonlyArray<TEntity>;\n totalItems: number;\n currentPage: number;\n pageSize: number;\n onPageChange: (newPage: number) => void;\n onPageSizeChange?: (newPageSize: number) => void;\n\n /**\n * Builds the link to a row's record. Supplied as a function rather than the\n * table routing by itself: this package must not depend on a router, so the\n * caller (a shell page, which does know one) owns the URL shape.\n *\n * Omit it and the actions column is not rendered at all.\n */\n hrefFor?: (id: EntityId) => string;\n /** Link to the create form. Omit and no `New` action is rendered. */\n newHref?: string;\n /**\n * Renders the `Open` and `New` links. Defaults to a plain anchor, which a\n * client-routed host experiences as a full document load — pass one built on\n * the host's own link component. See {@link RenderLink}.\n */\n renderLink?: RenderLink;\n\n /**\n * Picks a row instead of navigating to it — how a link picker reuses this\n * table as its browse dialog.\n *\n * Takes precedence over {@link hrefFor}: inside a picker the user is choosing\n * a value, and offering to navigate away from the form they are editing is the\n * wrong affordance. Either one renders the actions column.\n */\n onSelect?: (item: TEntity) => void;\n\n /** Scope for persisted personalization. Defaults to the entity's key. */\n preferencesKey?: string;\n /** Hide the toolbar (columns, filters, sorting) — a bare listing. */\n showControls?: boolean;\n /** Viewport width at which rows stop pivoting into cards. Default `md`. */\n pivotBreakpoint?: EntityTablePivotBreakpoint;\n\n /** The filtering currently applied, seeding the filter panel so it shows\n * what is in effect rather than an empty form. */\n filtering?: FilterGroup<TEntity>;\n /** The sorting currently applied, seeding the sort panel. */\n sorting?: EntitySorting<TEntity>;\n\n /** Notified when the filter panel is **applied** — not while it is edited, so\n * the value can feed a load request directly. */\n onFilteringChange?: (filtering: FilterGroup<TEntity>) => void;\n /** Notified when the sort panel is applied. */\n onSortingChange?: (sorting: EntitySorting<TEntity>) => void;\n\n /**\n * The rows a collection-bound action would act on.\n *\n * **Controlled from above the table**, exactly as `filtering` and `sorting`\n * are, and for the same reason plus one more: a selection has to survive\n * pagination, and the page owns the pager. A selection held inside the table\n * would be reset by the very navigation the user performs to add to it.\n *\n * Omit it and no selection column is rendered — a listing that is only read\n * gains nothing from a column of empty boxes.\n */\n selection?: EntitySelection<TEntity>;\n onSelectionChange?: (selection: EntitySelection<TEntity>) => void;\n\n /**\n * What this caller may do with the entity, as the service decided it.\n *\n * The same document `EntityForm` takes. The table reads only the\n * `collection`-bound half of `useCases` — the entity-bound verbs belong to a\n * record, and a row's own overflow menu is fed from the\n * `context-dependent` ones.\n *\n * Absent keeps the pre-ADR-0026 behaviour: no bulk bar and no row menu, which\n * is what every un-migrated call site renders today.\n */\n metadata?: EntityMetadataDocument;\n isMetadataLoading?: boolean;\n\n /** Runs a `collection`-bound verb over {@link selection}. */\n onBulkUseCase?: (key: string, selection: EntitySelection<TEntity>) => void;\n /** Runs an `entity`-bound, `context-dependent` verb on one row. */\n onUseCase?: (key: string, item: TEntity) => void;\n\n /** The outcome of the last bulk run, rendered per row until dismissed. */\n bulkOutcomes?: readonly BulkOutcome[];\n onBulkDismiss?: () => void;\n onBulkRetry?: (ids: EntityId[]) => void;\n /** A bulk run is in flight — every verb is disabled while it is. */\n isBulkRunning?: boolean;\n\n /** Customization slots — see `entity-table-slots`. */\n children?: ReactNode;\n}\n"],"names":[],"mappings":"AAkCA,WA6HC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entifix/react-controls",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.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.
|
|
22
|
+
"@entifix/core": "0.14.0"
|
|
23
23
|
},
|
|
24
24
|
"exports": {
|
|
25
25
|
"./package.json": "./package.json",
|
|
@@ -45,16 +45,16 @@
|
|
|
45
45
|
"LICENSE"
|
|
46
46
|
],
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@storybook/addon-docs": "^10.6.
|
|
49
|
-
"@storybook/addon-themes": "^10.6.
|
|
50
|
-
"@storybook/react-vite": "^10.6.
|
|
51
|
-
"effect": "4.0.
|
|
52
|
-
"i18next": "26.
|
|
48
|
+
"@storybook/addon-docs": "^10.6.5",
|
|
49
|
+
"@storybook/addon-themes": "^10.6.5",
|
|
50
|
+
"@storybook/react-vite": "^10.6.5",
|
|
51
|
+
"effect": "4.0.7",
|
|
52
|
+
"i18next": "26.6.0",
|
|
53
53
|
"react": "19.3.1",
|
|
54
|
-
"react-i18next": "17.1.
|
|
55
|
-
"storybook": "^10.6.
|
|
56
|
-
"@entifix/
|
|
57
|
-
"@entifix/
|
|
54
|
+
"react-i18next": "17.1.10",
|
|
55
|
+
"storybook": "^10.6.5",
|
|
56
|
+
"@entifix/i18n": "0.14.0",
|
|
57
|
+
"@entifix/style": "0.14.0"
|
|
58
58
|
},
|
|
59
59
|
"nx": {
|
|
60
60
|
"tags": [
|
|
@@ -93,11 +93,11 @@
|
|
|
93
93
|
}
|
|
94
94
|
},
|
|
95
95
|
"peerDependencies": {
|
|
96
|
-
"effect": "^4.0.
|
|
96
|
+
"effect": "^4.0.7",
|
|
97
97
|
"i18next": ">=26 <27",
|
|
98
98
|
"react": ">=19 <20",
|
|
99
99
|
"react-i18next": ">=17 <18",
|
|
100
|
-
"@entifix/i18n": "0.
|
|
100
|
+
"@entifix/i18n": "0.14.0"
|
|
101
101
|
},
|
|
102
102
|
"peerDependenciesMeta": {
|
|
103
103
|
"@entifix/i18n": {
|