Skip to content

Commit db98bf0

Browse files
demo: add clipboard button functionality and relevant styling (#4845)
Add a "Copy to clipboard" button on the code component to enable one-click copying of snippets across the demo site. Fixes #4826
1 parent 1b90da5 commit db98bf0

File tree

3 files changed

+37
-1
lines changed

3 files changed

+37
-1
lines changed

demo/src/app/shared/code.component.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,55 @@ import {
55
ElementRef,
66
inject,
77
input,
8+
signal,
89
viewChild,
910
} from '@angular/core';
1011

1112
import { ISnippet } from '../services/snippet';
1213
import { CodeHighlightService } from '../services/code-highlight.service';
14+
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
1315

1416
@Component({
1517
selector: 'ngbd-code',
1618
changeDetection: ChangeDetectionStrategy.OnPush,
19+
imports: [NgbTooltip],
1720
template: `
18-
<pre class="language-{{ snippet().lang }}"><code #code class="language-{{ snippet().lang }}"></code></pre>
21+
<div class="position-relative">
22+
<button
23+
(click)="copyToClipboard()"
24+
[ngbTooltip]="showAsCopied() ? 'Copied!' : 'Copy to clipboard'"
25+
class="btn btn-clipboard position-absolute top-0 end-0 mt-2 me-3 z-2"
26+
aria-label="Copy code to clipboard"
27+
>
28+
<i class="bi bi-{{ showAsCopied() ? 'clipboard-check' : 'clipboard' }}"></i>
29+
</button>
30+
<pre class="language-{{ snippet().lang }}"><code #code class="language-{{ snippet().lang }}"></code></pre>
31+
</div>
1932
`,
2033
})
2134
export class CodeComponent {
35+
private _copyBtnTooltip = viewChild.required(NgbTooltip);
2236
private _codeEl = viewChild.required('code', { read: ElementRef });
2337
snippet = input.required<ISnippet>();
2438

39+
showAsCopied = signal(false);
40+
2541
constructor() {
2642
const highlightService = inject(CodeHighlightService);
2743
afterNextRender(() => {
2844
this._codeEl().nativeElement.innerHTML = highlightService.highlight(this.snippet().code, this.snippet().lang);
2945
});
3046
}
47+
48+
copyToClipboard() {
49+
navigator.clipboard.writeText(this.snippet().code).then(() => {
50+
this.showAsCopied.set(true);
51+
52+
// 'Restart' tooltip to show 'Copied!' message
53+
this._copyBtnTooltip().close();
54+
setTimeout(() => this._copyBtnTooltip().open(), 0);
55+
56+
setTimeout(() => this.showAsCopied.set(false), 2000);
57+
});
58+
}
3159
}

demo/src/style/prism-dark.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,7 @@
130130
[data-bs-theme='dark'] .token.entity {
131131
cursor: help;
132132
}
133+
[data-bs-theme='dark'] .btn-clipboard,
134+
[data-bs-theme='dark'] .btn-clipboard:hover {
135+
background: #161b22;
136+
}

demo/src/style/prism-light.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,7 @@ pre[class*='language-'] > code[class*='language-'] {
129129
.token.entity {
130130
cursor: help;
131131
}
132+
.btn-clipboard,
133+
.btn-clipboard:hover {
134+
background: #f6f8fa;
135+
}

0 commit comments

Comments
 (0)