Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions 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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.3.1",
"@mui/material": "^7.3.1",
"clsx": "^2.1.1",
"debounce": "^2.2.0",
"es-toolkit": "^1.52.0",
"eventemitter3": "^5.0.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import clsx from "clsx";
import {
type SimulationValue,
wrappingIncrementSimulationValue,
} from "../../../../../../../../store/simulation";
import { stringifySimulationValue } from "../../../../../store/slices/core";
import styles from "./style.module.css";

/** Values up to this bit width are shown bit by bit */
const maxBitWidthToShowBits = 8;
/** Values up to this bit width are shown with wider bits */
const maxBitWidthToShowWideBits = 4;

type Props = {
value: SimulationValue;
/** `null` if the value is read-only */
onChange: ((value: SimulationValue) => void) | null;
};

export function CCComponentEditorRendererNodeInterfaceRendererValue({
value,
onChange,
}: Props) {
if (value.length > maxBitWidthToShowBits) {
const label = stringifySimulationValue(value);
return onChange ? (
<button
type="button"
className={styles.text}
onPointerDown={(e) => e.stopPropagation()}
onClick={() => onChange(wrappingIncrementSimulationValue(value))}
>
{label}
</button>
) : (
<span className={styles.text}>{label}</span>
);
}

const isSingleBit = value.length === 1;
return (
<span
className={clsx(
styles.bits,
isSingleBit
? styles.single
: value.length <= maxBitWidthToShowWideBits && styles.wide,
)}
>
{value.map((bit, index) => {
const className = clsx(styles.bit, bit && styles.on);
const label = isSingleBit ? (bit ? "ON" : "OFF") : bit ? "1" : "0";
return onChange ? (
<button
// biome-ignore lint/suspicious/noArrayIndexKey: bits are identified by their index
key={index}
type="button"
className={className}
onPointerDown={(e) => e.stopPropagation()}
onClick={() => onChange(value.with(index, !bit))}
>
{label}
</button>
) : (
// biome-ignore lint/suspicious/noArrayIndexKey: bits are identified by their index
<span key={index} className={className}>
{label}
</span>
);
})}
</span>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
.text {
padding: 0;
border: none;
background: none;
color: inherit;
font: inherit;
text-align: center;

&:is(button) {
cursor: pointer;
}
}

.bits {
display: flex;
align-self: center;
overflow: hidden;
border: 0.5px solid var(--color-border);
border-radius: 2px;

&.single {
align-self: stretch;
}
}

.bit {
display: flex;
justify-content: center;
align-items: center;
width: 10px;
height: 12px;
padding: 0;
border: none;
background: var(--color-white);
color: inherit;
font: inherit;
font-size: 8px;
text-align: center;

& + & {
border-left: 0.5px solid var(--color-border);
}

&.on {
background: var(--color-primary-light);
color: var(--color-white);
}

.single > & {
flex: 1;
}

.wide > & {
width: 16px;
}

&:is(button) {
cursor: pointer;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
CCComponentEditorRendererNodeLayoutSource,
} from "../../types";

const bodyWidth = 70;
const bodyWidth = 90;
const arrowDepth = 10;

export const ccComponentEditorRendererNodeInterfaceLayoutConstants = {
Expand Down
41 changes: 14 additions & 27 deletions src/pages/edit/Editor/renderer/Node/components/Interface/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import nullthrows from "nullthrows";
import { ccIntrinsicComponentTypes } from "../../../../../../../store/intrinsics/types";
import { useStore } from "../../../../../../../store/react";
import { useComponentPin } from "../../../../../../../store/react/selectors";
import { wrappingIncrementSimulationValue } from "../../../../../../../store/simulation";
import type { SimulationValue } from "../../../../../../../store/simulation";
import { useComponentEditorStore } from "../../../../store";
import { stringifySimulationValue } from "../../../../store/slices/core";
import nodeStyles from "../../style.module.css";
import type { CCComponentEditorRendererNodeRendererProps } from "../../types";
import { ccComponentEditorRendererNodeInterfaceLayoutConstants as constants } from "./geometry";
import { CCComponentEditorRendererNodeInterfaceRendererRenameButton } from "./RenameButton";
import styles from "./style.module.css";
import { CCComponentEditorRendererNodeInterfaceRendererValue } from "./Value";

export function CCComponentEditorRendererNodeInterfaceRenderer(
props: CCComponentEditorRendererNodeRendererProps,
Expand All @@ -26,29 +26,23 @@ export function CCComponentEditorRendererNodeInterfaceRenderer(
);
const isEditMode = editorState.editorMode === "edit";

const { value, onClick } = isEditMode
? { value: null, onClick: null }
const simulation = isEditMode
? null
: componentPin.type === "input"
? (() => {
const inputValueKey = {
componentPinId: componentPin.id,
timeStep: editorState.timeStep,
};
const value = nullthrows(editorState.getInputValue(inputValueKey));
return {
value: stringifySimulationValue(value),
onClick: () =>
editorState.setInputValue(
inputValueKey,
wrappingIncrementSimulationValue(value),
),
value: nullthrows(editorState.getInputValue(inputValueKey)),
onChange: (value: SimulationValue) =>
editorState.setInputValue(inputValueKey, value),
};
})()
: {
value: stringifySimulationValue(
nullthrows(editorState.getNodePinValue(nodePin.id)),
),
onClick: null,
value: nullthrows(editorState.getNodePinValue(nodePin.id)),
onChange: null,
};

const { bodyWidth, arrowDepth, width, height } = constants;
Expand All @@ -65,16 +59,6 @@ export function CCComponentEditorRendererNodeInterfaceRenderer(
points={points}
strokeLinejoin="round"
className={nodeStyles.shape}
{...(onClick
? {
onPointerDown: (e: React.PointerEvent) => {
// Prevent the node from being selected or dragged
e.stopPropagation();
onClick();
},
style: { cursor: "pointer" },
}
: {})}
/>
<foreignObject
x={bodyX}
Expand All @@ -83,7 +67,7 @@ export function CCComponentEditorRendererNodeInterfaceRenderer(
height={height}
className={styles.contentContainer}
>
{isEditMode ? (
{simulation === null ? (
<div className={styles.edit}>
<span className={styles.name}>{componentPin.name}</span>
<CCComponentEditorRendererNodeInterfaceRendererRenameButton
Expand All @@ -96,7 +80,10 @@ export function CCComponentEditorRendererNodeInterfaceRenderer(
) : (
<div className={styles.simulation}>
<span className={styles.name}>{componentPin.name}</span>
<span className={styles.value}>{value}</span>
<CCComponentEditorRendererNodeInterfaceRendererValue
value={simulation.value}
onChange={simulation.onChange}
/>
</div>
)}
</foreignObject>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,9 @@
justify-content: center;
height: 100%;
padding: 0 4px;
gap: 1px;

& .name {
font-size: 8px;
}

& .value {
text-align: center;
}
}
1 change: 1 addition & 0 deletions src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
--color-white: #ffffff;
--color-text-primary: #444444;
--color-primary: #009966;
--color-primary-light: #33ad84;
--color-error: #ff0000;
--color-border: #bbbbbb;
--color-editor-background: #fafafa;
Expand Down
1 change: 1 addition & 0 deletions src/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const theme = {
white: "#ffffff",
textPrimary: "#444444",
primary: "#009966",
primaryLight: "#33ad84",
error: "#ff0000",
border: "#bbbbbb",
editorBackground: "#fafafa",
Expand Down
Loading