@dxos/react-ui-form 0.8.2-main.fbd8ed0 → 0.8.2-staging.7ac8446

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 (97) hide show
  1. package/dist/lib/browser/index.mjs +537 -846
  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 +506 -807
  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 +537 -846
  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 → ControlSection.d.ts} +5 -8
  11. package/dist/types/src/components/ControlSection.d.ts.map +1 -0
  12. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  13. package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
  14. package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
  15. package/dist/types/src/components/Form/Form.d.ts +4 -6
  16. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  17. package/dist/types/src/components/Form/Form.stories.d.ts +34 -37
  18. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  19. package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
  20. package/dist/types/src/components/Form/FormContent.d.ts +11 -14
  21. package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
  22. package/dist/types/src/components/Form/FormContext.d.ts +2 -2
  23. package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
  24. package/dist/types/src/components/Form/Input.d.ts.map +1 -1
  25. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
  26. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -1
  27. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
  28. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
  29. package/dist/types/src/components/Form/factory.d.ts +1 -1
  30. package/dist/types/src/components/Form/factory.d.ts.map +1 -1
  31. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  32. package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +1 -1
  33. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +2 -2
  34. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  35. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  36. package/dist/types/src/components/deprecated/Form.d.ts +2 -2
  37. package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
  38. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
  39. package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -1
  40. package/dist/types/src/components/index.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 +0 -2
  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/util/field.d.ts.map +1 -1
  47. package/dist/types/src/util/formatting.d.ts.map +1 -1
  48. package/dist/types/src/util/index.d.ts +0 -2
  49. package/dist/types/src/util/index.d.ts.map +1 -1
  50. package/dist/types/tsconfig.tsbuildinfo +1 -1
  51. package/package.json +25 -26
  52. package/src/components/{ControlSection/ControlSection.tsx → ControlSection.tsx} +14 -38
  53. package/src/components/FieldEditor/FieldEditor.stories.tsx +1 -1
  54. package/src/components/Form/ArrayField.tsx +17 -13
  55. package/src/components/Form/Defaults.tsx +1 -4
  56. package/src/components/Form/Form.stories.tsx +43 -92
  57. package/src/components/Form/Form.tsx +3 -7
  58. package/src/components/Form/FormContent.tsx +19 -90
  59. package/src/components/Form/FormContext.tsx +3 -13
  60. package/src/components/Form/Input.tsx +9 -3
  61. package/src/components/Form/custom/GeoPointInput.tsx +2 -2
  62. package/src/components/Form/custom/SelectOptionsInput.stories.tsx +5 -6
  63. package/src/components/Form/custom/SelectOptionsInput.tsx +2 -2
  64. package/src/components/Form/factory.tsx +1 -1
  65. package/src/components/Form/index.ts +1 -0
  66. package/src/components/ViewEditor/ViewEditor.stories.tsx +4 -5
  67. package/src/components/ViewEditor/ViewEditor.tsx +20 -23
  68. package/src/components/deprecated/Form.stories.tsx +6 -6
  69. package/src/components/deprecated/Form.tsx +6 -6
  70. package/src/components/deprecated/FormInput.tsx +1 -1
  71. package/src/components/index.ts +0 -1
  72. package/src/hooks/index.ts +1 -3
  73. package/src/hooks/useForm.ts +3 -4
  74. package/src/util/formatting.ts +3 -15
  75. package/src/util/index.ts +1 -3
  76. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +0 -1
  77. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +0 -8
  78. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +0 -1
  79. package/dist/types/src/components/ControlSection/index.d.ts +0 -2
  80. package/dist/types/src/components/ControlSection/index.d.ts.map +0 -1
  81. package/dist/types/src/components/Form/RefField.d.ts +0 -12
  82. package/dist/types/src/components/Form/RefField.d.ts.map +0 -1
  83. package/dist/types/src/hooks/useQueryRefOptions.d.ts +0 -23
  84. package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +0 -1
  85. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +0 -8
  86. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +0 -1
  87. package/dist/types/src/util/refs.d.ts +0 -9
  88. package/dist/types/src/util/refs.d.ts.map +0 -1
  89. package/dist/types/src/util/schema.d.ts +0 -3
  90. package/dist/types/src/util/schema.d.ts.map +0 -1
  91. package/src/components/ControlSection/ControlSection.stories.tsx +0 -29
  92. package/src/components/ControlSection/index.ts +0 -5
  93. package/src/components/Form/RefField.tsx +0 -197
  94. package/src/hooks/useQueryRefOptions.ts +0 -53
  95. package/src/hooks/useRefQueryLookupHandler.ts +0 -41
  96. package/src/util/refs.ts +0 -38
  97. package/src/util/schema.ts +0 -14
@@ -1,11 +1,12 @@
1
1
  //
2
- // Copyright 2025 DXOS.org
2
+ // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Schema, SchemaAST, pipe } from 'effect';
5
+ import { 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';
9
10
  import { createJsonPath, findNode, getDiscriminatedType, isDiscriminatedUnion } from '@dxos/effect';
10
11
  import { mx } from '@dxos/react-ui-theme';
11
12
  import { getSchemaProperties, type SchemaProperty } from '@dxos/schema';
@@ -16,10 +17,17 @@ import { SelectInput } from './Defaults';
16
17
  import { type ComponentLookup } from './Form';
17
18
  import { useInputProps, useFormValues } from './FormContext';
18
19
  import { type InputComponent } from './Input';
19
- import { RefField } from './RefField';
20
20
  import { getInputComponent } from './factory';
21
- import { type QueryRefOptions } from '../../hooks';
22
- import { getRefProps } from '../../util';
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
+ };
23
31
 
24
32
  export type FormFieldProps = {
25
33
  property: SchemaProperty<any>;
@@ -27,20 +35,11 @@ export type FormFieldProps = {
27
35
  readonly?: boolean;
28
36
  /** Used to indicate if input should be presented inline (e.g. for array items). */
29
37
  inline?: boolean;
30
- onQueryRefOptions?: QueryRefOptions;
31
38
  lookupComponent?: ComponentLookup;
32
39
  Custom?: Partial<Record<string, InputComponent>>;
33
40
  };
34
41
 
35
- export const FormField = ({
36
- property,
37
- path,
38
- readonly,
39
- inline,
40
- onQueryRefOptions,
41
- lookupComponent,
42
- Custom,
43
- }: FormFieldProps) => {
42
+ export const FormField = ({ property, path, readonly, inline, lookupComponent, Custom }: FormFieldProps) => {
44
43
  const inputProps = useInputProps(path);
45
44
  const { ast, name, type, format, title, description, options, examples, array } = property;
46
45
 
@@ -50,13 +49,9 @@ export const FormField = ({
50
49
  [examples, description],
51
50
  );
52
51
 
53
- //
54
- // Registry and Custom.
55
- //
56
-
57
52
  const FoundComponent = lookupComponent?.({
58
53
  prop: name,
59
- schema: Schema.make(ast),
54
+ schema: S.make(ast),
60
55
  inputProps: {
61
56
  type,
62
57
  format,
@@ -87,40 +82,10 @@ export const FormField = ({
87
82
  );
88
83
  }
89
84
 
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
-
116
85
  if (array) {
117
86
  return <ArrayField property={property} path={path} inputProps={inputProps} readonly={readonly} Custom={Custom} />;
118
87
  }
119
88
 
120
- //
121
- // Standard Inputs.
122
- //
123
-
124
89
  const InputComponent = getInputComponent(type, format);
125
90
  if (InputComponent) {
126
91
  return (
@@ -138,10 +103,6 @@ export const FormField = ({
138
103
  );
139
104
  }
140
105
 
141
- //
142
- // Select.
143
- //
144
-
145
106
  if (options) {
146
107
  return (
147
108
  <div role='none'>
@@ -159,28 +120,17 @@ export const FormField = ({
159
120
  );
160
121
  }
161
122
 
162
- //
163
- // Nested Objects.
164
- //
165
-
166
123
  if (type === 'object') {
167
124
  const baseNode = findNode(ast, isDiscriminatedUnion);
168
125
  const typeLiteral = baseNode
169
126
  ? getDiscriminatedType(baseNode, inputProps.getValue() as any)
170
- : findNode(ast, SchemaAST.isTypeLiteral);
127
+ : findNode(ast, AST.isTypeLiteral);
171
128
 
172
129
  if (typeLiteral) {
173
130
  return (
174
131
  <div role='none'>
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
- />
132
+ {!inline && <div>{label}</div>}
133
+ <FormFields schema={S.make(typeLiteral)} path={path} readonly={readonly} Custom={Custom} />
184
134
  </div>
185
135
  );
186
136
  }
@@ -189,27 +139,7 @@ export const FormField = ({
189
139
  return null;
190
140
  };
191
141
 
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) => {
142
+ export const FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custom }: FormContentProps) => {
213
143
  const values = useFormValues(path);
214
144
 
215
145
  const properties = useMemo(() => {
@@ -229,7 +159,6 @@ export const FormFields = ({
229
159
  property={property}
230
160
  path={[...(path ?? []), property.name]}
231
161
  readonly={readonly}
232
- onQueryRefOptions={onQueryRefOptions}
233
162
  lookupComponent={lookupComponent}
234
163
  Custom={Custom}
235
164
  />
@@ -2,15 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import React, {
6
- createContext,
7
- type RefObject,
8
- useContext,
9
- useEffect,
10
- useMemo,
11
- type FocusEvent,
12
- type PropsWithChildren,
13
- } from 'react';
5
+ import React, { createContext, useContext, useEffect, useMemo, type FocusEvent, type PropsWithChildren } from 'react';
14
6
 
15
7
  import { raise } from '@dxos/debug';
16
8
  import { type BaseObject, getValue } from '@dxos/echo-schema';
@@ -61,7 +53,7 @@ export const FormProvider = ({
61
53
  ...formOptions
62
54
  }: PropsWithChildren<
63
55
  FormOptions<any> & {
64
- formRef?: RefObject<HTMLDivElement>;
56
+ formRef?: React.RefObject<HTMLDivElement>;
65
57
  autoSave?: boolean;
66
58
  }
67
59
  >) => {
@@ -76,9 +68,7 @@ export const FormProvider = ({
76
68
  const keyIsEnter = event.key === 'Enter';
77
69
  const modifierUsed = event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;
78
70
  const inputIsTextarea = (event.target as HTMLElement).tagName.toLowerCase() === 'textarea';
79
- const inputOptOut =
80
- (event.target as HTMLElement).hasAttribute('data-no-submit') ||
81
- (event.target as HTMLElement).closest('[data-no-submit]') !== null;
71
+ const inputOptOut = (event.target as HTMLElement).hasAttribute('data-no-submit');
82
72
 
83
73
  // Regular inputs: Submit on Enter (no modifiers).
84
74
  const shouldSubmitRegularInput = !inputIsTextarea && keyIsEnter && !modifierUsed;
@@ -37,9 +37,15 @@ 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.Trigger asChild content={error} side='bottom'>
41
- <Icon icon='ph--warning--regular' size={4} classNames={errorText} />
42
- </Tooltip.Trigger>
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>
43
49
  )}
44
50
  </div>
45
51
  );
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { useCallback, useState, useEffect, useMemo, type ChangeEvent } from 'react';
5
+ import React, { useCallback, useState, useEffect, useMemo } 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: ChangeEvent<HTMLInputElement>) => {
31
+ (event: React.ChangeEvent<HTMLInputElement>) => {
32
32
  const inputText = event.target.value;
33
33
  setText(inputText);
34
34
  if (inputText !== '' && inputText !== '-') {
@@ -5,10 +5,9 @@
5
5
  import '@dxos-theme';
6
6
 
7
7
  import { type Meta, type StoryObj } from '@storybook/react';
8
- import { Schema } from 'effect';
9
8
  import React, { useState } from 'react';
10
9
 
11
- import { SelectOptionSchema, type SelectOption } from '@dxos/echo-schema';
10
+ import { S, SelectOptionSchema, type SelectOption } from '@dxos/echo-schema';
12
11
  import { withLayout, withTheme } from '@dxos/storybook-utils';
13
12
 
14
13
  import { SelectOptionInput } from './SelectOptionsInput';
@@ -16,9 +15,9 @@ import translations from '../../../translations';
16
15
  import { TestLayout, TestPanel } from '../../testing';
17
16
  import { Form } from '../Form';
18
17
 
19
- const schema = Schema.Struct({
20
- options: Schema.Array(SelectOptionSchema).pipe(Schema.mutable),
21
- }).pipe(Schema.mutable);
18
+ const schema = S.Struct({
19
+ options: S.Array(SelectOptionSchema).pipe(S.mutable),
20
+ }).pipe(S.mutable);
22
21
 
23
22
  const DefaultStory = () => {
24
23
  const [values, setValues] = useState<{ options: SelectOption[] }>({
@@ -47,7 +46,7 @@ const DefaultStory = () => {
47
46
  const meta: Meta = {
48
47
  title: 'ui/react-ui-form/SelectOptionsInput',
49
48
  component: SelectOptionInput,
50
- decorators: [withTheme, withLayout()],
49
+ decorators: [withTheme, withLayout({ tooltips: true })],
51
50
  parameters: { translations },
52
51
  };
53
52
 
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import React, { type ChangeEvent, type KeyboardEvent, useCallback, useEffect, useState, useRef } from 'react';
5
+ import React, { useCallback, useEffect, useState, type ChangeEvent, 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: KeyboardEvent<HTMLInputElement>) => {
92
+ const handleKeyDown = useCallback((e: React.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 | string): InputComponent | undefined => {
15
+ export const getInputComponent = (type: SimpleType, format?: FormatEnum): InputComponent | undefined => {
16
16
  switch (format) {
17
17
  case FormatEnum.GeoPoint:
18
18
  return GeoPointInput;
@@ -3,6 +3,7 @@
3
3
  //
4
4
 
5
5
  export * from './custom';
6
+
6
7
  export * from './Defaults';
7
8
  export * from './Form';
8
9
  export * from './FormContext';
@@ -5,10 +5,9 @@
5
5
  import '@dxos-theme';
6
6
 
7
7
  import { type Meta, type StoryObj } from '@storybook/react';
8
- import { Schema } from 'effect';
9
8
  import React, { useCallback, useMemo, useState } from 'react';
10
9
 
11
- import { Format, type EchoSchema, toJsonSchema, TypedObject } from '@dxos/echo-schema';
10
+ import { Format, type EchoSchema, S, toJsonSchema, TypedObject } from '@dxos/echo-schema';
12
11
  import { Filter, useQuery, useSpace } from '@dxos/react-client/echo';
13
12
  import { withClientProvider } from '@dxos/react-client/testing';
14
13
  import { useAsyncEffect } from '@dxos/react-ui';
@@ -27,7 +26,7 @@ const DefaultStory = () => {
27
26
  useAsyncEffect(async () => {
28
27
  if (space) {
29
28
  class TestSchema extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
30
- name: Schema.String,
29
+ name: S.String,
31
30
  email: Format.Email,
32
31
  salary: Format.Currency(),
33
32
  }) {}
@@ -42,7 +41,7 @@ const DefaultStory = () => {
42
41
  }
43
42
  }, [space]);
44
43
 
45
- const views = useQuery(space, Filter.type(ViewType));
44
+ const views = useQuery(space, Filter.schema(ViewType));
46
45
  const currentTypename = useMemo(() => view?.query?.typename, [view]);
47
46
  const updateViewTypename = useCallback(
48
47
  (newTypename: string) => {
@@ -82,7 +81,7 @@ const meta: Meta<typeof ViewEditor> = {
82
81
  title: 'ui/react-ui-form/ViewEditor',
83
82
  component: ViewEditor,
84
83
  render: DefaultStory,
85
- decorators: [withClientProvider({ createSpace: true }), withLayout({ fullscreen: true }), withTheme],
84
+ decorators: [withClientProvider({ createSpace: true }), withLayout({ fullscreen: true, tooltips: true }), withTheme],
86
85
  parameters: {
87
86
  translations,
88
87
  },
@@ -2,11 +2,10 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Schema, SchemaAST } from 'effect';
6
5
  import React, { useCallback, useMemo, useState } from 'react';
7
6
 
8
7
  import { type SchemaRegistry } from '@dxos/echo-db';
9
- import { Format, type JsonProp, isMutable, toJsonSchema } from '@dxos/echo-schema';
8
+ import { AST, Format, type BaseSchema, S, type JsonProp } from '@dxos/echo-schema';
10
9
  import { invariant } from '@dxos/invariant';
11
10
  import { IconButton, type ThemedClassName, useTranslation } from '@dxos/react-ui';
12
11
  import { List } from '@dxos/react-ui-list';
@@ -19,19 +18,19 @@ import { Form } from '../Form';
19
18
 
20
19
  const grid = 'grid grid-cols-[32px_1fr_32px] min-bs-[2.5rem]';
21
20
 
22
- const ViewMetaSchema = Schema.Struct({
23
- name: Schema.String.annotations({
24
- [SchemaAST.TitleAnnotationId]: 'View',
21
+ const ViewMetaSchema = S.Struct({
22
+ name: S.String.annotations({
23
+ [AST.TitleAnnotationId]: 'View',
25
24
  }),
26
25
  typename: Format.URL.annotations({
27
- [SchemaAST.TitleAnnotationId]: 'Typename',
26
+ [AST.TitleAnnotationId]: 'Typename',
28
27
  }),
29
- }).pipe(Schema.mutable);
28
+ }).pipe(S.mutable);
30
29
 
31
- type ViewMetaType = Schema.Schema.Type<typeof ViewMetaSchema>;
30
+ type ViewMetaType = S.Schema.Type<typeof ViewMetaSchema>;
32
31
 
33
32
  export type ViewEditorProps = ThemedClassName<{
34
- schema: Schema.Schema.AnyNoContext;
33
+ schema: BaseSchema;
35
34
  view: ViewType;
36
35
  registry?: SchemaRegistry;
37
36
  readonly?: boolean;
@@ -54,9 +53,8 @@ export const ViewEditor = ({
54
53
  onDelete,
55
54
  }: ViewEditorProps) => {
56
55
  const { t } = useTranslation(translationKey);
57
- const projection = useMemo(() => new ViewProjection(toJsonSchema(schema), view), [schema, view]);
56
+ const projection = useMemo(() => new ViewProjection(schema.jsonSchema, view), [schema, view]);
58
57
  const [field, setField] = useState<FieldType>();
59
- const immutable = readonly || !isMutable(schema);
60
58
 
61
59
  // TODO(burdon): Should be reactive.
62
60
  const viewValues = useMemo(() => {
@@ -75,14 +73,14 @@ export const ViewEditor = ({
75
73
  // TODO(burdon): Check if mutable; variant of useCallback that return undefined if readonly?
76
74
 
77
75
  const handleAdd = useCallback(() => {
78
- invariant(!immutable);
76
+ invariant(!schema.readonly);
79
77
  const field = projection.createFieldProjection();
80
78
  setField(field);
81
79
  }, [schema, view]);
82
80
 
83
81
  const handleUpdate = useCallback(
84
82
  ({ name, typename }: ViewMetaType) => {
85
- invariant(!immutable);
83
+ invariant(!schema.readonly);
86
84
  requestAnimationFrame(() => {
87
85
  if (view.name !== name) {
88
86
  view.name = name;
@@ -98,7 +96,7 @@ export const ViewEditor = ({
98
96
 
99
97
  const handleDelete = useCallback(
100
98
  (fieldId: string) => {
101
- invariant(!immutable);
99
+ invariant(!schema.readonly);
102
100
  if (fieldId === field?.id) {
103
101
  setField(undefined);
104
102
  }
@@ -110,7 +108,7 @@ export const ViewEditor = ({
110
108
 
111
109
  const handleMove = useCallback(
112
110
  (fromIndex: number, toIndex: number) => {
113
- invariant(!immutable);
111
+ invariant(!schema.readonly);
114
112
  // NOTE(ZaymonFC): Using arrayMove here causes a race condition with the kanban model.
115
113
  const fields = [...view.fields];
116
114
  const [moved] = fields.splice(fromIndex, 1);
@@ -141,16 +139,15 @@ export const ViewEditor = ({
141
139
  );
142
140
 
143
141
  return (
144
- <div role='none' className={mx('grid grid-rows-[1fr_1fr] min-bs-0', classNames)}>
142
+ <div role='none' className={mx('flex flex-col w-full divide-y divide-separator', classNames)}>
145
143
  <Form<ViewMetaType>
146
144
  autoSave
147
145
  schema={ViewMetaSchema}
148
146
  values={viewValues}
149
- onSave={immutable ? undefined : handleUpdate}
150
- classNames='min-bs-0 overflow-y-auto'
147
+ onSave={schema.readonly ? undefined : handleUpdate}
151
148
  />
152
149
 
153
- <div role='none' className='border-bs border-separator min-bs-0 overflow-y-auto'>
150
+ <div>
154
151
  {/* TODO(burdon): Clean up common form ux. */}
155
152
  <div role='none' className='p-2'>
156
153
  <label className={mx(inputTextLabel)}>{t('fields label')}</label>
@@ -158,9 +155,9 @@ export const ViewEditor = ({
158
155
 
159
156
  <List.Root<FieldType>
160
157
  items={view.fields}
161
- isItem={Schema.is(FieldSchema)}
158
+ isItem={S.is(FieldSchema)}
162
159
  getId={(field) => field.id}
163
- onMove={immutable ? undefined : handleMove}
160
+ onMove={schema.readonly ? undefined : handleMove}
164
161
  >
165
162
  {({ items: fields }) => (
166
163
  <>
@@ -182,7 +179,7 @@ export const ViewEditor = ({
182
179
  disabled={view.fields.length <= 1}
183
180
  onClick={() => handleHide(field.id)}
184
181
  />
185
- {!immutable && (
182
+ {!schema.readonly && (
186
183
  <List.ItemDeleteButton
187
184
  icon='ph--trash--regular'
188
185
  disabled={view.fields.length <= 1}
@@ -244,7 +241,7 @@ export const ViewEditor = ({
244
241
  <IconButton
245
242
  icon='ph--plus--regular'
246
243
  label={t('button add property')}
247
- onClick={immutable ? undefined : handleAdd}
244
+ onClick={schema.readonly ? undefined : handleAdd}
248
245
  // TODO(burdon): Show field limit in ux (not tooltip).
249
246
  disabled={view.fields.length >= VIEW_FIELD_LIMIT}
250
247
  />
@@ -7,7 +7,7 @@ import '@dxos-theme';
7
7
  import { type Meta, type StoryObj } from '@storybook/react';
8
8
  import React from 'react';
9
9
 
10
- import { live } from '@dxos/live-object';
10
+ import { create } from '@dxos/live-object';
11
11
  import { testData, TestSchema, testView, type TestType } from '@dxos/schema/testing';
12
12
  import { withLayout, withTheme } from '@dxos/storybook-utils';
13
13
 
@@ -39,23 +39,23 @@ type Story = StoryObj<DeprecatedFormProps<TestType>>;
39
39
 
40
40
  export const Default: Story = {
41
41
  args: {
42
- object: live(testData),
43
- view: live(testView),
42
+ object: create(testData),
43
+ view: create(testView),
44
44
  schema: TestSchema,
45
45
  },
46
46
  };
47
47
 
48
48
  export const Empty: Story = {
49
49
  args: {
50
- view: live(testView),
50
+ view: create(testView),
51
51
  schema: TestSchema,
52
52
  },
53
53
  };
54
54
 
55
55
  export const Readonly: Story = {
56
56
  args: {
57
- object: live(testData),
58
- view: live(testView),
57
+ object: create(testData),
58
+ view: create(testView),
59
59
  schema: TestSchema,
60
60
  readonly: true,
61
61
  },
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { SchemaAST, type Schema, Option, pipe } from 'effect';
5
+ import { SchemaAST as AST, type Schema as S, Option, pipe } from 'effect';
6
6
  import React, { useState } from 'react';
7
7
 
8
8
  import { findProperty } from '@dxos/effect';
@@ -13,7 +13,7 @@ import { type ViewType, getFieldValue, setFieldValue } from '@dxos/schema';
13
13
  export type DeprecatedFormProps<T extends {} = {}> = ThemedClassName<{
14
14
  view: ViewType;
15
15
  object: T;
16
- schema?: Schema.Schema<T>;
16
+ schema?: S.Schema<T>;
17
17
  readonly?: boolean;
18
18
  }>;
19
19
 
@@ -31,16 +31,16 @@ export const DeprecatedForm = <T extends {} = {}>({
31
31
  <div role='none' className={mx('flex flex-col w-full gap-2 p-2', classNames)}>
32
32
  {view.fields.map((field) => {
33
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;
34
+ const title = (prop && pipe(AST.getTitleAnnotation(prop), Option.getOrUndefined)) ?? '';
35
+ const description = (prop && pipe(AST.getDescriptionAnnotation(prop), Option.getOrUndefined)) ?? title;
36
36
  // const format =
37
- // (prop && (getPatternAnnotation(prop) ?? (SchemaAST.isNumberKeyword(prop) && RealNumberFormat))) || undefined;
37
+ // (prop && (getPatternAnnotation(prop) ?? (AST.isNumberKeyword(prop) && RealNumberFormat))) || undefined;
38
38
 
39
39
  //
40
40
  // Boolean
41
41
  //
42
42
 
43
- if (prop && SchemaAST.isBooleanKeyword(prop)) {
43
+ if (prop && AST.isBooleanKeyword(prop)) {
44
44
  const value = object && getFieldValue(object, field);
45
45
  const onChange = (checked: boolean) => {
46
46
  setFieldValue(object, field, checked);
@@ -12,7 +12,7 @@ import { mx, textBlockWidth } from '@dxos/react-ui-theme';
12
12
  */
13
13
  export const DeprecatedFormContainer = ({ children, classNames }: ThemedClassName<PropsWithChildren>) => {
14
14
  return (
15
- <div role='none' className='container-max-width p-4 justify-center overflow-x-hidden overflow-y-auto'>
15
+ <div role='none' className='w-full p-4 justify-center overflow-x-hidden overflow-y-auto'>
16
16
  <div role='form' className={mx('flex flex-col', textBlockWidth, classNames)}>
17
17
  {children}
18
18
  </div>
@@ -3,7 +3,6 @@
3
3
  //
4
4
 
5
5
  export * from './deprecated';
6
-
7
6
  export * from './ControlSection';
8
7
  export * from './FieldEditor';
9
8
  export * from './Form';
@@ -1,7 +1,5 @@
1
1
  //
2
- // Copyright 2025 DXOS.org
2
+ // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
5
  export * from './useForm';
6
- export * from './useRefQueryLookupHandler';
7
- export * from './useQueryRefOptions';
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { SchemaAST, type Schema } from 'effect';
5
+ import { SchemaAST as AST, type Schema as S } from 'effect';
6
6
  import { useCallback, useEffect, useMemo, useState } from 'react';
7
7
 
8
8
  import { type BaseObject, getValue, setValue } from '@dxos/echo-schema';
@@ -45,7 +45,7 @@ export interface FormOptions<T extends BaseObject> {
45
45
  /**
46
46
  * Effect schema (Type literal).
47
47
  */
48
- schema: Schema.Schema<T, any>;
48
+ schema: S.Schema<T>;
49
49
 
50
50
  /**
51
51
  * Initial values (which may not pass validation).
@@ -90,10 +90,9 @@ export const useForm = <T extends BaseObject>({
90
90
  onValid,
91
91
  onSave,
92
92
  }: FormOptions<T>): FormHandler<T> => {
93
- invariant(SchemaAST.isTypeLiteral(schema.ast));
93
+ invariant(AST.isTypeLiteral(schema.ast));
94
94
 
95
95
  const [values, setValues] = useState<Partial<T>>(initialValues);
96
-
97
96
  useEffect(() => {
98
97
  setValues(initialValues);
99
98
  }, [initialValues]);