@asteby/metacore-runtime-react 16.0.1 → 17.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/action-modal-dispatcher.d.ts +1 -1
  3. package/dist/action-modal-dispatcher.d.ts.map +1 -1
  4. package/dist/addon-layout-context.d.ts +1 -1
  5. package/dist/addon-layout-context.d.ts.map +1 -1
  6. package/dist/addon-loader.d.ts +1 -1
  7. package/dist/addon-loader.d.ts.map +1 -1
  8. package/dist/api-context.d.ts +2 -2
  9. package/dist/api-context.d.ts.map +1 -1
  10. package/dist/capability-gate.d.ts +2 -2
  11. package/dist/capability-gate.d.ts.map +1 -1
  12. package/dist/dialogs/_primitives.d.ts +4 -4
  13. package/dist/dialogs/_primitives.d.ts.map +1 -1
  14. package/dist/dialogs/create-record-dialog.d.ts +1 -1
  15. package/dist/dialogs/create-record-dialog.d.ts.map +1 -1
  16. package/dist/dialogs/dynamic-record.d.ts +1 -1
  17. package/dist/dialogs/dynamic-record.d.ts.map +1 -1
  18. package/dist/dialogs/dynamic-record.js +8 -2
  19. package/dist/dialogs/export.d.ts +1 -1
  20. package/dist/dialogs/export.d.ts.map +1 -1
  21. package/dist/dialogs/import.d.ts +1 -1
  22. package/dist/dialogs/import.d.ts.map +1 -1
  23. package/dist/dialogs/view-record-dialog.d.ts +1 -1
  24. package/dist/dialogs/view-record-dialog.d.ts.map +1 -1
  25. package/dist/dynamic-columns.d.ts.map +1 -1
  26. package/dist/dynamic-columns.js +9 -2
  27. package/dist/dynamic-crud-page.d.ts +1 -1
  28. package/dist/dynamic-crud-page.d.ts.map +1 -1
  29. package/dist/dynamic-date-field.d.ts +1 -1
  30. package/dist/dynamic-date-field.d.ts.map +1 -1
  31. package/dist/dynamic-form.d.ts +1 -1
  32. package/dist/dynamic-form.d.ts.map +1 -1
  33. package/dist/dynamic-icon.d.ts +1 -1
  34. package/dist/dynamic-icon.d.ts.map +1 -1
  35. package/dist/dynamic-line-items.d.ts +1 -1
  36. package/dist/dynamic-line-items.d.ts.map +1 -1
  37. package/dist/dynamic-relation-helpers.d.ts +2 -0
  38. package/dist/dynamic-relation-helpers.d.ts.map +1 -1
  39. package/dist/dynamic-relation-helpers.js +47 -0
  40. package/dist/dynamic-relation.d.ts +2 -2
  41. package/dist/dynamic-relation.d.ts.map +1 -1
  42. package/dist/dynamic-relation.js +6 -12
  43. package/dist/dynamic-relations.d.ts +1 -1
  44. package/dist/dynamic-relations.d.ts.map +1 -1
  45. package/dist/dynamic-select-field.d.ts +1 -1
  46. package/dist/dynamic-select-field.d.ts.map +1 -1
  47. package/dist/dynamic-table.d.ts +1 -1
  48. package/dist/dynamic-table.d.ts.map +1 -1
  49. package/dist/i18n-provider.d.ts +1 -1
  50. package/dist/i18n-provider.d.ts.map +1 -1
  51. package/dist/index.d.ts +1 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +1 -0
  54. package/dist/model-action-toolbar.d.ts +1 -1
  55. package/dist/model-action-toolbar.d.ts.map +1 -1
  56. package/dist/navigation-builder.d.ts +1 -1
  57. package/dist/navigation-builder.d.ts.map +1 -1
  58. package/dist/nil-uuid.d.ts +15 -0
  59. package/dist/nil-uuid.d.ts.map +1 -0
  60. package/dist/nil-uuid.js +20 -0
  61. package/dist/slot.d.ts +1 -1
  62. package/dist/slot.d.ts.map +1 -1
  63. package/dist/upload-field.d.ts +1 -1
  64. package/dist/upload-field.d.ts.map +1 -1
  65. package/package.json +5 -5
  66. package/src/__tests__/dynamic-relation.test.ts +65 -0
  67. package/src/__tests__/nil-uuid.test.ts +42 -0
  68. package/src/__tests__/relation-option-cells.test.ts +13 -0
  69. package/src/dialogs/dynamic-record.tsx +8 -2
  70. package/src/dynamic-columns.tsx +8 -2
  71. package/src/dynamic-relation-helpers.ts +47 -0
  72. package/src/dynamic-relation.tsx +11 -12
  73. package/src/index.ts +1 -0
  74. package/src/nil-uuid.ts +25 -0
@@ -17,6 +17,6 @@ interface DynamicTableProps {
17
17
  */
18
18
  getDynamicColumns?: GetDynamicColumns;
19
19
  }
20
- export declare function DynamicTable({ model, endpoint, enableUrlSync, hiddenColumns, onAction, refreshTrigger, defaultFilters, extraColumns, getDynamicColumns, }: DynamicTableProps): import("react/jsx-runtime").JSX.Element;
20
+ export declare function DynamicTable({ model, endpoint, enableUrlSync, hiddenColumns, onAction, refreshTrigger, defaultFilters, extraColumns, getDynamicColumns, }: DynamicTableProps): import("react").JSX.Element;
21
21
  export {};
22
22
  //# sourceMappingURL=dynamic-table.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AA+B9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAUnF,UAAU,iBAAiB;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CACxC;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,aAAoB,EACpB,aAAkB,EAClB,QAAQ,EACR,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,GAC/C,EAAE,iBAAiB,2CA+xBnB"}
1
+ {"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AA+B9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAUnF,UAAU,iBAAiB;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CACxC;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,aAAoB,EACpB,aAAkB,EAClB,QAAQ,EACR,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,GAC/C,EAAE,iBAAiB,+BA+xBnB"}
@@ -12,5 +12,5 @@ export interface I18nProviderProps {
12
12
  contributions: AddonI18nResources[];
13
13
  children: React.ReactNode;
14
14
  }
15
- export declare function I18nProvider({ i18n, contributions, children }: I18nProviderProps): import("react/jsx-runtime").JSX.Element;
15
+ export declare function I18nProvider({ i18n, contributions, children }: I18nProviderProps): import("react").JSX.Element;
16
16
  //# sourceMappingURL=i18n-provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"i18n-provider.d.ts","sourceRoot":"","sources":["../src/i18n-provider.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,IAAI,YAAY,EAAE,MAAM,SAAS,CAAA;AAEnD,MAAM,WAAW,kBAAkB;IAC/B,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAA;IACd,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;CACjD;AAED,MAAM,WAAW,iBAAiB;IAC9B,mCAAmC;IACnC,IAAI,EAAE,YAAY,CAAA;IAClB,8DAA8D;IAC9D,aAAa,EAAE,kBAAkB,EAAE,CAAA;IACnC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC5B;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,iBAAiB,2CAWhF"}
1
+ {"version":3,"file":"i18n-provider.d.ts","sourceRoot":"","sources":["../src/i18n-provider.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,IAAI,YAAY,EAAE,MAAM,SAAS,CAAA;AAEnD,MAAM,WAAW,kBAAkB;IAC/B,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAA;IACd,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;CACjD;AAED,MAAM,WAAW,iBAAiB;IAC9B,mCAAmC;IACnC,IAAI,EAAE,YAAY,CAAA;IAClB,8DAA8D;IAC9D,aAAa,EAAE,kBAAkB,EAAE,CAAA;IACnC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC5B;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,iBAAiB,+BAWhF"}
package/dist/index.d.ts CHANGED
@@ -17,6 +17,7 @@ export { useHotSwapReload, applyHotSwapReload, withVersionParam, clearFederation
17
17
  export * from './dynamic-icon';
18
18
  export type { ColumnFilterConfig, FilterOption as DynamicColumnFilterOption, GetDynamicColumns, DynamicIconComponent, } from './dynamic-columns-shim';
19
19
  export { defaultGetDynamicColumns, makeDefaultGetDynamicColumns, relationKeyFor, resolveRelationLabel, type DynamicColumnsHelpers, } from './dynamic-columns';
20
+ export { NIL_UUID, isNilUuid, normalizeNilUuid } from './nil-uuid';
20
21
  export { DynamicRecordDialog } from './dialogs/dynamic-record';
21
22
  export { CreateRecordDialog } from './dialogs/create-record-dialog';
22
23
  export { ViewRecordDialog } from './dialogs/view-record-dialog';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,SAAS,CAAA;AACvB,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EACH,qBAAqB,EACrB,KAAK,gBAAgB,GACxB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACH,kBAAkB,EAClB,eAAe,EACf,KAAK,uBAAuB,EAC5B,KAAK,eAAe,GACvB,MAAM,wBAAwB,CAAA;AAC/B,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EACH,mBAAmB,EACnB,cAAc,EACd,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,WAAW,EAChB,KAAK,wBAAwB,GAChC,MAAM,wBAAwB,CAAA;AAC/B,cAAc,QAAQ,CAAA;AACtB,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,OAAO,EACH,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,KAAK,2BAA2B,EAChC,KAAK,qBAAqB,EAC1B,KAAK,8BAA8B,GACtC,MAAM,+BAA+B,CAAA;AACtC,OAAO,EACH,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,wBAAwB,EACxB,WAAW,EACX,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,GAC9B,MAAM,yBAAyB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,YAAY,EACR,kBAAkB,EAClB,YAAY,IAAI,yBAAyB,EACzC,iBAAiB,EACjB,oBAAoB,GACvB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACH,wBAAwB,EACxB,4BAA4B,EAC5B,cAAc,EACd,oBAAoB,EACpB,KAAK,qBAAqB,GAC7B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAC/D,YAAY,EACR,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,uBAAuB,EACvB,qBAAqB,GACxB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EACH,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,GAC9B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,kBAAkB,EAClB,wBAAwB,EACxB,cAAc,GACjB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACH,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,KAAK,qBAAqB,GAC7B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,mBAAmB,GAC3B,MAAM,4BAA4B,CAAA;AACnC,OAAO,EACH,sBAAsB,EACtB,uBAAuB,GAC1B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,kBAAkB,EAClB,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,GAChC,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACH,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,KAAK,eAAe,GACvB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA"}
package/dist/index.js CHANGED
@@ -21,6 +21,7 @@ export { ADDON_MANIFEST_CHANGED_TYPE, wireHotSwapInvalidation, useManifestHotSwa
21
21
  export { useHotSwapReload, applyHotSwapReload, withVersionParam, clearFederationContainer, shortenHash, } from './hotswap-reload-policy';
22
22
  export * from './dynamic-icon';
23
23
  export { defaultGetDynamicColumns, makeDefaultGetDynamicColumns, relationKeyFor, resolveRelationLabel, } from './dynamic-columns';
24
+ export { NIL_UUID, isNilUuid, normalizeNilUuid } from './nil-uuid';
24
25
  export { DynamicRecordDialog } from './dialogs/dynamic-record';
25
26
  export { CreateRecordDialog } from './dialogs/create-record-dialog';
26
27
  export { ViewRecordDialog } from './dialogs/view-record-dialog';
@@ -23,5 +23,5 @@ export interface ModelActionToolbarProps {
23
23
  * `actions` prop when provided, else the metadata cache, else fetches once.
24
24
  */
25
25
  export declare function useModelActions(model: string, placements?: ActionPlacement[], provided?: ActionDefinition[]): ActionDefinition[];
26
- export declare function ModelActionToolbar({ model, endpoint, actions, placements, onChange, className, }: ModelActionToolbarProps): import("react/jsx-runtime").JSX.Element | null;
26
+ export declare function ModelActionToolbar({ model, endpoint, actions, placements, onChange, className, }: ModelActionToolbarProps): import("react").JSX.Element | null;
27
27
  //# sourceMappingURL=model-action-toolbar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"model-action-toolbar.d.ts","sourceRoot":"","sources":["../src/model-action-toolbar.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,gBAAgB,EAAiC,MAAM,SAAS,CAAA;AAE9E,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAA;AAExD,MAAM,WAAW,uBAAuB;IACpC,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAA;IACb,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC5B,qEAAqE;IACrE,UAAU,CAAC,EAAE,eAAe,EAAE,CAAA;IAC9B,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAmBD;;;GAGG;AACH,wBAAgB,eAAe,CAC3B,KAAK,EAAE,MAAM,EACb,UAAU,GAAE,eAAe,EAAuB,EAClD,QAAQ,CAAC,EAAE,gBAAgB,EAAE,GAC9B,gBAAgB,EAAE,CA2BpB;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,OAAO,EACP,UAA+B,EAC/B,QAAQ,EACR,SAAS,GACZ,EAAE,uBAAuB,kDA4CzB"}
1
+ {"version":3,"file":"model-action-toolbar.d.ts","sourceRoot":"","sources":["../src/model-action-toolbar.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,gBAAgB,EAAiC,MAAM,SAAS,CAAA;AAE9E,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAA;AAExD,MAAM,WAAW,uBAAuB;IACpC,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAA;IACb,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC5B,qEAAqE;IACrE,UAAU,CAAC,EAAE,eAAe,EAAE,CAAA;IAC9B,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAmBD;;;GAGG;AACH,wBAAgB,eAAe,CAC3B,KAAK,EAAE,MAAM,EACb,UAAU,GAAE,eAAe,EAAuB,EAClD,QAAQ,CAAC,EAAE,gBAAgB,EAAE,GAC9B,gBAAgB,EAAE,CA2BpB;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,OAAO,EACP,UAA+B,EAC/B,QAAQ,EACR,SAAS,GACZ,EAAE,uBAAuB,sCA4CzB"}
@@ -30,5 +30,5 @@ export interface NavigationBuilderProps {
30
30
  }
31
31
  /** Render-prop component for hosts that want the merge logic but render
32
32
  * the sidebar with their own primitives. */
33
- export declare function NavigationBuilder({ base, contributions, render }: NavigationBuilderProps): import("react/jsx-runtime").JSX.Element;
33
+ export declare function NavigationBuilder({ base, contributions, render }: NavigationBuilderProps): import("react").JSX.Element;
34
34
  //# sourceMappingURL=navigation-builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-builder.d.ts","sourceRoot":"","sources":["../src/navigation-builder.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,OAAO;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;IACpB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,2BAA2B;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,OAAO,EAAE,CAAA;CACnB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,2BAA2B,EAAE,GAAG,OAAO,EAAE,CA8BxG;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,2BAA2B,EAAE,GAAG,OAAO,EAAE,CAEtG;AAED,MAAM,WAAW,sBAAsB;IACnC,IAAI,EAAE,OAAO,EAAE,CAAA;IACf,aAAa,EAAE,2BAA2B,EAAE,CAAA;IAC5C,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,KAAK,CAAC,SAAS,CAAA;CAChD;AAED;6CAC6C;AAC7C,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,sBAAsB,2CAGxF"}
1
+ {"version":3,"file":"navigation-builder.d.ts","sourceRoot":"","sources":["../src/navigation-builder.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,OAAO;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;IACpB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,2BAA2B;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,OAAO,EAAE,CAAA;CACnB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,2BAA2B,EAAE,GAAG,OAAO,EAAE,CA8BxG;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,2BAA2B,EAAE,GAAG,OAAO,EAAE,CAEtG;AAED,MAAM,WAAW,sBAAsB;IACnC,IAAI,EAAE,OAAO,EAAE,CAAA;IACf,aAAa,EAAE,2BAA2B,EAAE,CAAA;IAC5C,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,KAAK,CAAC,SAAS,CAAA;CAChD;AAED;6CAC6C;AAC7C,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,sBAAsB,+BAGxF"}
@@ -0,0 +1,15 @@
1
+ /** The canonical nil/zero UUID sentinel. */
2
+ export declare const NIL_UUID = "00000000-0000-0000-0000-000000000000";
3
+ /**
4
+ * True when `value` is the nil UUID string. Tolerant of surrounding whitespace
5
+ * and letter-case (UUIDs are conventionally lowercase, but be defensive). Only
6
+ * matches strings — numeric/object values are never the nil UUID.
7
+ */
8
+ export declare const isNilUuid: (value: unknown) => boolean;
9
+ /**
10
+ * Normalizes a raw cell value: returns `undefined` when it is the nil UUID so
11
+ * downstream renderers hit their existing nullish/empty branches; otherwise the
12
+ * value is passed through unchanged.
13
+ */
14
+ export declare const normalizeNilUuid: <T>(value: T) => T | undefined;
15
+ //# sourceMappingURL=nil-uuid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nil-uuid.d.ts","sourceRoot":"","sources":["../src/nil-uuid.ts"],"names":[],"mappings":"AAOA,4CAA4C;AAC5C,eAAO,MAAM,QAAQ,yCAAyC,CAAA;AAE9D;;;;GAIG;AACH,eAAO,MAAM,SAAS,GAAI,OAAO,OAAO,KAAG,OAC6B,CAAA;AAExE;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,CAAC,GAAG,SACX,CAAA"}
@@ -0,0 +1,20 @@
1
+ // Shared guard for the "nil UUID" — the all-zeros UUID Postgres/Go emit for a
2
+ // nullable FK that was never set (`00000000-0000-0000-0000-000000000000`).
3
+ // Backends sometimes serialize an unset `uuid` column as this sentinel instead
4
+ // of `null`, which then leaks into the UI as a long string of zeros. Treat it
5
+ // as "no value" so cell/detail renderers fall through to their existing empty
6
+ // markers ("-" / "—"). Cosmetic defense-in-depth — the backend should emit null.
7
+ /** The canonical nil/zero UUID sentinel. */
8
+ export const NIL_UUID = '00000000-0000-0000-0000-000000000000';
9
+ /**
10
+ * True when `value` is the nil UUID string. Tolerant of surrounding whitespace
11
+ * and letter-case (UUIDs are conventionally lowercase, but be defensive). Only
12
+ * matches strings — numeric/object values are never the nil UUID.
13
+ */
14
+ export const isNilUuid = (value) => typeof value === 'string' && value.trim().toLowerCase() === NIL_UUID;
15
+ /**
16
+ * Normalizes a raw cell value: returns `undefined` when it is the nil UUID so
17
+ * downstream renderers hit their existing nullish/empty branches; otherwise the
18
+ * value is passed through unchanged.
19
+ */
20
+ export const normalizeNilUuid = (value) => isNilUuid(value) ? undefined : value;
package/dist/slot.d.ts CHANGED
@@ -27,6 +27,6 @@ export interface SlotProps {
27
27
  /** Fallback element shown when no contribution is registered. */
28
28
  fallback?: React.ReactNode;
29
29
  }
30
- export declare function Slot({ name, props, fallback }: SlotProps): import("react/jsx-runtime").JSX.Element;
30
+ export declare function Slot({ name, props, fallback }: SlotProps): React.JSX.Element;
31
31
  export {};
32
32
  //# sourceMappingURL=slot.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"slot.d.ts","sourceRoot":"","sources":["../src/slot.tsx"],"names":[],"mappings":"AAGA,OAAO,KAA+B,MAAM,OAAO,CAAA;AAEnD,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,GAAG,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAA;AAE3D,UAAU,SAAS;IACf,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,aAAa,CAAA;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAA;AAE1B,cAAM,gBAAgB;IAClB,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,SAAS,CAAsB;IAEvC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,IAAI;IAoB7G,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE;IAIhC,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI;IAKzC,OAAO,CAAC,IAAI;CACf;AAED,eAAO,MAAM,YAAY,kBAAyB,CAAA;AAElD,MAAM,WAAW,SAAS;IACtB,eAAe;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC7B;AAED,wBAAgB,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAe,EAAE,EAAE,SAAS,2CAe/D"}
1
+ {"version":3,"file":"slot.d.ts","sourceRoot":"","sources":["../src/slot.tsx"],"names":[],"mappings":"AAGA,OAAO,KAA+B,MAAM,OAAO,CAAA;AAEnD,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,GAAG,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAA;AAE3D,UAAU,SAAS;IACf,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,aAAa,CAAA;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAA;AAE1B,cAAM,gBAAgB;IAClB,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,SAAS,CAAsB;IAEvC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,IAAI;IAoB7G,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE;IAIhC,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI;IAKzC,OAAO,CAAC,IAAI;CACf;AAED,eAAO,MAAM,YAAY,kBAAyB,CAAA;AAElD,MAAM,WAAW,SAAS;IACtB,eAAe;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC7B;AAED,wBAAgB,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAe,EAAE,EAAE,SAAS,qBAe/D"}
@@ -11,5 +11,5 @@ export interface UploadFieldProps {
11
11
  export declare function extractUploadedValue(payload: any): string;
12
12
  /** Short, human display name for an already-stored file value (a url/path). */
13
13
  export declare function uploadedDisplayName(value: unknown): string;
14
- export declare function UploadField({ field, value, onChange }: UploadFieldProps): import("react/jsx-runtime").JSX.Element;
14
+ export declare function UploadField({ field, value, onChange }: UploadFieldProps): import("react").JSX.Element;
15
15
  //# sourceMappingURL=upload-field.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"upload-field.d.ts","sourceRoot":"","sources":["../src/upload-field.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,MAAM,WAAW,gBAAgB;IAC7B,KAAK,EAAE,cAAc,CAAA;IACrB,KAAK,EAAE,GAAG,CAAA;IACV,QAAQ,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAA;CAC7B;AAKD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CAWzD;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAK1D;AAED,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,gBAAgB,2CAkHvE"}
1
+ {"version":3,"file":"upload-field.d.ts","sourceRoot":"","sources":["../src/upload-field.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,MAAM,WAAW,gBAAgB;IAC7B,KAAK,EAAE,cAAc,CAAA;IACrB,KAAK,EAAE,GAAG,CAAA;IACV,QAAQ,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAA;CAC7B;AAKD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CAWzD;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAK1D;AAED,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,gBAAgB,+BAkHvE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/metacore-runtime-react",
3
- "version": "16.0.1",
3
+ "version": "17.0.1",
4
4
  "description": "React runtime for metacore hosts — renders addon contributions dynamically",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,8 +33,8 @@
33
33
  "lucide-react": ">=0.460",
34
34
  "date-fns": ">=3",
35
35
  "react-day-picker": ">=8",
36
- "@asteby/metacore-sdk": "^3.1.0",
37
- "@asteby/metacore-ui": "^2.4.0"
36
+ "@asteby/metacore-ui": "^2.4.1",
37
+ "@asteby/metacore-sdk": "^3.2.0"
38
38
  },
39
39
  "peerDependenciesMeta": {
40
40
  "@tanstack/react-router": {
@@ -61,8 +61,8 @@
61
61
  "typescript": "^6.0.0",
62
62
  "vitest": "^4.0.0",
63
63
  "zustand": "^5.0.0",
64
- "@asteby/metacore-ui": "2.4.0",
65
- "@asteby/metacore-sdk": "3.1.0"
64
+ "@asteby/metacore-sdk": "3.2.0",
65
+ "@asteby/metacore-ui": "2.4.1"
66
66
  },
67
67
  "scripts": {
68
68
  "build": "tsc -p tsconfig.json",
@@ -7,6 +7,8 @@ import {
7
7
  deriveRelationFormFields,
8
8
  diffSelection,
9
9
  extractSelectedTargetIds,
10
+ formatRelationCell,
11
+ objectLabel,
10
12
  pickOptionLabel,
11
13
  relationRowKey,
12
14
  } from '../dynamic-relation-helpers'
@@ -350,3 +352,66 @@ describe('pickOptionLabel', () => {
350
352
  expect(pickOptionLabel({ id: 1, name: { nested: true }, email: 'a@x' }, undefined, cols)).toBe('a@x')
351
353
  })
352
354
  })
355
+
356
+ describe('objectLabel', () => {
357
+ it('lee label de un sibling {value,label}', () => {
358
+ expect(objectLabel({ value: 'u1', label: 'Test' })).toBe('Test')
359
+ })
360
+
361
+ it('lee name de un objeto usuario y title como fallback', () => {
362
+ expect(objectLabel({ avatar: '', email: 'd@x', name: 'Danny Hernandez' })).toBe('Danny Hernandez')
363
+ expect(objectLabel({ title: 'Pedido 7' })).toBe('Pedido 7')
364
+ })
365
+
366
+ it('devuelve undefined para objetos vacíos, arrays o escalares', () => {
367
+ expect(objectLabel({})).toBeUndefined()
368
+ expect(objectLabel([1, 2])).toBeUndefined()
369
+ expect(objectLabel('x')).toBeUndefined()
370
+ expect(objectLabel(null)).toBeUndefined()
371
+ })
372
+ })
373
+
374
+ describe('formatRelationCell', () => {
375
+ const col = (key: string, type: ColumnDefinition['type'] = 'text'): ColumnDefinition =>
376
+ ({ key, label: key, type, sortable: false, filterable: false }) as ColumnDefinition
377
+ const NIL = '00000000-0000-0000-0000-000000000000'
378
+
379
+ it('prefiere el label del sibling resuelto de una FK *_id sobre el uuid crudo', () => {
380
+ const row = { product_id: '249915fe-aaaa', product: { value: '249915fe-aaaa', label: 'Test' } }
381
+ expect(formatRelationCell(row, col('product_id'))).toBe('Test')
382
+ })
383
+
384
+ it('usa un sibling string plano cuando no es objeto', () => {
385
+ expect(formatRelationCell({ product_id: 'x', product: 'Aceite' }, col('product_id'))).toBe('Aceite')
386
+ })
387
+
388
+ it('ignora el sibling cuando es nil-uuid o vacío y cae al valor', () => {
389
+ expect(formatRelationCell({ product_id: 'abc', product: NIL }, col('product_id'))).toBe('abc')
390
+ })
391
+
392
+ it('nil-uuid en la celda → "—" (FK nullable sin setear)', () => {
393
+ expect(formatRelationCell({ seller_id: NIL }, col('seller_id'))).toBe('—')
394
+ })
395
+
396
+ it('objeto usuario en la celda (created_by) → name, nunca JSON', () => {
397
+ const row = { created_by: { avatar: '', email: 'd@x', name: 'Danny Hernandez' } }
398
+ expect(formatRelationCell(row, col('created_by', 'creator'))).toBe('Danny Hernandez')
399
+ })
400
+
401
+ it('objeto {value,label} directo en la celda → label', () => {
402
+ expect(formatRelationCell({ status: { value: 'open', label: 'Abierto' } }, col('status'))).toBe('Abierto')
403
+ })
404
+
405
+ it('objeto sin label usable → "—", no [object Object] ni JSON', () => {
406
+ expect(formatRelationCell({ meta: { amount: 5 } }, col('meta'))).toBe('—')
407
+ })
408
+
409
+ it('escalares: booleans, números, strings y vacíos', () => {
410
+ expect(formatRelationCell({ taxable: true }, col('taxable', 'boolean'))).toBe('✓')
411
+ expect(formatRelationCell({ taxable: false }, col('taxable', 'boolean'))).toBe('—')
412
+ expect(formatRelationCell({ qty: 0 }, col('qty', 'number'))).toBe('0')
413
+ expect(formatRelationCell({ sku: 'ABC' }, col('sku'))).toBe('ABC')
414
+ expect(formatRelationCell({ sku: '' }, col('sku'))).toBe('—')
415
+ expect(formatRelationCell({ note: null }, col('note'))).toBe('—')
416
+ })
417
+ })
@@ -0,0 +1,42 @@
1
+ // Locks the nil-UUID guard shared by the table cell renderer and the detail
2
+ // view. The all-zeros UUID is the sentinel a backend emits for an unset
3
+ // nullable FK; the UI must read it as "no value", never as a string of zeros.
4
+ import { describe, it, expect } from 'vitest'
5
+ import { NIL_UUID, isNilUuid, normalizeNilUuid } from '../nil-uuid'
6
+
7
+ describe('isNilUuid', () => {
8
+ it('matches the canonical nil UUID', () => {
9
+ expect(isNilUuid(NIL_UUID)).toBe(true)
10
+ expect(isNilUuid('00000000-0000-0000-0000-000000000000')).toBe(true)
11
+ })
12
+
13
+ it('is tolerant of whitespace and case', () => {
14
+ expect(isNilUuid(' 00000000-0000-0000-0000-000000000000 ')).toBe(true)
15
+ expect(isNilUuid('00000000-0000-0000-0000-000000000000'.toUpperCase())).toBe(true)
16
+ })
17
+
18
+ it('does not match a real UUID', () => {
19
+ expect(isNilUuid('3f2504e0-4f89-11d3-9a0c-0305e82c3301')).toBe(false)
20
+ })
21
+
22
+ it('does not match non-string values', () => {
23
+ expect(isNilUuid(null)).toBe(false)
24
+ expect(isNilUuid(undefined)).toBe(false)
25
+ expect(isNilUuid(0)).toBe(false)
26
+ expect(isNilUuid('')).toBe(false)
27
+ expect(isNilUuid({ value: NIL_UUID })).toBe(false)
28
+ })
29
+ })
30
+
31
+ describe('normalizeNilUuid', () => {
32
+ it('maps the nil UUID to undefined', () => {
33
+ expect(normalizeNilUuid(NIL_UUID)).toBeUndefined()
34
+ })
35
+
36
+ it('passes through real values unchanged', () => {
37
+ expect(normalizeNilUuid('real-id')).toBe('real-id')
38
+ expect(normalizeNilUuid(42)).toBe(42)
39
+ expect(normalizeNilUuid(null)).toBeNull()
40
+ expect(normalizeNilUuid('')).toBe('')
41
+ })
42
+ })
@@ -50,4 +50,17 @@ describe('resolveRelationLabel', () => {
50
50
  expect(resolveRelationLabel(col({ ref: 'categories' }), {})).toBe('')
51
51
  expect(resolveRelationLabel(col({ ref: 'categories' }), { category_id: null })).toBe('')
52
52
  })
53
+
54
+ it('treats an unresolved nil UUID FK as empty, not a string of zeros', () => {
55
+ const row = { category_id: '00000000-0000-0000-0000-000000000000' }
56
+ expect(resolveRelationLabel(col({ ref: 'categories' }), row)).toBe('')
57
+ })
58
+
59
+ it('still prefers a resolved sibling label even if the FK id is the nil UUID', () => {
60
+ const row = {
61
+ category_id: '00000000-0000-0000-0000-000000000000',
62
+ category: { value: '00000000-0000-0000-0000-000000000000', label: 'Sin categoría' },
63
+ }
64
+ expect(resolveRelationLabel(col({ ref: 'categories' }), row)).toBe('Sin categoría')
65
+ })
53
66
  })
@@ -42,6 +42,7 @@ import { ExternalLink, Loader2, CalendarIcon, ChevronDown, Check, Upload, X as X
42
42
  import { useApi } from '../api-context'
43
43
  import { DynamicSelectField } from '../dynamic-select-field'
44
44
  import { getFieldRef } from '../dynamic-form-schema'
45
+ import { normalizeNilUuid } from '../nil-uuid'
45
46
  import type { ActionFieldDef } from '../types'
46
47
 
47
48
  interface FieldOption {
@@ -137,7 +138,9 @@ function resolvePath(obj: any, path: string): any {
137
138
  return path.split('.').reduce((acc, part) => acc?.[part], obj)
138
139
  }
139
140
 
140
- function formatDisplayValue(value: any, field: FieldDef): string {
141
+ function formatDisplayValue(rawValue: any, field: FieldDef): string {
142
+ // Unset nullable FK serialized as the nil UUID renders as empty, not zeros.
143
+ const value = normalizeNilUuid(rawValue)
141
144
  if (value === null || value === undefined || value === '') return '—'
142
145
  if (field.type === 'boolean' || typeof value === 'boolean') return value ? 'Sí' : 'No'
143
146
 
@@ -489,7 +492,10 @@ function FieldRow({ field, record, value, mode, onChange }: FieldRowProps) {
489
492
  )
490
493
  }
491
494
 
492
- function ViewValue({ field, value }: { field: FieldDef; value: any; record: any }) {
495
+ function ViewValue({ field, value: rawValue }: { field: FieldDef; value: any; record: any }) {
496
+ // Normalize the nil UUID to undefined up front so the search/url/color/
497
+ // image/select branches all fall through to their empty states.
498
+ const value = normalizeNilUuid(rawValue)
493
499
  if (field.type === 'search' && value) {
494
500
  return <SearchViewValue field={field} value={value} />
495
501
  }
@@ -44,6 +44,7 @@ import {
44
44
  import { Progress } from './dialogs/_primitives'
45
45
  import { OptionsContext } from './options-context'
46
46
  import { DynamicIcon } from './dynamic-icon'
47
+ import { isNilUuid, normalizeNilUuid } from './nil-uuid'
47
48
  import type { TableMetadata, ColumnDefinition } from './types'
48
49
  import { isColumnVisibleInTable } from './column-visibility'
49
50
  import type {
@@ -328,7 +329,9 @@ export const resolveRelationLabel = (col: ColumnDefinition, row: any): string =>
328
329
  : undefined
329
330
  if (label !== undefined && label !== null && label !== '') return String(label)
330
331
  const raw = getNestedValue(row, col.key)
331
- return raw !== undefined && raw !== null ? String(raw) : ''
332
+ // An unresolved FK that arrived as the nil UUID reads as empty, not zeros.
333
+ if (raw === undefined || raw === null || isNilUuid(raw)) return ''
334
+ return String(raw)
332
335
  }
333
336
 
334
337
  /**
@@ -476,7 +479,10 @@ export function makeDefaultGetDynamicColumns(
476
479
  <DataTableColumnHeader column={column} title={translatedLabel} />
477
480
  ),
478
481
  cell: ({ row }) => {
479
- const value = getNestedValue(row.original, col.key)
482
+ // Treat the nil UUID (unset nullable FK serialized as
483
+ // all-zeros) as no value, so every type below hits its
484
+ // existing empty branch instead of printing the zeros.
485
+ const value = normalizeNilUuid(getNestedValue(row.original, col.key))
480
486
  // Kernel emits the renderer flag as `type`; older hosts used
481
487
  // `cellStyle`. Accept both so a single backend works across
482
488
  // SDK versions.
@@ -2,9 +2,56 @@
2
2
  // run in node (no DOM, no metacore-ui primitives) and consumers can reuse the
3
3
  // URL/payload conventions outside the component.
4
4
  import type { ActionFieldDef, ColumnDefinition, TableMetadata } from './types'
5
+ import { isNilUuid } from './nil-uuid'
5
6
 
6
7
  export type DynamicRelationKind = 'one_to_many' | 'many_to_many'
7
8
 
9
+ // Pulls a human label off a resolved relation/user object a backend serves:
10
+ // `{ value, label }` (FK sibling), `{ name, … }` (user object such as
11
+ // created_by) or `{ title }`. Returns undefined for plain/empty objects so the
12
+ // caller falls through to its empty marker instead of leaking raw JSON.
13
+ export function objectLabel(value: unknown): string | undefined {
14
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined
15
+ const obj = value as Record<string, unknown>
16
+ const label = obj.label ?? obj.name ?? obj.title
17
+ return label != null && label !== '' ? String(label) : undefined
18
+ }
19
+
20
+ // formatRelationCell renders one DynamicRelation row cell. Beyond coercing
21
+ // scalars it resolves the pro siblings a backend serves so a line-item shows
22
+ // "Test", not a raw uuid or `{"label":"Test",…}` JSON:
23
+ // 1. an FK column (`product_id`) → the sibling `row.product = { value, label }`
24
+ // (the key with the trailing `_id` stripped), preferring its label/name;
25
+ // 2. a value that is itself a resolved object (`{ value, label }` / a user
26
+ // `{ name }`) → its label/name, never `JSON.stringify`;
27
+ // 3. the nil/zero UUID (unset nullable FK) → the empty marker "—".
28
+ export function formatRelationCell(row: Record<string, unknown>, col: ColumnDefinition): string {
29
+ const value = row[col.key]
30
+
31
+ // Prefer the backend-resolved FK sibling keyed by the column key with the
32
+ // trailing `_id` stripped (`product_id` → `row.product`).
33
+ if (col.key.endsWith('_id')) {
34
+ const sibling = row[col.key.slice(0, -3)]
35
+ const siblingLabel =
36
+ objectLabel(sibling) ??
37
+ (typeof sibling === 'string' && sibling !== '' && !isNilUuid(sibling) ? sibling : undefined)
38
+ if (siblingLabel !== undefined) return siblingLabel
39
+ }
40
+
41
+ if (value === null || value === undefined) return '—'
42
+ if (isNilUuid(value)) return '—'
43
+ if (typeof value === 'boolean') return value ? '✓' : '—'
44
+
45
+ // The cell value is itself a resolved relation/user object → its label/name.
46
+ const inlineLabel = objectLabel(value)
47
+ if (inlineLabel !== undefined) return inlineLabel
48
+ // An object with no usable label (would JSON.stringify) → empty marker.
49
+ if (typeof value === 'object') return '—'
50
+
51
+ const text = String(value)
52
+ return text === '' ? '—' : text
53
+ }
54
+
8
55
  export interface PivotRowLike {
9
56
  id?: string | number | null
10
57
  [k: string]: unknown
@@ -35,6 +35,7 @@ import {
35
35
  deriveRelationFormFields,
36
36
  diffSelection,
37
37
  extractSelectedTargetIds,
38
+ formatRelationCell,
38
39
  pickOptionLabel,
39
40
  relationRowKey,
40
41
  type DynamicRelationKind,
@@ -49,6 +50,8 @@ export {
49
50
  deriveRelationFormFields,
50
51
  diffSelection,
51
52
  extractSelectedTargetIds,
53
+ formatRelationCell,
54
+ objectLabel,
52
55
  pickOptionLabel,
53
56
  relationRowKey,
54
57
  } from './dynamic-relation-helpers'
@@ -300,11 +303,14 @@ function OneToManyRelation({
300
303
  className="flex items-center justify-between gap-3 px-3 py-2"
301
304
  >
302
305
  <div className="flex-1 grid grid-cols-[repeat(auto-fit,minmax(0,1fr))] gap-2 text-sm">
303
- {visibleColumns.map(col => (
304
- <span key={col.key} className="truncate" title={String(row[col.key] ?? '')}>
305
- {formatCell(row[col.key])}
306
- </span>
307
- ))}
306
+ {visibleColumns.map(col => {
307
+ const cell = formatRelationCell(row, col)
308
+ return (
309
+ <span key={col.key} className="truncate" title={cell}>
310
+ {cell}
311
+ </span>
312
+ )
313
+ })}
308
314
  </div>
309
315
  <div className="flex items-center gap-1 shrink-0">
310
316
  {canEdit && (
@@ -372,13 +378,6 @@ function OneToManyRelation({
372
378
  )
373
379
  }
374
380
 
375
- function formatCell(value: unknown): string {
376
- if (value === null || value === undefined) return '—'
377
- if (typeof value === 'boolean') return value ? '✓' : '—'
378
- if (typeof value === 'object') return JSON.stringify(value)
379
- return String(value)
380
- }
381
-
382
381
  function ManyToManyRelation({
383
382
  kind,
384
383
  through,
package/src/index.ts CHANGED
@@ -66,6 +66,7 @@ export {
66
66
  resolveRelationLabel,
67
67
  type DynamicColumnsHelpers,
68
68
  } from './dynamic-columns'
69
+ export { NIL_UUID, isNilUuid, normalizeNilUuid } from './nil-uuid'
69
70
  export { DynamicRecordDialog } from './dialogs/dynamic-record'
70
71
  export { CreateRecordDialog } from './dialogs/create-record-dialog'
71
72
  export { ViewRecordDialog } from './dialogs/view-record-dialog'
@@ -0,0 +1,25 @@
1
+ // Shared guard for the "nil UUID" — the all-zeros UUID Postgres/Go emit for a
2
+ // nullable FK that was never set (`00000000-0000-0000-0000-000000000000`).
3
+ // Backends sometimes serialize an unset `uuid` column as this sentinel instead
4
+ // of `null`, which then leaks into the UI as a long string of zeros. Treat it
5
+ // as "no value" so cell/detail renderers fall through to their existing empty
6
+ // markers ("-" / "—"). Cosmetic defense-in-depth — the backend should emit null.
7
+
8
+ /** The canonical nil/zero UUID sentinel. */
9
+ export const NIL_UUID = '00000000-0000-0000-0000-000000000000'
10
+
11
+ /**
12
+ * True when `value` is the nil UUID string. Tolerant of surrounding whitespace
13
+ * and letter-case (UUIDs are conventionally lowercase, but be defensive). Only
14
+ * matches strings — numeric/object values are never the nil UUID.
15
+ */
16
+ export const isNilUuid = (value: unknown): boolean =>
17
+ typeof value === 'string' && value.trim().toLowerCase() === NIL_UUID
18
+
19
+ /**
20
+ * Normalizes a raw cell value: returns `undefined` when it is the nil UUID so
21
+ * downstream renderers hit their existing nullish/empty branches; otherwise the
22
+ * value is passed through unchanged.
23
+ */
24
+ export const normalizeNilUuid = <T>(value: T): T | undefined =>
25
+ isNilUuid(value) ? undefined : value