@7span/react-list 0.0.6 → 1.0.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.
@@ -0,0 +1,59 @@
1
+ import { memo, useEffect, useRef, useState } from "react";
2
+ import { useListContext } from "../context/list-provider";
3
+
4
+ export const ReactListSearch = memo(({ children, debounceTime = 500 }) => {
5
+ const { listState } = useListContext();
6
+ const { search, setSearch } = listState;
7
+ const [localSearch, setLocalSearch] = useState(search ?? "");
8
+ const debounceTimerRef = useRef(null);
9
+
10
+ // Sync local state with context when search prop changes
11
+ useEffect(() => {
12
+ if (search !== localSearch) {
13
+ setLocalSearch(search ?? "");
14
+ }
15
+ }, [search]);
16
+
17
+ const handleChange = (value) => {
18
+ setLocalSearch(value);
19
+
20
+ // Clear any existing timer
21
+ if (debounceTimerRef.current) {
22
+ clearTimeout(debounceTimerRef.current);
23
+ }
24
+
25
+ // Set a new timer
26
+ debounceTimerRef.current = setTimeout(() => {
27
+ setSearch(value);
28
+ }, debounceTime);
29
+ };
30
+
31
+ // Clean up timer on unmount
32
+ useEffect(() => {
33
+ return () => {
34
+ if (debounceTimerRef.current) {
35
+ clearTimeout(debounceTimerRef.current);
36
+ }
37
+ };
38
+ }, []);
39
+
40
+ const scope = {
41
+ search: localSearch,
42
+ setSearch: handleChange,
43
+ };
44
+
45
+ return (
46
+ <div className="react-list-search">
47
+ {children ? (
48
+ children(scope)
49
+ ) : (
50
+ <input
51
+ type="text"
52
+ value={localSearch}
53
+ onChange={(e) => handleChange(e.target.value)}
54
+ placeholder="Search..."
55
+ />
56
+ )}
57
+ </div>
58
+ );
59
+ });
@@ -0,0 +1,51 @@
1
+ import { memo, useMemo } from "react";
2
+ import { useListContext } from "../context/list-provider";
3
+
4
+ export const ReactListSummary = memo(({ children }) => {
5
+ const { listState } = useListContext();
6
+ const { data, count, pagination, loader, error } = listState;
7
+ const { page, perPage } = pagination;
8
+ const { initialLoading, isLoading } = loader;
9
+
10
+ const summaryData = useMemo(() => {
11
+ const from = page * perPage - perPage + 1;
12
+ const to = Math.min(page * perPage, count);
13
+ const visibleCount = data?.length || 0;
14
+
15
+ return { from, to, visibleCount };
16
+ }, [page, perPage, count, data]);
17
+
18
+ const scope = useMemo(
19
+ () => ({
20
+ ...summaryData,
21
+ count,
22
+ }),
23
+ [summaryData, count]
24
+ );
25
+
26
+ if (initialLoading) return null;
27
+
28
+ if (!data || data.length === 0) {
29
+ return null;
30
+ }
31
+
32
+ if (error) {
33
+ return null;
34
+ }
35
+
36
+ return (
37
+ <div className="react-list-summary">
38
+ {children ? (
39
+ children(scope)
40
+ ) : (
41
+ <span>
42
+ Showing <span>{summaryData.visibleCount}</span> items (
43
+ <span>
44
+ {summaryData.from} - {summaryData.to}
45
+ </span>
46
+ ) out of <span>{count}</span>
47
+ </span>
48
+ )}
49
+ </div>
50
+ );
51
+ });
@@ -0,0 +1,42 @@
1
+ export const deepEqual = (obj1, obj2) => {
2
+ if (obj1 === obj2) return true;
3
+
4
+ if (obj1 == null || obj2 == null) return obj1 === obj2;
5
+
6
+ if (typeof obj1 !== "object" || typeof obj2 !== "object")
7
+ return obj1 === obj2;
8
+
9
+ if (Array.isArray(obj1) && Array.isArray(obj2)) {
10
+ if (obj1.length !== obj2.length) return false;
11
+ for (let i = 0; i < obj1.length; i++) {
12
+ if (!deepEqual(obj1[i], obj2[i])) return false;
13
+ }
14
+ return true;
15
+ }
16
+
17
+ if (Array.isArray(obj1) || Array.isArray(obj2)) return false;
18
+
19
+ const keys1 = Object.keys(obj1).filter((key) => obj1[key] !== undefined);
20
+ const keys2 = Object.keys(obj2).filter((key) => obj2[key] !== undefined);
21
+
22
+ if (keys1.length !== keys2.length) return false;
23
+
24
+ for (let key of keys1) {
25
+ if (!keys2.includes(key)) return false;
26
+ if (!deepEqual(obj1[key], obj2[key])) return false;
27
+ }
28
+
29
+ return true;
30
+ };
31
+
32
+ export const hasActiveFilters = (currentFilters, initialFilters) => {
33
+ if (!initialFilters || Object.keys(initialFilters).length === 0) {
34
+ return currentFilters && Object.keys(currentFilters).length > 0;
35
+ }
36
+
37
+ if (!currentFilters || Object.keys(currentFilters).length === 0) {
38
+ return false;
39
+ }
40
+
41
+ return !deepEqual(currentFilters, initialFilters);
42
+ };
@@ -0,0 +1,55 @@
1
+ import { createContext, useContext, useMemo, useState } from "react";
2
+
3
+ const ListContext = createContext(null);
4
+
5
+ export const ReactListProvider = ({ children, config }) => {
6
+ const { requestHandler, stateManager = {} } = config;
7
+ const [listState, setListState] = useState({
8
+ data: [],
9
+ response: null,
10
+ error: null,
11
+ count: 0,
12
+ selection: [],
13
+ pagination: {
14
+ page: 1,
15
+ perPage: 25,
16
+ },
17
+ loader: {
18
+ isLoading: false,
19
+ initialLoading: true,
20
+ },
21
+ sort: {
22
+ sortBy: null,
23
+ sortOrder: "desc",
24
+ },
25
+ search: "",
26
+ filters: {},
27
+ attrs: [],
28
+ isEmpty: true,
29
+ isInitializing: true,
30
+ });
31
+
32
+ if (!requestHandler) {
33
+ throw new Error("ListProvider: requestHandler is required.");
34
+ }
35
+
36
+ const value = useMemo(
37
+ () => ({
38
+ requestHandler,
39
+ stateManager,
40
+ listState,
41
+ setListState,
42
+ }),
43
+ [requestHandler, stateManager, listState]
44
+ );
45
+
46
+ return <ListContext.Provider value={value}>{children}</ListContext.Provider>;
47
+ };
48
+
49
+ export const useListContext = () => {
50
+ const context = useContext(ListContext);
51
+ if (!context) {
52
+ throw new Error("useListContext must be used within a ListProvider");
53
+ }
54
+ return context;
55
+ };
package/src/index.js ADDED
@@ -0,0 +1,15 @@
1
+ export { ReactListAttributes } from "./components/attributes";
2
+ export { ReactListEmpty } from "./components/empty";
3
+ export { ReactListError } from "./components/error";
4
+ export { ReactListGoTo } from "./components/go-to";
5
+ export { ReactListInitialLoader } from "./components/initial-loader";
6
+ export { ReactListItems } from "./components/items";
7
+ export { default } from "./components/list";
8
+ export { ReactListLoadMore } from "./components/load-more";
9
+ export { ReactListLoader } from "./components/loader";
10
+ export { ReactListPagination } from "./components/pagination";
11
+ export { ReactListPerPage } from "./components/per-page";
12
+ export { ReactListRefresh } from "./components/refresh";
13
+ export { ReactListSearch } from "./components/search";
14
+ export { ReactListSummary } from "./components/summary";
15
+ export { ReactListProvider } from "./context/list-provider";