From 74914d973c8f37913550826bf7480f63e0929df6 Mon Sep 17 00:00:00 2001 From: Chanhyo Jung Date: Sat, 18 Jul 2026 16:56:01 +0900 Subject: [PATCH] Task/improve copy-to-clipboard feedback in value component (#7338) * Improve copy-to-clipboard feedback * Update changelog --------- Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> --- CHANGELOG.md | 1 + libs/ui/src/lib/value/value.component.html | 9 ++++-- libs/ui/src/lib/value/value.component.ts | 35 ++++++++++++++++++++-- 3 files changed, 40 insertions(+), 5 deletions(-) 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); + } } }