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
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { describe, expect, test } from '@jest/globals'
|
|
4
|
+
import Store, { Optgroup, Option } from './store'
|
|
5
|
+
|
|
6
|
+
describe('store module', () => {
|
|
7
|
+
test('constructor', () => {
|
|
8
|
+
let store = new Store('single', [])
|
|
9
|
+
expect(store).toBeInstanceOf(Store)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
test('set data', () => {
|
|
13
|
+
let store = new Store('single', [
|
|
14
|
+
{
|
|
15
|
+
text: 'test',
|
|
16
|
+
},
|
|
17
|
+
])
|
|
18
|
+
|
|
19
|
+
let data = store.getData()
|
|
20
|
+
|
|
21
|
+
// Make sure data has one item and that it has the correct text
|
|
22
|
+
expect(data.length).toBe(1)
|
|
23
|
+
expect((data[0] as Option).text).toBe('test')
|
|
24
|
+
|
|
25
|
+
// Make sure the data has all the default fields
|
|
26
|
+
expect((data[0] as Option).id).toBeDefined()
|
|
27
|
+
expect((data[0] as Option).value).toBeDefined()
|
|
28
|
+
expect((data[0] as Option).text).toBeDefined()
|
|
29
|
+
expect((data[0] as Option).html).toBeDefined()
|
|
30
|
+
expect((data[0] as Option).selected).toBeDefined()
|
|
31
|
+
expect((data[0] as Option).display).toBeDefined()
|
|
32
|
+
expect((data[0] as Option).disabled).toBeDefined()
|
|
33
|
+
expect((data[0] as Option).placeholder).toBeDefined()
|
|
34
|
+
expect((data[0] as Option).class).toBeDefined()
|
|
35
|
+
expect((data[0] as Option).style).toBeDefined()
|
|
36
|
+
expect((data[0] as Option).data).toBeDefined()
|
|
37
|
+
expect((data[0] as Option).mandatory).toBeDefined()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('set data with optgroup', () => {
|
|
41
|
+
let store = new Store('single', [
|
|
42
|
+
{
|
|
43
|
+
label: 'test',
|
|
44
|
+
options: [
|
|
45
|
+
{
|
|
46
|
+
text: 'test',
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
])
|
|
51
|
+
|
|
52
|
+
let data = store.getData()
|
|
53
|
+
|
|
54
|
+
// Make sure data has one item and that it has the correct text
|
|
55
|
+
expect(data.length).toBe(1)
|
|
56
|
+
expect((data[0] as Optgroup).label).toBe('test')
|
|
57
|
+
expect((data[0] as Optgroup).options?.length).toBe(1)
|
|
58
|
+
expect((data[0] as Optgroup).options?.[0].text).toBe('test')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('set data with optgroup and option', () => {
|
|
62
|
+
let store = new Store('single', [
|
|
63
|
+
{
|
|
64
|
+
label: 'test',
|
|
65
|
+
options: [
|
|
66
|
+
{
|
|
67
|
+
text: 'test',
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
text: 'test',
|
|
73
|
+
},
|
|
74
|
+
])
|
|
75
|
+
|
|
76
|
+
let data = store.getData()
|
|
77
|
+
|
|
78
|
+
// Make sure data has one item and that it has the correct text
|
|
79
|
+
expect(data.length).toBe(2)
|
|
80
|
+
expect((data[0] as Optgroup).label).toBe('test')
|
|
81
|
+
expect((data[0] as Optgroup).options?.length).toBe(1)
|
|
82
|
+
expect((data[0] as Optgroup).options?.[0].text).toBe('test')
|
|
83
|
+
expect((data[1] as Option).text).toBe('test')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('set data and set selected by ID', () => {
|
|
87
|
+
let store = new Store('single', [
|
|
88
|
+
{
|
|
89
|
+
id: '8675309',
|
|
90
|
+
text: 'test',
|
|
91
|
+
},
|
|
92
|
+
])
|
|
93
|
+
store.setSelectedBy('id', ['8675309'])
|
|
94
|
+
|
|
95
|
+
let data = store.getData()
|
|
96
|
+
|
|
97
|
+
// Make sure data has one item and that it has the correct text
|
|
98
|
+
expect(data.length).toBe(1)
|
|
99
|
+
expect((data[0] as Option).text).toBe('test')
|
|
100
|
+
expect((data[0] as Option).selected).toBe(true)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('set data and set selected by value', () => {
|
|
104
|
+
let store = new Store('single', [
|
|
105
|
+
{
|
|
106
|
+
text: 'test',
|
|
107
|
+
value: 'hello',
|
|
108
|
+
},
|
|
109
|
+
])
|
|
110
|
+
store.setSelectedBy('value', ['hello'])
|
|
111
|
+
|
|
112
|
+
let data = store.getData()
|
|
113
|
+
|
|
114
|
+
// Make sure data has one item and that it has the correct text
|
|
115
|
+
expect(data.length).toBe(1)
|
|
116
|
+
expect((data[0] as Option).text).toBe('test')
|
|
117
|
+
expect((data[0] as Option).selected).toBe(true)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
test('set data and set selected by value multiple for single element', () => {
|
|
121
|
+
let store = new Store('single', [
|
|
122
|
+
{
|
|
123
|
+
text: 'test1',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
text: 'test2',
|
|
127
|
+
},
|
|
128
|
+
])
|
|
129
|
+
store.setSelectedBy('value', ['test1', 'test2'])
|
|
130
|
+
|
|
131
|
+
let data = store.getData()
|
|
132
|
+
|
|
133
|
+
// Make sure data has one item and that it has the correct text
|
|
134
|
+
expect(data.length).toBe(2)
|
|
135
|
+
expect((data[0] as Option).text).toBe('test1')
|
|
136
|
+
expect((data[0] as Option).selected).toBe(true)
|
|
137
|
+
expect((data[1] as Option).text).toBe('test2')
|
|
138
|
+
expect((data[1] as Option).selected).toBe(false)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
test('set data and search', () => {
|
|
142
|
+
let store = new Store('single', [
|
|
143
|
+
{
|
|
144
|
+
text: 'test1',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
text: 'test2',
|
|
148
|
+
value: 'test2',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
text: 'test3',
|
|
152
|
+
},
|
|
153
|
+
])
|
|
154
|
+
|
|
155
|
+
let data = store.getData()
|
|
156
|
+
|
|
157
|
+
const searchFilter = (opt: Option, search: string): boolean => {
|
|
158
|
+
return opt.text.toLowerCase().indexOf(search.toLowerCase()) !== -1
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// With searchFilter search against current store data set
|
|
162
|
+
let search = store.search('test2', searchFilter)
|
|
163
|
+
expect(search.length).toBe(1)
|
|
164
|
+
expect((search[0] as Option).value).toBe('test2')
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
test('set data with non selected on single select', () => {
|
|
168
|
+
let store = new Store('single', [
|
|
169
|
+
{
|
|
170
|
+
text: 'test1',
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
text: 'test2',
|
|
174
|
+
value: 'test2',
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
text: 'test3',
|
|
178
|
+
},
|
|
179
|
+
])
|
|
180
|
+
|
|
181
|
+
let data = store.getData()
|
|
182
|
+
|
|
183
|
+
// Make sure data has one item and that it has the correct text
|
|
184
|
+
expect(data.length).toBe(3)
|
|
185
|
+
expect((data[0] as Option).text).toBe('test1')
|
|
186
|
+
expect((data[0] as Option).selected).toBe(true)
|
|
187
|
+
expect((data[1] as Option).text).toBe('test2')
|
|
188
|
+
expect((data[1] as Option).selected).toBe(false)
|
|
189
|
+
expect((data[2] as Option).text).toBe('test3')
|
|
190
|
+
expect((data[2] as Option).selected).toBe(false)
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
test('set data with multiple selected on single select', () => {
|
|
194
|
+
let store = new Store('single', [
|
|
195
|
+
{
|
|
196
|
+
text: 'test1',
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
text: 'test2',
|
|
200
|
+
value: 'test2',
|
|
201
|
+
selected: true,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
text: 'test3',
|
|
205
|
+
selected: true,
|
|
206
|
+
},
|
|
207
|
+
])
|
|
208
|
+
|
|
209
|
+
let data = store.getData()
|
|
210
|
+
|
|
211
|
+
// Make sure data has one item and that it has the correct text
|
|
212
|
+
expect(data.length).toBe(3)
|
|
213
|
+
expect((data[0] as Option).text).toBe('test1')
|
|
214
|
+
expect((data[0] as Option).selected).toBe(false)
|
|
215
|
+
expect((data[1] as Option).text).toBe('test2')
|
|
216
|
+
expect((data[1] as Option).selected).toBe(true)
|
|
217
|
+
expect((data[2] as Option).text).toBe('test3')
|
|
218
|
+
expect((data[2] as Option).selected).toBe(false)
|
|
219
|
+
})
|
|
220
|
+
})
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { generateID } from './helper'
|
|
2
|
+
|
|
3
|
+
export type DataArray = DataObject[]
|
|
4
|
+
export type DataObject = Optgroup | Option
|
|
5
|
+
|
|
6
|
+
export type DataArrayPartial = DataObjectPartial[]
|
|
7
|
+
export type DataObjectPartial = OptgroupOptional | OptionOptional
|
|
8
|
+
|
|
9
|
+
type selectType = 'single' | 'multiple'
|
|
10
|
+
|
|
11
|
+
export interface OptgroupOptional {
|
|
12
|
+
id?: string
|
|
13
|
+
label: string // Required
|
|
14
|
+
options?: OptionOptional[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class Optgroup {
|
|
18
|
+
public id: string
|
|
19
|
+
public label: string
|
|
20
|
+
public options: Option[]
|
|
21
|
+
|
|
22
|
+
constructor(optgroup: OptgroupOptional) {
|
|
23
|
+
this.id = !optgroup.id || optgroup.id === '' ? generateID() : optgroup.id
|
|
24
|
+
this.label = optgroup.label || ''
|
|
25
|
+
|
|
26
|
+
// If options exist, loop through options and create new option class
|
|
27
|
+
// and set the options to the optgroup options field
|
|
28
|
+
this.options = []
|
|
29
|
+
if (optgroup.options) {
|
|
30
|
+
for (const o of optgroup.options) {
|
|
31
|
+
this.options.push(new Option(o))
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface OptionOptional {
|
|
38
|
+
id?: string
|
|
39
|
+
value?: string
|
|
40
|
+
text: string // Required
|
|
41
|
+
html?: string
|
|
42
|
+
selected?: boolean
|
|
43
|
+
display?: boolean
|
|
44
|
+
disabled?: boolean
|
|
45
|
+
mandatory?: boolean
|
|
46
|
+
placeholder?: boolean
|
|
47
|
+
class?: string
|
|
48
|
+
style?: string
|
|
49
|
+
data?: { [key: string]: string }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class Option {
|
|
53
|
+
id: string
|
|
54
|
+
value: string
|
|
55
|
+
text: string
|
|
56
|
+
html: string
|
|
57
|
+
selected: boolean
|
|
58
|
+
display: boolean
|
|
59
|
+
disabled: boolean
|
|
60
|
+
placeholder: boolean
|
|
61
|
+
class: string
|
|
62
|
+
style: string
|
|
63
|
+
data: { [key: string]: string }
|
|
64
|
+
mandatory: boolean
|
|
65
|
+
|
|
66
|
+
constructor(option: OptionOptional) {
|
|
67
|
+
this.id = !option.id || option.id === '' ? generateID() : option.id
|
|
68
|
+
this.value = option.value === undefined ? option.text : option.value
|
|
69
|
+
this.text = option.text || ''
|
|
70
|
+
this.html = option.html || ''
|
|
71
|
+
this.selected = option.selected !== undefined ? option.selected : false
|
|
72
|
+
this.display = option.display !== undefined ? option.display : true
|
|
73
|
+
this.disabled = option.disabled !== undefined ? option.disabled : false
|
|
74
|
+
this.mandatory = option.mandatory !== undefined ? option.mandatory : false
|
|
75
|
+
this.placeholder = option.placeholder !== undefined ? option.placeholder : false
|
|
76
|
+
this.class = option.class || ''
|
|
77
|
+
this.style = option.style || ''
|
|
78
|
+
this.data = option.data || {}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export default class Store {
|
|
83
|
+
private selectType: selectType = 'single'
|
|
84
|
+
|
|
85
|
+
// Main data set, never null
|
|
86
|
+
private data: DataArray = []
|
|
87
|
+
|
|
88
|
+
constructor(type: selectType, data: DataArrayPartial) {
|
|
89
|
+
this.selectType = type
|
|
90
|
+
this.setData(data)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Validate DataArrayPartial
|
|
94
|
+
public validateDataArray(data: DataArray | DataArrayPartial): Error | null {
|
|
95
|
+
if (!Array.isArray(data)) {
|
|
96
|
+
return new Error('Data must be an array')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Loop through each data object
|
|
100
|
+
for (let dataObj of data) {
|
|
101
|
+
// Optgroup
|
|
102
|
+
if (dataObj instanceof Optgroup || 'label' in dataObj) {
|
|
103
|
+
if (!('label' in dataObj)) {
|
|
104
|
+
return new Error('Optgroup must have a label')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if ('options' in dataObj && dataObj.options) {
|
|
108
|
+
for (let option of dataObj.options) {
|
|
109
|
+
return this.validateOption(option)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} else if (dataObj instanceof Option || 'text' in dataObj) {
|
|
113
|
+
return this.validateOption(dataObj)
|
|
114
|
+
} else {
|
|
115
|
+
return new Error('Data object must be a valid optgroup or option')
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return null
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Validate Option
|
|
123
|
+
public validateOption(option: Option | OptionOptional): Error | null {
|
|
124
|
+
if (!('text' in option)) {
|
|
125
|
+
return new Error('Option must have a text')
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public partialToFullData(data: DataArrayPartial): DataArray {
|
|
132
|
+
let dataFinal: DataArray = []
|
|
133
|
+
data.forEach((dataObj: DataObject | DataObjectPartial) => {
|
|
134
|
+
// Optgroup
|
|
135
|
+
if (dataObj instanceof Optgroup || 'label' in dataObj) {
|
|
136
|
+
let optOptions: Option[] = []
|
|
137
|
+
if ('options' in dataObj && dataObj.options) {
|
|
138
|
+
dataObj.options.forEach((option: Option | OptionOptional) => {
|
|
139
|
+
optOptions.push(new Option(option))
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (optOptions.length > 0) {
|
|
144
|
+
dataFinal.push(new Optgroup(dataObj))
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Option
|
|
149
|
+
if (dataObj instanceof Option || 'text' in dataObj) {
|
|
150
|
+
dataFinal.push(new Option(dataObj))
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
return dataFinal
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public setData(data: DataArray | DataArrayPartial) {
|
|
158
|
+
this.data = this.partialToFullData(data)
|
|
159
|
+
|
|
160
|
+
// Run this.data through setSelected by value
|
|
161
|
+
// to set the selected property and clean any wrong selected
|
|
162
|
+
if (this.selectType === 'single') {
|
|
163
|
+
this.setSelectedBy('value', this.getSelected())
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Get data will return all the data
|
|
168
|
+
public getData(): DataArray {
|
|
169
|
+
return this.filter(null, true)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Get data options will return the data as a
|
|
173
|
+
// flat array of just options
|
|
174
|
+
public getDataOptions(): Option[] {
|
|
175
|
+
return this.filter(null, false) as Option[]
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
public addOption(option: OptionOptional) {
|
|
179
|
+
this.setData(this.getData().concat(new Option(option)))
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Pass in an array of id that will loop through
|
|
183
|
+
// each option and set the selected property to true
|
|
184
|
+
// but also clean selected by determining selectType
|
|
185
|
+
public setSelectedBy(selectedType: 'id' | 'value', selectedValues: string[]) {
|
|
186
|
+
let firstOption: Option | null = null
|
|
187
|
+
let hasSelected = false
|
|
188
|
+
|
|
189
|
+
for (let dataObj of this.data) {
|
|
190
|
+
// Optgroup
|
|
191
|
+
if (dataObj instanceof Optgroup) {
|
|
192
|
+
for (let option of dataObj.options) {
|
|
193
|
+
if (!firstOption) {
|
|
194
|
+
firstOption = option
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
option.selected = hasSelected ? false : selectedValues.includes(option[selectedType])
|
|
198
|
+
|
|
199
|
+
// If the option is selected, set hasSelected to true
|
|
200
|
+
// for single based selects
|
|
201
|
+
if (option.selected && this.selectType === 'single') {
|
|
202
|
+
hasSelected = true
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Option
|
|
208
|
+
if (dataObj instanceof Option) {
|
|
209
|
+
if (!firstOption) {
|
|
210
|
+
firstOption = dataObj
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
dataObj.selected = hasSelected ? false : selectedValues.includes(dataObj[selectedType])
|
|
214
|
+
|
|
215
|
+
// If the option is selected, set hasSelected to true
|
|
216
|
+
// for single based selects
|
|
217
|
+
if (dataObj.selected && this.selectType === 'single') {
|
|
218
|
+
hasSelected = true
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// If no options are selected, select the first option
|
|
224
|
+
if (this.selectType === 'single' && firstOption && !hasSelected) {
|
|
225
|
+
firstOption.selected = true
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
public getSelected(): string[] {
|
|
230
|
+
let selectedOptions = this.getSelectedOptions()
|
|
231
|
+
let selectedValues: string[] = []
|
|
232
|
+
|
|
233
|
+
// Loop through each option and get the value
|
|
234
|
+
selectedOptions.forEach((option: Option) => {
|
|
235
|
+
selectedValues.push(option.value)
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
return selectedValues
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
public getSelectedOptions(): Option[] {
|
|
242
|
+
return this.filter((opt: Option) => {
|
|
243
|
+
return opt.selected
|
|
244
|
+
}, false) as Option[]
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
public getSelectedIDs(): string[] {
|
|
248
|
+
let selectedOptions = this.getSelectedOptions()
|
|
249
|
+
|
|
250
|
+
let selectedIDs: string[] = []
|
|
251
|
+
selectedOptions.forEach((op: Option) => {
|
|
252
|
+
selectedIDs.push(op.id)
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
return selectedIDs
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
public getOptionByID(id: string): Option | null {
|
|
259
|
+
let options = this.filter((opt: Option) => {
|
|
260
|
+
return opt.id === id
|
|
261
|
+
}, false) as Option[]
|
|
262
|
+
|
|
263
|
+
return options.length ? options[0] : null
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Take in search string and return filtered list of values
|
|
267
|
+
public search(search: string, searchFilter: (opt: Option, search: string) => boolean): DataArray {
|
|
268
|
+
search = search.trim()
|
|
269
|
+
|
|
270
|
+
// If search is empty, return all data
|
|
271
|
+
if (search === '') {
|
|
272
|
+
return this.getData()
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Run filter with search function
|
|
276
|
+
return this.filter((opt: Option): boolean => {
|
|
277
|
+
return searchFilter(opt, search)
|
|
278
|
+
}, true)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Filter takes in a function that will be used to filter the data
|
|
282
|
+
// This will also keep optgroups of sub options meet the filter requirements
|
|
283
|
+
public filter(filter: { (opt: Option): boolean } | null, includeOptgroup: boolean): DataArray {
|
|
284
|
+
const dataSearch: DataArray = []
|
|
285
|
+
this.data.forEach((dataObj: DataObject) => {
|
|
286
|
+
// Optgroup
|
|
287
|
+
if (dataObj instanceof Optgroup) {
|
|
288
|
+
// If you dont want to include optgroups
|
|
289
|
+
if (!includeOptgroup) {
|
|
290
|
+
// Loop through each option and check if it meets the filter requirements
|
|
291
|
+
dataObj.options.forEach((option: Option) => {
|
|
292
|
+
if (filter && filter(option)) {
|
|
293
|
+
dataSearch.push(option)
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
} else {
|
|
297
|
+
let optOptions: Option[] = []
|
|
298
|
+
dataObj.options.forEach((option: Option) => {
|
|
299
|
+
if (!filter || filter(option)) {
|
|
300
|
+
optOptions.push(new Option(option))
|
|
301
|
+
}
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
if (optOptions.length > 0) {
|
|
305
|
+
dataSearch.push(new Optgroup({ id: dataObj.id, label: dataObj.label, options: optOptions }))
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Option
|
|
311
|
+
if (dataObj instanceof Option) {
|
|
312
|
+
if (!filter || filter(dataObj)) {
|
|
313
|
+
dataSearch.push(new Option(dataObj))
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
return dataSearch
|
|
319
|
+
}
|
|
320
|
+
}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"
|
|
4
|
-
"
|
|
3
|
+
"target": "es2016",
|
|
4
|
+
"module": "esnext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"strict": true /* Enable all strict type-checking options. */,
|
|
7
|
+
"declaration": true /* Generates corresponding '.d.ts' file. */,
|
|
5
8
|
"outDir": "../../dist", // Output to dist folder
|
|
6
|
-
"removeComments": true /* Do not emit comments to output.
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
+
"removeComments": true /* Do not emit comments to output. */,
|
|
10
|
+
"importHelpers": true /* Import emit helpers from 'tslib'. */
|
|
11
|
+
},
|
|
12
|
+
"include": ["**/*.ts"],
|
|
13
|
+
"exclude": ["**/*.test.ts"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { defineComponent, ref, watch, onMounted, onBeforeUnmount } from 'vue'
|
|
3
|
+
import SlimSelect from '../slim-select'
|
|
4
|
+
|
|
5
|
+
export default defineComponent({
|
|
6
|
+
name: 'SlimSelect',
|
|
7
|
+
props: {
|
|
8
|
+
options: {
|
|
9
|
+
type: Array,
|
|
10
|
+
required: true,
|
|
11
|
+
},
|
|
12
|
+
value: {
|
|
13
|
+
type: [String, Number, Array],
|
|
14
|
+
required: true,
|
|
15
|
+
},
|
|
16
|
+
config: {
|
|
17
|
+
type: Object,
|
|
18
|
+
required: false,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
emits: ['update:value'],
|
|
22
|
+
setup(props, { emit }) {
|
|
23
|
+
const selectRef = ref(null)
|
|
24
|
+
let select: SlimSelect
|
|
25
|
+
|
|
26
|
+
const updateValue = (value: string | number | string[] | number[]) => {
|
|
27
|
+
emit('update:value', value)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const updateSelect = () => {
|
|
31
|
+
select.set(props.value)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const initSelect = () => {
|
|
35
|
+
select = new SlimSelect(selectRef.value, {
|
|
36
|
+
...props.config,
|
|
37
|
+
onChange: updateValue,
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
onMounted(() => {
|
|
42
|
+
initSelect()
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
onBeforeUnmount(() => {
|
|
46
|
+
select.destroy()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
watch(
|
|
50
|
+
() => props.value,
|
|
51
|
+
() => {
|
|
52
|
+
updateSelect()
|
|
53
|
+
},
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
selectRef,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
</script>
|
package/tsconfig.json
CHANGED
|
@@ -1,37 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
+
"declaration": true,
|
|
3
4
|
"target": "esnext",
|
|
5
|
+
"useDefineForClassFields": true,
|
|
4
6
|
"module": "esnext",
|
|
5
|
-
"strict": true,
|
|
6
|
-
"importHelpers": true,
|
|
7
7
|
"moduleResolution": "node",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
8
|
+
"strict": true,
|
|
9
|
+
"jsx": "preserve",
|
|
10
|
+
"allowJs": false,
|
|
10
11
|
"sourceMap": true,
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
]
|
|
15
|
-
"paths": {
|
|
16
|
-
"@/*": [
|
|
17
|
-
"src/*"
|
|
18
|
-
]
|
|
19
|
-
},
|
|
20
|
-
"lib": [
|
|
21
|
-
"esnext",
|
|
22
|
-
"dom",
|
|
23
|
-
"dom.iterable",
|
|
24
|
-
"scripthost"
|
|
25
|
-
]
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"importHelpers": true,
|
|
15
|
+
"lib": ["esnext", "dom"]
|
|
26
16
|
},
|
|
27
|
-
"include": [
|
|
28
|
-
"src/**/*.ts",
|
|
29
|
-
"src/**/*.tsx",
|
|
30
|
-
"src/**/*.vue",
|
|
31
|
-
"tests/**/*.ts",
|
|
32
|
-
"tests/**/*.tsx"
|
|
33
|
-
],
|
|
34
|
-
"exclude": [
|
|
35
|
-
"node_modules"
|
|
36
|
-
]
|
|
17
|
+
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
|
|
37
18
|
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import vue from '@vitejs/plugin-vue'
|
|
2
|
+
import { defineConfig } from 'vite'
|
|
3
|
+
|
|
4
|
+
// https://vitejs.dev/config/
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
build: {
|
|
7
|
+
outDir: 'docs',
|
|
8
|
+
emptyOutDir: true,
|
|
9
|
+
rollupOptions: {
|
|
10
|
+
output: {
|
|
11
|
+
entryFileNames: `assets/[name].js`,
|
|
12
|
+
chunkFileNames: `assets/[name].js`,
|
|
13
|
+
assetFileNames: `assets/[name].[ext]`,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
plugins: [vue()],
|
|
18
|
+
})
|
package/.browserslistrc
DELETED