@w-lfpup/wctk 0.2.3 → 0.2.4
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 +1 -1
- package/README.md +3 -3
- package/dist/microtask.d.ts +4 -2
- package/dist/microtask.js +2 -2
- package/dist/query_selector.js +5 -5
- package/dist/wc.js +4 -7
- package/package.json +6 -6
- package/src/microtask.ts +5 -3
- package/src/query_selector.ts +6 -4
- package/src/wc.ts +4 -9
- package/docs/events.md +0 -93
- package/docs/microtask.md +0 -32
- package/docs/query_selector.md +0 -37
- package/docs/wc.md +0 -55
- package/examples/counter/index.html +0 -30
- package/examples/counter/mod.js +0 -37
- package/examples/counter/mod.ts +0 -52
- package/examples/form_associated/index.html +0 -31
- package/examples/form_associated/mod.js +0 -11
- package/examples/form_associated/mod.ts +0 -20
- package/examples/form_associated/text_input.js +0 -22
- package/examples/form_associated/text_input.ts +0 -31
- package/examples/stopwatch/index.html +0 -32
- package/examples/stopwatch/mod.js +0 -13
- package/examples/stopwatch/mod.ts +0 -13
- package/examples/stopwatch/stopwatch.js +0 -48
- package/examples/stopwatch/stopwatch.ts +0 -70
- package/examples/tsconfig.json +0 -11
- package/tests/dist/events.tests.js +0 -60
- package/tests/dist/microtask.tests.js +0 -38
- package/tests/dist/mod.js +0 -10
- package/tests/dist/query_selector.tests.js +0 -43
- package/tests/dist/wc.tests.js +0 -41
- package/tests/src/events.tests.ts +0 -73
- package/tests/src/microtask.tests.ts +0 -54
- package/tests/src/mod.ts +0 -11
- package/tests/src/query_selector.tests.ts +0 -46
- package/tests/src/wc.tests.ts +0 -52
- package/tests/tsconfig.json +0 -8
- /package/.github/workflows/{builds.yml → tests.yml} +0 -0
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -6,6 +6,9 @@ An SSR friendly (w)eb(c)omponent (t)ool (k)it without dependencies.
|
|
|
6
6
|
|
|
7
7
|
## About
|
|
8
8
|
|
|
9
|
+
The `wctk` is a collection of bare-metal facades over vanilla browser apis. They provide the basics for
|
|
10
|
+
events, reactivity, and forms.
|
|
11
|
+
|
|
9
12
|
There are no base classes, decorators, or mixins.
|
|
10
13
|
|
|
11
14
|
All features are compositional and designed for SSR and [declarative shadow dom](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM#declaratively_with_html).
|
|
@@ -59,9 +62,6 @@ The following examples demonstrate several common SSR use cases:
|
|
|
59
62
|
|
|
60
63
|
## Design Goals
|
|
61
64
|
|
|
62
|
-
The `wctk` is a collection of bare-metal facades over vanilla browser apis. They provide the basics for
|
|
63
|
-
events, reactivity, and forms.
|
|
64
|
-
|
|
65
65
|
If you know vanilla javascript and the DOM you are good to go.
|
|
66
66
|
|
|
67
67
|
The `wctk` is designed with SSR and declarative shadow dom in mind. Developers
|
package/dist/microtask.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export interface MicrotaskInterface {
|
|
2
2
|
queue(): void;
|
|
3
3
|
}
|
|
4
|
-
|
|
4
|
+
interface Callback {
|
|
5
|
+
(): void;
|
|
6
|
+
}
|
|
5
7
|
export declare class Microtask implements MicrotaskInterface {
|
|
6
8
|
#private;
|
|
7
|
-
constructor(callback: Callback);
|
|
8
9
|
queue: () => void;
|
|
10
|
+
constructor(callback: Callback);
|
|
9
11
|
}
|
|
10
12
|
export {};
|
package/dist/microtask.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
export class Microtask {
|
|
2
2
|
#queued = false;
|
|
3
3
|
#callback;
|
|
4
|
+
#microtask = this.#unboundMicrotask.bind(this);
|
|
5
|
+
queue = this.#queue.bind(this);
|
|
4
6
|
constructor(callback) {
|
|
5
7
|
this.#callback = callback;
|
|
6
8
|
}
|
|
7
|
-
queue = this.#queue.bind(this);
|
|
8
9
|
#queue() {
|
|
9
10
|
if (this.#queued)
|
|
10
11
|
return;
|
|
11
12
|
this.#queued = true;
|
|
12
13
|
window.queueMicrotask(this.#microtask);
|
|
13
14
|
}
|
|
14
|
-
#microtask = this.#unboundMicrotask.bind(this);
|
|
15
15
|
#unboundMicrotask() {
|
|
16
16
|
this.#queued = false;
|
|
17
17
|
this.#callback();
|
package/dist/query_selector.js
CHANGED
|
@@ -13,11 +13,11 @@ export class QuerySelector {
|
|
|
13
13
|
return query;
|
|
14
14
|
}
|
|
15
15
|
querySelectorAll(selector) {
|
|
16
|
-
let
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
let query = this.#queryAlls.get(selector);
|
|
17
|
+
if (!query) {
|
|
18
|
+
query = Array.from(this.#parentNode.querySelectorAll(selector));
|
|
19
|
+
this.#queryAlls.set(selector, query);
|
|
20
|
+
}
|
|
21
21
|
return query;
|
|
22
22
|
}
|
|
23
23
|
deleteAll() {
|
package/dist/wc.js
CHANGED
|
@@ -8,13 +8,10 @@ export class Wc {
|
|
|
8
8
|
constructor(params) {
|
|
9
9
|
let { adoptedStyleSheets, host, formState, formValue, shadowRootInit } = params;
|
|
10
10
|
this.#internals = host.attachInternals();
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
this.#
|
|
14
|
-
|
|
15
|
-
else {
|
|
16
|
-
this.#shadowRoot = host.attachShadow(shadowRootInit ?? shadowRootInitFallback);
|
|
17
|
-
}
|
|
11
|
+
this.#declarative = null !== this.#internals.shadowRoot;
|
|
12
|
+
this.#shadowRoot =
|
|
13
|
+
this.#internals.shadowRoot ??
|
|
14
|
+
host.attachShadow(shadowRootInit ?? shadowRootInitFallback);
|
|
18
15
|
this.adoptedStyleSheets = adoptedStyleSheets ?? [];
|
|
19
16
|
if (formValue)
|
|
20
17
|
this.setFormValue(formValue, formState);
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"main": "dist/mod.js",
|
|
5
5
|
"description": "A bare-metal webcomponent toolkit",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
7
|
-
"version": "0.2.
|
|
7
|
+
"version": "0.2.4",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"prepare": "npm run build",
|
|
10
10
|
"build": "npm run build:src && npm run build:tests && npm run build:examples",
|
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
"test:browsers": "npx jackrabbit_webdriver jr.json tests/dist/mod.js",
|
|
16
16
|
"format": "prettier --write ./"
|
|
17
17
|
},
|
|
18
|
-
"devDependencies": {
|
|
19
|
-
"@w-lfpup/jackrabbit": "^0.3.2",
|
|
20
|
-
"prettier": "^3.8.3",
|
|
21
|
-
"typescript": "^6.0.2"
|
|
22
|
-
},
|
|
23
18
|
"repository": {
|
|
24
19
|
"type": "git",
|
|
25
20
|
"url": "git+https://github.com/w-lfpup/wctk-js.git"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@w-lfpup/jackrabbit": "^0.3.3",
|
|
24
|
+
"prettier": "^3.8.4",
|
|
25
|
+
"typescript": "^6.0.3"
|
|
26
26
|
}
|
|
27
27
|
}
|
package/src/microtask.ts
CHANGED
|
@@ -2,17 +2,20 @@ export interface MicrotaskInterface {
|
|
|
2
2
|
queue(): void;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
interface Callback {
|
|
6
|
+
(): void;
|
|
7
|
+
}
|
|
6
8
|
|
|
7
9
|
export class Microtask implements MicrotaskInterface {
|
|
8
10
|
#queued = false;
|
|
9
11
|
#callback: Callback;
|
|
12
|
+
#microtask = this.#unboundMicrotask.bind(this);
|
|
13
|
+
queue = this.#queue.bind(this);
|
|
10
14
|
|
|
11
15
|
constructor(callback: Callback) {
|
|
12
16
|
this.#callback = callback;
|
|
13
17
|
}
|
|
14
18
|
|
|
15
|
-
queue = this.#queue.bind(this);
|
|
16
19
|
#queue() {
|
|
17
20
|
if (this.#queued) return;
|
|
18
21
|
this.#queued = true;
|
|
@@ -20,7 +23,6 @@ export class Microtask implements MicrotaskInterface {
|
|
|
20
23
|
window.queueMicrotask(this.#microtask);
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
#microtask = this.#unboundMicrotask.bind(this);
|
|
24
26
|
#unboundMicrotask() {
|
|
25
27
|
this.#queued = false;
|
|
26
28
|
this.#callback();
|
package/src/query_selector.ts
CHANGED
|
@@ -18,15 +18,17 @@ export class QuerySelector implements QuerySelectorInterface {
|
|
|
18
18
|
|
|
19
19
|
let query = this.#parentNode.querySelector(selector) ?? undefined;
|
|
20
20
|
this.#queries.set(selector, query);
|
|
21
|
+
|
|
21
22
|
return query;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
querySelectorAll(selector: string): Element[] {
|
|
25
|
-
let
|
|
26
|
-
if (
|
|
26
|
+
let query = this.#queryAlls.get(selector);
|
|
27
|
+
if (!query) {
|
|
28
|
+
query = Array.from(this.#parentNode.querySelectorAll(selector));
|
|
29
|
+
this.#queryAlls.set(selector, query);
|
|
30
|
+
}
|
|
27
31
|
|
|
28
|
-
let query = Array.from(this.#parentNode.querySelectorAll(selector));
|
|
29
|
-
this.#queryAlls.set(selector, query);
|
|
30
32
|
return query;
|
|
31
33
|
}
|
|
32
34
|
|
package/src/wc.ts
CHANGED
|
@@ -36,15 +36,10 @@ export class Wc implements WcInterface {
|
|
|
36
36
|
params;
|
|
37
37
|
|
|
38
38
|
this.#internals = host.attachInternals();
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
this.#
|
|
42
|
-
|
|
43
|
-
} else {
|
|
44
|
-
this.#shadowRoot = host.attachShadow(
|
|
45
|
-
shadowRootInit ?? shadowRootInitFallback,
|
|
46
|
-
);
|
|
47
|
-
}
|
|
39
|
+
this.#declarative = null !== this.#internals.shadowRoot;
|
|
40
|
+
this.#shadowRoot =
|
|
41
|
+
this.#internals.shadowRoot ??
|
|
42
|
+
host.attachShadow(shadowRootInit ?? shadowRootInitFallback);
|
|
48
43
|
|
|
49
44
|
this.adoptedStyleSheets = adoptedStyleSheets ?? [];
|
|
50
45
|
if (formValue) this.setFormValue(formValue, formState);
|
package/docs/events.md
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# Events Controller
|
|
2
|
-
|
|
3
|
-
Add event listeners to webcomponents.
|
|
4
|
-
|
|
5
|
-
## How to use
|
|
6
|
-
|
|
7
|
-
Add an `Events` controller to a webcomponent. Use a params object on instantiation.
|
|
8
|
-
|
|
9
|
-
### Params
|
|
10
|
-
|
|
11
|
-
An Events `params` object has three properties:
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
interface EventParams {
|
|
15
|
-
connected?: boolean;
|
|
16
|
-
listeners: Record<string, EventListenerOrEventListenerObject>;
|
|
17
|
-
target: EventTarget;
|
|
18
|
-
}
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
The `Events` controller adds event listeners on a `target` node.
|
|
22
|
-
|
|
23
|
-
The `target` node can be a shadowRoot, a document, or the custom element itself.
|
|
24
|
-
|
|
25
|
-
### Controller
|
|
26
|
-
|
|
27
|
-
Here is an example of using the `Events` controller.
|
|
28
|
-
|
|
29
|
-
```ts
|
|
30
|
-
import { Events, Wc } from "wctk";
|
|
31
|
-
|
|
32
|
-
class MyElement extends HTMLElement {
|
|
33
|
-
#wc = new Wc({ this: host });
|
|
34
|
-
|
|
35
|
-
#ec = new Events({
|
|
36
|
-
target: this.#wc.shadowRoot,
|
|
37
|
-
listeners: {
|
|
38
|
-
click: this.#onClick.bind(this),
|
|
39
|
-
keydown: this.#onKeyDown.bind(this),
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
#onClick(e: PointerEvent) {
|
|
44
|
-
// do something with pointer events here!
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
#onKeyDown(e: KeyboardEvent) {
|
|
48
|
-
// do something with key events here!
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// lifecycle method
|
|
52
|
-
connectedCallback() {
|
|
53
|
-
this.#ec.connect();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// lifecycle method
|
|
57
|
-
disconnectedCallback() {
|
|
58
|
-
this.#ec.disconnect();
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### Shortcut life-cycle methods
|
|
64
|
-
|
|
65
|
-
In the example below, the `connected` property is set to true and listeners are immediately added to the `target`.
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
import { Events, Wc } from "wctk";
|
|
69
|
-
|
|
70
|
-
class MyElement extends HTMLElement {
|
|
71
|
-
#wc = new Wc({ this: host });
|
|
72
|
-
#ec = new Events({
|
|
73
|
-
connected: true,
|
|
74
|
-
target: this.#wc.shadowRoot,
|
|
75
|
-
listeners: {
|
|
76
|
-
click: this.#onClick.bind(this),
|
|
77
|
-
keydown: this.#onKeyDown.bind(this),
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
#onClick(e: PointerEvent) {
|
|
82
|
-
// do something with pointer events here!
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
#onKeyDown(e: KeyEvent) {
|
|
86
|
-
// do something with key events here!
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### More complex interactions
|
|
92
|
-
|
|
93
|
-
If your component requires more complex declarative interactions, consider [superaction](https://github.com/w-lfpup/superaction-js/)
|
package/docs/microtask.md
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# Microtask Controller
|
|
2
|
-
|
|
3
|
-
Add callbacks to the `microtask queue`.
|
|
4
|
-
|
|
5
|
-
## How to use
|
|
6
|
-
|
|
7
|
-
Add a `Microtask` controller to a webcomponent. Provide a callback.
|
|
8
|
-
|
|
9
|
-
Call `Microtask.queue()` to push the callback to the microtask queue.
|
|
10
|
-
|
|
11
|
-
In the example below, a `Microtask` controller queues a render when the `width` attribute changes.
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
import { Microtask } from "wctk";
|
|
15
|
-
|
|
16
|
-
class MyElement extends HTMLElement {
|
|
17
|
-
static observedAttributes = ["width"];
|
|
18
|
-
|
|
19
|
-
#rc = new Microtask(this.#render.bind(this));
|
|
20
|
-
|
|
21
|
-
#render() {
|
|
22
|
-
// update DOM here!
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// lifecycle method
|
|
26
|
-
attributeChangedCallback() {
|
|
27
|
-
this.#rc.queue();
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
The `Microtask.queue()` method can be called multiple times per event loop but the callback will only be called _once_ during the microtaskqueue phase of the event-loop.
|
package/docs/query_selector.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# QuerySelector Controller
|
|
2
|
-
|
|
3
|
-
Lazily map selector queries.
|
|
4
|
-
|
|
5
|
-
## How to use
|
|
6
|
-
|
|
7
|
-
Add a `QuerySelector` controller to a webcomponent.
|
|
8
|
-
|
|
9
|
-
```html
|
|
10
|
-
<my-element>
|
|
11
|
-
<template shadowrootmode="closed">
|
|
12
|
-
<span greeting>UwU</span>
|
|
13
|
-
</template>
|
|
14
|
-
</my-element>
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Every query is cached. Call `<QuerySelector>.deleteAll()` to reset the cache (helpful after a new render).
|
|
18
|
-
|
|
19
|
-
```ts
|
|
20
|
-
import { QuerySelector } from "wctk";
|
|
21
|
-
|
|
22
|
-
class MyElement extends HTMLElement {
|
|
23
|
-
#wc = new Wc({ host: this });
|
|
24
|
-
#qc = new QuerySelector(this.#wc.shadowRoot);
|
|
25
|
-
|
|
26
|
-
showcaseApi() {
|
|
27
|
-
// first Element or undefined
|
|
28
|
-
let greeting = this.#qc.querySelector("[greeting]");
|
|
29
|
-
|
|
30
|
-
// Element[]
|
|
31
|
-
let greetings = this.#qc.querySelectorAll("[greeting]");
|
|
32
|
-
|
|
33
|
-
// create new cache
|
|
34
|
-
this.#qc.deleteAll();
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
```
|
package/docs/wc.md
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Wc Controller
|
|
2
|
-
|
|
3
|
-
Build a webcomponent.
|
|
4
|
-
|
|
5
|
-
## How to use
|
|
6
|
-
|
|
7
|
-
Add a `Wc` controller to a custom element:
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
import { Wc } from "wctk";
|
|
11
|
-
|
|
12
|
-
class MyElement extends HTMLElement {
|
|
13
|
-
#wc = new Wc({ host: this });
|
|
14
|
-
}
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Adopted stylesheets and form values
|
|
18
|
-
|
|
19
|
-
The `Wc` controller interfaces with core web componet APIs like adopted stylesheets and form values.
|
|
20
|
-
|
|
21
|
-
```ts
|
|
22
|
-
class MyElement extends HTMLElement {
|
|
23
|
-
#wc = new Wc({
|
|
24
|
-
host: this,
|
|
25
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow#options
|
|
26
|
-
shadowRootInit: { mode: "open" },
|
|
27
|
-
adoptedStyleSheets: [],
|
|
28
|
-
formValue: "^_^",
|
|
29
|
-
formState: ":3",
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
showcaseApi() {
|
|
33
|
-
// true if declarative shadow dom is present
|
|
34
|
-
this.#wc.delcarative;
|
|
35
|
-
|
|
36
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot
|
|
37
|
-
this.#wc.shadowRoot;
|
|
38
|
-
|
|
39
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/adoptedStyleSheets
|
|
40
|
-
this.#wc.adopedStylesheets;
|
|
41
|
-
|
|
42
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue
|
|
43
|
-
this.#wc.setFormValue(value, state);
|
|
44
|
-
|
|
45
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/checkValidity
|
|
46
|
-
this.#wc.checkValidity();
|
|
47
|
-
|
|
48
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/reportValidity
|
|
49
|
-
this.#wc.reportValidity();
|
|
50
|
-
|
|
51
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setValidity
|
|
52
|
-
this.#wc.setValidity(flags, message);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
```
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en-us">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset=utf-8>
|
|
5
|
-
<meta name=viewport content="width=device-width, initial-scale=1">
|
|
6
|
-
<script type="importmap">
|
|
7
|
-
{
|
|
8
|
-
"imports": {
|
|
9
|
-
"wctk": "../../dist/mod.js"
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
</script>
|
|
13
|
-
<script src="./mod.js" type=module></script>
|
|
14
|
-
</head>
|
|
15
|
-
<body>
|
|
16
|
-
<main>
|
|
17
|
-
<!-- webcomponent -->
|
|
18
|
-
<counter-wc>
|
|
19
|
-
<template shadowrootmode="closed">
|
|
20
|
-
<button decrease>-</button>
|
|
21
|
-
<slot></slot>
|
|
22
|
-
<button increase>+</button>
|
|
23
|
-
</template>
|
|
24
|
-
|
|
25
|
-
<!-- DOM content -->
|
|
26
|
-
<span>42</span>
|
|
27
|
-
</counter-wc>
|
|
28
|
-
</main>
|
|
29
|
-
</body>
|
|
30
|
-
</html>
|
package/examples/counter/mod.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { Wc, Events } from "wctk";
|
|
2
|
-
class Counter extends HTMLElement {
|
|
3
|
-
#wc = new Wc({ host: this });
|
|
4
|
-
#ev = new Events({
|
|
5
|
-
connected: true,
|
|
6
|
-
target: this.#wc.shadowRoot,
|
|
7
|
-
listeners: {
|
|
8
|
-
click: this.#clickHandler.bind(this),
|
|
9
|
-
},
|
|
10
|
-
});
|
|
11
|
-
#state = getStateFromDOM(this.#wc.shadowRoot);
|
|
12
|
-
#clickHandler(e) {
|
|
13
|
-
let increment = getIncrement(e);
|
|
14
|
-
if (increment) {
|
|
15
|
-
this.#state.count += increment;
|
|
16
|
-
let el = this.#state.el;
|
|
17
|
-
if (el)
|
|
18
|
-
el.textContent = this.#state.count.toString();
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
function getStateFromDOM(shadowRoot) {
|
|
23
|
-
let slot = shadowRoot.querySelector("slot");
|
|
24
|
-
let el;
|
|
25
|
-
if (slot)
|
|
26
|
-
for (let slotted of slot.assignedNodes()) {
|
|
27
|
-
if (slotted instanceof HTMLSpanElement)
|
|
28
|
-
el = slotted;
|
|
29
|
-
}
|
|
30
|
-
return { el, count: parseInt(el?.textContent ?? "0") };
|
|
31
|
-
}
|
|
32
|
-
function getIncrement(e) {
|
|
33
|
-
if (e.target instanceof HTMLButtonElement) {
|
|
34
|
-
return e.target.hasAttribute("increase") ? 1 : -1;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
customElements.define("counter-wc", Counter);
|
package/examples/counter/mod.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Custom Element with state and interactivity.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { Wc, Events } from "wctk";
|
|
6
|
-
|
|
7
|
-
interface State {
|
|
8
|
-
el: HTMLSpanElement | undefined;
|
|
9
|
-
count: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
class Counter extends HTMLElement {
|
|
13
|
-
#wc = new Wc({ host: this });
|
|
14
|
-
|
|
15
|
-
#ev = new Events({
|
|
16
|
-
connected: true,
|
|
17
|
-
target: this.#wc.shadowRoot,
|
|
18
|
-
listeners: {
|
|
19
|
-
click: this.#clickHandler.bind(this),
|
|
20
|
-
},
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
#state: State = getStateFromDOM(this.#wc.shadowRoot);
|
|
24
|
-
|
|
25
|
-
#clickHandler(e: PointerEvent) {
|
|
26
|
-
let increment = getIncrement(e);
|
|
27
|
-
if (increment) {
|
|
28
|
-
this.#state.count += increment;
|
|
29
|
-
let el = this.#state.el;
|
|
30
|
-
if (el) el.textContent = this.#state.count.toString();
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function getStateFromDOM(shadowRoot: ShadowRoot): State {
|
|
36
|
-
let slot = shadowRoot.querySelector("slot");
|
|
37
|
-
let el: HTMLSpanElement | undefined;
|
|
38
|
-
if (slot)
|
|
39
|
-
for (let slotted of slot.assignedNodes()) {
|
|
40
|
-
if (slotted instanceof HTMLSpanElement) el = slotted;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return { el, count: parseInt(el?.textContent ?? "0") };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function getIncrement(e: Event) {
|
|
47
|
-
if (e.target instanceof HTMLButtonElement) {
|
|
48
|
-
return e.target.hasAttribute("increase") ? 1 : -1;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
customElements.define("counter-wc", Counter);
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en-us">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset=utf-8>
|
|
5
|
-
<meta name=viewport content="width=device-width, initial-scale=1">
|
|
6
|
-
<script type="importmap">
|
|
7
|
-
{
|
|
8
|
-
"imports": {
|
|
9
|
-
"wctk": "../../dist/mod.js"
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
</script>
|
|
13
|
-
<script src="./mod.js" type=module></script>
|
|
14
|
-
</head>
|
|
15
|
-
<body>
|
|
16
|
-
<main>
|
|
17
|
-
<form>
|
|
18
|
-
<!-- html component input -->
|
|
19
|
-
<input name="html_element">
|
|
20
|
-
<!-- custom element input -->
|
|
21
|
-
<text-input name="web_component">
|
|
22
|
-
<template shadowrootmode="closed">
|
|
23
|
-
<input>
|
|
24
|
-
</template>
|
|
25
|
-
</text-input>
|
|
26
|
-
<button type="submit">submit !</button>
|
|
27
|
-
</form>
|
|
28
|
-
<pre results></pre>
|
|
29
|
-
</main>
|
|
30
|
-
</body>
|
|
31
|
-
</html>
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { TextInput } from "./text_input.js";
|
|
2
|
-
customElements.define("text-input", TextInput);
|
|
3
|
-
const results = document.querySelector("[results]");
|
|
4
|
-
document.addEventListener("submit", function (e) {
|
|
5
|
-
if (!(e.target instanceof HTMLFormElement))
|
|
6
|
-
return;
|
|
7
|
-
e.preventDefault();
|
|
8
|
-
let formdata = new FormData(e.target);
|
|
9
|
-
if (results)
|
|
10
|
-
results.textContent = JSON.stringify(Object.fromEntries(formdata), undefined, " ");
|
|
11
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { TextInput } from "./text_input.js";
|
|
2
|
-
|
|
3
|
-
customElements.define("text-input", TextInput);
|
|
4
|
-
|
|
5
|
-
const results = document.querySelector("[results]");
|
|
6
|
-
|
|
7
|
-
document.addEventListener("submit", function (e: SubmitEvent) {
|
|
8
|
-
if (!(e.target instanceof HTMLFormElement)) return;
|
|
9
|
-
|
|
10
|
-
e.preventDefault();
|
|
11
|
-
|
|
12
|
-
let formdata: FormData = new FormData(e.target);
|
|
13
|
-
|
|
14
|
-
if (results)
|
|
15
|
-
results.textContent = JSON.stringify(
|
|
16
|
-
Object.fromEntries(formdata),
|
|
17
|
-
undefined,
|
|
18
|
-
" ",
|
|
19
|
-
);
|
|
20
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Wc, Events } from "wctk";
|
|
2
|
-
export class TextInput extends HTMLElement {
|
|
3
|
-
static formAssociated = true;
|
|
4
|
-
#wc = new Wc({ host: this });
|
|
5
|
-
#ev = new Events({
|
|
6
|
-
connected: true,
|
|
7
|
-
target: this.#wc.shadowRoot,
|
|
8
|
-
listeners: {
|
|
9
|
-
change: this.#changeHandler.bind(this),
|
|
10
|
-
},
|
|
11
|
-
});
|
|
12
|
-
#changeHandler(event) {
|
|
13
|
-
let { target } = event;
|
|
14
|
-
if (target instanceof HTMLInputElement)
|
|
15
|
-
this.#wc.setFormValue(target.value);
|
|
16
|
-
}
|
|
17
|
-
formStateRestoreCallback(state) {
|
|
18
|
-
let input = this.#wc.shadowRoot.querySelector("input");
|
|
19
|
-
if (input)
|
|
20
|
-
input.value = state;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Form associated custom element.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { Wc, Events } from "wctk";
|
|
6
|
-
|
|
7
|
-
export class TextInput extends HTMLElement {
|
|
8
|
-
static formAssociated = true;
|
|
9
|
-
|
|
10
|
-
#wc = new Wc({ host: this });
|
|
11
|
-
|
|
12
|
-
#ev = new Events({
|
|
13
|
-
connected: true,
|
|
14
|
-
target: this.#wc.shadowRoot,
|
|
15
|
-
listeners: {
|
|
16
|
-
change: this.#changeHandler.bind(this),
|
|
17
|
-
},
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
#changeHandler(event: Event): void {
|
|
21
|
-
let { target } = event;
|
|
22
|
-
if (target instanceof HTMLInputElement)
|
|
23
|
-
this.#wc.setFormValue(target.value);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// lifecycle method
|
|
27
|
-
formStateRestoreCallback(state: string) {
|
|
28
|
-
let input = this.#wc.shadowRoot.querySelector("input");
|
|
29
|
-
if (input) input.value = state;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en-us">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset=utf-8>
|
|
5
|
-
<meta name=viewport content="width=device-width, initial-scale=1">
|
|
6
|
-
<script type="importmap">
|
|
7
|
-
{
|
|
8
|
-
"imports": {
|
|
9
|
-
"wctk": "../../dist/mod.js"
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
</script>
|
|
13
|
-
<script src="./mod.js" type=module></script>
|
|
14
|
-
</head>
|
|
15
|
-
<body>
|
|
16
|
-
<main>
|
|
17
|
-
<!-- webcomponent -->
|
|
18
|
-
<stopwatch-wc>
|
|
19
|
-
<template shadowrootmode="closed">
|
|
20
|
-
<!-- Shadow DOM content -->
|
|
21
|
-
<span>117.00</span>
|
|
22
|
-
</template>
|
|
23
|
-
</stopwatch-wc>
|
|
24
|
-
|
|
25
|
-
<section>
|
|
26
|
-
<button start>|></button>
|
|
27
|
-
<button pause>| |</button>
|
|
28
|
-
<button stop>[ ]</button>
|
|
29
|
-
</section>
|
|
30
|
-
</main>
|
|
31
|
-
</body>
|
|
32
|
-
</html>
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Stopwatch } from "./stopwatch.js";
|
|
2
|
-
customElements.define("stopwatch-wc", Stopwatch);
|
|
3
|
-
const stopwatch = document.querySelector("stopwatch-wc");
|
|
4
|
-
document.addEventListener("click", function (e) {
|
|
5
|
-
if (!(stopwatch && e.target instanceof HTMLButtonElement))
|
|
6
|
-
return;
|
|
7
|
-
if (e.target.hasAttribute("start"))
|
|
8
|
-
stopwatch.start();
|
|
9
|
-
if (e.target.hasAttribute("pause"))
|
|
10
|
-
stopwatch.pause();
|
|
11
|
-
if (e.target.hasAttribute("stop"))
|
|
12
|
-
stopwatch.stop();
|
|
13
|
-
});
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Stopwatch } from "./stopwatch.js";
|
|
2
|
-
|
|
3
|
-
customElements.define("stopwatch-wc", Stopwatch);
|
|
4
|
-
|
|
5
|
-
const stopwatch = document.querySelector<Stopwatch>("stopwatch-wc");
|
|
6
|
-
|
|
7
|
-
document.addEventListener("click", function (e: PointerEvent) {
|
|
8
|
-
if (!(stopwatch && e.target instanceof HTMLButtonElement)) return;
|
|
9
|
-
|
|
10
|
-
if (e.target.hasAttribute("start")) stopwatch.start();
|
|
11
|
-
if (e.target.hasAttribute("pause")) stopwatch.pause();
|
|
12
|
-
if (e.target.hasAttribute("stop")) stopwatch.stop();
|
|
13
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { Wc, Microtask } from "wctk";
|
|
2
|
-
export class Stopwatch extends HTMLElement {
|
|
3
|
-
#wc = new Wc({ host: this });
|
|
4
|
-
#rc = new Microtask(this.#render.bind(this));
|
|
5
|
-
#state = getStateFromShadowDOM(this.#wc.shadowRoot);
|
|
6
|
-
#render() {
|
|
7
|
-
let { el } = this.#state;
|
|
8
|
-
if (el)
|
|
9
|
-
el.textContent = this.#state.count.toFixed(2);
|
|
10
|
-
}
|
|
11
|
-
#update = this.#undboundUpdate.bind(this);
|
|
12
|
-
#undboundUpdate(now) {
|
|
13
|
-
this.#state.count += (now - this.#state.prevTimestamp) * 0.001;
|
|
14
|
-
this.#state.prevTimestamp = now;
|
|
15
|
-
this.#state.receipt = window.requestAnimationFrame(this.#update);
|
|
16
|
-
this.#rc.queue();
|
|
17
|
-
}
|
|
18
|
-
start() {
|
|
19
|
-
if (this.#state.receipt)
|
|
20
|
-
return;
|
|
21
|
-
this.#state.prevTimestamp = performance.now();
|
|
22
|
-
this.#state.receipt = window.requestAnimationFrame(this.#update);
|
|
23
|
-
}
|
|
24
|
-
pause() {
|
|
25
|
-
let { receipt } = this.#state;
|
|
26
|
-
if (receipt)
|
|
27
|
-
window.cancelAnimationFrame(receipt);
|
|
28
|
-
this.#state.receipt = undefined;
|
|
29
|
-
}
|
|
30
|
-
stop() {
|
|
31
|
-
this.pause();
|
|
32
|
-
this.#state.count = 0;
|
|
33
|
-
this.#rc.queue();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
function getStateFromShadowDOM(shadowRoot) {
|
|
37
|
-
let el = shadowRoot.querySelector("span");
|
|
38
|
-
let count = parseInt(el?.textContent ?? "0");
|
|
39
|
-
if (Number.isNaN(count))
|
|
40
|
-
count = 0;
|
|
41
|
-
let prevTimestamp = performance.now();
|
|
42
|
-
return {
|
|
43
|
-
count,
|
|
44
|
-
el,
|
|
45
|
-
prevTimestamp,
|
|
46
|
-
receipt: undefined,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Custom Element with performant and "asynchronous" renders
|
|
3
|
-
on the microtask queue.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// This example uses window.requestAnimationFrame.
|
|
7
|
-
// Multiple stopwatches means multiple animation frame requests.
|
|
8
|
-
// This is not terribly performant but it's a quick way to retrieve
|
|
9
|
-
// accurate timestamp data for a stopwatch.
|
|
10
|
-
|
|
11
|
-
import { Wc, Microtask } from "wctk";
|
|
12
|
-
|
|
13
|
-
interface State {
|
|
14
|
-
count: number;
|
|
15
|
-
el: HTMLSpanElement | null;
|
|
16
|
-
prevTimestamp: DOMHighResTimeStamp;
|
|
17
|
-
receipt: number | void;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export class Stopwatch extends HTMLElement {
|
|
21
|
-
#wc = new Wc({ host: this });
|
|
22
|
-
#rc = new Microtask(this.#render.bind(this));
|
|
23
|
-
#state: State = getStateFromShadowDOM(this.#wc.shadowRoot);
|
|
24
|
-
|
|
25
|
-
#render() {
|
|
26
|
-
let { el } = this.#state;
|
|
27
|
-
if (el) el.textContent = this.#state.count.toFixed(2);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
#update = this.#undboundUpdate.bind(this);
|
|
31
|
-
#undboundUpdate(now: DOMHighResTimeStamp) {
|
|
32
|
-
this.#state.count += (now - this.#state.prevTimestamp) * 0.001;
|
|
33
|
-
this.#state.prevTimestamp = now;
|
|
34
|
-
this.#state.receipt = window.requestAnimationFrame(this.#update);
|
|
35
|
-
this.#rc.queue();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
start() {
|
|
39
|
-
if (this.#state.receipt) return;
|
|
40
|
-
|
|
41
|
-
this.#state.prevTimestamp = performance.now();
|
|
42
|
-
this.#state.receipt = window.requestAnimationFrame(this.#update);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
pause() {
|
|
46
|
-
let { receipt } = this.#state;
|
|
47
|
-
if (receipt) window.cancelAnimationFrame(receipt);
|
|
48
|
-
this.#state.receipt = undefined;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
stop() {
|
|
52
|
-
this.pause();
|
|
53
|
-
this.#state.count = 0;
|
|
54
|
-
this.#rc.queue();
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function getStateFromShadowDOM(shadowRoot: ShadowRoot): State {
|
|
59
|
-
let el = shadowRoot.querySelector("span");
|
|
60
|
-
let count = parseInt(el?.textContent ?? "0");
|
|
61
|
-
if (Number.isNaN(count)) count = 0;
|
|
62
|
-
let prevTimestamp = performance.now();
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
count,
|
|
66
|
-
el,
|
|
67
|
-
prevTimestamp,
|
|
68
|
-
receipt: undefined,
|
|
69
|
-
};
|
|
70
|
-
}
|
package/examples/tsconfig.json
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { findElement, elementClick, elementSendKeys, } from "@w-lfpup/jackrabbit/browser/dist/commands.js";
|
|
2
|
-
import { Events } from "../../dist/mod.js";
|
|
3
|
-
let eventController;
|
|
4
|
-
let eventReceipts = [];
|
|
5
|
-
function setup() {
|
|
6
|
-
eventController = new Events({
|
|
7
|
-
target: document.body,
|
|
8
|
-
connected: true,
|
|
9
|
-
listeners: {
|
|
10
|
-
click: function (e) {
|
|
11
|
-
eventReceipts.push(e);
|
|
12
|
-
},
|
|
13
|
-
input: function (e) {
|
|
14
|
-
eventReceipts.push(e);
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
document.body.setHTMLUnsafe(`
|
|
19
|
-
<input>
|
|
20
|
-
<button>
|
|
21
|
-
`);
|
|
22
|
-
}
|
|
23
|
-
async function testClickEvents() {
|
|
24
|
-
let buttonId = await findElement("button");
|
|
25
|
-
if (!buttonId)
|
|
26
|
-
return "failed to query button";
|
|
27
|
-
await elementClick(buttonId);
|
|
28
|
-
await elementClick(buttonId);
|
|
29
|
-
let clicks = [];
|
|
30
|
-
for (let event of eventReceipts) {
|
|
31
|
-
if ("click" === event.type)
|
|
32
|
-
clicks.push(event);
|
|
33
|
-
}
|
|
34
|
-
if (2 !== clicks.length)
|
|
35
|
-
return `incorrect number of clicks: ${clicks.length}/2`;
|
|
36
|
-
}
|
|
37
|
-
async function testInputEvents() {
|
|
38
|
-
const expectedMessage = "UwU";
|
|
39
|
-
let inputId = await findElement("input");
|
|
40
|
-
if (!inputId)
|
|
41
|
-
return "failed to query input element";
|
|
42
|
-
await elementSendKeys(inputId, expectedMessage);
|
|
43
|
-
let inputs = [];
|
|
44
|
-
for (let event of eventReceipts) {
|
|
45
|
-
if ("input" === event.type)
|
|
46
|
-
inputs.push(event);
|
|
47
|
-
}
|
|
48
|
-
if (3 !== inputs.length)
|
|
49
|
-
return `incorrect number of input events: ${inputs.length}/1`;
|
|
50
|
-
let input = document.body.querySelector("input");
|
|
51
|
-
if (expectedMessage !== input?.value)
|
|
52
|
-
return `expected input: ${expectedMessage}, found: ${input?.value}`;
|
|
53
|
-
}
|
|
54
|
-
function tearDown() {
|
|
55
|
-
eventController.disconnect();
|
|
56
|
-
}
|
|
57
|
-
export const tests = [setup, testClickEvents, testInputEvents, tearDown];
|
|
58
|
-
export const options = {
|
|
59
|
-
title: import.meta.url,
|
|
60
|
-
};
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { Microtask } from "../../dist/mod.js";
|
|
2
|
-
let microtaskController;
|
|
3
|
-
let expectedSleepy = "-_-";
|
|
4
|
-
let expectedHappy = "^_^";
|
|
5
|
-
let span;
|
|
6
|
-
function nextFrame() {
|
|
7
|
-
return new Promise((resolve, reject) => {
|
|
8
|
-
window.queueMicrotask(function () {
|
|
9
|
-
resolve();
|
|
10
|
-
});
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
function updateExpression() {
|
|
14
|
-
if (!span)
|
|
15
|
-
return;
|
|
16
|
-
span.textContent = expectedHappy;
|
|
17
|
-
}
|
|
18
|
-
function setup() {
|
|
19
|
-
microtaskController = new Microtask(updateExpression);
|
|
20
|
-
document.body.setHTMLUnsafe(`
|
|
21
|
-
<span>-_-</span>
|
|
22
|
-
`);
|
|
23
|
-
span = document.querySelector("span");
|
|
24
|
-
}
|
|
25
|
-
async function testTaskCycle() {
|
|
26
|
-
if (expectedSleepy !== span?.textContent)
|
|
27
|
-
return "wrong expression, expected sleepy -_-";
|
|
28
|
-
microtaskController.queue();
|
|
29
|
-
if (expectedSleepy !== span?.textContent)
|
|
30
|
-
return "still wrong expression, still expected sleepy -_-";
|
|
31
|
-
await nextFrame();
|
|
32
|
-
if (expectedHappy !== span?.textContent)
|
|
33
|
-
return "wrong expression after frame, expected happy ^_^";
|
|
34
|
-
}
|
|
35
|
-
export const tests = [setup, testTaskCycle];
|
|
36
|
-
export const options = {
|
|
37
|
-
title: import.meta.url,
|
|
38
|
-
};
|
package/tests/dist/mod.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import * as EventTests from "./events.tests.js";
|
|
2
|
-
import * as MicrotaskQueueTests from "./microtask.tests.js";
|
|
3
|
-
import * as QuerySelectorTests from "./query_selector.tests.js";
|
|
4
|
-
import * as WcTests from "./wc.tests.js";
|
|
5
|
-
export const testModules = [
|
|
6
|
-
EventTests,
|
|
7
|
-
MicrotaskQueueTests,
|
|
8
|
-
QuerySelectorTests,
|
|
9
|
-
WcTests,
|
|
10
|
-
];
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { QuerySelector } from "../../dist/mod.js";
|
|
2
|
-
let qs;
|
|
3
|
-
function setup() {
|
|
4
|
-
qs = new QuerySelector(document.body);
|
|
5
|
-
document.body.setHTMLUnsafe(`
|
|
6
|
-
<p>
|
|
7
|
-
<span data-first>hello</span>
|
|
8
|
-
<span data-second>hai :3</span>
|
|
9
|
-
</p>
|
|
10
|
-
`);
|
|
11
|
-
}
|
|
12
|
-
function testQuerySelector() {
|
|
13
|
-
let p = qs.querySelector("p");
|
|
14
|
-
let span = qs.querySelector("[data-second]");
|
|
15
|
-
if (!p)
|
|
16
|
-
return "failed to query p";
|
|
17
|
-
if (!span)
|
|
18
|
-
return "failed to query span";
|
|
19
|
-
}
|
|
20
|
-
function testQuerySelectorAll() {
|
|
21
|
-
let peas = qs.querySelectorAll("p");
|
|
22
|
-
let spans = qs.querySelectorAll("span");
|
|
23
|
-
if (!peas.length)
|
|
24
|
-
return "failed to query peas";
|
|
25
|
-
if (!spans.length)
|
|
26
|
-
return "failed to query spans";
|
|
27
|
-
if (1 !== peas.length)
|
|
28
|
-
return `failed to query ${peas.length}/1 peas`;
|
|
29
|
-
if (2 !== spans.length)
|
|
30
|
-
return `failed to query ${spans.length}/2 spans`;
|
|
31
|
-
}
|
|
32
|
-
function testDeleteAll() {
|
|
33
|
-
qs.deleteAll();
|
|
34
|
-
}
|
|
35
|
-
export const tests = [
|
|
36
|
-
setup,
|
|
37
|
-
testQuerySelector,
|
|
38
|
-
testQuerySelectorAll,
|
|
39
|
-
testDeleteAll,
|
|
40
|
-
];
|
|
41
|
-
export const options = {
|
|
42
|
-
title: import.meta.url,
|
|
43
|
-
};
|
package/tests/dist/wc.tests.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { Wc } from "../../dist/mod.js";
|
|
2
|
-
class WcDeclarativeElement extends HTMLElement {
|
|
3
|
-
#wc = new Wc({ host: this });
|
|
4
|
-
get delcarative() {
|
|
5
|
-
return this.#wc.declarative;
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
window.customElements.define("declarative-element", WcDeclarativeElement);
|
|
9
|
-
let testEl;
|
|
10
|
-
let declarativeEl;
|
|
11
|
-
function setup() {
|
|
12
|
-
document.body.setHTMLUnsafe(`
|
|
13
|
-
<declarative-element></declarative-element>
|
|
14
|
-
<declarative-element data-declarative>
|
|
15
|
-
<template shadowrootmode="open">
|
|
16
|
-
<p>howdy!</p>
|
|
17
|
-
</template>
|
|
18
|
-
</declarative-element>
|
|
19
|
-
`);
|
|
20
|
-
[testEl, declarativeEl] = document.querySelectorAll("declarative-element");
|
|
21
|
-
}
|
|
22
|
-
function testDeclarativeShadowDomDoesNotExist() {
|
|
23
|
-
if (!testEl)
|
|
24
|
-
return "failed to query declarative-element";
|
|
25
|
-
if (testEl.delcarative)
|
|
26
|
-
return "element incorrectly labelled as declarative";
|
|
27
|
-
}
|
|
28
|
-
function testDeclarativeShadowDomExists() {
|
|
29
|
-
if (!declarativeEl)
|
|
30
|
-
return "failed to query declarative-element with declarative shadow dom";
|
|
31
|
-
if (!declarativeEl.delcarative)
|
|
32
|
-
return "element incorrectly labelled as not declarative";
|
|
33
|
-
}
|
|
34
|
-
export const tests = [
|
|
35
|
-
setup,
|
|
36
|
-
testDeclarativeShadowDomExists,
|
|
37
|
-
testDeclarativeShadowDomDoesNotExist,
|
|
38
|
-
];
|
|
39
|
-
export const options = {
|
|
40
|
-
title: import.meta.url,
|
|
41
|
-
};
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
findElement,
|
|
3
|
-
elementClick,
|
|
4
|
-
elementSendKeys,
|
|
5
|
-
} from "@w-lfpup/jackrabbit/browser/dist/commands.js";
|
|
6
|
-
import { Events } from "../../dist/mod.js";
|
|
7
|
-
|
|
8
|
-
let eventController: Events;
|
|
9
|
-
let eventReceipts: Event[] = [];
|
|
10
|
-
|
|
11
|
-
function setup() {
|
|
12
|
-
eventController = new Events({
|
|
13
|
-
target: document.body,
|
|
14
|
-
connected: true,
|
|
15
|
-
listeners: {
|
|
16
|
-
click: function (e: PointerEvent) {
|
|
17
|
-
eventReceipts.push(e);
|
|
18
|
-
},
|
|
19
|
-
input: function (e: InputEvent) {
|
|
20
|
-
eventReceipts.push(e);
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
document.body.setHTMLUnsafe(`
|
|
26
|
-
<input>
|
|
27
|
-
<button>
|
|
28
|
-
`);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function testClickEvents() {
|
|
32
|
-
let buttonId = await findElement("button");
|
|
33
|
-
if (!buttonId) return "failed to query button";
|
|
34
|
-
await elementClick(buttonId);
|
|
35
|
-
await elementClick(buttonId);
|
|
36
|
-
|
|
37
|
-
let clicks = [];
|
|
38
|
-
for (let event of eventReceipts) {
|
|
39
|
-
if ("click" === event.type) clicks.push(event);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (2 !== clicks.length)
|
|
43
|
-
return `incorrect number of clicks: ${clicks.length}/2`;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function testInputEvents() {
|
|
47
|
-
const expectedMessage = "UwU";
|
|
48
|
-
let inputId = await findElement("input");
|
|
49
|
-
if (!inputId) return "failed to query input element";
|
|
50
|
-
|
|
51
|
-
await elementSendKeys(inputId, expectedMessage);
|
|
52
|
-
let inputs = [];
|
|
53
|
-
for (let event of eventReceipts) {
|
|
54
|
-
if ("input" === event.type) inputs.push(event);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (3 !== inputs.length)
|
|
58
|
-
return `incorrect number of input events: ${inputs.length}/1`;
|
|
59
|
-
|
|
60
|
-
let input = document.body.querySelector("input");
|
|
61
|
-
if (expectedMessage !== input?.value)
|
|
62
|
-
return `expected input: ${expectedMessage}, found: ${input?.value}`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function tearDown() {
|
|
66
|
-
eventController.disconnect();
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export const tests = [setup, testClickEvents, testInputEvents, tearDown];
|
|
70
|
-
|
|
71
|
-
export const options = {
|
|
72
|
-
title: import.meta.url,
|
|
73
|
-
};
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { findElement, log } from "@w-lfpup/jackrabbit/browser/dist/commands.js";
|
|
2
|
-
import { Microtask } from "../../dist/mod.js";
|
|
3
|
-
|
|
4
|
-
let microtaskController: Microtask;
|
|
5
|
-
|
|
6
|
-
let expectedSleepy = "-_-";
|
|
7
|
-
let expectedHappy = "^_^";
|
|
8
|
-
|
|
9
|
-
let span: HTMLSpanElement | null;
|
|
10
|
-
|
|
11
|
-
function nextFrame() {
|
|
12
|
-
return new Promise<void>((resolve, reject) => {
|
|
13
|
-
window.queueMicrotask(function () {
|
|
14
|
-
resolve();
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function updateExpression() {
|
|
20
|
-
if (!span) return;
|
|
21
|
-
|
|
22
|
-
span.textContent = expectedHappy;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function setup() {
|
|
26
|
-
microtaskController = new Microtask(updateExpression);
|
|
27
|
-
|
|
28
|
-
document.body.setHTMLUnsafe(`
|
|
29
|
-
<span>-_-</span>
|
|
30
|
-
`);
|
|
31
|
-
|
|
32
|
-
span = document.querySelector("span");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function testTaskCycle() {
|
|
36
|
-
if (expectedSleepy !== span?.textContent)
|
|
37
|
-
return "wrong expression, expected sleepy -_-";
|
|
38
|
-
|
|
39
|
-
microtaskController.queue();
|
|
40
|
-
|
|
41
|
-
if (expectedSleepy !== span?.textContent)
|
|
42
|
-
return "still wrong expression, still expected sleepy -_-";
|
|
43
|
-
|
|
44
|
-
await nextFrame();
|
|
45
|
-
|
|
46
|
-
if (expectedHappy !== span?.textContent)
|
|
47
|
-
return "wrong expression after frame, expected happy ^_^";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export const tests = [setup, testTaskCycle];
|
|
51
|
-
|
|
52
|
-
export const options = {
|
|
53
|
-
title: import.meta.url,
|
|
54
|
-
};
|
package/tests/src/mod.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import * as EventTests from "./events.tests.js";
|
|
2
|
-
import * as MicrotaskQueueTests from "./microtask.tests.js";
|
|
3
|
-
import * as QuerySelectorTests from "./query_selector.tests.js";
|
|
4
|
-
import * as WcTests from "./wc.tests.js";
|
|
5
|
-
|
|
6
|
-
export const testModules = [
|
|
7
|
-
EventTests,
|
|
8
|
-
MicrotaskQueueTests,
|
|
9
|
-
QuerySelectorTests,
|
|
10
|
-
WcTests,
|
|
11
|
-
];
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { QuerySelector } from "../../dist/mod.js";
|
|
2
|
-
|
|
3
|
-
let qs: QuerySelector;
|
|
4
|
-
function setup() {
|
|
5
|
-
qs = new QuerySelector(document.body);
|
|
6
|
-
|
|
7
|
-
document.body.setHTMLUnsafe(`
|
|
8
|
-
<p>
|
|
9
|
-
<span data-first>hello</span>
|
|
10
|
-
<span data-second>hai :3</span>
|
|
11
|
-
</p>
|
|
12
|
-
`);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function testQuerySelector() {
|
|
16
|
-
let p = qs.querySelector("p");
|
|
17
|
-
let span = qs.querySelector("[data-second]");
|
|
18
|
-
|
|
19
|
-
if (!p) return "failed to query p";
|
|
20
|
-
if (!span) return "failed to query span";
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function testQuerySelectorAll() {
|
|
24
|
-
let peas = qs.querySelectorAll("p");
|
|
25
|
-
let spans = qs.querySelectorAll("span");
|
|
26
|
-
|
|
27
|
-
if (!peas.length) return "failed to query peas";
|
|
28
|
-
if (!spans.length) return "failed to query spans";
|
|
29
|
-
if (1 !== peas.length) return `failed to query ${peas.length}/1 peas`;
|
|
30
|
-
if (2 !== spans.length) return `failed to query ${spans.length}/2 spans`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function testDeleteAll() {
|
|
34
|
-
qs.deleteAll();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export const tests = [
|
|
38
|
-
setup,
|
|
39
|
-
testQuerySelector,
|
|
40
|
-
testQuerySelectorAll,
|
|
41
|
-
testDeleteAll,
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
export const options = {
|
|
45
|
-
title: import.meta.url,
|
|
46
|
-
};
|
package/tests/src/wc.tests.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { Wc } from "../../dist/mod.js";
|
|
2
|
-
|
|
3
|
-
class WcDeclarativeElement extends HTMLElement {
|
|
4
|
-
#wc = new Wc({ host: this });
|
|
5
|
-
|
|
6
|
-
get delcarative(): boolean {
|
|
7
|
-
return this.#wc.declarative;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
window.customElements.define("declarative-element", WcDeclarativeElement);
|
|
12
|
-
|
|
13
|
-
let testEl: WcDeclarativeElement | undefined;
|
|
14
|
-
let declarativeEl: WcDeclarativeElement | undefined;
|
|
15
|
-
|
|
16
|
-
function setup() {
|
|
17
|
-
document.body.setHTMLUnsafe(`
|
|
18
|
-
<declarative-element></declarative-element>
|
|
19
|
-
<declarative-element data-declarative>
|
|
20
|
-
<template shadowrootmode="open">
|
|
21
|
-
<p>howdy!</p>
|
|
22
|
-
</template>
|
|
23
|
-
</declarative-element>
|
|
24
|
-
`);
|
|
25
|
-
|
|
26
|
-
[testEl, declarativeEl] = document.querySelectorAll<WcDeclarativeElement>(
|
|
27
|
-
"declarative-element",
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function testDeclarativeShadowDomDoesNotExist() {
|
|
32
|
-
if (!testEl) return "failed to query declarative-element";
|
|
33
|
-
if (testEl.delcarative)
|
|
34
|
-
return "element incorrectly labelled as declarative";
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function testDeclarativeShadowDomExists() {
|
|
38
|
-
if (!declarativeEl)
|
|
39
|
-
return "failed to query declarative-element with declarative shadow dom";
|
|
40
|
-
if (!declarativeEl.delcarative)
|
|
41
|
-
return "element incorrectly labelled as not declarative";
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const tests = [
|
|
45
|
-
setup,
|
|
46
|
-
testDeclarativeShadowDomExists,
|
|
47
|
-
testDeclarativeShadowDomDoesNotExist,
|
|
48
|
-
];
|
|
49
|
-
|
|
50
|
-
export const options = {
|
|
51
|
-
title: import.meta.url,
|
|
52
|
-
};
|
package/tests/tsconfig.json
DELETED
|
File without changes
|