@object-ui/fields 3.1.5 → 3.3.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.
- package/CHANGELOG.md +141 -0
- package/README.md +24 -0
- package/dist/LookupField-CiIjeL_i.cjs +1 -0
- package/dist/LookupField-De-GqsfT.js +903 -0
- package/dist/chunk-D-qHiVGv.cjs +1 -0
- package/dist/index.cjs +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +704 -690
- package/dist/packages/fields/src/widgets/useFieldTranslation.d.ts +5 -0
- package/dist/rolldown-runtime-CAFD8bLK.js +11 -0
- package/dist/useFieldTranslation-BYPwtuNZ.cjs +1 -0
- package/dist/useFieldTranslation-Bv3oUVgb.js +9 -0
- package/package.json +35 -9
- package/src/__tests__/cell-renderers.test.tsx +39 -18
- package/src/__tests__/datetime-cell.test.tsx +2 -2
- package/src/__tests__/datetime-timezone.test.tsx +9 -9
- package/src/__tests__/validation-feedback.test.tsx +8 -8
- package/src/datetime-widgets.test.tsx +1 -1
- package/src/index.tsx +54 -28
- package/src/record-picker.test.tsx +30 -23
- package/src/remaining-widgets.test.tsx +1 -1
- package/src/widgets/AddressField.tsx +4 -3
- package/src/widgets/AutoNumberField.tsx +5 -3
- package/src/widgets/CodeField.tsx +2 -2
- package/src/widgets/ColorField.tsx +2 -2
- package/src/widgets/CurrencyField.tsx +2 -2
- package/src/widgets/DateField.tsx +2 -2
- package/src/widgets/DateTimeField.tsx +2 -2
- package/src/widgets/EmailField.tsx +2 -2
- package/src/widgets/FileField.tsx +2 -2
- package/src/widgets/FormulaField.tsx +14 -12
- package/src/widgets/GeolocationField.tsx +4 -3
- package/src/widgets/GridField.tsx +3 -3
- package/src/widgets/ImageField.tsx +2 -2
- package/src/widgets/LocationField.tsx +2 -2
- package/src/widgets/LookupField.tsx +8 -8
- package/src/widgets/NumberField.tsx +2 -2
- package/src/widgets/ObjectField.tsx +2 -2
- package/src/widgets/PercentField.tsx +2 -2
- package/src/widgets/PhoneField.tsx +2 -2
- package/src/widgets/QRCodeField.tsx +2 -2
- package/src/widgets/RichTextField.tsx +2 -2
- package/src/widgets/SelectField.tsx +12 -8
- package/src/widgets/SummaryField.tsx +12 -10
- package/src/widgets/TextAreaField.tsx +2 -2
- package/src/widgets/TextField.tsx +2 -2
- package/src/widgets/TimeField.tsx +2 -2
- package/src/widgets/UrlField.tsx +2 -2
- package/src/widgets/UserField.tsx +2 -2
- package/src/widgets/VectorField.tsx +2 -1
- package/src/widgets/useFieldTranslation.ts +17 -0
- package/dist/AddressField-B6fPb2Fs.js +0 -3
- package/dist/AddressField-Ctsxkj35.cjs +0 -1
- package/dist/AutoNumberField-DXIYJfiB.js +0 -3
- package/dist/AutoNumberField-f76XU1bR.cjs +0 -1
- package/dist/AvatarField-BHpex3Wa.js +0 -3
- package/dist/AvatarField-CXOdYavl.cjs +0 -1
- package/dist/BooleanField-Bs0urhsx.js +0 -3
- package/dist/BooleanField-DeexI_O2.cjs +0 -1
- package/dist/CodeField-DDOMH8E8.cjs +0 -1
- package/dist/CodeField-tRACqngO.js +0 -3
- package/dist/ColorField-Bar6bOJw.js +0 -3
- package/dist/ColorField-CM9CxZ9h.cjs +0 -1
- package/dist/CurrencyField-CezTX_rA.cjs +0 -1
- package/dist/CurrencyField-D4VwXI9k.js +0 -3
- package/dist/DateField-DDUllj7w.cjs +0 -1
- package/dist/DateField-DjGiK6BH.js +0 -3
- package/dist/DateTimeField-DBh-dYdR.js +0 -3
- package/dist/DateTimeField-DDqgVN3b.cjs +0 -1
- package/dist/EmailField-B-3nY8Pr.cjs +0 -1
- package/dist/EmailField-Cp6yJlpH.js +0 -3
- package/dist/FileField-C1bcL7tu.cjs +0 -1
- package/dist/FileField-DUgt6ai3.js +0 -3
- package/dist/FormulaField-BLV1KgkQ.js +0 -3
- package/dist/FormulaField-CsbQIZ7S.cjs +0 -1
- package/dist/GeolocationField-BbIRH51u.js +0 -3
- package/dist/GeolocationField-UFu9VR-x.cjs +0 -1
- package/dist/GridField-2m74haE0.cjs +0 -1
- package/dist/GridField-O3Jd1NuJ.js +0 -3
- package/dist/ImageField-Cna7g6KL.cjs +0 -1
- package/dist/ImageField-rc1Nzcvb.js +0 -3
- package/dist/LocationField-DpRe9QlH.cjs +0 -1
- package/dist/LocationField-QZPPYo0J.js +0 -3
- package/dist/LookupField-BX4sK8NG.js +0 -904
- package/dist/LookupField-C9rPrHi_.cjs +0 -1
- package/dist/MasterDetailField-BXncIT1s.js +0 -3
- package/dist/MasterDetailField-TNP2825Z.cjs +0 -1
- package/dist/NumberField-DCHTzmDG.js +0 -3
- package/dist/NumberField-SqRwbcY8.cjs +0 -1
- package/dist/ObjectField-BEUHpn_X.js +0 -3
- package/dist/ObjectField-BWZwaFIG.cjs +0 -1
- package/dist/PasswordField-BZ_OeQyw.js +0 -3
- package/dist/PasswordField-kLp-ZyPm.cjs +0 -1
- package/dist/PercentField-B2t2knlc.js +0 -3
- package/dist/PercentField-BzhD6tQm.cjs +0 -1
- package/dist/PhoneField-CirvuysP.cjs +0 -1
- package/dist/PhoneField-CkrCFmcO.js +0 -3
- package/dist/QRCodeField-C4OOLCbE.cjs +0 -1
- package/dist/QRCodeField-CQoGow8y.js +0 -3
- package/dist/RatingField-CZG8SgQ8.cjs +0 -1
- package/dist/RatingField-DecDJkou.js +0 -3
- package/dist/RichTextField-Bog2i4NN.cjs +0 -1
- package/dist/RichTextField-b-8QbNCJ.js +0 -3
- package/dist/SelectField-CAa6c0cB.cjs +0 -1
- package/dist/SelectField-DMy5IxRc.js +0 -3
- package/dist/SignatureField-BvTPE7Dn.js +0 -3
- package/dist/SignatureField-jsb-0AYp.cjs +0 -1
- package/dist/SliderField-B7WvKFv7.js +0 -3
- package/dist/SliderField-DRjEJSxK.cjs +0 -1
- package/dist/SummaryField-DA16Edq3.cjs +0 -1
- package/dist/SummaryField-DVSVfKmY.js +0 -3
- package/dist/TextAreaField--ut87UiB.js +0 -3
- package/dist/TextAreaField-Cxp3DFC6.cjs +0 -1
- package/dist/TextField-CkEDGbkQ.js +0 -3
- package/dist/TextField-aLAzBaLZ.cjs +0 -1
- package/dist/TimeField-D0lF0y_I.cjs +0 -1
- package/dist/TimeField-DA_S6lQV.js +0 -3
- package/dist/UrlField-D0ugmD3f.cjs +0 -1
- package/dist/UrlField-DCnXHR1A.js +0 -3
- package/dist/UserField-DGlorzyG.cjs +0 -1
- package/dist/UserField-V0GmvyGV.js +0 -3
- package/dist/VectorField-BSD85ntU.cjs +0 -1
- package/dist/VectorField-CVi5yrrc.js +0 -3
- package/dist/jsx-runtime-DHCF5ANT.cjs +0 -6
- package/dist/jsx-runtime-MxADXuRH.js +0 -189
- /package/dist/{fields → packages/fields}/src/__tests__/boolean-checkbox.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/__tests__/cell-renderers.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/__tests__/datetime-cell.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/__tests__/datetime-timezone.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/__tests__/extreme-inputs.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/__tests__/form-variants.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/__tests__/validation-feedback.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/complex-widgets.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/datetime-widgets.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/fields-edge-cases.stories.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/fields.stories.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/index.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/new-widgets.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/record-picker-dialog.stories.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/record-picker.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/remaining-widgets.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/standard-widgets.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/text-rich-widgets.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/visual-widgets.test.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/AddressField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/AutoNumberField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/AvatarField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/BooleanField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/CodeField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/ColorField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/CurrencyField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/DateField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/DateTimeField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/EmailField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/FileField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/FormulaField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/GeolocationField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/GridField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/ImageField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/LocationField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/LookupField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/MasterDetailField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/NumberField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/ObjectField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/PasswordField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/PercentField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/PhoneField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/QRCodeField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/RatingField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/RecordPickerDialog.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/RichTextField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/SelectField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/SignatureField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/SliderField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/SummaryField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/TextAreaField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/TextField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/TimeField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/UrlField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/UserField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/VectorField.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/_cell-renderer-bridge.d.ts +0 -0
- /package/dist/{fields → packages/fields}/src/widgets/types.d.ts +0 -0
|
@@ -1113,29 +1113,36 @@ describe('RecordPickerDialog — renderGrid slot', () => {
|
|
|
1113
1113
|
expect(screen.getByTestId('custom-grid')).toBeInTheDocument();
|
|
1114
1114
|
});
|
|
1115
1115
|
|
|
1116
|
-
//
|
|
1117
|
-
|
|
1118
|
-
expect.
|
|
1119
|
-
|
|
1120
|
-
expect.
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
expect.
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1116
|
+
// Wait for data to load, then check the last renderGrid call has records
|
|
1117
|
+
await waitFor(() => {
|
|
1118
|
+
expect(renderGrid).toHaveBeenLastCalledWith(
|
|
1119
|
+
expect.objectContaining({
|
|
1120
|
+
columns: expect.arrayContaining([
|
|
1121
|
+
expect.objectContaining({ field: 'name' }),
|
|
1122
|
+
]),
|
|
1123
|
+
records: expect.arrayContaining([
|
|
1124
|
+
expect.objectContaining({ id: '1', name: 'Acme Corp' }),
|
|
1125
|
+
]),
|
|
1126
|
+
loading: false,
|
|
1127
|
+
totalCount: 1,
|
|
1128
|
+
}),
|
|
1129
|
+
);
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
// Verify all expected props are present in the last call
|
|
1133
|
+
const lastCallArgs = renderGrid.mock.calls[renderGrid.mock.calls.length - 1][0];
|
|
1134
|
+
expect(lastCallArgs).toMatchObject({
|
|
1135
|
+
currentPage: 1,
|
|
1136
|
+
pageSize: 10,
|
|
1137
|
+
sortField: null,
|
|
1138
|
+
sortDirection: 'asc',
|
|
1139
|
+
multiple: false,
|
|
1140
|
+
idField: 'id',
|
|
1141
|
+
});
|
|
1142
|
+
expect(lastCallArgs.onSort).toBeTypeOf('function');
|
|
1143
|
+
expect(lastCallArgs.onPageChange).toBeTypeOf('function');
|
|
1144
|
+
expect(lastCallArgs.onRowClick).toBeTypeOf('function');
|
|
1145
|
+
expect(lastCallArgs.isSelected).toBeTypeOf('function');
|
|
1139
1146
|
});
|
|
1140
1147
|
|
|
1141
1148
|
it('hides built-in table when renderGrid is provided', async () => {
|
|
@@ -37,7 +37,7 @@ describe('Remaining Field Widgets', () => {
|
|
|
37
37
|
|
|
38
38
|
it('should render dash for null value', () => {
|
|
39
39
|
render(<AutoNumberField {...baseProps} value={null as any} />);
|
|
40
|
-
expect(screen.getByText('
|
|
40
|
+
expect(screen.getByText('—')).toBeInTheDocument();
|
|
41
41
|
});
|
|
42
42
|
});
|
|
43
43
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Input, Label } from '@object-ui/components';
|
|
2
|
+
import { Input, Label, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { FieldWidgetProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -34,11 +34,12 @@ export function AddressField({ value, onChange, field, readonly, ...props }: Fie
|
|
|
34
34
|
[addr.state, addr.zipCode].filter(Boolean).join(' '),
|
|
35
35
|
addr.country,
|
|
36
36
|
].filter(Boolean);
|
|
37
|
-
return parts.join(', ')
|
|
37
|
+
return parts.join(', ');
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
if (readonly) {
|
|
41
|
-
|
|
41
|
+
const formatted = formatAddress(address);
|
|
42
|
+
return formatted ? <span className="text-sm">{formatted}</span> : <EmptyValue />;
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
return (
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { EmptyValue } from '@object-ui/components';
|
|
2
3
|
import { FieldWidgetProps } from './types';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -6,11 +7,12 @@ import { FieldWidgetProps } from './types';
|
|
|
6
7
|
* Values are automatically generated by the system and cannot be edited
|
|
7
8
|
*/
|
|
8
9
|
export function AutoNumberField({ value, field, ...props }: FieldWidgetProps<string | number>) {
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
if (value == null) {
|
|
11
|
+
return <EmptyValue className={props.className} />;
|
|
12
|
+
}
|
|
11
13
|
return (
|
|
12
14
|
<span className={`text-sm font-mono text-gray-700 ${props.className || ''}`}>
|
|
13
|
-
{
|
|
15
|
+
{String(value)}
|
|
14
16
|
</span>
|
|
15
17
|
);
|
|
16
18
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Textarea, cn } from '@object-ui/components';
|
|
2
|
+
import { Textarea, cn, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { FieldWidgetProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -15,7 +15,7 @@ export function CodeField({ value, onChange, field, readonly, ...props }: FieldW
|
|
|
15
15
|
if (readonly) {
|
|
16
16
|
return (
|
|
17
17
|
<pre className={cn("text-sm bg-muted p-2 rounded overflow-x-auto border", props.className)}>
|
|
18
|
-
<code>{value ||
|
|
18
|
+
<code>{value || <EmptyValue />}</code>
|
|
19
19
|
</pre>
|
|
20
20
|
);
|
|
21
21
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Input } from '@object-ui/components';
|
|
2
|
+
import { Input, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { FieldWidgetProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -16,7 +16,7 @@ export function ColorField({ value, onChange, field, readonly, ...props }: Field
|
|
|
16
16
|
className="w-6 h-6 rounded border border-input"
|
|
17
17
|
style={{ backgroundColor: value || '#000000' }}
|
|
18
18
|
/>
|
|
19
|
-
<span className="text-sm">{value ||
|
|
19
|
+
<span className="text-sm">{value || <EmptyValue />}</span>
|
|
20
20
|
</div>
|
|
21
21
|
);
|
|
22
22
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Input } from '@object-ui/components';
|
|
2
|
+
import { Input, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { FieldWidgetProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -22,7 +22,7 @@ export function CurrencyField({ value, onChange, field, readonly, errorMessage,
|
|
|
22
22
|
const precision = currencyField?.precision ?? 2;
|
|
23
23
|
|
|
24
24
|
if (readonly) {
|
|
25
|
-
if (value == null) return <
|
|
25
|
+
if (value == null) return <EmptyValue />;
|
|
26
26
|
return (
|
|
27
27
|
<span className="text-sm font-medium tabular-nums">
|
|
28
28
|
{formatCurrency(Number(value), currency)}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Input } from '@object-ui/components';
|
|
2
|
+
import { Input, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { FieldWidgetProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -8,7 +8,7 @@ import { FieldWidgetProps } from './types';
|
|
|
8
8
|
*/
|
|
9
9
|
export function DateField({ value, onChange, field, readonly, ...props }: FieldWidgetProps<string>) {
|
|
10
10
|
if (readonly) {
|
|
11
|
-
return <span className="text-sm">{
|
|
11
|
+
return value ? <span className="text-sm">{new Date(value).toLocaleDateString()}</span> : <EmptyValue />;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
// Filter out non-DOM props
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Input } from '@object-ui/components';
|
|
2
|
+
import { Input, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { FieldWidgetProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -8,7 +8,7 @@ import { FieldWidgetProps } from './types';
|
|
|
8
8
|
*/
|
|
9
9
|
export function DateTimeField({ value, onChange, field, readonly, ...props }: FieldWidgetProps<string>) {
|
|
10
10
|
if (readonly) {
|
|
11
|
-
if (!value) return <
|
|
11
|
+
if (!value) return <EmptyValue />;
|
|
12
12
|
const date = new Date(value);
|
|
13
13
|
return (
|
|
14
14
|
<span className="text-sm">
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Input } from '@object-ui/components';
|
|
2
|
+
import { Input, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { FieldWidgetProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -9,7 +9,7 @@ import { FieldWidgetProps } from './types';
|
|
|
9
9
|
export function EmailField({ value, onChange, field, readonly, errorMessage, ...props }: FieldWidgetProps<string>) {
|
|
10
10
|
const config = field || (props as any).schema;
|
|
11
11
|
if (readonly) {
|
|
12
|
-
if (!value) return <
|
|
12
|
+
if (!value) return <EmptyValue />;
|
|
13
13
|
return (
|
|
14
14
|
<a
|
|
15
15
|
href={`mailto:${value}`}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useRef, useState, useCallback } from 'react';
|
|
2
|
-
import { Button } from '@object-ui/components';
|
|
2
|
+
import { Button, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { Upload, X, File as FileIcon, ImageIcon } from 'lucide-react';
|
|
4
4
|
import { FieldWidgetProps } from './types';
|
|
5
5
|
|
|
@@ -86,7 +86,7 @@ export function FileField({ value, onChange, field, readonly, ...props }: FieldW
|
|
|
86
86
|
}, [accept, processFiles]);
|
|
87
87
|
|
|
88
88
|
if (readonly) {
|
|
89
|
-
if (!value) return <
|
|
89
|
+
if (!value) return <EmptyValue />;
|
|
90
90
|
|
|
91
91
|
const readonlyFiles = Array.isArray(value) ? value : [value];
|
|
92
92
|
return (
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { EmptyValue } from '@object-ui/components';
|
|
2
3
|
import { FieldWidgetProps } from './types';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -9,18 +10,19 @@ export function FormulaField({ value, field, ...props }: FieldWidgetProps<any>)
|
|
|
9
10
|
const formulaField = (field || (props as any).schema) as any;
|
|
10
11
|
const returnType = formulaField?.return_type || 'text';
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
if (value == null) {
|
|
14
|
+
return <EmptyValue className={props.className} />;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let displayValue: string;
|
|
18
|
+
if (returnType === 'number' || returnType === 'currency') {
|
|
19
|
+
displayValue = typeof value === 'number' ? value.toFixed(2) : String(value);
|
|
20
|
+
} else if (returnType === 'boolean') {
|
|
21
|
+
displayValue = value ? 'Yes' : 'No';
|
|
22
|
+
} else if (returnType === 'date') {
|
|
23
|
+
displayValue = new Date(value).toLocaleDateString();
|
|
24
|
+
} else {
|
|
25
|
+
displayValue = String(value);
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
return (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Input, Button, Label } from '@object-ui/components';
|
|
2
|
+
import { Input, Button, Label, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { MapPin, Crosshair } from 'lucide-react';
|
|
4
4
|
import { FieldWidgetProps } from './types';
|
|
5
5
|
|
|
@@ -56,7 +56,7 @@ export function GeolocationField({ value, onChange, field, readonly, ...props }:
|
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
const formatLocation = (loc: GeolocationValue): string => {
|
|
59
|
-
if (!loc.latitude || !loc.longitude) return '
|
|
59
|
+
if (!loc.latitude || !loc.longitude) return '';
|
|
60
60
|
return `${loc.latitude.toFixed(6)}, ${loc.longitude.toFixed(6)}`;
|
|
61
61
|
};
|
|
62
62
|
|
|
@@ -67,10 +67,11 @@ export function GeolocationField({ value, onChange, field, readonly, ...props }:
|
|
|
67
67
|
};
|
|
68
68
|
|
|
69
69
|
if (readonly) {
|
|
70
|
+
const formatted = formatLocation(location);
|
|
70
71
|
return (
|
|
71
72
|
<div className="flex items-center gap-2">
|
|
72
73
|
<MapPin className="w-4 h-4 text-muted-foreground" />
|
|
73
|
-
<span className="text-sm">{
|
|
74
|
+
{formatted ? <span className="text-sm">{formatted}</span> : <EmptyValue />}
|
|
74
75
|
{location.latitude && location.longitude && (
|
|
75
76
|
<Button
|
|
76
77
|
type="button"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { FieldWidgetProps } from './types';
|
|
3
|
-
import { cn } from '@object-ui/components';
|
|
3
|
+
import { cn, EmptyValue } from '@object-ui/components';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* GridField - Sub-table/grid data display
|
|
@@ -12,7 +12,7 @@ export function GridField({ value, field, readonly, ...props }: FieldWidgetProps
|
|
|
12
12
|
const columns = gridField?.columns || [];
|
|
13
13
|
|
|
14
14
|
if (!value || !Array.isArray(value)) {
|
|
15
|
-
return <
|
|
15
|
+
return <EmptyValue />;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
if (readonly) {
|
|
@@ -45,7 +45,7 @@ export function GridField({ value, field, readonly, ...props }: FieldWidgetProps
|
|
|
45
45
|
<tr key={rowIdx} className="hover:bg-muted/50 transition-colors">
|
|
46
46
|
{columns.map((col: any, colIdx: number) => (
|
|
47
47
|
<td key={colIdx} className="px-3 py-2 text-foreground">
|
|
48
|
-
{row[col.name] != null ? String(row[col.name]) :
|
|
48
|
+
{row[col.name] != null ? String(row[col.name]) : <EmptyValue />}
|
|
49
49
|
</td>
|
|
50
50
|
))}
|
|
51
51
|
</tr>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useRef } from 'react';
|
|
2
|
-
import { Button } from '@object-ui/components';
|
|
2
|
+
import { Button, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { Upload, X, Image as ImageIcon } from 'lucide-react';
|
|
4
4
|
import { FieldWidgetProps } from './types';
|
|
5
5
|
|
|
@@ -14,7 +14,7 @@ export function ImageField({ value, onChange, field, readonly, ...props }: Field
|
|
|
14
14
|
const accept = imageField?.accept ? imageField.accept.join(',') : 'image/*';
|
|
15
15
|
|
|
16
16
|
if (readonly) {
|
|
17
|
-
if (!value) return <
|
|
17
|
+
if (!value) return <EmptyValue />;
|
|
18
18
|
|
|
19
19
|
const images = Array.isArray(value) ? value : [value];
|
|
20
20
|
return (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Input } from '@object-ui/components';
|
|
2
|
+
import { Input, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { FieldWidgetProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -15,7 +15,7 @@ export function LocationField({ value, onChange, field, readonly, ...props }: Fi
|
|
|
15
15
|
: '';
|
|
16
16
|
|
|
17
17
|
if (readonly) {
|
|
18
|
-
return <span className="text-sm">{displayValue ||
|
|
18
|
+
return <span className="text-sm">{displayValue || <EmptyValue />}</span>;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import React, { useState, useEffect, useCallback, useRef, useContext, useMemo } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
Button,
|
|
2
|
+
import { Button,
|
|
4
3
|
Input,
|
|
5
4
|
Badge,
|
|
6
5
|
Popover,
|
|
7
6
|
PopoverTrigger,
|
|
8
|
-
PopoverContent,
|
|
9
|
-
} from '@object-ui/components';
|
|
7
|
+
PopoverContent, EmptyValue } from '@object-ui/components';
|
|
10
8
|
import { Search, X, Loader2, AlertCircle, Plus, TableProperties } from 'lucide-react';
|
|
11
9
|
import { FieldWidgetProps } from './types';
|
|
12
10
|
import type { DataSource, QueryParams, LookupColumnDef } from '@object-ui/types';
|
|
@@ -14,6 +12,7 @@ import { RecordPickerDialog } from './RecordPickerDialog';
|
|
|
14
12
|
import type { RecordPickerFilterColumn } from './RecordPickerDialog';
|
|
15
13
|
import { getCellRendererResolver } from './_cell-renderer-bridge';
|
|
16
14
|
import { SchemaRendererContext as ImportedSchemaRendererContext } from '@object-ui/react';
|
|
15
|
+
import { useFieldTranslation } from './useFieldTranslation';
|
|
17
16
|
|
|
18
17
|
export interface LookupOption {
|
|
19
18
|
value: string | number;
|
|
@@ -79,6 +78,7 @@ function mapFieldTypeToFilterType(
|
|
|
79
78
|
export function LookupField({ value, onChange, field, readonly, ...props }: FieldWidgetProps<any>) {
|
|
80
79
|
const [isOpen, setIsOpen] = useState(false);
|
|
81
80
|
const [searchQuery, setSearchQuery] = useState('');
|
|
81
|
+
const { t } = useFieldTranslation();
|
|
82
82
|
|
|
83
83
|
// Dynamic data loading state
|
|
84
84
|
const [fetchedOptions, setFetchedOptions] = useState<LookupOption[]>([]);
|
|
@@ -335,7 +335,7 @@ export function LookupField({ value, onChange, field, readonly, ...props }: Fiel
|
|
|
335
335
|
|
|
336
336
|
if (readonly) {
|
|
337
337
|
if (!selectedOptions.length) {
|
|
338
|
-
return <
|
|
338
|
+
return <EmptyValue />;
|
|
339
339
|
}
|
|
340
340
|
|
|
341
341
|
if (multiple) {
|
|
@@ -393,8 +393,8 @@ export function LookupField({ value, onChange, field, readonly, ...props }: Fiel
|
|
|
393
393
|
>
|
|
394
394
|
<Search className="mr-2 size-4" />
|
|
395
395
|
{selectedOptions.length === 0
|
|
396
|
-
? lookupField?.placeholder || '
|
|
397
|
-
: multiple ?
|
|
396
|
+
? lookupField?.placeholder || t('common.select')
|
|
397
|
+
: multiple ? t('table.selected', { count: selectedOptions.length }) : t('common.select')
|
|
398
398
|
}
|
|
399
399
|
</Button>
|
|
400
400
|
</PopoverTrigger>
|
|
@@ -404,7 +404,7 @@ export function LookupField({ value, onChange, field, readonly, ...props }: Fiel
|
|
|
404
404
|
<div className="relative">
|
|
405
405
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" />
|
|
406
406
|
<Input
|
|
407
|
-
placeholder=
|
|
407
|
+
placeholder={t('common.search') + '...'}
|
|
408
408
|
value={searchQuery}
|
|
409
409
|
onChange={(e) => handleSearchChange(e.target.value)}
|
|
410
410
|
onKeyDown={handleSearchKeyDown}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Input } from '@object-ui/components';
|
|
2
|
+
import { Input, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { NumberFieldMetadata } from '@object-ui/types';
|
|
4
4
|
import { FieldWidgetProps } from './types';
|
|
5
5
|
|
|
@@ -9,7 +9,7 @@ import { FieldWidgetProps } from './types';
|
|
|
9
9
|
*/
|
|
10
10
|
export function NumberField({ value, onChange, field, readonly, ...props }: FieldWidgetProps<number>) {
|
|
11
11
|
if (readonly) {
|
|
12
|
-
return <span className="text-sm">{value
|
|
12
|
+
return value == null ? <EmptyValue /> : <span className="text-sm">{value}</span>;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const numberField = (field || (props as any).schema) as NumberFieldMetadata;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { Textarea, cn } from '@object-ui/components';
|
|
2
|
+
import { Textarea, cn, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { FieldWidgetProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -42,7 +42,7 @@ export function ObjectField({ value, onChange, field, readonly, ...props }: Fiel
|
|
|
42
42
|
}, [value, jsonString]);
|
|
43
43
|
|
|
44
44
|
if (readonly) {
|
|
45
|
-
if (!value) return <
|
|
45
|
+
if (!value) return <EmptyValue />;
|
|
46
46
|
return (
|
|
47
47
|
<pre className={cn("text-xs bg-gray-50 p-2 rounded border border-gray-200 overflow-auto max-h-40", props.className)}>
|
|
48
48
|
{JSON.stringify(value, null, 2)}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Input, Slider } from '@object-ui/components';
|
|
2
|
+
import { Input, Slider, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { FieldWidgetProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -12,7 +12,7 @@ export function PercentField({ value, onChange, field, readonly, errorMessage, c
|
|
|
12
12
|
const precision = percentField?.precision ?? 2;
|
|
13
13
|
|
|
14
14
|
if (readonly) {
|
|
15
|
-
if (value == null) return <
|
|
15
|
+
if (value == null) return <EmptyValue />;
|
|
16
16
|
return (
|
|
17
17
|
<span className="text-sm font-medium tabular-nums">
|
|
18
18
|
{(value * 100).toFixed(precision)}%
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Input } from '@object-ui/components';
|
|
2
|
+
import { Input, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { FieldWidgetProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -9,7 +9,7 @@ import { FieldWidgetProps } from './types';
|
|
|
9
9
|
export function PhoneField({ value, onChange, field, readonly, errorMessage, ...props }: FieldWidgetProps<string>) {
|
|
10
10
|
const config = field || (props as any).schema;
|
|
11
11
|
if (readonly) {
|
|
12
|
-
if (!value) return <
|
|
12
|
+
if (!value) return <EmptyValue />;
|
|
13
13
|
return (
|
|
14
14
|
<a
|
|
15
15
|
href={`tel:${value}`}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Input, Button } from '@object-ui/components';
|
|
2
|
+
import { Input, Button, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { QrCode, Copy } from 'lucide-react';
|
|
4
4
|
import { FieldWidgetProps } from './types';
|
|
5
5
|
|
|
@@ -27,7 +27,7 @@ export function QRCodeField({ value, onChange, field, readonly, ...props }: Fiel
|
|
|
27
27
|
if (readonly) {
|
|
28
28
|
return (
|
|
29
29
|
<div className="flex items-center gap-3">
|
|
30
|
-
<span className="text-sm flex-1 truncate">{value ||
|
|
30
|
+
<span className="text-sm flex-1 truncate">{value || <EmptyValue />}</span>
|
|
31
31
|
{value && (
|
|
32
32
|
<Button
|
|
33
33
|
type="button"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Textarea } from '@object-ui/components';
|
|
2
|
+
import { Textarea, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { FieldWidgetProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -13,7 +13,7 @@ export function RichTextField({ value, onChange, field, readonly, errorMessage,
|
|
|
13
13
|
<div
|
|
14
14
|
className="text-sm prose prose-sm max-w-none"
|
|
15
15
|
>
|
|
16
|
-
{value ||
|
|
16
|
+
{value || <EmptyValue />}
|
|
17
17
|
</div>
|
|
18
18
|
);
|
|
19
19
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
Select,
|
|
4
|
-
SelectContent,
|
|
5
|
-
SelectItem,
|
|
6
|
-
SelectTrigger,
|
|
7
|
-
SelectValue
|
|
2
|
+
import {
|
|
3
|
+
Select,
|
|
4
|
+
SelectContent,
|
|
5
|
+
SelectItem,
|
|
6
|
+
SelectTrigger,
|
|
7
|
+
SelectValue,
|
|
8
|
+
EmptyValue,
|
|
8
9
|
} from '@object-ui/components';
|
|
9
10
|
import { SelectFieldMetadata } from '@object-ui/types';
|
|
11
|
+
import { useFieldTranslation } from './useFieldTranslation';
|
|
10
12
|
import { FieldWidgetProps } from './types';
|
|
11
13
|
|
|
12
14
|
/**
|
|
@@ -16,10 +18,12 @@ import { FieldWidgetProps } from './types';
|
|
|
16
18
|
export function SelectField({ value, onChange, field, readonly, ...props }: FieldWidgetProps<string>) {
|
|
17
19
|
const config = (field || (props as any).schema) as SelectFieldMetadata;
|
|
18
20
|
const options = config?.options || [];
|
|
21
|
+
const { t } = useFieldTranslation();
|
|
19
22
|
|
|
20
23
|
if (readonly) {
|
|
21
24
|
const option = options.find((o) => o.value === value);
|
|
22
|
-
|
|
25
|
+
const display = option?.label || value;
|
|
26
|
+
return display ? <span className="text-sm">{display}</span> : <EmptyValue />;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
return (
|
|
@@ -30,7 +34,7 @@ export function SelectField({ value, onChange, field, readonly, ...props }: Fiel
|
|
|
30
34
|
disabled={readonly || props.disabled}
|
|
31
35
|
>
|
|
32
36
|
<SelectTrigger className={props.className} id={props.id}>
|
|
33
|
-
<SelectValue placeholder={config?.placeholder ||
|
|
37
|
+
<SelectValue placeholder={config?.placeholder || t('common.selectOption')} />
|
|
34
38
|
</SelectTrigger>
|
|
35
39
|
<SelectContent position="popper">
|
|
36
40
|
{options.map((option) => (
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { EmptyValue } from '@object-ui/components';
|
|
2
3
|
import { FieldWidgetProps } from './types';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -9,16 +10,17 @@ export function SummaryField({ value, field, ...props }: FieldWidgetProps<any>)
|
|
|
9
10
|
const summaryField = (field || (props as any).schema) as any;
|
|
10
11
|
const summaryType = summaryField?.summary_type || 'count';
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
if (value == null) {
|
|
14
|
+
return <EmptyValue className={props.className} />;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let displayValue: string;
|
|
18
|
+
if (summaryType === 'count') {
|
|
19
|
+
displayValue = String(value);
|
|
20
|
+
} else if (['sum', 'avg', 'min', 'max'].includes(summaryType)) {
|
|
21
|
+
displayValue = typeof value === 'number' ? value.toFixed(2) : String(value);
|
|
22
|
+
} else {
|
|
23
|
+
displayValue = String(value);
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
return (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Textarea } from '@object-ui/components';
|
|
2
|
+
import { Textarea, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { FieldWidgetProps } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -10,7 +10,7 @@ export function TextAreaField({ value, onChange, field, readonly, errorMessage,
|
|
|
10
10
|
if (readonly) {
|
|
11
11
|
return (
|
|
12
12
|
<div className="text-sm whitespace-pre-wrap">
|
|
13
|
-
{value ||
|
|
13
|
+
{value || <EmptyValue />}
|
|
14
14
|
</div>
|
|
15
15
|
);
|
|
16
16
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Input, Textarea } from '@object-ui/components';
|
|
2
|
+
import { Input, Textarea, EmptyValue } from '@object-ui/components';
|
|
3
3
|
import { TextareaFieldMetadata } from '@object-ui/types';
|
|
4
4
|
import { FieldWidgetProps } from './types';
|
|
5
5
|
|
|
@@ -11,7 +11,7 @@ export function TextField({ value, onChange, field, readonly, ...props }: FieldW
|
|
|
11
11
|
const fieldData = field || (props as any).schema;
|
|
12
12
|
|
|
13
13
|
if (readonly) {
|
|
14
|
-
return <span className="text-sm">{value ||
|
|
14
|
+
return <span className="text-sm">{value || <EmptyValue />}</span>;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
// Cast for rows property
|