gbs-add-block 1.0.11 → 1.0.13

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 (v1.0.11)
1
+ # GBS Building Blocks 2.0 (v1.0.13)
2
2
 
3
3
  Latest and upgraded version of GBS building blocks with headless UI and removed dependencies.
4
4
 
@@ -6,10 +6,10 @@ 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 1.0.11)
9
+ ## What's New 🎉 (Ver 1.0.13)
10
10
 
11
- - Uploader Enhancement
12
- - Select & MultiSelect Bug Fix
11
+ - Data Grid Enhancement
12
+ - End of Support for Grid Component
13
13
 
14
14
  ## Authors
15
15
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gbs-add-block",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "React Component Library",
5
5
  "type": "module",
6
6
  "files": [
@@ -70,7 +70,7 @@ export const GridProvider: React.FC<{
70
70
  goToFirstPage,
71
71
  goToPage,
72
72
  resetPage,
73
- } = usePagination(totalPages, lazy);
73
+ } = usePagination(totalPages, lazy, workingDataSource, pageSettings);
74
74
 
75
75
  // Search functionality
76
76
  const { searchParam, handleSearchInput, handleSearch } = useSearch({
@@ -1,9 +1,15 @@
1
1
  import { useState, useCallback, useEffect } from "react";
2
2
 
3
- export const usePagination = (totalPages: number, lazy: boolean) => {
3
+ export const usePagination = (
4
+ totalPages: number,
5
+ lazy: boolean,
6
+ dataSource?: any[],
7
+ pageSettings?: { pageNumber: number }
8
+ ) => {
4
9
  const [currentPage, setCurrentPage] = useState(0);
5
10
  const [pageStart, setPageStart] = useState(0);
6
11
  const [pageEnd, setPageEnd] = useState(10);
12
+ const [prevDataLength, setPrevDataLength] = useState(0);
7
13
 
8
14
  const updatePageRange = useCallback(
9
15
  (page: number) => {
@@ -22,6 +28,37 @@ export const usePagination = (totalPages: number, lazy: boolean) => {
22
28
  }
23
29
  }, [totalPages, lazy, pageStart]);
24
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
+
25
62
  const nextPage = useCallback(() => {
26
63
  setCurrentPage((prevPage) => {
27
64
  if (prevPage < totalPages - 1) {
@@ -153,7 +153,7 @@ const MultiSelect = forwardRef<MultiSelectHandle, MultiSelectProps>(
153
153
  selected.includes(value) ? "stroke-gray-500" : ""
154
154
  }`}
155
155
  />
156
- {label}
156
+ {label.length > 20 ? `${label.substring(0, 20)}...` : label}
157
157
  </button>
158
158
  ))
159
159
  ) : (
@@ -170,7 +170,7 @@ const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
170
170
  selectedItem === value ? "stroke-gray-500" : ""
171
171
  }`}
172
172
  />
173
- {label}
173
+ {label.length > 20 ? `${label.substring(0, 20)}...` : label}
174
174
  </button>
175
175
  ))
176
176
  ) : (
@@ -1,85 +0,0 @@
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 border z-50 mt-48 flex items-end flex-col shadow-lg gap-2 rounded-md dark:bg-gray-700"
38
- role="dialog"
39
- >
40
- <select
41
- name={`${columnHeader}-filter`}
42
- id={`${columnHeader}-filter-id`}
43
- className="w-full p-2 rounded-lg 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-sm dark:bg-gray-600"
57
- value={filterValue}
58
- onInput={handleFilterInput}
59
- />
60
- <div className="mt-2">
61
- <button
62
- className="text-xs bg-red-600 text-white p-1 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 p-1 rounded-lg dark:bg-white dark:text-white"
70
- onClick={clearFilter}
71
- >
72
- Clear Filter
73
- </button>
74
- )}
75
- <button
76
- className="text-xs bg-black text-white p-1 rounded-lg dark:bg-white dark:text-black"
77
- onClick={applyFilter}
78
- >
79
- Apply Filter
80
- </button>
81
- </div>
82
- </div>
83
- )
84
- );
85
- }
@@ -1,653 +0,0 @@
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
- * Extended Documentation for Grid can be found at
7
- * https://psychedelic-step-e70.notion.site/Data-GRID-by-GBS-R-D-20ff97c899d24bc590215a6196435fa3
8
- */
9
-
10
- import React, {
11
- forwardRef,
12
- useCallback,
13
- useEffect,
14
- useImperativeHandle,
15
- useState,
16
- } from "react";
17
- import FilterPopup from "./FilterPopup";
18
- import {
19
- clearFilterHelper,
20
- exportToExcelHelper,
21
- exportToPDFHelper,
22
- } from "./GridHelperFunctions";
23
- import { handleApplyFilterHelper } from "./GridHelperFunctions";
24
- import Icon from "../icon/Icon";
25
- import {
26
- leftArrows,
27
- leftArrow,
28
- rightArrow,
29
- rightArrows,
30
- search,
31
- listFilter,
32
- } from "../icon/iconPaths";
33
- import type { GridProps } from "./type";
34
- import { getSourceData } from "../utils";
35
-
36
- export const GridMemoised = forwardRef((props: GridProps, ref) => {
37
- const {
38
- dataSource,
39
- columns = [],
40
- pageSettings,
41
- enableSearch = false,
42
- lazy = false,
43
- enableExcelExport = false,
44
- excelName = "data",
45
- enablePdfExport = false,
46
- pdfName = "data",
47
- pdfOptions = {},
48
- gridButtonClass = "px-1 py-2 bg-white border rounded-lg text-xs text-black dark:bg-black dark:text-white",
49
- selectAll = false,
50
- onSelectRow,
51
- isFetching,
52
- tableHeaderStyle = "text-left border-b border-t bg-gray-50 px-2 py-4 dark:bg-gray-700 dark:text-white",
53
- gridContainerClass = "flex flex-col min-w-screen border rounded-md overflow-hidden dark:bg-black",
54
- gridColumnStyleSelectAll = "border-b px-4 text-sm dark:text-white",
55
- gridColumnStyle = "border-b p-2 text-sm dark:text-white",
56
- rowChange = () => {},
57
- pageStatus = () => {},
58
- } = props;
59
-
60
- // States Handling Grid
61
- const [workingDataSource, setWorkingDataSource] = useState<any>([]);
62
- const [fallbackSourceData, setFallbackSourceData] = useState([]);
63
- const [workingColumns, setWorkingColumns] = useState<any>([]);
64
- const [currentPage, setCurrentPage] = useState(0);
65
- const [pageStart, setPageStart] = useState(0);
66
- const [pageEnd, setPageEnd] = useState(10);
67
- const [totalPages, setTotalPages] = useState(0);
68
- const [searchParam, setSearchParam] = useState("");
69
- const [activeFilterArray, setActiveFilterArray] = useState<any>([]);
70
- const [selectedRows, setSelectedRows] = useState<any[]>([]);
71
-
72
- // This will returns the Page Navigation Status of Grid
73
- // Such as totalPages in the grid, currentpage, etc..
74
- useEffect(() => {
75
- if (pageStatus) {
76
- pageStatus({ currentPage: currentPage, totalPages: totalPages });
77
- }
78
- }, [pageStatus, currentPage, totalPages]);
79
-
80
- // Function to handle API datasource
81
- const getGridData = useCallback(async () => {
82
- try {
83
- const sourceData = await getSourceData(dataSource);
84
- const gridData = sourceData.sourcedata;
85
- setFallbackSourceData(gridData);
86
- setWorkingDataSource(gridData);
87
- const totalPages = Math.ceil(gridData.length / pageSettings.pageNumber);
88
- setTotalPages(totalPages);
89
- setPageEnd(Math.min(10, totalPages));
90
- } catch (error) {
91
- console.error("Error fetching grid source data:", error);
92
- }
93
- }, [dataSource, pageSettings.pageNumber]);
94
-
95
- // Calculates total pages and determine dataSource type
96
- useEffect(() => {
97
- const handleDataSource = async () => {
98
- if (Array.isArray(dataSource) && dataSource.length > 0) {
99
- setWorkingDataSource(dataSource);
100
- const totalPages = Math.ceil(
101
- dataSource.length / pageSettings.pageNumber
102
- );
103
- setTotalPages(totalPages);
104
- setPageEnd(Math.min(10, totalPages));
105
- } else if (typeof dataSource === "string") {
106
- await getGridData();
107
- }
108
- };
109
-
110
- handleDataSource();
111
- }, [dataSource, pageSettings]);
112
-
113
- // Adds Filter Column
114
- useEffect(() => {
115
- if (columns && columns.length > 0) {
116
- const filteredColumns = columns.map((column) => ({
117
- ...column,
118
- showFilterPopup: false,
119
- isFilterActive: false,
120
- }));
121
- setWorkingColumns(filteredColumns);
122
- }
123
- }, [columns]);
124
-
125
- useEffect(() => {
126
- if (!columns || columns.length === 0) {
127
- if (
128
- workingDataSource.length > 0 &&
129
- Object.keys(workingDataSource[0]).length > 0
130
- ) {
131
- const inferredColumns = Object.keys(workingDataSource[0]).map(
132
- (key) => ({
133
- field: key,
134
- headerText: key.charAt(0).toUpperCase() + key.slice(1),
135
- width: 150,
136
- })
137
- );
138
- setWorkingColumns((prevColumns: any) => {
139
- if (JSON.stringify(prevColumns) !== JSON.stringify(inferredColumns)) {
140
- return inferredColumns;
141
- }
142
- return prevColumns;
143
- });
144
- }
145
- }
146
- }, [columns, workingDataSource]);
147
-
148
- // *** Page Navigation Helper Methods Starts Here
149
- const nextPage = () => {
150
- setCurrentPage((prevPage) => {
151
- if (prevPage < totalPages - 1) {
152
- const newPage = prevPage + 1;
153
- updatePageRange(newPage);
154
- return newPage;
155
- }
156
- return prevPage;
157
- });
158
- };
159
-
160
- const prevPage = () => {
161
- setCurrentPage((prevPage) => {
162
- if (prevPage > 0) {
163
- const newPage = prevPage - 1;
164
- updatePageRange(newPage);
165
- return newPage;
166
- }
167
- return prevPage;
168
- });
169
- };
170
-
171
- const goToEndPage = () => {
172
- const lastPage = totalPages - 1;
173
- setCurrentPage(lastPage);
174
- updatePageRange(lastPage);
175
- };
176
-
177
- const goToFirstPage = () => {
178
- setCurrentPage(0);
179
- updatePageRange(0);
180
- };
181
-
182
- const goToPage = (page: number) => {
183
- setCurrentPage(page);
184
- updatePageRange(page);
185
- };
186
-
187
- const updatePageRange = (page: number) => {
188
- const start = Math.floor(page / 10) * 10;
189
- setPageStart(start);
190
- setPageEnd(Math.min(start + 10, totalPages));
191
- };
192
- // Page Navigation Helper Methods Ends Here ***
193
-
194
- // *** Functions handling global search starts here
195
- const handleSearchInput = (e: any) => {
196
- if (!lazy) {
197
- const searchValue = e.target.value;
198
- setSearchParam(searchValue);
199
- if (searchValue === "") {
200
- setWorkingDataSource(
201
- fallbackSourceData.length > 0 ? fallbackSourceData : dataSource
202
- );
203
- const fallback =
204
- fallbackSourceData.length > 0 ? fallbackSourceData : dataSource;
205
- const totalPages = Math.ceil(fallback.length / pageSettings.pageNumber);
206
- setTotalPages(totalPages);
207
- setPageEnd(Math.min(pageStart + 10, totalPages));
208
- }
209
- }
210
- };
211
-
212
- const handleSearch = (searchParam: string) => {
213
- const filteredData = workingDataSource.filter((item: any) =>
214
- Object.values(item).some((val: any) => {
215
- const trimmedVal = val.toString().toLowerCase().trim();
216
- const trimmedSearchParam = searchParam.toLowerCase().trim();
217
- return trimmedVal.includes(trimmedSearchParam);
218
- })
219
- );
220
- setWorkingDataSource(filteredData);
221
- const newTotalPages = Math.ceil(
222
- filteredData.length / pageSettings.pageNumber
223
- );
224
- setTotalPages(newTotalPages);
225
- const newCurrentPage = 0;
226
- setCurrentPage(newCurrentPage);
227
- const newPageStart = Math.floor(newCurrentPage / 10) * 10;
228
- setPageStart(newPageStart);
229
- setPageEnd(Math.min(newPageStart + 10, newTotalPages));
230
- };
231
- // Functions handling global search ends here ***
232
-
233
- // This will render template passed to Grid
234
- const renderCell = (rowData: any, column: any, rowIndex: number) => {
235
- const cellValue = rowData[column.field];
236
-
237
- if (column.template) {
238
- return (
239
- <column.template
240
- rowData={rowData}
241
- rowIndex={rowIndex + currentPage * pageSettings.pageNumber}
242
- rowChange={(changes: any) => {
243
- if (rowChange) rowChange(changes);
244
- }}
245
- />
246
- );
247
- }
248
-
249
- if (column.tooltip) {
250
- return (
251
- <span title={cellValue?.toString()}>
252
- {cellValue && cellValue.length > 10
253
- ? `${cellValue.slice(0, 10)}...`
254
- : cellValue}
255
- </span>
256
- );
257
- }
258
-
259
- return cellValue;
260
- };
261
-
262
- // *** Filter Handling Functions Starts Here
263
- const toggleFilterPopup = (index: number) => {
264
- setWorkingColumns((prevColumns: any) =>
265
- prevColumns.map((column: any, i: any) =>
266
- i === index
267
- ? { ...column, showFilterPopup: !column.showFilterPopup }
268
- : column
269
- )
270
- );
271
- };
272
-
273
- // Resetting pagination params for updating pagination
274
- function resetPage(dataSource: any) {
275
- const newTotalPages = Math.ceil(
276
- dataSource.length / pageSettings.pageNumber
277
- );
278
-
279
- setTotalPages(newTotalPages);
280
- setCurrentPage(0);
281
- setPageStart(0);
282
- setPageEnd(Math.min(10, newTotalPages));
283
- }
284
-
285
- // Applying Filter
286
- function handleApplyFilter(event: any) {
287
- const {
288
- columns: updatedColumns,
289
- workingDataSource: updatedFullDataSource,
290
- activeFilterArray: updatedActiveFilterArray,
291
- } = handleApplyFilterHelper(event, columns, workingDataSource);
292
-
293
- setWorkingColumns(updatedColumns);
294
- setWorkingDataSource(updatedFullDataSource);
295
- setActiveFilterArray(updatedActiveFilterArray);
296
-
297
- setActiveFilterArray(updatedActiveFilterArray);
298
- resetPage(updatedFullDataSource);
299
- }
300
-
301
- // Clearing Filter
302
- function clearFilter(event: any) {
303
- const clearDataSource = Array.isArray(dataSource)
304
- ? dataSource
305
- : fallbackSourceData;
306
- const {
307
- columns: updatedColumns,
308
- workingDataSource: updatedDataSource,
309
- activeFilterArray: updatedActiveFilterArray,
310
- } = clearFilterHelper(event, workingColumns, clearDataSource);
311
-
312
- setWorkingColumns(updatedColumns);
313
- setWorkingDataSource(updatedDataSource);
314
- setActiveFilterArray(updatedActiveFilterArray);
315
- resetPage(updatedDataSource);
316
- }
317
-
318
- const handleFilterAction = (action: any, colIndex: number) => {
319
- switch (action.type) {
320
- case "cancel":
321
- toggleFilterPopup(colIndex);
322
- break;
323
- case "applyFilter":
324
- handleApplyFilter(action);
325
- break;
326
- case "clearFilter":
327
- clearFilter(action);
328
- break;
329
- default:
330
- break;
331
- }
332
- };
333
- // *** Filter Handling Functions Ends Here
334
-
335
- // *** RowSelection Handling Logic Starts Here
336
- const handleSelectAll = () => {
337
- setSelectedRows(workingDataSource);
338
- if (onSelectRow) onSelectRow(workingDataSource);
339
- };
340
-
341
- const handleSelect = (rowData: any) => {
342
- setSelectedRows((prevData) => {
343
- const isSelected = prevData.includes(rowData);
344
- let updatedData;
345
- if (isSelected) {
346
- updatedData = prevData.filter((item) => item !== rowData);
347
- } else {
348
- updatedData = [...prevData, rowData];
349
- }
350
- if (onSelectRow) onSelectRow(updatedData);
351
- return updatedData;
352
- });
353
- };
354
-
355
- const isRowSelected = (rowData: any) => {
356
- return selectedRows.includes(rowData);
357
- };
358
- // RowSelection Handling Logic Ends Here ***
359
-
360
- // Making Grid Functions Accessible in Parent
361
- useImperativeHandle(
362
- ref,
363
- () => {
364
- return {
365
- goToPage,
366
- nextPage,
367
- prevPage,
368
- goToFirstPage,
369
- goToEndPage,
370
- handleSearch,
371
- handleApplyFilter,
372
- clearFilter,
373
- };
374
- },
375
- []
376
- );
377
-
378
- // JSX Rendering
379
- return (
380
- <div className={gridContainerClass}>
381
- <React.Fragment>
382
- {/* Global utility logic */}
383
- <div
384
- className={`min-w-full flex justify-between items-center ${
385
- enableExcelExport || enableSearch || enablePdfExport
386
- ? "block"
387
- : "hidden"
388
- }`}
389
- >
390
- <div className="flex justify-end gap-2 px-1 py-3 flex-grow">
391
- {enableExcelExport && (
392
- <button
393
- className={gridButtonClass}
394
- onClick={() => {
395
- exportToExcelHelper(workingDataSource, columns, excelName);
396
- }}
397
- >
398
- Export as Excel
399
- </button>
400
- )}
401
- {enablePdfExport && (
402
- <button
403
- className={gridButtonClass}
404
- onClick={() => {
405
- exportToPDFHelper(
406
- workingDataSource,
407
- columns,
408
- pdfName,
409
- pdfOptions
410
- );
411
- }}
412
- >
413
- Export as PDF
414
- </button>
415
- )}
416
- {enableSearch && (
417
- <div className="flex gap-1">
418
- <input
419
- type="search"
420
- value={searchParam}
421
- onChange={handleSearchInput}
422
- placeholder="search"
423
- className="outline-none p-2 text-sm font-normal bg-gray-50 rounded-lg max-sm:hidden dark:bg-black dark:border dark:text-white"
424
- onKeyDown={(event: React.KeyboardEvent<HTMLInputElement>) => {
425
- if (event.key === "Enter") {
426
- handleSearch(searchParam);
427
- }
428
- }}
429
- />
430
- <button
431
- className="bg-white border rounded-lg text-black w-10 flex items-center justify-center dark:bg-black dark:text-white"
432
- onClick={() => handleSearch(searchParam)}
433
- >
434
- <Icon
435
- elements={search}
436
- svgClass={"stroke-gray-500 fill-none dark:stroke-white"}
437
- />
438
- </button>
439
- </div>
440
- )}
441
- </div>
442
- </div>
443
-
444
- <div className="overflow-x-auto">
445
- <table className="min-w-full">
446
- <thead>
447
- <tr>
448
- {selectAll && (
449
- <th className="text-left border-b border-t bg-gray-50 px-4 py-4 dark:bg-gray-700 dark:text-white">
450
- <input type="checkbox" onChange={handleSelectAll} />
451
- </th>
452
- )}
453
- {/* Rendering column headers */}
454
- {workingColumns.map((columnHeader: any, colIndex: number) => (
455
- <th
456
- key={colIndex}
457
- className={tableHeaderStyle}
458
- style={{
459
- width: columnHeader.width
460
- ? `${columnHeader.width}px`
461
- : "auto",
462
- }}
463
- >
464
- <div className="flex items-center relative">
465
- {columnHeader.headerText
466
- ? columnHeader.headerText
467
- : columnHeader.field}
468
- {columnHeader.filter && !columnHeader.template && (
469
- <React.Fragment>
470
- <button
471
- onClick={() => {
472
- toggleFilterPopup(colIndex);
473
- }}
474
- >
475
- <Icon
476
- dimensions={{ width: "12", height: "12" }}
477
- elements={listFilter}
478
- svgClass={`ml-2 fill-none dark:stroke-white ${
479
- activeFilterArray &&
480
- activeFilterArray.some(
481
- (filter: any) =>
482
- filter.filterColumn === columnHeader.field
483
- )
484
- ? "stroke-red-400"
485
- : "stroke-black"
486
- }`}
487
- />
488
- </button>
489
- <FilterPopup
490
- show={columnHeader.showFilterPopup}
491
- columnHeader={columnHeader.field}
492
- filterAction={(action: any) => {
493
- handleFilterAction(action, colIndex);
494
- }}
495
- />
496
- </React.Fragment>
497
- )}
498
- </div>
499
- </th>
500
- ))}
501
- </tr>
502
- </thead>
503
- <tbody>
504
- {/* rendering body */}
505
- {workingDataSource.length > 0 && !isFetching ? (
506
- workingDataSource
507
- .slice(
508
- currentPage * pageSettings.pageNumber,
509
- (currentPage + 1) * pageSettings.pageNumber
510
- )
511
- .map((rowData: any, rowIndex: number) => (
512
- <tr
513
- key={rowIndex}
514
- className="hover:bg-gray-50 dark:hover:bg-gray-900 cursor-pointer"
515
- >
516
- {selectAll && (
517
- <td className={gridColumnStyleSelectAll}>
518
- <input
519
- type="checkbox"
520
- checked={isRowSelected(rowData)}
521
- onChange={() => handleSelect(rowData)}
522
- />
523
- </td>
524
- )}
525
- {workingColumns.map((column: any, colIndex: number) => (
526
- <td
527
- key={colIndex}
528
- className={gridColumnStyle}
529
- style={{
530
- width: column.width ? `${column.width}px` : "auto",
531
- }}
532
- >
533
- {renderCell(rowData, column, rowIndex)}
534
- </td>
535
- ))}
536
- </tr>
537
- ))
538
- ) : (
539
- <tr>
540
- <td
541
- colSpan={workingColumns.length}
542
- className="text-center p-4 border-b text-sm"
543
- >
544
- {isFetching
545
- ? "Fetching Data Please Wait..."
546
- : "No Data Found"}
547
- </td>
548
- </tr>
549
- )}
550
- </tbody>
551
- </table>
552
- </div>
553
- {/* Pagination logic */}
554
- <div className="flex p-2 justify-between dark:text-white">
555
- <div className="flex gap-4">
556
- <button
557
- onClick={goToFirstPage}
558
- className={`${
559
- currentPage === 0 ? "text-gray-200 dark:text-gray-700" : ""
560
- }`}
561
- >
562
- <Icon
563
- elements={leftArrows}
564
- svgClass={`${
565
- currentPage === 0
566
- ? "stroke-gray-200 fill-none dark:stroke-gray-700"
567
- : "stroke-black fill-none dark:stroke-white"
568
- }`}
569
- />
570
- </button>
571
- <button onClick={prevPage}>
572
- <Icon
573
- elements={leftArrow}
574
- svgClass={`${
575
- currentPage === 0
576
- ? "stroke-gray-200 fill-none dark:stroke-gray-700"
577
- : "stroke-black fill-none dark:stroke-white"
578
- }`}
579
- />
580
- </button>
581
- <div className="flex flex-row gap-3 items-center">
582
- {pageStart > 0 && (
583
- <button
584
- className="p-1 w-5 h-5 flex items-center justify-center rounded-full"
585
- onClick={() => goToPage(pageStart - 1)}
586
- >
587
- ...
588
- </button>
589
- )}
590
- {workingDataSource.length > 0 &&
591
- Array.from({ length: pageEnd - pageStart }).map((_, i) => (
592
- <button
593
- key={i}
594
- onClick={() => goToPage(pageStart + i)}
595
- className={`${
596
- pageStart + i === currentPage
597
- ? "font-bold text-white p-2 h-6 bg-black flex items-center justify-center rounded-md w-auto dark:bg-white dark:text-black"
598
- : ""
599
- }`}
600
- >
601
- {pageStart + i + 1}
602
- </button>
603
- ))}
604
- {pageEnd < totalPages && (
605
- <button onClick={() => goToPage(pageEnd)}>...</button>
606
- )}
607
- </div>
608
- <button
609
- onClick={nextPage}
610
- className={`${
611
- currentPage === totalPages - 1 || workingDataSource.length <= 0
612
- ? "text-gray-200 dark:text-gray-700"
613
- : ""
614
- }`}
615
- >
616
- <Icon
617
- elements={rightArrow}
618
- svgClass={`${
619
- currentPage === totalPages - 1 ||
620
- workingDataSource.length <= 0
621
- ? "stroke-gray-200 fill-none dark:stroke-gray-700"
622
- : "stroke-black fill-none dark:stroke-white"
623
- }`}
624
- />
625
- </button>
626
- <button
627
- onClick={goToEndPage}
628
- className={`${
629
- currentPage === totalPages - 1 || workingDataSource.length <= 0
630
- ? "text-gray-200 dark:text-gray-700"
631
- : ""
632
- }`}
633
- >
634
- <Icon
635
- elements={rightArrows}
636
- svgClass={`${
637
- currentPage === totalPages - 1 ||
638
- workingDataSource.length <= 0
639
- ? "stroke-gray-200 fill-none dark:stroke-gray-700"
640
- : "stroke-black fill-none dark:stroke-white"
641
- }`}
642
- />
643
- </button>
644
- </div>
645
- <div className="flex text-sm">
646
- {currentPage + 1} of {totalPages} pages ({workingDataSource.length})
647
- items
648
- </div>
649
- </div>
650
- </React.Fragment>
651
- </div>
652
- );
653
- });
@@ -1,218 +0,0 @@
1
- import * as XLSX from "xlsx";
2
- import jsPDF from "jspdf";
3
- import "jspdf-autotable";
4
-
5
- let activeFilterArray: any[] = [];
6
-
7
- interface PDFExportOptions {
8
- layout: "portrait" | "landscape";
9
- paperSize:
10
- | "a3"
11
- | "a4"
12
- | "letter"
13
- | "legal"
14
- | "tabloid"
15
- | "statement"
16
- | "executive";
17
- }
18
-
19
- export function handleApplyFilterHelper(
20
- event: any,
21
- columns: any,
22
- dataSource: any
23
- ) {
24
- let workingDataSource: any[] = [...dataSource];
25
- let filterValue = event.filterValue.toLowerCase();
26
- let filterCondition = event.filterType;
27
- let filterColumn = event.columnHeader;
28
-
29
- // Add the new filter to activeFilterArray
30
- activeFilterArray.push({
31
- filterValue: filterValue,
32
- filterCondition: filterCondition,
33
- filterColumn: filterColumn,
34
- });
35
-
36
- columns = columns.map((column: any) => {
37
- if (column.field === filterColumn) {
38
- return { ...column, isFilterActive: true, showFilterPopup: false };
39
- }
40
- return column;
41
- });
42
-
43
- // Apply all active filters
44
- activeFilterArray.forEach((filter: any) => {
45
- workingDataSource = dataSource.filter((item: any) => {
46
- let columnValue = item[filter.filterColumn].toString().toLowerCase();
47
- switch (filter.filterCondition) {
48
- case "contains":
49
- return columnValue.includes(filter.filterValue);
50
- case "equals":
51
- return columnValue === filter.filterValue;
52
- case "starts_with":
53
- return columnValue.startsWith(filter.filterValue);
54
- case "ends_with":
55
- return columnValue.endsWith(filter.filterValue);
56
- default:
57
- return true;
58
- }
59
- });
60
- });
61
-
62
- return { columns, workingDataSource, activeFilterArray };
63
- }
64
-
65
- // Function For Clearing the Filter
66
- export function clearFilterHelper(
67
- event: any,
68
- columns: any[],
69
- dataSource: any[]
70
- ) {
71
- let workingDataSource = [...dataSource];
72
- let filterColumn = event.columnHeader;
73
-
74
- // Remove the cleared filter from activeFilterArray
75
- activeFilterArray = activeFilterArray.filter(
76
- (filter: any) => filter.filterColumn !== filterColumn
77
- );
78
-
79
- let hasActiveFilters = activeFilterArray.length > 0;
80
-
81
- // Reset the isFilterActive and showFilterPopup flags for the cleared filter
82
- columns = columns.map((column: any) => {
83
- if (column.field === filterColumn) {
84
- return { ...column, isFilterActive: false, showFilterPopup: false };
85
- }
86
- return column;
87
- });
88
-
89
- // Reapply all remaining active filters
90
- if (hasActiveFilters) {
91
- activeFilterArray.forEach((filter: any) => {
92
- workingDataSource = workingDataSource.filter((item: any) => {
93
- let columnValue = item[filter.filterColumn].toString().toLowerCase();
94
- switch (filter.filterCondition) {
95
- case "contains":
96
- return columnValue.includes(filter.filterValue);
97
- case "equals":
98
- return columnValue === filter.filterValue;
99
- case "starts_with":
100
- return columnValue.startsWith(filter.filterValue);
101
- case "ends_with":
102
- return columnValue.endsWith(filter.filterValue);
103
- default:
104
- return true;
105
- }
106
- });
107
- });
108
- }
109
-
110
- return { columns, workingDataSource, hasActiveFilters, activeFilterArray };
111
- }
112
-
113
- export function exportToExcelHelper(
114
- dataSource: any[],
115
- columns: any[],
116
- excelName: string
117
- ) {
118
- // This will remove the column template.
119
- const templateRemovedColumn = columns.filter((column) => !column.template);
120
- const dataToExport = dataSource.map((row) => {
121
- const rowData: any = {};
122
- templateRemovedColumn.forEach((column: any) => {
123
- rowData[column.field] = row[column.field];
124
- });
125
- return rowData;
126
- });
127
- const ws = XLSX.utils.json_to_sheet(dataToExport);
128
- const wb = XLSX.utils.book_new();
129
- XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
130
- XLSX.writeFile(wb, `${excelName}.xlsx`);
131
- }
132
-
133
- export function exportToPDFHelper(
134
- dataSource: any[],
135
- columns: any[],
136
- pdfName: string,
137
- pdfoptions?: PDFExportOptions
138
- ) {
139
- const { layout = "portrait", paperSize = "a4" }: any = pdfoptions;
140
-
141
- const doc: any = new jsPDF({
142
- orientation: layout,
143
- unit: "mm",
144
- format: paperSize,
145
- });
146
- // This will remove the columns with template.
147
- const printableColumns = columns.filter(
148
- (column) => !column.template || column.showInPdf
149
- );
150
-
151
- const dataToExport = dataSource.map((row) => {
152
- const rowData: any = {};
153
- printableColumns.forEach((column: any) => {
154
- rowData[column.field] = row[column.field];
155
- });
156
- return Object.values(rowData);
157
- });
158
-
159
- const columnNames = printableColumns.map((column: any) => column.field);
160
- doc.autoTable({
161
- head: [columnNames],
162
- body: dataToExport,
163
- });
164
-
165
- doc.save(`${pdfName}.pdf`);
166
- }
167
-
168
- export function handleEditActionHelper(
169
- e: any,
170
- isEditModeActive: boolean,
171
- actionMode: string,
172
- newEntry: any,
173
- workingDataSource: any[],
174
- goToFirstPage: () => void
175
- ) {
176
- const { mode } = e.detail;
177
- let dataSourceUpdate = [...workingDataSource];
178
- let isEditModeActiveUpdate: boolean = isEditModeActive;
179
- let actionModeUpdate: string = actionMode;
180
- let newEntryUpdate: any = { ...newEntry };
181
-
182
- function resetEditMode() {
183
- isEditModeActiveUpdate = false;
184
- actionModeUpdate = "";
185
- newEntryUpdate = {};
186
- }
187
-
188
- function addNewEntry() {
189
- if (Object.keys(newEntryUpdate).length > 0) {
190
- dataSourceUpdate = [newEntryUpdate, ...workingDataSource];
191
- newEntryUpdate = {};
192
- }
193
- }
194
-
195
- switch (mode) {
196
- case "add":
197
- isEditModeActiveUpdate = true;
198
- actionModeUpdate = mode;
199
- goToFirstPage();
200
- break;
201
- case "cancel":
202
- resetEditMode();
203
- break;
204
- case "update":
205
- addNewEntry();
206
- resetEditMode();
207
- break;
208
- default:
209
- break;
210
- }
211
-
212
- return {
213
- dataSourceUpdate,
214
- isEditModeActiveUpdate,
215
- actionModeUpdate,
216
- newEntryUpdate,
217
- };
218
- }
@@ -1,26 +0,0 @@
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 "./Grid";
12
- import React, { memo } from "react";
13
-
14
- const Grid = 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 { Grid };
@@ -1,30 +0,0 @@
1
- interface PageSettingsProps {
2
- pageNumber: number;
3
- }
4
-
5
- export type GridProps = {
6
- dataSource: any[] | string;
7
- columns?: any[];
8
- pageSettings: PageSettingsProps;
9
- enableSearch?: boolean;
10
- lazy?: boolean;
11
- enableExcelExport?: boolean;
12
- excelName?: string;
13
- enablePdfExport?: boolean;
14
- pdfName?: string;
15
- gridContainerClass?: string;
16
- gridButtonClass?: string;
17
- gridHeaderClass?: string;
18
- gridGlobalSearchButtonClass?: string;
19
- gridPaginationButtonClass?: string;
20
- pdfOptions?: any;
21
- isFetching?: boolean;
22
- showPagination?: boolean;
23
- selectAll?: boolean;
24
- onSelectRow?: (value: any) => void;
25
- tableHeaderStyle?: string;
26
- gridColumnStyleSelectAll?: string;
27
- gridColumnStyle?: string;
28
- rowChange?: any;
29
- pageStatus?: any;
30
- };