新增input-number(数字输入框)、tooltip(文字提示)、修改input组件可接受number类型值

This commit is contained in:
xumi
2021-12-12 17:39:02 +08:00
parent e545b9221a
commit 961eac0127
17 changed files with 857 additions and 1 deletions

View File

@@ -0,0 +1,50 @@
import { Ref } from "vue";
// 计算各个方向位置
const postionFns: any = {
top(el: HTMLElement, popper: HTMLElement, innnerPosition: Ref, called: boolean) {
let {top, left, bottom} = el.getBoundingClientRect()
if ((top = top - popper.offsetHeight - 6) < 0 && bottom > popper.offsetHeight) {
innnerPosition.value = 'bottom';
top = bottom;
}
return {
top: `${top}px`,
left: `${left - (popper.offsetWidth - el.offsetWidth) / 2}px`
}
},
bottom(el: HTMLElement, popper: HTMLElement, innnerPosition: Ref, called: boolean) {
let { top, left, bottom } = el.getBoundingClientRect()
if (window.innerHeight - bottom < popper.offsetHeight + 6) {
innnerPosition.value = 'top';
bottom = top - popper.offsetHeight - 6;
}
return {
top: `${bottom}px`,
left: `${left - (popper.offsetWidth - el.offsetWidth) / 2}px`
}
},
left(el: HTMLElement, popper: HTMLElement, innnerPosition: Ref, called : boolean) {
let {top, left, right} = el.getBoundingClientRect()
left = left - popper.offsetWidth - 6;
if (left < 0) {
innnerPosition.value = 'right';
left = right;
}
return {
top: `${top - Math.abs(popper.offsetHeight - el.offsetHeight) / 2}px`,
left: `${left}px`
}
},
right(el: HTMLElement, popper: HTMLElement, innnerPosition: Ref, called: boolean) {
let { top, left, right } = el.getBoundingClientRect()
if (window.innerWidth < right + popper.offsetWidth + 6) {
innnerPosition.value = 'left';
right = left - popper.offsetWidth - 6;
}
return {
top: `${top - Math.abs(popper.offsetHeight - el.offsetHeight) / 2}px`,
left: `${right}px`
}
}
}
export default postionFns;

View File

@@ -0,0 +1,100 @@
// 主题颜色
// 浅色 --> 默认使用
@ligh-background: #FFF;
@ligh-color: #3a3a3a;
// 深色
@dark-background: #353535;
@dark-color: #FFF;
@border-clor: #cecece;
// 单一设置主题
.single-theme(@position, @contrary_position, @margin_postion, @color) {
@attr : ~'[position=@{position}]';
&.layui-popper@{attr}{
margin-@{contrary_position}: 6px;
.layui-popper-arrow {
@{contrary_position}: -6px;
border-@{contrary_position}-width: 0;
border-@{position}-color: @border-clor;
&::after{
@{contrary_position}: 1px;
border-@{contrary_position}-width: 0;
margin-@{margin_postion}: -6px;
border-@{position}-color: @color;
}
}
}
}
// 统一设置四个方向的主题
.theme(@background-color, @color) {
background-color: @background-color;
color: @color;
.single-theme(top, bottom, left, @background-color);
.single-theme(bottom, top, left, @background-color);
.single-theme(right, left, top, @background-color);
.single-theme(left, right, top, @background-color);
}
// 箭头默认居中
.arrow-default-center(@position, @prop) {
@attr : ~'[position=@{position}]';
&.layui-popper@{attr} {
.layui-popper-arrow{
@{prop}: -moz-calc(50% - 6px);
@{prop}: -webkit-calc(50% - 6px);
@{prop}: calc(50% - 6px);
}
}
}
.all-arrow-default-center() {
.arrow-default-center(top, left);
.arrow-default-center(bottom, left);
.arrow-default-center(left, top);
.arrow-default-center(right, top);
}
// 样式开始
.layui-popper {
position: fixed;
padding: 10px;
border-radius: 3px;
word-wrap: break-word;
min-width: 12px;
min-height: 12px;
font-size: 14px;
box-sizing: border-box;
border: 1px solid @border-clor;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.15);
.theme(@ligh-background, @ligh-color);
max-width: 300px;
z-index: 9999;
// 箭头默认居中
.all-arrow-default-center();
.layui-popper-arrow {
&,&::after{
position: absolute;
display: block;
width: 0;
height: 0;
border-width: 6px;
border-style: solid;
border-color: transparent;
}
&::after{
content: ' ';
}
}
/* 深色主题 */
&.layui-dark {
.theme(@dark-background, @dark-color);
border: 0;
}
}

View File

@@ -0,0 +1,89 @@
<template>
<transition v-show="visible">
<div :class="['layui-popper', {'layui-dark' : isDark}]" :style="style" :position="innnerPosition">
<slot>{{content}}</slot>
<div class="layui-popper-arrow"></div>
</div>
</transition>
</template>
<script lang="ts">
const NAME = "LayPopper";
export default {
name: NAME
}
</script>
<script setup lang="ts">
import "./index.less";
import postionFns from "./calcPosition";
import { getCurrentInstance, CSSProperties, ref, watch, onUpdated, defineEmits, onMounted} from "vue";
import {on} from "../../tools/domUtil";
const props = withDefaults(
defineProps<{
el : any,
content ?: string,
position ?: string,
trigger ?: string,
enterable ?: boolean,
isDark ?: boolean,
modelValue ?: boolean
}>(),
{
position : 'top',
enterable : true,
isDark : false,
trigger : 'hover',
modelValue : true
}
);
const EVENT_MAP : any = {
'hover' : ['mouseenter', null, 'mouseleave', false],
'click' : ['click', document, 'click', true]
}
const triggerArr = EVENT_MAP[props.trigger];
if (!triggerArr) {
console.error(`${NAME} render error!cause: 'Trigger' must be 'hover/click' `)
}
const style = ref<CSSProperties>({top: (-window.innerHeight) + 'px',left:0});
const visible = ref(props.modelValue);
const checkTarget = ref(false);
const popper = ref();
const innnerPosition = ref(props.position);
const emit = defineEmits(['update:modelValue'])
watch(visible, (val)=>{
emit('update:modelValue', val);
})
const doShow = function(){
visible.value = true;
}
const doHidden = function(e : MouseEvent){
if ((checkTarget.value && props.el.contains(e.target)) || (props.enterable && popper.value.contains(e.target))) return;
style.value = {top: (-window.innerHeight) + 'px',left:0};
visible.value = false;
innnerPosition.value = props.position;
}
// 事件绑定
on(props.el, triggerArr[0], doShow);
on(triggerArr[1]??props.el, triggerArr[2], doHidden);
checkTarget.value = triggerArr[3];
// 计算位置显示
const showPosistion = function(){
postionFns[props.position] && (style.value = postionFns[props.position](props.el, popper.value, innnerPosition));
}
// 节点变化, 获取当前实例对应的dom, 如果为显示状态,则计算位置显示
const nodeChange = function(){
popper.value = getCurrentInstance()?.vnode.el;
visible.value && (popper.value.offsetWidth === 0 ? setTimeout(showPosistion, 0) : showPosistion());
}
onMounted(nodeChange)
onUpdated(nodeChange)
</script>

View File

@@ -0,0 +1,26 @@
import { h, render} from "vue";
import popper from "./index.vue";
import { once } from "../../tools/domUtil";
const EVENT_MAP : any = {
'hover': 'mouseenter',
'click': 'click'
}
const usePopper = {
createPopper(el: HTMLElement, props: any, trigger : string) {
const _this = this;
once(el, EVENT_MAP[trigger], () => {
const _props = {...props};
_props.el = el;
_this.renderPopper(_props);
})
},
renderPopper(props: any) {
const container: HTMLDivElement = document.createElement("div");
// container.setAttribute("class", "lay-div");
const node = h(popper, props);
render(h(popper, props), container);
container.firstElementChild && document.body.appendChild(container.firstElementChild);
return node;
}
}
export default usePopper;