@wrnrlr/prelude 0.2.16 → 0.2.18
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/deno.jsonc +2 -2
- package/example/context.html +29 -0
- package/example/h.html +24 -0
- package/example/index.html +17 -4
- package/example/reset.css +505 -0
- package/example/style.css +27 -0
- package/example/table.html +25 -0
- package/example/vite.config.js +17 -0
- package/example/widget.html +112 -0
- package/package.json +1 -1
- package/readme.md +5 -1
- package/src/canvas.js +1 -1
- package/src/form.js +80 -7
- package/src/hyperscript.ts +184 -140
- package/src/mod.ts +2 -1
- package/src/reactive.ts +19 -18
- package/src/runtime.ts +3 -17
- package/test/hyperscript.js +4 -7
- package/src/select.js +0 -1
- /package/{src/osm.js → example/atlas.html} +0 -0
| @@ -0,0 +1,112 @@ | |
| 1 | 
            +
            <!DOCTYPE html>
         | 
| 2 | 
            +
            <head>
         | 
| 3 | 
            +
              <title>Widget</title>
         | 
| 4 | 
            +
              <script type="importmap">{"imports": {"prelude": "../src/mod.ts"}}</script>
         | 
| 5 | 
            +
              <link rel="stylesheet" href="./style.css"/>
         | 
| 6 | 
            +
            </head>
         | 
| 7 | 
            +
            <style>
         | 
| 8 | 
            +
            script,script[type="module"] {
         | 
| 9 | 
            +
              display: block;
         | 
| 10 | 
            +
            }
         | 
| 11 | 
            +
            </style>
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            <a href="/index.html">Home</a>
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            <h1>Widgets</h1>
         | 
| 16 | 
            +
            <ul>
         | 
| 17 | 
            +
              <li><a href="#dialog">Dialog</a></li>
         | 
| 18 | 
            +
              <li><a href="#input">Input</a></li>
         | 
| 19 | 
            +
              <li><a href="#checkbox">Checkbox</a></li>
         | 
| 20 | 
            +
              <li><a href="#select">Select</a></li>
         | 
| 21 | 
            +
            </ul>
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            <h2 id="dialog">Dialog</h2>
         | 
| 24 | 
            +
            <div class="example" id="dialog_example"></div>
         | 
| 25 | 
            +
            <details>
         | 
| 26 | 
            +
              <summary>Code</summary>
         | 
| 27 | 
            +
              <script type="module">
         | 
| 28 | 
            +
              import { h, signal, render, Dialog, useDialog } from '../src/mod.ts'
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              function HelloDialog(props) {
         | 
| 31 | 
            +
                let dialog
         | 
| 32 | 
            +
                return h('', {ref(e){dialog=e}}, [
         | 
| 33 | 
            +
                  'Hello',
         | 
| 34 | 
            +
                  h('button', {onClick:_=>dialog.close()}, 'Close')
         | 
| 35 | 
            +
                ])
         | 
| 36 | 
            +
              }
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              function MenuDialog(props) {
         | 
| 39 | 
            +
                let dialog
         | 
| 40 | 
            +
                return h('', {ref(e){dialog=e}}, [
         | 
| 41 | 
            +
                  'Hello',
         | 
| 42 | 
            +
                  h('button', {onClick:_=>dialog.close()}, 'Close')
         | 
| 43 | 
            +
                ])
         | 
| 44 | 
            +
              }
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              function DialogExample() {
         | 
| 47 | 
            +
                const showModal = signal(false)
         | 
| 48 | 
            +
                const showDialog = signal(false)
         | 
| 49 | 
            +
                return h('', {style:'display: flex; gap: 0.25rem'}, [
         | 
| 50 | 
            +
                  h('button', {onClick:e=>showModal(true)}, 'Modal'),
         | 
| 51 | 
            +
                  h(Dialog, {show:()=>showModal, modal: true}, h(HelloDialog)),
         | 
| 52 | 
            +
                  h('button', {onClick:e=>showDialog(true)}, 'Dialog'),
         | 
| 53 | 
            +
                  h(Dialog, {show:()=>showDialog}, h(MenuDialog)),
         | 
| 54 | 
            +
                ])
         | 
| 55 | 
            +
              }
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              render(DialogExample, document.getElementById('dialog_example'))
         | 
| 58 | 
            +
              </script>
         | 
| 59 | 
            +
            </details>
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            <h2 id="input">Input</h2>
         | 
| 62 | 
            +
            <div class="example" id="input_example"></div>
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            <h2>Input autosize</h2>
         | 
| 65 | 
            +
            <div class="example" id="input_autosize"></div>
         | 
| 66 | 
            +
            <script type="module">
         | 
| 67 | 
            +
            import { h, signal, render, Input } from '../src/mod.ts'
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            function InputExample() {
         | 
| 70 | 
            +
              const name = signal('Bob')
         | 
| 71 | 
            +
              return h(Input, {value:()=>name})
         | 
| 72 | 
            +
            }
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            render(InputExample, document.getElementById('input_autosize'))
         | 
| 75 | 
            +
            </script>
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            <h2 id="checkbox">Checkbox</h2>
         | 
| 78 | 
            +
            <div class="example" id="checkbox_example"></div>
         | 
| 79 | 
            +
            <script type="module">
         | 
| 80 | 
            +
            import { h, signal, render, Checkbox } from "../src/mod.ts"
         | 
| 81 | 
            +
             | 
| 82 | 
            +
            function CheckboxExample() {
         | 
| 83 | 
            +
              const valid = signal(false)
         | 
| 84 | 
            +
              return h(Checkbox, {value:()=>valid})
         | 
| 85 | 
            +
            }
         | 
| 86 | 
            +
             | 
| 87 | 
            +
            render(CheckboxExample, document.getElementById('checkbox_example'))
         | 
| 88 | 
            +
            </script>
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            <h2 id="select">Select</h2>
         | 
| 91 | 
            +
            <div class="example " id="select_car"></div>
         | 
| 92 | 
            +
            <script type="module">
         | 
| 93 | 
            +
            import { h, signal, memo, render, Select } from "../src/mod.ts"
         | 
| 94 | 
            +
             | 
| 95 | 
            +
            function SelectCar() {
         | 
| 96 | 
            +
              const cars = { 'Audi': ['A1','A6'], 'BMW': ['B5', 'BX'] }
         | 
| 97 | 
            +
              const companies = Object.keys(cars)
         | 
| 98 | 
            +
              const company = signal()
         | 
| 99 | 
            +
              const model = signal('')
         | 
| 100 | 
            +
              const models = memo(() => cars[company()] || null)
         | 
| 101 | 
            +
              return [
         | 
| 102 | 
            +
                '', h(Select, {options:companies, value:()=>company}),
         | 
| 103 | 
            +
                '', h(Select, {options:models, value:()=>model})
         | 
| 104 | 
            +
              ]
         | 
| 105 | 
            +
            }
         | 
| 106 | 
            +
             | 
| 107 | 
            +
            render(SelectCar, document.getElementById('select_car'))
         | 
| 108 | 
            +
            </script>
         | 
| 109 | 
            +
             | 
| 110 | 
            +
            <style>
         | 
| 111 | 
            +
             | 
| 112 | 
            +
            </style>
         | 
    
        package/package.json
    CHANGED
    
    
    
        package/readme.md
    CHANGED
    
    | @@ -157,7 +157,7 @@ const useCounter = () => useContext(CounterCtx) | |
| 157 157 | 
             
            function CounterProvider(props) {
         | 
| 158 158 | 
             
              const count = signal(0)
         | 
| 159 159 | 
             
              const increment = () => count(i=>i+1)
         | 
| 160 | 
            -
              return h(CounterCtx | 
| 160 | 
            +
              return h(CounterCtx, {value:[count,increment]}, props.children)
         | 
| 161 161 | 
             
            }
         | 
| 162 162 |  | 
| 163 163 | 
             
            function Counter() {
         | 
| @@ -227,3 +227,7 @@ deno task test | |
| 227 227 | 
             
            * [Homepage](https://wrnrlr.github.io/prelude)
         | 
| 228 228 | 
             
            * [NPM](https://www.npmjs.com/package/@wrnrlr/prelude)
         | 
| 229 229 | 
             
            * [JSR](https://jsr.io/@wrnrlr/prelude)
         | 
| 230 | 
            +
             | 
| 231 | 
            +
            ## More Links
         | 
| 232 | 
            +
             | 
| 233 | 
            +
            * [Fine-grained reactivity - A deep dive into SolidJS](https://github.com/everweij/solidjs-fine-grained-reactivity)
         | 
    
        package/src/canvas.js
    CHANGED
    
    
    
        package/src/form.js
    CHANGED
    
    | @@ -1,20 +1,93 @@ | |
| 1 | 
            -
            import {signal, context, useContext} from './reactive.ts'
         | 
| 1 | 
            +
            import { signal, memo, effect, context, useContext, renderEffect } from './reactive.ts'
         | 
| 2 | 
            +
            import { Show } from './show.ts'
         | 
| 3 | 
            +
            import { List } from './list.ts'
         | 
| 4 | 
            +
            import { h } from './hyperscript.ts'
         | 
| 5 | 
            +
            import { nbsp } from './constants.ts'
         | 
| 2 6 |  | 
| 3 7 | 
             
            const Ctx = context()
         | 
| 4 8 | 
             
            const useForm = () => useContext(Ctx)
         | 
| 5 9 |  | 
| 6 | 
            -
            function Form(props) {
         | 
| 10 | 
            +
            export function Form(props) {
         | 
| 7 11 | 
             
              return h('form', h(Ctx.Provider, {value:[props.value]}, props.children))
         | 
| 8 12 | 
             
            }
         | 
| 9 13 |  | 
| 10 | 
            -
            function Input(props) {
         | 
| 11 | 
            -
               | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
               | 
| 14 | 
            +
            export function Input(props) {
         | 
| 15 | 
            +
              props.onInput = ((e) => props.value((e?.target)?.value))
         | 
| 16 | 
            +
              props.autocomplete = props.autocomplete || 'off'
         | 
| 17 | 
            +
              // const onInput = ((e) => props.value((e?.target)?.value))
         | 
| 18 | 
            +
              if (props.autosize) props.ref = (r) => props.autosize(r, props)
         | 
| 15 19 | 
             
              return h('input', props)
         | 
| 16 20 | 
             
            }
         | 
| 17 21 |  | 
| 22 | 
            +
            export function autosize(r, props) {
         | 
| 23 | 
            +
              const style = globalThis.getComputedStyle(r)
         | 
| 24 | 
            +
              renderEffect(() => {
         | 
| 25 | 
            +
                const font = style.getPropertyValue('font')
         | 
| 26 | 
            +
                const metrics = fontMetrics(font, props.value().toString())
         | 
| 27 | 
            +
                r.style.width = metrics.width + 'px'
         | 
| 28 | 
            +
              })
         | 
| 29 | 
            +
            }
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            let _fontMetricsCtx
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            function fontMetrics(font, text) {
         | 
| 34 | 
            +
              if (!_fontMetricsCtx) _fontMetricsCtx = document.createElement('canvas').getContext('2d')
         | 
| 35 | 
            +
              _fontMetricsCtx.font = font;
         | 
| 36 | 
            +
              return _fontMetricsCtx.measureText(text)
         | 
| 37 | 
            +
            }
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            export function Checkbox(props) {
         | 
| 40 | 
            +
              return h('input',{
         | 
| 41 | 
            +
                ...props,
         | 
| 42 | 
            +
                type:'checkbox',
         | 
| 43 | 
            +
                checked:props.value,
         | 
| 44 | 
            +
                onInput:e => props.value(e.target.checked)
         | 
| 45 | 
            +
              })
         | 
| 46 | 
            +
            }
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            export function Select(props) {
         | 
| 49 | 
            +
              // const description = wrap(props.value,'description')
         | 
| 50 | 
            +
              const show = signal(false)
         | 
| 51 | 
            +
              const selected = memo(()=>{
         | 
| 52 | 
            +
                const options = typeof props.options === 'function' ? props.options() : props.options || []
         | 
| 53 | 
            +
                return {options}
         | 
| 54 | 
            +
              })
         | 
| 55 | 
            +
              const fallback = props.placeholder ? h('span', props.placeholder) : 'nbsp'
         | 
| 56 | 
            +
              return h('.select', [
         | 
| 57 | 
            +
                h('button', {onClick:e=>show(s=>!s)}, h(Show, {when:()=>props.value, fallback}, ()=>props.value() || nbsp)),
         | 
| 58 | 
            +
                h(Show, {when: show}, h('.options', {style:'position:absolute'}, h(List, {each:()=>selected().options},
         | 
| 59 | 
            +
                  (option) => h('.option', {
         | 
| 60 | 
            +
                    onClick: (_) => { props.value(option()); show(false) },
         | 
| 61 | 
            +
                    style: 'cursor: pointer'
         | 
| 62 | 
            +
                  }, option)
         | 
| 63 | 
            +
                )))
         | 
| 64 | 
            +
              ])
         | 
| 65 | 
            +
            }
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            const DialogCtx = context(null)
         | 
| 68 | 
            +
            export const useDialog = () => useContext(DialogCtx)
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            export function Dialog(props) {
         | 
| 71 | 
            +
              let dialog
         | 
| 72 | 
            +
              const close = () => dialog.close()
         | 
| 73 | 
            +
              const show = props.modal ? () => dialog.showModal() : () =>  dialog.show()
         | 
| 74 | 
            +
              return h('dialog', {
         | 
| 75 | 
            +
                ref(dia) {
         | 
| 76 | 
            +
                  dialog = dia
         | 
| 77 | 
            +
                  const closeHandler = (_) =>  props.show(false)
         | 
| 78 | 
            +
                  effect(() => props.show() && show())
         | 
| 79 | 
            +
                  dialog.addEventListener('close', closeHandler)
         | 
| 80 | 
            +
                  if (props.ref) props.ref(dialog)
         | 
| 81 | 
            +
                  return () => dialog.removeEventListener('close', closeHandler)
         | 
| 82 | 
            +
                }
         | 
| 83 | 
            +
              }, h(DialogCtx, {value:()=>[{close:close}]}, props.children))
         | 
| 84 | 
            +
            }
         | 
| 85 | 
            +
             | 
| 18 86 | 
             
            function CurrencyInput(props) {
         | 
| 19 87 | 
             
              return h(Input, props)
         | 
| 20 88 | 
             
            }
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            export function Button(props) {
         | 
| 91 | 
            +
              let checked
         | 
| 92 | 
            +
              return h('button', {}, props.children)
         | 
| 93 | 
            +
            }
         | 
    
        package/src/hyperscript.ts
    CHANGED
    
    | @@ -1,167 +1,211 @@ | |
| 1 1 | 
             
            import type {DOMElements} from './constants.ts'
         | 
| 2 | 
            -
            import type { | 
| 2 | 
            +
            import type {Mountable} from './runtime.ts'
         | 
| 3 3 | 
             
            import {r} from './runtime.ts'
         | 
| 4 4 |  | 
| 5 | 
            -
            const  | 
| 6 | 
            -
             | 
| 7 | 
            -
            // export type Mountable = View | HTMLElement | string | number | bigint | symbol | boolean | Date | Mountable[];
         | 
| 8 | 
            -
            export type Component<T> = ((props:T) => Mountable) | ((props:T) => Mountable[])
         | 
| 9 | 
            -
            export type Tag = typeof DOMElements extends Set<infer K> ? K : never;
         | 
| 10 | 
            -
            export type Child = { ():Child } | Element | Child[] | string | number | symbol | bigint | boolean | Date | Record<string,unknown> | {():Child, [ELEMENT]:boolean}
         | 
| 11 | 
            -
            export type View = {():void, [ELEMENT]?:boolean}
         | 
| 5 | 
            +
            // const document = globalThis.document
         | 
| 12 6 |  | 
| 13 7 | 
             
            export const h = hyperscript(r)
         | 
| 14 8 |  | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 9 | 
            +
            type MountableElement = Element | Document | ShadowRoot | DocumentFragment | Node
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            interface Runtime {
         | 
| 12 | 
            +
              insert(parent: MountableElement, accessor: any, marker?: Node | null, init?: any): any;
         | 
| 13 | 
            +
              spread(node: Element, accessor: any, isSVG?: Boolean, skipChildren?: Boolean): void;
         | 
| 14 | 
            +
              assign(node: Element, props: any, isSVG?: Boolean, skipChildren?: Boolean): void;
         | 
| 15 | 
            +
              component(Comp: (props: any) => any, props: any): any;
         | 
| 16 | 
            +
              SVGElements: Set<string>;
         | 
| 17 | 
            +
            }
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            const $ELEMENT = Symbol("hyper-element");
         | 
| 25 20 |  | 
| 26 | 
            -
             | 
| 21 | 
            +
            type ExpandableNode = Node & { [key: string]: any };
         | 
| 22 | 
            +
            type Props = { [key: string]: any };
         | 
| 27 23 |  | 
| 28 24 | 
             
            export type HyperScript = {
         | 
| 29 | 
            -
              ( | 
| 30 | 
            -
               | 
| 31 | 
            -
             | 
| 32 | 
            -
               | 
| 33 | 
            -
             | 
| 34 | 
            -
              (first: Component<Record<string,never>>): View
         | 
| 35 | 
            -
              <P extends Record<string, unknown>>(first: Component<P>, second: P): View
         | 
| 36 | 
            -
              <C>(first: Component<{children:C}>, second: C): View
         | 
| 37 | 
            -
              <P extends Record<string, unknown>, C>(first: Component<P & {children:C}>, second:P, third:C): View
         | 
| 38 | 
            -
            }
         | 
| 25 | 
            +
              (...args: any[]): () => ExpandableNode | ExpandableNode[];
         | 
| 26 | 
            +
              Fragment: (props: {
         | 
| 27 | 
            +
                children: (() => ExpandableNode) | (() => ExpandableNode)[];
         | 
| 28 | 
            +
              }) => ExpandableNode[];
         | 
| 29 | 
            +
            };
         | 
| 39 30 |  | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
              function item(e: Element, c: Child, m?: null): void {
         | 
| 48 | 
            -
                if (c===null) return
         | 
| 49 | 
            -
                const t = typeof c
         | 
| 50 | 
            -
                if (isArray(c))
         | 
| 51 | 
            -
                  for (const child of c)
         | 
| 52 | 
            -
                    item(e, child, m)
         | 
| 53 | 
            -
                else if (t === 'object' && r.isChild(c))
         | 
| 54 | 
            -
                  r.insert(e, c, m)
         | 
| 55 | 
            -
                else if (t === 'string')
         | 
| 56 | 
            -
                  e.appendChild(r.text((c as string)))
         | 
| 57 | 
            -
                else if (t === 'function') {
         | 
| 58 | 
            -
                  // while (c[ELEMENT]?.call) c = (c as any)()
         | 
| 59 | 
            -
                  r.insert(e, c, m)
         | 
| 60 | 
            -
                } else e.appendChild(r.text(c.toString()))
         | 
| 61 | 
            -
              }
         | 
| 31 | 
            +
            // Inspired by https://github.com/hyperhype/hyperscript
         | 
| 32 | 
            +
            export function hyperscript(r: Runtime): HyperScript {
         | 
| 33 | 
            +
              function h() {
         | 
| 34 | 
            +
                let args: any = [].slice.call(arguments),
         | 
| 35 | 
            +
                  e: ExpandableNode | undefined,
         | 
| 36 | 
            +
                  classes:string[] = [],
         | 
| 37 | 
            +
                  multiExpression = false;
         | 
| 62 38 |  | 
| 63 | 
            -
             | 
| 64 | 
            -
                 | 
| 65 | 
            -
                 | 
| 66 | 
            -
                 | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
                 | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
                 | 
| 75 | 
            -
                   | 
| 76 | 
            -
                   | 
| 39 | 
            +
                while (Array.isArray(args[0])) args = args[0];
         | 
| 40 | 
            +
                if (args[0][$ELEMENT]) args.unshift(h.Fragment);
         | 
| 41 | 
            +
                typeof args[0] === "string" && detectMultiExpression(args);
         | 
| 42 | 
            +
                const ret: (() => ExpandableNode) & { [$ELEMENT]?: boolean } = () => {
         | 
| 43 | 
            +
                  while (args.length) item(args.shift());
         | 
| 44 | 
            +
                  if (e instanceof globalThis.Element && classes.length) e.classList.add(...classes)
         | 
| 45 | 
            +
                  return e as ExpandableNode;
         | 
| 46 | 
            +
                };
         | 
| 47 | 
            +
                ret[$ELEMENT] = true;
         | 
| 48 | 
            +
                return ret;
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                function item(l: any) {
         | 
| 51 | 
            +
                  const type = typeof l;
         | 
| 52 | 
            +
                  if (l == null) void 0;
         | 
| 53 | 
            +
                  else if ("string" === type) {
         | 
| 54 | 
            +
                    if (!e) parseHtmlTag(l);
         | 
| 55 | 
            +
                    else e.appendChild(globalThis.document.createTextNode(l));
         | 
| 56 | 
            +
                  } else if (
         | 
| 57 | 
            +
                    "number" === type ||
         | 
| 58 | 
            +
                    "boolean" === type ||
         | 
| 59 | 
            +
                    "bigint" === type ||
         | 
| 60 | 
            +
                    "symbol" === type ||
         | 
| 61 | 
            +
                    l instanceof Date ||
         | 
| 62 | 
            +
                    l instanceof RegExp
         | 
| 63 | 
            +
                  ) {
         | 
| 64 | 
            +
                    (e as Node).appendChild(globalThis.document.createTextNode(l.toString()));
         | 
| 65 | 
            +
                  } else if (Array.isArray(l)) {
         | 
| 66 | 
            +
                    for (let i = 0; i < l.length; i++) item(l[i]);
         | 
| 67 | 
            +
                  } else if (l instanceof globalThis.Element) {
         | 
| 68 | 
            +
                    r.insert(e as globalThis.Element, l, multiExpression ? null : undefined);
         | 
| 69 | 
            +
                  } else if ("object" === type) {
         | 
| 70 | 
            +
                    let dynamic = false;
         | 
| 71 | 
            +
                    const d = Object.getOwnPropertyDescriptors(l);
         | 
| 72 | 
            +
                    for (const k in d) {
         | 
| 73 | 
            +
                      if (k === "class" && classes.length !== 0) {
         | 
| 74 | 
            +
                        console.log('classes',classes)
         | 
| 75 | 
            +
                        const fixedClasses = classes.join(" "),
         | 
| 76 | 
            +
                          value = typeof d["class"].value === "function" ?
         | 
| 77 | 
            +
                            ()=>[...classes,...(d["class"].value()??'').split(' ')].filter(c=>c).join(' ') :
         | 
| 78 | 
            +
                            [...classes,...(d["class"].value??'').split(' ')].filter(c=>c).join(' ')
         | 
| 79 | 
            +
                        Object.defineProperty(l,"class",{...d[k],value})
         | 
| 80 | 
            +
                        // classes = []
         | 
| 81 | 
            +
                      }
         | 
| 82 | 
            +
                      if (k !== "ref" && k.slice(0, 2) !== "on" && typeof d[k].value === "function") {
         | 
| 83 | 
            +
                        dynamicProperty(l, k);
         | 
| 84 | 
            +
                        dynamic = true;
         | 
| 85 | 
            +
                      } else if (d[k].get) dynamic = true;
         | 
| 86 | 
            +
                    }
         | 
| 87 | 
            +
                    dynamic
         | 
| 88 | 
            +
                      ? r.spread(e as globalThis.Element, l, !!args.length)
         | 
| 89 | 
            +
                      : r.assign(e as globalThis.Element, l, !!args.length);
         | 
| 90 | 
            +
                  } else if ("function" === type) {
         | 
| 91 | 
            +
                    if (!e) {
         | 
| 92 | 
            +
                      let props: Props | undefined,
         | 
| 93 | 
            +
                        next = args[0];
         | 
| 94 | 
            +
                      if (
         | 
| 95 | 
            +
                        next == null ||
         | 
| 96 | 
            +
                        (typeof next === "object" && !Array.isArray(next) && !(next instanceof globalThis.Element))
         | 
| 97 | 
            +
                      )
         | 
| 98 | 
            +
                        props = args.shift();
         | 
| 99 | 
            +
                      props || (props = {});
         | 
| 100 | 
            +
                      if (args.length) {
         | 
| 101 | 
            +
                        props.children = args.length > 1 ? args : args[0];
         | 
| 102 | 
            +
                      }
         | 
| 103 | 
            +
                      const d = Object.getOwnPropertyDescriptors(props);
         | 
| 104 | 
            +
                      for (const k in d) {
         | 
| 105 | 
            +
                        if (Array.isArray(d[k].value)) {
         | 
| 106 | 
            +
                          const list = d[k].value;
         | 
| 107 | 
            +
                          props[k] = () => {
         | 
| 108 | 
            +
                            for (let i = 0; i < list.length; i++) {
         | 
| 109 | 
            +
                              while (list[i][$ELEMENT]) list[i] = list[i]();
         | 
| 110 | 
            +
                            }
         | 
| 111 | 
            +
                            return list;
         | 
| 112 | 
            +
                          };
         | 
| 113 | 
            +
                          dynamicProperty(props, k);
         | 
| 114 | 
            +
                        } else if (typeof d[k].value === "function" && !d[k].value.length)
         | 
| 115 | 
            +
                          dynamicProperty(props, k);
         | 
| 116 | 
            +
                      }
         | 
| 117 | 
            +
                      e = r.component(l, props);
         | 
| 118 | 
            +
                      args = [];
         | 
| 119 | 
            +
                    } else {
         | 
| 120 | 
            +
                      while ((l as any)[$ELEMENT]) l = ((l as unknown) as () => ExpandableNode)();
         | 
| 121 | 
            +
                      r.insert(e as globalThis.Element, l, multiExpression ? null : undefined);
         | 
| 122 | 
            +
                    }
         | 
| 123 | 
            +
                  }
         | 
| 77 124 | 
             
                }
         | 
| 125 | 
            +
                function parseHtmlTag(s: string) {
         | 
| 126 | 
            +
                  let i:number
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                  i = s.indexOf('#')
         | 
| 129 | 
            +
                  if (i===-1) i = s.indexOf('.')
         | 
| 130 | 
            +
                  if (i===-1) i = s.length
         | 
| 131 | 
            +
                  const name = s.slice(0, i) || 'div'
         | 
| 132 | 
            +
                  e = r.SVGElements.has(name)
         | 
| 133 | 
            +
                    ? globalThis.document.createElementNS("http://www.w3.org/2000/svg", name)
         | 
| 134 | 
            +
                    : globalThis.document.createElement(name);
         | 
| 135 | 
            +
                  s = s.slice(i)
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                  if (s[0]==='#') {
         | 
| 138 | 
            +
                    i = s.indexOf('.')
         | 
| 139 | 
            +
                    if (i===-1) i = s.length
         | 
| 140 | 
            +
                    e!.setAttribute("id", s.slice(1, i))
         | 
| 141 | 
            +
                    s = s.slice(i)
         | 
| 142 | 
            +
                  }
         | 
| 78 143 |  | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
                  const e = r.element(tag.name)
         | 
| 85 | 
            -
                  const props2 = props as P & {class?: string|(()=>string)}
         | 
| 86 | 
            -
                  if (tag.id) e.setAttribute('id',tag.id)
         | 
| 87 | 
            -
                  if (tag.classes?.length) {
         | 
| 88 | 
            -
                    const cd = Object.getOwnPropertyDescriptor(props2,'class') ?? ({value:'',writable:true,enumerable:true});
         | 
| 89 | 
            -
                    props2.class = (cd.value?.call) ?
         | 
| 90 | 
            -
                      () => [...tag.classes,...(cd.value()??'').split(' ')].filter(c=>c).join(' ') :
         | 
| 91 | 
            -
                      [...tag.classes,...(cd.value??'').split(' ')].filter(c=>c).join(' ')
         | 
| 144 | 
            +
                  while(s[0]==='.') {
         | 
| 145 | 
            +
                    i = s.indexOf('.',1)
         | 
| 146 | 
            +
                    if (i===-1) i = s.length
         | 
| 147 | 
            +
                    classes.push(s.slice(1, i))
         | 
| 148 | 
            +
                    s = s.slice(i)
         | 
| 92 149 | 
             
                  }
         | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
                  const  | 
| 96 | 
            -
                   | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
                     | 
| 150 | 
            +
                }
         | 
| 151 | 
            +
                function parseClass(string: string) {
         | 
| 152 | 
            +
                  const m = string.split(/([\.#]?[^\s#.]+)/);
         | 
| 153 | 
            +
                  if (/^\.|#/.test(m[1])) e = globalThis.document.createElement("div");
         | 
| 154 | 
            +
                  for (let i = 0; i < m.length; i++) {
         | 
| 155 | 
            +
                    let v = m[i]
         | 
| 156 | 
            +
                    const s = v.substring(1, v.length);
         | 
| 157 | 
            +
                    if (!v) v = 'div';
         | 
| 158 | 
            +
                    if (!e)
         | 
| 159 | 
            +
                      e = r.SVGElements.has(v)
         | 
| 160 | 
            +
                        ? globalThis.document.createElementNS("http://www.w3.org/2000/svg", v)
         | 
| 161 | 
            +
                        : globalThis.document.createElement(v);
         | 
| 162 | 
            +
                    if (v[0] === ".") classes.push(s);
         | 
| 163 | 
            +
                    else if (v[0] === "#") e.setAttribute("id", s);
         | 
| 101 164 | 
             
                  }
         | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
                   | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
                      const list = d[k].value;
         | 
| 111 | 
            -
                      (props as Record<string, ()=>unknown>)[k] = () => {
         | 
| 112 | 
            -
                        for (let i = 0; i < list.length; i++)
         | 
| 113 | 
            -
                          while (list[i][ELEMENT]) list[i] = list[i]()
         | 
| 114 | 
            -
                        return list
         | 
| 115 | 
            -
                      }
         | 
| 116 | 
            -
                      dynamicProperty(props, k)
         | 
| 117 | 
            -
                    } else if (typeof d[k].value==='function' && !d[k].value.length) { // A function with zero arguments
         | 
| 118 | 
            -
                      dynamicProperty(props, k)
         | 
| 165 | 
            +
                }
         | 
| 166 | 
            +
                function detectMultiExpression(list: any[]) {
         | 
| 167 | 
            +
                  for (let i = 1; i < list.length; i++) {
         | 
| 168 | 
            +
                    if (typeof list[i] === "function") {
         | 
| 169 | 
            +
                      multiExpression = true;
         | 
| 170 | 
            +
                      return;
         | 
| 171 | 
            +
                    } else if (Array.isArray(list[i])) {
         | 
| 172 | 
            +
                      detectMultiExpression(list[i]);
         | 
| 119 173 | 
             
                    }
         | 
| 120 174 | 
             
                  }
         | 
| 121 | 
            -
                  const e = r.component(() => (first as Component<P>)(props))
         | 
| 122 | 
            -
                  ret = () => e
         | 
| 123 175 | 
             
                }
         | 
| 124 | 
            -
                ret[ELEMENT] = true
         | 
| 125 | 
            -
                return ret
         | 
| 126 176 | 
             
              }
         | 
| 127 | 
            -
            }
         | 
| 128 177 |  | 
| 129 | 
            -
             | 
| 130 | 
            -
               | 
| 131 | 
            -
              else if (!isArray(children)) return false
         | 
| 132 | 
            -
              for (const i of children) {
         | 
| 133 | 
            -
                if (typeof i === 'function') return true
         | 
| 134 | 
            -
                else if (isArray(i)) return detectMultiExpression(i)
         | 
| 135 | 
            -
              }
         | 
| 136 | 
            -
              return false
         | 
| 178 | 
            +
              h.Fragment = (props: any) => props.children;
         | 
| 179 | 
            +
              return h;
         | 
| 137 180 | 
             
            }
         | 
| 138 181 |  | 
| 182 | 
            +
             | 
| 139 183 | 
             
            // ^([a-zA-Z]\w*)?(#[a-zA-Z][-\w]*)?(.[a-zA-Z][-\w]*)*
         | 
| 140 | 
            -
            export function parseHtmlTag(s:Tag) {
         | 
| 141 | 
            -
             | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 144 | 
            -
              i = s.indexOf('#')
         | 
| 145 | 
            -
              if (i===-1) i = s.indexOf('.')
         | 
| 146 | 
            -
              if (i===-1) i = s.length
         | 
| 147 | 
            -
              const name = s.slice(0, i) || 'div'
         | 
| 148 | 
            -
              s = s.slice(i)
         | 
| 149 | 
            -
             | 
| 150 | 
            -
              if (s[0]==='#') {
         | 
| 151 | 
            -
                i = s.indexOf('.')
         | 
| 152 | 
            -
                if (i===-1) i = s.length
         | 
| 153 | 
            -
                id = s.slice(1, i)
         | 
| 154 | 
            -
                s = s.slice(i)
         | 
| 155 | 
            -
              }
         | 
| 184 | 
            +
            // export function parseHtmlTag(s:Tag) {
         | 
| 185 | 
            +
            //   const classes:string[] = [];
         | 
| 186 | 
            +
            //   let id:string|undefined = undefined, i:number
         | 
| 156 187 |  | 
| 157 | 
            -
             | 
| 158 | 
            -
             | 
| 159 | 
            -
             | 
| 160 | 
            -
             | 
| 161 | 
            -
             | 
| 162 | 
            -
             | 
| 163 | 
            -
             | 
| 164 | 
            -
             | 
| 188 | 
            +
            //   i = s.indexOf('#')
         | 
| 189 | 
            +
            //   if (i===-1) i = s.indexOf('.')
         | 
| 190 | 
            +
            //   if (i===-1) i = s.length
         | 
| 191 | 
            +
            //   const name = s.slice(0, i) || 'div'
         | 
| 192 | 
            +
            //   s = s.slice(i)
         | 
| 193 | 
            +
             | 
| 194 | 
            +
            //   if (s[0]==='#') {
         | 
| 195 | 
            +
            //     i = s.indexOf('.')
         | 
| 196 | 
            +
            //     if (i===-1) i = s.length
         | 
| 197 | 
            +
            //     id = s.slice(1, i)
         | 
| 198 | 
            +
            //     s = s.slice(i)
         | 
| 199 | 
            +
            //   }
         | 
| 200 | 
            +
             | 
| 201 | 
            +
            //   while(s[0]==='.') {
         | 
| 202 | 
            +
            //     i = s.indexOf('.',1)
         | 
| 203 | 
            +
            //     if (i===-1) i = s.length
         | 
| 204 | 
            +
            //     classes.push(s.slice(1, i))
         | 
| 205 | 
            +
            //     s = s.slice(i)
         | 
| 206 | 
            +
            //   }
         | 
| 207 | 
            +
            //   return {name:name as string,classes,id:id}
         | 
| 208 | 
            +
            // }
         | 
| 165 209 |  | 
| 166 210 | 
             
            function dynamicProperty<T>(props: Record<string, unknown>, key: string) {
         | 
| 167 211 | 
             
              const src = props[key] as ()=>unknown
         | 
    
        package/src/mod.ts
    CHANGED
    
    | @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            export type {Getter,Setter,Signal} from './reactive.ts'
         | 
| 2 | 
            -
            export {signal,effect,untrack,batch,memo,root,context,useContext,wrap,fuse,onMount,onCleanup} from './reactive.ts'
         | 
| 2 | 
            +
            export {signal,effect,untrack,batch,memo,root,context,useContext,wrap,fuse,onMount,onCleanup,renderEffect} from './reactive.ts'
         | 
| 3 3 | 
             
            export {nbsp} from './constants.ts'
         | 
| 4 4 | 
             
            export {List} from './list.ts'
         | 
| 5 5 | 
             
            export {Show} from './show.ts'
         | 
| @@ -10,6 +10,7 @@ export {HashRouter} from './router.js' | |
| 10 10 | 
             
            export {resource,makeAbortable,abortable} from './resource.js'
         | 
| 11 11 | 
             
            import {r} from './runtime.ts'
         | 
| 12 12 | 
             
            export const render = r.render
         | 
| 13 | 
            +
            export {Dialog,useDialog,Input,Checkbox,Select} from './form.js'
         | 
| 13 14 |  | 
| 14 15 | 
             
            // const r:Runtime = /*#__PURE__*/ (typeof window === 'object') ? runtime(window) : undefined as unknown as Runtime
         | 
| 15 16 |  | 
    
        package/src/reactive.ts
    CHANGED
    
    | @@ -59,13 +59,6 @@ export type Setter<T> = { | |
| 59 59 | 
             
              (value: T): T
         | 
| 60 60 | 
             
            }
         | 
| 61 61 |  | 
| 62 | 
            -
            export type Context<T> = {
         | 
| 63 | 
            -
              id: symbol,
         | 
| 64 | 
            -
              defaultValue: T,
         | 
| 65 | 
            -
              get(): T,
         | 
| 66 | 
            -
              set(value: T): void
         | 
| 67 | 
            -
            }
         | 
| 68 | 
            -
             | 
| 69 62 | 
             
            export type Options<T> = {
         | 
| 70 63 | 
             
              equals?: false | EqualsFn<T>
         | 
| 71 64 | 
             
            }
         | 
| @@ -297,22 +290,30 @@ export function root<T>(fn: RootFn<T>): T { | |
| 297 290 | 
             
              return new Root().wrap(fn)
         | 
| 298 291 | 
             
            }
         | 
| 299 292 |  | 
| 300 | 
            -
            export  | 
| 301 | 
            -
             | 
| 293 | 
            +
            export type Context<T> = {
         | 
| 294 | 
            +
              id: symbol,
         | 
| 295 | 
            +
              defaultValue: T,
         | 
| 296 | 
            +
              get(): T,
         | 
| 297 | 
            +
              set(value: T): void
         | 
| 298 | 
            +
            }
         | 
| 299 | 
            +
             | 
| 300 | 
            +
            export function context<T>(): Context<T | undefined>
         | 
| 301 | 
            +
            export function context<T>(defaultValue: T): Context<T>
         | 
| 302 302 | 
             
            export function context<T>(defaultValue?: T) {
         | 
| 303 303 | 
             
              const id = Symbol()
         | 
| 304 | 
            -
              const  | 
| 305 | 
            -
               | 
| 306 | 
            -
             | 
| 307 | 
            -
              const f = Object.assign((props:any) => {
         | 
| 308 | 
            -
                set(props.value)
         | 
| 304 | 
            +
              const s = {id, defaultValue}
         | 
| 305 | 
            +
              return Object.assign((props:any) => {
         | 
| 306 | 
            +
                OBSERVER?.set(id, props.value)
         | 
| 309 307 | 
             
                return () => props.children.call ? props.children() : props.children
         | 
| 310 | 
            -
              }, s)
         | 
| 311 | 
            -
             | 
| 308 | 
            +
              }, s) as unknown as Context<T>
         | 
| 309 | 
            +
            }
         | 
| 310 | 
            +
             | 
| 311 | 
            +
            export function useContext<T>(ctx: Context<T>): T {
         | 
| 312 | 
            +
              return OBSERVER?.get(ctx.id) ?? ctx.defaultValue
         | 
| 312 313 | 
             
            }
         | 
| 313 314 |  | 
| 314 | 
            -
            export function  | 
| 315 | 
            -
              return  | 
| 315 | 
            +
            export function renderEffect(fn:()=>void) {
         | 
| 316 | 
            +
              return globalThis.requestAnimationFrame(()=>effect(fn))
         | 
| 316 317 | 
             
            }
         | 
| 317 318 |  | 
| 318 319 | 
             
            export type S<T> = Getter<T> | Setter<T>
         |