@vaadin/component-base 22.0.1 → 22.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +2 -2
- package/src/element-mixin.js +1 -1
- package/src/slot-controller.d.ts +51 -0
- package/src/slot-controller.js +122 -0
package/index.d.ts
CHANGED
|
@@ -5,5 +5,6 @@ export { DisabledMixin } from './src/disabled-mixin.js';
|
|
|
5
5
|
export { ElementMixin } from './src/element-mixin.js';
|
|
6
6
|
export { FocusMixin } from './src/focus-mixin.js';
|
|
7
7
|
export { KeyboardMixin } from './src/keyboard-mixin.js';
|
|
8
|
+
export { SlotController } from './src/slot-controller.js';
|
|
8
9
|
export { SlotMixin } from './src/slot-mixin.js';
|
|
9
10
|
export { TabindexMixin } from './src/tabindex-mixin.js';
|
package/index.js
CHANGED
|
@@ -5,5 +5,6 @@ export { DisabledMixin } from './src/disabled-mixin.js';
|
|
|
5
5
|
export { ElementMixin } from './src/element-mixin.js';
|
|
6
6
|
export { FocusMixin } from './src/focus-mixin.js';
|
|
7
7
|
export { KeyboardMixin } from './src/keyboard-mixin.js';
|
|
8
|
+
export { SlotController } from './src/slot-controller.js';
|
|
8
9
|
export { SlotMixin } from './src/slot-mixin.js';
|
|
9
10
|
export { TabindexMixin } from './src/tabindex-mixin.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/component-base",
|
|
3
|
-
"version": "22.0.
|
|
3
|
+
"version": "22.0.2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -41,5 +41,5 @@
|
|
|
41
41
|
"@vaadin/testing-helpers": "^0.3.2",
|
|
42
42
|
"sinon": "^9.2.4"
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "df21370c4a655a38eac11f79686021ab3b0887ad"
|
|
45
45
|
}
|
package/src/element-mixin.js
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { ReactiveController } from 'lit';
|
|
7
|
+
|
|
8
|
+
export class SlotController implements ReactiveController {
|
|
9
|
+
constructor(
|
|
10
|
+
host: HTMLElement,
|
|
11
|
+
slotName: string,
|
|
12
|
+
slotFactory?: () => HTMLElement,
|
|
13
|
+
slotInitializer?: (host: HTMLElement, node: HTMLElement) => void
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
hostConnected(): void;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The controller host element.
|
|
20
|
+
*/
|
|
21
|
+
host: HTMLElement;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The slotted node managed by the controller.
|
|
25
|
+
*/
|
|
26
|
+
node: HTMLElement;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Return a reference to the node managed by the controller.
|
|
30
|
+
*/
|
|
31
|
+
getSlotChild(): Node;
|
|
32
|
+
|
|
33
|
+
protected initialized: boolean;
|
|
34
|
+
|
|
35
|
+
protected defaultNode: Node;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Override to initialize the newly added custom node.
|
|
39
|
+
*/
|
|
40
|
+
protected initCustomNode(node: Node): void;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Override to cleanup slotted node when it's removed.
|
|
44
|
+
*/
|
|
45
|
+
protected teardownNode(node: Node): void;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Setup the observer to manage slot content changes.
|
|
49
|
+
*/
|
|
50
|
+
protected observe(): void;
|
|
51
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A controller for providing content to slot element and observing changes.
|
|
10
|
+
*/
|
|
11
|
+
export class SlotController {
|
|
12
|
+
constructor(host, slotName, slotFactory, slotInitializer) {
|
|
13
|
+
this.host = host;
|
|
14
|
+
this.slotName = slotName;
|
|
15
|
+
this.slotFactory = slotFactory;
|
|
16
|
+
this.slotInitializer = slotInitializer;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
hostConnected() {
|
|
20
|
+
if (!this.initialized) {
|
|
21
|
+
const { host, slotName, slotFactory, slotInitializer } = this;
|
|
22
|
+
|
|
23
|
+
const slotted = this.getSlotChild();
|
|
24
|
+
|
|
25
|
+
if (!slotted) {
|
|
26
|
+
// Slot factory is optional, some slots don't have default content.
|
|
27
|
+
if (slotFactory) {
|
|
28
|
+
const slotContent = slotFactory(host);
|
|
29
|
+
if (slotContent instanceof Element) {
|
|
30
|
+
if (slotName !== '') {
|
|
31
|
+
slotContent.setAttribute('slot', slotName);
|
|
32
|
+
}
|
|
33
|
+
host.appendChild(slotContent);
|
|
34
|
+
this.node = slotContent;
|
|
35
|
+
|
|
36
|
+
// Store reference to not pass default node to `initCustomNode`.
|
|
37
|
+
this.defaultNode = slotContent;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
this.node = slotted;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Don't try to bind `this` to initializer (normally it's arrow function).
|
|
45
|
+
// Instead, pass the host as a first argument to access component's state.
|
|
46
|
+
if (slotInitializer) {
|
|
47
|
+
slotInitializer(host, this.node);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// TODO: Consider making this behavior opt-in to improve performance.
|
|
51
|
+
this.observe();
|
|
52
|
+
|
|
53
|
+
this.initialized = true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Return a reference to the node managed by the controller.
|
|
59
|
+
* @return {Node}
|
|
60
|
+
*/
|
|
61
|
+
getSlotChild() {
|
|
62
|
+
const { slotName } = this;
|
|
63
|
+
return Array.from(this.host.childNodes).find((node) => {
|
|
64
|
+
// Either an element (any slot) or a text node (only un-named slot).
|
|
65
|
+
return (
|
|
66
|
+
(node.nodeType === Node.ELEMENT_NODE && node.slot === slotName) ||
|
|
67
|
+
(node.nodeType === Node.TEXT_NODE && node.textContent.trim() && slotName === '')
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Override to initialize the newly added custom node.
|
|
74
|
+
*
|
|
75
|
+
* @param {Node} _node
|
|
76
|
+
* @protected
|
|
77
|
+
*/
|
|
78
|
+
initCustomNode(_node) {}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Override to teardown slotted node when it's removed.
|
|
82
|
+
*
|
|
83
|
+
* @param {Node} _node
|
|
84
|
+
* @protected
|
|
85
|
+
*/
|
|
86
|
+
teardownNode(_node) {}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Setup the observer to manage slot content changes.
|
|
90
|
+
* @protected
|
|
91
|
+
*/
|
|
92
|
+
observe() {
|
|
93
|
+
const { slotName } = this;
|
|
94
|
+
const selector = slotName === '' ? 'slot:not([name])' : `slot[name=${slotName}]`;
|
|
95
|
+
const slot = this.host.shadowRoot.querySelector(selector);
|
|
96
|
+
|
|
97
|
+
this.__slotObserver = new FlattenedNodesObserver(slot, (info) => {
|
|
98
|
+
// TODO: support default slot with multiple nodes (e.g. confirm-dialog)
|
|
99
|
+
const current = this.node;
|
|
100
|
+
const newNode = info.addedNodes.find((node) => node !== current);
|
|
101
|
+
|
|
102
|
+
if (info.removedNodes.length) {
|
|
103
|
+
info.removedNodes.forEach((node) => {
|
|
104
|
+
this.teardownNode(node);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (newNode) {
|
|
109
|
+
// Custom node is added, remove the current one.
|
|
110
|
+
if (current && current.isConnected) {
|
|
111
|
+
this.host.removeChild(current);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.node = newNode;
|
|
115
|
+
|
|
116
|
+
if (newNode !== this.defaultNode) {
|
|
117
|
+
this.initCustomNode(newNode);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|