what-core 0.6.1 → 0.6.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 +2 -0
- package/compiler.d.ts +30 -0
- package/devtools.d.ts +2 -0
- package/dist/compiler.js +1787 -0
- package/dist/compiler.js.map +7 -0
- package/dist/compiler.min.js +2 -0
- package/dist/compiler.min.js.map +7 -0
- package/dist/devtools.js +10 -0
- package/dist/devtools.js.map +7 -0
- package/dist/devtools.min.js +2 -0
- package/dist/devtools.min.js.map +7 -0
- package/dist/index.js +331 -382
- package/dist/index.js.map +4 -4
- package/dist/index.min.js +62 -62
- package/dist/index.min.js.map +4 -4
- package/dist/render.js +263 -21
- package/dist/render.js.map +4 -4
- package/dist/render.min.js +58 -1
- package/dist/render.min.js.map +4 -4
- package/dist/testing.js +3 -0
- package/dist/testing.js.map +2 -2
- package/dist/testing.min.js +1 -1
- package/dist/testing.min.js.map +2 -2
- package/index.d.ts +176 -1
- package/jsx-runtime.d.ts +622 -0
- package/package.json +20 -2
- package/src/agent-context.js +1 -1
- package/src/compiler.js +18 -0
- package/src/components.js +73 -27
- package/src/devtools.js +4 -0
- package/src/dom.js +7 -0
- package/src/guardrails.js +3 -4
- package/src/hooks.js +0 -11
- package/src/index.js +5 -9
- package/src/render.js +94 -24
- package/dist/a11y.js +0 -440
- package/dist/animation.js +0 -548
- package/dist/components.js +0 -229
- package/dist/data.js +0 -638
- package/dist/dom.js +0 -439
- package/dist/form.js +0 -509
- package/dist/h.js +0 -152
- package/dist/head.js +0 -51
- package/dist/helpers.js +0 -140
- package/dist/hooks.js +0 -210
- package/dist/reactive.js +0 -432
- package/dist/scheduler.js +0 -246
- package/dist/skeleton.js +0 -363
- package/dist/store.js +0 -83
- package/dist/what.js +0 -117
package/dist/form.js
DELETED
|
@@ -1,509 +0,0 @@
|
|
|
1
|
-
// What Framework - Form Utilities
|
|
2
|
-
// Controlled inputs, validation, and form state management
|
|
3
|
-
|
|
4
|
-
import { signal, computed, batch, effect } from './reactive.js';
|
|
5
|
-
import { h } from './h.js';
|
|
6
|
-
|
|
7
|
-
// --- useForm Hook ---
|
|
8
|
-
// Complete form state management with validation
|
|
9
|
-
|
|
10
|
-
export function useForm(options = {}) {
|
|
11
|
-
const {
|
|
12
|
-
defaultValues = {},
|
|
13
|
-
mode = 'onSubmit', // 'onSubmit' | 'onChange' | 'onBlur'
|
|
14
|
-
reValidateMode = 'onChange',
|
|
15
|
-
resolver,
|
|
16
|
-
} = options;
|
|
17
|
-
|
|
18
|
-
// Per-field signals for granular reactivity (avoids full-form re-renders on each keystroke)
|
|
19
|
-
const fieldSignals = {};
|
|
20
|
-
const errorSignals = {};
|
|
21
|
-
const touchedSignals = {};
|
|
22
|
-
|
|
23
|
-
function getFieldSignal(name) {
|
|
24
|
-
if (!fieldSignals[name]) {
|
|
25
|
-
fieldSignals[name] = signal(defaultValues[name] ?? '');
|
|
26
|
-
}
|
|
27
|
-
return fieldSignals[name];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function getErrorSignal(name) {
|
|
31
|
-
if (!errorSignals[name]) {
|
|
32
|
-
errorSignals[name] = signal(null);
|
|
33
|
-
}
|
|
34
|
-
return errorSignals[name];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function getTouchedSignal(name) {
|
|
38
|
-
if (!touchedSignals[name]) {
|
|
39
|
-
touchedSignals[name] = signal(false);
|
|
40
|
-
}
|
|
41
|
-
return touchedSignals[name];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Aggregate signals for bulk operations
|
|
45
|
-
const isDirty = signal(false);
|
|
46
|
-
const isSubmitting = signal(false);
|
|
47
|
-
const isSubmitted = signal(false);
|
|
48
|
-
const submitCount = signal(0);
|
|
49
|
-
|
|
50
|
-
// Helper: get all current values as a plain object
|
|
51
|
-
function getAllValues() {
|
|
52
|
-
const result = { ...defaultValues };
|
|
53
|
-
for (const [name, sig] of Object.entries(fieldSignals)) {
|
|
54
|
-
result[name] = sig.peek();
|
|
55
|
-
}
|
|
56
|
-
return result;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Helper: get all current errors as a plain object
|
|
60
|
-
function getAllErrors() {
|
|
61
|
-
const result = {};
|
|
62
|
-
for (const [name, sig] of Object.entries(errorSignals)) {
|
|
63
|
-
const err = sig.peek();
|
|
64
|
-
if (err) result[name] = err;
|
|
65
|
-
}
|
|
66
|
-
return result;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Computed states
|
|
70
|
-
const isValid = computed(() => {
|
|
71
|
-
for (const sig of Object.values(errorSignals)) {
|
|
72
|
-
if (sig()) return false;
|
|
73
|
-
}
|
|
74
|
-
return true;
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
const dirtyFields = computed(() => {
|
|
78
|
-
const dirty = {};
|
|
79
|
-
for (const [name, sig] of Object.entries(fieldSignals)) {
|
|
80
|
-
if (sig() !== (defaultValues[name] ?? '')) {
|
|
81
|
-
dirty[name] = true;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return dirty;
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
// Validation
|
|
88
|
-
async function validate(fieldName) {
|
|
89
|
-
if (!resolver) return true;
|
|
90
|
-
|
|
91
|
-
const result = await resolver(getAllValues());
|
|
92
|
-
|
|
93
|
-
if (fieldName) {
|
|
94
|
-
// Validate single field — only update that field's error signal
|
|
95
|
-
const errSig = getErrorSignal(fieldName);
|
|
96
|
-
if (result.errors[fieldName]) {
|
|
97
|
-
errSig.set(result.errors[fieldName]);
|
|
98
|
-
return false;
|
|
99
|
-
} else {
|
|
100
|
-
errSig.set(null);
|
|
101
|
-
return true;
|
|
102
|
-
}
|
|
103
|
-
} else {
|
|
104
|
-
// Validate all fields
|
|
105
|
-
batch(() => {
|
|
106
|
-
// Clear existing errors
|
|
107
|
-
for (const sig of Object.values(errorSignals)) {
|
|
108
|
-
sig.set(null);
|
|
109
|
-
}
|
|
110
|
-
// Set new errors
|
|
111
|
-
for (const [name, err] of Object.entries(result.errors || {})) {
|
|
112
|
-
getErrorSignal(name).set(err);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
return Object.keys(result.errors || {}).length === 0;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Register a field — only subscribes to THIS field's signal
|
|
120
|
-
function register(name, options = {}) {
|
|
121
|
-
const fieldSig = getFieldSignal(name);
|
|
122
|
-
return {
|
|
123
|
-
name,
|
|
124
|
-
// Use getter so value is always fresh, even if register result is cached
|
|
125
|
-
get value() { return fieldSig(); },
|
|
126
|
-
onInput: (e) => {
|
|
127
|
-
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
|
|
128
|
-
setValue(name, value);
|
|
129
|
-
|
|
130
|
-
if (mode === 'onChange' || (isSubmitted.peek() && reValidateMode === 'onChange')) {
|
|
131
|
-
validate(name);
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
onBlur: () => {
|
|
135
|
-
getTouchedSignal(name).set(true);
|
|
136
|
-
|
|
137
|
-
if (mode === 'onBlur' || (isSubmitted.peek() && reValidateMode === 'onBlur')) {
|
|
138
|
-
validate(name);
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
onFocus: () => {},
|
|
142
|
-
ref: options.ref,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Set single field value — only triggers re-render for components reading this field
|
|
147
|
-
function setValue(name, value, options = {}) {
|
|
148
|
-
const { shouldValidate = false, shouldDirty = true } = options;
|
|
149
|
-
|
|
150
|
-
getFieldSignal(name).set(value);
|
|
151
|
-
if (shouldDirty) isDirty.set(true);
|
|
152
|
-
|
|
153
|
-
if (shouldValidate) {
|
|
154
|
-
validate(name);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Get single field value
|
|
159
|
-
function getValue(name) {
|
|
160
|
-
return getFieldSignal(name)();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Set error for a field
|
|
164
|
-
function setError(name, error) {
|
|
165
|
-
getErrorSignal(name).set(error);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Clear error for a field
|
|
169
|
-
function clearError(name) {
|
|
170
|
-
getErrorSignal(name).set(null);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Clear all errors
|
|
174
|
-
function clearErrors() {
|
|
175
|
-
batch(() => {
|
|
176
|
-
for (const sig of Object.values(errorSignals)) {
|
|
177
|
-
sig.set(null);
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Reset form
|
|
183
|
-
function reset(newValues = defaultValues) {
|
|
184
|
-
batch(() => {
|
|
185
|
-
for (const [name, sig] of Object.entries(fieldSignals)) {
|
|
186
|
-
sig.set(newValues[name] ?? '');
|
|
187
|
-
}
|
|
188
|
-
for (const sig of Object.values(errorSignals)) {
|
|
189
|
-
sig.set(null);
|
|
190
|
-
}
|
|
191
|
-
for (const sig of Object.values(touchedSignals)) {
|
|
192
|
-
sig.set(false);
|
|
193
|
-
}
|
|
194
|
-
isDirty.set(false);
|
|
195
|
-
isSubmitted.set(false);
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Handle submit
|
|
200
|
-
function handleSubmit(onValid, onInvalid) {
|
|
201
|
-
return async (e) => {
|
|
202
|
-
if (e) e.preventDefault();
|
|
203
|
-
|
|
204
|
-
isSubmitting.set(true);
|
|
205
|
-
isSubmitted.set(true);
|
|
206
|
-
submitCount.set(submitCount.peek() + 1);
|
|
207
|
-
|
|
208
|
-
const isFormValid = await validate();
|
|
209
|
-
|
|
210
|
-
if (isFormValid) {
|
|
211
|
-
await onValid(getAllValues());
|
|
212
|
-
} else if (onInvalid) {
|
|
213
|
-
onInvalid(getAllErrors());
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
isSubmitting.set(false);
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Watch a field — returns a computed that subscribes only to this field
|
|
221
|
-
function watch(name) {
|
|
222
|
-
if (name) {
|
|
223
|
-
return computed(() => getFieldSignal(name)());
|
|
224
|
-
}
|
|
225
|
-
// Watch all: return a computed that reads all field signals
|
|
226
|
-
return computed(() => getAllValues());
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return {
|
|
230
|
-
register,
|
|
231
|
-
handleSubmit,
|
|
232
|
-
setValue,
|
|
233
|
-
getValue,
|
|
234
|
-
setError,
|
|
235
|
-
clearError,
|
|
236
|
-
clearErrors,
|
|
237
|
-
reset,
|
|
238
|
-
watch,
|
|
239
|
-
validate,
|
|
240
|
-
// Form state — uses getters for errors/touched to enable per-field granularity
|
|
241
|
-
formState: {
|
|
242
|
-
get values() { return getAllValues(); },
|
|
243
|
-
get errors() { return getAllErrors(); },
|
|
244
|
-
get touched() {
|
|
245
|
-
const result = {};
|
|
246
|
-
for (const [name, sig] of Object.entries(touchedSignals)) {
|
|
247
|
-
if (sig()) result[name] = true;
|
|
248
|
-
}
|
|
249
|
-
return result;
|
|
250
|
-
},
|
|
251
|
-
isDirty: () => isDirty(),
|
|
252
|
-
isValid,
|
|
253
|
-
isSubmitting: () => isSubmitting(),
|
|
254
|
-
isSubmitted: () => isSubmitted(),
|
|
255
|
-
submitCount: () => submitCount(),
|
|
256
|
-
dirtyFields,
|
|
257
|
-
},
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// --- Validation Resolvers ---
|
|
262
|
-
|
|
263
|
-
export function zodResolver(schema) {
|
|
264
|
-
return async (values) => {
|
|
265
|
-
try {
|
|
266
|
-
const result = await schema.parseAsync(values);
|
|
267
|
-
return { values: result, errors: {} };
|
|
268
|
-
} catch (e) {
|
|
269
|
-
const errors = {};
|
|
270
|
-
for (const issue of e.errors || []) {
|
|
271
|
-
const path = issue.path.join('.');
|
|
272
|
-
if (!errors[path]) {
|
|
273
|
-
errors[path] = { type: issue.code, message: issue.message };
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
return { values: {}, errors };
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
export function yupResolver(schema) {
|
|
282
|
-
return async (values) => {
|
|
283
|
-
try {
|
|
284
|
-
const result = await schema.validate(values, { abortEarly: false });
|
|
285
|
-
return { values: result, errors: {} };
|
|
286
|
-
} catch (e) {
|
|
287
|
-
const errors = {};
|
|
288
|
-
for (const err of e.inner || []) {
|
|
289
|
-
if (!errors[err.path]) {
|
|
290
|
-
errors[err.path] = { type: err.type, message: err.message };
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
return { values: {}, errors };
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Simple validation resolver
|
|
299
|
-
export function simpleResolver(rules) {
|
|
300
|
-
return async (values) => {
|
|
301
|
-
const errors = {};
|
|
302
|
-
|
|
303
|
-
for (const [field, fieldRules] of Object.entries(rules)) {
|
|
304
|
-
const value = values[field];
|
|
305
|
-
|
|
306
|
-
for (const rule of fieldRules) {
|
|
307
|
-
const error = rule(value, values);
|
|
308
|
-
if (error) {
|
|
309
|
-
errors[field] = { type: 'validation', message: error };
|
|
310
|
-
break;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return { values, errors };
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Built-in validation rules
|
|
320
|
-
export const rules = {
|
|
321
|
-
required: (message = 'This field is required') => (value) => {
|
|
322
|
-
if (value === undefined || value === null || value === '') {
|
|
323
|
-
return message;
|
|
324
|
-
}
|
|
325
|
-
},
|
|
326
|
-
|
|
327
|
-
minLength: (min, message) => (value) => {
|
|
328
|
-
if (typeof value === 'string' && value.length < min) {
|
|
329
|
-
return message || `Must be at least ${min} characters`;
|
|
330
|
-
}
|
|
331
|
-
},
|
|
332
|
-
|
|
333
|
-
maxLength: (max, message) => (value) => {
|
|
334
|
-
if (typeof value === 'string' && value.length > max) {
|
|
335
|
-
return message || `Must be at most ${max} characters`;
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
|
|
339
|
-
min: (min, message) => (value) => {
|
|
340
|
-
if (typeof value === 'number' && value < min) {
|
|
341
|
-
return message || `Must be at least ${min}`;
|
|
342
|
-
}
|
|
343
|
-
},
|
|
344
|
-
|
|
345
|
-
max: (max, message) => (value) => {
|
|
346
|
-
if (typeof value === 'number' && value > max) {
|
|
347
|
-
return message || `Must be at most ${max}`;
|
|
348
|
-
}
|
|
349
|
-
},
|
|
350
|
-
|
|
351
|
-
pattern: (regex, message = 'Invalid format') => (value) => {
|
|
352
|
-
if (typeof value === 'string' && !regex.test(value)) {
|
|
353
|
-
return message;
|
|
354
|
-
}
|
|
355
|
-
},
|
|
356
|
-
|
|
357
|
-
email: (message = 'Invalid email address') => (value) => {
|
|
358
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
359
|
-
if (typeof value === 'string' && !emailRegex.test(value)) {
|
|
360
|
-
return message;
|
|
361
|
-
}
|
|
362
|
-
},
|
|
363
|
-
|
|
364
|
-
url: (message = 'Invalid URL') => (value) => {
|
|
365
|
-
try {
|
|
366
|
-
if (typeof value === 'string' && value) {
|
|
367
|
-
new URL(value);
|
|
368
|
-
}
|
|
369
|
-
} catch {
|
|
370
|
-
return message;
|
|
371
|
-
}
|
|
372
|
-
},
|
|
373
|
-
|
|
374
|
-
match: (field, message) => (value, values) => {
|
|
375
|
-
if (value !== values[field]) {
|
|
376
|
-
return message || `Must match ${field}`;
|
|
377
|
-
}
|
|
378
|
-
},
|
|
379
|
-
|
|
380
|
-
custom: (validator) => validator,
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
// --- useField Hook ---
|
|
384
|
-
// Individual field control
|
|
385
|
-
|
|
386
|
-
export function useField(name, options = {}) {
|
|
387
|
-
const { validate: validateFn, defaultValue = '' } = options;
|
|
388
|
-
|
|
389
|
-
const value = signal(defaultValue);
|
|
390
|
-
const error = signal(null);
|
|
391
|
-
const isTouched = signal(false);
|
|
392
|
-
const isDirty = signal(false);
|
|
393
|
-
|
|
394
|
-
async function validate() {
|
|
395
|
-
if (!validateFn) return true;
|
|
396
|
-
const result = await validateFn(value.peek());
|
|
397
|
-
error.set(result || null);
|
|
398
|
-
return !result;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
return {
|
|
402
|
-
name,
|
|
403
|
-
value: () => value(),
|
|
404
|
-
error: () => error(),
|
|
405
|
-
isTouched: () => isTouched(),
|
|
406
|
-
isDirty: () => isDirty(),
|
|
407
|
-
setValue: (v) => {
|
|
408
|
-
value.set(v);
|
|
409
|
-
isDirty.set(true);
|
|
410
|
-
},
|
|
411
|
-
setError: (e) => error.set(e),
|
|
412
|
-
validate,
|
|
413
|
-
reset: () => {
|
|
414
|
-
value.set(defaultValue);
|
|
415
|
-
error.set(null);
|
|
416
|
-
isTouched.set(false);
|
|
417
|
-
isDirty.set(false);
|
|
418
|
-
},
|
|
419
|
-
inputProps: () => ({
|
|
420
|
-
name,
|
|
421
|
-
value: value(),
|
|
422
|
-
onInput: (e) => {
|
|
423
|
-
value.set(e.target.value);
|
|
424
|
-
isDirty.set(true);
|
|
425
|
-
},
|
|
426
|
-
onBlur: () => {
|
|
427
|
-
isTouched.set(true);
|
|
428
|
-
validate();
|
|
429
|
-
},
|
|
430
|
-
}),
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// --- Controlled Input Components ---
|
|
435
|
-
|
|
436
|
-
export function Input(props) {
|
|
437
|
-
const { register, error, ...rest } = props;
|
|
438
|
-
const registered = register ? register(props.name) : {};
|
|
439
|
-
|
|
440
|
-
return h('input', {
|
|
441
|
-
...rest,
|
|
442
|
-
...registered,
|
|
443
|
-
'aria-invalid': error ? 'true' : undefined,
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
export function Textarea(props) {
|
|
448
|
-
const { register, error, ...rest } = props;
|
|
449
|
-
const registered = register ? register(props.name) : {};
|
|
450
|
-
|
|
451
|
-
return h('textarea', {
|
|
452
|
-
...rest,
|
|
453
|
-
...registered,
|
|
454
|
-
'aria-invalid': error ? 'true' : undefined,
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
export function Select(props) {
|
|
459
|
-
const { register, error, children, ...rest } = props;
|
|
460
|
-
const registered = register ? register(props.name) : {};
|
|
461
|
-
|
|
462
|
-
return h('select', {
|
|
463
|
-
...rest,
|
|
464
|
-
...registered,
|
|
465
|
-
'aria-invalid': error ? 'true' : undefined,
|
|
466
|
-
}, children);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
export function Checkbox(props) {
|
|
470
|
-
const { register, ...rest } = props;
|
|
471
|
-
const registered = register ? register(props.name) : {};
|
|
472
|
-
|
|
473
|
-
return h('input', {
|
|
474
|
-
type: 'checkbox',
|
|
475
|
-
...rest,
|
|
476
|
-
...registered,
|
|
477
|
-
checked: registered.value,
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
export function Radio(props) {
|
|
482
|
-
const { register, value: radioValue, ...rest } = props;
|
|
483
|
-
const registered = register ? register(props.name) : {};
|
|
484
|
-
|
|
485
|
-
return h('input', {
|
|
486
|
-
type: 'radio',
|
|
487
|
-
value: radioValue,
|
|
488
|
-
...rest,
|
|
489
|
-
checked: registered.value === radioValue,
|
|
490
|
-
onChange: (e) => {
|
|
491
|
-
if (e.target.checked && registered.onInput) {
|
|
492
|
-
registered.onInput({ target: { value: radioValue } });
|
|
493
|
-
}
|
|
494
|
-
},
|
|
495
|
-
});
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// --- Form Error Display ---
|
|
499
|
-
|
|
500
|
-
export function ErrorMessage({ name, errors, render }) {
|
|
501
|
-
const error = errors ? errors()[name] : null;
|
|
502
|
-
if (!error) return null;
|
|
503
|
-
|
|
504
|
-
if (render) {
|
|
505
|
-
return render({ message: error.message, type: error.type });
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
return h('span', { class: 'what-error', role: 'alert' }, error.message);
|
|
509
|
-
}
|
package/dist/h.js
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
const EMPTY_OBJ = Object.create(null);
|
|
2
|
-
const EMPTY_ARR = [];
|
|
3
|
-
export function h(tag, props, ...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
|
-
}
|
|
13
|
-
export function Fragment({ children }) {
|
|
14
|
-
return children;
|
|
15
|
-
}
|
|
16
|
-
function flattenChildren(children) {
|
|
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
|
-
}
|
|
33
|
-
export function html(strings, ...values) {
|
|
34
|
-
const src = strings.reduce((acc, str, i) =>
|
|
35
|
-
acc + str + (i < values.length ? `\x00${i}\x00` : ''), '');
|
|
36
|
-
return parseTemplate(src, values);
|
|
37
|
-
}
|
|
38
|
-
function parseTemplate(src, values) {
|
|
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
|
-
}
|
|
57
|
-
function parseElement(src, start, values) {
|
|
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
|
-
}
|
|
137
|
-
function parseText(src, start, values) {
|
|
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++;
|
|
150
|
-
}
|
|
151
|
-
return { text: text.trim() || null, end: i };
|
|
152
|
-
}
|
package/dist/head.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
const headState = {
|
|
2
|
-
title: null,
|
|
3
|
-
metas: new Map(),
|
|
4
|
-
links: new Map(),
|
|
5
|
-
};
|
|
6
|
-
export function Head({ title, meta, link, children }) {
|
|
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;
|
|
25
|
-
}
|
|
26
|
-
function setHeadTag(tag, key, attrs) {
|
|
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);
|
|
38
|
-
}
|
|
39
|
-
function updateElement(el, attrs) {
|
|
40
|
-
for (const [k, v] of Object.entries(attrs)) {
|
|
41
|
-
if (el.getAttribute(k) !== v) {
|
|
42
|
-
el.setAttribute(k, v);
|
|
43
|
-
}
|
|
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
|
-
}
|