Overlay.scala 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. package com.weEat.modules
  2. import com.raquo.laminar.api.L._
  3. import org.scalajs.dom.document
  4. import scala.concurrent.ExecutionContext
  5. import scala.concurrent.Future
  6. import scala.concurrent.Promise
  7. import scala.util.Try
  8. object Overlay {
  9. implicit val ctx = org.scalajs.macrotaskexecutor.MacrotaskExecutor.Implicits.global
  10. object Closed extends Throwable()
  11. private class _PromiseWrapper[T](promise: Promise[T], root: RootNode)(implicit ctx: ExecutionContext) extends Promise[T] {
  12. lazy val future = {
  13. val result = promise.future
  14. result.onComplete((_) => root.unmount())
  15. result
  16. }
  17. def isCompleted = promise.isCompleted
  18. def tryComplete(result: Try[T]) = promise.tryComplete(result)
  19. }
  20. private def _toLoadingNode(content: Future[Node]) =
  21. Signal.fromFuture(content) map {
  22. case Some(node) => node
  23. case None => div(cls := "loader",
  24. span(),
  25. span(),
  26. span(),
  27. span(),
  28. span(),
  29. span()
  30. )
  31. }
  32. private val _backgroundStyle = Seq(
  33. backgroundColor := "rgba(0, 0, 0, 0.5)",
  34. position := "fixed",
  35. textAlign := "center",
  36. top := "0",
  37. left := "0",
  38. bottom := "0",
  39. right := "0",
  40. zIndex := 99
  41. )
  42. private val _popupWindowStyle = Seq(
  43. cls := "p-4 position-relative",
  44. backgroundColor := "#ffffff",
  45. borderRadius := "1em",
  46. top := "50%",
  47. transform := "translateY(-50%)",
  48. maxHeight := "80%",
  49. overflowY := "auto",
  50. display := "inline-block",
  51. verticalAlign := "middle",
  52. maxWidth := "50%"
  53. )
  54. private def _internal[T](content: Node)(implicit ctx: ExecutionContext) = {
  55. val promise = Promise[T]()
  56. val overlay = div(
  57. _backgroundStyle,
  58. div(
  59. _popupWindowStyle,
  60. content
  61. )
  62. )
  63. overlay.amend(onClick.stopPropagation --> ((event) =>
  64. if (event.target == overlay.ref) {
  65. promise.failure(Closed)
  66. }
  67. ))
  68. new _PromiseWrapper(promise, render(document.body, overlay))
  69. }
  70. def confirm[T](
  71. content: Node,
  72. readyToSubmit: Signal[Boolean] = Val(true)
  73. )(confirmFn: () => T) = {
  74. val cancelBtn = button(
  75. typ := "button",
  76. cls := "btn btn-light",
  77. "Cancel"
  78. )
  79. val submitBtn = button(
  80. typ := "button",
  81. cls := "btn btn-success",
  82. disabled <-- readyToSubmit.map(!_),
  83. "Submit"
  84. )
  85. val promise = _internal[T](div(
  86. content,
  87. div(
  88. cls := "pt-2 pb-2",
  89. textAlign := "right",
  90. cancelBtn,
  91. submitBtn
  92. )
  93. ))
  94. cancelBtn.amend(
  95. onClick.stopPropagation --> ((_) => promise.failure(Closed))
  96. )
  97. submitBtn.amend(
  98. onClick.stopPropagation --> ((_) => promise.success(confirmFn()))
  99. )
  100. promise.future
  101. }
  102. def confirmFuture[T](
  103. content: Future[Node],
  104. readyToSubmit: Signal[Boolean] = Val(true)
  105. )(confirmFn: () => Future[T])(implicit ctx: ExecutionContext) = {
  106. val cancelBtn = button(
  107. typ := "button",
  108. cls := "btn btn-light",
  109. "Cancel"
  110. )
  111. val submitBtn = button(
  112. typ := "button",
  113. cls := "btn btn-success",
  114. disabled <-- readyToSubmit.map(!_),
  115. "Submit"
  116. )
  117. val promise = _internal[T](div(
  118. child <-- _toLoadingNode(content),
  119. div(
  120. cls := "pt-2 pb-2",
  121. textAlign := "right",
  122. cancelBtn,
  123. submitBtn
  124. )
  125. ))
  126. cancelBtn.amend(
  127. onClick.stopPropagation --> ((_) => {promise.failure(Closed)})
  128. )
  129. submitBtn.amend(
  130. onClick.stopPropagation --> ((_) => {promise.completeWith(confirmFn())})
  131. )
  132. promise.future
  133. }
  134. }