Remove comments that only restate the code, keeping the section banners and the notes that carry a reason, a measured fact or an external contract. ButtonOptions.width claimed to be a fixed width but only set minWidth, so the Graph and Texture pickers grew with the name they showed. It is now what it promises, with a separate minWidth for the buttons that have two forms. Narrow buttons shrink their padding, otherwise a 24px icon button had 8px left for its glyph and truncate dropped it entirely. __gm.labelsDropped() guards that class: the overflow guard only saw text that was too wide, never a label truncated to nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
46 lines
1.6 KiB
TypeScript
46 lines
1.6 KiB
TypeScript
import { type PropertyBag, type PropertyValue, setBagValue } from '../model/Properties';
|
|
import type { Project } from '../model/Project';
|
|
import type { Command } from './Command';
|
|
|
|
/**
|
|
* A missing key is `undefined` on both ends: as a `computeNext` result it removes the key (this is
|
|
* how clearing an override works), and as remembered value it makes undo remove it again.
|
|
*
|
|
* Subclasses resolve their target on every call rather than holding it, so an undo whose target is
|
|
* gone fails with a `CommandError` instead of writing into an orphaned object.
|
|
*/
|
|
export abstract class SetBagValueCommand<T> implements Command {
|
|
readonly label: string;
|
|
private previous: PropertyValue | undefined;
|
|
|
|
protected constructor(
|
|
protected readonly key: string,
|
|
label: string,
|
|
) {
|
|
this.label = label;
|
|
}
|
|
|
|
/** Throws a `CommandError` when the target does not exist. */
|
|
protected abstract resolveTarget(project: Project): T;
|
|
|
|
protected abstract bagOf(target: T): PropertyBag;
|
|
|
|
/**
|
|
* The value to write, `undefined` to remove the key. Called before anything is mutated, so this
|
|
* is where a subclass rejects an invalid edit.
|
|
*/
|
|
protected abstract computeNext(target: T, project: Project): PropertyValue | undefined;
|
|
|
|
execute(project: Project): void {
|
|
const target = this.resolveTarget(project);
|
|
const bag = this.bagOf(target);
|
|
const next = this.computeNext(target, project);
|
|
this.previous = bag[this.key];
|
|
setBagValue(bag, this.key, next);
|
|
}
|
|
|
|
undo(project: Project): void {
|
|
setBagValue(this.bagOf(this.resolveTarget(project)), this.key, this.previous);
|
|
}
|
|
}
|