@azrico/reactgrid 1.6.6 → 1.6.9

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/CHANGELOG.md CHANGED
@@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. See [standa
4
4
 
5
5
  ### Unreleased
6
6
 
7
+ * Fix rows stopping 3px short of the grid's right and bottom edges. The grid's native scroller is now hidden (`scrollbar-width: none`) in the base theme stylesheet; `scrollbar-color` made Chrome use a 15px standard scrollbar that overflowed the 12px offset. The custom scrollbars are unaffected.
8
+ * Make closing the row context menu with Escape opt-in through `escapeClosesContextMenu` (default `false`).
9
+ * Keep the right-clicked row highlighted with the theme's hover colours while its context menu is open, preserving caller row classes and clearing the highlight on close. Correct row hover rules to use hover tokens instead of empty-state tokens.
7
10
  * Add `dontAutoCloseContextMenu` to keep the row context menu open on outside clicks, and `hideContextMenu` to hide it while leaving the browser's context menu available.
8
11
  * Fix columns with a `type` (`number`, `select`, `bool`, ...) staying read-only unless they were listed in `editableColumns`. A typed column is editable unless you set `editable: false`.
9
12
  * Fix a typed column's `editable` function being replaced by `true`. Functions and booleans are kept, so per-cell editability works again.
@@ -11,7 +11,8 @@ jest.mock('@inovua/reactdatagrid-enterprise', () => {
11
11
  class Grid extends React.Component {
12
12
  render() {
13
13
  mockGridProps = this.props;
14
- return React.createElement('div', { 'data-row-id': 'real-row' },
14
+ const rowClassName = this.props.rowClassName?.({ data: { __table_id: 'real-row' } });
15
+ return React.createElement('div', { 'data-row-id': 'real-row', className: rowClassName },
15
16
  React.createElement('span', { id: 'cell' }, 'Cell'));
16
17
  }
17
18
  }
@@ -63,6 +64,7 @@ test('omits ContextMenu entirely when there is no prop and leaves the native men
63
64
  expect(rightClick().defaultPrevented).toBe(false);
64
65
  expect(ContextMenu).not.toHaveBeenCalled();
65
66
  expect(popup()).toBeNull();
67
+ expect(container.querySelector('.azgrid-row--menu-open')).toBeNull();
66
68
  });
67
69
 
68
70
  test.each([
@@ -74,6 +76,7 @@ test.each([
74
76
  render(items);
75
77
  expect(rightClick().defaultPrevented).toBe(false);
76
78
  expect(popup()).toBeNull();
79
+ expect(container.querySelector('.azgrid-row--menu-open')).toBeNull();
77
80
  });
78
81
 
79
82
  test('resolves the real row before deciding whether to open and closes the previous menu for an empty row', () => {
@@ -118,17 +121,80 @@ test('passes the real row to onClick and closes the menu', () => {
118
121
  const onClick = jest.fn();
119
122
  render([{ title: 'Open', onClick }]);
120
123
  rightClick();
124
+ expect(container.querySelector('.azgrid-row--menu-open')).not.toBeNull();
121
125
  act(() => document.querySelector('[role="menuitem"]').click());
122
126
  expect(onClick).toHaveBeenCalledWith(row, undefined);
123
127
  expect(popup()).toBeNull();
128
+ expect(container.querySelector('.azgrid-row--menu-open')).toBeNull();
124
129
  });
125
130
 
126
131
  test('clicking outside closes the menu', async () => {
127
132
  render(['Open']);
128
133
  rightClick();
134
+ expect(container.querySelector('.azgrid-row--menu-open')).not.toBeNull();
129
135
  await act(() => new Promise((resolve) => setTimeout(resolve, 0)));
130
136
  act(() => document.body.click());
131
137
  expect(popup()).toBeNull();
138
+ expect(container.querySelector('.azgrid-row--menu-open')).toBeNull();
139
+ });
140
+
141
+ test.each(['light', 'dark', 'deco'])('keeps the menu row highlighted in %s when moving into the portal', (theme) => {
142
+ render(['Open'], { theme, escapeClosesContextMenu: true, gridOptions: { rowClassName: 'consumer-row' } });
143
+ rightClick();
144
+ const highlighted = container.querySelector('.azgrid-row--menu-open');
145
+ expect(highlighted.classList.contains('consumer-row')).toBe(true);
146
+ const menuItem = document.querySelector('[role="menuitem"]');
147
+ expect(container.contains(menuItem)).toBe(false);
148
+ act(() => menuItem.dispatchEvent(new MouseEvent('mouseover', { bubbles: true })));
149
+ expect(highlighted.classList.contains('azgrid-row--menu-open')).toBe(true);
150
+ act(() => document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })));
151
+ expect(popup()).toBeNull();
152
+ expect(container.querySelector('.azgrid-row--menu-open')).toBeNull();
153
+ expect(highlighted.classList.contains('consumer-row')).toBe(true);
154
+ });
155
+
156
+ test('merges caller rowClassName functions and matches rows by ID rather than object identity', () => {
157
+ const rowClassName = jest.fn(() => 'consumer-row');
158
+ render(['Open'], { gridOptions: { rowClassName } });
159
+ rightClick();
160
+ const args = { data: { ...row }, props: {}, className: 'inovua-row' };
161
+ expect(mockGridProps.rowClassName(args)).toBe('consumer-row azgrid-row--menu-open');
162
+ expect(rowClassName).toHaveBeenLastCalledWith(args);
163
+ expect(mockGridProps.rowClassName({ data: staleRow })).toBe('consumer-row');
164
+ expect(mockGridProps.rowClassName({ data: {} })).toBe('consumer-row');
165
+ });
166
+
167
+ test.each([undefined, false])('Escape leaves the menu and highlight open when escapeClosesContextMenu is %s', (escapeClosesContextMenu) => {
168
+ render(['Open'], { escapeClosesContextMenu });
169
+ rightClick();
170
+ act(() => document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })));
171
+ expect(popup()).not.toBeNull();
172
+ expect(container.querySelector('.azgrid-row--menu-open')).not.toBeNull();
173
+ });
174
+
175
+ test('escapeClosesContextMenu can be disabled and enabled while the menu is open', () => {
176
+ const items = ['Open'];
177
+ render(items, { escapeClosesContextMenu: true });
178
+ rightClick();
179
+ render(items, { escapeClosesContextMenu: false });
180
+ act(() => document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })));
181
+ expect(popup()).not.toBeNull();
182
+ render(items, { escapeClosesContextMenu: true });
183
+ act(() => document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })));
184
+ expect(popup()).toBeNull();
185
+ expect(container.querySelector('.azgrid-row--menu-open')).toBeNull();
186
+ });
187
+
188
+ test('removing menu items clears the highlight and does not reopen the menu when items return', () => {
189
+ const items = ['Open'];
190
+ render(items);
191
+ rightClick();
192
+ expect(container.querySelector('.azgrid-row--menu-open')).not.toBeNull();
193
+ render();
194
+ expect(container.querySelector('.azgrid-row--menu-open')).toBeNull();
195
+ render(items);
196
+ expect(popup()).toBeNull();
197
+ expect(container.querySelector('.azgrid-row--menu-open')).toBeNull();
132
198
  });
133
199
 
134
200
  test('dontAutoCloseContextMenu keeps the menu open on outside clicks and can be updated', async () => {
@@ -5,6 +5,29 @@ const cssTree = require('css-tree');
5
5
  const css = fs.readFileSync('src/themes/azgrid-theme.css', 'utf8');
6
6
  cssTree.parse(css);
7
7
  const theme = postcss.parse(css);
8
+ const selectorParser = require('postcss-selector-parser');
9
+ const { compare, selectorSpecificity } = require('@csstools/selector-specificity');
10
+ const specificity = selector => selectorSpecificity(selectorParser().astSync(selector).first);
11
+ const menuRowRule = theme.nodes.find(n => n.type === 'rule' && n.selector.includes('.azgrid-row--menu-open'));
12
+ assert(menuRowRule, 'missing open-menu row styles');
13
+ const menuPaint = Object.fromEntries(menuRowRule.nodes.filter(n => n.type === 'decl').map(d => [d.prop, d.value]));
14
+ assert.equal(menuPaint.background, 'var(--azgrid-row-bg-hover, var(--azgrid-preset-row-bg-hover))');
15
+ assert.equal(menuPaint.color, 'var(--azgrid-row-fg-hover, var(--azgrid-preset-row-fg-hover))');
16
+ for (const suffix of ['__row-hover-target', '__cell', '__row-index-column']) {
17
+ assert(menuRowRule.selectors.some(s => s.endsWith('.InovuaReactDataGrid' + suffix)), 'missing menu row surface ' + suffix);
18
+ }
19
+ theme.walkRules(rule => {
20
+ for (const selector of rule.selectors) {
21
+ if (!selector.includes('__row-hover-target:hover')) continue;
22
+ const surface = selector.includes('__row-index-column') ? '__row-index-column'
23
+ : selector.includes('__cell--over') ? '__cell' : '__row-hover-target';
24
+ const menuSelector = menuRowRule.selectors.find(s => s.endsWith('.InovuaReactDataGrid' + surface));
25
+ assert(compare(specificity(menuSelector), specificity(selector)) >= 0, 'menu highlight weaker than ' + selector);
26
+ for (const declaration of rule.nodes.filter(n => n.type === 'decl')) {
27
+ if (declaration.prop in menuPaint) assert.equal(declaration.value, menuPaint[declaration.prop], 'hover paint differs from menu highlight');
28
+ }
29
+ }
30
+ });
8
31
  const presets = new Set();
9
32
  const used = new Set();
10
33
  let important = [];
@@ -67,6 +67,8 @@ function tokenFor(selector, prop, literal) {
67
67
  if (/loader-spinner/.test(selector)) return literal === '#7986cb' ? 'accent' : 'muted-fg';
68
68
  if (/loader-loadbar/.test(selector)) return 'loading-fg';
69
69
  if (/load-mask|loading/.test(selector)) return bg ? 'loading-bg' : 'loading-fg';
70
+ // :not(.row--empty) in row hover selectors does not describe an empty state.
71
+ if (/__row-hover-target:hover/.test(selector)) return bg ? 'row-bg-hover' : 'row-fg-hover';
70
72
  if (/empty/.test(selector)) return bg ? 'empty-bg' : 'empty-fg';
71
73
  if (/placeholder/.test(selector)) return 'filter-placeholder';
72
74
  if (/sort-icon|selection-dragger|active-borders|checkbox/.test(selector)) {
@@ -1,102 +1,120 @@
1
- /* Additional surfaces and typography not assigned colours by Inovua. */
2
- .azgrid.InovuaReactDataGrid.InovuaReactDataGrid--theme-default-light .InovuaReactDataGrid__row--active:not(.InovuaReactDataGrid__row--selected) .InovuaReactDataGrid__cell:not(.InovuaReactDataGrid__cell--no-background) {
3
- background: var(--azgrid-row-bg-active, var(--azgrid-preset-row-bg-active));
4
- }
5
- .azgrid.azgrid-tree .InovuaReactDataGrid__row:not(.InovuaReactDataGrid__row--selected):not(.InovuaReactDataGrid__row--active) .InovuaReactDataGrid__row-hover-target:not(:hover),
6
- .azgrid .InovuaReactDataGrid__row--group-row:not(.InovuaReactDataGrid__row--selected):not(.InovuaReactDataGrid__row--active) .InovuaReactDataGrid__row-hover-target:not(:hover) {
7
- background: var(--azgrid-tree-bg, var(--azgrid-preset-tree-bg));
8
- color: var(--azgrid-tree-fg, var(--azgrid-preset-tree-fg));
9
- }
10
- .azgrid.azgrid-tree .InovuaReactDataGrid__cell__content > svg,
11
- .azgrid .InovuaReactDataGrid__cell--group-expand-cell svg {
12
- fill: var(--azgrid-tree-fg, var(--azgrid-preset-tree-fg));
13
- }
14
- .azgrid.InovuaReactDataGrid {
15
- background: var(--azgrid-bg, var(--azgrid-preset-bg));
16
- color: var(--azgrid-fg, var(--azgrid-preset-fg));
17
- border-color: var(--azgrid-border, var(--azgrid-preset-border));
18
- border-radius: var(--azgrid-radius, var(--azgrid-preset-radius));
19
- font-family: var(--azgrid-font-family, var(--azgrid-preset-font-family));
20
- }
21
- .azgrid .InovuaReactDataGrid__column-header__content,
22
- .azgrid .InovuaReactDataGrid__header-group__title {
23
- color: var(--azgrid-header-fg, var(--azgrid-preset-header-fg));
24
- font-size: var(--azgrid-header-font-size, var(--azgrid-preset-header-font-size));
25
- font-weight: var(--azgrid-header-font-weight, var(--azgrid-preset-header-font-weight));
26
- text-transform: var(--azgrid-header-text-transform, var(--azgrid-preset-header-text-transform));
27
- letter-spacing: var(--azgrid-header-letter-spacing, var(--azgrid-preset-header-letter-spacing));
28
- }
29
- .azgrid.InovuaReactDataGrid--theme-default-light .InovuaReactDataGrid__cell {
30
- border: var(--azgrid-cell-border-width, var(--azgrid-preset-cell-border-width)) solid var(--azgrid-cell-border, var(--azgrid-preset-cell-border));
31
- }
32
- .azgrid.InovuaReactDataGrid--theme-default-light .InovuaReactDataGrid__row--first .InovuaReactDataGrid__cell {
33
- /* Inovua's first-row border-top is !important. Keep the same border API on this row. */
34
- border-top: var(--azgrid-cell-border-width, var(--azgrid-preset-cell-border-width)) solid var(--azgrid-cell-border, var(--azgrid-preset-cell-border)) !important;
35
- }
36
- .azgrid.InovuaReactDataGrid--theme-default-light .InovuaReactDataGrid__cell:not(.InovuaReactDataGrid__cell--group-expand-cell):not(.InovuaReactDataGrid__cell--expand-cell):not(.InovuaReactDataGrid__cell--reorder) {
37
- padding: var(--azgrid-cell-padding, var(--azgrid-preset-cell-padding));
38
- }
39
- .azgrid .InovuaReactDataGrid__empty-wrapper {
40
- background: var(--azgrid-empty-bg, var(--azgrid-preset-empty-bg));
41
- color: var(--azgrid-empty-fg, var(--azgrid-preset-empty-fg));
42
- }
43
- .azgrid.InovuaReactDataGrid--theme-default-light .InovuaReactDataGrid__row-details {
44
- background: var(--azgrid-details-bg, var(--azgrid-preset-details-bg));
45
- }
46
- .azgrid .InovuaReactDataGrid__cell--locked-start.InovuaReactDataGrid__cell--last-in-section:after,
47
- .azgrid .InovuaReactDataGrid__cell--locked-end.InovuaReactDataGrid__cell--first-in-section:after {
48
- box-shadow: var(--azgrid-locked-column-shadow, var(--azgrid-preset-locked-column-shadow));
49
- }
50
- .azgrid-scope input,
51
- .azgrid-scope select,
52
- .azgrid-scope .azgrid-searcharea {
53
- color: var(--azgrid-filter-fg, var(--azgrid-preset-filter-fg));
54
- background-color: var(--azgrid-filter-bg, var(--azgrid-preset-filter-bg));
55
- border-color: var(--azgrid-filter-border, var(--azgrid-preset-filter-border));
56
- border-radius: var(--azgrid-radius, var(--azgrid-preset-radius));
57
- font-family: var(--azgrid-font-family, var(--azgrid-preset-font-family));
58
- }
59
- .azgrid-scope input::placeholder {
60
- color: var(--azgrid-filter-placeholder, var(--azgrid-preset-filter-placeholder));
61
- }
62
- .azgrid-scope input:focus-visible,
63
- .azgrid-scope select:focus-visible {
64
- outline: 1px solid var(--azgrid-filter-focus-ring, var(--azgrid-preset-filter-focus-ring));
65
- }
66
- .azgrid-scope .inovua-react-pagination-toolbar input,
67
- .azgrid-scope .inovua-react-pagination-toolbar select {
68
- background: var(--azgrid-pagination-bg, var(--azgrid-preset-pagination-bg));
69
- color: var(--azgrid-pagination-fg, var(--azgrid-preset-pagination-fg));
70
- border-color: var(--azgrid-pagination-border, var(--azgrid-preset-pagination-border));
71
- }
72
- .azgrid-scope .inovua-react-toolkit-text-input,
73
- .azgrid-scope .inovua-react-toolkit-combo-box,
74
- .azgrid-scope .inovua-react-toolkit-menu {
75
- border-radius: var(--azgrid-radius, var(--azgrid-preset-radius));
76
- }
77
- /* The row context menu is portaled to <body>, outside .azgrid-scope: these rules stay
78
- * unscoped and read the tokens from the theme class DataGrid puts on the menu.
79
- * Two classes exceed @azrico/controls' single-class menu rules. */
80
- .azgrid-context-menu.azrico-controls-contextmenu {
81
- --azrico-contextmenu-color: var(--azgrid-menu-fg, var(--azgrid-preset-menu-fg));
82
- --azrico-contextmenu-background: var(--azgrid-menu-bg, var(--azgrid-preset-menu-bg));
83
- --azrico-contextmenu-hover: var(--azgrid-menu-bg-hover, var(--azgrid-preset-menu-bg-hover));
84
- --azrico-contextmenu-focus: var(--azgrid-accent, var(--azgrid-preset-accent));
85
- --azrico-contextmenu-radius: var(--azgrid-radius, var(--azgrid-preset-radius));
86
- --azrico-contextmenu-shadow: 0 3px 8px var(--azgrid-shadow-color, var(--azgrid-preset-shadow-color));
87
- --azrico-contextmenu-font: var(--azgrid-font-family, var(--azgrid-preset-font-family));
88
- border: 1px solid var(--azgrid-menu-border, var(--azgrid-preset-menu-border));
89
- }
90
- .azgrid-scope, .azgrid-scope * {
91
- accent-color: var(--azgrid-accent, var(--azgrid-preset-accent));
92
- scrollbar-color: var(--azgrid-scrollbar-thumb, var(--azgrid-preset-scrollbar-thumb)) var(--azgrid-scrollbar-track, var(--azgrid-preset-scrollbar-track));
93
- }
94
- .azgrid-scope ::-webkit-scrollbar-track {
95
- background: var(--azgrid-scrollbar-track, var(--azgrid-preset-scrollbar-track));
96
- }
97
- .azgrid-scope ::-webkit-scrollbar-thumb {
98
- background: var(--azgrid-scrollbar-thumb, var(--azgrid-preset-scrollbar-thumb));
99
- }
100
- .azgrid .InovuaReactDataGrid__row.deleted:after {
101
- border-color: var(--azgrid-muted-fg, var(--azgrid-preset-muted-fg));
102
- }
1
+ /* Keep the menu row in its hover state while the pointer is in the portaled menu.
2
+ * Match the strongest selected-row :hover selectors, including their cells. */
3
+ .azgrid.InovuaReactDataGrid.InovuaReactDataGrid--theme-default-light.InovuaReactDataGrid--show-hover-rows .InovuaReactDataGrid__row.azgrid-row--menu-open:not(.InovuaReactDataGrid__row--scrolling):not(.InovuaReactDataGrid__row--empty) .InovuaReactDataGrid__row-hover-target,
4
+ .azgrid.InovuaReactDataGrid.InovuaReactDataGrid--theme-default-light.InovuaReactDataGrid--show-hover-rows .InovuaReactDataGrid__row.azgrid-row--menu-open:not(.InovuaReactDataGrid__row--scrolling):not(.InovuaReactDataGrid__row--empty) .InovuaReactDataGrid__row-hover-target .InovuaReactDataGrid__cell,
5
+ .azgrid.InovuaReactDataGrid.InovuaReactDataGrid--theme-default-light.InovuaReactDataGrid--show-hover-rows .InovuaReactDataGrid__row.azgrid-row--menu-open:not(.InovuaReactDataGrid__row--scrolling):not(.InovuaReactDataGrid__row--empty) .InovuaReactDataGrid__row-hover-target .InovuaReactDataGrid__row-index-column {
6
+ background: var(--azgrid-row-bg-hover, var(--azgrid-preset-row-bg-hover));
7
+ color: var(--azgrid-row-fg-hover, var(--azgrid-preset-row-fg-hover));
8
+ }
9
+
10
+ /* Additional surfaces and typography not assigned colours by Inovua. */
11
+ .azgrid.InovuaReactDataGrid.InovuaReactDataGrid--theme-default-light .InovuaReactDataGrid__row--active:not(.InovuaReactDataGrid__row--selected) .InovuaReactDataGrid__cell:not(.InovuaReactDataGrid__cell--no-background) {
12
+ background: var(--azgrid-row-bg-active, var(--azgrid-preset-row-bg-active));
13
+ }
14
+ .azgrid.azgrid-tree .InovuaReactDataGrid__row:not(.InovuaReactDataGrid__row--selected):not(.InovuaReactDataGrid__row--active) .InovuaReactDataGrid__row-hover-target:not(:hover),
15
+ .azgrid .InovuaReactDataGrid__row--group-row:not(.InovuaReactDataGrid__row--selected):not(.InovuaReactDataGrid__row--active) .InovuaReactDataGrid__row-hover-target:not(:hover) {
16
+ background: var(--azgrid-tree-bg, var(--azgrid-preset-tree-bg));
17
+ color: var(--azgrid-tree-fg, var(--azgrid-preset-tree-fg));
18
+ }
19
+ .azgrid.azgrid-tree .InovuaReactDataGrid__cell__content > svg,
20
+ .azgrid .InovuaReactDataGrid__cell--group-expand-cell svg {
21
+ fill: var(--azgrid-tree-fg, var(--azgrid-preset-tree-fg));
22
+ }
23
+ .azgrid.InovuaReactDataGrid {
24
+ background: var(--azgrid-bg, var(--azgrid-preset-bg));
25
+ color: var(--azgrid-fg, var(--azgrid-preset-fg));
26
+ border-color: var(--azgrid-border, var(--azgrid-preset-border));
27
+ border-radius: var(--azgrid-radius, var(--azgrid-preset-radius));
28
+ font-family: var(--azgrid-font-family, var(--azgrid-preset-font-family));
29
+ }
30
+ .azgrid .InovuaReactDataGrid__column-header__content,
31
+ .azgrid .InovuaReactDataGrid__header-group__title {
32
+ color: var(--azgrid-header-fg, var(--azgrid-preset-header-fg));
33
+ font-size: var(--azgrid-header-font-size, var(--azgrid-preset-header-font-size));
34
+ font-weight: var(--azgrid-header-font-weight, var(--azgrid-preset-header-font-weight));
35
+ text-transform: var(--azgrid-header-text-transform, var(--azgrid-preset-header-text-transform));
36
+ letter-spacing: var(--azgrid-header-letter-spacing, var(--azgrid-preset-header-letter-spacing));
37
+ }
38
+ .azgrid.InovuaReactDataGrid--theme-default-light .InovuaReactDataGrid__cell {
39
+ border: var(--azgrid-cell-border-width, var(--azgrid-preset-cell-border-width)) solid var(--azgrid-cell-border, var(--azgrid-preset-cell-border));
40
+ }
41
+ .azgrid.InovuaReactDataGrid--theme-default-light .InovuaReactDataGrid__row--first .InovuaReactDataGrid__cell {
42
+ /* Inovua's first-row border-top is !important. Keep the same border API on this row. */
43
+ border-top: var(--azgrid-cell-border-width, var(--azgrid-preset-cell-border-width)) solid var(--azgrid-cell-border, var(--azgrid-preset-cell-border)) !important;
44
+ }
45
+ .azgrid.InovuaReactDataGrid--theme-default-light .InovuaReactDataGrid__cell:not(.InovuaReactDataGrid__cell--group-expand-cell):not(.InovuaReactDataGrid__cell--expand-cell):not(.InovuaReactDataGrid__cell--reorder) {
46
+ padding: var(--azgrid-cell-padding, var(--azgrid-preset-cell-padding));
47
+ }
48
+ .azgrid .InovuaReactDataGrid__empty-wrapper {
49
+ background: var(--azgrid-empty-bg, var(--azgrid-preset-empty-bg));
50
+ color: var(--azgrid-empty-fg, var(--azgrid-preset-empty-fg));
51
+ }
52
+ .azgrid.InovuaReactDataGrid--theme-default-light .InovuaReactDataGrid__row-details {
53
+ background: var(--azgrid-details-bg, var(--azgrid-preset-details-bg));
54
+ }
55
+ .azgrid .InovuaReactDataGrid__cell--locked-start.InovuaReactDataGrid__cell--last-in-section:after,
56
+ .azgrid .InovuaReactDataGrid__cell--locked-end.InovuaReactDataGrid__cell--first-in-section:after {
57
+ box-shadow: var(--azgrid-locked-column-shadow, var(--azgrid-preset-locked-column-shadow));
58
+ }
59
+ .azgrid-scope input,
60
+ .azgrid-scope select,
61
+ .azgrid-scope .azgrid-searcharea {
62
+ color: var(--azgrid-filter-fg, var(--azgrid-preset-filter-fg));
63
+ background-color: var(--azgrid-filter-bg, var(--azgrid-preset-filter-bg));
64
+ border-color: var(--azgrid-filter-border, var(--azgrid-preset-filter-border));
65
+ border-radius: var(--azgrid-radius, var(--azgrid-preset-radius));
66
+ font-family: var(--azgrid-font-family, var(--azgrid-preset-font-family));
67
+ }
68
+ .azgrid-scope input::placeholder {
69
+ color: var(--azgrid-filter-placeholder, var(--azgrid-preset-filter-placeholder));
70
+ }
71
+ .azgrid-scope input:focus-visible,
72
+ .azgrid-scope select:focus-visible {
73
+ outline: 1px solid var(--azgrid-filter-focus-ring, var(--azgrid-preset-filter-focus-ring));
74
+ }
75
+ .azgrid-scope .inovua-react-pagination-toolbar input,
76
+ .azgrid-scope .inovua-react-pagination-toolbar select {
77
+ background: var(--azgrid-pagination-bg, var(--azgrid-preset-pagination-bg));
78
+ color: var(--azgrid-pagination-fg, var(--azgrid-preset-pagination-fg));
79
+ border-color: var(--azgrid-pagination-border, var(--azgrid-preset-pagination-border));
80
+ }
81
+ .azgrid-scope .inovua-react-toolkit-text-input,
82
+ .azgrid-scope .inovua-react-toolkit-combo-box,
83
+ .azgrid-scope .inovua-react-toolkit-menu {
84
+ border-radius: var(--azgrid-radius, var(--azgrid-preset-radius));
85
+ }
86
+ /* The row context menu is portaled to <body>, outside .azgrid-scope: these rules stay
87
+ * unscoped and read the tokens from the theme class DataGrid puts on the menu.
88
+ * Two classes exceed @azrico/controls' single-class menu rules. */
89
+ .azgrid-context-menu.azrico-controls-contextmenu {
90
+ --azrico-contextmenu-color: var(--azgrid-menu-fg, var(--azgrid-preset-menu-fg));
91
+ --azrico-contextmenu-background: var(--azgrid-menu-bg, var(--azgrid-preset-menu-bg));
92
+ --azrico-contextmenu-hover: var(--azgrid-menu-bg-hover, var(--azgrid-preset-menu-bg-hover));
93
+ --azrico-contextmenu-focus: var(--azgrid-accent, var(--azgrid-preset-accent));
94
+ --azrico-contextmenu-radius: var(--azgrid-radius, var(--azgrid-preset-radius));
95
+ --azrico-contextmenu-shadow: 0 3px 8px var(--azgrid-shadow-color, var(--azgrid-preset-shadow-color));
96
+ --azrico-contextmenu-font: var(--azgrid-font-family, var(--azgrid-preset-font-family));
97
+ border: 1px solid var(--azgrid-menu-border, var(--azgrid-preset-menu-border));
98
+ }
99
+ .azgrid-scope, .azgrid-scope * {
100
+ accent-color: var(--azgrid-accent, var(--azgrid-preset-accent));
101
+ scrollbar-color: var(--azgrid-scrollbar-thumb, var(--azgrid-preset-scrollbar-thumb)) var(--azgrid-scrollbar-track, var(--azgrid-preset-scrollbar-track));
102
+ }
103
+ .azgrid-scope ::-webkit-scrollbar-track {
104
+ background: var(--azgrid-scrollbar-track, var(--azgrid-preset-scrollbar-track));
105
+ }
106
+ .azgrid-scope ::-webkit-scrollbar-thumb {
107
+ background: var(--azgrid-scrollbar-thumb, var(--azgrid-preset-scrollbar-thumb));
108
+ }
109
+ /* The grid draws its own scrollbars and pushes the native one out of view by a
110
+ fixed offset. Setting scrollbar-color switches Chrome to the standard (wider)
111
+ scrollbar, which then overlaps the last pixels of the viewport. Hide it. */
112
+ .azgrid-scope .inovua-react-scroll-container__wrapper > div {
113
+ scrollbar-width: none;
114
+ }
115
+ .azgrid-scope .inovua-react-scroll-container__wrapper > div::-webkit-scrollbar {
116
+ display: none;
117
+ }
118
+ .azgrid .InovuaReactDataGrid__row.deleted:after {
119
+ border-color: var(--azgrid-muted-fg, var(--azgrid-preset-muted-fg));
120
+ }
@@ -78,8 +78,8 @@ function useGridEvents(grid, props) {
78
78
  }, [props.dataSource]);
79
79
  /* ------------------------------- ContextMenu ------------------------------ */
80
80
  _react.default.useEffect(() => {
81
- if (props.hideContextMenu) update_menuRow(null, null);
82
- }, [props.hideContextMenu, update_menuRow]);
81
+ if (props.hideContextMenu || !props.contextMenuItems) update_menuRow(null, null);
82
+ }, [props.hideContextMenu, props.contextMenuItems, update_menuRow]);
83
83
  const onRowContextMenu = _react.default.useCallback((rowProps, event) => {
84
84
  if (latestProps.current.hideContextMenu || event.defaultPrevented || !rowProps || !rowProps.data || _func_child.func_selection.has_selection()) {
85
85
  update_menuRow(null, null);