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
JavaScript
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
;