Files
feuerwerk/src/editor/FormDialog.ts
T
2026-09-17 14:19:53 +02:00

63 lines
2.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Button, type ButtonOptions, HStack, Label, Popup, Spacer, Theme, type Widget } from '../ui';
/** Matches the inspector's label column. */
const LABEL_WIDTH = 76;
/**
* A small modal form – the texture's Slice dialog, the timeline's row and frames dialogs: a bold
* title, rows led by a label in the inspector's label column, Cancel and a confirming button last.
*/
export abstract class FormDialog extends Popup {
protected constructor(minWidth: number, title: string) {
super({ modal: true, padding: Theme.padding * 2, gap: Theme.gap });
this.minWidth = minWidth;
this.content.add(new Label(title, { style: 'bodyBold' }));
}
protected fieldLabel(text: string): Label {
const label = new Label(text, { style: 'muted' });
label.minWidth = LABEL_WIDTH;
return label;
}
/** A row of `widgets`, left-aligned at the height of an inspector row. */
protected formRow(...widgets: Widget[]): HStack {
const row = new HStack({ gap: Theme.gap, align: 'center' });
row.minHeight = Theme.rowHeight;
for (const widget of widgets) row.add(widget);
row.add(new Spacer(), 1);
return row;
}
/** Cancel and the confirming button, right-aligned; the dialog closes before `onConfirm` runs. */
protected addButtons(text: string, variant: ButtonOptions['variant'], onConfirm: () => void): Button {
const buttons = new HStack({ gap: Theme.gap, align: 'center' });
buttons.add(new Spacer(), 1);
const cancel = new Button('Cancel');
cancel.onClick = this.closeThen();
buttons.add(cancel);
const confirm = new Button(text, { variant });
confirm.onClick = this.closeThen(onConfirm);
buttons.add(confirm);
this.content.add(buttons);
return confirm;
}
}
/** One open dialog of a kind at a time: opening the next closes the last. `current` is for the check scripts. */
export function dialogSlot<T extends Popup>(): { current(): T | null; open(dialog: T): T } {
let current: T | null = null;
return {
current: () => current,
open(dialog) {
current?.close();
current = dialog;
dialog.onClose = () => {
if (current === dialog) current = null;
};
dialog.openCentered();
return dialog;
},
};
}