@bit.rhplus/ag-grid 0.0.36 → 0.0.38

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 (36) hide show
  1. package/Aggregations.js +26 -3
  2. package/BulkEdit/BulkEditButton.jsx +149 -0
  3. package/BulkEdit/BulkEditPopover.jsx +301 -0
  4. package/BulkEdit/BulkEditSelect.jsx +80 -0
  5. package/BulkEdit/index.js +5 -0
  6. package/BulkEdit/useBulkCellEdit.js +328 -0
  7. package/BulkEdit/utils.js +532 -0
  8. package/Renderers/StateRenderer.jsx +32 -7
  9. package/dist/Aggregations.d.ts +1 -1
  10. package/dist/Aggregations.js +19 -2
  11. package/dist/Aggregations.js.map +1 -1
  12. package/dist/BulkEdit/BulkEditButton.d.ts +27 -0
  13. package/dist/BulkEdit/BulkEditButton.js +82 -0
  14. package/dist/BulkEdit/BulkEditButton.js.map +1 -0
  15. package/dist/BulkEdit/BulkEditPopover.d.ts +27 -0
  16. package/dist/BulkEdit/BulkEditPopover.js +122 -0
  17. package/dist/BulkEdit/BulkEditPopover.js.map +1 -0
  18. package/dist/BulkEdit/BulkEditSelect.d.ts +14 -0
  19. package/dist/BulkEdit/BulkEditSelect.js +31 -0
  20. package/dist/BulkEdit/BulkEditSelect.js.map +1 -0
  21. package/dist/BulkEdit/index.d.ts +4 -0
  22. package/dist/BulkEdit/index.js +6 -0
  23. package/dist/BulkEdit/index.js.map +1 -0
  24. package/dist/BulkEdit/useBulkCellEdit.d.ts +1 -0
  25. package/dist/BulkEdit/useBulkCellEdit.js +262 -0
  26. package/dist/BulkEdit/useBulkCellEdit.js.map +1 -0
  27. package/dist/BulkEdit/utils.d.ts +7 -0
  28. package/dist/BulkEdit/utils.js +472 -0
  29. package/dist/BulkEdit/utils.js.map +1 -0
  30. package/dist/Renderers/StateRenderer.js +31 -7
  31. package/dist/Renderers/StateRenderer.js.map +1 -1
  32. package/dist/index.js +63 -25
  33. package/dist/index.js.map +1 -1
  34. package/index.jsx +103 -24
  35. package/package.json +5 -4
  36. /package/dist/{preview-1759925900657.js → preview-1760387336452.js} +0 -0
package/Aggregations.js CHANGED
@@ -56,8 +56,30 @@ const isValidDate = value => {
56
56
  return typeValid;
57
57
  }
58
58
 
59
+ // Cache pro optimalizaci výpočtů
60
+ let cachedAggregations = null;
61
+ let cachedRangeHash = null;
62
+
63
+ // Jednoduchý hash funkce pro rychlé porovnání ranges
64
+ const hashRanges = (ranges) => {
65
+ if (!ranges || ranges.length === 0) return null;
66
+ return ranges.map(r =>
67
+ `${r.startRow?.rowIndex}-${r.endRow?.rowIndex}-${r.columns?.length}-${r.columns?.[0]?.colId}`
68
+ ).join('|');
69
+ };
70
+
59
71
  export default function Aggregations(gridRef) {
60
72
  const ranges = gridRef.current.api.getCellRanges();
73
+
74
+ if (!ranges[0] || !ranges[0].startRow)
75
+ return {};
76
+
77
+ // Cache check - vrátit cache pokud se ranges nezměnily
78
+ const rangeHash = hashRanges(ranges);
79
+ if (cachedRangeHash === rangeHash && cachedAggregations) {
80
+ return cachedAggregations;
81
+ }
82
+
61
83
  let sum = 0; let min = null; let max = null; let avg = null; let count = 0; let numberCount = 0;
62
84
  let earliest; let latest;
63
85
  let square = 0;
@@ -69,9 +91,6 @@ export default function Aggregations(gridRef) {
69
91
  let dateCount = 0;
70
92
  let currencyMap = {}; // Pro sledování měn
71
93
 
72
- if (!ranges[0] || !ranges[0].startRow)
73
- return {};
74
-
75
94
  const startRow = Math.min(ranges[0].startRow.rowIndex, ranges[0].endRow.rowIndex);
76
95
  const domensionRanges = Enumerable.from(ranges).where(w => rangeContainsRowIndex(w, startRow)).toArray();
77
96
  const dimensions = getDimensionDefinition(domensionRanges);
@@ -257,6 +276,10 @@ export default function Aggregations(gridRef) {
257
276
  cols: [...sumCols].length
258
277
  };
259
278
 
279
+ // Uložit do cache
280
+ cachedAggregations = result;
281
+ cachedRangeHash = rangeHash;
282
+
260
283
  return result;
261
284
 
262
285
 
@@ -0,0 +1,149 @@
1
+ /* eslint-disable */
2
+ import React, { useState, useRef, useEffect } from 'react';
3
+ import { Button, Tooltip } from 'antd';
4
+ import { EditOutlined } from '@ant-design/icons';
5
+ import BulkEditPopover from './BulkEditPopover';
6
+
7
+ /**
8
+ * Floating button komponenta pro bulk edit
9
+ * @param {Object} props
10
+ * @param {boolean} props.visible - Viditelnost buttonu
11
+ * @param {Object} props.position - Pozice buttonu {x, y}
12
+ * @param {Object} props.range - AG Grid cell range
13
+ * @param {Object} props.column - AG Grid column objekt
14
+ * @param {number} props.cellCount - Počet buněk k editaci
15
+ * @param {Object} props.editPopover - State popoveru
16
+ * @param {Function} props.onOpenPopover - Handler pro otevření popoveru
17
+ * @param {Function} props.onValueChange - Handler pro změnu hodnoty
18
+ * @param {Function} props.onSubmit - Handler pro submit
19
+ * @param {Function} props.onCancel - Handler pro cancel
20
+ */
21
+ const BulkEditButton = ({
22
+ visible,
23
+ position,
24
+ range,
25
+ column,
26
+ cellCount,
27
+ editPopover,
28
+ onOpenPopover,
29
+ onValueChange,
30
+ onSubmit,
31
+ onCancel,
32
+ }) => {
33
+ const [popoverVisible, setPopoverVisible] = useState(false);
34
+ const buttonRef = useRef(null);
35
+ const popoverRef = useRef(null);
36
+
37
+ // Sync local popover state s parent editPopover state
38
+ useEffect(() => {
39
+ setPopoverVisible(editPopover.visible);
40
+ }, [editPopover.visible]);
41
+
42
+ if (!visible) return null;
43
+
44
+ const colDef = column?.getColDef();
45
+ const fieldName = colDef?.headerName || colDef?.field || 'Sloupec';
46
+
47
+ /**
48
+ * Handler pro kliknutí na button
49
+ */
50
+ const handleButtonClick = (e) => {
51
+ e.stopPropagation();
52
+ if (!popoverVisible) {
53
+ setPopoverVisible(true);
54
+ onOpenPopover();
55
+ }
56
+ };
57
+
58
+ /**
59
+ * Handler pro kliknutí mimo popover (close)
60
+ */
61
+ const handleClickOutside = (e) => {
62
+ if (
63
+ popoverVisible &&
64
+ popoverRef.current &&
65
+ !popoverRef.current.contains(e.target) &&
66
+ buttonRef.current &&
67
+ !buttonRef.current.contains(e.target)
68
+ ) {
69
+ onCancel();
70
+ }
71
+ };
72
+
73
+ // Click outside listener
74
+ useEffect(() => {
75
+ if (popoverVisible) {
76
+ document.addEventListener('mousedown', handleClickOutside);
77
+ return () => {
78
+ document.removeEventListener('mousedown', handleClickOutside);
79
+ };
80
+ }
81
+ }, [popoverVisible]);
82
+
83
+ const tooltipTitle = `Hromadně změnit ${cellCount} ${
84
+ cellCount === 1 ? 'buňku' : cellCount < 5 ? 'buňky' : 'buněk'
85
+ } v "${fieldName}"`;
86
+
87
+ return (
88
+ <>
89
+ {/* Floating Button */}
90
+ <div
91
+ ref={buttonRef}
92
+ style={{
93
+ position: 'fixed',
94
+ top: position?.y || 0,
95
+ left: position?.x || 0,
96
+ zIndex: 9999,
97
+ pointerEvents: 'auto',
98
+ }}
99
+ >
100
+ <Tooltip
101
+ title={tooltipTitle}
102
+ placement="top"
103
+ getPopupContainer={() => document.body}
104
+ zIndex={10001}
105
+ >
106
+ <Button
107
+ type="primary"
108
+ shape="circle"
109
+ icon={<EditOutlined />}
110
+ size="small"
111
+ onClick={handleButtonClick}
112
+ style={{
113
+ boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
114
+ }}
115
+ />
116
+ </Tooltip>
117
+ </div>
118
+
119
+ {/* Popover */}
120
+ {popoverVisible && (
121
+ <div
122
+ ref={popoverRef}
123
+ style={{
124
+ position: 'fixed',
125
+ top: (position?.y || 0) + 35, // Pod buttonem
126
+ left: position?.x || 0,
127
+ zIndex: 10000,
128
+ pointerEvents: 'auto',
129
+ }}
130
+ >
131
+ <BulkEditPopover
132
+ visible={editPopover.visible}
133
+ value={editPopover.value}
134
+ loading={editPopover.loading}
135
+ error={editPopover.error}
136
+ column={column}
137
+ range={range}
138
+ cellCount={cellCount}
139
+ onValueChange={onValueChange}
140
+ onSubmit={onSubmit}
141
+ onCancel={onCancel}
142
+ />
143
+ </div>
144
+ )}
145
+ </>
146
+ );
147
+ };
148
+
149
+ export default BulkEditButton;
@@ -0,0 +1,301 @@
1
+ /* eslint-disable */
2
+ import React, { useCallback, useEffect, useRef } from 'react';
3
+ import { Input, InputNumber, Button, Alert, Space } from 'antd';
4
+ import { validateValue, getInputType } from './utils';
5
+
6
+ /**
7
+ * Popover komponenta s inputem pro bulk edit
8
+ * @param {Object} props
9
+ * @param {boolean} props.visible - Viditelnost popoveru
10
+ * @param {string} props.value - Aktuální hodnota
11
+ * @param {boolean} props.loading - Loading state
12
+ * @param {string} props.error - Chybová hláška
13
+ * @param {Object} props.column - AG Grid column objekt
14
+ * @param {Object} props.range - AG Grid cell range objekt
15
+ * @param {number} props.cellCount - Počet buněk k editaci
16
+ * @param {Function} props.onValueChange - Handler pro změnu hodnoty
17
+ * @param {Function} props.onSubmit - Handler pro submit
18
+ * @param {Function} props.onCancel - Handler pro cancel
19
+ */
20
+ const BulkEditPopover = ({
21
+ visible,
22
+ value,
23
+ loading,
24
+ error,
25
+ column,
26
+ range,
27
+ cellCount,
28
+ onValueChange,
29
+ onSubmit,
30
+ onCancel,
31
+ }) => {
32
+ const inputRef = useRef(null);
33
+
34
+ // Focus input při otevření
35
+ useEffect(() => {
36
+ if (visible && inputRef.current) {
37
+ setTimeout(() => {
38
+ inputRef.current?.focus();
39
+ }, 100);
40
+ }
41
+ }, [visible]);
42
+
43
+ if (!visible) return null;
44
+
45
+ const colDef = column?.getColDef();
46
+ const inputType = getInputType(colDef);
47
+ const fieldName = colDef?.headerName || colDef?.field || 'Sloupec';
48
+
49
+ /**
50
+ * Handler pro submit
51
+ */
52
+ const handleSubmit = useCallback(() => {
53
+ // Validace hodnoty
54
+ const validation = validateValue(value, colDef);
55
+
56
+ if (!validation.valid) {
57
+ // Zobrazit error (parent komponenta zobrazí)
58
+ return;
59
+ }
60
+
61
+ onSubmit(validation.parsedValue);
62
+ }, [value, colDef, onSubmit]);
63
+
64
+ /**
65
+ * Handler pro Enter key
66
+ */
67
+ const handleKeyPress = useCallback(
68
+ (e) => {
69
+ if (e.key === 'Enter' && !loading) {
70
+ e.preventDefault();
71
+ handleSubmit();
72
+ } else if (e.key === 'Escape') {
73
+ e.preventDefault();
74
+ onCancel();
75
+ }
76
+ },
77
+ [handleSubmit, loading, onCancel]
78
+ );
79
+
80
+ /**
81
+ * Render input podle typu
82
+ */
83
+ const renderInput = () => {
84
+ // Check pro custom bulkEditPopover komponentu
85
+ const CustomComponent = colDef?.bulkEditPopover;
86
+
87
+ if (CustomComponent && typeof CustomComponent === 'function') {
88
+ // Renderovat custom komponentu s props
89
+ return (
90
+ <CustomComponent
91
+ value={value}
92
+ onChange={onValueChange}
93
+ onSubmit={onSubmit}
94
+ onCancel={onCancel}
95
+ loading={loading}
96
+ error={error}
97
+ column={column}
98
+ range={range}
99
+ cellCount={cellCount}
100
+ colDef={colDef}
101
+ />
102
+ );
103
+ }
104
+
105
+ // Default input podle typu
106
+ switch (inputType) {
107
+ case 'number':
108
+ return (
109
+ <InputNumber
110
+ ref={inputRef}
111
+ style={{ width: '100%' }}
112
+ value={value}
113
+ onChange={onValueChange}
114
+ onPressEnter={handleKeyPress}
115
+ placeholder="Zadejte číslo..."
116
+ disabled={loading}
117
+ />
118
+ );
119
+
120
+ case 'date':
121
+ return (
122
+ <Input
123
+ ref={inputRef}
124
+ type="date"
125
+ value={value}
126
+ onChange={(e) => onValueChange(e.target.value)}
127
+ onKeyDown={handleKeyPress}
128
+ placeholder="Vyberte datum..."
129
+ disabled={loading}
130
+ />
131
+ );
132
+
133
+ case 'text':
134
+ default:
135
+ return (
136
+ <Input
137
+ ref={inputRef}
138
+ value={value}
139
+ onChange={(e) => onValueChange(e.target.value)}
140
+ onKeyDown={handleKeyPress}
141
+ placeholder="Zadejte hodnotu..."
142
+ disabled={loading}
143
+ />
144
+ );
145
+ }
146
+ };
147
+
148
+ // Check zda je custom komponenta
149
+ const CustomComponent = colDef?.bulkEditPopover;
150
+ const isCustomComponent = CustomComponent && typeof CustomComponent === 'function';
151
+
152
+ // Check zda má custom komponenta automatická tlačítka (default: true)
153
+ const autoButtons = colDef?.bulkEditPopoverAutoButtons !== false;
154
+
155
+ // Pokud je custom komponenta s automatickými tlačítky
156
+ if (isCustomComponent && autoButtons) {
157
+ return (
158
+ <div
159
+ style={{
160
+ background: '#fff',
161
+ border: '1px solid #d9d9d9',
162
+ borderRadius: '4px',
163
+ padding: '12px',
164
+ boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
165
+ minWidth: '280px',
166
+ maxWidth: '320px',
167
+ }}
168
+ >
169
+ <div style={{ marginBottom: '8px' }}>
170
+ <div style={{ fontSize: '14px', fontWeight: 500, marginBottom: '4px' }}>
171
+ Hromadná změna
172
+ </div>
173
+ <div style={{ fontSize: '12px', color: '#8c8c8c' }}>
174
+ {fieldName} · {cellCount} {cellCount === 1 ? 'buňka' : cellCount < 5 ? 'buňky' : 'buněk'}
175
+ </div>
176
+ </div>
177
+
178
+ {error && (
179
+ <Alert
180
+ message={error}
181
+ type="error"
182
+ showIcon
183
+ closable
184
+ style={{ marginBottom: '12px' }}
185
+ />
186
+ )}
187
+
188
+ <div style={{ marginBottom: '12px' }}>
189
+ {renderInput()}
190
+ </div>
191
+
192
+ {/* Automatická tlačítka */}
193
+ <Space style={{ width: '100%', justifyContent: 'flex-end' }}>
194
+ <Button size="small" onClick={onCancel} disabled={loading}>
195
+ Zrušit
196
+ </Button>
197
+ <Button
198
+ type="primary"
199
+ size="small"
200
+ onClick={handleSubmit}
201
+ loading={loading}
202
+ >
203
+ Použít
204
+ </Button>
205
+ </Space>
206
+ </div>
207
+ );
208
+ }
209
+
210
+ // Pokud je custom komponenta BEZ automatických tlačítek (plná kontrola)
211
+ if (isCustomComponent && !autoButtons) {
212
+ return (
213
+ <div
214
+ style={{
215
+ background: '#fff',
216
+ border: '1px solid #d9d9d9',
217
+ borderRadius: '4px',
218
+ padding: '12px',
219
+ boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
220
+ minWidth: '280px',
221
+ maxWidth: '320px',
222
+ }}
223
+ >
224
+ <div style={{ marginBottom: '8px' }}>
225
+ <div style={{ fontSize: '14px', fontWeight: 500, marginBottom: '4px' }}>
226
+ Hromadná změna
227
+ </div>
228
+ <div style={{ fontSize: '12px', color: '#8c8c8c' }}>
229
+ {fieldName} · {cellCount} {cellCount === 1 ? 'buňka' : cellCount < 5 ? 'buňky' : 'buněk'}
230
+ </div>
231
+ </div>
232
+
233
+ {error && (
234
+ <Alert
235
+ message={error}
236
+ type="error"
237
+ showIcon
238
+ closable
239
+ style={{ marginBottom: '12px' }}
240
+ />
241
+ )}
242
+
243
+ {renderInput()}
244
+ </div>
245
+ );
246
+ }
247
+
248
+ // Default popover s vlastními tlačítky
249
+ return (
250
+ <div
251
+ style={{
252
+ background: '#fff',
253
+ border: '1px solid #d9d9d9',
254
+ borderRadius: '4px',
255
+ padding: '12px',
256
+ boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
257
+ minWidth: '280px',
258
+ maxWidth: '320px',
259
+ }}
260
+ >
261
+ <div style={{ marginBottom: '8px' }}>
262
+ <div style={{ fontSize: '14px', fontWeight: 500, marginBottom: '4px' }}>
263
+ Hromadná změna
264
+ </div>
265
+ <div style={{ fontSize: '12px', color: '#8c8c8c' }}>
266
+ {fieldName} · {cellCount} {cellCount === 1 ? 'buňka' : cellCount < 5 ? 'buňky' : 'buněk'}
267
+ </div>
268
+ </div>
269
+
270
+ {error && (
271
+ <Alert
272
+ message={error}
273
+ type="error"
274
+ showIcon
275
+ closable
276
+ style={{ marginBottom: '12px' }}
277
+ />
278
+ )}
279
+
280
+ <div style={{ marginBottom: '12px' }}>
281
+ {renderInput()}
282
+ </div>
283
+
284
+ <Space style={{ width: '100%', justifyContent: 'flex-end' }}>
285
+ <Button size="small" onClick={onCancel} disabled={loading}>
286
+ Zrušit
287
+ </Button>
288
+ <Button
289
+ type="primary"
290
+ size="small"
291
+ onClick={handleSubmit}
292
+ loading={loading}
293
+ >
294
+ Použít
295
+ </Button>
296
+ </Space>
297
+ </div>
298
+ );
299
+ };
300
+
301
+ export default BulkEditPopover;
@@ -0,0 +1,80 @@
1
+ /* eslint-disable */
2
+ import React, { useRef, useEffect } from 'react';
3
+ import { Select, Button, Space } from 'antd';
4
+
5
+ const BulkEditSelect = ({
6
+ value,
7
+ onChange,
8
+ onSubmit,
9
+ onCancel,
10
+ loading,
11
+ options = [],
12
+ placeholder = 'Vyberte hodnotu...',
13
+ allowClear = true,
14
+ fieldLabel = 'value',
15
+ fieldValue = 'id',
16
+ showSearch = false,
17
+ }) => {
18
+ const selectRef = useRef(null);
19
+
20
+ useEffect(() => {
21
+ if (selectRef.current) {
22
+ setTimeout(() => {
23
+ selectRef.current?.focus();
24
+ }, 100);
25
+ }
26
+ }, []);
27
+
28
+ const handleKeyDown = (e) => {
29
+ if (e.key === 'Enter' && !loading) {
30
+ e.preventDefault();
31
+ onSubmit(value);
32
+ } else if (e.key === 'Escape') {
33
+ e.preventDefault();
34
+ onCancel();
35
+ }
36
+ };
37
+
38
+ return (
39
+ <>
40
+ <div style={{ marginBottom: '12px' }} onClick={(e) => e.stopPropagation()}>
41
+ <Select
42
+ ref={selectRef}
43
+ style={{ width: '100%' }}
44
+ value={value}
45
+ onChange={onChange}
46
+ placeholder={placeholder}
47
+ disabled={loading}
48
+ allowClear={allowClear}
49
+ showSearch={showSearch}
50
+ filterOption={showSearch ? (input, option) =>
51
+ (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
52
+ : false}
53
+ options={options.map(item => ({
54
+ label: item[fieldLabel],
55
+ value: item[fieldValue]
56
+ }))}
57
+ onKeyDown={handleKeyDown}
58
+ onClick={(e) => e.stopPropagation()}
59
+ getPopupContainer={(trigger) => trigger.parentNode}
60
+ />
61
+ </div>
62
+
63
+ <Space style={{ width: '100%', justifyContent: 'flex-end' }}>
64
+ <Button size="small" onClick={onCancel} disabled={loading}>
65
+ Zrušit
66
+ </Button>
67
+ <Button
68
+ type="primary"
69
+ size="small"
70
+ onClick={() => onSubmit(value)}
71
+ loading={loading}
72
+ >
73
+ Použít
74
+ </Button>
75
+ </Space>
76
+ </>
77
+ );
78
+ };
79
+
80
+ export default BulkEditSelect;
@@ -0,0 +1,5 @@
1
+ /* eslint-disable */
2
+ export { useBulkCellEdit } from './useBulkCellEdit';
3
+ export { default as BulkEditButton } from './BulkEditButton';
4
+ export { default as BulkEditPopover } from './BulkEditPopover';
5
+ export * from './utils';