From 942a32ff55886bd05397b07f4a09bb9737021a58 Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 26 Jun 2016 01:33:37 -0400 Subject: [PATCH] Add mouse effects and draggable example draggable implementation heavily inspired by http://elm-lang.org/examples/drag --- effects/mouse.js | 27 +++++++++ examples/ExamplesApp.js | 2 + examples/draggable/Draggable.js | 103 ++++++++++++++++++++++++++++++++ examples/draggable/app.js | 6 ++ package.json | 1 + 5 files changed, 139 insertions(+) create mode 100644 effects/mouse.js create mode 100644 examples/draggable/Draggable.js create mode 100644 examples/draggable/app.js diff --git a/effects/mouse.js b/effects/mouse.js new file mode 100644 index 0000000..6b8bfd6 --- /dev/null +++ b/effects/mouse.js @@ -0,0 +1,27 @@ +import { Sub } from '../spindle'; + +const ns = Symbol('MOUSE'); + + +const windowEvent = (ev, extract) => + Sub(ns, { + key: ev, + start: change => { + const handler = e => + change(handler, extract(e)); + window.addEventListener(ev, handler); + return handler; + }, + stop: handler => + window.removeEventListener(ev, handler), + }); + + +export const ups = windowEvent('mouseup', () => + undefined); + +export const downs = windowEvent('mousedown', () => + undefined); + +export const moves = windowEvent('mousemove', e => + ({ x: e.pageX, y: e.pageY })); diff --git a/examples/ExamplesApp.js b/examples/ExamplesApp.js index 5ed9539..4e7d44f 100644 --- a/examples/ExamplesApp.js +++ b/examples/ExamplesApp.js @@ -12,6 +12,7 @@ import ConstrainedCounters from './constrained-counters/Parent'; import Wrapper from './wrapper/Page'; import Diceroll from './diceroll/Diceroll'; import Clock from './clock/Clock'; +import Draggable from './draggable/Draggable'; import TodoMVC from './todomvc/TodoMVC'; @@ -24,6 +25,7 @@ const exampleComponents = [ Wrapper, Diceroll, Clock, + Draggable, TodoMVC, ]; diff --git a/examples/draggable/Draggable.js b/examples/draggable/Draggable.js new file mode 100644 index 0000000..29fe991 --- /dev/null +++ b/examples/draggable/Draggable.js @@ -0,0 +1,103 @@ +import React from 'react'; +import Spindle, { Update } from '../../spindle'; +import { Union, Maybe } from 'results'; +import { Record } from 'immutable'; +import { moves, ups } from '../../effects/mouse'; + + +const SIZE = 100; + + +const Action = Union({ + Grab: null, + Move: null, + Release: null, +}); + + +const Model = Record({ + position: { x: SIZE / 1.5, y: 240 }, + drag: Maybe.None(), +}); + + +const Drag = Record({ + start: { x: 0, y: 0 }, + current: { x: 0, y: 0 }, +}); + + +const init = () => + Update({ model: Model() }); + + +const update = (action, model) => Action.match(action, { + Grab: e => + Update({ + model: model.set('drag', Maybe.Some(Drag({ + start: { x: e.pageX, y: e.pageY }, + current: { x: e.pageX, y: e.pageY }, + })) ), + }), + + Move: newPos => + Update({ + model: model.update('drag', d => + d.andThen(({ start }) => + ({ start, current: newPos }))), + }), + + Release: () => + Update({ model: Model({ + position: getPosition(model), + drag: Maybe.None(), + }) }), +}); + + +const subscriptions = model => + model.drag.isSome() + ? [ [moves, Action.Move] + , [ups, Action.Release] + ] + : []; + + +const getPosition = model => + Maybe.match(model.drag, { + None: () => + model.position, + Some: drag => ({ + x: model.position.x + drag.current.x - drag.start.x, + y: model.position.y + drag.current.y - drag.start.y, + }), + }); + + +const view = (model, dispatch) => { + const { x, y } = getPosition(model); + return ( +
+ drag me +
+ ); +}; + + +export default Spindle('Draggable', + { Action, init, update, subscriptions, view }); diff --git a/examples/draggable/app.js b/examples/draggable/app.js new file mode 100644 index 0000000..80e2404 --- /dev/null +++ b/examples/draggable/app.js @@ -0,0 +1,6 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Draggable from './Draggable'; + + +ReactDOM.render(, document.getElementById('app')); diff --git a/package.json b/package.json index 2cefbe8..ff34823 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "watch-constrained": "watchify examples/constrained-counters/app.js -d -t babelify --outfile bundle.js", "watch-wrap": "watchify examples/wrapper/app.js -d -t babelify --outfile bundle.js", "watch-clock": "watchify examples/clock/app.js -d -t babelify --outfile bundle.js", + "watch-draggable": "watchify examples/draggable/app.js -d -t babelify --outfile bundle.js", "watch-diceroll": "watchify examples/diceroll/app.js -d -t babelify --outfile bundle.js" }, "author": "uniphil",