@dxos/react-ui-form 0.8.3-main.7f5a14c → 0.8.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 (63) hide show
  1. package/dist/lib/browser/index.mjs +190 -174
  2. package/dist/lib/browser/index.mjs.map +3 -3
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +150 -134
  5. package/dist/lib/node/index.cjs.map +3 -3
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/lib/node-esm/index.mjs +190 -174
  8. package/dist/lib/node-esm/index.mjs.map +3 -3
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/src/components/ControlSection/ControlSection.d.ts +1 -1
  11. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +1 -1
  12. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
  13. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +3 -2
  14. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  15. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +0 -1
  16. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  17. package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
  18. package/dist/types/src/components/Form/Form.d.ts +6 -15
  19. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  20. package/dist/types/src/components/Form/Form.stories.d.ts +2 -2
  21. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  22. package/dist/types/src/components/Form/FormActions.d.ts +3 -1
  23. package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
  24. package/dist/types/src/components/Form/FormContent.d.ts +30 -3
  25. package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
  26. package/dist/types/src/components/Form/Input.d.ts +6 -4
  27. package/dist/types/src/components/Form/Input.d.ts.map +1 -1
  28. package/dist/types/src/components/Form/RefField.d.ts.map +1 -1
  29. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
  30. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -1
  31. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
  32. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
  33. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +3 -2
  34. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  35. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +0 -1
  36. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  37. package/dist/types/src/components/testing.d.ts +3 -1
  38. package/dist/types/src/components/testing.d.ts.map +1 -1
  39. package/dist/types/src/translations.d.ts +3 -0
  40. package/dist/types/src/translations.d.ts.map +1 -1
  41. package/package.json +28 -27
  42. package/src/components/ControlSection/ControlSection.stories.tsx +15 -3
  43. package/src/components/ControlSection/ControlSection.tsx +11 -9
  44. package/src/components/FieldEditor/FieldEditor.stories.tsx +1 -4
  45. package/src/components/FieldEditor/FieldEditor.test.tsx +2 -1
  46. package/src/components/FieldEditor/FieldEditor.tsx +11 -2
  47. package/src/components/Form/ArrayField.tsx +1 -1
  48. package/src/components/Form/Defaults.tsx +5 -25
  49. package/src/components/Form/Form.stories.tsx +5 -5
  50. package/src/components/Form/Form.tsx +44 -52
  51. package/src/components/Form/FormActions.tsx +17 -2
  52. package/src/components/Form/FormContent.tsx +46 -46
  53. package/src/components/Form/Input.tsx +13 -8
  54. package/src/components/Form/RefField.tsx +1 -5
  55. package/src/components/Form/custom/GeoPointInput.tsx +1 -3
  56. package/src/components/Form/custom/SelectOptionsInput.stories.tsx +7 -5
  57. package/src/components/Form/custom/SelectOptionsInput.tsx +1 -3
  58. package/src/components/Form/custom/TupleInput.tsx +1 -3
  59. package/src/components/ViewEditor/ViewEditor.stories.tsx +1 -4
  60. package/src/components/ViewEditor/ViewEditor.test.tsx +2 -1
  61. package/src/components/ViewEditor/ViewEditor.tsx +66 -66
  62. package/src/components/testing.tsx +12 -7
  63. package/src/translations.ts +4 -1
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../../../src/components/testing.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAItD,eAAO,MAAM,SAAS,GAAI,0BAA0B,eAAe,CAAC,iBAAiB,CAAC,sBAErF,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,gCAAgC,eAAe,CAAC,iBAAiB,CAAC;IAAE,IAAI,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC,sBAK5G,CAAC"}
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../../../src/components/testing.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAKtD,eAAO,MAAM,UAAU,GAAI,gCAAgC,eAAe,CAAC,iBAAiB,CAAC;IAAE,IAAI,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC,sBAK5G,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,0BAA0B,eAAe,CAAC,iBAAiB,CAAC,sBAErF,CAAC;AAGF,eAAO,MAAM,wBAAwB,eAA4B,CAAC;AAClE,eAAO,MAAM,yBAAyB,eAA6B,CAAC"}
@@ -23,6 +23,7 @@ declare const _default: {
23
23
  'format date-time': string;
24
24
  'format did': string;
25
25
  'format duration': string;
26
+ 'format dxn': string;
26
27
  'format email': string;
27
28
  'format formula': string;
28
29
  'format hostname': string;
@@ -35,12 +36,14 @@ declare const _default: {
35
36
  'format regex': string;
36
37
  'format string': string;
37
38
  'format single-select': string;
39
+ 'format multi-select': string;
38
40
  'format text': string;
39
41
  'format time': string;
40
42
  'format timestamp': string;
41
43
  'format user': string;
42
44
  'format number': string;
43
45
  'format uri': string;
46
+ 'format url': string;
44
47
  'format uuid': string;
45
48
  'placeholder latitude': string;
46
49
  'placeholder longitude': string;
@@ -1 +1 @@
1
- {"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE9C,wBAoEE"}
1
+ {"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE9C,wBAuEE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-form",
3
- "version": "0.8.3-main.7f5a14c",
3
+ "version": "0.8.3",
4
4
  "description": "Data components.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -29,24 +29,25 @@
29
29
  "@radix-ui/react-context": "1.1.1",
30
30
  "date-fns": "^3.3.1",
31
31
  "jsonpath": "^1.1.1",
32
- "@dxos/async": "0.8.3-main.7f5a14c",
33
- "@dxos/echo-db": "0.8.3-main.7f5a14c",
34
- "@dxos/debug": "0.8.3-main.7f5a14c",
35
- "@dxos/echo-schema": "0.8.3-main.7f5a14c",
36
- "@dxos/keys": "0.8.3-main.7f5a14c",
37
- "@dxos/effect": "0.8.3-main.7f5a14c",
38
- "@dxos/lit-ui": "0.8.3-main.7f5a14c",
39
- "@dxos/invariant": "0.8.3-main.7f5a14c",
40
- "@dxos/log": "0.8.3-main.7f5a14c",
41
- "@dxos/live-object": "0.8.3-main.7f5a14c",
42
- "@dxos/react-ui-list": "0.8.3-main.7f5a14c",
43
- "@dxos/react-client": "0.8.3-main.7f5a14c",
44
- "@dxos/react-ui-stack": "0.8.3-main.7f5a14c",
45
- "@dxos/react-ui-tag-picker": "0.8.3-main.7f5a14c",
46
- "@dxos/react-ui-pickers": "0.8.3-main.7f5a14c",
47
- "@dxos/util": "0.8.3-main.7f5a14c",
48
- "@dxos/schema": "0.8.3-main.7f5a14c",
49
- "@dxos/react-ui-types": "0.8.3-main.7f5a14c"
32
+ "@dxos/debug": "0.8.3",
33
+ "@dxos/echo": "0.8.3",
34
+ "@dxos/effect": "0.8.3",
35
+ "@dxos/echo-db": "0.8.3",
36
+ "@dxos/async": "0.8.3",
37
+ "@dxos/invariant": "0.8.3",
38
+ "@dxos/echo-schema": "0.8.3",
39
+ "@dxos/keys": "0.8.3",
40
+ "@dxos/lit-ui": "0.8.3",
41
+ "@dxos/live-object": "0.8.3",
42
+ "@dxos/log": "0.8.3",
43
+ "@dxos/react-client": "0.8.3",
44
+ "@dxos/react-ui-list": "0.8.3",
45
+ "@dxos/react-ui-pickers": "0.8.3",
46
+ "@dxos/react-ui-stack": "0.8.3",
47
+ "@dxos/react-ui-tag-picker": "0.8.3",
48
+ "@dxos/react-ui-types": "0.8.3",
49
+ "@dxos/util": "0.8.3",
50
+ "@dxos/schema": "0.8.3"
50
51
  },
51
52
  "devDependencies": {
52
53
  "@phosphor-icons/react": "^2.1.5",
@@ -58,20 +59,20 @@
58
59
  "react": "~18.2.0",
59
60
  "react-dom": "~18.2.0",
60
61
  "vite": "5.4.7",
61
- "@dxos/echo-signals": "0.8.3-main.7f5a14c",
62
- "@dxos/random": "0.8.3-main.7f5a14c",
63
- "@dxos/react-ui": "0.8.3-main.7f5a14c",
64
- "@dxos/react-ui-theme": "0.8.3-main.7f5a14c",
65
- "@dxos/react-ui-syntax-highlighter": "0.8.3-main.7f5a14c",
66
- "@dxos/storybook-utils": "0.8.3-main.7f5a14c"
62
+ "@dxos/echo-signals": "0.8.3",
63
+ "@dxos/random": "0.8.3",
64
+ "@dxos/react-ui": "0.8.3",
65
+ "@dxos/react-ui-syntax-highlighter": "0.8.3",
66
+ "@dxos/react-ui-theme": "0.8.3",
67
+ "@dxos/storybook-utils": "0.8.3"
67
68
  },
68
69
  "peerDependencies": {
69
70
  "@phosphor-icons/react": "^2.1.5",
70
71
  "effect": "3.13.3",
71
72
  "react": "~18.2.0",
72
73
  "react-dom": "~18.2.0",
73
- "@dxos/react-ui": "0.8.3-main.7f5a14c",
74
- "@dxos/react-ui-theme": "0.8.3-main.7f5a14c"
74
+ "@dxos/react-ui": "0.8.3",
75
+ "@dxos/react-ui-theme": "0.8.3"
75
76
  },
76
77
  "publishConfig": {
77
78
  "access": "public"
@@ -5,16 +5,25 @@
5
5
  import '@dxos-theme';
6
6
 
7
7
  import { type Meta, type StoryObj } from '@storybook/react';
8
+ import React from 'react';
8
9
 
10
+ import { StackItem } from '@dxos/react-ui-stack';
9
11
  import { withLayout, withTheme } from '@dxos/storybook-utils';
10
12
 
11
- import { ControlSection, type ControlSectionProps } from './ControlSection';
13
+ import { ControlPage, ControlSection, type ControlSectionProps } from './ControlSection';
12
14
  import translations from '../../translations';
13
15
 
14
16
  const meta: Meta<ControlSectionProps> = {
15
17
  title: 'ui/react-ui-form/ControlSection',
16
18
  component: ControlSection,
17
- decorators: [withLayout(), withTheme],
19
+ render: (args) => (
20
+ <StackItem.Content classNames='w-[40rem]'>
21
+ <ControlPage>
22
+ <ControlSection {...args} />
23
+ </ControlPage>
24
+ </StackItem.Content>
25
+ ),
26
+ decorators: [withLayout({ fullscreen: true, classNames: 'justify-center' }), withTheme],
18
27
  parameters: {
19
28
  translations,
20
29
  },
@@ -25,5 +34,8 @@ export default meta;
25
34
  type Story = StoryObj<ControlSectionProps>;
26
35
 
27
36
  export const Default: Story = {
28
- args: {},
37
+ args: {
38
+ title: 'Control Section',
39
+ description: 'This is a control section',
40
+ },
29
41
  };
@@ -7,8 +7,8 @@ import React, { type ComponentPropsWithoutRef, type PropsWithChildren } from 're
7
7
  import {
8
8
  Button,
9
9
  type ButtonProps,
10
- type Label,
11
10
  Input,
11
+ type Label,
12
12
  type ThemedClassName,
13
13
  toLocalizedString,
14
14
  useTranslation,
@@ -21,7 +21,7 @@ export type ControlPageProps = ThemedClassName<ComponentPropsWithoutRef<'div'>>;
21
21
 
22
22
  export const ControlPage = ({ children, classNames, ...props }: ControlPageProps) => {
23
23
  return (
24
- <div role='none' className={mx('pli-2', classNames)} {...props}>
24
+ <div role='none' className={mx('pli-cardSpacingInline', classNames)} {...props}>
25
25
  {children}
26
26
  </div>
27
27
  );
@@ -45,9 +45,11 @@ export const ControlSectionHeading = ({ title, description }: Omit<ControlSectio
45
45
  const { t } = useTranslation(translationKey);
46
46
  return (
47
47
  <>
48
- <h2 className='pli-4 container-max-width text-xl mbs-6 mbe-4'>{toLocalizedString(title, t)}</h2>
48
+ <h2 className='pli-trimMd container-max-width text-xl mbs-trimLg mbe-trimMd'>{toLocalizedString(title, t)}</h2>
49
49
  {description && (
50
- <p className='pli-4 mlb-4 container-max-width text-description'>{toLocalizedString(description, t)}</p>
50
+ <p className='pli-trimMd mlb-trimMd container-max-width text-description'>
51
+ {toLocalizedString(description, t)}
52
+ </p>
51
53
  )}
52
54
  </>
53
55
  );
@@ -62,7 +64,7 @@ export type ControlGroupProps = ThemedClassName<PropsWithChildren<{}>>;
62
64
  export const ControlGroup = ({ children, classNames }: ControlGroupProps) => (
63
65
  <div
64
66
  role='none'
65
- className={mx('group container-max-width grid grid-cols-1 md:grid-cols-[1fr_min-content] gap-4', classNames)}
67
+ className={mx('group container-max-width grid grid-cols-1 md:grid-cols-[1fr_min-content] gap-trimMd', classNames)}
66
68
  >
67
69
  {children}
68
70
  </div>
@@ -71,7 +73,7 @@ export const ControlGroup = ({ children, classNames }: ControlGroupProps) => (
71
73
  export const ControlFrame = ({ children }: ControlGroupProps) => (
72
74
  <div
73
75
  role='none'
74
- className='p-4 border border-separator rounded-lg container-max-width grid grid-cols-1 md:grid-cols-[1fr_1fr] gap-2 md:gap-8'
76
+ className='p-trimMd container-max-width grid grid-cols-1 md:grid-cols-[1fr_1fr] gap-trimSm md:gap-trimMd border border-separator rounded-md'
75
77
  >
76
78
  {children}
77
79
  </div>
@@ -83,10 +85,10 @@ export type ControlItemProps = PropsWithChildren<{
83
85
  }>;
84
86
 
85
87
  export const controlItemClasses =
86
- 'p-4 border border-separator rounded-lg container-max-width grid md:col-span-2 grid-cols-subgrid items-center gap-2';
88
+ 'pli-trimMd plb-trimMd container-max-width grid md:col-span-2 grid-cols-subgrid items-center gap-trimSm border border-separator rounded-md *:first:!mbs-0 *:last:!mbe-0';
87
89
 
88
90
  const controlItemTitleClasses = 'text-lg font-normal';
89
- const controlItemDescriptionClasses = 'text-base mlb-2 md:mbe-0 text-description';
91
+ const controlItemDescriptionClasses = 'text-base mlb-trimSm md:mbe-0 text-description';
90
92
 
91
93
  export const ControlItem = ({ title, description, children }: ControlItemProps) => {
92
94
  const { t } = useTranslation(translationKey);
@@ -130,7 +132,7 @@ export const ControlFrameItem = ({ title, description, children }: ControlItemPr
130
132
  return (
131
133
  <div role='group' className='min-is-0'>
132
134
  <h3 className='text-lg mbe-2'>{toLocalizedString(title, t)}</h3>
133
- {description && <p className='mlb-2 md:mbe-0 text-description'>{toLocalizedString(description, t)}</p>}
135
+ {description && <p className='mlb-trimSm md:mbe-0 text-description'>{toLocalizedString(description, t)}</p>}
134
136
  {children}
135
137
  </div>
136
138
  );
@@ -15,10 +15,7 @@ import { withLayout, withTheme } from '@dxos/storybook-utils';
15
15
 
16
16
  import { FieldEditor, type FieldEditorProps } from './FieldEditor';
17
17
  import translations from '../../translations';
18
- import { TestLayout, TestPanel } from '../testing';
19
-
20
- // Symbol for accessing debug objects in tests.
21
- export const FIELD_EDITOR_DEBUG_SYMBOL = Symbol('fieldEditorDebug');
18
+ import { TestLayout, TestPanel, FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
22
19
 
23
20
  // Type definition for debug objects exposed to tests.
24
21
  export type FieldEditorDebugObjects = {
@@ -9,7 +9,8 @@ import { afterEach, describe, expect, test } from 'vitest';
9
9
  import { ViewProjection } from '@dxos/schema';
10
10
 
11
11
  import * as stories from './FieldEditor.stories';
12
- import { FIELD_EDITOR_DEBUG_SYMBOL, type FieldEditorDebugObjects } from './FieldEditor.stories';
12
+ import { type FieldEditorDebugObjects } from './FieldEditor.stories';
13
+ import { FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
13
14
 
14
15
  const { Default } = composeStories(stories);
15
16
 
@@ -29,12 +29,20 @@ export type FieldEditorProps = {
29
29
  registry?: SchemaRegistry;
30
30
  onSave: () => void;
31
31
  onCancel?: () => void;
32
- };
32
+ } & Pick<FormProps<any>, 'outerSpacing'>;
33
33
 
34
34
  /**
35
35
  * Displays a Form representing the metadata for a given `Field` and `View`.
36
36
  */
37
- export const FieldEditor = ({ view, projection, field, registry, onSave, onCancel }: FieldEditorProps) => {
37
+ export const FieldEditor = ({
38
+ view,
39
+ projection,
40
+ field,
41
+ registry,
42
+ onSave,
43
+ onCancel,
44
+ outerSpacing,
45
+ }: FieldEditorProps) => {
38
46
  const { t } = useTranslation(translationKey);
39
47
  const [props, setProps] = useState<PropertyType>(projection.getFieldProjection(field.id).props);
40
48
  useEffect(() => setProps(projection.getFieldProjection(field.id).props), [field, projection]);
@@ -189,6 +197,7 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
189
197
  onSave={handleSave}
190
198
  onCancel={handleCancel}
191
199
  Custom={custom}
200
+ outerSpacing={outerSpacing}
192
201
  />
193
202
  );
194
203
  };
@@ -69,7 +69,7 @@ export const ArrayField = ({ property, readonly, path, inputProps, Custom, looku
69
69
 
70
70
  return readonly && values.length < 1 ? null : (
71
71
  <>
72
- <InputHeader>{label}</InputHeader>
72
+ <InputHeader readonly label={label} />
73
73
  <div
74
74
  role='none'
75
75
  className={
@@ -26,11 +26,7 @@ export const TextInput = ({
26
26
  <p>{getValue() ?? ''}</p>
27
27
  ) : (
28
28
  <Input.Root validationValence={status}>
29
- {!inputOnly && (
30
- <InputHeader error={error}>
31
- <Input.Label>{label}</Input.Label>
32
- </InputHeader>
33
- )}
29
+ {!inputOnly && <InputHeader error={error} label={label} />}
34
30
  {disabled ? (
35
31
  <p>{getValue() ?? ''}</p>
36
32
  ) : (
@@ -64,11 +60,7 @@ export const NumberInput = ({
64
60
  <p>{getValue() ?? ''}</p>
65
61
  ) : (
66
62
  <Input.Root validationValence={status}>
67
- {!inputOnly && (
68
- <InputHeader error={error}>
69
- <Input.Label>{label}</Input.Label>
70
- </InputHeader>
71
- )}
63
+ {!inputOnly && <InputHeader error={error} label={label} />}
72
64
  {disabled ? (
73
65
  <p>{getValue() ?? ''}</p>
74
66
  ) : (
@@ -93,11 +85,7 @@ export const BooleanInput = ({ type, label, inputOnly, getStatus, getValue, onVa
93
85
 
94
86
  return (
95
87
  <Input.Root validationValence={status}>
96
- {!inputOnly && (
97
- <InputHeader error={error}>
98
- <Input.Label>{label}</Input.Label>
99
- </InputHeader>
100
- )}
88
+ {!inputOnly && <InputHeader error={error} label={label} />}
101
89
  {disabled ? (
102
90
  <p>{t(checked ? 'boolean input true value' : 'boolean input false value')}</p>
103
91
  ) : (
@@ -130,11 +118,7 @@ export const SelectInput = ({
130
118
 
131
119
  return disabled && !value ? null : (
132
120
  <Input.Root validationValence={status}>
133
- {!inputOnly && (
134
- <InputHeader error={error}>
135
- <Input.Label>{label}</Input.Label>
136
- </InputHeader>
137
- )}
121
+ {!inputOnly && <InputHeader error={error} label={label} />}
138
122
  {disabled ? (
139
123
  <p>{options?.find(({ value: optionValue }) => optionValue === value)?.label ?? String(value)}</p>
140
124
  ) : (
@@ -197,11 +181,7 @@ export const MarkdownInput = ({
197
181
 
198
182
  return (
199
183
  <Input.Root validationValence={status}>
200
- {!inputOnly && (
201
- <InputHeader error={error}>
202
- <Input.Label>{label}</Input.Label>
203
- </InputHeader>
204
- )}
184
+ {!inputOnly && <InputHeader error={error} label={label} />}
205
185
  {disabled ? (
206
186
  <p>{getValue() ?? ''}</p>
207
187
  ) : (
@@ -13,7 +13,7 @@ import { type BaseObject, Expando, Format, getObjectDXN, Ref, type TypeAnnotatio
13
13
  import { live } from '@dxos/live-object';
14
14
  import { withSurfaceVariantsLayout } from '@dxos/react-ui/testing';
15
15
  import { Testing } from '@dxos/schema/testing';
16
- import { withTheme } from '@dxos/storybook-utils';
16
+ import { withLayout, withTheme } from '@dxos/storybook-utils';
17
17
 
18
18
  import { SelectInput } from './Defaults';
19
19
  import { Form, type FormProps } from './Form';
@@ -66,9 +66,9 @@ const DebugStory = <T extends BaseObject>({ schema, values: initialValues, ...pr
66
66
 
67
67
  const meta: Meta<StoryProps<any>> = {
68
68
  title: 'ui/react-ui-form/Form',
69
- component: Form,
69
+ component: Form<any>,
70
70
  render: DebugStory,
71
- decorators: [withTheme],
71
+ decorators: [withLayout({ fullscreen: true }), withTheme],
72
72
  parameters: {
73
73
  translations,
74
74
  },
@@ -100,7 +100,7 @@ export const Default: Story<TestType> = {
100
100
  },
101
101
  };
102
102
 
103
- export const Organization: Story<Testing.Organization> = {
103
+ export const Organization: Story<Schema.Schema.Type<typeof Testing.OrganizationSchema>> = {
104
104
  args: {
105
105
  schema: Testing.OrganizationSchema,
106
106
  values: {
@@ -111,7 +111,7 @@ export const Organization: Story<Testing.Organization> = {
111
111
  },
112
112
  };
113
113
 
114
- export const OrganizationAutoSave: Story<Testing.Organization> = {
114
+ export const OrganizationAutoSave: Story<Schema.Schema.Type<typeof Testing.OrganizationSchema>> = {
115
115
  args: {
116
116
  schema: Testing.OrganizationSchema,
117
117
  values: {
@@ -7,15 +7,14 @@ import React, { type ReactElement, useEffect, useMemo, useRef } from 'react';
7
7
 
8
8
  import { type BaseObject, type PropertyKey } from '@dxos/echo-schema';
9
9
  import { type ThemedClassName } from '@dxos/react-ui';
10
- import { cardSpacing } from '@dxos/react-ui-stack';
11
- import { mx } from '@dxos/react-ui-theme';
10
+ import { cardDialogOverflow, cardSpacing } from '@dxos/react-ui-stack';
12
11
  import { type SchemaProperty } from '@dxos/schema';
13
12
 
14
- import { FormActions } from './FormActions';
15
- import { FormFields } from './FormContent';
13
+ import { FormActions, type FormOuterSpacing } from './FormActions';
14
+ import { FormFields, type FormFieldsProps } from './FormContent';
16
15
  import { FormProvider } from './FormContext';
17
16
  import { type InputProps, type InputComponent } from './Input';
18
- import { type FormOptions, type QueryRefOptions } from '../../hooks';
17
+ import { type FormOptions } from '../../hooks';
19
18
 
20
19
  export type PropsFilter<T extends BaseObject> = (props: SchemaProperty<T>[]) => SchemaProperty<T>[];
21
20
 
@@ -27,55 +26,39 @@ export type ComponentLookup = (args: {
27
26
 
28
27
  export type CustomInputMap = Partial<Record<string, InputComponent>>;
29
28
 
30
- export type FormProps<T extends BaseObject> = ThemedClassName<
31
- {
32
- values: Partial<T>;
33
-
34
- /** Path to the current object from the root. Used with nested forms. */
35
- path?: string[];
36
-
37
- // TODO(burdon): Autofocus first input.
38
- autoFocus?: boolean;
39
- readonly?: boolean;
40
- // TODO(burdon): Change to JsonPath includes/excludes.
41
- filter?: PropsFilter<T>;
42
- sort?: PropertyKey<T>[];
43
- autoSave?: boolean;
44
- flush?: boolean;
45
- testId?: string;
46
- onCancel?: () => void;
47
- onQueryRefOptions?: QueryRefOptions;
48
- lookupComponent?: ComponentLookup;
49
- /**
50
- * Map of custom renderers for specific properties.
51
- * Prefer lookupComponent for plugin specific input surfaces.
52
- */
53
- Custom?: CustomInputMap;
54
- } & Pick<FormOptions<T>, 'schema' | 'onValuesChanged' | 'onValidate' | 'onSave'>
55
- >;
29
+ export type FormProps<T extends BaseObject> = ThemedClassName<{
30
+ values: Partial<T>;
31
+ // TODO(burdon): Autofocus first input.
32
+ autoFocus?: boolean;
33
+ // TODO(burdon): Change to JsonPath includes/excludes.
34
+ filter?: PropsFilter<T>;
35
+ sort?: PropertyKey<T>[];
36
+ autoSave?: boolean;
37
+ outerSpacing?: FormOuterSpacing;
38
+ onCancel?: () => void;
39
+ }> &
40
+ Pick<FormOptions<T>, 'schema' | 'onValuesChanged' | 'onValidate' | 'onSave'> &
41
+ FormFieldsProps;
56
42
 
57
43
  export const Form = <T extends BaseObject>({
58
44
  classNames,
59
- schema,
45
+ testId,
60
46
  values: initialValues,
61
- path = [],
62
47
  autoFocus,
63
48
  readonly,
64
- filter,
65
- sort,
66
49
  autoSave,
67
- flush,
68
- testId,
50
+ outerSpacing = true,
51
+ onCancel,
52
+ schema,
69
53
  onValuesChanged,
70
54
  onValidate,
71
55
  onSave,
72
- onCancel,
73
- onQueryRefOptions,
74
- lookupComponent,
75
- Custom,
56
+ ...props
76
57
  }: FormProps<T>) => {
77
58
  const formRef = useRef<HTMLDivElement>(null);
78
- const onValid = useMemo(() => (autoSave ? onSave : undefined), [autoSave, onSave]);
59
+
60
+ // TODO(burdon): Rename.
61
+ const handleValid = useMemo(() => (autoSave ? onSave : undefined), [autoSave, onSave]);
79
62
 
80
63
  // Focus the first input element within this form.
81
64
  useEffect(() => {
@@ -95,21 +78,30 @@ export const Form = <T extends BaseObject>({
95
78
  initialValues={initialValues}
96
79
  onValuesChanged={onValuesChanged}
97
80
  onValidate={onValidate}
98
- onValid={onValid}
81
+ onValid={handleValid}
99
82
  onSave={onSave}
100
83
  >
101
- <div ref={formRef} role='none' className={mx(!flush && cardSpacing, classNames)} data-testid={testId}>
84
+ <div role='none' className='contents' data-testid={testId}>
102
85
  <FormFields
103
- schema={schema}
104
- path={path}
86
+ {...props}
87
+ ref={formRef}
88
+ classNames={[
89
+ outerSpacing === 'blockStart-0'
90
+ ? 'pli-cardSpacingInline mbe-cardSpacingBlock [&>.mbs-inputSpacingBlock:first-child]:!mbs-0'
91
+ : outerSpacing === 'scroll-fields'
92
+ ? 'pli-cardSpacingInline pbe-cardSpacingBlock'
93
+ : outerSpacing
94
+ ? cardSpacing
95
+ : false,
96
+ outerSpacing === 'scroll-fields' && cardDialogOverflow,
97
+ classNames,
98
+ ]}
105
99
  readonly={readonly}
106
- filter={filter}
107
- sort={sort}
108
- onQueryRefOptions={onQueryRefOptions}
109
- lookupComponent={lookupComponent}
110
- Custom={Custom}
100
+ schema={schema}
111
101
  />
112
- {(onCancel || onSave) && !autoSave && <FormActions onCancel={onCancel} readonly={readonly} />}
102
+ {(onCancel || onSave) && !autoSave && (
103
+ <FormActions readonly={readonly} onCancel={onCancel} outerSpacing={outerSpacing} />
104
+ )}
113
105
  </div>
114
106
  </FormProvider>
115
107
  );
@@ -5,21 +5,36 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { IconButton, useTranslation } from '@dxos/react-ui';
8
+ import { cardSpacing } from '@dxos/react-ui-stack';
9
+ import { mx } from '@dxos/react-ui-theme';
8
10
 
9
11
  import { useFormContext } from './FormContext';
10
12
  import { translationKey } from '../../translations';
11
13
 
14
+ export type FormOuterSpacing = boolean | 'blockStart-0' | 'scroll-fields';
15
+
12
16
  export type FormActionsProps = {
13
17
  readonly?: boolean;
14
18
  onCancel?: () => void;
19
+ outerSpacing?: FormOuterSpacing;
15
20
  };
16
21
 
17
- export const FormActions = ({ readonly, onCancel }: FormActionsProps) => {
22
+ export const FormActions = ({ readonly, onCancel, outerSpacing = true }: FormActionsProps) => {
18
23
  const { t } = useTranslation(translationKey);
19
24
  const { canSave, handleSave } = useFormContext();
20
25
 
21
26
  return (
22
- <div role='none' className='flex [&_button]:grow gap-1 mlb-3 last:mbe-0 first:mbs-0'>
27
+ <div
28
+ role='none'
29
+ className={mx(
30
+ outerSpacing === 'scroll-fields'
31
+ ? 'pli-cardSpacingInline mbe-cardSpacingBlock'
32
+ : outerSpacing
33
+ ? cardSpacing
34
+ : 'mbs-cardSpacingBlock',
35
+ 'flex [&_button]:grow gap-1 first:mbs-0',
36
+ )}
37
+ >
23
38
  {onCancel && !readonly && (
24
39
  <IconButton data-testid='cancel-button' icon='ph--x--regular' label={t('button cancel')} onClick={onCancel} />
25
40
  )}