Skip to content

Commit ca5e41e

Browse files
authored
feat: add web demo (#48)
* feat: add web demo
1 parent 6bc47b1 commit ca5e41e

12 files changed

Lines changed: 1026 additions & 0 deletions

File tree

pnpm-lock.yaml

Lines changed: 289 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
packages:
2+
- "web"

web/.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
node_modules
2+
dist
3+
.data
4+
.nitro
5+
.cache
6+
.output
7+
.env
8+
.env.local
9+
.vercel

web/app/app.ts

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import {
2+
barcode,
3+
qrcode,
4+
datamatrix,
5+
pdf417,
6+
aztec,
7+
wifi,
8+
email,
9+
phone,
10+
geo,
11+
vcard,
12+
event,
13+
} from "etiket";
14+
15+
export function setupApp() {
16+
const root = document.getElementById("app")!;
17+
18+
// Tabs
19+
const tabs = root.querySelectorAll<HTMLButtonElement>(".tab");
20+
const panels = root.querySelectorAll<HTMLElement>(".panel");
21+
for (const tab of tabs) {
22+
tab.addEventListener("click", () => {
23+
for (const t of tabs) t.classList.remove("active");
24+
for (const p of panels) p.classList.remove("active");
25+
tab.classList.add("active");
26+
root.querySelector<HTMLElement>(`[data-panel="${tab.dataset.tab}"]`)!.classList.add("active");
27+
});
28+
}
29+
30+
// Utils
31+
const $ = <T extends HTMLElement>(id: string) => document.getElementById(id) as T;
32+
const val = (id: string) => $<HTMLInputElement>(id).value;
33+
const numVal = (id: string) => Number(val(id));
34+
35+
function renderSafe(target: HTMLElement, fn: () => string) {
36+
try {
37+
target.innerHTML = fn();
38+
} catch (err) {
39+
target.innerHTML = `<div class="error">${(err as Error).message}</div>`;
40+
}
41+
}
42+
43+
function copyAndDownload(prefix: string, getFn: () => string) {
44+
$(`${prefix}-copy`).addEventListener("click", () => {
45+
try {
46+
navigator.clipboard.writeText(getFn());
47+
} catch {}
48+
});
49+
$(`${prefix}-download`).addEventListener("click", () => {
50+
try {
51+
const blob = new Blob([getFn()], { type: "image/svg+xml" });
52+
const a = document.createElement("a");
53+
a.href = URL.createObjectURL(blob);
54+
a.download = `${prefix}.svg`;
55+
a.click();
56+
URL.revokeObjectURL(a.href);
57+
} catch {}
58+
});
59+
}
60+
61+
// --- Barcode ---
62+
function renderBarcode() {
63+
renderSafe($("bc-output"), () =>
64+
barcode(val("bc-data"), {
65+
type: val("bc-type") as any,
66+
height: numVal("bc-height"),
67+
barWidth: numVal("bc-barwidth"),
68+
color: val("bc-color"),
69+
showText: val("bc-showtext") === "true",
70+
}),
71+
);
72+
}
73+
74+
for (const id of ["bc-data", "bc-type", "bc-height", "bc-barwidth", "bc-color", "bc-showtext"]) {
75+
$(id).addEventListener("input", renderBarcode);
76+
}
77+
copyAndDownload("bc", () =>
78+
barcode(val("bc-data"), {
79+
type: val("bc-type") as any,
80+
height: numVal("bc-height"),
81+
barWidth: numVal("bc-barwidth"),
82+
color: val("bc-color"),
83+
showText: val("bc-showtext") === "true",
84+
}),
85+
);
86+
renderBarcode();
87+
88+
// --- QR Code ---
89+
function renderQR() {
90+
renderSafe($("qr-output"), () =>
91+
qrcode(val("qr-data"), {
92+
size: numVal("qr-size"),
93+
ecLevel: val("qr-ec") as any,
94+
dotType: val("qr-dot") as any,
95+
color: val("qr-color"),
96+
background: val("qr-bg"),
97+
margin: numVal("qr-margin"),
98+
}),
99+
);
100+
}
101+
102+
for (const id of ["qr-data", "qr-size", "qr-ec", "qr-dot", "qr-color", "qr-bg", "qr-margin"]) {
103+
$(id).addEventListener("input", renderQR);
104+
}
105+
copyAndDownload("qr", () =>
106+
qrcode(val("qr-data"), {
107+
size: numVal("qr-size"),
108+
ecLevel: val("qr-ec") as any,
109+
dotType: val("qr-dot") as any,
110+
color: val("qr-color"),
111+
background: val("qr-bg"),
112+
margin: numVal("qr-margin"),
113+
}),
114+
);
115+
renderQR();
116+
117+
// --- 2D Codes ---
118+
const matrix2DGenerators: Record<string, (text: string, opts: any) => string> = {
119+
datamatrix,
120+
pdf417,
121+
aztec,
122+
};
123+
124+
function render2D() {
125+
const format = val("m2d-format");
126+
const fn = matrix2DGenerators[format];
127+
renderSafe($("m2d-output"), () => fn(val("m2d-data"), { color: val("m2d-color") }));
128+
}
129+
130+
for (const id of ["m2d-data", "m2d-format", "m2d-color"]) {
131+
$(id).addEventListener("input", render2D);
132+
}
133+
copyAndDownload("m2d", () => {
134+
const fn = matrix2DGenerators[val("m2d-format")];
135+
return fn(val("m2d-data"), { color: val("m2d-color") });
136+
});
137+
render2D();
138+
139+
// --- Helpers ---
140+
function renderHelpers() {
141+
renderSafe($("h-wifi-out"), () => wifi(val("h-wifi-ssid"), val("h-wifi-pass")));
142+
renderSafe($("h-email-out"), () => email(val("h-email")));
143+
renderSafe($("h-phone-out"), () => phone(val("h-phone")));
144+
renderSafe($("h-geo-out"), () => geo(numVal("h-geo-lat"), numVal("h-geo-lng")));
145+
renderSafe($("h-vcard-out"), () =>
146+
vcard({
147+
firstName: val("h-vc-fn"),
148+
lastName: val("h-vc-ln"),
149+
phone: val("h-vc-phone"),
150+
email: val("h-vc-email"),
151+
}),
152+
);
153+
renderSafe($("h-event-out"), () =>
154+
event({
155+
title: val("h-ev-title"),
156+
start: val("h-ev-start"),
157+
end: val("h-ev-end"),
158+
}),
159+
);
160+
}
161+
162+
const helperInputIds = [
163+
"h-wifi-ssid",
164+
"h-wifi-pass",
165+
"h-email",
166+
"h-phone",
167+
"h-geo-lat",
168+
"h-geo-lng",
169+
"h-vc-fn",
170+
"h-vc-ln",
171+
"h-vc-phone",
172+
"h-vc-email",
173+
"h-ev-title",
174+
"h-ev-start",
175+
"h-ev-end",
176+
];
177+
for (const id of helperInputIds) {
178+
$(id).addEventListener("input", renderHelpers);
179+
}
180+
renderHelpers();
181+
}

0 commit comments

Comments
 (0)