@wrnrlr/prelude 0.1.8 → 0.2.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.
Files changed (53) hide show
  1. package/.github/workflows/publish.yml +12 -11
  2. package/{deno.json → deno.jsonc} +13 -8
  3. package/example/index.html +43 -0
  4. package/package.json +6 -2
  5. package/src/constants.ts +110 -52
  6. package/src/controlflow.ts +140 -91
  7. package/src/hyperscript.ts +85 -109
  8. package/src/mod.ts +3 -5
  9. package/src/reactive.ts +29 -17
  10. package/src/runtime.ts +128 -66
  11. package/test/hyperscript.js +7 -6
  12. package/test/reactive.js +22 -2
  13. package/test/types.ts +44 -0
  14. package/src/components.js +0 -20
  15. package/src/ui/accordion.ts +0 -8
  16. package/src/ui/button.ts +0 -9
  17. package/src/ui/canvas.ts +0 -8
  18. package/src/ui/date.ts +0 -8
  19. package/src/ui/dialog.ts +0 -12
  20. package/src/ui/filter.ts +0 -8
  21. package/src/ui/form.ts +0 -7
  22. package/src/ui/h.ts +0 -48
  23. package/src/ui/image.ts +0 -5
  24. package/src/ui/input.ts +0 -49
  25. package/src/ui/mod.ts +0 -12
  26. package/src/ui/multiselect.ts +0 -42
  27. package/src/ui/option.ts +0 -1
  28. package/src/ui/select.ts +0 -28
  29. package/src/ui/tab.ts +0 -7
  30. package/src/ui/table.ts +0 -9
  31. package/src/ui/upload.ts +0 -7
  32. package/www/assets/css/presets.css +0 -504
  33. package/www/assets/css/style.css +0 -90
  34. package/www/demo.html +0 -28
  35. package/www/index.html +0 -211
  36. package/www/playground.html +0 -184
  37. package/www/public/banner.svg +0 -6
  38. package/www/public/example/admin.html +0 -88
  39. package/www/public/example/counter.html +0 -24
  40. package/www/public/example/greeting.html +0 -25
  41. package/www/public/example/select.html +0 -27
  42. package/www/public/example/show.html +0 -18
  43. package/www/public/example/todo.html +0 -70
  44. package/www/public/fonts/fab.ttf +0 -0
  45. package/www/public/fonts/fab.woff2 +0 -0
  46. package/www/public/fonts/far.ttf +0 -0
  47. package/www/public/fonts/far.woff2 +0 -0
  48. package/www/public/fonts/fas.ttf +0 -0
  49. package/www/public/fonts/fas.woff2 +0 -0
  50. package/www/public/logo.svg +0 -16
  51. package/www/typedoc.json +0 -13
  52. package/www/ui.html +0 -49
  53. package/www/vite.config.js +0 -106
package/src/runtime.ts CHANGED
@@ -1,10 +1,24 @@
1
1
  // @ts-nocheck:
2
2
  import {effect,untrack,root} from './reactive.ts'
3
3
  import {SVGNamespace,SVGElements,ChildProperties,getPropAlias,Properties,Aliases,DelegatedEvents} from './constants.ts'
4
- import type {Window,Mountable,Elem,Node} from './constants.ts'
4
+ // import type {Mountable} from './constants.ts'
5
5
 
6
6
  const {isArray} = Array
7
- export const $RUNTIME = Symbol()
7
+
8
+ export type Mountable = HTMLElement | Document | ShadowRoot | DocumentFragment | Node | string | number | bigint | symbol;
9
+
10
+ // declare global {
11
+ // interface Document {
12
+ // '_$DX_DELEGATE'?: Record<string, Set<unknown>>
13
+ // }
14
+ // // interface SVGElement {}
15
+ // }
16
+
17
+ // interface Element {
18
+ // style?: string
19
+ // }
20
+
21
+ // declare const globalThis: Document
8
22
 
9
23
  /**
10
24
 
@@ -12,47 +26,63 @@ export const $RUNTIME = Symbol()
12
26
  */
13
27
  export type Runtime = {
14
28
  // window:Window
15
- render(code:()=>void, element:Elem, init:any): any;
29
+ render(code:()=>void, element:Element, init:any): any;
16
30
  insert(parent:Mountable, accessor:any, marker?:Node|null, init?:any): any;
17
- spread(node:Elem, accessor:any, skipChildren?: boolean): void;
18
- assign(node:Elem, props:any, skipChildren?:boolean): void;
31
+ spread(node:Element, accessor:any, skipChildren?: boolean): void;
32
+ assign(node:Element, props:any, skipChildren?:boolean): void;
19
33
  element(name:string): any;
34
+ component(fn:()=>unknown): any
20
35
  text(s:string): any;
21
36
  isChild(a:any): boolean;
22
37
  clearDelegatedEvents():void
23
38
  }
24
39
 
40
+ type ClassListProps = Record<string, string|boolean>
41
+ type StyleProps = undefined | string | Record<string, string|undefined>
42
+
25
43
  /**
26
44
  Create `Runtime` for `window`
27
45
  @param window
28
46
  @group Internal
29
47
  */
30
- export function runtime(window:Window):Runtime {
31
- const document = window.document,
32
- isSVG = (e:any) => e instanceof (window.SVGElement as any),
33
- element = (name:string) => SVGElements.has(name) ? document.createElementNS("http://www.w3.org/2000/svg",name) : document.createElement(name),
48
+ export function runtime(w:Window):Runtime {
49
+ const document = w.document
50
+ const isSVG = (e: unknown) => e instanceof (w.SVGElement),
51
+ element = (name:string) => SVGElements.has(name) ?
52
+ document.createElementNS("http://www.w3.org/2000/svg",name) : document.createElement(name),
34
53
  text = (s:string) => document.createTextNode(s)
35
54
 
36
- function isChild(a:unknown):boolean {
37
- return a instanceof document.Element
55
+ function isChild(a:unknown): a is Element {
56
+ return a instanceof Element
57
+ }
58
+
59
+ function component(fn:()=>unknown) {
60
+ return untrack(fn)
38
61
  }
39
62
 
40
- function render(code:()=>void, element:Elem, init?:any) {
63
+ function render(code: ()=>void, element:Element, init?:any): void
64
+ function render(code: ()=>void, element:Document, init?:any): void
65
+ function render(code: ()=>void, element:Element|Document, init?:any): void {
41
66
  if (!element) throw new Error("The `element` passed to `render(..., element)` doesn't exist.");
42
67
  root(() => {
43
- if (element === document) code()
44
- else insert(element, code(), element.firstChild ? null : undefined, init)
68
+ if (element instanceof Document) code()
69
+ else insert(element as Element, code(), element.firstChild ? null : undefined, init)
45
70
  })
46
71
  }
47
72
 
48
- function insert(parent:Mountable, accessor:any, marker?:Node|null, initial?:any) {
73
+ type Value = string | number | Element | Text
74
+ type RxValue = (()=>Value) | Value
75
+
76
+ function insert(parent: Element, accessor: string, marker?: Node|null, initial?: any): void
77
+ function insert(parent: Element, accessor: ()=>string, marker?: Node|null, initial?: any): void
78
+ function insert(parent: Element, accessor: string|(()=>string), marker?: Node|null, initial?: any): void {
49
79
  if (marker !== undefined && !initial) initial = []
50
- if (!accessor.call) return insertExpression(parent, accessor, initial||[], marker)
80
+ if (typeof accessor !== 'function') return insertExpression(parent, accessor, initial||[], marker)
51
81
  let current = initial||[]
52
82
  effect(() => {current = insertExpression(parent, accessor(), current, marker)})
53
83
  }
54
84
 
55
- function spread(node:Elem, props:any = {}, skipChildren:boolean) {
85
+ function spread(node:Element, props:any = {}, skipChildren:boolean) {
56
86
  const prevProps:any = {}
57
87
  if (!skipChildren) effect(() => (prevProps.children = insertExpression(node, props.children, prevProps.children)))
58
88
  effect(() => (props.ref?.call ? untrack(() => props.ref(node)) : (props.ref = node)))
@@ -60,7 +90,7 @@ export function runtime(window:Window):Runtime {
60
90
  return prevProps
61
91
  }
62
92
 
63
- function assign(node:Elem, props:any, skipChildren:boolean, prevProps:any = {}, skipRef:boolean = false) {
93
+ function assign(node:Element, props:any, skipChildren:boolean, prevProps:any = {}, skipRef:boolean = false) {
64
94
  const svg = isSVG(node)
65
95
  props || (props = {})
66
96
  for (const prop in prevProps) {
@@ -79,22 +109,34 @@ export function runtime(window:Window):Runtime {
79
109
  }
80
110
  }
81
111
 
82
- function assignProp(node:Node, prop:any, value:any, prev:any, isSVG:any, skipRef:any) {
112
+ function assignProp(node: Element, prop: 'style', value: StyleProps, prev: StyleProps, isSVG: boolean, skipRef: boolean): StyleProps
113
+ function assignProp(node: Element, prop: 'classList', value: ClassListProps, prev: ClassListProps, isSVG: boolean, skipRef: boolean): ClassListProps
114
+ function assignProp(node: Element, prop: 'ref', value: ()=>string, prev:string, isSVG: boolean, skipRef: false): string
115
+ function assignProp(node: Element, prop: string, value: string, prev: string|undefined, isSVG: boolean, skipRef: boolean): string
116
+ function assignProp(node: Element, prop: string, value: undefined, prev: string|undefined, isSVG: boolean, skipRef: boolean): undefined
117
+ function assignProp(
118
+ node: Element,
119
+ prop: 'style' | 'classList' | 'ref' | string,
120
+ value: StyleProps | ClassListProps | (()=>string) | string | undefined,
121
+ prev: StyleProps | ClassListProps | (()=>string) | string | undefined,
122
+ isSVG: false | boolean,
123
+ skipRef: boolean
124
+ ) {
83
125
  let isCE, isProp, isChildProp, propAlias, forceProp;
84
- if (prop === "style") return style(node, value, prev);
85
- if (prop === "classList") return classList(node, value, prev);
126
+ if (prop === 'style') return style(node, value, prev);
127
+ if (prop === 'classList') return classList(node, value, prev);
86
128
  if (value === prev) return prev;
87
- if (prop === "ref") {
129
+ if (prop === 'ref') {
88
130
  if (!skipRef) value(node);
89
- } else if (prop.slice(0, 3) === "on:") {
131
+ } else if (prop.slice(0, 3) === 'on:') {
90
132
  const e = prop.slice(3);
91
133
  prev && node.removeEventListener(e, prev);
92
134
  value && node.addEventListener(e, value);
93
- } else if (prop.slice(0, 10) === "oncapture:") {
135
+ } else if (prop.slice(0, 10) === 'oncapture:') {
94
136
  const e = prop.slice(10);
95
137
  prev && node.removeEventListener(e, prev, true);
96
138
  value && node.addEventListener(e, value, true);
97
- } else if (prop.slice(0, 2) === "on") {
139
+ } else if (prop.slice(0, 2) === 'on') {
98
140
  const name = prop.slice(2).toLowerCase();
99
141
  const delegate = DelegatedEvents.has(name);
100
142
  if (!delegate && prev) {
@@ -105,13 +147,13 @@ export function runtime(window:Window):Runtime {
105
147
  addEventListener(node, name, value, delegate);
106
148
  delegate && delegateEvents([name],document);
107
149
  }
108
- } else if (prop.slice(0, 5) === "attr:") {
150
+ } else if (prop.slice(0, 5) === 'attr:') {
109
151
  setAttribute(node, prop.slice(5), value);
110
152
  } else if (
111
- (forceProp = prop.slice(0, 5) === "prop:") ||
153
+ (forceProp = prop.slice(0, 5) === 'prop:') ||
112
154
  (isChildProp = ChildProperties.has(prop)) ||
113
155
  (!isSVG && ((propAlias = getPropAlias(prop, node.tagName)) || (isProp = Properties.has(prop)))) ||
114
- (isCE = node.nodeName.includes("-"))
156
+ (isCE = node.nodeName.includes('-'))
115
157
  ) {
116
158
  if (forceProp) {
117
159
  prop = prop.slice(5);
@@ -121,15 +163,21 @@ export function runtime(window:Window):Runtime {
121
163
  else if (isCE && !isProp && !isChildProp) node[toPropertyName(prop)] = value;
122
164
  else node[propAlias || prop] = value;
123
165
  } else {
124
- const ns = isSVG && prop.indexOf(":") > -1 && SVGNamespace[prop.split(":")[0]];
125
- if (ns) setAttributeNS(node, ns, prop, value);
126
- else setAttribute(node, Aliases[prop] || prop, value);
166
+ const ns = isSVG && prop.indexOf(':') > -1 && SVGNamespace[prop.split(':')[0]]
167
+ if (ns) setAttributeNS(node, ns, prop, value)
168
+ else setAttribute(node, Aliases[prop] || prop, value)
127
169
  }
128
170
  return value;
129
171
  }
130
172
 
131
- function insertExpression(parent:Node, value:any, current?:any, marker?:Node, unwrapArray?:any) {
132
- while (current?.call) current = current();
173
+ function insertExpression(
174
+ parent: Element,
175
+ value: RxValue,
176
+ current?: RxValue|Value[],
177
+ marker?: Node,
178
+ unwrapArray?: boolean
179
+ ): Value|{():Value} {
180
+ while (typeof current === 'function') current = current();
133
181
  if (value === current) return current;
134
182
  const t = typeof value,
135
183
  multi = marker !== undefined;
@@ -147,21 +195,21 @@ export function runtime(window:Window):Runtime {
147
195
  } else node = document.createTextNode(value);
148
196
  current = cleanChildren(parent, current, marker, node);
149
197
  } else {
150
- if (current !== "" && typeof current === "string") {
151
- current = parent.firstChild.data = value;
198
+ if (current !== "" && typeof current === 'string') {
199
+ current = (parent.firstChild as Text).data = value;
152
200
  } else current = parent.textContent = value;
153
201
  }
154
- } else if (value == null || t === "boolean") {
202
+ } else if (value == null || t === 'boolean') {
155
203
  current = cleanChildren(parent, current, marker);
156
- } else if (t === "function") {
204
+ } else if (t === 'function') {
157
205
  effect(() => {
158
206
  let v = value();
159
- while (typeof v === "function") v = v();
207
+ while (typeof v === 'function') v = v();
160
208
  current = insertExpression(parent, v, current, marker);
161
209
  });
162
210
  return () => current;
163
211
  } else if (isArray(value)) {
164
- const array:any[] = [];
212
+ const array:Node[] = [];
165
213
  const currentArray = current && isArray(current);
166
214
  if (normalizeIncomingArray(array, value, current, unwrapArray)) {
167
215
  effect(() => (current = insertExpression(parent, array, current, marker, true)));
@@ -191,20 +239,21 @@ export function runtime(window:Window):Runtime {
191
239
  return current;
192
240
  }
193
241
 
194
- function normalizeIncomingArray(normalized:any, array:any, current:any, unwrap?:any):any {
242
+ function normalizeIncomingArray(normalized:Node[], array:Node[], current:Node[], unwrap?:boolean): boolean {
195
243
  let dynamic = false;
196
244
  for (let i = 0, len = array.length; i < len; i++) {
197
245
  let item = array[i]
198
246
  const prev = current && current[normalized.length];
199
- if (item == null || item === true || item === false) {
200
- // matches null, undefined, true or false skip
201
- } else if (typeof item === "object" && item.nodeType) {
247
+ // if (item == null || item === true || item === false) {
248
+ // // matches null, undefined, true or false skip
249
+ // } else
250
+ if (typeof item === 'object' && item.nodeType) {
202
251
  normalized.push(item);
203
252
  } else if (isArray(item)) {
204
253
  dynamic = normalizeIncomingArray(normalized, item, prev) || dynamic;
205
254
  } else if (item.call) {
206
255
  if (unwrap) {
207
- while (typeof item === "function") item = item();
256
+ while (typeof item === 'function') item = item();
208
257
  dynamic = normalizeIncomingArray(
209
258
  normalized,
210
259
  isArray(item) ? item : [item],
@@ -223,8 +272,13 @@ export function runtime(window:Window):Runtime {
223
272
  return dynamic;
224
273
  }
225
274
 
226
- function cleanChildren(parent:any, current?:any, marker?:Node, replacement?:any):any {
227
- if (marker === undefined) return (parent.textContent = "");
275
+ function cleanChildren(
276
+ parent: Element,
277
+ current?: Node[],
278
+ marker?: Node|null,
279
+ replacement?: boolean
280
+ ): string | Node[] {
281
+ if (marker === undefined) return (parent.textContent = '');
228
282
  const node = replacement || document.createTextNode('');
229
283
  if (current.length) {
230
284
  let inserted = false;
@@ -242,18 +296,18 @@ export function runtime(window:Window):Runtime {
242
296
  }
243
297
 
244
298
  function clearDelegatedEvents() {
245
- if (document[$$EVENTS]) {
246
- for (const name of document[$$EVENTS].keys()) document.removeEventListener(name, eventHandler);
247
- delete document[$$EVENTS];
299
+ if (globalThis[$$EVENTS]) {
300
+ for (const name of globalThis[$$EVENTS].keys()) document.removeEventListener(name, eventHandler);
301
+ delete globalThis[$$EVENTS];
248
302
  }
249
303
  }
250
304
 
251
- return {render,insert,spread,assign,element,text,isChild,clearDelegatedEvents}
305
+ return {render,component,insert,spread,assign,element,text,isChild,clearDelegatedEvents}
252
306
  }
253
307
 
254
308
  const $$EVENTS = "_$DX_DELEGATE"
255
309
 
256
- function delegateEvents(eventNames:string[], document:any) {
310
+ function delegateEvents(eventNames:string[], document:Document) {
257
311
  const e = document[$$EVENTS] || (document[$$EVENTS] = new Set());
258
312
  for (let i = 0, l = eventNames.length; i < l; i++) {
259
313
  const name = eventNames[i];
@@ -264,13 +318,13 @@ function delegateEvents(eventNames:string[], document:any) {
264
318
  }
265
319
  }
266
320
 
267
- function eventHandler(e:any) {
321
+ function eventHandler(e: Event) {
268
322
  const key = `$$${e.type}`
269
323
  let node = (e.composedPath && e.composedPath()[0]) || e.target
270
324
  // reverse Shadow DOM retargetting
271
- if (e.target !== node) Object.defineProperty(e, "target", {configurable: true, value: node})
325
+ if (e.target !== node) Object.defineProperty(e, 'target', {configurable: true, value: node})
272
326
  // simulate currentTarget
273
- Object.defineProperty(e, "currentTarget", {configurable: true, get() {return node || document}})
327
+ Object.defineProperty(e, 'currentTarget', {configurable: true, get() {return node || document}})
274
328
  while (node) {
275
329
  const handler = node[key];
276
330
  if (handler && !node.disabled) {
@@ -282,7 +336,7 @@ function eventHandler(e:any) {
282
336
  }
283
337
  }
284
338
 
285
- function setAttribute(node:Node, name:string, value?:string):any {
339
+ function setAttribute(node: Element, name: string, value?: string): undefined {
286
340
  value===undefined || value===null ? node.removeAttribute(name) : node.setAttribute(name, value)
287
341
  }
288
342
 
@@ -290,7 +344,7 @@ function setAttributeNS(node:Node, ns:string, name:string, value?:string):any {
290
344
  value ? node.setAttributeNS(ns, name, value) : node.removeAttributeNS(ns, name)
291
345
  }
292
346
 
293
- function addEventListener(node:Node, name:any, handler:any, delegate:any):any {
347
+ function addEventListener(node: Element, name: string, handler:((e:Event)=>void), delegate:boolean): void {
294
348
  if (delegate) {
295
349
  if (isArray(handler)) {
296
350
  node[`$$${name}`] = handler[0];
@@ -298,35 +352,43 @@ function addEventListener(node:Node, name:any, handler:any, delegate:any):any {
298
352
  } else node[`$$${name}`] = handler;
299
353
  } else if (isArray(handler)) {
300
354
  const handlerFn = handler[0];
301
- node.addEventListener(name, (handler[0] = (e:any) => handlerFn.call(node, handler[1], e)));
355
+ node.addEventListener(name, (handler[0] = (e:Event) => handlerFn.call(node, handler[1], e)));
302
356
  } else node.addEventListener(name, handler);
303
357
  }
304
358
 
305
- function classList(node:Node, value:any, prev:any = {}):any {
359
+ function classList(
360
+ node: Element,
361
+ value: ClassListProps,
362
+ prev: ClassListProps = {}
363
+ ): ClassListProps {
306
364
  const classKeys = Object.keys(value || {}),
307
365
  prevKeys = Object.keys(prev);
308
366
  let i, len;
309
367
  for (i = 0, len = prevKeys.length; i < len; i++) {
310
368
  const key = prevKeys[i];
311
- if (!key || key === "undefined" || value[key]) continue;
369
+ if (!key || key === 'undefined' || value[key]) continue;
312
370
  toggleClassKey(node, key, false);
313
371
  delete prev[key];
314
372
  }
315
373
  for (i = 0, len = classKeys.length; i < len; i++) {
316
374
  const key = classKeys[i],
317
375
  classValue = !!value[key];
318
- if (!key || key === "undefined" || prev[key] === classValue || !classValue) continue;
376
+ if (!key || key === 'undefined' || prev[key] === classValue || !classValue) continue;
319
377
  toggleClassKey(node, key, true);
320
378
  prev[key] = classValue;
321
379
  }
322
380
  return prev;
323
381
  }
324
382
 
325
- function style(node:Node, value:any, prev:any) {
326
- if (!value) return prev ? setAttribute(node, "style") : value;
383
+ function style(
384
+ node: Element,
385
+ value: StyleProps,
386
+ prev: StyleProps
387
+ ): StyleProps {
388
+ if (!value) return prev ? setAttribute(node, 'style', undefined) : value;
327
389
  const nodeStyle = node.style;
328
- if (typeof value === "string") return (nodeStyle.cssText = value);
329
- if (typeof prev === "string") nodeStyle.cssText = prev = undefined
390
+ if (typeof value === 'string') return (nodeStyle.cssText = value);
391
+ if (typeof prev === 'string') nodeStyle.cssText = prev = undefined
330
392
  if (!prev) prev = {}
331
393
  if (!value) value = {}
332
394
  let v, s;
@@ -348,7 +410,7 @@ function toPropertyName(name:string):string {
348
410
  return name.toLowerCase().replace(/-([a-z])/g, (_:unknown, w:string) => w.toUpperCase());
349
411
  }
350
412
 
351
- function toggleClassKey(node:Node, key:string, value:boolean) {
413
+ function toggleClassKey(node: Element, key:string, value:boolean) {
352
414
  const classNames = key.trim().split(/\s+/)
353
415
  for (let i = 0, nameLen = classNames.length; i < nameLen; i++)
354
416
  node.classList.toggle(classNames[i], value)
@@ -360,7 +422,7 @@ function appendNodes(parent:Node, array:Node[], marker:null|Node = null) {
360
422
  }
361
423
 
362
424
  // Slightly modified version of: https://github.com/WebReflection/udomdiff/blob/master/index.js
363
- function reconcileArrays(parentNode:Node, a:Node[], b:Node[]) {
425
+ function reconcileArrays(parentNode:Node, a:Element[], b:Element[]) {
364
426
  const bLength = b.length
365
427
  let aEnd = a.length,
366
428
  bEnd = bLength,
@@ -1,11 +1,12 @@
1
1
  import {runtime} from '../src/runtime.ts'
2
2
  import {hyperscript} from '../src/hyperscript.ts'
3
3
  import {signal,root} from '../src/reactive.ts'
4
- import {JSDOM} from 'jsdom'
4
+ import { Window } from 'happy-dom'
5
5
  import {assertEquals} from '@std/assert'
6
6
 
7
- const {window} = new JSDOM('<!DOCTYPE html>', {runScripts:'dangerously'})
8
- const {document,MouseEvent} = window
7
+ const window = new Window
8
+ const document = window.document
9
+ globalThis = window
9
10
  const r = runtime(window), h = hyperscript(r)
10
11
 
11
12
  function testing(name, props, f=props) {
@@ -13,7 +14,7 @@ function testing(name, props, f=props) {
13
14
  let disposer
14
15
  return root(dispose => {
15
16
  disposer = () => {
16
- document.body.textContent = ''
17
+ window.document.body.textContent = ''
17
18
  r.clearDelegatedEvents()
18
19
  dispose()
19
20
  }
@@ -25,7 +26,7 @@ function testing(name, props, f=props) {
25
26
 
26
27
  function assertHTML(t, e, msg) { assertEquals(t().outerHTML, e, msg) }
27
28
 
28
- testing('h with basic element', {skip:true}, async test => {
29
+ testing('h with basic element', async test => {
29
30
  await test('empty tag', () => assertHTML(h(''), '<div></div>'))
30
31
  await test('tag with id', () => assertHTML(h('#a'), '<div id="a"></div>'))
31
32
  await test('tag with class', () => assertHTML(h('.a'), '<div class="a"></div>'))
@@ -63,7 +64,7 @@ function assertText(t, e, msg) { assertEquals(t(), e, msg) }
63
64
  // await test('signal fragment', () => assertText(h([()=>1]), '1'))
64
65
  // })
65
66
 
66
- testing('h with reactive content', {skip:true}, async test => {
67
+ testing('h with reactive content', async test => {
67
68
  await test('higher-order component', () => {
68
69
  const Hi = p => h('b',['Hi ',p.name]),
69
70
  name = signal('An'),
package/test/reactive.js CHANGED
@@ -82,7 +82,7 @@ describe('memo',{skip:true},() => {
82
82
  describe('memo with initial value',() => {})
83
83
  })
84
84
 
85
- describe('wrap',()=>{
85
+ describe('wrap', ()=>{
86
86
  describe('wrap singal of array', () => {
87
87
  const all = signal(['a','b']), first = wrap(all,0)
88
88
  assertEquals(first(),'a')
@@ -95,6 +95,7 @@ describe('wrap',()=>{
95
95
  assertEquals(name(),'a')
96
96
  assertEquals(name('A'),'A')
97
97
  assertEquals(name(),'A')
98
+ assertEquals(all(),{name:'A'})
98
99
  })
99
100
 
100
101
  describe('wrap singal of array of objects', () => {
@@ -104,9 +105,10 @@ describe('wrap',()=>{
104
105
  assertEquals(name(),'b')
105
106
  assertEquals(name('A'),'A')
106
107
  assertEquals(name(),'A')
108
+ assertEquals(all(),[{name:'A'}])
107
109
  })
108
110
 
109
- describe('wrap singal of object of arrays', () => {
111
+ describe('wrap singal of object with arrays', () => {
110
112
  const all = signal({ids:[0,1,2]}), ids = wrap(all,'id'), last = wrap(ids,-1)
111
113
  assertEquals(ids([1,2,3]),[1,2,3])
112
114
  assertEquals(ids(),[1,2,3])
@@ -114,4 +116,22 @@ describe('wrap',()=>{
114
116
  assertEquals(last(4),4)
115
117
  assertEquals(last(),4)
116
118
  })
119
+
120
+ describe('wrap singal of array of objects with array', () => {
121
+ const all = signal([{ids:[0,1,2]}]), first = wrap(all,0), ids = wrap(first,'ids'), last = wrap(ids,-1)
122
+ assertEquals(ids([1,2,3]),[1,2,3])
123
+ assertEquals(last(4),4)
124
+ assertEquals(all(),[{ids:[1,2,4]}])
125
+ })
126
+
127
+ describe('wrap singal of object with array of objects', () => {
128
+ const obj = signal({todos:[{done:false,name:'a'}, {done:false,name:'b'}]}), todos = wrap(obj, 'todos'), todo = wrap(todos, 0),
129
+ name = wrap(todo, 'name'), done = wrap(todo,'done')
130
+ assertEquals(done(true),true)
131
+ console.log('OBJ',obj())
132
+ effect(()=>{
133
+ name();done()
134
+ })
135
+ assertEquals(obj(),{todos:[{done:true,name:'a'},{done:false,name:'b'}]})
136
+ })
117
137
  })
package/test/types.ts ADDED
@@ -0,0 +1,44 @@
1
+ import { runtime } from '../src/runtime.ts'
2
+ import { hyperscript, type Mountable } from '../src/hyperscript.ts'
3
+ import { signal, root } from '../src/reactive.ts'
4
+ import { List } from '../src/controlflow.ts'
5
+ import { Window } from 'happy-dom'
6
+
7
+ const window = new Window
8
+ const r = runtime(window as any), h = hyperscript(r)
9
+
10
+ h('hr')
11
+ h('div', ['hello'])
12
+ h('', {class:''})
13
+
14
+ function CompReturningString() { return 'hi' }
15
+ function CompReturningNumber() { return 0 }
16
+ function CompReturningEmptyFragment() { return [] }
17
+ function CompReturningFragment() { return [h(CompReturningString), h(CompReturningNumber), '', 1] }
18
+
19
+ h(CompReturningString)
20
+ h(CompReturningNumber)
21
+ h(CompReturningEmptyFragment)
22
+ h(CompReturningFragment)
23
+
24
+ function CompWithProps(props: {a: string}) { return 'hi' }
25
+
26
+ h(CompWithProps, {a:''})
27
+
28
+ function CompWithChildren(props: {children:Mountable}) { return 'hi' }
29
+
30
+ h(CompWithChildren, [])
31
+ h(CompWithChildren, {}, [])
32
+ h(CompWithChildren, {children:[]})
33
+
34
+
35
+ function CompWithOptionalChildren(props: {children?:string[]}) { return 'hi' }
36
+
37
+ h(CompWithOptionalChildren)
38
+ h(CompWithOptionalChildren, [])
39
+ h(CompWithOptionalChildren, {})
40
+ h(CompWithOptionalChildren, {children:[]})
41
+
42
+ const booleans = signal([true, true, false])
43
+
44
+ // h(List, {each:()=>booleans}, (b, _i) => b)
package/src/components.js DELETED
@@ -1,20 +0,0 @@
1
- /**
2
- TODO
3
- @group Components
4
- */
5
- export function Input(props) {
6
- }
7
-
8
- /**
9
- TODO
10
- @group Components
11
- */
12
- export function Table(props) {
13
- }
14
-
15
- /**
16
- TODO
17
- @group Components
18
- */
19
- export function Canvas(props) {
20
- }
@@ -1,8 +0,0 @@
1
- import {h} from './h.ts'
2
-
3
- export type AccordionProps = {
4
- }
5
-
6
- export function Accordion(props:AccordionProps):undefined|any {
7
- return h('.accordion', {},'')
8
- }
package/src/ui/button.ts DELETED
@@ -1,9 +0,0 @@
1
- import {h} from './h.ts'
2
-
3
- export type ButtonProps = {
4
- children?: any[]
5
- }
6
-
7
- export function Button(props:AccordionProps) {
8
- return h('button', {type: 'button'}, props.children)
9
- }
package/src/ui/canvas.ts DELETED
@@ -1,8 +0,0 @@
1
- import {h} from './h.ts'
2
-
3
- export type CanvasProps = {
4
- }
5
-
6
- export function Canvas(props:CanvasProps) {
7
- return h('canvas')
8
- }
package/src/ui/date.ts DELETED
@@ -1,8 +0,0 @@
1
- import {h} from './h.ts'
2
-
3
- export type DateInputProps = {
4
- }
5
-
6
- export function DateInput(props:DateInputProps) {
7
- return h('input', {type: 'date'})
8
- }
package/src/ui/dialog.ts DELETED
@@ -1,12 +0,0 @@
1
- import { effect } from '../reactive.ts'
2
- import { h } from './h.ts'
3
-
4
- export function Dialog(props) {
5
- return h('dialog', {
6
- ref(r) {
7
- r.addEventListener('cancel', ()=>props.show(false))
8
- const show = props.modal ? ()=>r.showModal() : ()=>r.show()
9
- effect(() => props.show() ? show() : r.close())
10
- },
11
- }, props.children)
12
- }
package/src/ui/filter.ts DELETED
@@ -1,8 +0,0 @@
1
- import {h} from './h.ts'
2
-
3
- export type FilterProps = {
4
- }
5
-
6
- export function Filter(props:FilterProps) {
7
- return h('.filter')
8
- }
package/src/ui/form.ts DELETED
@@ -1,7 +0,0 @@
1
- import {h} from './h.ts'
2
-
3
- export type FormProps = {}
4
-
5
- export function Form(props:FormProps) {
6
- return h('form', props)
7
- }