many changes
This commit is contained in:
parent
d455175044
commit
08b320b051
@ -6,6 +6,9 @@
|
|||||||
<title>Outwatch App</title>
|
<title>Outwatch App</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/blk-design-system@1.0.2/assets/css/nucleo-icons.css" integrity="sha256-03+9B37/His+rzjhgA6Y1+ByU9DGN2ZPWjjA5CJJF2w=" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/blk-design-system@1.0.2/assets/css/nucleo-icons.css" integrity="sha256-03+9B37/His+rzjhgA6Y1+ByU9DGN2ZPWjjA5CJJF2w=" crossorigin="anonymous">
|
||||||
|
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.min.js"></script> -->
|
||||||
|
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@sweetalert2/theme-dark@3/dark.css"> -->
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
26
build.sbt
26
build.sbt
@ -35,18 +35,28 @@ libraryDependencies ++= Seq(
|
|||||||
"com.beachape" %%% "enumeratum-circe" % "1.6.1",
|
"com.beachape" %%% "enumeratum-circe" % "1.6.1",
|
||||||
"com.github.valskalla" %%% "odin-core" % "0.7.0+95-ab4381ae+20201227-1831-SNAPSHOT",
|
"com.github.valskalla" %%% "odin-core" % "0.7.0+95-ab4381ae+20201227-1831-SNAPSHOT",
|
||||||
"io.github.cquiroz" %%% "scala-java-time" % "2.1.0",
|
"io.github.cquiroz" %%% "scala-java-time" % "2.1.0",
|
||||||
"io.github.cquiroz" %%% "scala-java-time-tzdb" % "2.1.0"
|
"io.github.cquiroz" %%% "scala-java-time-tzdb" % "2.1.0",
|
||||||
|
"com.lihaoyi" %%% "scalatags" % "0.9.2"
|
||||||
// "com.clovellytech" %%% "outwatch-router" % "0.0.9+7-5be0b1a2+20201227-2019-SNAPSHOT"
|
// "com.clovellytech" %%% "outwatch-router" % "0.0.9+7-5be0b1a2+20201227-2019-SNAPSHOT"
|
||||||
)
|
)
|
||||||
|
|
||||||
Compile / npmDependencies ++= Seq(
|
Compile / npmDependencies ++= Seq(
|
||||||
"jquery" -> "3.3.1",
|
"jquery" -> "3.5.1",
|
||||||
|
"@types/jquery" -> "3.5.5",
|
||||||
"blk-design-system" -> "1.0.2",
|
"blk-design-system" -> "1.0.2",
|
||||||
"bootstrap" -> "4.5.3",
|
"bootstrap" -> "4.5.3",
|
||||||
"@types/chart.js" -> "2.9.11",
|
"@types/chart.js" -> "2.9.11",
|
||||||
"chart.js" -> "2.9.3",
|
"chart.js" -> "2.9.3",
|
||||||
"snabbdom" -> "git://github.com/outwatch/snabbdom.git#semver:0.7.5",
|
"snabbdom" -> "git://github.com/outwatch/snabbdom.git#semver:0.7.5",
|
||||||
"fuse.js" -> "6.4.3"
|
"fuse.js" -> "6.4.3",
|
||||||
|
"datatables.net-bs4" -> "1.10.23",
|
||||||
|
// "datatables.net-dt" -> "1.10.23",
|
||||||
|
"@types/datatables.net" -> "1.10.19",
|
||||||
|
"sweetalert2" -> "10.12.6",
|
||||||
|
"@sweetalert2/themes" -> "4.0.1",
|
||||||
|
"reconnecting-websocket" -> "4.4.0",
|
||||||
|
"paralleljs" -> "1.1.0",
|
||||||
|
"@types/paralleljs" -> "0.0.20"
|
||||||
)
|
)
|
||||||
|
|
||||||
Compile / npmDevDependencies ++= Seq(
|
Compile / npmDevDependencies ++= Seq(
|
||||||
@ -57,8 +67,14 @@ Compile / npmDevDependencies ++= Seq(
|
|||||||
"url-loader" -> "1.1.2"
|
"url-loader" -> "1.1.2"
|
||||||
)
|
)
|
||||||
|
|
||||||
stIgnore ++= List("jquery", "blk-design-system", "bootstrap")
|
stIgnore ++= List(
|
||||||
stIgnore ++= List("snabbdom")
|
"datatables.net-bs4",
|
||||||
|
"datatables.net-dt",
|
||||||
|
"blk-design-system",
|
||||||
|
"bootstrap",
|
||||||
|
"snabbdom",
|
||||||
|
"@sweetalert2/themes"
|
||||||
|
)
|
||||||
stStdlib := List("es6")
|
stStdlib := List("es6")
|
||||||
stUseScalaJsDom := false
|
stUseScalaJsDom := false
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ object MonixWS {
|
|||||||
class MonixWS[F[_], S](val url: String)(implicit F: Sync[F], S: Show[S]) {
|
class MonixWS[F[_], S](val url: String)(implicit F: Sync[F], S: Show[S]) {
|
||||||
val ws = new org.scalajs.dom.WebSocket(url)
|
val ws = new org.scalajs.dom.WebSocket(url)
|
||||||
|
|
||||||
lazy val source: Observable[MessageEvent] =
|
val source: Observable[MessageEvent] =
|
||||||
Observable.create[MessageEvent](OverflowStrategy.Unbounded) { sub =>
|
Observable.create[MessageEvent](OverflowStrategy.Unbounded) { sub =>
|
||||||
ws.onmessage = (e: MessageEvent) => sub.onNext(e)
|
ws.onmessage = (e: MessageEvent) => sub.onNext(e)
|
||||||
ws.onerror =
|
ws.onerror =
|
||||||
@ -33,7 +33,7 @@ class MonixWS[F[_], S](val url: String)(implicit F: Sync[F], S: Show[S]) {
|
|||||||
Cancelable(() => ws.close())
|
Cancelable(() => ws.close())
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val sink: F[Observer[S]] = {
|
val sink: F[Observer[S]] = {
|
||||||
F.delay {
|
F.delay {
|
||||||
new Observer[S] {
|
new Observer[S] {
|
||||||
override def onNext(elem: S): Future[Ack] = {
|
override def onNext(elem: S): Future[Ack] = {
|
||||||
|
@ -20,7 +20,7 @@ object WebSocketF {
|
|||||||
class WebSocketF[F[_]](val url: String)(implicit F: Sync[F]) {
|
class WebSocketF[F[_]](val url: String)(implicit F: Sync[F]) {
|
||||||
val ws = new org.scalajs.dom.WebSocket(url)
|
val ws = new org.scalajs.dom.WebSocket(url)
|
||||||
|
|
||||||
lazy val source: Observable[MessageEvent] =
|
val source: Observable[MessageEvent] =
|
||||||
Observable.create[MessageEvent] { observer =>
|
Observable.create[MessageEvent] { observer =>
|
||||||
ws.onmessage = (e: MessageEvent) => observer.onNext(e)
|
ws.onmessage = (e: MessageEvent) => observer.onNext(e)
|
||||||
ws.onerror =
|
ws.onerror =
|
||||||
@ -28,7 +28,7 @@ class WebSocketF[F[_]](val url: String)(implicit F: Sync[F]) {
|
|||||||
Cancelable(() => ws.close())
|
Cancelable(() => ws.close())
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val sink: F[Observer[String]] = {
|
val sink: F[Observer[String]] = {
|
||||||
F.delay {
|
F.delay {
|
||||||
new Observer[String] {
|
new Observer[String] {
|
||||||
override def onNext(elem: String): Unit = ws.send(elem)
|
override def onNext(elem: String): Unit = ws.send(elem)
|
||||||
|
@ -8,28 +8,47 @@ import io.odin.consoleLogger
|
|||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import monix.eval.Coeval
|
import monix.eval.Coeval
|
||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
|
import monix.{eval => me}
|
||||||
import org.scalajs.dom.raw.Element
|
import org.scalajs.dom.raw.Element
|
||||||
import outwatch._
|
import outwatch._
|
||||||
import outwatch.dsl._
|
import outwatch.dsl._
|
||||||
import outwatch.router._
|
import outwatch.router._
|
||||||
|
import outwatchapp.components.ChartjsDemo
|
||||||
import outwatchapp.components.CounterDemo
|
import outwatchapp.components.CounterDemo
|
||||||
|
import outwatchapp.components.DatatablesDemo
|
||||||
|
import outwatchapp.components.FusejsDemo
|
||||||
import outwatchapp.components.RequestDemo
|
import outwatchapp.components.RequestDemo
|
||||||
import outwatchapp.components.todo.ChartjsDemo
|
|
||||||
import outwatchapp.components.todo.FusejsDemo
|
|
||||||
import outwatchapp.pages.HomePage
|
import outwatchapp.pages.HomePage
|
||||||
import outwatchapp.ui.components.todo.TodoListStore
|
import outwatchapp.ui.components.todo.TodoListStore
|
||||||
|
import outwatchapp.util.IOUtils
|
||||||
|
import outwatchapp.util.reactive.WebSocket
|
||||||
import outwatchapp.util.reactive.WebWorker
|
import outwatchapp.util.reactive.WebWorker
|
||||||
|
import outwatchapp.util.reactive.WebsocketData
|
||||||
import outwatchapp.util.reactive.WorkerData
|
import outwatchapp.util.reactive.WorkerData
|
||||||
|
import outwatchapp.util.reactive.ReconnectingWebSocket
|
||||||
|
import cats.syntax.eq._
|
||||||
|
import monix.execution.Ack
|
||||||
|
import monix.reactive.Observer
|
||||||
|
|
||||||
class MainApp(el: Element)(implicit
|
class MainApp(el: Element)(implicit
|
||||||
backend: AppTypes.Backend,
|
backend: AppTypes.Backend,
|
||||||
store: RouterStore[Page]
|
store: RouterStore[Page]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
import MainApp._
|
||||||
|
|
||||||
def run: Task[Unit] = for {
|
def run: Task[Unit] = for {
|
||||||
resolver <- resolver
|
resolver <- resolver
|
||||||
_ <- OutWatch.renderInto[Task](el, Router(resolver))
|
_ <- OutWatch.renderInto[Task](el, Router(resolver))
|
||||||
} yield ()
|
} yield ()
|
||||||
|
|
||||||
|
def producer(data: Long, ws: Observer[WebsocketData]): me.Task[Unit] =
|
||||||
|
me.Task.deferFuture(ws.onNext(WebsocketData(data))).flatMap {
|
||||||
|
case Ack.Continue => producer(data + 1, ws)
|
||||||
|
// .delayExecution(3.seconds)
|
||||||
|
case Ack.Stop => me.Task.unit
|
||||||
|
}
|
||||||
|
|
||||||
def resolver: Task[PartialFunction[Page, VDomModifier]] =
|
def resolver: Task[PartialFunction[Page, VDomModifier]] =
|
||||||
Task.deferAction(implicit s =>
|
Task.deferAction(implicit s =>
|
||||||
for {
|
for {
|
||||||
@ -38,35 +57,131 @@ class MainApp(el: Element)(implicit
|
|||||||
todoStore <- TodoListStore(consoleLogger[Task]())
|
todoStore <- TodoListStore(consoleLogger[Task]())
|
||||||
requestDemo <- RequestDemo(todoStore)
|
requestDemo <- RequestDemo(todoStore)
|
||||||
demoWorker <- WebWorker[WorkerData]("/worker.js")
|
demoWorker <- WebWorker[WorkerData]("/worker.js")
|
||||||
|
dtDemo <- DatatablesDemo()
|
||||||
|
_ <- IOUtils
|
||||||
|
.toIO(
|
||||||
|
Observable
|
||||||
|
.interval(1.second)
|
||||||
|
.take(2)
|
||||||
|
.doOnNextF(i => Coeval(println(s"Producer emitted $i")))
|
||||||
|
.doOnNextF(i =>
|
||||||
|
me.Task.deferFuture(demoWorker.onNext(WorkerData(i))).void
|
||||||
|
)
|
||||||
|
.completedL >>
|
||||||
|
me.Task.deferFuture(demoWorker.onNext(WorkerData(999)))
|
||||||
|
// .delayExecution(6.seconds)
|
||||||
|
)
|
||||||
|
.startAndForget
|
||||||
|
_ <-
|
||||||
|
ReconnectingWebSocket[WebsocketData]("ws://localhost:6789").flatMap {
|
||||||
|
case (source, sink) =>
|
||||||
|
IOUtils
|
||||||
|
.toIO(
|
||||||
|
me.Task.parZip2(
|
||||||
|
// Observable
|
||||||
|
// .interval(3.seconds)
|
||||||
|
// .doOnNext(_ => me.Task(println("Sending websocket data")))
|
||||||
|
// .doOnNext(i =>
|
||||||
|
// me.Task.deferFuture(
|
||||||
|
// ws.onNext(WebsocketData(i))
|
||||||
|
// ) >> me.Task.unit
|
||||||
|
// )
|
||||||
|
// .completedL,
|
||||||
|
producer(0, sink.value),
|
||||||
|
source.value
|
||||||
|
.doOnNext(data => me.Task(println(s"Received : $data")))
|
||||||
|
.completedL
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// .onErrorRestartIf {
|
||||||
|
// case e: Exception =>
|
||||||
|
// e.getMessage() === "Websocket closed"
|
||||||
|
// case _ => false
|
||||||
|
// }
|
||||||
|
// .onErrorRestartLoop(Backoff(3, 1.second)) { (err, state, retry) =>
|
||||||
|
// val Backoff(maxRetries, delay) = state
|
||||||
|
// if (maxRetries > 0)
|
||||||
|
// retry(Backoff(maxRetries - 1, delay * 2)).delayExecution(delay)
|
||||||
|
// else
|
||||||
|
// // No retries left, rethrow the error
|
||||||
|
// Task.raiseError(err)
|
||||||
|
// }
|
||||||
|
.startAndForget
|
||||||
|
// .start
|
||||||
|
// .startAndForget
|
||||||
|
// _ <- SweetAlertDemo.dumbNotif
|
||||||
|
// _ <- Task(println(s"Notif result: $notifRes"))
|
||||||
} yield {
|
} yield {
|
||||||
case Page.Home => wire[HomePage].render
|
case Page.Home => wire[HomePage].render
|
||||||
case Page.SomePage =>
|
case Page.SomePage =>
|
||||||
div(
|
div(
|
||||||
div(cls := "title", "SomePage"),
|
div(cls := "title", "SomePage"),
|
||||||
// RequestDemo(todoStore),
|
// RequestDemo(todoStore),
|
||||||
Observable
|
// Observable
|
||||||
.interval(1.second)
|
// .interval(1.second)
|
||||||
.doOnNextF(i => Coeval(println(s"Producer emitted $i")))
|
// .doOnNextF(i => Coeval(println(s"Producer emitted $i")))
|
||||||
.doOnNextF(i =>
|
// .doOnNextF(i =>
|
||||||
Coeval(demoWorker.onNext(WorkerData(i))) >> Coeval.unit
|
// Coeval(demoWorker.onNext(WorkerData(i))) >> Coeval.unit
|
||||||
)
|
// )
|
||||||
.map(_ => div()),
|
// .take(2)
|
||||||
|
// .map(_ => div()),
|
||||||
demoWorker.map(_.toString).map(v => p(cls := "text-white", v)),
|
demoWorker.map(_.toString).map(v => p(cls := "text-white", v)),
|
||||||
requestDemo,
|
requestDemo,
|
||||||
div(cls := "slider")
|
div(cls := "slider")
|
||||||
)
|
)
|
||||||
case Page.UserHome(id) =>
|
case Page.UserHome(id) =>
|
||||||
div(
|
div(
|
||||||
cls := "text-white",
|
// cls := "text-white",
|
||||||
div(cls := "title", "UserHome"),
|
div(cls := "title", "UserHome"),
|
||||||
s"User id: $id",
|
s"User id: $id",
|
||||||
div(FusejsDemo.y.map(_.item.toString).mkString(" "))
|
div(FusejsDemo.y.map(_.item.toString).mkString(" ")),
|
||||||
|
// ExampleDataTable.value(
|
||||||
|
// onDomMount.asHtml.foreachSync(el =>
|
||||||
|
// Coeval(DatatablesDemo.init(el)) >> Coeval.unit
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
dtDemo
|
||||||
)
|
)
|
||||||
case Page.NotFound =>
|
case Page.NotFound =>
|
||||||
|
Task(
|
||||||
div(
|
div(
|
||||||
div(cls := "title", "NotFound"),
|
cls := "page-header error-page header-filter",
|
||||||
p(cls := "text-white", "notfound")
|
div(
|
||||||
|
cls := "page-header-image"
|
||||||
|
// style := js.Dictionary(
|
||||||
|
// "background-image" -> "url('../assets/img/braden-collum.jpg')"
|
||||||
|
// )
|
||||||
|
),
|
||||||
|
div(
|
||||||
|
cls := "container",
|
||||||
|
cls := "text-center",
|
||||||
|
div(
|
||||||
|
cls := "row",
|
||||||
|
div(
|
||||||
|
cls := "col-md-12",
|
||||||
|
h1(
|
||||||
|
fontSize := "12em",
|
||||||
|
color := "#fff",
|
||||||
|
letterSpacing := "14px",
|
||||||
|
fontWeight := 700,
|
||||||
|
cls := "title",
|
||||||
|
"404"
|
||||||
|
),
|
||||||
|
h2(cls := "description", "Page not found :("),
|
||||||
|
h4(
|
||||||
|
cls := "description",
|
||||||
|
"Ooooups! Looks like you got lost."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object MainApp {
|
||||||
|
final case class Backoff(maxRetries: Long, delay: FiniteDuration)
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package outwatchapp.components.todo
|
package outwatchapp.components
|
||||||
import scala.scalajs.js.|
|
import scala.scalajs.js.|
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
|
||||||
@ -6,9 +6,10 @@ import colibri.ext.monix._
|
|||||||
import com.softwaremill.tagging._
|
import com.softwaremill.tagging._
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import monix.eval.Coeval
|
import monix.eval.Coeval
|
||||||
|
import monix.execution.Cancelable
|
||||||
import outwatch.HtmlVNode
|
import outwatch.HtmlVNode
|
||||||
|
import outwatch.ManagedSubscriptions.managedElement
|
||||||
import outwatch.dsl._
|
import outwatch.dsl._
|
||||||
import outwatch.reactive.handlers.monix._
|
|
||||||
import typings.chartJs.mod._
|
import typings.chartJs.mod._
|
||||||
import typings.chartJs.mod.{^ => Chart}
|
import typings.chartJs.mod.{^ => Chart}
|
||||||
import typings.moment.mod.Moment
|
import typings.moment.mod.Moment
|
||||||
@ -20,29 +21,19 @@ import scalajs.js
|
|||||||
sealed trait ChartjsDemo
|
sealed trait ChartjsDemo
|
||||||
object ChartjsDemo {
|
object ChartjsDemo {
|
||||||
val random = new Random()
|
val random = new Random()
|
||||||
def apply(): Task[HtmlVNode @@ ChartjsDemo] = Task.deferAction(implicit s =>
|
def apply(): Task[HtmlVNode @@ ChartjsDemo] = Coeval(
|
||||||
for {
|
|
||||||
canvasH <- Handler.createF[Task, HTMLCanvasElement]
|
|
||||||
} yield div(
|
|
||||||
canvas(
|
|
||||||
onDomMount.asHtml.map(a =>
|
|
||||||
a.asInstanceOf[HTMLCanvasElement]
|
|
||||||
) --> canvasH
|
|
||||||
),
|
|
||||||
div(
|
div(
|
||||||
canvasH
|
canvas(
|
||||||
.doOnNextF(el =>
|
managedElement { el =>
|
||||||
Coeval(
|
val chart = new Chart(
|
||||||
new Chart(
|
el.asInstanceOf[HTMLCanvasElement],
|
||||||
el,
|
|
||||||
chartConfig(ChartType.bar, randomData(100, random.nextInt()))
|
chartConfig(ChartType.bar, randomData(100, random.nextInt()))
|
||||||
)
|
)
|
||||||
) >> Coeval.unit
|
Cancelable(() => chart.destroy())
|
||||||
)
|
}
|
||||||
.map(_ => div())
|
|
||||||
)
|
)
|
||||||
).taggedWith[ChartjsDemo]
|
).taggedWith[ChartjsDemo]
|
||||||
)
|
).to[Task]
|
||||||
|
|
||||||
def randomData(
|
def randomData(
|
||||||
max: Int,
|
max: Int,
|
141
src/main/scala/outwatchapp/components/DatatablesDemo.scala
Normal file
141
src/main/scala/outwatchapp/components/DatatablesDemo.scala
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package outwatchapp.components
|
||||||
|
|
||||||
|
import scala.annotation.unused
|
||||||
|
import scala.scalajs.js.annotation.JSExportAll
|
||||||
|
import scala.scalajs.js.annotation.JSImport
|
||||||
|
|
||||||
|
import colibri.ext.monix._
|
||||||
|
import monix.bio.Task
|
||||||
|
import monix.eval.Coeval
|
||||||
|
import monix.execution.Cancelable
|
||||||
|
import org.scalajs.dom.raw.HTMLElement
|
||||||
|
import outwatch.ManagedSubscriptions.managedElement
|
||||||
|
import typings.datatablesNet.DataTables.ColumnSettings
|
||||||
|
import typings.datatablesNet.DataTables.FunctionColumnRender
|
||||||
|
import typings.datatablesNet.DataTables.Settings
|
||||||
|
import typings.datatablesNet.datatablesNetRequire
|
||||||
|
import typings.jquery.JQuery_
|
||||||
|
import typings.jquery.{mod => $}
|
||||||
|
|
||||||
|
import scalajs.js
|
||||||
|
|
||||||
|
@js.native
|
||||||
|
@JSImport(
|
||||||
|
"datatables.net-bs4/css/dataTables.bootstrap4.min.css",
|
||||||
|
JSImport.Namespace
|
||||||
|
)
|
||||||
|
object DataTablesBs4Css extends js.Object
|
||||||
|
@js.native
|
||||||
|
@JSImport(
|
||||||
|
"datatables.net-bs4/js/dataTables.bootstrap4.min.js",
|
||||||
|
JSImport.Namespace
|
||||||
|
)
|
||||||
|
object DataTablesBs4Js extends js.Object
|
||||||
|
|
||||||
|
object DatatablesDemo {
|
||||||
|
implicit def isJQueryDT[T](x: JQuery_[T]): typings.datatablesNet.JQuery =
|
||||||
|
x.asInstanceOf[typings.datatablesNet.JQuery]
|
||||||
|
|
||||||
|
datatablesNetRequire
|
||||||
|
DataTablesBs4Css
|
||||||
|
DataTablesBs4Js
|
||||||
|
|
||||||
|
@unused
|
||||||
|
private def _test(tableElement: HTMLElement) = $(tableElement)
|
||||||
|
.DataTable(
|
||||||
|
Settings().setColumnsVarargs(
|
||||||
|
ColumnSettings().setData("name").setRender(nameRenderFn)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.destroy()
|
||||||
|
|
||||||
|
val nameRenderFn: FunctionColumnRender = (_data, tpe, row, d) => {
|
||||||
|
if (tpe.toString == "display") println("Matched")
|
||||||
|
else println("Did not match")
|
||||||
|
// this can be done with scalatags instead
|
||||||
|
s"""
|
||||||
|
<div>${_data.toString.toUpperCase}</div>
|
||||||
|
"""
|
||||||
|
// with scalatags
|
||||||
|
// import scalatags.Text.all._
|
||||||
|
// div(_data.toString.toUpperCase).render
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSExportAll
|
||||||
|
case class TestData(
|
||||||
|
name: String,
|
||||||
|
position: String,
|
||||||
|
location: String,
|
||||||
|
id: String,
|
||||||
|
date: String,
|
||||||
|
salary: String
|
||||||
|
)
|
||||||
|
|
||||||
|
val dataset = js
|
||||||
|
.Array(
|
||||||
|
TestData(
|
||||||
|
"Tiger Nixon",
|
||||||
|
"System Architect",
|
||||||
|
"Edinburgh",
|
||||||
|
"5421",
|
||||||
|
"2011/04/25",
|
||||||
|
"$320,800"
|
||||||
|
),
|
||||||
|
TestData(
|
||||||
|
"Garrett Winters",
|
||||||
|
"Accountant",
|
||||||
|
"Tokyo",
|
||||||
|
"8422",
|
||||||
|
"2011/07/25",
|
||||||
|
"$170,750"
|
||||||
|
),
|
||||||
|
TestData(
|
||||||
|
"Ashton Cox",
|
||||||
|
"Junior Technical Author",
|
||||||
|
"San Francisco",
|
||||||
|
"1562",
|
||||||
|
"2009/01/12",
|
||||||
|
"$86,000"
|
||||||
|
),
|
||||||
|
TestData(
|
||||||
|
"Cedric Kelly",
|
||||||
|
"Senior Javascript Developer",
|
||||||
|
"Edinburgh",
|
||||||
|
"6224",
|
||||||
|
"2012/03/29",
|
||||||
|
"$433,060"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// $(document).ready(_ => $(tableElement).DataTable())
|
||||||
|
|
||||||
|
def apply() = {
|
||||||
|
import outwatch.dsl._
|
||||||
|
Coeval(
|
||||||
|
div(
|
||||||
|
table(idAttr := "myTable")(managedElement { el =>
|
||||||
|
val dt = $(el).DataTable(
|
||||||
|
Settings()
|
||||||
|
.setColumnsVarargs(
|
||||||
|
ColumnSettings()
|
||||||
|
.setTitle("Name")
|
||||||
|
.setData("name")
|
||||||
|
.setRender(nameRenderFn),
|
||||||
|
ColumnSettings().setData("position"),
|
||||||
|
ColumnSettings().setData("location"),
|
||||||
|
ColumnSettings().setData("id"),
|
||||||
|
ColumnSettings().setData("date"),
|
||||||
|
ColumnSettings().setData("salary")
|
||||||
|
)
|
||||||
|
.setDom("Blfrtip")
|
||||||
|
.setData(dataset)
|
||||||
|
)
|
||||||
|
Cancelable { () =>
|
||||||
|
println("Destroying")
|
||||||
|
dt.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).to[Task]
|
||||||
|
}
|
||||||
|
}
|
492
src/main/scala/outwatchapp/components/ExampleDataTable.scala
Normal file
492
src/main/scala/outwatchapp/components/ExampleDataTable.scala
Normal file
@ -0,0 +1,492 @@
|
|||||||
|
package outwatchapp.components
|
||||||
|
|
||||||
|
import outwatch.dsl._
|
||||||
|
|
||||||
|
object Table {
|
||||||
|
val value = table(
|
||||||
|
idAttr := "myTable",
|
||||||
|
cls := "display",
|
||||||
|
cls := "table",
|
||||||
|
// style := "width: 100%",
|
||||||
|
width := "100%",
|
||||||
|
// style("width") := "100%",
|
||||||
|
thead(
|
||||||
|
tr(
|
||||||
|
th("Name"),
|
||||||
|
th("Position"),
|
||||||
|
th("Office"),
|
||||||
|
th("Age"),
|
||||||
|
th("Start date"),
|
||||||
|
th("Salary")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
tbody(
|
||||||
|
tr(
|
||||||
|
td("Tiger Nixon"),
|
||||||
|
td("System Architect"),
|
||||||
|
td("Edinburgh"),
|
||||||
|
td("61"),
|
||||||
|
td("2011/04/25"),
|
||||||
|
td("$320,800")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Garrett Winters"),
|
||||||
|
td("Accountant"),
|
||||||
|
td("Tokyo"),
|
||||||
|
td("63"),
|
||||||
|
td("2011/07/25"),
|
||||||
|
td("$170,750")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Ashton Cox"),
|
||||||
|
td("Junior Technical Author"),
|
||||||
|
td("San Francisco"),
|
||||||
|
td("66"),
|
||||||
|
td("2009/01/12"),
|
||||||
|
td("$86,000")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Cedric Kelly"),
|
||||||
|
td("Senior Javascript Developer"),
|
||||||
|
td("Edinburgh"),
|
||||||
|
td("22"),
|
||||||
|
td("2012/03/29"),
|
||||||
|
td("$433,060")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Airi Satou"),
|
||||||
|
td("Accountant"),
|
||||||
|
td("Tokyo"),
|
||||||
|
td("33"),
|
||||||
|
td("2008/11/28"),
|
||||||
|
td("$162,700")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Brielle Williamson"),
|
||||||
|
td("Integration Specialist"),
|
||||||
|
td("New York"),
|
||||||
|
td("61"),
|
||||||
|
td("2012/12/02"),
|
||||||
|
td("$372,000")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Herrod Chandler"),
|
||||||
|
td("Sales Assistant"),
|
||||||
|
td("San Francisco"),
|
||||||
|
td("59"),
|
||||||
|
td("2012/08/06"),
|
||||||
|
td("$137,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Rhona Davidson"),
|
||||||
|
td("Integration Specialist"),
|
||||||
|
td("Tokyo"),
|
||||||
|
td("55"),
|
||||||
|
td("2010/10/14"),
|
||||||
|
td("$327,900")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Colleen Hurst"),
|
||||||
|
td("Javascript Developer"),
|
||||||
|
td("San Francisco"),
|
||||||
|
td("39"),
|
||||||
|
td("2009/09/15"),
|
||||||
|
td("$205,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Sonya Frost"),
|
||||||
|
td("Software Engineer"),
|
||||||
|
td("Edinburgh"),
|
||||||
|
td("23"),
|
||||||
|
td("2008/12/13"),
|
||||||
|
td("$103,600")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Jena Gaines"),
|
||||||
|
td("Office Manager"),
|
||||||
|
td("London"),
|
||||||
|
td("30"),
|
||||||
|
td("2008/12/19"),
|
||||||
|
td("$90,560")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Quinn Flynn"),
|
||||||
|
td("Support Lead"),
|
||||||
|
td("Edinburgh"),
|
||||||
|
td("22"),
|
||||||
|
td("2013/03/03"),
|
||||||
|
td("$342,000")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Charde Marshall"),
|
||||||
|
td("Regional Director"),
|
||||||
|
td("San Francisco"),
|
||||||
|
td("36"),
|
||||||
|
td("2008/10/16"),
|
||||||
|
td("$470,600")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Haley Kennedy"),
|
||||||
|
td("Senior Marketing Designer"),
|
||||||
|
td("London"),
|
||||||
|
td("43"),
|
||||||
|
td("2012/12/18"),
|
||||||
|
td("$313,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Tatyana Fitzpatrick"),
|
||||||
|
td("Regional Director"),
|
||||||
|
td("London"),
|
||||||
|
td("19"),
|
||||||
|
td("2010/03/17"),
|
||||||
|
td("$385,750")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Michael Silva"),
|
||||||
|
td("Marketing Designer"),
|
||||||
|
td("London"),
|
||||||
|
td("66"),
|
||||||
|
td("2012/11/27"),
|
||||||
|
td("$198,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Paul Byrd"),
|
||||||
|
td("Chief Financial Officer (CFO)"),
|
||||||
|
td("New York"),
|
||||||
|
td("64"),
|
||||||
|
td("2010/06/09"),
|
||||||
|
td("$725,000")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Gloria Little"),
|
||||||
|
td("Systems Administrator"),
|
||||||
|
td("New York"),
|
||||||
|
td("59"),
|
||||||
|
td("2009/04/10"),
|
||||||
|
td("$237,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Bradley Greer"),
|
||||||
|
td("Software Engineer"),
|
||||||
|
td("London"),
|
||||||
|
td("41"),
|
||||||
|
td("2012/10/13"),
|
||||||
|
td("$132,000")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Dai Rios"),
|
||||||
|
td("Personnel Lead"),
|
||||||
|
td("Edinburgh"),
|
||||||
|
td("35"),
|
||||||
|
td("2012/09/26"),
|
||||||
|
td("$217,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Jenette Caldwell"),
|
||||||
|
td("Development Lead"),
|
||||||
|
td("New York"),
|
||||||
|
td("30"),
|
||||||
|
td("2011/09/03"),
|
||||||
|
td("$345,000")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Yuri Berry"),
|
||||||
|
td("Chief Marketing Officer (CMO)"),
|
||||||
|
td("New York"),
|
||||||
|
td("40"),
|
||||||
|
td("2009/06/25"),
|
||||||
|
td("$675,000")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Caesar Vance"),
|
||||||
|
td("Pre-Sales Support"),
|
||||||
|
td("New York"),
|
||||||
|
td("21"),
|
||||||
|
td("2011/12/12"),
|
||||||
|
td("$106,450")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Doris Wilder"),
|
||||||
|
td("Sales Assistant"),
|
||||||
|
td("Sydney"),
|
||||||
|
td("23"),
|
||||||
|
td("2010/09/20"),
|
||||||
|
td("$85,600")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Angelica Ramos"),
|
||||||
|
td("Chief Executive Officer (CEO)"),
|
||||||
|
td("London"),
|
||||||
|
td("47"),
|
||||||
|
td("2009/10/09"),
|
||||||
|
td("$1,200,000")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Gavin Joyce"),
|
||||||
|
td("Developer"),
|
||||||
|
td("Edinburgh"),
|
||||||
|
td("42"),
|
||||||
|
td("2010/12/22"),
|
||||||
|
td("$92,575")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Jennifer Chang"),
|
||||||
|
td("Regional Director"),
|
||||||
|
td("Singapore"),
|
||||||
|
td("28"),
|
||||||
|
td("2010/11/14"),
|
||||||
|
td("$357,650")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Brenden Wagner"),
|
||||||
|
td("Software Engineer"),
|
||||||
|
td("San Francisco"),
|
||||||
|
td("28"),
|
||||||
|
td("2011/06/07"),
|
||||||
|
td("$206,850")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Fiona Green"),
|
||||||
|
td("Chief Operating Officer (COO)"),
|
||||||
|
td("San Francisco"),
|
||||||
|
td("48"),
|
||||||
|
td("2010/03/11"),
|
||||||
|
td("$850,000")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Shou Itou"),
|
||||||
|
td("Regional Marketing"),
|
||||||
|
td("Tokyo"),
|
||||||
|
td("20"),
|
||||||
|
td("2011/08/14"),
|
||||||
|
td("$163,000")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Michelle House"),
|
||||||
|
td("Integration Specialist"),
|
||||||
|
td("Sydney"),
|
||||||
|
td("37"),
|
||||||
|
td("2011/06/02"),
|
||||||
|
td("$95,400")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Suki Burks"),
|
||||||
|
td("Developer"),
|
||||||
|
td("London"),
|
||||||
|
td("53"),
|
||||||
|
td("2009/10/22"),
|
||||||
|
td("$114,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Prescott Bartlett"),
|
||||||
|
td("Technical Author"),
|
||||||
|
td("London"),
|
||||||
|
td("27"),
|
||||||
|
td("2011/05/07"),
|
||||||
|
td("$145,000")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Gavin Cortez"),
|
||||||
|
td("Team Leader"),
|
||||||
|
td("San Francisco"),
|
||||||
|
td("22"),
|
||||||
|
td("2008/10/26"),
|
||||||
|
td("$235,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Martena Mccray"),
|
||||||
|
td("Post-Sales support"),
|
||||||
|
td("Edinburgh"),
|
||||||
|
td("46"),
|
||||||
|
td("2011/03/09"),
|
||||||
|
td("$324,050")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Unity Butler"),
|
||||||
|
td("Marketing Designer"),
|
||||||
|
td("San Francisco"),
|
||||||
|
td("47"),
|
||||||
|
td("2009/12/09"),
|
||||||
|
td("$85,675")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Howard Hatfield"),
|
||||||
|
td("Office Manager"),
|
||||||
|
td("San Francisco"),
|
||||||
|
td("51"),
|
||||||
|
td("2008/12/16"),
|
||||||
|
td("$164,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Hope Fuentes"),
|
||||||
|
td("Secretary"),
|
||||||
|
td("San Francisco"),
|
||||||
|
td("41"),
|
||||||
|
td("2010/02/12"),
|
||||||
|
td("$109,850")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Vivian Harrell"),
|
||||||
|
td("Financial Controller"),
|
||||||
|
td("San Francisco"),
|
||||||
|
td("62"),
|
||||||
|
td("2009/02/14"),
|
||||||
|
td("$452,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Timothy Mooney"),
|
||||||
|
td("Office Manager"),
|
||||||
|
td("London"),
|
||||||
|
td("37"),
|
||||||
|
td("2008/12/11"),
|
||||||
|
td("$136,200")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Jackson Bradshaw"),
|
||||||
|
td("Director"),
|
||||||
|
td("New York"),
|
||||||
|
td("65"),
|
||||||
|
td("2008/09/26"),
|
||||||
|
td("$645,750")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Olivia Liang"),
|
||||||
|
td("Support Engineer"),
|
||||||
|
td("Singapore"),
|
||||||
|
td("64"),
|
||||||
|
td("2011/02/03"),
|
||||||
|
td("$234,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Bruno Nash"),
|
||||||
|
td("Software Engineer"),
|
||||||
|
td("London"),
|
||||||
|
td("38"),
|
||||||
|
td("2011/05/03"),
|
||||||
|
td("$163,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Sakura Yamamoto"),
|
||||||
|
td("Support Engineer"),
|
||||||
|
td("Tokyo"),
|
||||||
|
td("37"),
|
||||||
|
td("2009/08/19"),
|
||||||
|
td("$139,575")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Thor Walton"),
|
||||||
|
td("Developer"),
|
||||||
|
td("New York"),
|
||||||
|
td("61"),
|
||||||
|
td("2013/08/11"),
|
||||||
|
td("$98,540")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Finn Camacho"),
|
||||||
|
td("Support Engineer"),
|
||||||
|
td("San Francisco"),
|
||||||
|
td("47"),
|
||||||
|
td("2009/07/07"),
|
||||||
|
td("$87,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Serge Baldwin"),
|
||||||
|
td("Data Coordinator"),
|
||||||
|
td("Singapore"),
|
||||||
|
td("64"),
|
||||||
|
td("2012/04/09"),
|
||||||
|
td("$138,575")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Zenaida Frank"),
|
||||||
|
td("Software Engineer"),
|
||||||
|
td("New York"),
|
||||||
|
td("63"),
|
||||||
|
td("2010/01/04"),
|
||||||
|
td("$125,250")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Zorita Serrano"),
|
||||||
|
td("Software Engineer"),
|
||||||
|
td("San Francisco"),
|
||||||
|
td("56"),
|
||||||
|
td("2012/06/01"),
|
||||||
|
td("$115,000")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Jennifer Acosta"),
|
||||||
|
td("Junior Javascript Developer"),
|
||||||
|
td("Edinburgh"),
|
||||||
|
td("43"),
|
||||||
|
td("2013/02/01"),
|
||||||
|
td("$75,650")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Cara Stevens"),
|
||||||
|
td("Sales Assistant"),
|
||||||
|
td("New York"),
|
||||||
|
td("46"),
|
||||||
|
td("2011/12/06"),
|
||||||
|
td("$145,600")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Hermione Butler"),
|
||||||
|
td("Regional Director"),
|
||||||
|
td("London"),
|
||||||
|
td("47"),
|
||||||
|
td("2011/03/21"),
|
||||||
|
td("$356,250")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Lael Greer"),
|
||||||
|
td("Systems Administrator"),
|
||||||
|
td("London"),
|
||||||
|
td("21"),
|
||||||
|
td("2009/02/27"),
|
||||||
|
td("$103,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Jonas Alexander"),
|
||||||
|
td("Developer"),
|
||||||
|
td("San Francisco"),
|
||||||
|
td("30"),
|
||||||
|
td("2010/07/14"),
|
||||||
|
td("$86,500")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Shad Decker"),
|
||||||
|
td("Regional Director"),
|
||||||
|
td("Edinburgh"),
|
||||||
|
td("51"),
|
||||||
|
td("2008/11/13"),
|
||||||
|
td("$183,000")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Michael Bruce"),
|
||||||
|
td("Javascript Developer"),
|
||||||
|
td("Singapore"),
|
||||||
|
td("29"),
|
||||||
|
td("2011/06/27"),
|
||||||
|
td("$183,000")
|
||||||
|
),
|
||||||
|
tr(
|
||||||
|
td("Donna Snider"),
|
||||||
|
td("Customer Support"),
|
||||||
|
td("New York"),
|
||||||
|
td("27"),
|
||||||
|
td("2011/01/25"),
|
||||||
|
td("$112,000")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
tfoot(
|
||||||
|
tr(
|
||||||
|
th("Name"),
|
||||||
|
th("Position"),
|
||||||
|
th("Office"),
|
||||||
|
th("Age"),
|
||||||
|
th("Start date"),
|
||||||
|
th("Salary")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package outwatchapp.components.todo
|
package outwatchapp.components
|
||||||
|
|
||||||
import outwatchapp.ui.components.todo.Todo
|
import outwatchapp.ui.components.todo.Todo
|
||||||
import typings.fuseJs.mod.Fuse.IFuseOptions
|
import typings.fuseJs.mod.Fuse.IFuseOptions
|
@ -5,14 +5,14 @@ import com.softwaremill.tagging._
|
|||||||
import monix.bio._
|
import monix.bio._
|
||||||
import monix.eval.Coeval
|
import monix.eval.Coeval
|
||||||
import monix.{eval => me}
|
import monix.{eval => me}
|
||||||
import outwatchapp.ui.components.todo.Todo
|
|
||||||
import outwatchapp.ui.components.todo.TodoListStore
|
|
||||||
import outwatchapp.util.reactive.store.Store
|
|
||||||
import outwatch._
|
import outwatch._
|
||||||
import outwatch.dsl._
|
import outwatch.dsl._
|
||||||
import outwatch.reactive.handlers.monix._
|
import outwatch.reactive.handlers.monix._
|
||||||
import outwatchapp.AppTypes
|
import outwatchapp.AppTypes
|
||||||
import outwatchapp.implicits._
|
import outwatchapp.implicits._
|
||||||
|
import outwatchapp.ui.components.todo.Todo
|
||||||
|
import outwatchapp.ui.components.todo.TodoListStore
|
||||||
|
import outwatchapp.util.reactive.store.Store
|
||||||
import sttp.client._
|
import sttp.client._
|
||||||
|
|
||||||
sealed trait RequestDemo
|
sealed trait RequestDemo
|
||||||
|
104
src/main/scala/outwatchapp/components/SweetAlertDemo.scala
Normal file
104
src/main/scala/outwatchapp/components/SweetAlertDemo.scala
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package outwatchapp.components
|
||||||
|
import scala.scalajs.js.annotation.JSImport
|
||||||
|
|
||||||
|
import monix.bio.Task
|
||||||
|
import outwatchapp.implicits._
|
||||||
|
import typings.sweetalert2.mod.SweetAlertIcon
|
||||||
|
import typings.sweetalert2.mod.SweetAlertOptions
|
||||||
|
import typings.sweetalert2.mod.SweetAlertPosition
|
||||||
|
|
||||||
|
import scalajs.js
|
||||||
|
|
||||||
|
object SweetAlertDemo {
|
||||||
|
@JSImport(
|
||||||
|
"@sweetalert2/themes/borderless/borderless.min.css",
|
||||||
|
JSImport.Namespace
|
||||||
|
)
|
||||||
|
@js.native
|
||||||
|
object SweetAlertBorderlessTheme extends js.Object
|
||||||
|
@JSImport("@sweetalert2/themes/dark/dark.min.css", JSImport.Namespace)
|
||||||
|
@js.native
|
||||||
|
object SweetAlertDarkTheme extends js.Object
|
||||||
|
@JSImport("sweetalert2/dist/sweetalert2.js", JSImport.Namespace)
|
||||||
|
@js.native
|
||||||
|
object SweetAlertJs extends js.Object
|
||||||
|
|
||||||
|
SweetAlertDarkTheme
|
||||||
|
val Swal = SweetAlertJs.asInstanceOf[typings.sweetalert2.mod.default.type]
|
||||||
|
|
||||||
|
val loginPrompt = {
|
||||||
|
import scalatags.JsDom.all._
|
||||||
|
|
||||||
|
val nameInput = input(
|
||||||
|
tpe := "text",
|
||||||
|
`id` := "loginName",
|
||||||
|
cls := "swal-input",
|
||||||
|
placeholder := "Username"
|
||||||
|
).render
|
||||||
|
val passInput = input(
|
||||||
|
tpe := "password",
|
||||||
|
`id` := "loginPass",
|
||||||
|
cls := "swal-input",
|
||||||
|
placeholder := "Password"
|
||||||
|
).render
|
||||||
|
val html = div(nameInput, passInput).render.asST
|
||||||
|
|
||||||
|
def nameAndPass(a: js.Any) = (nameInput.value, passInput.value)
|
||||||
|
|
||||||
|
Swal.fireL(
|
||||||
|
SweetAlertOptions()
|
||||||
|
.setTitle("Login Form")
|
||||||
|
.setHtml(html)
|
||||||
|
.setFocusConfirm(false)
|
||||||
|
.setConfirmButtonText("Sign In")
|
||||||
|
.setPreConfirm(nameAndPass _)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val successPrompt =
|
||||||
|
Swal.fireL(
|
||||||
|
SweetAlertOptions()
|
||||||
|
.setPosition(SweetAlertPosition.`top-end`)
|
||||||
|
.setIcon(SweetAlertIcon.success)
|
||||||
|
.setTitle("Login Success")
|
||||||
|
.setShowConfirmButton(false)
|
||||||
|
.setTimer(1500)
|
||||||
|
) >> Task.unit
|
||||||
|
|
||||||
|
val failurePrompt =
|
||||||
|
Swal.fireL(
|
||||||
|
SweetAlertOptions()
|
||||||
|
.setPosition(SweetAlertPosition.`top-end`)
|
||||||
|
.setIcon(SweetAlertIcon.error)
|
||||||
|
.setTitle("Login Failure")
|
||||||
|
.setShowConfirmButton(false)
|
||||||
|
.setTimer(1500)
|
||||||
|
) >> Task.unit
|
||||||
|
|
||||||
|
val dumbNotif =
|
||||||
|
Swal
|
||||||
|
.fireL(
|
||||||
|
SweetAlertOptions()
|
||||||
|
.setTitle("Hello there")
|
||||||
|
.setText("Welcome to outwatch app")
|
||||||
|
// .setInput(SweetAlertInput.text)
|
||||||
|
// .setShowCancelButton(true)
|
||||||
|
.setIcon(SweetAlertIcon.info)
|
||||||
|
// .setToast(true)
|
||||||
|
// .setPreConfirm { res: String =>
|
||||||
|
// val username = Swal.getPopup().asOption
|
||||||
|
// username match {
|
||||||
|
// case Some(value) =>
|
||||||
|
// println(s"Got value: $value")
|
||||||
|
// 1
|
||||||
|
// case None =>
|
||||||
|
// println("Got none")
|
||||||
|
// 2
|
||||||
|
// }
|
||||||
|
// res
|
||||||
|
// }
|
||||||
|
) >> Task.unit
|
||||||
|
// .map(_.value.toOption)
|
||||||
|
|
||||||
|
}
|
@ -1,11 +1,19 @@
|
|||||||
package outwatchapp
|
package outwatchapp
|
||||||
|
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
import colibri.LiftSink
|
import colibri.LiftSink
|
||||||
import colibri.LiftSource
|
import colibri.LiftSource
|
||||||
import colibri.ext.monix._
|
import colibri.ext.monix._
|
||||||
|
import monix.bio.Task
|
||||||
import monix.execution.Scheduler
|
import monix.execution.Scheduler
|
||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
import monix.reactive.Observer
|
import monix.reactive.Observer
|
||||||
|
import org.scalajs.dom.raw.HTMLElement
|
||||||
|
import typings.sweetalert2.mod.SweetAlertOptions
|
||||||
|
import typings.sweetalert2.mod.{default => Swal}
|
||||||
|
|
||||||
|
import scalajs.js.|
|
||||||
|
|
||||||
package object implicits {
|
package object implicits {
|
||||||
// implicit def sinkForMyMonixProSub[A, M] =
|
// implicit def sinkForMyMonixProSub[A, M] =
|
||||||
@ -20,10 +28,26 @@ package object implicits {
|
|||||||
// )
|
// )
|
||||||
// }
|
// }
|
||||||
|
|
||||||
implicit class Ops[A](val sink: Observer[A]) {
|
implicit class Ops[A](val sink: Observer[A]) extends AnyVal {
|
||||||
@inline def liftSink[G[_]: LiftSink]: G[A] = LiftSink[G].lift(sink)
|
@inline def liftSink[G[_]: LiftSink]: G[A] = LiftSink[G].lift(sink)
|
||||||
}
|
}
|
||||||
implicit class Ops2[A](val source: Observable[A])(implicit s: Scheduler) {
|
implicit class Ops2[A](val source: Observable[A]) extends AnyVal {
|
||||||
@inline def liftSource[G[_]: LiftSource]: G[A] = LiftSource[G].lift(source)
|
@inline def liftSource[G[_]: LiftSource](implicit s: Scheduler): G[A] =
|
||||||
|
LiftSource[G].lift(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit class HTMLElementOps[T <: HTMLElement](private val el: T)
|
||||||
|
extends AnyVal {
|
||||||
|
def asST = el.asInstanceOf[typings.std.HTMLElement]
|
||||||
|
}
|
||||||
|
implicit class SweetAlertOps(private val sw: Swal.type) extends AnyVal {
|
||||||
|
def fireL[T](options: SweetAlertOptions[T, _]) =
|
||||||
|
Task.deferFuture(sw.fire(options).toFuture)
|
||||||
|
}
|
||||||
|
implicit class NullUnionOps[A, B](private val union: A | B) extends AnyVal {
|
||||||
|
@scala.inline
|
||||||
|
def asOption(implicit ev: B =:= scala.Null) = Try(
|
||||||
|
union.asInstanceOf[A]
|
||||||
|
).toOption
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,28 @@
|
|||||||
package outwatchapp.pages
|
package outwatchapp.pages
|
||||||
|
import cats.syntax.eq._
|
||||||
import com.softwaremill.tagging._
|
import com.softwaremill.tagging._
|
||||||
|
import monix.bio.Task
|
||||||
import outwatch._
|
import outwatch._
|
||||||
import outwatch.dsl._
|
import outwatch.dsl._
|
||||||
|
import outwatchapp.components.ChartjsDemo
|
||||||
import outwatchapp.components.CounterDemo
|
import outwatchapp.components.CounterDemo
|
||||||
import outwatchapp.components.todo.ChartjsDemo
|
import outwatchapp.components.SweetAlertDemo
|
||||||
|
|
||||||
class HomePage(
|
class HomePage(
|
||||||
counterDemo: VNode @@ CounterDemo,
|
counterDemo: VNode @@ CounterDemo,
|
||||||
chartDemo: VNode @@ ChartjsDemo
|
chartDemo: VNode @@ ChartjsDemo
|
||||||
) {
|
) {
|
||||||
def render = div(
|
val loginDemo = for {
|
||||||
|
res <- SweetAlertDemo.loginPrompt.map(_.value.toOption)
|
||||||
|
_ <- Task(println(s"Got $res"))
|
||||||
|
_ <-
|
||||||
|
if (res === Some("foo" -> "bar")) SweetAlertDemo.successPrompt
|
||||||
|
else SweetAlertDemo.failurePrompt
|
||||||
|
} yield ()
|
||||||
|
|
||||||
|
def render = Task.deferAction(implicit s =>
|
||||||
|
Task(
|
||||||
|
div(
|
||||||
div(cls := "title", "Home"),
|
div(cls := "title", "Home"),
|
||||||
div(
|
div(
|
||||||
cls := "card",
|
cls := "card",
|
||||||
@ -17,6 +30,14 @@ class HomePage(
|
|||||||
cls := "card-body",
|
cls := "card-body",
|
||||||
counterDemo,
|
counterDemo,
|
||||||
chartDemo,
|
chartDemo,
|
||||||
|
div(
|
||||||
|
cls := "text-center",
|
||||||
|
button(
|
||||||
|
cls := "btn btn-primary",
|
||||||
|
onClick.preventDefault.doAsync(loginDemo),
|
||||||
|
"Login"
|
||||||
|
)
|
||||||
|
),
|
||||||
p(
|
p(
|
||||||
cls := "text-white",
|
cls := "text-white",
|
||||||
div(
|
div(
|
||||||
@ -38,4 +59,6 @@ class HomePage(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
34
src/main/scala/outwatchapp/util/ParallelDemo.scala
Normal file
34
src/main/scala/outwatchapp/util/ParallelDemo.scala
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package outwatchapp.util
|
||||||
|
import scala.concurrent.Future
|
||||||
|
|
||||||
|
import typings.paralleljs.ParallelOptions
|
||||||
|
import typings.paralleljs.mod.{^ => Parallel}
|
||||||
|
|
||||||
|
import scalajs.js
|
||||||
|
import scalajs.js.|
|
||||||
|
|
||||||
|
object ParallelDemo {
|
||||||
|
val p =
|
||||||
|
new Parallel(js.Array(1, 2, 3, 4, 5), ParallelOptions().setMaxWorkers(2))
|
||||||
|
// val pr = new js.Promise()
|
||||||
|
|
||||||
|
// p.map((_: Int) * 2)
|
||||||
|
|
||||||
|
def toFuture[A](p: Parallel[A]): Future[A] = {
|
||||||
|
val p2 = scala.concurrent.Promise[A]()
|
||||||
|
p.`then`(
|
||||||
|
{ (v: A) =>
|
||||||
|
p2.success(v)
|
||||||
|
(): Unit | js.Thenable[Unit]
|
||||||
|
},
|
||||||
|
{ (e: typings.std.Error) =>
|
||||||
|
p2.failure(e match {
|
||||||
|
case th: Throwable => th
|
||||||
|
case _ => js.JavaScriptException(e)
|
||||||
|
})
|
||||||
|
(): Unit | js.Thenable[Unit]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
p2.future
|
||||||
|
}
|
||||||
|
}
|
14
src/main/scala/outwatchapp/util/SubtleCryptoTest.scala
Normal file
14
src/main/scala/outwatchapp/util/SubtleCryptoTest.scala
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package outwatchapp.util
|
||||||
|
|
||||||
|
// import typings.std.{SubtleCrypto => TS}
|
||||||
|
// import org.scalajs.dom.crypto.SubtleCrypto
|
||||||
|
import typings.std.global.Uint8Array
|
||||||
|
import typings.std.global.{SubtleCrypto => TS}
|
||||||
|
|
||||||
|
object SubtleCryptoTest {
|
||||||
|
val sc = new TS()
|
||||||
|
// Algorithm
|
||||||
|
// sc.deriveKey
|
||||||
|
// val n = WindowBase64
|
||||||
|
new Uint8Array(10)
|
||||||
|
}
|
44
src/main/scala/outwatchapp/util/WorkerTest.scala
Normal file
44
src/main/scala/outwatchapp/util/WorkerTest.scala
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// package outwatchapp.util
|
||||||
|
|
||||||
|
// import org.scalajs.dom
|
||||||
|
|
||||||
|
// import scala.scalajs.js
|
||||||
|
// import scala.scalajs.js.annotation.JSExport
|
||||||
|
// import org.scalajs.dom.webworkers.DedicatedWorkerGlobalScope
|
||||||
|
// import scala.scalajs.js.annotation.JSGlobalScope
|
||||||
|
// import scala.scalajs.js.annotation.JSExportTopLevel
|
||||||
|
|
||||||
|
// // @js.native
|
||||||
|
// // object WorkerGlobal extends js.GlobalScope {
|
||||||
|
// // def addEventListener(`type`: String, f: js.Function): Unit = js.native
|
||||||
|
// // def postMessage(data: js.Any): Unit = js.native
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// @js.native
|
||||||
|
// @JSGlobalScope
|
||||||
|
// object WorkerGlobal extends DedicatedWorkerGlobalScope
|
||||||
|
|
||||||
|
// @JSExportTopLevel("WorkerMain")
|
||||||
|
// object WorkerMain {
|
||||||
|
// @JSExport
|
||||||
|
// def main(): Unit = {
|
||||||
|
// // WorkerGlobal.addEventListener("message", onMessage _)
|
||||||
|
// WorkerGlobal.onmessage = onMessage _
|
||||||
|
// WorkerGlobal.postMessage(s"Started")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// val timeMessage = """Time.*""".r
|
||||||
|
// var count = 0
|
||||||
|
|
||||||
|
// def onMessage(msg: dom.MessageEvent) = {
|
||||||
|
// val s = msg.data.asInstanceOf[String]
|
||||||
|
// s match {
|
||||||
|
// case timeMessage() =>
|
||||||
|
// count += 1
|
||||||
|
// if (count % 600 == 0)
|
||||||
|
// WorkerGlobal.postMessage("60fps")
|
||||||
|
// case _ =>
|
||||||
|
// WorkerGlobal.postMessage(s"Received: $s")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
@ -0,0 +1,77 @@
|
|||||||
|
package outwatchapp.util.reactive
|
||||||
|
|
||||||
|
import io.circe.Decoder
|
||||||
|
import io.circe.Encoder
|
||||||
|
import io.circe.Printer
|
||||||
|
import io.circe.parser._
|
||||||
|
import io.circe.syntax._
|
||||||
|
import monix.execution.Ack
|
||||||
|
import monix.execution.Cancelable
|
||||||
|
import monix.execution.cancelables.SingleAssignCancelable
|
||||||
|
import monix.reactive.Observable
|
||||||
|
import monix.reactive.Observer
|
||||||
|
import monix.reactive.OverflowStrategy
|
||||||
|
import org.scalajs.dom.raw.MessageEvent
|
||||||
|
import org.scalajs.dom.webworkers.DedicatedWorkerGlobalScope
|
||||||
|
import scala.concurrent.Future
|
||||||
|
import outwatchapp.util.reactive.Exceptions.WrongTypeException
|
||||||
|
// @js.native
|
||||||
|
// @JSGlobalScope
|
||||||
|
// object WorkerGlobal extends DedicatedWorkerGlobalScope
|
||||||
|
|
||||||
|
// class DedicatedWorkerImpl[T: Encoder: Decoder](wg: DedicatedWorkerGlobalScope) {
|
||||||
|
// def run = Task.deferAction(implicit s =>
|
||||||
|
// for {
|
||||||
|
// _ <- Task.unit
|
||||||
|
// sub <- Task(ConcurrentSubject.publish[T])
|
||||||
|
// } yield ()
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
object DedicatedWorker {
|
||||||
|
def source[T: Decoder](wg: DedicatedWorkerGlobalScope) =
|
||||||
|
Observable.create[T](OverflowStrategy.DropOld(50)) { sub =>
|
||||||
|
val c = SingleAssignCancelable()
|
||||||
|
def onmessageFn(event: MessageEvent): Unit = {
|
||||||
|
event.data match {
|
||||||
|
case s: String =>
|
||||||
|
decode[T](s)
|
||||||
|
.map { res =>
|
||||||
|
if (sub.onNext(res) == Ack.Stop) c.cancel()
|
||||||
|
res
|
||||||
|
}
|
||||||
|
.left
|
||||||
|
.foreach(err =>
|
||||||
|
sub.onError(
|
||||||
|
new Exception(s"Failed to decode $s. Error was $err")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case other =>
|
||||||
|
sub.onError(WrongTypeException(s"Received wrong type: $other"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wg.onmessage = onmessageFn _
|
||||||
|
|
||||||
|
c := Cancelable(() => wg.onmessage = _ => ())
|
||||||
|
}
|
||||||
|
|
||||||
|
def sink[T: Encoder](wg: DedicatedWorkerGlobalScope) =
|
||||||
|
new Observer[T] {
|
||||||
|
val printer = Printer.noSpaces
|
||||||
|
|
||||||
|
override def onNext(elem: T): Future[Ack] = {
|
||||||
|
// wg.onoffline
|
||||||
|
wg.postMessage(printer.print(elem.asJson))
|
||||||
|
Ack.Continue
|
||||||
|
}
|
||||||
|
|
||||||
|
override def onError(ex: Throwable): Unit = ex.printStackTrace()
|
||||||
|
|
||||||
|
override def onComplete(): Unit = println("Worker observer completed")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply[T: Encoder: Decoder](
|
||||||
|
wg: DedicatedWorkerGlobalScope
|
||||||
|
): DedicatedWorker[T] = MonixProSubject.from(sink(wg), source(wg))
|
||||||
|
}
|
16
src/main/scala/outwatchapp/util/reactive/Exceptions.scala
Normal file
16
src/main/scala/outwatchapp/util/reactive/Exceptions.scala
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package outwatchapp.util.reactive
|
||||||
|
|
||||||
|
object Exceptions {
|
||||||
|
//cause: Option[Throwable]) extends Exception(message, cause.orNull)
|
||||||
|
sealed abstract class ReactiveException(val message: String)
|
||||||
|
extends Throwable(message)
|
||||||
|
final case class DecodeException(override val message: String)
|
||||||
|
extends ReactiveException(message)
|
||||||
|
final case class WrongTypeException(override val message: String)
|
||||||
|
extends ReactiveException(message)
|
||||||
|
final case class UseAfterClose(override val message: String)
|
||||||
|
extends ReactiveException(message)
|
||||||
|
final case class TerminatedException(override val message: String)
|
||||||
|
extends ReactiveException(message)
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,165 @@
|
|||||||
|
package outwatchapp.util.reactive
|
||||||
|
|
||||||
|
import scala.concurrent.Future
|
||||||
|
|
||||||
|
import io.circe.Decoder
|
||||||
|
import io.circe.Encoder
|
||||||
|
import io.circe.Printer
|
||||||
|
import io.circe.parser._
|
||||||
|
import io.circe.syntax._
|
||||||
|
import monix.bio.Task
|
||||||
|
import monix.execution.Ack
|
||||||
|
import monix.execution.Cancelable
|
||||||
|
import monix.execution.CancelablePromise
|
||||||
|
import monix.execution.cancelables.SingleAssignCancelable
|
||||||
|
import monix.reactive.Observable
|
||||||
|
import monix.reactive.Observer
|
||||||
|
import monix.reactive.OverflowStrategy
|
||||||
|
import typings.reconnectingWebsocket.eventsMod.CloseEvent
|
||||||
|
import typings.reconnectingWebsocket.eventsMod.ErrorEvent
|
||||||
|
import typings.reconnectingWebsocket.eventsMod.Event
|
||||||
|
import typings.reconnectingWebsocket.mod.{default => RW}
|
||||||
|
|
||||||
|
import scalajs.js
|
||||||
|
import typings.std.MessageEvent
|
||||||
|
import typings.reconnectingWebsocket.mod.Options
|
||||||
|
import outwatchapp.util.reactive.Exceptions.DecodeException
|
||||||
|
import outwatchapp.util.reactive.Exceptions.WrongTypeException
|
||||||
|
import monix.reactive.observers.Subscriber
|
||||||
|
import monix.reactive.observers.BufferedSubscriber
|
||||||
|
import outwatchapp.util.reactive.Exceptions.UseAfterClose
|
||||||
|
import monix.bio.IO
|
||||||
|
|
||||||
|
class ReconnectingWebSocketImpl[T: Encoder: Decoder](
|
||||||
|
ws: RW,
|
||||||
|
overflowStrategy: OverflowStrategy.Synchronous[T]
|
||||||
|
) {
|
||||||
|
val printer = Printer.noSpaces
|
||||||
|
|
||||||
|
/** @throws ReactiveException
|
||||||
|
*/
|
||||||
|
val source: Task[Observable[T]] =
|
||||||
|
Task.deferAction(implicit s =>
|
||||||
|
for {
|
||||||
|
obs <- Task(
|
||||||
|
Observable
|
||||||
|
.create[T](overflowStrategy) { sub =>
|
||||||
|
val c = SingleAssignCancelable()
|
||||||
|
val onmessage: js.Function1[MessageEvent[_], Unit] =
|
||||||
|
_.data match {
|
||||||
|
case s: String =>
|
||||||
|
decode[T](s)
|
||||||
|
.map { res =>
|
||||||
|
if (sub.onNext(res) == Ack.Stop) c.cancel()
|
||||||
|
res
|
||||||
|
}
|
||||||
|
.left
|
||||||
|
.foreach(err =>
|
||||||
|
sub.onError(
|
||||||
|
DecodeException(
|
||||||
|
s"Failed to decode $s. Error was $err"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case other =>
|
||||||
|
sub.onError(
|
||||||
|
WrongTypeException(s"Received wrong type: $other")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val emptyMsgFn: js.Function1[MessageEvent[_], Unit] =
|
||||||
|
_ => println("message fn not initialized")
|
||||||
|
ws.onmessage = onmessage
|
||||||
|
|
||||||
|
val onclose: js.Function1[CloseEvent, Unit] = a => {
|
||||||
|
println(
|
||||||
|
s"Websocket closing - ${a.code} ${a.wasClean} ${a.reason}"
|
||||||
|
)
|
||||||
|
sub.onComplete()
|
||||||
|
}
|
||||||
|
ws.onclose = onclose
|
||||||
|
|
||||||
|
val onerror: js.Function1[ErrorEvent, Unit] = (e: Event) =>
|
||||||
|
sub.onError(new Exception(s"Error in WebSocket: $e"))
|
||||||
|
|
||||||
|
ws.onerror = onerror
|
||||||
|
|
||||||
|
c := Cancelable { () => ws.onmessage = emptyMsgFn }
|
||||||
|
}
|
||||||
|
.publish
|
||||||
|
.refCount
|
||||||
|
)
|
||||||
|
_ <- Task(obs.subscribe(Observer.empty))
|
||||||
|
} yield obs
|
||||||
|
)
|
||||||
|
|
||||||
|
val sink: Task[Observer[T]] =
|
||||||
|
Task.deferAction(implicit s =>
|
||||||
|
Task(
|
||||||
|
BufferedSubscriber(
|
||||||
|
new Subscriber[T] {
|
||||||
|
import monix.execution.FutureUtils
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
override def scheduler = s
|
||||||
|
override def onNext(elem: T): Future[Ack] = {
|
||||||
|
val msg = printer.print(elem.asJson)
|
||||||
|
if (ws.readyState == 2 || ws.readyState == 3)
|
||||||
|
Ack.Stop
|
||||||
|
else {
|
||||||
|
FutureUtils
|
||||||
|
.delayedResult(2.second)(ws.send(msg))
|
||||||
|
.flatMap(_ => Ack.Continue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override def onError(ex: Throwable): Unit = s.reportFailure(ex)
|
||||||
|
override def onComplete(): Unit = println("CLOSING WEBSOCKET 2 ")
|
||||||
|
},
|
||||||
|
OverflowStrategy.Default
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
object ReconnectingWebSocket {
|
||||||
|
sealed trait Error
|
||||||
|
final case object Error extends Error
|
||||||
|
|
||||||
|
/** @throws ReactiveException
|
||||||
|
*/
|
||||||
|
final case class Source[T](value: Observable[T])
|
||||||
|
|
||||||
|
/** A buffered Observer
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
final case class Sink[T](value: Observer[T]) {
|
||||||
|
def send(t: T) =
|
||||||
|
IO.deferFuture(value.onNext(t)).flatMap {
|
||||||
|
case Ack.Continue => Task.unit
|
||||||
|
case Ack.Stop => IO.raiseError(UseAfterClose("Websocket was closed"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val defaultOptions = Options()
|
||||||
|
.setMinReconnectionDelay(5000)
|
||||||
|
.setMaxRetries(2)
|
||||||
|
.setMaxEnqueuedMessages(50)
|
||||||
|
|
||||||
|
def onopen[A](op: => A): js.Function1[Event, Unit] = _ => op
|
||||||
|
|
||||||
|
def apply[T <: Product: Encoder: Decoder](
|
||||||
|
path: String,
|
||||||
|
options: Options = defaultOptions,
|
||||||
|
overflowStrategy: OverflowStrategy.Synchronous[T] =
|
||||||
|
OverflowStrategy.DropOld(50)
|
||||||
|
) = for {
|
||||||
|
websocket <- Task(new RW(path, js.undefined, options))
|
||||||
|
p = CancelablePromise[Unit]()
|
||||||
|
_ <- Task(websocket.onopen = onopen(p.success(())))
|
||||||
|
_ <- Task.deferFuture(p.future)
|
||||||
|
_ <- Task(websocket.onopen = onopen(()))
|
||||||
|
impl = new ReconnectingWebSocketImpl[T](websocket, overflowStrategy)
|
||||||
|
source <- impl.source
|
||||||
|
sink <- impl.sink
|
||||||
|
// _ <- Task.deferAction(implicit s => Task(source.subscribe(sink)))
|
||||||
|
} yield Source(source) -> Sink(sink)
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package outwatchapp.util.reactive
|
package outwatchapp.util.reactive
|
||||||
|
|
||||||
import org.scalajs.dom.{raw => sjsdr}
|
import scala.concurrent.Future
|
||||||
|
|
||||||
import io.circe.Decoder
|
import io.circe.Decoder
|
||||||
import io.circe.Encoder
|
import io.circe.Encoder
|
||||||
import io.circe.Printer
|
import io.circe.Printer
|
||||||
@ -10,13 +11,20 @@ import io.circe.syntax._
|
|||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import monix.execution.Ack
|
import monix.execution.Ack
|
||||||
import monix.execution.Cancelable
|
import monix.execution.Cancelable
|
||||||
|
import monix.execution.CancelablePromise
|
||||||
|
import monix.execution.cancelables.SingleAssignCancelable
|
||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
import monix.reactive.Observer
|
import monix.reactive.Observer
|
||||||
import monix.reactive.OverflowStrategy
|
import monix.reactive.OverflowStrategy
|
||||||
import outwatchapp.util.reactive.MonixProSubject
|
|
||||||
import org.scalajs.dom.raw.Event
|
import org.scalajs.dom.raw.Event
|
||||||
import org.scalajs.dom.raw.MessageEvent
|
import org.scalajs.dom.raw.MessageEvent
|
||||||
import scala.concurrent.Future
|
import org.scalajs.dom.{raw => sjsdr}
|
||||||
|
import outwatchapp.util.reactive.MonixProSubject
|
||||||
|
import monix.reactive.observers.Subscriber
|
||||||
|
import monix.execution.Scheduler
|
||||||
|
import monix.reactive.observers.BufferedSubscriber
|
||||||
|
import monix.execution.cancelables.CompositeCancelable
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
class WebSocketImpl[T: Encoder: Decoder](
|
class WebSocketImpl[T: Encoder: Decoder](
|
||||||
ws: sjsdr.WebSocket,
|
ws: sjsdr.WebSocket,
|
||||||
@ -24,49 +32,84 @@ class WebSocketImpl[T: Encoder: Decoder](
|
|||||||
) {
|
) {
|
||||||
val printer = Printer.noSpaces
|
val printer = Printer.noSpaces
|
||||||
|
|
||||||
lazy val source: Task[Observable[T]] =
|
val source: Task[Observable[T]] =
|
||||||
Task.deferAction(implicit s =>
|
Task.deferAction(implicit s =>
|
||||||
for {
|
for {
|
||||||
|
c <- Task(CompositeCancelable())
|
||||||
obs <- Task(
|
obs <- Task(
|
||||||
Observable
|
Observable
|
||||||
.create[T](overflowStrategy) { sub =>
|
.create[T](overflowStrategy) { sub =>
|
||||||
|
// val c = SingleAssignCancelable()
|
||||||
ws.onmessage = (e: MessageEvent) =>
|
ws.onmessage = (e: MessageEvent) =>
|
||||||
e.data match {
|
e.data match {
|
||||||
case s: String =>
|
case s: String =>
|
||||||
decode[T](s).map(sub.onNext).left.foreach(println)
|
decode[T](s)
|
||||||
case other => println(other)
|
.map { res =>
|
||||||
|
if (sub.onNext(res) == Ack.Stop) c.cancel()
|
||||||
|
res
|
||||||
}
|
}
|
||||||
ws.onerror = (e: Event) =>
|
.left
|
||||||
|
.foreach(err =>
|
||||||
|
sub.onError(
|
||||||
|
new Exception(s"Failed to decode $s. Error was $err")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case other =>
|
||||||
|
sub.onError(new Exception(s"Received wrong type: $other"))
|
||||||
|
}
|
||||||
|
ws.onclose = a => {
|
||||||
|
println(
|
||||||
|
s"Websocket closing - ${a.code} ${a.wasClean} ${a.reason}"
|
||||||
|
)
|
||||||
|
sub.onComplete()
|
||||||
|
}
|
||||||
|
ws.onerror = (e: Event) => {
|
||||||
|
println("Error in websocket")
|
||||||
sub.onError(new Exception(s"Error in WebSocket: $e"))
|
sub.onError(new Exception(s"Error in WebSocket: $e"))
|
||||||
Cancelable(() => ws.close())
|
}
|
||||||
|
c += Cancelable { () =>
|
||||||
|
println("Closing websocket")
|
||||||
|
ws.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.publish
|
.publish
|
||||||
.refCount
|
.refCount
|
||||||
)
|
)
|
||||||
_ <- Task(obs.subscribe(Observer.empty))
|
// empty subscription because otherwise ouwtatch kills the observable when
|
||||||
|
// the dom containing this observable is unmounted
|
||||||
|
_ <- Task(c += obs.subscribe(Observer.empty))
|
||||||
} yield obs
|
} yield obs
|
||||||
)
|
)
|
||||||
|
|
||||||
lazy val sink: Task[Observer[T]] =
|
val sink: Task[Subscriber[T]] =
|
||||||
|
Task.deferAction(implicit s =>
|
||||||
Task(
|
Task(
|
||||||
new Observer[T] {
|
BufferedSubscriber(
|
||||||
|
new Subscriber[T] {
|
||||||
|
override implicit def scheduler: Scheduler = s
|
||||||
|
|
||||||
override def onNext(elem: T): Future[Ack] = {
|
override def onNext(elem: T): Future[Ack] = {
|
||||||
val msg = printer.print(elem.asJson)
|
val msg = printer.print(elem.asJson)
|
||||||
|
if (ws.readyState == 2 || ws.readyState == 3)
|
||||||
|
// Future.failed(new Exception("Websocket closed"))
|
||||||
|
Future.successful(Ack.Stop)
|
||||||
|
else {
|
||||||
ws.send(msg)
|
ws.send(msg)
|
||||||
Future.successful(Ack.Continue)
|
Future.successful(Ack.Continue)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
override def onError(ex: Throwable): Unit = println(ex)
|
override def onError(ex: Throwable): Unit = println(ex)
|
||||||
override def onComplete(): Unit = ()
|
override def onComplete(): Unit = ()
|
||||||
}
|
},
|
||||||
|
OverflowStrategy.BackPressure(50)
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
object WebSocket {
|
object WebSocket {
|
||||||
sealed trait Error
|
sealed trait Error
|
||||||
final case object Error extends Error
|
final case object Error extends Error
|
||||||
// val w = new sjsdr.Worker("/worker.js").asInstanceOf[Worker]
|
|
||||||
|
|
||||||
// type MonixWebWorker[T] = MonixProSubject[T, T]
|
|
||||||
|
|
||||||
def apply[T <: Product: Encoder: Decoder](
|
def apply[T <: Product: Encoder: Decoder](
|
||||||
path: String,
|
path: String,
|
||||||
@ -74,6 +117,11 @@ object WebSocket {
|
|||||||
OverflowStrategy.DropOld(50)
|
OverflowStrategy.DropOld(50)
|
||||||
): Task[WebSocket[T]] = for {
|
): Task[WebSocket[T]] = for {
|
||||||
websocket <- Task(new sjsdr.WebSocket(path))
|
websocket <- Task(new sjsdr.WebSocket(path))
|
||||||
|
p <- Task(CancelablePromise[Unit]())
|
||||||
|
// wait for websocket to open
|
||||||
|
_ <- Task(websocket.onopen = _ => p.success(()))
|
||||||
|
_ <- Task.deferFuture(p.future).timeout(10.seconds)
|
||||||
|
_ <- Task(websocket.onopen = _ => ())
|
||||||
impl = new WebSocketImpl[T](websocket, overflowStrategy)
|
impl = new WebSocketImpl[T](websocket, overflowStrategy)
|
||||||
source <- impl.source
|
source <- impl.source
|
||||||
sink <- impl.sink
|
sink <- impl.sink
|
||||||
|
@ -11,99 +11,98 @@ import io.circe.syntax._
|
|||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import monix.execution.Ack
|
import monix.execution.Ack
|
||||||
import monix.execution.Cancelable
|
import monix.execution.Cancelable
|
||||||
|
import monix.execution.cancelables.SingleAssignCancelable
|
||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
import monix.reactive.Observer
|
import monix.reactive.Observer
|
||||||
import monix.reactive.OverflowStrategy
|
import monix.reactive.OverflowStrategy
|
||||||
import outwatchapp.util.reactive.MonixProSubject
|
|
||||||
import org.scalajs.dom.raw.Event
|
import org.scalajs.dom.raw.Event
|
||||||
import org.scalajs.dom.raw.MessageEvent
|
import org.scalajs.dom.raw.MessageEvent
|
||||||
import org.scalajs.dom.{raw => sjsdr}
|
import org.scalajs.dom.{raw => sjsdr}
|
||||||
|
import outwatchapp.util.reactive.MonixProSubject
|
||||||
|
import monix.execution.cancelables.CompositeCancelable
|
||||||
|
import monix.eval.Coeval
|
||||||
|
import monix.reactive.observers.Subscriber
|
||||||
|
import monix.execution.Scheduler
|
||||||
|
import monix.reactive.observers.BufferedSubscriber
|
||||||
|
import outwatchapp.util.reactive.Exceptions.DecodeException
|
||||||
|
import outwatchapp.util.reactive.Exceptions.WrongTypeException
|
||||||
|
import outwatchapp.util.reactive.Exceptions.TerminatedException
|
||||||
|
|
||||||
class WebWorkerImpl[T <: Product: Encoder: Decoder](
|
class WebWorkerImpl[T <: Product: Encoder: Decoder](
|
||||||
worker: sjsdr.Worker,
|
worker: sjsdr.Worker,
|
||||||
overflowStrategy: OverflowStrategy.Synchronous[T]
|
overflowStrategy: OverflowStrategy.Synchronous[T]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// private def parseFn(data: T) = {
|
|
||||||
// data match {
|
|
||||||
// case _: AnyRef => parseRef(data)
|
|
||||||
// case s: String =>
|
|
||||||
// case other =>
|
|
||||||
// println(other)
|
|
||||||
// Left(WebWorker.Error)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private def parseRef(data: T): Either[WebWorker.Error, T] = {
|
|
||||||
// data match {
|
|
||||||
// case s: String => decode[T](s).leftMap(_ => WebWorker.Error)
|
|
||||||
// case a: Int =>
|
|
||||||
// println(a)
|
|
||||||
// Left(WebWorker.Error)
|
|
||||||
// case other =>
|
|
||||||
// println(other)
|
|
||||||
// Left(WebWorker.Error)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// private def parsePrimitive(data: T): Either[WebWorker.Error, T] = {
|
|
||||||
// data match {
|
|
||||||
// case s: String => decode[T](s).leftMap(_ => WebWorker.Error)
|
|
||||||
// case a: Int =>
|
|
||||||
// println(a)
|
|
||||||
// Left(WebWorker.Error)
|
|
||||||
// case other =>
|
|
||||||
// println(other)
|
|
||||||
// Left(WebWorker.Error)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// println(s"Got data $a")
|
|
||||||
// .map(sub.onNext).left.foreach(println)
|
|
||||||
|
|
||||||
val printer = Printer.noSpaces
|
val printer = Printer.noSpaces
|
||||||
|
|
||||||
lazy val source: Task[Observable[T]] =
|
val source: Task[Observable[T]] =
|
||||||
Task.deferAction(implicit s =>
|
Task.deferAction(implicit s =>
|
||||||
for {
|
for {
|
||||||
|
c <- Task(CompositeCancelable())
|
||||||
obs <- Task(
|
obs <- Task(
|
||||||
Observable
|
Observable
|
||||||
.create[T](overflowStrategy) { sub =>
|
.create[T](overflowStrategy) { sub =>
|
||||||
|
// val c = SingleAssignCancelable()
|
||||||
worker.onmessage = (e: MessageEvent) =>
|
worker.onmessage = (e: MessageEvent) =>
|
||||||
e.data match {
|
e.data match {
|
||||||
case s: String =>
|
case s: String =>
|
||||||
decode[T](s).map(sub.onNext).left.foreach(println)
|
decode[T](s)
|
||||||
case other => println(other)
|
.map { res =>
|
||||||
|
if (sub.onNext(res) == Ack.Stop) c.cancel()
|
||||||
|
res
|
||||||
|
}
|
||||||
|
.left
|
||||||
|
.foreach(err =>
|
||||||
|
sub.onError(
|
||||||
|
DecodeException(
|
||||||
|
s"Failed to decode $s. Error was $err"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case other =>
|
||||||
|
sub.onError(
|
||||||
|
WrongTypeException(s"Received wrong type: $other")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
worker.onerror = (e: Event) =>
|
worker.onerror = (e: Event) =>
|
||||||
sub.onError(new Exception(s"Error in WebSocket: $e"))
|
sub.onError(
|
||||||
Cancelable(() => worker.terminate())
|
TerminatedException(s"Worker terminated with error: $e")
|
||||||
|
)
|
||||||
|
c += Cancelable(() => worker.terminate())
|
||||||
}
|
}
|
||||||
|
.doOnSubscriptionCancelF(Coeval(println("Worker cancelled")))
|
||||||
.publish
|
.publish
|
||||||
.refCount
|
.refCount
|
||||||
)
|
)
|
||||||
_ <- Task(obs.subscribe(Observer.empty))
|
_ <- Task(c += obs.subscribe(Observer.empty))
|
||||||
} yield obs
|
} yield obs
|
||||||
)
|
)
|
||||||
|
|
||||||
lazy val sink: Task[Observer[T]] =
|
val sink: Task[Observer[T]] =
|
||||||
|
Task.deferAction(implicit s =>
|
||||||
Task(
|
Task(
|
||||||
new Observer[T] {
|
BufferedSubscriber(
|
||||||
|
new Subscriber[T] {
|
||||||
|
|
||||||
|
override implicit def scheduler: Scheduler = s
|
||||||
|
|
||||||
override def onNext(elem: T): Future[Ack] = {
|
override def onNext(elem: T): Future[Ack] = {
|
||||||
val msg = printer.print(elem.asJson)
|
val msg = printer.print(elem.asJson)
|
||||||
worker.postMessage(msg)
|
worker.postMessage(msg)
|
||||||
Future.successful(Ack.Continue)
|
Ack.Continue
|
||||||
}
|
}
|
||||||
override def onError(ex: Throwable): Unit = println(ex)
|
override def onError(ex: Throwable): Unit = s.reportFailure(ex)
|
||||||
override def onComplete(): Unit = ()
|
override def onComplete(): Unit = ()
|
||||||
}
|
},
|
||||||
|
OverflowStrategy.Default
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
object WebWorker {
|
object WebWorker {
|
||||||
sealed trait Error
|
sealed trait Error
|
||||||
final case object Error extends Error
|
final case object Error extends Error
|
||||||
// val w = new sjsdr.Worker("/worker.js").asInstanceOf[Worker]
|
|
||||||
|
|
||||||
// type MonixWebWorker[T] = MonixProSubject[T, T]
|
|
||||||
|
|
||||||
def apply[T <: Product: Encoder: Decoder](
|
def apply[T <: Product: Encoder: Decoder](
|
||||||
path: String,
|
path: String,
|
||||||
|
@ -8,4 +8,5 @@ package object reactive {
|
|||||||
type MonixSubject[A] = MonixProSubject[A, A]
|
type MonixSubject[A] = MonixProSubject[A, A]
|
||||||
type WebWorker[A] = MonixSubject[A]
|
type WebWorker[A] = MonixSubject[A]
|
||||||
type WebSocket[A] = MonixSubject[A]
|
type WebSocket[A] = MonixSubject[A]
|
||||||
|
type DedicatedWorker[A] = MonixSubject[A]
|
||||||
}
|
}
|
||||||
|
23690
src/main/scala/outwatchapp/util/reactive/store/test.css
Normal file
23690
src/main/scala/outwatchapp/util/reactive/store/test.css
Normal file
File diff suppressed because it is too large
Load Diff
67
yarn.lock
67
yarn.lock
@ -2,6 +2,11 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@sweetalert2/themes@4.0.1":
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@sweetalert2/themes/-/themes-4.0.1.tgz#ba07a40743fd439663341480c20360116c2e1f27"
|
||||||
|
integrity sha512-dE+SXd0ym2RUkDQT/rVyBIwe6QIKl1cfBF3133XdLeXVKUR+XEtkr1uVBK821uX5DRQfN9doJmVCd2jFdG/qXw==
|
||||||
|
|
||||||
"@types/chart.js@2.9.11":
|
"@types/chart.js@2.9.11":
|
||||||
version "2.9.11"
|
version "2.9.11"
|
||||||
resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.11.tgz#2b73fe59e78dfe31c2f8d6c6d0c169e98e65c16b"
|
resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.11.tgz#2b73fe59e78dfe31c2f8d6c6d0c169e98e65c16b"
|
||||||
@ -9,6 +14,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
moment "^2.10.2"
|
moment "^2.10.2"
|
||||||
|
|
||||||
|
"@types/datatables.net@1.10.19":
|
||||||
|
version "1.10.19"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/datatables.net/-/datatables.net-1.10.19.tgz#17c5f94433f761086131c6c8dc055a0e1099d1f9"
|
||||||
|
integrity sha512-WuzgytEmsIpVYZbkce+EvK1UqBI7/cwcC/WgYeAtXdq2zi+yWzJwMT5Yb6irAiOi52DBjeAEeRt3bYzFYvHWCQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/jquery" "*"
|
||||||
|
|
||||||
"@types/glob@^7.1.1":
|
"@types/glob@^7.1.1":
|
||||||
version "7.1.3"
|
version "7.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
|
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
|
||||||
@ -17,6 +29,13 @@
|
|||||||
"@types/minimatch" "*"
|
"@types/minimatch" "*"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/jquery@*", "@types/jquery@3.5.5":
|
||||||
|
version "3.5.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.5.tgz#2c63f47c9c8d96693d272f5453602afd8338c903"
|
||||||
|
integrity sha512-6RXU9Xzpc6vxNrS6FPPapN1SxSHgQ336WC6Jj/N8q30OiaBZ00l1GBgeP7usjVZPivSkGUfL1z/WW6TX989M+w==
|
||||||
|
dependencies:
|
||||||
|
"@types/sizzle" "*"
|
||||||
|
|
||||||
"@types/json-schema@^7.0.6":
|
"@types/json-schema@^7.0.6":
|
||||||
version "7.0.6"
|
version "7.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
|
||||||
@ -32,6 +51,16 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b"
|
||||||
integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==
|
integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==
|
||||||
|
|
||||||
|
"@types/paralleljs@0.0.20":
|
||||||
|
version "0.0.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/paralleljs/-/paralleljs-0.0.20.tgz#5013416ccb05d2fec5e2bcf5331d37bd203abe44"
|
||||||
|
integrity sha512-+1y+7aNWEvsXfdubI9y5aUFYU7RiUgVqAo+Px9siGLPUq4RZodvrP2EdoCV+oPQdcgKuQnW+CuikWcUSZuAkhA==
|
||||||
|
|
||||||
|
"@types/sizzle@*":
|
||||||
|
version "2.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47"
|
||||||
|
integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==
|
||||||
|
|
||||||
"@webassemblyjs/ast@1.9.0":
|
"@webassemblyjs/ast@1.9.0":
|
||||||
version "1.9.0"
|
version "1.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964"
|
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964"
|
||||||
@ -1010,6 +1039,21 @@ cyclist@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
|
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
|
||||||
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
|
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
|
||||||
|
|
||||||
|
datatables.net-bs4@1.10.23:
|
||||||
|
version "1.10.23"
|
||||||
|
resolved "https://registry.yarnpkg.com/datatables.net-bs4/-/datatables.net-bs4-1.10.23.tgz#bdd5d0dcb1bd68a7afe649a4424ba20647523b52"
|
||||||
|
integrity sha512-ChUB8t5t5uzPnJYTPXx2DOvnlm2shz8OadXrKoFavOadB308OuwHVxSldYq9+KGedCeiVxEjNqcaV4nFSXkRsw==
|
||||||
|
dependencies:
|
||||||
|
datatables.net "1.10.23"
|
||||||
|
jquery ">=1.7"
|
||||||
|
|
||||||
|
datatables.net@1.10.23:
|
||||||
|
version "1.10.23"
|
||||||
|
resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-1.10.23.tgz#59f7d7b12845183b1b379530d1385077e113ec01"
|
||||||
|
integrity sha512-we3tlNkzpxvgkKKlTxTMXPCt35untVXNg8zUYWpQyC1U5vJc+lT0+Zdc1ztK8d3lh5CfdnuFde2p8n3XwaGl3Q==
|
||||||
|
dependencies:
|
||||||
|
jquery ">=1.7"
|
||||||
|
|
||||||
debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
|
debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
|
||||||
version "2.6.9"
|
version "2.6.9"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||||
@ -2190,10 +2234,10 @@ isobject@^3.0.0, isobject@^3.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||||
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
|
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
|
||||||
|
|
||||||
jquery@3.3.1:
|
jquery@3.5.1, jquery@>=1.7:
|
||||||
version "3.3.1"
|
version "3.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
|
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5"
|
||||||
integrity sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==
|
integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==
|
||||||
|
|
||||||
json-parse-better-errors@^1.0.2:
|
json-parse-better-errors@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
@ -2836,6 +2880,11 @@ parallel-transform@^1.1.0:
|
|||||||
inherits "^2.0.3"
|
inherits "^2.0.3"
|
||||||
readable-stream "^2.1.5"
|
readable-stream "^2.1.5"
|
||||||
|
|
||||||
|
paralleljs@1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/paralleljs/-/paralleljs-1.1.0.tgz#d0cd5540fe4dd8e80fa94d3cb6a530510dbe6e70"
|
||||||
|
integrity sha512-5sYLAEzM+qMDYzQeVWyNdqINU4h+/Fhr0CrZs3+HCYPIhOWuA2BCzn74js2qzX+LKFwRQR6sSJBZhgT+WWhbfw==
|
||||||
|
|
||||||
parse-asn1@^5.0.0, parse-asn1@^5.1.5:
|
parse-asn1@^5.0.0, parse-asn1@^5.1.5:
|
||||||
version "5.1.6"
|
version "5.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4"
|
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4"
|
||||||
@ -3176,6 +3225,11 @@ readdirp@~3.5.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
picomatch "^2.2.1"
|
picomatch "^2.2.1"
|
||||||
|
|
||||||
|
reconnecting-websocket@4.4.0:
|
||||||
|
version "4.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz#3b0e5b96ef119e78a03135865b8bb0af1b948783"
|
||||||
|
integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==
|
||||||
|
|
||||||
regex-not@^1.0.0, regex-not@^1.0.2:
|
regex-not@^1.0.0, regex-not@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
|
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
|
||||||
@ -3751,6 +3805,11 @@ supports-color@^6.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^3.0.0"
|
has-flag "^3.0.0"
|
||||||
|
|
||||||
|
sweetalert2@10.12.6:
|
||||||
|
version "10.12.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/sweetalert2/-/sweetalert2-10.12.6.tgz#249b95fecb28986a1aff82710a460ecb4717e2a9"
|
||||||
|
integrity sha512-kkPRpNqP0wF9tAVu1Ygp4pQx4pXY/pgseyW3dOXXRu0S6s7HKfHEmpBUIZrBylRfeJFPOsSEk6sALSwKzNZ9RQ==
|
||||||
|
|
||||||
tapable@^1.0.0, tapable@^1.1.3:
|
tapable@^1.0.0, tapable@^1.1.3:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
|
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
|
||||||
|
Loading…
Reference in New Issue
Block a user