Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
7754286
new FrameworkOptions, HostStore, configureFramework() action
forman Oct 25, 2024
9fcbe0a
new FrameworkOptions.configuration
forman Oct 25, 2024
8f14be5
now get input values from input kinds "AppState" and "State"
forman Oct 25, 2024
f8cbd17
type-safety for the hostStore
forman Oct 25, 2024
1682ea9
Adjust demo for application state changes
forman Oct 25, 2024
656c0e8
integrated "ContributionModel" into "ContributionState"
forman Oct 29, 2024
7e01da4
integrated "ContributionModel" into "ContributionState"
forman Oct 29, 2024
f38e958
integrated "ContributionModel" into "ContributionState"
forman Oct 29, 2024
0f9203e
renamed "contributionStatesRecord" --> "contributionsRecord"
forman Oct 30, 2024
f016e3d
renamed "componentStateResult" --> "componentResult"
forman Oct 30, 2024
cf791fc
renamed "componentState" --> "component"
forman Oct 31, 2024
5d8202c
supporting both output kinds "State" and "AppState" now
forman Oct 31, 2024
7905ecd
about to implement reaction to host state changes
forman Nov 4, 2024
20cd667
Fixed typing
forman Nov 4, 2024
668b48d
implemented handleHostStoreChange();
forman Nov 4, 2024
1804154
renamed PropertyChangeEvent --> ComponentChangeEvent;
forman Nov 4, 2024
48f7663
renamed onPropertyChange --> onChange
forman Nov 4, 2024
ddf34f9
removed Dashi prefix from component names
forman Nov 4, 2024
419ad3d
fixes
forman Nov 4, 2024
83c4f08
using JSON schema for types
forman Nov 4, 2024
ee41340
using JSON schema for types
forman Nov 4, 2024
c66c9c6
fix
forman Nov 4, 2024
6354ae8
fix
forman Nov 4, 2024
5efd218
debugging
forman Nov 4, 2024
302e9a3
simplify
forman Nov 4, 2024
07def6c
fixed options generation
forman Nov 4, 2024
c629872
added debug logs
forman Nov 4, 2024
0a2d7e8
Aktualisieren von handleComponentChange.ts
forman Nov 5, 2024
cea9c72
Taking care of valid variable names
forman Nov 5, 2024
f7951c4
Merge remote-tracking branch 'origin/forman-20-support_app_state' int…
forman Nov 5, 2024
e1de893
apply component state changes before submitting callback requests
forman Nov 5, 2024
cf78f75
refactorings / code cleanup
forman Nov 5, 2024
02617cb
refactorings / code cleanup
forman Nov 5, 2024
b676414
removed redundancy
forman Nov 5, 2024
03a466d
improved readability
forman Nov 5, 2024
b0ee3e8
Introduced Typography component and using it in a 3rd demo panel
forman Nov 5, 2024
f84e2eb
Introduced Typography component
forman Nov 5, 2024
ba1eead
New type PropertyPath
forman Nov 5, 2024
775b3c1
Memoized getHostStorePropertyRefs()
forman Nov 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions dashi/TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Dashi UI TODOs

- Keep state flat, avoid deep nesting!

- Why: Changing a deeply nested property is more complex, error prone,
and takes more time to execute.
Deeply nested state models are harder to understand too.
It will also require changing all parent objects and arrays that contain the changed value
which potentially causes more UI renders and/or more tests whether component properties changed.

- How: Go back to former design where initial contributions that stay constant
are separate from changing contribution states and components.

- Consequently use `propertyPath: string[]` instead of `propertyName: string`

- Add true actions from `src/actions` to store state so that lib users
know what actions are public.
7 changes: 7 additions & 0 deletions dashi/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dashi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@emotion/styled": "^11.13.0",
"@fontsource/roboto": "^5.1.0",
"@mui/material": "^6.1.5",
"memoize-one": "^6.0.0",
"microdiff": "^1.4.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
12 changes: 8 additions & 4 deletions dashi/src/demo/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { CssBaseline, ThemeProvider, createTheme } from "@mui/material";
import Typography from "@mui/material/Typography";

import { configureLogging, initSystemStore } from "@/lib";
import { initializeContributions } from "@/lib";
import ExtensionsInfo from "./components/ExtensionInfo";
import ControlBar from "@/demo/components/ControlBar";
import PanelsControl from "./components/PanelsControl";
import PanelsRow from "./components/PanelsRow";
import { appStore } from "@/demo/store";

configureLogging();

initSystemStore();
initializeContributions({
hostStore: appStore,
logging: { enabled: true },
});

// MUI's default font family
const fontFamily = "Roboto, Arial, sans-serif";
Expand All @@ -32,6 +35,7 @@ function App() {
Dashi Demo
</Typography>
<ExtensionsInfo />
<ControlBar />
<PanelsControl />
<PanelsRow />
</ThemeProvider>
Expand Down
5 changes: 3 additions & 2 deletions dashi/src/demo/actions/hidePanel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { setComponentVisibility } from "@/lib";
import { updateContributionState } from "@/lib";
import type { PanelState } from "@/demo/types";

export function hidePanel(panelIndex: number) {
setComponentVisibility("panels", panelIndex, false);
updateContributionState<PanelState>("panels", panelIndex, { visible: false });
}
5 changes: 3 additions & 2 deletions dashi/src/demo/actions/showPanel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { setComponentVisibility } from "@/lib";
import { updateContributionState } from "@/lib";
import type { PanelState } from "@/demo/types";

export function showPanel(panelIndex: number) {
setComponentVisibility("panels", panelIndex, true);
updateContributionState<PanelState>("panels", panelIndex, { visible: true });
}
33 changes: 33 additions & 0 deletions dashi/src/demo/components/ControlBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import FormControl from "@mui/material/FormControl";
import InputLabel from "@mui/material/InputLabel";
import Select from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem";

import { useAppStore } from "@/demo/store";

function ControlBar() {
const { datasets, selectedDatasetId, setSelectedDatasetId } = useAppStore();

return (
<div>
<FormControl variant="filled" size="small" style={{ minWidth: 180 }}>
<InputLabel id="dataset">Dataset (App State)</InputLabel>
<Select
labelId="dataset"
value={selectedDatasetId}
onChange={(event) =>
void setSelectedDatasetId(event.target.value || null)
}
>
{datasets.map(({ id, title }) => (
<MenuItem key={id} value={id}>
{title}
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}

export default ControlBar;
43 changes: 21 additions & 22 deletions dashi/src/demo/components/Panel.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { type CSSProperties, type ReactElement } from "react";
import type { CSSProperties, ReactElement } from "react";
import CircularProgress from "@mui/material/CircularProgress";

import {
type Contribution,
type ComponentChangeHandler,
type ContributionState,
type PropertyChangeHandler,
DashiComponent,
Component,
} from "@/lib";
import type { PanelState } from "@/demo/types";

const panelContainerStyle: CSSProperties = {
display: "flex",
Expand All @@ -32,40 +32,39 @@ const panelContentStyle: CSSProperties = {
padding: 2,
};

interface PanelProps {
panelModel: Contribution;
panelState: ContributionState;
onPropertyChange: PropertyChangeHandler;
interface PanelProps extends ContributionState<PanelState> {
onChange: ComponentChangeHandler;
}

function Panel({ panelModel, panelState, onPropertyChange }: PanelProps) {
if (!panelState.visible) {
function Panel({
name,
state,
component,
componentResult,
onChange,
}: PanelProps) {
if (!state.visible) {
return null;
}
const componentState = panelState.componentState;
let panelElement: ReactElement | null = null;
const componentModelResult = panelState.componentStateResult;
if (componentModelResult.data && componentState) {
panelElement = (
<DashiComponent {...componentState} onPropertyChange={onPropertyChange} />
);
} else if (componentModelResult.error) {
if (component) {
panelElement = <Component {...component} onChange={onChange} />;
} else if (componentResult.error) {
panelElement = (
<span>
Error loading {panelModel.name}: {componentModelResult.error.message}
Error loading {name}: {componentResult.error.message}
</span>
);
} else if (componentModelResult.status === "pending") {
} else if (componentResult.status === "pending") {
panelElement = (
<span>
<CircularProgress size={30} color="secondary" /> Loading{" "}
{panelModel.name}...
<CircularProgress size={30} color="secondary" /> Loading {name}...
</span>
);
}
return (
<div style={panelContainerStyle}>
<div style={panelHeaderStyle}>{panelState.title}</div>
<div style={panelHeaderStyle}>{state.title}</div>
<div style={panelContentStyle}>{panelElement}</div>
</div>
);
Expand Down
11 changes: 4 additions & 7 deletions dashi/src/demo/components/PanelsControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ import Checkbox from "@mui/material/Checkbox";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormGroup from "@mui/material/FormGroup";

import { useContributionStatesRecord } from "@/lib";
import { hidePanel } from "@/demo/actions/hidePanel";
import { showPanel } from "@/demo/actions/showPanel";

const contribPoint = "panels";
import { usePanelStates } from "@/demo/hooks";

function PanelsControl() {
const contributionStatesRecord = useContributionStatesRecord();
const panelStates = contributionStatesRecord[contribPoint];
const panelStates = usePanelStates();
if (!panelStates) {
// Ok, not ready yet
return null;
Expand All @@ -23,12 +20,12 @@ function PanelsControl() {
return (
<FormControlLabel
key={panelIndex}
label={panelState.title}
label={panelState.state.title}
control={
<Checkbox
color="secondary"
id={id}
checked={panelState.visible || false}
checked={panelState.state.visible || false}
value={panelIndex}
onChange={(e) => {
if (e.currentTarget.checked) {
Expand Down
38 changes: 12 additions & 26 deletions dashi/src/demo/components/PanelsRow.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,30 @@
import React from "react";
import type { JSX } from "react";

import {
type PropertyChangeEvent,
applyPropertyChange,
useContributionModelsRecord,
useContributionStatesRecord,
} from "@/lib";
import { type ComponentChangeEvent, handleComponentChange } from "@/lib";
import { usePanelStates } from "@/demo/hooks";
import Panel from "./Panel";

const contribPoint = "panels";

function PanelsRow() {
const contributionModelsRecord = useContributionModelsRecord();
const contributionStatesRecord = useContributionStatesRecord();
const panelModels = contributionModelsRecord[contribPoint];
const panelStates = contributionStatesRecord[contribPoint];
if (!panelModels || !panelStates) {
const panelStates = usePanelStates();
if (!panelStates) {
// Ok, not ready yet
return null;
}
// TODO: assert panelModels.length === panelStates.length
if (panelModels.length != panelStates?.length) {
throw Error("internal state error");
}

const handlePropertyChange = (
const handlePanelChange = (
panelIndex: number,
panelEvent: PropertyChangeEvent,
panelEvent: ComponentChangeEvent,
) => {
applyPropertyChange(contribPoint, panelIndex, panelEvent);
handleComponentChange("panels", panelIndex, panelEvent);
};
const visiblePanels: React.JSX.Element[] = [];
const visiblePanels: JSX.Element[] = [];
panelStates.forEach((panelState, panelIndex) => {
if (panelState.visible) {
if (panelState.state.visible) {
visiblePanels.push(
<Panel
key={panelIndex}
panelState={panelState}
panelModel={panelModels[panelIndex]}
onPropertyChange={(e) => handlePropertyChange(panelIndex, e)}
{...panelState}
onChange={(e) => handlePanelChange(panelIndex, e)}
/>,
);
}
Expand Down
7 changes: 7 additions & 0 deletions dashi/src/demo/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { type ContributionState, useContributionsRecord } from "@/lib";
import type { PanelState } from "@/demo/types";

export function usePanelStates() {
const contributionStatesRecord = useContributionsRecord();
return contributionStatesRecord["panels"] as ContributionState<PanelState>[];
}
28 changes: 28 additions & 0 deletions dashi/src/demo/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { create } from "zustand/react";

export interface Dataset {
id: string;
title: string;
}

export interface AppState {
datasets: Dataset[];
selectedDatasetId: string | null;
setSelectedDatasetId(setSelectedDatasetId: string | null): void;
}

export const appStore = create<AppState>((set, get) => ({
// TODO: get from demo server
datasets: [
{ id: "ds0", title: "Dataset #1" },
{ id: "ds1", title: "Dataset #2" },
],
selectedDatasetId: "ds0",
setSelectedDatasetId: (selectedDatasetId: string | null) => {
if (selectedDatasetId !== get().selectedDatasetId) {
set({ selectedDatasetId });
}
},
}));

export const useAppStore = appStore;
4 changes: 4 additions & 0 deletions dashi/src/demo/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface PanelState {
title: string;
visible: boolean;
}
Loading