110 lines
2.7 KiB
CoffeeScript
110 lines
2.7 KiB
CoffeeScript
class Menu
|
|
constructor: ->
|
|
@visible = false
|
|
@items = []
|
|
@node = null
|
|
@height = 0
|
|
@direction = "bottom"
|
|
|
|
show: =>
|
|
window.visible_menu?.hide()
|
|
@visible = true
|
|
window.visible_menu = @
|
|
@direction = @getDirection()
|
|
|
|
hide: =>
|
|
@visible = false
|
|
|
|
toggle: =>
|
|
if @visible
|
|
@hide()
|
|
else
|
|
@show()
|
|
Page.projector.scheduleRender()
|
|
|
|
|
|
addItem: (title, cb, selected=false) ->
|
|
@items.push([title, cb, selected])
|
|
|
|
|
|
storeNode: (node) =>
|
|
@node = node
|
|
# Animate visible
|
|
if @visible
|
|
node.className = node.className.replace("visible", "")
|
|
setTimeout (=>
|
|
node.className += " visible"
|
|
node.attributes.style.value = @getStyle()
|
|
), 20
|
|
node.style.maxHeight = "none"
|
|
@height = node.offsetHeight
|
|
node.style.maxHeight = "0px"
|
|
@direction = @getDirection()
|
|
|
|
getDirection: =>
|
|
if @node and @node.parentNode.getBoundingClientRect().top + @height + 60 > document.body.clientHeight and @node.parentNode.getBoundingClientRect().top - @height > 0
|
|
return "top"
|
|
else
|
|
return "bottom"
|
|
|
|
handleClick: (e) =>
|
|
keep_menu = false
|
|
for item in @items
|
|
[title, cb, selected] = item
|
|
if title == e.currentTarget.textContent or e.currentTarget["data-title"] == title
|
|
keep_menu = cb?(item)
|
|
break
|
|
if keep_menu != true and cb != null
|
|
@hide()
|
|
return false
|
|
|
|
renderItem: (item) =>
|
|
[title, cb, selected] = item
|
|
if typeof(selected) == "function"
|
|
selected = selected()
|
|
|
|
if title == "---"
|
|
return h("div.menu-item-separator", {key: Time.timestamp()})
|
|
else
|
|
if cb == null
|
|
href = undefined
|
|
onclick = @handleClick
|
|
else if typeof(cb) == "string" # Url
|
|
href = cb
|
|
onclick = true
|
|
else # Callback
|
|
href = "#"+title
|
|
onclick = @handleClick
|
|
classes = {
|
|
"selected": selected,
|
|
"noaction": (cb == null)
|
|
}
|
|
return h("a.menu-item", {href: href, onclick: onclick, "data-title": title, key: title, classes: classes}, title)
|
|
|
|
getStyle: =>
|
|
if @visible
|
|
max_height = @height
|
|
else
|
|
max_height = 0
|
|
style = "max-height: #{max_height}px"
|
|
if @direction == "top"
|
|
style += ";margin-top: #{0 - @height - 50}px"
|
|
else
|
|
style += ";margin-top: 0px"
|
|
return style
|
|
|
|
render: (class_name="") =>
|
|
if @visible or @node
|
|
h("div.menu#{class_name}", {classes: {"visible": @visible}, style: @getStyle(), afterCreate: @storeNode}, @items.map(@renderItem))
|
|
|
|
window.Menu = Menu
|
|
|
|
# Hide menu on outside click
|
|
document.body.addEventListener "mouseup", (e) ->
|
|
if not window.visible_menu or not window.visible_menu.node
|
|
return false
|
|
menu_node = window.visible_menu.node
|
|
menu_parents = [menu_node, menu_node.parentNode]
|
|
if e.target.parentNode not in menu_parents and e.target.parentNode.parentNode not in menu_parents
|
|
window.visible_menu.hide()
|
|
Page.projector.scheduleRender()
|