@bit.rhplus/ag-grid 0.0.63 → 0.0.64

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/index.jsx CHANGED
@@ -17,7 +17,9 @@ import SelectRenderer from './Renderers/SelectRenderer';
17
17
  import ButtonRenderer from './Renderers/ButtonRenderer';
18
18
  import CountrySelectRenderer from './Renderers/CountrySelectRenderer';
19
19
  import NotificationOptionsInit from "./NotificationOptions";
20
- import { notification } from "antd";
20
+ import AggregationStatusBar from "./AggregationStatusBar";
21
+ import { notification, Button } from "antd";
22
+ import { CompressOutlined } from '@ant-design/icons';
21
23
  import Aggregations, { hashRanges } from "./Aggregations";
22
24
  import { useBulkCellEdit, BulkEditButton } from './BulkEdit';
23
25
  import {
@@ -53,13 +55,27 @@ const AgGrid = React.forwardRef((props, ref) => {
53
55
  newRowFlash = true,
54
56
  updatedRowFlash = false,
55
57
  onGridReady,
56
- enableNotification,
58
+ // Notification props
59
+ notificationMode = 'full', // 'full' | 'simple' | 'none'
57
60
  notificationOptions: {
58
61
  notificationHead = NotificationOptionsInit.head,
59
62
  notificationBody = NotificationOptionsInit.body,
60
63
  style = NotificationOptionsInit.style,
61
64
  placement = NotificationOptionsInit.placement,
62
65
  } = {},
66
+ // Status Bar props (pro simple mode)
67
+ statusBarMetrics = [
68
+ 'count',
69
+ 'sum',
70
+ 'min',
71
+ 'max',
72
+ 'avg',
73
+ 'median',
74
+ 'range',
75
+ 'geometry',
76
+ 'dateRange'
77
+ ],
78
+ statusBarHeight = 36,
63
79
  // Bulk Edit props
64
80
  enableBulkEdit = false,
65
81
  bulkEditOptions = {},
@@ -79,8 +95,15 @@ const AgGrid = React.forwardRef((props, ref) => {
79
95
  }, [theme]);
80
96
  const [, setIsGridReady] = React.useState(false);
81
97
  const [isSelecting, setIsSelecting] = React.useState(false);
98
+ const [aggregationData, setAggregationData] = React.useState(null); // Pro simple mode status bar
99
+ const [activeNotificationMode, setActiveNotificationMode] = React.useState(notificationMode); // Aktivní mód (může být dočasně přepsán uživatelem)
82
100
  const previousRowDataRef = React.useRef(rowData);
83
101
 
102
+ // Synchronizovat activeNotificationMode s notificationMode prop
103
+ React.useEffect(() => {
104
+ setActiveNotificationMode(notificationMode);
105
+ }, [notificationMode]);
106
+
84
107
  // Bulk Edit hook
85
108
  const {
86
109
  floatingButton,
@@ -141,19 +164,104 @@ const AgGrid = React.forwardRef((props, ref) => {
141
164
  const notificationLastCallRef = React.useRef(0);
142
165
  const lastRangeHashRef = React.useRef(null);
143
166
 
167
+ // ✅ Callback pro přepnutí z full na simple mód
168
+ const handleSwitchToSimple = React.useCallback(() => {
169
+ const gridId = props.gridName || props.id || 'default';
170
+ const key = `aggregation-grid-${gridId}`;
171
+
172
+ // Zavřít full notifikaci
173
+ notification.destroy(key);
174
+
175
+ // Znovu spočítat a zobrazit simple status bar
176
+ if (internalRef?.current?.api) {
177
+ const ranges = internalRef.current.api.getCellRanges();
178
+ if (ranges && ranges.length > 0 && ranges[0]?.startRow) {
179
+ const messageInfo = Aggregations(internalRef);
180
+ if (messageInfo.count > 1) {
181
+ setAggregationData(messageInfo);
182
+ }
183
+ }
184
+ }
185
+
186
+ setActiveNotificationMode('simple');
187
+ }, [props, internalRef]);
188
+
189
+ // ✅ Helper funkce pro vytvoření custom description s tlačítkem pro přepnutí na simple
190
+ const createNotificationDescription = React.useCallback((messageInfo, showSwitchButton = false) => {
191
+ const bodyContent = notificationBody(messageInfo);
192
+
193
+ if (!showSwitchButton) {
194
+ return bodyContent;
195
+ }
196
+
197
+ return (
198
+ <div>
199
+ {bodyContent}
200
+ <div style={{ marginTop: '12px', borderTop: '1px solid #f0f0f0', paddingTop: '8px' }}>
201
+ <Button
202
+ type="link"
203
+ size="small"
204
+ icon={<CompressOutlined />}
205
+ onClick={handleSwitchToSimple}
206
+ style={{ padding: 0 }}
207
+ >
208
+ Zobrazit kompaktní režim
209
+ </Button>
210
+ </div>
211
+ </div>
212
+ );
213
+ }, [notificationBody, handleSwitchToSimple]);
214
+
215
+ // ✅ Callback pro přepnutí z simple na full mód
216
+ const handleSwitchToFull = React.useCallback(() => {
217
+ if (!aggregationData) return;
218
+
219
+ const gridId = props.gridName || props.id || 'default';
220
+ const key = `aggregation-grid-${gridId}`;
221
+
222
+ // Zobrazit full notifikaci s aktuálními daty a tlačítkem pro přepnutí
223
+ notification.info({
224
+ key,
225
+ message: notificationHead(aggregationData),
226
+ description: createNotificationDescription(aggregationData, true),
227
+ duration: 0,
228
+ style,
229
+ placement,
230
+ });
231
+
232
+ // Skrýt simple status bar
233
+ setAggregationData(null);
234
+ setActiveNotificationMode('full');
235
+ }, [aggregationData, props, notificationHead, createNotificationDescription, style, placement]);
236
+
144
237
  // ✅ OPTIMALIZACE: Helper funkce pro aktualizaci aggregace notifikace s cache check
145
238
  // Volá se BĚHEM označování s 100ms throttle pro real-time feedback
146
239
  const updateAggregationNotification = React.useCallback((event) => {
240
+ // Pokud je notificationMode 'none', nedělat nic
241
+ if (notificationMode === 'none') {
242
+ return;
243
+ }
244
+
147
245
  // Lightweight cache check PŘED voláním Aggregations()
148
246
  const ranges = event.api.getCellRanges();
149
247
  const currentRangeHash = hashRanges(ranges);
150
248
 
151
- // Žádné ranges nebo jen jedna buňka - zavřít notifikaci
249
+ // Žádné ranges nebo jen jedna buňka - vyčistit vše
152
250
  if (!ranges || ranges.length === 0 || !ranges[0]?.startRow) {
153
- const gridId = props.gridName || props.id || 'default';
154
- const key = `aggregation-grid-${gridId}`;
155
251
  lastRangeHashRef.current = null;
156
- notification.destroy(key);
252
+
253
+ // V 'full' módu zavřít notifikaci
254
+ if (activeNotificationMode === 'full') {
255
+ const gridId = props.gridName || props.id || 'default';
256
+ const key = `aggregation-grid-${gridId}`;
257
+ notification.destroy(key);
258
+ }
259
+
260
+ // V 'simple' módu vyčistit aggregationData
261
+ if (activeNotificationMode === 'simple') {
262
+ setAggregationData(null);
263
+ }
264
+
157
265
  return;
158
266
  }
159
267
 
@@ -165,38 +273,53 @@ const AgGrid = React.forwardRef((props, ref) => {
165
273
  // Cache miss - spočítat aggregace
166
274
  const messageInfo = Aggregations(internalRef);
167
275
 
168
- const gridId = props.gridName || props.id || 'default';
169
- const key = `aggregation-grid-${gridId}`;
170
-
171
276
  if (messageInfo.count > 1) {
172
277
  // Uložit hash pro příští porovnání
173
278
  lastRangeHashRef.current = currentRangeHash;
174
279
 
280
+ // Zavolat onAggregationChanged callback pokud existuje
175
281
  if (props.onAggregationChanged) {
176
282
  props.onAggregationChanged(messageInfo);
177
283
  }
178
284
 
179
- // Dynamický style s pointer-events podle stavu označování
180
- const dynamicStyle = {
181
- ...style,
182
- pointerEvents: isSelecting ? 'none' : 'auto'
183
- };
285
+ // Podle aktivního módu zobrazit buď notifikaci nebo aktualizovat status bar
286
+ if (activeNotificationMode === 'full') {
287
+ // FULL mód - zobrazit plovoucí notifikaci s tlačítkem pro přepnutí na simple
288
+ const gridId = props.gridName || props.id || 'default';
289
+ const key = `aggregation-grid-${gridId}`;
184
290
 
185
- // Aktualizace existující notifikace nebo vytvoření nové
186
- notification.info({
187
- key,
188
- message: notificationHead(messageInfo),
189
- description: notificationBody(messageInfo),
190
- duration: 0,
191
- style: dynamicStyle,
192
- placement,
193
- });
291
+ const dynamicStyle = {
292
+ ...style,
293
+ pointerEvents: isSelecting ? 'none' : 'auto'
294
+ };
295
+
296
+ notification.info({
297
+ key,
298
+ message: notificationHead(messageInfo),
299
+ description: createNotificationDescription(messageInfo, true),
300
+ duration: 0,
301
+ style: dynamicStyle,
302
+ placement,
303
+ });
304
+ } else if (activeNotificationMode === 'simple') {
305
+ // SIMPLE mód - aktualizovat state pro status bar
306
+ setAggregationData(messageInfo);
307
+ }
194
308
  } else {
195
- // Jen jedna buňka - zavřít notifikaci
309
+ // Jen jedna buňka - zavřít/vyčistit vše
196
310
  lastRangeHashRef.current = null;
197
- notification.destroy(key);
311
+
312
+ if (activeNotificationMode === 'full') {
313
+ const gridId = props.gridName || props.id || 'default';
314
+ const key = `aggregation-grid-${gridId}`;
315
+ notification.destroy(key);
316
+ }
317
+
318
+ if (activeNotificationMode === 'simple') {
319
+ setAggregationData(null);
320
+ }
198
321
  }
199
- }, [internalRef, props, notificationHead, notificationBody, style, placement, isSelecting]);
322
+ }, [notificationMode, activeNotificationMode, internalRef, props, notificationHead, createNotificationDescription, style, placement, isSelecting]);
200
323
 
201
324
  // Detekce konce označování pomocí mouseup event
202
325
  React.useEffect(() => {
@@ -205,6 +328,16 @@ const AgGrid = React.forwardRef((props, ref) => {
205
328
  // takže tady jen ukončíme isSelecting stav
206
329
  setTimeout(() => {
207
330
  setIsSelecting(false);
331
+
332
+ // ✅ FIX: Zkontrolovat jestli jsou stále nějaké ranges, pokud ne - vyčistit status bar
333
+ if (internalRef?.current?.api && activeNotificationMode === 'simple') {
334
+ const ranges = internalRef.current.api.getCellRanges();
335
+ if (!ranges || ranges.length === 0 || !ranges[0]?.startRow) {
336
+ setAggregationData(null);
337
+ // Reset na původní notificationMode když jsou všechny buňky odznačeny
338
+ setActiveNotificationMode(notificationMode);
339
+ }
340
+ }
208
341
  }, 50);
209
342
  };
210
343
 
@@ -215,7 +348,7 @@ const AgGrid = React.forwardRef((props, ref) => {
215
348
  document.removeEventListener('mouseup', handleMouseUp);
216
349
  document.removeEventListener('touchend', handleMouseUp);
217
350
  };
218
- }, []);
351
+ }, [notificationMode, activeNotificationMode, internalRef]);
219
352
 
220
353
  // Helper funkce pro nastavení pointer-events na notifikace
221
354
  const setNotificationPointerEvents = React.useCallback((enable) => {
@@ -306,7 +439,7 @@ const AgGrid = React.forwardRef((props, ref) => {
306
439
  clearTimeout(notificationThrottleRef.current);
307
440
  }
308
441
 
309
- if (enableNotification) {
442
+ if (notificationMode === 'full') {
310
443
  const gridId = props.gridName || props.id || 'default';
311
444
  const key = `aggregation-grid-${gridId}`;
312
445
  notification.destroy(key);
@@ -317,7 +450,7 @@ const AgGrid = React.forwardRef((props, ref) => {
317
450
  delete window.agGridTransactionCallbacks[queryKey];
318
451
  }
319
452
  };
320
- }, [enableNotification, props.gridName, props.id, queryKey]);
453
+ }, [notificationMode, props.gridName, props.id, queryKey]);
321
454
 
322
455
  React.useEffect(() => {
323
456
  // VYPNUTO: Cell flash je globálně vypnutý
@@ -408,15 +541,16 @@ const AgGrid = React.forwardRef((props, ref) => {
408
541
  // Detekovat začátek označování - nastavit isSelecting na true
409
542
  setIsSelecting(true);
410
543
 
411
- // 1. ✅ OPTIMALIZACE: Zobrazení notifikace BĚHEM označování s THROTTLE (100ms)
412
- // Throttle = spustí okamžitě první event, pak každých 100ms během označování
413
- // Cache check v updateAggregationNotification zajistí, že se agregace nepočítají znovu pokud se range nezměnil
414
- if (enableNotification) {
544
+ // 1. ✅ OPTIMALIZACE: Zobrazení notifikace BĚHEM označování s THROTTLE
545
+ // Simple mode: 300ms throttle (méně častá aktualizace, lepší výkon)
546
+ // Full mode: 100ms throttle (rychlejší feedback, plovoucí notifikace je levná)
547
+ if (notificationMode !== 'none') {
548
+ const throttleInterval = notificationMode === 'simple' ? 300 : 100;
415
549
  const now = Date.now();
416
550
  const timeSinceLastCall = now - notificationLastCallRef.current;
417
551
 
418
- // První volání NEBO uplynulo 100ms od posledního volání
419
- if (timeSinceLastCall >= 100) {
552
+ // První volání NEBO uplynul throttle interval
553
+ if (timeSinceLastCall >= throttleInterval) {
420
554
  // Okamžité volání
421
555
  notificationLastCallRef.current = now;
422
556
  if (internalRef?.current) {
@@ -433,7 +567,7 @@ const AgGrid = React.forwardRef((props, ref) => {
433
567
  if (internalRef?.current) {
434
568
  updateAggregationNotification(event);
435
569
  }
436
- }, 100 - timeSinceLastCall);
570
+ }, throttleInterval - timeSinceLastCall);
437
571
  }
438
572
  }
439
573
 
@@ -447,7 +581,7 @@ const AgGrid = React.forwardRef((props, ref) => {
447
581
  // 3. Custom onRangeSelectionChanged callback - bez debounce
448
582
  if (props.onRangeSelectionChanged) props.onRangeSelectionChanged(event);
449
583
  },
450
- [enableNotification, enableBulkEdit, handleRangeChange, props.onRangeSelectionChanged, updateAggregationNotification]
584
+ [notificationMode, enableBulkEdit, handleRangeChange, props.onRangeSelectionChanged, updateAggregationNotification]
451
585
  );
452
586
 
453
587
  const AgGridOnGridReady = (event, options) => {
@@ -627,10 +761,13 @@ const AgGrid = React.forwardRef((props, ref) => {
627
761
  onRangeSelectionChanged: RhPlusRangeSelectionChanged,
628
762
  context: memoizedContext,
629
763
  components,
764
+ // Explicitně zajistit že enableRangeSelection je propagováno
765
+ enableRangeSelection: props.enableRangeSelection,
630
766
  }), [
631
767
  internalRef,
632
768
  themeObject,
633
769
  props.columnDefs,
770
+ props.enableRangeSelection,
634
771
  memoizedDefaultColDef,
635
772
  memoizedOnCellEditingStarted,
636
773
  memoizedOnCellDoubleClicked,
@@ -655,9 +792,47 @@ const AgGrid = React.forwardRef((props, ref) => {
655
792
  }
656
793
  }, [quickFilterText, isApiReady]);
657
794
 
795
+ // Status bar se zobrazuje pouze v simple mode
796
+ const showStatusBar = notificationMode === 'simple' && aggregationData;
797
+
798
+ // ✅ FIX: Rezervovat místo pro status bar pouze když má data
799
+ // Grid se dynamicky rozšíří/zmenší podle přítomnosti status baru
800
+ const shouldReserveSpace = showStatusBar;
801
+ const gridContainerStyle = shouldReserveSpace
802
+ ? { height: `calc(100% - ${statusBarHeight}px)`, width: '100%', position: 'relative' }
803
+ : { height: '100%', width: '100%', position: 'relative' };
804
+
658
805
  return (
659
- <>
660
- <AgGridReact {...allGridProps} />
806
+ <div style={{ height: '100%', width: '100%', position: 'relative' }}>
807
+ {/* AG Grid - fixní výška, nikdy se nemění */}
808
+ <div style={gridContainerStyle}>
809
+ <AgGridReact {...allGridProps} />
810
+ </div>
811
+
812
+ {/* Aggregation Status Bar - absolutně pozicovaný na spodku */}
813
+ {shouldReserveSpace && (
814
+ <div style={{
815
+ position: 'absolute',
816
+ bottom: 0,
817
+ left: 0,
818
+ right: 0,
819
+ height: `${statusBarHeight}px`,
820
+ opacity: showStatusBar ? 1 : 0,
821
+ visibility: showStatusBar ? 'visible' : 'hidden',
822
+ pointerEvents: showStatusBar ? 'auto' : 'none',
823
+ zIndex: 10,
824
+ // GPU acceleration pro plynulejší zobrazení
825
+ transform: 'translateZ(0)',
826
+ willChange: 'opacity'
827
+ }}>
828
+ <AggregationStatusBar
829
+ data={aggregationData || {}}
830
+ metrics={statusBarMetrics}
831
+ height={statusBarHeight}
832
+ onSwitchToFull={handleSwitchToFull}
833
+ />
834
+ </div>
835
+ )}
661
836
 
662
837
  {/* Bulk Edit Floating Button */}
663
838
  {enableBulkEdit && floatingButton.visible && (
@@ -675,7 +850,7 @@ const AgGrid = React.forwardRef((props, ref) => {
675
850
  onCancel={handleCancelEdit}
676
851
  />
677
852
  )}
678
- </>
853
+ </div>
679
854
  );
680
855
  });
681
856
 
package/package.json CHANGED
@@ -1,29 +1,29 @@
1
1
  {
2
2
  "name": "@bit.rhplus/ag-grid",
3
- "version": "0.0.63",
3
+ "version": "0.0.64",
4
4
  "homepage": "https://bit.cloud/remote-scope/ag-grid",
5
5
  "main": "dist/index.js",
6
6
  "componentId": {
7
7
  "scope": "remote-scope",
8
8
  "name": "ag-grid",
9
- "version": "0.0.63"
9
+ "version": "0.0.64"
10
10
  },
11
11
  "dependencies": {
12
- "linq": "^4.0.3",
13
12
  "moment": "^2.30.1",
13
+ "@ant-design/icons": "^5.4.0",
14
+ "antd": "^5.20.6",
14
15
  "lodash": "^4.17.21",
15
16
  "ag-grid-community": "^33.3.2",
16
17
  "ag-grid-react": "^33.3.2",
17
- "antd": "^5.20.6",
18
- "@ant-design/icons": "^5.4.0",
19
18
  "date-holidays": "^3.26.1",
20
19
  "dayjs": "^1.11.13",
21
20
  "styled-components": "^6.1.19",
22
21
  "lucide-react": "^0.503.0",
23
- "@bit.rhplus/ui.grid": "0.0.81",
22
+ "linq": "^4.0.3",
23
+ "@bit.rhplus/ui.grid": "0.0.82",
24
24
  "@bit.rhplus/linq": "0.0.8",
25
- "@bit.rhplus/ui2.module-dropdown-list": "0.1.57",
26
- "@bit.rhplus/data": "0.0.60"
25
+ "@bit.rhplus/ui2.module-dropdown-list": "0.1.58",
26
+ "@bit.rhplus/data": "0.0.61"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@teambit/react.react-env": "1.0.132"