Skip to content

Commit

Permalink
🌠
Browse files Browse the repository at this point in the history
  • Loading branch information
calebmer committed Jul 2, 2016
0 parents commit 568fd4a
Show file tree
Hide file tree
Showing 79 changed files with 2,947 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.DS_Store
npm-debug.log
lerna-debug.log
dist
build

# We build custom links for the vulture dependencies in our monorepo, don’t
# ignore those.
**/node_modules/*
!packages/*/node_modules/vulture*
4 changes: 4 additions & 0 deletions assets/entries/vulture.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// TODO: This file…

export { jsx } from 'vulture/index'
export { render } from 'vulture-dom/index'
3 changes: 3 additions & 0 deletions assets/env-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require('ts-node/register')

global.Observable = require('rxjs').Observable
39 changes: 39 additions & 0 deletions assets/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Source: https://github.com/zenparsing/es-observable

declare class Observable<T> {
constructor (subscriber: SubscriberFn<T>)

subscribe (observer: Observer<T>): Subscription
subscribe (
onNext: (value: T) => void,
onError?: (error: Error) => void,
onComplete?: (value?: any) => void
): Subscription

static of <T>(...items: T[]): Observable<T>

static from <T>(observable: Observable<T>): Observable<T>
static from <T>(observable: Iterable<T>): Iterable<T>
static from (observable: any): Observable<any>
}

interface Observer<T> {
start (subscription: Subscription): void
next (value: T): void
error (error: Error): void
complete (value?: any): void
}

interface Subscription {
unsubscribe (): void
closed: boolean
}

interface SubscriptionObserver<T> {
next (value: T): void
error (error: Error): void
complete (value?: any): void
closed: boolean
}

type SubscriberFn<T> = (observer: SubscriptionObserver<T>) => (() => void) | Subscription
50 changes: 50 additions & 0 deletions examples/counter-simple/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!doctype html>
<html>
<head>
<title>Vulture Example: Simple Counter</title>
<script src="https://npmcdn.com/[email protected]/zen-observable.js"></script>
<script src="../../build/dist/vulture.js"></script>
</head>
<body>
<div id="app"></div>
<script>
var count = 0
var observers = []

function increment () {
count += 1
observers.forEach(function observerNext (observer) { observer.next(count) })
}

function decrement () {
count -= 1
observers.forEach(function observerNext (observer) { observer.next(count) })
}

var counts = new Observable(function subscribe (observer) {
var i = observers.length
observers.push(observer)
observer.next(count)
return function unsubscribe () { observers.splice(i, 1) }
})

counts.subscribe(function onNext (count) {
console.log('Count: ', count)
})

var jsx = Vulture.jsx

var app =
jsx('p', [
counts.map(function computeClickedText (count) {
return 'Clicked: ' + count + ' times'
}),
' ',
jsx('button', { onClick: increment }, ['+']),
jsx('button', { onClick: decrement }, ['-']),
])

Vulture.render(app, document.getElementById('app'))
</script>
</body>
</html>
1 change: 1 addition & 0 deletions examples/todomvc/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
24 changes: 24 additions & 0 deletions examples/todomvc/src/components/todoApp.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as todoHeader from './todoHeader'
import * as todoItem from './todoItem'
import * as todoFooter from './todoFooter'

export const render = store =>
<main>
<div>
<header>
{todoHeader.render(store)}
</header>
<section>
<ul>
{store.filteredTodoIDs$.map(todoIDs => todoIDs.map(id =>
<li id={`todo-${id}`}>
{todoItem.render(store)(id)}
</li>
))}
</ul>
</section>
<footer>
{todoFooter.render(store)}
</footer>
</div>
</main>
20 changes: 20 additions & 0 deletions examples/todomvc/src/components/todoFooter.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const render = store =>
<div>
<p>
{store.incompleteTodosCount$} item{store.incompleteTodosCount$.map(n => n === 1 ? '' : 's')} left
</p>
<nav>
<ul>
<li><a onClick={() => store.setTodoFilter('all')}>All</a></li>
<li><a onClick={() => store.setTodoFilter('active')}>Active</a></li>
<li><a onClick={() => store.setTodoFilter('completed')}>Completed</a></li>
</ul>
</nav>
{store.completedTodosCount$.map(n =>
n > 0
? <button onClick={store.clearCompleted}>
Clear completed
</button>
: null
)}
</div>
26 changes: 26 additions & 0 deletions examples/todomvc/src/components/todoHeader.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export const render = store =>
<div>
{store.todoCount$.map(n =>
n !== 0
? <button onClick={store.toggleAllTodosComplete}/>
: null
)}
<input
type="text"
name="new-todo"
value=""
autofocus={true}
placeholder="What needs to be done?"
onKeyDown={event => {
switch (event.keyCode) {
case ESCAPE_KEY:
event.target.value = ''
break
case ENTER_KEY:
store.newTodo(event.target.value)
event.target.value = ''
break
}
}}
/>
</div>
39 changes: 39 additions & 0 deletions examples/todomvc/src/components/todoItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export const render = store => memoize(todoID => {
const editing$ = new Subject()
return (
<div>
{Observable.combineLatest(
store.getTodoText$(todoID),
editing$.startWith(false),
(text, editing) =>
editing
? renderInput(text, newText => {
if (newText) store.updateTodoText(todoID, newText)
editing$.next(false)
})
: <p onDblClick={() => editing$.next(true)}>
{text}
</p>
)}
<button onClick={() => store.toggleTodoCompleted(todoID)}/>
<button onClick={() => store.deleteTodo(todoID)}/>
</div>
)
})

const renderInput = (initialText, finish) =>
<input
type="text"
name="edit-todo"
value={initialText}
onKeyDown={event => {
switch (event.keyCode) {
case ESCAPE_KEY:
finish()
break
case ENTER_KEY:
finish(event.target.value)
break
}
}}
/>
29 changes: 29 additions & 0 deletions examples/todomvc/src/model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as selectors from './selectors'
import * as updates from './updates'

const initialState = {
todoIDs: [],
todoByID: {},
todoFilter: 'all',
}

// function createStore () {
// const update$ = new Subject()
// const state$ = update$.scan((state, update) => update(state), initialState).cache(1)
//
// return {
// update$,
// state$,
// ...createSelectors(state$),
// ...bindUpdates(update$)(createUpdates()),
// }
// }

const store = createStore(
initialState,
selectors,
updates
)

// TODO: URL stuffs…
// store.todoFilter$.subscribe()
64 changes: 64 additions & 0 deletions examples/todomvc/src/selectors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const todoIDs$ = state$ =>
state$.map(state => state.todoIDs).distinctUntilChanged()

const todos$ = state$ =>
state$.map(state => state.todoIDs.map(id => state.todoByID[id])).distinctUntilChanged(shallowArrayCompare)

const todosWithIDs$ = state$ =>
Observable.combineLatest(
todoIDs$(state$),
todos$(state$),
(ids, todos) => ids.map((id, i) => ({
id,
todo: todos[i],
}
)))

const todoFilter$ = state$ =>
state$.map(state => state.todoFilter).distinctUntilChanged()

const todoFilterFunction$ = state$ =>
todoFilter$(state$).map(filter => {
switch (filter) {
case 'all': return () => true
case 'active': return todo => !todo.completed
case 'completed': return todo => todo.completed
default: throw new Error(`Filter '${filter}' not supported.`)
}
})

export const filteredTodoIDs$ = state$ =>
Observable.combineLatest(
todosWithIDs$(state$),
todoFilterFunction$(state$),
(todosWithIDs, todoFilterFunction) =>
todosWithIDs.filter(({ todo }) => todoFilterFunction(todo)).map(({ id }) => id)
)

export const todoCount$ = state$ =>
state$.map(state => state.todoIDs.length).distinctUntilChanged()

export const completedTodosCount$ = state$ =>
todos$(state$).map(todos => todos.filter(todo => todo.completed).length)

export const incompleteTodosCount$ = state$ =>
todos$(state$).map(todos => todos.filter(todo => !todo.completed).length)

const getTodo$ = state$ => memoize(id =>
state$.map(state => state.todoByID[id]).distinctUntilChanged()
)

export const getTodoText$ = state$ => memoize(id =>
getTodo$(id).map(todo => todo.text).distinctUntilChanged()
)

function shallowArrayCompare (a, b) {
if (a.length !== b.length)
return false

for (const i = 0; i < a.length; i++)
if (a[i] !== b[i])
return false

return true
}
Loading

0 comments on commit 568fd4a

Please sign in to comment.