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

jackle

v0.0.1

Published

🦊 Minimal and experimental redux-like web framework

Downloads

5

Readme

Jackle

Travis CI badge Codeclimate maintainability Greenkeeper badge Bundlephobia minified badge Bundlephobia minified zipped badge

Jackle is a tiny and experimental framework for building redux-like web applications. It exposes a small API for managing components, state changes, routing and more!

Getting Started

Install with yarn or npm​

yarn add jackle

Import and start using

import { Jackle } from 'jackle';
​
const jackle = new Jackle();
jackle.parser([...]);
jackle.handler([...]);
jackle.component([...]);
jackle.route([...]);

API

Additional documentation and guides can be found in the github wiki.

Overview

In Jackle there are a few core modules, Parser, Handler, Component, all modules follow a simple Object structure and allow for a lot of flexiblity in regards to how they're composed.

I'm (8eecf0d2) currently learning about Redux and wanted to build a small framework similar to Jagwah but with a more minimal approach and redux-like ideas. I'm not completely sold on the immutable wave but it seems interesting enough to give it a crack.

If you get lost or are confused about how Jackle works it's highly recommended to read the source, Jackle is less than 200 lines of verbose and commented code.

Parser

A Parser is similar to an Action in redux, it's used to format input and which can be used by a handler.

To register a parser use jackle.parser(parser: Jackle.parser|Jackle.parser[]).

The example below expects an input element to be passed in and it returns that element's value.

const update_temp_parser: Jackle.parser = {
  name: 'update:temp',
  parser: (input: HTMLInputElement) => {
    return input.value;
  }
}

jackle.parser(update_temp_parser);

Handler

A Handler is similar to a Reducer in redux, it's purpose is to change the state of the application, it recieves data from a parser and decides what needs to change in the state.

To register a handler use jackle.handler(handler: Jackle.handler|Jackle.handler[])

The example below recieves the return value from the input element Parser above, and returns a modified state.

const update_temp_handler: Jackle.handler = {
  name: 'update:temp',
  handlers: [
    (state: State, text: string) => {
      state.temp = {
        text: text
      }
      return state;
    }
  ]
}

jackle.handler(update_temp_handler);

Handlers can be async and you can provide an array of functions to simplify duties, each handler must return a state object.

The example below first checks that the text argument is at least 10 characters long, before continuing and modifying the state.

If a Handler throws an error the chain will stop.

export const update_temp_handler: Jackle.handler = {
  name: 'update:temp',
  handlers: [
    (state, text: string) => {
      if(text.length < 10) {
        throw new Error('Text must be at least 10 characters long')
      }
      return state;
    },
    (state, text: string) => {
      state.temp = {
        text: text
      }
      return state;
    }
  ]
}

jackle.handler(update_temp_handler);

Change

The change method in Jackle is similar to Dispatch in redux, it's used to attempt changes the state - you can access it directly from a Jackle instance with jackle.change(), or from the state with state.change().

To call the change method use jackle.change() or state.change().

If you call the change method and a handler throws an error, the error will bubble to the caller - so be ready to catch them.

The example below calls the update:temp example from above.

jackle.change('update:temp', document.querySelector('input'));
// this calls the update:temp parser
// which is then passed into the update:temp handler

Component

Components are a used to create html, you must provide a selector value which will be used to bind to an element on the dom with document.querySelector(selector).

To register a component use jackle.component(component: Jackle.component|Jackle.component[]).

The Component template property will be called every time the state changes, the template property can be async and will be given three arguments, state, html and controller.

The state argument is a frozen (Object.freeze()) copy of the state, this is because all changes to the state should be performed by calling the change method. The html argument is a hyperhtml.BoundTemplateFunction<HTMLElement> function, which is more or less a string -> html function, learn more here. The controller argument will only be provided if there was a controller defined for the component.

The example below will use document.querySelector() to find an element matching #form and then output the tagged template literal result to the DOM.

export const form_component: Jackle.component = {
  selector: '#form',
  template: async (state, html) => {
    html`
      <form>
        <input type="text" value=${state.temp.text}>
      </form>
    `;
  }
}
<body>
  <div id="form">
    <!-- <form> element will be placed here -->
  </div>
</body>

That example is pretty straight forward, to take it further we can bind the input element's value with the state, to do this we can use the change() method when the input element value changes. To implement this we can set an event handler for the input elements onchange or oninput events.

export const form_component: Jackle.component = {
  selector: '#form',
  template: async (state, html) => {
    html`
      <form>
        <input type="text" oninput=${(event: Event) => state.change('update:temp', e.target)} value=${state.temp.text}>
      </form>
    `;
  }
}

However, writing long lambdas within element attributes can get hard to read pretty quickly, so we can write a simple controller for handling these interactions.

export const form_component: Jackle.component = {
  selector: '#form',
  controller: (state) => {
    return {
      oninput: (event: Event) => {
        state.change('update:temp', event.target);
      }
    }
  },
  template: async (state, html, controller) => {
    html`
      <form>
        <input type="text" oninput=${controller.oninput} value=${state.temp.text}>
      </form>
    `;
  }
}

Personally I'm a fan of keeping file counts to a minimum but since Components are simple objects you can move the controller and template properties into separate files and change the structure to suit your needs.

components/form/form.component.ts

import { controller } from './form.controller';
import { template } from './form.template';

export const form_component: Jackle.component = {
  selector: '#form',
  controller: controller,
  template: template,
}

components/form/form.controller.ts

export const controller: Jackle.component.controller = (state) => {
  return {
    oninput: (event: Event) => {
      state.change('update:temp', event.target);
    }
  }
}

components/form/form.template.ts

export const template: Jackle.component.template = async (state, html, controller) => {
  html`
    <form>
      <input type="text" oninput=${controller.oninput} value=${state.temp.text}>
    </form>
  `;
}

Doing this does mean you'll lose the easily inferred types, so you'll need to wire them out separately.

You can also take the idea of components much further by also mixing in hyperHTML components.