@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.
- package/.turbo/turbo-build.log +43 -41
- package/CHANGELOG.md +20 -0
- package/dist/{AddressField-B1iVr404.js → AddressField-BtiTrEpf.js} +1 -1
- package/dist/{AvatarField-Duw4xOLZ.js → AvatarField-CwlnWNSf.js} +7 -7
- package/dist/{BooleanField-CZ4axVeq.js → BooleanField-DpMXU2ya.js} +1 -1
- package/dist/{CodeField-BSz-mk2v.js → CodeField-gwmcFihg.js} +2 -2
- package/dist/{ColorField-B522ad8m.js → ColorField-CWmF_zoW.js} +1 -1
- package/dist/{CurrencyField-Cwr3_pow.js → CurrencyField-BF3tYAgm.js} +1 -1
- package/dist/{DateField-DCo6dxud.js → DateField-a6Ka9ph2.js} +1 -1
- package/dist/{DateTimeField-BWfBuANO.js → DateTimeField-C4wWOEiw.js} +1 -1
- package/dist/{EmailField-CpwbdVCU.js → EmailField-DJqiQ4sp.js} +1 -1
- package/dist/{FileField-DVAUAJ8e.js → FileField-ChjjCydz.js} +1 -1
- package/dist/{GeolocationField-DNCKitgo.js → GeolocationField-BnkeUBek.js} +1 -1
- package/dist/{GridField-DSblZNfp.js → GridField-DoHqc2ON.js} +6 -6
- package/dist/{ImageField-DBAlnMon.js → ImageField-Ld7SHA8N.js} +1 -1
- package/dist/{LocationField-DsHsXA6R.js → LocationField-Bgu-vMAE.js} +1 -1
- package/dist/{MasterDetailField-Db8b7Gqs.js → MasterDetailField-Bp5WBTzU.js} +3 -3
- package/dist/{NumberField-0IGp7lcA.js → NumberField-uBqVZ-gt.js} +1 -1
- package/dist/{ObjectField-BLApgJtS.js → ObjectField-BH1Md9gH.js} +6 -6
- package/dist/{PasswordField-pHKyNlmo.js → PasswordField-D8GZjY7d.js} +1 -1
- package/dist/{PercentField-CwgKmlIb.js → PercentField-DyK8vg8M.js} +1 -1
- package/dist/{PhoneField-lKtbYOdN.js → PhoneField-B3qJyLP0.js} +1 -1
- package/dist/{QRCodeField-BTTasT3w.js → QRCodeField-CGiRTCZq.js} +1 -1
- package/dist/{RatingField-De2X-l44.js → RatingField-CWVaJNyf.js} +4 -4
- package/dist/{RichTextField-B5QnvUOr.js → RichTextField-CusveP9T.js} +1 -1
- package/dist/{SelectField-C9AZRHWu.js → SelectField-UdDfsEZo.js} +1 -1
- package/dist/{SignatureField-BgcEmYzd.js → SignatureField-DFvPKbuI.js} +1 -1
- package/dist/{SliderField-BzrttVOY.js → SliderField-C-HvGV9e.js} +1 -1
- package/dist/{TextAreaField-DSE_CaU6.js → TextAreaField-C5KygUT3.js} +1 -1
- package/dist/{TextField-DFQ4T9PR.js → TextField-oUjuqQ1x.js} +1 -1
- package/dist/{TimeField-F0cfmsps.js → TimeField-SsQ6rfk5.js} +1 -1
- package/dist/{UrlField-DLXrFIH-.js → UrlField-kd48Ip95.js} +1 -1
- package/dist/{UserField-PXMmxJY9.js → UserField-BOjE_CAz.js} +11 -11
- package/dist/index-D2t9pLAg.js +99948 -0
- package/dist/index.js +10 -10
- package/dist/index.umd.cjs +118 -49
- package/dist/plugin-detail.css +1 -1
- package/dist/src/DetailSection.d.ts +19 -0
- package/dist/src/DetailSection.d.ts.map +1 -1
- package/dist/src/DetailView.d.ts.map +1 -1
- package/dist/src/HeaderHighlight.d.ts +2 -0
- package/dist/src/HeaderHighlight.d.ts.map +1 -1
- package/dist/src/RecordChatterPanel.d.ts +2 -0
- package/dist/src/RecordChatterPanel.d.ts.map +1 -1
- package/dist/src/autoLayout.d.ts +10 -3
- package/dist/src/autoLayout.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/DetailSection.tsx +79 -22
- package/src/DetailView.tsx +16 -9
- package/src/HeaderHighlight.tsx +22 -1
- package/src/RecordChatterPanel.tsx +6 -1
- package/src/RelatedList.tsx +1 -1
- package/src/__tests__/DetailSection.test.tsx +171 -1
- package/src/__tests__/DetailView.test.tsx +31 -0
- package/src/__tests__/HeaderHighlight.test.tsx +145 -0
- package/src/__tests__/RecordChatterPanel.test.tsx +38 -0
- package/src/__tests__/RelatedList.test.tsx +3 -3
- package/src/__tests__/autoLayout.test.ts +44 -0
- package/src/autoLayout.ts +25 -8
- package/src/index.tsx +1 -1
- package/dist/LookupField-CsT0QQz2.js +0 -96
- package/dist/index-qQ1C-yUR.js +0 -59976
- 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;
|
|
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,
|
|
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;
|
|
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,
|
|
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"}
|
package/dist/src/autoLayout.d.ts
CHANGED
|
@@ -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
|
|
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"}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAChF,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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/plugin-detail",
|
|
3
|
-
"version": "3.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.
|
|
28
|
-
"@object-ui/components": "3.1.
|
|
29
|
-
"@object-ui/
|
|
30
|
-
"@object-ui/
|
|
31
|
-
"@object-ui/react": "3.1.
|
|
32
|
-
"@object-ui/types": "3.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",
|
package/src/DetailSection.tsx
CHANGED
|
@@ -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
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
//
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
190
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
{
|
|
265
|
+
{renderedFields.map(renderField)}
|
|
209
266
|
</div>
|
|
210
267
|
);
|
|
211
268
|
|
package/src/DetailView.tsx
CHANGED
|
@@ -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
|
-
|
|
134
|
-
|
|
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 */}
|
package/src/HeaderHighlight.tsx
CHANGED
|
@@ -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
|
-
{
|
|
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
|
)}
|
package/src/RelatedList.tsx
CHANGED
|
@@ -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,
|