diff --git a/frappe/public/js/desk.bundle.js b/frappe/public/js/desk.bundle.js index 66eb72cda0..c4ecf67c4f 100644 --- a/frappe/public/js/desk.bundle.js +++ b/frappe/public/js/desk.bundle.js @@ -103,3 +103,4 @@ import "./frappe/ui/datatable.js"; import "./frappe/ui/driver.js"; import "./frappe/ui/plyr.js"; import "./frappe/barcode_scanner/index.js"; +import "./frappe/scanner"; diff --git a/frappe/public/js/frappe/scanner/index.js b/frappe/public/js/frappe/scanner/index.js new file mode 100644 index 0000000000..efc87f397b --- /dev/null +++ b/frappe/public/js/frappe/scanner/index.js @@ -0,0 +1,87 @@ +frappe.provide("frappe.ui"); + +frappe.ui.Scanner = class Scanner { + constructor(options) { + this.dialog = null; + this.handler = null; + this.options = options; + + if (!("multiple" in this.options)) { + this.options.multiple = false; + } + if (options.container) { + this.$scan_area = $(options.container); + this.scan_area_id = frappe.dom.set_unique_id(this.$scan_area); + } + if (options.dialog) { + this.dialog = this.make_dialog(); + this.dialog.show(); + } + } + + scan() { + this.load_lib().then(() => this.start_scan()); + } + + start_scan() { + if (!this.handler) { + this.handler = new Html5Qrcode(this.scan_area_id); + } + this.handler + .start( + { facingMode: "environment" }, + { fps: 10, qrbox: 250 }, + (decodedText, decodedResult) => { + if (this.options.on_scan) { + this.options.on_scan(decodedResult); + } + if (!this.options.multiple) { + this.stop_scan(); + } + }, + errorMessage => { + // parse error, ignore it. + } + ) + .catch(err => { + console.warn(errorMessage); + }); + } + + stop_scan() { + if (this.handler) { + this.handler.stop().then(() => { + this.$scan_area.empty(); + }); + } + } + + make_dialog() { + let dialog = new frappe.ui.Dialog({ + title: __("Scan QRCode"), + fields: [ + { + fieldtype: "HTML", + fieldname: "scan_area" + } + ], + on_page_show: () => { + this.$scan_area = dialog.get_field("scan_area").$wrapper; + this.$scan_area.addClass("barcode-scanner"); + this.scan_area_id = frappe.dom.set_unique_id(this.$scan_area); + this.scan(); + } + }); + return dialog; + } + + hide_dialog() { + this.dialog && this.dialog.hide(); + } + + load_lib() { + return frappe.require( + "/assets/frappe/node_modules/html5-qrcode/dist/html5-qrcode.min.js" + ); + } +}; diff --git a/package.json b/package.json index e84fa1b581..4cd13a7efc 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "frappe-gantt": "^0.5.0", "fuse.js": "^3.4.6", "highlight.js": "^10.4.1", + "html5-qrcode": "^2.0.11", "jquery": "3.5.0", "js-sha256": "^0.9.0", "jsbarcode": "^3.9.0", diff --git a/yarn.lock b/yarn.lock index f21f493aef..1d59c3f3c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2930,6 +2930,11 @@ hsla-regex@^1.0.0: resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= +html5-qrcode@^2.0.11: + version "2.0.11" + resolved "https://registry.yarnpkg.com/html5-qrcode/-/html5-qrcode-2.0.11.tgz#2cc5f63e767be53dd6c6d56b6c4f180a12aa8075" + integrity sha512-cCrVOK2yJGPGSTjchqRhkBJIrxvojEwF/pDKLNxmTH1wiAsVc61ZnIqyApIVyNfn5dKRFax70Qpr7pZwbUNiUw== + http-cache-semantics@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"