slim-select 1.27.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/issue-report.md +3 -3
- package/.nvmrc +1 -0
- package/.prettierrc +6 -0
- package/.vscode/extensions.json +3 -0
- package/.vscode/settings.json +15 -5
- package/README.md +17 -19
- 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 +196 -73
- 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 +131 -117
- package/src/docs/pages/install.vue +51 -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 +40 -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/src/slim-select/webpack.config.js +0 -73
- package/tslint.json +0 -26
- package/vue.config.js +0 -23
package/src/slim-select/index.ts
CHANGED
|
@@ -1,531 +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
|
-
this.data.addToSelected(value, type)
|
|
173
|
-
} else {
|
|
174
|
-
this.data.setSelected(value, type)
|
|
175
|
-
}
|
|
176
|
-
this.select.setValue()
|
|
177
|
-
this.data.onDataChange() // Trigger on change callback
|
|
178
|
-
this.render()
|
|
187
|
+
public getData(): DataArray {
|
|
188
|
+
return this.store.getData()
|
|
189
|
+
}
|
|
179
190
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
|
183
199
|
}
|
|
184
200
|
|
|
185
|
-
|
|
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)
|
|
186
211
|
}
|
|
187
212
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
this.set(value, type, close, render)
|
|
213
|
+
public getSelected(): string[] {
|
|
214
|
+
return this.store.getSelected()
|
|
191
215
|
}
|
|
192
216
|
|
|
193
|
-
public
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
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()
|
|
197
221
|
|
|
198
|
-
|
|
199
|
-
|
|
222
|
+
// Update the select element
|
|
223
|
+
this.select.updateOptions(data)
|
|
200
224
|
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
newData[i].value = newData[i].text
|
|
206
|
-
}
|
|
207
|
-
}
|
|
225
|
+
// Update the render
|
|
226
|
+
this.render.renderValues()
|
|
227
|
+
this.render.renderOptions(data)
|
|
228
|
+
}
|
|
208
229
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
for (const r of reverseSelected) {
|
|
214
|
-
newData.unshift(r)
|
|
215
|
-
}
|
|
216
|
-
} else {
|
|
217
|
-
newData.unshift(selected)
|
|
218
|
-
|
|
219
|
-
// Look for duplicate selected if so remove it
|
|
220
|
-
for (let i = 0; i < newData.length; i++) {
|
|
221
|
-
if (!newData[i].placeholder && newData[i].value === (selected as Option).value && newData[i].text === (selected as Option).text) {
|
|
222
|
-
newData.splice(i, 1)
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Add placeholder if it doesnt already have one
|
|
227
|
-
let hasPlaceholder = false
|
|
228
|
-
for (let i = 0; i < newData.length; i++) {
|
|
229
|
-
if (newData[i].placeholder) {
|
|
230
|
-
hasPlaceholder = true
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
if (!hasPlaceholder) {
|
|
234
|
-
newData.unshift({ text: '', placeholder: true })
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
230
|
+
public addOption(option: OptionOptional): void {
|
|
231
|
+
// Add option to store
|
|
232
|
+
this.store.addOption(option)
|
|
233
|
+
const data = this.store.getData()
|
|
238
234
|
|
|
239
|
-
|
|
240
|
-
this.
|
|
241
|
-
this.data.setSelectedFromSelect()
|
|
242
|
-
}
|
|
235
|
+
// Update the select element
|
|
236
|
+
this.select.updateOptions(data)
|
|
243
237
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const isValid = validateData([data])
|
|
248
|
-
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
|
|
249
|
-
|
|
250
|
-
this.data.add(this.data.newOption(data))
|
|
251
|
-
this.select.create(this.data.data)
|
|
252
|
-
this.data.parseSelectData()
|
|
253
|
-
this.data.setSelectedFromSelect()
|
|
254
|
-
this.render()
|
|
238
|
+
// Update the render
|
|
239
|
+
this.render.renderValues()
|
|
240
|
+
this.render.renderOptions(data)
|
|
255
241
|
}
|
|
256
242
|
|
|
257
|
-
// Open content section
|
|
258
243
|
public open(): void {
|
|
259
244
|
// Dont open if disabled
|
|
260
|
-
if (!this.config.isEnabled) { return }
|
|
261
|
-
|
|
262
245
|
// Dont do anything if the content is already open
|
|
263
|
-
if (this.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if (this.config.hideSelectedOption && this.config.isMultiple && (this.data.getSelected() as Option[]).length === this.data.data.length) { return }
|
|
246
|
+
if (!this.settings.isEnabled || this.settings.isOpen) {
|
|
247
|
+
return
|
|
248
|
+
}
|
|
267
249
|
|
|
268
250
|
// Run beforeOpen callback
|
|
269
|
-
if (this.beforeOpen) {
|
|
270
|
-
|
|
271
|
-
if (this.config.isMultiple && this.slim.multiSelected) {
|
|
272
|
-
this.slim.multiSelected.plus.classList.add('ss-cross')
|
|
273
|
-
} else if (this.slim.singleSelected) {
|
|
274
|
-
this.slim.singleSelected.arrowIcon.arrow.classList.remove('arrow-down')
|
|
275
|
-
this.slim.singleSelected.arrowIcon.arrow.classList.add('arrow-up')
|
|
251
|
+
if (this.events.beforeOpen) {
|
|
252
|
+
this.events.beforeOpen()
|
|
276
253
|
}
|
|
277
|
-
(this.slim as any)[(this.config.isMultiple ? 'multiSelected' : 'singleSelected')].container.classList.add((this.data.contentPosition === 'above' ? this.config.openAbove : this.config.openBelow))
|
|
278
|
-
|
|
279
|
-
if (this.config.addToBody) {
|
|
280
|
-
// move the content in to the right location
|
|
281
|
-
const containerRect = this.slim.container.getBoundingClientRect()
|
|
282
|
-
this.slim.content.style.top = (containerRect.top + containerRect.height + window.scrollY) + 'px'
|
|
283
|
-
this.slim.content.style.left = (containerRect.left + window.scrollX) + 'px'
|
|
284
|
-
this.slim.content.style.width = containerRect.width + 'px'
|
|
285
|
-
}
|
|
286
|
-
this.slim.content.classList.add(this.config.open)
|
|
287
254
|
|
|
288
|
-
//
|
|
289
|
-
|
|
290
|
-
this.moveContentAbove()
|
|
291
|
-
} else if (this.config.showContent.toLowerCase() === 'down') {
|
|
292
|
-
this.moveContentBelow()
|
|
293
|
-
} else {
|
|
294
|
-
// Auto identify where to put it
|
|
295
|
-
if (putContent(this.slim.content, this.data.contentPosition, this.data.contentOpen) === 'above') {
|
|
296
|
-
this.moveContentAbove()
|
|
297
|
-
} else {
|
|
298
|
-
this.moveContentBelow()
|
|
299
|
-
}
|
|
300
|
-
}
|
|
255
|
+
// Tell render to open
|
|
256
|
+
this.render.open()
|
|
301
257
|
|
|
302
|
-
//
|
|
303
|
-
if (
|
|
304
|
-
|
|
305
|
-
if (selected) {
|
|
306
|
-
const selectedId = selected.id
|
|
307
|
-
const selectedOption = this.slim.list.querySelector('[data-id="' + selectedId + '"]') as HTMLElement
|
|
308
|
-
if (selectedOption) {
|
|
309
|
-
ensureElementInView(this.slim.list, selectedOption)
|
|
310
|
-
}
|
|
311
|
-
}
|
|
258
|
+
// Focus on input field only if search is enabled
|
|
259
|
+
if (this.settings.showSearch) {
|
|
260
|
+
this.render.searchFocus(false)
|
|
312
261
|
}
|
|
313
262
|
|
|
314
263
|
// setTimeout is for animation completion
|
|
315
264
|
setTimeout(() => {
|
|
316
|
-
this.data.contentOpen = true
|
|
317
|
-
|
|
318
|
-
// Focus on input field
|
|
319
|
-
if (this.config.searchFocus) {
|
|
320
|
-
this.slim.search.input.focus()
|
|
321
|
-
}
|
|
322
|
-
|
|
323
265
|
// Run afterOpen callback
|
|
324
|
-
if (this.afterOpen) {
|
|
325
|
-
this.afterOpen()
|
|
266
|
+
if (this.events.afterOpen) {
|
|
267
|
+
this.events.afterOpen()
|
|
326
268
|
}
|
|
327
|
-
|
|
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)
|
|
328
280
|
}
|
|
329
281
|
|
|
330
|
-
// Close content section
|
|
331
282
|
public close(): void {
|
|
332
283
|
// Dont do anything if the content is already closed
|
|
333
|
-
if
|
|
284
|
+
// Dont do anything if alwaysOpen is true
|
|
285
|
+
if (!this.settings.isOpen || this.settings.alwaysOpen) {
|
|
286
|
+
return
|
|
287
|
+
}
|
|
334
288
|
|
|
335
289
|
// Run beforeClose calback
|
|
336
|
-
if (this.beforeClose) {
|
|
337
|
-
|
|
338
|
-
// this.slim.search.input.blur() // Removed due to safari quirk
|
|
339
|
-
if (this.config.isMultiple && this.slim.multiSelected) {
|
|
340
|
-
this.slim.multiSelected.container.classList.remove(this.config.openAbove)
|
|
341
|
-
this.slim.multiSelected.container.classList.remove(this.config.openBelow)
|
|
342
|
-
this.slim.multiSelected.plus.classList.remove('ss-cross')
|
|
343
|
-
} else if (this.slim.singleSelected) {
|
|
344
|
-
this.slim.singleSelected.container.classList.remove(this.config.openAbove)
|
|
345
|
-
this.slim.singleSelected.container.classList.remove(this.config.openBelow)
|
|
346
|
-
this.slim.singleSelected.arrowIcon.arrow.classList.add('arrow-down')
|
|
347
|
-
this.slim.singleSelected.arrowIcon.arrow.classList.remove('arrow-up')
|
|
290
|
+
if (this.events.beforeClose) {
|
|
291
|
+
this.events.beforeClose()
|
|
348
292
|
}
|
|
349
|
-
this.slim.content.classList.remove(this.config.open)
|
|
350
|
-
this.data.contentOpen = false
|
|
351
293
|
|
|
294
|
+
// Tell render to close
|
|
295
|
+
this.render.close()
|
|
296
|
+
|
|
297
|
+
// Clear search
|
|
352
298
|
this.search('') // Clear search
|
|
353
299
|
|
|
300
|
+
// If we arent tabbing focus back on the main element
|
|
301
|
+
this.render.mainFocus(false)
|
|
302
|
+
|
|
354
303
|
// Reset the content below
|
|
355
304
|
setTimeout(() => {
|
|
356
|
-
|
|
357
|
-
this.
|
|
358
|
-
|
|
359
|
-
if (this.config.isMultiple && this.slim.multiSelected) {
|
|
360
|
-
this.slim.multiSelected.container.classList.remove(this.config.openAbove)
|
|
361
|
-
this.slim.multiSelected.container.classList.remove(this.config.openBelow)
|
|
362
|
-
} else if (this.slim.singleSelected) {
|
|
363
|
-
this.slim.singleSelected.container.classList.remove(this.config.openAbove)
|
|
364
|
-
this.slim.singleSelected.container.classList.remove(this.config.openBelow)
|
|
305
|
+
// Run afterClose callback
|
|
306
|
+
if (this.events.afterClose) {
|
|
307
|
+
this.events.afterClose()
|
|
365
308
|
}
|
|
366
309
|
|
|
367
|
-
//
|
|
368
|
-
this.
|
|
310
|
+
// Update settings
|
|
311
|
+
this.settings.isOpen = false
|
|
312
|
+
}, this.settings.timeoutDelay)
|
|
369
313
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}, this.config.timeoutDelay)
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
public moveContentAbove(): void {
|
|
376
|
-
let selectHeight: number = 0
|
|
377
|
-
if (this.config.isMultiple && this.slim.multiSelected) {
|
|
378
|
-
selectHeight = this.slim.multiSelected.container.offsetHeight
|
|
379
|
-
} else if (this.slim.singleSelected) {
|
|
380
|
-
selectHeight = this.slim.singleSelected.container.offsetHeight
|
|
381
|
-
}
|
|
382
|
-
const contentHeight = this.slim.content.offsetHeight
|
|
383
|
-
const height = selectHeight + contentHeight - 1
|
|
384
|
-
this.slim.content.style.margin = '-' + height + 'px 0 0 0'
|
|
385
|
-
this.slim.content.style.height = (height - selectHeight + 1) + 'px'
|
|
386
|
-
this.slim.content.style.transformOrigin = 'center bottom'
|
|
387
|
-
this.data.contentPosition = 'above'
|
|
388
|
-
|
|
389
|
-
if (this.config.isMultiple && this.slim.multiSelected) {
|
|
390
|
-
this.slim.multiSelected.container.classList.remove(this.config.openBelow)
|
|
391
|
-
this.slim.multiSelected.container.classList.add(this.config.openAbove)
|
|
392
|
-
} else if (this.slim.singleSelected) {
|
|
393
|
-
this.slim.singleSelected.container.classList.remove(this.config.openBelow)
|
|
394
|
-
this.slim.singleSelected.container.classList.add(this.config.openAbove)
|
|
314
|
+
if (this.settings.intervalMove) {
|
|
315
|
+
clearInterval(this.settings.intervalMove)
|
|
395
316
|
}
|
|
396
317
|
}
|
|
397
318
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
this.
|
|
404
|
-
} else if (this.slim.singleSelected) {
|
|
405
|
-
this.slim.singleSelected.container.classList.remove(this.config.openAbove)
|
|
406
|
-
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
|
|
407
325
|
}
|
|
408
|
-
}
|
|
409
326
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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
|
|
417
334
|
}
|
|
418
335
|
|
|
419
|
-
//
|
|
420
|
-
this.
|
|
421
|
-
this.select.element.disabled = false
|
|
422
|
-
this.slim.search.input.disabled = false
|
|
423
|
-
this.select.triggerMutationObserver = true
|
|
424
|
-
}
|
|
336
|
+
// Search event exists so lets render the searching text
|
|
337
|
+
this.render.renderSearching()
|
|
425
338
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
this.config.isEnabled = false
|
|
429
|
-
if (this.config.isMultiple && this.slim.multiSelected) {
|
|
430
|
-
this.slim.multiSelected.container.classList.add(this.config.disabled)
|
|
431
|
-
} else if (this.slim.singleSelected) {
|
|
432
|
-
this.slim.singleSelected.container.classList.add(this.config.disabled)
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Enable original select but dont trigger observer
|
|
436
|
-
this.select.triggerMutationObserver = false
|
|
437
|
-
this.select.element.disabled = true
|
|
438
|
-
this.slim.search.input.disabled = true
|
|
439
|
-
this.select.triggerMutationObserver = true
|
|
440
|
-
}
|
|
339
|
+
// Based upon the search event deal with the response
|
|
340
|
+
const searchResp = this.events.search(value, this.store.getSelectedOptions())
|
|
441
341
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
if (this.config.isAjax) {
|
|
449
|
-
const master = this
|
|
450
|
-
this.config.isSearching = true
|
|
451
|
-
this.render()
|
|
452
|
-
|
|
453
|
-
// If ajax call it
|
|
454
|
-
if (this.ajax) {
|
|
455
|
-
this.ajax(value, (info: any) => {
|
|
456
|
-
// Only process if return callback is not false
|
|
457
|
-
master.config.isSearching = false
|
|
458
|
-
if (Array.isArray(info)) {
|
|
459
|
-
info.unshift({ text: '', placeholder: true })
|
|
460
|
-
master.setData(info)
|
|
461
|
-
master.data.search(value)
|
|
462
|
-
master.render()
|
|
463
|
-
} else if (typeof info === 'string') {
|
|
464
|
-
master.slim.options(info)
|
|
465
|
-
} else {
|
|
466
|
-
master.render()
|
|
467
|
-
}
|
|
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))
|
|
468
348
|
})
|
|
469
|
-
|
|
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))
|
|
470
358
|
} else {
|
|
471
|
-
|
|
472
|
-
this.render()
|
|
359
|
+
// Update the render with error
|
|
360
|
+
this.render.renderError('Search event must return a promise or an array of data')
|
|
473
361
|
}
|
|
474
362
|
}
|
|
475
363
|
|
|
476
|
-
public
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
this.slim.values()
|
|
483
|
-
} else {
|
|
484
|
-
this.slim.placeholder()
|
|
485
|
-
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)
|
|
486
370
|
}
|
|
487
|
-
this.slim.options()
|
|
488
|
-
}
|
|
489
371
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
const slim = (id ? document.querySelector('.' + id + '.ss-main') : this.slim.container)
|
|
493
|
-
const select = (id ? document.querySelector(`[data-ssid=${id}]`) as HTMLSelectElement : this.select.element)
|
|
494
|
-
// If there is no slim dont do anything
|
|
495
|
-
if (!slim || !select) { return }
|
|
372
|
+
// Delete the store data
|
|
373
|
+
this.store.setData([])
|
|
496
374
|
|
|
375
|
+
// Remove the render
|
|
376
|
+
this.render.destroy()
|
|
497
377
|
|
|
498
|
-
|
|
378
|
+
// Show the original select element
|
|
379
|
+
this.select.destroy()
|
|
380
|
+
}
|
|
499
381
|
|
|
500
|
-
|
|
501
|
-
|
|
382
|
+
private windowResize: (e: Event) => void = debounce(() => {
|
|
383
|
+
if (!this.settings.isOpen) {
|
|
384
|
+
return
|
|
502
385
|
}
|
|
503
386
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
delete select.dataset.ssid
|
|
387
|
+
this.render.moveContent()
|
|
388
|
+
})
|
|
507
389
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
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
|
+
}
|
|
511
396
|
|
|
512
|
-
//
|
|
513
|
-
if (
|
|
514
|
-
|
|
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
|
|
515
404
|
}
|
|
516
405
|
|
|
517
|
-
//
|
|
518
|
-
if (this.
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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()
|
|
522
413
|
}
|
|
523
|
-
}
|
|
414
|
+
})
|
|
524
415
|
|
|
416
|
+
// Event listener for document click
|
|
525
417
|
private documentClick: (e: Event) => void = (e: Event) => {
|
|
526
|
-
|
|
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)) {
|
|
527
425
|
this.close()
|
|
528
426
|
}
|
|
529
427
|
}
|
|
530
|
-
|
|
531
428
|
}
|