Skip to content

Commit aa5fa04

Browse files
committed
[#noissue] Add copy & paste interface on agent-list
- Update supported browser version info for the clipboard api usage
1 parent 41e85ac commit aa5fa04

File tree

7 files changed

+148
-11
lines changed

7 files changed

+148
-11
lines changed

web/src/main/angular/src/app/core/components/server-and-agent-list/server-and-agent-list.component.css

+27
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
margin-left: 15px;
2424
font-size: 90%;
2525
max-width: calc(100% - 15px);
26+
position: relative;
2627
}
2728
.l-name-wrapper {
2829
display: block;
@@ -41,3 +42,29 @@
4142
.l-icon-alert > img {
4243
vertical-align: middle;
4344
}
45+
.l-depth-1 .l-name-wrapper:hover .copy-button {
46+
display: inline-block;
47+
}
48+
49+
.copy-button {
50+
display: none;
51+
position: absolute;
52+
/* right: 25px; */
53+
/* top: 0px; */
54+
bottom: -5px;
55+
z-index: 100;
56+
/* left: 100%; */
57+
}
58+
59+
.copy-button:focus {
60+
outline: 0;
61+
}
62+
63+
.fa-copy {
64+
font-size: 15px;
65+
color: var(--icon-default);
66+
}
67+
68+
.fa-copy:hover {
69+
color: var(--icon-hover);
70+
}

web/src/main/angular/src/app/core/components/server-and-agent-list/server-and-agent-list.component.html

+6-2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@
99
<ul class="l-agent-list l-depth-1" [@collapseSpread]="getCollapsedState(server.groupName)">
1010
<li class="l-agent" *ngFor="let agent of server.instancesList" (click)="onSelectAgent(agent.agentId)" [attr.title]="agent.agentName || agent.agentId">
1111
<div class="l-agent-name-wrapper l-name-wrapper" [class.active]="agent.agentId === agentId">
12-
<span class="l-name">
13-
<i class="fas fa-hdd"></i> {{getAgentLabel(agent)}}
12+
<span class="l-name" #labelWrapper>
13+
<i class="fas fa-hdd"></i>
14+
<span class="agent-label" #label>{{getAgentLabel(agent)}}</span>
1415
</span>
16+
<button type="button" class="copy-button" [style.left]="getLeftPosition(labelWrapper, label)" (click)="onClickCopy($event, getAgentLabel(agent))">
17+
<span class="far fa-copy" title="Copy to Clipboard"></span>
18+
</button>
1519
<span class="l-icon-alert" *ngIf="agent.status.state.code !== 100"><img [src]="getIconPath(agent.status.state.code)"></span>
1620
</div>
1721
</li>

web/src/main/angular/src/app/core/components/server-and-agent-list/server-and-agent-list.component.ts

+39-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
1+
import { Component, OnInit, Input, Output, EventEmitter, Renderer2 } from '@angular/core';
22
import { trigger, state, style, animate, transition } from '@angular/animations';
3+
import { from } from 'rxjs';
34

45
import { SortOption } from './server-and-agent-list-container.component';
56

7+
const enum ListStyle {
8+
LEFT_PADDING = 17,
9+
ICON_WIDTH = 17
10+
}
11+
612
@Component({
713
selector: 'pp-server-and-agent-list',
814
templateUrl: './server-and-agent-list.component.html',
@@ -57,7 +63,9 @@ export class ServerAndAgentListComponent implements OnInit {
5763
private _serverKeyList: string[];
5864
isCollapsed: { [key: string]: boolean };
5965

60-
constructor() {}
66+
constructor(
67+
private renderer: Renderer2
68+
) {}
6169
ngOnInit() {}
6270
getIconPath(iconState: number) {
6371
let iconName = '';
@@ -106,4 +114,33 @@ export class ServerAndAgentListComponent implements OnInit {
106114
return agentName ? agentName : `N/A (${agentId})`;
107115
}
108116
}
117+
118+
getLeftPosition(labelWrapperElement: HTMLElement, labelElement: HTMLElement): string {
119+
const labelWrapperWidth = labelWrapperElement.offsetWidth;
120+
const labelWidth = labelElement.offsetWidth;
121+
122+
return labelWidth + ListStyle.ICON_WIDTH >= labelWrapperWidth
123+
? `${ListStyle.LEFT_PADDING + labelWrapperWidth - 25}px`
124+
: `${ListStyle.LEFT_PADDING + ListStyle.ICON_WIDTH + labelElement.offsetWidth - 4}px`;
125+
}
126+
127+
onClickCopy(event: Event, agentLabel: string): void {
128+
event.stopPropagation();
129+
from(navigator.clipboard.writeText(agentLabel)).subscribe(() => {
130+
const copyBtnElement = event.target as HTMLElement;
131+
const wrapperElement = copyBtnElement.parentElement;
132+
const pElement = this.renderer.createElement('p');
133+
const copiedTextElement = this.renderer.createText('Copied');
134+
135+
this.renderer.setStyle(copyBtnElement, 'display', 'none');
136+
this.renderer.setStyle(pElement, 'font-size', '10px');
137+
this.renderer.appendChild(pElement, copiedTextElement);
138+
this.renderer.appendChild(wrapperElement, pElement);
139+
140+
setTimeout(() => {
141+
this.renderer.removeChild(wrapperElement, pElement);
142+
this.renderer.setStyle(copyBtnElement, 'display', 'inline-block');
143+
}, 1500);
144+
});
145+
}
109146
}

web/src/main/angular/src/app/core/components/server-list/server-list.component.css

+26
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,36 @@
6969
display: flex;
7070
align-items: center;
7171
flex: 1;
72+
position: relative;
7273
}
7374

7475
.btn-wrapper {
7576
display: flex;
7677
align-items: center;
7778
gap: 2px;
7879
}
80+
.depth-1 li:hover .copy-button {
81+
display: inline-block;
82+
}
83+
.copy-button {
84+
display: none;
85+
position: absolute;
86+
/* right: 25px; */
87+
/* right: 2px; */
88+
/* top: 0px; */
89+
bottom: -13px;
90+
z-index: 100;
91+
}
92+
93+
.copy-button:focus {
94+
outline: 0;
95+
}
96+
97+
.fa-copy {
98+
font-size: 15px;
99+
color: var(--icon-default);
100+
}
101+
102+
.fa-copy:hover {
103+
color: var(--icon-hover);
104+
}

web/src/main/angular/src/app/core/components/server-list/server-list.component.html

+10-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,22 @@
33
<div class="server-name" [attr.title]="server.groupName">{{server.groupName}}</div>
44
<ul class="depth-1">
55
<li *ngFor="let agent of server.instancesList; let innerIndex = index">
6-
<div class="label-radio-wrapper" [style.max-width]="getLabelMaxWidth(btnWrapper)">
6+
<div class="label-radio-wrapper" [style.max-width]="getLabelMaxWidth(btnWrapper)" #labelWrapper>
77
<input
88
type="radio"
99
class="radio-input"
1010
id="step3-category-{{outerIndex}}-{{innerIndex}}"
1111
name="main-server-list-selected-agent" (click)="onSelectAgent(agent)" [checked]="isSelectedAgent(agent)"
1212
>
13-
<label for="step3-category-{{outerIndex}}-{{innerIndex}}" class="radio-label" title="{{getAgentName(agent)}}">{{getAgentLabel(agent)}}</label>
13+
<label
14+
for="step3-category-{{outerIndex}}-{{innerIndex}}"
15+
class="radio-label"
16+
[attr.title]="agent.agentName || agent.agentId"
17+
#label
18+
>{{getAgentLabel(agent)}}</label>
19+
<button type="button" class="copy-button" [style.left]="getLeftPosition(labelWrapper, label)" (click)="onClickCopy($event, getAgentLabel(agent))">
20+
<span class="far fa-copy" title="Copy to Clipboard"></span>
21+
</button>
1422
</div>
1523
<div class="btn-wrapper" #btnWrapper>
1624
<span class="icon-alert" *ngIf="hasError(agent)"><img [src]="getAlertImgPath()" alt=""></span>

web/src/main/angular/src/app/core/components/server-list/server-list.component.ts

+37-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
import { Component, OnInit, Input, Output, EventEmitter} from '@angular/core';
1+
import { Component, OnInit, Input, Output, EventEmitter, Renderer2} from '@angular/core';
2+
import { from } from 'rxjs';
23

4+
const enum ListStyle {
5+
RADIO_BTN_WIDTH = 23
6+
}
37
@Component({
48
selector: 'pp-server-list',
59
templateUrl: './server-list.component.html',
@@ -14,7 +18,9 @@ export class ServerListComponent implements OnInit {
1418
@Output() outSelectAgent = new EventEmitter<string>();
1519
@Output() outOpenInspector = new EventEmitter<string>();
1620

17-
constructor() {}
21+
constructor(
22+
private renderer: Renderer2
23+
) {}
1824
ngOnInit() {}
1925
getAgentLabel({agentId, agentName}: IAgentDataV2): string {
2026
return `${agentId} (${this.getAgentName({agentName} as IAgentDataV2)})`;
@@ -51,4 +57,33 @@ export class ServerListComponent implements OnInit {
5157
getLabelMaxWidth(btnWrapper: HTMLElement): string {
5258
return `calc(100% - ${btnWrapper.offsetWidth}px)`;
5359
}
60+
61+
getLeftPosition(labelWrapperElement: HTMLElement, labelElement: HTMLElement): string {
62+
const labelWrapperWidth = labelWrapperElement.offsetWidth;
63+
const labelWidth = labelElement.offsetWidth;
64+
65+
return labelWidth + ListStyle.RADIO_BTN_WIDTH >= labelWrapperWidth
66+
? `${labelWrapperWidth - 25}px`
67+
: `${ListStyle.RADIO_BTN_WIDTH + labelWidth - 4}px`;
68+
}
69+
70+
onClickCopy(event: Event, agentLabel: string): void {
71+
event.stopPropagation();
72+
from(navigator.clipboard.writeText(agentLabel)).subscribe(() => {
73+
const copyBtnElement = event.target as HTMLElement;
74+
const wrapperElement = copyBtnElement.parentElement;
75+
const pElement = this.renderer.createElement('p');
76+
const copiedTextElement = this.renderer.createText('Copied');
77+
78+
this.renderer.setStyle(copyBtnElement, 'display', 'none');
79+
this.renderer.setStyle(pElement, 'font-size', '10px');
80+
this.renderer.appendChild(pElement, copiedTextElement);
81+
this.renderer.appendChild(wrapperElement, pElement);
82+
83+
setTimeout(() => {
84+
this.renderer.removeChild(wrapperElement, pElement);
85+
this.renderer.setStyle(copyBtnElement, 'display', 'inline-block');
86+
}, 1500);
87+
});
88+
}
5489
}

web/src/main/angular/src/app/shared/services/browser-support-check.service.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ export class BrowserSupportCheckService {
1616
version: 66
1717
}, {
1818
name: 'firefox',
19-
version: 60
19+
version: 63
2020
}, {
2121
name: 'safari',
22-
version: 11
22+
version: 13
2323
}, {
2424
name: 'edge',
25-
version: 42
25+
version: 79
2626
}, {
2727
name: 'whale',
2828
version: 1

0 commit comments

Comments
 (0)