@joist/element 4.2.3-next.2 → 4.2.3-next.21
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 +4 -2
- package/src/lib/attr.test.ts +69 -18
- package/src/lib/attr.ts +5 -6
- package/src/lib/element.test.ts +33 -25
- package/src/lib/element.ts +21 -27
- package/src/lib/listen.test.ts +75 -0
- package/src/lib/metadata.ts +5 -7
- package/target/lib/attr.d.ts +1 -1
- package/target/lib/attr.js +3 -4
- package/target/lib/attr.js.map +1 -1
- package/target/lib/attr.test.js +90 -17
- package/target/lib/attr.test.js.map +1 -1
- package/target/lib/element.js +16 -17
- package/target/lib/element.js.map +1 -1
- package/target/lib/element.test.js +68 -0
- package/target/lib/element.test.js.map +1 -1
- package/target/lib/listen.test.js +97 -0
- package/target/lib/listen.test.js.map +1 -1
- package/target/lib/metadata.d.ts +4 -2
- package/target/lib/metadata.js.map +1 -1
- package/src/lib/templating/README.md +0 -406
- package/src/lib/templating/bind.ts +0 -40
- package/src/lib/templating/define.ts +0 -5
- package/src/lib/templating/elements/async.element.test.ts +0 -90
- package/src/lib/templating/elements/async.element.ts +0 -122
- package/src/lib/templating/elements/for.element.test.ts +0 -221
- package/src/lib/templating/elements/for.element.ts +0 -189
- package/src/lib/templating/elements/if.element.test.ts +0 -90
- package/src/lib/templating/elements/if.element.ts +0 -93
- package/src/lib/templating/elements/props.element.test.ts +0 -62
- package/src/lib/templating/elements/props.element.ts +0 -80
- package/src/lib/templating/elements/scope.ts +0 -45
- package/src/lib/templating/elements/value.element.test.ts +0 -20
- package/src/lib/templating/elements/value.element.ts +0 -41
- package/src/lib/templating/events.ts +0 -21
- package/src/lib/templating/token.test.ts +0 -74
- package/src/lib/templating/token.ts +0 -34
- package/src/lib/templating.ts +0 -2
- package/target/lib/templating/bind.d.ts +0 -1
- package/target/lib/templating/bind.js +0 -30
- package/target/lib/templating/bind.js.map +0 -1
- package/target/lib/templating/define.d.ts +0 -5
- package/target/lib/templating/define.js +0 -6
- package/target/lib/templating/define.js.map +0 -1
- package/target/lib/templating/elements/async.element.d.ts +0 -17
- package/target/lib/templating/elements/async.element.js +0 -115
- package/target/lib/templating/elements/async.element.js.map +0 -1
- package/target/lib/templating/elements/async.element.test.d.ts +0 -1
- package/target/lib/templating/elements/async.element.test.js +0 -75
- package/target/lib/templating/elements/async.element.test.js.map +0 -1
- package/target/lib/templating/elements/for.element.d.ts +0 -24
- package/target/lib/templating/elements/for.element.js +0 -186
- package/target/lib/templating/elements/for.element.js.map +0 -1
- package/target/lib/templating/elements/for.element.test.d.ts +0 -2
- package/target/lib/templating/elements/for.element.test.js +0 -153
- package/target/lib/templating/elements/for.element.test.js.map +0 -1
- package/target/lib/templating/elements/if.element.d.ts +0 -12
- package/target/lib/templating/elements/if.element.js +0 -85
- package/target/lib/templating/elements/if.element.js.map +0 -1
- package/target/lib/templating/elements/if.element.test.d.ts +0 -1
- package/target/lib/templating/elements/if.element.test.js +0 -78
- package/target/lib/templating/elements/if.element.test.js.map +0 -1
- package/target/lib/templating/elements/props.element.d.ts +0 -11
- package/target/lib/templating/elements/props.element.js +0 -92
- package/target/lib/templating/elements/props.element.js.map +0 -1
- package/target/lib/templating/elements/props.element.test.d.ts +0 -1
- package/target/lib/templating/elements/props.element.test.js +0 -53
- package/target/lib/templating/elements/props.element.test.js.map +0 -1
- package/target/lib/templating/elements/scope.d.ts +0 -13
- package/target/lib/templating/elements/scope.js +0 -59
- package/target/lib/templating/elements/scope.js.map +0 -1
- package/target/lib/templating/elements/value.element.d.ts +0 -9
- package/target/lib/templating/elements/value.element.js +0 -56
- package/target/lib/templating/elements/value.element.js.map +0 -1
- package/target/lib/templating/elements/value.element.test.d.ts +0 -1
- package/target/lib/templating/elements/value.element.test.js +0 -16
- package/target/lib/templating/elements/value.element.test.js.map +0 -1
- package/target/lib/templating/events.d.ts +0 -12
- package/target/lib/templating/events.js +0 -10
- package/target/lib/templating/events.js.map +0 -1
- package/target/lib/templating/token.d.ts +0 -8
- package/target/lib/templating/token.js +0 -27
- package/target/lib/templating/token.js.map +0 -1
- package/target/lib/templating/token.test.d.ts +0 -1
- package/target/lib/templating/token.test.js +0 -56
- package/target/lib/templating/token.test.js.map +0 -1
- package/target/lib/templating.d.ts +0 -2
- package/target/lib/templating.js +0 -3
- package/target/lib/templating.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@joist/element",
|
|
3
|
-
"version": "4.2.3-next.
|
|
3
|
+
"version": "4.2.3-next.21",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./target/lib.js",
|
|
6
6
|
"module": "./target/lib.js",
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
"src",
|
|
14
14
|
"target"
|
|
15
15
|
],
|
|
16
|
-
"sideEffects":
|
|
16
|
+
"sideEffects": [
|
|
17
|
+
"**/define.js"
|
|
18
|
+
],
|
|
17
19
|
"description": "Intelligently apply styles to WebComponents",
|
|
18
20
|
"repository": {
|
|
19
21
|
"type": "git",
|
package/src/lib/attr.test.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { expect } from "chai";
|
|
1
|
+
import { expect, assert } from "chai";
|
|
2
|
+
import { effect, observe } from "@joist/observable";
|
|
2
3
|
|
|
3
4
|
import { attr } from "./attr.js";
|
|
4
5
|
import { element } from "./element.js";
|
|
@@ -138,23 +139,6 @@ it("should throw an error for symbols with no description", async () => {
|
|
|
138
139
|
}).to.throw("Cannot handle Symbol property without description");
|
|
139
140
|
});
|
|
140
141
|
|
|
141
|
-
it("should not reflect property to attribute", async () => {
|
|
142
|
-
@element({
|
|
143
|
-
tagName: "attr-test-5",
|
|
144
|
-
})
|
|
145
|
-
class MyElement extends HTMLElement {
|
|
146
|
-
@attr({ reflect: false })
|
|
147
|
-
accessor value = "foo";
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const el = new MyElement();
|
|
151
|
-
el.value = "bar";
|
|
152
|
-
|
|
153
|
-
expect(el.value).to.equal("bar");
|
|
154
|
-
|
|
155
|
-
expect(el.hasAttribute("value")).to.be.false;
|
|
156
|
-
});
|
|
157
|
-
|
|
158
142
|
it("non reflective attributes should still read new attribute values", async () => {
|
|
159
143
|
@element({
|
|
160
144
|
tagName: "attr-test-6",
|
|
@@ -190,3 +174,70 @@ it("should allow a manually defined attribute name", async () => {
|
|
|
190
174
|
|
|
191
175
|
el.remove();
|
|
192
176
|
});
|
|
177
|
+
|
|
178
|
+
it("should update property when attribute changes", async () => {
|
|
179
|
+
@element({
|
|
180
|
+
tagName: "attr-test-8",
|
|
181
|
+
})
|
|
182
|
+
class MyElement extends HTMLElement {
|
|
183
|
+
@attr()
|
|
184
|
+
accessor value = "foo";
|
|
185
|
+
|
|
186
|
+
@attr()
|
|
187
|
+
accessor count = 0;
|
|
188
|
+
|
|
189
|
+
@attr()
|
|
190
|
+
accessor enabled = false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const el = new MyElement();
|
|
194
|
+
document.body.append(el);
|
|
195
|
+
|
|
196
|
+
// Test string property
|
|
197
|
+
el.setAttribute("value", "bar");
|
|
198
|
+
expect(el.value).to.equal("bar");
|
|
199
|
+
|
|
200
|
+
// Test number property
|
|
201
|
+
el.setAttribute("count", "42");
|
|
202
|
+
expect(el.count).to.equal(42);
|
|
203
|
+
|
|
204
|
+
// Test boolean property
|
|
205
|
+
el.setAttribute("enabled", "");
|
|
206
|
+
expect(el.enabled).to.equal(true);
|
|
207
|
+
|
|
208
|
+
el.removeAttribute("enabled");
|
|
209
|
+
expect(el.enabled).to.equal(false);
|
|
210
|
+
|
|
211
|
+
el.remove();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("setters should be called when attributes change", async () => {
|
|
215
|
+
let callCount = 0;
|
|
216
|
+
|
|
217
|
+
@element({
|
|
218
|
+
tagName: "attr-test-9",
|
|
219
|
+
})
|
|
220
|
+
class MyElement extends HTMLElement {
|
|
221
|
+
@attr()
|
|
222
|
+
@observe()
|
|
223
|
+
accessor value = "foo";
|
|
224
|
+
|
|
225
|
+
@effect()
|
|
226
|
+
onValueChange() {
|
|
227
|
+
callCount++;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const el = new MyElement();
|
|
232
|
+
document.body.append(el);
|
|
233
|
+
|
|
234
|
+
el.setAttribute("value", "bar");
|
|
235
|
+
|
|
236
|
+
// needs to wait for the mutation observer to run
|
|
237
|
+
await Promise.resolve();
|
|
238
|
+
await Promise.resolve();
|
|
239
|
+
|
|
240
|
+
assert.equal(callCount, 1);
|
|
241
|
+
|
|
242
|
+
el.remove();
|
|
243
|
+
});
|
package/src/lib/attr.ts
CHANGED
|
@@ -6,9 +6,9 @@ export interface AttrOpts {
|
|
|
6
6
|
reflect?: boolean;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export function attr(opts?: AttrOpts) {
|
|
10
|
-
return function attrDecorator
|
|
11
|
-
|
|
9
|
+
export function attr<This extends HTMLElement>(opts?: AttrOpts) {
|
|
10
|
+
return function attrDecorator(
|
|
11
|
+
base: ClassAccessorDecoratorTarget<This, unknown>,
|
|
12
12
|
ctx: ClassAccessorDecoratorContext<This>,
|
|
13
13
|
): ClassAccessorDecoratorResult<This, any> {
|
|
14
14
|
const attrName = opts?.name ?? parseAttrName(ctx.name);
|
|
@@ -19,8 +19,7 @@ export function attr(opts?: AttrOpts) {
|
|
|
19
19
|
propName: ctx.name,
|
|
20
20
|
observe: opts?.observed ?? true,
|
|
21
21
|
reflect,
|
|
22
|
-
|
|
23
|
-
setPropValue: set,
|
|
22
|
+
access: base,
|
|
24
23
|
});
|
|
25
24
|
|
|
26
25
|
return {
|
|
@@ -43,7 +42,7 @@ export function attr(opts?: AttrOpts) {
|
|
|
43
42
|
}
|
|
44
43
|
}
|
|
45
44
|
|
|
46
|
-
set.call(this, value);
|
|
45
|
+
base.set.call(this, value);
|
|
47
46
|
},
|
|
48
47
|
};
|
|
49
48
|
};
|
package/src/lib/element.test.ts
CHANGED
|
@@ -34,31 +34,39 @@ it("should write default value to attribute", async () => {
|
|
|
34
34
|
el.remove();
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
37
|
+
it("should register attributes", async () => {
|
|
38
|
+
const observedAttrs: string[] = [];
|
|
39
|
+
|
|
40
|
+
@element({
|
|
41
|
+
tagName: "element-2",
|
|
42
|
+
})
|
|
43
|
+
class MyElement extends HTMLElement {
|
|
44
|
+
@attr()
|
|
45
|
+
accessor value1 = "hello";
|
|
46
|
+
|
|
47
|
+
@attr()
|
|
48
|
+
accessor value2 = 0;
|
|
49
|
+
|
|
50
|
+
@attr()
|
|
51
|
+
accessor value3 = true;
|
|
52
|
+
|
|
53
|
+
@attr({ observed: false })
|
|
54
|
+
accessor value4 = "hello world";
|
|
55
|
+
|
|
56
|
+
attributeChangedCallback(name: string) {
|
|
57
|
+
observedAttrs.push(name);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const el = new MyElement();
|
|
62
|
+
|
|
63
|
+
el.setAttribute("value1", "foo");
|
|
64
|
+
el.setAttribute("value2", "1");
|
|
65
|
+
el.setAttribute("value3", "false");
|
|
66
|
+
el.setAttribute("value4", "bar");
|
|
67
|
+
|
|
68
|
+
expect(observedAttrs).to.deep.equal(["value1", "value2", "value3"]);
|
|
69
|
+
});
|
|
62
70
|
|
|
63
71
|
it("should attach shadow root when the shadow property exists", async () => {
|
|
64
72
|
@element({
|
package/src/lib/element.ts
CHANGED
|
@@ -47,28 +47,24 @@ export function element<T extends ElementConstructor>(opts?: ElementOpts) {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
attributeChangedCallback(
|
|
51
|
-
name: string,
|
|
52
|
-
oldValue: string,
|
|
53
|
-
newValue: string,
|
|
54
|
-
) {
|
|
50
|
+
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
|
|
55
51
|
const attr = meta.attrs.get(name);
|
|
56
52
|
const cbs = meta.attrChanges.get(name);
|
|
57
53
|
|
|
58
54
|
if (attr) {
|
|
59
55
|
if (oldValue !== newValue) {
|
|
60
|
-
const
|
|
56
|
+
const sourceValue = attr.access.get.call(this);
|
|
57
|
+
let value: string | number | boolean = newValue;
|
|
61
58
|
|
|
62
|
-
if (
|
|
59
|
+
if (typeof sourceValue === "boolean") {
|
|
63
60
|
// treat as boolean
|
|
64
|
-
|
|
65
|
-
} else if (typeof
|
|
61
|
+
value = newValue !== null;
|
|
62
|
+
} else if (typeof sourceValue === "number") {
|
|
66
63
|
// treat as number
|
|
67
|
-
|
|
68
|
-
} else {
|
|
69
|
-
// treat as string
|
|
70
|
-
attr.setPropValue.call(this, newValue);
|
|
64
|
+
value = Number(newValue);
|
|
71
65
|
}
|
|
66
|
+
|
|
67
|
+
attr.access.set.call(this, value);
|
|
72
68
|
}
|
|
73
69
|
|
|
74
70
|
if (cbs) {
|
|
@@ -86,13 +82,13 @@ export function element<T extends ElementConstructor>(opts?: ElementOpts) {
|
|
|
86
82
|
}
|
|
87
83
|
|
|
88
84
|
connectedCallback() {
|
|
89
|
-
if (this
|
|
85
|
+
if (!this.#abortController) {
|
|
86
|
+
this.#abortController = new AbortController();
|
|
87
|
+
|
|
90
88
|
for (const { event, cb, selector } of meta.listeners) {
|
|
91
89
|
const root = selector(this);
|
|
92
90
|
|
|
93
91
|
if (root) {
|
|
94
|
-
this.#abortController = new AbortController();
|
|
95
|
-
|
|
96
92
|
root.addEventListener(event, cb.bind(this), {
|
|
97
93
|
signal: this.#abortController.signal,
|
|
98
94
|
});
|
|
@@ -100,12 +96,12 @@ export function element<T extends ElementConstructor>(opts?: ElementOpts) {
|
|
|
100
96
|
throw new Error(`could not add listener to ${root}`);
|
|
101
97
|
}
|
|
102
98
|
}
|
|
99
|
+
}
|
|
103
100
|
|
|
104
|
-
|
|
101
|
+
reflectAttributeValues(this, meta.attrs);
|
|
105
102
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
103
|
+
if (super.connectedCallback) {
|
|
104
|
+
super.connectedCallback();
|
|
109
105
|
}
|
|
110
106
|
}
|
|
111
107
|
|
|
@@ -126,13 +122,10 @@ export function element<T extends ElementConstructor>(opts?: ElementOpts) {
|
|
|
126
122
|
};
|
|
127
123
|
}
|
|
128
124
|
|
|
129
|
-
function reflectAttributeValues<T extends HTMLElement>(
|
|
130
|
-
|
|
131
|
-
attrs: AttrMetadata,
|
|
132
|
-
) {
|
|
133
|
-
for (const [attrName, { getPropValue, reflect }] of attrs) {
|
|
125
|
+
function reflectAttributeValues<T extends HTMLElement>(el: T, attrs: AttrMetadata) {
|
|
126
|
+
for (const [attrName, { access, reflect }] of attrs) {
|
|
134
127
|
if (reflect) {
|
|
135
|
-
const value =
|
|
128
|
+
const value = access.get.call(el);
|
|
136
129
|
|
|
137
130
|
// reflect values back to attributes
|
|
138
131
|
if (value !== null && value !== undefined && value !== "") {
|
|
@@ -143,7 +136,8 @@ function reflectAttributeValues<T extends HTMLElement>(
|
|
|
143
136
|
el.setAttribute(attrName, "");
|
|
144
137
|
}
|
|
145
138
|
}
|
|
146
|
-
} else {
|
|
139
|
+
} else if (!el.hasAttribute(attrName)) {
|
|
140
|
+
// only set parent attribute if it doesn't exist
|
|
147
141
|
// set key/value attribute
|
|
148
142
|
const strValue = String(value);
|
|
149
143
|
|
package/src/lib/listen.test.ts
CHANGED
|
@@ -102,3 +102,78 @@ describe("@listen()", () => {
|
|
|
102
102
|
el.remove();
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
|
+
|
|
106
|
+
it("should remove event listeners during cleanup", () => {
|
|
107
|
+
let clickCount = 0;
|
|
108
|
+
|
|
109
|
+
@element({
|
|
110
|
+
tagName: "listener-cleanup",
|
|
111
|
+
shadowDom: [],
|
|
112
|
+
})
|
|
113
|
+
class MyElement extends HTMLElement {
|
|
114
|
+
@listen("click")
|
|
115
|
+
onClick1() {
|
|
116
|
+
clickCount++;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@listen("click")
|
|
120
|
+
onClick2() {
|
|
121
|
+
clickCount++;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const el = new MyElement();
|
|
126
|
+
document.body.append(el);
|
|
127
|
+
|
|
128
|
+
// First click should increment counter
|
|
129
|
+
el.shadowRoot?.dispatchEvent(new Event("click"));
|
|
130
|
+
assert.equal(clickCount, 2);
|
|
131
|
+
|
|
132
|
+
// Remove element which should cleanup listeners
|
|
133
|
+
el.remove();
|
|
134
|
+
|
|
135
|
+
// Second click after removal should not increment counter
|
|
136
|
+
el.shadowRoot?.dispatchEvent(new Event("click"));
|
|
137
|
+
assert.equal(clickCount, 2);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should not add event listeners multiple times when element is moved", () => {
|
|
141
|
+
let clickCount = 0;
|
|
142
|
+
|
|
143
|
+
@element({
|
|
144
|
+
tagName: "listener-move",
|
|
145
|
+
shadowDom: [],
|
|
146
|
+
})
|
|
147
|
+
class MyElement extends HTMLElement {
|
|
148
|
+
@listen("click")
|
|
149
|
+
onClick() {
|
|
150
|
+
clickCount++;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const el = new MyElement();
|
|
155
|
+
const container1 = document.createElement("div");
|
|
156
|
+
const container2 = document.createElement("div");
|
|
157
|
+
|
|
158
|
+
document.body.append(container1);
|
|
159
|
+
document.body.append(container2);
|
|
160
|
+
|
|
161
|
+
// Add to first container
|
|
162
|
+
container1.append(el);
|
|
163
|
+
|
|
164
|
+
// Click should increment once
|
|
165
|
+
el.shadowRoot?.dispatchEvent(new Event("click"));
|
|
166
|
+
assert.equal(clickCount, 1);
|
|
167
|
+
|
|
168
|
+
// Move to second container
|
|
169
|
+
container2.append(el);
|
|
170
|
+
|
|
171
|
+
// Click should still only increment once
|
|
172
|
+
el.shadowRoot?.dispatchEvent(new Event("click"));
|
|
173
|
+
assert.equal(clickCount, 2);
|
|
174
|
+
|
|
175
|
+
// Cleanup
|
|
176
|
+
el.remove();
|
|
177
|
+
container1.remove();
|
|
178
|
+
container2.remove();
|
|
179
|
+
});
|
package/src/lib/metadata.ts
CHANGED
|
@@ -4,8 +4,10 @@ export interface AttrDef {
|
|
|
4
4
|
propName: string | symbol;
|
|
5
5
|
observe: boolean;
|
|
6
6
|
reflect: boolean;
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
access: {
|
|
8
|
+
get: () => unknown;
|
|
9
|
+
set: (value: unknown) => void;
|
|
10
|
+
};
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
export type ListenerSelector<T> = (el: T) => EventTarget | null;
|
|
@@ -16,11 +18,7 @@ export interface Listener<T> {
|
|
|
16
18
|
selector: ListenerSelector<T>;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
export type AttrChangedCallback = (
|
|
20
|
-
name: string,
|
|
21
|
-
oldValue: string,
|
|
22
|
-
newValue: string,
|
|
23
|
-
) => void;
|
|
21
|
+
export type AttrChangedCallback = (name: string, oldValue: string, newValue: string) => void;
|
|
24
22
|
|
|
25
23
|
export class AttrMetadata extends Map<string, AttrDef> {}
|
|
26
24
|
export class AttrChangeMetadata extends Map<string, Set<AttrChangedCallback>> {}
|
package/target/lib/attr.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export interface AttrOpts {
|
|
|
3
3
|
observed?: boolean;
|
|
4
4
|
reflect?: boolean;
|
|
5
5
|
}
|
|
6
|
-
export declare function attr
|
|
6
|
+
export declare function attr<This extends HTMLElement>(opts?: AttrOpts): (base: ClassAccessorDecoratorTarget<This, unknown>, ctx: ClassAccessorDecoratorContext<This>) => ClassAccessorDecoratorResult<This, any>;
|
package/target/lib/attr.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { metadataStore } from "./metadata.js";
|
|
2
2
|
export function attr(opts) {
|
|
3
|
-
return function attrDecorator(
|
|
3
|
+
return function attrDecorator(base, ctx) {
|
|
4
4
|
const attrName = opts?.name ?? parseAttrName(ctx.name);
|
|
5
5
|
const meta = metadataStore.read(ctx.metadata);
|
|
6
6
|
const reflect = opts?.reflect ?? true;
|
|
@@ -8,8 +8,7 @@ export function attr(opts) {
|
|
|
8
8
|
propName: ctx.name,
|
|
9
9
|
observe: opts?.observed ?? true,
|
|
10
10
|
reflect,
|
|
11
|
-
|
|
12
|
-
setPropValue: set,
|
|
11
|
+
access: base,
|
|
13
12
|
});
|
|
14
13
|
return {
|
|
15
14
|
set(value) {
|
|
@@ -31,7 +30,7 @@ export function attr(opts) {
|
|
|
31
30
|
}
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
|
-
set.call(this, value);
|
|
33
|
+
base.set.call(this, value);
|
|
35
34
|
},
|
|
36
35
|
};
|
|
37
36
|
};
|
package/target/lib/attr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attr.js","sourceRoot":"","sources":["../../src/lib/attr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAQ9C,MAAM,UAAU,IAAI,
|
|
1
|
+
{"version":3,"file":"attr.js","sourceRoot":"","sources":["../../src/lib/attr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAQ9C,MAAM,UAAU,IAAI,CAA2B,IAAe;IAC5D,OAAO,SAAS,aAAa,CAC3B,IAAiD,EACjD,GAAwC;QAExC,MAAM,QAAQ,GAAG,IAAI,EAAE,IAAI,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvD,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAO,GAAG,CAAC,QAAQ,CAAC,CAAC;QACpD,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC;QAEtC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE;YACvB,QAAQ,EAAE,GAAG,CAAC,IAAI;YAClB,OAAO,EAAE,IAAI,EAAE,QAAQ,IAAI,IAAI;YAC/B,OAAO;YACP,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;QAEH,OAAO;YACL,GAAG,CAAC,KAAc;gBAChB,IAAI,OAAO,EAAE,CAAC;oBACZ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;wBACnB,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;4BACjC,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;wBAClC,CAAC;oBACH,CAAC;yBAAM,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;wBAC3B,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;4BAChC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;wBACjC,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;wBAE/B,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,QAAQ,EAAE,CAAC;4BAC7C,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;wBACxC,CAAC;oBACH,CAAC;gBACH,CAAC;gBAED,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC7B,CAAC;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,GAAoB;IACzC,IAAI,KAAa,CAAC;IAElB,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;YACpB,KAAK,GAAG,GAAG,CAAC,WAAW,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,GAAG,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAClD,CAAC"}
|
package/target/lib/attr.test.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { __esDecorate, __propKey, __runInitializers } from "tslib";
|
|
2
|
-
import { expect } from "chai";
|
|
2
|
+
import { expect, assert } from "chai";
|
|
3
|
+
import { effect, observe } from "@joist/observable";
|
|
3
4
|
import { attr } from "./attr.js";
|
|
4
5
|
import { element } from "./element.js";
|
|
5
6
|
it("should read and parse the correct values", () => {
|
|
@@ -288,10 +289,10 @@ it("should throw an error for symbols with no description", async () => {
|
|
|
288
289
|
new MyElement();
|
|
289
290
|
}).to.throw("Cannot handle Symbol property without description");
|
|
290
291
|
});
|
|
291
|
-
it("should
|
|
292
|
+
it("non reflective attributes should still read new attribute values", async () => {
|
|
292
293
|
let MyElement = (() => {
|
|
293
294
|
let _classDecorators = [element({
|
|
294
|
-
tagName: "attr-test-
|
|
295
|
+
tagName: "attr-test-6",
|
|
295
296
|
})];
|
|
296
297
|
let _classDescriptor;
|
|
297
298
|
let _classExtraInitializers = [];
|
|
@@ -322,14 +323,13 @@ it("should not reflect property to attribute", async () => {
|
|
|
322
323
|
return MyElement = _classThis;
|
|
323
324
|
})();
|
|
324
325
|
const el = new MyElement();
|
|
325
|
-
el.value
|
|
326
|
+
el.setAttribute("value", "bar");
|
|
326
327
|
expect(el.value).to.equal("bar");
|
|
327
|
-
expect(el.hasAttribute("value")).to.be.false;
|
|
328
328
|
});
|
|
329
|
-
it("
|
|
329
|
+
it("should allow a manually defined attribute name", async () => {
|
|
330
330
|
let MyElement = (() => {
|
|
331
331
|
let _classDecorators = [element({
|
|
332
|
-
tagName: "attr-test-
|
|
332
|
+
tagName: "attr-test-7",
|
|
333
333
|
})];
|
|
334
334
|
let _classDescriptor;
|
|
335
335
|
let _classExtraInitializers = [];
|
|
@@ -342,14 +342,16 @@ it("non reflective attributes should still read new attribute values", async ()
|
|
|
342
342
|
static { _classThis = this; }
|
|
343
343
|
static {
|
|
344
344
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
345
|
-
_value_decorators = [attr({
|
|
345
|
+
_value_decorators = [attr({
|
|
346
|
+
name: "aria-label",
|
|
347
|
+
})];
|
|
346
348
|
__esDecorate(this, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
|
|
347
349
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
348
350
|
MyElement = _classThis = _classDescriptor.value;
|
|
349
351
|
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
350
352
|
__runInitializers(_classThis, _classExtraInitializers);
|
|
351
353
|
}
|
|
352
|
-
#value_accessor_storage = __runInitializers(this, _value_initializers, "
|
|
354
|
+
#value_accessor_storage = __runInitializers(this, _value_initializers, "");
|
|
353
355
|
get value() { return this.#value_accessor_storage; }
|
|
354
356
|
set value(value) { this.#value_accessor_storage = value; }
|
|
355
357
|
constructor() {
|
|
@@ -360,37 +362,106 @@ it("non reflective attributes should still read new attribute values", async ()
|
|
|
360
362
|
return MyElement = _classThis;
|
|
361
363
|
})();
|
|
362
364
|
const el = new MyElement();
|
|
365
|
+
el.setAttribute("aria-label", "TEST");
|
|
366
|
+
document.body.append(el);
|
|
367
|
+
expect(el.value).to.equal("TEST");
|
|
368
|
+
el.remove();
|
|
369
|
+
});
|
|
370
|
+
it("should update property when attribute changes", async () => {
|
|
371
|
+
let MyElement = (() => {
|
|
372
|
+
let _classDecorators = [element({
|
|
373
|
+
tagName: "attr-test-8",
|
|
374
|
+
})];
|
|
375
|
+
let _classDescriptor;
|
|
376
|
+
let _classExtraInitializers = [];
|
|
377
|
+
let _classThis;
|
|
378
|
+
let _classSuper = HTMLElement;
|
|
379
|
+
let _value_decorators;
|
|
380
|
+
let _value_initializers = [];
|
|
381
|
+
let _value_extraInitializers = [];
|
|
382
|
+
let _count_decorators;
|
|
383
|
+
let _count_initializers = [];
|
|
384
|
+
let _count_extraInitializers = [];
|
|
385
|
+
let _enabled_decorators;
|
|
386
|
+
let _enabled_initializers = [];
|
|
387
|
+
let _enabled_extraInitializers = [];
|
|
388
|
+
var MyElement = class extends _classSuper {
|
|
389
|
+
static { _classThis = this; }
|
|
390
|
+
static {
|
|
391
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
392
|
+
_value_decorators = [attr()];
|
|
393
|
+
_count_decorators = [attr()];
|
|
394
|
+
_enabled_decorators = [attr()];
|
|
395
|
+
__esDecorate(this, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
|
|
396
|
+
__esDecorate(this, null, _count_decorators, { kind: "accessor", name: "count", static: false, private: false, access: { has: obj => "count" in obj, get: obj => obj.count, set: (obj, value) => { obj.count = value; } }, metadata: _metadata }, _count_initializers, _count_extraInitializers);
|
|
397
|
+
__esDecorate(this, null, _enabled_decorators, { kind: "accessor", name: "enabled", static: false, private: false, access: { has: obj => "enabled" in obj, get: obj => obj.enabled, set: (obj, value) => { obj.enabled = value; } }, metadata: _metadata }, _enabled_initializers, _enabled_extraInitializers);
|
|
398
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
399
|
+
MyElement = _classThis = _classDescriptor.value;
|
|
400
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
401
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
402
|
+
}
|
|
403
|
+
#value_accessor_storage = __runInitializers(this, _value_initializers, "foo");
|
|
404
|
+
get value() { return this.#value_accessor_storage; }
|
|
405
|
+
set value(value) { this.#value_accessor_storage = value; }
|
|
406
|
+
#count_accessor_storage = (__runInitializers(this, _value_extraInitializers), __runInitializers(this, _count_initializers, 0));
|
|
407
|
+
get count() { return this.#count_accessor_storage; }
|
|
408
|
+
set count(value) { this.#count_accessor_storage = value; }
|
|
409
|
+
#enabled_accessor_storage = (__runInitializers(this, _count_extraInitializers), __runInitializers(this, _enabled_initializers, false));
|
|
410
|
+
get enabled() { return this.#enabled_accessor_storage; }
|
|
411
|
+
set enabled(value) { this.#enabled_accessor_storage = value; }
|
|
412
|
+
constructor() {
|
|
413
|
+
super(...arguments);
|
|
414
|
+
__runInitializers(this, _enabled_extraInitializers);
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
return MyElement = _classThis;
|
|
418
|
+
})();
|
|
419
|
+
const el = new MyElement();
|
|
420
|
+
document.body.append(el);
|
|
363
421
|
el.setAttribute("value", "bar");
|
|
364
422
|
expect(el.value).to.equal("bar");
|
|
423
|
+
el.setAttribute("count", "42");
|
|
424
|
+
expect(el.count).to.equal(42);
|
|
425
|
+
el.setAttribute("enabled", "");
|
|
426
|
+
expect(el.enabled).to.equal(true);
|
|
427
|
+
el.removeAttribute("enabled");
|
|
428
|
+
expect(el.enabled).to.equal(false);
|
|
429
|
+
el.remove();
|
|
365
430
|
});
|
|
366
|
-
it("should
|
|
431
|
+
it("setters should be called when attributes change", async () => {
|
|
432
|
+
let callCount = 0;
|
|
367
433
|
let MyElement = (() => {
|
|
368
434
|
let _classDecorators = [element({
|
|
369
|
-
tagName: "attr-test-
|
|
435
|
+
tagName: "attr-test-9",
|
|
370
436
|
})];
|
|
371
437
|
let _classDescriptor;
|
|
372
438
|
let _classExtraInitializers = [];
|
|
373
439
|
let _classThis;
|
|
374
440
|
let _classSuper = HTMLElement;
|
|
441
|
+
let _instanceExtraInitializers = [];
|
|
375
442
|
let _value_decorators;
|
|
376
443
|
let _value_initializers = [];
|
|
377
444
|
let _value_extraInitializers = [];
|
|
445
|
+
let _onValueChange_decorators;
|
|
378
446
|
var MyElement = class extends _classSuper {
|
|
379
447
|
static { _classThis = this; }
|
|
380
448
|
static {
|
|
381
449
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
382
|
-
_value_decorators = [attr(
|
|
383
|
-
|
|
384
|
-
})];
|
|
450
|
+
_value_decorators = [attr(), observe()];
|
|
451
|
+
_onValueChange_decorators = [effect()];
|
|
385
452
|
__esDecorate(this, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
|
|
453
|
+
__esDecorate(this, null, _onValueChange_decorators, { kind: "method", name: "onValueChange", static: false, private: false, access: { has: obj => "onValueChange" in obj, get: obj => obj.onValueChange }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
386
454
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
387
455
|
MyElement = _classThis = _classDescriptor.value;
|
|
388
456
|
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
389
457
|
__runInitializers(_classThis, _classExtraInitializers);
|
|
390
458
|
}
|
|
391
|
-
#value_accessor_storage = __runInitializers(this, _value_initializers, "");
|
|
459
|
+
#value_accessor_storage = (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _value_initializers, "foo"));
|
|
392
460
|
get value() { return this.#value_accessor_storage; }
|
|
393
461
|
set value(value) { this.#value_accessor_storage = value; }
|
|
462
|
+
onValueChange() {
|
|
463
|
+
callCount++;
|
|
464
|
+
}
|
|
394
465
|
constructor() {
|
|
395
466
|
super(...arguments);
|
|
396
467
|
__runInitializers(this, _value_extraInitializers);
|
|
@@ -399,9 +470,11 @@ it("should allow a manually defined attribute name", async () => {
|
|
|
399
470
|
return MyElement = _classThis;
|
|
400
471
|
})();
|
|
401
472
|
const el = new MyElement();
|
|
402
|
-
el.setAttribute("aria-label", "TEST");
|
|
403
473
|
document.body.append(el);
|
|
404
|
-
|
|
474
|
+
el.setAttribute("value", "bar");
|
|
475
|
+
await Promise.resolve();
|
|
476
|
+
await Promise.resolve();
|
|
477
|
+
assert.equal(callCount, 1);
|
|
405
478
|
el.remove();
|
|
406
479
|
});
|
|
407
480
|
//# sourceMappingURL=attr.test.js.map
|