Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: frappe.ui.Scanner #13855

Merged
merged 10 commits into from Aug 11, 2021
3 changes: 2 additions & 1 deletion frappe/model/__init__.py
Expand Up @@ -72,7 +72,8 @@
'Email',
'Name',
'Phone',
'URL'
'URL',
'Barcode'
)

default_fields = (
Expand Down
4 changes: 4 additions & 0 deletions frappe/public/icons/timeless/symbol-defs.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frappe/public/js/desk.bundle.js
Expand Up @@ -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";
41 changes: 41 additions & 0 deletions frappe/public/js/frappe/form/controls/data.js
Expand Up @@ -67,6 +67,10 @@ frappe.ui.form.ControlData = class ControlData extends frappe.ui.form.ControlInp
if (this.df.options == 'URL') {
this.setup_url_field();
}

if (this.df.options == 'Barcode') {
this.setup_barcode_field();
}
}

setup_url_field() {
Expand Down Expand Up @@ -113,6 +117,43 @@ frappe.ui.form.ControlData = class ControlData extends frappe.ui.form.ControlInp
});
}

setup_barcode_field() {
this.$wrapper.find('.control-input').append(
`<span class="link-btn">
<a class="btn-open no-decoration" title="${__("Scan")}">
${frappe.utils.icon('scan', 'sm')}
</a>
</span>`
);

this.$scan_btn = this.$wrapper.find('.link-btn');

this.$input.on("focus", () => {
setTimeout(() => {
this.$scan_btn.toggle(true);
}, 500);
});

const me = this;
this.$scan_btn.on('click', 'a', () => {
new frappe.ui.Scanner({
dialog: true,
multiple: false,
on_scan(data) {
if (data && data.result && data.result.text) {
me.set_value(data.result.text);
}
}
});
});

this.$input.on("blur", () => {
setTimeout(() => {
this.$scan_btn.toggle(false);
}, 500);
});
}

bind_change_event() {
const change_handler = e => {
if (this.change) this.change(e);
Expand Down
101 changes: 101 additions & 0 deletions frappe/public/js/frappe/scanner/index.js
@@ -0,0 +1,101 @@
frappe.provide("frappe.ui");

frappe.ui.Scanner = class Scanner {
constructor(options) {
this.dialog = null;
this.handler = null;
this.options = options;
this.is_alive = false;

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); // eslint-disable-line
}
this.handler
.start(
{ facingMode: "environment" },
{ fps: 10, qrbox: 250 },
(decodedText, decodedResult) => {
if (this.options.on_scan) {
try {
this.options.on_scan(decodedResult);
} catch (error) {
console.error(error); // eslint-disable-line
}
}
if (!this.options.multiple) {
this.stop_scan();
this.hide_dialog();
}
},
errorMessage => { // eslint-disable-line
// parse error, ignore it.
}
)
.catch(err => {
this.is_alive = false;
this.hide_dialog();
console.error(err); // eslint-disable-line
});
this.is_alive = true;
}

stop_scan() {
if (this.handler && this.is_alive) {
this.handler.stop().then(() => {
this.is_alive = false;
this.$scan_area.empty();
this.hide_dialog();
});
}
}

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();
},
on_hide: () => {
this.stop_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"
);
}
};
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -37,6 +37,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",
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Expand Up @@ -3183,6 +3183,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"
Expand Down