libs/ui/copy/src/lib/copy/copy.component.ts
A component to facilitate the easy copying of text
<ts-copy
[disableInitialSelection]="true"
[enableQuickCopy]="true"
[format]="icon"
theme="accent"
>My text to copy!</ts-copy>
<example-url>https://getterminus.github.io/ui-demos-release/components/copy</example-url>
changeDetection | ChangeDetectionStrategy.OnPush |
encapsulation | ViewEncapsulation.None |
exportAs | tsCopy |
host | { |
selector | ts-copy |
styleUrls | ./copy.component.scss |
templateUrl | ./copy.component.html |
Properties |
|
Methods |
|
Inputs |
Accessors |
constructor(documentService: TsDocumentService, windowService: TsWindowService)
|
|||||||||
Parameters :
|
disableInitialSelection | |
Default value : false
|
|
Define if the initial click should select the contents |
enableQuickCopy | |
Default value : true
|
|
Define if the copy to clipboard functionality is enabled |
format | |
Define the UI style |
theme | |
Type : TsStyleThemeTypes
|
|
Default value : 'primary'
|
|
Define the component theme |
Public copyToClipboard | ||||||||
copyToClipboard(text: string)
|
||||||||
Copy text to the user's clipboard
Parameters :
Returns :
void
|
Public resetSelection |
resetSelection()
|
Reset the text selection
NOTE: The containing div must have a
Returns :
void
|
Public selectText | ||||||||||||||||
selectText(element: HTMLDivElement, hasSelected: boolean, disabled: boolean)
|
||||||||||||||||
Select the text content of the passed in element
Parameters :
Returns :
boolean
The value representing if the copy was successful |
Public content |
Type : ElementRef
|
Decorators :
@ViewChild('content', {static: true})
|
Define access to the wrapper around the content to be copied |
Public copyIcon |
Default value : faCopy
|
Define the copy icon |
Public hasSelected |
Default value : false
|
Internal flag to track if the contents have been selected |
Public rippleColor |
Type : string
|
Default value : '#1a237e'
|
Define the color of the material ripple |
Public tooltipCollection |
Type : QueryList<TsTooltipComponent>
|
Decorators :
@ViewChildren(TsTooltipComponent)
|
Define access to all tooltip instances |
format | ||||
getformat()
|
||||
setformat(value)
|
||||
Define the UI style
Parameters :
Returns :
void
|
textContent |
gettextContent()
|
Return the inner text content
Returns :
string
|
import {
ChangeDetectionStrategy,
Component,
ElementRef,
Input,
isDevMode,
QueryList,
ViewChild,
ViewChildren,
ViewEncapsulation,
} from '@angular/core';
import { faCopy } from '@fortawesome/pro-solid-svg-icons/faCopy';
import {
TsDocumentService,
TsWindowService,
} from '@terminus/fe-utilities';
import { TsTooltipComponent } from '@terminus/ui-tooltip';
import {
TsStyleThemeTypes,
TsUILibraryError,
} from '@terminus/ui-utilities';
/**
* The possible display formats for {@link TsCopyComponent}
*/
export type TsCopyDisplayFormat
= 'standard'
| 'minimal'
| 'icon'
;
/**
* A component to facilitate the easy copying of text
*
* @example
* <ts-copy
* [disableInitialSelection]="true"
* [enableQuickCopy]="true"
* [format]="icon"
* theme="accent"
* >My text to copy!</ts-copy>
*
* <example-url>https://getterminus.github.io/ui-demos-release/components/copy</example-url>
*/
@Component({
selector: 'ts-copy',
templateUrl: './copy.component.html',
styleUrls: ['./copy.component.scss'],
host: {
'class': 'ts-copy',
'[class.ts-copy--standard]': 'format === "standard"',
'[class.ts-copy--minimal]': 'format === "minimal"',
'[class.ts-copy--icon]': 'format === "icon"',
'[class.ts-copy--primary]': 'theme === "primary"',
'[class.ts-copy--accent]': 'theme === "accent"',
'[class.ts-copy--warn]': 'theme === "warn"',
},
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
exportAs: 'tsCopy',
})
export class TsCopyComponent {
/**
* Store a reference to the document object
*/
private document: Document = this.documentService.document;
/**
* Internal flag to track if the contents have been selected
*/
public hasSelected = false;
/**
* Define the copy icon
*/
public copyIcon = faCopy;
/**
* Define the color of the material ripple
*/
// TODO: This color should be coming from a config
// https://github.com/GetTerminus/terminus-ui/issues/1490
public rippleColor = '#1a237e';
/**
* Store a reference to the window object
*/
private window: Window = this.windowService.nativeWindow;
/**
* Define access to the wrapper around the content to be copied
*/
@ViewChild('content', { static: true })
public content!: ElementRef;
/**
* Define access to all tooltip instances
*/
@ViewChildren(TsTooltipComponent)
public tooltipCollection!: QueryList<TsTooltipComponent>;
/**
* Define if the initial click should select the contents
*/
@Input()
public disableInitialSelection = false;
/**
* Define if the copy to clipboard functionality is enabled
*/
@Input()
public enableQuickCopy = true;
/**
* Define the UI style
*
* @param value
*/
@Input()
public set format(value: TsCopyDisplayFormat) {
this._format = value ? value : 'standard';
if (this.format === 'icon' && !this.enableQuickCopy) {
this.enableQuickCopy = true;
// istanbul ignore else
if (isDevMode()) {
throw new TsUILibraryError(`'enableQuickCopy' must be set to 'true' when using the icon only display mode`);
}
}
}
public get format(): TsCopyDisplayFormat {
return this._format;
}
private _format: TsCopyDisplayFormat = 'standard';
/**
* Define the component theme
*/
@Input()
public theme: TsStyleThemeTypes = 'primary';
constructor(
private documentService: TsDocumentService,
private windowService: TsWindowService,
) {}
/**
* Return the inner text content
*
* @returns The text content of the inner <ng-content>
*/
public get textContent(): string {
const hasInnerText = this.content && this.content.nativeElement && this.content.nativeElement.innerText;
return hasInnerText ? this.content.nativeElement.innerText : '';
}
/**
* Select the text content of the passed in element
*
* @param element - The element whose text should be selected
* @param hasSelected - The flag defining if the selection has already been made
* @param disabled - The flag defining if the selection functionality should be disabled
* @returns The value representing if the copy was successful
*/
public selectText(element: HTMLDivElement, hasSelected: boolean, disabled: boolean): boolean {
// If this functionality is disabled OR the text has already been selected,
// do not intercept any more clicks until the focus is reset
if (disabled || hasSelected) {
return false;
}
const selection = this.window.getSelection();
// NOTE: Adding the type of 'Range' to this causes an error with `range.selectNodeContents`
// `Argument of type ElementRef is not assignable to type 'Node'`
const range = this.document.createRange();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
range.selectNodeContents(element as any);
selection?.removeAllRanges();
selection?.addRange(range);
this.hasSelected = true;
return true;
}
/**
* Reset the text selection
* NOTE: The containing div must have a `tabindex` set or no blur event will be fired
*/
public resetSelection(): void {
this.hasSelected = false;
}
/**
* Copy text to the user's clipboard
*
* @param text - The text to copy
*/
public copyToClipboard(text: string): void {
// Create a hidden textarea to seed with text content
// FIXME: During the upgrade to TS 3.7.x 'createElement' is marked as deprecated but the signature for the deprecated and new methods
// are the same. So even when using the 'new' method, a deprecation error is still reported.
// I believe the new format should be `this.document.createElement<textarea>('textarea');`
// eslint-disable-next-line deprecation/deprecation
const target = this.document.createElement('textarea');
target.className = 'targetElement';
target.style.position = 'absolute';
target.style.left = '101%';
target.style.top = '0';
target.style.width = '1px';
target.style.height = '1px';
target.textContent = text;
// Add the textarea, focus and select the text
this.document.body.appendChild(target);
target.focus();
target.setSelectionRange(0, target.value.length);
// Copy the selection or fall back to prompt
try {
this.document.execCommand('copy');
target.remove();
} catch (error) {
// Fall back to the native alert
this.window.prompt('Copy to clipboard: Ctrl+C, Enter', text);
}
}
}