Skip to content

Commit 95ce64e

Browse files
committed
feat: add qrcode generator
1 parent bf6a082 commit 95ce64e

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
2+
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
3+
import { HlmButtonImports } from '@spartan-ng/helm/button';
4+
import { HlmInputImports } from '@spartan-ng/helm/input';
5+
import { NgIconComponent, provideIcons } from '@ng-icons/core';
6+
import { lucideQrCode } from '@ng-icons/lucide';
7+
import { QrCodePipe } from '@ngx-transforms';
8+
import {AsyncPipe} from '@angular/common';
9+
10+
@Component({
11+
selector: 'app-qrcode-example',
12+
standalone: true,
13+
imports: [ReactiveFormsModule, HlmInputImports, HlmButtonImports, NgIconComponent, QrCodePipe, AsyncPipe],
14+
providers: [provideIcons({ lucideQrCode }), QrCodePipe],
15+
template: `
16+
<div class="w-full flex flex-col lg:flex-row items-center justify-center gap-8 lg:gap-16 p-4">
17+
<div class="flex-1 w-full max-w-md flex flex-col items-center lg:items-start">
18+
<div class="text-center lg:text-left">
19+
<h3 class="text-2xl font-semibold">QR Code Generator</h3>
20+
<p class="text-sm text-muted-foreground mt-1">Enter a URL to generate a QR code instantly.</p>
21+
</div>
22+
23+
<div class="w-full flex flex-col sm:flex-row items-center gap-2 mt-6">
24+
<input
25+
hlmInput
26+
[formControl]="url"
27+
placeholder="e.g., https://example.com"
28+
class="w-full"
29+
/>
30+
<button hlmBtn (click)="generateQrCode()" [disabled]="isGenerating()" class="w-full sm:w-auto flex-shrink-0">
31+
{{ isGenerating() ? 'Generating...' : 'Generate' }}
32+
</button>
33+
</div>
34+
35+
@if (urlToPipe()) {
36+
<button hlmBtn variant="outline" (click)="downloadQrCode()" class="mt-4 w-full sm:w-auto qr-fade-in">
37+
Download QR Code
38+
</button>
39+
}
40+
</div>
41+
42+
<div class="flex-shrink-0 w-56 h-56">
43+
<div
44+
class="w-full h-full flex items-center justify-center rounded-lg border-2 border-dashed bg-muted/50 transition-all duration-300">
45+
@if (isGenerating()) {
46+
<div class="spinner"></div>
47+
} @else if (urlToPipe(); as url) {
48+
@if (url | qrCode | async; as qrCodeUrl) {
49+
<img [src]="qrCodeUrl" alt="Generated QR Code"
50+
class="w-full h-full object-contain rounded-md qr-fade-in"/>
51+
}
52+
} @else {
53+
<div class="flex flex-col items-center gap-2 text-muted-foreground">
54+
<ng-icon name="lucideQrCode" size="48"/>
55+
<span class="text-sm">QR code will appear here</span>
56+
</div>
57+
}
58+
</div>
59+
</div>
60+
</div>
61+
`,
62+
styles: [`
63+
.qr-animation-enter {
64+
opacity: 0;
65+
transform: scale(0.9);
66+
}
67+
.qr-animation-enter-active {
68+
transition: all 0.5s ease-out;
69+
}
70+
71+
.spinner {
72+
width: 40px;
73+
height: 40px;
74+
border: 4px solid hsl(var(--border));
75+
border-top: 4px solid hsl(var(--primary));
76+
border-radius: 50%;
77+
animation: spin 1s linear infinite;
78+
}
79+
80+
@keyframes spin {
81+
0% { transform: rotate(0deg); }
82+
100% { transform: rotate(360deg); }
83+
}
84+
`],
85+
changeDetection: ChangeDetectionStrategy.OnPush,
86+
})
87+
export class QrcodeGenerator {
88+
protected readonly url = new FormControl('https://angular.dev/', {
89+
validators: [Validators.required, Validators.pattern('^(http|https)://[^ "]+$')],
90+
nonNullable: true,
91+
});
92+
protected readonly urlToPipe = signal<string | null>(null);
93+
protected readonly isGenerating = signal(false);
94+
95+
private readonly qrCodePipe = inject(QrCodePipe);
96+
97+
async generateQrCode(): Promise<void> {
98+
if (this.url.invalid) return;
99+
100+
this.isGenerating.set(true);
101+
this.urlToPipe.set(null); // Reset previous QR code
102+
103+
// Simulate network latency for a better UX
104+
await new Promise(resolve => setTimeout(resolve, 500));
105+
106+
this.urlToPipe.set(this.url.value);
107+
this.isGenerating.set(false);
108+
}
109+
110+
async downloadQrCode(): Promise<void> {
111+
const url = this.urlToPipe();
112+
if (!url) return;
113+
114+
// The pipe's transform method returns a Promise<string>
115+
const dataUrl = await this.qrCodePipe.transform(url);
116+
117+
const a = document.createElement('a');
118+
a.href = dataUrl;
119+
a.download = 'qrcode.png';
120+
document.body.appendChild(a);
121+
a.click();
122+
document.body.removeChild(a);
123+
}
124+
}

0 commit comments

Comments
 (0)