what-core 0.4.2 → 0.5.1
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 -915
- 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 +392 -152
- package/package.json +4 -3
- package/render.d.ts +12 -0
- package/src/a11y.js +52 -6
- package/src/dom.js +91 -8
- package/src/form.js +85 -54
- package/src/helpers.js +1 -12
- package/src/hooks.js +11 -0
- package/src/index.js +2 -2
- package/src/render.js +117 -54
- package/src/store.js +6 -1
package/dist/h.js
CHANGED
|
@@ -1,203 +1,152 @@
|
|
|
1
|
-
// What Framework - Hyperscript / VDOM
|
|
2
|
-
// Minimal virtual DOM nodes. No classes, no fibers, just plain objects.
|
|
3
|
-
|
|
4
|
-
// h(tag, props, ...children) -> VNode
|
|
5
|
-
// h(Component, props, ...children) -> VNode
|
|
6
|
-
// VNode = { tag, props, children, key }
|
|
7
|
-
|
|
8
1
|
const EMPTY_OBJ = {};
|
|
9
2
|
const EMPTY_ARR = [];
|
|
10
|
-
|
|
11
3
|
export function h(tag, props, ...children) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return { tag, props, children: flat, key, _vnode: true };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Fragment — just returns children
|
|
4
|
+
props = props || EMPTY_OBJ;
|
|
5
|
+
const flat = flattenChildren(children);
|
|
6
|
+
const key = props.key ?? null;
|
|
7
|
+
if (props.key !== undefined) {
|
|
8
|
+
props = { ...props };
|
|
9
|
+
delete props.key;
|
|
10
|
+
}
|
|
11
|
+
return { tag, props, children: flat, key, _vnode: true };
|
|
12
|
+
}
|
|
26
13
|
export function Fragment({ children }) {
|
|
27
|
-
|
|
14
|
+
return children;
|
|
28
15
|
}
|
|
29
|
-
|
|
30
16
|
function flattenChildren(children) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
// JSX-like tagged template alternative (no build step needed)
|
|
48
|
-
// html`<div class="foo">${content}</div>`
|
|
49
|
-
// Uses a simple parser, not full HTML — good enough for most cases.
|
|
50
|
-
|
|
17
|
+
const out = [];
|
|
18
|
+
for (let i = 0; i < children.length; i++) {
|
|
19
|
+
const child = children[i];
|
|
20
|
+
if (child == null || child === false || child === true) continue;
|
|
21
|
+
if (Array.isArray(child)) {
|
|
22
|
+
out.push(...flattenChildren(child));
|
|
23
|
+
} else if (typeof child === 'object' && child._vnode) {
|
|
24
|
+
out.push(child);
|
|
25
|
+
} else if (typeof child === 'function') {
|
|
26
|
+
out.push(child);
|
|
27
|
+
} else {
|
|
28
|
+
out.push(String(child));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
51
33
|
export function html(strings, ...values) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
34
|
+
const src = strings.reduce((acc, str, i) =>
|
|
35
|
+
acc + str + (i < values.length ? `\x00${i}\x00` : ''), '');
|
|
36
|
+
return parseTemplate(src, values);
|
|
55
37
|
}
|
|
56
|
-
|
|
57
38
|
function parseTemplate(src, values) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
i = result.end;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return nodes.length === 1 ? nodes[0] : nodes;
|
|
80
|
-
}
|
|
81
|
-
|
|
39
|
+
src = src.trim();
|
|
40
|
+
const nodes = [];
|
|
41
|
+
let i = 0;
|
|
42
|
+
while (i < src.length) {
|
|
43
|
+
if (src[i] === '<') {
|
|
44
|
+
const result = parseElement(src, i, values);
|
|
45
|
+
if (result) {
|
|
46
|
+
nodes.push(result.node);
|
|
47
|
+
i = result.end;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const result = parseText(src, i, values);
|
|
52
|
+
if (result.text) nodes.push(result.text);
|
|
53
|
+
i = result.end;
|
|
54
|
+
}
|
|
55
|
+
return nodes.length === 1 ? nodes[0] : nodes;
|
|
56
|
+
}
|
|
82
57
|
function parseElement(src, start, values) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
i += closeTag.length;
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (src[i] === '<') {
|
|
167
|
-
const child = parseElement(src, i, values);
|
|
168
|
-
if (child) {
|
|
169
|
-
children.push(child.node);
|
|
170
|
-
i = child.end;
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const text = parseText(src, i, values);
|
|
176
|
-
if (text.text != null) children.push(text.text);
|
|
177
|
-
i = text.end;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return {
|
|
181
|
-
node: h(tag, Object.keys(props).length ? props : null, ...children),
|
|
182
|
-
end: i,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
58
|
+
const openMatch = src.slice(start).match(/^<([a-zA-Z][a-zA-Z0-9-]*|[A-Z]\w*)/);
|
|
59
|
+
if (!openMatch) return null;
|
|
60
|
+
const tag = openMatch[1];
|
|
61
|
+
let i = start + openMatch[0].length;
|
|
62
|
+
const props = {};
|
|
63
|
+
while (i < src.length) {
|
|
64
|
+
while (i < src.length && /\s/.test(src[i])) i++;
|
|
65
|
+
if (src.slice(i, i + 2) === '/>') {
|
|
66
|
+
return { node: h(tag, Object.keys(props).length ? props : null), end: i + 2 };
|
|
67
|
+
}
|
|
68
|
+
if (src[i] === '>') {
|
|
69
|
+
i++;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
if (src.slice(i, i + 3) === '...') {
|
|
73
|
+
const placeholder = src.slice(i + 3).match(/^\x00(\d+)\x00/);
|
|
74
|
+
if (placeholder) {
|
|
75
|
+
Object.assign(props, values[Number(placeholder[1])]);
|
|
76
|
+
i += 3 + placeholder[0].length;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const attrMatch = src.slice(i).match(/^([a-zA-Z_@:][a-zA-Z0-9_:.-]*)/);
|
|
81
|
+
if (!attrMatch) break;
|
|
82
|
+
const attrName = attrMatch[1];
|
|
83
|
+
i += attrMatch[0].length;
|
|
84
|
+
while (i < src.length && /\s/.test(src[i])) i++;
|
|
85
|
+
if (src[i] === '=') {
|
|
86
|
+
i++;
|
|
87
|
+
while (i < src.length && /\s/.test(src[i])) i++;
|
|
88
|
+
const ph = src.slice(i).match(/^\x00(\d+)\x00/);
|
|
89
|
+
if (ph) {
|
|
90
|
+
props[attrName] = values[Number(ph[1])];
|
|
91
|
+
i += ph[0].length;
|
|
92
|
+
} else if (src[i] === '"' || src[i] === "'") {
|
|
93
|
+
const q = src[i];
|
|
94
|
+
i++;
|
|
95
|
+
let val = '';
|
|
96
|
+
while (i < src.length && src[i] !== q) {
|
|
97
|
+
const tph = src.slice(i).match(/^\x00(\d+)\x00/);
|
|
98
|
+
if (tph) {
|
|
99
|
+
val += String(values[Number(tph[1])]);
|
|
100
|
+
i += tph[0].length;
|
|
101
|
+
} else {
|
|
102
|
+
val += src[i];
|
|
103
|
+
i++;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
i++;
|
|
107
|
+
props[attrName] = val;
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
props[attrName] = true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const children = [];
|
|
114
|
+
const closeTag = `</${tag}>`;
|
|
115
|
+
while (i < src.length) {
|
|
116
|
+
if (src.slice(i, i + closeTag.length) === closeTag) {
|
|
117
|
+
i += closeTag.length;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
if (src[i] === '<') {
|
|
121
|
+
const child = parseElement(src, i, values);
|
|
122
|
+
if (child) {
|
|
123
|
+
children.push(child.node);
|
|
124
|
+
i = child.end;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const text = parseText(src, i, values);
|
|
129
|
+
if (text.text != null) children.push(text.text);
|
|
130
|
+
i = text.end;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
node: h(tag, Object.keys(props).length ? props : null, ...children),
|
|
134
|
+
end: i,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
186
137
|
function parseText(src, start, values) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
i++;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return { text: text.trim() || null, end: i };
|
|
138
|
+
let i = start;
|
|
139
|
+
let text = '';
|
|
140
|
+
while (i < src.length && src[i] !== '<') {
|
|
141
|
+
const ph = src.slice(i).match(/^\x00(\d+)\x00/);
|
|
142
|
+
if (ph) {
|
|
143
|
+
if (text.trim()) {
|
|
144
|
+
return { text: text.trim(), end: i };
|
|
145
|
+
}
|
|
146
|
+
return { text: values[Number(ph[1])], end: i + ph[0].length };
|
|
147
|
+
}
|
|
148
|
+
text += src[i];
|
|
149
|
+
i++;
|
|
203
150
|
}
|
|
151
|
+
return { text: text.trim() || null, end: i };
|
|
152
|
+
}
|
package/dist/head.js
CHANGED
|
@@ -1,68 +1,51 @@
|
|
|
1
|
-
// What Framework - Head Management
|
|
2
|
-
// Declarative <head> updates from any component.
|
|
3
|
-
// Supports title, meta, link, script tags. Auto-deduplicates by key.
|
|
4
|
-
|
|
5
1
|
const headState = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
title: null,
|
|
3
|
+
metas: new Map(),
|
|
4
|
+
links: new Map(),
|
|
9
5
|
};
|
|
10
|
-
|
|
11
|
-
// --- Head component ---
|
|
12
|
-
// Use in any component to set head tags. Last one wins for title/meta.
|
|
13
|
-
|
|
14
6
|
export function Head({ title, meta, link, children }) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return children || null;
|
|
7
|
+
if (typeof document === 'undefined') return null;
|
|
8
|
+
if (title) {
|
|
9
|
+
document.title = title;
|
|
10
|
+
headState.title = title;
|
|
11
|
+
}
|
|
12
|
+
if (meta) {
|
|
13
|
+
for (const attrs of (Array.isArray(meta) ? meta : [meta])) {
|
|
14
|
+
const key = attrs.name || attrs.property || attrs.httpEquiv || JSON.stringify(attrs);
|
|
15
|
+
setHeadTag('meta', key, attrs);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (link) {
|
|
19
|
+
for (const attrs of (Array.isArray(link) ? link : [link])) {
|
|
20
|
+
const key = attrs.rel + (attrs.href || '');
|
|
21
|
+
setHeadTag('link', key, attrs);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return children || null;
|
|
37
25
|
}
|
|
38
|
-
|
|
39
26
|
function setHeadTag(tag, key, attrs) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
document.head.appendChild(el);
|
|
27
|
+
const existing = document.head.querySelector(`[data-what-head="${key}"]`);
|
|
28
|
+
if (existing) {
|
|
29
|
+
updateElement(existing, attrs);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const el = document.createElement(tag);
|
|
33
|
+
el.setAttribute('data-what-head', key);
|
|
34
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
35
|
+
el.setAttribute(k, v);
|
|
36
|
+
}
|
|
37
|
+
document.head.appendChild(el);
|
|
52
38
|
}
|
|
53
|
-
|
|
54
39
|
function updateElement(el, attrs) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
}
|
|
40
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
41
|
+
if (el.getAttribute(k) !== v) {
|
|
42
|
+
el.setAttribute(k, v);
|
|
60
43
|
}
|
|
61
|
-
|
|
62
|
-
// --- Cleanup: remove head tags added by What ---
|
|
63
|
-
export function clearHead() {
|
|
64
|
-
const tags = document.head.querySelectorAll('[data-what-head]');
|
|
65
|
-
for (const tag of tags) tag.remove();
|
|
66
|
-
headState.metas.clear();
|
|
67
|
-
headState.links.clear();
|
|
68
44
|
}
|
|
45
|
+
}
|
|
46
|
+
export function clearHead() {
|
|
47
|
+
const tags = document.head.querySelectorAll('[data-what-head]');
|
|
48
|
+
for (const tag of tags) tag.remove();
|
|
49
|
+
headState.metas.clear();
|
|
50
|
+
headState.links.clear();
|
|
51
|
+
}
|