982a7a3b73
- vcard.ts: shared vCard-Generator (downloadVCard, downloadVCardAll, buildVCardString) - QrModal.svelte + QrButton.svelte: wiederverwendbare QR-Komponenten mit PNG-Download - AdressbuchBlock: Org-Gruppenansicht (Toggle), QR + vCard pro Kontakt, Buchstaben-Trennzeilen, Such-Highlight, "Alle exportieren", Print-Styles - ContactCard: vCard-Download-Button - PostActions: QR-Button für Seiten-URL - qrcode npm-Paket hinzugefügt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
48 lines
1.5 KiB
TypeScript
48 lines
1.5 KiB
TypeScript
import type { ContactEntry } from "./block-types";
|
|
|
|
function vcardEscape(s: string): string {
|
|
return s.replace(/\\/g, "\\\\").replace(/,/g, "\\,").replace(/;/g, "\\;").replace(/\n/g, "\\n");
|
|
}
|
|
|
|
function buildVCard(c: ContactEntry): string {
|
|
const name = vcardEscape(c.name ?? "");
|
|
const orgs = (c.organisations ?? [])
|
|
.filter((o): o is { name?: string } => typeof o === "object" && !!o.name)
|
|
.map((o) => vcardEscape(o.name ?? ""))
|
|
.join(", ");
|
|
return [
|
|
"BEGIN:VCARD",
|
|
"VERSION:3.0",
|
|
`FN:${name}`,
|
|
"N:;;;;",
|
|
orgs ? `ORG:${orgs}` : null,
|
|
c.role ? `TITLE:${vcardEscape(c.role)}` : null,
|
|
c.phone ? `TEL;TYPE=CELL:${c.phone.replace(/\s/g, "")}` : null,
|
|
c.email ? `EMAIL:${vcardEscape(c.email)}` : null,
|
|
c.note ? `ADR:;;${vcardEscape(c.note)};;;;` : null,
|
|
"END:VCARD",
|
|
].filter(Boolean).join("\r\n");
|
|
}
|
|
|
|
export function buildVCardString(c: ContactEntry): string {
|
|
return buildVCard(c);
|
|
}
|
|
|
|
function triggerDownload(content: string, filename: string): void {
|
|
const blob = new Blob([content], { type: "text/vcard;charset=utf-8" });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = filename;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
export function downloadVCard(c: ContactEntry): void {
|
|
triggerDownload(buildVCard(c), `${(c.name ?? "kontakt").toLowerCase().replace(/\s+/g, "-")}.vcf`);
|
|
}
|
|
|
|
export function downloadVCardAll(contacts: ContactEntry[], filename = "kontakte.vcf"): void {
|
|
triggerDownload(contacts.map(buildVCard).join("\r\n"), filename);
|
|
}
|