diff --git a/core/shared/src/main/scala/reftree/dot/DotEncoding.scala b/core/shared/src/main/scala/reftree/dot/DotEncoding.scala index 4b36c3e..fda0923 100644 --- a/core/shared/src/main/scala/reftree/dot/DotEncoding.scala +++ b/core/shared/src/main/scala/reftree/dot/DotEncoding.scala @@ -30,8 +30,10 @@ private[dot] sealed trait DotAttrEncoding extends LabelledProductTypeClassCompan if (headEnc.encoded.isEmpty) { ct.encoding(tail) } else { + // The 'URL' attribute must remain in uppercase otherwise it will be ignored. + val encodedName = if (name == "URL") name else name.toLowerCase Chunk.join(" ")( - Chunk.join("=")(Chunk(name.toLowerCase), headEnc), + Chunk.join("=")(Chunk(encodedName), headEnc), ct.encoding(tail) ) } diff --git a/core/shared/src/main/scala/reftree/dot/Graph.scala b/core/shared/src/main/scala/reftree/dot/Graph.scala index f3fbb2f..9667835 100644 --- a/core/shared/src/main/scala/reftree/dot/Graph.scala +++ b/core/shared/src/main/scala/reftree/dot/Graph.scala @@ -38,6 +38,7 @@ object Node { case class Attrs( shape: Option[String] = None, tooltip: Option[String] = None, + URL: Option[String] = None, color: Option[Color] = None, fontName: Option[String] = None, fontColor: Option[Color] = None diff --git a/core/shared/src/main/scala/reftree/graph/Graphs.scala b/core/shared/src/main/scala/reftree/graph/Graphs.scala index 0641f47..7292eb5 100644 --- a/core/shared/src/main/scala/reftree/graph/Graphs.scala +++ b/core/shared/src/main/scala/reftree/graph/Graphs.scala @@ -32,11 +32,11 @@ object Graphs { depth: Int ): Seq[GraphStatement] = tree match { case r @ RefTree.Ref(_, id, children, _) ⇒ - Seq(Primitives.node(r, color, anchorId, namespace)) ++ + Seq(Primitives.node(r, color, anchorId, namespace, options.nodeURLs.get(id), options.nodeTips.get(id))) ++ children.filterNot(_.elideRefs).flatMap(c ⇒ inner(c.value, color, None, namespace, depth + 1)) ++ children.zipWithIndex.flatMap { case (c, i) ⇒ Primitives.edge(id, c.value, i, color, namespace) } case _ if depth == 0 ⇒ - Seq(Primitives.node(tree, color, anchorId, namespace)) + Seq(Primitives.node(tree, color, anchorId, namespace, None, None)) case _ ⇒ Seq.empty } diff --git a/core/shared/src/main/scala/reftree/graph/Primitives.scala b/core/shared/src/main/scala/reftree/graph/Primitives.scala index 3617b43..2001658 100644 --- a/core/shared/src/main/scala/reftree/graph/Primitives.scala +++ b/core/shared/src/main/scala/reftree/graph/Primitives.scala @@ -29,7 +29,7 @@ object Primitives { Seq(captionNode, captionEdge) } - def node(tree: RefTree, color: Color, anchorId: Option[String], namespace: Seq[String]): Node = { + def node(tree: RefTree, color: Color, anchorId: Option[String], namespace: Seq[String], url: Option[String], tip: Option[String]): Node = { val background = if (tree.highlight) color.opacify(0.2) else defaultBackground val labelContent: Seq[Row] = tree match { case ref: RefTree.Ref ⇒ @@ -51,9 +51,9 @@ object Primitives { cellSpacing = Some(0), cellPadding = Some(6), cellBorder = Some(0), columns = Some("*"), bgColor = Some(background), style = Some("rounded") )) - val tooltip = anchorId.map(a ⇒ s"anchor-$a") + val tooltip = tip.orElse(anchorId.map(a ⇒ s"anchor-$a")) val id = namespaced(tree.id, namespace) - Node(id, label, Node.Attrs(fontColor = Some(color), color = Some(color), tooltip = tooltip)) + Node(id, label, Node.Attrs(fontColor = Some(color), color = Some(color), tooltip = tooltip, URL = url)) } private def cellLabel(tree: RefTree, elideRefs: Boolean = false): Html = tree match { diff --git a/core/shared/src/main/scala/reftree/render/Options.scala b/core/shared/src/main/scala/reftree/render/Options.scala index 1ca6c49..244eebe 100644 --- a/core/shared/src/main/scala/reftree/render/Options.scala +++ b/core/shared/src/main/scala/reftree/render/Options.scala @@ -7,12 +7,16 @@ import java.time.Duration * Options for rendering static diagrams * * @param verticalSpacing vertical spacing to set for Graphviz + * @param nodeURLs map of node ID to a URL + * @param nodeTips map of node ID to a node tooltip overriding the anchor tooltip, if defined * @param palette a sequence of colors to be used * @param font the font for text rendering * @param density the desired image density, in pixels per inch */ case class RenderingOptions( verticalSpacing: Double = 0.8, + nodeURLs: Map[String, String] = Map.empty, + nodeTips: Map[String, String] = Map.empty, palette: IndexedSeq[Color] = Array( Color.fromRgbaString("#104e8b"), Color.fromRgbaString("#228b22"), diff --git a/site/src/main/tut/Guide.md b/site/src/main/tut/Guide.md index 27de71d..c029ec9 100644 --- a/site/src/main/tut/Guide.md +++ b/site/src/main/tut/Guide.md @@ -80,6 +80,26 @@ val renderer = Renderer( ) ``` +**For SVG rendering (JVM, Scala.js):** + +It is possible to specify for each `RefTree.Ref` +a hyperlink URL and a tooltip text +by providing associating to the `RefTree.Ref.id` a URL string +or a tooltip string respectively. + +```scala +import reftree.render._ +import reftree.diagram._ + +val renderer = Renderer( + renderingOptions = RenderingOptions( + density = 75, + nodeURLs = Map("1" -> "http://www.example.org", "2" -> "http://www.foobar.org"), + nodeTips = Map("1" -> "example", "2" -> "foobar")), + format = "svg" +) +``` + There are two ways to use renderers: **JVM**