commit 72bc92a45a461d25be83eb78ccd9c2113e22a47e
parent ccd755e39d730e3973429e4fd0418461cc0b4b34
Author: Sebastian <sebasjm@taler-systems.com>
Date: Thu, 28 May 2026 08:09:25 -0300
add copy button from attention component
Diffstat:
1 file changed, 77 insertions(+), 21 deletions(-)
diff --git a/packages/web-util/src/components/Attention.tsx b/packages/web-util/src/components/Attention.tsx
@@ -4,6 +4,8 @@ import {
assertUnreachable,
} from "@gnu-taler/taler-util";
import { ComponentChildren, Fragment, VNode, h } from "preact";
+import { useRef } from "preact/hooks";
+import { composeRef, saveRef } from "./utils.js";
interface Props {
type?: "info" | "success" | "warning" | "danger" | "error" | "low";
@@ -11,15 +13,18 @@ interface Props {
title: TranslatedString | VNode;
children?: ComponentChildren;
timeout?: Duration;
+ copy?: boolean;
}
export function Attention({
type = "info",
title,
children,
onClose,
+ copy,
timeout = Duration.getForever(),
}: Props): VNode {
- if (type == "error") type = "danger"
+ const divHtml = useRef<HTMLDivElement>();
+ if (type == "error") type = "danger";
return (
<div class={`group attention-${type} mt-2 shadow-lg`}>
{/* {timeout.d_ms === "forever" ? undefined : <style>{`
@@ -86,7 +91,7 @@ export function Attention({
</svg>
)}
</div>
- <div class="ml-3 w-full">
+ <div ref={composeRef(saveRef(divHtml))} class="ml-3 w-full">
<h3 class="text-sm font-bold group-[.attention-info]:text-blue-800 group-[.attention-success]:text-green-800 group-[.attention-warning]:text-yellow-800 group-[.attention-danger]:text-red-800">
{title}
</h3>
@@ -94,27 +99,57 @@ export function Attention({
{children}
</div>
</div>
- {onClose && (
- <div>
- <button
- type="button"
- class="font-semibold items-center rounded bg-transparent px-2 py-1 text-xs text-gray-900 hover:bg-gray-50"
- onClick={(e) => {
- e.preventDefault();
- onClose();
- }}
- >
- <svg
- class="h-5 w-5"
- viewBox="0 0 20 20"
- fill="currentColor"
- aria-hidden="true"
+ <div>
+ <div class="flex">
+ {!copy ? undefined : (
+ <button
+ type="button"
+ class="font-semibold items-center rounded bg-transparent px-2 py-1 text-xs text-gray-900 hover:bg-gray-50"
+ onClick={(e) => {
+ e.preventDefault();
+
+ navigator.clipboard.writeText(
+ fromNodeToText(divHtml.current),
+ );
+ }}
>
- <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
- </svg>
- </button>
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ stroke-width="1.5"
+ stroke="currentColor"
+ class="size-6"
+ >
+ <path
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75"
+ />
+ </svg>
+ </button>
+ )}
+ {onClose && (
+ <button
+ type="button"
+ class="font-semibold items-center rounded bg-transparent px-2 py-1 text-xs text-gray-900 hover:bg-gray-50"
+ onClick={(e) => {
+ e.preventDefault();
+ onClose();
+ }}
+ >
+ <svg
+ class="h-5 w-5"
+ viewBox="0 0 20 20"
+ fill="currentColor"
+ aria-hidden="true"
+ >
+ <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
+ </svg>
+ </button>
+ )}
</div>
- )}
+ </div>
</div>
</div>
{timeout.d_ms === "forever" ? undefined : (
@@ -127,3 +162,24 @@ export function Attention({
</div>
);
}
+
+function fromNodeToText(node: ChildNode | undefined) {
+ var i, result, text, child;
+ result = "";
+
+ if (node)
+ for (i = 0; i < node.childNodes.length; i++) {
+ child = node.childNodes[i];
+ text = null;
+ if (child.nodeType === 1) {
+ text = fromNodeToText(child);
+ } else if (child.nodeType === 3) {
+ text = child.nodeValue;
+ }
+ if (text) {
+ result += "\n";
+ result += text;
+ }
+ }
+ return result;
+}