Skip to content

Commit

Permalink
feat(MultiSelect): allows to append the dropdown to a specific element
Browse files Browse the repository at this point in the history
  • Loading branch information
mrholek committed Oct 21, 2024
1 parent 46d447a commit 39e84de
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 9 deletions.
1 change: 1 addition & 0 deletions docs/content/forms/multi-select.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ const mulitSelectList = mulitSelectElementList.map(mulitSelectEl => {
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `cleaner`| boolean| `true` | Enables selection cleaner element. |
| `container` | string, element, false | `false` | Appends the dropdown to a specific element. Example: `container: 'body'`. |
| `disabled` | boolean | `false` | Toggle the disabled state for the component. |
| `invalid` | boolean | `false` | Toggle the invalid state for the component. |
| `multiple` | boolean | `true` | It specifies that multiple options can be selected at once. |
Expand Down
53 changes: 46 additions & 7 deletions js/src/multi-select.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import SelectorEngine from './dom/selector-engine.js'
import {
defineJQueryPlugin,
getNextActiveElement,
getElement,
isVisible,
isRTL
} from './util/index.js'
Expand Down Expand Up @@ -80,6 +81,7 @@ const CLASS_NAME_TAG_DELETE = 'form-multi-select-tag-delete'

const Default = {
cleaner: true,
container: false,
disabled: false,
invalid: false,
multiple: true,
Expand All @@ -100,6 +102,7 @@ const Default = {

const DefaultType = {
cleaner: 'boolean',
container: '(string|element|boolean)',
disabled: 'boolean',
invalid: 'boolean',
multiple: 'boolean',
Expand Down Expand Up @@ -179,6 +182,12 @@ class MultiSelect extends BaseComponent {
EventHandler.trigger(this._element, EVENT_SHOW)
this._clone.classList.add(CLASS_NAME_SHOW)
this._clone.setAttribute('aria-expanded', true)

if (this._config.container) {
this._menu.style.minWidth = `${this._clone.offsetWidth}px`
this._menu.classList.add(CLASS_NAME_SHOW)
}

EventHandler.trigger(this._element, EVENT_SHOWN)

this._createPopper()
Expand All @@ -199,6 +208,11 @@ class MultiSelect extends BaseComponent {
this._onSearchChange(this._searchElement)
this._clone.classList.remove(CLASS_NAME_SHOW)
this._clone.setAttribute('aria-expanded', 'false')

if (this._config.container) {
this._menu.classList.remove(CLASS_NAME_SHOW)
}

EventHandler.trigger(this._element, EVENT_HIDDEN)
}

Expand All @@ -220,6 +234,7 @@ class MultiSelect extends BaseComponent {
this._config = this._getConfig(config)
this._options = this._getOptions()
this._selected = this._getSelectedOptions(this._options)
this._menu.remove()
this._clone.remove()
this._element.innerHTML = ''
this._createNativeOptions(this._element, this._options)
Expand Down Expand Up @@ -534,6 +549,7 @@ class MultiSelect extends BaseComponent {
}],
placement: isRTL() ? 'bottom-end' : 'bottom-start'
}

this._popper = Popper.createPopper(this._togglerElement, this._menu, popperConfig)
}

Expand Down Expand Up @@ -575,7 +591,13 @@ class MultiSelect extends BaseComponent {

dropdownDiv.append(optionsDiv)

this._clone.append(dropdownDiv)
const { container } = this._config
if (container) {
// this._clone.parentNode.insertBefore(dropdownDiv, this._clone.nextSibling)
getElement(container).append(dropdownDiv)
} else {
this._clone.append(dropdownDiv)
}

this._createOptions(optionsDiv, this._options)
this._optionsElement = optionsDiv
Expand Down Expand Up @@ -649,7 +671,7 @@ class MultiSelect extends BaseComponent {
}

const value = String(element.dataset.value)
const { text } = this._options.find(option => option.value === value)
const { text } = this._findOptionByValue(value)

if (this._config.multiple && element.classList.contains(CLASS_NAME_SELECTED)) {
this._deselectOption(value)
Expand All @@ -666,6 +688,23 @@ class MultiSelect extends BaseComponent {
}
}

_findOptionByValue(value, options = this._options) {
for (const option of options) {
if (option.value === value) {
return option
}

if (option.options && Array.isArray(option.options)) {
const found = this._findOptionByValue(value, option.options)
if (found) {
return found
}
}
}

return null
}

_selectOption(value, text) {
if (!this._config.multiple) {
this.deselectAll()
Expand Down Expand Up @@ -860,7 +899,7 @@ class MultiSelect extends BaseComponent {
}

_filterOptionsList() {
const options = SelectorEngine.find(SELECTOR_OPTION, this._clone)
const options = SelectorEngine.find(SELECTOR_OPTION, this._menu)
let visibleOptions = 0

for (const option of options) {
Expand All @@ -884,8 +923,8 @@ class MultiSelect extends BaseComponent {
}

if (visibleOptions > 0) {
if (SelectorEngine.findOne(SELECTOR_OPTIONS_EMPTY, this._clone)) {
SelectorEngine.findOne(SELECTOR_OPTIONS_EMPTY, this._clone).remove()
if (SelectorEngine.findOne(SELECTOR_OPTIONS_EMPTY, this._menu)) {
SelectorEngine.findOne(SELECTOR_OPTIONS_EMPTY, this._menu).remove()
}

return
Expand All @@ -896,8 +935,8 @@ class MultiSelect extends BaseComponent {
placeholder.classList.add(CLASS_NAME_OPTIONS_EMPTY)
placeholder.innerHTML = this._config.searchNoResultsLabel

if (!SelectorEngine.findOne(SELECTOR_OPTIONS_EMPTY, this._clone)) {
SelectorEngine.findOne(SELECTOR_OPTIONS, this._clone).append(placeholder)
if (!SelectorEngine.findOne(SELECTOR_OPTIONS_EMPTY, this._menu)) {
SelectorEngine.findOne(SELECTOR_OPTIONS, this._menu).append(placeholder)
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions scss/forms/_form-multi-select.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.form-multi-select {
.form-multi-select,
*:not(.form-multi-select) > .form-multi-select-dropdown {
// scss-docs-start form-multi-select-css-vars
--#{$prefix}form-multi-select-zindex: #{$form-multi-select-zindex};
--#{$prefix}form-multi-select-font-family: #{$form-multi-select-font-family};
Expand Down Expand Up @@ -326,7 +327,8 @@ select.form-multi-select {
@include box-shadow(var(--#{$prefix}form-multi-select-dropdown-box-shadow));
@include elevation(4);

.form-multi-select.show & {
.form-multi-select.show &,
&.show {
display: block;
}
}
Expand Down

0 comments on commit 39e84de

Please sign in to comment.