Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"mouseleave",
"navbars",
"navs",
"navoverflow",
"Neue",
"noindex",
"Noto",
Expand Down
1 change: 1 addition & 0 deletions js/index.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export { default as Collapse } from './src/collapse.js'
export { default as Datepicker } from './src/datepicker.js'
export { default as Dialog } from './src/dialog.js'
export { default as Dropdown } from './src/dropdown.js'
export { default as NavOverflow } from './src/nav-overflow.js'
export { default as Offcanvas } from './src/offcanvas.js'
export { default as Strength } from './src/strength.js'
export { default as OtpInput } from './src/otp-input.js'
Expand Down
2 changes: 2 additions & 0 deletions js/index.umd.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Collapse from './src/collapse.js'
import Datepicker from './src/datepicker.js'
import Dialog from './src/dialog.js'
import Dropdown from './src/dropdown.js'
import NavOverflow from './src/nav-overflow.js'
import Offcanvas from './src/offcanvas.js'
import Strength from './src/strength.js'
import OtpInput from './src/otp-input.js'
Expand All @@ -30,6 +31,7 @@ export default {
Datepicker,
Dialog,
Dropdown,
NavOverflow,
Offcanvas,
Strength,
OtpInput,
Expand Down
58 changes: 53 additions & 5 deletions js/src/dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,13 @@ const triangleSign = (p1, p2, p3) =>
const Default = {
autoClose: true,
boundary: 'clippingParents',
container: false,
display: 'dynamic',
offset: [0, 2],
floatingConfig: null,
placement: DEFAULT_PLACEMENT,
reference: 'toggle',
strategy: 'absolute',
// Submenu options
submenuTrigger: 'both', // 'click', 'hover', or 'both'
submenuDelay: SUBMENU_CLOSE_DELAY
Expand All @@ -110,11 +112,13 @@ const Default = {
const DefaultType = {
autoClose: '(boolean|string)',
boundary: '(string|element)',
container: '(string|element|boolean)',
display: 'string',
offset: '(array|string|function)',
floatingConfig: '(null|object|function)',
placement: 'string',
reference: '(string|element|object)',
strategy: 'string',
submenuTrigger: 'string',
submenuDelay: 'number'
}
Expand Down Expand Up @@ -145,6 +149,9 @@ class Dropdown extends BaseComponent {
SelectorEngine.prev(this._element, SELECTOR_MENU)[0] ||
SelectorEngine.findOne(SELECTOR_MENU, this._parent)

// Store original menu parent for container option
this._menuOriginalParent = this._menu?.parentNode

// Parse responsive placements on init
this._parseResponsivePlacements()

Expand Down Expand Up @@ -185,6 +192,9 @@ class Dropdown extends BaseComponent {
return
}

// Move menu to container if specified (to escape overflow clipping)
this._moveMenuToContainer()

this._createFloating()

// If this is a touch-enabled device we add extra
Expand Down Expand Up @@ -220,6 +230,7 @@ class Dropdown extends BaseComponent {

dispose() {
this._disposeFloating()
this._restoreMenuToOriginalParent()
this._disposeMediaQueryListeners()
this._closeAllSubmenus()
this._clearAllSubmenuTimeouts()
Expand Down Expand Up @@ -252,6 +263,9 @@ class Dropdown extends BaseComponent {

this._disposeFloating()

// Restore menu to original parent if it was moved
this._restoreMenuToOriginalParent()

this._menu.classList.remove(CLASS_NAME_SHOW)
this._element.classList.remove(CLASS_NAME_SHOW)
this._parent.classList.remove(CLASS_NAME_SHOW)
Expand Down Expand Up @@ -326,7 +340,8 @@ class Dropdown extends BaseComponent {
referenceElement,
this._menu,
floatingConfig.placement,
floatingConfig.middleware
floatingConfig.middleware,
floatingConfig.strategy
)
}

Expand Down Expand Up @@ -434,7 +449,8 @@ class Dropdown extends BaseComponent {
_getFloatingConfig(placement, middleware) {
const defaultConfig = {
placement,
middleware
middleware,
strategy: this._config.strategy
}

return {
Expand All @@ -450,24 +466,56 @@ class Dropdown extends BaseComponent {
}
}

_getContainer() {
const { container } = this._config
if (container === false) {
return null
}

return container === true ? document.body : getElement(container)
}

_moveMenuToContainer() {
const container = this._getContainer()
if (!container || !this._menu) {
return
}

// Only move if not already in the container
if (this._menu.parentNode !== container) {
container.append(this._menu)
}
}

_restoreMenuToOriginalParent() {
if (!this._menuOriginalParent || !this._menu) {
return
}

// Only restore if menu was moved
if (this._menu.parentNode !== this._menuOriginalParent) {
this._menuOriginalParent.append(this._menu)
}
}

// Shared helper for positioning any floating element
async _applyFloatingPosition(reference, floating, placement, middleware) {
async _applyFloatingPosition(reference, floating, placement, middleware, strategy = 'absolute') {
if (!floating.isConnected) {
return null
}

const { x, y, placement: finalPlacement } = await computePosition(
reference,
floating,
{ placement, middleware }
{ placement, middleware, strategy }
)

if (!floating.isConnected) {
return null
}

Object.assign(floating.style, {
position: 'absolute',
position: strategy,
left: `${x}px`,
top: `${y}px`,
margin: '0'
Expand Down
Loading
Loading