@object-ui/fields 6.2.3 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +203 -0
  2. package/dist/complex-widgets.test.d.ts +1 -0
  3. package/dist/currency.d.ts +13 -0
  4. package/dist/datetime-widgets.test.d.ts +1 -0
  5. package/dist/field-type-coverage.test.d.ts +1 -0
  6. package/dist/index.cjs +4 -3
  7. package/dist/index.d.ts +300 -2
  8. package/dist/index.js +2460 -1274
  9. package/dist/standard-widgets.test.d.ts +0 -0
  10. package/dist/{packages/fields/src/widgets → widgets}/AddressField.d.ts +2 -1
  11. package/dist/{packages/fields/src/widgets → widgets}/AutoNumberField.d.ts +2 -1
  12. package/dist/{packages/fields/src/widgets → widgets}/AvatarField.d.ts +2 -1
  13. package/dist/{packages/fields/src/widgets → widgets}/BooleanField.d.ts +2 -1
  14. package/dist/widgets/CheckboxesField.d.ts +8 -0
  15. package/dist/{packages/fields/src/widgets → widgets}/CodeField.d.ts +2 -1
  16. package/dist/{packages/fields/src/widgets → widgets}/ColorField.d.ts +2 -1
  17. package/dist/{packages/fields/src/widgets → widgets}/CurrencyField.d.ts +2 -1
  18. package/dist/{packages/fields/src/widgets → widgets}/DateField.d.ts +2 -1
  19. package/dist/{packages/fields/src/widgets → widgets}/DateTimeField.d.ts +2 -1
  20. package/dist/{packages/fields/src/widgets → widgets}/EmailField.d.ts +2 -1
  21. package/dist/{packages/fields/src/widgets → widgets}/FileField.d.ts +2 -1
  22. package/dist/{packages/fields/src/widgets → widgets}/FormulaField.d.ts +2 -1
  23. package/dist/{packages/fields/src/widgets → widgets}/GeolocationField.d.ts +2 -1
  24. package/dist/widgets/GridField.d.ts +123 -0
  25. package/dist/widgets/GridField.test.d.ts +1 -0
  26. package/dist/{packages/fields/src/widgets → widgets}/ImageField.d.ts +2 -1
  27. package/dist/{packages/fields/src/widgets → widgets}/LocationField.d.ts +2 -1
  28. package/dist/{packages/fields/src/widgets → widgets}/LookupField.d.ts +2 -1
  29. package/dist/widgets/MarkdownContent.d.ts +9 -0
  30. package/dist/{packages/fields/src/widgets → widgets}/MasterDetailField.d.ts +2 -1
  31. package/dist/widgets/MultiSelectField.d.ts +9 -0
  32. package/dist/{packages/fields/src/widgets → widgets}/NumberField.d.ts +2 -1
  33. package/dist/{packages/fields/src/widgets → widgets}/ObjectField.d.ts +2 -1
  34. package/dist/{packages/fields/src/widgets → widgets}/PasswordField.d.ts +2 -1
  35. package/dist/{packages/fields/src/widgets → widgets}/PercentField.d.ts +2 -1
  36. package/dist/{packages/fields/src/widgets → widgets}/PhoneField.d.ts +2 -1
  37. package/dist/{packages/fields/src/widgets → widgets}/QRCodeField.d.ts +2 -1
  38. package/dist/widgets/RadioField.d.ts +8 -0
  39. package/dist/{packages/fields/src/widgets → widgets}/RatingField.d.ts +2 -1
  40. package/dist/{packages/fields/src/widgets → widgets}/RecordPickerDialog.d.ts +8 -2
  41. package/dist/{packages/fields/src/widgets → widgets}/RichTextField.d.ts +2 -1
  42. package/dist/{packages/fields/src/widgets → widgets}/SelectField.d.ts +2 -1
  43. package/dist/{packages/fields/src/widgets → widgets}/SignatureField.d.ts +2 -1
  44. package/dist/{packages/fields/src/widgets → widgets}/SliderField.d.ts +2 -1
  45. package/dist/{packages/fields/src/widgets → widgets}/SummaryField.d.ts +2 -1
  46. package/dist/widgets/TagsField.d.ts +8 -0
  47. package/dist/{packages/fields/src/widgets → widgets}/TextAreaField.d.ts +2 -1
  48. package/dist/{packages/fields/src/widgets → widgets}/TextField.d.ts +2 -1
  49. package/dist/{packages/fields/src/widgets → widgets}/TimeField.d.ts +2 -1
  50. package/dist/{packages/fields/src/widgets → widgets}/UrlField.d.ts +2 -1
  51. package/dist/{packages/fields/src/widgets → widgets}/UserField.d.ts +2 -1
  52. package/dist/{packages/fields/src/widgets → widgets}/VectorField.d.ts +2 -1
  53. package/dist/widgets/deriveLookupColumns.d.ts +36 -0
  54. package/dist/widgets/deriveLookupColumns.test.d.ts +1 -0
  55. package/dist/widgets/recentLookups.d.ts +13 -0
  56. package/dist/widgets/recentLookups.test.d.ts +1 -0
  57. package/dist/{packages/fields/src/widgets → widgets}/types.d.ts +1 -1
  58. package/package.json +14 -11
  59. package/src/__tests__/registry-collision.test.ts +91 -0
  60. package/src/__tests__/resolveFieldCurrency.test.ts +20 -0
  61. package/src/coerce-safe-value.test.ts +34 -0
  62. package/src/complex-widgets.test.tsx +58 -7
  63. package/src/currency.ts +14 -0
  64. package/src/field-type-coverage.test.ts +65 -0
  65. package/src/index.tsx +372 -57
  66. package/src/widgets/CheckboxesField.tsx +53 -0
  67. package/src/widgets/CurrencyField.tsx +5 -1
  68. package/src/widgets/GridField.test.tsx +309 -0
  69. package/src/widgets/GridField.tsx +907 -33
  70. package/src/widgets/LookupField.tsx +258 -107
  71. package/src/widgets/MarkdownContent.tsx +19 -0
  72. package/src/widgets/MultiSelectField.tsx +59 -0
  73. package/src/widgets/PercentField.tsx +19 -7
  74. package/src/widgets/RadioField.tsx +41 -0
  75. package/src/widgets/RecordPickerDialog.tsx +22 -18
  76. package/src/widgets/SelectField.tsx +23 -2
  77. package/src/widgets/TagsField.tsx +67 -0
  78. package/src/widgets/deriveLookupColumns.test.ts +74 -0
  79. package/src/widgets/deriveLookupColumns.ts +104 -0
  80. package/src/widgets/recentLookups.test.ts +45 -0
  81. package/src/widgets/recentLookups.ts +47 -0
  82. package/src/widgets/useFieldTranslation.ts +30 -0
  83. package/dist/packages/fields/src/fields-edge-cases.stories.d.ts +0 -45
  84. package/dist/packages/fields/src/fields.stories.d.ts +0 -56
  85. package/dist/packages/fields/src/index.d.ts +0 -286
  86. package/dist/packages/fields/src/record-picker-dialog.stories.d.ts +0 -25
  87. package/dist/packages/fields/src/widgets/GridField.d.ts +0 -7
  88. package/src/fields-edge-cases.stories.tsx +0 -407
  89. package/src/fields.stories.tsx +0 -566
  90. package/src/record-picker-dialog.stories.tsx +0 -160
  91. /package/dist/{packages/fields/src/__tests__/validation-feedback.test.d.ts → __tests__/registry-collision.test.d.ts} +0 -0
  92. /package/dist/{packages/fields/src/complex-widgets.test.d.ts → __tests__/resolveFieldCurrency.test.d.ts} +0 -0
  93. /package/dist/{packages/fields/src/standard-widgets.test.d.ts → __tests__/validation-feedback.test.d.ts} +0 -0
  94. /package/dist/{packages/fields/src/datetime-widgets.test.d.ts → coerce-safe-value.test.d.ts} +0 -0
  95. /package/dist/{packages/fields/src/widgets → widgets}/ImageCropperDialog.d.ts +0 -0
  96. /package/dist/{packages/fields/src/widgets → widgets}/_cell-renderer-bridge.d.ts +0 -0
  97. /package/dist/{packages/fields/src/widgets → widgets}/useFieldTranslation.d.ts +0 -0
@@ -1,566 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
2
- import React, { useState } from 'react';
3
- import {
4
- TextField,
5
- CurrencyField,
6
- SelectField,
7
- DateField,
8
- DateTimeField,
9
- TimeField,
10
- BooleanField,
11
- NumberField,
12
- PercentField,
13
- UrlField,
14
- PhoneField,
15
- EmailField,
16
- PasswordField,
17
- TextAreaField,
18
- AutoNumberField,
19
- LookupField,
20
- UserField,
21
- FileField,
22
- LocationField,
23
- FormulaField,
24
- SummaryField,
25
- RichTextField,
26
- ImageField,
27
- ObjectField,
28
- VectorField,
29
- GridField,
30
- ColorField,
31
- SliderField,
32
- RatingField,
33
- CodeField,
34
- AvatarField,
35
- AddressField,
36
- GeolocationField,
37
- SignatureField,
38
- QRCodeField,
39
- MasterDetailField
40
- } from './index';
41
- import { FieldMetadata } from '@object-ui/types';
42
-
43
- /**
44
- * **Field Widgets** from `@object-ui/fields`.
45
- *
46
- * These components handle data formatting, validation, and display logic for specific data types.
47
- * They are typically used within `ObjectForm` or `ObjectGrid` but can be used standalone.
48
- *
49
- * Note: These widgets implement the `FieldWidgetProps` interface.
50
- */
51
- const meta = {
52
- title: 'Fields/Gallery',
53
- parameters: {
54
- layout: 'centered',
55
- },
56
- tags: ['autodocs'],
57
- } satisfies Meta;
58
-
59
- export default meta;
60
- type Story = StoryObj<any>;
61
-
62
- // Wrapper to handle state since these are controlled components
63
- const FieldWrapper = ({
64
- Component,
65
- field,
66
- initialValue,
67
- readonly = false
68
- }: {
69
- Component: any,
70
- field: Partial<FieldMetadata>,
71
- initialValue?: any,
72
- readonly?: boolean
73
- }) => {
74
- const [value, setValue] = useState(initialValue);
75
-
76
- // Ensure field has minimal required props
77
- const fullField = {
78
- name: 'test_field',
79
- label: 'Test Field',
80
- type: 'text',
81
- ...field
82
- } as FieldMetadata;
83
-
84
- return (
85
- <div className="w-[350px] space-y-2 p-4 border rounded-lg shadow-sm bg-white">
86
- <div className="flex justify-between items-center mb-2">
87
- <label className="text-sm font-semibold text-gray-700">{fullField.label}</label>
88
- {readonly && <span className="text-[10px] bg-slate-100 px-2 py-0.5 rounded text-slate-500 font-medium">Read Only</span>}
89
- </div>
90
-
91
- <Component
92
- value={value}
93
- onChange={(val: any) => {
94
- console.log('onChange', val);
95
- setValue(val);
96
- }}
97
- field={fullField}
98
- readonly={readonly}
99
- />
100
-
101
- {!readonly && (
102
- <div className="text-xs text-muted-foreground mt-4 pt-2 border-t font-mono overflow-hidden text-ellipsis">
103
- Value: {JSON.stringify(value)}
104
- </div>
105
- )}
106
- </div>
107
- );
108
- };
109
-
110
- // --- Text Based ---
111
-
112
- export const Text: Story = {
113
- render: () => (
114
- <FieldWrapper
115
- Component={TextField}
116
- field={{ label: 'Deal Name', type: 'text', placeholder: 'Enter deal name...' }}
117
- initialValue="Big Enterprise Deal"
118
- />
119
- )
120
- };
121
-
122
- export const TextArea: Story = {
123
- render: () => (
124
- <FieldWrapper
125
- Component={TextAreaField}
126
- field={{ label: 'Description', type: 'textarea', rows: 4 }}
127
- initialValue="This is a multi-line description text area."
128
- />
129
- )
130
- };
131
-
132
- export const Email: Story = {
133
- render: () => (
134
- <FieldWrapper
135
- Component={EmailField}
136
- field={{ label: 'Email', type: 'email' }}
137
- initialValue="contact@example.com"
138
- />
139
- )
140
- };
141
-
142
- export const Phone: Story = {
143
- render: () => (
144
- <FieldWrapper
145
- Component={PhoneField}
146
- field={{ label: 'Phone', type: 'phone' }}
147
- initialValue="+1 (555) 123-4567"
148
- />
149
- )
150
- };
151
-
152
- export const Url: Story = {
153
- render: () => (
154
- <FieldWrapper
155
- Component={UrlField}
156
- field={{ label: 'Website', type: 'url' }}
157
- initialValue="https://objectui.org"
158
- />
159
- )
160
- };
161
-
162
- export const Password: Story = {
163
- render: () => (
164
- <FieldWrapper
165
- Component={PasswordField}
166
- field={{ label: 'Secret Key', type: 'password' }}
167
- initialValue="supersecret"
168
- />
169
- )
170
- };
171
-
172
- export const AutoNumber: Story = {
173
- render: () => (
174
- <FieldWrapper
175
- Component={AutoNumberField}
176
- field={{ label: 'Order Number', type: 'auto_number' }}
177
- initialValue="ORD-2024-001"
178
- readonly={true}
179
- />
180
- )
181
- };
182
-
183
- // --- Numbers ---
184
-
185
- export const Number: Story = {
186
- render: () => (
187
- <FieldWrapper
188
- Component={NumberField}
189
- field={{ label: 'Quantity', type: 'number', precision: 0 }}
190
- initialValue={42}
191
- />
192
- )
193
- };
194
-
195
- export const Currency: Story = {
196
- render: () => (
197
- <FieldWrapper
198
- Component={CurrencyField}
199
- field={{ label: 'Amount', type: 'currency', currency: 'USD', precision: 2 }}
200
- initialValue={1234567.89}
201
- />
202
- )
203
- };
204
-
205
- export const Percent: Story = {
206
- render: () => (
207
- <FieldWrapper
208
- Component={PercentField}
209
- field={{ label: 'Probability', type: 'percent', precision: 2 }}
210
- initialValue={0.75}
211
- />
212
- )
213
- };
214
-
215
- // --- Date & Time ---
216
-
217
- export const Date: Story = {
218
- render: () => (
219
- <FieldWrapper
220
- Component={DateField}
221
- field={{ label: 'Close Date', type: 'date' }}
222
- initialValue={new globalThis.Date().toISOString()}
223
- />
224
- )
225
- };
226
-
227
- export const DateTime: Story = {
228
- render: () => (
229
- <FieldWrapper
230
- Component={DateTimeField}
231
- field={{ label: 'Meeting Time', type: 'datetime' }}
232
- initialValue={new globalThis.Date().toISOString()}
233
- />
234
- )
235
- };
236
-
237
- export const Time: Story = {
238
- render: () => (
239
- <FieldWrapper
240
- Component={TimeField}
241
- field={{ label: 'Start Time', type: 'time' }}
242
- initialValue="14:30"
243
- />
244
- )
245
- };
246
-
247
- // --- Selection ---
248
-
249
- export const Boolean: Story = {
250
- render: () => (
251
- <FieldWrapper
252
- Component={BooleanField}
253
- field={{ label: 'Is Active?', type: 'boolean' }}
254
- initialValue={true}
255
- />
256
- )
257
- };
258
-
259
- export const Select: Story = {
260
- render: () => (
261
- <FieldWrapper
262
- Component={SelectField}
263
- field={{
264
- label: 'Stage',
265
- type: 'select',
266
- options: [
267
- { label: 'Prospecting', value: 'prospecting' },
268
- { label: 'Qualification', value: 'qualification' },
269
- { label: 'Proposal', value: 'proposal' },
270
- { label: 'Closed Won', value: 'closed_won' },
271
- { label: 'Closed Lost', value: 'closed_lost' },
272
- ]
273
- }}
274
- initialValue="proposal"
275
- />
276
- )
277
- };
278
-
279
- export const Lookup: Story = {
280
- render: () => (
281
- <FieldWrapper
282
- Component={LookupField}
283
- field={{
284
- label: 'Account',
285
- type: 'lookup',
286
- options: [
287
- { label: 'Acme Corp', value: 'acme' },
288
- { label: 'Globex', value: 'globex' },
289
- { label: 'Soylent Corp', value: 'soylent' },
290
- ] as any
291
- }}
292
- initialValue="acme"
293
- />
294
- )
295
- };
296
-
297
- export const MultiSelectLookup: Story = {
298
- render: () => (
299
- <FieldWrapper
300
- Component={LookupField}
301
- field={{
302
- label: 'Related Accounts',
303
- type: 'lookup',
304
- multiple: true,
305
- options: [
306
- { label: 'Acme Corp', value: 'acme' },
307
- { label: 'Globex', value: 'globex' },
308
- { label: 'Soylent Corp', value: 'soylent' },
309
- ] as any
310
- }}
311
- initialValue={['acme', 'soylent']}
312
- />
313
- )
314
- };
315
-
316
- // --- Special ---
317
-
318
- export const User: Story = {
319
- render: () => (
320
- <FieldWrapper
321
- Component={UserField}
322
- field={{ label: 'Assigned To', type: 'user' }}
323
- initialValue={{ name: 'John Doe', username: 'jdoe' }}
324
- />
325
- )
326
- };
327
-
328
- export const File: Story = {
329
- render: () => (
330
- <FieldWrapper
331
- Component={FileField}
332
- field={{ label: 'Attachment', type: 'file' }}
333
- initialValue={{ name: 'report.pdf', size: 1024000 }}
334
- />
335
- )
336
- };
337
-
338
- export const Location: Story = {
339
- render: () => (
340
- <FieldWrapper
341
- Component={LocationField}
342
- field={{ label: 'Coordinates', type: 'location' }}
343
- initialValue={{ latitude: 37.7749, longitude: -122.4194 }}
344
- />
345
- )
346
- };
347
-
348
- export const Formula: Story = {
349
- render: () => (
350
- <FieldWrapper
351
- Component={FormulaField}
352
- field={{ label: 'Net Profit', type: 'formula' }}
353
- initialValue={5000}
354
- readonly
355
- />
356
- )
357
- };
358
-
359
- export const Summary: Story = {
360
- render: () => (
361
- <FieldWrapper
362
- Component={SummaryField}
363
- field={{ label: 'Total Sales', type: 'summary' }}
364
- initialValue={100000}
365
- readonly
366
- />
367
- )
368
- };
369
-
370
- export const ReadOnly: Story = {
371
- render: () => (
372
- <div className="flex flex-col gap-4">
373
- <FieldWrapper
374
- Component={TextField}
375
- field={{ label: 'Read Only Text', type: 'text' }}
376
- initialValue="You cannot edit this"
377
- readonly={true}
378
- />
379
- <FieldWrapper
380
- Component={CurrencyField}
381
- field={{ label: 'Read Only Currency', type: 'currency' }}
382
- initialValue={9999.99}
383
- readonly={true}
384
- />
385
- <FieldWrapper
386
- Component={UserField}
387
- field={{ label: 'Read Only User', type: 'user' }}
388
- initialValue={{ name: 'Jane Smith' }}
389
- readonly={true}
390
- />
391
- </div>
392
- )
393
- };
394
-
395
- // --- New Widgets ---
396
-
397
- export const RichText: Story = {
398
- render: () => (
399
- <FieldWrapper
400
- Component={RichTextField}
401
- field={{ label: 'Description', type: 'html' }}
402
- initialValue="<p><b>Rich</b> text content...</p>"
403
- />
404
- )
405
- };
406
-
407
- export const Image: Story = {
408
- render: () => (
409
- <FieldWrapper
410
- Component={ImageField}
411
- field={{ label: 'Banner', type: 'image' }}
412
- initialValue="https://placehold.co/600x400"
413
- />
414
- )
415
- };
416
-
417
- export const Color: Story = {
418
- render: () => (
419
- <FieldWrapper
420
- Component={ColorField}
421
- field={{ label: 'Theme Color', type: 'color' }}
422
- initialValue="#3b82f6"
423
- />
424
- )
425
- };
426
-
427
- export const Slider: Story = {
428
- render: () => (
429
- <FieldWrapper
430
- Component={SliderField}
431
- field={{ label: 'Opacity', type: 'slider', min: 0, max: 100 } as any}
432
- initialValue={75}
433
- />
434
- )
435
- };
436
-
437
- export const Rating: Story = {
438
- render: () => (
439
- <FieldWrapper
440
- Component={RatingField}
441
- field={{ label: 'Score', type: 'rating', max: 5 } as any}
442
- initialValue={4}
443
- />
444
- )
445
- };
446
-
447
- export const Code: Story = {
448
- render: () => (
449
- <FieldWrapper
450
- Component={CodeField}
451
- field={{ label: 'Config JSON', type: 'code' }}
452
- initialValue='{ "debug": true }'
453
- />
454
- )
455
- };
456
-
457
- export const Avatar: Story = {
458
- render: () => (
459
- <FieldWrapper
460
- Component={AvatarField}
461
- field={{ label: 'Profile', type: 'avatar' }}
462
- initialValue="https://github.com/shadcn.png"
463
- />
464
- )
465
- };
466
-
467
- export const Signature: Story = {
468
- render: () => (
469
- <FieldWrapper
470
- Component={SignatureField}
471
- field={{ label: 'Sign Here', type: 'signature' }}
472
- initialValue=""
473
- />
474
- )
475
- };
476
-
477
- export const QRCode: Story = {
478
- render: () => (
479
- <FieldWrapper
480
- Component={QRCodeField}
481
- field={{ label: 'Scan Me', type: 'qrcode' }}
482
- initialValue="https://objectui.org"
483
- />
484
- )
485
- };
486
-
487
- export const Address: Story = {
488
- render: () => (
489
- <FieldWrapper
490
- Component={AddressField}
491
- field={{ label: 'Shipping Address', type: 'address' }}
492
- initialValue={{ street: '123 Main St', city: 'Anytown', country: 'USA' }}
493
- />
494
- )
495
- };
496
-
497
- export const Geolocation: Story = {
498
- render: () => (
499
- <FieldWrapper
500
- Component={GeolocationField}
501
- field={{ label: 'Location', type: 'geolocation' }}
502
- initialValue={{ lat: 37.7749, lng: -122.4194 }}
503
- />
504
- )
505
- };
506
-
507
- export const Object: Story = {
508
- render: () => (
509
- <FieldWrapper
510
- Component={ObjectField}
511
- field={{
512
- label: 'Metadata',
513
- type: 'object',
514
- fields: [
515
- { name: 'key', type: 'text', label: 'Key' },
516
- { name: 'value', type: 'text', label: 'Value' }
517
- ]
518
- } as any}
519
- initialValue={{ key: 'theme', value: 'dark' }}
520
- />
521
- )
522
- };
523
-
524
- export const Grid: Story = {
525
- render: () => (
526
- <FieldWrapper
527
- Component={GridField}
528
- field={{
529
- label: 'Order Items',
530
- type: 'grid',
531
- columns: [
532
- { field: 'item', label: 'Item' },
533
- { field: 'qty', label: 'Qty' }
534
- ]
535
- } as any}
536
- initialValue={[
537
- { item: 'Apple', qty: 2 },
538
- { item: 'Banana', qty: 5 }
539
- ]}
540
- />
541
- )
542
- };
543
-
544
- export const MasterDetail: Story = {
545
- render: () => (
546
- <FieldWrapper
547
- Component={MasterDetailField}
548
- field={{
549
- label: 'Line Items',
550
- type: 'master_detail',
551
- reference_to: 'products'
552
- } as any}
553
- initialValue={[]}
554
- />
555
- )
556
- };
557
-
558
- export const Vector: Story = {
559
- render: () => (
560
- <FieldWrapper
561
- Component={VectorField}
562
- field={{ label: 'Embedding', type: 'vector' }}
563
- initialValue={[0.1, 0.2, 0.3, 0.4]}
564
- />
565
- )
566
- };