Skip to content

Commit

Permalink
Sync with master.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jorge Bucaran committed Jul 26, 2017
2 parents 0257e80 + 27d531a commit 2ee8790
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 199 deletions.
50 changes: 14 additions & 36 deletions docs/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,60 +12,41 @@ app({
move: (state, actions, { x, y }) => ({ x, y })
},
events: {
init: (state, actions) =>
load(state, actions) {
addEventListener("mousemove", e =>
actions.move({
x: e.clientX,
y: e.clientY
})
)
}
}
})
```

init willInit beforeLoad

loaded didInit

beforeAction beforeAction
afterAction afterAction

update willUpdate
didUpdate
render willRender




## Default Events

### beforeLoad

The init event fires before the first render. This is a good place to initialize your application, create a network request, access the local [Storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage), etc.

### loaded
### load

The loaded event fires after the first render. This event is useful if you need to access actual DOM nodes after initialization.
The load event fires before the first render. This is a good place to initialize your application, create network requests, access the local [Storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage), etc.

### beforeAction
### action

The beforeAction event fires before an action is called. This event can be useful to log action activity, extract action information, etc.

### afterAction
### resolve

The afterAction event fires after an action returns, allowing you to intercept its return value. Use this event to change the action state update mechanism, customize what types an action is allowed to return, etc.
The resolve event fires after an action returns, allowing you to intercept its return value. Use this event to customize what types an action is allowed to return or modify the default state update mechanism.

For example to allow actions to return an [Observable](https://github.com/tc39/proposal-observable).

```jsx
app({
//...
events: {
afterAction(state, actions, { result }) {
resolve(state, actions, { result }) {
if (data != null && typeof data.subscribe == "function") {
return {
result: update => result.subscribe({ next: update })
}
return update => result.subscribe({ next: update })
}
}
}
Expand All @@ -74,21 +55,18 @@ app({

### update

The update event fires before the state is updated. This event can be useful to validate the state before an update takes place.
The update event fires before the state is updated. This event can be useful to validate the new state before an update takes place.

### render

The render event fires every time before the view is rendered. You can return a new view to overwrite the default view.
The render event fires every time the view is rendered, allowing you to overwrite the default view. For a practical example see the [Router](https://github.com/hyperapp/router).

```jsx
app({
view: state => <h1>Default view.</h1>,
// ...
events: {
render(state, actions) {
if (location.pathname === "/warp") {
return state => <h1>Welcome to warp zone!</h1>
}
}
render: (state, actions, view) =>
location.pathanem === "/" ? view : notFoundView
}
})
```
Expand Down
8 changes: 4 additions & 4 deletions docs/mixins.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

Use [mixins](/docs/api.md#mixins) to encapsulate your application behavior into reusable modules, to share or just to organize your code.

This mixin listens to [beforeAction](/docs/events.md#beforeAction) events to log action information to the console.
This mixin listens to [action](/docs/events.md#action) events to log action information to the console.

```jsx
const ActionLogger = () => ({
events: {
beforeAction(state, actions, { name, data }) {
action(state, actions, { name, data }) {
console.group("Action Info")
console.log("Name:", name)
console.log("Data:", data)
Expand All @@ -30,12 +30,12 @@ app({

Mixins receive the [`emit`](/docs/api.md#emit) function as the first argument allowing you to create [custom events](/docs/events.md#custom-events).

This mixin listens to [beforeAction](/docs/events.md#beforeAction) and [afterAction](/docs/events.md#afterAction) events to time and log action performance to the console.
This mixin listens to [action](/docs/events.md#action) and [afterAction](/docs/events.md#afterAction) events to time and log action performance to the console.

```jsx
const ActionPerformance = (ignored = [], cache = []) => emit => ({
events: {
beforeAction(state, actions, { name }) {
action(state, actions, { name }) {
cache.push({
name,
time: performance.now()
Expand Down
112 changes: 35 additions & 77 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export function app(props) {
var node
var element
var locked = false
var loaded = false

for (var i = -1; i < mixins.length; i++) {
props = mixins[i] ? mixins[i](emit) : props
Expand All @@ -17,43 +16,39 @@ export function app(props) {
events[key] = (events[key] || []).concat(props.events[key])
})

if (props.state != null) {
state = merge(state, props.state)
}

iterate(actions, props.actions)
mixins = mixins.concat(props.mixins || [])

initialize(actions, props.actions)
state = merge(state, props.state || state)
}

node = hydrate((element = root.querySelector("[data-ssr]")), [].map)
schedule(
(node = hydrate((element = root.querySelector("[data-ssr]")), [].map))
)

repaint(emit("init"))

return emit
return emit("load")

function update(withState) {
if (withState != null) {
repaint((state = merge(state, emit("update", withState))))
if (withState) {
schedule((state = emit("update", merge(state, withState))))
}
}

function repaint() {
function schedule() {
if (!locked) {
requestAnimationFrame(render, (locked = !locked))
}
}

function hydrate(element, map) {
return element == null
? element
: {
return element
? {
tag: element.tagName,
data: {},
children: map.call(element.childNodes, function(element) {
hydrate(element, map)
})
}
: element
}

function render() {
Expand All @@ -63,72 +58,43 @@ export function app(props) {
node,
(node = emit("render", view)(state, actions))
)

locked = !locked

if (!loaded) {
emit("loaded", (loaded = true))
}
}

function initialize(namespace, children, lastName) {
function iterate(namespace, children, lastName) {
Object.keys(children || []).map(function(key) {
var action = children[key]
var name = lastName ? lastName + "." + key : key

if (typeof action === "function") {
namespace[key] = function(data) {
var result = action(
state,
actions,
emit("beforeAction", {
name: name,
data: data
}).data
)

if (result == null) {
} else if (typeof result === "function") {
result = result(update)
} else if (typeof result.then === "function") {
result.then(update)
} else {
update(result)
}
emit("action", { name: name, data: data })
var result = emit("resolve", action(state, actions, data))
return typeof result === "function" ? result(update) : update(result)

return result
// return result && result.then && result.then(update)
// ? result
// : typeof result === "function" ? result(update) : update(result)
}
} else {
initialize(namespace[key] || (namespace[key] = {}), action, name)
iterate(namespace[key] || (namespace[key] = {}), action, name)
}
})
}

function emit(name, data) {
return (events[name] || []).map(function(cb) {
var result = cb(state, actions, data)
if (result != null) {
data = result
}
}), data
function emit(event, withData) {
return (events[event] || []).map(function(cb) {
withData = cb(state, actions, withData)
}), withData
}

function merge(a, b) {
if (typeof b !== "object") {
return b
}

var obj = {}

for (var i in a) {
obj[i] = a[i]
}

for (var i in b) {
obj[i] = b[i]
function merge(from, to) {
for (var i in from) {
if (!(i in to)) {
to[i] = from[i]
}
}

return obj
return to
}

function getKey(node) {
Expand All @@ -152,8 +118,6 @@ export function app(props) {
for (var i in node.data) {
if (i === "oncreate") {
node.data[i](element)
} else if (i === "oninsert") {
setTimeout(node.data[i], 0, element)
} else {
setElementData(element, i, node.data[i])
}
Expand All @@ -167,11 +131,9 @@ export function app(props) {
if (
name === "key" ||
name === "oncreate" ||
name === "oninsert" ||
name === "onupdate" ||
name === "onremove"
) {
return name
} else if (name === "style") {
for (var i in merge(oldValue, (value = value || {}))) {
element.style[i] = value[i] || ""
Expand All @@ -191,22 +153,18 @@ export function app(props) {
}
}

function updateElementData(element, oldData, data, cb) {
function updateElementData(element, oldData, data) {
for (var name in merge(oldData, data)) {
var value = data[name]
var oldValue = oldData[name]

if (
value !== oldValue &&
value !== element[name] &&
setElementData(element, name, value, oldValue) == null
) {
cb = data.onupdate
if (value !== oldValue && value !== element[name]) {
setElementData(element, name, value, oldValue)
}
}

if (cb != null) {
cb(element)
if (data && data.onupdate) {
data.onupdate(element, oldData)
}
}

Expand Down
Loading

0 comments on commit 2ee8790

Please sign in to comment.