diff --git a/devui/tooltip/index.ts b/devui/tooltip/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..b5c96f4db064987a842fb0ad212550f6f8538db9 --- /dev/null +++ b/devui/tooltip/index.ts @@ -0,0 +1,16 @@ +import type { App } from 'vue' +import Tooltip from './src/tooltip' + +Tooltip.install = function(app: App) { + app.component(Tooltip.name, Tooltip) +} + +export { Tooltip } + +export default { + title: 'Tooltip提示', + category: '反馈', + install(app: App): void { + app.use(Tooltip as any) + } +} diff --git a/devui/tooltip/src/tooltip-types.ts b/devui/tooltip/src/tooltip-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc5fd184406152fd597681e0d299f41fff8a01b4 --- /dev/null +++ b/devui/tooltip/src/tooltip-types.ts @@ -0,0 +1,28 @@ +import type { ExtractPropTypes } from 'vue' + +export type DevTooltip = 'top' | 'right' | 'bottom' | 'left'; +export const DevTooltipShowAnimation = true; + +export const tooltipProps = { + position: { + type: String, + default: 'top' + }, + showAnimation: { + type: Boolean, + default: true + }, + content: { + type: String + }, + mouseLeaveDelay: { + type: String, + default: '150' + }, + mouseEnterDelay: { + type: String, + default: '100' + } +} as const + +export type TooltipProps = ExtractPropTypes diff --git a/devui/tooltip/src/tooltip.scss b/devui/tooltip/src/tooltip.scss new file mode 100644 index 0000000000000000000000000000000000000000..4b2186b137dd3b3e21ec84fc8021389cd62fc802 --- /dev/null +++ b/devui/tooltip/src/tooltip.scss @@ -0,0 +1,22 @@ +.d-tooltip { + box-sizing: border-box; + .tooltip { + box-sizing: border-box; + position: absolute; + width: fit-content; + transition: all 0.5s; + .arrow { + width: 0; + height: 0; + position: absolute; + } + .tooltipcontent { + box-sizing: border-box; + padding: 10px; + margin-left: 10px; + width: fit-content; + background-color: rgb(51, 51, 51); + color: #fff; + } + } +} diff --git a/devui/tooltip/src/tooltip.tsx b/devui/tooltip/src/tooltip.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b169d3cbeb82efd223db9b39f68ef4a6b46f8dfa --- /dev/null +++ b/devui/tooltip/src/tooltip.tsx @@ -0,0 +1,159 @@ +import './tooltip.scss' +import { defineComponent, reactive, ref, watch, onMounted, getCurrentInstance, onBeforeUnmount, renderSlot, useSlots} from 'vue' +import { tooltipProps } from './tooltip-types' +import EventListener from '../utils/EventListener' + +export default defineComponent({ + name: 'DTooltip', + props: tooltipProps, + setup(props, ctx){ + let position = reactive({ + left: 0, + top: 0 + }) + let show = ref(false) + + // slotElement元素的ref + let slotElement = ref(null) + // tooltip元素的引用 + let tooltip = ref(null) + // arrow元素的引用 + let arrow = ref(null) + // tooltipcontent的引用 + let tooltipcontent = ref(null) + + let enterEvent + let leaveEvent + + const arrowStyle = (attr, value)=>{ + arrow.value.style[attr] = value + } + + // 延迟显示 + const delayShowTrue = function (fn, delay=props.mouseEnterDelay){ + let start + if (parseInt(delay)>=0){ + return function (){ + if (start){ + clearTimeout(start) + } + start = setTimeout(fn, parseInt(delay)) + } + } else{ + console.error('the value of delay is bigger than 0 and the type of delay must be string!') + return + } + } + // 延迟消失 + const delayShowFalse = function (fn, delay=props.mouseLeaveDelay){ + if (show.value && parseInt(delay) >= 0){ + setTimeout(fn, parseInt(delay)) + } + } + + onMounted(()=>{ + // 组件初始化不渲染tooltip + if (!show.value){ + tooltip.value.style.opacity = '0' + } + + // 注册鼠标引入事件 + /*enterEvent = EventListener.listen(slotElement.value.children[0], 'mouseenter', function (){ + show.value = true + })*/ + enterEvent = EventListener.listen(slotElement.value.children[0], 'mouseenter', delayShowTrue(function (){ + show.value = true + }, props.mouseEnterDelay)) + + // 注册鼠标移除事件 + leaveEvent = EventListener.listen(slotElement.value.children[0], 'mouseleave', function (){ + setTimeout(function (){ + show.value = false + }, props.mouseLeaveDelay) + }) + }) + + watch(show, function (newValue, oldValue){ + if (newValue){ + // 鼠标悬浮为true,显示提示框 + tooltip.value.style.opacity = '1' + tooltip.value.style.zIndex = '999' + arrow.value.style.border = '10px solid transparent' + // 具体的判定规则 + switch (props.position){ + case 'top': + // 设置 tooltip 内容的样式 + position.left = slotElement.value.children[0].offsetLeft - tooltip.value.offsetWidth / 2 + slotElement.value.children[0].offsetWidth / 2 + position.top = slotElement.value.children[0].offsetTop - 10 - tooltipcontent.value.offsetHeight + // 设置箭头的样式 + arrowStyle('borderTop', '10px solid cornflowerblue') + arrow.value.style.top = `${tooltipcontent.value.offsetHeight}px` + arrow.value.style.left = `${tooltipcontent.value.offsetWidth/2 - 5}px` + break + + case 'right': + // 设置tooltip 内容的样式 + position.left = slotElement.value.children[0].offsetLeft + slotElement.value.children[0].offsetWidth + position.top = slotElement.value.children[0].offsetTop + slotElement.value.children[0].offsetHeight/2 - tooltipcontent.value.offsetHeight/2 + // 设置箭头的样式 + arrowStyle('borderRight', '10px solid cornflowerblue') + arrow.value.style.top = `${tooltipcontent.value.offsetHeight/2 - 10}px` + arrow.value.style.left = '-10px' + break + + case 'bottom': + // 设置tooltip的样式 + position.top = slotElement.value.children[0].offsetHeight + slotElement.value.children[0].offsetTop + 10 + position.left = slotElement.value.children[0].offsetLeft + slotElement.value.children[0].offsetWidth/2 - tooltipcontent.value.offsetWidth/2 + // 设置arrow.value的样式 + arrowStyle('borderBottom', '10px solid cornflowerblue') + arrow.value.style.top = '-20px' + arrow.value.style.left = `${tooltipcontent.value.offsetWidth/2 - 10}px` + break + + case 'left': + position.top = slotElement.value.children[0].offsetTop + slotElement.value.children[0].offsetHeight/2 - tooltipcontent.value.offsetHeight/2 + position.left = slotElement.value.children[0].offsetLeft - 20 - tooltipcontent.value.offsetWidth + // 设置arrow.value的样式 + arrowStyle('borderLeft', '10px solid cornflowerblue') + arrow.value.style.left = `${tooltipcontent.value.offsetWidth + 10}px` + arrow.value.style.top = `${tooltipcontent.value.offsetHeight/2 - 10}px` + break + + default: + console.error('The attribute position value is wrong, the value is one of top、right、left、bottom') + break + } + tooltip.value.style.top = position.top + 'px' + tooltip.value.style.left = position.left + 'px' + } else { + position.top = 0 + position.left = 0 + // 鼠标移走为false,隐藏提示框 + tooltip.value.style.opacity = '0' + } + }) + + onBeforeUnmount (()=>{ + enterEvent.remove() + leaveEvent.remove() + }) + + return ()=>{ + const defaultSlot = renderSlot(useSlots(), 'default') + return ( +
+
+ {defaultSlot} +
+
+
+
+ {props.content} +
+
+
+ ) + } + } +}) \ No newline at end of file diff --git a/devui/tooltip/utils/EventListener.js b/devui/tooltip/utils/EventListener.js new file mode 100644 index 0000000000000000000000000000000000000000..73f6f68c535deb2a2001bca79a2c0916460fd1f2 --- /dev/null +++ b/devui/tooltip/utils/EventListener.js @@ -0,0 +1,21 @@ +const EventListener = { + listen: function (target, eventType, callback) { + if (target.addEventListener){ + target.addEventListener(eventType, callback, false); + return { + remove (){ + target.removeEventListener(target, callback, false); + } + } + } else { + target.attchEvent(eventType, callback); + return { + remove (){ + target.detachEvent(eventType, callback); + } + }; + } + } +}; + +export default EventListener; \ No newline at end of file diff --git a/sites/components/tabs/index.md b/sites/components/tabs/index.md index 1fe147671caabaffff282bdff74f4af2dce92a39..f318f6d26e93bf658622729f447360e622ac6f74 100644 --- a/sites/components/tabs/index.md +++ b/sites/components/tabs/index.md @@ -5,3 +5,4 @@ ### 何时使用 用户需要通过平级的区域将大块内容进行收纳和展现,保持界面整洁。 + diff --git a/sites/components/toast/index.md b/sites/components/toast/index.md index 2218666a7c7d57731ec5fd533f2052446343b728..40b5474a85bb841ca0071d25ca9adc644cd19ba4 100644 --- a/sites/components/toast/index.md +++ b/sites/components/toast/index.md @@ -1,3 +1,4 @@ + # Toast 全局通知 全局信息提示组件。 diff --git a/sites/components/tooltip/index.md b/sites/components/tooltip/index.md new file mode 100644 index 0000000000000000000000000000000000000000..ee69846fcf23c66474a5ab306eb9511779e5880b --- /dev/null +++ b/sites/components/tooltip/index.md @@ -0,0 +1,90 @@ +# Tooltip 提示 + +文字提示组件。 + +### 何时使用 + +用户鼠标移动到文字上,需要进一步的提示时使用。 + +### 基本用法 + +:::demo我们可以通过控制属性`position`来控制tooltip的显示位置,`position`取值有4个,分别是`top`、`right`、`bottom`、`left`。通过属性`content`控制tooltip提示框的内容。 + + + + + + + + + +```vue + +``` + +```css +.example { + height: 50px; + width: 60px; + background: cornflowerblue; + margin-top: 30px; +} +``` +::: + +### 延时触发 + +鼠标移入的时长超过 [mouseEnterDelay] 毫秒之后才会触发,以防止用户无意划过导致的闪现,默认值是150毫秒;鼠标移出之后,再经过[mouseLeaveDelay]毫秒后,toolTip组件才会隐藏,默认值是100毫秒。 + +:::demo 通过`mouseEnterDelay`属性来控制tooltip提示框的`延迟显示`(默认是100ms),`mouseLeaveDelay`属性来控制tooltip提示框的`延迟消失`(默认是150ms) +
MouseEnter delay 500ms
+ +
MouseLeave delay 1000ms
+```vue + +``` + +```css +.customCss { + width: fit-content; + height: 30px; + padding: 10px; + display: flex; + justify-content: center; + align-items: center; + color: #fff; + background: cornflowerblue; +} +.customCss-leave { + width: fit-content; + height: 30px; + padding: 10px; + display: flex; + justify-content: center; + align-items: center; + color: #252b3a; + background: #fff; +} +``` +::: \ No newline at end of file