Skip to content

Cancelable text input

Karsten Schmidt edited this page Jul 3, 2018 · 3 revisions

(See general info in cookbook intro)

This higher-order textfield component sources its value from a preconfigured app state view and triggers an event to store value changes. Each time the input element is being focused we record the current value, which is then being restored if the user types Esc and thereby cancels any edits made. Pressing Enter triggers element.blur().

export function cancelableInput(ctx: AppContext, view: IView<string | number>, eventID: string) {
    let prev: string;
    const attribs = {
        ...ctx.ui.input,
        type: "text",
        onfocus: (e) => (prev = view.deref()),
        oninput: (e) => ctx.bus.dispatch([eventID, e.target.value]),
        onkeydown: (e: KeyboardEvent) => {
            switch (e.key) {
                case "Escape":
                    ctx.bus.dispatch([eventID, prev]);
                    (<HTMLElement>e.target).blur();
                    break;
                case "Enter":
                    ctx.bus.dispatch([eventID, (<HTMLInputElement>e.target).value]);
                    (<HTMLInputElement>e.target).blur();
                    break;
                default:
            }
        }
    };
    return () => ["input", {...attribs, value: view.deref()}];
}

Usage example

function labeledInput(ctx: AppContext, view: IView<string | number>, label: string) {
    const name = cancelableInput(ctx, ctx.views.name, "set-name");
    return () => ["div", ["label", ctx.ui.label, label], name];
}

function demoForm(ctx: AppContext) {
    const firstName = labeledInput(ctx, ctx.views.firstName, "First name");
    const surname = labeledInput(ctx, ctx.views.surname, "Surname");
    return () => ["div", firstName, surname];
}