diff --git a/libs/ui/src/lib/value/value.component.html b/libs/ui/src/lib/value/value.component.html
index 14080c16d..58211ac01 100644
--- a/libs/ui/src/lib/value/value.component.html
+++ b/libs/ui/src/lib/value/value.component.html
@@ -4,7 +4,23 @@
}
-
+
+
+
+
+
+ @if (enableCopyToClipboardButton) {
+
+
+
+ }
+
+
@if (value || value === 0 || value === null) {
}
+ @if (!hasLabel) {
+
+ }
}
@@ -88,6 +107,9 @@
@if (size === 'large') {
+ @if (hasLabel) {
+
+ }
@if (subLabel) {
{{ subLabel }}
}
@@ -95,6 +117,9 @@
} @else {
+ @if (hasLabel) {
+
+ }
}
diff --git a/libs/ui/src/lib/value/value.component.scss b/libs/ui/src/lib/value/value.component.scss
index 087bf31e1..7df2d17c4 100644
--- a/libs/ui/src/lib/value/value.component.scss
+++ b/libs/ui/src/lib/value/value.component.scss
@@ -7,4 +7,8 @@
font-variant-numeric: initial;
line-height: 1;
}
+ .mdc-copy {
+ display: inline-flex;
+ vertical-align: middle;
+ }
}
diff --git a/libs/ui/src/lib/value/value.component.stories.ts b/libs/ui/src/lib/value/value.component.stories.ts
index eeebe9399..aea666644 100644
--- a/libs/ui/src/lib/value/value.component.stories.ts
+++ b/libs/ui/src/lib/value/value.component.stories.ts
@@ -32,6 +32,32 @@ export const Loading: Story = {
}
};
+export const IsinNoLabel: Story = {
+ args: {
+ isCurrency: false,
+ locale: 'en-US',
+ unit: 'USD',
+ value: 'US5949181045',
+ enableCopyToClipboardButton: true
+ },
+ name: 'Without Label with Copy'
+};
+
+export const IsinWithLabel: Story = {
+ args: {
+ isCurrency: false,
+ locale: 'en-US',
+ unit: 'USD',
+ value: 'US5949181045',
+ enableCopyToClipboardButton: true
+ },
+ render: (args) => ({
+ props: args,
+ template: `
ISIN`
+ }),
+ name: 'With Label and Copy'
+};
+
export const Currency: Story = {
args: {
isCurrency: true,
diff --git a/libs/ui/src/lib/value/value.component.ts b/libs/ui/src/lib/value/value.component.ts
index 795e16491..39565fd9e 100644
--- a/libs/ui/src/lib/value/value.component.ts
+++ b/libs/ui/src/lib/value/value.component.ts
@@ -1,17 +1,25 @@
import { getLocale } from '@ghostfolio/common/helper';
+import { Clipboard } from '@angular/cdk/clipboard';
import { CommonModule } from '@angular/common';
import {
- CUSTOM_ELEMENTS_SCHEMA,
+ AfterViewInit,
ChangeDetectionStrategy,
Component,
+ computed,
+ CUSTOM_ELEMENTS_SCHEMA,
+ ElementRef,
+ input,
Input,
OnChanges,
- computed,
- input
+ ViewChild
} from '@angular/core';
+import { MatSnackBar } from '@angular/material/snack-bar';
import { IonIcon } from '@ionic/angular/standalone';
+import { addIcons } from 'ionicons';
+import { copyOutline } from 'ionicons/icons';
import { isNumber } from 'lodash';
+import ms from 'ms';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
@Component({
@@ -22,9 +30,10 @@ import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
styleUrls: ['./value.component.scss'],
templateUrl: './value.component.html'
})
-export class GfValueComponent implements OnChanges {
+export class GfValueComponent implements AfterViewInit, OnChanges {
@Input() colorizeSign = false;
@Input() deviceType: string;
+ @Input() enableCopyToClipboardButton = false;
@Input() icon = '';
@Input() isAbsolute = false;
@Input() isCurrency = false;
@@ -37,12 +46,24 @@ export class GfValueComponent implements OnChanges {
@Input() unit = '';
@Input() value: number | string = '';
+ @ViewChild('labelContent', { static: false }) labelContent!: ElementRef;
+
public absoluteValue = 0;
public formattedValue = '';
+ public hasLabel = false;
public isNumber = false;
public isString = false;
public useAbsoluteValue = false;
+ public constructor(
+ private clipboard: Clipboard,
+ private snackBar: MatSnackBar
+ ) {
+ addIcons({
+ copyOutline
+ });
+ }
+
public readonly precision = input
();
private readonly formatOptions = computed(() => {
@@ -59,6 +80,20 @@ export class GfValueComponent implements OnChanges {
return precision !== undefined && precision >= 0;
}
+ private initializeVariables() {
+ this.absoluteValue = 0;
+ this.formattedValue = '';
+ this.isNumber = false;
+ this.isString = false;
+ this.locale = this.locale || getLocale();
+ this.useAbsoluteValue = false;
+ }
+
+ public ngAfterViewInit() {
+ const el = this.labelContent.nativeElement;
+ this.hasLabel = el.textContent.trim().length > 0 || el.children.length > 0;
+ }
+
public ngOnChanges() {
this.initializeVariables();
@@ -137,12 +172,20 @@ export class GfValueComponent implements OnChanges {
}
}
- private initializeVariables() {
- this.absoluteValue = 0;
- this.formattedValue = '';
- this.isNumber = false;
- this.isString = false;
- this.locale = this.locale || getLocale();
- this.useAbsoluteValue = false;
+ public onCopyValueToClipboard() {
+ if (this.value || this.value === 0) {
+ this.clipboard.copy(this.formattedValue);
+ this.snackBar.open(
+ '✅ ' + $localize`Value has been copied to the clipboard`,
+ undefined,
+ {
+ duration: ms('3 seconds')
+ }
+ );
+ } else {
+ this.snackBar.open($localize`Value is empty`, undefined, {
+ duration: ms('3 seconds')
+ });
+ }
}
}