# -*- coding: utf-8 -*- from . import core as html5 from . import utils class Button(html5.Button): def __init__(self, txt=None, callback=None, className=None, *args, **kwargs): super().__init__(*args, **kwargs) self["class"] = "btn" if className: self.addClass(className) self["type"] = "button" if txt is not None: self.setText(txt) self.callback = callback self.sinkEvent("onClick") def setText(self, txt): if txt is not None: self.element.innerHTML = txt self["title"] = txt else: self.element.innerHTML = "" self["title"] = "" def onClick(self, event): event.stopPropagation() event.preventDefault() if self.callback is not None: self.callback(self) class Input(html5.Input): def __init__(self, type="text", placeholder=None, callback=None, id=None, focusCallback=None, *args, **kwargs): """ :param type: Input type. Default: "text :param placeholder: Placeholder text. Default: None :param callback: Function to be called onChanged: callback(id, value) :param id: Optional id of the input element. Will be passed to callback :return: """ super().__init__(*args, **kwargs) self["class"] = "input" self["type"] = type if placeholder is not None: self["placeholder"] = placeholder self.callback = callback if id is not None: self["id"] = id self.sinkEvent("onChange") self.focusCallback = focusCallback if focusCallback: self.sinkEvent("onFocus") def onChange(self, event): event.stopPropagation() event.preventDefault() if self.callback is not None: self.callback(self, self["id"], self["value"]) def onFocus(self, event): event.stopPropagation() event.preventDefault() if self.focusCallback is not None: self.focusCallback(self, self["id"], self["value"]) def onDetach(self): super().onDetach() self.callback = None class Popup(html5.Div): def __init__(self, title=None, id=None, className=None, icon=None, enableShortcuts=True, closeable=True, *args, **kwargs): super().__init__("""
""") self.appendChild = self.popupBody.appendChild self.fromHTML = lambda *args, **kwargs: self.popupBody.fromHTML(*args, **kwargs) if kwargs.get("bindTo") else self.popupBody.fromHTML(bindTo=self, *args, **kwargs) self["class"] = "popup popup--center is-active" if className: self.addClass(className) if closeable: closeBtn = Button("×", self.close, className="item-action") closeBtn.removeClass("btn") self.popupHeadItem.appendChild(closeBtn) if title: self.popupHeadline.appendChild(title) if icon: self.popupIcon.appendChild(icon[0]) elif title: self.popupIcon.appendChild(title[0]) else: self.popupIcon.appendChild("Vi") #fixme!!! this _LIBRARY_ is not only used in the Vi... # id can be used to pass information to callbacks self.id = id #FIXME: Implement a global overlay! One popupOverlay next to a list of popups. self.popupOverlay = html5.Div() self.popupOverlay["class"] = "popup-overlay is-active" self.enableShortcuts = enableShortcuts self.onDocumentKeyDownMethod = None self.popupOverlay.appendChild(self) html5.Body().appendChild(self.popupOverlay) #FIXME: Close/Cancel every popup with click on popupCloseBtn without removing the global overlay. def onAttach(self): super(Popup, self).onAttach() if self.enableShortcuts: self.onDocumentKeyDownMethod = self.onDocumentKeyDown # safe reference to method html5.document.addEventListener("keydown", self.onDocumentKeyDownMethod) def onDetach(self): super(Popup, self).onDetach() if self.enableShortcuts: html5.document.removeEventListener("keydown", self.onDocumentKeyDownMethod) def onDocumentKeyDown(self, event): if html5.isEscape(event): self.close() def close(self, *args, **kwargs): html5.Body().removeChild(self.popupOverlay) self.popupOverlay = None class InputDialog(Popup): def __init__(self, text, value="", successHandler=None, abortHandler=None, successLbl="OK", abortLbl="Cancel", placeholder="", *args, **kwargs): super().__init__(*args, **kwargs) self.addClass("popup--inputdialog") self.sinkEvent("onKeyDown", "onKeyUp") self.successHandler = successHandler self.abortHandler = abortHandler self.fromHTML( """
""", vars={ "text": text, "value": value, "placeholder": placeholder } ) # Cancel self.popupFoot.appendChild(Button(abortLbl, self.onCancel, className="btn--cancel btn--danger")) # Okay self.okayBtn = Button(successLbl, self.onOkay, className="btn--okay btn--primary") if not value: self.okayBtn.disable() self.popupFoot.appendChild(self.okayBtn) self.inputElem.focus() def onKeyDown(self, event): if html5.isReturn(event) and self.inputElem["value"]: event.stopPropagation() event.preventDefault() self.onOkay() def onKeyUp(self, event): if self.inputElem["value"]: self.okayBtn.enable() else: self.okayBtn.disable() def onDocumentKeyDown(self, event): if html5.isEscape(event): event.stopPropagation() event.preventDefault() self.onCancel() def onOkay(self, *args, **kwargs): if self.successHandler: self.successHandler(self, self.inputElem["value"]) self.close() def onCancel(self, *args, **kwargs): if self.abortHandler: self.abortHandler(self, self.inputElem["value"]) self.close() class Alert(Popup): """ Just displaying an alerting message box with OK-button. """ def __init__(self, msg, title=None, className=None, okCallback=None, okLabel="OK", icon="!", closeable=True, *args, **kwargs): super().__init__(title, className=None, icon=icon, closeable=closeable, *args, **kwargs) self.addClass("popup--alert") if className: self.addClass(className) self.okCallback = okCallback message = html5.Span() message.addClass("alert-msg") self.popupBody.appendChild(message) if isinstance(msg, str): msg = msg.replace("\n", "
") message.appendChild(msg, bindTo=False) self.sinkEvent("onKeyDown") if closeable: okBtn = Button(okLabel, callback=self.onOkBtnClick) okBtn.addClass("btn--okay btn--primary") self.popupFoot.appendChild(okBtn) okBtn.focus() def drop(self): self.okCallback = None self.close() def onOkBtnClick(self, sender=None): if self.okCallback: self.okCallback(self) self.drop() def onKeyDown(self, event): if html5.isReturn(event): event.stopPropagation() event.preventDefault() self.onOkBtnClick() class YesNoDialog(Popup): def __init__(self, question, title=None, yesCallback=None, noCallback=None, yesLabel="Yes", noLabel="No", icon="?", closeable=False, *args, **kwargs): super().__init__(title, closeable=closeable, icon=icon, *args, **kwargs) self.addClass("popup--yesnodialog") self.yesCallback = yesCallback self.noCallback = noCallback lbl = html5.Span() lbl["class"].append("question") self.popupBody.appendChild(lbl) if isinstance(question, html5.Widget): lbl.appendChild(question) else: utils.textToHtml(lbl, question) if len(noLabel): btnNo = Button(noLabel, className="btn--no", callback=self.onNoClicked) #btnNo["class"].append("btn--no") self.popupFoot.appendChild(btnNo) btnYes = Button(yesLabel, callback=self.onYesClicked) btnYes["class"].append("btn--yes") self.popupFoot.appendChild(btnYes) self.sinkEvent("onKeyDown") btnYes.focus() def onKeyDown(self, event): if html5.isReturn(event): event.stopPropagation() event.preventDefault() self.onYesClicked() def onDocumentKeyDown(self, event): if html5.isEscape(event): event.stopPropagation() event.preventDefault() self.onNoClicked() def drop(self): self.yesCallback = None self.noCallback = None self.close() def onYesClicked(self, *args, **kwargs): if self.yesCallback: self.yesCallback(self) self.drop() def onNoClicked(self, *args, **kwargs): if self.noCallback: self.noCallback(self) self.drop() class SelectDialog(Popup): def __init__(self, prompt, items=None, title=None, okBtn="OK", cancelBtn="Cancel", forceSelect=False, callback=None, *args, **kwargs): super().__init__(title, *args, **kwargs) self["class"].append("popup--selectdialog") self.callback = callback self.items = items assert isinstance(self.items, list) # Prompt if prompt: lbl = html5.Span() lbl["class"].append("prompt") if isinstance(prompt, html5.Widget): lbl.appendChild(prompt) else: utils.textToHtml(lbl, prompt) self.popupBody.appendChild(lbl) # Items if not forceSelect and len(items) <= 3: for idx, item in enumerate(items): if isinstance(item, dict): title = item.get("title") cssc = item.get("class") elif isinstance(item, tuple): title = item[1] cssc = None else: title = item btn = Button(title, callback=self.onAnyBtnClick) btn.idx = idx if cssc: btn.addClass(cssc) self.popupBody.appendChild(btn) else: self.select = html5.Select() self.popupBody.appendChild(self.select) for idx, item in enumerate(items): if isinstance(item, dict): title = item.get("title") elif isinstance(item, tuple): title = item[1] else: title = item opt = html5.Option(title) opt["value"] = str(idx) self.select.appendChild(opt) if okBtn: self.popupFoot.appendChild(Button(okBtn, callback=self.onOkClick)) if cancelBtn: self.popupFoot.appendChild(Button(cancelBtn, callback=self.onCancelClick)) def onAnyBtnClick(self, sender): item = self.items[sender.idx] if isinstance(item, dict) and item.get("callback") and callable(item["callback"]): item["callback"](item) if self.callback: self.callback(item) self.items = None self.close() def onCancelClick(self, sender=None): self.close() def onOkClick(self, sender=None): assert self.select["selectedIndex"] >= 0 item = self.items[int(self.select.children(self.select["selectedIndex"])["value"])] if isinstance(item, dict) and item.get("callback") and callable(item["callback"]): item["callback"](item) if self.callback: self.callback(item) self.items = None self.select = None self.close() class TextareaDialog(Popup): def __init__(self, text, value="", successHandler=None, abortHandler=None, successLbl="OK", abortLbl="Cancel", *args, **kwargs): super().__init__(*args, **kwargs) self["class"].append("popup--textareadialog") self.successHandler = successHandler self.abortHandler = abortHandler span = html5.Span() span.element.innerHTML = text self.popupBody.appendChild(span) self.inputElem = html5.Textarea() self.inputElem["value"] = value self.popupBody.appendChild(self.inputElem) okayBtn = Button(successLbl, self.onOkay) okayBtn["class"].append("btn--okay") self.popupFoot.appendChild(okayBtn) cancelBtn = Button(abortLbl, self.onCancel) cancelBtn["class"].append("btn--cancel") self.popupFoot.appendChild(cancelBtn) self.sinkEvent("onKeyDown") self.inputElem.focus() def onDocumentKeyDown(self, event): if html5.isEscape(event): event.stopPropagation() event.preventDefault() self.onCancel() def onOkay(self, *args, **kwargs): if self.successHandler: self.successHandler(self, self.inputElem["value"]) self.close() def onCancel(self, *args, **kwargs): if self.abortHandler: self.abortHandler(self, self.inputElem["value"]) self.close()