slim-select 1.27.1 → 2.0.0
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/.nvmrc +1 -0
- package/.prettierrc +6 -0
- package/.vscode/extensions.json +3 -0
- package/.vscode/settings.json +15 -5
- package/README.md +2 -1
- package/dist/helper.d.ts +1 -4
- package/dist/index.d.ts +38 -58
- package/dist/render.d.ts +109 -0
- package/dist/select.d.ts +31 -20
- package/dist/settings.d.ts +33 -0
- package/dist/slimselect.cjs.js +2 -0
- package/dist/slimselect.cjs.js.map +1 -0
- package/dist/slimselect.css +1 -2
- package/dist/slimselect.css.map +1 -0
- package/dist/slimselect.es.js +2 -0
- package/dist/slimselect.es.js.map +1 -0
- package/dist/slimselect.global.js +2 -0
- package/dist/slimselect.global.js.map +1 -0
- package/dist/slimselect.js +2 -1970
- package/dist/slimselect.js.map +1 -0
- package/dist/slimselect.umd.js +2 -0
- package/dist/slimselect.umd.js.map +1 -0
- package/dist/store.d.ts +71 -0
- package/docs/assets/check-mark.svg +6 -0
- package/docs/assets/data.js +59 -0
- package/docs/assets/home.css +1 -0
- package/docs/assets/home.js +1 -0
- package/docs/assets/index.css +1 -0
- package/docs/assets/index.js +16 -0
- package/docs/assets/index2.js +274 -0
- package/docs/assets/index3.js +126 -0
- package/docs/assets/index4.js +60 -0
- package/docs/assets/install.js +34 -0
- package/docs/assets/selects.js +55 -0
- package/docs/index.html +18 -5
- package/docs.go +3 -1
- package/index.html +16 -0
- package/jest.config.js +7 -0
- package/package.json +39 -35
- package/src/docs/app.vue +185 -76
- package/src/docs/assets/scss/_variables.scss +26 -10
- package/src/docs/assets/scss/alert.scss +12 -0
- package/src/docs/assets/scss/animations.scss +5 -3
- package/src/docs/assets/scss/button.scss +21 -0
- package/src/docs/assets/scss/index.scss +8 -6
- package/src/docs/assets/scss/input.scss +2 -2
- package/src/docs/assets/scss/layout.scss +195 -65
- package/src/docs/assets/scss/misc.scss +144 -34
- package/src/docs/assets/scss/prism.scss +155 -153
- package/src/docs/env.d.ts +12 -0
- package/src/docs/images/check-mark.svg +6 -0
- package/src/docs/images/chrome.svg +22 -0
- package/src/docs/images/firefox.svg +58 -0
- package/src/docs/images/github.png +0 -0
- package/src/docs/images/ie.svg +11 -0
- package/src/docs/images/npm.png +0 -0
- package/src/docs/images/opera.svg +15 -0
- package/src/docs/images/safari.svg +22 -0
- package/src/docs/main.ts +27 -0
- package/src/docs/pages/data.vue +130 -0
- package/src/docs/pages/events/addable.vue +79 -0
- package/src/docs/pages/events/before_after_change.vue +140 -0
- package/src/docs/pages/events/before_after_open_close.vue +122 -0
- package/src/docs/pages/events/error.vue +59 -0
- package/src/docs/pages/events/index.vue +33 -0
- package/src/docs/pages/events/search.vue +154 -0
- package/src/docs/pages/events/search_filter.vue +45 -0
- package/src/docs/pages/home.vue +96 -116
- package/src/docs/pages/install.vue +47 -74
- package/src/docs/pages/methods/destroy.vue +66 -0
- package/src/docs/pages/methods/enable_disable.vue +70 -0
- package/src/docs/pages/methods/get_data.vue +81 -0
- package/src/docs/pages/methods/get_selected.vue +78 -0
- package/src/docs/pages/methods/index.vue +36 -0
- package/src/docs/pages/methods/open_close.vue +63 -0
- package/src/docs/pages/methods/search.vue +62 -0
- package/src/docs/pages/methods/set_data.vue +106 -0
- package/src/docs/pages/methods/set_selected.vue +70 -0
- package/src/docs/pages/selects.vue +108 -114
- package/src/docs/pages/settings/always_open.vue +88 -0
- package/src/docs/pages/settings/close_on_select.vue +69 -0
- package/src/docs/pages/settings/content_location.vue +64 -0
- package/src/docs/pages/settings/content_position.vue +86 -0
- package/src/docs/pages/settings/css.vue +73 -0
- package/src/docs/pages/settings/data_attributes.vue +39 -0
- package/src/docs/pages/settings/deselect.vue +69 -0
- package/src/docs/pages/settings/display.vue +88 -0
- package/src/docs/pages/settings/hide_selected.vue +73 -0
- package/src/docs/pages/settings/html.vue +86 -0
- package/src/docs/pages/settings/index.vue +73 -0
- package/src/docs/pages/settings/mandatory.vue +91 -0
- package/src/docs/pages/settings/min_max.vue +61 -0
- package/src/docs/pages/settings/open_position.vue +60 -0
- package/src/docs/pages/settings/placeholder.vue +72 -0
- package/src/docs/pages/settings/search.vue +140 -0
- package/src/docs/pages/settings/select.vue +32 -0
- package/src/docs/pages/settings/select_by_group.vue +46 -0
- package/src/docs/pages/settings/show_tooltip.vue +44 -0
- package/src/docs/pages/settings/styles.vue +51 -0
- package/src/docs/router.ts +41 -13
- package/src/slim-select/helper.ts +25 -86
- package/src/slim-select/index.ts +317 -420
- package/src/slim-select/render.test.ts +39 -0
- package/src/slim-select/render.ts +1129 -0
- package/src/slim-select/rollup.config.mjs +39 -0
- package/src/slim-select/select.test.ts +155 -0
- package/src/slim-select/select.ts +280 -108
- package/src/slim-select/settings.ts +68 -0
- package/src/slim-select/slimselect.scss +263 -278
- package/src/slim-select/store.test.ts +220 -0
- package/src/slim-select/store.ts +320 -0
- package/src/slim-select/tsconfig.json +11 -5
- package/src/vue/slimselect.vue +61 -0
- package/tsconfig.json +10 -29
- package/vite.config.ts +18 -0
- package/.browserslistrc +0 -4
- package/.postcssrc.js +0 -5
- package/.travis.yml +0 -22
- package/babel.config.js +0 -5
- package/dist/config.d.ts +0 -83
- package/dist/data.d.ts +0 -50
- package/dist/slim.d.ts +0 -50
- package/dist/slimselect.min.css +0 -1
- package/dist/slimselect.min.js +0 -1
- package/dist/slimselect.min.mjs +0 -1
- package/docs/css/app.d5840d21.css +0 -1
- package/docs/css/chunk-vendors.fa8199af.css +0 -1
- package/docs/css/home.57020178.css +0 -1
- package/docs/css/install.baa5d3fb.css +0 -1
- package/docs/css/methods.728c742a.css +0 -1
- package/docs/css/options.b28a32ef.css +0 -1
- package/docs/css/selects.1a7e1e6a.css +0 -1
- package/docs/js/app.238ee382.js +0 -2
- package/docs/js/app.238ee382.js.map +0 -1
- package/docs/js/chunk-vendors.5125e3d8.js +0 -21
- package/docs/js/chunk-vendors.5125e3d8.js.map +0 -1
- package/docs/js/home.e7b28581.js +0 -2
- package/docs/js/home.e7b28581.js.map +0 -1
- package/docs/js/install.d4300783.js +0 -2
- package/docs/js/install.d4300783.js.map +0 -1
- package/docs/js/methods.fa1608f1.js +0 -11
- package/docs/js/methods.fa1608f1.js.map +0 -1
- package/docs/js/options.2c4ceb87.js +0 -2
- package/docs/js/options.2c4ceb87.js.map +0 -1
- package/docs/js/selects.1eeba693.js +0 -2
- package/docs/js/selects.1eeba693.js.map +0 -1
- package/src/docs/pages/methods.vue +0 -427
- package/src/docs/pages/options.vue +0 -1480
- package/src/main.ts +0 -29
- package/src/shims-vue.d.ts +0 -8
- package/src/slim-select/config.ts +0 -118
- package/src/slim-select/data.ts +0 -377
- package/src/slim-select/slim.ts +0 -775
- package/tslint.json +0 -26
- package/vue.config.js +0 -23
package/src/slim-select/slim.ts
DELETED
|
@@ -1,775 +0,0 @@
|
|
|
1
|
-
import SlimSelect from './index'
|
|
2
|
-
import { ensureElementInView, isValueInArrayOfObjects, highlight } from './helper'
|
|
3
|
-
import { Option, Optgroup, validateOption } from './data'
|
|
4
|
-
|
|
5
|
-
interface SingleSelected {
|
|
6
|
-
container: HTMLDivElement
|
|
7
|
-
placeholder: HTMLSpanElement
|
|
8
|
-
deselect: HTMLSpanElement
|
|
9
|
-
arrowIcon: {
|
|
10
|
-
container: HTMLSpanElement
|
|
11
|
-
arrow: HTMLSpanElement
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface MultiSelected {
|
|
16
|
-
container: HTMLDivElement
|
|
17
|
-
values: HTMLDivElement
|
|
18
|
-
add: HTMLDivElement
|
|
19
|
-
plus: HTMLSpanElement
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
interface Search {
|
|
23
|
-
container: HTMLDivElement
|
|
24
|
-
input: HTMLInputElement
|
|
25
|
-
addable?: HTMLDivElement
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Class is responsible for creating all the elements
|
|
29
|
-
export class Slim {
|
|
30
|
-
public main: SlimSelect
|
|
31
|
-
public container: HTMLDivElement
|
|
32
|
-
public singleSelected: SingleSelected | null
|
|
33
|
-
public multiSelected: MultiSelected | null
|
|
34
|
-
public content: HTMLDivElement
|
|
35
|
-
public search: Search
|
|
36
|
-
public list: HTMLDivElement
|
|
37
|
-
constructor(info: { main: SlimSelect }) {
|
|
38
|
-
this.main = info.main
|
|
39
|
-
|
|
40
|
-
// Create elements in order of appending
|
|
41
|
-
this.container = this.containerDiv()
|
|
42
|
-
this.content = this.contentDiv()
|
|
43
|
-
this.search = this.searchDiv()
|
|
44
|
-
this.list = this.listDiv()
|
|
45
|
-
this.options()
|
|
46
|
-
|
|
47
|
-
this.singleSelected = null
|
|
48
|
-
this.multiSelected = null
|
|
49
|
-
if (this.main.config.isMultiple) {
|
|
50
|
-
this.multiSelected = this.multiSelectedDiv()
|
|
51
|
-
if (this.multiSelected) {
|
|
52
|
-
this.container.appendChild(this.multiSelected.container)
|
|
53
|
-
}
|
|
54
|
-
} else {
|
|
55
|
-
this.singleSelected = this.singleSelectedDiv()
|
|
56
|
-
this.container.appendChild(this.singleSelected.container)
|
|
57
|
-
}
|
|
58
|
-
if (this.main.config.addToBody) {
|
|
59
|
-
// add the id to the content as a class as well
|
|
60
|
-
// this is important on touch devices as the close method is
|
|
61
|
-
// triggered when clicks on the document body occur
|
|
62
|
-
this.content.classList.add(this.main.config.id)
|
|
63
|
-
document.body.appendChild(this.content)
|
|
64
|
-
} else {
|
|
65
|
-
this.container.appendChild(this.content)
|
|
66
|
-
}
|
|
67
|
-
this.content.appendChild(this.search.container)
|
|
68
|
-
this.content.appendChild(this.list)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Create main container
|
|
72
|
-
public containerDiv(): HTMLDivElement {
|
|
73
|
-
// Create main container
|
|
74
|
-
const container = document.createElement('div') as HTMLDivElement
|
|
75
|
-
|
|
76
|
-
// Add style and classes
|
|
77
|
-
container.style.cssText = this.main.config.style
|
|
78
|
-
|
|
79
|
-
this.updateContainerDivClass(container)
|
|
80
|
-
|
|
81
|
-
return container
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Will look at the original select and pull classes from it
|
|
85
|
-
public updateContainerDivClass(container: HTMLDivElement) {
|
|
86
|
-
// Set config class
|
|
87
|
-
this.main.config.class = this.main.select.element.className.split(' ')
|
|
88
|
-
|
|
89
|
-
// Clear out classlist
|
|
90
|
-
container.className = ''
|
|
91
|
-
|
|
92
|
-
// Loop through config class and add
|
|
93
|
-
container.classList.add(this.main.config.id)
|
|
94
|
-
container.classList.add(this.main.config.main)
|
|
95
|
-
for (const c of this.main.config.class) {
|
|
96
|
-
if (c.trim() !== '') {
|
|
97
|
-
container.classList.add(c)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
public singleSelectedDiv(): SingleSelected {
|
|
103
|
-
const container: HTMLDivElement = document.createElement('div')
|
|
104
|
-
container.classList.add(this.main.config.singleSelected)
|
|
105
|
-
|
|
106
|
-
// Placeholder text
|
|
107
|
-
const placeholder: HTMLSpanElement = document.createElement('span')
|
|
108
|
-
placeholder.classList.add('placeholder')
|
|
109
|
-
container.appendChild(placeholder)
|
|
110
|
-
|
|
111
|
-
// Deselect
|
|
112
|
-
const deselect = document.createElement('span')
|
|
113
|
-
deselect.innerHTML = this.main.config.deselectLabel
|
|
114
|
-
deselect.classList.add('ss-deselect')
|
|
115
|
-
deselect.onclick = (e) => {
|
|
116
|
-
e.stopPropagation()
|
|
117
|
-
|
|
118
|
-
// Dont do anything if disabled
|
|
119
|
-
if (!this.main.config.isEnabled) {return}
|
|
120
|
-
|
|
121
|
-
this.main.set('')
|
|
122
|
-
}
|
|
123
|
-
container.appendChild(deselect)
|
|
124
|
-
|
|
125
|
-
// Arrow
|
|
126
|
-
const arrowContainer: HTMLSpanElement = document.createElement('span')
|
|
127
|
-
arrowContainer.classList.add(this.main.config.arrow)
|
|
128
|
-
const arrowIcon = document.createElement('span')
|
|
129
|
-
arrowIcon.classList.add('arrow-down')
|
|
130
|
-
arrowContainer.appendChild(arrowIcon)
|
|
131
|
-
container.appendChild(arrowContainer)
|
|
132
|
-
|
|
133
|
-
// Add onclick for main selector div
|
|
134
|
-
container.onclick = () => {
|
|
135
|
-
if (!this.main.config.isEnabled) { return }
|
|
136
|
-
|
|
137
|
-
this.main.data.contentOpen ? this.main.close() : this.main.open()
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return {
|
|
141
|
-
container,
|
|
142
|
-
placeholder,
|
|
143
|
-
deselect,
|
|
144
|
-
arrowIcon: {
|
|
145
|
-
container: arrowContainer,
|
|
146
|
-
arrow: arrowIcon
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Based upon current selection set placeholder text
|
|
152
|
-
public placeholder(): void {
|
|
153
|
-
const selected = this.main.data.getSelected() as Option
|
|
154
|
-
|
|
155
|
-
// Placeholder display
|
|
156
|
-
if (selected === null || (selected && selected.placeholder)) {
|
|
157
|
-
const placeholder = document.createElement('span')
|
|
158
|
-
placeholder.classList.add(this.main.config.disabled)
|
|
159
|
-
placeholder.innerHTML = this.main.config.placeholderText
|
|
160
|
-
if (this.singleSelected) {
|
|
161
|
-
this.singleSelected.placeholder.innerHTML = placeholder.outerHTML
|
|
162
|
-
}
|
|
163
|
-
} else {
|
|
164
|
-
let selectedValue = ''
|
|
165
|
-
if (selected) {
|
|
166
|
-
selectedValue = selected.innerHTML && this.main.config.valuesUseText !== true ? selected.innerHTML : selected.text
|
|
167
|
-
}
|
|
168
|
-
if (this.singleSelected) {
|
|
169
|
-
this.singleSelected.placeholder.innerHTML = (selected ? selectedValue : '')
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Based upon current selection/settings hide/show deselect
|
|
175
|
-
public deselect(): void {
|
|
176
|
-
if (this.singleSelected) {
|
|
177
|
-
// if allowDeselect is false just hide it
|
|
178
|
-
if (!this.main.config.allowDeselect) {
|
|
179
|
-
this.singleSelected.deselect.classList.add('ss-hide')
|
|
180
|
-
return
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (this.main.selected() === '') {
|
|
184
|
-
this.singleSelected.deselect.classList.add('ss-hide')
|
|
185
|
-
} else {
|
|
186
|
-
this.singleSelected.deselect.classList.remove('ss-hide')
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
public multiSelectedDiv(): MultiSelected {
|
|
192
|
-
const container = document.createElement('div')
|
|
193
|
-
container.classList.add(this.main.config.multiSelected)
|
|
194
|
-
|
|
195
|
-
const values = document.createElement('div')
|
|
196
|
-
values.classList.add(this.main.config.values)
|
|
197
|
-
container.appendChild(values)
|
|
198
|
-
|
|
199
|
-
const add = document.createElement('div')
|
|
200
|
-
add.classList.add(this.main.config.add)
|
|
201
|
-
const plus = document.createElement('span')
|
|
202
|
-
plus.classList.add(this.main.config.plus)
|
|
203
|
-
plus.onclick = (e) => {
|
|
204
|
-
if (this.main.data.contentOpen) {
|
|
205
|
-
this.main.close()
|
|
206
|
-
e.stopPropagation()
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
add.appendChild(plus)
|
|
210
|
-
container.appendChild(add)
|
|
211
|
-
|
|
212
|
-
container.onclick = (e) => {
|
|
213
|
-
if (!this.main.config.isEnabled) { return }
|
|
214
|
-
|
|
215
|
-
// Open only if you are not clicking on x text
|
|
216
|
-
const target = e.target as Element
|
|
217
|
-
if (!target.classList.contains(this.main.config.valueDelete)) {
|
|
218
|
-
this.main.data.contentOpen ? this.main.close() : this.main.open()
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return {
|
|
223
|
-
container,
|
|
224
|
-
values,
|
|
225
|
-
add,
|
|
226
|
-
plus
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Get selected values and append to multiSelected values container
|
|
231
|
-
// and remove those who shouldnt exist
|
|
232
|
-
public values(): void {
|
|
233
|
-
if (!this.multiSelected) { return }
|
|
234
|
-
let currentNodes = this.multiSelected.values.childNodes as any as HTMLDivElement[]
|
|
235
|
-
const selected = this.main.data.getSelected() as Option[]
|
|
236
|
-
|
|
237
|
-
// Remove nodes that shouldnt be there
|
|
238
|
-
let exists
|
|
239
|
-
const nodesToRemove = []
|
|
240
|
-
for (const c of currentNodes) {
|
|
241
|
-
exists = true
|
|
242
|
-
for (const s of selected) {
|
|
243
|
-
if (String(s.id) === String(c.dataset.id)) {
|
|
244
|
-
exists = false
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (exists) { nodesToRemove.push(c) }
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
for (const n of nodesToRemove) {
|
|
252
|
-
n.classList.add('ss-out')
|
|
253
|
-
this.multiSelected.values.removeChild(n)
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Add values that dont currently exist
|
|
257
|
-
currentNodes = this.multiSelected.values.childNodes as any as HTMLDivElement[]
|
|
258
|
-
for (let s = 0; s < selected.length; s++) {
|
|
259
|
-
exists = false
|
|
260
|
-
for (const c of currentNodes) {
|
|
261
|
-
if (String(selected[s].id) === String(c.dataset.id)) {
|
|
262
|
-
exists = true
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (!exists) {
|
|
267
|
-
if (currentNodes.length === 0 || !HTMLElement.prototype.insertAdjacentElement) {
|
|
268
|
-
this.multiSelected.values.appendChild(this.valueDiv(selected[s]))
|
|
269
|
-
} else if (s === 0) {
|
|
270
|
-
this.multiSelected.values.insertBefore(this.valueDiv(selected[s]), (currentNodes[s] as any))
|
|
271
|
-
} else {
|
|
272
|
-
(currentNodes[s - 1] as any).insertAdjacentElement('afterend', this.valueDiv(selected[s]))
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// If there are no values set placeholder
|
|
278
|
-
if (selected.length === 0) {
|
|
279
|
-
const placeholder = document.createElement('span')
|
|
280
|
-
placeholder.classList.add(this.main.config.disabled)
|
|
281
|
-
placeholder.innerHTML = this.main.config.placeholderText
|
|
282
|
-
this.multiSelected.values.innerHTML = placeholder.outerHTML
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
public valueDiv(optionObj: Option): HTMLDivElement {
|
|
287
|
-
const value = document.createElement('div')
|
|
288
|
-
value.classList.add(this.main.config.value)
|
|
289
|
-
value.dataset.id = optionObj.id
|
|
290
|
-
|
|
291
|
-
const text = document.createElement('span')
|
|
292
|
-
text.classList.add(this.main.config.valueText)
|
|
293
|
-
text.innerHTML = (optionObj.innerHTML && this.main.config.valuesUseText !== true ? optionObj.innerHTML : optionObj.text)
|
|
294
|
-
value.appendChild(text)
|
|
295
|
-
|
|
296
|
-
if (!optionObj.mandatory) {
|
|
297
|
-
const deleteSpan = document.createElement('span')
|
|
298
|
-
deleteSpan.classList.add(this.main.config.valueDelete)
|
|
299
|
-
deleteSpan.innerHTML = this.main.config.deselectLabel
|
|
300
|
-
deleteSpan.onclick = (e) => {
|
|
301
|
-
e.preventDefault()
|
|
302
|
-
e.stopPropagation()
|
|
303
|
-
let shouldUpdate = false
|
|
304
|
-
|
|
305
|
-
// If no beforeOnChange is set automatically update at end
|
|
306
|
-
if (!this.main.beforeOnChange) {shouldUpdate = true}
|
|
307
|
-
|
|
308
|
-
if (this.main.beforeOnChange) {
|
|
309
|
-
const selected = this.main.data.getSelected() as Option
|
|
310
|
-
const currentValues = JSON.parse(JSON.stringify(selected))
|
|
311
|
-
|
|
312
|
-
// Remove from current selection
|
|
313
|
-
for (let i = 0; i < currentValues.length; i++) {
|
|
314
|
-
if (currentValues[i].id === optionObj.id) {
|
|
315
|
-
currentValues.splice(i, 1)
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
const beforeOnchange = this.main.beforeOnChange(currentValues)
|
|
320
|
-
if (beforeOnchange !== false) { shouldUpdate = true }
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
if (shouldUpdate) {
|
|
324
|
-
this.main.data.removeFromSelected((optionObj.id as any), 'id')
|
|
325
|
-
this.main.render()
|
|
326
|
-
this.main.select.setValue()
|
|
327
|
-
this.main.data.onDataChange() // Trigger on change callback
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
value.appendChild(deleteSpan)
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
return value
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Create content container
|
|
339
|
-
public contentDiv(): HTMLDivElement {
|
|
340
|
-
const container = document.createElement('div')
|
|
341
|
-
container.classList.add(this.main.config.content)
|
|
342
|
-
return container
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
public searchDiv(): Search {
|
|
346
|
-
const container = document.createElement('div')
|
|
347
|
-
const input = document.createElement('input')
|
|
348
|
-
const addable = document.createElement('div')
|
|
349
|
-
container.classList.add(this.main.config.search)
|
|
350
|
-
|
|
351
|
-
// Setup search return object
|
|
352
|
-
const searchReturn: Search = {
|
|
353
|
-
container,
|
|
354
|
-
input
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// We still want the search to be tabable but not shown
|
|
358
|
-
if (!this.main.config.showSearch) {
|
|
359
|
-
container.classList.add(this.main.config.hide)
|
|
360
|
-
input.readOnly = true
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
input.type = 'search'
|
|
364
|
-
input.placeholder = this.main.config.searchPlaceholder
|
|
365
|
-
input.tabIndex = 0
|
|
366
|
-
input.setAttribute('aria-label', this.main.config.searchPlaceholder)
|
|
367
|
-
input.setAttribute('autocapitalize', 'off')
|
|
368
|
-
input.setAttribute('autocomplete', 'off')
|
|
369
|
-
input.setAttribute('autocorrect', 'off')
|
|
370
|
-
input.onclick = (e) => {
|
|
371
|
-
setTimeout(() => {
|
|
372
|
-
const target = e.target as HTMLInputElement
|
|
373
|
-
if (target.value === '') { this.main.search('') }
|
|
374
|
-
}, 10)
|
|
375
|
-
}
|
|
376
|
-
input.onkeydown = (e) => {
|
|
377
|
-
if (e.key === 'ArrowUp') {
|
|
378
|
-
this.main.open()
|
|
379
|
-
this.highlightUp()
|
|
380
|
-
e.preventDefault()
|
|
381
|
-
} else if (e.key === 'ArrowDown') {
|
|
382
|
-
this.main.open()
|
|
383
|
-
this.highlightDown()
|
|
384
|
-
e.preventDefault()
|
|
385
|
-
} else if (e.key === 'Tab') {
|
|
386
|
-
if (!this.main.data.contentOpen) {
|
|
387
|
-
setTimeout(() => { this.main.close() }, this.main.config.timeoutDelay)
|
|
388
|
-
} else {
|
|
389
|
-
this.main.close()
|
|
390
|
-
}
|
|
391
|
-
} else if (e.key === 'Enter') {
|
|
392
|
-
e.preventDefault()
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
input.onkeyup = (e) => {
|
|
396
|
-
const target = e.target as HTMLInputElement
|
|
397
|
-
if (e.key === 'Enter') {
|
|
398
|
-
if (this.main.addable && e.ctrlKey) {
|
|
399
|
-
addable.click()
|
|
400
|
-
e.preventDefault()
|
|
401
|
-
e.stopPropagation()
|
|
402
|
-
return
|
|
403
|
-
}
|
|
404
|
-
const highlighted = this.list.querySelector('.' + this.main.config.highlighted) as HTMLDivElement
|
|
405
|
-
if (highlighted) { highlighted.click() }
|
|
406
|
-
} else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
|
|
407
|
-
// Cancel out to leave for onkeydown to handle
|
|
408
|
-
} else if (e.key === 'Escape') {
|
|
409
|
-
this.main.close()
|
|
410
|
-
} else {
|
|
411
|
-
if (this.main.config.showSearch && this.main.data.contentOpen) {
|
|
412
|
-
this.main.search(target.value)
|
|
413
|
-
} else {
|
|
414
|
-
input.value = ''
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
e.preventDefault()
|
|
418
|
-
e.stopPropagation()
|
|
419
|
-
}
|
|
420
|
-
input.onfocus = () => { this.main.open() }
|
|
421
|
-
container.appendChild(input)
|
|
422
|
-
|
|
423
|
-
if (this.main.addable) {
|
|
424
|
-
addable.classList.add(this.main.config.addable)
|
|
425
|
-
addable.innerHTML = '+'
|
|
426
|
-
addable.onclick = (e) => {
|
|
427
|
-
if (this.main.addable) {
|
|
428
|
-
e.preventDefault()
|
|
429
|
-
e.stopPropagation()
|
|
430
|
-
|
|
431
|
-
const inputValue = this.search.input.value
|
|
432
|
-
if (inputValue.trim() === '') { this.search.input.focus(); return }
|
|
433
|
-
|
|
434
|
-
const addableValue = this.main.addable(inputValue)
|
|
435
|
-
let addableValueStr = ''
|
|
436
|
-
if (!addableValue) { return }
|
|
437
|
-
|
|
438
|
-
if (typeof addableValue === 'object') {
|
|
439
|
-
const validValue = validateOption(addableValue)
|
|
440
|
-
if (validValue) {
|
|
441
|
-
this.main.addData(addableValue)
|
|
442
|
-
addableValueStr = (addableValue.value ? addableValue.value : addableValue.text)
|
|
443
|
-
}
|
|
444
|
-
} else {
|
|
445
|
-
this.main.addData(this.main.data.newOption({
|
|
446
|
-
text: addableValue,
|
|
447
|
-
value: addableValue
|
|
448
|
-
}))
|
|
449
|
-
addableValueStr = addableValue
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
this.main.search('')
|
|
453
|
-
setTimeout(() => { // Temp fix to solve multi render issue
|
|
454
|
-
this.main.set(addableValueStr, 'value', false, false)
|
|
455
|
-
}, 100)
|
|
456
|
-
|
|
457
|
-
// Close it only if closeOnSelect = true
|
|
458
|
-
if (this.main.config.closeOnSelect) {
|
|
459
|
-
setTimeout(() => { // Give it a little padding for a better looking animation
|
|
460
|
-
this.main.close()
|
|
461
|
-
}, 100)
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
container.appendChild(addable)
|
|
466
|
-
|
|
467
|
-
searchReturn.addable = addable
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return searchReturn
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
public highlightUp(): void {
|
|
474
|
-
const highlighted = this.list.querySelector('.' + this.main.config.highlighted) as HTMLDivElement
|
|
475
|
-
let prev: HTMLDivElement | null = null
|
|
476
|
-
if (highlighted) {
|
|
477
|
-
prev = highlighted.previousSibling as HTMLDivElement
|
|
478
|
-
while (prev !== null) {
|
|
479
|
-
if (prev.classList.contains(this.main.config.disabled)) {
|
|
480
|
-
prev = prev.previousSibling as HTMLDivElement
|
|
481
|
-
continue
|
|
482
|
-
} else {
|
|
483
|
-
break
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
} else {
|
|
487
|
-
const allOptions = this.list.querySelectorAll('.' + this.main.config.option + ':not(.' + this.main.config.disabled + ')')
|
|
488
|
-
prev = allOptions[allOptions.length - 1] as HTMLDivElement
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// Do not select if optgroup label
|
|
492
|
-
if (prev && prev.classList.contains(this.main.config.optgroupLabel)) { prev = null }
|
|
493
|
-
|
|
494
|
-
// Check if parent is optgroup
|
|
495
|
-
if (prev === null) {
|
|
496
|
-
const parent = highlighted.parentNode as HTMLDivElement
|
|
497
|
-
if (parent.classList.contains(this.main.config.optgroup)) {
|
|
498
|
-
if (parent.previousSibling) {
|
|
499
|
-
const prevNodes = (parent.previousSibling as HTMLDivElement).querySelectorAll('.' + this.main.config.option + ':not(.' + this.main.config.disabled + ')')
|
|
500
|
-
if (prevNodes.length) {
|
|
501
|
-
prev = prevNodes[prevNodes.length - 1] as HTMLDivElement
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// If previous element exists highlight it
|
|
508
|
-
if (prev) {
|
|
509
|
-
if (highlighted) { highlighted.classList.remove(this.main.config.highlighted) }
|
|
510
|
-
prev.classList.add(this.main.config.highlighted)
|
|
511
|
-
ensureElementInView(this.list, prev)
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
public highlightDown(): void {
|
|
516
|
-
const highlighted = this.list.querySelector('.' + this.main.config.highlighted) as HTMLDivElement
|
|
517
|
-
let next = null
|
|
518
|
-
|
|
519
|
-
if (highlighted) {
|
|
520
|
-
next = highlighted.nextSibling as HTMLDivElement
|
|
521
|
-
while (next !== null) {
|
|
522
|
-
if (next.classList.contains(this.main.config.disabled)) {
|
|
523
|
-
next = next.nextSibling as HTMLDivElement
|
|
524
|
-
continue
|
|
525
|
-
} else {
|
|
526
|
-
break
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
} else {
|
|
530
|
-
next = this.list.querySelector('.' + this.main.config.option + ':not(.' + this.main.config.disabled + ')') as HTMLDivElement
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// Check if parent is optgroup
|
|
534
|
-
if (next === null && highlighted !== null) {
|
|
535
|
-
const parent = highlighted.parentNode as HTMLDivElement
|
|
536
|
-
if (parent.classList.contains(this.main.config.optgroup)) {
|
|
537
|
-
if (parent.nextSibling) {
|
|
538
|
-
const sibling = parent.nextSibling as HTMLDivElement
|
|
539
|
-
next = sibling.querySelector('.' + this.main.config.option + ':not(.' + this.main.config.disabled + ')') as HTMLDivElement
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// If previous element exists highlight it
|
|
545
|
-
if (next) {
|
|
546
|
-
if (highlighted) { highlighted.classList.remove(this.main.config.highlighted) }
|
|
547
|
-
next.classList.add(this.main.config.highlighted)
|
|
548
|
-
ensureElementInView(this.list, next)
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// Create main container that options will reside
|
|
553
|
-
public listDiv(): HTMLDivElement {
|
|
554
|
-
const list = document.createElement('div')
|
|
555
|
-
list.classList.add(this.main.config.list)
|
|
556
|
-
list.setAttribute('role', 'listbox')
|
|
557
|
-
// @todo Link to?
|
|
558
|
-
// list.setAttribute('aria-labelledby', '')
|
|
559
|
-
return list
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
// Loop through data || filtered data and build options and append to list container
|
|
563
|
-
public options(content: string = ''): void {
|
|
564
|
-
const data = this.main.data.filtered || this.main.data.data
|
|
565
|
-
|
|
566
|
-
// Clear out innerHtml
|
|
567
|
-
this.list.innerHTML = ''
|
|
568
|
-
|
|
569
|
-
// If content is being passed just use that text
|
|
570
|
-
if (content !== '') {
|
|
571
|
-
const searching = document.createElement('div')
|
|
572
|
-
searching.classList.add(this.main.config.option)
|
|
573
|
-
searching.classList.add(this.main.config.disabled)
|
|
574
|
-
searching.innerHTML = content
|
|
575
|
-
this.list.appendChild(searching)
|
|
576
|
-
return
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// If ajax and isSearching
|
|
580
|
-
if (this.main.config.isAjax && this.main.config.isSearching) {
|
|
581
|
-
const searching = document.createElement('div')
|
|
582
|
-
searching.classList.add(this.main.config.option)
|
|
583
|
-
searching.classList.add(this.main.config.disabled)
|
|
584
|
-
searching.innerHTML = this.main.config.searchingText
|
|
585
|
-
this.list.appendChild(searching)
|
|
586
|
-
return
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
// If no results show no results text
|
|
590
|
-
if (data.length === 0) {
|
|
591
|
-
const noResults = document.createElement('div')
|
|
592
|
-
noResults.classList.add(this.main.config.option)
|
|
593
|
-
noResults.classList.add(this.main.config.disabled)
|
|
594
|
-
noResults.innerHTML = this.main.config.searchText
|
|
595
|
-
this.list.appendChild(noResults)
|
|
596
|
-
return
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
// Append individual options to div container
|
|
600
|
-
for (const d of data) {
|
|
601
|
-
// Create optgroup
|
|
602
|
-
if (d.hasOwnProperty('label')) {
|
|
603
|
-
const item = d as Optgroup
|
|
604
|
-
const optgroupEl = document.createElement('div')
|
|
605
|
-
optgroupEl.classList.add(this.main.config.optgroup)
|
|
606
|
-
|
|
607
|
-
// Create label
|
|
608
|
-
const optgroupLabel = document.createElement('div')
|
|
609
|
-
optgroupLabel.classList.add(this.main.config.optgroupLabel)
|
|
610
|
-
if (this.main.config.selectByGroup && this.main.config.isMultiple) {
|
|
611
|
-
optgroupLabel.classList.add(this.main.config.optgroupLabelSelectable)
|
|
612
|
-
}
|
|
613
|
-
optgroupLabel.innerHTML = item.label
|
|
614
|
-
optgroupEl.appendChild(optgroupLabel)
|
|
615
|
-
|
|
616
|
-
const options = item.options
|
|
617
|
-
if (options) {
|
|
618
|
-
for (const o of options) {
|
|
619
|
-
optgroupEl.appendChild(this.option(o))
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// Selecting all values by clicking the group label
|
|
623
|
-
if (this.main.config.selectByGroup && this.main.config.isMultiple) {
|
|
624
|
-
const master = this
|
|
625
|
-
optgroupLabel.addEventListener('click', (e: MouseEvent) => {
|
|
626
|
-
e.preventDefault()
|
|
627
|
-
e.stopPropagation()
|
|
628
|
-
|
|
629
|
-
for (const childEl of optgroupEl.children as any as HTMLDivElement[]) {
|
|
630
|
-
if (childEl.className.indexOf(master.main.config.option) !== -1) {
|
|
631
|
-
childEl.click()
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
})
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
this.list.appendChild(optgroupEl)
|
|
638
|
-
} else {
|
|
639
|
-
this.list.appendChild(this.option(d as Option))
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
// Create single option
|
|
645
|
-
public option(data: Option): HTMLDivElement {
|
|
646
|
-
// Add hidden placeholder
|
|
647
|
-
if (data.placeholder) {
|
|
648
|
-
const placeholder = document.createElement('div')
|
|
649
|
-
placeholder.classList.add(this.main.config.option)
|
|
650
|
-
placeholder.classList.add(this.main.config.hide)
|
|
651
|
-
return placeholder
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
const optionEl = document.createElement('div')
|
|
655
|
-
|
|
656
|
-
// Add class to div element
|
|
657
|
-
optionEl.classList.add(this.main.config.option)
|
|
658
|
-
// Add WCAG attribute
|
|
659
|
-
optionEl.setAttribute('role', 'option')
|
|
660
|
-
if (data.class) {
|
|
661
|
-
data.class.split(' ').forEach((dataClass: string) => {
|
|
662
|
-
optionEl.classList.add(dataClass)
|
|
663
|
-
})
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// Add style to div element
|
|
667
|
-
if (data.style) {
|
|
668
|
-
optionEl.style.cssText = data.style
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
const selected = this.main.data.getSelected() as Option
|
|
672
|
-
|
|
673
|
-
optionEl.dataset.id = data.id
|
|
674
|
-
if (this.main.config.searchHighlight && this.main.slim && data.innerHTML && this.main.slim.search.input.value.trim() !== '') {
|
|
675
|
-
optionEl.innerHTML = highlight(data.innerHTML, this.main.slim.search.input.value, this.main.config.searchHighlighter)
|
|
676
|
-
} else if (data.innerHTML) {
|
|
677
|
-
optionEl.innerHTML = data.innerHTML
|
|
678
|
-
}
|
|
679
|
-
if (this.main.config.showOptionTooltips && optionEl.textContent) {
|
|
680
|
-
optionEl.setAttribute('title', optionEl.textContent)
|
|
681
|
-
}
|
|
682
|
-
const master = this
|
|
683
|
-
optionEl.addEventListener('click', function(e: MouseEvent) {
|
|
684
|
-
e.preventDefault()
|
|
685
|
-
e.stopPropagation()
|
|
686
|
-
|
|
687
|
-
const element = this
|
|
688
|
-
const elementID = element.dataset.id
|
|
689
|
-
|
|
690
|
-
if (data.selected === true && master.main.config.allowDeselectOption) {
|
|
691
|
-
let shouldUpdate = false
|
|
692
|
-
|
|
693
|
-
// If no beforeOnChange is set automatically update at end
|
|
694
|
-
if (!master.main.beforeOnChange || !master.main.config.isMultiple) {shouldUpdate = true}
|
|
695
|
-
|
|
696
|
-
if (master.main.beforeOnChange && master.main.config.isMultiple) {
|
|
697
|
-
const selectedValues = master.main.data.getSelected() as Option
|
|
698
|
-
const currentValues = JSON.parse(JSON.stringify(selectedValues))
|
|
699
|
-
|
|
700
|
-
// Remove from current selection
|
|
701
|
-
|
|
702
|
-
for (let i = 0; i < currentValues.length; i++) {
|
|
703
|
-
if (currentValues[i].id === elementID) {
|
|
704
|
-
currentValues.splice(i, 1)
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
const beforeOnchange = master.main.beforeOnChange(currentValues)
|
|
709
|
-
if (beforeOnchange !== false) { shouldUpdate = true }
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
if (shouldUpdate) {
|
|
713
|
-
if (master.main.config.isMultiple) {
|
|
714
|
-
master.main.data.removeFromSelected((elementID as any), 'id')
|
|
715
|
-
master.main.render()
|
|
716
|
-
master.main.select.setValue()
|
|
717
|
-
master.main.data.onDataChange() // Trigger on change callback
|
|
718
|
-
} else {
|
|
719
|
-
master.main.set('')
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
}
|
|
723
|
-
} else {
|
|
724
|
-
// Check if option is disabled or is already selected, do nothing
|
|
725
|
-
if (data.disabled || data.selected) {
|
|
726
|
-
return
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
// Check if hit limit
|
|
730
|
-
if (master.main.config.limit && Array.isArray(selected) && master.main.config.limit <= selected.length) {
|
|
731
|
-
return
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
if (master.main.beforeOnChange) {
|
|
735
|
-
let value
|
|
736
|
-
const objectInfo = JSON.parse(JSON.stringify(master.main.data.getObjectFromData(elementID as string)))
|
|
737
|
-
objectInfo.selected = true
|
|
738
|
-
|
|
739
|
-
if (master.main.config.isMultiple) {
|
|
740
|
-
value = JSON.parse(JSON.stringify(selected))
|
|
741
|
-
value.push(objectInfo)
|
|
742
|
-
} else {
|
|
743
|
-
value = JSON.parse(JSON.stringify(objectInfo))
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
const beforeOnchange = master.main.beforeOnChange(value)
|
|
747
|
-
if (beforeOnchange !== false) {
|
|
748
|
-
master.main.set((elementID as string), 'id', master.main.config.closeOnSelect)
|
|
749
|
-
}
|
|
750
|
-
} else {
|
|
751
|
-
master.main.set((elementID as string), 'id', master.main.config.closeOnSelect)
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
})
|
|
755
|
-
|
|
756
|
-
const isSelected = selected && isValueInArrayOfObjects(selected, 'id', (data.id as string))
|
|
757
|
-
if (data.disabled || isSelected) {
|
|
758
|
-
optionEl.onclick = null
|
|
759
|
-
if (!master.main.config.allowDeselectOption) {
|
|
760
|
-
optionEl.classList.add(this.main.config.disabled)
|
|
761
|
-
}
|
|
762
|
-
if (master.main.config.hideSelectedOption) {
|
|
763
|
-
optionEl.classList.add(this.main.config.hide)
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
if (isSelected) {
|
|
768
|
-
optionEl.classList.add(this.main.config.optionSelected)
|
|
769
|
-
} else {
|
|
770
|
-
optionEl.classList.remove(this.main.config.optionSelected)
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
return optionEl
|
|
774
|
-
}
|
|
775
|
-
}
|