feat: sectioned toolbar + custom component editor
- Redesigned toolbar with I/O, Gates, and Components sections - Component editor: sub-canvas mode to design reusable chips - Save/Cancel with main circuit state preservation - Components persist in localStorage - Custom components render as purple chips with dynamic I/O ports - Component evaluation simulates internal circuit as black box - Toolbar height increased to 56px for section labels - All height references updated consistently Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
59
js/events.js
59
js/events.js
@@ -1,13 +1,14 @@
|
||||
// Event handlers — mouse, keyboard, toolbar, waveform controls
|
||||
import { GATE_W, GATE_H } from './constants.js';
|
||||
import { GATE_W, GATE_H, COMP_W } from './constants.js';
|
||||
import { state } from './state.js';
|
||||
import { evaluateAll, findGateAt, findPortAt } from './gates.js';
|
||||
import { evaluateAll, findGateAt, findPortAt, getComponentWidth, getComponentHeight } from './gates.js';
|
||||
import { manualStep, clearWaveData } from './waveform.js';
|
||||
import { startSim, stopSim, adjustSpeed } from './simulation.js';
|
||||
import { resize, screenToWorld } from './renderer.js';
|
||||
import { puzzleMode, currentLevel, showLevelPanel } from './puzzleUI.js';
|
||||
import { getLevel } from './levels.js';
|
||||
import { saveState, loadState, exportAsFile, importFromFile } from './saveLoad.js';
|
||||
import { enterComponentEditor, exitComponentEditor, updateComponentButtons, setResizeCallback } from './components.js';
|
||||
|
||||
const PAN_SPEED = 40;
|
||||
|
||||
@@ -19,6 +20,9 @@ function updateWaveZoomLabel() {
|
||||
export function initEvents() {
|
||||
const canvas = document.getElementById('canvas');
|
||||
|
||||
// Set up resize callback for component editor
|
||||
setResizeCallback(resize);
|
||||
|
||||
// ==================== CANVAS MOUSE ====================
|
||||
canvas.addEventListener('mousemove', e => {
|
||||
state.mouseX = e.offsetX;
|
||||
@@ -62,13 +66,27 @@ export function initEvents() {
|
||||
|
||||
// Placing a new gate
|
||||
if (state.placingGate) {
|
||||
state.gates.push({
|
||||
let w = GATE_W, h = GATE_H;
|
||||
if (state.placingGate.startsWith('COMPONENT:')) {
|
||||
const componentId = state.placingGate.substring(10);
|
||||
const component = state.customComponents?.[componentId];
|
||||
if (component) {
|
||||
w = getComponentWidth({ component });
|
||||
h = getComponentHeight({ component });
|
||||
}
|
||||
}
|
||||
|
||||
const newGate = {
|
||||
id: state.nextId++,
|
||||
type: state.placingGate,
|
||||
x: world.x - GATE_W / 2,
|
||||
y: world.y - GATE_H / 2,
|
||||
x: world.x - w / 2,
|
||||
y: world.y - h / 2,
|
||||
value: 0
|
||||
});
|
||||
};
|
||||
if (state.placingGate.startsWith('COMPONENT:')) {
|
||||
newGate.component = state.customComponents[state.placingGate.substring(10)];
|
||||
}
|
||||
state.gates.push(newGate);
|
||||
evaluateAll();
|
||||
state.placingGate = null;
|
||||
return;
|
||||
@@ -311,4 +329,33 @@ export function initEvents() {
|
||||
}
|
||||
});
|
||||
document.addEventListener('mouseup', () => { state.resizingWave = false; });
|
||||
|
||||
// ==================== COMPONENT EDITOR ====================
|
||||
document.getElementById('create-component-btn').addEventListener('click', () => {
|
||||
enterComponentEditor();
|
||||
});
|
||||
|
||||
document.getElementById('component-editor-save').addEventListener('click', () => {
|
||||
const name = prompt('Component name:', 'MyComponent');
|
||||
if (name && name.trim()) {
|
||||
exitComponentEditor(name.trim(), true);
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('component-editor-cancel').addEventListener('click', () => {
|
||||
if (confirm('Discard component without saving?')) {
|
||||
exitComponentEditor('', false);
|
||||
}
|
||||
});
|
||||
|
||||
// Event delegation for saved component buttons
|
||||
document.addEventListener('click', e => {
|
||||
if (e.target.classList.contains('component-btn')) {
|
||||
const componentId = e.target.dataset.componentId;
|
||||
state.placingGate = `COMPONENT:${componentId}`;
|
||||
}
|
||||
});
|
||||
|
||||
// Update component buttons initially and when customComponents changes
|
||||
updateComponentButtons();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user