kist-view

Simple UI view.


Keywords
kist, view, ui, browserify
License
MIT
Install
bower install kist-view

Documentation

figura

Build Status BrowserStack Status

View component for markup you already have.

Features:

  • Basic state and props setting and implicit rendering
  • Subview managment: adding, getting and removing

Inspired by:

Try it now!

Install

npm install figura --save

Usage

<div id="shelby">
	<span id="sasha">
		<button type="button" class="lilly">Lilly button</button>
		<span class="honey">Honey content</span>
	</span>
	<i class="roxie">Roxie content</i>
</div>
import Figura from 'figura';

class Shelby extends Figura {
	static el = '#shelby';
	static childrenEl = {
		sasha: '#sasha'
	};
	static events = {
		'click .lilly': 'clickMethod'
	};
	clickMethod() {
		// .lilly clicked!
	}
}

class Sasha extends Shelby {
	static el = '#sasha';
	static childrenEl = {
		...Shelby.childrenEl,
		honey: '.honey'
	};
	clickMethod() {
		// .lilly clicked, with overriden method on `Sasha`.
	}
}

const shelby = new Shelby();

// `roxie` won’t have any child elements and won’t have any event listeners since it doesn’t have any children nodes
const roxie = new Sasha({
	el: '.roxie'
});

Using state and props. All render specifics can be contained inside render method.

import Figura from 'figura';

class Shelby extends Figura {
	static el = '#shelby';
	static defaultProps = {
		text: 'shelby'
	};
	constructor(props) {
		super(props);
		this.render(); // Initial render only with props
		this.setState({
			jackie: 42,
			romeo: '42'
		});
		const state = this.state;
		state.jackie; // => 42 as number
	}
	render(key, value) {
		if (typeof key === 'undefined') {
			this.$el.innerHTML = `Initial content is ${this.props.text}.`; // Initial content is shelby.
		}
		if (key === 'romeo') {
			this.$el.innerHTML = `Value is ${value}.`; // Value is 42.
		}
		return this;
	}
}

Render placeholder and view assign usage.

import Figura from 'figura';

class Shelby extends Figura {
	static el = '#shelby';
	template() {
		// Template function result
	}
	constructor(props) {
		super(props);
		this.addSubview(new Figura(), 'customKey');
	}
	render() {
		this.$el.innerHTML = this.template({
			customKeyComponent: this.getSubview(
				'customKey'
			).getRenderPlaceholder()
		});
		this.assignSubview('customKey');
		return this;
	}
}

API

Every property except el, childrenEl and events will be merged with defaultProps and added to this.props.

el

Type: (string|HTMLElement)
Default: ``

Element on which should view be created.

childrenEl

Type: Object
Default: {}

List of children elements, mapped to DOM nodes based on CSS selector.

To handle common use case of selecting only one element, if result contains only one element, only that element is returned, otherwise array of elements is returned.

If you want to return array of elements regardless of resulting count, append [] to list key.

{
	'shelby': '.shelby', // this.$shelby = this.$('.shelby'); - Returns either one or array of elements, based on resulting count
	'sasha': '.sasha' // this.$sasha = this.$('.sasha'); - Returns either one or array of elements, based on resulting count
	'rudy[]': '.rudy' // this.$rudy = this.$('.rudy'); - Always returns array of elements
}

events

Type: Object
Default: {}

List of events, delegated to children elements.

There are certain considerations to take when using Figura’s event delegation:

  • Since delegating focus and blur events is not natively possible, those events are mapped to focusin and focusout respectively
  • If you don’t provide selector to which you want to delegate event, that event will be attached to root element (i.e., el property value).
{
	'click .shelby': 'method1', // Delegated click event to `.shelby` calling instance method `method1`
	'submit .sasha': 'method2', // Delegated submit event to `.sasha` calling instance method `method2`
	'mouseleave .lilly': 'method3', // Delegated mouseleave event to `.lilly` calling instance method `method3`
	'mouseenter .rudy': ( e ) {  // Delegated mouseenter event to `.rudy` calling anonymous function
		// Do something
	},
	'click': 'method4' // Attached to root element since there is no selector to delegate to
}

defaultProps

Type: function
Default: {}

Default values for props to ensure that this.props property has value if it hasn’t been passed when creating view instance.

$(selector, returnAllNodes)

Type: function
Returns: (HTMLElement|HTMLElement[])

Finds all descendants of $el filtered by CSS selector.

Property Type Default Description
selector string N/A Standard CSS selector.
returnAllNodes boolean false Always return array of elements. By default, if result contains only one element only that element is returned, if result doesn’t contain any element null is returned, otherwise array of elements is returned.

render(key, value)

Type: function
Returns: Figura

Render view. Takes into account state modifications if you use state—every time state is modified render is called with key which is changed and current state. In this instance, state is combination of previous and new state.

Property Type Description
key string Current state key that should be handled in render.
value * Value of current state key that should be handled in render.

remove

Type: function

Remove view, but not root DOM element—use removeElement for that.

setElement(element)

Type: function

Sets or re-sets current UI element.

element

Type: String|Element

removeElement

Type: function

Remove view DOM element.

cacheChildrenEl(elements)

Type: function

Caches children elements.

elements

Type: Object

See childrenEl.

delegateEvents(events)

Type: function

Delegates events.

events

Type: Object

See events.

undelegateEvents

Type: function

Undelegates events.

delegate(eventName, selector, listener)

Type: function

Delegate single event.

If you provide empty string selector, event will be attached to root element.

eventName

Type: string

selector

Type: string

listener

Type: function

undelegate(eventName, selector, listener)

Type: function

Undelegate single event. For argument definition, see delegate.

setState(data)

Type: function

Set state for instance. Runs synchronously, so if one piece of state depends on other (e.g. one key depends on another key), run multiple setState calls with different keys.

addSubview(view, key)

Adds subview to current view.

view

Type: Figura

Subview to add to current view.

key

Type: (string|number)

Subview key so it can be easily identified in subview collection. If undefined, view’s uid property will be used.

getSubview(key)

Gets subview referenced by key.

key

Type: (string|number)

Key which is used to reference subview.

removeSubviews

Removes all subviews from current view.

getRenderPlaceholder

Returns view’s placeholder element which will be used in resolving for assignSubview.

assignSubview(key)

Replaces view’s render placeholder with real content. Real content should be rendered inside subview render method.

key

Type: (string|number)

Key which is used to reference subview.

addSideEffect(...args)

Add side effect. See manageSideEffects.add documentation.

removeSideEffect(...args)

Remove side effect. See manageSideEffects.remove documentation. Every registered side effect is removed on remove method call.

FAQ

Running setState on multiple keys which depend on each other in that call doesn’t render changes properly.

setState runs synchronously, so if one piece of state depends on other (e.g. one key depends on another key in current setState call), run multiple setState calls.

For example, if romeo and layla values depend on each other in current setState call:

// Instead of this
this.setState({
	romeo: 'gigi',
	layla: 'buddy'
});

// Use this
this.setState({
	romeo: 'gigi'
});
this.setState({
	layla: 'buddy'
});

Communicating with parent component from child component

There are a lot of ways you can achieve this:

  • Passing parent component instance to child component instance as prop
  • Using event emmiter and publish/subscribe pattern
  • Passing callback from parent component to child component as prop (recommended, try the example)

Test

For local automated tests, run npm run test:automated:local.

Browser support

Tested in IE9+ and all modern browsers. For IE <= 10 support, you will have to polyfill __proto__ if you use class extending.

For static class properties, you need to use Babel plugin, otherwise set properties explictly on class.

import Figura from 'figura';

// With static class properties
class Shelby extends Figura {
	static el = '';
}

// Without static class properties
class Shelby extends Figura {}
Shelby.el = '';

License

MIT © Ivan Nikolić