slim-select 1.27.0 → 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 -1956
- 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 -39
- 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 +318 -413
- 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 -107
- 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.4db6930d.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.db13c724.js +0 -2
- package/docs/js/app.db13c724.js.map +0 -1
- package/docs/js/chunk-vendors.cc40aa28.js +0 -28
- package/docs/js/chunk-vendors.cc40aa28.js.map +0 -1
- package/docs/js/home.8bcb44fd.js +0 -2
- package/docs/js/home.8bcb44fd.js.map +0 -1
- package/docs/js/install.02f31028.js +0 -2
- package/docs/js/install.02f31028.js.map +0 -1
- package/docs/js/methods.1b31e988.js +0 -11
- package/docs/js/methods.1b31e988.js.map +0 -1
- package/docs/js/options.39fe1005.js +0 -2
- package/docs/js/options.39fe1005.js.map +0 -1
- package/docs/js/selects.f4d0b859.js +0 -2
- package/docs/js/selects.f4d0b859.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 -770
- package/tslint.json +0 -26
- package/vue.config.js +0 -23
package/src/slim-select/index.ts
CHANGED
|
@@ -1,523 +1,428 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { debounce, hasClassInTree } from './helper'
|
|
2
|
+
import Render from './render'
|
|
3
|
+
import Select from './select'
|
|
4
|
+
import Settings, { SettingsPartial } from './settings'
|
|
5
|
+
import Store, { DataArray, DataArrayPartial, Option, OptionOptional } from './store'
|
|
6
|
+
|
|
7
|
+
// Export everything except the "export default"
|
|
8
|
+
export * from './helper'
|
|
9
|
+
export * from './settings'
|
|
10
|
+
export * from './select'
|
|
11
|
+
export * from './store'
|
|
12
|
+
export * from './render'
|
|
13
|
+
|
|
14
|
+
// Export all "export defaults"
|
|
15
|
+
export { Settings, Select, Store, Render }
|
|
16
|
+
|
|
17
|
+
export interface Config {
|
|
8
18
|
select: string | Element
|
|
9
|
-
data?:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
searchingText?: string
|
|
14
|
-
searchFocus?: boolean
|
|
15
|
-
searchHighlight?: boolean
|
|
16
|
-
searchFilter?: (opt: Option, search: string) => boolean
|
|
17
|
-
closeOnSelect?: boolean
|
|
18
|
-
showContent?: string
|
|
19
|
-
placeholder?: string
|
|
20
|
-
allowDeselect?: boolean
|
|
21
|
-
allowDeselectOption?: boolean
|
|
22
|
-
hideSelectedOption?: boolean
|
|
23
|
-
deselectLabel?: string
|
|
24
|
-
isEnabled?: boolean
|
|
25
|
-
valuesUseText?: boolean // Use text value when showing selected value
|
|
26
|
-
showOptionTooltips?: boolean
|
|
27
|
-
selectByGroup?: boolean
|
|
28
|
-
limit?: number
|
|
29
|
-
timeoutDelay?: number
|
|
30
|
-
addToBody?: boolean
|
|
19
|
+
data?: DataArrayPartial
|
|
20
|
+
settings?: SettingsPartial
|
|
21
|
+
events?: Events
|
|
22
|
+
}
|
|
31
23
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
export interface Events {
|
|
25
|
+
search?: (searchValue: string, currentData: DataArray) => Promise<DataArrayPartial> | DataArrayPartial
|
|
26
|
+
searchFilter?: (option: Option, search: string) => boolean
|
|
27
|
+
addable?: (value: string) => OptionOptional | string
|
|
28
|
+
beforeChange?: (newVal: Option[], oldVal: Option[]) => boolean | void
|
|
29
|
+
afterChange?: (newVal: Option[]) => void
|
|
37
30
|
beforeOpen?: () => void
|
|
38
31
|
afterOpen?: () => void
|
|
39
32
|
beforeClose?: () => void
|
|
40
33
|
afterClose?: () => void
|
|
34
|
+
error?: (err: Error) => void
|
|
41
35
|
}
|
|
42
36
|
|
|
43
37
|
export default class SlimSelect {
|
|
44
|
-
public
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
public
|
|
48
|
-
public
|
|
49
|
-
public
|
|
50
|
-
public
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
public
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
38
|
+
public selectEl: HTMLSelectElement
|
|
39
|
+
|
|
40
|
+
// Classes
|
|
41
|
+
public settings!: Settings
|
|
42
|
+
public select!: Select
|
|
43
|
+
public store!: Store
|
|
44
|
+
public render!: Render
|
|
45
|
+
|
|
46
|
+
// Events
|
|
47
|
+
public events = {
|
|
48
|
+
search: undefined,
|
|
49
|
+
searchFilter: (opt: Option, search: string) => {
|
|
50
|
+
return opt.text.toLowerCase().indexOf(search.toLowerCase()) !== -1
|
|
51
|
+
},
|
|
52
|
+
addable: undefined,
|
|
53
|
+
beforeChange: undefined,
|
|
54
|
+
afterChange: undefined,
|
|
55
|
+
beforeOpen: undefined,
|
|
56
|
+
afterOpen: undefined,
|
|
57
|
+
beforeClose: undefined,
|
|
58
|
+
afterClose: undefined,
|
|
59
|
+
} as Events
|
|
60
|
+
|
|
61
|
+
constructor(config: Config) {
|
|
62
|
+
// Make sure you get the right element
|
|
63
|
+
this.selectEl = (
|
|
64
|
+
typeof config.select === 'string' ? document.querySelector(config.select) : config.select
|
|
65
|
+
) as HTMLSelectElement
|
|
66
|
+
if (!this.selectEl) {
|
|
67
|
+
if (config.events && config.events.error) {
|
|
68
|
+
config.events.error(new Error('Could not find select element'))
|
|
63
69
|
}
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
if (this.selectEl.tagName !== 'SELECT') {
|
|
73
|
+
if (config.events && config.events.error) {
|
|
74
|
+
config.events.error(new Error('Element isnt of type select'))
|
|
75
|
+
}
|
|
76
|
+
return
|
|
64
77
|
}
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
constructor(info: Constructor) {
|
|
68
|
-
const selectElement = this.validate(info)
|
|
69
78
|
|
|
70
79
|
// If select already has a slim select id on it lets destroy it first
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (info.addable) { this.addable = info.addable }
|
|
78
|
-
|
|
79
|
-
this.config = new Config({
|
|
80
|
-
select: selectElement,
|
|
81
|
-
isAjax: (info.ajax ? true : false),
|
|
82
|
-
showSearch: info.showSearch,
|
|
83
|
-
searchPlaceholder: info.searchPlaceholder,
|
|
84
|
-
searchText: info.searchText,
|
|
85
|
-
searchingText: info.searchingText,
|
|
86
|
-
searchFocus: info.searchFocus,
|
|
87
|
-
searchHighlight: info.searchHighlight,
|
|
88
|
-
searchFilter: info.searchFilter,
|
|
89
|
-
closeOnSelect: info.closeOnSelect,
|
|
90
|
-
showContent: info.showContent,
|
|
91
|
-
placeholderText: info.placeholder,
|
|
92
|
-
allowDeselect: info.allowDeselect,
|
|
93
|
-
allowDeselectOption: info.allowDeselectOption,
|
|
94
|
-
hideSelectedOption: info.hideSelectedOption,
|
|
95
|
-
deselectLabel: info.deselectLabel,
|
|
96
|
-
isEnabled: info.isEnabled,
|
|
97
|
-
valuesUseText: info.valuesUseText,
|
|
98
|
-
showOptionTooltips: info.showOptionTooltips,
|
|
99
|
-
selectByGroup: info.selectByGroup,
|
|
100
|
-
limit: info.limit,
|
|
101
|
-
timeoutDelay: info.timeoutDelay,
|
|
102
|
-
addToBody: info.addToBody
|
|
103
|
-
})
|
|
80
|
+
if (this.selectEl.dataset.ssid) {
|
|
81
|
+
this.destroy()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Set settings
|
|
85
|
+
this.settings = new Settings(config.settings)
|
|
104
86
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
87
|
+
// Set events
|
|
88
|
+
for (const key in config.events) {
|
|
89
|
+
if (config.events.hasOwnProperty(key)) {
|
|
90
|
+
;(this.events as { [key: string]: any })[key] = (config.events as { [key: string]: any })[key]
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Upate settings with type, style and classname
|
|
95
|
+
this.settings.isMultiple = this.selectEl.multiple
|
|
96
|
+
this.settings.style = this.selectEl.style.cssText
|
|
97
|
+
this.settings.class = this.selectEl.className.split(' ')
|
|
98
|
+
|
|
99
|
+
// Set select class
|
|
100
|
+
this.select = new Select(this.selectEl)
|
|
101
|
+
this.select.updateSelect(this.settings.id, this.settings.style, this.settings.class)
|
|
102
|
+
this.select.hideUI() // Hide the original select element
|
|
103
|
+
|
|
104
|
+
// Add select listeners
|
|
105
|
+
this.select.addSelectChangeListener((data: DataArray) => {
|
|
106
|
+
// Run set data from the values given
|
|
107
|
+
this.setData(data)
|
|
108
|
+
})
|
|
109
|
+
this.select.addValueChangeListener((values: string[]) => {
|
|
110
|
+
// Run set selected from the values given
|
|
111
|
+
this.setSelected(values)
|
|
108
112
|
})
|
|
109
113
|
|
|
110
|
-
|
|
111
|
-
this.
|
|
114
|
+
// Set store class
|
|
115
|
+
this.store = new Store(
|
|
116
|
+
this.settings.isMultiple ? 'multiple' : 'single',
|
|
117
|
+
config.data ? config.data : this.select.getData(),
|
|
118
|
+
)
|
|
112
119
|
|
|
113
|
-
//
|
|
114
|
-
if (
|
|
115
|
-
this.select.
|
|
120
|
+
// If data is passed update the original select element
|
|
121
|
+
if (config.data) {
|
|
122
|
+
this.select.updateOptions(this.store.getData())
|
|
116
123
|
}
|
|
117
124
|
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
this.
|
|
125
|
+
// Set render callbacks
|
|
126
|
+
const callbacks = {
|
|
127
|
+
open: this.open.bind(this),
|
|
128
|
+
close: this.close.bind(this),
|
|
129
|
+
addable: this.events.addable ? this.events.addable : undefined,
|
|
130
|
+
setSelected: this.setSelected.bind(this),
|
|
131
|
+
addOption: this.addOption.bind(this),
|
|
132
|
+
search: this.search.bind(this),
|
|
133
|
+
beforeChange: this.events.beforeChange,
|
|
134
|
+
afterChange: this.events.afterChange,
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Setup render class
|
|
138
|
+
this.render = new Render(this.settings, this.store, callbacks)
|
|
139
|
+
|
|
140
|
+
// Add render after original select element
|
|
141
|
+
if (this.selectEl.parentNode) {
|
|
142
|
+
this.selectEl.parentNode.insertBefore(this.render.main.main, this.selectEl.nextSibling)
|
|
125
143
|
}
|
|
126
144
|
|
|
127
145
|
// Add onclick listener to document to closeContent if clicked outside
|
|
128
146
|
document.addEventListener('click', this.documentClick)
|
|
129
147
|
|
|
148
|
+
// Add window resize listener to moveContent if window size changes
|
|
149
|
+
window.addEventListener('resize', this.windowResize, false)
|
|
150
|
+
|
|
130
151
|
// If the user wants to show the content forcibly on a specific side,
|
|
131
152
|
// there is no need to listen for scroll events
|
|
132
|
-
if (this.
|
|
153
|
+
if (this.settings.openPosition === 'auto') {
|
|
133
154
|
window.addEventListener('scroll', this.windowScroll, false)
|
|
134
155
|
}
|
|
135
156
|
|
|
136
|
-
// Add event callbacks after everthing has been created
|
|
137
|
-
if (info.beforeOnChange) { this.beforeOnChange = info.beforeOnChange }
|
|
138
|
-
if (info.onChange) { this.onChange = info.onChange }
|
|
139
|
-
if (info.beforeOpen) { this.beforeOpen = info.beforeOpen }
|
|
140
|
-
if (info.afterOpen) { this.afterOpen = info.afterOpen }
|
|
141
|
-
if (info.beforeClose) { this.beforeClose = info.beforeClose }
|
|
142
|
-
if (info.afterClose) { this.afterClose = info.afterClose }
|
|
143
|
-
|
|
144
157
|
// If disabled lets call it
|
|
145
|
-
if (!this.
|
|
158
|
+
if (!this.settings.isEnabled) {
|
|
159
|
+
this.disable()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// If alwaysOpnen then open it
|
|
163
|
+
if (this.settings.alwaysOpen) {
|
|
164
|
+
this.open()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Add SlimSelect to select element
|
|
168
|
+
;(this.selectEl as any).slim = this
|
|
146
169
|
}
|
|
147
170
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
171
|
+
// Set to enabled and remove disabled classes
|
|
172
|
+
public enable(): void {
|
|
173
|
+
this.settings.isEnabled = true
|
|
174
|
+
|
|
175
|
+
this.select.enable()
|
|
176
|
+
this.render.enable()
|
|
153
177
|
}
|
|
154
178
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
return outputSelected
|
|
163
|
-
} else {
|
|
164
|
-
const selected = this.data.getSelected() as Option
|
|
165
|
-
return (selected ? selected.value as string : '')
|
|
166
|
-
}
|
|
179
|
+
// Set to disabled and add disabled classes
|
|
180
|
+
public disable(): void {
|
|
181
|
+
this.settings.isEnabled = false
|
|
182
|
+
|
|
183
|
+
this.select.disable()
|
|
184
|
+
this.render.disable()
|
|
167
185
|
}
|
|
168
186
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
187
|
+
public getData(): DataArray {
|
|
188
|
+
return this.store.getData()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
public setData(data: DataArrayPartial): void {
|
|
192
|
+
// Validate data
|
|
193
|
+
const err = this.store.validateDataArray(data)
|
|
194
|
+
if (err) {
|
|
195
|
+
if (this.events.error) {
|
|
196
|
+
this.events.error(err)
|
|
197
|
+
}
|
|
198
|
+
return
|
|
175
199
|
}
|
|
176
|
-
this.select.setValue()
|
|
177
|
-
this.data.onDataChange() // Trigger on change callback
|
|
178
|
-
this.render()
|
|
179
200
|
|
|
180
|
-
|
|
201
|
+
// Update the store
|
|
202
|
+
this.store.setData(data)
|
|
203
|
+
const dataClean = this.store.getData()
|
|
204
|
+
|
|
205
|
+
// Update original select element
|
|
206
|
+
this.select.updateOptions(dataClean)
|
|
207
|
+
|
|
208
|
+
// Update the render
|
|
209
|
+
this.render.renderValues()
|
|
210
|
+
this.render.renderOptions(dataClean)
|
|
181
211
|
}
|
|
182
212
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
this.set(value, type, close, render)
|
|
213
|
+
public getSelected(): string[] {
|
|
214
|
+
return this.store.getSelected()
|
|
186
215
|
}
|
|
187
216
|
|
|
188
|
-
public
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
217
|
+
public setSelected(value: string | string[]): void {
|
|
218
|
+
// Update the store
|
|
219
|
+
this.store.setSelectedBy('value', Array.isArray(value) ? value : [value])
|
|
220
|
+
const data = this.store.getData()
|
|
192
221
|
|
|
193
|
-
|
|
194
|
-
|
|
222
|
+
// Update the select element
|
|
223
|
+
this.select.updateOptions(data)
|
|
195
224
|
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
newData[i].value = newData[i].text
|
|
201
|
-
}
|
|
202
|
-
}
|
|
225
|
+
// Update the render
|
|
226
|
+
this.render.renderValues()
|
|
227
|
+
this.render.renderOptions(data)
|
|
228
|
+
}
|
|
203
229
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
for (const r of reverseSelected) {
|
|
209
|
-
newData.unshift(r)
|
|
210
|
-
}
|
|
211
|
-
} else {
|
|
212
|
-
newData.unshift(selected)
|
|
213
|
-
|
|
214
|
-
// Look for duplicate selected if so remove it
|
|
215
|
-
for (let i = 0; i < newData.length; i++) {
|
|
216
|
-
if (!newData[i].placeholder && newData[i].value === (selected as Option).value && newData[i].text === (selected as Option).text) {
|
|
217
|
-
delete newData[i]
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Add placeholder if it doesnt already have one
|
|
222
|
-
let hasPlaceholder = false
|
|
223
|
-
for (let i = 0; i < newData.length; i++) {
|
|
224
|
-
if (newData[i].placeholder) {
|
|
225
|
-
hasPlaceholder = true
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
if (!hasPlaceholder) {
|
|
229
|
-
newData.unshift({ text: '', placeholder: true })
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
230
|
+
public addOption(option: OptionOptional): void {
|
|
231
|
+
// Add option to store
|
|
232
|
+
this.store.addOption(option)
|
|
233
|
+
const data = this.store.getData()
|
|
233
234
|
|
|
234
|
-
|
|
235
|
-
this.
|
|
236
|
-
this.data.setSelectedFromSelect()
|
|
237
|
-
}
|
|
235
|
+
// Update the select element
|
|
236
|
+
this.select.updateOptions(data)
|
|
238
237
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const isValid = validateData([data])
|
|
243
|
-
if (!isValid) { console.error('Validation problem on: #' + this.select.element.id); return } // If data passed in is not valid DO NOT parse, set and render
|
|
244
|
-
|
|
245
|
-
this.data.add(this.data.newOption(data))
|
|
246
|
-
this.select.create(this.data.data)
|
|
247
|
-
this.data.parseSelectData()
|
|
248
|
-
this.data.setSelectedFromSelect()
|
|
249
|
-
this.render()
|
|
238
|
+
// Update the render
|
|
239
|
+
this.render.renderValues()
|
|
240
|
+
this.render.renderOptions(data)
|
|
250
241
|
}
|
|
251
242
|
|
|
252
|
-
// Open content section
|
|
253
243
|
public open(): void {
|
|
254
244
|
// Dont open if disabled
|
|
255
|
-
if (!this.config.isEnabled) { return }
|
|
256
|
-
|
|
257
245
|
// Dont do anything if the content is already open
|
|
258
|
-
if (this.
|
|
246
|
+
if (!this.settings.isEnabled || this.settings.isOpen) {
|
|
247
|
+
return
|
|
248
|
+
}
|
|
259
249
|
|
|
260
250
|
// Run beforeOpen callback
|
|
261
|
-
if (this.beforeOpen) {
|
|
262
|
-
|
|
263
|
-
if (this.config.isMultiple && this.slim.multiSelected) {
|
|
264
|
-
this.slim.multiSelected.plus.classList.add('ss-cross')
|
|
265
|
-
} else if (this.slim.singleSelected) {
|
|
266
|
-
this.slim.singleSelected.arrowIcon.arrow.classList.remove('arrow-down')
|
|
267
|
-
this.slim.singleSelected.arrowIcon.arrow.classList.add('arrow-up')
|
|
251
|
+
if (this.events.beforeOpen) {
|
|
252
|
+
this.events.beforeOpen()
|
|
268
253
|
}
|
|
269
|
-
(this.slim as any)[(this.config.isMultiple ? 'multiSelected' : 'singleSelected')].container.classList.add((this.data.contentPosition === 'above' ? this.config.openAbove : this.config.openBelow))
|
|
270
|
-
|
|
271
|
-
if (this.config.addToBody) {
|
|
272
|
-
// move the content in to the right location
|
|
273
|
-
const containerRect = this.slim.container.getBoundingClientRect()
|
|
274
|
-
this.slim.content.style.top = (containerRect.top + containerRect.height + window.scrollY) + 'px'
|
|
275
|
-
this.slim.content.style.left = (containerRect.left + window.scrollX) + 'px'
|
|
276
|
-
this.slim.content.style.width = containerRect.width + 'px'
|
|
277
|
-
}
|
|
278
|
-
this.slim.content.classList.add(this.config.open)
|
|
279
254
|
|
|
280
|
-
//
|
|
281
|
-
|
|
282
|
-
this.moveContentAbove()
|
|
283
|
-
} else if (this.config.showContent.toLowerCase() === 'down') {
|
|
284
|
-
this.moveContentBelow()
|
|
285
|
-
} else {
|
|
286
|
-
// Auto identify where to put it
|
|
287
|
-
if (putContent(this.slim.content, this.data.contentPosition, this.data.contentOpen) === 'above') {
|
|
288
|
-
this.moveContentAbove()
|
|
289
|
-
} else {
|
|
290
|
-
this.moveContentBelow()
|
|
291
|
-
}
|
|
292
|
-
}
|
|
255
|
+
// Tell render to open
|
|
256
|
+
this.render.open()
|
|
293
257
|
|
|
294
|
-
//
|
|
295
|
-
if (
|
|
296
|
-
|
|
297
|
-
if (selected) {
|
|
298
|
-
const selectedId = selected.id
|
|
299
|
-
const selectedOption = this.slim.list.querySelector('[data-id="' + selectedId + '"]') as HTMLElement
|
|
300
|
-
if (selectedOption) {
|
|
301
|
-
ensureElementInView(this.slim.list, selectedOption)
|
|
302
|
-
}
|
|
303
|
-
}
|
|
258
|
+
// Focus on input field only if search is enabled
|
|
259
|
+
if (this.settings.showSearch) {
|
|
260
|
+
this.render.searchFocus(false)
|
|
304
261
|
}
|
|
305
262
|
|
|
306
263
|
// setTimeout is for animation completion
|
|
307
264
|
setTimeout(() => {
|
|
308
|
-
this.data.contentOpen = true
|
|
309
|
-
|
|
310
|
-
// Focus on input field
|
|
311
|
-
if (this.config.searchFocus) {
|
|
312
|
-
this.slim.search.input.focus()
|
|
313
|
-
}
|
|
314
|
-
|
|
315
265
|
// Run afterOpen callback
|
|
316
|
-
if (this.afterOpen) {
|
|
317
|
-
this.afterOpen()
|
|
266
|
+
if (this.events.afterOpen) {
|
|
267
|
+
this.events.afterOpen()
|
|
318
268
|
}
|
|
319
|
-
|
|
269
|
+
|
|
270
|
+
// Update settings
|
|
271
|
+
this.settings.isOpen = true
|
|
272
|
+
}, this.settings.timeoutDelay)
|
|
273
|
+
|
|
274
|
+
// Start an interval to check if main has moved
|
|
275
|
+
// in order to keep content close to main
|
|
276
|
+
if (this.settings.intervalMove) {
|
|
277
|
+
clearInterval(this.settings.intervalMove)
|
|
278
|
+
}
|
|
279
|
+
this.settings.intervalMove = setInterval(this.render.moveContent.bind(this.render), 500)
|
|
320
280
|
}
|
|
321
281
|
|
|
322
|
-
// Close content section
|
|
323
282
|
public close(): void {
|
|
324
283
|
// Dont do anything if the content is already closed
|
|
325
|
-
if
|
|
284
|
+
// Dont do anything if alwaysOpen is true
|
|
285
|
+
if (!this.settings.isOpen || this.settings.alwaysOpen) {
|
|
286
|
+
return
|
|
287
|
+
}
|
|
326
288
|
|
|
327
289
|
// Run beforeClose calback
|
|
328
|
-
if (this.beforeClose) {
|
|
329
|
-
|
|
330
|
-
// this.slim.search.input.blur() // Removed due to safari quirk
|
|
331
|
-
if (this.config.isMultiple && this.slim.multiSelected) {
|
|
332
|
-
this.slim.multiSelected.container.classList.remove(this.config.openAbove)
|
|
333
|
-
this.slim.multiSelected.container.classList.remove(this.config.openBelow)
|
|
334
|
-
this.slim.multiSelected.plus.classList.remove('ss-cross')
|
|
335
|
-
} else if (this.slim.singleSelected) {
|
|
336
|
-
this.slim.singleSelected.container.classList.remove(this.config.openAbove)
|
|
337
|
-
this.slim.singleSelected.container.classList.remove(this.config.openBelow)
|
|
338
|
-
this.slim.singleSelected.arrowIcon.arrow.classList.add('arrow-down')
|
|
339
|
-
this.slim.singleSelected.arrowIcon.arrow.classList.remove('arrow-up')
|
|
290
|
+
if (this.events.beforeClose) {
|
|
291
|
+
this.events.beforeClose()
|
|
340
292
|
}
|
|
341
|
-
this.slim.content.classList.remove(this.config.open)
|
|
342
|
-
this.data.contentOpen = false
|
|
343
293
|
|
|
294
|
+
// Tell render to close
|
|
295
|
+
this.render.close()
|
|
296
|
+
|
|
297
|
+
// Clear search
|
|
344
298
|
this.search('') // Clear search
|
|
345
299
|
|
|
300
|
+
// If we arent tabbing focus back on the main element
|
|
301
|
+
this.render.mainFocus(false)
|
|
302
|
+
|
|
346
303
|
// Reset the content below
|
|
347
304
|
setTimeout(() => {
|
|
348
|
-
|
|
349
|
-
this.
|
|
350
|
-
|
|
351
|
-
if (this.config.isMultiple && this.slim.multiSelected) {
|
|
352
|
-
this.slim.multiSelected.container.classList.remove(this.config.openAbove)
|
|
353
|
-
this.slim.multiSelected.container.classList.remove(this.config.openBelow)
|
|
354
|
-
} else if (this.slim.singleSelected) {
|
|
355
|
-
this.slim.singleSelected.container.classList.remove(this.config.openAbove)
|
|
356
|
-
this.slim.singleSelected.container.classList.remove(this.config.openBelow)
|
|
305
|
+
// Run afterClose callback
|
|
306
|
+
if (this.events.afterClose) {
|
|
307
|
+
this.events.afterClose()
|
|
357
308
|
}
|
|
358
309
|
|
|
359
|
-
//
|
|
360
|
-
this.
|
|
310
|
+
// Update settings
|
|
311
|
+
this.settings.isOpen = false
|
|
312
|
+
}, this.settings.timeoutDelay)
|
|
361
313
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
}, this.config.timeoutDelay)
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
public moveContentAbove(): void {
|
|
368
|
-
let selectHeight: number = 0
|
|
369
|
-
if (this.config.isMultiple && this.slim.multiSelected) {
|
|
370
|
-
selectHeight = this.slim.multiSelected.container.offsetHeight
|
|
371
|
-
} else if (this.slim.singleSelected) {
|
|
372
|
-
selectHeight = this.slim.singleSelected.container.offsetHeight
|
|
373
|
-
}
|
|
374
|
-
const contentHeight = this.slim.content.offsetHeight
|
|
375
|
-
const height = selectHeight + contentHeight - 1
|
|
376
|
-
this.slim.content.style.margin = '-' + height + 'px 0 0 0'
|
|
377
|
-
this.slim.content.style.height = (height - selectHeight + 1) + 'px'
|
|
378
|
-
this.slim.content.style.transformOrigin = 'center bottom'
|
|
379
|
-
this.data.contentPosition = 'above'
|
|
380
|
-
|
|
381
|
-
if (this.config.isMultiple && this.slim.multiSelected) {
|
|
382
|
-
this.slim.multiSelected.container.classList.remove(this.config.openBelow)
|
|
383
|
-
this.slim.multiSelected.container.classList.add(this.config.openAbove)
|
|
384
|
-
} else if (this.slim.singleSelected) {
|
|
385
|
-
this.slim.singleSelected.container.classList.remove(this.config.openBelow)
|
|
386
|
-
this.slim.singleSelected.container.classList.add(this.config.openAbove)
|
|
314
|
+
if (this.settings.intervalMove) {
|
|
315
|
+
clearInterval(this.settings.intervalMove)
|
|
387
316
|
}
|
|
388
317
|
}
|
|
389
318
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
this.
|
|
396
|
-
} else if (this.slim.singleSelected) {
|
|
397
|
-
this.slim.singleSelected.container.classList.remove(this.config.openAbove)
|
|
398
|
-
this.slim.singleSelected.container.classList.add(this.config.openBelow)
|
|
319
|
+
// Take in string value and search current options
|
|
320
|
+
public search(value: string): void {
|
|
321
|
+
// If the passed in value is not the same as the search input value
|
|
322
|
+
// then lets update the search input value
|
|
323
|
+
if (this.render.content.search.input.value !== value) {
|
|
324
|
+
this.render.content.search.input.value = value
|
|
399
325
|
}
|
|
400
|
-
}
|
|
401
326
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
327
|
+
// If no search event run regular search
|
|
328
|
+
if (!this.events.search) {
|
|
329
|
+
// If value is empty then render all options
|
|
330
|
+
this.render.renderOptions(
|
|
331
|
+
value === '' ? this.store.getData() : this.store.search(value, this.events.searchFilter!),
|
|
332
|
+
)
|
|
333
|
+
return
|
|
409
334
|
}
|
|
410
335
|
|
|
411
|
-
//
|
|
412
|
-
this.
|
|
413
|
-
this.select.element.disabled = false
|
|
414
|
-
this.slim.search.input.disabled = false
|
|
415
|
-
this.select.triggerMutationObserver = true
|
|
416
|
-
}
|
|
336
|
+
// Search event exists so lets render the searching text
|
|
337
|
+
this.render.renderSearching()
|
|
417
338
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
this.config.isEnabled = false
|
|
421
|
-
if (this.config.isMultiple && this.slim.multiSelected) {
|
|
422
|
-
this.slim.multiSelected.container.classList.add(this.config.disabled)
|
|
423
|
-
} else if (this.slim.singleSelected) {
|
|
424
|
-
this.slim.singleSelected.container.classList.add(this.config.disabled)
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Enable original select but dont trigger observer
|
|
428
|
-
this.select.triggerMutationObserver = false
|
|
429
|
-
this.select.element.disabled = true
|
|
430
|
-
this.slim.search.input.disabled = true
|
|
431
|
-
this.select.triggerMutationObserver = true
|
|
432
|
-
}
|
|
339
|
+
// Based upon the search event deal with the response
|
|
340
|
+
const searchResp = this.events.search(value, this.store.getSelectedOptions())
|
|
433
341
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
if (this.config.isAjax) {
|
|
441
|
-
const master = this
|
|
442
|
-
this.config.isSearching = true
|
|
443
|
-
this.render()
|
|
444
|
-
|
|
445
|
-
// If ajax call it
|
|
446
|
-
if (this.ajax) {
|
|
447
|
-
this.ajax(value, (info: any) => {
|
|
448
|
-
// Only process if return callback is not false
|
|
449
|
-
master.config.isSearching = false
|
|
450
|
-
if (Array.isArray(info)) {
|
|
451
|
-
info.unshift({ text: '', placeholder: true })
|
|
452
|
-
master.setData(info)
|
|
453
|
-
master.data.search(value)
|
|
454
|
-
master.render()
|
|
455
|
-
} else if (typeof info === 'string') {
|
|
456
|
-
master.slim.options(info)
|
|
457
|
-
} else {
|
|
458
|
-
master.render()
|
|
459
|
-
}
|
|
342
|
+
// If the search event returns a promise
|
|
343
|
+
if (searchResp instanceof Promise) {
|
|
344
|
+
searchResp
|
|
345
|
+
.then((data: DataArrayPartial) => {
|
|
346
|
+
// Update the render with the new data
|
|
347
|
+
this.render.renderOptions(this.store.partialToFullData(data))
|
|
460
348
|
})
|
|
461
|
-
|
|
349
|
+
.catch((err: Error | string) => {
|
|
350
|
+
// Update the render with error
|
|
351
|
+
this.render.renderError(typeof err === 'string' ? err : err.message)
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
return
|
|
355
|
+
} else if (Array.isArray(searchResp)) {
|
|
356
|
+
// Update the render options
|
|
357
|
+
this.render.renderOptions(this.store.partialToFullData(searchResp))
|
|
462
358
|
} else {
|
|
463
|
-
|
|
464
|
-
this.render()
|
|
359
|
+
// Update the render with error
|
|
360
|
+
this.render.renderError('Search event must return a promise or an array of data')
|
|
465
361
|
}
|
|
466
362
|
}
|
|
467
363
|
|
|
468
|
-
public
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
this.slim.values()
|
|
475
|
-
} else {
|
|
476
|
-
this.slim.placeholder()
|
|
477
|
-
this.slim.deselect()
|
|
364
|
+
public destroy(): void {
|
|
365
|
+
// Remove all event listeners
|
|
366
|
+
document.removeEventListener('click', this.documentClick)
|
|
367
|
+
window.removeEventListener('resize', this.windowResize, false)
|
|
368
|
+
if (this.settings.openPosition === 'auto') {
|
|
369
|
+
window.removeEventListener('scroll', this.windowScroll, false)
|
|
478
370
|
}
|
|
479
|
-
this.slim.options()
|
|
480
|
-
}
|
|
481
371
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const slim = (id ? document.querySelector('.' + id + '.ss-main') : this.slim.container)
|
|
485
|
-
const select = (id ? document.querySelector(`[data-ssid=${id}]`) as HTMLSelectElement : this.select.element)
|
|
486
|
-
// If there is no slim dont do anything
|
|
487
|
-
if (!slim || !select) { return }
|
|
372
|
+
// Delete the store data
|
|
373
|
+
this.store.setData([])
|
|
488
374
|
|
|
375
|
+
// Remove the render
|
|
376
|
+
this.render.destroy()
|
|
489
377
|
|
|
490
|
-
|
|
378
|
+
// Show the original select element
|
|
379
|
+
this.select.destroy()
|
|
380
|
+
}
|
|
491
381
|
|
|
492
|
-
|
|
493
|
-
|
|
382
|
+
private windowResize: (e: Event) => void = debounce(() => {
|
|
383
|
+
if (!this.settings.isOpen) {
|
|
384
|
+
return
|
|
494
385
|
}
|
|
495
386
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
delete select.dataset.ssid
|
|
387
|
+
this.render.moveContent()
|
|
388
|
+
})
|
|
499
389
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
390
|
+
// Event listener for window scrolling
|
|
391
|
+
private windowScroll: (e: Event) => void = debounce(() => {
|
|
392
|
+
// If the content is not open, there is no need to move it
|
|
393
|
+
if (!this.settings.isOpen) {
|
|
394
|
+
return
|
|
395
|
+
}
|
|
503
396
|
|
|
504
|
-
//
|
|
505
|
-
if (
|
|
506
|
-
|
|
397
|
+
// If openContent is not auto set content
|
|
398
|
+
if (this.settings.openPosition === 'down') {
|
|
399
|
+
this.render.moveContentBelow()
|
|
400
|
+
return
|
|
401
|
+
} else if (this.settings.openPosition === 'up') {
|
|
402
|
+
this.render.moveContentAbove()
|
|
403
|
+
return
|
|
507
404
|
}
|
|
508
405
|
|
|
509
|
-
//
|
|
510
|
-
if (this.
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
406
|
+
// Determine where to put the content
|
|
407
|
+
if (this.settings.contentPosition === 'relative') {
|
|
408
|
+
this.render.moveContentBelow()
|
|
409
|
+
} else if (this.render.putContent(this.render.content.main, this.settings.isOpen) === 'up') {
|
|
410
|
+
this.render.moveContentAbove()
|
|
411
|
+
} else {
|
|
412
|
+
this.render.moveContentBelow()
|
|
514
413
|
}
|
|
515
|
-
}
|
|
414
|
+
})
|
|
516
415
|
|
|
416
|
+
// Event listener for document click
|
|
517
417
|
private documentClick: (e: Event) => void = (e: Event) => {
|
|
518
|
-
|
|
418
|
+
// If the content is not open, there is no need to close it
|
|
419
|
+
if (!this.settings.isOpen) {
|
|
420
|
+
return
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Check if the click was on the content by looking at the parents
|
|
424
|
+
if (e.target && !hasClassInTree(e.target as HTMLElement, this.settings.id)) {
|
|
519
425
|
this.close()
|
|
520
426
|
}
|
|
521
427
|
}
|
|
522
|
-
|
|
523
428
|
}
|