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

domorphic

v0.0.3

Published

pure js DOM templating

Downloads

2

Readme

domorphic

is a plain javascript program to interact with the DOM, in a functional, reactive and keep-it-simple philosophy.

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/dom.min.js"></script> 

Inspired by the power of d3 and the beauty of elm, this library attempts to breed their many respective qualities, to make shaping DOM interfaces within js enjoyable, smooth and pure.

Foreword

There are two layers to this library:

  • the first one is really just convenience for templating: pure js code which outputs DOM subtrees, regardless of anything else, so that you may very well be content with it.

  • the second one, much more subtle, is about designing stateful and reactive applications with pure monadic code.

Whether you've ever heard about monads or not, they're intuitive and powerful, though there's no need to worship their metaphysical nature to use the package :) For more documentation and examples, visit the project's website  !

Usage

Everything happens in the polymorphic type a -> Node of functions returning DOM nodes, which in scientific terms, is also called the category of types above Node. What?!

Above the DOM. A domorphic instance is just a function returning a DOM node. That's it!

let node = dom('h1').html('Hello World:!')();

At first glance, the library hence only helps you conveniently parametrise functions returning DOM nodes. And you may very well do document.body.appendChild or whatever you like with them.

Syntax. All view-related code is meant to be as light and readable as any other templating language: except it's javascript!

The dom constructor parses arguments looking for the following pattern:

dom('tag#id.class', ?{...attrs}, m ?-> [...branches])

Each branch may be given either as a dom instance, or as an array of arguments following the same pattern, so that you may nest arrays just as you would nest html tags.

Attributes. All node attributes are interpreted either as values or as data dependent functions.

You may supply them by d3-like chainable accessors:

let a = dom('a')
    .html("internet")
    .attr('href', m => m.href)
    .on('hover', () => alert("wooo"))

although the dom constructor also interprets the equivalent syntax:

let a = dom('a', {
    html: "internet",
    href: m => m.href,
    onhover: () => alert("wooo")),
});

Branches. Nodes are essentially trees of DOM elements, i.e. Node = (Element, [Node]).

The branching attribute itself may be given in a -> [Node], as a node array returning function:

let m = ['cats', 'are', 'cute'],

//  p : Str -> Node
let p = dom('p')
    .html(m => m);

//  div : [Str] -> Node
let div = dom('div')
    .branch(m => m.map(p));

document.body.appendChild(div(m));

and the above produces the same output as:

//  div : () -> Node
let div = dom('div', [
    ['p', {html: "cats"}],
    ['p', {html: "are"}],
    ['p', {html: "cute"}]
]);

document.body.appendChild(div());

Functors

In the category of types, a functor T:

  • assigns to every type a a type T a,
  • transforms any map a -> b to a map T a -> T b.

Because dom instances are pure functions, it is perfectly safe to pipe them into functorial transformations.

Pullbacks. Given a function a -> b, precompose a b -> Node instance to get an a -> Node map:

dom.pull : (a -> b) -> (b -> Node) -> (a -> Node)  

You're looking at the contravariant hom-functor Hom(-, Node) in the cartesian category of types :)

Arrays. Functions of type a -> Node are naturally transformed to [a] -> [Node] maps:

dom.map : (a -> Node) -> [a] -> [Node] 

As far as dom instances are pure functions, this is rigorously equivalent to calling Array.map.

Using dom.map will be mostly useful to associate index-specific DOM actions in the IO monad.

Records. Similarly, we map a -> Node to a type of functions on records {a} -> {Node}:

dom.rmap : (a -> Node) -> {a} -> {Node} 

This is not yet implemented, but there's little more to it than its array counterpart.

Monads

The main originality of domorphic is the monadic approach it takes to describe interactions between an internal state and the DOM state.

KISS: no automatic diff refreshes -- only handmade, pure pipelines.

IO. The IO monad describes input/output operations with the DOM.

IO(e) : IO operations, eventually triggering an event of type e.

State. The State monad describes stateful computations.

St(s, a) = s -> (a, s) : Computations with state in s, return value in a.

Updates. The State and IO monads yield a composed monadic type St(s, IO(e)).

Upon an event of type e, this monad describes how to update the internal state and which IO operations should take place:

update : e -> St(s, IO(e)) : Upon event, update state and trigger IO actions.

Binding then the update function to its return value just defines the main loop!

let main = (e0, s0) => {
    let [io, s1] = update(e0).run(s0);
    return io.bind(e1 => main(e1, s1));
}

let start = s0 => main('start', s0);