toggle.ts

 1import merge from "ts-deepmerge"
 2
 3type ToggleState = "inactive" | "active"
 4
 5type Toggleable<T> = Record<ToggleState, T>
 6
 7const NO_INACTIVE_OR_BASE_ERROR =
 8    "A toggleable object must have an inactive state, or a base property."
 9const NO_ACTIVE_ERROR = "A toggleable object must have an active state."
10
11interface ToggleableProps<T> {
12    base?: T
13    state: Partial<Record<ToggleState, T>>
14}
15
16/**
17 * Helper function for creating Toggleable objects.
18 * @template T The type of the object being toggled.
19 * @param props Object containing the base (inactive) state and state modifications to create the active state.
20 * @returns A Toggleable object containing both the inactive and active states.
21 * @example
22 * ```
23 * toggleable({
24 *   base: { background: "#000000", text: "#CCCCCC" },
25 *   state: { active: { text: "#CCCCCC" } },
26 * })
27 * ```
28 */
29export function toggleable<T extends object>(
30    props: ToggleableProps<T>
31): Toggleable<T> {
32    const { base, state } = props
33
34    if (!base && !state.inactive) throw new Error(NO_INACTIVE_OR_BASE_ERROR)
35    if (!state.active) throw new Error(NO_ACTIVE_ERROR)
36
37    const inactiveState = base
38        ? ((state.inactive ? merge(base, state.inactive) : base) as T)
39        : (state.inactive as T)
40
41    const toggleObj: Toggleable<T> = {
42        inactive: inactiveState,
43        active: merge(base ?? {}, state.active) as T,
44    }
45
46    return toggleObj
47}