gbs-add-block 0.0.100 → 1.0.1

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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # GBS Building Blocks 2.0 (v0.0.100)
1
+ # GBS Building Blocks 2.0 (v1.0.1)
2
2
 
3
3
  Latest and upgraded version of GBS building blocks with headless UI and removed dependencies.
4
4
 
@@ -6,9 +6,13 @@ Latest and upgraded version of GBS building blocks with headless UI and removed
6
6
 
7
7
  For detailed documentation on usage and props, Please visit: [Building Block Documentation v2.0](https://blackmax-designs.gitbook.io/building-block-v2.0)
8
8
 
9
- ## What's New 🎉 (Ver 0.0.100)
9
+ ## What's New 🎉 (Ver 1.0.1)
10
10
 
11
- - Updated for bug fixes.
11
+ - Major Update with more stable features and Data Grid.
12
+ - Deprecated Grid Component, Use New Data Grid instead.
13
+ - More UI/UX Optimization.
14
+ - Animation improvements.
15
+ - Custom Components in SideBar
12
16
 
13
17
  ## Authors
14
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gbs-add-block",
3
- "version": "0.0.100",
3
+ "version": "1.0.1",
4
4
  "description": "React Component Library",
5
5
  "type": "module",
6
6
  "files": [
@@ -1,73 +1,14 @@
1
- import React, {
2
- createContext,
3
- useContext,
4
- useState,
5
- useCallback,
6
- useEffect,
7
- } from "react";
8
- import { getSourceData } from "../../utils";
1
+ import React, { createContext, useContext } from "react";
2
+ import type { GridContextType, GridProps } from "../type";
9
3
  import {
10
- clearFilterHelper,
11
- handleApplyFilterHelper,
12
- } from "@grampro/headless-helpers";
13
- import type { GridProps } from "../type";
14
-
15
- interface GridContextType {
16
- workingDataSource: any[];
17
- fallbackSourceData: any[];
18
- workingColumns: any[];
19
- currentPage: number;
20
- pageStart: number;
21
- pageEnd: number;
22
- totalPages: number;
23
- searchParam: string;
24
- activeFilterArray: any[];
25
- selectedRows: any[];
26
- isFetching: boolean | undefined;
27
-
28
- // Navigation methods
29
- nextPage: () => void;
30
- prevPage: () => void;
31
- goToEndPage: () => void;
32
- goToFirstPage: () => void;
33
- goToPage: (page: number) => void;
34
- lazy: boolean;
35
-
36
- // Search methods
37
- handleSearchInput: (e: any) => void;
38
- handleSearch: (searchParam: string) => void;
39
-
40
- // Filter methods
41
- toggleFilterPopup: (index: number) => void;
42
- handleApplyFilter: (event: any) => void;
43
- clearFilter: (event: any) => void;
44
- handleFilterAction: (action: any, colIndex: number) => void;
45
-
46
- // Row selection methods
47
- handleSelectAll: (event: React.ChangeEvent<HTMLInputElement>) => void;
48
- handleSelect: (rowData: any) => void;
49
- isRowSelected: (rowData: any) => boolean;
50
-
51
- // Grid settings
52
- columns: any[];
53
- pageSettings: any;
54
- enableSearch: boolean;
55
- enableExcelExport: boolean;
56
- enablePdfExport: boolean;
57
- excelName: string;
58
- pdfName: string;
59
- pdfOptions: any;
60
- gridButtonClass: string;
61
- selectAll: boolean;
62
- tableHeaderStyle: string;
63
- gridContainerClass: string;
64
- gridColumnStyleSelectAll: string;
65
- gridColumnStyle: string;
66
- rowChange: (rowData: any) => void;
67
- showTotalPages?: boolean;
68
- onSearch?: (searchParam: string) => void;
69
- onToolbarButtonClick?: (action: string) => void;
70
- }
4
+ useDataSource,
5
+ useColumns,
6
+ usePagination,
7
+ useSearch,
8
+ useFiltering,
9
+ useRowSelection,
10
+ usePageStatus,
11
+ } from "../hooks";
71
12
 
72
13
  const GridContext = createContext<GridContextType | undefined>(undefined);
73
14
 
@@ -103,279 +44,76 @@ export const GridProvider: React.FC<{
103
44
  onToolbarButtonClick = () => {},
104
45
  } = props;
105
46
 
106
- // States Handling Grid
107
- const [workingDataSource, setWorkingDataSource] = useState<any>([]);
108
- const [fallbackSourceData, setFallbackSourceData] = useState([]);
109
- const [workingColumns, setWorkingColumns] = useState<any>([]);
110
- const [currentPage, setCurrentPage] = useState(0);
111
- const [pageStart, setPageStart] = useState(0);
112
- const [pageEnd, setPageEnd] = useState(10);
113
- const [totalPages, setTotalPages] = useState(0);
114
- const [searchParam, setSearchParam] = useState("");
115
- const [activeFilterArray, setActiveFilterArray] = useState<any>([]);
116
- const [selectedRows, setSelectedRows] = useState<any[]>([]);
117
-
118
- // This will returns the Page Navigation Status of Grid
119
- useEffect(() => {
120
- if (pageStatus) {
121
- pageStatus({ currentPage: currentPage, totalPages: totalPages });
122
- }
123
- }, [pageStatus, currentPage, totalPages]);
124
-
125
- // Function to handle API datasource
126
- const getGridData = useCallback(async () => {
127
- try {
128
- const sourceData = await getSourceData(dataSource);
129
- const gridData = sourceData.sourcedata;
130
- setFallbackSourceData(gridData);
131
- setWorkingDataSource(gridData);
132
- const totalPages = Math.ceil(gridData.length / pageSettings.pageNumber);
133
- setTotalPages(totalPages);
134
- setPageEnd(Math.min(10, totalPages));
135
- } catch (error) {
136
- console.error("Error fetching grid source data:", error);
137
- }
138
- }, [dataSource, pageSettings.pageNumber]);
139
-
140
- // Calculates total pages and determine dataSource type
141
- useEffect(() => {
142
- const handleDataSource = async () => {
143
- if (Array.isArray(dataSource)) {
144
- setWorkingDataSource(dataSource);
145
-
146
- // if lazy is true, then totalPages will be taken from pageSettings
147
- const totalPages =
148
- lazy && pageSettings.totalCount
149
- ? Math.ceil(pageSettings.totalCount / pageSettings.pageNumber)
150
- : Math.ceil(dataSource.length / pageSettings.pageNumber);
151
-
152
- setTotalPages(totalPages);
153
- if (!lazy) {
154
- setPageEnd(Math.min(10, totalPages));
155
- } else {
156
- setPageEnd(Math.min(pageStart + 10, totalPages));
157
- }
158
- } else if (typeof dataSource === "string") {
159
- await getGridData();
160
- }
161
- };
162
-
163
- handleDataSource();
164
- }, [dataSource, pageSettings, getGridData]);
165
-
166
- // Adds Filter Column
167
- useEffect(() => {
168
- if (columns && columns.length > 0) {
169
- const filteredColumns = columns.map((column) => ({
170
- ...column,
171
- showFilterPopup: false,
172
- isFilterActive: false,
173
- }));
174
- setWorkingColumns(filteredColumns);
175
- }
176
- }, [columns]);
177
-
178
- // Adds Search Column
179
- useEffect(() => {
180
- if (!columns || columns.length === 0) {
181
- if (
182
- workingDataSource.length > 0 &&
183
- Object.keys(workingDataSource[0]).length > 0
184
- ) {
185
- const inferredColumns = Object.keys(workingDataSource[0]).map(
186
- (key) => ({
187
- field: key,
188
- headerText: key.charAt(0).toUpperCase() + key.slice(1),
189
- width: 150,
190
- })
191
- );
192
- setWorkingColumns((prevColumns: any) => {
193
- if (JSON.stringify(prevColumns) !== JSON.stringify(inferredColumns)) {
194
- return inferredColumns;
195
- }
196
- return prevColumns;
197
- });
198
- }
199
- }
200
- }, [columns, workingDataSource]);
201
-
202
- // *** Page Navigation Helper Methods
203
- const updatePageRange = (page: number) => {
204
- const start = Math.floor(page / 10) * 10;
205
- setPageStart(start);
206
- setPageEnd(Math.min(start + 10, totalPages));
207
- };
208
-
209
- const nextPage = () => {
210
- setCurrentPage((prevPage) => {
211
- if (prevPage < totalPages - 1) {
212
- const newPage = prevPage + 1;
213
- updatePageRange(newPage);
214
- return newPage;
215
- }
216
- return prevPage;
217
- });
218
- };
219
-
220
- const prevPage = () => {
221
- setCurrentPage((prevPage) => {
222
- if (prevPage > 0) {
223
- const newPage = prevPage - 1;
224
- updatePageRange(newPage);
225
- return newPage;
226
- }
227
- return prevPage;
228
- });
229
- };
230
-
231
- const goToEndPage = () => {
232
- const lastPage = totalPages - 1;
233
- setCurrentPage(lastPage);
234
- updatePageRange(lastPage);
235
- };
236
-
237
- const goToFirstPage = () => {
238
- setCurrentPage(0);
239
- updatePageRange(0);
240
- };
241
-
242
- const goToPage = (page: number) => {
243
- setCurrentPage(page);
244
- updatePageRange(page);
245
- };
246
-
247
- // *** Search Functions
248
- const handleSearchInput = (e: any) => {
249
- const searchValue = e.target.value;
250
- if (searchParamValue) searchParamValue(searchValue);
251
- setSearchParam(searchValue);
252
-
253
- if (searchValue === "") {
254
- if (!lazy) {
255
- setWorkingDataSource(
256
- fallbackSourceData.length > 0 ? fallbackSourceData : dataSource
257
- );
258
- const fallback =
259
- fallbackSourceData.length > 0 ? fallbackSourceData : dataSource;
260
- const totalPages = Math.ceil(fallback.length / pageSettings.pageNumber);
261
- setTotalPages(totalPages);
262
- setPageEnd(Math.min(pageStart + 10, totalPages));
263
- }
264
- }
265
- };
266
-
267
- const handleSearch = (searchParam: string) => {
268
- if (!lazy) {
269
- const filteredData = workingDataSource.filter((item: any) =>
270
- Object.values(item).some((val: any) => {
271
- const trimmedVal = val.toString().toLowerCase().trim();
272
- const trimmedSearchParam = searchParam.toLowerCase().trim();
273
- return trimmedVal.includes(trimmedSearchParam);
274
- })
275
- );
276
- setWorkingDataSource(filteredData);
277
- resetPage(filteredData);
278
- }
279
- };
280
-
281
- // *** Filter Functions
282
- // Resetting pagination params for updating pagination
283
- function resetPage(dataSource: any) {
284
- const newTotalPages = Math.ceil(
285
- dataSource.length / pageSettings.pageNumber
286
- );
287
-
288
- setTotalPages(newTotalPages);
289
- setCurrentPage(0);
290
- setPageStart(0);
291
- setPageEnd(Math.min(10, newTotalPages));
292
- }
293
-
294
- const toggleFilterPopup = (index: number) => {
295
- setWorkingColumns((prevColumns: any) =>
296
- prevColumns.map((column: any, i: any) =>
297
- i === index
298
- ? { ...column, showFilterPopup: !column.showFilterPopup }
299
- : column
300
- )
301
- );
302
- };
303
-
304
- function handleApplyFilter(event: any) {
305
- const {
306
- columns: updatedColumns,
307
- workingDataSource: updatedFullDataSource,
308
- activeFilterArray: updatedActiveFilterArray,
309
- } = handleApplyFilterHelper(event, columns, workingDataSource);
310
-
311
- setWorkingColumns(updatedColumns);
312
- setWorkingDataSource(updatedFullDataSource);
313
- setActiveFilterArray(updatedActiveFilterArray);
314
- if (activeFilterArrayValue)
315
- activeFilterArrayValue(updatedActiveFilterArray);
316
- resetPage(updatedFullDataSource);
317
- }
47
+ // Data source management
48
+ const {
49
+ workingDataSource,
50
+ setWorkingDataSource,
51
+ fallbackSourceData,
52
+ totalPages,
53
+ setTotalPages,
54
+ } = useDataSource(dataSource, pageSettings, lazy);
318
55
 
319
- function clearFilter(event: any) {
320
- const clearDataSource = Array.isArray(dataSource)
321
- ? dataSource
322
- : fallbackSourceData;
323
- const {
324
- columns: updatedColumns,
325
- workingDataSource: updatedDataSource,
326
- activeFilterArray: updatedActiveFilterArray,
327
- } = clearFilterHelper(event, workingColumns, clearDataSource);
56
+ // Column management
57
+ const { workingColumns, setWorkingColumns } = useColumns(
58
+ columns,
59
+ workingDataSource
60
+ );
328
61
 
329
- setWorkingColumns(updatedColumns);
330
- setWorkingDataSource(updatedDataSource);
331
- setActiveFilterArray(updatedActiveFilterArray);
332
- if (activeFilterArrayValue)
333
- activeFilterArrayValue(updatedActiveFilterArray);
334
- resetPage(updatedDataSource);
335
- }
62
+ // Pagination management
63
+ const {
64
+ currentPage,
65
+ pageStart,
66
+ pageEnd,
67
+ nextPage,
68
+ prevPage,
69
+ goToEndPage,
70
+ goToFirstPage,
71
+ goToPage,
72
+ resetPage,
73
+ } = usePagination(totalPages, lazy);
336
74
 
337
- const handleFilterAction = (action: any, colIndex: number) => {
338
- switch (action.type) {
339
- case "cancel":
340
- toggleFilterPopup(colIndex);
341
- break;
342
- case "applyFilter":
343
- handleApplyFilter(action);
344
- break;
345
- case "clearFilter":
346
- clearFilter(action);
347
- break;
348
- default:
349
- break;
350
- }
351
- };
75
+ // Search functionality
76
+ const { searchParam, handleSearchInput, handleSearch } = useSearch({
77
+ workingDataSource,
78
+ setWorkingDataSource,
79
+ fallbackSourceData,
80
+ dataSource,
81
+ lazy,
82
+ pageSettings,
83
+ resetPage,
84
+ setTotalPages,
85
+ searchParamValue,
86
+ });
352
87
 
353
- // *** Row Selection Functions
354
- const handleSelectAll = (event: React.ChangeEvent<HTMLInputElement>) => {
355
- const selected = event.target.checked ? workingDataSource : [];
356
- setSelectedRows(selected);
357
- onSelectRow?.(selected);
358
- };
88
+ // Filtering functionality
89
+ const {
90
+ activeFilterArray,
91
+ toggleFilterPopup,
92
+ handleApplyFilter,
93
+ clearFilter,
94
+ handleFilterAction,
95
+ } = useFiltering({
96
+ columns,
97
+ workingColumns,
98
+ setWorkingColumns,
99
+ workingDataSource,
100
+ setWorkingDataSource,
101
+ dataSource,
102
+ fallbackSourceData,
103
+ resetPage,
104
+ setTotalPages,
105
+ pageSettings,
106
+ activeFilterArrayValue,
107
+ });
359
108
 
360
- const handleSelect = (rowData: any) => {
361
- setSelectedRows((prevData) => {
362
- const isSelected = prevData.includes(rowData);
363
- let updatedData;
364
- if (isSelected) {
365
- updatedData = prevData.filter((item) => item !== rowData);
366
- } else {
367
- updatedData = [...prevData, rowData];
368
- }
369
- if (onSelectRow) onSelectRow(updatedData);
370
- return updatedData;
371
- });
372
- };
109
+ // Row selection functionality
110
+ const { selectedRows, handleSelectAll, handleSelect, isRowSelected } =
111
+ useRowSelection(workingDataSource, onSelectRow);
373
112
 
374
- const isRowSelected = (rowData: any) => {
375
- return selectedRows.includes(rowData);
376
- };
113
+ // Page status reporting
114
+ usePageStatus(currentPage, totalPages, pageStatus);
377
115
 
378
- // Provide context value
116
+ // Context value
379
117
  const contextValue: GridContextType = {
380
118
  // State
381
119
  workingDataSource,
@@ -0,0 +1,7 @@
1
+ export { useDataSource } from "./useDataSource";
2
+ export { useColumns } from "./useColumns";
3
+ export { usePagination } from "./usePagination";
4
+ export { useSearch } from "./useSearch";
5
+ export { useFiltering } from "./useFiltering";
6
+ export { useRowSelection } from "./useRowSelection";
7
+ export { usePageStatus } from "./usePageStatus";
@@ -0,0 +1,33 @@
1
+ import { useState, useEffect } from "react";
2
+
3
+ export const useColumns = (columns: any[], workingDataSource: any[]) => {
4
+ const [workingColumns, setWorkingColumns] = useState<any>([]);
5
+
6
+ useEffect(() => {
7
+ if (columns && columns.length > 0) {
8
+ const filteredColumns = columns.map((column) => ({
9
+ ...column,
10
+ showFilterPopup: false,
11
+ isFilterActive: false,
12
+ }));
13
+ setWorkingColumns(filteredColumns);
14
+ } else if (
15
+ workingDataSource.length > 0 &&
16
+ Object.keys(workingDataSource[0]).length > 0
17
+ ) {
18
+ const inferredColumns = Object.keys(workingDataSource[0]).map((key) => ({
19
+ field: key,
20
+ headerText: key.charAt(0).toUpperCase() + key.slice(1),
21
+ width: 150,
22
+ }));
23
+ setWorkingColumns((prevColumns: any) => {
24
+ if (JSON.stringify(prevColumns) !== JSON.stringify(inferredColumns)) {
25
+ return inferredColumns;
26
+ }
27
+ return prevColumns;
28
+ });
29
+ }
30
+ }, [columns, workingDataSource]);
31
+
32
+ return { workingColumns, setWorkingColumns };
33
+ };
@@ -0,0 +1,54 @@
1
+ import { useState, useCallback, useEffect } from "react";
2
+ import { getSourceData } from "../../utils";
3
+
4
+ export const useDataSource = (
5
+ dataSource: any,
6
+ pageSettings: any,
7
+ lazy: boolean
8
+ ) => {
9
+ const [workingDataSource, setWorkingDataSource] = useState<any>([]);
10
+ const [fallbackSourceData, setFallbackSourceData] = useState([]);
11
+ const [totalPages, setTotalPages] = useState(0);
12
+
13
+ const getGridData = useCallback(async () => {
14
+ try {
15
+ const sourceData = await getSourceData(dataSource);
16
+ const gridData = sourceData.sourcedata;
17
+ setFallbackSourceData(gridData);
18
+ setWorkingDataSource(gridData);
19
+ const totalPages = Math.ceil(gridData.length / pageSettings.pageNumber);
20
+ setTotalPages(totalPages);
21
+ return totalPages;
22
+ } catch (error) {
23
+ console.error("Error fetching grid source data:", error);
24
+ return 0;
25
+ }
26
+ }, [dataSource, pageSettings.pageNumber]);
27
+
28
+ useEffect(() => {
29
+ const handleDataSource = async () => {
30
+ if (Array.isArray(dataSource)) {
31
+ setWorkingDataSource(dataSource);
32
+
33
+ const totalPages =
34
+ lazy && pageSettings.totalCount
35
+ ? Math.ceil(pageSettings.totalCount / pageSettings.pageNumber)
36
+ : Math.ceil(dataSource.length / pageSettings.pageNumber);
37
+
38
+ setTotalPages(totalPages);
39
+ } else if (typeof dataSource === "string") {
40
+ await getGridData();
41
+ }
42
+ };
43
+
44
+ handleDataSource();
45
+ }, [dataSource, pageSettings, lazy, getGridData]);
46
+
47
+ return {
48
+ workingDataSource,
49
+ setWorkingDataSource,
50
+ fallbackSourceData,
51
+ totalPages,
52
+ setTotalPages,
53
+ };
54
+ };
@@ -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,88 @@
1
+ import { useState, useCallback, useEffect } from "react";
2
+
3
+ export const usePagination = (totalPages: number, lazy: boolean) => {
4
+ const [currentPage, setCurrentPage] = useState(0);
5
+ const [pageStart, setPageStart] = useState(0);
6
+ const [pageEnd, setPageEnd] = useState(10);
7
+
8
+ const updatePageRange = useCallback(
9
+ (page: number) => {
10
+ const start = Math.floor(page / 10) * 10;
11
+ setPageStart(start);
12
+ setPageEnd(Math.min(start + 10, totalPages));
13
+ },
14
+ [totalPages]
15
+ );
16
+
17
+ useEffect(() => {
18
+ if (!lazy) {
19
+ setPageEnd(Math.min(10, totalPages));
20
+ } else {
21
+ setPageEnd(Math.min(pageStart + 10, totalPages));
22
+ }
23
+ }, [totalPages, lazy, pageStart]);
24
+
25
+ const nextPage = useCallback(() => {
26
+ setCurrentPage((prevPage) => {
27
+ if (prevPage < totalPages - 1) {
28
+ const newPage = prevPage + 1;
29
+ updatePageRange(newPage);
30
+ return newPage;
31
+ }
32
+ return prevPage;
33
+ });
34
+ }, [totalPages, updatePageRange]);
35
+
36
+ const prevPage = useCallback(() => {
37
+ setCurrentPage((prevPage) => {
38
+ if (prevPage > 0) {
39
+ const newPage = prevPage - 1;
40
+ updatePageRange(newPage);
41
+ return newPage;
42
+ }
43
+ return prevPage;
44
+ });
45
+ }, [updatePageRange]);
46
+
47
+ const goToEndPage = useCallback(() => {
48
+ const lastPage = totalPages - 1;
49
+ setCurrentPage(lastPage);
50
+ updatePageRange(lastPage);
51
+ }, [totalPages, updatePageRange]);
52
+
53
+ const goToFirstPage = useCallback(() => {
54
+ setCurrentPage(0);
55
+ updatePageRange(0);
56
+ }, [updatePageRange]);
57
+
58
+ const goToPage = useCallback(
59
+ (page: number) => {
60
+ setCurrentPage(page);
61
+ updatePageRange(page);
62
+ },
63
+ [updatePageRange]
64
+ );
65
+
66
+ const resetPage = useCallback((dataSource: any, pageSettings: any) => {
67
+ const newTotalPages = Math.ceil(
68
+ dataSource.length / pageSettings.pageNumber
69
+ );
70
+ setCurrentPage(0);
71
+ setPageStart(0);
72
+ setPageEnd(Math.min(10, newTotalPages));
73
+ return newTotalPages;
74
+ }, []);
75
+
76
+ return {
77
+ currentPage,
78
+ pageStart,
79
+ pageEnd,
80
+ nextPage,
81
+ prevPage,
82
+ goToEndPage,
83
+ goToFirstPage,
84
+ goToPage,
85
+ resetPage,
86
+ updatePageRange,
87
+ };
88
+ };
@@ -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
+ };
@@ -1,6 +1,5 @@
1
- import React from "react";
1
+ import React, { act } from "react";
2
2
  import { useGridContext } from "../context/GridContext";
3
- import { useGridFilter, useGridRowSelection } from "../hooks/GridHooks";
4
3
  import FilterPopup from "./FilterPopup";
5
4
  import Icon from "../../icon/Icon";
6
5
  import { listFilter } from "../../icon/iconPaths";
@@ -18,6 +17,13 @@ const GridBody = () => {
18
17
  gridColumnStyleSelectAll,
19
18
  rowChange,
20
19
  lazy,
20
+ activeFilterArray,
21
+ toggleFilterPopup,
22
+ handleFilterAction,
23
+ selectAll,
24
+ handleSelectAll,
25
+ handleSelect,
26
+ isRowSelected,
21
27
  } = useGridContext();
22
28
 
23
29
  const renderCell = (rowData: any, column: Column, rowIndex: number) => {
@@ -47,11 +53,6 @@ const GridBody = () => {
47
53
  return cellValue;
48
54
  };
49
55
 
50
- const { activeFilterArray, toggleFilterPopup, handleFilterAction } =
51
- useGridFilter();
52
- const { selectAll, handleSelectAll, handleSelect, isRowSelected } =
53
- useGridRowSelection();
54
-
55
56
  const displayStart = currentPage * pageSettings.pageNumber;
56
57
  const displayEnd = displayStart + pageSettings.pageNumber;
57
58
  const displayData = lazy
@@ -1,5 +1,4 @@
1
1
  import React from "react";
2
- import { useGridPagination } from "../hooks/GridHooks";
3
2
  import Icon from "../../icon/Icon";
4
3
  import {
5
4
  leftArrow,
@@ -7,6 +6,7 @@ import {
7
6
  rightArrow,
8
7
  rightArrows,
9
8
  } from "../../icon/iconPaths";
9
+ import { useGridContext } from "../context/GridContext";
10
10
 
11
11
  const Pagination = () => {
12
12
  const {
@@ -23,7 +23,7 @@ const Pagination = () => {
23
23
  lazy,
24
24
  pageSettings,
25
25
  showTotalPages,
26
- } = useGridPagination();
26
+ } = useGridContext();
27
27
 
28
28
  return (
29
29
  <div className="w-full flex flex-wrap justify-between items-center text-xs px-2 py-4 bg-zinc-100 dark:bg-zinc-900 gap-2 md:gap-4">
@@ -1,11 +1,11 @@
1
- import React, { lazy } from "react";
2
- import { useGridExport, useGridSearch } from "../hooks/GridHooks";
1
+ import React from "react";
3
2
  import {
4
3
  exportToExcelHelper,
5
4
  exportToPDFHelper,
6
5
  } from "@grampro/headless-helpers";
7
6
  import Icon from "../../icon/Icon";
8
7
  import { search } from "../../icon/iconPaths";
8
+ import { useGridContext } from "../context/GridContext";
9
9
 
10
10
  const GridToolbar = () => {
11
11
  const {
@@ -14,9 +14,6 @@ const GridToolbar = () => {
14
14
  handleSearch,
15
15
  enableSearch,
16
16
  onSearch,
17
- } = useGridSearch();
18
-
19
- const {
20
17
  enableExcelExport,
21
18
  enablePdfExport,
22
19
  workingDataSource,
@@ -26,7 +23,8 @@ const GridToolbar = () => {
26
23
  pdfOptions,
27
24
  gridButtonClass,
28
25
  onToolbarButtonClick,
29
- } = useGridExport();
26
+ lazy,
27
+ } = useGridContext();
30
28
 
31
29
  if (!enableSearch && !enableExcelExport && !enablePdfExport) {
32
30
  return null;
@@ -71,6 +69,7 @@ const GridToolbar = () => {
71
69
  <button
72
70
  className={gridButtonClass}
73
71
  onClick={() => {
72
+ console.log("clicked", lazy.toString());
74
73
  if (!lazy) {
75
74
  exportToExcelHelper(workingDataSource, columns, excelName);
76
75
  }
@@ -121,3 +121,60 @@ export type GridBodyProps = {
121
121
  gridColumnStyle: string;
122
122
  rowChange?: (changes: any) => void;
123
123
  };
124
+
125
+ export interface GridContextType {
126
+ workingDataSource: any[];
127
+ fallbackSourceData: any[];
128
+ workingColumns: any[];
129
+ currentPage: number;
130
+ pageStart: number;
131
+ pageEnd: number;
132
+ totalPages: number;
133
+ searchParam: string;
134
+ activeFilterArray: any[];
135
+ selectedRows: any[];
136
+ isFetching: boolean | undefined;
137
+
138
+ // Navigation methods
139
+ nextPage: () => void;
140
+ prevPage: () => void;
141
+ goToEndPage: () => void;
142
+ goToFirstPage: () => void;
143
+ goToPage: (page: number) => void;
144
+ lazy: boolean;
145
+
146
+ // Search methods
147
+ handleSearchInput: (e: any) => void;
148
+ handleSearch: (searchParam: string) => void;
149
+
150
+ // Filter methods
151
+ toggleFilterPopup: (index: number) => void;
152
+ handleApplyFilter: (event: any) => void;
153
+ clearFilter: (event: any) => void;
154
+ handleFilterAction: (action: any, colIndex: number) => void;
155
+
156
+ // Row selection methods
157
+ handleSelectAll: (event: React.ChangeEvent<HTMLInputElement>) => void;
158
+ handleSelect: (rowData: any) => void;
159
+ isRowSelected: (rowData: any) => boolean;
160
+
161
+ // Grid settings
162
+ columns: any[];
163
+ pageSettings: any;
164
+ enableSearch: boolean;
165
+ enableExcelExport: boolean;
166
+ enablePdfExport: boolean;
167
+ excelName: string;
168
+ pdfName: string;
169
+ pdfOptions: any;
170
+ gridButtonClass: string;
171
+ selectAll: boolean;
172
+ tableHeaderStyle: string;
173
+ gridContainerClass: string;
174
+ gridColumnStyleSelectAll: string;
175
+ gridColumnStyle: string;
176
+ rowChange: (rowData: any) => void;
177
+ showTotalPages?: boolean;
178
+ onSearch?: (searchParam: string) => void;
179
+ onToolbarButtonClick?: (action: string) => void;
180
+ }
@@ -21,6 +21,18 @@ export const MenuItemComponent = ({
21
21
  }
22
22
  }, []);
23
23
 
24
+ if (item.type === "custom" && item.component) {
25
+ const CustomComponent = item.component;
26
+ return (
27
+ <li className="py-2 px-3 flex items-center text-sm text-gray-700">
28
+ <span className="mr-3">
29
+ <IconComponent />
30
+ </span>
31
+ <CustomComponent />
32
+ </li>
33
+ );
34
+ }
35
+
24
36
  return (
25
37
  <li>
26
38
  <a
@@ -3,14 +3,9 @@ import { MenuItem, IconComponent } from "../type";
3
3
  type SidebarFooterProps = {
4
4
  footerItems?: MenuItem[];
5
5
  iconMap: Record<string, IconComponent>;
6
- toggleProMode: () => void;
7
6
  };
8
7
 
9
- export const SidebarFooter = ({
10
- footerItems,
11
- iconMap,
12
- toggleProMode,
13
- }: SidebarFooterProps) => {
8
+ export const SidebarFooter = ({ footerItems, iconMap }: SidebarFooterProps) => {
14
9
  if (!footerItems) return null;
15
10
  if (!iconMap) return null;
16
11
 
@@ -20,28 +15,17 @@ export const SidebarFooter = ({
20
15
  {footerItems?.map((item) => {
21
16
  const IconComponent = iconMap[item.icon] || (() => null);
22
17
 
23
- if (item.toggle) {
18
+ if (item.type === "custom" && item.component) {
19
+ const CustomComponent = item.component;
24
20
  return (
25
21
  <li
26
22
  key={item.id}
27
- className="flex items-center py-2 px-3 text-sm text-gray-700"
23
+ className="py-2 px-3 flex items-center text-sm text-gray-700"
28
24
  >
29
25
  <span className="mr-3">
30
26
  <IconComponent />
31
27
  </span>
32
- <span className="flex-grow">{item.name}</span>
33
- <button
34
- onClick={toggleProMode}
35
- className={`w-10 h-5 flex items-center rounded-full p-1 ${
36
- item.active ? "bg-black" : "bg-gray-300"
37
- }`}
38
- >
39
- <span
40
- className={`bg-white w-4 h-4 rounded-full shadow-md transform transition-transform ${
41
- item.active ? "translate-x-5" : ""
42
- }`}
43
- />
44
- </button>
28
+ <CustomComponent />
45
29
  </li>
46
30
  );
47
31
  }
@@ -11,6 +11,7 @@ export const SidebarMenu = ({
11
11
  sections,
12
12
  iconMap,
13
13
  toggleItem,
14
+
14
15
  }: SidebarMenuProps) => {
15
16
  return (
16
17
  <div className="flex-grow overflow-y-auto px-4 py-2">
@@ -19,20 +19,23 @@ export const useSidebarState = (menuData: MenuData) => {
19
19
  }
20
20
  };
21
21
 
22
- const toggleProMode = (): void => {
22
+ const toggleMode = (): void => {
23
23
  const newData = { ...sidebarData };
24
24
  if (!newData.footer) return;
25
25
 
26
- const proModeIndex = newData.footer.findIndex((item) => item.toggle);
27
- if (proModeIndex === -1) return;
26
+ const toggleModeIndex = newData.footer.findIndex(
27
+ (item) => item.type === "toggle"
28
+ );
29
+ if (toggleModeIndex === -1) return;
28
30
 
29
- newData.footer[proModeIndex].active = !newData.footer[proModeIndex].active;
31
+ newData.footer[toggleModeIndex].active =
32
+ !newData.footer[toggleModeIndex].active;
30
33
  setSidebarData({ ...newData });
31
34
  };
32
35
 
33
36
  return {
34
37
  sidebarData,
35
38
  toggleItem,
36
- toggleProMode,
39
+ toggleMode,
37
40
  };
38
41
  };
@@ -6,21 +6,17 @@ import { SidebarFooter } from "./components/SidebarFooter";
6
6
  import { UserProfile } from "./components/UserProfile";
7
7
 
8
8
  export function Sidebar({ menuData, iconMap }: SidebarProps) {
9
- const { sidebarData, toggleItem, toggleProMode } = useSidebarState(menuData);
9
+ const { sidebarData, toggleItem } = useSidebarState(menuData);
10
10
 
11
11
  return (
12
- <div className="hidden md:flex flex-col h-screen bg-white w-64 border-r border-gray-200">
12
+ <div className="hidden md:flex flex-col h-screen bg-white w-64 border-r border-gray-200 sticky left-0 top-0">
13
13
  <SidebarLogo logo={sidebarData.logo} />
14
14
  <SidebarMenu
15
15
  sections={sidebarData.sections}
16
16
  iconMap={iconMap}
17
17
  toggleItem={toggleItem}
18
18
  />
19
- <SidebarFooter
20
- footerItems={sidebarData.footer}
21
- iconMap={iconMap}
22
- toggleProMode={toggleProMode}
23
- />
19
+ <SidebarFooter footerItems={sidebarData.footer} iconMap={iconMap} />
24
20
  <UserProfile profile={sidebarData.profile} />
25
21
  </div>
26
22
  );
@@ -1,20 +1,25 @@
1
1
  export type IconComponent = React.FC;
2
2
 
3
3
  export type MenuItem = {
4
- id: number;
4
+ id: number | string;
5
5
  name: string;
6
6
  icon: string;
7
- path: string;
7
+ path?: string;
8
8
  active?: boolean;
9
9
  expandable?: boolean;
10
10
  expanded?: boolean;
11
11
  badge?: string;
12
12
  toggle?: boolean;
13
+ type?: string;
14
+ component?: React.FC;
13
15
  };
14
16
 
15
17
  export type MenuSection = {
18
+ id?: number | string;
16
19
  title: string;
17
20
  items: MenuItem[];
21
+ component?: React.FC;
22
+ type?: string;
18
23
  };
19
24
 
20
25
  export type MenuData = {
@@ -1,114 +0,0 @@
1
- import { useGridContext } from "../context/GridContext";
2
-
3
- // Hook for pagination functionality
4
- export const useGridPagination = () => {
5
- const {
6
- currentPage,
7
- pageStart,
8
- pageEnd,
9
- totalPages,
10
- goToFirstPage,
11
- prevPage,
12
- goToPage,
13
- nextPage,
14
- goToEndPage,
15
- workingDataSource,
16
- lazy,
17
- pageSettings,
18
- showTotalPages,
19
- } = useGridContext();
20
-
21
- return {
22
- currentPage,
23
- pageStart,
24
- pageEnd,
25
- totalPages,
26
- goToFirstPage,
27
- prevPage,
28
- goToPage,
29
- nextPage,
30
- goToEndPage,
31
- workingDataSource,
32
- lazy,
33
- pageSettings,
34
- showTotalPages,
35
- };
36
- };
37
-
38
- // Hook for grid search functionality
39
- export const useGridSearch = () => {
40
- const {
41
- searchParam,
42
- handleSearchInput,
43
- handleSearch,
44
- enableSearch,
45
- onSearch,
46
- } = useGridContext();
47
-
48
- return {
49
- searchParam,
50
- handleSearchInput,
51
- handleSearch,
52
- enableSearch,
53
- onSearch,
54
- };
55
- };
56
-
57
- // Hook for grid filter functionality
58
- export const useGridFilter = () => {
59
- const {
60
- workingColumns,
61
- activeFilterArray,
62
- toggleFilterPopup,
63
- handleFilterAction,
64
- } = useGridContext();
65
-
66
- return {
67
- workingColumns,
68
- activeFilterArray,
69
- toggleFilterPopup,
70
- handleFilterAction,
71
- };
72
- };
73
-
74
- // Hook for row selection
75
- export const useGridRowSelection = () => {
76
- const { selectAll, handleSelectAll, handleSelect, isRowSelected } =
77
- useGridContext();
78
-
79
- return {
80
- selectAll,
81
- handleSelectAll,
82
- handleSelect,
83
- isRowSelected,
84
- };
85
- };
86
-
87
- // Hook for export functionality
88
- export const useGridExport = () => {
89
- const {
90
- enableExcelExport,
91
- enablePdfExport,
92
- workingDataSource,
93
- columns,
94
- excelName,
95
- pdfName,
96
- pdfOptions,
97
- gridButtonClass,
98
- onToolbarButtonClick,
99
- lazy,
100
- } = useGridContext();
101
-
102
- return {
103
- enableExcelExport,
104
- enablePdfExport,
105
- workingDataSource,
106
- columns,
107
- excelName,
108
- pdfName,
109
- pdfOptions,
110
- gridButtonClass,
111
- onToolbarButtonClick,
112
- lazy,
113
- };
114
- };