Refactor clipboard to use native clipboard API
This commit is contained in:
parent
3b4eda432f
commit
cab01f7f26
@ -1,15 +1,17 @@
|
|||||||
<ng-template [ngIf]="button" [ngIfElse]="btnLink">
|
<ng-template [ngIf]="button" [ngIfElse]="btnLink">
|
||||||
<button #btn [attr.data-clipboard-text]="text" [class]="class" type="button" [disabled]="text === ''">
|
<button [class]="class" type="button" [disabled]="text === ''" style="box-shadow: none;" (click)="copyText()">
|
||||||
<span #buttonWrapper [attr.data-tlite]="copiedMessage" style="position: relative;top: -2px;left: 1px;">
|
<span style="position: relative;top: -2px;left: 1px;">
|
||||||
<app-svg-images name="clippy" [width]="widths[size]" viewBox="0 0 1000 1000"></app-svg-images>
|
<app-svg-images name="clippy" [width]="widths[size]" viewBox="0 0 1000 1000"></app-svg-images>
|
||||||
|
<span *ngIf="showMessage" class="copied-message" style="top: 29px; left: -23.5px;">{{ copiedMessage }}</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #btnLink>
|
<ng-template #btnLink>
|
||||||
<span #buttonWrapper [attr.data-tlite]="copiedMessage" style="position: relative;">
|
<span style="position: relative;">
|
||||||
<button #btn class="btn btn-sm btn-link pt-0 {{ leftPadding ? 'padding' : '' }}" [attr.data-clipboard-text]="text">
|
<button class="btn btn-sm btn-link pt-0 {{ leftPadding ? 'padding' : '' }}" style="box-shadow: none;" (click)="copyText()">
|
||||||
<app-svg-images name="clippy" [width]="widths[size]" viewBox="0 0 1000 1000"></app-svg-images>
|
<app-svg-images name="clippy" [width]="widths[size]" viewBox="0 0 1000 1000"></app-svg-images>
|
||||||
</button>
|
</button>
|
||||||
|
<span *ngIf="showMessage" class="copied-message" style="top: 29px; left: -23.5px;">{{ copiedMessage }}</span>
|
||||||
</span>
|
</span>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -7,7 +7,19 @@
|
|||||||
padding-left: 0.4rem;
|
padding-left: 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
.copied-message {
|
||||||
position: relative;
|
background: color-mix(in srgb, var(--active-bg) 95%, transparent);
|
||||||
left: -3px;
|
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;
|
||||||
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import { Component, ViewChild, ElementRef, AfterViewInit, Input, ChangeDetectionStrategy } from '@angular/core';
|
import { Component, Input, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||||
import * as ClipboardJS from 'clipboard';
|
|
||||||
import * as tlite from 'tlite';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-clipboard',
|
selector: 'app-clipboard',
|
||||||
@ -8,15 +6,14 @@ import * as tlite from 'tlite';
|
|||||||
styleUrls: ['./clipboard.component.scss'],
|
styleUrls: ['./clipboard.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class ClipboardComponent implements AfterViewInit {
|
export class ClipboardComponent {
|
||||||
@ViewChild('btn') btn: ElementRef;
|
|
||||||
@ViewChild('buttonWrapper') buttonWrapper: ElementRef;
|
|
||||||
@Input() button = false;
|
@Input() button = false;
|
||||||
@Input() class = 'btn btn-secondary ml-1';
|
@Input() class = 'btn btn-secondary ml-1';
|
||||||
@Input() size: 'small' | 'normal' | 'large' = 'normal';
|
@Input() size: 'small' | 'normal' | 'large' = 'normal';
|
||||||
@Input() text: string;
|
@Input() text: string;
|
||||||
@Input() leftPadding = true;
|
@Input() leftPadding = true;
|
||||||
copiedMessage: string = $localize`:@@clipboard.copied-message:Copied!`;
|
copiedMessage: string = $localize`:@@clipboard.copied-message:Copied!`;
|
||||||
|
showMessage = false;
|
||||||
|
|
||||||
widths = {
|
widths = {
|
||||||
small: '10',
|
small: '10',
|
||||||
@ -24,22 +21,40 @@ export class ClipboardComponent implements AfterViewInit {
|
|||||||
large: '18',
|
large: '18',
|
||||||
};
|
};
|
||||||
|
|
||||||
clipboard: any;
|
constructor(
|
||||||
|
private cd: ChangeDetectorRef,
|
||||||
|
) { }
|
||||||
|
|
||||||
constructor() { }
|
async copyText() {
|
||||||
|
if (this.text && !this.showMessage) {
|
||||||
ngAfterViewInit() {
|
try {
|
||||||
this.clipboard = new ClipboardJS(this.btn.nativeElement);
|
await this.copyToClipboard(this.text);
|
||||||
this.clipboard.on('success', () => {
|
this.showMessage = true;
|
||||||
tlite.show(this.buttonWrapper.nativeElement);
|
this.cd.markForCheck();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
tlite.hide(this.buttonWrapper.nativeElement);
|
this.showMessage = false;
|
||||||
}, 1000);
|
this.cd.markForCheck();
|
||||||
});
|
}, 1000);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Clipboard copy failed:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestroy() {
|
async copyToClipboard(text: string) {
|
||||||
this.clipboard.destroy();
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user