@azrico/reactgrid 1.6.1 → 1.6.2
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 +4 -0
- package/dist/src/hooks/useGridEvents.js +4 -3
- package/package.json +1 -1
- package/src/hooks/useGridEvents.ts +158 -157
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### 1.7.1 (2026-10-14)
|
|
6
|
+
|
|
7
|
+
* Fix two quick clicks on different rows firing `onRowDoubleClick`: the double-click check now keys clicks by the row's id instead of `undefined`.
|
|
8
|
+
|
|
5
9
|
### 1.7.0 (2026-10-14)
|
|
6
10
|
|
|
7
11
|
* Add `advancedFilterLabel` to rename the quick-search label (default "Search").
|
|
@@ -60,15 +60,16 @@ function useGridEvents(grid, props) {
|
|
|
60
60
|
onRowDoubleClick
|
|
61
61
|
} = latestProps.current;
|
|
62
62
|
const nowDate = Date.now();
|
|
63
|
-
const
|
|
63
|
+
const rowId = (0, _func_grid.get_row_id)(rowProps.data);
|
|
64
|
+
const lastclick = click_data.current[rowId];
|
|
64
65
|
if (lastclick) {
|
|
65
66
|
const diffTime = Math.abs(nowDate - lastclick);
|
|
66
67
|
if (diffTime < 400) {
|
|
67
|
-
click_data.current[
|
|
68
|
+
click_data.current[rowId] = null;
|
|
68
69
|
return onRowDoubleClick && onRowDoubleClick(rowProps.rowIndex, rowProps.data);
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
|
-
click_data.current[
|
|
72
|
+
click_data.current[rowId] = nowDate;
|
|
72
73
|
return onRowClick && onRowClick(rowProps.rowIndex, rowProps.data);
|
|
73
74
|
}, []);
|
|
74
75
|
_react.default.useEffect(() => {
|
package/package.json
CHANGED
|
@@ -1,157 +1,158 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { DataGridWrapperProps, GridType } from '../types/types';
|
|
3
|
-
import { find_realData, get_row_id } from '../func/func_grid';
|
|
4
|
-
import { func_selection } from '../func/func_child';
|
|
5
|
-
import {
|
|
6
|
-
apply_root_a11y,
|
|
7
|
-
apply_row_a11y,
|
|
8
|
-
get_grid_root,
|
|
9
|
-
get_root_a11y_key,
|
|
10
|
-
is_grid_busy,
|
|
11
|
-
} from '../func/func_a11y';
|
|
12
|
-
|
|
13
|
-
function useGridMenuRow() {
|
|
14
|
-
const [menuAnchorEl, set_menuAnchorEl] = React.useState<any>(null);
|
|
15
|
-
const [menuRow, set_menuRow] = React.useState<any>(null);
|
|
16
|
-
const update_menuRow = React.useCallback((r: any = null, el: any = null) => {
|
|
17
|
-
set_menuRow(r || null);
|
|
18
|
-
set_menuAnchorEl(el || null);
|
|
19
|
-
}, []);
|
|
20
|
-
return { update_menuRow, menuAnchorEl, menuRow };
|
|
21
|
-
}
|
|
22
|
-
export function useGridEvents(grid: GridType, props: DataGridWrapperProps) {
|
|
23
|
-
const click_data = React.useRef<any>(null);
|
|
24
|
-
// latest props, so the callbacks below stay stable and don't re-render the grid
|
|
25
|
-
const latestProps = React.useRef(props);
|
|
26
|
-
latestProps.current = props;
|
|
27
|
-
const { update_menuRow, menuAnchorEl, menuRow } = useGridMenuRow();
|
|
28
|
-
|
|
29
|
-
const refreshColumn = React.useCallback(
|
|
30
|
-
(col: string) => {
|
|
31
|
-
const dt = grid.ungroupedData;
|
|
32
|
-
for (let index = 0; index < dt.length; index++) {
|
|
33
|
-
(grid as any).setItemPropertyAt(index, col, dt[index][col]);
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
[grid]
|
|
37
|
-
);
|
|
38
|
-
const refresh = React.useCallback(() => {
|
|
39
|
-
const dt = grid.ungroupedData;
|
|
40
|
-
for (let index = 0; index < dt.length; index++) {
|
|
41
|
-
(grid as any).setItemAt(index, {}, { replace: false });
|
|
42
|
-
}
|
|
43
|
-
}, [grid]);
|
|
44
|
-
|
|
45
|
-
React.useEffect(() => {
|
|
46
|
-
if (!grid) return;
|
|
47
|
-
grid['refresh'] = refresh;
|
|
48
|
-
grid['refreshColumn'] = refreshColumn;
|
|
49
|
-
}, [grid, props.dataSource, refresh, refreshColumn]);
|
|
50
|
-
/* -------------------------------------------------------------------------- */
|
|
51
|
-
/* events */
|
|
52
|
-
/* -------------------------------------------------------------------------- */
|
|
53
|
-
const onRowClick = React.useCallback((rowProps, event) => {
|
|
54
|
-
const { onRowClick, onRowDoubleClick } = latestProps.current;
|
|
55
|
-
const nowDate = Date.now();
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
/*
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
key
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
rootSyncFrame.current
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { DataGridWrapperProps, GridType } from '../types/types';
|
|
3
|
+
import { find_realData, get_row_id } from '../func/func_grid';
|
|
4
|
+
import { func_selection } from '../func/func_child';
|
|
5
|
+
import {
|
|
6
|
+
apply_root_a11y,
|
|
7
|
+
apply_row_a11y,
|
|
8
|
+
get_grid_root,
|
|
9
|
+
get_root_a11y_key,
|
|
10
|
+
is_grid_busy,
|
|
11
|
+
} from '../func/func_a11y';
|
|
12
|
+
|
|
13
|
+
function useGridMenuRow() {
|
|
14
|
+
const [menuAnchorEl, set_menuAnchorEl] = React.useState<any>(null);
|
|
15
|
+
const [menuRow, set_menuRow] = React.useState<any>(null);
|
|
16
|
+
const update_menuRow = React.useCallback((r: any = null, el: any = null) => {
|
|
17
|
+
set_menuRow(r || null);
|
|
18
|
+
set_menuAnchorEl(el || null);
|
|
19
|
+
}, []);
|
|
20
|
+
return { update_menuRow, menuAnchorEl, menuRow };
|
|
21
|
+
}
|
|
22
|
+
export function useGridEvents(grid: GridType, props: DataGridWrapperProps) {
|
|
23
|
+
const click_data = React.useRef<any>(null);
|
|
24
|
+
// latest props, so the callbacks below stay stable and don't re-render the grid
|
|
25
|
+
const latestProps = React.useRef(props);
|
|
26
|
+
latestProps.current = props;
|
|
27
|
+
const { update_menuRow, menuAnchorEl, menuRow } = useGridMenuRow();
|
|
28
|
+
|
|
29
|
+
const refreshColumn = React.useCallback(
|
|
30
|
+
(col: string) => {
|
|
31
|
+
const dt = grid.ungroupedData;
|
|
32
|
+
for (let index = 0; index < dt.length; index++) {
|
|
33
|
+
(grid as any).setItemPropertyAt(index, col, dt[index][col]);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
[grid]
|
|
37
|
+
);
|
|
38
|
+
const refresh = React.useCallback(() => {
|
|
39
|
+
const dt = grid.ungroupedData;
|
|
40
|
+
for (let index = 0; index < dt.length; index++) {
|
|
41
|
+
(grid as any).setItemAt(index, {}, { replace: false });
|
|
42
|
+
}
|
|
43
|
+
}, [grid]);
|
|
44
|
+
|
|
45
|
+
React.useEffect(() => {
|
|
46
|
+
if (!grid) return;
|
|
47
|
+
grid['refresh'] = refresh;
|
|
48
|
+
grid['refreshColumn'] = refreshColumn;
|
|
49
|
+
}, [grid, props.dataSource, refresh, refreshColumn]);
|
|
50
|
+
/* -------------------------------------------------------------------------- */
|
|
51
|
+
/* events */
|
|
52
|
+
/* -------------------------------------------------------------------------- */
|
|
53
|
+
const onRowClick = React.useCallback((rowProps, event) => {
|
|
54
|
+
const { onRowClick, onRowDoubleClick } = latestProps.current;
|
|
55
|
+
const nowDate = Date.now();
|
|
56
|
+
const rowId = get_row_id(rowProps.data);
|
|
57
|
+
const lastclick: number = click_data.current[rowId];
|
|
58
|
+
if (lastclick) {
|
|
59
|
+
const diffTime = Math.abs(nowDate - lastclick);
|
|
60
|
+
if (diffTime < 400) {
|
|
61
|
+
click_data.current[rowId] = null;
|
|
62
|
+
return onRowDoubleClick && onRowDoubleClick(rowProps.rowIndex, rowProps.data);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
click_data.current[rowId] = nowDate;
|
|
67
|
+
return onRowClick && onRowClick(rowProps.rowIndex, rowProps.data);
|
|
68
|
+
}, []);
|
|
69
|
+
|
|
70
|
+
React.useEffect(() => {
|
|
71
|
+
click_data.current = {};
|
|
72
|
+
}, [props.dataSource]);
|
|
73
|
+
|
|
74
|
+
/* ------------------------------- ContextMenu ------------------------------ */
|
|
75
|
+
const onRowContextMenu = React.useCallback((rowProps, event) => {
|
|
76
|
+
if (!rowProps || !rowProps.data || func_selection.has_selection()) {
|
|
77
|
+
update_menuRow(null, null);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
|
|
82
|
+
const realRow = find_realData(rowProps.dataSourceArray, event.target, rowProps.data);
|
|
83
|
+
const anEl = func_selection.getVirtualElement(event.clientX, event.clientY);
|
|
84
|
+
update_menuRow(realRow, anEl);
|
|
85
|
+
return false;
|
|
86
|
+
}, []);
|
|
87
|
+
|
|
88
|
+
/* -------------------------------------------------------------------------- */
|
|
89
|
+
/* ------------------------------ Accessibility ----------------------------- */
|
|
90
|
+
const rootSyncFrame = React.useRef(0);
|
|
91
|
+
// last applied root values, so row renders (e.g. while scrolling) don't re-apply them
|
|
92
|
+
const lastRoot = React.useRef<{ el: Element | null; key: string; busy: boolean }>({
|
|
93
|
+
el: null,
|
|
94
|
+
key: '',
|
|
95
|
+
busy: false,
|
|
96
|
+
});
|
|
97
|
+
const getRootOptions = () => ({
|
|
98
|
+
ariaLabel: latestProps.current.ariaLabel,
|
|
99
|
+
editable: !!latestProps.current.editable,
|
|
100
|
+
});
|
|
101
|
+
// root attributes are set on the DOM node, batched to once per frame
|
|
102
|
+
const scheduleRootSync = React.useCallback((force = false) => {
|
|
103
|
+
if (rootSyncFrame.current || typeof requestAnimationFrame === 'undefined') return;
|
|
104
|
+
const grid = latestProps.current.liveGridRef?.current?.current;
|
|
105
|
+
const key = get_root_a11y_key(grid, getRootOptions());
|
|
106
|
+
if (
|
|
107
|
+
!force &&
|
|
108
|
+
key &&
|
|
109
|
+
key === lastRoot.current.key &&
|
|
110
|
+
get_grid_root(grid) === lastRoot.current.el
|
|
111
|
+
)
|
|
112
|
+
return;
|
|
113
|
+
rootSyncFrame.current = requestAnimationFrame(() => {
|
|
114
|
+
rootSyncFrame.current = 0;
|
|
115
|
+
const liveGrid = latestProps.current.liveGridRef?.current?.current;
|
|
116
|
+
const options = getRootOptions();
|
|
117
|
+
const el = apply_root_a11y(liveGrid, options);
|
|
118
|
+
lastRoot.current = {
|
|
119
|
+
el,
|
|
120
|
+
key: el ? get_root_a11y_key(liveGrid, options) : '',
|
|
121
|
+
busy: el ? is_grid_busy(liveGrid) : false,
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
}, []);
|
|
125
|
+
React.useEffect(() => {
|
|
126
|
+
scheduleRootSync();
|
|
127
|
+
}, [scheduleRootSync, props.ariaLabel, props.editable, props.columns, props.dataSource]);
|
|
128
|
+
React.useEffect(
|
|
129
|
+
() => () => {
|
|
130
|
+
if (rootSyncFrame.current) cancelAnimationFrame(rootSyncFrame.current);
|
|
131
|
+
rootSyncFrame.current = 0;
|
|
132
|
+
},
|
|
133
|
+
[]
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const onRenderRow = React.useCallback((rowProps) => {
|
|
137
|
+
// save the original handlers to be called later
|
|
138
|
+
rowProps['data-row-id'] = get_row_id(rowProps.data);
|
|
139
|
+
const liveGrid: any = latestProps.current.liveGridRef?.current?.current;
|
|
140
|
+
apply_row_a11y(rowProps, !!liveGrid?.computedRowSelectionEnabled);
|
|
141
|
+
scheduleRootSync();
|
|
142
|
+
}, []);
|
|
143
|
+
|
|
144
|
+
// called by the grid on every render with the load mask state, keeps aria-busy in sync
|
|
145
|
+
const onRenderLoadMask = React.useCallback((maskProps: { visible?: boolean }) => {
|
|
146
|
+
if (!!maskProps?.visible !== lastRoot.current.busy) scheduleRootSync(true);
|
|
147
|
+
}, []);
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
onRowClick,
|
|
151
|
+
onRenderLoadMask,
|
|
152
|
+
onRowContextMenu,
|
|
153
|
+
onRenderRow,
|
|
154
|
+
update_menuRow,
|
|
155
|
+
menuAnchorEl,
|
|
156
|
+
menuRow,
|
|
157
|
+
};
|
|
158
|
+
}
|