@dxos/react-ui-form 0.7.3-staging.cc8dd3e → 0.7.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"Form.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/Form/Form.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5D,OAAO,EAAO,KAAK,UAAU,EAAU,CAAC,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAI/C,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAK9C,QAAA,MAAM,UAAU;;;;;;;;;;;GAaE,CAAC;AAEnB,KAAK,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,UAAU,CAAC,CAAC;AAEjD,KAAK,UAAU,CAAC,CAAC,SAAS,UAAU,IAAI;IAAE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;CAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;AAiB/E,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAQ/B,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,CAAC,CAAC,SAAS,UAAU,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAE3D,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,QAAQ,CAWnC,CAAC;AAGF,eAAO,MAAM,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,aAAa,CAQ5C,CAAC;AAkBF,QAAA,MAAM,WAAW;;;;;;;;GAaC,CAAC;AAEnB,KAAK,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,WAAW,CAAC,CAAC;AAEnD,KAAK,4BAA4B,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;AAgCzD,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,4BAA4B,CAUrE,CAAC"}
1
+ {"version":3,"file":"Form.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/Form/Form.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5D,OAAO,EAAO,KAAK,UAAU,EAAU,CAAC,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAI/C,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAY9C,QAAA,MAAM,UAAU;;;;;;;;;;;GAME,CAAC;AAEnB,KAAK,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,UAAU,CAAC,CAAC;AAEjD,KAAK,UAAU,CAAC,CAAC,SAAS,UAAU,IAAI;IAAE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;CAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;AAiB/E,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAQ/B,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,CAAC,CAAC,SAAS,UAAU,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAE3D,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,QAAQ,CAWnC,CAAC;AAGF,eAAO,MAAM,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,aAAa,CAQ5C,CAAC;AAkBF,QAAA,MAAM,WAAW;;;;;;;;GAaC,CAAC;AAEnB,KAAK,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,WAAW,CAAC,CAAC;AAEnD,KAAK,4BAA4B,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;AAgCzD,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,4BAA4B,CAUrE,CAAC;AAEF,QAAA,MAAM,YAAY;;;;;;;;GAGA,CAAC;AAEnB,KAAK,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,YAAY,CAAC,CAAC;AAiBrD,eAAO,MAAM,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,CAQlD,CAAC"}
@@ -1,17 +1,18 @@
1
1
  import React, { type FC, type PropsWithChildren } from 'react';
2
- import { type BaseObject, type FormatEnum, type PropertyKey } from '@dxos/echo-schema';
2
+ import { type BaseObject, type FormatEnum } from '@dxos/echo-schema';
3
3
  import { type SimpleType } from '@dxos/effect';
4
- import { type FormHandler } from '../../hooks';
4
+ import { type FormPath, type FormHandler } from '../../hooks';
5
5
  /**
6
6
  * Props passed to input components.
7
7
  */
8
8
  export type InputProps<T extends BaseObject> = {
9
- property: PropertyKey<T>;
9
+ property: FormPath<T>;
10
10
  type: SimpleType;
11
11
  format?: FormatEnum;
12
- label: string;
12
+ label?: string;
13
13
  disabled?: boolean;
14
14
  placeholder?: string;
15
+ inputOnly?: boolean;
15
16
  } & Pick<FormHandler<T>, 'getStatus' | 'getValue' | 'onValueChange' | 'onBlur'>;
16
17
  /**
17
18
  * Form input component.
@@ -1 +1 @@
1
- {"version":3,"file":"Input.d.ts","sourceRoot":"","sources":["../../../../../src/components/Form/Input.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAE/D,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACvF,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAI/C,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,UAAU,IAAI;IAC7C,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,eAAe,GAAG,QAAQ,CAAC,CAAC;AAEhF;;GAEG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,UAAU,IAAI,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAErE,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH,eAAO,MAAM,WAAW,wBAAyB,gBAAgB,sBAiBhE,CAAC"}
1
+ {"version":3,"file":"Input.d.ts","sourceRoot":"","sources":["../../../../../src/components/Form/Input.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAE/D,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAI/C,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,UAAU,IAAI;IAC7C,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,eAAe,GAAG,QAAQ,CAAC,CAAC;AAEhF;;GAEG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,UAAU,IAAI,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAErE,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH,eAAO,MAAM,WAAW,wBAAyB,gBAAgB,sBAiBhE,CAAC"}
@@ -3,22 +3,23 @@ import { type BaseObject, type PropertyKey } from '@dxos/echo-schema';
3
3
  import { type SimpleType, type S } from '@dxos/effect';
4
4
  import { type ValidationError } from '@dxos/schema';
5
5
  import { type MaybePromise } from '@dxos/util';
6
+ export type FormPath<T extends object = {}> = PropertyKey<T> | `${PropertyKey<T>}.${number}`;
6
7
  /**
7
8
  * Return type from `useForm` hook.
8
9
  */
9
10
  export type FormHandler<T extends BaseObject> = {
10
11
  values: T;
11
- errors: Record<PropertyKey<T>, string>;
12
- touched: Record<PropertyKey<T>, boolean>;
13
- changed: Record<PropertyKey<T>, boolean>;
12
+ errors: Record<FormPath<T>, string>;
13
+ touched: Record<FormPath<T>, boolean>;
14
+ changed: Record<FormPath<T>, boolean>;
14
15
  canSave: boolean;
15
16
  handleSave: () => void;
16
- getStatus: (property: PropertyKey<T>) => {
17
+ getStatus: (property: FormPath<T>) => {
17
18
  status?: 'error';
18
19
  error?: string;
19
20
  };
20
- getValue: <V>(property: PropertyKey<T>) => V;
21
- onValueChange: <V>(property: PropertyKey<T>, type: SimpleType, value: V) => void;
21
+ getValue: <V>(property: FormPath<T>) => V;
22
+ onValueChange: <V>(property: FormPath<T>, type: SimpleType, value: V) => void;
22
23
  onBlur: (event: FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
23
24
  };
24
25
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"useForm.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useForm.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,UAAU,EAA6C,MAAM,OAAO,CAAC;AAEnF,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,CAAC,EAAE,MAAM,cAAc,CAAC;AAGvD,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,UAAU,IAAI;IAK9C,MAAM,EAAE,CAAC,CAAC;IACV,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,IAAI,CAAC;IAMvB,SAAS,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9E,QAAQ,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC7C,aAAa,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IACjF,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,gBAAgB,GAAG,mBAAmB,CAAC,KAAK,IAAI,CAAC;CAC7E,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,UAAU;IAC/C;;OAEG;IAEH,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAEpB;;OAEG;IAEH,aAAa,EAAE,CAAC,CAAC;IAEjB;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC;IAEtC;;;;OAIG;IAEH,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,eAAe,EAAE,GAAG,SAAS,CAAC;IAE1D;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE;QAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;KAAE,KAAK,IAAI,CAAC;IAE5E;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE;QAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;KAAE,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;CAC1F;AAED;;;GAGG;AACH,eAAO,MAAM,OAAO,GAAI,CAAC,SAAS,UAAU,4EAOzC,WAAW,CAAC,CAAC,CAAC,KAAG,WAAW,CAAC,CAAC,CAwIhC,CAAC"}
1
+ {"version":3,"file":"useForm.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useForm.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,UAAU,EAA6C,MAAM,OAAO,CAAC;AAEnF,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,CAAC,EAAE,MAAM,cAAc,CAAC;AAGvD,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,MAAM,GAAG,EAAE,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC;AAE7F;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,UAAU,IAAI;IAK9C,MAAM,EAAE,CAAC,CAAC;IACV,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,IAAI,CAAC;IAMvB,SAAS,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E,QAAQ,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC1C,aAAa,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC9E,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,gBAAgB,GAAG,mBAAmB,CAAC,KAAK,IAAI,CAAC;CAC7E,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,UAAU;IAC/C;;OAEG;IAEH,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAEpB;;OAEG;IAEH,aAAa,EAAE,CAAC,CAAC;IAEjB;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC;IAEtC;;;;OAIG;IAEH,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,eAAe,EAAE,GAAG,SAAS,CAAC;IAE1D;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE;QAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;KAAE,KAAK,IAAI,CAAC;IAE5E;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE;QAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;KAAE,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;CAC1F;AAED;;;GAGG;AACH,eAAO,MAAM,OAAO,GAAI,CAAC,SAAS,UAAU,4EAOzC,WAAW,CAAC,CAAC,CAAC,KAAG,WAAW,CAAC,CAAC,CAwIhC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-form",
3
- "version": "0.7.3-staging.cc8dd3e",
3
+ "version": "0.7.3",
4
4
  "description": "Data components.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -29,20 +29,20 @@
29
29
  "date-fns": "^3.3.1",
30
30
  "effect": "^3.9.2",
31
31
  "jsonpath": "^1.1.1",
32
- "@dxos/async": "0.7.3-staging.cc8dd3e",
33
- "@dxos/debug": "0.7.3-staging.cc8dd3e",
34
- "@dxos/effect": "0.7.3-staging.cc8dd3e",
35
- "@dxos/echo-db": "0.7.3-staging.cc8dd3e",
36
- "@dxos/echo-schema": "0.7.3-staging.cc8dd3e",
37
- "@dxos/invariant": "0.7.3-staging.cc8dd3e",
38
- "@dxos/keys": "0.7.3-staging.cc8dd3e",
39
- "@dxos/live-object": "0.7.3-staging.cc8dd3e",
40
- "@dxos/react-client": "0.7.3-staging.cc8dd3e",
41
- "@dxos/log": "0.7.3-staging.cc8dd3e",
42
- "@dxos/react-ui-list": "0.7.3-staging.cc8dd3e",
43
- "@dxos/react-ui-types": "0.7.3-staging.cc8dd3e",
44
- "@dxos/util": "0.7.3-staging.cc8dd3e",
45
- "@dxos/schema": "0.7.3-staging.cc8dd3e"
32
+ "@dxos/async": "0.7.3",
33
+ "@dxos/debug": "0.7.3",
34
+ "@dxos/echo-db": "0.7.3",
35
+ "@dxos/echo-schema": "0.7.3",
36
+ "@dxos/invariant": "0.7.3",
37
+ "@dxos/effect": "0.7.3",
38
+ "@dxos/keys": "0.7.3",
39
+ "@dxos/live-object": "0.7.3",
40
+ "@dxos/react-client": "0.7.3",
41
+ "@dxos/react-ui-list": "0.7.3",
42
+ "@dxos/log": "0.7.3",
43
+ "@dxos/react-ui-types": "0.7.3",
44
+ "@dxos/schema": "0.7.3",
45
+ "@dxos/util": "0.7.3"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@phosphor-icons/react": "^2.1.5",
@@ -53,12 +53,12 @@
53
53
  "react": "~18.2.0",
54
54
  "react-dom": "~18.2.0",
55
55
  "vite": "5.4.7",
56
- "@dxos/echo-signals": "0.7.3-staging.cc8dd3e",
57
- "@dxos/random": "0.7.3-staging.cc8dd3e",
58
- "@dxos/react-ui": "0.7.3-staging.cc8dd3e",
59
- "@dxos/react-ui-syntax-highlighter": "0.7.3-staging.cc8dd3e",
60
- "@dxos/react-ui-theme": "0.7.3-staging.cc8dd3e",
61
- "@dxos/storybook-utils": "0.7.3-staging.cc8dd3e"
56
+ "@dxos/random": "0.7.3",
57
+ "@dxos/echo-signals": "0.7.3",
58
+ "@dxos/react-ui": "0.7.3",
59
+ "@dxos/react-ui-syntax-highlighter": "0.7.3",
60
+ "@dxos/react-ui-theme": "0.7.3",
61
+ "@dxos/storybook-utils": "0.7.3"
62
62
  },
63
63
  "peerDependencies": {
64
64
  "@effect/schema": "^0.75.5",
@@ -66,8 +66,8 @@
66
66
  "effect": "^3.9.2",
67
67
  "react": "~18.2.0",
68
68
  "react-dom": "~18.2.0",
69
- "@dxos/react-ui": "0.7.3-staging.cc8dd3e",
70
- "@dxos/react-ui-theme": "0.7.3-staging.cc8dd3e"
69
+ "@dxos/react-ui-theme": "0.7.3",
70
+ "@dxos/react-ui": "0.7.3"
71
71
  },
72
72
  "publishConfig": {
73
73
  "access": "public"
@@ -13,6 +13,7 @@ export const TextInput = <T extends BaseObject>({
13
13
  property,
14
14
  type,
15
15
  label,
16
+ inputOnly,
16
17
  disabled,
17
18
  placeholder,
18
19
  getStatus,
@@ -24,9 +25,11 @@ export const TextInput = <T extends BaseObject>({
24
25
 
25
26
  return (
26
27
  <Input.Root validationValence={status}>
27
- <InputHeader error={error}>
28
- <Input.Label>{label}</Input.Label>
29
- </InputHeader>
28
+ {!inputOnly && (
29
+ <InputHeader error={error}>
30
+ <Input.Label>{label}</Input.Label>
31
+ </InputHeader>
32
+ )}
30
33
  <Input.TextInput
31
34
  disabled={disabled}
32
35
  placeholder={placeholder}
@@ -34,6 +37,7 @@ export const TextInput = <T extends BaseObject>({
34
37
  onChange={(event) => onValueChange(property, type, event.target.value)}
35
38
  onBlur={onBlur}
36
39
  />
40
+ {inputOnly && <Input.Validation>{error}</Input.Validation>}
37
41
  </Input.Root>
38
42
  );
39
43
  };
@@ -42,6 +46,7 @@ export const NumberInput = <T extends BaseObject>({
42
46
  property,
43
47
  type,
44
48
  label,
49
+ inputOnly,
45
50
  disabled,
46
51
  placeholder,
47
52
  getStatus,
@@ -51,12 +56,13 @@ export const NumberInput = <T extends BaseObject>({
51
56
  }: InputProps<T>) => {
52
57
  const { status, error } = getStatus?.(property);
53
58
 
54
- // TODO(burdon): Only show stepper if bounded integer.
55
59
  return (
56
60
  <Input.Root validationValence={status}>
57
- <InputHeader error={error}>
58
- <Input.Label>{label}</Input.Label>
59
- </InputHeader>
61
+ {!inputOnly && (
62
+ <InputHeader error={error}>
63
+ <Input.Label>{label}</Input.Label>
64
+ </InputHeader>
65
+ )}
60
66
  <Input.TextInput
61
67
  type='number'
62
68
  disabled={disabled}
@@ -65,6 +71,7 @@ export const NumberInput = <T extends BaseObject>({
65
71
  onChange={(event) => onValueChange(property, type, event.target.value)}
66
72
  onBlur={onBlur}
67
73
  />
74
+ {inputOnly && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
68
75
  </Input.Root>
69
76
  );
70
77
  };
@@ -73,6 +80,7 @@ export const BooleanInput = <T extends BaseObject>({
73
80
  property,
74
81
  type,
75
82
  label,
83
+ inputOnly,
76
84
  getStatus,
77
85
  getValue,
78
86
  onValueChange,
@@ -81,13 +89,16 @@ export const BooleanInput = <T extends BaseObject>({
81
89
 
82
90
  return (
83
91
  <Input.Root validationValence={status}>
84
- <InputHeader error={error}>
85
- <Input.Label>{label}</Input.Label>
86
- </InputHeader>
92
+ {!inputOnly && (
93
+ <InputHeader error={error}>
94
+ <Input.Label>{label}</Input.Label>
95
+ </InputHeader>
96
+ )}
87
97
  <Input.Switch
88
98
  checked={getValue<boolean>(property)}
89
99
  onCheckedChange={(value) => onValueChange(property, type, value)}
90
100
  />
101
+ {inputOnly && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
91
102
  </Input.Root>
92
103
  );
93
104
  };
@@ -100,6 +111,7 @@ export const SelectInput = <T extends BaseObject>({
100
111
  property,
101
112
  type,
102
113
  label,
114
+ inputOnly,
103
115
  disabled,
104
116
  placeholder,
105
117
  options,
@@ -111,9 +123,11 @@ export const SelectInput = <T extends BaseObject>({
111
123
 
112
124
  return (
113
125
  <Input.Root validationValence={status}>
114
- <InputHeader error={error}>
115
- <Input.Label>{label}</Input.Label>
116
- </InputHeader>
126
+ {!inputOnly && (
127
+ <InputHeader error={error}>
128
+ <Input.Label>{label}</Input.Label>
129
+ </InputHeader>
130
+ )}
117
131
  <Select.Root value={getValue(property)} onValueChange={(value) => onValueChange(property, type, value)}>
118
132
  {/* TODO(burdon): Placeholder not working? */}
119
133
  <Select.TriggerButton classNames='is-full' disabled={disabled} placeholder={placeholder} />
@@ -130,6 +144,7 @@ export const SelectInput = <T extends BaseObject>({
130
144
  </Select.Content>
131
145
  </Select.Portal>
132
146
  </Select.Root>
147
+ {inputOnly && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
133
148
  </Input.Root>
134
149
  );
135
150
  };
@@ -16,20 +16,20 @@ import { Form, type FormProps } from './Form';
16
16
  import translations from '../../translations';
17
17
  import { TestLayout, TestPanel } from '../testing';
18
18
 
19
+ const AddressSchema = S.Struct({
20
+ street: S.optional(S.String.annotations({ [AST.TitleAnnotationId]: 'Street' })),
21
+ city: S.optional(S.String.annotations({ [AST.TitleAnnotationId]: 'City' })),
22
+ zip: S.optional(S.String.pipe(S.pattern(/^\d{5}(-\d{4})?$/)).annotations({ [AST.TitleAnnotationId]: 'ZIP' })),
23
+ location: S.optional(Format.GeoPoint.annotations({ [AST.TitleAnnotationId]: 'Location' })),
24
+ }).annotations({ [AST.TitleAnnotationId]: 'Address' });
25
+
19
26
  // TODO(burdon): Translations?
20
27
  const TestSchema = S.Struct({
21
28
  name: S.optional(S.String.annotations({ [AST.TitleAnnotationId]: 'Name' })),
22
29
  active: S.optional(S.Boolean.annotations({ [AST.TitleAnnotationId]: 'Active' })),
23
30
  rank: S.optional(S.Number.annotations({ [AST.TitleAnnotationId]: 'Rank' })),
24
31
  website: S.optional(Format.URL.annotations({ [AST.TitleAnnotationId]: 'Website' })),
25
- address: S.optional(
26
- S.Struct({
27
- street: S.optional(S.String.annotations({ [AST.TitleAnnotationId]: 'Street' })),
28
- city: S.optional(S.String.annotations({ [AST.TitleAnnotationId]: 'City' })),
29
- zip: S.optional(S.String.pipe(S.pattern(/^\d{5}(-\d{4})?$/)).annotations({ [AST.TitleAnnotationId]: 'ZIP' })),
30
- location: S.optional(Format.GeoPoint.annotations({ [AST.TitleAnnotationId]: 'Location' })),
31
- }).annotations({ [AST.TitleAnnotationId]: 'Address' }),
32
- ),
32
+ address: S.optional(AddressSchema),
33
33
  }).pipe(S.mutable);
34
34
 
35
35
  type TestType = S.Schema.Type<typeof TestSchema>;
@@ -83,8 +83,8 @@ export const Org: Story<Testing.OrgSchemaType> = {
83
83
  args: {
84
84
  schema: Testing.OrgSchema,
85
85
  values: {
86
- name: 'DXOS',
87
- website: 'https://dxos.org',
86
+ // name: 'DXOS',
87
+ // website: 'https://dxos.org',
88
88
  },
89
89
  },
90
90
  };
@@ -165,3 +165,35 @@ export const DiscriminatedShape: StoryObj<DiscriminatedUnionStoryProps> = {
165
165
  },
166
166
  },
167
167
  };
168
+
169
+ const ArraysSchema = S.Struct({
170
+ names: S.Array(S.String.pipe(S.nonEmptyString())),
171
+ addresses: S.Array(AddressSchema),
172
+ }).pipe(S.mutable);
173
+
174
+ type ArraysType = S.Schema.Type<typeof ArraysSchema>;
175
+
176
+ const ArraysStory = ({ values: initialValues }: FormProps<ArraysType>) => {
177
+ const [values, setValues] = useState(initialValues);
178
+ const handleSave = useCallback<NonNullable<FormProps<ArraysType>['onSave']>>((values) => {
179
+ setValues(values);
180
+ }, []);
181
+
182
+ return (
183
+ <TestLayout json={{ values, schema: ArraysSchema.ast.toJSON() }}>
184
+ <TestPanel>
185
+ <Form<ArraysType> schema={ArraysSchema} values={values} onSave={handleSave} />
186
+ </TestPanel>
187
+ </TestLayout>
188
+ );
189
+ };
190
+
191
+ export const Arrays: StoryObj<FormProps<ArraysType>> = {
192
+ render: ArraysStory,
193
+ args: {
194
+ values: {
195
+ names: ['Alice', 'Bob'],
196
+ addresses: [],
197
+ },
198
+ },
199
+ };
@@ -6,8 +6,8 @@ import { pipe } from 'effect';
6
6
  import { capitalize } from 'effect/String';
7
7
  import React, { useEffect, useMemo } from 'react';
8
8
 
9
- import { AST, type BaseObject, S, type PropertyKey } from '@dxos/echo-schema';
10
- import { findNode, getDiscriminatedType, isDiscriminatedUnion } from '@dxos/effect';
9
+ import { AST, type BaseObject, S, type PropertyKey, type FormatEnum } from '@dxos/echo-schema';
10
+ import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from '@dxos/effect';
11
11
  import { log } from '@dxos/log';
12
12
  import { IconButton, type ThemedClassName, useTranslation } from '@dxos/react-ui';
13
13
  import { mx } from '@dxos/react-ui-theme';
@@ -15,9 +15,9 @@ import { getSchemaProperties, type SchemaProperty } from '@dxos/schema';
15
15
  import { isNotFalsy } from '@dxos/util';
16
16
 
17
17
  import { SelectInput } from './Defaults';
18
- import { type InputComponent } from './Input';
18
+ import { InputHeader, type InputComponent } from './Input';
19
19
  import { getInputComponent } from './factory';
20
- import { type FormOptions, useForm } from '../../hooks';
20
+ import { type FormHandler, type FormOptions, useForm } from '../../hooks';
21
21
  import { translationKey } from '../../translations';
22
22
 
23
23
  // TODO(burdon): Rename package react-ui-form; delete react-ui-card.
@@ -71,7 +71,6 @@ export const Form = <T extends BaseObject>({
71
71
  onCancel,
72
72
  Custom,
73
73
  }: FormProps<T>) => {
74
- const { t } = useTranslation(translationKey);
75
74
  const onValid = useMemo(() => (autoSave ? onSave : undefined), [autoSave, onSave]);
76
75
  const { canSave, values, errors, handleSave, ...inputProps } = useForm<T>({
77
76
  schema,
@@ -106,12 +105,13 @@ export const Form = <T extends BaseObject>({
106
105
  <div role='form' className={mx('flex flex-col w-full gap-2 py-2', classNames)} data-testid={testId}>
107
106
  {properties
108
107
  .map((property) => {
109
- const { ast, name, type, format, title, description, examples, options } = property;
108
+ const { ast, name, type, format, title, description, examples, options, array } = property;
110
109
  const key = [...path, name];
111
110
  const label = title ?? pipe(name, capitalize);
112
111
  const placeholder = examples?.length ? `Example: "${examples[0]}"` : description;
113
112
 
114
113
  // Get generic input.
114
+ // TODO(ZaymonFC): We might need to switch this to using globs since we're now indexing into arrays.
115
115
  let InputComponent = Custom?.[key.join('.')];
116
116
  if (!InputComponent) {
117
117
  // Select.
@@ -133,6 +133,26 @@ export const Form = <T extends BaseObject>({
133
133
  }
134
134
 
135
135
  InputComponent = getInputComponent<T>(type, format);
136
+
137
+ // TODO(ZaymonFC): Array handling is inelegant. We need to push this complexity down into SDK/Schema.
138
+ if (array) {
139
+ return (
140
+ <ArrayField
141
+ key={name}
142
+ name={name}
143
+ type={type}
144
+ array={true}
145
+ label={label}
146
+ ast={ast}
147
+ values={values}
148
+ format={format}
149
+ readonly={readonly}
150
+ placeholder={placeholder}
151
+ inputProps={inputProps}
152
+ Custom={Custom}
153
+ />
154
+ );
155
+ }
136
156
  }
137
157
 
138
158
  if (!InputComponent) {
@@ -181,31 +201,187 @@ export const Form = <T extends BaseObject>({
181
201
  );
182
202
  })
183
203
  .filter(isNotFalsy)}
184
-
185
204
  {(onCancel || onSave) && !autoSave && (
186
- <div role='none' className='flex justify-center'>
187
- <div role='none' className={mx(onCancel && !readonly && 'grid grid-cols-2 gap-2')}>
188
- {onCancel && !readonly && (
189
- <IconButton
190
- data-testid='cancel-button'
191
- icon='ph--x--regular'
192
- label={t('button cancel')}
193
- onClick={onCancel}
194
- />
195
- )}
196
- {onSave && (
197
- <IconButton
198
- type='submit'
199
- data-testid='save-button'
200
- disabled={!canSave}
201
- icon='ph--check--regular'
202
- label={t('button save')}
203
- onClick={handleSave}
204
- />
205
- )}
205
+ <FormActions onCancel={onCancel} onSubmit={handleSave} canSubmit={canSave} readonly={readonly} />
206
+ )}
207
+ </div>
208
+ );
209
+ };
210
+
211
+ type ArrayFieldProps<T extends BaseObject> = {
212
+ name: PropertyKey<T>;
213
+ type: SimpleType;
214
+ array: true;
215
+ label: string;
216
+ ast: AST.AST;
217
+ values: Record<string, any>;
218
+ format?: FormatEnum;
219
+ readonly?: boolean;
220
+ placeholder?: string;
221
+ inputProps: Pick<FormHandler<T>, 'getStatus' | 'getValue' | 'onValueChange' | 'onBlur'>;
222
+ Custom?: Partial<Record<string, InputComponent<T>>>;
223
+ };
224
+
225
+ const ArrayField = <T extends BaseObject>({
226
+ name,
227
+ type,
228
+ label,
229
+ ast,
230
+ values,
231
+ format,
232
+ readonly,
233
+ placeholder,
234
+ inputProps,
235
+ Custom,
236
+ }: ArrayFieldProps<T>) => {
237
+ const { t } = useTranslation(translationKey);
238
+ const arrayValues = (values[name] ?? []) as any[];
239
+ const key = [name];
240
+
241
+ // TODO(ZaymonFC): Should this unwrapping happen at a lower level?
242
+ const tupleType = findNode(ast, AST.isTupleType);
243
+ const elementType = (tupleType as AST.TupleType | undefined)?.rest[0]?.type;
244
+
245
+ if (type === 'object' && elementType) {
246
+ const baseNode = findNode(elementType, isDiscriminatedUnion);
247
+ const typeLiteral = baseNode
248
+ ? getDiscriminatedType(baseNode, values[name] as any)
249
+ : findNode(elementType, AST.isTypeLiteral);
250
+
251
+ if (typeLiteral) {
252
+ const schema = S.make(typeLiteral);
253
+
254
+ return (
255
+ <div key={name} role='none' className={mx(padding)}>
256
+ <InputHeader>{label}</InputHeader>
257
+ <div role='none' className='flex flex-col gap-1'>
258
+ {arrayValues.map((value, index) => {
259
+ return (
260
+ <div key={index} role='none' className='flex items-center gap-1'>
261
+ <div role='none' className='flex-1'>
262
+ <Form<any>
263
+ schema={schema}
264
+ path={[...key, index.toString()]}
265
+ values={value}
266
+ onValuesChanged={(childValues) => {
267
+ const newValues = [...arrayValues];
268
+ newValues[index] = childValues;
269
+ inputProps.onValueChange(name, 'object', newValues);
270
+ }}
271
+ Custom={Custom as any}
272
+ />
273
+ </div>
274
+ <IconButton
275
+ icon='ph--trash--regular'
276
+ iconOnly
277
+ label={t('button remove')}
278
+ onClick={() => {
279
+ const newValues = arrayValues.filter((_, i) => i !== index);
280
+ inputProps.onValueChange(name, type, newValues);
281
+ }}
282
+ />
283
+ </div>
284
+ );
285
+ })}
286
+ </div>
287
+ <div role='none' className='flex justify-between items-center plb-1'>
288
+ <IconButton
289
+ icon='ph--plus--regular'
290
+ iconOnly
291
+ label={t('button add')}
292
+ onClick={() => {
293
+ const newValues = [...arrayValues, {}];
294
+ inputProps.onValueChange(name, type, newValues);
295
+ }}
296
+ />
206
297
  </div>
207
298
  </div>
208
- )}
299
+ );
300
+ }
301
+ return (
302
+ <div key={name} role='none'>
303
+ Nested form not supported in arrays, yet.
304
+ </div>
305
+ );
306
+ }
307
+
308
+ const InputComponent = getInputComponent<T>(type, format);
309
+ if (!InputComponent) {
310
+ return null;
311
+ }
312
+
313
+ return (
314
+ <div role='none' key='meta-input' className={mx(padding)}>
315
+ <InputHeader>{label}</InputHeader>
316
+ <div role='none' className='flex flex-col gap-1'>
317
+ {arrayValues.map((_, index) => (
318
+ <div key={index} role='none' className='flex items-start gap-1'>
319
+ <div role='none' className='flex-1'>
320
+ <InputComponent
321
+ type={type}
322
+ format={format}
323
+ label={label}
324
+ inputOnly
325
+ property={`${name}.${index}`}
326
+ disabled={readonly}
327
+ placeholder={placeholder}
328
+ {...inputProps}
329
+ />
330
+ </div>
331
+ <IconButton
332
+ icon='ph--trash--regular'
333
+ iconOnly
334
+ label={t('button remove')}
335
+ onClick={() => {
336
+ const newValues = arrayValues.filter((_, i) => i !== index);
337
+ inputProps.onValueChange(name, type, newValues);
338
+ }}
339
+ />
340
+ </div>
341
+ ))}
342
+ </div>
343
+ <div role='none' className='flex justify-between items-center plb-1'>
344
+ <IconButton
345
+ icon='ph--plus--regular'
346
+ iconOnly
347
+ label={t('button add')}
348
+ onClick={() => {
349
+ const newValues = [...arrayValues, SimpleType.getDefaultValue(type)];
350
+ inputProps.onValueChange(name, type, newValues);
351
+ }}
352
+ />
353
+ </div>
354
+ </div>
355
+ );
356
+ };
357
+
358
+ type FormActionsProps = {
359
+ readonly?: boolean;
360
+ canSubmit: boolean;
361
+ onCancel?: () => void;
362
+ onSubmit: () => void;
363
+ };
364
+
365
+ export const FormActions = ({ onCancel, onSubmit, canSubmit, readonly }: FormActionsProps) => {
366
+ const { t } = useTranslation(translationKey);
367
+
368
+ return (
369
+ <div role='none' className='flex justify-center'>
370
+ <div role='none' className={mx(onCancel && !readonly && 'grid grid-cols-2 gap-2')}>
371
+ {onCancel && !readonly && (
372
+ <IconButton data-testid='cancel-button' icon='ph--x--regular' label={t('button cancel')} onClick={onCancel} />
373
+ )}
374
+ {onSubmit && (
375
+ <IconButton
376
+ type='submit'
377
+ data-testid='save-button'
378
+ disabled={!canSubmit}
379
+ icon='ph--check--regular'
380
+ label={t('button save')}
381
+ onClick={onSubmit}
382
+ />
383
+ )}
384
+ </div>
209
385
  </div>
210
386
  );
211
387
  };
@@ -4,23 +4,24 @@
4
4
 
5
5
  import React, { type FC, type PropsWithChildren } from 'react';
6
6
 
7
- import { type BaseObject, type FormatEnum, type PropertyKey } from '@dxos/echo-schema';
7
+ import { type BaseObject, type FormatEnum } from '@dxos/echo-schema';
8
8
  import { type SimpleType } from '@dxos/effect';
9
9
  import { Icon, Tooltip } from '@dxos/react-ui';
10
10
  import { errorText } from '@dxos/react-ui-theme';
11
11
 
12
- import { type FormHandler } from '../../hooks';
12
+ import { type FormPath, type FormHandler } from '../../hooks';
13
13
 
14
14
  /**
15
15
  * Props passed to input components.
16
16
  */
17
17
  export type InputProps<T extends BaseObject> = {
18
- property: PropertyKey<T>;
18
+ property: FormPath<T>;
19
19
  type: SimpleType;
20
20
  format?: FormatEnum;
21
- label: string;
21
+ label?: string;
22
22
  disabled?: boolean;
23
23
  placeholder?: string;
24
+ inputOnly?: boolean;
24
25
  } & Pick<FormHandler<T>, 'getStatus' | 'getValue' | 'onValueChange' | 'onBlur'>;
25
26
 
26
27
  /**