taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

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:
Mpackages/web-util/src/components/Attention.tsx | 98++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
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; +}