@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.
Files changed (49) hide show
  1. package/.github/workflows/build_and_test.yml +18 -0
  2. package/.prettierignore +5 -0
  3. package/.prettierrc +5 -0
  4. package/LICENSE +28 -0
  5. package/README.md +49 -0
  6. package/dist/bind.d.ts +7 -0
  7. package/dist/bind.js +14 -0
  8. package/dist/events.d.ts +21 -0
  9. package/dist/events.js +39 -0
  10. package/dist/microtask.d.ts +12 -0
  11. package/dist/microtask.js +23 -0
  12. package/dist/mod.d.ts +6 -0
  13. package/dist/mod.js +6 -0
  14. package/dist/query_selector.d.ts +15 -0
  15. package/dist/query_selector.js +25 -0
  16. package/dist/subscription.d.ts +19 -0
  17. package/dist/subscription.js +26 -0
  18. package/dist/wc.d.ts +33 -0
  19. package/dist/wc.js +46 -0
  20. package/docs/bind.md +26 -0
  21. package/docs/events.md +99 -0
  22. package/docs/microtask.md +35 -0
  23. package/docs/query_selector.md +39 -0
  24. package/docs/subscription.md +85 -0
  25. package/docs/wc.md +57 -0
  26. package/examples/counter/index.html +30 -0
  27. package/examples/counter/mod.js +39 -0
  28. package/examples/counter/mod.ts +51 -0
  29. package/examples/form_associated/index.html +31 -0
  30. package/examples/form_associated/mod.js +13 -0
  31. package/examples/form_associated/mod.ts +21 -0
  32. package/examples/form_associated/text_input.js +25 -0
  33. package/examples/form_associated/text_input.ts +28 -0
  34. package/examples/stopwatch/index.html +29 -0
  35. package/examples/stopwatch/mod.js +8 -0
  36. package/examples/stopwatch/mod.ts +11 -0
  37. package/examples/stopwatch/stopwatch.js +45 -0
  38. package/examples/stopwatch/stopwatch.ts +61 -0
  39. package/examples/tsconfig.json +10 -0
  40. package/package.json +22 -0
  41. package/src/bind.ts +22 -0
  42. package/src/events.ts +67 -0
  43. package/src/microtask.ts +37 -0
  44. package/src/mod.ts +6 -0
  45. package/src/query_selector.ts +46 -0
  46. package/src/subscription.ts +47 -0
  47. package/src/tsconfig.json +7 -0
  48. package/src/wc.ts +87 -0
  49. 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
@@ -0,0 +1,5 @@
1
+ dist/
2
+
3
+ *.html
4
+ *.js
5
+ package-lock.json
package/.prettierrc ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "trailingComma": "all",
3
+ "useTabs": true,
4
+ "tabWidth": 4
5
+ }
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
@@ -0,0 +1,7 @@
1
+ export interface BindParamsInterface {
2
+ host: Object;
3
+ callbacks: Function[];
4
+ }
5
+ export declare class Bind {
6
+ constructor(params: BindParamsInterface);
7
+ }
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
+ }
@@ -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
@@ -0,0 +1,6 @@
1
+ export * from "./bind.js";
2
+ export * from "./events.js";
3
+ export * from "./microtask.js";
4
+ export * from "./subscription.js";
5
+ export * from "./query_selector.js";
6
+ export * from "./wc.js";
package/dist/mod.js ADDED
@@ -0,0 +1,6 @@
1
+ export * from "./bind.js";
2
+ export * from "./events.js";
3
+ export * from "./microtask.js";
4
+ export * from "./subscription.js";
5
+ export * from "./query_selector.js";
6
+ export * from "./wc.js";
@@ -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
+ ```