a = new AppStateUI
a.on 'firstSlide', enter: ->
@layer = new Layer width: 100, height: 100, x: 100, y: 100, backgroundColor: "green"
@layer.on Events.Click, a.go.bind(a, 'secondSlide')
, leave: ->
@layer.destroy()
a.on 'secondSlide', enter: ->
@layer = new Layer width: 100, height: 100, x: 100, y: 200, backgroundColor: "blue"
@layer.on Events.Click, a.go.bind(a, 'firstSlide')
, leave: ->
@layer.destroy()
Last active
August 9, 2018 14:32
-
-
Save elliottkember/0105c2c2279b0d347707 to your computer and use it in GitHub Desktop.
A simple application framework for Framer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
EventEmitter = (require?("./EventEmitter") || Framer).EventEmitter | |
class AppStateMachine extends EventEmitter | |
constructor: -> | |
@states = [] | |
# Start the state machine with the document's hash, or the given route | |
start: (name) -> | |
@go if document.location.hash then document.location.hash[1..-1] else name | |
# Add a callback | |
on: (event, options) -> | |
@states.push event unless event.indexOf(":") > -1 or event in @states | |
options = enter: options if typeof options == 'function' | |
super event, options.enter if options.enter | |
super "#{event}:leave", options.leave if options.leave | |
@emit 'change:states' unless event == 'change:states' | |
# Transition to a state | |
go: (state, callback=false) -> | |
return if state == @state | |
@emit "#{@state}:leave" | |
@emit document.location.hash = @state = state | |
@emit "#{@state}:enter" | |
callback?() | |
@emit 'change:state' | |
## A UI wrapper for the above state machine. | |
class AppStateUI extends AppStateMachine | |
constructor: (options...) -> | |
super options | |
@on "change:state", @render.bind @ | |
@on "change:states", @render.bind @ | |
@render() | |
render: -> | |
frame = @layer?.frame || width: 150, height: 0, x: 20, y: 20 | |
@layer?.destroy() | |
@layer = new Layer frame: frame, backgroundColor: 'transparent' | |
lineHeight = 40 | |
for state, i in @states | |
stateLine = new Layer width: 150, height: 40, x: 0, y: lineHeight * i, superLayer: @layer, backgroundColor: 'transparent' | |
stateLine.html = state | |
stateLine.style['font-weight'] = if state == @state then 800 else 100 | |
stateLine.on Events.Click, @go.bind(@, state) | |
@layer.height += lineHeight | |
exports = window if typeof exports == 'undefined' | |
exports.AppStateUI = AppStateUI | |
exports.AppStateMachine = AppStateMachine |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe "AppStateMachine", -> | |
describe "setup", -> | |
it "should be created with empty states", -> | |
a = new AppStateMachine() | |
a.states.should.have.length(0) | |
describe "states", -> | |
it "should let you create a new state", -> | |
j = new AppStateMachine() | |
l = j.states.length | |
j.on "asdfasdf", -> | |
j.success = true | |
j.states.should.have.length(l + 1) | |
it "should have unique state names", -> | |
sm = new AppStateMachine | |
sm.on "a", -> | |
sm.on "a", -> | |
sm.states.should.eql(['a']) | |
describe "callbacks", -> | |
it "should not fire change:states when on('change:states') is added", -> | |
sm = new AppStateMachine | |
sm.success = false | |
sm.on "change:states", -> | |
sm.success = "true" | |
sm.success.should.eql(false) | |
it "should fire change:states when a state is added", -> | |
success = false | |
sm = new AppStateMachine | |
sm.on "change:states", enter: -> | |
sm.success = "true" | |
sm.on "something", -> | |
true | |
sm.success.should.eql("true") | |
it "should run callbacks on state change", -> | |
sm = new AppStateMachine | |
sm.on "asdf", -> sm.success = true | |
sm.go "asdf" | |
sm.success.should.equal(true) | |
it "should remove a state with .off", -> | |
sm = new AppStateMachine() | |
sm.success = false | |
callback = (-> sm.success = true) | |
sm.on "fdsa", callback | |
sm.off "fdsa", callback | |
sm.go "fdsa" | |
sm.success.should.equal(false) | |
it "should run callbacks on state leave", -> | |
sm = new AppStateMachine | |
sm.on "create", enter: (-> sm.success = "false"), leave: (-> sm.success = "true") | |
sm.go "create" | |
sm.go "somethingElse" # this calls create:end | |
sm.success.should.equal("true") | |
it "should run callbacks on state leave", -> | |
sm = new AppStateMachine | |
sm.success = "" | |
sm.on "create", enter: (-> sm.success += "create"), leave: (-> sm.success += " leave") | |
sm.go "create" | |
sm.go "somethingElse" # this calls create:end | |
sm.success.should.equal("create leave") | |
it "should run callbacks added separately on state leave", -> | |
sm = new AppStateMachine | |
sm.on "create:enter", -> sm.success = "false" | |
sm.on "create:leave", -> sm.success = "true" | |
sm.go "create" | |
sm.go "somethingElse" # calls create:leave | |
sm.success.should.equal("true") | |
it "should run a .once once", -> | |
sm = new AppStateMachine | |
i = 0 | |
sm.once "create", -> i++ | |
sm.go "create" | |
sm.go "away" | |
sm.go "create" | |
i.should.eql(1) | |
describe "document.location.hash", -> | |
it "should write on go()", -> | |
sm = new AppStateMachine | |
sm.on "create", enter: (() ->), leave: (() ->) | |
sm.go 'create' | |
document.location.hash.should.eql("#create") | |
it "should read on setup()", -> | |
sm = new AppStateMachine | |
document.location.hash = "#create" | |
sm.on "default", enter: (() ->), leave: (() ->) | |
sm.on "create", enter: (() ->), leave: (() ->) | |
sm.start('default') | |
sm.state.should.eql("create") | |
document.location.hash.should.eql("#create") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment