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

olive-spa

v4.3.121

Published

Tiny vanilla-based SPA framework.

Downloads

74

Readme

olive 🍸

A vanillaJS based declarative DOM API.

OliveJS is finally released! You can install it as an npm package:

> npm i olive-spa

OliveJS is a lightweight vanilla SPA framework. Docs still a WIP.

Declaring a Component

import { html } from 'olive-spa'

//declare a component as a simple function. This produces a single empty main element.
const MyComponent = () => 
  html().main()
  
//there's nothing stopping you from defining a static component, notice it's not a function.
//this will be in memory indefinitely, but it will always refer to the same DOM object.
const MyStaticComponent = 
  html().div()

Declare Components Using Fluent API Syntax

olive was designed to use a fluent API everywhere that HTML would be expected. It allows us to use any element name as a dot operator function, and most attributes as extensions. Plus bunches other helper functions too, like css for example:

//we can use any element and most attributes via dot operator:
const Square = (size, color) => 
    html()
        .div()
        .css({
            height: `${size}px`,
            width: `${size}px`,
            backgroundColor: `${color}`
        })

Map to Elements

Like other front-end libraries, olive has an element repeater function.


const myData = [ {name: 'Ross'}, {name: 'Kay'} ]

const DataConsumer = () =>
    html() //maps over 
        .each(myData, (hx, {name}) => hx.h1().text(name))

//You could also use repeat:

const SimpleRepeater = () => 
    html()
        .repeat(3, hx => hx.h1().text('hello!'))
/*
<h1>hello!</h1>
<h1>hello!</h1>
<h1>hello!</h1>
*/

Prevent Unecessary Re-Renders with Procedural Target Tracking

olive keeps track of its target elements procedurally. That means until you declare another element, all of the extensions you apply will affect only the current target. This allows us to keep chaining extension methods until we want to declare a new element. We also have control over nesting using the open and close functions. close can typically be omitted unless you want to have multiple nested trees.

//olive procedurally tracks the target dom element upon construction
//this component doesn't update, so it will never re-render any of it's parts unless replaced by parent updates.
const AnotherComp = (myTitle) => 
    html()
        .main()
        .nest()                 //<- current target is the main element, open() nests next elems
            .article()
            .nest()             //<- current target is the article element
                .h1()
                .text(myTitle)  //<- current target is the h1 elem

//AnotherComp('Hello')
//
//<main>
//  <article>
//      <h1>Hello</h1>
//  </article>
//</main>

Precise Reactivity

olive can handle reactivity too, and with pinpoint accuracy. olive will only run updates on the specific target elements subscribe is called upon

//we can make any target element reactive by using the `.subscribe()` function combined with a state store.
import { html, customDispatcher } from 'olive-spa'

const CHANGE_COLOR = 'Change Color'

const ChangeButton = () => 
    html()
        .button()
        .text("Click Me.")
        .on('click', hx => hx.dispatch(CHANGE_COLOR, 'red'))
        .subscribe({ //model is passed as second param to subscriptions                
            [CHANGE_COLOR]: (hx, color) =>
            hx.css({ color })
        })

const App = () =>
    html()
        .div()
        .class('app-container')
        .use(customDispatcher({id: 'AppDispatcher'}))
        .concat(ChangeButton())

Composition-Forward by Design

We can use olive's concat method to compose our components:

//connect your components together arbitrarily
const App = () =>
    html()
        .concat(Square(100, 'blue'))
        .concat(ChangeButton())

Routing Support

olive supports history-API based routing.

import { navigate } from 'olive-spa'

const HOME     = '/home',
      CONTACT  = '/contact',
      ABOUT    = '/about',
      PROJECTS = '/projects'
      
const Home = () => 
  html()
    //...
  
const Contact = () => 
  html()
    //...
  
const About = () => 
  html()
    //...
  
const Projects = () => 
  html()
    //...
      
const app = () => 
  html()
    .router({
      '/':         Home,
      [HOME]:      Home,
      [CONTACT]:   Contact,
      [ABOUT]:     About,
      [PROJECTS]:  Projects,
    })

navigate(HOME, /*...args*/) //=> replace router outlet w component registered to HOME

Isolating Side Effects

Olive was designed with functional purity in mind. So I included the ability to write complex middlewares for olive stores. go to https://js.plainenglish.io/understanding-redux-through-implementation-pt-2-20707b3ef3f5 to read about how middleware works in olive. The store I'm writing about is the literal implementation olive uses. Here's a simple devlogger middleware:

const devLogger = (model, action) => {
    const [k, data] = action
    console.log(`Processing action [${k}]${data ? `with data: ${JSON.stringify(data, null, 2)}` : '.'}`)
    return action
}

//use it as a middleware in your dispatcher to get logs of actions as they pass through.
const App = () =>
    html()
        .div().class('app-container')
        .use(customDispatcher({id: 'MyDispatcher', mw: devLogger}))
        .concat(SomeOtherComponent())