From c5e83ec7ddf228e55c0539572e5f4579d37a045a Mon Sep 17 00:00:00 2001 From: Ruben Thoms Date: Fri, 6 Sep 2024 18:04:30 +0200 Subject: [PATCH] wip --- .../src/modules/LayerSpike/layers/Broker.ts | 20 +++++++ .../modules/LayerSpike/layers/GroupHandler.ts | 20 ++----- .../src/modules/LayerSpike/layers/ItemBase.ts | 3 -- .../modules/LayerSpike/layers/LayerBase.ts | 3 +- .../layers/PublishSubscribeHandler.ts | 14 +++++ .../src/modules/LayerSpike/layers/Setting.ts | 0 .../src/modules/LayerSpike/layers/Setting.tsx | 30 ----------- .../LayerSpike/layers/SettingsContext.ts | 51 +++++++++++++++--- .../LayerSpike/layers/components/Group.tsx | 9 +++- .../LayerSpike/layers/components/Layer.tsx | 30 +++++++++-- .../LayerSpike/layers/components/Setting.tsx | 35 ++++++++++++ .../LayerSpike/layers/components/utils.tsx | 31 +++++++++-- .../layers/implementations/Ensemble.tsx | 43 +++++++++++++-- .../layers/implementations/SurfaceContext.ts | 18 +++++-- .../LayerSpike/layers/implementations/View.ts | 29 ++++++++++ .../modules/LayerSpike/layers/interfaces.ts | 54 +++++++++++++++++++ frontend/src/modules/LayerSpike/settings.tsx | 14 +++-- 17 files changed, 320 insertions(+), 84 deletions(-) create mode 100644 frontend/src/modules/LayerSpike/layers/Broker.ts delete mode 100644 frontend/src/modules/LayerSpike/layers/ItemBase.ts create mode 100644 frontend/src/modules/LayerSpike/layers/Setting.ts delete mode 100644 frontend/src/modules/LayerSpike/layers/Setting.tsx create mode 100644 frontend/src/modules/LayerSpike/layers/components/Setting.tsx create mode 100644 frontend/src/modules/LayerSpike/layers/implementations/View.ts create mode 100644 frontend/src/modules/LayerSpike/layers/interfaces.ts diff --git a/frontend/src/modules/LayerSpike/layers/Broker.ts b/frontend/src/modules/LayerSpike/layers/Broker.ts new file mode 100644 index 000000000..091eb8808 --- /dev/null +++ b/frontend/src/modules/LayerSpike/layers/Broker.ts @@ -0,0 +1,20 @@ +import { WorkbenchServices } from "@framework/WorkbenchServices"; +import { WorkbenchSession } from "@framework/WorkbenchSession"; + +export class Broker { + private _workbenchServices: WorkbenchServices; + private _workbenchSession: WorkbenchSession; + + constructor(workbenchServices: WorkbenchServices, workbenchSession: WorkbenchSession) { + this._workbenchServices = workbenchServices; + this._workbenchSession = workbenchSession; + } + + getWorkbenchServices(): WorkbenchServices { + return this._workbenchServices; + } + + getWorkbenchSession(): WorkbenchSession { + return this._workbenchSession; + } +} diff --git a/frontend/src/modules/LayerSpike/layers/GroupHandler.ts b/frontend/src/modules/LayerSpike/layers/GroupHandler.ts index cd468bb33..8ffea28df 100644 --- a/frontend/src/modules/LayerSpike/layers/GroupHandler.ts +++ b/frontend/src/modules/LayerSpike/layers/GroupHandler.ts @@ -2,22 +2,8 @@ import React from "react"; import { v4 } from "uuid"; -import { Item } from "./ItemBase"; import { PublishSubscribe, PublishSubscribeHandler } from "./PublishSubscribeHandler"; - -export interface Group extends Item { - getName(): string; - setName(name: string): void; - getGroupHandler(): GroupHandler; -} - -export function instanceofGroup(item: Item): item is Group { - return ( - (item as Group).getName !== undefined && - (item as Group).setName !== undefined && - (item as Group).getGroupHandler !== undefined - ); -} +import { Item, instanceofGroup } from "./interfaces"; export enum GroupBaseTopic { CHILDREN_CHANGED = "CHILDREN_CHANGED", @@ -82,8 +68,8 @@ export class GroupHandler implements Item, PublishSubscribe = Record; export interface PublishSubscribe> { @@ -36,3 +38,15 @@ export class PublishSubscribeHandler { return subscriber; } } + +export function usePublishSubscribeTopicValue>( + publishSubscribe: PublishSubscribe, + topic: TTopic +): TTopicPayloads[TTopic] { + const value = React.useSyncExternalStore( + publishSubscribe.makeSubscriberFunction(topic), + publishSubscribe.makeSnapshotGetter(topic) + ); + + return value; +} diff --git a/frontend/src/modules/LayerSpike/layers/Setting.ts b/frontend/src/modules/LayerSpike/layers/Setting.ts new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/modules/LayerSpike/layers/Setting.tsx b/frontend/src/modules/LayerSpike/layers/Setting.tsx deleted file mode 100644 index 9b39e3e94..000000000 --- a/frontend/src/modules/LayerSpike/layers/Setting.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from "react"; - -import { WorkbenchSession } from "@framework/WorkbenchSession"; -import { WorkbenchSettings } from "@framework/WorkbenchSettings"; - -export type SettingComponentProps = { - onValueChange: (newValue: TValue) => void; - value: TValue; - availableValues: TValue[]; - workbenchSettings: WorkbenchSettings; - workbenchSession: WorkbenchSession; -}; - -export class SettingBase { - private _label: string; - private _value: TValue; - - constructor(label: string, initialValue: TValue) { - this._label = label; - this._value = initialValue; - } - - getLabel(): string { - return this._label; - } - - makeComponent(): (props: SettingComponentProps) => React.ReactNode { - throw new Error("Not implemented"); - } -} diff --git a/frontend/src/modules/LayerSpike/layers/SettingsContext.ts b/frontend/src/modules/LayerSpike/layers/SettingsContext.ts index 1aee3670f..8cfa3834a 100644 --- a/frontend/src/modules/LayerSpike/layers/SettingsContext.ts +++ b/frontend/src/modules/LayerSpike/layers/SettingsContext.ts @@ -1,16 +1,53 @@ -import { SettingBase } from "./Setting"; +import { PublishSubscribe, PublishSubscribeHandler } from "./PublishSubscribeHandler"; +import { Setting, SettingTopic } from "./interfaces"; -export class SettingsContext { - private _settings: SettingBase[] = []; - private _values: unknown[] = []; +export enum SettingsContextTopic { + AVAILABE_SETTINGS_CHANGED = "AVAILABE_SETTINGS_CHANGED", +} + +export type SettingsContextTopicPayloads = { + [SettingsContextTopic.AVAILABE_SETTINGS_CHANGED]: Setting[]; +}; + +export class SettingsContextHelper implements PublishSubscribe { + private _settings: Setting[]; + private _publishSubscribeHandler = new PublishSubscribeHandler(); + private _checkIfRefetchRequired: () => boolean; + private _fetchAvailableSettings: () => any[][]; - constructor(settings: SettingBase[]) { - this._settings = settings; + constructor(checkIfRefetchRequired: () => boolean, fetchAvailableSettings: () => any[][]) { + this._settings = []; } - private maybeFetchData() {} + addSetting(setting: Setting) { + this._settings.push(setting); + setting.makeSubscriberFunction(SettingTopic.VALUE_CHANGED)(() => { + this.maybeRefetchAvailableSettings(); + }); + } getSettings() { return this._settings; } + + private maybeRefetchAvailableSettings() { + if (this._checkIfRefetchRequired()) { + const newSettings = this._fetchAvailableSettings(); + this._publishSubscribeHandler.notifySubscribers(SettingsContextTopic.AVAILABE_SETTINGS_CHANGED); + } + } + + makeSnapshotGetter(topic: T): () => SettingsContextTopicPayloads[T] { + const snapshotGetter = (): any => { + if (topic === SettingsContextTopic.AVAILABE_SETTINGS_CHANGED) { + return this._settings; + } + }; + + return snapshotGetter; + } + + makeSubscriberFunction(topic: SettingsContextTopic): (onStoreChangeCallback: () => void) => () => void { + return this._publishSubscribeHandler.makeSubscriberFunction(topic); + } } diff --git a/frontend/src/modules/LayerSpike/layers/components/Group.tsx b/frontend/src/modules/LayerSpike/layers/components/Group.tsx index 3edb24f6e..113a1d874 100644 --- a/frontend/src/modules/LayerSpike/layers/components/Group.tsx +++ b/frontend/src/modules/LayerSpike/layers/components/Group.tsx @@ -1,11 +1,16 @@ +import { WorkbenchSession } from "@framework/WorkbenchSession"; +import { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { SortableListGroup } from "@lib/components/SortableList"; import { makeComponent } from "./utils"; -import { Group, GroupBaseTopic, useGroupBaseTopicValue } from "../GroupHandler"; +import { GroupBaseTopic, useGroupBaseTopicValue } from "../GroupHandler"; +import { Group } from "../interfaces"; export type LayerComponentProps = { group: Group; + workbenchSettings: WorkbenchSettings; + workbenchSession: WorkbenchSession; }; export function GroupComponent(props: LayerComponentProps): React.ReactNode { @@ -13,7 +18,7 @@ export function GroupComponent(props: LayerComponentProps): React.ReactNode { return ( - {children.map((child) => makeComponent(child))} + {children.map((child) => makeComponent(child, props.workbenchSettings, props.workbenchSession))} ); } diff --git a/frontend/src/modules/LayerSpike/layers/components/Layer.tsx b/frontend/src/modules/LayerSpike/layers/components/Layer.tsx index c5e40ea16..d9ddb2366 100644 --- a/frontend/src/modules/LayerSpike/layers/components/Layer.tsx +++ b/frontend/src/modules/LayerSpike/layers/components/Layer.tsx @@ -1,18 +1,38 @@ +import { WorkbenchSession } from "@framework/WorkbenchSession"; +import { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { SortableListItem } from "@lib/components/SortableList"; +import { SettingComponent } from "./Setting"; + import { LayerBase } from "../LayerBase"; +import { Setting } from "../interfaces"; export type LayerComponentProps = { layer: LayerBase; onRemove: (id: string) => void; + workbenchSettings: WorkbenchSettings; + workbenchSession: WorkbenchSession; }; export function Layer(props: LayerComponentProps): React.ReactNode { + function makeSetting(setting: Setting) { + return ( + + ); + } return ( - + +
+ {props.layer + .getSettingsContext() + .getSettings() + .map((setting) => makeSetting(setting))} +
+
); } diff --git a/frontend/src/modules/LayerSpike/layers/components/Setting.tsx b/frontend/src/modules/LayerSpike/layers/components/Setting.tsx new file mode 100644 index 000000000..3bb54127c --- /dev/null +++ b/frontend/src/modules/LayerSpike/layers/components/Setting.tsx @@ -0,0 +1,35 @@ +import { WorkbenchSession } from "@framework/WorkbenchSession"; +import { WorkbenchSettings } from "@framework/WorkbenchSettings"; + +import { usePublishSubscribeTopicValue } from "../PublishSubscribeHandler"; +import { Setting, SettingTopic } from "../interfaces"; + +export type SettingComponentProps = { + setting: Setting; + workbenchSettings: WorkbenchSettings; + workbenchSession: WorkbenchSession; +}; + +export function SettingComponent(props: SettingComponentProps): React.ReactNode { + const value = usePublishSubscribeTopicValue(props.setting, SettingTopic.VALUE_CHANGED); + + function handleValueChanged(newValue: TValue) { + props.setting.setValue(newValue); + } + + const Component = props.setting.makeComponent(); + return ( +
+
{props.setting.getLabel()}:
+
+ +
+
+ ); +} diff --git a/frontend/src/modules/LayerSpike/layers/components/utils.tsx b/frontend/src/modules/LayerSpike/layers/components/utils.tsx index dce5df9ff..f73507007 100644 --- a/frontend/src/modules/LayerSpike/layers/components/utils.tsx +++ b/frontend/src/modules/LayerSpike/layers/components/utils.tsx @@ -1,18 +1,39 @@ +import { WorkbenchSession } from "@framework/WorkbenchSession"; +import { WorkbenchSettings } from "@framework/WorkbenchSettings"; import { SortableListItemProps } from "@lib/components/SortableList"; import { GroupComponent } from "./Group"; import { Layer } from "./Layer"; import { GroupHandler } from "../GroupHandler"; -import { Item } from "../ItemBase"; import { LayerBase } from "../LayerBase"; +import { Item, instanceofGroup } from "../interfaces"; -export function makeComponent(item: Item): React.ReactElement { +export function makeComponent( + item: Item, + workbenchSettings: WorkbenchSettings, + workbenchSession: WorkbenchSession +): React.ReactElement { if (item instanceof LayerBase) { - return {}} />; + return ( + {}} + workbenchSession={workbenchSession} + workbenchSettings={workbenchSettings} + /> + ); } - if (item instanceof GroupHandler) { - return ; + if (instanceofGroup(item)) { + return ( + + ); } throw new Error("Not implemented"); } diff --git a/frontend/src/modules/LayerSpike/layers/implementations/Ensemble.tsx b/frontend/src/modules/LayerSpike/layers/implementations/Ensemble.tsx index cc82f2339..d03cf88f6 100644 --- a/frontend/src/modules/LayerSpike/layers/implementations/Ensemble.tsx +++ b/frontend/src/modules/LayerSpike/layers/implementations/Ensemble.tsx @@ -1,17 +1,50 @@ import React from "react"; import { EnsembleIdent } from "@framework/EnsembleIdent"; +import { useEnsembleSet } from "@framework/WorkbenchSession"; +import { EnsembleDropdown } from "@framework/components/EnsembleDropdown"; -import { SettingBase, SettingComponentProps } from "../Setting"; +import { PublishSubscribeHandler } from "../PublishSubscribeHandler"; +import { Setting, SettingComponentProps, SettingTopic, SettingTopicPayloads, SettingsContext } from "../interfaces"; -export class Ensemble extends SettingBase { - constructor() { - super("Ensemble", null); +export class Ensemble implements Setting { + private _value: EnsembleIdent | null = null; + private _publishSubscribeHandler = new PublishSubscribeHandler(); + + constructor() {} + + getLabel(): string { + return "Ensemble"; + } + + setValue(value: EnsembleIdent | null) { + this._value = value; + this._publishSubscribeHandler.notifySubscribers(SettingTopic.VALUE_CHANGED); } makeComponent(): (props: SettingComponentProps) => React.ReactNode { return function Ensemble(props: SettingComponentProps) { - return
{props.value ? props.value.getEnsembleName() : "No ensemble selected"}
; + const ensembleSet = useEnsembleSet(props.workbenchSession); + + return ; }; } + + makeSnapshotGetter(topic: T): () => SettingTopicPayloads[T] { + const snapshotGetter = (): any => { + if (topic === SettingTopic.VALUE_CHANGED) { + return this._value; + } + }; + + return snapshotGetter; + } + + makeSubscriberFunction(topic: SettingTopic): (onStoreChangeCallback: () => void) => () => void { + return this._publishSubscribeHandler.makeSubscriberFunction(topic); + } + + getAvailableValues(): (EnsembleIdent | null)[] { + return []; + } } diff --git a/frontend/src/modules/LayerSpike/layers/implementations/SurfaceContext.ts b/frontend/src/modules/LayerSpike/layers/implementations/SurfaceContext.ts index a1160f330..993d4157b 100644 --- a/frontend/src/modules/LayerSpike/layers/implementations/SurfaceContext.ts +++ b/frontend/src/modules/LayerSpike/layers/implementations/SurfaceContext.ts @@ -1,9 +1,21 @@ import { Ensemble } from "./Ensemble"; -import { SettingsContext } from "../SettingsContext"; +import { SettingsContextHelper } from "../SettingsContext"; +import { SettingsContext } from "../interfaces"; + +export class SurfaceContext implements SettingsContext { + private _contextHelper: SettingsContextHelper; -export class SurfaceContext extends SettingsContext { constructor() { - super([new Ensemble()]); + this._contextHelper = new SettingsContextHelper(this.checkIfRefetchRequired); + this._contextHelper.addSetting(new Ensemble()); + } + + getSettings() { + return this._contextHelper.getSettings(); + } + + private checkIfRefetchRequired(): boolean { + return false; } } diff --git a/frontend/src/modules/LayerSpike/layers/implementations/View.ts b/frontend/src/modules/LayerSpike/layers/implementations/View.ts new file mode 100644 index 000000000..c7ca64696 --- /dev/null +++ b/frontend/src/modules/LayerSpike/layers/implementations/View.ts @@ -0,0 +1,29 @@ +import { GroupHandler } from "../GroupHandler"; +import { Group } from "../interfaces"; + +export class View implements Group { + private _name: string; + private _id: string; + private _groupHandler: GroupHandler = new GroupHandler(); + + constructor(name: string) { + this._id = "view"; + this._name = name; + } + + getId(): string { + return this._id; + } + + getName(): string { + return this._name; + } + + setName(name: string) { + this._name = name; + } + + getGroupHandler(): GroupHandler { + return this._groupHandler; + } +} diff --git a/frontend/src/modules/LayerSpike/layers/interfaces.ts b/frontend/src/modules/LayerSpike/layers/interfaces.ts new file mode 100644 index 000000000..b88af3618 --- /dev/null +++ b/frontend/src/modules/LayerSpike/layers/interfaces.ts @@ -0,0 +1,54 @@ +import { WorkbenchSession } from "@framework/WorkbenchSession"; +import { WorkbenchSettings } from "@framework/WorkbenchSettings"; + +import { GroupHandler } from "./GroupHandler"; +import { PublishSubscribe } from "./PublishSubscribeHandler"; + +export interface Item { + getId(): string; +} + +export function instanceofItem(item: any): item is Item { + return (item as Item).getId !== undefined; +} + +export interface Group extends Item { + getName(): string; + setName(name: string): void; + getGroupHandler(): GroupHandler; +} + +export function instanceofGroup(item: Item): item is Group { + return ( + (item as Group).getName !== undefined && + (item as Group).setName !== undefined && + (item as Group).getGroupHandler !== undefined + ); +} + +export interface SettingsContext { + getSettings(): Setting[]; +} + +export type SettingComponentProps = { + onValueChange: (newValue: TValue) => void; + value: TValue; + availableValues: TValue[]; + workbenchSettings: WorkbenchSettings; + workbenchSession: WorkbenchSession; +}; + +export interface Setting extends PublishSubscribe> { + getLabel(): string; + setValue(value: TValue): void; + getAvailableValues(): TValue[]; + makeComponent(): (props: SettingComponentProps) => React.ReactNode; +} + +export enum SettingTopic { + VALUE_CHANGED = "VALUE_CHANGED", +} + +export type SettingTopicPayloads = { + [SettingTopic.VALUE_CHANGED]: TValue; +}; diff --git a/frontend/src/modules/LayerSpike/settings.tsx b/frontend/src/modules/LayerSpike/settings.tsx index 5864607ec..696de88bb 100644 --- a/frontend/src/modules/LayerSpike/settings.tsx +++ b/frontend/src/modules/LayerSpike/settings.tsx @@ -4,12 +4,14 @@ import { ModuleSettingsProps } from "@framework/Module"; import { SortableList } from "@lib/components/SortableList"; import { GroupAdd, Layers } from "@mui/icons-material"; -import { GroupBaseTopic, GroupHandler, instanceofGroup, useGroupBaseTopicValue } from "./layers/GroupHandler"; +import { GroupBaseTopic, GroupHandler, useGroupBaseTopicValue } from "./layers/GroupHandler"; +import { View } from "./layers/View"; import { makeComponent } from "./layers/components/utils"; import { SurfaceLayer } from "./layers/implementations/SurfaceLayer"; +import { instanceofGroup } from "./layers/interfaces"; export function Settings(props: ModuleSettingsProps): React.ReactNode { - const mainGroup = React.useRef(new GroupHandler("main")); + const mainGroup = React.useRef(new GroupHandler()); const items = useGroupBaseTopicValue(mainGroup.current, GroupBaseTopic.CHILDREN_CHANGED); @@ -18,7 +20,7 @@ export function Settings(props: ModuleSettingsProps): React.ReactNode { } function handleAddGroup() { - mainGroup.current.appendChild(new GroupHandler("Group")); + mainGroup.current.appendChild(new View("View")); } function handleItemMoved( @@ -53,8 +55,8 @@ export function Settings(props: ModuleSettingsProps): React.ReactNode { return; } - origin.removeChild(movedItem); destination.insertChild(movedItem, position); + origin.removeChild(movedItem); } return ( @@ -69,7 +71,9 @@ export function Settings(props: ModuleSettingsProps): React.ReactNode {
- {items.map((item) => makeComponent(item))} + + {items.map((item) => makeComponent(item, props.workbenchSettings, props.workbenchSession))} +
);