multi-qr-scanner-poc 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/README.md +67 -0
- package/dist/components/MultiQRScanner.d.ts +1 -0
- package/dist/multi-qr-scanner.js +494 -0
- package/dist/multi-qr-scanner.umd.cjs +6 -0
- package/dist/vite.svg +1 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Dự Án Thử Nghiệm Quét Đa Mã QR (Multi-QR Scanning Feasibility Test)
|
|
2
|
+
|
|
3
|
+
## 1. Tổng Quan (Overview)
|
|
4
|
+
Dự án này là một Proof of Concept (PoC) nhằm kiểm tra tính khả thi của việc quét đồng thời nhiều mã QR cùng một lúc từ webcam.
|
|
5
|
+
**Bối cảnh:** Sử dụng cho việc check-in tại hội nghị hoặc lớp học, nơi có nhiều người cùng đưa mã QR ra trước camera cùng một lúc (3-5 người).
|
|
6
|
+
|
|
7
|
+
## 2. Công Nghệ Sử Dụng (Tech Stack)
|
|
8
|
+
- **Frontend Framework**: React (với TypeScript) - được khởi tạo bằng Vite.
|
|
9
|
+
- **QR Detection**:
|
|
10
|
+
- Sử dụng **Barcode Detection API** (Native Browser API) cho Android/Chrome.
|
|
11
|
+
- Sử dụng **Polyfill (@undecaf/barcode-detector-polyfill)** cho **iOS (Safari)** và **Electron**.
|
|
12
|
+
- *Lý do:* Đảm bảo tốc độ cao nhất trên thiết bị hỗ trợ native, và vẫn hoạt động tốt trên các thiết bị chưa hỗ trợ.
|
|
13
|
+
|
|
14
|
+
## 3. Chức Năng Chính (Features)
|
|
15
|
+
1. **Quét Đa Điểm (Multi-Code Detection)**:
|
|
16
|
+
- Có thể nhận diện và đọc dữ liệu của nhiều mã QR trong cùng một khung hình video.
|
|
17
|
+
2. **Quản Lý Trạng Thái (State Management & De-duplication)**:
|
|
18
|
+
- Giải quyết vấn đề "Spam API" khi camera quét liên tục (30-60 khung hình/giây).
|
|
19
|
+
- Mỗi mã QR sau khi quét sẽ có trạng thái riêng:
|
|
20
|
+
- **Mới (New)**: Chưa từng quét -> Bắt đầu xử lý.
|
|
21
|
+
- **Đang xử lý (Processing - Vàng)**: Đang gọi API kiểm tra. Bỏ qua các lần quét trùng lặp trong lúc này.
|
|
22
|
+
- **Thành công (Success - Xanh)**: Đã check-in thành công. Không gọi lại API nữa.
|
|
23
|
+
- **Lỗi (Error - Đỏ)**: Check-in thất bại. Hệ thống sẽ cho phép thử lại (retry) sau một khoảng thời gian (ví dụ: 5 giây).
|
|
24
|
+
3. **Giao Diện Trực Quan (Visual Feedback)**:
|
|
25
|
+
- Vẽ khung bao (bounding box) quanh mã QR theo màu sắc trạng thái tương ứng.
|
|
26
|
+
|
|
27
|
+
## 4. Cơ Chế Hoạt Động (How it works)
|
|
28
|
+
1. **Khởi tạo Camera**: Ứng dụng xin quyền truy cập webcam và hiển thị luồng video (ưu tiên camera sau/môi trường trên mobile).
|
|
29
|
+
2. **Vòng lặp Phát hiện (Detection Loop)**:
|
|
30
|
+
- Sử dụng `BarcodeDetector.detect()` liên tục trên luồng video.
|
|
31
|
+
- Kết quả trả về là danh sách các mã QR có trong khung hình.
|
|
32
|
+
3. **Xử lý Logic (Scanning Logic)**:
|
|
33
|
+
- Với mỗi mã tìm thấy, kiểm tra trong bộ nhớ đệm (`scanStates` map):
|
|
34
|
+
- Nếu mã **chưa có** hoặc **đã từng lỗi (cách đây 5s)** -> Đánh dấu là `Processing` và gọi hàm `checkInUser` (Mock API).
|
|
35
|
+
- Nếu mã đang `Processing` hoặc đã `Success` -> Bỏ qua, không làm gì cả.
|
|
36
|
+
4. **Mock API**:
|
|
37
|
+
- Hàm `checkInUser` giả lập độ trễ mạng 2 giây.
|
|
38
|
+
- Nếu nội dung mã QR chứa từ "fail" -> trả về Lỗi, ngược lại -> Thành công.
|
|
39
|
+
|
|
40
|
+
## 5. Hướng Dẫn Cài Đặt & Chạy (Setup & Run)
|
|
41
|
+
Yêu cầu: Node.js version 18+ hoặc 20+.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# 1. Cài đặt thư viện
|
|
45
|
+
npm install
|
|
46
|
+
|
|
47
|
+
# 2. Chạy dự án (môi trường dev)
|
|
48
|
+
npm run dev
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 6. Hướng Phát Triển & Mở Rộng (Future Scalability)
|
|
52
|
+
|
|
53
|
+
Nếu dự án phát triển lớn hơn, đây là các bước nâng cấp đề xuất:
|
|
54
|
+
|
|
55
|
+
1. **Tối Ưu Hiệu Năng (Performance)**:
|
|
56
|
+
- Thay vì chạy logic xử lý ảnh trên luồng chính (Main Thread), hãy chuyển nó xuống **Web Worker**. Điều này giúp UI không bao giờ bị đơ, kể cả khi xử lý thuật toán nặng.
|
|
57
|
+
- Sử dụng **WASM** (WebAssembly) cho các thuật toán xử lý ảnh chuyên sâu (nếu BarcodeDetector chưa đủ).
|
|
58
|
+
|
|
59
|
+
2. **Giao Tiếp Backend (Real-time Communication)**:
|
|
60
|
+
- Thay vì gọi API REST (`POST /check-in`), hãy chuyển sang dùng **WebSocket**.
|
|
61
|
+
- WebSocket giảm độ trễ kết nối, phù hợp cho việc check-in dòng người liên tục.
|
|
62
|
+
|
|
63
|
+
3. **Tính Năng Mở Rộng**:
|
|
64
|
+
- Thêm nút bật/tắt đèn Flash (Torch).
|
|
65
|
+
- Hỗ trợ Zoom Camera (sử dụng `imageCapture.setOptions`).
|
|
66
|
+
- Chuyển đổi linh hoạt giữa Camera trước/sau.
|
|
67
|
+
- Hỗ trợ đọc thêm Barcode 1D (EAN-13, UPC) song song với QR Code.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import le, { useRef as q, useState as z, useEffect as J } from "react";
|
|
2
|
+
import { ZBarSymbolType as c, ZBarConfigType as ne, scanRGBABuffer as ue, getDefaultScanner as fe } from "https://cdn.jsdelivr.net/npm/@undecaf/zbar-wasm@0.9.16/dist/main.js";
|
|
3
|
+
var k = { exports: {} }, B = {};
|
|
4
|
+
var Q;
|
|
5
|
+
function de() {
|
|
6
|
+
if (Q) return B;
|
|
7
|
+
Q = 1;
|
|
8
|
+
var l = /* @__PURE__ */ Symbol.for("react.transitional.element"), t = /* @__PURE__ */ Symbol.for("react.fragment");
|
|
9
|
+
function n(a, s, i) {
|
|
10
|
+
var R = null;
|
|
11
|
+
if (i !== void 0 && (R = "" + i), s.key !== void 0 && (R = "" + s.key), "key" in s) {
|
|
12
|
+
i = {};
|
|
13
|
+
for (var v in s)
|
|
14
|
+
v !== "key" && (i[v] = s[v]);
|
|
15
|
+
} else i = s;
|
|
16
|
+
return s = i.ref, {
|
|
17
|
+
$$typeof: l,
|
|
18
|
+
type: a,
|
|
19
|
+
key: R,
|
|
20
|
+
ref: s !== void 0 ? s : null,
|
|
21
|
+
props: i
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return B.Fragment = t, B.jsx = n, B.jsxs = n, B;
|
|
25
|
+
}
|
|
26
|
+
var N = {};
|
|
27
|
+
var K;
|
|
28
|
+
function me() {
|
|
29
|
+
return K || (K = 1, process.env.NODE_ENV !== "production" && (function() {
|
|
30
|
+
function l(e) {
|
|
31
|
+
if (e == null) return null;
|
|
32
|
+
if (typeof e == "function")
|
|
33
|
+
return e.$$typeof === ie ? null : e.displayName || e.name || null;
|
|
34
|
+
if (typeof e == "string") return e;
|
|
35
|
+
switch (e) {
|
|
36
|
+
case A:
|
|
37
|
+
return "Fragment";
|
|
38
|
+
case b:
|
|
39
|
+
return "Profiler";
|
|
40
|
+
case y:
|
|
41
|
+
return "StrictMode";
|
|
42
|
+
case C:
|
|
43
|
+
return "Suspense";
|
|
44
|
+
case F:
|
|
45
|
+
return "SuspenseList";
|
|
46
|
+
case oe:
|
|
47
|
+
return "Activity";
|
|
48
|
+
}
|
|
49
|
+
if (typeof e == "object")
|
|
50
|
+
switch (typeof e.tag == "number" && console.error(
|
|
51
|
+
"Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
|
|
52
|
+
), e.$$typeof) {
|
|
53
|
+
case h:
|
|
54
|
+
return "Portal";
|
|
55
|
+
case D:
|
|
56
|
+
return e.displayName || "Context";
|
|
57
|
+
case x:
|
|
58
|
+
return (e._context.displayName || "Context") + ".Consumer";
|
|
59
|
+
case j:
|
|
60
|
+
var o = e.render;
|
|
61
|
+
return e = e.displayName, e || (e = o.displayName || o.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e;
|
|
62
|
+
case ae:
|
|
63
|
+
return o = e.displayName || null, o !== null ? o : l(e.type) || "Memo";
|
|
64
|
+
case M:
|
|
65
|
+
o = e._payload, e = e._init;
|
|
66
|
+
try {
|
|
67
|
+
return l(e(o));
|
|
68
|
+
} catch {
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
function t(e) {
|
|
74
|
+
return "" + e;
|
|
75
|
+
}
|
|
76
|
+
function n(e) {
|
|
77
|
+
try {
|
|
78
|
+
t(e);
|
|
79
|
+
var o = !1;
|
|
80
|
+
} catch {
|
|
81
|
+
o = !0;
|
|
82
|
+
}
|
|
83
|
+
if (o) {
|
|
84
|
+
o = console;
|
|
85
|
+
var u = o.error, d = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
|
|
86
|
+
return u.call(
|
|
87
|
+
o,
|
|
88
|
+
"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
|
|
89
|
+
d
|
|
90
|
+
), t(e);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function a(e) {
|
|
94
|
+
if (e === A) return "<>";
|
|
95
|
+
if (typeof e == "object" && e !== null && e.$$typeof === M)
|
|
96
|
+
return "<...>";
|
|
97
|
+
try {
|
|
98
|
+
var o = l(e);
|
|
99
|
+
return o ? "<" + o + ">" : "<...>";
|
|
100
|
+
} catch {
|
|
101
|
+
return "<...>";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function s() {
|
|
105
|
+
var e = Z.A;
|
|
106
|
+
return e === null ? null : e.getOwner();
|
|
107
|
+
}
|
|
108
|
+
function i() {
|
|
109
|
+
return Error("react-stack-top-frame");
|
|
110
|
+
}
|
|
111
|
+
function R(e) {
|
|
112
|
+
if (U.call(e, "key")) {
|
|
113
|
+
var o = Object.getOwnPropertyDescriptor(e, "key").get;
|
|
114
|
+
if (o && o.isReactWarning) return !1;
|
|
115
|
+
}
|
|
116
|
+
return e.key !== void 0;
|
|
117
|
+
}
|
|
118
|
+
function v(e, o) {
|
|
119
|
+
function u() {
|
|
120
|
+
W || (W = !0, console.error(
|
|
121
|
+
"%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
|
|
122
|
+
o
|
|
123
|
+
));
|
|
124
|
+
}
|
|
125
|
+
u.isReactWarning = !0, Object.defineProperty(e, "key", {
|
|
126
|
+
get: u,
|
|
127
|
+
configurable: !0
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function _() {
|
|
131
|
+
var e = l(this.type);
|
|
132
|
+
return $[e] || ($[e] = !0, console.error(
|
|
133
|
+
"Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
|
|
134
|
+
)), e = this.props.ref, e !== void 0 ? e : null;
|
|
135
|
+
}
|
|
136
|
+
function g(e, o, u, d, I, Y) {
|
|
137
|
+
var m = u.ref;
|
|
138
|
+
return e = {
|
|
139
|
+
$$typeof: E,
|
|
140
|
+
type: e,
|
|
141
|
+
key: o,
|
|
142
|
+
props: u,
|
|
143
|
+
_owner: d
|
|
144
|
+
}, (m !== void 0 ? m : null) !== null ? Object.defineProperty(e, "ref", {
|
|
145
|
+
enumerable: !1,
|
|
146
|
+
get: _
|
|
147
|
+
}) : Object.defineProperty(e, "ref", { enumerable: !1, value: null }), e._store = {}, Object.defineProperty(e._store, "validated", {
|
|
148
|
+
configurable: !1,
|
|
149
|
+
enumerable: !1,
|
|
150
|
+
writable: !0,
|
|
151
|
+
value: 0
|
|
152
|
+
}), Object.defineProperty(e, "_debugInfo", {
|
|
153
|
+
configurable: !1,
|
|
154
|
+
enumerable: !1,
|
|
155
|
+
writable: !0,
|
|
156
|
+
value: null
|
|
157
|
+
}), Object.defineProperty(e, "_debugStack", {
|
|
158
|
+
configurable: !1,
|
|
159
|
+
enumerable: !1,
|
|
160
|
+
writable: !0,
|
|
161
|
+
value: I
|
|
162
|
+
}), Object.defineProperty(e, "_debugTask", {
|
|
163
|
+
configurable: !1,
|
|
164
|
+
enumerable: !1,
|
|
165
|
+
writable: !0,
|
|
166
|
+
value: Y
|
|
167
|
+
}), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
|
|
168
|
+
}
|
|
169
|
+
function p(e, o, u, d, I, Y) {
|
|
170
|
+
var m = o.children;
|
|
171
|
+
if (m !== void 0)
|
|
172
|
+
if (d)
|
|
173
|
+
if (se(m)) {
|
|
174
|
+
for (d = 0; d < m.length; d++)
|
|
175
|
+
O(m[d]);
|
|
176
|
+
Object.freeze && Object.freeze(m);
|
|
177
|
+
} else
|
|
178
|
+
console.error(
|
|
179
|
+
"React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
|
|
180
|
+
);
|
|
181
|
+
else O(m);
|
|
182
|
+
if (U.call(o, "key")) {
|
|
183
|
+
m = l(e);
|
|
184
|
+
var S = Object.keys(o).filter(function(ce) {
|
|
185
|
+
return ce !== "key";
|
|
186
|
+
});
|
|
187
|
+
d = 0 < S.length ? "{key: someKey, " + S.join(": ..., ") + ": ...}" : "{key: someKey}", G[m + d] || (S = 0 < S.length ? "{" + S.join(": ..., ") + ": ...}" : "{}", console.error(
|
|
188
|
+
`A props object containing a "key" prop is being spread into JSX:
|
|
189
|
+
let props = %s;
|
|
190
|
+
<%s {...props} />
|
|
191
|
+
React keys must be passed directly to JSX without using spread:
|
|
192
|
+
let props = %s;
|
|
193
|
+
<%s key={someKey} {...props} />`,
|
|
194
|
+
d,
|
|
195
|
+
m,
|
|
196
|
+
S,
|
|
197
|
+
m
|
|
198
|
+
), G[m + d] = !0);
|
|
199
|
+
}
|
|
200
|
+
if (m = null, u !== void 0 && (n(u), m = "" + u), R(o) && (n(o.key), m = "" + o.key), "key" in o) {
|
|
201
|
+
u = {};
|
|
202
|
+
for (var H in o)
|
|
203
|
+
H !== "key" && (u[H] = o[H]);
|
|
204
|
+
} else u = o;
|
|
205
|
+
return m && v(
|
|
206
|
+
u,
|
|
207
|
+
typeof e == "function" ? e.displayName || e.name || "Unknown" : e
|
|
208
|
+
), g(
|
|
209
|
+
e,
|
|
210
|
+
m,
|
|
211
|
+
u,
|
|
212
|
+
s(),
|
|
213
|
+
I,
|
|
214
|
+
Y
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
function O(e) {
|
|
218
|
+
w(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e !== null && e.$$typeof === M && (e._payload.status === "fulfilled" ? w(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
|
|
219
|
+
}
|
|
220
|
+
function w(e) {
|
|
221
|
+
return typeof e == "object" && e !== null && e.$$typeof === E;
|
|
222
|
+
}
|
|
223
|
+
var f = le, E = /* @__PURE__ */ Symbol.for("react.transitional.element"), h = /* @__PURE__ */ Symbol.for("react.portal"), A = /* @__PURE__ */ Symbol.for("react.fragment"), y = /* @__PURE__ */ Symbol.for("react.strict_mode"), b = /* @__PURE__ */ Symbol.for("react.profiler"), x = /* @__PURE__ */ Symbol.for("react.consumer"), D = /* @__PURE__ */ Symbol.for("react.context"), j = /* @__PURE__ */ Symbol.for("react.forward_ref"), C = /* @__PURE__ */ Symbol.for("react.suspense"), F = /* @__PURE__ */ Symbol.for("react.suspense_list"), ae = /* @__PURE__ */ Symbol.for("react.memo"), M = /* @__PURE__ */ Symbol.for("react.lazy"), oe = /* @__PURE__ */ Symbol.for("react.activity"), ie = /* @__PURE__ */ Symbol.for("react.client.reference"), Z = f.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, U = Object.prototype.hasOwnProperty, se = Array.isArray, L = console.createTask ? console.createTask : function() {
|
|
224
|
+
return null;
|
|
225
|
+
};
|
|
226
|
+
f = {
|
|
227
|
+
react_stack_bottom_frame: function(e) {
|
|
228
|
+
return e();
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
var W, $ = {}, V = f.react_stack_bottom_frame.bind(
|
|
232
|
+
f,
|
|
233
|
+
i
|
|
234
|
+
)(), X = L(a(i)), G = {};
|
|
235
|
+
N.Fragment = A, N.jsx = function(e, o, u) {
|
|
236
|
+
var d = 1e4 > Z.recentlyCreatedOwnerStacks++;
|
|
237
|
+
return p(
|
|
238
|
+
e,
|
|
239
|
+
o,
|
|
240
|
+
u,
|
|
241
|
+
!1,
|
|
242
|
+
d ? Error("react-stack-top-frame") : V,
|
|
243
|
+
d ? L(a(e)) : X
|
|
244
|
+
);
|
|
245
|
+
}, N.jsxs = function(e, o, u) {
|
|
246
|
+
var d = 1e4 > Z.recentlyCreatedOwnerStacks++;
|
|
247
|
+
return p(
|
|
248
|
+
e,
|
|
249
|
+
o,
|
|
250
|
+
u,
|
|
251
|
+
!0,
|
|
252
|
+
d ? Error("react-stack-top-frame") : V,
|
|
253
|
+
d ? L(a(e)) : X
|
|
254
|
+
);
|
|
255
|
+
};
|
|
256
|
+
})()), N;
|
|
257
|
+
}
|
|
258
|
+
var ee;
|
|
259
|
+
function he() {
|
|
260
|
+
return ee || (ee = 1, process.env.NODE_ENV === "production" ? k.exports = de() : k.exports = me()), k.exports;
|
|
261
|
+
}
|
|
262
|
+
var P = he();
|
|
263
|
+
function ge(l, t, n, a) {
|
|
264
|
+
return new (n || (n = Promise))((function(s, i) {
|
|
265
|
+
function R(g) {
|
|
266
|
+
try {
|
|
267
|
+
_(a.next(g));
|
|
268
|
+
} catch (p) {
|
|
269
|
+
i(p);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function v(g) {
|
|
273
|
+
try {
|
|
274
|
+
_(a.throw(g));
|
|
275
|
+
} catch (p) {
|
|
276
|
+
i(p);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function _(g) {
|
|
280
|
+
var p;
|
|
281
|
+
g.done ? s(g.value) : (p = g.value, p instanceof n ? p : new n((function(O) {
|
|
282
|
+
O(p);
|
|
283
|
+
}))).then(R, v);
|
|
284
|
+
}
|
|
285
|
+
_((a = a.apply(l, [])).next());
|
|
286
|
+
}));
|
|
287
|
+
}
|
|
288
|
+
class r {
|
|
289
|
+
constructor(t, n = ne.ZBAR_CFG_ENABLE, a = 1) {
|
|
290
|
+
this.symbolType = t, this.configType = n, this.number = a, this.configSteps = [this];
|
|
291
|
+
}
|
|
292
|
+
static register(t, n, a = n.symbolType) {
|
|
293
|
+
return r.formatsToConfigs[t] = n, r.typesToFormats[a] = r.typesToFormats[a] || t, n;
|
|
294
|
+
}
|
|
295
|
+
static formats() {
|
|
296
|
+
return Object.keys(r.formatsToConfigs);
|
|
297
|
+
}
|
|
298
|
+
static toFormat(t) {
|
|
299
|
+
return r.typesToFormats[t];
|
|
300
|
+
}
|
|
301
|
+
static configure(t, n) {
|
|
302
|
+
var a;
|
|
303
|
+
(a = r.formatsToConfigs[n]) === null || a === void 0 || a.configSteps.forEach(((s) => t.setConfig(s.symbolType, s.configType, s.number)));
|
|
304
|
+
}
|
|
305
|
+
add(t) {
|
|
306
|
+
return this.configSteps.push(t), this;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
var te;
|
|
310
|
+
r.formatsToConfigs = {}, r.typesToFormats = {}, r.register("codabar", new r(c.ZBAR_CODABAR)), r.register("code_39", new r(c.ZBAR_CODE39)), r.register("code_93", new r(c.ZBAR_CODE93)), r.register("code_128", new r(c.ZBAR_CODE128)), r.register("databar", new r(c.ZBAR_DATABAR)), r.register("databar_exp", new r(c.ZBAR_DATABAR_EXP)), r.register("ean_2", new r(c.ZBAR_EAN2)), r.register("ean_5", new r(c.ZBAR_EAN5)), r.register("ean_8", new r(c.ZBAR_EAN8)), r.register("ean_13", new r(c.ZBAR_EAN13)), r.register("ean_13+2", new r(c.ZBAR_EAN13)).add(new r(c.ZBAR_EAN2)), r.register("ean_13+5", new r(c.ZBAR_EAN13)).add(new r(c.ZBAR_EAN5)), r.register("isbn_10", new r(c.ZBAR_ISBN10)).add(new r(c.ZBAR_EAN13)), r.register("isbn_13", new r(c.ZBAR_ISBN13)).add(new r(c.ZBAR_EAN13)), r.register("isbn_13+2", new r(c.ZBAR_ISBN13)).add(new r(c.ZBAR_EAN13)).add(new r(c.ZBAR_EAN2)), r.register("isbn_13+5", new r(c.ZBAR_ISBN13)).add(new r(c.ZBAR_EAN13)).add(new r(c.ZBAR_EAN5)), r.register("itf", new r(c.ZBAR_I25)), r.register("qr_code", new r(c.ZBAR_QRCODE)), r.register("sq_code", new r(c.ZBAR_SQCODE)), r.register("upc_a", new r(c.ZBAR_UPCA)).add(new r(c.ZBAR_EAN13)), r.register("upc_e", new r(c.ZBAR_UPCE)).add(new r(c.ZBAR_EAN13)), (function(l) {
|
|
311
|
+
l[l.UNKNOWN = -1] = "UNKNOWN", l[l.UPRIGHT = 0] = "UPRIGHT", l[l.ROTATED_RIGHT = 1] = "ROTATED_RIGHT", l[l.UPSIDE_DOWN = 2] = "UPSIDE_DOWN", l[l.ROTATED_LEFT = 3] = "ROTATED_LEFT";
|
|
312
|
+
})(te || (te = {}));
|
|
313
|
+
class Ee {
|
|
314
|
+
}
|
|
315
|
+
const Re = (() => {
|
|
316
|
+
try {
|
|
317
|
+
return new OffscreenCanvas(1, 1).getContext("2d") instanceof OffscreenCanvasRenderingContext2D;
|
|
318
|
+
} catch {
|
|
319
|
+
return !1;
|
|
320
|
+
}
|
|
321
|
+
})();
|
|
322
|
+
class T {
|
|
323
|
+
constructor(t = {}) {
|
|
324
|
+
if (t.formats !== void 0) {
|
|
325
|
+
if (!Array.isArray(t.formats) || !t.formats.length) throw new TypeError(`Barcode formats should be a non-empty array of strings but are: ${JSON.stringify(t)}`);
|
|
326
|
+
const n = t.formats.filter(((a) => !r.formats().includes(a)));
|
|
327
|
+
if (n.length) throw new TypeError(`Unsupported barcode format(s): ${n.join(", ")}`);
|
|
328
|
+
}
|
|
329
|
+
this.formats = t.formats || r.formats(), this.zbarConfig = t.zbar || new Ee();
|
|
330
|
+
}
|
|
331
|
+
static getSupportedFormats() {
|
|
332
|
+
return Promise.resolve(r.formats());
|
|
333
|
+
}
|
|
334
|
+
detect(t) {
|
|
335
|
+
T.validate(t);
|
|
336
|
+
const n = T.intrinsicDimensions(t);
|
|
337
|
+
if (n.width === 0 || n.height === 0) return Promise.resolve([]);
|
|
338
|
+
try {
|
|
339
|
+
return Promise.all([this.toImageData(t), this.getScanner()]).then(((a) => {
|
|
340
|
+
const s = a[0], i = a[1];
|
|
341
|
+
return this.zbarConfig.enableCache !== void 0 && i.enableCache(this.zbarConfig.enableCache), ue(s.data, s.width, s.height, i);
|
|
342
|
+
})).then(((a) => a.map(((s) => this.toBarcodeDetectorResult(s)))));
|
|
343
|
+
} catch (a) {
|
|
344
|
+
return Promise.reject(a);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
getScanner() {
|
|
348
|
+
return new Promise(((t, n) => ge(this, void 0, void 0, (function* () {
|
|
349
|
+
if (!this.scanner) {
|
|
350
|
+
const a = yield fe();
|
|
351
|
+
this.formats.length > 0 && (a.setConfig(c.ZBAR_NONE, ne.ZBAR_CFG_ENABLE, 0), this.formats.forEach(((s) => r.configure(a, s)))), this.scanner = a;
|
|
352
|
+
}
|
|
353
|
+
t(this.scanner);
|
|
354
|
+
}))));
|
|
355
|
+
}
|
|
356
|
+
toImageData(t) {
|
|
357
|
+
const n = (a) => {
|
|
358
|
+
const s = T.intrinsicDimensions(a);
|
|
359
|
+
this.canvas && this.canvas.width === s.width && this.canvas.height === s.height || (this.canvas = (function(v, _) {
|
|
360
|
+
if (Re) return new OffscreenCanvas(v, _);
|
|
361
|
+
{
|
|
362
|
+
const g = document.createElement("canvas");
|
|
363
|
+
return g.width = v, g.height = _, g;
|
|
364
|
+
}
|
|
365
|
+
})(s.width, s.height));
|
|
366
|
+
const i = this.canvas, R = i.getContext("2d");
|
|
367
|
+
return R.drawImage(a, 0, 0), R.getImageData(0, 0, i.width, i.height);
|
|
368
|
+
};
|
|
369
|
+
if (t instanceof ImageData) return Promise.resolve(t);
|
|
370
|
+
if (t instanceof Blob) {
|
|
371
|
+
if (typeof createImageBitmap == "function") return createImageBitmap(t).then(((a) => n(a)));
|
|
372
|
+
{
|
|
373
|
+
const a = document.createElement("img");
|
|
374
|
+
return a.src = URL.createObjectURL(t), a.decode().then((() => n(a))).finally((() => URL.revokeObjectURL(a.src)));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return T.isRenderingContext(t) ? Promise.resolve(t.getImageData(0, 0, t.canvas.width, t.canvas.height)) : Promise.resolve(n(t));
|
|
378
|
+
}
|
|
379
|
+
toBarcodeDetectorResult(t) {
|
|
380
|
+
const n = { minX: 1 / 0, maxX: -1 / 0, minY: 1 / 0, maxY: -1 / 0 };
|
|
381
|
+
return t.points.forEach(((a) => {
|
|
382
|
+
n.minX = Math.min(n.minX, a.x), n.maxX = Math.max(n.maxX, a.x), n.minY = Math.min(n.minY, a.y), n.maxY = Math.max(n.maxY, a.y);
|
|
383
|
+
})), { format: r.toFormat(t.type), rawValue: t.decode(this.zbarConfig.encoding), orientation: t.orientation, quality: t.quality, boundingBox: DOMRectReadOnly.fromRect({ x: n.minX, y: n.minY, width: n.maxX - n.minX, height: n.maxY - n.minY }), cornerPoints: [{ x: n.minX, y: n.minY }, { x: n.maxX, y: n.minY }, { x: n.maxX, y: n.maxY }, { x: n.minX, y: n.maxY }] };
|
|
384
|
+
}
|
|
385
|
+
static validate(t) {
|
|
386
|
+
if (!T.isPolyfillImageBitmapSource(t)) throw new TypeError("BarcodeDetector.detect() argument is not an ImageBitmapSource");
|
|
387
|
+
if (typeof HTMLImageElement < "u" && t instanceof HTMLImageElement && !t.complete) throw new DOMException(`Invalid HTMLImageElement.complete state: ${t.complete}`, "InvalidStateError");
|
|
388
|
+
if (typeof HTMLVideoElement < "u" && t instanceof HTMLVideoElement && [HTMLMediaElement.HAVE_NOTHING, HTMLMediaElement.HAVE_METADATA].includes(t.readyState)) throw new DOMException(`Invalid HTMLVideoElement.readyState: ${t.readyState}`, "InvalidStateError");
|
|
389
|
+
return !0;
|
|
390
|
+
}
|
|
391
|
+
static isRenderingContext(t) {
|
|
392
|
+
return typeof CanvasRenderingContext2D < "u" && t instanceof CanvasRenderingContext2D || typeof OffscreenCanvasRenderingContext2D < "u" && t instanceof OffscreenCanvasRenderingContext2D;
|
|
393
|
+
}
|
|
394
|
+
static isPolyfillImageBitmapSource(t) {
|
|
395
|
+
return typeof HTMLImageElement < "u" && t instanceof HTMLImageElement || typeof HTMLVideoElement < "u" && t instanceof HTMLVideoElement || typeof HTMLCanvasElement < "u" && t instanceof HTMLCanvasElement || typeof SVGImageElement < "u" && t instanceof SVGImageElement || typeof ImageBitmap < "u" && t instanceof ImageBitmap || typeof OffscreenCanvas < "u" && t instanceof OffscreenCanvas || typeof VideoFrame < "u" && t instanceof VideoFrame || T.isRenderingContext(t) || t instanceof ImageData || t instanceof Blob || t && (t.width == 0 || t.height == 0);
|
|
396
|
+
}
|
|
397
|
+
static intrinsicDimensions(t) {
|
|
398
|
+
return T.isRenderingContext(t) && (t = t.canvas), { width: Number(t.naturalWidth || t.videoWidth || t.codedWidth || t.clientWidth || t.width), height: Number(t.naturalHeight || t.videoHeight || t.codedHeight || t.clientHeight || t.height) };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const re = window.BarcodeDetector || T, _e = ({
|
|
402
|
+
onCodesDetected: l,
|
|
403
|
+
codeStatuses: t = /* @__PURE__ */ new Map(),
|
|
404
|
+
scanInterval: n = 600,
|
|
405
|
+
className: a,
|
|
406
|
+
style: s
|
|
407
|
+
}) => {
|
|
408
|
+
const i = q(null), R = q(null), [v, _] = z(!0), [g, p] = z("");
|
|
409
|
+
J(() => {
|
|
410
|
+
if (!re) {
|
|
411
|
+
_(!1), p("Barcode Detection is not supported and polyfill failed to load.");
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
return (async () => {
|
|
415
|
+
try {
|
|
416
|
+
const f = await navigator.mediaDevices.getUserMedia({
|
|
417
|
+
video: {
|
|
418
|
+
facingMode: "environment",
|
|
419
|
+
width: { ideal: 1280 },
|
|
420
|
+
height: { ideal: 720 }
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
i.current && (i.current.srcObject = f);
|
|
424
|
+
} catch (f) {
|
|
425
|
+
console.error("Error accessing webcam:", f), p("Could not access webcam. Please verify permissions.");
|
|
426
|
+
}
|
|
427
|
+
})(), () => {
|
|
428
|
+
i.current && i.current.srcObject && i.current.srcObject.getTracks().forEach((E) => E.stop());
|
|
429
|
+
};
|
|
430
|
+
}, []), J(() => {
|
|
431
|
+
if (!v || !i.current || !R.current) return;
|
|
432
|
+
const w = new re({ formats: ["qr_code"] });
|
|
433
|
+
let f, E = 0;
|
|
434
|
+
const h = async (A) => {
|
|
435
|
+
if (i.current && i.current.readyState === i.current.HAVE_ENOUGH_DATA && A - E >= n) {
|
|
436
|
+
E = A;
|
|
437
|
+
try {
|
|
438
|
+
const y = await w.detect(i.current);
|
|
439
|
+
l(y), O(y);
|
|
440
|
+
} catch (y) {
|
|
441
|
+
console.error("Barcode detection error:", y);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
f = requestAnimationFrame(h);
|
|
445
|
+
};
|
|
446
|
+
return f = requestAnimationFrame(h), () => cancelAnimationFrame(f);
|
|
447
|
+
}, [v, n, l, t]);
|
|
448
|
+
const O = (w) => {
|
|
449
|
+
const f = i.current, E = R.current;
|
|
450
|
+
if (!f || !E) return;
|
|
451
|
+
const h = E.getContext("2d");
|
|
452
|
+
h && ((E.width !== f.videoWidth || E.height !== f.videoHeight) && (E.width = f.videoWidth, E.height = f.videoHeight), h.clearRect(0, 0, E.width, E.height), w.forEach((A) => {
|
|
453
|
+
const y = A.rawValue, b = t.get(y);
|
|
454
|
+
let x = "#FFFFFF";
|
|
455
|
+
b === "processing" && (x = "#FFFF00"), b === "success" && (x = "#00FF00"), b === "error" && (x = "#FF0000"), h.beginPath(), A.cornerPoints.forEach((C, F) => {
|
|
456
|
+
F === 0 ? h.moveTo(C.x, C.y) : h.lineTo(C.x, C.y);
|
|
457
|
+
}), h.closePath(), h.lineWidth = 4, h.strokeStyle = x, h.stroke();
|
|
458
|
+
const D = A.cornerPoints[0], j = b ? `[${b.toUpperCase()}]` : "[NEW]";
|
|
459
|
+
h.font = "bold 16px Arial", h.fillStyle = x, h.fillText(`${y} ${j}`, D.x, D.y - 10);
|
|
460
|
+
}));
|
|
461
|
+
};
|
|
462
|
+
return /* @__PURE__ */ P.jsxs("div", { className: a, style: { position: "relative", width: "100%", maxWidth: "800px", margin: "0 auto", ...s }, children: [
|
|
463
|
+
g && /* @__PURE__ */ P.jsx("div", { style: { color: "red", marginBottom: "10px" }, children: g }),
|
|
464
|
+
/* @__PURE__ */ P.jsxs("div", { style: { position: "relative" }, children: [
|
|
465
|
+
/* @__PURE__ */ P.jsx(
|
|
466
|
+
"video",
|
|
467
|
+
{
|
|
468
|
+
ref: i,
|
|
469
|
+
autoPlay: !0,
|
|
470
|
+
playsInline: !0,
|
|
471
|
+
muted: !0,
|
|
472
|
+
style: { width: "100%", display: "block" }
|
|
473
|
+
}
|
|
474
|
+
),
|
|
475
|
+
/* @__PURE__ */ P.jsx(
|
|
476
|
+
"canvas",
|
|
477
|
+
{
|
|
478
|
+
ref: R,
|
|
479
|
+
style: {
|
|
480
|
+
position: "absolute",
|
|
481
|
+
top: 0,
|
|
482
|
+
left: 0,
|
|
483
|
+
width: "100%",
|
|
484
|
+
height: "100%",
|
|
485
|
+
pointerEvents: "none"
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
)
|
|
489
|
+
] })
|
|
490
|
+
] });
|
|
491
|
+
};
|
|
492
|
+
export {
|
|
493
|
+
_e as default
|
|
494
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
(function(T,o){typeof exports=="object"&&typeof module<"u"?module.exports=o(require("react"),require("https://cdn.jsdelivr.net/npm/@undecaf/zbar-wasm@0.9.16/dist/main.js")):typeof define=="function"&&define.amd?define(["react","https://cdn.jsdelivr.net/npm/@undecaf/zbar-wasm@0.9.16/dist/main.js"],o):(T=typeof globalThis<"u"?globalThis:T||self,T.MultiQRScanner=o(T.React,T.main_js))})(this,(function(T,o){"use strict";var D={exports:{}},Z={};var V;function te(){if(V)return Z;V=1;var l=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function n(a,c,s){var g=null;if(s!==void 0&&(g=""+s),c.key!==void 0&&(g=""+c.key),"key"in c){s={};for(var R in c)R!=="key"&&(s[R]=c[R])}else s=c;return c=s.ref,{$$typeof:l,type:a,key:g,ref:c!==void 0?c:null,props:s}}return Z.Fragment=t,Z.jsx=n,Z.jsxs=n,Z}var N={};var X;function re(){return X||(X=1,process.env.NODE_ENV!=="production"&&(function(){function l(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===le?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case v:return"Fragment";case B:return"Profiler";case w:return"StrictMode";case O:return"Suspense";case M:return"SuspenseList";case ce:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case y:return"Portal";case I:return e.displayName||"Context";case _:return(e._context.displayName||"Context")+".Consumer";case F:var i=e.render;return e=e.displayName,e||(e=i.displayName||i.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case se:return i=e.displayName||null,i!==null?i:l(e.type)||"Memo";case L:i=e._payload,e=e._init;try{return l(e(i))}catch{}}return null}function t(e){return""+e}function n(e){try{t(e);var i=!1}catch{i=!0}if(i){i=console;var u=i.error,d=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return u.call(i,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",d),t(e)}}function a(e){if(e===v)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===L)return"<...>";try{var i=l(e);return i?"<"+i+">":"<...>"}catch{return"<...>"}}function c(){var e=Y.A;return e===null?null:e.getOwner()}function s(){return Error("react-stack-top-frame")}function g(e){if(q.call(e,"key")){var i=Object.getOwnPropertyDescriptor(e,"key").get;if(i&&i.isReactWarning)return!1}return e.key!==void 0}function R(e,i){function u(){$||($=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",i))}u.isReactWarning=!0,Object.defineProperty(e,"key",{get:u,configurable:!0})}function b(){var e=l(this.type);return J[e]||(J[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function p(e,i,u,d,k,U){var m=u.ref;return e={$$typeof:h,type:e,key:i,props:u,_owner:d},(m!==void 0?m:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:b}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:k}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:U}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function E(e,i,u,d,k,U){var m=i.children;if(m!==void 0)if(d)if(ue(m)){for(d=0;d<m.length;d++)x(m[d]);Object.freeze&&Object.freeze(m)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else x(m);if(q.call(i,"key")){m=l(e);var C=Object.keys(i).filter(function(fe){return fe!=="key"});d=0<C.length?"{key: someKey, "+C.join(": ..., ")+": ...}":"{key: someKey}",ee[m+d]||(C=0<C.length?"{"+C.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
|
|
2
|
+
let props = %s;
|
|
3
|
+
<%s {...props} />
|
|
4
|
+
React keys must be passed directly to JSX without using spread:
|
|
5
|
+
let props = %s;
|
|
6
|
+
<%s key={someKey} {...props} />`,d,m,C,m),ee[m+d]=!0)}if(m=null,u!==void 0&&(n(u),m=""+u),g(i)&&(n(i.key),m=""+i.key),"key"in i){u={};for(var W in i)W!=="key"&&(u[W]=i[W])}else u=i;return m&&R(u,typeof e=="function"?e.displayName||e.name||"Unknown":e),p(e,m,u,c(),k,U)}function x(e){S(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===L&&(e._payload.status==="fulfilled"?S(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function S(e){return typeof e=="object"&&e!==null&&e.$$typeof===h}var f=T,h=Symbol.for("react.transitional.element"),y=Symbol.for("react.portal"),v=Symbol.for("react.fragment"),w=Symbol.for("react.strict_mode"),B=Symbol.for("react.profiler"),_=Symbol.for("react.consumer"),I=Symbol.for("react.context"),F=Symbol.for("react.forward_ref"),O=Symbol.for("react.suspense"),M=Symbol.for("react.suspense_list"),se=Symbol.for("react.memo"),L=Symbol.for("react.lazy"),ce=Symbol.for("react.activity"),le=Symbol.for("react.client.reference"),Y=f.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,q=Object.prototype.hasOwnProperty,ue=Array.isArray,H=console.createTask?console.createTask:function(){return null};f={react_stack_bottom_frame:function(e){return e()}};var $,J={},Q=f.react_stack_bottom_frame.bind(f,s)(),K=H(a(s)),ee={};N.Fragment=v,N.jsx=function(e,i,u){var d=1e4>Y.recentlyCreatedOwnerStacks++;return E(e,i,u,!1,d?Error("react-stack-top-frame"):Q,d?H(a(e)):K)},N.jsxs=function(e,i,u){var d=1e4>Y.recentlyCreatedOwnerStacks++;return E(e,i,u,!0,d?Error("react-stack-top-frame"):Q,d?H(a(e)):K)}})()),N}var j;function ne(){return j||(j=1,process.env.NODE_ENV==="production"?D.exports=te():D.exports=re()),D.exports}var P=ne();function ae(l,t,n,a){return new(n||(n=Promise))((function(c,s){function g(p){try{b(a.next(p))}catch(E){s(E)}}function R(p){try{b(a.throw(p))}catch(E){s(E)}}function b(p){var E;p.done?c(p.value):(E=p.value,E instanceof n?E:new n((function(x){x(E)}))).then(g,R)}b((a=a.apply(l,[])).next())}))}class r{constructor(t,n=o.ZBarConfigType.ZBAR_CFG_ENABLE,a=1){this.symbolType=t,this.configType=n,this.number=a,this.configSteps=[this]}static register(t,n,a=n.symbolType){return r.formatsToConfigs[t]=n,r.typesToFormats[a]=r.typesToFormats[a]||t,n}static formats(){return Object.keys(r.formatsToConfigs)}static toFormat(t){return r.typesToFormats[t]}static configure(t,n){var a;(a=r.formatsToConfigs[n])===null||a===void 0||a.configSteps.forEach((c=>t.setConfig(c.symbolType,c.configType,c.number)))}add(t){return this.configSteps.push(t),this}}var G;r.formatsToConfigs={},r.typesToFormats={},r.register("codabar",new r(o.ZBarSymbolType.ZBAR_CODABAR)),r.register("code_39",new r(o.ZBarSymbolType.ZBAR_CODE39)),r.register("code_93",new r(o.ZBarSymbolType.ZBAR_CODE93)),r.register("code_128",new r(o.ZBarSymbolType.ZBAR_CODE128)),r.register("databar",new r(o.ZBarSymbolType.ZBAR_DATABAR)),r.register("databar_exp",new r(o.ZBarSymbolType.ZBAR_DATABAR_EXP)),r.register("ean_2",new r(o.ZBarSymbolType.ZBAR_EAN2)),r.register("ean_5",new r(o.ZBarSymbolType.ZBAR_EAN5)),r.register("ean_8",new r(o.ZBarSymbolType.ZBAR_EAN8)),r.register("ean_13",new r(o.ZBarSymbolType.ZBAR_EAN13)),r.register("ean_13+2",new r(o.ZBarSymbolType.ZBAR_EAN13)).add(new r(o.ZBarSymbolType.ZBAR_EAN2)),r.register("ean_13+5",new r(o.ZBarSymbolType.ZBAR_EAN13)).add(new r(o.ZBarSymbolType.ZBAR_EAN5)),r.register("isbn_10",new r(o.ZBarSymbolType.ZBAR_ISBN10)).add(new r(o.ZBarSymbolType.ZBAR_EAN13)),r.register("isbn_13",new r(o.ZBarSymbolType.ZBAR_ISBN13)).add(new r(o.ZBarSymbolType.ZBAR_EAN13)),r.register("isbn_13+2",new r(o.ZBarSymbolType.ZBAR_ISBN13)).add(new r(o.ZBarSymbolType.ZBAR_EAN13)).add(new r(o.ZBarSymbolType.ZBAR_EAN2)),r.register("isbn_13+5",new r(o.ZBarSymbolType.ZBAR_ISBN13)).add(new r(o.ZBarSymbolType.ZBAR_EAN13)).add(new r(o.ZBarSymbolType.ZBAR_EAN5)),r.register("itf",new r(o.ZBarSymbolType.ZBAR_I25)),r.register("qr_code",new r(o.ZBarSymbolType.ZBAR_QRCODE)),r.register("sq_code",new r(o.ZBarSymbolType.ZBAR_SQCODE)),r.register("upc_a",new r(o.ZBarSymbolType.ZBAR_UPCA)).add(new r(o.ZBarSymbolType.ZBAR_EAN13)),r.register("upc_e",new r(o.ZBarSymbolType.ZBAR_UPCE)).add(new r(o.ZBarSymbolType.ZBAR_EAN13)),(function(l){l[l.UNKNOWN=-1]="UNKNOWN",l[l.UPRIGHT=0]="UPRIGHT",l[l.ROTATED_RIGHT=1]="ROTATED_RIGHT",l[l.UPSIDE_DOWN=2]="UPSIDE_DOWN",l[l.ROTATED_LEFT=3]="ROTATED_LEFT"})(G||(G={}));class oe{}const ie=(()=>{try{return new OffscreenCanvas(1,1).getContext("2d")instanceof OffscreenCanvasRenderingContext2D}catch{return!1}})();class A{constructor(t={}){if(t.formats!==void 0){if(!Array.isArray(t.formats)||!t.formats.length)throw new TypeError(`Barcode formats should be a non-empty array of strings but are: ${JSON.stringify(t)}`);const n=t.formats.filter((a=>!r.formats().includes(a)));if(n.length)throw new TypeError(`Unsupported barcode format(s): ${n.join(", ")}`)}this.formats=t.formats||r.formats(),this.zbarConfig=t.zbar||new oe}static getSupportedFormats(){return Promise.resolve(r.formats())}detect(t){A.validate(t);const n=A.intrinsicDimensions(t);if(n.width===0||n.height===0)return Promise.resolve([]);try{return Promise.all([this.toImageData(t),this.getScanner()]).then((a=>{const c=a[0],s=a[1];return this.zbarConfig.enableCache!==void 0&&s.enableCache(this.zbarConfig.enableCache),o.scanRGBABuffer(c.data,c.width,c.height,s)})).then((a=>a.map((c=>this.toBarcodeDetectorResult(c)))))}catch(a){return Promise.reject(a)}}getScanner(){return new Promise(((t,n)=>ae(this,void 0,void 0,(function*(){if(!this.scanner){const a=yield o.getDefaultScanner();this.formats.length>0&&(a.setConfig(o.ZBarSymbolType.ZBAR_NONE,o.ZBarConfigType.ZBAR_CFG_ENABLE,0),this.formats.forEach((c=>r.configure(a,c)))),this.scanner=a}t(this.scanner)}))))}toImageData(t){const n=a=>{const c=A.intrinsicDimensions(a);this.canvas&&this.canvas.width===c.width&&this.canvas.height===c.height||(this.canvas=(function(R,b){if(ie)return new OffscreenCanvas(R,b);{const p=document.createElement("canvas");return p.width=R,p.height=b,p}})(c.width,c.height));const s=this.canvas,g=s.getContext("2d");return g.drawImage(a,0,0),g.getImageData(0,0,s.width,s.height)};if(t instanceof ImageData)return Promise.resolve(t);if(t instanceof Blob){if(typeof createImageBitmap=="function")return createImageBitmap(t).then((a=>n(a)));{const a=document.createElement("img");return a.src=URL.createObjectURL(t),a.decode().then((()=>n(a))).finally((()=>URL.revokeObjectURL(a.src)))}}return A.isRenderingContext(t)?Promise.resolve(t.getImageData(0,0,t.canvas.width,t.canvas.height)):Promise.resolve(n(t))}toBarcodeDetectorResult(t){const n={minX:1/0,maxX:-1/0,minY:1/0,maxY:-1/0};return t.points.forEach((a=>{n.minX=Math.min(n.minX,a.x),n.maxX=Math.max(n.maxX,a.x),n.minY=Math.min(n.minY,a.y),n.maxY=Math.max(n.maxY,a.y)})),{format:r.toFormat(t.type),rawValue:t.decode(this.zbarConfig.encoding),orientation:t.orientation,quality:t.quality,boundingBox:DOMRectReadOnly.fromRect({x:n.minX,y:n.minY,width:n.maxX-n.minX,height:n.maxY-n.minY}),cornerPoints:[{x:n.minX,y:n.minY},{x:n.maxX,y:n.minY},{x:n.maxX,y:n.maxY},{x:n.minX,y:n.maxY}]}}static validate(t){if(!A.isPolyfillImageBitmapSource(t))throw new TypeError("BarcodeDetector.detect() argument is not an ImageBitmapSource");if(typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement&&!t.complete)throw new DOMException(`Invalid HTMLImageElement.complete state: ${t.complete}`,"InvalidStateError");if(typeof HTMLVideoElement<"u"&&t instanceof HTMLVideoElement&&[HTMLMediaElement.HAVE_NOTHING,HTMLMediaElement.HAVE_METADATA].includes(t.readyState))throw new DOMException(`Invalid HTMLVideoElement.readyState: ${t.readyState}`,"InvalidStateError");return!0}static isRenderingContext(t){return typeof CanvasRenderingContext2D<"u"&&t instanceof CanvasRenderingContext2D||typeof OffscreenCanvasRenderingContext2D<"u"&&t instanceof OffscreenCanvasRenderingContext2D}static isPolyfillImageBitmapSource(t){return typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement||typeof HTMLVideoElement<"u"&&t instanceof HTMLVideoElement||typeof HTMLCanvasElement<"u"&&t instanceof HTMLCanvasElement||typeof SVGImageElement<"u"&&t instanceof SVGImageElement||typeof ImageBitmap<"u"&&t instanceof ImageBitmap||typeof OffscreenCanvas<"u"&&t instanceof OffscreenCanvas||typeof VideoFrame<"u"&&t instanceof VideoFrame||A.isRenderingContext(t)||t instanceof ImageData||t instanceof Blob||t&&(t.width==0||t.height==0)}static intrinsicDimensions(t){return A.isRenderingContext(t)&&(t=t.canvas),{width:Number(t.naturalWidth||t.videoWidth||t.codedWidth||t.clientWidth||t.width),height:Number(t.naturalHeight||t.videoHeight||t.codedHeight||t.clientHeight||t.height)}}}const z=window.BarcodeDetector||A;return({onCodesDetected:l,codeStatuses:t=new Map,scanInterval:n=600,className:a,style:c})=>{const s=T.useRef(null),g=T.useRef(null),[R,b]=T.useState(!0),[p,E]=T.useState("");T.useEffect(()=>{if(!z){b(!1),E("Barcode Detection is not supported and polyfill failed to load.");return}return(async()=>{try{const f=await navigator.mediaDevices.getUserMedia({video:{facingMode:"environment",width:{ideal:1280},height:{ideal:720}}});s.current&&(s.current.srcObject=f)}catch(f){console.error("Error accessing webcam:",f),E("Could not access webcam. Please verify permissions.")}})(),()=>{s.current&&s.current.srcObject&&s.current.srcObject.getTracks().forEach(h=>h.stop())}},[]),T.useEffect(()=>{if(!R||!s.current||!g.current)return;const S=new z({formats:["qr_code"]});let f,h=0;const y=async v=>{if(s.current&&s.current.readyState===s.current.HAVE_ENOUGH_DATA&&v-h>=n){h=v;try{const w=await S.detect(s.current);l(w),x(w)}catch(w){console.error("Barcode detection error:",w)}}f=requestAnimationFrame(y)};return f=requestAnimationFrame(y),()=>cancelAnimationFrame(f)},[R,n,l,t]);const x=S=>{const f=s.current,h=g.current;if(!f||!h)return;const y=h.getContext("2d");y&&((h.width!==f.videoWidth||h.height!==f.videoHeight)&&(h.width=f.videoWidth,h.height=f.videoHeight),y.clearRect(0,0,h.width,h.height),S.forEach(v=>{const w=v.rawValue,B=t.get(w);let _="#FFFFFF";B==="processing"&&(_="#FFFF00"),B==="success"&&(_="#00FF00"),B==="error"&&(_="#FF0000"),y.beginPath(),v.cornerPoints.forEach((O,M)=>{M===0?y.moveTo(O.x,O.y):y.lineTo(O.x,O.y)}),y.closePath(),y.lineWidth=4,y.strokeStyle=_,y.stroke();const I=v.cornerPoints[0],F=B?`[${B.toUpperCase()}]`:"[NEW]";y.font="bold 16px Arial",y.fillStyle=_,y.fillText(`${w} ${F}`,I.x,I.y-10)}))};return P.jsxs("div",{className:a,style:{position:"relative",width:"100%",maxWidth:"800px",margin:"0 auto",...c},children:[p&&P.jsx("div",{style:{color:"red",marginBottom:"10px"},children:p}),P.jsxs("div",{style:{position:"relative"},children:[P.jsx("video",{ref:s,autoPlay:!0,playsInline:!0,muted:!0,style:{width:"100%",display:"block"}}),P.jsx("canvas",{ref:g,style:{position:"absolute",top:0,left:0,width:"100%",height:"100%",pointerEvents:"none"}})]})]})}}));
|
package/dist/vite.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "multi-qr-scanner-poc",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/multi-qr-scanner.umd.cjs",
|
|
7
|
+
"module": "./dist/multi-qr-scanner.js",
|
|
8
|
+
"types": "./dist/components/MultiQRScanner.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/multi-qr-scanner.js",
|
|
12
|
+
"require": "./dist/multi-qr-scanner.umd.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "vite",
|
|
20
|
+
"build": "tsc -b && vite build",
|
|
21
|
+
"build:lib": "tsc -b && vite build --config vite.lib.config.ts",
|
|
22
|
+
"lint": "eslint .",
|
|
23
|
+
"preview": "vite preview"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": "^19.2.0",
|
|
27
|
+
"react-dom": "^19.2.0"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@undecaf/barcode-detector-polyfill": "^0.9.23",
|
|
31
|
+
"@undecaf/zbar-wasm": "^0.11.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@eslint/js": "^9.39.1",
|
|
35
|
+
"@types/node": "^24.10.1",
|
|
36
|
+
"@types/react": "^19.2.5",
|
|
37
|
+
"@types/react-dom": "^19.2.3",
|
|
38
|
+
"@vitejs/plugin-react": "^5.1.1",
|
|
39
|
+
"eslint": "^9.39.1",
|
|
40
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
41
|
+
"eslint-plugin-react-refresh": "^0.4.24",
|
|
42
|
+
"globals": "^16.5.0",
|
|
43
|
+
"typescript": "~5.9.3",
|
|
44
|
+
"typescript-eslint": "^8.46.4",
|
|
45
|
+
"vite": "^7.2.4",
|
|
46
|
+
"vite-plugin-dts": "^4.5.4",
|
|
47
|
+
"react": "^19.2.0",
|
|
48
|
+
"react-dom": "^19.2.0"
|
|
49
|
+
}
|
|
50
|
+
}
|