Component: ExportPanel.jsx
Relevant source files
The following files were used as context for generating this wiki page:
The ExportPanel.jsx component provides the user interface for downloading the generated QR code. It features buttons for different file formats (PNG, JPG, SVG) and handles the logic for preparing the QR code image or vector data for export. This includes adding padding, handling background transparency, and triggering the file download. It is a key part of the application's output functionality, interacting closely with the main App.jsx state and the QrPreview.jsx component's rendered output.
Sources: src/components/ExportPanel.jsx, src/App.jsx:1289-1297
Component Architecture
The ExportPanel is a functional React component that receives its state and references to the rendered QR code elements from its parent, App.jsx. It does not manage its own data but contains the logic to process and export the QR code based on user interaction.
Sources: src/components/ExportPanel.jsx:13, src/App.jsx:1289-1297
Component Interaction
The following diagram illustrates how App.jsx orchestrates the flow of data and references between ExportPanel and QrPreview.
This diagram shows that App.jsx holds the main state and passes props down to both QrPreview and ExportPanel. QrPreview renders the QR code into a div with a ref (canvasRef or svgRef), which is also passed to ExportPanel so it can access the rendered element for exporting.
Sources: src/App.jsx:1289-1317, src/components/ExportPanel.jsx:13-21, src/components/QrPreview.jsx:10-14
Props
The component's behavior is controlled by several props passed from App.jsx.
| Prop | Type | Description | Source File |
|---|---|---|---|
format | string | The current QR code render format ('canvas' or 'svg'). Disables irrelevant download buttons. | src/components/ExportPanel.jsx:14 |
canvasRef | React.RefObject | A ref pointing to the wrapper div containing the <canvas> element. | src/components/ExportPanel.jsx:15 |
svgRef | React.RefObject | A ref pointing to the wrapper div containing the <svg> element. | src/components/ExportPanel.jsx:16 |
includeMargin | boolean | If true, the QR code's "Quiet Zone" is included. If false, padding is added during export. | src/components/ExportPanel.jsx:17 |
transparentBg | boolean | If true (and format is 'canvas'), the PNG will have a transparent background. | src/components/ExportPanel.jsx:18 |
bgColor | string | The hex color code for the background. Used if transparentBg is false or for JPG/SVG exports. | src/components/ExportPanel.jsx:19 |
onError | Function | A callback function (message, type) to display toast notifications for success, warnings, or errors. | src/components/ExportPanel.jsx:20 |
Core Functionality: handleDownload
The primary logic resides within the handleDownload function, which is triggered when a user clicks one of the download buttons. It is memoized using React.useCallback for performance.
Sources: src/components/ExportPanel.jsx:25-90
Download Process Flow
The download process follows a clear sequence of steps, from user action to file generation and download.
This flowchart shows the logical path inside handleDownload, including validation, user confirmation for padding, format-specific warnings, and the final calls to helper functions to generate and download the file.
Sources: src/components/ExportPanel.jsx:25-90
Key Logic Points
1. Source Element Retrieval
The function first determines whether to export from a <canvas> or <svg> element based on the format prop. It uses the corresponding ref (canvasRef or svgRef) to find the DOM element. If the element is not found, it triggers an error.
// src/components/ExportPanel.jsx:30-36
const isSVG = format === 'svg';
const srcCanvas = !isSVG ? canvasRef.current?.querySelector('canvas') : null;
const srcSVG = isSVG ? svgRef.current?.querySelector('svg') : null;
if ((isSVG && !srcSVG) || (!isSVG && !srcCanvas)) {
onError?.('Không tìm thấy QR code để tải xuống', 'error');
// ...
return;
}
2. Automatic Padding (Quiet Zone)
If the user has disabled the "Quiet Zone" (includeMargin is false), the export process automatically adds an 8% padding to ensure the QR code remains scannable. The user is warned about this via a window.confirm dialog before proceeding.
// src/components/ExportPanel.jsx:41-49
const safePadding = Math.max(8, Math.ceil(size * 0.08));
const padPx = includeMargin ? 0 : safePadding;
if (!includeMargin) {
const confirmed = window.confirm(
`⚠️ QR thiếu "Quiet Zone" (viền trắng) có thể khó quét!\n\nẢnh export sẽ tự động thêm padding ${safePadding}px (8% kích thước) để đảm bảo an toàn khi in.\n\nBạn có muốn tiếp tục không?`,
);
if (!confirmed) {
// ...
return;
}
}
3. Format-Specific Handling
- JPG Transparency: If the user tries to export a JPG with a transparent background, a warning is displayed, and the background is automatically set to white.
- Canvas vs. SVG: The function calls different helper utilities based on the format:
buildDataURLforcanvas(PNG/JPG) andbuildSVGBlobforsvg.
Sources: src/components/ExportPanel.jsx:51-76
4. Error Handling
A try...catch block wraps the export logic. It specifically checks for CORS-related errors, which can occur if a logo from a cross-origin source is used, and provides a user-friendly error message.
// src/components/ExportPanel.jsx:81-87
if (error.message?.includes('tainted') || error.message?.includes('cross-origin')) {
onError?.(
'❌ Lỗi CORS: Logo từ nguồn khác không cho phép export. Vui lòng upload logo từ máy hoặc dùng URL hỗ trợ CORS.',
'error',
);
} else {
onError?.(`❌ Lỗi export: ${error.message}`, 'error');
}
This is a crucial security and usability feature, as detailed in the project's security audit.
Sources: SECURITY_AUDIT_REPORT.md, src/components/ExportPanel.jsx:78-88
Helper Utilities from qr-helpers.js
The ExportPanel relies on several utility functions from src/utils/qr-helpers.js to perform the file generation and download.
Sequence of Helper Function Calls
Function Descriptions
| Function | Description | Source File |
|---|---|---|
buildDataURL | Takes a source <canvas>, an extension (png or jpg), and options (padPx, bg). It creates a new temporary canvas, fills the background, draws the source canvas with padding, and returns a data:URL string for the image. | src/utils/qr-helpers.js:188-220 |
buildSVGBlob | Takes a source <svg> element and options (padPx, bg). It clones the SVG, adjusts its dimensions to include padding, prepends a background <rect> element, and returns a Blob containing the new SVG data. | src/utils/qr-helpers.js:228-261 |
downloadFile | Takes a data:URL string or a Blob, and a filename. It creates a temporary <a> element, sets its href and download attributes, and programmatically clicks it to initiate a file download in the browser. | src/utils/qr-helpers.js:268-280 |
These helpers are tested in tests/qr-helpers.test.js, although the canvas-related tests are skipped in the Node.js environment.
Sources: tests/qr-helpers.test.js:101-158
UI and State
The component's UI consists of three styled buttons for PNG, JPG, and SVG downloads.
- State: A single state variable,
isExporting(boolean), is used to disable all download buttons while an export is in progress, preventing concurrent operations. - Conditional Disabling: Buttons are also disabled based on the
formatprop. For example, if the QR code is rendered as an SVG, the PNG and JPG download buttons are disabled.
// src/components/ExportPanel.jsx:100-105
<button
onClick={() => handleDownload('png')}
disabled={isExporting || format === 'svg'}
aria-label="Tải xuống PNG"
// ...
>
The component also includes a "notes" section that provides helpful tips to the user about the different file formats and the status of the "Quiet Zone".
Sources: src/components/ExportPanel.jsx:93-148
Summary
ExportPanel.jsx is a well-defined, single-responsibility component that encapsulates all logic related to downloading the generated QR code. It effectively uses props for configuration, local state for managing UI interactivity, and delegates complex file generation tasks to utility functions in qr-helpers.js. Its robust error handling, particularly for CORS issues, and user-centric features like automatic padding make it a critical and polished part of the QR-Tool application.