@alixex/ranview 0.1.0-alpha.0 → 0.2.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/README.md +100 -26
- package/dist/builder.d.ts +128 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/core.d.ts +5 -116
- package/dist/core.d.ts.map +1 -1
- package/dist/events.d.ts +2 -63
- package/dist/events.d.ts.map +1 -1
- package/dist/factories-DYUNLmkS.js +203 -0
- package/dist/factories-DYUNLmkS.js.map +1 -0
- package/dist/factories.d.ts +22 -0
- package/dist/factories.d.ts.map +1 -0
- package/dist/factory.d.ts +1 -32
- package/dist/factory.d.ts.map +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.js +99 -248
- package/dist/index.js.map +1 -1
- package/dist/selectors.d.ts +1 -1
- package/dist/static.d.ts +18 -0
- package/dist/static.d.ts.map +1 -0
- package/dist/static.js +25 -0
- package/dist/static.js.map +1 -0
- package/package.json +15 -12
- package/dist/index.cjs +0 -2
- package/dist/index.cjs.map +0 -1
- package/dist/mocks-DvJQ7jGP.cjs +0 -2
- package/dist/mocks-DvJQ7jGP.cjs.map +0 -1
- package/dist/mocks.cjs +0 -1
- package/dist/signal.cjs +0 -2
- package/dist/signal.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -7,15 +7,57 @@ the exact node bound to it.
|
|
|
7
7
|
It runs in a browser against real elements, and in Node against a mock that serialises to
|
|
8
8
|
HTML, so the same view code renders client-side or to a string.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
The DOM builder and reactive primitives behind [ranui](https://www.npmjs.com/package/ranui).
|
|
11
|
+
Use it on its own for application views; `ranui/builder` exposes the builder APIs for
|
|
12
|
+
ranui consumers. It does not register custom elements.
|
|
13
|
+
|
|
14
|
+
**ESM-only · TypeScript.** Use Node 24+ for server rendering;
|
|
15
|
+
browser reactivity requires a browser DOM. There is no hydration API.
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
14
18
|
|
|
15
19
|
```sh
|
|
16
|
-
npm
|
|
20
|
+
npm install @alixex/ranview
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { Div, Span, ButtonBuilder, signal, createRoot } from '@alixex/ranview';
|
|
25
|
+
|
|
26
|
+
const dispose = createRoot((dispose) => {
|
|
27
|
+
const [count, setCount] = signal(0);
|
|
28
|
+
const view = Div()
|
|
29
|
+
.children(
|
|
30
|
+
Span().text(() => `Count: ${count()}`),
|
|
31
|
+
ButtonBuilder()
|
|
32
|
+
.text('+1')
|
|
33
|
+
.on('click', () => setCount((value) => value + 1)),
|
|
34
|
+
)
|
|
35
|
+
.build();
|
|
36
|
+
document.body.appendChild(view);
|
|
37
|
+
return () => {
|
|
38
|
+
view.remove();
|
|
39
|
+
dispose();
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
// Call dispose() when the page is unmounted.
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
For a static server snapshot, import from `@alixex/ranview/static`:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { Div } from '@alixex/ranview/static';
|
|
49
|
+
|
|
50
|
+
console.log(Div().text('Hello').serialize()); // <div>Hello</div>
|
|
17
51
|
```
|
|
18
52
|
|
|
53
|
+
## Guide
|
|
54
|
+
|
|
55
|
+
- [Elements](#1-elements) — factories, SVG, attributes and events
|
|
56
|
+
- [Reactivity](#2-reactivity) — signals, bindings and control flow
|
|
57
|
+
- [Ownership](#3-ownership--the-key-to-leak-free-apps-and-mpas) — disposal and page scopes
|
|
58
|
+
- [EventManager](#4-web-component-listeners--eventmanager) — listener lifecycle
|
|
59
|
+
- [Static DOM and SSR](#static-dom-and-ssr--ssg-snapshots) — one-shot snapshots
|
|
60
|
+
|
|
19
61
|
> **Principle: build once, update in place.** A view function runs once; state
|
|
20
62
|
> changes flow through fine-grained bindings, never by re-running the view. Pick
|
|
21
63
|
> the primitive that matches the shape — value → getter binding; conditional →
|
|
@@ -23,27 +65,7 @@ npm i @alixex/ranview
|
|
|
23
65
|
> [`For`](#keyed-lists--for) / [`Index`](#index-lists--index). A raw getter child
|
|
24
66
|
> is the coarse fallback (rebuilds its whole region on any read).
|
|
25
67
|
|
|
26
|
-
|
|
27
|
-
import {
|
|
28
|
-
View,
|
|
29
|
-
Div,
|
|
30
|
-
Span,
|
|
31
|
-
ButtonBuilder, // element factories
|
|
32
|
-
signal,
|
|
33
|
-
computed,
|
|
34
|
-
createEffect,
|
|
35
|
-
batch,
|
|
36
|
-
untrack, // reactivity
|
|
37
|
-
createRoot,
|
|
38
|
-
onCleanup,
|
|
39
|
-
getOwner,
|
|
40
|
-
runWithOwner, // ownership
|
|
41
|
-
EventManager, // lifecycle-scoped events
|
|
42
|
-
} from '@alixex/ranview';
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
The builder does **not** register custom elements. To use `<r-button>` etc.,
|
|
46
|
-
also import the component subpath once: `import 'ranui/button'`.
|
|
68
|
+
To use `<r-button>` etc., import the component subpath once: `import 'ranui/button'`.
|
|
47
69
|
|
|
48
70
|
---
|
|
49
71
|
|
|
@@ -444,3 +466,55 @@ disconnectedCallback() { this.events.abort(); } // resets for the next connect
|
|
|
444
466
|
- **Components vs builder**: use registered `<r-*>` elements (see
|
|
445
467
|
[ranui's component list](../ranui/docs/COMPONENTS.md)) for rich widgets; use the builder for
|
|
446
468
|
layout/glue and reactive views. Theme tokens: [ranui's DESIGN.md](../ranui/docs/DESIGN.md).
|
|
469
|
+
|
|
470
|
+
## Static DOM and SSR / SSG snapshots
|
|
471
|
+
|
|
472
|
+
Use the static entry when nodes will not update after construction:
|
|
473
|
+
|
|
474
|
+
```ts
|
|
475
|
+
import { Div, Span, For } from '@alixex/ranview/static';
|
|
476
|
+
|
|
477
|
+
const page = Div()
|
|
478
|
+
.class('page')
|
|
479
|
+
.children(
|
|
480
|
+
Span().text(() => data.title),
|
|
481
|
+
For({
|
|
482
|
+
each: () => data.items,
|
|
483
|
+
key: (item) => item.id,
|
|
484
|
+
render: (item) => Span().text(item.title),
|
|
485
|
+
}),
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
const element = page.build(); // real DOM in a browser, a mock node in Node
|
|
489
|
+
const html = page.serialize(); // escaped HTML suitable for SSR / SSG
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
The static entry shares node operations with the default entry but imports no signal
|
|
493
|
+
runtime. Binding and child getters are evaluated when applied, once per occurrence.
|
|
494
|
+
`Show`, `Switch`, `For`, and `Index` return one-shot child getters: there are no effects,
|
|
495
|
+
subscriptions, list reconciliation, or comment markers. `For` accepts the familiar
|
|
496
|
+
`key` option for source compatibility but does not use it to reconcile a snapshot.
|
|
497
|
+
Refs, SVG namespaces, shadow construction, event listeners, and serialization work
|
|
498
|
+
as with the default entry. Static listeners still handle events; static does not mean
|
|
499
|
+
that clicks are disabled. Do not pass reactive `For` / `Index` handles to this entry;
|
|
500
|
+
import the control-flow helpers from the same entry as your factories.
|
|
501
|
+
|
|
502
|
+
Static getters are ordinary callbacks. If you build a static subtree _inside_ an
|
|
503
|
+
existing reactive effect and read signals, wrap construction in `untrack` from the
|
|
504
|
+
default entry to avoid subscribing that surrounding effect.
|
|
505
|
+
|
|
506
|
+
The default entry now also treats server rendering as a snapshot: bindings and
|
|
507
|
+
control flow execute without tracking, and subsequent signal writes do not change
|
|
508
|
+
already-built server nodes. Browser bindings from the default entry remain reactive.
|
|
509
|
+
Direct values already avoid effects in both entries, so prefer them when no getter
|
|
510
|
+
is needed. This change does not globally disable `signal`, `computed`, or explicit
|
|
511
|
+
`createEffect` calls in server application code.
|
|
512
|
+
|
|
513
|
+
Both entries retain an intermediate node tree. There is no hydration or direct HTML
|
|
514
|
+
streaming API added by the static entry; use it when refs, node manipulation, and
|
|
515
|
+
shadow serialization matter.
|
|
516
|
+
|
|
517
|
+
Run `pnpm -F @alixex/ranview bench:ssr` for build-and-serialize timings covering
|
|
518
|
+
plain values, getters, conditionals and 100-item lists. The benchmark checks output
|
|
519
|
+
equivalence before timing and reports the median of seven warmed batches. Results
|
|
520
|
+
are local microbenchmarks, not end-to-end request latency or a memory measurement.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { EventManager } from "./events.js";
|
|
2
|
+
import type { Child, Ref } from "./core.js";
|
|
3
|
+
type Getter<T> = () => T;
|
|
4
|
+
export interface BuilderRuntime {
|
|
5
|
+
bind<V>(value: V | Getter<V>, apply: (value: V) => void): void;
|
|
6
|
+
append(parent: Node, items: Child[]): void;
|
|
7
|
+
}
|
|
8
|
+
/** One-shot node operations shared by SSR and the signal-free static entry. */
|
|
9
|
+
export declare const staticRuntime: BuilderRuntime;
|
|
10
|
+
export declare const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
11
|
+
export declare class ElementBuilder<T extends HTMLElement = HTMLElement> {
|
|
12
|
+
private runtime;
|
|
13
|
+
private el;
|
|
14
|
+
/**
|
|
15
|
+
* @param tag element name
|
|
16
|
+
* @param namespace element namespace; inferred for SVG-only tags, and forced
|
|
17
|
+
* by `Svg()` for the tags SVG shares with HTML.
|
|
18
|
+
*
|
|
19
|
+
* The namespace matters more than it looks. `document.createElement('svg')`
|
|
20
|
+
* yields an `HTMLUnknownElement`: the browser does not render it as SVG, and
|
|
21
|
+
* -- because HTML lowercases attribute names -- `viewBox` lands as `viewbox`,
|
|
22
|
+
* which SVG reads case-sensitively and therefore ignores. An icon built that
|
|
23
|
+
* way is invisible, with markup that looks right in a serialized page.
|
|
24
|
+
*/
|
|
25
|
+
constructor(tag: string, namespace?: string, runtime?: BuilderRuntime);
|
|
26
|
+
id(value: string): this;
|
|
27
|
+
/**
|
|
28
|
+
* Apply a value now, or bind it reactively when a getter is passed.
|
|
29
|
+
* A getter creates an effect owned by the current reactive scope (createRoot),
|
|
30
|
+
* so the binding is disposed automatically when that scope is torn down.
|
|
31
|
+
*/
|
|
32
|
+
private bind;
|
|
33
|
+
/**
|
|
34
|
+
* Sets the element's class.
|
|
35
|
+
*
|
|
36
|
+
* Written through `setAttribute` rather than `className`. On an SVG element `className`
|
|
37
|
+
* is a read-only `SVGAnimatedString`, so assigning to it throws
|
|
38
|
+
* `Cannot set property className of [object SVGElement] which has only a getter` — which,
|
|
39
|
+
* happening inside a custom element's constructor, leaves the element un-upgraded with
|
|
40
|
+
* no shadow root rather than reporting a styling problem. `setAttribute` is equivalent
|
|
41
|
+
* for HTML elements and correct for both.
|
|
42
|
+
*/
|
|
43
|
+
class(name: string | Getter<string>): this;
|
|
44
|
+
addClass(...names: string[]): this;
|
|
45
|
+
removeClass(...names: string[]): this;
|
|
46
|
+
attr(name: string, value: string | Getter<string>): this;
|
|
47
|
+
attrs(values: Record<string, string | number | boolean | null | undefined>): this;
|
|
48
|
+
boolAttr(name: string, value: boolean | Getter<boolean>, enabledValue?: string): this;
|
|
49
|
+
part(value: string | Getter<string>): this;
|
|
50
|
+
data(key: string, value: string | Getter<string>): this;
|
|
51
|
+
style(keyOrMap: string | Record<string, string>, value?: string | Getter<string>): this;
|
|
52
|
+
cssVar(name: string, value: string | Getter<string>): this;
|
|
53
|
+
aria(key: string, value: string | Getter<string>): this;
|
|
54
|
+
role(value: string | Getter<string>): this;
|
|
55
|
+
tabIndex(value: number): this;
|
|
56
|
+
label(value: string | Getter<string>): this;
|
|
57
|
+
labelledBy(id: string | Getter<string>): this;
|
|
58
|
+
describedBy(id: string | Getter<string>): this;
|
|
59
|
+
ariaHidden(hidden?: boolean): this;
|
|
60
|
+
/**
|
|
61
|
+
* Permanent build-time listener — tied to the element's lifetime.
|
|
62
|
+
* Use for internal shadow DOM elements created in the constructor.
|
|
63
|
+
*/
|
|
64
|
+
on<K extends keyof HTMLElementEventMap>(type: K, listener: (this: T, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): this;
|
|
65
|
+
/**
|
|
66
|
+
* Lifecycle-managed listener — registered into an EventManager.
|
|
67
|
+
* Use in connectedCallback when building elements that need cleanup on disconnect.
|
|
68
|
+
* Call manager.abort() in disconnectedCallback to remove all listeners at once.
|
|
69
|
+
*/
|
|
70
|
+
listen<K extends keyof HTMLElementEventMap>(manager: EventManager, type: K, handler: (this: T, ev: HTMLElementEventMap[K]) => any, options?: Omit<AddEventListenerOptions, 'signal'>): this;
|
|
71
|
+
/**
|
|
72
|
+
* Delegated listener — the built element acts as the parent container.
|
|
73
|
+
* Fires handler only when the event originates from a descendant matching selector.
|
|
74
|
+
* Registered into an EventManager so it is cleaned up with manager.abort().
|
|
75
|
+
*
|
|
76
|
+
* Div().class('list')
|
|
77
|
+
* .children(...)
|
|
78
|
+
* .delegate(scope, '.item', 'click', (ev, item) => handleItem(item))
|
|
79
|
+
* .build();
|
|
80
|
+
*/
|
|
81
|
+
delegate<K extends keyof HTMLElementEventMap>(manager: EventManager, selector: string, type: K, handler: (ev: HTMLElementEventMap[K], target: Element) => void, options?: Omit<AddEventListenerOptions, 'signal'>): this;
|
|
82
|
+
children(...items: Child[]): this;
|
|
83
|
+
replaceChildren(...items: Child[]): this;
|
|
84
|
+
text(value: string | Getter<string>): this;
|
|
85
|
+
/**
|
|
86
|
+
* Set the element's content from a string of markup, without escaping it.
|
|
87
|
+
*
|
|
88
|
+
* The counterpart to `text()`, and named for the difference: `text()` escapes,
|
|
89
|
+
* this does not. Everything handed here is parsed as HTML, so anything that
|
|
90
|
+
* came from outside the program must be sanitized before it arrives.
|
|
91
|
+
*
|
|
92
|
+
* It exists because some content simply *is* HTML, and a builder without this
|
|
93
|
+
* cannot express it at all: markdown rendered for a page, a fragment assembled
|
|
94
|
+
* somewhere else, a translated string that carries its own emphasis
|
|
95
|
+
* (`Local-first · <b>open source</b>`). That gap is why a static-site
|
|
96
|
+
* generator with markdown in it could not be written against this API and had
|
|
97
|
+
* to concatenate strings instead.
|
|
98
|
+
*
|
|
99
|
+
* Like `textContent`, this replaces whatever content the element had --
|
|
100
|
+
* including anything set by `text()` or `children()` before it.
|
|
101
|
+
*/
|
|
102
|
+
unsafeHtml(value: string | Getter<string>): this;
|
|
103
|
+
ref(holder: Ref<T>): this;
|
|
104
|
+
shadow(options?: ShadowRootInit): ShadowBuilder<T>;
|
|
105
|
+
build(): T;
|
|
106
|
+
serialize(): string;
|
|
107
|
+
}
|
|
108
|
+
export declare class ShadowBuilder<T extends HTMLElement = HTMLElement> {
|
|
109
|
+
private runtime;
|
|
110
|
+
private root;
|
|
111
|
+
private hostEl;
|
|
112
|
+
/**
|
|
113
|
+
* `options` is taken and discarded: `attachShadow` has already consumed it by the time
|
|
114
|
+
* a builder exists, and nothing ever read the copy this class used to keep. The
|
|
115
|
+
* parameter stays so the call signature is unchanged.
|
|
116
|
+
*/
|
|
117
|
+
constructor(host: T, root: ShadowRoot, _options: ShadowRootInit, runtime?: BuilderRuntime);
|
|
118
|
+
children(...items: Child[]): this;
|
|
119
|
+
adoptSheet(...sheets: CSSStyleSheet[]): this;
|
|
120
|
+
css(cssText: string): this;
|
|
121
|
+
done(): {
|
|
122
|
+
host: T;
|
|
123
|
+
shadow: ShadowRoot;
|
|
124
|
+
};
|
|
125
|
+
serialize(): string;
|
|
126
|
+
}
|
|
127
|
+
export {};
|
|
128
|
+
//# sourceMappingURL=builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
|
package/dist/core.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { type Getter } from
|
|
1
|
+
import { ElementBuilder as BaseElementBuilder } from "./builder.js";
|
|
2
|
+
import { type Getter } from "./signal.js";
|
|
3
3
|
export interface Ref<T extends HTMLElement = HTMLElement> {
|
|
4
4
|
current: T | null;
|
|
5
5
|
}
|
|
@@ -135,120 +135,9 @@ export type IndexHandle = {
|
|
|
135
135
|
* );
|
|
136
136
|
*/
|
|
137
137
|
export declare function Index<T>(options: IndexOptions<T>): IndexHandle;
|
|
138
|
-
export
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* @param tag element name
|
|
143
|
-
* @param namespace element namespace; inferred for SVG-only tags, and forced
|
|
144
|
-
* by `Svg()` for the tags SVG shares with HTML.
|
|
145
|
-
*
|
|
146
|
-
* The namespace matters more than it looks. `document.createElement('svg')`
|
|
147
|
-
* yields an `HTMLUnknownElement`: the browser does not render it as SVG, and
|
|
148
|
-
* -- because HTML lowercases attribute names -- `viewBox` lands as `viewbox`,
|
|
149
|
-
* which SVG reads case-sensitively and therefore ignores. An icon built that
|
|
150
|
-
* way is invisible, with markup that looks right in a serialized page.
|
|
151
|
-
*/
|
|
138
|
+
export { ShadowBuilder, SVG_NAMESPACE } from "./builder.js";
|
|
139
|
+
/** Reactive browser builder; SSR bindings are untracked snapshots. */
|
|
140
|
+
export declare class ElementBuilder<T extends HTMLElement = HTMLElement> extends BaseElementBuilder<T> {
|
|
152
141
|
constructor(tag: string, namespace?: string);
|
|
153
|
-
id(value: string): this;
|
|
154
|
-
/**
|
|
155
|
-
* Apply a value now, or bind it reactively when a getter is passed.
|
|
156
|
-
* A getter creates an effect owned by the current reactive scope (createRoot),
|
|
157
|
-
* so the binding is disposed automatically when that scope is torn down.
|
|
158
|
-
*/
|
|
159
|
-
private bind;
|
|
160
|
-
/**
|
|
161
|
-
* Sets the element's class.
|
|
162
|
-
*
|
|
163
|
-
* Written through `setAttribute` rather than `className`. On an SVG element `className`
|
|
164
|
-
* is a read-only `SVGAnimatedString`, so assigning to it throws
|
|
165
|
-
* `Cannot set property className of [object SVGElement] which has only a getter` — which,
|
|
166
|
-
* happening inside a custom element's constructor, leaves the element un-upgraded with
|
|
167
|
-
* no shadow root rather than reporting a styling problem. `setAttribute` is equivalent
|
|
168
|
-
* for HTML elements and correct for both.
|
|
169
|
-
*/
|
|
170
|
-
class(name: string | Getter<string>): this;
|
|
171
|
-
addClass(...names: string[]): this;
|
|
172
|
-
removeClass(...names: string[]): this;
|
|
173
|
-
attr(name: string, value: string | Getter<string>): this;
|
|
174
|
-
attrs(values: Record<string, string | number | boolean | null | undefined>): this;
|
|
175
|
-
boolAttr(name: string, value: boolean | Getter<boolean>, enabledValue?: string): this;
|
|
176
|
-
part(value: string | Getter<string>): this;
|
|
177
|
-
data(key: string, value: string | Getter<string>): this;
|
|
178
|
-
style(keyOrMap: string | Record<string, string>, value?: string | Getter<string>): this;
|
|
179
|
-
cssVar(name: string, value: string | Getter<string>): this;
|
|
180
|
-
aria(key: string, value: string | Getter<string>): this;
|
|
181
|
-
role(value: string | Getter<string>): this;
|
|
182
|
-
tabIndex(value: number): this;
|
|
183
|
-
label(value: string | Getter<string>): this;
|
|
184
|
-
labelledBy(id: string | Getter<string>): this;
|
|
185
|
-
describedBy(id: string | Getter<string>): this;
|
|
186
|
-
ariaHidden(hidden?: boolean): this;
|
|
187
|
-
/**
|
|
188
|
-
* Permanent build-time listener — tied to the element's lifetime.
|
|
189
|
-
* Use for internal shadow DOM elements created in the constructor.
|
|
190
|
-
*/
|
|
191
|
-
on<K extends keyof HTMLElementEventMap>(type: K, listener: (this: T, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): this;
|
|
192
|
-
/**
|
|
193
|
-
* Lifecycle-managed listener — registered into an EventManager.
|
|
194
|
-
* Use in connectedCallback when building elements that need cleanup on disconnect.
|
|
195
|
-
* Call manager.abort() in disconnectedCallback to remove all listeners at once.
|
|
196
|
-
*/
|
|
197
|
-
listen<K extends keyof HTMLElementEventMap>(manager: EventManager, type: K, handler: (this: T, ev: HTMLElementEventMap[K]) => any, options?: Omit<AddEventListenerOptions, 'signal'>): this;
|
|
198
|
-
/**
|
|
199
|
-
* Delegated listener — the built element acts as the parent container.
|
|
200
|
-
* Fires handler only when the event originates from a descendant matching selector.
|
|
201
|
-
* Registered into an EventManager so it is cleaned up with manager.abort().
|
|
202
|
-
*
|
|
203
|
-
* Div().class('list')
|
|
204
|
-
* .children(...)
|
|
205
|
-
* .delegate(scope, '.item', 'click', (ev, item) => handleItem(item))
|
|
206
|
-
* .build();
|
|
207
|
-
*/
|
|
208
|
-
delegate<K extends keyof HTMLElementEventMap>(manager: EventManager, selector: string, type: K, handler: (ev: HTMLElementEventMap[K], target: Element) => void, options?: Omit<AddEventListenerOptions, 'signal'>): this;
|
|
209
|
-
children(...items: Child[]): this;
|
|
210
|
-
replaceChildren(...items: Child[]): this;
|
|
211
|
-
text(value: string | Getter<string>): this;
|
|
212
|
-
/**
|
|
213
|
-
* Set the element's content from a string of markup, without escaping it.
|
|
214
|
-
*
|
|
215
|
-
* The counterpart to `text()`, and named for the difference: `text()` escapes,
|
|
216
|
-
* this does not. Everything handed here is parsed as HTML, so anything that
|
|
217
|
-
* came from outside the program must be sanitized before it arrives.
|
|
218
|
-
*
|
|
219
|
-
* It exists because some content simply *is* HTML, and a builder without this
|
|
220
|
-
* cannot express it at all: markdown rendered for a page, a fragment assembled
|
|
221
|
-
* somewhere else, a translated string that carries its own emphasis
|
|
222
|
-
* (`Local-first · <b>open source</b>`). That gap is why a static-site
|
|
223
|
-
* generator with markdown in it could not be written against this API and had
|
|
224
|
-
* to concatenate strings instead.
|
|
225
|
-
*
|
|
226
|
-
* Like `textContent`, this replaces whatever content the element had --
|
|
227
|
-
* including anything set by `text()` or `children()` before it.
|
|
228
|
-
*/
|
|
229
|
-
unsafeHtml(value: string | Getter<string>): this;
|
|
230
|
-
ref(holder: Ref<T>): this;
|
|
231
|
-
shadow(options?: ShadowRootInit): ShadowBuilder<T>;
|
|
232
|
-
build(): T;
|
|
233
|
-
serialize(): string;
|
|
234
142
|
}
|
|
235
|
-
export declare class ShadowBuilder<T extends HTMLElement = HTMLElement> {
|
|
236
|
-
private root;
|
|
237
|
-
private hostEl;
|
|
238
|
-
/**
|
|
239
|
-
* `options` is taken and discarded: `attachShadow` has already consumed it by the time
|
|
240
|
-
* a builder exists, and nothing ever read the copy this class used to keep. The
|
|
241
|
-
* parameter stays so the call signature is unchanged.
|
|
242
|
-
*/
|
|
243
|
-
constructor(host: T, root: ShadowRoot, _options: ShadowRootInit);
|
|
244
|
-
children(...items: Child[]): this;
|
|
245
|
-
adoptSheet(...sheets: CSSStyleSheet[]): this;
|
|
246
|
-
css(cssText: string): this;
|
|
247
|
-
done(): {
|
|
248
|
-
host: T;
|
|
249
|
-
shadow: ShadowRoot;
|
|
250
|
-
};
|
|
251
|
-
serialize(): string;
|
|
252
|
-
}
|
|
253
|
-
export {};
|
|
254
143
|
//# sourceMappingURL=core.d.ts.map
|
package/dist/core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
|
package/dist/events.d.ts
CHANGED
|
@@ -1,64 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Web Component usage:
|
|
5
|
-
*
|
|
6
|
-
* private _events = new EventManager();
|
|
7
|
-
*
|
|
8
|
-
* connectedCallback() {
|
|
9
|
-
* this._events
|
|
10
|
-
* .on(this._input, 'input', this.handleInput)
|
|
11
|
-
* .on(this, 'click', this.handleClick, { capture: true });
|
|
12
|
-
* }
|
|
13
|
-
*
|
|
14
|
-
* disconnectedCallback() {
|
|
15
|
-
* this._events.abort(); // removes all listeners, resets for next connect
|
|
16
|
-
* }
|
|
17
|
-
*
|
|
18
|
-
* Page development usage:
|
|
19
|
-
*
|
|
20
|
-
* function initSection(container: HTMLElement) {
|
|
21
|
-
* const scope = new EventManager();
|
|
22
|
-
*
|
|
23
|
-
* scope
|
|
24
|
-
* .on(input, 'input', handleSearch)
|
|
25
|
-
* .delegate(container, '[data-action]', 'click', (ev, target) => {
|
|
26
|
-
* handleAction(target.getAttribute('data-action'));
|
|
27
|
-
* });
|
|
28
|
-
*
|
|
29
|
-
* return () => scope.abort(); // call on section teardown
|
|
30
|
-
* }
|
|
31
|
-
*/
|
|
32
|
-
export declare class EventManager {
|
|
33
|
-
/**
|
|
34
|
-
* Created on first use, and dropped again by {@link abort}.
|
|
35
|
-
*
|
|
36
|
-
* No component registers a listener in its constructor — they all wait for
|
|
37
|
-
* `connectedCallback` — and some managers, like the drag-scoped one in `r-progress`,
|
|
38
|
-
* only ever register during an interaction that may not happen. Allocating eagerly
|
|
39
|
-
* charged every construction, and every `abort()`, for a controller that might go unused:
|
|
40
|
-
* about 30% of a mount/unmount cycle for `r-progress`.
|
|
41
|
-
*/
|
|
42
|
-
private ac;
|
|
43
|
-
/** The underlying AbortSignal — pass to addEventListener options directly if needed. */
|
|
44
|
-
get signal(): AbortSignal;
|
|
45
|
-
on<K extends keyof HTMLElementEventMap>(target: HTMLElement, type: K, handler: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: Omit<AddEventListenerOptions, 'signal'>): this;
|
|
46
|
-
on(target: EventTarget, type: string, handler: EventListener, options?: Omit<AddEventListenerOptions, 'signal'>): this;
|
|
47
|
-
/**
|
|
48
|
-
* Event delegation — attach one listener to `parent`, fire `handler` only when
|
|
49
|
-
* the event originates from a descendant matching `selector`.
|
|
50
|
-
*
|
|
51
|
-
* The handler receives the original event and the matched element as arguments.
|
|
52
|
-
*
|
|
53
|
-
* scope.delegate(list, '.item', 'click', (ev, item) => {
|
|
54
|
-
* console.log(item.dataset.id);
|
|
55
|
-
* });
|
|
56
|
-
*/
|
|
57
|
-
delegate<K extends keyof HTMLElementEventMap>(parent: HTMLElement, selector: string, type: K, handler: (ev: HTMLElementEventMap[K], target: Element) => void, options?: Omit<AddEventListenerOptions, 'signal'>): this;
|
|
58
|
-
/**
|
|
59
|
-
* Remove all registered listeners and drop the controller.
|
|
60
|
-
* Safe to call multiple times; the next on() / delegate() starts a fresh one.
|
|
61
|
-
*/
|
|
62
|
-
abort(): void;
|
|
63
|
-
}
|
|
1
|
+
/** Lifecycle-scoped listeners share the utility package's lazy controller runtime. */
|
|
2
|
+
export { EventManager } from 'ranuts/utils';
|
|
64
3
|
//# sourceMappingURL=events.d.ts.map
|
package/dist/events.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
|