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..52dd4685c2fdfc78d0aab1d695f07f908f705c32 --- /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..625fada9d01e8595fb845a265aeb46465082e4cf --- /dev/null +++ b/devui/tooltip/src/tooltip.tsx @@ -0,0 +1,168 @@ +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 delayShowTrue = function (fn, delay){ + 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){ + 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; + }); + + // 注册鼠标移除事件 + leaveEvent = EventListener.listen(slotElement.value.children[0], 'mouseleave', function (){ + show.value = false; + }); + }); + + watch(show, function (newValue, oldValue){ + if (newValue){ + // 鼠标悬浮为true,显示提示框 + tooltip.value.style.opacity = '1'; + tooltip.value.style.zIndex = '500'; + // 具体的判定规则 + 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; + // 设置箭头的样式 + arrow.value.style.borderLeft = '10px solid transparent'; + arrow.value.style.borderRight = '10px solid transparent'; + arrow.value.style.borderBottom = '10px solid transparent'; + arrow.value.style.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; + // 设置箭头的样式 + arrow.value.style.borderLeft = '10px solid transparent'; + arrow.value.style.borderRight = '10px solid cornflowerblue'; + arrow.value.style.borderBottom = '10px solid transparent'; + arrow.value.style.borderTop = '10px solid transparent'; + 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的样式 + arrow.value.style.borderBottom = '10px solid orange'; + arrow.value.style.borderRight = '10px solid transparent'; + arrow.value.style.borderTop = '10px solid transparent'; + arrow.value.style.borderLeft = '10px solid transparent'; + 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的样式 + arrow.value.style.borderLeft = '10px solid cornflowerblue'; + arrow.value.style.borderRight = '10px solid transparent'; + arrow.value.style.borderBottom = '10px solid transparent'; + arrow.value.style.borderTop = '10px solid transparent'; + 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..724a31f607b14a42a50ef6ef75ee3dcd089d8ee6 --- /dev/null +++ b/devui/tooltip/utils/EventListener.js @@ -0,0 +1,23 @@ +const EventListener = { + listen: function (target, eventType, callback) { + console.log('target:', target); + console.log('eventType:', eventType); + 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..d055d230121234a7f8ac63f62ba4f8795bb62c7e --- /dev/null +++ b/sites/components/tooltip/index.md @@ -0,0 +1,63 @@ +# Tooltip 提示 + +文字提示组件。 + +### 何时使用 + +用户鼠标移动到文字上,需要进一步的提示时使用。 + +### 基本用法 + + + + + + + + + +```html +
+ + + + + + + + + + + + +
+``` + +```scss +.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; + } + } +} +``` + +### 延时触发 +