From 5005b787499dbbb20f17f69fde095d9b9241bdb5 Mon Sep 17 00:00:00 2001 From: Georgina Date: Wed, 25 Jun 2025 15:04:48 -0400 Subject: [PATCH 01/12] remove useDegrees from syncedslider, fix dropdown to properly handle undefined values, add stub for material in iv2 --- .../src/components/debug/debugPane.tsx | 62 +++++++++---------- ...oundPropertyLine.tsx => boundProperty.tsx} | 8 ++- .../materialTransparencyProperties.tsx | 33 ++++++++++ .../meshOutlineOverlayProperties.tsx | 3 + packages/dev/inspector-v2/src/inspector.tsx | 2 + .../properties/materialPropertiesService.tsx | 44 +++++++++++++ packages/dev/inspector-v2/test/app/index.ts | 4 +- .../src/fluent/hoc/vectorPropertyLine.tsx | 44 ++++++++----- .../src/fluent/primitives/dropdown.tsx | 28 ++++++--- .../src/fluent/primitives/syncedSlider.tsx | 26 ++------ .../src/lines/optionsLineComponent.tsx | 12 ++-- 11 files changed, 176 insertions(+), 90 deletions(-) rename packages/dev/inspector-v2/src/components/properties/{boundPropertyLine.tsx => boundProperty.tsx} (66%) create mode 100644 packages/dev/inspector-v2/src/components/properties/materialTransparencyProperties.tsx create mode 100644 packages/dev/inspector-v2/src/services/panes/properties/materialPropertiesService.tsx diff --git a/packages/dev/inspector-v2/src/components/debug/debugPane.tsx b/packages/dev/inspector-v2/src/components/debug/debugPane.tsx index d654e28f43d..74c4f012d6e 100644 --- a/packages/dev/inspector-v2/src/components/debug/debugPane.tsx +++ b/packages/dev/inspector-v2/src/components/debug/debugPane.tsx @@ -16,7 +16,7 @@ import { Texture } from "core/Materials/Textures/texture"; import { StandardMaterial } from "core/Materials/standardMaterial"; import type { Scene } from "core/scene"; import { MaterialFlags } from "core/Materials/materialFlags"; -import { BoundPropertyLine } from "../properties/boundPropertyLine"; +import { BoundProperty } from "../properties/boundProperty"; import { Accordion, AccordionSection } from "shared-ui-components/fluent/primitives/accordion"; const SwitchGrid = function (renderScene: Scene) { @@ -180,41 +180,35 @@ export const DebugPane: typeof AccordionPane = (props) => { /> - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/packages/dev/inspector-v2/src/components/properties/boundPropertyLine.tsx b/packages/dev/inspector-v2/src/components/properties/boundProperty.tsx similarity index 66% rename from packages/dev/inspector-v2/src/components/properties/boundPropertyLine.tsx rename to packages/dev/inspector-v2/src/components/properties/boundProperty.tsx index c036df68875..66b900f0bd9 100644 --- a/packages/dev/inspector-v2/src/components/properties/boundPropertyLine.tsx +++ b/packages/dev/inspector-v2/src/components/properties/boundProperty.tsx @@ -9,7 +9,13 @@ export type BoundPropertyProps = Omit(props: BoundPropertyProps) { +/** + * Intercepts the passed in component's target[propertyKey] with useInterceptObservable and sets component state using useObservableState. + * Renders the passed in component with value as the new observableState value and onChange as a callback to set the target[propertyKey] value. + * @param props + * @returns + */ +export function BoundProperty(props: BoundPropertyProps) { // eslint-disable-next-line @typescript-eslint/naming-convention const { target, propertyKey, component: Component, ...rest } = props; const value = useObservableState(() => target[propertyKey], useInterceptObservable("property", target, propertyKey)); diff --git a/packages/dev/inspector-v2/src/components/properties/materialTransparencyProperties.tsx b/packages/dev/inspector-v2/src/components/properties/materialTransparencyProperties.tsx new file mode 100644 index 00000000000..6fb4de34117 --- /dev/null +++ b/packages/dev/inspector-v2/src/components/properties/materialTransparencyProperties.tsx @@ -0,0 +1,33 @@ +// eslint-disable-next-line import/no-internal-modules +import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { Material } from "core/Materials/material"; + +import type { FunctionComponent } from "react"; + +import { DropdownPropertyLine } from "shared-ui-components/fluent/hoc/dropdownPropertyLine"; +import { BoundProperty } from "./boundProperty"; + +const TransparencyModeOptions = [ + { label: "Opaque", value: PBRMaterial.PBRMATERIAL_OPAQUE }, + { label: "Alpha test", value: PBRMaterial.PBRMATERIAL_ALPHATEST }, + { label: "Alpha blend", value: PBRMaterial.PBRMATERIAL_ALPHABLEND }, + { label: "Alpha blend and test", value: PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND }, +]; + +export const MaterialTransparencyProperties: FunctionComponent<{ material: Material }> = (props) => { + const { material } = props; + + return ( + <> + + + ); +}; diff --git a/packages/dev/inspector-v2/src/components/properties/meshOutlineOverlayProperties.tsx b/packages/dev/inspector-v2/src/components/properties/meshOutlineOverlayProperties.tsx index c321f9b28b6..d1d0c8df8fc 100644 --- a/packages/dev/inspector-v2/src/components/properties/meshOutlineOverlayProperties.tsx +++ b/packages/dev/inspector-v2/src/components/properties/meshOutlineOverlayProperties.tsx @@ -10,6 +10,9 @@ import { SwitchPropertyLine } from "shared-ui-components/fluent/hoc/switchProper import { useInterceptObservable } from "../../hooks/instrumentationHooks"; import { useObservableState } from "../../hooks/observableHooks"; +// Ensures that the outlineRenderer properties exist on the prototype of the Mesh +import "core/Rendering/outlineRenderer"; + type Color3Keys = { [P in keyof T]: T[P] extends Color3 ? P : never }[keyof T]; // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/packages/dev/inspector-v2/src/inspector.tsx b/packages/dev/inspector-v2/src/inspector.tsx index 38b36059d8d..9233d020afd 100644 --- a/packages/dev/inspector-v2/src/inspector.tsx +++ b/packages/dev/inspector-v2/src/inspector.tsx @@ -27,6 +27,7 @@ import { ToolsServiceDefinition } from "./services/panes/toolsService"; import { SceneContextIdentity } from "./services/sceneContext"; import { SelectionServiceDefinition } from "./services/selectionService"; import { ShellServiceIdentity } from "./services/shellService"; +import { MaterialPropertiesServiceDefinition } from "./services/panes/properties/materialPropertiesService"; let CurrentInspectorToken: Nullable = null; @@ -177,6 +178,7 @@ function _ShowInspector(scene: Nullable, options: Partial = { + friendlyName: "Material Properties", + consumes: [PropertiesServiceIdentity, SelectionServiceIdentity], + factory: (propertiesService) => { + // Transparency + const transparencySectionRegistration = propertiesService.addSection({ + order: 1, + identity: TransparencyPropertiesSectionIdentity, + }); + + const materialContentRegistration = propertiesService.addSectionContent({ + key: "Material Properties", + predicate: (entity: unknown): entity is Material => entity instanceof Material, + content: [ + // "Transparency" section. + { + section: TransparencyPropertiesSectionIdentity, + order: 0, + component: ({ context }) => , + }, + ], + }); + + return { + dispose: () => { + materialContentRegistration.dispose(); + transparencySectionRegistration.dispose(); + }, + }; + }, +}; diff --git a/packages/dev/inspector-v2/test/app/index.ts b/packages/dev/inspector-v2/test/app/index.ts index 3c507096ca0..57286bd7863 100644 --- a/packages/dev/inspector-v2/test/app/index.ts +++ b/packages/dev/inspector-v2/test/app/index.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line import/no-internal-modules -import type { ArcRotateCamera, Nullable } from "core/index"; +import { type ArcRotateCamera, type Nullable } from "core/index"; import { Engine } from "core/Engines/engine"; import { LoadAssetContainerAsync } from "core/Loading/sceneLoader"; @@ -9,8 +9,6 @@ import { registerBuiltInLoaders } from "loaders/dynamic"; import { ShowInspector } from "../../src/inspector"; import "core/Helpers/sceneHelpers"; -// For testing the Outline & Overlay section -import "core/Rendering/outlineRenderer"; // Register scene loader plugins. registerBuiltInLoaders(); diff --git a/packages/dev/sharedUiComponents/src/fluent/hoc/vectorPropertyLine.tsx b/packages/dev/sharedUiComponents/src/fluent/hoc/vectorPropertyLine.tsx index 365d8c4c4ae..67bc8acf145 100644 --- a/packages/dev/sharedUiComponents/src/fluent/hoc/vectorPropertyLine.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/hoc/vectorPropertyLine.tsx @@ -1,4 +1,4 @@ -import type { FunctionComponent } from "react"; +import { useState, type FunctionComponent } from "react"; import { Body1 } from "@fluentui/react-components"; import { PropertyLine } from "./propertyLine"; @@ -10,14 +10,13 @@ import { Vector4 } from "core/Maths/math.vector"; import type { Vector3 } from "core/Maths/math.vector"; import { Tools } from "core/Misc/tools"; -export type DegreesLineProps = { - /** - * Do we want to use angles with degrees instead of radians? - */ - useDegrees?: boolean; -}; - -export type VectorPropertyLineProps = BaseComponentProps & PropertyLineProps & DegreesLineProps; +export type VectorPropertyLineProps = BaseComponentProps & + PropertyLineProps & { + /** + * Display angles as degrees instead of radians + */ + useDegrees?: boolean; + }; /** * Reusable component which renders a vector property line containing a label, vector value, and expandable XYZW values @@ -39,14 +38,29 @@ const VectorPropertyLine: FunctionComponent = (props) => { - const { value: vector } = props; +const VectorSliders: FunctionComponent> = (props) => { + const [vector, setVector] = useState(props.value); + + const min = props.useDegrees ? 0 : undefined; + const max = props.useDegrees ? 360 : undefined; + + const onChange = (val: number, key: "x" | "y" | "z" | "w") => { + const value = props.useDegrees ? Tools.ToRadians(val) : val; + const newVector = vector.clone(); + (newVector as Vector4)[key] = value; // The syncedSlider for 'w' is only rendered when vector is a Vector4, so this is safe + + setVector(newVector); + props.onChange(newVector); + }; + + const converted = (val: number) => (props.useDegrees ? Tools.ToDegrees(val) : val); + return ( <> - (vector.x = value)} /> - (vector.y = value)} /> - (vector.z = value)} /> - {vector instanceof Vector4 && (vector.w = value)} />} + onChange(val, "x")} /> + onChange(val, "y")} /> + onChange(val, "z")} /> + {vector instanceof Vector4 && onChange(val, "w")} />} ); }; diff --git a/packages/dev/sharedUiComponents/src/fluent/primitives/dropdown.tsx b/packages/dev/sharedUiComponents/src/fluent/primitives/dropdown.tsx index 06f3c8f1172..ac92f9fd78e 100644 --- a/packages/dev/sharedUiComponents/src/fluent/primitives/dropdown.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/primitives/dropdown.tsx @@ -1,4 +1,5 @@ import { Dropdown as FluentDropdown, makeStyles, Option } from "@fluentui/react-components"; +import { useEffect, useState } from "react"; import type { FunctionComponent } from "react"; import type { BaseComponentProps } from "../hoc/propertyLine"; @@ -10,6 +11,7 @@ const useDropdownStyles = makeStyles({ optionsLine: {}, }); +export type AcceptedDropdownValue = string | number; export type DropdownOption = { /** * Defines the visible part of the option @@ -18,31 +20,43 @@ export type DropdownOption = { /** * Defines the value part of the option */ - value: string | number | null; + value: AcceptedDropdownValue; }; -export type DropdownProps = BaseComponentProps & { +export type DropdownProps = BaseComponentProps & { options: DropdownOption[]; + + includeUndefined?: boolean; // If true, adds an option with label 'Not Defined' and value undefined }; /** - * Renders a fluent UI dropdown with a calback for selection and a required default value + * Renders a fluent UI dropdown component for the options passed in, and an additional 'Not Defined' option if includeUndefined is set to true * @param props * @returns dropdown component */ export const Dropdown: FunctionComponent = (props) => { const classes = useDropdownStyles(); + const [options] = useState(props.includeUndefined ? [{ label: "", value: Number.MAX_SAFE_INTEGER }, ...props.options] : props.options); + const [defaultVal, setDefaultVal] = useState(props.includeUndefined && props.value === undefined ? Number.MAX_SAFE_INTEGER : props.value); + useEffect(() => { + setDefaultVal(props.includeUndefined && props.value === undefined ? Number.MAX_SAFE_INTEGER : props.value); + }, [props.value]); + return ( { - data.optionValue != undefined && props.onChange(props.options.find((o) => o.value?.toString() === data.optionValue) as DropdownOption); + let value = typeof props.value === "number" ? Number(data.optionValue) : data.optionValue; + setDefaultVal(value); + if (props.includeUndefined && value === Number.MAX_SAFE_INTEGER.toString()) { + value = undefined; + } + props.onChange(value); }} - defaultValue={props.value.label} - defaultSelectedOptions={[props.value.toString()]} + value={options.find((o) => o.value === defaultVal)?.label} > - {props.options.map((option: DropdownOption) => ( + {options.map((option: DropdownOption) => ( diff --git a/packages/dev/sharedUiComponents/src/fluent/primitives/syncedSlider.tsx b/packages/dev/sharedUiComponents/src/fluent/primitives/syncedSlider.tsx index 8973de90e24..2ed5c6508a8 100644 --- a/packages/dev/sharedUiComponents/src/fluent/primitives/syncedSlider.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/primitives/syncedSlider.tsx @@ -4,7 +4,6 @@ import { Input } from "./input"; import type { ChangeEvent, FunctionComponent } from "react"; import { useEffect, useState } from "react"; import type { BaseComponentProps } from "../hoc/propertyLine"; -import { Tools } from "core/Misc/tools"; const useSyncedSliderStyles = makeStyles({ syncedSlider: { @@ -27,7 +26,6 @@ export type SyncedSliderProps = BaseComponentProps & { min?: number; max?: number; step?: number; - useDegrees?: boolean; // Optional prop to use degrees instead of radians }; /** @@ -44,39 +42,23 @@ export const SyncedSliderInput: FunctionComponent = (props) = }, [props.value]); const handleSliderChange = (_: ChangeEvent, data: SliderOnChangeData) => { - let value = data.value; - if (props.useDegrees) { - // Convert degrees to radians if necessary - value = Tools.ToRadians(value); - } + const value = data.value; setValue(value); props.onChange(value); // Notify parent }; const handleInputChange = (value: string | number) => { - let newValue = Number(value); + const newValue = Number(value); if (!isNaN(newValue)) { - if (props.useDegrees) { - // Convert degrees to radians if necessary - newValue = Tools.ToRadians(newValue); - } - setValue(newValue); props.onChange(newValue); // Notify parent } }; - const min = props.min ?? (props.useDegrees ? 0 : undefined); - const max = props.max ?? (props.useDegrees ? 360 : undefined); - - const convertedValue = props.useDegrees ? Tools.ToDegrees(value) : value; - return (
- {min !== undefined && max !== undefined && ( - - )} - + {props.min !== undefined && props.max !== undefined && } +
); }; diff --git a/packages/dev/sharedUiComponents/src/lines/optionsLineComponent.tsx b/packages/dev/sharedUiComponents/src/lines/optionsLineComponent.tsx index e62ec50edf5..3f858f81c24 100644 --- a/packages/dev/sharedUiComponents/src/lines/optionsLineComponent.tsx +++ b/packages/dev/sharedUiComponents/src/lines/optionsLineComponent.tsx @@ -6,7 +6,7 @@ import type { IInspectableOptions } from "core/Misc/iInspectable"; import copyIcon from "../imgs/copy.svg"; import { PropertyLine } from "../fluent/hoc/propertyLine"; import { Dropdown } from "../fluent/primitives/dropdown"; -import type { DropdownOption } from "../fluent/primitives/dropdown"; +import type { AcceptedDropdownValue } from "../fluent/primitives/dropdown"; import { ToolContext } from "../fluent/hoc/fluentToolWrapper"; // eslint-disable-next-line @typescript-eslint/naming-convention @@ -125,14 +125,10 @@ export class OptionsLine extends React.Component this.onCopyClickStr()}> { - if (val.value === null || val.value === undefined) { - this.updateValue(Null_Value.toString()); - } else { - this.updateValue(val.value.toString()); - } + onChange={(val: AcceptedDropdownValue | undefined) => { + val !== undefined && this.updateValue(val.toString()); }} - value={this.props.options.find((o) => o.value === this.state.value || o.selected) || ({ label: "Default", value: null } as DropdownOption)} + value={this.state.value} /> ); From 3e5a8d528dc4d8ab027385a81499bf8eddab8555 Mon Sep 17 00:00:00 2001 From: Georgina Date: Wed, 25 Jun 2025 16:18:59 -0400 Subject: [PATCH 02/12] move the notion of degrees outside of vector3 --- .../transformNodeTransformProperties.tsx | 4 +- .../src/fluent/hoc/colorPropertyLine.tsx | 43 ++++++----- .../src/fluent/hoc/vectorPropertyLine.tsx | 77 ++++++++++++------- 3 files changed, 74 insertions(+), 50 deletions(-) diff --git a/packages/dev/inspector-v2/src/components/properties/transformNodeTransformProperties.tsx b/packages/dev/inspector-v2/src/components/properties/transformNodeTransformProperties.tsx index 1dc313b0f84..26b39741ac6 100644 --- a/packages/dev/inspector-v2/src/components/properties/transformNodeTransformProperties.tsx +++ b/packages/dev/inspector-v2/src/components/properties/transformNodeTransformProperties.tsx @@ -3,7 +3,7 @@ import type { TransformNode, Vector3 } from "core/index"; import type { FunctionComponent } from "react"; -import { Vector3PropertyLine } from "shared-ui-components/fluent/hoc/vectorPropertyLine"; +import { RotationVectorPropertyLine, Vector3PropertyLine } from "shared-ui-components/fluent/hoc/vectorPropertyLine"; import { useInterceptObservable } from "../../hooks/instrumentationHooks"; import { useObservableState } from "../../hooks/observableHooks"; @@ -34,7 +34,7 @@ export const TransformNodeTransformProperties: FunctionComponent<{ node: Transfo return ( <> (node.position = val)} /> - (node.rotation = val)} useDegrees={useDegrees} /> + (node.rotation = val)} useDegrees={useDegrees} /> (node.scaling = val)} /> ); diff --git a/packages/dev/sharedUiComponents/src/fluent/hoc/colorPropertyLine.tsx b/packages/dev/sharedUiComponents/src/fluent/hoc/colorPropertyLine.tsx index a955cb08ac0..461a4c72f8d 100644 --- a/packages/dev/sharedUiComponents/src/fluent/hoc/colorPropertyLine.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/hoc/colorPropertyLine.tsx @@ -1,7 +1,7 @@ import type { FunctionComponent } from "react"; import { forwardRef, useState } from "react"; -import type { BaseComponentProps, PropertyLineProps } from "./propertyLine"; +import type { PropertyLineProps } from "./propertyLine"; import { PropertyLine } from "./propertyLine"; import { SyncedSliderLine } from "./syncedSliderLine"; @@ -16,23 +16,11 @@ export type ColorPropertyLineProps = ColorPickerProps & Propert * Reusable component which renders a color property line containing a label, colorPicker popout, and expandable RGBA values * The expandable RGBA values are synced sliders that allow the user to modify the color's RGBA values directly * @param props - PropertyLine props, replacing children with a color object so that we can properly display the color - * @returns Component wrapping a colorPicker component (coming soon) with a property line + * @returns Component wrapping a colorPicker component with a property line */ const ColorPropertyLine = forwardRef((props, ref) => { const [color, setColor] = useState(props.value); - const onChange = (value: Color3 | Color4) => { - setColor(value); - props.onChange(value); - }; - return ( - }> - - - ); -}); -const ColorSliders: FunctionComponent> = (props) => { - const [color, setColor] = useState(props.value); const onChange = (value: number, key: "r" | "g" | "b" | "a") => { let newColor; if (key === "a") { @@ -46,15 +34,28 @@ const ColorSliders: FunctionComponent> = (pr props.onChange(newColor); }; + const onColorPickerChange = (newColor: Color3 | Color4) => { + setColor(newColor); + props.onChange(newColor); + }; + return ( - <> - onChange(value, "r")} /> - onChange(value, "g")} /> - onChange(value, "b")} /> - {color instanceof Color4 && onChange(value, "a")} />} - + + onChange(value, "r")} /> + onChange(value, "g")} /> + onChange(value, "b")} /> + {color instanceof Color4 && onChange(value, "a")} />} + + } + > + + ); -}; +}); export const Color3PropertyLine = ColorPropertyLine as FunctionComponent & PropertyLineProps>; export const Color4PropertyLine = ColorPropertyLine as FunctionComponent & PropertyLineProps>; diff --git a/packages/dev/sharedUiComponents/src/fluent/hoc/vectorPropertyLine.tsx b/packages/dev/sharedUiComponents/src/fluent/hoc/vectorPropertyLine.tsx index 67bc8acf145..d33fdb93a6b 100644 --- a/packages/dev/sharedUiComponents/src/fluent/hoc/vectorPropertyLine.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/hoc/vectorPropertyLine.tsx @@ -13,9 +13,26 @@ import { Tools } from "core/Misc/tools"; export type VectorPropertyLineProps = BaseComponentProps & PropertyLineProps & { /** - * Display angles as degrees instead of radians + * If passed, all sliders will use this for the min value */ - useDegrees?: boolean; + min?: number; + /** + * If passed, all sliders will use this for the max value + */ + max?: number; + /** + * If passed, the UX will use the conversion functions to display/update values + */ + valConverter?: { + /** + * Will call from(val) before displaying in the UX + */ + from: (val: number) => number; + /** + * Will call to(val) before calling onChange + */ + to: (val: number) => number; + }; }; /** @@ -25,27 +42,14 @@ export type VectorPropertyLineProps = BaseComponent * @returns */ const VectorPropertyLine: FunctionComponent> = (props) => { - let converter = (v: number) => v.toFixed(2); - - if (props.useDegrees) { - converter = (v: number) => Tools.ToDegrees(v).toFixed(2); - } + const converted = (val: number) => (props.valConverter ? props.valConverter.from(val) : val); + const formatted = (val: number) => converted(val).toFixed(2); - return ( - }> - {`X: ${converter(props.value.x)} | Y: ${converter(props.value.y)} | Z: ${converter(props.value.z)}${props.value instanceof Vector4 ? ` | W: ${converter(props.value.w)}` : ""}`} - - ); -}; - -const VectorSliders: FunctionComponent> = (props) => { const [vector, setVector] = useState(props.value); - - const min = props.useDegrees ? 0 : undefined; - const max = props.useDegrees ? 360 : undefined; + const { min, max } = props; const onChange = (val: number, key: "x" | "y" | "z" | "w") => { - const value = props.useDegrees ? Tools.ToRadians(val) : val; + const value = props.valConverter ? props.valConverter.to(val) : val; const newVector = vector.clone(); (newVector as Vector4)[key] = value; // The syncedSlider for 'w' is only rendered when vector is a Vector4, so this is safe @@ -53,17 +57,36 @@ const VectorSliders: FunctionComponent (props.useDegrees ? Tools.ToDegrees(val) : val); - return ( - <> - onChange(val, "x")} /> - onChange(val, "y")} /> - onChange(val, "z")} /> - {vector instanceof Vector4 && onChange(val, "w")} />} - + + onChange(val, "x")} /> + onChange(val, "y")} /> + onChange(val, "z")} /> + {vector instanceof Vector4 && onChange(val, "w")} />} + + } + > + {`X: ${formatted(props.value.x)} | Y: ${formatted(props.value.y)} | Z: ${formatted(props.value.z)}${props.value instanceof Vector4 ? ` | W: ${formatted(props.value.w)}` : ""}`} + ); }; +type RotationVectorPropertyLineProps = VectorPropertyLineProps & { + /** + * Display angles as degrees instead of radians + */ + useDegrees?: boolean; +}; + +const ToDegreesConverter = { from: Tools.ToDegrees, to: Tools.ToRadians }; +export const RotationVectorPropertyLine: FunctionComponent = (props) => { + const min = props.useDegrees ? 0 : undefined; + const max = props.useDegrees ? 360 : undefined; + return ; +}; + export const Vector3PropertyLine = VectorPropertyLine as FunctionComponent>; export const Vector4PropertyLine = VectorPropertyLine as FunctionComponent>; From 584e0d563727c48294644b4959820b784f284699 Mon Sep 17 00:00:00 2001 From: Georgina Date: Wed, 25 Jun 2025 17:24:40 -0400 Subject: [PATCH 03/12] pr comments --- .../properties/materialTransparencyProperties.tsx | 3 ++- packages/dev/inspector-v2/test/app/index.ts | 2 +- .../src/fluent/hoc/colorPropertyLine.tsx | 10 +++++----- .../src/fluent/hoc/vectorPropertyLine.tsx | 11 ++++++----- .../src/fluent/primitives/dropdown.tsx | 8 ++++---- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/dev/inspector-v2/src/components/properties/materialTransparencyProperties.tsx b/packages/dev/inspector-v2/src/components/properties/materialTransparencyProperties.tsx index 6fb4de34117..e291495384e 100644 --- a/packages/dev/inspector-v2/src/components/properties/materialTransparencyProperties.tsx +++ b/packages/dev/inspector-v2/src/components/properties/materialTransparencyProperties.tsx @@ -6,8 +6,9 @@ import type { FunctionComponent } from "react"; import { DropdownPropertyLine } from "shared-ui-components/fluent/hoc/dropdownPropertyLine"; import { BoundProperty } from "./boundProperty"; +import type { DropdownOption } from "shared-ui-components/fluent/primitives/dropdown"; -const TransparencyModeOptions = [ +const TransparencyModeOptions: DropdownOption[] = [ { label: "Opaque", value: PBRMaterial.PBRMATERIAL_OPAQUE }, { label: "Alpha test", value: PBRMaterial.PBRMATERIAL_ALPHATEST }, { label: "Alpha blend", value: PBRMaterial.PBRMATERIAL_ALPHABLEND }, diff --git a/packages/dev/inspector-v2/test/app/index.ts b/packages/dev/inspector-v2/test/app/index.ts index 57286bd7863..d975e3aab5e 100644 --- a/packages/dev/inspector-v2/test/app/index.ts +++ b/packages/dev/inspector-v2/test/app/index.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line import/no-internal-modules -import { type ArcRotateCamera, type Nullable } from "core/index"; +import type { ArcRotateCamera, Nullable } from "core/index"; import { Engine } from "core/Engines/engine"; import { LoadAssetContainerAsync } from "core/Loading/sceneLoader"; diff --git a/packages/dev/sharedUiComponents/src/fluent/hoc/colorPropertyLine.tsx b/packages/dev/sharedUiComponents/src/fluent/hoc/colorPropertyLine.tsx index 461a4c72f8d..f5284ac8ac9 100644 --- a/packages/dev/sharedUiComponents/src/fluent/hoc/colorPropertyLine.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/hoc/colorPropertyLine.tsx @@ -21,7 +21,7 @@ export type ColorPropertyLineProps = ColorPickerProps & Propert const ColorPropertyLine = forwardRef((props, ref) => { const [color, setColor] = useState(props.value); - const onChange = (value: number, key: "r" | "g" | "b" | "a") => { + const onSliderChange = (value: number, key: "r" | "g" | "b" | "a") => { let newColor; if (key === "a") { newColor = Color4.FromColor3(color, value); @@ -45,10 +45,10 @@ const ColorPropertyLine = forwardRef((pr {...props} expandedContent={ <> - onChange(value, "r")} /> - onChange(value, "g")} /> - onChange(value, "b")} /> - {color instanceof Color4 && onChange(value, "a")} />} + onSliderChange(value, "r")} /> + onSliderChange(value, "g")} /> + onSliderChange(value, "b")} /> + {color instanceof Color4 && onSliderChange(value, "a")} />} } > diff --git a/packages/dev/sharedUiComponents/src/fluent/hoc/vectorPropertyLine.tsx b/packages/dev/sharedUiComponents/src/fluent/hoc/vectorPropertyLine.tsx index d33fdb93a6b..795f81016f8 100644 --- a/packages/dev/sharedUiComponents/src/fluent/hoc/vectorPropertyLine.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/hoc/vectorPropertyLine.tsx @@ -1,4 +1,5 @@ -import { useState, type FunctionComponent } from "react"; +import { useState } from "react"; +import type { FunctionComponent } from "react"; import { Body1 } from "@fluentui/react-components"; import { PropertyLine } from "./propertyLine"; @@ -23,7 +24,7 @@ export type VectorPropertyLineProps = BaseComponent /** * If passed, the UX will use the conversion functions to display/update values */ - valConverter?: { + valueConverter?: { /** * Will call from(val) before displaying in the UX */ @@ -42,14 +43,14 @@ export type VectorPropertyLineProps = BaseComponent * @returns */ const VectorPropertyLine: FunctionComponent> = (props) => { - const converted = (val: number) => (props.valConverter ? props.valConverter.from(val) : val); + const converted = (val: number) => (props.valueConverter ? props.valueConverter.from(val) : val); const formatted = (val: number) => converted(val).toFixed(2); const [vector, setVector] = useState(props.value); const { min, max } = props; const onChange = (val: number, key: "x" | "y" | "z" | "w") => { - const value = props.valConverter ? props.valConverter.to(val) : val; + const value = props.valueConverter ? props.valueConverter.to(val) : val; const newVector = vector.clone(); (newVector as Vector4)[key] = value; // The syncedSlider for 'w' is only rendered when vector is a Vector4, so this is safe @@ -85,7 +86,7 @@ const ToDegreesConverter = { from: Tools.ToDegrees, to: Tools.ToRadians }; export const RotationVectorPropertyLine: FunctionComponent = (props) => { const min = props.useDegrees ? 0 : undefined; const max = props.useDegrees ? 360 : undefined; - return ; + return ; }; export const Vector3PropertyLine = VectorPropertyLine as FunctionComponent>; diff --git a/packages/dev/sharedUiComponents/src/fluent/primitives/dropdown.tsx b/packages/dev/sharedUiComponents/src/fluent/primitives/dropdown.tsx index ac92f9fd78e..0d2122554eb 100644 --- a/packages/dev/sharedUiComponents/src/fluent/primitives/dropdown.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/primitives/dropdown.tsx @@ -36,10 +36,10 @@ export type DropdownProps = BaseComponentProps = (props) => { const classes = useDropdownStyles(); - const [options] = useState(props.includeUndefined ? [{ label: "", value: Number.MAX_SAFE_INTEGER }, ...props.options] : props.options); - const [defaultVal, setDefaultVal] = useState(props.includeUndefined && props.value === undefined ? Number.MAX_SAFE_INTEGER : props.value); + const [options] = useState(props.includeUndefined ? [{ label: "", value: Number.NaN }, ...props.options] : props.options); + const [defaultVal, setDefaultVal] = useState(props.includeUndefined && props.value === undefined ? Number.NaN : props.value); useEffect(() => { - setDefaultVal(props.includeUndefined && props.value === undefined ? Number.MAX_SAFE_INTEGER : props.value); + setDefaultVal(props.includeUndefined && props.value === undefined ? Number.NaN : props.value); }, [props.value]); return ( @@ -49,7 +49,7 @@ export const Dropdown: FunctionComponent = (props) => { onOptionSelect={(evt, data) => { let value = typeof props.value === "number" ? Number(data.optionValue) : data.optionValue; setDefaultVal(value); - if (props.includeUndefined && value === Number.MAX_SAFE_INTEGER.toString()) { + if (props.includeUndefined && value === Number.NaN.toString()) { value = undefined; } props.onChange(value); From 8f4721ac058882d02139654c6bc20287613ddb15 Mon Sep 17 00:00:00 2001 From: Georgina Date: Fri, 27 Jun 2025 12:52:31 -0400 Subject: [PATCH 04/12] in progress --- .../src/components/accordionPane.tsx | 5 - .../src/hooks/compoundPropertyHooks.ts | 6 +- .../src/fluent/hoc/propertyLine.tsx | 6 +- .../src/lines/draggableLineComponent.tsx | 22 +- .../src/lines/lineContainerComponent.tsx | 12 +- .../components/nodeList/nodeListComponent.tsx | 58 ++- .../propertyTab/propertyTabComponent.tsx | 382 +++++++++--------- 7 files changed, 272 insertions(+), 219 deletions(-) diff --git a/packages/dev/inspector-v2/src/components/accordionPane.tsx b/packages/dev/inspector-v2/src/components/accordionPane.tsx index f8f3cf7eb36..6cbfb9d77c1 100644 --- a/packages/dev/inspector-v2/src/components/accordionPane.tsx +++ b/packages/dev/inspector-v2/src/components/accordionPane.tsx @@ -64,11 +64,6 @@ const useStyles = makeStyles({ display: "flex", flexDirection: "column", }, - panelDiv: { - display: "flex", - flexDirection: "column", - overflow: "hidden", - }, }); export type AccordionPaneSectionProps = { diff --git a/packages/dev/inspector-v2/src/hooks/compoundPropertyHooks.ts b/packages/dev/inspector-v2/src/hooks/compoundPropertyHooks.ts index 9e70c39abf3..295314b51e3 100644 --- a/packages/dev/inspector-v2/src/hooks/compoundPropertyHooks.ts +++ b/packages/dev/inspector-v2/src/hooks/compoundPropertyHooks.ts @@ -15,7 +15,7 @@ type Vector3Keys = { [P in keyof T]: T[P] extends Vector3 ? P : never }[keyof // to re-render when the property changes or when the x/y/z components of the Vector3 change. // eslint-disable-next-line @typescript-eslint/naming-convention export function useVector3Property>(target: T, propertyKey: K): Vector3 { - const vector = useProperty(target, propertyKey) as Vector3; + const vector = useProperty(target, propertyKey); useProperty(vector, "x"); useProperty(vector, "y"); useProperty(vector, "z"); @@ -28,7 +28,7 @@ type Color3Keys = { [P in keyof T]: T[P] extends Color3 ? P : never }[keyof T // to re-render when the property changes or when the r/g/b components of the Color3 change. // eslint-disable-next-line @typescript-eslint/naming-convention export function useColor3Property>(target: T, propertyKey: K): Color3 { - const color = useProperty(target, propertyKey) as Color3; + const color = useProperty(target, propertyKey); useProperty(color, "r"); useProperty(color, "g"); useProperty(color, "b"); @@ -41,7 +41,7 @@ type QuaternionKeys = { [P in keyof T]: T[P] extends Nullable ? P // to re-render when the property changes or when the x/y/z components of the Quaternion change. // eslint-disable-next-line @typescript-eslint/naming-convention export function useQuaternionProperty>(target: T, propertyKey: K): Quaternion { - const quaternion = useProperty(target, propertyKey) as Quaternion; + const quaternion = useProperty(target, propertyKey); useProperty(quaternion, "x"); useProperty(quaternion, "y"); useProperty(quaternion, "z"); diff --git a/packages/dev/sharedUiComponents/src/fluent/hoc/propertyLine.tsx b/packages/dev/sharedUiComponents/src/fluent/hoc/propertyLine.tsx index 208b733f7e1..449afeb4c93 100644 --- a/packages/dev/sharedUiComponents/src/fluent/hoc/propertyLine.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/hoc/propertyLine.tsx @@ -1,7 +1,7 @@ import { Body1, Body1Strong, Button, InfoLabel, Link, ToggleButton, makeStyles, tokens } from "@fluentui/react-components"; import { Collapse } from "@fluentui/react-motion-components-preview"; import { AddFilled, CopyRegular, SubtractFilled } from "@fluentui/react-icons"; -import type { FunctionComponent, PropsWithChildren } from "react"; +import type { FunctionComponent, HTMLProps, PropsWithChildren } from "react"; import { useContext, useState, forwardRef } from "react"; import { copyCommandToClipboard } from "../../copyCommandToClipboard"; import { ToolContext } from "./fluentToolWrapper"; @@ -81,10 +81,10 @@ export type PropertyLineProps = { docLink?: string; }; -export const LineContainer = forwardRef((props, ref) => { +export const LineContainer = forwardRef>((props, ref) => { const classes = usePropertyLineStyles(); return ( -
+
{props.children}
); diff --git a/packages/dev/sharedUiComponents/src/lines/draggableLineComponent.tsx b/packages/dev/sharedUiComponents/src/lines/draggableLineComponent.tsx index 47d9268d5cf..1dca4013844 100644 --- a/packages/dev/sharedUiComponents/src/lines/draggableLineComponent.tsx +++ b/packages/dev/sharedUiComponents/src/lines/draggableLineComponent.tsx @@ -1,4 +1,6 @@ import * as React from "react"; +import { ToolContext } from "shared-ui-components/fluent/hoc/fluentToolWrapper"; +import { LineContainer } from "shared-ui-components/fluent/hoc/propertyLine"; export interface IButtonLineComponentProps { format: string; @@ -11,7 +13,21 @@ export class DraggableLineComponent extends React.Component { + event.dataTransfer.setData(this.props.format, this.props.data); + }} + > + {this.props.data.replace("Block", "")} + + ); + } + + renderOriginal() { return (
); } + + override render() { + return {({ useFluent }) => (useFluent ? this.renderFluent() : this.renderOriginal())}; + } } diff --git a/packages/dev/sharedUiComponents/src/lines/lineContainerComponent.tsx b/packages/dev/sharedUiComponents/src/lines/lineContainerComponent.tsx index c23b91ac268..12080a95c48 100644 --- a/packages/dev/sharedUiComponents/src/lines/lineContainerComponent.tsx +++ b/packages/dev/sharedUiComponents/src/lines/lineContainerComponent.tsx @@ -2,6 +2,8 @@ import * as React from "react"; import { DataStorage } from "core/Misc/dataStorage"; import type { ISelectedLineContainer } from "./iSelectedLineContainer"; import downArrow from "./downArrow.svg"; +import { AccordionSection } from "shared-ui-components/fluent/primitives/accordion"; +import { ToolContext } from "shared-ui-components/fluent/hoc/fluentToolWrapper"; interface ILineContainerComponentProps { selection?: ISelectedLineContainer; @@ -69,7 +71,11 @@ export class LineContainerComponent extends React.Component{this.props.children}; + } + + renderOriginal() { if (!this.state.isExpanded) { return (
@@ -88,4 +94,8 @@ export class LineContainerComponent extends React.Component ); } + + override render() { + return {({ useFluent }) => (useFluent ? this.renderFluent() : this.renderOriginal())}; + } } diff --git a/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx b/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx index db0986ad9bb..bc1c96e8317 100644 --- a/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx +++ b/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx @@ -14,6 +14,9 @@ import deleteButton from "../../imgs/delete.svg"; import { NodeLedger } from "shared-ui-components/nodeGraphSystem/nodeLedger"; import "./nodeList.scss"; +import { ToolContext } from "shared-ui-components/fluent/hoc/fluentToolWrapper"; +import { Accordion } from "shared-ui-components/fluent/primitives/accordion"; +import { Input } from "shared-ui-components/fluent/primitives/input"; interface INodeListComponentProps { globalState: GlobalState; @@ -327,6 +330,38 @@ export class NodeListComponent extends React.Component + this.filterContent(val.toString())} /> + {blockMenu} +
+ ); + } + + renderOriginal(blockMenu: any[]) { + return ( +
+
+
+
+ (this.props.globalState.lockObject.lock = true)} + onBlur={() => { + this.props.globalState.lockObject.lock = false; + }} + onChange={(evt) => this.filterContent(evt.target.value)} + /> +
+
{blockMenu}
+
+
+
+ ); + } + override render() { const customFrameNames: string[] = []; for (const frame in this._customFrameList) { @@ -556,7 +591,7 @@ export class NodeListComponent extends React.Component !this.state.filter || b.toLowerCase().indexOf(this.state.filter.toLowerCase()) !== -1) @@ -661,25 +696,6 @@ export class NodeListComponent extends React.Component -
-
-
- (this.props.globalState.lockObject.lock = true)} - onBlur={() => { - this.props.globalState.lockObject.lock = false; - }} - onChange={(evt) => this.filterContent(evt.target.value)} - /> -
-
{blockMenu}
-
-
-
- ); + return {({ useFluent }) => (useFluent ? this.renderFluent(blockMenu) : this.renderOriginal(blockMenu))}; } } diff --git a/packages/tools/nodeEditor/src/components/propertyTab/propertyTabComponent.tsx b/packages/tools/nodeEditor/src/components/propertyTab/propertyTabComponent.tsx index 26dc7955b49..4b480b26367 100644 --- a/packages/tools/nodeEditor/src/components/propertyTab/propertyTabComponent.tsx +++ b/packages/tools/nodeEditor/src/components/propertyTab/propertyTabComponent.tsx @@ -40,6 +40,8 @@ import { FloatLineComponent } from "shared-ui-components/lines/floatLineComponen import { SliderLineComponent } from "shared-ui-components/lines/sliderLineComponent"; import { SetToDefaultGaussianSplatting, SetToDefaultSFE } from "core/Materials/Node/nodeMaterialDefault"; import { AlphaModeOptions } from "shared-ui-components/constToOptionsMaps"; +import { ToolContext } from "shared-ui-components/fluent/hoc/fluentToolWrapper"; +import { Accordion } from "shared-ui-components/fluent/primitives/accordion"; interface IPropertyTabComponentProps { globalState: GlobalState; @@ -460,191 +462,201 @@ export class PropertyTabComponent extends React.Component
-
- - this.props.globalState.mode} - options={modeList} - onSelect={(value) => this.changeMode(value)} - propertyName={""} - /> - this.props.globalState.engine} - options={engineList} - onSelect={(value) => { - this.props.globalState.engine = value as number; - this.forceUpdate(); - }} - propertyName={""} - /> - - this.props.globalState.hostDocument.defaultView!.open("https://doc.babylonjs.com/how_to/node_material", "_blank")} - /> - - { - switch (this.props.globalState.mode) { - case NodeMaterialModes.Material: - this.props.globalState.nodeMaterial.setToDefault(); - break; - case NodeMaterialModes.PostProcess: - this.props.globalState.nodeMaterial.setToDefaultPostProcess(); - break; - case NodeMaterialModes.SFE: - SetToDefaultSFE(this.props.globalState.nodeMaterial!); - break; - case NodeMaterialModes.Particle: - this.props.globalState.nodeMaterial.setToDefaultParticle(); - break; - case NodeMaterialModes.ProceduralTexture: - this.props.globalState.nodeMaterial.setToDefaultProceduralTexture(); - break; - case NodeMaterialModes.GaussianSplatting: - SetToDefaultGaussianSplatting(this.props.globalState.nodeMaterial); - break; - } - this.props.globalState.onResetRequiredObservable.notifyObservers(true); - this.props.globalState.onClearUndoStack.notifyObservers(); - }} - /> - - - { - this.props.globalState.onZoomToFitRequiredObservable.notifyObservers(); - }} - /> - { - this.props.globalState.onReOrganizedRequiredObservable.notifyObservers(); - }} - /> - - - DataStorage.ReadBoolean("EmbedTextures", true)} - onSelect={(value: boolean) => { - DataStorage.WriteBoolean("EmbedTextures", value); - }} - /> - { - DataStorage.WriteNumber("GridSize", value); - this.props.globalState.stateManager.onGridSizeChanged.notifyObservers(); - this.forceUpdate(); - }} - /> - DataStorage.ReadBoolean("ShowGrid", true)} - onSelect={(value: boolean) => { - DataStorage.WriteBoolean("ShowGrid", value); - this.props.globalState.stateManager.onGridSizeChanged.notifyObservers(); - }} - /> - - - this.load(file)} accept=".json" /> - { - this.save(); - }} - /> - {this.props.globalState.mode === NodeMaterialModes.SFE && ( - { - this.props.globalState.nodeMaterial.build(); - const fragment = await this.props.globalState.nodeMaterial!._getProcessedFragmentAsync(); - StringTools.DownloadAsFile(this.props.globalState.hostDocument, fragment, "nme.block.glsl"); - }} - /> - )} - { - StringTools.DownloadAsFile(this.props.globalState.hostDocument, this.props.globalState.nodeMaterial.generateCode(), "code.txt"); - }} - /> - { - this.props.globalState.nodeMaterial.build(); - StringTools.DownloadAsFile(this.props.globalState.hostDocument, this.props.globalState.nodeMaterial.compiledShaders, "shaders.txt"); - }} - /> - {this.props.globalState.customSave && ( - { - this.customSave(); - }} - /> - )} - this.loadFrame(file)} accept=".json" /> - - {!this.props.globalState.customSave && ( - - {this.props.globalState.nodeMaterial.snippetId && } - this.loadFromSnippet()} /> - { - this.saveToSnippetServer(); - }} - /> - - )} - - this.props.globalState.stateManager.onUpdateRequiredObservable.notifyObservers(null)} - /> - this.props.globalState.stateManager.onUpdateRequiredObservable.notifyObservers(null)} - /> - - -
+ + {(context) => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const Wrapper = context.useFluent ? Accordion : "div"; + return ( + + + this.props.globalState.mode} + options={modeList} + onSelect={(value) => this.changeMode(value)} + propertyName={""} + /> + this.props.globalState.engine} + options={engineList} + onSelect={(value) => { + this.props.globalState.engine = value as number; + this.forceUpdate(); + }} + propertyName={""} + /> + + this.props.globalState.hostDocument.defaultView!.open("https://doc.babylonjs.com/how_to/node_material", "_blank")} + /> + + { + switch (this.props.globalState.mode) { + case NodeMaterialModes.Material: + this.props.globalState.nodeMaterial.setToDefault(); + break; + case NodeMaterialModes.PostProcess: + this.props.globalState.nodeMaterial.setToDefaultPostProcess(); + break; + case NodeMaterialModes.SFE: + SetToDefaultSFE(this.props.globalState.nodeMaterial!); + break; + case NodeMaterialModes.Particle: + this.props.globalState.nodeMaterial.setToDefaultParticle(); + break; + case NodeMaterialModes.ProceduralTexture: + this.props.globalState.nodeMaterial.setToDefaultProceduralTexture(); + break; + case NodeMaterialModes.GaussianSplatting: + SetToDefaultGaussianSplatting(this.props.globalState.nodeMaterial); + break; + } + this.props.globalState.onResetRequiredObservable.notifyObservers(true); + this.props.globalState.onClearUndoStack.notifyObservers(); + }} + /> + + + { + this.props.globalState.onZoomToFitRequiredObservable.notifyObservers(); + }} + /> + { + this.props.globalState.onReOrganizedRequiredObservable.notifyObservers(); + }} + /> + + + DataStorage.ReadBoolean("EmbedTextures", true)} + onSelect={(value: boolean) => { + DataStorage.WriteBoolean("EmbedTextures", value); + }} + /> + { + DataStorage.WriteNumber("GridSize", value); + this.props.globalState.stateManager.onGridSizeChanged.notifyObservers(); + this.forceUpdate(); + }} + /> + DataStorage.ReadBoolean("ShowGrid", true)} + onSelect={(value: boolean) => { + DataStorage.WriteBoolean("ShowGrid", value); + this.props.globalState.stateManager.onGridSizeChanged.notifyObservers(); + }} + /> + + + this.load(file)} accept=".json" /> + { + this.save(); + }} + /> + {this.props.globalState.mode === NodeMaterialModes.SFE && ( + { + this.props.globalState.nodeMaterial.build(); + const fragment = await this.props.globalState.nodeMaterial!._getProcessedFragmentAsync(); + StringTools.DownloadAsFile(this.props.globalState.hostDocument, fragment, "nme.block.glsl"); + }} + /> + )} + { + StringTools.DownloadAsFile(this.props.globalState.hostDocument, this.props.globalState.nodeMaterial.generateCode(), "code.txt"); + }} + /> + { + this.props.globalState.nodeMaterial.build(); + StringTools.DownloadAsFile(this.props.globalState.hostDocument, this.props.globalState.nodeMaterial.compiledShaders, "shaders.txt"); + }} + /> + {this.props.globalState.customSave && ( + { + this.customSave(); + }} + /> + )} + this.loadFrame(file)} accept=".json" /> + + {!this.props.globalState.customSave && ( + + {this.props.globalState.nodeMaterial.snippetId && ( + + )} + this.loadFromSnippet()} /> + { + this.saveToSnippetServer(); + }} + /> + + )} + + this.props.globalState.stateManager.onUpdateRequiredObservable.notifyObservers(null)} + /> + this.props.globalState.stateManager.onUpdateRequiredObservable.notifyObservers(null)} + /> + + + + ); + }} + ); } From 0d9341f51d4d92c195f281e99da73f71df8e1f2b Mon Sep 17 00:00:00 2001 From: Georgina Date: Thu, 3 Jul 2025 12:00:11 -0400 Subject: [PATCH 05/12] update propertytabs to use fluent accordion --- .../tabs/propertyGridTabComponent.tsx | 2 +- .../components/propertyTabComponentBase.tsx | 36 ++ .../src/fluent/hoc/fluentToolWrapper.tsx | 21 +- .../src/fluent/hoc/pane.tsx | 45 ++ .../src/fluent/primitives/accordion.tsx | 1 + .../inputsPropertyTabComponent.tsx | 33 +- .../propertyTab/propertyTabComponent.tsx | 402 ++++++++---------- packages/tools/nodeEditor/src/graphEditor.tsx | 2 +- .../colorMergerPropertyComponent.tsx | 9 +- .../debugNodePropertyTabComponent.tsx | 11 +- .../frameNodePortPropertyComponent.tsx | 49 +-- .../properties/framePropertyComponent.tsx | 69 ++- .../genericNodePropertyComponent.tsx | 336 ++++++++------- .../gradientNodePropertyComponent.tsx | 9 +- .../imageSourcePropertyTabComponent.tsx | 11 +- .../properties/inputNodePropertyComponent.tsx | 9 +- .../lightInformationPropertyTabComponent.tsx | 9 +- .../properties/lightPropertyTabComponent.tsx | 9 +- .../properties/nodePortPropertyComponent.tsx | 13 +- .../teleportOutNodePropertyComponent.tsx | 9 +- .../texturePropertyTabComponent.tsx | 11 +- .../vectorMergerPropertyComponent.tsx | 9 +- .../graphSystem/registerToPropertyLedger.ts | 4 +- 23 files changed, 605 insertions(+), 504 deletions(-) create mode 100644 packages/dev/sharedUiComponents/src/components/propertyTabComponentBase.tsx create mode 100644 packages/dev/sharedUiComponents/src/fluent/hoc/pane.tsx diff --git a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx index 505ae89cb26..d67eb582f9c 100644 --- a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx +++ b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx @@ -775,7 +775,7 @@ export class PropertyGridTabComponent extends PaneComponent { const entity = this.props.selectedEntity || {}; const entityHasMetadataProp = Object.prototype.hasOwnProperty.call(entity, "metadata"); return ( - +
{this.renderContent()} {Tags.HasTags(entity) && ( diff --git a/packages/dev/sharedUiComponents/src/components/propertyTabComponentBase.tsx b/packages/dev/sharedUiComponents/src/components/propertyTabComponentBase.tsx new file mode 100644 index 00000000000..385d2867bf8 --- /dev/null +++ b/packages/dev/sharedUiComponents/src/components/propertyTabComponentBase.tsx @@ -0,0 +1,36 @@ +import { useContext, type FunctionComponent, type PropsWithChildren } from "react"; +import { ToolContext } from "../fluent/hoc/fluentToolWrapper"; +import { Pane } from "../fluent/hoc/pane"; +import { Accordion } from "shared-ui-components/fluent/primitives/accordion"; + +/** + * A wrapper component for the property tab that provides a consistent layout and styling. + * It uses a Pane and an Accordion to organize the content, so its direct children + * must have 'title' props to be compatible with the Accordion structure. + * @param props The props to pass to the component. + * @returns The rendered component. + */ +export const PropertyTabComponentBase: FunctionComponent = (props) => { + const context = useContext(ToolContext); + const fluentWrapper: FunctionComponent = (props) => { + return ( + + {props.children} + + ); + }; + const originalWrapper: FunctionComponent = (props) => { + return ( +
+ +
{props.children}
+
+ ); + }; + // eslint-disable-next-line @typescript-eslint/naming-convention + const Wrapper = context.useFluent ? fluentWrapper : originalWrapper; + return {props.children}; +}; diff --git a/packages/dev/sharedUiComponents/src/fluent/hoc/fluentToolWrapper.tsx b/packages/dev/sharedUiComponents/src/fluent/hoc/fluentToolWrapper.tsx index bdcb04b3860..5c94756e07a 100644 --- a/packages/dev/sharedUiComponents/src/fluent/hoc/fluentToolWrapper.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/hoc/fluentToolWrapper.tsx @@ -13,9 +13,14 @@ export type ToolHostProps = { * Can be set to true to disable the copy button in the tool's property lines. Default is false (copy enabled) */ disableCopy?: boolean; + + /** + * Name of the tool displayed in the UX + */ + toolName: string; }; -export const ToolContext = createContext({ useFluent: false as boolean, disableCopy: false as boolean } as const); +export const ToolContext = createContext({ useFluent: false as boolean, disableCopy: false as boolean, toolName: "" as string } as const); /** * For tools which are ready to move over the fluent, wrap the root of the tool (or the panel which you want fluentized) with this component @@ -25,13 +30,17 @@ export const ToolContext = createContext({ useFluent: false as boolean, disableC */ export const FluentToolWrapper: FunctionComponent> = (props) => { const url = new URL(window.location.href); - const enableFluent = url.searchParams.has("newUX") || url.hash.includes("newUX"); - - return enableFluent ? ( + const useFluent = url.searchParams.has("newUX") || url.hash.includes("newUX"); + const contextValue = { + useFluent, + disableCopy: !!props.disableCopy, + toolName: props.toolName, + }; + return useFluent ? ( - {props.children} + {props.children} ) : ( - props.children + {props.children} ); }; diff --git a/packages/dev/sharedUiComponents/src/fluent/hoc/pane.tsx b/packages/dev/sharedUiComponents/src/fluent/hoc/pane.tsx new file mode 100644 index 00000000000..4de87397bfc --- /dev/null +++ b/packages/dev/sharedUiComponents/src/fluent/hoc/pane.tsx @@ -0,0 +1,45 @@ +import { Body1Strong, makeStyles, tokens } from "@fluentui/react-components"; +import type { FluentIcon } from "@fluentui/react-icons"; +import type { FunctionComponent, PropsWithChildren } from "react"; + +const useStyles = makeStyles({ + rootDiv: { + flex: 1, + overflow: "hidden", + display: "flex", + flexDirection: "column", + }, + icon: { + width: tokens.fontSizeBase400, + height: tokens.fontSizeBase400, + verticalAlign: "middle", + }, + header: { + height: tokens.fontSizeBase400, + fontSize: tokens.fontSizeBase400, + color: "white", + textAlign: "center", + verticalAlign: "middle", + }, +}); + +export type PaneProps = { + title: string; + icon?: FluentIcon; +}; +export const Pane: FunctionComponent> = (props) => { + const classes = useStyles(); + return ( +
+
+ {props.icon ? ( + + ) : ( + + )} + {props.title} +
+ {props.children} +
+ ); +}; diff --git a/packages/dev/sharedUiComponents/src/fluent/primitives/accordion.tsx b/packages/dev/sharedUiComponents/src/fluent/primitives/accordion.tsx index 92540acc01a..84096305aaa 100644 --- a/packages/dev/sharedUiComponents/src/fluent/primitives/accordion.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/primitives/accordion.tsx @@ -12,6 +12,7 @@ const useStyles = makeStyles({ display: "flex", flexDirection: "column", rowGap: tokens.spacingVerticalM, + height: "100%", }, panelDiv: { display: "flex", diff --git a/packages/tools/nodeEditor/src/components/propertyTab/inputsPropertyTabComponent.tsx b/packages/tools/nodeEditor/src/components/propertyTab/inputsPropertyTabComponent.tsx index 52ca95550c9..6d1cef3dfff 100644 --- a/packages/tools/nodeEditor/src/components/propertyTab/inputsPropertyTabComponent.tsx +++ b/packages/tools/nodeEditor/src/components/propertyTab/inputsPropertyTabComponent.tsx @@ -21,7 +21,22 @@ interface IInputsPropertyTabComponentProps { lockObject: LockObject; } -export class InputsPropertyTabComponent extends React.Component { +/** + * NOTE if being used within a PropertyTabComponentBase (which is a wrapper for Accordion), call as a function rather than + * rendering as a component. This will avoid a wrapper JSX element existing before the lineContainerComponent and will ensure + * the lineContainerComponent gets properly rendered as a child of the Accordion + * @param props + * @returns + */ +export const InputProperties = (props: IInputsPropertyTabComponentProps) => { + return ( + + + + ); +}; + +class InputsPropertyContent extends React.Component { constructor(props: IInputsPropertyTabComponentProps) { super(props); } @@ -137,15 +152,11 @@ export class InputsPropertyTabComponent extends React.Component - {this.props.inputs.map((ib) => { - if (!ib.isUniform || ib.isSystemValue || !ib.name) { - return null; - } - return this.renderInputBlock(ib); - })} - - ); + return this.props.inputs.map((ib) => { + if (!ib.isUniform || ib.isSystemValue || !ib.name) { + return null; + } + return this.renderInputBlock(ib); + }); } } diff --git a/packages/tools/nodeEditor/src/components/propertyTab/propertyTabComponent.tsx b/packages/tools/nodeEditor/src/components/propertyTab/propertyTabComponent.tsx index 4b480b26367..c66deed9f45 100644 --- a/packages/tools/nodeEditor/src/components/propertyTab/propertyTabComponent.tsx +++ b/packages/tools/nodeEditor/src/components/propertyTab/propertyTabComponent.tsx @@ -18,7 +18,7 @@ import type { Observer } from "core/Misc/observable"; import { NodeMaterial } from "core/Materials/Node/nodeMaterial"; import { NodeMaterialModes } from "core/Materials/Node/Enums/nodeMaterialModes"; import { PreviewType } from "../preview/previewType"; -import { InputsPropertyTabComponent } from "./inputsPropertyTabComponent"; +import { InputProperties } from "./inputsPropertyTabComponent"; import { LogEntry } from "../log/logComponent"; import "./propertyTab.scss"; import { GraphNode } from "shared-ui-components/nodeGraphSystem/graphNode"; @@ -40,8 +40,7 @@ import { FloatLineComponent } from "shared-ui-components/lines/floatLineComponen import { SliderLineComponent } from "shared-ui-components/lines/sliderLineComponent"; import { SetToDefaultGaussianSplatting, SetToDefaultSFE } from "core/Materials/Node/nodeMaterialDefault"; import { AlphaModeOptions } from "shared-ui-components/constToOptionsMaps"; -import { ToolContext } from "shared-ui-components/fluent/hoc/fluentToolWrapper"; -import { Accordion } from "shared-ui-components/fluent/primitives/accordion"; +import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; interface IPropertyTabComponentProps { globalState: GlobalState; @@ -410,15 +409,7 @@ export class PropertyTabComponent extends React.Component - - {this.state.currentNode?.renderProperties() || this.state.currentNodePort?.node.renderProperties()} -
- ); + return this.state.currentNode?.renderProperties() || this.state.currentNodePort?.node.renderProperties(); } if (this.state.currentFrameNodePort && this.state.currentFrame) { @@ -455,209 +446,192 @@ export class PropertyTabComponent extends React.Component - - - {(context) => { - // eslint-disable-next-line @typescript-eslint/naming-convention - const Wrapper = context.useFluent ? Accordion : "div"; - return ( - - - this.props.globalState.mode} - options={modeList} - onSelect={(value) => this.changeMode(value)} - propertyName={""} - /> - this.props.globalState.engine} - options={engineList} - onSelect={(value) => { - this.props.globalState.engine = value as number; - this.forceUpdate(); - }} - propertyName={""} - /> - - this.props.globalState.hostDocument.defaultView!.open("https://doc.babylonjs.com/how_to/node_material", "_blank")} - /> - - { - switch (this.props.globalState.mode) { - case NodeMaterialModes.Material: - this.props.globalState.nodeMaterial.setToDefault(); - break; - case NodeMaterialModes.PostProcess: - this.props.globalState.nodeMaterial.setToDefaultPostProcess(); - break; - case NodeMaterialModes.SFE: - SetToDefaultSFE(this.props.globalState.nodeMaterial!); - break; - case NodeMaterialModes.Particle: - this.props.globalState.nodeMaterial.setToDefaultParticle(); - break; - case NodeMaterialModes.ProceduralTexture: - this.props.globalState.nodeMaterial.setToDefaultProceduralTexture(); - break; - case NodeMaterialModes.GaussianSplatting: - SetToDefaultGaussianSplatting(this.props.globalState.nodeMaterial); - break; - } - this.props.globalState.onResetRequiredObservable.notifyObservers(true); - this.props.globalState.onClearUndoStack.notifyObservers(); - }} - /> - - - { - this.props.globalState.onZoomToFitRequiredObservable.notifyObservers(); - }} - /> - { - this.props.globalState.onReOrganizedRequiredObservable.notifyObservers(); - }} - /> - - - DataStorage.ReadBoolean("EmbedTextures", true)} - onSelect={(value: boolean) => { - DataStorage.WriteBoolean("EmbedTextures", value); - }} - /> - { - DataStorage.WriteNumber("GridSize", value); - this.props.globalState.stateManager.onGridSizeChanged.notifyObservers(); - this.forceUpdate(); - }} - /> - DataStorage.ReadBoolean("ShowGrid", true)} - onSelect={(value: boolean) => { - DataStorage.WriteBoolean("ShowGrid", value); - this.props.globalState.stateManager.onGridSizeChanged.notifyObservers(); - }} - /> - - - this.load(file)} accept=".json" /> - { - this.save(); - }} - /> - {this.props.globalState.mode === NodeMaterialModes.SFE && ( - { - this.props.globalState.nodeMaterial.build(); - const fragment = await this.props.globalState.nodeMaterial!._getProcessedFragmentAsync(); - StringTools.DownloadAsFile(this.props.globalState.hostDocument, fragment, "nme.block.glsl"); - }} - /> - )} - { - StringTools.DownloadAsFile(this.props.globalState.hostDocument, this.props.globalState.nodeMaterial.generateCode(), "code.txt"); - }} - /> - { - this.props.globalState.nodeMaterial.build(); - StringTools.DownloadAsFile(this.props.globalState.hostDocument, this.props.globalState.nodeMaterial.compiledShaders, "shaders.txt"); - }} - /> - {this.props.globalState.customSave && ( - { - this.customSave(); - }} - /> - )} - this.loadFrame(file)} accept=".json" /> - - {!this.props.globalState.customSave && ( - - {this.props.globalState.nodeMaterial.snippetId && ( - - )} - this.loadFromSnippet()} /> - { - this.saveToSnippetServer(); - }} - /> - - )} - - this.props.globalState.stateManager.onUpdateRequiredObservable.notifyObservers(null)} - /> - this.props.globalState.stateManager.onUpdateRequiredObservable.notifyObservers(null)} - /> - - - - ); - }} - - + + + this.props.globalState.mode} + options={modeList} + onSelect={(value) => this.changeMode(value)} + propertyName={""} + /> + this.props.globalState.engine} + options={engineList} + onSelect={(value) => { + this.props.globalState.engine = value as number; + this.forceUpdate(); + }} + propertyName={""} + /> + + this.props.globalState.hostDocument.defaultView!.open("https://doc.babylonjs.com/how_to/node_material", "_blank")} + /> + + { + switch (this.props.globalState.mode) { + case NodeMaterialModes.Material: + this.props.globalState.nodeMaterial.setToDefault(); + break; + case NodeMaterialModes.PostProcess: + this.props.globalState.nodeMaterial.setToDefaultPostProcess(); + break; + case NodeMaterialModes.SFE: + SetToDefaultSFE(this.props.globalState.nodeMaterial!); + break; + case NodeMaterialModes.Particle: + this.props.globalState.nodeMaterial.setToDefaultParticle(); + break; + case NodeMaterialModes.ProceduralTexture: + this.props.globalState.nodeMaterial.setToDefaultProceduralTexture(); + break; + case NodeMaterialModes.GaussianSplatting: + SetToDefaultGaussianSplatting(this.props.globalState.nodeMaterial); + break; + } + this.props.globalState.onResetRequiredObservable.notifyObservers(true); + this.props.globalState.onClearUndoStack.notifyObservers(); + }} + /> + + + { + this.props.globalState.onZoomToFitRequiredObservable.notifyObservers(); + }} + /> + { + this.props.globalState.onReOrganizedRequiredObservable.notifyObservers(); + }} + /> + + + DataStorage.ReadBoolean("EmbedTextures", true)} + onSelect={(value: boolean) => { + DataStorage.WriteBoolean("EmbedTextures", value); + }} + /> + { + DataStorage.WriteNumber("GridSize", value); + this.props.globalState.stateManager.onGridSizeChanged.notifyObservers(); + this.forceUpdate(); + }} + /> + DataStorage.ReadBoolean("ShowGrid", true)} + onSelect={(value: boolean) => { + DataStorage.WriteBoolean("ShowGrid", value); + this.props.globalState.stateManager.onGridSizeChanged.notifyObservers(); + }} + /> + + + this.load(file)} accept=".json" /> + { + this.save(); + }} + /> + {this.props.globalState.mode === NodeMaterialModes.SFE && ( + { + this.props.globalState.nodeMaterial.build(); + const fragment = await this.props.globalState.nodeMaterial!._getProcessedFragmentAsync(); + StringTools.DownloadAsFile(this.props.globalState.hostDocument, fragment, "nme.block.glsl"); + }} + /> + )} + { + StringTools.DownloadAsFile(this.props.globalState.hostDocument, this.props.globalState.nodeMaterial.generateCode(), "code.txt"); + }} + /> + { + this.props.globalState.nodeMaterial.build(); + StringTools.DownloadAsFile(this.props.globalState.hostDocument, this.props.globalState.nodeMaterial.compiledShaders, "shaders.txt"); + }} + /> + {this.props.globalState.customSave && ( + { + this.customSave(); + }} + /> + )} + this.loadFrame(file)} accept=".json" /> + + {!this.props.globalState.customSave && ( + + {this.props.globalState.nodeMaterial.snippetId && } + this.loadFromSnippet()} /> + { + this.saveToSnippetServer(); + }} + /> + + )} + + this.props.globalState.stateManager.onUpdateRequiredObservable.notifyObservers(null)} + /> + this.props.globalState.stateManager.onUpdateRequiredObservable.notifyObservers(null)} + /> + + {InputProperties({ + globalState: this.props.globalState, + lockObject: this.props.lockObject, + inputs: this.props.globalState.nodeMaterial.getInputBlocks(), + })} + ); } } diff --git a/packages/tools/nodeEditor/src/graphEditor.tsx b/packages/tools/nodeEditor/src/graphEditor.tsx index a6abe7f47a6..da197f86241 100644 --- a/packages/tools/nodeEditor/src/graphEditor.tsx +++ b/packages/tools/nodeEditor/src/graphEditor.tsx @@ -633,7 +633,7 @@ export class GraphEditor extends React.Component - + { constructor(props: IPropertyComponentProps) { @@ -21,8 +22,8 @@ export class ColorMergerPropertyTabComponent extends React.Component - + + {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} - + ); } } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/debugNodePropertyTabComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/debugNodePropertyTabComponent.tsx index d02ab0846db..5ad01c12fc8 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/debugNodePropertyTabComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/debugNodePropertyTabComponent.tsx @@ -1,10 +1,11 @@ import * as React from "react"; import { LineContainerComponent } from "shared-ui-components/lines/lineContainerComponent"; -import { GeneralPropertyTabComponent, GenericPropertyTabComponent } from "./genericNodePropertyComponent"; +import { GenericProperties, GeneralProperties } from "./genericNodePropertyComponent"; import type { GlobalState } from "../../globalState"; import type { IPropertyComponentProps } from "shared-ui-components/nodeGraphSystem/interfaces/propertyComponentProps"; import { ButtonLineComponent } from "shared-ui-components/lines/buttonLineComponent"; import type { NodeMaterialDebugBlock } from "core/Materials/Node/Blocks/debugBlock"; +import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; export class DebugNodePropertyTabComponent extends React.Component { refreshAll() { @@ -18,13 +19,13 @@ export class DebugNodePropertyTabComponent extends React.Component - - + + {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GenericProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} this.refreshAll()} /> - + ); } } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/frameNodePortPropertyComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/frameNodePortPropertyComponent.tsx index c063bb5feb3..33f94e9e027 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/frameNodePortPropertyComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/frameNodePortPropertyComponent.tsx @@ -11,6 +11,7 @@ import { FramePortPosition } from "shared-ui-components/nodeGraphSystem/graphFra import { IsFramePortData } from "shared-ui-components/nodeGraphSystem/tools"; import type { FrameNodePort } from "shared-ui-components/nodeGraphSystem/frameNodePort"; import { ButtonLineComponent } from "shared-ui-components/lines/buttonLineComponent"; +import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; export interface IFrameNodePortPropertyTabComponentProps { stateManager: StateManager; @@ -53,34 +54,28 @@ export class FrameNodePortPropertyTabComponent extends React.Component - -
- - - {this.props.frameNodePort.framePortPosition !== FramePortPosition.Top && ( - { - this.props.frame.moveFramePortUp(this.props.frameNodePort); - }} - /> - )} + + + + {this.props.frameNodePort.framePortPosition !== FramePortPosition.Top && ( + { + this.props.frame.moveFramePortUp(this.props.frameNodePort); + }} + /> + )} - {this.props.frameNodePort.framePortPosition !== FramePortPosition.Bottom && ( - { - this.props.frame.moveFramePortDown(this.props.frameNodePort); - }} - /> - )} - -
- + {this.props.frameNodePort.framePortPosition !== FramePortPosition.Bottom && ( + { + this.props.frame.moveFramePortDown(this.props.frameNodePort); + }} + /> + )} + + ); } } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/framePropertyComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/framePropertyComponent.tsx index a7da3d50304..b8dbee11f1e 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/framePropertyComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/framePropertyComponent.tsx @@ -3,13 +3,14 @@ import { LineContainerComponent } from "shared-ui-components/lines/lineContainer import type { GlobalState } from "../../globalState"; import type { Nullable } from "core/types"; import type { Observer } from "core/Misc/observable"; -import { InputsPropertyTabComponent } from "../../components/propertyTab/inputsPropertyTabComponent"; +import { InputProperties } from "../../components/propertyTab/inputsPropertyTabComponent"; import type { InputBlock } from "core/Materials/Node/Blocks/Input/inputBlock"; import { TextInputLineComponent } from "shared-ui-components/lines/textInputLineComponent"; import type { GraphFrame } from "shared-ui-components/nodeGraphSystem/graphFrame"; import type { NodeMaterialBlock } from "core/Materials/Node/nodeMaterialBlock"; import { Color3LineComponent } from "shared-ui-components/lines/color3LineComponent"; import { ButtonLineComponent } from "shared-ui-components/lines/buttonLineComponent"; +import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; export interface IFramePropertyTabComponentProps { globalState: GlobalState; @@ -48,46 +49,40 @@ export class FramePropertyTabComponent extends React.Component - -
- - - - - {!this.props.frame.isCollapsed && ( - { - this.props.frame.isCollapsed = true; - }} - /> - )} - {this.props.frame.isCollapsed && ( - { - this.props.frame.isCollapsed = false; - }} - /> - )} + + + + + + {!this.props.frame.isCollapsed && ( { - this.props.frame.export(); + this.props.frame.isCollapsed = true; }} /> - - -
- + )} + {this.props.frame.isCollapsed && ( + { + this.props.frame.isCollapsed = false; + }} + /> + )} + { + this.props.frame.export(); + }} + /> + + {InputProperties({ + globalState: this.props.globalState, + lockObject: this.props.globalState.lockObject, + inputs: configurableInputBlocks, + })} + ); } } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/genericNodePropertyComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/genericNodePropertyComponent.tsx index 7fe215c7b0d..f2b43548bdc 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/genericNodePropertyComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/genericNodePropertyComponent.tsx @@ -14,23 +14,63 @@ import { TextLineComponent } from "shared-ui-components/lines/textLineComponent" import { FloatLineComponent } from "shared-ui-components/lines/floatLineComponent"; import { SliderLineComponent } from "shared-ui-components/lines/sliderLineComponent"; import { ForceRebuild } from "shared-ui-components/nodeGraphSystem/automaticProperties"; +import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; -export class GenericPropertyComponent extends React.Component { +export class DefaultPropertyTabComponent extends React.Component { constructor(props: IPropertyComponentProps) { super(props); } override render() { return ( - <> - - - + + {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GenericProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + ); } } -export class GeneralPropertyTabComponent extends React.Component { +/** + * NOTE if being used within a PropertyTabComponentBase (which is a wrapper for Accordion), call as a function rather than + * rendering as a component. This will avoid a wrapper JSX element existing before the lineContainerComponent and will ensure + * the lineContainerComponent gets properly rendered as a child of the Accordion + * @param props + * @returns + */ +export const GeneralProperties = (props: IPropertyComponentProps) => ( + + + +); + +/** + * NOTE if being used within a PropertyTabComponentBase (which is a wrapper for Accordion), call as a function rather than + * rendering as a component. This will avoid a wrapper JSX element existing before the lineContainerComponent and will ensure + * the lineContainerComponent gets properly rendered as a child of the Accordion + * @param props + * @returns + */ +export const GenericProperties = (props: IPropertyComponentProps) => { + const content = GetGenericPropertiesContent(props); + if (!content) { + return <>; + } + + const { groups, componentList } = content; + + return ( + <> + {groups.map((group) => ( + + {componentList[group]} + + ))} + + ); +}; + +class GeneralPropertiesContent extends React.Component { constructor(props: IPropertyComponentProps) { super(props); } @@ -46,187 +86,177 @@ export class GeneralPropertyTabComponent extends React.Component - - {(!block.isInput || !(block as InputBlock).isAttribute) && ( - this.props.stateManager.onUpdateRequiredObservable.notifyObservers(block)} - throttlePropertyChangedNotification={true} - validator={(newName) => { - if (!block.validateBlockName(newName)) { - this.props.stateManager.onErrorMessageDialogRequiredObservable.notifyObservers(`"${newName}" is a reserved name, please choose another`); - return false; - } - return true; - }} - /> - )} - {block._originalTargetIsNeutral && ( - { - this.forceUpdate(); - - this.props.stateManager.onUpdateRequiredObservable.notifyObservers(block); - this.props.stateManager.onRebuildRequiredObservable.notifyObservers(); - }} - /> - )} - {!block._originalTargetIsNeutral && } - + {(!block.isInput || !(block as InputBlock).isAttribute) && ( this.props.stateManager.onUpdateRequiredObservable.notifyObservers(block)} throttlePropertyChangedNotification={true} + validator={(newName) => { + if (!block.validateBlockName(newName)) { + this.props.stateManager.onErrorMessageDialogRequiredObservable.notifyObservers(`"${newName}" is a reserved name, please choose another`); + return false; + } + return true; + }} /> - + )} + {block._originalTargetIsNeutral && ( + { + this.forceUpdate(); + + this.props.stateManager.onUpdateRequiredObservable.notifyObservers(block); + this.props.stateManager.onRebuildRequiredObservable.notifyObservers(); + }} + /> + )} + {!block._originalTargetIsNeutral && } + + this.props.stateManager.onUpdateRequiredObservable.notifyObservers(block)} + throttlePropertyChangedNotification={true} + /> ); } } -export class GenericPropertyTabComponent extends React.Component { - constructor(props: IPropertyComponentProps) { - super(props); +type GenericContent = { + componentList: { [groupName: string]: JSX.Element[] }; + groups: string[]; +}; +const GetGenericPropertiesContent = (props: IPropertyComponentProps): GenericContent | undefined => { + const block = props.nodeData.data as NodeMaterialBlock, + propStore: IPropertyDescriptionForEdition[] = (block as any)._propStore; + + if (!propStore) { + return undefined; } - override render() { - const block = this.props.nodeData.data as NodeMaterialBlock, - propStore: IPropertyDescriptionForEdition[] = (block as any)._propStore; + const componentList: { [groupName: string]: JSX.Element[] } = {}, + groups: string[] = []; - if (!propStore) { - return <>; - } + const classes: string[] = []; - const componentList: { [groupName: string]: JSX.Element[] } = {}, - groups: string[] = []; + let proto = Object.getPrototypeOf(block); + while (proto && proto.getClassName) { + classes.push(proto.getClassName()); + proto = Object.getPrototypeOf(proto); + } - const classes: string[] = []; + for (const { propertyName, displayName, type, groupName, options, className } of propStore) { + let components = componentList[groupName]; - let proto = Object.getPrototypeOf(block); - while (proto && proto.getClassName) { - classes.push(proto.getClassName()); - proto = Object.getPrototypeOf(proto); + if (options.embedded || classes.indexOf(className) === -1) { + continue; } - for (const { propertyName, displayName, type, groupName, options, className } of propStore) { - let components = componentList[groupName]; - - if (options.embedded || classes.indexOf(className) === -1) { - continue; - } + if (!components) { + components = []; + componentList[groupName] = components; + groups.push(groupName); + } - if (!components) { - components = []; - componentList[groupName] = components; - groups.push(groupName); + switch (type) { + case PropertyTypeForEdition.Boolean: { + components.push( + ForceRebuild(block, props.stateManager, propertyName, options.notifiers)} + /> + ); + break; } - - switch (type) { - case PropertyTypeForEdition.Boolean: { - components.push( - ForceRebuild(block, this.props.stateManager, propertyName, options.notifiers)} - /> - ); - break; - } - case PropertyTypeForEdition.Float: { - const cantDisplaySlider = isNaN(options.min as number) || isNaN(options.max as number) || options.min === options.max; - if (cantDisplaySlider) { - components.push( - ForceRebuild(block, this.props.stateManager, propertyName, options.notifiers)} - /> - ); - } else { - components.push( - ForceRebuild(block, this.props.stateManager, propertyName, options.notifiers)} - /> - ); - } - break; - } - case PropertyTypeForEdition.Int: { + case PropertyTypeForEdition.Float: { + const cantDisplaySlider = isNaN(options.min as number) || isNaN(options.max as number) || options.min === options.max; + if (cantDisplaySlider) { components.push( ForceRebuild(block, this.props.stateManager, propertyName, options.notifiers)} + onChange={() => ForceRebuild(block, props.stateManager, propertyName, options.notifiers)} /> ); - break; - } - case PropertyTypeForEdition.Vector2: { - components.push( - ForceRebuild(block, this.props.stateManager, propertyName, options.notifiers)} - /> - ); - break; - } - case PropertyTypeForEdition.List: { + } else { components.push( - ForceRebuild(block, this.props.stateManager, propertyName, options.notifiers)} + step={Math.abs((options.max as number) - (options.min as number)) / 100.0} + minimum={Math.min(options.min as number, options.max as number)} + maximum={options.max as number} + onChange={() => ForceRebuild(block, props.stateManager, propertyName, options.notifiers)} /> ); - break; } + break; + } + case PropertyTypeForEdition.Int: { + components.push( + ForceRebuild(block, props.stateManager, propertyName, options.notifiers)} + /> + ); + break; + } + case PropertyTypeForEdition.Vector2: { + components.push( + ForceRebuild(block, props.stateManager, propertyName, options.notifiers)} + /> + ); + break; + } + case PropertyTypeForEdition.List: { + components.push( + ForceRebuild(block, props.stateManager, propertyName, options.notifiers)} + /> + ); + break; } } - - return ( - <> - {groups.map((group) => ( - - {componentList[group]} - - ))} - - ); } -} + return { + componentList, + groups, + }; +}; diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/gradientNodePropertyComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/gradientNodePropertyComponent.tsx index b4546b5d727..9de06ec82b0 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/gradientNodePropertyComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/gradientNodePropertyComponent.tsx @@ -4,13 +4,14 @@ import type { GradientBlock } from "core/Materials/Node/Blocks/gradientBlock"; import { GradientBlockColorStep } from "core/Materials/Node/Blocks/gradientBlock"; import { GradientStepComponent } from "./gradientStepComponent"; import { Color3 } from "core/Maths/math.color"; -import { GeneralPropertyTabComponent } from "./genericNodePropertyComponent"; +import { GeneralProperties } from "./genericNodePropertyComponent"; import type { Nullable } from "core/types"; import type { Observer } from "core/Misc/observable"; import type { IPropertyComponentProps } from "shared-ui-components/nodeGraphSystem/interfaces/propertyComponentProps"; import { ButtonLineComponent } from "shared-ui-components/lines/buttonLineComponent"; import type { InputBlock } from "core/Materials/Node/Blocks/Input/inputBlock"; import { OptionsLine } from "shared-ui-components/lines/optionsLineComponent"; +import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; export class GradientPropertyTabComponent extends React.Component { private _onValueChangedObserver: Nullable>; @@ -102,8 +103,8 @@ export class GradientPropertyTabComponent extends React.Component - + + {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} - + ); } } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/imageSourcePropertyTabComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/imageSourcePropertyTabComponent.tsx index d39c3077f0e..2888c6086b4 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/imageSourcePropertyTabComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/imageSourcePropertyTabComponent.tsx @@ -6,7 +6,7 @@ import { LineContainerComponent } from "shared-ui-components/lines/lineContainer import { CheckBoxLineComponent } from "../../sharedComponents/checkBoxLineComponent"; import { Texture } from "core/Materials/Textures/texture"; import type { ImageSourceBlock } from "core/Materials/Node/Blocks/Dual/imageSourceBlock"; -import { GeneralPropertyTabComponent, GenericPropertyTabComponent } from "./genericNodePropertyComponent"; +import { GeneralProperties, GenericProperties } from "./genericNodePropertyComponent"; import type { NodeMaterialBlock } from "core/Materials/Node/nodeMaterialBlock"; import type { GlobalState } from "../../globalState"; import { TextInputLineComponent } from "shared-ui-components/lines/textInputLineComponent"; @@ -15,6 +15,7 @@ import { ButtonLineComponent } from "shared-ui-components/lines/buttonLineCompon import { OptionsLine } from "shared-ui-components/lines/optionsLineComponent"; import { FloatLineComponent } from "shared-ui-components/lines/floatLineComponent"; import { SliderLineComponent } from "shared-ui-components/lines/sliderLineComponent"; +import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; export class ImageSourcePropertyTabComponent extends React.Component { get imageSourceBlock(): ImageSourceBlock { @@ -149,8 +150,8 @@ export class ImageSourcePropertyTabComponent extends React.Component - + + {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} {texture && texture.updateSamplingMode && ( this.removeTexture()} />} - - + {GenericProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + ); } } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/inputNodePropertyComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/inputNodePropertyComponent.tsx index 975d6ada28e..8ef19c9c39f 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/inputNodePropertyComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/inputNodePropertyComponent.tsx @@ -11,7 +11,7 @@ import { NodeMaterialBlockConnectionPointTypes } from "core/Materials/Node/Enums import { NodeMaterialSystemValues } from "core/Materials/Node/Enums/nodeMaterialSystemValues"; import { AnimatedInputBlockTypes } from "core/Materials/Node/Blocks/Input/animatedInputBlockTypes"; import type { InputBlock } from "core/Materials/Node/Blocks/Input/inputBlock"; -import { GeneralPropertyTabComponent } from "./genericNodePropertyComponent"; +import { GeneralProperties } from "./genericNodePropertyComponent"; import { CheckBoxLineComponent } from "../../sharedComponents/checkBoxLineComponent"; import { Color4PropertyTabComponent } from "../../components/propertyTab/properties/color4PropertyTabComponent"; import type { Nullable } from "core/types"; @@ -21,6 +21,7 @@ import type { IPropertyComponentProps } from "shared-ui-components/nodeGraphSyst import { OptionsLine } from "shared-ui-components/lines/optionsLineComponent"; import { FloatLineComponent } from "shared-ui-components/lines/floatLineComponent"; import { SliderLineComponent } from "shared-ui-components/lines/sliderLineComponent"; +import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; export class InputPropertyTabComponent extends React.Component { private _onValueChangedObserver: Nullable>; @@ -273,8 +274,8 @@ export class InputPropertyTabComponent extends React.Component - + + {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} {inputBlock.isUniform && !inputBlock.isSystemValue && inputBlock.animationType === AnimatedInputBlockTypes.None && ( )} - + ); } } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/lightInformationPropertyTabComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/lightInformationPropertyTabComponent.tsx index 31936dadcef..534eb97564c 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/lightInformationPropertyTabComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/lightInformationPropertyTabComponent.tsx @@ -1,11 +1,12 @@ import * as React from "react"; import { LineContainerComponent } from "shared-ui-components/lines/lineContainerComponent"; import type { LightInformationBlock } from "core/Materials/Node/Blocks/Vertex/lightInformationBlock"; -import { GeneralPropertyTabComponent } from "./genericNodePropertyComponent"; +import { GeneralProperties } from "./genericNodePropertyComponent"; import type { Light } from "core/Lights/light"; import type { GlobalState } from "../../globalState"; import type { IPropertyComponentProps } from "shared-ui-components/nodeGraphSystem/interfaces/propertyComponentProps"; import { OptionsLine } from "shared-ui-components/lines/optionsLineComponent"; +import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; export class LightInformationPropertyTabComponent extends React.Component { override render() { @@ -17,8 +18,8 @@ export class LightInformationPropertyTabComponent extends React.Component - + + {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} - + ); } } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/lightPropertyTabComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/lightPropertyTabComponent.tsx index 9d04681e86a..22221824277 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/lightPropertyTabComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/lightPropertyTabComponent.tsx @@ -1,11 +1,12 @@ import * as React from "react"; import { LineContainerComponent } from "shared-ui-components/lines/lineContainerComponent"; import type { LightBlock } from "core/Materials/Node/Blocks/Dual/lightBlock"; -import { GeneralPropertyTabComponent } from "./genericNodePropertyComponent"; +import { GeneralProperties } from "./genericNodePropertyComponent"; import type { Light } from "core/Lights/light"; import type { GlobalState } from "../../globalState"; import type { IPropertyComponentProps } from "shared-ui-components/nodeGraphSystem/interfaces/propertyComponentProps"; import { OptionsLine } from "shared-ui-components/lines/optionsLineComponent"; +import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; export class LightPropertyTabComponent extends React.Component { override render() { @@ -19,8 +20,8 @@ export class LightPropertyTabComponent extends React.Component - + + {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} - + ); } } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/nodePortPropertyComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/nodePortPropertyComponent.tsx index a2ffd0e8f92..af3bbd78461 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/nodePortPropertyComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/nodePortPropertyComponent.tsx @@ -8,6 +8,7 @@ import { TextLineComponent } from "shared-ui-components/lines/textLineComponent" import type { NodeMaterialConnectionPoint } from "core/Materials"; import { NodeMaterialBlockConnectionPointTypes } from "core/Materials"; import { GetListOfAcceptedTypes } from "shared-ui-components/nodeGraphSystem/tools"; +import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; export interface IFrameNodePortPropertyTabComponentProps { stateManager: StateManager; @@ -57,15 +58,9 @@ export class NodePortPropertyTabComponent extends React.Component - -
- {info} -
- + + {info} + ); } } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/teleportOutNodePropertyComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/teleportOutNodePropertyComponent.tsx index db5630e14e6..e9fc8a6aedd 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/teleportOutNodePropertyComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/teleportOutNodePropertyComponent.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { GeneralPropertyTabComponent } from "./genericNodePropertyComponent"; +import { GeneralProperties } from "./genericNodePropertyComponent"; import type { IPropertyComponentProps } from "shared-ui-components/nodeGraphSystem/interfaces/propertyComponentProps"; import type { Observer } from "core/Misc/observable"; import type { Nullable } from "core/types"; @@ -8,6 +8,7 @@ import { OptionsLine } from "shared-ui-components/lines/optionsLineComponent"; import type { NodeMaterialTeleportOutBlock } from "core/Materials/Node/Blocks/Teleport/teleportOutBlock"; import type { NodeMaterialTeleportInBlock } from "core/Materials/Node/Blocks/Teleport/teleportInBlock"; import type { GlobalState } from "../../globalState"; +import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; export class TeleportOutPropertyTabComponent extends React.Component { private _onUpdateRequiredObserver: Nullable>; @@ -47,8 +48,8 @@ export class TeleportOutPropertyTabComponent extends React.Component - + + {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} - + ); } } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/texturePropertyTabComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/texturePropertyTabComponent.tsx index 744ef2a7c90..b880d306c42 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/texturePropertyTabComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/texturePropertyTabComponent.tsx @@ -12,7 +12,7 @@ import { RefractionBlock } from "core/Materials/Node/Blocks/PBR/refractionBlock" import type { TextureBlock } from "core/Materials/Node/Blocks/Dual/textureBlock"; import { CurrentScreenBlock } from "core/Materials/Node/Blocks/Dual/currentScreenBlock"; import { ParticleTextureBlock } from "core/Materials/Node/Blocks/Particle/particleTextureBlock"; -import { GeneralPropertyTabComponent, GenericPropertyTabComponent } from "./genericNodePropertyComponent"; +import { GeneralProperties, GenericProperties } from "./genericNodePropertyComponent"; import { NodeMaterialModes } from "core/Materials/Node/Enums/nodeMaterialModes"; import type { NodeMaterialBlock } from "core/Materials/Node/nodeMaterialBlock"; import type { GlobalState } from "../../globalState"; @@ -23,6 +23,7 @@ import { OptionsLine } from "shared-ui-components/lines/optionsLineComponent"; import { FloatLineComponent } from "shared-ui-components/lines/floatLineComponent"; import { SliderLineComponent } from "shared-ui-components/lines/sliderLineComponent"; import type { TriPlanarBlock } from "core/Materials/Node/Blocks/triPlanarBlock"; +import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; type ReflectionTexture = ReflectionTextureBlock | ReflectionBlock | RefractionBlock; @@ -239,8 +240,8 @@ export class TexturePropertyTabComponent extends React.Component - + + {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} this.removeTexture()} />} )} - - + {GenericProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + ); } } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/vectorMergerPropertyComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/vectorMergerPropertyComponent.tsx index 3024c96738b..cf7edcc4b69 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/vectorMergerPropertyComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/vectorMergerPropertyComponent.tsx @@ -1,9 +1,10 @@ import * as React from "react"; import { LineContainerComponent } from "shared-ui-components/lines/lineContainerComponent"; -import { GeneralPropertyTabComponent } from "./genericNodePropertyComponent"; +import { GeneralProperties } from "./genericNodePropertyComponent"; import type { VectorMergerBlock } from "core/Materials/Node/Blocks/vectorMergerBlock"; import type { IPropertyComponentProps } from "shared-ui-components/nodeGraphSystem/interfaces/propertyComponentProps"; import { OptionsLine } from "shared-ui-components/lines/optionsLineComponent"; +import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; export class VectorMergerPropertyTabComponent extends React.Component { constructor(props: IPropertyComponentProps) { @@ -21,8 +22,8 @@ export class VectorMergerPropertyTabComponent extends React.Component - + + {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} - + ); } } diff --git a/packages/tools/nodeEditor/src/graphSystem/registerToPropertyLedger.ts b/packages/tools/nodeEditor/src/graphSystem/registerToPropertyLedger.ts index b34e59ba5d3..d62aca4014b 100644 --- a/packages/tools/nodeEditor/src/graphSystem/registerToPropertyLedger.ts +++ b/packages/tools/nodeEditor/src/graphSystem/registerToPropertyLedger.ts @@ -1,6 +1,6 @@ import { PropertyLedger } from "shared-ui-components/nodeGraphSystem/propertyLedger"; import { ColorMergerPropertyTabComponent } from "./properties/colorMergerPropertyComponent"; -import { GenericPropertyComponent } from "./properties/genericNodePropertyComponent"; +import { DefaultPropertyTabComponent } from "./properties/genericNodePropertyComponent"; import { GradientPropertyTabComponent } from "./properties/gradientNodePropertyComponent"; import { ImageSourcePropertyTabComponent } from "./properties/imageSourcePropertyTabComponent"; import { InputPropertyTabComponent } from "./properties/inputNodePropertyComponent"; @@ -11,7 +11,7 @@ import { TeleportOutPropertyTabComponent } from "./properties/teleportOutNodePro import { DebugNodePropertyTabComponent } from "./properties/debugNodePropertyTabComponent"; export const RegisterToPropertyTabManagers = () => { - PropertyLedger.DefaultControl = GenericPropertyComponent; + PropertyLedger.DefaultControl = DefaultPropertyTabComponent; PropertyLedger.RegisteredControls["NodeMaterialDebugBlock"] = DebugNodePropertyTabComponent; PropertyLedger.RegisteredControls["InputBlock"] = InputPropertyTabComponent; PropertyLedger.RegisteredControls["GradientBlock"] = GradientPropertyTabComponent; From 0d3c61a50f3449350d579f3073d031ff4da4fc3c Mon Sep 17 00:00:00 2001 From: Georgina Date: Thu, 3 Jul 2025 13:42:58 -0400 Subject: [PATCH 06/12] nit --- packages/dev/sharedUiComponents/src/fluent/hoc/pane.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dev/sharedUiComponents/src/fluent/hoc/pane.tsx b/packages/dev/sharedUiComponents/src/fluent/hoc/pane.tsx index 4de87397bfc..edfb2454cba 100644 --- a/packages/dev/sharedUiComponents/src/fluent/hoc/pane.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/hoc/pane.tsx @@ -17,7 +17,6 @@ const useStyles = makeStyles({ header: { height: tokens.fontSizeBase400, fontSize: tokens.fontSizeBase400, - color: "white", textAlign: "center", verticalAlign: "middle", }, From 0320ce50a3bbeef1a21cdd363aa209e05aaf31d9 Mon Sep 17 00:00:00 2001 From: Georgina Date: Thu, 3 Jul 2025 14:18:19 -0400 Subject: [PATCH 07/12] fix build --- .../src/components/propertyTabComponentBase.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/dev/sharedUiComponents/src/components/propertyTabComponentBase.tsx b/packages/dev/sharedUiComponents/src/components/propertyTabComponentBase.tsx index 385d2867bf8..2d8f1ca9d7f 100644 --- a/packages/dev/sharedUiComponents/src/components/propertyTabComponentBase.tsx +++ b/packages/dev/sharedUiComponents/src/components/propertyTabComponentBase.tsx @@ -1,4 +1,5 @@ -import { useContext, type FunctionComponent, type PropsWithChildren } from "react"; +import { useContext } from "react"; +import type { FunctionComponent, PropsWithChildren } from "react"; import { ToolContext } from "../fluent/hoc/fluentToolWrapper"; import { Pane } from "../fluent/hoc/pane"; import { Accordion } from "shared-ui-components/fluent/primitives/accordion"; From 1a097cb05e10820398ff39a1978891f5390990e7 Mon Sep 17 00:00:00 2001 From: Georgina Date: Thu, 3 Jul 2025 16:47:24 -0400 Subject: [PATCH 08/12] rename getter to be clear that its a function, pr comments --- .../components/propertyTabComponentBase.tsx | 2 +- .../src/fluent/hoc/propertyLine.tsx | 2 +- .../src/lines/draggableLineComponent.tsx | 22 +---- .../src/lines/lineContainerComponent.tsx | 4 +- .../src/split/splitContainer.tsx | 1 + .../components/nodeList/nodeListComponent.tsx | 8 +- .../colorMergerPropertyComponent.tsx | 4 +- .../debugNodePropertyTabComponent.tsx | 6 +- .../genericNodePropertyComponent.tsx | 93 ++++++++++--------- .../gradientNodePropertyComponent.tsx | 4 +- .../imageSourcePropertyTabComponent.tsx | 6 +- .../properties/inputNodePropertyComponent.tsx | 4 +- .../lightInformationPropertyTabComponent.tsx | 4 +- .../properties/lightPropertyTabComponent.tsx | 4 +- .../teleportOutNodePropertyComponent.tsx | 4 +- .../texturePropertyTabComponent.tsx | 6 +- .../vectorMergerPropertyComponent.tsx | 4 +- 17 files changed, 80 insertions(+), 98 deletions(-) diff --git a/packages/dev/sharedUiComponents/src/components/propertyTabComponentBase.tsx b/packages/dev/sharedUiComponents/src/components/propertyTabComponentBase.tsx index 2d8f1ca9d7f..af4f612ec0d 100644 --- a/packages/dev/sharedUiComponents/src/components/propertyTabComponentBase.tsx +++ b/packages/dev/sharedUiComponents/src/components/propertyTabComponentBase.tsx @@ -2,7 +2,7 @@ import { useContext } from "react"; import type { FunctionComponent, PropsWithChildren } from "react"; import { ToolContext } from "../fluent/hoc/fluentToolWrapper"; import { Pane } from "../fluent/hoc/pane"; -import { Accordion } from "shared-ui-components/fluent/primitives/accordion"; +import { Accordion } from "../fluent/primitives/accordion"; /** * A wrapper component for the property tab that provides a consistent layout and styling. diff --git a/packages/dev/sharedUiComponents/src/fluent/hoc/propertyLine.tsx b/packages/dev/sharedUiComponents/src/fluent/hoc/propertyLine.tsx index 449afeb4c93..eed22909864 100644 --- a/packages/dev/sharedUiComponents/src/fluent/hoc/propertyLine.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/hoc/propertyLine.tsx @@ -81,7 +81,7 @@ export type PropertyLineProps = { docLink?: string; }; -export const LineContainer = forwardRef>((props, ref) => { +export const LineContainer = forwardRef>>((props, ref) => { const classes = usePropertyLineStyles(); return (
diff --git a/packages/dev/sharedUiComponents/src/lines/draggableLineComponent.tsx b/packages/dev/sharedUiComponents/src/lines/draggableLineComponent.tsx index 1dca4013844..47d9268d5cf 100644 --- a/packages/dev/sharedUiComponents/src/lines/draggableLineComponent.tsx +++ b/packages/dev/sharedUiComponents/src/lines/draggableLineComponent.tsx @@ -1,6 +1,4 @@ import * as React from "react"; -import { ToolContext } from "shared-ui-components/fluent/hoc/fluentToolWrapper"; -import { LineContainer } from "shared-ui-components/fluent/hoc/propertyLine"; export interface IButtonLineComponentProps { format: string; @@ -13,21 +11,7 @@ export class DraggableLineComponent extends React.Component { - event.dataTransfer.setData(this.props.format, this.props.data); - }} - > - {this.props.data.replace("Block", "")} - - ); - } - - renderOriginal() { + override render() { return (
); } - - override render() { - return {({ useFluent }) => (useFluent ? this.renderFluent() : this.renderOriginal())}; - } } diff --git a/packages/dev/sharedUiComponents/src/lines/lineContainerComponent.tsx b/packages/dev/sharedUiComponents/src/lines/lineContainerComponent.tsx index 12080a95c48..7a7ef0d91bb 100644 --- a/packages/dev/sharedUiComponents/src/lines/lineContainerComponent.tsx +++ b/packages/dev/sharedUiComponents/src/lines/lineContainerComponent.tsx @@ -2,8 +2,8 @@ import * as React from "react"; import { DataStorage } from "core/Misc/dataStorage"; import type { ISelectedLineContainer } from "./iSelectedLineContainer"; import downArrow from "./downArrow.svg"; -import { AccordionSection } from "shared-ui-components/fluent/primitives/accordion"; -import { ToolContext } from "shared-ui-components/fluent/hoc/fluentToolWrapper"; +import { AccordionSection } from "../fluent/primitives/accordion"; +import { ToolContext } from "../fluent/hoc/fluentToolWrapper"; interface ILineContainerComponentProps { selection?: ISelectedLineContainer; diff --git a/packages/dev/sharedUiComponents/src/split/splitContainer.tsx b/packages/dev/sharedUiComponents/src/split/splitContainer.tsx index c26c60a3a45..fd22103de78 100644 --- a/packages/dev/sharedUiComponents/src/split/splitContainer.tsx +++ b/packages/dev/sharedUiComponents/src/split/splitContainer.tsx @@ -319,6 +319,7 @@ export const SplitContainer: React.FC> =
props.onPointerDown && props.onPointerDown(evt)} onPointerMove={(evt) => props.onPointerMove && props.onPointerMove(evt)} diff --git a/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx b/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx index bc1c96e8317..c607d0be40b 100644 --- a/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx +++ b/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx @@ -330,16 +330,16 @@ export class NodeListComponent extends React.Component - this.filterContent(val.toString())} /> + this.filterContent(val.toString())} /> {blockMenu}
); } - renderOriginal(blockMenu: any[]) { + renderOriginal(blockMenu: JSX.Element[]) { return (
@@ -591,7 +591,7 @@ export class NodeListComponent extends React.Component !this.state.filter || b.toLowerCase().indexOf(this.state.filter.toLowerCase()) !== -1) diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/colorMergerPropertyComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/colorMergerPropertyComponent.tsx index 80a0031a2c5..943e6fae153 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/colorMergerPropertyComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/colorMergerPropertyComponent.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { LineContainerComponent } from "shared-ui-components/lines/lineContainerComponent"; -import { GeneralProperties } from "./genericNodePropertyComponent"; +import { GetGeneralProperties } from "./genericNodePropertyComponent"; import type { ColorMergerBlock } from "core/Materials/Node/Blocks/colorMergerBlock"; import type { IPropertyComponentProps } from "shared-ui-components/nodeGraphSystem/interfaces/propertyComponentProps"; import { OptionsLine } from "shared-ui-components/lines/optionsLineComponent"; @@ -23,7 +23,7 @@ export class ColorMergerPropertyTabComponent extends React.Component - {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} - {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} - {GenericProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGenericProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} this.refreshAll()} /> diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/genericNodePropertyComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/genericNodePropertyComponent.tsx index f2b43548bdc..7b7927411cf 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/genericNodePropertyComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/genericNodePropertyComponent.tsx @@ -15,6 +15,8 @@ import { FloatLineComponent } from "shared-ui-components/lines/floatLineComponen import { SliderLineComponent } from "shared-ui-components/lines/sliderLineComponent"; import { ForceRebuild } from "shared-ui-components/nodeGraphSystem/automaticProperties"; import { PropertyTabComponentBase } from "shared-ui-components/components/propertyTabComponentBase"; +import type { StateManager } from "shared-ui-components/nodeGraphSystem/stateManager"; +import type { INodeData } from "shared-ui-components/nodeGraphSystem/interfaces/nodeData"; export class DefaultPropertyTabComponent extends React.Component { constructor(props: IPropertyComponentProps) { @@ -24,51 +26,26 @@ export class DefaultPropertyTabComponent extends React.Component - {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} - {GenericProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGenericProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} ); } } /** - * NOTE if being used within a PropertyTabComponentBase (which is a wrapper for Accordion), call as a function rather than - * rendering as a component. This will avoid a wrapper JSX element existing before the lineContainerComponent and will ensure + * NOTE This is intentionally a function to avoid another wrapper JSX element around the lineContainerComponent, and will ensure * the lineContainerComponent gets properly rendered as a child of the Accordion * @param props * @returns */ -export const GeneralProperties = (props: IPropertyComponentProps) => ( - - - -); - -/** - * NOTE if being used within a PropertyTabComponentBase (which is a wrapper for Accordion), call as a function rather than - * rendering as a component. This will avoid a wrapper JSX element existing before the lineContainerComponent and will ensure - * the lineContainerComponent gets properly rendered as a child of the Accordion - * @param props - * @returns - */ -export const GenericProperties = (props: IPropertyComponentProps) => { - const content = GetGenericPropertiesContent(props); - if (!content) { - return <>; - } - - const { groups, componentList } = content; - +export function GetGeneralProperties(props: IPropertyComponentProps) { return ( - <> - {groups.map((group) => ( - - {componentList[group]} - - ))} - + + + ); -}; +} class GeneralPropertiesContent extends React.Component { constructor(props: IPropertyComponentProps) { @@ -132,12 +109,36 @@ class GeneralPropertiesContent extends React.Component } } +/** + * NOTE This is intentionally a function to avoid another wrapper JSX element around the lineContainerComponent, and will ensure + * the lineContainerComponent gets properly rendered as a child of the Accordion + * @param props + * @returns + */ +export function GetGenericProperties(props: IPropertyComponentProps) { + const content = GetGenericPropertiesContent(props.stateManager, props.nodeData); + if (!content) { + return <>; + } + const { groups, componentList } = content; + + return ( + <> + {groups.map((group) => ( + + {componentList[group]} + + ))} + + ); +} + type GenericContent = { componentList: { [groupName: string]: JSX.Element[] }; groups: string[]; }; -const GetGenericPropertiesContent = (props: IPropertyComponentProps): GenericContent | undefined => { - const block = props.nodeData.data as NodeMaterialBlock, +function GetGenericPropertiesContent(stateManager: StateManager, nodeData: INodeData): GenericContent | undefined { + const block = nodeData.data as NodeMaterialBlock, propStore: IPropertyDescriptionForEdition[] = (block as any)._propStore; if (!propStore) { @@ -176,7 +177,7 @@ const GetGenericPropertiesContent = (props: IPropertyComponentProps): GenericCon label={displayName} target={block} propertyName={propertyName} - onValueChanged={() => ForceRebuild(block, props.stateManager, propertyName, options.notifiers)} + onValueChanged={() => ForceRebuild(block, stateManager, propertyName, options.notifiers)} /> ); break; @@ -187,25 +188,25 @@ const GetGenericPropertiesContent = (props: IPropertyComponentProps): GenericCon components.push( ForceRebuild(block, props.stateManager, propertyName, options.notifiers)} + onChange={() => ForceRebuild(block, stateManager, propertyName, options.notifiers)} /> ); } else { components.push( ForceRebuild(block, props.stateManager, propertyName, options.notifiers)} + onChange={() => ForceRebuild(block, stateManager, propertyName, options.notifiers)} /> ); } @@ -215,14 +216,14 @@ const GetGenericPropertiesContent = (props: IPropertyComponentProps): GenericCon components.push( ForceRebuild(block, props.stateManager, propertyName, options.notifiers)} + onChange={() => ForceRebuild(block, stateManager, propertyName, options.notifiers)} /> ); break; @@ -231,11 +232,11 @@ const GetGenericPropertiesContent = (props: IPropertyComponentProps): GenericCon components.push( ForceRebuild(block, props.stateManager, propertyName, options.notifiers)} + onChange={() => ForceRebuild(block, stateManager, propertyName, options.notifiers)} /> ); break; @@ -248,7 +249,7 @@ const GetGenericPropertiesContent = (props: IPropertyComponentProps): GenericCon options={options.options as IEditablePropertyListOption[]} target={block} propertyName={propertyName} - onSelect={() => ForceRebuild(block, props.stateManager, propertyName, options.notifiers)} + onSelect={() => ForceRebuild(block, stateManager, propertyName, options.notifiers)} /> ); break; @@ -259,4 +260,4 @@ const GetGenericPropertiesContent = (props: IPropertyComponentProps): GenericCon componentList, groups, }; -}; +} diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/gradientNodePropertyComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/gradientNodePropertyComponent.tsx index 9de06ec82b0..175e60f617f 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/gradientNodePropertyComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/gradientNodePropertyComponent.tsx @@ -4,7 +4,7 @@ import type { GradientBlock } from "core/Materials/Node/Blocks/gradientBlock"; import { GradientBlockColorStep } from "core/Materials/Node/Blocks/gradientBlock"; import { GradientStepComponent } from "./gradientStepComponent"; import { Color3 } from "core/Maths/math.color"; -import { GeneralProperties } from "./genericNodePropertyComponent"; +import { GetGeneralProperties } from "./genericNodePropertyComponent"; import type { Nullable } from "core/types"; import type { Observer } from "core/Misc/observable"; import type { IPropertyComponentProps } from "shared-ui-components/nodeGraphSystem/interfaces/propertyComponentProps"; @@ -104,7 +104,7 @@ export class GradientPropertyTabComponent extends React.Component - {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} - {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} {texture && texture.updateSamplingMode && ( this.removeTexture()} />} - {GenericProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGenericProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} ); } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/inputNodePropertyComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/inputNodePropertyComponent.tsx index 8ef19c9c39f..20f48cad8a4 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/inputNodePropertyComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/inputNodePropertyComponent.tsx @@ -11,7 +11,7 @@ import { NodeMaterialBlockConnectionPointTypes } from "core/Materials/Node/Enums import { NodeMaterialSystemValues } from "core/Materials/Node/Enums/nodeMaterialSystemValues"; import { AnimatedInputBlockTypes } from "core/Materials/Node/Blocks/Input/animatedInputBlockTypes"; import type { InputBlock } from "core/Materials/Node/Blocks/Input/inputBlock"; -import { GeneralProperties } from "./genericNodePropertyComponent"; +import { GetGeneralProperties } from "./genericNodePropertyComponent"; import { CheckBoxLineComponent } from "../../sharedComponents/checkBoxLineComponent"; import { Color4PropertyTabComponent } from "../../components/propertyTab/properties/color4PropertyTabComponent"; import type { Nullable } from "core/types"; @@ -275,7 +275,7 @@ export class InputPropertyTabComponent extends React.Component - {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} {inputBlock.isUniform && !inputBlock.isSystemValue && inputBlock.animationType === AnimatedInputBlockTypes.None && ( - {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} { override render() { @@ -21,7 +21,7 @@ export class LightPropertyTabComponent extends React.Component - {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} - {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} - {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} this.removeTexture()} />} )} - {GenericProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGenericProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} ); } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/vectorMergerPropertyComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/vectorMergerPropertyComponent.tsx index cf7edcc4b69..d9ab134048c 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/vectorMergerPropertyComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/vectorMergerPropertyComponent.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { LineContainerComponent } from "shared-ui-components/lines/lineContainerComponent"; -import { GeneralProperties } from "./genericNodePropertyComponent"; +import { GetGeneralProperties } from "./genericNodePropertyComponent"; import type { VectorMergerBlock } from "core/Materials/Node/Blocks/vectorMergerBlock"; import type { IPropertyComponentProps } from "shared-ui-components/nodeGraphSystem/interfaces/propertyComponentProps"; import { OptionsLine } from "shared-ui-components/lines/optionsLineComponent"; @@ -23,7 +23,7 @@ export class VectorMergerPropertyTabComponent extends React.Component - {GeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} + {GetGeneralProperties({ stateManager: this.props.stateManager, nodeData: this.props.nodeData })} Date: Thu, 3 Jul 2025 16:53:13 -0400 Subject: [PATCH 09/12] input properties getter made into a function --- .../src/components/propertyTab/inputsPropertyTabComponent.tsx | 4 ++-- .../src/components/propertyTab/propertyTabComponent.tsx | 4 ++-- .../src/graphSystem/properties/framePropertyComponent.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/tools/nodeEditor/src/components/propertyTab/inputsPropertyTabComponent.tsx b/packages/tools/nodeEditor/src/components/propertyTab/inputsPropertyTabComponent.tsx index 6d1cef3dfff..f406b7ea976 100644 --- a/packages/tools/nodeEditor/src/components/propertyTab/inputsPropertyTabComponent.tsx +++ b/packages/tools/nodeEditor/src/components/propertyTab/inputsPropertyTabComponent.tsx @@ -28,13 +28,13 @@ interface IInputsPropertyTabComponentProps { * @param props * @returns */ -export const InputProperties = (props: IInputsPropertyTabComponentProps) => { +export function GetInputProperties(props: IInputsPropertyTabComponentProps) { return ( ); -}; +} class InputsPropertyContent extends React.Component { constructor(props: IInputsPropertyTabComponentProps) { diff --git a/packages/tools/nodeEditor/src/components/propertyTab/propertyTabComponent.tsx b/packages/tools/nodeEditor/src/components/propertyTab/propertyTabComponent.tsx index ce0713f683a..43f1f1fccd4 100644 --- a/packages/tools/nodeEditor/src/components/propertyTab/propertyTabComponent.tsx +++ b/packages/tools/nodeEditor/src/components/propertyTab/propertyTabComponent.tsx @@ -18,7 +18,7 @@ import type { Observer } from "core/Misc/observable"; import { NodeMaterial } from "core/Materials/Node/nodeMaterial"; import { NodeMaterialModes } from "core/Materials/Node/Enums/nodeMaterialModes"; import { PreviewType } from "../preview/previewType"; -import { InputProperties } from "./inputsPropertyTabComponent"; +import { GetInputProperties } from "./inputsPropertyTabComponent"; import { LogEntry } from "../log/logComponent"; import "./propertyTab.scss"; import { GraphNode } from "shared-ui-components/nodeGraphSystem/graphNode"; @@ -627,7 +627,7 @@ export class PropertyTabComponent extends React.Component this.props.globalState.stateManager.onUpdateRequiredObservable.notifyObservers(null)} /> - {InputProperties({ lockObject: this.props.lockObject, globalState: this.props.globalState, inputs: this.props.globalState.nodeMaterial.getInputBlocks() })} + {GetInputProperties({ lockObject: this.props.lockObject, globalState: this.props.globalState, inputs: this.props.globalState.nodeMaterial.getInputBlocks() })} ); } diff --git a/packages/tools/nodeEditor/src/graphSystem/properties/framePropertyComponent.tsx b/packages/tools/nodeEditor/src/graphSystem/properties/framePropertyComponent.tsx index b8dbee11f1e..35997573f0a 100644 --- a/packages/tools/nodeEditor/src/graphSystem/properties/framePropertyComponent.tsx +++ b/packages/tools/nodeEditor/src/graphSystem/properties/framePropertyComponent.tsx @@ -3,7 +3,7 @@ import { LineContainerComponent } from "shared-ui-components/lines/lineContainer import type { GlobalState } from "../../globalState"; import type { Nullable } from "core/types"; import type { Observer } from "core/Misc/observable"; -import { InputProperties } from "../../components/propertyTab/inputsPropertyTabComponent"; +import { GetInputProperties } from "../../components/propertyTab/inputsPropertyTabComponent"; import type { InputBlock } from "core/Materials/Node/Blocks/Input/inputBlock"; import { TextInputLineComponent } from "shared-ui-components/lines/textInputLineComponent"; import type { GraphFrame } from "shared-ui-components/nodeGraphSystem/graphFrame"; @@ -77,7 +77,7 @@ export class FramePropertyTabComponent extends React.Component - {InputProperties({ + {GetInputProperties({ globalState: this.props.globalState, lockObject: this.props.globalState.lockObject, inputs: configurableInputBlocks, From c637abad6a252f948d86658c1fd6f98eeba6996c Mon Sep 17 00:00:00 2001 From: Georgina Date: Thu, 3 Jul 2025 17:16:46 -0400 Subject: [PATCH 10/12] input forwardref --- .../src/fluent/primitives/input.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/dev/sharedUiComponents/src/fluent/primitives/input.tsx b/packages/dev/sharedUiComponents/src/fluent/primitives/input.tsx index 51b1fcacd0d..fb0d5e91405 100644 --- a/packages/dev/sharedUiComponents/src/fluent/primitives/input.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/primitives/input.tsx @@ -1,7 +1,8 @@ -import type { FunctionComponent, KeyboardEvent, ChangeEvent } from "react"; -import { useEffect, useState } from "react"; +import type { KeyboardEvent, ChangeEvent } from "react"; +import { useEffect, useState, forwardRef } from "react"; import { Input as FluentInput, makeStyles } from "@fluentui/react-components"; +import type { InputProps as FluentInputProps } from "@fluentui/react-components"; import type { BaseComponentProps } from "../hoc/propertyLine"; const useInputStyles = makeStyles({ @@ -27,9 +28,9 @@ export type InputProps = BaseComponentProps & { * @param props * @returns */ -export const Input: FunctionComponent> = (props) => { +export const Input = forwardRef & InputProps>((props, ref) => { const classes = useInputStyles(); - const [value, setValue] = useState(props.value ?? ""); + const [value, setValue] = useState(props.value ?? ""); const type = typeof props.value === "number" ? "number" : "text"; @@ -50,7 +51,9 @@ export const Input: FunctionComponent> = (props) => return ( > = (props) => onKeyDown={handleKeyDown} /> ); -}; +}); From b0d11120b067e46d02faeddc89132a0caddde4d4 Mon Sep 17 00:00:00 2001 From: Georgina Date: Thu, 3 Jul 2025 18:39:18 -0400 Subject: [PATCH 11/12] remove custom input styling and use fluent searchbox --- .../src/fluent/primitives/input.tsx | 13 ++++------ .../src/fluent/primitives/searchBox.tsx | 25 +++++++++++++++++++ .../components/nodeList/nodeListComponent.tsx | 4 +-- 3 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 packages/dev/sharedUiComponents/src/fluent/primitives/searchBox.tsx diff --git a/packages/dev/sharedUiComponents/src/fluent/primitives/input.tsx b/packages/dev/sharedUiComponents/src/fluent/primitives/input.tsx index fb0d5e91405..51b1fcacd0d 100644 --- a/packages/dev/sharedUiComponents/src/fluent/primitives/input.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/primitives/input.tsx @@ -1,8 +1,7 @@ -import type { KeyboardEvent, ChangeEvent } from "react"; -import { useEffect, useState, forwardRef } from "react"; +import type { FunctionComponent, KeyboardEvent, ChangeEvent } from "react"; +import { useEffect, useState } from "react"; import { Input as FluentInput, makeStyles } from "@fluentui/react-components"; -import type { InputProps as FluentInputProps } from "@fluentui/react-components"; import type { BaseComponentProps } from "../hoc/propertyLine"; const useInputStyles = makeStyles({ @@ -28,9 +27,9 @@ export type InputProps = BaseComponentProps & { * @param props * @returns */ -export const Input = forwardRef & InputProps>((props, ref) => { +export const Input: FunctionComponent> = (props) => { const classes = useInputStyles(); - const [value, setValue] = useState(props.value ?? ""); + const [value, setValue] = useState(props.value ?? ""); const type = typeof props.value === "number" ? "number" : "text"; @@ -51,9 +50,7 @@ export const Input = forwardRef ); -}); +}; diff --git a/packages/dev/sharedUiComponents/src/fluent/primitives/searchBox.tsx b/packages/dev/sharedUiComponents/src/fluent/primitives/searchBox.tsx new file mode 100644 index 00000000000..62c39144c98 --- /dev/null +++ b/packages/dev/sharedUiComponents/src/fluent/primitives/searchBox.tsx @@ -0,0 +1,25 @@ +import { Field, SearchBox as FluentSearchBox, makeStyles } from "@fluentui/react-components"; +import type { InputOnChangeData } from "@fluentui/react-components"; +import type { SearchBoxChangeEvent } from "@fluentui/react-components"; + +type SearchProps = { + onChange: (val: string) => void; + placeholder?: string; +}; +const useStyles = makeStyles({ + search: { + minWidth: "50px", + }, +}); +export const SearchBox = (props: SearchProps) => { + const classes = useStyles(); + const onChange: (ev: SearchBoxChangeEvent, data: InputOnChangeData) => void = (_, data) => { + props.onChange(data.value); + }; + + return ( + + + + ); +}; diff --git a/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx b/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx index c607d0be40b..a12a9d5dcc6 100644 --- a/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx +++ b/packages/tools/nodeEditor/src/components/nodeList/nodeListComponent.tsx @@ -16,7 +16,7 @@ import { NodeLedger } from "shared-ui-components/nodeGraphSystem/nodeLedger"; import "./nodeList.scss"; import { ToolContext } from "shared-ui-components/fluent/hoc/fluentToolWrapper"; import { Accordion } from "shared-ui-components/fluent/primitives/accordion"; -import { Input } from "shared-ui-components/fluent/primitives/input"; +import { SearchBox } from "shared-ui-components/fluent/primitives/searchBox"; interface INodeListComponentProps { globalState: GlobalState; @@ -333,7 +333,7 @@ export class NodeListComponent extends React.Component - this.filterContent(val.toString())} /> + this.filterContent(val.toString())} /> {blockMenu}
); From d5676ae58ef3d03879290d58dbf69d8101bebe97 Mon Sep 17 00:00:00 2001 From: Georgina Date: Mon, 7 Jul 2025 10:31:41 -0400 Subject: [PATCH 12/12] remove style change to split container --- packages/dev/sharedUiComponents/src/split/splitContainer.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dev/sharedUiComponents/src/split/splitContainer.tsx b/packages/dev/sharedUiComponents/src/split/splitContainer.tsx index fd22103de78..c26c60a3a45 100644 --- a/packages/dev/sharedUiComponents/src/split/splitContainer.tsx +++ b/packages/dev/sharedUiComponents/src/split/splitContainer.tsx @@ -319,7 +319,6 @@ export const SplitContainer: React.FC> =
props.onPointerDown && props.onPointerDown(evt)} onPointerMove={(evt) => props.onPointerMove && props.onPointerMove(evt)}