From 6c53c09a618ac5cd62ddd666e100efcd2aa3c84d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Thu, 24 Jun 2021 12:15:12 +0200 Subject: [PATCH] Add copy to clipboard button to virtualized output (#393) * Add copy to clipboard button to virtualized output * Move text output into its own component * Update button background --- assets/css/components.css | 4 +- assets/css/js_interop.css | 4 ++ assets/js/virtualized_lines/index.js | 14 ++++++ .../live/output/text_component.ex | 29 ++++++++++++ .../live/session_live/cell_component.ex | 46 +++++-------------- 5 files changed, 60 insertions(+), 37 deletions(-) create mode 100644 lib/livebook_web/live/output/text_component.ex diff --git a/assets/css/components.css b/assets/css/components.css index efd63c08c..9d2da14b8 100644 --- a/assets/css/components.css +++ b/assets/css/components.css @@ -54,11 +54,11 @@ } .icon-button { - @apply p-1 flex items-center justify-center text-gray-400 hover:text-gray-800; + @apply p-1 flex items-center justify-center text-gray-400 hover:text-gray-800 rounded-full; } .icon-button:focus { - @apply rounded-full bg-gray-100; + @apply bg-gray-100; } .icon-button i { diff --git a/assets/css/js_interop.css b/assets/css/js_interop.css index 0ffa25a7d..4a80d88e5 100644 --- a/assets/css/js_interop.css +++ b/assets/css/js_interop.css @@ -121,3 +121,7 @@ solely client-side operations. [data-element="clients-list-item"][data-js-followed] [data-meta="follow"] { @apply hidden; } + +[phx-hook="VirtualizedLines"]:not(:hover) [data-clipboard] { + @apply hidden; +} diff --git a/assets/js/virtualized_lines/index.js b/assets/js/virtualized_lines/index.js index fff23bab6..48de1ba1c 100644 --- a/assets/js/virtualized_lines/index.js +++ b/assets/js/virtualized_lines/index.js @@ -23,6 +23,8 @@ import { getLineHeight } from "../lib/utils"; * * * one annotated with `data-content` where the visible elements are rendered, * it should contain any styling relevant for the container + * + * Also a `data-clipboard` child button is used for triggering copy-to-clipboard. */ const VirtualizedLines = { mounted() { @@ -62,6 +64,18 @@ const VirtualizedLines = { this.state.contentElement, config ); + + this.el + .querySelector("[data-clipboard]") + .addEventListener("click", (event) => { + const content = Array.from(this.state.templateElement.children) + .map((child) => child.innerText) + .join(""); + + if ("clipboard" in navigator) { + navigator.clipboard.writeText(content); + } + }); }, updated() { diff --git a/lib/livebook_web/live/output/text_component.ex b/lib/livebook_web/live/output/text_component.ex new file mode 100644 index 000000000..99f6b487c --- /dev/null +++ b/lib/livebook_web/live/output/text_component.ex @@ -0,0 +1,29 @@ +defmodule LivebookWeb.Output.TextComponent do + use LivebookWeb, :live_component + + @impl true + def render(assigns) do + ~L""" +
+ +
+
+ +
+
+ """ + end +end diff --git a/lib/livebook_web/live/session_live/cell_component.ex b/lib/livebook_web/live/session_live/cell_component.ex index fdd22bddd..0c0ef643d 100644 --- a/lib/livebook_web/live/session_live/cell_component.ex +++ b/lib/livebook_web/live/session_live/cell_component.ex @@ -286,15 +286,22 @@ defmodule LivebookWeb.SessionLive.CellComponent do end defp render_output(_socket, text, id) when is_binary(text) do - text # Captured output usually has a trailing newline that we can ignore, # because each line is itself an HTML block anyway. - |> String.replace_suffix("\n", "") - |> render_virtualized_output(id, follow: true) + text = String.replace_suffix(text, "\n", "") + live_component(LivebookWeb.Output.TextComponent, id: id, content: text, follow: true) end defp render_output(_socket, {:text, text}, id) do - render_virtualized_output(text, id) + live_component(LivebookWeb.Output.TextComponent, id: id, content: text, follow: false) + end + + defp render_output(_socket, {:image, content, mime_type}, id) do + live_component(LivebookWeb.Output.ImageComponent, + id: id, + content: content, + mime_type: mime_type + ) end defp render_output(_socket, {:vega_lite_static, spec}, id) do @@ -315,14 +322,6 @@ defmodule LivebookWeb.SessionLive.CellComponent do ) end - defp render_output(_socket, {:image, content, mime_type}, id) do - live_component(LivebookWeb.Output.ImageComponent, - id: id, - content: content, - mime_type: mime_type - ) - end - defp render_output(_socket, {:error, formatted}, _id) do render_error_message_output(formatted) end @@ -334,29 +333,6 @@ defmodule LivebookWeb.SessionLive.CellComponent do """) end - defp render_virtualized_output(text, id, opts \\ []) do - follow = Keyword.get(opts, :follow, false) - lines = ansi_to_html_lines(text) - assigns = %{lines: lines, id: id, follow: follow} - - ~L""" -
- -
-
- """ - end - defp render_error_message_output(message) do assigns = %{message: message}