npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

vdtree

v0.9.0

Published

Write your components once; Use them in vanilla JS, React, Svelte, SSR and more

Downloads

719

Readme

vdtree

Write your components once; Use them in vanilla JS, React, Svelte, SSR and more

Why vdtree?

  • You want to build a web component library targeting various frameworks
  • JSX support
  • Translate states to target framework
  • Strong types (made using Typescript)

Table of Contents

Installation

Install vdtree from npm

npm i vdtree

or

yarn add vdtree

Then you may have to install one of the target packages like react, svelte, etc.

# vanilla JS (no framework):
npm i vdtree-dom

# React
npm i vdtree-react

# Svelte
npm i vdtree-svelte

Typescript Note: Typescript currently has issues with .tsx templates with vdtree. You should put your jsx templates in .jsx files to skip type checking.

Quick-Start Tutorials

Hello World

Let's quickly create a simple abstract DOM tree and render it on the browser

To create the abstract DOM tree using JSX:

/** @jsx h */
import {h} from 'vdtree'

let HelloWorld = <div>Hello, World!</div>

or the non-JSX version:

import {h} from 'vdtree'

let HelloWorld = h('div', {}, 'Hello, World!')

Then targeting various frameworks:

Vanilla Javascript (no framework):

import {renderToDom} from 'vdtree-dom'
renderToDom(HelloWorld, document.body)

React:

import {ReactWrapper} from 'vdtree-react'
ReactDOM.render(<ReactWrapper dom={HelloWorld}/>, document.body)

Svelte:

<script>
    import {SvelteWrapper} from 'vdtree-svelte'
</script>

<SvelteWrapper dom={HelloWorld}/>

Check out vdtree-plugins repository for various framework targets.

Greeter

An abstract greeter component is a pure function that accepts name as a prop and greets with that name.

/** @jsx h */
import {h} from 'vdtree'

const AbstractGreeter =
    props => <div>Hello, {props.name}</div>

or in non-JSX:

const AbstractGreeter =
    props => h('div', {}, 'Hello, ', props.name)

Then to render that component,

// Vanilla JS:
renderToDom(<AbstractGreeter name="Vanilla-JS" />, document.body)

// React:
<ReactWrapper dom={AbstractGreeter} props={{name: 'React'}} />

// Svelte:
<SvelteWrapper dom={AbstractGreeter} props={{name: 'React'}} />

Counter

An abstract counter component using withState(initialState, state => components) method:

/** @jsx h */
import {h, withState} from 'vdtree'

const Counter = withState(0, count =>
    <div>
        <div>{count.get()}</div>
        <button onclick={e => count.update(c => c + 1)}>+</button>
    </div>)

Getting Started

Use the h() method (signifying "hyperscript") to create an abstract DOM tree.

h(tag, attributes, children)

For example, to create a virtual <div />

import {h} from 'vdtree'

const comp = h('div')

To create the <div> with attributes and children,

h('div', {class: 'container'},
    h('div', {}, 'item1'),
    h('div', {}, 'item2')
)

which is an equivalent of:

<div class="container">
    <div>item1</div>
    <div>item2</div>
</div>

Styles

You can pass style as a string or as a javascript object

<p style="color: red; border-color: green"></p>

would be the same as

<p style={{color: 'red', borderColor: 'green'}}></p>

Note: Both will work in React using the ReactWrapper wrapper. See React

Event Handlers

You can use event handlers, as you would, using the JS DOM APIs

<div onclick={e => alert('Clicked!')}></div>

<form onsubmit={e => e.preventDefault()}>
    <input placeholder="User name" id="userName" />
    <input type="password" required={true} id="userName" />
    <input type="submit" value="Login" />
</form>

All valid DOM events can be used. See.

You can mix vanilla events style and react style event handler names:

// Both are valid
onclick={...}
onClick={...}

Components With Props

Lazy evaluated components can be used in the abstract DOM.

A simple component with props:

const Greeter =
    ({name = ''}) => <div>Hello, {name}</div>

or as a full-blown function:

function Greeter({name = ''}) {
    return <div>Hello, {name}</div>
}

Such components can also be included in the virtual DOM tree as:

<div>
    Greetings output
    <Greeter name="John" />
    <hr />
</div>

State

Use withState() method to create an abstract component with internal state.

withState(initialStateValue, state => componentTree)

Use

  • state.get() method to read values
  • state.update(s => newState) to update state
  • state.set(newState) method to write values.
  • state.mutate(s => mutation) to mutate big state trees.
  • state.bind() to utilize two-way binding in input elements.

An abstract counter component could look like:

export const AbstractCounter = withState(0, count =>
    <div>
        <div>{count.get()}</div>
        <button onclick={e => count.update(c => c + 1)}>+</button>
    </div>
)

And a reset button in the above counter could look like:

<button onclick={e => count.set(0)}>Reset</button>

Upon rendering the above component

  • When targeting Vanilla JS, a built-in state handling will be generated.
  • When targeting react, the state will be changed to hooks (const [count, setCount] = useState(0))
  • When targeting svelte, a run-time state handling will be generated.
  • States are not supported by SSR

Two-way data binding is also supported. Use myState.bind() as

export const AbstractGreeter = withState('', name =>
    <div>
        <input value={name.bind()} placeholder="Name" />
        <div>Hello, {name.get()}</div>
    </div>
)

// binding with custom property expression
const initialState = {name: '', email: '', isCompany: false}
export const AbstractContact = withState(initialState, state =>
    <div>
        <input value={state.bind(s => s.name)} />
        <input value={state.bind(s => s.email)} type="email" />
        <label>
            <input type="checkbox" checked={state.bind(s => s.isCompany)} /> Is Company
        </label>
    </div>
)

You can also provide custom two-way binding by providing a getter and setter for the input as state.bind(gettter, setter)

// Assuming initial state is { items: [] }

<input value={state.bind(
    s => s.items.find(i => i.id == 1).name,
    (s, val) => s.mutate(prevState => prevState.items.find(i => i.id == 1).name = val)
)} />

State mutations are also supported through mutate() method. This can be useful when the state tree is big and mutation would rather be easier.

state.mutate(prev => prev.items[1].name = '')
state.mutate(prev => prev.items.push({name: ''}))

You can also derive a read-only state from another one:

// a, b, c and d are derived from the state of coefficients {c1, c2, c3}
const AbstractQuadraticSolver = withState({c1: '0', c2: '0', c3: '0'}, coef => {
        const a = parseFloat(coef.get().c1)
        const b = parseFloat(coef.get().c2)
        const c = parseFloat(coef.get().c3)
        const d = b*b - 4*a*c
        return <div>
            <input value={coef.bind(i => i.c1)} placeholder="A" type="number"/> X<sup>2</sup> +
            <input value={coef.bind(i => i.c2)} placeholder="B" type="number"/> X +
            <input value={coef.bind(i => i.c3)} placeholder="C" type="number"/> = 0
            <div>
                {d < 0
                    ? 'No solution'
                    : <div>
                        X1 = {(- b + Math.sqrt(d)) / (2*a)},
                        X2 = {(- b - Math.sqrt(d)) / (2*a)}
                    </div>
                }
            </div>
        </div>
    }
)

Rendering to the browser DOM

See vdtree-dom plugin.

React

See vdtree-react plugin.

Server-Side Rendered (SSR) HTML

See vdtree-ssr plugin.

Svelte

See vdtree-svelte plugin.

License

ISC License