Skip to content

Commit 095ac24

Browse files
committed
fix(CDropdown): prevents closing the dropdown menu when clicking on the scrollbar
1 parent 0db5c82 commit 095ac24

File tree

2 files changed

+158
-11
lines changed

2 files changed

+158
-11
lines changed

packages/coreui-vue/src/components/dropdown/CDropdown.ts

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ const CDropdown = defineComponent({
194194
initPopper(dropdownToggleRef.value, dropdownMenuRef.value, popperConfig)
195195
}
196196

197-
window.addEventListener('mouseup', handleMouseUp)
197+
window.addEventListener('click', handleClick)
198198
window.addEventListener('keyup', handleKeyup)
199199
dropdownToggleRef.value.addEventListener('keydown', handleKeydown)
200200
dropdownMenuRef.value.addEventListener('keydown', handleKeydown)
@@ -205,15 +205,19 @@ const CDropdown = defineComponent({
205205
pendingKeyDownEventRef.value = null
206206
})
207207
}
208-
208+
209209
emit('show')
210210
return
211211
}
212212

213-
popper.value && destroyPopper()
214-
window.removeEventListener('mouseup', handleMouseUp)
213+
if (popper.value) {
214+
destroyPopper()
215+
}
216+
217+
window.removeEventListener('click', handleClick)
215218
window.removeEventListener('keyup', handleKeyup)
216219
dropdownMenuRef.value && dropdownMenuRef.value.removeEventListener('keydown', handleKeydown)
220+
dropdownToggleRef.value && dropdownToggleRef.value.removeEventListener('keydown', handleKeydown)
217221
emit('hide')
218222
})
219223

@@ -259,24 +263,36 @@ const CDropdown = defineComponent({
259263
}
260264
}
261265

262-
const handleMouseUp = (event: Event) => {
266+
const handleClick = (event: Event) => {
263267
if (!dropdownToggleRef.value || !dropdownMenuRef.value) {
264268
return
265269
}
266270

267-
if (dropdownToggleRef.value.contains(event.target as HTMLElement)) {
271+
if ((event as MouseEvent).button === 2) {
272+
return
273+
}
274+
275+
const composedPath = event.composedPath()
276+
const isOnToggle = composedPath.includes(dropdownToggleRef.value)
277+
const isOnMenu = composedPath.includes(dropdownMenuRef.value)
278+
279+
if (isOnToggle) {
280+
return
281+
}
282+
283+
const target = event.target as HTMLElement | null
284+
const FORM_TAG_RE = /^(input|select|option|textarea|form|button|label)$/i
285+
286+
if (isOnMenu && target && FORM_TAG_RE.test(target.tagName)) {
268287
return
269288
}
270289

271290
if (
272291
props.autoClose === true ||
273-
(props.autoClose === 'inside' &&
274-
dropdownMenuRef.value.contains(event.target as HTMLElement)) ||
275-
(props.autoClose === 'outside' &&
276-
!dropdownMenuRef.value.contains(event.target as HTMLElement))
292+
(props.autoClose === 'inside' && isOnMenu) ||
293+
(props.autoClose === 'outside' && !isOnMenu)
277294
) {
278295
setVisible(false)
279-
return
280296
}
281297
}
282298

packages/docs/components/dropdown.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,137 @@ Put a form within a dropdown menu, or make it into a dropdown menu.
680680
<CDropdownItem href="#">Forgot password?</CDropdownItem>
681681
```
682682

683+
## Dropdown options
684+
685+
Use `offset` to displace the dropdown from its default position. The value is a string with two numbers separated by a comma, e.g. `:offset="[10, 20]"`. Use `teleport` property to render dropdowns in `body` instead of the parent element. This helps to avoid any overflow or z-index issues.
686+
687+
::: demo
688+
<div class="d-flex gap-1">
689+
<CDropdown :offset="[10, 20]">
690+
<CDropdownToggle color="secondary">Offset</CDropdownToggle>
691+
<CDropdownMenu>
692+
<CDropdownItem href="#">Action</CDropdownItem>
693+
<CDropdownItem href="#">Another action</CDropdownItem>
694+
<CDropdownItem href="#">Something else here</CDropdownItem>
695+
</CDropdownMenu>
696+
</CDropdown>
697+
<CDropdown teleport>
698+
<CDropdownToggle color="secondary" aria-controls="dropdownMenuInTeleport">
699+
Teleport
700+
</CDropdownToggle>
701+
<CDropdownMenu id="dropdownMenuInTeleport">
702+
<CDropdownItem href="#">Action</CDropdownItem>
703+
<CDropdownItem href="#">Another action</CDropdownItem>
704+
<CDropdownItem href="#">Something else here</CDropdownItem>
705+
</CDropdownMenu>
706+
</CDropdown>
707+
</div>
708+
:::
709+
```vue
710+
<div class="d-flex gap-1">
711+
<CDropdown :offset="[10, 20]">
712+
<CDropdownToggle color="secondary">Offset</CDropdownToggle>
713+
<CDropdownMenu>
714+
<CDropdownItem href="#">Action</CDropdownItem>
715+
<CDropdownItem href="#">Another action</CDropdownItem>
716+
<CDropdownItem href="#">Something else here</CDropdownItem>
717+
</CDropdownMenu>
718+
</CDropdown>
719+
<CDropdown teleport>
720+
<CDropdownToggle color="secondary" aria-controls="dropdownMenuInTeleport">
721+
Teleport
722+
</CDropdownToggle>
723+
<CDropdownMenu id="dropdownMenuInTeleport">
724+
<CDropdownItem href="#">Action</CDropdownItem>
725+
<CDropdownItem href="#">Another action</CDropdownItem>
726+
<CDropdownItem href="#">Something else here</CDropdownItem>
727+
</CDropdownMenu>
728+
</CDropdown>
729+
</div>
730+
```
731+
732+
### Auto close behavior
733+
734+
By default, dropdowns are closed when clicking outside of the dropdown menu or the toggle button. You can change this behavior with the `autoClose` property. Set `autoClose` to:
735+
736+
- `true` - Close on clicks inside or outside of the React.js dropdown menu.
737+
- `false` - Disable auto-close; close manually by setting the `:visible="false"` (also not closed by `Escape`).
738+
- `'inside'` - Close only when clicking inside the React.js dropdown menu.
739+
- `'outside'` - Close only when clicking outside the React.js dropdown menu.
740+
741+
::: demo
742+
<div class="d-flex gap-1">
743+
<CDropdown>
744+
<CDropdownToggle color="secondary">Default dropdown</CDropdownToggle>
745+
<CDropdownMenu>
746+
<CDropdownItem href="#">Action</CDropdownItem>
747+
<CDropdownItem href="#">Another action</CDropdownItem>
748+
<CDropdownItem href="#">Something else here</CDropdownItem>
749+
</CDropdownMenu>
750+
</CDropdown>
751+
<CDropdown autoClose="inside">
752+
<CDropdownToggle color="secondary">Clickable inside</CDropdownToggle>
753+
<CDropdownMenu>
754+
<CDropdownItem href="#">Action</CDropdownItem>
755+
<CDropdownItem href="#">Another action</CDropdownItem>
756+
<CDropdownItem href="#">Something else here</CDropdownItem>
757+
</CDropdownMenu>
758+
</CDropdown>
759+
<CDropdown autoClose="outside">
760+
<CDropdownToggle color="secondary">Clickable outside</CDropdownToggle>
761+
<CDropdownMenu>
762+
<CDropdownItem href="#">Action</CDropdownItem>
763+
<CDropdownItem href="#">Another action</CDropdownItem>
764+
<CDropdownItem href="#">Something else here</CDropdownItem>
765+
</CDropdownMenu>
766+
</CDropdown>
767+
<CDropdown :autoClose="false">
768+
<CDropdownToggle color="secondary">Manual close</CDropdownToggle>
769+
<CDropdownMenu>
770+
<CDropdownItem href="#">Action</CDropdownItem>
771+
<CDropdownItem href="#">Another action</CDropdownItem>
772+
<CDropdownItem href="#">Something else here</CDropdownItem>
773+
</CDropdownMenu>
774+
</CDropdown>
775+
</div>
776+
:::
777+
```vue
778+
<div class="d-flex gap-1">
779+
<CDropdown>
780+
<CDropdownToggle color="secondary">Default dropdown</CDropdownToggle>
781+
<CDropdownMenu>
782+
<CDropdownItem href="#">Action</CDropdownItem>
783+
<CDropdownItem href="#">Another action</CDropdownItem>
784+
<CDropdownItem href="#">Something else here</CDropdownItem>
785+
</CDropdownMenu>
786+
</CDropdown>
787+
<CDropdown autoClose="inside">
788+
<CDropdownToggle color="secondary">Clickable inside</CDropdownToggle>
789+
<CDropdownMenu>
790+
<CDropdownItem href="#">Action</CDropdownItem>
791+
<CDropdownItem href="#">Another action</CDropdownItem>
792+
<CDropdownItem href="#">Something else here</CDropdownItem>
793+
</CDropdownMenu>
794+
</CDropdown>
795+
<CDropdown autoClose="outside">
796+
<CDropdownToggle color="secondary">Clickable outside</CDropdownToggle>
797+
<CDropdownMenu>
798+
<CDropdownItem href="#">Action</CDropdownItem>
799+
<CDropdownItem href="#">Another action</CDropdownItem>
800+
<CDropdownItem href="#">Something else here</CDropdownItem>
801+
</CDropdownMenu>
802+
</CDropdown>
803+
<CDropdown :autoClose="false">
804+
<CDropdownToggle color="secondary">Manual close</CDropdownToggle>
805+
<CDropdownMenu>
806+
<CDropdownItem href="#">Action</CDropdownItem>
807+
<CDropdownItem href="#">Another action</CDropdownItem>
808+
<CDropdownItem href="#">Something else here</CDropdownItem>
809+
</CDropdownMenu>
810+
</CDropdown>
811+
</div>
812+
```
813+
683814
## Customizing
684815

685816
### CSS variables

0 commit comments

Comments
 (0)