@opencorestack/opengridx 0.1.6 → 0.1.8

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 (130) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/README.md +9 -0
  3. package/dist/index.d.ts +12 -1
  4. package/dist/opengridx.css +1 -1
  5. package/dist/opengridx.es.js +2505 -2450
  6. package/dist/opengridx.es.js.map +1 -1
  7. package/dist/opengridx.umd.js +12 -12
  8. package/dist/opengridx.umd.js.map +1 -1
  9. package/docs/API_REFERENCE.md +177 -0
  10. package/docs/README.md +60 -0
  11. package/docs/components/cell.md +44 -0
  12. package/docs/components/column-group-header.md +57 -0
  13. package/docs/components/column-resize.md +63 -0
  14. package/docs/components/column-visibility.md +61 -0
  15. package/docs/components/datagrid.md +70 -0
  16. package/docs/components/filter-panel.md +26 -0
  17. package/docs/components/header.md +39 -0
  18. package/docs/components/pagination.md +34 -0
  19. package/docs/components/row.md +31 -0
  20. package/docs/components/toolbar.md +40 -0
  21. package/docs/components/tooltip.md +68 -0
  22. package/docs/customization/slots-api.md +227 -0
  23. package/docs/customization/theming.md +293 -0
  24. package/docs/features/aggregation-pivot.md +79 -0
  25. package/docs/features/cell-spanning.md +520 -0
  26. package/docs/features/clipboard.md +76 -0
  27. package/docs/features/custom-pagination.md +143 -0
  28. package/docs/features/data-source.md +72 -0
  29. package/docs/features/editing-reordering.md +99 -0
  30. package/docs/features/export-guide.md +434 -0
  31. package/docs/features/filtering.md +83 -0
  32. package/docs/features/infinite-scroll.md +87 -0
  33. package/docs/features/list-view.md +65 -0
  34. package/docs/features/loading-states.md +68 -0
  35. package/docs/features/master-detail.md +46 -0
  36. package/docs/features/pinning.md +67 -0
  37. package/docs/features/selection.md +70 -0
  38. package/docs/features/sorting-pagination.md +66 -0
  39. package/docs/features/state-persistence.md +67 -0
  40. package/docs/features/tree-data-grouping.md +67 -0
  41. package/docs/features/virtualization.md +188 -0
  42. package/docs/roadmap.md +71 -0
  43. package/lib/components/Cell/Cell.css +119 -0
  44. package/lib/components/Cell/Cell.tsx +264 -0
  45. package/lib/components/Cell/GridEditInputCell.css +62 -0
  46. package/lib/components/Cell/GridEditInputCell.tsx +152 -0
  47. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +67 -0
  48. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.tsx +220 -0
  49. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.css +31 -0
  50. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.tsx +85 -0
  51. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +137 -0
  52. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +190 -0
  53. package/lib/components/DataGrid/DataGrid.css +224 -0
  54. package/lib/components/DataGrid/DataGrid.tsx +2269 -0
  55. package/lib/components/DetailPanel/DetailPanel.css +36 -0
  56. package/lib/components/DetailPanel/DetailPanel.tsx +34 -0
  57. package/lib/components/FilterPanel/FilterPanel.css +196 -0
  58. package/lib/components/FilterPanel/FilterPanel.tsx +214 -0
  59. package/lib/components/FilterPanel/index.ts +1 -0
  60. package/lib/components/Header/ColumnMenu.css +68 -0
  61. package/lib/components/Header/ColumnMenu.tsx +249 -0
  62. package/lib/components/Header/Header.css +224 -0
  63. package/lib/components/Header/Header.tsx +516 -0
  64. package/lib/components/ListView/ListView.css +112 -0
  65. package/lib/components/ListView/ListViewRow.tsx +63 -0
  66. package/lib/components/Pagination/Pagination.css +91 -0
  67. package/lib/components/Pagination/Pagination.tsx +151 -0
  68. package/lib/components/QuickFilter/QuickFilter.css +23 -0
  69. package/lib/components/QuickFilter/QuickFilter.tsx +79 -0
  70. package/lib/components/Row/Row.css +126 -0
  71. package/lib/components/Row/Row.tsx +377 -0
  72. package/lib/components/Skeleton/Skeleton.css +55 -0
  73. package/lib/components/Skeleton/Skeleton.tsx +22 -0
  74. package/lib/components/SkeletonRow/SkeletonRow.css +32 -0
  75. package/lib/components/SkeletonRow/SkeletonRow.tsx +89 -0
  76. package/lib/components/SkeletonRow/index.ts +1 -0
  77. package/lib/components/Toolbar/GlobalSearch.css +110 -0
  78. package/lib/components/Toolbar/GlobalSearch.tsx +167 -0
  79. package/lib/components/Toolbar/GridToolbar.css +221 -0
  80. package/lib/components/Toolbar/GridToolbar.tsx +658 -0
  81. package/lib/components/Toolbar/PivotPanel.css +212 -0
  82. package/lib/components/Toolbar/PivotPanel.tsx +239 -0
  83. package/lib/components/Tooltip/Tooltip.css +59 -0
  84. package/lib/components/Tooltip/Tooltip.tsx +106 -0
  85. package/lib/components/ui/Button.css +89 -0
  86. package/lib/components/ui/Button.tsx +39 -0
  87. package/lib/components/ui/Checkbox.css +71 -0
  88. package/lib/components/ui/Checkbox.tsx +66 -0
  89. package/lib/components/ui/DragHandleIcon.css +19 -0
  90. package/lib/components/ui/DragHandleIcon.tsx +18 -0
  91. package/lib/components/ui/ExpandIcon.css +38 -0
  92. package/lib/components/ui/ExpandIcon.tsx +75 -0
  93. package/lib/components/ui/Input.css +59 -0
  94. package/lib/components/ui/Input.tsx +45 -0
  95. package/lib/components/ui/index.ts +15 -0
  96. package/lib/hooks/core/useDataGrid.ts +392 -0
  97. package/lib/hooks/core/useGridApiRef.ts +10 -0
  98. package/lib/hooks/features/useAggregation.ts +143 -0
  99. package/lib/hooks/features/useGridClipboard.ts +133 -0
  100. package/lib/hooks/features/useGridDataSource.ts +190 -0
  101. package/lib/hooks/features/useGridEditing.ts +100 -0
  102. package/lib/hooks/features/useGridSpanning.ts +349 -0
  103. package/lib/hooks/features/usePivot.ts +174 -0
  104. package/lib/hooks/useColumnReorder.ts +114 -0
  105. package/lib/hooks/useRowGrouping.ts +302 -0
  106. package/lib/hooks/useRowReorder.ts +83 -0
  107. package/lib/hooks/useTreeData.ts +323 -0
  108. package/lib/index.ts +106 -0
  109. package/lib/state/index.ts +17 -0
  110. package/lib/state/types.ts +48 -0
  111. package/lib/state/useGridStateStorage.ts +113 -0
  112. package/lib/styles/base/reset.css +21 -0
  113. package/lib/styles/base/variables.css +161 -0
  114. package/lib/styles/opengridx.css +27 -0
  115. package/lib/styles/opengridx.css.d.ts +4 -0
  116. package/lib/theme/DataGridThemeProvider.tsx +276 -0
  117. package/lib/theme/defaultStyles.ts +0 -0
  118. package/lib/theme/index.ts +21 -0
  119. package/lib/theme/presets.ts +148 -0
  120. package/lib/theme/types.ts +155 -0
  121. package/lib/types/index.ts +764 -0
  122. package/lib/utils/export/exportToExcelAdvanced.ts +516 -0
  123. package/lib/utils/export/index.ts +550 -0
  124. package/lib/utils/filtering/index.ts +240 -0
  125. package/lib/utils/pinning/index.ts +282 -0
  126. package/lib/utils/sorting/index.ts +55 -0
  127. package/lib/utils/virtualization/index.ts +106 -0
  128. package/lib/vite-env.d.ts +1 -0
  129. package/llms.txt +2 -1
  130. package/package.json +12 -4
@@ -0,0 +1,91 @@
1
+ .ogx-pagination {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: flex-end;
5
+ gap: var(--ogx-spacing-lg);
6
+ padding: 12px 16px;
7
+ border-top: var(--ogx-border-width-thin) solid var(--ogx-grid-border-color);
8
+ background: var(--ogx-grid-background);
9
+ font-size: var(--ogx-font-size-sm);
10
+ color: var(--ogx-grid-row-text);
11
+ min-height: 52px;
12
+ border-radius: 0 0 var(--ogx-border-radius-lg) var(--ogx-border-radius-lg);
13
+ }
14
+ .ogx-pagination__page-size {
15
+ display: flex;
16
+ align-items: center;
17
+ gap: 8px;
18
+ }
19
+ .ogx-pagination__label {
20
+ font-size: var(--ogx-font-size-sm);
21
+ color: var(--ogx-grid-header-text);
22
+ white-space: nowrap;
23
+ }
24
+ .ogx-pagination__select {
25
+ min-width: 55px;
26
+ padding: 0 18px 0 4px !important;
27
+ font-family: var(--ogx-font-family);
28
+ font-size: var(--ogx-font-size-sm);
29
+ font-weight: 500;
30
+ color: var(--ogx-grid-row-text);
31
+ background: var(--ogx-grid-background);
32
+ border: 1px solid var(--ogx-border-color);
33
+ border-radius: 4px;
34
+ cursor: pointer;
35
+ transition: all var(--ogx-transition-duration-fast) var(--ogx-transition-easing);
36
+ appearance: none;
37
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='%23666' d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
38
+ background-repeat: no-repeat;
39
+ background-position: right -1px center;
40
+ background-size: 20px;
41
+ line-height: 1.5;
42
+ }
43
+ .ogx-pagination__select:hover {
44
+ border-color: var(--ogx-border-color-hover);
45
+ }
46
+ .ogx-pagination__select:focus {
47
+ outline: none;
48
+ border-color: var(--ogx-color-primary);
49
+ box-shadow: 0 0 0 2px var(--ogx-color-primary-focus);
50
+ }
51
+ .ogx-pagination__info {
52
+ font-size: var(--ogx-font-size-sm);
53
+ color: var(--ogx-grid-header-text);
54
+ white-space: nowrap;
55
+ }
56
+ .ogx-pagination__actions {
57
+ display: flex;
58
+ align-items: center;
59
+ gap: var(--ogx-spacing-xs);
60
+ }
61
+ .ogx-pagination__page-info {
62
+ padding: 0 var(--ogx-spacing-sm);
63
+ font-size: var(--ogx-font-size-sm);
64
+ color: var(--ogx-grid-header-text);
65
+ white-space: nowrap;
66
+ }
67
+ .ogx-pagination .ogx-button {
68
+ min-width: 32px;
69
+ padding: var(--ogx-spacing-xs);
70
+ }
71
+ .ogx-pagination .ogx-button svg {
72
+ display: block;
73
+ }
74
+ @media (max-width: 768px) {
75
+ .ogx-pagination {
76
+ flex-wrap: wrap;
77
+ gap: var(--ogx-spacing-md);
78
+ }
79
+ .ogx-pagination__page-size {
80
+ order: 3;
81
+ width: 100%;
82
+ justify-content: center;
83
+ }
84
+ .ogx-pagination__info {
85
+ order: 1;
86
+ flex: 1;
87
+ }
88
+ .ogx-pagination__actions {
89
+ order: 2;
90
+ }
91
+ }
@@ -0,0 +1,151 @@
1
+
2
+ import React from 'react';
3
+ import { Button } from '../ui/Button';
4
+
5
+ export interface PaginationProps {
6
+ page: number;
7
+ pageSize: number;
8
+ rowCount: number;
9
+ onPageChange: (page: number) => void;
10
+ onPageSizeChange: (pageSize: number) => void;
11
+ pageSizeOptions?: number[];
12
+ showFirstButton?: boolean;
13
+ showLastButton?: boolean;
14
+ }
15
+
16
+ export function Pagination(props: PaginationProps) {
17
+ const {
18
+ page,
19
+ pageSize,
20
+ rowCount,
21
+ onPageChange,
22
+ onPageSizeChange,
23
+ pageSizeOptions = [10, 25, 50, 100],
24
+ showFirstButton = true,
25
+ showLastButton = true
26
+ } = props;
27
+
28
+ const pageCount = Math.max(1, Math.ceil(rowCount / pageSize));
29
+ const currentPage = Math.min(page, pageCount - 1);
30
+
31
+ const firstRowIndex = currentPage * pageSize;
32
+ const lastRowIndex = Math.min(firstRowIndex + pageSize, rowCount);
33
+ const displayedRows = rowCount === 0 ? 0 : `${firstRowIndex + 1}–${lastRowIndex}`;
34
+
35
+ const handleFirstPage = () => {
36
+ onPageChange(0);
37
+ };
38
+
39
+ const handlePreviousPage = () => {
40
+ onPageChange(Math.max(0, currentPage - 1));
41
+ };
42
+
43
+ const handleNextPage = () => {
44
+ onPageChange(Math.min(pageCount - 1, currentPage + 1));
45
+ };
46
+
47
+ const handleLastPage = () => {
48
+ onPageChange(pageCount - 1);
49
+ };
50
+
51
+ const handlePageSizeChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
52
+ const newPageSize = parseInt(event.target.value, 10);
53
+ onPageSizeChange(newPageSize);
54
+
55
+ };
56
+
57
+ const isFirstPage = currentPage === 0;
58
+ const isLastPage = currentPage >= pageCount - 1;
59
+
60
+ return (
61
+ <div className="ogx-pagination" role="navigation" aria-label="Pagination">
62
+ { }
63
+ <div className="ogx-pagination__page-size">
64
+ <label htmlFor="page-size-select" className="ogx-pagination__label">
65
+ Rows per page:
66
+ </label>
67
+ <select
68
+ id="page-size-select"
69
+ className="ogx-pagination__select"
70
+ value={pageSize}
71
+ onChange={handlePageSizeChange}
72
+ aria-label="Rows per page"
73
+ >
74
+ {pageSizeOptions.map(option => (
75
+ <option key={option} value={option}>
76
+ {option}
77
+ </option>
78
+ ))}
79
+ </select>
80
+ </div>
81
+
82
+ { }
83
+ <div className="ogx-pagination__info" aria-live="polite" aria-atomic="true">
84
+ {displayedRows} of {rowCount}
85
+ </div>
86
+
87
+ { }
88
+ <div className="ogx-pagination__actions">
89
+ {showFirstButton && (
90
+ <Button
91
+ variant="text"
92
+ size="small"
93
+ onClick={handleFirstPage}
94
+ disabled={isFirstPage}
95
+ aria-label="Go to first page"
96
+ title="First page"
97
+ >
98
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
99
+ <path d="M18.41 16.59L13.82 12l4.59-4.59L17 6l-6 6 6 6zM6 6h2v12H6z" />
100
+ </svg>
101
+ </Button>
102
+ )}
103
+
104
+ <Button
105
+ variant="text"
106
+ size="small"
107
+ onClick={handlePreviousPage}
108
+ disabled={isFirstPage}
109
+ aria-label="Go to previous page"
110
+ title="Previous page"
111
+ >
112
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
113
+ <path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" />
114
+ </svg>
115
+ </Button>
116
+
117
+ <div className="ogx-pagination__page-info" aria-current="page">
118
+ Page {currentPage + 1} of {pageCount}
119
+ </div>
120
+
121
+ <Button
122
+ variant="text"
123
+ size="small"
124
+ onClick={handleNextPage}
125
+ disabled={isLastPage}
126
+ aria-label="Go to next page"
127
+ title="Next page"
128
+ >
129
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
130
+ <path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
131
+ </svg>
132
+ </Button>
133
+
134
+ {showLastButton && (
135
+ <Button
136
+ variant="text"
137
+ size="small"
138
+ onClick={handleLastPage}
139
+ disabled={isLastPage}
140
+ aria-label="Go to last page"
141
+ title="Last page"
142
+ >
143
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
144
+ <path d="M5.59 7.41L10.18 12l-4.59 4.59L7 18l6-6-6-6zM16 6h2v12h-2z" />
145
+ </svg>
146
+ </Button>
147
+ )}
148
+ </div>
149
+ </div>
150
+ );
151
+ }
@@ -0,0 +1,23 @@
1
+ .ogx-quick-filter {
2
+ width: 100%;
3
+ max-width: 400px;
4
+ }
5
+ .ogx-quick-filter__clear {
6
+ background: none;
7
+ border: none;
8
+ padding: 4px;
9
+ cursor: pointer;
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ color: var(--ogx-color-gray-500);
14
+ transition: color var(--ogx-transition-duration-fast) var(--ogx-transition-easing);
15
+ border-radius: var(--ogx-border-radius-sm);
16
+ }
17
+ .ogx-quick-filter__clear:hover {
18
+ color: var(--ogx-color-gray-700);
19
+ background: var(--ogx-color-gray-100);
20
+ }
21
+ .ogx-quick-filter__clear:active {
22
+ color: var(--ogx-color-gray-900);
23
+ }
@@ -0,0 +1,79 @@
1
+
2
+ import React, { useState, useCallback } from 'react';
3
+ import { Input } from '../ui/Input';
4
+
5
+ export interface QuickFilterProps {
6
+ value?: string;
7
+ onChange: (value: string) => void;
8
+ placeholder?: string;
9
+ debounceMs?: number;
10
+ }
11
+
12
+ export function QuickFilter(props: QuickFilterProps) {
13
+ const {
14
+ value = '',
15
+ onChange,
16
+ placeholder = 'Search...',
17
+ debounceMs = 300
18
+ } = props;
19
+
20
+ const [localValue, setLocalValue] = useState(value);
21
+ const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout | null>(null);
22
+
23
+ const handleChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
24
+ const newValue = event.target.value;
25
+ setLocalValue(newValue);
26
+
27
+ // Clear existing timeout
28
+ if (timeoutId) {
29
+ clearTimeout(timeoutId);
30
+ }
31
+
32
+ // Set new timeout for debounced onChange
33
+ const newTimeoutId = setTimeout(() => {
34
+ onChange(newValue);
35
+ }, debounceMs);
36
+
37
+ setTimeoutId(newTimeoutId);
38
+ }, [timeoutId, onChange, debounceMs]);
39
+
40
+ const handleClear = useCallback(() => {
41
+ setLocalValue('');
42
+ onChange('');
43
+ if (timeoutId) {
44
+ clearTimeout(timeoutId);
45
+ }
46
+ }, [onChange, timeoutId]);
47
+
48
+ const searchIcon = (
49
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
50
+ <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" />
51
+ </svg>
52
+ );
53
+
54
+ const clearIcon = localValue ? (
55
+ <button
56
+ className="ogx-quick-filter__clear"
57
+ onClick={handleClear}
58
+ aria-label="Clear search"
59
+ type="button"
60
+ >
61
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
62
+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
63
+ </svg>
64
+ </button>
65
+ ) : null;
66
+
67
+ return (
68
+ <div className="ogx-quick-filter">
69
+ <Input
70
+ value={localValue}
71
+ onChange={handleChange}
72
+ placeholder={placeholder}
73
+ startAdornment={searchIcon}
74
+ endAdornment={clearIcon}
75
+ fullWidth
76
+ />
77
+ </div>
78
+ );
79
+ }
@@ -0,0 +1,126 @@
1
+ .ogx__row {
2
+ display: flex;
3
+ align-items: stretch;
4
+ border-bottom: 1px solid var(--ogx-grid-border-color);
5
+ background: var(--ogx-grid-background);
6
+ position: relative;
7
+ min-height: var(--ogx-row-height);
8
+ transition: background-color 0.15s ease;
9
+ }
10
+ .ogx__row--even {
11
+ background: var(--ogx-grid-row-alternate-background, var(--ogx-grid-background));
12
+ }
13
+ .ogx__cell--expand {
14
+ width: 48px;
15
+ min-width: 48px;
16
+ flex-shrink: 0;
17
+ display: flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+ background: inherit;
21
+ }
22
+ .ogx:not(.ogx--row-spanning) .ogx__row:hover {
23
+ background: var(--ogx-grid-row-hover-background);
24
+ }
25
+ .ogx__row--selected {
26
+ background: var(--ogx-grid-row-selected-background);
27
+ }
28
+ .ogx:not(.ogx--row-spanning) .ogx__row--selected:hover {
29
+ background: var(--ogx-grid-row-selected-hover-background);
30
+ }
31
+ .ogx__row--pinned-top,
32
+ .ogx__row--pinned-bottom {
33
+ background: var(--ogx-grid-background);
34
+ }
35
+ .ogx:not(.ogx--row-spanning) .ogx__row--pinned-top:hover,
36
+ .ogx:not(.ogx--row-spanning) .ogx__row--pinned-bottom:hover {
37
+ background: var(--ogx-grid-row-hover-background);
38
+ }
39
+ .ogx__row--pinned-top.ogx__row--selected,
40
+ .ogx__row--pinned-bottom.ogx__row--selected {
41
+ background: var(--ogx-grid-row-selected-background);
42
+ }
43
+ .ogx__row--dragging {
44
+ cursor: grabbing;
45
+ opacity: 0.8;
46
+ background: var(--ogx-color-primary-light);
47
+ box-shadow: var(--ogx-shadow-lg);
48
+ z-index: 20;
49
+ }
50
+ .ogx__row--drag-over {
51
+ position: relative;
52
+ z-index: 10;
53
+ }
54
+ .ogx__row--drag-over::after {
55
+ content: '';
56
+ position: absolute;
57
+ left: 0;
58
+ right: 0;
59
+ bottom: -1px;
60
+ height: 2px;
61
+ background: var(--ogx-color-primary);
62
+ z-index: 20;
63
+ }
64
+ .ogx__cell--drag-handle {
65
+ width: 48px;
66
+ min-width: 48px;
67
+ flex-shrink: 0;
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ background: inherit;
72
+ cursor: grab;
73
+ color: var(--ogx-grid-header-text);
74
+ }
75
+ .ogx__cell--drag-handle:active {
76
+ cursor: grabbing;
77
+ color: var(--ogx-grid-row-text);
78
+ }
79
+ .ogx__row--group {
80
+ background: var(--ogx-color-gray-50);
81
+ font-weight: 500;
82
+ }
83
+ .ogx:not(.ogx--row-spanning) .ogx__row--group:hover {
84
+ background: var(--ogx-color-gray-100);
85
+ }
86
+ .ogx__row--group.ogx__row--selected {
87
+ background: var(--ogx-grid-row-selected-background);
88
+ }
89
+
90
+ /* ── Skeleton / loading rows (infinite scroll) ───────────────────────── */
91
+ @keyframes ogx-skeleton-shimmer {
92
+ 0% { background-position: -600px 0; }
93
+ 100% { background-position: 600px 0; }
94
+ }
95
+ .ogx__row--skeleton {
96
+ pointer-events: none;
97
+ border-bottom: 1px solid var(--ogx-grid-border-color);
98
+ background-color: var(--ogx-grid-background);
99
+ }
100
+ .ogx__row--skeleton .ogx__skeleton-cell {
101
+ flex: 1;
102
+ height: 100%;
103
+ display: flex;
104
+ align-items: center;
105
+ padding: 0 16px;
106
+ }
107
+ .ogx__row--skeleton .ogx__skeleton-bar {
108
+ height: 14px;
109
+ border-radius: 6px;
110
+ width: 80%;
111
+ background: linear-gradient(
112
+ 90deg,
113
+ var(--ogx-skeleton-base) 25%,
114
+ var(--ogx-skeleton-highlight) 50%,
115
+ var(--ogx-skeleton-base) 75%
116
+ );
117
+ background-size: 600px 100%;
118
+ animation: ogx-skeleton-shimmer 1.4s infinite linear;
119
+ }
120
+ /* Vary bar widths for a natural look */
121
+ .ogx__row--skeleton .ogx__skeleton-cell:nth-child(1) .ogx__skeleton-bar { width: 40%; }
122
+ .ogx__row--skeleton .ogx__skeleton-cell:nth-child(2) .ogx__skeleton-bar { width: 70%; }
123
+ .ogx__row--skeleton .ogx__skeleton-cell:nth-child(3) .ogx__skeleton-bar { width: 85%; }
124
+ .ogx__row--skeleton .ogx__skeleton-cell:nth-child(4) .ogx__skeleton-bar { width: 55%; }
125
+ .ogx__row--skeleton .ogx__skeleton-cell:nth-child(5) .ogx__skeleton-bar { width: 65%; }
126
+ .ogx__row--skeleton .ogx__skeleton-cell:nth-child(n+6) .ogx__skeleton-bar { width: 60%; }