react-simplikit 0.0.30 → 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/hooks/useCounter/index.ts
21
+ var useCounter_exports = {};
22
+ __export(useCounter_exports, {
23
+ useCounter: () => useCounter
24
+ });
25
+ module.exports = __toCommonJS(useCounter_exports);
26
+
27
+ // src/hooks/useCounter/useCounter.ts
28
+ var import_react = require("react");
29
+ function useCounter({ initialValue = 0, min, max, step = 1 } = {}) {
30
+ const validateValue = (value) => {
31
+ let validatedValue = value;
32
+ if (min !== void 0 && validatedValue < min) {
33
+ validatedValue = min;
34
+ }
35
+ if (max !== void 0 && validatedValue > max) {
36
+ validatedValue = max;
37
+ }
38
+ return validatedValue;
39
+ };
40
+ const [count, setCountState] = (0, import_react.useState)(() => validateValue(initialValue));
41
+ const validateValueMemoized = (0, import_react.useCallback)(validateValue, [min, max]);
42
+ const setCount = (0, import_react.useCallback)(
43
+ (value) => {
44
+ setCountState((prev) => {
45
+ const nextValue = typeof value === "function" ? value(prev) : value;
46
+ return validateValueMemoized(nextValue);
47
+ });
48
+ },
49
+ [validateValueMemoized]
50
+ );
51
+ const increment = (0, import_react.useCallback)(() => {
52
+ setCount((prev) => prev + step);
53
+ }, [setCount, step]);
54
+ const decrement = (0, import_react.useCallback)(() => {
55
+ setCount((prev) => prev - step);
56
+ }, [setCount, step]);
57
+ const reset = (0, import_react.useCallback)(() => {
58
+ setCount(initialValue);
59
+ }, [setCount, initialValue]);
60
+ return {
61
+ count,
62
+ increment,
63
+ decrement,
64
+ reset,
65
+ setCount
66
+ };
67
+ }
68
+ // Annotate the CommonJS export names for ESM import in node:
69
+ 0 && (module.exports = {
70
+ useCounter
71
+ });
@@ -0,0 +1,49 @@
1
+ type UseCounterOptions = {
2
+ initialValue?: number;
3
+ min?: number;
4
+ max?: number;
5
+ step?: number;
6
+ };
7
+ type UseCounterReturn = {
8
+ count: number;
9
+ increment: () => void;
10
+ decrement: () => void;
11
+ reset: () => void;
12
+ setCount: (value: number | ((prev: number) => number)) => void;
13
+ };
14
+ /**
15
+ * @description
16
+ * `useCounter` is a React hook that manages a numeric counter state with increment, decrement, and reset capabilities.
17
+ * Optionally, you can provide minimum and maximum values to constrain the counter's range.
18
+ *
19
+ * @param {UseCounterOptions} options - The options for the counter.
20
+ * @param {number} [options.initialValue=0] - Initial value for the counter. Defaults to 0.
21
+ * @param {number} [options.min] - Minimum value the counter can reach. If not provided, there is no lower limit.
22
+ * @param {number} [options.max] - Maximum value the counter can reach. If not provided, there is no upper limit.
23
+ * @param {number} [options.step=1] - Value to increment or decrement by. Defaults to 1.
24
+ *
25
+ * @returns {UseCounterReturn} An object with count value and control functions.
26
+ *
27
+ * @example
28
+ * import { useCounter } from 'react-simplikit';
29
+ *
30
+ * function ShoppingCart() {
31
+ * const { count, increment, decrement, reset } = useCounter({
32
+ * initialValue: 1,
33
+ * min: 1,
34
+ * max: 10,
35
+ * });
36
+ *
37
+ * return (
38
+ * <div>
39
+ * <span>Quantity: {count}</span>
40
+ * <button type="button" onClick={decrement}>-</button>
41
+ * <button type="button" onClick={increment}>+</button>
42
+ * <button type="button" onClick={reset}>Reset</button>
43
+ * </div>
44
+ * );
45
+ * }
46
+ */
47
+ declare function useCounter({ initialValue, min, max, step }?: UseCounterOptions): UseCounterReturn;
48
+
49
+ export { useCounter };
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/hooks/useDoubleClick/index.ts
21
+ var useDoubleClick_exports = {};
22
+ __export(useDoubleClick_exports, {
23
+ useDoubleClick: () => useDoubleClick
24
+ });
25
+ module.exports = __toCommonJS(useDoubleClick_exports);
26
+
27
+ // src/hooks/useDoubleClick/useDoubleClick.ts
28
+ var import_react2 = require("react");
29
+
30
+ // src/hooks/usePreservedCallback/usePreservedCallback.ts
31
+ var import_react = require("react");
32
+ function usePreservedCallback(callback) {
33
+ const callbackRef = (0, import_react.useRef)(callback);
34
+ (0, import_react.useEffect)(() => {
35
+ callbackRef.current = callback;
36
+ }, [callback]);
37
+ return (0, import_react.useCallback)((...args) => {
38
+ return callbackRef.current(...args);
39
+ }, []);
40
+ }
41
+
42
+ // src/hooks/useDoubleClick/useDoubleClick.ts
43
+ function useDoubleClick({
44
+ delay = 250,
45
+ click,
46
+ doubleClick
47
+ }) {
48
+ const clickTimeout = (0, import_react2.useRef)(null);
49
+ const clearClickTimeout = usePreservedCallback(() => {
50
+ if (clickTimeout.current != null) {
51
+ window.clearTimeout(clickTimeout.current);
52
+ clickTimeout.current = null;
53
+ }
54
+ });
55
+ (0, import_react2.useEffect)(() => () => clearClickTimeout(), [clearClickTimeout]);
56
+ const handleEvent = (0, import_react2.useCallback)(
57
+ (event) => {
58
+ clearClickTimeout();
59
+ if (click && event.detail === 1) {
60
+ clickTimeout.current = window.setTimeout(() => {
61
+ click(event);
62
+ }, delay);
63
+ }
64
+ if (event.detail === 2) {
65
+ doubleClick(event);
66
+ }
67
+ },
68
+ [click, doubleClick, delay, clearClickTimeout]
69
+ );
70
+ return handleEvent;
71
+ }
72
+ // Annotate the CommonJS export names for ESM import in node:
73
+ 0 && (module.exports = {
74
+ useDoubleClick
75
+ });
@@ -0,0 +1,42 @@
1
+ import { MouseEvent } from 'react';
2
+
3
+ type UseDoubleClickProps<E extends HTMLElement> = {
4
+ delay?: number;
5
+ click?: (event: MouseEvent<E>) => void;
6
+ doubleClick: (event: MouseEvent<E>) => void;
7
+ };
8
+ /**
9
+ * @description
10
+ * `useDoubleClick` is a React hook that differentiates between single and double click events.
11
+ * It delays the single click callback execution for a specified time, and cancels it if a second click (i.e. a double click) occurs within that time.
12
+ *
13
+ * @template {HTMLElement} E - The specific type of HTMLElement to be used with this hook (e.g., HTMLButtonElement, HTMLDivElement).
14
+ * @param {Object} params - Configuration options for click handling.
15
+ * @param {number} [params.delay=250] - The number of milliseconds to wait before triggering the single click callback. Defaults to 250ms.
16
+ * @param {(event: MouseEvent<E>) => void} [params.click] - The callback function to be executed on a single click.
17
+ * @param {(event: MouseEvent<E>) => void} params.doubleClick - The callback function to be executed on a double click. Required.
18
+ *
19
+ * @returns {(event: MouseEvent<E>) => void} A click handler function to attach to an element's `onClick` event.
20
+ *
21
+ * @example
22
+ * function GalleryCard() {
23
+ * const [selected, setSelected] = useState(false);
24
+ *
25
+ * const handleClick = () => setSelected((prev) => !prev);
26
+ * const handleDoubleClick = () => alert('Zoom in!');
27
+ *
28
+ * const handleEvent = useDoubleClick({
29
+ * click: handleClick,
30
+ * doubleClick: handleDoubleClick,
31
+ * });
32
+ *
33
+ * return (
34
+ * <div onClick={handleEvent}>
35
+ * {selected ? 'Selected' : 'Not selected'}
36
+ * </div>
37
+ * );
38
+ * }
39
+ */
40
+ declare function useDoubleClick<E extends HTMLElement = HTMLElement>({ delay, click, doubleClick, }: UseDoubleClickProps<E>): (event: MouseEvent<E>) => void;
41
+
42
+ export { useDoubleClick };
package/dist/index.cjs CHANGED
@@ -28,6 +28,7 @@ __export(index_exports, {
28
28
  useAsyncEffect: () => useAsyncEffect,
29
29
  useBooleanState: () => useBooleanState,
30
30
  useCallbackOncePerRender: () => useCallbackOncePerRender,
31
+ useCounter: () => useCounter,
31
32
  useDebounce: () => useDebounce,
32
33
  useImpressionRef: () => useImpressionRef,
33
34
  useInputState: () => useInputState,
@@ -374,13 +375,55 @@ function useCallbackOncePerRender(callback, deps) {
374
375
  });
375
376
  }
376
377
 
377
- // src/hooks/useDebounce/useDebounce.ts
378
+ // src/hooks/useCounter/useCounter.ts
378
379
  var import_react11 = require("react");
380
+ function useCounter({ initialValue = 0, min, max, step = 1 } = {}) {
381
+ const validateValue = (value) => {
382
+ let validatedValue = value;
383
+ if (min !== void 0 && validatedValue < min) {
384
+ validatedValue = min;
385
+ }
386
+ if (max !== void 0 && validatedValue > max) {
387
+ validatedValue = max;
388
+ }
389
+ return validatedValue;
390
+ };
391
+ const [count, setCountState] = (0, import_react11.useState)(() => validateValue(initialValue));
392
+ const validateValueMemoized = (0, import_react11.useCallback)(validateValue, [min, max]);
393
+ const setCount = (0, import_react11.useCallback)(
394
+ (value) => {
395
+ setCountState((prev) => {
396
+ const nextValue = typeof value === "function" ? value(prev) : value;
397
+ return validateValueMemoized(nextValue);
398
+ });
399
+ },
400
+ [validateValueMemoized]
401
+ );
402
+ const increment = (0, import_react11.useCallback)(() => {
403
+ setCount((prev) => prev + step);
404
+ }, [setCount, step]);
405
+ const decrement = (0, import_react11.useCallback)(() => {
406
+ setCount((prev) => prev - step);
407
+ }, [setCount, step]);
408
+ const reset = (0, import_react11.useCallback)(() => {
409
+ setCount(initialValue);
410
+ }, [setCount, initialValue]);
411
+ return {
412
+ count,
413
+ increment,
414
+ decrement,
415
+ reset,
416
+ setCount
417
+ };
418
+ }
419
+
420
+ // src/hooks/useDebounce/useDebounce.ts
379
421
  var import_react12 = require("react");
422
+ var import_react13 = require("react");
380
423
  function useDebounce(callback, wait, options = {}) {
381
424
  const preservedCallback = usePreservedCallback(callback);
382
425
  const { leading = false, trailing = true } = options;
383
- const edges = (0, import_react12.useMemo)(() => {
426
+ const edges = (0, import_react13.useMemo)(() => {
384
427
  const _edges = [];
385
428
  if (leading) {
386
429
  _edges.push("leading");
@@ -390,10 +433,10 @@ function useDebounce(callback, wait, options = {}) {
390
433
  }
391
434
  return _edges;
392
435
  }, [leading, trailing]);
393
- const debounced = (0, import_react12.useMemo)(() => {
436
+ const debounced = (0, import_react13.useMemo)(() => {
394
437
  return debounce(preservedCallback, wait, { edges });
395
438
  }, [preservedCallback, wait, edges]);
396
- (0, import_react11.useEffect)(() => {
439
+ (0, import_react12.useEffect)(() => {
397
440
  return () => {
398
441
  debounced.cancel();
399
442
  };
@@ -402,10 +445,10 @@ function useDebounce(callback, wait, options = {}) {
402
445
  }
403
446
 
404
447
  // src/hooks/useInputState/useInputState.ts
405
- var import_react13 = require("react");
448
+ var import_react14 = require("react");
406
449
  function useInputState(initialValue = "", transformValue = echo) {
407
- const [value, setValue] = (0, import_react13.useState)(initialValue);
408
- const handleValueChange = (0, import_react13.useCallback)(
450
+ const [value, setValue] = (0, import_react14.useState)(initialValue);
451
+ const handleValueChange = (0, import_react14.useCallback)(
409
452
  ({ target: { value: value2 } }) => {
410
453
  setValue(transformValue(value2));
411
454
  },
@@ -418,18 +461,18 @@ function echo(v) {
418
461
  }
419
462
 
420
463
  // src/hooks/useInterval/useInterval.ts
421
- var import_react14 = require("react");
464
+ var import_react15 = require("react");
422
465
  function useInterval(callback, options) {
423
466
  const delay = typeof options === "number" ? options : options.delay;
424
467
  const immediate = typeof options === "number" ? false : options.immediate;
425
468
  const enabled = typeof options === "number" ? true : options.enabled ?? true;
426
469
  const preservedCallback = usePreservedCallback(callback);
427
- (0, import_react14.useEffect)(() => {
470
+ (0, import_react15.useEffect)(() => {
428
471
  if (immediate === true && enabled) {
429
472
  preservedCallback();
430
473
  }
431
474
  }, [immediate, preservedCallback, enabled]);
432
- (0, import_react14.useEffect)(() => {
475
+ (0, import_react15.useEffect)(() => {
433
476
  if (!enabled) {
434
477
  return;
435
478
  }
@@ -439,11 +482,11 @@ function useInterval(callback, options) {
439
482
  }
440
483
 
441
484
  // src/hooks/useLoading/useLoading.ts
442
- var import_react15 = require("react");
485
+ var import_react16 = require("react");
443
486
  function useLoading() {
444
- const [loading, setLoading] = (0, import_react15.useState)(false);
487
+ const [loading, setLoading] = (0, import_react16.useState)(false);
445
488
  const ref = useIsMountedRef();
446
- const startTransition = (0, import_react15.useCallback)(
489
+ const startTransition = (0, import_react16.useCallback)(
447
490
  async (promise) => {
448
491
  try {
449
492
  setLoading(true);
@@ -457,11 +500,11 @@ function useLoading() {
457
500
  },
458
501
  [ref.isMounted]
459
502
  );
460
- return (0, import_react15.useMemo)(() => [loading, startTransition], [loading, startTransition]);
503
+ return (0, import_react16.useMemo)(() => [loading, startTransition], [loading, startTransition]);
461
504
  }
462
505
  function useIsMountedRef() {
463
- const ref = (0, import_react15.useRef)({ isMounted: true }).current;
464
- (0, import_react15.useEffect)(() => {
506
+ const ref = (0, import_react16.useRef)({ isMounted: true }).current;
507
+ (0, import_react16.useEffect)(() => {
465
508
  ref.isMounted = true;
466
509
  return () => {
467
510
  ref.isMounted = false;
@@ -471,9 +514,9 @@ function useIsMountedRef() {
471
514
  }
472
515
 
473
516
  // src/hooks/useOutsideClickEffect/useOutsideClickEffect.ts
474
- var import_react16 = require("react");
517
+ var import_react17 = require("react");
475
518
  function useOutsideClickEffect(container, callback) {
476
- const containers = (0, import_react16.useRef)([]);
519
+ const containers = (0, import_react17.useRef)([]);
477
520
  const handleDocumentClick = usePreservedCallback(({ target }) => {
478
521
  if (target === null) {
479
522
  return;
@@ -486,10 +529,10 @@ function useOutsideClickEffect(container, callback) {
486
529
  }
487
530
  callback();
488
531
  });
489
- (0, import_react16.useEffect)(() => {
532
+ (0, import_react17.useEffect)(() => {
490
533
  containers.current = [container].flat(1).filter((item) => item != null);
491
534
  }, [container]);
492
- (0, import_react16.useEffect)(() => {
535
+ (0, import_react17.useEffect)(() => {
493
536
  document.addEventListener("click", handleDocumentClick);
494
537
  return () => {
495
538
  document.removeEventListener("click", handleDocumentClick);
@@ -498,10 +541,10 @@ function useOutsideClickEffect(container, callback) {
498
541
  }
499
542
 
500
543
  // src/hooks/usePreservedReference/usePreservedReference.ts
501
- var import_react17 = require("react");
544
+ var import_react18 = require("react");
502
545
  function usePreservedReference(value, areValuesEqual = areDeeplyEqual) {
503
- const ref = (0, import_react17.useRef)(value);
504
- return (0, import_react17.useMemo)(() => {
546
+ const ref = (0, import_react18.useRef)(value);
547
+ return (0, import_react18.useMemo)(() => {
505
548
  if (!areValuesEqual(ref.current, value)) {
506
549
  ref.current = value;
507
550
  }
@@ -513,12 +556,12 @@ function areDeeplyEqual(x, y) {
513
556
  }
514
557
 
515
558
  // src/hooks/usePrevious/usePrevious.ts
516
- var import_react18 = require("react");
559
+ var import_react19 = require("react");
517
560
  var strictEquals = (prev, next) => prev === next;
518
561
  function usePrevious(state, compare = strictEquals) {
519
- const prevRef = (0, import_react18.useRef)(state);
520
- const currentRef = (0, import_react18.useRef)(state);
521
- const isFirstRender = (0, import_react18.useRef)(true);
562
+ const prevRef = (0, import_react19.useRef)(state);
563
+ const currentRef = (0, import_react19.useRef)(state);
564
+ const isFirstRender = (0, import_react19.useRef)(true);
522
565
  if (isFirstRender.current) {
523
566
  isFirstRender.current = false;
524
567
  return prevRef.current;
@@ -531,7 +574,7 @@ function usePrevious(state, compare = strictEquals) {
531
574
  }
532
575
 
533
576
  // src/hooks/useStorageState/useStorageState.ts
534
- var import_react19 = require("react");
577
+ var import_react20 = require("react");
535
578
 
536
579
  // src/hooks/useStorageState/storage.ts
537
580
  var MemoStorage = class {
@@ -643,11 +686,11 @@ function useStorageState(key, {
643
686
  ...options
644
687
  } = {}) {
645
688
  const serializedDefaultValue = defaultValue;
646
- const cache = (0, import_react19.useRef)({
689
+ const cache = (0, import_react20.useRef)({
647
690
  data: null,
648
691
  parsed: serializedDefaultValue
649
692
  });
650
- const getSnapshot = (0, import_react19.useCallback)(() => {
693
+ const getSnapshot = (0, import_react20.useCallback)(() => {
651
694
  const deserializer = "deserializer" in options ? options.deserializer : JSON.parse;
652
695
  const data = storage.get(key);
653
696
  if (data !== cache.current.data) {
@@ -660,7 +703,7 @@ function useStorageState(key, {
660
703
  }
661
704
  return cache.current.parsed;
662
705
  }, [defaultValue, key, storage]);
663
- const storageState = (0, import_react19.useSyncExternalStore)(
706
+ const storageState = (0, import_react20.useSyncExternalStore)(
664
707
  (onStoreChange) => {
665
708
  listeners.add(onStoreChange);
666
709
  const handler = (event) => {
@@ -677,7 +720,7 @@ function useStorageState(key, {
677
720
  () => getSnapshot(),
678
721
  () => serializedDefaultValue
679
722
  );
680
- const setStorageState = (0, import_react19.useCallback)(
723
+ const setStorageState = (0, import_react20.useCallback)(
681
724
  (value) => {
682
725
  const serializer = "serializer" in options ? options.serializer : JSON.stringify;
683
726
  const nextValue = typeof value === "function" ? value(getSnapshot()) : value;
@@ -690,14 +733,14 @@ function useStorageState(key, {
690
733
  },
691
734
  [getSnapshot, key, storage]
692
735
  );
693
- const refreshStorageState = (0, import_react19.useCallback)(() => {
736
+ const refreshStorageState = (0, import_react20.useCallback)(() => {
694
737
  setStorageState(getSnapshot());
695
738
  }, [storage, getSnapshot, setStorageState]);
696
739
  return ensureSerializable([storageState, setStorageState, refreshStorageState]);
697
740
  }
698
741
 
699
742
  // src/hooks/useThrottle/useThrottle.ts
700
- var import_react20 = require("react");
743
+ var import_react21 = require("react");
701
744
 
702
745
  // src/hooks/useThrottle/throttle.ts
703
746
  function throttle(func, throttleMs, { edges = ["leading", "trailing"] } = {}) {
@@ -723,11 +766,11 @@ function throttle(func, throttleMs, { edges = ["leading", "trailing"] } = {}) {
723
766
  function useThrottle(callback, wait, options) {
724
767
  const preservedCallback = usePreservedCallback(callback);
725
768
  const preservedOptions = usePreservedReference(options ?? {});
726
- const throttledCallback = (0, import_react20.useMemo)(
769
+ const throttledCallback = (0, import_react21.useMemo)(
727
770
  () => throttle(preservedCallback, wait, preservedOptions),
728
771
  [preservedOptions, preservedCallback, wait]
729
772
  );
730
- (0, import_react20.useEffect)(() => {
773
+ (0, import_react21.useEffect)(() => {
731
774
  return () => {
732
775
  throttledCallback.cancel();
733
776
  };
@@ -736,29 +779,29 @@ function useThrottle(callback, wait, options) {
736
779
  }
737
780
 
738
781
  // src/hooks/useTimeout/useTimeout.ts
739
- var import_react21 = require("react");
782
+ var import_react22 = require("react");
740
783
  function useTimeout(callback, delay = 0) {
741
784
  const preservedCallback = usePreservedCallback(callback);
742
- (0, import_react21.useEffect)(() => {
785
+ (0, import_react22.useEffect)(() => {
743
786
  const timeoutId = window.setTimeout(preservedCallback, delay);
744
787
  return () => window.clearTimeout(timeoutId);
745
788
  }, [delay, preservedCallback]);
746
789
  }
747
790
 
748
791
  // src/hooks/useToggle/useToggle.ts
749
- var import_react22 = require("react");
792
+ var import_react23 = require("react");
750
793
  function useToggle(initialValue = false) {
751
- return (0, import_react22.useReducer)(toggle, initialValue);
794
+ return (0, import_react23.useReducer)(toggle, initialValue);
752
795
  }
753
796
  var toggle = (state) => !state;
754
797
 
755
798
  // src/utils/buildContext/buildContext.tsx
756
- var import_react23 = require("react");
799
+ var import_react24 = require("react");
757
800
  var import_jsx_runtime3 = require("react/jsx-runtime");
758
801
  function buildContext(contextName, defaultContextValues) {
759
- const Context = (0, import_react23.createContext)(defaultContextValues ?? void 0);
802
+ const Context = (0, import_react24.createContext)(defaultContextValues ?? void 0);
760
803
  function Provider({ children, ...contextValues }) {
761
- const value = (0, import_react23.useMemo)(
804
+ const value = (0, import_react24.useMemo)(
762
805
  () => Object.keys(contextValues).length > 0 ? contextValues : null,
763
806
  // eslint-disable-next-line react-hooks/exhaustive-deps
764
807
  [...Object.values(contextValues)]
@@ -766,7 +809,7 @@ function buildContext(contextName, defaultContextValues) {
766
809
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Context.Provider, { value, children });
767
810
  }
768
811
  function useInnerContext() {
769
- const context = (0, import_react23.useContext)(Context);
812
+ const context = (0, import_react24.useContext)(Context);
770
813
  if (context != null) {
771
814
  return context;
772
815
  }
@@ -787,6 +830,7 @@ function buildContext(contextName, defaultContextValues) {
787
830
  useAsyncEffect,
788
831
  useBooleanState,
789
832
  useCallbackOncePerRender,
833
+ useCounter,
790
834
  useDebounce,
791
835
  useImpressionRef,
792
836
  useInputState,
package/dist/index.d.cts CHANGED
@@ -4,6 +4,7 @@ export { SwitchCase } from './components/SwitchCase/index.cjs';
4
4
  export { useAsyncEffect } from './hooks/useAsyncEffect/index.cjs';
5
5
  export { useBooleanState } from './hooks/useBooleanState/index.cjs';
6
6
  export { useCallbackOncePerRender } from './hooks/useCallbackOncePerRender/index.cjs';
7
+ export { useCounter } from './hooks/useCounter/index.cjs';
7
8
  export { useDebounce } from './hooks/useDebounce/index.cjs';
8
9
  export { useImpressionRef } from './hooks/useImpressionRef/index.cjs';
9
10
  export { useInputState } from './hooks/useInputState/index.cjs';
@@ -0,0 +1,49 @@
1
+ type UseCounterOptions = {
2
+ initialValue?: number;
3
+ min?: number;
4
+ max?: number;
5
+ step?: number;
6
+ };
7
+ type UseCounterReturn = {
8
+ count: number;
9
+ increment: () => void;
10
+ decrement: () => void;
11
+ reset: () => void;
12
+ setCount: (value: number | ((prev: number) => number)) => void;
13
+ };
14
+ /**
15
+ * @description
16
+ * `useCounter` is a React hook that manages a numeric counter state with increment, decrement, and reset capabilities.
17
+ * Optionally, you can provide minimum and maximum values to constrain the counter's range.
18
+ *
19
+ * @param {UseCounterOptions} options - The options for the counter.
20
+ * @param {number} [options.initialValue=0] - Initial value for the counter. Defaults to 0.
21
+ * @param {number} [options.min] - Minimum value the counter can reach. If not provided, there is no lower limit.
22
+ * @param {number} [options.max] - Maximum value the counter can reach. If not provided, there is no upper limit.
23
+ * @param {number} [options.step=1] - Value to increment or decrement by. Defaults to 1.
24
+ *
25
+ * @returns {UseCounterReturn} An object with count value and control functions.
26
+ *
27
+ * @example
28
+ * import { useCounter } from 'react-simplikit';
29
+ *
30
+ * function ShoppingCart() {
31
+ * const { count, increment, decrement, reset } = useCounter({
32
+ * initialValue: 1,
33
+ * min: 1,
34
+ * max: 10,
35
+ * });
36
+ *
37
+ * return (
38
+ * <div>
39
+ * <span>Quantity: {count}</span>
40
+ * <button type="button" onClick={decrement}>-</button>
41
+ * <button type="button" onClick={increment}>+</button>
42
+ * <button type="button" onClick={reset}>Reset</button>
43
+ * </div>
44
+ * );
45
+ * }
46
+ */
47
+ declare function useCounter({ initialValue, min, max, step }?: UseCounterOptions): UseCounterReturn;
48
+
49
+ export { useCounter };
@@ -0,0 +1,44 @@
1
+ // src/hooks/useCounter/useCounter.ts
2
+ import { useCallback, useState } from "react";
3
+ function useCounter({ initialValue = 0, min, max, step = 1 } = {}) {
4
+ const validateValue = (value) => {
5
+ let validatedValue = value;
6
+ if (min !== void 0 && validatedValue < min) {
7
+ validatedValue = min;
8
+ }
9
+ if (max !== void 0 && validatedValue > max) {
10
+ validatedValue = max;
11
+ }
12
+ return validatedValue;
13
+ };
14
+ const [count, setCountState] = useState(() => validateValue(initialValue));
15
+ const validateValueMemoized = useCallback(validateValue, [min, max]);
16
+ const setCount = useCallback(
17
+ (value) => {
18
+ setCountState((prev) => {
19
+ const nextValue = typeof value === "function" ? value(prev) : value;
20
+ return validateValueMemoized(nextValue);
21
+ });
22
+ },
23
+ [validateValueMemoized]
24
+ );
25
+ const increment = useCallback(() => {
26
+ setCount((prev) => prev + step);
27
+ }, [setCount, step]);
28
+ const decrement = useCallback(() => {
29
+ setCount((prev) => prev - step);
30
+ }, [setCount, step]);
31
+ const reset = useCallback(() => {
32
+ setCount(initialValue);
33
+ }, [setCount, initialValue]);
34
+ return {
35
+ count,
36
+ increment,
37
+ decrement,
38
+ reset,
39
+ setCount
40
+ };
41
+ }
42
+ export {
43
+ useCounter
44
+ };
@@ -0,0 +1,42 @@
1
+ import { MouseEvent } from 'react';
2
+
3
+ type UseDoubleClickProps<E extends HTMLElement> = {
4
+ delay?: number;
5
+ click?: (event: MouseEvent<E>) => void;
6
+ doubleClick: (event: MouseEvent<E>) => void;
7
+ };
8
+ /**
9
+ * @description
10
+ * `useDoubleClick` is a React hook that differentiates between single and double click events.
11
+ * It delays the single click callback execution for a specified time, and cancels it if a second click (i.e. a double click) occurs within that time.
12
+ *
13
+ * @template {HTMLElement} E - The specific type of HTMLElement to be used with this hook (e.g., HTMLButtonElement, HTMLDivElement).
14
+ * @param {Object} params - Configuration options for click handling.
15
+ * @param {number} [params.delay=250] - The number of milliseconds to wait before triggering the single click callback. Defaults to 250ms.
16
+ * @param {(event: MouseEvent<E>) => void} [params.click] - The callback function to be executed on a single click.
17
+ * @param {(event: MouseEvent<E>) => void} params.doubleClick - The callback function to be executed on a double click. Required.
18
+ *
19
+ * @returns {(event: MouseEvent<E>) => void} A click handler function to attach to an element's `onClick` event.
20
+ *
21
+ * @example
22
+ * function GalleryCard() {
23
+ * const [selected, setSelected] = useState(false);
24
+ *
25
+ * const handleClick = () => setSelected((prev) => !prev);
26
+ * const handleDoubleClick = () => alert('Zoom in!');
27
+ *
28
+ * const handleEvent = useDoubleClick({
29
+ * click: handleClick,
30
+ * doubleClick: handleDoubleClick,
31
+ * });
32
+ *
33
+ * return (
34
+ * <div onClick={handleEvent}>
35
+ * {selected ? 'Selected' : 'Not selected'}
36
+ * </div>
37
+ * );
38
+ * }
39
+ */
40
+ declare function useDoubleClick<E extends HTMLElement = HTMLElement>({ delay, click, doubleClick, }: UseDoubleClickProps<E>): (event: MouseEvent<E>) => void;
41
+
42
+ export { useDoubleClick };
@@ -0,0 +1,48 @@
1
+ // src/hooks/useDoubleClick/useDoubleClick.ts
2
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2 } from "react";
3
+
4
+ // src/hooks/usePreservedCallback/usePreservedCallback.ts
5
+ import { useCallback, useEffect, useRef } from "react";
6
+ function usePreservedCallback(callback) {
7
+ const callbackRef = useRef(callback);
8
+ useEffect(() => {
9
+ callbackRef.current = callback;
10
+ }, [callback]);
11
+ return useCallback((...args) => {
12
+ return callbackRef.current(...args);
13
+ }, []);
14
+ }
15
+
16
+ // src/hooks/useDoubleClick/useDoubleClick.ts
17
+ function useDoubleClick({
18
+ delay = 250,
19
+ click,
20
+ doubleClick
21
+ }) {
22
+ const clickTimeout = useRef2(null);
23
+ const clearClickTimeout = usePreservedCallback(() => {
24
+ if (clickTimeout.current != null) {
25
+ window.clearTimeout(clickTimeout.current);
26
+ clickTimeout.current = null;
27
+ }
28
+ });
29
+ useEffect2(() => () => clearClickTimeout(), [clearClickTimeout]);
30
+ const handleEvent = useCallback2(
31
+ (event) => {
32
+ clearClickTimeout();
33
+ if (click && event.detail === 1) {
34
+ clickTimeout.current = window.setTimeout(() => {
35
+ click(event);
36
+ }, delay);
37
+ }
38
+ if (event.detail === 2) {
39
+ doubleClick(event);
40
+ }
41
+ },
42
+ [click, doubleClick, delay, clearClickTimeout]
43
+ );
44
+ return handleEvent;
45
+ }
46
+ export {
47
+ useDoubleClick
48
+ };
package/esm/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export { SwitchCase } from './components/SwitchCase/index.js';
4
4
  export { useAsyncEffect } from './hooks/useAsyncEffect/index.js';
5
5
  export { useBooleanState } from './hooks/useBooleanState/index.js';
6
6
  export { useCallbackOncePerRender } from './hooks/useCallbackOncePerRender/index.js';
7
+ export { useCounter } from './hooks/useCounter/index.js';
7
8
  export { useDebounce } from './hooks/useDebounce/index.js';
8
9
  export { useImpressionRef } from './hooks/useImpressionRef/index.js';
9
10
  export { useInputState } from './hooks/useInputState/index.js';
package/esm/index.js CHANGED
@@ -325,6 +325,48 @@ function useCallbackOncePerRender(callback, deps) {
325
325
  });
326
326
  }
327
327
 
328
+ // src/hooks/useCounter/useCounter.ts
329
+ import { useCallback as useCallback6, useState as useState2 } from "react";
330
+ function useCounter({ initialValue = 0, min, max, step = 1 } = {}) {
331
+ const validateValue = (value) => {
332
+ let validatedValue = value;
333
+ if (min !== void 0 && validatedValue < min) {
334
+ validatedValue = min;
335
+ }
336
+ if (max !== void 0 && validatedValue > max) {
337
+ validatedValue = max;
338
+ }
339
+ return validatedValue;
340
+ };
341
+ const [count, setCountState] = useState2(() => validateValue(initialValue));
342
+ const validateValueMemoized = useCallback6(validateValue, [min, max]);
343
+ const setCount = useCallback6(
344
+ (value) => {
345
+ setCountState((prev) => {
346
+ const nextValue = typeof value === "function" ? value(prev) : value;
347
+ return validateValueMemoized(nextValue);
348
+ });
349
+ },
350
+ [validateValueMemoized]
351
+ );
352
+ const increment = useCallback6(() => {
353
+ setCount((prev) => prev + step);
354
+ }, [setCount, step]);
355
+ const decrement = useCallback6(() => {
356
+ setCount((prev) => prev - step);
357
+ }, [setCount, step]);
358
+ const reset = useCallback6(() => {
359
+ setCount(initialValue);
360
+ }, [setCount, initialValue]);
361
+ return {
362
+ count,
363
+ increment,
364
+ decrement,
365
+ reset,
366
+ setCount
367
+ };
368
+ }
369
+
328
370
  // src/hooks/useDebounce/useDebounce.ts
329
371
  import { useEffect as useEffect6 } from "react";
330
372
  import { useMemo as useMemo3 } from "react";
@@ -353,10 +395,10 @@ function useDebounce(callback, wait, options = {}) {
353
395
  }
354
396
 
355
397
  // src/hooks/useInputState/useInputState.ts
356
- import { useCallback as useCallback6, useState as useState2 } from "react";
398
+ import { useCallback as useCallback7, useState as useState3 } from "react";
357
399
  function useInputState(initialValue = "", transformValue = echo) {
358
- const [value, setValue] = useState2(initialValue);
359
- const handleValueChange = useCallback6(
400
+ const [value, setValue] = useState3(initialValue);
401
+ const handleValueChange = useCallback7(
360
402
  ({ target: { value: value2 } }) => {
361
403
  setValue(transformValue(value2));
362
404
  },
@@ -390,11 +432,11 @@ function useInterval(callback, options) {
390
432
  }
391
433
 
392
434
  // src/hooks/useLoading/useLoading.ts
393
- import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef6, useState as useState3 } from "react";
435
+ import { useCallback as useCallback8, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef6, useState as useState4 } from "react";
394
436
  function useLoading() {
395
- const [loading, setLoading] = useState3(false);
437
+ const [loading, setLoading] = useState4(false);
396
438
  const ref = useIsMountedRef();
397
- const startTransition = useCallback7(
439
+ const startTransition = useCallback8(
398
440
  async (promise) => {
399
441
  try {
400
442
  setLoading(true);
@@ -482,7 +524,7 @@ function usePrevious(state, compare = strictEquals) {
482
524
  }
483
525
 
484
526
  // src/hooks/useStorageState/useStorageState.ts
485
- import { useCallback as useCallback8, useRef as useRef10, useSyncExternalStore } from "react";
527
+ import { useCallback as useCallback9, useRef as useRef10, useSyncExternalStore } from "react";
486
528
 
487
529
  // src/hooks/useStorageState/storage.ts
488
530
  var MemoStorage = class {
@@ -598,7 +640,7 @@ function useStorageState(key, {
598
640
  data: null,
599
641
  parsed: serializedDefaultValue
600
642
  });
601
- const getSnapshot = useCallback8(() => {
643
+ const getSnapshot = useCallback9(() => {
602
644
  const deserializer = "deserializer" in options ? options.deserializer : JSON.parse;
603
645
  const data = storage.get(key);
604
646
  if (data !== cache.current.data) {
@@ -628,7 +670,7 @@ function useStorageState(key, {
628
670
  () => getSnapshot(),
629
671
  () => serializedDefaultValue
630
672
  );
631
- const setStorageState = useCallback8(
673
+ const setStorageState = useCallback9(
632
674
  (value) => {
633
675
  const serializer = "serializer" in options ? options.serializer : JSON.stringify;
634
676
  const nextValue = typeof value === "function" ? value(getSnapshot()) : value;
@@ -641,7 +683,7 @@ function useStorageState(key, {
641
683
  },
642
684
  [getSnapshot, key, storage]
643
685
  );
644
- const refreshStorageState = useCallback8(() => {
686
+ const refreshStorageState = useCallback9(() => {
645
687
  setStorageState(getSnapshot());
646
688
  }, [storage, getSnapshot, setStorageState]);
647
689
  return ensureSerializable([storageState, setStorageState, refreshStorageState]);
@@ -737,6 +779,7 @@ export {
737
779
  useAsyncEffect,
738
780
  useBooleanState,
739
781
  useCallbackOncePerRender,
782
+ useCounter,
740
783
  useDebounce,
741
784
  useImpressionRef,
742
785
  useInputState,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-simplikit",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "main": "./dist/index.cjs",
5
5
  "type": "module",
6
6
  "sideEffects": false,