diff --git a/core-js/src/main/scala/coursier/core/compatibility/package.scala b/core-js/src/main/scala/coursier/core/compatibility/package.scala index 7766bc79b..86c0a614d 100644 --- a/core-js/src/main/scala/coursier/core/compatibility/package.scala +++ b/core-js/src/main/scala/coursier/core/compatibility/package.scala @@ -30,6 +30,16 @@ package object compatibility { js.Dynamic.newInstance(defn)() } + lazy val XMLSerializer = { + import js.Dynamic.{global => g} + import js.DynamicImplicits._ + + val defn = + if (js.isUndefined(g.XMLSerializer)) g.require("xmldom").XMLSerializer + else g.XMLSerializer + js.Dynamic.newInstance(defn)() + } + def fromNode(node: org.scalajs.dom.raw.Node): Xml.Node = { val node0 = node.asInstanceOf[js.Dynamic] @@ -52,6 +62,9 @@ package object compatibility { def isElement = option[Int](node0.nodeType) .exists(_ == 1) // org.scalajs.dom.raw.Node.ELEMENT_NODE + + override def toString = + XMLSerializer.serializeToString(node).asInstanceOf[String] } } diff --git a/web/src/main/resources/index-dev.html b/web/src/main/resources/index-dev.html index a0726ba04..648f446ee 100644 --- a/web/src/main/resources/index-dev.html +++ b/web/src/main/resources/index-dev.html @@ -27,6 +27,10 @@ .customButton { margin: 5px; } + + .popover { + max-width: 400px; + } diff --git a/web/src/main/scala/coursier/web/Backend.scala b/web/src/main/scala/coursier/web/Backend.scala index 4b1cb1d2e..975799fc9 100644 --- a/web/src/main/scala/coursier/web/Backend.scala +++ b/web/src/main/scala/coursier/web/Backend.scala @@ -175,6 +175,10 @@ class Backend($: BackendScope[Unit, State]) { } } + def enablePopover(e: ReactEventI) = { + g.$("[data-toggle='popover']").popover() + } + object options { def toggleOptional(e: ReactEventI) = { $.modState(s => s.copy(options = s.options.copy(followOptional = !s.options.followOptional))) @@ -189,8 +193,22 @@ object App { lazy val arbor = g.arbor - val resultDependencies = ReactComponentB[Resolution]("Result") - .render{ res => + val resultDependencies = ReactComponentB[(Resolution, Backend)]("Result") + .render{ T => + val (res, backend) = T + + def infoLabel(label: String) = + <.span(^.`class` := "label label-info", label) + def errorLabel(label: String, desc: String) = + <.button(^.`type` := "button", ^.`class` := "btn btn-xs btn-danger", + Attr("data-trigger") := "focus", + Attr("data-toggle") := "popover", Attr("data-placement") := "bottom", + Attr("data-content") := desc, + ^.onClick ==> backend.enablePopover, + ^.onMouseOver ==> backend.enablePopover, + label + ) + def depItem(dep: Dependency) = <.tr( ^.`class` := (if (res.errors.contains(dep.module)) "danger" else ""), @@ -198,10 +216,11 @@ object App { <.td(dep.module.name), <.td(dep.module.version), <.td(Seq[Seq[TagMod]]( - if (dep.scope == Scope.Compile) Seq() else Seq(<.span(^.`class` := "label label-info", dep.scope.name)), - if (dep.`type`.isEmpty || dep.`type` == "jar") Seq() else Seq(<.span(^.`class` := "label label-info", dep.`type`)), - if (dep.classifier.isEmpty) Seq() else Seq(<.span(^.`class` := "label label-info", dep.classifier)), - if (dep.optional) Seq(<.span(^.`class` := "label label-info", dep.classifier)) else Seq() + if (dep.scope == Scope.Compile) Seq() else Seq(infoLabel(dep.scope.name)), + if (dep.`type`.isEmpty || dep.`type` == "jar") Seq() else Seq(infoLabel(dep.`type`)), + if (dep.classifier.isEmpty) Seq() else Seq(infoLabel(dep.classifier)), + if (dep.optional) Seq(infoLabel("optional")) else Seq(), + res.errors.get(dep.module).map(errs => errorLabel("Error", errs.mkString("; "))).toSeq )), <.td(Seq[Seq[TagMod]]( res.projectsCache.get(dep.module) match { @@ -244,7 +263,6 @@ object App { <.tbody( sortedDeps.map(depItem) ) -// <.div(dangerouslySetInnerHtml("")) ) } .build @@ -406,21 +424,23 @@ object App { } .build - val resolution = ReactComponentB[Option[Resolution]]("Resolution") - .render(resOpt => + val resolution = ReactComponentB[(Option[Resolution], Backend)]("Resolution") + .render{ T => + val (resOpt, backend) = T + resOpt match { case Some(res) => <.div( <.div(^.`class` := "page-header", <.h1("Resolution") ), - resultDependencies(res) + resultDependencies((res, backend)) ) case None => <.div() } - ) + } .build val initialState = State(Nil, Seq(coursier.repository.mavenCentral), ResolutionOptions(), None, -1, resolving = false, reverseTree = false, log = Nil) @@ -494,7 +514,7 @@ object App { ), <.div(^.`class` := "tab-content", <.div(^.role := "tabpanel", ^.`class` := "tab-pane", ^.id := "resolution", - resolution(S.resolutionOpt) + resolution((S.resolutionOpt, B)) ), <.div(^.role := "tabpanel", ^.`class` := "tab-pane", ^.id := "log", <.button(^.`type` := "button", ^.`class` := "btn btn-default",