@zoijs/core 1.0.0 → 1.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/CHANGELOG.md +16 -2
- package/LICENSE +1 -1
- package/README.md +1 -1
- package/package.json +5 -5
- package/src/core/renderer.js +36 -0
- package/src/index.d.ts +17 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,20 @@ All notable changes to Zoijs are documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/), and Zoijs follows
|
|
5
5
|
[Semantic Versioning](https://semver.org/) (see `VERSIONING.md`).
|
|
6
6
|
|
|
7
|
-
## [1.
|
|
7
|
+
## [1.1.0] — 2026-06-25
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Element refs.** A new `ref` binding gives you the rendered DOM element:
|
|
11
|
+
`html\`<input ref=${(el) => el.focus()} />\``. The callback runs once, just after
|
|
12
|
+
the element is inserted (so `focus`/`scroll`/`measure`/`canvas` work), is not
|
|
13
|
+
reactive, and may return a cleanup function that runs on unmount or list-item
|
|
14
|
+
removal. Works inside keyed `each` lists. Non-function values are ignored with a
|
|
15
|
+
dev-mode warning and never become a DOM attribute. No new export — `ref` is a
|
|
16
|
+
binding semantic, so the seven-function public surface is unchanged (additive
|
|
17
|
+
MINOR per `VERSIONING.md`). See [Element refs](docs/concepts/refs.md) and
|
|
18
|
+
[RFC 0001](docs/rfcs/0001-element-refs.md).
|
|
19
|
+
|
|
20
|
+
## [1.0.0] — 2026-06-24
|
|
8
21
|
|
|
9
22
|
First stable release. The public API is frozen at seven functions.
|
|
10
23
|
|
|
@@ -33,4 +46,5 @@ First stable release. The public API is frozen at seven functions.
|
|
|
33
46
|
(Playwright), and TypeScript type tests.
|
|
34
47
|
- No build step required at any point.
|
|
35
48
|
|
|
36
|
-
[1.
|
|
49
|
+
[1.1.0]: https://github.com/Zoijs/zoijs/releases/tag/core-v1.1.0
|
|
50
|
+
[1.0.0]: https://github.com/Zoijs/zoijs/releases/tag/core-v1.0.0
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A lightweight frontend framework you don't have to learn before you use it — **plain HTML, CSS, and JavaScript**, no JSX, no build step, no Virtual DOM.
|
|
4
4
|
|
|
5
|
-
**[
|
|
5
|
+
**[Documentation](https://zoijs.dev)** · **[GitHub](https://github.com/Zoijs)** · **[npm](https://www.npmjs.com/package/@zoijs/core)**
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install @zoijs/core
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zoijs/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Zoijs — a beginner-friendly, no-build, no-Virtual-DOM frontend framework. Plain HTML, CSS, and JavaScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=18"
|
|
23
23
|
},
|
|
24
|
-
"author": "Zoijs contributors (https://zoijs.
|
|
24
|
+
"author": "Zoijs contributors (https://zoijs.dev)",
|
|
25
25
|
"license": "MIT",
|
|
26
|
-
"homepage": "https://zoijs.
|
|
26
|
+
"homepage": "https://zoijs.dev",
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|
|
29
29
|
"url": "git+https://github.com/Zoijs/zoijs.git",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"spa"
|
|
50
50
|
],
|
|
51
51
|
"scripts": {
|
|
52
|
-
"test": "node --test --import ./tests/setup-dom.js
|
|
53
|
-
"test:unit": "node --test
|
|
52
|
+
"test": "node --test --import ./tests/setup-dom.js",
|
|
53
|
+
"test:unit": "node --test",
|
|
54
54
|
"test:types": "tsc --noEmit -p tsconfig.json",
|
|
55
55
|
"test:browser": "playwright test",
|
|
56
56
|
"dev": "npx serve -l 3000 ."
|
package/src/core/renderer.js
CHANGED
|
@@ -82,6 +82,13 @@ function bindChild(anchor, value) {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
function bindAttribute(el, attr, values) {
|
|
85
|
+
if (attr.name === "ref") {
|
|
86
|
+
// A callback ref. Only a single ${fn} is meaningful; anything else (a string,
|
|
87
|
+
// a number, a multi-part value) is rejected by bindRef without touching the DOM.
|
|
88
|
+
bindRef(el, attr.whole ? values[attr.holes[0]] : undefined);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
85
92
|
if (attr.event) {
|
|
86
93
|
const handler = values[attr.holes[0]];
|
|
87
94
|
// Only real functions are accepted as handlers — a string/object is ignored,
|
|
@@ -119,6 +126,35 @@ function bindAttribute(el, attr, values) {
|
|
|
119
126
|
else applyAttribute(el, attr.name, compute());
|
|
120
127
|
}
|
|
121
128
|
|
|
129
|
+
// A callback ref: hand the real element to user code AFTER the current render is
|
|
130
|
+
// inserted, so focus/scroll/measure see a CONNECTED node. We defer one microtask
|
|
131
|
+
// (render binds while the DOM is still a detached fragment; insertion happens
|
|
132
|
+
// right after render returns, synchronously, so a microtask runs once it's live).
|
|
133
|
+
// Not reactive — the function is read once. An optional returned function is an
|
|
134
|
+
// owner-scoped cleanup, disposed on unmount or list-item removal, exactly like a
|
|
135
|
+
// listener. A non-function value is ignored (with a dev warning) and never sets a
|
|
136
|
+
// "ref" attribute — so an inert string can't be wired up.
|
|
137
|
+
function bindRef(el, fn) {
|
|
138
|
+
if (typeof fn !== "function") {
|
|
139
|
+
if (isDev()) console.warn(`Zoijs: "ref" expects a function (el) => …; ignoring ${typeof fn} value`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
let active = true;
|
|
143
|
+
let cleanup = null;
|
|
144
|
+
onCleanup(() => {
|
|
145
|
+
active = false;
|
|
146
|
+
if (cleanup) { cleanup(); cleanup = null; }
|
|
147
|
+
});
|
|
148
|
+
queueMicrotask(() => {
|
|
149
|
+
if (!active) return; // removed before the microtask fired
|
|
150
|
+
const c = fn(el);
|
|
151
|
+
if (typeof c === "function") {
|
|
152
|
+
if (active) cleanup = c;
|
|
153
|
+
else c(); // disposed during fn(): tear down immediately
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
122
158
|
// ---- text / content bindings -------------------------------------------------
|
|
123
159
|
|
|
124
160
|
function bindReactiveContent(anchor, getValue) {
|
package/src/index.d.ts
CHANGED
|
@@ -40,12 +40,29 @@ export interface EachResult {
|
|
|
40
40
|
/** A component is a function that returns an `html` template. */
|
|
41
41
|
export type Component = () => TemplateResult;
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* A callback ref. Place it on an element as `ref=${fn}`; it receives the real DOM
|
|
45
|
+
* element once the surrounding render has been inserted (deferred one microtask,
|
|
46
|
+
* so `focus()` / `scrollIntoView()` / `getBoundingClientRect()` work). It may
|
|
47
|
+
* return a cleanup function, which runs on unmount or list-item removal. It runs
|
|
48
|
+
* once and is not reactive.
|
|
49
|
+
*
|
|
50
|
+
* ```ts
|
|
51
|
+
* html`<input ref=${(el: HTMLInputElement) => el.focus()} />`
|
|
52
|
+
* html`<div ref=${(el) => { const c = chart(el); return () => c.destroy(); }}></div>`
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export type Ref<E extends Element = Element> = (element: E) => void | (() => void);
|
|
56
|
+
|
|
43
57
|
/**
|
|
44
58
|
* Tagged-template function — write your markup as HTML.
|
|
45
59
|
*
|
|
46
60
|
* ```js
|
|
47
61
|
* html`<button onclick=${() => count.set(count.get() + 1)}>${() => count.get()}</button>`
|
|
48
62
|
* ```
|
|
63
|
+
*
|
|
64
|
+
* To reach the rendered DOM element, add a callback `ref` (see {@link Ref}):
|
|
65
|
+
* `html\`<input ref=${(el) => el.focus()} />\``.
|
|
49
66
|
*/
|
|
50
67
|
export function html(strings: TemplateStringsArray, ...values: unknown[]): TemplateResult;
|
|
51
68
|
|