|
|
@@ -66,14 +66,6 @@ export function newOnion({readonly, primitiveRegistry, versionRegistry}) {
|
|
|
let x = 0;
|
|
|
let y = 0;
|
|
|
|
|
|
- const initialState: VersionedModelState = {
|
|
|
- version: versionRegistry.initialVersion,
|
|
|
- graph: emptyGraph,
|
|
|
- historyGraph: initialHistoryGraph(versionRegistry.initialVersion),
|
|
|
- deltaGraphL1: emptyGraph,
|
|
|
- deltaGraphL0: emptyGraph,
|
|
|
- }
|
|
|
-
|
|
|
// The "current version" is both part of the React state (for rendering undo/redo buttons) and a local variable here, such that we can get the current version (synchronously), even outside of a setState-callback.
|
|
|
let currentVersion = versionRegistry.initialVersion;
|
|
|
function getCurrentVersion() {
|
|
|
@@ -81,7 +73,11 @@ export function newOnion({readonly, primitiveRegistry, versionRegistry}) {
|
|
|
}
|
|
|
|
|
|
function useOnion(overridenCallbacks: (any) => VersionedModelCallbacks) {
|
|
|
- const [state, setState] = React.useState<VersionedModelState>(initialState);
|
|
|
+ const [version, setVersion] = React.useState<Version>(versionRegistry.initialVersion);
|
|
|
+ const [graph, setGraph] = React.useState<D3OnionGraphData>(emptyGraph);
|
|
|
+ const [historyGraph, setHistoryGraph] = React.useState<HistoryGraphState>(initialHistoryGraph(versionRegistry.initialVersion));
|
|
|
+ const [deltaGraphL1, setDeltaGraphL1] = React.useState<DeltaGraphState>(emptyGraph);
|
|
|
+ const [deltaGraphL0, setDeltaGraphL0] = React.useState<DeltaGraphState>(emptyGraph);
|
|
|
|
|
|
// Reducer
|
|
|
|
|
|
@@ -100,19 +96,9 @@ export function newOnion({readonly, primitiveRegistry, versionRegistry}) {
|
|
|
|
|
|
const newVersion = versionRegistry.createVersion(parentVersion, composite, embeddings);
|
|
|
|
|
|
- setState(({historyGraph, deltaGraphL1, deltaGraphL0, ...rest}) => {
|
|
|
- return {
|
|
|
- // add new version to history graph:
|
|
|
- historyGraph: historyGraphReducer(historyGraph, {type: 'addVersion', version: newVersion}),
|
|
|
- // add the composite delta to the L1-graph + highlight it as 'active':
|
|
|
- deltaGraphL1: composite.deltas.length > 0 ? deltaGraphReducer(deltaGraphL1, {type: 'addDelta', delta: composite, active: false}) : deltaGraphL1, // never add an empty composite
|
|
|
- // add the primitive L0-deltas to the L0-graph + highlight them as 'active':
|
|
|
- deltaGraphL0: composite.deltas.reduce(
|
|
|
- (graph, delta) => deltaGraphReducer(graph, {type: 'addDelta', delta, active: false}),
|
|
|
- deltaGraphL0),
|
|
|
- ...rest,
|
|
|
- };
|
|
|
- });
|
|
|
+ setHistoryGraph(historyGraph => historyGraphReducer(historyGraph, {type: 'addVersion', version: newVersion}));
|
|
|
+ setDeltaGraphL1(deltaGraphL1 => composite.deltas.length > 0 ? deltaGraphReducer(deltaGraphL1, {type: 'addDelta', delta: composite, active: false}) : deltaGraphL1);
|
|
|
+ setDeltaGraphL0(deltaGraphL0 => composite.deltas.reduce((graph, delta) => deltaGraphReducer(graph, {type: 'addDelta', delta, active: false}), deltaGraphL0));
|
|
|
|
|
|
return newVersion;
|
|
|
}
|
|
|
@@ -124,7 +110,7 @@ export function newOnion({readonly, primitiveRegistry, versionRegistry}) {
|
|
|
};
|
|
|
// Idempotent
|
|
|
const appendVersions = (versions: Version[]) => {
|
|
|
- setState(({historyGraph, ...rest}) => {
|
|
|
+ setHistoryGraph(historyGraph => {
|
|
|
const versionsToAdd: Version[] = [];
|
|
|
const addIfDontHaveYet = version => {
|
|
|
if (!(historyGraph.nodes.some(n => n.obj === version) || versionsToAdd.includes(version))) {
|
|
|
@@ -145,57 +131,57 @@ export function newOnion({readonly, primitiveRegistry, versionRegistry}) {
|
|
|
for (const v of versions) {
|
|
|
collectVersions(v);
|
|
|
}
|
|
|
- return {
|
|
|
- historyGraph: versionsToAdd.reduceRight((historyGraph, version) => historyGraphReducer(historyGraph, {type: 'addVersion', version}), historyGraph),
|
|
|
- ...rest,
|
|
|
- };
|
|
|
- });
|
|
|
+ return versionsToAdd.reduceRight((historyGraph, version) => historyGraphReducer(historyGraph, {type: 'addVersion', version}), historyGraph);
|
|
|
+ })
|
|
|
}
|
|
|
-
|
|
|
- // helper
|
|
|
- const setGraph = callback =>
|
|
|
- setState(({graph, ...rest}) => ({graph: callback(graph), ...rest}));
|
|
|
+ // // Idempotent
|
|
|
+ // const appendDelta = (delta: CompositeDelta) => {
|
|
|
+ // setState(({deltaGraphL0, deltaGraphL1, ...rest}) => {
|
|
|
+ // return {
|
|
|
+ // deltaGraphL1: deltaGraphReducer(deltaGraphL1, {type: 'addDelta', delta, active: false}),
|
|
|
+ // deltaGraphL0: delta.reduce(
|
|
|
+ // (graph, delta) => deltaGraphReducer(deltaGraphL0, {type: 'addDelta', delta, active: false}),
|
|
|
+ // deltaGraphL0),
|
|
|
+ // ...rest,
|
|
|
+ // };
|
|
|
+ // });
|
|
|
+ // }
|
|
|
|
|
|
const undoWithoutUpdatingHistoryGraph = (deltaToUndo) => {
|
|
|
const d3Updater = new D3GraphUpdater(setGraph, x, y);
|
|
|
graphState.unexec(deltaToUndo, d3Updater);
|
|
|
- setState(({deltaGraphL0: prevDeltaGraphL0, deltaGraphL1: prevDeltaGraphL1, ...rest}) => ({
|
|
|
- deltaGraphL1: deltaGraphReducer(prevDeltaGraphL1, {type: 'setDeltaInactive', delta: deltaToUndo}),
|
|
|
- deltaGraphL0: deltaToUndo.deltas.reduce((prevDeltaGraphL0, delta) => deltaGraphReducer(prevDeltaGraphL0, {type: 'setDeltaInactive', delta}),prevDeltaGraphL0),
|
|
|
- ...rest,
|
|
|
- }));
|
|
|
+
|
|
|
+ setDeltaGraphL0(deltaGraphL0 => deltaToUndo.deltas.reduce((deltaGraphL0, delta) => deltaGraphReducer(deltaGraphL0, {type: 'setDeltaInactive', delta}), deltaGraphL0));
|
|
|
+ setDeltaGraphL1(deltaGraphL1 => deltaGraphReducer(deltaGraphL1, {type: 'setDeltaInactive', delta: deltaToUndo}));
|
|
|
};
|
|
|
const redoWithoutUpdatingHistoryGraph = (deltaToRedo) => {
|
|
|
const d3Updater = new D3GraphUpdater(setGraph, x, y);
|
|
|
graphState.exec(deltaToRedo, d3Updater);
|
|
|
- setState(({deltaGraphL0: prevDeltaGraphL0, deltaGraphL1: prevDeltaGraphL1, ...rest}) => ({
|
|
|
- deltaGraphL1: deltaGraphReducer(prevDeltaGraphL1, {type: 'setDeltaActive', delta: deltaToRedo}),
|
|
|
- deltaGraphL0: deltaToRedo.deltas.reduce((prevDeltaGraphL0, delta) => deltaGraphReducer(prevDeltaGraphL0, {type: 'setDeltaActive', delta}),
|
|
|
- prevDeltaGraphL0),
|
|
|
- ...rest,
|
|
|
- }));
|
|
|
+
|
|
|
+ setDeltaGraphL0(deltaGraphL0 => deltaToRedo.deltas.reduce((deltaGraphL0, delta) => deltaGraphReducer(deltaGraphL0, {type: 'setDeltaActive', delta}), deltaGraphL0));
|
|
|
+ setDeltaGraphL1(deltaGraphL1 => deltaGraphReducer(deltaGraphL1, {type: 'setDeltaActive', delta: deltaToRedo}));
|
|
|
};
|
|
|
const undo = (parentVersion, deltaToUndo) => {
|
|
|
undoWithoutUpdatingHistoryGraph(deltaToUndo);
|
|
|
currentVersion = parentVersion;
|
|
|
- setState(({historyGraph: prevHistoryGraph, version: prevVersion, ...rest}) => ({
|
|
|
- version: parentVersion,
|
|
|
- historyGraph: historyGraphReducer(historyGraphReducer(prevHistoryGraph,
|
|
|
- {type: 'highlightVersion', version: prevVersion, bold: false}),
|
|
|
- {type: 'highlightVersion', version: parentVersion, bold: true}),
|
|
|
- ...rest,
|
|
|
- }));
|
|
|
+
|
|
|
+ setVersion(prevVersion => {
|
|
|
+ setHistoryGraph(historyGraph => historyGraphReducer(historyGraphReducer(historyGraph,
|
|
|
+ {type: 'highlightVersion', version: prevVersion, bold: false}),
|
|
|
+ {type: 'highlightVersion', version: parentVersion, bold: true}));
|
|
|
+ return parentVersion;
|
|
|
+ });
|
|
|
};
|
|
|
const redo = (childVersion, deltaToRedo) => {
|
|
|
redoWithoutUpdatingHistoryGraph(deltaToRedo);
|
|
|
currentVersion = childVersion;
|
|
|
- setState(({historyGraph: prevHistoryGraph, version: prevVersion, ...rest}) => ({
|
|
|
- version: childVersion,
|
|
|
- historyGraph: historyGraphReducer(historyGraphReducer(prevHistoryGraph,
|
|
|
- {type: 'highlightVersion', version: prevVersion, bold: false}),
|
|
|
- {type: 'highlightVersion', version: childVersion, bold: true}),
|
|
|
- ...rest,
|
|
|
- }));
|
|
|
+
|
|
|
+ setVersion(prevVersion => {
|
|
|
+ setHistoryGraph(historyGraph => historyGraphReducer(historyGraphReducer(historyGraph,
|
|
|
+ {type: 'highlightVersion', version: prevVersion, bold: false}),
|
|
|
+ {type: 'highlightVersion', version: childVersion, bold: true}));
|
|
|
+ return childVersion;
|
|
|
+ });
|
|
|
};
|
|
|
const gotoVersion = (chosenVersion: Version) => {
|
|
|
const path = currentVersion.findPathTo(chosenVersion);
|
|
|
@@ -211,13 +197,12 @@ export function newOnion({readonly, primitiveRegistry, versionRegistry}) {
|
|
|
}
|
|
|
}
|
|
|
currentVersion = chosenVersion;
|
|
|
- setState(({historyGraph, version: oldVersion, ...rest}) => ({
|
|
|
- version: chosenVersion,
|
|
|
- historyGraph: historyGraphReducer(historyGraphReducer(historyGraph,
|
|
|
- {type: 'highlightVersion', version: oldVersion, bold: false}),
|
|
|
- {type: 'highlightVersion', version: chosenVersion, bold: true}),
|
|
|
- ...rest,
|
|
|
- }));
|
|
|
+ setVersion(prevVersion => {
|
|
|
+ setHistoryGraph(historyGraph => historyGraphReducer(historyGraphReducer(historyGraph,
|
|
|
+ {type: 'highlightVersion', version: prevVersion, bold: false}),
|
|
|
+ {type: 'highlightVersion', version: chosenVersion, bold: true}));
|
|
|
+ return chosenVersion;
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
const reducer = {
|
|
|
@@ -242,10 +227,10 @@ export function newOnion({readonly, primitiveRegistry, versionRegistry}) {
|
|
|
const callbacks = Object.assign({}, defaultCallbacks, overridenCallbacks(reducer));
|
|
|
|
|
|
const graphStateComponent = readonly ?
|
|
|
- <GraphView graphData={state.graph} help={helpText.graphEditorReadonly} mouseUpHandler={()=>{}} />
|
|
|
+ <GraphView graphData={graph} help={helpText.graphEditorReadonly} mouseUpHandler={()=>{}} />
|
|
|
: <InfoHoverCardOverlay contents={helpText.graphEditor}>
|
|
|
<D3GraphEditable
|
|
|
- graph={state.graph}
|
|
|
+ graph={graph}
|
|
|
graphState={graphState}
|
|
|
forces={defaultGraphForces}
|
|
|
setNextNodePosition={(newX,newY) => {x = newX; y = newY;}}
|
|
|
@@ -262,11 +247,11 @@ export function newOnion({readonly, primitiveRegistry, versionRegistry}) {
|
|
|
}
|
|
|
},
|
|
|
};
|
|
|
- const deltaGraphL0Component = <GraphView<Delta,null> graphData={state.deltaGraphL0} {...deltaComponentProps} />;
|
|
|
- const deltaGraphL1Component = <GraphView<Delta,null> graphData={state.deltaGraphL1} {...deltaComponentProps} />;
|
|
|
+ const deltaGraphL0Component = <GraphView<Delta,null> graphData={deltaGraphL0} {...deltaComponentProps} />;
|
|
|
+ const deltaGraphL1Component = <GraphView<Delta,null> graphData={deltaGraphL1} {...deltaComponentProps} />;
|
|
|
|
|
|
const historyComponentWithMerge = <MergeView
|
|
|
- history={state.historyGraph}
|
|
|
+ history={historyGraph}
|
|
|
forces={defaultGraphForces}
|
|
|
versionRegistry={versionRegistry}
|
|
|
onMerge={outputs => callbacks.onMerge?.(outputs)}
|
|
|
@@ -277,7 +262,7 @@ export function newOnion({readonly, primitiveRegistry, versionRegistry}) {
|
|
|
|
|
|
const rountangleEditor = <InfoHoverCardOverlay contents={helpText.rountangleEditor}>
|
|
|
<RountangleEditor
|
|
|
- graph={state.graph}
|
|
|
+ graph={graph}
|
|
|
graphState={graphState}
|
|
|
onUserEdit={callbacks.onUserEdit}
|
|
|
/>
|
|
|
@@ -301,8 +286,8 @@ export function newOnion({readonly, primitiveRegistry, versionRegistry}) {
|
|
|
</Mantine.Menu>;
|
|
|
|
|
|
}
|
|
|
- const undoButton = makeUndoOrRedoButton(state.version.parents, "Undo", <Icons.IconChevronLeft/>, null, callbacks.onUndoClicked);
|
|
|
- const redoButton = makeUndoOrRedoButton(state.version.children, "Redo", null, <Icons.IconChevronRight/>, callbacks.onRedoClicked);
|
|
|
+ const undoButton = makeUndoOrRedoButton(version.parents, "Undo", <Icons.IconChevronLeft/>, null, callbacks.onUndoClicked);
|
|
|
+ const redoButton = makeUndoOrRedoButton(version.children, "Redo", null, <Icons.IconChevronRight/>, callbacks.onRedoClicked);
|
|
|
|
|
|
const undoRedoButtons = <>
|
|
|
{undoButton}
|
|
|
@@ -310,7 +295,7 @@ export function newOnion({readonly, primitiveRegistry, versionRegistry}) {
|
|
|
{redoButton}
|
|
|
</>;
|
|
|
|
|
|
- const stackedUndoButtons = state.version.parents.map(([parentVersion,deltaToUndo]) => {
|
|
|
+ const stackedUndoButtons = version.parents.map(([parentVersion,deltaToUndo]) => {
|
|
|
return (
|
|
|
<div key={fullVersionId(parentVersion)}>
|
|
|
<Mantine.Button fullWidth={true} compact={true} leftIcon={<Icons.IconChevronLeft size={18}/>} onClick={callbacks.onUndoClicked?.bind(null, parentVersion, deltaToUndo)}>
|
|
|
@@ -320,7 +305,7 @@ export function newOnion({readonly, primitiveRegistry, versionRegistry}) {
|
|
|
</div>
|
|
|
);
|
|
|
});
|
|
|
- const stackedRedoButtons = state.version.children.map(([childVersion,deltaToRedo]) => {
|
|
|
+ const stackedRedoButtons = version.children.map(([childVersion,deltaToRedo]) => {
|
|
|
return (
|
|
|
<div key={fullVersionId(childVersion)}>
|
|
|
<Mantine.Button style={{width: "100%"}} compact={true} rightIcon={<Icons.IconChevronRight size={18}/>} onClick={callbacks.onRedoClicked?.bind(null, childVersion, deltaToRedo)}>
|
|
|
@@ -367,7 +352,9 @@ export function newOnion({readonly, primitiveRegistry, versionRegistry}) {
|
|
|
|
|
|
|
|
|
return {
|
|
|
- state,
|
|
|
+ state: {
|
|
|
+ version,
|
|
|
+ },
|
|
|
reducer,
|
|
|
components: {
|
|
|
graphStateComponent,
|
|
|
@@ -385,10 +372,7 @@ export function newOnion({readonly, primitiveRegistry, versionRegistry}) {
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
- initialState,
|
|
|
graphState,
|
|
|
- versionRegistry,
|
|
|
- getCurrentVersion,
|
|
|
useOnion,
|
|
|
}
|
|
|
}
|