@object-ui/plugin-detail 3.1.1 → 3.1.3

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 (65) hide show
  1. package/.turbo/turbo-build.log +43 -41
  2. package/CHANGELOG.md +20 -0
  3. package/dist/{AddressField-B1iVr404.js → AddressField-BtiTrEpf.js} +1 -1
  4. package/dist/{AvatarField-Duw4xOLZ.js → AvatarField-CwlnWNSf.js} +7 -7
  5. package/dist/{BooleanField-CZ4axVeq.js → BooleanField-DpMXU2ya.js} +1 -1
  6. package/dist/{CodeField-BSz-mk2v.js → CodeField-gwmcFihg.js} +2 -2
  7. package/dist/{ColorField-B522ad8m.js → ColorField-CWmF_zoW.js} +1 -1
  8. package/dist/{CurrencyField-Cwr3_pow.js → CurrencyField-BF3tYAgm.js} +1 -1
  9. package/dist/{DateField-DCo6dxud.js → DateField-a6Ka9ph2.js} +1 -1
  10. package/dist/{DateTimeField-BWfBuANO.js → DateTimeField-C4wWOEiw.js} +1 -1
  11. package/dist/{EmailField-CpwbdVCU.js → EmailField-DJqiQ4sp.js} +1 -1
  12. package/dist/{FileField-DVAUAJ8e.js → FileField-ChjjCydz.js} +1 -1
  13. package/dist/{GeolocationField-DNCKitgo.js → GeolocationField-BnkeUBek.js} +1 -1
  14. package/dist/{GridField-DSblZNfp.js → GridField-DoHqc2ON.js} +6 -6
  15. package/dist/{ImageField-DBAlnMon.js → ImageField-Ld7SHA8N.js} +1 -1
  16. package/dist/{LocationField-DsHsXA6R.js → LocationField-Bgu-vMAE.js} +1 -1
  17. package/dist/{MasterDetailField-Db8b7Gqs.js → MasterDetailField-Bp5WBTzU.js} +3 -3
  18. package/dist/{NumberField-0IGp7lcA.js → NumberField-uBqVZ-gt.js} +1 -1
  19. package/dist/{ObjectField-BLApgJtS.js → ObjectField-BH1Md9gH.js} +6 -6
  20. package/dist/{PasswordField-pHKyNlmo.js → PasswordField-D8GZjY7d.js} +1 -1
  21. package/dist/{PercentField-CwgKmlIb.js → PercentField-DyK8vg8M.js} +1 -1
  22. package/dist/{PhoneField-lKtbYOdN.js → PhoneField-B3qJyLP0.js} +1 -1
  23. package/dist/{QRCodeField-BTTasT3w.js → QRCodeField-CGiRTCZq.js} +1 -1
  24. package/dist/{RatingField-De2X-l44.js → RatingField-CWVaJNyf.js} +4 -4
  25. package/dist/{RichTextField-B5QnvUOr.js → RichTextField-CusveP9T.js} +1 -1
  26. package/dist/{SelectField-C9AZRHWu.js → SelectField-UdDfsEZo.js} +1 -1
  27. package/dist/{SignatureField-BgcEmYzd.js → SignatureField-DFvPKbuI.js} +1 -1
  28. package/dist/{SliderField-BzrttVOY.js → SliderField-C-HvGV9e.js} +1 -1
  29. package/dist/{TextAreaField-DSE_CaU6.js → TextAreaField-C5KygUT3.js} +1 -1
  30. package/dist/{TextField-DFQ4T9PR.js → TextField-oUjuqQ1x.js} +1 -1
  31. package/dist/{TimeField-F0cfmsps.js → TimeField-SsQ6rfk5.js} +1 -1
  32. package/dist/{UrlField-DLXrFIH-.js → UrlField-kd48Ip95.js} +1 -1
  33. package/dist/{UserField-PXMmxJY9.js → UserField-BOjE_CAz.js} +11 -11
  34. package/dist/index-D2t9pLAg.js +99948 -0
  35. package/dist/index.js +10 -10
  36. package/dist/index.umd.cjs +118 -49
  37. package/dist/plugin-detail.css +1 -1
  38. package/dist/src/DetailSection.d.ts +19 -0
  39. package/dist/src/DetailSection.d.ts.map +1 -1
  40. package/dist/src/DetailView.d.ts.map +1 -1
  41. package/dist/src/HeaderHighlight.d.ts +2 -0
  42. package/dist/src/HeaderHighlight.d.ts.map +1 -1
  43. package/dist/src/RecordChatterPanel.d.ts +2 -0
  44. package/dist/src/RecordChatterPanel.d.ts.map +1 -1
  45. package/dist/src/autoLayout.d.ts +10 -3
  46. package/dist/src/autoLayout.d.ts.map +1 -1
  47. package/dist/src/index.d.ts +1 -1
  48. package/dist/src/index.d.ts.map +1 -1
  49. package/package.json +7 -7
  50. package/src/DetailSection.tsx +79 -22
  51. package/src/DetailView.tsx +16 -9
  52. package/src/HeaderHighlight.tsx +22 -1
  53. package/src/RecordChatterPanel.tsx +6 -1
  54. package/src/RelatedList.tsx +1 -1
  55. package/src/__tests__/DetailSection.test.tsx +171 -1
  56. package/src/__tests__/DetailView.test.tsx +31 -0
  57. package/src/__tests__/HeaderHighlight.test.tsx +145 -0
  58. package/src/__tests__/RecordChatterPanel.test.tsx +38 -0
  59. package/src/__tests__/RelatedList.test.tsx +3 -3
  60. package/src/__tests__/autoLayout.test.ts +44 -0
  61. package/src/autoLayout.ts +25 -8
  62. package/src/index.tsx +1 -1
  63. package/dist/LookupField-CsT0QQz2.js +0 -96
  64. package/dist/index-qQ1C-yUR.js +0 -59976
  65. package/src/registration.test.tsx +0 -18
@@ -7,6 +7,23 @@ import { DetailViewSection as DetailViewSectionType } from '../../types/src';
7
7
  * LICENSE file in the root directory of this source tree.
8
8
  */
9
9
  import * as React from 'react';
10
+ /**
11
+ * Compute responsive col-span classes so that col-span never exceeds the
12
+ * visible column count at each Tailwind breakpoint.
13
+ *
14
+ * For columns=1: no span class (always single column)
15
+ * For columns=2: md:col-span-{min(span,2)}
16
+ * For columns>=3: md:col-span-{min(span,2)} lg:col-span-{min(span,3)}
17
+ */
18
+ export declare function getResponsiveSpanClass(span: number | undefined, columns: number): string;
19
+ export interface VirtualScrollOptions {
20
+ /** Enable virtual scrolling for large field sets */
21
+ enabled?: boolean;
22
+ /** Height of each field row in px (default: 60) */
23
+ itemHeight?: number;
24
+ /** Number of fields to render in the initial batch before revealing all (default: 20) */
25
+ batchSize?: number;
26
+ }
10
27
  export interface DetailSectionProps {
11
28
  section: DetailViewSectionType;
12
29
  data?: any;
@@ -19,6 +36,8 @@ export interface DetailSectionProps {
19
36
  isEditing?: boolean;
20
37
  /** Callback when a field value changes during inline editing */
21
38
  onFieldChange?: (field: string, value: any) => void;
39
+ /** Virtual scrolling configuration for sections with many fields */
40
+ virtualScroll?: VirtualScrollOptions;
22
41
  }
23
42
  export declare const DetailSection: React.FC<DetailSectionProps>;
24
43
  //# sourceMappingURL=DetailSection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DetailSection.d.ts","sourceRoot":"","sources":["../../src/DetailSection.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAoB/B,OAAO,KAAK,EAAE,iBAAiB,IAAI,qBAAqB,EAAkC,MAAM,kBAAkB,CAAC;AAKnH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,qBAAqB,CAAC;IAC/B,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,GAAG,CAAC;IACnB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gEAAgE;IAChE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CACrD;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA2OtD,CAAC"}
1
+ {"version":3,"file":"DetailSection.d.ts","sourceRoot":"","sources":["../../src/DetailSection.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAoB/B,OAAO,KAAK,EAAE,iBAAiB,IAAI,qBAAqB,EAAkC,MAAM,kBAAkB,CAAC;AAKnH;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAYxF;AAED,MAAM,WAAW,oBAAoB;IACnC,oDAAoD;IACpD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,qBAAqB,CAAC;IAC/B,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,GAAG,CAAC;IACnB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gEAAgE;IAChE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IACpD,oEAAoE;IACpE,aAAa,CAAC,EAAE,oBAAoB,CAAC;CACtC;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAmQtD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"DetailView.d.ts","sourceRoot":"","sources":["../../src/DetailView.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA4C/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAMrE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,qDAAqD;IACrD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kDAAkD;IAClD,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChF;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAwuBhD,CAAC"}
1
+ {"version":3,"file":"DetailView.d.ts","sourceRoot":"","sources":["../../src/DetailView.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA4C/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAMrE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,qDAAqD;IACrD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kDAAkD;IAClD,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChF;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA+uBhD,CAAC"}
@@ -13,6 +13,8 @@ export interface HeaderHighlightProps {
13
13
  className?: string;
14
14
  /** Object name for i18n field label resolution */
15
15
  objectName?: string;
16
+ /** Object schema for field metadata enrichment */
17
+ objectSchema?: any;
16
18
  }
17
19
  export declare const HeaderHighlight: React.FC<HeaderHighlightProps>;
18
20
  //# sourceMappingURL=HeaderHighlight.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"HeaderHighlight.d.ts","sourceRoot":"","sources":["../../src/HeaderHighlight.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA6C1D,CAAC"}
1
+ {"version":3,"file":"HeaderHighlight.d.ts","sourceRoot":"","sources":["../../src/HeaderHighlight.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIvD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,YAAY,CAAC,EAAE,GAAG,CAAC;CACpB;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA+D1D,CAAC"}
@@ -33,6 +33,8 @@ export interface RecordChatterPanelProps {
33
33
  filterMode?: FeedFilterMode;
34
34
  /** Called when filter changes */
35
35
  onFilterChange?: (mode: FeedFilterMode) => void;
36
+ /** When true, auto-collapse panel when there are no feed items */
37
+ collapseWhenEmpty?: boolean;
36
38
  className?: string;
37
39
  }
38
40
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"RecordChatterPanel.d.ts","sourceRoot":"","sources":["../../src/RecordChatterPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,2BAA2B,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAElG,OAAO,KAAK,EAAE,cAAc,EAA+B,MAAM,0BAA0B,CAAC;AAE5F,MAAM,WAAW,uBAAuB;IACtC,mEAAmE;IACnE,MAAM,CAAC,EAAE,2BAA2B,CAAC;IACrC,qDAAqD;IACrD,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,oBAAoB;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yCAAyC;IACzC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,uCAAuC;IACvC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpF,yBAAyB;IACzB,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,4CAA4C;IAC5C,oBAAoB,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,kBAAkB;IAClB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,iCAAiC;IACjC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAqJhE,CAAC"}
1
+ {"version":3,"file":"RecordChatterPanel.d.ts","sourceRoot":"","sources":["../../src/RecordChatterPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,2BAA2B,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAElG,OAAO,KAAK,EAAE,cAAc,EAA+B,MAAM,0BAA0B,CAAC;AAE5F,MAAM,WAAW,uBAAuB;IACtC,mEAAmE;IACnE,MAAM,CAAC,EAAE,2BAA2B,CAAC;IACrC,qDAAqD;IACrD,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,oBAAoB;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yCAAyC;IACzC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,uCAAuC;IACvC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpF,yBAAyB;IACzB,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,4CAA4C;IAC5C,oBAAoB,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,kBAAkB;IAClB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,iCAAiC;IACjC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAChD,kEAAkE;IAClE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAwJhE,CAAC"}
@@ -5,13 +5,19 @@ import { DetailViewField } from '../../types/src';
5
5
  export declare function isWideFieldType(type: string): boolean;
6
6
  /**
7
7
  * Infer optimal number of columns for a detail section based on field count.
8
+ * When containerWidth is provided, limits columns for narrower viewports.
8
9
  *
9
- * Rules:
10
+ * Rules (field-count based):
10
11
  * - 0-3 fields → 1 column
11
12
  * - 4-10 fields → 2 columns
12
13
  * - 11+ fields → 3 columns
14
+ *
15
+ * Responsive capping (when containerWidth is supplied):
16
+ * - containerWidth < 640px → max 1 column
17
+ * - containerWidth < 900px → max 2 columns
18
+ * - containerWidth >= 900px → no cap
13
19
  */
14
- export declare function inferDetailColumns(fieldCount: number): number;
20
+ export declare function inferDetailColumns(fieldCount: number, containerWidth?: number): number;
15
21
  /**
16
22
  * Apply auto span to wide fields so they span the full row.
17
23
  * Only sets span if the field does not already have one explicitly set.
@@ -25,9 +31,10 @@ export declare function applyAutoSpan(fields: DetailViewField[], columns: number
25
31
  *
26
32
  * @param fields - The section fields
27
33
  * @param schemaColumns - User-provided columns (from DetailViewSection or DetailViewSchema)
34
+ * @param containerWidth - Optional container width in px for responsive column capping
28
35
  * @returns Object with processed fields and inferred columns
29
36
  */
30
- export declare function applyDetailAutoLayout(fields: DetailViewField[], schemaColumns: number | undefined): {
37
+ export declare function applyDetailAutoLayout(fields: DetailViewField[], schemaColumns: number | undefined, containerWidth?: number): {
31
38
  fields: DetailViewField[];
32
39
  columns: number;
33
40
  };
@@ -1 +1 @@
1
- {"version":3,"file":"autoLayout.d.ts","sourceRoot":"","sources":["../../src/autoLayout.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAgBxD;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAI7D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,eAAe,EAAE,EACzB,OAAO,EAAE,MAAM,GACd,eAAe,EAAE,CAcnB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,eAAe,EAAE,EACzB,aAAa,EAAE,MAAM,GAAG,SAAS,GAChC;IAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAchD"}
1
+ {"version":3,"file":"autoLayout.d.ts","sourceRoot":"","sources":["../../src/autoLayout.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAgBxD;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAatF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,eAAe,EAAE,EACzB,OAAO,EAAE,MAAM,GACd,eAAe,EAAE,CAcnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,eAAe,EAAE,EACzB,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,cAAc,CAAC,EAAE,MAAM,GACtB;IAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAchD"}
@@ -25,7 +25,7 @@ export { SubscriptionToggle } from './SubscriptionToggle';
25
25
  export { ReactionPicker } from './ReactionPicker';
26
26
  export { ThreadedReplies } from './ThreadedReplies';
27
27
  export type { DetailViewProps } from './DetailView';
28
- export type { DetailSectionProps } from './DetailSection';
28
+ export type { DetailSectionProps, VirtualScrollOptions } from './DetailSection';
29
29
  export type { DetailTabsProps } from './DetailTabs';
30
30
  export type { RelatedListProps } from './RelatedList';
31
31
  export type { SectionGroupProps } from './SectionGroup';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACzG,OAAO,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACtH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,YAAY,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACpF,YAAY,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACnH,YAAY,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3F,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACnF,YAAY,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AAChF,YAAY,EAAE,sBAAsB,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC7E,YAAY,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC9E,YAAY,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACnF,YAAY,EAAE,2BAA2B,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC5F,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACpE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,YAAY,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC7F,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACpE,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/plugin-detail",
3
- "version": "3.1.1",
3
+ "version": "3.1.3",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "DetailView plugin for Object UI - comprehensive detail page with sections, tabs, and related lists",
@@ -24,12 +24,12 @@
24
24
  }
25
25
  },
26
26
  "dependencies": {
27
- "lucide-react": "^0.576.0",
28
- "@object-ui/components": "3.1.1",
29
- "@object-ui/fields": "3.1.1",
30
- "@object-ui/core": "3.1.1",
31
- "@object-ui/react": "3.1.1",
32
- "@object-ui/types": "3.1.1"
27
+ "lucide-react": "^0.577.0",
28
+ "@object-ui/components": "3.1.3",
29
+ "@object-ui/core": "3.1.3",
30
+ "@object-ui/fields": "3.1.3",
31
+ "@object-ui/react": "3.1.3",
32
+ "@object-ui/types": "3.1.3"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "react": "^18.0.0 || ^19.0.0",
@@ -31,6 +31,37 @@ import { applyDetailAutoLayout } from './autoLayout';
31
31
  import { useDetailTranslation } from './useDetailTranslation';
32
32
  import { useSafeFieldLabel } from '@object-ui/react';
33
33
 
34
+ /**
35
+ * Compute responsive col-span classes so that col-span never exceeds the
36
+ * visible column count at each Tailwind breakpoint.
37
+ *
38
+ * For columns=1: no span class (always single column)
39
+ * For columns=2: md:col-span-{min(span,2)}
40
+ * For columns>=3: md:col-span-{min(span,2)} lg:col-span-{min(span,3)}
41
+ */
42
+ export function getResponsiveSpanClass(span: number | undefined, columns: number): string {
43
+ if (!span || span <= 1 || columns <= 1) return '';
44
+
45
+ if (columns === 2) {
46
+ return span >= 2 ? 'md:col-span-2' : '';
47
+ }
48
+
49
+ // columns >= 3: grid-cols-1 md:grid-cols-2 lg:grid-cols-3
50
+ if (span === 2) return 'md:col-span-2';
51
+ if (span >= 3) return 'md:col-span-2 lg:col-span-3';
52
+
53
+ return '';
54
+ }
55
+
56
+ export interface VirtualScrollOptions {
57
+ /** Enable virtual scrolling for large field sets */
58
+ enabled?: boolean;
59
+ /** Height of each field row in px (default: 60) */
60
+ itemHeight?: number;
61
+ /** Number of fields to render in the initial batch before revealing all (default: 20) */
62
+ batchSize?: number;
63
+ }
64
+
34
65
  export interface DetailSectionProps {
35
66
  section: DetailViewSectionType;
36
67
  data?: any;
@@ -43,6 +74,8 @@ export interface DetailSectionProps {
43
74
  isEditing?: boolean;
44
75
  /** Callback when a field value changes during inline editing */
45
76
  onFieldChange?: (field: string, value: any) => void;
77
+ /** Virtual scrolling configuration for sections with many fields */
78
+ virtualScroll?: VirtualScrollOptions;
46
79
  }
47
80
 
48
81
  export const DetailSection: React.FC<DetailSectionProps> = ({
@@ -53,9 +86,11 @@ export const DetailSection: React.FC<DetailSectionProps> = ({
53
86
  objectName,
54
87
  isEditing = false,
55
88
  onFieldChange,
89
+ virtualScroll,
56
90
  }) => {
57
91
  const [isCollapsed, setIsCollapsed] = React.useState(section.defaultCollapsed ?? false);
58
92
  const [copiedField, setCopiedField] = React.useState<string | null>(null);
93
+ const [visibleCount, setVisibleCount] = React.useState<number | undefined>(undefined);
59
94
  const { t } = useDetailTranslation();
60
95
  const { fieldLabel } = useSafeFieldLabel();
61
96
 
@@ -67,6 +102,23 @@ export const DetailSection: React.FC<DetailSectionProps> = ({
67
102
  });
68
103
  }, []);
69
104
 
105
+ // Filter out empty fields when hideEmpty is set
106
+ const visibleFields = section.hideEmpty
107
+ ? section.fields.filter((field) => {
108
+ const value = data?.[field.name] ?? field.value;
109
+ return value !== null && value !== undefined && value !== '';
110
+ })
111
+ : section.fields;
112
+
113
+ // Hide entire section when all fields are empty
114
+ if (visibleFields.length === 0) return null;
115
+
116
+ // Apply auto-layout: infer columns and auto-span wide fields
117
+ const { fields: layoutFields, columns: effectiveColumns } = applyDetailAutoLayout(
118
+ visibleFields,
119
+ section.columns
120
+ );
121
+
70
122
  const renderField = (field: DetailViewField) => {
71
123
  const value = data?.[field.name] ?? field.value;
72
124
 
@@ -75,13 +127,9 @@ export const DetailSection: React.FC<DetailSectionProps> = ({
75
127
  return <SchemaRenderer schema={field.render} data={{ ...data, value }} />;
76
128
  }
77
129
 
78
- // Calculate span class based on field.span value
79
- const spanClass = field.span === 1 ? 'col-span-1' :
80
- field.span === 2 ? 'col-span-2' :
81
- field.span === 3 ? 'col-span-3' :
82
- field.span === 4 ? 'col-span-4' :
83
- field.span === 5 ? 'col-span-5' :
84
- field.span === 6 ? 'col-span-6' : '';
130
+ // Calculate responsive span class so col-span never exceeds the visible
131
+ // column count at each breakpoint, preventing implicit columns on mobile.
132
+ const spanClass = getResponsiveSpanClass(field.span, effectiveColumns);
85
133
 
86
134
  const displayValue = (() => {
87
135
  if (value === null || value === undefined) return <span className="text-muted-foreground/50 text-xs italic">—</span>;
@@ -178,22 +226,31 @@ export const DetailSection: React.FC<DetailSectionProps> = ({
178
226
  );
179
227
  };
180
228
 
181
- // Filter out empty fields when hideEmpty is set
182
- const visibleFields = section.hideEmpty
183
- ? section.fields.filter((field) => {
184
- const value = data?.[field.name] ?? field.value;
185
- return value !== null && value !== undefined && value !== '';
186
- })
187
- : section.fields;
229
+ // Virtual scroll: progressive batch rendering for large field sets
230
+ const vsEnabled = virtualScroll?.enabled === true;
231
+ const vsBatchSize = virtualScroll?.batchSize ?? 20;
232
+ /** Delay (ms) before revealing remaining fields after the initial batch */
233
+ const VS_REVEAL_DELAY = 100;
188
234
 
189
- // Hide entire section when all fields are empty
190
- if (visibleFields.length === 0) return null;
235
+ React.useEffect(() => {
236
+ if (!vsEnabled) {
237
+ setVisibleCount(undefined);
238
+ return;
239
+ }
240
+ // Start with a batch, then progressively reveal more
241
+ if (layoutFields.length <= vsBatchSize) {
242
+ setVisibleCount(undefined);
243
+ return;
244
+ }
245
+ setVisibleCount(vsBatchSize);
246
+ const timer = setTimeout(() => setVisibleCount(undefined), VS_REVEAL_DELAY);
247
+ return () => clearTimeout(timer);
248
+ // eslint-disable-next-line react-hooks/exhaustive-deps
249
+ }, [vsEnabled, layoutFields.length, vsBatchSize]);
191
250
 
192
- // Apply auto-layout: infer columns and auto-span wide fields
193
- const { fields: layoutFields, columns: effectiveColumns } = applyDetailAutoLayout(
194
- visibleFields,
195
- section.columns
196
- );
251
+ const renderedFields = visibleCount !== undefined
252
+ ? layoutFields.slice(0, visibleCount)
253
+ : layoutFields;
197
254
 
198
255
  const content = (
199
256
  <div
@@ -205,7 +262,7 @@ export const DetailSection: React.FC<DetailSectionProps> = ({
205
262
  "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
206
263
  )}
207
264
  >
208
- {layoutFields.map(renderField)}
265
+ {renderedFields.map(renderField)}
209
266
  </div>
210
267
  );
211
268
 
@@ -130,14 +130,8 @@ export const DetailView: React.FC<DetailViewProps> = ({
130
130
  ? dataSource.findOne(objectName, resourceId, params)
131
131
  : dataSource.findOne(objectName, resourceId);
132
132
 
133
- return findOnePromise.then((result) => {
134
- if (!isMounted) return;
135
- if (result) {
136
- setData(result);
137
- setLoading(false);
138
- return;
139
- }
140
- // Fallback: try alternate ID format for backward compatibility
133
+ // Helper: try alternate ID format (strip or prepend objectName prefix)
134
+ const tryAltId = () => {
141
135
  const resIdStr = String(resourceId);
142
136
  const altId = resIdStr.startsWith(prefix)
143
137
  ? resIdStr.slice(prefix.length) // strip prefix
@@ -156,6 +150,19 @@ export const DetailView: React.FC<DetailViewProps> = ({
156
150
  setLoading(false);
157
151
  }
158
152
  });
153
+ };
154
+
155
+ return findOnePromise
156
+ .catch(() => null) // Convert any error to null to trigger alternate ID fallback
157
+ .then((result) => {
158
+ if (!isMounted) return;
159
+ if (result) {
160
+ setData(result);
161
+ setLoading(false);
162
+ return;
163
+ }
164
+ // Fallback: try alternate ID format for backward compatibility
165
+ return tryAltId();
159
166
  });
160
167
  }).catch((err) => {
161
168
  if (isMounted) {
@@ -593,7 +600,7 @@ export const DetailView: React.FC<DetailViewProps> = ({
593
600
 
594
601
  {/* Header Highlight Area */}
595
602
  {schema.highlightFields && schema.highlightFields.length > 0 && (
596
- <HeaderHighlight fields={schema.highlightFields} data={data} objectName={schema.objectName} />
603
+ <HeaderHighlight fields={schema.highlightFields} data={data} objectName={schema.objectName} objectSchema={objectSchema} />
597
604
  )}
598
605
 
599
606
  {/* Auto Tabs mode: wrap sections, related, activity into tabs */}
@@ -9,6 +9,7 @@
9
9
  import * as React from 'react';
10
10
  import { cn, Card, CardContent } from '@object-ui/components';
11
11
  import type { HighlightField } from '@object-ui/types';
12
+ import { getCellRenderer } from '@object-ui/fields';
12
13
  import { useSafeFieldLabel } from '@object-ui/react';
13
14
 
14
15
  export interface HeaderHighlightProps {
@@ -17,6 +18,8 @@ export interface HeaderHighlightProps {
17
18
  className?: string;
18
19
  /** Object name for i18n field label resolution */
19
20
  objectName?: string;
21
+ /** Object schema for field metadata enrichment */
22
+ objectSchema?: any;
20
23
  }
21
24
 
22
25
  export const HeaderHighlight: React.FC<HeaderHighlightProps> = ({
@@ -24,6 +27,7 @@ export const HeaderHighlight: React.FC<HeaderHighlightProps> = ({
24
27
  data,
25
28
  className,
26
29
  objectName,
30
+ objectSchema,
27
31
  }) => {
28
32
  const { fieldLabel } = useSafeFieldLabel();
29
33
  if (!fields.length || !data) return null;
@@ -48,6 +52,23 @@ export const HeaderHighlight: React.FC<HeaderHighlightProps> = ({
48
52
  )}>
49
53
  {visibleFields.map((field) => {
50
54
  const value = data[field.name];
55
+ // Enrich field metadata from objectSchema
56
+ const objectDefField = objectSchema?.fields?.[field.name];
57
+ const resolvedType = field.type || objectDefField?.type;
58
+ const enrichedField = {
59
+ name: field.name,
60
+ label: field.label,
61
+ type: resolvedType || 'text',
62
+ ...(objectDefField?.options && { options: objectDefField.options }),
63
+ ...(objectDefField?.currency && { currency: objectDefField.currency }),
64
+ ...(objectDefField?.precision !== undefined && { precision: objectDefField.precision }),
65
+ ...(objectDefField?.format && { format: objectDefField.format }),
66
+ };
67
+
68
+ // Use type-aware cell renderer — all renderers coerce values via
69
+ // coerceToSafeValue() so even object/array data is safe (no error #310).
70
+ const CellRenderer = getCellRenderer(resolvedType || 'text');
71
+
51
72
  return (
52
73
  <div key={field.name} className="flex flex-col gap-0.5">
53
74
  <span className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
@@ -55,7 +76,7 @@ export const HeaderHighlight: React.FC<HeaderHighlightProps> = ({
55
76
  {fieldLabel(objectName || '', field.name, field.label)}
56
77
  </span>
57
78
  <span className="text-sm font-semibold truncate">
58
- {String(value)}
79
+ <CellRenderer value={value} field={enrichedField as any} />
59
80
  </span>
60
81
  </div>
61
82
  );
@@ -38,6 +38,8 @@ export interface RecordChatterPanelProps {
38
38
  filterMode?: FeedFilterMode;
39
39
  /** Called when filter changes */
40
40
  onFilterChange?: (mode: FeedFilterMode) => void;
41
+ /** When true, auto-collapse panel when there are no feed items */
42
+ collapseWhenEmpty?: boolean;
41
43
  className?: string;
42
44
  }
43
45
 
@@ -63,12 +65,13 @@ export const RecordChatterPanel: React.FC<RecordChatterPanelProps> = ({
63
65
  onToggleSubscription,
64
66
  filterMode,
65
67
  onFilterChange,
68
+ collapseWhenEmpty = false,
66
69
  className,
67
70
  }) => {
68
71
  const position = config?.position ?? 'right';
69
72
  const width = config?.width ?? '360px';
70
73
  const collapsible = config?.collapsible ?? true;
71
- const defaultCollapsed = config?.defaultCollapsed ?? false;
74
+ const defaultCollapsed = (collapseWhenEmpty && items.length === 0) || (config?.defaultCollapsed ?? false);
72
75
 
73
76
  const [collapsed, setCollapsed] = React.useState(defaultCollapsed);
74
77
 
@@ -142,6 +145,7 @@ export const RecordChatterPanel: React.FC<RecordChatterPanelProps> = ({
142
145
  onToggleSubscription={onToggleSubscription}
143
146
  filterMode={filterMode}
144
147
  onFilterChange={onFilterChange}
148
+ collapseWhenEmpty={collapseWhenEmpty}
145
149
  className="border-0 shadow-none"
146
150
  />
147
151
  </div>
@@ -194,6 +198,7 @@ export const RecordChatterPanel: React.FC<RecordChatterPanelProps> = ({
194
198
  onToggleSubscription={onToggleSubscription}
195
199
  filterMode={filterMode}
196
200
  onFilterChange={onFilterChange}
201
+ collapseWhenEmpty={collapseWhenEmpty}
197
202
  />
198
203
  </div>
199
204
  )}
@@ -197,7 +197,7 @@ export const RelatedList: React.FC<RelatedListProps> = ({
197
197
  if (!objectSchema?.fields) return [];
198
198
  const resolvedObjectName = objectName || api || '';
199
199
  return Object.entries(objectSchema.fields)
200
- .filter(([key]) => !key.startsWith('_'))
200
+ .filter(([key]) => !key.startsWith('_') && key !== 'id')
201
201
  .map(([key, def]: [string, any]) => {
202
202
  const col: any = {
203
203
  accessorKey: key,