fewer unnecessary re-renders
This commit is contained in:
parent
4f9a546fd1
commit
0fac3977b3
2 changed files with 95 additions and 34 deletions
|
|
@ -38,3 +38,31 @@ export function arraysEqual<T>(a: T[], b: T[], cmp: (a: T, b: T) => boolean = (a
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setsEqual<T>(a: Set<T>, b: Set<T>): boolean {
|
||||||
|
if (a === b)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (a.size !== b.size)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (const itemA of a)
|
||||||
|
if (!b.has(itemA))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function objectsEqual<T>(a: {[key: string]: T}, b: {[key: string]: T}, cmp: (a: T, b: T) => boolean = (a,b)=>a===b): boolean {
|
||||||
|
if (a === b)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (Object.keys(a).length !== Object.keys(b).length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (const [keyA, valueA] of Object.entries(a))
|
||||||
|
if (!cmp(b[keyA], valueA))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ import { Connections, detectConnections } from "../statecharts/detect_connection
|
||||||
|
|
||||||
import "./VisualEditor.css";
|
import "./VisualEditor.css";
|
||||||
import { TraceState } from "@/App/App";
|
import { TraceState } from "@/App/App";
|
||||||
|
import { Mode } from "@/statecharts/runtime_types";
|
||||||
|
import { arraysEqual, objectsEqual, setsEqual } from "@/App/util";
|
||||||
|
|
||||||
export type VisualEditorState = {
|
export type VisualEditorState = {
|
||||||
rountangles: Rountangle[];
|
rountangles: Rountangle[];
|
||||||
|
|
@ -701,29 +703,8 @@ export const VisualEditor = memo(function VisualEditor({state, setState, trace,
|
||||||
|
|
||||||
{(rootErrors.length>0) && <text className="error" x={5} y={20}>{rootErrors.join(' ')}</text>}
|
{(rootErrors.length>0) && <text className="error" x={5} y={20}>{rootErrors.join(' ')}</text>}
|
||||||
|
|
||||||
{state.rountangles.map(rountangle => {
|
<Rountangles rountangles={state.rountangles} {...{selection, sidesToHighlight, rountanglesToHighlight, errors, highlightActive}}/>
|
||||||
return <RountangleSVG
|
<Diamonds diamonds={state.diamonds} {...{selection, sidesToHighlight, rountanglesToHighlight, errors}}/>
|
||||||
key={rountangle.uid}
|
|
||||||
rountangle={rountangle}
|
|
||||||
selected={selection.find(r => r.uid === rountangle.uid)?.parts as RectSide[] || []}
|
|
||||||
highlight={[...(sidesToHighlight[rountangle.uid] || []), ...(rountanglesToHighlight[rountangle.uid]?["left","right","top","bottom"]:[]) as RectSide[]]}
|
|
||||||
error={errors
|
|
||||||
.filter(({shapeUid}) => shapeUid === rountangle.uid)
|
|
||||||
.map(({message}) => message).join(', ')}
|
|
||||||
active={highlightActive.has(rountangle.uid)}
|
|
||||||
/>})}
|
|
||||||
|
|
||||||
{state.diamonds.map(diamond => <>
|
|
||||||
<DiamondSVG
|
|
||||||
key={diamond.uid}
|
|
||||||
diamond={diamond}
|
|
||||||
selected={selection.find(r => r.uid === diamond.uid)?.parts as RectSide[] || []}
|
|
||||||
highlight={[...(sidesToHighlight[diamond.uid] || []), ...(rountanglesToHighlight[diamond.uid]?["left","right","top","bottom"]:[]) as RectSide[]]}
|
|
||||||
error={errors
|
|
||||||
.filter(({shapeUid}) => shapeUid === diamond.uid)
|
|
||||||
.map(({message}) => message).join(', ')}
|
|
||||||
active={false}/>
|
|
||||||
</>)}
|
|
||||||
|
|
||||||
{state.history.map(history => <>
|
{state.history.map(history => <>
|
||||||
<HistorySVG
|
<HistorySVG
|
||||||
|
|
@ -756,17 +737,7 @@ export const VisualEditor = memo(function VisualEditor({state, setState, trace,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{state.texts.map(txt => {
|
<Texts texts={state.texts} {...{selection, textsToHighlight, errors, onEditText, setModal}}/>
|
||||||
return <TextSVG
|
|
||||||
key={txt.uid}
|
|
||||||
error={errors.find(({shapeUid}) => txt.uid === shapeUid)}
|
|
||||||
text={txt}
|
|
||||||
selected={Boolean(selection.find(s => s.uid === txt.uid)?.parts?.length)}
|
|
||||||
highlight={textsToHighlight.hasOwnProperty(txt.uid)}
|
|
||||||
onEdit={onEditText}
|
|
||||||
setModal={setModal}
|
|
||||||
/>
|
|
||||||
})}
|
|
||||||
|
|
||||||
{selectingState && <Selecting {...selectingState} />}
|
{selectingState && <Selecting {...selectingState} />}
|
||||||
</svg>;
|
</svg>;
|
||||||
|
|
@ -782,6 +753,68 @@ export function rountangleMinSize(size: Vec2D): Vec2D {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Rountangles = memo(function Rountangles({rountangles, selection, sidesToHighlight, rountanglesToHighlight, errors, highlightActive}: {rountangles: Rountangle[], selection: Selection, sidesToHighlight: {[key: string]: RectSide[]}, rountanglesToHighlight: {[key: string]: boolean}, errors: TraceableError[], highlightActive: Mode}) {
|
||||||
|
return <>{rountangles.map(rountangle => {
|
||||||
|
return <RountangleSVG
|
||||||
|
key={rountangle.uid}
|
||||||
|
rountangle={rountangle}
|
||||||
|
selected={selection.find(r => r.uid === rountangle.uid)?.parts as RectSide[] || []}
|
||||||
|
highlight={[...(sidesToHighlight[rountangle.uid] || []), ...(rountanglesToHighlight[rountangle.uid]?["left","right","top","bottom"]:[]) as RectSide[]]}
|
||||||
|
error={errors
|
||||||
|
.filter(({shapeUid}) => shapeUid === rountangle.uid)
|
||||||
|
.map(({message}) => message).join(', ')}
|
||||||
|
active={highlightActive.has(rountangle.uid)}
|
||||||
|
/>})}</>;
|
||||||
|
}, (p, n) => {
|
||||||
|
return arraysEqual(p.rountangles, n.rountangles)
|
||||||
|
&& arraysEqual(p.selection, n.selection)
|
||||||
|
&& objectsEqual(p.sidesToHighlight, n.sidesToHighlight)
|
||||||
|
&& objectsEqual(p.rountanglesToHighlight, n.rountanglesToHighlight)
|
||||||
|
&& arraysEqual(p.errors, n.errors)
|
||||||
|
&& setsEqual(p.highlightActive, n.highlightActive);
|
||||||
|
});
|
||||||
|
|
||||||
|
const Diamonds = memo(function Diamonds({diamonds, selection, sidesToHighlight, rountanglesToHighlight, errors}: {diamonds: Diamond[], selection: Selection, sidesToHighlight: {[key: string]: RectSide[]}, rountanglesToHighlight: {[key: string]: boolean}, errors: TraceableError[]}) {
|
||||||
|
return <>{diamonds.map(diamond => <>
|
||||||
|
<DiamondSVG
|
||||||
|
key={diamond.uid}
|
||||||
|
diamond={diamond}
|
||||||
|
selected={selection.find(r => r.uid === diamond.uid)?.parts as RectSide[] || []}
|
||||||
|
highlight={[...(sidesToHighlight[diamond.uid] || []), ...(rountanglesToHighlight[diamond.uid]?["left","right","top","bottom"]:[]) as RectSide[]]}
|
||||||
|
error={errors
|
||||||
|
.filter(({shapeUid}) => shapeUid === diamond.uid)
|
||||||
|
.map(({message}) => message).join(', ')}
|
||||||
|
active={false}/>
|
||||||
|
</>)}</>;
|
||||||
|
}, (p, n) => {
|
||||||
|
return arraysEqual(p.diamonds, n.diamonds)
|
||||||
|
&& arraysEqual(p.selection, n.selection)
|
||||||
|
&& objectsEqual(p.sidesToHighlight, n.sidesToHighlight)
|
||||||
|
&& objectsEqual(p.rountanglesToHighlight, n.rountanglesToHighlight)
|
||||||
|
&& arraysEqual(p.errors, n.errors);
|
||||||
|
});
|
||||||
|
|
||||||
|
const Texts = memo(function Texts({texts, selection, textsToHighlight, errors, onEditText, setModal}: {texts: Text[], selection: Selection, textsToHighlight: {[key: string]: boolean}, errors: TraceableError[], onEditText: (text: Text, newText: string) => void, setModal: Dispatch<SetStateAction<ReactElement|null>>}) {
|
||||||
|
return <>{texts.map(txt => {
|
||||||
|
return <TextSVG
|
||||||
|
key={txt.uid}
|
||||||
|
error={errors.find(({shapeUid}) => txt.uid === shapeUid)}
|
||||||
|
text={txt}
|
||||||
|
selected={Boolean(selection.find(s => s.uid === txt.uid)?.parts?.length)}
|
||||||
|
highlight={textsToHighlight.hasOwnProperty(txt.uid)}
|
||||||
|
onEdit={onEditText}
|
||||||
|
setModal={setModal}
|
||||||
|
/>
|
||||||
|
})}</>;
|
||||||
|
}, (p, n) => {
|
||||||
|
return arraysEqual(p.texts, n.texts)
|
||||||
|
&& arraysEqual(p.selection, n.selection)
|
||||||
|
&& objectsEqual(p.textsToHighlight, n.textsToHighlight)
|
||||||
|
&& arraysEqual(p.errors, n.errors)
|
||||||
|
&& p.onEditText === n.onEditText
|
||||||
|
&& p.setModal === n.setModal;
|
||||||
|
});
|
||||||
|
|
||||||
export function Selecting(props: SelectingState) {
|
export function Selecting(props: SelectingState) {
|
||||||
const normalizedRect = normalizeRect(props!);
|
const normalizedRect = normalizeRect(props!);
|
||||||
return <rect
|
return <rect
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue