63 lines
2.2 KiB
TypeScript
63 lines
2.2 KiB
TypeScript
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;
|
||
},
|
||
};
|
||
}
|