@dxos/react-ui-form 0.8.4-main.f5c0578 → 0.8.4-main.fcc0d83b33

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 (246) hide show
  1. package/README.md +0 -1
  2. package/dist/lib/browser/index.mjs +2774 -2137
  3. package/dist/lib/browser/index.mjs.map +4 -4
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/translations.mjs +82 -0
  6. package/dist/lib/browser/translations.mjs.map +7 -0
  7. package/dist/lib/node-esm/index.mjs +2774 -2137
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/lib/node-esm/translations.mjs +84 -0
  11. package/dist/lib/node-esm/translations.mjs.map +7 -0
  12. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +8 -8
  13. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  14. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +84 -5
  15. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  16. package/dist/types/src/components/Form/Form.d.ts +109 -25
  17. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  18. package/dist/types/src/components/Form/Form.stories.d.ts +130 -56
  19. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  20. package/dist/types/src/components/Form/FormField.d.ts +43 -0
  21. package/dist/types/src/components/Form/FormField.d.ts.map +1 -0
  22. package/dist/types/src/components/Form/FormFieldComponent.d.ts +79 -0
  23. package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -0
  24. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts +9 -0
  25. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +1 -0
  26. package/dist/types/src/components/Form/FormFieldSet.d.ts +15 -0
  27. package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -0
  28. package/dist/types/src/components/Form/fields/ArrayField.d.ts +18 -0
  29. package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -0
  30. package/dist/types/src/components/Form/fields/BooleanField.d.ts +4 -0
  31. package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +1 -0
  32. package/dist/types/src/components/Form/fields/GeoPointField.d.ts +5 -0
  33. package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +1 -0
  34. package/dist/types/src/components/Form/fields/MarkdownField.d.ts +11 -0
  35. package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +1 -0
  36. package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts +83 -0
  37. package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts.map +1 -0
  38. package/dist/types/src/components/Form/fields/NumberField.d.ts +4 -0
  39. package/dist/types/src/components/Form/fields/NumberField.d.ts.map +1 -0
  40. package/dist/types/src/components/Form/fields/RefField.d.ts +22 -0
  41. package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -0
  42. package/dist/types/src/components/Form/fields/SelectField.d.ts +10 -0
  43. package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -0
  44. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts +5 -0
  45. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -0
  46. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +84 -0
  47. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -0
  48. package/dist/types/src/components/Form/fields/TextAreaField.d.ts +4 -0
  49. package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +1 -0
  50. package/dist/types/src/components/Form/fields/TextField.d.ts +4 -0
  51. package/dist/types/src/components/Form/fields/TextField.d.ts.map +1 -0
  52. package/dist/types/src/components/Form/fields/TupleField.d.ts +6 -0
  53. package/dist/types/src/components/Form/fields/TupleField.d.ts.map +1 -0
  54. package/dist/types/src/components/Form/fields/index.d.ts +12 -0
  55. package/dist/types/src/components/Form/fields/index.d.ts.map +1 -0
  56. package/dist/types/src/components/Form/index.d.ts +2 -4
  57. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  58. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts +9 -0
  59. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts.map +1 -0
  60. package/dist/types/src/components/ObjectForm/index.d.ts +2 -0
  61. package/dist/types/src/components/ObjectForm/index.d.ts.map +1 -0
  62. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +68 -0
  63. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -0
  64. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +87 -0
  65. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -0
  66. package/dist/types/src/components/ObjectPicker/index.d.ts +2 -0
  67. package/dist/types/src/components/ObjectPicker/index.d.ts.map +1 -0
  68. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts +9 -0
  69. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts.map +1 -0
  70. package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts +127 -0
  71. package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts.map +1 -0
  72. package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts +2 -0
  73. package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts.map +1 -0
  74. package/dist/types/src/components/ObjectProperties/index.d.ts +2 -0
  75. package/dist/types/src/components/ObjectProperties/index.d.ts.map +1 -0
  76. package/dist/types/src/components/Settings/Settings.d.ts +51 -0
  77. package/dist/types/src/components/Settings/Settings.d.ts.map +1 -0
  78. package/dist/types/src/components/Settings/Settings.stories.d.ts +84 -0
  79. package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -0
  80. package/dist/types/src/components/Settings/SettingsFieldSet.d.ts +43 -0
  81. package/dist/types/src/components/Settings/SettingsFieldSet.d.ts.map +1 -0
  82. package/dist/types/src/components/Settings/SettingsItem.d.ts +16 -0
  83. package/dist/types/src/components/Settings/SettingsItem.d.ts.map +1 -0
  84. package/dist/types/src/components/Settings/SettingsSelectField.d.ts +18 -0
  85. package/dist/types/src/components/Settings/SettingsSelectField.d.ts.map +1 -0
  86. package/dist/types/src/components/Settings/index.d.ts +4 -0
  87. package/dist/types/src/components/Settings/index.d.ts.map +1 -0
  88. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +22 -10
  89. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  90. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +86 -8
  91. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  92. package/dist/types/src/components/index.d.ts +4 -2
  93. package/dist/types/src/components/index.d.ts.map +1 -1
  94. package/dist/types/src/components/testing.d.ts +5 -4
  95. package/dist/types/src/components/testing.d.ts.map +1 -1
  96. package/dist/types/src/hooks/index.d.ts +2 -3
  97. package/dist/types/src/hooks/index.d.ts.map +1 -1
  98. package/dist/types/src/hooks/useFormHandler.d.ts +82 -0
  99. package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -0
  100. package/dist/types/src/hooks/useKeyHandler.d.ts +7 -0
  101. package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -0
  102. package/dist/types/src/index.d.ts +0 -2
  103. package/dist/types/src/index.d.ts.map +1 -1
  104. package/dist/types/src/translations.d.ts +65 -55
  105. package/dist/types/src/translations.d.ts.map +1 -1
  106. package/dist/types/src/util/field-type.d.ts +19 -0
  107. package/dist/types/src/util/field-type.d.ts.map +1 -0
  108. package/dist/types/src/util/field.d.ts +3 -3
  109. package/dist/types/src/util/field.d.ts.map +1 -1
  110. package/dist/types/src/util/formatting.d.ts +2 -2
  111. package/dist/types/src/util/formatting.d.ts.map +1 -1
  112. package/dist/types/src/util/index.d.ts +2 -1
  113. package/dist/types/src/util/index.d.ts.map +1 -1
  114. package/dist/types/src/util/properties.d.ts +7 -0
  115. package/dist/types/src/util/properties.d.ts.map +1 -0
  116. package/dist/types/src/util/refs.d.ts +3 -3
  117. package/dist/types/src/util/refs.d.ts.map +1 -1
  118. package/dist/types/tsconfig.tsbuildinfo +1 -1
  119. package/package.json +56 -46
  120. package/src/components/FieldEditor/FieldEditor.stories.tsx +49 -31
  121. package/src/components/FieldEditor/FieldEditor.test.tsx +0 -1
  122. package/src/components/FieldEditor/FieldEditor.tsx +86 -91
  123. package/src/components/Form/AUDIT.md +17 -0
  124. package/src/components/Form/Form.stories.tsx +147 -224
  125. package/src/components/Form/Form.tsx +376 -96
  126. package/src/components/Form/FormField.tsx +306 -0
  127. package/src/components/Form/FormFieldComponent.stories.tsx +65 -0
  128. package/src/components/Form/FormFieldComponent.tsx +174 -0
  129. package/src/components/Form/FormFieldSet.tsx +144 -0
  130. package/src/components/Form/README.md +3 -0
  131. package/src/components/Form/fields/ArrayField.tsx +159 -0
  132. package/src/components/Form/fields/BooleanField.tsx +22 -0
  133. package/src/components/Form/fields/GeoPointField.tsx +121 -0
  134. package/src/components/Form/fields/MarkdownField.stories.tsx +152 -0
  135. package/src/components/Form/fields/MarkdownField.tsx +168 -0
  136. package/src/components/Form/fields/NumberField.tsx +39 -0
  137. package/src/components/Form/fields/RefField.tsx +229 -0
  138. package/src/components/Form/fields/SelectField.tsx +65 -0
  139. package/src/components/Form/fields/SelectOptionField.stories.tsx +66 -0
  140. package/src/components/Form/{custom/SelectOptionsInput.tsx → fields/SelectOptionField.tsx} +37 -31
  141. package/src/components/Form/fields/TextAreaField.tsx +38 -0
  142. package/src/components/Form/fields/TextField.tsx +38 -0
  143. package/src/components/Form/{custom/TupleInput.tsx → fields/TupleField.tsx} +17 -10
  144. package/src/components/Form/fields/index.ts +15 -0
  145. package/src/components/Form/index.ts +3 -4
  146. package/src/components/ObjectForm/ObjectForm.tsx +114 -0
  147. package/src/components/{ControlSection → ObjectForm}/index.ts +1 -1
  148. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +212 -0
  149. package/src/components/ObjectPicker/ObjectPicker.tsx +194 -0
  150. package/src/components/ObjectPicker/index.ts +5 -0
  151. package/src/components/ObjectProperties/ObjectProperties.stories.tsx +362 -0
  152. package/src/components/ObjectProperties/ObjectProperties.test.tsx +101 -0
  153. package/src/components/ObjectProperties/ObjectProperties.tsx +141 -0
  154. package/src/components/ObjectProperties/index.ts +5 -0
  155. package/src/components/Settings/AUDIT.md +52 -0
  156. package/src/components/Settings/Settings.stories.tsx +104 -0
  157. package/src/components/Settings/Settings.tsx +82 -0
  158. package/src/components/Settings/SettingsFieldSet.tsx +257 -0
  159. package/src/components/Settings/SettingsItem.tsx +62 -0
  160. package/src/components/Settings/SettingsSelectField.tsx +59 -0
  161. package/src/components/Settings/index.ts +7 -0
  162. package/src/components/ViewEditor/ViewEditor.stories.tsx +140 -63
  163. package/src/components/ViewEditor/ViewEditor.test.tsx +12 -15
  164. package/src/components/ViewEditor/ViewEditor.tsx +348 -212
  165. package/src/components/index.ts +4 -3
  166. package/src/components/testing.tsx +31 -12
  167. package/src/hooks/index.ts +2 -3
  168. package/src/hooks/useFormHandler.ts +380 -0
  169. package/src/hooks/useKeyHandler.tsx +37 -0
  170. package/src/index.ts +0 -4
  171. package/src/translations.ts +70 -59
  172. package/src/util/field-type.ts +60 -0
  173. package/src/util/field.ts +33 -43
  174. package/src/util/formatting.ts +22 -22
  175. package/src/util/index.ts +2 -1
  176. package/src/util/properties.ts +18 -0
  177. package/src/util/refs.ts +17 -19
  178. package/dist/types/src/components/ControlSection/ControlSection.d.ts +0 -23
  179. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +0 -1
  180. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +0 -8
  181. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +0 -1
  182. package/dist/types/src/components/ControlSection/index.d.ts +0 -2
  183. package/dist/types/src/components/ControlSection/index.d.ts.map +0 -1
  184. package/dist/types/src/components/Form/ArrayField.d.ts +0 -16
  185. package/dist/types/src/components/Form/ArrayField.d.ts.map +0 -1
  186. package/dist/types/src/components/Form/Defaults.d.ts +0 -14
  187. package/dist/types/src/components/Form/Defaults.d.ts.map +0 -1
  188. package/dist/types/src/components/Form/FormActions.d.ts +0 -9
  189. package/dist/types/src/components/Form/FormActions.d.ts.map +0 -1
  190. package/dist/types/src/components/Form/FormContent.d.ts +0 -57
  191. package/dist/types/src/components/Form/FormContent.d.ts.map +0 -1
  192. package/dist/types/src/components/Form/FormContext.d.ts +0 -23
  193. package/dist/types/src/components/Form/FormContext.d.ts.map +0 -1
  194. package/dist/types/src/components/Form/Input.d.ts +0 -26
  195. package/dist/types/src/components/Form/Input.d.ts.map +0 -1
  196. package/dist/types/src/components/Form/RefField.d.ts +0 -12
  197. package/dist/types/src/components/Form/RefField.d.ts.map +0 -1
  198. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +0 -4
  199. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +0 -1
  200. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +0 -4
  201. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +0 -1
  202. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +0 -7
  203. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +0 -1
  204. package/dist/types/src/components/Form/custom/TupleInput.d.ts +0 -6
  205. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +0 -1
  206. package/dist/types/src/components/Form/custom/index.d.ts +0 -4
  207. package/dist/types/src/components/Form/custom/index.d.ts.map +0 -1
  208. package/dist/types/src/components/Form/factory.d.ts +0 -8
  209. package/dist/types/src/components/Form/factory.d.ts.map +0 -1
  210. package/dist/types/src/components/deprecated/Form.d.ts +0 -15
  211. package/dist/types/src/components/deprecated/Form.d.ts.map +0 -1
  212. package/dist/types/src/components/deprecated/Form.stories.d.ts +0 -13
  213. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +0 -1
  214. package/dist/types/src/components/deprecated/FormInput.d.ts +0 -16
  215. package/dist/types/src/components/deprecated/FormInput.d.ts.map +0 -1
  216. package/dist/types/src/components/deprecated/index.d.ts +0 -2
  217. package/dist/types/src/components/deprecated/index.d.ts.map +0 -1
  218. package/dist/types/src/hooks/useForm.d.ts +0 -67
  219. package/dist/types/src/hooks/useForm.d.ts.map +0 -1
  220. package/dist/types/src/hooks/useQueryRefOptions.d.ts +0 -23
  221. package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +0 -1
  222. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +0 -8
  223. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +0 -1
  224. package/dist/types/src/util/schema.d.ts +0 -3
  225. package/dist/types/src/util/schema.d.ts.map +0 -1
  226. package/src/components/ControlSection/ControlSection.stories.tsx +0 -42
  227. package/src/components/ControlSection/ControlSection.tsx +0 -139
  228. package/src/components/Form/ArrayField.tsx +0 -125
  229. package/src/components/Form/Defaults.tsx +0 -204
  230. package/src/components/Form/FormActions.tsx +0 -54
  231. package/src/components/Form/FormContent.tsx +0 -241
  232. package/src/components/Form/FormContext.tsx +0 -110
  233. package/src/components/Form/Input.tsx +0 -51
  234. package/src/components/Form/RefField.tsx +0 -214
  235. package/src/components/Form/custom/GeoPointInput.tsx +0 -72
  236. package/src/components/Form/custom/SelectOptionsInput.stories.tsx +0 -62
  237. package/src/components/Form/custom/index.ts +0 -7
  238. package/src/components/Form/factory.tsx +0 -31
  239. package/src/components/deprecated/Form.stories.tsx +0 -63
  240. package/src/components/deprecated/Form.tsx +0 -119
  241. package/src/components/deprecated/FormInput.tsx +0 -71
  242. package/src/components/deprecated/index.ts +0 -5
  243. package/src/hooks/useForm.ts +0 -293
  244. package/src/hooks/useQueryRefOptions.ts +0 -53
  245. package/src/hooks/useRefQueryLookupHandler.ts +0 -39
  246. package/src/util/schema.ts +0 -14
@@ -1,204 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- import React, { useCallback, useEffect, useRef } from 'react';
6
-
7
- import { Input, Select, useTranslation } from '@dxos/react-ui';
8
-
9
- import { translationKey } from '../../translations';
10
-
11
- import { InputHeader, type InputProps } from './Input';
12
-
13
- export const TextInput = ({
14
- type,
15
- label,
16
- inputOnly,
17
- disabled,
18
- placeholder,
19
- getStatus,
20
- getValue,
21
- onValueChange,
22
- onBlur,
23
- }: InputProps) => {
24
- const { status, error } = getStatus();
25
-
26
- return disabled && !getValue() ? null : disabled && inputOnly ? (
27
- <p>{getValue() ?? ''}</p>
28
- ) : (
29
- <Input.Root validationValence={status}>
30
- {!inputOnly && <InputHeader error={error} label={label} />}
31
- {disabled ? (
32
- <p>{getValue() ?? ''}</p>
33
- ) : (
34
- <Input.TextInput
35
- disabled={disabled}
36
- placeholder={placeholder}
37
- value={getValue() ?? ''}
38
- onChange={(event) => onValueChange(type, event.target.value)}
39
- onBlur={onBlur}
40
- />
41
- )}
42
- {inputOnly && <Input.Validation>{error}</Input.Validation>}
43
- </Input.Root>
44
- );
45
- };
46
-
47
- export const NumberInput = ({
48
- type,
49
- label,
50
- inputOnly,
51
- disabled,
52
- placeholder,
53
- getStatus,
54
- getValue,
55
- onValueChange,
56
- onBlur,
57
- }: InputProps) => {
58
- const { status, error } = getStatus();
59
-
60
- return disabled && !getValue() ? null : disabled && inputOnly ? (
61
- <p>{getValue() ?? ''}</p>
62
- ) : (
63
- <Input.Root validationValence={status}>
64
- {!inputOnly && <InputHeader error={error} label={label} />}
65
- {disabled ? (
66
- <p>{getValue() ?? ''}</p>
67
- ) : (
68
- <Input.TextInput
69
- type='number'
70
- disabled={disabled}
71
- placeholder={placeholder}
72
- value={getValue() ?? 0}
73
- onChange={(event) => onValueChange(type, event.target.value)}
74
- onBlur={onBlur}
75
- />
76
- )}
77
- {inputOnly && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
78
- </Input.Root>
79
- );
80
- };
81
-
82
- export const BooleanInput = ({ type, label, inputOnly, getStatus, getValue, onValueChange, disabled }: InputProps) => {
83
- const { status, error } = getStatus();
84
- const checked = Boolean(getValue());
85
- const { t } = useTranslation(translationKey);
86
-
87
- return (
88
- <Input.Root validationValence={status}>
89
- {!inputOnly && <InputHeader error={error} label={label} />}
90
- {disabled ? (
91
- <p>{t(checked ? 'boolean input true value' : 'boolean input false value')}</p>
92
- ) : (
93
- <Input.Switch checked={checked} onCheckedChange={(value) => onValueChange(type, value)} />
94
- )}
95
- {inputOnly && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
96
- </Input.Root>
97
- );
98
- };
99
-
100
- export type SelectInputOptions = InputProps & {
101
- options?: Array<{ value: string | number; label?: string }>;
102
- };
103
-
104
- export const SelectInput = ({
105
- type,
106
- label,
107
- inputOnly,
108
- disabled,
109
- placeholder,
110
- options,
111
- getStatus,
112
- getValue,
113
- onValueChange,
114
- }: SelectInputOptions) => {
115
- const { status, error } = getStatus();
116
-
117
- const value = getValue() as string | undefined;
118
- const handleValueChange = useCallback((value: string | number) => onValueChange(type, value), [type, onValueChange]);
119
-
120
- return disabled && !value ? null : (
121
- <Input.Root validationValence={status}>
122
- {!inputOnly && <InputHeader error={error} label={label} />}
123
- {disabled ? (
124
- <p>{options?.find(({ value: optionValue }) => optionValue === value)?.label ?? String(value)}</p>
125
- ) : (
126
- <Select.Root value={value} onValueChange={handleValueChange}>
127
- {/* TODO(burdon): Placeholder not working? */}
128
- <Select.TriggerButton classNames='is-full' disabled={disabled} placeholder={placeholder} />
129
- <Select.Portal>
130
- <Select.Content>
131
- <Select.Viewport>
132
- {options?.map(({ value, label }) => (
133
- // NOTE: Numeric values are converted to and from strings.
134
- <Select.Option key={String(value)} value={String(value)}>
135
- {label ?? String(value)}
136
- </Select.Option>
137
- ))}
138
- </Select.Viewport>
139
- </Select.Content>
140
- </Select.Portal>
141
- </Select.Root>
142
- )}
143
- {inputOnly && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
144
- </Input.Root>
145
- );
146
- };
147
-
148
- export const MarkdownInput = ({
149
- type,
150
- label,
151
- inputOnly,
152
- disabled,
153
- placeholder,
154
- getStatus,
155
- getValue,
156
- onValueChange,
157
- onBlur,
158
- }: InputProps) => {
159
- const { status, error } = getStatus();
160
- const textareaRef = useRef<HTMLTextAreaElement>(null);
161
-
162
- // TODO(ZaymonFC): If we start using `Enter` for form submission, we should prevent default behavior here
163
- // since we're using a textarea element and the user needs to be able to enter new lines.
164
-
165
- const adjustHeight = useCallback(() => {
166
- const textarea = textareaRef.current;
167
- if (textarea) {
168
- textarea.style.blockSize = 'auto'; // For measurement.
169
- textarea.style.blockSize = `${textarea.scrollHeight + 2}px`;
170
- }
171
- }, []);
172
-
173
- // Adjust height on initial render.
174
- useEffect(() => {
175
- adjustHeight();
176
- }, [adjustHeight]);
177
-
178
- // Adjust height when the content changes.
179
- useEffect(() => {
180
- adjustHeight();
181
- }, [getValue(), adjustHeight]);
182
-
183
- return (
184
- <Input.Root validationValence={status}>
185
- {!inputOnly && <InputHeader error={error} label={label} />}
186
- {disabled ? (
187
- <p>{getValue() ?? ''}</p>
188
- ) : (
189
- <Input.TextArea
190
- ref={textareaRef}
191
- disabled={disabled}
192
- placeholder={placeholder}
193
- value={getValue() ?? ''}
194
- classNames={'min-bs-auto max-h-40 overflow-auto'}
195
- onChange={(event) => onValueChange(type, event.target.value)}
196
- onBlur={onBlur}
197
- style={{ resize: 'none' }}
198
- spellCheck={false}
199
- />
200
- )}
201
- {inputOnly && <Input.Validation>{error}</Input.Validation>}
202
- </Input.Root>
203
- );
204
- };
@@ -1,54 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import React from 'react';
6
-
7
- import { IconButton, useTranslation } from '@dxos/react-ui';
8
- import { cardSpacing } from '@dxos/react-ui-stack';
9
- import { mx } from '@dxos/react-ui-theme';
10
-
11
- import { translationKey } from '../../translations';
12
-
13
- import { useFormContext } from './FormContext';
14
-
15
- export type FormOuterSpacing = boolean | 'blockStart-0' | 'scroll-fields';
16
-
17
- export type FormActionsProps = {
18
- readonly?: boolean;
19
- onCancel?: () => void;
20
- outerSpacing?: FormOuterSpacing;
21
- };
22
-
23
- export const FormActions = ({ readonly, onCancel, outerSpacing = true }: FormActionsProps) => {
24
- const { t } = useTranslation(translationKey);
25
- const { canSave, handleSave } = useFormContext();
26
-
27
- return (
28
- <div
29
- role='none'
30
- className={mx(
31
- outerSpacing === 'scroll-fields'
32
- ? 'pli-cardSpacingInline mbe-cardSpacingBlock'
33
- : outerSpacing
34
- ? cardSpacing
35
- : 'mbs-cardSpacingBlock',
36
- 'flex [&_button]:grow gap-1 first:mbs-0',
37
- )}
38
- >
39
- {onCancel && !readonly && (
40
- <IconButton data-testid='cancel-button' icon='ph--x--regular' label={t('button cancel')} onClick={onCancel} />
41
- )}
42
- {handleSave && (
43
- <IconButton
44
- type='submit'
45
- data-testid='save-button'
46
- disabled={!canSave}
47
- icon='ph--check--regular'
48
- label={t('button save')}
49
- onClick={handleSave}
50
- />
51
- )}
52
- </div>
53
- );
54
- };
@@ -1,241 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { Schema, SchemaAST, pipe } from 'effect';
6
- import { capitalize } from 'effect/String';
7
- import React, { forwardRef, useMemo } from 'react';
8
-
9
- import { createJsonPath, findNode, getDiscriminatedType, isDiscriminatedUnion } from '@dxos/effect';
10
- import { type ThemedClassName } from '@dxos/react-ui';
11
- import { mx } from '@dxos/react-ui-theme';
12
- import { type SchemaProperty, getSchemaProperties } from '@dxos/schema';
13
- import { isNotFalsy } from '@dxos/util';
14
-
15
- import { type QueryRefOptions } from '../../hooks';
16
- import { getRefProps } from '../../util';
17
-
18
- import { ArrayField } from './ArrayField';
19
- import { SelectInput } from './Defaults';
20
- import { getInputComponent } from './factory';
21
- import { type ComponentLookup } from './Form';
22
- import { useFormValues, useInputProps } from './FormContext';
23
- import { type InputComponent } from './Input';
24
- import { RefField } from './RefField';
25
-
26
- export type FormFieldProps = {
27
- property: SchemaProperty<any>;
28
- path?: (string | number)[];
29
- readonly?: boolean;
30
- /** Used to indicate if input should be presented inline (e.g. for array items). */
31
- inline?: boolean;
32
- onQueryRefOptions?: QueryRefOptions;
33
- lookupComponent?: ComponentLookup;
34
- Custom?: Partial<Record<string, InputComponent>>;
35
- };
36
-
37
- export const FormField = ({
38
- property,
39
- path,
40
- readonly,
41
- inline,
42
- onQueryRefOptions,
43
- lookupComponent,
44
- Custom,
45
- }: FormFieldProps) => {
46
- const { ast, name, type, format, title, description, options, examples, array } = property;
47
- const inputProps = useInputProps(path);
48
-
49
- const label = useMemo(() => title ?? pipe(name, capitalize), [title, name]);
50
- const placeholder = useMemo(
51
- () => (examples?.length ? `Example: "${examples[0]}"` : description),
52
- [examples, description],
53
- );
54
-
55
- //
56
- // Registry and Custom.
57
- //
58
-
59
- const FoundComponent = lookupComponent?.({
60
- prop: name,
61
- schema: Schema.make(ast),
62
- inputProps: {
63
- type,
64
- format,
65
- label,
66
- disabled: readonly,
67
- placeholder,
68
- ...inputProps,
69
- },
70
- });
71
-
72
- if (FoundComponent) {
73
- return FoundComponent;
74
- }
75
-
76
- const jsonPath = createJsonPath(path ?? []);
77
- const CustomComponent = Custom?.[jsonPath];
78
- if (CustomComponent) {
79
- return (
80
- <CustomComponent
81
- type={type}
82
- format={format}
83
- label={label}
84
- inputOnly={inline}
85
- placeholder={placeholder}
86
- disabled={readonly}
87
- {...inputProps}
88
- />
89
- );
90
- }
91
-
92
- //
93
- // Refs.
94
- //
95
-
96
- const refProps = getRefProps(property);
97
- if (refProps) {
98
- return (
99
- <RefField
100
- ast={refProps.ast}
101
- array={refProps.isArray}
102
- type={type}
103
- format={format}
104
- label={label}
105
- placeholder={placeholder}
106
- disabled={readonly}
107
- inputOnly={inline}
108
- onQueryRefOptions={onQueryRefOptions}
109
- {...inputProps}
110
- />
111
- );
112
- }
113
-
114
- //
115
- // Arrays.
116
- //
117
-
118
- if (array) {
119
- return <ArrayField property={property} path={path} inputProps={inputProps} readonly={readonly} Custom={Custom} />;
120
- }
121
-
122
- //
123
- // Standard Inputs.
124
- //
125
-
126
- const InputComponent = getInputComponent(type, format);
127
- if (InputComponent) {
128
- return (
129
- <InputComponent
130
- type={type}
131
- format={format}
132
- label={label}
133
- inputOnly={inline}
134
- placeholder={placeholder}
135
- disabled={readonly}
136
- {...inputProps}
137
- />
138
- );
139
- }
140
-
141
- //
142
- // Select.
143
- //
144
-
145
- if (options) {
146
- return (
147
- <SelectInput
148
- type={type}
149
- format={format}
150
- disabled={readonly}
151
- inputOnly={inline}
152
- label={label}
153
- options={options.map((option) => ({ value: option, label: String(option) }))}
154
- placeholder={placeholder}
155
- {...inputProps}
156
- />
157
- );
158
- }
159
-
160
- //
161
- // Nested Objects.
162
- //
163
-
164
- if (type === 'object') {
165
- const baseNode = findNode(ast, isDiscriminatedUnion);
166
- const typeLiteral = baseNode
167
- ? getDiscriminatedType(baseNode, inputProps.getValue() as any)
168
- : findNode(ast, SchemaAST.isTypeLiteral);
169
-
170
- if (typeLiteral) {
171
- return (
172
- <>
173
- {!inline && <h3 className={mx('text-lg mlb-inputSpacingBlock first:mbs-0')}>{label}</h3>}
174
- <FormFields
175
- schema={Schema.make(typeLiteral)}
176
- path={path}
177
- readonly={readonly}
178
- onQueryRefOptions={onQueryRefOptions}
179
- Custom={Custom}
180
- lookupComponent={lookupComponent}
181
- />
182
- </>
183
- );
184
- }
185
- }
186
-
187
- return null;
188
- };
189
-
190
- export type FormFieldsProps = ThemedClassName<{
191
- testId?: string;
192
- readonly?: boolean;
193
- schema: Schema.Schema.All;
194
- /**
195
- * Path to the current object from the root. Used with nested forms.
196
- */
197
- path?: (string | number)[];
198
- filter?: (props: SchemaProperty<any>[]) => SchemaProperty<any>[];
199
- sort?: string[];
200
- lookupComponent?: ComponentLookup;
201
- /**
202
- * Map of custom renderers for specific properties.
203
- * Prefer lookupComponent for plugin specific input surfaces.
204
- */
205
- Custom?: Partial<Record<string, InputComponent>>;
206
- onQueryRefOptions?: QueryRefOptions;
207
- }>;
208
-
209
- export const FormFields = forwardRef<HTMLDivElement, FormFieldsProps>(
210
- (
211
- { classNames, testId, schema, path, filter, sort, readonly, lookupComponent, Custom, onQueryRefOptions },
212
- forwardRef,
213
- ) => {
214
- const values = useFormValues(path);
215
- const properties = useMemo(() => {
216
- const props = getSchemaProperties(schema.ast, values);
217
- const filtered = filter ? filter(props) : props;
218
- return sort ? filtered.sort((a, b) => sort.indexOf(a.name) - sort.indexOf(b.name)) : filtered;
219
- }, [schema, values, filter, sort]);
220
-
221
- return (
222
- <div role='form' className={mx('is-full', classNames)} ref={forwardRef}>
223
- {properties
224
- .map((property) => {
225
- return (
226
- <FormField
227
- key={property.name}
228
- property={property}
229
- path={[...(path ?? []), property.name]}
230
- readonly={readonly}
231
- onQueryRefOptions={onQueryRefOptions}
232
- lookupComponent={lookupComponent}
233
- Custom={Custom}
234
- />
235
- );
236
- })
237
- .filter(isNotFalsy)}
238
- </div>
239
- );
240
- },
241
- );
@@ -1,110 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import React, {
6
- type FocusEvent,
7
- type PropsWithChildren,
8
- type RefObject,
9
- createContext,
10
- useCallback,
11
- useContext,
12
- useEffect,
13
- useMemo,
14
- } from 'react';
15
-
16
- import { raise } from '@dxos/debug';
17
- import { type BaseObject, getValue } from '@dxos/echo-schema';
18
- import { type SimpleType, createJsonPath } from '@dxos/effect';
19
-
20
- import { type FormHandler, type FormOptions, useForm } from '../../hooks';
21
-
22
- type FormContextValue<T extends BaseObject> = FormHandler<T>;
23
-
24
- const FormContext = createContext<FormContextValue<any> | undefined>(undefined);
25
-
26
- export const useFormContext = <T extends BaseObject>(): FormContextValue<T> => {
27
- return useContext(FormContext) ?? raise(new Error('Missing FormContext'));
28
- };
29
-
30
- export const useFormValues = (path: (string | number)[] = []): any => {
31
- const { values: formValues } = useFormContext();
32
- const jsonPath = createJsonPath(path);
33
- return getValue(formValues, jsonPath) as BaseObject;
34
- };
35
-
36
- export type FormInputStateProps = {
37
- getStatus: () => { status?: 'error'; error?: string };
38
- getValue: <V>() => V | undefined;
39
- onValueChange: (type: SimpleType, value: any) => void;
40
- onBlur: (event: FocusEvent<HTMLElement>) => void;
41
- };
42
-
43
- export const useInputProps = (path: (string | number)[] = []): FormInputStateProps => {
44
- const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
45
-
46
- const stablePath = useMemo(() => path, [Array.isArray(path) ? path.join('.') : path]);
47
- return useMemo(
48
- () => ({
49
- getStatus: () => getStatus(stablePath),
50
- getValue: () => getFormValue(stablePath),
51
- onValueChange: (type: SimpleType, value: any) => onValueChange(stablePath, type, value),
52
- onBlur: () => onTouched(stablePath),
53
- }),
54
- [getStatus, getFormValue, onValueChange, onTouched, stablePath],
55
- );
56
- };
57
-
58
- export const FormProvider = ({
59
- children,
60
- formRef,
61
- autoSave,
62
- ...formOptions
63
- }: PropsWithChildren<
64
- FormOptions<any> & {
65
- formRef?: RefObject<HTMLDivElement>;
66
- autoSave?: boolean;
67
- }
68
- >) => {
69
- const form = useForm(formOptions);
70
-
71
- const handleKeyDown = useCallback(
72
- (event: KeyboardEvent) => {
73
- const keyIsEnter = event.key === 'Enter';
74
- const modifierUsed = event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;
75
- const inputIsTextarea = (event.target as HTMLElement).tagName.toLowerCase() === 'textarea';
76
- const inputOptOut =
77
- (event.target as HTMLElement).hasAttribute('data-no-submit') ||
78
- (event.target as HTMLElement).closest('[data-no-submit]') !== null;
79
-
80
- // Regular inputs: Submit on Enter (no modifiers).
81
- const shouldSubmitRegularInput = !inputIsTextarea && keyIsEnter && !modifierUsed;
82
-
83
- // Textareas: Submit only on Meta+Enter.
84
- const shouldSubmitTextarea = inputIsTextarea && keyIsEnter && event.metaKey;
85
-
86
- if ((shouldSubmitRegularInput || shouldSubmitTextarea) && !inputOptOut) {
87
- if (!autoSave && form.canSave) {
88
- form.handleSave();
89
- }
90
- if (autoSave && form.formIsValid) {
91
- (event.target as HTMLElement).blur();
92
- }
93
- }
94
- },
95
- [form, autoSave],
96
- );
97
-
98
- useEffect(() => {
99
- if (!formRef?.current) {
100
- return;
101
- }
102
-
103
- const formElement = formRef.current;
104
-
105
- formElement.addEventListener('keydown', handleKeyDown);
106
- return () => formElement.removeEventListener('keydown', handleKeyDown);
107
- }, [formRef, form.canSave, form.formIsValid, form.handleSave, autoSave]);
108
-
109
- return <FormContext.Provider value={form}>{children}</FormContext.Provider>;
110
- };
@@ -1,51 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- import React, { type FC } from 'react';
6
-
7
- import { type FormatEnum } from '@dxos/echo-schema';
8
- import { type SimpleType } from '@dxos/effect';
9
- import { Icon, Input, Tooltip } from '@dxos/react-ui';
10
- import { labelSpacing } from '@dxos/react-ui-stack';
11
- import { errorText, mx } from '@dxos/react-ui-theme';
12
-
13
- import { type FormInputStateProps } from './FormContext';
14
-
15
- /**
16
- * Props passed to input components.
17
- */
18
- export type InputProps = {
19
- type: SimpleType;
20
- format?: FormatEnum;
21
- label: string;
22
- disabled?: boolean;
23
- placeholder?: string;
24
- inputOnly?: boolean;
25
- } & FormInputStateProps;
26
-
27
- /**
28
- * Form input component.
29
- */
30
- export type InputComponent = FC<InputProps>;
31
-
32
- export type InputHeaderProps = {
33
- error?: string;
34
- label: string;
35
- readonly?: boolean;
36
- };
37
-
38
- export const InputHeader = ({ error, label, readonly }: InputHeaderProps) => {
39
- const Label = readonly ? 'span' : Input.Label;
40
- const labelProps = readonly ? {} : { classNames: '!mlb-0' };
41
- return (
42
- <div role='none' className={mx('flex justify-between items-center', labelSpacing)}>
43
- <Label {...labelProps}>{label}</Label>
44
- {error && (
45
- <Tooltip.Trigger asChild content={error} side='bottom'>
46
- <Icon icon='ph--warning--regular' size={4} classNames={errorText} />
47
- </Tooltip.Trigger>
48
- )}
49
- </div>
50
- );
51
- };