@visns-studio/visns-components 5.6.10 → 5.6.11

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
@@ -191,12 +191,28 @@ A powerful data table component with sorting, filtering, and pagination.
191
191
  params: {},
192
192
  }}
193
193
  columns={[
194
- { field: 'id', headerName: 'ID', width: 90 },
195
- { field: 'name', headerName: 'Name', width: 150 },
194
+ { name: 'id', headerName: 'ID', width: 90 },
195
+ { name: 'name', headerName: 'Name', width: 150 },
196
+ {
197
+ name: 'department',
198
+ headerName: 'Department',
199
+ width: 180,
200
+ groupSummaryReducer: 'count',
201
+ groupSummaryRenderer: (count) => `${count} employees`,
202
+ },
203
+ {
204
+ name: 'salary',
205
+ headerName: 'Salary',
206
+ width: 120,
207
+ type: 'number',
208
+ groupSummaryReducer: 'sum',
209
+ groupSummaryRenderer: (sum) => `$${sum.toLocaleString()}`,
210
+ },
196
211
  // More columns...
197
212
  ]}
198
213
  form={formConfig}
199
214
  gridHeight={500}
215
+ defaultGroupBy={[{ name: 'department' }]}
200
216
  settings={{
201
217
  pagination: true,
202
218
  pageSize: 25,
@@ -214,16 +230,26 @@ A powerful data table component with sorting, filtering, and pagination.
214
230
 
215
231
  #### DataGrid Props
216
232
 
217
- | Prop | Description | Type | Default |
218
- | ------------- | ---------------------------------- | -------- | ------- |
219
- | ajaxSetting | Configuration for data fetching | Object | - |
220
- | columns | Column definitions | Array | - |
221
- | form | Form configuration for editing | Object | - |
222
- | gridHeight | Height of the grid | Number | 500 |
223
- | settings | Grid settings (pagination, etc.) | Object | - |
224
- | setFilterData | Callback for filter changes | Function | - |
225
- | setConfig | Callback for configuration changes | Function | - |
226
- | style | Custom styles | Object | - |
233
+ | Prop | Description | Type | Default |
234
+ | --------------------- | ------------------------------------------ | -------- | ------- |
235
+ | ajaxSetting | Configuration for data fetching | Object | - |
236
+ | columns | Column definitions | Array | - |
237
+ | defaultExpandedNodes | Initial expanded nodes for TreeGrid | Object | - |
238
+ | defaultGroupBy | Initial grouping configuration | Array | - |
239
+ | expandedNodes | Current expanded nodes for TreeGrid | Object | - |
240
+ | form | Form configuration for editing | Object | - |
241
+ | gridHeight | Height of the grid | Number | 500 |
242
+ | groupBy | Current grouping configuration | Array | - |
243
+ | loadNode | Function to load TreeGrid nodes on demand | Function | - |
244
+ | onExpandedNodesChange | Callback for TreeGrid node expand/collapse | Function | - |
245
+ | onGroupByChange | Callback for grouping changes | Function | - |
246
+ | renderGroupTitle | Custom renderer for group headers | Function | - |
247
+ | settings | Grid settings (pagination, etc.) | Object | - |
248
+ | setFilterData | Callback for filter changes | Function | - |
249
+ | setConfig | Callback for configuration changes | Function | - |
250
+ | style | Custom styles | Object | - |
251
+ | treeColumn | Column name to display TreeGrid hierarchy | String | - |
252
+ | treeNestingSize | Indentation size for TreeGrid levels | Number | 20 |
227
253
 
228
254
  #### Auto-Refresh Functionality
229
255
 
@@ -253,6 +279,444 @@ When auto-refresh is configured:
253
279
  - Setting `autoRefresh: true` uses the default 30-second interval
254
280
  - The component silently refreshes data without showing notifications
255
281
 
282
+ #### Group By Functionality
283
+
284
+ The DataGrid component supports grouping data by one or more columns, allowing you to organize and visualize data in hierarchical structures.
285
+
286
+ ##### Using defaultGroupBy
287
+
288
+ The `defaultGroupBy` property allows you to specify initial grouping when the DataGrid loads:
289
+
290
+ ```jsx
291
+ <DataGrid
292
+ // Other props...
293
+ defaultGroupBy={[
294
+ { name: 'category' }, // Group by a single column
295
+ ]}
296
+ />
297
+ ```
298
+
299
+ For more complex grouping with multiple levels:
300
+
301
+ ```jsx
302
+ <DataGrid
303
+ // Other props...
304
+ defaultGroupBy={[
305
+ { name: 'department' }, // First level grouping
306
+ { name: 'role' }, // Second level grouping
307
+ ]}
308
+ />
309
+ ```
310
+
311
+ ##### Dynamic Grouping
312
+
313
+ You can also control grouping programmatically:
314
+
315
+ ```jsx
316
+ const [groupBy, setGroupBy] = useState([{ name: 'category' }]);
317
+
318
+ // Later in your component
319
+ <DataGrid
320
+ // Other props...
321
+ groupBy={groupBy}
322
+ onGroupByChange={setGroupBy}
323
+ />;
324
+ ```
325
+
326
+ ##### Server-Side Grouping
327
+
328
+ For server-side grouping, the DataGrid sends the groupBy information to your API endpoint:
329
+
330
+ ```jsx
331
+ <DataGrid
332
+ ajaxSetting={{
333
+ url: '/api/data',
334
+ method: 'POST',
335
+ // Other settings...
336
+ }}
337
+ defaultGroupBy={[{ name: 'category' }]}
338
+ // Other props...
339
+ />
340
+ ```
341
+
342
+ Your server will receive a request with groupBy information:
343
+
344
+ ```json
345
+ {
346
+ "page": 1,
347
+ "take": 25,
348
+ "groupBy": [{ "name": "category" }]
349
+ // Other parameters...
350
+ }
351
+ ```
352
+
353
+ ##### Group Summaries
354
+
355
+ You can add summary calculations to grouped data:
356
+
357
+ ```jsx
358
+ <DataGrid
359
+ // Other props...
360
+ columns={[
361
+ {
362
+ name: 'amount',
363
+ headerName: 'Amount',
364
+ type: 'number',
365
+ groupSummaryReducer: 'sum', // Options: 'sum', 'avg', 'min', 'max', 'count'
366
+ groupSummaryRenderer: (summary) => `Total: $${summary.toFixed(2)}`,
367
+ },
368
+ // Other columns...
369
+ ]}
370
+ defaultGroupBy={[{ name: 'category' }]}
371
+ />
372
+ ```
373
+
374
+ ##### Customizing Group Rendering
375
+
376
+ You can customize how groups are displayed:
377
+
378
+ ```jsx
379
+ <DataGrid
380
+ // Other props...
381
+ renderGroupTitle={(groupData) => (
382
+ <div style={{ fontWeight: 'bold', color: 'var(--primary-color)' }}>
383
+ {groupData.name}: {groupData.value} ({groupData.count} items)
384
+ </div>
385
+ )}
386
+ defaultGroupBy={[{ name: 'category' }]}
387
+ />
388
+ ```
389
+
390
+ ##### Complete Example with Dynamic Grouping
391
+
392
+ Here's a complete example of a component that uses DataGrid with dynamic grouping controls:
393
+
394
+ ```jsx
395
+ import React, { useState, useEffect } from 'react';
396
+ import { DataGrid } from 'visns-components';
397
+
398
+ const EmployeeDataGrid = () => {
399
+ // State for grouping
400
+ const [groupBy, setGroupBy] = useState([]);
401
+
402
+ // Sample data for the grid
403
+ const [employees, setEmployees] = useState([]);
404
+
405
+ // Fetch data on component mount
406
+ useEffect(() => {
407
+ const fetchEmployees = async () => {
408
+ try {
409
+ const response = await fetch('/api/employees');
410
+ const data = await response.json();
411
+ setEmployees(data);
412
+ } catch (error) {
413
+ console.error('Error fetching employees:', error);
414
+ }
415
+ };
416
+
417
+ fetchEmployees();
418
+ }, []);
419
+
420
+ // Column definitions
421
+ const columns = [
422
+ {
423
+ name: 'id',
424
+ headerName: 'ID',
425
+ width: 80,
426
+ },
427
+ {
428
+ name: 'name',
429
+ headerName: 'Name',
430
+ width: 180,
431
+ },
432
+ {
433
+ name: 'department',
434
+ headerName: 'Department',
435
+ width: 150,
436
+ groupSummaryReducer: 'count',
437
+ groupSummaryRenderer: (count) => `${count} employees`,
438
+ },
439
+ {
440
+ name: 'role',
441
+ headerName: 'Role',
442
+ width: 150,
443
+ },
444
+ {
445
+ name: 'salary',
446
+ headerName: 'Salary',
447
+ width: 120,
448
+ type: 'number',
449
+ groupSummaryReducer: 'sum',
450
+ groupSummaryRenderer: (sum) => `$${sum.toLocaleString()}`,
451
+ },
452
+ {
453
+ name: 'hireDate',
454
+ headerName: 'Hire Date',
455
+ width: 120,
456
+ type: 'date',
457
+ },
458
+ ];
459
+
460
+ // Group by options for the dropdown
461
+ const groupByOptions = [
462
+ { label: 'No Grouping', value: 'none' },
463
+ { label: 'Department', value: 'department' },
464
+ { label: 'Role', value: 'role' },
465
+ { label: 'Department & Role', value: 'department-role' },
466
+ ];
467
+
468
+ // Handle group by change
469
+ const handleGroupByChange = (e) => {
470
+ const value = e.target.value;
471
+
472
+ switch (value) {
473
+ case 'department':
474
+ setGroupBy([{ name: 'department' }]);
475
+ break;
476
+ case 'role':
477
+ setGroupBy([{ name: 'role' }]);
478
+ break;
479
+ case 'department-role':
480
+ setGroupBy([{ name: 'department' }, { name: 'role' }]);
481
+ break;
482
+ default:
483
+ setGroupBy([]);
484
+ break;
485
+ }
486
+ };
487
+
488
+ return (
489
+ <div>
490
+ <div style={{ marginBottom: '1rem' }}>
491
+ <label htmlFor="groupBy">Group By: </label>
492
+ <select
493
+ id="groupBy"
494
+ onChange={handleGroupByChange}
495
+ style={{ padding: '0.5rem', borderRadius: '4px' }}
496
+ >
497
+ {groupByOptions.map((option) => (
498
+ <option key={option.value} value={option.value}>
499
+ {option.label}
500
+ </option>
501
+ ))}
502
+ </select>
503
+ </div>
504
+
505
+ <DataGrid
506
+ columns={columns}
507
+ dataSource={employees}
508
+ groupBy={groupBy}
509
+ onGroupByChange={setGroupBy}
510
+ gridHeight={600}
511
+ settings={{
512
+ pagination: true,
513
+ pageSize: 25,
514
+ }}
515
+ renderGroupTitle={(groupData) => (
516
+ <div
517
+ style={{
518
+ fontWeight: 'bold',
519
+ color: 'var(--primary-color)',
520
+ padding: '0.25rem 0',
521
+ }}
522
+ >
523
+ {groupData.name === 'department'
524
+ ? 'Department'
525
+ : 'Role'}
526
+ :<span style={{ marginLeft: '0.5rem' }}>
527
+ {groupData.value} ({groupData.count} employees)
528
+ </span>
529
+ </div>
530
+ )}
531
+ />
532
+ </div>
533
+ );
534
+ };
535
+
536
+ export default EmployeeDataGrid;
537
+ ```
538
+
539
+ #### TreeGrid Functionality
540
+
541
+ The DataGrid component also supports hierarchical data display through its TreeGrid functionality. This allows you to represent parent-child relationships in your data with expandable/collapsible nodes.
542
+
543
+ ##### Basic TreeGrid Configuration
544
+
545
+ To use the TreeGrid functionality:
546
+
547
+ ```jsx
548
+ <DataGrid
549
+ // Other props...
550
+ treeColumn="name" // Specify which column will display the expand/collapse icons
551
+ dataSource={hierarchicalData} // Data with parent-child relationships
552
+ />
553
+ ```
554
+
555
+ Your data should have a structure where parent nodes contain child nodes:
556
+
557
+ ```jsx
558
+ const hierarchicalData = [
559
+ {
560
+ id: 1,
561
+ name: 'Parent 1',
562
+ nodes: [
563
+ // Child nodes
564
+ { id: 11, name: 'Child 1.1' },
565
+ {
566
+ id: 12,
567
+ name: 'Child 1.2',
568
+ nodes: [
569
+ // Nested children
570
+ { id: 121, name: 'Grandchild 1.2.1' },
571
+ ],
572
+ },
573
+ ],
574
+ },
575
+ {
576
+ id: 2,
577
+ name: 'Parent 2',
578
+ nodes: [{ id: 21, name: 'Child 2.1' }],
579
+ },
580
+ ];
581
+ ```
582
+
583
+ ##### Controlling Expanded Nodes
584
+
585
+ You can control which nodes are expanded:
586
+
587
+ ```jsx
588
+ const [expandedNodes, setExpandedNodes] = useState({
589
+ 1: true, // Node with ID 1 is expanded
590
+ '1/12': true, // Node with path 1/12 is expanded
591
+ });
592
+
593
+ <DataGrid
594
+ // Other props...
595
+ treeColumn="name"
596
+ dataSource={hierarchicalData}
597
+ expandedNodes={expandedNodes}
598
+ onExpandedNodesChange={setExpandedNodes}
599
+ />;
600
+ ```
601
+
602
+ ##### Asynchronous Node Loading
603
+
604
+ For large datasets, you can load child nodes on demand:
605
+
606
+ ```jsx
607
+ <DataGrid
608
+ // Other props...
609
+ treeColumn="name"
610
+ dataSource={asyncHierarchicalData}
611
+ loadNode={({ node }) => {
612
+ // Return a Promise that resolves to the child nodes
613
+ return fetchChildNodes(node.id);
614
+ }}
615
+ />
616
+ ```
617
+
618
+ For asynchronous loading, parent nodes should have `nodes: null` to indicate they have children that need to be loaded.
619
+
620
+ ##### Complete TreeGrid Example
621
+
622
+ Here's a complete example of a component that uses DataGrid with TreeGrid functionality:
623
+
624
+ ```jsx
625
+ import React, { useState } from 'react';
626
+ import { DataGrid } from 'visns-components';
627
+
628
+ const OrganizationTreeGrid = () => {
629
+ // Sample hierarchical data
630
+ const departments = [
631
+ {
632
+ id: 1,
633
+ name: 'Executive',
634
+ budget: 1500000,
635
+ nodes: [
636
+ {
637
+ id: 11,
638
+ name: 'Finance',
639
+ budget: 850000,
640
+ nodes: [
641
+ { id: 111, name: 'Accounting', budget: 350000 },
642
+ { id: 112, name: 'Investment', budget: 500000 },
643
+ ],
644
+ },
645
+ {
646
+ id: 12,
647
+ name: 'Operations',
648
+ budget: 650000,
649
+ nodes: [
650
+ { id: 121, name: 'HR', budget: 200000 },
651
+ { id: 122, name: 'Facilities', budget: 450000 },
652
+ ],
653
+ },
654
+ ],
655
+ },
656
+ {
657
+ id: 2,
658
+ name: 'Sales & Marketing',
659
+ budget: 1200000,
660
+ nodes: [
661
+ { id: 21, name: 'Sales', budget: 700000 },
662
+ { id: 22, name: 'Marketing', budget: 500000 },
663
+ ],
664
+ },
665
+ {
666
+ id: 3,
667
+ name: 'Technology',
668
+ budget: 2000000,
669
+ nodes: [
670
+ { id: 31, name: 'Development', budget: 1200000 },
671
+ { id: 32, name: 'IT Support', budget: 800000 },
672
+ ],
673
+ },
674
+ ];
675
+
676
+ // State for expanded nodes
677
+ const [expandedNodes, setExpandedNodes] = useState({
678
+ 1: true, // Executive department is expanded by default
679
+ 3: true, // Technology department is expanded by default
680
+ });
681
+
682
+ // Column definitions
683
+ const columns = [
684
+ {
685
+ name: 'name',
686
+ headerName: 'Department',
687
+ width: 250,
688
+ // This column will display the tree expand/collapse icons
689
+ },
690
+ {
691
+ name: 'budget',
692
+ headerName: 'Budget',
693
+ width: 150,
694
+ type: 'number',
695
+ render: ({ value }) => `$${value.toLocaleString()}`,
696
+ },
697
+ ];
698
+
699
+ return (
700
+ <div>
701
+ <h2>Organization Structure</h2>
702
+
703
+ <DataGrid
704
+ columns={columns}
705
+ dataSource={departments}
706
+ treeColumn="name"
707
+ expandedNodes={expandedNodes}
708
+ onExpandedNodesChange={setExpandedNodes}
709
+ treeNestingSize={30} // Indentation size for nested levels
710
+ gridHeight={500}
711
+ idProperty="id"
712
+ />
713
+ </div>
714
+ );
715
+ };
716
+
717
+ export default OrganizationTreeGrid;
718
+ ```
719
+
256
720
  ### Form Components
257
721
 
258
722
  #### Form
package/package.json CHANGED
@@ -84,7 +84,7 @@
84
84
  "react-dom": "^17.0.0 || ^18.0.0"
85
85
  },
86
86
  "name": "@visns-studio/visns-components",
87
- "version": "5.6.10",
87
+ "version": "5.6.11",
88
88
  "description": "Various packages to assist in the development of our Custom Applications.",
89
89
  "main": "src/index.js",
90
90
  "files": [
@@ -3206,6 +3206,22 @@ const DataGrid = forwardRef(
3206
3206
  };
3207
3207
  }, []);
3208
3208
 
3209
+ // Reset state when ajaxSetting changes (e.g., when navigating to a new page)
3210
+ useEffect(() => {
3211
+ // Reset selected state
3212
+ setSelected({});
3213
+
3214
+ // Reset filter value if needed
3215
+ if (setFilterData) {
3216
+ setFilterData([]);
3217
+ }
3218
+
3219
+ // Force reload of data
3220
+ if (gridRef.current && gridRef.current.paginationProps) {
3221
+ gridRef.current.paginationProps.reload();
3222
+ }
3223
+ }, [ajaxSetting?.url]);
3224
+
3209
3225
  // Auto-refresh effect
3210
3226
  useEffect(() => {
3211
3227
  // Clear any existing timer
@@ -3258,9 +3274,15 @@ const DataGrid = forwardRef(
3258
3274
  <div className={styles.dataGridContainer}>
3259
3275
  {limit && limit > 0 ? (
3260
3276
  <ReactDataGrid
3277
+ key={`datagrid-${ajaxSetting?.url || ''}-${
3278
+ ajaxSetting?.groupBy ? 'grouped' : 'ungrouped'
3279
+ }`}
3261
3280
  {...tableSetting}
3262
3281
  columns={gridColumns}
3263
3282
  dataSource={dataSource}
3283
+ {...(ajaxSetting && ajaxSetting.groupBy
3284
+ ? { defaultGroupBy: ajaxSetting.groupBy }
3285
+ : {})}
3264
3286
  filterValue={filterValue}
3265
3287
  limit={limit}
3266
3288
  enableColumnAutosize={false}