what-core 0.10.0 → 0.11.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.
Files changed (76) hide show
  1. package/dist/chunk-D5YDPQ57.min.js +1 -0
  2. package/dist/chunk-O3SKPRTY.min.js +0 -1
  3. package/dist/chunk-W33M3HL5.min.js +1 -0
  4. package/dist/index.min.js +6 -7
  5. package/dist/jsx-dev-runtime.min.js +0 -1
  6. package/dist/jsx-runtime.min.js +0 -1
  7. package/dist/render.min.js +1 -2
  8. package/dist/testing.min.js +1 -2
  9. package/package.json +2 -2
  10. package/render.d.ts +18 -0
  11. package/src/agent-context.js +3 -2
  12. package/src/dom.js +16 -0
  13. package/src/guardrails.js +17 -46
  14. package/src/reactive.js +42 -4
  15. package/src/render.js +159 -55
  16. package/dist/a11y.js +0 -440
  17. package/dist/animation.js +0 -548
  18. package/dist/chunk-AW3BAPIK.js +0 -1685
  19. package/dist/chunk-AW3BAPIK.js.map +0 -7
  20. package/dist/chunk-AZP2EOGX.js +0 -188
  21. package/dist/chunk-AZP2EOGX.js.map +0 -7
  22. package/dist/chunk-F2HUXI22.js +0 -1675
  23. package/dist/chunk-F2HUXI22.js.map +0 -7
  24. package/dist/chunk-KBM6CWG4.min.js +0 -2
  25. package/dist/chunk-KBM6CWG4.min.js.map +0 -7
  26. package/dist/chunk-KL7TNUIU.min.js +0 -2
  27. package/dist/chunk-KL7TNUIU.min.js.map +0 -7
  28. package/dist/chunk-L6XOF7P4.min.js +0 -2
  29. package/dist/chunk-L6XOF7P4.min.js.map +0 -7
  30. package/dist/chunk-M7UEET5O.js +0 -1323
  31. package/dist/chunk-M7UEET5O.js.map +0 -7
  32. package/dist/chunk-O3SKPRTY.min.js.map +0 -7
  33. package/dist/chunk-RN6QIBWL.min.js +0 -2
  34. package/dist/chunk-RN6QIBWL.min.js.map +0 -7
  35. package/dist/chunk-VMTTYB4L.min.js +0 -2
  36. package/dist/chunk-VMTTYB4L.min.js.map +0 -7
  37. package/dist/chunk-VP4WLF5A.js +0 -1323
  38. package/dist/chunk-VP4WLF5A.js.map +0 -7
  39. package/dist/chunk-YA3W4XKH.js +0 -1323
  40. package/dist/chunk-YA3W4XKH.js.map +0 -7
  41. package/dist/compiler.js +0 -1799
  42. package/dist/compiler.js.map +0 -7
  43. package/dist/compiler.min.js +0 -2
  44. package/dist/compiler.min.js.map +0 -7
  45. package/dist/components.js +0 -229
  46. package/dist/data.js +0 -638
  47. package/dist/devtools.js +0 -10
  48. package/dist/devtools.js.map +0 -7
  49. package/dist/devtools.min.js +0 -2
  50. package/dist/devtools.min.js.map +0 -7
  51. package/dist/dom.js +0 -439
  52. package/dist/form.js +0 -509
  53. package/dist/h.js +0 -152
  54. package/dist/head.js +0 -51
  55. package/dist/helpers.js +0 -140
  56. package/dist/hooks.js +0 -210
  57. package/dist/index.js +0 -3578
  58. package/dist/index.js.map +0 -7
  59. package/dist/index.min.js.map +0 -7
  60. package/dist/jsx-dev-runtime.js +0 -23
  61. package/dist/jsx-dev-runtime.js.map +0 -7
  62. package/dist/jsx-dev-runtime.min.js.map +0 -7
  63. package/dist/jsx-runtime.js +0 -21
  64. package/dist/jsx-runtime.js.map +0 -7
  65. package/dist/jsx-runtime.min.js.map +0 -7
  66. package/dist/reactive.js +0 -432
  67. package/dist/render.js +0 -41
  68. package/dist/render.js.map +0 -7
  69. package/dist/render.min.js.map +0 -7
  70. package/dist/scheduler.js +0 -246
  71. package/dist/skeleton.js +0 -363
  72. package/dist/store.js +0 -83
  73. package/dist/testing.js +0 -439
  74. package/dist/testing.js.map +0 -7
  75. package/dist/testing.min.js.map +0 -7
  76. package/dist/what.js +0 -117
package/dist/a11y.js DELETED
@@ -1,440 +0,0 @@
1
- // What Framework - Accessibility Utilities
2
- // Focus management, ARIA helpers, screen reader announcements
3
-
4
- import { signal, effect } from './reactive.js';
5
- import { h } from './h.js';
6
- import { getCurrentComponent } from './dom.js';
7
-
8
- // --- Focus Management ---
9
-
10
- // Track currently focused element
11
- const focusedElement = signal(null);
12
-
13
- if (typeof document !== 'undefined') {
14
- document.addEventListener('focusin', (e) => {
15
- focusedElement.set(e.target);
16
- });
17
- }
18
-
19
- export function useFocus() {
20
- return {
21
- current: () => focusedElement(),
22
- focus: (element) => element?.focus(),
23
- blur: () => document.activeElement?.blur(),
24
- };
25
- }
26
-
27
- // --- Focus Trap ---
28
- // Keep focus within a container (for modals, dialogs, etc.)
29
-
30
- export function useFocusTrap(containerRef) {
31
- let previousFocus = null;
32
-
33
- function activate() {
34
- if (typeof document === 'undefined') return;
35
-
36
- previousFocus = document.activeElement;
37
- const container = containerRef.current || containerRef;
38
-
39
- if (!container) return;
40
-
41
- // Find all focusable elements
42
- const focusables = getFocusableElements(container);
43
- if (focusables.length === 0) return;
44
-
45
- // Focus first element
46
- focusables[0].focus();
47
-
48
- // Handle Tab key
49
- function handleKeydown(e) {
50
- if (e.key !== 'Tab') return;
51
-
52
- const focusables = getFocusableElements(container);
53
- const first = focusables[0];
54
- const last = focusables[focusables.length - 1];
55
-
56
- if (e.shiftKey) {
57
- // Shift+Tab: if on first, go to last
58
- if (document.activeElement === first) {
59
- e.preventDefault();
60
- last.focus();
61
- }
62
- } else {
63
- // Tab: if on last, go to first
64
- if (document.activeElement === last) {
65
- e.preventDefault();
66
- first.focus();
67
- }
68
- }
69
- }
70
-
71
- container.addEventListener('keydown', handleKeydown);
72
-
73
- return () => {
74
- container.removeEventListener('keydown', handleKeydown);
75
- };
76
- }
77
-
78
- function deactivate() {
79
- if (previousFocus && typeof previousFocus.focus === 'function') {
80
- previousFocus.focus();
81
- }
82
- }
83
-
84
- return { activate, deactivate };
85
- }
86
-
87
- function getFocusableElements(container) {
88
- const selector = [
89
- 'button:not([disabled])',
90
- 'a[href]',
91
- 'input:not([disabled])',
92
- 'select:not([disabled])',
93
- 'textarea:not([disabled])',
94
- '[tabindex]:not([tabindex="-1"])',
95
- ].join(',');
96
-
97
- return Array.from(container.querySelectorAll(selector)).filter(el => {
98
- return el.offsetParent !== null; // Visible
99
- });
100
- }
101
-
102
- // --- Focus Scope ---
103
- // Component wrapper that traps focus
104
-
105
- export function FocusTrap({ children, active = true }) {
106
- const containerRef = { current: null };
107
- const trap = useFocusTrap(containerRef);
108
-
109
- // Scope the effect to the component lifecycle so it disposes on unmount
110
- const dispose = effect(() => {
111
- if (active) {
112
- const cleanup = trap.activate();
113
- return () => {
114
- cleanup?.();
115
- trap.deactivate();
116
- };
117
- }
118
- });
119
-
120
- // Register cleanup on component context
121
- const ctx = getCurrentComponent?.();
122
- if (ctx) {
123
- ctx._cleanupCallbacks = ctx._cleanupCallbacks || [];
124
- ctx._cleanupCallbacks.push(dispose);
125
- }
126
-
127
- return h('div', { ref: containerRef }, children);
128
- }
129
-
130
- // --- Screen Reader Announcements ---
131
-
132
- let announcer = null;
133
- let announcerId = 0;
134
-
135
- function getAnnouncer() {
136
- if (typeof document === 'undefined') return null;
137
-
138
- if (!announcer) {
139
- announcer = document.createElement('div');
140
- announcer.id = 'what-announcer';
141
- announcer.setAttribute('aria-live', 'polite');
142
- announcer.setAttribute('aria-atomic', 'true');
143
- announcer.style.cssText = `
144
- position: absolute;
145
- width: 1px;
146
- height: 1px;
147
- padding: 0;
148
- margin: -1px;
149
- overflow: hidden;
150
- clip: rect(0, 0, 0, 0);
151
- white-space: nowrap;
152
- border: 0;
153
- `;
154
- document.body.appendChild(announcer);
155
- }
156
- return announcer;
157
- }
158
-
159
- export function announce(message, options = {}) {
160
- const { priority = 'polite', timeout = 1000 } = options;
161
- const announcer = getAnnouncer();
162
- if (!announcer) return;
163
-
164
- announcer.setAttribute('aria-live', priority);
165
-
166
- // Clear and re-announce (required for some screen readers)
167
- const id = ++announcerId;
168
- announcer.textContent = '';
169
-
170
- requestAnimationFrame(() => {
171
- if (announcerId === id) {
172
- announcer.textContent = message;
173
- }
174
- });
175
-
176
- // Clear after timeout
177
- setTimeout(() => {
178
- if (announcerId === id) {
179
- announcer.textContent = '';
180
- }
181
- }, timeout);
182
- }
183
-
184
- export function announceAssertive(message) {
185
- return announce(message, { priority: 'assertive' });
186
- }
187
-
188
- // --- Skip Link ---
189
- // Accessible skip navigation
190
-
191
- export function SkipLink({ href = '#main', children = 'Skip to content' }) {
192
- return h('a', {
193
- href,
194
- class: 'what-skip-link',
195
- onClick: (e) => {
196
- e.preventDefault();
197
- const target = document.querySelector(href);
198
- if (target) {
199
- target.focus();
200
- target.scrollIntoView();
201
- }
202
- },
203
- style: {
204
- position: 'absolute',
205
- top: '-40px',
206
- left: '0',
207
- padding: '8px',
208
- background: '#000',
209
- color: '#fff',
210
- textDecoration: 'none',
211
- zIndex: '10000',
212
- },
213
- onFocus: (e) => {
214
- e.target.style.top = '0';
215
- },
216
- onBlur: (e) => {
217
- e.target.style.top = '-40px';
218
- },
219
- }, children);
220
- }
221
-
222
- // --- ARIA Helpers ---
223
-
224
- export function useAriaExpanded(initialExpanded = false) {
225
- const expanded = signal(initialExpanded);
226
-
227
- return {
228
- expanded: () => expanded(),
229
- toggle: () => expanded.set(!expanded.peek()),
230
- open: () => expanded.set(true),
231
- close: () => expanded.set(false),
232
- buttonProps: () => ({
233
- 'aria-expanded': expanded(),
234
- onClick: () => expanded.set(!expanded.peek()),
235
- }),
236
- panelProps: () => ({
237
- hidden: !expanded(),
238
- }),
239
- };
240
- }
241
-
242
- export function useAriaSelected(initialSelected = null) {
243
- const selected = signal(initialSelected);
244
-
245
- return {
246
- selected: () => selected(),
247
- select: (value) => selected.set(value),
248
- isSelected: (value) => selected() === value,
249
- itemProps: (value) => ({
250
- 'aria-selected': selected() === value,
251
- onClick: () => selected.set(value),
252
- }),
253
- };
254
- }
255
-
256
- export function useAriaChecked(initialChecked = false) {
257
- const checked = signal(initialChecked);
258
-
259
- return {
260
- checked: () => checked(),
261
- toggle: () => checked.set(!checked.peek()),
262
- set: (value) => checked.set(value),
263
- checkboxProps: () => ({
264
- role: 'checkbox',
265
- 'aria-checked': checked(),
266
- tabIndex: 0,
267
- onClick: () => checked.set(!checked.peek()),
268
- onKeyDown: (e) => {
269
- if (e.key === ' ' || e.key === 'Enter') {
270
- e.preventDefault();
271
- checked.set(!checked.peek());
272
- }
273
- },
274
- }),
275
- };
276
- }
277
-
278
- // --- Roving Tab Index ---
279
- // For keyboard navigation in lists, toolbars, etc.
280
-
281
- export function useRovingTabIndex(itemCountOrSignal) {
282
- // Accept either a static number or a signal/getter for dynamic lists
283
- const getCount = typeof itemCountOrSignal === 'function'
284
- ? itemCountOrSignal
285
- : () => itemCountOrSignal;
286
- const focusIndex = signal(0);
287
-
288
- function handleKeyDown(e) {
289
- const count = getCount();
290
- if (count <= 0) return;
291
- switch (e.key) {
292
- case 'ArrowDown':
293
- case 'ArrowRight':
294
- e.preventDefault();
295
- focusIndex.set((focusIndex.peek() + 1) % count);
296
- break;
297
- case 'ArrowUp':
298
- case 'ArrowLeft':
299
- e.preventDefault();
300
- focusIndex.set((focusIndex.peek() - 1 + count) % count);
301
- break;
302
- case 'Home':
303
- e.preventDefault();
304
- focusIndex.set(0);
305
- break;
306
- case 'End':
307
- e.preventDefault();
308
- focusIndex.set(count - 1);
309
- break;
310
- }
311
- }
312
-
313
- return {
314
- focusIndex: () => focusIndex(),
315
- setFocusIndex: (i) => focusIndex.set(i),
316
- getItemProps: (index) => ({
317
- tabIndex: focusIndex() === index ? 0 : -1,
318
- onKeyDown: handleKeyDown,
319
- onFocus: () => focusIndex.set(index),
320
- }),
321
- containerProps: () => ({
322
- role: 'listbox',
323
- }),
324
- };
325
- }
326
-
327
- // --- Visually Hidden ---
328
- // Hide content visually but keep accessible to screen readers
329
-
330
- export function VisuallyHidden({ children, as = 'span' }) {
331
- return h(as, {
332
- style: {
333
- position: 'absolute',
334
- width: '1px',
335
- height: '1px',
336
- padding: '0',
337
- margin: '-1px',
338
- overflow: 'hidden',
339
- clip: 'rect(0, 0, 0, 0)',
340
- whiteSpace: 'nowrap',
341
- border: '0',
342
- },
343
- }, children);
344
- }
345
-
346
- // --- Live Region Component ---
347
-
348
- export function LiveRegion({ children, priority = 'polite', atomic = true }) {
349
- return h('div', {
350
- 'aria-live': priority,
351
- 'aria-atomic': atomic,
352
- }, children);
353
- }
354
-
355
- // --- ID Generator ---
356
- // Generate unique IDs for ARIA attributes
357
-
358
- let idCounter = 0;
359
-
360
- export function useId(prefix = 'what') {
361
- const id = `${prefix}-${++idCounter}`;
362
- return () => id;
363
- }
364
-
365
- export function useIds(count, prefix = 'what') {
366
- const ids = [];
367
- for (let i = 0; i < count; i++) {
368
- ids.push(`${prefix}-${++idCounter}`);
369
- }
370
- return ids;
371
- }
372
-
373
- // --- Describe ---
374
- // Associate description with an element
375
-
376
- export function useDescribedBy(description) {
377
- const id = useId('desc');
378
-
379
- return {
380
- descriptionId: id,
381
- descriptionProps: () => ({
382
- id: id(),
383
- style: { display: 'none' },
384
- }),
385
- describedByProps: () => ({
386
- 'aria-describedby': id(),
387
- }),
388
- Description: () => h('div', {
389
- id: id(),
390
- style: { display: 'none' },
391
- }, description),
392
- };
393
- }
394
-
395
- // --- Labelledby ---
396
-
397
- export function useLabelledBy(label) {
398
- const id = useId('label');
399
-
400
- return {
401
- labelId: id,
402
- labelProps: () => ({
403
- id: id(),
404
- }),
405
- labelledByProps: () => ({
406
- 'aria-labelledby': id(),
407
- }),
408
- };
409
- }
410
-
411
- // --- Keyboard Navigation Helpers ---
412
-
413
- export const Keys = {
414
- Enter: 'Enter',
415
- Space: ' ',
416
- Escape: 'Escape',
417
- ArrowUp: 'ArrowUp',
418
- ArrowDown: 'ArrowDown',
419
- ArrowLeft: 'ArrowLeft',
420
- ArrowRight: 'ArrowRight',
421
- Home: 'Home',
422
- End: 'End',
423
- Tab: 'Tab',
424
- };
425
-
426
- export function onKey(key, handler) {
427
- return (e) => {
428
- if (e.key === key) {
429
- handler(e);
430
- }
431
- };
432
- }
433
-
434
- export function onKeys(keys, handler) {
435
- return (e) => {
436
- if (keys.includes(e.key)) {
437
- handler(e);
438
- }
439
- };
440
- }