修复 src 结构

This commit is contained in:
就眠儀式
2022-01-22 21:30:17 +08:00
parent 96bb84020b
commit d0debbb1b2
186 changed files with 251 additions and 251 deletions

View File

@@ -0,0 +1,73 @@
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 - (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 - (popper.offsetHeight - el.offsetHeight) / 2}px`,
left: `${right}px`,
};
},
};
export default postionFns;

View File

@@ -0,0 +1,126 @@
// 主题颜色
// 浅色 --> 默认使用
@ligh-background: #FFF;
@ligh-color: #3a3a3a;
// 深色
@dark-background: #353535;
@dark-color: #FFF;
@border-clor: #cecece;
// 单一设置主题
.single-theme(@position, @contrary_position, @margin_postion, @color, @m-border-color) {
@attr : ~'[position=@{position}]';
&{
border: 1px solid @m-border-color;
&@{attr}{
margin-@{contrary_position}: 6px;
.layui-popper-arrow {
@{contrary_position}: -6px;
border-@{contrary_position}-width: 0;
border-@{position}-color: @m-border-color;
&::after{
@{contrary_position}: 1px;
border-@{contrary_position}-width: 0;
margin-@{margin_postion}: -6px;
border-@{position}-color: @color;
}
}
}
}
}
// 统一设置四个方向的主题
.theme(@background-color, @color, @border-color) {
background-color: @background-color;
color: @color;
.single-theme(top, bottom, left, @background-color, @border-color);
.single-theme(bottom, top, left, @background-color, @border-color);
.single-theme(right, left, top, @background-color, @border-color);
.single-theme(left, right, top, @background-color, @border-color);
}
// 箭头默认居中
.arrow-default-center(@position, @prop) {
@attr : ~'[position=@{position}]';
&@{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);
}
// 填充popper支持可以移动到popper使用到
.single-fill-popper(@position, @contrary_position, @zeroPosition, @all, @seven){
@attr : ~'[position=@{position}]';
&@{attr}::after{
@{contrary_position}: -7px;
@{zeroPosition}: 0;
@{all}: 100%;
@{seven}: 7px;
}
}
.fill-popper(){
.single-fill-popper(top, bottom, left, width, height);
.single-fill-popper(bottom, top, left, width, height);
.single-fill-popper(left, right, bottom, height, width);
.single-fill-popper(right, left, bottom, height, width);
}
// 样式开始
.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;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.15);
.theme(@ligh-background, @ligh-color, @border-clor);
max-width: 300px;
z-index: 9999;
// 箭头默认居中
.all-arrow-default-center();
&::after{
content: " ";
position: absolute;
display: block;
}
// 填充
.fill-popper();
.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, @dark-background);
}
}

View File

@@ -0,0 +1,140 @@
<template>
<teleport to="body" v-if="isExist">
<transition v-show="innerVisible">
<div
ref="popper"
:class="['layui-popper', { 'layui-dark': isDark }]"
:style="style"
:position="position"
>
<slot>{{ content }}</slot>
<div class="layui-popper-arrow"></div>
</div>
</transition>
</teleport>
</template>
<script lang="ts">
const NAME = "LayPopper";
export default {
name: NAME,
};
</script>
<script setup lang="ts">
import "./index.less";
import postionFns from "./calcPosition";
import {
CSSProperties,
ref,
watch,
defineEmits,
onMounted,
} from "vue";
import { on } from "../../utils/domUtil";
const props = withDefaults(
defineProps<{
el: any;
content?: string | Number;
position?: string;
trigger?: string;
enterable?: boolean;
isDark?: boolean;
disabled?: boolean;
visible?: boolean;
isCanHide?: boolean;
}>(),
{
position: 'top',
isDark: true,
disabled: false,
enterable: true,
visible: true,
isCanHide: true,
trigger: "hover",
}
);
const emit = defineEmits(["update:visible"]);
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 checkTarget = ref(false);
const popper = ref<HTMLDivElement>({} as HTMLDivElement);
const innnerPosition = ref(props.position);
const innerVisible = ref(props.visible ?? true);
const isExist = ref(false);
watch(innerVisible, (val) => {
invokeShowPosistion();
emit("update:visible", val);
});
watch(popper, (val) => {
if (props.trigger === 'hover' && props.enterable) {
on(popper.value, EVENT_MAP['hover'][0], doShow);
on(popper.value, EVENT_MAP['hover'][2], doHidden);
}
});
watch(
() => props.content,
(val) => {
innerVisible.value && invokeShowPosistion();
}
);
const doShow = function () {
if (!props.disabled) {
innerVisible.value = true;
isExist.value = true;
}
};
const doHidden = function (e: MouseEvent) {
// ||(props.enterable && popper.value.contains(e.target as Node))
if (
(checkTarget.value && props.el.contains(e.target))
)
return;
// style.value = {top: (-window.innerHeight) + 'px',left:0};
// popper.value.remove();
if (props.isCanHide !== false) {
innerVisible.value = false;
}
innnerPosition.value = props.position;
};
// 计算位置显示
const showPosistion = function () {
postionFns[props.position] &&
(style.value = postionFns[props.position](
props.el,
popper.value,
innnerPosition
));
};
const invokeShowPosistion = function () {
if (innerVisible.value) {
popper.value.offsetWidth === 0
? setTimeout(showPosistion, 0)
: showPosistion();
// 延时确保计算位置正确
setTimeout(() => innerVisible.value && showPosistion(), 2);
}
};
// 事件绑定
on(props.el, triggerArr[0], doShow);
on(triggerArr[1] ?? props.el, triggerArr[2], doHidden);
checkTarget.value = triggerArr[3];
onMounted(() => {
invokeShowPosistion();
});
</script>