@operato/input 2.0.0-alpha.4 → 2.0.0-alpha.48
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.
- package/CHANGELOG.md +126 -0
- package/dist/src/ox-input-barcode.d.ts +48 -5
- package/dist/src/ox-input-barcode.js +96 -69
- package/dist/src/ox-input-barcode.js.map +1 -1
- package/dist/src/ox-input-data.d.ts +3 -4
- package/dist/src/ox-input-data.js +86 -34
- package/dist/src/ox-input-data.js.map +1 -1
- package/dist/src/ox-input-i18n-label.d.ts +33 -0
- package/dist/src/ox-input-i18n-label.js +129 -0
- package/dist/src/ox-input-i18n-label.js.map +1 -0
- package/dist/src/ox-input-mass-fraction.d.ts +1 -0
- package/dist/src/ox-input-mass-fraction.js +48 -31
- package/dist/src/ox-input-mass-fraction.js.map +1 -1
- package/dist/stories/ox-input-barcode.stories.js +4 -0
- package/dist/stories/ox-input-barcode.stories.js.map +1 -1
- package/dist/stories/ox-input-data.stories.d.ts +33 -0
- package/dist/stories/ox-input-data.stories.js +40 -0
- package/dist/stories/ox-input-data.stories.js.map +1 -0
- package/dist/stories/ox-input-i18n-label.stories.d.ts +29 -0
- package/dist/stories/ox-input-i18n-label.stories.js +58 -0
- package/dist/stories/ox-input-i18n-label.stories.js.map +1 -0
- package/dist/stories/ox-input-mass-fraction.stories.d.ts +4 -0
- package/dist/stories/ox-input-mass-fraction.stories.js +9 -2
- package/dist/stories/ox-input-mass-fraction.stories.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +21 -17
- package/src/ox-input-barcode.ts +127 -63
- package/src/ox-input-data.ts +95 -36
- package/src/ox-input-i18n-label.ts +139 -0
- package/src/ox-input-mass-fraction.ts +46 -32
- package/stories/ox-input-barcode.stories.ts +4 -0
- package/stories/ox-input-data.stories.ts +55 -0
- package/stories/ox-input-i18n-label.stories.ts +73 -0
- package/stories/ox-input-mass-fraction.stories.ts +10 -1
- package/yarn-error.log +17084 -0
package/package.json
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
"name": "@operato/input",
|
3
3
|
"description": "Webcomponents for input following open-wc recommendations",
|
4
4
|
"author": "heartyoh@hatiolab.com",
|
5
|
-
"version": "2.0.0-alpha.
|
5
|
+
"version": "2.0.0-alpha.48",
|
6
6
|
"main": "dist/src/index.js",
|
7
7
|
"module": "dist/src/index.js",
|
8
8
|
"license": "MIT",
|
@@ -46,6 +46,7 @@
|
|
46
46
|
"./ox-input-search.js": "./dist/src/ox-input-search.js",
|
47
47
|
"./ox-input-value-map.js": "./dist/src/ox-input-value-map.js",
|
48
48
|
"./ox-input-value-ranges.js": "./dist/src/ox-input-value-ranges.js",
|
49
|
+
"./ox-input-i18n-label.js": "./dist/src/ox-input-i18n-label.js",
|
49
50
|
"./ox-input-partition-keys.js": "./dist/src/ox-input-partition-keys.js",
|
50
51
|
"./ox-input-table.js": "./dist/src/ox-input-table.js",
|
51
52
|
"./ox-input-scene-component-id.js": "./dist/src/ox-input-scene-component-id.js",
|
@@ -143,6 +144,9 @@
|
|
143
144
|
"./ox-input-value-ranges.js": [
|
144
145
|
"./dist/src/ox-input-value-ranges.d.ts"
|
145
146
|
],
|
147
|
+
"./ox-input-i18n-label.js": [
|
148
|
+
"./dist/src/ox-input-i18n-label.d.ts"
|
149
|
+
],
|
146
150
|
"./ox-input-partition-keys.js": [
|
147
151
|
"./dist/src/ox-input-partition-keys.d.ts"
|
148
152
|
],
|
@@ -192,36 +196,36 @@
|
|
192
196
|
"@lit/localize": "^0.11.2",
|
193
197
|
"@material/mwc-icon": "^0.27.0",
|
194
198
|
"@operato/color-picker": "^2.0.0-alpha.0",
|
195
|
-
"@operato/i18n": "^2.0.0-alpha.
|
196
|
-
"@operato/popup": "^2.0.0-alpha.
|
197
|
-
"@operato/styles": "^2.0.0-alpha.
|
198
|
-
"@operato/utils": "^2.0.0-alpha.
|
199
|
+
"@operato/i18n": "^2.0.0-alpha.28",
|
200
|
+
"@operato/popup": "^2.0.0-alpha.48",
|
201
|
+
"@operato/styles": "^2.0.0-alpha.47",
|
202
|
+
"@operato/utils": "^2.0.0-alpha.35",
|
199
203
|
"@polymer/paper-dropdown-menu": "^3.2.0",
|
200
204
|
"@polymer/paper-item": "^3.0.1",
|
201
205
|
"@thebespokepixel/es-tinycolor": "^3.1.0",
|
202
206
|
"@types/codemirror": "^5.60.5",
|
203
|
-
"@
|
207
|
+
"@undecaf/zbar-wasm": "^0.10.1",
|
204
208
|
"codemirror": "^6.0.1",
|
205
209
|
"lit": "^2.5.0",
|
206
210
|
"lodash-es": "^4.17.21"
|
207
211
|
},
|
208
212
|
"devDependencies": {
|
209
|
-
"@custom-elements-manifest/analyzer": "^0.
|
213
|
+
"@custom-elements-manifest/analyzer": "^0.9.2",
|
210
214
|
"@hatiolab/prettier-config": "^1.0.0",
|
211
215
|
"@lit/localize-tools": "^0.6.3",
|
212
|
-
"@open-wc/eslint-config": "^
|
216
|
+
"@open-wc/eslint-config": "^12.0.3",
|
213
217
|
"@open-wc/testing": "^3.1.6",
|
214
|
-
"@typescript-eslint/eslint-plugin": "^
|
215
|
-
"@typescript-eslint/parser": "^
|
218
|
+
"@typescript-eslint/eslint-plugin": "^7.0.1",
|
219
|
+
"@typescript-eslint/parser": "^7.0.1",
|
216
220
|
"@web/dev-server": "^0.3.0",
|
217
|
-
"@web/dev-server-storybook": "^0.
|
218
|
-
"@web/test-runner": "^0.
|
221
|
+
"@web/dev-server-storybook": "^2.0.1",
|
222
|
+
"@web/test-runner": "^0.18.0",
|
219
223
|
"concurrently": "^8.0.1",
|
220
224
|
"eslint": "^8.39.0",
|
221
|
-
"eslint-config-prettier": "^
|
222
|
-
"husky": "^
|
223
|
-
"lint-staged": "^
|
224
|
-
"prettier": "^2.
|
225
|
+
"eslint-config-prettier": "^9.1.0",
|
226
|
+
"husky": "^9.0.11",
|
227
|
+
"lint-staged": "^15.2.2",
|
228
|
+
"prettier": "^3.2.5",
|
225
229
|
"tslib": "^2.3.1",
|
226
230
|
"typescript": "^5.0.4"
|
227
231
|
},
|
@@ -239,5 +243,5 @@
|
|
239
243
|
"prettier --write"
|
240
244
|
]
|
241
245
|
},
|
242
|
-
"gitHead": "
|
246
|
+
"gitHead": "cc799e87b203d9865a6d1a5188dd53a4f8e778d0"
|
243
247
|
}
|
package/src/ox-input-barcode.ts
CHANGED
@@ -6,14 +6,40 @@ import '@operato/popup/ox-popup.js'
|
|
6
6
|
|
7
7
|
import { css, html } from 'lit'
|
8
8
|
import { customElement, property, query, state } from 'lit/decorators.js'
|
9
|
+
import { scanImageData } from '@undecaf/zbar-wasm'
|
9
10
|
|
10
11
|
import { OxPopup } from '@operato/popup'
|
11
|
-
import { BrowserMultiFormatReader } from '@zxing/library'
|
12
12
|
|
13
13
|
import { OxFormField } from './ox-form-field.js'
|
14
14
|
|
15
15
|
const barcodeIcon = `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAYBAMAAAAfR1CMAAADKGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFNjM4RURDQkQ1OUExMUU5QkExMkQ4NUY3NkMxNzBFOSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFNjM4RURDQ0Q1OUExMUU5QkExMkQ4NUY3NkMxNzBFOSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkU2MzhFREM5RDU5QTExRTlCQTEyRDg1Rjc2QzE3MEU5IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkU2MzhFRENBRDU5QTExRTlCQTEyRDg1Rjc2QzE3MEU5Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+55pr/QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAAkUExURQAAAEdwTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEus/7UCWQwAAAALdFJOU9YAg3wKBFBDSz9PnvQNDgAAAE9JREFUGNNjEEQFDKLJSnCOklkgg9QUJFn3RgZhRyS+iCGDEIp2RSBfQICRkRGIgTSQL4jCF6ScvxsZYOFT2T50/6D7Fz080MMLPTzRwhsAHVspfelur08AAAAASUVORK5CYII=`
|
16
16
|
|
17
|
+
/**
|
18
|
+
* Custom input component for barcode scanning.
|
19
|
+
*
|
20
|
+
* This component provides a text input field and a barcode scanning button. Users can input text
|
21
|
+
* manually or scan barcodes using the device camera. Supported barcode formats include:
|
22
|
+
*
|
23
|
+
* - Code-39
|
24
|
+
* - Code-93
|
25
|
+
* - Code-128
|
26
|
+
* - Codabar
|
27
|
+
* - Databar/Expanded
|
28
|
+
* - EAN/GTIN-5/8/13
|
29
|
+
* - ISBN-10/13
|
30
|
+
* - ISBN-13+2
|
31
|
+
* - ISBN-13+5
|
32
|
+
* - ITF (Interleaved 2 of 5)
|
33
|
+
* - QR Code
|
34
|
+
* - UPC-A/E
|
35
|
+
*
|
36
|
+
* @fires CustomEvent#change - Dispatched when the input value changes.
|
37
|
+
* @fires KeyboardEvent#keydown - Dispatched when the Enter key is pressed (if not withoutEnter).
|
38
|
+
*
|
39
|
+
* @cssprop {String} --barcodescan-input-button-icon - Icon for the barcode scanning button.
|
40
|
+
*
|
41
|
+
* @customElement
|
42
|
+
*/
|
17
43
|
@customElement('ox-input-barcode')
|
18
44
|
export class OxInputBarcode extends OxFormField {
|
19
45
|
static styles = [
|
@@ -59,46 +85,45 @@ export class OxInputBarcode extends OxFormField {
|
|
59
85
|
#scan-button[hidden] {
|
60
86
|
display: none;
|
61
87
|
}
|
62
|
-
|
63
|
-
ox-popup {
|
64
|
-
position: fixed;
|
65
|
-
|
66
|
-
width: 80vw;
|
67
|
-
height: 80vh;
|
68
|
-
transform: translate(10%, 10%);
|
69
|
-
}
|
70
|
-
|
71
|
-
video {
|
72
|
-
width: 100%;
|
73
|
-
height: 100%;
|
74
|
-
}
|
75
|
-
|
76
|
-
@media screen and (max-width: 460px) {
|
77
|
-
ox-popup {
|
78
|
-
position: fixed;
|
79
|
-
left: 0;
|
80
|
-
top: 0;
|
81
|
-
width: 100vw;
|
82
|
-
height: 100vh;
|
83
|
-
height: 100dvh;
|
84
|
-
transform: translate(0%, 0%);
|
85
|
-
}
|
86
|
-
}
|
87
88
|
`
|
88
89
|
]
|
89
90
|
|
91
|
+
/**
|
92
|
+
* Indicates whether barcode scanning is enabled.
|
93
|
+
* @property {Boolean} scannable
|
94
|
+
*/
|
90
95
|
@property({ type: Boolean }) scannable?: boolean
|
96
|
+
|
97
|
+
/**
|
98
|
+
* If true, the "Enter" key press event is not fired after scanning a barcode.
|
99
|
+
* @property {Boolean} withoutEnter
|
100
|
+
*/
|
91
101
|
@property({ attribute: 'without-enter', type: Boolean }) withoutEnter?: boolean
|
102
|
+
|
103
|
+
/**
|
104
|
+
* The value of the input field.
|
105
|
+
* @property {String} declare value
|
106
|
+
*/
|
92
107
|
@property({ type: String }) declare value?: string
|
108
|
+
|
109
|
+
/**
|
110
|
+
* If true, only English characters are allowed in the input field.
|
111
|
+
* @property {Boolean} englishOnly
|
112
|
+
*/
|
93
113
|
@property({ attribute: 'english-only', type: Boolean }) englishOnly?: boolean
|
114
|
+
|
115
|
+
/**
|
116
|
+
* If true, the input field is automatically selected after a change event.
|
117
|
+
* @property {Boolean} selectAfterChange
|
118
|
+
*/
|
94
119
|
@property({ attribute: 'select-after-change', type: Boolean }) selectAfterChange?: boolean
|
95
120
|
|
96
121
|
@state() stream?: MediaStream
|
97
|
-
@state() reader?: BrowserMultiFormatReader
|
98
122
|
|
99
123
|
@query('input') input!: HTMLInputElement
|
100
|
-
|
101
|
-
|
124
|
+
|
125
|
+
private popup: OxPopup | null = null
|
126
|
+
private video: HTMLVideoElement | null = null
|
102
127
|
|
103
128
|
connectedCallback() {
|
104
129
|
super.connectedCallback()
|
@@ -108,7 +133,7 @@ export class OxInputBarcode extends OxFormField {
|
|
108
133
|
if (navigator.mediaDevices) {
|
109
134
|
;(async () => {
|
110
135
|
try {
|
111
|
-
var stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } })
|
136
|
+
var stream = await navigator.mediaDevices.getUserMedia({ audio: false, video: { facingMode: 'environment' } })
|
112
137
|
if (stream) {
|
113
138
|
stream.getTracks().forEach(track => track.stop())
|
114
139
|
this.scannable = true
|
@@ -143,14 +168,6 @@ export class OxInputBarcode extends OxFormField {
|
|
143
168
|
}}
|
144
169
|
?disabled=${this.disabled}
|
145
170
|
></button>
|
146
|
-
|
147
|
-
<ox-popup
|
148
|
-
@focusout=${() => {
|
149
|
-
this.stopScan()
|
150
|
-
}}
|
151
|
-
>
|
152
|
-
<video></video>
|
153
|
-
</ox-popup>
|
154
171
|
`
|
155
172
|
}
|
156
173
|
|
@@ -201,27 +218,74 @@ export class OxInputBarcode extends OxFormField {
|
|
201
218
|
|
202
219
|
async scan(e: MouseEvent) {
|
203
220
|
try {
|
204
|
-
this.popup
|
221
|
+
if (this.popup) {
|
222
|
+
this.stopScan()
|
223
|
+
}
|
224
|
+
|
225
|
+
this.popup = OxPopup.open({
|
226
|
+
template: html`
|
227
|
+
<video></video>
|
228
|
+
<mwc-icon
|
229
|
+
style="position: fixed; right: 0; top: 0; color: red; tabindex: 0"
|
230
|
+
@click=${() => {
|
231
|
+
this.stopScan()
|
232
|
+
}}
|
233
|
+
>close</mwc-icon
|
234
|
+
>
|
235
|
+
`,
|
236
|
+
width: '100vw',
|
237
|
+
height: '100dvh'
|
238
|
+
})
|
205
239
|
|
206
|
-
|
207
|
-
await this.updateComplete
|
240
|
+
this.video! = this.popup.querySelector('video') as HTMLVideoElement
|
208
241
|
|
209
|
-
var constraints = { video: { facingMode: 'environment' } } /* backside camera first */
|
242
|
+
var constraints = { audio: false, video: { facingMode: 'environment' } } /* backside camera first */
|
210
243
|
this.stream = await navigator.mediaDevices.getUserMedia(constraints)
|
211
244
|
|
212
|
-
this.
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
245
|
+
this.video.srcObject = this.stream
|
246
|
+
this.video.play()
|
247
|
+
|
248
|
+
this.video.onloadedmetadata = async e => {
|
249
|
+
var canvas = new OffscreenCanvas(
|
250
|
+
this.video!.videoWidth || this.video!.width,
|
251
|
+
this.video!.videoHeight || this.video!.height
|
252
|
+
)
|
253
|
+
|
254
|
+
var context = canvas.getContext('2d', {
|
255
|
+
willReadFrequently: true
|
256
|
+
})
|
257
|
+
|
258
|
+
const detect = async () => {
|
259
|
+
try {
|
260
|
+
if (!this.stream?.active) {
|
261
|
+
return
|
262
|
+
}
|
263
|
+
|
264
|
+
context!.drawImage(this.video!, 0, 0, canvas.width, canvas.height)
|
265
|
+
const imageData = context!.getImageData(0, 0, canvas.width, canvas.height)
|
266
|
+
const symbols = await scanImageData(imageData)
|
267
|
+
const result = symbols[0]?.decode()
|
268
|
+
|
269
|
+
if (result) {
|
270
|
+
this.stopScan()
|
271
|
+
|
272
|
+
var input = this.input
|
273
|
+
input.focus()
|
274
|
+
this.value = input.value = String(result)
|
275
|
+
|
276
|
+
if (!this.withoutEnter) {
|
277
|
+
input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }))
|
278
|
+
}
|
279
|
+
} else {
|
280
|
+
requestAnimationFrame(async () => await detect())
|
281
|
+
}
|
282
|
+
} catch (e) {
|
283
|
+
console.warn(e)
|
284
|
+
this.stopScan()
|
285
|
+
}
|
221
286
|
}
|
222
|
-
|
223
|
-
|
224
|
-
this.stopScan()
|
287
|
+
|
288
|
+
await detect()
|
225
289
|
}
|
226
290
|
} catch (err) {
|
227
291
|
/*
|
@@ -229,20 +293,20 @@ export class OxInputBarcode extends OxFormField {
|
|
229
293
|
* 2. 뒤로가기 등으로 popup이 종료된 경우에도 NotFoundException: Video stream has ended before any code could be detected. 이 발생한다.
|
230
294
|
*/
|
231
295
|
console.warn(err)
|
232
|
-
} finally {
|
233
|
-
this.popup.close()
|
234
|
-
|
235
|
-
this.stopScan()
|
236
296
|
}
|
237
297
|
}
|
238
298
|
|
239
299
|
stopScan() {
|
240
|
-
this.video
|
300
|
+
if (this.video) {
|
301
|
+
this.video.pause()
|
302
|
+
this.video.srcObject = null
|
303
|
+
}
|
241
304
|
|
242
|
-
this.
|
243
|
-
|
305
|
+
if (this.popup) {
|
306
|
+
this.popup.close()
|
307
|
+
this.popup = null
|
308
|
+
}
|
244
309
|
|
245
|
-
|
246
|
-
delete this.reader
|
310
|
+
this.stream?.getTracks().forEach(track => track.stop())
|
247
311
|
}
|
248
312
|
}
|
package/src/ox-input-data.ts
CHANGED
@@ -4,11 +4,13 @@
|
|
4
4
|
|
5
5
|
import './ox-input-code'
|
6
6
|
|
7
|
-
import { css, html
|
7
|
+
import { css, html } from 'lit'
|
8
8
|
import { customElement } from 'lit/decorators.js'
|
9
|
+
import { live } from 'lit/directives/live.js'
|
9
10
|
|
10
11
|
import { OxFormField } from './ox-form-field.js'
|
11
12
|
import { OxInputCode } from './ox-input-code.js'
|
13
|
+
import { isEqual } from 'lodash-es'
|
12
14
|
|
13
15
|
/**
|
14
16
|
WEB Component for code-mirror based data editor.
|
@@ -49,38 +51,56 @@ export class OxInputData extends OxFormField {
|
|
49
51
|
]
|
50
52
|
|
51
53
|
render() {
|
54
|
+
const valueType = typeof this.value
|
55
|
+
|
52
56
|
return html`
|
53
57
|
<div datatype>
|
54
58
|
<input
|
59
|
+
id="string"
|
55
60
|
type="radio"
|
56
61
|
name="data-type"
|
57
62
|
data-value="string"
|
58
|
-
.checked=${
|
63
|
+
.checked=${live(valueType == 'string')}
|
59
64
|
@click=${() => this._setDataType('string')}
|
60
65
|
?disabled=${this.disabled}
|
61
|
-
/>
|
66
|
+
/>
|
67
|
+
<label for="string">string</label>
|
62
68
|
|
63
69
|
<input
|
70
|
+
id="number"
|
64
71
|
type="radio"
|
65
72
|
name="data-type"
|
66
73
|
data-value="number"
|
67
|
-
.checked=${
|
74
|
+
.checked=${live(valueType == 'number')}
|
68
75
|
@click=${() => this._setDataType('number')}
|
69
76
|
?disabled=${this.disabled}
|
70
|
-
/>
|
77
|
+
/>
|
78
|
+
<label for="number">number</label>
|
71
79
|
|
72
80
|
<input
|
81
|
+
id="object"
|
73
82
|
type="radio"
|
74
83
|
name="data-type"
|
75
84
|
data-value="object"
|
76
|
-
.checked=${
|
85
|
+
.checked=${live(valueType == 'object')}
|
77
86
|
@click=${() => this._setDataType('object')}
|
78
87
|
?disabled=${this.disabled}
|
79
|
-
/>
|
88
|
+
/>
|
89
|
+
<label for="object">object</label>
|
90
|
+
|
80
91
|
<mwc-icon @click=${() => this._clearData()} title="delete">delete_forever</mwc-icon>
|
81
92
|
</div>
|
82
93
|
|
83
|
-
<ox-input-code
|
94
|
+
<ox-input-code
|
95
|
+
.value=${this._getStringData(this.value)}
|
96
|
+
language="text"
|
97
|
+
editor
|
98
|
+
?disabled=${this.disabled}
|
99
|
+
@change=${(e: CustomEvent) => {
|
100
|
+
e.stopPropagation()
|
101
|
+
this._setDataTypeAndValue(valueType, (e.target as any).value)
|
102
|
+
}}
|
103
|
+
>
|
84
104
|
</ox-input-code>
|
85
105
|
`
|
86
106
|
}
|
@@ -88,45 +108,75 @@ export class OxInputData extends OxFormField {
|
|
88
108
|
firstUpdated() {
|
89
109
|
this.renderRoot.addEventListener('change', e => {
|
90
110
|
e.stopPropagation()
|
111
|
+
|
91
112
|
const target = e.target as OxInputCode
|
92
113
|
if (target.hasAttribute('editor')) {
|
114
|
+
if (this.value === undefined && target.value == '') {
|
115
|
+
return
|
116
|
+
}
|
93
117
|
this.value = target.value
|
94
118
|
}
|
95
|
-
|
96
|
-
const type = this.renderRoot.querySelector('input[name=data-type]:checked')?.getAttribute('data-value')
|
97
|
-
this._setDataType(type)
|
98
119
|
})
|
99
120
|
}
|
100
121
|
|
101
|
-
|
102
|
-
|
103
|
-
|
122
|
+
_setDataTypeAndValue(type: string | undefined | null, value: any) {
|
123
|
+
/* value must be a string */
|
124
|
+
try {
|
125
|
+
switch (type) {
|
126
|
+
case 'number':
|
127
|
+
if (!isNaN(Number(value))) {
|
128
|
+
value = Number(value)
|
129
|
+
}
|
130
|
+
break
|
131
|
+
case 'object':
|
132
|
+
value = eval('(' + value + ')')
|
133
|
+
break
|
134
|
+
}
|
135
|
+
} catch (e) {}
|
136
|
+
|
137
|
+
if (isEqual(this.value, value)) {
|
138
|
+
return
|
104
139
|
}
|
140
|
+
|
141
|
+
this.value = value
|
142
|
+
|
143
|
+
this.requestUpdate()
|
144
|
+
this._onAfterValueChange()
|
105
145
|
}
|
106
146
|
|
107
147
|
_setDataType(type: string | undefined | null) {
|
108
|
-
if (typeof this.value
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
148
|
+
if (typeof this.value == type) {
|
149
|
+
return
|
150
|
+
}
|
151
|
+
|
152
|
+
var value = this.value
|
153
|
+
|
154
|
+
try {
|
155
|
+
switch (type) {
|
156
|
+
case 'string':
|
157
|
+
value = this._getStringData(value)
|
158
|
+
break
|
159
|
+
case 'number':
|
160
|
+
if (!isNaN(value)) {
|
161
|
+
value = Number(value)
|
162
|
+
}
|
163
|
+
break
|
164
|
+
case 'object':
|
165
|
+
value = eval('(' + value + ')')
|
166
|
+
break
|
127
167
|
}
|
168
|
+
} catch (e) {
|
169
|
+
console.log(e)
|
128
170
|
}
|
129
171
|
|
172
|
+
if (isEqual(this.value, value)) {
|
173
|
+
this.requestUpdate()
|
174
|
+
return
|
175
|
+
}
|
176
|
+
|
177
|
+
this.value = value
|
178
|
+
|
179
|
+
this.requestUpdate()
|
130
180
|
this._onAfterValueChange()
|
131
181
|
}
|
132
182
|
|
@@ -135,11 +185,20 @@ export class OxInputData extends OxFormField {
|
|
135
185
|
this._onAfterValueChange()
|
136
186
|
}
|
137
187
|
|
138
|
-
|
139
|
-
|
188
|
+
_getStringData(data: any) {
|
189
|
+
const type = typeof data
|
190
|
+
|
191
|
+
switch (type) {
|
192
|
+
case 'object':
|
193
|
+
return JSON.stringify(data, null, 1)
|
194
|
+
case 'undefined':
|
195
|
+
return ''
|
196
|
+
default:
|
197
|
+
return String(data) || ''
|
198
|
+
}
|
140
199
|
}
|
141
200
|
|
142
|
-
_onAfterValueChange() {
|
201
|
+
async _onAfterValueChange() {
|
143
202
|
this.dispatchEvent(
|
144
203
|
new CustomEvent('change', {
|
145
204
|
bubbles: true,
|
@@ -0,0 +1,139 @@
|
|
1
|
+
/**
|
2
|
+
* @license Copyright © HatioLab Inc. All rights reserved.
|
3
|
+
*/
|
4
|
+
|
5
|
+
import '@material/mwc-icon'
|
6
|
+
|
7
|
+
import { css, html } from 'lit'
|
8
|
+
import { customElement, property, queryAll } from 'lit/decorators.js'
|
9
|
+
|
10
|
+
import { OxFormField } from './ox-form-field'
|
11
|
+
|
12
|
+
type I18nLabel = { [code: string]: string }
|
13
|
+
|
14
|
+
const LANGUAGES = [
|
15
|
+
{
|
16
|
+
code: 'en',
|
17
|
+
display: 'English'
|
18
|
+
}
|
19
|
+
]
|
20
|
+
|
21
|
+
/**
|
22
|
+
input component for i18n labels
|
23
|
+
|
24
|
+
Example:
|
25
|
+
|
26
|
+
<ox-input-i18n-label
|
27
|
+
value=${map}
|
28
|
+
languages=${languages}
|
29
|
+
</ox-input-i18n-label>
|
30
|
+
*/
|
31
|
+
@customElement('ox-input-i18n-label')
|
32
|
+
export class OxInputI18nLabels extends OxFormField {
|
33
|
+
static styles = css`
|
34
|
+
:host {
|
35
|
+
display: flex;
|
36
|
+
flex-direction: column;
|
37
|
+
overflow: hidden;
|
38
|
+
}
|
39
|
+
|
40
|
+
[data-record] {
|
41
|
+
display: flex;
|
42
|
+
flex-direction: row;
|
43
|
+
gap: 10px;
|
44
|
+
}
|
45
|
+
|
46
|
+
label {
|
47
|
+
width: 80px;
|
48
|
+
align-self: center;
|
49
|
+
text-align: end;
|
50
|
+
}
|
51
|
+
|
52
|
+
input {
|
53
|
+
flex: 1;
|
54
|
+
border: 0;
|
55
|
+
border-bottom: var(--border-dark-color);
|
56
|
+
padding: var(--input-padding);
|
57
|
+
font: var(--input-font);
|
58
|
+
color: var(--primary-text-color);
|
59
|
+
min-width: 100px;
|
60
|
+
}
|
61
|
+
|
62
|
+
input:focus {
|
63
|
+
outline: none;
|
64
|
+
border-bottom: 1px solid var(--primary-color);
|
65
|
+
}
|
66
|
+
|
67
|
+
input[type='hidden'] {
|
68
|
+
flex: 0;
|
69
|
+
}
|
70
|
+
`
|
71
|
+
|
72
|
+
@property({ type: Object }) value: I18nLabel = {}
|
73
|
+
@property({ type: Array }) languages: { display: string; code: string }[] = LANGUAGES
|
74
|
+
|
75
|
+
@queryAll('[data-record]') records!: NodeListOf<HTMLElement>
|
76
|
+
|
77
|
+
private changing: boolean = false
|
78
|
+
|
79
|
+
firstUpdated() {
|
80
|
+
this.renderRoot.addEventListener('change', this.onChange.bind(this))
|
81
|
+
}
|
82
|
+
|
83
|
+
render() {
|
84
|
+
const value = !this.value || typeof this.value !== 'object' ? {} : this.value
|
85
|
+
const languages = (this.languages && this.languages.length > 0 && this.languages) || LANGUAGES
|
86
|
+
|
87
|
+
return html`
|
88
|
+
${languages.map(
|
89
|
+
({ display, code }) => html`
|
90
|
+
<div data-record>
|
91
|
+
<label>${display}</label>
|
92
|
+
<input type="hidden" data-code value=${code} />
|
93
|
+
<input
|
94
|
+
type="text"
|
95
|
+
data-label
|
96
|
+
placeholder="label"
|
97
|
+
.value=${value?.[code] || ''}
|
98
|
+
?disabled=${this.disabled}
|
99
|
+
/>
|
100
|
+
</div>
|
101
|
+
`
|
102
|
+
)}
|
103
|
+
`
|
104
|
+
}
|
105
|
+
|
106
|
+
private onChange(e: Event) {
|
107
|
+
e.stopPropagation()
|
108
|
+
|
109
|
+
if (this.changing) {
|
110
|
+
return
|
111
|
+
}
|
112
|
+
|
113
|
+
this.changing = true
|
114
|
+
|
115
|
+
this.build()
|
116
|
+
|
117
|
+
this.changing = false
|
118
|
+
}
|
119
|
+
|
120
|
+
private build() {
|
121
|
+
var records = this.renderRoot.querySelectorAll('[data-record]') as NodeListOf<HTMLElement>
|
122
|
+
|
123
|
+
var newmap: I18nLabel = {}
|
124
|
+
|
125
|
+
for (var i = 0; i < records.length; i++) {
|
126
|
+
var record = records[i]
|
127
|
+
|
128
|
+
const code = (record.querySelector('[data-code]') as HTMLInputElement).value
|
129
|
+
const label = (record.querySelector('[data-label]') as HTMLInputElement).value
|
130
|
+
|
131
|
+
if (code) {
|
132
|
+
newmap[code] = label || ''
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
this.value = { ...this.value, ...newmap }
|
137
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))
|
138
|
+
}
|
139
|
+
}
|