@w-lfpup/wctk 0.1.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/.github/workflows/build_and_test.yml +18 -0
- package/.prettierignore +5 -0
- package/.prettierrc +5 -0
- package/LICENSE +28 -0
- package/README.md +49 -0
- package/dist/bind.d.ts +7 -0
- package/dist/bind.js +14 -0
- package/dist/events.d.ts +21 -0
- package/dist/events.js +39 -0
- package/dist/microtask.d.ts +12 -0
- package/dist/microtask.js +23 -0
- package/dist/mod.d.ts +6 -0
- package/dist/mod.js +6 -0
- package/dist/query_selector.d.ts +15 -0
- package/dist/query_selector.js +25 -0
- package/dist/subscription.d.ts +19 -0
- package/dist/subscription.js +26 -0
- package/dist/wc.d.ts +33 -0
- package/dist/wc.js +46 -0
- package/docs/bind.md +26 -0
- package/docs/events.md +99 -0
- package/docs/microtask.md +35 -0
- package/docs/query_selector.md +39 -0
- package/docs/subscription.md +85 -0
- package/docs/wc.md +57 -0
- package/examples/counter/index.html +30 -0
- package/examples/counter/mod.js +39 -0
- package/examples/counter/mod.ts +51 -0
- package/examples/form_associated/index.html +31 -0
- package/examples/form_associated/mod.js +13 -0
- package/examples/form_associated/mod.ts +21 -0
- package/examples/form_associated/text_input.js +25 -0
- package/examples/form_associated/text_input.ts +28 -0
- package/examples/stopwatch/index.html +29 -0
- package/examples/stopwatch/mod.js +8 -0
- package/examples/stopwatch/mod.ts +11 -0
- package/examples/stopwatch/stopwatch.js +45 -0
- package/examples/stopwatch/stopwatch.ts +61 -0
- package/examples/tsconfig.json +10 -0
- package/package.json +22 -0
- package/src/bind.ts +22 -0
- package/src/events.ts +67 -0
- package/src/microtask.ts +37 -0
- package/src/mod.ts +6 -0
- package/src/query_selector.ts +46 -0
- package/src/subscription.ts +47 -0
- package/src/tsconfig.json +7 -0
- package/src/wc.ts +87 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: Build and Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["main"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build_and_test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v5
|
|
14
|
+
- uses: actions/setup-node@v4
|
|
15
|
+
- name: Install
|
|
16
|
+
run: npm ci
|
|
17
|
+
- name: Format
|
|
18
|
+
run: npm run format
|
package/.prettierignore
ADDED
package/.prettierrc
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, Taylor Vann
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Wctk-JS
|
|
2
|
+
|
|
3
|
+
A web component tool kit.
|
|
4
|
+
|
|
5
|
+
## About
|
|
6
|
+
|
|
7
|
+
A half-dozen controllers help developers:
|
|
8
|
+
|
|
9
|
+
- create [shadow dom](./docs/wc.md)
|
|
10
|
+
- render on the [microtask queue](./docs/microtask.md)
|
|
11
|
+
- listen for [events](./docs/events.md)
|
|
12
|
+
- [subscribe](./docs/subscription.md) to external data stores
|
|
13
|
+
- manage [form values](./docs/wc.md#adopted-stylesheets-and-form-values)
|
|
14
|
+
- [query](./docs/query_selector.md) the shadow dom
|
|
15
|
+
- [bind](./docs/bind.md) functions to elements
|
|
16
|
+
|
|
17
|
+
All features are compositional and built to support [declarative shadow dom](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM#declaratively_with_html) SSR.
|
|
18
|
+
|
|
19
|
+
There are no base classes or decorators.
|
|
20
|
+
|
|
21
|
+
`Wctk-js` even supports `#private` methods as callbacks, fully encapsulating a web component's API (aside from required [lifecycle methods](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks)).
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
Install with npm.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install --save-dev @w-lfpup/wctk
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or install directly from github.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install --save-dev https://github.com/w-lfpup/wctk-js/
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Examples
|
|
38
|
+
|
|
39
|
+
### SSR (server side rendering)
|
|
40
|
+
|
|
41
|
+
The following examples demonstrate several common SSR use cases:
|
|
42
|
+
|
|
43
|
+
- a [counter](https://w-lfpup.github.io/wctk-js/examples/counter/) with initial state in the DOM ([code](https://github.com/w-lfpup/wctk-js/tree/main/examples/counter/)).
|
|
44
|
+
- a [stopwatch](https://w-lfpup.github.io/wctk-js/examples/stopwatch/) with initial state in the Shadow DOM ([code](https://github.com/w-lfpup/wctk-js/tree/main/examples/stopwatch/)).
|
|
45
|
+
- a [form associated](https://w-lfpup.github.io/wctk-js/examples/form_associated/) element ([code](https://github.com/w-lfpup/wctk-js/tree/main/examples/form_associated/)).
|
|
46
|
+
|
|
47
|
+
## License
|
|
48
|
+
|
|
49
|
+
`Wctk-js` is released under the BSD-3 Clause License.
|
package/dist/bind.d.ts
ADDED
package/dist/bind.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export class Bind {
|
|
2
|
+
constructor(params) {
|
|
3
|
+
let { host, callbacks } = params;
|
|
4
|
+
for (let callback of callbacks) {
|
|
5
|
+
// do not bind and replace already bound functions
|
|
6
|
+
if (callback instanceof Function &&
|
|
7
|
+
!callback.hasOwnProperty("prototype")) {
|
|
8
|
+
let { name } = callback;
|
|
9
|
+
if (!name.startsWith("#"))
|
|
10
|
+
host[name] = callback.bind(host);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type Callbacks = Array<[string, EventListenerOrEventListenerObject]>;
|
|
2
|
+
export interface EventsInterface {
|
|
3
|
+
connect(): void;
|
|
4
|
+
disconnect(): void;
|
|
5
|
+
}
|
|
6
|
+
export interface EventElementInterface {
|
|
7
|
+
addEventListener: EventTarget["addEventListener"];
|
|
8
|
+
removeEventListener: EventTarget["removeEventListener"];
|
|
9
|
+
}
|
|
10
|
+
export interface EventParamsInterface {
|
|
11
|
+
host: EventElementInterface;
|
|
12
|
+
connected?: boolean;
|
|
13
|
+
target?: EventElementInterface;
|
|
14
|
+
callbacks: Callbacks;
|
|
15
|
+
}
|
|
16
|
+
export declare class Events implements EventsInterface {
|
|
17
|
+
#private;
|
|
18
|
+
constructor(params: EventParamsInterface);
|
|
19
|
+
connect(): void;
|
|
20
|
+
disconnect(): void;
|
|
21
|
+
}
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export class Events {
|
|
2
|
+
#connected = false;
|
|
3
|
+
#callbacks = [];
|
|
4
|
+
#target;
|
|
5
|
+
constructor(params) {
|
|
6
|
+
const { host, target, callbacks, connected } = params;
|
|
7
|
+
this.#target = target ?? host;
|
|
8
|
+
this.#callbacks = getBoundCallbacks(host, callbacks);
|
|
9
|
+
if (connected)
|
|
10
|
+
this.connect();
|
|
11
|
+
}
|
|
12
|
+
connect() {
|
|
13
|
+
if (this.#connected)
|
|
14
|
+
return;
|
|
15
|
+
this.#connected = true;
|
|
16
|
+
for (let [name, callback] of this.#callbacks) {
|
|
17
|
+
this.#target.addEventListener(name, callback);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
disconnect() {
|
|
21
|
+
if (!this.#connected)
|
|
22
|
+
return;
|
|
23
|
+
this.#connected = false;
|
|
24
|
+
for (let [name, callback] of this.#callbacks) {
|
|
25
|
+
this.#target.removeEventListener(name, callback);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function getBoundCallbacks(host, callbacks) {
|
|
30
|
+
let boundCallbacks = [];
|
|
31
|
+
for (let [name, callback] of callbacks) {
|
|
32
|
+
if (callback instanceof Function &&
|
|
33
|
+
!callback.hasOwnProperty("prototype")) {
|
|
34
|
+
callback = callback.bind(host);
|
|
35
|
+
}
|
|
36
|
+
boundCallbacks.push([name, callback]);
|
|
37
|
+
}
|
|
38
|
+
return boundCallbacks;
|
|
39
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface MicrotaskParamsInterface {
|
|
2
|
+
host: Object;
|
|
3
|
+
callback: Function;
|
|
4
|
+
}
|
|
5
|
+
export interface MicrotaskInterface {
|
|
6
|
+
queue(): void;
|
|
7
|
+
}
|
|
8
|
+
export declare class Microtask implements MicrotaskInterface {
|
|
9
|
+
#private;
|
|
10
|
+
constructor(params: MicrotaskParamsInterface);
|
|
11
|
+
queue(): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export class Microtask {
|
|
2
|
+
#queued = false;
|
|
3
|
+
#callback;
|
|
4
|
+
constructor(params) {
|
|
5
|
+
let { host, callback } = params;
|
|
6
|
+
this.queue = this.queue.bind(this);
|
|
7
|
+
this.#callback = callback;
|
|
8
|
+
if (callback instanceof Function &&
|
|
9
|
+
!callback.hasOwnProperty("prototype")) {
|
|
10
|
+
this.#callback = callback.bind(host);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
queue() {
|
|
14
|
+
if (this.#queued)
|
|
15
|
+
return;
|
|
16
|
+
this.#queued = true;
|
|
17
|
+
// could this be a bound function? less function creation
|
|
18
|
+
queueMicrotask(() => {
|
|
19
|
+
this.#queued = false;
|
|
20
|
+
this.#callback();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
package/dist/mod.d.ts
ADDED
package/dist/mod.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface QuerySelectorParamsInterface {
|
|
2
|
+
parent: ParentNode;
|
|
3
|
+
}
|
|
4
|
+
export interface QuerySelectorInterface {
|
|
5
|
+
querySelector(name: string): Element | undefined;
|
|
6
|
+
querySelectorAll(name: string): Element[] | undefined;
|
|
7
|
+
deleteAll(): void;
|
|
8
|
+
}
|
|
9
|
+
export declare class QuerySelector implements QuerySelectorInterface {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(params: QuerySelectorParamsInterface);
|
|
12
|
+
querySelector(selector: string): Element | undefined;
|
|
13
|
+
querySelectorAll(selector: string): Element[];
|
|
14
|
+
deleteAll(): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class QuerySelector {
|
|
2
|
+
#queries = new Map();
|
|
3
|
+
#params;
|
|
4
|
+
constructor(params) {
|
|
5
|
+
this.#params = params;
|
|
6
|
+
}
|
|
7
|
+
querySelector(selector) {
|
|
8
|
+
return getQuery(this.#params, this.#queries, selector)[0];
|
|
9
|
+
}
|
|
10
|
+
querySelectorAll(selector) {
|
|
11
|
+
return getQuery(this.#params, this.#queries, selector);
|
|
12
|
+
}
|
|
13
|
+
deleteAll() {
|
|
14
|
+
this.#queries = new Map();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function getQuery(params, queries, selector) {
|
|
18
|
+
const { parent } = params;
|
|
19
|
+
let results = queries.get(selector);
|
|
20
|
+
if (!results) {
|
|
21
|
+
results = Array.from(parent.querySelectorAll(selector));
|
|
22
|
+
queries.set(selector, results);
|
|
23
|
+
}
|
|
24
|
+
return results;
|
|
25
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type Subscribe<E, A> = (cb: E) => A;
|
|
2
|
+
export type Unsubscribe<A> = (affect: A) => void;
|
|
3
|
+
export interface SubscriptionInterface {
|
|
4
|
+
connect(): void;
|
|
5
|
+
disconnect(): void;
|
|
6
|
+
}
|
|
7
|
+
export interface SubscriptionParamsInterface<E, A> {
|
|
8
|
+
host: Object;
|
|
9
|
+
callback: E;
|
|
10
|
+
connected?: boolean;
|
|
11
|
+
subscribe: Subscribe<E, A>;
|
|
12
|
+
unsubscribe: Unsubscribe<A>;
|
|
13
|
+
}
|
|
14
|
+
export declare class Subscription<E, A> implements SubscriptionInterface {
|
|
15
|
+
#private;
|
|
16
|
+
constructor(params: SubscriptionParamsInterface<E, A>);
|
|
17
|
+
connect(): void;
|
|
18
|
+
disconnect(): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export class Subscription {
|
|
2
|
+
#callback;
|
|
3
|
+
#affect;
|
|
4
|
+
#subscribe;
|
|
5
|
+
#unsubscribe;
|
|
6
|
+
constructor(params) {
|
|
7
|
+
let { host, callback, connected, subscribe, unsubscribe } = params;
|
|
8
|
+
this.#subscribe = subscribe;
|
|
9
|
+
this.#unsubscribe = unsubscribe;
|
|
10
|
+
this.#callback = callback;
|
|
11
|
+
if (callback instanceof Function &&
|
|
12
|
+
!callback.hasOwnProperty("prototype")) {
|
|
13
|
+
this.#callback = callback.bind(host);
|
|
14
|
+
}
|
|
15
|
+
if (connected)
|
|
16
|
+
this.connect();
|
|
17
|
+
}
|
|
18
|
+
connect() {
|
|
19
|
+
if (!this.#affect)
|
|
20
|
+
this.#affect = this.#subscribe(this.#callback);
|
|
21
|
+
}
|
|
22
|
+
disconnect() {
|
|
23
|
+
if (this.#affect)
|
|
24
|
+
this.#unsubscribe(this.#affect);
|
|
25
|
+
}
|
|
26
|
+
}
|
package/dist/wc.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface WcElementInterface {
|
|
2
|
+
attachInternals: HTMLElement["attachInternals"];
|
|
3
|
+
attachShadow: Element["attachShadow"];
|
|
4
|
+
}
|
|
5
|
+
type FormDataArguments = Parameters<ElementInternals["setFormValue"]>;
|
|
6
|
+
export interface WcParamsInterface {
|
|
7
|
+
host: WcElementInterface;
|
|
8
|
+
adoptedStyleSheets?: CSSStyleSheet[];
|
|
9
|
+
shadowRootInit?: ShadowRootInit;
|
|
10
|
+
formValue?: FormDataArguments[0];
|
|
11
|
+
formState?: FormDataArguments[1];
|
|
12
|
+
}
|
|
13
|
+
export interface WcInterface {
|
|
14
|
+
readonly declarative: boolean;
|
|
15
|
+
readonly shadowRoot: ShadowRoot;
|
|
16
|
+
adoptedStyleSheets: DocumentOrShadowRoot["adoptedStyleSheets"];
|
|
17
|
+
setFormValue: ElementInternals["setFormValue"];
|
|
18
|
+
setValidity: ElementInternals["setValidity"];
|
|
19
|
+
reportValidity: ElementInternals["reportValidity"];
|
|
20
|
+
}
|
|
21
|
+
export declare class Wc implements WcInterface {
|
|
22
|
+
#private;
|
|
23
|
+
constructor(params: WcParamsInterface);
|
|
24
|
+
get declarative(): boolean;
|
|
25
|
+
get shadowRoot(): ShadowRoot;
|
|
26
|
+
get adoptedStyleSheets(): CSSStyleSheet[];
|
|
27
|
+
set adoptedStyleSheets(stylesheets: CSSStyleSheet[]);
|
|
28
|
+
checkValidity(): boolean;
|
|
29
|
+
reportValidity(): boolean;
|
|
30
|
+
setFormValue(value: FormDataArguments[0], state?: FormDataArguments[1]): void;
|
|
31
|
+
setValidity(flags?: ValidityStateFlags, message?: string, anchor?: HTMLElement): void;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
package/dist/wc.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const shadowRootInitFallback = {
|
|
2
|
+
mode: "closed",
|
|
3
|
+
};
|
|
4
|
+
export class Wc {
|
|
5
|
+
#declarative = true;
|
|
6
|
+
#internals;
|
|
7
|
+
#shadowRoot;
|
|
8
|
+
constructor(params) {
|
|
9
|
+
let { host, shadowRootInit, adoptedStyleSheets, formValue, formState } = params;
|
|
10
|
+
this.#internals = host.attachInternals();
|
|
11
|
+
let { shadowRoot } = this.#internals;
|
|
12
|
+
if (!shadowRoot) {
|
|
13
|
+
this.#declarative = false;
|
|
14
|
+
shadowRoot = host.attachShadow(shadowRootInit ?? shadowRootInitFallback);
|
|
15
|
+
}
|
|
16
|
+
this.#shadowRoot = shadowRoot;
|
|
17
|
+
if (formValue)
|
|
18
|
+
this.setFormValue(formValue, formState);
|
|
19
|
+
if (adoptedStyleSheets)
|
|
20
|
+
this.adoptedStyleSheets = adoptedStyleSheets;
|
|
21
|
+
}
|
|
22
|
+
get declarative() {
|
|
23
|
+
return this.#declarative;
|
|
24
|
+
}
|
|
25
|
+
get shadowRoot() {
|
|
26
|
+
return this.#shadowRoot;
|
|
27
|
+
}
|
|
28
|
+
get adoptedStyleSheets() {
|
|
29
|
+
return this.#shadowRoot.adoptedStyleSheets ?? [];
|
|
30
|
+
}
|
|
31
|
+
set adoptedStyleSheets(stylesheets) {
|
|
32
|
+
this.#shadowRoot.adoptedStyleSheets = stylesheets;
|
|
33
|
+
}
|
|
34
|
+
checkValidity() {
|
|
35
|
+
return this.#internals.checkValidity();
|
|
36
|
+
}
|
|
37
|
+
reportValidity() {
|
|
38
|
+
return this.#internals.reportValidity();
|
|
39
|
+
}
|
|
40
|
+
setFormValue(value, state) {
|
|
41
|
+
this.#internals.setFormValue(value, state);
|
|
42
|
+
}
|
|
43
|
+
setValidity(flags, message, anchor) {
|
|
44
|
+
this.#internals.setValidity(flags, message, anchor);
|
|
45
|
+
}
|
|
46
|
+
}
|
package/docs/bind.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Bind Controller
|
|
2
|
+
|
|
3
|
+
Bind functions to objects.
|
|
4
|
+
|
|
5
|
+
This utility is mainly designed to avoid using the constructor.
|
|
6
|
+
|
|
7
|
+
## How to use
|
|
8
|
+
|
|
9
|
+
Add a `Bind` controller to a web component.
|
|
10
|
+
|
|
11
|
+
Add a list of methods on instantiation. The methods cannot be `#private`.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { Bind } from "wctk";
|
|
15
|
+
|
|
16
|
+
class MyElement extends HTMLElement {
|
|
17
|
+
#bc = new Bind({
|
|
18
|
+
host: this,
|
|
19
|
+
callbacks: [this.elementCallback],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
elementCallback(e: KeyboardEvent) {
|
|
23
|
+
// This function can be passed as a callback to event listeners now!
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
package/docs/events.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Events Controller
|
|
2
|
+
|
|
3
|
+
Add methods as event listener callbacks.
|
|
4
|
+
|
|
5
|
+
## How to use
|
|
6
|
+
|
|
7
|
+
Add an `Events` controller to a web component. Use a params object on instantiation.
|
|
8
|
+
|
|
9
|
+
### Params
|
|
10
|
+
|
|
11
|
+
An Events `params` object has four properties:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
interface EventParams {
|
|
15
|
+
host: Node;
|
|
16
|
+
callbacks: Array<[string, EventListener]>;
|
|
17
|
+
connected?: boolean;
|
|
18
|
+
target?: Node;
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The `Events` controller binds a set of `callbacks` to a `host`.
|
|
23
|
+
|
|
24
|
+
Afterwards, the `Events` controller adds the callbacks as event listeners on a `target` node.
|
|
25
|
+
|
|
26
|
+
The `target` node can be a shadowRoot, a document, or the custom element itself.
|
|
27
|
+
|
|
28
|
+
If the `target` property is undefined, the `host` property is used as a fallback.
|
|
29
|
+
|
|
30
|
+
### Controller
|
|
31
|
+
|
|
32
|
+
Here is an example of using the `Events` controller.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { Events, Wc } from "wctk";
|
|
36
|
+
|
|
37
|
+
class MyElement extends HTMLElement {
|
|
38
|
+
#wc = new Wc({ this: host });
|
|
39
|
+
#ec = new Events({
|
|
40
|
+
host: this,
|
|
41
|
+
target: this.#wc.shadowRoot,
|
|
42
|
+
callbacks: [
|
|
43
|
+
["click", this.#onClick],
|
|
44
|
+
["keydown", this.#onKeyDown],
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
#onClick(e: PointerEvent) {
|
|
49
|
+
// do something with pointer events here!
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#onKeyDown(e: KeyEvent) {
|
|
53
|
+
// do something with key events here!
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// lifecycle method
|
|
57
|
+
connectedCallback() {
|
|
58
|
+
this.#ec.connect();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// lifecycle method
|
|
62
|
+
disconnectedCallback() {
|
|
63
|
+
this.#ec.disconnect();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Shortcut life cycle methods
|
|
69
|
+
|
|
70
|
+
In the example below, the `connected` property is set to true and callbacks are immediately added to the `target`.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { Events, Wc } from "wctk";
|
|
74
|
+
|
|
75
|
+
class MyElement extends HTMLElement {
|
|
76
|
+
#wc = new Wc({ this: host });
|
|
77
|
+
#ec = new Events({
|
|
78
|
+
host: this,
|
|
79
|
+
target: this.#wc.shadowRoot,
|
|
80
|
+
connected: true,
|
|
81
|
+
callbacks: [
|
|
82
|
+
["click", this.#onClick],
|
|
83
|
+
["keydown", this.#onKeyDown],
|
|
84
|
+
],
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
#onClick(e: PointerEvent) {
|
|
88
|
+
// do something with pointer events here!
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
#onKeyDown(e: KeyEvent) {
|
|
92
|
+
// do something with key events here!
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### More complex interactions
|
|
98
|
+
|
|
99
|
+
If your component requires more complex declarative interactions, consider [superaction](https://github.com/w-lfpup/superaction-js/)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Microtask Controller
|
|
2
|
+
|
|
3
|
+
Add callbacks to the `microtask queue`.
|
|
4
|
+
|
|
5
|
+
## How to use
|
|
6
|
+
|
|
7
|
+
Add a `Microtask` controller to a web component. 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({
|
|
20
|
+
host: this,
|
|
21
|
+
callback: this.#render,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
#render() {
|
|
25
|
+
// update DOM here!
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// lifecycle method
|
|
29
|
+
attributeChangedCallback() {
|
|
30
|
+
this.#rc.queue();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The `Microtask.queue()` method can be called multiple times per event loop but the callback will only be called _once_ per microtask in the event loop.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# QuerySelector Controller
|
|
2
|
+
|
|
3
|
+
Lazily map selector queries.
|
|
4
|
+
|
|
5
|
+
## How to use
|
|
6
|
+
|
|
7
|
+
Add a `QuerySelector` controller to a web component.
|
|
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({
|
|
25
|
+
parent: this.#wc.shadowRoot,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
showcaseApi() {
|
|
29
|
+
// first Element or undefined
|
|
30
|
+
let greeting = this.#qc.querySelector("[greeting]");
|
|
31
|
+
|
|
32
|
+
// Element[]
|
|
33
|
+
let greetings = this.#qc.querySelectorAll("[greeting]");
|
|
34
|
+
|
|
35
|
+
// create new cache
|
|
36
|
+
this.#qc.deleteAll();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|