@bit.rhplus/ag-grid 0.0.38 → 0.0.40
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/Aggregations.js +1 -1
- package/BulkEdit/BulkEditDatePicker.jsx +295 -0
- package/BulkEdit/BulkEditPopover.jsx +2 -2
- package/BulkEdit/index.js +4 -0
- package/BulkEdit/useBulkCellEdit.js +113 -55
- package/BulkEdit/utils.js +48 -8
- package/dist/Aggregations.d.ts +1 -0
- package/dist/Aggregations.js +1 -1
- package/dist/Aggregations.js.map +1 -1
- package/dist/BulkEdit/BulkEditDatePicker.d.ts +10 -0
- package/dist/BulkEdit/BulkEditDatePicker.js +154 -0
- package/dist/BulkEdit/BulkEditDatePicker.js.map +1 -0
- package/dist/BulkEdit/BulkEditPopover.js +2 -2
- package/dist/BulkEdit/BulkEditPopover.js.map +1 -1
- package/dist/BulkEdit/index.d.ts +2 -0
- package/dist/BulkEdit/index.js +2 -0
- package/dist/BulkEdit/index.js.map +1 -1
- package/dist/BulkEdit/useBulkCellEdit.js +101 -52
- package/dist/BulkEdit/useBulkCellEdit.js.map +1 -1
- package/dist/BulkEdit/utils.js +45 -5
- package/dist/BulkEdit/utils.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +167 -46
- package/dist/index.js.map +1 -1
- package/index.jsx +196 -50
- package/package.json +7 -4
- /package/dist/{preview-1760387336452.js → preview-1760449337207.js} +0 -0
package/Aggregations.js
CHANGED
|
@@ -61,7 +61,7 @@ let cachedAggregations = null;
|
|
|
61
61
|
let cachedRangeHash = null;
|
|
62
62
|
|
|
63
63
|
// Jednoduchý hash funkce pro rychlé porovnání ranges
|
|
64
|
-
const hashRanges = (ranges) => {
|
|
64
|
+
export const hashRanges = (ranges) => {
|
|
65
65
|
if (!ranges || ranges.length === 0) return null;
|
|
66
66
|
return ranges.map(r =>
|
|
67
67
|
`${r.startRow?.rowIndex}-${r.endRow?.rowIndex}-${r.columns?.length}-${r.columns?.[0]?.colId}`
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import React, { useState, useCallback, useMemo } from 'react';
|
|
3
|
+
import { Calendar, Button, Space, Tooltip } from 'antd';
|
|
4
|
+
import dayjs from 'dayjs';
|
|
5
|
+
import 'dayjs/locale/cs';
|
|
6
|
+
import styled from 'styled-components';
|
|
7
|
+
import Holidays from 'date-holidays';
|
|
8
|
+
|
|
9
|
+
dayjs.locale('cs');
|
|
10
|
+
|
|
11
|
+
const CalendarContainer = styled.div`
|
|
12
|
+
display: flex;
|
|
13
|
+
gap: 8px;
|
|
14
|
+
margin-bottom: 12px;
|
|
15
|
+
width: 900px;
|
|
16
|
+
justify-content: space-between;
|
|
17
|
+
|
|
18
|
+
.ant-picker-calendar {
|
|
19
|
+
max-width: 260px;
|
|
20
|
+
flex: 1;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.ant-picker-calendar-header {
|
|
24
|
+
padding: 8px 12px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.ant-picker-cell-selected .ant-picker-cell-inner {
|
|
28
|
+
background: transparent !important;
|
|
29
|
+
color: inherit !important;
|
|
30
|
+
font-weight: normal !important;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.ant-picker-cell-disabled {
|
|
34
|
+
pointer-events: none;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.ant-picker-cell-disabled .ant-picker-cell-inner {
|
|
38
|
+
color: rgba(0, 0, 0, 0.25) !important;
|
|
39
|
+
background: transparent !important;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.ant-picker-cell:not(.ant-picker-cell-in-view) .ant-picker-cell-inner {
|
|
43
|
+
color: rgba(0, 0, 0, 0.25) !important;
|
|
44
|
+
background: transparent !important;
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
const BulkEditDatePicker = ({
|
|
49
|
+
value,
|
|
50
|
+
onChange,
|
|
51
|
+
onSubmit,
|
|
52
|
+
onCancel,
|
|
53
|
+
loading,
|
|
54
|
+
format = 'DD.MM.YYYY',
|
|
55
|
+
showTime = false,
|
|
56
|
+
}) => {
|
|
57
|
+
const today = dayjs();
|
|
58
|
+
const [selectedDate, setSelectedDate] = useState(value ? dayjs(value) : null);
|
|
59
|
+
const [baseMonth, setBaseMonth] = useState(today);
|
|
60
|
+
|
|
61
|
+
const month1 = baseMonth;
|
|
62
|
+
const month2 = baseMonth.add(1, 'month');
|
|
63
|
+
const month3 = baseMonth.add(2, 'month');
|
|
64
|
+
|
|
65
|
+
const hd = useMemo(() => new Holidays('CZ'), []);
|
|
66
|
+
|
|
67
|
+
const handleDateSelect = (date) => {
|
|
68
|
+
setSelectedDate(date);
|
|
69
|
+
if (showTime) {
|
|
70
|
+
onChange(date.toISOString());
|
|
71
|
+
} else {
|
|
72
|
+
onChange(date.format('YYYY-MM-DD'));
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const getHoliday = useCallback((date) => {
|
|
77
|
+
const holidays = hd.isHoliday(date.toDate());
|
|
78
|
+
return holidays && holidays.length > 0 ? holidays[0].name : null;
|
|
79
|
+
}, [hd]);
|
|
80
|
+
|
|
81
|
+
const getVisibleHolidays = useCallback(() => {
|
|
82
|
+
const holidays = [];
|
|
83
|
+
const months = [month1, month2, month3];
|
|
84
|
+
|
|
85
|
+
months.forEach(month => {
|
|
86
|
+
const startOfMonth = month.startOf('month');
|
|
87
|
+
const endOfMonth = month.endOf('month');
|
|
88
|
+
let current = startOfMonth;
|
|
89
|
+
|
|
90
|
+
while (current.isBefore(endOfMonth) || current.isSame(endOfMonth, 'day')) {
|
|
91
|
+
const holidayName = getHoliday(current);
|
|
92
|
+
if (holidayName) {
|
|
93
|
+
holidays.push({
|
|
94
|
+
date: current.format('DD.MM.YYYY'),
|
|
95
|
+
name: holidayName,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
current = current.add(1, 'day');
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return holidays;
|
|
103
|
+
}, [month1, month2, month3, getHoliday]);
|
|
104
|
+
|
|
105
|
+
const visibleHolidays = useMemo(() => getVisibleHolidays(), [getVisibleHolidays]);
|
|
106
|
+
|
|
107
|
+
const createCellRender = useCallback((monthValue) => {
|
|
108
|
+
return (current, info) => {
|
|
109
|
+
const isSelected = selectedDate &&
|
|
110
|
+
current.date() === selectedDate.date() &&
|
|
111
|
+
current.month() === selectedDate.month() &&
|
|
112
|
+
current.year() === selectedDate.year();
|
|
113
|
+
const isToday = current.isSame(today, 'day');
|
|
114
|
+
const holidayName = getHoliday(current);
|
|
115
|
+
|
|
116
|
+
if (isSelected) {
|
|
117
|
+
return (
|
|
118
|
+
<div className="ant-picker-cell-inner" style={{
|
|
119
|
+
background: '#1890ff',
|
|
120
|
+
color: 'white',
|
|
121
|
+
fontWeight: 600,
|
|
122
|
+
}}>
|
|
123
|
+
{current.date()}
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (holidayName) {
|
|
129
|
+
return (
|
|
130
|
+
<div className="ant-picker-cell-inner" style={{
|
|
131
|
+
background: '#fff1f0',
|
|
132
|
+
color: '#cf1322',
|
|
133
|
+
fontWeight: 600,
|
|
134
|
+
}}>
|
|
135
|
+
{current.date()}
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (isToday) {
|
|
141
|
+
return (
|
|
142
|
+
<div className="ant-picker-cell-inner" style={{
|
|
143
|
+
background: '#e6f7ff',
|
|
144
|
+
color: '#1890ff',
|
|
145
|
+
fontWeight: 600,
|
|
146
|
+
border: '1px solid #1890ff',
|
|
147
|
+
}}>
|
|
148
|
+
{current.date()}
|
|
149
|
+
</div>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<div className="ant-picker-cell-inner">
|
|
155
|
+
{current.date()}
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
158
|
+
};
|
|
159
|
+
}, [selectedDate, today, getHoliday]);
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<>
|
|
163
|
+
<CalendarContainer onClick={(e) => e.stopPropagation()}>
|
|
164
|
+
<Calendar
|
|
165
|
+
fullscreen={false}
|
|
166
|
+
value={month1}
|
|
167
|
+
onSelect={handleDateSelect}
|
|
168
|
+
fullCellRender={createCellRender(month1)}
|
|
169
|
+
headerRender={({ value: currentValue }) => (
|
|
170
|
+
<div style={{
|
|
171
|
+
padding: '8px 12px',
|
|
172
|
+
fontWeight: 500,
|
|
173
|
+
textAlign: 'center',
|
|
174
|
+
display: 'flex',
|
|
175
|
+
alignItems: 'center',
|
|
176
|
+
justifyContent: 'space-between'
|
|
177
|
+
}}>
|
|
178
|
+
<span
|
|
179
|
+
style={{ cursor: 'pointer', padding: '0 4px' }}
|
|
180
|
+
onClick={() => setBaseMonth(baseMonth.subtract(1, 'month'))}
|
|
181
|
+
>
|
|
182
|
+
‹
|
|
183
|
+
</span>
|
|
184
|
+
<span>{currentValue.format('MMMM YYYY')}</span>
|
|
185
|
+
<span
|
|
186
|
+
style={{ cursor: 'pointer', padding: '0 4px' }}
|
|
187
|
+
onClick={() => setBaseMonth(baseMonth.add(1, 'month'))}
|
|
188
|
+
>
|
|
189
|
+
›
|
|
190
|
+
</span>
|
|
191
|
+
</div>
|
|
192
|
+
)}
|
|
193
|
+
/>
|
|
194
|
+
<Calendar
|
|
195
|
+
fullscreen={false}
|
|
196
|
+
value={month2}
|
|
197
|
+
onSelect={handleDateSelect}
|
|
198
|
+
fullCellRender={createCellRender(month2)}
|
|
199
|
+
headerRender={({ value: currentValue }) => (
|
|
200
|
+
<div style={{
|
|
201
|
+
padding: '8px 12px',
|
|
202
|
+
fontWeight: 500,
|
|
203
|
+
textAlign: 'center',
|
|
204
|
+
display: 'flex',
|
|
205
|
+
alignItems: 'center',
|
|
206
|
+
justifyContent: 'space-between'
|
|
207
|
+
}}>
|
|
208
|
+
<span
|
|
209
|
+
style={{ cursor: 'pointer', padding: '0 4px' }}
|
|
210
|
+
onClick={() => setBaseMonth(baseMonth.subtract(1, 'month'))}
|
|
211
|
+
>
|
|
212
|
+
‹
|
|
213
|
+
</span>
|
|
214
|
+
<span>{currentValue.format('MMMM YYYY')}</span>
|
|
215
|
+
<span
|
|
216
|
+
style={{ cursor: 'pointer', padding: '0 4px' }}
|
|
217
|
+
onClick={() => setBaseMonth(baseMonth.add(1, 'month'))}
|
|
218
|
+
>
|
|
219
|
+
›
|
|
220
|
+
</span>
|
|
221
|
+
</div>
|
|
222
|
+
)}
|
|
223
|
+
/>
|
|
224
|
+
<Calendar
|
|
225
|
+
fullscreen={false}
|
|
226
|
+
value={month3}
|
|
227
|
+
onSelect={handleDateSelect}
|
|
228
|
+
fullCellRender={createCellRender(month3)}
|
|
229
|
+
headerRender={({ value: currentValue }) => (
|
|
230
|
+
<div style={{
|
|
231
|
+
padding: '8px 12px',
|
|
232
|
+
fontWeight: 500,
|
|
233
|
+
textAlign: 'center',
|
|
234
|
+
display: 'flex',
|
|
235
|
+
alignItems: 'center',
|
|
236
|
+
justifyContent: 'space-between'
|
|
237
|
+
}}>
|
|
238
|
+
<span
|
|
239
|
+
style={{ cursor: 'pointer', padding: '0 4px' }}
|
|
240
|
+
onClick={() => setBaseMonth(baseMonth.subtract(1, 'month'))}
|
|
241
|
+
>
|
|
242
|
+
‹
|
|
243
|
+
</span>
|
|
244
|
+
<span>{currentValue.format('MMMM YYYY')}</span>
|
|
245
|
+
<span
|
|
246
|
+
style={{ cursor: 'pointer', padding: '0 4px' }}
|
|
247
|
+
onClick={() => setBaseMonth(baseMonth.add(1, 'month'))}
|
|
248
|
+
>
|
|
249
|
+
›
|
|
250
|
+
</span>
|
|
251
|
+
</div>
|
|
252
|
+
)}
|
|
253
|
+
/>
|
|
254
|
+
</CalendarContainer>
|
|
255
|
+
|
|
256
|
+
{visibleHolidays.length > 0 && (
|
|
257
|
+
<div style={{ marginBottom: '12px', fontSize: '11px', color: '#666' }}>
|
|
258
|
+
{visibleHolidays.map((holiday, index) => {
|
|
259
|
+
const isSelectedHoliday = selectedDate && holiday.date === selectedDate.format('DD.MM.YYYY');
|
|
260
|
+
return (
|
|
261
|
+
<div
|
|
262
|
+
key={index}
|
|
263
|
+
style={{
|
|
264
|
+
marginBottom: '2px',
|
|
265
|
+
background: isSelectedHoliday ? '#e6f7ff' : 'transparent',
|
|
266
|
+
padding: isSelectedHoliday ? '2px 4px' : '0',
|
|
267
|
+
borderRadius: '2px',
|
|
268
|
+
fontWeight: isSelectedHoliday ? 600 : 'normal',
|
|
269
|
+
}}
|
|
270
|
+
>
|
|
271
|
+
<span style={{ color: '#cf1322', fontWeight: 500 }}>{holiday.date}</span> - {holiday.name}
|
|
272
|
+
</div>
|
|
273
|
+
);
|
|
274
|
+
})}
|
|
275
|
+
</div>
|
|
276
|
+
)}
|
|
277
|
+
|
|
278
|
+
<Space style={{ width: '100%', justifyContent: 'flex-end' }}>
|
|
279
|
+
<Button size="small" onClick={onCancel} disabled={loading}>
|
|
280
|
+
Zrušit
|
|
281
|
+
</Button>
|
|
282
|
+
<Button
|
|
283
|
+
type="primary"
|
|
284
|
+
size="small"
|
|
285
|
+
onClick={() => onSubmit(value)}
|
|
286
|
+
loading={loading}
|
|
287
|
+
>
|
|
288
|
+
Použít
|
|
289
|
+
</Button>
|
|
290
|
+
</Space>
|
|
291
|
+
</>
|
|
292
|
+
);
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
export default BulkEditDatePicker;
|
|
@@ -163,7 +163,7 @@ const BulkEditPopover = ({
|
|
|
163
163
|
padding: '12px',
|
|
164
164
|
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
|
|
165
165
|
minWidth: '280px',
|
|
166
|
-
maxWidth: '
|
|
166
|
+
maxWidth: '1050px',
|
|
167
167
|
}}
|
|
168
168
|
>
|
|
169
169
|
<div style={{ marginBottom: '8px' }}>
|
|
@@ -218,7 +218,7 @@ const BulkEditPopover = ({
|
|
|
218
218
|
padding: '12px',
|
|
219
219
|
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
|
|
220
220
|
minWidth: '280px',
|
|
221
|
-
maxWidth: '
|
|
221
|
+
maxWidth: '1050px',
|
|
222
222
|
}}
|
|
223
223
|
>
|
|
224
224
|
<div style={{ marginBottom: '8px' }}>
|
package/BulkEdit/index.js
CHANGED
|
@@ -2,4 +2,8 @@
|
|
|
2
2
|
export { useBulkCellEdit } from './useBulkCellEdit';
|
|
3
3
|
export { default as BulkEditButton } from './BulkEditButton';
|
|
4
4
|
export { default as BulkEditPopover } from './BulkEditPopover';
|
|
5
|
+
export { default as BulkEditSelect } from './BulkEditSelect';
|
|
6
|
+
export { default as BulkEditDatePicker } from './BulkEditDatePicker';
|
|
7
|
+
|
|
5
8
|
export * from './utils';
|
|
9
|
+
|
|
@@ -44,27 +44,111 @@ export const useBulkCellEdit = (gridRef, options = {}) => {
|
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
const scrollListenerRef = useRef(null);
|
|
47
|
-
const
|
|
47
|
+
const debounceTimeoutRef = useRef(null);
|
|
48
|
+
const pendingEventRef = useRef(null);
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
|
-
*
|
|
51
|
+
* Helper funkce pro výpočet pozice buttonu
|
|
52
|
+
*/
|
|
53
|
+
const calculateButtonPosition = useCallback((cellPosition) => {
|
|
54
|
+
let buttonX, buttonY;
|
|
55
|
+
|
|
56
|
+
switch (buttonPosition) {
|
|
57
|
+
case 'bottom-right':
|
|
58
|
+
buttonX = cellPosition.right + buttonOffset.x;
|
|
59
|
+
buttonY = cellPosition.bottom + buttonOffset.y;
|
|
60
|
+
break;
|
|
61
|
+
case 'bottom-left':
|
|
62
|
+
buttonX = cellPosition.left - buttonOffset.x;
|
|
63
|
+
buttonY = cellPosition.bottom + buttonOffset.y;
|
|
64
|
+
break;
|
|
65
|
+
case 'top-right':
|
|
66
|
+
buttonX = cellPosition.right + buttonOffset.x;
|
|
67
|
+
buttonY = cellPosition.top - buttonOffset.y;
|
|
68
|
+
break;
|
|
69
|
+
case 'top-left':
|
|
70
|
+
buttonX = cellPosition.left - buttonOffset.x;
|
|
71
|
+
buttonY = cellPosition.top - buttonOffset.y;
|
|
72
|
+
break;
|
|
73
|
+
default:
|
|
74
|
+
buttonX = cellPosition.right + buttonOffset.x;
|
|
75
|
+
buttonY = cellPosition.bottom + buttonOffset.y;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { x: buttonX, y: buttonY };
|
|
79
|
+
}, [buttonPosition, buttonOffset]);
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Handler pro změnu range selection s okamžitou + debounced validací
|
|
51
83
|
*/
|
|
52
84
|
const handleRangeChange = useCallback(
|
|
53
85
|
(event) => {
|
|
54
|
-
//
|
|
55
|
-
if (rangeTimeoutRef.current) {
|
|
56
|
-
clearTimeout(rangeTimeoutRef.current);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// 1. Kontrola enableBulkEdit
|
|
86
|
+
// Lightweight pre-checks - okamžitě bez debounce
|
|
60
87
|
if (!enabled) {
|
|
61
88
|
setFloatingButton({ visible: false });
|
|
62
89
|
return;
|
|
63
90
|
}
|
|
64
91
|
|
|
65
92
|
const ranges = event.api.getCellRanges();
|
|
93
|
+
if (!ranges || ranges.length === 0) {
|
|
94
|
+
setFloatingButton({ visible: false });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const range = ranges[0];
|
|
99
|
+
|
|
100
|
+
// ✨ OKAŽITÁ lightweight validace - zobraz button HNED
|
|
101
|
+
if (allowMultiColumn || range.columns.length === 1) {
|
|
102
|
+
const cellCount = Math.abs(range.endRow.rowIndex - range.startRow.rowIndex) + 1;
|
|
103
|
+
|
|
104
|
+
if (cellCount >= minCells) {
|
|
105
|
+
// Kontrola zda má sloupec bulkEditApi (rychlá kontrola)
|
|
106
|
+
const column = range.columns[0];
|
|
107
|
+
const colDef = column?.getColDef();
|
|
108
|
+
const hasBulkEditApi = colDef?.bulkEditApi || colDef?.bulkEditPopover;
|
|
109
|
+
|
|
110
|
+
// Zobraz button jen pokud má bulkEditApi nebo bulkEditPopover
|
|
111
|
+
if (hasBulkEditApi) {
|
|
112
|
+
const cellPosition = getLastCellPosition(range, event.api);
|
|
113
|
+
if (cellPosition) {
|
|
114
|
+
setFloatingButton({
|
|
115
|
+
visible: true,
|
|
116
|
+
position: calculateButtonPosition(cellPosition),
|
|
117
|
+
range,
|
|
118
|
+
column: column,
|
|
119
|
+
cellCount,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
// Skryj button pokud sloupec nemá bulk edit podporu
|
|
124
|
+
setFloatingButton({ visible: false });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Uložit event pro debounced těžkou validaci (editable check)
|
|
130
|
+
pendingEventRef.current = event;
|
|
66
131
|
|
|
67
|
-
//
|
|
132
|
+
// Debounce logika - těžká validace proběhne na pozadí (50ms)
|
|
133
|
+
if (debounceTimeoutRef.current) {
|
|
134
|
+
clearTimeout(debounceTimeoutRef.current);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
debounceTimeoutRef.current = setTimeout(() => {
|
|
138
|
+
if (pendingEventRef.current) {
|
|
139
|
+
executeRangeValidation(pendingEventRef.current);
|
|
140
|
+
}
|
|
141
|
+
}, 50);
|
|
142
|
+
},
|
|
143
|
+
[enabled, allowMultiColumn, minCells, calculateButtonPosition]
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Těžké validace a výpočty v debounced callbacku
|
|
148
|
+
*/
|
|
149
|
+
const executeRangeValidation = useCallback(
|
|
150
|
+
(event) => {
|
|
151
|
+
const ranges = event.api.getCellRanges();
|
|
68
152
|
if (!ranges || ranges.length === 0) {
|
|
69
153
|
setFloatingButton({ visible: false });
|
|
70
154
|
return;
|
|
@@ -72,70 +156,44 @@ export const useBulkCellEdit = (gridRef, options = {}) => {
|
|
|
72
156
|
|
|
73
157
|
const range = ranges[0];
|
|
74
158
|
|
|
75
|
-
//
|
|
159
|
+
// Kontrola single column
|
|
76
160
|
if (!allowMultiColumn && range.columns.length !== 1) {
|
|
77
161
|
setFloatingButton({ visible: false });
|
|
78
162
|
return;
|
|
79
163
|
}
|
|
80
164
|
|
|
81
|
-
//
|
|
165
|
+
// Výpočet počtu buněk
|
|
82
166
|
const cellCount =
|
|
83
167
|
Math.abs(range.endRow.rowIndex - range.startRow.rowIndex) + 1;
|
|
84
168
|
|
|
85
|
-
//
|
|
169
|
+
// Kontrola min počtu buněk
|
|
86
170
|
if (cellCount < minCells) {
|
|
87
171
|
setFloatingButton({ visible: false });
|
|
88
172
|
return;
|
|
89
173
|
}
|
|
90
174
|
|
|
91
|
-
//
|
|
175
|
+
// Těžká validace - editable sloupce
|
|
92
176
|
if (!validateRangeEditable(range, event.api)) {
|
|
93
177
|
setFloatingButton({ visible: false });
|
|
94
178
|
return;
|
|
95
179
|
}
|
|
96
180
|
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
let buttonX, buttonY;
|
|
106
|
-
|
|
107
|
-
switch (buttonPosition) {
|
|
108
|
-
case 'bottom-right':
|
|
109
|
-
buttonX = cellPosition.right + buttonOffset.x;
|
|
110
|
-
buttonY = cellPosition.bottom + buttonOffset.y;
|
|
111
|
-
break;
|
|
112
|
-
case 'bottom-left':
|
|
113
|
-
buttonX = cellPosition.left - buttonOffset.x;
|
|
114
|
-
buttonY = cellPosition.bottom + buttonOffset.y;
|
|
115
|
-
break;
|
|
116
|
-
case 'top-right':
|
|
117
|
-
buttonX = cellPosition.right + buttonOffset.x;
|
|
118
|
-
buttonY = cellPosition.top - buttonOffset.y;
|
|
119
|
-
break;
|
|
120
|
-
case 'top-left':
|
|
121
|
-
buttonX = cellPosition.left - buttonOffset.x;
|
|
122
|
-
buttonY = cellPosition.top - buttonOffset.y;
|
|
123
|
-
break;
|
|
124
|
-
default:
|
|
125
|
-
buttonX = cellPosition.right + buttonOffset.x;
|
|
126
|
-
buttonY = cellPosition.bottom + buttonOffset.y;
|
|
127
|
-
}
|
|
181
|
+
// Těžký výpočet - pozice buňky
|
|
182
|
+
const cellPosition = getLastCellPosition(range, event.api);
|
|
183
|
+
if (!cellPosition) {
|
|
184
|
+
setFloatingButton({ visible: false });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
128
187
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}, 500);
|
|
188
|
+
setFloatingButton({
|
|
189
|
+
visible: true,
|
|
190
|
+
position: calculateButtonPosition(cellPosition),
|
|
191
|
+
range,
|
|
192
|
+
column: range.columns[0],
|
|
193
|
+
cellCount,
|
|
194
|
+
});
|
|
137
195
|
},
|
|
138
|
-
[
|
|
196
|
+
[minCells, allowMultiColumn, buttonPosition, buttonOffset]
|
|
139
197
|
);
|
|
140
198
|
|
|
141
199
|
/**
|
|
@@ -310,8 +368,8 @@ export const useBulkCellEdit = (gridRef, options = {}) => {
|
|
|
310
368
|
*/
|
|
311
369
|
useEffect(() => {
|
|
312
370
|
return () => {
|
|
313
|
-
if (
|
|
314
|
-
clearTimeout(
|
|
371
|
+
if (debounceTimeoutRef.current) {
|
|
372
|
+
clearTimeout(debounceTimeoutRef.current);
|
|
315
373
|
}
|
|
316
374
|
};
|
|
317
375
|
}, []);
|
package/BulkEdit/utils.js
CHANGED
|
@@ -386,13 +386,28 @@ export const applyBulkChangesWithApi = async (
|
|
|
386
386
|
};
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
-
// Sestavit request podle bulkEditApi
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
389
|
+
// Sestavit request podle bulkEditApi konfigurace
|
|
390
|
+
let requestData;
|
|
391
|
+
if (bulkEditApi.lookup) {
|
|
392
|
+
// Lookup pattern - automaticky vytvořit PATCH request
|
|
393
|
+
requestData = {
|
|
394
|
+
ids,
|
|
395
|
+
patch: [{
|
|
396
|
+
op: 'replace',
|
|
397
|
+
path: bulkEditApi.lookup.patchPath,
|
|
398
|
+
value: newValue
|
|
399
|
+
}]
|
|
400
|
+
};
|
|
401
|
+
} else if (bulkEditApi.mapRequest) {
|
|
402
|
+
// Custom mapRequest funkce
|
|
403
|
+
requestData = bulkEditApi.mapRequest(ids, newValue, field, rows);
|
|
404
|
+
} else {
|
|
405
|
+
// Fallback default
|
|
406
|
+
requestData = {
|
|
407
|
+
ids,
|
|
408
|
+
patch: [{ op: 'replace', path: `/${field}`, value: newValue }]
|
|
409
|
+
};
|
|
410
|
+
}
|
|
396
411
|
|
|
397
412
|
// Zavolat API
|
|
398
413
|
const response = await fetchDataUIAsync(
|
|
@@ -404,7 +419,32 @@ export const applyBulkChangesWithApi = async (
|
|
|
404
419
|
if (response.success) {
|
|
405
420
|
// Update grid - použít data z response nebo lokálně
|
|
406
421
|
let updatedData;
|
|
407
|
-
if (bulkEditApi.
|
|
422
|
+
if (bulkEditApi.lookup) {
|
|
423
|
+
// Lookup pattern - najít objekt v lookupData a aktualizovat rows
|
|
424
|
+
let selectedItem = null;
|
|
425
|
+
|
|
426
|
+
// Pokud je newValue prázdné ("" nebo null), nastavit selectedItem na null
|
|
427
|
+
if (newValue !== "" && newValue !== null && newValue !== undefined) {
|
|
428
|
+
selectedItem = bulkEditApi.lookup.data.find(item => item.id === newValue);
|
|
429
|
+
|
|
430
|
+
if (!selectedItem) {
|
|
431
|
+
console.warn(`⚠️ ${bulkEditApi.lookup.objectField} nenalezen pro ID:`, newValue);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Aktualizovat rows - i když je selectedItem null (= clear hodnoty)
|
|
436
|
+
updatedData = rows.map(row => {
|
|
437
|
+
if (ids.includes(row.id)) {
|
|
438
|
+
return {
|
|
439
|
+
...row,
|
|
440
|
+
[bulkEditApi.lookup.objectField]: selectedItem,
|
|
441
|
+
[bulkEditApi.lookup.idField]: selectedItem ? selectedItem.id : null
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
return row;
|
|
445
|
+
});
|
|
446
|
+
} else if (bulkEditApi.mapResponse) {
|
|
447
|
+
// Custom mapResponse funkce
|
|
408
448
|
updatedData = bulkEditApi.mapResponse(response.data, ids, rows, field);
|
|
409
449
|
} else if (response.data?.records) {
|
|
410
450
|
// Mapovat response records (id + value) na existující rows
|
package/dist/Aggregations.d.ts
CHANGED
package/dist/Aggregations.js
CHANGED
|
@@ -53,7 +53,7 @@ const isValidDate = value => {
|
|
|
53
53
|
let cachedAggregations = null;
|
|
54
54
|
let cachedRangeHash = null;
|
|
55
55
|
// Jednoduchý hash funkce pro rychlé porovnání ranges
|
|
56
|
-
const hashRanges = (ranges) => {
|
|
56
|
+
export const hashRanges = (ranges) => {
|
|
57
57
|
if (!ranges || ranges.length === 0)
|
|
58
58
|
return null;
|
|
59
59
|
return ranges.map(r => `${r.startRow?.rowIndex}-${r.endRow?.rowIndex}-${r.columns?.length}-${r.columns?.[0]?.colId}`).join('|');
|