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

stateful-dom

v1.1.5

Published

A stateful & reactive dom library for building vanilla js/ts applications using component patterns

Downloads

19

Readme

Stateful DOM

Introduction

A tiny stateful & reactive DOM wrapper library to build vanilla JavaScript or TypeScript front-end applications using modern component based patterns. Try in CodeSandbox

Installation

npm i stateful-dom

Usage

  • Create a vanilla js/ts project. We recomment using Vite
  • Install stateful-dom package using command npm i stateful-dom
  • import router object from the package
  • router.render() is the entry point of the application
  • Refer package API for more detailed usage

Creating app in index.js (with single route)

import {Lib,router} from 'stateful-dom';

const app = () => {
   const template = `<div> App is working </div>`;
   return Lib.createComponent(template);
}
router.render(app);

Creating app in index.js (with multiple routes)

import {Lib,router} from 'stateful-dom';

const page1 = () => {
   const template = `<div> Page1 is working </div>`;
   return Lib.createComponent(template);
}
const page2 = () => {
   const template = `<div> Page2 is working </div>`;
   return Lib.createComponent(template);
}

const page3 = () => {
 const template = `<div> Page3 is working </div>`;
 return Lib.createComponent(template);
}

const notFound = () => {
 const template = `<div> 404 ! route not found </div>`;
 return Lib.createComponent(template);
}

const app = [
   {path:"/",root: page1},
   //page 2 will be redirected to page3 , for this use 'redirectTo' option like below
   {path:"/page2",root: page2, redirectTo: "/page3"},
   {path:"/page3",root: page3},
   {path:"*",root: notFound} // for handling routes that don't exist, add path as "*"
]

router.render(app);

Creating a component

import {Lib} from 'stateful-dom';

export const myButton = (label) => {
   const template = `<button id='my-btn'> ${label} </button>`;
   return Lib.createComponent(template);
}

Club multiple components to form single component

import {Lib} from 'stateful-dom';

export const myButton = (label) => {
   const template = `<button id='my-btn'> ${label} </button>`;
   return Lib.createComponent(template);
}
export const display = (text) => {
   const template = `<div> ${text} </text>`;
   return Lib.createComponent(template);
}

export const buttonWithDisplay = () => {
   return Lib.clubComponents(
   	[
   		display("click this") , // this array of componets will be rendered in exact order
   		myButton("add"), 
   		myButton("substract")
   	])
}

Handle event listeners

import {Lib,events} from 'stateful-dom';

//make sure you DON'T add events inside component function (myButton()) 
events.add('#my-btn','click',() => { 
   console.log("myButton is clicked");
});

export const myButton = (label) => {
   const template = `<button id='my-btn'> ${label} </button>`;
   return Lib.createComponent(template);
}

Handle preloading (page-loading events that occur at initial loading of page)

import {Lib,events} from 'stateful-dom';
//best for preloading async data by calling apis
//make sure you DON'T add events inside component function (myButton()) 
events.preload(() => { 
   console.log("This will be preloaded");
});

export const myButton = (label) => {
   const template = `<button id='my-btn'> ${label} </button>`;
   return Lib.createComponent(template);
}

Add DOM attributes to a component

import {Lib,events} from 'stateful-dom';

//props is an object!
//Lib.spreadAttributes(props) can be used to add - 
//attributes in props object to any dom node

export const myButton = (label,props) => {
   const template = `<button ${Lib.spreadAttributes(props)} id='my-btn'> ${label} </button>`;
   return Lib.createComponent(template);
}

Render list with adding events

import {Lib,events} from 'stateful-dom';
import {myComponent} from "./myComponent";

const list = ["Banana","Apple","Kiwi"].map(item=>{
   return myComponent(item);
});

export const fruitsListComponent = () => {
   return Lib.createListNode(list);
}

Conditional rendering

import {myButton} from "./myButton";

export const myComponent = (loading) => {

   const template = `<div> ${loading ? 'Loading...' : myButton("Click").innerHTML} </div>`;
   
   return Lib.createComponent(template);
}

Compose component templates

import {myButton} from "./myButton";
import {display} from "./display";

export const myComponent = () => {
   const template = `<div> 
   	${myButton("Click").innerHTML}
   	${display("Some text").innerHTML} 
</div>`;
   return Lib.createComponent(template);
}

Navigate to a route

import {Lib,events,router} from 'stateful-dom';

//make sure you DON'T add events inside component function (myButton()) 
events.add('#my-btn','click',() => { 
   router.navigate("/home"); //navigate method doesn't reload the page
});

export const myButton = (label) => {
   const template = `<button id='my-btn'> ${label} </button>`;
   return Lib.createComponent(template);
}

Handle form data

import  {  Lib,  events  }  from  "../../lib"
import  {  $  }  from  "../../main" //global state exported from main file
import  './cart.css'

events.add('.checkout-btn','click',  ()  =>  {
   const  name  =  Lib.getFormItem('#checkout-name').value //get input value
   const  address  =  Lib.getFormItem('#checkout-address').value
   $.setState('userData',{name,address,cartItems:  $.state.cart}) //update state
})

export  const  cart  =  ()  =>  {
   const  template  =  `
   	<form>
   		<input  type='text'  id='checkout-name'  placeholder='your name'/>
   		<input  type='text'  id='checkout-address'  placeholder='Address'/>
   		<button  type='button'  class='checkout-btn'>Checkout and Pay</button>
   	</form>
   `
   const element = Lib.createComponent(template)
   return Lib.clubComponents([element],{class:'cart-checkout'})
}

State

The state of the application or component can be managed using the State class. We can create object of State class providing some initial state of the application. On the state object we get state variable to access the state and a setter function called setState , which expects the key and value parameters of state variable that needs to be updated. On the setState call , the state is updated and the The entire current route UI is re-rendered! setState calls are expected mostly in event handlers. Avoid setting state in component functions as it may cause infinite render loop!

import {State} from 'stateful-dom';

export const _ = new State({user: "" }) // this state can be imported into any file of app
console.log(_.state.user)

API

Lib

|Method|Parameters|Remarks| |----------|-------|-------| |createComponent()|template: string, name?:string| name is the parent node ex. 'div' |clubComponents()|components:(Element or null)[], attributes?: object,parent?:string | used to club multiple components to form a single component. the order of 'components' array is maintained in the dom| |createListNode()| list: (Element or null)[], parent: string = 'div'| creates a single list node from array of Elements (Nodes)| |spreadAttributes() | attributes?: IComponentProps,omits: string[] = [] | the names of attributes in 'omits' array will be ignored| |getFormItem()| selector: string | returns HTMLFormElement to handle form input values etc.|

Events

|Method|Parameters|Remarks| |------|--|--| |add()| selector:string,event:string, callback:(...args: any[]) => void | to handle broweser events. 'selector' should be valid html selector ex. '#app2', '.class2'|

State

|Method|Parameters|Remarks| |------|--|--| |state| Not a method| used to access the state |setState()|key: string, value: any | used to update state| |new State()|initialState?: {[key: string]: any},logger?: string| 'logger' is a string name to log state object to console|

Known issues

  • When any state is updated, the entire UI of current route is re-rendered. This is bit performance heavy.
  • Each time the dom is re-rendered , we need to add all the registered event listeners again :(

ToDos

  • Child routing
  • link() component development
  • handling changes/effects
  • Solve event handling issues