//
// Copyright (c) 2014, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 21 Dec 2014 Andy Frank Creation
//
using concurrent
using dom
**
** Dialog manages a modal window above page content.
**
** See also: [docDomkit]`docDomkit::Modals#dialog`
**
@Js class Dialog : Box
{
new make() : super()
{
this.uid = nextId.val
nextId.val = uid+1
this.style.addClass("domkit-Dialog")
this->tabIndex = 0
}
** 'Str' or 'Elem' content displayed in title bar, or
** 'null' to hide title bar.
Obj? title := null
** Protected sub-class callback invoked directly before dialog is opened.
protected virtual Void onBeforeOpen() {}
** Protected sub-class callback invoked directly after dialog is opened.
protected virtual Void onAfterOpen() {}
** Callback when a key is pressed while Dialog is open, including
** events that where dispatched outside the dialog.
protected Void onKeyDown(|Event e| f) { this.cbKeyDown = f }
** Open this dialog in the current Window. If dialog
** is already open this method does nothing.
Void open()
{
onBeforeOpen
mask := Elem {
it.id = "domkitDialog-mask-$uid"
it->tabIndex = 0
it.style.addClass("domkit-Dialog-mask")
it.style->opacity = "0"
it.onEvent("keydown", false) |e| { cbKeyDown?.call(e) }
}
this.frame = Elem
{
it.style.addClass("domkit-Dialog-frame")
it.style.setAll([
"transform": "scale(0.75)",
"opacity": "0.0"
])
}
if (title != null)
{
telem := title as Elem ?:
Label { it.style.addClass("def-label"); it.text=title.toStr }
frame.add(Elem {
it.style.addClass("domkit-Dialog-title")
it.add(telem)
it.onEvent("mousedown", false) |e| {
e.stop
vp := Win.cur.viewport
doc := Win.cur.doc
off := doc.body.relPos(e.pagePos)
fps := frame.pos
fsz := frame.size
Obj? fmove
Obj? fup
fmove = doc.onEvent("mousemove", true) |de| {
pos := doc.body.relPos(de.pagePos)
fx := (pos.x.toInt - (off.x.toInt - fps.x.toInt)).max(0).min(vp.w.toInt - fsz.w.toInt)
fy := (pos.y.toInt - (off.y.toInt - fps.y.toInt)).max(0).min(vp.h.toInt - fsz.h.toInt)
mask.style->display = "block"
frame.style->position = "absolute"
frame.style->left = "${fx}px"
frame.style->top = "${fy}px"
}
fup = doc.onEvent("mouseup", true) |de| {
de.stop
doc.removeEvent("mousemove", true, fmove)
doc.removeEvent("mouseup", true, fup)
}
}
})
}
frame.add(this)
mask.add(frame)
body := Win.cur.doc.body
body.add(mask)
fireMounted
mask.transition(["opacity":"1"], null, 100ms)
frame.transition([
"transform": "scale(1)",
"opacity": "1"
], null, 100ms) { this.focus; onAfterOpen(); fireOpen }
}
** Close this dialog. If dialog is already closed
** this method does nothing.
Void close()
{
mask := Win.cur.doc.elemById("domkitDialog-mask-$uid")
mask?.transition(["opacity":"0"], null, 100ms)
frame?.transition(["transform": "scale(0.75)", "opacity": "0"], null, 100ms)
{
mask?.parent?.remove(mask)
fireClose
}
}
** Callback when dialog is mounted but not yet visible.
@NoDoc Void onMounted(|This| f) { cbMounted = f }
** Callback when dialog is opened.
Void onOpen(|This| f) { cbOpen = f }
** Callback when popup is closed.
Void onClose(|This| f) { cbClose = f }
private Void fireMounted() { cbMounted?.call(this) }
private Void fireOpen() { cbOpen?.call(this) }
private Void fireClose() { cbClose?.call(this) }
private const Int uid
private static const AtomicRef nextId := AtomicRef(0)
private Elem? frame := null
private Func? cbMounted := null
private Func? cbOpen := null
private Func? cbClose := null
private Func? cbKeyDown := null
}