Skip to content

Commit 17c8ef6

Browse files
committed
feat(MultiSelect): allow to pass initially selected values using the value option
1 parent 0c24f6b commit 17c8ef6

File tree

2 files changed

+76
-45
lines changed

2 files changed

+76
-45
lines changed

Diff for: docs/content/forms/multi-select.md

+1
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ const mulitSelectList = mulitSelectElementList.map(mulitSelectEl => {
351351
| `selectionType` | string | `'tag'` | Sets the selection style. |
352352
| `selectionTypeCounterText` | string | `'item(s) selected'` | Sets the counter selection label. |
353353
| `valid` | boolean | `false` | Toggle the valid state for the component. |
354+
| `value` | boolean | `null` | Sets the initial value for the multi-select component. |
354355
{{< /bs-table >}}
355356

356357
### Methods

Diff for: js/src/multi-select.js

+75-45
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ const Default = {
101101
selectAllLabel: 'Select all options',
102102
selectionType: 'tags',
103103
selectionTypeCounterText: 'item(s) selected',
104-
valid: false
104+
valid: false,
105+
value: null
105106
}
106107

107108
const DefaultType = {
@@ -123,7 +124,8 @@ const DefaultType = {
123124
selectAllLabel: 'string',
124125
selectionType: 'string',
125126
selectionTypeCounterText: 'string',
126-
valid: 'boolean'
127+
valid: 'boolean',
128+
value: '(string|array|null)'
127129
}
128130

129131
/**
@@ -136,6 +138,7 @@ class MultiSelect extends BaseComponent {
136138
constructor(element, config) {
137139
super(element, config)
138140

141+
this._configureNativeSelect()
139142
this._indicatorElement = null
140143
this._selectAllElement = null
141144
this._selectionElement = null
@@ -146,13 +149,13 @@ class MultiSelect extends BaseComponent {
146149

147150
this._clone = null
148151
this._menu = null
152+
this._selected = []
149153
this._options = this._getOptions()
150154
this._popper = null
151155
this._search = ''
152-
this._selected = this._getSelectedOptions(this._options)
153156

154157
if (this._config.options.length > 0) {
155-
this._createNativeSelect(this._config.options)
158+
this._createNativeOptions(this._element, this._config.options)
156159
}
157160

158161
this._createSelect()
@@ -240,8 +243,8 @@ class MultiSelect extends BaseComponent {
240243

241244
update(config) {
242245
this._config = this._getConfig(config)
246+
this._selected = []
243247
this._options = this._getOptions()
244-
this._selected = this._getSelectedOptions(this._options)
245248
this._menu.remove()
246249
this._clone.remove()
247250
this._element.innerHTML = ''
@@ -386,61 +389,81 @@ class MultiSelect extends BaseComponent {
386389
return this._element.classList.value.split(' ')
387390
}
388391

389-
_getOptions(node = this._element) {
392+
_getOptions() {
390393
if (this._config.options) {
391-
return this._config.options
394+
return this._getOptionsFromConfig()
392395
}
393396

397+
return this._getOptionsFromElement()
398+
}
399+
400+
_getOptionsFromConfig(options = this._config.options) {
401+
const _options = []
402+
for (const option of options) {
403+
if (option.options && Array.isArray(option.options)) {
404+
_options.push({
405+
label: option.label,
406+
options: this._getOptionsFromConfig(option.options)
407+
})
408+
continue
409+
}
410+
411+
const value = String(option.value)
412+
const isSelected = option.selected || (this._config.value && this._config.value.includes(value))
413+
414+
_options.push({
415+
...option,
416+
value,
417+
...isSelected && { selected: true },
418+
...option.disabled && { disabled: true }
419+
})
420+
421+
if (isSelected) {
422+
this._selected.push({
423+
value: String(option.value),
424+
text: option.text
425+
})
426+
}
427+
}
428+
429+
return _options
430+
}
431+
432+
_getOptionsFromElement(node = this._element) {
394433
const nodes = Array.from(node.childNodes).filter(element => element.nodeName === 'OPTION' || element.nodeName === 'OPTGROUP')
395434
const options = []
396435

397436
for (const node of nodes) {
398437
if (node.nodeName === 'OPTION' && node.value) {
438+
const isSelected = node.selected || (this._config.value && this._config.value.includes(node.value))
399439
options.push({
400440
value: node.value,
401441
text: node.innerHTML,
402-
selected: node.selected,
442+
selected: isSelected,
403443
disabled: node.disabled
404444
})
445+
446+
if (node.selected || isSelected) {
447+
this._selected.push({
448+
value: node.value,
449+
text: node.innerHTML,
450+
...node.disabled && { disabled: true }
451+
})
452+
}
405453
}
406454

407455
if (node.nodeName === 'OPTGROUP') {
408456
options.push({
409457
label: node.label,
410-
options: this._getOptions(node)
458+
options: this._getOptionsFromElement(node)
411459
})
412460
}
413461
}
414462

415463
return options
416464
}
417465

418-
_getSelectedOptions(options) {
419-
const selected = []
420-
421-
for (const option of options) {
422-
if (typeof option.value === 'undefined') {
423-
this._getSelectedOptions(option.options)
424-
continue
425-
}
426-
427-
if (option.selected) {
428-
// Add only the last option if single select
429-
if (!this._config.multiple) {
430-
selected.length = 0
431-
}
432-
433-
selected.push({
434-
value: String(option.value),
435-
text: option.text
436-
})
437-
}
438-
}
439-
440-
return selected
441-
}
442-
443-
_createNativeSelect(data) {
466+
_configureNativeSelect() {
444467
this._element.classList.add(CLASS_NAME_SELECT)
445468

446469
if (this._config.multiple) {
@@ -450,8 +473,6 @@ class MultiSelect extends BaseComponent {
450473
if (this._config.required) {
451474
this._element.setAttribute('required', true)
452475
}
453-
454-
this._createNativeOptions(this._element, data)
455476
}
456477

457478
_createNativeOptions(parentElement, options) {
@@ -681,13 +702,13 @@ class MultiSelect extends BaseComponent {
681702
}
682703
}
683704

684-
_createTag(value, text) {
705+
_createTag(value, text, disabled) {
685706
const tag = document.createElement('div')
686707
tag.classList.add(CLASS_NAME_TAG)
687708
tag.dataset.value = value
688709
tag.innerHTML = text
689710

690-
if (!this._config.disabled) {
711+
if (!this._config.disabled && disabled !== true) {
691712
const closeBtn = document.createElement('button')
692713
closeBtn.type = 'button'
693714
closeBtn.classList.add(CLASS_NAME_TAG_DELETE)
@@ -732,7 +753,7 @@ class MultiSelect extends BaseComponent {
732753

733754
_findOptionByValue(value, options = this._options) {
734755
for (const option of options) {
735-
if (option.value === value) {
756+
if (String(option.value) === value) {
736757
return option
737758
}
738759

@@ -781,8 +802,7 @@ class MultiSelect extends BaseComponent {
781802
}
782803

783804
_deselectOption(value) {
784-
const selected = this._selected.filter(option => option.value !== String(value))
785-
this._selected = selected
805+
this._selected = this._selected.filter(option => option.value !== String(value))
786806

787807
SelectorEngine.findOne(`option[value="${value}"]`, this._element).selected = false
788808

@@ -803,8 +823,10 @@ class MultiSelect extends BaseComponent {
803823

804824
_deselectLastOption() {
805825
if (this._selected.length > 0) {
806-
const last = this._selected.pop()
807-
this._deselectOption(last.value)
826+
const last = this._selected.findLast(option => option.disabled !== true)
827+
if (last) {
828+
this._deselectOption(last.value)
829+
}
808830
}
809831
}
810832

@@ -825,7 +847,7 @@ class MultiSelect extends BaseComponent {
825847
selection.innerHTML = ''
826848

827849
for (const option of this._selected) {
828-
selection.append(this._createTag(option.value, option.text))
850+
selection.append(this._createTag(option.value, option.text, option.disabled))
829851
}
830852
}
831853

@@ -1004,6 +1026,14 @@ class MultiSelect extends BaseComponent {
10041026
config.container = getElement(config.container)
10051027
}
10061028

1029+
if (typeof config.value === 'number') {
1030+
config.value = [String(config.value)]
1031+
}
1032+
1033+
if (typeof config.value === 'string') {
1034+
config.value = config.value.split(/,\s*/).map(String)
1035+
}
1036+
10071037
return config
10081038
}
10091039

0 commit comments

Comments
 (0)