interactive.ts

 1import merge from "ts-deepmerge"
 2
 3type InteractiveState = "default" | "hovered" | "clicked" | "selected" | "disabled";
 4
 5type Interactive<T> = {
 6    default: T,
 7    hovered?: T,
 8    clicked?: T,
 9    selected?: T,
10    disabled?: T,
11};
12
13interface InteractiveProps<T> {
14    base?: T,
15    state: Partial<Record<InteractiveState, T>>
16}
17
18/**
19 * Helper function for creating Interactive<T> objects that works pretty much like Toggle<T>.
20 * It takes a object to be used as a value for `default` field and then fills out other fields
21 * with fields from either `base` or `modifications`.
22 * Notably, it does not touch `hover`, `clicked` and `disabled` if there are no modifications for it.
23 *
24 * @param defaultObj Object to be used as the value for `default` field.
25 * @param base Object containing base fields to be included in the resulting object.
26 * @param modifications Object containing modified fields to be included in the resulting object.
27 * @returns Interactive<T> object with fields from `base` and `modifications`.
28 */
29export function interactive<T extends Object>({ base, state }: InteractiveProps<T>): Interactive<T> {
30    if (!base && !state.default) throw new Error("An interactive object must have a default state, or a base property.");
31
32    let defaultState: T;
33
34    if (state.default && base) {
35        defaultState = merge(base, state.default) as T;
36    } else {
37        defaultState = base ? base : state.default as T;
38    }
39
40    let interactiveObj: Interactive<T> = {
41        default: defaultState,
42    };
43
44    let stateCount = 0;
45
46    if (state.hovered !== undefined) {
47        interactiveObj.hovered = merge(interactiveObj.default, state.hovered) as T;
48        stateCount++;
49    }
50
51    if (state.clicked !== undefined) {
52        interactiveObj.clicked = merge(interactiveObj.default, state.clicked) as T;
53        stateCount++;
54    }
55
56    if (state.selected !== undefined) {
57        interactiveObj.selected = merge(interactiveObj.default, state.selected) as T;
58        stateCount++;
59    }
60
61    if (state.disabled !== undefined) {
62        interactiveObj.disabled = merge(interactiveObj.default, state.disabled) as T;
63        stateCount++;
64    }
65
66    if (stateCount < 1) {
67        throw new Error("An interactive object must have a default and at least one other state.");
68    }
69
70    return interactiveObj;
71}