feat: implements onSceneUpdated callback

This commit is contained in:
Mark Mankarious
2023-08-08 13:12:36 +01:00
parent 51f0d4fd4b
commit f1f77d4eca
11 changed files with 102 additions and 28 deletions

View File

@@ -1,4 +1,5 @@
import React, { useEffect } from 'react';
import { shallow } from 'zustand/shallow';
import { ThemeProvider } from '@mui/material/styles';
import { Box } from '@mui/material';
import { theme } from 'src/styles/theme';
@@ -11,6 +12,7 @@ import {
GroupInput,
Scene
} from 'src/types';
import { sceneToSceneInput } from 'src/utils';
import { useSceneStore, SceneProvider } from 'src/stores/sceneStore';
import { GlobalStyles } from 'src/styles/GlobalStyles';
import { Renderer } from 'src/components/Renderer/Renderer';
@@ -24,7 +26,7 @@ interface Props {
zoom?: number;
};
interactionsEnabled?: boolean;
onSceneUpdated?: (scene: SceneInput, prevScene: SceneInput) => void;
onSceneUpdated?: (scene: SceneInput) => void;
width?: number | string;
height?: number | string;
}
@@ -37,6 +39,9 @@ const App = ({
onSceneUpdated
}: Props) => {
useWindowUtils();
const scene = useSceneStore(({ nodes, connectors, groups, icons }) => {
return { nodes, connectors, groups, icons };
}, shallow);
const sceneActions = useSceneStore((state) => {
return state.actions;
});
@@ -58,6 +63,13 @@ const App = ({
}
}, [initialScene, sceneActions]);
useEffect(() => {
if (onSceneUpdated) {
const sceneInput = sceneToSceneInput(scene);
onSceneUpdated(sceneInput);
}
}, [scene, onSceneUpdated]);
return (
<>
<GlobalStyles />

View File

@@ -0,0 +1,33 @@
import React, { useCallback } from 'react';
import Isoflow from 'src/Isoflow';
import { icons } from '../icons';
export const Callbacks = () => {
const onSceneUpdated = useCallback(() => {
console.log('Scene updated');
}, []);
return (
<Isoflow
initialScene={{
icons,
nodes: [
{
id: 'server',
label: 'Callbacks example',
labelHeight: 40,
iconId: 'server',
position: {
x: 0,
y: 0
}
}
],
connectors: [],
groups: []
}}
onSceneUpdated={onSceneUpdated}
height="100%"
/>
);
};

View File

@@ -64,7 +64,6 @@ export const CustomNode = () => {
groups: [
{
id: 'group1',
label: 'Group 1',
nodeIds: ['server', 'database']
}
],

View File

@@ -2,8 +2,10 @@ import React, { useState, useMemo } from 'react';
import { Box, Select, MenuItem, useTheme } from '@mui/material';
import { BasicEditor } from './BasicEditor/BasicEditor';
import { CustomNode } from './CustomNode/CustomNode';
import { Callbacks } from './Callbacks/Callbacks';
const examples = [
{ name: 'Callbacks', component: Callbacks },
{ name: 'Live Diagrams', component: CustomNode },
{ name: 'Basic Editor', component: BasicEditor }
];

View File

@@ -87,7 +87,10 @@ export const SceneProvider = ({ children }: ProviderProps) => {
);
};
export function useSceneStore<T>(selector: (state: SceneStore) => T) {
export function useSceneStore<T>(
selector: (state: SceneStore) => T,
equalityFn?: (left: T, right: T) => boolean
) {
const store = useContext(SceneContext);
useEffect(() => {
@@ -100,6 +103,6 @@ export function useSceneStore<T>(selector: (state: SceneStore) => T) {
throw new Error('Missing provider in the tree');
}
const value = useStore(store, selector);
const value = useStore(store, selector, equalityFn);
return value;
}

View File

@@ -46,5 +46,5 @@ export const scene: SceneInput = {
{ id: 'connector1', label: 'Connector1', from: 'node1', to: 'node2' },
{ id: 'connector2', label: 'Connector2', from: 'node2', to: 'node3' }
],
groups: [{ id: 'group1', label: 'Group1', nodeIds: ['node1', 'node2'] }]
groups: [{ id: 'group1', nodeIds: ['node1', 'node2'] }]
};

View File

@@ -30,6 +30,7 @@ export interface Node {
export interface Connector {
type: SceneItemTypeEnum.CONNECTOR;
id: string;
label: string;
color: string;
from: string;
to: string;

View File

@@ -1,5 +1,4 @@
import chroma from 'chroma-js';
import { SceneInput, Scene } from 'src/types';
export const clamp = (num: number, min: number, max: number) => {
// eslint-disable-next-line no-nested-ternary
@@ -14,26 +13,6 @@ export const roundToOneDecimalPlace = (num: number) => {
return Math.round(num * 10) / 10;
};
export const sceneToSceneInput = (scene: Scene): SceneInput => {
const nodes: SceneInput['nodes'] = scene.nodes.map((node) => {
return {
id: node.id,
position: node.position,
label: node.label,
labelHeight: node.labelHeight,
color: node.color,
iconId: node.iconId
};
});
return {
nodes,
connectors: [],
groups: [],
icons: scene.icons
} as SceneInput;
};
interface GetColorVariantOpts {
alpha?: number;
grade?: number;

View File

@@ -40,6 +40,7 @@ export const connectorInputToConnector = (
return {
type: SceneItemTypeEnum.CONNECTOR,
id: connectorInput.id,
label: connectorInput.label ?? '',
color: connectorInput.color ?? DEFAULT_COLOR,
from: connectorInput.from,
to: connectorInput.to
@@ -67,3 +68,49 @@ export const sceneInputtoScene = (sceneInput: SceneInput): Scene => {
icons: sceneInput.icons
} as Scene;
};
export const nodeToNodeInput = (node: Node): NodeInput => {
return {
id: node.id,
position: node.position,
label: node.label,
labelHeight: node.labelHeight,
color: node.color,
iconId: node.iconId
};
};
export const connectorToConnectorInput = (
connector: Connector
): ConnectorInput => {
return {
id: connector.id,
label: connector.label,
from: connector.from,
to: connector.to,
color: connector.color
};
};
export const groupToGroupInput = (group: Group): GroupInput => {
return {
id: group.id,
nodeIds: group.nodeIds,
color: group.color
};
};
export const sceneToSceneInput = (scene: Scene): SceneInput => {
const nodes: NodeInput[] = scene.nodes.map(nodeInputToNode);
const connectors: ConnectorInput[] = scene.connectors.map(
connectorToConnectorInput
);
const groups: GroupInput[] = scene.groups.map(groupToGroupInput);
return {
nodes,
connectors,
groups,
icons: scene.icons
} as SceneInput;
};

View File

@@ -29,7 +29,6 @@ export const connectorInput = z.object({
export const groupInput = z.object({
id: z.string(),
label: z.string().nullable(),
color: z.string().optional(),
nodeIds: z.array(z.string())
});

View File

@@ -54,7 +54,6 @@ describe('scene validation works correctly', () => {
const { nodes } = scene;
const invalidGroup: GroupInput = {
id: 'invalidGroup',
label: null,
nodeIds: ['invalidNode', 'node1']
};
const groups: GroupInput[] = [...scene.groups, invalidGroup];