@dxos/react-ui-form 0.8.4-main.3c1ae3b → 0.8.4-main.3eb6e50203
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.
- package/dist/lib/browser/index.mjs +1610 -1825
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +1610 -1825
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +3 -2
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +2 -2
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form-old.stories.d.ts +1 -42
- package/dist/types/src/components/Form/Form-old.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +9 -6
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +32 -32
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField.d.ts +2 -2
- package/dist/types/src/components/Form/FormField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormFieldSet.d.ts +3 -3
- package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/RefField.d.ts +8 -12
- package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +14 -9
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
- package/dist/types/src/components/Settings/Settings.d.ts +56 -0
- package/dist/types/src/components/Settings/Settings.d.ts.map +1 -0
- package/dist/types/src/components/{ControlSection/ControlSection.stories.d.ts → Settings/Settings.stories.d.ts} +3 -9
- package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -0
- package/dist/types/src/components/Settings/index.d.ts +2 -0
- package/dist/types/src/components/Settings/index.d.ts.map +1 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +0 -2
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -2
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/echo.d.ts +6 -0
- package/dist/types/src/util/echo.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +1 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +49 -45
- package/src/components/FieldEditor/FieldEditor.stories.tsx +39 -18
- package/src/components/FieldEditor/FieldEditor.tsx +13 -6
- package/src/components/Form/Form-old.stories.tsx +42 -42
- package/src/components/Form/Form.stories.tsx +12 -17
- package/src/components/Form/Form.tsx +52 -27
- package/src/components/Form/FormField.tsx +23 -17
- package/src/components/Form/FormFieldComponent.stories.tsx +1 -1
- package/src/components/Form/FormFieldComponent.tsx +17 -16
- package/src/components/Form/FormFieldSet.tsx +8 -5
- package/src/components/Form/fields/ArrayField.tsx +11 -11
- package/src/components/Form/fields/RefField.tsx +82 -99
- package/src/components/Form/fields/SelectOptionField.stories.tsx +1 -1
- package/src/components/Form/fields/SelectOptionField.tsx +2 -2
- package/src/components/Form/fields/TupleField.tsx +1 -1
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +12 -5
- package/src/components/ObjectPicker/ObjectPicker.tsx +66 -57
- package/src/components/Settings/Settings.stories.tsx +56 -0
- package/src/components/Settings/Settings.tsx +234 -0
- package/src/components/{ControlSection → Settings}/index.ts +1 -1
- package/src/components/ViewEditor/ViewEditor.stories.tsx +18 -12
- package/src/components/ViewEditor/ViewEditor.test.tsx +1 -5
- package/src/components/ViewEditor/ViewEditor.tsx +77 -44
- package/src/components/index.ts +1 -1
- package/src/components/testing.tsx +3 -5
- package/src/hooks/index.ts +0 -2
- package/src/hooks/useFormHandler.ts +20 -6
- package/src/translations.ts +1 -1
- package/src/util/echo.ts +25 -0
- package/src/util/index.ts +1 -0
- package/dist/types/src/components/ControlSection/ControlSection.d.ts +0 -23
- package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +0 -1
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +0 -1
- package/dist/types/src/components/ControlSection/index.d.ts +0 -2
- package/dist/types/src/components/ControlSection/index.d.ts.map +0 -1
- package/dist/types/src/hooks/useQueryRefOptions.d.ts +0 -30
- package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +0 -1
- package/dist/types/src/hooks/useRefQueryOptions.d.ts +0 -8
- package/dist/types/src/hooks/useRefQueryOptions.d.ts.map +0 -1
- package/src/components/ControlSection/ControlSection.stories.tsx +0 -38
- package/src/components/ControlSection/ControlSection.tsx +0 -153
- package/src/hooks/useQueryRefOptions.ts +0 -70
- package/src/hooks/useRefQueryOptions.ts +0 -37
|
@@ -7,13 +7,22 @@ import type * as Schema from 'effect/Schema';
|
|
|
7
7
|
import type * as SchemaAST from 'effect/SchemaAST';
|
|
8
8
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import { type
|
|
10
|
+
import { Obj } from '@dxos/echo';
|
|
11
|
+
import { type AnyProperties } from '@dxos/echo/internal';
|
|
12
|
+
import {
|
|
13
|
+
type JsonPath,
|
|
14
|
+
createJsonPath,
|
|
15
|
+
fromEffectValidationPath,
|
|
16
|
+
getValue as getValue$,
|
|
17
|
+
setValue as setValue$,
|
|
18
|
+
} from '@dxos/effect';
|
|
12
19
|
import { log } from '@dxos/log';
|
|
13
20
|
import { useDefaultValue } from '@dxos/react-ui';
|
|
14
21
|
import { type ValidationError, validateSchema } from '@dxos/schema';
|
|
15
22
|
import { type MaybePromise } from '@dxos/util';
|
|
16
23
|
|
|
24
|
+
import { setValueEchoAware } from '../util';
|
|
25
|
+
|
|
17
26
|
export type FormFieldStatus = {
|
|
18
27
|
status?: 'error';
|
|
19
28
|
error?: string;
|
|
@@ -126,7 +135,7 @@ export const useFormHandler = <T extends AnyProperties>({
|
|
|
126
135
|
const [errors, setErrors] = useState<Record<JsonPath, string>>({});
|
|
127
136
|
const [saving, setSaving] = useState(false);
|
|
128
137
|
const defaultValues = useDefaultValue<Partial<T>>(defaultValuesProp, () => ({}));
|
|
129
|
-
const [values
|
|
138
|
+
const [values$, setValues] = useControllableState<Partial<T>>({
|
|
130
139
|
prop: valuesProp,
|
|
131
140
|
defaultProp: defaultValues,
|
|
132
141
|
onChange: () => {
|
|
@@ -137,6 +146,8 @@ export const useFormHandler = <T extends AnyProperties>({
|
|
|
137
146
|
setSaving(false);
|
|
138
147
|
},
|
|
139
148
|
});
|
|
149
|
+
// TODO(wittjosiah): Upgrade @radix-ui/react-use-controllable-state.
|
|
150
|
+
const values = values$ as Partial<T>;
|
|
140
151
|
|
|
141
152
|
// Validate.
|
|
142
153
|
const validate = useCallback(
|
|
@@ -259,12 +270,15 @@ export const useFormHandler = <T extends AnyProperties>({
|
|
|
259
270
|
}
|
|
260
271
|
|
|
261
272
|
// Update.
|
|
262
|
-
|
|
273
|
+
// For ECHO objects, mutate in place via Obj.change(); for plain objects, spread to create new reference.
|
|
274
|
+
const newValues = Obj.isObject(values)
|
|
275
|
+
? (setValueEchoAware(values, jsonPath, parsedValue), values)
|
|
276
|
+
: { ...setValue$(values, jsonPath, parsedValue) };
|
|
263
277
|
setValues(newValues);
|
|
264
278
|
|
|
265
279
|
// TODO(burdon): Check value has changed from original.
|
|
266
|
-
const newChanged = {
|
|
267
|
-
setChanged({ ...changed,
|
|
280
|
+
const newChanged = { [jsonPath]: true };
|
|
281
|
+
setChanged({ ...changed, ...newChanged });
|
|
268
282
|
|
|
269
283
|
// Validate.
|
|
270
284
|
const isValid = validate(newValues);
|
package/src/translations.ts
CHANGED
package/src/util/echo.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Obj } from '@dxos/echo';
|
|
6
|
+
import { type JsonPath, setValue as setValuePlain, splitJsonPath } from '@dxos/effect';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Sets a value on an object, using Obj.change() if it's an ECHO object.
|
|
10
|
+
*/
|
|
11
|
+
// TODO(wittjosiah): Consider refactoring Form to not know about ECHO at all.
|
|
12
|
+
// Instead of directly mutating the values object, Form could work with a plain
|
|
13
|
+
// object copy and communicate all mutations through callbacks (e.g., onValuesChanged).
|
|
14
|
+
// The caller would then be responsible for applying changes to ECHO objects via
|
|
15
|
+
// Obj.change(). This would keep Form agnostic to the underlying data layer and
|
|
16
|
+
// avoid coupling react-ui-form to @dxos/echo.
|
|
17
|
+
export const setValueEchoAware = <T extends object>(obj: T, path: JsonPath, value: any): T => {
|
|
18
|
+
if (Obj.isObject(obj)) {
|
|
19
|
+
Obj.change(obj, (mutableObj) => {
|
|
20
|
+
Obj.setValue(mutableObj, splitJsonPath(path), value);
|
|
21
|
+
});
|
|
22
|
+
return obj;
|
|
23
|
+
}
|
|
24
|
+
return setValuePlain(obj, path, value);
|
|
25
|
+
};
|
package/src/util/index.ts
CHANGED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import React, { type ComponentPropsWithoutRef, type PropsWithChildren } from 'react';
|
|
2
|
-
import { type ButtonProps, type Label, type ThemedClassName } from '@dxos/react-ui';
|
|
3
|
-
export type ControlPageProps = ThemedClassName<ComponentPropsWithoutRef<'div'>>;
|
|
4
|
-
export declare const ControlPage: ({ children, classNames, ...props }: ControlPageProps) => React.JSX.Element;
|
|
5
|
-
export type ControlSectionProps = PropsWithChildren<{
|
|
6
|
-
title: Label;
|
|
7
|
-
description?: Label;
|
|
8
|
-
}>;
|
|
9
|
-
export declare const ControlSection: ({ title, description, children }: ControlSectionProps) => React.JSX.Element;
|
|
10
|
-
export declare const ControlSectionHeading: ({ title, description }: Omit<ControlSectionProps, "children">) => React.JSX.Element;
|
|
11
|
-
export declare const ControlGroupButton: ({ classNames, ...props }: ButtonProps) => React.JSX.Element;
|
|
12
|
-
export type ControlGroupProps = ThemedClassName<PropsWithChildren>;
|
|
13
|
-
export declare const ControlGroup: ({ children, classNames }: ControlGroupProps) => React.JSX.Element;
|
|
14
|
-
export declare const ControlFrame: ({ children }: ControlGroupProps) => React.JSX.Element;
|
|
15
|
-
export declare const controlItemClasses: string;
|
|
16
|
-
export type ControlItemProps = PropsWithChildren<{
|
|
17
|
-
title: Label;
|
|
18
|
-
description?: Label;
|
|
19
|
-
}>;
|
|
20
|
-
export declare const ControlItem: ({ title, description, children }: ControlItemProps) => React.JSX.Element;
|
|
21
|
-
export declare const ControlItemInput: ({ title, description, children }: ControlItemProps) => React.JSX.Element;
|
|
22
|
-
export declare const ControlFrameItem: ({ title, description, children }: ControlItemProps) => React.JSX.Element;
|
|
23
|
-
//# sourceMappingURL=ControlSection.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ControlSection.d.ts","sourceRoot":"","sources":["../../../../../src/components/ControlSection/ControlSection.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,wBAAwB,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAErF,OAAO,EAEL,KAAK,WAAW,EAEhB,KAAK,KAAK,EACV,KAAK,eAAe,EAGrB,MAAM,gBAAgB,CAAC;AAMxB,MAAM,MAAM,gBAAgB,GAAG,eAAe,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC,CAAC;AAEhF,eAAO,MAAM,WAAW,GAAI,oCAAoC,gBAAgB,sBAQ/E,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;IAClD,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,CAAC,EAAE,KAAK,CAAC;CACrB,CAAC,CAAC;AAEH,eAAO,MAAM,cAAc,GAAI,kCAAkC,mBAAmB,sBAOnF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,wBAAwB,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,sBAYlG,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,0BAA0B,WAAW,sBAEvE,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,eAAe,CAAC,iBAAiB,CAAC,CAAC;AAEnE,eAAO,MAAM,YAAY,GAAI,0BAA0B,iBAAiB,sBAWvE,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,cAAc,iBAAiB,sBAU3D,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAK7B,CAAC;AAKH,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;IAC/C,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,CAAC,EAAE,KAAK,CAAC;CACrB,CAAC,CAAC;AAEH,eAAO,MAAM,WAAW,GAAI,kCAAkC,gBAAgB,sBAY7E,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,kCAAkC,gBAAgB,sBAoBlF,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,kCAAkC,gBAAgB,sBAUlF,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ControlSection.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/ControlSection/ControlSection.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAa6B,CAAC;AAExC,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAKrB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/ControlSection/index.ts"],"names":[],"mappings":"AAIA,cAAc,kBAAkB,CAAC"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { type DXN } from '@dxos/keys';
|
|
2
|
-
import { type Palette } from '@dxos/react-ui-types';
|
|
3
|
-
import { type MaybePromise } from '@dxos/util';
|
|
4
|
-
export type RefOption = {
|
|
5
|
-
dxn: DXN;
|
|
6
|
-
label?: string;
|
|
7
|
-
};
|
|
8
|
-
export type QueryRefOptions = ({ typename }: {
|
|
9
|
-
typename: string;
|
|
10
|
-
}) => MaybePromise<RefOption[]>;
|
|
11
|
-
export type QueryTag = {
|
|
12
|
-
id: string;
|
|
13
|
-
label: string;
|
|
14
|
-
hue?: Palette;
|
|
15
|
-
};
|
|
16
|
-
type UseQueryRefOptionsProps = {
|
|
17
|
-
typename?: string;
|
|
18
|
-
onQueryRefOptions?: QueryRefOptions;
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* Hook to query reference options based on type information.
|
|
22
|
-
* Used internally within forms to fetch and format reference options for reference fields.
|
|
23
|
-
*/
|
|
24
|
-
export declare const useQueryRefOptions: ({ typename, onQueryRefOptions }: UseQueryRefOptionsProps) => {
|
|
25
|
-
options: QueryTag[];
|
|
26
|
-
update: () => void;
|
|
27
|
-
loading: boolean;
|
|
28
|
-
};
|
|
29
|
-
export {};
|
|
30
|
-
//# sourceMappingURL=useQueryRefOptions.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useQueryRefOptions.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useQueryRefOptions.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,GAAG,EAAE,MAAM,YAAY,CAAC;AAGtC,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,GAAG,CAAC;IACT,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,KAAK,YAAY,CAAC,SAAS,EAAE,CAAC,CAAC;AAEhG,MAAM,MAAM,QAAQ,GAAG;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,KAAK,uBAAuB,GAAG;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,eAAe,CAAC;CACrC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,iCAAiC,uBAAuB;;;;CAmC1F,CAAC"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { type Space } from '@dxos/react-client/echo';
|
|
2
|
-
import { type QueryRefOptions } from './useQueryRefOptions';
|
|
3
|
-
type UseRefQueryOptionsProps = {
|
|
4
|
-
space?: Space;
|
|
5
|
-
};
|
|
6
|
-
export declare const useRefQueryOptions: ({ space }: UseRefQueryOptionsProps) => QueryRefOptions;
|
|
7
|
-
export {};
|
|
8
|
-
//# sourceMappingURL=useRefQueryOptions.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useRefQueryOptions.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useRefQueryOptions.ts"],"names":[],"mappings":"AAOA,OAAO,EAAU,KAAK,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAG7D,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,KAAK,uBAAuB,GAAG;IAAE,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAEjD,eAAO,MAAM,kBAAkB,GAAI,WAAW,uBAAuB,KAAG,eAsBvE,CAAC"}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
-
import React from 'react';
|
|
7
|
-
|
|
8
|
-
import { withTheme } from '@dxos/react-ui/testing';
|
|
9
|
-
|
|
10
|
-
import { translations } from '../../translations';
|
|
11
|
-
|
|
12
|
-
import { ControlPage, ControlSection } from './ControlSection';
|
|
13
|
-
|
|
14
|
-
const meta = {
|
|
15
|
-
title: 'ui/react-ui-form/ControlSection',
|
|
16
|
-
component: ControlSection,
|
|
17
|
-
render: (args) => (
|
|
18
|
-
<ControlPage classNames='is-[40rem]'>
|
|
19
|
-
<ControlSection {...args} />
|
|
20
|
-
</ControlPage>
|
|
21
|
-
),
|
|
22
|
-
decorators: [withTheme],
|
|
23
|
-
parameters: {
|
|
24
|
-
layout: 'centered',
|
|
25
|
-
translations,
|
|
26
|
-
},
|
|
27
|
-
} satisfies Meta<typeof ControlSection>;
|
|
28
|
-
|
|
29
|
-
export default meta;
|
|
30
|
-
|
|
31
|
-
type Story = StoryObj<typeof meta>;
|
|
32
|
-
|
|
33
|
-
export const Default: Story = {
|
|
34
|
-
args: {
|
|
35
|
-
title: 'Control Section',
|
|
36
|
-
description: 'This is a control section',
|
|
37
|
-
},
|
|
38
|
-
};
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import React, { type ComponentPropsWithoutRef, type PropsWithChildren } from 'react';
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
Button,
|
|
9
|
-
type ButtonProps,
|
|
10
|
-
Input,
|
|
11
|
-
type Label,
|
|
12
|
-
type ThemedClassName,
|
|
13
|
-
toLocalizedString,
|
|
14
|
-
useTranslation,
|
|
15
|
-
} from '@dxos/react-ui';
|
|
16
|
-
import { StackItem } from '@dxos/react-ui-stack';
|
|
17
|
-
import { mx } from '@dxos/react-ui-theme';
|
|
18
|
-
|
|
19
|
-
import { translationKey } from '../../translations';
|
|
20
|
-
|
|
21
|
-
export type ControlPageProps = ThemedClassName<ComponentPropsWithoutRef<'div'>>;
|
|
22
|
-
|
|
23
|
-
export const ControlPage = ({ children, classNames, ...props }: ControlPageProps) => {
|
|
24
|
-
return (
|
|
25
|
-
<StackItem.Content scrollable classNames='[--control-spacing:var(--dx-trimMd)]'>
|
|
26
|
-
<div role='none' className={mx('pli-cardSpacingInline pbe-trimLg', classNames)} {...props}>
|
|
27
|
-
{children}
|
|
28
|
-
</div>
|
|
29
|
-
</StackItem.Content>
|
|
30
|
-
);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export type ControlSectionProps = PropsWithChildren<{
|
|
34
|
-
title: Label;
|
|
35
|
-
description?: Label;
|
|
36
|
-
}>;
|
|
37
|
-
|
|
38
|
-
export const ControlSection = ({ title, description, children }: ControlSectionProps) => {
|
|
39
|
-
return (
|
|
40
|
-
<>
|
|
41
|
-
<ControlSectionHeading title={title} description={description} />
|
|
42
|
-
{children}
|
|
43
|
-
</>
|
|
44
|
-
);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export const ControlSectionHeading = ({ title, description }: Omit<ControlSectionProps, 'children'>) => {
|
|
48
|
-
const { t } = useTranslation(translationKey);
|
|
49
|
-
return (
|
|
50
|
-
<>
|
|
51
|
-
<h2 className='pli-trimMd container-max-width text-xl mbs-trimLg mbe-trimMd'>{toLocalizedString(title, t)}</h2>
|
|
52
|
-
{description && (
|
|
53
|
-
<p className='pli-trimMd mlb-trimMd container-max-width text-description'>
|
|
54
|
-
{toLocalizedString(description, t)}
|
|
55
|
-
</p>
|
|
56
|
-
)}
|
|
57
|
-
</>
|
|
58
|
-
);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export const ControlGroupButton = ({ classNames, ...props }: ButtonProps) => {
|
|
62
|
-
return <Button {...props} classNames={['md:col-span-2', classNames]} />;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export type ControlGroupProps = ThemedClassName<PropsWithChildren>;
|
|
66
|
-
|
|
67
|
-
export const ControlGroup = ({ children, classNames }: ControlGroupProps) => (
|
|
68
|
-
<div
|
|
69
|
-
role='none'
|
|
70
|
-
className={mx(
|
|
71
|
-
'group container-max-width grid grid-cols-1 md:grid-cols-[1fr_min-content] gap-trimMd',
|
|
72
|
-
'[--control-spacing:0px] [&_input]:justify-self-end [&_button]:justify-self-end',
|
|
73
|
-
classNames,
|
|
74
|
-
)}
|
|
75
|
-
>
|
|
76
|
-
{children}
|
|
77
|
-
</div>
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
export const ControlFrame = ({ children }: ControlGroupProps) => (
|
|
81
|
-
<div
|
|
82
|
-
role='none'
|
|
83
|
-
className={mx(
|
|
84
|
-
'container-max-width grid grid-cols-1 md:grid-cols-[1fr_1fr] gap-trimSm md:gap-trimMd p-trimMd',
|
|
85
|
-
'border border-separator rounded-md',
|
|
86
|
-
)}
|
|
87
|
-
>
|
|
88
|
-
{children}
|
|
89
|
-
</div>
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
export const controlItemClasses = mx([
|
|
93
|
-
'container-max-width grid md:col-span-2 grid-cols-subgrid gap-trimSm items-center',
|
|
94
|
-
// TODO(burdon): Use grid gap consistently or apply margins consistently?
|
|
95
|
-
'mlb-[--control-spacing] *:first:!mbs-0 *:last:!mbe-0 pli-trimMd plb-trimMd',
|
|
96
|
-
'border border-separator rounded-md',
|
|
97
|
-
]);
|
|
98
|
-
|
|
99
|
-
const controlItemTitleClasses = 'mbe-0 text-lg text-baseText font-normal';
|
|
100
|
-
const controlItemDescriptionClasses = 'mlb-trimSm md:mbe-0 text-base text-description';
|
|
101
|
-
|
|
102
|
-
export type ControlItemProps = PropsWithChildren<{
|
|
103
|
-
title: Label;
|
|
104
|
-
description?: Label;
|
|
105
|
-
}>;
|
|
106
|
-
|
|
107
|
-
export const ControlItem = ({ title, description, children }: ControlItemProps) => {
|
|
108
|
-
const { t } = useTranslation(translationKey);
|
|
109
|
-
|
|
110
|
-
return (
|
|
111
|
-
<div className={controlItemClasses}>
|
|
112
|
-
<div role='none'>
|
|
113
|
-
<h3 className={controlItemTitleClasses}>{toLocalizedString(title, t)}</h3>
|
|
114
|
-
{description && <p className={controlItemDescriptionClasses}>{toLocalizedString(description, t)}</p>}
|
|
115
|
-
</div>
|
|
116
|
-
{children}
|
|
117
|
-
</div>
|
|
118
|
-
);
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
export const ControlItemInput = ({ title, description, children }: ControlItemProps) => {
|
|
122
|
-
const { t } = useTranslation(translationKey);
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<Input.Root>
|
|
126
|
-
<div className={controlItemClasses}>
|
|
127
|
-
<div role='none'>
|
|
128
|
-
<Input.Label classNames={controlItemTitleClasses}>{toLocalizedString(title, t)}</Input.Label>
|
|
129
|
-
{description && (
|
|
130
|
-
<Input.DescriptionAndValidation>
|
|
131
|
-
<Input.Description classNames={controlItemDescriptionClasses}>
|
|
132
|
-
{toLocalizedString(description, t)}
|
|
133
|
-
</Input.Description>
|
|
134
|
-
</Input.DescriptionAndValidation>
|
|
135
|
-
)}
|
|
136
|
-
</div>
|
|
137
|
-
{children}
|
|
138
|
-
</div>
|
|
139
|
-
</Input.Root>
|
|
140
|
-
);
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
export const ControlFrameItem = ({ title, description, children }: ControlItemProps) => {
|
|
144
|
-
const { t } = useTranslation(translationKey);
|
|
145
|
-
|
|
146
|
-
return (
|
|
147
|
-
<div role='group' className='min-is-0'>
|
|
148
|
-
<h3 className='text-lg mbe-2'>{toLocalizedString(title, t)}</h3>
|
|
149
|
-
{description && <p className='mlb-trimSm md:mbe-0 text-description'>{toLocalizedString(description, t)}</p>}
|
|
150
|
-
{children}
|
|
151
|
-
</div>
|
|
152
|
-
);
|
|
153
|
-
};
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { useCallback, useState } from 'react';
|
|
6
|
-
|
|
7
|
-
import { type DXN } from '@dxos/keys';
|
|
8
|
-
import { log } from '@dxos/log';
|
|
9
|
-
import { useAsyncEffect } from '@dxos/react-ui';
|
|
10
|
-
import { type Palette } from '@dxos/react-ui-types';
|
|
11
|
-
import { type MaybePromise } from '@dxos/util';
|
|
12
|
-
|
|
13
|
-
export type RefOption = {
|
|
14
|
-
dxn: DXN;
|
|
15
|
-
label?: string;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type QueryRefOptions = ({ typename }: { typename: string }) => MaybePromise<RefOption[]>;
|
|
19
|
-
|
|
20
|
-
export type QueryTag = {
|
|
21
|
-
id: string;
|
|
22
|
-
label: string;
|
|
23
|
-
hue?: Palette;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
type UseQueryRefOptionsProps = {
|
|
27
|
-
typename?: string;
|
|
28
|
-
onQueryRefOptions?: QueryRefOptions;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Hook to query reference options based on type information.
|
|
33
|
-
* Used internally within forms to fetch and format reference options for reference fields.
|
|
34
|
-
*/
|
|
35
|
-
export const useQueryRefOptions = ({ typename, onQueryRefOptions }: UseQueryRefOptionsProps) => {
|
|
36
|
-
const [options, setOptions] = useState<QueryTag[]>([]);
|
|
37
|
-
const [loading, setLoading] = useState(false);
|
|
38
|
-
const [state, setState] = useState({});
|
|
39
|
-
const update = useCallback(() => setState({}), []);
|
|
40
|
-
|
|
41
|
-
useAsyncEffect(async () => {
|
|
42
|
-
if (!typename || !onQueryRefOptions) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
setLoading(true);
|
|
47
|
-
try {
|
|
48
|
-
// TODO(wittjosiah): This should be a reactive query so that the options are always up to date.
|
|
49
|
-
const options = await onQueryRefOptions({ typename });
|
|
50
|
-
log('options', { options: options.length });
|
|
51
|
-
setOptions(
|
|
52
|
-
options.map((option) => {
|
|
53
|
-
const dxn = option.dxn.toString() as string;
|
|
54
|
-
return {
|
|
55
|
-
id: dxn,
|
|
56
|
-
label: option.label ?? dxn,
|
|
57
|
-
hue: 'neutral' as any,
|
|
58
|
-
};
|
|
59
|
-
}),
|
|
60
|
-
);
|
|
61
|
-
} catch (error) {
|
|
62
|
-
log.error('Failed to fetch ref options:', error);
|
|
63
|
-
setOptions([]);
|
|
64
|
-
} finally {
|
|
65
|
-
setLoading(false);
|
|
66
|
-
}
|
|
67
|
-
}, [typename, onQueryRefOptions, state]);
|
|
68
|
-
|
|
69
|
-
return { options, update, loading };
|
|
70
|
-
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { useCallback } from 'react';
|
|
6
|
-
|
|
7
|
-
import { Obj } from '@dxos/echo';
|
|
8
|
-
import { Filter, type Space } from '@dxos/react-client/echo';
|
|
9
|
-
import { isNonNullable } from '@dxos/util';
|
|
10
|
-
|
|
11
|
-
import { type QueryRefOptions } from './useQueryRefOptions';
|
|
12
|
-
|
|
13
|
-
type UseRefQueryOptionsProps = { space?: Space };
|
|
14
|
-
|
|
15
|
-
export const useRefQueryOptions = ({ space }: UseRefQueryOptionsProps): QueryRefOptions => {
|
|
16
|
-
return useCallback<QueryRefOptions>(
|
|
17
|
-
async ({ typename }) => {
|
|
18
|
-
if (!space) {
|
|
19
|
-
return [];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const query = space.db.query(Filter.typename(typename));
|
|
23
|
-
const objects = await query.run();
|
|
24
|
-
return objects
|
|
25
|
-
.map((object) => {
|
|
26
|
-
const dxn = Obj.getDXN(object);
|
|
27
|
-
if (!dxn) {
|
|
28
|
-
return undefined;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return { dxn, label: Obj.getLabel(object) ?? object?.id ?? '' };
|
|
32
|
-
})
|
|
33
|
-
.filter(isNonNullable);
|
|
34
|
-
},
|
|
35
|
-
[space],
|
|
36
|
-
);
|
|
37
|
-
};
|