@opencorestack/opengridx 0.1.6 → 0.1.7

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.
Files changed (129) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +9 -0
  3. package/dist/index.d.ts +6 -0
  4. package/dist/opengridx.es.js +6 -6
  5. package/dist/opengridx.es.js.map +1 -1
  6. package/dist/opengridx.umd.js +4 -4
  7. package/dist/opengridx.umd.js.map +1 -1
  8. package/docs/API_REFERENCE.md +177 -0
  9. package/docs/README.md +60 -0
  10. package/docs/components/cell.md +44 -0
  11. package/docs/components/column-group-header.md +57 -0
  12. package/docs/components/column-resize.md +63 -0
  13. package/docs/components/column-visibility.md +61 -0
  14. package/docs/components/datagrid.md +70 -0
  15. package/docs/components/filter-panel.md +26 -0
  16. package/docs/components/header.md +39 -0
  17. package/docs/components/pagination.md +34 -0
  18. package/docs/components/row.md +31 -0
  19. package/docs/components/toolbar.md +40 -0
  20. package/docs/components/tooltip.md +68 -0
  21. package/docs/customization/slots-api.md +227 -0
  22. package/docs/customization/theming.md +293 -0
  23. package/docs/features/aggregation-pivot.md +79 -0
  24. package/docs/features/cell-spanning.md +520 -0
  25. package/docs/features/clipboard.md +76 -0
  26. package/docs/features/custom-pagination.md +143 -0
  27. package/docs/features/data-source.md +72 -0
  28. package/docs/features/editing-reordering.md +99 -0
  29. package/docs/features/export-guide.md +434 -0
  30. package/docs/features/filtering.md +83 -0
  31. package/docs/features/infinite-scroll.md +87 -0
  32. package/docs/features/list-view.md +65 -0
  33. package/docs/features/loading-states.md +68 -0
  34. package/docs/features/master-detail.md +46 -0
  35. package/docs/features/pinning.md +67 -0
  36. package/docs/features/selection.md +70 -0
  37. package/docs/features/sorting-pagination.md +66 -0
  38. package/docs/features/state-persistence.md +67 -0
  39. package/docs/features/tree-data-grouping.md +67 -0
  40. package/docs/features/virtualization.md +188 -0
  41. package/docs/roadmap.md +71 -0
  42. package/lib/components/Cell/Cell.css +119 -0
  43. package/lib/components/Cell/Cell.tsx +264 -0
  44. package/lib/components/Cell/GridEditInputCell.css +62 -0
  45. package/lib/components/Cell/GridEditInputCell.tsx +152 -0
  46. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +67 -0
  47. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.tsx +220 -0
  48. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.css +31 -0
  49. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.tsx +85 -0
  50. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +137 -0
  51. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +190 -0
  52. package/lib/components/DataGrid/DataGrid.css +224 -0
  53. package/lib/components/DataGrid/DataGrid.tsx +2253 -0
  54. package/lib/components/DetailPanel/DetailPanel.css +36 -0
  55. package/lib/components/DetailPanel/DetailPanel.tsx +34 -0
  56. package/lib/components/FilterPanel/FilterPanel.css +196 -0
  57. package/lib/components/FilterPanel/FilterPanel.tsx +183 -0
  58. package/lib/components/FilterPanel/index.ts +1 -0
  59. package/lib/components/Header/ColumnMenu.css +68 -0
  60. package/lib/components/Header/ColumnMenu.tsx +249 -0
  61. package/lib/components/Header/Header.css +224 -0
  62. package/lib/components/Header/Header.tsx +516 -0
  63. package/lib/components/ListView/ListView.css +112 -0
  64. package/lib/components/ListView/ListViewRow.tsx +63 -0
  65. package/lib/components/Pagination/Pagination.css +91 -0
  66. package/lib/components/Pagination/Pagination.tsx +151 -0
  67. package/lib/components/QuickFilter/QuickFilter.css +23 -0
  68. package/lib/components/QuickFilter/QuickFilter.tsx +79 -0
  69. package/lib/components/Row/Row.css +126 -0
  70. package/lib/components/Row/Row.tsx +377 -0
  71. package/lib/components/Skeleton/Skeleton.css +55 -0
  72. package/lib/components/Skeleton/Skeleton.tsx +22 -0
  73. package/lib/components/SkeletonRow/SkeletonRow.css +32 -0
  74. package/lib/components/SkeletonRow/SkeletonRow.tsx +89 -0
  75. package/lib/components/SkeletonRow/index.ts +1 -0
  76. package/lib/components/Toolbar/GlobalSearch.css +113 -0
  77. package/lib/components/Toolbar/GlobalSearch.tsx +120 -0
  78. package/lib/components/Toolbar/GridToolbar.css +204 -0
  79. package/lib/components/Toolbar/GridToolbar.tsx +647 -0
  80. package/lib/components/Toolbar/PivotPanel.css +212 -0
  81. package/lib/components/Toolbar/PivotPanel.tsx +239 -0
  82. package/lib/components/Tooltip/Tooltip.css +59 -0
  83. package/lib/components/Tooltip/Tooltip.tsx +106 -0
  84. package/lib/components/ui/Button.css +89 -0
  85. package/lib/components/ui/Button.tsx +39 -0
  86. package/lib/components/ui/Checkbox.css +71 -0
  87. package/lib/components/ui/Checkbox.tsx +66 -0
  88. package/lib/components/ui/DragHandleIcon.css +19 -0
  89. package/lib/components/ui/DragHandleIcon.tsx +18 -0
  90. package/lib/components/ui/ExpandIcon.css +38 -0
  91. package/lib/components/ui/ExpandIcon.tsx +75 -0
  92. package/lib/components/ui/Input.css +59 -0
  93. package/lib/components/ui/Input.tsx +45 -0
  94. package/lib/components/ui/index.ts +15 -0
  95. package/lib/hooks/core/useDataGrid.ts +392 -0
  96. package/lib/hooks/core/useGridApiRef.ts +10 -0
  97. package/lib/hooks/features/useAggregation.ts +143 -0
  98. package/lib/hooks/features/useGridClipboard.ts +133 -0
  99. package/lib/hooks/features/useGridDataSource.ts +190 -0
  100. package/lib/hooks/features/useGridEditing.ts +100 -0
  101. package/lib/hooks/features/useGridSpanning.ts +349 -0
  102. package/lib/hooks/features/usePivot.ts +149 -0
  103. package/lib/hooks/useColumnReorder.ts +114 -0
  104. package/lib/hooks/useRowGrouping.ts +302 -0
  105. package/lib/hooks/useRowReorder.ts +83 -0
  106. package/lib/hooks/useTreeData.ts +323 -0
  107. package/lib/index.ts +106 -0
  108. package/lib/state/index.ts +17 -0
  109. package/lib/state/types.ts +48 -0
  110. package/lib/state/useGridStateStorage.ts +113 -0
  111. package/lib/styles/base/reset.css +21 -0
  112. package/lib/styles/base/variables.css +161 -0
  113. package/lib/styles/opengridx.css +27 -0
  114. package/lib/styles/opengridx.css.d.ts +4 -0
  115. package/lib/theme/DataGridThemeProvider.tsx +276 -0
  116. package/lib/theme/defaultStyles.ts +0 -0
  117. package/lib/theme/index.ts +21 -0
  118. package/lib/theme/presets.ts +148 -0
  119. package/lib/theme/types.ts +155 -0
  120. package/lib/types/index.ts +764 -0
  121. package/lib/utils/export/exportToExcelAdvanced.ts +516 -0
  122. package/lib/utils/export/index.ts +550 -0
  123. package/lib/utils/filtering/index.ts +240 -0
  124. package/lib/utils/pinning/index.ts +282 -0
  125. package/lib/utils/sorting/index.ts +55 -0
  126. package/lib/utils/virtualization/index.ts +106 -0
  127. package/lib/vite-env.d.ts +1 -0
  128. package/llms.txt +2 -1
  129. package/package.json +13 -4
@@ -0,0 +1,190 @@
1
+ import { useState, useMemo, useRef } from 'react';
2
+ import { Checkbox } from '../ui/Checkbox';
3
+ import type { GridColDef, GridRowModel } from '../../types';
4
+
5
+
6
+ export interface ColumnVisibilityPanelProps<R extends GridRowModel = GridRowModel> {
7
+ columns: GridColDef<R>[];
8
+ visibleColumns: Set<string>;
9
+ onVisibilityChange: (field: string, isVisible: boolean) => void;
10
+ onShowAll: () => void;
11
+ onHideAll: () => void;
12
+ onColumnReorder?: (fromField: string, toField: string) => void;
13
+ }
14
+
15
+ function SearchIcon() {
16
+ return (
17
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
18
+ <circle cx="11" cy="11" r="8" />
19
+ <line x1="21" y1="21" x2="16.65" y2="16.65" />
20
+ </svg>
21
+ );
22
+ }
23
+
24
+ function DragHandleIcon() {
25
+ return (
26
+ <svg
27
+ className="ogx-column-visibility-panel__drag-handle"
28
+ width="16"
29
+ height="16"
30
+ viewBox="0 0 24 24"
31
+ fill="currentColor"
32
+ >
33
+ <circle cx="9" cy="5" r="1.5" />
34
+ <circle cx="15" cy="5" r="1.5" />
35
+ <circle cx="9" cy="12" r="1.5" />
36
+ <circle cx="15" cy="12" r="1.5" />
37
+ <circle cx="9" cy="19" r="1.5" />
38
+ <circle cx="15" cy="19" r="1.5" />
39
+ </svg>
40
+ );
41
+ }
42
+
43
+ export function ColumnVisibilityPanel<R extends GridRowModel = GridRowModel>(
44
+ props: ColumnVisibilityPanelProps<R>
45
+ ) {
46
+ const { columns, visibleColumns, onVisibilityChange, onShowAll, onHideAll, onColumnReorder } = props;
47
+ const [searchQuery, setSearchQuery] = useState('');
48
+ const [dragOverField, setDragOverField] = useState<string | null>(null);
49
+ const dragFieldRef = useRef<string | null>(null);
50
+
51
+ const hideableColumns = useMemo(() => columns.filter(col => col.hideable !== false), [columns]);
52
+ const allVisible = hideableColumns.every(col => visibleColumns.has(col.field));
53
+
54
+ const filteredColumns = useMemo(() => {
55
+ if (!searchQuery) return columns;
56
+ const lowerQuery = searchQuery.toLowerCase();
57
+ return columns.filter(col =>
58
+ (col.headerName || col.field).toLowerCase().includes(lowerQuery)
59
+ );
60
+ }, [columns, searchQuery]);
61
+
62
+ const handleToggleAll = (e: React.ChangeEvent<HTMLInputElement>) => {
63
+ if (e.target.checked) {
64
+ onShowAll();
65
+ } else {
66
+ onHideAll();
67
+ }
68
+ };
69
+
70
+ // ── Drag handlers ──────────────────────────────────────────────────────────
71
+ const handleDragStart = (field: string) => (e: React.DragEvent) => {
72
+ dragFieldRef.current = field;
73
+ e.dataTransfer.effectAllowed = 'move';
74
+ // Use a ghost image offset so the handle looks natural
75
+ e.dataTransfer.setDragImage(e.currentTarget.closest('.ogx-column-visibility-panel__item') as HTMLElement, 20, 20);
76
+ };
77
+
78
+ const handleDragOver = (field: string) => (e: React.DragEvent) => {
79
+ e.preventDefault();
80
+ e.dataTransfer.dropEffect = 'move';
81
+ if (field !== dragFieldRef.current) {
82
+ setDragOverField(field);
83
+ }
84
+ };
85
+
86
+ const handleDragLeave = () => {
87
+ setDragOverField(null);
88
+ };
89
+
90
+ const handleDrop = (toField: string) => (e: React.DragEvent) => {
91
+ e.preventDefault();
92
+ const fromField = dragFieldRef.current;
93
+ setDragOverField(null);
94
+ dragFieldRef.current = null;
95
+ if (fromField && fromField !== toField && onColumnReorder) {
96
+ onColumnReorder(fromField, toField);
97
+ }
98
+ };
99
+
100
+ const handleDragEnd = () => {
101
+ setDragOverField(null);
102
+ dragFieldRef.current = null;
103
+ };
104
+
105
+ return (
106
+ <div className="ogx-column-visibility-panel">
107
+ <div className="ogx-column-visibility-panel__search-section">
108
+ <div className="ogx-column-visibility-panel__search-container">
109
+ <SearchIcon />
110
+ <input
111
+ id="ogx-column-search"
112
+ name="ogx-column-search"
113
+ autoComplete="off"
114
+ className="ogx-column-visibility-panel__search-input"
115
+ placeholder="Search"
116
+ value={searchQuery}
117
+ onChange={(e) => setSearchQuery(e.target.value)}
118
+ />
119
+ </div>
120
+ </div>
121
+
122
+ <div className="ogx-column-visibility-panel__list">
123
+ {filteredColumns.map(col => {
124
+ const isVisible = visibleColumns.has(col.field);
125
+ const isHideable = col.hideable !== false;
126
+ const isDragOver = dragOverField === col.field;
127
+ const canReorder = !!onColumnReorder && !searchQuery;
128
+
129
+ return (
130
+ <div
131
+ key={col.field}
132
+ className={[
133
+ 'ogx-column-visibility-panel__item',
134
+ !isHideable && 'ogx-column-visibility-panel__item--disabled',
135
+ isDragOver && 'ogx-column-visibility-panel__item--drag-over',
136
+ ].filter(Boolean).join(' ')}
137
+ draggable={canReorder}
138
+ onDragOver={canReorder ? handleDragOver(col.field) : undefined}
139
+ onDragLeave={canReorder ? handleDragLeave : undefined}
140
+ onDrop={canReorder ? handleDrop(col.field) : undefined}
141
+ onDragEnd={canReorder ? handleDragEnd : undefined}
142
+ >
143
+ {canReorder && (
144
+ <span
145
+ className="ogx-column-visibility-panel__drag-handle-wrapper"
146
+ draggable
147
+ onDragStart={handleDragStart(col.field)}
148
+ title="Drag to reorder"
149
+ >
150
+ <DragHandleIcon />
151
+ </span>
152
+ )}
153
+ <label
154
+ className="ogx-column-visibility-panel__item-label"
155
+ style={{ display: 'flex', alignItems: 'center', gap: 'var(--ogx-spacing-md)', flex: 1, cursor: isHideable ? 'pointer' : 'not-allowed' }}
156
+ >
157
+ <Checkbox
158
+ checked={isVisible}
159
+ onChange={(e) => onVisibilityChange(col.field, e.target.checked)}
160
+ disabled={!isHideable}
161
+ />
162
+ <span className="ogx-column-visibility-panel__label">
163
+ {col.headerName || col.field}
164
+ </span>
165
+ </label>
166
+ </div>
167
+ );
168
+ })}
169
+ </div>
170
+
171
+ <div className="ogx-column-visibility-panel__footer">
172
+ <label className="ogx-column-visibility-panel__item">
173
+ <Checkbox
174
+ checked={allVisible}
175
+ onChange={handleToggleAll}
176
+ />
177
+ <span className="ogx-column-visibility-panel__label">
178
+ Show/Hide All
179
+ </span>
180
+ </label>
181
+ <button
182
+ className="ogx-column-visibility-panel__reset-btn"
183
+ onClick={onShowAll}
184
+ >
185
+ Reset
186
+ </button>
187
+ </div>
188
+ </div>
189
+ );
190
+ }
@@ -0,0 +1,224 @@
1
+ .ogx {
2
+ display: flex;
3
+ flex-direction: column;
4
+ width: 100%;
5
+ background: var(--ogx-grid-background);
6
+ border: 1px solid var(--ogx-grid-border-color);
7
+ border-radius: var(--ogx-border-radius-lg);
8
+ font-family: var(--ogx-font-family);
9
+ position: relative;
10
+ box-shadow: var(--ogx-shadow-sm);
11
+ transition: box-shadow var(--ogx-transition-duration-normal);
12
+ overflow: hidden;
13
+ }
14
+ .ogx > :first-child {
15
+ border-top-left-radius: calc(var(--ogx-border-radius-lg) - 1px);
16
+ border-top-right-radius: calc(var(--ogx-border-radius-lg) - 1px);
17
+ }
18
+ .ogx > :last-child {
19
+ border-bottom-left-radius: calc(var(--ogx-border-radius-lg) - 1px);
20
+ border-bottom-right-radius: calc(var(--ogx-border-radius-lg) - 1px);
21
+ }
22
+ .ogx:hover {
23
+ box-shadow: var(--ogx-shadow-md);
24
+ }
25
+ .ogx__viewport {
26
+ flex: 1;
27
+ overflow: auto;
28
+ position: relative;
29
+ scrollbar-gutter: stable;
30
+ }
31
+ .ogx__content {
32
+ position: relative;
33
+ min-width: fit-content;
34
+ display: flex;
35
+ flex-direction: column;
36
+ min-height: 100%;
37
+ }
38
+ .ogx__virtual-container {
39
+ position: relative;
40
+ flex: 1 0 auto;
41
+ }
42
+ .ogx__rows {
43
+ position: relative;
44
+ will-change: transform;
45
+ }
46
+ .ogx__pinned-rows {
47
+ position: relative;
48
+ z-index: 3;
49
+ }
50
+ .ogx__pinned-rows--top {
51
+ position: sticky;
52
+ top: var(--ogx-header-height);
53
+ z-index: 10;
54
+ background: var(--ogx-grid-background);
55
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
56
+ }
57
+ .ogx__pinned-rows--bottom {
58
+ position: sticky;
59
+ bottom: 0;
60
+ z-index: 10;
61
+ background: var(--ogx-grid-background);
62
+ box-shadow: 0 -4px 6px -1px rgba(0, 0, 0, 0.05);
63
+ margin-top: auto;
64
+ }
65
+ .ogx--loading {
66
+ opacity: 0.6;
67
+ pointer-events: none;
68
+ }
69
+ .ogx--loading::after {
70
+ content: '';
71
+ position: absolute;
72
+ top: 0;
73
+ left: 0;
74
+ right: 0;
75
+ bottom: 0;
76
+ background: color-mix(in srgb, var(--ogx-grid-background) 60%, transparent);
77
+ backdrop-filter: blur(2px);
78
+ display: flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ z-index: var(--ogx-z-index-modal);
82
+ }
83
+ .ogx--auto-height {
84
+ height: auto;
85
+ }
86
+ .ogx--auto-height .ogx__viewport {
87
+ overflow-y: visible;
88
+ }
89
+ .ogx__viewport::-webkit-scrollbar {
90
+ width: 8px;
91
+ height: 8px;
92
+ }
93
+ .ogx__viewport::-webkit-scrollbar-track {
94
+ background: transparent;
95
+ }
96
+ .ogx__viewport::-webkit-scrollbar-thumb {
97
+ background: var(--ogx-color-gray-300);
98
+ border-radius: 4px;
99
+ border: 2px solid transparent;
100
+ background-clip: content-box;
101
+ }
102
+ .ogx__viewport::-webkit-scrollbar-thumb:hover {
103
+ background-color: var(--ogx-border-color-hover);
104
+ }
105
+ .ogx__viewport {
106
+ scrollbar-width: thin;
107
+ scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
108
+ }
109
+ .ogx__empty {
110
+ display: flex;
111
+ flex-direction: column;
112
+ align-items: center;
113
+ justify-content: center;
114
+ padding: var(--ogx-spacing-xxl);
115
+ color: var(--ogx-color-gray-500);
116
+ font-size: var(--ogx-font-size-lg);
117
+ text-align: center;
118
+ gap: var(--ogx-spacing-md);
119
+ min-height: 200px;
120
+ }
121
+ .ogx__empty svg {
122
+ color: var(--ogx-color-gray-300);
123
+ }
124
+
125
+ .ogx-error-overlay {
126
+ position: absolute;
127
+ top: 0;
128
+ left: 0;
129
+ right: 0;
130
+ bottom: 0;
131
+ display: flex;
132
+ flex-direction: column;
133
+ align-items: center;
134
+ justify-content: center;
135
+ background: color-mix(in srgb, var(--ogx-grid-background) 10%, transparent);
136
+ backdrop-filter: blur(2px);
137
+ z-index: var(--ogx-z-index-modal);
138
+ gap: var(--ogx-spacing-md);
139
+ padding: var(--ogx-spacing-xl);
140
+ text-align: center;
141
+ border-radius: var(--ogx-border-radius-lg);
142
+ overflow: hidden;
143
+ }
144
+ .ogx-error-overlay > svg {
145
+ flex-shrink: 0;
146
+ overflow: visible;
147
+ display: block;
148
+ }
149
+ .ogx-error-overlay__title {
150
+ font-weight: 600;
151
+ color: var(--ogx-grid-row-text);
152
+ }
153
+ .ogx-error-overlay__message {
154
+ font-size: var(--ogx-font-size-sm);
155
+ color: var(--ogx-color-gray-500);
156
+ max-width: 400px;
157
+ }
158
+
159
+ .ogx__aggregation-footer {
160
+ display: flex;
161
+ align-items: center;
162
+ border-top: 2px solid var(--ogx-grid-border-color);
163
+ background: var(--ogx-grid-header-background);
164
+ font-weight: 600;
165
+ font-size: 0.85em;
166
+ position: sticky;
167
+ bottom: 0;
168
+ z-index: 2;
169
+ width: 100%;
170
+ }
171
+ .ogx__aggregation-cell {
172
+ flex-shrink: 0;
173
+ padding: 0 var(--ogx-grid-cell-padding-x);
174
+ display: flex;
175
+ flex-direction: column;
176
+ justify-content: center;
177
+ overflow: hidden;
178
+ box-sizing: border-box;
179
+ border-right: 1px solid var(--ogx-grid-border-color);
180
+ }
181
+ .ogx__aggregation-label {
182
+ font-size: 0.7em;
183
+ text-transform: uppercase;
184
+ letter-spacing: 0.05em;
185
+ color: var(--ogx-color-gray-500);
186
+ line-height: 1.2;
187
+ }
188
+ .ogx__aggregation-value {
189
+ color: var(--ogx-color-primary);
190
+ line-height: 1.4;
191
+ white-space: nowrap;
192
+ overflow: hidden;
193
+ text-overflow: ellipsis;
194
+ }
195
+
196
+ .ogx--density-compact .ogx__cell,
197
+ .ogx--density-compact .ogx__header-cell {
198
+ padding: var(--ogx-spacing-xs) var(--ogx-spacing-sm);
199
+ font-size: var(--ogx-font-size-xs);
200
+ }
201
+ .ogx--density-comfortable .ogx__cell,
202
+ .ogx--density-comfortable .ogx__header-cell {
203
+ padding: var(--ogx-spacing-md) var(--ogx-spacing-lg);
204
+ font-size: var(--ogx-font-size-md);
205
+ }
206
+ .ogx__group-cell-content {
207
+ font-weight: 500;
208
+ text-align: left;
209
+ display: flex;
210
+ align-items: center;
211
+ gap: 4px;
212
+ }
213
+
214
+ .ogx-aria-live-status {
215
+ position: absolute;
216
+ width: 1px;
217
+ height: 1px;
218
+ padding: 0;
219
+ margin: -1px;
220
+ overflow: hidden;
221
+ clip: rect(0, 0, 0, 0);
222
+ white-space: nowrap;
223
+ border: 0;
224
+ }