{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/useHistoryState/index.ts"],"sourcesContent":["import { isFunction, at } from '@modern-kit/utils';\nimport { useCallback, useMemo, useState } from 'react';\n\ninterface UseHistoryStateReturn<T> {\n  state: T;\n  canForward: boolean;\n  canBack: boolean;\n  setState: (newState: T | ((prevState: T) => T)) => void;\n  replaceState: (newState: T | ((prevState: T) => T)) => void;\n  back: () => void;\n  forward: () => void;\n  go: (index: number) => void;\n}\n\ninterface HistoryState<T> {\n  history: T[];\n  current: T;\n  pointer: number;\n}\n\n/**\n * @description 상태와 상태 변경 이력을 관리하는 훅입니다.\n *\n * 상태 변경 시마다 이력이 저장되며, 상태 히스토리를 이전(`back`)과 다음(`forward`)으로 이동할 수 있습니다. 또한 특정 시점의 상태로 이동(`go`)할 수 있으며, 현재 상태를 대체(`replaceState`)할 수 있습니다.\n *\n * 저장되는 이력의 최대 개수(`capacity`)를 제한할 수 있습니다.\n *\n * @template T - 상태의 타입\n * @param {T | (() => T)} initialValue - 초기 상태 값 또는 초기 상태를 반환하는 함수\n * @param {number} [capacity=10] - 상태 변경 이력의 최대 저장 개수. 기본값은 10\n * @returns {UseHistoryStateReturn<T>} 상태 관리를 위한 객체\n * - `state`: 현재 상태 값\n * - `canForward`: 다음 상태로 이동할 수 있는지 여부\n * - `canBack`: 이전 상태로 이동할 수 있는지 여부\n * - `setState`: 상태 히스토리에 상태를 추가하고 새로운 상태로 업데이트합니다.\n * - `replaceState`: 상태 히스토리의 현재 상태를 대체합니다.\n * - `back`: 상태 히스토리에서 이전 상태로 되돌리는 함수\n * - `forward`: 상태 히스토리에서 다음 상태로 이동하는 함수\n * - `go`: 상태 히스토리에서 특정 인덱스의 상태로 이동하는 함수\n *\n * @example\n * const { state, setState, replaceState, back, forward, go } = useHistoryState(0, 5);\n *\n * setState(1);  // history: [0, 1], state: 1\n * setState(2);  // history: [0, 1, 2], state: 2\n * replaceState(3);  // history: [0, 1, 3], state: 3\n * back();       // history: [0, 1, 3], state: 1\n * forward();    // history: [0, 1, 3], state: 3\n * go(0); // history: [0, 1, 2], state: 0\n * setState(4);  // history: [0, 1, 3, 4], state: 4\n */\nexport function useHistoryState<T>(\n  initialValue: T | (() => T),\n  capacity: number = 10\n): UseHistoryStateReturn<T> {\n  const initialValueToUse = isFunction(initialValue)\n    ? initialValue()\n    : initialValue;\n\n  const [state, innerSetState] = useState<HistoryState<T>>({\n    history: [initialValueToUse],\n    current: initialValueToUse,\n    pointer: 0,\n  });\n\n  const canForward = useMemo(\n    () => state.pointer < state.history.length - 1,\n    [state.pointer, state.history.length]\n  );\n  const canBack = useMemo(() => state.pointer > 0, [state.pointer]);\n\n  const setState = useCallback(\n    (newState: T | ((prevState: T) => T)) => {\n      innerSetState((prev) => {\n        const history = [...prev.history];\n        const newStateToUse = isFunction(newState)\n          ? newState(history[prev.pointer])\n          : newState;\n\n        if (history.length === capacity) {\n          history.shift();\n        }\n\n        history.push(newStateToUse);\n        const pointer = history.length - 1;\n\n        return { history, current: newStateToUse, pointer };\n      });\n    },\n    [capacity]\n  );\n\n  const replaceState = useCallback((newState: T | ((prevState: T) => T)) => {\n    innerSetState((prev) => {\n      const history = [...prev.history];\n      const newStateToUse = isFunction(newState)\n        ? newState(history[prev.pointer])\n        : newState;\n\n      history[prev.pointer] = newStateToUse;\n      return { ...prev, history, current: newStateToUse };\n    });\n  }, []);\n\n  const back = useCallback(() => {\n    innerSetState((prev) => {\n      if (prev.pointer < 1) {\n        return prev;\n      }\n\n      const pointer = prev.pointer - 1;\n\n      return {\n        ...prev,\n        current: prev.history[pointer],\n        pointer,\n      };\n    });\n  }, []);\n\n  const forward = useCallback(() => {\n    innerSetState((prev) => {\n      if (prev.pointer >= prev.history.length - 1) {\n        return prev;\n      }\n\n      const pointer = prev.pointer + 1;\n\n      return {\n        ...prev,\n        current: prev.history[pointer],\n        pointer,\n      };\n    });\n  }, []);\n\n  const go = useCallback((index: number) => {\n    innerSetState((prev) => {\n      const element = at(prev.history, index);\n\n      if (element == null) {\n        return prev;\n      }\n\n      const pointer = index < 0 ? prev.history.length + index : index;\n\n      return { ...prev, current: element, pointer };\n    });\n  }, []);\n\n  return {\n    state: state.current,\n    canForward,\n    canBack,\n    setState,\n    replaceState,\n    forward,\n    back,\n    go,\n  };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,SAAgB,gBACd,cACA,WAAmB,IACO;CAC1B,MAAM,oBAAoB,WAAW,aAAa,GAC9C,cAAc,GACd;CAEJ,MAAM,CAAC,OAAO,iBAAiB,SAA0B;EACvD,SAAS,CAAC,kBAAkB;EAC5B,SAAS;EACT,SAAS;EACV,CAAC;CAEF,MAAM,aAAa,cACX,MAAM,UAAU,MAAM,QAAQ,SAAS,GAC7C,CAAC,MAAM,SAAS,MAAM,QAAQ,OAAO,CACtC;CACD,MAAM,UAAU,cAAc,MAAM,UAAU,GAAG,CAAC,MAAM,QAAQ,CAAC;CAEjE,MAAM,WAAW,aACd,aAAwC;EACvC,eAAe,SAAS;GACtB,MAAM,UAAU,CAAC,GAAG,KAAK,QAAQ;GACjC,MAAM,gBAAgB,WAAW,SAAS,GACtC,SAAS,QAAQ,KAAK,SAAS,GAC/B;GAEJ,IAAI,QAAQ,WAAW,UACrB,QAAQ,OAAO;GAGjB,QAAQ,KAAK,cAAc;GAG3B,OAAO;IAAE;IAAS,SAAS;IAAe,SAF1B,QAAQ,SAAS;IAEkB;IACnD;IAEJ,CAAC,SAAS,CACX;CAED,MAAM,eAAe,aAAa,aAAwC;EACxE,eAAe,SAAS;GACtB,MAAM,UAAU,CAAC,GAAG,KAAK,QAAQ;GACjC,MAAM,gBAAgB,WAAW,SAAS,GACtC,SAAS,QAAQ,KAAK,SAAS,GAC/B;GAEJ,QAAQ,KAAK,WAAW;GACxB,OAAO;IAAE,GAAG;IAAM;IAAS,SAAS;IAAe;IACnD;IACD,EAAE,CAAC;CAEN,MAAM,OAAO,kBAAkB;EAC7B,eAAe,SAAS;GACtB,IAAI,KAAK,UAAU,GACjB,OAAO;GAGT,MAAM,UAAU,KAAK,UAAU;GAE/B,OAAO;IACL,GAAG;IACH,SAAS,KAAK,QAAQ;IACtB;IACD;IACD;IACD,EAAE,CAAC;CAEN,MAAM,UAAU,kBAAkB;EAChC,eAAe,SAAS;GACtB,IAAI,KAAK,WAAW,KAAK,QAAQ,SAAS,GACxC,OAAO;GAGT,MAAM,UAAU,KAAK,UAAU;GAE/B,OAAO;IACL,GAAG;IACH,SAAS,KAAK,QAAQ;IACtB;IACD;IACD;IACD,EAAE,CAAC;CAEN,MAAM,KAAK,aAAa,UAAkB;EACxC,eAAe,SAAS;GACtB,MAAM,UAAU,GAAG,KAAK,SAAS,MAAM;GAEvC,IAAI,WAAW,MACb,OAAO;GAGT,MAAM,UAAU,QAAQ,IAAI,KAAK,QAAQ,SAAS,QAAQ;GAE1D,OAAO;IAAE,GAAG;IAAM,SAAS;IAAS;IAAS;IAC7C;IACD,EAAE,CAAC;CAEN,OAAO;EACL,OAAO,MAAM;EACb;EACA;EACA;EACA;EACA;EACA;EACA;EACD"}