gbs-add-block 1.0.13 → 1.0.15-beta

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,135 @@
1
+ import { useState, useCallback } from "react";
2
+ import {
3
+ clearFilterHelper,
4
+ handleApplyFilterHelper,
5
+ } from "@grampro/headless-helpers";
6
+
7
+ interface UseFilteringProps {
8
+ columns: any[];
9
+ workingColumns: any[];
10
+ setWorkingColumns: (columns: any[]) => void;
11
+ workingDataSource: any[];
12
+ setWorkingDataSource: (data: any[]) => void;
13
+ dataSource: any;
14
+ fallbackSourceData: any[];
15
+ resetPage: (data: any[], settings: any) => number;
16
+ setTotalPages: (pages: number) => void;
17
+ pageSettings: any;
18
+ activeFilterArrayValue?: (filters: any[]) => void;
19
+ }
20
+
21
+ export const useFiltering = ({
22
+ columns,
23
+ workingColumns,
24
+ setWorkingColumns,
25
+ workingDataSource,
26
+ setWorkingDataSource,
27
+ dataSource,
28
+ fallbackSourceData,
29
+ resetPage,
30
+ setTotalPages,
31
+ pageSettings,
32
+ activeFilterArrayValue,
33
+ }: UseFilteringProps) => {
34
+ const [activeFilterArray, setActiveFilterArray] = useState<any>([]);
35
+
36
+ const toggleFilterPopup = useCallback(
37
+ (index: number) => {
38
+ setWorkingColumns(
39
+ workingColumns.map((column: any, i: any) =>
40
+ i === index
41
+ ? { ...column, showFilterPopup: !column.showFilterPopup }
42
+ : column
43
+ )
44
+ );
45
+ },
46
+ [workingColumns, setWorkingColumns]
47
+ );
48
+
49
+ const handleApplyFilter = useCallback(
50
+ (event: any) => {
51
+ const {
52
+ columns: updatedColumns,
53
+ workingDataSource: updatedFullDataSource,
54
+ activeFilterArray: updatedActiveFilterArray,
55
+ } = handleApplyFilterHelper(event, columns, workingDataSource);
56
+
57
+ setWorkingColumns(updatedColumns);
58
+ setWorkingDataSource(updatedFullDataSource);
59
+ setActiveFilterArray(updatedActiveFilterArray);
60
+ if (activeFilterArrayValue)
61
+ activeFilterArrayValue(updatedActiveFilterArray);
62
+ const totalPages = resetPage(updatedFullDataSource, pageSettings);
63
+ setTotalPages(totalPages);
64
+ },
65
+ [
66
+ columns,
67
+ workingDataSource,
68
+ setWorkingColumns,
69
+ setWorkingDataSource,
70
+ activeFilterArrayValue,
71
+ resetPage,
72
+ pageSettings,
73
+ setTotalPages,
74
+ ]
75
+ );
76
+
77
+ const clearFilter = useCallback(
78
+ (event: any) => {
79
+ const clearDataSource = Array.isArray(dataSource)
80
+ ? dataSource
81
+ : fallbackSourceData;
82
+ const {
83
+ columns: updatedColumns,
84
+ workingDataSource: updatedDataSource,
85
+ activeFilterArray: updatedActiveFilterArray,
86
+ } = clearFilterHelper(event, workingColumns, clearDataSource);
87
+
88
+ setWorkingColumns(updatedColumns);
89
+ setWorkingDataSource(updatedDataSource);
90
+ setActiveFilterArray(updatedActiveFilterArray);
91
+ if (activeFilterArrayValue)
92
+ activeFilterArrayValue(updatedActiveFilterArray);
93
+ const totalPages = resetPage(updatedDataSource, pageSettings);
94
+ setTotalPages(totalPages);
95
+ },
96
+ [
97
+ dataSource,
98
+ fallbackSourceData,
99
+ workingColumns,
100
+ setWorkingColumns,
101
+ setWorkingDataSource,
102
+ activeFilterArrayValue,
103
+ resetPage,
104
+ pageSettings,
105
+ setTotalPages,
106
+ ]
107
+ );
108
+
109
+ const handleFilterAction = useCallback(
110
+ (action: any, colIndex: number) => {
111
+ switch (action.type) {
112
+ case "cancel":
113
+ toggleFilterPopup(colIndex);
114
+ break;
115
+ case "applyFilter":
116
+ handleApplyFilter(action);
117
+ break;
118
+ case "clearFilter":
119
+ clearFilter(action);
120
+ break;
121
+ default:
122
+ break;
123
+ }
124
+ },
125
+ [toggleFilterPopup, handleApplyFilter, clearFilter]
126
+ );
127
+
128
+ return {
129
+ activeFilterArray,
130
+ toggleFilterPopup,
131
+ handleApplyFilter,
132
+ clearFilter,
133
+ handleFilterAction,
134
+ };
135
+ };
@@ -0,0 +1,13 @@
1
+ import { useEffect } from "react";
2
+
3
+ export const usePageStatus = (
4
+ currentPage: number,
5
+ totalPages: number,
6
+ pageStatus?: (status: any) => void
7
+ ) => {
8
+ useEffect(() => {
9
+ if (pageStatus) {
10
+ pageStatus({ currentPage, totalPages });
11
+ }
12
+ }, [pageStatus, currentPage, totalPages]);
13
+ };
@@ -0,0 +1,125 @@
1
+ import { useState, useCallback, useEffect } from "react";
2
+
3
+ export const usePagination = (
4
+ totalPages: number,
5
+ lazy: boolean,
6
+ dataSource?: any[],
7
+ pageSettings?: { pageNumber: number }
8
+ ) => {
9
+ const [currentPage, setCurrentPage] = useState(0);
10
+ const [pageStart, setPageStart] = useState(0);
11
+ const [pageEnd, setPageEnd] = useState(10);
12
+ const [prevDataLength, setPrevDataLength] = useState(0);
13
+
14
+ const updatePageRange = useCallback(
15
+ (page: number) => {
16
+ const start = Math.floor(page / 10) * 10;
17
+ setPageStart(start);
18
+ setPageEnd(Math.min(start + 10, totalPages));
19
+ },
20
+ [totalPages]
21
+ );
22
+
23
+ useEffect(() => {
24
+ if (!lazy) {
25
+ setPageEnd(Math.min(10, totalPages));
26
+ } else {
27
+ setPageEnd(Math.min(pageStart + 10, totalPages));
28
+ }
29
+ }, [totalPages, lazy, pageStart]);
30
+
31
+ // This will handle the case when dataSource changes
32
+ // and adjust the current page accordingly
33
+ useEffect(() => {
34
+ if (dataSource && prevDataLength > 0) {
35
+ const currentDataLength = dataSource.length;
36
+
37
+ if (currentDataLength > prevDataLength) {
38
+ const lastPage = totalPages - 1;
39
+ setCurrentPage(lastPage);
40
+ updatePageRange(lastPage);
41
+ } else if (currentDataLength < prevDataLength) {
42
+ const rowsPerPage = lazy ? 1 : pageSettings?.pageNumber || 10;
43
+ const startIndex = currentPage * rowsPerPage;
44
+
45
+ if (startIndex >= currentDataLength && currentPage > 0) {
46
+ const newPage = currentPage - 1;
47
+ setCurrentPage(newPage);
48
+ updatePageRange(newPage);
49
+ }
50
+ }
51
+ }
52
+ setPrevDataLength(dataSource?.length || 0);
53
+ }, [
54
+ dataSource?.length,
55
+ totalPages,
56
+ updatePageRange,
57
+ prevDataLength,
58
+ currentPage,
59
+ lazy,
60
+ ]);
61
+
62
+ const nextPage = useCallback(() => {
63
+ setCurrentPage((prevPage) => {
64
+ if (prevPage < totalPages - 1) {
65
+ const newPage = prevPage + 1;
66
+ updatePageRange(newPage);
67
+ return newPage;
68
+ }
69
+ return prevPage;
70
+ });
71
+ }, [totalPages, updatePageRange]);
72
+
73
+ const prevPage = useCallback(() => {
74
+ setCurrentPage((prevPage) => {
75
+ if (prevPage > 0) {
76
+ const newPage = prevPage - 1;
77
+ updatePageRange(newPage);
78
+ return newPage;
79
+ }
80
+ return prevPage;
81
+ });
82
+ }, [updatePageRange]);
83
+
84
+ const goToEndPage = useCallback(() => {
85
+ const lastPage = totalPages - 1;
86
+ setCurrentPage(lastPage);
87
+ updatePageRange(lastPage);
88
+ }, [totalPages, updatePageRange]);
89
+
90
+ const goToFirstPage = useCallback(() => {
91
+ setCurrentPage(0);
92
+ updatePageRange(0);
93
+ }, [updatePageRange]);
94
+
95
+ const goToPage = useCallback(
96
+ (page: number) => {
97
+ setCurrentPage(page);
98
+ updatePageRange(page);
99
+ },
100
+ [updatePageRange]
101
+ );
102
+
103
+ const resetPage = useCallback((dataSource: any, pageSettings: any) => {
104
+ const newTotalPages = Math.ceil(
105
+ dataSource.length / pageSettings.pageNumber
106
+ );
107
+ setCurrentPage(0);
108
+ setPageStart(0);
109
+ setPageEnd(Math.min(10, newTotalPages));
110
+ return newTotalPages;
111
+ }, []);
112
+
113
+ return {
114
+ currentPage,
115
+ pageStart,
116
+ pageEnd,
117
+ nextPage,
118
+ prevPage,
119
+ goToEndPage,
120
+ goToFirstPage,
121
+ goToPage,
122
+ resetPage,
123
+ updatePageRange,
124
+ };
125
+ };
@@ -0,0 +1,48 @@
1
+ import { useState, useCallback } from "react";
2
+
3
+ export const useRowSelection = (
4
+ workingDataSource: any[],
5
+ onSelectRow?: (rows: any[]) => void
6
+ ) => {
7
+ const [selectedRows, setSelectedRows] = useState<any[]>([]);
8
+
9
+ const handleSelectAll = useCallback(
10
+ (event: React.ChangeEvent<HTMLInputElement>) => {
11
+ const selected = event.target.checked ? workingDataSource : [];
12
+ setSelectedRows(selected);
13
+ onSelectRow?.(selected);
14
+ },
15
+ [workingDataSource, onSelectRow]
16
+ );
17
+
18
+ const handleSelect = useCallback(
19
+ (rowData: any) => {
20
+ setSelectedRows((prevData) => {
21
+ const isSelected = prevData.includes(rowData);
22
+ let updatedData;
23
+ if (isSelected) {
24
+ updatedData = prevData.filter((item) => item !== rowData);
25
+ } else {
26
+ updatedData = [...prevData, rowData];
27
+ }
28
+ if (onSelectRow) onSelectRow(updatedData);
29
+ return updatedData;
30
+ });
31
+ },
32
+ [onSelectRow]
33
+ );
34
+
35
+ const isRowSelected = useCallback(
36
+ (rowData: any) => {
37
+ return selectedRows.includes(rowData);
38
+ },
39
+ [selectedRows]
40
+ );
41
+
42
+ return {
43
+ selectedRows,
44
+ handleSelectAll,
45
+ handleSelect,
46
+ isRowSelected,
47
+ };
48
+ };
@@ -0,0 +1,86 @@
1
+ import { useState, useCallback } from "react";
2
+
3
+ interface UseSearchProps {
4
+ workingDataSource: any[];
5
+ setWorkingDataSource: (data: any[]) => void;
6
+ fallbackSourceData: any[];
7
+ dataSource: any;
8
+ lazy: boolean;
9
+ pageSettings: any;
10
+ resetPage: (data: any[], settings: any) => number;
11
+ setTotalPages: (pages: number) => void;
12
+ searchParamValue?: (value: string) => void;
13
+ }
14
+
15
+ export const useSearch = ({
16
+ workingDataSource,
17
+ setWorkingDataSource,
18
+ fallbackSourceData,
19
+ dataSource,
20
+ lazy,
21
+ pageSettings,
22
+ resetPage,
23
+ setTotalPages,
24
+ searchParamValue,
25
+ }: UseSearchProps) => {
26
+ const [searchParam, setSearchParam] = useState("");
27
+
28
+ const handleSearchInput = useCallback(
29
+ (e: any) => {
30
+ const searchValue = e.target.value;
31
+ if (searchParamValue) searchParamValue(searchValue);
32
+ setSearchParam(searchValue);
33
+
34
+ if (searchValue === "") {
35
+ if (!lazy) {
36
+ const fallback =
37
+ fallbackSourceData.length > 0 ? fallbackSourceData : dataSource;
38
+ setWorkingDataSource(fallback);
39
+ const totalPages = resetPage(fallback, pageSettings);
40
+ setTotalPages(totalPages);
41
+ }
42
+ }
43
+ },
44
+ [
45
+ searchParamValue,
46
+ lazy,
47
+ fallbackSourceData,
48
+ dataSource,
49
+ setWorkingDataSource,
50
+ resetPage,
51
+ pageSettings,
52
+ setTotalPages,
53
+ ]
54
+ );
55
+
56
+ const handleSearch = useCallback(
57
+ (searchParam: string) => {
58
+ if (!lazy) {
59
+ const filteredData = workingDataSource.filter((item: any) =>
60
+ Object.values(item).some((val: any) => {
61
+ const trimmedVal = val.toString().toLowerCase().trim();
62
+ const trimmedSearchParam = searchParam.toLowerCase().trim();
63
+ return trimmedVal.includes(trimmedSearchParam);
64
+ })
65
+ );
66
+ setWorkingDataSource(filteredData);
67
+ const totalPages = resetPage(filteredData, pageSettings);
68
+ setTotalPages(totalPages);
69
+ }
70
+ },
71
+ [
72
+ lazy,
73
+ workingDataSource,
74
+ setWorkingDataSource,
75
+ resetPage,
76
+ pageSettings,
77
+ setTotalPages,
78
+ ]
79
+ );
80
+
81
+ return {
82
+ searchParam,
83
+ handleSearchInput,
84
+ handleSearch,
85
+ };
86
+ };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Copyright (c) Grampro Business Services and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ * Here We Will Re-Render Grid as a Memoized Component For Better Performance.
7
+ * Extended Documentation for Grid can be found at
8
+ * https://psychedelic-step-e70.notion.site/Data-GRID-by-GBS-R-D-20ff97c899d24bc590215a6196435fa3
9
+ */
10
+
11
+ import { GridMemoised as GridComponent } from "./layout";
12
+ import { memo } from "react";
13
+
14
+ const DataGrid = memo(GridComponent, (prevProps, nextProps) => {
15
+ // Custom comparison function
16
+ return (
17
+ prevProps.dataSource === nextProps.dataSource &&
18
+ prevProps.columns === nextProps.columns &&
19
+ prevProps.pageSettings.pageNumber === nextProps.pageSettings.pageNumber &&
20
+ prevProps.enableSearch === nextProps.enableSearch &&
21
+ prevProps.enablePdfExport === nextProps.enablePdfExport &&
22
+ prevProps.enableExcelExport === nextProps.enableExcelExport
23
+ );
24
+ });
25
+
26
+ export { DataGrid };
@@ -0,0 +1,85 @@
1
+ import React from "react";
2
+
3
+ interface EditableRowProps {
4
+ columnType: string | undefined;
5
+ cellValue: any;
6
+ rowIndex: number;
7
+ field: string;
8
+ style?: React.CSSProperties;
9
+ rowData: any;
10
+ }
11
+
12
+ export default function EditableRow({
13
+ columnType,
14
+ cellValue,
15
+ rowIndex,
16
+ field,
17
+ style,
18
+ rowData,
19
+ }: EditableRowProps) {
20
+ // Function to handle cell value change
21
+ const handleCellChange = (newValue: any, field: string) => {
22
+ if (rowData) {
23
+ rowData[field] = newValue;
24
+ rowData(rowData);
25
+ console.log(rowData);
26
+ }
27
+ };
28
+
29
+ switch (columnType) {
30
+ case "text":
31
+ return (
32
+ <input
33
+ key={`${field}-${rowIndex}`}
34
+ id={`${field}-${rowIndex}`}
35
+ type="text"
36
+ defaultValue={cellValue || ""}
37
+ onBlur={(e) => handleCellChange(e.target.value, field)}
38
+ onKeyDown={(e) => {
39
+ if (e.key === "Enter") {
40
+ e.currentTarget.blur();
41
+ }
42
+ }}
43
+ className="px-2 py-1 outline-none focus:ring-1 focus:ring-black rounded"
44
+ style={style}
45
+ />
46
+ );
47
+
48
+ case "number":
49
+ return (
50
+ <input
51
+ key={`${field}-${rowIndex}`}
52
+ id={`${field}-${rowIndex}`}
53
+ type="number"
54
+ defaultValue={cellValue || ""}
55
+ onBlur={(e) => handleCellChange(e.target.value, field)}
56
+ onKeyDown={(e) => {
57
+ if (e.key === "Enter") {
58
+ e.currentTarget.blur();
59
+ }
60
+ }}
61
+ className="px-2 py-1 outline-none focus:ring-1 focus:ring-black rounded"
62
+ style={style}
63
+ />
64
+ );
65
+
66
+ default:
67
+ return (
68
+ <input
69
+ type="text"
70
+ defaultValue={cellValue || ""}
71
+ onBlur={(e) => handleCellChange(e.target.value, field)}
72
+ onKeyDown={(e) => {
73
+ if (e.key === "Enter") {
74
+ e.currentTarget.blur();
75
+ }
76
+ }}
77
+ style={{
78
+ width: "100%",
79
+ border: "none",
80
+ background: "transparent",
81
+ }}
82
+ />
83
+ );
84
+ }
85
+ }
@@ -0,0 +1,85 @@
1
+ import React from "react";
2
+ import { useState } from "react";
3
+
4
+ export default function FilterPopup({ show, columnHeader, filterAction }: any) {
5
+ const [filterValue, setFilterValue] = useState("");
6
+ const [filterType, setFilterType] = useState("contains");
7
+ const [isFilterActive, setIsFilterActive] = useState(false);
8
+
9
+ const handleFilterInput = (e: any) => {
10
+ const filterKeyword = e.target.value;
11
+ setFilterValue(filterKeyword);
12
+ };
13
+
14
+ const onCancel = () => {
15
+ filterAction({ type: "cancel" });
16
+ };
17
+
18
+ const applyFilter = () => {
19
+ filterAction({
20
+ type: "applyFilter",
21
+ filterValue,
22
+ filterType,
23
+ columnHeader,
24
+ });
25
+ show = false;
26
+ setIsFilterActive(true);
27
+ };
28
+
29
+ const clearFilter = () => {
30
+ setIsFilterActive(false);
31
+ filterAction({ type: "clearFilter", columnHeader });
32
+ };
33
+
34
+ return (
35
+ show && (
36
+ <div
37
+ className="absolute bg-gray-100 p-2 text-xs z-50 mt-48 flex flex-col shadow-lg gap-2 rounded-md dark:bg-zinc-700"
38
+ role="dialog"
39
+ >
40
+ <select
41
+ name={`${columnHeader}-filter`}
42
+ id={`${columnHeader}-filter-id`}
43
+ className="w-full p-2 rounded-lg bg-gray-200 outline-none dark:bg-gray-600"
44
+ value={filterType}
45
+ onChange={(e: any) => {
46
+ setFilterType(e.target.value);
47
+ }}
48
+ >
49
+ <option value="contains">Contains</option>
50
+ <option value="starts_with">Starts With</option>
51
+ <option value="ends_with">Ends With</option>
52
+ </select>
53
+ <input
54
+ type="text"
55
+ placeholder="Enter filter value"
56
+ className="rounded-lg p-2 text-xs bg-gray-200 outline-none dark:bg-gray-600"
57
+ value={filterValue}
58
+ onInput={handleFilterInput}
59
+ />
60
+ <div className="mt-2 flex space-x-1">
61
+ <button
62
+ className="text-xs bg-red-600 text-white px-2 py-0.5 rounded-lg hover:bg-red-600"
63
+ onClick={onCancel}
64
+ >
65
+ Cancel
66
+ </button>
67
+ {isFilterActive && (
68
+ <button
69
+ className="text-xs bg-black text-white px-2 py-0.5 rounded-lg dark:bg-white dark:text-black"
70
+ onClick={clearFilter}
71
+ >
72
+ Clear Filter
73
+ </button>
74
+ )}
75
+ <button
76
+ className="text-xs bg-black text-white px-2 py-0.5 rounded-lg dark:bg-white dark:text-black"
77
+ onClick={applyFilter}
78
+ >
79
+ Apply Filter
80
+ </button>
81
+ </div>
82
+ </div>
83
+ )
84
+ );
85
+ }