@dxos/react-ui-form 0.7.5-main.9d26e3a → 0.7.5-main.b19bfc8

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 (86) hide show
  1. package/dist/lib/browser/index.mjs +1020 -571
  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 +1046 -608
  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 +1020 -571
  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/FieldEditor/FieldEditor.d.ts +1 -2
  11. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  12. package/dist/types/src/components/Form/ArrayField.d.ts +15 -0
  13. package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -0
  14. package/dist/types/src/components/Form/Defaults.d.ts +6 -7
  15. package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
  16. package/dist/types/src/components/Form/Form.d.ts +13 -16
  17. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  18. package/dist/types/src/components/Form/Form.stories.d.ts +2 -1
  19. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  20. package/dist/types/src/components/Form/FormActions.d.ts +6 -0
  21. package/dist/types/src/components/Form/FormActions.d.ts.map +1 -0
  22. package/dist/types/src/components/Form/FormContent.d.ts +25 -0
  23. package/dist/types/src/components/Form/FormContent.d.ts.map +1 -0
  24. package/dist/types/src/components/Form/FormContext.d.ts +23 -0
  25. package/dist/types/src/components/Form/FormContext.d.ts.map +1 -0
  26. package/dist/types/src/components/Form/Input.d.ts +7 -8
  27. package/dist/types/src/components/Form/Input.d.ts.map +1 -1
  28. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +1 -3
  29. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
  30. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +3 -0
  31. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -0
  32. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +7 -0
  33. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -0
  34. package/dist/types/src/components/Form/custom/TupleInput.d.ts +2 -4
  35. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
  36. package/dist/types/src/components/Form/custom/index.d.ts +1 -0
  37. package/dist/types/src/components/Form/custom/index.d.ts.map +1 -1
  38. package/dist/types/src/components/Form/factory.d.ts +2 -2
  39. package/dist/types/src/components/Form/factory.d.ts.map +1 -1
  40. package/dist/types/src/components/Form/index.d.ts +2 -0
  41. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  42. package/dist/types/src/components/ViewEditor/Test.stories.d.ts +1 -2
  43. package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +1 -1
  44. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +2 -2
  45. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  46. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  47. package/dist/types/src/components/deprecated/Form.d.ts +1 -2
  48. package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
  49. package/dist/types/src/components/deprecated/Form.stories.d.ts +1 -2
  50. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
  51. package/dist/types/src/components/deprecated/FormInput.d.ts +6 -1
  52. package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -1
  53. package/dist/types/src/components/testing.d.ts +3 -3
  54. package/dist/types/src/components/testing.d.ts.map +1 -1
  55. package/dist/types/src/hooks/useForm.d.ts +11 -11
  56. package/dist/types/src/hooks/useForm.d.ts.map +1 -1
  57. package/dist/types/src/translations.d.ts +6 -0
  58. package/dist/types/src/translations.d.ts.map +1 -1
  59. package/dist/types/src/util/field.d.ts.map +1 -1
  60. package/dist/types/src/util/formatting.d.ts.map +1 -1
  61. package/dist/types/tsconfig.tsbuildinfo +1 -1
  62. package/package.json +29 -27
  63. package/src/components/FieldEditor/FieldEditor.stories.tsx +1 -1
  64. package/src/components/FieldEditor/FieldEditor.tsx +58 -40
  65. package/src/components/Form/ArrayField.tsx +111 -0
  66. package/src/components/Form/Defaults.tsx +78 -34
  67. package/src/components/Form/Form.stories.tsx +15 -5
  68. package/src/components/Form/Form.tsx +51 -327
  69. package/src/components/Form/FormActions.tsx +41 -0
  70. package/src/components/Form/FormContent.tsx +170 -0
  71. package/src/components/Form/FormContext.tsx +92 -0
  72. package/src/components/Form/Input.tsx +5 -6
  73. package/src/components/Form/custom/GeoPointInput.tsx +44 -23
  74. package/src/components/Form/custom/SelectOptionsInput.stories.tsx +58 -0
  75. package/src/components/Form/custom/SelectOptionsInput.tsx +190 -0
  76. package/src/components/Form/custom/TupleInput.tsx +5 -9
  77. package/src/components/Form/custom/index.ts +1 -0
  78. package/src/components/Form/factory.tsx +6 -7
  79. package/src/components/Form/index.ts +3 -0
  80. package/src/components/ViewEditor/ViewEditor.stories.tsx +25 -4
  81. package/src/components/ViewEditor/ViewEditor.tsx +16 -8
  82. package/src/components/deprecated/FormInput.tsx +20 -7
  83. package/src/hooks/useForm.ts +63 -49
  84. package/src/translations.ts +9 -1
  85. package/src/util/field.ts +19 -1
  86. package/src/util/formatting.ts +29 -1
@@ -1,9 +1,8 @@
1
1
  import '@dxos-theme';
2
2
  import { type Meta, type StoryObj } from '@storybook/react';
3
- import React from 'react';
4
3
  import { type TestType } from '@dxos/schema/testing';
5
4
  import { type DeprecatedFormProps } from './Form';
6
- declare const DefaultStory: (props: DeprecatedFormProps) => React.JSX.Element;
5
+ declare const DefaultStory: (props: DeprecatedFormProps) => import("react/jsx-runtime").JSX.Element;
7
6
  declare const meta: Meta<typeof DefaultStory>;
8
7
  export default meta;
9
8
  type Story = StoryObj<DeprecatedFormProps<TestType>>;
@@ -1 +1 @@
1
- {"version":3,"file":"Form.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/deprecated/Form.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAkC,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGrF,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAIlE,QAAA,MAAM,YAAY,UAAW,mBAAmB,sBAM/C,CAAC;AAEF,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,YAAY,CAQnC,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAErD,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAKnB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAOtB,CAAC"}
1
+ {"version":3,"file":"Form.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/deprecated/Form.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAI5D,OAAO,EAAkC,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGrF,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAIlE,QAAA,MAAM,YAAY,UAAW,mBAAmB,4CAM/C,CAAC;AAEF,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,YAAY,CAQnC,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAErD,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAKnB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAOtB,CAAC"}
@@ -1,4 +1,9 @@
1
1
  import { type JSX, type PropsWithChildren } from 'react';
2
+ import { type ThemedClassName } from '@dxos/react-ui';
3
+ /**
4
+ * @deprecated
5
+ */
6
+ export declare const DeprecatedFormContainer: ({ children, classNames }: ThemedClassName<PropsWithChildren>) => import("react/jsx-runtime").JSX.Element;
2
7
  export type DeprecatedFormInputProps = {
3
8
  label: string;
4
9
  description?: JSX.Element;
@@ -7,5 +12,5 @@ export type DeprecatedFormInputProps = {
7
12
  /**
8
13
  * @deprecated
9
14
  */
10
- export declare const DeprecatedFormInput: ({ label, description, secondary, children, }: PropsWithChildren<DeprecatedFormInputProps>) => JSX.Element;
15
+ export declare const DeprecatedFormInput: ({ label, description, secondary, children, }: PropsWithChildren<DeprecatedFormInputProps>) => import("react/jsx-runtime").JSX.Element;
11
16
  //# sourceMappingURL=FormInput.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FormInput.d.ts","sourceRoot":"","sources":["../../../../../src/components/deprecated/FormInput.tsx"],"names":[],"mappings":"AAIA,OAAc,EAAE,KAAK,GAAG,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAIhE,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;CACzB,CAAC;AAEF;;GAEG;AAEH,eAAO,MAAM,mBAAmB,iDAK7B,iBAAiB,CAAC,wBAAwB,CAAC,gBAkC7C,CAAC"}
1
+ {"version":3,"file":"FormInput.d.ts","sourceRoot":"","sources":["../../../../../src/components/deprecated/FormInput.tsx"],"names":[],"mappings":"AAIA,OAAc,EAAE,KAAK,GAAG,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEhE,OAAO,EAAS,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAG7D;;GAEG;AACH,eAAO,MAAM,uBAAuB,6BAA8B,eAAe,CAAC,iBAAiB,CAAC,4CAQnG,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,iDAK7B,iBAAiB,CAAC,wBAAwB,CAAC,4CAkC7C,CAAC"}
@@ -1,7 +1,7 @@
1
- import React, { type PropsWithChildren } from 'react';
1
+ import { type PropsWithChildren } from 'react';
2
2
  import { type ThemedClassName } from '@dxos/react-ui';
3
- export declare const TestPanel: ({ classNames, children }: ThemedClassName<PropsWithChildren>) => React.JSX.Element;
3
+ export declare const TestPanel: ({ classNames, children }: ThemedClassName<PropsWithChildren>) => import("react/jsx-runtime").JSX.Element;
4
4
  export declare const TestLayout: ({ classNames, json, children }: ThemedClassName<PropsWithChildren<{
5
5
  json?: any;
6
- }>>) => React.JSX.Element;
6
+ }>>) => import("react/jsx-runtime").JSX.Element;
7
7
  //# sourceMappingURL=testing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../../../src/components/testing.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAItD,eAAO,MAAM,SAAS,6BAA8B,eAAe,CAAC,iBAAiB,CAAC,sBAIrF,CAAC;AAEF,eAAO,MAAM,UAAU,mCAAoC,eAAe,CAAC,iBAAiB,CAAC;IAAE,IAAI,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC,sBAO5G,CAAC"}
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../../../src/components/testing.tsx"],"names":[],"mappings":"AAIA,OAAc,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAItD,eAAO,MAAM,SAAS,6BAA8B,eAAe,CAAC,iBAAiB,CAAC,4CAIrF,CAAC;AAEF,eAAO,MAAM,UAAU,mCAAoC,eAAe,CAAC,iBAAiB,CAAC;IAAE,IAAI,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC,4CAO5G,CAAC"}
@@ -1,6 +1,5 @@
1
- import { type FocusEvent } from 'react';
2
- import { type BaseObject, type PropertyKey } from '@dxos/echo-schema';
3
- import { type SimpleType, type S } from '@dxos/effect';
1
+ import { type BaseObject } from '@dxos/echo-schema';
2
+ import { type S, type SimpleType, type JsonPath } from '@dxos/effect';
4
3
  import { type ValidationError } from '@dxos/schema';
5
4
  import { type MaybePromise } from '@dxos/util';
6
5
  /**
@@ -8,25 +7,26 @@ import { type MaybePromise } from '@dxos/util';
8
7
  */
9
8
  export type FormHandler<T extends BaseObject> = {
10
9
  values: Partial<T>;
11
- errors: Record<PropertyKey<T>, string>;
12
- touched: Record<PropertyKey<T>, boolean>;
13
- changed: Record<PropertyKey<T>, boolean>;
10
+ errors: Record<JsonPath, string>;
11
+ touched: Record<JsonPath, boolean>;
12
+ changed: Record<JsonPath, boolean>;
14
13
  canSave: boolean;
14
+ formIsValid: boolean;
15
15
  handleSave: () => void;
16
- getStatus: (property: PropertyKey<T>) => {
16
+ getStatus: (path: string | (string | number)[]) => {
17
17
  status?: 'error';
18
18
  error?: string;
19
19
  };
20
- getValue: <V>(property: PropertyKey<T>) => V | undefined;
21
- onValueChange: <V>(property: PropertyKey<T>, type: SimpleType, value: V) => void;
22
- onBlur: (event: FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
20
+ getValue: <V>(path: (string | number)[]) => V | undefined;
21
+ onValueChange: <V>(path: (string | number)[], type: SimpleType, value: V) => void;
22
+ onTouched: (path: (string | number)[]) => void;
23
23
  };
24
24
  /**
25
25
  * Hook options.
26
26
  */
27
27
  export interface FormOptions<T extends BaseObject> {
28
28
  /**
29
- * Effect schema.
29
+ * Effect schema (Type literal).
30
30
  */
31
31
  schema: S.Schema<T>;
32
32
  /**
@@ -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,EAAsB,MAAM,mBAAmB,CAAC;AAC1F,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,CAAC,EAAiB,MAAM,cAAc,CAAC;AAGtE,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,OAAO,CAAC,CAAC,CAAC,CAAC;IACnB,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,GAAG,SAAS,CAAC;IACzD,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;IACH,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAE1B;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAE/C;;;;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,CAuIhC,CAAC"}
1
+ {"version":3,"file":"useForm.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useForm.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,UAAU,EAAsB,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAO,KAAK,CAAC,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAA4C,MAAM,cAAc,CAAC;AAGrH,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,OAAO,CAAC,CAAC,CAAC,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IAMvB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,KAAK;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxF,QAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;IAC1D,aAAa,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAClF,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,UAAU;IAC/C;;OAEG;IACH,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAEpB;;OAEG;IACH,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAE1B;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAE/C;;;;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,CAmJhC,CAAC"}
@@ -30,6 +30,7 @@ declare const _default: {
30
30
  'format ref': string;
31
31
  'format regex': string;
32
32
  'format string': string;
33
+ 'format single-select': string;
33
34
  'format text': string;
34
35
  'format time': string;
35
36
  'format timestamp': string;
@@ -39,6 +40,11 @@ declare const _default: {
39
40
  'format uuid': string;
40
41
  'placeholder latitude': string;
41
42
  'placeholder longitude': string;
43
+ 'select option label': string;
44
+ 'select option label placeholder': string;
45
+ 'select option color': string;
46
+ 'select option delete': string;
47
+ 'select option add': string;
42
48
  };
43
49
  };
44
50
  }[];
@@ -1 +1 @@
1
- {"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE9C,wBAoDE"}
1
+ {"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE9C,wBA4DE"}
@@ -1 +1 @@
1
- {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../../../src/util/field.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,KAAK,EAAE,GAAG,CAAC;CACZ,CAAC;AAEF,eAAO,MAAM,UAAU,4BAA6B,UAAU,QAoE7D,CAAC;AAGF,MAAM,MAAM,4BAA4B,GAAG;IACzC,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,uBAAuB,qBAAsB,4BAA4B,KAAG,MAAM,EAAE,GAAG,SAiCnG,CAAC;AAEF,eAAO,MAAM,0BAA0B,aAAc,OAAO,yBAAsD,CAAC;AAGnH,eAAO,MAAM,kBAAkB,SAAU,MAAM,KAAG,eAGhD,CAAC"}
1
+ {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../../../src/util/field.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAe,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,KAAK,EAAE,GAAG,CAAC;CACZ,CAAC;AAEF,eAAO,MAAM,UAAU,4BAA6B,UAAU,QAsF7D,CAAC;AAGF,MAAM,MAAM,4BAA4B,GAAG;IACzC,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,uBAAuB,qBAAsB,4BAA4B,KAAG,MAAM,EAAE,GAAG,SAiCnG,CAAC;AAEF,eAAO,MAAM,0BAA0B,aAAc,OAAO,yBAAsD,CAAC;AAGnH,eAAO,MAAM,kBAAkB,SAAU,MAAM,KAAG,eAGhD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"formatting.d.ts","sourceRoot":"","sources":["../../../../src/util/formatting.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAEzD,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,KAAK,EAAE,GAAG,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,CAAC;AAEF;;;GAGG;AAGH,eAAO,MAAM,gBAAgB,oCAAiD,gBAAgB,KAAG,MA+DhG,CAAC;AAEF,eAAO,MAAM,gBAAgB,oCAAiD,gBAAgB,KAAG,MA+ChG,CAAC"}
1
+ {"version":3,"file":"formatting.d.ts","sourceRoot":"","sources":["../../../../src/util/formatting.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAe,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAEtE,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,KAAK,EAAE,GAAG,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,CAAC;AAEF;;;GAGG;AAGH,eAAO,MAAM,gBAAgB,oCAAiD,gBAAgB,KAAG,MA4EhG,CAAC;AAEF,eAAO,MAAM,gBAAgB,oCAAiD,gBAAgB,KAAG,MA8DhG,CAAC"}
@@ -1 +1 @@
1
- {"version":"5.7.2"}
1
+ {"version":"5.7.3"}
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-form",
3
- "version": "0.7.5-main.9d26e3a",
3
+ "version": "0.7.5-main.b19bfc8",
4
4
  "description": "Data components.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
7
  "license": "MIT",
8
8
  "author": "DXOS.org",
9
+ "type": "module",
9
10
  "exports": {
10
11
  ".": {
11
12
  "types": "./dist/types/src/index.d.ts",
@@ -25,49 +26,50 @@
25
26
  "@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
26
27
  "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3",
27
28
  "@effect/schema": "^0.75.5",
28
- "@radix-ui/react-context": "^1.0.0",
29
+ "@radix-ui/react-context": "1.1.1",
29
30
  "date-fns": "^3.3.1",
30
- "effect": "^3.12.1",
31
31
  "jsonpath": "^1.1.1",
32
- "@dxos/debug": "0.7.5-main.9d26e3a",
33
- "@dxos/echo-db": "0.7.5-main.9d26e3a",
34
- "@dxos/echo-schema": "0.7.5-main.9d26e3a",
35
- "@dxos/effect": "0.7.5-main.9d26e3a",
36
- "@dxos/async": "0.7.5-main.9d26e3a",
37
- "@dxos/keys": "0.7.5-main.9d26e3a",
38
- "@dxos/invariant": "0.7.5-main.9d26e3a",
39
- "@dxos/log": "0.7.5-main.9d26e3a",
40
- "@dxos/live-object": "0.7.5-main.9d26e3a",
41
- "@dxos/react-client": "0.7.5-main.9d26e3a",
42
- "@dxos/react-ui-list": "0.7.5-main.9d26e3a",
43
- "@dxos/react-ui-types": "0.7.5-main.9d26e3a",
44
- "@dxos/schema": "0.7.5-main.9d26e3a",
45
- "@dxos/util": "0.7.5-main.9d26e3a"
32
+ "@dxos/async": "0.7.5-main.b19bfc8",
33
+ "@dxos/echo-schema": "0.7.5-main.b19bfc8",
34
+ "@dxos/effect": "0.7.5-main.b19bfc8",
35
+ "@dxos/echo-db": "0.7.5-main.b19bfc8",
36
+ "@dxos/invariant": "0.7.5-main.b19bfc8",
37
+ "@dxos/log": "0.7.5-main.b19bfc8",
38
+ "@dxos/keys": "0.7.5-main.b19bfc8",
39
+ "@dxos/live-object": "0.7.5-main.b19bfc8",
40
+ "@dxos/debug": "0.7.5-main.b19bfc8",
41
+ "@dxos/react-client": "0.7.5-main.b19bfc8",
42
+ "@dxos/react-ui-list": "0.7.5-main.b19bfc8",
43
+ "@dxos/react-ui-pickers": "0.7.5-main.b19bfc8",
44
+ "@dxos/react-ui-types": "0.7.5-main.b19bfc8",
45
+ "@dxos/schema": "0.7.5-main.b19bfc8",
46
+ "@dxos/util": "0.7.5-main.b19bfc8"
46
47
  },
47
48
  "devDependencies": {
48
49
  "@phosphor-icons/react": "^2.1.5",
49
- "@types/jsonpath": "~0.2.4",
50
+ "@types/jsonpath": "^0.2.4",
50
51
  "@types/react": "~18.2.0",
51
52
  "@types/react-dom": "~18.2.0",
53
+ "effect": "^3.12.3",
52
54
  "fast-check": "^3.19.0",
53
55
  "react": "~18.2.0",
54
56
  "react-dom": "~18.2.0",
55
57
  "vite": "5.4.7",
56
- "@dxos/react-ui": "0.7.5-main.9d26e3a",
57
- "@dxos/echo-signals": "0.7.5-main.9d26e3a",
58
- "@dxos/random": "0.7.5-main.9d26e3a",
59
- "@dxos/react-ui-syntax-highlighter": "0.7.5-main.9d26e3a",
60
- "@dxos/storybook-utils": "0.7.5-main.9d26e3a",
61
- "@dxos/react-ui-theme": "0.7.5-main.9d26e3a"
58
+ "@dxos/echo-signals": "0.7.5-main.b19bfc8",
59
+ "@dxos/random": "0.7.5-main.b19bfc8",
60
+ "@dxos/react-ui-syntax-highlighter": "0.7.5-main.b19bfc8",
61
+ "@dxos/react-ui-theme": "0.7.5-main.b19bfc8",
62
+ "@dxos/react-ui": "0.7.5-main.b19bfc8",
63
+ "@dxos/storybook-utils": "0.7.5-main.b19bfc8"
62
64
  },
63
65
  "peerDependencies": {
64
66
  "@effect/schema": "^0.75.5",
65
67
  "@phosphor-icons/react": "^2.1.5",
66
- "effect": "^3.9.2",
68
+ "effect": "^3.12.3",
67
69
  "react": "~18.2.0",
68
70
  "react-dom": "~18.2.0",
69
- "@dxos/react-ui": "0.7.5-main.9d26e3a",
70
- "@dxos/react-ui-theme": "0.7.5-main.9d26e3a"
71
+ "@dxos/react-ui": "0.7.5-main.b19bfc8",
72
+ "@dxos/react-ui-theme": "0.7.5-main.b19bfc8"
71
73
  },
72
74
  "publishConfig": {
73
75
  "access": "public"
@@ -37,7 +37,7 @@ const meta: Meta<StoryProps> = {
37
37
  title: 'ui/react-ui-form/FieldEditor',
38
38
  component: FieldEditor,
39
39
  render: DefaultStory,
40
- decorators: [withLayout({ fullscreen: true }), withTheme],
40
+ decorators: [withLayout({ fullscreen: true, tooltips: true }), withTheme],
41
41
  parameters: {
42
42
  translations,
43
43
  },
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { useCallback, useEffect, useState } from 'react';
5
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
6
6
 
7
7
  import { type SchemaRegistry } from '@dxos/echo-db';
8
8
  import { FormatEnum, FormatEnums, formatToType, type EchoSchema } from '@dxos/echo-schema';
@@ -16,10 +16,11 @@ import {
16
16
  type PropertyType,
17
17
  type ViewType,
18
18
  type ViewProjection,
19
+ type SchemaProperty,
19
20
  } from '@dxos/schema';
20
21
 
21
22
  import { translationKey } from '../../translations';
22
- import { Form, type FormProps, SelectInput } from '../Form';
23
+ import { Form, type FormProps, type InputComponent, SelectInput, SelectOptionInput } from '../Form';
23
24
 
24
25
  export type FieldEditorProps = {
25
26
  view: ViewType;
@@ -57,9 +58,9 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
57
58
  };
58
59
  }, [registry]);
59
60
 
60
- const [schema, setSchema] = useState<EchoSchema>();
61
+ const [referenceSchema, setReferenceSchema] = useState<EchoSchema>();
61
62
  useEffect(() => {
62
- setSchema(schemas.find((schema) => schema.typename === props?.referenceSchema));
63
+ setReferenceSchema(schemas.find((schema) => schema.typename === props?.referenceSchema));
63
64
  }, [schemas, props?.referenceSchema]);
64
65
 
65
66
  // TODO(burdon): Need to wrap otherwise throws error:
@@ -78,9 +79,15 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
78
79
  }
79
80
  return { fieldSchema };
80
81
  });
81
- if (_props.referenceSchema !== props.referenceSchema) {
82
- setSchema(schemas.find((schema) => schema.typename === _props.referenceSchema));
83
- }
82
+ setReferenceSchema((prev) => {
83
+ if (_props.referenceSchema !== prev?.typename) {
84
+ const newSchema = schemas.find((schema) => schema.typename === _props.referenceSchema);
85
+ if (newSchema) {
86
+ return newSchema;
87
+ }
88
+ }
89
+ return prev;
90
+ });
84
91
 
85
92
  setProps((props) => {
86
93
  const type = formatToType[_props.format as keyof typeof formatToType];
@@ -88,7 +95,7 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
88
95
  return { ...props, ..._props, type };
89
96
  }
90
97
 
91
- return props;
98
+ return _props as PropertyType;
92
99
  });
93
100
  },
94
101
  [schemas, props.format, props.referenceSchema],
@@ -122,6 +129,47 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
122
129
  onSave();
123
130
  }, [onSave]);
124
131
 
132
+ const Custom: Partial<Record<string, InputComponent>> = useMemo(
133
+ () => ({
134
+ ['format' satisfies keyof PropertyType]: (props) => (
135
+ <SelectInput
136
+ {...props}
137
+ options={FormatEnums.filter((value) => value !== FormatEnum.None).map((value) => ({
138
+ value,
139
+ label: t(`format ${value}`),
140
+ }))}
141
+ />
142
+ ),
143
+ ['referenceSchema' satisfies keyof PropertyType]: (props) => (
144
+ <SelectInput
145
+ {...props}
146
+ options={schemas.map((schema) => ({
147
+ value: schema.typename,
148
+ }))}
149
+ />
150
+ ),
151
+ ['referencePath' satisfies keyof PropertyType]: (props) => (
152
+ <SelectInput
153
+ {...props}
154
+ options={
155
+ referenceSchema
156
+ ? getSchemaProperties(referenceSchema.ast)
157
+ .sort(sortProperties)
158
+ .map((p) => ({ value: p.name }))
159
+ : []
160
+ }
161
+ />
162
+ ),
163
+ ['options' satisfies keyof PropertyType]: (props) => <SelectOptionInput {...props} />,
164
+ }),
165
+ [t, schemas, referenceSchema],
166
+ );
167
+
168
+ const propIsNotType = useCallback(
169
+ (props: SchemaProperty<PropertyType>[]) => props.filter((p) => p.name !== 'type'),
170
+ [],
171
+ );
172
+
125
173
  if (!fieldSchema) {
126
174
  log.warn('invalid format', { props });
127
175
  return null;
@@ -133,43 +181,13 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
133
181
  autoFocus
134
182
  values={props}
135
183
  schema={fieldSchema}
136
- filter={(props) => props.filter((p) => p.name !== 'type')}
184
+ filter={propIsNotType}
137
185
  sort={['property', 'format']}
138
186
  onValuesChanged={handleValuesChanged}
139
187
  onValidate={handleValidate}
140
188
  onSave={handleSave}
141
189
  onCancel={handleCancel}
142
- Custom={{
143
- ['format' satisfies keyof PropertyType]: (props) => (
144
- <SelectInput<PropertyType>
145
- {...props}
146
- options={FormatEnums.filter((value) => value !== FormatEnum.None).map((value) => ({
147
- value,
148
- label: t(`format ${value}`),
149
- }))}
150
- />
151
- ),
152
- ['referenceSchema' satisfies keyof PropertyType]: (props) => (
153
- <SelectInput<PropertyType>
154
- {...props}
155
- options={schemas.map((schema) => ({
156
- value: schema.typename,
157
- }))}
158
- />
159
- ),
160
- ['referencePath' satisfies keyof PropertyType]: (props) => (
161
- <SelectInput<PropertyType>
162
- {...props}
163
- options={
164
- schema
165
- ? getSchemaProperties(schema.ast)
166
- .sort(sortProperties)
167
- .map((p) => ({ value: p.name }))
168
- : []
169
- }
170
- />
171
- ),
172
- }}
190
+ Custom={Custom}
173
191
  />
174
192
  );
175
193
  };
@@ -0,0 +1,111 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { pipe } from 'effect';
6
+ import { capitalize } from 'effect/String';
7
+ import React, { useCallback, useMemo } from 'react';
8
+
9
+ import { AST } from '@dxos/echo-schema';
10
+ import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from '@dxos/effect';
11
+ import { invariant } from '@dxos/invariant';
12
+ import { IconButton, useTranslation } from '@dxos/react-ui';
13
+ import { mx } from '@dxos/react-ui-theme';
14
+ import { getSchemaProperties, type SchemaProperty } from '@dxos/schema';
15
+
16
+ import { type ComponentLookup } from './Form';
17
+ import { FormField } from './FormContent';
18
+ import { useFormValues, type FormInputStateProps } from './FormContext';
19
+ import { InputHeader, type InputComponent } from './Input';
20
+ import { translationKey } from '../../translations';
21
+
22
+ const padding = 'px-2';
23
+
24
+ type ArrayFieldProps = {
25
+ property: SchemaProperty<any>;
26
+ readonly?: boolean;
27
+ inputProps: FormInputStateProps;
28
+ path?: (string | number)[];
29
+ Custom?: Partial<Record<string, InputComponent>>;
30
+ lookupComponent?: ComponentLookup;
31
+ };
32
+
33
+ export const ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponent }: ArrayFieldProps) => {
34
+ const { t } = useTranslation(translationKey);
35
+ const { ast, name, type, title } = property;
36
+ const values = useFormValues(path ?? []) as any[];
37
+ invariant(Array.isArray(values), `Values at path ${path?.join('.')} must be an array.`);
38
+ const label = title ?? pipe(name, capitalize);
39
+
40
+ const tupleType = findNode(ast, AST.isTupleType);
41
+ const elementType = (tupleType as AST.TupleType | undefined)?.rest[0]?.type;
42
+
43
+ const getDefaultObjectValue = (typeNode: AST.AST): any => {
44
+ const baseNode = findNode(typeNode, isDiscriminatedUnion);
45
+ const typeLiteral = baseNode ? getDiscriminatedType(baseNode, {}) : findNode(typeNode, AST.isTypeLiteral);
46
+ if (!typeLiteral) {
47
+ return {};
48
+ }
49
+
50
+ return Object.fromEntries(getSchemaProperties(typeLiteral, {}).map((prop) => [prop.name, prop.defaultValue]));
51
+ };
52
+
53
+ const newValue = useMemo(() => {
54
+ if (type === 'object' && elementType) {
55
+ return getDefaultObjectValue(elementType);
56
+ } else {
57
+ return SimpleType.getDefaultValue(type);
58
+ }
59
+ }, [type, elementType]);
60
+
61
+ const handleAdd = useCallback(() => {
62
+ inputProps.onValueChange(type, [...values, newValue]);
63
+ }, [type, elementType, inputProps, newValue, values]);
64
+
65
+ const handleRemove = useCallback(
66
+ (index: number) => {
67
+ const newValues = values.filter((_, i) => i !== index);
68
+ inputProps.onValueChange(type, newValues);
69
+ },
70
+ [type, inputProps, values],
71
+ );
72
+
73
+ if (!elementType) {
74
+ return null;
75
+ }
76
+
77
+ return (
78
+ <div role='none' className={mx(padding)}>
79
+ <InputHeader>{label}</InputHeader>
80
+ <div role='none' className='flex flex-col gap-1'>
81
+ {values.map((_value, index) => (
82
+ <div key={index} role='none' className='flex items-center gap-1'>
83
+ <div role='none' className='flex-1'>
84
+ <FormField
85
+ property={{
86
+ ...property,
87
+ array: false, // NOTE(ZaymonFC): This breaks arrays of arrays but ¯\_(ツ)_/¯. Ping me if you need that.
88
+ ast: elementType,
89
+ }}
90
+ path={[...(path ?? []), index]}
91
+ readonly={readonly}
92
+ inline
93
+ Custom={Custom}
94
+ lookupComponent={lookupComponent}
95
+ />
96
+ </div>
97
+ <IconButton
98
+ icon='ph--trash--regular'
99
+ iconOnly
100
+ label={t('button remove')}
101
+ onClick={() => handleRemove(index)}
102
+ />
103
+ </div>
104
+ ))}
105
+ </div>
106
+ <div role='none' className='flex justify-between items-center plb-1'>
107
+ <IconButton icon='ph--plus--regular' iconOnly label={t('button add')} onClick={handleAdd} />
108
+ </div>
109
+ </div>
110
+ );
111
+ };