/brisky

A lightning fast js library for building state driven user interfaces.

Primary LanguageJavaScript

brisky

Build Status js-standard-style npm version Coverage Status

Brisky is a lightning fast universal JS library for building state driven user interfaces.

It consist of multiple sub-modules, each module adding specific funcitonality

Find and create functional examples in our example repo.

Examples

Simple

First, let's start by displaying two DOM elements with hello and world as their content:

const render = require('brisky/render')
const s = require('vigour-state/s')

const state = s({})

const element = {
  container1: {
    text: 'Hello'
  },
  container2: {
    text: 'World!'
  }
}

document.body.appendChild(render(element, state))

Notice that the object containing the content can be named anything, as long as you camelCase it, just like a normal JavaScript object. In this example container1 and container2 is used.


State driven

Here we are setting the state object, containing hello and world.

const render = require('brisky/render')
const s = require('vigour-state/s')

const state = s({
  object: {
    hello: 'Hello',
    world: 'World!'
  }
})

const element = {
  $: 'object',
  container1: {
    text: {
      $: 'hello'
    }
  },
  container2: {
    text: {
      $: 'world'
    }
  }
}

document.body.appendChild(render(element, state))

Notice the $: notation. In the above example, we subscribe to object. This means that whenever object changes, our two DOM-elements are updated with the new content.

Also notice the nested nature of subscriptions. The two containers inside are scoped from within object, allowing them to grab hello and world directly.


tag

The tag field allows you to render normal DOM elements. By default, every object you render to the DOM is a div. You change this by defining a tag type, e.g. tag: 'section'.

const render = require('brisky/render')
const s = require('vigour-state/s')

const state = s({})

const element = {
  title: {
    tag: 'h1',
    text: 'I am a title'
  },
  paragraph: {
    tag: 'p',
    text: 'I am a paragraph'
  },
  canvas: {
    tag: 'canvas',
    text: 'I am a canvas',
    props: {
      id: 'canvas',
      width: '150',
      height: '150'
    }
  }
}

document.body.appendChild(render(element, state))

props

Extending from the example above, we have props. These allow you to set and manipulate the different attributes in a tag.

const render = require('brisky/render')
const s = require('vigour-state/s')

const state = s({
  canvas: {
    width: 150,
    height: 150
  }
})

const element = {
  canvas: {
    $: 'canvas',
    tag: 'canvas',
    text: 'I am a canvas',
    props: {
      id: 'canvas',
      width: { $: 'width' },
      height: { $: 'height' }
    }
  }
}

document.body.appendChild(render(element, state))

Modifying state

In this example we have an input field that overwrites state when enter is pressed. We are using brisky-events to make this happen.

const render = require('brisky/render')
const s = require('vigour-state/s')

const state = s({
  username: 'John Doe'
})

const element = {
  input: {
    tag: 'input',
    props: { placeholder: 'Enter username' },
    on: {
      enter: (e, stamp) => {
        e.state.root.set({ username: e.target.value }, stamp)
        e.target.value = '' // Reset input field
      }
    }
  }
}

document.body.appendChild(render(element, state))

Using normal JavaScript inside Brisky

In this example we have a DOM element with a 'current-time' class, displaying the time when in hours and minutes. Notice that a function can be hoisted outside the scope - this is just normal JS.

const render = require('brisky/render')
const s = require('vigour-state/s')

const state = s({})

const element = {
  currentTime: {
    class: 'current-time',
    text: {
      $transform: data => {
        const time = new Date().getTime();
        const hours = formatTime(time.getHours())
        const minutes = formatTime(time.getMinutes())

        return `${hours}:${minutes}`
      }
    }
  }
}

function formatTime (value) {
  return (value < 10 ? `0${value}` : value)
}

document.body.appendChild(render(element, state))

$transform

Using transform, you are able to take something from state, and manipulate your element based on it. In this example we have a counter that displays amount of todos left, counting the amount of todos in our state.

This is inherited from vigour-observable.

const render = require('brisky/render')
const s = require('vigour-state/s')

const state = s({
  todos: [
    { text: 'Finish todo' },
    { text: 'Rule the world' },
  ]
})

const element = {
  $: 'todos',
  counter: {
    text: {
      $: true,
      $transform: state => {
        var count = 0
        state.each(item => {
          count++
        })
        return `${count} items left`
      }
    }
  }
}

document.body.appendChild(render(element, state))

test

Good practice entails not rendering something that isn't needed for the user. Brisky facilitates this by giving you the test field.

Here we only render donaldsSecretBunker if the username is Donald

const render = require('brisky/render')
const s = require('vigour-state/s')

const state = s({
  username: 'Donald'
})

const element = {
  $: '$test',
  $test: state => {
    const username = state.root.username.compute()
    if (username === 'Donald') {
      return true
    }
    return false
  },
  donaldsSecretBunker: {
    contentInsideBobsBunker: {
      text: 'Secret message for Donald'
    }
  }
}

document.body.appendChild(render(element, state))

To extend from this, you can subscribe to test multiple things in the state. A normal use-case could be when you subscribe to something specific, like we do here with username:

const render = require('brisky/render')
const s = require('vigour-state/s')

const state = s({
  username: 'Donald',
  defconWarningLevel: '3'
})

const element = {
  donaldsSecretBunker: {
    $: 'username.$test',
    $test: {
      val: state => {
        const username = state.root.username.compute()
        if (username === 'Donald') {
          return true
        }
        return false
      },
      $: {
        $root: { defconWarningLevel: true }
      }
    },
    contentInsideBobsBunker: {
      text: 'Secret message for Donald'
    }
  }
}

document.body.appendChild(render(element, state))

switch

The switch simply switches content based on the value it is subscribing to. You can use the switch to change content within a component or on an application level, switching out whole pages.

const render = require('brisky/render')
const s = require('vigour-state/s')

const state = s({
  current: 'home'
})

const element = {
  $: 'current.$switch',
  $switch: state => state.compute() ? 'home' : 'profile',
  properties: {
    home,
    profile
  }
}

const home = {
  text: 'This is the default page'
}

const profile = {
  text: 'This is the profile page'
}

document.body.appendChild(render(element, state))

fragment

The fragment resembles the behavior of DocumentFragment. For an comparative example of document fragment implementation, see JavaScript DocumentFragment.

Using DocumentFragments is faster than repeated single DOM node injection and allows developers to perform DOM node operations (like adding events) on new elements instead of mass-injection via innerHTML. Keep DocumentFragment close by when performing lots of DOM operations -- it could speed up your app considerably!

const render = require('brisky/render')
const s = require('vigour-state/s')

const state = s({})

const element = {
  container: {
    tag: 'fragment'
  }
}

document.body.appendChild(render(element, state))