Skip to content

Instantly share code, notes, and snippets.

@andrewliebchen
Last active February 4, 2018 13:08
Show Gist options
  • Save andrewliebchen/8de1548569b70e8f2b6eb661e498458c to your computer and use it in GitHub Desktop.
Save andrewliebchen/8de1548569b70e8f2b6eb661e498458c to your computer and use it in GitHub Desktop.
React-style state machine for Framer
{ Machine } = require 'stateMachine'
{ TextLayer } = require 'TextLayer' # https://github.com/awt2542/textLayer-for-Framer
InputModule = require 'input' # https://github.com/ajimix/Input-Framer
Framer.Defaults.Layer =
backgroundColor: null
color: 'black'
p =
todo:
width: Screen.width * 0.75
height: 40
textStyle:
fontSize: 16
fontFamily: '-apple-system'
color: 'black'
lineHeight: '40px'
state =
todos: [
{
label: 'Make a state machine for Framer'
done: true
}, {
label: 'Create a todo app in Framer'
done: false
}
]
layers = () ->
todosCompleted = _.sumBy(state.todos, {done: true}) / state.todos.length
todoList = new Layer
x: Align.center
y: Align.top(p.todo.height * 1.5)
width: p.todo.width
input = new InputModule.Input
parent: todoList
y: (p.todo.height + 10) * state.todos.length
width: p.todo.width
height: p.todo.height
fontSize: 16
borderWidth: 1
borderColor: 'black'
placeholder: 'New todo'
input.style =
'line-height': '40px'
'box-sizing': 'border-box'
input.form.addEventListener "submit", (event) ->
event.preventDefault()
app.setState("todos[#{state.todos.length}].label", input.value)
if todosCompleted == 1
banner = new TextLayer
bannerTextStyle =
text: 'All done! 👍' 
width: p.todo.width
x: Align.center
y: Align.top(20)
fontWeight: 'bold'
_.assign banner, _.extend bannerTextStyle, p.textStyle
else
progress = new Layer
width: p.todo.width
height: 10
backgroundColor: '#ddd'
x: Align.center
y: Align.top(p.todo.height)
progressBar = new Layer
parent: progress
width: todosCompleted * progress.width
height: progress.height
backgroundColor: 'black'
for item, index in state.todos
do (item, index) ->
todo = new Layer
parent: todoList
borderWidth: 1
borderColor: 'black'
width: p.todo.width
height: p.todo.height
y: Align.top((p.todo.height + 10) * index)
todo.onClick ->
app.setState("todos[#{index}].done", !item.done)
checkBox = new Layer
parent: todo
borderWidth: 1
borderColor: 'black'
size: p.todo.height / 2
x: Align.left(9)
y: Align.center
backgroundColor: if item.done then 'black' else null
todoText = new TextLayer
todoTextStyle =
parent: todo
text: item.label
width: todo.width
paddingLeft: p.todo.height
style:
'text-decoration': if item.done then 'line-through' else 'none'
_.assign todoText, _.assign todoTextStyle, p.textStyle
app = new Machine
state: state
layers: layers
framer: Framer
class exports.Machine
constructor: (options) ->
@options = options
# Render the initial view
@render()
render: -> @options.layers()
killLayers: () ->
# Destroy all layers
# It'd be cool to do some diffing here...
for layer in @options.framer.CurrentContext._layers
layer.destroy()
setState: (key, value) ->
# Set the state
# Need to iterate through theses
@options.state = _.set @options.state, key, value
@killLayers()
@render()
toggleState: (key) ->
# Get the current value
@value = @options.state[key]
# Set the value to the opposite
@options.state = _.set @options.state, key, !@value
@killLayers()
@render()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment