@dxos/react-ui-form 0.8.4-main.70d3990 → 0.8.4-main.74a063c4e0

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 (183) hide show
  1. package/README.md +0 -1
  2. package/dist/lib/browser/index.mjs +2319 -2347
  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 +2319 -2347
  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 +66 -60
  11. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  12. package/dist/types/src/components/Form/Form.d.ts +87 -27
  13. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  14. package/dist/types/src/components/Form/Form.stories.d.ts +111 -150
  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 +23 -10
  53. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
  54. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +62 -59
  55. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
  56. package/dist/types/src/components/Settings/Settings.d.ts +39 -0
  57. package/dist/types/src/components/Settings/Settings.d.ts.map +1 -0
  58. package/dist/types/src/components/Settings/Settings.stories.d.ts +81 -0
  59. package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -0
  60. package/dist/types/src/components/Settings/index.d.ts +2 -0
  61. package/dist/types/src/components/Settings/index.d.ts.map +1 -0
  62. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +9 -11
  63. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  64. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +66 -62
  65. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  66. package/dist/types/src/components/index.d.ts +1 -1
  67. package/dist/types/src/components/index.d.ts.map +1 -1
  68. package/dist/types/src/components/testing.d.ts +0 -2
  69. package/dist/types/src/components/testing.d.ts.map +1 -1
  70. package/dist/types/src/hooks/index.d.ts +2 -3
  71. package/dist/types/src/hooks/index.d.ts.map +1 -1
  72. package/dist/types/src/hooks/useFormHandler.d.ts +82 -0
  73. package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -0
  74. package/dist/types/src/hooks/useKeyHandler.d.ts +7 -0
  75. package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -0
  76. package/dist/types/src/translations.d.ts +63 -60
  77. package/dist/types/src/translations.d.ts.map +1 -1
  78. package/dist/types/src/util/index.d.ts +1 -1
  79. package/dist/types/src/util/index.d.ts.map +1 -1
  80. package/dist/types/src/util/properties.d.ts +7 -0
  81. package/dist/types/src/util/properties.d.ts.map +1 -0
  82. package/dist/types/src/util/refs.d.ts +2 -2
  83. package/dist/types/src/util/refs.d.ts.map +1 -1
  84. package/dist/types/tsconfig.tsbuildinfo +1 -1
  85. package/package.json +51 -48
  86. package/src/components/FieldEditor/FieldEditor.stories.tsx +43 -25
  87. package/src/components/FieldEditor/FieldEditor.test.tsx +0 -1
  88. package/src/components/FieldEditor/FieldEditor.tsx +73 -77
  89. package/src/components/Form/AUDIT.md +17 -0
  90. package/src/components/Form/Form.stories.tsx +147 -249
  91. package/src/components/Form/Form.tsx +324 -110
  92. package/src/components/Form/FormField.tsx +304 -0
  93. package/src/components/Form/FormFieldComponent.stories.tsx +65 -0
  94. package/src/components/Form/FormFieldComponent.tsx +171 -0
  95. package/src/components/Form/FormFieldSet.tsx +128 -0
  96. package/src/components/Form/README.md +3 -0
  97. package/src/components/Form/fields/ArrayField.tsx +154 -0
  98. package/src/components/Form/fields/BooleanField.tsx +22 -0
  99. package/src/components/Form/fields/GeoPointField.tsx +120 -0
  100. package/src/components/Form/fields/MarkdownField.tsx +78 -0
  101. package/src/components/Form/fields/NumberField.tsx +39 -0
  102. package/src/components/Form/fields/RefField.tsx +198 -0
  103. package/src/components/Form/fields/SelectField.tsx +65 -0
  104. package/src/components/Form/{custom/SelectOptionsInput.stories.tsx → fields/SelectOptionField.stories.tsx} +19 -17
  105. package/src/components/Form/{custom/SelectOptionsInput.tsx → fields/SelectOptionField.tsx} +29 -24
  106. package/src/components/Form/fields/TextAreaField.tsx +38 -0
  107. package/src/components/Form/fields/TextField.tsx +38 -0
  108. package/src/components/Form/{custom/TupleInput.tsx → fields/TupleField.tsx} +16 -9
  109. package/src/components/Form/fields/index.ts +15 -0
  110. package/src/components/Form/index.ts +3 -4
  111. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +18 -16
  112. package/src/components/ObjectPicker/ObjectPicker.tsx +103 -78
  113. package/src/components/Settings/Settings.stories.tsx +55 -0
  114. package/src/components/Settings/Settings.tsx +129 -0
  115. package/src/components/{ControlSection → Settings}/index.ts +1 -1
  116. package/src/components/ViewEditor/ViewEditor.stories.tsx +44 -39
  117. package/src/components/ViewEditor/ViewEditor.test.tsx +5 -5
  118. package/src/components/ViewEditor/ViewEditor.tsx +295 -197
  119. package/src/components/index.ts +1 -1
  120. package/src/components/testing.tsx +23 -9
  121. package/src/hooks/index.ts +2 -3
  122. package/src/hooks/useFormHandler.ts +380 -0
  123. package/src/hooks/useKeyHandler.tsx +37 -0
  124. package/src/translations.ts +66 -65
  125. package/src/util/index.ts +1 -1
  126. package/src/util/properties.ts +18 -0
  127. package/src/util/refs.ts +13 -15
  128. package/dist/types/src/components/ControlSection/ControlSection.d.ts +0 -23
  129. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +0 -1
  130. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +0 -84
  131. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +0 -1
  132. package/dist/types/src/components/ControlSection/index.d.ts +0 -2
  133. package/dist/types/src/components/ControlSection/index.d.ts.map +0 -1
  134. package/dist/types/src/components/Form/ArrayField.d.ts +0 -16
  135. package/dist/types/src/components/Form/ArrayField.d.ts.map +0 -1
  136. package/dist/types/src/components/Form/Defaults.d.ts +0 -14
  137. package/dist/types/src/components/Form/Defaults.d.ts.map +0 -1
  138. package/dist/types/src/components/Form/FormActions.d.ts +0 -9
  139. package/dist/types/src/components/Form/FormActions.d.ts.map +0 -1
  140. package/dist/types/src/components/Form/FormContent.d.ts +0 -63
  141. package/dist/types/src/components/Form/FormContent.d.ts.map +0 -1
  142. package/dist/types/src/components/Form/FormContext.d.ts +0 -23
  143. package/dist/types/src/components/Form/FormContext.d.ts.map +0 -1
  144. package/dist/types/src/components/Form/Input.d.ts +0 -29
  145. package/dist/types/src/components/Form/Input.d.ts.map +0 -1
  146. package/dist/types/src/components/Form/RefField.d.ts +0 -22
  147. package/dist/types/src/components/Form/RefField.d.ts.map +0 -1
  148. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +0 -4
  149. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +0 -1
  150. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +0 -4
  151. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +0 -1
  152. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +0 -79
  153. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +0 -1
  154. package/dist/types/src/components/Form/custom/TupleInput.d.ts +0 -6
  155. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +0 -1
  156. package/dist/types/src/components/Form/custom/index.d.ts +0 -4
  157. package/dist/types/src/components/Form/custom/index.d.ts.map +0 -1
  158. package/dist/types/src/components/Form/factory.d.ts +0 -8
  159. package/dist/types/src/components/Form/factory.d.ts.map +0 -1
  160. package/dist/types/src/hooks/useForm.d.ts +0 -67
  161. package/dist/types/src/hooks/useForm.d.ts.map +0 -1
  162. package/dist/types/src/hooks/useQueryRefOptions.d.ts +0 -29
  163. package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +0 -1
  164. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +0 -8
  165. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +0 -1
  166. package/dist/types/src/util/schema.d.ts +0 -3
  167. package/dist/types/src/util/schema.d.ts.map +0 -1
  168. package/src/components/ControlSection/ControlSection.stories.tsx +0 -38
  169. package/src/components/ControlSection/ControlSection.tsx +0 -153
  170. package/src/components/Form/ArrayField.tsx +0 -127
  171. package/src/components/Form/Defaults.tsx +0 -205
  172. package/src/components/Form/FormActions.tsx +0 -59
  173. package/src/components/Form/FormContent.tsx +0 -362
  174. package/src/components/Form/FormContext.tsx +0 -110
  175. package/src/components/Form/Input.tsx +0 -54
  176. package/src/components/Form/RefField.tsx +0 -283
  177. package/src/components/Form/custom/GeoPointInput.tsx +0 -80
  178. package/src/components/Form/custom/index.ts +0 -7
  179. package/src/components/Form/factory.tsx +0 -34
  180. package/src/hooks/useForm.ts +0 -296
  181. package/src/hooks/useQueryRefOptions.ts +0 -62
  182. package/src/hooks/useRefQueryLookupHandler.ts +0 -39
  183. package/src/util/schema.ts +0 -12
@@ -6,17 +6,16 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import * as Schema from 'effect/Schema';
7
7
  import React, { useState } from 'react';
8
8
 
9
- import { type SelectOption, SelectOptionSchema } from '@dxos/echo/internal';
10
- import { withTheme } from '@dxos/react-ui/testing';
9
+ import { SelectOption } from '@dxos/echo/internal';
10
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
11
 
12
12
  import { translations } from '../../../translations';
13
- import { TestLayout, TestPanel } from '../../testing';
13
+ import { TestLayout } from '../../testing';
14
14
  import { Form } from '../Form';
15
-
16
- import { SelectOptionInput } from './SelectOptionsInput';
15
+ import { SelectOptionField } from './SelectOptionField';
17
16
 
18
17
  const schema = Schema.Struct({
19
- options: Schema.Array(SelectOptionSchema).pipe(Schema.mutable),
18
+ options: Schema.Array(SelectOption).pipe(Schema.mutable),
20
19
  }).pipe(Schema.mutable);
21
20
 
22
21
  const DefaultStory = () => {
@@ -31,23 +30,26 @@ const DefaultStory = () => {
31
30
 
32
31
  return (
33
32
  <TestLayout>
34
- <TestPanel>
35
- <Form
36
- schema={schema}
37
- values={values}
38
- onSave={(vals) => setValues(vals)}
39
- Custom={{ options: SelectOptionInput }}
40
- />
41
- </TestPanel>
33
+ <Form.Root
34
+ schema={schema}
35
+ values={values}
36
+ fieldMap={{ options: SelectOptionField }}
37
+ onSave={(values) => setValues(values)}
38
+ >
39
+ <Form.Content>
40
+ <Form.FieldSet />
41
+ <Form.Actions />
42
+ </Form.Content>
43
+ </Form.Root>
42
44
  </TestLayout>
43
45
  );
44
46
  };
45
47
 
46
48
  const meta = {
47
- title: 'ui/react-ui-form/SelectOptionsInput',
48
- component: SelectOptionInput as any,
49
+ title: 'ui/react-ui-form/SelectOptionField',
50
+ component: SelectOptionField as any,
49
51
  render: DefaultStory,
50
- decorators: [withTheme],
52
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
51
53
  parameters: {
52
54
  layout: 'fullscreen',
53
55
  translations,
@@ -9,25 +9,32 @@ import { PublicKey } from '@dxos/keys';
9
9
  import { type ChromaticPalette, IconButton, Input, Tag, useTranslation } from '@dxos/react-ui';
10
10
  import { List } from '@dxos/react-ui-list';
11
11
  import { HuePicker } from '@dxos/react-ui-pickers';
12
- import { hues, subtleHover } from '@dxos/react-ui-theme';
12
+ import { hues, osTranslations, subtleHover } from '@dxos/ui-theme';
13
13
 
14
14
  import { translationKey } from '../../../translations';
15
- import { InputHeader, type InputProps } from '../Input';
16
-
17
- export const SelectOptionInput = ({ type, label, readonly, getStatus, getValue, onValueChange }: InputProps) => {
15
+ import { type FormFieldComponentProps, FormFieldLabel } from '../FormFieldComponent';
16
+
17
+ export const SelectOptionField = ({
18
+ type,
19
+ readonly,
20
+ label,
21
+ getStatus,
22
+ getValue,
23
+ onValueChange,
24
+ }: FormFieldComponentProps<SelectOption[] | undefined>) => {
25
+ const { t } = useTranslation(translationKey);
26
+ const { status, error } = getStatus();
18
27
  const [selected, setSelectedId] = useState<string | null>(null);
19
28
  const [isNewOption, setIsNewOption] = useState(false);
20
29
  const inputRef = useRef<HTMLInputElement>(null);
21
- const { t } = useTranslation(translationKey);
22
- const { status, error } = getStatus();
23
- const options = getValue<SelectOption[] | undefined>();
30
+ const options = getValue();
24
31
 
25
32
  // Initialization.
26
33
  useEffect(() => {
27
34
  if (options === undefined) {
28
35
  onValueChange(type, []);
29
36
  }
30
- }, [options, onValueChange, type]);
37
+ }, [options, type, onValueChange]);
31
38
 
32
39
  const randomHue = () => {
33
40
  const usedHues = new Set(options?.map((option) => option.color) ?? []);
@@ -88,8 +95,8 @@ export const SelectOptionInput = ({ type, label, readonly, getStatus, getValue,
88
95
  [options, type, onValueChange],
89
96
  );
90
97
 
91
- const handleKeyDown = useCallback((e: KeyboardEvent<HTMLInputElement>) => {
92
- if (e.key === 'Enter') {
98
+ const handleKeyDown = useCallback((ev: KeyboardEvent<HTMLInputElement>) => {
99
+ if (ev.key === 'Enter') {
93
100
  setSelectedId(null);
94
101
  }
95
102
  }, []);
@@ -111,23 +118,23 @@ export const SelectOptionInput = ({ type, label, readonly, getStatus, getValue,
111
118
 
112
119
  return (
113
120
  <Input.Root validationValence={status}>
114
- <InputHeader error={error} label={label} />
121
+ <FormFieldLabel error={error} readonly={readonly} label={label} />
115
122
  <div role='none'>
116
123
  {options && (
117
124
  <List.Root
118
125
  items={options}
119
- isItem={(item) => true}
126
+ isItem={(_item) => true}
120
127
  onMove={readonly ? undefined : handleMove}
121
128
  readonly={!!readonly}
122
129
  >
123
130
  {({ items }) => (
124
- <div role='list' className='is-full overflow-auto'>
131
+ <div role='list' className='w-full overflow-auto'>
125
132
  {items?.map((item) => (
126
133
  <div key={item.id}>
127
134
  <List.Item
128
135
  role='button'
129
136
  item={item}
130
- classNames={[subtleHover, 'flex flex-col cursor-pointer rounded-sm']}
137
+ classNames={[subtleHover, 'flex flex-col cursor-pointer rounded-xs']}
131
138
  aria-expanded={selected === item.id}
132
139
  >
133
140
  <div className='flex items-center'>
@@ -139,32 +146,30 @@ export const SelectOptionInput = ({ type, label, readonly, getStatus, getValue,
139
146
  <IconButton
140
147
  iconOnly
141
148
  variant='ghost'
142
- label={t(selected === item.id ? 'collapse label' : 'expand label', { ns: 'os' })}
149
+ label={t(selected === item.id ? 'collapse label' : 'expand label', { ns: osTranslations })}
143
150
  icon={selected === item.id ? 'ph--caret-down--regular' : 'ph--caret-right--regular'}
144
151
  onClick={() => handleClick(item.id)}
145
152
  />
146
153
  </div>
147
154
  {selected === item.id && (
148
- <div className='pli-cardSpacingInline plb-cardSpacingBlock flex flex-col gap-1 density-fine'>
149
- {/* 16px to match drag handle width. */}
150
- <Input.Label classNames='text-xs'>{t('select option label')}</Input.Label>
151
- <div className='grid grid-cols-[1fr_min-content_min-content] gap-1'>
155
+ <div className='flex flex-col p-form-padding gap-form-gap dx-density-fine'>
156
+ <Input.Label classNames='text-xs'>{t('select-option.label')}</Input.Label>
157
+ <div className='grid grid-cols-[1fr_min-content_min-content] gap-form-gap'>
152
158
  <Input.TextInput
153
159
  disabled={!!readonly}
154
- placeholder={t('select option label placeholder')}
160
+ placeholder={t('select-option-label.placeholder')}
155
161
  ref={selected === item.id ? inputRef : undefined}
156
162
  value={item.title}
157
163
  onChange={handleTitleChange(item.id)}
158
164
  onKeyDown={handleKeyDown}
159
165
  classNames='flex-1'
160
- data-no-submit
161
166
  />
162
167
  <HuePicker disabled={!!readonly} value={item.color} onChange={handleColorChange(item.id)} />
163
168
  <IconButton
164
169
  disabled={!!readonly}
165
170
  icon='ph--trash--fill'
166
171
  iconOnly
167
- label={t('select option delete')}
172
+ label={t('select-option-delete.button')}
168
173
  onClick={() => handleDelete(item.id)}
169
174
  />
170
175
  </div>
@@ -176,10 +181,10 @@ export const SelectOptionInput = ({ type, label, readonly, getStatus, getValue,
176
181
  <IconButton
177
182
  variant='ghost'
178
183
  icon='ph--plus--regular'
179
- label={t('select option add')}
184
+ label={t('select-option-add.button')}
180
185
  onClick={handleAdd}
181
186
  disabled={!!readonly}
182
- classNames='is-full'
187
+ classNames='w-full'
183
188
  />
184
189
  </div>
185
190
  )}
@@ -0,0 +1,38 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback } from 'react';
6
+
7
+ import { Input, type TextAreaProps } from '@dxos/react-ui';
8
+
9
+ import { type FormFieldComponentProps, FormFieldWrapper } from '../FormFieldComponent';
10
+
11
+ export const TextAreaField = ({
12
+ type,
13
+ readonly,
14
+ placeholder,
15
+ onValueChange,
16
+ onBlur,
17
+ ...props
18
+ }: FormFieldComponentProps<string>) => {
19
+ const handleChange = useCallback<NonNullable<TextAreaProps['onChange']>>(
20
+ (event) => onValueChange(type, event.target.value),
21
+ [type, onValueChange],
22
+ );
23
+
24
+ return (
25
+ <FormFieldWrapper<string> readonly={readonly} {...props}>
26
+ {({ value = '' }) => (
27
+ <Input.TextArea
28
+ rows={3}
29
+ disabled={!!readonly}
30
+ placeholder={placeholder}
31
+ value={value}
32
+ onChange={handleChange}
33
+ onBlur={onBlur}
34
+ />
35
+ )}
36
+ </FormFieldWrapper>
37
+ );
38
+ };
@@ -0,0 +1,38 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback } from 'react';
6
+
7
+ import { Input, type TextInputProps } from '@dxos/react-ui';
8
+
9
+ import { type FormFieldComponentProps, FormFieldWrapper } from '../FormFieldComponent';
10
+
11
+ export const TextField = ({
12
+ type,
13
+ readonly,
14
+ placeholder,
15
+ onValueChange,
16
+ onBlur,
17
+ ...props
18
+ }: FormFieldComponentProps<string>) => {
19
+ const handleChange = useCallback<NonNullable<TextInputProps['onChange']>>(
20
+ (event) => onValueChange(type, event.target.value),
21
+ [type, onValueChange],
22
+ );
23
+
24
+ return (
25
+ <FormFieldWrapper<string> readonly={readonly} {...props}>
26
+ {({ value = '' }) => (
27
+ <Input.TextInput
28
+ noAutoFill
29
+ disabled={!!readonly}
30
+ placeholder={placeholder}
31
+ value={value}
32
+ onChange={handleChange}
33
+ onBlur={onBlur}
34
+ />
35
+ )}
36
+ </FormFieldWrapper>
37
+ );
38
+ };
@@ -5,39 +5,46 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { Input } from '@dxos/react-ui';
8
- import { mx } from '@dxos/react-ui-theme';
8
+ import { mx } from '@dxos/ui-theme';
9
9
  import { safeParseFloat } from '@dxos/util';
10
10
 
11
- import { InputHeader, type InputProps } from '../Input';
11
+ import { type FormFieldComponentProps, FormFieldLabel } from '../FormFieldComponent';
12
12
 
13
13
  const gridCols = ['grid-cols-1', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4'];
14
14
 
15
- export const TupleInput = ({
15
+ export const TupleField = ({
16
16
  binding,
17
17
  type,
18
- label,
19
18
  readonly,
19
+ label,
20
20
  getStatus,
21
21
  getValue,
22
22
  onValueChange,
23
23
  onBlur,
24
- }: InputProps & { binding: string[] }) => {
24
+ }: FormFieldComponentProps<Record<string, number>> & {
25
+ binding: string[];
26
+ }) => {
25
27
  const { status, error } = getStatus();
26
28
 
27
29
  // TODO(burdon): Generalize number/float/string, etc.
28
- const values: Record<string, number> = getValue<Record<string, number>>() ?? {};
30
+ const values: Record<string, number> = getValue() ?? {};
29
31
 
30
32
  return (
31
33
  <Input.Root validationValence={status}>
32
- <InputHeader error={error} label={label} />
33
- <div className={mx('grid gap-2', gridCols[binding.length - 1])}>
34
+ <FormFieldLabel error={error} readonly={readonly} label={label} />
35
+ <div className={mx('grid gap-form-gap', gridCols[binding.length - 1])}>
34
36
  {binding.map((prop) => (
35
37
  <Input.TextInput
36
38
  key={prop}
37
39
  type='number'
38
40
  disabled={!!readonly}
39
41
  value={values[prop]}
40
- onChange={(event) => onValueChange(type, { ...values, [prop]: safeParseFloat(event.target.value, 0) })}
42
+ onChange={(event) => {
43
+ onValueChange(type, {
44
+ ...values,
45
+ [prop]: safeParseFloat(event.target.value, 0),
46
+ });
47
+ }}
41
48
  onBlur={onBlur}
42
49
  />
43
50
  ))}
@@ -0,0 +1,15 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './ArrayField';
6
+ export * from './BooleanField';
7
+ export * from './GeoPointField';
8
+ export * from './MarkdownField';
9
+ export * from './NumberField';
10
+ export * from './RefField';
11
+ export * from './SelectField';
12
+ export * from './SelectOptionField';
13
+ export * from './TextField';
14
+ export * from './TextAreaField';
15
+ export * from './TupleField';
@@ -2,8 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- export * from './custom';
6
- export * from './Defaults';
5
+ export * from './fields';
6
+
7
7
  export * from './Form';
8
- export * from './FormContext';
9
- export * from './Input';
8
+ export * from './FormFieldComponent';
@@ -8,22 +8,22 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
8
8
  import { expect, fn, userEvent, within } from 'storybook/test';
9
9
 
10
10
  import { Obj } from '@dxos/echo';
11
- import { faker } from '@dxos/random';
11
+ import { random } from '@dxos/random';
12
12
  import { Filter, useQuery } from '@dxos/react-client/echo';
13
- import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
13
+ import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
14
14
  import { Button } from '@dxos/react-ui';
15
15
  import { withTheme } from '@dxos/react-ui/testing';
16
16
  import { Person } from '@dxos/types';
17
+ import { osTranslations } from '@dxos/ui-theme';
17
18
 
18
19
  import { translations } from '../../translations';
19
-
20
20
  import { ObjectPicker } from './ObjectPicker';
21
21
 
22
- faker.seed(1);
22
+ random.seed(1);
23
23
 
24
24
  const createPerson = () =>
25
25
  Obj.make(Person.Person, {
26
- fullName: faker.person.fullName(),
26
+ fullName: random.person.fullName(),
27
27
  });
28
28
 
29
29
  const omitId = Schema.omit<any, any, ['id']>('id');
@@ -34,7 +34,7 @@ const mockHandleSelect = fn();
34
34
  const mockHandleCreate = fn();
35
35
 
36
36
  const DefaultStory = () => {
37
- const { space } = useClientProvider();
37
+ const { space } = useClientStory();
38
38
  const [isOpen, setIsOpen] = useState(false);
39
39
 
40
40
  // Create sample Person objects
@@ -48,7 +48,7 @@ const DefaultStory = () => {
48
48
  }, [space]);
49
49
 
50
50
  // Get all objects in the space (similar to BoardContainer)
51
- const allObjects = useQuery(space, Filter.everything());
51
+ const allObjects = useQuery(space?.db, Filter.everything());
52
52
 
53
53
  // Map objects to options format expected by ObjectPicker
54
54
  const options = useMemo(
@@ -73,22 +73,22 @@ const DefaultStory = () => {
73
73
  );
74
74
 
75
75
  return (
76
- <div className='p-4'>
76
+ <div role='none' className='w-96'>
77
77
  <ObjectPicker.Root open={isOpen} onOpenChange={setIsOpen}>
78
78
  <ObjectPicker.Trigger asChild>
79
- <Button variant='primary' data-testid='trigger'>
79
+ <Button variant='primary' data-testid='trigger' classNames='w-full'>
80
80
  Select Person
81
81
  </Button>
82
82
  </ObjectPicker.Trigger>
83
83
  <ObjectPicker.Content
84
+ classNames='dx-card-popover-width'
84
85
  options={options}
85
- onSelect={mockHandleSelect}
86
86
  createSchema={personSchema}
87
- createOptionLabel={['create new person label', { ns: 'os' }]}
87
+ createOptionLabel={['create-new-person.label', { ns: osTranslations }]}
88
88
  createOptionIcon='ph--user-plus--regular'
89
89
  createInitialValuePath='fullName'
90
90
  onCreate={handleCreateCallback}
91
- classNames='popover-card-width'
91
+ onSelect={mockHandleSelect}
92
92
  />
93
93
  </ObjectPicker.Root>
94
94
  </div>
@@ -99,7 +99,7 @@ const meta = {
99
99
  title: 'ui/react-ui-form/ObjectPicker',
100
100
  render: DefaultStory,
101
101
  decorators: [
102
- withTheme,
102
+ withTheme(),
103
103
  withClientProvider({
104
104
  types: [Person.Person],
105
105
  createIdentity: true,
@@ -200,8 +200,10 @@ export const WithTest: Story = {
200
200
  await userEvent.click(saveButton);
201
201
 
202
202
  // Check that clicking the save button calls onCreate with expected values.
203
- await expect(mockHandleCreate).toHaveBeenCalledWith({
204
- fullName: unrelatedTerm,
205
- });
203
+ await expect(mockHandleCreate).toHaveBeenCalledWith(
204
+ expect.objectContaining({
205
+ fullName: unrelatedTerm,
206
+ }),
207
+ );
206
208
  },
207
209
  };