@operato/input 10.29.27 → 10.29.28

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.
@@ -26,13 +26,16 @@ import { OxFormField } from './ox-form-field.js';
26
26
  * @fires KeyboardEvent#keydown - Dispatched when the Enter key is pressed (if not withoutEnter).
27
27
  *
28
28
  * @cssprop {String} --barcodescan-input-button-icon - Icon for the barcode scanning button.
29
+ * @cssprop {String} --barcodescan-input-button-icon-size - The icon's background-size (default: auto, its own 30x24).
30
+ * @cssprop {String} --barcodescan-input-button-width - The button's width, and the room the input keeps for it (default: 35px).
29
31
  *
30
32
  * @customElement
31
33
  */
32
34
  export declare class OxInputBarcode extends OxFormField {
33
35
  static styles: import("lit").CSSResult[];
34
36
  /**
35
- * Indicates whether barcode scanning is enabled.
37
+ * Whether this device has a camera to scan with - the camera button shows only then. Reflected, so a host can
38
+ * style itself by it (`ox-input-barcode[scannable]`) without asking the device again.
36
39
  * @property {Boolean} scannable
37
40
  */
38
41
  scannable?: boolean;
@@ -6,6 +6,7 @@ import '@operato/popup/ox-popup.js';
6
6
  import { css, html } from 'lit';
7
7
  import { customElement, property, query, state } from 'lit/decorators.js';
8
8
  import { scanImageData } from '@undecaf/zbar-wasm';
9
+ import { i18next } from '@operato/i18n';
9
10
  import { OxPopup } from '@operato/popup';
10
11
  import { formElementHeight } from '@operato/styles/form-element-heights.js';
11
12
  import { OxFormField } from './ox-form-field.js';
@@ -34,6 +35,8 @@ const barcodeIcon = `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAYBAMAA
34
35
  * @fires KeyboardEvent#keydown - Dispatched when the Enter key is pressed (if not withoutEnter).
35
36
  *
36
37
  * @cssprop {String} --barcodescan-input-button-icon - Icon for the barcode scanning button.
38
+ * @cssprop {String} --barcodescan-input-button-icon-size - The icon's background-size (default: auto, its own 30x24).
39
+ * @cssprop {String} --barcodescan-input-button-width - The button's width, and the room the input keeps for it (default: 35px).
37
40
  *
38
41
  * @customElement
39
42
  */
@@ -72,7 +75,7 @@ let OxInputBarcode = class OxInputBarcode extends OxFormField {
72
75
  font-size: var(--md-sys-typescale-label-large-size, 0.875rem);
73
76
 
74
77
  box-sizing: border-box;
75
- padding-right: 35px;
78
+ padding-right: var(--barcodescan-input-button-width, 35px);
76
79
  }
77
80
 
78
81
  input:focus {
@@ -83,10 +86,11 @@ let OxInputBarcode = class OxInputBarcode extends OxFormField {
83
86
  #scan-button {
84
87
  display: block;
85
88
  position: relative;
86
- margin-left: -35px;
87
- width: 35px;
89
+ margin-left: calc(-1 * var(--barcodescan-input-button-width, 35px));
90
+ width: var(--barcodescan-input-button-width, 35px);
88
91
  border: none;
89
92
  background: var(--barcodescan-input-button-icon) no-repeat center center;
93
+ background-size: var(--barcodescan-input-button-icon-size, auto);
90
94
  }
91
95
 
92
96
  #scan-button[hidden] {
@@ -96,15 +100,17 @@ let OxInputBarcode = class OxInputBarcode extends OxFormField {
96
100
  ]; }
97
101
  connectedCallback() {
98
102
  super.connectedCallback();
99
- if (navigator.mediaDevices) {
103
+ /*
104
+ * Whether to offer the camera button: ask which kinds of device there are, never open one. Opening the camera
105
+ * here asked for camera permission - and switched the camera on - wherever the field was merely drawn, so a
106
+ * field in an app's header asked on every page. Permission is asked when the person presses the button.
107
+ */
108
+ if (navigator.mediaDevices?.enumerateDevices) {
100
109
  ;
101
110
  (async () => {
102
111
  try {
103
- var stream = await navigator.mediaDevices.getUserMedia({ audio: false, video: { facingMode: 'environment' } });
104
- if (stream) {
105
- stream.getTracks().forEach(track => track.stop());
106
- this.scannable = true;
107
- }
112
+ const devices = await navigator.mediaDevices.enumerateDevices();
113
+ this.scannable = devices.some(device => device.kind === 'videoinput');
108
114
  }
109
115
  catch (e) {
110
116
  this.scannable = false;
@@ -121,9 +127,17 @@ let OxInputBarcode = class OxInputBarcode extends OxFormField {
121
127
  }
122
128
  render() {
123
129
  this.style.setProperty('--barcodescan-input-button-icon', `url(${barcodeIcon})`);
130
+ /*
131
+ * Name both controls. The input had no name and no placeholder - `placeholder` set by the caller never reached
132
+ * it - and the camera button was an empty button. Same rule as ox-input-search: the caller's placeholder is the
133
+ * name, "barcode" when there is none.
134
+ */
135
+ const scanLabel = i18next.t('button.scan-with-camera', { defaultValue: 'scan with camera' });
124
136
  return html `
125
137
  <input
126
138
  type="text"
139
+ aria-label=${this.placeholder || i18next.t('label.barcode', { defaultValue: 'barcode' })}
140
+ .placeholder=${this.placeholder || ''}
127
141
  .value=${this.value || ''}
128
142
  @change=${(e) => this.onInputChange(e)}
129
143
  @keydown=${(e) => this.onInputKeyDown(e)}
@@ -132,6 +146,8 @@ let OxInputBarcode = class OxInputBarcode extends OxFormField {
132
146
  <button
133
147
  ?hidden=${!this.scannable}
134
148
  id="scan-button"
149
+ aria-label=${scanLabel}
150
+ title=${scanLabel}
135
151
  @click=${(e) => {
136
152
  this.scan(e);
137
153
  }}
@@ -255,7 +271,7 @@ let OxInputBarcode = class OxInputBarcode extends OxFormField {
255
271
  }
256
272
  };
257
273
  __decorate([
258
- property({ type: Boolean, attribute: true })
274
+ property({ type: Boolean, attribute: true, reflect: true })
259
275
  ], OxInputBarcode.prototype, "scannable", void 0);
260
276
  __decorate([
261
277
  property({ attribute: 'without-enter', type: Boolean })
@@ -1 +1 @@
1
- {"version":3,"file":"ox-input-barcode.js","sourceRoot":"","sources":["../../src/ox-input-barcode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport '@operato/popup/ox-popup.js'\n\nimport { css, html } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\nimport { scanImageData } from '@undecaf/zbar-wasm'\n\nimport { OxPopup } from '@operato/popup'\nimport { formElementHeight } from '@operato/styles/form-element-heights.js'\n\nimport { OxFormField } from './ox-form-field.js'\nimport { InputStyles } from './input-styles.js'\n\nconst barcodeIcon = `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAYBAMAAAAfR1CMAAADKGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFNjM4RURDQkQ1OUExMUU5QkExMkQ4NUY3NkMxNzBFOSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFNjM4RURDQ0Q1OUExMUU5QkExMkQ4NUY3NkMxNzBFOSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkU2MzhFREM5RDU5QTExRTlCQTEyRDg1Rjc2QzE3MEU5IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkU2MzhFRENBRDU5QTExRTlCQTEyRDg1Rjc2QzE3MEU5Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+55pr/QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAAkUExURQAAAEdwTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEus/7UCWQwAAAALdFJOU9YAg3wKBFBDSz9PnvQNDgAAAE9JREFUGNNjEEQFDKLJSnCOklkgg9QUJFn3RgZhRyS+iCGDEIp2RSBfQICRkRGIgTSQL4jCF6ScvxsZYOFT2T50/6D7Fz080MMLPTzRwhsAHVspfelur08AAAAASUVORK5CYII=`\n\n/**\n * Custom input component for barcode scanning.\n *\n * This component provides a text input field and a barcode scanning button. Users can input text\n * manually or scan barcodes using the device camera. Supported barcode formats include:\n *\n * - Code-39\n * - Code-93\n * - Code-128\n * - Codabar\n * - Databar/Expanded\n * - EAN/GTIN-5/8/13\n * - ISBN-10/13\n * - ISBN-13+2\n * - ISBN-13+5\n * - ITF (Interleaved 2 of 5)\n * - QR Code\n * - UPC-A/E\n *\n * @fires CustomEvent#change - Dispatched when the input value changes.\n * @fires KeyboardEvent#keydown - Dispatched when the Enter key is pressed (if not withoutEnter).\n *\n * @cssprop {String} --barcodescan-input-button-icon - Icon for the barcode scanning button.\n *\n * @customElement\n */\n@customElement('ox-input-barcode')\nexport class OxInputBarcode extends OxFormField {\n static styles = [\n InputStyles,\n css`\n :host {\n display: flex;\n align-items: center;\n border: none;\n background-color: transparent;\n height: ${formElementHeight('--form-element-height-medium')};\n }\n\n * {\n align-self: stretch;\n }\n\n *:focus {\n outline: none;\n }\n\n input {\n flex: 1;\n width: 10px; /* intentionally width set */\n border: var(--ox-input-border, 1px solid var(--md-sys-color-outline));\n border-radius: var(--ox-input-radius, var(--md-sys-shape-corner-small));\n background-color: var(--ox-input-background, var(--md-sys-color-on-primary));\n color: var(--ox-input-color, var(--md-sys-color-on-primary-container));\n font-size: var(--md-sys-typescale-label-large-size, 0.875rem);\n\n box-sizing: border-box;\n padding-right: 35px;\n }\n\n input:focus {\n outline: none;\n border-color: var(--md-sys-color-secondary-fixed-dim);\n }\n\n #scan-button {\n display: block;\n position: relative;\n margin-left: -35px;\n width: 35px;\n border: none;\n background: var(--barcodescan-input-button-icon) no-repeat center center;\n }\n\n #scan-button[hidden] {\n display: none;\n }\n `\n ]\n\n /**\n * Indicates whether barcode scanning is enabled.\n * @property {Boolean} scannable\n */\n @property({ type: Boolean, attribute: true }) scannable?: boolean\n\n /**\n * If true, the \"Enter\" key press event is not fired after scanning a barcode.\n * @property {Boolean} withoutEnter\n */\n @property({ attribute: 'without-enter', type: Boolean }) withoutEnter?: boolean\n\n /**\n * The value of the input field.\n * @property {String} declare value\n */\n @property({ type: String }) declare value?: string\n\n /**\n * If true, only English characters are allowed in the input field.\n * @property {Boolean} englishOnly\n */\n @property({ attribute: 'english-only', type: Boolean }) englishOnly?: boolean\n\n /**\n * If true, the input field is automatically selected after a change event.\n * @property {Boolean} selectAfterChange\n */\n @property({ attribute: 'select-after-change', type: Boolean }) selectAfterChange?: boolean\n\n @state() stream?: MediaStream\n\n @query('input') input!: HTMLInputElement\n\n private popup: OxPopup | null = null\n private video: HTMLVideoElement | null = null\n\n connectedCallback() {\n super.connectedCallback()\n\n if (navigator.mediaDevices) {\n ;(async () => {\n try {\n var stream = await navigator.mediaDevices.getUserMedia({ audio: false, video: { facingMode: 'environment' } })\n if (stream) {\n stream.getTracks().forEach(track => track.stop())\n this.scannable = true\n }\n } catch (e) {\n this.scannable = false\n console.warn('this device not support camera for barcode scan', e)\n }\n })()\n } else {\n this.scannable = false\n }\n }\n\n disconnectedCallback() {\n this.stopScan()\n }\n\n render() {\n this.style.setProperty('--barcodescan-input-button-icon', `url(${barcodeIcon})`)\n\n return html`\n <input\n type=\"text\"\n .value=${this.value || ''}\n @change=${(e: Event) => this.onInputChange(e)}\n @keydown=${(e: KeyboardEvent) => this.onInputKeyDown(e)}\n ?disabled=${this.disabled}\n />\n <button\n ?hidden=${!this.scannable}\n id=\"scan-button\"\n @click=${(e: MouseEvent) => {\n this.scan(e)\n }}\n ?disabled=${this.disabled}\n ></button>\n `\n }\n\n onInputChange(e: Event) {\n e.stopPropagation()\n\n if (this.englishOnly) {\n /* englishOnly 인 경우에는 멀티바이트 문자들을 모두 제거한다. */\n this.value = this.input.value = this.input.value?.replace(/[^\\x00-\\x7F]/g, '')\n } else {\n this.value = this.input.value\n }\n\n if (this.selectAfterChange) {\n requestAnimationFrame(() => {\n this.input.select()\n })\n }\n\n this.dispatchEvent(\n new CustomEvent('change', {\n detail: this.value\n })\n )\n }\n\n onInputKeyDown(e: KeyboardEvent) {\n if (e.key === 'Enter' && !e.isComposing) {\n /* Even if the value has not changed, the enter key triggers a change event. */\n e.preventDefault() /* Prevent change event from occurring twice. */\n\n this.input.dispatchEvent(new CustomEvent('change'))\n } else if (this.englishOnly && !e.metaKey && !e.ctrlKey && !e.altKey && /^Key/.test(e.code)) {\n e.stopPropagation()\n e.preventDefault()\n\n /* englishOnly 인 경우에 문자들은 여기에서 처리한다. 멀티바이트 문자들이 대부분 알파벳의 자모음을 조합하므로, ... */\n const key = e.shiftKey ? e.code.charAt(3) : e.code.charAt(3).toLowerCase()\n const value = this.input.value\n\n const start = this.input.selectionStart || 0\n const end = this.input.selectionEnd || start\n\n this.input.value = [value.substring(0, start), key, value.substring(end)].join('')\n this.input.setSelectionRange(start + 1, start + 1)\n }\n }\n\n async scan(e: MouseEvent) {\n try {\n if (this.popup) {\n this.stopScan()\n }\n\n this.popup = OxPopup.open({\n template: html`\n <video></video>\n <md-icon\n style=\"position: fixed; right: 0; top: 0; color: red; tabindex: 0\"\n @click=${() => {\n this.stopScan()\n }}\n >close</md-icon\n >\n `,\n width: '100vw',\n height: '100dvh'\n })\n\n this.video! = this.popup.querySelector('video') as HTMLVideoElement\n\n var constraints = { audio: false, video: { facingMode: 'environment' } } /* backside camera first */\n this.stream = await navigator.mediaDevices.getUserMedia(constraints)\n\n this.video.srcObject = this.stream\n this.video.play()\n\n this.video.onloadedmetadata = async e => {\n var canvas = new OffscreenCanvas(\n this.video!.videoWidth || this.video!.width,\n this.video!.videoHeight || this.video!.height\n )\n\n var context = canvas.getContext('2d', {\n willReadFrequently: true\n })\n\n const detect = async () => {\n try {\n if (!this.stream?.active) {\n return\n }\n\n context!.drawImage(this.video!, 0, 0, canvas.width, canvas.height)\n const imageData = context!.getImageData(0, 0, canvas.width, canvas.height)\n const symbols = await scanImageData(imageData)\n const result = symbols[0]?.decode()\n\n if (result) {\n this.stopScan()\n\n var input = this.input\n input.focus()\n this.value = input.value = String(result)\n\n if (!this.withoutEnter) {\n input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }))\n }\n } else {\n requestAnimationFrame(async () => await detect())\n }\n } catch (e) {\n console.warn(e)\n this.stopScan()\n }\n }\n\n await detect()\n }\n } catch (err) {\n /*\n * 1. stream device 문제로 예외 발생한 경우.\n * 2. 뒤로가기 등으로 popup이 종료된 경우에도 NotFoundException: Video stream has ended before any code could be detected. 이 발생한다.\n */\n console.warn(err)\n }\n }\n\n stopScan() {\n if (this.video) {\n this.video.pause()\n this.video.srcObject = null\n }\n\n if (this.popup) {\n this.popup.close()\n this.popup = null\n }\n\n this.stream?.getTracks().forEach(track => track.stop())\n }\n}\n"]}
1
+ {"version":3,"file":"ox-input-barcode.js","sourceRoot":"","sources":["../../src/ox-input-barcode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport '@operato/popup/ox-popup.js'\n\nimport { css, html } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\nimport { scanImageData } from '@undecaf/zbar-wasm'\n\nimport { i18next } from '@operato/i18n'\nimport { OxPopup } from '@operato/popup'\nimport { formElementHeight } from '@operato/styles/form-element-heights.js'\n\nimport { OxFormField } from './ox-form-field.js'\nimport { InputStyles } from './input-styles.js'\n\nconst barcodeIcon = `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAYBAMAAAAfR1CMAAADKGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFNjM4RURDQkQ1OUExMUU5QkExMkQ4NUY3NkMxNzBFOSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFNjM4RURDQ0Q1OUExMUU5QkExMkQ4NUY3NkMxNzBFOSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkU2MzhFREM5RDU5QTExRTlCQTEyRDg1Rjc2QzE3MEU5IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkU2MzhFRENBRDU5QTExRTlCQTEyRDg1Rjc2QzE3MEU5Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+55pr/QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAAkUExURQAAAEdwTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEus/7UCWQwAAAALdFJOU9YAg3wKBFBDSz9PnvQNDgAAAE9JREFUGNNjEEQFDKLJSnCOklkgg9QUJFn3RgZhRyS+iCGDEIp2RSBfQICRkRGIgTSQL4jCF6ScvxsZYOFT2T50/6D7Fz080MMLPTzRwhsAHVspfelur08AAAAASUVORK5CYII=`\n\n/**\n * Custom input component for barcode scanning.\n *\n * This component provides a text input field and a barcode scanning button. Users can input text\n * manually or scan barcodes using the device camera. Supported barcode formats include:\n *\n * - Code-39\n * - Code-93\n * - Code-128\n * - Codabar\n * - Databar/Expanded\n * - EAN/GTIN-5/8/13\n * - ISBN-10/13\n * - ISBN-13+2\n * - ISBN-13+5\n * - ITF (Interleaved 2 of 5)\n * - QR Code\n * - UPC-A/E\n *\n * @fires CustomEvent#change - Dispatched when the input value changes.\n * @fires KeyboardEvent#keydown - Dispatched when the Enter key is pressed (if not withoutEnter).\n *\n * @cssprop {String} --barcodescan-input-button-icon - Icon for the barcode scanning button.\n * @cssprop {String} --barcodescan-input-button-icon-size - The icon's background-size (default: auto, its own 30x24).\n * @cssprop {String} --barcodescan-input-button-width - The button's width, and the room the input keeps for it (default: 35px).\n *\n * @customElement\n */\n@customElement('ox-input-barcode')\nexport class OxInputBarcode extends OxFormField {\n static styles = [\n InputStyles,\n css`\n :host {\n display: flex;\n align-items: center;\n border: none;\n background-color: transparent;\n height: ${formElementHeight('--form-element-height-medium')};\n }\n\n * {\n align-self: stretch;\n }\n\n *:focus {\n outline: none;\n }\n\n input {\n flex: 1;\n width: 10px; /* intentionally width set */\n border: var(--ox-input-border, 1px solid var(--md-sys-color-outline));\n border-radius: var(--ox-input-radius, var(--md-sys-shape-corner-small));\n background-color: var(--ox-input-background, var(--md-sys-color-on-primary));\n color: var(--ox-input-color, var(--md-sys-color-on-primary-container));\n font-size: var(--md-sys-typescale-label-large-size, 0.875rem);\n\n box-sizing: border-box;\n padding-right: var(--barcodescan-input-button-width, 35px);\n }\n\n input:focus {\n outline: none;\n border-color: var(--md-sys-color-secondary-fixed-dim);\n }\n\n #scan-button {\n display: block;\n position: relative;\n margin-left: calc(-1 * var(--barcodescan-input-button-width, 35px));\n width: var(--barcodescan-input-button-width, 35px);\n border: none;\n background: var(--barcodescan-input-button-icon) no-repeat center center;\n background-size: var(--barcodescan-input-button-icon-size, auto);\n }\n\n #scan-button[hidden] {\n display: none;\n }\n `\n ]\n\n /**\n * Whether this device has a camera to scan with - the camera button shows only then. Reflected, so a host can\n * style itself by it (`ox-input-barcode[scannable]`) without asking the device again.\n * @property {Boolean} scannable\n */\n @property({ type: Boolean, attribute: true, reflect: true }) scannable?: boolean\n\n /**\n * If true, the \"Enter\" key press event is not fired after scanning a barcode.\n * @property {Boolean} withoutEnter\n */\n @property({ attribute: 'without-enter', type: Boolean }) withoutEnter?: boolean\n\n /**\n * The value of the input field.\n * @property {String} declare value\n */\n @property({ type: String }) declare value?: string\n\n /**\n * If true, only English characters are allowed in the input field.\n * @property {Boolean} englishOnly\n */\n @property({ attribute: 'english-only', type: Boolean }) englishOnly?: boolean\n\n /**\n * If true, the input field is automatically selected after a change event.\n * @property {Boolean} selectAfterChange\n */\n @property({ attribute: 'select-after-change', type: Boolean }) selectAfterChange?: boolean\n\n @state() stream?: MediaStream\n\n @query('input') input!: HTMLInputElement\n\n private popup: OxPopup | null = null\n private video: HTMLVideoElement | null = null\n\n connectedCallback() {\n super.connectedCallback()\n\n /*\n * Whether to offer the camera button: ask which kinds of device there are, never open one. Opening the camera\n * here asked for camera permission - and switched the camera on - wherever the field was merely drawn, so a\n * field in an app's header asked on every page. Permission is asked when the person presses the button.\n */\n if (navigator.mediaDevices?.enumerateDevices) {\n ;(async () => {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices()\n this.scannable = devices.some(device => device.kind === 'videoinput')\n } catch (e) {\n this.scannable = false\n console.warn('this device not support camera for barcode scan', e)\n }\n })()\n } else {\n this.scannable = false\n }\n }\n\n disconnectedCallback() {\n this.stopScan()\n }\n\n render() {\n this.style.setProperty('--barcodescan-input-button-icon', `url(${barcodeIcon})`)\n\n /*\n * Name both controls. The input had no name and no placeholder - `placeholder` set by the caller never reached\n * it - and the camera button was an empty button. Same rule as ox-input-search: the caller's placeholder is the\n * name, \"barcode\" when there is none.\n */\n const scanLabel = i18next.t('button.scan-with-camera', { defaultValue: 'scan with camera' })\n\n return html`\n <input\n type=\"text\"\n aria-label=${this.placeholder || i18next.t('label.barcode', { defaultValue: 'barcode' })}\n .placeholder=${this.placeholder || ''}\n .value=${this.value || ''}\n @change=${(e: Event) => this.onInputChange(e)}\n @keydown=${(e: KeyboardEvent) => this.onInputKeyDown(e)}\n ?disabled=${this.disabled}\n />\n <button\n ?hidden=${!this.scannable}\n id=\"scan-button\"\n aria-label=${scanLabel}\n title=${scanLabel}\n @click=${(e: MouseEvent) => {\n this.scan(e)\n }}\n ?disabled=${this.disabled}\n ></button>\n `\n }\n\n onInputChange(e: Event) {\n e.stopPropagation()\n\n if (this.englishOnly) {\n /* englishOnly 인 경우에는 멀티바이트 문자들을 모두 제거한다. */\n this.value = this.input.value = this.input.value?.replace(/[^\\x00-\\x7F]/g, '')\n } else {\n this.value = this.input.value\n }\n\n if (this.selectAfterChange) {\n requestAnimationFrame(() => {\n this.input.select()\n })\n }\n\n this.dispatchEvent(\n new CustomEvent('change', {\n detail: this.value\n })\n )\n }\n\n onInputKeyDown(e: KeyboardEvent) {\n if (e.key === 'Enter' && !e.isComposing) {\n /* Even if the value has not changed, the enter key triggers a change event. */\n e.preventDefault() /* Prevent change event from occurring twice. */\n\n this.input.dispatchEvent(new CustomEvent('change'))\n } else if (this.englishOnly && !e.metaKey && !e.ctrlKey && !e.altKey && /^Key/.test(e.code)) {\n e.stopPropagation()\n e.preventDefault()\n\n /* englishOnly 인 경우에 문자들은 여기에서 처리한다. 멀티바이트 문자들이 대부분 알파벳의 자모음을 조합하므로, ... */\n const key = e.shiftKey ? e.code.charAt(3) : e.code.charAt(3).toLowerCase()\n const value = this.input.value\n\n const start = this.input.selectionStart || 0\n const end = this.input.selectionEnd || start\n\n this.input.value = [value.substring(0, start), key, value.substring(end)].join('')\n this.input.setSelectionRange(start + 1, start + 1)\n }\n }\n\n async scan(e: MouseEvent) {\n try {\n if (this.popup) {\n this.stopScan()\n }\n\n this.popup = OxPopup.open({\n template: html`\n <video></video>\n <md-icon\n style=\"position: fixed; right: 0; top: 0; color: red; tabindex: 0\"\n @click=${() => {\n this.stopScan()\n }}\n >close</md-icon\n >\n `,\n width: '100vw',\n height: '100dvh'\n })\n\n this.video! = this.popup.querySelector('video') as HTMLVideoElement\n\n var constraints = { audio: false, video: { facingMode: 'environment' } } /* backside camera first */\n this.stream = await navigator.mediaDevices.getUserMedia(constraints)\n\n this.video.srcObject = this.stream\n this.video.play()\n\n this.video.onloadedmetadata = async e => {\n var canvas = new OffscreenCanvas(\n this.video!.videoWidth || this.video!.width,\n this.video!.videoHeight || this.video!.height\n )\n\n var context = canvas.getContext('2d', {\n willReadFrequently: true\n })\n\n const detect = async () => {\n try {\n if (!this.stream?.active) {\n return\n }\n\n context!.drawImage(this.video!, 0, 0, canvas.width, canvas.height)\n const imageData = context!.getImageData(0, 0, canvas.width, canvas.height)\n const symbols = await scanImageData(imageData)\n const result = symbols[0]?.decode()\n\n if (result) {\n this.stopScan()\n\n var input = this.input\n input.focus()\n this.value = input.value = String(result)\n\n if (!this.withoutEnter) {\n input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }))\n }\n } else {\n requestAnimationFrame(async () => await detect())\n }\n } catch (e) {\n console.warn(e)\n this.stopScan()\n }\n }\n\n await detect()\n }\n } catch (err) {\n /*\n * 1. stream device 문제로 예외 발생한 경우.\n * 2. 뒤로가기 등으로 popup이 종료된 경우에도 NotFoundException: Video stream has ended before any code could be detected. 이 발생한다.\n */\n console.warn(err)\n }\n }\n\n stopScan() {\n if (this.video) {\n this.video.pause()\n this.video.srcObject = null\n }\n\n if (this.popup) {\n this.popup.close()\n this.popup = null\n }\n\n this.stream?.getTracks().forEach(track => track.stop())\n }\n}\n"]}