magiccube-vue3
Version:
vue3-js版组件库
211 lines (184 loc) • 6.49 kB
JavaScript
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 }