slim-select 2.4.4 → 2.5.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.
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ 'use strict'
6
+ import { describe, expect, test } from '@jest/globals'
7
+ import SlimSelect from './'
8
+ import { OptionOptional } from 'src/vue/dist/slim-select/store'
9
+ import { Config } from 'src/vue/dist/slim-select'
10
+
11
+ describe('SlimSelect Module', () => {
12
+ test('constructor', () => {
13
+ document.body.innerHTML = '<select id="test"></select>'
14
+
15
+ let slimSelect = new SlimSelect({
16
+ select: '#test',
17
+ })
18
+ expect(slimSelect).toBeInstanceOf(SlimSelect)
19
+ })
20
+
21
+ test('multiple - do not render deselect all with no selected options', () => {
22
+ document.body.innerHTML = `<select id="test" multiple>
23
+ <option data-placeholder="true"></option>
24
+ <option id="1" value="1">One</option>
25
+ <option id="2" value="2">Two</option>
26
+ <option id="3" value="3">Two</option>
27
+ </select>`
28
+
29
+ const options: OptionOptional[] = [
30
+ {
31
+ id: '1',
32
+ value: '1',
33
+ text: 'One',
34
+ },
35
+ {
36
+ id: '2',
37
+ value: '2',
38
+ text: 'Two',
39
+ },
40
+ {
41
+ id: '3',
42
+ value: '3',
43
+ text: 'Three',
44
+ },
45
+ ]
46
+ const config: Config = {
47
+ select: '#test',
48
+ settings: {
49
+ allowDeselect: true,
50
+ },
51
+ data: options,
52
+ }
53
+
54
+ let slimSelect = new SlimSelect(config)
55
+ expect(slimSelect.store.getSelectType()).toEqual('multiple')
56
+ expect(slimSelect.getSelected()).toHaveLength(0)
57
+ expect(slimSelect.render.main.deselect.main.classList).toContain(slimSelect.render.classes.hide)
58
+ })
59
+ test('multiple - render deselect all option with selected options', () => {
60
+ document.body.innerHTML = `<select id="test" multiple>
61
+ <option data-placeholder="true"></option>
62
+ <option id="1" value="1">One</option>
63
+ <option id="2" value="2">Two</option>
64
+ <option id="3" value="3">Two</option>
65
+ </select>`
66
+
67
+ const options: OptionOptional[] = [
68
+ {
69
+ id: '1',
70
+ value: '1',
71
+ text: 'One',
72
+ selected: true,
73
+ },
74
+ {
75
+ id: '2',
76
+ value: '2',
77
+ text: 'Two',
78
+ selected: true,
79
+ },
80
+ {
81
+ id: '3',
82
+ value: '3',
83
+ text: 'Three',
84
+ },
85
+ ]
86
+ const config: Config = {
87
+ select: '#test',
88
+ settings: {
89
+ allowDeselect: true,
90
+ },
91
+ data: options,
92
+ }
93
+
94
+ let slimSelect = new SlimSelect(config)
95
+ expect(slimSelect.store.getSelectType()).toEqual('multiple')
96
+ expect(slimSelect.getSelected()).toHaveLength(2)
97
+ expect(slimSelect.render.main.deselect.main.classList).not.toContain(slimSelect.render.classes.hide)
98
+ })
99
+ })
@@ -281,8 +281,13 @@ export default class Render {
281
281
  // Add deselect
282
282
  const deselect = document.createElement('div')
283
283
  deselect.classList.add(this.classes.deselect)
284
- if (!this.settings.allowDeselect || this.settings.isMultiple) {
284
+
285
+ // Check if deselect is to be shown or not
286
+ const selectedOptions = this.store?.getSelectedOptions()
287
+ if (!this.settings.allowDeselect || (this.settings.isMultiple && selectedOptions && selectedOptions.length <= 0)) {
285
288
  deselect.classList.add(this.classes.hide)
289
+ } else {
290
+ deselect.classList.remove(this.classes.hide)
286
291
  }
287
292
 
288
293
  // Add deselect onclick event
@@ -305,7 +310,12 @@ export default class Render {
305
310
  }
306
311
 
307
312
  if (shouldDelete) {
308
- this.callbacks.setSelected([''], false)
313
+ if (this.settings.isMultiple) {
314
+ this.callbacks.setSelected([], false)
315
+ this.updateDeselectAll()
316
+ } else {
317
+ this.callbacks.setSelected([''], false)
318
+ }
309
319
 
310
320
  // Check if we need to close the dropdown
311
321
  if (this.settings.closeOnSelect) {
@@ -414,13 +424,17 @@ export default class Render {
414
424
  // Create single value container
415
425
  const singleValue = document.createElement('div')
416
426
  singleValue.classList.add(this.classes.single)
417
- singleValue.innerHTML = selectedSingle.html ? selectedSingle.html : selectedSingle.text
427
+ if (selectedSingle.html) {
428
+ singleValue.innerHTML = selectedSingle.html
429
+ } else {
430
+ singleValue.innerText = selectedSingle.text
431
+ }
418
432
 
419
433
  // If there is a selected value, set a single div
420
434
  this.main.values.innerHTML = singleValue.outerHTML
421
435
  }
422
436
 
423
- // If allowDeselect is false or selected value is empty just hide deslect
437
+ // If allowDeselect is false or selected value is empty just hide deselect
424
438
  if (!this.settings.allowDeselect || !selected.length) {
425
439
  this.main.deselect.main.classList.add(this.classes.hide)
426
440
  } else {
@@ -488,7 +502,9 @@ export default class Render {
488
502
  for (const n of removeNodes) {
489
503
  n.classList.add(this.classes.valueOut)
490
504
  setTimeout(() => {
491
- this.main.values.removeChild(n)
505
+ if (this.main.values.hasChildNodes() && this.main.values.contains(n)) {
506
+ this.main.values.removeChild(n)
507
+ }
492
508
  }, 100)
493
509
  }
494
510
 
@@ -513,6 +529,7 @@ export default class Render {
513
529
  }
514
530
  }
515
531
  }
532
+ this.updateDeselectAll()
516
533
  }
517
534
 
518
535
  public multipleValue(option: Option): HTMLDivElement {
@@ -522,7 +539,7 @@ export default class Render {
522
539
 
523
540
  const text = document.createElement('div')
524
541
  text.classList.add(this.classes.valueText)
525
- text.innerHTML = option.text // For multiple values always use text
542
+ text.innerText = option.text // For multiple values always use text
526
543
  value.appendChild(text)
527
544
 
528
545
  // Only add deletion if the option is not mandatory
@@ -583,6 +600,8 @@ export default class Render {
583
600
  if (this.callbacks.afterChange) {
584
601
  this.callbacks.afterChange(after)
585
602
  }
603
+
604
+ this.updateDeselectAll()
586
605
  }
587
606
  }
588
607
 
@@ -1373,4 +1392,24 @@ export default class Render {
1373
1392
  // Move content below
1374
1393
  return 'down'
1375
1394
  }
1395
+
1396
+ // Updates deselect based on item count and allowDeselect setting
1397
+ public updateDeselectAll(): void {
1398
+ if (!this.store || !this.settings) {
1399
+ return
1400
+ }
1401
+ const selected = this.store.getSelectedOptions()
1402
+ const hasSelectedItems = selected && selected.length > 0
1403
+ const isMultiple = this.settings.isMultiple
1404
+ const allowDeselect = this.settings.allowDeselect
1405
+
1406
+ const deselectButton = this.main.deselect.main
1407
+ const hideClass = this.classes.hide
1408
+
1409
+ if (allowDeselect && !(isMultiple && !hasSelectedItems)) {
1410
+ deselectButton.classList.remove(hideClass)
1411
+ } else {
1412
+ deselectButton.classList.add(hideClass)
1413
+ }
1414
+ }
1376
1415
  }
@@ -338,4 +338,8 @@ export default class Store {
338
338
 
339
339
  return dataSearch
340
340
  }
341
+
342
+ public getSelectType(): string {
343
+ return this.selectType
344
+ }
341
345
  }
@@ -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;
@@ -1,46 +1,46 @@
1
- import Settings, { SettingsPartial } from './settings';
2
- import Render from './render';
3
- import Select from './select';
4
- import Store, { DataArray, DataArrayPartial, Option, OptionOptional } from './store';
5
- export interface Config {
6
- select: string | Element;
7
- data?: DataArrayPartial;
8
- settings?: SettingsPartial;
9
- events?: Events;
10
- }
11
- export interface Events {
12
- search?: (searchValue: string, currentData: DataArray) => Promise<DataArrayPartial> | DataArrayPartial;
13
- searchFilter?: (option: Option, search: string) => boolean;
14
- addable?: (value: string) => Promise<OptionOptional | string> | OptionOptional | string | false | null | undefined;
15
- beforeChange?: (newVal: Option[], oldVal: Option[]) => boolean | void;
16
- afterChange?: (newVal: Option[]) => void;
17
- beforeOpen?: () => void;
18
- afterOpen?: () => void;
19
- beforeClose?: () => void;
20
- afterClose?: () => void;
21
- error?: (err: Error) => void;
22
- }
23
- export default class SlimSelect {
24
- selectEl: HTMLSelectElement;
25
- settings: Settings;
26
- select: Select;
27
- store: Store;
28
- render: Render;
29
- events: Events;
30
- constructor(config: Config);
31
- enable(): void;
32
- disable(): void;
33
- getData(): DataArray;
34
- setData(data: DataArrayPartial): void;
35
- getSelected(): string[];
36
- setSelected(value: string | string[], runAfterChange?: boolean): void;
37
- addOption(option: OptionOptional): void;
38
- open(): void;
39
- close(eventType?: string | null): void;
40
- search(value: string): void;
41
- destroy(): void;
42
- private windowResize;
43
- private windowScroll;
44
- private documentClick;
45
- private windowVisibilityChange;
46
- }
1
+ import Settings, { SettingsPartial } from './settings';
2
+ import Render from './render';
3
+ import Select from './select';
4
+ import Store, { DataArray, DataArrayPartial, Option, OptionOptional } from './store';
5
+ export interface Config {
6
+ select: string | Element;
7
+ data?: DataArrayPartial;
8
+ settings?: SettingsPartial;
9
+ events?: Events;
10
+ }
11
+ export interface Events {
12
+ search?: (searchValue: string, currentData: DataArray) => Promise<DataArrayPartial> | DataArrayPartial;
13
+ searchFilter?: (option: Option, search: string) => boolean;
14
+ addable?: (value: string) => Promise<OptionOptional | string> | OptionOptional | string | false | null | undefined;
15
+ beforeChange?: (newVal: Option[], oldVal: Option[]) => boolean | void;
16
+ afterChange?: (newVal: Option[]) => void;
17
+ beforeOpen?: () => void;
18
+ afterOpen?: () => void;
19
+ beforeClose?: () => void;
20
+ afterClose?: () => void;
21
+ error?: (err: Error) => void;
22
+ }
23
+ export default class SlimSelect {
24
+ selectEl: HTMLSelectElement;
25
+ settings: Settings;
26
+ select: Select;
27
+ store: Store;
28
+ render: Render;
29
+ events: Events;
30
+ constructor(config: Config);
31
+ enable(): void;
32
+ disable(): void;
33
+ getData(): DataArray;
34
+ setData(data: DataArrayPartial): void;
35
+ getSelected(): string[];
36
+ setSelected(value: string | string[], runAfterChange?: boolean): void;
37
+ addOption(option: OptionOptional): void;
38
+ open(): void;
39
+ close(eventType?: string | null): void;
40
+ search(value: string): void;
41
+ destroy(): void;
42
+ private windowResize;
43
+ private windowScroll;
44
+ private documentClick;
45
+ private windowVisibilityChange;
46
+ }
@@ -1,119 +1,120 @@
1
- import Settings from './settings';
2
- import Store, { DataArray, Option, OptionOptional } from './store';
3
- export interface Callbacks {
4
- open: () => void;
5
- close: () => void;
6
- addable?: (value: string) => Promise<OptionOptional | string> | OptionOptional | string | false | undefined | null;
7
- setSelected: (value: string[], runAfterChange: boolean) => void;
8
- addOption: (option: Option) => void;
9
- search: (search: string) => void;
10
- beforeChange?: (newVal: Option[], oldVal: Option[]) => boolean | void;
11
- afterChange?: (newVal: Option[]) => void;
12
- }
13
- export interface Main {
14
- main: HTMLDivElement;
15
- values: HTMLDivElement;
16
- deselect: {
17
- main: HTMLDivElement;
18
- svg: SVGSVGElement;
19
- path: SVGPathElement;
20
- };
21
- arrow: {
22
- main: SVGSVGElement;
23
- path: SVGPathElement;
24
- };
25
- }
26
- export interface Content {
27
- main: HTMLDivElement;
28
- search: Search;
29
- list: HTMLDivElement;
30
- }
31
- export interface Search {
32
- main: HTMLDivElement;
33
- input: HTMLInputElement;
34
- addable?: {
35
- main: HTMLDivElement;
36
- svg: SVGSVGElement;
37
- path: SVGPathElement;
38
- };
39
- }
40
- export default class Render {
41
- settings: Settings;
42
- store: Store;
43
- callbacks: Callbacks;
44
- main: Main;
45
- content: Content;
46
- classes: {
47
- main: string;
48
- placeholder: string;
49
- values: string;
50
- single: string;
51
- max: string;
52
- value: string;
53
- valueText: string;
54
- valueDelete: string;
55
- valueOut: string;
56
- deselect: string;
57
- deselectPath: string;
58
- arrow: string;
59
- arrowClose: string;
60
- arrowOpen: string;
61
- content: string;
62
- openAbove: string;
63
- openBelow: string;
64
- search: string;
65
- searchHighlighter: string;
66
- searching: string;
67
- addable: string;
68
- addablePath: string;
69
- list: string;
70
- optgroup: string;
71
- optgroupLabel: string;
72
- optgroupLabelText: string;
73
- optgroupActions: string;
74
- optgroupSelectAll: string;
75
- optgroupSelectAllBox: string;
76
- optgroupSelectAllCheck: string;
77
- optgroupClosable: string;
78
- option: string;
79
- optionDelete: string;
80
- highlighted: string;
81
- open: string;
82
- close: string;
83
- selected: string;
84
- error: string;
85
- disabled: string;
86
- hide: string;
87
- };
88
- constructor(settings: Required<Settings>, store: Store, callbacks: Callbacks);
89
- enable(): void;
90
- disable(): void;
91
- open(): void;
92
- close(): void;
93
- updateClassStyles(): void;
94
- updateAriaAttributes(): void;
95
- mainDiv(): Main;
96
- mainFocus(eventType: string | null): void;
97
- placeholder(): HTMLDivElement;
98
- renderValues(): void;
99
- private renderSingleValue;
100
- private renderMultipleValues;
101
- multipleValue(option: Option): HTMLDivElement;
102
- contentDiv(): Content;
103
- moveContent(): void;
104
- searchDiv(): Search;
105
- searchFocus(): void;
106
- getOptions(notPlaceholder?: boolean, notDisabled?: boolean, notHidden?: boolean): HTMLDivElement[];
107
- highlight(dir: 'up' | 'down'): void;
108
- listDiv(): HTMLDivElement;
109
- renderError(error: string): void;
110
- renderSearching(): void;
111
- renderOptions(data: DataArray): void;
112
- option(option: Option): HTMLDivElement;
113
- destroy(): void;
114
- private highlightText;
115
- moveContentAbove(): void;
116
- moveContentBelow(): void;
117
- ensureElementInView(container: HTMLElement, element: HTMLElement): void;
118
- putContent(): 'up' | 'down';
119
- }
1
+ import Settings from './settings';
2
+ import Store, { DataArray, Option, OptionOptional } from './store';
3
+ export interface Callbacks {
4
+ open: () => void;
5
+ close: () => void;
6
+ addable?: (value: string) => Promise<OptionOptional | string> | OptionOptional | string | false | undefined | null;
7
+ setSelected: (value: string[], runAfterChange: boolean) => void;
8
+ addOption: (option: Option) => void;
9
+ search: (search: string) => void;
10
+ beforeChange?: (newVal: Option[], oldVal: Option[]) => boolean | void;
11
+ afterChange?: (newVal: Option[]) => void;
12
+ }
13
+ export interface Main {
14
+ main: HTMLDivElement;
15
+ values: HTMLDivElement;
16
+ deselect: {
17
+ main: HTMLDivElement;
18
+ svg: SVGSVGElement;
19
+ path: SVGPathElement;
20
+ };
21
+ arrow: {
22
+ main: SVGSVGElement;
23
+ path: SVGPathElement;
24
+ };
25
+ }
26
+ export interface Content {
27
+ main: HTMLDivElement;
28
+ search: Search;
29
+ list: HTMLDivElement;
30
+ }
31
+ export interface Search {
32
+ main: HTMLDivElement;
33
+ input: HTMLInputElement;
34
+ addable?: {
35
+ main: HTMLDivElement;
36
+ svg: SVGSVGElement;
37
+ path: SVGPathElement;
38
+ };
39
+ }
40
+ export default class Render {
41
+ settings: Settings;
42
+ store: Store;
43
+ callbacks: Callbacks;
44
+ main: Main;
45
+ content: Content;
46
+ classes: {
47
+ main: string;
48
+ placeholder: string;
49
+ values: string;
50
+ single: string;
51
+ max: string;
52
+ value: string;
53
+ valueText: string;
54
+ valueDelete: string;
55
+ valueOut: string;
56
+ deselect: string;
57
+ deselectPath: string;
58
+ arrow: string;
59
+ arrowClose: string;
60
+ arrowOpen: string;
61
+ content: string;
62
+ openAbove: string;
63
+ openBelow: string;
64
+ search: string;
65
+ searchHighlighter: string;
66
+ searching: string;
67
+ addable: string;
68
+ addablePath: string;
69
+ list: string;
70
+ optgroup: string;
71
+ optgroupLabel: string;
72
+ optgroupLabelText: string;
73
+ optgroupActions: string;
74
+ optgroupSelectAll: string;
75
+ optgroupSelectAllBox: string;
76
+ optgroupSelectAllCheck: string;
77
+ optgroupClosable: string;
78
+ option: string;
79
+ optionDelete: string;
80
+ highlighted: string;
81
+ open: string;
82
+ close: string;
83
+ selected: string;
84
+ error: string;
85
+ disabled: string;
86
+ hide: string;
87
+ };
88
+ constructor(settings: Required<Settings>, store: Store, callbacks: Callbacks);
89
+ enable(): void;
90
+ disable(): void;
91
+ open(): void;
92
+ close(): void;
93
+ updateClassStyles(): void;
94
+ updateAriaAttributes(): void;
95
+ mainDiv(): Main;
96
+ mainFocus(eventType: string | null): void;
97
+ placeholder(): HTMLDivElement;
98
+ renderValues(): void;
99
+ private renderSingleValue;
100
+ private renderMultipleValues;
101
+ multipleValue(option: Option): HTMLDivElement;
102
+ contentDiv(): Content;
103
+ moveContent(): void;
104
+ searchDiv(): Search;
105
+ searchFocus(): void;
106
+ getOptions(notPlaceholder?: boolean, notDisabled?: boolean, notHidden?: boolean): HTMLDivElement[];
107
+ highlight(dir: 'up' | 'down'): void;
108
+ listDiv(): HTMLDivElement;
109
+ renderError(error: string): void;
110
+ renderSearching(): void;
111
+ renderOptions(data: DataArray): void;
112
+ option(option: Option): HTMLDivElement;
113
+ destroy(): void;
114
+ private highlightText;
115
+ moveContentAbove(): void;
116
+ moveContentBelow(): void;
117
+ ensureElementInView(container: HTMLElement, element: HTMLElement): void;
118
+ putContent(): 'up' | 'down';
119
+ updateDeselectAll(): void;
120
+ }
@@ -1,28 +1,28 @@
1
- import { DataArray, DataArrayPartial, Optgroup, OptgroupOptional, Option } from './store';
2
- export default class Select {
3
- select: HTMLSelectElement;
4
- onValueChange?: (value: string[]) => void;
5
- onClassChange?: (classes: string[]) => void;
6
- onDisabledChange?: (disabled: boolean) => void;
7
- onOptionsChange?: (data: DataArrayPartial) => void;
8
- listen: boolean;
9
- private observer;
10
- constructor(select: HTMLSelectElement);
11
- enable(): void;
12
- disable(): void;
13
- hideUI(): void;
14
- showUI(): void;
15
- changeListen(listen: boolean): void;
16
- valueChange(ev: Event): boolean;
17
- private observeCall;
18
- getData(): DataArrayPartial;
19
- getDataFromOptgroup(optgroup: HTMLOptGroupElement): OptgroupOptional;
20
- getDataFromOption(option: HTMLOptionElement): Option;
21
- getSelectedValues(): string[];
22
- setSelected(value: string[]): void;
23
- updateSelect(id?: string, style?: string, classes?: string[]): void;
24
- updateOptions(data: DataArray): void;
25
- createOptgroup(optgroup: Optgroup): HTMLOptGroupElement;
26
- createOption(info: Option): HTMLOptionElement;
27
- destroy(): void;
28
- }
1
+ import { DataArray, DataArrayPartial, Optgroup, OptgroupOptional, Option } from './store';
2
+ export default class Select {
3
+ select: HTMLSelectElement;
4
+ onValueChange?: (value: string[]) => void;
5
+ onClassChange?: (classes: string[]) => void;
6
+ onDisabledChange?: (disabled: boolean) => void;
7
+ onOptionsChange?: (data: DataArrayPartial) => void;
8
+ listen: boolean;
9
+ private observer;
10
+ constructor(select: HTMLSelectElement);
11
+ enable(): void;
12
+ disable(): void;
13
+ hideUI(): void;
14
+ showUI(): void;
15
+ changeListen(listen: boolean): void;
16
+ valueChange(ev: Event): boolean;
17
+ private observeCall;
18
+ getData(): DataArrayPartial;
19
+ getDataFromOptgroup(optgroup: HTMLOptGroupElement): OptgroupOptional;
20
+ getDataFromOption(option: HTMLOptionElement): Option;
21
+ getSelectedValues(): string[];
22
+ setSelected(value: string[]): void;
23
+ updateSelect(id?: string, style?: string, classes?: string[]): void;
24
+ updateOptions(data: DataArray): void;
25
+ createOptgroup(optgroup: Optgroup): HTMLOptGroupElement;
26
+ createOption(info: Option): HTMLOptionElement;
27
+ destroy(): void;
28
+ }