@bit.rhplus/ui.grid-layout 0.0.58 → 0.0.60
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/dist/useGridLayout.d.ts +47 -10
- package/dist/useGridLayout.js +482 -892
- package/dist/useGridLayout.js.map +1 -1
- package/dist/useGridLayoutApi.js +1 -1
- package/dist/useGridLayoutApi.js.map +1 -1
- package/package.json +3 -3
- package/useGridLayout.js +536 -1084
- package/useGridLayoutApi.js +1 -1
- /package/dist/{preview-1769765664330.js → preview-1771153821287.js} +0 -0
package/useGridLayout.js
CHANGED
|
@@ -2,20 +2,28 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Hlavní hook pro správu grid layout - automatické ukládání a načítání rozvržení sloupců
|
|
4
4
|
* Kombinuje AG-Grid API s Grid Layout službou pro persistence uživatelských preferencí
|
|
5
|
+
*
|
|
6
|
+
* ARCHITEKTURA - ZERO RE-RENDER PATTERN:
|
|
7
|
+
* ─────────────────────────────────────
|
|
8
|
+
* Při drag/resize/auto-save se NEPOUŽÍVÁ žádný useState.
|
|
9
|
+
* Všechny "pozadí" operace (save, detekce změn) běží přes useRef.
|
|
10
|
+
* Re-rendery se spouštějí POUZE při explicitních akcích (init, reload, editor save).
|
|
11
|
+
*
|
|
12
|
+
* preTransformedColumnDefs se po inicializaci ZAMKNE (stabilní reference).
|
|
13
|
+
* Zámek se uvolní jen při: změně columnDefs od parenta, editor save, reload.
|
|
14
|
+
* Tím se zabrání tomu, aby AG-Grid dostal columnDefs s původním pořadím.
|
|
5
15
|
*/
|
|
6
16
|
|
|
7
17
|
import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
|
8
18
|
import { useGridLayoutApi } from './useGridLayoutApi';
|
|
9
19
|
import { debounce } from 'lodash';
|
|
10
|
-
|
|
11
|
-
//
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// HELPER FUNKCE
|
|
23
|
+
// ============================================================================
|
|
12
24
|
|
|
13
25
|
/**
|
|
14
|
-
*
|
|
15
|
-
* Porovnává klíčové vlastnosti sloupců pro detekci změn
|
|
16
|
-
* @param {Array} state1 - První column state
|
|
17
|
-
* @param {Array} state2 - Druhý column state
|
|
18
|
-
* @returns {boolean} - true pokud jsou identické, false pokud se liší
|
|
26
|
+
* Porovnání dvou column state pro detekci skutečných změn
|
|
19
27
|
*/
|
|
20
28
|
const isColumnStateEqual = (state1, state2) => {
|
|
21
29
|
if (!state1 || !state2) return false;
|
|
@@ -24,35 +32,49 @@ const isColumnStateEqual = (state1, state2) => {
|
|
|
24
32
|
for (let i = 0; i < state1.length; i++) {
|
|
25
33
|
const col1 = state1[i];
|
|
26
34
|
const col2 = state2[i];
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (col1.
|
|
30
|
-
if (col1.
|
|
31
|
-
if (col1.hide !== col2.hide) return false; // Viditelnost
|
|
32
|
-
if (col1.pinned !== col2.pinned) return false; // Pinning
|
|
35
|
+
if (col1.colId !== col2.colId) return false;
|
|
36
|
+
if (col1.width !== col2.width) return false;
|
|
37
|
+
if (col1.hide !== col2.hide) return false;
|
|
38
|
+
if (col1.pinned !== col2.pinned) return false;
|
|
33
39
|
}
|
|
34
|
-
|
|
35
40
|
return true;
|
|
36
41
|
};
|
|
37
42
|
|
|
38
43
|
/**
|
|
39
|
-
*
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
*
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
44
|
+
* Získá API objekt s metodou applyColumnState (AG Grid v31+ nebo starší)
|
|
45
|
+
*/
|
|
46
|
+
const getApplyApi = (gridApiRef, columnApiRef) => {
|
|
47
|
+
if (gridApiRef.current?.applyColumnState) return gridApiRef.current;
|
|
48
|
+
if (columnApiRef.current?.applyColumnState) return columnApiRef.current;
|
|
49
|
+
return null;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Získá aktuální column state z AG-Grid (AG Grid v31+ nebo starší)
|
|
54
|
+
*/
|
|
55
|
+
const getColumnState = (gridApiRef, columnApiRef) => {
|
|
56
|
+
try {
|
|
57
|
+
if (gridApiRef.current?.getColumnState) return gridApiRef.current.getColumnState();
|
|
58
|
+
if (columnApiRef.current?.getColumnState) return columnApiRef.current.getColumnState();
|
|
59
|
+
} catch (e) {
|
|
60
|
+
console.error('[GridLayout] Chyba při čtení column state:', e);
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Získá API objekt s metodou resetColumnState
|
|
55
67
|
*/
|
|
68
|
+
const getResetApi = (gridApiRef, columnApiRef) => {
|
|
69
|
+
if (gridApiRef.current?.resetColumnState) return gridApiRef.current;
|
|
70
|
+
if (columnApiRef.current?.resetColumnState) return columnApiRef.current;
|
|
71
|
+
return null;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// ============================================================================
|
|
75
|
+
// HLAVNÍ HOOK
|
|
76
|
+
// ============================================================================
|
|
77
|
+
|
|
56
78
|
export const useGridLayout = ({
|
|
57
79
|
userKey,
|
|
58
80
|
applicationName,
|
|
@@ -63,51 +85,62 @@ export const useGridLayout = ({
|
|
|
63
85
|
autoSaveDelay = 500,
|
|
64
86
|
columnDefs = [],
|
|
65
87
|
accessToken,
|
|
66
|
-
waitForSavedFields = false,
|
|
88
|
+
waitForSavedFields = false,
|
|
67
89
|
onLayoutLoaded,
|
|
68
90
|
onLayoutSaved,
|
|
69
91
|
onError,
|
|
70
92
|
}) => {
|
|
71
93
|
|
|
72
|
-
// Validace columnDefs
|
|
73
|
-
if (
|
|
74
|
-
columnDefs
|
|
75
|
-
columnDefs !== null &&
|
|
76
|
-
!Array.isArray(columnDefs)
|
|
77
|
-
) {
|
|
78
|
-
console.error(
|
|
79
|
-
'[GridLayout] columnDefs is not an array:',
|
|
80
|
-
typeof columnDefs,
|
|
81
|
-
columnDefs
|
|
82
|
-
);
|
|
94
|
+
// Validace columnDefs
|
|
95
|
+
if (columnDefs !== undefined && columnDefs !== null && !Array.isArray(columnDefs)) {
|
|
96
|
+
console.error('[GridLayout] columnDefs is not an array:', typeof columnDefs, columnDefs);
|
|
83
97
|
throw new Error('useGridLayout: columnDefs musí být array');
|
|
84
98
|
}
|
|
85
99
|
|
|
86
|
-
//
|
|
100
|
+
// ==========================================================================
|
|
101
|
+
// REFS - stabilní reference pro event handlery a save funkce
|
|
102
|
+
// ==========================================================================
|
|
87
103
|
const gridApiRef = useRef(null);
|
|
88
104
|
const columnApiRef = useRef(null);
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
105
|
+
const columnWidthRefsMap = useRef(new Map()); // fieldId -> aktuální šířka
|
|
106
|
+
const headerNameMapRef = useRef(new Map()); // fieldId -> aktuální headerName
|
|
107
|
+
const lastKnownColumnStateRef = useRef(null); // Poslední známý stav pro detekci změn
|
|
92
108
|
const isInitializedRef = useRef(false);
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
109
|
+
const enabledRef = useRef(enabled);
|
|
110
|
+
const isGridReadyRef = useRef(false);
|
|
111
|
+
const autoSaveRef = useRef(autoSave);
|
|
112
|
+
const stableColumnDefsRef = useRef(columnDefs);
|
|
113
|
+
const stableGridLayoutApiRef = useRef(null);
|
|
114
|
+
const stableOnLayoutSavedRef = useRef(onLayoutSaved);
|
|
115
|
+
const stableOnErrorRef = useRef(onError);
|
|
116
|
+
const stableOnLayoutLoadedRef = useRef(onLayoutLoaded);
|
|
117
|
+
|
|
118
|
+
// "Pozadí" refs - ŽÁDNÉ useState pro tyto hodnoty (zero re-render pattern)
|
|
119
|
+
const isSavingRef = useRef(false);
|
|
120
|
+
const hasUnsavedChangesRef = useRef(false);
|
|
121
|
+
const isApplyingLayoutRef = useRef(false);
|
|
122
|
+
|
|
123
|
+
// Zámek pro preTransformedColumnDefs (stabilní reference po inicializaci)
|
|
124
|
+
const lockedColumnDefsRef = useRef(null);
|
|
125
|
+
const lockedForColumnDefsRef = useRef(null);
|
|
126
|
+
const lockedForFrozenRecordsRef = useRef(null);
|
|
127
|
+
|
|
128
|
+
// ==========================================================================
|
|
129
|
+
// STATE - POUZE pro hodnoty kde re-render je žádoucí
|
|
130
|
+
// ==========================================================================
|
|
97
131
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
98
132
|
const [isGridReady, setIsGridReady] = useState(false);
|
|
99
133
|
const [isLoading, setIsLoading] = useState(false);
|
|
100
|
-
const [isSaving, setIsSaving] = useState(false);
|
|
101
|
-
const [isApplyingLayout, setIsApplyingLayout] = useState(false);
|
|
102
134
|
const [error, setError] = useState(null);
|
|
103
|
-
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
|
104
135
|
const [isColumnEditorOpen, setIsColumnEditorOpen] = useState(false);
|
|
105
|
-
const [
|
|
106
|
-
const [columnWidthsVersion, setColumnWidthsVersion] = useState(0); // Pro trigger preTransformedColumnDefs
|
|
136
|
+
const [columnWidthsVersion, setColumnWidthsVersion] = useState(0);
|
|
107
137
|
|
|
138
|
+
// Zamrazené savedFields records - aktualizují se POUZE při init, reload a editor save
|
|
139
|
+
const [frozenSavedRecords, setFrozenSavedRecords] = useState(null);
|
|
108
140
|
|
|
109
|
-
|
|
110
|
-
//
|
|
141
|
+
// ==========================================================================
|
|
142
|
+
// API HOOK & QUERY
|
|
143
|
+
// ==========================================================================
|
|
111
144
|
const gridLayoutApi = useGridLayoutApi({
|
|
112
145
|
userKey,
|
|
113
146
|
applicationName,
|
|
@@ -116,7 +149,6 @@ export const useGridLayout = ({
|
|
|
116
149
|
accessToken,
|
|
117
150
|
});
|
|
118
151
|
|
|
119
|
-
// Query pro načtení saved layoutu
|
|
120
152
|
const {
|
|
121
153
|
data: savedFields,
|
|
122
154
|
isLoading: isFieldsLoading,
|
|
@@ -125,11 +157,7 @@ export const useGridLayout = ({
|
|
|
125
157
|
} = gridLayoutApi.useUserFields(
|
|
126
158
|
(columnDefs || []).map((colDef, index) => ({
|
|
127
159
|
name: colDef.field || colDef.colId || `column_${index}`,
|
|
128
|
-
displayName:
|
|
129
|
-
colDef.headerName ||
|
|
130
|
-
colDef.field ||
|
|
131
|
-
colDef.colId ||
|
|
132
|
-
`Column ${index + 1}`,
|
|
160
|
+
displayName: colDef.headerName || colDef.field || colDef.colId || `Column ${index + 1}`,
|
|
133
161
|
dataType: colDef.type || 'string',
|
|
134
162
|
isVisible: !colDef.hide,
|
|
135
163
|
width: colDef.width || 100,
|
|
@@ -145,874 +173,425 @@ export const useGridLayout = ({
|
|
|
145
173
|
}
|
|
146
174
|
);
|
|
147
175
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
// Stabilní reference pro debouncedSave
|
|
162
|
-
const stableColumnDefsRef = useRef(columnDefs);
|
|
163
|
-
const stableGridLayoutApiRef = useRef(gridLayoutApi);
|
|
164
|
-
const stableOnLayoutSavedRef = useRef(onLayoutSaved);
|
|
165
|
-
const stableHandleErrorRef = useRef(handleError);
|
|
166
|
-
// Reference pro ukládání stavu sloupců bez state update v render cyklu
|
|
167
|
-
const stableCurrentColumnsRef = useRef(null);
|
|
176
|
+
// ==========================================================================
|
|
177
|
+
// SYNCHRONIZACE REFS
|
|
178
|
+
// ==========================================================================
|
|
179
|
+
useEffect(() => { enabledRef.current = enabled; }, [enabled]);
|
|
180
|
+
useEffect(() => { isGridReadyRef.current = isGridReady; }, [isGridReady]);
|
|
181
|
+
useEffect(() => { autoSaveRef.current = autoSave; }, [autoSave]);
|
|
182
|
+
useEffect(() => { isInitializedRef.current = isInitialized; }, [isInitialized]);
|
|
183
|
+
useEffect(() => { stableGridLayoutApiRef.current = gridLayoutApi; }, [gridLayoutApi]);
|
|
184
|
+
useEffect(() => {
|
|
185
|
+
stableOnLayoutSavedRef.current = onLayoutSaved;
|
|
186
|
+
stableOnErrorRef.current = onError;
|
|
187
|
+
stableOnLayoutLoadedRef.current = onLayoutLoaded;
|
|
188
|
+
}, [onLayoutSaved, onError, onLayoutLoaded]);
|
|
168
189
|
|
|
169
|
-
//
|
|
190
|
+
// Synchronizace columnDefs ref + zachování existujících šířek
|
|
170
191
|
useEffect(() => {
|
|
171
|
-
// Při změně columnDefs vyčistíme columnWidthRefsMap a přeneseme existující šířky
|
|
172
192
|
if (columnDefs !== stableColumnDefsRef.current) {
|
|
173
193
|
const newWidthMap = new Map();
|
|
174
|
-
|
|
175
|
-
// Zachováme šířky pro sloupce, které stále existují
|
|
176
194
|
if (Array.isArray(columnDefs)) {
|
|
177
195
|
columnDefs.forEach(colDef => {
|
|
178
196
|
const fieldId = colDef.field || colDef.colId;
|
|
179
197
|
if (fieldId) {
|
|
180
|
-
// Zkusíme najít existující šířku v ref map
|
|
181
198
|
const existingWidth = columnWidthRefsMap.current.get(fieldId);
|
|
182
199
|
if (existingWidth !== undefined) {
|
|
183
200
|
newWidthMap.set(fieldId, existingWidth);
|
|
184
201
|
} else if (colDef.width) {
|
|
185
|
-
// Použijeme šířku z nového columnDef
|
|
186
202
|
newWidthMap.set(fieldId, colDef.width);
|
|
187
203
|
}
|
|
188
204
|
}
|
|
189
205
|
});
|
|
190
206
|
}
|
|
191
|
-
|
|
192
207
|
columnWidthRefsMap.current = newWidthMap;
|
|
193
|
-
setColumnWidthsVersion(prev => prev + 1);
|
|
208
|
+
setColumnWidthsVersion(prev => prev + 1);
|
|
194
209
|
}
|
|
195
|
-
|
|
196
210
|
stableColumnDefsRef.current = columnDefs;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
211
|
+
}, [columnDefs]);
|
|
212
|
+
|
|
213
|
+
// ==========================================================================
|
|
214
|
+
// HEADER NAME MAPA - plní se z savedFields při načtení
|
|
215
|
+
// ==========================================================================
|
|
216
|
+
useEffect(() => {
|
|
217
|
+
if (savedFields?.records && Array.isArray(savedFields.records)) {
|
|
218
|
+
savedFields.records.forEach(field => {
|
|
219
|
+
if (field.fieldName && field.headerName) {
|
|
220
|
+
headerNameMapRef.current.set(field.fieldName, field.headerName);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}, [savedFields?.records]);
|
|
201
225
|
|
|
202
|
-
//
|
|
226
|
+
// ==========================================================================
|
|
227
|
+
// FROZEN SAVED RECORDS - aktualizují se pouze při prvním načtení
|
|
228
|
+
// ==========================================================================
|
|
203
229
|
useEffect(() => {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
230
|
+
if (!frozenSavedRecords && savedFields?.records?.length > 0) {
|
|
231
|
+
setFrozenSavedRecords(savedFields.records);
|
|
232
|
+
}
|
|
233
|
+
}, [savedFields?.records, frozenSavedRecords]);
|
|
234
|
+
|
|
235
|
+
// ==========================================================================
|
|
236
|
+
// ERROR HANDLER (pouze pro explicitní akce - ne auto-save)
|
|
237
|
+
// ==========================================================================
|
|
238
|
+
const handleError = useCallback((error, context = '') => {
|
|
239
|
+
setError(error);
|
|
240
|
+
if (stableOnErrorRef.current) {
|
|
241
|
+
stableOnErrorRef.current(error, context);
|
|
242
|
+
}
|
|
243
|
+
}, []);
|
|
244
|
+
|
|
245
|
+
// ==========================================================================
|
|
246
|
+
// SAVE CURRENT LAYOUT
|
|
247
|
+
// ZERO RE-RENDER: používá POUZE refs, žádné setState
|
|
248
|
+
// ==========================================================================
|
|
222
249
|
const saveCurrentLayout = useCallback(async () => {
|
|
223
|
-
|
|
224
|
-
if (!enabled || !gridApiRef.current || !isInitialized) {
|
|
250
|
+
if (!enabledRef.current || !gridApiRef.current || !isInitializedRef.current) {
|
|
225
251
|
return;
|
|
226
252
|
}
|
|
227
|
-
try {
|
|
228
|
-
setIsSaving(true);
|
|
229
|
-
setError(null);
|
|
230
|
-
|
|
231
|
-
// Získáme současný column state z AG-Grid s fallbackem pro AG Grid v31+
|
|
232
|
-
let columnState = null;
|
|
233
|
-
if (
|
|
234
|
-
gridApiRef.current &&
|
|
235
|
-
typeof gridApiRef.current.getColumnState === 'function'
|
|
236
|
-
) {
|
|
237
|
-
// AG Grid v31+ - getColumnState je přímo v main API
|
|
238
|
-
try {
|
|
239
|
-
columnState = gridApiRef.current.getColumnState();
|
|
240
|
-
|
|
241
|
-
// Získáme aktuální headerName hodnoty z DOM pro každý sloupec
|
|
242
|
-
if (columnState && Array.isArray(columnState)) {
|
|
243
|
-
columnState = columnState.map(colState => {
|
|
244
|
-
// Pokusíme se získat aktuální headerName z DOM
|
|
245
|
-
try {
|
|
246
|
-
const headerCell = document.querySelector(`[col-id="${colState.colId}"]`);
|
|
247
|
-
if (headerCell) {
|
|
248
|
-
const headerTextEl = headerCell.querySelector('.ag-header-cell-text');
|
|
249
|
-
if (headerTextEl && headerTextEl.textContent) {
|
|
250
|
-
return {
|
|
251
|
-
...colState,
|
|
252
|
-
headerName: headerTextEl.textContent
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
} catch (headerError) {
|
|
257
|
-
console.log(`❌ [GridLayout] Could not get headerName from DOM for ${colState.colId}`);
|
|
258
|
-
}
|
|
259
|
-
return colState;
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
253
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (columnState === undefined || columnState === null) {
|
|
266
|
-
console.warn(
|
|
267
|
-
'[GridLayout] getColumnState returned undefined/null, trying getColumnDefs() alternative'
|
|
268
|
-
);
|
|
269
|
-
console.warn(
|
|
270
|
-
'[GridLayout] Full gridApiRef.current object:',
|
|
271
|
-
gridApiRef.current
|
|
272
|
-
);
|
|
273
|
-
console.warn(
|
|
274
|
-
'[GridLayout] Available methods on gridApiRef.current:',
|
|
275
|
-
gridApiRef.current
|
|
276
|
-
? Object.getOwnPropertyNames(gridApiRef.current).filter(
|
|
277
|
-
(name) => typeof gridApiRef.current[name] === 'function'
|
|
278
|
-
)
|
|
279
|
-
: 'NO_GRID_API'
|
|
280
|
-
);
|
|
254
|
+
// Ochrana proti souběžným uložením
|
|
255
|
+
if (isSavingRef.current) return;
|
|
281
256
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const columnDefs = gridApiRef.current.getColumnDefs();
|
|
285
|
-
|
|
286
|
-
if (
|
|
287
|
-
columnDefs &&
|
|
288
|
-
Array.isArray(columnDefs) &&
|
|
289
|
-
columnDefs.length > 0
|
|
290
|
-
) {
|
|
291
|
-
// Vytvoříme column state z column defs
|
|
292
|
-
columnState = columnDefs.map((colDef, index) => {
|
|
293
|
-
// Zkusíme získat aktuální šířku sloupce z DOM
|
|
294
|
-
let currentWidth = colDef.width || 100;
|
|
295
|
-
try {
|
|
296
|
-
const fieldId = colDef.field || colDef.colId;
|
|
297
|
-
const headerElement = document.querySelector(
|
|
298
|
-
`[col-id="${fieldId}"]`
|
|
299
|
-
);
|
|
300
|
-
if (headerElement && headerElement.offsetWidth) {
|
|
301
|
-
currentWidth = headerElement.offsetWidth;
|
|
302
|
-
}
|
|
303
|
-
} catch (domError) {
|
|
304
|
-
console.log('❌ [GridLayout] Could not get width from DOM for',
|
|
305
|
-
colDef.field
|
|
306
|
-
);
|
|
307
|
-
}
|
|
257
|
+
try {
|
|
258
|
+
isSavingRef.current = true; // ref only → žádný re-render
|
|
308
259
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
headerName: colDef.headerName, // Přidáme aktuální headerName
|
|
314
|
-
sort: null, // Nebudeme zachovávat sort při této fallback metodě
|
|
315
|
-
sortIndex: null,
|
|
316
|
-
};
|
|
317
|
-
});
|
|
260
|
+
const columnState = getColumnState(gridApiRef, columnApiRef);
|
|
261
|
+
if (!columnState || !Array.isArray(columnState) || columnState.length === 0) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
318
264
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if (
|
|
325
|
-
cachedColumnDefs &&
|
|
326
|
-
Array.isArray(cachedColumnDefs) &&
|
|
327
|
-
cachedColumnDefs.length > 0
|
|
328
|
-
) {
|
|
329
|
-
columnState = cachedColumnDefs.map((colDef, index) => {
|
|
330
|
-
// Použijeme cached definice
|
|
331
|
-
let currentWidth = colDef.width || 100;
|
|
332
|
-
|
|
333
|
-
return {
|
|
334
|
-
colId: colDef.field || colDef.colId,
|
|
335
|
-
hide: colDef.hide || false,
|
|
336
|
-
width: currentWidth,
|
|
337
|
-
headerName: colDef.headerName, // Přidáme aktuální headerName
|
|
338
|
-
sort: null,
|
|
339
|
-
sortIndex: null,
|
|
340
|
-
};
|
|
341
|
-
});
|
|
342
|
-
} else {
|
|
343
|
-
console.error(
|
|
344
|
-
'[GridLayout] All fallback methods failed - no column data available'
|
|
345
|
-
);
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
} catch (columnDefError) {
|
|
350
|
-
console.error(
|
|
351
|
-
'[GridLayout] getColumnDefs() alternative failed:',
|
|
352
|
-
columnDefError
|
|
353
|
-
);
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
} catch (error) {
|
|
358
|
-
console.error(
|
|
359
|
-
'[GridLayout] Error calling gridApiRef.current.getColumnState():',
|
|
360
|
-
error
|
|
361
|
-
);
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
} else if (
|
|
365
|
-
columnApiRef.current &&
|
|
366
|
-
typeof columnApiRef.current.getColumnState === 'function'
|
|
367
|
-
) {
|
|
368
|
-
// Starší verze AG Grid - getColumnState je v columnApi
|
|
369
|
-
try {
|
|
370
|
-
columnState = columnApiRef.current.getColumnState();
|
|
371
|
-
|
|
372
|
-
// Získáme aktuální headerName hodnoty z DOM pro každý sloupec
|
|
373
|
-
if (columnState && Array.isArray(columnState)) {
|
|
374
|
-
columnState = columnState.map(colState => {
|
|
375
|
-
// Pokusíme se získat aktuální headerName z DOM
|
|
376
|
-
try {
|
|
377
|
-
const headerCell = document.querySelector(`[col-id="${colState.colId}"]`);
|
|
378
|
-
if (headerCell) {
|
|
379
|
-
const headerTextEl = headerCell.querySelector('.ag-header-cell-text');
|
|
380
|
-
if (headerTextEl && headerTextEl.textContent) {
|
|
381
|
-
return {
|
|
382
|
-
...colState,
|
|
383
|
-
headerName: headerTextEl.textContent
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
} catch (headerError) {
|
|
388
|
-
console.log(`❌ [GridLayout] Could not get headerName from DOM for ${colState.colId}`);
|
|
389
|
-
}
|
|
390
|
-
return colState;
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
} catch (error) {
|
|
395
|
-
console.error(
|
|
396
|
-
'❌ [GridLayout] Error calling columnApiRef.current.getColumnState():',
|
|
397
|
-
error
|
|
398
|
-
);
|
|
399
|
-
return;
|
|
265
|
+
// Obohatíme column state o headerName z ref mapy (BEZ čtení z DOM)
|
|
266
|
+
const enrichedColumnState = columnState.map(colState => {
|
|
267
|
+
const savedHeaderName = headerNameMapRef.current.get(colState.colId);
|
|
268
|
+
if (savedHeaderName) {
|
|
269
|
+
return { ...colState, headerName: savedHeaderName };
|
|
400
270
|
}
|
|
401
|
-
|
|
271
|
+
return colState;
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const columnDefsToUse = stableColumnDefsRef.current;
|
|
275
|
+
if (!columnDefsToUse || !Array.isArray(columnDefsToUse) || columnDefsToUse.length === 0) {
|
|
402
276
|
return;
|
|
403
277
|
}
|
|
404
278
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
console.warn(
|
|
413
|
-
'[GridLayout] stableColumnDefsRef is empty, using fallback from stableCurrentColumnsRef'
|
|
414
|
-
);
|
|
415
|
-
if (
|
|
416
|
-
stableCurrentColumnsRef.current &&
|
|
417
|
-
Array.isArray(stableCurrentColumnsRef.current)
|
|
418
|
-
) {
|
|
419
|
-
// Převedeme cached sloupce zpět na columnDefs format
|
|
420
|
-
columnDefsToUse = stableCurrentColumnsRef.current.map((col) => ({
|
|
421
|
-
field: col.field,
|
|
422
|
-
headerName: col.headerName || col.field,
|
|
423
|
-
width: col.width || 100,
|
|
424
|
-
hide: !col.visible,
|
|
425
|
-
}));
|
|
426
|
-
} else {
|
|
427
|
-
console.error(
|
|
428
|
-
'[GridLayout] No valid columnDefs available for saving'
|
|
429
|
-
);
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
279
|
+
const fields = stableGridLayoutApiRef.current.transformColumnStateToFields(
|
|
280
|
+
enrichedColumnState,
|
|
281
|
+
columnDefsToUse
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
if (!fields || fields.length === 0) {
|
|
285
|
+
return;
|
|
432
286
|
}
|
|
433
287
|
|
|
434
|
-
|
|
435
|
-
const fields =
|
|
436
|
-
stableGridLayoutApiRef.current.transformColumnStateToFields(
|
|
437
|
-
columnState,
|
|
438
|
-
columnDefsToUse
|
|
439
|
-
);
|
|
288
|
+
const result = await stableGridLayoutApiRef.current.saveGridLayout(fields);
|
|
440
289
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
.
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
if (stableOnLayoutSavedRef.current) {
|
|
448
|
-
stableOnLayoutSavedRef.current(fields, columnState);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
})
|
|
452
|
-
.catch((error) => {
|
|
453
|
-
stableHandleErrorRef.current(error, 'při ukládání layoutu');
|
|
454
|
-
});
|
|
290
|
+
if (result.success) {
|
|
291
|
+
hasUnsavedChangesRef.current = false; // ref only → žádný re-render
|
|
292
|
+
if (stableOnLayoutSavedRef.current) {
|
|
293
|
+
stableOnLayoutSavedRef.current(fields, enrichedColumnState);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
455
296
|
} catch (error) {
|
|
456
|
-
|
|
297
|
+
console.error('[GridLayout] Chyba při ukládání layoutu:', error);
|
|
298
|
+
// Při auto-save NENASTAVUJEME error state (žádný re-render)
|
|
299
|
+
// Error se loguje do konzole a volá se onError callback
|
|
300
|
+
if (stableOnErrorRef.current) {
|
|
301
|
+
stableOnErrorRef.current(error, 'při ukládání layoutu');
|
|
302
|
+
}
|
|
457
303
|
} finally {
|
|
458
|
-
|
|
304
|
+
isSavingRef.current = false; // ref only → žádný re-render
|
|
459
305
|
}
|
|
460
|
-
}, [
|
|
306
|
+
}, []); // PRÁZDNÉ DEPS → stabilní funkce
|
|
307
|
+
|
|
308
|
+
// ==========================================================================
|
|
309
|
+
// DEBOUNCED SAVE
|
|
310
|
+
// ==========================================================================
|
|
311
|
+
const saveCurrentLayoutRef = useRef(saveCurrentLayout);
|
|
312
|
+
useEffect(() => {
|
|
313
|
+
saveCurrentLayoutRef.current = saveCurrentLayout;
|
|
314
|
+
}, [saveCurrentLayout]);
|
|
461
315
|
|
|
462
|
-
/**
|
|
463
|
-
* Debounced auto-save pro předcházení častým voláním API
|
|
464
|
-
*/
|
|
465
316
|
const debouncedSave = useMemo(() => {
|
|
466
|
-
|
|
467
|
-
|
|
317
|
+
return debounce(() => {
|
|
318
|
+
saveCurrentLayoutRef.current();
|
|
468
319
|
}, autoSaveDelay);
|
|
469
|
-
|
|
470
|
-
}, [saveCurrentLayout, autoSaveDelay]);
|
|
320
|
+
}, [autoSaveDelay]);
|
|
471
321
|
|
|
472
|
-
// ✅ FIX #3: Stabilní ref pro debouncedSave (eliminuje regeneraci event handlerů)
|
|
473
322
|
const debouncedSaveRef = useRef(debouncedSave);
|
|
474
|
-
|
|
475
|
-
// Aktualizovat ref když se debouncedSave změní
|
|
476
323
|
useEffect(() => {
|
|
477
324
|
debouncedSaveRef.current = debouncedSave;
|
|
478
325
|
}, [debouncedSave]);
|
|
479
326
|
|
|
480
|
-
// ✅ FIX #3+: Stabilní refs pro enabled, isGridReady, autoSave (KRITICKÉ pro stabilitu handlerů)
|
|
481
|
-
const enabledRef = useRef(enabled);
|
|
482
|
-
const isGridReadyRef = useRef(isGridReady);
|
|
483
|
-
const autoSaveRef = useRef(autoSave);
|
|
484
|
-
|
|
485
|
-
// Aktualizovat refs při změnách
|
|
486
327
|
useEffect(() => {
|
|
487
|
-
|
|
488
|
-
}, [
|
|
328
|
+
return () => { debouncedSave.cancel(); };
|
|
329
|
+
}, [debouncedSave]);
|
|
489
330
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
331
|
+
// ==========================================================================
|
|
332
|
+
// APPLY SAVED LAYOUT (pouze při inicializaci - re-render je OK)
|
|
333
|
+
// ==========================================================================
|
|
334
|
+
const applySavedLayout = useCallback((forceApply = false) => {
|
|
335
|
+
if (!savedFields?.records || savedFields.records.length === 0 || (!forceApply && isInitialized)) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
493
338
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
) {
|
|
339
|
+
const applyApi = getApplyApi(gridApiRef, columnApiRef);
|
|
340
|
+
if (!applyApi) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
setIsLoading(true);
|
|
346
|
+
isApplyingLayoutRef.current = true;
|
|
347
|
+
|
|
348
|
+
const columnDefsToUse = stableColumnDefsRef.current || columnDefs;
|
|
349
|
+
const columnState = gridLayoutApi.transformFieldsToColumnState(
|
|
350
|
+
savedFields.records,
|
|
351
|
+
columnDefsToUse
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
if (!columnState || columnState.length === 0) {
|
|
510
355
|
return;
|
|
511
356
|
}
|
|
512
357
|
|
|
513
|
-
//
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
} else if (
|
|
521
|
-
columnApiRef.current &&
|
|
522
|
-
typeof columnApiRef.current.applyColumnState === 'function'
|
|
523
|
-
) {
|
|
524
|
-
applyColumnStateApi = columnApiRef.current;
|
|
358
|
+
// Inicializace width ref mapy s API šířkami
|
|
359
|
+
if (!isInitialized) {
|
|
360
|
+
columnState.forEach(colState => {
|
|
361
|
+
if (colState.colId && colState.width) {
|
|
362
|
+
columnWidthRefsMap.current.set(colState.colId, colState.width);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
525
365
|
}
|
|
526
366
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
367
|
+
// Sestavení headerName mapy z API dat
|
|
368
|
+
const headerNameMap = new Map();
|
|
369
|
+
savedFields.records.forEach(field => {
|
|
370
|
+
if (field.fieldName && field.headerName) {
|
|
371
|
+
headerNameMap.set(field.fieldName, field.headerName);
|
|
372
|
+
headerNameMapRef.current.set(field.fieldName, field.headerName);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
531
375
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
savedFields.records,
|
|
541
|
-
columnDefsToUse
|
|
542
|
-
);
|
|
376
|
+
// Adjusted column state (s ref šířkami pro re-apply)
|
|
377
|
+
const adjustedColumnState = columnState.map(colState => {
|
|
378
|
+
const refWidth = columnWidthRefsMap.current.get(colState.colId);
|
|
379
|
+
if (refWidth !== undefined && isInitialized) {
|
|
380
|
+
return { ...colState, width: refWidth };
|
|
381
|
+
}
|
|
382
|
+
return colState;
|
|
383
|
+
});
|
|
543
384
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
if (colState.colId && colState.width) {
|
|
549
|
-
columnWidthRefsMap.current.set(colState.colId, colState.width);
|
|
550
|
-
}
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
// Pokud je waitForSavedFields true, columnDefs jsou už pre-transformované,
|
|
555
|
-
// takže aplikujeme jen width a hide vlastnosti bez delay pro pořadí
|
|
556
|
-
const applyFunction = () => {
|
|
385
|
+
const applyFunction = () => {
|
|
386
|
+
try {
|
|
387
|
+
// KROK 1: Aktualizace headerName přes setColumnDefs PRVNÍ
|
|
388
|
+
if (headerNameMap.size > 0 && gridApiRef.current) {
|
|
557
389
|
try {
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
const refWidth = columnWidthRefsMap.current.get(colState.colId);
|
|
569
|
-
// Použijeme ref hodnotu POUZE pokud existuje A není to prvotní načítání
|
|
570
|
-
if (refWidth !== undefined && isInitialized) {
|
|
571
|
-
return {
|
|
572
|
-
...colState,
|
|
573
|
-
width: refWidth
|
|
574
|
-
};
|
|
575
|
-
}
|
|
576
|
-
// Při prvotním načítání nebo pokud nemáme ref hodnotu, zachováme API šířku
|
|
577
|
-
return colState;
|
|
578
|
-
});
|
|
579
|
-
result = applyColumnStateApi.applyColumnState({
|
|
580
|
-
state: adjustedColumnState,
|
|
581
|
-
applyOrder: applyOrderEnabled, // Pořadí jen když není waitForSavedFields
|
|
582
|
-
defaultState: {
|
|
583
|
-
sort: null, // Reset sorting na všech sloupcích
|
|
584
|
-
sortIndex: null, // Reset sort index
|
|
585
|
-
pivot: null, // Reset pivot
|
|
586
|
-
rowGroup: null, // Reset row grouping
|
|
587
|
-
},
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
// ✅ FIX: Aktualizovat lastKnownColumnStateRef po aplikování layoutu z API
|
|
591
|
-
// Tím zajistíme, že handleDragStopped má správnou referenci pro porovnání
|
|
592
|
-
lastKnownColumnStateRef.current = adjustedColumnState;
|
|
593
|
-
|
|
594
|
-
// Explicitně aktualizujeme headerName pro každý sloupec, protože AG-Grid
|
|
595
|
-
// nepodporuje nastavení headerName přes applyColumnState
|
|
596
|
-
if (
|
|
597
|
-
savedFields.records &&
|
|
598
|
-
Array.isArray(savedFields.records) &&
|
|
599
|
-
gridApiRef.current
|
|
600
|
-
) {
|
|
601
|
-
// Nejprve zkusíme použít refreshHeader funkci, pokud je dostupná
|
|
602
|
-
try {
|
|
603
|
-
if (
|
|
604
|
-
typeof gridApiRef.current.refreshHeader === 'function'
|
|
605
|
-
) {
|
|
606
|
-
gridApiRef.current.refreshHeader();
|
|
390
|
+
const currentColDefs = gridApiRef.current.getColumnDefs?.();
|
|
391
|
+
if (currentColDefs && Array.isArray(currentColDefs)) {
|
|
392
|
+
let hasHeaderChanges = false;
|
|
393
|
+
const updatedColDefs = currentColDefs.map(colDef => {
|
|
394
|
+
const fieldName = colDef.field;
|
|
395
|
+
if (fieldName && headerNameMap.has(fieldName)) {
|
|
396
|
+
const newName = headerNameMap.get(fieldName);
|
|
397
|
+
if (colDef.headerName !== newName) {
|
|
398
|
+
hasHeaderChanges = true;
|
|
399
|
+
return { ...colDef, headerName: newName };
|
|
607
400
|
}
|
|
608
|
-
} catch (refreshError) {
|
|
609
|
-
console.error(
|
|
610
|
-
'[GridLayout] Error in refreshHeader:',
|
|
611
|
-
refreshError
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
// Získáme aktuální definice sloupců z gridu
|
|
615
|
-
let currentColDefs;
|
|
616
|
-
try {
|
|
617
|
-
currentColDefs = gridApiRef.current.getColumnDefs
|
|
618
|
-
? gridApiRef.current.getColumnDefs()
|
|
619
|
-
: null;
|
|
620
|
-
} catch (error) {
|
|
621
|
-
console.error(
|
|
622
|
-
'[GridLayout] Error getting column definitions:',
|
|
623
|
-
error
|
|
624
|
-
);
|
|
625
|
-
currentColDefs = null;
|
|
626
401
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
const headerNameMap = new Map();
|
|
630
|
-
savedFields.records.forEach((field) => {
|
|
631
|
-
if (field.fieldName && field.headerName) {
|
|
632
|
-
headerNameMap.set(field.fieldName, field.headerName);
|
|
633
|
-
}
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
// Aktualizujeme headerName pro každý sloupec
|
|
637
|
-
const updatedColDefs = currentColDefs.map((colDef) => {
|
|
638
|
-
const fieldName = colDef.field;
|
|
639
|
-
if (fieldName && headerNameMap.has(fieldName)) {
|
|
640
|
-
const newHeaderName = headerNameMap.get(fieldName);
|
|
641
|
-
// Vytvoříme novou kopii definice sloupce s aktualizovaným headerName
|
|
642
|
-
return {
|
|
643
|
-
...colDef,
|
|
644
|
-
headerName: newHeaderName,
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
|
-
return colDef;
|
|
648
|
-
});
|
|
649
|
-
// Aplikujeme aktualizované definice sloupců zpět do gridu
|
|
650
|
-
try {
|
|
651
|
-
if (
|
|
652
|
-
typeof gridApiRef.current.setColumnDefs === 'function'
|
|
653
|
-
) {
|
|
654
|
-
gridApiRef.current.setColumnDefs(updatedColDefs);
|
|
402
|
+
return colDef;
|
|
403
|
+
});
|
|
655
404
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
try {
|
|
659
|
-
// DOM operace dokončeny, můžeme ukončit loading
|
|
660
|
-
setIsApplyingLayout(false);
|
|
661
|
-
} catch (checkError) {
|
|
662
|
-
console.error(
|
|
663
|
-
'[GridLayout] Error checking updated columns:',
|
|
664
|
-
checkError
|
|
665
|
-
);
|
|
666
|
-
// I při chybě ukončíme loading
|
|
667
|
-
setIsApplyingLayout(false);
|
|
668
|
-
}
|
|
669
|
-
}, 100);
|
|
670
|
-
} else {
|
|
671
|
-
// Alternativní řešení - přímá manipulace s DOM
|
|
672
|
-
|
|
673
|
-
// Počkáme, až se grid vyrenderuje
|
|
674
|
-
setTimeout(() => {
|
|
675
|
-
try {
|
|
676
|
-
// Vytvoříme mapu pro rychlou identifikaci
|
|
677
|
-
const headerUpdates = new Map();
|
|
678
|
-
updatedColDefs.forEach((colDef) => {
|
|
679
|
-
if (colDef.field && colDef.headerName) {
|
|
680
|
-
headerUpdates.set(
|
|
681
|
-
colDef.field,
|
|
682
|
-
colDef.headerName
|
|
683
|
-
);
|
|
684
|
-
}
|
|
685
|
-
});
|
|
686
|
-
|
|
687
|
-
// Najdeme všechny hlavičky sloupců pomocí DOM
|
|
688
|
-
const headerCells =
|
|
689
|
-
document.querySelectorAll('.ag-header-cell');
|
|
690
|
-
|
|
691
|
-
headerCells.forEach((headerCell) => {
|
|
692
|
-
try {
|
|
693
|
-
// Získáme ID sloupce z DOM atributů
|
|
694
|
-
const colId = headerCell.getAttribute('col-id');
|
|
695
|
-
if (colId && headerUpdates.has(colId)) {
|
|
696
|
-
// Najdeme element s textem hlavičky
|
|
697
|
-
const headerTextEl = headerCell.querySelector(
|
|
698
|
-
'.ag-header-cell-text'
|
|
699
|
-
);
|
|
700
|
-
if (headerTextEl) {
|
|
701
|
-
const newHeaderName =
|
|
702
|
-
headerUpdates.get(colId);
|
|
703
|
-
headerTextEl.textContent = newHeaderName;
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
} catch (cellError) {
|
|
707
|
-
console.error(
|
|
708
|
-
'[GridLayout] Error updating header cell:',
|
|
709
|
-
cellError
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
// DOM manipulace dokončena, ukončíme loading
|
|
715
|
-
setIsApplyingLayout(false);
|
|
716
|
-
} catch (domError) {
|
|
717
|
-
console.error(
|
|
718
|
-
'[GridLayout] Error in DOM manipulation:',
|
|
719
|
-
domError
|
|
720
|
-
);
|
|
721
|
-
// I při chybě ukončíme loading
|
|
722
|
-
setIsApplyingLayout(false);
|
|
723
|
-
}
|
|
724
|
-
}, 200);
|
|
725
|
-
}
|
|
726
|
-
} catch (setError) {
|
|
727
|
-
console.error(
|
|
728
|
-
'[GridLayout] Error applying column definitions:',
|
|
729
|
-
setError
|
|
730
|
-
);
|
|
731
|
-
}
|
|
732
|
-
}
|
|
405
|
+
if (hasHeaderChanges && typeof gridApiRef.current.setColumnDefs === 'function') {
|
|
406
|
+
gridApiRef.current.setColumnDefs(updatedColDefs);
|
|
733
407
|
}
|
|
734
|
-
} catch (applyError) {
|
|
735
|
-
throw applyError;
|
|
736
408
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
onLayoutLoaded(savedFields.records, columnState);
|
|
740
|
-
}
|
|
741
|
-
} catch (delayedError) {
|
|
742
|
-
handleError(delayedError, 'při delayed aplikování layoutu');
|
|
743
|
-
} finally {
|
|
744
|
-
// Bezpečnostní ukončení loading pokud se nedokončilo jinde
|
|
745
|
-
setTimeout(() => {
|
|
746
|
-
setIsApplyingLayout(false);
|
|
747
|
-
}, 300);
|
|
409
|
+
} catch (e) {
|
|
410
|
+
console.error('[GridLayout] Chyba při aktualizaci headerName:', e);
|
|
748
411
|
}
|
|
749
|
-
};
|
|
750
|
-
|
|
751
|
-
// Pro waitForSavedFields aplikujeme okamžitě (pořadí je už v columnDefs)
|
|
752
|
-
// Pro normální režim použijeme delay
|
|
753
|
-
if (waitForSavedFields) {
|
|
754
|
-
applyFunction();
|
|
755
|
-
} else {
|
|
756
|
-
setTimeout(applyFunction, 100); // 100ms delay jen pro normální režim
|
|
757
412
|
}
|
|
758
|
-
}
|
|
759
|
-
} catch (error) {
|
|
760
|
-
handleError(error, 'při aplikování layoutu');
|
|
761
|
-
} finally {
|
|
762
|
-
setIsInitialized(true);
|
|
763
|
-
setIsGridReady(true); // Obnovíme také isGridReady pro event handlery
|
|
764
|
-
setIsLoading(false);
|
|
765
|
-
// setIsApplyingLayout(false) se nastaví až po dokončení všech DOM operací
|
|
766
|
-
}
|
|
767
|
-
},
|
|
768
|
-
[
|
|
769
|
-
savedFields,
|
|
770
|
-
gridLayoutApi,
|
|
771
|
-
isInitialized,
|
|
772
|
-
onLayoutLoaded,
|
|
773
|
-
handleError,
|
|
774
|
-
columnDefs,
|
|
775
|
-
waitForSavedFields,
|
|
776
|
-
]
|
|
777
|
-
);
|
|
778
413
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
414
|
+
// KROK 2: Aplikovat column state (pořadí, šířky) PO setColumnDefs
|
|
415
|
+
const applyOrderEnabled = !waitForSavedFields;
|
|
416
|
+
applyApi.applyColumnState({
|
|
417
|
+
state: adjustedColumnState,
|
|
418
|
+
applyOrder: applyOrderEnabled,
|
|
419
|
+
defaultState: { sort: null, sortIndex: null, pivot: null, rowGroup: null },
|
|
420
|
+
});
|
|
783
421
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
switch (action.type) {
|
|
791
|
-
case 'dragStopped':
|
|
792
|
-
if (autoSaveRef.current && debouncedSaveRef.current) { // ✅ FIX #3+: Použít refs
|
|
793
|
-
debouncedSaveRef.current();
|
|
794
|
-
}
|
|
795
|
-
break;
|
|
422
|
+
lastKnownColumnStateRef.current = adjustedColumnState;
|
|
423
|
+
|
|
424
|
+
try { gridApiRef.current?.refreshHeader?.(); } catch (e) { /* ignorujeme */ }
|
|
425
|
+
|
|
426
|
+
if (stableOnLayoutLoadedRef.current) {
|
|
427
|
+
stableOnLayoutLoadedRef.current(savedFields.records, columnState);
|
|
796
428
|
}
|
|
797
|
-
})
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
429
|
+
} catch (error) {
|
|
430
|
+
console.error('[GridLayout] Chyba při aplikování layoutu:', error);
|
|
431
|
+
handleError(error, 'při aplikování layoutu');
|
|
432
|
+
} finally {
|
|
433
|
+
isApplyingLayoutRef.current = false;
|
|
434
|
+
}
|
|
435
|
+
};
|
|
802
436
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
437
|
+
if (waitForSavedFields) {
|
|
438
|
+
applyFunction();
|
|
439
|
+
} else {
|
|
440
|
+
setTimeout(applyFunction, 100);
|
|
441
|
+
}
|
|
442
|
+
} catch (error) {
|
|
443
|
+
console.error('[GridLayout] Chyba při přípravě layoutu:', error);
|
|
444
|
+
handleError(error, 'při aplikování layoutu');
|
|
445
|
+
isApplyingLayoutRef.current = false;
|
|
446
|
+
} finally {
|
|
447
|
+
setIsInitialized(true);
|
|
448
|
+
setIsGridReady(true);
|
|
449
|
+
setIsLoading(false);
|
|
809
450
|
}
|
|
451
|
+
}, [savedFields, gridLayoutApi, isInitialized, handleError, columnDefs, waitForSavedFields]);
|
|
810
452
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
453
|
+
// ==========================================================================
|
|
454
|
+
// AG-GRID EVENT HANDLERS
|
|
455
|
+
// ZERO RE-RENDER: všechny mají prázdné deps a používají POUZE refs
|
|
456
|
+
// ==========================================================================
|
|
457
|
+
|
|
458
|
+
const handleColumnMoved = useCallback(() => {
|
|
459
|
+
if (!enabledRef.current || !isGridReadyRef.current) return;
|
|
460
|
+
hasUnsavedChangesRef.current = true; // ref only → žádný re-render
|
|
461
|
+
}, []);
|
|
814
462
|
|
|
815
463
|
const handleDragStopped = useCallback(() => {
|
|
816
|
-
if (!enabledRef.current || !isGridReadyRef.current)
|
|
817
|
-
return;
|
|
818
|
-
}
|
|
464
|
+
if (!enabledRef.current || !isGridReadyRef.current) return;
|
|
819
465
|
|
|
820
|
-
//
|
|
821
|
-
// Pokud se nic nezměnilo, SKIP celý handler (eliminuje zbytečné re-rendery při range selection)
|
|
466
|
+
// Detekce skutečné změny
|
|
822
467
|
try {
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
if (isColumnStateEqual(lastKnownColumnStateRef.current, currentColumnState)) {
|
|
829
|
-
// Žádná změna - SKIP celý handler (nejčastější případ při range selection)
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
468
|
+
const currentColumnState = getColumnState(gridApiRef, columnApiRef);
|
|
469
|
+
if (currentColumnState && Array.isArray(currentColumnState)) {
|
|
470
|
+
if (isColumnStateEqual(lastKnownColumnStateRef.current, currentColumnState)) {
|
|
471
|
+
return; // Žádná změna → skip
|
|
472
|
+
}
|
|
832
473
|
|
|
833
|
-
|
|
834
|
-
lastKnownColumnStateRef.current = currentColumnState;
|
|
474
|
+
lastKnownColumnStateRef.current = currentColumnState;
|
|
835
475
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
});
|
|
842
|
-
// NEPOUŽÍVÁME setColumnWidthsVersion zde - způsobilo by reset pořadí/šířek!
|
|
843
|
-
}
|
|
476
|
+
currentColumnState.forEach(colState => {
|
|
477
|
+
if (colState.colId && colState.width) {
|
|
478
|
+
columnWidthRefsMap.current.set(colState.colId, colState.width);
|
|
479
|
+
}
|
|
480
|
+
});
|
|
844
481
|
}
|
|
845
482
|
} catch (error) {
|
|
846
|
-
console.error('[GridLayout]
|
|
483
|
+
console.error('[GridLayout] Chyba při detekci změn v handleDragStopped:', error);
|
|
847
484
|
}
|
|
848
485
|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
if (!isInitializedRef.current
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
486
|
+
hasUnsavedChangesRef.current = true; // ref only → žádný re-render
|
|
487
|
+
|
|
488
|
+
if (!isInitializedRef.current) {
|
|
489
|
+
const checkInterval = setInterval(() => {
|
|
490
|
+
if (isInitializedRef.current) {
|
|
491
|
+
clearInterval(checkInterval);
|
|
492
|
+
if (autoSaveRef.current && debouncedSaveRef.current) {
|
|
493
|
+
debouncedSaveRef.current();
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}, 100);
|
|
497
|
+
setTimeout(() => clearInterval(checkInterval), 5000);
|
|
856
498
|
return;
|
|
857
499
|
}
|
|
858
500
|
|
|
859
|
-
|
|
860
|
-
if (autoSaveRef.current && debouncedSaveRef.current) { // ✅ FIX #3+: Použít refs
|
|
501
|
+
if (autoSaveRef.current && debouncedSaveRef.current) {
|
|
861
502
|
debouncedSaveRef.current();
|
|
862
503
|
}
|
|
863
|
-
}, []);
|
|
504
|
+
}, []);
|
|
864
505
|
|
|
865
|
-
// Handler pro DOKONČENÍ resize - spouští auto-save
|
|
866
|
-
// DŮLEŽITÉ: Reaguje pouze na finished === true (po uvolnění myši)
|
|
867
506
|
const handleColumnResized = useCallback((event) => {
|
|
868
|
-
if (!enabledRef.current || !isGridReadyRef.current) return;
|
|
869
|
-
|
|
870
|
-
// Reagujeme POUZE na dokončení resize operace (po uvolnění myši)
|
|
871
|
-
// To zabrání poskakování sloupců během tažení
|
|
507
|
+
if (!enabledRef.current || !isGridReadyRef.current) return;
|
|
872
508
|
if (!event || event.finished !== true) return;
|
|
873
509
|
|
|
874
|
-
|
|
510
|
+
hasUnsavedChangesRef.current = true; // ref only → žádný re-render
|
|
875
511
|
|
|
876
|
-
// Uložit aktuální šířky sloupců do ref map pro persistenci
|
|
877
|
-
// DŮLEŽITÉ: NEMĚNÍME columnWidthsVersion - AG-Grid už má správnou šířku ve svém interním stavu
|
|
878
|
-
// Změna columnWidthsVersion by triggerovala přepočet preTransformedColumnDefs a reset šířek
|
|
879
512
|
try {
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
columnWidthRefsMap.current.set(colState.colId, colState.width);
|
|
888
|
-
}
|
|
889
|
-
});
|
|
890
|
-
// NEPOUŽÍVÁME setColumnWidthsVersion zde - způsobilo by reset šířek!
|
|
891
|
-
}
|
|
513
|
+
const currentColumnState = getColumnState(gridApiRef, columnApiRef);
|
|
514
|
+
if (currentColumnState && Array.isArray(currentColumnState)) {
|
|
515
|
+
currentColumnState.forEach(colState => {
|
|
516
|
+
if (colState.colId && colState.width) {
|
|
517
|
+
columnWidthRefsMap.current.set(colState.colId, colState.width);
|
|
518
|
+
}
|
|
519
|
+
});
|
|
892
520
|
}
|
|
893
521
|
} catch (error) {
|
|
894
|
-
console.error('[GridLayout]
|
|
522
|
+
console.error('[GridLayout] Chyba při aktualizaci šířek v handleColumnResized:', error);
|
|
895
523
|
}
|
|
896
524
|
|
|
897
|
-
|
|
898
|
-
// OCHRANA: Ukládáme pouze pokud už byl layout načten z API (isInitialized)
|
|
899
|
-
if (autoSaveRef.current && isInitializedRef.current && debouncedSaveRef.current) { // ✅ FIX #3+: Použít refs
|
|
525
|
+
if (autoSaveRef.current && isInitializedRef.current && debouncedSaveRef.current) {
|
|
900
526
|
debouncedSaveRef.current();
|
|
901
527
|
}
|
|
902
|
-
}, []);
|
|
528
|
+
}, []);
|
|
903
529
|
|
|
904
530
|
const handleColumnVisible = useCallback(() => {
|
|
905
|
-
if (!enabledRef.current || !isGridReadyRef.current) return;
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
if (autoSaveRef.current && isInitializedRef.current && debouncedSaveRef.current) { // ✅ FIX #3+: Použít refs
|
|
531
|
+
if (!enabledRef.current || !isGridReadyRef.current) return;
|
|
532
|
+
hasUnsavedChangesRef.current = true; // ref only → žádný re-render
|
|
533
|
+
if (autoSaveRef.current && isInitializedRef.current && debouncedSaveRef.current) {
|
|
909
534
|
debouncedSaveRef.current();
|
|
910
535
|
}
|
|
911
|
-
}, []);
|
|
536
|
+
}, []);
|
|
912
537
|
|
|
913
538
|
const handleColumnPinned = useCallback(() => {
|
|
914
|
-
if (!enabledRef.current || !isGridReadyRef.current) return;
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
if (autoSaveRef.current && isInitializedRef.current && debouncedSaveRef.current) { // ✅ FIX #3+: Použít refs
|
|
539
|
+
if (!enabledRef.current || !isGridReadyRef.current) return;
|
|
540
|
+
hasUnsavedChangesRef.current = true; // ref only → žádný re-render
|
|
541
|
+
if (autoSaveRef.current && isInitializedRef.current && debouncedSaveRef.current) {
|
|
918
542
|
debouncedSaveRef.current();
|
|
919
543
|
}
|
|
920
|
-
}, []);
|
|
921
|
-
|
|
922
|
-
/**
|
|
923
|
-
* AG-Grid Ready handler
|
|
924
|
-
*/
|
|
925
|
-
const handleGridReady = useCallback(
|
|
926
|
-
(params) => {
|
|
927
|
-
if (!enabledRef.current) { // ✅ FIX #3+: Použít ref místo přímé hodnoty
|
|
928
|
-
return;
|
|
929
|
-
}
|
|
544
|
+
}, []);
|
|
930
545
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
546
|
+
// ==========================================================================
|
|
547
|
+
// AG-GRID READY HANDLER
|
|
548
|
+
// ==========================================================================
|
|
549
|
+
const handleGridReady = useCallback((params) => {
|
|
550
|
+
if (!enabledRef.current) return;
|
|
551
|
+
gridApiRef.current = params.api;
|
|
552
|
+
columnApiRef.current = params.columnApi || params.api;
|
|
553
|
+
setIsGridReady(true);
|
|
554
|
+
}, []);
|
|
937
555
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
[] // ✅ FIX #3+: ŽÁDNÉ dependencies → handler se nikdy neregeneruje (savedFields, isInitialized, applySavedLayout se nepoužívají)
|
|
942
|
-
);
|
|
556
|
+
// ==========================================================================
|
|
557
|
+
// EFFECTS
|
|
558
|
+
// ==========================================================================
|
|
943
559
|
|
|
944
|
-
/**
|
|
945
|
-
* Effect pro aplikování layoutu při prvotním načtení savedFields
|
|
946
|
-
*/
|
|
947
560
|
useEffect(() => {
|
|
948
|
-
// Aplikujeme layout pokud:
|
|
949
|
-
// 1. Máme savedFields z API (i když prázdné - první zobrazení modulu)
|
|
950
|
-
// 2. Grid je ready (má API references)
|
|
951
|
-
// 3. Ještě jsme neinicializovali layout
|
|
952
561
|
if (savedFields?.records !== undefined && gridApiRef.current && !isInitialized) {
|
|
953
562
|
if (savedFields.records.length > 0) {
|
|
954
563
|
applySavedLayout();
|
|
955
564
|
} else {
|
|
956
|
-
// Pro prázdné savedFields jen nastavíme inicializaci
|
|
957
565
|
setIsInitialized(true);
|
|
958
566
|
setIsGridReady(true);
|
|
959
567
|
}
|
|
960
568
|
}
|
|
961
569
|
}, [savedFields?.records, isInitialized, applySavedLayout]);
|
|
962
570
|
|
|
963
|
-
|
|
964
|
-
/**
|
|
965
|
-
* Effect pro error handling
|
|
966
|
-
*/
|
|
967
571
|
useEffect(() => {
|
|
968
572
|
if (fieldsError) {
|
|
969
573
|
handleError(fieldsError, 'při načítání saved layoutu');
|
|
970
574
|
}
|
|
971
575
|
}, [fieldsError, handleError]);
|
|
972
576
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
useEffect(() => {
|
|
977
|
-
return () => {
|
|
978
|
-
debouncedSave.cancel();
|
|
979
|
-
};
|
|
980
|
-
}, [debouncedSave]);
|
|
577
|
+
// ==========================================================================
|
|
578
|
+
// MANUÁLNÍ AKCE (re-render je zde OK - explicitní uživatelská akce)
|
|
579
|
+
// ==========================================================================
|
|
981
580
|
|
|
982
|
-
/**
|
|
983
|
-
* Resetuje layout na default
|
|
984
|
-
*/
|
|
985
581
|
const resetToDefault = useCallback(async () => {
|
|
986
582
|
if (!enabled) return;
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
let resetColumnStateApi = null;
|
|
990
|
-
if (
|
|
991
|
-
gridApiRef.current &&
|
|
992
|
-
typeof gridApiRef.current.resetColumnState === 'function'
|
|
993
|
-
) {
|
|
994
|
-
resetColumnStateApi = gridApiRef.current;
|
|
995
|
-
} else if (
|
|
996
|
-
columnApiRef.current &&
|
|
997
|
-
typeof columnApiRef.current.resetColumnState === 'function'
|
|
998
|
-
) {
|
|
999
|
-
resetColumnStateApi = columnApiRef.current;
|
|
1000
|
-
} else {
|
|
1001
|
-
console.warn('[GridLayout] resetColumnState method not available');
|
|
1002
|
-
return;
|
|
1003
|
-
}
|
|
583
|
+
const resetApi = getResetApi(gridApiRef, columnApiRef);
|
|
584
|
+
if (!resetApi) return;
|
|
1004
585
|
|
|
1005
586
|
try {
|
|
1006
587
|
setIsLoading(true);
|
|
588
|
+
resetApi.resetColumnState();
|
|
589
|
+
headerNameMapRef.current.clear();
|
|
1007
590
|
|
|
1008
|
-
// Resetujeme AG-Grid na původní column definitions
|
|
1009
|
-
resetColumnStateApi.resetColumnState();
|
|
1010
|
-
|
|
1011
|
-
// Pokud je autoSave zapnuté, uložíme resetovaný stav
|
|
1012
591
|
if (autoSave) {
|
|
1013
592
|
await saveCurrentLayout();
|
|
1014
593
|
} else {
|
|
1015
|
-
|
|
594
|
+
hasUnsavedChangesRef.current = true;
|
|
1016
595
|
}
|
|
1017
596
|
} catch (error) {
|
|
1018
597
|
handleError(error, 'při resetování layoutu');
|
|
@@ -1021,36 +600,29 @@ export const useGridLayout = ({
|
|
|
1021
600
|
}
|
|
1022
601
|
}, [enabled, autoSave, saveCurrentLayout, handleError]);
|
|
1023
602
|
|
|
1024
|
-
/**
|
|
1025
|
-
* Manuální uložení
|
|
1026
|
-
*/
|
|
1027
603
|
const saveLayout = useCallback(async () => {
|
|
1028
604
|
await saveCurrentLayout();
|
|
1029
605
|
}, [saveCurrentLayout]);
|
|
1030
606
|
|
|
1031
|
-
/**
|
|
1032
|
-
* Manuální reload
|
|
1033
|
-
*/
|
|
1034
607
|
const reloadLayout = useCallback(async () => {
|
|
1035
608
|
try {
|
|
1036
|
-
|
|
1037
|
-
const
|
|
1038
|
-
|
|
609
|
+
const savedGridApi = gridApiRef.current;
|
|
610
|
+
const savedColumnApi = columnApiRef.current;
|
|
611
|
+
|
|
612
|
+
// Reset zámku - při reloadu chceme přepočítat preTransformedColumnDefs
|
|
613
|
+
lockedColumnDefsRef.current = null;
|
|
1039
614
|
|
|
1040
615
|
setIsInitialized(false);
|
|
1041
616
|
setIsGridReady(false);
|
|
1042
|
-
await refetchFields();
|
|
617
|
+
const freshData = await refetchFields();
|
|
1043
618
|
|
|
1044
|
-
|
|
1045
|
-
if (!
|
|
1046
|
-
gridApiRef.current = savedGridApiRef;
|
|
1047
|
-
}
|
|
619
|
+
if (!gridApiRef.current && savedGridApi) gridApiRef.current = savedGridApi;
|
|
620
|
+
if (!columnApiRef.current && savedColumnApi) columnApiRef.current = savedColumnApi;
|
|
1048
621
|
|
|
1049
|
-
if (
|
|
1050
|
-
|
|
622
|
+
if (freshData?.data?.records) {
|
|
623
|
+
setFrozenSavedRecords(freshData.data.records);
|
|
1051
624
|
}
|
|
1052
625
|
|
|
1053
|
-
// Po refetch dat obnovíme oba stavy (layout je už v DB)
|
|
1054
626
|
setIsInitialized(true);
|
|
1055
627
|
setIsGridReady(true);
|
|
1056
628
|
} catch (error) {
|
|
@@ -1058,45 +630,18 @@ export const useGridLayout = ({
|
|
|
1058
630
|
}
|
|
1059
631
|
}, [refetchFields, handleError]);
|
|
1060
632
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
633
|
+
// ==========================================================================
|
|
634
|
+
// COLUMN EDITOR
|
|
635
|
+
// ==========================================================================
|
|
636
|
+
|
|
1064
637
|
const getCurrentColumnsForEditor = useCallback(() => {
|
|
1065
|
-
// Zajistíme, že máme validní columnDefs
|
|
1066
638
|
const validColumnDefs = Array.isArray(columnDefs) ? columnDefs : [];
|
|
1067
639
|
|
|
1068
640
|
try {
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
// Pokusíme se získat column state s různými fallbacky pro AG Grid v31+
|
|
1072
|
-
if (
|
|
1073
|
-
gridApiRef.current &&
|
|
1074
|
-
typeof gridApiRef.current.getColumnState === 'function'
|
|
1075
|
-
) {
|
|
1076
|
-
// AG Grid v31+ - getColumnState je přímo v main API
|
|
1077
|
-
currentColumnState = gridApiRef.current.getColumnState();
|
|
1078
|
-
} else if (
|
|
1079
|
-
columnApiRef.current &&
|
|
1080
|
-
typeof columnApiRef.current.getColumnState === 'function'
|
|
1081
|
-
) {
|
|
1082
|
-
// Starší verze AG Grid - getColumnState je v columnApi
|
|
1083
|
-
currentColumnState = columnApiRef.current.getColumnState();
|
|
1084
|
-
} else {
|
|
1085
|
-
throw new Error(
|
|
1086
|
-
'getColumnState method is not available on gridApiRef or columnApiRef'
|
|
1087
|
-
);
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
if (
|
|
1091
|
-
!Array.isArray(currentColumnState) ||
|
|
1092
|
-
currentColumnState.length === 0
|
|
1093
|
-
) {
|
|
1094
|
-
// Fallback pokud getColumnState() nevrátí validní array
|
|
1095
|
-
if (lastKnownColumnState) {
|
|
1096
|
-
return lastKnownColumnState;
|
|
1097
|
-
}
|
|
641
|
+
const currentColumnState = getColumnState(gridApiRef, columnApiRef);
|
|
1098
642
|
|
|
1099
|
-
|
|
643
|
+
if (!Array.isArray(currentColumnState) || currentColumnState.length === 0) {
|
|
644
|
+
return validColumnDefs.map((col, index) => ({
|
|
1100
645
|
id: col.field,
|
|
1101
646
|
field: col.field,
|
|
1102
647
|
headerName: col.headerName || col.field,
|
|
@@ -1105,27 +650,24 @@ export const useGridLayout = ({
|
|
|
1105
650
|
originalWidth: col.width || 100,
|
|
1106
651
|
visible: !col.hide,
|
|
1107
652
|
order: index,
|
|
1108
|
-
originalOrder: index,
|
|
653
|
+
originalOrder: index,
|
|
1109
654
|
}));
|
|
1110
|
-
|
|
1111
|
-
// POZN: Neukládáme do state, aby nedocházelo k infinite loop
|
|
1112
|
-
return defaultColumns;
|
|
1113
655
|
}
|
|
1114
656
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
cd.field === columnStateItem.colId ||
|
|
1121
|
-
cd.colId === columnStateItem.colId
|
|
1122
|
-
) || {};
|
|
1123
|
-
// Pokusíme se najít saved hodnotu pro headerName
|
|
657
|
+
return currentColumnState.map((columnStateItem, index) => {
|
|
658
|
+
const colDef = validColumnDefs.find(
|
|
659
|
+
(cd) => cd.field === columnStateItem.colId || cd.colId === columnStateItem.colId
|
|
660
|
+
) || {};
|
|
661
|
+
|
|
1124
662
|
const savedField = savedFields?.records?.find(
|
|
1125
663
|
(sf) => sf.fieldName === columnStateItem.colId
|
|
1126
664
|
);
|
|
665
|
+
const headerName =
|
|
666
|
+
headerNameMapRef.current.get(columnStateItem.colId) ||
|
|
667
|
+
savedField?.headerName ||
|
|
668
|
+
colDef.headerName ||
|
|
669
|
+
columnStateItem.colId;
|
|
1127
670
|
|
|
1128
|
-
// Najdeme původní index z columnDefs
|
|
1129
671
|
const originalIndex = validColumnDefs.findIndex(
|
|
1130
672
|
(cd) => (cd.field || cd.colId) === columnStateItem.colId
|
|
1131
673
|
);
|
|
@@ -1133,284 +675,212 @@ export const useGridLayout = ({
|
|
|
1133
675
|
return {
|
|
1134
676
|
id: columnStateItem.colId,
|
|
1135
677
|
field: columnStateItem.colId,
|
|
1136
|
-
headerName
|
|
1137
|
-
savedField?.headerName ||
|
|
1138
|
-
colDef.headerName ||
|
|
1139
|
-
columnStateItem.colId,
|
|
1140
|
-
// Původní headerName z columnDefs (ne z uložených uživatelských preferencí)
|
|
678
|
+
headerName,
|
|
1141
679
|
originalHeaderName: colDef.headerName || columnStateItem.colId,
|
|
1142
680
|
width: columnStateItem.width || colDef.width || 100,
|
|
1143
681
|
originalWidth: colDef.width || 100,
|
|
1144
682
|
visible: !columnStateItem.hide,
|
|
1145
683
|
order: index,
|
|
1146
|
-
originalOrder: originalIndex !== -1 ? originalIndex : index,
|
|
684
|
+
originalOrder: originalIndex !== -1 ? originalIndex : index,
|
|
1147
685
|
};
|
|
1148
686
|
});
|
|
1149
|
-
|
|
1150
|
-
// POZN: Neukládáme do state, aby nedocházelo k infinite loop
|
|
1151
|
-
// Tato aktualizace proběhne v useEffect
|
|
1152
|
-
return result;
|
|
1153
687
|
} catch (error) {
|
|
1154
|
-
console.error('[GridLayout]
|
|
1155
|
-
|
|
1156
|
-
// Použijeme lastKnownColumnState, pokud existuje
|
|
1157
|
-
if (lastKnownColumnState) {
|
|
1158
|
-
return lastKnownColumnState;
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
// Poslední fallback na columnDefs
|
|
1162
|
-
const defaultColumns = validColumnDefs.map((col, index) => ({
|
|
688
|
+
console.error('[GridLayout] Chyba v getCurrentColumnsForEditor:', error);
|
|
689
|
+
return validColumnDefs.map((col, index) => ({
|
|
1163
690
|
id: col.field || col.colId,
|
|
1164
691
|
field: col.field || col.colId,
|
|
1165
692
|
headerName: col.headerName || col.field || col.colId,
|
|
1166
|
-
// Vždy používáme původní definici jako referenci pro porovnání
|
|
1167
693
|
originalHeaderName: col.headerName || col.field || col.colId,
|
|
1168
694
|
width: col.width || 100,
|
|
1169
695
|
originalWidth: col.width || 100,
|
|
1170
696
|
visible: !col.hide,
|
|
1171
697
|
order: index,
|
|
1172
|
-
originalOrder: index,
|
|
698
|
+
originalOrder: index,
|
|
1173
699
|
}));
|
|
1174
|
-
|
|
1175
|
-
// POZN: Neukládáme do state, aby nedocházelo k infinite loop
|
|
1176
|
-
return defaultColumns;
|
|
1177
700
|
}
|
|
1178
|
-
}, [columnDefs, savedFields
|
|
701
|
+
}, [columnDefs, savedFields]);
|
|
1179
702
|
|
|
1180
|
-
/**
|
|
1181
|
-
* Otevře Column Editor modal
|
|
1182
|
-
*/
|
|
1183
703
|
const openColumnEditor = useCallback(() => {
|
|
1184
|
-
// Před otevřením editoru si pouze zaznamenáme, že chceme aktualizovat stav sloupců
|
|
1185
|
-
// Samotná aktualizace proběhne v useEffect, ne během renderu
|
|
1186
704
|
setIsColumnEditorOpen(true);
|
|
1187
705
|
}, []);
|
|
1188
706
|
|
|
1189
|
-
/**
|
|
1190
|
-
* Zavře Column Editor modal
|
|
1191
|
-
*/
|
|
1192
707
|
const closeColumnEditor = useCallback(() => {
|
|
1193
708
|
setIsColumnEditorOpen(false);
|
|
1194
709
|
}, []);
|
|
1195
710
|
|
|
1196
|
-
/**
|
|
1197
|
-
* Uloží změny z Column Editoru
|
|
1198
|
-
* @param {Array} editedColumns - Editované sloupce z modalu
|
|
1199
|
-
*/
|
|
1200
711
|
const saveColumnEditorChanges = async (editedColumns) => {
|
|
1201
712
|
if (!enabled) return;
|
|
1202
713
|
|
|
1203
714
|
try {
|
|
1204
715
|
setIsLoading(true);
|
|
1205
|
-
|
|
716
|
+
isSavingRef.current = true;
|
|
1206
717
|
|
|
1207
|
-
|
|
1208
|
-
const validColumns = editedColumns.filter(
|
|
1209
|
-
(col) => col.field && col.field !== undefined
|
|
1210
|
-
);
|
|
718
|
+
const validColumns = editedColumns.filter(col => col.field && col.field !== undefined);
|
|
1211
719
|
|
|
1212
|
-
// Připravíme UserFields pro API podle C# SaveUserFieldModel struktury
|
|
1213
720
|
const completeUserFields = validColumns.map((col, index) => ({
|
|
1214
|
-
Id: 0,
|
|
721
|
+
Id: 0,
|
|
1215
722
|
UserKey: userKey,
|
|
1216
723
|
ApplicationName: applicationName,
|
|
1217
724
|
GridName: gridName,
|
|
1218
725
|
FilterName: filterName || null,
|
|
1219
726
|
FieldName: col.field,
|
|
1220
727
|
HeaderName: col.headerName || col.field,
|
|
1221
|
-
Order: index,
|
|
728
|
+
Order: index,
|
|
1222
729
|
Show: col.visible,
|
|
1223
|
-
Width: col.width != null ? col.width : null,
|
|
730
|
+
Width: col.width != null ? col.width : null,
|
|
1224
731
|
System: false,
|
|
1225
732
|
}));
|
|
1226
733
|
|
|
1227
|
-
//
|
|
1228
|
-
|
|
1229
|
-
.
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
freshFields.data.records.forEach((field) => {
|
|
1274
|
-
if (field.fieldName && field.headerName) {
|
|
1275
|
-
headerNameMap.set(field.fieldName, field.headerName);
|
|
1276
|
-
}
|
|
1277
|
-
});
|
|
1278
|
-
|
|
1279
|
-
// Aplikujeme nové headerName hodnoty přes DOM
|
|
1280
|
-
setTimeout(() => {
|
|
1281
|
-
try {
|
|
1282
|
-
const headerCells = document.querySelectorAll('.ag-header-cell');
|
|
1283
|
-
headerCells.forEach((headerCell) => {
|
|
1284
|
-
const colId = headerCell.getAttribute('col-id');
|
|
1285
|
-
if (colId && headerNameMap.has(colId)) {
|
|
1286
|
-
const headerTextEl = headerCell.querySelector('.ag-header-cell-text');
|
|
1287
|
-
if (headerTextEl) {
|
|
1288
|
-
headerTextEl.textContent = headerNameMap.get(colId);
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
});
|
|
1292
|
-
|
|
1293
|
-
// Vynutíme refresh hlavičky
|
|
1294
|
-
if (gridApiRef.current?.refreshHeader) {
|
|
1295
|
-
gridApiRef.current.refreshHeader();
|
|
1296
|
-
}
|
|
1297
|
-
} catch (headerError) {
|
|
1298
|
-
console.error('[GridLayout] Error updating headers:', headerError);
|
|
1299
|
-
}
|
|
1300
|
-
}, 50);
|
|
734
|
+
// Aktualizace headerName ref mapy
|
|
735
|
+
validColumns.forEach(col => {
|
|
736
|
+
if (col.field && col.headerName) {
|
|
737
|
+
headerNameMapRef.current.set(col.field, col.headerName);
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
const result = await gridLayoutApi.saveGridLayout(completeUserFields);
|
|
742
|
+
|
|
743
|
+
if (result.success) {
|
|
744
|
+
hasUnsavedChangesRef.current = false;
|
|
745
|
+
|
|
746
|
+
try {
|
|
747
|
+
const freshFields = await refetchFields();
|
|
748
|
+
|
|
749
|
+
// Reset zámku a aktualizace frozenSavedRecords → uvolní zámek v preTransformedColumnDefs
|
|
750
|
+
lockedColumnDefsRef.current = null;
|
|
751
|
+
if (freshFields?.data?.records) {
|
|
752
|
+
setFrozenSavedRecords(freshFields.data.records);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Aplikujeme nový layout na grid
|
|
756
|
+
setTimeout(() => {
|
|
757
|
+
if (gridApiRef.current && freshFields?.data?.records) {
|
|
758
|
+
const columnState = gridLayoutApi.transformFieldsToColumnState(
|
|
759
|
+
freshFields.data.records,
|
|
760
|
+
columnDefs
|
|
761
|
+
);
|
|
762
|
+
|
|
763
|
+
if (columnState && columnState.length > 0) {
|
|
764
|
+
const applyApi = getApplyApi(gridApiRef, columnApiRef);
|
|
765
|
+
if (applyApi) {
|
|
766
|
+
// KROK 1: Aktualizace headerName
|
|
767
|
+
try {
|
|
768
|
+
const currentColDefs = gridApiRef.current.getColumnDefs?.();
|
|
769
|
+
if (currentColDefs && Array.isArray(currentColDefs)) {
|
|
770
|
+
const updatedColDefs = currentColDefs.map(colDef => {
|
|
771
|
+
const fieldName = colDef.field;
|
|
772
|
+
const newName = headerNameMapRef.current.get(fieldName);
|
|
773
|
+
if (newName && colDef.headerName !== newName) {
|
|
774
|
+
return { ...colDef, headerName: newName };
|
|
775
|
+
}
|
|
776
|
+
return colDef;
|
|
777
|
+
});
|
|
778
|
+
if (typeof gridApiRef.current.setColumnDefs === 'function') {
|
|
779
|
+
gridApiRef.current.setColumnDefs(updatedColDefs);
|
|
1301
780
|
}
|
|
1302
781
|
}
|
|
782
|
+
} catch (e) {
|
|
783
|
+
console.error('[GridLayout] Chyba při aktualizaci headerName v editoru:', e);
|
|
1303
784
|
}
|
|
1304
|
-
}, 150);
|
|
1305
|
-
} catch (refreshError) {
|
|
1306
|
-
console.error('[GridLayout] Error in post-save refresh:', refreshError);
|
|
1307
|
-
// Fallback na standardní applySavedLayout
|
|
1308
|
-
setTimeout(() => {
|
|
1309
|
-
if (gridApiRef.current) {
|
|
1310
|
-
applySavedLayout(true);
|
|
1311
|
-
}
|
|
1312
|
-
}, 200);
|
|
1313
|
-
}
|
|
1314
|
-
});
|
|
1315
785
|
|
|
1316
|
-
|
|
1317
|
-
|
|
786
|
+
// KROK 2: Aplikovat column state
|
|
787
|
+
applyApi.applyColumnState({
|
|
788
|
+
state: columnState,
|
|
789
|
+
applyOrder: true,
|
|
790
|
+
defaultState: { sort: null, sortIndex: null, pivot: null, rowGroup: null },
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
try { gridApiRef.current?.refreshHeader?.(); } catch (e) { /* ignorujeme */ }
|
|
794
|
+
}
|
|
795
|
+
}
|
|
1318
796
|
}
|
|
1319
|
-
}
|
|
1320
|
-
})
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
797
|
+
}, 150);
|
|
798
|
+
} catch (refreshError) {
|
|
799
|
+
console.error('[GridLayout] Chyba při obnově po uložení z editoru:', refreshError);
|
|
800
|
+
setTimeout(() => {
|
|
801
|
+
if (gridApiRef.current) applySavedLayout(true);
|
|
802
|
+
}, 200);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
if (onLayoutSaved) {
|
|
806
|
+
onLayoutSaved(completeUserFields);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
1324
809
|
} catch (error) {
|
|
1325
810
|
handleError(error, 'při ukládání změn z Column Editoru');
|
|
1326
811
|
} finally {
|
|
1327
812
|
setIsLoading(false);
|
|
1328
|
-
|
|
813
|
+
isSavingRef.current = false;
|
|
1329
814
|
}
|
|
1330
815
|
};
|
|
1331
|
-
//, [enabled, gridLayoutApi, onLayoutSaved, handleError, userKey, applicationName, gridName, filterName, reloadLayout]);
|
|
1332
816
|
|
|
1333
|
-
//
|
|
817
|
+
// ==========================================================================
|
|
818
|
+
// PRE-TRANSFORMED COLUMN DEFS
|
|
819
|
+
// ZAMKNUTÍ PO INICIALIZACI → stabilní reference, žádné zbytečné přepočty
|
|
820
|
+
// ==========================================================================
|
|
1334
821
|
const shouldShowColumns = useMemo(() => {
|
|
1335
|
-
if (!waitForSavedFields) return true;
|
|
1336
|
-
|
|
1337
|
-
// Pokud je waitForSavedFields true, čekáme na dokončení načítání
|
|
822
|
+
if (!waitForSavedFields) return true;
|
|
1338
823
|
return !isFieldsLoading && !isLoading;
|
|
1339
824
|
}, [waitForSavedFields, isFieldsLoading, isLoading]);
|
|
1340
825
|
|
|
1341
|
-
// Pre-transformované columnDefs podle savedFields pro waitForSavedFields mode
|
|
1342
|
-
// DŮLEŽITÉ: Toto useMemo nyní obsahuje i AgGridColumns transformaci pro eliminaci circular dependency
|
|
1343
826
|
const preTransformedColumnDefs = useMemo(() => {
|
|
1344
|
-
//
|
|
1345
|
-
|
|
1346
|
-
|
|
827
|
+
// ── ZÁMEK: Po inicializaci vrátíme zamknutou referenci ──
|
|
828
|
+
// Zámek se uvolní jen při: změně columnDefs od parenta, frozenSavedRecords (editor/reload)
|
|
829
|
+
// Tím se zabrání tomu, aby AG-Grid dostal nové columnDefs při jakémkoliv re-renderu
|
|
830
|
+
if (isInitialized
|
|
831
|
+
&& lockedColumnDefsRef.current
|
|
832
|
+
&& lockedForColumnDefsRef.current === columnDefs
|
|
833
|
+
&& lockedForFrozenRecordsRef.current === frozenSavedRecords) {
|
|
834
|
+
return lockedColumnDefsRef.current;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// ── Normální výpočet ──
|
|
838
|
+
const columnDefsToUse = stableColumnDefsRef.current;
|
|
1347
839
|
let baseColumnDefs = columnDefsToUse;
|
|
1348
840
|
|
|
1349
|
-
// Pro waitForSavedFields
|
|
1350
|
-
if (
|
|
1351
|
-
waitForSavedFields &&
|
|
1352
|
-
savedFields?.records &&
|
|
1353
|
-
Array.isArray(columnDefsToUse)
|
|
1354
|
-
) {
|
|
1355
|
-
// Transformujeme columnDefs podle savedFields PŘED zobrazením gridu
|
|
841
|
+
// Pro waitForSavedFields: seřadíme columnDefs podle frozenSavedRecords
|
|
842
|
+
if (waitForSavedFields && frozenSavedRecords && Array.isArray(columnDefsToUse)) {
|
|
1356
843
|
const columnState = gridLayoutApi.transformFieldsToColumnState(
|
|
1357
|
-
|
|
844
|
+
frozenSavedRecords,
|
|
1358
845
|
columnDefsToUse
|
|
1359
846
|
);
|
|
1360
847
|
|
|
1361
848
|
if (columnState && columnState.length > 0) {
|
|
1362
|
-
// Vytvoříme mapu pro rychlé vyhledávání
|
|
1363
849
|
const columnStateMap = new Map();
|
|
1364
850
|
columnState.forEach((colState, index) => {
|
|
1365
851
|
columnStateMap.set(colState.colId, { ...colState, __order: index });
|
|
1366
852
|
});
|
|
1367
853
|
|
|
1368
|
-
// Vytvoříme mapu fieldName -> headerName z API dat
|
|
1369
854
|
const headerNameMap = new Map();
|
|
1370
|
-
|
|
855
|
+
frozenSavedRecords.forEach(field => {
|
|
1371
856
|
if (field.fieldName && field.headerName) {
|
|
1372
857
|
headerNameMap.set(field.fieldName, field.headerName);
|
|
1373
858
|
}
|
|
1374
859
|
});
|
|
1375
860
|
|
|
1376
|
-
// Seřadíme columnDefs podle pořadí z columnState a upravíme headerName podle savedFields
|
|
1377
861
|
baseColumnDefs = [...columnDefsToUse]
|
|
1378
862
|
.sort((a, b) => {
|
|
1379
863
|
const fieldA = a.field || a.colId;
|
|
1380
864
|
const fieldB = b.field || b.colId;
|
|
1381
|
-
const
|
|
1382
|
-
const
|
|
1383
|
-
|
|
1384
|
-
const aOrder = aState?.__order ?? 999;
|
|
1385
|
-
const bOrder = bState?.__order ?? 999;
|
|
1386
|
-
|
|
865
|
+
const aOrder = columnStateMap.get(fieldA)?.__order ?? 999;
|
|
866
|
+
const bOrder = columnStateMap.get(fieldB)?.__order ?? 999;
|
|
1387
867
|
return aOrder - bOrder;
|
|
1388
868
|
})
|
|
1389
|
-
.map(
|
|
869
|
+
.map(colDef => {
|
|
1390
870
|
const fieldId = colDef.field || colDef.colId;
|
|
1391
871
|
const columnStateItem = columnStateMap.get(fieldId);
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
...(headerNameMap.has(fieldId) && { headerName: headerNameMap.get(fieldId) }),
|
|
1399
|
-
// Aplikujeme hide hodnotu z columnState (z API show hodnoty)
|
|
1400
|
-
...(columnStateItem && { hide: columnStateItem.hide }),
|
|
1401
|
-
// Aplikujeme také width pro konzistentní zobrazení
|
|
1402
|
-
...(columnStateItem && columnStateItem.width && { width: columnStateItem.width }),
|
|
1403
|
-
};
|
|
1404
|
-
}
|
|
1405
|
-
return colDef;
|
|
872
|
+
return {
|
|
873
|
+
...colDef,
|
|
874
|
+
...(headerNameMap.has(fieldId) && { headerName: headerNameMap.get(fieldId) }),
|
|
875
|
+
...(columnStateItem && { hide: columnStateItem.hide }),
|
|
876
|
+
...(columnStateItem?.width && { width: columnStateItem.width }),
|
|
877
|
+
};
|
|
1406
878
|
});
|
|
1407
879
|
}
|
|
1408
880
|
}
|
|
1409
881
|
|
|
1410
|
-
//
|
|
1411
|
-
// Tím eliminujeme circular dependency pro všechny případy použití
|
|
882
|
+
// Konverze ColumnBuilder na array
|
|
1412
883
|
if (!Array.isArray(baseColumnDefs)) {
|
|
1413
|
-
// Pokud baseColumnDefs není array, mohlo by to být ColumnBuilder
|
|
1414
884
|
if (baseColumnDefs && typeof baseColumnDefs.build === 'function') {
|
|
1415
885
|
baseColumnDefs = baseColumnDefs.build();
|
|
1416
886
|
} else {
|
|
@@ -1418,63 +888,45 @@ export const useGridLayout = ({
|
|
|
1418
888
|
}
|
|
1419
889
|
}
|
|
1420
890
|
|
|
1421
|
-
//
|
|
1422
|
-
// Při prvotním načítání zachováváme API šířky (které jsou už v baseColumnDefs pro waitForSavedFields)
|
|
891
|
+
// Aplikace ref šířek (POUZE po inicializaci)
|
|
1423
892
|
const finalColumnDefs = baseColumnDefs.map(colDef => {
|
|
1424
893
|
const fieldId = colDef.field || colDef.colId;
|
|
1425
894
|
const refWidth = columnWidthRefsMap.current.get(fieldId);
|
|
1426
|
-
|
|
1427
|
-
// Použijeme ref šířku POUZE pokud už je grid inicializovaný (uživatel manipuloval s šířkami)
|
|
1428
895
|
if (refWidth !== undefined && isInitialized) {
|
|
1429
|
-
return {
|
|
1430
|
-
...colDef,
|
|
1431
|
-
width: refWidth
|
|
1432
|
-
};
|
|
896
|
+
return { ...colDef, width: refWidth };
|
|
1433
897
|
}
|
|
1434
898
|
return colDef;
|
|
1435
899
|
});
|
|
1436
900
|
|
|
901
|
+
// ── Zamknout po inicializaci ──
|
|
902
|
+
if (isInitialized) {
|
|
903
|
+
lockedColumnDefsRef.current = finalColumnDefs;
|
|
904
|
+
lockedForColumnDefsRef.current = columnDefs;
|
|
905
|
+
lockedForFrozenRecordsRef.current = frozenSavedRecords;
|
|
906
|
+
}
|
|
907
|
+
|
|
1437
908
|
return finalColumnDefs;
|
|
909
|
+
}, [waitForSavedFields, frozenSavedRecords, columnDefs, gridLayoutApi, columnWidthsVersion, isInitialized]);
|
|
1438
910
|
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
// // Aplikujeme tooltip logiku pokud je potřeba
|
|
1445
|
-
// if (processedColumn.contentTooltip) {
|
|
1446
|
-
// // Statické mapování tooltipů pro lepší performance a eliminaci circular dependency
|
|
1447
|
-
// switch (processedColumn.contentTooltip) {
|
|
1448
|
-
// case 'User':
|
|
1449
|
-
// processedColumn.tooltipComponent = Tooltip.User;
|
|
1450
|
-
// break;
|
|
1451
|
-
// default:
|
|
1452
|
-
// console.warn(`[GridLayout] Unknown tooltip component: ${processedColumn.contentTooltip}`);
|
|
1453
|
-
// }
|
|
1454
|
-
// }
|
|
1455
|
-
|
|
1456
|
-
// return processedColumn;
|
|
1457
|
-
// }) || [];
|
|
1458
|
-
|
|
1459
|
-
// return processedColumns;
|
|
1460
|
-
}, [waitForSavedFields, savedFields?.records, columnDefs, gridLayoutApi, columnWidthsVersion, isInitialized]);
|
|
1461
|
-
|
|
1462
|
-
// ✅ FIX #10: NEPOUŽÍVAT memoizaci - spoléháme se na refs v Grid komponentě
|
|
1463
|
-
// Memoizace s dependencies způsobuje rerender při změně JAKÉKOLIV hodnoty (např. hasUnsavedChanges)
|
|
1464
|
-
// Refs v Grid komponentě zajišťují, že i když se tento objekt změní, Grid nevidí změnu
|
|
911
|
+
// ==========================================================================
|
|
912
|
+
// RETURN - API interface
|
|
913
|
+
// Pozadí hodnoty se čtou z refs (aktuální hodnota v okamžiku renderu)
|
|
914
|
+
// ==========================================================================
|
|
1465
915
|
return {
|
|
1466
|
-
// State
|
|
916
|
+
// State (re-render pouze při explicitních akcích)
|
|
1467
917
|
isLoading: isLoading || isFieldsLoading,
|
|
1468
|
-
isSaving,
|
|
1469
|
-
isApplyingLayout,
|
|
1470
918
|
error,
|
|
1471
|
-
hasUnsavedChanges,
|
|
1472
919
|
isInitialized,
|
|
1473
920
|
isGridReady,
|
|
1474
921
|
shouldShowColumns,
|
|
1475
922
|
preTransformedColumnDefs,
|
|
1476
923
|
|
|
1477
|
-
//
|
|
924
|
+
// Refs čtené při renderu (ŽÁDNÝ re-render při změně)
|
|
925
|
+
isSaving: isSavingRef.current,
|
|
926
|
+
hasUnsavedChanges: hasUnsavedChangesRef.current,
|
|
927
|
+
isApplyingLayout: isApplyingLayoutRef.current,
|
|
928
|
+
|
|
929
|
+
// AG-Grid event handlers (stabilní - prázdné deps)
|
|
1478
930
|
onGridReady: handleGridReady,
|
|
1479
931
|
onColumnMoved: handleColumnMoved,
|
|
1480
932
|
onDragStopped: handleDragStopped,
|
|
@@ -1482,7 +934,7 @@ export const useGridLayout = ({
|
|
|
1482
934
|
onColumnVisible: handleColumnVisible,
|
|
1483
935
|
onColumnPinned: handleColumnPinned,
|
|
1484
936
|
|
|
1485
|
-
//
|
|
937
|
+
// Manuální akce
|
|
1486
938
|
saveLayout,
|
|
1487
939
|
resetToDefault,
|
|
1488
940
|
reloadLayout,
|