UNPKG

codedsaif-react-hooks

Version:

To make it easy for you to get started with GitLab, here's a list of recommended next steps.

1,162 lines (1,141 loc) โ€ข 41.2 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var react = require('react'); /** * Custom hook that triggers a callback when a user clicks outside the referenced element. * @param {Function} callback - The function to call when clicking outside the element. * @returns {React.RefObject} - A ref that should be attached to the element you want to monitor. * @example * function Dropdown() { * const [isOpen, setIsOpen] = useState(false); * const dropdownRef = useClickOutside(() => setIsOpen(false)); * return ( * <div> * <button onClick={() => setIsOpen(!isOpen)}>Toggle Menu</button> * {isOpen && ( * <ul ref={dropdownRef}> * <li>Option 1</li> * <li>Option 2</li> * <li>Option 3</li> * </ul> * )} * </div> * ); */ function useClickOutside(callback) { var ref = react.useRef(); react.useEffect(function () { function handleClickOutside(event) { if (ref.current && !ref.current.contains(event.target)) { callback === null || callback === void 0 || callback(); } } document.addEventListener("mousedown", handleClickOutside); return function () { return document.removeEventListener("mousedown", handleClickOutside); }; }, [callback]); return ref; } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); } function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); } function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = true, o = false; try { if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = true, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _regeneratorRuntime() { _regeneratorRuntime = function () { return r; }; var t, r = {}, e = Object.prototype, n = e.hasOwnProperty, o = "function" == typeof Symbol ? Symbol : {}, i = o.iterator || "@@iterator", a = o.asyncIterator || "@@asyncIterator", u = o.toStringTag || "@@toStringTag"; function c(t, r, e, n) { Object.defineProperty(t, r, { value: e, enumerable: !n, configurable: !n, writable: !n }); } try { c({}, ""); } catch (t) { c = function (t, r, e) { return t[r] = e; }; } function h(r, e, n, o) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype); return c(a, "_invoke", function (r, e, n) { var o = 1; return function (i, a) { if (3 === o) throw Error("Generator is already running"); if (4 === o) { if ("throw" === i) throw a; return { value: t, done: true }; } for (n.method = i, n.arg = a;;) { var u = n.delegate; if (u) { var c = d(u, n); if (c) { if (c === f) continue; return c; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (1 === o) throw o = 4, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = 3; var h = s(r, e, n); if ("normal" === h.type) { if (o = n.done ? 4 : 2, h.arg === f) continue; return { value: h.arg, done: n.done }; } "throw" === h.type && (o = 4, n.method = "throw", n.arg = h.arg); } }; }(r, n, new Context(o || [])), true), a; } function s(t, r, e) { try { return { type: "normal", arg: t.call(r, e) }; } catch (t) { return { type: "throw", arg: t }; } } r.wrap = h; var f = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var l = {}; c(l, i, function () { return this; }); var p = Object.getPrototypeOf, y = p && p(p(x([]))); y && y !== e && n.call(y, i) && (l = y); var v = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(l); function g(t) { ["next", "throw", "return"].forEach(function (r) { c(t, r, function (t) { return this._invoke(r, t); }); }); } function AsyncIterator(t, r) { function e(o, i, a, u) { var c = s(t[o], t, i); if ("throw" !== c.type) { var h = c.arg, f = h.value; return f && "object" == typeof f && n.call(f, "__await") ? r.resolve(f.__await).then(function (t) { e("next", t, a, u); }, function (t) { e("throw", t, a, u); }) : r.resolve(f).then(function (t) { h.value = t, a(h); }, function (t) { return e("throw", t, a, u); }); } u(c.arg); } var o; c(this, "_invoke", function (t, n) { function i() { return new r(function (r, o) { e(t, n, r, o); }); } return o = o ? o.then(i, i) : i(); }, true); } function d(r, e) { var n = e.method, o = r.i[n]; if (o === t) return e.delegate = null, "throw" === n && r.i.return && (e.method = "return", e.arg = t, d(r, e), "throw" === e.method) || "return" !== n && (e.method = "throw", e.arg = new TypeError("The iterator does not provide a '" + n + "' method")), f; var i = s(o, r.i, e.arg); if ("throw" === i.type) return e.method = "throw", e.arg = i.arg, e.delegate = null, f; var a = i.arg; return a ? a.done ? (e[r.r] = a.value, e.next = r.n, "return" !== e.method && (e.method = "next", e.arg = t), e.delegate = null, f) : a : (e.method = "throw", e.arg = new TypeError("iterator result is not an object"), e.delegate = null, f); } function w(t) { this.tryEntries.push(t); } function m(r) { var e = r[4] || {}; e.type = "normal", e.arg = t, r[4] = e; } function Context(t) { this.tryEntries = [[-1]], t.forEach(w, this), this.reset(true); } function x(r) { if (null != r) { var e = r[i]; if (e) return e.call(r); if ("function" == typeof r.next) return r; if (!isNaN(r.length)) { var o = -1, a = function e() { for (; ++o < r.length;) if (n.call(r, o)) return e.value = r[o], e.done = false, e; return e.value = t, e.done = true, e; }; return a.next = a; } } throw new TypeError(typeof r + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, c(v, "constructor", GeneratorFunctionPrototype), c(GeneratorFunctionPrototype, "constructor", GeneratorFunction), c(GeneratorFunctionPrototype, u, GeneratorFunction.displayName = "GeneratorFunction"), r.isGeneratorFunction = function (t) { var r = "function" == typeof t && t.constructor; return !!r && (r === GeneratorFunction || "GeneratorFunction" === (r.displayName || r.name)); }, r.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, c(t, u, "GeneratorFunction")), t.prototype = Object.create(v), t; }, r.awrap = function (t) { return { __await: t }; }, g(AsyncIterator.prototype), c(AsyncIterator.prototype, a, function () { return this; }), r.AsyncIterator = AsyncIterator, r.async = function (t, e, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(h(t, e, n, o), i); return r.isGeneratorFunction(e) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, g(v), c(v, u, "Generator"), c(v, i, function () { return this; }), c(v, "toString", function () { return "[object Generator]"; }), r.keys = function (t) { var r = Object(t), e = []; for (var n in r) e.unshift(n); return function t() { for (; e.length;) if ((n = e.pop()) in r) return t.value = n, t.done = false, t; return t.done = true, t; }; }, r.values = x, Context.prototype = { constructor: Context, reset: function (r) { if (this.prev = this.next = 0, this.sent = this._sent = t, this.done = false, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(m), !r) for (var e in this) "t" === e.charAt(0) && n.call(this, e) && !isNaN(+e.slice(1)) && (this[e] = t); }, stop: function () { this.done = true; var t = this.tryEntries[0][4]; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function (r) { if (this.done) throw r; var e = this; function n(t) { a.type = "throw", a.arg = r, e.next = t; } for (var o = e.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i[4], u = this.prev, c = i[1], h = i[2]; if (-1 === i[0]) return n("end"), false; if (!c && !h) throw Error("try statement without catch or finally"); if (null != i[0] && i[0] <= u) { if (u < c) return this.method = "next", this.arg = t, n(c), true; if (u < h) return n(h), false; } } }, abrupt: function (t, r) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var n = this.tryEntries[e]; if (n[0] > -1 && n[0] <= this.prev && this.prev < n[2]) { var o = n; break; } } o && ("break" === t || "continue" === t) && o[0] <= r && r <= o[2] && (o = null); var i = o ? o[4] : {}; return i.type = t, i.arg = r, o ? (this.method = "next", this.next = o[2], f) : this.complete(i); }, complete: function (t, r) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && r && (this.next = r), f; }, finish: function (t) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var e = this.tryEntries[r]; if (e[2] === t) return this.complete(e[4], e[3]), m(e), f; } }, catch: function (t) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var e = this.tryEntries[r]; if (e[0] === t) { var n = e[4]; if ("throw" === n.type) { var o = n.arg; m(e); } return o; } } throw Error("illegal catch attempt"); }, delegateYield: function (r, e, n) { return this.delegate = { i: x(r), r: e, n: n }, "next" === this.method && (this.arg = t), f; } }, r; } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } /** * Custom hook to debounce a value after a specified delay. * @param {*} value - The value to debounce. * @param {number} [delay=500] - Delay in milliseconds before updating the debounced value. * @returns {*} - The debounced value. * @example * const [searchTerm, setSearchTerm] = useState(""); * const debouncedSearch = useDebounce(searchTerm, 300); * useEffect(() => { * if (debouncedSearch) { * fetch(`https://api.example.com/search?q=${debouncedSearch}`) * .then((res) => res.json()) * .then((data) => console.log(data)); * } * }, [debouncedSearch]); * return ( * <input * type="text" * placeholder="Search..." * onChange={(e) => setSearchTerm(e.target.value)} * /> * ); */ function useDebounce(value) { var delay = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 500; var _useState = react.useState(value), _useState2 = _slicedToArray(_useState, 2), debouncedValue = _useState2[0], setDebouncedValue = _useState2[1]; react.useEffect(function () { var handler = setTimeout(function () { return setDebouncedValue(value); }, delay); return function () { return clearTimeout(handler); }; }, [value, delay]); return debouncedValue; } /** * Custom hook to fetch data from an API. * @param {string} url - The URL to fetch data from. * @param {object} options - Optional fetch options (headers, method, etc.). * @returns {{ data: any, loading: boolean, error: string|null }} - The fetched data, loading state, and error message. * @example * const { data, loading, error } = useFetch("https://api.example.com/users"); * if (loading) return <p>Loading...</p>; * if (error) return <p>Error: {error}</p>; * return ( * <ul> * {data.map(user => ( * <li key={user.id}>{user.name}</li> * ))} * </ul> * ); */ function useFetch(url) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var _useState = react.useState(null), _useState2 = _slicedToArray(_useState, 2), data = _useState2[0], setData = _useState2[1]; var _useState3 = react.useState(true), _useState4 = _slicedToArray(_useState3, 2), loading = _useState4[0], setLoading = _useState4[1]; var _useState5 = react.useState(null), _useState6 = _slicedToArray(_useState5, 2), error = _useState6[0], setError = _useState6[1]; react.useEffect(function () { var fetchData = /*#__PURE__*/function () { var _ref = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee() { var response, result; return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: _context.prev = 0; _context.next = 3; return fetch(url, options); case 3: response = _context.sent; if (response.ok) { _context.next = 6; break; } throw new Error("Network response was not ok"); case 6: _context.next = 8; return response.json(); case 8: result = _context.sent; setData(result); _context.next = 15; break; case 12: _context.prev = 12; _context.t0 = _context["catch"](0); setError(_context.t0.message); case 15: _context.prev = 15; setLoading(false); return _context.finish(15); case 18: case "end": return _context.stop(); } }, _callee, null, [[0, 12, 15, 18]]); })); return function fetchData() { return _ref.apply(this, arguments); }; }(); fetchData(); }, [url]); return { data: data, loading: loading, error: error }; } /** * Custom hook to persist state in localStorage. * @param {string} key - The localStorage key to store the value under. * @param {*} initialValue - The initial value to use if none is found in localStorage. * @returns {[any, function]} - Returns the stored value and a setter function. * @example * function Dropdown() { * const [theme, setTheme] = useLocalStorage("theme", "light"); * useEffect(() => { * document.body.className = theme; * }, [theme]); * return ( * <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}> * Toggle Theme * </button> * ); * } */ function useLocalStorage(key, initialValue) { var _useState = react.useState(function () { try { var item = window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch (error) { console.error("Error reading localStorage", error); return initialValue; } }), _useState2 = _slicedToArray(_useState, 2), storedValue = _useState2[0], setStoredValue = _useState2[1]; var setValue = function setValue(value) { try { setStoredValue(value); window.localStorage.setItem(key, JSON.stringify(value)); } catch (error) { console.error("Error setting localStorage", error); } }; return [storedValue, setValue]; } /** * Custom hook to get the previous value of a prop or state. * @param {*} value - The current value to track. * @returns {*} - The previous value before the current render. * @example * const [count, setCount] = useState(0); * const prevCount = usePrevious(count); * return ( * <> * <p>Current: {count}, Previous: {prevCount}</p> * <button onClick={() => setCount(count + 1)}>Increase</button> * </> * ); */ function usePrevious(value) { var ref = react.useRef(); react.useEffect(function () { ref.current = value; }, [value]); return ref.current; } /** * Custom hook to manage dark and light theme modes, * persisting the theme preference in localStorage. * @returns {[string, function]} - Current theme and a toggle function. * @example * const [theme, toggleTheme] = useDarkMode(); * return ( * <button onClick={toggleTheme}> * Switch to {theme === "light" ? "Dark" : "Light"} Mode * </button> * ); */ function useDarkMode() { var _useLocalStorage = useLocalStorage("theme", "light"), _useLocalStorage2 = _slicedToArray(_useLocalStorage, 2), theme = _useLocalStorage2[0], setTheme = _useLocalStorage2[1]; useEffect(function () { document.body.className = theme; }, [theme]); return [theme, function () { return setTheme(theme === "light" ? "dark" : "light"); }]; } /** * Custom hook to get the current window dimensions. * @returns {{ width: number, height: number }} - The current width and height of the window. * @example * function ResponsiveComponent() { * const { width } = useWindowSize(); * return ( * <div> * {width > 1024 ? ( * <h1>Desktop View ๐Ÿ–ฅ๏ธ</h1> * ) : width > 768 ? ( * <h1>Tablet View ๐Ÿ“ฑ</h1> * ) : ( * <h1>Mobile View ๐Ÿ“ฒ</h1> * )} * </div> * ); */ function useWindowSize() { var _useState = react.useState({ width: window.innerWidth, height: window.innerHeight }), _useState2 = _slicedToArray(_useState, 2), size = _useState2[0], setSize = _useState2[1]; react.useEffect(function () { var handleResize = function handleResize() { return setSize({ width: window.innerWidth, height: window.innerHeight }); }; window.addEventListener("resize", handleResize); return function () { return window.removeEventListener("resize", handleResize); }; }, []); return size; } /** * useInfiniteScroll * Supports both automatic callback fetching and manual slicing with scroll. * @param {Function|null} callback - Called when scroll reaches bottom (set null for manual mode) * @param {Object} options - Configuration options * @param {React.RefObject} [options.containerRef=null] - Scrollable container ref * @param {boolean} [options.manual=false] - If true, enables manual mode and returns 'limit' * @param {number} [options.start=10] - Initial limit in manual mode * @param {number} [options.pace=10] - Increment step for limit * @param {number} [options.offset=100] - Distance from bottom to trigger scroll (in px) * @param {boolean} [options.hasMore=true] - Set false to stop infinite loading when no more data * @param {boolean} [options.debug=false] - Enable funny console logs * * @returns {[boolean, Function]|number} * - If manual=false: returns [isFetching, setIsFetching] * - If manual=true: returns limit (number) * * Example: Auto-fetch Mode (Default) * ------------------------------------- * const containerRef = useRef(null); * const [data, setData] = useState([]); * const [page, setPage] = useState(1); * const [hasMore, setHasMore] = useState(true); * const fetchMore = async () => { * const res = await fetch(`https://medrum.herokuapp.com/feeds/?source=5718e53e7a84fb1901e05971&page=${page}&sort=latest`); * const json = await res.json(); * if (json.length === 0) setHasMore(false); * // OR if(json.count === data.length) setHasMore(false); * else { * setData(prev => [...prev, ...json]); * setPage(prev => prev + 1); * } * }; * const [isFetching, setIsFetching] = useInfiniteScroll(fetchMore, { containerRef, hasMore }); * * Example: Manual Slice Mode * ------------------------------ * const containerRef = useRef(null); * const [data, setData] = useState([]); * const [page, setPage] = useState(1); * const [hasMore, setHasMore] = useState(true); * const limit = useInfiniteScroll(null, { containerRef, manual: true, start: 10, pace: 10, hasMore }); * const fetchMore = async () => { * const res = await fetch(`https://medrum.herokuapp.com/feeds/?source=5718e53e7a84fb1901e05971&page=${page}&sort=latest`); * const json = await res.json(); * if (json.length === 0) setHasMore(false); * else { * setData(prev => [...prev, ...json]); * setPage(prev => prev + 1); * } * }; * useEffect(() => { * fetchMore(); * }, [limit]); * return ( * <ul ref={containerRef} style={{ height: "400px", overflowY: "scroll" }}> * {data.slice(0, limit).map(item => ( * <li key={item.id}>{item.title}</li> * ))} * </ul> * ); */ var useInfiniteScroll = function useInfiniteScroll(callback) { var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, _ref$containerRef = _ref.containerRef, containerRef = _ref$containerRef === void 0 ? null : _ref$containerRef, _ref$manual = _ref.manual, manual = _ref$manual === void 0 ? false : _ref$manual, _ref$start = _ref.start, start = _ref$start === void 0 ? 10 : _ref$start, _ref$pace = _ref.pace, pace = _ref$pace === void 0 ? 10 : _ref$pace, _ref$offset = _ref.offset, offset = _ref$offset === void 0 ? 10 : _ref$offset, _ref$hasMore = _ref.hasMore, hasMore = _ref$hasMore === void 0 ? true : _ref$hasMore, _ref$debug = _ref.debug, debug = _ref$debug === void 0 ? false : _ref$debug; var _useState = react.useState(false), _useState2 = _slicedToArray(_useState, 2), isFetching = _useState2[0], setIsFetching = _useState2[1]; var _useState3 = react.useState(start), _useState4 = _slicedToArray(_useState3, 2), limit = _useState4[0], setLimit = _useState4[1]; var handleScroll = react.useCallback(function () { var el = (containerRef === null || containerRef === void 0 ? void 0 : containerRef.current) || document.documentElement; if (debug) console.log("[๐ŸŒ€ useInfiniteScroll] Scroll event fired..."); var scrollTop = (el === null || el === void 0 ? void 0 : el.scrollTop) || window.scrollY; var scrollHeight = (el === null || el === void 0 ? void 0 : el.scrollHeight) || document.body.scrollHeight; var clientHeight = (el === null || el === void 0 ? void 0 : el.clientHeight) || window.innerHeight; var nearBottom = clientHeight + scrollTop >= scrollHeight - offset; if (debug) { console.log("[๐ŸŒ€ useInfiniteScroll] Scroll event fired..."); console.log("[\uD83D\uDD0D Scroll Check] scrollTop: ".concat(scrollTop, ", clientHeight: ").concat(clientHeight, ", scrollHeight: ").concat(scrollHeight, ", nearBottom: ").concat(nearBottom)); } if (nearBottom && !isFetching && hasMore) { if (debug) console.log("[๐Ÿš€ Trigger] Threshold hit. Loading more..."); if (manual) { setLimit(function (prev) { if (debug) console.log("[๐Ÿ“ฆ Manual Mode] Increasing limit..."); return prev + pace; }); } else { setIsFetching(true); } } else { if (debug) { console.log("[โŒ Ignored Scroll] Conditions not met:"); console.table({ nearBottom: nearBottom, isFetching: isFetching, hasMore: hasMore }); } } }, [containerRef, isFetching, hasMore, manual, offset, pace, debug]); react.useEffect(function () { var el = (containerRef === null || containerRef === void 0 ? void 0 : containerRef.current) || window; if (debug) console.log("๐Ÿ“Œ Binding scroll event on", el === window ? "window" : "custom container"); el.addEventListener("scroll", handleScroll); return function () { if (debug) console.log("[๐Ÿ”Œ useInfiniteScroll] Detaching scroll listener..."); el.removeEventListener("scroll", handleScroll); }; }, [handleScroll, containerRef, debug]); react.useEffect(function () { if (!manual && isFetching) { if (debug) console.log("[๐Ÿ”„ useInfiniteScroll] Calling fetch callback..."); var maybePromise = callback === null || callback === void 0 ? void 0 : callback(); if (maybePromise !== null && maybePromise !== void 0 && maybePromise["finally"]) { maybePromise["finally"](function () { if (debug) console.log("[โœ… useInfiniteScroll] Fetch complete!"); setIsFetching(false); }); } else { setIsFetching(false); } } }, [isFetching, manual, callback, debug]); return manual ? limit : [isFetching, setIsFetching]; }; /** * useInterval * Runs a callback at a specified interval with support for pausing, debugging, initial delay, and external control. * * @param {Function} callback - Function to run at each interval tick. * @param {number|null} delay - Interval duration in ms. Use `null` to pause. * @param {Object} [options={}] - Additional options. * @param {boolean} [options.debug=false] - Log lifecycle events to the console. * @param {boolean} [options.stop=false] - Stops interval immediately when set to true. * @param {boolean} [options.immediate=false] - Fires callback immediately on mount. * @param {number} [options.initialDelay] - Wait this long before starting interval loop. * @param {number} [options.maxTicks] - Stop after this many ticks. * * @returns {{ * count: number, * reset: () => void, * forceTick: () => void, * pause: () => void, * resume: () => void, * }} * * @example * // Logs "Tick" every second * useInterval(() => console.log('Tick'), 1000); * * @example * const { count, reset } = useInterval(() => { * console.log('Tick!'); * }, 1000); * * @example * // With pause/resume capability * const [delay, setDelay] = useState(1000); * <button onClick={() => setDelay(prev => prev ? null : 1000)}>Toggle</button> * useInterval(() => console.log('Toggling...'), delay); * * @example * function CountDown({ initialCount, downLimit, initialDelay = 1000, bgColor = 'transparent' }) { * const [count, setCount] = useState(initialCount); * const [delay, setDelay] = useState(null); * useInterval(() => { * const newValue = count - 1; * setCount(newValue) * if (newValue <= downLimit) setDelay(null); * }, delay); * return ( * <div style={{ display: 'flex', backgroundColor: bgColor }}>{count} * <button onClick={() => setDelay(initialDelay)}>Start</button> * <button onClick={() => setDelay(null)}>Stop</button> * </div> * ) * } * * @example * import React, { useState, useCallback } from 'react'; * import useInterval from './useInterval'; * const ShoppingList = () => { * // Wait 5 seconds before fetching new data * const POLL_DELAY = 5000; * const [items, setItems] = useState([]); * const fetchItems = useCallback(async () => { * const response = await fetch('/shopping-list/items'); * const json = await response.json(); * setItems(json); * }, []); * useEffect(() => { * // Fetch items from API on mount * fetchItems(); * }, []); * useInterval(() => { * fetchItems(); * }, POLL_DELAY); * return ( * <ul> * {items.map((item) => <li>{item.title}</li>)} * </ul> * ) * }; */ var useInterval = function useInterval(callback, delay, options) { var _ref = options || {}, _ref$stop = _ref.stop, stop = _ref$stop === void 0 ? false : _ref$stop, _ref$debug = _ref.debug, debug = _ref$debug === void 0 ? false : _ref$debug, _ref$immediate = _ref.immediate, immediate = _ref$immediate === void 0 ? false : _ref$immediate, initialDelay = _ref.initialDelay, maxTicks = _ref.maxTicks; var savedCallback = react.useRef(); var intervalId = react.useRef(null); var initialTimeoutId = react.useRef(null); var _useState = react.useState(0), _useState2 = _slicedToArray(_useState, 2), tickCount = _useState2[0], setTickCount = _useState2[1]; var tick = function tick() { var _savedCallback$curren; if (debug) console.log('[โฑ๏ธ useInterval] Tick', tickCount + 1); setTickCount(function (prev) { return prev + 1; }); (_savedCallback$curren = savedCallback.current) === null || _savedCallback$curren === void 0 || _savedCallback$curren.call(savedCallback); }; var clear = function clear() { if (intervalId.current) { clearInterval(intervalId.current); intervalId.current = null; if (debug) console.log('[๐Ÿงน useInterval] Interval cleared'); } if (initialTimeoutId.current) { clearTimeout(initialTimeoutId.current); initialTimeoutId.current = null; if (debug) console.log('[๐Ÿงผ useInterval] Initial delay timeout cleared'); } }; var start = function start() { if (intervalId.current || delay == null || stop) return; var runInterval = function runInterval() { intervalId.current = setInterval(tick, delay); if (debug) console.log('[๐Ÿš€ useInterval] Interval started:', intervalId.current); }; if (initialDelay != null) { if (debug) console.log('[โณ useInterval] Waiting initialDelay:', initialDelay); initialTimeoutId.current = setTimeout(function () { runInterval(); }, initialDelay); } else { runInterval(); } }; react.useEffect(function () { savedCallback.current = callback; if (debug) console.log('[๐Ÿ’พ useInterval] Callback updated'); }, [callback]); react.useEffect(function () { if (stop || delay === null || delay === undefined) { clear(); return; } start(); return clear; }, [delay, debug, stop, initialDelay]); react.useEffect(function () { if (maxTicks != null && tickCount >= maxTicks) { clear(); if (debug) console.log('[โœ… useInterval] Max ticks reached'); } }, [tickCount]); react.useEffect(function () { if (immediate && typeof savedCallback.current === 'function') { savedCallback.current(); setTickCount(function (prev) { return prev + 1; }); if (debug) console.log('[โšก useInterval] Immediate tick fired'); } }, []); return { count: tickCount, reset: function reset() { return setTickCount(0); }, forceTick: function forceTick() { return tick(); }, pause: clear, resume: start }; }; /** * useKeyPress * * A powerful custom React hook for handling keyboard interactions. * * Features: * - Detect and respond to individual key presses * - Match specific target key (e.g. 'a') * - Maintain history of pressed keys * - Detect modifier key states: Shift, Alt, Ctrl, Meta * - Detect multi-key combos like 'ctrl+s', 'cmd+z' * - Pause and resume key tracking * - Optionally ignore key presses in input, textarea, or contentEditable fields * * @param {Object} options - Configuration options * @param {Function} [options.onKeyPress] - Function to run when a valid key is pressed * @param {string} [options.targetKey] - Key to match for triggering `keyMatched` (case-insensitive) * @param {boolean} [options.enableHistory=true] - Whether to track history of pressed keys * @param {number} [options.maxHistory=10] - Max number of keys to store in history * @param {boolean} [options.ignoreInput=false] - If true, ignores key presses from editable elements (input, textarea, contentEditable) * @param {string[]} [options.combos=[]] - List of keyboard combos to detect (e.g. ['ctrl+s', 'cmd+z']) * * @returns {Object} Hook output * @returns {string|null} keyPressed - The current key being pressed (null if none) * @returns {boolean} keyMatched - True if the most recent key matched `targetKey` * @returns {string[]} keyHistory - Array of previously pressed keys (up to `maxHistory`) * @returns {string|null} comboMatched - The matched combo string, if any (e.g. 'ctrl+s') * @returns {Object} modifiers - Current modifier key states { shift, alt, ctrl, meta } * @returns {Function} reset - Clears the keyMatched, keyHistory, and comboMatched states * @returns {Function} setPaused - Function to pause/resume the hook (use `setPaused(true)` to pause) * * @example * // Basic key detection and match tracking * const { keyPressed, keyMatched, keyHistory } = useKeyPress({ * onKeyPress: (key) => console.log('You pressed:', key), * targetKey: 'a', * enableHistory: true, * maxHistory: 5, * }); * * @example * // Full usage with modifiers, combo detection, pause and reset * const { * keyPressed, * keyMatched, * keyHistory, * comboMatched, * modifiers, * reset, * setPaused, * } = useKeyPress({ * onKeyPress: (key) => console.log('Key:', key), * targetKey: 's', * combos: ['ctrl+s', 'cmd+z'], * ignoreInput: true, * }); * * useEffect(() => { * if (comboMatched === 'ctrl+s') { * console.log('Save triggered!'); * } * }, [comboMatched]); */ var useKeyPress = function useKeyPress() { var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, onKeyPress = _ref.onKeyPress, targetKey = _ref.targetKey, _ref$enableHistory = _ref.enableHistory, enableHistory = _ref$enableHistory === void 0 ? true : _ref$enableHistory, _ref$maxHistory = _ref.maxHistory, maxHistory = _ref$maxHistory === void 0 ? 10 : _ref$maxHistory, _ref$ignoreInput = _ref.ignoreInput, ignoreInput = _ref$ignoreInput === void 0 ? false : _ref$ignoreInput, _ref$combos = _ref.combos, combos = _ref$combos === void 0 ? [] : _ref$combos; var _useState = react.useState(null), _useState2 = _slicedToArray(_useState, 2), keyPressed = _useState2[0], setKeyPressed = _useState2[1]; var _useState3 = react.useState([]), _useState4 = _slicedToArray(_useState3, 2), keyHistory = _useState4[0], setKeyHistory = _useState4[1]; var _useState5 = react.useState(false), _useState6 = _slicedToArray(_useState5, 2), keyMatched = _useState6[0], setKeyMatched = _useState6[1]; var _useState7 = react.useState(null), _useState8 = _slicedToArray(_useState7, 2), comboMatched = _useState8[0], setComboMatched = _useState8[1]; var _useState9 = react.useState(false), _useState0 = _slicedToArray(_useState9, 2), paused = _useState0[0], setPaused = _useState0[1]; var _useState1 = react.useState({ shift: false, alt: false, ctrl: false, meta: false }), _useState10 = _slicedToArray(_useState1, 2), modifiers = _useState10[0], setModifiers = _useState10[1]; var reset = function reset() { setKeyHistory([]); setKeyMatched(false); setComboMatched(null); }; var isEditableElement = function isEditableElement(el) { var _el$tagName; var tag = el === null || el === void 0 || (_el$tagName = el.tagName) === null || _el$tagName === void 0 ? void 0 : _el$tagName.toLowerCase(); return tag === 'input' || tag === 'textarea' || (el === null || el === void 0 ? void 0 : el.isContentEditable); }; react.useEffect(function () { var handleKeyDown = function handleKeyDown(e) { var _document, _e$key; if (paused) return; if (ignoreInput && isEditableElement((_document = document) === null || _document === void 0 ? void 0 : _document.activeElement)) return; var key = e === null || e === void 0 || (_e$key = e.key) === null || _e$key === void 0 ? void 0 : _e$key.toLowerCase(); setModifiers({ shift: e === null || e === void 0 ? void 0 : e.shiftKey, alt: e === null || e === void 0 ? void 0 : e.altKey, ctrl: e === null || e === void 0 ? void 0 : e.ctrlKey, meta: e === null || e === void 0 ? void 0 : e.metaKey }); // Debounce repeated key presses if (keyPressed === key) return; setKeyPressed(key); if (onKeyPress) onKeyPress(key); // Handle history if (enableHistory) { setKeyHistory(function (prev) { var updated = [].concat(_toConsumableArray(prev), [key]); return updated.slice(-maxHistory); }); } // Match single key if (key === (targetKey === null || targetKey === void 0 ? void 0 : targetKey.toLowerCase())) { setKeyMatched(true); } // Match combo var comboKey = [e.ctrlKey ? 'ctrl' : '', e.metaKey ? 'cmd' : '', e.altKey ? 'alt' : '', e.shiftKey ? 'shift' : '', key].filter(Boolean).join('+'); if (combos.includes(comboKey)) { setComboMatched(comboKey); } }; var handleKeyUp = function handleKeyUp() { setKeyPressed(null); }; window.addEventListener('keydown', handleKeyDown); window.addEventListener('keyup', handleKeyUp); return function () { window.removeEventListener('keydown', handleKeyDown); window.removeEventListener('keyup', handleKeyUp); }; }, [keyPressed, paused, onKeyPress, targetKey, enableHistory, maxHistory, combos, ignoreInput]); return { keyPressed: keyPressed, keyMatched: keyMatched, keyHistory: keyHistory, comboMatched: comboMatched, modifiers: modifiers, reset: reset, setPaused: setPaused }; }; var index = { useClickOutside: useClickOutside, useDebounce: useDebounce, useFetch: useFetch, useLocalStorage: useLocalStorage, usePrevious: usePrevious, useTheme: useDarkMode, useWindowSize: useWindowSize, useInfiniteScroll: useInfiniteScroll, useInterval: useInterval, useKeyPress: useKeyPress }; exports.default = index; exports.useClickOutside = useClickOutside; exports.useDebounce = useDebounce; exports.useFetch = useFetch; exports.useInfiniteScroll = useInfiniteScroll; exports.useInterval = useInterval; exports.useKeyPress = useKeyPress; exports.useLocalStorage = useLocalStorage; exports.usePrevious = usePrevious; exports.useTheme = useDarkMode; exports.useWindowSize = useWindowSize; //# sourceMappingURL=index.cjs.js.map