persagy-vant 0.0.52 → 0.0.54

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.
Files changed (57) hide show
  1. package/es/icon/index.css +1 -1
  2. package/es/icon/latest-iconfont.css +1 -1
  3. package/es/icon/latest-iconfont.less +2 -14
  4. package/es/icon/svg-icon-masks.css +1 -0
  5. package/es/icon/svg-icon-masks.less +18 -0
  6. package/es/index.d.ts +1 -1
  7. package/es/index.mjs +1 -1
  8. package/es/locale/lang/en-US.d.ts +9 -0
  9. package/es/locale/lang/en-US.mjs +9 -0
  10. package/es/locale/lang/zh-CN.d.ts +9 -0
  11. package/es/locale/lang/zh-CN.mjs +9 -0
  12. package/es/locale/lang/zh-TW.d.ts +9 -0
  13. package/es/locale/lang/zh-TW.mjs +9 -0
  14. package/es/qr-scanner/QrScanner.d.ts +18 -1
  15. package/es/qr-scanner/QrScanner.mjs +141 -18
  16. package/es/qr-scanner/decoder.d.ts +3 -3
  17. package/es/qr-scanner/decoder.mjs +99 -27
  18. package/es/qr-scanner/function-call.d.ts +13 -1
  19. package/es/qr-scanner/function-call.mjs +5 -0
  20. package/es/qr-scanner/index.d.ts +1 -1
  21. package/es/qr-scanner/startup.d.ts +3 -0
  22. package/es/qr-scanner/startup.mjs +33 -0
  23. package/es/qr-scanner/types.d.ts +16 -1
  24. package/lib/{decoder-8f5b00db.js → decoder-03a4b162.js} +112 -34
  25. package/lib/{decoder-a36ba9ae.mjs → decoder-9637718d.mjs} +112 -34
  26. package/lib/icon/index.css +1 -1
  27. package/lib/icon/latest-iconfont.css +1 -1
  28. package/lib/icon/latest-iconfont.less +2 -14
  29. package/lib/icon/svg-icon-masks.css +1 -0
  30. package/lib/icon/svg-icon-masks.less +18 -0
  31. package/lib/index-d68a26ab.mjs +22968 -0
  32. package/lib/index-ea574b22.js +22967 -0
  33. package/lib/index.css +1 -1
  34. package/lib/index.d.ts +1 -1
  35. package/lib/index.js +1 -1
  36. package/lib/locale/lang/en-US.d.ts +9 -0
  37. package/lib/locale/lang/en-US.js +9 -0
  38. package/lib/locale/lang/zh-CN.d.ts +9 -0
  39. package/lib/locale/lang/zh-CN.js +9 -0
  40. package/lib/locale/lang/zh-TW.d.ts +9 -0
  41. package/lib/locale/lang/zh-TW.js +9 -0
  42. package/lib/qr-scanner/QrScanner.d.ts +18 -1
  43. package/lib/qr-scanner/QrScanner.js +141 -18
  44. package/lib/qr-scanner/decoder.d.ts +3 -3
  45. package/lib/qr-scanner/decoder.js +94 -26
  46. package/lib/qr-scanner/function-call.d.ts +13 -1
  47. package/lib/qr-scanner/function-call.js +5 -0
  48. package/lib/qr-scanner/index.d.ts +1 -1
  49. package/lib/qr-scanner/startup.d.ts +3 -0
  50. package/lib/qr-scanner/startup.js +52 -0
  51. package/lib/qr-scanner/types.d.ts +16 -1
  52. package/lib/vant.cjs.js +131 -22802
  53. package/lib/vant.es.js +131 -22802
  54. package/lib/vant.js +372 -137
  55. package/lib/vant.min.js +1 -1
  56. package/lib/web-types.json +1 -1
  57. package/package.json +6 -4
@@ -26,12 +26,16 @@ import { Button } from "../button/index.mjs";
26
26
  import { Icon } from "../icon/index.mjs";
27
27
  import { Loading } from "../loading/index.mjs";
28
28
  import { Popup } from "../popup/index.mjs";
29
- const [name, bem] = createNamespace("qr-scanner");
29
+ import zhCN from "../locale/lang/zh-CN.mjs";
30
+ import { DEFAULT_STARTUP_TIMEOUT, waitForAbort } from "./startup.mjs";
31
+ const [name, bem, t] = createNamespace("qr-scanner");
30
32
  const qrScannerProps = {
31
33
  show: Boolean,
32
34
  title: makeStringProp("\u626B\u63CF\u8BBE\u5907\u4E8C\u7EF4\u7801"),
33
35
  tip: makeStringProp("\u5C06\u8BBE\u5907\u4E8C\u7EF4\u7801\u7F6E\u4E8E\u53D6\u666F\u6846\u5185\uFF0C\u5373\u53EF\u81EA\u52A8\u8BC6\u522B"),
34
36
  processingText: makeStringProp("\u6B63\u5728\u5904\u7406\u626B\u7801\u7ED3\u679C..."),
37
+ startupTimeout: makeNumericProp(DEFAULT_STARTUP_TIMEOUT),
38
+ startupTexts: Object,
35
39
  formats: extend(makeArrayProp(), {
36
40
  default: () => ["QRCode"]
37
41
  }),
@@ -57,7 +61,7 @@ const qrScannerProps = {
57
61
  var stdin_default = defineComponent({
58
62
  name,
59
63
  props: qrScannerProps,
60
- emits: ["update:show", "scan", "error", "cancel", "close", "closed", "ready"],
64
+ emits: ["update:show", "scan", "error", "cancel", "close", "closed", "ready", "stageChange"],
61
65
  setup(props, {
62
66
  emit
63
67
  }) {
@@ -66,6 +70,7 @@ var stdin_default = defineComponent({
66
70
  const frame = ref();
67
71
  const fileInput = ref();
68
72
  const phase = ref("idle");
73
+ const startupStage = ref("camera");
69
74
  const message = ref("");
70
75
  const hasTorch = ref(false);
71
76
  const torchOn = ref(false);
@@ -76,13 +81,25 @@ var stdin_default = defineComponent({
76
81
  let stream;
77
82
  let canvas;
78
83
  let timer;
84
+ let startupTimer;
85
+ let startupStartedAt = 0;
86
+ let startupDeadline = 0;
79
87
  let generation = 0;
80
88
  let abortController;
81
89
  let resumeWhenVisible = false;
90
+ let pickingAlbum = false;
82
91
  const recentResults = /* @__PURE__ */ new Map();
83
92
  const isCurrent = (id) => id === generation && props.show && !props.paused;
93
+ const startupText = (key) => t(key) || zhCN.vanQrScanner[key];
94
+ const clearStartupTimer = () => {
95
+ if (startupTimer !== void 0)
96
+ clearTimeout(startupTimer);
97
+ startupTimer = void 0;
98
+ startupDeadline = 0;
99
+ };
84
100
  const release = () => {
85
101
  generation += 1;
102
+ clearStartupTimer();
86
103
  if (timer)
87
104
  clearTimeout(timer);
88
105
  timer = void 0;
@@ -101,6 +118,7 @@ var stdin_default = defineComponent({
101
118
  phase.value = "paused";
102
119
  };
103
120
  const close = (cancelled = false) => {
121
+ pickingAlbum = false;
104
122
  release();
105
123
  phase.value = "idle";
106
124
  if (cancelled)
@@ -108,14 +126,46 @@ var stdin_default = defineComponent({
108
126
  emit("update:show", false);
109
127
  };
110
128
  const reportError = (code, text, cause) => {
129
+ const startupInfo = phase.value === "starting" && abortController && !props.nativeScan ? {
130
+ stage: startupStage.value,
131
+ elapsedMs: Math.max(0, Date.now() - startupStartedAt)
132
+ } : {};
111
133
  release();
112
134
  phase.value = "error";
113
135
  message.value = text;
114
- emit("error", {
136
+ emit("error", extend({
115
137
  code,
116
138
  message: text,
117
139
  cause
140
+ }, startupInfo));
141
+ };
142
+ const reportStartupTimeout = () => {
143
+ const timeoutMessages = {
144
+ camera: startupText("timeoutCamera"),
145
+ preview: startupText("timeoutPreview"),
146
+ decoder: startupText("timeoutDecoder"),
147
+ ready: ""
148
+ };
149
+ reportError("STARTUP_TIMEOUT", timeoutMessages[startupStage.value]);
150
+ };
151
+ const canContinueStartup = (id) => {
152
+ if (!isCurrent(id))
153
+ return false;
154
+ if (startupDeadline && Date.now() >= startupDeadline) {
155
+ reportStartupTimeout();
156
+ return false;
157
+ }
158
+ return true;
159
+ };
160
+ const setStartupStage = (stage, id) => {
161
+ if (!canContinueStartup(id))
162
+ return false;
163
+ startupStage.value = stage;
164
+ emit("stageChange", {
165
+ stage,
166
+ elapsedMs: Math.max(0, Date.now() - startupStartedAt)
118
167
  });
168
+ return isCurrent(id);
119
169
  };
120
170
  const cameraError = (error) => {
121
171
  const errorName = error == null ? void 0 : error.name;
@@ -203,7 +253,7 @@ var stdin_default = defineComponent({
203
253
  height: target.height / scale
204
254
  });
205
255
  if (image) {
206
- const result = yield decoder.decode(image, props.formats, props.wasmUrl);
256
+ const result = yield decoder.decode(image, props.formats, props.wasmUrl, abortController == null ? void 0 : abortController.signal);
207
257
  if (!isCurrent(id))
208
258
  return;
209
259
  if (result)
@@ -229,6 +279,7 @@ var stdin_default = defineComponent({
229
279
  return;
230
280
  }
231
281
  const id = generation;
282
+ pickingAlbum = false;
232
283
  phase.value = "starting";
233
284
  message.value = "";
234
285
  if (props.nativeScan) {
@@ -264,8 +315,25 @@ var stdin_default = defineComponent({
264
315
  reportError("CAMERA_UNSUPPORTED", "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u5B9E\u65F6\u626B\u7801\uFF0C\u8BF7\u4ECE\u76F8\u518C\u8BC6\u522B\u6216\u5728\u7CFB\u7EDF\u6D4F\u89C8\u5668\u6253\u5F00");
265
316
  return;
266
317
  }
318
+ abortController = new AbortController();
319
+ const {
320
+ signal
321
+ } = abortController;
322
+ startupStartedAt = Date.now();
323
+ const configuredTimeout = Number(props.startupTimeout);
324
+ const timeout = Number.isFinite(configuredTimeout) ? Math.max(0, configuredTimeout) : DEFAULT_STARTUP_TIMEOUT;
325
+ if (timeout > 0) {
326
+ startupDeadline = startupStartedAt + timeout;
327
+ startupTimer = setTimeout(() => {
328
+ if (!isCurrent(id) || phase.value !== "starting")
329
+ return;
330
+ reportStartupTimeout();
331
+ }, timeout);
332
+ }
333
+ if (!setStartupStage("camera", id))
334
+ return;
267
335
  try {
268
- const acquiredStream = yield navigator.mediaDevices.getUserMedia({
336
+ const acquiredStream = yield waitForAbort(navigator.mediaDevices.getUserMedia({
269
337
  audio: false,
270
338
  video: extend(deviceId ? {
271
339
  deviceId: {
@@ -283,28 +351,44 @@ var stdin_default = defineComponent({
283
351
  ideal: 720
284
352
  }
285
353
  })
286
- });
354
+ }).then((acquired) => {
355
+ if (!isCurrent(id))
356
+ acquired.getTracks().forEach((track2) => track2.stop());
357
+ return acquired;
358
+ }), signal);
287
359
  if (!isCurrent(id)) {
288
360
  acquiredStream.getTracks().forEach((track2) => track2.stop());
289
361
  return;
290
362
  }
291
363
  stream = acquiredStream;
364
+ } catch (error) {
365
+ if (isCurrent(id))
366
+ cameraError(error);
367
+ return;
368
+ }
369
+ if (!setStartupStage("preview", id))
370
+ return;
371
+ try {
292
372
  yield nextTick();
293
- if (!isCurrent(id))
373
+ if (!canContinueStartup(id))
294
374
  return;
295
375
  if (!video.value)
296
376
  throw new Error("\u626B\u7801\u9884\u89C8\u5C1A\u672A\u5C31\u7EEA");
297
377
  video.value.srcObject = stream;
298
- yield video.value.play();
378
+ yield waitForAbort(video.value.play(), signal);
299
379
  } catch (error) {
300
380
  if (isCurrent(id))
301
- cameraError(error);
381
+ reportError("PREVIEW_FAILED", startupText("previewFailed"), error);
302
382
  return;
303
383
  }
384
+ if (!setStartupStage("decoder", id))
385
+ return;
304
386
  let decoder;
305
387
  try {
306
- decoder = yield import("./decoder");
307
- yield decoder.prepareDecoder(props.wasmUrl);
388
+ decoder = yield waitForAbort(import("./decoder"), signal);
389
+ if (!canContinueStartup(id))
390
+ return;
391
+ yield waitForAbort(decoder.prepareDecoder(props.wasmUrl, signal), signal);
308
392
  } catch (error) {
309
393
  if (isCurrent(id))
310
394
  reportError("DECODER_LOAD_FAILED", "\u626B\u7801\u8D44\u6E90\u52A0\u8F7D\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u8D44\u6E90\u90E8\u7F72\u540E\u91CD\u8BD5", error);
@@ -312,6 +396,8 @@ var stdin_default = defineComponent({
312
396
  }
313
397
  if (!isCurrent(id))
314
398
  return;
399
+ if (!canContinueStartup(id))
400
+ return;
315
401
  const track = stream == null ? void 0 : stream.getVideoTracks()[0];
316
402
  try {
317
403
  const capability = (_b = track == null ? void 0 : track.getCapabilities) == null ? void 0 : _b.call(track).torch;
@@ -333,6 +419,9 @@ var stdin_default = defineComponent({
333
419
  }).catch(() => {
334
420
  });
335
421
  phase.value = "scanning";
422
+ clearStartupTimer();
423
+ if (!setStartupStage("ready", id))
424
+ return;
336
425
  emit("ready", {
337
426
  torch: hasTorch.value
338
427
  });
@@ -342,25 +431,32 @@ var stdin_default = defineComponent({
342
431
  const scanImage = (file) => __async(this, null, function* () {
343
432
  if (!props.show || props.paused)
344
433
  return;
434
+ pickingAlbum = false;
435
+ release();
436
+ phase.value = "processing";
437
+ message.value = "";
345
438
  if (file.size > Number(props.maxImageSize)) {
346
439
  reportError("IMAGE_TOO_LARGE", "\u56FE\u7247\u8FC7\u5927\uFF0C\u8BF7\u9009\u62E9\u8F83\u5C0F\u7684\u56FE\u7247");
347
440
  return;
348
441
  }
349
- release();
350
442
  const id = generation;
351
- phase.value = "processing";
352
- message.value = "";
443
+ abortController = new AbortController();
444
+ const {
445
+ signal
446
+ } = abortController;
353
447
  let imageCanvas;
354
448
  try {
355
- const decoder = yield import("./decoder");
356
- const image = yield decoder.loadImage(file);
449
+ const decoder = yield waitForAbort(import("./decoder"), signal);
450
+ if (!isCurrent(id))
451
+ return;
452
+ const image = yield waitForAbort(decoder.loadImage(file, signal), signal);
357
453
  if (!isCurrent(id))
358
454
  return;
359
455
  imageCanvas = document.createElement("canvas");
360
456
  const data = decoder.captureImage(image, imageCanvas, 1920);
361
457
  if (!data)
362
458
  throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u56FE\u7247");
363
- const result = yield decoder.decode(data, props.formats, props.wasmUrl);
459
+ const result = yield waitForAbort(decoder.decode(data, props.formats, props.wasmUrl, signal), signal);
364
460
  if (!isCurrent(id))
365
461
  return;
366
462
  if (!result) {
@@ -429,6 +525,15 @@ var stdin_default = defineComponent({
429
525
  start();
430
526
  }
431
527
  };
528
+ const onAlbumCancel = () => {
529
+ if (pickingAlbum && phase.value === "paused")
530
+ start();
531
+ pickingAlbum = false;
532
+ };
533
+ watch(fileInput, (input, previous) => {
534
+ previous == null ? void 0 : previous.removeEventListener("cancel", onAlbumCancel);
535
+ input == null ? void 0 : input.addEventListener("cancel", onAlbumCancel);
536
+ });
432
537
  watch(() => props.show, (show) => __async(this, null, function* () {
433
538
  release();
434
539
  recentResults.clear();
@@ -455,9 +560,15 @@ var stdin_default = defineComponent({
455
560
  if (props.show)
456
561
  start();
457
562
  });
563
+ watch(() => props.wasmUrl, () => {
564
+ if (props.show && !props.nativeScan && (phase.value === "starting" || phase.value === "scanning"))
565
+ start();
566
+ });
458
567
  onMounted(() => document.addEventListener("visibilitychange", onVisibilityChange));
459
568
  onBeforeUnmount(() => {
569
+ var _a;
460
570
  release();
571
+ (_a = fileInput.value) == null ? void 0 : _a.removeEventListener("cancel", onAlbumCancel);
461
572
  if (canvas) {
462
573
  canvas.width = 0;
463
574
  canvas.height = 0;
@@ -542,7 +653,15 @@ var stdin_default = defineComponent({
542
653
  "color": "white",
543
654
  "vertical": true
544
655
  }, {
545
- default: () => [phase.value === "starting" ? props.nativeScan ? "\u6B63\u5728\u6253\u5F00\u626B\u7801..." : "\u6B63\u5728\u542F\u52A8\u6444\u50CF\u5934..." : props.processingText]
656
+ default: () => {
657
+ var _a, _b, _c, _d, _e, _f;
658
+ return [phase.value === "starting" ? props.nativeScan ? "\u6B63\u5728\u6253\u5F00\u626B\u7801..." : {
659
+ camera: (_b = (_a = props.startupTexts) == null ? void 0 : _a.camera) != null ? _b : startupText("startupCamera"),
660
+ preview: (_d = (_c = props.startupTexts) == null ? void 0 : _c.preview) != null ? _d : startupText("startupPreview"),
661
+ decoder: (_f = (_e = props.startupTexts) == null ? void 0 : _e.decoder) != null ? _f : startupText("startupDecoder"),
662
+ ready: ""
663
+ }[startupStage.value] : props.processingText];
664
+ }
546
665
  }), needsRetry && _createVNode(_Fragment, null, [_createVNode("p", {
547
666
  "class": bem("message")
548
667
  }, [message.value || "\u626B\u63CF\u5DF2\u6682\u505C"]), _createVNode(Button, {
@@ -575,6 +694,7 @@ var stdin_default = defineComponent({
575
694
  var _a;
576
695
  stop();
577
696
  resumeWhenVisible = false;
697
+ pickingAlbum = true;
578
698
  (_a = fileInput.value) == null ? void 0 : _a.click();
579
699
  }
580
700
  }, [_createVNode(Icon, {
@@ -604,6 +724,9 @@ var stdin_default = defineComponent({
604
724
  input.value = "";
605
725
  if (file)
606
726
  scanImage(file);
727
+ else if (pickingAlbum && phase.value === "paused")
728
+ start();
729
+ pickingAlbum = false;
607
730
  }
608
731
  }, null)]
609
732
  });
@@ -1,7 +1,7 @@
1
1
  import type { QrScannerFormat } from './types';
2
- export declare function prepareDecoder(wasmUrl?: string): Promise<void>;
3
- export declare function decode(image: ImageData, formats: QrScannerFormat[], wasmUrl?: string): Promise<import("zxing-wasm/reader").ReadResult | undefined>;
4
- export declare function loadImage(file: File): Promise<HTMLImageElement>;
2
+ export declare function prepareDecoder(wasmUrl?: string, signal?: AbortSignal): Promise<void>;
3
+ export declare function decode(image: ImageData, formats: QrScannerFormat[], wasmUrl?: string, signal?: AbortSignal): Promise<import("zxing-wasm/reader").ReadResult | undefined>;
4
+ export declare function loadImage(file: File, signal?: AbortSignal): Promise<HTMLImageElement>;
5
5
  export declare function captureImage(source: HTMLVideoElement | HTMLImageElement, canvas: HTMLCanvasElement, maxSize?: number, region?: {
6
6
  x: number;
7
7
  y: number;
@@ -18,48 +18,120 @@ var __async = (__this, __arguments, generator) => {
18
18
  step((generator = generator.apply(__this, __arguments)).next());
19
19
  });
20
20
  };
21
- import { prepareZXingModule, readBarcodes } from "zxing-wasm/reader";
22
- let ready;
23
- let activeWasmUrl;
24
- function prepareDecoder(wasmUrl) {
25
- return __async(this, null, function* () {
21
+ import {
22
+ prepareZXingModule,
23
+ purgeZXingModule,
24
+ readBarcodes
25
+ } from "zxing-wasm/reader";
26
+ import { abortError, waitForAbort } from "./startup.mjs";
27
+ let preparation;
28
+ function invalidate(entry) {
29
+ if (preparation === entry) {
30
+ preparation = void 0;
31
+ purgeZXingModule();
32
+ }
33
+ }
34
+ function createPreparation(wasmUrl) {
35
+ const controller = new AbortController();
36
+ const entry = {
37
+ wasmUrl,
38
+ pending: true,
39
+ controller,
40
+ consumers: /* @__PURE__ */ new Set(),
41
+ promise: void 0
42
+ };
43
+ const initialize = () => __async(this, null, function* () {
26
44
  const url = wasmUrl || (yield import("zxing-wasm/reader/zxing_reader.wasm?url")).default;
27
- if (!ready || url !== activeWasmUrl) {
28
- activeWasmUrl = url;
29
- ready = Promise.resolve(
30
- prepareZXingModule({
31
- overrides: { locateFile: () => url },
32
- fireImmediately: true
33
- })
34
- ).catch((error) => {
35
- ready = void 0;
36
- throw error;
37
- });
45
+ if (controller.signal.aborted || preparation !== entry)
46
+ throw abortError();
47
+ yield prepareZXingModule({
48
+ overrides: { locateFile: () => url },
49
+ fireImmediately: true
50
+ });
51
+ });
52
+ entry.promise = waitForAbort(
53
+ Promise.resolve().then(initialize),
54
+ controller.signal
55
+ ).then(
56
+ () => {
57
+ entry.pending = false;
58
+ },
59
+ (error) => {
60
+ entry.pending = false;
61
+ invalidate(entry);
62
+ throw error;
63
+ }
64
+ );
65
+ return entry;
66
+ }
67
+ function prepareDecoder(wasmUrl, signal) {
68
+ return __async(this, null, function* () {
69
+ if (signal == null ? void 0 : signal.aborted)
70
+ throw abortError();
71
+ if (!preparation || preparation.wasmUrl !== wasmUrl) {
72
+ preparation = createPreparation(wasmUrl);
73
+ }
74
+ const entry = preparation;
75
+ const consumer = Symbol("decoder-consumer");
76
+ entry.consumers.add(consumer);
77
+ const releaseConsumer = () => {
78
+ entry.consumers.delete(consumer);
79
+ if (entry.pending && !entry.consumers.size) {
80
+ invalidate(entry);
81
+ entry.controller.abort();
82
+ }
83
+ };
84
+ signal == null ? void 0 : signal.addEventListener("abort", releaseConsumer, { once: true });
85
+ try {
86
+ yield waitForAbort(entry.promise, signal);
87
+ } finally {
88
+ signal == null ? void 0 : signal.removeEventListener("abort", releaseConsumer);
89
+ releaseConsumer();
38
90
  }
39
- yield ready;
40
91
  });
41
92
  }
42
- function decode(image, formats, wasmUrl) {
93
+ function decode(image, formats, wasmUrl, signal) {
43
94
  return __async(this, null, function* () {
44
- yield prepareDecoder(wasmUrl);
45
- const results = yield readBarcodes(image, {
46
- formats: formats.length ? formats : ["QRCode"],
47
- tryHarder: true,
48
- maxNumberOfSymbols: 1
49
- });
95
+ yield prepareDecoder(wasmUrl, signal);
96
+ if (signal == null ? void 0 : signal.aborted)
97
+ throw abortError();
98
+ const results = yield waitForAbort(
99
+ readBarcodes(image, {
100
+ formats: formats.length ? formats : ["QRCode"],
101
+ tryHarder: true,
102
+ maxNumberOfSymbols: 1
103
+ }),
104
+ signal
105
+ );
50
106
  return results.find((result) => result.isValid && result.text.length > 0);
51
107
  });
52
108
  }
53
- function loadImage(file) {
109
+ function loadImage(file, signal) {
54
110
  return new Promise((resolve, reject) => {
111
+ if (signal == null ? void 0 : signal.aborted) {
112
+ reject(abortError());
113
+ return;
114
+ }
55
115
  const url = URL.createObjectURL(file);
56
116
  const image = new Image();
57
- image.onload = () => {
117
+ const cleanup = () => {
58
118
  URL.revokeObjectURL(url);
119
+ signal == null ? void 0 : signal.removeEventListener("abort", onAbort);
120
+ image.onload = null;
121
+ image.onerror = null;
122
+ };
123
+ const onAbort = () => {
124
+ cleanup();
125
+ image.src = "";
126
+ reject(abortError());
127
+ };
128
+ signal == null ? void 0 : signal.addEventListener("abort", onAbort, { once: true });
129
+ image.onload = () => {
130
+ cleanup();
59
131
  resolve(image);
60
132
  };
61
133
  image.onerror = () => {
62
- URL.revokeObjectURL(url);
134
+ cleanup();
63
135
  reject(new Error("\u65E0\u6CD5\u8BFB\u53D6\u56FE\u7247\uFF0C\u8BF7\u9009\u62E9 JPG\u3001PNG \u7B49\u6D4F\u89C8\u5668\u652F\u6301\u7684\u56FE\u7247"));
64
136
  };
65
137
  image.src = url;
@@ -17,6 +17,11 @@ declare const QrScanner: {
17
17
  type: import("vue").PropType<string>;
18
18
  default: string;
19
19
  };
20
+ startupTimeout: {
21
+ type: (NumberConstructor | StringConstructor)[];
22
+ default: number;
23
+ };
24
+ startupTexts: import("vue").PropType<Partial<Record<"camera" | "preview" | "decoder", string>>>;
20
25
  formats: {
21
26
  type: import("vue").PropType<import("./types").QrScannerFormat[]>;
22
27
  default: () => never[];
@@ -68,7 +73,7 @@ declare const QrScanner: {
68
73
  type: BooleanConstructor;
69
74
  default: true;
70
75
  };
71
- }, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("close" | "error" | "closed" | "update:show" | "cancel" | "scan" | "ready")[], "close" | "error" | "closed" | "update:show" | "cancel" | "scan" | "ready", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
76
+ }, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("close" | "error" | "closed" | "update:show" | "cancel" | "scan" | "ready" | "stageChange")[], "close" | "error" | "closed" | "update:show" | "cancel" | "scan" | "ready" | "stageChange", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
72
77
  show: BooleanConstructor;
73
78
  title: {
74
79
  type: import("vue").PropType<string>;
@@ -82,6 +87,11 @@ declare const QrScanner: {
82
87
  type: import("vue").PropType<string>;
83
88
  default: string;
84
89
  };
90
+ startupTimeout: {
91
+ type: (NumberConstructor | StringConstructor)[];
92
+ default: number;
93
+ };
94
+ startupTexts: import("vue").PropType<Partial<Record<"camera" | "preview" | "decoder", string>>>;
85
95
  formats: {
86
96
  type: import("vue").PropType<import("./types").QrScannerFormat[]>;
87
97
  default: () => never[];
@@ -141,6 +151,7 @@ declare const QrScanner: {
141
151
  onCancel?: ((...args: any[]) => any) | undefined;
142
152
  onScan?: ((...args: any[]) => any) | undefined;
143
153
  onReady?: ((...args: any[]) => any) | undefined;
154
+ onStageChange?: ((...args: any[]) => any) | undefined;
144
155
  }, {
145
156
  title: string;
146
157
  show: boolean;
@@ -149,6 +160,7 @@ declare const QrScanner: {
149
160
  closeOnPopstate: boolean;
150
161
  tip: string;
151
162
  processingText: string;
163
+ startupTimeout: string | number;
152
164
  formats: import("./types").QrScannerFormat[];
153
165
  facingMode: "environment" | "user";
154
166
  showAlbum: boolean;
@@ -3,11 +3,14 @@ import { extend, inBrowser, withInstall } from "../utils/index.mjs";
3
3
  import { mountComponent, usePopupState } from "../utils/mount-component.mjs";
4
4
  import { useExpose } from "../composables/use-expose.mjs";
5
5
  import VanQrScanner from "./QrScanner.mjs";
6
+ import { DEFAULT_STARTUP_TIMEOUT } from "./startup.mjs";
6
7
  let instance;
7
8
  const defaultOptions = {
8
9
  title: "\u626B\u63CF\u8BBE\u5907\u4E8C\u7EF4\u7801",
9
10
  tip: "\u5C06\u8BBE\u5907\u4E8C\u7EF4\u7801\u7F6E\u4E8E\u53D6\u666F\u6846\u5185\uFF0C\u5373\u53EF\u81EA\u52A8\u8BC6\u522B",
10
11
  processingText: "\u6B63\u5728\u5904\u7406\u626B\u7801\u7ED3\u679C...",
12
+ startupTimeout: DEFAULT_STARTUP_TIMEOUT,
13
+ startupTexts: void 0,
11
14
  formats: ["QRCode"],
12
15
  facingMode: "environment",
13
16
  showAlbum: true,
@@ -26,6 +29,8 @@ const defaultOptions = {
26
29
  closeOnPopstate: true,
27
30
  onScan: void 0,
28
31
  onError: void 0,
32
+ onStageChange: void 0,
33
+ onReady: void 0,
29
34
  onCancel: void 0,
30
35
  onClose: void 0
31
36
  };
@@ -2,7 +2,7 @@ import { QrScanner } from './function-call';
2
2
  export default QrScanner;
3
3
  export { QrScanner };
4
4
  export type { QrScannerProps } from './QrScanner';
5
- export type { QrScannerBeforeConfirm, QrScannerError, QrScannerErrorCode, QrScannerExpose, QrScannerFormat, QrScannerInstance, QrScannerNativeScan, QrScannerOptions, QrScannerResult, } from './types';
5
+ export type { QrScannerBeforeConfirm, QrScannerError, QrScannerErrorCode, QrScannerExpose, QrScannerFormat, QrScannerInstance, QrScannerNativeScan, QrScannerOptions, QrScannerResult, QrScannerStageChange, QrScannerStartupStage, QrScannerStartupTexts, } from './types';
6
6
  declare module 'vue' {
7
7
  interface GlobalComponents {
8
8
  VanQrScanner: typeof QrScanner.Component;
@@ -0,0 +1,3 @@
1
+ export declare const DEFAULT_STARTUP_TIMEOUT = 15000;
2
+ export declare function abortError(): Error;
3
+ export declare function waitForAbort<T>(task: Promise<T>, signal?: AbortSignal): Promise<T>;
@@ -0,0 +1,33 @@
1
+ const DEFAULT_STARTUP_TIMEOUT = 15e3;
2
+ function abortError() {
3
+ const error = new Error("Scanner startup cancelled");
4
+ error.name = "AbortError";
5
+ return error;
6
+ }
7
+ function waitForAbort(task, signal) {
8
+ return new Promise((resolve, reject) => {
9
+ const onAbort = () => {
10
+ signal == null ? void 0 : signal.removeEventListener("abort", onAbort);
11
+ reject(abortError());
12
+ };
13
+ if (signal == null ? void 0 : signal.aborted)
14
+ onAbort();
15
+ else
16
+ signal == null ? void 0 : signal.addEventListener("abort", onAbort, { once: true });
17
+ task.then(
18
+ (value) => {
19
+ signal == null ? void 0 : signal.removeEventListener("abort", onAbort);
20
+ resolve(value);
21
+ },
22
+ (error) => {
23
+ signal == null ? void 0 : signal.removeEventListener("abort", onAbort);
24
+ reject(error);
25
+ }
26
+ );
27
+ });
28
+ }
29
+ export {
30
+ DEFAULT_STARTUP_TIMEOUT,
31
+ abortError,
32
+ waitForAbort
33
+ };
@@ -6,11 +6,20 @@ export declare type QrScannerResult = {
6
6
  format: string;
7
7
  source: 'camera' | 'image' | 'native';
8
8
  };
9
- export declare type QrScannerErrorCode = 'INSECURE_CONTEXT' | 'CAMERA_UNSUPPORTED' | 'CAMERA_PERMISSION_DENIED' | 'CAMERA_NOT_FOUND' | 'CAMERA_UNAVAILABLE' | 'DECODER_LOAD_FAILED' | 'DECODE_FAILED' | 'IMAGE_TOO_LARGE' | 'IMAGE_NOT_FOUND' | 'CONFIRM_FAILED' | 'NATIVE_SCAN_FAILED' | 'TORCH_UNAVAILABLE';
9
+ export declare type QrScannerStartupStage = 'camera' | 'preview' | 'decoder' | 'ready';
10
+ export declare type QrScannerStartupTexts = Partial<Record<Exclude<QrScannerStartupStage, 'ready'>, string>>;
11
+ export declare type QrScannerStageChange = {
12
+ stage: QrScannerStartupStage;
13
+ /** Milliseconds since this H5 startup attempt began. */
14
+ elapsedMs: number;
15
+ };
16
+ export declare type QrScannerErrorCode = 'INSECURE_CONTEXT' | 'CAMERA_UNSUPPORTED' | 'CAMERA_PERMISSION_DENIED' | 'CAMERA_NOT_FOUND' | 'CAMERA_UNAVAILABLE' | 'PREVIEW_FAILED' | 'STARTUP_TIMEOUT' | 'DECODER_LOAD_FAILED' | 'DECODE_FAILED' | 'IMAGE_TOO_LARGE' | 'IMAGE_NOT_FOUND' | 'CONFIRM_FAILED' | 'NATIVE_SCAN_FAILED' | 'TORCH_UNAVAILABLE';
10
17
  export declare type QrScannerError = {
11
18
  code: QrScannerErrorCode;
12
19
  message: string;
13
20
  cause?: unknown;
21
+ stage?: QrScannerStartupStage;
22
+ elapsedMs?: number;
14
23
  };
15
24
  export declare type QrScannerBeforeConfirm = (result: QrScannerResult) => boolean | void | Promise<boolean | void>;
16
25
  export declare type QrScannerNativeScan = (options: {
@@ -24,6 +33,8 @@ export declare type QrScannerOptions = {
24
33
  title?: string;
25
34
  tip?: string;
26
35
  processingText?: string;
36
+ startupTimeout?: number | string;
37
+ startupTexts?: QrScannerStartupTexts;
27
38
  formats?: QrScannerFormat[];
28
39
  facingMode?: 'environment' | 'user';
29
40
  showAlbum?: boolean;
@@ -42,6 +53,10 @@ export declare type QrScannerOptions = {
42
53
  closeOnPopstate?: boolean;
43
54
  onScan?: (result: QrScannerResult) => void;
44
55
  onError?: (error: QrScannerError) => void;
56
+ onStageChange?: (info: QrScannerStageChange) => void;
57
+ onReady?: (info: {
58
+ torch: boolean;
59
+ }) => void;
45
60
  onCancel?: () => void;
46
61
  onClose?: () => void;
47
62
  };