Skip to content

Commit

Permalink
Styling and API
Browse files Browse the repository at this point in the history
  • Loading branch information
jonatanklosko committed Oct 15, 2024
1 parent cdebf14 commit 7aaa001
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 108 deletions.
40 changes: 25 additions & 15 deletions assets/packs/mermaid/main.css
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
#contents button#download {
position: absolute;
display: none;
.container {
display: flex;
align-items: flex-start;
gap: 8px;
width: max-content;
}

.container .download-btn {
cursor: pointer;
padding: 0.25rem;
color: #61758A;
background: none;
border: none;
}

.container .download-btn:hover {
color: #0D1829;
}

#contents:hover button#download {
display: inline;
right: 0;
.container:not(:hover) .download-btn {
opacity: 0;
}

figure {
.figure {
margin: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: max-content;
}

figure figcaption {
border-radius: 0.5rem;
background-color: rgb(240 245 249);
padding: 0.5rem;
.caption {
margin-top: 8px;
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 500;
color: rgb(97 117 138);
color: #61758a;
}
63 changes: 27 additions & 36 deletions assets/packs/mermaid/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,54 +9,45 @@ export function init(ctx, { diagram, caption, download }) {
function render() {
mermaid.render("diagram", diagram).then(({ svg, bindFunctions }) => {
// Fix for: https://github.com/mermaid-js/mermaid/issues/1766
const renderedSvg = svg.replace(/<br>/gi, "<br />");
svg = svg.replace(/<br>/gi, "<br/>");

let contents = document.createElement("div");
contents.id = "contents";
ctx.root.appendChild(contents);
let container = document.createElement("div");
container.classList.add("container");
ctx.root.appendChild(container);

let figure = document.createElement("figure");
figure.id = "figure";
figure.innerHTML = renderedSvg;
contents.appendChild(figure);
const figure = document.createElement("figure");
figure.classList.add("figure");
figure.innerHTML = svg;
container.appendChild(figure);

if (caption) {
let figcaption = document.createElement("figcaption");
const figcaption = document.createElement("figcaption");
figcaption.classList.add("caption");
figcaption.textContent = caption;
figure.appendChild(figcaption);
}

if (download) {
let downloadButton = document.createElement("button");
downloadButton.id = "download";
downloadButton.title = `Download ${download.title}`;
downloadButton.textContent = "⇩";
contents.prepend(downloadButton);
const downloadBtn = document.createElement("button");
downloadBtn.classList.add("download-btn");
downloadBtn.title = "Download";
downloadBtn.innerHTML = `<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13 10H18L12 16L6 10H11V3H13V10ZM4 19H20V12H22V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V12H4V19Z"></path></svg>`;
container.appendChild(downloadBtn);

contents
.querySelector("#download")
.addEventListener("click", (event) => {
var downloadData = [];
downloadData.push(renderedSvg);
const downloadBlob = URL.createObjectURL(
new Blob(downloadData, { type: "image/svg+xml" })
);
downloadBtn.addEventListener("click", (event) => {
const blobURL = URL.createObjectURL(
new Blob([svg], { type: "image/svg+xml" }),
);

const downloadLink = document.createElement("a");
downloadLink.href = downloadBlob;
downloadLink.download = download.filename;
contents.appendChild(downloadLink);
const a = document.createElement("a");
a.style.display = "none";
a.href = blobURL;
a.download = "diagram.svg";

downloadLink.dispatchEvent(
new MouseEvent("click", {
bubbles: true,
cancelable: true,
view: window,
})
);

contents.removeChild(downloadLink);
});
container.appendChild(a);
a.click();
container.removeChild(a);
});
}

if (bindFunctions) {
Expand Down
1 change: 1 addition & 0 deletions lib/assets/mermaid/build/main.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/assets/mermaid/build/main.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 6 additions & 29 deletions lib/kino/mermaid.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,47 +24,24 @@ defmodule Kino.Mermaid do

@type t :: Kino.JS.t()

@download_defaults [title: "Diagram", filename: "diagram.svg"]

@doc """
Creates a new kino displaying the given Mermaid diagram.
## Options
* `:caption` - an optional caption for the rendered diagram.
Can be `nil` or a string. Defaults to `nil`.
* `:download` - whether or not to allow downloading the rendered Mermaid svg.
Defaults to `true`.
Downloads can be further customized by providing a keyword list
instead of a boolean, containing:
* `:title` - The alt text displayed for the download button.
* `:filename` - The name of the file to be downloaded.
* `:download` - whether or not to show a button for downloading
the diagram as a SVG. Defaults to `true`.
"""
@spec new(binary(), keyword()) :: t()
def new(diagram, opts \\ []) do
opts = Keyword.validate!(opts, caption: false, download: true)

download =
case Keyword.fetch!(opts, :download) do
true ->
Map.new(@download_defaults)

download_opts when is_list(download_opts) ->
download_opts
|> Keyword.validate!(@download_defaults)
|> Map.new()

_ ->
false
end

caption = Keyword.fetch!(opts, :caption)
opts = Keyword.validate!(opts, caption: nil, download: true)

Kino.JS.new(__MODULE__, %{diagram: diagram, caption: caption, download: download},
Kino.JS.new(
__MODULE__,
%{diagram: diagram, caption: opts[:caption], download: opts[:download]},
export: fn diagram -> {"mermaid", diagram} end
)
end
Expand Down
35 changes: 9 additions & 26 deletions lib/kino/process.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ defmodule Kino.Process do
* `:render_ets_tables` - determines whether ETS tables associated with the
supervision tree are rendered. Defaults to `false`.
* `:caption` - an optional caption for the diagram.
Can be `true` to use the default, falsey for none, or a string for a custom caption.
Defaults to the provided `application` name.
* `:caption` - an optional caption for the diagram. Either a custom
caption as string, or `nil` to disable the default caption.
## Examples
Expand Down Expand Up @@ -90,10 +89,7 @@ defmodule Kino.Process do
{:dictionary, dictionary} = process_info(root_supervisor, :dictionary)
[ancestor] = dictionary[:"$ancestors"]

caption =
opts
|> Keyword.get(:caption, true)
|> get_caption("Application tree for #{inspect(application)}")
caption = Keyword.get(opts, :caption, "Application tree for #{inspect(application)}")

Mermaid.new(
"""
Expand All @@ -120,9 +116,8 @@ defmodule Kino.Process do
* `:direction` - defines the direction of the graph visual. The
value can either be `:top_down` or `:left_right`. Defaults to `:top_down`.
* `:caption` - an optional caption for the diagram.
Can be `true` to use the default, falsey for none, or a string for a custom caption.
Defaults to the provided `supervisor`.
* `:caption` - an optional caption for the diagram. Either a custom
caption as string, or `nil` to disable the default caption.
## Examples
Expand Down Expand Up @@ -178,10 +173,7 @@ defmodule Kino.Process do

edges = traverse_supervisor(supervisor_pid, opts)

caption =
opts
|> Keyword.get(:caption, true)
|> get_caption("Supervisor tree for #{inspect(supervisor)}")
caption = Keyword.get(opts, :caption, "Supervisor tree for #{inspect(supervisor)}")

Mermaid.new(
"""
Expand Down Expand Up @@ -260,9 +252,8 @@ defmodule Kino.Process do
is used. However, if the function returns a `String.t()`, then
that will be used for the label.
* `:caption` - an optional caption for the diagram.
Can be `true` to use the default, falsey for none, or a string for a custom caption.
Defaults to the provided `trace_target`.
* `:caption` - an optional caption for the diagram. Either a custom
caption as string, or `nil` to disable the default caption.
## Examples
Expand Down Expand Up @@ -440,10 +431,7 @@ defmodule Kino.Process do
|> Enum.reverse()
|> Enum.join("\n")

caption =
opts
|> Keyword.get(:caption, true)
|> get_caption("Messages traced from #{inspect(trace_pids)}")
caption = Keyword.get(opts, :caption, "Messages traced from #{inspect(trace_pids)}")

sequence_diagram =
Mermaid.new(
Expand Down Expand Up @@ -842,9 +830,4 @@ defmodule Kino.Process do
defp process_info(pid, spec) do
:erpc.call(node(pid), Process, :info, [pid, spec])
end

defp get_caption(true, default), do: default
defp get_caption(false, _default), do: nil
defp get_caption(nil, _default), do: nil
defp get_caption(string, _default) when is_binary(string), do: string
end
2 changes: 1 addition & 1 deletion lib/kino/shorts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ defmodule Kino.Shorts do
""")
'''
@spec mermaid(String.t(), keyword()) :: Kino.Mermaid.t()
def mermaid(diagram, options \\ []), do: Kino.Mermaid.new(diagram, options)
def mermaid(diagram, opts \\ []), do: Kino.Mermaid.new(diagram, opts)

@doc """
A placeholder for static outputs that can be dynamically updated.
Expand Down

0 comments on commit 7aaa001

Please sign in to comment.