@dxos/react-ui-form 0.8.2-main.f081794 → 0.8.2-main.fbd8ed0

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 (85) hide show
  1. package/dist/lib/browser/index.mjs +827 -537
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +675 -395
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/lib/node-esm/index.mjs +827 -537
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +1 -1
  11. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  12. package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
  13. package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
  14. package/dist/types/src/components/Form/Form.d.ts +6 -4
  15. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  16. package/dist/types/src/components/Form/Form.stories.d.ts +37 -34
  17. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  18. package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
  19. package/dist/types/src/components/Form/FormContent.d.ts +14 -11
  20. package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
  21. package/dist/types/src/components/Form/FormContext.d.ts +2 -2
  22. package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
  23. package/dist/types/src/components/Form/Input.d.ts.map +1 -1
  24. package/dist/types/src/components/Form/RefField.d.ts +12 -0
  25. package/dist/types/src/components/Form/RefField.d.ts.map +1 -0
  26. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
  27. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -1
  28. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
  29. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
  30. package/dist/types/src/components/Form/factory.d.ts +1 -1
  31. package/dist/types/src/components/Form/factory.d.ts.map +1 -1
  32. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  33. package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +1 -1
  34. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +2 -2
  35. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  36. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  37. package/dist/types/src/components/deprecated/Form.d.ts +2 -2
  38. package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
  39. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
  40. package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -1
  41. package/dist/types/src/components/testing.d.ts.map +1 -1
  42. package/dist/types/src/hooks/index.d.ts +2 -0
  43. package/dist/types/src/hooks/index.d.ts.map +1 -1
  44. package/dist/types/src/hooks/useForm.d.ts +2 -2
  45. package/dist/types/src/hooks/useForm.d.ts.map +1 -1
  46. package/dist/types/src/hooks/useQueryRefOptions.d.ts +23 -0
  47. package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +1 -0
  48. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +8 -0
  49. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +1 -0
  50. package/dist/types/src/util/field.d.ts.map +1 -1
  51. package/dist/types/src/util/formatting.d.ts.map +1 -1
  52. package/dist/types/src/util/index.d.ts +2 -0
  53. package/dist/types/src/util/index.d.ts.map +1 -1
  54. package/dist/types/src/util/refs.d.ts +9 -0
  55. package/dist/types/src/util/refs.d.ts.map +1 -0
  56. package/dist/types/src/util/schema.d.ts +3 -0
  57. package/dist/types/src/util/schema.d.ts.map +1 -0
  58. package/dist/types/tsconfig.tsbuildinfo +1 -1
  59. package/package.json +26 -25
  60. package/src/components/ControlSection/ControlSection.stories.tsx +1 -1
  61. package/src/components/FieldEditor/FieldEditor.stories.tsx +1 -1
  62. package/src/components/Form/ArrayField.tsx +13 -17
  63. package/src/components/Form/Defaults.tsx +4 -1
  64. package/src/components/Form/Form.stories.tsx +92 -43
  65. package/src/components/Form/Form.tsx +7 -3
  66. package/src/components/Form/FormContent.tsx +90 -19
  67. package/src/components/Form/FormContext.tsx +13 -3
  68. package/src/components/Form/Input.tsx +3 -9
  69. package/src/components/Form/RefField.tsx +197 -0
  70. package/src/components/Form/custom/GeoPointInput.tsx +2 -2
  71. package/src/components/Form/custom/SelectOptionsInput.stories.tsx +6 -5
  72. package/src/components/Form/custom/SelectOptionsInput.tsx +2 -2
  73. package/src/components/Form/factory.tsx +1 -1
  74. package/src/components/Form/index.ts +0 -1
  75. package/src/components/ViewEditor/ViewEditor.stories.tsx +5 -4
  76. package/src/components/ViewEditor/ViewEditor.tsx +23 -20
  77. package/src/components/deprecated/Form.tsx +6 -6
  78. package/src/hooks/index.ts +3 -1
  79. package/src/hooks/useForm.ts +4 -3
  80. package/src/hooks/useQueryRefOptions.ts +53 -0
  81. package/src/hooks/useRefQueryLookupHandler.ts +41 -0
  82. package/src/util/formatting.ts +15 -3
  83. package/src/util/index.ts +3 -1
  84. package/src/util/refs.ts +38 -0
  85. package/src/util/schema.ts +14 -0
@@ -1,12 +1,11 @@
1
1
  //
2
- // Copyright 2024 DXOS.org
2
+ // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { pipe } from 'effect';
5
+ import { Schema, SchemaAST, pipe } from 'effect';
6
6
  import { capitalize } from 'effect/String';
7
7
  import React, { useMemo } from 'react';
8
8
 
9
- import { AST, S } from '@dxos/echo-schema';
10
9
  import { createJsonPath, findNode, getDiscriminatedType, isDiscriminatedUnion } from '@dxos/effect';
11
10
  import { mx } from '@dxos/react-ui-theme';
12
11
  import { getSchemaProperties, type SchemaProperty } from '@dxos/schema';
@@ -17,17 +16,10 @@ import { SelectInput } from './Defaults';
17
16
  import { type ComponentLookup } from './Form';
18
17
  import { useInputProps, useFormValues } from './FormContext';
19
18
  import { type InputComponent } from './Input';
19
+ import { RefField } from './RefField';
20
20
  import { getInputComponent } from './factory';
21
-
22
- export type FormContentProps = {
23
- schema: S.Schema.All;
24
- path?: (string | number)[];
25
- filter?: (props: SchemaProperty<any>[]) => SchemaProperty<any>[];
26
- sort?: string[];
27
- readonly?: boolean;
28
- lookupComponent?: ComponentLookup;
29
- Custom?: Partial<Record<string, InputComponent>>;
30
- };
21
+ import { type QueryRefOptions } from '../../hooks';
22
+ import { getRefProps } from '../../util';
31
23
 
32
24
  export type FormFieldProps = {
33
25
  property: SchemaProperty<any>;
@@ -35,11 +27,20 @@ export type FormFieldProps = {
35
27
  readonly?: boolean;
36
28
  /** Used to indicate if input should be presented inline (e.g. for array items). */
37
29
  inline?: boolean;
30
+ onQueryRefOptions?: QueryRefOptions;
38
31
  lookupComponent?: ComponentLookup;
39
32
  Custom?: Partial<Record<string, InputComponent>>;
40
33
  };
41
34
 
42
- export const FormField = ({ property, path, readonly, inline, lookupComponent, Custom }: FormFieldProps) => {
35
+ export const FormField = ({
36
+ property,
37
+ path,
38
+ readonly,
39
+ inline,
40
+ onQueryRefOptions,
41
+ lookupComponent,
42
+ Custom,
43
+ }: FormFieldProps) => {
43
44
  const inputProps = useInputProps(path);
44
45
  const { ast, name, type, format, title, description, options, examples, array } = property;
45
46
 
@@ -49,9 +50,13 @@ export const FormField = ({ property, path, readonly, inline, lookupComponent, C
49
50
  [examples, description],
50
51
  );
51
52
 
53
+ //
54
+ // Registry and Custom.
55
+ //
56
+
52
57
  const FoundComponent = lookupComponent?.({
53
58
  prop: name,
54
- schema: S.make(ast),
59
+ schema: Schema.make(ast),
55
60
  inputProps: {
56
61
  type,
57
62
  format,
@@ -82,10 +87,40 @@ export const FormField = ({ property, path, readonly, inline, lookupComponent, C
82
87
  );
83
88
  }
84
89
 
90
+ //
91
+ // Refs.
92
+ //
93
+
94
+ const refProps = getRefProps(property);
95
+ if (refProps) {
96
+ return (
97
+ <RefField
98
+ ast={refProps.ast}
99
+ array={refProps.isArray}
100
+ type={type}
101
+ format={format}
102
+ label={label}
103
+ placeholder={placeholder}
104
+ disabled={readonly}
105
+ inputOnly={inline}
106
+ onQueryRefOptions={onQueryRefOptions}
107
+ {...inputProps}
108
+ />
109
+ );
110
+ }
111
+
112
+ //
113
+ // Arrays.
114
+ //
115
+
85
116
  if (array) {
86
117
  return <ArrayField property={property} path={path} inputProps={inputProps} readonly={readonly} Custom={Custom} />;
87
118
  }
88
119
 
120
+ //
121
+ // Standard Inputs.
122
+ //
123
+
89
124
  const InputComponent = getInputComponent(type, format);
90
125
  if (InputComponent) {
91
126
  return (
@@ -103,6 +138,10 @@ export const FormField = ({ property, path, readonly, inline, lookupComponent, C
103
138
  );
104
139
  }
105
140
 
141
+ //
142
+ // Select.
143
+ //
144
+
106
145
  if (options) {
107
146
  return (
108
147
  <div role='none'>
@@ -120,17 +159,28 @@ export const FormField = ({ property, path, readonly, inline, lookupComponent, C
120
159
  );
121
160
  }
122
161
 
162
+ //
163
+ // Nested Objects.
164
+ //
165
+
123
166
  if (type === 'object') {
124
167
  const baseNode = findNode(ast, isDiscriminatedUnion);
125
168
  const typeLiteral = baseNode
126
169
  ? getDiscriminatedType(baseNode, inputProps.getValue() as any)
127
- : findNode(ast, AST.isTypeLiteral);
170
+ : findNode(ast, SchemaAST.isTypeLiteral);
128
171
 
129
172
  if (typeLiteral) {
130
173
  return (
131
174
  <div role='none'>
132
- {!inline && <div>{label}</div>}
133
- <FormFields schema={S.make(typeLiteral)} path={path} readonly={readonly} Custom={Custom} />
175
+ {!inline && <h3 className='text-lg mbs-2 mbe-1'>{label}</h3>}
176
+ <FormFields
177
+ schema={Schema.make(typeLiteral)}
178
+ path={path}
179
+ readonly={readonly}
180
+ onQueryRefOptions={onQueryRefOptions}
181
+ Custom={Custom}
182
+ lookupComponent={lookupComponent}
183
+ />
134
184
  </div>
135
185
  );
136
186
  }
@@ -139,7 +189,27 @@ export const FormField = ({ property, path, readonly, inline, lookupComponent, C
139
189
  return null;
140
190
  };
141
191
 
142
- export const FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custom }: FormContentProps) => {
192
+ export type FormContentProps = {
193
+ schema: Schema.Schema.All;
194
+ path?: (string | number)[];
195
+ filter?: (props: SchemaProperty<any>[]) => SchemaProperty<any>[];
196
+ sort?: string[];
197
+ readonly?: boolean;
198
+ onQueryRefOptions?: QueryRefOptions;
199
+ lookupComponent?: ComponentLookup;
200
+ Custom?: Partial<Record<string, InputComponent>>;
201
+ };
202
+
203
+ export const FormFields = ({
204
+ schema,
205
+ path,
206
+ filter,
207
+ sort,
208
+ readonly,
209
+ onQueryRefOptions,
210
+ lookupComponent,
211
+ Custom,
212
+ }: FormContentProps) => {
143
213
  const values = useFormValues(path);
144
214
 
145
215
  const properties = useMemo(() => {
@@ -159,6 +229,7 @@ export const FormFields = ({ schema, path, filter, sort, readonly, lookupCompone
159
229
  property={property}
160
230
  path={[...(path ?? []), property.name]}
161
231
  readonly={readonly}
232
+ onQueryRefOptions={onQueryRefOptions}
162
233
  lookupComponent={lookupComponent}
163
234
  Custom={Custom}
164
235
  />
@@ -2,7 +2,15 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import React, { createContext, useContext, useEffect, useMemo, type FocusEvent, type PropsWithChildren } from 'react';
5
+ import React, {
6
+ createContext,
7
+ type RefObject,
8
+ useContext,
9
+ useEffect,
10
+ useMemo,
11
+ type FocusEvent,
12
+ type PropsWithChildren,
13
+ } from 'react';
6
14
 
7
15
  import { raise } from '@dxos/debug';
8
16
  import { type BaseObject, getValue } from '@dxos/echo-schema';
@@ -53,7 +61,7 @@ export const FormProvider = ({
53
61
  ...formOptions
54
62
  }: PropsWithChildren<
55
63
  FormOptions<any> & {
56
- formRef?: React.RefObject<HTMLDivElement>;
64
+ formRef?: RefObject<HTMLDivElement>;
57
65
  autoSave?: boolean;
58
66
  }
59
67
  >) => {
@@ -68,7 +76,9 @@ export const FormProvider = ({
68
76
  const keyIsEnter = event.key === 'Enter';
69
77
  const modifierUsed = event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;
70
78
  const inputIsTextarea = (event.target as HTMLElement).tagName.toLowerCase() === 'textarea';
71
- const inputOptOut = (event.target as HTMLElement).hasAttribute('data-no-submit');
79
+ const inputOptOut =
80
+ (event.target as HTMLElement).hasAttribute('data-no-submit') ||
81
+ (event.target as HTMLElement).closest('[data-no-submit]') !== null;
72
82
 
73
83
  // Regular inputs: Submit on Enter (no modifiers).
74
84
  const shouldSubmitRegularInput = !inputIsTextarea && keyIsEnter && !modifierUsed;
@@ -37,15 +37,9 @@ export const InputHeader = ({ children, error }: InputHeaderProps) => {
37
37
  <div role='none' className='flex justify-between items-center mb-1'>
38
38
  {children}
39
39
  {error && (
40
- <Tooltip.Root>
41
- <Tooltip.Trigger asChild>
42
- <Icon icon='ph--warning--regular' size={4} classNames={errorText} />
43
- </Tooltip.Trigger>
44
- <Tooltip.Content side='bottom'>
45
- <Tooltip.Arrow />
46
- {error}
47
- </Tooltip.Content>
48
- </Tooltip.Root>
40
+ <Tooltip.Trigger asChild content={error} side='bottom'>
41
+ <Icon icon='ph--warning--regular' size={4} classNames={errorText} />
42
+ </Tooltip.Trigger>
49
43
  )}
50
44
  </div>
51
45
  );
@@ -0,0 +1,197 @@
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 { Input } from '@dxos/react-ui';
18
+ import { TagPicker, type TagPickerMode, type TagPickerItemData } from '@dxos/react-ui-tag-picker';
19
+ import { isNonNullable } from '@dxos/util';
20
+
21
+ import { TextInput } from './Defaults';
22
+ import { InputHeader, type InputProps } from './Input';
23
+ import { type QueryRefOptions, useQueryRefOptions } from '../../hooks';
24
+
25
+ type RefFieldProps = InputProps & {
26
+ ast?: SchemaAST.AST;
27
+ array?: boolean;
28
+ onQueryRefOptions?: QueryRefOptions;
29
+ };
30
+
31
+ export const RefField = ({
32
+ type,
33
+ label,
34
+ disabled,
35
+ placeholder,
36
+ inputOnly,
37
+ array,
38
+ ast,
39
+ getValue,
40
+ onBlur,
41
+ onQueryRefOptions,
42
+ onValueChange,
43
+ ...restInputProps
44
+ }: RefFieldProps) => {
45
+ const refTypeInfo = useMemo(
46
+ () => (ast ? findAnnotation<ReferenceAnnotationValue>(ast, ReferenceAnnotationId) : undefined),
47
+ [ast],
48
+ );
49
+ const { options: availableOptions, loading: _loading } = useQueryRefOptions({ refTypeInfo, onQueryRefOptions });
50
+
51
+ if ((refTypeInfo && refTypeInfo?.typename === getTypeAnnotation(Expando)?.typename) || !onQueryRefOptions) {
52
+ // If ref type is expando, fall back to taking a DXN in string format.
53
+ return (
54
+ <RefFieldFallback
55
+ {...{ type, label, placeholder, disabled, inputOnly, getValue, onBlur, onValueChange, ...restInputProps }}
56
+ />
57
+ );
58
+ }
59
+
60
+ const handleGetValue = useCallback((): TagPickerItemData[] => {
61
+ const formValue = getValue();
62
+
63
+ const unknownToRefOption = (val: unknown) => {
64
+ if (Ref.isRef(val)) {
65
+ const dxnString = val.dxn.toString();
66
+ const matchingOption = availableOptions.find((option) => option.id === dxnString);
67
+ if (matchingOption) {
68
+ return matchingOption;
69
+ }
70
+ }
71
+ return undefined;
72
+ };
73
+
74
+ if (array && Array.isArray(formValue)) {
75
+ return formValue.map(unknownToRefOption).filter(isNonNullable) ?? [];
76
+ }
77
+
78
+ const option = unknownToRefOption(formValue);
79
+ if (option) {
80
+ return [option];
81
+ }
82
+
83
+ return [];
84
+ }, [getValue, availableOptions, array]);
85
+
86
+ const handleSearch = useCallback(
87
+ (text: string, ids: string[]): TagPickerItemData[] =>
88
+ availableOptions
89
+ .filter((option) => !ids.includes(option.id))
90
+ .filter((option) => option.label.toLowerCase().includes(text.toLowerCase()))
91
+ .map((option) => ({ id: option.id, label: option.label, hue: option.hue as any })),
92
+ [availableOptions],
93
+ );
94
+
95
+ const handleUpdate = useCallback(
96
+ (ids: string[]) => {
97
+ if (ids.length === 0) {
98
+ onValueChange('object', undefined);
99
+ return;
100
+ }
101
+
102
+ const refs = ids
103
+ .map((id) => {
104
+ const item = availableOptions.find((option) => option.id === id);
105
+ if (item) {
106
+ const dxn = DXN.parse(item.id);
107
+ return Ref.fromDXN(dxn);
108
+ }
109
+ return null;
110
+ })
111
+ .filter(isNonNullable);
112
+
113
+ if (array) {
114
+ onValueChange('object', refs);
115
+ } else {
116
+ onValueChange('object', refs[0]);
117
+ }
118
+ },
119
+ [availableOptions, array, onValueChange],
120
+ );
121
+
122
+ const tagPickerMode: TagPickerMode = useMemo(() => (array ? 'multi-select' : 'single-select'), [array]);
123
+
124
+ if (!refTypeInfo) {
125
+ return null;
126
+ }
127
+
128
+ const { status, error } = restInputProps.getStatus();
129
+
130
+ return (
131
+ <Input.Root validationValence={status}>
132
+ {!inputOnly && (
133
+ <InputHeader error={error}>
134
+ <Input.Label>{label}</Input.Label>
135
+ </InputHeader>
136
+ )}
137
+ <div data-no-submit>
138
+ <TagPicker
139
+ items={handleGetValue()}
140
+ mode={tagPickerMode}
141
+ onBlur={(event) => onBlur(event as unknown as FocusEvent<HTMLElement>)}
142
+ onUpdate={handleUpdate}
143
+ onSearch={handleSearch}
144
+ classNames='rounded-sm bg-input p-1.5'
145
+ />
146
+ </div>
147
+ {inputOnly && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
148
+ </Input.Root>
149
+ );
150
+ };
151
+
152
+ const RefFieldFallback = ({
153
+ type,
154
+ label,
155
+ disabled,
156
+ placeholder,
157
+ inputOnly,
158
+ getValue,
159
+ onValueChange,
160
+ ...restInputProps
161
+ }: InputProps) => {
162
+ const handleOnValueChange = (_type: any, dxnString: string) => {
163
+ const dxn = DXN.tryParse(dxnString);
164
+ if (dxn) {
165
+ onValueChange?.('object', Ref.fromDXN(dxn));
166
+ } else if (dxnString === '') {
167
+ onValueChange?.('object', undefined);
168
+ } else {
169
+ onValueChange?.('string', dxnString);
170
+ }
171
+ };
172
+
173
+ const handleGetValue = () => {
174
+ const formValue = getValue();
175
+ if (typeof formValue === 'string') {
176
+ return formValue;
177
+ }
178
+ if (Ref.isRef(formValue)) {
179
+ return formValue.dxn.toString();
180
+ }
181
+
182
+ return undefined;
183
+ };
184
+
185
+ return (
186
+ <TextInput
187
+ type={type}
188
+ label={label}
189
+ disabled={disabled}
190
+ placeholder={placeholder}
191
+ inputOnly={inputOnly}
192
+ getValue={handleGetValue as <V>() => V | undefined}
193
+ onValueChange={handleOnValueChange}
194
+ {...restInputProps}
195
+ />
196
+ );
197
+ };
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { useCallback, useState, useEffect, useMemo } from 'react';
5
+ import React, { useCallback, useState, useEffect, useMemo, type ChangeEvent } from 'react';
6
6
 
7
7
  import { type GeoPoint, GeoLocation } from '@dxos/echo-schema';
8
8
  import { Input, useTranslation } from '@dxos/react-ui';
@@ -28,7 +28,7 @@ export const GeoPointInput = ({ type, label, disabled, getStatus, getValue, onVa
28
28
 
29
29
  const handleCoordinateChange = useCallback(
30
30
  (coordinateType: keyof Pick<GeoLocation, 'longitude' | 'latitude'>, setText: (text: string) => void) =>
31
- (event: React.ChangeEvent<HTMLInputElement>) => {
31
+ (event: ChangeEvent<HTMLInputElement>) => {
32
32
  const inputText = event.target.value;
33
33
  setText(inputText);
34
34
  if (inputText !== '' && inputText !== '-') {
@@ -5,9 +5,10 @@
5
5
  import '@dxos-theme';
6
6
 
7
7
  import { type Meta, type StoryObj } from '@storybook/react';
8
+ import { Schema } from 'effect';
8
9
  import React, { useState } from 'react';
9
10
 
10
- import { S, SelectOptionSchema, type SelectOption } from '@dxos/echo-schema';
11
+ import { SelectOptionSchema, type SelectOption } from '@dxos/echo-schema';
11
12
  import { withLayout, withTheme } from '@dxos/storybook-utils';
12
13
 
13
14
  import { SelectOptionInput } from './SelectOptionsInput';
@@ -15,9 +16,9 @@ import translations from '../../../translations';
15
16
  import { TestLayout, TestPanel } from '../../testing';
16
17
  import { Form } from '../Form';
17
18
 
18
- const schema = S.Struct({
19
- options: S.Array(SelectOptionSchema).pipe(S.mutable),
20
- }).pipe(S.mutable);
19
+ const schema = Schema.Struct({
20
+ options: Schema.Array(SelectOptionSchema).pipe(Schema.mutable),
21
+ }).pipe(Schema.mutable);
21
22
 
22
23
  const DefaultStory = () => {
23
24
  const [values, setValues] = useState<{ options: SelectOption[] }>({
@@ -46,7 +47,7 @@ const DefaultStory = () => {
46
47
  const meta: Meta = {
47
48
  title: 'ui/react-ui-form/SelectOptionsInput',
48
49
  component: SelectOptionInput,
49
- decorators: [withTheme, withLayout({ tooltips: true })],
50
+ decorators: [withTheme, withLayout()],
50
51
  parameters: { translations },
51
52
  };
52
53
 
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import React, { useCallback, useEffect, useState, type ChangeEvent, useRef } from 'react';
5
+ import React, { type ChangeEvent, type KeyboardEvent, useCallback, useEffect, useState, useRef } from 'react';
6
6
 
7
7
  import { type SelectOption } from '@dxos/echo-schema';
8
8
  import { PublicKey } from '@dxos/keys';
@@ -89,7 +89,7 @@ export const SelectOptionInput = ({ type, label, disabled, getStatus, getValue,
89
89
  [options, type, onValueChange],
90
90
  );
91
91
 
92
- const handleKeyDown = useCallback((e: React.KeyboardEvent<HTMLInputElement>) => {
92
+ const handleKeyDown = useCallback((e: KeyboardEvent<HTMLInputElement>) => {
93
93
  if (e.key === 'Enter') {
94
94
  setSelectedId(null);
95
95
  }
@@ -12,7 +12,7 @@ import { GeoPointInput } from './custom';
12
12
  /**
13
13
  * Get property input component.
14
14
  */
15
- export const getInputComponent = (type: SimpleType, format?: FormatEnum): InputComponent | undefined => {
15
+ export const getInputComponent = (type: SimpleType, format?: FormatEnum | string): InputComponent | undefined => {
16
16
  switch (format) {
17
17
  case FormatEnum.GeoPoint:
18
18
  return GeoPointInput;
@@ -3,7 +3,6 @@
3
3
  //
4
4
 
5
5
  export * from './custom';
6
-
7
6
  export * from './Defaults';
8
7
  export * from './Form';
9
8
  export * from './FormContext';
@@ -5,9 +5,10 @@
5
5
  import '@dxos-theme';
6
6
 
7
7
  import { type Meta, type StoryObj } from '@storybook/react';
8
+ import { Schema } from 'effect';
8
9
  import React, { useCallback, useMemo, useState } from 'react';
9
10
 
10
- import { Format, type EchoSchema, S, toJsonSchema, TypedObject } from '@dxos/echo-schema';
11
+ import { Format, type EchoSchema, toJsonSchema, TypedObject } from '@dxos/echo-schema';
11
12
  import { Filter, useQuery, useSpace } from '@dxos/react-client/echo';
12
13
  import { withClientProvider } from '@dxos/react-client/testing';
13
14
  import { useAsyncEffect } from '@dxos/react-ui';
@@ -26,7 +27,7 @@ const DefaultStory = () => {
26
27
  useAsyncEffect(async () => {
27
28
  if (space) {
28
29
  class TestSchema extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
29
- name: S.String,
30
+ name: Schema.String,
30
31
  email: Format.Email,
31
32
  salary: Format.Currency(),
32
33
  }) {}
@@ -41,7 +42,7 @@ const DefaultStory = () => {
41
42
  }
42
43
  }, [space]);
43
44
 
44
- const views = useQuery(space, Filter.schema(ViewType));
45
+ const views = useQuery(space, Filter.type(ViewType));
45
46
  const currentTypename = useMemo(() => view?.query?.typename, [view]);
46
47
  const updateViewTypename = useCallback(
47
48
  (newTypename: string) => {
@@ -81,7 +82,7 @@ const meta: Meta<typeof ViewEditor> = {
81
82
  title: 'ui/react-ui-form/ViewEditor',
82
83
  component: ViewEditor,
83
84
  render: DefaultStory,
84
- decorators: [withClientProvider({ createSpace: true }), withLayout({ fullscreen: true, tooltips: true }), withTheme],
85
+ decorators: [withClientProvider({ createSpace: true }), withLayout({ fullscreen: true }), withTheme],
85
86
  parameters: {
86
87
  translations,
87
88
  },