slim-select 2.8.1 → 2.9.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 (69) hide show
  1. package/.prettierignore +1 -0
  2. package/.prettierrc +4 -3
  3. package/README.md +2 -0
  4. package/dist/classes.d.ts +43 -0
  5. package/dist/helpers.d.ts +1 -1
  6. package/dist/index.d.ts +5 -2
  7. package/dist/render.d.ts +4 -44
  8. package/dist/select.d.ts +4 -2
  9. package/dist/settings.d.ts +2 -1
  10. package/dist/slimselect.cjs.js +248 -120
  11. package/dist/slimselect.css +1 -1
  12. package/dist/slimselect.css.map +1 -1
  13. package/dist/slimselect.es.js +248 -120
  14. package/dist/slimselect.global.js +248 -120
  15. package/dist/slimselect.js +248 -120
  16. package/dist/slimselect.min.js +1 -1
  17. package/dist/slimselect.umd.js +248 -120
  18. package/dist/slimselect.umd.min.js +1 -1
  19. package/dist/store.d.ts +4 -2
  20. package/index.html +1 -4
  21. package/jest.config.js +6 -0
  22. package/package.json +30 -41
  23. package/src/slim-select/classes.test.ts +72 -0
  24. package/src/slim-select/classes.ts +109 -0
  25. package/src/slim-select/helpers.test.ts +160 -0
  26. package/src/slim-select/helpers.ts +3 -12
  27. package/src/slim-select/index.test.ts +109 -16
  28. package/src/slim-select/index.ts +41 -12
  29. package/src/slim-select/render.test.ts +1169 -18
  30. package/src/slim-select/render.ts +75 -96
  31. package/src/slim-select/rollup.config.mjs +28 -28
  32. package/src/slim-select/select.test.ts +306 -156
  33. package/src/slim-select/select.ts +69 -20
  34. package/src/slim-select/settings.test.ts +257 -0
  35. package/src/slim-select/settings.ts +4 -0
  36. package/src/slim-select/slimselect.scss +7 -3
  37. package/src/slim-select/store.test.ts +986 -235
  38. package/src/slim-select/store.ts +70 -28
  39. package/src/{vue/slimselect.vue → slim-select/vue.vue} +19 -17
  40. package/tsconfig.json +2 -3
  41. package/src/react/.eslintignore +0 -2
  42. package/src/react/.eslintrc.cjs +0 -24
  43. package/src/react/index.html +0 -14
  44. package/src/react/jestconfig.json +0 -11
  45. package/src/react/package.json +0 -32
  46. package/src/react/src/frameworkpage.tsx +0 -133
  47. package/src/react/src/index.ts +0 -3
  48. package/src/react/src/slimselect.tsx +0 -148
  49. package/src/react/tests/slimselect.test.tsx +0 -52
  50. package/src/react/tsconfig.json +0 -37
  51. package/src/react/tsconfig.node.json +0 -10
  52. package/src/react/vite.config.ts +0 -55
  53. package/src/vue/README.md +0 -48
  54. package/src/vue/dist/slim-select/helpers.d.ts +0 -5
  55. package/src/vue/dist/slim-select/index.d.ts +0 -46
  56. package/src/vue/dist/slim-select/render.d.ts +0 -120
  57. package/src/vue/dist/slim-select/select.d.ts +0 -28
  58. package/src/vue/dist/slim-select/settings.d.ts +0 -34
  59. package/src/vue/dist/slim-select/store.d.ts +0 -80
  60. package/src/vue/dist/slimselectvue.es.js +0 -1978
  61. package/src/vue/dist/slimselectvue.global.js +0 -1981
  62. package/src/vue/dist/slimselectvue.ssr.js +0 -1980
  63. package/src/vue/dist/vue/hello.d.ts +0 -2
  64. package/src/vue/dist/vue/slimselect.vue.d.ts +0 -326
  65. package/src/vue/hello.ts +0 -5
  66. package/src/vue/package.json +0 -28
  67. package/src/vue/rollup.config.mjs +0 -44
  68. package/src/vue/tsconfig.json +0 -14
  69. /package/{vite.config.ts → vite.config.mts} +0 -0
@@ -1,11 +1,14 @@
1
1
  import { debounce } from './helpers'
2
2
  import Settings from './settings'
3
3
  import Store, { DataArray, Optgroup, Option, OptionOptional } from './store'
4
+ import CssClasses from './classes'
4
5
 
5
6
  export interface Callbacks {
6
7
  open: () => void
7
8
  close: () => void
8
- addable?: (value: string) => Promise<OptionOptional | string> | OptionOptional | string | false | undefined | null
9
+ addable?: (
10
+ value: string
11
+ ) => Promise<OptionOptional | string> | OptionOptional | string | false | undefined | null | Error
9
12
  setSelected: (value: string | string[], runAfterChange: boolean) => void
10
13
  addOption: (option: Option) => void
11
14
  search: (search: string) => void
@@ -53,73 +56,12 @@ export default class Render {
53
56
  public content: Content
54
57
 
55
58
  // Classes
56
- public classes = {
57
- // Main
58
- main: 'ss-main',
59
-
60
- // Placeholder
61
- placeholder: 'ss-placeholder',
62
-
63
- // Values
64
- values: 'ss-values',
65
- single: 'ss-single',
66
- max: 'ss-max',
67
- value: 'ss-value',
68
- valueText: 'ss-value-text',
69
- valueDelete: 'ss-value-delete',
70
- valueOut: 'ss-value-out',
71
-
72
- // Deselect
73
- deselect: 'ss-deselect',
74
- deselectPath: 'M10,10 L90,90 M10,90 L90,10', // Not a class but whatever
75
-
76
- // Arrow
77
- arrow: 'ss-arrow',
78
- arrowClose: 'M10,30 L50,70 L90,30', // Not a class but whatever
79
- arrowOpen: 'M10,70 L50,30 L90,70', // Not a class but whatever
80
-
81
- // Content
82
- content: 'ss-content',
83
- openAbove: 'ss-open-above',
84
- openBelow: 'ss-open-below',
85
-
86
- // Search
87
- search: 'ss-search',
88
- searchHighlighter: 'ss-search-highlight',
89
- searching: 'ss-searching',
90
- addable: 'ss-addable',
91
- addablePath: 'M50,10 L50,90 M10,50 L90,50', // Not a class but whatever
92
-
93
- // List optgroups/options
94
- list: 'ss-list',
95
-
96
- // Optgroup
97
- optgroup: 'ss-optgroup',
98
- optgroupLabel: 'ss-optgroup-label',
99
- optgroupLabelText: 'ss-optgroup-label-text',
100
- optgroupActions: 'ss-optgroup-actions',
101
- optgroupSelectAll: 'ss-selectall', // optgroup select all
102
- optgroupSelectAllBox: 'M60,10 L10,10 L10,90 L90,90 L90,50', // Not a class but whatever
103
- optgroupSelectAllCheck: 'M30,45 L50,70 L90,10', // Not a class but whatever
104
- optgroupClosable: 'ss-closable',
105
-
106
- // Option
107
- option: 'ss-option',
108
- optionDelete: 'M10,10 L90,90 M10,90 L90,10', // Not a class but whatever
109
- highlighted: 'ss-highlighted',
110
-
111
- // Misc
112
- open: 'ss-open',
113
- close: 'ss-close',
114
- selected: 'ss-selected',
115
- error: 'ss-error',
116
- disabled: 'ss-disabled',
117
- hide: 'ss-hide',
118
- }
59
+ public classes: CssClasses
119
60
 
120
- constructor(settings: Required<Settings>, store: Store, callbacks: Callbacks) {
61
+ constructor(settings: Required<Settings>, classes: Required<CssClasses>, store: Store, callbacks: Callbacks) {
121
62
  this.store = store
122
63
  this.settings = settings
64
+ this.classes = classes
123
65
  this.callbacks = callbacks
124
66
 
125
67
  this.main = this.mainDiv()
@@ -265,7 +207,7 @@ export default class Render {
265
207
  return false
266
208
  }
267
209
 
268
- return false
210
+ return true
269
211
  }
270
212
 
271
213
  // Add onclick for main div
@@ -321,9 +263,9 @@ export default class Render {
321
263
  } else {
322
264
  // Get first option and set it as selected
323
265
  const firstOption = this.store.getFirstOption()
324
- const value = firstOption ? firstOption.value : ''
266
+ const id = firstOption ? firstOption.id : ''
325
267
 
326
- this.callbacks.setSelected(value, false)
268
+ this.callbacks.setSelected(id, false)
327
269
  }
328
270
 
329
271
  // Check if we need to close the dropdown
@@ -365,12 +307,12 @@ export default class Render {
365
307
  deselect: {
366
308
  main: deselect,
367
309
  svg: deselectSvg,
368
- path: deselectPath,
310
+ path: deselectPath
369
311
  },
370
312
  arrow: {
371
313
  main: arrow,
372
- path: arrowPath,
373
- },
314
+ path: arrowPath
315
+ }
374
316
  }
375
317
  }
376
318
 
@@ -491,6 +433,11 @@ export default class Render {
491
433
  }
492
434
  }
493
435
 
436
+ // Lets check for data selected order
437
+ if (this.settings.keepOrder) {
438
+ selectedOptions = this.store.selectedOrderOptions(selectedOptions)
439
+ }
440
+
494
441
  // Loop through currentNodes and only include ones that are not in selectedIDs
495
442
  let removeNodes: HTMLDivElement[] = []
496
443
  for (let i = 0; i < currentNodes.length; i++) {
@@ -555,7 +502,7 @@ export default class Render {
555
502
 
556
503
  const text = document.createElement('div')
557
504
  text.classList.add(this.classes.valueText)
558
- text.innerText = option.text // For multiple values always use text
505
+ text.textContent = option.text // For multiple values always use text
559
506
  value.appendChild(text)
560
507
 
561
508
  // Only add deletion if the option is not mandatory
@@ -592,20 +539,20 @@ export default class Render {
592
539
  }
593
540
 
594
541
  if (shouldDelete) {
595
- // Loop through after and append values to a variable called selected
596
- let selectedValues: string[] = []
542
+ // Loop through after and append ids to a variable called selected
543
+ let selectedIds: string[] = []
597
544
  for (const o of after) {
598
545
  if (o instanceof Optgroup) {
599
546
  for (const c of o.options) {
600
- selectedValues.push(c.value)
547
+ selectedIds.push(c.id)
601
548
  }
602
549
  }
603
550
 
604
551
  if (o instanceof Option) {
605
- selectedValues.push(o.value)
552
+ selectedIds.push(o.id)
606
553
  }
607
554
  }
608
- this.callbacks.setSelected(selectedValues, false)
555
+ this.callbacks.setSelected(selectedIds, false)
609
556
 
610
557
  // Check if we need to close the dropdown
611
558
  if (this.settings.closeOnSelect) {
@@ -653,7 +600,7 @@ export default class Render {
653
600
  return {
654
601
  main: main,
655
602
  search: search,
656
- list: list,
603
+ list: list
657
604
  }
658
605
  }
659
606
 
@@ -690,7 +637,7 @@ export default class Render {
690
637
  // Setup search return object
691
638
  const searchReturn: Search = {
692
639
  main,
693
- input,
640
+ input
694
641
  }
695
642
 
696
643
  // We still want the search to be tabable but not shown
@@ -724,14 +671,19 @@ export default class Render {
724
671
  // which will also focus on main div
725
672
  // and then continuing normal tabbing
726
673
  this.callbacks.close()
727
-
728
674
  return true // Continue doing normal tabbing
729
675
  case 'Escape':
730
676
  this.callbacks.close()
731
677
  return false
732
- case 'Enter':
733
678
  case ' ':
734
- if (this.callbacks.addable && e.ctrlKey) {
679
+ const highlighted = this.content.list.querySelector('.' + this.classes.highlighted) as HTMLDivElement
680
+ if (highlighted) {
681
+ highlighted.click()
682
+ return false
683
+ }
684
+ return true
685
+ case 'Enter':
686
+ if (this.callbacks.addable) {
735
687
  addable.click()
736
688
  return false
737
689
  } else {
@@ -790,11 +742,11 @@ export default class Render {
790
742
 
791
743
  // set selected value for single and multiple
792
744
  if (this.settings.isMultiple) {
793
- let values = this.store.getSelected()
794
- values.push(newOption.value)
795
- this.callbacks.setSelected(values, true)
745
+ let ids = this.store.getSelected()
746
+ ids.push(newOption.id)
747
+ this.callbacks.setSelected(ids, true)
796
748
  } else {
797
- this.callbacks.setSelected([newOption.value], true)
749
+ this.callbacks.setSelected([newOption.id], true)
798
750
  }
799
751
 
800
752
  // Clear search
@@ -823,8 +775,10 @@ export default class Render {
823
775
  if (typeof value === 'string') {
824
776
  runFinish({
825
777
  text: value,
826
- value: value,
778
+ value: value
827
779
  })
780
+ } else if (addableValue instanceof Error) {
781
+ this.renderError(addableValue.message)
828
782
  } else {
829
783
  runFinish(value)
830
784
  }
@@ -832,8 +786,10 @@ export default class Render {
832
786
  } else if (typeof addableValue === 'string') {
833
787
  runFinish({
834
788
  text: addableValue,
835
- value: addableValue,
789
+ value: addableValue
836
790
  })
791
+ } else if (addableValue instanceof Error) {
792
+ this.renderError(addableValue.message)
837
793
  } else {
838
794
  runFinish(addableValue)
839
795
  }
@@ -846,7 +802,7 @@ export default class Render {
846
802
  searchReturn.addable = {
847
803
  main: addable,
848
804
  svg: plus,
849
- path: plusPath,
805
+ path: plusPath
850
806
  }
851
807
  }
852
808
 
@@ -991,11 +947,34 @@ export default class Render {
991
947
  if (data.length === 0) {
992
948
  const noResults = document.createElement('div')
993
949
  noResults.classList.add(this.classes.search)
994
- noResults.innerHTML = this.settings.searchText
950
+
951
+ //
952
+ if (this.callbacks.addable) {
953
+ noResults.innerHTML = this.settings.addableText.replace('{value}', this.content.search.input.value)
954
+ } else {
955
+ noResults.innerHTML = this.settings.searchText
956
+ }
995
957
  this.content.list.appendChild(noResults)
996
958
  return
997
959
  }
998
960
 
961
+ // If settings has allowDeselect and isSingle, add empty placeholder in the event they want to deselect
962
+ if (this.settings.allowDeselect && !this.settings.isMultiple) {
963
+ // Check if store options have a placeholder
964
+ const placeholderOption = this.store.filter((o) => o.placeholder, false) as Option[]
965
+ if (!placeholderOption.length) {
966
+ this.store.addOption(
967
+ new Option({
968
+ text: '',
969
+ value: '',
970
+ selected: false,
971
+ placeholder: true
972
+ }),
973
+ true
974
+ )
975
+ }
976
+ }
977
+
999
978
  // Append individual options to div container
1000
979
  for (const d of data) {
1001
980
  // Create optgroup
@@ -1074,7 +1053,7 @@ export default class Render {
1074
1053
  // Put together new list minus all options in this optgroup
1075
1054
  const newSelected = currentSelected.filter((s) => {
1076
1055
  for (const o of d.options) {
1077
- if (s === o.value) {
1056
+ if (s === o.id) {
1078
1057
  return false
1079
1058
  }
1080
1059
  }
@@ -1086,7 +1065,7 @@ export default class Render {
1086
1065
  return
1087
1066
  } else {
1088
1067
  // Put together new list with all options in this optgroup
1089
- const newSelected = currentSelected.concat(d.options.map((o) => o.value))
1068
+ const newSelected = currentSelected.concat(d.options.map((o) => o.id))
1090
1069
 
1091
1070
  // Loop through options and if they don't exist in the store
1092
1071
  // run addOption callback
@@ -1203,7 +1182,7 @@ export default class Render {
1203
1182
  optionEl.innerHTML = this.highlightText(
1204
1183
  option.html !== '' ? option.html : option.text,
1205
1184
  this.content.search.input.value,
1206
- this.classes.searchHighlighter,
1185
+ this.classes.searchHighlighter
1207
1186
  )
1208
1187
  } else if (option.html !== '') {
1209
1188
  optionEl.innerHTML = option.html
@@ -1315,8 +1294,8 @@ export default class Render {
1315
1294
 
1316
1295
  // Get values from after and set as selected
1317
1296
  this.callbacks.setSelected(
1318
- after.map((o: Option) => o.value),
1319
- false,
1297
+ after.map((o: Option) => o.id),
1298
+ false
1320
1299
  )
1321
1300
 
1322
1301
  // If closeOnSelect is true
@@ -1345,7 +1324,7 @@ export default class Render {
1345
1324
  private highlightText(str: string, search: any, className: string) {
1346
1325
  // the completed string will be itself if already set, otherwise, the string that was passed in
1347
1326
  let completedString: any = str
1348
- const regex = new RegExp('(' + search.trim() + ')(?![^<]*>[^<>]*</)', 'i')
1327
+ const regex = new RegExp('(?![^<]*>)(' + search.trim() + ')(?![^<]*>[^<>]*</)', 'i')
1349
1328
 
1350
1329
  // If the regex doesn't match the string just exit
1351
1330
  if (!str.match(regex)) {
@@ -11,7 +11,7 @@ export default [
11
11
  output: {
12
12
  file: '../../dist/slimselect.cjs.js',
13
13
  format: 'cjs',
14
- exports: 'default',
14
+ exports: 'default'
15
15
  },
16
16
  plugins: [
17
17
  typescript(),
@@ -27,12 +27,12 @@ export default [
27
27
  {
28
28
  modules: false,
29
29
  useBuiltIns: 'usage',
30
- targets: 'maintained node versions',
31
- },
32
- ],
33
- ],
34
- }),
35
- ],
30
+ targets: 'maintained node versions'
31
+ }
32
+ ]
33
+ ]
34
+ })
35
+ ]
36
36
  },
37
37
 
38
38
  // UMD build for browsers
@@ -42,13 +42,13 @@ export default [
42
42
  {
43
43
  name: 'SlimSelect',
44
44
  file: '../../dist/slimselect.js',
45
- format: 'umd',
45
+ format: 'umd'
46
46
  },
47
47
  {
48
48
  name: 'SlimSelect',
49
49
  file: '../../dist/slimselect.umd.js',
50
- format: 'umd',
51
- },
50
+ format: 'umd'
51
+ }
52
52
  ],
53
53
  plugins: [
54
54
  typescript(),
@@ -56,9 +56,9 @@ export default [
56
56
  commonjs(),
57
57
  babel({
58
58
  babelHelpers: 'bundled',
59
- exclude: 'node_modules/**',
60
- }),
61
- ],
59
+ exclude: 'node_modules/**'
60
+ })
61
+ ]
62
62
  },
63
63
 
64
64
  // UMD build for browsers minified with terser
@@ -68,13 +68,13 @@ export default [
68
68
  {
69
69
  name: 'SlimSelect',
70
70
  file: '../../dist/slimselect.min.js',
71
- format: 'umd',
71
+ format: 'umd'
72
72
  },
73
73
  {
74
74
  name: 'SlimSelect',
75
75
  file: '../../dist/slimselect.umd.min.js',
76
- format: 'umd',
77
- },
76
+ format: 'umd'
77
+ }
78
78
  ],
79
79
  plugins: [
80
80
  typescript(),
@@ -82,10 +82,10 @@ export default [
82
82
  commonjs(),
83
83
  babel({
84
84
  babelHelpers: 'bundled',
85
- exclude: 'node_modules/**',
85
+ exclude: 'node_modules/**'
86
86
  }),
87
- terser(),
88
- ],
87
+ terser()
88
+ ]
89
89
  },
90
90
 
91
91
  // ES module build for modern browsers
@@ -93,7 +93,7 @@ export default [
93
93
  input: './index.ts',
94
94
  output: {
95
95
  file: '../../dist/slimselect.es.js',
96
- format: 'es',
96
+ format: 'es'
97
97
  },
98
98
  plugins: [
99
99
  typescript(),
@@ -101,9 +101,9 @@ export default [
101
101
  commonjs(),
102
102
  babel({
103
103
  babelHelpers: 'bundled',
104
- exclude: 'node_modules/**',
105
- }),
106
- ],
104
+ exclude: 'node_modules/**'
105
+ })
106
+ ]
107
107
  },
108
108
 
109
109
  // IIFE build for modern browsers
@@ -112,7 +112,7 @@ export default [
112
112
  output: {
113
113
  name: 'SlimSelect',
114
114
  file: '../../dist/slimselect.global.js',
115
- format: 'iife',
115
+ format: 'iife'
116
116
  },
117
117
  plugins: [
118
118
  typescript(),
@@ -120,8 +120,8 @@ export default [
120
120
  commonjs(),
121
121
  babel({
122
122
  babelHelpers: 'bundled',
123
- exclude: 'node_modules/**',
124
- }),
125
- ],
126
- },
123
+ exclude: 'node_modules/**'
124
+ })
125
+ ]
126
+ }
127
127
  ]