@topconsultnpm/sdkui-react-beta 6.13.34 → 6.13.36
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/lib/components/base/TMPanelManager.d.ts +9 -0
- package/lib/components/base/TMPanelManager.js +465 -0
- package/lib/components/base/TMPanelManagerToolbar.d.ts +16 -0
- package/lib/components/base/TMPanelManagerToolbar.js +100 -0
- package/lib/components/base/TMPanelManagerUtils.d.ts +36 -0
- package/lib/components/base/TMPanelManagerUtils.js +27 -0
- package/lib/components/index.d.ts +3 -2
- package/lib/components/index.js +3 -2
- package/package.json +1 -1
- package/lib/components/base/TMPanelManagerMatrix.d.ts +0 -9
- package/lib/components/base/TMPanelManagerMatrix.js +0 -262
- package/lib/components/base/TMPanelManagerMatrixUtils.d.ts +0 -69
- package/lib/components/base/TMPanelManagerMatrixUtils.js +0 -155
@@ -0,0 +1,9 @@
|
|
1
|
+
import { TMPanelItemConfig } from './TMPanelManagerUtils';
|
2
|
+
type TMPanelManagerProps = {
|
3
|
+
panels: Array<TMPanelItemConfig>;
|
4
|
+
initialMobilePanelId: string;
|
5
|
+
showToolbar?: boolean;
|
6
|
+
gutters?: number;
|
7
|
+
};
|
8
|
+
declare const TMPanelManager: (props: TMPanelManagerProps) => import("react/jsx-runtime").JSX.Element;
|
9
|
+
export default TMPanelManager;
|
@@ -0,0 +1,465 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
3
|
+
import { flattenPanels, isAtLeastOnePanelVisible, isPanelArray } from './TMPanelManagerUtils';
|
4
|
+
import { SDKUI_Globals, IconInfo, SDKUI_Localizator } from '../../helper';
|
5
|
+
import { useDeviceType, DeviceType } from './TMDeviceProvider';
|
6
|
+
import TMPanel from './TMPanel';
|
7
|
+
import TMPanelManagerToolbar from './TMPanelManagerToolbar';
|
8
|
+
const TMPanelManager = (props) => {
|
9
|
+
const { panels, initialMobilePanelId, showToolbar = true, gutters = SDKUI_Globals.userSettings.themeSettings.gutters } = props;
|
10
|
+
const deviceType = useDeviceType();
|
11
|
+
let isMobile = useMemo(() => { return deviceType === DeviceType.MOBILE; }, [deviceType]);
|
12
|
+
const allPanels = useMemo(() => flattenPanels(panels), [panels]);
|
13
|
+
const panelMap = useMemo(() => {
|
14
|
+
const map = new Map();
|
15
|
+
allPanels.forEach(p => map.set(p.fullId, p));
|
16
|
+
return map;
|
17
|
+
}, [allPanels]);
|
18
|
+
const panelRefs = useRef({});
|
19
|
+
// Active buttons state & panel map
|
20
|
+
const [activeButtons, setActiveButtons] = useState(() => Object.fromEntries(allPanels.map(p => [p.fullId, p.config.toolbarOptions?.isActive ?? false])));
|
21
|
+
const [originalSizes, setOriginalSizes] = useState(new Map());
|
22
|
+
const [panelsState, setPanelsState] = useState(new Map());
|
23
|
+
const [maximizedPanelPath, setMaximizedPanelPath] = useState([]);
|
24
|
+
const [isRowDragging, setIsRowDragging] = useState(null);
|
25
|
+
const [isColumnDragging, setIsColumnDragging] = useState(null);
|
26
|
+
const [updatedDisabledButton, setUpdatedDisabledButton] = useState(undefined);
|
27
|
+
const [updatedVisibleButton, setUpdatedVisibleButton] = useState(undefined);
|
28
|
+
// Initialize two new Maps to store panel state and their original sizes
|
29
|
+
useEffect(() => {
|
30
|
+
const newState = new Map();
|
31
|
+
const newOriginalSizes = new Map();
|
32
|
+
// Recursive function to initialize panel map data
|
33
|
+
const initPanelsMap = (panels, parentPath = '') => {
|
34
|
+
panels.forEach(panel => {
|
35
|
+
// Skip if the panel has no contentOptions
|
36
|
+
if (!panel.contentOptions)
|
37
|
+
return;
|
38
|
+
// Construct a unique full ID for each panel based on its parent path
|
39
|
+
const fullId = parentPath ? `${parentPath}.${panel.id}` : panel.id;
|
40
|
+
// Use default values '0' if width/height is not provided
|
41
|
+
const width = panel.contentOptions.width;
|
42
|
+
const height = panel.contentOptions.height;
|
43
|
+
// Check if the panel contains nested children
|
44
|
+
const hasChildren = Array.isArray(panel.contentOptions.content);
|
45
|
+
// Store the original width and height for future reference
|
46
|
+
newOriginalSizes.set(fullId, { width, height });
|
47
|
+
// Determine the layout orientation
|
48
|
+
// A panel is considered a "column" layout if height is 100% and width isn't.
|
49
|
+
// A panel is considered a "row" layout if width is 100% and height isn't.
|
50
|
+
const isColumn = height === '100%' && width !== '100%';
|
51
|
+
const isRow = width === '100%' && height !== '100%';
|
52
|
+
// Save the current panel's state including dimensions and layout direction
|
53
|
+
newState.set(fullId, { width, height, isColumn, isRow, });
|
54
|
+
// If the panel has nested children, recursively initialize their states
|
55
|
+
if (hasChildren) {
|
56
|
+
initPanelsMap(panel.contentOptions.content, fullId);
|
57
|
+
}
|
58
|
+
});
|
59
|
+
};
|
60
|
+
initPanelsMap(panels);
|
61
|
+
setOriginalSizes(newOriginalSizes);
|
62
|
+
setPanelsState(newState);
|
63
|
+
}, [panels]);
|
64
|
+
useEffect(() => {
|
65
|
+
// If there are no original sizes available, exit early
|
66
|
+
if (!originalSizes.size)
|
67
|
+
return;
|
68
|
+
// Clone the current state of the panels into a new Map to modify it safely
|
69
|
+
const newState = new Map(panelsState);
|
70
|
+
// Helper function to check if a panel is currently maximized
|
71
|
+
const isMaximized = (panelId) => maximizedPanelPath.includes(panelId);
|
72
|
+
// Iterate through all panel sizes stored in originalSizes
|
73
|
+
for (const [panelId, sizes] of originalSizes.entries()) {
|
74
|
+
const currentPanel = newState.get(panelId);
|
75
|
+
if (!currentPanel) {
|
76
|
+
// If for some reason the panel doesn't exist in state, skip it
|
77
|
+
continue;
|
78
|
+
}
|
79
|
+
if (isMaximized(panelId)) {
|
80
|
+
// If the panel is maximized, set its dimensions to take up the full available space
|
81
|
+
newState.set(panelId, { ...currentPanel, width: '100%', height: '100%' });
|
82
|
+
}
|
83
|
+
else if (maximizedPanelPath.length > 0) {
|
84
|
+
// If at least one panel is maximized, collapse all others to zero size
|
85
|
+
newState.set(panelId, { ...currentPanel, width: '0', height: '0' });
|
86
|
+
}
|
87
|
+
else {
|
88
|
+
// If no panels are maximized, restore their original dimensions
|
89
|
+
newState.set(panelId, { ...currentPanel, width: sizes.width, height: sizes.height });
|
90
|
+
}
|
91
|
+
}
|
92
|
+
// Apply the updated panel states
|
93
|
+
setPanelsState(newState);
|
94
|
+
// Run this effect whenever maximizedPanelPath or originalSizes change
|
95
|
+
}, [maximizedPanelPath, originalSizes]);
|
96
|
+
useEffect(() => {
|
97
|
+
const isDragging = isColumnDragging || isRowDragging;
|
98
|
+
// Handles the end of dragging (mouse release)
|
99
|
+
const onMouseUp = () => {
|
100
|
+
setIsColumnDragging(null);
|
101
|
+
setIsRowDragging(null);
|
102
|
+
document.body.style.userSelect = '';
|
103
|
+
};
|
104
|
+
// Handles column resizing during dragging
|
105
|
+
const onColumnMouseMove = (e) => {
|
106
|
+
if (!isColumnDragging)
|
107
|
+
return;
|
108
|
+
const columnKeys = getKeysByOrientation('column');
|
109
|
+
const currentIndex = columnKeys.indexOf(isColumnDragging);
|
110
|
+
const nextKey = columnKeys[currentIndex + 1];
|
111
|
+
if (!nextKey)
|
112
|
+
return;
|
113
|
+
const deltaPercent = (e.movementX / window.innerWidth) * 100;
|
114
|
+
resizePanels(columnKeys, currentIndex, nextKey, deltaPercent, 'width');
|
115
|
+
};
|
116
|
+
// Handles row resizing during dragging
|
117
|
+
const onRowMouseMove = (e) => {
|
118
|
+
if (!isRowDragging)
|
119
|
+
return;
|
120
|
+
const rowKeys = getKeysByOrientation('row');
|
121
|
+
const currentIndex = rowKeys.indexOf(isRowDragging);
|
122
|
+
const nextKey = rowKeys[currentIndex + 1];
|
123
|
+
if (!nextKey)
|
124
|
+
return;
|
125
|
+
const deltaPercent = (e.movementY / window.innerHeight) * 100;
|
126
|
+
resizePanels(rowKeys, currentIndex, nextKey, deltaPercent, 'height');
|
127
|
+
};
|
128
|
+
// Returns panel keys that match the given orientation (row or column)
|
129
|
+
const getKeysByOrientation = (type) => {
|
130
|
+
return Array.from(panelsState.entries())
|
131
|
+
.filter(([_, value]) => (type === 'row' ? value.isRow : value.isColumn))
|
132
|
+
.map(([key]) => key);
|
133
|
+
};
|
134
|
+
// Shared resizing logic for both rows and columns
|
135
|
+
const resizePanels = (keys, currentIndex, nextKey, deltaPercent, dimension) => {
|
136
|
+
setPanelsState(prev => {
|
137
|
+
const updated = new Map(prev);
|
138
|
+
const updatedOriginalSizes = new Map(originalSizes);
|
139
|
+
const currentKey = keys[currentIndex];
|
140
|
+
const currentSize = parseFloat(updated.get(currentKey)?.[dimension] || '0');
|
141
|
+
const nextSize = parseFloat(updated.get(nextKey)?.[dimension] || '0');
|
142
|
+
const total = currentSize + nextSize;
|
143
|
+
let newCurrent = Math.min(currentSize + deltaPercent, 100);
|
144
|
+
let newNext = Math.min(nextSize - deltaPercent, 100);
|
145
|
+
// Enforce minimum panel size
|
146
|
+
if (newCurrent < 5) {
|
147
|
+
newCurrent = 5;
|
148
|
+
newNext = total - newCurrent;
|
149
|
+
}
|
150
|
+
else if (newNext < 5) {
|
151
|
+
newNext = 5;
|
152
|
+
newCurrent = total - newNext;
|
153
|
+
}
|
154
|
+
// Safely get current and next panel data
|
155
|
+
const currentPanel = updated.get(currentKey);
|
156
|
+
const nextPanel = updated.get(nextKey);
|
157
|
+
// Ensure both panels exist before updating
|
158
|
+
if (!currentPanel || !nextPanel)
|
159
|
+
return prev;
|
160
|
+
// Update panel dimensions
|
161
|
+
updated.set(currentKey, { ...currentPanel, [dimension]: `${newCurrent}%` });
|
162
|
+
updated.set(nextKey, { ...nextPanel, [dimension]: `${newNext}%` });
|
163
|
+
// Update original sizes for persistence
|
164
|
+
updatedOriginalSizes.set(currentKey, { ...currentPanel, [dimension]: `${newCurrent}%` });
|
165
|
+
updatedOriginalSizes.set(nextKey, { ...nextPanel, [dimension]: `${newNext}%` });
|
166
|
+
setOriginalSizes(updatedOriginalSizes);
|
167
|
+
return updated;
|
168
|
+
});
|
169
|
+
};
|
170
|
+
// Add event listeners when dragging starts
|
171
|
+
if (isDragging) {
|
172
|
+
if (isColumnDragging) {
|
173
|
+
window.addEventListener('mousemove', onColumnMouseMove);
|
174
|
+
}
|
175
|
+
else if (isRowDragging) {
|
176
|
+
window.addEventListener('mousemove', onRowMouseMove);
|
177
|
+
}
|
178
|
+
window.addEventListener('mouseup', onMouseUp);
|
179
|
+
document.body.style.userSelect = 'none'; // Prevents text selection during drag
|
180
|
+
}
|
181
|
+
// Cleanup function to remove event listeners
|
182
|
+
return () => {
|
183
|
+
window.removeEventListener('mousemove', onColumnMouseMove);
|
184
|
+
window.removeEventListener('mousemove', onRowMouseMove);
|
185
|
+
window.removeEventListener('mouseup', onMouseUp);
|
186
|
+
document.body.style.userSelect = '';
|
187
|
+
};
|
188
|
+
}, [isColumnDragging, isRowDragging, panelsState, originalSizes]);
|
189
|
+
useEffect(() => {
|
190
|
+
if (isMobile) {
|
191
|
+
setActiveButtons(prev => {
|
192
|
+
// Clone the previous state to avoid mutating it directly
|
193
|
+
const newActive = { ...prev };
|
194
|
+
// Get the panel object from the panel map
|
195
|
+
const panel = panelMap.get(initialMobilePanelId);
|
196
|
+
Object.keys(newActive).forEach(panelId => {
|
197
|
+
newActive[panelId] = false;
|
198
|
+
});
|
199
|
+
// Toggle the selected panel's visibility
|
200
|
+
newActive[initialMobilePanelId] = true;
|
201
|
+
// If the panel is being hidden and has children, recursively hide its children
|
202
|
+
if (panel && panel.childrenIds.length > 0 && !newActive[initialMobilePanelId]) {
|
203
|
+
const hideChildrenRecursively = (ids) => {
|
204
|
+
ids.forEach(childId => {
|
205
|
+
newActive[childId] = false;
|
206
|
+
const childPanel = panelMap.get(childId);
|
207
|
+
if (childPanel?.childrenIds.length) {
|
208
|
+
hideChildrenRecursively(childPanel.childrenIds);
|
209
|
+
}
|
210
|
+
});
|
211
|
+
};
|
212
|
+
hideChildrenRecursively(panel.childrenIds);
|
213
|
+
}
|
214
|
+
// After toggling, ensure the visibility of parent panels is in sync with their children
|
215
|
+
if (panel?.parentId) {
|
216
|
+
syncParentVisibilityWithChildren(panel.parentId, newActive);
|
217
|
+
}
|
218
|
+
// Return the updated visibility map
|
219
|
+
return newActive;
|
220
|
+
});
|
221
|
+
}
|
222
|
+
}, [isMobile]);
|
223
|
+
// Handler for starting to drag a column divider (for resizing panels horizontally)
|
224
|
+
const onColumnMouseDown = (panelKey) => {
|
225
|
+
setIsColumnDragging(panelKey); // Set the state to indicate which column is being dragged
|
226
|
+
};
|
227
|
+
// Handler for starting to drag a row divider (for resizing panels vertically)
|
228
|
+
const onRowMouseDown = (rowKey) => {
|
229
|
+
setIsRowDragging(rowKey); // Set the state to indicate which row is being dragged
|
230
|
+
};
|
231
|
+
// Function to disable a toolbar button by its ID
|
232
|
+
const handleDisableButton = (id, value) => {
|
233
|
+
// Update state to inform toolbar which button should be disabled/enabled
|
234
|
+
setUpdatedDisabledButton({ id, value });
|
235
|
+
};
|
236
|
+
// Function to change the visibility of a toolbar button by its ID
|
237
|
+
const handleVisibilityButton = (id, value) => {
|
238
|
+
// Update state to inform toolbar which button should be shown/hidden
|
239
|
+
setUpdatedVisibleButton({ id, value });
|
240
|
+
};
|
241
|
+
/**
|
242
|
+
* Recursively updates the visibility status of parent panels based on the visibility of their child panels.
|
243
|
+
* If all children are hidden, the parent is hidden.
|
244
|
+
* If at least one child is visible, the parent is shown. Propagates visibility changes up the tree.
|
245
|
+
*/
|
246
|
+
const syncParentVisibilityWithChildren = (panelId, visibleMap) => {
|
247
|
+
const panel = panelMap.get(panelId);
|
248
|
+
if (!panel || panel.childrenIds.length === 0)
|
249
|
+
return;
|
250
|
+
const allChildrenHidden = panel.childrenIds.every(id => !visibleMap[id]);
|
251
|
+
if (allChildrenHidden && visibleMap[panelId]) {
|
252
|
+
visibleMap[panelId] = false;
|
253
|
+
if (panel.parentId) {
|
254
|
+
syncParentVisibilityWithChildren(panel.parentId, visibleMap);
|
255
|
+
}
|
256
|
+
}
|
257
|
+
else if (!allChildrenHidden && !visibleMap[panelId]) {
|
258
|
+
visibleMap[panelId] = true;
|
259
|
+
}
|
260
|
+
};
|
261
|
+
/**
|
262
|
+
* Toggles the maximized state of a panel based on its full ID.
|
263
|
+
* If the panel is already maximized (i.e., the maximized path matches its hierarchy path),
|
264
|
+
* it will be unmaximized. Otherwise, it will be maximized along with its ancestor panels.
|
265
|
+
* The full dot-separated ID of the panel (e.g., "root.section.panel")
|
266
|
+
*/
|
267
|
+
const handleToggleMaximize = (fullId) => {
|
268
|
+
// Split the full panel ID into its segments (e.g., ["root", "section", "panel"])
|
269
|
+
const segments = fullId.split('.');
|
270
|
+
// Build the full path hierarchy up to this panel (e.g., ["root", "root.section", "root.section.panel"])
|
271
|
+
const path = segments.map((_, i) => segments.slice(0, i + 1).join('.'));
|
272
|
+
// Check if this panel is already maximized by comparing path arrays
|
273
|
+
const isAlreadyMaximized = maximizedPanelPath.length === path.length && maximizedPanelPath.every((v, i) => v === path[i]);
|
274
|
+
if (isAlreadyMaximized) {
|
275
|
+
// Unmaximize by clearing the maximized path
|
276
|
+
setMaximizedPanelPath([]);
|
277
|
+
}
|
278
|
+
else {
|
279
|
+
// Maximize this panel and its parent hierarchy
|
280
|
+
setMaximizedPanelPath(path);
|
281
|
+
}
|
282
|
+
};
|
283
|
+
/**
|
284
|
+
* Toggles the active (visible) state of a panel by its ID.
|
285
|
+
* - On **desktop**, the panel's visibility is toggled independently,
|
286
|
+
* and if the panel is hidden, all its child panels are also recursively hidden.
|
287
|
+
* - On **mobile**, only one panel can be visible at a time:
|
288
|
+
* activating a panel will hide all others, including their children.
|
289
|
+
* After updating visibility, the function also ensures that each parent panel's
|
290
|
+
* visibility is synchronized with the state of its children.
|
291
|
+
*
|
292
|
+
*/
|
293
|
+
const handleTogglePanel = (id) => {
|
294
|
+
// If the panel is currently maximized, toggle its maximization state first
|
295
|
+
if (maximizedPanelPath.includes(id)) {
|
296
|
+
handleToggleMaximize(id);
|
297
|
+
}
|
298
|
+
// Update the activeButtons state
|
299
|
+
setActiveButtons(prev => {
|
300
|
+
// Clone the previous state to avoid mutating it directly
|
301
|
+
const newActive = { ...prev };
|
302
|
+
// Get the panel object from the panel map
|
303
|
+
const panel = panelMap.get(id);
|
304
|
+
// Determine if the current panel is currently visible
|
305
|
+
const currentlyVisible = !!newActive[id];
|
306
|
+
// Mobile-specific behavior: only one panel can be visible at a time
|
307
|
+
if (isMobile) {
|
308
|
+
// Hide all panels
|
309
|
+
Object.keys(newActive).forEach(panelId => {
|
310
|
+
newActive[panelId] = false;
|
311
|
+
});
|
312
|
+
// Toggle the selected panel's visibility
|
313
|
+
newActive[id] = !currentlyVisible;
|
314
|
+
// If the panel is being hidden and has children, recursively hide its children
|
315
|
+
if (panel && panel.childrenIds.length > 0 && !newActive[id]) {
|
316
|
+
const hideChildrenRecursively = (ids) => {
|
317
|
+
ids.forEach(childId => {
|
318
|
+
newActive[childId] = false;
|
319
|
+
const childPanel = panelMap.get(childId);
|
320
|
+
if (childPanel?.childrenIds.length) {
|
321
|
+
hideChildrenRecursively(childPanel.childrenIds);
|
322
|
+
}
|
323
|
+
});
|
324
|
+
};
|
325
|
+
hideChildrenRecursively(panel.childrenIds);
|
326
|
+
}
|
327
|
+
}
|
328
|
+
else {
|
329
|
+
// Desktop behavior: toggle visibility of the selected panel
|
330
|
+
if (!currentlyVisible) {
|
331
|
+
// If the panel is currently not visible (we are activating it)
|
332
|
+
if (panel?.alternativePanelIds.length) {
|
333
|
+
// If the panel has alternative panels defined
|
334
|
+
panel.alternativePanelIds.forEach(altId => {
|
335
|
+
// Deactivate each alternative panel
|
336
|
+
newActive[altId] = false;
|
337
|
+
});
|
338
|
+
}
|
339
|
+
// Activate the selected panel
|
340
|
+
newActive[id] = true;
|
341
|
+
}
|
342
|
+
else {
|
343
|
+
// The panel is currently visible, so we are deactivating it
|
344
|
+
newActive[id] = false;
|
345
|
+
}
|
346
|
+
// If hiding the panel and it has children, hide them recursively
|
347
|
+
if (panel && panel.childrenIds.length > 0 && !newActive[id]) {
|
348
|
+
const hideChildrenRecursively = (ids) => {
|
349
|
+
ids.forEach(childId => {
|
350
|
+
newActive[childId] = false;
|
351
|
+
const childPanel = panelMap.get(childId);
|
352
|
+
if (childPanel?.childrenIds.length) {
|
353
|
+
hideChildrenRecursively(childPanel.childrenIds);
|
354
|
+
}
|
355
|
+
});
|
356
|
+
};
|
357
|
+
hideChildrenRecursively(panel.childrenIds);
|
358
|
+
}
|
359
|
+
}
|
360
|
+
// After toggling, ensure the visibility of parent panels is in sync with their children
|
361
|
+
if (panel?.parentId) {
|
362
|
+
syncParentVisibilityWithChildren(panel.parentId, newActive);
|
363
|
+
}
|
364
|
+
// Return the updated visibility map
|
365
|
+
return newActive;
|
366
|
+
});
|
367
|
+
};
|
368
|
+
// Render panels
|
369
|
+
const renderPanels = (panels, parentPath = '', direction = 'horizontal') => {
|
370
|
+
const visiblePanels = panels.filter(p => {
|
371
|
+
const fullId = parentPath ? `${parentPath}.${p.id}` : p.id;
|
372
|
+
return p.contentOptions?.content && activeButtons[fullId];
|
373
|
+
});
|
374
|
+
const keys = visiblePanels.map(p => (parentPath ? `${parentPath}.${p.id}` : p.id));
|
375
|
+
const totalSize = keys.reduce((sum, key) => {
|
376
|
+
const entry = panelsState.get(key);
|
377
|
+
const size = parseFloat(direction === 'horizontal' ? entry?.width || '0' : entry?.height || '0');
|
378
|
+
return sum + size;
|
379
|
+
}, 0);
|
380
|
+
const sizeMap = Object.fromEntries(keys.map(key => {
|
381
|
+
const entry = panelsState.get(key);
|
382
|
+
const size = parseFloat(direction === 'horizontal' ? entry?.width || '0' : entry?.height || '0');
|
383
|
+
const percent = totalSize > 0 ? (size / totalSize) * 100 : 0;
|
384
|
+
return [key, `${percent}%`];
|
385
|
+
}));
|
386
|
+
return panels.map(panel => {
|
387
|
+
if (!panel.contentOptions)
|
388
|
+
return null;
|
389
|
+
const fullId = parentPath ? `${parentPath}.${panel.id}` : panel.id;
|
390
|
+
const isActive = activeButtons[fullId];
|
391
|
+
const hasChildren = isPanelArray(panel.contentOptions?.content);
|
392
|
+
const isLastVisible = keys.indexOf(fullId) === keys.length - 1;
|
393
|
+
const flexDirection = direction === 'horizontal' ? 'row' : 'column';
|
394
|
+
const panelStyle = {
|
395
|
+
display: 'flex',
|
396
|
+
flexDirection,
|
397
|
+
width: direction === 'horizontal' ? (isActive ? sizeMap[fullId] : '0%') : '100%',
|
398
|
+
height: direction === 'vertical' ? (isActive ? sizeMap[fullId] : '0%') : '100%',
|
399
|
+
visibility: isActive ? 'visible' : 'hidden',
|
400
|
+
overflow: 'hidden',
|
401
|
+
position: 'relative',
|
402
|
+
};
|
403
|
+
const contentStyle = {
|
404
|
+
flexGrow: 1,
|
405
|
+
overflow: 'auto',
|
406
|
+
};
|
407
|
+
return (_jsxs("div", { ref: el => {
|
408
|
+
panelRefs.current[fullId] = el;
|
409
|
+
}, style: panelStyle, children: [_jsx("div", { style: contentStyle, children: hasChildren
|
410
|
+
? renderPanels(panel.contentOptions.content, fullId, 'vertical')
|
411
|
+
: panel.contentOptions?.panelContainer ? _jsx(TMPanel, { title: panel.contentOptions.panelContainer.title, totalItems: panel.contentOptions.panelContainer.totalItems, displayedItemsCount: panel.contentOptions.panelContainer.displayedItemsCount, onClose: () => handleTogglePanel(fullId), onMaximize: () => handleToggleMaximize(fullId), allowMaximize: !isMobile, children: typeof panel.contentOptions.content === "function" ? panel.contentOptions.content(handleTogglePanel, handleToggleMaximize, handleVisibilityButton, handleDisableButton) : panel.contentOptions.content })
|
412
|
+
:
|
413
|
+
typeof panel.contentOptions.content === "function" ? panel.contentOptions.content(handleTogglePanel, handleToggleMaximize, handleVisibilityButton, handleDisableButton) : panel.contentOptions.content }), !isLastVisible && isActive && maximizedPanelPath.length === 0 && (_jsx("div", { style: {
|
414
|
+
cursor: direction === 'horizontal' ? 'col-resize' : 'row-resize',
|
415
|
+
userSelect: 'none',
|
416
|
+
flexShrink: 0,
|
417
|
+
backgroundColor: 'transparent',
|
418
|
+
width: direction === 'horizontal' ? '4px' : '100%',
|
419
|
+
height: direction === 'vertical' ? '4px' : '100%',
|
420
|
+
marginLeft: maximizedPanelPath.length === 0 && direction === 'horizontal' ? `${(gutters - 4) / 2}px` : undefined,
|
421
|
+
marginTop: maximizedPanelPath.length === 0 && direction === 'vertical' ? `${(gutters - 4) / 2}px` : undefined,
|
422
|
+
marginRight: maximizedPanelPath.length === 0 && direction === 'horizontal' ? `${(gutters - 4) / 2}px` : undefined,
|
423
|
+
marginBottom: maximizedPanelPath.length === 0 && direction === 'vertical' ? `${(gutters - 4) / 2}px` : undefined,
|
424
|
+
}, onMouseDown: () => {
|
425
|
+
if (direction === 'horizontal') {
|
426
|
+
onColumnMouseDown(fullId);
|
427
|
+
}
|
428
|
+
else {
|
429
|
+
onRowMouseDown(fullId);
|
430
|
+
}
|
431
|
+
} }))] }, fullId));
|
432
|
+
});
|
433
|
+
};
|
434
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: isMobile ? 'column' : 'row', height: '100%', width: '100%' }, children: [_jsx("div", { style: {
|
435
|
+
display: 'flex',
|
436
|
+
flexGrow: 1,
|
437
|
+
width: isAtLeastOnePanelVisible(activeButtons) ? `calc(100% - ${showToolbar ? (isMobile ? 0 : 60) : 0}px)` : '0%',
|
438
|
+
height: isAtLeastOnePanelVisible(activeButtons) ? `calc(100% - ${isMobile ? 60 : 0}px)` : '0%',
|
439
|
+
visibility: isAtLeastOnePanelVisible(activeButtons) ? 'visible' : 'hidden',
|
440
|
+
flexDirection: 'row',
|
441
|
+
}, children: renderPanels(panels, '', 'horizontal') }), !isAtLeastOnePanelVisible(activeButtons) && (_jsxs("div", { style: {
|
442
|
+
width: '100%',
|
443
|
+
height: isMobile ? 'calc(100% - 60px)' : '100%',
|
444
|
+
display: 'flex',
|
445
|
+
flexDirection: 'column',
|
446
|
+
alignItems: 'center',
|
447
|
+
justifyContent: 'center',
|
448
|
+
backgroundColor: '#fff',
|
449
|
+
borderRadius: '12px',
|
450
|
+
textAlign: 'center',
|
451
|
+
color: '#555',
|
452
|
+
fontSize: '18px',
|
453
|
+
}, children: [_jsx(IconInfo, { style: { fontSize: 50 } }), _jsx("div", { children: SDKUI_Localizator.NoPanelSelected })] })), showToolbar && _jsx("div", { style: {
|
454
|
+
width: isMobile ? '100%' : '60px',
|
455
|
+
height: isMobile ? '60px' : '100%',
|
456
|
+
borderLeft: '1px solid #ccc',
|
457
|
+
display: 'flex',
|
458
|
+
flexDirection: isMobile ? 'row' : 'column',
|
459
|
+
padding: '8px',
|
460
|
+
boxSizing: 'border-box',
|
461
|
+
gap: '6px',
|
462
|
+
backgroundColor: '#f9f9f9',
|
463
|
+
}, children: _jsx(TMPanelManagerToolbar, { allPanels: allPanels, activeButtons: activeButtons, handleTogglePanel: handleTogglePanel, updatedDisabledButton: updatedDisabledButton, updatedVisibleButton: updatedVisibleButton }) })] }));
|
464
|
+
};
|
465
|
+
export default TMPanelManager;
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import { TMPanelEntry } from './TMPanelManagerUtils';
|
2
|
+
interface TNPanelManagerToolbarProps {
|
3
|
+
allPanels: Array<TMPanelEntry>;
|
4
|
+
activeButtons: Record<string, boolean>;
|
5
|
+
handleTogglePanel: (id: string) => void;
|
6
|
+
updatedDisabledButton?: {
|
7
|
+
id: string;
|
8
|
+
value: boolean;
|
9
|
+
};
|
10
|
+
updatedVisibleButton?: {
|
11
|
+
id: string;
|
12
|
+
value: boolean;
|
13
|
+
};
|
14
|
+
}
|
15
|
+
declare const TMPanelManagerToolbar: (props: TNPanelManagerToolbarProps) => import("react/jsx-runtime").JSX.Element;
|
16
|
+
export default TMPanelManagerToolbar;
|
@@ -0,0 +1,100 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
import { useEffect, useMemo, useState } from 'react';
|
3
|
+
import { useDeviceType, DeviceType } from './TMDeviceProvider';
|
4
|
+
import TMTooltip from './TMTooltip';
|
5
|
+
const TMPanelManagerToolbar = (props) => {
|
6
|
+
const { allPanels, activeButtons, handleTogglePanel, updatedDisabledButton, updatedVisibleButton } = props;
|
7
|
+
const deviceType = useDeviceType();
|
8
|
+
let isMobile = useMemo(() => { return deviceType === DeviceType.MOBILE; }, [deviceType]);
|
9
|
+
const [filteredPanels, setFilteredPanels] = useState([]);
|
10
|
+
const [disabledButtons, setDisabledButtons] = useState({});
|
11
|
+
const [visibleButtons, setVisibleButtons] = useState({});
|
12
|
+
// Initialize visibility and disabled states
|
13
|
+
useEffect(() => {
|
14
|
+
const visible = {};
|
15
|
+
const disabled = {};
|
16
|
+
const filtered = allPanels
|
17
|
+
.filter((p) => {
|
18
|
+
const vis = !!p.config.toolbarOptions?.visible;
|
19
|
+
visible[p.fullId] = vis;
|
20
|
+
disabled[p.fullId] = !!p.config.toolbarOptions?.disabled;
|
21
|
+
return vis;
|
22
|
+
})
|
23
|
+
.sort((a, b) => (a.config.toolbarOptions?.orderNumber ?? 0) - (b.config.toolbarOptions?.orderNumber ?? 0));
|
24
|
+
setVisibleButtons(visible);
|
25
|
+
setDisabledButtons(disabled);
|
26
|
+
setFilteredPanels(filtered);
|
27
|
+
}, [allPanels]);
|
28
|
+
// Update disabled button
|
29
|
+
useEffect(() => {
|
30
|
+
if (updatedDisabledButton) {
|
31
|
+
setDisabledButtons((prev) => ({
|
32
|
+
...prev,
|
33
|
+
[updatedDisabledButton.id]: updatedDisabledButton.value,
|
34
|
+
}));
|
35
|
+
}
|
36
|
+
}, [updatedDisabledButton]);
|
37
|
+
// Update visible button
|
38
|
+
useEffect(() => {
|
39
|
+
if (updatedVisibleButton) {
|
40
|
+
const { id, value } = updatedVisibleButton;
|
41
|
+
setVisibleButtons((prev) => ({ ...prev, [id]: value }));
|
42
|
+
setFilteredPanels((prev) => {
|
43
|
+
if (value) {
|
44
|
+
const newPanel = allPanels.find((p) => p.fullId === id);
|
45
|
+
if (newPanel && !prev.some((p) => p.fullId === id)) {
|
46
|
+
return [...prev, newPanel].sort((a, b) => (a.config.toolbarOptions?.orderNumber ?? 0) - (b.config.toolbarOptions?.orderNumber ?? 0));
|
47
|
+
}
|
48
|
+
return prev;
|
49
|
+
}
|
50
|
+
else {
|
51
|
+
return prev.filter((p) => p.fullId !== id);
|
52
|
+
}
|
53
|
+
});
|
54
|
+
}
|
55
|
+
}, [updatedVisibleButton, allPanels]);
|
56
|
+
return (_jsx("div", { style: {
|
57
|
+
display: 'flex',
|
58
|
+
flexDirection: isMobile ? 'row' : 'column',
|
59
|
+
gap: '6px',
|
60
|
+
alignItems: isMobile ? 'center' : 'flex-start',
|
61
|
+
justifyContent: isMobile ? 'center' : 'flex-start',
|
62
|
+
width: '100%',
|
63
|
+
height: '100%',
|
64
|
+
}, children: filteredPanels.map((p, idx) => {
|
65
|
+
const isActive = activeButtons[p.fullId];
|
66
|
+
const handleClick = p.config.buttonOptions?.onClick;
|
67
|
+
const isToolbarDisabled = disabledButtons[p.fullId];
|
68
|
+
// Separator JSX if beginGroup is true and NOT first button (otherwise no separator before first button)
|
69
|
+
const separator = (p.config.toolbarOptions?.beginGroup && idx > 0) ? (_jsx("div", { style: {
|
70
|
+
width: isMobile ? '1px' : '80%',
|
71
|
+
height: isMobile ? '80%' : '1px',
|
72
|
+
backgroundColor: '#ccc',
|
73
|
+
margin: isMobile ? '0 6px' : '6px 0',
|
74
|
+
alignSelf: 'center',
|
75
|
+
} }, `sep-${p.fullId}`)) : null;
|
76
|
+
// Button JSX
|
77
|
+
const button = (_jsx(TMTooltip, { content: p.config.name, position: 'left', children: _jsx("button", { disabled: isToolbarDisabled, onClick: () => { handleTogglePanel(p.fullId); if (handleClick)
|
78
|
+
handleClick(); }, style: {
|
79
|
+
display: 'flex',
|
80
|
+
alignItems: 'center',
|
81
|
+
justifyContent: 'center',
|
82
|
+
height: '40px',
|
83
|
+
width: '40px',
|
84
|
+
border: 'none',
|
85
|
+
borderRadius: '10px',
|
86
|
+
fontSize: '18px',
|
87
|
+
padding: '8px 10px',
|
88
|
+
color: '#fff',
|
89
|
+
transition: 'transform 0.2s ease, box-shadow 0.2s ease',
|
90
|
+
cursor: isToolbarDisabled ? 'not-allowed' : 'pointer',
|
91
|
+
opacity: isToolbarDisabled ? 0.6 : 1,
|
92
|
+
backgroundColor: isActive ? '#81c784' : '#e57373',
|
93
|
+
}, onMouseEnter: (e) => { if (!isToolbarDisabled) {
|
94
|
+
e.currentTarget.style.transform = 'scale(1.1)';
|
95
|
+
} }, onMouseLeave: (e) => { e.currentTarget.style.transform = 'scale(1)'; }, children: typeof p.config.toolbarOptions?.icon === 'string' ? (_jsx("i", { className: `dx-icon dx-icon-${p.config.toolbarOptions?.icon}` })) : (p.config.toolbarOptions?.icon) }) }, p.fullId));
|
96
|
+
// Return separator first, then button
|
97
|
+
return [separator, button];
|
98
|
+
}) }));
|
99
|
+
};
|
100
|
+
export default TMPanelManagerToolbar;
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import { ITMPanelProps } from "./TMPanel";
|
2
|
+
export type TMPanelItemConfig = {
|
3
|
+
id: string;
|
4
|
+
name: string;
|
5
|
+
type: 'content' | 'button';
|
6
|
+
alternativePanelIds?: Array<string>;
|
7
|
+
buttonOptions?: {
|
8
|
+
onClick: () => void;
|
9
|
+
};
|
10
|
+
contentOptions?: {
|
11
|
+
height: string;
|
12
|
+
width: string;
|
13
|
+
visible?: boolean;
|
14
|
+
content?: JSX.Element | ((handleTogglePanel: (id: string) => void, handleToggleMaximize: (fullId: string) => void, handleVisibilityButton: (id: string, value: boolean) => void, handleDisableButton: (id: string, value: boolean) => void) => JSX.Element) | Array<TMPanelItemConfig>;
|
15
|
+
panelContainer?: ITMPanelProps;
|
16
|
+
};
|
17
|
+
toolbarOptions?: {
|
18
|
+
icon: string | JSX.Element;
|
19
|
+
isActive: boolean;
|
20
|
+
visible: boolean;
|
21
|
+
disabled?: boolean;
|
22
|
+
orderNumber?: number;
|
23
|
+
beginGroup?: boolean;
|
24
|
+
};
|
25
|
+
};
|
26
|
+
export type TMPanelEntry = {
|
27
|
+
fullId: string;
|
28
|
+
config: TMPanelItemConfig;
|
29
|
+
parentId: string | null;
|
30
|
+
childrenIds: Array<string>;
|
31
|
+
alternativePanelIds: Array<string>;
|
32
|
+
};
|
33
|
+
export declare const isPanelArray: (content: unknown) => content is Array<TMPanelItemConfig>;
|
34
|
+
export declare const flattenPanels: (panels: Array<TMPanelItemConfig>, parentId?: string | null, parentPath?: string) => Array<TMPanelEntry>;
|
35
|
+
export declare const isAtLeastOnePanelVisible: (activeButtons: Record<string, boolean>) => boolean;
|
36
|
+
export declare const isExactlyOnePanelVisible: (activeButtons: Record<string, boolean>) => boolean;
|
@@ -0,0 +1,27 @@
|
|
1
|
+
// Type guard to check if a value is an array of TMPanelItemConfig objects.
|
2
|
+
// Ensures that every item in the array is a non-null object with 'id' and 'contentOptions' properties.
|
3
|
+
export const isPanelArray = (content) => Array.isArray(content) && content.every(p => typeof p === 'object' && p !== null && 'id' in p && 'contentOptions' in p);
|
4
|
+
// Recursively flattens a nested array of panel configurations into a flat array of TMPanelEntry objects.
|
5
|
+
// - panels: the input array of TMPanelItemConfig.
|
6
|
+
// - parentId: optional ID of the parent panel (used for hierarchical relationships).
|
7
|
+
// - parentPath: string path of ancestor IDs, used to build a full, unique identifier for each panel.
|
8
|
+
export const flattenPanels = (panels, parentId = null, parentPath = '') => {
|
9
|
+
return panels.flatMap(panel => {
|
10
|
+
// Construct the full ID by appending the current panel ID to the parent path.
|
11
|
+
const fullId = parentPath ? `${parentPath}.${panel.id}` : panel.id;
|
12
|
+
let children = [];
|
13
|
+
// If the panel has nested content that is also a valid panel array, recursively flatten them.
|
14
|
+
if (isPanelArray(panel.contentOptions?.content)) {
|
15
|
+
children = flattenPanels(panel.contentOptions.content, fullId, fullId);
|
16
|
+
}
|
17
|
+
// Extract full IDs of all child entries.
|
18
|
+
const childrenIds = children.map(c => c.fullId);
|
19
|
+
const alternativePanelIds = panel.alternativePanelIds ?? [];
|
20
|
+
// Return the current panel as a TMPanelEntry followed by its flattened children.
|
21
|
+
return [{ fullId, config: panel, parentId, childrenIds, alternativePanelIds }, ...children];
|
22
|
+
});
|
23
|
+
};
|
24
|
+
// Checks if at least one panel is currently visible (i.e., at least one active button is true)
|
25
|
+
export const isAtLeastOnePanelVisible = (activeButtons) => Object.values(activeButtons).some(Boolean);
|
26
|
+
// Checks if exactly one panel is currently visible (i.e., only one active button is true)
|
27
|
+
export const isExactlyOnePanelVisible = (activeButtons) => Object.values(activeButtons).filter(Boolean).length === 1;
|
@@ -14,8 +14,9 @@ export * from './base/TMToolbarCard';
|
|
14
14
|
export * from './base/TMRightSidebar';
|
15
15
|
export * from './base/TMTreeView';
|
16
16
|
export * from './base/TMPanel';
|
17
|
-
export { default as
|
18
|
-
export
|
17
|
+
export { default as TMPanelManager } from './base/TMPanelManager';
|
18
|
+
export { default as TMPanelManagerToolbar } from './base/TMPanelManagerToolbar';
|
19
|
+
export * from './base/TMPanelManagerUtils';
|
19
20
|
export { default as CounterBar } from './base/TMCounterBar';
|
20
21
|
export { default as TMProgressBar } from './base/TMProgressBar';
|
21
22
|
export { default as TMSpinner } from './base/TMSpinner';
|
package/lib/components/index.js
CHANGED
@@ -15,8 +15,9 @@ export * from './base/TMToolbarCard';
|
|
15
15
|
export * from './base/TMRightSidebar';
|
16
16
|
export * from './base/TMTreeView';
|
17
17
|
export * from './base/TMPanel';
|
18
|
-
export { default as
|
19
|
-
export
|
18
|
+
export { default as TMPanelManager } from './base/TMPanelManager';
|
19
|
+
export { default as TMPanelManagerToolbar } from './base/TMPanelManagerToolbar';
|
20
|
+
export * from './base/TMPanelManagerUtils';
|
20
21
|
export { default as CounterBar } from './base/TMCounterBar';
|
21
22
|
export { default as TMProgressBar } from './base/TMProgressBar';
|
22
23
|
export { default as TMSpinner } from './base/TMSpinner';
|
package/package.json
CHANGED
@@ -1,9 +0,0 @@
|
|
1
|
-
import { TMPanelManagerMatrixColumn, TMPanelManagerToolbar } from './TMPanelManagerMatrixUtils';
|
2
|
-
export interface TMPanelManagerMatrixProps {
|
3
|
-
panelMatrixMap: Map<string, TMPanelManagerMatrixColumn>;
|
4
|
-
toolbar?: TMPanelManagerToolbar;
|
5
|
-
initialMobilePanelID?: string;
|
6
|
-
gutters?: number;
|
7
|
-
}
|
8
|
-
declare const TMPanelManagerMatrix: (props: TMPanelManagerMatrixProps) => import("react/jsx-runtime").JSX.Element;
|
9
|
-
export default TMPanelManagerMatrix;
|
@@ -1,262 +0,0 @@
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
-
import React, { useEffect, useMemo, useState } from 'react';
|
3
|
-
import { SDKUI_Globals, SDKUI_Localizator, IconInfo } from '../../helper';
|
4
|
-
import { useDeviceType, DeviceType } from './TMDeviceProvider';
|
5
|
-
import TMPanel from './TMPanel';
|
6
|
-
import { calculatePanelSizes, DesktopToolbar, getDynamicColumnWidth, getInitialMobilePanelID, MobileToolbar } from './TMPanelManagerMatrixUtils';
|
7
|
-
const TMPanelManagerMatrix = (props) => {
|
8
|
-
// Destructure props
|
9
|
-
const { panelMatrixMap, toolbar, initialMobilePanelID, gutters = SDKUI_Globals.userSettings.themeSettings.gutters } = props;
|
10
|
-
// Get the current device type (e.g., mobile, tablet, desktop) using a custom hook.
|
11
|
-
const deviceType = useDeviceType();
|
12
|
-
// This avoids unnecessary re-renders by only recalculating when deviceType changes.
|
13
|
-
let isMobile = useMemo(() => { return deviceType === DeviceType.MOBILE; }, [deviceType]);
|
14
|
-
// State for toolbar items
|
15
|
-
const [toolbarState, setToolbarState] = useState(toolbar);
|
16
|
-
// State to track which panel IDs are currently hidden
|
17
|
-
const [hiddenPanelIds, setHiddenPanelIds] = useState(() => {
|
18
|
-
const initialHidden = new Set();
|
19
|
-
if (isMobile) {
|
20
|
-
// Mobile: mostra solo il primo pannello
|
21
|
-
let firstMobilePanelID = initialMobilePanelID ?? getInitialMobilePanelID(panelMatrixMap);
|
22
|
-
panelMatrixMap.forEach(col => {
|
23
|
-
col.rows.forEach(row => {
|
24
|
-
if (row.id !== firstMobilePanelID)
|
25
|
-
initialHidden.add(row.id);
|
26
|
-
});
|
27
|
-
});
|
28
|
-
}
|
29
|
-
else {
|
30
|
-
// Desktop: nascondi quelli con hidden true
|
31
|
-
panelMatrixMap.forEach(col => {
|
32
|
-
col.rows.forEach(row => {
|
33
|
-
if (row.hidden)
|
34
|
-
initialHidden.add(row.id);
|
35
|
-
});
|
36
|
-
});
|
37
|
-
}
|
38
|
-
return initialHidden;
|
39
|
-
});
|
40
|
-
// State to store the current widths of all column, represented as a Map
|
41
|
-
const [columnWidths, setColumnWidths] = useState(new Map());
|
42
|
-
// State to store the current heights of all panels, represented as a Map
|
43
|
-
const [panelHeights, setPanelHeights] = useState(new Map());
|
44
|
-
// State to track maximized panel
|
45
|
-
const [maximizedPanelId, setMaximizedPanelId] = useState(null);
|
46
|
-
// Memoized computation of visible columns and the total number of visible panels
|
47
|
-
const { visibleColumns, totalVisiblePanels } = useMemo(() => {
|
48
|
-
let totalVisiblePanels = 0;
|
49
|
-
const visibleColumns = [];
|
50
|
-
for (const [columnKey, column] of panelMatrixMap.entries()) {
|
51
|
-
// Filter visible rows in the current column
|
52
|
-
const visibleRows = column.rows.filter(row => !hiddenPanelIds.has(row.id));
|
53
|
-
// If any visible rows exist, include the column and count them
|
54
|
-
if (visibleRows.length > 0) {
|
55
|
-
visibleColumns.push(columnKey);
|
56
|
-
totalVisiblePanels += visibleRows.length;
|
57
|
-
}
|
58
|
-
}
|
59
|
-
return { visibleColumns, totalVisiblePanels };
|
60
|
-
}, [panelMatrixMap, hiddenPanelIds]);
|
61
|
-
// Effect hook to update column widths
|
62
|
-
useEffect(() => {
|
63
|
-
const newColumnWidths = new Map();
|
64
|
-
Array.from(panelMatrixMap.entries()).forEach(([columnKey, col]) => {
|
65
|
-
if (!maximizedPanelId) {
|
66
|
-
const width = getDynamicColumnWidth(col, hiddenPanelIds, Array.from(panelMatrixMap.values()));
|
67
|
-
newColumnWidths.set(columnKey, { width });
|
68
|
-
}
|
69
|
-
});
|
70
|
-
setColumnWidths(newColumnWidths);
|
71
|
-
}, [panelMatrixMap, hiddenPanelIds, maximizedPanelId]);
|
72
|
-
// Effect hook to set initial hidden panels and their heights
|
73
|
-
useEffect(() => {
|
74
|
-
const initialHidden = new Set();
|
75
|
-
if (isMobile) {
|
76
|
-
// On mobile, only show the first selected mobile panel; hide the rest
|
77
|
-
let firstMobilePanelID = initialMobilePanelID ?? getInitialMobilePanelID(panelMatrixMap);
|
78
|
-
panelMatrixMap.forEach(col => {
|
79
|
-
col.rows.forEach(row => {
|
80
|
-
if (row.id !== firstMobilePanelID)
|
81
|
-
initialHidden.add(row.id);
|
82
|
-
});
|
83
|
-
});
|
84
|
-
}
|
85
|
-
else {
|
86
|
-
// On non-mobile devices, hide panels that are marked as `hidden`
|
87
|
-
panelMatrixMap.forEach(col => {
|
88
|
-
col.rows.forEach(row => {
|
89
|
-
if (row.hidden)
|
90
|
-
initialHidden.add(row.id);
|
91
|
-
});
|
92
|
-
});
|
93
|
-
}
|
94
|
-
// Update state with the calculated hidden panels and panel sizes
|
95
|
-
setHiddenPanelIds(initialHidden);
|
96
|
-
setPanelHeights(calculatePanelSizes(panelMatrixMap, initialHidden));
|
97
|
-
}, [panelMatrixMap, isMobile]);
|
98
|
-
// Function to toggle the disabled of a toolbar given its item
|
99
|
-
const toggleToolbarItemDisabled = (toolbarItem) => {
|
100
|
-
if (!toolbarItem)
|
101
|
-
return;
|
102
|
-
setToolbarState(prevToolbar => {
|
103
|
-
if (!prevToolbar)
|
104
|
-
return prevToolbar;
|
105
|
-
return {
|
106
|
-
...prevToolbar,
|
107
|
-
items: prevToolbar.items.map(item => item.id === toolbarItem.id ? { ...item, disabled: !item.disabled } : item)
|
108
|
-
};
|
109
|
-
});
|
110
|
-
};
|
111
|
-
// Function to toggle the visibility of a toolbar given its item
|
112
|
-
const toggleToolbarItemVisibility = (toolbarItem) => {
|
113
|
-
if (!toolbarItem)
|
114
|
-
return;
|
115
|
-
setToolbarState(prevToolbar => {
|
116
|
-
if (!prevToolbar)
|
117
|
-
return prevToolbar;
|
118
|
-
return {
|
119
|
-
...prevToolbar,
|
120
|
-
items: prevToolbar.items.map(item => item.id === toolbarItem.id ? { ...item, visible: !(item.visible ?? true) } : item)
|
121
|
-
};
|
122
|
-
});
|
123
|
-
};
|
124
|
-
// Function to toggle the visibility of a panel given its item
|
125
|
-
const togglePanelVisibility = (toolbarItem) => {
|
126
|
-
if (!toolbarItem)
|
127
|
-
return;
|
128
|
-
setHiddenPanelIds(prevHidden => {
|
129
|
-
let newHidden;
|
130
|
-
if (isMobile) {
|
131
|
-
// On mobile, make only the selected panel visible; hide all others
|
132
|
-
newHidden = new Set();
|
133
|
-
panelMatrixMap.forEach(col => {
|
134
|
-
col.rows.forEach(row => {
|
135
|
-
if (row.id !== toolbarItem.panelManagerMatrixRowId) {
|
136
|
-
newHidden.add(row.id);
|
137
|
-
}
|
138
|
-
});
|
139
|
-
});
|
140
|
-
}
|
141
|
-
else {
|
142
|
-
// On non-mobile devices, toggle visibility normally
|
143
|
-
newHidden = new Set(prevHidden);
|
144
|
-
// Hide alternative panels if any
|
145
|
-
if (toolbarItem.alternativePanelManagerMatrixRowId?.length) {
|
146
|
-
toolbarItem.alternativePanelManagerMatrixRowId.forEach(altId => newHidden.add(altId));
|
147
|
-
}
|
148
|
-
// Toggle clicked panel visibility
|
149
|
-
if (newHidden.has(toolbarItem.panelManagerMatrixRowId)) {
|
150
|
-
newHidden.delete(toolbarItem.panelManagerMatrixRowId);
|
151
|
-
}
|
152
|
-
else {
|
153
|
-
// If the panel is maximized, call maximizeMinimizePanelCallback to minimize it
|
154
|
-
if (maximizedPanelId === toolbarItem.panelManagerMatrixRowId) {
|
155
|
-
toogleMaximizeMinimizePanelCallback(toolbarItem.panelManagerMatrixRowId);
|
156
|
-
}
|
157
|
-
newHidden.add(toolbarItem.panelManagerMatrixRowId);
|
158
|
-
}
|
159
|
-
}
|
160
|
-
setPanelHeights(calculatePanelSizes(panelMatrixMap, newHidden));
|
161
|
-
return newHidden;
|
162
|
-
});
|
163
|
-
};
|
164
|
-
const toogleMaximizeMinimizePanelCallback = (id) => {
|
165
|
-
setMaximizedPanelId(prevId => (prevId === id ? null : id));
|
166
|
-
};
|
167
|
-
return (_jsx("div", { style: { width: '100%', height: '100%' }, children: _jsxs("div", { style: {
|
168
|
-
display: isMobile ? 'block' : 'flex',
|
169
|
-
flexDirection: 'row',
|
170
|
-
width: "100%",
|
171
|
-
height: '100%',
|
172
|
-
overflow: 'hidden',
|
173
|
-
position: 'relative',
|
174
|
-
}, children: [_jsx("div", { style: {
|
175
|
-
display: 'flex',
|
176
|
-
flexDirection: 'row',
|
177
|
-
width: (toolbarState && toolbarState.items.length > 0) ? `calc(100% - ${isMobile ? "0px" : "60px"})` : '100%',
|
178
|
-
height: `calc(100% - ${isMobile ? "60px" : "0px"})`,
|
179
|
-
overflow: 'hidden',
|
180
|
-
// transition: 'width 0.3s',
|
181
|
-
}, children: visibleColumns.length > 0 ? visibleColumns.map((column, colIndex) => {
|
182
|
-
// Retrieve the column data from the panelMatrixMap using the current column key
|
183
|
-
const col = panelMatrixMap.get(column);
|
184
|
-
// Check if the current column contains the maximized panel
|
185
|
-
const hasMaximized = !!col?.rows?.some(row => row.id === maximizedPanelId);
|
186
|
-
const isLastColumn = colIndex === visibleColumns.length - 1;
|
187
|
-
// Define the base style for all columns with flex layout, full height, smooth width transition, and no minimum width
|
188
|
-
const columnBaseStyle = {
|
189
|
-
display: 'flex',
|
190
|
-
flexDirection: 'column',
|
191
|
-
height: '100%',
|
192
|
-
// transition: isMobile ? 'none' : 'width 0.3s',
|
193
|
-
paddingRight: (!maximizedPanelId && !isLastColumn) ? `${gutters}px` : '0px'
|
194
|
-
};
|
195
|
-
// Declare a variable to hold the final computed style for the column
|
196
|
-
let columnStyle;
|
197
|
-
if (maximizedPanelId) {
|
198
|
-
// If a panel is maximized, if it does, give the column full width
|
199
|
-
columnStyle = hasMaximized ? { ...columnBaseStyle, width: '100%' } : { ...columnBaseStyle, width: '0px', overflow: 'hidden' };
|
200
|
-
}
|
201
|
-
else {
|
202
|
-
// Otherwise, hide this column by setting width to 0 and hiding overflow
|
203
|
-
const savedWidth = columnWidths.get(column)?.width ?? getDynamicColumnWidth(col, hiddenPanelIds, Array.from(panelMatrixMap.values()));
|
204
|
-
columnStyle = { ...columnBaseStyle, width: savedWidth };
|
205
|
-
}
|
206
|
-
return (_jsx(React.Fragment, { children: _jsx("div", { style: columnStyle, children: col?.rows?.map((row) => {
|
207
|
-
const isHiddenPanel = hiddenPanelIds.has(row.id);
|
208
|
-
const visibleRows = col.rows?.filter(r => !hiddenPanelIds.has(r.id));
|
209
|
-
const isLastVisible = visibleRows.length > 0 && visibleRows[visibleRows.length - 1].id === row.id;
|
210
|
-
const isMaximized = maximizedPanelId === row.id;
|
211
|
-
const baseStyle = {
|
212
|
-
borderRadius: '8px',
|
213
|
-
display: 'block',
|
214
|
-
// transition: isMobile ? 'none' : 'all 0.3s ease',
|
215
|
-
paddingBottom: (isHiddenPanel || isLastVisible || isMaximized) ? '0px' : `${gutters}px`,
|
216
|
-
};
|
217
|
-
const hiddenStyle = {
|
218
|
-
minWidth: '0px',
|
219
|
-
width: '0px',
|
220
|
-
height: '0px',
|
221
|
-
visibility: 'hidden',
|
222
|
-
opacity: 0,
|
223
|
-
};
|
224
|
-
const maximizedStyle = {
|
225
|
-
minWidth: '50px',
|
226
|
-
width: '100%',
|
227
|
-
height: '100%',
|
228
|
-
visibility: 'visible',
|
229
|
-
opacity: 1,
|
230
|
-
};
|
231
|
-
const defaultStyle = {
|
232
|
-
minWidth: isHiddenPanel ? '0px' : '50px',
|
233
|
-
width: '100%',
|
234
|
-
height: isHiddenPanel ? '0px' : panelHeights.get(row.id)?.height ?? '100%',
|
235
|
-
visibility: isHiddenPanel ? 'hidden' : 'visible',
|
236
|
-
opacity: isHiddenPanel ? 0 : 1,
|
237
|
-
};
|
238
|
-
const panelStyle = maximizedPanelId ? (isMaximized ? { ...baseStyle, ...maximizedStyle } : hiddenStyle) : { ...baseStyle, ...defaultStyle };
|
239
|
-
return _jsx("div", { style: panelStyle, children: row.panel ? _jsx(TMPanel, { title: row.panel.title, totalItems: row.panel.totalItems ?? undefined, displayedItemsCount: row.panel.displayedItemsCount ?? undefined, showHeader: row.panel.showHeader ?? true, allowMaximize: !isMobile, onClose: (!isMobile && toolbarState) ? () => togglePanelVisibility(toolbarState.items.find(item => item.panelManagerMatrixRowId === row.id)) : undefined, onMaximize: () => toogleMaximizeMinimizePanelCallback(row.id), onHeaderDoubleClick: () => toogleMaximizeMinimizePanelCallback(row.id), children: _jsx("div", { style: { width: '100%', height: '100%' }, children: typeof row.content === "function" ? row.content(togglePanelVisibility, toogleMaximizeMinimizePanelCallback, toggleToolbarItemDisabled, toggleToolbarItemVisibility) : row.content }) })
|
240
|
-
: _jsx("div", { style: { width: '100%', height: '100%' }, children: typeof row.content === "function" ? row.content(togglePanelVisibility, toogleMaximizeMinimizePanelCallback, toggleToolbarItemDisabled, toggleToolbarItemVisibility) : row.content }) }, "TMPanelManagerMatrix-r-" + row.id);
|
241
|
-
}) }) }, "TMPanelManagerMatrix-c-" + column));
|
242
|
-
}) : _jsxs("div", { style: {
|
243
|
-
width: '100%',
|
244
|
-
height: '100%',
|
245
|
-
display: 'flex',
|
246
|
-
flexDirection: 'column',
|
247
|
-
alignItems: 'center',
|
248
|
-
justifyContent: 'center',
|
249
|
-
backgroundColor: '#fff',
|
250
|
-
border: '1px solid #ddd',
|
251
|
-
borderRadius: '12px',
|
252
|
-
boxShadow: '0 4px 10px rgba(0, 0, 0, 0.05)',
|
253
|
-
padding: '40px',
|
254
|
-
textAlign: 'center',
|
255
|
-
color: '#555',
|
256
|
-
fontSize: '18px',
|
257
|
-
gap: '16px'
|
258
|
-
}, children: [_jsx(IconInfo, { style: { fontSize: 50 } }), _jsx("div", { children: SDKUI_Localizator.NoPanelSelected })] }) }), toolbarState && toolbarState.items.length > 0 && (!isMobile
|
259
|
-
? DesktopToolbar(toolbarState, hiddenPanelIds, togglePanelVisibility)
|
260
|
-
: MobileToolbar(toolbarState, hiddenPanelIds, togglePanelVisibility))] }) }));
|
261
|
-
};
|
262
|
-
export default TMPanelManagerMatrix;
|
@@ -1,69 +0,0 @@
|
|
1
|
-
import { ITMPanelProps } from './TMPanel';
|
2
|
-
export declare const defaultTMPanelManagerToolbarStyles: {
|
3
|
-
container: {
|
4
|
-
backgroundColor: string;
|
5
|
-
padding: string;
|
6
|
-
};
|
7
|
-
items: {
|
8
|
-
width: string;
|
9
|
-
height: string;
|
10
|
-
inactiveBackgroundColor: string;
|
11
|
-
activeBackgroundColor: string;
|
12
|
-
borderRadius: string;
|
13
|
-
marginBottom: string;
|
14
|
-
iconFontSize: string;
|
15
|
-
iconColor: string;
|
16
|
-
};
|
17
|
-
};
|
18
|
-
export interface TMPanelManagerMatrixColumn {
|
19
|
-
rows: Array<TMPanelManagerMatrixRow>;
|
20
|
-
width?: string;
|
21
|
-
}
|
22
|
-
export interface ExtendedTMPanelProps extends ITMPanelProps {
|
23
|
-
showCustomMaximizeMinimizeButton?: boolean;
|
24
|
-
}
|
25
|
-
export interface TMPanelManagerMatrixRow {
|
26
|
-
id: string;
|
27
|
-
hidden: boolean;
|
28
|
-
content: JSX.Element | ((togglePanelVisibility: (toolbarItem: TMPanelManagerToolbarItem | undefined) => void, toogleMaximizeMinimizePanelCallback: (id: string) => void, toggleToolbarItemDisabled: (toolbarItem: TMPanelManagerToolbarItem | undefined) => void, toggleToolbarItemVisibility: (toolbarItem: TMPanelManagerToolbarItem | undefined) => void) => JSX.Element);
|
29
|
-
height?: string;
|
30
|
-
width?: string;
|
31
|
-
panel?: ExtendedTMPanelProps;
|
32
|
-
}
|
33
|
-
export interface TMPanelManagerToolbarStyles {
|
34
|
-
container?: {
|
35
|
-
backgroundColor?: string;
|
36
|
-
padding?: string;
|
37
|
-
};
|
38
|
-
items?: {
|
39
|
-
width?: string;
|
40
|
-
height?: string;
|
41
|
-
inactiveBackgroundColor?: string;
|
42
|
-
activeBackgroundColor?: string;
|
43
|
-
borderRadius?: string;
|
44
|
-
marginBottom?: string;
|
45
|
-
iconFontSize?: string;
|
46
|
-
iconColor?: string;
|
47
|
-
};
|
48
|
-
}
|
49
|
-
export interface TMPanelManagerToolbarItem {
|
50
|
-
id: string;
|
51
|
-
icon: string | JSX.Element;
|
52
|
-
tooltipName: string;
|
53
|
-
panelManagerMatrixRowId: string;
|
54
|
-
alternativePanelManagerMatrixRowId?: Array<string>;
|
55
|
-
beginGroup?: boolean;
|
56
|
-
disabled?: boolean;
|
57
|
-
visible?: boolean;
|
58
|
-
}
|
59
|
-
export interface TMPanelManagerToolbar {
|
60
|
-
items: Array<TMPanelManagerToolbarItem>;
|
61
|
-
styles?: TMPanelManagerToolbarStyles;
|
62
|
-
}
|
63
|
-
export declare const calculatePanelSizes: (panelMatrixMap: Map<string, TMPanelManagerMatrixColumn>, hiddenIds: Set<string>) => Map<string, {
|
64
|
-
height: string;
|
65
|
-
}>;
|
66
|
-
export declare const getInitialMobilePanelID: (panelMatrixMap: Map<string, TMPanelManagerMatrixColumn>) => string | undefined;
|
67
|
-
export declare const DesktopToolbar: (toolbar: TMPanelManagerToolbar, hiddenPanelIds: Set<string>, togglePanelVisibility: (panel: TMPanelManagerToolbarItem) => void) => import("react/jsx-runtime").JSX.Element;
|
68
|
-
export declare const MobileToolbar: (toolbar: TMPanelManagerToolbar, hiddenPanelIds: Set<string>, togglePanelVisibility: (panel: TMPanelManagerToolbarItem) => void) => import("react/jsx-runtime").JSX.Element;
|
69
|
-
export declare const getDynamicColumnWidth: (col: TMPanelManagerMatrixColumn | undefined, hiddenPanelIds: Set<string>, allColumns: Array<TMPanelManagerMatrixColumn>) => string;
|
@@ -1,155 +0,0 @@
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
-
import { ScrollView } from "devextreme-react";
|
3
|
-
import { SDKUI_Globals } from '../../helper';
|
4
|
-
import TMTooltip from './TMTooltip';
|
5
|
-
export const defaultTMPanelManagerToolbarStyles = {
|
6
|
-
container: {
|
7
|
-
backgroundColor: '#f0f0f0',
|
8
|
-
padding: '6px'
|
9
|
-
},
|
10
|
-
items: {
|
11
|
-
width: '35px',
|
12
|
-
height: '35px',
|
13
|
-
inactiveBackgroundColor: '#e57373',
|
14
|
-
activeBackgroundColor: '#81c784',
|
15
|
-
borderRadius: '50%',
|
16
|
-
marginBottom: '6px',
|
17
|
-
iconFontSize: "25px",
|
18
|
-
iconColor: '#ffffff'
|
19
|
-
}
|
20
|
-
};
|
21
|
-
;
|
22
|
-
// Helper to calculate sizes based on hidden panel IDs
|
23
|
-
export const calculatePanelSizes = (panelMatrixMap, hiddenIds) => {
|
24
|
-
const sizes = new Map();
|
25
|
-
panelMatrixMap.forEach((column) => {
|
26
|
-
const visiblePanels = column.rows.filter(p => !hiddenIds.has(p.id));
|
27
|
-
const hiddenPanels = column.rows.filter(p => hiddenIds.has(p.id));
|
28
|
-
if (visiblePanels.length > 0) {
|
29
|
-
const totalVisibleHeight = visiblePanels.reduce((sum, panel) => {
|
30
|
-
return sum + parseFloat(panel.height || '0');
|
31
|
-
}, 0);
|
32
|
-
visiblePanels.forEach(panel => {
|
33
|
-
const originalHeight = parseFloat(panel.height || '0');
|
34
|
-
const newHeightPercent = (originalHeight / totalVisibleHeight) * 100;
|
35
|
-
sizes.set(panel.id, { height: `${newHeightPercent}%` });
|
36
|
-
});
|
37
|
-
}
|
38
|
-
hiddenPanels.forEach(panel => {
|
39
|
-
sizes.set(panel.id, { height: '0%' });
|
40
|
-
});
|
41
|
-
});
|
42
|
-
return sizes;
|
43
|
-
};
|
44
|
-
export const getInitialMobilePanelID = (panelMatrixMap) => {
|
45
|
-
const firstEntry = panelMatrixMap.values().next().value?.rows;
|
46
|
-
if (!firstEntry)
|
47
|
-
return undefined;
|
48
|
-
const visiblePanel = firstEntry.find((row) => !row.hidden);
|
49
|
-
return visiblePanel?.id;
|
50
|
-
};
|
51
|
-
export const DesktopToolbar = (toolbar, hiddenPanelIds, togglePanelVisibility) => {
|
52
|
-
return _jsx("div", { style: {
|
53
|
-
width: '40px',
|
54
|
-
display: 'flex',
|
55
|
-
flexDirection: 'column',
|
56
|
-
justifyContent: 'flex-start',
|
57
|
-
borderRadius: '8px',
|
58
|
-
alignItems: 'center',
|
59
|
-
marginLeft: SDKUI_Globals.userSettings.themeSettings.gutters,
|
60
|
-
// customizable properties
|
61
|
-
backgroundColor: toolbar.styles?.container?.backgroundColor ?? defaultTMPanelManagerToolbarStyles.container.backgroundColor,
|
62
|
-
padding: toolbar.styles?.container?.padding ?? defaultTMPanelManagerToolbarStyles.container.padding,
|
63
|
-
}, children: toolbar.items.filter(item => item.visible !== false).map((item, index) => {
|
64
|
-
return _jsxs("div", { children: [item.beginGroup && index !== 0 && (_jsx("hr", { style: { margin: '10px 0', borderTop: '1px solid #c6c6c6', width: '100%' } })), _jsx("button", { disabled: item.disabled ?? false, style: {
|
65
|
-
display: 'flex',
|
66
|
-
justifyContent: 'center',
|
67
|
-
alignItems: 'center',
|
68
|
-
border: 'none',
|
69
|
-
cursor: 'pointer',
|
70
|
-
transition: 'background-color 0.3s, transform 0.2s',
|
71
|
-
// customizable properties
|
72
|
-
width: toolbar.styles?.items?.width ?? defaultTMPanelManagerToolbarStyles.items.width,
|
73
|
-
height: toolbar.styles?.items?.height ?? defaultTMPanelManagerToolbarStyles.items.height,
|
74
|
-
backgroundColor: item.disabled ? '#cccccc'
|
75
|
-
: hiddenPanelIds.has(item.panelManagerMatrixRowId)
|
76
|
-
? toolbar.styles?.items?.inactiveBackgroundColor ?? defaultTMPanelManagerToolbarStyles.items.inactiveBackgroundColor
|
77
|
-
: toolbar.styles?.items?.activeBackgroundColor ?? defaultTMPanelManagerToolbarStyles.items.activeBackgroundColor,
|
78
|
-
borderRadius: toolbar.styles?.items?.borderRadius ?? defaultTMPanelManagerToolbarStyles.items.borderRadius,
|
79
|
-
marginBottom: toolbar.styles?.items?.marginBottom ?? defaultTMPanelManagerToolbarStyles.items.marginBottom,
|
80
|
-
}, onMouseEnter: (e) => {
|
81
|
-
e.currentTarget.style.transform = 'scale(1.1)';
|
82
|
-
}, onMouseLeave: (e) => {
|
83
|
-
e.currentTarget.style.transform = 'scale(1)';
|
84
|
-
}, onClick: () => togglePanelVisibility(item), children: _jsx(TMTooltip, { content: item.tooltipName, children: typeof item.icon === 'string' ? _jsx("i", { className: `dx-icon dx-icon-${item.icon}`, style: { fontSize: toolbar.styles?.items?.iconFontSize ?? defaultTMPanelManagerToolbarStyles.items.iconFontSize, color: toolbar.styles?.items?.iconColor ?? defaultTMPanelManagerToolbarStyles.items.iconColor } }) : item.icon }) })] }, "TMPanelManagerMatrix-Desktop-Toolbar-Item-" + item.id);
|
85
|
-
}) }, "TMPanelManagerMatrix-Desktop-Toolbar");
|
86
|
-
};
|
87
|
-
export const MobileToolbar = (toolbar, hiddenPanelIds, togglePanelVisibility) => {
|
88
|
-
return _jsx("div", { style: {
|
89
|
-
width: '100%',
|
90
|
-
height: '60px',
|
91
|
-
background: 'linear-gradient(135deg, #e0eafc, #cfdef3)',
|
92
|
-
display: 'flex',
|
93
|
-
flexDirection: 'column',
|
94
|
-
justifyContent: 'center',
|
95
|
-
borderRadius: '12px',
|
96
|
-
boxShadow: '0 6px 10px rgba(0, 0, 0, 0.15)',
|
97
|
-
alignItems: 'center',
|
98
|
-
padding: '5px',
|
99
|
-
}, children: _jsx("div", { style: {
|
100
|
-
display: "flex",
|
101
|
-
overflow: "hidden",
|
102
|
-
borderRadius: "12px",
|
103
|
-
backgroundColor: "#ffffff",
|
104
|
-
boxShadow: "0 6px 12px rgba(0,0,0,0.1)",
|
105
|
-
width: "100%",
|
106
|
-
height: '100%',
|
107
|
-
}, children: _jsx(ScrollView, { direction: "horizontal", showScrollbar: "always", useNative: Number(SDKUI_Globals.userSettings?.themeSettings.gridSettings.useNativeScrollbar) === 1, style: { whiteSpace: "nowrap", width: "100%" }, children: _jsx("div", { style: { display: "flex", gap: "8px", justifyContent: "center", padding: "5px" }, children: toolbar.items.filter(item => item.visible !== false).map((item, index) => {
|
108
|
-
return _jsxs("div", { style: { display: "flex", flexDirection: "row", alignItems: "center" }, children: [item.beginGroup && index !== 0 && (_jsx("div", { style: { height: '35px', width: '1px', backgroundColor: '#c6c6c6', margin: '0 8px' } })), _jsx("button", { disabled: item.disabled ?? false, style: {
|
109
|
-
display: 'flex',
|
110
|
-
justifyContent: 'center',
|
111
|
-
alignItems: 'center',
|
112
|
-
border: 'none',
|
113
|
-
cursor: 'pointer',
|
114
|
-
transition: 'background-color 0.3s, transform 0.2s',
|
115
|
-
// customizable properties
|
116
|
-
width: toolbar.styles?.items?.width ?? defaultTMPanelManagerToolbarStyles.items.width,
|
117
|
-
height: toolbar.styles?.items?.height ?? defaultTMPanelManagerToolbarStyles.items.height,
|
118
|
-
backgroundColor: item.disabled ? '#cccccc'
|
119
|
-
: hiddenPanelIds.has(item.panelManagerMatrixRowId)
|
120
|
-
? toolbar.styles?.items?.inactiveBackgroundColor ?? defaultTMPanelManagerToolbarStyles.items.inactiveBackgroundColor
|
121
|
-
: toolbar.styles?.items?.activeBackgroundColor ?? defaultTMPanelManagerToolbarStyles.items.activeBackgroundColor,
|
122
|
-
borderRadius: toolbar.styles?.items?.borderRadius ?? defaultTMPanelManagerToolbarStyles.items.borderRadius,
|
123
|
-
marginBottom: toolbar.styles?.items?.marginBottom ?? defaultTMPanelManagerToolbarStyles.items.marginBottom,
|
124
|
-
}, onMouseEnter: (e) => {
|
125
|
-
e.currentTarget.style.transform = 'scale(1.1)';
|
126
|
-
}, onMouseLeave: (e) => {
|
127
|
-
e.currentTarget.style.transform = 'scale(1)';
|
128
|
-
}, onClick: () => togglePanelVisibility(item), children: _jsx(TMTooltip, { content: item.tooltipName, children: typeof item.icon === 'string' ? _jsx("i", { className: `dx-icon dx-icon-${item.icon}`, style: { fontSize: toolbar.styles?.items?.iconFontSize ?? defaultTMPanelManagerToolbarStyles.items.iconFontSize, color: toolbar.styles?.items?.iconColor ?? defaultTMPanelManagerToolbarStyles.items.iconColor } }) : item.icon }) })] }, "TMPanelManagerMatrix-Mobile-Toolbar-Item-" + item.id);
|
129
|
-
}) }) }) }) }, "TMPanelManagerMatrix-Mobile-Toolbar");
|
130
|
-
};
|
131
|
-
export const getDynamicColumnWidth = (col, hiddenPanelIds, allColumns) => {
|
132
|
-
if (!col)
|
133
|
-
return '100%';
|
134
|
-
// If all rows in the column are hidden, return width 0%
|
135
|
-
const allRowsHidden = col.rows.every(row => hiddenPanelIds.has(row.id));
|
136
|
-
if (allRowsHidden)
|
137
|
-
return '100%';
|
138
|
-
// Calculate which columns are visible (at least one row not hidden)
|
139
|
-
const visibleColumns = allColumns.filter(c => c.rows.some(r => !hiddenPanelIds.has(r.id)));
|
140
|
-
const visibleCount = visibleColumns.length;
|
141
|
-
// If all columns are visible, use the original widths
|
142
|
-
if (visibleCount === allColumns.length) {
|
143
|
-
return col.width ?? `${100 / visibleCount}%`;
|
144
|
-
}
|
145
|
-
// If some columns are hidden, redistribute the 100% width among visible columns
|
146
|
-
// proportionally to their original widths
|
147
|
-
// First, sum the original widths (in percent) of the visible columns
|
148
|
-
const totalPercent = visibleColumns.reduce((sum, c) => {
|
149
|
-
const w = c.width ? parseFloat(c.width) : (100 / visibleCount);
|
150
|
-
return sum + w;
|
151
|
-
}, 0);
|
152
|
-
// Calculate the width percentage for this column based on its original width proportion
|
153
|
-
const thisPercent = col.width ? parseFloat(col.width) : (100 / visibleCount);
|
154
|
-
return `${(thisPercent / totalPercent) * 100}%`;
|
155
|
-
};
|