what-core 0.4.1 → 0.5.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/dist/components.js +213 -319
- package/dist/dom.js +730 -857
- package/dist/h.js +140 -191
- package/dist/head.js +42 -59
- package/dist/helpers.js +124 -187
- package/dist/hooks.js +186 -279
- package/dist/reactive.js +244 -317
- package/dist/store.js +73 -118
- package/dist/what.js +5 -3
- package/index.d.ts +391 -152
- package/package.json +4 -3
- package/render.d.ts +11 -0
- package/src/a11y.js +52 -6
- package/src/dom.js +137 -22
- package/src/form.js +85 -54
- package/src/helpers.js +1 -12
- package/src/hooks.js +11 -0
- package/src/index.js +1 -1
- package/src/render.js +114 -51
- package/src/store.js +6 -1
package/src/render.js
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
// No VDOM diffing — direct DOM manipulation with surgical signal-driven updates.
|
|
4
4
|
|
|
5
5
|
import { effect, untrack, createRoot, signal } from './reactive.js';
|
|
6
|
+
import { createDOM, disposeTree } from './dom.js';
|
|
7
|
+
|
|
8
|
+
export { effect, untrack };
|
|
6
9
|
|
|
7
10
|
// --- template(html) ---
|
|
8
11
|
// Pre-parse HTML string into a <template> element. Returns a factory function
|
|
@@ -22,72 +25,124 @@ export function template(html) {
|
|
|
22
25
|
// - array → insert each element
|
|
23
26
|
|
|
24
27
|
export function insert(parent, child, marker) {
|
|
25
|
-
if (
|
|
28
|
+
if (typeof child === 'function') {
|
|
29
|
+
let current = null;
|
|
30
|
+
effect(() => {
|
|
31
|
+
current = reconcileInsert(parent, child(), current, marker || null);
|
|
32
|
+
});
|
|
33
|
+
return current;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return reconcileInsert(parent, child, null, marker || null);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isDomNode(value) {
|
|
40
|
+
if (!value || typeof value !== 'object') return false;
|
|
41
|
+
if (typeof Node !== 'undefined' && value instanceof Node) return true;
|
|
42
|
+
return typeof value.nodeType === 'number' && typeof value.nodeName === 'string';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isVNode(value) {
|
|
46
|
+
return !!value && typeof value === 'object' && (value._vnode === true || 'tag' in value);
|
|
47
|
+
}
|
|
26
48
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
parent
|
|
30
|
-
|
|
49
|
+
function isSvgParent(parent) {
|
|
50
|
+
return typeof SVGElement !== 'undefined'
|
|
51
|
+
&& parent instanceof SVGElement
|
|
52
|
+
&& parent.tagName.toLowerCase() !== 'foreignobject';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function asNodeArray(value) {
|
|
56
|
+
if (value == null) return [];
|
|
57
|
+
return Array.isArray(value) ? value : [value];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function valuesToNodes(value, parent, out) {
|
|
61
|
+
if (value == null || typeof value === 'boolean') return out;
|
|
62
|
+
|
|
63
|
+
if (Array.isArray(value)) {
|
|
64
|
+
for (let i = 0; i < value.length; i++) {
|
|
65
|
+
valuesToNodes(value[i], parent, out);
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
31
68
|
}
|
|
32
69
|
|
|
33
|
-
if (typeof
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
70
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
71
|
+
out.push(document.createTextNode(String(value)));
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
37
74
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
75
|
+
if (isDomNode(value)) {
|
|
76
|
+
out.push(value);
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (isVNode(value)) {
|
|
81
|
+
out.push(createDOM(value, parent, isSvgParent(parent)));
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
out.push(document.createTextNode(String(value)));
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function sameNodeArray(a, b) {
|
|
90
|
+
if (a.length !== b.length) return false;
|
|
91
|
+
for (let i = 0; i < a.length; i++) {
|
|
92
|
+
if (a[i] !== b[i]) return false;
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function reconcileInsert(parent, value, current, marker) {
|
|
98
|
+
const targetMarker = marker || null;
|
|
99
|
+
|
|
100
|
+
if (value == null || typeof value === 'boolean') {
|
|
101
|
+
const oldNodes = asNodeArray(current);
|
|
102
|
+
for (let i = 0; i < oldNodes.length; i++) {
|
|
103
|
+
const oldNode = oldNodes[i];
|
|
104
|
+
if (oldNode.parentNode === parent) {
|
|
105
|
+
disposeTree(oldNode);
|
|
106
|
+
parent.removeChild(oldNode);
|
|
59
107
|
}
|
|
60
|
-
}
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
61
111
|
|
|
62
|
-
|
|
112
|
+
if ((typeof value === 'string' || typeof value === 'number')
|
|
113
|
+
&& current && !Array.isArray(current) && current.nodeType === 3) {
|
|
114
|
+
const text = String(value);
|
|
115
|
+
if (current.textContent !== text) current.textContent = text;
|
|
116
|
+
return current;
|
|
63
117
|
}
|
|
64
118
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
119
|
+
const newNodes = valuesToNodes(value, parent, []);
|
|
120
|
+
const oldNodes = asNodeArray(current);
|
|
121
|
+
|
|
122
|
+
if (sameNodeArray(oldNodes, newNodes)) {
|
|
123
|
+
return current;
|
|
68
124
|
}
|
|
69
125
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
126
|
+
const keep = new Set(newNodes);
|
|
127
|
+
for (let i = 0; i < oldNodes.length; i++) {
|
|
128
|
+
const oldNode = oldNodes[i];
|
|
129
|
+
if (!keep.has(oldNode) && oldNode.parentNode === parent) {
|
|
130
|
+
disposeTree(oldNode);
|
|
131
|
+
parent.removeChild(oldNode);
|
|
75
132
|
}
|
|
76
|
-
return nodes;
|
|
77
133
|
}
|
|
78
|
-
}
|
|
79
134
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
frag.appendChild(arr[i]);
|
|
86
|
-
} else if (arr[i] != null && typeof arr[i] !== 'boolean') {
|
|
87
|
-
frag.appendChild(document.createTextNode(String(arr[i])));
|
|
135
|
+
let ref = targetMarker;
|
|
136
|
+
for (let i = newNodes.length - 1; i >= 0; i--) {
|
|
137
|
+
const node = newNodes[i];
|
|
138
|
+
if (node.parentNode !== parent || node.nextSibling !== ref) {
|
|
139
|
+
parent.insertBefore(node, ref);
|
|
88
140
|
}
|
|
141
|
+
ref = node;
|
|
89
142
|
}
|
|
90
|
-
|
|
143
|
+
|
|
144
|
+
if (newNodes.length === 0) return null;
|
|
145
|
+
return newNodes.length === 1 ? newNodes[0] : newNodes;
|
|
91
146
|
}
|
|
92
147
|
|
|
93
148
|
// --- mapArray(source, mapFn, options?) ---
|
|
@@ -650,6 +705,14 @@ export function spread(el, props) {
|
|
|
650
705
|
function setPropDirect(el, key, value) {
|
|
651
706
|
if (key === 'class' || key === 'className') {
|
|
652
707
|
el.className = value || '';
|
|
708
|
+
} else if (key === 'dangerouslySetInnerHTML') {
|
|
709
|
+
el.innerHTML = value?.__html ?? '';
|
|
710
|
+
} else if (key === 'innerHTML') {
|
|
711
|
+
if (value && typeof value === 'object' && '__html' in value) {
|
|
712
|
+
el.innerHTML = value.__html ?? '';
|
|
713
|
+
} else {
|
|
714
|
+
el.innerHTML = value ?? '';
|
|
715
|
+
}
|
|
653
716
|
} else if (key === 'style') {
|
|
654
717
|
if (typeof value === 'string') {
|
|
655
718
|
el.style.cssText = value;
|
package/src/store.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Lightweight global state management. Signal-based, type-safe, ergonomic.
|
|
3
3
|
// Like Zustand meets signals — define a store, use it anywhere.
|
|
4
4
|
|
|
5
|
-
import { signal, computed, batch } from './reactive.js';
|
|
5
|
+
import { signal, computed, batch, __DEV__ } from './reactive.js';
|
|
6
6
|
|
|
7
7
|
// --- storeComputed ---
|
|
8
8
|
// Marker wrapper to explicitly tag a function as a computed in createStore.
|
|
@@ -57,6 +57,11 @@ export function createStore(definition) {
|
|
|
57
57
|
// Use explicit _storeComputed marker instead of function.length heuristic
|
|
58
58
|
for (const [key, value] of Object.entries(definition)) {
|
|
59
59
|
if (typeof value === 'function' && value._storeComputed) {
|
|
60
|
+
if (__DEV__ && value.length === 0) {
|
|
61
|
+
console.warn(
|
|
62
|
+
`[what] derived() for "${key}" should accept the state parameter, e.g. derived(state => ...).`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
60
65
|
// Computed: explicitly marked with storeComputed()
|
|
61
66
|
computeds[key] = value;
|
|
62
67
|
} else if (typeof value === 'function') {
|