@remote-dom/polyfill 1.2.0 → 1.3.0
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/CHANGELOG.md +170 -0
- package/build/cjs/Element.cjs +4 -3
- package/build/cjs/Event.cjs +14 -20
- package/build/cjs/EventTarget.cjs +9 -14
- package/build/cjs/NamedNodeMap.cjs +1 -1
- package/build/cjs/ParentNode.cjs +2 -0
- package/build/cjs/constants.cjs +4 -2
- package/build/esm/Element.mjs +5 -4
- package/build/esm/Event.mjs +15 -21
- package/build/esm/EventTarget.mjs +10 -15
- package/build/esm/NamedNodeMap.mjs +1 -1
- package/build/esm/ParentNode.mjs +2 -0
- package/build/esm/constants.mjs +3 -2
- package/build/esnext/Element.esnext +5 -4
- package/build/esnext/Event.esnext +15 -21
- package/build/esnext/EventTarget.esnext +10 -15
- package/build/esnext/NamedNodeMap.esnext +1 -1
- package/build/esnext/ParentNode.esnext +2 -0
- package/build/esnext/constants.esnext +3 -2
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/typescript/Element.d.ts +2 -2
- package/build/typescript/Element.d.ts.map +1 -1
- package/build/typescript/Event.d.ts +5 -5
- package/build/typescript/Event.d.ts.map +1 -1
- package/build/typescript/EventTarget.d.ts.map +1 -1
- package/build/typescript/NamedNodeMap.d.ts.map +1 -1
- package/build/typescript/ParentNode.d.ts.map +1 -1
- package/build/typescript/constants.d.ts +2 -1
- package/build/typescript/constants.d.ts.map +1 -1
- package/package.json +1 -1
- package/source/Element.ts +7 -4
- package/source/Event.ts +24 -26
- package/source/EventTarget.ts +14 -15
- package/source/NamedNodeMap.ts +2 -1
- package/source/ParentNode.ts +2 -0
- package/source/constants.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,175 @@
|
|
|
1
1
|
# @remote-dom/polyfill
|
|
2
2
|
|
|
3
|
+
## 1.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#389](https://github.com/Shopify/remote-dom/pull/389) [`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403) Thanks [@lemonmade](https://github.com/lemonmade)! - Added native support for synchronizing attributes and event listeners
|
|
8
|
+
|
|
9
|
+
Previously, Remote DOM only offered “remote properties” as a way to synchronize element state between the host and remote environments. These remote properties effectively synchronize a subset of a custom element’s instance properties. The `RemoteElement` class offers [a declarative way to define the properties that should be synchronized](/packages/core/README.md#remote-properties).
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import {RemoteElement} from '@remote-dom/core/elements';
|
|
13
|
+
|
|
14
|
+
class MyElement extends RemoteElement {
|
|
15
|
+
static get remoteProperties() {
|
|
16
|
+
return ['label'];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
customElements.define('my-element', MyElement);
|
|
21
|
+
|
|
22
|
+
const myElement = document.createElement('my-element');
|
|
23
|
+
myElement.label = 'Hello, World!';
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The same `remoteProperties` configuration can create special handling for attributes and event listeners. By default, a remote property is automatically updated when setting an [attribute](https://developer.mozilla.org/en-US/docs/Glossary/Attribute) of the same name:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
const myElement = document.createElement('my-element');
|
|
30
|
+
myElement.setAttribute('label', 'Hello, World!');
|
|
31
|
+
|
|
32
|
+
// myElement.label === 'Hello, World!', and this value is synchronized
|
|
33
|
+
// with the host environment as a “remote property”
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Similarly, a remote property can be automatically updated when adding an event listener based on a conventional `on` property naming prefix:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import {RemoteElement} from '@remote-dom/core/elements';
|
|
40
|
+
|
|
41
|
+
class MyElement extends RemoteElement {
|
|
42
|
+
static get remoteProperties() {
|
|
43
|
+
return {
|
|
44
|
+
onChange: {
|
|
45
|
+
event: true,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
customElements.define('my-element', MyElement);
|
|
52
|
+
|
|
53
|
+
const myElement = document.createElement('my-element');
|
|
54
|
+
|
|
55
|
+
// This adds a callback property that is synchronized with the host environment
|
|
56
|
+
myElement.onChange = () => console.log('Changed!');
|
|
57
|
+
|
|
58
|
+
// And so does this, but using the `addEventListener` method instead
|
|
59
|
+
myElement.addEventListener('change', () => console.log('Changed!'));
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
These utilities are handy, but they don’t align with patterns in native DOM elements, particularly when it comes to events. Now, both of these can be represented in a fashion that is more conventional in HTML. The `remoteAttributes` configuration allows you to define a set of element attributes that will be synchronized directly the host environment, instead of being treated as instance properties:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import {RemoteElement} from '@remote-dom/core/elements';
|
|
66
|
+
|
|
67
|
+
class MyElement extends RemoteElement {
|
|
68
|
+
static get remoteAttributes() {
|
|
69
|
+
return ['label'];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// If you want to add instance properties, you can do it with getters and
|
|
73
|
+
// setters that manipulate the attribute value:
|
|
74
|
+
//
|
|
75
|
+
// get label() {
|
|
76
|
+
// return this.getAttribute('label');
|
|
77
|
+
// }
|
|
78
|
+
//
|
|
79
|
+
// set label(value) {
|
|
80
|
+
// this.setAttribute('label', value);
|
|
81
|
+
// }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
customElements.define('my-element', MyElement);
|
|
85
|
+
|
|
86
|
+
const myElement = document.createElement('my-element');
|
|
87
|
+
myElement.setAttribute('label', 'Hello, World!');
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Similarly, the `remoteEvents` configuration allows you to define a set of event listeners that will be synchronized directly with the host environment:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import {RemoteElement} from '@remote-dom/core/elements';
|
|
94
|
+
|
|
95
|
+
class MyElement extends RemoteElement {
|
|
96
|
+
static get remoteEvents() {
|
|
97
|
+
return ['change'];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
customElements.define('my-element', MyElement);
|
|
102
|
+
|
|
103
|
+
const myElement = document.createElement('my-element');
|
|
104
|
+
|
|
105
|
+
// And so does this, but using the `addEventListener` method instead
|
|
106
|
+
myElement.addEventListener('change', () => console.log('Changed!'));
|
|
107
|
+
|
|
108
|
+
// No `myElement.onChange` property is created
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The `remoteProperties` configuration will continue to be supported for cases where you want to synchronize instance properties. Because instance properties can be any JavaScript type, properties are the highest-fidelity field that can be synchronized between the remote and host environments. However, adding event listeners using the `remoteProperties.event` configuration is **deprecated and will be removed in the next major version**. You should use the `remoteEvents` configuration instead. If you were previously defining remote properties which only accepted strings, consider using the `remoteAttributes` configuration, which stores the value entirely in an HTML attribute instead.
|
|
112
|
+
|
|
113
|
+
This change is being released in a backwards-compatible way, so you can continue to use the existing `remoteProperties` configuration on host and/or remote environments without any code changes.
|
|
114
|
+
|
|
115
|
+
All host utilities have been updated to support the new `attributes` and `eventListeners` property that are synchronized with the remote environment. This includes updates to the [React](/packages/react/README.md#event-listener-props) and [Preact hosts to map events to conventional callback props](/packages/preact/README.md#event-listener-props), and updates to the [`DOMRemoteReceiver` class](/packages/core/README.md#domremotereceiver), which now applies fields to the host element exactly as they were applied in the remote environment:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
// Remote environment:
|
|
119
|
+
|
|
120
|
+
class MyElement extends RemoteElement {
|
|
121
|
+
static get remoteEvents() {
|
|
122
|
+
return ['change'];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
customElements.define('my-element', MyElement);
|
|
127
|
+
|
|
128
|
+
const myElement = document.createElement('my-element');
|
|
129
|
+
|
|
130
|
+
myElement.addEventListener('change', (event) => {
|
|
131
|
+
console.log('Changed! New value: ', event.detail);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Host environment:
|
|
135
|
+
|
|
136
|
+
class MyElement extends HTMLElement {
|
|
137
|
+
connectedCallback() {
|
|
138
|
+
// Emit a change event on this element, with detail that will be passed
|
|
139
|
+
// to the remote environment
|
|
140
|
+
this.addEventListener('change', (event) => {
|
|
141
|
+
event.stopImmediatePropagation();
|
|
142
|
+
|
|
143
|
+
this.dispatchEvent(
|
|
144
|
+
new CustomEvent('change', {
|
|
145
|
+
detail: this.value,
|
|
146
|
+
}),
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Additional implementation details of the host custom element...
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
customElements.define('my-element', MyElement);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Patch Changes
|
|
158
|
+
|
|
159
|
+
- [#389](https://github.com/Shopify/remote-dom/pull/389) [`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403) Thanks [@lemonmade](https://github.com/lemonmade)! - Bug fixes to event dispatching
|
|
160
|
+
|
|
161
|
+
- Listeners on the target are now called during both the capture and bubble phases.
|
|
162
|
+
- `stopPropagation` now respected.
|
|
163
|
+
- `stopImmediatePropagation` now also stops regular propagation.
|
|
164
|
+
|
|
165
|
+
- [#389](https://github.com/Shopify/remote-dom/pull/389) [`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403) Thanks [@lemonmade](https://github.com/lemonmade)! - Fix `Event.bubbles` and `Event.composedPath()` implementations
|
|
166
|
+
|
|
167
|
+
## 1.2.1
|
|
168
|
+
|
|
169
|
+
### Patch Changes
|
|
170
|
+
|
|
171
|
+
- [#419](https://github.com/Shopify/remote-dom/pull/419) [`3c6bd29`](https://github.com/Shopify/remote-dom/commit/3c6bd291121b9fa02cac4ba57274601e97b2a2d2) Thanks [@lemonmade](https://github.com/lemonmade)! - Fix synchronization of `slot` property in some edge cases
|
|
172
|
+
|
|
3
173
|
## 1.2.0
|
|
4
174
|
|
|
5
175
|
### Minor Changes
|
package/build/cjs/Element.cjs
CHANGED
|
@@ -11,7 +11,6 @@ const serialization = require('./serialization.cjs');
|
|
|
11
11
|
class Element extends ParentNode.ParentNode {
|
|
12
12
|
nodeType = constants.NodeType.ELEMENT_NODE;
|
|
13
13
|
[constants.NS] = constants.NamespaceURI.XHTML;
|
|
14
|
-
[constants.SLOT] = '';
|
|
15
14
|
get namespaceURI() {
|
|
16
15
|
return this[constants.NS];
|
|
17
16
|
}
|
|
@@ -19,11 +18,10 @@ class Element extends ParentNode.ParentNode {
|
|
|
19
18
|
return this.nodeName;
|
|
20
19
|
}
|
|
21
20
|
get slot() {
|
|
22
|
-
return this
|
|
21
|
+
return this.getAttribute('slot') ?? '';
|
|
23
22
|
}
|
|
24
23
|
set slot(slot) {
|
|
25
24
|
const finalSlot = String(slot);
|
|
26
|
-
this[constants.SLOT] = finalSlot;
|
|
27
25
|
if (this.getAttribute('slot') !== finalSlot) {
|
|
28
26
|
this.attributes.setNamedItem(new Attr.Attr('slot', finalSlot));
|
|
29
27
|
}
|
|
@@ -36,6 +34,9 @@ class Element extends ParentNode.ParentNode {
|
|
|
36
34
|
}
|
|
37
35
|
return attributes;
|
|
38
36
|
}
|
|
37
|
+
getAttributeNames() {
|
|
38
|
+
return [...this.attributes].map(attr => attr.name);
|
|
39
|
+
}
|
|
39
40
|
get firstElementChild() {
|
|
40
41
|
return this.children[0] ?? null;
|
|
41
42
|
}
|
package/build/cjs/Event.cjs
CHANGED
|
@@ -33,11 +33,11 @@ class Event {
|
|
|
33
33
|
composed = false;
|
|
34
34
|
defaultPrevented = false;
|
|
35
35
|
cancelBubble = false;
|
|
36
|
-
immediatePropagationStopped = false;
|
|
37
36
|
eventPhase = 0;
|
|
38
|
-
path = [];
|
|
39
37
|
// private inPassiveListener = false;
|
|
40
38
|
|
|
39
|
+
[constants.PATH] = [];
|
|
40
|
+
[constants.STOP_IMMEDIATE_PROPAGATION] = false;
|
|
41
41
|
constructor(type, options) {
|
|
42
42
|
this.type = type;
|
|
43
43
|
Object.defineProperty(this, constants.IS_TRUSTED, {
|
|
@@ -50,17 +50,18 @@ class Event {
|
|
|
50
50
|
if (options.composed) this.composed = options.composed;
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
-
get composedPath() {
|
|
54
|
-
return this.path;
|
|
55
|
-
}
|
|
56
53
|
get isTrusted() {
|
|
57
54
|
return this[constants.IS_TRUSTED];
|
|
58
55
|
}
|
|
56
|
+
composedPath() {
|
|
57
|
+
return this[constants.PATH];
|
|
58
|
+
}
|
|
59
59
|
stopPropagation() {
|
|
60
60
|
this.cancelBubble = true;
|
|
61
61
|
}
|
|
62
62
|
stopImmediatePropagation() {
|
|
63
|
-
this.
|
|
63
|
+
this[constants.STOP_IMMEDIATE_PROPAGATION] = true;
|
|
64
|
+
this.cancelBubble = true;
|
|
64
65
|
}
|
|
65
66
|
preventDefault() {
|
|
66
67
|
this.defaultPrevented = true;
|
|
@@ -79,31 +80,24 @@ class Event {
|
|
|
79
80
|
this.cancelable = Boolean(cancelable);
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
|
-
function fireEvent(event,
|
|
83
|
-
const listeners =
|
|
83
|
+
function fireEvent(event, currentTarget, phase) {
|
|
84
|
+
const listeners = currentTarget[constants.LISTENERS];
|
|
84
85
|
const list = listeners?.get(`${event.type}${phase === EventPhase.CAPTURING_PHASE ? CAPTURE_MARKER : ''}`);
|
|
85
|
-
if (!list) return
|
|
86
|
-
let defaultPrevented = false;
|
|
86
|
+
if (!list) return;
|
|
87
87
|
for (const listener of list) {
|
|
88
|
-
event.eventPhase = phase;
|
|
89
|
-
event.currentTarget =
|
|
88
|
+
event.eventPhase = event.target === currentTarget ? EventPhase.AT_TARGET : phase;
|
|
89
|
+
event.currentTarget = currentTarget;
|
|
90
90
|
try {
|
|
91
91
|
if (typeof listener === 'object') {
|
|
92
92
|
listener.handleEvent(event);
|
|
93
93
|
} else {
|
|
94
|
-
listener.call(
|
|
94
|
+
listener.call(currentTarget, event);
|
|
95
95
|
}
|
|
96
96
|
} catch (err) {
|
|
97
97
|
setTimeout(thrower, 0, err);
|
|
98
98
|
}
|
|
99
|
-
if (event.
|
|
100
|
-
defaultPrevented = true;
|
|
101
|
-
}
|
|
102
|
-
if (event.immediatePropagationStopped) {
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
99
|
+
if (event[constants.STOP_IMMEDIATE_PROPAGATION]) break;
|
|
105
100
|
}
|
|
106
|
-
return defaultPrevented;
|
|
107
101
|
}
|
|
108
102
|
function thrower(error) {
|
|
109
103
|
throw error;
|
|
@@ -71,22 +71,17 @@ class EventTarget {
|
|
|
71
71
|
// }
|
|
72
72
|
event.target = this;
|
|
73
73
|
event.srcElement = this;
|
|
74
|
-
event.
|
|
75
|
-
let
|
|
76
|
-
|
|
77
|
-
if (
|
|
78
|
-
defaultPrevented = true;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (Event.fireEvent(event, this, Event.EventPhase.AT_TARGET)) {
|
|
82
|
-
defaultPrevented = true;
|
|
74
|
+
event[constants.PATH] = path;
|
|
75
|
+
for (let i = path.length; i--;) {
|
|
76
|
+
Event.fireEvent(event, path[i], Event.EventPhase.CAPTURING_PHASE);
|
|
77
|
+
if (event.cancelBubble) return event.defaultPrevented;
|
|
83
78
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
79
|
+
const bubblePath = event.bubbles ? path : path.slice(0, 1);
|
|
80
|
+
for (let i = 0; i < bubblePath.length; i++) {
|
|
81
|
+
Event.fireEvent(event, bubblePath[i], Event.EventPhase.BUBBLING_PHASE);
|
|
82
|
+
if (event.cancelBubble) return event.defaultPrevented;
|
|
88
83
|
}
|
|
89
|
-
return
|
|
84
|
+
return event.defaultPrevented;
|
|
90
85
|
}
|
|
91
86
|
}
|
|
92
87
|
function removeEventListener(type, listener, options) {
|
|
@@ -111,7 +111,7 @@ function updateElementAttribute(element, name, oldValue, newValue) {
|
|
|
111
111
|
const {
|
|
112
112
|
attributeChangedCallback
|
|
113
113
|
} = element;
|
|
114
|
-
if (name === 'slot'
|
|
114
|
+
if (name === 'slot') {
|
|
115
115
|
element.slot = newValue ?? '';
|
|
116
116
|
}
|
|
117
117
|
if (attributeChangedCallback == null || observedAttributes == null || !observedAttributes.includes(name)) {
|
package/build/cjs/ParentNode.cjs
CHANGED
|
@@ -49,6 +49,7 @@ class ParentNode extends ChildNode.ChildNode {
|
|
|
49
49
|
const children = this.children;
|
|
50
50
|
children.splice(children.indexOf(child), 1);
|
|
51
51
|
}
|
|
52
|
+
child.disconnectedCallback?.();
|
|
52
53
|
this[constants.HOOKS].removeChild?.(this, child, childNodesIndex);
|
|
53
54
|
}
|
|
54
55
|
replaceChild(newChild, oldChild) {
|
|
@@ -124,6 +125,7 @@ class ParentNode extends ChildNode.ChildNode {
|
|
|
124
125
|
childNodes.push(child);
|
|
125
126
|
if (isElement) this.children.push(child);
|
|
126
127
|
}
|
|
128
|
+
child.connectedCallback?.();
|
|
127
129
|
this[constants.HOOKS].insertChild?.(this, child, insertIndex);
|
|
128
130
|
}
|
|
129
131
|
}
|
package/build/cjs/constants.cjs
CHANGED
|
@@ -15,8 +15,9 @@ const PARENT = Symbol('parent');
|
|
|
15
15
|
const DATA = Symbol('data');
|
|
16
16
|
const LISTENERS = Symbol('listeners');
|
|
17
17
|
const IS_TRUSTED = Symbol('isTrusted');
|
|
18
|
+
const PATH = Symbol('path');
|
|
19
|
+
const STOP_IMMEDIATE_PROPAGATION = Symbol('stop_immediate_propagation');
|
|
18
20
|
const CONTENT = Symbol('content');
|
|
19
|
-
const SLOT = Symbol('slot');
|
|
20
21
|
const HOOKS = Symbol('hooks');
|
|
21
22
|
|
|
22
23
|
// @TODO remove explicit values
|
|
@@ -56,6 +57,7 @@ exports.NodeType = NodeType;
|
|
|
56
57
|
exports.OWNER_DOCUMENT = OWNER_DOCUMENT;
|
|
57
58
|
exports.OWNER_ELEMENT = OWNER_ELEMENT;
|
|
58
59
|
exports.PARENT = PARENT;
|
|
60
|
+
exports.PATH = PATH;
|
|
59
61
|
exports.PREV = PREV;
|
|
60
|
-
exports.
|
|
62
|
+
exports.STOP_IMMEDIATE_PROPAGATION = STOP_IMMEDIATE_PROPAGATION;
|
|
61
63
|
exports.VALUE = VALUE;
|
package/build/esm/Element.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NodeType, NS, NamespaceURI,
|
|
1
|
+
import { NodeType, NS, NamespaceURI, ATTRIBUTES } from './constants.mjs';
|
|
2
2
|
import { ParentNode } from './ParentNode.mjs';
|
|
3
3
|
import { NamedNodeMap } from './NamedNodeMap.mjs';
|
|
4
4
|
import { Attr } from './Attr.mjs';
|
|
@@ -7,7 +7,6 @@ import { serializeNode, serializeChildren, parseHtml } from './serialization.mjs
|
|
|
7
7
|
class Element extends ParentNode {
|
|
8
8
|
nodeType = NodeType.ELEMENT_NODE;
|
|
9
9
|
[NS] = NamespaceURI.XHTML;
|
|
10
|
-
[SLOT] = '';
|
|
11
10
|
get namespaceURI() {
|
|
12
11
|
return this[NS];
|
|
13
12
|
}
|
|
@@ -15,11 +14,10 @@ class Element extends ParentNode {
|
|
|
15
14
|
return this.nodeName;
|
|
16
15
|
}
|
|
17
16
|
get slot() {
|
|
18
|
-
return this
|
|
17
|
+
return this.getAttribute('slot') ?? '';
|
|
19
18
|
}
|
|
20
19
|
set slot(slot) {
|
|
21
20
|
const finalSlot = String(slot);
|
|
22
|
-
this[SLOT] = finalSlot;
|
|
23
21
|
if (this.getAttribute('slot') !== finalSlot) {
|
|
24
22
|
this.attributes.setNamedItem(new Attr('slot', finalSlot));
|
|
25
23
|
}
|
|
@@ -32,6 +30,9 @@ class Element extends ParentNode {
|
|
|
32
30
|
}
|
|
33
31
|
return attributes;
|
|
34
32
|
}
|
|
33
|
+
getAttributeNames() {
|
|
34
|
+
return [...this.attributes].map(attr => attr.name);
|
|
35
|
+
}
|
|
35
36
|
get firstElementChild() {
|
|
36
37
|
return this.children[0] ?? null;
|
|
37
38
|
}
|
package/build/esm/Event.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IS_TRUSTED, LISTENERS } from './constants.mjs';
|
|
1
|
+
import { PATH, STOP_IMMEDIATE_PROPAGATION, IS_TRUSTED, LISTENERS } from './constants.mjs';
|
|
2
2
|
|
|
3
3
|
let EventPhase = /*#__PURE__*/function (EventPhase) {
|
|
4
4
|
EventPhase[EventPhase["NONE"] = 0] = "NONE";
|
|
@@ -29,11 +29,11 @@ class Event {
|
|
|
29
29
|
composed = false;
|
|
30
30
|
defaultPrevented = false;
|
|
31
31
|
cancelBubble = false;
|
|
32
|
-
immediatePropagationStopped = false;
|
|
33
32
|
eventPhase = 0;
|
|
34
|
-
path = [];
|
|
35
33
|
// private inPassiveListener = false;
|
|
36
34
|
|
|
35
|
+
[PATH] = [];
|
|
36
|
+
[STOP_IMMEDIATE_PROPAGATION] = false;
|
|
37
37
|
constructor(type, options) {
|
|
38
38
|
this.type = type;
|
|
39
39
|
Object.defineProperty(this, IS_TRUSTED, {
|
|
@@ -46,17 +46,18 @@ class Event {
|
|
|
46
46
|
if (options.composed) this.composed = options.composed;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
-
get composedPath() {
|
|
50
|
-
return this.path;
|
|
51
|
-
}
|
|
52
49
|
get isTrusted() {
|
|
53
50
|
return this[IS_TRUSTED];
|
|
54
51
|
}
|
|
52
|
+
composedPath() {
|
|
53
|
+
return this[PATH];
|
|
54
|
+
}
|
|
55
55
|
stopPropagation() {
|
|
56
56
|
this.cancelBubble = true;
|
|
57
57
|
}
|
|
58
58
|
stopImmediatePropagation() {
|
|
59
|
-
this
|
|
59
|
+
this[STOP_IMMEDIATE_PROPAGATION] = true;
|
|
60
|
+
this.cancelBubble = true;
|
|
60
61
|
}
|
|
61
62
|
preventDefault() {
|
|
62
63
|
this.defaultPrevented = true;
|
|
@@ -75,31 +76,24 @@ class Event {
|
|
|
75
76
|
this.cancelable = Boolean(cancelable);
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
|
-
function fireEvent(event,
|
|
79
|
-
const listeners =
|
|
79
|
+
function fireEvent(event, currentTarget, phase) {
|
|
80
|
+
const listeners = currentTarget[LISTENERS];
|
|
80
81
|
const list = listeners?.get(`${event.type}${phase === EventPhase.CAPTURING_PHASE ? CAPTURE_MARKER : ''}`);
|
|
81
|
-
if (!list) return
|
|
82
|
-
let defaultPrevented = false;
|
|
82
|
+
if (!list) return;
|
|
83
83
|
for (const listener of list) {
|
|
84
|
-
event.eventPhase = phase;
|
|
85
|
-
event.currentTarget =
|
|
84
|
+
event.eventPhase = event.target === currentTarget ? EventPhase.AT_TARGET : phase;
|
|
85
|
+
event.currentTarget = currentTarget;
|
|
86
86
|
try {
|
|
87
87
|
if (typeof listener === 'object') {
|
|
88
88
|
listener.handleEvent(event);
|
|
89
89
|
} else {
|
|
90
|
-
listener.call(
|
|
90
|
+
listener.call(currentTarget, event);
|
|
91
91
|
}
|
|
92
92
|
} catch (err) {
|
|
93
93
|
setTimeout(thrower, 0, err);
|
|
94
94
|
}
|
|
95
|
-
if (event
|
|
96
|
-
defaultPrevented = true;
|
|
97
|
-
}
|
|
98
|
-
if (event.immediatePropagationStopped) {
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
95
|
+
if (event[STOP_IMMEDIATE_PROPAGATION]) break;
|
|
101
96
|
}
|
|
102
|
-
return defaultPrevented;
|
|
103
97
|
}
|
|
104
98
|
function thrower(error) {
|
|
105
99
|
throw error;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LISTENERS, OWNER_DOCUMENT, HOOKS } from './constants.mjs';
|
|
1
|
+
import { LISTENERS, OWNER_DOCUMENT, HOOKS, PATH } from './constants.mjs';
|
|
2
2
|
import { fireEvent, CAPTURE_MARKER, EventPhase } from './Event.mjs';
|
|
3
3
|
|
|
4
4
|
const ONCE_LISTENERS = Symbol('onceListeners');
|
|
@@ -67,22 +67,17 @@ class EventTarget {
|
|
|
67
67
|
// }
|
|
68
68
|
event.target = this;
|
|
69
69
|
event.srcElement = this;
|
|
70
|
-
event
|
|
71
|
-
let
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
defaultPrevented = true;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
if (fireEvent(event, this, EventPhase.AT_TARGET)) {
|
|
78
|
-
defaultPrevented = true;
|
|
70
|
+
event[PATH] = path;
|
|
71
|
+
for (let i = path.length; i--;) {
|
|
72
|
+
fireEvent(event, path[i], EventPhase.CAPTURING_PHASE);
|
|
73
|
+
if (event.cancelBubble) return event.defaultPrevented;
|
|
79
74
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
75
|
+
const bubblePath = event.bubbles ? path : path.slice(0, 1);
|
|
76
|
+
for (let i = 0; i < bubblePath.length; i++) {
|
|
77
|
+
fireEvent(event, bubblePath[i], EventPhase.BUBBLING_PHASE);
|
|
78
|
+
if (event.cancelBubble) return event.defaultPrevented;
|
|
84
79
|
}
|
|
85
|
-
return
|
|
80
|
+
return event.defaultPrevented;
|
|
86
81
|
}
|
|
87
82
|
}
|
|
88
83
|
function removeEventListener(type, listener, options) {
|
|
@@ -107,7 +107,7 @@ function updateElementAttribute(element, name, oldValue, newValue) {
|
|
|
107
107
|
const {
|
|
108
108
|
attributeChangedCallback
|
|
109
109
|
} = element;
|
|
110
|
-
if (name === 'slot'
|
|
110
|
+
if (name === 'slot') {
|
|
111
111
|
element.slot = newValue ?? '';
|
|
112
112
|
}
|
|
113
113
|
if (attributeChangedCallback == null || observedAttributes == null || !observedAttributes.includes(name)) {
|
package/build/esm/ParentNode.mjs
CHANGED
|
@@ -45,6 +45,7 @@ class ParentNode extends ChildNode {
|
|
|
45
45
|
const children = this.children;
|
|
46
46
|
children.splice(children.indexOf(child), 1);
|
|
47
47
|
}
|
|
48
|
+
child.disconnectedCallback?.();
|
|
48
49
|
this[HOOKS].removeChild?.(this, child, childNodesIndex);
|
|
49
50
|
}
|
|
50
51
|
replaceChild(newChild, oldChild) {
|
|
@@ -120,6 +121,7 @@ class ParentNode extends ChildNode {
|
|
|
120
121
|
childNodes.push(child);
|
|
121
122
|
if (isElement) this.children.push(child);
|
|
122
123
|
}
|
|
124
|
+
child.connectedCallback?.();
|
|
123
125
|
this[HOOKS].insertChild?.(this, child, insertIndex);
|
|
124
126
|
}
|
|
125
127
|
}
|
package/build/esm/constants.mjs
CHANGED
|
@@ -11,8 +11,9 @@ const PARENT = Symbol('parent');
|
|
|
11
11
|
const DATA = Symbol('data');
|
|
12
12
|
const LISTENERS = Symbol('listeners');
|
|
13
13
|
const IS_TRUSTED = Symbol('isTrusted');
|
|
14
|
+
const PATH = Symbol('path');
|
|
15
|
+
const STOP_IMMEDIATE_PROPAGATION = Symbol('stop_immediate_propagation');
|
|
14
16
|
const CONTENT = Symbol('content');
|
|
15
|
-
const SLOT = Symbol('slot');
|
|
16
17
|
const HOOKS = Symbol('hooks');
|
|
17
18
|
|
|
18
19
|
// @TODO remove explicit values
|
|
@@ -37,4 +38,4 @@ let NamespaceURI = /*#__PURE__*/function (NamespaceURI) {
|
|
|
37
38
|
return NamespaceURI;
|
|
38
39
|
}({});
|
|
39
40
|
|
|
40
|
-
export { ATTRIBUTES, CHILD, CONTENT, DATA, HOOKS, IS_TRUSTED, LISTENERS, NAME, NEXT, NS, NamespaceURI, NodeType, OWNER_DOCUMENT, OWNER_ELEMENT, PARENT, PREV,
|
|
41
|
+
export { ATTRIBUTES, CHILD, CONTENT, DATA, HOOKS, IS_TRUSTED, LISTENERS, NAME, NEXT, NS, NamespaceURI, NodeType, OWNER_DOCUMENT, OWNER_ELEMENT, PARENT, PATH, PREV, STOP_IMMEDIATE_PROPAGATION, VALUE };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NodeType, NS, NamespaceURI,
|
|
1
|
+
import { NodeType, NS, NamespaceURI, ATTRIBUTES } from './constants.esnext';
|
|
2
2
|
import { ParentNode } from './ParentNode.esnext';
|
|
3
3
|
import { NamedNodeMap } from './NamedNodeMap.esnext';
|
|
4
4
|
import { Attr } from './Attr.esnext';
|
|
@@ -8,7 +8,6 @@ class Element extends ParentNode {
|
|
|
8
8
|
static observedAttributes;
|
|
9
9
|
nodeType = NodeType.ELEMENT_NODE;
|
|
10
10
|
[NS] = NamespaceURI.XHTML;
|
|
11
|
-
[SLOT] = "";
|
|
12
11
|
get namespaceURI() {
|
|
13
12
|
return this[NS];
|
|
14
13
|
}
|
|
@@ -17,11 +16,10 @@ class Element extends ParentNode {
|
|
|
17
16
|
}
|
|
18
17
|
[ATTRIBUTES];
|
|
19
18
|
get slot() {
|
|
20
|
-
return this
|
|
19
|
+
return this.getAttribute("slot") ?? "";
|
|
21
20
|
}
|
|
22
21
|
set slot(slot) {
|
|
23
22
|
const finalSlot = String(slot);
|
|
24
|
-
this[SLOT] = finalSlot;
|
|
25
23
|
if (this.getAttribute("slot") !== finalSlot) {
|
|
26
24
|
this.attributes.setNamedItem(new Attr("slot", finalSlot));
|
|
27
25
|
}
|
|
@@ -34,6 +32,9 @@ class Element extends ParentNode {
|
|
|
34
32
|
}
|
|
35
33
|
return attributes;
|
|
36
34
|
}
|
|
35
|
+
getAttributeNames() {
|
|
36
|
+
return [...this.attributes].map((attr) => attr.name);
|
|
37
|
+
}
|
|
37
38
|
get firstElementChild() {
|
|
38
39
|
return this.children[0] ?? null;
|
|
39
40
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IS_TRUSTED, LISTENERS } from './constants.esnext';
|
|
1
|
+
import { IS_TRUSTED, PATH, STOP_IMMEDIATE_PROPAGATION, LISTENERS } from './constants.esnext';
|
|
2
2
|
|
|
3
3
|
var EventPhase = /* @__PURE__ */ ((EventPhase2) => {
|
|
4
4
|
EventPhase2[EventPhase2["NONE"] = 0] = "NONE";
|
|
@@ -36,23 +36,24 @@ class Event {
|
|
|
36
36
|
composed = false;
|
|
37
37
|
defaultPrevented = false;
|
|
38
38
|
cancelBubble = false;
|
|
39
|
-
immediatePropagationStopped = false;
|
|
40
39
|
eventPhase = 0;
|
|
41
|
-
path = [];
|
|
42
40
|
// private inPassiveListener = false;
|
|
43
41
|
data;
|
|
42
|
+
[PATH] = [];
|
|
44
43
|
[IS_TRUSTED];
|
|
45
|
-
|
|
46
|
-
return this.path;
|
|
47
|
-
}
|
|
44
|
+
[STOP_IMMEDIATE_PROPAGATION] = false;
|
|
48
45
|
get isTrusted() {
|
|
49
46
|
return this[IS_TRUSTED];
|
|
50
47
|
}
|
|
48
|
+
composedPath() {
|
|
49
|
+
return this[PATH];
|
|
50
|
+
}
|
|
51
51
|
stopPropagation() {
|
|
52
52
|
this.cancelBubble = true;
|
|
53
53
|
}
|
|
54
54
|
stopImmediatePropagation() {
|
|
55
|
-
this
|
|
55
|
+
this[STOP_IMMEDIATE_PROPAGATION] = true;
|
|
56
|
+
this.cancelBubble = true;
|
|
56
57
|
}
|
|
57
58
|
preventDefault() {
|
|
58
59
|
this.defaultPrevented = true;
|
|
@@ -70,33 +71,26 @@ class Event {
|
|
|
70
71
|
this.cancelable = Boolean(cancelable);
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
|
-
function fireEvent(event,
|
|
74
|
-
const listeners =
|
|
74
|
+
function fireEvent(event, currentTarget, phase) {
|
|
75
|
+
const listeners = currentTarget[LISTENERS];
|
|
75
76
|
const list = listeners?.get(
|
|
76
77
|
`${event.type}${phase === 1 /* CAPTURING_PHASE */ ? CAPTURE_MARKER : ""}`
|
|
77
78
|
);
|
|
78
|
-
if (!list) return
|
|
79
|
-
let defaultPrevented = false;
|
|
79
|
+
if (!list) return;
|
|
80
80
|
for (const listener of list) {
|
|
81
|
-
event.eventPhase = phase;
|
|
82
|
-
event.currentTarget =
|
|
81
|
+
event.eventPhase = event.target === currentTarget ? 2 /* AT_TARGET */ : phase;
|
|
82
|
+
event.currentTarget = currentTarget;
|
|
83
83
|
try {
|
|
84
84
|
if (typeof listener === "object") {
|
|
85
85
|
listener.handleEvent(event);
|
|
86
86
|
} else {
|
|
87
|
-
listener.call(
|
|
87
|
+
listener.call(currentTarget, event);
|
|
88
88
|
}
|
|
89
89
|
} catch (err) {
|
|
90
90
|
setTimeout(thrower, 0, err);
|
|
91
91
|
}
|
|
92
|
-
if (event
|
|
93
|
-
defaultPrevented = true;
|
|
94
|
-
}
|
|
95
|
-
if (event.immediatePropagationStopped) {
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
92
|
+
if (event[STOP_IMMEDIATE_PROPAGATION]) break;
|
|
98
93
|
}
|
|
99
|
-
return defaultPrevented;
|
|
100
94
|
}
|
|
101
95
|
function thrower(error) {
|
|
102
96
|
throw error;
|