@operato/input 8.0.0-beta.0 → 8.0.0-beta.2
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 +18 -0
- package/package.json +7 -7
- package/.editorconfig +0 -29
- package/.storybook/main.js +0 -3
- package/.storybook/preview.js +0 -52
- package/.storybook/server.mjs +0 -8
- package/demo/index-3dish.html +0 -38
- package/demo/index-angle.html +0 -36
- package/demo/index-barcode.html +0 -68
- package/demo/index-button-radio.html +0 -42
- package/demo/index-checkbox.html +0 -69
- package/demo/index-code.html +0 -61
- package/demo/index-color-gradient.html +0 -35
- package/demo/index-color-stops.html +0 -62
- package/demo/index-color.html +0 -35
- package/demo/index-crontab.html +0 -37
- package/demo/index-file.html +0 -41
- package/demo/index-image.html +0 -40
- package/demo/index-multiple-colors.html +0 -52
- package/demo/index-options.html +0 -43
- package/demo/index-partition-keys.html +0 -79
- package/demo/index-range.html +0 -40
- package/demo/index-select.html +0 -136
- package/demo/index-stack.html +0 -33
- package/demo/index-table.html +0 -50
- package/demo/index-value-map.html +0 -80
- package/demo/index-value-ranges.html +0 -80
- package/demo/index-work-shift.html +0 -59
- package/demo/index.html +0 -54
- package/src/index.ts +0 -35
- package/src/locale/locale-codes.ts +0 -18
- package/src/locale/locale-picker.ts +0 -43
- package/src/locale/localization.ts +0 -15
- package/src/locales/en.ts +0 -30
- package/src/locales/ko.ts +0 -30
- package/src/locales/ms.ts +0 -30
- package/src/locales/zh.ts +0 -30
- package/src/ox-buttons-radio.ts +0 -140
- package/src/ox-checkbox.ts +0 -181
- package/src/ox-form-field.ts +0 -53
- package/src/ox-input-3axis.ts +0 -77
- package/src/ox-input-3dish.ts +0 -211
- package/src/ox-input-angle.ts +0 -73
- package/src/ox-input-barcode.ts +0 -318
- package/src/ox-input-code.ts +0 -139
- package/src/ox-input-color-gradient.ts +0 -349
- package/src/ox-input-color-stops.ts +0 -525
- package/src/ox-input-color.ts +0 -338
- package/src/ox-input-container.ts +0 -32
- package/src/ox-input-crontab.ts +0 -595
- package/src/ox-input-data.ts +0 -219
- package/src/ox-input-direction.ts +0 -92
- package/src/ox-input-duration.ts +0 -175
- package/src/ox-input-file.ts +0 -209
- package/src/ox-input-hashtags.ts +0 -185
- package/src/ox-input-i18n-label.ts +0 -140
- package/src/ox-input-image.ts +0 -168
- package/src/ox-input-key-values.ts +0 -301
- package/src/ox-input-layout/ox-input-card-layout.ts +0 -58
- package/src/ox-input-layout/ox-input-grid-layout.ts +0 -64
- package/src/ox-input-layout/ox-input-layout.ts +0 -77
- package/src/ox-input-mass-fraction.ts +0 -437
- package/src/ox-input-multiple-colors.ts +0 -135
- package/src/ox-input-options.ts +0 -216
- package/src/ox-input-partition-keys.ts +0 -303
- package/src/ox-input-privilege.ts +0 -163
- package/src/ox-input-quantifier.ts +0 -62
- package/src/ox-input-range.ts +0 -146
- package/src/ox-input-scene-component-id.ts +0 -73
- package/src/ox-input-search.ts +0 -126
- package/src/ox-input-select-buttons.ts +0 -75
- package/src/ox-input-signature.ts +0 -208
- package/src/ox-input-stack.ts +0 -136
- package/src/ox-input-switch.ts +0 -117
- package/src/ox-input-table-column-config.ts +0 -211
- package/src/ox-input-table.ts +0 -404
- package/src/ox-input-textarea.ts +0 -86
- package/src/ox-input-unit-number.ts +0 -354
- package/src/ox-input-value-map.ts +0 -342
- package/src/ox-input-value-ranges.ts +0 -363
- package/src/ox-input-work-shift.ts +0 -290
- package/src/ox-select-floor.ts +0 -246
- package/src/ox-select.ts +0 -219
- package/stories/image-for-select-floor.ts +0 -2
- package/stories/ox-buttons-radio.stories.ts +0 -89
- package/stories/ox-checkbox.stories.ts +0 -111
- package/stories/ox-input-3axis.stories.ts +0 -77
- package/stories/ox-input-3dish.stories.ts +0 -106
- package/stories/ox-input-angle.stories.ts +0 -84
- package/stories/ox-input-barcode.stories.ts +0 -117
- package/stories/ox-input-code.stories.ts +0 -99
- package/stories/ox-input-crontab.stories.ts +0 -82
- package/stories/ox-input-data.stories.ts +0 -82
- package/stories/ox-input-direction.stories.ts +0 -91
- package/stories/ox-input-duration.stories.ts +0 -84
- package/stories/ox-input-file.stories.ts +0 -111
- package/stories/ox-input-hashtags.stories.ts +0 -82
- package/stories/ox-input-i18n-label.stories.ts +0 -103
- package/stories/ox-input-key-values.stories.ts +0 -97
- package/stories/ox-input-mass-fraction.stories.ts +0 -102
- package/stories/ox-input-multiple-colors.stories.ts +0 -72
- package/stories/ox-input-options.stories.ts +0 -80
- package/stories/ox-input-partition-keys.stories.ts +0 -84
- package/stories/ox-input-privilege.stories.ts +0 -108
- package/stories/ox-input-quantifier.stories.ts +0 -80
- package/stories/ox-input-range.stories.ts +0 -89
- package/stories/ox-input-search.stories.ts +0 -91
- package/stories/ox-input-select-buttons.stories.ts +0 -118
- package/stories/ox-input-signature.stories.ts +0 -75
- package/stories/ox-input-switch.stories.ts +0 -91
- package/stories/ox-input-table-column-config.stories.ts +0 -109
- package/stories/ox-input-unit.stories.ts +0 -151
- package/stories/ox-input-value-map.stories.ts +0 -92
- package/stories/ox-input-value-ranges.stories.ts +0 -92
- package/stories/ox-input-work-shift.stories.ts +0 -106
- package/stories/ox-select-floor.stories.ts +0 -197
- package/stories/ox-select-set-options.stories.ts +0 -208
- package/stories/ox-select.stories.ts +0 -181
- package/tsconfig.json +0 -25
- package/web-dev-server.config.mjs +0 -27
- package/web-test-runner.config.mjs +0 -41
package/src/ox-input-barcode.ts
DELETED
@@ -1,318 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* @license Copyright © HatioLab Inc. All rights reserved.
|
3
|
-
*/
|
4
|
-
|
5
|
-
import '@operato/popup/ox-popup.js'
|
6
|
-
|
7
|
-
import { css, html } from 'lit'
|
8
|
-
import { customElement, property, query, state } from 'lit/decorators.js'
|
9
|
-
import { scanImageData } from '@undecaf/zbar-wasm'
|
10
|
-
|
11
|
-
import { OxPopup } from '@operato/popup'
|
12
|
-
|
13
|
-
import { OxFormField } from './ox-form-field.js'
|
14
|
-
|
15
|
-
const barcodeIcon = `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAYBAMAAAAfR1CMAAADKGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFNjM4RURDQkQ1OUExMUU5QkExMkQ4NUY3NkMxNzBFOSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFNjM4RURDQ0Q1OUExMUU5QkExMkQ4NUY3NkMxNzBFOSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkU2MzhFREM5RDU5QTExRTlCQTEyRDg1Rjc2QzE3MEU5IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkU2MzhFRENBRDU5QTExRTlCQTEyRDg1Rjc2QzE3MEU5Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+55pr/QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAAkUExURQAAAEdwTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEus/7UCWQwAAAALdFJOU9YAg3wKBFBDSz9PnvQNDgAAAE9JREFUGNNjEEQFDKLJSnCOklkgg9QUJFn3RgZhRyS+iCGDEIp2RSBfQICRkRGIgTSQL4jCF6ScvxsZYOFT2T50/6D7Fz080MMLPTzRwhsAHVspfelur08AAAAASUVORK5CYII=`
|
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
|
-
*/
|
43
|
-
@customElement('ox-input-barcode')
|
44
|
-
export class OxInputBarcode extends OxFormField {
|
45
|
-
static styles = [
|
46
|
-
css`
|
47
|
-
:host {
|
48
|
-
display: flex;
|
49
|
-
align-items: center;
|
50
|
-
border: none;
|
51
|
-
background-color: transparent;
|
52
|
-
height: var(--ox-input-height, var(--form-element-height-medium));
|
53
|
-
}
|
54
|
-
|
55
|
-
* {
|
56
|
-
align-self: stretch;
|
57
|
-
}
|
58
|
-
|
59
|
-
*:focus {
|
60
|
-
outline: none;
|
61
|
-
}
|
62
|
-
|
63
|
-
input {
|
64
|
-
flex: 1;
|
65
|
-
width: 10px; /* intentionally width set */
|
66
|
-
border: var(--ox-input-border, 1px solid var(--md-sys-color-outline));
|
67
|
-
border-radius: var(--ox-input-radius, var(--md-sys-shape-corner-small));
|
68
|
-
background-color: var(--ox-input-background-color, var(--md-sys-color-on-primary));
|
69
|
-
color: var(--ox-input-color, var(--md-sys-color-on-primary-container));
|
70
|
-
font-size: var(--md-sys-typescale-label-large-size, 0.875rem);
|
71
|
-
|
72
|
-
box-sizing: border-box;
|
73
|
-
padding-right: 35px;
|
74
|
-
}
|
75
|
-
|
76
|
-
input:focus {
|
77
|
-
outline: none;
|
78
|
-
border-color: var(--md-sys-color-secondary-fixed-dim);
|
79
|
-
}
|
80
|
-
|
81
|
-
#scan-button {
|
82
|
-
display: block;
|
83
|
-
position: relative;
|
84
|
-
margin-left: -35px;
|
85
|
-
width: 35px;
|
86
|
-
border: none;
|
87
|
-
background: var(--barcodescan-input-button-icon) no-repeat center center;
|
88
|
-
}
|
89
|
-
|
90
|
-
#scan-button[hidden] {
|
91
|
-
display: none;
|
92
|
-
}
|
93
|
-
`
|
94
|
-
]
|
95
|
-
|
96
|
-
/**
|
97
|
-
* Indicates whether barcode scanning is enabled.
|
98
|
-
* @property {Boolean} scannable
|
99
|
-
*/
|
100
|
-
@property({ type: Boolean, attribute: true }) scannable?: boolean
|
101
|
-
|
102
|
-
/**
|
103
|
-
* If true, the "Enter" key press event is not fired after scanning a barcode.
|
104
|
-
* @property {Boolean} withoutEnter
|
105
|
-
*/
|
106
|
-
@property({ attribute: 'without-enter', type: Boolean }) withoutEnter?: boolean
|
107
|
-
|
108
|
-
/**
|
109
|
-
* The value of the input field.
|
110
|
-
* @property {String} declare value
|
111
|
-
*/
|
112
|
-
@property({ type: String }) declare value?: string
|
113
|
-
|
114
|
-
/**
|
115
|
-
* If true, only English characters are allowed in the input field.
|
116
|
-
* @property {Boolean} englishOnly
|
117
|
-
*/
|
118
|
-
@property({ attribute: 'english-only', type: Boolean }) englishOnly?: boolean
|
119
|
-
|
120
|
-
/**
|
121
|
-
* If true, the input field is automatically selected after a change event.
|
122
|
-
* @property {Boolean} selectAfterChange
|
123
|
-
*/
|
124
|
-
@property({ attribute: 'select-after-change', type: Boolean }) selectAfterChange?: boolean
|
125
|
-
|
126
|
-
@state() stream?: MediaStream
|
127
|
-
|
128
|
-
@query('input') input!: HTMLInputElement
|
129
|
-
|
130
|
-
private popup: OxPopup | null = null
|
131
|
-
private video: HTMLVideoElement | null = null
|
132
|
-
|
133
|
-
connectedCallback() {
|
134
|
-
super.connectedCallback()
|
135
|
-
|
136
|
-
if (navigator.mediaDevices) {
|
137
|
-
;(async () => {
|
138
|
-
try {
|
139
|
-
var stream = await navigator.mediaDevices.getUserMedia({ audio: false, video: { facingMode: 'environment' } })
|
140
|
-
if (stream) {
|
141
|
-
stream.getTracks().forEach(track => track.stop())
|
142
|
-
this.scannable = true
|
143
|
-
}
|
144
|
-
} catch (e) {
|
145
|
-
this.scannable = false
|
146
|
-
console.warn('this device not support camera for barcode scan', e)
|
147
|
-
}
|
148
|
-
})()
|
149
|
-
} else {
|
150
|
-
this.scannable = false
|
151
|
-
}
|
152
|
-
}
|
153
|
-
|
154
|
-
disconnectedCallback() {
|
155
|
-
this.stopScan()
|
156
|
-
}
|
157
|
-
|
158
|
-
render() {
|
159
|
-
this.style.setProperty('--barcodescan-input-button-icon', `url(${barcodeIcon})`)
|
160
|
-
|
161
|
-
return html`
|
162
|
-
<input
|
163
|
-
type="text"
|
164
|
-
.value=${this.value || ''}
|
165
|
-
@change=${(e: Event) => this.onInputChange(e)}
|
166
|
-
@keydown=${(e: KeyboardEvent) => this.onInputKeyDown(e)}
|
167
|
-
?disabled=${this.disabled}
|
168
|
-
/>
|
169
|
-
<button
|
170
|
-
?hidden=${!this.scannable}
|
171
|
-
id="scan-button"
|
172
|
-
@click=${(e: MouseEvent) => {
|
173
|
-
this.scan(e)
|
174
|
-
}}
|
175
|
-
?disabled=${this.disabled}
|
176
|
-
></button>
|
177
|
-
`
|
178
|
-
}
|
179
|
-
|
180
|
-
onInputChange(e: Event) {
|
181
|
-
e.stopPropagation()
|
182
|
-
|
183
|
-
if (this.englishOnly) {
|
184
|
-
/* englishOnly 인 경우에는 멀티바이트 문자들을 모두 제거한다. */
|
185
|
-
this.value = this.input.value = this.input.value?.replace(/[^\x00-\x7F]/g, '')
|
186
|
-
} else {
|
187
|
-
this.value = this.input.value
|
188
|
-
}
|
189
|
-
|
190
|
-
if (this.selectAfterChange) {
|
191
|
-
requestAnimationFrame(() => {
|
192
|
-
this.input.select()
|
193
|
-
})
|
194
|
-
}
|
195
|
-
|
196
|
-
this.dispatchEvent(
|
197
|
-
new CustomEvent('change', {
|
198
|
-
detail: this.value
|
199
|
-
})
|
200
|
-
)
|
201
|
-
}
|
202
|
-
|
203
|
-
onInputKeyDown(e: KeyboardEvent) {
|
204
|
-
if (e.key === 'Enter' && !e.isComposing) {
|
205
|
-
/* Even if the value has not changed, the enter key triggers a change event. */
|
206
|
-
e.preventDefault() /* Prevent change event from occurring twice. */
|
207
|
-
|
208
|
-
this.input.dispatchEvent(new CustomEvent('change'))
|
209
|
-
} else if (this.englishOnly && !e.metaKey && !e.ctrlKey && !e.altKey && /^Key/.test(e.code)) {
|
210
|
-
e.stopPropagation()
|
211
|
-
e.preventDefault()
|
212
|
-
|
213
|
-
/* englishOnly 인 경우에 문자들은 여기에서 처리한다. 멀티바이트 문자들이 대부분 알파벳의 자모음을 조합하므로, ... */
|
214
|
-
const key = e.shiftKey ? e.code.charAt(3) : e.code.charAt(3).toLowerCase()
|
215
|
-
const value = this.input.value
|
216
|
-
|
217
|
-
const start = this.input.selectionStart || 0
|
218
|
-
const end = this.input.selectionEnd || start
|
219
|
-
|
220
|
-
this.input.value = [value.substring(0, start), key, value.substring(end)].join('')
|
221
|
-
this.input.setSelectionRange(start + 1, start + 1)
|
222
|
-
}
|
223
|
-
}
|
224
|
-
|
225
|
-
async scan(e: MouseEvent) {
|
226
|
-
try {
|
227
|
-
if (this.popup) {
|
228
|
-
this.stopScan()
|
229
|
-
}
|
230
|
-
|
231
|
-
this.popup = OxPopup.open({
|
232
|
-
template: html`
|
233
|
-
<video></video>
|
234
|
-
<md-icon
|
235
|
-
style="position: fixed; right: 0; top: 0; color: red; tabindex: 0"
|
236
|
-
@click=${() => {
|
237
|
-
this.stopScan()
|
238
|
-
}}
|
239
|
-
>close</md-icon
|
240
|
-
>
|
241
|
-
`,
|
242
|
-
width: '100vw',
|
243
|
-
height: '100dvh'
|
244
|
-
})
|
245
|
-
|
246
|
-
this.video! = this.popup.querySelector('video') as HTMLVideoElement
|
247
|
-
|
248
|
-
var constraints = { audio: false, video: { facingMode: 'environment' } } /* backside camera first */
|
249
|
-
this.stream = await navigator.mediaDevices.getUserMedia(constraints)
|
250
|
-
|
251
|
-
this.video.srcObject = this.stream
|
252
|
-
this.video.play()
|
253
|
-
|
254
|
-
this.video.onloadedmetadata = async e => {
|
255
|
-
var canvas = new OffscreenCanvas(
|
256
|
-
this.video!.videoWidth || this.video!.width,
|
257
|
-
this.video!.videoHeight || this.video!.height
|
258
|
-
)
|
259
|
-
|
260
|
-
var context = canvas.getContext('2d', {
|
261
|
-
willReadFrequently: true
|
262
|
-
})
|
263
|
-
|
264
|
-
const detect = async () => {
|
265
|
-
try {
|
266
|
-
if (!this.stream?.active) {
|
267
|
-
return
|
268
|
-
}
|
269
|
-
|
270
|
-
context!.drawImage(this.video!, 0, 0, canvas.width, canvas.height)
|
271
|
-
const imageData = context!.getImageData(0, 0, canvas.width, canvas.height)
|
272
|
-
const symbols = await scanImageData(imageData)
|
273
|
-
const result = symbols[0]?.decode()
|
274
|
-
|
275
|
-
if (result) {
|
276
|
-
this.stopScan()
|
277
|
-
|
278
|
-
var input = this.input
|
279
|
-
input.focus()
|
280
|
-
this.value = input.value = String(result)
|
281
|
-
|
282
|
-
if (!this.withoutEnter) {
|
283
|
-
input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }))
|
284
|
-
}
|
285
|
-
} else {
|
286
|
-
requestAnimationFrame(async () => await detect())
|
287
|
-
}
|
288
|
-
} catch (e) {
|
289
|
-
console.warn(e)
|
290
|
-
this.stopScan()
|
291
|
-
}
|
292
|
-
}
|
293
|
-
|
294
|
-
await detect()
|
295
|
-
}
|
296
|
-
} catch (err) {
|
297
|
-
/*
|
298
|
-
* 1. stream device 문제로 예외 발생한 경우.
|
299
|
-
* 2. 뒤로가기 등으로 popup이 종료된 경우에도 NotFoundException: Video stream has ended before any code could be detected. 이 발생한다.
|
300
|
-
*/
|
301
|
-
console.warn(err)
|
302
|
-
}
|
303
|
-
}
|
304
|
-
|
305
|
-
stopScan() {
|
306
|
-
if (this.video) {
|
307
|
-
this.video.pause()
|
308
|
-
this.video.srcObject = null
|
309
|
-
}
|
310
|
-
|
311
|
-
if (this.popup) {
|
312
|
-
this.popup.close()
|
313
|
-
this.popup = null
|
314
|
-
}
|
315
|
-
|
316
|
-
this.stream?.getTracks().forEach(track => track.stop())
|
317
|
-
}
|
318
|
-
}
|
package/src/ox-input-code.ts
DELETED
@@ -1,139 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* @license Copyright © HatioLab Inc. All rights reserved.
|
3
|
-
*/
|
4
|
-
|
5
|
-
import { css, PropertyValues } from 'lit'
|
6
|
-
import { customElement, property } from 'lit/decorators.js'
|
7
|
-
|
8
|
-
import { minimalSetup } from 'codemirror'
|
9
|
-
import { history, historyKeymap, indentWithTab } from '@codemirror/commands'
|
10
|
-
import { EditorView, highlightActiveLine, keymap, lineNumbers } from '@codemirror/view'
|
11
|
-
import { autocompletion, closeBrackets } from '@codemirror/autocomplete'
|
12
|
-
import { bracketMatching, LanguageSupport, syntaxHighlighting } from '@codemirror/language'
|
13
|
-
import { oneDarkHighlightStyle, oneDark } from '@codemirror/theme-one-dark'
|
14
|
-
|
15
|
-
import { javascript } from '@codemirror/lang-javascript'
|
16
|
-
import { sql } from '@codemirror/lang-sql'
|
17
|
-
import { json } from '@codemirror/lang-json'
|
18
|
-
|
19
|
-
import { ScrollbarStyles } from '@operato/styles'
|
20
|
-
import { togglefullscreen } from '@operato/utils'
|
21
|
-
|
22
|
-
import { OxFormField } from './ox-form-field'
|
23
|
-
|
24
|
-
/**
|
25
|
-
WEB Component for code-mirror code editor.
|
26
|
-
|
27
|
-
Example:
|
28
|
-
|
29
|
-
<ox-input-code .value=${text} language="javascript" show-line-numbers>
|
30
|
-
</ox-input-code>
|
31
|
-
*/
|
32
|
-
@customElement('ox-input-code')
|
33
|
-
export class OxInputCode extends OxFormField {
|
34
|
-
static styles = [
|
35
|
-
ScrollbarStyles,
|
36
|
-
css`
|
37
|
-
:host {
|
38
|
-
display: flex;
|
39
|
-
flex-direction: column;
|
40
|
-
position: relative;
|
41
|
-
background: white;
|
42
|
-
overflow: auto;
|
43
|
-
border:1px solid var(--md-sys-color-outline);
|
44
|
-
border-radius:var(--md-sys-shape-corner-small);
|
45
|
-
}
|
46
|
-
|
47
|
-
.cm-editor {
|
48
|
-
flex: 1;
|
49
|
-
|
50
|
-
}
|
51
|
-
`
|
52
|
-
]
|
53
|
-
|
54
|
-
/**
|
55
|
-
* `value`는 에디터에서 작성중인 contents이다.
|
56
|
-
*/
|
57
|
-
@property({ type: String }) value: string = ''
|
58
|
-
@property({ type: Boolean, attribute: 'show-line-numbers' }) showLineNumbers: boolean = false
|
59
|
-
@property({ type: String }) language?: string = 'javascript'
|
60
|
-
|
61
|
-
private _self_changing: boolean = false
|
62
|
-
private _editor?: EditorView
|
63
|
-
private _changed: boolean = false
|
64
|
-
|
65
|
-
updated(changes: PropertyValues<this>) {
|
66
|
-
if (changes.has('value') && this.editor && !this._self_changing) {
|
67
|
-
const to = this.editor.state.doc.toString().length
|
68
|
-
this.editor.dispatch({
|
69
|
-
changes: { from: 0, to, insert: this.value === undefined ? '' : String(this.value) }
|
70
|
-
})
|
71
|
-
}
|
72
|
-
}
|
73
|
-
|
74
|
-
get editor() {
|
75
|
-
if (!this._editor) {
|
76
|
-
let language: LanguageSupport[] = []
|
77
|
-
switch (this.language) {
|
78
|
-
case 'sql':
|
79
|
-
language = [sql()]
|
80
|
-
break
|
81
|
-
case 'json':
|
82
|
-
language = [json()]
|
83
|
-
break
|
84
|
-
case 'javascript':
|
85
|
-
language = [javascript()]
|
86
|
-
break
|
87
|
-
default:
|
88
|
-
break
|
89
|
-
}
|
90
|
-
|
91
|
-
this._editor = new EditorView({
|
92
|
-
doc: this.value,
|
93
|
-
extensions: [
|
94
|
-
minimalSetup,
|
95
|
-
...language,
|
96
|
-
...(this.showLineNumbers ? [lineNumbers()] : []),
|
97
|
-
bracketMatching(),
|
98
|
-
closeBrackets(),
|
99
|
-
history(),
|
100
|
-
autocompletion(),
|
101
|
-
oneDark,
|
102
|
-
syntaxHighlighting(oneDarkHighlightStyle),
|
103
|
-
highlightActiveLine(),
|
104
|
-
keymap.of([...historyKeymap, indentWithTab]),
|
105
|
-
EditorView.updateListener.of(async v => {
|
106
|
-
if (v.docChanged) {
|
107
|
-
this._self_changing = true
|
108
|
-
this._changed = true
|
109
|
-
|
110
|
-
await this.updateComplete
|
111
|
-
|
112
|
-
this._self_changing = false
|
113
|
-
}
|
114
|
-
})
|
115
|
-
],
|
116
|
-
parent: this.renderRoot
|
117
|
-
})
|
118
|
-
}
|
119
|
-
|
120
|
-
this._editor.contentDOM.addEventListener('keydown', event => {
|
121
|
-
event.stopPropagation()
|
122
|
-
|
123
|
-
if (event.key === 'Escape') {
|
124
|
-
togglefullscreen(this._editor!.contentDOM)
|
125
|
-
}
|
126
|
-
})
|
127
|
-
|
128
|
-
this._editor.contentDOM.addEventListener('blur', e => {
|
129
|
-
if (!this._changed) {
|
130
|
-
return
|
131
|
-
}
|
132
|
-
|
133
|
-
this.value = this._editor!.state.doc.toString()
|
134
|
-
this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: this.value }))
|
135
|
-
})
|
136
|
-
|
137
|
-
return this._editor
|
138
|
-
}
|
139
|
-
}
|