gbs-add-block 0.0.59 → 0.0.61

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.58)
1
+ # GBS Building Blocks 2.0 (v0.0.61)
2
2
 
3
3
  Latest and upgraded version of GBS building blocks with headless UI and removed dependencies.
4
4
 
@@ -6,11 +6,11 @@ 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.58)
9
+ ## What's New 🎉 (Ver 0.0.61)
10
10
 
11
11
  - Updated for bug fixes.
12
12
  - New component "Skeleton" & "Navbar" Added.
13
-
13
+ - Planned Deprecation of Current Grid Component and Adding Beta version for the New Data Grid.
14
14
 
15
15
  ## Authors
16
16
 
package/index.js CHANGED
@@ -26,6 +26,7 @@ const CONFIG = {
26
26
  "ContextMenu",
27
27
  "Skeleton",
28
28
  "Navbar",
29
+ "DataGrid",
29
30
  ],
30
31
  // Define component dependencies
31
32
  dependencies: {
@@ -95,7 +96,10 @@ const installComponentWithDependencies = async (component, destPath) => {
95
96
 
96
97
  if (pendingInstalls.length === 0) {
97
98
  console.log(
98
- `✓ ${component} and all its dependencies are already installed.`
99
+ `✓ ${component} and all its dependencies are already installed. ${
100
+ component === "Grid" &&
101
+ "This Version of Grid will be deprecated soon. Please Install The New Data Grid Component"
102
+ }`
99
103
  );
100
104
  return;
101
105
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gbs-add-block",
3
- "version": "0.0.59",
3
+ "version": "0.0.61",
4
4
  "description": "React Component Library",
5
5
  "files": [
6
6
  "index.js",
@@ -0,0 +1,419 @@
1
+ import React, {
2
+ createContext,
3
+ useContext,
4
+ useState,
5
+ useCallback,
6
+ useEffect,
7
+ } from "react";
8
+ import { getSourceData } from "../../utils";
9
+ 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
+
35
+ // Search methods
36
+ handleSearchInput: (e: any) => void;
37
+ handleSearch: (searchParam: string) => void;
38
+
39
+ // Filter methods
40
+ toggleFilterPopup: (index: number) => void;
41
+ handleApplyFilter: (event: any) => void;
42
+ clearFilter: (event: any) => void;
43
+ handleFilterAction: (action: any, colIndex: number) => void;
44
+
45
+ // Row selection methods
46
+ handleSelectAll: (event: React.ChangeEvent<HTMLInputElement>) => void;
47
+ handleSelect: (rowData: any) => void;
48
+ isRowSelected: (rowData: any) => boolean;
49
+
50
+ // Grid settings
51
+ columns: any[];
52
+ pageSettings: any;
53
+ enableSearch: boolean;
54
+ enableExcelExport: boolean;
55
+ enablePdfExport: boolean;
56
+ excelName: string;
57
+ pdfName: string;
58
+ pdfOptions: any;
59
+ gridButtonClass: string;
60
+ selectAll: boolean;
61
+ tableHeaderStyle: string;
62
+ gridContainerClass: string;
63
+ gridColumnStyleSelectAll: string;
64
+ gridColumnStyle: string;
65
+ rowChange: (rowData: any) => void;
66
+ }
67
+
68
+ const GridContext = createContext<GridContextType | undefined>(undefined);
69
+
70
+ export const GridProvider: React.FC<{
71
+ children: React.ReactNode;
72
+ props: GridProps;
73
+ }> = ({ children, props }) => {
74
+ const {
75
+ dataSource,
76
+ columns = [],
77
+ pageSettings,
78
+ enableSearch = false,
79
+ lazy = false,
80
+ enableExcelExport = false,
81
+ excelName = "data",
82
+ enablePdfExport = false,
83
+ pdfName = "data",
84
+ pdfOptions = {},
85
+ gridButtonClass = "px-1 py-2 text-xs rounded bg-zinc-200 dark:bg-zinc-700",
86
+ selectAll = false,
87
+ onSelectRow,
88
+ isFetching,
89
+ tableHeaderStyle = "text-left px-2 py-4 bg-zinc-200 dark:bg-zinc-800",
90
+ gridContainerClass = "flex flex-col min-w-screen rounded-md overflow-hidden",
91
+ gridColumnStyleSelectAll = "px-4 text-xs",
92
+ gridColumnStyle = "p-2 text-xs",
93
+ rowChange = () => {},
94
+ pageStatus = () => {},
95
+ } = props;
96
+
97
+ // States Handling Grid
98
+ const [workingDataSource, setWorkingDataSource] = useState<any>([]);
99
+ const [fallbackSourceData, setFallbackSourceData] = useState([]);
100
+ const [workingColumns, setWorkingColumns] = useState<any>([]);
101
+ const [currentPage, setCurrentPage] = useState(0);
102
+ const [pageStart, setPageStart] = useState(0);
103
+ const [pageEnd, setPageEnd] = useState(10);
104
+ const [totalPages, setTotalPages] = useState(0);
105
+ const [searchParam, setSearchParam] = useState("");
106
+ const [activeFilterArray, setActiveFilterArray] = useState<any>([]);
107
+ const [selectedRows, setSelectedRows] = useState<any[]>([]);
108
+
109
+ // This will returns the Page Navigation Status of Grid
110
+ useEffect(() => {
111
+ if (pageStatus) {
112
+ pageStatus({ currentPage: currentPage, totalPages: totalPages });
113
+ }
114
+ }, [pageStatus, currentPage, totalPages]);
115
+
116
+ // Function to handle API datasource
117
+ const getGridData = useCallback(async () => {
118
+ try {
119
+ const sourceData = await getSourceData(dataSource);
120
+ const gridData = sourceData.sourcedata;
121
+ setFallbackSourceData(gridData);
122
+ setWorkingDataSource(gridData);
123
+ const totalPages = Math.ceil(gridData.length / pageSettings.pageNumber);
124
+ setTotalPages(totalPages);
125
+ setPageEnd(Math.min(10, totalPages));
126
+ } catch (error) {
127
+ console.error("Error fetching grid source data:", error);
128
+ }
129
+ }, [dataSource, pageSettings.pageNumber]);
130
+
131
+ // Calculates total pages and determine dataSource type
132
+ useEffect(() => {
133
+ const handleDataSource = async () => {
134
+ if (Array.isArray(dataSource) && dataSource.length > 0) {
135
+ setWorkingDataSource(dataSource);
136
+ const totalPages = Math.ceil(
137
+ dataSource.length / pageSettings.pageNumber
138
+ );
139
+ setTotalPages(totalPages);
140
+ setPageEnd(Math.min(10, totalPages));
141
+ } else if (typeof dataSource === "string") {
142
+ await getGridData();
143
+ }
144
+ };
145
+
146
+ handleDataSource();
147
+ }, [dataSource, pageSettings, getGridData]);
148
+
149
+ // Adds Filter Column
150
+ useEffect(() => {
151
+ if (columns && columns.length > 0) {
152
+ const filteredColumns = columns.map((column) => ({
153
+ ...column,
154
+ showFilterPopup: false,
155
+ isFilterActive: false,
156
+ }));
157
+ setWorkingColumns(filteredColumns);
158
+ }
159
+ }, [columns]);
160
+
161
+ useEffect(() => {
162
+ if (!columns || columns.length === 0) {
163
+ if (
164
+ workingDataSource.length > 0 &&
165
+ Object.keys(workingDataSource[0]).length > 0
166
+ ) {
167
+ const inferredColumns = Object.keys(workingDataSource[0]).map(
168
+ (key) => ({
169
+ field: key,
170
+ headerText: key.charAt(0).toUpperCase() + key.slice(1),
171
+ width: 150,
172
+ })
173
+ );
174
+ setWorkingColumns((prevColumns: any) => {
175
+ if (JSON.stringify(prevColumns) !== JSON.stringify(inferredColumns)) {
176
+ return inferredColumns;
177
+ }
178
+ return prevColumns;
179
+ });
180
+ }
181
+ }
182
+ }, [columns, workingDataSource]);
183
+
184
+ // *** Page Navigation Helper Methods
185
+ const updatePageRange = (page: number) => {
186
+ const start = Math.floor(page / 10) * 10;
187
+ setPageStart(start);
188
+ setPageEnd(Math.min(start + 10, totalPages));
189
+ };
190
+
191
+ const nextPage = () => {
192
+ setCurrentPage((prevPage) => {
193
+ if (prevPage < totalPages - 1) {
194
+ const newPage = prevPage + 1;
195
+ updatePageRange(newPage);
196
+ return newPage;
197
+ }
198
+ return prevPage;
199
+ });
200
+ };
201
+
202
+ const prevPage = () => {
203
+ setCurrentPage((prevPage) => {
204
+ if (prevPage > 0) {
205
+ const newPage = prevPage - 1;
206
+ updatePageRange(newPage);
207
+ return newPage;
208
+ }
209
+ return prevPage;
210
+ });
211
+ };
212
+
213
+ const goToEndPage = () => {
214
+ const lastPage = totalPages - 1;
215
+ setCurrentPage(lastPage);
216
+ updatePageRange(lastPage);
217
+ };
218
+
219
+ const goToFirstPage = () => {
220
+ setCurrentPage(0);
221
+ updatePageRange(0);
222
+ };
223
+
224
+ const goToPage = (page: number) => {
225
+ setCurrentPage(page);
226
+ updatePageRange(page);
227
+ };
228
+
229
+ // *** Search Functions
230
+ const handleSearchInput = (e: any) => {
231
+ if (!lazy) {
232
+ const searchValue = e.target.value;
233
+ setSearchParam(searchValue);
234
+ if (searchValue === "") {
235
+ setWorkingDataSource(
236
+ fallbackSourceData.length > 0 ? fallbackSourceData : dataSource
237
+ );
238
+ const fallback =
239
+ fallbackSourceData.length > 0 ? fallbackSourceData : dataSource;
240
+ const totalPages = Math.ceil(fallback.length / pageSettings.pageNumber);
241
+ setTotalPages(totalPages);
242
+ setPageEnd(Math.min(pageStart + 10, totalPages));
243
+ }
244
+ }
245
+ };
246
+
247
+ const handleSearch = (searchParam: string) => {
248
+ const filteredData = workingDataSource.filter((item: any) =>
249
+ Object.values(item).some((val: any) => {
250
+ const trimmedVal = val.toString().toLowerCase().trim();
251
+ const trimmedSearchParam = searchParam.toLowerCase().trim();
252
+ return trimmedVal.includes(trimmedSearchParam);
253
+ })
254
+ );
255
+ setWorkingDataSource(filteredData);
256
+ resetPage(filteredData);
257
+ };
258
+
259
+ // *** Filter Functions
260
+ // Resetting pagination params for updating pagination
261
+ function resetPage(dataSource: any) {
262
+ const newTotalPages = Math.ceil(
263
+ dataSource.length / pageSettings.pageNumber
264
+ );
265
+
266
+ setTotalPages(newTotalPages);
267
+ setCurrentPage(0);
268
+ setPageStart(0);
269
+ setPageEnd(Math.min(10, newTotalPages));
270
+ }
271
+
272
+ const toggleFilterPopup = (index: number) => {
273
+ setWorkingColumns((prevColumns: any) =>
274
+ prevColumns.map((column: any, i: any) =>
275
+ i === index
276
+ ? { ...column, showFilterPopup: !column.showFilterPopup }
277
+ : column
278
+ )
279
+ );
280
+ };
281
+
282
+ function handleApplyFilter(event: any) {
283
+ const {
284
+ columns: updatedColumns,
285
+ workingDataSource: updatedFullDataSource,
286
+ activeFilterArray: updatedActiveFilterArray,
287
+ } = handleApplyFilterHelper(event, columns, workingDataSource);
288
+
289
+ setWorkingColumns(updatedColumns);
290
+ setWorkingDataSource(updatedFullDataSource);
291
+ setActiveFilterArray(updatedActiveFilterArray);
292
+ resetPage(updatedFullDataSource);
293
+ }
294
+
295
+ function clearFilter(event: any) {
296
+ const clearDataSource = Array.isArray(dataSource)
297
+ ? dataSource
298
+ : fallbackSourceData;
299
+ const {
300
+ columns: updatedColumns,
301
+ workingDataSource: updatedDataSource,
302
+ activeFilterArray: updatedActiveFilterArray,
303
+ } = clearFilterHelper(event, workingColumns, clearDataSource);
304
+
305
+ setWorkingColumns(updatedColumns);
306
+ setWorkingDataSource(updatedDataSource);
307
+ setActiveFilterArray(updatedActiveFilterArray);
308
+ resetPage(updatedDataSource);
309
+ }
310
+
311
+ const handleFilterAction = (action: any, colIndex: number) => {
312
+ switch (action.type) {
313
+ case "cancel":
314
+ toggleFilterPopup(colIndex);
315
+ break;
316
+ case "applyFilter":
317
+ handleApplyFilter(action);
318
+ break;
319
+ case "clearFilter":
320
+ clearFilter(action);
321
+ break;
322
+ default:
323
+ break;
324
+ }
325
+ };
326
+
327
+ // *** Row Selection Functions
328
+ const handleSelectAll = (event: React.ChangeEvent<HTMLInputElement>) => {
329
+ const selected = event.target.checked ? workingDataSource : [];
330
+ setSelectedRows(selected);
331
+ onSelectRow?.(selected);
332
+ };
333
+
334
+ const handleSelect = (rowData: any) => {
335
+ setSelectedRows((prevData) => {
336
+ const isSelected = prevData.includes(rowData);
337
+ let updatedData;
338
+ if (isSelected) {
339
+ updatedData = prevData.filter((item) => item !== rowData);
340
+ } else {
341
+ updatedData = [...prevData, rowData];
342
+ }
343
+ if (onSelectRow) onSelectRow(updatedData);
344
+ return updatedData;
345
+ });
346
+ };
347
+
348
+ const isRowSelected = (rowData: any) => {
349
+ return selectedRows.includes(rowData);
350
+ };
351
+
352
+ // Provide context value
353
+ const contextValue: GridContextType = {
354
+ // State
355
+ workingDataSource,
356
+ fallbackSourceData,
357
+ workingColumns,
358
+ currentPage,
359
+ pageStart,
360
+ pageEnd,
361
+ totalPages,
362
+ searchParam,
363
+ activeFilterArray,
364
+ selectedRows,
365
+ isFetching,
366
+
367
+ // Navigation methods
368
+ nextPage,
369
+ prevPage,
370
+ goToEndPage,
371
+ goToFirstPage,
372
+ goToPage,
373
+
374
+ // Search methods
375
+ handleSearchInput,
376
+ handleSearch,
377
+
378
+ // Filter methods
379
+ toggleFilterPopup,
380
+ handleApplyFilter,
381
+ clearFilter,
382
+ handleFilterAction,
383
+
384
+ // Row selection methods
385
+ handleSelectAll,
386
+ handleSelect,
387
+ isRowSelected,
388
+
389
+ // Grid settings
390
+ columns,
391
+ pageSettings,
392
+ enableSearch,
393
+ enableExcelExport,
394
+ enablePdfExport,
395
+ excelName,
396
+ pdfName,
397
+ pdfOptions,
398
+ gridButtonClass,
399
+ selectAll,
400
+ tableHeaderStyle,
401
+ gridContainerClass,
402
+ gridColumnStyleSelectAll,
403
+ gridColumnStyle,
404
+ rowChange,
405
+ };
406
+
407
+ return (
408
+ <GridContext.Provider value={contextValue}>{children}</GridContext.Provider>
409
+ );
410
+ };
411
+
412
+ // Base context hook
413
+ export const useGridContext = () => {
414
+ const context = useContext(GridContext);
415
+ if (context === undefined) {
416
+ throw new Error("useGridContext must be used within a GridProvider");
417
+ }
418
+ return context;
419
+ };
@@ -0,0 +1,98 @@
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
+ } = useGridContext();
17
+
18
+ return {
19
+ currentPage,
20
+ pageStart,
21
+ pageEnd,
22
+ totalPages,
23
+ goToFirstPage,
24
+ prevPage,
25
+ goToPage,
26
+ nextPage,
27
+ goToEndPage,
28
+ workingDataSource,
29
+ };
30
+ };
31
+
32
+ // Hook for grid search functionality
33
+ export const useGridSearch = () => {
34
+ const { searchParam, handleSearchInput, handleSearch, enableSearch } =
35
+ useGridContext();
36
+
37
+ return {
38
+ searchParam,
39
+ handleSearchInput,
40
+ handleSearch,
41
+ enableSearch,
42
+ };
43
+ };
44
+
45
+ // Hook for grid filter functionality
46
+ export const useGridFilter = () => {
47
+ const {
48
+ workingColumns,
49
+ activeFilterArray,
50
+ toggleFilterPopup,
51
+ handleFilterAction,
52
+ } = useGridContext();
53
+
54
+ return {
55
+ workingColumns,
56
+ activeFilterArray,
57
+ toggleFilterPopup,
58
+ handleFilterAction,
59
+ };
60
+ };
61
+
62
+ // Hook for row selection
63
+ export const useGridRowSelection = () => {
64
+ const { selectAll, handleSelectAll, handleSelect, isRowSelected } =
65
+ useGridContext();
66
+
67
+ return {
68
+ selectAll,
69
+ handleSelectAll,
70
+ handleSelect,
71
+ isRowSelected,
72
+ };
73
+ };
74
+
75
+ // Hook for export functionality
76
+ export const useGridExport = () => {
77
+ const {
78
+ enableExcelExport,
79
+ enablePdfExport,
80
+ workingDataSource,
81
+ columns,
82
+ excelName,
83
+ pdfName,
84
+ pdfOptions,
85
+ gridButtonClass,
86
+ } = useGridContext();
87
+
88
+ return {
89
+ enableExcelExport,
90
+ enablePdfExport,
91
+ workingDataSource,
92
+ columns,
93
+ excelName,
94
+ pdfName,
95
+ pdfOptions,
96
+ gridButtonClass,
97
+ };
98
+ };
@@ -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 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 };
@@ -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
+ }