diff --git a/CHANGELOG.md b/CHANGELOG.md
index 045fc395d..cc7ba5626 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
+- Improved the copy-to-clipboard functionality in the value component by providing a visual confirmation
- Improved the language localization for German (`de`)
- Upgraded `stripe` from version `22.2.3` to `22.3.2`
diff --git a/libs/ui/src/lib/value/value.component.html b/libs/ui/src/lib/value/value.component.html
index d5476f42d..14ac7154a 100644
--- a/libs/ui/src/lib/value/value.component.html
+++ b/libs/ui/src/lib/value/value.component.html
@@ -13,13 +13,16 @@
@if (enableCopyToClipboardButton) {
}
diff --git a/libs/ui/src/lib/value/value.component.ts b/libs/ui/src/lib/value/value.component.ts
index dffcb89e7..494f7a347 100644
--- a/libs/ui/src/lib/value/value.component.ts
+++ b/libs/ui/src/lib/value/value.component.ts
@@ -13,13 +13,14 @@ import {
input,
Input,
OnChanges,
+ OnDestroy,
ViewChild
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatSnackBar } from '@angular/material/snack-bar';
import { IonIcon } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons';
-import { copyOutline } from 'ionicons/icons';
+import { checkmarkOutline, copyOutline } from 'ionicons/icons';
import { isNumber } from 'lodash';
import ms from 'ms';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
@@ -32,7 +33,7 @@ import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
styleUrls: ['./value.component.scss'],
templateUrl: './value.component.html'
})
-export class GfValueComponent implements AfterViewInit, OnChanges {
+export class GfValueComponent implements AfterViewInit, OnChanges, OnDestroy {
@Input() colorizeSign = false;
@Input() deviceType: string;
@Input() enableCopyToClipboardButton = false;
@@ -54,22 +55,29 @@ export class GfValueComponent implements AfterViewInit, OnChanges {
public absoluteValue = 0;
public formattedValue = '';
public hasLabel = false;
+ public isCopied = false;
public isNumber = false;
public isString = false;
public useAbsoluteValue = false;
+ public readonly copiedTitle = $localize`The value has been copied to the clipboard`;
+ public readonly copyToClipboardTitle = $localize`Copy to clipboard`;
+
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private clipboard: Clipboard,
private snackBar: MatSnackBar
) {
addIcons({
+ checkmarkOutline,
copyOutline
});
}
public readonly precision = input();
+ private copyToClipboardTimeout: ReturnType;
+
private readonly formatOptions = computed(() => {
const digits = this.hasPrecision ? this.precision() : 2;
@@ -178,6 +186,18 @@ export class GfValueComponent implements AfterViewInit, OnChanges {
public onCopyValueToClipboard() {
this.clipboard.copy(String(this.value));
+ this.isCopied = true;
+
+ if (this.copyToClipboardTimeout) {
+ clearTimeout(this.copyToClipboardTimeout);
+ }
+
+ this.copyToClipboardTimeout = setTimeout(() => {
+ this.isCopied = false;
+
+ this.changeDetectorRef.markForCheck();
+ }, ms('3 seconds'));
+
this.snackBar.open(
'✅ ' + $localize`${this.value} has been copied to the clipboard`,
undefined,
@@ -187,12 +207,23 @@ export class GfValueComponent implements AfterViewInit, OnChanges {
);
}
+ public ngOnDestroy() {
+ if (this.copyToClipboardTimeout) {
+ clearTimeout(this.copyToClipboardTimeout);
+ }
+ }
+
private initializeVariables() {
this.absoluteValue = 0;
this.formattedValue = '';
+ this.isCopied = false;
this.isNumber = false;
this.isString = false;
this.locale = this.locale || getLocale();
this.useAbsoluteValue = false;
+
+ if (this.copyToClipboardTimeout) {
+ clearTimeout(this.copyToClipboardTimeout);
+ }
}
}