figura
v6.1.0
Published
View component for markup you already have.
Downloads
3,429
Readme
figura
View component for markup you already have.
Features:
- Basic state and props setting and implicit rendering
- Subview managment: adding, getting and removing
Inspired by:
- Event handling from Backbone.View
- State and props management from React and Preact
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
andblur
events is not natively possible, those events are mapped tofocusin
andfocusout
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|HTMLElement)
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ć