Nocturnal Pumpkin Maelstrom

    spect

    23.1.0 • Public • Published

    spect

    Micro DOM aspects: pieces of logic declared with CSS rules.

    npm bundle size npm

    Run


    Spect provides 3 conventional DOM functions − $, h and v for dom aspects, reactive html and observable data.

    It attempts to follow the principles:

    💎 Separation of cross-cutting concerns.

    🌳 No native API wrapping − work directly with HTML tree, vanilla js.

    📲 Progressive enhancement-friendly.

    🐤 Familiarity − no entry barrier.

    💫 No tooling, no boilerplate code, no environment setup needed.

    :shipit: Low-profile - can be used as side-utility, no guesses about storage, actions, renderer, can be used with different flavors.

    Good performance / size.

    API

    spect/$

    $( container=document , selector , handler? )

    Observe selector within container, call handler when matching elements found. Handler can return teardown function. Returns live collection of matched elements (hypothetical SelectorCollection API).

    import $ from 'spect/$.js'
    
    let foos = $('.foo', el => {
      console.log('active')
      return () => console.log('inactive') // teardown
    })
    
    let foo = document.createElement('div')
    foo.className = 'foo'
    document.body.append(foo)
    // ... "active"
    
    foo.remove()
    // ... "inactive"
    
    foos[Symbol.dispose]() // destroy selector observer

    spect/h

    el = h`...content`

    HTML builder with HTM syntax and reactive fields support: Promise, Async Iterable, any Observable, RXjs, any observ* etc.

    import {h, v} from 'spect'
    
    const text = v('foo') // reactive value (== vue3 ref)
    const a = h`<a>${ text }</a>` // <a>foo</a>
    text.value = 'bar' // <a>bar</a>
    
    const frag = h`<x ...${{x: 1}}>1</x><y>2</y>`  // htm syntax
    h`<${a}>${ frag }</a>` // <a><x x="1">1</x><y>2</y></a>
    
    a[Symbol.dispose]() // destroy observers
    
    /* jsx h */
    const a2 = <a>{ rxSubject } or { asyncIterable } or { promise }</a>
    
    h(a, a2) // render/update

    spect/v

    ref = v( init? )

    Creates reactive mutable ref object with a single .value property holding internal value. ref is identical to vue3 ref with Observable and AsyncIterable interface.

    import v from 'spect/v.js'
    
    let count = v(0)
    count.value // 0
    
    count.subscribe(value => {
      console.log(value)
      return () => console.log('teardown', value)
    })
    count.value = 1
    // > 1
    count.value = 2
    // > "teardown" 1
    // > 2
    
    let double = count.map(value => value * 2) // create mapped ref
    double.value // 4
    
    count.value = 3
    double.value // 6
    
    let sum = v(count.value + double.value)
    count.subscribe(v => sum.value = v + double.value)
    double.subscribe(v => sum.value = count.value + v)
    
    // async iterable
    for await (const value of sum) console.log(value)
    
    sum[Symbol.dispose]() // destroy observations

    Examples

    Hello World
    <div class="user">Loading...</div>
    
    <script type="module">
      import { $, h, v } from 'spect.js'
    
      $('.user', async el => {
        // create user state
        const user = v({ name: 'guest' })
    
        // render element content, map user state
        h`<${el}>Hello, ${ user.map(u => u.name) }!</>`
    
        // load data & set user
        user.value = (await fetch('/user')).json()
      })
    </script>
    Timer
    <time id="timer"></time>
    
    <script type="module">
      import { $, v, h } from 'spect.js'
    
      $('#timer', timer => {
        const count = v(0), id = setInterval(() => count.value++, 1000)
        h`<${timer}>${ count }</>`
        return () => clearInterval(id)
      })
    </script>
    Counter
    <output id="count">0</output>
    <button id="inc">+</button><button id="dec">-</button>
    
    <script type="module">
      import { $, h, v } from 'spect.js'
    
      const count = v(0)
      $('#count', el => count.subscribe(c => el.value = c))
      $('#inc', el => el.onclick = e => count.value++)
      $('#dec', el => el.onclick = e => count.value--)
    </script>
    Todo list
    <form class="todo">
      <label for="add-todo">
        <span>Add Todo</span>
        <input name="text" required>
      </label>
      <button type="submit">Add</button>
      <ul class="todo-list"><ul>
    </form>
    
    <script type="module">
      import { $, h, v } from 'spect.js'
    
      const todos = v([])
      $('.todo-list', el => h`<${el}>${
        todos.map(items =>
          items.map(item => h`<li>${ item.text }</li>`)
        )
      }</>`)
      $('.todo-form', form => form.addEventListener('submit', e => {
        e.preventDefault()
        if (!form.checkValidity()) return
    
        todos.value = [...todos.value, { text: form.text.value }]
    
        form.reset()
      }))
    </script>
    Form validator
    <form></form>
    
    <script type="module">
      import { $, h, v } from 'spect.js'
    
      const isValidEmail = s => /.+@.+\..+/i.test(s)
    
      $('form', form => {
        const valid = v(false)
        h`<${form}>
          <label for="email">Please enter an email address:</label>
          <input#email onchange=${ e => valid.value = isValidEmail(e.target.value) }/>
          The address is ${ v(valid, b => b ? "valid" : "invalid") }
        </>`
      })
    </script>
    Prompt
    <script>
    import {v,h} from 'spect.js'
    
    const showPrompt = v(false), proceed = v(false)
    
    document.body.appendChild(h`<dialog open=${showPrompt}>
      Proceed?
      <menu>
        <button onclick=${e => (showPrompt.value = false, proceed.value = false)}>Cancel</button>
        <button onclick=${e => (showPrompt.value = false, proceed.value = true)}>Confirm</button>
      </menu>
    </>`)
    </script>

    See all examples.

    License

    MIT

    Install

    npm i spect

    DownloadsWeekly Downloads

    577

    Version

    23.1.0

    License

    MIT

    Unpacked Size

    145 kB

    Total Files

    15

    Last publish

    Collaborators

    • dy