mirror of
https://github.com/coollabsio/coolify.git
synced 2026-08-24 02:24:11 -05:00
feat(ui): add shared copy button component
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { initializeCopyButtonComponent } from './copy-button.js';
|
||||
import { initializeTerminalComponent } from './terminal.js';
|
||||
|
||||
// Livewire 3.5.19+ re-applies `x-cloak` to morphed elements during wire:navigate
|
||||
@@ -12,6 +13,7 @@ document.addEventListener('livewire:navigated', () => {
|
||||
// Keeping this registration independent from the current route also makes it
|
||||
// available before Alpine processes terminal markup after wire:navigate.
|
||||
document.addEventListener('alpine:init', initializeTerminalComponent);
|
||||
document.addEventListener('alpine:init', initializeCopyButtonComponent);
|
||||
|
||||
/**
|
||||
* Smooth-scroll a settings section into view, then flash its border for 500ms
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Alpine data provider for the <x-copy-button> component (x-data="copyButton").
|
||||
export function initializeCopyButtonComponent() {
|
||||
window.Alpine.data('copyButton', () => ({
|
||||
copied: false,
|
||||
async copy(value) {
|
||||
if (value === null || value === undefined) {
|
||||
window.toast('Value is not available.', { type: 'warning' });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (navigator.clipboard?.writeText && window.isSecureContext) {
|
||||
await navigator.clipboard.writeText(value);
|
||||
} else {
|
||||
// Deprecated, but the only copy path on plain http (non-secure contexts).
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = value;
|
||||
textarea.setAttribute('readonly', '');
|
||||
textarea.style.position = 'fixed';
|
||||
textarea.style.left = '-9999px';
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
const ok = document.execCommand('copy');
|
||||
document.body.removeChild(textarea);
|
||||
if (!ok) {
|
||||
throw new Error('Copy command was rejected.');
|
||||
}
|
||||
}
|
||||
this.copied = true;
|
||||
setTimeout(() => (this.copied = false), 1200);
|
||||
} catch (e) {
|
||||
window.toast('Could not copy to clipboard.', { type: 'warning' });
|
||||
}
|
||||
},
|
||||
}));
|
||||
}
|
||||
@@ -1,22 +1,16 @@
|
||||
@props([
|
||||
'value',
|
||||
'value' => null,
|
||||
'resolve' => null,
|
||||
'label' => 'Copy to clipboard',
|
||||
])
|
||||
|
||||
<button type="button"
|
||||
x-data="{ copied: false }"
|
||||
x-on:click.prevent.stop="await window.copyToClipboard({{ Js::from($value) }}); copied = true; setTimeout(() => copied = false, 1000)"
|
||||
{{ $attributes->class('inline-flex size-6 shrink-0 items-center justify-center rounded-md text-neutral-500 transition-colors hover:bg-neutral-100 hover:text-black disabled:pointer-events-none disabled:opacity-40 dark:text-fg-dim dark:hover:bg-white/[0.06] dark:hover:text-white') }}
|
||||
title="{{ $label }}" aria-label="{{ $label }}" @disabled(blank($value))>
|
||||
<svg x-show="!copied" class="size-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
aria-hidden="true">
|
||||
<path d="M8 8.75H6.5A2.25 2.25 0 0 0 4.25 11v6.5a2.25 2.25 0 0 0 2.25 2.25H13a2.25 2.25 0 0 0 2.25-2.25V16"
|
||||
stroke-width="1.5" stroke-linecap="round" />
|
||||
<rect x="8.75" y="4.25" width="11" height="11" rx="2.25" stroke-width="1.5" />
|
||||
</svg>
|
||||
<svg x-show="copied" x-cloak class="size-3.5 text-green-500" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" aria-hidden="true">
|
||||
<path d="m6.75 12.25 3.5 3.5 7-7" stroke-width="1.5" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
</svg>
|
||||
@php
|
||||
$valueExpression = $resolve ?? \Illuminate\Support\Js::from($value);
|
||||
@endphp
|
||||
|
||||
<button type="button" title="{{ $label }}" aria-label="{{ $label }}"
|
||||
{{ $attributes->class(['icon-button shrink-0']) }} @disabled($resolve === null && blank($value))
|
||||
x-data="copyButton" @click="copy(await ({{ $valueExpression }}))">
|
||||
<x-reicon name="copy" x-show="!copied" class="size-3.5" />
|
||||
<x-reicon name="check" x-cloak x-show="copied" class="size-3.5 text-success" />
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user