@dxos/react-ui-form 0.8.4-main.b97322e → 0.8.4-main.bcb3aa67d6

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 (205) hide show
  1. package/README.md +0 -1
  2. package/dist/lib/browser/index.mjs +2315 -2116
  3. package/dist/lib/browser/index.mjs.map +4 -4
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/node-esm/index.mjs +2315 -2116
  6. package/dist/lib/node-esm/index.mjs.map +4 -4
  7. package/dist/lib/node-esm/meta.json +1 -1
  8. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +8 -7
  9. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  10. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +82 -5
  11. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  12. package/dist/types/src/components/Form/Form.d.ts +84 -25
  13. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  14. package/dist/types/src/components/Form/Form.stories.d.ts +127 -55
  15. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  16. package/dist/types/src/components/Form/FormField.d.ts +43 -0
  17. package/dist/types/src/components/Form/FormField.d.ts.map +1 -0
  18. package/dist/types/src/components/Form/FormFieldComponent.d.ts +77 -0
  19. package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -0
  20. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts +9 -0
  21. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +1 -0
  22. package/dist/types/src/components/Form/FormFieldSet.d.ts +18 -0
  23. package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -0
  24. package/dist/types/src/components/Form/fields/ArrayField.d.ts +18 -0
  25. package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -0
  26. package/dist/types/src/components/Form/fields/BooleanField.d.ts +4 -0
  27. package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +1 -0
  28. package/dist/types/src/components/Form/fields/GeoPointField.d.ts +5 -0
  29. package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +1 -0
  30. package/dist/types/src/components/Form/fields/MarkdownField.d.ts +4 -0
  31. package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +1 -0
  32. package/dist/types/src/components/Form/fields/NumberField.d.ts +4 -0
  33. package/dist/types/src/components/Form/fields/NumberField.d.ts.map +1 -0
  34. package/dist/types/src/components/Form/fields/RefField.d.ts +16 -0
  35. package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -0
  36. package/dist/types/src/components/Form/fields/SelectField.d.ts +10 -0
  37. package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -0
  38. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts +5 -0
  39. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -0
  40. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +82 -0
  41. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -0
  42. package/dist/types/src/components/Form/fields/TextAreaField.d.ts +4 -0
  43. package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +1 -0
  44. package/dist/types/src/components/Form/fields/TextField.d.ts +4 -0
  45. package/dist/types/src/components/Form/fields/TextField.d.ts.map +1 -0
  46. package/dist/types/src/components/Form/fields/TupleField.d.ts +6 -0
  47. package/dist/types/src/components/Form/fields/TupleField.d.ts.map +1 -0
  48. package/dist/types/src/components/Form/fields/index.d.ts +12 -0
  49. package/dist/types/src/components/Form/fields/index.d.ts.map +1 -0
  50. package/dist/types/src/components/Form/index.d.ts +2 -4
  51. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  52. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +54 -0
  53. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -0
  54. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +85 -0
  55. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -0
  56. package/dist/types/src/components/ObjectPicker/index.d.ts +2 -0
  57. package/dist/types/src/components/ObjectPicker/index.d.ts.map +1 -0
  58. package/dist/types/src/components/Settings/Settings.d.ts +39 -0
  59. package/dist/types/src/components/Settings/Settings.d.ts.map +1 -0
  60. package/dist/types/src/components/Settings/Settings.stories.d.ts +81 -0
  61. package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -0
  62. package/dist/types/src/components/Settings/index.d.ts +2 -0
  63. package/dist/types/src/components/Settings/index.d.ts.map +1 -0
  64. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +22 -10
  65. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  66. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +84 -8
  67. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  68. package/dist/types/src/components/index.d.ts +2 -2
  69. package/dist/types/src/components/index.d.ts.map +1 -1
  70. package/dist/types/src/components/testing.d.ts +4 -3
  71. package/dist/types/src/components/testing.d.ts.map +1 -1
  72. package/dist/types/src/hooks/index.d.ts +2 -3
  73. package/dist/types/src/hooks/index.d.ts.map +1 -1
  74. package/dist/types/src/hooks/useFormHandler.d.ts +82 -0
  75. package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -0
  76. package/dist/types/src/hooks/useKeyHandler.d.ts +6 -0
  77. package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -0
  78. package/dist/types/src/translations.d.ts +63 -54
  79. package/dist/types/src/translations.d.ts.map +1 -1
  80. package/dist/types/src/util/field.d.ts +3 -3
  81. package/dist/types/src/util/field.d.ts.map +1 -1
  82. package/dist/types/src/util/formatting.d.ts +2 -2
  83. package/dist/types/src/util/formatting.d.ts.map +1 -1
  84. package/dist/types/src/util/index.d.ts +1 -1
  85. package/dist/types/src/util/index.d.ts.map +1 -1
  86. package/dist/types/src/util/properties.d.ts +7 -0
  87. package/dist/types/src/util/properties.d.ts.map +1 -0
  88. package/dist/types/src/util/refs.d.ts +3 -3
  89. package/dist/types/src/util/refs.d.ts.map +1 -1
  90. package/dist/types/tsconfig.tsbuildinfo +1 -1
  91. package/package.json +52 -43
  92. package/src/components/FieldEditor/FieldEditor.stories.tsx +51 -32
  93. package/src/components/FieldEditor/FieldEditor.test.tsx +3 -2
  94. package/src/components/FieldEditor/FieldEditor.tsx +87 -83
  95. package/src/components/Form/AUDIT.md +17 -0
  96. package/src/components/Form/Form.stories.tsx +154 -219
  97. package/src/components/Form/Form.tsx +314 -97
  98. package/src/components/Form/FormField.tsx +305 -0
  99. package/src/components/Form/FormFieldComponent.stories.tsx +65 -0
  100. package/src/components/Form/FormFieldComponent.tsx +171 -0
  101. package/src/components/Form/FormFieldSet.tsx +129 -0
  102. package/src/components/Form/README.md +3 -0
  103. package/src/components/Form/fields/ArrayField.tsx +154 -0
  104. package/src/components/Form/fields/BooleanField.tsx +22 -0
  105. package/src/components/Form/fields/GeoPointField.tsx +120 -0
  106. package/src/components/Form/fields/MarkdownField.tsx +78 -0
  107. package/src/components/Form/fields/NumberField.tsx +39 -0
  108. package/src/components/Form/fields/RefField.tsx +199 -0
  109. package/src/components/Form/fields/SelectField.tsx +65 -0
  110. package/src/components/Form/fields/SelectOptionField.stories.tsx +64 -0
  111. package/src/components/Form/{custom/SelectOptionsInput.tsx → fields/SelectOptionField.tsx} +49 -45
  112. package/src/components/Form/fields/TextAreaField.tsx +38 -0
  113. package/src/components/Form/fields/TextField.tsx +38 -0
  114. package/src/components/Form/{custom/TupleInput.tsx → fields/TupleField.tsx} +17 -10
  115. package/src/components/Form/fields/index.ts +15 -0
  116. package/src/components/Form/index.ts +3 -4
  117. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +210 -0
  118. package/src/components/ObjectPicker/ObjectPicker.tsx +186 -0
  119. package/src/components/{ControlSection → ObjectPicker}/index.ts +1 -1
  120. package/src/components/Settings/Settings.stories.tsx +56 -0
  121. package/src/components/Settings/Settings.tsx +129 -0
  122. package/src/components/Settings/index.ts +5 -0
  123. package/src/components/ViewEditor/ViewEditor.stories.tsx +141 -63
  124. package/src/components/ViewEditor/ViewEditor.test.tsx +17 -18
  125. package/src/components/ViewEditor/ViewEditor.tsx +353 -203
  126. package/src/components/index.ts +2 -3
  127. package/src/components/testing.tsx +33 -13
  128. package/src/hooks/index.ts +2 -3
  129. package/src/hooks/useFormHandler.ts +380 -0
  130. package/src/hooks/useKeyHandler.tsx +37 -0
  131. package/src/translations.ts +66 -58
  132. package/src/util/field.ts +33 -43
  133. package/src/util/formatting.ts +22 -22
  134. package/src/util/index.ts +1 -1
  135. package/src/util/properties.ts +18 -0
  136. package/src/util/refs.ts +17 -19
  137. package/dist/types/src/components/ControlSection/ControlSection.d.ts +0 -23
  138. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +0 -1
  139. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +0 -8
  140. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +0 -1
  141. package/dist/types/src/components/ControlSection/index.d.ts +0 -2
  142. package/dist/types/src/components/ControlSection/index.d.ts.map +0 -1
  143. package/dist/types/src/components/Form/ArrayField.d.ts +0 -16
  144. package/dist/types/src/components/Form/ArrayField.d.ts.map +0 -1
  145. package/dist/types/src/components/Form/Defaults.d.ts +0 -14
  146. package/dist/types/src/components/Form/Defaults.d.ts.map +0 -1
  147. package/dist/types/src/components/Form/FormActions.d.ts +0 -9
  148. package/dist/types/src/components/Form/FormActions.d.ts.map +0 -1
  149. package/dist/types/src/components/Form/FormContent.d.ts +0 -57
  150. package/dist/types/src/components/Form/FormContent.d.ts.map +0 -1
  151. package/dist/types/src/components/Form/FormContext.d.ts +0 -23
  152. package/dist/types/src/components/Form/FormContext.d.ts.map +0 -1
  153. package/dist/types/src/components/Form/Input.d.ts +0 -26
  154. package/dist/types/src/components/Form/Input.d.ts.map +0 -1
  155. package/dist/types/src/components/Form/RefField.d.ts +0 -12
  156. package/dist/types/src/components/Form/RefField.d.ts.map +0 -1
  157. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +0 -4
  158. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +0 -1
  159. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +0 -4
  160. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +0 -1
  161. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +0 -7
  162. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +0 -1
  163. package/dist/types/src/components/Form/custom/TupleInput.d.ts +0 -6
  164. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +0 -1
  165. package/dist/types/src/components/Form/custom/index.d.ts +0 -4
  166. package/dist/types/src/components/Form/custom/index.d.ts.map +0 -1
  167. package/dist/types/src/components/Form/factory.d.ts +0 -8
  168. package/dist/types/src/components/Form/factory.d.ts.map +0 -1
  169. package/dist/types/src/components/deprecated/Form.d.ts +0 -15
  170. package/dist/types/src/components/deprecated/Form.d.ts.map +0 -1
  171. package/dist/types/src/components/deprecated/Form.stories.d.ts +0 -13
  172. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +0 -1
  173. package/dist/types/src/components/deprecated/FormInput.d.ts +0 -16
  174. package/dist/types/src/components/deprecated/FormInput.d.ts.map +0 -1
  175. package/dist/types/src/components/deprecated/index.d.ts +0 -2
  176. package/dist/types/src/components/deprecated/index.d.ts.map +0 -1
  177. package/dist/types/src/hooks/useForm.d.ts +0 -67
  178. package/dist/types/src/hooks/useForm.d.ts.map +0 -1
  179. package/dist/types/src/hooks/useQueryRefOptions.d.ts +0 -23
  180. package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +0 -1
  181. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +0 -8
  182. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +0 -1
  183. package/dist/types/src/util/schema.d.ts +0 -3
  184. package/dist/types/src/util/schema.d.ts.map +0 -1
  185. package/src/components/ControlSection/ControlSection.stories.tsx +0 -41
  186. package/src/components/ControlSection/ControlSection.tsx +0 -139
  187. package/src/components/Form/ArrayField.tsx +0 -124
  188. package/src/components/Form/Defaults.tsx +0 -203
  189. package/src/components/Form/FormActions.tsx +0 -53
  190. package/src/components/Form/FormContent.tsx +0 -240
  191. package/src/components/Form/FormContext.tsx +0 -110
  192. package/src/components/Form/Input.tsx +0 -51
  193. package/src/components/Form/RefField.tsx +0 -213
  194. package/src/components/Form/custom/GeoPointInput.tsx +0 -72
  195. package/src/components/Form/custom/SelectOptionsInput.stories.tsx +0 -61
  196. package/src/components/Form/custom/index.ts +0 -7
  197. package/src/components/Form/factory.tsx +0 -31
  198. package/src/components/deprecated/Form.stories.tsx +0 -62
  199. package/src/components/deprecated/Form.tsx +0 -119
  200. package/src/components/deprecated/FormInput.tsx +0 -71
  201. package/src/components/deprecated/index.ts +0 -5
  202. package/src/hooks/useForm.ts +0 -291
  203. package/src/hooks/useQueryRefOptions.ts +0 -53
  204. package/src/hooks/useRefQueryLookupHandler.ts +0 -39
  205. package/src/util/schema.ts +0 -14
@@ -1,213 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { type SchemaAST } from 'effect';
6
- import React, { useCallback, useMemo, type FocusEvent } from 'react';
7
-
8
- import {
9
- Expando,
10
- getTypeAnnotation,
11
- Ref,
12
- ReferenceAnnotationId,
13
- type ReferenceAnnotationValue,
14
- } from '@dxos/echo-schema';
15
- import { findAnnotation } from '@dxos/effect';
16
- import { DXN } from '@dxos/keys';
17
- import { DxRefTag } from '@dxos/lit-ui/react';
18
- import { Input, useTranslation } from '@dxos/react-ui';
19
- import { TagPicker, type TagPickerMode, type TagPickerItemData } from '@dxos/react-ui-tag-picker';
20
- import { descriptionText, mx } from '@dxos/react-ui-theme';
21
- import { isNonNullable } from '@dxos/util';
22
-
23
- import { TextInput } from './Defaults';
24
- import { InputHeader, type InputProps } from './Input';
25
- import { type QueryRefOptions, useQueryRefOptions } from '../../hooks';
26
- import { translationKey } from '../../translations';
27
-
28
- type RefFieldProps = InputProps & {
29
- ast?: SchemaAST.AST;
30
- array?: boolean;
31
- onQueryRefOptions?: QueryRefOptions;
32
- };
33
-
34
- export const RefField = ({
35
- type,
36
- label,
37
- disabled,
38
- placeholder,
39
- inputOnly,
40
- array,
41
- ast,
42
- getValue,
43
- onBlur,
44
- onQueryRefOptions,
45
- onValueChange,
46
- ...restInputProps
47
- }: RefFieldProps) => {
48
- const { t } = useTranslation(translationKey);
49
- const refTypeInfo = useMemo(
50
- () => (ast ? findAnnotation<ReferenceAnnotationValue>(ast, ReferenceAnnotationId) : undefined),
51
- [ast],
52
- );
53
- const { options: availableOptions, loading: _loading } = useQueryRefOptions({ refTypeInfo, onQueryRefOptions });
54
-
55
- if ((refTypeInfo && refTypeInfo?.typename === getTypeAnnotation(Expando)?.typename) || !onQueryRefOptions) {
56
- // If ref type is expando, fall back to taking a DXN in string format.
57
- return (
58
- <RefFieldFallback
59
- {...{ type, label, placeholder, disabled, inputOnly, getValue, onBlur, onValueChange, ...restInputProps }}
60
- />
61
- );
62
- }
63
-
64
- const handleGetValue = useCallback((): TagPickerItemData[] => {
65
- const formValue = getValue();
66
-
67
- const unknownToRefOption = (val: unknown) => {
68
- if (Ref.isRef(val)) {
69
- const dxnString = val.dxn.toString();
70
- const matchingOption = availableOptions.find((option) => option.id === dxnString);
71
- if (matchingOption) {
72
- return matchingOption;
73
- }
74
- }
75
- return undefined;
76
- };
77
-
78
- if (array && Array.isArray(formValue)) {
79
- return formValue.map(unknownToRefOption).filter(isNonNullable) ?? [];
80
- }
81
-
82
- const option = unknownToRefOption(formValue);
83
- if (option) {
84
- return [option];
85
- }
86
-
87
- return [];
88
- }, [getValue, availableOptions, array]);
89
-
90
- const handleSearch = useCallback(
91
- (text: string, ids: string[]): TagPickerItemData[] =>
92
- availableOptions
93
- .filter((option) => !ids.includes(option.id))
94
- .filter((option) => option.label.toLowerCase().includes(text.toLowerCase()))
95
- .map((option) => ({ id: option.id, label: option.label, hue: option.hue as any })),
96
- [availableOptions],
97
- );
98
-
99
- const handleUpdate = useCallback(
100
- (ids: string[]) => {
101
- if (ids.length === 0) {
102
- onValueChange('object', undefined);
103
- return;
104
- }
105
-
106
- const refs = ids
107
- .map((id) => {
108
- const item = availableOptions.find((option) => option.id === id);
109
- if (item) {
110
- const dxn = DXN.parse(item.id);
111
- return Ref.fromDXN(dxn);
112
- }
113
- return null;
114
- })
115
- .filter(isNonNullable);
116
-
117
- if (array) {
118
- onValueChange('object', refs);
119
- } else {
120
- onValueChange('object', refs[0]);
121
- }
122
- },
123
- [availableOptions, array, onValueChange],
124
- );
125
-
126
- const tagPickerMode: TagPickerMode = useMemo(() => (array ? 'multi-select' : 'single-select'), [array]);
127
-
128
- if (!refTypeInfo) {
129
- return null;
130
- }
131
-
132
- const { status, error } = restInputProps.getStatus();
133
-
134
- const items = handleGetValue();
135
-
136
- // NOTE(thure): I left both predicates in-place in case we decide to add variants which do render readonly but empty values.
137
- return disabled && items.length < 1 ? null : (
138
- <Input.Root validationValence={status}>
139
- {!inputOnly && <InputHeader error={error} label={label} />}
140
- <div data-no-submit>
141
- {disabled ? (
142
- items.length < 1 ? (
143
- <p className={mx(descriptionText, 'mbe-2')}>{t('empty readonly ref field label')}</p>
144
- ) : (
145
- items.map((item) => (
146
- <DxRefTag key={item.id} refid={item.id} rootclassname='mie-1' style={{ marginInlineEnd: '.25rem' }}>
147
- {item.label}
148
- </DxRefTag>
149
- ))
150
- )
151
- ) : (
152
- <TagPicker
153
- readonly={disabled}
154
- items={items}
155
- mode={tagPickerMode}
156
- onBlur={(event) => onBlur(event as unknown as FocusEvent<HTMLElement>)}
157
- onUpdate={handleUpdate}
158
- onSearch={handleSearch}
159
- classNames='bg-inputSurface p-1.5 rounded-sm'
160
- />
161
- )}
162
- </div>
163
- {inputOnly && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
164
- </Input.Root>
165
- );
166
- };
167
-
168
- const RefFieldFallback = ({
169
- type,
170
- label,
171
- disabled,
172
- placeholder,
173
- inputOnly,
174
- getValue,
175
- onValueChange,
176
- ...restInputProps
177
- }: InputProps) => {
178
- const handleOnValueChange = (_type: any, dxnString: string) => {
179
- const dxn = DXN.tryParse(dxnString);
180
- if (dxn) {
181
- onValueChange?.('object', Ref.fromDXN(dxn));
182
- } else if (dxnString === '') {
183
- onValueChange?.('object', undefined);
184
- } else {
185
- onValueChange?.('string', dxnString);
186
- }
187
- };
188
-
189
- const handleGetValue = () => {
190
- const formValue = getValue();
191
- if (typeof formValue === 'string') {
192
- return formValue;
193
- }
194
- if (Ref.isRef(formValue)) {
195
- return formValue.dxn.toString();
196
- }
197
-
198
- return undefined;
199
- };
200
-
201
- return (
202
- <TextInput
203
- type={type}
204
- label={label}
205
- disabled={disabled}
206
- placeholder={placeholder}
207
- inputOnly={inputOnly}
208
- getValue={handleGetValue as <V>() => V | undefined}
209
- onValueChange={handleOnValueChange}
210
- {...restInputProps}
211
- />
212
- );
213
- };
@@ -1,72 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- import React, { useCallback, useState, useEffect, useMemo, type ChangeEvent } from 'react';
6
-
7
- import { type GeoPoint, GeoLocation } from '@dxos/echo-schema';
8
- import { Input, useTranslation } from '@dxos/react-ui';
9
- import { safeParseFloat } from '@dxos/util';
10
-
11
- import { translationKey } from '../../../translations';
12
- import { InputHeader, type InputProps } from '../Input';
13
-
14
- export const GeoPointInput = ({ type, label, disabled, getStatus, getValue, onValueChange, onBlur }: InputProps) => {
15
- const { t } = useTranslation(translationKey);
16
- const { status, error } = getStatus();
17
- const geoPoint = useMemo<GeoPoint>(() => getValue<GeoPoint>() ?? [0, 0], [getValue]);
18
- const geoLocation = useMemo(() => GeoLocation.fromGeoPoint(geoPoint), [geoPoint]);
19
-
20
- const [longitudeText, setLongitudeText] = useState(geoLocation.longitude?.toString());
21
- const [latitudeText, setLatitudeText] = useState(geoLocation.latitude?.toString());
22
-
23
- useEffect(() => {
24
- const location = GeoLocation.fromGeoPoint(geoPoint);
25
- setLongitudeText(location.longitude.toString());
26
- setLatitudeText(location.latitude.toString());
27
- }, [geoPoint]);
28
-
29
- const handleCoordinateChange = useCallback(
30
- (coordinateType: keyof Pick<GeoLocation, 'longitude' | 'latitude'>, setText: (text: string) => void) =>
31
- (event: ChangeEvent<HTMLInputElement>) => {
32
- const inputText = event.target.value;
33
- setText(inputText);
34
- if (inputText !== '' && inputText !== '-') {
35
- const coord = safeParseFloat(inputText);
36
- if (coord !== undefined && !isNaN(coord)) {
37
- const currentLocation = GeoLocation.fromGeoPoint(getValue<GeoPoint>() ?? [0, 0]);
38
- const newLocation = { ...currentLocation, [coordinateType]: coord };
39
- const newValue = GeoLocation.toGeoPoint(newLocation);
40
- onValueChange(type, newValue);
41
- }
42
- }
43
- },
44
- [getValue, onValueChange, type],
45
- );
46
-
47
- return (
48
- <Input.Root validationValence={status}>
49
- <InputHeader error={error} label={label} />
50
- <div role='none' className='grid grid-cols-2 gap-2'>
51
- <Input.TextInput
52
- type='text'
53
- pattern='^-?[0-9]*\.?[0-9]*$'
54
- disabled={disabled}
55
- placeholder={t('placeholder latitude')}
56
- value={latitudeText}
57
- onChange={handleCoordinateChange('latitude', setLatitudeText)}
58
- onBlur={onBlur}
59
- />
60
- <Input.TextInput
61
- type='text'
62
- pattern='^-?[0-9]*\.?[0-9]*$'
63
- disabled={disabled}
64
- placeholder={t('placeholder longitude')}
65
- value={longitudeText}
66
- onChange={handleCoordinateChange('longitude', setLongitudeText)}
67
- onBlur={onBlur}
68
- />
69
- </div>
70
- </Input.Root>
71
- );
72
- };
@@ -1,61 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import '@dxos-theme';
6
-
7
- import { type Meta, type StoryObj } from '@storybook/react-vite';
8
- import { Schema } from 'effect';
9
- import React, { useState } from 'react';
10
-
11
- import { SelectOptionSchema, type SelectOption } from '@dxos/echo-schema';
12
- import { withLayout, withTheme } from '@dxos/storybook-utils';
13
-
14
- import { SelectOptionInput } from './SelectOptionsInput';
15
- import { translations } from '../../../translations';
16
- import { TestLayout, TestPanel } from '../../testing';
17
- import { Form } from '../Form';
18
-
19
- const schema = Schema.Struct({
20
- options: Schema.Array(SelectOptionSchema).pipe(Schema.mutable),
21
- }).pipe(Schema.mutable);
22
-
23
- const DefaultStory = () => {
24
- const [values, setValues] = useState<{ options: SelectOption[] }>({
25
- // TODO(Zaymon): Pick colors from our limited set.
26
- options: [
27
- { id: 'opt_1', title: 'First option', color: 'amber' },
28
- { id: 'opt_2', title: 'Second option', color: 'emerald' },
29
- { id: 'opt_3', title: 'Third option', color: 'rose' },
30
- ],
31
- });
32
-
33
- return (
34
- <TestLayout>
35
- <TestPanel>
36
- <Form
37
- schema={schema}
38
- values={values}
39
- onSave={(vals) => setValues(vals)}
40
- Custom={{ options: SelectOptionInput }}
41
- />
42
- </TestPanel>
43
- </TestLayout>
44
- );
45
- };
46
-
47
- const meta: Meta = {
48
- title: 'ui/react-ui-form/SelectOptionsInput',
49
- component: SelectOptionInput,
50
- render: DefaultStory,
51
- decorators: [withTheme, withLayout({ fullscreen: true })],
52
- parameters: {
53
- translations,
54
- },
55
- };
56
-
57
- export default meta;
58
-
59
- type Story = StoryObj;
60
-
61
- export const Default: Story = {};
@@ -1,7 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- export * from './GeoPointInput';
6
- export * from './SelectOptionsInput';
7
- export * from './TupleInput';
@@ -1,31 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- import { FormatEnum } from '@dxos/echo-schema';
6
- import { type SimpleType } from '@dxos/effect';
7
-
8
- import { BooleanInput, MarkdownInput, NumberInput, TextInput } from './Defaults';
9
- import { type InputComponent } from './Input';
10
- import { GeoPointInput } from './custom';
11
-
12
- /**
13
- * Get property input component.
14
- */
15
- export const getInputComponent = (type: SimpleType, format?: FormatEnum | string): InputComponent | undefined => {
16
- switch (format) {
17
- case FormatEnum.GeoPoint:
18
- return GeoPointInput;
19
- case FormatEnum.Markdown:
20
- return MarkdownInput;
21
- }
22
-
23
- switch (type) {
24
- case 'string':
25
- return TextInput;
26
- case 'number':
27
- return NumberInput;
28
- case 'boolean':
29
- return BooleanInput;
30
- }
31
- };
@@ -1,62 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- import '@dxos-theme';
6
-
7
- import { type Meta, type StoryObj } from '@storybook/react-vite';
8
- import React from 'react';
9
-
10
- import { live } from '@dxos/live-object';
11
- import { testData, TestSchema, testView, type TestType } from '@dxos/schema/testing';
12
- import { withLayout, withTheme } from '@dxos/storybook-utils';
13
-
14
- import { DeprecatedForm, type DeprecatedFormProps } from './Form';
15
- import { translations } from '../../translations';
16
- import { TestLayout, TestPanel } from '../testing';
17
-
18
- const DefaultStory = (props: DeprecatedFormProps) => (
19
- <TestLayout json={{ props }}>
20
- <TestPanel>
21
- <DeprecatedForm {...props} />
22
- </TestPanel>
23
- </TestLayout>
24
- );
25
-
26
- const meta: Meta<typeof DefaultStory> = {
27
- title: 'ui/react-ui-form/DeprecatedForm',
28
- component: DeprecatedForm,
29
- render: DefaultStory,
30
- decorators: [withLayout({ fullscreen: true }), withTheme],
31
- parameters: {
32
- translations,
33
- },
34
- };
35
-
36
- export default meta;
37
-
38
- type Story = StoryObj<DeprecatedFormProps<TestType>>;
39
-
40
- export const Default: Story = {
41
- args: {
42
- object: live(testData),
43
- projection: live(testView).projection,
44
- schema: TestSchema,
45
- },
46
- };
47
-
48
- export const Empty: Story = {
49
- args: {
50
- projection: live(testView).projection,
51
- schema: TestSchema,
52
- },
53
- };
54
-
55
- export const Readonly: Story = {
56
- args: {
57
- object: live(testData),
58
- projection: live(testView).projection,
59
- schema: TestSchema,
60
- readonly: true,
61
- },
62
- };
@@ -1,119 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- import { SchemaAST, type Schema, Option, pipe } from 'effect';
6
- import React, { useState } from 'react';
7
-
8
- import { findProperty } from '@dxos/effect';
9
- import { Input, type ThemedClassName, type TextInputProps as NativeTextInputProps } from '@dxos/react-ui';
10
- import { mx } from '@dxos/react-ui-theme';
11
- import { getFieldValue, setFieldValue, type Projection } from '@dxos/schema';
12
-
13
- export type DeprecatedFormProps<T extends {} = {}> = ThemedClassName<{
14
- projection: Projection;
15
- object: T;
16
- schema?: Schema.Schema<T>;
17
- readonly?: boolean;
18
- }>;
19
-
20
- /**
21
- * Schema-based object form.
22
- */
23
- export const DeprecatedForm = <T extends {} = {}>({
24
- classNames,
25
- projection,
26
- object,
27
- schema,
28
- readonly,
29
- }: DeprecatedFormProps<T>) => {
30
- return (
31
- <div role='none' className={mx('flex flex-col w-full gap-2 p-2', classNames)}>
32
- {projection.fields.map((field) => {
33
- const prop = schema && findProperty(schema, field.path);
34
- const title = (prop && pipe(SchemaAST.getTitleAnnotation(prop), Option.getOrUndefined)) ?? '';
35
- const description = (prop && pipe(SchemaAST.getDescriptionAnnotation(prop), Option.getOrUndefined)) ?? title;
36
- // const format =
37
- // (prop && (getPatternAnnotation(prop) ?? (SchemaAST.isNumberKeyword(prop) && RealNumberFormat))) || undefined;
38
-
39
- //
40
- // Boolean
41
- //
42
-
43
- if (prop && SchemaAST.isBooleanKeyword(prop)) {
44
- const value = object && getFieldValue(object, field);
45
- const onChange = (checked: boolean) => {
46
- setFieldValue(object, field, checked);
47
- };
48
-
49
- return (
50
- <div key={field.path} className='flex flex-col w-full gap-1'>
51
- <Input.Root>
52
- <Input.Label classNames='px-1'>{title}</Input.Label>
53
- <Input.Switch disabled={readonly} checked={!!value} onCheckedChange={(checked) => onChange(checked)} />
54
- </Input.Root>
55
- </div>
56
- );
57
- }
58
-
59
- //
60
- // Strings
61
- // TODO(burdon): Other types:
62
- // - RichText editor
63
- // - Numbers (with formatting: commas, currency, etc.)
64
- // - Date (https://ark-ui.com/react/docs/components/date-picker)
65
- // - Enum (single/multi-select)
66
- //
67
-
68
- const value = object ? getFieldValue(object, field) : undefined;
69
-
70
- return (
71
- <div key={field.path} className='flex flex-col w-full gap-1'>
72
- <Input.Root>
73
- <Input.Label classNames='px-1'>{title}</Input.Label>
74
- <TextInput
75
- disabled={readonly}
76
- placeholder={description}
77
- // format={format}
78
- value={value ? String(value) : ''}
79
- onChange={(event) => setFieldValue(object, field, event.target.value)}
80
- />
81
- </Input.Root>
82
- </div>
83
- );
84
- })}
85
- </div>
86
- );
87
- };
88
-
89
- type TextInputProps = {
90
- // format?: PatternAnnotation;
91
- } & Omit<NativeTextInputProps, 'style'>;
92
-
93
- /**
94
- * @deprecated
95
- */
96
- // TODO(burdon): Remove.
97
- // TODO(burdon): Use schema validation directly.
98
- const TextInput = ({ onChange, ...props }: TextInputProps) => {
99
- const [valid, _setValid] = useState(true);
100
-
101
- const handleChange: NativeTextInputProps['onChange'] = (ev) => {
102
- // const text = ev.target.value;
103
- // if (!format?.filter || text.match(format.filter)) {
104
- onChange?.(ev);
105
- // if (format?.valid) {
106
- // setValid(format.valid.test(text));
107
- // }
108
- // }
109
- };
110
-
111
- return (
112
- <Input.TextInput
113
- {...props}
114
- // TODO(burdon): validationValence on Input.
115
- style={valid ? {} : ({ '--tw-ring-color': 'red' } as any)}
116
- onChange={handleChange}
117
- />
118
- );
119
- };
@@ -1,71 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import React, { type JSX, type PropsWithChildren } from 'react';
6
-
7
- import { Input, type ThemedClassName } from '@dxos/react-ui';
8
- import { mx, textBlockWidth } from '@dxos/react-ui-theme';
9
-
10
- /**
11
- * @deprecated
12
- */
13
- export const DeprecatedFormContainer = ({ children, classNames }: ThemedClassName<PropsWithChildren>) => {
14
- return (
15
- <div role='none' className='container-max-width p-4 justify-center overflow-x-hidden overflow-y-auto'>
16
- <div role='form' className={mx('flex flex-col', textBlockWidth, classNames)}>
17
- {children}
18
- </div>
19
- </div>
20
- );
21
- };
22
-
23
- export type DeprecatedFormInputProps = {
24
- label: string;
25
- description?: JSX.Element;
26
- secondary?: JSX.Element;
27
- };
28
-
29
- /**
30
- * @deprecated
31
- */
32
- export const DeprecatedFormInput = ({
33
- label,
34
- description,
35
- secondary,
36
- children,
37
- }: PropsWithChildren<DeprecatedFormInputProps>) => {
38
- const primary = (
39
- <div role='none' className='flex w-full gap-4 py-1'>
40
- <Input.Root>
41
- <div role='none' className='flex flex-col w-full'>
42
- <Input.Label classNames='flex min-bs-[--rail-action] items-center' style={{ fontSize: 'unset' }}>
43
- {label}
44
- </Input.Label>
45
- {description && (
46
- <Input.DescriptionAndValidation classNames='mbs-0.5'>
47
- <Input.Description>{description}</Input.Description>
48
- </Input.DescriptionAndValidation>
49
- )}
50
- </div>
51
-
52
- <div role='none'>
53
- <div role='none' className='flex min-bs-[--rail-action] items-center'>
54
- {children}
55
- </div>
56
- </div>
57
- </Input.Root>
58
- </div>
59
- );
60
-
61
- if (secondary) {
62
- return (
63
- <div role='none'>
64
- {primary}
65
- {secondary}
66
- </div>
67
- );
68
- }
69
-
70
- return primary;
71
- };
@@ -1,5 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- export * from './FormInput';