slim-select 2.5.1 → 2.7.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.
Files changed (46) hide show
  1. package/dist/render.d.ts +1 -1
  2. package/dist/settings.d.ts +1 -0
  3. package/dist/slimselect.cjs.js +52 -10
  4. package/dist/slimselect.css +1 -1
  5. package/dist/slimselect.css.map +1 -1
  6. package/dist/slimselect.es.js +52 -10
  7. package/dist/slimselect.global.js +52 -10
  8. package/dist/slimselect.js +52 -10
  9. package/dist/slimselect.min.js +1 -1
  10. package/dist/slimselect.umd.js +52 -10
  11. package/dist/slimselect.umd.min.js +1 -1
  12. package/dist/store.d.ts +4 -1
  13. package/index.html +4 -0
  14. package/package.json +51 -26
  15. package/src/react/.eslintignore +2 -0
  16. package/src/react/.eslintrc.cjs +24 -0
  17. package/src/react/index.html +14 -0
  18. package/src/react/jestconfig.json +11 -0
  19. package/src/react/package.json +32 -0
  20. package/src/react/src/frameworkpage.tsx +133 -0
  21. package/src/react/src/index.ts +3 -0
  22. package/src/react/src/slimselect.tsx +148 -0
  23. package/src/react/tests/slimselect.test.tsx +52 -0
  24. package/src/react/tsconfig.json +37 -0
  25. package/src/react/tsconfig.node.json +10 -0
  26. package/src/react/vite.config.ts +55 -0
  27. package/src/slim-select/index.test.ts +2 -2
  28. package/src/slim-select/render.ts +49 -10
  29. package/src/slim-select/select.ts +4 -2
  30. package/src/slim-select/settings.ts +2 -0
  31. package/src/slim-select/slimselect.scss +3 -2
  32. package/src/slim-select/store.ts +23 -3
  33. package/src/vue/dist/slim-select/helpers.d.ts +5 -5
  34. package/src/vue/dist/slim-select/index.d.ts +46 -46
  35. package/src/vue/dist/slim-select/render.d.ts +120 -120
  36. package/src/vue/dist/slim-select/select.d.ts +28 -28
  37. package/src/vue/dist/slim-select/settings.d.ts +33 -32
  38. package/src/vue/dist/slim-select/store.d.ts +80 -77
  39. package/src/vue/dist/slimselectvue.es.js +1940 -1903
  40. package/src/vue/dist/slimselectvue.global.js +1940 -1903
  41. package/src/vue/dist/slimselectvue.ssr.js +1940 -1903
  42. package/src/vue/dist/vue/hello.d.ts +2 -2
  43. package/src/vue/dist/vue/slimselect.vue.d.ts +324 -317
  44. package/src/vue/package.json +1 -1
  45. package/src/vue/slimselect.vue +2 -7
  46. package/tsconfig.json +1 -1
@@ -0,0 +1,148 @@
1
+ import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react'
2
+ import { useEffect } from 'react'
3
+
4
+ import SlimSelect, { Events, Config } from '../../slim-select'
5
+ import { SettingsPartial } from '../../slim-select/settings'
6
+ import { DataArrayPartial, Option } from '../../slim-select/store'
7
+ import '../../slim-select/slimselect.scss'
8
+
9
+ export type SlimSelectProps = {
10
+ style?: React.CSSProperties
11
+ id?: string | undefined
12
+ modelValue?: string | string[] | undefined
13
+ multiple?: boolean
14
+ data?: DataArrayPartial
15
+ settings?: SettingsPartial
16
+ events?: Events
17
+ children?: React.ReactNode
18
+ }
19
+ export interface SlimSelectRef {
20
+ set: (newValue: string | string[]) => void
21
+ // Potentially call other functions on SlimSelect that have not been exposed via Ref yet
22
+ getSlimSelectInstance: () => SlimSelect
23
+ }
24
+
25
+ const SlimSelectComponent = (props: SlimSelectProps, ref: React.Ref<any>): JSX.Element => {
26
+ const { id = undefined, modelValue, style = {}, multiple = false, data, settings, events, children } = props
27
+
28
+ const slimHTMLElement = useRef(null)
29
+ const slimSelect = useRef<SlimSelect>()
30
+ const value = useRef<any>()
31
+
32
+ useEffect(() => {
33
+ const config = {
34
+ select: slimHTMLElement.current as unknown as Element,
35
+ events: {},
36
+ } as Config
37
+
38
+ // If data is passed in, use it
39
+ if (data) {
40
+ config.data = data
41
+ }
42
+ // If settings are passed in, use it
43
+ if (settings) {
44
+ config.settings = settings
45
+ }
46
+ // If events are passed in, use it
47
+ if (events) {
48
+ config.events = events
49
+ }
50
+ // Satisfy tsc
51
+ if (config.events == undefined) {
52
+ config.events = {}
53
+ }
54
+
55
+ // Wrap config.events.afterChange to run value update
56
+ const originalPropAfterChange = config.events.afterChange
57
+
58
+ config.events.afterChange = function (newSelectedOption: Option[]): void {
59
+ if (Array.isArray(newSelectedOption) && newSelectedOption.length > 0) {
60
+ const stringValue = multiple ? newSelectedOption.map((option) => option.value) : newSelectedOption[0].value
61
+ if (value.current !== stringValue) {
62
+ value.current = stringValue
63
+ }
64
+ // If they had an original afterChange, run it
65
+ if (config.events && originalPropAfterChange && typeof originalPropAfterChange === 'function') {
66
+ originalPropAfterChange(newSelectedOption)
67
+ }
68
+ }
69
+ }
70
+
71
+ // Initialize SlimSelect
72
+ slimSelect.current = new SlimSelect(config)
73
+
74
+ // Get SlimSelect selected values
75
+ const selected = multiple
76
+ ? (slimSelect.current as SlimSelect).getSelected()
77
+ : (slimSelect.current as SlimSelect).getSelected()[0]
78
+
79
+ // Check if modelValue is the same as the value of the select
80
+ if (selected !== modelValue && modelValue) {
81
+ // If not, set the value of the select to the modelValue
82
+ ;(slimSelect.current as SlimSelect).setSelected(modelValue)
83
+ }
84
+
85
+ // Destroy SlimSelect on unmount
86
+ return () => {
87
+ if (slimSelect.current) {
88
+ ;(slimSelect.current as SlimSelect)?.destroy()
89
+ }
90
+ }
91
+ }, [modelValue, multiple, data, settings, events, children])
92
+
93
+ const getCleanValue = useCallback(
94
+ (val: string | string[] | undefined): string | string[] => {
95
+ const multi = multiple
96
+
97
+ // If its multiple and the modelValue is a string, return an array with the string
98
+ if (typeof val === 'string') {
99
+ return multi ? [val] : val
100
+ }
101
+
102
+ // If its not multiple and the modelValue is an array, return the first item
103
+ if (Array.isArray(val)) {
104
+ return multi ? val : val[0]
105
+ }
106
+
107
+ return multi ? [] : ''
108
+ },
109
+ [multiple],
110
+ )
111
+
112
+ useEffect(() => {
113
+ if (modelValue) {
114
+ ;(slimSelect.current as SlimSelect)?.setSelected(getCleanValue(modelValue))
115
+ }
116
+ }, [modelValue, getCleanValue])
117
+
118
+ useEffect(() => {
119
+ if (data) {
120
+ ;(slimSelect.current as SlimSelect)?.setData(data)
121
+ }
122
+ }, [data])
123
+
124
+ useImperativeHandle(
125
+ ref,
126
+ () => {
127
+ return {
128
+ set(newValue: string | string[]): void {
129
+ ;(slimSelect.current as SlimSelect)?.setSelected(newValue)
130
+ },
131
+ getSlimSelectInstance(): SlimSelect {
132
+ return slimSelect.current as SlimSelect
133
+ },
134
+ } as SlimSelectRef
135
+ },
136
+ [],
137
+ )
138
+
139
+ return (
140
+ <>
141
+ <select style={style} id={id} data-testid={id} multiple={multiple} ref={slimHTMLElement}>
142
+ {children}
143
+ </select>
144
+ </>
145
+ )
146
+ }
147
+
148
+ export default forwardRef(SlimSelectComponent)
@@ -0,0 +1,52 @@
1
+ import React from 'react'
2
+
3
+ import { render } from '@testing-library/react'
4
+ import userEvent from '@testing-library/user-event'
5
+ import '@testing-library/jest-dom/extend-expect'
6
+
7
+ import 'jest-canvas-mock'
8
+
9
+ import SlimSelectComponent from '../src/slimselect'
10
+
11
+ describe('Slim Select', () => {
12
+ console.log(React.version)
13
+ it('renders successfully', () => {
14
+ const { queryByTestId, queryAllByText, getByText } = render(
15
+ <SlimSelectComponent id="slimselect" ref={null} multiple={true}>
16
+ <option value={'value1'}>Value 1</option>
17
+ <option value={'value2'}>Value 2</option>
18
+ <option value={'value3'}>Value 3</option>
19
+ </SlimSelectComponent>,
20
+ )
21
+ expect(queryByTestId('slimselect')).not.toEqual(null)
22
+ expect(queryByTestId('slimselect')).not.toBeVisible()
23
+ expect(getByText('Select Value')).not.toEqual(null)
24
+ expect(queryAllByText('Value 1')[1].getAttribute('aria-selected')).toEqual('false')
25
+ expect(queryAllByText('Value 2')[1].getAttribute('aria-selected')).toEqual('false')
26
+ expect(queryAllByText('Value 3')[1].getAttribute('aria-selected')).toEqual('false')
27
+ })
28
+ it('can be selected with onchange callback', async () => {
29
+ const { queryAllByText } = render(
30
+ <SlimSelectComponent id="slimselect" ref={null} multiple={true}>
31
+ <option value={'value1'}>Value 1</option>
32
+ <option value={'value2'}>Value 2</option>
33
+ <option value={'value3'}>Value 3</option>
34
+ </SlimSelectComponent>,
35
+ )
36
+
37
+ // First original html select element gets hidden, after comes slimselect div - that is why array index = 1
38
+ const firstSlimOption = queryAllByText('Value 1')[1]
39
+ const thirdSlimOpption = queryAllByText('Value 3')[1]
40
+
41
+ expect(firstSlimOption).not.toBeUndefined()
42
+ expect(thirdSlimOpption).not.toBeUndefined()
43
+
44
+ await userEvent.click(firstSlimOption as HTMLElement)
45
+ await userEvent.click(thirdSlimOpption as HTMLElement)
46
+
47
+ expect(queryAllByText('Select Value')).toHaveLength(0)
48
+ expect(queryAllByText('Value 1')[1].getAttribute('aria-selected')).toEqual('true')
49
+ expect(queryAllByText('Value 2')[1].getAttribute('aria-selected')).toEqual('false')
50
+ expect(queryAllByText('Value 3')[1].getAttribute('aria-selected')).toEqual('true')
51
+ })
52
+ })
@@ -0,0 +1,37 @@
1
+ {
2
+ "exclude": ["dist", "node_modules"],
3
+ "compilerOptions": {
4
+ "target": "ESNext",
5
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+ "importHelpers": true,
9
+ "declaration": true,
10
+ "sourceMap": true,
11
+ //"rootDir": "./src",
12
+ "outDir": "./dist",
13
+ "esModuleInterop": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+
16
+ /* Bundler mode */
17
+ "moduleResolution": "node",
18
+ "allowImportingTsExtensions": true,
19
+ "resolveJsonModule": true,
20
+ "isolatedModules": true,
21
+ "noEmit": true,
22
+
23
+ "jsx": "react-jsx",
24
+
25
+ /* Linting */
26
+ "strict": true,
27
+ "noFallthroughCasesInSwitch": true,
28
+ "noImplicitReturns": true,
29
+ "noUnusedLocals": false,
30
+ "noUnusedParameters": false,
31
+ "paths": {
32
+ "react": ["./node_modules/@types/react"]
33
+ }
34
+ },
35
+ "include": ["./src"],
36
+ "references": [{ "path": "./tsconfig.node.json" }]
37
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "skipLibCheck": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "allowSyntheticDefaultImports": true
8
+ },
9
+ "include": ["vite.config.ts"]
10
+ }
@@ -0,0 +1,55 @@
1
+ import react from '@vitejs/plugin-react'
2
+ import { defineConfig } from 'vite'
3
+ import { babel } from '@rollup/plugin-babel'
4
+ import commonjs from '@rollup/plugin-commonjs'
5
+ import peerDepsExternal from 'rollup-plugin-peer-deps-external'
6
+ import dts from 'vite-plugin-dts'
7
+
8
+ const formatNameMap = {
9
+ iife: 'global',
10
+ cjs: 'ssr',
11
+ es: 'es',
12
+ }
13
+
14
+ // https://vitejs.dev/config/
15
+ export default defineConfig({
16
+ plugins: [
17
+ react(),
18
+ dts({
19
+ insertTypesEntry: true,
20
+ skipDiagnostics: true,
21
+ }),
22
+ ],
23
+ build: {
24
+ target: 'modules',
25
+ lib: {
26
+ // Could also be a dictionary or array of multiple entry points
27
+ entry: 'src/index.ts',
28
+ formats: ['es', 'cjs', 'iife'],
29
+ name: 'SlimSelectReact',
30
+ // the proper extensions will be added
31
+ fileName: (format) => `slimselectreact.${formatNameMap[format]}.js`,
32
+ },
33
+ rollupOptions: {
34
+ // make sure to externalize deps that shouldn't be bundled
35
+ // into your library
36
+ // external: ['react', 'react-dom'],
37
+ plugins: [
38
+ peerDepsExternal(),
39
+ babel({
40
+ babelHelpers: 'bundled',
41
+ presets: ['@babel/preset-env', '@babel/preset-react'],
42
+ exclude: '../../node_modules/',
43
+ }),
44
+ commonjs(),
45
+ ],
46
+ output: {
47
+ // for externalized deps
48
+ globals: {
49
+ react: 'React',
50
+ 'react-dom': 'ReactDOM',
51
+ },
52
+ },
53
+ },
54
+ },
55
+ })
@@ -5,8 +5,8 @@
5
5
  'use strict'
6
6
  import { describe, expect, test } from '@jest/globals'
7
7
  import SlimSelect from './'
8
- import { OptionOptional } from 'src/vue/dist/slim-select/store'
9
- import { Config } from 'src/vue/dist/slim-select'
8
+ import { OptionOptional } from './store'
9
+ import { Config } from './index'
10
10
 
11
11
  describe('SlimSelect Module', () => {
12
12
  test('constructor', () => {
@@ -6,7 +6,7 @@ export interface Callbacks {
6
6
  open: () => void
7
7
  close: () => void
8
8
  addable?: (value: string) => Promise<OptionOptional | string> | OptionOptional | string | false | undefined | null
9
- setSelected: (value: string[], runAfterChange: boolean) => void
9
+ setSelected: (value: string | string[], runAfterChange: boolean) => void
10
10
  addOption: (option: Option) => void
11
11
  search: (search: string) => void
12
12
  beforeChange?: (newVal: Option[], oldVal: Option[]) => boolean | void
@@ -230,7 +230,10 @@ export default class Render {
230
230
 
231
231
  // Add id to data-id
232
232
  main.dataset.id = this.settings.id
233
- main.id = this.settings.id
233
+ // main.id = this.settings.id+'-main' // Remove for now as it is not needed
234
+
235
+ // Add label
236
+ main.setAttribute('aria-label', this.settings.ariaLabel)
234
237
 
235
238
  // Set tabable to allow tabbing to the element
236
239
  main.tabIndex = 0
@@ -238,7 +241,7 @@ export default class Render {
238
241
  // Deal with keyboard events on the main div
239
242
  // This is to allow for normal selecting
240
243
  // when you may not have a search bar
241
- main.onkeydown = (e: KeyboardEvent) => {
244
+ main.onkeydown = (e: KeyboardEvent): boolean => {
242
245
  // Convert above if else statemets to switch
243
246
  switch (e.key) {
244
247
  case 'ArrowUp':
@@ -261,10 +264,12 @@ export default class Render {
261
264
  this.callbacks.close()
262
265
  return false
263
266
  }
267
+
268
+ return false
264
269
  }
265
270
 
266
271
  // Add onclick for main div
267
- main.onclick = (e: Event) => {
272
+ main.onclick = (e: Event): void => {
268
273
  // Dont do anything if disabled
269
274
  if (this.settings.disabled) {
270
275
  return
@@ -314,7 +319,11 @@ export default class Render {
314
319
  this.callbacks.setSelected([], false)
315
320
  this.updateDeselectAll()
316
321
  } else {
317
- this.callbacks.setSelected([''], false)
322
+ // Get first option and set it as selected
323
+ const firstOption = this.store.getFirstOption()
324
+ const value = firstOption ? firstOption.value : ''
325
+
326
+ this.callbacks.setSelected(value, false)
318
327
  }
319
328
 
320
329
  // Check if we need to close the dropdown
@@ -324,7 +333,7 @@ export default class Render {
324
333
 
325
334
  // Run afterChange callback
326
335
  if (this.callbacks.afterChange) {
327
- this.callbacks.afterChange(after)
336
+ this.callbacks.afterChange(this.store.getSelectedOptions())
328
337
  }
329
338
  }
330
339
  }
@@ -624,7 +633,7 @@ export default class Render {
624
633
 
625
634
  // Add id to data-id
626
635
  main.dataset.id = this.settings.id
627
- main.id = this.settings.id
636
+ // main.id = this.settings.id + '-content' // Remove for now as it is not needed
628
637
 
629
638
  // Add search
630
639
  const search = this.searchDiv()
@@ -696,7 +705,7 @@ export default class Render {
696
705
  }, 100)
697
706
 
698
707
  // Deal with keyboard events on search input field
699
- input.onkeydown = (e: KeyboardEvent) => {
708
+ input.onkeydown = (e: KeyboardEvent): boolean => {
700
709
  // Convert above if else statemets to switch
701
710
  switch (e.key) {
702
711
  case 'ArrowUp':
@@ -727,6 +736,8 @@ export default class Render {
727
736
  }
728
737
  return true
729
738
  }
739
+
740
+ return true // Allow normal typing
730
741
  }
731
742
 
732
743
  main.appendChild(input)
@@ -867,15 +878,34 @@ export default class Render {
867
878
 
868
879
  // If length is 1, highlight it
869
880
  if (options.length === 1) {
870
- // Check if option doesnt already has highlighted class
881
+ // Check if option doesnt already have highlighted class
871
882
  if (!options[0].classList.contains(this.classes.highlighted)) {
872
883
  options[0].classList.add(this.classes.highlighted)
873
884
  return
874
885
  }
875
886
  }
876
887
 
888
+ // Loop through options and see if there are no highlighted ones
889
+ let highlighted = false
890
+ for (const o of options) {
891
+ if (o.classList.contains(this.classes.highlighted)) {
892
+ highlighted = true
893
+ }
894
+ }
895
+
896
+ // If no highlighted, see if any are selected and if so highlight selected first one
897
+ if (!highlighted) {
898
+ for (const o of options) {
899
+ if (o.classList.contains(this.classes.selected)) {
900
+ o.classList.add(this.classes.highlighted)
901
+ break
902
+ }
903
+ }
904
+ }
905
+
877
906
  // Loop through options and find the highlighted one
878
907
  for (let i = 0; i < options.length; i++) {
908
+ // Found highlighted option
879
909
  if (options[i].classList.contains(this.classes.highlighted)) {
880
910
  const prevOption = options[i]
881
911
  // Remove highlighted class from current one
@@ -1005,7 +1035,7 @@ export default class Render {
1005
1035
 
1006
1036
  // Add select all text span
1007
1037
  const selectAllText = document.createElement('span')
1008
- selectAllText.textContent = 'Select All'
1038
+ selectAllText.textContent = d.selectAllText
1009
1039
  selectAll.appendChild(selectAllText)
1010
1040
 
1011
1041
  // Create new svg for checkbox
@@ -1051,7 +1081,16 @@ export default class Render {
1051
1081
  // Put together new list with all options in this optgroup
1052
1082
  const newSelected = currentSelected.concat(d.options.map((o) => o.value))
1053
1083
 
1084
+ // Loop through options and if they don't exist in the store
1085
+ // run addOption callback
1086
+ for (const o of d.options) {
1087
+ if (!this.store.getOptionByID(o.id)) {
1088
+ this.callbacks.addOption(o)
1089
+ }
1090
+ }
1091
+
1054
1092
  this.callbacks.setSelected(newSelected, true)
1093
+ return
1055
1094
  }
1056
1095
  })
1057
1096
 
@@ -17,8 +17,9 @@ export default class Select {
17
17
  constructor(select: HTMLSelectElement) {
18
18
  this.select = select
19
19
 
20
+ this.valueChange = this.valueChange.bind(this)
20
21
  // Add change event listener
21
- this.select.addEventListener('change', this.valueChange.bind(this), {
22
+ this.select.addEventListener('change', this.valueChange, {
22
23
  // allow bubbling of event
23
24
  passive: true,
24
25
  })
@@ -160,6 +161,7 @@ export default class Select {
160
161
  id: optgroup.id,
161
162
  label: optgroup.label,
162
163
  selectAll: optgroup.dataset ? optgroup.dataset.selectall === 'true' : false,
164
+ selectAllText: optgroup.dataset ? optgroup.dataset.selectalltext : 'Select all',
163
165
  closable: optgroup.dataset ? optgroup.dataset.closable : 'off',
164
166
  options: [],
165
167
  } as OptgroupOptional
@@ -360,7 +362,7 @@ export default class Select {
360
362
  this.changeListen(false)
361
363
 
362
364
  // Remove event change listener
363
- this.select.removeEventListener('change', this.valueChange.bind(this))
365
+ this.select.removeEventListener('change', this.valueChange)
364
366
 
365
367
  // Disconnect observer and null
366
368
  if (this.observer) {
@@ -17,6 +17,7 @@ export default class Settings {
17
17
  public disabled: boolean
18
18
  public alwaysOpen: boolean
19
19
  public showSearch: boolean
20
+ public ariaLabel: string
20
21
  public searchPlaceholder: string
21
22
  public searchText: string
22
23
  public searchingText: string
@@ -47,6 +48,7 @@ export default class Settings {
47
48
  this.disabled = settings.disabled !== undefined ? settings.disabled : false
48
49
  this.alwaysOpen = settings.alwaysOpen !== undefined ? settings.alwaysOpen : false
49
50
  this.showSearch = settings.showSearch !== undefined ? settings.showSearch : true
51
+ this.ariaLabel = settings.ariaLabel || 'Combobox'
50
52
  this.searchPlaceholder = settings.searchPlaceholder || 'Search'
51
53
  this.searchText = settings.searchText || 'No Results'
52
54
  this.searchingText = settings.searchingText || 'Searching...'
@@ -9,6 +9,7 @@
9
9
  --ss-highlight-color: #fffb8c;
10
10
  --ss-success-color: #00b755;
11
11
  --ss-error-color: #dc3545;
12
+ --ss-focus-color: #5897fb;
12
13
 
13
14
  // Heights
14
15
  --ss-main-height: 30px;
@@ -71,7 +72,7 @@
71
72
  overflow: hidden;
72
73
 
73
74
  &:focus {
74
- box-shadow: 0 0 5px var(--ss-primary-color);
75
+ box-shadow: 0 0 5px var(--ss-focus-color);
75
76
  }
76
77
 
77
78
  &.ss-disabled {
@@ -303,7 +304,7 @@
303
304
  }
304
305
 
305
306
  &:focus {
306
- box-shadow: 0 0 5px var(--ss-primary-color);
307
+ box-shadow: 0 0 5px var(--ss-focus-color);
307
308
  }
308
309
  }
309
310
 
@@ -12,6 +12,7 @@ export interface OptgroupOptional {
12
12
  id?: string
13
13
  label: string // Required
14
14
  selectAll?: boolean
15
+ selectAllText?: string
15
16
  closable?: 'off' | 'open' | 'close'
16
17
  options?: OptionOptional[]
17
18
  }
@@ -20,6 +21,7 @@ export class Optgroup {
20
21
  public id: string
21
22
  public label: string
22
23
  public selectAll: boolean
24
+ public selectAllText: string
23
25
  public closable: 'off' | 'open' | 'close'
24
26
  public options: Option[]
25
27
 
@@ -27,6 +29,7 @@ export class Optgroup {
27
29
  this.id = !optgroup.id || optgroup.id === '' ? generateID() : optgroup.id
28
30
  this.label = optgroup.label || ''
29
31
  this.selectAll = optgroup.selectAll === undefined ? false : optgroup.selectAll
32
+ this.selectAllText = optgroup.selectAllText || 'Select All'
30
33
  this.closable = optgroup.closable || 'off'
31
34
 
32
35
  // If options exist, loop through options and create new option class
@@ -281,6 +284,26 @@ export default class Store {
281
284
  return options.length ? options[0] : null
282
285
  }
283
286
 
287
+ public getSelectType(): string {
288
+ return this.selectType
289
+ }
290
+
291
+ public getFirstOption(): Option | null {
292
+ let option: Option | null = null
293
+ for (let dataObj of this.data) {
294
+ if (dataObj instanceof Optgroup) {
295
+ option = dataObj.options[0]
296
+ } else if (dataObj instanceof Option) {
297
+ option = dataObj
298
+ }
299
+
300
+ if (option) {
301
+ break
302
+ }
303
+ }
304
+ return option
305
+ }
306
+
284
307
  // Take in search string and return filtered list of values
285
308
  public search(search: string, searchFilter: (opt: Option, search: string) => boolean): DataArray {
286
309
  search = search.trim()
@@ -339,7 +362,4 @@ export default class Store {
339
362
  return dataSearch
340
363
  }
341
364
 
342
- public getSelectType(): string {
343
- return this.selectType
344
- }
345
365
  }
@@ -1,5 +1,5 @@
1
- export declare function generateID(): string;
2
- export declare function hasClassInTree(element: HTMLElement, className: string): any;
3
- export declare function debounce<T extends (...args: any[]) => void>(func: T, wait?: number, immediate?: boolean): () => void;
4
- export declare function isEqual(a: any, b: any): boolean;
5
- export declare function kebabCase(str: string): string;
1
+ export declare function generateID(): string;
2
+ export declare function hasClassInTree(element: HTMLElement, className: string): any;
3
+ export declare function debounce<T extends (...args: any[]) => void>(func: T, wait?: number, immediate?: boolean): () => void;
4
+ export declare function isEqual(a: any, b: any): boolean;
5
+ export declare function kebabCase(str: string): string;