This commit is contained in:
Theluyuan 2022-11-15 08:28:03 +08:00
commit c892ebfd44
37 changed files with 5495 additions and 0 deletions

15
.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
.DS_Store
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn.lock
package-lock.json
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

97
README.md Normal file
View File

@ -0,0 +1,97 @@
## Introduction
<p>
<a href="https://www.npmjs.com/package/@layui/layer-vue"><img src="https://img.shields.io/npm/v/@layui/layer-vue.svg?sanitize=true"></a>
<a href="https://www.npmjs.com/package/@layui/layer-vue"><img src="https://img.shields.io/npm/l/@layui/layer-vue.svg?sanitize=true"></a>
<a href="https://travis-ci.org/sentsin/layui"><img src="https://img.shields.io/travis/sentsin/layui/master.svg"></a>
<a href="https://coveralls.io/r/sentsin/layui?branch=master"><img src="https://img.shields.io/coveralls/sentsin/layui/master.svg"></a>
</p>
**[🔶 Explore the docs »](http://layer.layui-vue.com)** **[Join us](https://jq.qq.com/?_wv=1027&k=ffiUQgnE)**
Layer vue is a web elastic layer component.
## Get Started
Use npm to install.
```bash
npm i @layui/layer-vue
```
Before using, you need to mount layui Vue to Vue and introduce index.css style file.
```
import App from './App';
import { createApp } from 'vue';
import layer from '@layui/layer-vue';
import '@layui/layer-vue/lib/index.css';
const app = createApp(App);
app.use(layer).mount('#app');
```
We support functional invocation:
```
<template>
<button type="button" @click="openSuccess">提示消息</button>
</template>
<script setup>
import { ref } from "vue";
import { layer } from "@layui/layer-vue";
const openSuccess = function () {
layer.msg("成功消息", {icon:1, time:1000});
};
</script>
```
Componentized calls are also supported:
```
<template>
<button @click="changeVisible">显示/隐藏</button>
<lay-layer v-model="visible">
content
</lay-layer>
</template>
<script setup>
import { ref } from "vue";
const visible = ref(false);
const changeVisible = () => {
visible.value = !visible.value;
}
</script>
```
## Open Source Licence
layui vue is licensed under the [MIT license](https://opensource.org/licenses/MIT).
```
The MIT License (MIT)
Copyright 2021 就眠儀式
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
```

1
lib/index.css Normal file

File diff suppressed because one or more lines are too long

2262
lib/layer-vue.es.js Normal file

File diff suppressed because one or more lines are too long

3
lib/layer-vue.umd.js Normal file

File diff suppressed because one or more lines are too long

38
package.json Normal file
View File

@ -0,0 +1,38 @@
{
"name": "@layui/layer-vue",
"version": "1.4.7",
"description": "a component library for Vue 3 base on layui-vue",
"homepage": "http://www.layui-vue.com",
"module": "lib/layer-vue.es.js",
"main": "lib/layer-vue.umd.js",
"exports": {
".": {
"import": "./lib/layer-vue.es.js",
"require": "./lib/layer-vue.umd.js"
},
"./lib/": "./lib/"
},
"types": "types/index.d.ts",
"style": "lib/index.css",
"scripts": {
"build": "vite build --emptyOutDir && npm run build:types",
"build:types": "rimraf types && tsc -d"
},
"keywords": [
"layui-vue",
"layui",
"vue"
],
"files": [
"lib",
"types"
],
"browserslist": [
"current node",
"last 2 versions and > 2%",
"ie > 10"
],
"devDependencies": {
"@babel/plugin-transform-runtime": "^7.18.5"
}
}

25
shims-vue.d.ts vendored Normal file
View File

@ -0,0 +1,25 @@
declare module "*.vue" {
import { DefineComponent } from "vue";
const comp: DefineComponent;
export default comp;
}
declare module "*.md" {
import { DefineComponent } from "vue";
const comp: DefineComponent;
export default comp;
}
declare module "prismjs";
declare module "prismjs/components/index";
declare module "escape-html";
interface ImportMeta {
env: {
MODE: string;
BASE_URL: string;
PROD: boolean;
DEV: boolean;
SSR: boolean;
};
}

View File

@ -0,0 +1,29 @@
<script lang="ts">
export default {
name: "CloseBtn",
};
</script>
<script lang="ts" setup>
import { computed, defineEmits } from "vue";
export interface CloseBtnProps {
closeBtn?: number | string | boolean;
}
const props = defineProps<CloseBtnProps>();
const emit = defineEmits(["closeHandle"]);
const closeHandle = () => {
emit("closeHandle");
};
</script>
<template>
<a
:class="['layui-layer-ico layui-layer-close layui-layer-close' + closeBtn]"
href="javascript:;"
@click="closeHandle"
></a>
</template>

28
src/component/Iframe.vue Normal file
View File

@ -0,0 +1,28 @@
<script lang="ts">
import { computed, VNodeTypes } from "vue";
export default {
name: "Iframe",
};
</script>
<script lang="ts" setup>
export interface IframeProps {
src?: string | Function | object | VNodeTypes;
}
const props = defineProps<IframeProps>();
const src = computed(() => {
return props.src as string;
});
</script>
<template>
<iframe
scrolling="auto"
class="layui-layer-iframe"
allowtransparency="true"
frameborder="0"
:src="src"
></iframe>
</template>

63
src/component/Notifiy.vue Normal file
View File

@ -0,0 +1,63 @@
<template>
<div class="layui-layer-notifiy-wrapper" ref="notifyRef">
<h2 class="title">
<i v-if="icon" :class="iconClass"></i>
{{ title }}
</h2>
<div class="content" v-if="!isHtmlFragment">
<p>{{ content }}</p>
</div>
<div class="content" v-html="content" v-else></div>
<CloseBtnVue @click="close"></CloseBtnVue>
</div>
</template>
<script lang="ts">
export default {
name: "Notifiy",
};
</script>
<script lang="ts" setup>
import { nextTick, onMounted, ref, shallowRef } from "vue";
import CloseBtnVue from "./CloseBtn.vue";
export interface LayNotifyProps {
title: any;
content: any;
isHtmlFragment?: boolean;
icon?: string | number | undefined;
iconClass: string[];
}
const props = withDefaults(defineProps<LayNotifyProps>(), {
isHtmlFragment: false,
});
const emit = defineEmits(["close"]);
const notifyRef = shallowRef<HTMLElement | null>(null);
const close = () => {
emit("close");
};
function addClass(obj: HTMLElement, cls: string) {
// class .
let obj_class = obj.className,
// class , ''.
blank = obj_class != "" ? " " : "";
let added = obj_class + blank + cls; // class class.
obj.className = added; // class.
}
onMounted(() => {
nextTick(() => {
setTimeout(() => {
//class
if (!notifyRef.value?.parentElement?.parentElement) return;
addClass(
notifyRef.value?.parentElement?.parentElement,
"layui-layer-notifiy-transition"
);
}, 300);
});
});
</script>

100
src/component/Photos.vue Normal file
View File

@ -0,0 +1,100 @@
<template>
<div class="layui-layer-phimg">
<img :src="imgList[index].src" />
<div class="layui-layer-imgsee" v-if="imgList.length > 0">
<span class="layui-layer-imguide" v-if="imgList.length > 1">
<a
href="javascript:;"
class="layui-layer-iconext layui-layer-imgprev"
@click="changeIndex(-1)"
></a>
<a
href="javascript:;"
class="layui-layer-iconext layui-layer-imgnext"
@click="changeIndex(1)"
></a>
</span>
<div
class="layui-layer-imgbar"
v-if="imgList.length > 1 || imgList[index].alt"
:style="{ opacity: showLayerImgBar ? 1 : 0 }"
>
<div class="thumb-row" v-if="ifSetThumb">
<div
class="thumb-box"
v-for="(item, i) in imgList"
:key="'thumb-box' + i"
@click="index = i"
>
<img :src="item.thumb" />
</div>
<div
class="thumb-box-border"
:style="{
left: `calc(calc( calc(100% - ${100 * imgList.length}px) / 2) + ${
index * 100
}px)`,
}"
></div>
</div>
<span class="layui-layer-imgtit" v-else>
<span v-if="imgList[index].alt">{{ imgList[index].alt }}</span>
<em v-if="imgList.length > 1"
>{{ index + 1 }} / {{ imgList.length }}</em
>
</span>
</div>
</div>
</div>
</template>
<script lang="ts">
export default {
name: "Photos",
};
</script>
<script lang="ts" setup>
import { watch, ref, onMounted, nextTick, computed } from "vue";
export interface LayPhotoProps {
imgList: { src: string; alt: string; thumb: string }[];
startIndex: number;
}
const emit = defineEmits(["resetCalculationPohtosArea"]);
const props = withDefaults(defineProps<LayPhotoProps>(), {
startIndex: 0,
});
const index = ref(props.startIndex);
watch(index, () => {
//
emit("resetCalculationPohtosArea", index.value);
});
const changeIndex = (step: number) => {
let nowIndex = index.value;
let next = nowIndex + step;
if (next < 0) {
next = props.imgList.length - 1;
}
if (next >= props.imgList.length) {
next = 0;
}
index.value = next;
};
const showLayerImgBar = ref(false);
onMounted(() => {
nextTick(() => {
showLayerImgBar.value = true;
});
});
const ifSetThumb = computed(() => {
let res = false;
props.imgList.forEach((e) => {
if (e.thumb) {
res = true;
}
});
return res;
});
</script>

9
src/component/Resize.vue Normal file
View File

@ -0,0 +1,9 @@
<script lang="ts">
export default {
name: "Resize",
};
</script>
<template>
<span class="layui-layer-resize"></span>
</template>

39
src/component/Shade.vue Normal file
View File

@ -0,0 +1,39 @@
<script lang="ts">
export default {
name: "Shade",
};
</script>
<script lang="ts" setup>
import { computed, defineEmits, StyleValue } from "vue";
export interface ShadeProps {
opacity: string;
index: number | Function;
visible: boolean | string;
}
const props = defineProps<ShadeProps>();
const emit = defineEmits(["shadeClick"]);
const styles = computed<any>(() => {
return {
opacity: props.opacity,
zIndex: props.index,
};
});
const shadeClick = () => {
emit("shadeClick");
};
</script>
<template>
<div
class="layui-layer-shade"
:style="styles"
@click="shadeClick"
v-if="visible"
></div>
</template>

26
src/component/Title.vue Normal file
View File

@ -0,0 +1,26 @@
<script lang="ts">
export default {
name: "Title",
};
</script>
<script lang="ts" setup>
export interface HeaderProps {
title: string | boolean | Function;
}
const renderContent = function (content: any) {
if (typeof content === "function") {
return content();
}
return content;
};
const props = defineProps<HeaderProps>();
</script>
<template>
<div class="layui-layer-title" style="cursor: move">
{{ renderContent(title) }}
</div>
</template>

1
src/component/index.ts Normal file
View File

@ -0,0 +1 @@
export { default as LayLayer } from "./index.vue"; // layer component

712
src/component/index.vue Normal file
View File

@ -0,0 +1,712 @@
<script lang="ts">
export default {
name: "LayLayer",
};
</script>
<script lang="ts" setup>
import Shade from "./Shade.vue";
import Iframe from "./Iframe.vue";
import Title from "./Title.vue";
import CloseBtn from "./CloseBtn.vue";
import Resize from "./Resize.vue";
import Photos from "./Photos.vue";
import Notifiy from "./Notifiy.vue";
import {
Ref,
ref,
watch,
computed,
useSlots,
VNodeTypes,
nextTick,
inject,
} from "vue";
import {
nextId,
maxArea,
maxOffset,
getArea,
calculateArea,
calculateOffset,
calculateContent,
calculateType,
minArea,
minOffset,
updateMinArrays,
getDrawerAnimationClass,
calculateDrawerArea,
calculatePhotosArea,
calculateNotifOffset,
removeNotifiyFromQueen,
getNotifyAnimationClass,
} from "../utils";
import useResize from "../composable/useResize";
import useMove from "../composable/useMove";
import { zIndexKey } from "../tokens";
export interface LayerProps {
id?: string;
title?: string | boolean | Function;
icon?: string | number;
skin?: string;
zIndex?: number | Function;
setTop?: boolean;
offset?: string[] | string;
area?: string[] | "auto";
modelValue?: boolean;
maxmin?: boolean | string;
btn?: Record<string, Function>[] | false;
move?: boolean | string;
resize?: boolean | string;
type?:
| 0
| 1
| 2
| 3
| 4
| 5
| 6
| "dialog"
| "page"
| "iframe"
| "loading"
| "drawer"
| "photos"
| "notifiy";
content?: string | Function | object | VNodeTypes;
isHtmlFragment?: boolean;
shade?: boolean | string;
shadeClose?: boolean | string;
shadeOpacity?: string;
closeBtn?: boolean | string;
btnAlign?: "l" | "c" | "r";
time?: number;
load?: number;
anim?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
isOutAnim?: boolean;
destroy?: Function;
success?: Function;
end?: Function;
yes?: Function;
yesText?: string;
isFunction?: boolean;
isMessage?: boolean;
appContext?: any;
startIndex?: number;
imgList?: { src: string; alt: string; thumb: string }[];
min?: Function;
full?: Function;
restore?: Function;
}
const props = withDefaults(defineProps<LayerProps>(), {
title: "标题",
setTop: false,
offset: () => ["50%", "50%"],
area: "auto",
modelValue: false,
maxmin: false,
move: true,
type: 1,
time: 0,
shade: true,
shadeClose: true,
shadeOpacity: "0.1",
closeBtn: "1",
btnAlign: "r",
load: 0,
anim: 0,
resize: false,
isHtmlFragment: false,
isOutAnim: true,
destroy: () => {},
success: () => {},
end: () => {},
full: () => {},
min: () => {},
restore: () => {},
yesText: "确定",
isFunction: false,
isMessage: false,
startIndex: 0,
imgList: () => [],
});
const emit = defineEmits(["close", "update:modelValue"]);
const slots = useSlots();
const max: Ref<boolean> = ref(false);
const min: Ref<boolean> = ref(false);
const id: Ref<string> = ref(props.id || nextId());
const layero = ref<HTMLElement | null>(null);
const type: number = calculateType(props.type);
const area: Ref<string[]> = ref(
calculateArea(props.type, props.area, props.offset)
);
const offset: Ref<string[]> = ref(
calculateOffset(props.offset, area.value, props.type)
);
const contentHeight = ref(
calculateContent(props.title, area.value[1], props.btn, type, props.isMessage)
);
const index: Ref<number | Function> = ref(
props.zIndex ?? inject(zIndexKey, 99999)
);
const visible: Ref<boolean> = ref(false);
const first: Ref<boolean> = ref(true);
const w: Ref<string> = ref(area.value[0]);
const h: Ref<string> = ref(area.value[1]);
const t: Ref<string> = ref(offset.value[0]);
const l: Ref<string> = ref(offset.value[1]);
const _w: Ref<string> = ref(area.value[0]);
const _h: Ref<string> = ref(area.value[0]);
const _t: Ref<string> = ref(offset.value[0]);
const _l: Ref<string> = ref(offset.value[1]);
/**
* 首次打开
* <p>
*/
const firstOpenDelayCalculation = function () {
nextTick(async () => {
area.value = getArea(layero.value);
if (type == 4) {
area.value = calculateDrawerArea(props.offset, props.area);
}
if (type == 5) {
area.value = await calculatePhotosArea(
props.imgList[props.startIndex].src,
props
);
}
offset.value = calculateOffset(props.offset, area.value, props.type);
if (type == 6) {
offset.value = calculateNotifOffset(props.offset, area.value, id.value);
}
w.value = area.value[0];
h.value = area.value[1];
_w.value = area.value[0];
_l.value = area.value[1];
t.value = offset.value[0];
l.value = offset.value[1];
_t.value = offset.value[0];
_l.value = offset.value[1];
supportMove();
});
};
/**
* 普通打开
* <p>
*/
const notFirstOpenLayerInit = function () {
w.value = _w.value;
h.value = _h.value;
t.value = _t.value;
l.value = _l.value;
supportMove();
};
/**
* Component 模式, 关闭事件
* <p>
* Component 模式下, 隐藏前需要恢复正常窗体位置与尺寸, 保存
**/
const beforeCloseSaveData = function () {
if (min.value) minHandle();
if (max.value) maxHandle();
_w.value = w.value;
_h.value = h.value;
_t.value = t.value;
_l.value = l.value;
};
/**
* 弹层最大化
* <p>
*/
const maxHandle = () => {
if (max.value) {
w.value = _w.value;
h.value = _h.value;
t.value = _t.value;
l.value = _l.value;
props.restore(props.id);
} else {
_t.value = t.value;
_l.value = l.value;
_w.value = w.value;
_h.value = h.value;
w.value = maxArea().w;
h.value = maxArea().h;
t.value = maxOffset().t;
l.value = maxOffset().l;
props.full(props.id);
}
max.value = !max.value;
};
/**
* 弹层最小化
* <p>
*/
const minHandle = () => {
let left = 180 * updateMinArrays(id.value, !min.value);
if (left > document.documentElement.clientWidth - 180) {
left = document.documentElement.clientWidth - 180;
}
if (min.value) {
w.value = _w.value;
h.value = _h.value;
t.value = _t.value;
l.value = _l.value;
props.restore(props.id);
} else {
_w.value = w.value;
_h.value = h.value;
_t.value = t.value;
_l.value = l.value;
h.value = minArea().h;
w.value = minArea().w;
t.value = minOffset(left).t;
l.value = minOffset(left).l;
props.min(props.id);
}
min.value = !min.value;
};
/**
* 重置弹层
* <p>
*/
const reset = function () {
if (!first.value) {
min.value = false;
max.value = false;
w.value = area.value[0];
h.value = area.value[1];
t.value = offset.value[0];
l.value = offset.value[1];
_w.value = area.value[0];
_h.value = area.value[1];
_t.value = offset.value[0];
_l.value = offset.value[1];
}
if (!props.modelValue) {
emit("update:modelValue", true);
}
};
/**
* 监听 modalValue 的值
* <p>
* 只有 Component 模式会触发
*/
watch(
() => props.modelValue,
() => {
visible.value = props.modelValue;
if (visible.value) {
if (first.value) {
first.value = false;
firstOpenDelayCalculation();
} else {
notFirstOpenLayerInit();
}
} else {
beforeCloseSaveData();
}
},
{ deep: true, immediate: true }
);
/**
* 监听 visible
* <p>
*/
watch(
() => visible.value,
() => {
if (visible.value) {
if (props.isFunction) {
firstOpenDelayCalculation();
}
props.success();
}
},
{ immediate: true }
);
watch(
() => visible.value,
() => {
if (!visible.value) {
props.end();
}
}
);
/**
* 高度监听
* <p>
*
* 在发生拖拽时, 需根据弹出层的外层高度计算 content 高度, 需要
* 考虑 btn 操作栏, 计算公式contentHeight = h - btnHeight
*
* @param h 弹层高度
* @param btn 操作栏
* @param type 弹层类型
*/
watch(
() => h.value,
() => {
contentHeight.value = calculateContent(
props.title,
h.value,
props.btn,
type,
props.isMessage
);
}
);
/**
* 弹层类型
*
* @param type 类型
* @param isMessage 是消息
* @param icon 图标类型
*/
const boxClasses = computed(() => {
return [
{
"layui-layer-dialog": type === 0,
"layui-layer-page": type === 1,
"layui-layer-iframe": type === 2,
"layui-layer-loading": type === 3,
"layui-layer-drawer": type === 4,
"layui-layer-photos": type === 5,
"layui-layer-notifiy": type === 6,
"layui-layer-msg": props.isMessage,
"layui-layer-hui": props.isMessage && !props.icon,
},
props.skin,
];
});
/**
* 拖拽拉伸
* <p>
*/
const supportMove = function () {
if (props.move && type != 4) {
nextTick(() => {
//
if (!layero.value) return;
useMove(layero.value, (left: string, top: string) => {
l.value = left;
t.value = top;
});
//
useResize(layero.value, (width: string, height: string) => {
h.value = height;
w.value = width;
});
});
}
};
/**
* 弹层样式
* <p>
*/
const styles = computed<any>(() => {
let style = {
top: t.value,
left: l.value,
width: w.value,
height: h.value,
zIndex: index.value,
};
return style;
});
/**
* 弹层内容
* <p>
* @param type 类型
* @param load 加载动画
* @param icon 图标
*/
const contentClasses = computed(() => {
return [
type === 3 ? `layui-layer-loading${props.load}` : "",
props.icon ? "layui-layer-padding" : "",
];
});
/**
* 关闭操作
* <p>
* @param null
*/
const closeHandle = () => {
emit("close");
emit("update:modelValue", false);
props.destroy();
if (type === 6) {
//@ts-ignore
removeNotifiyFromQueen(props.id);
}
};
/**
* 确定操作
* <p>
* @param null
*/
const yesHandle = () => {
if (props.yes != undefined) props.yes();
else closeHandle();
};
/**
* 遮盖层点击
* <p>
* @param null
*/
const shadeHandle = () => {
if (props.shadeClose) closeHandle();
};
/**
* 获取内容
* <p>
* @param content 文本 / 方法
*/
const renderContent = function (content: any) {
if (content instanceof Function) {
return content();
}
return content;
};
/**
* 弹层图标
* <p>
* @param icon 图标
*/
const iconClass = computed(() => {
return ["layui-layer-ico", `layui-layer-ico${props.icon}`];
});
/**
* 入场动画
* <p>
* @param type 类型
* @param anim 入场动画
*/
const enterActiveClass = computed(() => {
if (type === 4) {
return getDrawerAnimationClass(props.offset);
}
if (type === 6) {
return getNotifyAnimationClass(props.offset);
}
return `layer-anim layer-anim-0${props.anim}`;
});
/**
* 离场动画
* <p>
* @param type 类型
* @param isOutAnim 离场动画
*/
const leaveActiveClass = computed(() => {
if (type === 4) {
return getDrawerAnimationClass(props.offset, true);
}
return props.isOutAnim ? `layer-anim-close` : "";
});
/**
* 打开弹层
* <p>
*/
const open = () => {
visible.value = true;
};
/**
* 关闭弹层
* <p>
*/
const close = () => {
visible.value = false;
};
/**
* 显示遮盖
* <p>
* @param visible 弹层状态
* @param shade 开启遮盖
* @param min 最小化
*/
const shadeVisible = computed(() => {
return visible.value && props.shade && !min.value;
});
/**
* 拉伸支持
* <p>
* @param resize 拉伸
* @param max 最大化
* @param min 最小化
*/
const showResize = computed(() => {
return props.resize && !max.value && !min.value;
});
/**
* 显示标题
* <p>
* @param title 标题
* @param type 类型
*/
const showTitle = computed(() => {
return props.title && props.type != 3 && props.type != 5 && props.type != 6;
});
/*
* 图片弹层重新计算
*/
const resetCalculationPohtosArea = function (index: number) {
nextTick(async () => {
area.value = await calculatePhotosArea(props.imgList[index].src, props);
offset.value = calculateOffset(props.offset, area.value, props.type);
w.value = area.value[0];
h.value = area.value[1];
t.value = offset.value[0];
l.value = offset.value[1];
_w.value = area.value[0];
_l.value = area.value[1];
_t.value = offset.value[0];
_l.value = offset.value[1];
});
};
defineExpose({ reset, open, close });
</script>
<template>
<div>
<!-- 遮盖层 -->
<Shade
:index="index"
:visible="shadeVisible"
:opacity="shadeOpacity"
@shadeClick="shadeHandle"
></Shade>
<!-- 动画容器 -->
<transition
:enter-active-class="enterActiveClass"
:leave-active-class="leaveActiveClass"
>
<!-- 弹出层 -->
<div
ref="layero"
class="layui-layer layui-layer-border"
:class="boxClasses"
:style="styles"
v-if="visible"
>
<!-- 标题 -->
<Title v-if="showTitle" :title="title"></Title>
<!-- 内容 -->
<div
class="layui-layer-content"
:style="{ height: contentHeight }"
:class="contentClasses"
>
<template v-if="type === 0 || type === 1 || type === 4">
<i v-if="icon" :class="iconClass"></i>
<slot v-if="slots.default"></slot>
<template v-else>
<template v-if="isHtmlFragment">
<span v-html="renderContent(props.content)"></span>
</template>
<template v-else>{{ renderContent(props.content) }}</template>
</template>
</template>
<Iframe v-if="type === 2" :src="props.content"></Iframe>
<Photos
v-if="type === 5"
:imgList="props.imgList"
:startIndex="props.startIndex"
@resetCalculationPohtosArea="resetCalculationPohtosArea"
></Photos>
<Notifiy
v-if="type === 6"
@close="closeHandle"
:title="props.title"
:content="props.content"
:isHtmlFragment="props.isHtmlFragment"
:icon="props.icon"
:iconClass="iconClass"
></Notifiy>
</div>
<!-- 工具栏 -->
<span
class="layui-layer-setwin"
v-if="type != 3 && type != 5 && type != 6"
>
<a
v-if="maxmin && !max"
class="layui-layer-min"
:class="[min ? 'layui-layer-ico layui-layer-maxmin' : '']"
href="javascript:;"
@click="minHandle"
>
<cite v-if="!min"></cite>
</a>
<a
v-if="maxmin && !min"
class="layui-layer-ico layui-layer-max"
:class="[max ? 'layui-layer-maxmin' : '']"
href="javascript:;"
@click="maxHandle"
></a>
<CloseBtn
v-if="closeBtn != false"
:close-btn="closeBtn"
@closeHandle="closeHandle"
></CloseBtn>
</span>
<!-- 操作栏 -->
<div
v-if="((btn && btn.length > 0) || type === 0) && !isMessage"
class="layui-layer-btn"
:class="[`layui-layer-btn-${btnAlign}`]"
>
<template v-if="btn && btn.length > 0">
<template v-for="(b, index) in btn" :key="index">
<a :class="[`layui-layer-btn${index}`]" @click="b.callback(id)">{{
b.text
}}</a>
</template>
</template>
<template v-else>
<template v-if="type === 0">
<a class="layui-layer-btn0" @click="yesHandle()">{{ yesText }}</a>
</template>
</template>
</div>
<!-- 辅助栏 -->
<Resize v-if="showResize"></Resize>
</div>
</transition>
</div>
</template>

53
src/composable/useMove.ts Normal file
View File

@ -0,0 +1,53 @@
const useMove = function (el: HTMLElement, callback: Function) {
el.style.position = "fixed";
let offsetX: number;
let offsetY: number;
if (el != null) {
el.addEventListener("mousedown", function (event: any) {
const path = (event.composedPath && event.composedPath()) || event.path;
if (path[0].className === "layui-layer-title") {
if (event.button == 0 && el != null) {
const lexObj: any = getComputedStyle(el);
offsetX =
event.pageX - el.offsetLeft + parseInt(lexObj["margin-left"]);
offsetY =
event.pageY - el.offsetTop + parseInt(lexObj["margin-right"]);
const move = function (event: any) {
if (el != null) {
let x = event.pageX - offsetX;
let y = event.pageY - offsetY;
if (x < 0) {
x = 0;
} else if (
x >
document.documentElement.clientWidth - el.offsetWidth
) {
x = document.documentElement.clientWidth - el.offsetWidth;
}
if (y < 0) {
y = 0;
} else if (
y >
document.documentElement.clientHeight - el.offsetHeight
) {
y = document.documentElement.clientHeight - el.offsetHeight;
}
el.style.left = `${x}px`;
el.style.top = `${y}px`;
callback(el.style.left, el.style.top);
}
return false;
};
document.addEventListener("mousemove", move);
const stop = function () {
document.removeEventListener("mousemove", move);
document.removeEventListener("mouseup", stop);
};
document.addEventListener("mouseup", stop);
}
}
return false;
});
}
};
export default useMove;

View File

@ -0,0 +1,35 @@
const useMove = function (el: HTMLElement, callback: Function) {
if (el != null) {
el.addEventListener("mousedown", function (event: any) {
const path = (event.composedPath && event.composedPath()) || event.path;
if (path[0].className === "layui-layer-resize") {
if (event.button == 0 && el != null) {
var x = el.offsetLeft;
var y = el.offsetTop;
const move = function (moveEvent: any) {
if (el != null) {
var offsetX = moveEvent.clientX;
var offsetY = moveEvent.clientY;
var w = offsetX - x;
var h = offsetY - y;
w < 260 && (w = 260);
h < 115 && (h = 115);
el.style.width = `${w}px`;
el.style.height = `${h}px`;
callback(el.style.width, el.style.height);
}
return false;
};
document.addEventListener("mousemove", move);
const stop = function () {
document.removeEventListener("mousemove", move);
document.removeEventListener("mouseup", stop);
};
document.addEventListener("mouseup", stop);
}
}
return false;
});
}
};
export default useMove;

270
src/index.ts Normal file
View File

@ -0,0 +1,270 @@
import { render, h, isVNode, getCurrentInstance, AppContext, App } from "vue";
import LayLayer from "./component/index.vue";
import { InstallOptions } from "./types";
import { zIndexKey } from "./tokens";
import { nextId, removeNotifiyFromQueen } from "./utils";
// 实例队列
const layerInstance: any = [];
// 新增实例
const addInstance = (instance: any) => {
layerInstance.push(instance);
};
// 删除实例
const delInstance = (id: any) => {
layerInstance.forEach((item: any, index: number) => {
if (item.modalContainer.id === id) {
// 删除元素
layerInstance.splice(index, 1);
}
});
};
// 清空实例
const cleanInstance = () => {
layerInstance.splice(0, layerInstance.length);
};
// 是否存在
const isExist = (id: any) => {
let b = false;
layerInstance.forEach((item: any, index: number) => {
if (item.modalContainer.id == id) {
b = true;
}
});
return b;
};
const findById = (id: any) => {
let instance = null;
layerInstance.forEach((item: any, index: number) => {
if (item.modalContainer.id === id) {
instance = item;
}
});
return instance;
};
// 聚合 modal 配置
const mergeOption = (option: any, defaultOption: any) => {
if (option) defaultOption = Object.assign(defaultOption, option);
return defaultOption;
};
// 创建 modal 容器
const createContainer = (options: any) => {
const modalContainer = document.createElement("div");
modalContainer.id = options.id;
document.body.appendChild(modalContainer);
return modalContainer;
};
// modal 子 VNode
const modalChildrenVNode = (content: any) => {
if (typeof content === "function") {
return isVNode(content()) ? { default: () => content() } : undefined;
}
return isVNode(content) ? { default: () => content } : undefined;
};
const layer = {
_context: <AppContext | null>null,
// 页面
open: (option: any, callback?: Function) => {
let defaultOption = {};
return layer.create(option, defaultOption, callback);
},
// 抽屉
drawer: (option: any, callback?: Function) => {
let defaultOption = {
type: "drawer",
};
return layer.create(option, defaultOption, callback);
},
// 消息
msg: (message: string, option?: any, callback?: Function) => {
let defaultOption = {
type: 0,
title: false,
content: message,
closeBtn: false,
shadeClose: false,
isMessage: true,
shade: false,
time: 1000,
btn: false,
};
return layer.create(option, defaultOption, callback);
},
// 加载
load: (load: number, option?: any, callback?: Function) => {
let defaultOption = {
type: 3,
load: load,
anim: 5,
isOutAnim: false,
shadeClose: false,
};
return layer.create(option, defaultOption, callback);
},
// 确认
confirm: (msg: string, option?: any, callback?: Function) => {
let defaultOption = {
type: 0,
content: msg,
shadeClose: false,
};
return layer.create(option, defaultOption, callback);
},
//图片预览
photos: (option: any, callback?: Function) => {
if (typeof option === "string") {
option = {
imgList: [{ src: option }],
};
}
let defaultOption = {
type: 5,
anim: 2,
startIndex: 0,
isOutAnim: true,
shadeClose: true,
shadeOpacity: "0.7",
};
return layer.create(option, defaultOption, callback);
},
//通知
notifiy: (option: any = {}, callback?: Function) => {
option.type = 6;
let defaultOption = {
offset: "rt",
time: 2000,
area: "auto",
shade: false,
};
return layer.create(option, defaultOption, callback);
},
// 创建弹出层
create: (option: any, defaultOption: any, callback?: Function) => {
// 销毁定时
let timer: NodeJS.Timeout;
// 聚合配置 Opt
const options = mergeOption(option, defaultOption);
// 生成唯一标识
if (options.hasOwnProperty("id")) {
// 判断 id 存在, 并销毁窗体
layer.close(options.id);
} else {
// 生成新的唯一标识
options.id = nextId();
}
// 创建容器 Dom
const modalContainer = createContainer(options);
// 创建虚拟 Dom
const modalInstance = h(
LayLayer,
{
...options,
isFunction: true,
destroy() {
clearTimeout(timer);
modalInstance.component?.exposed?.close();
setTimeout(() => {
render(null, modalContainer);
// 清空 dom
if (document.body.contains(modalContainer)) {
document.body.removeChild(modalContainer);
}
}, 2000);
delInstance(modalContainer.id);
},
},
modalChildrenVNode(options.content)
);
modalInstance.appContext = options.appContext || layer._context;
// 将虚拟 dom 渲染到 dom 容器
render(modalInstance, modalContainer);
// 调用 open 函数
modalInstance.component?.exposed?.open();
// 延时 time 销毁
if (
defaultOption &&
defaultOption.time != undefined &&
defaultOption.time != 0
) {
timer = setTimeout(() => {
modalInstance.component?.exposed?.close();
if (callback) callback(modalContainer.id);
setTimeout(() => {
render(null, modalContainer);
if (document.body.contains(modalContainer)) {
document.body.removeChild(modalContainer);
}
}, 2000);
// 销毁实例
delInstance(modalContainer.id);
//Notifiy特殊处理
if (options.type === 6) {
removeNotifiyFromQueen(options.id);
}
}, defaultOption.time);
}
// 维护实例
addInstance({ modalContainer, modalInstance });
// 返回实例
return modalContainer.id;
},
// 关闭弹出层
close: (id: any) => {
if (id != null && isExist(id)) {
// 找到这个实例
const instance: any = findById(id);
instance.modalInstance.component?.exposed?.close();
setTimeout(() => {
render(null, instance.modalContainer);
if (document.body.contains(instance.modalContainer))
document.body.removeChild(instance.modalContainer);
}, 2000);
}
// 销毁实例
delInstance(id);
},
// 关闭所有弹出层
closeAll: () => {
layerInstance.forEach((item: any) => {
item.modalInstance.component?.exposed?.close();
setTimeout(() => {
render(null, item.modalContainer);
if (document.body.contains(item.modalContainer))
document.body.removeChild(item.modalContainer);
}, 2000);
});
// 清空实例
cleanInstance();
},
// 重置位置
reset: (instance: any) => {
instance.modalInstance.component?.exposed?.reset();
},
};
// 全局安装
const install = (app: App, options?: InstallOptions): void => {
layer._context = app._context;
app.component(LayLayer.name, LayLayer);
app.config.globalProperties.$layer = layer;
if (options) {
app.provide(zIndexKey, options.zIndex);
}
};
export { layer, LayLayer };
export default { install };
import "./theme/index.css";
import { getSystemErrorMap } from "util";

BIN
src/theme/icon-ext.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
src/theme/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

1128
src/theme/index.css Normal file

File diff suppressed because it is too large Load Diff

BIN
src/theme/loading-0.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
src/theme/loading-1.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

BIN
src/theme/loading-2.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

1
src/tokens/index.ts Normal file
View File

@ -0,0 +1 @@
export const zIndexKey = Symbol("zIndex");

3
src/types/index.ts Normal file
View File

@ -0,0 +1,3 @@
export type StringObject = Record<string, unknown>;
export interface InstallOptions extends StringObject {}

416
src/utils/index.ts Normal file
View File

@ -0,0 +1,416 @@
import { title } from "process";
import { layer } from "../index";
// 随机数
export function nextId() {
var s: any = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4";
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
s[8] = s[13] = s[18] = s[23] = "-";
var uuid = s.join("");
return uuid;
}
export function calculateMinArea(minArea: any) {
if (!minArea) {
return ["120px", "100px"];
}
if (typeof minArea == "string") {
return [minArea];
}
return [minArea];
}
export function calculateArea(type: any, area: any, offset: any) {
return type != "drawer"
? calculateBaseArea(area)
: calculateDrawerArea(offset, area);
}
// 计算宽高
// @param area
// @param type
// @return 正确宽高
export function calculateBaseArea(area: any) {
if (area === "auto") {
return [];
}
// @ts-ignore
if (typeof area == "string") {
return [area];
}
return [...area];
}
// 抽屉宽/高
export function calculateDrawerArea(
offset: any,
drawerArea: string[] | string = "30%"
) {
if (drawerArea instanceof Array) {
return drawerArea;
}
if (drawerArea === "auto") {
drawerArea = "30%";
}
if (offset === "l" || offset === "r") {
return [drawerArea, "100%"];
} else if (offset === "t" || offset === "b") {
return ["100%", drawerArea];
}
return [drawerArea, "100%"];
}
// 计算偏移
// @param offset
// @param domSize
// @return 正确位置
export function calculateOffset(offset: any, area: any, type: any) {
var arr = ["t", "r", "b", "l", "lt", "lb", "rt", "rb"];
var t = offset[0];
var l = offset[1];
if (offset instanceof Array && type === "drawer") {
offset = "r";
}
// @ts-ignore
if (arr.indexOf(offset) > -1) {
t = "50%";
l = "50%";
}
// 预备处理
if (arr.indexOf(offset) != -1 || t.indexOf("%") > -1)
t = "calc(" + t + " - (" + (area === "auto" ? "100px" : area[1]) + "/2 ))";
if (arr.indexOf(offset) != -1 || l.indexOf("%") > -1)
l = "calc(" + l + " - (" + (area === "auto" ? "100px" : area[0]) + "/2 ))";
// 关键字处理
if (offset === "t") t = "0px";
else if (offset === "r") l = "calc(100% - " + area[0] + ")";
else if (offset === "b") t = "calc(100% - " + area[1] + ")";
else if (offset === "l") l = "0px";
else if (offset === "lt") {
t = "0px";
l = "0px";
} else if (offset === "lb") {
t = "calc(100% - " + area[1] + ")";
l = "0px";
} else if (offset === "rt") {
t = "0px";
l = "calc(100% - " + area[0] + ")";
} else if (offset === "rb") {
t = "calc(100% - " + area[1] + ")";
l = "calc(100% - " + area[0] + ")";
}
// 返回位置
return [t, l];
}
// 窗体类型
export function calculateType(modalType: number | string) {
if (modalType === "dialog" || modalType == 0) {
return 0;
} else if (modalType === "page" || modalType == 1) {
return 1;
} else if (modalType === "iframe" || modalType == 2) {
return 2;
} else if (modalType === "loading" || modalType == 3) {
return 3;
} else if (modalType === "drawer" || modalType == 4) {
return 4;
} else if (modalType === "photos" || modalType == 5) {
return 5;
} else if (modalType === "notifiy" || modalType == 6) {
return 6;
}
return 0;
}
// 计算高度
// @param height 高度
// @param btn 操作集合
export function calculateContent(
title: any,
height: any,
btn: any,
type: any,
isMessage?: boolean
) {
if (height && height.indexOf("%") != -1) {
height = "100%";
}
if (btn && btn.length > 0) {
if (type == 0) {
if (title) {
return "calc(" + height + " - 137px)";
} else {
return "calc(" + height + " - 86px)";
}
}
if (type == 1 || type == 4) {
if (title) {
return "calc(" + height + " - 102px)";
} else {
return "calc(" + height + " - 51px)";
}
}
if (type == 2) {
if (title) {
return "calc(" + height + " - 102px)";
} else {
return "calc(" + height + " - 51px)";
}
}
} else {
if (type == 0) {
if (title) {
return isMessage ? "" : "calc(" + height + " - 137px)";
} else {
return isMessage ? "" : "calc(" + height + " - 86px)";
}
}
if (type == 1 || type == 4) {
if (title) {
return "calc(" + height + " - 51px)";
} else {
return "calc(" + height + " - 0px)";
}
}
if (type == 2) {
if (title) {
return "calc(" + height + " - 51px)";
} else {
return "calc(" + height + " - 0px)";
}
}
}
}
// 尺寸常量
export function maxArea() {
return { w: "100%", h: "100%" };
}
// 初始位置
export function maxOffset() {
return { t: "0px", l: "0px" };
}
// 最小化尺寸
export function minArea() {
return { w: "180px", h: "51px" };
}
// 最小化位置
export function minOffset(left: any) {
return { t: "calc(100% - 51px)", l: left + "px" };
}
// 元素位置
// @param x 横坐标
// @param y 纵坐标
export function getPosition(dom: any) {
return { x: dom?.style.left, y: dom?.style.top };
}
// 元素宽高
export function getArea(dom: any) {
// @ts-ignore
let width = getComputedStyle(dom, null).width;
// @ts-ignore
let height = getComputedStyle(dom, null).height;
return [width, height];
}
// 最小化的队列
let minArrays: Array<String> = [];
// 更新最小化队列
export function updateMinArrays(id: string, state: Boolean) {
var i = 0;
if (state) {
const index = minArrays.findIndex((v) => v === undefined);
if (index === -1) {
minArrays.push(id);
i = minArrays.length - 1;
} else {
minArrays[index] = id;
i = index;
}
} else {
delete minArrays[minArrays.findIndex((v) => v == id)];
i = -1;
}
return i;
}
// 抽屉动画类
export function getDrawerAnimationClass(offset: any, isClose: boolean = false) {
const prefix = "layer-drawer-anim layer-anim";
let suffix = "rl";
if (offset === "l") {
suffix = "lr";
} else if (offset === "r") {
suffix = "rl";
} else if (offset === "t") {
suffix = "tb";
} else if (offset === "b") {
suffix = "bt";
}
return isClose ? `${prefix}-${suffix}-close` : `${prefix}-${suffix}`;
}
//计算图片大小 并缩放
export async function calculatePhotosArea(
url: string,
options: object
): Promise<Array<string>> {
let img = new Image();
img.src = url;
return new Promise((resolve, reject) => {
if (img.complete) {
resolve(area(img));
return;
}
const layerId = layer.load(2);
img.onload = () => {
layer.close(layerId);
resolve(area(img));
};
img.onerror = () => {
layer.close(layerId);
layer.msg("图片加载失败");
reject(false);
};
});
function area(img: { width: number; height: number }) {
var imgarea = [img.width, img.height];
var winarea = [window.innerWidth - 250, window.innerHeight - 250];
//如果 实际图片的宽或者高比 屏幕大(那么进行缩放)
if (imgarea[0] > winarea[0] || imgarea[1] > winarea[1]) {
let wh = [imgarea[0] / winarea[0], imgarea[1] / winarea[1]]; //取宽度缩放比例、高度缩放比例
if (wh[0] > wh[1]) {
//取缩放比例最大的进行缩放
imgarea[0] = imgarea[0] / wh[0];
imgarea[1] = imgarea[1] / wh[0];
} else if (wh[0] < wh[1]) {
imgarea[0] = imgarea[0] / wh[1];
imgarea[1] = imgarea[1] / wh[1];
}
}
return [imgarea[0] + "px", imgarea[1] + "px"];
}
}
// 计算Notify位置 队列 此处先暂时定义Notify的间距为15px
export function calculateNotifOffset(offset: any, area: any, layerId: string) {
let arr = ["lt", "lb", "rt", "rb"];
let t = "0",
l = "0";
// 间隙
let transOffsetLeft = 15;
let transOffsetTop = 15;
(window as any).NotifiyQueen = (window as any).NotifiyQueen || [];
let notifiyQueen = (window as any).NotifiyQueen;
if (typeof offset != "string" || arr.indexOf(offset) === -1) {
offset = "rt";
}
// 当前区域元素集合
let nodeList = notifiyQueen.filter((e: { offset: any }) => {
if (e.offset === offset) {
return e;
}
});
//前一个元素
let prevNode = nodeList.length > 0 ? nodeList[nodeList.length - 1] : null;
if (prevNode) {
prevNode = document.getElementById(prevNode["id"])?.firstElementChild
?.firstElementChild;
if (offset === "rt" || offset === "lt") {
transOffsetTop +=
prevNode.offsetHeight + parseFloat(prevNode.style["top"]);
} else {
let bottom = parseFloat(prevNode.style["top"].split(" - ")[1]);
transOffsetTop += prevNode.offsetHeight + bottom;
}
} else {
if (offset === "rb" || offset === "lb") {
transOffsetTop += parseFloat(area[1]);
}
}
// 关键字处理
if (offset === "rt") {
t = transOffsetTop + "px";
l = "calc(100% - " + (parseFloat(area[0]) + transOffsetLeft) + "px)";
} else if (offset === "rb") {
t = "calc(100vh - " + transOffsetTop + "px)";
l = "calc(100% - " + (parseFloat(area[0]) + transOffsetLeft) + "px)";
} else if (offset === "lt") {
t = transOffsetTop + "px";
l = transOffsetLeft + "px";
} else if (offset === "lb") {
t = "calc(100vh - " + transOffsetTop + "px)";
l = transOffsetLeft + "px";
}
notifiyQueen.push({
id: layerId,
offset: offset,
});
// 返回位置
return [t, l];
}
//移除Notify队列中某项并且重新计算其他Notify位置
export function removeNotifiyFromQueen(layerId: string) {
// 间隙
let transOffsetTop = 15;
// 删除项的高度
let notifiyDom = document.getElementById(layerId)?.firstElementChild
?.firstElementChild as HTMLElement;
let offsetHeight = notifiyDom.offsetHeight;
(window as any).NotifiyQueen = (window as any).NotifiyQueen || [];
let notifiyQueen = (window as any).NotifiyQueen;
let index = notifiyQueen.findIndex((e: { id: string }) => e.id === layerId);
let offsetType = notifiyQueen[index].offset;
let list = notifiyQueen.filter((e: { offset: any }) => {
if (e.offset === offsetType) {
return e;
}
});
let findIndex = list.findIndex((e: { id: string }) => e.id === layerId);
// //得到需要修改的定位的Notifiy集合
let needCalculatelist = list.slice(findIndex + 1);
needCalculatelist.forEach((e: { id: string }) => {
let dom = document.getElementById(e.id)?.firstElementChild
?.firstElementChild as HTMLElement;
if (offsetType === "rt" || offsetType === "lt") {
dom.style["top"] =
parseFloat(dom.style["top"]) - transOffsetTop - offsetHeight + "px";
} else {
let bottom =
parseFloat(dom.style["top"].split(" - ")[1]) -
transOffsetTop -
offsetHeight;
dom.style["top"] = "calc(100vh - " + bottom + "px)";
}
});
notifiyQueen.splice(index, 1); //删除
}
// Notify动画类
export function getNotifyAnimationClass(offset: any) {
const prefix = "layer-drawer-anim layer-anim";
let suffix = "";
if (offset === "lt" || offset === "lb") {
suffix = "lr";
} else {
suffix = "rl";
}
return `${prefix}-${suffix}`;
}

19
tsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"baseUrl": ".",
"module": "esnext",
"target": "es2015",
"moduleResolution": "Node",
"strict": true,
"allowJs": true,
"resolveJsonModule": true,
"declaration": true,
"emitDeclarationOnly": true,
"esModuleInterop": true,
"declarationDir": "types",
"jsx": "preserve",
"lib": ["ESNext","DOM"]
},
"include": ["src/**/*","shims-vue.d.ts"],
"exclude": ["node_modules"]
}

1
types/component/index.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export { default as LayLayer } from "./index.vue";

2
types/composable/useMove.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
declare const useMove: (el: HTMLElement, callback: Function) => void;
export default useMove;

2
types/composable/useResize.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
declare const useMove: (el: HTMLElement, callback: Function) => void;
export default useMove;

23
types/index.d.ts vendored Normal file
View File

@ -0,0 +1,23 @@
import { AppContext, App } from "vue";
import LayLayer from "./component/index.vue";
import { InstallOptions } from "./types";
declare const layer: {
_context: AppContext | null;
open: (option: any, callback?: Function) => string;
drawer: (option: any, callback?: Function) => string;
msg: (message: string, option?: any, callback?: Function) => string;
load: (load: number, option?: any, callback?: Function) => string;
confirm: (msg: string, option?: any, callback?: Function) => string;
photos: (option: any, callback?: Function) => string;
notifiy: (option?: any, callback?: Function) => string;
create: (option: any, defaultOption: any, callback?: Function) => string;
close: (id: any) => void;
closeAll: () => void;
reset: (instance: any) => void;
};
export { layer, LayLayer };
declare const _default: {
install: (app: App<any>, options?: InstallOptions | undefined) => void;
};
export default _default;
import "./theme/index.css";

1
types/tokens/index.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export declare const zIndexKey: unique symbol;

3
types/types/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
export declare type StringObject = Record<string, unknown>;
export interface InstallOptions extends StringObject {
}

35
types/utils/index.d.ts vendored Normal file
View File

@ -0,0 +1,35 @@
export declare function nextId(): any;
export declare function calculateMinArea(minArea: any): any[];
export declare function calculateArea(type: any, area: any, offset: any): any[];
export declare function calculateBaseArea(area: any): any[];
export declare function calculateDrawerArea(offset: any, drawerArea?: string[] | string): string[];
export declare function calculateOffset(offset: any, area: any, type: any): any[];
export declare function calculateType(modalType: number | string): 1 | 0 | 2 | 3 | 4 | 5 | 6;
export declare function calculateContent(title: any, height: any, btn: any, type: any, isMessage?: boolean): string | undefined;
export declare function maxArea(): {
w: string;
h: string;
};
export declare function maxOffset(): {
t: string;
l: string;
};
export declare function minArea(): {
w: string;
h: string;
};
export declare function minOffset(left: any): {
t: string;
l: string;
};
export declare function getPosition(dom: any): {
x: any;
y: any;
};
export declare function getArea(dom: any): string[];
export declare function updateMinArrays(id: string, state: Boolean): number;
export declare function getDrawerAnimationClass(offset: any, isClose?: boolean): string;
export declare function calculatePhotosArea(url: string, options: object): Promise<Array<string>>;
export declare function calculateNotifOffset(offset: any, area: any, layerId: string): string[];
export declare function removeNotifiyFromQueen(layerId: string): void;
export declare function getNotifyAnimationClass(offset: any): string;

55
vite.config.ts Normal file
View File

@ -0,0 +1,55 @@
import { defineConfig } from "vite";
import { name } from "./package.json";
import { babel } from "@rollup/plugin-babel";
import vue from "@vitejs/plugin-vue";
import path from "path";
const camelize = (name: string) =>
name.replace(/(^|-)(\w)/g, (a, b, c) => c.toUpperCase());
export default defineConfig({
resolve: {
alias: {
"/@src": path.resolve(__dirname, "src"),
},
},
plugins: [vue()],
build: {
target: "es2015",
outDir: path.resolve(__dirname, "lib"),
lib: {
entry: path.resolve(__dirname, "src/index.ts"),
name: camelize(name),
},
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
pure_funcs: ["console.log"],
},
output: {
comments: true,
},
},
rollupOptions: {
output: {
exports: "named",
globals: (id: string) => {
const name = id.replace(/^@/, "").split("/")[0];
return camelize(name);
},
assetFileNames: "index.css",
},
plugins: [
babel({
babelHelpers: "runtime",
exclude: "node_modules/**",
extensions: [".js", ".jsx", ".ts", ".tsx", ".vue"],
presets: ["@babel/preset-env", "@babel/preset-typescript"],
plugins: [["@babel/plugin-transform-runtime"]],
}),
],
external: ["vue"],
},
},
});