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

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 (59) hide show
  1. package/dist/lib/browser/index.mjs +168 -149
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +168 -149
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
  8. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +2 -1
  9. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  10. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  11. package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
  12. package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
  13. package/dist/types/src/components/Form/Form.d.ts +2 -2
  14. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  15. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  16. package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
  17. package/dist/types/src/components/Form/FormContent.d.ts +1 -1
  18. package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
  19. package/dist/types/src/components/Form/FormContext.d.ts +1 -1
  20. package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
  21. package/dist/types/src/components/Form/RefField.d.ts +1 -1
  22. package/dist/types/src/components/Form/RefField.d.ts.map +1 -1
  23. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -1
  24. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
  25. package/dist/types/src/components/Form/factory.d.ts.map +1 -1
  26. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  27. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  28. package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
  29. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
  30. package/dist/types/src/hooks/useForm.d.ts +1 -1
  31. package/dist/types/src/hooks/useForm.d.ts.map +1 -1
  32. package/dist/types/src/translations.d.ts +3 -2
  33. package/dist/types/src/translations.d.ts.map +1 -1
  34. package/dist/types/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +30 -29
  36. package/src/components/ControlSection/ControlSection.stories.tsx +2 -1
  37. package/src/components/FieldEditor/FieldEditor.stories.tsx +4 -3
  38. package/src/components/FieldEditor/FieldEditor.test.tsx +3 -2
  39. package/src/components/FieldEditor/FieldEditor.tsx +16 -6
  40. package/src/components/Form/ArrayField.tsx +7 -6
  41. package/src/components/Form/Defaults.tsx +2 -1
  42. package/src/components/Form/Form.stories.tsx +4 -3
  43. package/src/components/Form/Form.tsx +4 -3
  44. package/src/components/Form/FormActions.tsx +2 -1
  45. package/src/components/Form/FormContent.tsx +6 -5
  46. package/src/components/Form/FormContext.tsx +5 -5
  47. package/src/components/Form/RefField.tsx +6 -5
  48. package/src/components/Form/custom/GeoPointInput.tsx +2 -2
  49. package/src/components/Form/custom/SelectOptionsInput.stories.tsx +3 -2
  50. package/src/components/Form/custom/SelectOptionsInput.tsx +28 -29
  51. package/src/components/Form/factory.tsx +1 -1
  52. package/src/components/ViewEditor/ViewEditor.stories.tsx +5 -4
  53. package/src/components/ViewEditor/ViewEditor.test.tsx +5 -4
  54. package/src/components/ViewEditor/ViewEditor.tsx +69 -54
  55. package/src/components/deprecated/Form.stories.tsx +3 -2
  56. package/src/components/deprecated/Form.tsx +3 -3
  57. package/src/hooks/useForm.ts +5 -3
  58. package/src/hooks/useRefQueryLookupHandler.ts +1 -1
  59. package/src/translations.ts +3 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-form",
3
- "version": "0.8.4-main.b97322e",
3
+ "version": "0.8.4-main.f5c0578",
4
4
  "description": "Data components.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -9,6 +9,7 @@
9
9
  "type": "module",
10
10
  "exports": {
11
11
  ".": {
12
+ "source": "./src/index.ts",
12
13
  "types": "./dist/types/src/index.d.ts",
13
14
  "browser": "./dist/lib/browser/index.mjs",
14
15
  "node": "./dist/lib/node-esm/index.mjs"
@@ -29,48 +30,48 @@
29
30
  "@radix-ui/react-context": "1.1.1",
30
31
  "date-fns": "^3.3.1",
31
32
  "jsonpath": "^1.1.1",
32
- "@dxos/async": "0.8.4-main.b97322e",
33
- "@dxos/debug": "0.8.4-main.b97322e",
34
- "@dxos/echo-schema": "0.8.4-main.b97322e",
35
- "@dxos/echo": "0.8.4-main.b97322e",
36
- "@dxos/echo-db": "0.8.4-main.b97322e",
37
- "@dxos/effect": "0.8.4-main.b97322e",
38
- "@dxos/invariant": "0.8.4-main.b97322e",
39
- "@dxos/keys": "0.8.4-main.b97322e",
40
- "@dxos/lit-ui": "0.8.4-main.b97322e",
41
- "@dxos/log": "0.8.4-main.b97322e",
42
- "@dxos/react-client": "0.8.4-main.b97322e",
43
- "@dxos/live-object": "0.8.4-main.b97322e",
44
- "@dxos/react-ui-list": "0.8.4-main.b97322e",
45
- "@dxos/react-ui-stack": "0.8.4-main.b97322e",
46
- "@dxos/react-ui-pickers": "0.8.4-main.b97322e",
47
- "@dxos/react-ui-types": "0.8.4-main.b97322e",
48
- "@dxos/react-ui-tag-picker": "0.8.4-main.b97322e",
49
- "@dxos/schema": "0.8.4-main.b97322e",
50
- "@dxos/util": "0.8.4-main.b97322e"
33
+ "@dxos/async": "0.8.4-main.f5c0578",
34
+ "@dxos/echo": "0.8.4-main.f5c0578",
35
+ "@dxos/debug": "0.8.4-main.f5c0578",
36
+ "@dxos/echo-schema": "0.8.4-main.f5c0578",
37
+ "@dxos/echo-db": "0.8.4-main.f5c0578",
38
+ "@dxos/effect": "0.8.4-main.f5c0578",
39
+ "@dxos/invariant": "0.8.4-main.f5c0578",
40
+ "@dxos/keys": "0.8.4-main.f5c0578",
41
+ "@dxos/log": "0.8.4-main.f5c0578",
42
+ "@dxos/lit-ui": "0.8.4-main.f5c0578",
43
+ "@dxos/live-object": "0.8.4-main.f5c0578",
44
+ "@dxos/react-client": "0.8.4-main.f5c0578",
45
+ "@dxos/react-ui-pickers": "0.8.4-main.f5c0578",
46
+ "@dxos/react-ui-list": "0.8.4-main.f5c0578",
47
+ "@dxos/react-ui-stack": "0.8.4-main.f5c0578",
48
+ "@dxos/react-ui-tag-picker": "0.8.4-main.f5c0578",
49
+ "@dxos/react-ui-types": "0.8.4-main.f5c0578",
50
+ "@dxos/schema": "0.8.4-main.f5c0578",
51
+ "@dxos/util": "0.8.4-main.f5c0578"
51
52
  },
52
53
  "devDependencies": {
53
54
  "@types/jsonpath": "^0.2.4",
54
55
  "@types/react": "~18.2.0",
55
56
  "@types/react-dom": "~18.2.0",
56
- "effect": "3.17.0",
57
+ "effect": "3.17.7",
57
58
  "fast-check": "^3.19.0",
58
59
  "react": "~18.2.0",
59
60
  "react-dom": "~18.2.0",
60
61
  "vite": "5.4.7",
61
- "@dxos/echo-signals": "0.8.4-main.b97322e",
62
- "@dxos/random": "0.8.4-main.b97322e",
63
- "@dxos/react-ui": "0.8.4-main.b97322e",
64
- "@dxos/react-ui-syntax-highlighter": "0.8.4-main.b97322e",
65
- "@dxos/storybook-utils": "0.8.4-main.b97322e",
66
- "@dxos/react-ui-theme": "0.8.4-main.b97322e"
62
+ "@dxos/echo-signals": "0.8.4-main.f5c0578",
63
+ "@dxos/react-ui": "0.8.4-main.f5c0578",
64
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.f5c0578",
65
+ "@dxos/storybook-utils": "0.8.4-main.f5c0578",
66
+ "@dxos/react-ui-theme": "0.8.4-main.f5c0578",
67
+ "@dxos/random": "0.8.4-main.f5c0578"
67
68
  },
68
69
  "peerDependencies": {
69
70
  "effect": "^3.13.3",
70
71
  "react": "~18.2.0",
71
72
  "react-dom": "~18.2.0",
72
- "@dxos/react-ui": "0.8.4-main.b97322e",
73
- "@dxos/react-ui-theme": "0.8.4-main.b97322e"
73
+ "@dxos/react-ui": "0.8.4-main.f5c0578",
74
+ "@dxos/react-ui-theme": "0.8.4-main.f5c0578"
74
75
  },
75
76
  "publishConfig": {
76
77
  "access": "public"
@@ -10,9 +10,10 @@ import React from 'react';
10
10
  import { StackItem } from '@dxos/react-ui-stack';
11
11
  import { withLayout, withTheme } from '@dxos/storybook-utils';
12
12
 
13
- import { ControlPage, ControlSection, type ControlSectionProps } from './ControlSection';
14
13
  import { translations } from '../../translations';
15
14
 
15
+ import { ControlPage, ControlSection, type ControlSectionProps } from './ControlSection';
16
+
16
17
  const meta: Meta<ControlSectionProps> = {
17
18
  title: 'ui/react-ui-form/ControlSection',
18
19
  component: ControlSection,
@@ -5,7 +5,7 @@
5
5
  import '@dxos-theme';
6
6
 
7
7
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
- import React, { useMemo, useEffect } from 'react';
8
+ import React, { useEffect, useMemo } from 'react';
9
9
 
10
10
  import { createEchoSchema } from '@dxos/live-object/testing';
11
11
  import { log } from '@dxos/log';
@@ -13,9 +13,10 @@ import { ProjectionModel } from '@dxos/schema';
13
13
  import { TestSchema, testView } from '@dxos/schema/testing';
14
14
  import { withLayout, withTheme } from '@dxos/storybook-utils';
15
15
 
16
- import { FieldEditor, type FieldEditorProps } from './FieldEditor';
17
16
  import { translations } from '../../translations';
18
- import { TestLayout, TestPanel, FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
17
+ import { FIELD_EDITOR_DEBUG_SYMBOL, TestLayout, TestPanel } from '../testing';
18
+
19
+ import { FieldEditor, type FieldEditorProps } from './FieldEditor';
19
20
 
20
21
  // Type definition for debug objects exposed to tests.
21
22
  export type FieldEditorDebugObjects = {
@@ -3,14 +3,15 @@
3
3
  //
4
4
 
5
5
  import { composeStories } from '@storybook/react';
6
- import { screen, cleanup, fireEvent, act } from '@testing-library/react';
6
+ import { act, cleanup, fireEvent, screen } from '@testing-library/react';
7
7
  import { afterEach, describe, expect, test } from 'vitest';
8
8
 
9
9
  import { ProjectionModel } from '@dxos/schema';
10
10
 
11
+ import { FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
12
+
11
13
  import * as stories from './FieldEditor.stories';
12
14
  import { type FieldEditorDebugObjects } from './FieldEditor.stories';
13
- import { FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
14
15
 
15
16
  const { Default } = composeStories(stories);
16
17
 
@@ -5,23 +5,24 @@
5
5
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
6
6
 
7
7
  import { type SchemaRegistry } from '@dxos/echo-db';
8
- import { FormatEnum, FormatEnums, formatToType, type EchoSchema } from '@dxos/echo-schema';
8
+ import { type EchoSchema, FormatEnum, FormatEnums, formatToType } from '@dxos/echo-schema';
9
9
  import { log } from '@dxos/log';
10
10
  import { useTranslation } from '@dxos/react-ui';
11
11
  import {
12
- getFormatSchema,
13
- getSchemaProperties,
14
- sortProperties,
15
12
  type FieldType,
16
- type PropertyType,
17
13
  type ProjectionModel,
14
+ type PropertyType,
18
15
  type SchemaProperty,
16
+ getFormatSchema,
17
+ getSchemaProperties,
18
+ sortProperties,
19
19
  } from '@dxos/schema';
20
20
 
21
21
  import { translationKey } from '../../translations';
22
22
  import { Form, type FormProps, type InputComponent, SelectInput, SelectOptionInput } from '../Form';
23
23
 
24
24
  export type FieldEditorProps = {
25
+ readonly?: boolean;
25
26
  projection: ProjectionModel;
26
27
  field: FieldType;
27
28
  registry?: SchemaRegistry;
@@ -32,7 +33,15 @@ export type FieldEditorProps = {
32
33
  /**
33
34
  * Displays a Form representing the metadata for a given `Field` and `View`.
34
35
  */
35
- export const FieldEditor = ({ projection, field, registry, onSave, onCancel, outerSpacing }: FieldEditorProps) => {
36
+ export const FieldEditor = ({
37
+ readonly,
38
+ projection,
39
+ field,
40
+ registry,
41
+ onSave,
42
+ onCancel,
43
+ outerSpacing,
44
+ }: FieldEditorProps) => {
36
45
  const { t } = useTranslation(translationKey);
37
46
  const [props, setProps] = useState<PropertyType>(projection.getFieldProjection(field.id).props);
38
47
  useEffect(() => setProps(projection.getFieldProjection(field.id).props), [field, projection]);
@@ -178,6 +187,7 @@ export const FieldEditor = ({ projection, field, registry, onSave, onCancel, out
178
187
  <Form<PropertyType>
179
188
  key={field.id}
180
189
  autoFocus
190
+ readonly={readonly}
181
191
  values={props}
182
192
  schema={fieldSchema}
183
193
  filter={propIsNotType}
@@ -6,18 +6,19 @@ import { SchemaAST, pipe } from 'effect';
6
6
  import { capitalize } from 'effect/String';
7
7
  import React, { Fragment, useCallback } from 'react';
8
8
 
9
- import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from '@dxos/effect';
9
+ import { SimpleType, findNode, getDiscriminatedType, isDiscriminatedUnion } from '@dxos/effect';
10
10
  import { invariant } from '@dxos/invariant';
11
11
  import { IconButton, useTranslation } from '@dxos/react-ui';
12
- import { getSchemaProperties, type SchemaProperty } from '@dxos/schema';
12
+ import { type SchemaProperty, getSchemaProperties } from '@dxos/schema';
13
13
 
14
- import { type ComponentLookup } from './Form';
15
- import { FormField } from './FormContent';
16
- import { useFormValues, type FormInputStateProps } from './FormContext';
17
- import { InputHeader, type InputComponent } from './Input';
18
14
  import { translationKey } from '../../translations';
19
15
  import { findArrayElementType } from '../../util';
20
16
 
17
+ import { type ComponentLookup } from './Form';
18
+ import { FormField } from './FormContent';
19
+ import { type FormInputStateProps, useFormValues } from './FormContext';
20
+ import { type InputComponent, InputHeader } from './Input';
21
+
21
22
  type ArrayFieldProps = {
22
23
  property: SchemaProperty<any>;
23
24
  readonly?: boolean;
@@ -6,9 +6,10 @@ import React, { useCallback, useEffect, useRef } from 'react';
6
6
 
7
7
  import { Input, Select, useTranslation } from '@dxos/react-ui';
8
8
 
9
- import { type InputProps, InputHeader } from './Input';
10
9
  import { translationKey } from '../../translations';
11
10
 
11
+ import { InputHeader, type InputProps } from './Input';
12
+
12
13
  export const TextInput = ({
13
14
  type,
14
15
  label,
@@ -9,17 +9,18 @@ import { Schema } from 'effect';
9
9
  import React, { useCallback, useState } from 'react';
10
10
 
11
11
  import { ContactType } from '@dxos/client/testing';
12
- import { type BaseObject, Expando, Format, getObjectDXN, Ref, type TypeAnnotation } from '@dxos/echo-schema';
12
+ import { type BaseObject, Expando, Format, Ref, type TypeAnnotation, getObjectDXN } from '@dxos/echo-schema';
13
13
  import { live } from '@dxos/live-object';
14
14
  import { withSurfaceVariantsLayout } from '@dxos/react-ui/testing';
15
15
  import { Testing } from '@dxos/schema/testing';
16
16
  import { withLayout, withTheme } from '@dxos/storybook-utils';
17
17
 
18
- import { SelectInput } from './Defaults';
19
- import { Form, type FormProps } from './Form';
20
18
  import { translations } from '../../translations';
21
19
  import { TestLayout, TestPanel } from '../testing';
22
20
 
21
+ import { SelectInput } from './Defaults';
22
+ import { Form, type FormProps } from './Form';
23
+
23
24
  const AddressSchema = Schema.Struct({
24
25
  street: Schema.optional(Schema.String.annotations({ title: 'Street' })),
25
26
  city: Schema.optional(Schema.String.annotations({ title: 'City' })),
@@ -10,11 +10,12 @@ import { type ThemedClassName } from '@dxos/react-ui';
10
10
  import { cardDialogOverflow, cardSpacing } from '@dxos/react-ui-stack';
11
11
  import { type SchemaProperty } from '@dxos/schema';
12
12
 
13
+ import { type FormOptions } from '../../hooks';
14
+
13
15
  import { FormActions, type FormOuterSpacing } from './FormActions';
14
16
  import { FormFields, type FormFieldsProps } from './FormContent';
15
17
  import { FormProvider } from './FormContext';
16
- import { type InputProps, type InputComponent } from './Input';
17
- import { type FormOptions } from '../../hooks';
18
+ import { type InputComponent, type InputProps } from './Input';
18
19
 
19
20
  export type PropsFilter<T extends BaseObject> = (props: SchemaProperty<T>[]) => SchemaProperty<T>[];
20
21
 
@@ -99,7 +100,7 @@ export const Form = <T extends BaseObject>({
99
100
  readonly={readonly}
100
101
  schema={schema}
101
102
  />
102
- {(onCancel || onSave) && !autoSave && (
103
+ {(onCancel || onSave) && !autoSave && !readonly && (
103
104
  <FormActions readonly={readonly} onCancel={onCancel} outerSpacing={outerSpacing} />
104
105
  )}
105
106
  </div>
@@ -8,9 +8,10 @@ import { IconButton, useTranslation } from '@dxos/react-ui';
8
8
  import { cardSpacing } from '@dxos/react-ui-stack';
9
9
  import { mx } from '@dxos/react-ui-theme';
10
10
 
11
- import { useFormContext } from './FormContext';
12
11
  import { translationKey } from '../../translations';
13
12
 
13
+ import { useFormContext } from './FormContext';
14
+
14
15
  export type FormOuterSpacing = boolean | 'blockStart-0' | 'scroll-fields';
15
16
 
16
17
  export type FormActionsProps = {
@@ -9,18 +9,19 @@ import React, { forwardRef, useMemo } from 'react';
9
9
  import { createJsonPath, findNode, getDiscriminatedType, isDiscriminatedUnion } from '@dxos/effect';
10
10
  import { type ThemedClassName } from '@dxos/react-ui';
11
11
  import { mx } from '@dxos/react-ui-theme';
12
- import { getSchemaProperties, type SchemaProperty } from '@dxos/schema';
12
+ import { type SchemaProperty, getSchemaProperties } from '@dxos/schema';
13
13
  import { isNotFalsy } from '@dxos/util';
14
14
 
15
+ import { type QueryRefOptions } from '../../hooks';
16
+ import { getRefProps } from '../../util';
17
+
15
18
  import { ArrayField } from './ArrayField';
16
19
  import { SelectInput } from './Defaults';
20
+ import { getInputComponent } from './factory';
17
21
  import { type ComponentLookup } from './Form';
18
- import { useInputProps, useFormValues } from './FormContext';
22
+ import { useFormValues, useInputProps } from './FormContext';
19
23
  import { type InputComponent } from './Input';
20
24
  import { RefField } from './RefField';
21
- import { getInputComponent } from './factory';
22
- import { type QueryRefOptions } from '../../hooks';
23
- import { getRefProps } from '../../util';
24
25
 
25
26
  export type FormFieldProps = {
26
27
  property: SchemaProperty<any>;
@@ -3,19 +3,19 @@
3
3
  //
4
4
 
5
5
  import React, {
6
- createContext,
6
+ type FocusEvent,
7
+ type PropsWithChildren,
7
8
  type RefObject,
9
+ createContext,
10
+ useCallback,
8
11
  useContext,
9
12
  useEffect,
10
13
  useMemo,
11
- type FocusEvent,
12
- type PropsWithChildren,
13
- useCallback,
14
14
  } from 'react';
15
15
 
16
16
  import { raise } from '@dxos/debug';
17
17
  import { type BaseObject, getValue } from '@dxos/echo-schema';
18
- import { createJsonPath, type SimpleType } from '@dxos/effect';
18
+ import { type SimpleType, createJsonPath } from '@dxos/effect';
19
19
 
20
20
  import { type FormHandler, type FormOptions, useForm } from '../../hooks';
21
21
 
@@ -3,28 +3,29 @@
3
3
  //
4
4
 
5
5
  import { type SchemaAST } from 'effect';
6
- import React, { useCallback, useMemo, type FocusEvent } from 'react';
6
+ import React, { type FocusEvent, useCallback, useMemo } from 'react';
7
7
 
8
8
  import {
9
9
  Expando,
10
- getTypeAnnotation,
11
10
  Ref,
12
11
  ReferenceAnnotationId,
13
12
  type ReferenceAnnotationValue,
13
+ getTypeAnnotation,
14
14
  } from '@dxos/echo-schema';
15
15
  import { findAnnotation } from '@dxos/effect';
16
16
  import { DXN } from '@dxos/keys';
17
17
  import { DxRefTag } from '@dxos/lit-ui/react';
18
18
  import { Input, useTranslation } from '@dxos/react-ui';
19
- import { TagPicker, type TagPickerMode, type TagPickerItemData } from '@dxos/react-ui-tag-picker';
19
+ import { TagPicker, type TagPickerItemData, type TagPickerMode } from '@dxos/react-ui-tag-picker';
20
20
  import { descriptionText, mx } from '@dxos/react-ui-theme';
21
21
  import { isNonNullable } from '@dxos/util';
22
22
 
23
- import { TextInput } from './Defaults';
24
- import { InputHeader, type InputProps } from './Input';
25
23
  import { type QueryRefOptions, useQueryRefOptions } from '../../hooks';
26
24
  import { translationKey } from '../../translations';
27
25
 
26
+ import { TextInput } from './Defaults';
27
+ import { InputHeader, type InputProps } from './Input';
28
+
28
29
  type RefFieldProps = InputProps & {
29
30
  ast?: SchemaAST.AST;
30
31
  array?: boolean;
@@ -2,9 +2,9 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { useCallback, useState, useEffect, useMemo, type ChangeEvent } from 'react';
5
+ import React, { type ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react';
6
6
 
7
- import { type GeoPoint, GeoLocation } from '@dxos/echo-schema';
7
+ import { GeoLocation, type GeoPoint } from '@dxos/echo-schema';
8
8
  import { Input, useTranslation } from '@dxos/react-ui';
9
9
  import { safeParseFloat } from '@dxos/util';
10
10
 
@@ -8,14 +8,15 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
8
  import { Schema } from 'effect';
9
9
  import React, { useState } from 'react';
10
10
 
11
- import { SelectOptionSchema, type SelectOption } from '@dxos/echo-schema';
11
+ import { type SelectOption, SelectOptionSchema } from '@dxos/echo-schema';
12
12
  import { withLayout, withTheme } from '@dxos/storybook-utils';
13
13
 
14
- import { SelectOptionInput } from './SelectOptionsInput';
15
14
  import { translations } from '../../../translations';
16
15
  import { TestLayout, TestPanel } from '../../testing';
17
16
  import { Form } from '../Form';
18
17
 
18
+ import { SelectOptionInput } from './SelectOptionsInput';
19
+
19
20
  const schema = Schema.Struct({
20
21
  options: Schema.Array(SelectOptionSchema).pipe(Schema.mutable),
21
22
  }).pipe(Schema.mutable);
@@ -2,19 +2,18 @@
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, { type ChangeEvent, type KeyboardEvent, useCallback, useEffect, useRef, useState } from 'react';
6
6
 
7
7
  import { type SelectOption } from '@dxos/echo-schema';
8
8
  import { PublicKey } from '@dxos/keys';
9
- import { type ChromaticPalette, Icon, IconButton, Input, Tag, Toolbar, useTranslation } from '@dxos/react-ui';
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, mx } from '@dxos/react-ui-theme';
12
+ import { hues, subtleHover } from '@dxos/react-ui-theme';
13
13
 
14
14
  import { translationKey } from '../../../translations';
15
15
  import { InputHeader, type InputProps } from '../Input';
16
16
 
17
- // TODO(ZaymonFC): Handle disabled.
18
17
  export const SelectOptionInput = ({ type, label, disabled, getStatus, getValue, onValueChange }: InputProps) => {
19
18
  const [selected, setSelectedId] = useState<string | null>(null);
20
19
  const [isNewOption, setIsNewOption] = useState(false);
@@ -115,7 +114,12 @@ export const SelectOptionInput = ({ type, label, disabled, getStatus, getValue,
115
114
  <InputHeader error={error} label={label} />
116
115
  <div role='none'>
117
116
  {options && (
118
- <List.Root items={options} isItem={(item) => true} onMove={handleMove}>
117
+ <List.Root
118
+ items={options}
119
+ isItem={(item) => true}
120
+ onMove={disabled ? undefined : handleMove}
121
+ readonly={disabled}
122
+ >
119
123
  {({ items }) => (
120
124
  <div role='list' className='w-full overflow-auto'>
121
125
  {items?.map((item) => (
@@ -123,26 +127,30 @@ export const SelectOptionInput = ({ type, label, disabled, getStatus, getValue,
123
127
  <List.Item
124
128
  role='button'
125
129
  item={item}
126
- classNames={['p-1 plb-2', 'flex flex-col', 'cursor-pointer', 'hover:bg-hoverOverlay']}
130
+ classNames={[subtleHover, 'flex flex-col cursor-pointer rounded-sm']}
127
131
  aria-expanded={selected === item.id}
128
132
  >
129
133
  <div className='flex items-center'>
130
- <List.ItemDragHandle />
134
+ <List.ItemDragHandle disabled={disabled} />
131
135
  <List.ItemTitle onClick={() => handleClick(item.id)} classNames='flex-1'>
132
136
  {/* TODO(ZaymonFC): Move spacer into Tag component. */}
133
137
  <Tag palette={item.color as ChromaticPalette}>{item.title || '\u200b'}</Tag>
134
138
  </List.ItemTitle>
135
- <Icon
139
+ <IconButton
140
+ iconOnly
141
+ variant='ghost'
142
+ label={t(selected === item.id ? 'collapse label' : 'expand label', { ns: 'os' })}
136
143
  icon={selected === item.id ? 'ph--caret-down--regular' : 'ph--caret-right--regular'}
137
144
  onClick={() => handleClick(item.id)}
138
145
  />
139
146
  </div>
140
147
  {selected === item.id && (
141
- <div className='mis-4 mbs-2 flex flex-col gap-1'>
148
+ <div className='pli-cardSpacingInline plb-cardSpacingBlock flex flex-col gap-1 density-fine'>
142
149
  {/* 16px to match drag handle width. */}
143
150
  <Input.Label classNames='text-xs'>{t('select option label')}</Input.Label>
144
- <div className='flex flex-row items-center gap-1'>
151
+ <div className='grid grid-cols-[1fr_min-content_min-content] gap-1'>
145
152
  <Input.TextInput
153
+ disabled={disabled}
146
154
  placeholder={t('select option label placeholder')}
147
155
  ref={selected === item.id ? inputRef : undefined}
148
156
  value={item.title}
@@ -151,14 +159,9 @@ export const SelectOptionInput = ({ type, label, disabled, getStatus, getValue,
151
159
  classNames='flex-1'
152
160
  data-no-submit
153
161
  />
154
- <Toolbar.Root classNames='p-0 m-0 !is-auto'>
155
- <HuePicker
156
- value={item.color}
157
- onChange={handleColorChange(item.id)}
158
- rootVariant='toolbar-button'
159
- />
160
- </Toolbar.Root>
162
+ <HuePicker disabled={disabled} value={item.color} onChange={handleColorChange(item.id)} />
161
163
  <IconButton
164
+ disabled={disabled}
162
165
  icon='ph--trash--fill'
163
166
  iconOnly
164
167
  label={t('select option delete')}
@@ -170,18 +173,14 @@ export const SelectOptionInput = ({ type, label, disabled, getStatus, getValue,
170
173
  </List.Item>
171
174
  </div>
172
175
  ))}
173
- <div role='listitem'>
174
- <div
175
- role='button'
176
- className={mx('p-1 plb-2', 'flex flex-col', 'cursor-pointer', 'hover:bg-hoverOverlay')}
177
- onClick={handleAdd}
178
- >
179
- <div className='flex items-center gap-1'>
180
- <Icon icon='ph--plus--regular' />
181
- <span className='text-sm'>{t('select option add')}</span>
182
- </div>
183
- </div>
184
- </div>
176
+ <IconButton
177
+ variant='ghost'
178
+ icon='ph--plus--regular'
179
+ label={t('select option add')}
180
+ onClick={handleAdd}
181
+ disabled={disabled}
182
+ classNames='is-full'
183
+ />
185
184
  </div>
186
185
  )}
187
186
  </List.Root>
@@ -5,9 +5,9 @@
5
5
  import { FormatEnum } from '@dxos/echo-schema';
6
6
  import { type SimpleType } from '@dxos/effect';
7
7
 
8
+ import { GeoPointInput } from './custom';
8
9
  import { BooleanInput, MarkdownInput, NumberInput, TextInput } from './Defaults';
9
10
  import { type InputComponent } from './Input';
10
- import { GeoPointInput } from './custom';
11
11
 
12
12
  /**
13
13
  * Get property input component.
@@ -6,20 +6,21 @@ import '@dxos-theme';
6
6
 
7
7
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
8
  import { Schema } from 'effect';
9
- import React, { useCallback, useMemo, useState, useEffect } from 'react';
9
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
10
10
 
11
11
  import { Obj, Type } from '@dxos/echo';
12
- import { Format, type EchoSchema, toJsonSchema, TypedObject } from '@dxos/echo-schema';
12
+ import { type EchoSchema, Format, TypedObject, toJsonSchema } from '@dxos/echo-schema';
13
13
  import { Filter, useQuery, useSpace } from '@dxos/react-client/echo';
14
14
  import { withClientProvider } from '@dxos/react-client/testing';
15
15
  import { useAsyncEffect } from '@dxos/react-ui';
16
16
  import { DataType, ProjectionModel, createView } from '@dxos/schema';
17
- import { withTheme, withLayout } from '@dxos/storybook-utils';
17
+ import { withLayout, withTheme } from '@dxos/storybook-utils';
18
18
 
19
- import { ViewEditor, type ViewEditorProps } from './ViewEditor';
20
19
  import { translations } from '../../translations';
21
20
  import { TestLayout, TestPanel, VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
22
21
 
22
+ import { ViewEditor, type ViewEditorProps } from './ViewEditor';
23
+
23
24
  // Type definition for debug objects exposed to tests.
24
25
  export type ViewEditorDebugObjects = {
25
26
  schema: EchoSchema;
@@ -3,15 +3,16 @@
3
3
  //
4
4
 
5
5
  import { composeStories } from '@storybook/react';
6
- import { screen, cleanup, fireEvent, waitFor } from '@testing-library/react';
6
+ import { cleanup, fireEvent, screen, waitFor } from '@testing-library/react';
7
7
  import { afterEach, describe, expect, test } from 'vitest';
8
8
 
9
9
  import { EchoSchema, isInstanceOf } from '@dxos/echo-schema';
10
10
  import { DataType, ProjectionModel } from '@dxos/schema';
11
11
 
12
+ import { VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
13
+
12
14
  import * as stories from './ViewEditor.stories';
13
15
  import { type ViewEditorDebugObjects } from './ViewEditor.stories';
14
- import { VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
15
16
 
16
17
  const { Default } = composeStories(stories);
17
18
 
@@ -41,7 +42,7 @@ describe('ViewEditor', () => {
41
42
  test('renders view editor', async () => {
42
43
  await Default.run();
43
44
  await waitForViewEditor();
44
- expect(screen.getByText('Typename')).toBeInTheDocument();
45
+ expect(screen.getByText('Record type')).toBeInTheDocument();
45
46
  });
46
47
 
47
48
  test('change property name', async () => {
@@ -140,7 +141,7 @@ describe('ViewEditor', () => {
140
141
  // Find the delete button for the 'name' property.
141
142
  const nameProperty = screen.getByText('name');
142
143
  const propertyRow = nameProperty.closest('[role="listitem"]');
143
- const deleteButton = propertyRow?.querySelector('button:last-child');
144
+ const deleteButton = propertyRow?.querySelector('[data-testid="field.delete"]');
144
145
 
145
146
  if (!deleteButton) {
146
147
  throw new Error('Delete button not found');