-
Notifications
You must be signed in to change notification settings - Fork 79
/
PickerElement.js
128 lines (114 loc) · 3.58 KB
/
PickerElement.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import SveltePicker from './components/Picker/Picker.svelte'
import { DEFAULT_DATA_SOURCE, DEFAULT_LOCALE } from '../database/constants'
import { DEFAULT_CATEGORY_SORTING, DEFAULT_SKIN_TONE_EMOJI } from './constants'
import enI18n from '../picker/i18n/en.js'
import styles from 'emoji-picker-element-styles'
import Database from './ImportedDatabase'
const PROPS = [
'customEmoji',
'customCategorySorting',
'database',
'dataSource',
'i18n',
'locale',
'skinToneEmoji'
]
export default class PickerElement extends HTMLElement {
constructor (props) {
performance.mark('initialLoad')
super()
this.attachShadow({ mode: 'open' })
const style = document.createElement('style')
style.textContent = styles
this.shadowRoot.appendChild(style)
this._ctx = {
// Set defaults
locale: DEFAULT_LOCALE,
dataSource: DEFAULT_DATA_SOURCE,
skinToneEmoji: DEFAULT_SKIN_TONE_EMOJI,
customCategorySorting: DEFAULT_CATEGORY_SORTING,
customEmoji: null,
i18n: enI18n,
...props
}
// Handle properties set before the element was upgraded
for (const prop of PROPS) {
if (prop !== 'database' && Object.hasOwnProperty.call(this, prop)) {
this._ctx[prop] = this[prop]
delete this[prop]
}
}
this._dbFlush() // wait for a flush before creating the db, in case the user calls e.g. a setter or setAttribute
}
connectedCallback () {
this._cmp = new SveltePicker({
target: this.shadowRoot,
props: this._ctx
})
}
disconnectedCallback () {
this._cmp.$destroy()
this._cmp = undefined
const { database } = this._ctx
if (database) {
database.close()
// only happens if the database failed to load in the first place, so we don't care)
.catch(err => console.error(err))
}
}
static get observedAttributes () {
return ['locale', 'data-source', 'skin-tone-emoji'] // complex objects aren't supported, also use kebab-case
}
attributeChangedCallback (attrName, oldValue, newValue) {
// convert from kebab-case to camelcase
// see https://github.com/sveltejs/svelte/issues/3852#issuecomment-665037015
this._set(
attrName.replace(/-([a-z])/g, (_, up) => up.toUpperCase()),
newValue
)
}
_set (prop, newValue) {
this._ctx[prop] = newValue
if (this._cmp) {
this._cmp.$set({ [prop]: newValue })
}
if (['locale', 'dataSource'].includes(prop)) {
this._dbFlush()
}
}
_dbCreate () {
const { locale, dataSource, database } = this._ctx
// only create a new database if we really need to
if (!database || database.locale !== locale || database.dataSource !== dataSource) {
this._set('database', new Database({ locale, dataSource }))
}
}
// Update the Database in one microtask if the locale/dataSource change. We do one microtask
// so we don't create two Databases if e.g. both the locale and the dataSource change
_dbFlush () {
Promise.resolve().then(() => (
this._dbCreate()
))
}
}
const definitions = {}
for (const prop of PROPS) {
definitions[prop] = {
get () {
if (prop === 'database') {
// in rare cases, the microtask may not be flushed yet, so we need to instantiate the DB
// now if the user is asking for it
this._dbCreate()
}
return this._ctx[prop]
},
set (val) {
if (prop === 'database') {
throw new Error('database is read-only')
}
this._set(prop, val)
}
}
}
Object.defineProperties(PickerElement.prototype, definitions)
customElements.define('emoji-picker', PickerElement)