Skip to content

vigetlabs/washi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

92 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

washi

A small (2.2kb gzipped), Backbone-inspired, library for greater organization on small projects.

  1. Installation
  2. Basic Usage
  3. API

Installation

package.json

Washi is available on npm:

npm install --save washi

global

If npm isn't your thing, or you'd rather include Washi as vendor code, download it from the unpkg CDN:

https://unpkg.com/washi

This is a UMD module. It should interop perfectly with CommonJS and AMD, or otherwise as a global by accessing Washi.

Basic usage

var Washi = require('washi');
var $     = Washi.$;

var Sample = {
  ui: {
    title: '.title',
  },
  events: {
    // Use ui helper selectors as aliases with {element}
    'mousedown {title}, touchstart {title}' : 'doSomething',

    // Alternatively, follow the syntax used by Backbone.Marionette
    'click @ui.title': 'doSomethingElse'
  },
  initialize: function(options) {
    this.ui.title.forEach(function(e) {
      e.innerHTML = 'Washi is for Origami'
    });
  },
  doSomething: function() {
    var text = $.map(this.ui.title, el => el.innerHTML).join(' ')
    console.log(text)
  },
  doSomethingElse: function() {
    console.log("Something else")
  }
}

var sample = Washi(Sample, {
    el: "#sample-el"
})

Corresponding with:

<div id="sample">
  <h1 class="title">Paper Crane</h1>
</div>

API

  1. Washi Components
  2. Washi Utilities

Washi Components

Create a view component based upon a configuration:

let Sample = {
  ui: {
    title: '.title',
  },
  events: {
    'click @ui.title': 'doSomething'
  },
  initialize: function(options) {
    // Startup
    console.log("Hello, world!")
  },
  doSomething: function() {
    alert("click!")
  }
}

let sample = Washi(Sample, {
  el: "#sample-el"
})

initialize(options: Object)

Invoked after the Washi instance has been created. Use this method for setup behavior.

let Widget = {
  initialize: function(options) {
    console.log('all set!')
  }
}

let component = Washi(Widget, { el: document.body }) // "all set!"

events: Object

Attach event listeners to the element associated with the component. Keys in this object are selectors, values are string references to methods on the configuration object:

let Widget = {
  events: {
    'click button': 'onClick'
  },

  onClick: function(event) {
    alert("HELLO!")
  }
}

let component = Washi(Widget, { el: '.selector' })

component.query('button').invoke('click') // "HELLO!"

ui: Object

Preselect child nodes within the element provided to Washi. These selections are available in a few places.

As aliases to their underlying selectors in the events object

In the events object, @ui.{name} is replaced with the CSS selector for the UI entry:

let Widget = {
  ui: {
    'button': 'button'
  },
  events: {
    'click @ui.button': 'onClick'
  },
  onClick: function(event) {
    alert("HELLO!")
  }
}
As entries in this.ui

Reference the selected elements associated with a ui entry under this.ui:

let Widget = {
  ui: {
    'button': 'button'
  },
  initialize: function() {
    // An array of DOM elements
    this.ui.button.forEach(el => el.click())
    // Alternatively, a Washi chain
    this.ui.$button.invoke('click')
  }
}

Washi Utilities

Washi exposes a number of utility methods under the $ namespace:

  1. chain
  2. Array operations
  3. toArray
  4. extend
  5. has
  6. query
  7. queryAll
  8. invoke
  9. isBlank
  10. isDOM
  11. isFunction
  12. isObject
  13. isRegExp
  14. isString
  15. isUndefined
  16. matches
  17. on
  18. off
  19. result
  20. tap
  21. template
  22. append
  23. remove
  24. addClass
  25. removeClass
  26. toggleClass

chain(target: Object | Array)

Creates a pipeline for executing multiple operatins on a value, this can be a single object or a list of values. Use valueOf() to end the chain, returning the underlying value:

const $ = Washi.$

let chain = $.chain([1,2,3]).map(i => i + 1)
let result = chain.valueOf()

console.log(result) // [2,3,4]

Passing a string to Washi.$ executes document.querySelectorAll, converting the selection into a chain:

const $ = Washi.$

$('button').on('click', () => {
  alert("HI!")
})

Array operations

Washi.$ includes most ES5 methods from the Array prototype. The primary benefit of doing this is to work around DOM APIs that return "array like" values such as NodeList:

You can use these directly, however they are primary intended for use with chaining:

const $ = Washi.$

let internalLinks = $('a').filter(el => el.href.origin === window.location.origin)

internalLinks.on('click', () => console.log("Retention! Hurrah!"))

toArray(list: Enumerable)

Convert a list-like value, such as a NodeList returned from document.querySelectorAll into an array:

const $ = Washi.$

let links = document.querySelectorAll('a')
let hrefs = $.toArray(links).map(el => el.href)

extend(target: Object, ...others)

Given a list of arguments, extend a target object with additional properties:

const $ = Washi.$

let red = { r: 200, g: 0, b: 0 }
let blue = { r: 0, g: 0, b: 220 }

let purple = $.extend({}, red, blue) // { r: 200, g: 0, b: 200 }

Important: the first argument, target, is mutated. This is a destructive operation!

has(target: Object, property: String)

Returns true if a given object has a property. This is sugar around Object.prototype.hasOwnProperty that covers some edge cases, such as null or undefined values.

const $ = Washi.$

let styles = { color: 'blue', font: '14px Helvetica' }

$.has(styles, 'color')  // true
$.has(styles, 'border') // false

query(selector: String, root: HTMLElement = document)

Select an HTML element. When no second argument is given, selection occurs on the document. When no element is found, it returns null.

const $ = Washi.$

let btn = $.query('button')

if (btn) {
  btn.click()
}

queryAll(selector: String)

Select multiple HTML elements. The resulting selection is converted into an Array, making its safe to perform operations like forEach, map, and reduce. When no element is found, it returns an empty array.

const $ = Washi.$

let items = $('ul').queryAll('li')

items.forEach(el => console.log(item.innerHTML))

invoke(list: Array, method: String, ...arguments: any[])

Execute a method on each member of a list:

const $ = Washi.$

// Operate on data
let planets = ['Mercury', 'Venus', 'Earth', 'Mars']
$.invoke(planets, 'toUpperCase') // ['MERCURY', 'VENUS', 'EARTH', 'MARS']

// Or call methods on a list of elements
$('button').invoke('click')

isBlank(value: any)

Is a value null or undefined?

const $ = Washi.$

$.isBlank('')         // false
$.isBlank(false)      // false
$.isBlank(0)          // false
$.isBlank(null)       // true
$.isBlank(undefined)  // true

isDOM(value: any)

Is a value a DOM element?

const $ = Washi.$

$.isDOM({})          // false
$.isDOM(new Image()) // true

isFunction(value: any)

Is a value a function?

const $ = Washi.$

$.isFunction({})            // false
$.isFunction(function() {}) // true

isObject(value: any)

Is a value an object? This function helps to avoid pitfalls with type checking objects. For example: typeof null === 'object'!

const $ = Washi.$

$.isObject({})            // true
$.isObject([])            // true
$.isObject(null)          // false
$.isObject(function() {}) // false

isRegExp(value: any)

Is a value a regular expression?

const $ = Washi.$

$.isRegExp({})                  // false
$.isRegExp(/[A-Z]/)             // true
$.isRegExp(new RegExp("[A-Z]")) // true

isString(value: any)

Is a value a string?

const $ = Washi.$

$.isString({})            // false
$.isString("Hello world") // true

isUndefined(value: any)

Is a value undefined?

const $ = Washi.$

$.isUndefined(null)      // false
$.isUndefined('')        // false
$.isUndefined(0)         // false
$.isUndefined(undefined) // true

matches(el: Element, selector: String)

Returns true if the provided element matches a CSS selector. When possible, this function uses the Element.matches DOM API.

const $ = Washi.$

$.matches(document.body, '.class-name')

on(el: Element, event: String, callback: Function, capture: boolean)

Safely attach an event listener with extreme browser support (IE5+).

const $ = Washi.$

$.on(document.body, 'click', event => alert("CLICK!"))

// Or chain it:
$('button').on('click', event => alert("CLICK!"))

For more information about event listening, see EventTarget.addEventListener

off(el: Element, event: String, callback: Function, capture: boolean)

Safely remove an event listener with extreme browser support (IE5+).

const $ = Washi.$

let buttons = $('button')
let handler = event => alert("CLICK!")

buttons.on('click', handler)
buttons.invoke('click') // CLICK! CLICK! CLICK!

buttons.off('click', handler)
buttons.invoke('click') // silence

result(target: any, property: String, fallback: any)

Check for ownership of a value, optionally calling it if it is a function. If the value is undefined, return the fallback value.

const $ = Washi.$

$.result({}, 'method', 'missing') // 'missing'
$.result('foobar', 'toUpperCase', 'UNKNOWN') // 'FOOBAR'

tap(target: any, fn: Function, scope: any)

Calls a function at a given scope, passing in the target value as the only argument. This is primarily intended side-effects when chaining:

const $ = Washi.$

function render(items) {
  let p = document.createElement('p')

  p.innerHTML = items.join(', ')

  document.body.appendChild(p)
}

let dates = $([new Date('01-01-2000'), new Date('02-02-2010'])

template(target: any, fn: Function, scope: any)

An extremely simple templating language. Primary used for basic string replacement. For more serious uses, particulary with DOM manipulation, use a vetted templating language such as mustachejs.

const $ = Washi.$

$.template('{foo}', { foo: 'bar' }) //=> 'bar'
$.template('{foo}') //=> '{foo}'

append(parent: Element, ...elements: Element[])

Append a list of children to an element:

const $ = Washi.$

let planets = $(['Mercury', 'Venus', 'Earth', 'Mars'])

planets.map(item => {
  var el = document.createElement('li')
  el.innerHTML = item
  return el
})

$('ul').append(planets.valueOf())

remove(elements: Element | Element[])

Remove a single HTML element from its parent, or each element within a list:

const $ = Washi.$

// Remove buttons from a form:
$('#my-form button').remove()

addClass(element: Element, classes: String)

Add one or more class names to an element:

const $ = Washi.$

let el = document.createElement('button')

el.className = 'btn'

$.addClass(el, 'btn-large')

console.log(el.className) // 'btn btn-large'

removeClass(element: Element, classes: String)

Remove one or more class names to an element:

const $ = Washi.$

let el = document.createElement('button')

el.className = 'btn btn-large'

$.removeClass(el, 'btn btn-large')

console.log(el.className) // ''

toggleClass(element: Element, classes: String, keep?: Boolean)

Toggle one or more class names to an element. When provided, adds/removes the class names based upon the keep argument

const $ = Washi.$

let el = document.createElement('button')

el.className = 'btn'

$.toggleClass(el, 'active')
console.log(el.className) // 'btn active'

$.toggleClass(el, 'active')
console.log(el.className) // 'btn'

$.toggleClass(el, 'active', false)
console.log(el.className) // 'btn'

$.toggleClass(el, 'active', true)
console.log(el.className) // 'btn active'

Code At Viget

Visit code.viget.com to see more projects from Viget.