Add Box component to improve renderers performance

This commit is contained in:
Nilay Majorwar 2022-02-22 14:54:17 +05:30
parent 3e6fb8c780
commit 032f9a6792
6 changed files with 83 additions and 69 deletions

View File

@ -1,10 +1,18 @@
import { Card, Colors, Icon, IconName } from "@blueprintjs/core";
import * as React from "react";
import { Colors, Icon, IconName } from "@blueprintjs/core";
import { RendererProps } from "../types";
import { Box } from "../ui-utils";
import { Bfg93Direction, Bfg93RS } from "./constants";
/** Common border color for dark and light, using transparency */
export const BorderColor = Colors.GRAY3 + "55";
// Parameters for cell sizing, balanced to span the full row width
// Constraint: `(width% + 2 * margin%) * ROWSIZE = 100%`
const ROWSIZE = 8;
const CELL_WIDTH = "12%";
const CELL_MARGIN = "5px 0.25%";
const styles = {
placeholderDiv: {
height: "100%",
@ -33,9 +41,9 @@ const styles = {
},
stackItem: {
// Sizing
width: "10%",
height: "40px",
margin: "5px 0.25%",
width: CELL_WIDTH,
margin: CELL_MARGIN,
height: "50px",
// Center-align values
display: "flex",
justifyContent: "center",
@ -50,10 +58,10 @@ const DirectionIcons: { [k: string]: IconName } = {
[Bfg93Direction.DOWN]: "arrow-down",
};
const StackItem = ({ value }: { value: number }) => {
const cellStyle = { ...styles.stackItem };
return <Card style={cellStyle}>{value}</Card>;
};
/** Component for displaying a single stack item */
const StackItem = React.memo(({ value }: { value: number }) => {
return <Box style={styles.stackItem}>{value}</Box>;
});
export const Renderer = ({ state }: RendererProps<Bfg93RS>) => {
if (state == null)
@ -64,7 +72,6 @@ export const Renderer = ({ state }: RendererProps<Bfg93RS>) => {
<div style={styles.dirnContainer}>
<span style={{ fontWeight: "bold", marginRight: 5 }}>Direction: </span>
<Icon icon={DirectionIcons[state.direction]} />
{/* <span style={{ marginLeft: 10 }} /> */}
<span style={{ marginLeft: 30, fontWeight: "bold", marginRight: 5 }}>
String mode:{" "}
</span>

View File

@ -81,9 +81,12 @@ export const editorTokensProvider: MonacoTokensProvider = {
};
/** Serialize tape from object format into linear array */
export const serializeTapeMap = (tape: BFRS["tape"]): number[] => {
export const serializeTapeMap = (
tape: BFRS["tape"],
minCells: number = 0
): number[] => {
const cellIdxs = Object.keys(tape).map((s) => parseInt(s, 10));
const maxCellIdx = Math.max(15, ...cellIdxs);
const maxCellIdx = Math.max(minCells - 1, ...cellIdxs);
const linearTape: number[] = Array(maxCellIdx + 1).fill(0);
cellIdxs.forEach((i) => (linearTape[i] = tape[i] || 0));
return linearTape;

View File

@ -1,14 +1,17 @@
import { Card, Colors } from "@blueprintjs/core";
import { CSSProperties } from "react";
import { useDarkMode } from "../../ui/providers/dark-mode-provider";
import * as React from "react";
import { RendererProps } from "../types";
import { Box } from "../ui-utils";
import { BFRS, serializeTapeMap } from "./common";
// Colors used as background of active cells
const darkActiveBG = Colors.DARK_GRAY2;
const lightActiveBG = Colors.LIGHT_GRAY3;
/** Number of cells shown in a single row */
const ROWSIZE = 8;
const styles: { [k: string]: CSSProperties } = {
// Parameters for cell sizing, balanced to span the full row width
// Constraint: `(width% + 2 * margin%) * ROWSIZE = 100%`
const CELL_WIDTH = "12%";
const CELL_MARGIN = "5px 0.25%";
const styles: { [k: string]: React.CSSProperties } = {
container: {
padding: 10,
height: "100%",
@ -19,9 +22,9 @@ const styles: { [k: string]: CSSProperties } = {
},
cell: {
// Sizing
width: "12%",
width: CELL_WIDTH,
margin: CELL_MARGIN,
height: "50px",
margin: "5px 0.25%",
// Center-align values
display: "flex",
justifyContent: "center",
@ -30,22 +33,24 @@ const styles: { [k: string]: CSSProperties } = {
};
/** Component for displaying a single tape cell */
const Cell = ({ value, active }: { value: number; active: boolean }) => {
const { isDark } = useDarkMode();
const cellStyle = { ...styles.cell };
const activeBg = isDark ? darkActiveBG : lightActiveBG;
if (active) {
cellStyle.backgroundColor = activeBg;
cellStyle.fontWeight = "bold";
const Cell = React.memo(
({ value, active }: { value: number; active: boolean }) => {
return (
<Box
intent={active ? "active" : "plain"}
style={{ ...styles.cell, fontWeight: active ? "bold" : undefined }}
>
{value}
</Box>
);
}
return <Card style={cellStyle}>{value}</Card>;
};
);
/** Renderer for Brainfuck */
export const Renderer = ({ state }: RendererProps<BFRS>) => {
return (
<div style={styles.container}>
{serializeTapeMap(state?.tape || {}).map((num, i) => (
{serializeTapeMap(state?.tape || {}, 2 * ROWSIZE).map((num, i) => (
<Cell value={num} key={i} active={(state?.pointer || 0) === i} />
))}
</div>

View File

@ -1,5 +1,5 @@
import { Box } from "../../ui-utils";
import { CharacterValue } from "../common";
import { SimpleTag } from "./utils";
type Props = {
name: string;
@ -13,9 +13,9 @@ export const CharacterRow = (props: Props) => {
<div style={{ margin: "20px 10px" }}>
<div>
<b style={{ marginRight: 5 }}>{name}:</b>{" "}
<pre style={{ display: "inline" }}>{value.value}</pre>
<pre style={{ display: "inline", marginRight: 15 }}>{value.value}</pre>
{value.stack.map((v, i) => (
<SimpleTag key={i}>{v}</SimpleTag>
<Box key={i}>{v}</Box>
))}
</div>
<div></div>

View File

@ -1,5 +1,6 @@
import { Tag, Text } from "@blueprintjs/core";
import { SimpleTag } from "./utils";
import React from "react";
import { Text } from "@blueprintjs/core";
import { Box } from "../../ui-utils";
const styles = {
charChip: {
@ -22,20 +23,16 @@ export const TopBar = (props: Props) => {
const characterChips =
charactersOnStage.length === 0 ? (
<Tag large minimal>
The stage is empty
</Tag>
<Box>The stage is empty</Box>
) : (
charactersOnStage.map((character) => {
return (
<SimpleTag
key={character}
intent={character === currSpeaker ? "active" : undefined}
>
{character}
</SimpleTag>
);
})
charactersOnStage.map((character) => (
<Box
key={character}
intent={character === currSpeaker ? "active" : "plain"}
>
{character}
</Box>
))
);
return (
@ -46,9 +43,9 @@ export const TopBar = (props: Props) => {
<Text tagName="span" style={styles.questionText}>
Answer to question:
</Text>
<SimpleTag intent={questionState ? "success" : "danger"}>
<Box intent={questionState ? "success" : "danger"}>
{questionState ? "yes" : "no"}
</SimpleTag>
</Box>
</>
)}
</div>

View File

@ -1,41 +1,43 @@
import { CSSProperties } from "react";
import { Colors } from "@blueprintjs/core";
import { useDarkMode } from "../../../ui/providers/dark-mode-provider";
import { useDarkMode } from "../ui/providers/dark-mode-provider";
const backgroundColorsLight = {
success: Colors.GREEN3,
danger: Colors.RED3,
plain: Colors.GRAY3,
active: Colors.DARK_GRAY1,
success: Colors.GREEN5,
danger: Colors.RED5,
plain: Colors.LIGHT_GRAY1,
active: Colors.GRAY4,
};
const backgroundColorsDark = {
success: Colors.GREEN3,
danger: Colors.RED3,
plain: Colors.GRAY3,
active: Colors.LIGHT_GRAY5,
success: Colors.GREEN1,
danger: Colors.RED1,
plain: Colors.DARK_GRAY5,
active: Colors.GRAY1,
};
const foregroundColorsLight = {
success: Colors.GREEN2,
danger: Colors.RED2,
success: Colors.GREEN1,
danger: Colors.RED1,
plain: Colors.DARK_GRAY1,
active: Colors.LIGHT_GRAY5,
active: Colors.DARK_GRAY1,
};
const foregroundColorsDark = {
success: Colors.GREEN5,
danger: Colors.RED5,
plain: Colors.LIGHT_GRAY5,
active: Colors.DARK_GRAY1,
active: Colors.LIGHT_GRAY5,
};
/**
* Utility component that renders a tag similar to BlueprintJS tags, but underneath
* is just a single span tag with no frills and high performance.
* Utility component for rendering a simple general-purpose stylable box. Useful
* for performance-critical components in visualisation renderers.
*/
export const SimpleTag = (props: {
export const Box = (props: {
children: React.ReactNode;
intent?: "success" | "danger" | "active";
intent?: "plain" | "success" | "danger" | "active";
style?: CSSProperties;
}) => {
const { isDark } = useDarkMode();
const intent = props.intent == null ? "plain" : props.intent;
@ -49,9 +51,9 @@ export const SimpleTag = (props: {
margin: 5,
padding: "5px 10px",
borderRadius: 3,
backgroundColor:
backgroundMap[intent] + (intent === "active" ? "aa" : "55"),
backgroundColor: backgroundMap[intent],
color: foregroundMap[intent],
...props.style,
}}
>
{props.children}