This repo contains code to mirror other repos. It also contains the code that is getting mirrored.
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

476 linhas
12 KiB

  1. # -*- coding: utf-8 -*-
  2. from . import core as html5
  3. from . import utils
  4. class Button(html5.Button):
  5. def __init__(self, txt=None, callback=None, className=None, *args, **kwargs):
  6. super().__init__(*args, **kwargs)
  7. self["class"] = "btn"
  8. if className:
  9. self.addClass(className)
  10. self["type"] = "button"
  11. if txt is not None:
  12. self.setText(txt)
  13. self.callback = callback
  14. self.sinkEvent("onClick")
  15. def setText(self, txt):
  16. if txt is not None:
  17. self.element.innerHTML = txt
  18. self["title"] = txt
  19. else:
  20. self.element.innerHTML = ""
  21. self["title"] = ""
  22. def onClick(self, event):
  23. event.stopPropagation()
  24. event.preventDefault()
  25. if self.callback is not None:
  26. self.callback(self)
  27. class Input(html5.Input):
  28. def __init__(self, type="text", placeholder=None, callback=None, id=None, focusCallback=None, *args, **kwargs):
  29. """
  30. :param type: Input type. Default: "text
  31. :param placeholder: Placeholder text. Default: None
  32. :param callback: Function to be called onChanged: callback(id, value)
  33. :param id: Optional id of the input element. Will be passed to callback
  34. :return:
  35. """
  36. super().__init__(*args, **kwargs)
  37. self["class"] = "input"
  38. self["type"] = type
  39. if placeholder is not None:
  40. self["placeholder"] = placeholder
  41. self.callback = callback
  42. if id is not None:
  43. self["id"] = id
  44. self.sinkEvent("onChange")
  45. self.focusCallback = focusCallback
  46. if focusCallback:
  47. self.sinkEvent("onFocus")
  48. def onChange(self, event):
  49. event.stopPropagation()
  50. event.preventDefault()
  51. if self.callback is not None:
  52. self.callback(self, self["id"], self["value"])
  53. def onFocus(self, event):
  54. event.stopPropagation()
  55. event.preventDefault()
  56. if self.focusCallback is not None:
  57. self.focusCallback(self, self["id"], self["value"])
  58. def onDetach(self):
  59. super().onDetach()
  60. self.callback = None
  61. class Popup(html5.Div):
  62. def __init__(self, title=None, id=None, className=None, icon=None, enableShortcuts=True, closeable=True, *args, **kwargs):
  63. super().__init__("""
  64. <div class="box" [name]="popupBox">
  65. <div class="box-head" [name]="popupHead">
  66. <div class="item" [name]="popupHeadItem">
  67. <div class="item-image">
  68. <i class="i i--small" [name]="popupIcon"></i>
  69. </div>
  70. <div class="item-content">
  71. <div class="item-headline" [name]="popupHeadline"></div>
  72. </div>
  73. </div>
  74. </div>
  75. <div class="box-body box--content" [name]="popupBody"></div>
  76. <div class="box-foot box--content bar" [name]="popupFoot"></div>
  77. </div>
  78. """)
  79. self.appendChild = self.popupBody.appendChild
  80. self.fromHTML = lambda *args, **kwargs: self.popupBody.fromHTML(*args, **kwargs) if kwargs.get("bindTo") else self.popupBody.fromHTML(bindTo=self, *args, **kwargs)
  81. self["class"] = "popup popup--center is-active"
  82. if className:
  83. self.addClass(className)
  84. if closeable:
  85. closeBtn = Button("&times;", self.close, className="item-action")
  86. closeBtn.removeClass("btn")
  87. self.popupHeadItem.appendChild(closeBtn)
  88. if title:
  89. self.popupHeadline.appendChild(title)
  90. if icon:
  91. self.popupIcon.appendChild(icon[0])
  92. elif title:
  93. self.popupIcon.appendChild(title[0])
  94. else:
  95. self.popupIcon.appendChild("Vi") #fixme!!! this _LIBRARY_ is not only used in the Vi...
  96. # id can be used to pass information to callbacks
  97. self.id = id
  98. #FIXME: Implement a global overlay! One popupOverlay next to a list of popups.
  99. self.popupOverlay = html5.Div()
  100. self.popupOverlay["class"] = "popup-overlay is-active"
  101. self.enableShortcuts = enableShortcuts
  102. self.onDocumentKeyDownMethod = None
  103. self.popupOverlay.appendChild(self)
  104. html5.Body().appendChild(self.popupOverlay)
  105. #FIXME: Close/Cancel every popup with click on popupCloseBtn without removing the global overlay.
  106. def onAttach(self):
  107. super(Popup, self).onAttach()
  108. if self.enableShortcuts:
  109. self.onDocumentKeyDownMethod = self.onDocumentKeyDown # safe reference to method
  110. html5.document.addEventListener("keydown", self.onDocumentKeyDownMethod)
  111. def onDetach(self):
  112. super(Popup, self).onDetach()
  113. if self.enableShortcuts:
  114. html5.document.removeEventListener("keydown", self.onDocumentKeyDownMethod)
  115. def onDocumentKeyDown(self, event):
  116. if html5.isEscape(event):
  117. self.close()
  118. def close(self, *args, **kwargs):
  119. html5.Body().removeChild(self.popupOverlay)
  120. self.popupOverlay = None
  121. class InputDialog(Popup):
  122. def __init__(self, text, value="", successHandler=None, abortHandler=None,
  123. successLbl="OK", abortLbl="Cancel", placeholder="", *args, **kwargs):
  124. super().__init__(*args, **kwargs)
  125. self.addClass("popup--inputdialog")
  126. self.sinkEvent("onKeyDown", "onKeyUp")
  127. self.successHandler = successHandler
  128. self.abortHandler = abortHandler
  129. self.fromHTML(
  130. """
  131. <div class="input-group">
  132. <label class="label">
  133. {{text}}
  134. </label>
  135. <input class="input" [name]="inputElem" value="{{value}}" placeholder="{{placeholder}}" />
  136. </div>
  137. """,
  138. vars={
  139. "text": text,
  140. "value": value,
  141. "placeholder": placeholder
  142. }
  143. )
  144. # Cancel
  145. self.popupFoot.appendChild(Button(abortLbl, self.onCancel, className="btn--cancel btn--danger"))
  146. # Okay
  147. self.okayBtn = Button(successLbl, self.onOkay, className="btn--okay btn--primary")
  148. if not value:
  149. self.okayBtn.disable()
  150. self.popupFoot.appendChild(self.okayBtn)
  151. self.inputElem.focus()
  152. def onKeyDown(self, event):
  153. if html5.isReturn(event) and self.inputElem["value"]:
  154. event.stopPropagation()
  155. event.preventDefault()
  156. self.onOkay()
  157. def onKeyUp(self, event):
  158. if self.inputElem["value"]:
  159. self.okayBtn.enable()
  160. else:
  161. self.okayBtn.disable()
  162. def onDocumentKeyDown(self, event):
  163. if html5.isEscape(event):
  164. event.stopPropagation()
  165. event.preventDefault()
  166. self.onCancel()
  167. def onOkay(self, *args, **kwargs):
  168. if self.successHandler:
  169. self.successHandler(self, self.inputElem["value"])
  170. self.close()
  171. def onCancel(self, *args, **kwargs):
  172. if self.abortHandler:
  173. self.abortHandler(self, self.inputElem["value"])
  174. self.close()
  175. class Alert(Popup):
  176. """
  177. Just displaying an alerting message box with OK-button.
  178. """
  179. def __init__(self, msg, title=None, className=None, okCallback=None, okLabel="OK", icon="!", closeable=True, *args, **kwargs):
  180. super().__init__(title, className=None, icon=icon, closeable=closeable, *args, **kwargs)
  181. self.addClass("popup--alert")
  182. if className:
  183. self.addClass(className)
  184. self.okCallback = okCallback
  185. message = html5.Span()
  186. message.addClass("alert-msg")
  187. self.popupBody.appendChild(message)
  188. if isinstance(msg, str):
  189. msg = msg.replace("\n", "<br>")
  190. message.appendChild(msg, bindTo=False)
  191. self.sinkEvent("onKeyDown")
  192. if closeable:
  193. okBtn = Button(okLabel, callback=self.onOkBtnClick)
  194. okBtn.addClass("btn--okay btn--primary")
  195. self.popupFoot.appendChild(okBtn)
  196. okBtn.focus()
  197. def drop(self):
  198. self.okCallback = None
  199. self.close()
  200. def onOkBtnClick(self, sender=None):
  201. if self.okCallback:
  202. self.okCallback(self)
  203. self.drop()
  204. def onKeyDown(self, event):
  205. if html5.isReturn(event):
  206. event.stopPropagation()
  207. event.preventDefault()
  208. self.onOkBtnClick()
  209. class YesNoDialog(Popup):
  210. def __init__(self, question, title=None, yesCallback=None, noCallback=None,
  211. yesLabel="Yes", noLabel="No", icon="?",
  212. closeable=False, *args, **kwargs):
  213. super().__init__(title, closeable=closeable, icon=icon, *args, **kwargs)
  214. self.addClass("popup--yesnodialog")
  215. self.yesCallback = yesCallback
  216. self.noCallback = noCallback
  217. lbl = html5.Span()
  218. lbl["class"].append("question")
  219. self.popupBody.appendChild(lbl)
  220. if isinstance(question, html5.Widget):
  221. lbl.appendChild(question)
  222. else:
  223. utils.textToHtml(lbl, question)
  224. if len(noLabel):
  225. btnNo = Button(noLabel, className="btn--no", callback=self.onNoClicked)
  226. #btnNo["class"].append("btn--no")
  227. self.popupFoot.appendChild(btnNo)
  228. btnYes = Button(yesLabel, callback=self.onYesClicked)
  229. btnYes["class"].append("btn--yes")
  230. self.popupFoot.appendChild(btnYes)
  231. self.sinkEvent("onKeyDown")
  232. btnYes.focus()
  233. def onKeyDown(self, event):
  234. if html5.isReturn(event):
  235. event.stopPropagation()
  236. event.preventDefault()
  237. self.onYesClicked()
  238. def onDocumentKeyDown(self, event):
  239. if html5.isEscape(event):
  240. event.stopPropagation()
  241. event.preventDefault()
  242. self.onNoClicked()
  243. def drop(self):
  244. self.yesCallback = None
  245. self.noCallback = None
  246. self.close()
  247. def onYesClicked(self, *args, **kwargs):
  248. if self.yesCallback:
  249. self.yesCallback(self)
  250. self.drop()
  251. def onNoClicked(self, *args, **kwargs):
  252. if self.noCallback:
  253. self.noCallback(self)
  254. self.drop()
  255. class SelectDialog(Popup):
  256. def __init__(self, prompt, items=None, title=None, okBtn="OK", cancelBtn="Cancel", forceSelect=False,
  257. callback=None, *args, **kwargs):
  258. super().__init__(title, *args, **kwargs)
  259. self["class"].append("popup--selectdialog")
  260. self.callback = callback
  261. self.items = items
  262. assert isinstance(self.items, list)
  263. # Prompt
  264. if prompt:
  265. lbl = html5.Span()
  266. lbl["class"].append("prompt")
  267. if isinstance(prompt, html5.Widget):
  268. lbl.appendChild(prompt)
  269. else:
  270. utils.textToHtml(lbl, prompt)
  271. self.popupBody.appendChild(lbl)
  272. # Items
  273. if not forceSelect and len(items) <= 3:
  274. for idx, item in enumerate(items):
  275. if isinstance(item, dict):
  276. title = item.get("title")
  277. cssc = item.get("class")
  278. elif isinstance(item, tuple):
  279. title = item[1]
  280. cssc = None
  281. else:
  282. title = item
  283. btn = Button(title, callback=self.onAnyBtnClick)
  284. btn.idx = idx
  285. if cssc:
  286. btn.addClass(cssc)
  287. self.popupBody.appendChild(btn)
  288. else:
  289. self.select = html5.Select()
  290. self.popupBody.appendChild(self.select)
  291. for idx, item in enumerate(items):
  292. if isinstance(item, dict):
  293. title = item.get("title")
  294. elif isinstance(item, tuple):
  295. title = item[1]
  296. else:
  297. title = item
  298. opt = html5.Option(title)
  299. opt["value"] = str(idx)
  300. self.select.appendChild(opt)
  301. if okBtn:
  302. self.popupFoot.appendChild(Button(okBtn, callback=self.onOkClick))
  303. if cancelBtn:
  304. self.popupFoot.appendChild(Button(cancelBtn, callback=self.onCancelClick))
  305. def onAnyBtnClick(self, sender):
  306. item = self.items[sender.idx]
  307. if isinstance(item, dict) and item.get("callback") and callable(item["callback"]):
  308. item["callback"](item)
  309. if self.callback:
  310. self.callback(item)
  311. self.items = None
  312. self.close()
  313. def onCancelClick(self, sender=None):
  314. self.close()
  315. def onOkClick(self, sender=None):
  316. assert self.select["selectedIndex"] >= 0
  317. item = self.items[int(self.select.children(self.select["selectedIndex"])["value"])]
  318. if isinstance(item, dict) and item.get("callback") and callable(item["callback"]):
  319. item["callback"](item)
  320. if self.callback:
  321. self.callback(item)
  322. self.items = None
  323. self.select = None
  324. self.close()
  325. class TextareaDialog(Popup):
  326. def __init__(self, text, value="", successHandler=None, abortHandler=None, successLbl="OK", abortLbl="Cancel",
  327. *args, **kwargs):
  328. super().__init__(*args, **kwargs)
  329. self["class"].append("popup--textareadialog")
  330. self.successHandler = successHandler
  331. self.abortHandler = abortHandler
  332. span = html5.Span()
  333. span.element.innerHTML = text
  334. self.popupBody.appendChild(span)
  335. self.inputElem = html5.Textarea()
  336. self.inputElem["value"] = value
  337. self.popupBody.appendChild(self.inputElem)
  338. okayBtn = Button(successLbl, self.onOkay)
  339. okayBtn["class"].append("btn--okay")
  340. self.popupFoot.appendChild(okayBtn)
  341. cancelBtn = Button(abortLbl, self.onCancel)
  342. cancelBtn["class"].append("btn--cancel")
  343. self.popupFoot.appendChild(cancelBtn)
  344. self.sinkEvent("onKeyDown")
  345. self.inputElem.focus()
  346. def onDocumentKeyDown(self, event):
  347. if html5.isEscape(event):
  348. event.stopPropagation()
  349. event.preventDefault()
  350. self.onCancel()
  351. def onOkay(self, *args, **kwargs):
  352. if self.successHandler:
  353. self.successHandler(self, self.inputElem["value"])
  354. self.close()
  355. def onCancel(self, *args, **kwargs):
  356. if self.abortHandler:
  357. self.abortHandler(self, self.inputElem["value"])
  358. self.close()