UNPKG

@difizen/mana-core

Version:

147 lines (138 loc) 4.05 kB
import type { Event, MaybePromise } from '@difizen/mana-common'; import type { View } from '..'; export interface Saveable { readonly dirty: boolean; readonly onDirtyChanged: Event<void>; readonly autoSave: 'on' | 'off'; /** * Saves dirty changes. */ save: () => MaybePromise<void>; /** * Reverts dirty changes. */ restore?: () => MaybePromise<void>; } export interface SaveableSource { readonly saveable: Saveable; } export namespace Saveable { export function isSource(arg: any): arg is SaveableSource { return !!arg && 'saveable' in arg && is(arg.saveable); } export function is(arg: any): arg is Saveable { return !!arg && 'dirty' in arg && 'onDirtyChanged' in arg; } export function get(arg: any): Saveable | undefined { if (is(arg)) { return arg; } if (isSource(arg)) { return arg.saveable; } return undefined; } export function getDirty(arg: any): Saveable | undefined { const saveable = get(arg); if (saveable && saveable.dirty) { return saveable; } return undefined; } export function isDirty(arg: any): boolean { return !!getDirty(arg); } export async function save(arg: any): Promise<void> { const saveable = get(arg); if (saveable) { await saveable.save(); } } // export function apply(widget: View): SaveableView | undefined { // // eslint-disable-next-line @typescript-eslint/no-use-before-define // if (SaveableView.is(widget)) { // return widget; // } // const saveable = Saveable.get(widget); // if (!saveable) { // return undefined; // } // setDirty(widget, saveable.dirty); // saveable.onDirtyChanged(() => setDirty(widget, saveable.dirty)); // const closeWidget = widget.close.bind(widget); // const closeWithoutSaving: SaveableWidget['closeWithoutSaving'] = async () => { // if (saveable.dirty && saveable.revert) { // await saveable.revert(); // } // closeWidget(); // return waitForClosed(widget); // }; // let closing = false; // const closeWithSaving: SaveableWidget['closeWithSaving'] = async options => { // if (closing) { // return; // } // closing = true; // try { // const result = await shouldSave(saveable, () => { // if (options && options.shouldSave) { // return options.shouldSave(); // } // return new ShouldSaveDialog(widget).open(); // }); // if (typeof result === 'boolean') { // if (result) { // await Saveable.save(widget); // } // await closeWithoutSaving(); // } // } finally { // closing = false; // } // }; // return Object.assign(widget, { // closeWithoutSaving, // closeWithSaving, // close: () => closeWithSaving(), // }); // } export async function shouldSave( saveable: Saveable, cb: () => MaybePromise<boolean | undefined>, ): Promise<boolean | undefined> { if (!saveable.dirty) { return false; } if (saveable.autoSave === 'on') { return true; } return cb(); } } export interface SaveableView extends View { closeWithoutSaving: () => Promise<void>; closeWithSaving: (options?: SaveableView.CloseOptions) => Promise<void>; } export namespace SaveableView { export function is(view: View | undefined): view is SaveableView { return !!view && 'closeWithoutSaving' in view; } export function getDirty<T extends View>( views: Iterable<T>, ): IterableIterator<SaveableView & T> { return get(views, Saveable.isDirty); } export function* get<T extends View>( views: Iterable<T>, filter: (view: T) => boolean = () => true, ): IterableIterator<SaveableView & T> { for (const view of views) { if (SaveableView.is(view) && filter(view)) { yield view; } } } export interface CloseOptions { shouldSave?: () => MaybePromise<boolean | undefined>; } }