Skip to content

Commit 1154cd2

Browse files
committed
feat(CDropdown): add reference prop for custom positioning targets
1 parent ff2f78a commit 1154cd2

File tree

5 files changed

+87
-25
lines changed

5 files changed

+87
-25
lines changed

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

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { defineComponent, h, ref, provide, watch, PropType, onUnmounted, nextTick } from 'vue'
1+
import { defineComponent, h, nextTick, onUnmounted, provide, PropType, ref, Ref, watch } from 'vue'
22
import type { Placement } from '@popperjs/core'
33

44
import { usePopper } from '../../composables'
55
import type { Triggers } from '../../types'
66
import { getNextActiveElement, isRTL } from '../../utils'
77

88
import type { Alignments } from './types'
9-
import { getPlacement } from './utils'
9+
import { getPlacement, getReferenceElement } from './utils'
1010
import { CFocusTrap } from '../focus-trap'
1111

1212
const CDropdown = defineComponent({
@@ -113,6 +113,21 @@ const CDropdown = defineComponent({
113113
type: Boolean,
114114
default: true,
115115
},
116+
/**
117+
* Sets the reference element for positioning the Vue Dropdown Menu.
118+
* - `toggle` - The Vue Dropdown Toggle button (default).
119+
* - `parent` - The Vue Dropdown wrapper element.
120+
* - `HTMLElement` - A custom HTML element.
121+
* - `Ref` - A custom reference element.
122+
*
123+
* @since 5.7.0
124+
*/
125+
reference: {
126+
type: [String, Object] as PropType<
127+
'parent' | 'toggle' | HTMLElement | Ref<HTMLElement | null>
128+
>,
129+
default: 'toggle',
130+
},
116131
/**
117132
* Generates dropdown menu using Teleport.
118133
*
@@ -157,8 +172,9 @@ const CDropdown = defineComponent({
157172
'show',
158173
],
159174
setup(props, { slots, emit }) {
160-
const dropdownToggleRef = ref()
161-
const dropdownMenuRef = ref()
175+
const dropdownRef = ref<HTMLElement | null>(null)
176+
const dropdownMenuRef = ref<HTMLElement | null>(null)
177+
const dropdownToggleRef = ref<HTMLElement | null>(null)
162178
const pendingKeyDownEventRef = ref<KeyboardEvent | null>(null)
163179
const popper = ref(typeof props.alignment === 'object' ? false : props.popper)
164180
const visible = ref(props.visible)
@@ -191,8 +207,13 @@ const CDropdown = defineComponent({
191207

192208
watch(visible, () => {
193209
if (visible.value && dropdownToggleRef.value && dropdownMenuRef.value) {
194-
if (popper.value) {
195-
initPopper(dropdownToggleRef.value, dropdownMenuRef.value, popperConfig)
210+
const referenceElement = getReferenceElement(
211+
props.reference,
212+
dropdownToggleRef,
213+
dropdownRef
214+
)
215+
if (referenceElement && popper.value) {
216+
initPopper(referenceElement, dropdownMenuRef.value, popperConfig)
196217
}
197218

198219
window.addEventListener('click', handleClick)
@@ -334,6 +355,7 @@ const CDropdown = defineComponent({
334355
? 'dropup dropup-center'
335356
: props.direction,
336357
],
358+
ref: dropdownRef,
337359
},
338360
slots.default && slots.default()
339361
)

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Ref } from 'vue'
12
import type { Placement } from '@popperjs/core'
23
import type { Placements } from '../../types'
34
import type { Alignments, Breakpoints } from './types'
@@ -49,3 +50,23 @@ export const getPlacement = (
4950

5051
return _placement
5152
}
53+
54+
export const getReferenceElement = (
55+
reference: 'parent' | 'toggle' | Ref<HTMLElement | null> | HTMLElement,
56+
dropdownToggleRef: Ref<HTMLElement | null>,
57+
dropdownRef: Ref<HTMLElement | null>
58+
): HTMLElement | null => {
59+
if (reference === 'parent') {
60+
return dropdownRef.value
61+
}
62+
63+
if (reference instanceof HTMLElement) {
64+
return reference
65+
}
66+
67+
if (reference instanceof Object && 'value' in reference) {
68+
return reference.value
69+
}
70+
71+
return dropdownToggleRef.value
72+
}

0 commit comments

Comments
 (0)