@dxos/react-ui-form 0.8.4-main.84f28bd → 0.8.4-main.937b3ca

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 (194) hide show
  1. package/dist/lib/browser/index.mjs +2203 -2067
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +2203 -2067
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/components/ControlSection/ControlSection.d.ts +4 -4
  8. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +1 -1
  9. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +84 -5
  10. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
  11. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +8 -8
  12. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  13. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +84 -7
  14. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/Form/Form-old.stories.d.ts +131 -0
  16. package/dist/types/src/components/Form/Form-old.stories.d.ts.map +1 -0
  17. package/dist/types/src/components/Form/Form.d.ts +81 -25
  18. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  19. package/dist/types/src/components/Form/Form.stories.d.ts +127 -55
  20. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  21. package/dist/types/src/components/Form/FormField.d.ts +37 -0
  22. package/dist/types/src/components/Form/FormField.d.ts.map +1 -0
  23. package/dist/types/src/components/Form/FormFieldComponent.d.ts +78 -0
  24. package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -0
  25. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts +9 -0
  26. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +1 -0
  27. package/dist/types/src/components/Form/FormFieldSet.d.ts +19 -0
  28. package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -0
  29. package/dist/types/src/components/Form/fields/ArrayField.d.ts +18 -0
  30. package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -0
  31. package/dist/types/src/components/Form/fields/BooleanField.d.ts +4 -0
  32. package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +1 -0
  33. package/dist/types/src/components/Form/fields/GeoPointField.d.ts +5 -0
  34. package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +1 -0
  35. package/dist/types/src/components/Form/fields/MarkdownField.d.ts +4 -0
  36. package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +1 -0
  37. package/dist/types/src/components/Form/fields/NumberField.d.ts +4 -0
  38. package/dist/types/src/components/Form/fields/NumberField.d.ts.map +1 -0
  39. package/dist/types/src/components/Form/fields/RefField.d.ts +14 -0
  40. package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -0
  41. package/dist/types/src/components/Form/fields/SelectField.d.ts +10 -0
  42. package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -0
  43. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts +5 -0
  44. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -0
  45. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +82 -0
  46. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -0
  47. package/dist/types/src/components/Form/fields/TextAreaField.d.ts +4 -0
  48. package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +1 -0
  49. package/dist/types/src/components/Form/fields/TextField.d.ts +4 -0
  50. package/dist/types/src/components/Form/fields/TextField.d.ts.map +1 -0
  51. package/dist/types/src/components/Form/fields/TupleField.d.ts +6 -0
  52. package/dist/types/src/components/Form/fields/TupleField.d.ts.map +1 -0
  53. package/dist/types/src/components/Form/fields/index.d.ts +12 -0
  54. package/dist/types/src/components/Form/fields/index.d.ts.map +1 -0
  55. package/dist/types/src/components/Form/index.d.ts +2 -4
  56. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  57. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +46 -0
  58. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -0
  59. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +85 -0
  60. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -0
  61. package/dist/types/src/components/ObjectPicker/index.d.ts +2 -0
  62. package/dist/types/src/components/ObjectPicker/index.d.ts.map +1 -0
  63. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +21 -9
  64. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  65. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +84 -9
  66. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  67. package/dist/types/src/components/index.d.ts +1 -1
  68. package/dist/types/src/components/index.d.ts.map +1 -1
  69. package/dist/types/src/components/testing.d.ts +4 -3
  70. package/dist/types/src/components/testing.d.ts.map +1 -1
  71. package/dist/types/src/hooks/index.d.ts +2 -3
  72. package/dist/types/src/hooks/index.d.ts.map +1 -1
  73. package/dist/types/src/hooks/useFormHandler.d.ts +82 -0
  74. package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -0
  75. package/dist/types/src/hooks/useKeyHandler.d.ts +6 -0
  76. package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -0
  77. package/dist/types/src/translations.d.ts +19 -10
  78. package/dist/types/src/translations.d.ts.map +1 -1
  79. package/dist/types/src/util/field.d.ts +3 -3
  80. package/dist/types/src/util/field.d.ts.map +1 -1
  81. package/dist/types/src/util/formatting.d.ts +2 -2
  82. package/dist/types/src/util/formatting.d.ts.map +1 -1
  83. package/dist/types/src/util/index.d.ts +1 -1
  84. package/dist/types/src/util/index.d.ts.map +1 -1
  85. package/dist/types/src/util/properties.d.ts +7 -0
  86. package/dist/types/src/util/properties.d.ts.map +1 -0
  87. package/dist/types/src/util/refs.d.ts +2 -3
  88. package/dist/types/src/util/refs.d.ts.map +1 -1
  89. package/dist/types/tsconfig.tsbuildinfo +1 -1
  90. package/package.json +51 -40
  91. package/src/components/ControlSection/ControlSection.stories.tsx +11 -14
  92. package/src/components/ControlSection/ControlSection.tsx +30 -16
  93. package/src/components/FieldEditor/FieldEditor.stories.tsx +53 -30
  94. package/src/components/FieldEditor/FieldEditor.test.tsx +14 -13
  95. package/src/components/FieldEditor/FieldEditor.tsx +87 -92
  96. package/src/components/Form/Form-old.stories.tsx +314 -0
  97. package/src/components/Form/Form.stories.tsx +155 -219
  98. package/src/components/Form/Form.tsx +319 -96
  99. package/src/components/Form/FormField.tsx +286 -0
  100. package/src/components/Form/FormFieldComponent.stories.tsx +65 -0
  101. package/src/components/Form/FormFieldComponent.tsx +174 -0
  102. package/src/components/Form/FormFieldSet.tsx +125 -0
  103. package/src/components/Form/README.md +3 -0
  104. package/src/components/Form/fields/ArrayField.tsx +151 -0
  105. package/src/components/Form/fields/BooleanField.tsx +22 -0
  106. package/src/components/Form/fields/GeoPointField.tsx +120 -0
  107. package/src/components/Form/fields/MarkdownField.tsx +78 -0
  108. package/src/components/Form/fields/NumberField.tsx +39 -0
  109. package/src/components/Form/fields/RefField.tsx +181 -0
  110. package/src/components/Form/fields/SelectField.tsx +65 -0
  111. package/src/components/Form/{custom/SelectOptionsInput.stories.tsx → fields/SelectOptionField.stories.tsx} +25 -22
  112. package/src/components/Form/{custom/SelectOptionsInput.tsx → fields/SelectOptionField.tsx} +47 -42
  113. package/src/components/Form/fields/TextAreaField.tsx +38 -0
  114. package/src/components/Form/fields/TextField.tsx +38 -0
  115. package/src/components/Form/{custom/TupleInput.tsx → fields/TupleField.tsx} +16 -9
  116. package/src/components/Form/fields/index.ts +15 -0
  117. package/src/components/Form/index.ts +3 -4
  118. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +214 -0
  119. package/src/components/ObjectPicker/ObjectPicker.tsx +175 -0
  120. package/src/components/ObjectPicker/index.ts +5 -0
  121. package/src/components/ViewEditor/ViewEditor.stories.tsx +141 -59
  122. package/src/components/ViewEditor/ViewEditor.test.tsx +25 -21
  123. package/src/components/ViewEditor/ViewEditor.tsx +317 -214
  124. package/src/components/index.ts +1 -2
  125. package/src/components/testing.tsx +19 -11
  126. package/src/hooks/index.ts +2 -3
  127. package/src/hooks/useFormHandler.ts +356 -0
  128. package/src/hooks/useKeyHandler.tsx +37 -0
  129. package/src/translations.ts +21 -12
  130. package/src/util/field.ts +33 -43
  131. package/src/util/formatting.ts +22 -22
  132. package/src/util/index.ts +1 -1
  133. package/src/util/properties.ts +18 -0
  134. package/src/util/refs.ts +12 -18
  135. package/dist/types/src/components/Form/ArrayField.d.ts +0 -16
  136. package/dist/types/src/components/Form/ArrayField.d.ts.map +0 -1
  137. package/dist/types/src/components/Form/Defaults.d.ts +0 -14
  138. package/dist/types/src/components/Form/Defaults.d.ts.map +0 -1
  139. package/dist/types/src/components/Form/FormActions.d.ts +0 -9
  140. package/dist/types/src/components/Form/FormActions.d.ts.map +0 -1
  141. package/dist/types/src/components/Form/FormContent.d.ts +0 -57
  142. package/dist/types/src/components/Form/FormContent.d.ts.map +0 -1
  143. package/dist/types/src/components/Form/FormContext.d.ts +0 -23
  144. package/dist/types/src/components/Form/FormContext.d.ts.map +0 -1
  145. package/dist/types/src/components/Form/Input.d.ts +0 -26
  146. package/dist/types/src/components/Form/Input.d.ts.map +0 -1
  147. package/dist/types/src/components/Form/RefField.d.ts +0 -12
  148. package/dist/types/src/components/Form/RefField.d.ts.map +0 -1
  149. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +0 -4
  150. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +0 -1
  151. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +0 -4
  152. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +0 -1
  153. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +0 -7
  154. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +0 -1
  155. package/dist/types/src/components/Form/custom/TupleInput.d.ts +0 -6
  156. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +0 -1
  157. package/dist/types/src/components/Form/custom/index.d.ts +0 -4
  158. package/dist/types/src/components/Form/custom/index.d.ts.map +0 -1
  159. package/dist/types/src/components/Form/factory.d.ts +0 -8
  160. package/dist/types/src/components/Form/factory.d.ts.map +0 -1
  161. package/dist/types/src/components/deprecated/Form.d.ts +0 -15
  162. package/dist/types/src/components/deprecated/Form.d.ts.map +0 -1
  163. package/dist/types/src/components/deprecated/Form.stories.d.ts +0 -13
  164. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +0 -1
  165. package/dist/types/src/components/deprecated/FormInput.d.ts +0 -16
  166. package/dist/types/src/components/deprecated/FormInput.d.ts.map +0 -1
  167. package/dist/types/src/components/deprecated/index.d.ts +0 -2
  168. package/dist/types/src/components/deprecated/index.d.ts.map +0 -1
  169. package/dist/types/src/hooks/useForm.d.ts +0 -67
  170. package/dist/types/src/hooks/useForm.d.ts.map +0 -1
  171. package/dist/types/src/hooks/useQueryRefOptions.d.ts +0 -23
  172. package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +0 -1
  173. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +0 -8
  174. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +0 -1
  175. package/dist/types/src/util/schema.d.ts +0 -3
  176. package/dist/types/src/util/schema.d.ts.map +0 -1
  177. package/src/components/Form/ArrayField.tsx +0 -124
  178. package/src/components/Form/Defaults.tsx +0 -203
  179. package/src/components/Form/FormActions.tsx +0 -53
  180. package/src/components/Form/FormContent.tsx +0 -240
  181. package/src/components/Form/FormContext.tsx +0 -110
  182. package/src/components/Form/Input.tsx +0 -51
  183. package/src/components/Form/RefField.tsx +0 -213
  184. package/src/components/Form/custom/GeoPointInput.tsx +0 -72
  185. package/src/components/Form/custom/index.ts +0 -7
  186. package/src/components/Form/factory.tsx +0 -31
  187. package/src/components/deprecated/Form.stories.tsx +0 -62
  188. package/src/components/deprecated/Form.tsx +0 -119
  189. package/src/components/deprecated/FormInput.tsx +0 -71
  190. package/src/components/deprecated/index.ts +0 -5
  191. package/src/hooks/useForm.ts +0 -291
  192. package/src/hooks/useQueryRefOptions.ts +0 -53
  193. package/src/hooks/useRefQueryLookupHandler.ts +0 -39
  194. package/src/util/schema.ts +0 -14
@@ -4,51 +4,42 @@
4
4
 
5
5
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
6
6
 
7
- import { type SchemaRegistry } from '@dxos/echo-db';
8
- import { FormatEnum, FormatEnums, formatToType, type EchoSchema } from '@dxos/echo-schema';
7
+ import { Obj, type SchemaRegistry } from '@dxos/echo';
8
+ import { type EchoSchema, Format, FormatEnums, formatToType } from '@dxos/echo/internal';
9
+ import { type SchemaProperty } from '@dxos/effect';
9
10
  import { log } from '@dxos/log';
10
- import { useTranslation } from '@dxos/react-ui';
11
+ import { useAsyncEffect, useTranslation } from '@dxos/react-ui';
11
12
  import {
12
- getFormatSchema,
13
- getSchemaProperties,
14
- sortProperties,
15
13
  type FieldType,
14
+ type ProjectionModel,
16
15
  type PropertyType,
17
- type ViewType,
18
- type ViewProjection,
19
- type SchemaProperty,
16
+ formatToAdditionalPropertyAttributes,
17
+ getFormatSchema,
20
18
  } from '@dxos/schema';
21
19
 
22
20
  import { translationKey } from '../../translations';
23
- import { Form, type FormProps, type InputComponent, SelectInput, SelectOptionInput } from '../Form';
21
+ import { getFormProperties } from '../../util';
22
+ import { Form, type FormFieldMap, type FormRootProps, SelectField, SelectOptionField } from '../Form';
24
23
 
25
24
  export type FieldEditorProps = {
26
- view: ViewType;
27
- projection: ViewProjection;
25
+ projection: ProjectionModel;
28
26
  field: FieldType;
29
- registry?: SchemaRegistry;
27
+ registry?: SchemaRegistry.SchemaRegistry;
28
+ view?: Obj.Unknown;
30
29
  onSave: () => void;
31
30
  onCancel?: () => void;
32
- } & Pick<FormProps<any>, 'outerSpacing'>;
31
+ } & Pick<FormRootProps<any>, 'readonly'>;
33
32
 
34
33
  /**
35
34
  * Displays a Form representing the metadata for a given `Field` and `View`.
36
35
  */
37
- export const FieldEditor = ({
38
- view,
39
- projection,
40
- field,
41
- registry,
42
- onSave,
43
- onCancel,
44
- outerSpacing,
45
- }: FieldEditorProps) => {
36
+ export const FieldEditor = ({ readonly, projection, field, registry, view, onSave, onCancel }: FieldEditorProps) => {
46
37
  const { t } = useTranslation(translationKey);
47
38
  const [props, setProps] = useState<PropertyType>(projection.getFieldProjection(field.id).props);
48
39
  useEffect(() => setProps(projection.getFieldProjection(field.id).props), [field, projection]);
49
40
 
50
41
  const [schemas, setSchemas] = useState<EchoSchema[]>([]);
51
- useEffect(() => {
42
+ useAsyncEffect(async () => {
52
43
  if (!registry) {
53
44
  return;
54
45
  }
@@ -56,14 +47,10 @@ export const FieldEditor = ({
56
47
  const subscription = registry.query().subscribe((query) => setSchemas(query.results), { fire: true });
57
48
 
58
49
  // TODO(dmaretskyi): This shouldn't be needed.
59
- const t = setTimeout(async () => {
60
- const schemas = await registry.query().run();
61
- setSchemas(schemas);
62
- });
63
- return () => {
64
- clearTimeout(t);
65
- subscription?.();
66
- };
50
+ const schemas = await registry.query().run();
51
+ setSchemas(schemas);
52
+
53
+ return () => subscription?.();
67
54
  }, [registry]);
68
55
 
69
56
  const [referenceSchema, setReferenceSchema] = useState<EchoSchema>();
@@ -75,11 +62,47 @@ export const FieldEditor = ({
75
62
  // Class constructor SchemaClass cannot be invoked without 'new'.
76
63
  const [{ fieldSchema }, setFieldSchema] = useState({ fieldSchema: getFormatSchema(props?.format) });
77
64
 
78
- const handleValuesChanged = useCallback<NonNullable<FormProps<PropertyType>['onValuesChanged']>>(
65
+ const fieldMap = useMemo<FormFieldMap>(
66
+ () => ({
67
+ ['format' satisfies keyof PropertyType]: (props) => (
68
+ <SelectField
69
+ {...props}
70
+ options={FormatEnums.filter((value) => value !== Format.TypeFormat.None).map((value) => ({
71
+ value,
72
+ label: t(`format ${value}`),
73
+ }))}
74
+ />
75
+ ),
76
+ ['referenceSchema' satisfies keyof PropertyType]: (props) => (
77
+ <SelectField
78
+ {...props}
79
+ options={schemas.map((schema) => ({
80
+ value: schema.typename,
81
+ }))}
82
+ />
83
+ ),
84
+ ['referencePath' satisfies keyof PropertyType]: (props) => (
85
+ <SelectField
86
+ {...props}
87
+ options={
88
+ referenceSchema
89
+ ? getFormProperties(referenceSchema.ast)
90
+ .sort((a, b) => a.name.toString().localeCompare(b.name.toString()))
91
+ .map((p) => ({ value: p.name.toString() }))
92
+ : []
93
+ }
94
+ />
95
+ ),
96
+ ['options' satisfies keyof PropertyType]: (props) => <SelectOptionField {...props} />,
97
+ }),
98
+ [t, schemas, referenceSchema],
99
+ );
100
+
101
+ const propIsNotType = useCallback((props: SchemaProperty[]) => props.filter((prop) => prop.name !== 'type'), []);
102
+
103
+ const handleValuesChanged = useCallback<NonNullable<FormRootProps<PropertyType>['onValuesChanged']>>(
79
104
  (_props) => {
80
- // TODO(burdon): Callback should pass `changed` to indicate which fields have changed.
81
- // TODO(Zaymon): Workout why old and new format values are the same sometimes even when
82
- // selecting novel format values.
105
+ // TODO(Zaymon): Workout why old and new format values are the same sometimes even when selecting novel format values.
83
106
  setFieldSchema((prev) => {
84
107
  const fieldSchema = getFormatSchema(_props.format);
85
108
  if (prev.fieldSchema === fieldSchema) {
@@ -100,8 +123,10 @@ export const FieldEditor = ({
100
123
 
101
124
  setProps((props) => {
102
125
  const type = formatToType[_props.format as keyof typeof formatToType];
126
+ const additionalProps =
127
+ formatToAdditionalPropertyAttributes[_props.format as keyof typeof formatToAdditionalPropertyAttributes];
103
128
  if (props.type !== type) {
104
- return { ...props, ..._props, type };
129
+ return { ...props, ..._props, ...additionalProps, type } as PropertyType;
105
130
  }
106
131
 
107
132
  return _props as PropertyType;
@@ -110,9 +135,9 @@ export const FieldEditor = ({
110
135
  [schemas, props.format, props.referenceSchema],
111
136
  );
112
137
 
113
- const handleValidate = useCallback<NonNullable<FormProps<PropertyType>['onValidate']>>(
138
+ const handleValidate = useCallback<NonNullable<FormRootProps<PropertyType>['onValidate']>>(
114
139
  ({ property }) => {
115
- if (property && view.fields.find((f) => f.path === property && f.path !== field.path)) {
140
+ if (property && projection.getFields().find((f) => f.path === property && f.path !== field.path)) {
116
141
  return [
117
142
  {
118
143
  path: 'property',
@@ -121,83 +146,53 @@ export const FieldEditor = ({
121
146
  ];
122
147
  }
123
148
  },
124
- [view.fields, field],
149
+ [projection.getFields(), field],
125
150
  );
126
151
 
127
- const handleSave = useCallback<NonNullable<FormProps<PropertyType>['onSave']>>(
152
+ const handleSave = useCallback<NonNullable<FormRootProps<PropertyType>['onSave']>>(
128
153
  (props) => {
129
- projection.setFieldProjection({ field, props });
154
+ if (view) {
155
+ Obj.change(view, () => {
156
+ projection.setFieldProjection({ field, props });
157
+ });
158
+ } else {
159
+ projection.setFieldProjection({ field, props });
160
+ }
130
161
  onSave();
131
162
  },
132
- [projection, field, onSave],
163
+ [projection, field, view, onSave],
133
164
  );
134
165
 
135
- const handleCancel = useCallback<NonNullable<FormProps<PropertyType>['onCancel']>>(() => {
166
+ const handleCancel = useCallback<NonNullable<FormRootProps<PropertyType>['onCancel']>>(() => {
167
+ onSave();
136
168
  // Need to defer to allow form to close.
137
169
  requestAnimationFrame(() => onCancel?.());
138
- onSave();
139
170
  }, [onSave]);
140
171
 
141
- const custom: Partial<Record<string, InputComponent>> = useMemo(
142
- () => ({
143
- ['format' satisfies keyof PropertyType]: (props) => (
144
- <SelectInput
145
- {...props}
146
- options={FormatEnums.filter((value) => value !== FormatEnum.None).map((value) => ({
147
- value,
148
- label: t(`format ${value}`),
149
- }))}
150
- />
151
- ),
152
- ['referenceSchema' satisfies keyof PropertyType]: (props) => (
153
- <SelectInput
154
- {...props}
155
- options={schemas.map((schema) => ({
156
- value: schema.typename,
157
- }))}
158
- />
159
- ),
160
- ['referencePath' satisfies keyof PropertyType]: (props) => (
161
- <SelectInput
162
- {...props}
163
- options={
164
- referenceSchema
165
- ? getSchemaProperties(referenceSchema.ast)
166
- .sort(sortProperties)
167
- .map((p) => ({ value: p.name }))
168
- : []
169
- }
170
- />
171
- ),
172
- ['options' satisfies keyof PropertyType]: (props) => <SelectOptionInput {...props} />,
173
- }),
174
- [t, schemas, referenceSchema],
175
- );
176
-
177
- const propIsNotType = useCallback(
178
- (props: SchemaProperty<PropertyType>[]) => props.filter((p) => p.name !== 'type'),
179
- [],
180
- );
181
-
182
172
  if (!fieldSchema) {
183
173
  log.warn('invalid format', { props });
184
174
  return null;
185
175
  }
186
176
 
187
177
  return (
188
- <Form<PropertyType>
178
+ <Form.Root<PropertyType>
189
179
  key={field.id}
180
+ fieldMap={fieldMap}
190
181
  autoFocus
191
- values={props}
182
+ readonly={readonly}
192
183
  schema={fieldSchema}
193
- filter={propIsNotType}
184
+ values={props}
185
+ exclude={propIsNotType}
194
186
  sort={['property', 'format']}
195
187
  onValuesChanged={handleValuesChanged}
196
188
  onValidate={handleValidate}
197
189
  onSave={handleSave}
198
190
  onCancel={handleCancel}
199
- Custom={custom}
200
- outerSpacing={outerSpacing}
201
- />
191
+ >
192
+ <Form.Content>
193
+ <Form.FieldSet />
194
+ <Form.Actions />
195
+ </Form.Content>
196
+ </Form.Root>
202
197
  );
203
198
  };
@@ -0,0 +1,314 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import * as Schema from 'effect/Schema';
7
+ import React, { useCallback, useState } from 'react';
8
+
9
+ import { Format } from '@dxos/echo';
10
+ import { type AnyProperties } from '@dxos/echo/internal';
11
+ import { Tooltip } from '@dxos/react-ui';
12
+ import { withLayoutVariants, withTheme } from '@dxos/react-ui/testing';
13
+ import { TestSchema } from '@dxos/schema/testing';
14
+
15
+ import { translations } from '../../translations';
16
+ import { TestLayout } from '../testing';
17
+
18
+ import { SelectField } from './fields';
19
+ import { Form, type FormRootProps } from './Form';
20
+
21
+ // TODO(burdon): Extract into Form.stories.tsx
22
+
23
+ type StoryProps<T extends AnyProperties> = {
24
+ debug?: boolean;
25
+ schema: Schema.Schema.AnyNoContext;
26
+ } & FormRootProps<T>;
27
+
28
+ const DefaultStory = <T extends AnyProperties = any>({
29
+ debug,
30
+ schema,
31
+ values: valuesProp,
32
+ ...props
33
+ }: StoryProps<T>) => {
34
+ const [values, setValues] = useState(valuesProp);
35
+ const handleSave = useCallback<NonNullable<FormRootProps<T>['onSave']>>((values) => {
36
+ setValues(values);
37
+ }, []);
38
+
39
+ if (debug) {
40
+ return (
41
+ <Tooltip.Provider>
42
+ <TestLayout json={{ values, schema: schema.ast }}>
43
+ <Form.Root<T> schema={schema} values={values} onSave={handleSave} {...props}>
44
+ <Form.Content>
45
+ <Form.FieldSet />
46
+ <Form.Actions />
47
+ </Form.Content>
48
+ </Form.Root>
49
+ </TestLayout>
50
+ </Tooltip.Provider>
51
+ );
52
+ }
53
+
54
+ return (
55
+ <Form.Root<T> schema={schema} values={values} onSave={handleSave} {...props}>
56
+ <Form.Content>
57
+ <Form.FieldSet />
58
+ <Form.Actions />
59
+ </Form.Content>
60
+ </Form.Root>
61
+ );
62
+ };
63
+
64
+ // const RefStory = <T extends AnyProperties = any>(props: StoryProps<T>) => {
65
+ // const onQueryRefOptions = useCallback<NonNullable<FormRootProps<T>['onQueryRefOptions']>>(({ typename }) => {
66
+ // switch (typename) {
67
+ // case TestSchema.Person.typename:
68
+ // return [
69
+ // { dxn: Obj.getDXN(contact1), label: 'Alice' },
70
+ // { dxn: Obj.getDXN(contact2), label: 'Bob' },
71
+ // ];
72
+ // default:
73
+ // return [];
74
+ // }
75
+ // }, []);
76
+
77
+ // return <DefaultStory<T> onQueryRefOptions={onQueryRefOptions} {...props} />;
78
+ // };
79
+
80
+ const AddressSchema = Schema.Struct({
81
+ street: Schema.optional(Schema.String.annotations({ title: 'Street' })),
82
+ city: Schema.optional(Schema.String.annotations({ title: 'City' })),
83
+ zip: Schema.optional(Schema.String.pipe(Schema.pattern(/^\d{5}(-\d{4})?$/)).annotations({ title: 'ZIP' })),
84
+ location: Schema.optional(Format.GeoPoint.annotations({ title: 'Location' })),
85
+ }).annotations({ title: 'Address' });
86
+
87
+ const PersonSchema = Schema.Struct({
88
+ name: Schema.optional(Schema.String.annotations({ title: 'Name' })),
89
+ active: Schema.optional(Schema.Boolean.annotations({ title: 'Active' })),
90
+ rank: Schema.optional(Schema.Number.annotations({ title: 'Rank' })),
91
+ website: Schema.optional(Format.URL.annotations({ title: 'Website' })),
92
+ address: Schema.optional(AddressSchema),
93
+ }).pipe(Schema.mutable);
94
+
95
+ type PersonSchema = Schema.Schema.Type<typeof PersonSchema>;
96
+
97
+ const meta = {
98
+ title: 'ui/react-ui-form/Form-Old',
99
+ component: Form.Root as any,
100
+ render: DefaultStory,
101
+ decorators: [withTheme, withLayoutVariants()],
102
+ parameters: {
103
+ layout: 'fullscreen',
104
+ translations,
105
+ },
106
+ argTypes: {
107
+ readonly: {
108
+ control: 'boolean',
109
+ description: 'Readonly',
110
+ },
111
+ },
112
+ } satisfies Meta<StoryProps<any>>;
113
+
114
+ export default meta;
115
+
116
+ type Story = StoryObj<typeof meta>;
117
+
118
+ export const Default: Story = {
119
+ args: {
120
+ schema: PersonSchema,
121
+ values: {
122
+ name: 'DXOS',
123
+ active: true,
124
+ address: {
125
+ zip: '11205',
126
+ },
127
+ },
128
+ readonly: false,
129
+ },
130
+ };
131
+
132
+ export const Organization: Story = {
133
+ args: {
134
+ debug: true,
135
+ schema: TestSchema.OrganizationSchema,
136
+ values: {
137
+ name: 'DXOS',
138
+ website: 'https://dxos.org',
139
+ },
140
+ readonly: false,
141
+ },
142
+ };
143
+
144
+ export const OrganizationAutoSave: Story = {
145
+ args: {
146
+ debug: true,
147
+ schema: TestSchema.OrganizationSchema,
148
+ values: {
149
+ name: 'DXOS',
150
+ website: 'https://dxos.org',
151
+ },
152
+ autoSave: true,
153
+ readonly: false,
154
+ },
155
+ };
156
+
157
+ // TODO(burdon): Type issue with employer reference.
158
+ // TODO(burdon): Test table/form with compound values (e.g., address).
159
+ // Property name is missing in type EncodedReference but required in type
160
+ export const Person: Story = {
161
+ args: {
162
+ debug: true,
163
+ schema: TestSchema.Person,
164
+ values: {
165
+ name: 'Bot',
166
+ },
167
+ },
168
+ };
169
+
170
+ //
171
+ // Union
172
+ //
173
+
174
+ const ShapeSchema = Schema.Struct({
175
+ shape: Schema.optional(
176
+ Schema.Union(
177
+ Schema.Struct({
178
+ type: Schema.Literal('circle').annotations({ title: 'Type' }),
179
+ radius: Schema.optional(Schema.Number.annotations({ title: 'Radius' })),
180
+ }),
181
+ Schema.Struct({
182
+ type: Schema.Literal('square').annotations({ title: 'Type' }),
183
+ size: Schema.optional(Schema.Number.pipe(Schema.nonNegative()).annotations({ title: 'Size' })),
184
+ }),
185
+ ).annotations({ title: 'Shape' }),
186
+ ),
187
+ }).pipe(Schema.mutable);
188
+
189
+ type ShapeSchema = Schema.Schema.Type<typeof ShapeSchema>;
190
+
191
+ export const DiscriminatedShape: StoryObj<StoryProps<ShapeSchema>> = {
192
+ args: {
193
+ debug: true,
194
+ schema: ShapeSchema,
195
+ readonly: false,
196
+ values: {
197
+ shape: {
198
+ type: 'circle',
199
+ radius: 5,
200
+ },
201
+ },
202
+ fieldMap: {
203
+ ['shape.type' as const]: (props) => (
204
+ <SelectField
205
+ {...props}
206
+ options={['circle', 'square'].map((value) => ({
207
+ value,
208
+ label: value,
209
+ }))}
210
+ />
211
+ ),
212
+ },
213
+ },
214
+ };
215
+
216
+ //
217
+ // Arrays
218
+ //
219
+
220
+ const ArraysSchema = Schema.Struct({
221
+ names: Schema.Array(Schema.String.pipe(Schema.nonEmptyString())),
222
+ addresses: Schema.Array(AddressSchema),
223
+ }).pipe(Schema.mutable);
224
+
225
+ type ArraysSchema = Schema.Schema.Type<typeof ArraysSchema>;
226
+
227
+ export const Arrays: StoryObj<StoryProps<ArraysSchema>> = {
228
+ args: {
229
+ debug: true,
230
+ schema: ArraysSchema,
231
+ readonly: false,
232
+ values: {
233
+ names: ['Alice', 'Bob'],
234
+ addresses: [],
235
+ },
236
+ },
237
+ };
238
+
239
+ //
240
+ // Tuple
241
+ //
242
+
243
+ // TODO(wittjosiah): Only GeoPoint is works currently.
244
+ const TupleSchema = Schema.Struct({
245
+ tuple: Schema.Tuple(Schema.Number, Schema.String, Schema.Boolean),
246
+ geopoint: Format.GeoPoint,
247
+ }).pipe(Schema.mutable);
248
+
249
+ type TupleSchema = Schema.Schema.Type<typeof TupleSchema>;
250
+
251
+ export const Tuple: StoryObj<StoryProps<TupleSchema>> = {
252
+ args: {
253
+ debug: true,
254
+ schema: TupleSchema,
255
+ readonly: false,
256
+ values: {
257
+ tuple: [1, 'a', true],
258
+ geopoint: [-122.41941, 37.77492],
259
+ },
260
+ },
261
+ };
262
+
263
+ //
264
+ // Enum
265
+ //
266
+
267
+ const ColorSchema = Schema.Struct({
268
+ color: Schema.Union(Schema.Literal('red'), Schema.Literal('green'), Schema.Literal('blue')).annotations({
269
+ title: 'Color',
270
+ }),
271
+ }).pipe(Schema.mutable);
272
+
273
+ type ColorType = Schema.Schema.Type<typeof ColorSchema>;
274
+
275
+ export const Enum: StoryObj<StoryProps<ColorType>> = {
276
+ args: {
277
+ debug: true,
278
+ schema: ColorSchema,
279
+ readonly: false,
280
+ values: {
281
+ color: 'red',
282
+ },
283
+ },
284
+ };
285
+
286
+ //
287
+ // Refs
288
+ //
289
+
290
+ // const RefSchema = Schema.Struct({
291
+ // contact: Type.Ref(TestSchema.Person).annotations({ title: 'Contact Reference' }),
292
+ // optionalContact: Schema.optional(Type.Ref(TestSchema.Person).annotations({ title: 'Optional Contact Reference' })),
293
+ // refArray: Schema.optional(Schema.Array(Type.Ref(TestSchema.Person))),
294
+ // unknownExpando: Schema.optional(
295
+ // Type.Ref(TestSchema.Expando).annotations({ title: 'Optional Ref to an Expando (DXN Input)' }),
296
+ // ),
297
+ // });
298
+
299
+ // type RefSchema = Schema.Schema.Type<typeof RefSchema>;
300
+
301
+ // const contact1 = Obj.make(TestSchema.Person, { name: 'Alice' });
302
+ // const contact2 = Obj.make(TestSchema.Person, { name: 'Bob' });
303
+
304
+ // export const Refs: StoryObj<StoryProps<RefSchema>> = {
305
+ // render: RefStory,
306
+ // args: {
307
+ // debug: true,
308
+ // schema: RefSchema,
309
+ // readonly: false,
310
+ // values: {
311
+ // refArray: [Ref.make(contact1), Ref.make(contact2)],
312
+ // },
313
+ // },
314
+ // };