Skip to content

Commit 038944b

Browse files
committed
feat: add download functionality and improve architecture
1 parent 0a34c9d commit 038944b

File tree

7 files changed

+187
-20
lines changed

7 files changed

+187
-20
lines changed
Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import JsBarcode from "jsbarcode";
2-
import { QRCodeSVG } from "qrcode.react";
3-
import { ReactElement, useEffect, useRef } from "react";
1+
import { ReactElement, useRef } from "react";
42
import { BarcodeGeneratorContainerProps } from "../typings/BarcodeGeneratorProps";
3+
import { useDownload } from "./hooks/useDownload";
4+
import { CodeRenderer } from "./components/CodeRenderer";
55

66
import "./ui/BarcodeGenerator.scss";
77

@@ -13,41 +13,45 @@ export default function BarcodeGenerator({
1313
codeMargin,
1414
displayValue,
1515
qrSize,
16-
tabIndex
16+
tabIndex,
17+
allowDownload
1718
}: BarcodeGeneratorContainerProps): ReactElement {
1819
const svgRef = useRef<SVGSVGElement>(null);
20+
const qrContainerRef = useRef<HTMLDivElement>(null);
1921

2022
const value = codeValue?.status === "available" ? codeValue.value : "";
2123
const width = codeWidth ?? 128;
2224
const height = codeHeight ?? 128;
2325
const format = codeFormat ?? "CODE128";
2426
const margin = codeMargin ?? 2;
2527
const showValue = displayValue ?? false;
28+
const download = allowDownload ?? false;
2629
const size = qrSize ?? 128;
2730

28-
useEffect(() => {
29-
if (format !== "QRCode" && svgRef.current && value) {
30-
try {
31-
JsBarcode(svgRef.current, value, {
32-
format,
33-
width,
34-
height,
35-
margin,
36-
displayValue: showValue
37-
});
38-
} catch (error) {
39-
console.error("Error generating barcode:", error);
40-
}
41-
}
42-
}, [value, width, height, format, margin, showValue]);
31+
const { downloadSVG } = useDownload({ format, svgRef, qrContainerRef });
4332

4433
if (!value) {
4534
return <span>No barcode value provided</span>;
4635
}
4736

4837
return (
4938
<div className="barcode-generator" tabIndex={tabIndex}>
50-
{format === "QRCode" ? <QRCodeSVG value={value} size={size} /> : <svg ref={svgRef} />}
39+
<CodeRenderer
40+
format={format}
41+
value={value}
42+
size={size}
43+
width={width}
44+
height={height}
45+
margin={margin}
46+
displayValue={showValue}
47+
svgRef={svgRef}
48+
qrContainerRef={qrContainerRef}
49+
/>
50+
{download && (
51+
<button type="button" onClick={downloadSVG} className="btn btn-default">
52+
Download {format === "QRCode" ? "QR Code" : "Barcode"}
53+
</button>
54+
)}
5155
</div>
5256
);
5357
}

packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
<enumerationValue key="Custom">Custom Format</enumerationValue>
2525
</enumerationValues>
2626
</property>
27+
<property key="allowDownload" type="boolean" defaultValue="false">
28+
<caption>Allow download</caption>
29+
<description>Adds a download button</description>
30+
</property>
2731
</propertyGroup>
2832
<propertyGroup caption="Display">
2933
<property key="displayValue" type="boolean" defaultValue="false">
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import JsBarcode from "jsbarcode";
2+
import { forwardRef, useEffect } from "react";
3+
4+
interface BarcodeRendererProps {
5+
value: string;
6+
width: number;
7+
height: number;
8+
format: string;
9+
margin: number;
10+
displayValue: boolean;
11+
}
12+
13+
export const BarcodeRenderer = forwardRef<SVGSVGElement, BarcodeRendererProps>(
14+
({ value, width, height, format, margin, displayValue }, ref) => {
15+
useEffect(() => {
16+
if (ref && typeof ref !== "function" && ref.current && value) {
17+
try {
18+
JsBarcode(ref.current, value, {
19+
format,
20+
width,
21+
height,
22+
margin,
23+
displayValue
24+
});
25+
} catch (error) {
26+
console.error("Error generating barcode:", error);
27+
}
28+
}
29+
}, [value, width, height, format, margin, displayValue, ref]);
30+
31+
return <svg ref={ref} />;
32+
}
33+
);
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { ReactElement } from "react";
2+
import { QRCodeRenderer } from "./QRCodeRenderer";
3+
import { BarcodeRenderer } from "./BarcodeRenderer";
4+
5+
interface CodeRendererProps {
6+
format: string;
7+
value: string;
8+
// QR Code props
9+
size: number;
10+
// Barcode props
11+
width: number;
12+
height: number;
13+
margin: number;
14+
displayValue: boolean;
15+
// Refs for download functionality
16+
svgRef: React.RefObject<SVGSVGElement>;
17+
qrContainerRef: React.RefObject<HTMLDivElement>;
18+
}
19+
20+
export function CodeRenderer({
21+
format,
22+
value,
23+
size,
24+
width,
25+
height,
26+
margin,
27+
displayValue,
28+
svgRef,
29+
qrContainerRef
30+
}: CodeRendererProps): ReactElement {
31+
if (format === "QRCode") {
32+
return <QRCodeRenderer ref={qrContainerRef} value={value} size={size} />;
33+
}
34+
35+
return (
36+
<BarcodeRenderer
37+
ref={svgRef}
38+
value={value}
39+
width={width}
40+
height={height}
41+
format={format}
42+
margin={margin}
43+
displayValue={displayValue}
44+
/>
45+
);
46+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { QRCodeSVG } from "qrcode.react";
2+
import { forwardRef } from "react";
3+
4+
const QRCode = QRCodeSVG as React.ComponentType<{ value: string; size: number }>;
5+
6+
interface QRCodeRendererProps {
7+
value: string;
8+
size: number;
9+
}
10+
11+
export const QRCodeRenderer = forwardRef<HTMLDivElement, QRCodeRendererProps>(({ value, size }, ref) => {
12+
return (
13+
<div ref={ref}>
14+
<QRCode value={value} size={size} />
15+
</div>
16+
);
17+
});
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { useCallback } from "react";
2+
3+
interface UseDownloadParams {
4+
format: string;
5+
svgRef: React.RefObject<SVGSVGElement>;
6+
qrContainerRef: React.RefObject<HTMLDivElement>;
7+
}
8+
interface UseDownloadReturn {
9+
downloadSVG: () => void;
10+
}
11+
12+
export function useDownload({ format, svgRef, qrContainerRef }: UseDownloadParams): UseDownloadReturn {
13+
const downloadSVG = useCallback(() => {
14+
let svgElement: SVGSVGElement | null = null;
15+
let filename = "";
16+
17+
if (format === "QRCode") {
18+
// Find the SVG element inside the QR container
19+
svgElement = qrContainerRef.current?.querySelector("svg") || null;
20+
filename = "qrcode.svg";
21+
} else {
22+
svgElement = svgRef.current;
23+
filename = "barcode.svg";
24+
}
25+
26+
if (!svgElement) {
27+
console.error("SVG element not found for download");
28+
return;
29+
}
30+
31+
try {
32+
// Clone the SVG to avoid modifying the original
33+
const clonedSvg = svgElement.cloneNode(true) as SVGSVGElement;
34+
35+
// Ensure proper SVG namespace and attributes
36+
clonedSvg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
37+
38+
// Serialize the SVG
39+
const serializer = new XMLSerializer();
40+
const svgString = serializer.serializeToString(clonedSvg);
41+
42+
// Create download link
43+
const blob = new Blob([svgString], { type: "image/svg+xml;charset=utf-8" });
44+
const url = URL.createObjectURL(blob);
45+
46+
const link = document.createElement("a");
47+
link.href = url;
48+
link.download = filename;
49+
document.body.appendChild(link);
50+
link.click();
51+
document.body.removeChild(link);
52+
53+
// Clean up the URL object
54+
URL.revokeObjectURL(url);
55+
} catch (error) {
56+
console.error("Error downloading SVG:", error);
57+
}
58+
}, [format, svgRef, qrContainerRef]);
59+
60+
return { downloadSVG };
61+
}

packages/pluggableWidgets/barcode-generator-web/typings/BarcodeGeneratorProps.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export interface BarcodeGeneratorContainerProps {
1717
tabIndex?: number;
1818
codeValue: EditableValue<string>;
1919
codeFormat: CodeFormatEnum;
20+
allowDownload: boolean;
2021
displayValue: boolean;
2122
codeWidth: number;
2223
codeHeight: number;
@@ -38,6 +39,7 @@ export interface BarcodeGeneratorPreviewProps {
3839
translate: (text: string) => string;
3940
codeValue: string;
4041
codeFormat: CodeFormatEnum;
42+
allowDownload: boolean;
4143
displayValue: boolean;
4244
codeWidth: number | null;
4345
codeHeight: number | null;

0 commit comments

Comments
 (0)