add tabbar

This commit is contained in:
sfja 2026-06-10 05:19:22 +02:00
parent 022e8b89f7
commit a0f0486590
8 changed files with 148 additions and 51 deletions

View File

@ -3,6 +3,7 @@ import "./style.css";
import Canvas from "./Canvas";
import { Editor } from "./editor/Editor";
import Toolbar from "./Toolbar";
import Tabbar from "./Tabbar";
function App(): ReactElement {
const [editor] = useState(() => new Editor());
@ -12,8 +13,13 @@ function App(): ReactElement {
<>
<h1>nandsim</h1>
<div className="Editor">
<Toolbar editor={editor} canvasRef={canvasRef} />
<Canvas editor={editor} canvasRef={canvasRef} width={800} height={800} />
<div>
<Toolbar editor={editor} canvasRef={canvasRef} />
</div>
<main>
<Tabbar editor={editor} />
<Canvas editor={editor} canvasRef={canvasRef} />
</main>
</div>
</>
);

View File

@ -5,11 +5,9 @@ import { v2 } from "./editor/V2";
type Props = {
editor: Editor;
canvasRef: RefObject<HTMLCanvasElement | null>;
width: number;
height: number;
};
function Canvas({ editor, canvasRef, width, height }: Props): ReactElement {
function Canvas({ editor, canvasRef }: Props): ReactElement {
useEffect(() => {
if (!canvasRef.current) return;
@ -21,9 +19,7 @@ function Canvas({ editor, canvasRef, width, height }: Props): ReactElement {
<div className="Canvas">
<canvas
ref={canvasRef}
width={width}
height={height}
style={{ width, height, backgroundColor: "black" }}
style={{ backgroundColor: "black" }}
tabIndex={0}
onMouseDown={(ev) => {
const pos = v2(ev.nativeEvent.offsetX, ev.nativeEvent.offsetY);

20
editor/src/Tabbar.tsx Normal file
View File

@ -0,0 +1,20 @@
import { useEffect, useState, type ReactElement } from "react";
import type { Editor } from "./editor/Editor";
type Props = { editor: Editor };
function Tabbar({ editor }: Props): ReactElement {
const [selectedTool, setSelectedTool] = useState("select");
return (
<>
<div className="Tabbar">
<button className="active">&lt;unnamed&gt;</button>
<button>Component one</button>
<button>Another components</button>
</div>
</>
);
}
export default Tabbar;

View File

@ -15,18 +15,24 @@ function Toolbar({ editor, canvasRef }: Props): ReactElement {
return (
<>
<div className="Toolbar">
{editor.tools().map((tool, key) => (
<button
key={`${key}`}
className={selectedTool === tool ? "active" : ""}
onClick={() => {
editor.events.send({ tag: "SelectTool", tool });
canvasRef.current?.focus();
}}
>
{tool}
</button>
))}
<h2>Toolbar</h2>
<div>
{editor.tools().map((tool, key) => (
<button
key={`${key}`}
className={selectedTool === tool ? "active" : ""}
onClick={() => {
editor.events.send({ tag: "SelectTool", tool });
canvasRef.current?.focus();
}}
>
{tool}
</button>
))}
</div>
<div>
<button className="add">+</button>
</div>
</div>
</>
);

View File

@ -1,6 +1,5 @@
import { Cx, type Tool } from "./Cx";
import { EventBus } from "./events";
import { V2 } from "./V2";
export class Editor {
public events = new EventBus();

View File

@ -14,6 +14,13 @@ export class Renderer {
clear() {
const { canvas, c } = this;
const width = canvas.offsetWidth;
const height = canvas.offsetHeight;
canvas.width = width;
canvas.height = height;
c.fillStyle = "#666";
c.fillRect(0, 0, canvas.width, canvas.height);
}
@ -25,8 +32,8 @@ export class Renderer {
const gridSize = v2(20, 20);
c.fillStyle = "#111";
for (let y = 0; y < canvas.width / gridSize.x + 1; ++y) {
for (let x = 0; x < canvas.height / gridSize.y + 1; ++x) {
for (let y = 0; y < canvas.height / gridSize.y + 1; ++y) {
for (let x = 0; x < canvas.width / gridSize.x + 1; ++x) {
c.fillRect(
(this.offset.x % gridSize.x) + x * gridSize.x - dotSize.x / 2,
(this.offset.y % gridSize.y) + y * gridSize.y - dotSize.y / 2,

View File

@ -2,14 +2,21 @@
color-scheme: light dark;
}
* {
font-family: sans;
}
#root {
margin: 0 auto;
min-height: 100svh;
height: 100svh;
text-align: center;
display: flex;
flex-direction: column;
}
body {
margin: 0;
height: 100vh;
}
h1 {

View File

@ -1,37 +1,93 @@
.Editor {
display: flex;
flex-direction: row;
justify-content: center;
.Toolbar {
display: flex;
flex-direction: row;
justify-content: center;
height: 100%;
.Toolbar {
display: flex;
flex-direction: column;
gap: 2px;
max-width: 300px;
height: 100%;
background-color: #2b2a33;
button {
padding-left: 5px;
padding-right: 5px;
padding-top: 5px;
padding-bottom: 5px;
font-size: 1rem;
text-transform: capitalize;
width: 200px;
text-align: left;
border: 2px solid gray;
border-radius: 5px;
}
button.active {
border: 2px solid #ff8800;
}
h2 {
margin: 0;
}
.Canvas {
canvas {
image-rendering: pixelated;
}
button {
padding-left: 20px;
padding-right: 20px;
padding-top: 5px;
padding-bottom: 5px;
font-size: 1rem;
text-transform: capitalize;
font-weight: bold;
min-width: 200px;
width: 100%;
text-align: left;
border: none;
border-bottom: 2px solid #2b2a33;
}
button.active {
background-color: #373541;
border-bottom: 2px solid #ff8800;
}
button.add {
text-align: center;
}
}
.Tabbar {
display: flex;
flex-direction: row;
gap: 2px;
button {
padding-left: 5px;
padding-right: 5px;
padding-top: 5px;
padding-bottom: 5px;
font-size: 1rem;
text-transform: capitalize;
max-width: 200px;
text-align: left;
border-radius: 10px 10px 0 0;
border: none;
border-bottom: 2px solid #2b2a33;
}
button.active {
border-bottom: 2px solid #ff8800;
}
}
.Canvas {
width: 100%;
height: 100%;
canvas {
width: 100%;
height: 100%;
image-rendering: pixelated;
}
}
> div,
> main {
display: flex;
flex-direction: column;
}
main {
height: 100%;
width: 100%;
}
}