@qrcodesdk/react 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/LICENSE +19 -0
- package/README.md +310 -0
- package/dist/index.d.mts +26 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +84 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +94 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2026 Dominik Dafert
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
<!-- Generated from apps/docs/src/content/docs/packages/react.mdx. Run `pnpm --filter docs generate-readmes` to update. -->
|
|
2
|
+
|
|
3
|
+
<p align="center"><img src="https://qrcodesdk.dev/favicon.svg" alt="QRCodeSDK logo" width="240"></p>
|
|
4
|
+
|
|
5
|
+
# @qrcodesdk/react
|
|
6
|
+
|
|
7
|
+
[](https://npmx.dev/@qrcodesdk/react)    [](https://github.com/Dafnik/qrcodesdk/tree/main/packages/react)
|
|
8
|
+
|
|
9
|
+
**[Live Demo](https://qrcodesdk.dev/playground/?package=react)**
|
|
10
|
+
|
|
11
|
+
`@qrcodesdk/react` provides React components for rendering QR codes as inline SVG, PNG-backed Image elements, and Canvas elements.
|
|
12
|
+
|
|
13
|
+
It supports React and React DOM 18 and 19.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install @qrcodesdk/react @qrcodesdk/core @qrcodesdk/browser
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
pnpm add @qrcodesdk/react @qrcodesdk/core @qrcodesdk/browser
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
<details>
|
|
26
|
+
<summary>Other package managers</summary>
|
|
27
|
+
|
|
28
|
+
**vp**
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
vp add @qrcodesdk/react @qrcodesdk/core @qrcodesdk/browser
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**deno**
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
deno add @qrcodesdk/react @qrcodesdk/core @qrcodesdk/browser
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**bun**
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
bun add @qrcodesdk/react @qrcodesdk/core @qrcodesdk/browser
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**yarn**
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
yarn add @qrcodesdk/react @qrcodesdk/core @qrcodesdk/browser
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
</details>
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
Import the components directly where you need them:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import {QRCodeCanvas, QRCodeImage, QRCodeSVG} from '@qrcodesdk/react';
|
|
60
|
+
|
|
61
|
+
export function App() {
|
|
62
|
+
return (
|
|
63
|
+
<>
|
|
64
|
+
<QRCodeSVG data="https://qrcodesdk.dev" />
|
|
65
|
+
<QRCodeImage data="https://qrcodesdk.dev" />
|
|
66
|
+
<QRCodeCanvas data="https://qrcodesdk.dev" />
|
|
67
|
+
</>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Components
|
|
73
|
+
|
|
74
|
+
| Component | Output | Download support |
|
|
75
|
+
| -------------- | ------------------------------------------- | ---------------- |
|
|
76
|
+
| `QRCodeSVG` | Inline SVG inside a wrapper `<div>` | SVG |
|
|
77
|
+
| `QRCodeImage` | PNG-backed `<img>` inside a wrapper `<div>` | PNG |
|
|
78
|
+
| `QRCodeCanvas` | `<canvas>` inside a wrapper `<div>` | None |
|
|
79
|
+
|
|
80
|
+
All components accept:
|
|
81
|
+
|
|
82
|
+
| Prop | Type | Description |
|
|
83
|
+
| ----------- | -------------------------- | ----------------------------------------------- |
|
|
84
|
+
| `data` | `string \| number` | Required QR code payload. |
|
|
85
|
+
| `options` | Component-specific options | Optional matrix and renderer configuration. |
|
|
86
|
+
| `className` | `string` | CSS class applied to the component wrapper div. |
|
|
87
|
+
|
|
88
|
+
`options` supports shared matrix options such as `version`, `mode`, `errorCorrectionLevel`, and `mask`. Renderer options match the corresponding QRCodeSDK renderer:
|
|
89
|
+
|
|
90
|
+
- `QRCodeSVG` uses `QRCodeSVGOptions`.
|
|
91
|
+
- `QRCodeImage` uses `QRCodeImageOptions`.
|
|
92
|
+
- `QRCodeCanvas` uses `QRCodeCanvasOptions`.
|
|
93
|
+
|
|
94
|
+
Import `QRCodeSVGOptions` from `@qrcodesdk/core`. Import `QRCodeImageOptions` and `QRCodeCanvasOptions` from `@qrcodesdk/browser`.
|
|
95
|
+
|
|
96
|
+
## Matrix options
|
|
97
|
+
|
|
98
|
+
The `options` prop combines the component's renderer options with the shared QR matrix options:
|
|
99
|
+
|
|
100
|
+
| Option | Type | Default | Description |
|
|
101
|
+
| ---------------------- | ---------------------------------------- | --------- | ----------------------- |
|
|
102
|
+
| `mode` | `'numeric' \| 'alphanumeric' \| 'octet'` | Automatic | Encoding mode. |
|
|
103
|
+
| `errorCorrectionLevel` | `'L' \| 'M' \| 'Q' \| 'H'` | `'M'` | Error correction level. |
|
|
104
|
+
| `version` | `1` through `40` | Automatic | Pins the QR version. |
|
|
105
|
+
| `mask` | `0` through `7` | Automatic | Pins the QR mask. |
|
|
106
|
+
|
|
107
|
+
Most applications should let the builder select the mode, version, and mask automatically.
|
|
108
|
+
|
|
109
|
+
## Live examples
|
|
110
|
+
|
|
111
|
+
### SVG component
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
import {QRCodeSVG} from '@qrcodesdk/react';
|
|
115
|
+
|
|
116
|
+
export default function QRCodeSVGExample() {
|
|
117
|
+
return (
|
|
118
|
+
<QRCodeSVG
|
|
119
|
+
data="https://qrcodesdk.dev"
|
|
120
|
+
options={{
|
|
121
|
+
title: 'QR code for qrcodesdk.dev',
|
|
122
|
+
ariaLabel: 'Scan to open qrcodesdk.dev',
|
|
123
|
+
}}
|
|
124
|
+
/>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Image component
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
import type {QRCodeImageOptions} from '@qrcodesdk/browser';
|
|
133
|
+
import {QRCodeImage} from '@qrcodesdk/react';
|
|
134
|
+
|
|
135
|
+
export default function QRCodeImageExample() {
|
|
136
|
+
const options: QRCodeImageOptions = {
|
|
137
|
+
size: 8,
|
|
138
|
+
margin: 4,
|
|
139
|
+
alt: 'QR code for qrcodesdk.dev',
|
|
140
|
+
ariaLabel: 'Scan to open qrcodesdk.dev',
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return <QRCodeImage data="https://qrcodesdk.dev" options={options} />;
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Canvas component
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
import type {QRCodeCanvasOptions} from '@qrcodesdk/browser';
|
|
151
|
+
import {QRCodeCanvas} from '@qrcodesdk/react';
|
|
152
|
+
|
|
153
|
+
export default function QRCodeCanvasExample() {
|
|
154
|
+
const options: QRCodeCanvasOptions = {
|
|
155
|
+
size: 8,
|
|
156
|
+
margin: 4,
|
|
157
|
+
colors: {
|
|
158
|
+
colorDark: '#111827',
|
|
159
|
+
colorLight: '#ffffff',
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
return <QRCodeCanvas data="https://qrcodesdk.dev" options={options} />;
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### PNG download
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
import {useRef} from 'react';
|
|
171
|
+
|
|
172
|
+
import {QRCodeImage, type QRCodeDownloadHandle} from '@qrcodesdk/react';
|
|
173
|
+
|
|
174
|
+
export default function QRCodeDownloadImageExample() {
|
|
175
|
+
const qrcode = useRef<QRCodeDownloadHandle>(null);
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<div className="flex flex-col items-center">
|
|
179
|
+
<QRCodeImage
|
|
180
|
+
data="https://qrcodesdk.dev"
|
|
181
|
+
options={{
|
|
182
|
+
alt: 'QR code for qrcodesdk.dev',
|
|
183
|
+
}}
|
|
184
|
+
ref={qrcode}
|
|
185
|
+
/>
|
|
186
|
+
<button
|
|
187
|
+
className="btn-primary"
|
|
188
|
+
onClick={() => qrcode.current?.download('qrcodesdk')}
|
|
189
|
+
type="button">
|
|
190
|
+
Download PNG
|
|
191
|
+
</button>
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Renderer options
|
|
198
|
+
|
|
199
|
+
| Option | Components | Type | Default |
|
|
200
|
+
| ----------------------- | ---------- | ---------------------------- | -----------------: |
|
|
201
|
+
| `size` | All | `number` | `5` |
|
|
202
|
+
| `margin` | All | `number` | `4` |
|
|
203
|
+
| `colors.colorDark` | All | `string` | `'#000000'` |
|
|
204
|
+
| `colors.colorLight` | All | `string` | `'#ffffff'` |
|
|
205
|
+
| `dotsOptions` | All | `QRCodeDotsOptions` | `{type: 'square'}` |
|
|
206
|
+
| `cornersSquareOptions` | All | `QRCodeCornersSquareOptions` | `{type: 'square'}` |
|
|
207
|
+
| `cornersDotOptions` | All | `QRCodeCornersDotOptions` | `{type: 'square'}` |
|
|
208
|
+
| `alt` | SVG, Image | `string` | `undefined` |
|
|
209
|
+
| `ariaLabel` | SVG, Image | `string` | `undefined` |
|
|
210
|
+
| `title` | SVG, Image | `string` | `undefined` |
|
|
211
|
+
| `image.source` | All | Renderer-specific source | `undefined` |
|
|
212
|
+
| `image.size` | All | `number` | `0.4` |
|
|
213
|
+
| `image.padding` | All | `number` | `1` |
|
|
214
|
+
| `image.clearBackground` | All | `boolean` | `true` |
|
|
215
|
+
|
|
216
|
+
Color options use hash-prefixed values such as `#111827`. All built-in renderers require a positive safe integer `size` and a non-negative safe integer `margin`.
|
|
217
|
+
|
|
218
|
+
Module, finder-ring, and finder-center options pass through to every component. Their optional
|
|
219
|
+
colors independently inherit `colors.colorDark`.
|
|
220
|
+
|
|
221
|
+
### Prepared center images
|
|
222
|
+
|
|
223
|
+
Prepare browser image sources before rendering:
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
import {useState} from 'react';
|
|
227
|
+
|
|
228
|
+
import {QRCodeImage} from '@qrcodesdk/react';
|
|
229
|
+
|
|
230
|
+
export function QRCodeWithLogo() {
|
|
231
|
+
const [logo, setLogo] = useState<HTMLImageElement>();
|
|
232
|
+
|
|
233
|
+
async function selectLogo(file: File) {
|
|
234
|
+
const source = new Image();
|
|
235
|
+
const localUrl = URL.createObjectURL(file);
|
|
236
|
+
source.src = localUrl;
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
await source.decode();
|
|
240
|
+
setLogo(source);
|
|
241
|
+
} finally {
|
|
242
|
+
URL.revokeObjectURL(localUrl);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<>
|
|
248
|
+
<input
|
|
249
|
+
accept="image/*"
|
|
250
|
+
type="file"
|
|
251
|
+
onChange={(event) => {
|
|
252
|
+
const file = event.currentTarget.files?.[0];
|
|
253
|
+
if (file) void selectLogo(file);
|
|
254
|
+
}}
|
|
255
|
+
/>
|
|
256
|
+
{logo ? (
|
|
257
|
+
<QRCodeImage
|
|
258
|
+
data="https://qrcodesdk.dev"
|
|
259
|
+
options={{errorCorrectionLevel: 'H', image: {source: logo, size: 0.3}}}
|
|
260
|
+
/>
|
|
261
|
+
) : null}
|
|
262
|
+
</>
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Use an embedded `data:image/...` URL for `QRCodeSVG`. Components never load paths or URLs
|
|
268
|
+
themselves, and SVG/PNG downloads include the overlay.
|
|
269
|
+
|
|
270
|
+
## Download files
|
|
271
|
+
|
|
272
|
+
- `QRCodeSVG` exposes `download(filename?)` and writes an SVG file through a React ref.
|
|
273
|
+
- `QRCodeImage` exposes `download(filename?)` and writes a PNG file through a React ref.
|
|
274
|
+
|
|
275
|
+
```tsx
|
|
276
|
+
import {useRef} from 'react';
|
|
277
|
+
|
|
278
|
+
import {QRCodeImage, type QRCodeDownloadHandle} from '@qrcodesdk/react';
|
|
279
|
+
|
|
280
|
+
export function QRCodeDownload() {
|
|
281
|
+
const qrcode = useRef<QRCodeDownloadHandle>(null);
|
|
282
|
+
|
|
283
|
+
return (
|
|
284
|
+
<>
|
|
285
|
+
<button type="button" onClick={() => qrcode.current?.download('qrcodesdk')}>
|
|
286
|
+
Download PNG
|
|
287
|
+
</button>
|
|
288
|
+
<QRCodeImage ref={qrcode} data="https://qrcodesdk.dev" />
|
|
289
|
+
</>
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
The appropriate `.svg` or `.png` extension is appended when necessary.
|
|
295
|
+
|
|
296
|
+
`QRCodeCanvas` does not include a download method. Use `QRCodeImage` when you want built-in PNG download support.
|
|
297
|
+
|
|
298
|
+
## Server-side rendering
|
|
299
|
+
|
|
300
|
+
`QRCodeSVG` produces runtime-neutral SVG and can render on the server.
|
|
301
|
+
|
|
302
|
+
`QRCodeImage` and `QRCodeCanvas` rely on browser DOM and Canvas APIs, so they skip element creation and downloads outside the browser and populate their host after hydration.
|
|
303
|
+
|
|
304
|
+
## Documentation
|
|
305
|
+
|
|
306
|
+
- [@qrcodesdk/react](https://qrcodesdk.dev/packages/react/)
|
|
307
|
+
- [Customize QR Codes](https://qrcodesdk.dev/advanced/customize/)
|
|
308
|
+
- [Render SVG](https://qrcodesdk.dev/renderers/core/svg/)
|
|
309
|
+
- [Render to an Image Element](https://qrcodesdk.dev/renderers/browser/image/)
|
|
310
|
+
- [Render to Canvas](https://qrcodesdk.dev/renderers/browser/canvas/)
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { QRCodeCanvasOptions, QRCodeImageOptions } from "@qrcodesdk/browser";
|
|
2
|
+
import { QRCodeInputData, QRCodeSVGOptions } from "@qrcodesdk/core";
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
type QRCodeBaseProps<TOptions> = {
|
|
5
|
+
data: QRCodeInputData;
|
|
6
|
+
options?: TOptions;
|
|
7
|
+
className?: string;
|
|
8
|
+
};
|
|
9
|
+
type QRCodeDownloadHandle = {
|
|
10
|
+
download(filename?: string): void;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/QRCodeCanvas.d.ts
|
|
14
|
+
type QRCodeCanvasProps = QRCodeBaseProps<QRCodeCanvasOptions>;
|
|
15
|
+
declare function QRCodeCanvas({ data, options, className }: QRCodeCanvasProps): import("react").JSX.Element;
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/QRCodeImage.d.ts
|
|
18
|
+
type QRCodeImageProps = QRCodeBaseProps<QRCodeImageOptions>;
|
|
19
|
+
declare const QRCodeImage: import("react").ForwardRefExoticComponent<QRCodeImageProps & import("react").RefAttributes<QRCodeDownloadHandle>>;
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/QRCodeSVG.d.ts
|
|
22
|
+
type QRCodeSVGProps = QRCodeBaseProps<QRCodeSVGOptions>;
|
|
23
|
+
declare const QRCodeSVG: import("react").ForwardRefExoticComponent<QRCodeSVGProps & import("react").RefAttributes<QRCodeDownloadHandle>>;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { type QRCodeBaseProps, QRCodeCanvas, type QRCodeCanvasProps, type QRCodeDownloadHandle, QRCodeImage, type QRCodeImageProps, QRCodeSVG, type QRCodeSVGProps };
|
|
26
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/QRCodeCanvas.tsx","../src/QRCodeImage.tsx","../src/QRCodeSVG.tsx"],"mappings":";;;KAEY,gBAAgB;EAC1B,MAAM;EACN,UAAU;EACV;;KAGU;EACV,SAAS;;;;KCFC,oBAAoB,gBAAgB;iBAEhC,eAAc,MAAM,SAAS,aAAY,oCAAiB,IAAA;;;KCE9D,mBAAmB,gBAAgB;cAElC,6BAAW,0BAAA,mCAAA,cAAA;;;KCNZ,iBAAiB,gBAAgB;cAEhC,2BAAS,0BAAA,iCAAA,cAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { QRCodeCanvasRenderer, QRCodeDownloadImageRenderer, QRCodeDownloadSVGRenderer, QRCodeImageRenderer } from "@qrcodesdk/browser";
|
|
2
|
+
import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from "react";
|
|
3
|
+
import { QRCodeSVGRenderer, qrcode } from "@qrcodesdk/core";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/replace-children.ts
|
|
6
|
+
function replaceElementChildren(element, child) {
|
|
7
|
+
element.replaceChildren(child);
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/QRCodeCanvas.tsx
|
|
11
|
+
function QRCodeCanvas({ data, options, className }) {
|
|
12
|
+
const containerRef = useRef(null);
|
|
13
|
+
const canvasRenderer = useMemo(() => QRCodeCanvasRenderer(options), [options]);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const container = containerRef.current;
|
|
16
|
+
if (!container) return;
|
|
17
|
+
replaceElementChildren(container, qrcode(data).config(options).render(canvasRenderer));
|
|
18
|
+
}, [
|
|
19
|
+
canvasRenderer,
|
|
20
|
+
data,
|
|
21
|
+
options
|
|
22
|
+
]);
|
|
23
|
+
return /* @__PURE__ */ jsx("div", {
|
|
24
|
+
className,
|
|
25
|
+
ref: containerRef
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/QRCodeImage.tsx
|
|
30
|
+
const QRCodeImage = forwardRef(function QRCodeImage({ data, options, className }, ref) {
|
|
31
|
+
const containerRef = useRef(null);
|
|
32
|
+
const imageRenderer = useMemo(() => QRCodeImageRenderer(options), [options]);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const container = containerRef.current;
|
|
35
|
+
if (!container) return;
|
|
36
|
+
replaceElementChildren(container, qrcode(data).config(options).render(imageRenderer));
|
|
37
|
+
}, [
|
|
38
|
+
data,
|
|
39
|
+
imageRenderer,
|
|
40
|
+
options
|
|
41
|
+
]);
|
|
42
|
+
useImperativeHandle(ref, () => ({ download(filename) {
|
|
43
|
+
qrcode(data).config(options).render(QRCodeDownloadImageRenderer({
|
|
44
|
+
renderer: imageRenderer,
|
|
45
|
+
filename
|
|
46
|
+
}));
|
|
47
|
+
} }), [
|
|
48
|
+
data,
|
|
49
|
+
imageRenderer,
|
|
50
|
+
options
|
|
51
|
+
]);
|
|
52
|
+
return /* @__PURE__ */ jsx("div", {
|
|
53
|
+
className,
|
|
54
|
+
ref: containerRef
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/QRCodeSVG.tsx
|
|
59
|
+
const QRCodeSVG = forwardRef(function QRCodeSVG({ data, options, className }, ref) {
|
|
60
|
+
const svgRenderer = useMemo(() => QRCodeSVGRenderer(options), [options]);
|
|
61
|
+
const svg = useMemo(() => qrcode(data).config(options).render(svgRenderer), [
|
|
62
|
+
data,
|
|
63
|
+
options,
|
|
64
|
+
svgRenderer
|
|
65
|
+
]);
|
|
66
|
+
useImperativeHandle(ref, () => ({ download(filename) {
|
|
67
|
+
qrcode(data).config(options).render(QRCodeDownloadSVGRenderer({
|
|
68
|
+
renderer: svgRenderer,
|
|
69
|
+
filename
|
|
70
|
+
}));
|
|
71
|
+
} }), [
|
|
72
|
+
data,
|
|
73
|
+
options,
|
|
74
|
+
svgRenderer
|
|
75
|
+
]);
|
|
76
|
+
return /* @__PURE__ */ jsx("div", {
|
|
77
|
+
className,
|
|
78
|
+
dangerouslySetInnerHTML: { __html: svg }
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
//#endregion
|
|
82
|
+
export { QRCodeCanvas, QRCodeImage, QRCodeSVG };
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/replace-children.ts","../src/QRCodeCanvas.tsx","../src/QRCodeImage.tsx","../src/QRCodeSVG.tsx"],"sourcesContent":["export function replaceElementChildren(element: HTMLElement, child: Element): void {\n element.replaceChildren(child);\n}\n","import {type QRCodeCanvasOptions, QRCodeCanvasRenderer} from '@qrcodesdk/browser';\nimport {useEffect, useMemo, useRef} from 'react';\n\nimport {replaceElementChildren} from './replace-children';\nimport type {QRCodeBaseProps} from './types';\nimport {qrcode} from '@qrcodesdk/core';\n\nexport type QRCodeCanvasProps = QRCodeBaseProps<QRCodeCanvasOptions>;\n\nexport function QRCodeCanvas({data, options, className}: QRCodeCanvasProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const canvasRenderer = useMemo(() => QRCodeCanvasRenderer(options), [options]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n replaceElementChildren(container, qrcode(data).config(options).render(canvasRenderer));\n }, [canvasRenderer, data, options]);\n\n return <div className={className} ref={containerRef} />;\n}\n","import {\n QRCodeDownloadImageRenderer,\n type QRCodeImageOptions,\n QRCodeImageRenderer,\n} from '@qrcodesdk/browser';\nimport {forwardRef, useEffect, useImperativeHandle, useMemo, useRef} from 'react';\n\nimport {replaceElementChildren} from './replace-children';\nimport type {QRCodeBaseProps, QRCodeDownloadHandle} from './types';\nimport {qrcode} from '@qrcodesdk/core';\n\nexport type QRCodeImageProps = QRCodeBaseProps<QRCodeImageOptions>;\n\nexport const QRCodeImage = forwardRef<QRCodeDownloadHandle, QRCodeImageProps>(function QRCodeImage(\n {data, options, className},\n ref,\n) {\n const containerRef = useRef<HTMLDivElement>(null);\n const imageRenderer = useMemo(() => QRCodeImageRenderer(options), [options]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n replaceElementChildren(container, qrcode(data).config(options).render(imageRenderer));\n }, [data, imageRenderer, options]);\n\n useImperativeHandle(\n ref,\n () => ({\n download(filename?: string) {\n qrcode(data)\n .config(options)\n .render(\n QRCodeDownloadImageRenderer({\n renderer: imageRenderer,\n filename,\n }),\n );\n },\n }),\n [data, imageRenderer, options],\n );\n\n return <div className={className} ref={containerRef} />;\n});\n","import {QRCodeDownloadSVGRenderer} from '@qrcodesdk/browser';\nimport {type QRCodeSVGOptions, QRCodeSVGRenderer} from '@qrcodesdk/core';\nimport {forwardRef, useImperativeHandle, useMemo} from 'react';\n\nimport type {QRCodeBaseProps, QRCodeDownloadHandle} from './types';\nimport {qrcode} from '@qrcodesdk/core';\n\nexport type QRCodeSVGProps = QRCodeBaseProps<QRCodeSVGOptions>;\n\nexport const QRCodeSVG = forwardRef<QRCodeDownloadHandle, QRCodeSVGProps>(function QRCodeSVG(\n {data, options, className},\n ref,\n) {\n const svgRenderer = useMemo(() => QRCodeSVGRenderer(options), [options]);\n const svg = useMemo(\n () => qrcode(data).config(options).render(svgRenderer),\n [data, options, svgRenderer],\n );\n\n useImperativeHandle(\n ref,\n () => ({\n download(filename?: string) {\n qrcode(data)\n .config(options)\n .render(\n QRCodeDownloadSVGRenderer({\n renderer: svgRenderer,\n filename,\n }),\n );\n },\n }),\n [data, options, svgRenderer],\n );\n\n return <div className={className} dangerouslySetInnerHTML={{__html: svg}} />;\n});\n"],"mappings":";;;;;AAAA,SAAgB,uBAAuB,SAAsB,OAAsB;CACjF,QAAQ,gBAAgB,KAAK;AAC/B;;;ACOA,SAAgB,aAAa,EAAC,MAAM,SAAS,aAA+B;CAC1E,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,iBAAiB,cAAc,qBAAqB,OAAO,GAAG,CAAC,OAAO,CAAC;CAE7E,gBAAgB;EACd,MAAM,YAAY,aAAa;EAC/B,IAAI,CAAC,WAAW;EAEhB,uBAAuB,WAAW,OAAO,IAAI,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,OAAO,cAAc,CAAC;CACvF,GAAG;EAAC;EAAgB;EAAM;CAAO,CAAC;CAElC,OAAO,oBAAC,OAAD;EAAgB;EAAW,KAAK;CAAe,CAAA;AACxD;;;ACRA,MAAa,cAAc,WAAmD,SAAS,YACrF,EAAC,MAAM,SAAS,aAChB,KACA;CACA,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,gBAAgB,cAAc,oBAAoB,OAAO,GAAG,CAAC,OAAO,CAAC;CAE3E,gBAAgB;EACd,MAAM,YAAY,aAAa;EAC/B,IAAI,CAAC,WAAW;EAEhB,uBAAuB,WAAW,OAAO,IAAI,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,OAAO,aAAa,CAAC;CACtF,GAAG;EAAC;EAAM;EAAe;CAAO,CAAC;CAEjC,oBACE,YACO,EACL,SAAS,UAAmB;EAC1B,OAAO,IAAI,CAAC,CACT,OAAO,OAAO,CAAC,CACf,OACC,4BAA4B;GAC1B,UAAU;GACV;EACF,CAAC,CACH;CACJ,EACF,IACA;EAAC;EAAM;EAAe;CAAO,CAC/B;CAEA,OAAO,oBAAC,OAAD;EAAgB;EAAW,KAAK;CAAe,CAAA;AACxD,CAAC;;;ACpCD,MAAa,YAAY,WAAiD,SAAS,UACjF,EAAC,MAAM,SAAS,aAChB,KACA;CACA,MAAM,cAAc,cAAc,kBAAkB,OAAO,GAAG,CAAC,OAAO,CAAC;CACvE,MAAM,MAAM,cACJ,OAAO,IAAI,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,OAAO,WAAW,GACrD;EAAC;EAAM;EAAS;CAAW,CAC7B;CAEA,oBACE,YACO,EACL,SAAS,UAAmB;EAC1B,OAAO,IAAI,CAAC,CACT,OAAO,OAAO,CAAC,CACf,OACC,0BAA0B;GACxB,UAAU;GACV;EACF,CAAC,CACH;CACJ,EACF,IACA;EAAC;EAAM;EAAS;CAAW,CAC7B;CAEA,OAAO,oBAAC,OAAD;EAAgB;EAAW,yBAAyB,EAAC,QAAQ,IAAG;CAAI,CAAA;AAC7E,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@qrcodesdk/react",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"description": "@qrcodesdk/react provides React components for rendering QR codes as SVG, PNG-backed image elements, and canvas elements.",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"private": false,
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"author": {
|
|
10
|
+
"name": "Dafnik",
|
|
11
|
+
"email": "contact@dafnik.me",
|
|
12
|
+
"url": "https://dafnik.me"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"typescript",
|
|
16
|
+
"types",
|
|
17
|
+
"qr-code",
|
|
18
|
+
"qrcode",
|
|
19
|
+
"qr",
|
|
20
|
+
"code",
|
|
21
|
+
"esm",
|
|
22
|
+
"modules",
|
|
23
|
+
"module",
|
|
24
|
+
"react",
|
|
25
|
+
"tsx",
|
|
26
|
+
"jsx"
|
|
27
|
+
],
|
|
28
|
+
"homepage": "https://qrcodesdk.dev/packages/react",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git@github.com:Dafnik/qrcodesdk.git",
|
|
32
|
+
"directory": "packages/react"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/Dafnik/qrcodesdk/issues"
|
|
36
|
+
},
|
|
37
|
+
"funding": [
|
|
38
|
+
{
|
|
39
|
+
"type": "github",
|
|
40
|
+
"url": "https://github.com/sponsors/Dafnik"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"type": "liberapay",
|
|
44
|
+
"url": "https://liberapay.com/Dafnik"
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public",
|
|
49
|
+
"provenance": true
|
|
50
|
+
},
|
|
51
|
+
"types": "./dist/index.d.mts",
|
|
52
|
+
"exports": {
|
|
53
|
+
".": "./dist/index.mjs",
|
|
54
|
+
"./package.json": "./package.json"
|
|
55
|
+
},
|
|
56
|
+
"files": [
|
|
57
|
+
"CHANGELOG.md",
|
|
58
|
+
"LICENSE",
|
|
59
|
+
"dist"
|
|
60
|
+
],
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
63
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
64
|
+
"@qrcodesdk/browser": "^0.0.1",
|
|
65
|
+
"@qrcodesdk/core": "^0.0.1"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@testing-library/dom": "10.4.1",
|
|
69
|
+
"@testing-library/react": "16.3.2",
|
|
70
|
+
"@types/react": "19.2.17",
|
|
71
|
+
"@types/react-dom": "19.2.3",
|
|
72
|
+
"jsdom": "29.1.1",
|
|
73
|
+
"react": "19.2.7",
|
|
74
|
+
"react-dom": "19.2.7",
|
|
75
|
+
"tsdown": "0.22.9",
|
|
76
|
+
"typescript": "7.0.2",
|
|
77
|
+
"vitest": "4.1.10",
|
|
78
|
+
"@qrcodesdk/browser": "0.0.1",
|
|
79
|
+
"@qrcodesdk/core": "0.0.1",
|
|
80
|
+
"@repo/eslint-config": "0.0.0",
|
|
81
|
+
"@repo/typescript-config": "0.0.0",
|
|
82
|
+
"@repo/core-testing": "0.0.0"
|
|
83
|
+
},
|
|
84
|
+
"scripts": {
|
|
85
|
+
"build": "tsdown",
|
|
86
|
+
"postbuild": "cp ../../LICENSE .",
|
|
87
|
+
"watch": "tsdown --watch",
|
|
88
|
+
"test": "vitest --watch false",
|
|
89
|
+
"test:coverage:raw": "vitest --coverage --watch false",
|
|
90
|
+
"check-types": "tsc --noEmit",
|
|
91
|
+
"lint": "eslint src tests --max-warnings 0",
|
|
92
|
+
"clean": "rm -rf .turbo dist"
|
|
93
|
+
}
|
|
94
|
+
}
|