elements-kit 0.0.2 → 0.0.3

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 CHANGED
@@ -1,217 +1,277 @@
1
1
  # ElementsKit
2
2
 
3
- A lightweight reactive UI library built on JSX and native DOM APIs. No virtual DOM, no diffing fine-grained reactivity that updates only what changes.
3
+ **Universal reactive primitives for the web.** Signals, JSX, and custom elements that work anywhere — standalone, inside React, Vue, or any framework, or as the foundation of your own component model.
4
4
 
5
5
  ```tsx
6
- import { signal, computed } from "elements-kit/signals";
6
+ import { signal, computed, reactive } from "elements-kit/signals";
7
+ import { attributes, ATTRIBUTES as attr } from "elements-kit/attributes";
7
8
 
8
- class Counter {
9
- #count = signal(0);
9
+ @attributes
10
+ class CounterElement extends HTMLElement {
11
+ static [attr] = {
12
+ count(this: CounterElement, value: string | null) {
13
+ this.count = Number(value ?? 0);
14
+ },
15
+ };
10
16
 
11
- render() {
12
- return (
17
+ @reactive() count = 0;
18
+ doubled = computed(() => this.count * 2);
19
+
20
+ connectedCallback() {
21
+ this.appendChild(
13
22
  <section>
14
- <p>Count: <strong>{this.#count}</strong></p>
15
- <button onClick={() => this.#count(this.#count() + 1)}>+1</button>
16
- </section>
17
- ) as Element;
23
+ <p>Count: <strong>{() => this.count}</strong> — Doubled: <strong>{this.doubled}</strong></p>
24
+ <button onClick={() => this.count++}>+1</button>{" "}
25
+ <button onClick={() => this.count--}>−1</button>
26
+ </section> as Element,
27
+ );
18
28
  }
19
29
  }
20
30
 
21
- document.getElementById("app")!.appendChild(new Counter().render());
31
+ customElements.define("x-counter", CounterElement);
22
32
  ```
23
33
 
24
- ## Principles
34
+ ---
35
+
36
+ ## Why ElementsKit
37
+
38
+ Modern UI frameworks solve reactivity and rendering together — you adopt the whole system or none of it. ElementsKit separates the two:
39
+
40
+ - **Signals** are the reactive core — fine-grained, framework-agnostic, composable with any rendering model.
41
+ - **JSX** compiles to real `document.createElement` calls — no virtual DOM, no runtime overhead.
42
+ - **Custom elements** are standard browser components — ElementsKit enhances them with signals and JSX without wrapping or abstracting the platform.
25
43
 
26
- - **Direct DOM Manipulation** Works with native HTMLElements, no virtual DOM or diffing overhead
27
- - **Fine-Grained Reactivity** — Only the exact DOM nodes that depend on a changed signal update
28
- - **JSX Without a Framework** — Standard JSX syntax compiled to real DOM nodes, no plugin required
29
- - **Decorator-Driven** — `@reactive()` turns any class field into a signal transparently
30
- - **Web Component Ready** — First-class support for custom elements and `attributeChangedCallback`
31
- - **Type-Safe** — Full TypeScript support with comprehensive type inference
44
+ Use one piece, or all three. Integrate with React for complex UIs. Build web components that work anywhere HTML does.
45
+
46
+ ---
47
+
48
+ ## Installation
49
+
50
+ ```sh
51
+ npm install elements-kit
52
+ ```
53
+
54
+ Configure JSX in your `tsconfig.json`:
55
+
56
+ ```json
57
+ {
58
+ "compilerOptions": {
59
+ "jsx": "react-jsx",
60
+ "jsxImportSource": "elements-kit"
61
+ }
62
+ }
63
+ ```
32
64
 
33
65
  ---
34
66
 
35
67
  ## Signals
36
68
 
69
+ Fine-grained reactive state. Signals track their dependencies automatically — only the exact computeds and effects that depend on a changed signal are re-evaluated.
70
+
37
71
  ```ts
38
- import { signal, computed, effect, batch, untracked } from "elements-kit/signals";
72
+ import { signal, computed, effect, batch, untracked, onCleanup } from "elements-kit/signals";
39
73
 
40
- const count = signal(0); // writable signal
41
- const doubled = computed(() => count() * 2); // derived, read-only
74
+ const count = signal(0);
75
+ const doubled = computed(() => count() * 2);
42
76
 
43
- effect(() => console.log(count())); // runs whenever count changes
77
+ const stop = effect(() => {
78
+ console.log("count:", count()); // runs on every change
79
+ });
44
80
 
45
- count(count() + 1); // write by calling with a value
46
- console.log(count()); // read by calling with no arguments
81
+ count(1); // count: 1
82
+ count(2); // count: 2
83
+ stop(); // unsubscribe
47
84
 
48
- batch(() => { // defer updates until the batch ends
49
- count(10);
50
- count(20);
51
- });
85
+ batch(() => { count(10); count(20); }); // single notification
52
86
 
53
87
  const raw = untracked(() => count()); // read without subscribing
54
- ```
55
88
 
56
- Signals are the reactive primitive. Passing a signal directly as a JSX child or prop creates a live binding — no wrapper needed:
89
+ effect(() => {
90
+ const id = setInterval(() => count(count() + 1), 1000);
91
+ onCleanup(() => clearInterval(id)); // runs before re-run or on stop
92
+ });
93
+ ```
57
94
 
58
- ```tsx
59
- const name = signal("world");
95
+ ### Store
60
96
 
61
- // Both are equivalent live bindings:
62
- <p>{name}</p>
63
- <p>{() => name()}</p>
64
- ```
97
+ A **store** is a class whose fields are made reactive with `@reactive`. It holds shared state — no `render()`, no DOM — and any subscriber updates automatically.
65
98
 
66
- ---
99
+ ```ts
100
+ import { reactive, computed } from "elements-kit/signals";
67
101
 
68
- ## JSX
102
+ export class CartStore {
103
+ @reactive() items: { name: string; price: number }[] = [];
104
+ @reactive() discount = 0;
69
105
 
70
- Configure your `tsconfig.json` to use the built-in JSX runtime:
106
+ total = computed(() =>
107
+ this.items.reduce((s, i) => s + i.price, 0) * (1 - this.discount),
108
+ );
71
109
 
72
- ```json
73
- {
74
- "compilerOptions": {
75
- "jsx": "react-jsx",
76
- "jsxImportSource": "elements-kit"
110
+ add(item: { name: string; price: number }) {
111
+ this.items = [...this.items, item];
77
112
  }
78
113
  }
114
+
115
+ export const cart = new CartStore();
79
116
  ```
80
117
 
81
- ### Props
118
+ Stores are **framework-agnostic** — the same instance drives a custom element, a React component, and a plain effect in sync.
82
119
 
83
- | Syntax | Effect |
84
- | --- | --- |
85
- | `value={signal}` | Live-bound — updates DOM when signal changes |
86
- | `value={42}` | Set once at render time |
87
- | `onClick={fn}` | Camel-case event listener (`onclick`) |
88
- | `on:click={fn}` | Explicit event namespace |
89
- | `style:color={signal}` | Reactive inline style property |
90
- | `class:active={signal}` | Reactive `classList.toggle` |
91
- | `prop:foo={val}` | Force property assignment (bypasses `setAttribute`) |
120
+ ---
92
121
 
93
- ```tsx
94
- const active = signal(false);
95
- const label = signal("Submit");
96
-
97
- <button
98
- class:active={active}
99
- style:opacity={computed(() => active() ? "1" : "0.5")}
100
- onClick={() => (active(!active()))}
101
- >
102
- {label}
103
- </button>
104
- ```
122
+ ## JSX → DOM
105
123
 
106
- ### Children
124
+ JSX compiles directly to `document.createElement`. No virtual DOM, no diffing.
107
125
 
108
- Any of the following are valid children:
126
+ ```tsx
127
+ // This:
128
+ const el = <button onClick={() => count(count() + 1)}>{count}</button>;
129
+
130
+ // Is equivalent to:
131
+ const el = document.createElement("button");
132
+ el.addEventListener("click", () => count(count() + 1));
133
+ // `count` signal creates a live text node — updates in place on change
134
+ ```
109
135
 
110
- - Primitive values (`string`, `number`, …)
111
- - `Node` / `Element`
112
- - A signal or `computed` — re-renders in place when it changes
113
- - A plain function `() => value` — re-evaluated reactively
114
- - Arrays of the above
136
+ Passing a signal or `() => T` as a child or prop creates a **live binding** — the DOM updates in place, never re-rendering the surrounding tree.
115
137
 
116
138
  ```tsx
117
- const show = signal(true);
118
-
119
- <div>
120
- <strong>Static text</strong>
121
- {count} // signal — live
122
- {() => count() * 2} // thunk — live
123
- {() => show() && <span>Conditional</span>}
124
- </div>
139
+ const name = signal("Alice");
140
+
141
+ <p>Hello, {name}!</p> // live text node
142
+ <input value={name} /> // live attribute
143
+ <div class:active={computed(() => name() !== "")} /> // reactive class
144
+ <span style:color={signal("red")} /> // reactive style
125
145
  ```
126
146
 
127
- ---
147
+ ### Prop namespaces
128
148
 
129
- ## `@reactive()` Decorator
149
+ | Syntax | Effect |
150
+ |--------|--------|
151
+ | `{signal}` / `{() => fn()}` | Live-bound reactive child |
152
+ | `onClick={fn}` | Event listener (camelCase → `onclick`) |
153
+ | `on:click={fn}` | Explicit event namespace |
154
+ | `class:active={bool}` | Reactive `classList.toggle` |
155
+ | `style:color={value}` | Reactive inline style property |
156
+ | `prop:foo={val}` | Force property assignment (skips `setAttribute`) |
130
157
 
131
- Makes any class field behave like a signal — reads subscribe, writes trigger updates.
158
+ ---
132
159
 
133
- ```ts
134
- import { computed, reactive } from "elements-kit/signals";
160
+ ## Class Components
135
161
 
136
- class Todo {
137
- text: string;
138
- @reactive() done: boolean;
139
- }
162
+ Any class with a `render()` method returning an `Element` is a component. Components own their state and produce elements.
140
163
 
141
- class TodoApp {
142
- @reactive()
143
- todos: Todo[] = [];
164
+ ```tsx
165
+ import { reactive, computed } from "elements-kit/signals";
144
166
 
145
- @reactive()
146
- showDone = true;
167
+ class Counter {
168
+ @reactive() count = 0;
169
+ doubled = computed(() => this.count * 2);
147
170
 
148
- // Bind to an existing computed
149
- @reactive((self) => computed(() => self.todos.filter(t => !t.done)))
150
- readonly pending: Todo[] = [];
171
+ render() {
172
+ return (
173
+ <section>
174
+ <p>{() => this.count} × 2 = {this.doubled}</p>
175
+ <button onClick={() => this.count++}>+1</button>
176
+ </section>
177
+ ) as Element;
178
+ }
151
179
  }
152
- ```
153
180
 
154
- `@reactive()` without arguments auto-wraps the field's initial value in a `signal`. Pass a factory `(self) => signal | computed` to bind the field to an existing reactive value.
181
+ document.getElementById("app")!.appendChild(new Counter().render());
182
+ ```
155
183
 
156
184
  ---
157
185
 
158
- ## `@attributes` Decorator
186
+ ## Custom Elements
159
187
 
160
- Automatically wires `observedAttributes` and `attributeChangedCallback` for custom elements from a static `[ATTRIBUTES]` map.
188
+ ElementsKit enhances native `HTMLElement` subclasses start with the platform, add only what you need.
161
189
 
162
190
  ```ts
191
+ import { reactive, computed } from "elements-kit/signals";
163
192
  import { attributes, ATTRIBUTES as attr } from "elements-kit/attributes";
164
- import { signal, reactive } from "elements-kit/signals";
165
193
 
166
194
  @attributes
167
- class Counter extends HTMLElement {
195
+ class CounterElement extends HTMLElement {
168
196
  static [attr] = {
169
- count(this: Counter, value: string | null) {
170
- this.count = Number(value); // calls the @reactive setter
197
+ count(this: CounterElement, value: string | null) {
198
+ this.count = Number(value ?? 0);
171
199
  },
172
200
  };
173
201
 
174
- #count = signal(0);
175
-
176
- @reactive((s) => s.#count)
177
- count: number = 0;
202
+ @reactive() count = 0;
203
+ doubled = computed(() => this.count * 2);
178
204
 
179
205
  connectedCallback() {
180
- const Host = this;
181
- <Host>
182
- <p>Count: <strong>{this.#count}</strong></p>
183
- <button onClick={() => this.count++}>+1</button>
184
- </Host>;
206
+ this.appendChild(
207
+ <section>
208
+ <p>{() => this.count} × 2 = {this.doubled}</p>
209
+ <button onClick={() => this.count++}>+1</button>
210
+ </section> as Element,
211
+ );
185
212
  }
186
213
  }
187
214
 
188
- customElements.define("x-counter", Counter);
215
+ customElements.define("x-counter", CounterElement);
216
+ ```
217
+
218
+ `<x-counter count="5" />` — attribute bound, reactive, works in any HTML context.
219
+
220
+ ---
221
+
222
+ ## React Integration
223
+
224
+ Connect signals and stores to React components via `useSyncExternalStore`:
225
+
226
+ ```tsx
227
+ import { useSignal, useScope } from "elements-kit/signals/react";
228
+ import { cart } from "./cart-store";
229
+
230
+ function CartSummary() {
231
+ // Reads a @reactive field — re-renders only when cart.items changes
232
+ const items = useSignal(() => cart.items);
233
+ const total = useSignal(cart.total); // Computed<T> works directly
234
+
235
+ // Effects tied to this component's lifetime
236
+ useScope(() => {
237
+ effect(() => console.log("cart updated:", items));
238
+ });
239
+
240
+ return <p>{items.length} items — ${total.toFixed(2)}</p>;
241
+ }
189
242
  ```
190
243
 
191
- Use `<x-counter count={signal(9)} />` to pass a reactive attribute from JSX.
244
+ The same `cart` store drives custom elements, React trees, and plain scripts — all in sync.
245
+
246
+ ---
247
+
248
+ ## Signal Helpers
249
+
250
+ Pre-built signal factories for common browser APIs:
251
+
252
+ ```ts
253
+ import { createMediaSignal } from "elements-kit/signals/media";
254
+
255
+ const isDark = createMediaSignal("(prefers-color-scheme: dark)");
256
+ const isMobile = createMediaSignal("(max-width: 640px)");
257
+
258
+ effect(() => document.documentElement.classList.toggle("dark", isDark()));
259
+ ```
192
260
 
193
261
  ---
194
262
 
195
263
  ## `For` — Keyed List Rendering
196
264
 
197
- Efficiently reconciles a reactive array into the DOM. Each item is rendered once per unique key — no full re-renders on reorder, add, or remove.
265
+ Reconciles a reactive array into the DOM. Each item renders once per key — no full re-renders on reorder, add, or remove.
198
266
 
199
267
  ```tsx
200
268
  import { For } from "elements-kit";
201
269
 
202
- const todos = computed(() => state.todos.filter(t => !t.done));
203
-
204
270
  <ul>
205
271
  <For each={todos} by={(todo) => todo.id}>
206
272
  {(todo) => (
207
- <li
208
- style:text-decoration={computed(() => todo.done ? "line-through" : "none")}
209
- >
210
- <input
211
- type="checkbox"
212
- checked={computed(() => todo.done)}
213
- on:change={() => (todo.done = !todo.done)}
214
- />{" "}
273
+ <li>
274
+ <input type="checkbox" checked={computed(() => todo.done)} on:change={() => (todo.done = !todo.done)} />
215
275
  {todo.text}
216
276
  </li>
217
277
  )}
@@ -219,40 +279,54 @@ const todos = computed(() => state.todos.filter(t => !t.done));
219
279
  </ul>
220
280
  ```
221
281
 
222
- | Prop | Type | Description |
223
- | --- | --- | --- |
224
- | `each` | `T[] \| (() => T[])` | Reactive array to render |
225
- | `by` | `(item: T, index: number) => string \| number` | Key function — defaults to index |
226
- | `children` | `(item: T, index: number) => Element` | Render function, called once per new key |
227
-
228
282
  ---
229
283
 
230
- ## Class Components
284
+ ## `@reactive()` Decorator
231
285
 
232
- Any class with a `render()` method returning an `Element` or `DocumentFragment` works as a component. JSX instantiates it automatically:
286
+ Makes any class field reactive reads subscribe, writes trigger updates.
233
287
 
234
- ```tsx
235
- class App {
236
- render() {
237
- return (
238
- <div style="max-width: 480px; margin: 40px auto">
239
- <h1>My App</h1>
240
- <x-counter count={signal(0)} />
241
- <TodoApp />
242
- </div>
243
- ) as Element;
244
- }
288
+ ```ts
289
+ import { reactive, computed } from "elements-kit/signals";
290
+
291
+ class TodoApp {
292
+ @reactive() todos: Todo[] = [];
293
+ @reactive() showDone = true;
294
+
295
+ visible = computed(() =>
296
+ this.showDone ? this.todos : this.todos.filter((t) => !t.done),
297
+ );
245
298
  }
299
+ ```
300
+
301
+ ---
302
+
303
+ ## `@attributes` Decorator
304
+
305
+ Wires `observedAttributes` and `attributeChangedCallback` from a static map:
306
+
307
+ ```ts
308
+ import { attributes, ATTRIBUTES as attr } from "elements-kit/attributes";
309
+
310
+ @attributes
311
+ class MyElement extends HTMLElement {
312
+ static [attr] = {
313
+ value(this: MyElement, v: string | null) {
314
+ this.value = v ?? "";
315
+ },
316
+ };
246
317
 
247
- document.getElementById("app")!.appendChild(new App().render());
318
+ @reactive() value = "";
319
+ }
248
320
  ```
249
321
 
250
322
  ---
251
323
 
252
- ## TO-DO
324
+ ## Roadmap
253
325
 
254
- - [ ] Complete type safety
255
- - [ ] Async signal
256
- - [ ] URLPattern signal
257
- - [ ] Context
258
- - [ ] `Key` component (conditional key-gated subtrees)
326
+ - [ ] More signal helpers (`localStorage`, `IntersectionObserver`, `ResizeObserver`, …)
327
+ - [ ] Context — share state across a subtree without prop drilling
328
+ - [ ] Async signal — `signal.from(promise)`, `signal.from(observable)`
329
+ - [ ] UI library — pre-built reactive components built on ElementsKit primitives
330
+ - [ ] More framework integrations (Vue, Solid, Angular, …)
331
+ - [ ] Tutorial — building a full app from scratch
332
+ - [ ] Complete TypeScript strict-mode coverage
@@ -77,6 +77,5 @@ function attributes(target, context) {
77
77
  };
78
78
  return target;
79
79
  }
80
-
81
80
  //#endregion
82
- export { ATTRIBUTES, attributes, dispatchAttrChange, observedAttributes };
81
+ export { ATTRIBUTES, attributes, dispatchAttrChange, observedAttributes };
@@ -1,5 +1,4 @@
1
- import "../polyfill-Bvo2e52W.mjs";
2
- import { ReactiveElementOf } from "./index.mjs";
1
+ import { c as ReactiveElementOf } from "../index-DJejH8Ff.mjs";
3
2
 
4
3
  //#region src/builder/dom.d.ts
5
4
  interface Lifecycle {
@@ -1,6 +1,4 @@
1
- import "../polyfill-DAalJpCO.mjs";
2
1
  import { builder } from "./index.mjs";
3
-
4
2
  //#region src/builder/dom.ts
5
3
  const a = () => builder(document.createElement("a"));
6
4
  const abbr = () => builder(document.createElement("abbr"));
@@ -203,6 +201,5 @@ const mtr = () => builder(document.createElement("mtr"));
203
201
  const munder = () => builder(document.createElement("munder"));
204
202
  const munderover = () => builder(document.createElement("munderover"));
205
203
  const semantics = () => builder(document.createElement("semantics"));
206
-
207
204
  //#endregion
208
- export { a, abbr, address, animate, animateMotion, animateTransform, annotation, annotationXml, area, article, aside, audio, b, base, bdi, bdo, blockquote, body, br, button, canvas, caption, circle, cite, clipPath, code, col, colgroup, data, datalist, dd, defs, del, desc, details, dfn, dialog, div, dl, dt, ellipse, em, embed, feBlend, feColorMatrix, feComponentTransfer, feComposite, feConvolveMatrix, feDiffuseLighting, feDisplacementMap, feDistantLight, feDropShadow, feFlood, feFuncA, feFuncB, feFuncG, feFuncR, feGaussianBlur, feImage, feMerge, feMergeNode, feMorphology, feOffset, fePointLight, feSpecularLighting, feSpotLight, feTile, feTurbulence, fieldset, figcaption, figure, filter, footer, foreignObject, form, g, h1, h2, h3, h4, h5, h6, head, header, hgroup, hr, html, i, iframe, image, img, input, ins, kbd, label, legend, li, line, linearGradient, link, maction, main, map, mark, marker, mask, math, mathVar, menu, merror, meta, metadata, meter, mfrac, mi, mmultiscripts, mn, mo, mover, mpadded, mpath, mphantom, mprescripts, mroot, mrow, ms, mspace, msqrt, mstyle, msub, msubsup, msup, mtable, mtd, mtext, mtr, munder, munderover, nav, noscript, object, ol, optgroup, option, output, p, path, pattern, picture, polygon, polyline, pre, progress, q, radialGradient, rect, rp, rt, ruby, s, samp, script, search, section, select, semantics, set, slot, small, source, span, stop, strong, style, sub, summary, sup, svg, svgSwitch, symbol, table, tbody, td, template, text, textPath, textarea, tfoot, th, thead, time, title, tr, track, tspan, u, ul, use, video, view, wbr };
205
+ export { a, abbr, address, animate, animateMotion, animateTransform, annotation, annotationXml, area, article, aside, audio, b, base, bdi, bdo, blockquote, body, br, button, canvas, caption, circle, cite, clipPath, code, col, colgroup, data, datalist, dd, defs, del, desc, details, dfn, dialog, div, dl, dt, ellipse, em, embed, feBlend, feColorMatrix, feComponentTransfer, feComposite, feConvolveMatrix, feDiffuseLighting, feDisplacementMap, feDistantLight, feDropShadow, feFlood, feFuncA, feFuncB, feFuncG, feFuncR, feGaussianBlur, feImage, feMerge, feMergeNode, feMorphology, feOffset, fePointLight, feSpecularLighting, feSpotLight, feTile, feTurbulence, fieldset, figcaption, figure, filter, footer, foreignObject, form, g, h1, h2, h3, h4, h5, h6, head, header, hgroup, hr, html, i, iframe, image, img, input, ins, kbd, label, legend, li, line, linearGradient, link, maction, main, map, mark, marker, mask, math, mathVar, menu, merror, meta, metadata, meter, mfrac, mi, mmultiscripts, mn, mo, mover, mpadded, mpath, mphantom, mprescripts, mroot, mrow, ms, mspace, msqrt, mstyle, msub, msubsup, msup, mtable, mtd, mtext, mtr, munder, munderover, nav, noscript, object, ol, optgroup, option, output, p, path, pattern, picture, polygon, polyline, pre, progress, q, radialGradient, rect, rp, rt, ruby, s, samp, script, search, section, select, semantics, set, slot, small, source, span, stop, strong, style, sub, summary, sup, svg, svgSwitch, symbol, table, tbody, td, template, text, textPath, textarea, tfoot, th, thead, time, title, tr, track, tspan, u, ul, use, video, view, wbr };
@@ -1,89 +1,2 @@
1
- import { t as PrimitiveNodeType } from "../polyfill-Bvo2e52W.mjs";
2
-
3
- //#region src/builder/index.d.ts
4
- declare const DISPOSABLES: unique symbol;
5
- declare const DISPOSE: symbol;
6
- declare const VALUE: unique symbol;
7
- declare const EFFECT: unique symbol;
8
- declare class ElementBuilder<T extends Element = Element> {
9
- /** Dispose the reactive element and run all cleanup functions */
10
- [DISPOSE]: () => void;
11
- /** The underlying DOM element */
12
- private [VALUE];
13
- /** A set of cleanup functions to run when disposing the element */
14
- private [DISPOSABLES];
15
- [EFFECT](fn: () => void): this;
16
- private constructor();
17
- static create<T extends Element>(el: T): ReactiveElement<T>;
18
- children(...children: ValueOrReactive<ElementBuilder | Node | string | number>[]): T;
19
- ref(): T;
20
- ref(apply: (ref: T) => void | (() => void)): this;
21
- on<K extends keyof HTMLElementEventMap>(eventType: K, listener: (ev: HTMLElementEventMap[K]) => void, options?: boolean | AddEventListenerOptions): this;
22
- }
23
- declare function toNode(c: ElementBuilder | PrimitiveNodeType): Node;
24
- declare function builder<T extends Element>(el: T): ReactiveElementOf<T>;
25
- /**
26
- * Callable overloads matching the Proxy apply trap → ref() behavior.
27
- * Calling a reactive element as a function delegates to ref():
28
- * el() → returns the raw DOM element
29
- * el((ref) => {}) → applies a side-effect, returns the builder for chaining
30
- */
31
- interface RefCallable<T extends Element> {
32
- (): T;
33
- (apply: (ref: T) => void | (() => void)): this;
34
- }
35
- type ReactiveElement<T extends Element> = ElementBuilder<T> & RefCallable<T>;
36
- type ValueOrReactive<T> = (() => T) | T;
37
- type ValueOrReactiveArray<T extends any[]> = { [K in keyof T]: ValueOrReactive<T[K]> | T[K] };
38
- /**
39
- * Filter keys that are writable (exclude readonly and getter-only).
40
- */
41
- type WritableKeys<T> = { [K in keyof T]: (<U>() => U extends { [Q in K]: T[K] } ? 1 : 2) extends (<U>() => U extends { readonly [Q in K]: T[K] } ? 1 : 2) ? never : K }[keyof T];
42
- /**
43
- * Extracts the object chaining part of ReactiveBuilder.
44
- * Used for properties like `style` where you can do both:
45
- * .style("padding: 20px;") // setter
46
- * .style.padding("20px") // sub-property chaining
47
- */
48
- type Chain<R, T> = T extends object ? { [K in WritableKeys<T>]: Builder<R, T[K]> } : {};
49
- type Builder<R, T = R> = T extends ((...args: infer U) => unknown) ? (...value: ValueOrReactiveArray<U>) => R : (value?: ValueOrReactive<T>) => R;
50
- /**
51
- * Types eligible for sub-property chaining (e.g., `.style.padding("20px")`).
52
- */
53
- type ChainableType = CSSStyleDeclaration | DOMTokenList | DOMStringMap | StylePropertyMap;
54
- /**
55
- * Filters keys to only writable data properties.
56
- * Excludes methods (function-valued properties) and event handlers (`on*`).
57
- */
58
- type DataPropertyKeys<T> = { [K in keyof T]: K extends `on${string}` ? never : T[K] extends ((...args: any[]) => any) ? never : K }[keyof T];
59
- /**
60
- * Reactive setter for a single property.
61
- * Chainable types (CSSStyleDeclaration, DOMTokenList, etc.) get both
62
- * sub-property chaining and direct setter support.
63
- */
64
- type ReactiveSetter<R, T> = T extends ChainableType ? Chain<R, T> & ((value: ValueOrReactive<T>) => R) : (value: ValueOrReactive<T>) => R;
65
- /**
66
- * A fully-typed reactive element builder for any Element type.
67
- * Automatically maps all writable data properties into reactive setters.
68
- *
69
- * Use this for custom elements instead of needing generated builder interfaces.
70
- *
71
- * @example
72
- * ```ts
73
- * import { reactive, type ReactiveElementOf } from "elements-kit";
74
- *
75
- * class MyElement extends HTMLElement {
76
- * greeting = "hello";
77
- * }
78
- * customElements.define("my-element", MyElement);
79
- *
80
- * const myEl = () =>
81
- * reactive(document.createElement("my-element") as MyElement);
82
- *
83
- * // Full type support for both own and inherited properties:
84
- * myEl().greeting("world").style.padding("20px").id("main");
85
- * ```
86
- */
87
- type ReactiveElementOf<T extends Element> = ElementBuilder<T> & { [K in WritableKeys<T> & DataPropertyKeys<T> & keyof T]: ReactiveSetter<ReactiveElementOf<T>, T[K]> };
88
- //#endregion
1
+ import { a as EFFECT, c as ReactiveElementOf, d as builder, f as toNode, i as DISPOSE, l as VALUE, n as Chain, o as ElementBuilder, r as DISPOSABLES, s as ReactiveElement, t as Builder, u as ValueOrReactive } from "../index-DJejH8Ff.mjs";
89
2
  export { Builder, Chain, DISPOSABLES, DISPOSE, EFFECT, ElementBuilder, ReactiveElement, ReactiveElementOf, VALUE, ValueOrReactive, builder, toNode };
@@ -1,7 +1,6 @@
1
- import { effect, isReactive } from "../signals.mjs";
1
+ import { a as effect, t as isReactive } from "../signals-Cr7xgAJH.mjs";
2
2
  import { Slot } from "../slot.mjs";
3
- import { t as resolveNode } from "../polyfill-DAalJpCO.mjs";
4
-
3
+ import { t as resolveNode } from "../lib-B2drrxlV.mjs";
5
4
  //#region src/builder/index.ts
6
5
  const DISPOSABLES = Symbol("disposables");
7
6
  const DISPOSE = Symbol.dispose;
@@ -110,6 +109,5 @@ function isObject(v) {
110
109
  function builder(el) {
111
110
  return ElementBuilder.create(el);
112
111
  }
113
-
114
112
  //#endregion
115
- export { DISPOSABLES, DISPOSE, EFFECT, ElementBuilder, VALUE, builder, toNode };
113
+ export { DISPOSABLES, DISPOSE, EFFECT, ElementBuilder, VALUE, builder, toNode };