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

rilti

v0.10.1

Published

small opinionated furture forward frontend framework

Downloads

3

Readme

💫 rilti.js 💫

a small flavorful and unapologetic library built for the modern web

JavaScript Style Guide

Build Status

currently in beta phase and potentially subject to breaking changes

Feel free to fork or raise issues. Constructive criticism is always welcome

  • 🐫 Loadbearing Spirit - Expressive DOM generation and custom-element components sans polyfill
  • 🐱 Lion - Simple and fearless element/component data-binding
  • 👶 Child - Proxy enhanced DOM manipulation and Powerful all accepting async Rendering System

features

  • elm-like ideas about architecture
  • flexible declarative programming style
  • node lifecycle hooks
  • observe attributes
  • generate all your dynamic/interactive/transient elements
  • program without concern for page load state
  • components aka. custom-elements. No polyfill needed!
  • vue-like directives aka custom attributes
  • great dom manipulation functions
  • composition & currying
  • powerful emitter system (pub/sub with proxy magic)
  • no classes, no this, no extra fuzz, functional positive
  • no old javascript, we use modern features like Proxy
  • a gziped rilti.min.js weighs > 7kb

To use rilti just download /dist/rilti.min.js and pop it in a script tag. If you have any issues just tell me, I'm on it.

Install

yarn add rilti

npm i rilti

or

<script src="https://unpkg.com/rilti"></script>

The Dao of Rilti

  1. Nominalism Good | Explicit Paradigms, Patterns & Universalism Bad
    • More than one way of doing things, what is right/best is always contextual.
    • No Explicit Object Orientation anything (no classes or this)
    • simple functions and objects before imposing rigid structures
    • Reserve identities only for things that would be otherwise obscure
    • Don't hide things, let them be what they are
    • Nothing for the sake of itself, no postmodern sollutions.
    • Logic is just data (with potential) so pass it around too
    • Factory-Functions always
  2. Mess With the DOM, it's alive
    • Selectors & Templates? No, treat HTML as Mutable Object Holons
    • Plain HTML is for simple websites, where state is not as complex
    • DOM generation and components are for Apps and the modern interactive Web.
  3. MASA: Minimal API Surface Area.
    • Polymorphic functions/functions-as-class-like-objects
    • Infer Info with Good API Design before creating 12 parameter long functions.
    • Use configurable Objects create dynamic APIs
    • Some APIs can often be reduced to a fn e.g. Get: fn(key), Set: fn(key, val), Del: fn(key, nil)
    • As Functional As Possible
    • Perspectivism vs Pragmatism, people won't always use an API the same way.
    • Leave some internals or lower level API features accessible for extendibility

Different strokes for different folks: Also look at rilti.on which can be used like this on['any-event'](node, func, =options), as well as like this on('any-event', node)(func, =options)
and also on(node, { click: e => {} }, =options).

examples of rilti used to build things

future plans

  • offer collection of useful optional plugins
  • stabalize features and release
  • expand with a UI library

Example time!

Click Counting Button

const { component, componentReady, dom: { h1 }} = rilti

component('counter-button', {
  bind: {
    count: {
      key: 'clicks:innerText',
      val: 0,
      views: {
        clicks: count => `clicks: ${count}`
      }
    }
  },
  on: {
    click: (e, el) => ++el.count
  }
})

componentReady('body > counter-button', el => {
  const tellEm = h1['tell-em'](`You just won't stop clicking huh?`)
  el.$count.on.change(count => {
    if (count > 20 && count < 40 && !tellEm.mounted) tellEm.render('body')
    else if (count > 40 && count < 100) tellEm.txt += ' Seriously? '
    else if (count > 100) tellEm.txt = 'What? You want a prize or something?'
  })
})

Two Button Counter

const {databind, dom: {button, div, h1}} = rilti

div.multicounter({
  render: 'body', // <- apend to <body> on load
  bind: {
    count: {
      val: 0,
      views: {
        display: count => `current count is at: ${count}`
      }
    }
  }
},
  host => [ // <com-po-nents> bind natively, other elements bind to el.bind['$bind/bindValue']
    h1(
      host.bind.$count.text('display')
    ),
    button({onclick: e => ++host.bind.count}, '+'),
    button({onclick: e => --host.bind.count}, '-')
  ]
)

Pointer tracker

rilti.dom.div.pointer_tracker({
  $: 'body',
  css: {width: '300px', height: '300px'},
  bind: {
    pointer: {
      key: 'location:innerText',
      val: {x: 0, y: 0},
      views: {
        location: ({x, y}) => `Pointer is at (${x}x, ${y}y)`
      },
      change: ({x, y}) => ({x: x.toFixed(2), y: y.toFixed(2)})
    }
  },
  onpointermove ({x, y}, el) {
    el.bind.pointer = {x, y}
  }
})

the above produces this:

<div class="pointer-tracker" style="width: 300px; height: 300px;">
  Pointer is at (0x, 0y)
</div>

Declaratively Generate a Site Navbar

Stop writing html (yes JSX too)! Just generate everything, it's so simple.

const {a, button, h1, header, nav, section} = rilti.dom

section.navbar({$: 'body'}, // <- $ is shorthand for render: 'Node/Selector'
  header(h1('My Wicked Website')),
  nav(
    'Home Blog About Contact'.split(' ').map(name =>
      a['nav-bn']({href: '#/' + name.toLowerCase()}, name)
    ),
    a['nav-bn']({
      css: {backgroundColor: 'white', color: 'dimgrey'},
      href: 'https://github.com/SaulDoesCode',
      target: '_blank'
    },
      'Github'
    )
  )
)

The above produces this html

<section class="navbar">
  <header>
    <h1>My Wicked Website</h1>
  </header>
  <nav>
    <a class="nv-btn" href="#/home">
      <button>Home</button>
    </a>
    <a class="nv-btn" href="#/blog">
      <button>Blog</button>
    </a>
    <a class="nv-btn" href="#/about">
      <button>About</button>
    </a>
    <a class="nv-btn" href="#/contact">
      <button>Contact</button>
    </a>
    <a class="nv-btn" href="https://github.com/SaulDoesCode/rilti.js" target="_blank" style="background-color: dimgrey; color: white;">
      <button>Github</button>
    </a>
  </nav>
</section>

API

| method | description | |--------|--------------| | .dom(tag, =options, ...children) | where the magic happens, define behavior for elements and see them come to life | | .dom["any-tag"](=options, ...children) | pre-bound tag version of .dom | | .query(string, Selector/Node) | improved alternative to document.querySelector| | .queryAll(string, Selector/Node) | improved alternative to document.querySelectorAll| | .queryAsync(string, func, Selector/Node) | same as querySelector but async, good for pre-load logic | | .queryEach(string, Selector/Node, func) | queries nodes returned by selector and iterates over them like .forEach would| | .dom.frag(=string) | create a fragment or convert html text to nodes | | .on(target, type, listener, =options) | generates event listener | | .once(target, type, listener, =options) | generates event listener that triggers only once | | .curry(func, =argsLimit) | curries a function | | .component(tag, {create, mount, unmount, attr, props, methods}) | define custom elements, no polyfills needed | | .each(iterable, func) | loop through objects, numbers, array(like)s, sets, maps... | | .extend(hostObj, obj, =safeMode) | extends host object with all props of other object, won't overwrite if safe is true | | .flatten(arraylike) | flattens multidimensional arraylike objects | | .render(AlmostAnything, Selector/Node, =connector = 'appendChild') | renders things, independent of ready state | | .run(func) | asynchronously executes a given function when the DOM is loaded | | .runAsync(func, ...args) | run a function asynchronously | | .isMounted(node, =parentNode) | determines whether or not the dom or other node contains a specific node |

rilti also exports a couple of useful Type-Testing functions

usage : rilti.isX( {any} ) // -> boolean
isArr, isNil, isDef, isObj, isFunc, isBool, isStr, isNum, isArrlike, isNodeList, isNode, isPrimitive, isPromise, isRenderable, isRegExp, isInput, isEmpty, isEl

DOM manipulation

rilti contains a domfn that contains several useful dom manipulation functions. these fucntions will all return the node passed as the first argument unless specified otherwise such as with has/get(this/that) type functions

const {
  attach,
  css, // (node, stylePropery, val) || (node, { styleProp:'4em' }) set element.style properties
  class, // (node, class, =state) add/remove or toggle classes
  hasClass, // (node, class) -> bool
  attr, // (node, attrNameOrObj, =value): attr(el, 'href', '/') or attr(el, 'href') -> '/'
  removeAttribute, // (node, ...attrNames) removes attributes
  hasAttr, // hasAttr(node, attrName) -> bool
  attrToggle, // (node, attrName, state = !hasAttr, =val = getAttr(name) || '')
  emit, // (node, {type string/Event/CustomEvent}) dispatchEvents on node
  append, prepend, appendTo, prependTo, // (node, selectorOrNode)
  remove, // (node, =afterMS) // remove node or remove after timeout
} = rilti.domfn

everything found in rilti.domfn will be available as:
rilti.$(Node).domfnMethod(...args)

const contentCard = async (src, hidden = false) => {
  const card = rilti.dom.div.card() // <- <div class="card"></div>
  
  card.class({hidden}) // add class .hidden if hidden === true
  card.class.hidden = hidden // <- this works too

  card.attr['aria-hidden'] = hidden // set attribute

  card.css({
    '--custom-theme': 'hsl(331, 70%, 48%)',
    borderTop: '2px solid var(--custom-theme, --theme-color)'
  })

  card.prependTo('.content-list')

  try {
    const res = await fetch(src)
    card.append(await res.text())

    card.on.click((e, card) => {
      card.once.animationend(() => {
        card.class('flip-animation', false)
      })
      card.class('flip-animation', true)
    })
  } catch (e) {
    card.remove()
    console.error('could not load content from: ' + src)
  }
}

Create Elements with Any Tag

dom['any-arbitrary-tag'](=options, ...children) -> Node/Element

dom['random-tag'] // <- <random-tag class="with random chainable classes">
.with
.random
['chainable']
.classes({
  // render to dom using selectors or nodes
  render: '.main > header',
  // add attributes
  attr: {contenteditable: true},
  // set classes
  class: 'card active',
  // or
  class: ['card', 'active']
  // or conditionally
  class: {
    card: true,
    active: false // active won't be added
  },
  // some styles?
  css: {
    boxShadow: '0 2px 6px rgba(0,0,0,.12)',
    '--highlight-color': 'crimson',
  // ^- oh yeah, css variables work too
  },
  // attach properties to the element
  props: {
    oldtxt: '',
    // create property get/set traps
    accesors: {
      contents: {
        get: el => el.txt,
        set (el, val) { el.txt = val.trim() }
      },
      // or as one function
      innerds (el, val) {
        if (val == null) return el.txt
        el.txt = val.trim()
      }
    },
    // plain getter/setters work too
    get ye_old_txt () { return this.innerText },
    set ye_old_txt (val) { this.innerText = val.trim() }
  },
  methods: {
    // el will be pre-bound upon execution
    // think of it like self in python classes
    // or rust struct methods
    warn (el, ...args) {
      el.oldtxt = el.txt
      el.contents = 'Sure you want to remove random-tag?'
    },
    reset (el) { el.contents = el.oldtxt }
  },
  // listen for events
  on: {
    click (evt, {warn}) { warn() },
    mouseout (evt, {reset}) { reset() }
  },
  // if there's just one listener then use:
  // once/onxevent: fn instead of once: { evt: fn }
  oncedblclick (evt, el) { el.remove() },
  // manage the element's lifecycle
  cycle: {
    create (el) { /*...*/ },
    mount (el) { /*...*/ },
    unmount (el) { /*...*/ },
    remount (el) { /*...*/ }
  }
},
  ...children // [], "", =>, Node, NodeList : should all render
)

Directives / Custom Attributes

// observe attributes with vue-like directives
rilti.directive('custom-attr', {
  init (element, value) { ... },
  update (element, value, oldValue) { ... },
  remove (element, value) { ... }
})
// revoke a directive
rilti.directives.delete('custom-attr')

Web Components / Custom Elements, no polyfills needed

rilti.component('tick-box', {
  props: {
    accessors: {
      ticked: {
        get: el => el.attr.has('ticked'),
        set: (el, state) => el.attrToggle('ticked', !!state)
      }
    }
  },
  create (el) {
    el.on.click(e => el.attrToggle('ticked'))
    el.css({
      display: 'block',
      width: '20px',
      height: '20px',
      margin: '5px auto',
      cursor: 'pointer',
      border: `1px solid ${el.ticked ? 'white' : 'dimgrey'}`,
      backgroundColor: el.ticked ? 'dimgrey' : 'white'
    })
  },
  mount (el) {
    console.log('tick-box mounted to ', el.parent)
  },
  unmount (el) {
    console.log('unmounted: tick-box is no more :(')
  },
  attr: {
    ticked: {
      update (el) {
        el.css({
          backgroundColor: 'dimgrey',
          border: `1px solid #fff`
        })
        el.emit('ticked', true)
      },
      remove (el) {
        el.css({
          backgroundColor: '#fff',
          border: `1px solid dimgrey`
        })
        el.emit('ticked', false)
      }
    }
  }
})

weight

  • unminified: 45.9kb
  • minified: 18.9kb
  • minified && compressed: 7.21kb

license = MIT