Tuesday, July 3, 2018

Daily report cutter part II

Daily report cutter part II



Its always nice to come home after a tired day, sit down, make a tea and do some CoffeeScript to calm yourself down. Its like really your first frozen girlfriend. You do whatever you want and you even can peep under the skirt and its still stable.


Im still experimenting the language but its 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. Ive got some taste from the flexibility of CoffeeScript as well - how you can define the exact same thing in different ways. (Wait, isnt that JavaScript? Ups.)

So I cleaned the daily report cutter and added a little. I had to realize its tough to make a UI controller. I wanted to use a library but I havent 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


Its 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 - thats 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 Im sure you think its not the best structure - and I agree. Thats the brilliant :) You still can make it better. Its just a bit tricky when youre full with the dinner and want to watch the same adventure time episode again and again. Check out the current source on GitHub.

---

Peter

go to link download