Skip to content

Commit

Permalink
Implement EnumGroupEditor that is compatible with current tools
Browse files Browse the repository at this point in the history
  • Loading branch information
saskliutas committed Dec 20, 2024
1 parent 22c8d3b commit c52b778
Show file tree
Hide file tree
Showing 16 changed files with 313 additions and 69 deletions.
61 changes: 7 additions & 54 deletions apps/test-app/src/frontend/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,11 @@ import {
import { ThemeProvider as IUI2_ThemeProvider } from "@itwin/itwinui-react-v2";
import { useEngagementTime } from "./appui/useEngagementTime";
import { AppLocalizationProvider } from "./Localization";
import { EditorSpec, EditorsRegistryProvider } from "@itwin/components-react";
import {
ColorEditorSpec,
QuantityEditorSpec,
WeightEditorSpec,
} from "@itwin/imodel-components-react";
import { ButtonGroup, IconButton } from "@itwin/itwinui-react";
import { SvgPlaceholder } from "@itwin/itwinui-icons-react";
import {
EditorProps,
EditorSpec,
EditorsRegistryProvider,
NumericEditorSpec,
useEnumEditorProps,
} from "@itwin/components-react";

interface AppProps {
featureOverrides?: React.ComponentProps<
Expand All @@ -47,12 +38,10 @@ export function App({ featureOverrides }: AppProps) {
<AppPreviewFeatures featureOverrides={featureOverrides}>
<AppLocalizationProvider>
<EditorsRegistryProvider editors={rootEditors}>
<EditorsRegistryProvider editors={editors}>
<ConfigurableUiContent
appBackstage={<BackstageComposer />}
childWindow={ChildWindow}
/>
</EditorsRegistryProvider>
<ConfigurableUiContent
appBackstage={<BackstageComposer />}
childWindow={ChildWindow}
/>
</EditorsRegistryProvider>
</AppLocalizationProvider>
</AppPreviewFeatures>
Expand All @@ -67,41 +56,5 @@ function ChildWindow(props: React.PropsWithChildren<object>) {
return <IUI2_ThemeProvider>{props.children}</IUI2_ThemeProvider>;
}

const editors: EditorSpec[] = [
NumericEditorSpec,
WeightEditorSpec,
ColorEditorSpec,
];

const rootEditors: EditorSpec[] = [
{
applies: (metadata) =>
metadata.type === "enum" &&
metadata.preferredEditor === "enum-buttongroup",
Editor: CustomEnumEditor,
},
QuantityEditorSpec,
];

function CustomEnumEditor(props: EditorProps) {
const { value, onChange, choices, size, onFinish } =
useEnumEditorProps(props);
return (
<ButtonGroup orientation="horizontal" onBlur={onFinish}>
{choices.map((c) => (
<IconButton
key={c.value}
onClick={() => {
onChange({ choice: c.value, label: c.label });
onFinish();
}}
isActive={value.choice === c.value}
size={size}
label={c.label}
>
<SvgPlaceholder />
</IconButton>
))}
</ButtonGroup>
);
}
// add custom editors from `@itwin/imodel-components-react` to the registry
const rootEditors: EditorSpec[] = [WeightEditorSpec, ColorEditorSpec];
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ function EditorRenderer({
});
}}
onCancel={onCancel}
disabled={propertyRecord.isReadonly || propertyRecord.isDisabled}
size="small"
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export function Editor({
value,
onChange,
onFinish,
disabled,
size,
}: EditorProps) {
const TypeEditor = useEditor(metadata, value);
Expand All @@ -29,6 +30,7 @@ export function Editor({
onChange={onChange}
size={size}
onFinish={onFinish ?? noopOnFinish}
disabled={disabled}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { useBooleanEditorProps } from "./UseBooleanEditorProps.js";
*
*/
export function BooleanEditor(props: EditorProps) {
const { value, onChange, onFinish } = useBooleanEditorProps(props);
const { value, onChange, onFinish, size, disabled } =
useBooleanEditorProps(props);

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = { value: e.target.checked };
Expand All @@ -23,7 +24,8 @@ export function BooleanEditor(props: EditorProps) {
<ToggleSwitch
checked={value.value}
onChange={handleChange}
size={props.size === "small" ? "small" : undefined}
size={size === "small" ? "small" : undefined}
disabled={disabled}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { useDateTimeEditorProps } from "./UseDateTimeEditorProps.js";
*
*/
export function DateTimeEditor(props: EditorProps) {
const { value, onChange, onFinish } = useDateTimeEditorProps(props);
const { value, onChange, onFinish, size, disabled } =
useDateTimeEditorProps(props);
const dateStr = value.value.toLocaleDateString();

return (
Expand All @@ -31,7 +32,9 @@ export function DateTimeEditor(props: EditorProps) {
}
}}
>
<Button size={props.size}>{dateStr}</Button>
<Button size={size} disabled={disabled}>
{dateStr}
</Button>
</Popover>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { useEnumEditorProps } from "./UseEnumEditorProps.js";
*
*/
export function EnumEditor(props: EditorProps) {
const { value, onChange, onFinish, choices } = useEnumEditorProps(props);
const { value, onChange, onFinish, choices, disabled, size } =
useEnumEditorProps(props);

const handleChange = (newChoice: number | string) => {
const choice = choices.find((c) => c.value === newChoice);
Expand All @@ -22,10 +23,11 @@ export function EnumEditor(props: EditorProps) {

return (
<Select
size={props.size}
size={size}
value={value.choice}
onChange={handleChange}
options={choices.map((c) => ({ value: c.value, label: c.label }))}
disabled={disabled}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import { useNumericEditorProps } from "./UseNumericEditorProps.js";
*
*/
export function NumericEditor(props: EditorProps) {
const { value, onChange, onFinish } = useNumericEditorProps(props);
const { value, onChange, onFinish, size, disabled } =
useNumericEditorProps(props);

return (
<Input
type="number"
value={value.displayValue}
onChange={(e) =>
onChange({
Expand All @@ -23,7 +24,8 @@ export function NumericEditor(props: EditorProps) {
})
}
onBlur={onFinish}
size={props.size}
size={size}
disabled={disabled}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import { useTextEditorProps } from "./UseTextEditorProps.js";
*
*/
export function TextEditor(props: EditorProps) {
const { value, onChange, onFinish } = useTextEditorProps(props);
const { value, onChange, onFinish, size, disabled } =
useTextEditorProps(props);
return (
<Input
value={value.value}
onChange={(e) => onChange({ value: e.target.value })}
size={props.size}
onBlur={onFinish}
size={size}
disabled={disabled}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { DateTimeEditor } from "../editors/date/DateTimeEditor.js";
import { EnumEditor } from "../editors/enum/EnumEditor.js";
import { NumericEditor } from "../editors/numeric/NumericEditor.js";
import { TextEditor } from "../editors/text/TextEditor.js";
import { OldEnumEditorSpec } from "../interop/old-editors/enum/Enum.js";
import { EnumButtonGroupEditorSpec } from "../interop/old-editors/enum/EnumButtonGroup.js";

import type { EditorSpec } from "../Types.js";

export const TextEditorSpec: EditorSpec = {
Expand Down Expand Up @@ -41,3 +44,9 @@ export const defaultEditors: EditorSpec[] = [
DateEditorSpec,
EnumEditorSpec,
];

// editors that are rewritten based on the old version that accepts editor params from `PropertyRecord`
export const interopEditors: EditorSpec[] = [
EnumButtonGroupEditorSpec,
OldEnumEditorSpec,
];
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import type { EditorSpec } from "../Types.js";
import type { ValueMetadata } from "../values/Metadata.js";
import type { Value } from "../values/Values.js";
import { defaultEditors } from "./DefaultEditors.js";
import { defaultEditors, interopEditors } from "./DefaultEditors.js";
import { useEditorsRegistry } from "./EditorsRegistry.js";

/**
Expand All @@ -24,6 +24,13 @@ export function useEditor(
return registeredEditor;
}

const oldEditor = interopEditors.find((editor) =>
editor.applies(metadata, value)
)?.Editor;
if (oldEditor) {
return oldEditor;
}

const defaultEditor = defaultEditors.find((editor) =>
editor.applies(metadata, value)
)?.Editor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import type { PrimitiveValue, PropertyRecord } from "@itwin/appui-abstract";
import type {
Primitives,
PrimitiveValue,
PropertyRecord,
} from "@itwin/appui-abstract";
import { PropertyValueFormat } from "@itwin/appui-abstract";
import type {
BooleanValue,
DateValue,
EnumValue,
InstanceKeyValue,
Value as NewEditorValue,
NumericValue,
TextValue,
Expand Down Expand Up @@ -84,7 +89,11 @@ export namespace EditorInterop {
},
value: {
rawValue: primitiveValue.value as number,
displayValue: primitiveValue.displayValue ?? "",
displayValue:
primitiveValue.displayValue !== undefined &&
primitiveValue.displayValue !== ""
? `${parseFloat(primitiveValue.displayValue)}`
: `${(primitiveValue.value as number) ?? ""}`,
} satisfies NumericValue,
};
case "enum":
Expand All @@ -98,6 +107,17 @@ export namespace EditorInterop {
label: primitiveValue.displayValue as string,
} satisfies EnumValue,
};
case "navigation":
return {
metadata: {
...baseMetadata,
type: "instanceKey",
},
value: {
key: primitiveValue.value as Primitives.InstanceKey,
label: primitiveValue.displayValue ?? "",
} satisfies InstanceKeyValue,
};
}

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/

import * as React from "react";
import type { EditorProps, EditorSpec } from "../../../Types.js";
import { useEnumChoices } from "./UseEnumChoices.js";
import type { EnumValueMetadata } from "../../../values/Metadata.js";
import { EnumEditor as NewEnumEditor } from "../../../editors/enum/EnumEditor.js";
import { isOldEditorMetadata } from "../../Metadata.js";

export const EnumEditorSpec: EditorSpec = {
applies: (metadata) =>
isOldEditorMetadata(metadata) && metadata.type === "enum",
Editor: EnumEditor,
};

function EnumEditor(props: EditorProps) {
const choices = useEnumChoices(props.metadata);
const newMetadata = React.useMemo<EnumValueMetadata>(
() => ({
type: "enum" as const,
choices: choices.map(({ value, label }) => ({ value, label })),
}),
[choices]
);

return <NewEnumEditor {...props} metadata={newMetadata} />;
}
Loading

0 comments on commit c52b778

Please sign in to comment.