Thanks to visit codestin.com
Credit goes to github.com

Skip to content

dy/spect

Repository files navigation

spect

Reactive aspect-oriented web-framework.

npm bundle size npm

Run


Spect is minimalistic aspect-oriented web framework with smooth DX, enabling compact UI code and efficient DOM manipulations with 3 essential functions − $, h and v, successors of jquery, hyperscript and observable.

💎 Separation of cross-cutting concerns with aspects in CSS-like style.

🌳 Native first − semantic clean tree, vanilla js.

📲 Organic progressive enhancement.

🐤 Low entry barrier.

💫 0 bundling, 0 server, 0 boilerplate.

:shipit: Low-profile − can be used as utility.

⛳ Reasonable performance / size balance.

Installation

A. Directly as a module:

<script type="module">
import { $, h, v } from 'https://unpkg.com/spect?module'
</script>

Available from CDN: unpkg, pika.

B. As dependency from npm:

npm i spect

import { $, h, v } from 'spect'

Spect plays well with snowpack, but any other bundler will do.

Examples

Hello World

<div class="user">Loading...</div>

<script type="module">
  import { $, h, v } from 'spect'

  $('.user', async el => {
    // create user state
    const user = v({})

    // render element content, map user state
    h`<${el}>Hello, ${ user.map(u => u.name || 'guest') }!</>`

    // load data & set user
    user((await fetch('/user')).json())
  })
</script>

Timer

<time id="timer"></time>

<script type="module">
  import { $, v, h } from 'spect'

  $('#timer', timer => {
    const count = v(0),
      id = setInterval(() => count(c => c + 1), 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'

  const count = v(0)
  $('#count', el => count.subscribe(c => el.value = c))
  $('#inc', el => el.onclick = e => count(c => c+1))
  $('#dec', el => el.onclick = e => count(c => c-1))
</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'

  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

    // push data, update state
    todos().push({ text: form.text.value })
    todos(todos())

    form.reset()
  }))
</script>

Form validator

<form></form>

<script type="module">
  import { $, h, v } from 'spect'

  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(isValidEmail(e.target.value)) }/>
      The address is ${ valid.map(b => b ? "valid" : "invalid") }
    </>`
  })
</script>

See all examples.

API

$ − selector aspect

$( scope? , selector , aspect? )

Observe selector, trigger aspect callback for elements matching the selector.

  • selector is a valid CSS selector.
  • scope is optional HTMLElement or a list of elements to narrow down observation scope.
  • aspect is a function with (element) => teardown? signature.
import { $, v, h } from 'spect'

let foos = $('.foo', el => {
  console.log('active')
  return () => console.log('inactive')
})

let foo = h`<div.foo/>`
document.body.append(foo)
// ... "active"

foo.remove()
// ... "inactive"

// dispose
foos[Symbol.dispose]()

h − hyperscript / html

el = h`...content`

Hyperscript with observables. Can be used as template literal with htm syntax or as JSX.

import { h, v } from 'spect'

const text = v('foo')

// create <baz>
const foo = h`<a>${ text }</a>`
foo // <a>foo</a>

// update content
text('bar')
foo // <a>bar</a>

// fragment
const frag = h`<a>1</a><a>2</a>`

// hydrate
h`<${foo} ...${props}>${ children }</>`

// observables
h`<a>${ rxSubject } - ${ asyncIterable } - ${ promise }</a>`

/* jsx h */
const bar = <a>{ text }</a>

// dispose
bar[Symbol.dispose]()

v − value observable

value = v( init ? )

Simple observable state, tiny replacement for useState. Creates a getter/setter function with observable interface.

import { v } from 'spect'

let v1 = v(0)

// get
v1() // 0

// set
v1(1)

// subscribe
v1.subscribe(value => {
  console.log(value)
  return () => console.log('teardown', value)
})

// transform
let v2 = v(v1).map(v1 => v1 * 2)
v2() // 2

// initialize
let v3 = v(() => 3)
v3() // 3

// set with fn
v3(v => v + 1)
v3() // 4

// async iterator
for await (const value of v3) console.log(value)

// dispose
v3[Symbol.dispose]()

R&D

Existing solutions for the functions were considered:

Spect has long story of research, at v13.0 it had repository reset. See changelog.

Related

  • element-props − unified access to element props with observable support. Comes handy for organizing components.
  • strui − collection of UI streams, such as router, storage etc. Comes handy for building complex reactive web-apps (spect, rxjs etc).

License

MIT

About

Observable selectors in DOM

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •