Actions

CopyButton

Copies text to the clipboard and says so for a moment, to the eye and to screen readers.

#button#utility#interactive

Demo

Preview
quillo run app.qwe

Get it

It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.

$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/copy_button.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/copy_button.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/copy_button.qwe
view CopyButtonDemo() =    <div.demo-stack>        <div.demo-row>            <code style="padding: 8px 12px; border-radius: var(--ui-radius-sm); background: var(--ui-subtle); font: 13px var(--ui-mono)">quillo run app.qwe</code>            <CopyButton text="quillo run app.qwe"/>        </div>        <div.demo-row>            <CopyButton text="https://quillolang.org/components/" label="Copy link" variant="soft" size="sm"/>            <CopyButton text="demo-key-not-a-secret" label="Copy key" done="Key copied" variant="ghost"/>        </div>    </div>

Source

ui/copy_button.qwe
// CopyButton: copies a piece of text to the clipboard, then says "Copied" for a// moment (and tells screen readers so).////   <CopyButton text={invite_link}/>//   <CopyButton text={api_key} label="Copy key" variant="soft" size="sm"/>//// text: what it copies. label ("Copy") and done ("Copied"): what it says before// and after; failed ("Could not copy") shows if the browser refuses (the page is// not https, or the user said no).// variant: "outline" (default), "soft", "ghost" or "solid"; size: "sm", "md" or "lg".// It is styled as a Button when button_css is on the page.import ui/theme.qwe use ui_clslet copy_button_css = css`.ui-copy-btn { position: relative; }.ui-copy-btn svg { width: 16px; height: 16px; flex: none; }.ui-copy-btn .ui-copy-check, .ui-copy-ok .ui-copy-icon { display: none; }.ui-copy-ok .ui-copy-check { display: block; color: var(--ui-success); }.ui-copy-labels { display: inline-grid; }.ui-copy-labels > span { grid-area: 1 / 1; text-align: center; }.ui-copy-btn:not(.ui-copy-ok) .ui-copy-done, .ui-copy-ok .ui-copy-label { visibility: hidden; }.ui-copy-fail, .ui-copy-bad .ui-copy-label, .ui-copy-bad .ui-copy-done { display: none; }.ui-copy-bad .ui-copy-fail { display: inline; }`component CopyButton(text: str, label: str = "Copy", done: str = "Copied", failed: str = "Could not copy",                     variant: str = "outline", size: str = "md", class: str? = null) {    style global copy_button_css    state result = ""    // "", "ok" or "bad"    if result != "" { every(1500, () => { result = "" }) }    <span>        <button type="button"                class={ui_cls(["ui-btn", `ui-btn-${variant}`, size != "md" && `ui-btn-${size}`, "ui-copy-btn",                               result != "" && `ui-copy-${result}`, class])}                @click={() => {                    if js("isSecureContext") != true { result = "bad" } else {                        // The promise's callbacks are given undefined, which is no argument: hence `= null`.                        js("navigator.clipboard.writeText", text).then((_ = null) => { result = "ok" }, (_ = null) => { result = "bad" })                    }                }}>            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"                 stroke-linejoin="round" aria-hidden="true">                <path class="ui-copy-icon" d="M10 8h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H10a2 2 0 0 1-2-2V10a2 2 0 0 1 2-2ZM4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2"/>                <path class="ui-copy-check" d="M20 6 9 17l-5-5"/>            </svg>            <span.ui-copy-labels>                <span.ui-copy-label>{label}</span>                <span.ui-copy-done aria-hidden="true">{done}</span>                <span.ui-copy-fail aria-hidden="true">{failed}</span>            </span>        </button>        <span.ui-sr role="status">{result == "ok" ? done : result == "bad" ? failed : ""}</span>    </span>}
Thread
46Useful? Like it, or add to its thread below.

Thread 0 posts

On CopyButton: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.