UNPKG

spicyhooks

Version:

A collection of spicy React hooks

1 lines 14.7 kB
{"version":3,"sources":["../src/hooks/in-view-element/in-view-element.ts","../src/hooks/screen-size/screen-size.ts","../src/hooks/counter/counter.ts","../src/hooks/previous/previous.ts","../src/hooks/toggle/toggle.ts","../src/hooks/debounce/debounce.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport type {\n InViewElement,\n InViewElementOptions,\n} from './_lib/in-view-element.types';\n\nconst INITIAL_VALUE: InViewElement = {\n element: null,\n id: '',\n};\n\n/**\n * Custom React hook that tracks which element is currently intersecting the viewport\n * using the IntersectionObserver API.\n *\n * It observes all elements in the document that possess a specific attribute (defaulting to 'id').\n * When an element enters the viewport according to the specified threshold, the hook updates\n * its state to reflect that element. Only the *first* element found to be intersecting\n * in any observation callback cycle is considered \"in view\".\n *\n * @param {InViewElementOptions} [config={}] - Optional configuration object.\n * @param {IntersectionObserverInit} [config.options={ threshold: 0.9 }] - Options to pass directly to the IntersectionObserver constructor (e.g., `threshold`, `root`, `rootMargin`). Defaults to detecting intersection when 90% of the element is visible.\n * @param {string} [config.baseOn='id'] - The HTML attribute name used to query for elements to observe. Defaults to 'id'. Any element with this attribute will be observed.\n *\n * @returns {InViewElement} An object containing the currently \"in view\" element and its ID.\n * - `element`: The DOM element currently intersecting the viewport based on the options, or `null` if none is actively intersecting or initially.\n * - `id`: The value of the `id` attribute of the intersecting element, or an empty string (`''`) if no element is intersecting or initially.\n *\n * @example\n *\n* const MyComponent = () => {\n* const inViewElement = useInViewElement();\n\n* return (\n* <section className='space-x-6'>\n* {navbarRoutes.map((route, i) => {\n* const elementId = route.href.replace('#', '');\n* const isInView = inViewElement.id === elementId;\n* return (\n* <Link\n* key={i}\n* href={route.href}\n* className={cn(\n* 'rounded-md px-2 py-1',\n* isInView && 'bg-dark',\n* 'hover:bg-dark/50'\n* )}\n* >\n* {route.title}\n* </Link>\n* );\n* })}\n* </section>\n* ); // This example shows how to highlight the active navigation link based on which section is in view\n* };\n *\n */\nfunction useInViewElement({\n options = { threshold: 0.9 },\n baseOn = 'id',\n}: InViewElementOptions = {}): InViewElement {\n const [inViewElement, setInViewElement] =\n useState<InViewElement>(INITIAL_VALUE);\n\n useEffect(() => {\n const observer = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n const targetElement = entry.target as HTMLElement;\n setInViewElement({\n element: targetElement,\n id: targetElement.id,\n });\n break; // Exit after the first intersecting entry -> just one element can be active at a time\n }\n }\n }, options);\n\n const elements = document.querySelectorAll<HTMLElement>(`[${baseOn}]`);\n\n if (elements.length > 0)\n elements.forEach((element) => observer.observe(element));\n\n return () => observer.disconnect();\n }, [options, baseOn]);\n\n return inViewElement;\n}\n\nexport { useInViewElement };\n","import { useEffect, useState } from 'react';\nimport type { ScreenSize } from './_lib/screen-size.types';\n\nconst INITIAL_VALUE: ScreenSize = {\n width: 0,\n height: 0,\n};\n\n/**\n * A custom React hook that tracks the current inner dimensions (width and height) of the browser window.\n *\n * It attaches a 'resize' event listener to the window and updates the returned dimensions\n * whenever the window size changes. The hook automatically handles setting the initial dimensions\n * on mount and cleaning up the event listener on unmount.\n *\n * @returns {ScreenSize} An object containing the current screen dimensions:\n * - `width` (number): The current inner width of the window in pixels (`window.innerWidth`).\n * - `height` (number): The current inner height of the window in pixels (`window.innerHeight`).\n *\n * @example\n * function ResponsiveLayout() {\n * const { width, height } = useScreenSize();\n *\n * const isSmallScreen = width < 600;\n *\n * return (\n * <div>\n * <p>Current window size: {width}px x {height}px</p>\n * <p>{isSmallScreen && \"Mobile\"}</p>\n * </div>\n * );\n * }\n */\nfunction useScreenSize(): ScreenSize {\n const [screenSize, setScreenSize] = useState<ScreenSize>(INITIAL_VALUE);\n\n useEffect(() => {\n function handleResize() {\n setScreenSize({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n }\n\n handleResize();\n\n window.addEventListener('resize', handleResize);\n\n return () => window.removeEventListener('resize', handleResize);\n }, []);\n\n return screenSize;\n}\n\nexport { useScreenSize };\n","import { useCallback, useState } from 'react';\n\nconst INITIAL_VALUE = 0;\n\n/**\n * A custom React hook for managing a counter state.\n *\n * Provides the current count and memoized functions to increment, decrement,\n * and reset the count to its initial value.\n *\n * @param {number} [initialValue=0] - The starting value for the counter. Defaults to 0 if not provided.\n * @returns {{\n * count: number;\n * increment: () => void;\n * decrement: () => void;\n * reset: () => void;\n * }} An object containing:\n * - `count`: The current state of the counter.\n * - `increment`: A memoized function that increases the count by 1.\n * - `decrement`: A memoized function that decreases the count by 1.\n * - `reset`: A memoized function that resets the count to the `initialValue`.\n *\n * @example\n * const { count, increment, decrement, reset } = useCounter();\n *\n * @example\n * const { count: score, increment: increaseScore, decrement: decreaseScore } = useCounter(100);\n *\n * @example\n * function MyCounterComponent() {\n * const { count, increment, decrement, reset } = useCounter(5);\n *\n * return (\n * <section>\n * <h2>Counter</h2>\n * <p>Current Count: {count}</p>\n * <button onClick={increment}>+</button>\n * <button onClick={decrement}>-</button>\n * <button onClick={reset}>Reset to 5</button>\n * </section>\n * );\n * }\n */\nfunction useCounter(initialValue: number = INITIAL_VALUE) {\n const [count, setCount] = useState(initialValue);\n\n const increment = useCallback(() => setCount((prev) => prev + 1), []);\n\n const decrement = useCallback(() => setCount((prev) => prev - 1), []);\n\n const reset = useCallback(() => setCount(initialValue), [initialValue]);\n\n return { count, increment, decrement, reset };\n}\n\nexport { useCounter };\n","import { useEffect, useRef } from 'react';\n\n/**\n * A custom React hook that returns the value of a variable from the previous render cycle.\n *\n * This hook is useful for comparing props or state between renders, for example,\n * to trigger effects only when a specific value has changed.\n *\n * It works by storing the current value in a ref after the component renders,\n * so on the next render, the ref still holds the value from the previous cycle.\n *\n * @template T The type of the value being tracked. Can be any type.\n *\n * @param {T} value The value whose previous instance you want to track.\n * This would typically be a prop or state variable.\n *\n * @returns {T | null} The value from the previous render.\n * Returns `null` on the initial render, as there's no \"previous\" value yet.\n *\n * @example\n * function Counter() {\n * const [count, setCount] = useState(0);\n *\n * const prevCount = usePrevious(count);\n *\n * return (\n * <section>\n * <button onClick={() => setCount((prev) => prev + 1)}>+</button>\n * <p>prev:{prevCount}</p>\n * <p>current: {count}</p>\n * </section>\n * );\n * }\n */\nfunction usePrevious<T>(value: T): T | null {\n const ref = useRef<T>(null);\n\n useEffect(() => {\n ref.current = value;\n }, [value]);\n\n return ref.current;\n}\n\nexport { usePrevious };\n","import { useCallback, useState } from 'react';\n\nconst INITIAL_VALUE = false;\n\n/**\n * A custom React hook for managing a boolean (on/off) toggle state.\n *\n * Provides the current state (`isOn`) and memoized functions to control the state:\n * `toggle` (flips the state), `setOn` (sets state to true), and `setOff` (sets state to false).\n * Useful for managing UI elements like checkboxes, modals, dropdowns, etc.\n *\n * @param {boolean} [initialValue=false] - The initial state of the toggle. Defaults to `false` (off).\n * @returns {{\n * isOn: boolean;\n * toggle: () => void;\n * setOn: () => void;\n * setOff: () => void;\n * }} An object containing:\n * - `isOn`: The current boolean state (`true` or `false`).\n * - `toggle`: A memoized function to invert the state (true -> false, false -> true).\n * - `setOn`: A memoized function to explicitly set the state to `true`.\n * - `setOff`: A memoized function to explicitly set the state to `false`.\n *\n * @example\n * const { isOn, toggle } = useToggle();\n * console.log(isOn); // false\n * toggle(); // Sets isOn to true\n *\n * @example\n * const { isOn: isVisible, toggle: toggleVisibility, setOn: show, setOff: hide } = useToggle(true);\n * console.log(isVisible); // true\n * hide(); // Sets isVisible to false\n * show(); // Sets isVisible to true\n *\n * @example\n * function ModalExample() {\n * const { isOn: isModalOpen, toggle: toggleModal, setOff: closeModal } = useToggle(false);\n *\n * return (\n * <section>\n * <button onClick={toggleModal}>Open Modal</button>\n *\n * {isModalOpen && (\n * <div className=\"modal-backdrop\">\n * <div className=\"modal-content\">\n * <p>This is the modal content!</p>\n * <button onClick={closeModal}>Close</button> {* Using setOff *}\n * </div>\n * </div>\n * )}\n * </section>\n * );\n * }\n */\nfunction useToggle(initialValue: boolean = INITIAL_VALUE) {\n const [isOn, setIsOn] = useState(initialValue);\n\n const toggle = useCallback(() => {\n setIsOn((prev) => !prev);\n }, []);\n\n const setOn = useCallback(() => {\n setIsOn(true);\n }, []);\n\n const setOff = useCallback(() => {\n setIsOn(false);\n }, []);\n\n return { isOn, toggle, setOn, setOff };\n}\n\nexport { useToggle };\n","import { useEffect, useState } from 'react';\n\n/**\n * A custom React hook that debounces a value.\n *\n * This hook delays updating the value until a specified time has elapsed since the last change.\n * Useful for delaying expensive operations like API calls based on rapidly changing input (e.g., search box).\n *\n * @template T The type of the value being debounced.\n * @param {T} value The value to debounce.\n * @param {number} delay The delay in milliseconds before the debounced value is updated.\n * @returns {T} The debounced value.\n *\n * @example\n * function CounterComponent() {\n * const [count, setCount] = useState(0);\n *\n * const debounceCount = useDebounce(count, 3000);\n *\n * return (\n * <section>\n * <button onClick={() => setCount((prev) => prev + 1)}>+</button>\n * <p>Instant Count: {count}</p>\n * <p>Debounced Count: {debounceCount}</p>\n * </section>\n * );\n * }\n */\nfunction useDebounce<T>(value: T, delay: number) {\n const [debounceValue, setDebounceValue] = useState(value);\n\n useEffect(() => {\n const handler = setTimeout(() => {\n setDebounceValue(value);\n }, delay);\n\n return () => clearTimeout(handler);\n }, [value, delay]);\n\n return debounceValue;\n}\n\nexport { useDebounce };\n"],"mappings":"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","names":["useEffect","useState","INITIAL_VALUE","useInViewElement","options","baseOn","inViewElement","setInViewElement","observer","entries","entry","targetElement","elements","element","useEffect","useState","INITIAL_VALUE","useScreenSize","screenSize","setScreenSize","handleResize","useCallback","useState","INITIAL_VALUE","useCounter","initialValue","count","setCount","increment","prev","decrement","reset","useEffect","useRef","usePrevious","value","ref","useCallback","useState","INITIAL_VALUE","useToggle","initialValue","isOn","setIsOn","toggle","prev","setOn","setOff","useEffect","useState","useDebounce","value","delay","debounceValue","setDebounceValue","handler"]}