@coreui/vue-pro
Version:
UI Components Library for Vue.js
213 lines (194 loc) • 5.57 kB
text/typescript
import { cloneVNode, defineComponent, h, PropType, ref, watch } from 'vue'
import { createPopper } from '@popperjs/core'
import { CConditionalTeleport } from '../conditional-teleport'
import { CFocusTrap } from '../focus-trap'
import { isRTL } from '../../utils'
const CPicker = defineComponent({
name: 'CPicker',
props: {
/**
* Set container type for the component.
*/
container: {
type: String as PropType<'dropdown' | 'inline'>,
default: 'dropdown',
},
/**
* Toggle the disabled state for the component.
*/
disabled: Boolean,
/**
* A string of all className you want applied to the dropdown menu.
*/
dropdownClassNames: String,
/**
* Toggle visibility of footer element or set the content of footer.
*/
footer: Boolean,
/**
* Generates dropdown menu using Teleport.
*
* @since 5.8.0
*/
teleport: {
type: [Boolean],
default: false,
},
/**
* Toggle the visibility of the component.
*/
visible: Boolean,
},
emits: [
/**
* Callback fired when the component requests to be hidden.
*/
'hide',
/**
* Callback fired when the component requests to be shown.
*/
'show',
],
setup(props, { attrs, emit, slots }) {
const pickerRef = ref()
const dropdownRef = ref()
const togglerRef = ref()
const popper = ref()
const visible = ref(props.visible)
watch(
() => props.visible,
() => {
visible.value = props.visible
}
)
watch(visible, () => {
if (props.container === 'inline') {
return
}
if (visible.value) {
emit('show')
window.addEventListener('mouseup', handleMouseUp)
window.addEventListener('keyup', handleKeyUp)
initPopper()
return
}
emit('hide')
window.removeEventListener('mouseup', handleMouseUp)
window.removeEventListener('keyup', handleKeyUp)
destroyPopper()
})
const initPopper = () => {
if (togglerRef.value && dropdownRef.value) {
popper.value = createPopper(togglerRef.value, dropdownRef.value, {
placement: isRTL(pickerRef.value) ? 'bottom-end' : 'bottom-start',
modifiers: [
{
name: 'preventOverflow',
options: {
boundary: 'clippingParents',
},
},
{
name: 'offset',
options: {
offset: [0, 2],
},
},
],
})
}
}
const destroyPopper = () => {
if (popper.value) {
popper.value.destroy()
}
popper.value = undefined
}
const handleKeyUp = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
visible.value = false
}
}
const handleMouseUp = (event: Event) => {
if (pickerRef.value && pickerRef.value.contains(event.target as HTMLElement)) {
return
}
if (dropdownRef.value && dropdownRef.value.contains(event.target as HTMLElement)) {
return
}
visible.value = false
}
switch (props.container) {
case 'inline': {
return () => h('div', { class: 'picker', ref: pickerRef }, slots.default && slots.default())
}
default: {
return () =>
h(
CFocusTrap,
{ active: visible.value, ...(props.teleport && { additionalContainer: dropdownRef }) },
() =>
h(
'div',
{
class: [
attrs.class,
{
show: visible.value,
},
],
ref: pickerRef,
},
[
/**
* @slot Location for the toggler element.
*/
slots.toggler &&
slots.toggler().map((slot) =>
cloneVNode(slot, {
onClick: () => {
if (!props.disabled && !visible.value) {
visible.value = true
}
},
ref: (el) => {
togglerRef.value = el
},
})
),
h(
CConditionalTeleport,
{
teleport: props.teleport,
},
{
default: () =>
h(
'div',
{
class: [
props.dropdownClassNames,
{
show: props.teleport && visible.value,
},
],
ref: dropdownRef,
},
[
slots.default && slots.default(),
/**
* @slot Location for the footer element.
*/
props.footer && slots.footer && slots.footer(),
]
),
}
),
]
)
)
}
}
},
})
export { CPicker }