@vaadin/component-base 24.2.0-alpha4 → 24.2.0-alpha6
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/package.json +3 -3
- package/src/controller-mixin.js +55 -50
- package/src/dom-utils.d.ts +7 -0
- package/src/dom-utils.js +21 -0
- package/src/element-mixin.js +1 -1
- package/src/overflow-controller.js +23 -13
- package/src/polylit-mixin.js +1 -1
- package/src/slot-controller.js +6 -6
- package/src/slot-observer.d.ts +34 -0
- package/src/slot-observer.js +105 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/component-base",
|
|
3
|
-
"version": "24.2.0-
|
|
3
|
+
"version": "24.2.0-alpha6",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@esm-bundle/chai": "^4.3.4",
|
|
42
|
-
"@vaadin/testing-helpers": "^0.4.
|
|
42
|
+
"@vaadin/testing-helpers": "^0.4.3",
|
|
43
43
|
"sinon": "^13.0.2"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "3ef6e6cd66919b3ef7637e42916e4c54656beb51"
|
|
46
46
|
}
|
package/src/controller-mixin.js
CHANGED
|
@@ -15,62 +15,67 @@ import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
|
|
|
15
15
|
*
|
|
16
16
|
* @polymerMixin
|
|
17
17
|
*/
|
|
18
|
-
export const ControllerMixin = dedupingMixin(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
export const ControllerMixin = dedupingMixin((superClass) => {
|
|
19
|
+
// If the superclass extends from LitElement,
|
|
20
|
+
// use its own controllers implementation.
|
|
21
|
+
if (typeof superClass.prototype.addController === 'function') {
|
|
22
|
+
return superClass;
|
|
23
|
+
}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.__controllers = new Set();
|
|
28
|
-
}
|
|
25
|
+
return class ControllerMixinClass extends superClass {
|
|
26
|
+
constructor() {
|
|
27
|
+
super();
|
|
29
28
|
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
/**
|
|
30
|
+
* @type {Set<ReactiveController>}
|
|
31
|
+
*/
|
|
32
|
+
this.__controllers = new Set();
|
|
33
|
+
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
}
|
|
35
|
+
/** @protected */
|
|
36
|
+
connectedCallback() {
|
|
37
|
+
super.connectedCallback();
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
this.__controllers.forEach((c) => {
|
|
40
|
+
if (c.hostConnected) {
|
|
41
|
+
c.hostConnected();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
}
|
|
46
|
+
/** @protected */
|
|
47
|
+
disconnectedCallback() {
|
|
48
|
+
super.disconnectedCallback();
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
* @param {ReactiveController} controller
|
|
56
|
-
* @protected
|
|
57
|
-
*/
|
|
58
|
-
addController(controller) {
|
|
59
|
-
this.__controllers.add(controller);
|
|
60
|
-
// Call hostConnected if a controller is added after the element is attached.
|
|
61
|
-
if (this.$ !== undefined && this.isConnected && controller.hostConnected) {
|
|
62
|
-
controller.hostConnected();
|
|
50
|
+
this.__controllers.forEach((c) => {
|
|
51
|
+
if (c.hostDisconnected) {
|
|
52
|
+
c.hostDisconnected();
|
|
63
53
|
}
|
|
64
|
-
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
65
56
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Registers a controller to participate in the element update cycle.
|
|
59
|
+
*
|
|
60
|
+
* @param {ReactiveController} controller
|
|
61
|
+
* @protected
|
|
62
|
+
*/
|
|
63
|
+
addController(controller) {
|
|
64
|
+
this.__controllers.add(controller);
|
|
65
|
+
// Call hostConnected if a controller is added after the element is attached.
|
|
66
|
+
if (this.$ !== undefined && this.isConnected && controller.hostConnected) {
|
|
67
|
+
controller.hostConnected();
|
|
74
68
|
}
|
|
75
|
-
}
|
|
76
|
-
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Removes a controller from the element.
|
|
73
|
+
*
|
|
74
|
+
* @param {ReactiveController} controller
|
|
75
|
+
* @protected
|
|
76
|
+
*/
|
|
77
|
+
removeController(controller) {
|
|
78
|
+
this.__controllers.delete(controller);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
});
|
package/src/dom-utils.d.ts
CHANGED
|
@@ -13,6 +13,13 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export function getAncestorRootNodes(node: Node): Node[];
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Returns the list of flattened elements for the given `node`.
|
|
18
|
+
* This list consists of a node's children and, for any children that are
|
|
19
|
+
* `<slot>` elements, the expanded flattened list of `assignedElements`.
|
|
20
|
+
*/
|
|
21
|
+
export function getFlattenedElements(node: Node): Element[];
|
|
22
|
+
|
|
16
23
|
/**
|
|
17
24
|
* Traverses the given node and its parents, including those that are across
|
|
18
25
|
* the shadow root boundaries, until it finds a node that matches the selector.
|
package/src/dom-utils.js
CHANGED
|
@@ -40,6 +40,27 @@ export function getAncestorRootNodes(node) {
|
|
|
40
40
|
return result;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Returns the list of flattened elements for the given `node`.
|
|
45
|
+
* This list consists of a node's children and, for any children that are
|
|
46
|
+
* `<slot>` elements, the expanded flattened list of `assignedElements`.
|
|
47
|
+
*
|
|
48
|
+
* @param {Node} node
|
|
49
|
+
* @return {Element[]}
|
|
50
|
+
*/
|
|
51
|
+
export function getFlattenedElements(node) {
|
|
52
|
+
const result = [];
|
|
53
|
+
let elements;
|
|
54
|
+
if (node.localName === 'slot') {
|
|
55
|
+
elements = node.assignedElements();
|
|
56
|
+
} else {
|
|
57
|
+
result.push(node);
|
|
58
|
+
elements = [...node.children];
|
|
59
|
+
}
|
|
60
|
+
elements.forEach((elem) => result.push(...getFlattenedElements(elem)));
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
43
64
|
/**
|
|
44
65
|
* Traverses the given node and its parents, including those that are across
|
|
45
66
|
* the shadow root boundaries, until it finds a node that matches the selector.
|
package/src/element-mixin.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';
|
|
7
6
|
import { animationFrame } from './async.js';
|
|
8
7
|
import { Debouncer } from './debounce.js';
|
|
9
8
|
|
|
@@ -47,30 +46,41 @@ export class OverflowController {
|
|
|
47
46
|
* @protected
|
|
48
47
|
*/
|
|
49
48
|
observe() {
|
|
50
|
-
|
|
49
|
+
const { host } = this;
|
|
50
|
+
|
|
51
|
+
this.__resizeObserver = new ResizeObserver((entries) => {
|
|
51
52
|
this.__debounceOverflow = Debouncer.debounce(this.__debounceOverflow, animationFrame, () => {
|
|
52
53
|
this.__updateOverflow();
|
|
53
54
|
});
|
|
54
55
|
});
|
|
55
56
|
|
|
56
|
-
this.__resizeObserver.observe(
|
|
57
|
+
this.__resizeObserver.observe(host);
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
});
|
|
59
|
+
// Observe initial children
|
|
60
|
+
[...host.children].forEach((child) => {
|
|
61
|
+
this.__resizeObserver.observe(child);
|
|
62
|
+
});
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
this.__childObserver = new MutationObserver((mutations) => {
|
|
65
|
+
mutations.forEach(({ addedNodes, removedNodes }) => {
|
|
66
|
+
addedNodes.forEach((node) => {
|
|
67
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
68
|
+
this.__resizeObserver.observe(node);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
removedNodes.forEach((node) => {
|
|
73
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
74
|
+
this.__resizeObserver.unobserve(node);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
69
77
|
});
|
|
70
78
|
|
|
71
79
|
this.__updateOverflow();
|
|
72
80
|
});
|
|
73
81
|
|
|
82
|
+
this.__childObserver.observe(host, { childList: true });
|
|
83
|
+
|
|
74
84
|
// Update overflow attribute on scroll
|
|
75
85
|
this.scrollTarget.addEventListener('scroll', this.__boundOnScroll);
|
|
76
86
|
|
package/src/polylit-mixin.js
CHANGED
package/src/slot-controller.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';
|
|
7
6
|
import { isEmptyTextNode } from './dom-utils.js';
|
|
7
|
+
import { SlotObserver } from './slot-observer.js';
|
|
8
8
|
import { generateUniqueId } from './unique-id-utils.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -199,17 +199,17 @@ export class SlotController extends EventTarget {
|
|
|
199
199
|
const selector = slotName === '' ? 'slot:not([name])' : `slot[name=${slotName}]`;
|
|
200
200
|
const slot = this.host.shadowRoot.querySelector(selector);
|
|
201
201
|
|
|
202
|
-
this.__slotObserver = new
|
|
202
|
+
this.__slotObserver = new SlotObserver(slot, ({ addedNodes, removedNodes }) => {
|
|
203
203
|
const current = this.multiple ? this.nodes : [this.node];
|
|
204
204
|
|
|
205
205
|
// Calling `slot.assignedNodes()` includes whitespace text nodes in case of default slot:
|
|
206
206
|
// unlike comment nodes, they are not filtered out. So we need to manually ignore them.
|
|
207
|
-
const newNodes =
|
|
207
|
+
const newNodes = addedNodes.filter((node) => !isEmptyTextNode(node) && !current.includes(node));
|
|
208
208
|
|
|
209
|
-
if (
|
|
210
|
-
this.nodes = current.filter((node) => !
|
|
209
|
+
if (removedNodes.length) {
|
|
210
|
+
this.nodes = current.filter((node) => !removedNodes.includes(node));
|
|
211
211
|
|
|
212
|
-
|
|
212
|
+
removedNodes.forEach((node) => {
|
|
213
213
|
this.teardownNode(node);
|
|
214
214
|
});
|
|
215
215
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A helper for observing slot changes.
|
|
9
|
+
*/
|
|
10
|
+
export class SlotObserver {
|
|
11
|
+
constructor(
|
|
12
|
+
slot: HTMLSlotElement,
|
|
13
|
+
callback: (info: { addedNodes: Node[]; movedNodes: Node[]; removedNodes: Node[] }) => void,
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Activates an observer. This method is automatically called when
|
|
18
|
+
* a `SlotObserver` is created. It should only be called to re-activate
|
|
19
|
+
* an observer that has been deactivated via the `disconnect` method.
|
|
20
|
+
*/
|
|
21
|
+
connect(): void;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Deactivates the observer. After calling this method the observer callback
|
|
25
|
+
* will not be called when changes to slotted nodes occur. The `connect` method
|
|
26
|
+
* may be subsequently called to reactivate the observer.
|
|
27
|
+
*/
|
|
28
|
+
disconnect(): void;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Run the observer callback synchronously.
|
|
32
|
+
*/
|
|
33
|
+
flush(): void;
|
|
34
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A helper for observing slot changes.
|
|
9
|
+
*/
|
|
10
|
+
export class SlotObserver {
|
|
11
|
+
constructor(slot, callback) {
|
|
12
|
+
/** @type HTMLSlotElement */
|
|
13
|
+
this.slot = slot;
|
|
14
|
+
|
|
15
|
+
/** @type Function */
|
|
16
|
+
this.callback = callback;
|
|
17
|
+
|
|
18
|
+
/** @type {Node[]} */
|
|
19
|
+
this._storedNodes = [];
|
|
20
|
+
|
|
21
|
+
this._connected = false;
|
|
22
|
+
this._scheduled = false;
|
|
23
|
+
|
|
24
|
+
this._boundSchedule = () => {
|
|
25
|
+
this._schedule();
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
this.connect();
|
|
29
|
+
this._schedule();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Activates an observer. This method is automatically called when
|
|
34
|
+
* a `SlotObserver` is created. It should only be called to re-activate
|
|
35
|
+
* an observer that has been deactivated via the `disconnect` method.
|
|
36
|
+
*/
|
|
37
|
+
connect() {
|
|
38
|
+
this.slot.addEventListener('slotchange', this._boundSchedule);
|
|
39
|
+
this._connected = true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Deactivates the observer. After calling this method the observer callback
|
|
44
|
+
* will not be called when changes to slotted nodes occur. The `connect` method
|
|
45
|
+
* may be subsequently called to reactivate the observer.
|
|
46
|
+
*/
|
|
47
|
+
disconnect() {
|
|
48
|
+
this.slot.removeEventListener('slotchange', this._boundSchedule);
|
|
49
|
+
this._connected = false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** @private */
|
|
53
|
+
_schedule() {
|
|
54
|
+
if (!this._scheduled) {
|
|
55
|
+
this._scheduled = true;
|
|
56
|
+
|
|
57
|
+
queueMicrotask(() => {
|
|
58
|
+
this.flush();
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Run the observer callback synchronously.
|
|
65
|
+
*/
|
|
66
|
+
flush() {
|
|
67
|
+
if (!this._connected) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this._scheduled = false;
|
|
72
|
+
|
|
73
|
+
this._processNodes();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** @private */
|
|
77
|
+
_processNodes() {
|
|
78
|
+
const currentNodes = this.slot.assignedNodes({ flatten: true });
|
|
79
|
+
|
|
80
|
+
let addedNodes = [];
|
|
81
|
+
const removedNodes = [];
|
|
82
|
+
const movedNodes = [];
|
|
83
|
+
|
|
84
|
+
if (currentNodes.length) {
|
|
85
|
+
addedNodes = currentNodes.filter((node) => !this._storedNodes.includes(node));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (this._storedNodes.length) {
|
|
89
|
+
this._storedNodes.forEach((node, index) => {
|
|
90
|
+
const idx = currentNodes.indexOf(node);
|
|
91
|
+
if (idx === -1) {
|
|
92
|
+
removedNodes.push(node);
|
|
93
|
+
} else if (idx !== index) {
|
|
94
|
+
movedNodes.push(node);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (addedNodes.length || removedNodes.length || movedNodes.length) {
|
|
100
|
+
this.callback({ addedNodes, movedNodes, removedNodes });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
this._storedNodes = currentNodes;
|
|
104
|
+
}
|
|
105
|
+
}
|