CopyButton
composantA button that copies a text, given or read from another element, and says so.
Aperçu
Code
<twig:CopyButton text="https://example.com/invite" />
Code
<twig:CopyButton text="https://example.com/invite" />
Installation
php bin/console ux:install copy-button --kit=github.com/parler-haut-interagir-librement/ux-kit
Propriétés
| Propriété | Type | Par défaut | Description |
|---|---|---|---|
text |
string|null |
null |
The text to copy. |
source |
string|null |
null |
The CSS selector of the element whose value or text is copied, when text is null. |
label |
string |
'Copy' |
The name of the button. |
copiedLabel |
string |
'Copied' |
What the button says once the text is copied. |
failedLabel |
string |
'Copy failed' |
What the button says when the browser refuses. |
duration |
int |
2000 |
How long the outcome shows, in milliseconds. |
Accessibility
What happened is written on the button and in a polite live region. The button stays hidden where the browser offers no clipboard, which needs a secure context.
Fichiers à copier
assets/controllers/copy_button_controller.js
import { Controller } from '@hotwired/stimulus';
/*
* Copies a text to the clipboard, says so on the button for a moment and in the live region. The button shows only
* where the browser can copy.
*/
export default class extends Controller {
static targets = ['trigger', 'status'];
static values = {
text: String,
source: String,
label: String,
copiedLabel: String,
failedLabel: String,
duration: { type: Number, default: 2000 },
};
#timer = null;
connect() {
this.triggerTarget.hidden = !navigator.clipboard;
}
disconnect() {
clearTimeout(this.#timer);
}
async copy() {
try {
await navigator.clipboard.writeText(this.#text());
this.#say(this.copiedLabelValue);
this.dispatch('copied');
} catch {
this.#say(this.failedLabelValue);
}
}
#text() {
if (this.sourceValue === '') {
return this.textValue;
}
const source = document.querySelector(this.sourceValue);
return source?.value ?? source?.textContent ?? '';
}
#say(message) {
clearTimeout(this.#timer);
this.triggerTarget.textContent = message;
this.statusTarget.textContent = message;
this.#timer = setTimeout(() => {
this.triggerTarget.textContent = this.labelValue;
this.statusTarget.textContent = '';
}, this.durationValue);
}
}
templates/components/CopyButton.html.twig
{#-
# A button that copies a text: given here, or read from another element. It stays hidden while no script can copy, and
# says what happened in a live region of its own.
#}
{%- props
## string|null The text to copy.
text = null,
## string|null The CSS selector of the element whose value or text is copied, when `text` is null.
source = null,
## string The name of the button.
label = 'Copy',
## string What the button says once the text is copied.
copiedLabel = 'Copied',
## string What the button says when the browser refuses.
failedLabel = 'Copy failed',
## int How long the outcome shows, in milliseconds.
duration = 2000
-%}
<span data-copy-button-text-value="{{ text ?? '' }}" data-copy-button-source-value="{{ source ?? '' }}" data-copy-button-label-value="{{ label }}" data-copy-button-copied-label-value="{{ copiedLabel }}" data-copy-button-failed-label-value="{{ failedLabel }}" data-copy-button-duration-value="{{ duration }}"{{ attributes.defaults({class: 'isCopyButton', 'data-controller': 'copy-button'}) }}><button type="button" class="isCopyButtonTrigger" hidden data-copy-button-target="trigger" data-action="copy-button#copy">{{ label }}</button><span class="isVisuallyHidden" role="status" aria-live="polite" data-copy-button-target="status"></span></span>