diff --git a/frontend/src/app/components/clipboard/clipboard.component.html b/frontend/src/app/components/clipboard/clipboard.component.html
index d23ccdf8c..c3a18d90b 100644
--- a/frontend/src/app/components/clipboard/clipboard.component.html
+++ b/frontend/src/app/components/clipboard/clipboard.component.html
@@ -1,15 +1,17 @@
-
-
-
+
+
+ {{ copiedMessage }}
diff --git a/frontend/src/app/components/clipboard/clipboard.component.scss b/frontend/src/app/components/clipboard/clipboard.component.scss
index 49294e548..6ae620ae7 100644
--- a/frontend/src/app/components/clipboard/clipboard.component.scss
+++ b/frontend/src/app/components/clipboard/clipboard.component.scss
@@ -7,7 +7,19 @@
padding-left: 0.4rem;
}
-img {
- position: relative;
- left: -3px;
-}
\ No newline at end of file
+.copied-message {
+ background: color-mix(in srgb, var(--active-bg) 95%, transparent);
+ color: var(--fg);
+ font-family: sans-serif;
+ font-size: .8rem;
+ font-weight: 400;
+ text-decoration: none;
+ text-align: left;
+ padding: .6em .75rem;
+ border-radius: 4px;
+ position: absolute;
+ white-space: nowrap;
+ box-shadow: 0 .5rem 1rem -.5rem #000;
+ z-index: 1000;
+ opacity: .9;
+}
diff --git a/frontend/src/app/components/clipboard/clipboard.component.ts b/frontend/src/app/components/clipboard/clipboard.component.ts
index 6e577d8b3..31f882d12 100644
--- a/frontend/src/app/components/clipboard/clipboard.component.ts
+++ b/frontend/src/app/components/clipboard/clipboard.component.ts
@@ -1,6 +1,4 @@
-import { Component, ViewChild, ElementRef, AfterViewInit, Input, ChangeDetectionStrategy } from '@angular/core';
-import * as ClipboardJS from 'clipboard';
-import * as tlite from 'tlite';
+import { Component, Input, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'app-clipboard',
@@ -8,15 +6,14 @@ import * as tlite from 'tlite';
styleUrls: ['./clipboard.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class ClipboardComponent implements AfterViewInit {
- @ViewChild('btn') btn: ElementRef;
- @ViewChild('buttonWrapper') buttonWrapper: ElementRef;
+export class ClipboardComponent {
@Input() button = false;
@Input() class = 'btn btn-secondary ml-1';
@Input() size: 'small' | 'normal' | 'large' = 'normal';
@Input() text: string;
@Input() leftPadding = true;
copiedMessage: string = $localize`:@@clipboard.copied-message:Copied!`;
+ showMessage = false;
widths = {
small: '10',
@@ -24,22 +21,40 @@ export class ClipboardComponent implements AfterViewInit {
large: '18',
};
- clipboard: any;
+ constructor(
+ private cd: ChangeDetectorRef,
+ ) { }
- constructor() { }
-
- ngAfterViewInit() {
- this.clipboard = new ClipboardJS(this.btn.nativeElement);
- this.clipboard.on('success', () => {
- tlite.show(this.buttonWrapper.nativeElement);
- setTimeout(() => {
- tlite.hide(this.buttonWrapper.nativeElement);
- }, 1000);
- });
+ async copyText() {
+ if (this.text && !this.showMessage) {
+ try {
+ await this.copyToClipboard(this.text);
+ this.showMessage = true;
+ this.cd.markForCheck();
+ setTimeout(() => {
+ this.showMessage = false;
+ this.cd.markForCheck();
+ }, 1000);
+ } catch (error) {
+ console.error('Clipboard copy failed:', error);
+ }
+ }
}
- onDestroy() {
- this.clipboard.destroy();
+ async copyToClipboard(text: string) {
+ if (navigator.clipboard) {
+ await navigator.clipboard.writeText(text);
+ } else {
+ // Use the 'out of viewport hidden text area' trick on non-secure contexts
+ const textarea = document.createElement('textarea');
+ textarea.value = this.text;
+ textarea.style.opacity = '0';
+ textarea.setAttribute('readonly', 'true'); // Don't trigger keyboard on mobile
+ document.body.appendChild(textarea);
+ textarea.select();
+ document.execCommand('copy');
+ textarea.remove();
+ }
}
}