UNPKG

magiccube-vue3

Version:

vue3-js版组件库

211 lines (184 loc) 6.49 kB
import { ref, onUnmounted, Teleport, getCurrentInstance } from 'vue' import * as utils from '../../utils/common' /** * @component tip组件 * 支持content属性传入内容(可以支持html标签) * 也可以支持#content插槽置入内容 * !如果同时有content属性和content插槽时,优先使用#content插槽 */ const Popup = { props: { data: String, background: { type: String, default: '#fff' }, textColor: { type: String, default: '#333' }, maxWidth: { type: [Number, String], default: 220 }, maxHeight: { type: [Number, String], default: 200 }, }, setup(props, { expose, slots }) { const uuid = utils._uuid() /* 动态锚点 */ const instance = getCurrentInstance() const globalOptions = instance.appContext?.config?.globalProperties?.$ELEMENT const TELEPORT_NAME = globalOptions.teleportName? `.${globalOptions.teleportName}` : 'body' const popupEl = ref(null) const elementStyle = ref({}) const visible = ref(false) const setVisible = (options) => { visible.value = true const { mouseLeft, mouseTop, targetWidth, targetHeight, position } = options const popupWidth = popupEl.value.offsetWidth const popupHeight = popupEl.value.offsetHeight const style = {} switch (position) { case 'top': { style['top'] = mouseTop - targetHeight - 10 + 'px' style['left'] = mouseLeft + (targetWidth / 2) - (popupWidth / 2) + 'px' break } case 'bottom': { style['top'] = mouseTop + targetHeight + 'px' style['left'] = mouseLeft + (targetWidth / 2) - (popupWidth / 2) + 'px' break } case 'left': { style['top'] = mouseTop + (targetHeight / 2) - (popupHeight / 2) + 'px' style['left'] = mouseLeft - popupWidth - 5 + 'px' break } case 'right': { style['top'] = mouseTop + (targetHeight / 2) - (popupHeight / 2) + 'px' style['left'] = mouseLeft + targetWidth + 5 + 'px' break } } elementStyle.value = style } const setInvisible = () => { visible.value = false } onUnmounted(() => { /** * 注销并移除下拉dom */ const target = document.getElementById(uuid) target && target.parentNode.removeChild(target) }) expose({ setVisible, setInvisible }) return () => ( <Teleport to={TELEPORT_NAME}> { // 如果有内容插槽则优先内容插槽 slots.content? ( <div id={uuid} class="mc-mop-tips-container__popup" ref={popupEl} style={{ ...elementStyle.value, opacity: visible.value ? 1 : 0, backgroundColor: props.background, color: props.textColor, maxWidth: props.maxWidth + 'px', maxHeight: props.maxHeight + 'px' }}> { slots.content && slots.content() } </div> ) : ( <span id={uuid} class="mc-mop-tips-container__popup" ref={popupEl} style={{ ...elementStyle.value, opacity: visible.value ? 1 : 0, backgroundColor: props.background, color: props.textColor, maxWidth: props.maxWidth + 'px', maxHeight: props.maxHeight + 'px' }} v-html={props.data}> </span> ) } </Teleport> ) } } const Tip = { name: 'McTip', props: { content: String, position: { type: String, default: 'top' }, background: { type: String, default: '#fff' }, textColor: { type: String, default: '#333' }, maxWidth: { type: [Number, String], default: 220 }, maxHeight: { type: [Number, String], default: 200 }, }, setup(props, { slots }) { const targetEl = ref(null) const popupEl = ref(null) const handleMouseenter = (event) => { const options = { position: props.position, mouseTop: event.clientY - event.offsetY, mouseLeft: event.clientX - event.offsetX, targetWidth: targetEl.value.offsetWidth, targetHeight: targetEl.value.offsetHeight, } popupEl.value.setVisible(options) } const handleMouseleave = () => { popupEl.value.setInvisible() } return () => ( <div class="mc-tip" ref={targetEl} onMouseenter={handleMouseenter} onMouseleave={handleMouseleave}> {slots.default ? slots.default() : ''} <Popup ref={popupEl} background={props.background} textColor={props.textColor} maxWidth={props.maxWidth} maxHeight={props.maxHeight} data={props.content} v-slots={{ content: slots.content }} /> </div> ) } } Tip.install = (app) => { app.component(Tip.name, Tip) } const McTip = Tip export { McTip, McTip as default }