project-nexus
Version:
A hub for all your programming projects
152 lines (140 loc) • 5.36 kB
text/coffeescript
class extends React.Component
constructor: ->
=
menu_open: no
pressed: no
pressed_and_held: no
mouse_left: no
holding_tid: null
active_menu_item_i: null
render: ->
{action, title, icon, menu} =
has_menu = menu?.length
if icon or menu
icon ?= "octicon-primitive-dot"
E ".launcher",
class:
"has-menu": has_menu
"menu-open": .menu_open
E "button.button.icobutton",
class: "no-primary-action": not action
onClick: =>
clearTimeout .holding_tid
if action
unless .mouse_left
pressed_and_held: no, menu_open: no
action()
onContextMenu: (e)=>
e.preventDefault()
menu_open: yes if has_menu
onMouseDown: (e)=>
mouse_left: no
e.preventDefault()
clearTimeout .holding_tid
if has_menu and e.button is 0
pressed: yes
holding_tid: setTimeout =>
pressed_and_held: yes, menu_open: yes
, 500
onMouseLeave: =>
mouse_left: yes
if has_menu and .pressed
clearTimeout .holding_tid
pressed_and_held: yes, menu_open: yes
title: title
E "i", class: [icon, ("mega-octicon" if icon?.match /octicon-/)]
if has_menu
E "ul.launcher-context-menu.context-menu.menu.window-frame.csd",
style: display: "block"
class: "open" if .menu_open
for item, i in menu then do (item, i)=>
hovered = .hovered_menu_item_i is i
E "li.menuitem",
key: i
class:
hover: hovered
active: hovered and .pressed_and_held
selected: hovered and .pressed_and_held
onMouseEnter: =>
hovered_menu_item_i: i
onMouseLeave: =>
hovered_menu_item_i: null
onMouseDown: =>
pressed_and_held: yes
onMouseUp: (e)=>
if .pressed_and_held
e.preventDefault() # prevent click handler from duplicating item.action() call
menu_open: no
item.action()
onClick: =>
menu_open: no
item.action()
item.text ? item.name
# @TODO: keyboard navigation
else
E ".launcher"
componentDidMount: ->
element = React.findDOMNode @
menu = element.querySelector ".launcher-context-menu"
if menu
scroller = element.closest ".projects"
= (e)=>
# NOTE: e.target can be the Window for blur events
if e.target instanceof Node and menu.contains e.target
e.preventDefault()
else
menu_open: no
window.addEventListener "mousedown",
window.addEventListener "blur",
window.addEventListener "mouseup", = (e)=>
pressed: no
if .pressed_and_held
setTimeout => pressed_and_held: no
else
(e)
window.addEventListener "keydown", = (e)=>
menu_open: no if e.keyCode is 27 # Escape
= =>
return unless menu.classList.contains "open"
menu.style.display = "block" # so menu_rect.height isn't 0
launcher_rect = element.getBoundingClientRect()
menu_rect = menu.getBoundingClientRect()
scroller_rect = scroller.getBoundingClientRect()
menu.style.display = ""
# if it would go off the screen on the bottom
if launcher_rect.bottom + menu_rect.height > scroller_rect.bottom
# attach to the top of the launcher
menu.style.top = "#{launcher_rect.top - menu_rect.height}px"
else
# attach to the bottom of the launcher
menu.style.top = "#{launcher_rect.bottom}px"
menu.style.left = "#{launcher_rect.left}px"
scroller.addEventListener "scroll",
window.addEventListener "resize",
window.addEventListener "keydown",
window.addEventListener "click",
window.addEventListener "mousedown",
window.addEventListener "mouseup",
componentWillUnmount: ->
element = React.findDOMNode @
menu = element.querySelector ".launcher-context-menu"
if menu
scroller = element.closest ".projects"
window.removeEventListener "mousedown",
window.removeEventListener "blur",
window.removeEventListener "mouseup",
window.removeEventListener "keydown",
scroller?.removeEventListener "scroll",
window.removeEventListener "resize",
window.removeEventListener "keydown",
window.removeEventListener "click",
window.removeEventListener "mousedown",
window.removeEventListener "mouseup",
componentDidUpdate: ->
if .active
element = React.findDOMNode @
menu = element.querySelector ".launcher-context-menu"
if .pressed_and_held
menu.children[.hovered_menu_item_i].focus()
?()