diff --git a/packages/opendesign/src/components/dialog/style/index.scss b/packages/opendesign/src/components/dialog/style/index.scss
index 2dd4310281b54d7093070a93b500def5741932db..8274de6ee3785c27c3f991746faa330470c283d1 100644
--- a/packages/opendesign/src/components/dialog/style/index.scss
+++ b/packages/opendesign/src/components/dialog/style/index.scss
@@ -1,7 +1,7 @@
@use './var.scss';
.o-dialog {
- position: fixed;
+ position: absolute;
left: 0;
right: 0;
top: 0;
@@ -9,10 +9,7 @@
align-items: center;
justify-content: center;
z-index: var(--dlg-z-index);
- display: none;
- &.is-show {
- display: flex;
- }
+ display: flex;
}
.o-dlg-mask {
position: absolute;
@@ -20,40 +17,40 @@
right: 0;
top: 0;
bottom: 0;
- background-color: var(--o-color-mask1);
+ background-color: var(--dlg-mask);
}
.o-dlg-main {
position: relative;
- background-color: var(--o-color-control-light);
+ background-color: var(--dlg-bg-color);
min-width: 200px;
- box-shadow: var(--o-shadow-1);
+ box-shadow: var(--dlg-shadow);
+ border-radius: var(--dlg-radius);
+ min-width: var(--dlg-min-width);
}
.o-dlg-btn-close {
position: absolute;
right: var(--o-gap-3);
top: var(--o-gap-3);
- font-size: var(--o-icon_size-s);
+ font-size: var(--dlg-close-size);
color: var(--dlg-close-color);
- border-radius: var(--dlg-close-radius);
display: flex;
+ transition: transform var(--o-easing-standard) var(--o-duration-m2);
&:hover {
- text-decoration: none;
color: var(--dlg-close-color-hover);
- background-color: var(--dlg-close-bg-color-hover);
}
&:active {
color: var(--dlg-close-color-active);
- background-color: var(--dlg-close-bg-color-active);
+ transform: scale(0.9);
}
}
.o-dlg-head {
- padding: 8px 16px;
+ padding: var(--dlg-head-padding);
}
.o-dlg-body {
- padding: 8px 16px;
+ padding: var(--dlg-body-padding);
}
.o-dlg-foot {
- padding: 8px 16px;
+ padding: var(--dlg-foot-padding);
}
diff --git a/packages/opendesign/src/components/dialog/style/var.scss b/packages/opendesign/src/components/dialog/style/var.scss
index 664c3d71e96d4e28f8cb59704dbf902cd724ff61..1d0c641c5529914684ab31da59616ba1ae28da78 100644
--- a/packages/opendesign/src/components/dialog/style/var.scss
+++ b/packages/opendesign/src/components/dialog/style/var.scss
@@ -1,9 +1,18 @@
.o-dialog {
+ --dlg-close-size: var(--o-icon_size-xs);
--dlg-close-color: var(--o-color-info2);
--dlg-close-color-hover: var(--o-color-primary2);
--dlg-close-color-active: var(--o-color-primary3);
- --dlg-close-bg-color-hover: var(--o-color-control1-light);
- --dlg-close-bg-color-active: var(--o-color-control2-light);
- --dlg-close-radius: 50%;
+
--dlg-z-index: calc(var(--o-z-index-base) + 10);
+
+ --dlg-mask: var(--o-color-mask1);
+ --dlg-bg-color: var(--o-color-control-light);
+ --dlg-radius: var(--o-radius-l);
+ --dlg-shadow: var(--o-shadow-1);
+
+ --dlg-min-width: 320px;
+ --dlg-head-padding: 16px 24px;
+ --dlg-body-padding: 0 24px;
+ --dlg-foot-padding: 16px 24px;
}
diff --git a/packages/opendesign/src/components/dialog/types.ts b/packages/opendesign/src/components/dialog/types.ts
index 487360b544345876b089410bdbbb9eacbac96be9..1228613c4c33f9b74f6f47ba97a13eca25fdfc4f 100644
--- a/packages/opendesign/src/components/dialog/types.ts
+++ b/packages/opendesign/src/components/dialog/types.ts
@@ -21,7 +21,7 @@ export const dialogProps = {
type: Boolean,
},
/**
- * 是否在popup隐藏时unmout
+ * 是否在隐藏时unmout
*/
unmountOnHide: {
type: Boolean,
@@ -40,7 +40,13 @@ export const dialogProps = {
type: String,
default: 'o-zoom-fade',
},
-
+ /**
+ * 是否需要mask
+ */
+ mask: {
+ type: Boolean,
+ default: true,
+ },
};
export type DialogPropsT = ExtractPropTypes
;
diff --git a/packages/opendesign/src/components/directves/click-outside.ts b/packages/opendesign/src/components/directves/click-outside.ts
index 2530a3cd35c6d3f3fc3e227e2f649eb2f9e77bbe..03c3f2a8bf31025fd28ec21441e86967879276f6 100644
--- a/packages/opendesign/src/components/directves/click-outside.ts
+++ b/packages/opendesign/src/components/directves/click-outside.ts
@@ -1,18 +1,15 @@
import { ObjectDirective, DirectiveBinding } from 'vue';
import { useOutClick } from '../hooks/use-out-click';
-
let out: ReturnType | null = null;
const vOutClick: ObjectDirective = {
beforeMount(el: HTMLElement, binding: DirectiveBinding) {
out = useOutClick();
- out?.addOutClickListener(el, binding.value);
+ out?.addListener(el, binding.value);
},
unmounted(el: HTMLElement) {
- out?.removeOutClickListener(el);
- }
+ out?.removeListener(el);
+ },
};
-export {
- vOutClick
-};
\ No newline at end of file
+export { vOutClick };
diff --git a/packages/opendesign/src/components/directves/on-intersection.ts b/packages/opendesign/src/components/directves/on-intersection.ts
index 158c2cf84ac77419349a3ad3dd973a5772a1de5f..7a2e14da524b799963795f95857a68c709b057f1 100644
--- a/packages/opendesign/src/components/directves/on-intersection.ts
+++ b/packages/opendesign/src/components/directves/on-intersection.ts
@@ -12,16 +12,14 @@ const vIntersection: ObjectDirective = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
if (isFunction(binding.value)) {
listener = binding.value;
- io?.addIntersectionListener(el, listener);
+ io?.observe(el, listener);
}
},
unmounted(el: HTMLElement) {
if (listener) {
- io?.removeIntersectionListener(el, listener);
+ io?.unobserve(el, listener);
}
},
};
-export {
- vIntersection
-};
\ No newline at end of file
+export { vIntersection };
diff --git a/packages/opendesign/src/components/directves/on-resize.ts b/packages/opendesign/src/components/directves/on-resize.ts
index 7d7f9619ad0f76b2e6db159dfccd676f5066e995..2bb477b2b2432a87ec43f1759209161c4cc1f849 100644
--- a/packages/opendesign/src/components/directves/on-resize.ts
+++ b/packages/opendesign/src/components/directves/on-resize.ts
@@ -12,12 +12,12 @@ const vOnResize: ObjectDirective = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
if (isFunction(binding.value)) {
listener = binding.value;
- ro?.addResizeListener(el, listener);
+ ro?.observe(el, listener);
}
},
unmounted(el: HTMLElement) {
if (listener) {
- ro?.removeResizeListener(el, listener);
+ ro?.unobserve(el, listener);
}
},
};
diff --git a/packages/opendesign/src/components/hooks/use-element-intersection.ts b/packages/opendesign/src/components/hooks/use-element-intersection.ts
index 30c20e713ba137099401df560ce5a78dfa459849..a381f10dbc5664faa12cfe2a6571003aee8eaf18 100644
--- a/packages/opendesign/src/components/hooks/use-element-intersection.ts
+++ b/packages/opendesign/src/components/hooks/use-element-intersection.ts
@@ -4,10 +4,9 @@ import type { IntersectionListenerT } from './use-intersection-observer';
import { isFunction } from '../_shared/is';
let io: ReturnType | null = null;
-export function useIntersectionObserverDirective({ listener, removeOnUnmounted }: {
- listener: IntersectionListenerT,
- removeOnUnmounted?: boolean
-}): { vIntersectionObserver: ObjectDirective } {
+export function useIntersectionObserverDirective({ listener, removeOnUnmounted }: { listener: IntersectionListenerT; removeOnUnmounted?: boolean }): {
+ vIntersectionObserver: ObjectDirective;
+} {
return {
vIntersectionObserver: {
beforeMount() {
@@ -15,14 +14,14 @@ export function useIntersectionObserverDirective({ listener, removeOnUnmounted }
},
mounted(el: HTMLElement) {
if (isFunction(listener)) {
- io?.addIntersectionListener(el, listener);
+ io?.observe(el, listener);
}
},
unmounted(el: HTMLElement) {
if (listener && removeOnUnmounted) {
- io?.removeIntersectionListener(el, listener);
+ io?.unobserve(el, listener);
}
},
- }
+ },
};
-}
\ No newline at end of file
+}
diff --git a/packages/opendesign/src/components/hooks/use-element-resize.ts b/packages/opendesign/src/components/hooks/use-element-resize.ts
index bfc633ed2fe143d2ff3619e2f35ae5413a5be61f..22b569da2b4c8e0eb1ddeec4eafac7a936ce82b9 100644
--- a/packages/opendesign/src/components/hooks/use-element-resize.ts
+++ b/packages/opendesign/src/components/hooks/use-element-resize.ts
@@ -12,14 +12,14 @@ export function useReiszeObserverDirective(onResize: ResizeListenerT): { vResize
},
mounted(el: HTMLElement) {
if (isFunction(onResize)) {
- ro?.addResizeListener(el, onResize);
+ ro?.observe(el, onResize);
}
},
unmounted(el: HTMLElement) {
if (onResize) {
- ro?.removeResizeListener(el, onResize);
+ ro?.unobserve(el, onResize);
}
},
- }
+ },
};
-}
\ No newline at end of file
+}
diff --git a/packages/opendesign/src/components/hooks/use-intersection-observer.ts b/packages/opendesign/src/components/hooks/use-intersection-observer.ts
index 8d20223190f153ab30d14ee9ffbac0eb1d169ee1..07d9366948094f8e3f8d7afb92e1302f65870466 100644
--- a/packages/opendesign/src/components/hooks/use-intersection-observer.ts
+++ b/packages/opendesign/src/components/hooks/use-intersection-observer.ts
@@ -2,29 +2,27 @@
import { isFunction } from '../_shared/is';
export type IntersectionListenerT = (entry: IntersectionObserverEntry) => void;
+
/**
- * 监听元素尺寸变化,
- * ele: 监听元素;
- * onResize: resize回调(entry: 尺寸变化元素,isFirst: 是否为初次监听时的回调);
+ * 监听池
+ * isFirst: 监听的第一次回调
*/
-export function useIntersectionObserver(
- element?: HTMLElement,
- listenerFn?: IntersectionListenerT,
-) {
- let el = element;
- let cb = listenerFn;
+const observerPool = new WeakMap<
+ HTMLElement,
+ {
+ element: HTMLElement;
+ callbacks: Array;
+ } | null
+>();
- /**
- * 监听池
- * isFirst: 监听的第一次回调
- */
- const observerPool = new Map
- } | null>();
+// 记录监听数量
+let record = 0;
- // 创建监听实例
- const instance = new IntersectionObserver((entries: IntersectionObserverEntry[]) => {
+// 创建监听实例
+let instance: ResizeObserver | null = null;
+
+function createObserver() {
+ return new IntersectionObserver((entries: IntersectionObserverEntry[]) => {
entries.forEach((entry) => {
const ele = entry.target as HTMLElement;
const ins = observerPool.get(ele);
@@ -33,10 +31,17 @@ export function useIntersectionObserver(
return;
}
- ins?.callbacks?.forEach(fn => fn(entry));
+ ins?.callbacks?.forEach((fn) => fn(entry));
});
});
+}
+/**
+ * 监听元素尺寸变化,
+ * ele: 监听元素;
+ * onResize: resize回调(entry: 尺寸变化元素,isFirst: 是否为初次监听时的回调);
+ */
+export function useIntersectionObserver() {
return {
/**
* 监听实例
@@ -48,24 +53,25 @@ export function useIntersectionObserver(
* listener: resize回调, 移除监听时需要指定该监听函数
* isFirst: 是否为初次监听时的回调
*/
- addIntersectionListener: (ele?: HTMLElement, listener?: IntersectionListenerT) => {
- el = ele || element;
- cb = listener || cb;
-
- if (!el || !cb || !isFunction(cb)) {
+ observe: (el?: HTMLElement, listener?: IntersectionListenerT) => {
+ if (!el || !isFunction(listener)) {
return null;
}
// 同一元素,只创建一次ResizeObserver,通过数组存储回调
const val = observerPool.get(el);
if (val) {
- val.callbacks.push(cb);
+ val.callbacks.push(listener);
} else {
+ if (!instance) {
+ instance = createObserver();
+ }
instance.observe(el);
+ record++;
observerPool.set(el, {
element: el,
- callbacks: [cb],
+ callbacks: [listener],
});
}
@@ -75,28 +81,29 @@ export function useIntersectionObserver(
* 移除监听
* listener: 要移除的监听函数,如果不传,则使用初始化时的回调
*/
- removeIntersectionListener: (ele?: HTMLElement, listener?: IntersectionListenerT) => {
- let fn = listener || cb;
- el = ele || element;
-
- if (!el || !fn) {
+ unobserve: (el?: HTMLElement, listener?: IntersectionListenerT) => {
+ if (!el || !isFunction(listener) || !instance) {
return;
}
const val = observerPool.get(el);
if (val) {
- const idx = val.callbacks.indexOf(fn);
+ const idx = val.callbacks.indexOf(listener);
val.callbacks.splice(idx, 1);
if (val.callbacks.length === 0) {
// 当el无监听时,销毁resizeObserver
instance.unobserve(el);
observerPool.delete(el);
- }
- if (observerPool.size === 0) {
- instance.disconnect();
+
+ // 无监听元素,就断开
+ record--;
+ if (record === 0) {
+ instance.disconnect();
+ instance = null;
+ }
}
}
- }
+ },
};
-};
\ No newline at end of file
+}
diff --git a/packages/opendesign/src/components/hooks/use-out-click.ts b/packages/opendesign/src/components/hooks/use-out-click.ts
index b2a8126eff7b6265c6bef6c93f7c115f719f402e..6ddb2e86c3a1e9f7c6fb3ac98b57db90f19079f1 100644
--- a/packages/opendesign/src/components/hooks/use-out-click.ts
+++ b/packages/opendesign/src/components/hooks/use-out-click.ts
@@ -1,11 +1,11 @@
interface handlerItemT {
- handler: () => void,
- exception?: (e: MouseEvent) => boolean
+ handler: () => void;
+ exception?: (e: MouseEvent) => boolean;
}
const elList = new Map>();
-function addOutClickListener(el: HTMLElement, fn: () => void, exception?: (e: MouseEvent) => boolean) {
+function addListener(el: HTMLElement, fn: () => void, exception?: (e: MouseEvent) => boolean) {
if (!elList.has(el)) {
elList.set(el, []);
}
@@ -14,18 +14,18 @@ function addOutClickListener(el: HTMLElement, fn: () => void, exception?: (e: Mo
if (handlers) {
handlers.push({
handler: fn,
- exception: exception
+ exception: exception,
});
}
}
-function removeOutClickListener(el: HTMLElement, listener?: () => void) {
+function removeListener(el: HTMLElement, listener?: () => void) {
if (listener) {
const handlers = elList.get(el);
if (!handlers) {
return;
}
- const idx = handlers.findIndex(item => item.handler === listener);
+ const idx = handlers.findIndex((item) => item.handler === listener);
if (idx > -1) {
handlers.splice(idx, 1);
}
@@ -38,7 +38,7 @@ export function useOutClick() {
window.addEventListener('mousedown', (e) => {
elList.forEach((handlers, el) => {
if (!el.contains(e.target as HTMLElement)) {
- handlers.forEach(item => {
+ handlers.forEach((item) => {
if (!item.exception || !item.exception(e)) {
item.handler();
}
@@ -48,7 +48,7 @@ export function useOutClick() {
});
return {
- addOutClickListener,
- removeOutClickListener,
+ addListener,
+ removeListener,
};
-}
\ No newline at end of file
+}
diff --git a/packages/opendesign/src/components/hooks/use-resize-observer.ts b/packages/opendesign/src/components/hooks/use-resize-observer.ts
index 70a5ec1b601cdae24abae32a854cf2ddb8237b1d..c6c536ccbad75148bb093e0f2caa7f27c03843cb 100644
--- a/packages/opendesign/src/components/hooks/use-resize-observer.ts
+++ b/packages/opendesign/src/components/hooks/use-resize-observer.ts
@@ -1,31 +1,27 @@
// import ResizeObserver from 'resize-observer-polyfill';
import { isFunction } from '../_shared/is';
-export type ResizeListenerT = (entry: ResizeObserverEntry, isFirst: boolean) => void;
/**
- * 监听元素尺寸变化,
- * ele: 监听元素;
- * onResize: resize回调(entry: 尺寸变化元素,isFirst: 是否为初次监听时的回调);
+ * 监听池
+ * isFirst: 监听的第一次回调
*/
-export function useResizeObserver(
- element?: HTMLElement,
- onResize?: ResizeListenerT,
-) {
- let el = element;
- let cb = onResize;
+const observerPool = new WeakMap<
+ HTMLElement,
+ {
+ element: HTMLElement;
+ isFirst: boolean; // 初次监听时会触发一次回调
+ callbacks: Array<(entry: ResizeObserverEntry, isFirst: boolean) => void>;
+ } | null
+>();
+
+// 记录监听数量
+let record = 0;
- /**
- * 监听池
- * isFirst: 监听的第一次回调
- */
- const observerPool = new Map void>
- } | null>();
+// 创建监听实例
+let instance: ResizeObserver | null = null;
- // 创建监听实例
- const instance = new ResizeObserver((entries: ResizeObserverEntry[]) => {
+function createObserver() {
+ return new ResizeObserver((entries: ResizeObserverEntry[]) => {
entries.forEach((entry) => {
const ele = entry.target as HTMLElement;
const ins = observerPool.get(ele);
@@ -33,14 +29,21 @@ export function useResizeObserver(
return;
}
- ins?.callbacks?.forEach(fn => fn(entry, ins.isFirst));
+ ins?.callbacks?.forEach((fn) => fn(entry, ins.isFirst));
if (ins.isFirst) {
ins.isFirst = false;
}
});
});
-
+}
+export type ResizeListenerT = (entry: ResizeObserverEntry, isFirst: boolean) => void;
+/**
+ * 监听元素尺寸变化,
+ * ele: 监听元素;
+ * onResize: resize回调(entry: 尺寸变化元素,isFirst: 是否为初次监听时的回调);
+ */
+export function useResizeObserver() {
return {
/**
* 监听实例
@@ -52,25 +55,26 @@ export function useResizeObserver(
* listener: resize回调, 移除监听时需要指定该监听函数
* isFirst: 是否为初次监听时的回调
*/
- addResizeListener: (ele?: HTMLElement, listener?: ResizeListenerT) => {
- el = ele || element;
- cb = listener || onResize;
-
- if (!el || !cb || !isFunction(cb)) {
+ observe: (el?: HTMLElement, listener?: ResizeListenerT) => {
+ if (!el || !isFunction(listener)) {
return null;
}
// 同一元素,只创建一次ResizeObserver,通过数组存储回调
const val = observerPool.get(el);
if (val) {
- val.callbacks.push(cb);
+ val.callbacks.push(listener);
} else {
+ if (!instance) {
+ instance = createObserver();
+ }
instance.observe(el);
+ record++;
observerPool.set(el, {
element: el,
- callbacks: [cb],
- isFirst: true
+ callbacks: [listener],
+ isFirst: true,
});
}
@@ -80,29 +84,30 @@ export function useResizeObserver(
* 移除监听
* listener: 要移除的监听函数,如果不传,则使用初始化时的onResize回调
*/
- removeResizeListener: (ele?: HTMLElement, listener?: ResizeListenerT) => {
-
- let fn = listener || cb;
- el = ele || element;
-
- if (!el || !fn) {
+ unobserve: (el?: HTMLElement, listener?: ResizeListenerT) => {
+ if (!el || !isFunction(listener) || !instance) {
return;
}
const val = observerPool.get(el);
if (val) {
- const idx = val.callbacks.indexOf(fn);
+ const idx = val.callbacks.indexOf(listener);
val.callbacks.splice(idx, 1);
+
+ // 当el无监听时,销毁resizeObserver
if (val.callbacks.length === 0) {
- // 当el无监听时,销毁resizeObserver
instance.unobserve(el);
observerPool.delete(el);
- }
- if (observerPool.size === 0) {
- instance.disconnect();
+
+ // 无监听元素,就断开
+ record--;
+ if (record === 0) {
+ instance.disconnect();
+ instance = null;
+ }
}
}
- }
+ },
};
-};
\ No newline at end of file
+}
diff --git a/packages/opendesign/src/components/link/OLink.vue b/packages/opendesign/src/components/link/OLink.vue
index c29be9b2920be8298e19f74583dab6bb57c4f78b..518b304e6b716adc0c3e8eee243f84f8d8ac8fa4 100644
--- a/packages/opendesign/src/components/link/OLink.vue
+++ b/packages/opendesign/src/components/link/OLink.vue
@@ -7,40 +7,38 @@ const props = defineProps(linkProps);
const emits = defineEmits<{ (e: 'click', val: MouseEvent): void }>();
const onClick = (e: MouseEvent) => {
- if (props.disabled) {
+ if (props.disabled || props.loading) {
e.preventDefault();
return;
}
- if (!props.href) {
- e.preventDefault();
- }
emits('click', e);
};
-
+
-
-
+
+
+
-
-
+
+
-
diff --git a/packages/opendesign/src/components/link/__demo__/LinkBasic.vue b/packages/opendesign/src/components/link/__demo__/LinkBasic.vue
index 2dc69c75994b6e7ca3cbb6ab05729c96f16c3edc..25dd2fe848cf12704a357a866ad188cc982877f4 100644
--- a/packages/opendesign/src/components/link/__demo__/LinkBasic.vue
+++ b/packages/opendesign/src/components/link/__demo__/LinkBasic.vue
@@ -28,39 +28,44 @@ const onLinkClick2 = () => {
普通链接
-
hoverable
+
hover-bg
+
hover-underline
禁用链接
success链接
-
hoverable
+
hover-bg
+
hover-underline
禁用链接
primary链接
-
hoverable
+
hover-bg
+
hover-underline
禁用链接
warning链接
-
hoverable
+
hover-bg
+
hover-underline
禁用链接
danger链接
-
hoverable
+
hover-bg
+
hover-underline
禁用链接
图标
- 图标链接1
- 图标链接2
+ icon-prefix
+ icon-suffix
- 自定义图标链接1
+ 自定义图标链接slot:iconPrefix
- 自定义图标链接2
+ 自定义图标链接slot:iconSuffix
点击事件
diff --git a/packages/opendesign/src/components/link/style/index.scss b/packages/opendesign/src/components/link/style/index.scss
index b4337165b6d27837b66a556757c92340aac55e9c..fc0afdfde65046f123d354fd1d88764a4f42851d 100644
--- a/packages/opendesign/src/components/link/style/index.scss
+++ b/packages/opendesign/src/components/link/style/index.scss
@@ -5,10 +5,12 @@
text-decoration: none;
padding: 0 2px;
color: var(--link-color);
+ display: inline-flex;
+ align-items: center;
&:not(.o-link-disabled) {
&:hover {
- text-decoration: underline;
+ text-decoration: none;
color: var(--link-color-hover);
text-underline-offset: 4px;
.o-link-icon.arrow svg {
@@ -21,6 +23,17 @@
}
}
}
+.o-link-hover-underline {
+ background: linear-gradient(0deg, var(--link-color-hover), var(--link-color-hover)) no-repeat right bottom;
+ background-size: 0 1px;
+ transition: background-size var(--o-easing-standard) var(--o-duration-m2);
+ &:not(.o-link-disabled) {
+ &:hover {
+ background-size: 100% 1px;
+ background-position-x: left;
+ }
+ }
+}
.o-link-disabled {
cursor: not-allowed;
@@ -28,20 +41,18 @@
color: var(--link-color-disabled);
}
-.o-link-icon {
+.o-link-icon-wrap {
+ display: flex;
+ align-items: center;
&.prefix {
margin-right: 2px;
}
&.suffix {
margin-left: 2px;
}
-
- &.arrow svg {
- transition: transform var(--o-duration-s) var(--o-easing-standard);
- }
}
-.o-link-hoverable {
+.o-link-hover-bg {
&:not(.is-disabled) {
&:hover {
text-decoration: none;
diff --git a/packages/opendesign/src/components/link/types.ts b/packages/opendesign/src/components/link/types.ts
index cc397dc32a073a07723211552d32097147247ea5..d847f691ee9be5aa41369e694266e68b7303b715 100644
--- a/packages/opendesign/src/components/link/types.ts
+++ b/packages/opendesign/src/components/link/types.ts
@@ -25,7 +25,7 @@ export const linkProps = {
*/
color: {
type: String as PropType,
- default: 'normal'
+ default: 'normal',
},
/**
* 是否禁用
@@ -40,15 +40,21 @@ export const linkProps = {
type: Boolean,
},
/**
- * 图标箭头
+ * 后缀图标
*/
- iconArrow: {
+ iconSuffix: {
type: Boolean,
},
/**
* hover时是否显示背景
*/
- hoverable: {
+ hoverBg: {
+ type: Boolean,
+ },
+ /**
+ * hover时是否下划线
+ */
+ hoverUnderline: {
type: Boolean,
},
};
diff --git a/packages/opendesign/src/components/loading/OLoading.vue b/packages/opendesign/src/components/loading/OLoading.vue
new file mode 100644
index 0000000000000000000000000000000000000000..cd957eb9998277ce12580c6285939836a438a19f
--- /dev/null
+++ b/packages/opendesign/src/components/loading/OLoading.vue
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ loadingLabel }}
+
+
+
+
+
+
+
diff --git a/packages/opendesign/src/components/loading/__demo__/IndexLoading.vue b/packages/opendesign/src/components/loading/__demo__/IndexLoading.vue
new file mode 100644
index 0000000000000000000000000000000000000000..529d30047f1c19d3bdca2a1996079ca55472004d
--- /dev/null
+++ b/packages/opendesign/src/components/loading/__demo__/IndexLoading.vue
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/packages/opendesign/src/components/loading/__demo__/LoadingBasic.vue b/packages/opendesign/src/components/loading/__demo__/LoadingBasic.vue
new file mode 100644
index 0000000000000000000000000000000000000000..61e91e85c24ecfc0ad048b931793fc7475050a57
--- /dev/null
+++ b/packages/opendesign/src/components/loading/__demo__/LoadingBasic.vue
@@ -0,0 +1,71 @@
+
+
+ 全局
+ show1:{{ show1 }}
+
+ 局部
+ show2:{{ show2 }}
+
+ 指令
+ show3:{{ show3 }}
+
+ 服务
+ service to body:{{ show3 }}
+ service to box:{{ show3 }}
+
+
+
diff --git a/packages/opendesign/src/components/loading/index.ts b/packages/opendesign/src/components/loading/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ab5b0fd23ec9f66aae42e3aa263a6c607c7723c4
--- /dev/null
+++ b/packages/opendesign/src/components/loading/index.ts
@@ -0,0 +1,14 @@
+import type { App } from 'vue';
+import _OLoading from './OLoading.vue';
+import { vLoading } from './vLoading';
+import useLoading from './useLoading';
+
+const OLoading = Object.assign(_OLoading, {
+ vLoading,
+ useLoading,
+ install(app: App) {
+ app.component(_OLoading.name, _OLoading);
+ },
+});
+
+export { OLoading, vLoading, useLoading };
diff --git a/packages/opendesign/src/components/loading/style/index.scss b/packages/opendesign/src/components/loading/style/index.scss
new file mode 100644
index 0000000000000000000000000000000000000000..d309bd256ff375ac110a3658b3c25a52c710276c
--- /dev/null
+++ b/packages/opendesign/src/components/loading/style/index.scss
@@ -0,0 +1,44 @@
+@use './var.scss';
+
+.o-loading {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ align-items: center;
+ justify-content: center;
+ z-index: var(--loading-z-index);
+ display: flex;
+ body > & {
+ position: fixed;
+ }
+}
+
+.o-loading-mask {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background-color: var(--loading-mask);
+ z-index: 0;
+}
+.o-loading-main {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--loading-color);
+}
+.o-loading-icon {
+ font-size: var(--loading-icon-size);
+ + .o-loading-label {
+ margin-left: 4px;
+ }
+}
+.o-loading-label {
+ font-size: var(--loading-txt-size);
+ line-height: var(--loading-txt-height);
+}
diff --git a/packages/opendesign/src/components/loading/style/index.ts b/packages/opendesign/src/components/loading/style/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..591b2fbd72dcd5e987362d645b561bd2fac42716
--- /dev/null
+++ b/packages/opendesign/src/components/loading/style/index.ts
@@ -0,0 +1,2 @@
+import '../../style';
+import './index.scss';
diff --git a/packages/opendesign/src/components/loading/style/var.scss b/packages/opendesign/src/components/loading/style/var.scss
new file mode 100644
index 0000000000000000000000000000000000000000..65c8304344caa2280bc3e29712089c91a30a6a4a
--- /dev/null
+++ b/packages/opendesign/src/components/loading/style/var.scss
@@ -0,0 +1,9 @@
+.o-loading {
+ --loading-color: var(--o-color-info1-inverse);
+ --loading-mask: var(--o-color-mask1);
+ --loading-icon-size: var(--o-icon_size-xs);
+ --loading-icon-color: var(--o-color-info2);
+ --loading-z-index: calc(var(--o-z-index-base) + 10);
+ --loading-txt-size: var(--o-font_size-text1);
+ --loading-txt-height: var(--o-line_height-text1);
+}
diff --git a/packages/opendesign/src/components/loading/types.ts b/packages/opendesign/src/components/loading/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1ac7decebf674f77d91c7cf6a0e3ba46a9a5d8b7
--- /dev/null
+++ b/packages/opendesign/src/components/loading/types.ts
@@ -0,0 +1,46 @@
+import { ExtractPropTypes, PropType } from 'vue';
+
+export const loadingProps = {
+ /**
+ * 是否显示
+ */
+ show: {
+ type: Boolean,
+ },
+ /**
+ * 是否在隐藏时unmout
+ */
+ unmountOnHide: {
+ type: Boolean,
+ default: true,
+ },
+ /**
+ * 过渡名称
+ */
+ transition: {
+ type: String,
+ default: 'o-zoom-fade',
+ },
+ /**
+ * 是否需要mask
+ */
+ mask: {
+ type: Boolean,
+ default: true,
+ },
+ /**
+ * 点击mask是否可关闭
+ */
+ maskClose: {
+ type: Boolean,
+ },
+ /**
+ * 挂载容器,默认为body
+ */
+ wrapper: {
+ type: [String, Object] as PropType,
+ default: undefined,
+ },
+};
+
+export type LoadingPropsT = ExtractPropTypes;
diff --git a/packages/opendesign/src/components/loading/useLoading.ts b/packages/opendesign/src/components/loading/useLoading.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6dc6cfb2452379626b21b86f8cea93fbc57adc24
--- /dev/null
+++ b/packages/opendesign/src/components/loading/useLoading.ts
@@ -0,0 +1,39 @@
+import { createVNode, render, isRef, Ref, watch, ComponentInternalInstance } from 'vue';
+import OLoading from './OLoading.vue';
+import { LoadingPropsT } from './types';
+
+const initLoading = (opt?: LoadingPropsT, el?: HTMLElement) => {
+ const vnode = createVNode(OLoading, Object.assign(opt || {}, { wrapper: el }));
+ if (el) {
+ render(vnode, el);
+ }
+ return vnode.component;
+};
+
+const useLoading = (opt?: LoadingPropsT, wrap: Ref | HTMLElement | string = 'body') => {
+ let instance: ComponentInternalInstance | null = null;
+ if (typeof wrap === 'string') {
+ const el = document.querySelector(wrap);
+ if (el) {
+ instance = initLoading(opt, el as HTMLElement);
+ }
+ } else if ((wrap as HTMLElement).nodeType === 1) {
+ instance = initLoading(opt, wrap as HTMLElement);
+ } else if (isRef(wrap)) {
+ watch(
+ () => wrap.value,
+ (el) => {
+ instance = initLoading(opt, el);
+ }
+ );
+ }
+ return {
+ toggle() {
+ if (instance) {
+ instance.exposed?.toggle();
+ }
+ },
+ };
+};
+
+export default useLoading;
diff --git a/packages/opendesign/src/components/loading/vLoading.ts b/packages/opendesign/src/components/loading/vLoading.ts
new file mode 100644
index 0000000000000000000000000000000000000000..79f32de208ff3a0cdafa66592d2c55fc2e691e25
--- /dev/null
+++ b/packages/opendesign/src/components/loading/vLoading.ts
@@ -0,0 +1,21 @@
+import { ObjectDirective, DirectiveBinding, createVNode, render } from 'vue';
+import OLoading from './OLoading.vue';
+
+const vLoading: ObjectDirective = {
+ mounted(el: HTMLElement, binding: DirectiveBinding) {
+ const vnode = createVNode(OLoading, { show: binding.value });
+ render(vnode, el);
+ const vm = vnode.component;
+ (el as any).__loading_data = {
+ instance: vm,
+ };
+ },
+ updated(el: HTMLElement, binding: DirectiveBinding) {
+ const data = (el as any).__loading_data;
+ if (data) {
+ data.instance.exposed.toggle(binding.value);
+ }
+ },
+};
+
+export { vLoading };
diff --git a/packages/opendesign/src/components/popover/OPopover.vue b/packages/opendesign/src/components/popover/OPopover.vue
index 201b7e943d1746fddcad7b0983d495b7f5f7e60e..16ef118bdbb95d0c450d4d337c4b63687468c81b 100644
--- a/packages/opendesign/src/components/popover/OPopover.vue
+++ b/packages/opendesign/src/components/popover/OPopover.vue
@@ -29,8 +29,8 @@ const targetElRef = ref(null);
:trigger="props.trigger"
:target="props.target || targetElRef"
:wrapper="props.wrapper"
- wrap-class="o-popover-wrap"
- :anchor-class="props.anchor ? props.anchorClass : ''"
+ :wrap-class="props.wrapClass ?? 'o-popover-wrap'"
+ :anchor-class="props.anchor ? props.anchorClass ?? 'o-popover-anchor' : ''"
:unmount-on-hide="props.unmountOnHide"
@update:visible="updateVisible"
>
diff --git a/packages/opendesign/src/components/popover/types.ts b/packages/opendesign/src/components/popover/types.ts
index a04d4b8ac966977a79c105feb0993392fc750403..73166c378a6a4a5b4c503aa8fe651669a4ac6377 100644
--- a/packages/opendesign/src/components/popover/types.ts
+++ b/packages/opendesign/src/components/popover/types.ts
@@ -2,7 +2,7 @@ import { ExtractPropTypes } from 'vue';
import { popupProps } from '../popup/types';
-const { position, trigger, target, visible, wrapper, unmountOnHide, anchorClass } = popupProps;
+const { position, trigger, target, visible, wrapper, unmountOnHide, anchorClass, wrapClass } = popupProps;
export const popoverProps = {
position,
@@ -12,6 +12,7 @@ export const popoverProps = {
wrapper,
unmountOnHide,
anchorClass,
+ wrapClass,
/**
* 距离触发元素的偏移量
*/
@@ -28,4 +29,4 @@ export const popoverProps = {
},
};
-export type PopupPropsT = ExtractPropTypes;
\ No newline at end of file
+export type PopupPropsT = ExtractPropTypes;
diff --git a/packages/opendesign/src/components/popup/OPopup.vue b/packages/opendesign/src/components/popup/OPopup.vue
index 3adb623480162cd3b42039ef086019e96ac50fbe..82b50585ebce11116561ae08ac965827d6dc2155 100644
--- a/packages/opendesign/src/components/popup/OPopup.vue
+++ b/packages/opendesign/src/components/popup/OPopup.vue
@@ -107,7 +107,7 @@ const bindTargetEvent = (el: HTMLElement | null) => {
});
if (props.hideWhenTargetInvisible) {
- io?.addIntersectionListener(targetEl, onTargetInterscting);
+ io?.observe(targetEl, onTargetInterscting);
}
};
@@ -118,10 +118,10 @@ onUnmounted(() => {
});
// 销毁popup 的 resize监听
if (wrapperEl.value) {
- ro?.removeResizeListener(wrapperEl.value, onResize);
+ ro?.unobserve(wrapperEl.value, onResize);
}
if (targetEl) {
- ro?.removeResizeListener(targetEl, onResize);
+ ro?.unobserve(targetEl, onResize);
}
});
@@ -219,7 +219,7 @@ const updateVisible = (isVisible?: boolean, delay?: number) => {
toMount.value = true;
if (props.hideWhenTargetInvisible && targetEl) {
- io?.addIntersectionListener(targetEl, onTargetInterscting);
+ io?.observe(targetEl, onTargetInterscting);
}
}
};
@@ -297,7 +297,7 @@ watch(popupRef, (popEl) => {
});
// 监听targetEL尺寸变化
- ro?.addResizeListener(targetEl, (en: ResizeObserverEntry, isFirst: boolean) => {
+ ro?.observe(targetEl, (en: ResizeObserverEntry, isFirst: boolean) => {
if (props.adjustMinWidth) {
popStyle.minWidth = `${targetEl?.offsetWidth}px`;
} else if (props.adjustWidth) {
@@ -309,7 +309,7 @@ watch(popupRef, (popEl) => {
if (wrapperEl.value) {
// 监听warpper尺寸变化
- ro?.addResizeListener(wrapperEl.value, onResize);
+ ro?.observe(wrapperEl.value, onResize);
}
} else {
/**
@@ -318,11 +318,11 @@ watch(popupRef, (popEl) => {
handles.forEach((hl) => hl());
if (wrapperEl.value) {
- ro?.removeResizeListener(wrapperEl.value, onResize);
+ ro?.unobserve(wrapperEl.value, onResize);
}
if (targetEl) {
- ro?.removeResizeListener(targetEl, onResize);
- io?.removeIntersectionListener(targetEl, onTargetInterscting);
+ ro?.unobserve(targetEl, onResize);
+ io?.unobserve(targetEl, onTargetInterscting);
isTargetInViewport.value = true;
}
}
diff --git a/packages/opendesign/src/components/popup/popup.ts b/packages/opendesign/src/components/popup/popup.ts
index 4f0190bc0ad44fa32a5d2b3508e123d47d531c7e..268a52d5358c49502355342e3e4a92b806de6404 100644
--- a/packages/opendesign/src/components/popup/popup.ts
+++ b/packages/opendesign/src/components/popup/popup.ts
@@ -6,24 +6,24 @@ import type { PopupPositionT, PopupTriggerT } from './types';
import { useOutClick } from '../hooks/use-out-click';
interface Pos {
- left: number,
- top: number
+ left: number;
+ top: number;
}
interface DomContentRect {
- left: number,
- right: number,
- top: number,
- bottom: number,
+ left: number;
+ right: number;
+ top: number;
+ bottom: number;
}
interface AnchorPosition {
- left?: number,
- top?: number,
- right?: number,
- bottom?: number
+ left?: number;
+ top?: number;
+ right?: number;
+ bottom?: number;
}
-type ElementSize = ReturnType
+type ElementSize = ReturnType;
// 获取wrapper content box范围,因为绝对定位需要排除border
function getWrapperContentRect(wrapperEl: HTMLElement, wrapperRect?: DOMRect): DomContentRect {
@@ -33,7 +33,7 @@ function getWrapperContentRect(wrapperEl: HTMLElement, wrapperRect?: DOMRect): D
left: rect.left + left,
top: rect.top + top,
right: rect.right - right,
- bottom: rect.bottom - bottom
+ bottom: rect.bottom - bottom,
};
}
@@ -43,62 +43,62 @@ function getPopupViewOffset(
t: DOMRect,
p: DOMRect,
{
- offset = 0
+ offset = 0,
}: {
- offset?: number
- } = {}): Pos {
+ offset?: number;
+ } = {}
+): Pos {
const formula: {
- [k in PopupPositionT]: { left: number, top: number }
+ [k in PopupPositionT]: { left: number; top: number };
} = {
top: {
left: t.left + t.width / 2 - p.width / 2,
- top: t.top - p.height - offset
+ top: t.top - p.height - offset,
},
tl: {
left: t.left,
- top: t.top - p.height - offset
+ top: t.top - p.height - offset,
},
tr: {
left: t.right - p.width,
- top: t.top - p.height - offset
+ top: t.top - p.height - offset,
},
bottom: {
left: t.left + t.width / 2 - p.width / 2,
- top: t.bottom + offset
+ top: t.bottom + offset,
},
bl: {
left: t.left,
- top: t.bottom + offset
+ top: t.bottom + offset,
},
br: {
left: t.right - p.width,
- top: t.bottom + offset
+ top: t.bottom + offset,
},
left: {
left: t.left - p.width - offset,
- top: t.top + t.height / 2 - p.height / 2
+ top: t.top + t.height / 2 - p.height / 2,
},
lt: {
left: t.left - p.width - offset,
- top: t.top
+ top: t.top,
},
lb: {
left: t.left - p.width - offset,
- top: t.bottom - p.height
+ top: t.bottom - p.height,
},
right: {
left: t.right + offset,
- top: t.top + t.height / 2 - p.height / 2
+ top: t.top + t.height / 2 - p.height / 2,
},
rt: {
left: t.right + offset,
- top: t.top
+ top: t.top,
},
rb: {
left: t.right + offset,
- top: t.bottom - p.height
-
- }
+ top: t.bottom - p.height,
+ },
};
return formula[position] || { left: 0, top: 0 };
}
@@ -109,7 +109,7 @@ function getWrapperViewEdge(popupSize: ElementSize, wrapperRect?: DomContentRect
left: 0,
right: window.innerWidth - popupSize.width,
top: 0,
- bottom: window.innerHeight - popupSize.height
+ bottom: window.innerHeight - popupSize.height,
};
if (!wrapperRect) {
@@ -131,7 +131,7 @@ function getPopupEdge(popupSize: ElementSize, targetRect: DOMRect, anchorOffset:
left: left - width + anchorOffset,
top: top - height + anchorOffset,
right: right - anchorOffset,
- bottom: bottom - anchorOffset
+ bottom: bottom - anchorOffset,
};
}
@@ -144,12 +144,12 @@ function getPopupWrapOffset(pos: Pos, wrapperEl: HTMLElement | null, wrapperCont
if (wrapperContentRect) {
return {
left: pos.left + cs.scrollLeft - wrapperContentRect.left,
- top: pos.top + cs.scrollTop - wrapperContentRect.top
+ top: pos.top + cs.scrollTop - wrapperContentRect.top,
};
}
return {
left: pos.left + cs.scrollLeft,
- top: pos.top + cs.scrollTop
+ top: pos.top + cs.scrollTop,
};
}
@@ -217,7 +217,7 @@ function adjustPosition(position: PopupPositionT, direction: DirectionT) {
return 'rb';
}
return p;
- }
+ },
};
const fn = fixFn[direction];
@@ -234,10 +234,10 @@ function adjustOffset(
wRect?: DomContentRect,
{
anchorOffset,
- offset
+ offset,
}: {
- anchorOffset?: number,
- offset?: number
+ anchorOffset?: number;
+ offset?: number;
} = {}
) {
const { top, left } = popupPosition;
@@ -298,7 +298,7 @@ function getAnchorOffset(position: PopupPositionT, tRect: DOMRect, popupStyle: P
const { left: pl, top: pt } = popupStyle;
if (['top', 'tl', 'tr', 'bottom', 'bl', 'br'].includes(position)) {
- let l = tRect.left + (tRect.width / 2) - pl;
+ let l = tRect.left + tRect.width / 2 - pl;
if (l > popupSize.width - limit) {
l = popupSize.width - limit;
@@ -314,7 +314,7 @@ function getAnchorOffset(position: PopupPositionT, tRect: DOMRect, popupStyle: P
pos.top = 0;
}
} else if (['left', 'lt', 'lb', 'right', 'rt', 'rb'].includes(position)) {
- let t = tRect.top + (tRect.height / 2) - pt;
+ let t = tRect.top + tRect.height / 2 - pt;
if (t > popupSize.height - limit) {
t = popupSize.height - limit;
@@ -331,18 +331,24 @@ function getAnchorOffset(position: PopupPositionT, tRect: DOMRect, popupStyle: P
}
}
-
return pos;
}
// 处理popup位置
-export function calcPopupStyle(popupEl: HTMLElement, targetEl: HTMLElement, position: PopupPositionT,
+export function calcPopupStyle(
+ popupEl: HTMLElement,
+ targetEl: HTMLElement,
+ position: PopupPositionT,
{
- adaptive = true, anchorOffset = 8, offset = 8
+ adaptive = true,
+ anchorOffset = 8,
+ offset = 8,
}: {
- adaptive?: boolean, anchorOffset?: number, offset?: number
- } = {}) {
-
+ adaptive?: boolean;
+ anchorOffset?: number;
+ offset?: number;
+ } = {}
+) {
const tRect = targetEl.getBoundingClientRect();
const pRect = popupEl.getBoundingClientRect();
@@ -357,7 +363,7 @@ export function calcPopupStyle(popupEl: HTMLElement, targetEl: HTMLElement, posi
return {
popupStyle,
position,
- anchorStyle
+ anchorStyle,
};
}
const wrapperRect = wrapperEl.getBoundingClientRect();
@@ -383,9 +389,9 @@ export function calcPopupStyle(popupEl: HTMLElement, targetEl: HTMLElement, posi
return {
position: fixedPosition,
popupStyle,
- anchorStyle
+ anchorStyle,
};
-};
+}
// 监听元素的触发事件
export function bindTrigger(
@@ -394,16 +400,16 @@ export function bindTrigger(
triggers: PopupTriggerT[],
{
updateFn,
- hoverDelay = 100
+ hoverDelay = 100,
}: {
- updateFn: (isVisible?: boolean, delay?: number) => void,
- hoverDelay?: number
+ updateFn: (isVisible?: boolean, delay?: number) => void;
+ hoverDelay?: number;
}
) {
if (!el) {
return [];
}
- const { addOutClickListener, removeOutClickListener } = useOutClick();
+ const outClick = useOutClick();
const listeners: Array<() => void> = [];
@@ -437,7 +443,7 @@ export function bindTrigger(
click: () => {
el?.addEventListener('click', toggleFn);
- addOutClickListener(el, hideFn, (e: MouseEvent) => {
+ outClick.addListener(el, hideFn, (e: MouseEvent) => {
return !!popupRef.value?.contains(e.target as HTMLElement);
});
@@ -445,7 +451,7 @@ export function bindTrigger(
el?.removeEventListener('click', toggleFn);
});
listeners.push(() => {
- removeOutClickListener(el, hideFn);
+ outClick.removeListener(el, hideFn);
});
},
focus: () => {
@@ -464,7 +470,7 @@ export function bindTrigger(
};
el?.addEventListener('contextmenu', fn);
- addOutClickListener(el, hideFn, (e: MouseEvent) => {
+ outClick.addListener(el, hideFn, (e: MouseEvent) => {
return !!popupRef.value?.contains(e.target as HTMLElement);
});
@@ -473,10 +479,10 @@ export function bindTrigger(
});
listeners.push(() => {
- removeOutClickListener(el, hideFn);
+ outClick.removeListener(el, hideFn);
});
},
- none: () => { }
+ none: () => {},
};
triggers.forEach((tr: PopupTriggerT) => {
@@ -484,7 +490,7 @@ export function bindTrigger(
});
return listeners;
-};
+}
export function getTransformOrigin(position: PopupPositionT) {
let left = '0px';
@@ -505,4 +511,4 @@ export function getTransformOrigin(position: PopupPositionT) {
top,
left,
};
-}
\ No newline at end of file
+}
diff --git a/packages/opendesign/src/components/popup/types.ts b/packages/opendesign/src/components/popup/types.ts
index 7b3552a6485f664be5fe95f5daa58e5fc5aa0df4..63cf4470df0b9b939ef3f2bbe2c8c3e5cbeacef0 100644
--- a/packages/opendesign/src/components/popup/types.ts
+++ b/packages/opendesign/src/components/popup/types.ts
@@ -67,7 +67,7 @@ export const popupProps = {
*/
anchorClass: {
type: String,
- default: '',
+ default: undefined,
},
/**
* 是否在popup隐藏时unmout
@@ -88,7 +88,7 @@ export const popupProps = {
*/
wrapClass: {
type: String,
- default: '',
+ default: undefined,
},
/**
* popup最小宽度设置为触发元素宽度
@@ -113,4 +113,4 @@ export const popupProps = {
},
};
-export type PopupPropsT = ExtractPropTypes;
\ No newline at end of file
+export type PopupPropsT = ExtractPropTypes;
diff --git a/packages/opendesign/src/components/slides/OSlideItem.vue b/packages/opendesign/src/components/slides/OSlideItem.vue
index d4a662951e55f93b027c2a48d340a6f053729771..7199667213297ad4299d599ba9c20e7d2ce7e323 100644
--- a/packages/opendesign/src/components/slides/OSlideItem.vue
+++ b/packages/opendesign/src/components/slides/OSlideItem.vue
@@ -1,6 +1,11 @@
-
+
-
+
diff --git a/packages/opendesign/src/components/slides/OSlides.vue b/packages/opendesign/src/components/slides/OSlides.vue
index 34d898d56e3878b5be06af7fa803f537c9082045..a15f9cc1bbc5c185ecd4cd59f9e2848af4f1306d 100644
--- a/packages/opendesign/src/components/slides/OSlides.vue
+++ b/packages/opendesign/src/components/slides/OSlides.vue
@@ -1,13 +1,18 @@
-
-
-
+
@@ -261,7 +185,7 @@ onUnmounted(() => {
-
+
diff --git a/packages/opendesign/src/components/slides/__demo__/SlidesBasic.vue b/packages/opendesign/src/components/slides/__demo__/SlidesBasic.vue
index f089c0d28306019dc3da3160553024944b69745a..4778fe493a9ec47db77b10d591a00c78f816b0ca 100644
--- a/packages/opendesign/src/components/slides/__demo__/SlidesBasic.vue
+++ b/packages/opendesign/src/components/slides/__demo__/SlidesBasic.vue
@@ -9,41 +9,92 @@ const slides = [
];
const onChange = (now: number, last: number) => {
- console.log(now, last);
+ console.log('slides changed', now, last);
};
基本
-
diff --git a/packages/opendesign/src/components/slides/gallery.ts b/packages/opendesign/src/components/slides/gallery.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b7bd23d3fdf19cce3aadf3afe1b3bb147e6a10ce
--- /dev/null
+++ b/packages/opendesign/src/components/slides/gallery.ts
@@ -0,0 +1,156 @@
+interface SlideItemT {
+ el: HTMLElement;
+ width: number;
+ left: number;
+}
+interface ContainerT {
+ el: HTMLElement;
+ width: number;
+}
+type AlignT = 'center' | 'left';
+
+export interface GallerySlidesT {
+ active: (slideIndex: number) => Promise
;
+ loopRange: () => void;
+ transformX: (value: number, animate: boolean) => Promise;
+}
+export interface GallerySlidesOptionT {
+ alignType: 'center' | 'left';
+ onChange?: () => void;
+}
+
+let resolveArr: ((value: null | number) => void)[] = [];
+export default class GallerySlides {
+ container: ContainerT;
+ slideList: SlideItemT[];
+ total: number;
+ alignType: AlignT;
+ moveValue: number;
+ currentIndex: number;
+ isChanging: boolean;
+ waitAnimation: (() => void) | null;
+ constructor(slideElList: HTMLElement[], slideContainer: HTMLElement, activeIndex: number, options?: GallerySlidesOptionT) {
+ const { alignType = 'center' } = options || {};
+ this.total = slideElList.length;
+ this.waitAnimation = null;
+
+ slideContainer.addEventListener('transitionend', () => {
+ slideContainer.style.willChange = '';
+ slideContainer.classList.remove('is-animating');
+ console.log('animate end');
+
+ this.loopRange();
+ this.isChanging = false;
+
+ if (resolveArr.length > 1) {
+ resolveArr.forEach((fn) => fn(null));
+ resolveArr = [];
+ }
+ });
+ slideContainer.addEventListener('transitionstart', () => {
+ slideContainer.style.willChange = 'transform';
+ });
+
+ let s = 0;
+ this.slideList = slideElList.map((el) => {
+ const w = el.clientWidth;
+ const l = s;
+ el.style.left = `${l}px`;
+
+ s += w;
+ return {
+ el,
+ width: el.clientWidth,
+ left: l,
+ };
+ });
+
+ this.container = {
+ el: slideContainer,
+ width: slideContainer.clientWidth,
+ };
+
+ this.alignType = alignType;
+ this.moveValue = 0;
+ this.currentIndex = -1;
+ this.isChanging = false;
+
+ this.active(activeIndex, false);
+ this.loopRange();
+ }
+ active(slideIndex: number, animate = true): Promise {
+ if (this.total === 0 || this.isChanging || this.currentIndex === slideIndex) {
+ return Promise.resolve(null);
+ }
+ this.isChanging = animate;
+ this.currentIndex = slideIndex;
+ const slide = this.slideList[slideIndex];
+ if (!slide) {
+ return Promise.resolve(null);
+ }
+ const { width: cw } = this.container;
+ const { width: sw, left: sl } = slide;
+ if (this.alignType === 'center') {
+ return this.transformX((cw - sw) / 2 - sl, animate);
+ }
+ return Promise.resolve(slideIndex);
+ }
+ loopRange() {
+ const cidx = this.currentIndex;
+ const half = (this.total - 1) / 2;
+
+ const orderSlideList = [];
+ const tm = [];
+ for (let i = cidx; i <= cidx + Math.ceil(half); i++) {
+ if (i < this.total) {
+ orderSlideList.push(this.slideList[i]);
+ tm.push(i);
+ } else {
+ orderSlideList.push(this.slideList[i - this.total]);
+ tm.push(i - this.total);
+ }
+ }
+ for (let i = cidx - 1; i >= cidx - Math.floor(half); i--) {
+ if (i >= 0) {
+ orderSlideList.unshift(this.slideList[i]);
+ tm.unshift(i);
+ } else {
+ orderSlideList.unshift(this.slideList[this.total + i]);
+ tm.unshift(this.total + i);
+ }
+ }
+ let s = 0;
+ const { left: oldLeft } = this.slideList[cidx];
+
+ orderSlideList.forEach((item) => {
+ const { el, width } = item;
+
+ el.style.left = `${s}px`;
+ item.left = s;
+ s += width;
+ });
+
+ const { left: newLeft } = this.slideList[cidx];
+ const d = newLeft - oldLeft;
+
+ this.transformX(this.moveValue - d, false);
+ }
+ transformX(value: number, animate: boolean = true): Promise {
+ return new Promise((resolve) => {
+ this.moveValue = value;
+
+ const { el } = this.container;
+
+ if (animate === true) {
+ el.classList.add('is-animating');
+ }
+
+ el.style.transform = `translate3d(${value}px,0,0)`;
+ if (animate) {
+ resolveArr.push(resolve);
+ } else {
+ resolve(null);
+ }
+ });
+ }
+}
diff --git a/packages/opendesign/src/components/slides/provide.ts b/packages/opendesign/src/components/slides/provide.ts
index e70f164f98ac91627b607011c96b21206a0d10a6..8448b55c489ae77cd05bf0e910273027f25f64bc 100644
--- a/packages/opendesign/src/components/slides/provide.ts
+++ b/packages/opendesign/src/components/slides/provide.ts
@@ -1,10 +1,6 @@
import { InjectionKey, Ref } from 'vue';
-
export const slidesInjectKey: InjectionKey<{
- currentIndex: Ref
- // Ref: Ref,
- // updateValue: (value: string | number, navEl: HTMLElement | null) => void,
- // onDeletePane: (value: string | number, evt: MouseEvent) => void,
+ currentIndex?: Ref;
+ type: 'carousel' | 'switch' | 'gallery';
}> = Symbol('provide-slides');
-
diff --git a/packages/opendesign/src/components/slides/style/index.scss b/packages/opendesign/src/components/slides/style/index.scss
index 04ddbb072729a1c7b6c8b54030e2817c9a710653..baeec9c7d3ab775ec7003b5442347ab81dd0e2f9 100644
--- a/packages/opendesign/src/components/slides/style/index.scss
+++ b/packages/opendesign/src/components/slides/style/index.scss
@@ -3,7 +3,6 @@
.o-slides {
position: relative;
visibility: hidden;
- overflow: hidden;
}
.o-slides-visible {
visibility: visible;
@@ -60,105 +59,74 @@
left: 50%;
transform: translate3d(-50%, 0, 0);
}
-.o-slides-indicator-dot {
+.o-slides-indicator-bar {
width: 48px;
- height: 3px;
- background-color: var(--slides-indicator-bg-color);
+ height: 12px;
margin: 4px;
- border-radius: 3px;
- opacity: 0.3;
cursor: pointer;
-}
-.o-slides-indicator-dot-active {
- opacity: 1;
- background-color: var(--slides-indicator-bg-color-active);
-}
-
-// slide
-.o-slides-type-switch {
+ position: relative;
+ border-radius: 20px;
overflow: hidden;
- .o-slide-item {
+ &::before {
+ content: '';
position: absolute;
left: 0;
- right: 0;
+ top: 50%;
width: 100%;
- height: 100%;
- overflow: hidden;
- z-index: 1;
- opacity: 0;
- }
- .o-slide-item-animating {
- opacity: 1;
- }
- .o-slide-item-active {
- z-index: 5;
- opacity: 1;
- }
-}
-
-.o-slide-item-in {
- z-index: 5;
- opacity: 1;
- animation: slides-slide-x-in var(--o-duration-l) var(--o-easing-standard-in);
-}
-.o-slide-item-out {
- z-index: 3;
- animation: slides-slide-x-out var(--o-duration-l) var(--o-easing-standard-in);
-}
-.o-slide-item-in-reverse {
- z-index: 5;
- animation: slides-slide-x-in-reverse var(--o-duration-l) var(--o-easing-standard-in);
-}
-.o-slide-item-out-reverse {
- z-index: 3;
- animation: slides-slide-x-out-reverse var(--o-duration-l) var(--o-easing-standard-in);
-}
-
-@keyframes slides-slide-x-in {
- 0% {
- transform: translate3d(100%, 0, 0);
- }
- 100% {
- transform: translate3d(0, 0, 0);
+ height: var(--slides-indicator-height);
+ background-color: var(--slides-indicator-bg-color);
+ transform: translateY(-50%);
+ opacity: 0.3;
+ transition: opacity var(--o-duration-m2) var(--o-easing-standard);
}
-}
-@keyframes slides-slide-x-in-reverse {
- 0% {
- transform: translate3d(-100%, 0, 0);
+ &:hover {
+ &::before {
+ opacity: 0.6;
+ }
}
- 100% {
- transform: translate3d(0, 0, 0);
+ &::after {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 50%;
+ width: 100%;
+ height: var(--slides-indicator-height);
+ background-color: var(--slides-indicator-bg-color);
+ transform: translateY(-50%) scaleX(0);
}
}
-@keyframes slides-slide-x-out {
- 0% {
- transform: translate3d(0, 0, 0);
+@keyframes grow {
+ from {
+ transform: translateY(-50%) scaleX(0);
}
- 100% {
- transform: translate3d(-100%, 0, 0);
+ to {
+ transform: translateY(-50%) scaleX(1);
}
}
-@keyframes slides-slide-x-out-reverse {
- 0% {
- transform: translate3d(0, 0, 0);
+.o-slides-indicator-bar-active {
+ &::after {
+ transform: translateY(-50%) scaleX(1);
+ transform-origin: left center;
}
- 100% {
- transform: translate3d(100%, 0, 0);
+ &.is-autoplay::after {
+ animation: grow var(--slides-interval, '5000ms') var(--o-easing-linear);
}
}
-.o-slides-type-carousel {
- transition: transform var(--o-duration-l) var(--o-easing-emphasized);
- display: flex;
- width: 100%;
+// gallery
+
+.o-slide-container-gallery {
height: 100%;
- .o-slide-item {
- flex-shrink: 0;
- flex-grow: 0;
- width: 100%;
- height: 100%;
- overflow: hidden;
- z-index: 1;
- margin-right: var(--slide-gap-x);
+ position: absolute;
+ width: 100%;
+ display: flex;
+ flex-wrap: nowrap;
+ &.is-animating {
+ transition: transform var(--o-duration-l) var(--o-easing-emphasized);
}
}
+.o-slide-gallery {
+ position: absolute;
+ left: 0;
+ flex-shrink: 0;
+}
diff --git a/packages/opendesign/src/components/slides/style/var.scss b/packages/opendesign/src/components/slides/style/var.scss
index 7fd24baba73ee6bf5865bdf068963c9d6db70e1b..23849a211472beda3196f4bbdea4f8696594a074 100644
--- a/packages/opendesign/src/components/slides/style/var.scss
+++ b/packages/opendesign/src/components/slides/style/var.scss
@@ -10,6 +10,7 @@
--slides-arrow-bg-color: rgba(var(--o-gray-1), 0.3);
--slides-arrow-bg-color-hover: rgba(var(--o-gray-1), 0.3);
// indicator
+ --slides-indicator-height: 3px;
--slides-indicator-opacity: 0.3;
--slides-indicator-opacity-active: 1;
--slides-indicator-bg-color: var(--o-color-control-light);
diff --git a/packages/opendesign/src/components/slides/types.ts b/packages/opendesign/src/components/slides/types.ts
index 9b3a169107f709e7bc14f92cc4890b8f1d54f81c..6cf96d34e21bdd33be2ceeb543e83a99c1fd63d7 100644
--- a/packages/opendesign/src/components/slides/types.ts
+++ b/packages/opendesign/src/components/slides/types.ts
@@ -11,48 +11,54 @@ export const slidesProps = {
* 样式类型
*/
type: {
- type: String as PropType<'carousel'|'switch'>,
- default: 'switch'
+ type: String as PropType<'gallery'>,
+ default: 'gallery',
},
/**
* 自动播放
*/
autoPlay: {
- type: Boolean
+ type: Boolean,
},
/**
* 自动播放间隔
*/
interval: {
type: Number,
- default: 5000
+ default: 5000,
},
/**
* 是否显示切换箭头
*/
arrow: {
type: Boolean,
- default: true
+ default: true,
},
/**
* 设置切换箭头容器类
*/
arrowWrapClass: {
- type: String
+ type: String,
},
/**
* 是否显示指示器
*/
indicator: {
type: Boolean,
- default: true
+ default: true,
},
/**
* 设置指示器容器类
*/
indicatorWrapClass: {
- type: String
- }
+ type: String,
+ },
+ /**
+ * 是否无限切换,使用下一张补位
+ */
+ loop: {
+ type: Boolean,
+ },
};
export type SlidesPropsT = ExtractPropTypes;
diff --git a/packages/opendesign/src/components/style/animation.scss b/packages/opendesign/src/components/style/animation.scss
index 619bff4d6acb9d687552d8cfe00adc7d16c26757..0553eef0d6b04ed48b6df583e317f04983d53ecc 100644
--- a/packages/opendesign/src/components/style/animation.scss
+++ b/packages/opendesign/src/components/style/animation.scss
@@ -24,10 +24,10 @@
}
.o-zoom-fade-enter-active {
- animation: o-zoom-fade-in var(--o-duration-m1);
+ animation: o-zoom-fade-in var(--o-duration-m1) var(--o-easing-standard-in);
}
.o-zoom-fade-leave-active {
- animation: o-zoom-fade-in var(--o-duration-s) reverse;
+ animation: o-zoom-fade-in var(--o-duration-s) var(--o-easing-standard-out) reverse;
}
@keyframes o-fade-in {
@@ -39,6 +39,13 @@
}
}
+.o-fade-in-enter-active {
+ animation: o-fade-in var(--o-duration-m1) var(--o-easing-standard-in);
+}
+.o-fade-in-leave-active {
+ animation: o-fade-in var(--o-duration-m1) var(--o-easing-standard-out) reverse;
+}
+
@keyframes o-fade-up {
0% {
transform: translateY(10px);
@@ -50,22 +57,9 @@
}
}
-.o-fade-up-enter-enter-active {
- animation: o-fade-up var(--o-duration-s) var(--o-easing-standard-out);
-}
-.o-fade-in-enter-enter-active {
- animation: o-fade-in var(--o-duration-m1) var(--o-easing-standard-in);
-}
-
.o-fade-up-enter-active {
- transition: all var(--o-duration-m1) var(--o-easing-standard-in);
+ animation: o-fade-up var(--o-duration-m1) var(--o-easing-standard-in);
}
.o-fade-up-leave-active {
- transition: all var(--o-duration-s) var(--o-easing-standard-out);
-}
-
-.o-fade-up-enter-from,
-.o-fade-up-leave-to {
- opacity: 0;
- transform: translateY(10px);
+ animation: o-fade-up var(--o-duration-s) var(--o-easing-standard-out) reverse;
}
diff --git a/packages/opendesign/src/components/tabs/types.ts b/packages/opendesign/src/components/tabs/types.ts
index b3ddf43aa5a7ed5fab7091c4830cdb0caa8e6e41..07f13cc40228fe4a7e6f486c2fe97f35efb0ea31 100644
--- a/packages/opendesign/src/components/tabs/types.ts
+++ b/packages/opendesign/src/components/tabs/types.ts
@@ -7,14 +7,14 @@ export const tabsProps = {
*/
modelValue: {
type: [String, Number],
- default: undefined
+ default: undefined,
},
/**
* VariantT
*/
variant: {
type: String as PropType<'solid' | 'text'>,
- default: 'text'
+ default: 'text',
},
/**
* 是否激活时再加载
@@ -33,7 +33,7 @@ export const tabsProps = {
*/
line: {
type: Boolean,
- default: true
+ default: true,
},
/**
* 是否nav的横向排列布局,支持justify-content的所有值
@@ -45,7 +45,6 @@ export const tabsProps = {
export type TabsPropsT = ExtractPropTypes;
-
export const tabPaneProps = {
/**
* 页签项的key
@@ -66,7 +65,7 @@ export const tabPaneProps = {
*/
transition: {
type: String,
- default: 'o-fade-in-enter',
+ default: 'o-fade-in',
},
/**
* 是否禁用选中该页签
@@ -98,4 +97,4 @@ export const tabPaneProps = {
},
};
-export type TabPanePropsT = ExtractPropTypes;
\ No newline at end of file
+export type TabPanePropsT = ExtractPropTypes;
diff --git a/packages/opendesign/tsconfig.json b/packages/opendesign/tsconfig.json
index 2fcfbf84fa774df5245ba864a7571fce067c2ed7..492992fd143df55759e57ba913209e72d6450c2d 100644
--- a/packages/opendesign/tsconfig.json
+++ b/packages/opendesign/tsconfig.json
@@ -26,10 +26,11 @@
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
- "src/**/*.vue",
+ "src/**/*.vue"
],
"exclude": [
"src/tokens/token.config.ts",
"src/icons/icon.config.ts",
+ "src/icons/cleansvg.config.ts"
]
}
\ No newline at end of file
diff --git a/packages/portal/src/router.ts b/packages/portal/src/router.ts
index 5c01f700fd2f9dd85d0d613fe144714ef86fc9c0..e358a0c5545addb5bbe001761e22069c4e907d33 100644
--- a/packages/portal/src/router.ts
+++ b/packages/portal/src/router.ts
@@ -188,6 +188,12 @@ export const routes = [
label: '栅格 Grid',
component: () => import('@components/grid/__demo__/IndexGrid.vue'),
},
+ {
+ path: '/loading',
+ name: 'Loading',
+ label: '加载 Loading',
+ component: () => import('@components/loading/__demo__/IndexLoading.vue'),
+ },
{
path: '/resize-observer',
name: 'ResizeObserver',
diff --git a/packages/scripts/src/gen-icon/config.ts b/packages/scripts/src/gen-icon/config.ts
index f2c7fdcaf31d117ca709410caf60806513ec770c..786be0810a733a2489c97acb584b764be517561b 100644
--- a/packages/scripts/src/gen-icon/config.ts
+++ b/packages/scripts/src/gen-icon/config.ts
@@ -2,26 +2,26 @@ import type { Config, PluginConfig } from 'svgo';
export interface IconsConfig {
svgo: {
- fill: Config,
- stroke: Config,
- color: Config,
- },
- input: string,
- output: string,
- template: typeof template
+ fill: Config;
+ stroke: Config;
+ color: Config;
+ };
+ input: string;
+ output: string;
+ template: typeof template;
}
-export const basePlugins:PluginConfig[] = [
+export const basePlugins: PluginConfig[] = [
{
- // 将id替换成class
+ // 将id添加到class
name: 'addClassesbyId',
fn: () => {
- const nodes:string[] = ['*'];
+ const nodes: string[] = ['*'];
return {
element: {
enter: (node) => {
- if (nodes.includes('*') || nodes.includes(node.name)){
- const classname = node.attributes.class|| '';
- const id = node.attributes.id||'';
+ if (nodes.includes('*') || nodes.includes(node.name)) {
+ const classname = node.attributes.class || '';
+ const id = node.attributes.id || '';
const cls = classname.split(' ');
cls.push(id);
const classStr = cls.join(' ').trim();
@@ -43,19 +43,19 @@ export const basePlugins:PluginConfig[] = [
},
},
},
+ // 'prefixIds',
'removeStyleElement',
'removeScriptElement',
'removeDimensions',
'sortAttrs',
'removeUselessStrokeAndFill',
+ 'removeXMLNS',
{
name: 'addAttributesToSVGElement',
params: {
- attributes: [
- { ':class': 'classnames' },
- ],
+ attributes: [{ ':class': 'classnames' }],
},
- }
+ },
];
const fillSvgoConfig: Config = {
plugins: [
@@ -63,12 +63,9 @@ const fillSvgoConfig: Config = {
{
name: 'removeAttrs',
params: {
- attrs: [
- 'svg:class',
- 'fill',
- ],
+ attrs: ['svg:class', 'fill'],
},
- }
+ },
],
};
const strokeSvgoConfig: Config = {
@@ -77,12 +74,9 @@ const strokeSvgoConfig: Config = {
{
name: 'removeAttrs',
params: {
- attrs: [
- 'svg:class',
- 'stroke',
- ],
+ attrs: ['svg:class', 'stroke'],
},
- }
+ },
],
};
const colorSvgoConfig: Config = {
@@ -91,15 +85,13 @@ const colorSvgoConfig: Config = {
{
name: 'removeAttrs',
params: {
- attrs: [
- 'svg:class',
- ],
+ attrs: ['svg:class'],
},
},
- ]
+ ],
};
-const template = ({ name, componentName, svg, type }: { name: string, componentName: string, svg: string, type: 'fill' | 'stroke' | 'color' }) => {
+const template = ({ name, componentName, svg, type }: { name: string; componentName: string; svg: string; type: 'fill' | 'stroke' | 'color' }) => {
return `