import {useBindable} from 'app/common/gutil'; import {BindableValue, Computed, dom, IDisposableOwner, Observable, UseCB} from 'grainjs'; /** * Version of makeTestId that can be appended conditionally. */ export function makeTestId(prefix: string) { return (id: BindableValue, obs?: BindableValue) => { return dom.cls(use => { if (obs !== undefined && !useBindable(use, obs)) { return ''; } return `${useBindable(use, prefix)}${useBindable(use, id)}`; }); }; } export function autoFocus() { return (el: HTMLElement) => void setTimeout(() => el.focus(), 10); } export function autoSelect() { return (el: HTMLElement) => void setTimeout(() => (el as any).select?.(), 10); } /** * Async computed version of Computed. */ export const AsyncComputed = { create(owner: IDisposableOwner, cb: (use: UseCB) => Promise): AsyncComputed { const backend: Observable = Observable.create(owner, undefined); const dirty = Observable.create(owner, true); const computed: Computed> = Computed.create(owner, cb as any); let ticket = 0; const listener = (prom: Promise): void => { dirty.set(true); const myTicket = ++ticket; prom.then(v => { if (ticket !== myTicket) { return; } if (backend.isDisposed()) { return; } dirty.set(false); backend.set(v); }).catch(reportError); }; owner?.autoDispose(computed.addListener(listener)); listener(computed.get()); return Object.assign(backend, { dirty }); } }; export interface AsyncComputed extends Observable { /** * Whether computed wasn't updated yet. */ dirty: Observable; } /** * Stops propagation of the event, and prevents default action. */ export function stopEvent(ev: Event) { ev.stopPropagation(); ev.preventDefault(); ev.stopImmediatePropagation(); }