@quartic/bokehjs
Version:
Interactive, novel data visualization
134 lines (110 loc) • 3.51 kB
text/coffeescript
import {Annotation, AnnotationView} from "./annotation"
import {logger} from "core/logging"
import {div, show, hide, empty} from "core/dom"
import * as p from "core/properties"
export class TooltipView extends AnnotationView
className: "bk-tooltip"
initialize: (options) ->
super(options)
# TODO (bev) really probably need multiple divs
.canvas_overlays.appendChild()
.style.zIndex = 1010
hide()
bind_bokeh_events: () ->
render: () ->
if not .visible
return
_draw_tips: () ->
data = .data
empty()
hide()
if .custom
.classList.add("bk-tooltip-custom")
else
.classList.remove("bk-tooltip-custom")
if data.length == 0
return
for val in data
[vx, vy, content] = val
if .inner_only and not .frame.contains(vx, vy)
continue
tip = div({}, content)
.appendChild(tip)
sx = .model.canvas.vx_to_sx(vx)
sy = .model.canvas.vy_to_sy(vy)
attachment = .attachment
switch attachment
when "horizontal"
width = .frame.width
left = .frame.left
if vx - left < width/2
side = 'right'
else
side = 'left'
when "vertical"
height = .frame.height
bottom = .frame.bottom
if vy - bottom < height/2
side = 'below'
else
side = 'above'
else
side = attachment
.classList.remove("bk-right")
.classList.remove("bk-left")
.classList.remove("bk-above")
.classList.remove("bk-below")
arrow_size = 10 # XXX: keep in sync with less
show() # XXX: {offset,client}Width() gives 0 when display="none"
switch side
when "right"
.classList.add("bk-left")
left = sx + (.offsetWidth - .clientWidth) + arrow_size
top = sy - .offsetHeight/2
when "left"
.classList.add("bk-right")
left = sx - .offsetWidth - arrow_size
top = sy - .offsetHeight/2
when "above"
.classList.add("bk-above")
top = sy + (.offsetHeight - .clientHeight) + arrow_size
left = Math.round(sx - .offsetWidth/2)
when "below"
.classList.add("bk-below")
top = sy - .offsetHeight - arrow_size
left = Math.round(sx - .offsetWidth/2)
if .show_arrow
.classList.add("bk-tooltip-arrow")
# TODO (bev) this is not currently bulletproof. If there are
# two hits, not colocated and one is off the screen, that can
# be problematic
if .childNodes.length > 0
.style.top = "#{top}px"
.style.left = "#{left}px"
else
hide()
export class Tooltip extends Annotation
default_view: TooltipView
type: 'Tooltip'
{
attachment: [ p.String, 'horizontal' ] # TODO enum: "horizontal" | "vertical" | "left" | "right" | "above" | "below"
inner_only: [ p.Bool, true ]
show_arrow: [ p.Bool, true ]
}
{
level: 'overlay'
}
{
data: [ p.Any, [] ]
custom: [ p.Any ]
}
clear: () ->
= []
add: (vx, vy, content) ->
data =
data.push([vx, vy, content])
= data
# TODO (bev) not sure why this is now necessary