It's always nice to come home after a tired day, sit down, make a tea and do some CoffeeScript to calm yourself down. It's like really your first frozen girlfriend. You do whatever you want and you even can peep under the skirt and it's still stable.
I'm still experimenting the language but it's already obvious how easy to work with a well structured syntax. The old cluttered function macaroni is now a clean (more or less) class collection. I've got some taste from the flexibility of CoffeeScript as well - how you can define the exact same thing in different ways. (Wait, isn't that JavaScript? Ups.)
So I cleaned the daily report cutter and added a little. I had to realize it's tough to make a UI controller. I wanted to use a library but I haven't found anything that supports multiple scrollers on the same controller.
A little aperitif from the hipster colored demo:
The current structure is very raw. We have a Label class that is responsible about the label items and their data:
class Label @labels: [] @width: 100 html: null constructor: (position) -> Label.labels.push(this) @html = jQuery('<input type="textfield" value="activity"/>') jQuery('#label_bar').append(@html) jQuery(@html).change -> StageManager.updateReport() StageManager.updateElements() offsetX: () -> jQuery(@html).offset().left setOffsetX: (offsetX) -> jQuery(@html).css('left', offsetX) kill: () -> jQuery(@html).remove() _this = @ Label.labels = Label.labels.filter (l) -> l != _this text: () -> jQuery(@html).val()
The other class is for the divider:
class Divider @dividers: [] @width: 10 @widthHalf: @width >> 1 @counter: 0 @activeDivider: null html: null count: @counter++ constructor: (coordX) -> dividerWidthHalf = Divider.width >> 1 Divider.dividers.push(this) @html = jQuery('<div class="divider"/>') @html.attr('id', 'divider_' + Divider.counter) @setOffsetX(coordX - dividerWidthHalf) @html.mousedown (e) => @mouseDown(e) @html.click (e) => e.stopPropagation() @html.dblclick StageManager.doubleClickOnDivider jQuery('#time_bar').append(@html) Divider.activeDivider = this Divider.counter++ offsetX: () -> jQuery(@html).offset().left setOffsetX: (offsetX) -> @html.css('left', offsetX) mouseDown: (e) -> Divider.activeDivider = @ e.stopPropagation() getHTMLAttr: (param) -> jQuery(@html).attr(param) kill: () -> _this = @ Divider.dividers = Divider.dividers.filter (d) -> d != _this jQuery(@html).remove() index: () -> for divider, i in Divider.dividers if divider == @ return i -1 @getByID: (id) -> for divider in Divider.dividers if divider.getHTMLAttr('id') == id return divider null @sort: () -> Divider.dividers.sort (a, b) -> jQuery(a.html).offset().left > jQuery(b.html).offset().left
It's a bit more but on the UI it also more in the central of interactions. Having these 2 objects on the scene is not really enough. When adding dividers you want to adjust on the label set also. And the logical solution is a higher level controller - that's the stage manager:
class StageManager @updateElements: () -> labelWidthHalf = Label.width >> 1 if Divider.dividers.length == 0 bar_width_half = jQuery('#label_bar').width() >> 1 Label.labels[0].setOffsetX(bar_width_half - labelWidthHalf) else prev_x = 0 for divider, i in Divider.dividers divider_x = divider.offsetX() label_x = (divider_x + prev_x) * 0.5 - labelWidthHalf Label.labels[i].setOffsetX(label_x) prev_x = divider_x label_i = Label.labels[i] label_i.setOffsetX((jQuery('#label_bar').width() + prev_x) * 0.5 - labelWidthHalf) @updateReport() @updateReport: -> jQuery('#report').html('<ol />') for label in Label.labels text = label.text() jQuery('#report ol').append('<li>' + text + ' ' + label.timeLabel() + '</li>') @moveBarMouseMove: (e) -> if Divider.activeDivider Divider.activeDivider.setOffsetX(e.offsetX - Divider.widthHalf) @timeBarMouseUp: () -> Divider.activeDivider = null Divider.sort() StageManager.updateElements() @onClickTimeBar: (e) -> new Divider(e.offsetX) new Label(0) @doubleClickOnDivider: (e) -> id = jQuery(e.delegateTarget).attr('id') divider_to_kill = Divider.getByID(id) idx_of_divider = divider_to_kill.index() Label.labels[idx_of_divider].kill() divider_to_kill.kill() @getTimeInterval: () -> { start: 540, end: 1080 } @timeBarWidth: -> jQuery('#label_bar').width()
It cares about the high level events, layout of labels and dividers and the app lifecycle. The whole app initialization is pretty simple:
jQuery -> jQuery('#time_bar').click StageManager.onClickTimeBar jQuery('#time_bar').mouseup StageManager.timeBarMouseUp jQuery('#move_layer').mousemove StageManager.moveBarMouseMove new Label()
And I'm sure you think it's not the best structure - and I agree. That's the brilliant :) You still can make it better. It's just a bit tricky when you're full with the dinner and want to watch the same adventure time episode again and again. Check out the current source on GitHub.
---
Peter
No comments:
Post a Comment
Note: only a member of this blog may post a comment.