diff --git a/.eslintrc.js b/.eslintrc.js index 3da2f8038e830ccdbd9d559118c4e74b0baf58b9..95881caded69522f82da79944aa6219b673ac879 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,3 +1,4 @@ + module.exports = { parser: 'vue-eslint-parser', parserOptions: { diff --git a/devui/toast/src/toast.tsx b/devui/toast/src/toast.tsx index 5f925c361d101e215315cfc73b9511178e522edf..a056b7c4d947c05fedff5ca79f97ba2df458692d 100644 --- a/devui/toast/src/toast.tsx +++ b/devui/toast/src/toast.tsx @@ -1,3 +1,4 @@ + import './toast.scss' import { computed, defineComponent, nextTick, onUnmounted, ref, watch } from 'vue' diff --git a/devui/tooltip/index.ts b/devui/tooltip/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..b5b67564f8f0347b69a4785e20508267d36946b6 --- /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 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..0d203d2b3b64b7f27d83256da70132293932a98f --- /dev/null +++ b/devui/tooltip/src/tooltip-types.ts @@ -0,0 +1,31 @@ +import type { ExtractPropTypes } from 'vue' + +export type DevTooltip = 'top' | 'right' | 'bottom' | 'left'; +export const DevTooltipShowAnimation = true; + +export const tooltipProps = { + /* test: { + type: Object as PropType<{ xxx: xxx }> + } */ + 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..d123bbe24214df918249b2dc1d90f3bbce82ca17 --- /dev/null +++ b/devui/tooltip/src/tooltip.scss @@ -0,0 +1,23 @@ +.d-tooltip { + box-sizing: border-box; + // position: relative; + .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..0f4231cd656adcaedcfde203aff428ee875c6a6e --- /dev/null +++ b/devui/tooltip/src/tooltip.tsx @@ -0,0 +1,165 @@ +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 (){ + // show.value = false + 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} +
+
{/** tooltip 提示框 */} +
{/** tooltip 提示框箭头 */} +
{/** tooltip提示的内容 */} + {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..770d55d477ee96e8f24c5299ea014aca9dedb7a9 --- /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