@mohasinac/react 1.0.0 → 1.1.0

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.
package/dist/index.js CHANGED
@@ -1,3 +1,23 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+
1
21
  // src/hooks/useMediaQuery.ts
2
22
  import { useState, useEffect } from "react";
3
23
  function useMediaQuery(query) {
@@ -708,8 +728,386 @@ function useCamera() {
708
728
  switchCamera
709
729
  };
710
730
  }
731
+
732
+ // src/hooks/useBulkSelection.ts
733
+ import { useState as useState5, useCallback as useCallback5, useMemo as useMemo2 } from "react";
734
+ var BULK_MAX_IDS = 100;
735
+ function useBulkSelection({
736
+ items,
737
+ keyExtractor,
738
+ maxSelection = BULK_MAX_IDS
739
+ }) {
740
+ const [selectedIds, setSelectedIds] = useState5([]);
741
+ const allIds = useMemo2(() => items.map(keyExtractor), [items, keyExtractor]);
742
+ const isSelected = useCallback5(
743
+ (id) => selectedIds.includes(id),
744
+ [selectedIds]
745
+ );
746
+ const toggle = useCallback5(
747
+ (id) => {
748
+ setSelectedIds((prev) => {
749
+ if (prev.includes(id)) return prev.filter((s) => s !== id);
750
+ if (prev.length >= maxSelection) return prev;
751
+ return [...prev, id];
752
+ });
753
+ },
754
+ [maxSelection]
755
+ );
756
+ const toggleAll = useCallback5(() => {
757
+ setSelectedIds((prev) => {
758
+ const allSelected = prev.length === allIds.length && allIds.length > 0;
759
+ if (allSelected) return [];
760
+ return allIds.slice(0, maxSelection);
761
+ });
762
+ }, [allIds, maxSelection]);
763
+ const clearSelection = useCallback5(() => setSelectedIds([]), []);
764
+ const isAllSelected = allIds.length > 0 && selectedIds.length === allIds.length;
765
+ const isIndeterminate = selectedIds.length > 0 && selectedIds.length < allIds.length;
766
+ return {
767
+ selectedIds,
768
+ selectedCount: selectedIds.length,
769
+ isSelected,
770
+ isAllSelected,
771
+ isIndeterminate,
772
+ toggle,
773
+ toggleAll,
774
+ clearSelection,
775
+ setSelectedIds
776
+ };
777
+ }
778
+
779
+ // src/hooks/useUrlTable.ts
780
+ import { useRouter, usePathname, useSearchParams } from "next/navigation";
781
+ import { useCallback as useCallback6 } from "react";
782
+ var NON_RESETTING_KEYS = ["page", "pageSize", "view"];
783
+ function useUrlTable(options) {
784
+ var _a;
785
+ const router = useRouter();
786
+ const pathname = usePathname();
787
+ const searchParams = useSearchParams();
788
+ const defaults = (_a = options == null ? void 0 : options.defaults) != null ? _a : {};
789
+ const get = useCallback6(
790
+ (key) => {
791
+ var _a2, _b;
792
+ return (_b = (_a2 = searchParams.get(key)) != null ? _a2 : defaults[key]) != null ? _b : "";
793
+ },
794
+ [searchParams, defaults]
795
+ );
796
+ const getNumber = useCallback6(
797
+ (key, fallback = 0) => {
798
+ const v = get(key);
799
+ if (!v) return fallback;
800
+ const n = Number(v);
801
+ return isNaN(n) ? fallback : n;
802
+ },
803
+ [get]
804
+ );
805
+ const buildParams = useCallback6(
806
+ (updates) => {
807
+ const p = new URLSearchParams(searchParams.toString());
808
+ for (const [k, v] of Object.entries(updates)) {
809
+ if (v === "" || v === void 0) p.delete(k);
810
+ else p.set(k, v);
811
+ }
812
+ return p;
813
+ },
814
+ [searchParams]
815
+ );
816
+ const set = useCallback6(
817
+ (key, value) => {
818
+ const p = buildParams({ [key]: value });
819
+ if (!NON_RESETTING_KEYS.includes(key)) {
820
+ p.set("page", "1");
821
+ }
822
+ router.replace(`${pathname}?${p.toString()}`);
823
+ },
824
+ [buildParams, pathname, router]
825
+ );
826
+ const setMany = useCallback6(
827
+ (updates) => {
828
+ const p = buildParams(updates);
829
+ const keys = Object.keys(updates);
830
+ const allNonResetting = keys.every(
831
+ (k) => NON_RESETTING_KEYS.includes(k)
832
+ );
833
+ if (!allNonResetting && !keys.includes("page")) {
834
+ p.set("page", "1");
835
+ }
836
+ router.replace(`${pathname}?${p.toString()}`);
837
+ },
838
+ [buildParams, pathname, router]
839
+ );
840
+ const clear = useCallback6(
841
+ (keys) => {
842
+ if (keys) {
843
+ const p = new URLSearchParams(searchParams.toString());
844
+ keys.forEach((k) => p.delete(k));
845
+ p.set("page", "1");
846
+ router.replace(`${pathname}?${p.toString()}`);
847
+ } else {
848
+ router.replace(pathname);
849
+ }
850
+ },
851
+ [searchParams, pathname, router]
852
+ );
853
+ const setPage = (page) => set("page", String(page));
854
+ const setPageSize = (size) => set("pageSize", String(size));
855
+ const setSort = (sort) => set("sort", sort);
856
+ const buildSieveParams = useCallback6(
857
+ (sieveFilters) => {
858
+ const page = get("page") || "1";
859
+ const pageSize = get("pageSize") || defaults["pageSize"] || "25";
860
+ const sort = get("sort") || defaults["sort"] || "-createdAt";
861
+ const parts = new URLSearchParams();
862
+ if (sieveFilters) parts.set("filters", sieveFilters);
863
+ parts.set("sorts", sort);
864
+ parts.set("page", page);
865
+ parts.set("pageSize", pageSize);
866
+ return `?${parts.toString()}`;
867
+ },
868
+ [get, defaults]
869
+ );
870
+ const buildSearchParams = useCallback6(() => {
871
+ const p = new URLSearchParams();
872
+ const addIfPresent = (k) => {
873
+ const v = get(k);
874
+ if (v) p.set(k, v);
875
+ };
876
+ ["q", "category", "minPrice", "maxPrice"].forEach(addIfPresent);
877
+ p.set("sort", get("sort") || defaults["sort"] || "-createdAt");
878
+ p.set("page", get("page") || "1");
879
+ p.set("pageSize", get("pageSize") || defaults["pageSize"] || "24");
880
+ return `?${p.toString()}`;
881
+ }, [get, defaults]);
882
+ return {
883
+ params: searchParams,
884
+ get,
885
+ getNumber,
886
+ set,
887
+ setMany,
888
+ clear,
889
+ setPage,
890
+ setPageSize,
891
+ setSort,
892
+ buildSieveParams,
893
+ buildSearchParams
894
+ };
895
+ }
896
+
897
+ // src/hooks/usePendingFilters.ts
898
+ import { useCallback as useCallback7, useMemo as useMemo3, useState as useState6 } from "react";
899
+ function usePendingFilters({
900
+ table,
901
+ keys
902
+ }) {
903
+ const parseValues = useCallback7(
904
+ (key) => {
905
+ const raw = table.get(key);
906
+ return raw ? raw.split(",").filter(Boolean) : [];
907
+ },
908
+ [table]
909
+ );
910
+ const [pending, setPending] = useState6(
911
+ () => Object.fromEntries(keys.map((k) => [k, parseValues(k)]))
912
+ );
913
+ const applied = useMemo3(
914
+ () => Object.fromEntries(keys.map((k) => [k, parseValues(k)])),
915
+ [keys, parseValues]
916
+ );
917
+ const pendingCount = useMemo3(
918
+ () => Object.values(pending).reduce((sum, arr) => sum + arr.length, 0),
919
+ [pending]
920
+ );
921
+ const appliedCount = useMemo3(
922
+ () => Object.values(applied).reduce((sum, arr) => sum + arr.length, 0),
923
+ [applied]
924
+ );
925
+ const isDirty = useMemo3(() => {
926
+ return keys.some((k) => {
927
+ var _a, _b;
928
+ const p = ((_a = pending[k]) != null ? _a : []).slice().sort();
929
+ const a = ((_b = applied[k]) != null ? _b : []).slice().sort();
930
+ return p.length !== a.length || p.some((v, i) => v !== a[i]);
931
+ });
932
+ }, [keys, pending, applied]);
933
+ const set = useCallback7((key, values) => {
934
+ setPending((prev) => __spreadProps(__spreadValues({}, prev), { [key]: values }));
935
+ }, []);
936
+ const apply = useCallback7(() => {
937
+ var _a;
938
+ const updates = { page: "1" };
939
+ for (const k of keys) {
940
+ updates[k] = ((_a = pending[k]) != null ? _a : []).join(",");
941
+ }
942
+ table.setMany(updates);
943
+ }, [keys, pending, table]);
944
+ const reset = useCallback7(() => {
945
+ setPending(Object.fromEntries(keys.map((k) => [k, parseValues(k)])));
946
+ }, [keys, parseValues]);
947
+ const clear = useCallback7(() => {
948
+ const empty = Object.fromEntries(keys.map((k) => [k, []]));
949
+ setPending(empty);
950
+ const updates = { page: "1" };
951
+ for (const k of keys) updates[k] = "";
952
+ table.setMany(updates);
953
+ }, [keys, table]);
954
+ return {
955
+ pending,
956
+ applied,
957
+ isDirty,
958
+ pendingCount,
959
+ appliedCount,
960
+ set,
961
+ apply,
962
+ reset,
963
+ clear
964
+ };
965
+ }
966
+
967
+ // src/hooks/usePendingTable.ts
968
+ import { useMemo as useMemo4 } from "react";
969
+ function usePendingTable(table, keys) {
970
+ const filters = usePendingFilters({ table, keys });
971
+ const pendingTable = useMemo4(
972
+ () => ({
973
+ get: (key) => {
974
+ var _a, _b;
975
+ return (_b = (_a = filters.pending[key]) == null ? void 0 : _a[0]) != null ? _b : "";
976
+ },
977
+ set: (key, value) => {
978
+ filters.set(key, value ? [value] : []);
979
+ },
980
+ setMany: (updates) => {
981
+ for (const [k, v] of Object.entries(updates)) {
982
+ filters.set(k, v ? [v] : []);
983
+ }
984
+ }
985
+ }),
986
+ // eslint-disable-next-line react-hooks/exhaustive-deps
987
+ [filters.pending, filters.set]
988
+ );
989
+ return {
990
+ pendingTable,
991
+ filterActiveCount: filters.appliedCount,
992
+ onFilterApply: filters.apply,
993
+ onFilterClear: filters.clear
994
+ };
995
+ }
996
+
997
+ // src/hooks/useUnsavedChanges.ts
998
+ import { useState as useState7, useEffect as useEffect10, useCallback as useCallback8, useRef as useRef8 } from "react";
999
+ var UNSAVED_CHANGES_EVENT = "unsaved-changes:confirm";
1000
+ function useUnsavedChanges({
1001
+ formValues,
1002
+ initialValues,
1003
+ extraDirty = false,
1004
+ confirmFn,
1005
+ beforeUnloadWarning = "You have unsaved changes. Leave?"
1006
+ }) {
1007
+ const [savedSnapshot, setSavedSnapshot] = useState7(initialValues);
1008
+ const savedSnapshotRef = useRef8(savedSnapshot);
1009
+ savedSnapshotRef.current = savedSnapshot;
1010
+ useEffect10(() => {
1011
+ if (initialValues && !savedSnapshotRef.current) {
1012
+ setSavedSnapshot(initialValues);
1013
+ }
1014
+ }, [initialValues]);
1015
+ const isFormDirty = (() => {
1016
+ if (!savedSnapshot) return false;
1017
+ return Object.keys(formValues).some(
1018
+ (key) => {
1019
+ var _a, _b;
1020
+ return ((_a = formValues[key]) != null ? _a : "") !== ((_b = savedSnapshot[key]) != null ? _b : "");
1021
+ }
1022
+ );
1023
+ })();
1024
+ const isDirty = isFormDirty || extraDirty;
1025
+ useEffect10(() => {
1026
+ if (!isDirty) return;
1027
+ const handler = (e) => {
1028
+ e.preventDefault();
1029
+ e.returnValue = beforeUnloadWarning;
1030
+ return beforeUnloadWarning;
1031
+ };
1032
+ window.addEventListener("beforeunload", handler);
1033
+ return () => window.removeEventListener("beforeunload", handler);
1034
+ }, [isDirty, beforeUnloadWarning]);
1035
+ const markClean = useCallback8(() => {
1036
+ setSavedSnapshot(__spreadValues({}, formValues));
1037
+ }, [formValues]);
1038
+ const confirmLeave = useCallback8(() => {
1039
+ if (!isDirty) return Promise.resolve(true);
1040
+ if (confirmFn) return confirmFn();
1041
+ return Promise.resolve(
1042
+ window.confirm("You have unsaved changes. Leave without saving?")
1043
+ );
1044
+ }, [isDirty, confirmFn]);
1045
+ return { isDirty, isFormDirty, markClean, confirmLeave };
1046
+ }
1047
+
1048
+ // src/hooks/useBulkAction.ts
1049
+ import { useState as useState8, useCallback as useCallback9, useRef as useRef9 } from "react";
1050
+ function useBulkAction(options) {
1051
+ const [isLoading, setIsLoading] = useState8(false);
1052
+ const [result, setResult] = useState8(null);
1053
+ const [error, setError] = useState8(null);
1054
+ const [pendingPayload, setPendingPayload] = useState8(null);
1055
+ const optionsRef = useRef9(options);
1056
+ optionsRef.current = options;
1057
+ const runMutation = useCallback9(async (payload) => {
1058
+ var _a, _b, _c, _d, _e;
1059
+ setIsLoading(true);
1060
+ setError(null);
1061
+ try {
1062
+ const res = await optionsRef.current.mutationFn(payload);
1063
+ setResult(res);
1064
+ await ((_b = (_a = optionsRef.current).onSuccess) == null ? void 0 : _b.call(_a, res, payload));
1065
+ } catch (err) {
1066
+ const error2 = err instanceof Error ? err : new Error((_c = err == null ? void 0 : err.message) != null ? _c : "Unexpected error");
1067
+ setError(error2);
1068
+ (_e = (_d = optionsRef.current).onError) == null ? void 0 : _e.call(_d, error2, payload);
1069
+ } finally {
1070
+ setIsLoading(false);
1071
+ }
1072
+ }, []);
1073
+ const execute = useCallback9(
1074
+ async (payload) => {
1075
+ if (optionsRef.current.requiresConfirm) {
1076
+ setPendingPayload(payload);
1077
+ return;
1078
+ }
1079
+ await runMutation(payload);
1080
+ },
1081
+ [runMutation]
1082
+ );
1083
+ const confirmAndExecute = useCallback9(async () => {
1084
+ if (!pendingPayload) return;
1085
+ const payload = pendingPayload;
1086
+ setPendingPayload(null);
1087
+ await runMutation(payload);
1088
+ }, [pendingPayload, runMutation]);
1089
+ const cancelConfirm = useCallback9(() => setPendingPayload(null), []);
1090
+ const reset = useCallback9(() => {
1091
+ setResult(null);
1092
+ setError(null);
1093
+ setPendingPayload(null);
1094
+ }, []);
1095
+ return {
1096
+ execute,
1097
+ isLoading,
1098
+ result,
1099
+ error,
1100
+ reset,
1101
+ pendingPayload,
1102
+ confirmAndExecute,
1103
+ cancelConfirm
1104
+ };
1105
+ }
711
1106
  export {
1107
+ UNSAVED_CHANGES_EVENT,
712
1108
  useBreakpoint,
1109
+ useBulkAction,
1110
+ useBulkSelection,
713
1111
  useCamera,
714
1112
  useClickOutside,
715
1113
  useCountdown,
@@ -717,6 +1115,10 @@ export {
717
1115
  useKeyPress,
718
1116
  useLongPress,
719
1117
  useMediaQuery,
1118
+ usePendingFilters,
1119
+ usePendingTable,
720
1120
  usePullToRefresh,
721
- useSwipe
1121
+ useSwipe,
1122
+ useUnsavedChanges,
1123
+ useUrlTable
722
1124
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/react",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -25,7 +25,8 @@
25
25
  },
26
26
  "peerDependencies": {
27
27
  "react": ">=18",
28
- "react-dom": ">=18"
28
+ "react-dom": ">=18",
29
+ "next": ">=14"
29
30
  },
30
31
  "devDependencies": {
31
32
  "tsup": "^8.5.0",