Skip to content

Commit

Permalink
Merge pull request #400 from dekujs/update/f
Browse files Browse the repository at this point in the history
Small refactor
  • Loading branch information
anthonyshort committed Feb 3, 2016
2 parents 5c94e94 + 0dad55b commit c2cb784
Show file tree
Hide file tree
Showing 18 changed files with 304 additions and 277 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ node_modules
_book
dist
lib
npm-debug.log
18 changes: 14 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"test": "hihat test/index.js -- --debug -t babelify -p tap-dev-tool",
"test:headless": "browserify test/index.js -t babelify | tape-run",
"test:browsers": "zuul -- ./test/index.js",
"test:lint": "standard src/*.js test/*.js | snazzy",
"test:lint": "standard src/**/*.js test/**/*.js | snazzy",
"docs:install": "gitbook install",
"docs:build": "npm run docs:install && gitbook build",
"docs:serve": "npm run docs:install && gitbook serve",
Expand Down Expand Up @@ -48,12 +48,22 @@
"zuul": "3.9.0"
},
"dependencies": {
"@f/create-element": "1.0.0",
"@f/empty-element": "^1.0.0",
"@f/foreach": "1.2.2",
"@f/is-function": "1.1.1",
"@f/is-null": "1.0.0",
"@f/is-number": "1.1.1",
"@f/is-string": "1.1.1",
"@f/is-undefined": "1.1.1",
"@f/is-valid-attr": "1.0.0",
"@f/noop": "1.2.0",
"@f/reduce-array": "^0.2.1",
"@f/set-attribute": "1.0.1",
"@f/to-array": "1.1.1",
"dift": "0.1.12",
"index-of": "0.2.0",
"is-svg-element": "1.0.1",
"setify": "1.0.3",
"svg-attribute-namespace": "2.1.0",
"uid": "0.0.2",
"union-type": "0.1.6"
},
"keywords": [
Expand Down
13 changes: 8 additions & 5 deletions src/app/index.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
import * as dom from '../dom'
import {diffNode} from '../diff'
import empty from '@f/empty-element'
import noop from '@f/noop'

/**
* Create a DOM renderer using a container element. Everything will be rendered
* inside of that container. Returns a function that accepts new state that can
* replace what is currently rendered.
*/

export function create (container, dispatch, options = {}) {
export function createApp (container, handler = noop, options = {}) {
let oldVnode = null
let node = null
let rootId = options.id || '0'
let dispatch = effect => effect && handler(effect)

if (container && container.childNodes.length > 0) {
container.innerHTML = ''
if (container) {
empty(container)
}

let update = (newVnode, context) => {
let changes = diffNode(oldVnode, newVnode, rootId)
node = changes.reduce(dom.update(dispatch, context), node)
node = changes.reduce(dom.updateElement(dispatch, context), node)
oldVnode = newVnode
return node
}

let create = (vnode, context) => {
node = dom.create(vnode, rootId, dispatch, context)
node = dom.createElement(vnode, rootId, dispatch, context)
if (container) container.appendChild(node)
oldVnode = vnode
return node
Expand Down
40 changes: 26 additions & 14 deletions src/diff/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {isText, isThunk, isSameThunk, isEmpty, groupByKey, createPath} from '../element'
import {isText, isThunk, isSameThunk, isNative, isEmpty, groupByKey, createPath} from '../element'
import dift, * as diffActions from 'dift'
import isUndefined from '@f/is-undefined'
import isNull from '@f/is-null'
import Type from 'union-type'
let Any = () => true
let Path = () => String
Expand Down Expand Up @@ -114,35 +116,47 @@ export function diffChildren (previous, next, parentPath) {
*/

export function diffNode (prev, next, path) {
let changes = []
let {replaceNode, setAttribute, sameNode, removeNode, updateThunk} = Actions

// No left node to compare it to
// TODO: This should just return a createNode action
if (prev === null || prev === undefined) {
if (isUndefined(prev)) {
throw new Error('Left node must not be null or undefined')
}

// Bail out and skip updating this whole sub-tree
if (prev === next) {
changes.push(sameNode())
return changes
return [sameNode()]
}

// Remove
if (prev != null && next == null) {
changes.push(removeNode(prev))
return changes
if (!isUndefined(prev) && isUndefined(next)) {
return [removeNode(prev)]
}

// Replace with empty
if (!isNull(prev) && isNull(next) || isNull(prev) && !isNull(next)) {
return [replaceNode(prev, next, path)]
}

// Replace
if (prev.type !== next.type) {
changes.push(replaceNode(prev, next, path))
return [replaceNode(prev, next, path)]
}

// Native
if (isNative(next)) {
if (prev.tagName !== next.tagName) {
return [replaceNode(prev, next, path)]
}
let changes = diffAttributes(prev, next)
changes.push(diffChildren(prev, next, path))
return changes
}

// Text
if (isText(next)) {
let changes = []
if (prev.nodeValue !== next.nodeValue) {
changes.push(setAttribute('nodeValue', next.nodeValue, prev.nodeValue))
}
Expand All @@ -151,6 +165,7 @@ export function diffNode (prev, next, path) {

// Thunk
if (isThunk(next)) {
let changes = []
if (isSameThunk(prev, next)) {
changes.push(updateThunk(prev, next, path))
} else {
Expand All @@ -161,11 +176,8 @@ export function diffNode (prev, next, path) {

// Empty
if (isEmpty(next)) {
return changes
return []
}

changes = diffAttributes(prev, next)
changes.push(diffChildren(prev, next, path))

return changes
return []
}
111 changes: 57 additions & 54 deletions src/dom/create.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import {isText, isThunk, isEmpty, createPath} from '../element'
import createNativeElement from '@f/create-element'
import {createPath} from '../element'
import {setAttribute} from './setAttribute'
import svg from './svg'
import isUndefined from '@f/is-undefined'
import isString from '@f/is-string'
import isNumber from '@f/is-number'
import isNull from '@f/is-null'
const cache = {}

/**
Expand All @@ -9,68 +13,67 @@ const cache = {}
* so they are treated like any other native element.
*/

export default function createElement (vnode, path, dispatch, context) {
if (isText(vnode)) {
let value = typeof vnode.nodeValue === 'string' || typeof vnode.nodeValue === 'number'
? vnode.nodeValue
: ''
return document.createTextNode(value)
}

if (isEmpty(vnode)) {
return document.createElement('noscript')
export function createElement (vnode, path, dispatch, context) {
switch (vnode.type) {
case 'text':
return createTextNode(vnode.nodeValue)
case 'empty':
return getCachedElement('noscript')
case 'thunk':
return createThunk(vnode, path, dispatch, context)
case 'native':
return createHTMLElement(vnode, path, dispatch, context)
}
}

if (isThunk(vnode)) {
let { props, component, children } = vnode
let { onCreate } = component
let render = typeof component === 'function' ? component : component.render
let model = {
children,
props,
path,
dispatch,
context
}
let output = render(model)
let DOMElement = createElement(
output,
createPath(path, output.key || '0'),
dispatch,
context
)
if (onCreate) onCreate(model)
vnode.state = {
vnode: output,
model: model
}
return DOMElement
function getCachedElement (type) {
let cached = cache[type]
if (isUndefined(cached)) {
cached = cache[type] = createNativeElement(type)
}
return cached.cloneNode(false)
}

let cached = cache[vnode.type]
function createTextNode (text) {
let value = isString(text) || isNumber(text)
? text
: ''
return document.createTextNode(value)
}

if (typeof cached === 'undefined') {
cached = cache[vnode.type] = svg.isElement(vnode.type)
? document.createElementNS(svg.namespace, vnode.type)
: document.createElement(vnode.type)
function createThunk (vnode, path, dispatch, context) {
let { props, children } = vnode
let { onCreate } = vnode.options
let model = {
children,
props,
path,
dispatch,
context
}
let output = vnode.fn(model)
let childPath = createPath(path, output.key || '0')
let DOMElement = createElement(output, childPath, dispatch, context)
if (onCreate) dispatch(onCreate(model))
vnode.state = {
vnode: output,
model: model
}
return DOMElement
}

let DOMElement = cached.cloneNode(false)
function createHTMLElement (vnode, path, dispatch, context) {
let { tagName, attributes, children } = vnode
let DOMElement = getCachedElement(tagName)

for (let name in vnode.attributes) {
setAttribute(DOMElement, name, vnode.attributes[name])
for (let name in attributes) {
setAttribute(DOMElement, name, attributes[name])
}

vnode.children.forEach((node, index) => {
if (node === null || node === undefined) {
return
}
let child = createElement(
node,
createPath(path, node.key || index),
dispatch,
context
)
children.forEach((node, index) => {
if (isNull(node) || isUndefined(node)) return
let childPath = createPath(path, node.key || index)
let child = createElement(node, childPath, dispatch, context)
DOMElement.appendChild(child)
})

Expand Down
8 changes: 4 additions & 4 deletions src/dom/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import create from './create'
import update from './update'
import {createElement} from './create'
import {updateElement} from './update'

export {
create,
update
createElement,
updateElement
}
19 changes: 8 additions & 11 deletions src/dom/setAttribute.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import svgAttributeNS from 'svg-attribute-namespace'
import {isValidAttribute} from '../element'
import setNativeAttribute from '@f/set-attribute'
import isValidAttribute from '@f/is-valid-attr'
import isFunction from '@f/is-function'
import indexOf from 'index-of'
import setValue from 'setify'
import events from './events'

export function removeAttribute (DOMElement, name, previousValue) {
let eventType = events[name]
if (eventType) {
if (typeof previousValue === 'function') {
DOMElement.removeEventListener(eventType, previousValue)
}
if (eventType && isFunction(previousValue)) {
DOMElement.removeEventListener(eventType, previousValue)
return
}
switch (name) {
Expand All @@ -20,10 +19,8 @@ export function removeAttribute (DOMElement, name, previousValue) {
break
case 'innerHTML':
case 'nodeValue':
DOMElement.innerHTML = ''
break
case 'value':
DOMElement.value = ''
DOMElement[name] = ''
break
default:
DOMElement.removeAttribute(name)
Expand All @@ -37,7 +34,7 @@ export function setAttribute (DOMElement, name, value, previousValue) {
return
}
if (eventType) {
if (typeof previousValue === 'function') {
if (isFunction(previousValue)) {
DOMElement.removeEventListener(eventType, previousValue)
}
DOMElement.addEventListener(eventType, value)
Expand Down Expand Up @@ -66,7 +63,7 @@ export function setAttribute (DOMElement, name, value, previousValue) {
setValue(DOMElement, value)
break
default:
DOMElement.setAttributeNS(svgAttributeNS(name), name, value)
setNativeAttribute(DOMElement, name, value)
break
}
}
8 changes: 0 additions & 8 deletions src/dom/svg.js

This file was deleted.

Loading

0 comments on commit c2cb784

Please sign in to comment.