- UpdateParamsCommand with execute/undo for color/speed/direction/length - Uses module-level currentShowId (not this._showId) matching existing pattern - BlockInspector renders compact strip with animation label, color swatch, speed input, direction select, length input, and remove button - Color change committed on 'change' event (not 'input') to avoid command flood - _onDelete callback wired to remove button for app.js to handle
119 lines
4.9 KiB
JavaScript
119 lines
4.9 KiB
JavaScript
// BlockInspector — compact horizontal strip for editing selected block params
|
|
// Appears between timeline canvas and transport bar when a block is selected.
|
|
// All parameter changes go through UpdateParamsCommand for full undo/redo support.
|
|
|
|
import { UpdateParamsCommand } from './commands.js';
|
|
|
|
export class BlockInspector {
|
|
constructor(containerEl, history) {
|
|
this.el = containerEl; // #block-inspector div
|
|
this.history = history; // CommandHistory reference
|
|
this.tracks = null; // set by app.js from timeline.tracks
|
|
this.currentBlock = null;
|
|
this._onDelete = null; // callback(block) — set by app.js
|
|
}
|
|
|
|
show(block) {
|
|
this.currentBlock = block;
|
|
if (!block) {
|
|
this.el.style.display = 'none';
|
|
return;
|
|
}
|
|
this.el.style.display = 'flex';
|
|
this._render(block);
|
|
}
|
|
|
|
hide() {
|
|
this.currentBlock = null;
|
|
this.el.style.display = 'none';
|
|
}
|
|
|
|
_render(block) {
|
|
const animName = (block.animation || 'BLOCK').toUpperCase().replace(/_/g, ' ');
|
|
const color = block.params?.color || '#00ffff';
|
|
const speed = block.params?.speed ?? 1.0;
|
|
const direction = block.params?.direction || 'forward';
|
|
const length = block.params?.length ?? 0.3;
|
|
|
|
this.el.innerHTML = `
|
|
<span class="inspector-label">${animName}</span>
|
|
<label class="inspector-field">
|
|
COLOR
|
|
<div class="inspector-color-swatch" id="color-swatch" style="background:${color};" title="Click to change color"></div>
|
|
<input type="color" id="inspector-color" value="${color}" style="display:none;">
|
|
</label>
|
|
<label class="inspector-field">
|
|
SPEED
|
|
<input type="number" id="inspector-speed" step="0.1" min="0.1" max="10" value="${speed}">
|
|
</label>
|
|
<label class="inspector-field">
|
|
DIR
|
|
<select id="inspector-direction">
|
|
<option value="forward" ${direction === 'forward' ? 'selected' : ''}>FWD</option>
|
|
<option value="reverse" ${direction === 'reverse' ? 'selected' : ''}>REV</option>
|
|
</select>
|
|
</label>
|
|
<label class="inspector-field">
|
|
LEN
|
|
<input type="number" id="inspector-length" step="0.05" min="0.05" max="1" value="${length}">
|
|
</label>
|
|
<div style="flex:1"></div>
|
|
<button class="inspector-remove" title="Remove block">✕</button>
|
|
`;
|
|
|
|
this._wireEvents(block);
|
|
}
|
|
|
|
_wireEvents(block) {
|
|
// Color swatch → open hidden color input
|
|
const swatch = this.el.querySelector('#color-swatch');
|
|
const colorInput = this.el.querySelector('#inspector-color');
|
|
swatch?.addEventListener('click', () => colorInput?.click());
|
|
|
|
// Live preview: update swatch as color drags
|
|
colorInput?.addEventListener('input', (e) => {
|
|
swatch.style.background = e.target.value;
|
|
});
|
|
// Commit on change (not during drag — avoids command flood)
|
|
colorInput?.addEventListener('change', (e) => {
|
|
const oldParams = { ...block.params };
|
|
const newParams = { ...block.params, color: e.target.value };
|
|
const cmd = new UpdateParamsCommand(this.tracks, block, oldParams, newParams);
|
|
this.history.execute(cmd);
|
|
});
|
|
|
|
// Speed
|
|
this.el.querySelector('#inspector-speed')?.addEventListener('change', (e) => {
|
|
const val = parseFloat(e.target.value);
|
|
if (isNaN(val) || val < 0.1 || val > 10) return;
|
|
const oldParams = { ...block.params };
|
|
const newParams = { ...block.params, speed: val };
|
|
const cmd = new UpdateParamsCommand(this.tracks, block, oldParams, newParams);
|
|
this.history.execute(cmd);
|
|
});
|
|
|
|
// Direction
|
|
this.el.querySelector('#inspector-direction')?.addEventListener('change', (e) => {
|
|
const oldParams = { ...block.params };
|
|
const newParams = { ...block.params, direction: e.target.value };
|
|
const cmd = new UpdateParamsCommand(this.tracks, block, oldParams, newParams);
|
|
this.history.execute(cmd);
|
|
});
|
|
|
|
// Length
|
|
this.el.querySelector('#inspector-length')?.addEventListener('change', (e) => {
|
|
const val = parseFloat(e.target.value);
|
|
if (isNaN(val) || val < 0.05 || val > 1) return;
|
|
const oldParams = { ...block.params };
|
|
const newParams = { ...block.params, length: val };
|
|
const cmd = new UpdateParamsCommand(this.tracks, block, oldParams, newParams);
|
|
this.history.execute(cmd);
|
|
});
|
|
|
|
// Remove button
|
|
this.el.querySelector('.inspector-remove')?.addEventListener('click', () => {
|
|
if (this._onDelete) this._onDelete(block);
|
|
});
|
|
}
|
|
}
|