@es-labs/jslib 0.0.1
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 +4 -0
- package/README.md +42 -0
- package/__test__/services.test.js +32 -0
- package/auth/index.js +226 -0
- package/auth/keyv.js +23 -0
- package/auth/knex.js +29 -0
- package/auth/redis.js +23 -0
- package/comms/email.js +123 -0
- package/comms/nexmo.js +44 -0
- package/comms/telegram.js +43 -0
- package/comms/telegram2/inbound.js +314 -0
- package/comms/telegram2/outbound.js +574 -0
- package/comms/webpush.js +60 -0
- package/config.js +37 -0
- package/express/controller/auth/oauth.js +39 -0
- package/express/controller/auth/oidc.js +87 -0
- package/express/controller/auth/own.js +100 -0
- package/express/controller/auth/saml.js +74 -0
- package/express/upload.js +48 -0
- package/index.js +1 -0
- package/iso/README.md +4 -0
- package/iso/__tests__/csv-utils.spec.js +128 -0
- package/iso/__tests__/datetime.spec.js +101 -0
- package/iso/__tests__/fetch.spec.js +270 -0
- package/iso/csv-utils.js +206 -0
- package/iso/datetime.js +103 -0
- package/iso/fetch.js +129 -0
- package/iso/fetch2.js +180 -0
- package/iso/log-filter.js +17 -0
- package/iso/sleep.js +6 -0
- package/iso/ws.js +63 -0
- package/node/oss-files/oss-uploader-client-fetch.js +258 -0
- package/node/oss-files/oss-uploader-client-fetch.md +31 -0
- package/node/oss-files/oss-uploader-client.js +219 -0
- package/node/oss-files/oss-uploader-server.js +199 -0
- package/node/oss-files/oss-uploader-usage.js +121 -0
- package/node/oss-files/oss-uploader-usage.md +34 -0
- package/node/oss-files/s3-uploader-client.js +217 -0
- package/node/oss-files/s3-uploader-server.js +123 -0
- package/node/oss-files/s3-uploader-usage.js +77 -0
- package/node/oss-files/s3-uploader-usage.md +34 -0
- package/package.json +53 -0
- package/packageInfo.js +9 -0
- package/services/ali.js +279 -0
- package/services/aws.js +194 -0
- package/services/db/__tests__/keyv.spec.js +31 -0
- package/services/db/keyv.js +14 -0
- package/services/db/knex.js +67 -0
- package/services/db/redis.js +51 -0
- package/services/index.js +57 -0
- package/services/mq/README.md +8 -0
- package/services/websocket.js +139 -0
- package/t4t/README.md +1 -0
- package/traps.js +20 -0
- package/utils/__tests__/aes.spec.js +52 -0
- package/utils/aes.js +23 -0
- package/web/UI.md +71 -0
- package/web/bwc-autocomplete.js +211 -0
- package/web/bwc-combobox.js +343 -0
- package/web/bwc-fileupload.js +87 -0
- package/web/bwc-loading-overlay.js +54 -0
- package/web/bwc-t4t-form.js +511 -0
- package/web/bwc-table.js +756 -0
- package/web/fetch.js +129 -0
- package/web/i18n.js +24 -0
- package/web/idle.js +49 -0
- package/web/parse-jwt.js +15 -0
- package/web/pwa.js +84 -0
- package/web/sign-pad.js +164 -0
- package/web/t4t-fe.js +164 -0
- package/web/util.js +126 -0
- package/web/web-cam.js +182 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
// Combobox with autocomplete component using input, datalist and tags
|
|
2
|
+
// TODO fix multi select init in bwc-t4t-form.js
|
|
3
|
+
// TODO Initially if no data for the list, please fetch some
|
|
4
|
+
// TODO allow configurable classnames for tag and tag wrapper, clear icons (for bootstrap, muicss)
|
|
5
|
+
// TODO single select clear value if not found and custom tags not allowed
|
|
6
|
+
// TODO use ul/li instead of datalist (big change)
|
|
7
|
+
// OR https://stackoverflow.com/questions/30022728/perform-action-when-clicking-html5-datalist-option
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
attributes:
|
|
11
|
+
- value (via v-model), at the text input
|
|
12
|
+
- required
|
|
13
|
+
- disabled
|
|
14
|
+
- listid (needed if using more than 2 components on the same page)
|
|
15
|
+
- input-class (style the input)
|
|
16
|
+
- multiple (v2)
|
|
17
|
+
- repeat (v2) for multiple selects allow same item to be selected many times
|
|
18
|
+
- allow-custom-tag (v2) - allow user defined tags
|
|
19
|
+
- object-key
|
|
20
|
+
- object-text
|
|
21
|
+
- tag-limit - maximum allowed tags
|
|
22
|
+
|
|
23
|
+
properties:
|
|
24
|
+
- items [string] or [{ key, text }]
|
|
25
|
+
- tags [string] or [{ key, text }]
|
|
26
|
+
|
|
27
|
+
methods:
|
|
28
|
+
- _setList(items) // should be private, called when items property changes
|
|
29
|
+
- _setTags(tags) // should be private, called with tags property changes
|
|
30
|
+
|
|
31
|
+
events emitted:
|
|
32
|
+
- @input (via v-model) - e.target.value
|
|
33
|
+
- @search - e.detail String
|
|
34
|
+
- @select - e.detail String or Object or null
|
|
35
|
+
|
|
36
|
+
if selected data is null (no match found, else match found)
|
|
37
|
+
|
|
38
|
+
Usage with (VueJS):
|
|
39
|
+
|
|
40
|
+
<bwc-combobox required :items="ac.items" v-model="ac.value" @search="(e) => autoComplete(e)" @select="(e) => selectItem"></bwc-combobox>
|
|
41
|
+
|
|
42
|
+
// string version
|
|
43
|
+
const ac = reactive({ value: 'a', items: ['aa9','aa5'] })
|
|
44
|
+
|
|
45
|
+
const autoComplete = (e) => {
|
|
46
|
+
const list = ['aa1', 'aa15', 'aa16', 'aa17', 'aa18', 'aa19', 'aa20', 'aa21', 'aa22', 'aa23']
|
|
47
|
+
const result = []
|
|
48
|
+
for (let i = 0; i < list.length; i++) {
|
|
49
|
+
if (list[i].includes(e.detail)) result.push(list[i])
|
|
50
|
+
}
|
|
51
|
+
ac.items = result
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// object version
|
|
55
|
+
[
|
|
56
|
+
{ key: 'unique', text: 'longer description' }
|
|
57
|
+
]
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
const template = document.createElement('template')
|
|
61
|
+
template.innerHTML = /*html*/`
|
|
62
|
+
<input type="text" list="json-datalist" placeholder="search..." autocomplete="off">
|
|
63
|
+
<span class="icon is-small is-left clear-btn" style="pointer-events: all; cursor:pointer;">
|
|
64
|
+
<i class="fas fa-times"></i>
|
|
65
|
+
</span>
|
|
66
|
+
<datalist id="json-datalist"></datalist>
|
|
67
|
+
`
|
|
68
|
+
|
|
69
|
+
class BwcCombobox extends HTMLElement {
|
|
70
|
+
// local properties
|
|
71
|
+
#items = [] // list of items
|
|
72
|
+
#tags = [] // multi-select
|
|
73
|
+
#selected = null // single-select
|
|
74
|
+
#key = '' // must have both, other wise string is assumed?
|
|
75
|
+
#text = ''
|
|
76
|
+
|
|
77
|
+
#elTags = null // div.tags element
|
|
78
|
+
#elInput = null // input element
|
|
79
|
+
#elList = null // datalist element
|
|
80
|
+
#elClearBtn = null // clear button
|
|
81
|
+
|
|
82
|
+
#multiple = false // hold readonly attributes
|
|
83
|
+
#repeat = false // for multiselect, tag can be added multiple times
|
|
84
|
+
#allowCustomTag = false // can add new items
|
|
85
|
+
#tagLimit = 0 // unlimited tags
|
|
86
|
+
|
|
87
|
+
constructor() {
|
|
88
|
+
super()
|
|
89
|
+
this._onInput = this._onInput.bind(this)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
connectedCallback() {
|
|
93
|
+
this.appendChild(template.content.cloneNode(true))
|
|
94
|
+
|
|
95
|
+
this.#elInput = this.querySelector('input')
|
|
96
|
+
this.#elList = this.querySelector('datalist')
|
|
97
|
+
this.#elClearBtn = this.querySelector('.clear-btn')
|
|
98
|
+
|
|
99
|
+
this.#elList.id = this.listid // console.log('listid', this.listid)
|
|
100
|
+
this.#elInput.setAttribute('list', this.listid)
|
|
101
|
+
this.#elInput.addEventListener('input', this._onInput)
|
|
102
|
+
|
|
103
|
+
this.#allowCustomTag = this.hasAttribute('allow-custom-tag')
|
|
104
|
+
this.#multiple = this.hasAttribute('multiple')
|
|
105
|
+
this.#repeat = this.hasAttribute('repeat')
|
|
106
|
+
|
|
107
|
+
if (this.hasAttribute('tag-limit')) this.#tagLimit = Number(this.getAttribute('tag-limit'))
|
|
108
|
+
if (this.hasAttribute('object-key')) this.#key = this.getAttribute('object-key')
|
|
109
|
+
if (this.hasAttribute('object-text')) this.#text = this.getAttribute('object-text')
|
|
110
|
+
|
|
111
|
+
if (this.#multiple) { // if multiple... use tags
|
|
112
|
+
this.#elTags = document.createElement('div')
|
|
113
|
+
this.#elTags.className = 'tags'
|
|
114
|
+
// this.prepend(this.#elTags)
|
|
115
|
+
this.append(this.#elTags)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this.#elClearBtn.onclick = (e) => {
|
|
119
|
+
this.#elInput.value = ''
|
|
120
|
+
if (!this.#multiple) {
|
|
121
|
+
// console.log('clear button click')
|
|
122
|
+
this.#selected = null
|
|
123
|
+
this.dispatchEvent(new CustomEvent('select', { detail: this.#selected }))
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
this.#elInput.onblur = (e) => {
|
|
127
|
+
// console.log('onblur', e)
|
|
128
|
+
const found = this.items.find(item => this._itemMatchInput(item))
|
|
129
|
+
if (this.#multiple) {
|
|
130
|
+
// multiple
|
|
131
|
+
if (!found) { // not found
|
|
132
|
+
if (this.#allowCustomTag) { // can add new
|
|
133
|
+
this._addTag(this._makeItemFromValue())
|
|
134
|
+
this.dispatchEvent(new CustomEvent('select', { detail: this.#tags }))
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
// if repeatable? set tags list if not there already
|
|
138
|
+
this._addTag(found)
|
|
139
|
+
this.dispatchEvent(new CustomEvent('select', { detail: this.#tags }))
|
|
140
|
+
}
|
|
141
|
+
this.value = ''
|
|
142
|
+
} else {
|
|
143
|
+
// single
|
|
144
|
+
if (!found) { // not found
|
|
145
|
+
if (this.#selected) {
|
|
146
|
+
console.log('onBlur - single select - not found and this.#selected truthy')
|
|
147
|
+
this.#selected = null
|
|
148
|
+
this.dispatchEvent(new CustomEvent('select', { detail: this.#selected }))
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
if (!this.#selected) {
|
|
152
|
+
console.log('onBlur - single select - found and this.#selected falsy')
|
|
153
|
+
this.#selected = found
|
|
154
|
+
this.dispatchEvent(new CustomEvent('select', { detail: this.#selected }))
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// console.log('combo box connected', this.required, this.disabled, this.inputClass)
|
|
161
|
+
this.#elInput.value = this.value
|
|
162
|
+
this.#elInput.className = this.inputClass || 'input' // default to bulma - // if (this.hasAttribute('input-class')) el.setAttribute('class', this.getAttribute('input-class'))
|
|
163
|
+
this.required ? this.#elInput.setAttribute('required', '') : this.#elInput.removeAttribute('required')
|
|
164
|
+
this.disabled ? this.#elInput.setAttribute('disabled', '') : this.#elInput.removeAttribute('disabled')
|
|
165
|
+
this._setList(this.items)
|
|
166
|
+
|
|
167
|
+
this.dispatchEvent(new CustomEvent('load'))
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
disconnectedCallback() {
|
|
171
|
+
this.#elInput.removeEventListener('input', this._onInput)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
175
|
+
const el = this.#elInput
|
|
176
|
+
switch (name) {
|
|
177
|
+
case 'value': {
|
|
178
|
+
if (el) el.value = newVal // v-model affects this
|
|
179
|
+
this.dispatchEvent(new CustomEvent('input', { detail: newVal }))
|
|
180
|
+
break
|
|
181
|
+
}
|
|
182
|
+
case 'required': {
|
|
183
|
+
el && el.setAttribute('required', '')
|
|
184
|
+
break
|
|
185
|
+
}
|
|
186
|
+
case 'disabled': {
|
|
187
|
+
el && el.setAttribute('disabled', '')
|
|
188
|
+
break
|
|
189
|
+
}
|
|
190
|
+
case 'input-class': {
|
|
191
|
+
if (el) el.className = newVal
|
|
192
|
+
break
|
|
193
|
+
}
|
|
194
|
+
default:
|
|
195
|
+
break
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
static get observedAttributes() {
|
|
200
|
+
return ['value', 'required', 'listid', 'disabled', 'input-class']
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
get value() { return this.getAttribute('value') }
|
|
204
|
+
set value(val) { this.setAttribute('value', val) }
|
|
205
|
+
|
|
206
|
+
get required() { return this.hasAttribute('required') }
|
|
207
|
+
set required(val) { val ? this.setAttribute('required', '') : this.removeAttribute('required') }
|
|
208
|
+
|
|
209
|
+
get listid() { return this.getAttribute('listid') }
|
|
210
|
+
set listid(val) { this.setAttribute('listid', val) }
|
|
211
|
+
|
|
212
|
+
get disabled() { return this.hasAttribute('disabled') }
|
|
213
|
+
set disabled(val) { val ? this.setAttribute('disabled', '') : this.removeAttribute('disabled') }
|
|
214
|
+
|
|
215
|
+
get inputClass() { return this.getAttribute('input-class') }
|
|
216
|
+
set inputClass(val) { this.setAttribute('input-class', val) }
|
|
217
|
+
|
|
218
|
+
// properties
|
|
219
|
+
get items() { return this.#items }
|
|
220
|
+
set items(val) {
|
|
221
|
+
// console.log('set items', val.length)
|
|
222
|
+
this.#items = val
|
|
223
|
+
this._setList(val)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// multi-select
|
|
227
|
+
get tags() { return this.#tags }
|
|
228
|
+
set tags(val) { this._setTags(val) } // this.#tags will be set in _setTags()
|
|
229
|
+
|
|
230
|
+
// single-select
|
|
231
|
+
get selected() { return this.#selected }
|
|
232
|
+
set selected(val) { this.#selected = val } // TODO set it correctly
|
|
233
|
+
|
|
234
|
+
_isStringType() { // is list item and selected values string ?
|
|
235
|
+
return !(this.#key && this.#text) // console.log('_isStringType', !(this.#key && this.#text))
|
|
236
|
+
}
|
|
237
|
+
_itemMatchInput(item) { // item match to text input
|
|
238
|
+
return this._isStringType() ? item === this.value : item[this.#key] === this.value || item[this.#text] === this.value
|
|
239
|
+
}
|
|
240
|
+
_matchItems(item1, item2) { // item match to another item
|
|
241
|
+
if (item1 === null && item2 === null) return true
|
|
242
|
+
else if (item1 === null) return false
|
|
243
|
+
else if (item2 === null) return false
|
|
244
|
+
return this._isStringType() ? item1 === item2 : item1[this.#key] === item2[this.#key] || item1[this.#text] === item2[this.#text]
|
|
245
|
+
}
|
|
246
|
+
_makeItemFromValue () {
|
|
247
|
+
// TODO if all spaces only... return? trim white spaces?
|
|
248
|
+
return this._isStringType() ? this.value : { [this.#key]: this.value, [this.#text]: this.value }
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
_tagLimitReached() {
|
|
252
|
+
return this.#tagLimit && this.#elTags.children.length >= this.#tagLimit
|
|
253
|
+
}
|
|
254
|
+
_addTag(item) {
|
|
255
|
+
if (this._tagLimitReached()) return
|
|
256
|
+
const itemExists = this.#tags.find(tag => this._isStringType() ? tag === item : (tag[this.#key] === item[this.#key] && tag[this.#text] === item[this.#text]))
|
|
257
|
+
if (!this.#repeat && itemExists) return // duplicates not allowed
|
|
258
|
+
const span = document.createElement('span')
|
|
259
|
+
span.className = 'tag is-black'
|
|
260
|
+
span.innerText = this._isStringType() ? item : item[this.#text]
|
|
261
|
+
span.value = this._isStringType() ? item : item[this.#key]
|
|
262
|
+
span.onclick = (e) => { // e.target.innerText, e.target.value
|
|
263
|
+
this._removeTag(span)
|
|
264
|
+
this.dispatchEvent(new CustomEvent('select', { detail: this.#tags }))
|
|
265
|
+
}
|
|
266
|
+
this.#elTags.appendChild(span)
|
|
267
|
+
this._updateTags()
|
|
268
|
+
if (this._tagLimitReached()) this.#elInput.setAttribute('disabled', '')
|
|
269
|
+
}
|
|
270
|
+
_updateTags() {
|
|
271
|
+
let tags = [...this.#elTags.children]
|
|
272
|
+
this.#tags = tags.map(tag => this._isStringType() ? tag.innerText : ({ [this.#key]: tag.value, [this.#text]: tag.innerText }))
|
|
273
|
+
// console.log('_updateTags', this.#tags)
|
|
274
|
+
}
|
|
275
|
+
_removeTag(span) {
|
|
276
|
+
this.#elTags.removeChild(span)
|
|
277
|
+
this._updateTags()
|
|
278
|
+
if (!this._tagLimitReached() && !this.disabled) this.#elInput.removeAttribute('disabled')
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
_onInput(e) { // whether clicked or typed
|
|
282
|
+
// console.log('_onInput', e.target.value, this.items.length)
|
|
283
|
+
const prevItem = this.#selected
|
|
284
|
+
this.value = this.#elInput.value
|
|
285
|
+
|
|
286
|
+
const found = this.items.find(item => this._itemMatchInput(item))
|
|
287
|
+
if (!found) { // not found
|
|
288
|
+
this.#selected = null
|
|
289
|
+
this.dispatchEvent(new CustomEvent('search', { detail: this.value }))
|
|
290
|
+
} else {
|
|
291
|
+
this.#selected = found
|
|
292
|
+
}
|
|
293
|
+
if (!this._matchItems(prevItem, this.#selected) && !this.#multiple) {
|
|
294
|
+
console.log('_onInput - selected && provItem not match this.#selected')
|
|
295
|
+
if (!this.#selected && this.allowCustomTag) {
|
|
296
|
+
this.#selected = this._makeItemFromValue()
|
|
297
|
+
}
|
|
298
|
+
this.dispatchEvent(new CustomEvent('select', { detail: this.#selected }))
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
_setTags(_tags) {
|
|
303
|
+
// console.log('_setTags', _tags, this.#elTags)
|
|
304
|
+
if (!this.#elTags) return
|
|
305
|
+
this.#elTags.innerHTML = ''
|
|
306
|
+
this.#tags = []
|
|
307
|
+
_tags.forEach(tag => this._addTag(tag))
|
|
308
|
+
this.dispatchEvent(new CustomEvent('select', { detail: this.#tags }))
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
_setList(_items) { // set list items
|
|
312
|
+
// console.log('items', _items, this.value)
|
|
313
|
+
const dd = this.#elList
|
|
314
|
+
if (!dd) return
|
|
315
|
+
while(dd.firstChild) {
|
|
316
|
+
dd.removeChild(dd.lastChild)
|
|
317
|
+
}
|
|
318
|
+
if (typeof _items !== 'object') return
|
|
319
|
+
|
|
320
|
+
// EVENT TEST START
|
|
321
|
+
// dd.style.pointerEvents = 'all'
|
|
322
|
+
// dd.style.cursor = 'pointer'
|
|
323
|
+
// dd.onclick = (e) => console.log('whwhwhwh22a')
|
|
324
|
+
// dd.onmousedown = (e) => console.log('whwhwhwh22b')
|
|
325
|
+
// EVENT TEST END
|
|
326
|
+
_items.forEach((item) => {
|
|
327
|
+
const li = document.createElement('option')
|
|
328
|
+
li.innerHTML = typeof item === 'string' ? item : item[this.#key]
|
|
329
|
+
li.value = typeof item === 'string' ? item : item[this.#text]
|
|
330
|
+
// EVENT TEST START
|
|
331
|
+
// li.style.pointerEvents = 'all'
|
|
332
|
+
// li.style.cursor = 'pointer'
|
|
333
|
+
// li.onclick = (e) => console.log('whwhwhwha')
|
|
334
|
+
// li.onmousedown = (e) => console.log('whwhwhwhb')
|
|
335
|
+
// li.addEventListener('click', (e) => console.log('whwhwhwh'), true)
|
|
336
|
+
// li.onmousedown // useless on a datalist with listid...
|
|
337
|
+
// EVENT TEST END
|
|
338
|
+
dd.appendChild(li)
|
|
339
|
+
})
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
customElements.define('bwc-combobox', BwcCombobox)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file
|
|
2
|
+
// attributes: {
|
|
3
|
+
// accept: { type: String, default: '*/*' },
|
|
4
|
+
// value: { type: Object, required: true }
|
|
5
|
+
// },
|
|
6
|
+
|
|
7
|
+
// attributes
|
|
8
|
+
// accept: One or more unique file type specifiers describing file types to allow
|
|
9
|
+
// capture: What source to use for capturing image or video data
|
|
10
|
+
// files: A FileList listing the chosen files
|
|
11
|
+
// multiple: A Boolean which, if present, indicates that the user may choose more than one file
|
|
12
|
+
// required
|
|
13
|
+
// value
|
|
14
|
+
|
|
15
|
+
// Events change and input
|
|
16
|
+
const html = /*html*/`
|
|
17
|
+
<input type="text" readonly />
|
|
18
|
+
<input type="file" style="display: none" accept="text/csv" onclick="event.stopPropagation()" />
|
|
19
|
+
`
|
|
20
|
+
|
|
21
|
+
class BwcFileupload extends HTMLElement {
|
|
22
|
+
files = null
|
|
23
|
+
|
|
24
|
+
constructor() {
|
|
25
|
+
super()
|
|
26
|
+
this.click = this.click.bind(this)
|
|
27
|
+
this.change = this.change.bind(this)
|
|
28
|
+
// const shadowRoot = this.attachShadow({ mode: 'open' })
|
|
29
|
+
// const template = document.createElement('template')
|
|
30
|
+
// template.innerHTML = html
|
|
31
|
+
// // this.shadowRoot.appendChild(template.content.cloneNode(true))
|
|
32
|
+
// shadowRoot.innerHTML = html
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
connectedCallback() {
|
|
36
|
+
// this.appendChild(template.content.cloneNode(true))
|
|
37
|
+
this.innerHTML = html
|
|
38
|
+
this.files = null
|
|
39
|
+
|
|
40
|
+
// console.log('attrs', this.attributes)
|
|
41
|
+
if (!this.hasAttribute('value')) this.setAttribute('value', '')
|
|
42
|
+
|
|
43
|
+
if (this.hasAttribute('input-class')) this.querySelector('input[type=text]').setAttribute('class', this.getAttribute('input-class'))
|
|
44
|
+
|
|
45
|
+
if (this.hasAttribute('accept')) this.querySelector('input[type=file]').setAttribute('accept', this.getAttribute('accept'))
|
|
46
|
+
if (this.hasAttribute('capture')) this.querySelector('input[type=file]').setAttribute('capture', this.getAttribute('capture'))
|
|
47
|
+
if (this.hasAttribute('multiple')) this.querySelector('input[type=file]').setAttribute('multiple', '')
|
|
48
|
+
if (this.hasAttribute('required')) this.querySelector('input[type=file]').setAttribute('required', '')
|
|
49
|
+
|
|
50
|
+
this.querySelector('input[type=text]').addEventListener('click', this.click)
|
|
51
|
+
this.querySelector('input[type=file]').addEventListener('change', this.change)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
disconnectedCallback() {
|
|
55
|
+
this.querySelector('input[type=text]').removeEventListener('click', this.click)
|
|
56
|
+
this.querySelector('input[type=file]').removeEventListener('change', this.change)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
60
|
+
const el = this.querySelector('input[type=text]')
|
|
61
|
+
switch (name) {
|
|
62
|
+
case 'value':
|
|
63
|
+
if (el) el.value = newVal
|
|
64
|
+
// console.log('bwc-fileupload', newVal)
|
|
65
|
+
// this.dispatchEvent(new CustomEvent('input', { detail: newVal }))
|
|
66
|
+
break
|
|
67
|
+
default: break
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static get observedAttributes() { return ['value', 'class'] }
|
|
72
|
+
get value() { return this.getAttribute('value') }
|
|
73
|
+
set value(val) { this.setAttribute('value', val) }
|
|
74
|
+
|
|
75
|
+
click(e) {
|
|
76
|
+
this.querySelector('input[type=file]').click()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
change(e) {
|
|
80
|
+
this.files = e.target.files
|
|
81
|
+
this.value = (this.files && this.files.length) ? Array.from(this.files).map(f => f.name).join(',') : ''
|
|
82
|
+
this.dispatchEvent(new CustomEvent('input', { detail: this.files }))
|
|
83
|
+
this.dispatchEvent(new CustomEvent('change', { detail: this.files }))
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
customElements.define('bwc-fileupload', BwcFileupload)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// FRONTEND ONLY
|
|
2
|
+
const template = document.createElement('template')
|
|
3
|
+
template.innerHTML = `
|
|
4
|
+
<style>
|
|
5
|
+
#overlay {
|
|
6
|
+
position: fixed;
|
|
7
|
+
top: 0; left: 0;
|
|
8
|
+
width: 100%; height: 100vh;
|
|
9
|
+
background: rgba(0,0,0,0.75);
|
|
10
|
+
z-index: 10;
|
|
11
|
+
opacity: 1;
|
|
12
|
+
pointer-events: all;
|
|
13
|
+
display: flex;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
align-items: center;
|
|
16
|
+
}
|
|
17
|
+
.loader {
|
|
18
|
+
border: 16px solid #888;
|
|
19
|
+
border-top: 16px solid #fff;
|
|
20
|
+
border-radius: 50%;
|
|
21
|
+
width: 64px;
|
|
22
|
+
height: 64px;
|
|
23
|
+
animation: spin 1.5s linear infinite;
|
|
24
|
+
}
|
|
25
|
+
@-webkit-keyframes spin {
|
|
26
|
+
0% { -webkit-transform: rotate(0deg); }
|
|
27
|
+
100% { -webkit-transform: rotate(360deg); }
|
|
28
|
+
}
|
|
29
|
+
@keyframes spin {
|
|
30
|
+
0% { transform: rotate(0deg); }
|
|
31
|
+
100% { transform: rotate(360deg); }
|
|
32
|
+
}
|
|
33
|
+
</style>
|
|
34
|
+
<div id="overlay"><div class="loader"></div></div>
|
|
35
|
+
`
|
|
36
|
+
class LoadingOverlay extends HTMLElement {
|
|
37
|
+
constructor() {
|
|
38
|
+
super()
|
|
39
|
+
const shadowRoot = this.attachShadow({ mode: 'open' })
|
|
40
|
+
shadowRoot.appendChild(template.content.cloneNode(true))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static get observedAttributes() {
|
|
44
|
+
return ['show']
|
|
45
|
+
}
|
|
46
|
+
get show() {
|
|
47
|
+
return this.hasAttribute('show')
|
|
48
|
+
}
|
|
49
|
+
set show(value) {
|
|
50
|
+
value ? this.setAttribute('show', '') : this.removeAttribute('show')
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
customElements.define('bwc-loading-overlay', LoadingOverlay)
|