ngx-clipboard
Version:
angular 2 clipboard
308 lines (300 loc) • 13.4 kB
JavaScript
import { DOCUMENT, CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { Injectable, Inject, Optional, EventEmitter, Directive, Input, Output, NgModule } from '@angular/core';
import { WINDOW } from 'ngx-window-token';
import { Subject } from 'rxjs';
/**
* The following code is heavily copied from https://github.com/zenorocha/clipboard.js
*/
class ClipboardService {
constructor(ngZone, document, window) {
this.ngZone = ngZone;
this.document = document;
this.window = window;
this.copySubject = new Subject();
this.copyResponse$ = this.copySubject.asObservable();
this.config = {};
}
configure(config) {
this.config = config;
}
copy(content) {
if (!this.isSupported || !content) {
return this.pushCopyResponse({ isSuccess: false, content });
}
const copyResult = this.copyFromContent(content);
if (copyResult) {
return this.pushCopyResponse({ content, isSuccess: copyResult });
}
return this.pushCopyResponse({ isSuccess: false, content });
}
get isSupported() {
return !!this.document.queryCommandSupported && !!this.document.queryCommandSupported('copy') && !!this.window;
}
isTargetValid(element) {
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
if (element.hasAttribute('disabled')) {
throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');
}
return true;
}
throw new Error('Target should be input or textarea');
}
/**
* Attempts to copy from an input `targetElm`
*/
copyFromInputElement(targetElm, isFocus = true) {
try {
this.selectTarget(targetElm);
const re = this.copyText();
this.clearSelection(isFocus ? targetElm : undefined, this.window);
return re && this.isCopySuccessInIE11();
}
catch (error) {
return false;
}
}
/**
* This is a hack for IE11 to return `true` even if copy fails.
*/
isCopySuccessInIE11() {
const clipboardData = this.window['clipboardData'];
if (clipboardData && clipboardData.getData) {
if (!clipboardData.getData('Text')) {
return false;
}
}
return true;
}
/**
* Creates a fake textarea element, sets its value from `text` property,
* and makes a selection on it.
*/
copyFromContent(content, container = this.document.body) {
// check if the temp textarea still belongs to the current container.
// In case we have multiple places using ngx-clipboard, one is in a modal using container but the other one is not.
if (this.tempTextArea && !container.contains(this.tempTextArea)) {
this.destroy(this.tempTextArea.parentElement || undefined);
}
if (!this.tempTextArea) {
this.tempTextArea = this.createTempTextArea(this.document, this.window);
try {
container.appendChild(this.tempTextArea);
}
catch (error) {
throw new Error('Container should be a Dom element');
}
}
this.tempTextArea.value = content;
const toReturn = this.copyFromInputElement(this.tempTextArea, false);
if (this.config.cleanUpAfterCopy) {
this.destroy(this.tempTextArea.parentElement || undefined);
}
return toReturn;
}
/**
* Remove temporary textarea if any exists.
*/
destroy(container = this.document.body) {
if (this.tempTextArea) {
container.removeChild(this.tempTextArea);
// removeChild doesn't remove the reference from memory
this.tempTextArea = undefined;
}
}
/**
* Select the target html input element.
*/
selectTarget(inputElement) {
inputElement.select();
inputElement.setSelectionRange(0, inputElement.value.length);
return inputElement.value.length;
}
copyText() {
return this.document.execCommand('copy');
}
/**
* Moves focus away from `target` and back to the trigger, removes current selection.
*/
clearSelection(inputElement, window) {
inputElement && inputElement.focus();
window.getSelection()?.removeAllRanges();
}
/**
* Creates a fake textarea for copy command.
*/
createTempTextArea(doc, window) {
const isRTL = doc.documentElement.getAttribute('dir') === 'rtl';
let ta;
ta = doc.createElement('textarea');
// Prevent zooming on iOS
ta.style.fontSize = '12pt';
// Reset box model
ta.style.border = '0';
ta.style.padding = '0';
ta.style.margin = '0';
// Move element out of screen horizontally
ta.style.position = 'absolute';
ta.style[isRTL ? 'right' : 'left'] = '-9999px';
// Move element to the same position vertically
const yPosition = window.pageYOffset || doc.documentElement.scrollTop;
ta.style.top = yPosition + 'px';
ta.setAttribute('readonly', '');
return ta;
}
/**
* Pushes copy operation response to copySubject, to provide global access
* to the response.
*/
pushCopyResponse(response) {
if (this.copySubject.observers.length > 0) {
this.ngZone.run(() => {
this.copySubject.next(response);
});
}
}
/**
* @deprecated use pushCopyResponse instead.
*/
pushCopyReponse(response) {
this.pushCopyResponse(response);
}
}
ClipboardService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: ClipboardService, deps: [{ token: i0.NgZone }, { token: DOCUMENT }, { token: WINDOW, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
ClipboardService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: ClipboardService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: ClipboardService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: i0.NgZone }, { type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [WINDOW]
}] }]; } });
class ClipboardDirective {
constructor(ngZone, host, renderer, clipboardSrv) {
this.ngZone = ngZone;
this.host = host;
this.renderer = renderer;
this.clipboardSrv = clipboardSrv;
this.cbOnSuccess = new EventEmitter();
this.cbOnError = new EventEmitter();
this.onClick = (event) => {
if (!this.clipboardSrv.isSupported) {
this.handleResult(false, undefined, event);
}
else if (this.targetElm && this.clipboardSrv.isTargetValid(this.targetElm)) {
this.handleResult(this.clipboardSrv.copyFromInputElement(this.targetElm), this.targetElm.value, event);
}
else if (this.cbContent) {
this.handleResult(this.clipboardSrv.copyFromContent(this.cbContent, this.container), this.cbContent, event);
}
};
}
// eslint-disable-next-line no-empty, @typescript-eslint/no-empty-function
ngOnInit() {
this.ngZone.runOutsideAngular(() => {
// By default each host listener schedules change detection and also wrapped
// into additional function that calls `markForCheck()`. We're listening the `click`
// event in the context of the root zone to avoid running unnecessary change detections,
// since this directive doesn't do anything template-related (e.g. updates template variables).
this.clickListener = this.renderer.listen(this.host.nativeElement, 'click', this.onClick);
});
}
ngOnDestroy() {
if (this.clickListener) {
this.clickListener();
}
this.clipboardSrv.destroy(this.container);
}
/**
* Fires an event based on the copy operation result.
* @param succeeded
*/
handleResult(succeeded, copiedContent, event) {
let response = {
isSuccess: succeeded,
content: copiedContent,
successMessage: this.cbSuccessMsg,
event
};
if (succeeded) {
if (this.cbOnSuccess.observed) {
this.ngZone.run(() => {
this.cbOnSuccess.emit(response);
});
}
}
else {
if (this.cbOnError.observed) {
this.ngZone.run(() => {
this.cbOnError.emit(response);
});
}
}
this.clipboardSrv.pushCopyResponse(response);
}
}
ClipboardDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: ClipboardDirective, deps: [{ token: i0.NgZone }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: ClipboardService }], target: i0.ɵɵFactoryTarget.Directive });
ClipboardDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1.4", type: ClipboardDirective, selector: "[ngxClipboard]", inputs: { targetElm: ["ngxClipboard", "targetElm"], container: "container", cbContent: "cbContent", cbSuccessMsg: "cbSuccessMsg" }, outputs: { cbOnSuccess: "cbOnSuccess", cbOnError: "cbOnError" }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: ClipboardDirective, decorators: [{
type: Directive,
args: [{ selector: '[ngxClipboard]' }]
}], ctorParameters: function () { return [{ type: i0.NgZone }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: ClipboardService }]; }, propDecorators: { targetElm: [{
type: Input,
args: ['ngxClipboard']
}], container: [{
type: Input
}], cbContent: [{
type: Input
}], cbSuccessMsg: [{
type: Input
}], cbOnSuccess: [{
type: Output
}], cbOnError: [{
type: Output
}] } });
class ClipboardIfSupportedDirective {
constructor(_clipboardService, _viewContainerRef, _templateRef) {
this._clipboardService = _clipboardService;
this._viewContainerRef = _viewContainerRef;
this._templateRef = _templateRef;
}
ngOnInit() {
if (this._clipboardService.isSupported) {
this._viewContainerRef.createEmbeddedView(this._templateRef);
}
}
}
ClipboardIfSupportedDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: ClipboardIfSupportedDirective, deps: [{ token: ClipboardService }, { token: i0.ViewContainerRef }, { token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
ClipboardIfSupportedDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.1.4", type: ClipboardIfSupportedDirective, selector: "[ngxClipboardIfSupported]", ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: ClipboardIfSupportedDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngxClipboardIfSupported]'
}]
}], ctorParameters: function () { return [{ type: ClipboardService }, { type: i0.ViewContainerRef }, { type: i0.TemplateRef }]; } });
class ClipboardModule {
}
ClipboardModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: ClipboardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ClipboardModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.1.4", ngImport: i0, type: ClipboardModule, declarations: [ClipboardDirective, ClipboardIfSupportedDirective], imports: [CommonModule], exports: [ClipboardDirective, ClipboardIfSupportedDirective] });
ClipboardModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: ClipboardModule, imports: [CommonModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.4", ngImport: i0, type: ClipboardModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule],
declarations: [ClipboardDirective, ClipboardIfSupportedDirective],
exports: [ClipboardDirective, ClipboardIfSupportedDirective]
}]
}] });
/*
* Public API Surface of ngx-clipboard
*/
/**
* Generated bundle index. Do not edit.
*/
export { ClipboardDirective, ClipboardIfSupportedDirective, ClipboardModule, ClipboardService };
//# sourceMappingURL=ngx-clipboard.mjs.map