@vaadin/dashboard 24.6.0-alpha1
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/LICENSE +3 -0
- package/README.md +34 -0
- package/package.json +57 -0
- package/src/keyboard-controller.js +107 -0
- package/src/vaadin-dashboard-button.js +45 -0
- package/src/vaadin-dashboard-helpers.js +99 -0
- package/src/vaadin-dashboard-item-mixin.d.ts +20 -0
- package/src/vaadin-dashboard-item-mixin.js +355 -0
- package/src/vaadin-dashboard-layout-mixin.d.ts +28 -0
- package/src/vaadin-dashboard-layout-mixin.js +148 -0
- package/src/vaadin-dashboard-layout.d.ts +56 -0
- package/src/vaadin-dashboard-layout.js +70 -0
- package/src/vaadin-dashboard-section.d.ts +76 -0
- package/src/vaadin-dashboard-section.js +203 -0
- package/src/vaadin-dashboard-styles.js +143 -0
- package/src/vaadin-dashboard-widget.d.ts +101 -0
- package/src/vaadin-dashboard-widget.js +271 -0
- package/src/vaadin-dashboard.d.ts +290 -0
- package/src/vaadin-dashboard.js +489 -0
- package/src/widget-reorder-controller.js +247 -0
- package/src/widget-resize-controller.js +214 -0
- package/theme/lumo/vaadin-dashboard-button-styles.d.ts +2 -0
- package/theme/lumo/vaadin-dashboard-button-styles.js +8 -0
- package/theme/lumo/vaadin-dashboard-button.d.ts +1 -0
- package/theme/lumo/vaadin-dashboard-button.js +1 -0
- package/theme/lumo/vaadin-dashboard-layout-styles.d.ts +1 -0
- package/theme/lumo/vaadin-dashboard-layout-styles.js +11 -0
- package/theme/lumo/vaadin-dashboard-layout.d.ts +2 -0
- package/theme/lumo/vaadin-dashboard-layout.js +2 -0
- package/theme/lumo/vaadin-dashboard-section-styles.d.ts +5 -0
- package/theme/lumo/vaadin-dashboard-section-styles.js +22 -0
- package/theme/lumo/vaadin-dashboard-section.d.ts +3 -0
- package/theme/lumo/vaadin-dashboard-section.js +3 -0
- package/theme/lumo/vaadin-dashboard-styles.d.ts +1 -0
- package/theme/lumo/vaadin-dashboard-styles.js +16 -0
- package/theme/lumo/vaadin-dashboard-widget-styles.d.ts +9 -0
- package/theme/lumo/vaadin-dashboard-widget-styles.js +242 -0
- package/theme/lumo/vaadin-dashboard-widget.d.ts +3 -0
- package/theme/lumo/vaadin-dashboard-widget.js +3 -0
- package/theme/lumo/vaadin-dashboard.d.ts +4 -0
- package/theme/lumo/vaadin-dashboard.js +4 -0
- package/theme/material/vaadin-dashboard-layout.d.ts +1 -0
- package/theme/material/vaadin-dashboard-layout.js +1 -0
- package/theme/material/vaadin-dashboard-section.d.ts +1 -0
- package/theme/material/vaadin-dashboard-section.js +1 -0
- package/theme/material/vaadin-dashboard-widget.d.ts +1 -0
- package/theme/material/vaadin-dashboard-widget.js +1 -0
- package/theme/material/vaadin-dashboard.d.ts +3 -0
- package/theme/material/vaadin-dashboard.js +3 -0
- package/vaadin-dashboard-layout.d.ts +1 -0
- package/vaadin-dashboard-layout.js +3 -0
- package/vaadin-dashboard-section.d.ts +1 -0
- package/vaadin-dashboard-section.js +3 -0
- package/vaadin-dashboard-widget.d.ts +1 -0
- package/vaadin-dashboard-widget.js +3 -0
- package/vaadin-dashboard.d.ts +1 -0
- package/vaadin-dashboard.js +3 -0
- package/web-types.json +260 -0
- package/web-types.lit.json +146 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2000 - 2024 Vaadin Ltd.
|
|
4
|
+
*
|
|
5
|
+
* This program is available under Vaadin Commercial License and Service Terms.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
* See https://vaadin.com/commercial-license-and-service-terms for the full
|
|
9
|
+
* license.
|
|
10
|
+
*/
|
|
11
|
+
import { getElementItem, getItemsArrayOfItem, itemsEqual, WRAPPER_LOCAL_NAME } from './vaadin-dashboard-helpers.js';
|
|
12
|
+
|
|
13
|
+
const REORDER_EVENT_TIMEOUT = 200;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A controller to widget reordering inside a dashboard.
|
|
17
|
+
*/
|
|
18
|
+
export class WidgetReorderController {
|
|
19
|
+
constructor(host) {
|
|
20
|
+
this.host = host;
|
|
21
|
+
this.draggedElementRemoveObserver = new MutationObserver(() => this.__restoreDraggedElement());
|
|
22
|
+
|
|
23
|
+
host.addEventListener('dragstart', (e) => this.__dragStart(e));
|
|
24
|
+
host.addEventListener('dragend', (e) => this.__dragEnd(e));
|
|
25
|
+
host.addEventListener('dragover', (e) => this.__dragOver(e));
|
|
26
|
+
host.addEventListener('drop', (e) => this.__drop(e));
|
|
27
|
+
host.addEventListener('item-move', (e) => this.__itemMove(e));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** @private */
|
|
31
|
+
__dragStart(e) {
|
|
32
|
+
const handle = [...e.composedPath()].find((el) => el.classList && el.classList.contains('drag-handle'));
|
|
33
|
+
if (!handle) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this.__draggedElement = e.target;
|
|
38
|
+
|
|
39
|
+
// Avoid having the selection/focus outline styles in the drag image
|
|
40
|
+
this.__draggedElement.__exitMode();
|
|
41
|
+
this.__draggedElement.__focused = false;
|
|
42
|
+
this.__draggedElement.__selected = false;
|
|
43
|
+
|
|
44
|
+
this.draggedItem = getElementItem(this.__draggedElement);
|
|
45
|
+
|
|
46
|
+
// Set the drag image to the dragged element
|
|
47
|
+
const { left, top } = this.__draggedElement.getBoundingClientRect();
|
|
48
|
+
e.dataTransfer.setDragImage(this.__draggedElement, e.clientX - left, e.clientY - top);
|
|
49
|
+
// Set the text/plain data to enable dragging on mobile devices
|
|
50
|
+
e.dataTransfer.setData('text/plain', 'item');
|
|
51
|
+
|
|
52
|
+
// Observe the removal of the dragged element from the DOM
|
|
53
|
+
this.draggedElementRemoveObserver.observe(this.host, { childList: true, subtree: true });
|
|
54
|
+
|
|
55
|
+
requestAnimationFrame(() => {
|
|
56
|
+
// Re-render to have the dragged element turn into a placeholder
|
|
57
|
+
this.host.items = [...this.host.items];
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** @private */
|
|
62
|
+
__dragOver(e) {
|
|
63
|
+
if (!this.draggedItem) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
e.dataTransfer.dropEffect = 'move';
|
|
69
|
+
|
|
70
|
+
// Get all elements that are candidates for reordering with the dragged element
|
|
71
|
+
const dragContextElements = this.__getDragContextElements(this.__draggedElement);
|
|
72
|
+
// Find the up-to-date element instance representing the dragged item
|
|
73
|
+
const draggedElement = dragContextElements.find((element) => getElementItem(element) === this.draggedItem);
|
|
74
|
+
if (!draggedElement) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// Get all elements except the dragged element from the drag context
|
|
78
|
+
const otherElements = dragContextElements.filter((element) => element !== draggedElement);
|
|
79
|
+
if (otherElements.length === 0) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Find the element that is being dragged over
|
|
84
|
+
let targetElement = otherElements.find((other) => other.contains(e.target));
|
|
85
|
+
if (!targetElement) {
|
|
86
|
+
// Find the element closest to the x and y coordinates of the drag event
|
|
87
|
+
targetElement = this.__getClosestElement(otherElements, e.clientX, e.clientY);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check if the dragged element is dragged enough over the target element
|
|
91
|
+
if (!this.__reordering && this.__isDraggedOver(draggedElement, targetElement, e.clientX, e.clientY)) {
|
|
92
|
+
// Prevent reordering multiple times in quick succession
|
|
93
|
+
this.__reordering = true;
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
this.__reordering = false;
|
|
96
|
+
}, REORDER_EVENT_TIMEOUT);
|
|
97
|
+
|
|
98
|
+
const targetItem = getElementItem(targetElement);
|
|
99
|
+
const targetItems = getItemsArrayOfItem(targetItem, this.host.items);
|
|
100
|
+
const targetIndex = targetItems.indexOf(targetItem);
|
|
101
|
+
|
|
102
|
+
this.__reorderItems(this.draggedItem, targetIndex);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** @private */
|
|
107
|
+
__dragEnd() {
|
|
108
|
+
if (!this.draggedItem) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// If the originally dragged element is restored to the DOM (as a direct child of the host),
|
|
113
|
+
// to make sure "dragend" event gets dispatched, remove it to avoid duplicates
|
|
114
|
+
if (this.__draggedElement.parentElement === this.host) {
|
|
115
|
+
this.__draggedElement.remove();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Dispatch the moved event
|
|
119
|
+
this.__fireItemMovedEvent(this.draggedItem);
|
|
120
|
+
|
|
121
|
+
// Reset the dragged element and item, and re-render to remove the placeholder
|
|
122
|
+
this.__draggedElement = null;
|
|
123
|
+
this.draggedItem = null;
|
|
124
|
+
this.host.items = [...this.host.items];
|
|
125
|
+
|
|
126
|
+
// Disconnect the observer for the dragged element removal
|
|
127
|
+
this.draggedElementRemoveObserver.disconnect();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** @private */
|
|
131
|
+
__fireItemMovedEvent(item) {
|
|
132
|
+
const section = this.host.items.find((hostItem) => hostItem.items && hostItem.items.includes(item));
|
|
133
|
+
this.host.dispatchEvent(
|
|
134
|
+
new CustomEvent('dashboard-item-moved', {
|
|
135
|
+
detail: { item, items: this.host.items, section },
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** @private */
|
|
141
|
+
__drop(e) {
|
|
142
|
+
if (!this.draggedItem) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
e.preventDefault();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Returns the element closest to the given coordinates.
|
|
150
|
+
* @private
|
|
151
|
+
*/
|
|
152
|
+
__getClosestElement(elements, x, y) {
|
|
153
|
+
return elements.reduce(
|
|
154
|
+
(closest, element) => {
|
|
155
|
+
const { left, top, width, height } = element.getBoundingClientRect();
|
|
156
|
+
const centerX = left + width / 2;
|
|
157
|
+
const centerY = top + height / 2;
|
|
158
|
+
const distance = Math.hypot(centerX - x, centerY - y);
|
|
159
|
+
|
|
160
|
+
return distance < closest.distance ? { element, distance } : closest;
|
|
161
|
+
},
|
|
162
|
+
{ element: null, distance: Number.MAX_VALUE },
|
|
163
|
+
).element;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Returns true if the dragged element is dragged enough over the target element in
|
|
168
|
+
* the direction relative to their positions where x and y are the coordinates
|
|
169
|
+
* of the drag event.
|
|
170
|
+
* @private
|
|
171
|
+
*/
|
|
172
|
+
__isDraggedOver(draggedElement, targetElement, x, y) {
|
|
173
|
+
const draggedPos = draggedElement.getBoundingClientRect();
|
|
174
|
+
const targetPos = targetElement.getBoundingClientRect();
|
|
175
|
+
if (draggedPos.top >= targetPos.bottom) {
|
|
176
|
+
// target is on a row above the dragged widget
|
|
177
|
+
return y < targetPos.top + targetPos.height / 2;
|
|
178
|
+
} else if (draggedPos.bottom <= targetPos.top) {
|
|
179
|
+
// target is on a row below the dragged widget
|
|
180
|
+
return y > targetPos.top + targetPos.height / 2;
|
|
181
|
+
} else if (draggedPos.left >= targetPos.right) {
|
|
182
|
+
// target is on a column to the left of the dragged widget
|
|
183
|
+
return x < targetPos.left + targetPos.width / 2;
|
|
184
|
+
} else if (draggedPos.right <= targetPos.left) {
|
|
185
|
+
// target is on a column to the right of the dragged widget
|
|
186
|
+
return x > targetPos.left + targetPos.width / 2;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Returns the elements (widgets or sections) that are candidates for reordering with the
|
|
192
|
+
* currently dragged item. Effectively, this is the list of child widgets or sections inside
|
|
193
|
+
* the same parent container (host or a section) as the dragged item.
|
|
194
|
+
* @private
|
|
195
|
+
*/
|
|
196
|
+
__getDragContextElements() {
|
|
197
|
+
// Find the wrapper element representing the dragged item
|
|
198
|
+
const draggedItemWrapper = [...this.host.querySelectorAll(WRAPPER_LOCAL_NAME)].find((el) =>
|
|
199
|
+
itemsEqual(el.__item, this.draggedItem),
|
|
200
|
+
);
|
|
201
|
+
if (!draggedItemWrapper) {
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
204
|
+
// Find all child wrappers in the same parent container as the dragged item's wrapper and
|
|
205
|
+
// return their first children (the actual widgets or sections)
|
|
206
|
+
return [...draggedItemWrapper.parentElement.children]
|
|
207
|
+
.filter((el) => el.localName === WRAPPER_LOCAL_NAME && el.firstElementChild)
|
|
208
|
+
.map((el) => el.firstElementChild);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** @private */
|
|
212
|
+
__reorderItems(draggedItem, targetIndex) {
|
|
213
|
+
if (targetIndex < 0 || targetIndex >= getItemsArrayOfItem(draggedItem, this.host.items).length) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const items = getItemsArrayOfItem(draggedItem, this.host.items);
|
|
217
|
+
const draggedIndex = items.indexOf(draggedItem);
|
|
218
|
+
items.splice(draggedIndex, 1);
|
|
219
|
+
items.splice(targetIndex, 0, draggedItem);
|
|
220
|
+
this.host.items = [...this.host.items];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* The dragged element might be removed from the DOM during the drag operation if
|
|
225
|
+
* the widgets get re-rendered. This method restores the dragged element if it's not
|
|
226
|
+
* present in the DOM to ensure the dragend event is fired.
|
|
227
|
+
* @private
|
|
228
|
+
*/
|
|
229
|
+
__restoreDraggedElement() {
|
|
230
|
+
if (!this.host.contains(this.__draggedElement)) {
|
|
231
|
+
this.__draggedElement.style.display = 'none';
|
|
232
|
+
this.host.appendChild(this.__draggedElement);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Handle the item-move event dispatched by a widget / section.
|
|
238
|
+
* @private
|
|
239
|
+
*/
|
|
240
|
+
__itemMove(e) {
|
|
241
|
+
e.stopImmediatePropagation();
|
|
242
|
+
const item = getElementItem(e.target);
|
|
243
|
+
const items = getItemsArrayOfItem(item, this.host.items);
|
|
244
|
+
this.__reorderItems(item, items.indexOf(item) + e.detail.delta);
|
|
245
|
+
this.__fireItemMovedEvent(item);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2000 - 2024 Vaadin Ltd.
|
|
4
|
+
*
|
|
5
|
+
* This program is available under Vaadin Commercial License and Service Terms.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
* See https://vaadin.com/commercial-license-and-service-terms for the full
|
|
9
|
+
* license.
|
|
10
|
+
*/
|
|
11
|
+
import { addListener } from '@vaadin/component-base/src/gestures.js';
|
|
12
|
+
import { getElementItem, itemsEqual, WRAPPER_LOCAL_NAME } from './vaadin-dashboard-helpers.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A controller to widget resizing inside a dashboard.
|
|
16
|
+
*/
|
|
17
|
+
export class WidgetResizeController {
|
|
18
|
+
constructor(host) {
|
|
19
|
+
this.host = host;
|
|
20
|
+
this.__resizedElementRemoveObserver = new MutationObserver(() => this.__restoreResizedElement());
|
|
21
|
+
this.__touchMoveCancelListener = (e) => e.preventDefault();
|
|
22
|
+
addListener(host, 'track', (e) => this.__onTrack(e));
|
|
23
|
+
host.addEventListener('item-resize', (e) => this.__itemResize(e));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** @private */
|
|
27
|
+
__onTrack(e) {
|
|
28
|
+
if (e.detail.state === 'start') {
|
|
29
|
+
this.__onResizeStart(e);
|
|
30
|
+
} else if (e.detail.state === 'track') {
|
|
31
|
+
this.__onResize(e);
|
|
32
|
+
} else if (e.detail.state === 'end') {
|
|
33
|
+
this.__onResizeEnd(e);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** @private */
|
|
38
|
+
__onResizeStart(e) {
|
|
39
|
+
const handle = [...e.composedPath()].find((el) => el.classList && el.classList.contains('resize-handle'));
|
|
40
|
+
if (!handle) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.host.$.grid.toggleAttribute('item-resizing', true);
|
|
45
|
+
this.resizedItem = getElementItem(e.target);
|
|
46
|
+
|
|
47
|
+
this.__resizeStartWidth = e.target.offsetWidth;
|
|
48
|
+
this.__resizeStartHeight = e.target.offsetHeight;
|
|
49
|
+
this.__resizeWidth = this.__resizeStartWidth + e.detail.dx;
|
|
50
|
+
this.__resizeHeight = this.__resizeStartHeight + e.detail.dy;
|
|
51
|
+
this.__updateWidgetStyles();
|
|
52
|
+
|
|
53
|
+
this.__resizedElement = e.target;
|
|
54
|
+
// Observe the removal of the resized element from the DOM
|
|
55
|
+
this.__resizedElementRemoveObserver.observe(this.host, { childList: true, subtree: true });
|
|
56
|
+
|
|
57
|
+
// Prevent scrolling on touch devices while resizing
|
|
58
|
+
document.addEventListener('touchmove', this.__touchMoveCancelListener, { passive: false });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** @private */
|
|
62
|
+
__onResize(e) {
|
|
63
|
+
if (!this.resizedItem) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this.__resizeWidth = this.__resizeStartWidth + (document.dir === 'rtl' ? -e.detail.dx : e.detail.dx);
|
|
68
|
+
this.__resizeHeight = this.__resizeStartHeight + e.detail.dy;
|
|
69
|
+
this.__updateWidgetStyles();
|
|
70
|
+
|
|
71
|
+
const itemWrapper = this.__getItemWrapper(this.resizedItem);
|
|
72
|
+
if (!itemWrapper.firstElementChild) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const gridStyle = getComputedStyle(this.host.$.grid);
|
|
77
|
+
const gapSize = parseFloat(gridStyle.gap || 0);
|
|
78
|
+
|
|
79
|
+
const currentElementWidth = itemWrapper.firstElementChild.offsetWidth;
|
|
80
|
+
const columns = gridStyle.gridTemplateColumns.split(' ');
|
|
81
|
+
const columnWidth = parseFloat(columns[0]);
|
|
82
|
+
if (this.__resizeWidth > currentElementWidth + gapSize + columnWidth / 2) {
|
|
83
|
+
// Resized horizontally above the half of the next column, increase colspan
|
|
84
|
+
this.__updateResizedItem(1, 0);
|
|
85
|
+
} else if (this.__resizeWidth < currentElementWidth - columnWidth / 2) {
|
|
86
|
+
// Resized horizontally below the half of the current column, decrease colspan
|
|
87
|
+
this.__updateResizedItem(-1, 0);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const currentElementHeight = itemWrapper.firstElementChild.offsetHeight;
|
|
91
|
+
const rowMinHeight = Math.min(...gridStyle.gridTemplateRows.split(' ').map((height) => parseFloat(height)));
|
|
92
|
+
if (this.__resizeHeight > currentElementHeight + gapSize + rowMinHeight / 2) {
|
|
93
|
+
// Resized vertically above the half of the next row, increase rowspan
|
|
94
|
+
this.__updateResizedItem(0, 1);
|
|
95
|
+
} else if (this.__resizeHeight < currentElementHeight - rowMinHeight / 2) {
|
|
96
|
+
// Resized vertically below the half of the current row, decrease rowspan
|
|
97
|
+
this.__updateResizedItem(0, -1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** @private */
|
|
102
|
+
__onResizeEnd() {
|
|
103
|
+
if (!this.resizedItem) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// If the originally resized element is restored to the DOM (as a direct child of the host),
|
|
108
|
+
// to make sure "track" event gets dispatched, remove it to avoid duplicates
|
|
109
|
+
if (this.__resizedElement.parentElement === this.host) {
|
|
110
|
+
this.__resizedElement.remove();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const itemWrapper = this.__getItemWrapper(this.resizedItem);
|
|
114
|
+
itemWrapper.style.removeProperty('--_vaadin-dashboard-widget-resizer-width');
|
|
115
|
+
itemWrapper.style.removeProperty('--_vaadin-dashboard-widget-resizer-height');
|
|
116
|
+
if (itemWrapper.firstElementChild) {
|
|
117
|
+
itemWrapper.firstElementChild.toggleAttribute('resizing', false);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.host.$.grid.toggleAttribute('item-resizing', false);
|
|
121
|
+
|
|
122
|
+
// Disconnect the observer for the resized element removal
|
|
123
|
+
this.__resizedElementRemoveObserver.disconnect();
|
|
124
|
+
// Cleanup the touchmove listener
|
|
125
|
+
document.removeEventListener('touchmove', this.__touchMoveCancelListener);
|
|
126
|
+
|
|
127
|
+
// Dispatch the resize end event
|
|
128
|
+
this.__fireItemResizedEvent(this.resizedItem);
|
|
129
|
+
this.resizedItem = null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** @private */
|
|
133
|
+
__fireItemResizedEvent(item) {
|
|
134
|
+
this.host.dispatchEvent(
|
|
135
|
+
new CustomEvent('dashboard-item-resized', {
|
|
136
|
+
detail: { item, items: this.host.items },
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** @private */
|
|
142
|
+
__getItemWrapper(item) {
|
|
143
|
+
return [...this.host.querySelectorAll(WRAPPER_LOCAL_NAME)].find((el) => itemsEqual(el.__item, item));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** @private */
|
|
147
|
+
__updateResizedItem(colspanDelta, rowspanDelta) {
|
|
148
|
+
this.__resizeItem(this.resizedItem, colspanDelta, rowspanDelta);
|
|
149
|
+
requestAnimationFrame(() => this.__updateWidgetStyles());
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** @private */
|
|
153
|
+
__resizeItem(item, colspanDelta, rowspanDelta) {
|
|
154
|
+
if (item.items) {
|
|
155
|
+
// Do not resize sections
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const gridStyle = getComputedStyle(this.host.$.grid);
|
|
160
|
+
if (rowspanDelta && !gridStyle.getPropertyValue('--vaadin-dashboard-row-min-height')) {
|
|
161
|
+
// Do not resize vertically if the min row height is not set
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const columns = gridStyle.gridTemplateColumns.split(' ');
|
|
166
|
+
const currentColspan = item.colspan || 1;
|
|
167
|
+
const currentRowspan = item.rowspan || 1;
|
|
168
|
+
|
|
169
|
+
const newColspan = Math.min(Math.max(currentColspan + colspanDelta, 1), columns.length);
|
|
170
|
+
const newRowspan = Math.max(currentRowspan + rowspanDelta, 1);
|
|
171
|
+
|
|
172
|
+
if ((item.colspan || 1) === newColspan && (item.rowspan || 1) === newRowspan) {
|
|
173
|
+
// No change in size
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
item.colspan = newColspan;
|
|
178
|
+
item.rowspan = newRowspan;
|
|
179
|
+
this.host.items = [...this.host.items];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** @private */
|
|
183
|
+
__updateWidgetStyles() {
|
|
184
|
+
const itemWrapper = this.__getItemWrapper(this.resizedItem);
|
|
185
|
+
itemWrapper.style.setProperty('--_vaadin-dashboard-widget-resizer-width', `${this.__resizeWidth}px`);
|
|
186
|
+
itemWrapper.style.setProperty('--_vaadin-dashboard-widget-resizer-height', `${this.__resizeHeight}px`);
|
|
187
|
+
if (itemWrapper.firstElementChild) {
|
|
188
|
+
itemWrapper.firstElementChild.toggleAttribute('resizing', true);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** @private */
|
|
193
|
+
__restoreResizedElement() {
|
|
194
|
+
if (!this.host.contains(this.__resizedElement)) {
|
|
195
|
+
this.__resizedElement.style.display = 'none';
|
|
196
|
+
this.host.appendChild(this.__resizedElement);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Handle the item-resize event dispatched by a widget / section.
|
|
202
|
+
* @private
|
|
203
|
+
*/
|
|
204
|
+
__itemResize(e) {
|
|
205
|
+
e.stopImmediatePropagation();
|
|
206
|
+
const item = getElementItem(e.target);
|
|
207
|
+
this.__resizeItem(item, e.detail.colspanDelta, e.detail.rowspanDelta);
|
|
208
|
+
this.__fireItemResizedEvent(item);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
hostDisconnected() {
|
|
212
|
+
document.removeEventListener('touchmove', this.__touchMoveCancelListener);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './vaadin-dashboard-button-styles.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './vaadin-dashboard-button-styles.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const dashboardLayoutStyles: import("lit").CSSResult;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
2
|
+
|
|
3
|
+
export const dashboardLayoutStyles = css`
|
|
4
|
+
#grid {
|
|
5
|
+
--_vaadin-dashboard-default-spacing: var(--lumo-space-xl);
|
|
6
|
+
}
|
|
7
|
+
`;
|
|
8
|
+
|
|
9
|
+
registerStyles('vaadin-dashboard-layout', [dashboardLayoutStyles], {
|
|
10
|
+
moduleId: 'lumo-dashboard-layout',
|
|
11
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import '@vaadin/vaadin-lumo-styles/color.js';
|
|
2
|
+
import '@vaadin/vaadin-lumo-styles/sizing.js';
|
|
3
|
+
import '@vaadin/vaadin-lumo-styles/spacing.js';
|
|
4
|
+
import '@vaadin/vaadin-lumo-styles/style.js';
|
|
5
|
+
import '@vaadin/vaadin-lumo-styles/typography.js';
|
|
6
|
+
import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
7
|
+
import { dashboardWidgetAndSection } from './vaadin-dashboard-widget-styles.js';
|
|
8
|
+
|
|
9
|
+
const section = css`
|
|
10
|
+
:host {
|
|
11
|
+
--_focus-ring-spacing-max-offset: calc(var(--_vaadin-dashboard-spacing) / 2);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
:host([move-mode]) ::slotted(*) {
|
|
15
|
+
--_vaadin-dashboard-widget-opacity: 0.3;
|
|
16
|
+
--_vaadin-dashboard-widget-filter: blur(10px);
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
registerStyles('vaadin-dashboard-section', [dashboardWidgetAndSection, section], {
|
|
21
|
+
moduleId: 'lumo-dashboard-section',
|
|
22
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
2
|
+
import { dashboardLayoutStyles } from './vaadin-dashboard-layout-styles.js';
|
|
3
|
+
|
|
4
|
+
const dashboard = css`
|
|
5
|
+
:host {
|
|
6
|
+
--_vaadin-dashboard-widget-opacity: 1;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
:host([item-selected]) {
|
|
10
|
+
--_vaadin-dashboard-widget-opacity: 0.7;
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
registerStyles('vaadin-dashboard', [dashboardLayoutStyles, dashboard], {
|
|
15
|
+
moduleId: 'lumo-dashboard',
|
|
16
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import '@vaadin/vaadin-lumo-styles/color.js';
|
|
2
|
+
import '@vaadin/vaadin-lumo-styles/sizing.js';
|
|
3
|
+
import '@vaadin/vaadin-lumo-styles/spacing.js';
|
|
4
|
+
import '@vaadin/vaadin-lumo-styles/style.js';
|
|
5
|
+
import '@vaadin/vaadin-lumo-styles/typography.js';
|
|
6
|
+
import '@vaadin/vaadin-lumo-styles/font-icons.js';
|
|
7
|
+
declare const dashboardWidgetAndSection: import("lit").CSSResult;
|
|
8
|
+
declare const dashboardWidget: import("lit").CSSResult;
|
|
9
|
+
export { dashboardWidget, dashboardWidgetAndSection };
|