slim-select 2.2.3 → 2.3.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/README.md +2 -6
- package/dist/index.d.ts +1 -1
- package/dist/render.d.ts +4 -2
- package/dist/select.d.ts +6 -11
- package/dist/settings.d.ts +3 -1
- package/dist/slimselect.cjs.js +206 -173
- package/dist/slimselect.css +1 -1
- package/dist/slimselect.css.map +1 -1
- package/dist/slimselect.es.js +206 -173
- package/dist/slimselect.global.js +206 -173
- package/dist/slimselect.js +206 -173
- package/dist/slimselect.min.js +1 -1
- package/dist/slimselect.umd.js +206 -173
- package/dist/slimselect.umd.min.js +1 -1
- package/jest.config.js +1 -0
- package/package.json +4 -4
- package/src/slim-select/index.ts +45 -35
- package/src/slim-select/render.ts +146 -97
- package/src/slim-select/select.test.ts +143 -23
- package/src/slim-select/select.ts +110 -91
- package/src/slim-select/settings.ts +6 -2
- package/src/slim-select/slimselect.scss +33 -10
- package/src/slim-select/store.test.ts +22 -0
- package/src/vue/dist/slim-select/index.d.ts +1 -1
- package/src/vue/dist/slim-select/render.d.ts +4 -2
- package/src/vue/dist/slim-select/select.d.ts +6 -11
- package/src/vue/dist/slim-select/settings.d.ts +3 -1
- package/src/vue/dist/slimselectvue.es.js +225 -186
- package/src/vue/dist/slimselectvue.global.js +225 -186
- package/src/vue/dist/slimselectvue.ssr.js +225 -186
- package/src/vue/dist/vue/slimselect.vue.d.ts +21 -17
- package/src/vue/package.json +1 -1
- package/src/vue/slimselect.vue +27 -23
|
@@ -6,7 +6,7 @@ export interface Callbacks {
|
|
|
6
6
|
open: () => void
|
|
7
7
|
close: () => void
|
|
8
8
|
addable?: (value: string) => Promise<OptionOptional | string> | OptionOptional | string
|
|
9
|
-
setSelected: (value: string[]) => void
|
|
9
|
+
setSelected: (value: string[], runAfterChange: boolean) => void
|
|
10
10
|
addOption: (option: Option) => void
|
|
11
11
|
search: (search: string) => void
|
|
12
12
|
beforeChange?: (newVal: Option[], oldVal: Option[]) => boolean | void
|
|
@@ -63,6 +63,7 @@ export default class Render {
|
|
|
63
63
|
// Values
|
|
64
64
|
values: 'ss-values',
|
|
65
65
|
single: 'ss-single',
|
|
66
|
+
max: 'ss-max',
|
|
66
67
|
value: 'ss-value',
|
|
67
68
|
valueText: 'ss-value-text',
|
|
68
69
|
valueDelete: 'ss-value-delete',
|
|
@@ -124,6 +125,9 @@ export default class Render {
|
|
|
124
125
|
this.main = this.mainDiv()
|
|
125
126
|
this.content = this.contentDiv()
|
|
126
127
|
|
|
128
|
+
// Add classes and styles to main/content
|
|
129
|
+
this.updateClassStyles()
|
|
130
|
+
|
|
127
131
|
// Add content to the content location settings
|
|
128
132
|
this.settings.contentLocation.appendChild(this.content.main)
|
|
129
133
|
}
|
|
@@ -155,25 +159,6 @@ export default class Render {
|
|
|
155
159
|
// move the content in to the right location
|
|
156
160
|
this.moveContent()
|
|
157
161
|
|
|
158
|
-
// Render the options
|
|
159
|
-
this.renderOptions(this.store.getData())
|
|
160
|
-
|
|
161
|
-
// Check showContent to see if they want to specifically show in a certain direction
|
|
162
|
-
if (this.settings.contentPosition === 'relative') {
|
|
163
|
-
this.moveContentBelow()
|
|
164
|
-
} else if (this.settings.openPosition.toLowerCase() === 'up') {
|
|
165
|
-
this.moveContentAbove()
|
|
166
|
-
} else if (this.settings.openPosition.toLowerCase() === 'down') {
|
|
167
|
-
this.moveContentBelow()
|
|
168
|
-
} else {
|
|
169
|
-
// Auto identify where to put it
|
|
170
|
-
if (this.putContent(this.content.main, this.settings.isOpen) === 'up') {
|
|
171
|
-
this.moveContentAbove()
|
|
172
|
-
} else {
|
|
173
|
-
this.moveContentBelow()
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
162
|
// Move to last selected option
|
|
178
163
|
const selectedOptions = this.store.getSelectedOptions()
|
|
179
164
|
if (selectedOptions.length) {
|
|
@@ -193,30 +178,49 @@ export default class Render {
|
|
|
193
178
|
this.main.arrow.path.setAttribute('d', this.classes.arrowClose)
|
|
194
179
|
}
|
|
195
180
|
|
|
196
|
-
public
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
main.
|
|
181
|
+
public updateClassStyles(): void {
|
|
182
|
+
// Clear all classes and styles
|
|
183
|
+
this.main.main.className = ''
|
|
184
|
+
this.main.main.removeAttribute('style')
|
|
185
|
+
this.content.main.className = ''
|
|
186
|
+
this.content.main.removeAttribute('style')
|
|
202
187
|
|
|
203
|
-
//
|
|
204
|
-
main.
|
|
188
|
+
// Make sure main/content has its base class
|
|
189
|
+
this.main.main.classList.add(this.classes.main)
|
|
190
|
+
this.content.main.classList.add(this.classes.content)
|
|
205
191
|
|
|
206
|
-
//
|
|
207
|
-
|
|
192
|
+
// Add styles
|
|
193
|
+
if (this.settings.style !== '') {
|
|
194
|
+
this.content.main.style.cssText = this.settings.style
|
|
195
|
+
}
|
|
208
196
|
|
|
209
|
-
//
|
|
210
|
-
|
|
211
|
-
main.classList.add(this.classes.main)
|
|
212
|
-
if (this.settings.class) {
|
|
197
|
+
// Add classes
|
|
198
|
+
if (this.settings.class.length) {
|
|
213
199
|
for (const c of this.settings.class) {
|
|
214
200
|
if (c.trim() !== '') {
|
|
215
|
-
main.classList.add(c.trim())
|
|
201
|
+
this.main.main.classList.add(c.trim())
|
|
202
|
+
this.content.main.classList.add(c.trim())
|
|
216
203
|
}
|
|
217
204
|
}
|
|
218
205
|
}
|
|
219
206
|
|
|
207
|
+
// Misc classes
|
|
208
|
+
// Add content position class
|
|
209
|
+
if (this.settings.contentPosition === 'relative') {
|
|
210
|
+
this.content.main.classList.add('ss-' + this.settings.contentPosition)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
public mainDiv(): Main {
|
|
215
|
+
// Create main container
|
|
216
|
+
const main = document.createElement('div')
|
|
217
|
+
|
|
218
|
+
// Add id to data-id
|
|
219
|
+
main.dataset.id = this.settings.id
|
|
220
|
+
|
|
221
|
+
// Set tabable to allow tabbing to the element
|
|
222
|
+
main.tabIndex = 0
|
|
223
|
+
|
|
220
224
|
// If main gets focus, open the content
|
|
221
225
|
main.onfocus = () => {
|
|
222
226
|
if (this.settings.triggerFocus) {
|
|
@@ -253,7 +257,7 @@ export default class Render {
|
|
|
253
257
|
// Add onclick for main div
|
|
254
258
|
main.onclick = (e: Event) => {
|
|
255
259
|
// Dont do anything if disabled
|
|
256
|
-
if (
|
|
260
|
+
if (this.settings.disabled) {
|
|
257
261
|
return
|
|
258
262
|
}
|
|
259
263
|
|
|
@@ -277,7 +281,7 @@ export default class Render {
|
|
|
277
281
|
e.stopPropagation()
|
|
278
282
|
|
|
279
283
|
// Dont do anything if disabled
|
|
280
|
-
if (
|
|
284
|
+
if (this.settings.disabled) {
|
|
281
285
|
return
|
|
282
286
|
}
|
|
283
287
|
|
|
@@ -292,7 +296,7 @@ export default class Render {
|
|
|
292
296
|
}
|
|
293
297
|
|
|
294
298
|
if (shouldDelete) {
|
|
295
|
-
this.callbacks.setSelected([''])
|
|
299
|
+
this.callbacks.setSelected([''], false)
|
|
296
300
|
|
|
297
301
|
// Check if we need to close the dropdown
|
|
298
302
|
if (this.settings.closeOnSelect) {
|
|
@@ -415,7 +419,7 @@ export default class Render {
|
|
|
415
419
|
}
|
|
416
420
|
|
|
417
421
|
private renderMultipleValues(): void {
|
|
418
|
-
// Get various
|
|
422
|
+
// Get various pieces of data
|
|
419
423
|
let currentNodes = this.main.values.childNodes as NodeListOf<HTMLDivElement>
|
|
420
424
|
let selectedOptions = this.store.filter((opt: Option) => {
|
|
421
425
|
// Only grab options that are selected and display is true
|
|
@@ -434,6 +438,24 @@ export default class Render {
|
|
|
434
438
|
}
|
|
435
439
|
}
|
|
436
440
|
|
|
441
|
+
// If selectedOptions is greater than maxItems, set maxValuesMessage
|
|
442
|
+
if (selectedOptions.length > this.settings.maxValuesShown) {
|
|
443
|
+
// Creating the element that shows the number of selected items
|
|
444
|
+
const singleValue = document.createElement('div')
|
|
445
|
+
singleValue.classList.add(this.classes.max)
|
|
446
|
+
singleValue.textContent = this.settings.maxValuesMessage.replace('{number}', selectedOptions.length.toString())
|
|
447
|
+
|
|
448
|
+
// If there is a selected value, set a single div
|
|
449
|
+
this.main.values.innerHTML = singleValue.outerHTML
|
|
450
|
+
return
|
|
451
|
+
} else {
|
|
452
|
+
// If there is a message, remove it
|
|
453
|
+
const maxValuesMessage = this.main.values.querySelector('.' + this.classes.max)
|
|
454
|
+
if (maxValuesMessage) {
|
|
455
|
+
maxValuesMessage.remove()
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
437
459
|
// Loop through currentNodes and only include ones that are not in selectedIDs
|
|
438
460
|
let removeNodes: HTMLDivElement[] = []
|
|
439
461
|
for (let i = 0; i < currentNodes.length; i++) {
|
|
@@ -504,6 +526,11 @@ export default class Render {
|
|
|
504
526
|
e.preventDefault()
|
|
505
527
|
e.stopPropagation()
|
|
506
528
|
|
|
529
|
+
// Dont do anything if disabled
|
|
530
|
+
if (this.settings.disabled) {
|
|
531
|
+
return
|
|
532
|
+
}
|
|
533
|
+
|
|
507
534
|
// By Default we will delete
|
|
508
535
|
let shouldDelete = true
|
|
509
536
|
const before = this.store.getSelectedOptions()
|
|
@@ -535,7 +562,7 @@ export default class Render {
|
|
|
535
562
|
selectedValues.push(o.value)
|
|
536
563
|
}
|
|
537
564
|
}
|
|
538
|
-
this.callbacks.setSelected(selectedValues)
|
|
565
|
+
this.callbacks.setSelected(selectedValues, false)
|
|
539
566
|
|
|
540
567
|
// Check if we need to close the dropdown
|
|
541
568
|
if (this.settings.closeOnSelect) {
|
|
@@ -565,28 +592,10 @@ export default class Render {
|
|
|
565
592
|
|
|
566
593
|
public contentDiv(): Content {
|
|
567
594
|
const main = document.createElement('div')
|
|
568
|
-
main.classList.add(this.classes.content)
|
|
569
595
|
|
|
570
596
|
// Add id to data-id
|
|
571
597
|
main.dataset.id = this.settings.id
|
|
572
598
|
|
|
573
|
-
// Add styles
|
|
574
|
-
if (this.settings.style !== '') {
|
|
575
|
-
main.style.cssText = this.settings.style
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
// Add classes
|
|
579
|
-
if (this.settings.contentPosition === 'relative') {
|
|
580
|
-
main.classList.add('ss-' + this.settings.contentPosition)
|
|
581
|
-
}
|
|
582
|
-
if (this.settings.class.length) {
|
|
583
|
-
for (const c of this.settings.class) {
|
|
584
|
-
if (c.trim() !== '') {
|
|
585
|
-
main.classList.add(c.trim())
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
599
|
// Add search
|
|
591
600
|
const search = this.searchDiv()
|
|
592
601
|
main.appendChild(search.main)
|
|
@@ -603,14 +612,27 @@ export default class Render {
|
|
|
603
612
|
}
|
|
604
613
|
|
|
605
614
|
public moveContent(): void {
|
|
615
|
+
// If contentPosition is relative, dont move the content anywhere other than below
|
|
606
616
|
if (this.settings.contentPosition === 'relative') {
|
|
617
|
+
this.moveContentBelow()
|
|
607
618
|
return
|
|
608
619
|
}
|
|
609
620
|
|
|
610
|
-
|
|
611
|
-
this.
|
|
612
|
-
|
|
613
|
-
|
|
621
|
+
// If openContent is not auto set content
|
|
622
|
+
if (this.settings.openPosition === 'down') {
|
|
623
|
+
this.moveContentBelow()
|
|
624
|
+
return
|
|
625
|
+
} else if (this.settings.openPosition === 'up') {
|
|
626
|
+
this.moveContentAbove()
|
|
627
|
+
return
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Auto - Determine where to put the content
|
|
631
|
+
if (this.putContent() === 'up') {
|
|
632
|
+
this.moveContentAbove()
|
|
633
|
+
} else {
|
|
634
|
+
this.moveContentBelow()
|
|
635
|
+
}
|
|
614
636
|
}
|
|
615
637
|
|
|
616
638
|
public searchDiv(): Search {
|
|
@@ -729,9 +751,9 @@ export default class Render {
|
|
|
729
751
|
if (this.settings.isMultiple) {
|
|
730
752
|
let values = this.store.getSelected()
|
|
731
753
|
values.push(newOption.value)
|
|
732
|
-
this.callbacks.setSelected(values)
|
|
754
|
+
this.callbacks.setSelected(values, true)
|
|
733
755
|
} else {
|
|
734
|
-
this.callbacks.setSelected([newOption.value])
|
|
756
|
+
this.callbacks.setSelected([newOption.value], true)
|
|
735
757
|
}
|
|
736
758
|
|
|
737
759
|
// Clear search
|
|
@@ -908,8 +930,7 @@ export default class Render {
|
|
|
908
930
|
// If no results show no results text
|
|
909
931
|
if (data.length === 0) {
|
|
910
932
|
const noResults = document.createElement('div')
|
|
911
|
-
noResults.classList.add(this.classes.
|
|
912
|
-
noResults.classList.add(this.classes.disabled)
|
|
933
|
+
noResults.classList.add(this.classes.search)
|
|
913
934
|
noResults.innerHTML = this.settings.searchText
|
|
914
935
|
this.content.list.appendChild(noResults)
|
|
915
936
|
return
|
|
@@ -1001,13 +1022,13 @@ export default class Render {
|
|
|
1001
1022
|
return true
|
|
1002
1023
|
})
|
|
1003
1024
|
|
|
1004
|
-
this.callbacks.setSelected(newSelected)
|
|
1025
|
+
this.callbacks.setSelected(newSelected, true)
|
|
1005
1026
|
return
|
|
1006
1027
|
} else {
|
|
1007
1028
|
// Put together new list with all options in this optgroup
|
|
1008
1029
|
const newSelected = currentSelected.concat(d.options.map((o) => o.value))
|
|
1009
1030
|
|
|
1010
|
-
this.callbacks.setSelected(newSelected)
|
|
1031
|
+
this.callbacks.setSelected(newSelected, true)
|
|
1011
1032
|
}
|
|
1012
1033
|
})
|
|
1013
1034
|
|
|
@@ -1031,8 +1052,8 @@ export default class Render {
|
|
|
1031
1052
|
const optgroupClosableArrow = document.createElementNS('http://www.w3.org/2000/svg', 'path')
|
|
1032
1053
|
optgroupClosableSvg.appendChild(optgroupClosableArrow)
|
|
1033
1054
|
|
|
1034
|
-
// If any options are selected, set optgroup to open
|
|
1035
|
-
if (d.options.some((o) => o.selected)) {
|
|
1055
|
+
// If any options are selected or someone is searching, set optgroup to open
|
|
1056
|
+
if (d.options.some((o) => o.selected) || this.content.search.input.value.trim() !== '') {
|
|
1036
1057
|
optgroupClosable.classList.add(this.classes.open)
|
|
1037
1058
|
optgroupClosableArrow.setAttribute('d', this.classes.arrowOpen)
|
|
1038
1059
|
} else if (d.closable === 'open') {
|
|
@@ -1109,9 +1130,8 @@ export default class Render {
|
|
|
1109
1130
|
|
|
1110
1131
|
// Set option content
|
|
1111
1132
|
if (this.settings.searchHighlight && this.content.search.input.value.trim() !== '') {
|
|
1112
|
-
const textOrHtml = option.html !== '' ? option.html : option.text
|
|
1113
1133
|
optionEl.innerHTML = this.highlightText(
|
|
1114
|
-
|
|
1134
|
+
option.html !== '' ? option.html : option.text,
|
|
1115
1135
|
this.content.search.input.value,
|
|
1116
1136
|
this.classes.searchHighlighter,
|
|
1117
1137
|
)
|
|
@@ -1126,8 +1146,13 @@ export default class Render {
|
|
|
1126
1146
|
optionEl.setAttribute('title', optionEl.textContent)
|
|
1127
1147
|
}
|
|
1128
1148
|
|
|
1149
|
+
// If option is disabled
|
|
1150
|
+
if (!option.display) {
|
|
1151
|
+
optionEl.classList.add(this.classes.hide)
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1129
1154
|
// If allowed to deselect, null onclick and add disabled
|
|
1130
|
-
if (
|
|
1155
|
+
if (option.disabled) {
|
|
1131
1156
|
optionEl.classList.add(this.classes.disabled)
|
|
1132
1157
|
}
|
|
1133
1158
|
|
|
@@ -1158,11 +1183,11 @@ export default class Render {
|
|
|
1158
1183
|
return
|
|
1159
1184
|
}
|
|
1160
1185
|
|
|
1161
|
-
// Check limit and do nothing if limit is reached
|
|
1186
|
+
// Check limit and do nothing if limit is reached and the option is not selected
|
|
1187
|
+
// Also check reverse for min limit and is selected
|
|
1162
1188
|
if (
|
|
1163
|
-
this.settings.isMultiple &&
|
|
1164
|
-
|
|
1165
|
-
this.settings.maxSelected <= selectedOptions.length
|
|
1189
|
+
(this.settings.isMultiple && this.settings.maxSelected <= selectedOptions.length && !option.selected) ||
|
|
1190
|
+
(this.settings.isMultiple && this.settings.minSelected >= selectedOptions.length && option.selected)
|
|
1166
1191
|
) {
|
|
1167
1192
|
return
|
|
1168
1193
|
}
|
|
@@ -1216,7 +1241,10 @@ export default class Render {
|
|
|
1216
1241
|
}
|
|
1217
1242
|
|
|
1218
1243
|
// Get values from after and set as selected
|
|
1219
|
-
this.callbacks.setSelected(
|
|
1244
|
+
this.callbacks.setSelected(
|
|
1245
|
+
after.map((o: Option) => o.value),
|
|
1246
|
+
false,
|
|
1247
|
+
)
|
|
1220
1248
|
|
|
1221
1249
|
// If closeOnSelect is true
|
|
1222
1250
|
if (this.settings.closeOnSelect) {
|
|
@@ -1260,27 +1288,40 @@ export default class Render {
|
|
|
1260
1288
|
}
|
|
1261
1289
|
|
|
1262
1290
|
public moveContentAbove(): void {
|
|
1263
|
-
|
|
1264
|
-
|
|
1291
|
+
// Get main and content height
|
|
1292
|
+
const mainHeight = this.main.main.offsetHeight
|
|
1265
1293
|
const contentHeight = this.content.main.offsetHeight
|
|
1266
|
-
const height = mainHeight + contentHeight - 1
|
|
1267
|
-
this.content.main.style.margin = '-' + height + 'px 0px 0px 0px'
|
|
1268
|
-
this.content.main.style.transformOrigin = 'center bottom'
|
|
1269
1294
|
|
|
1295
|
+
// Set classes
|
|
1270
1296
|
this.main.main.classList.remove(this.classes.openBelow)
|
|
1271
1297
|
this.main.main.classList.add(this.classes.openAbove)
|
|
1272
1298
|
this.content.main.classList.remove(this.classes.openBelow)
|
|
1273
1299
|
this.content.main.classList.add(this.classes.openAbove)
|
|
1300
|
+
|
|
1301
|
+
// Set the content position
|
|
1302
|
+
const containerRect = this.main.main.getBoundingClientRect()
|
|
1303
|
+
this.content.main.style.margin = '-' + (mainHeight + contentHeight - 1) + 'px 0px 0px 0px'
|
|
1304
|
+
this.content.main.style.top = containerRect.top + containerRect.height + window.scrollY + 'px'
|
|
1305
|
+
this.content.main.style.left = containerRect.left + window.scrollX + 'px'
|
|
1306
|
+
this.content.main.style.width = containerRect.width + 'px'
|
|
1274
1307
|
}
|
|
1275
1308
|
|
|
1276
1309
|
public moveContentBelow(): void {
|
|
1277
|
-
|
|
1278
|
-
this.content.main.style.transformOrigin = 'center top'
|
|
1279
|
-
|
|
1310
|
+
// Set classes
|
|
1280
1311
|
this.main.main.classList.remove(this.classes.openAbove)
|
|
1281
1312
|
this.main.main.classList.add(this.classes.openBelow)
|
|
1282
1313
|
this.content.main.classList.remove(this.classes.openAbove)
|
|
1283
1314
|
this.content.main.classList.add(this.classes.openBelow)
|
|
1315
|
+
|
|
1316
|
+
// Set the content position
|
|
1317
|
+
const containerRect = this.main.main.getBoundingClientRect()
|
|
1318
|
+
this.content.main.style.margin = '-1px 0px 0px 0px'
|
|
1319
|
+
// Dont do anything if the content is relative
|
|
1320
|
+
if (this.settings.contentPosition !== 'relative') {
|
|
1321
|
+
this.content.main.style.top = containerRect.top + containerRect.height + window.scrollY + 'px'
|
|
1322
|
+
this.content.main.style.left = containerRect.left + window.scrollX + 'px'
|
|
1323
|
+
this.content.main.style.width = containerRect.width + 'px'
|
|
1324
|
+
}
|
|
1284
1325
|
}
|
|
1285
1326
|
|
|
1286
1327
|
public ensureElementInView(container: HTMLElement, element: HTMLElement): void {
|
|
@@ -1300,20 +1341,28 @@ export default class Render {
|
|
|
1300
1341
|
}
|
|
1301
1342
|
}
|
|
1302
1343
|
|
|
1303
|
-
public putContent(
|
|
1304
|
-
|
|
1305
|
-
const
|
|
1306
|
-
const
|
|
1307
|
-
const
|
|
1344
|
+
public putContent(): 'up' | 'down' {
|
|
1345
|
+
// Get main and content height
|
|
1346
|
+
const mainHeight = this.main.main.offsetHeight
|
|
1347
|
+
const mainRect = this.main.main.getBoundingClientRect()
|
|
1348
|
+
const contentHeight = this.content.main.offsetHeight
|
|
1308
1349
|
|
|
1309
|
-
if
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1350
|
+
// From bottom of mainHeight figure out if content will fit below without going below the window
|
|
1351
|
+
const spaceBelow = window.innerHeight - (mainRect.top + mainHeight)
|
|
1352
|
+
|
|
1353
|
+
// If space below is less than content height
|
|
1354
|
+
if (spaceBelow <= contentHeight) {
|
|
1355
|
+
// If space above is more than content height
|
|
1356
|
+
if (mainRect.top > contentHeight) {
|
|
1357
|
+
// Move content above
|
|
1358
|
+
return 'up'
|
|
1359
|
+
} else {
|
|
1360
|
+
// Move content below
|
|
1361
|
+
return 'down'
|
|
1362
|
+
}
|
|
1314
1363
|
}
|
|
1315
1364
|
|
|
1316
|
-
//
|
|
1365
|
+
// Move content below
|
|
1317
1366
|
return 'down'
|
|
1318
1367
|
}
|
|
1319
1368
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
'use strict'
|
|
6
6
|
|
|
7
|
-
import { describe, expect, test } from '@jest/globals'
|
|
7
|
+
import { describe, expect, jest, test } from '@jest/globals'
|
|
8
8
|
import Select from './select'
|
|
9
9
|
import Store, { Optgroup, Option } from './store'
|
|
10
10
|
|
|
@@ -19,7 +19,7 @@ describe('select module', () => {
|
|
|
19
19
|
|
|
20
20
|
test('get data from select options', () => {
|
|
21
21
|
document.body.innerHTML = `<select id="test">
|
|
22
|
-
<option value="1">One</option>
|
|
22
|
+
<option id="value1" value="1">One</option>
|
|
23
23
|
<option value="2">Two</option>
|
|
24
24
|
</select>`
|
|
25
25
|
|
|
@@ -27,6 +27,7 @@ describe('select module', () => {
|
|
|
27
27
|
let select = new Select(selectElement)
|
|
28
28
|
let data = select.getData() as Option[]
|
|
29
29
|
expect(data.length).toBe(2)
|
|
30
|
+
expect(data[0].id).toBe('value1')
|
|
30
31
|
expect(data[0].value).toBe('1')
|
|
31
32
|
expect(data[0].text).toBe('One')
|
|
32
33
|
expect(data[1].value).toBe('2')
|
|
@@ -88,10 +89,12 @@ describe('select module', () => {
|
|
|
88
89
|
let data = store.getData()
|
|
89
90
|
select.updateOptions(data)
|
|
90
91
|
|
|
91
|
-
expect(selectElement.
|
|
92
|
+
expect(selectElement.outerHTML).toBe(
|
|
93
|
+
'<select id="test"><option id="1" value="1">One</option><option id="2" value="2">Two</option></select>',
|
|
94
|
+
)
|
|
92
95
|
})
|
|
93
96
|
|
|
94
|
-
test('event change value listener', () => {
|
|
97
|
+
test('event change value listener', async () => {
|
|
95
98
|
document.body.innerHTML = `<select id="test">
|
|
96
99
|
<option value="1">One</option>
|
|
97
100
|
<option value="2">Two</option>
|
|
@@ -105,24 +108,56 @@ describe('select module', () => {
|
|
|
105
108
|
|
|
106
109
|
expect(data[0].selected).toBe(true)
|
|
107
110
|
|
|
108
|
-
|
|
109
|
-
select.
|
|
110
|
-
didChange = true
|
|
111
|
-
})
|
|
111
|
+
const callback = jest.fn()
|
|
112
|
+
select.onValueChange = callback
|
|
112
113
|
|
|
114
|
+
// Change the value
|
|
113
115
|
selectElement.value = '2'
|
|
116
|
+
selectElement.dispatchEvent(new Event('change'))
|
|
114
117
|
|
|
115
|
-
|
|
118
|
+
// TODO: figure out why this is not working
|
|
119
|
+
// expect(callback).toHaveBeenCalled()
|
|
120
|
+
|
|
121
|
+
// Get selected data
|
|
122
|
+
const selected = select.getSelectedValues()
|
|
123
|
+
expect(selected[0]).toBe('2')
|
|
116
124
|
|
|
125
|
+
// Change the full data
|
|
126
|
+
data = select.getData() as Option[]
|
|
117
127
|
expect(data[1].selected).toBe(true)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
test('event change value listener when options are replaced', () => {
|
|
131
|
+
document.body.innerHTML = `<select id="test">
|
|
132
|
+
<option value="1">One</option>
|
|
133
|
+
<option value="2">Two</option>
|
|
134
|
+
<option value="3">Three</option>
|
|
135
|
+
</select>`
|
|
136
|
+
|
|
137
|
+
let selectElement = document.getElementById('test') as HTMLSelectElement
|
|
138
|
+
let select = new Select(selectElement)
|
|
139
|
+
|
|
140
|
+
let data = select.getData() as Option[]
|
|
141
|
+
|
|
142
|
+
expect(data[0].selected).toBe(true)
|
|
143
|
+
|
|
144
|
+
const callback = jest.fn()
|
|
145
|
+
select.onValueChange = callback
|
|
146
|
+
|
|
147
|
+
selectElement.innerHTML = `<option value="4">Four</option>
|
|
148
|
+
<option value="5" selected>Five</option>
|
|
149
|
+
<option value="6">Six</option>`
|
|
150
|
+
|
|
151
|
+
// TODO: figure out why this is not working
|
|
152
|
+
// expect(callback).toHaveBeenCalled()
|
|
118
153
|
|
|
119
154
|
// Give the mutation observer time to run
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
155
|
+
data = select.getData() as Option[]
|
|
156
|
+
expect(data[1].value).toBe('5')
|
|
157
|
+
expect(data[1].selected).toBe(true)
|
|
123
158
|
})
|
|
124
159
|
|
|
125
|
-
test('mutation observer listener', () => {
|
|
160
|
+
test('mutation observer listener for select option changes', () => {
|
|
126
161
|
document.body.innerHTML = `<select id="test">
|
|
127
162
|
<option value="1">One</option>
|
|
128
163
|
<option value="2">Two</option>
|
|
@@ -133,23 +168,108 @@ describe('select module', () => {
|
|
|
133
168
|
let select = new Select(selectElement)
|
|
134
169
|
|
|
135
170
|
let data = select.getData() as Option[]
|
|
136
|
-
|
|
137
171
|
expect(data.length).toBe(3)
|
|
138
172
|
|
|
139
|
-
|
|
140
|
-
select.
|
|
141
|
-
didChange = true
|
|
142
|
-
})
|
|
173
|
+
const callback = jest.fn()
|
|
174
|
+
select.onOptionsChange = callback
|
|
143
175
|
|
|
144
176
|
selectElement.innerHTML = '<option value="1">One</option><option value="2">Two</option>'
|
|
145
177
|
|
|
146
|
-
|
|
178
|
+
// TODO: figure out why this is not working
|
|
179
|
+
// expect(callback).toHaveBeenCalled()
|
|
147
180
|
|
|
181
|
+
data = select.getData() as Option[]
|
|
148
182
|
expect(data.length).toBe(2)
|
|
183
|
+
})
|
|
149
184
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
185
|
+
test('mutation observer listener for select optgroup option changes', () => {
|
|
186
|
+
document.body.innerHTML = `<select id="test">
|
|
187
|
+
<optgroup id="test_optgroup" label="test1">
|
|
188
|
+
<option value="1">One</option>
|
|
189
|
+
<option value="2">Two</option>
|
|
190
|
+
</optgroup>
|
|
191
|
+
<optgroup label="test2">
|
|
192
|
+
<option value="3">Three</option>
|
|
193
|
+
<option value="4">Four</option>
|
|
194
|
+
<option value="5">Five</option>
|
|
195
|
+
</optgroup>
|
|
196
|
+
</select>`
|
|
197
|
+
|
|
198
|
+
let selectElement = document.getElementById('test') as HTMLSelectElement
|
|
199
|
+
let select = new Select(selectElement)
|
|
200
|
+
|
|
201
|
+
let dataOptgroup = select.getData() as Optgroup[]
|
|
202
|
+
|
|
203
|
+
expect(dataOptgroup.length).toBe(2)
|
|
204
|
+
|
|
205
|
+
const callback = jest.fn()
|
|
206
|
+
select.onOptionsChange = callback
|
|
207
|
+
|
|
208
|
+
let selectOptgroup = document.getElementById('test_optgroup') as HTMLOptGroupElement
|
|
209
|
+
selectOptgroup.innerHTML = '<option value="8">Eight</option><option value="9">Nine</option>'
|
|
210
|
+
|
|
211
|
+
// TODO: figure out why this is not working
|
|
212
|
+
// expect(callback).toHaveBeenCalled()
|
|
213
|
+
|
|
214
|
+
let dataOptgroups = select.getData() as Option[]
|
|
215
|
+
expect(dataOptgroups.length).toBe(2)
|
|
216
|
+
|
|
217
|
+
// get selected data
|
|
218
|
+
let selected = select.getSelectedValues()
|
|
219
|
+
expect(selected.length).toBe(1)
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
test('mutation observer listener for select option text changes', () => {
|
|
223
|
+
document.body.innerHTML = `<select id="test">
|
|
224
|
+
<option value="1">One</option>
|
|
225
|
+
<option value="2">Two</option>
|
|
226
|
+
<option value="3">Three</option>
|
|
227
|
+
</select>`
|
|
228
|
+
|
|
229
|
+
let selectElement = document.getElementById('test') as HTMLSelectElement
|
|
230
|
+
let select = new Select(selectElement)
|
|
231
|
+
|
|
232
|
+
let data = select.getData() as Option[]
|
|
233
|
+
|
|
234
|
+
expect(data[0].text).toBe('One')
|
|
235
|
+
|
|
236
|
+
const callback = jest.fn()
|
|
237
|
+
select.onOptionsChange = callback
|
|
238
|
+
|
|
239
|
+
let option = selectElement.options[0]
|
|
240
|
+
option.text = 'New One'
|
|
241
|
+
|
|
242
|
+
data = select.getData() as Option[]
|
|
243
|
+
expect(data[0].text).toBe('New One')
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
test('mutation observer listener for select optgroup option text changes', () => {
|
|
247
|
+
document.body.innerHTML = `<select id="test">
|
|
248
|
+
<optgroup id="test_optgroup" label="test1">
|
|
249
|
+
<option id=test_option value="1">One</option>
|
|
250
|
+
<option value="2">Two</option>
|
|
251
|
+
</optgroup>
|
|
252
|
+
<optgroup label="test2">
|
|
253
|
+
<option value="3">Three</option>
|
|
254
|
+
<option value="4">Four</option>
|
|
255
|
+
<option value="5">Five</option>
|
|
256
|
+
</optgroup>
|
|
257
|
+
</select>`
|
|
258
|
+
|
|
259
|
+
let selectElement = document.getElementById('test') as HTMLSelectElement
|
|
260
|
+
let select = new Select(selectElement)
|
|
261
|
+
|
|
262
|
+
let dataOptgroup = select.getData() as Optgroup[]
|
|
263
|
+
|
|
264
|
+
expect(dataOptgroup[0].options[0].text).toBe('One')
|
|
265
|
+
|
|
266
|
+
const callback = jest.fn()
|
|
267
|
+
select.onOptionsChange = callback
|
|
268
|
+
|
|
269
|
+
let selectOption = document.getElementById('test_option') as HTMLOptionElement
|
|
270
|
+
selectOption.text = 'New One'
|
|
271
|
+
|
|
272
|
+
let dataOption = select.getData() as Optgroup[]
|
|
273
|
+
expect(dataOption[0].options[0].text).toBe('New One')
|
|
154
274
|
})
|
|
155
275
|
})
|