fix(portal): Update copy to clipboard button (#8907)

Why:

* The copy to clipboard button was not working at all on the API new
token page due to the fact that the FlowbiteJS library expects the
presence of the elements in the DOM on first render. This was not true
of the API Token code block. Along with that issue the existing code
blocks copy to clipboard buttons did not give any visual indication that
the copy had been completed. It was also somewhat difficult to see the
copy to clipboard button on those code blocks as well. This commit
updates the buttons to be more visible, as well as adds a phx-hook to
make sure the FlowbiteJS init functions are run on every code block even
if it's inserted after the initial load of the page and adds functions
that are run as a callback to toggle the button text and icon to show
the text has been copied.
This commit is contained in:
Brian Manifold
2025-04-25 17:43:43 -07:00
committed by GitHub
parent 4fbfa5247f
commit 3f3f007920
3 changed files with 64 additions and 22 deletions

View File

@@ -1,4 +1,4 @@
import { initTabs, Popover } from "flowbite";
import { initCopyClipboards, initTabs, Popover } from "flowbite";
let Hooks = {};
@@ -128,4 +128,39 @@ Hooks.Popover = {
},
};
Hooks.CopyClipboard = {
mounted() {
initCopyClipboards();
const id = this.el.id;
const clipboard = FlowbiteInstances.getInstance('CopyClipboard', `${id}-code`);
const $defaultMessage = document.getElementById(`${id}-default-message`);
const $successMessage = document.getElementById(`${id}-success-message`);
clipboard.updateOnCopyCallback((clipboard) => {
showSuccess();
// reset to default state
setTimeout(() => {
resetToDefault();
}, 2000);
})
const showSuccess = () => {
$defaultMessage.classList.add('hidden');
$successMessage.classList.remove('hidden');
}
const resetToDefault = () => {
$defaultMessage.classList.remove('hidden');
$successMessage.classList.add('hidden');
}
},
updated() {
this.mounted();
}
};
export default Hooks;

View File

@@ -69,7 +69,7 @@ defmodule Web.CoreComponents do
def code_block(assigns) do
~H"""
<div id={@id} class="relative">
<div id={@id} class="relative" phx-hook="CopyClipboard">
<div id={"#{@id}-nested"} class={[~w[
text-sm text-left text-neutral-50
inline-flex items-center
@@ -84,25 +84,32 @@ defmodule Web.CoreComponents do
><%= render_slot(@inner_block) %></code>
</div>
<button
type="button"
data-copy-to-clipboard-target={"#{@id}-code"}
data-copy-to-clipboard-content-type="innerHTML"
data-copy-to-clipboard-html-entities="true"
title="Click to copy"
class={[
"absolute top-1 right-1",
"items-center",
"cursor-pointer",
"rounded",
"p-1",
"bg-neutral-50/25",
"text-xs text-neutral-50",
"hover:bg-neutral-50 hover:text-neutral-900 hover:opacity-50"
]}
>
<.icon name="hero-clipboard-document" data-icon class="h-4 w-4" />
</button>
<div class="absolute top-1 end-1">
<button
type="button"
data-copy-to-clipboard-target={"#{@id}-code"}
data-copy-to-clipboard-content-type="innerHTML"
data-copy-to-clipboard-html-entities="true"
class={~w[
absolute end-1 top-1 text-gray-900 hover:bg-gray-100
rounded py-2 px-2.5 inline-flex items-center justify-center
bg-white border-gray-200 border h-8
]}
>
<span id={"#{@id}-default-message"} class="inline-flex items-center">
<span class="inline-flex items-center">
<.icon name="hero-clipboard" data-icon class="h-4 w-4 me-1.5" />
<span class="text-xs font-semibold">Copy</span>
</span>
</span>
<span id={"#{@id}-success-message"} class="inline-flex items-center hidden">
<span class="inline-flex items-center">
<.icon name="hero-check" data-icon class="text-green-700 h-4 w-4 me-1.5" />
<span class="text-xs font-semibold text-green-700">Copied</span>
</span>
</span>
</button>
</div>
</div>
"""
end

View File

@@ -292,7 +292,7 @@ defmodule Web.ConnCase do
Floki.attribute(button, "disabled") != "disabled"
end)
|> elements_to_text()
|> Enum.reject(&(&1 in ["", "Previous", "Next", "Clear filters"]))
|> Enum.reject(&(&1 in ["", "Previous", "Next", "Clear filters", "CopyCopied"]))
end
## Wait helpers