diff --git a/assets/controllers/elements/nonprintable_char_input_controller.js b/assets/controllers/elements/nonprintable_char_input_controller.js deleted file mode 100644 index bd172f1b..00000000 --- a/assets/controllers/elements/nonprintable_char_input_controller.js +++ /dev/null @@ -1,106 +0,0 @@ -/* - * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). - * - * Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import {Controller} from "@hotwired/stimulus"; - -/** - * Purpose of this controller is to allow users to input non-printable characters like EOT, FS, etc. in a form field and submit them correctly with the form. - * The visible input field encodes non-printable characters via their Unicode Control picture representation, e.g. \n becomes ␊ and \t becomes ␉, so that they can be displayed in the input field without breaking the form submission. - * The actual value of the field, which is submitted with the form, is stored in a hidden input and contains the non-printable characters in their original form. - */ -export default class extends Controller { - - _hiddenInput; - - connect() { - this.element.addEventListener("input", this._update.bind(this)); - - // We use a hidden input to store the actual value of the field, which is submitted with the form. - // The visible input is just for user interaction and can contain non-printable characters, which are not allowed in the hidden input. - this._hiddenInput = document.createElement("input"); - this._hiddenInput.type = "hidden"; - this._hiddenInput.name = this.element.name; - this.element.removeAttribute("name"); - this.element.parentNode.insertBefore(this._hiddenInput, this.element.nextSibling); - - this.element.addEventListener("keypress", this._onKeyPress.bind(this)); - } - - /** - * Ensures that non-printable characters like EOT, FS, etc. gets added to the input value when the user types them - * @param event - * @private - */ - _onKeyPress(event) { - const ALLOWED_INPUT_CODES = [4, 28, 29, 30, 31]; //EOT, FS, GS, RS, US - - if (!ALLOWED_INPUT_CODES.includes(event.keyCode)) { - return; - } - - event.preventDefault(); - - const char = String.fromCharCode(event.keyCode); - this.element.value += char; - - this._update(); - - - } - - _update() { - //Chrome workaround: Remove a leading ∠ character (U+2220) that appears when the user types a non-printable character at the beginning of the input field. - if (this.element.value.startsWith("∠")) { - this.element.value = this.element.value.substring(1); - } - - // Remove non-printable characters from the input value and store them in the hidden input - const normalizedValue = this.decodeNonPrintableChars(this.element.value); - this._hiddenInput.value = normalizedValue; - - // Encode non-printable characters in the visible input to their Unicode Control picture representation - const encodedValue = this.encodeNonPrintableChars(normalizedValue); - if (encodedValue !== this.element.value) { - this.element.value = encodedValue; - } - } - - /** - * Encodes non-printable characters in the given string via their Unicode Control picture representation, e.g. \n becomes ␊ and \t becomes ␉. - * This allows us to display non-printable characters in the input field without breaking the form submission. - * @param str - */ - encodeNonPrintableChars(str) { - return str.replace(/[\x00-\x1F\x7F]/g, (char) => { - const code = char.charCodeAt(0); - return String.fromCharCode(0x2400 + code); - }); - } - - /** - * Decodes the Unicode Control picture representation of non-printable characters back to their original form, e.g. ␊ becomes \n and ␉ becomes \t. - * @param str - */ - decodeNonPrintableChars(str) { - return str.replace(/[\u2400-\u241F\u2421]/g, (char) => { - const code = char.charCodeAt(0) - 0x2400; - return String.fromCharCode(code); - }); - } -} diff --git a/assets/controllers/helpers/scan_special_char_controller.js b/assets/controllers/helpers/scan_special_char_controller.js deleted file mode 100644 index 154b2a94..00000000 --- a/assets/controllers/helpers/scan_special_char_controller.js +++ /dev/null @@ -1,136 +0,0 @@ -/* - * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). - * - * Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import { Controller } from "@hotwired/stimulus" - -/** - * This controller listens for a special non-printable character (SOH / ASCII 1) to be entered anywhere on the page, - * which is then used as a trigger to submit the following characters as a barcode / scan input. - */ -export default class extends Controller { - connect() { - // Optional: Log to confirm global attachment - console.log("Scanner listener active") - - this.isCapturing = false - this.buffer = "" - - window.addEventListener("keypress", this.handleKeydown.bind(this)) - } - - initialize() { - this.isCapturing = false - this.buffer = "" - this.timeoutId = null - } - - handleKeydown(event) { - - // Ignore if the user is typing in a form field - const isInput = ["INPUT", "TEXTAREA", "SELECT"].includes(event.target.tagName) || - event.target.isContentEditable; - if (isInput) return - - // 1. Detect Start of Header (SOH / Ctrl+A) - if (event.key === "\x01" || event.keyCode === 1) { - this.startCapturing(event) - return - } - - // 2. Process characters if in capture mode - if (this.isCapturing) { - this.resetTimeout() // Push the expiration back with every keypress - - if (event.key === "Enter" || event.keyCode === 13) { - - this.finishCapturing(event) - } else if (event.key.length === 1) { - this.buffer += event.key - } - } - } - - startCapturing(event) { - this.isCapturing = true - this.buffer = "" - this.resetTimeout() - event.preventDefault() - console.debug("Scan character detected. Capture started...") - } - - finishCapturing(event) { - event.preventDefault() - const data = this.buffer; - this.stopCapturing() - this.processCapture(data) - } - - stopCapturing() { - this.isCapturing = false - this.buffer = "" - if (this.timeoutId) clearTimeout(this.timeoutId) - console.debug("Capture cleared/finished.") - } - - resetTimeout() { - if (this.timeoutId) clearTimeout(this.timeoutId) - - this.timeoutId = setTimeout(() => { - if (this.isCapturing) { - console.warn("Capture timed out. Resetting buffer.") - this.stopCapturing() - } - }, 500) - } - - processCapture(data) { - if (!data) return - - console.debug("Captured scan data: " + data) - - const scanInput = document.getElementById("scan_dialog_input"); - if (scanInput) { //When we are on the scan dialog page, submit the form there - this._submitScanForm(data); - } else { //Otherwise use our own form (e.g. on the part list page) - this.element.querySelector("input[name='input']").value = data; - this.element.requestSubmit(); - } - - - } - - _submitScanForm(data) { - const scanInput = document.getElementById("scan_dialog_input"); - if (!scanInput) { - console.error("Scan input field not found!") - return; - } - - scanInput.value = data; - scanInput.dispatchEvent(new Event('input', { bubbles: true })); - - const form = document.getElementById("scan_dialog_form"); - if (!form) { - console.error("Scan form not found!") - return; - } - - form.requestSubmit(); - } -} diff --git a/assets/controllers/pages/barcode_scan_controller.js b/assets/controllers/pages/barcode_scan_controller.js index bdc9c78c..ae51e951 100644 --- a/assets/controllers/pages/barcode_scan_controller.js +++ b/assets/controllers/pages/barcode_scan_controller.js @@ -114,11 +114,7 @@ export default class extends Controller { // Mark as handled immediately (prevents spam even if callback fires repeatedly) this._lastDecodedText = normalized; - const input = document.getElementById('scan_dialog_input'); - input.value = decodedText; - //Trigger nonprintable char input controller to update the hidden input value - input.dispatchEvent(new Event('input', { bubbles: true })); - + document.getElementById('scan_dialog_input').value = decodedText; //Submit form document.getElementById('scan_dialog_form').requestSubmit(); } diff --git a/docs/usage/scanner.md b/docs/usage/scanner.md deleted file mode 100644 index 47b3feff..00000000 --- a/docs/usage/scanner.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: Barcode Scanner -layout: default -parent: Usage ---- - -# Barcode scanner - -When the user has the correct permission there will be a barcode scanner button in the navbar. -On this page you can either input a barcode code by hand, use an external barcode scanner, or use your devices camera to -scan a barcode. - -In info mode (when the "Info" toggle is enabled) you can scan a barcode and Part-DB will parse it and show information -about it. - -Without info mode, the barcode will directly redirect you to the corresponding page. - -### Barcode matching - -When you scan a barcode, Part-DB will try to match it to an existing part, part lot or storage location first. -For Part-DB generated barcodes, it will use the internal ID of a part. Alternatively you can also scan a barcode that contains the part's IPN. - -You can set a GTIN/EAN code in the part properties and Part-DB will open the part page when you scan the corresponding GTIN/EAN barcode. - -On a part lot you can under "Advanced" set a user barcode, that will redirect you to the part lot page when scanned. This allows to reuse -arbitrary existing barcodes that already exist on the part lots (for example, from the manufacturer) and link them to the part lot in Part-DB. - -Part-DB can also parse various distributor barcodes (for example from Digikey and Mouser) and will try to redirect you to the corresponding -part page based on the distributor part number in the barcode. - -### Part creation from barcodes -For certain barcodes Part-DB can automatically create a new part, when it cannot find a matching part. -Part-DB will try to retrieve the part information from an information provider and redirects you to the part creation page -with the retrieved information pre-filled. - -## Using an external barcode scanner - -Part-DB supports the use of external barcode scanners that emulate keyboard input. To use a barcode scanner with Part-DB, -simply connect the scanner to your computer and scan a barcode while the cursor is in a text field in Part-DB. -The scanned barcode will be entered into the text field as if you had typed it on the keyboard. - -In scanner fields, it will also try to insert special non-printable characters the scanner send via Alt + key combinations. -This is required for EIGP114 datamatrix codes. - -### Automatically redirect on barcode scanning - -If you configure your barcode scanner to send a (Start of heading, 0x01) non-printable character at the beginning -of the scanned barcode, Part-DB will automatically scan the barcode that comes afterward (and is ended with an enter key) -and redirects you to the corresponding page. -This allows you to quickly scan a barcode from anywhere in Part-DB without the need to first open the scanner page. -If an input field is focused, the barcode will be entered into the field as usual and no redirection will happen. diff --git a/src/Form/LabelSystem/ScanDialogType.php b/src/Form/LabelSystem/ScanDialogType.php index 5f9ce65f..cd1440c6 100644 --- a/src/Form/LabelSystem/ScanDialogType.php +++ b/src/Form/LabelSystem/ScanDialogType.php @@ -61,8 +61,6 @@ class ScanDialogType extends AbstractType 'attr' => [ 'autofocus' => true, 'id' => 'scan_dialog_input', - 'style' => 'font-family: var(--bs-font-monospace)', - 'data-controller' => 'elements--nonprintable-char-input', ], ]); diff --git a/templates/base.html.twig b/templates/base.html.twig index afc7a8bf..62f0ce53 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -2,7 +2,7 @@ @@ -73,17 +73,9 @@ {{ encore_entry_script_tags('webauthn_tfa') }} {% endblock %} - - -{# Listen for the special #} -{% if is_granted("@tools.label_scanner") %} -
- -
-{% endif %} - {% block body %}
@@ -129,13 +121,13 @@ {# Must be outside of the sidebar or it will be hidden too #}