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