fix: make task IDs selectable and copyable with feedback
This commit is contained in:
1 parent
9d6833f092
commit
60ac066859
8 files changed
+179
-7
No files matched your search
@@ -0,0 +1,66 @@
|
||||
import { Clipboard } from '@angular/cdk/clipboard';
|
||||
import { Component, DestroyRef, Input, inject, signal } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-task-id-copy',
|
||||
standalone: true,
|
||||
template: `
|
||||
<span class="copy-target" role="button" tabindex="0"
|
||||
[attr.aria-label]="'复制任务 ID:' + taskId" title="点击复制任务 ID,也可选中文字复制"
|
||||
(click)="copy($event)" (keydown.enter)="copy($event)" (keydown.space)="copy($event)">
|
||||
<span class="value">ID: {{ taskId }}</span>
|
||||
<span class="material-symbols-outlined" aria-hidden="true">content_copy</span>
|
||||
</span>
|
||||
<span class="feedback" role="status" aria-live="polite">{{ feedback() }}</span>
|
||||
`,
|
||||
styles: [`
|
||||
:host { display: block; min-width: 0; font-size: 11px; color: inherit; user-select: text; }
|
||||
.copy-target { display: inline-flex; align-items: center; gap: 5px; max-width: 100%;
|
||||
padding: 3px 0; cursor: pointer; user-select: text; border-radius: 3px; }
|
||||
.value { min-width: 0; overflow-wrap: anywhere; user-select: text; }
|
||||
.material-symbols-outlined { flex-shrink: 0; font-size: 14px; user-select: none; }
|
||||
.copy-target:hover { color: #2563eb; }
|
||||
.copy-target:focus-visible { outline: 2px solid #60a5fa; outline-offset: 2px; }
|
||||
.feedback { display: block; line-height: 1.6; user-select: text; }
|
||||
.feedback:empty { display: none; }
|
||||
`]
|
||||
})
|
||||
export class TaskIdCopyComponent {
|
||||
@Input({ required: true }) taskId = '';
|
||||
readonly feedback = signal('');
|
||||
private readonly clipboard = inject(Clipboard);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private timer: ReturnType<typeof setTimeout> | undefined;
|
||||
|
||||
constructor() {
|
||||
this.destroyRef.onDestroy(() => clearTimeout(this.timer));
|
||||
}
|
||||
|
||||
async copy(event: Event): Promise<void> {
|
||||
event.stopPropagation();
|
||||
// A drag selection must remain available for the browser's normal copy action.
|
||||
const selection = window.getSelection();
|
||||
if (event.type === 'click' && selection && !selection.isCollapsed
|
||||
&& (event.currentTarget as HTMLElement).contains(selection.anchorNode)) return;
|
||||
event.preventDefault();
|
||||
let copied = false;
|
||||
try {
|
||||
if (navigator.clipboard?.writeText) {
|
||||
await navigator.clipboard.writeText(this.taskId);
|
||||
copied = true;
|
||||
} else {
|
||||
copied = this.clipboard.copy(this.taskId);
|
||||
}
|
||||
} catch {
|
||||
try {
|
||||
copied = this.clipboard.copy(this.taskId);
|
||||
} catch {
|
||||
// Keep manual selection available when the browser blocks both clipboard APIs.
|
||||
}
|
||||
}
|
||||
if (this.destroyRef.destroyed) return;
|
||||
this.feedback.set(copied ? '任务 ID 已复制' : '复制失败,请选中任务 ID 手动复制');
|
||||
clearTimeout(this.timer);
|
||||
this.timer = setTimeout(() => this.feedback.set(''), 3000);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user