diff --git a/CHANGELOG.zh_CN.md b/CHANGELOG.zh_CN.md index bc12dfb6..f55aae06 100644 --- a/CHANGELOG.zh_CN.md +++ b/CHANGELOG.zh_CN.md @@ -13,6 +13,7 @@ - 恢复 table 的`isTreeTable`属性 - 修复表格内存溢出问题 - 修复`layout` 收缩展开功能在分割模式下失效 +- 修复 modal 高度计算错误 ## 2.0.0-rc.15 (2020-12-31) diff --git a/README.md b/README.md index 4b5ae359..f0d72922 100644 --- a/README.md +++ b/README.md @@ -256,8 +256,8 @@ yarn clean:lib # 删除node_modules,兼容window系统 如果这些插件对你有帮助,可以给一个 star 支持下 -- [vite-plugin-mock](https://github.com/anncwb/vite-plugin-mock) -- [vite-plugin-html](https://github.com/anncwb/vite-plugin-html) +- [vite-plugin-mock](https://github.com/vbenjs/vite-plugin-mock) +- [vite-plugin-html](https://github.com/vbenjs/vite-plugin-html) ## 加入我们 diff --git a/src/components/Basic/src/BasicArrow.vue b/src/components/Basic/src/BasicArrow.vue index f9686e90..1d8daddf 100644 --- a/src/components/Basic/src/BasicArrow.vue +++ b/src/components/Basic/src/BasicArrow.vue @@ -15,6 +15,7 @@ export default defineComponent({ name: 'BasicArrow', + inheritAttrs: false, components: { RightOutlined }, props: { // Expand contract, expand by default diff --git a/src/components/Basic/src/BasicHelp.vue b/src/components/Basic/src/BasicHelp.vue index 09ef2568..06fd3ac0 100644 --- a/src/components/Basic/src/BasicHelp.vue +++ b/src/components/Basic/src/BasicHelp.vue @@ -12,6 +12,7 @@ import { useDesign } from '/@/hooks/web/useDesign'; export default defineComponent({ name: 'BasicHelp', + inheritAttrs: false, components: { Tooltip }, props: { // max-width diff --git a/src/components/Basic/src/BasicTitle.vue b/src/components/Basic/src/BasicTitle.vue index 77e1e949..6d607d4c 100644 --- a/src/components/Basic/src/BasicTitle.vue +++ b/src/components/Basic/src/BasicTitle.vue @@ -15,6 +15,7 @@ export default defineComponent({ name: 'BasicTitle', + inheritAttrs: false, components: { BasicHelp }, props: { helpMessage: { diff --git a/src/components/Container/src/LazyContainer.vue b/src/components/Container/src/LazyContainer.vue index 847f1308..794db196 100644 --- a/src/components/Container/src/LazyContainer.vue +++ b/src/components/Container/src/LazyContainer.vue @@ -35,6 +35,7 @@ export default defineComponent({ name: 'LazyContainer', + inheritAttrs: false, components: { Skeleton }, props: { // Waiting time, if the time is specified, whether visible or not, it will be automatically loaded after the specified time diff --git a/src/components/Container/src/ScrollContainer.vue b/src/components/Container/src/ScrollContainer.vue index 93f1b2e9..446f0723 100644 --- a/src/components/Container/src/ScrollContainer.vue +++ b/src/components/Container/src/ScrollContainer.vue @@ -12,6 +12,7 @@ export default defineComponent({ name: 'ScrollContainer', + inheritAttrs: false, components: { Scrollbar }, setup() { const scrollbarRef = ref>(null); diff --git a/src/components/Markdown/src/index.vue b/src/components/Markdown/src/index.vue index d2a1133a..9657fbf2 100644 --- a/src/components/Markdown/src/index.vue +++ b/src/components/Markdown/src/index.vue @@ -8,19 +8,23 @@ import { propTypes } from '/@/utils/propTypes'; import { useLocale } from '/@/hooks/web/useLocale'; + import { useModalContext } from '../../Modal'; type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined; export default defineComponent({ - emits: ['change'], + inheritAttrs: false, props: { height: propTypes.number.def(360), value: propTypes.string.def(''), }, + emits: ['change', 'get'], setup(props, { attrs, emit }) { const wrapRef = ref(null); const vditorRef = ref>(null); const initedRef = ref(false); + const modalFn = useModalContext(); + const lang = ref(); const { getLang } = useLocale(); @@ -66,10 +70,19 @@ initedRef.value = true; } + const instance = { + getVditor: (): Vditor => vditorRef.value!, + }; + onMounted(() => { nextTick(() => { init(); + setTimeout(() => { + modalFn?.redoModalHeight?.(); + }, 200); }); + + emit('get', instance); }); onUnmounted(() => { @@ -82,7 +95,7 @@ return { wrapRef, - getVditor: (): Vditor => vditorRef.value!, + ...instance, }; }, }); diff --git a/src/components/Modal/src/BasicModal.vue b/src/components/Modal/src/BasicModal.vue index f91129a2..4b442006 100644 --- a/src/components/Modal/src/BasicModal.vue +++ b/src/components/Modal/src/BasicModal.vue @@ -27,7 +27,7 @@ :height="getProps.height" :visible="visibleRef" :modalFooterHeight="footer !== undefined && !footer ? 0 : undefined" - v-bind="omit(getProps.wrapperProps, 'visible')" + v-bind="omit(getProps.wrapperProps, 'visible', 'height')" @ext-height="handleExtHeight" @height-change="handleHeightChange" > @@ -51,6 +51,7 @@ watchEffect, toRef, getCurrentInstance, + nextTick, } from 'vue'; import Modal from './components/Modal'; @@ -67,6 +68,7 @@ import { omit } from 'lodash-es'; export default defineComponent({ name: 'BasicModal', + inheritAttrs: false, components: { Modal, ModalWrapper, ModalClose, ModalFooter, ModalHeader }, props: basicProps, emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register'], diff --git a/src/components/Modal/src/components/ModalWrapper.vue b/src/components/Modal/src/components/ModalWrapper.vue index 7c1a4c64..981990fe 100644 --- a/src/components/Modal/src/components/ModalWrapper.vue +++ b/src/components/Modal/src/components/ModalWrapper.vue @@ -31,6 +31,7 @@ export default defineComponent({ name: 'ModalWrapper', + inheritAttrs: false, components: { Spin, ScrollContainer }, props: { loading: propTypes.bool, @@ -51,6 +52,8 @@ const realHeightRef = ref(0); const minRealHeightRef = ref(0); + let realHeight = 0; + let stopElResizeFn: Fn = () => {}; useWindowSizeFn(setModalHeight); @@ -137,8 +140,9 @@ if (!spinEl) return; - const realHeight = spinEl.scrollHeight; - + if (!realHeight) { + realHeight = spinEl.scrollHeight; + } if (props.fullScreen) { realHeightRef.value = window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight; @@ -147,7 +151,7 @@ ? props.height : realHeight > maxHeight ? maxHeight - : realHeight + 16 + 30; + : realHeight + 46; } emit('height-change', unref(realHeightRef)); } catch (error) { diff --git a/src/components/Scrollbar/src/index.vue b/src/components/Scrollbar/src/index.vue index 841e15b1..ebda1b2a 100644 --- a/src/components/Scrollbar/src/index.vue +++ b/src/components/Scrollbar/src/index.vue @@ -33,6 +33,7 @@ export default defineComponent({ name: 'Scrollbar', + inheritAttrs: false, components: { Bar }, props: { native: { @@ -91,12 +92,18 @@ onMounted(() => { if (props.native) return; nextTick(update); - !props.noresize && addResizeListener(resize.value, update); + if (!props.noresize) { + addResizeListener(resize.value, update); + addResizeListener(wrap.value, update); + } }); onBeforeUnmount(() => { if (props.native) return; - !props.noresize && removeResizeListener(resize.value, update); + if (!props.noresize) { + removeResizeListener(resize.value, update); + removeResizeListener(wrap.value, update); + } }); const style = computed(() => { let style: any = props.wrapStyle; @@ -127,7 +134,7 @@ &__wrap { height: 100%; - overflow: scroll; + overflow: auto; &--hidden-default { scrollbar-width: none; diff --git a/src/components/Tinymce/src/Editor.vue b/src/components/Tinymce/src/Editor.vue index a58df7aa..7c01c4ec 100644 --- a/src/components/Tinymce/src/Editor.vue +++ b/src/components/Tinymce/src/Editor.vue @@ -31,6 +31,7 @@ export default defineComponent({ name: 'Tinymce', + inheritAttrs: false, props: basicProps, emits: ['change', 'update:modelValue'], setup(props, { emit, attrs }) { diff --git a/src/components/Tree/src/BasicTree.tsx b/src/components/Tree/src/BasicTree.tsx index 394ecf30..9a508c8f 100644 --- a/src/components/Tree/src/BasicTree.tsx +++ b/src/components/Tree/src/BasicTree.tsx @@ -15,6 +15,7 @@ import { extendSlots } from '/@/utils/helper/tsxHelper'; import { basicProps } from './props'; import { useTree } from './useTree'; import { useExpose } from '/@/hooks/core/useExpose'; +import { onMounted } from 'vue'; interface State { expandedKeys: Keys; @@ -25,7 +26,7 @@ const prefixCls = 'basic-tree'; export default defineComponent({ name: 'BasicTree', props: basicProps, - emits: ['update:expandedKeys', 'update:selectedKeys', 'update:value'], + emits: ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'get'], setup(props, { attrs, slots, emit }) { const state = reactive({ expandedKeys: props.expandedKeys || [], @@ -182,7 +183,7 @@ export default defineComponent({ state.checkedKeys = props.checkedKeys; }); - useExpose({ + const instance: TreeActionType = { setExpandedKeys, getExpandedKeys, setSelectedKeys, @@ -195,6 +196,12 @@ export default defineComponent({ filterByLevel: (level: number) => { state.expandedKeys = filterByLevel(level); }, + }; + + useExpose(instance); + + onMounted(() => { + emit('get', instance); }); return () => { diff --git a/src/hooks/component/usePageContext.ts b/src/hooks/component/usePageContext.ts index f9e4d013..12cc1605 100644 --- a/src/hooks/component/usePageContext.ts +++ b/src/hooks/component/usePageContext.ts @@ -1,8 +1,6 @@ import type { InjectionKey, ComputedRef, Ref } from 'vue'; import { createContext, useContext } from '/@/hooks/core/useContext'; -import {} from 'vue'; - export interface PageContextProps { contentHeight: ComputedRef; pageHeight: Ref; diff --git a/src/layouts/default/sider/MixSider.vue b/src/layouts/default/sider/MixSider.vue index 2f154ae0..ee285982 100644 --- a/src/layouts/default/sider/MixSider.vue +++ b/src/layouts/default/sider/MixSider.vue @@ -113,7 +113,6 @@ const activePath = ref(''); const chilrenMenus = ref([]); const openMenu = ref(false); - const isInit = ref(false); const dragBarRef = ref(null); const sideRef = ref(null); const currentRoute = ref>(null); @@ -251,8 +250,8 @@ } function handleClickOutside() { + setActive(true); closeMenu(); - setActive(); } function getItemEvents(item: Menu) { diff --git a/src/utils/browser.ts b/src/utils/browser.ts index 0b7ed544..b116d38c 100644 --- a/src/utils/browser.ts +++ b/src/utils/browser.ts @@ -75,7 +75,7 @@ export function isOperaFn() { * set page Title * @param {*} title :page Title */ -const setDocumentTitle = (title: string) => { +function setDocumentTitle(title: string) { document.title = title; const ua = navigator.userAgent; const regex = /\bMicroMessenger\/([\d.]+)/; @@ -91,7 +91,7 @@ const setDocumentTitle = (title: string) => { }; document.body.appendChild(i); } -}; +} export function setTitle(title: string, appTitle?: string) { if (title) { diff --git a/src/utils/color.ts b/src/utils/color.ts index bd45ab9f..825c3858 100644 --- a/src/utils/color.ts +++ b/src/utils/color.ts @@ -5,10 +5,10 @@ * @param String color 十六进制颜色值 * @return Boolean */ -export const isHexColor = function (color: string) { +export function isHexColor(color: string) { const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/; return reg.test(color); -}; +} /** * RGB 颜色值转换为 十六进制颜色值. @@ -19,18 +19,18 @@ export const isHexColor = function (color: string) { * @param g * @param b */ -export const rgbToHex = function (r: number, g: number, b: number) { +export function rgbToHex(r: number, g: number, b: number) { // tslint:disable-next-line:no-bitwise const hex = ((r << 16) | (g << 8) | b).toString(16); return '#' + new Array(Math.abs(hex.length - 7)).join('0') + hex; -}; +} /** * Transform a HEX color to its RGB representation * @param {string} hex The color to transform * @returns The RGB representation of the passed color */ -export const hexToRGB = function (hex: string) { +export function hexToRGB(hex: string) { let sHex = hex.toLowerCase(); if (isHexColor(hex)) { if (sHex.length === 4) { @@ -47,16 +47,16 @@ export const hexToRGB = function (hex: string) { return 'RGB(' + sColorChange.join(',') + ')'; } return sHex; -}; +} -export const colorIsDark = (color: string) => { +export function colorIsDark(color: string) { if (!isHexColor(color)) return; const [r, g, b] = hexToRGB(color) .replace(/(?:\(|\)|rgb|RGB)*/g, '') .split(',') .map((item) => Number(item)); return r * 0.299 + g * 0.578 + b * 0.114 < 192; -}; +} /** * Darkens a HEX color given the passed percentage @@ -64,14 +64,14 @@ export const colorIsDark = (color: string) => { * @param {number} amount The amount to change the color by * @returns {string} The HEX representation of the processed color */ -export const darken = (color: string, amount: number) => { +export function darken(color: string, amount: number) { color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color; amount = Math.trunc((255 * amount) / 100); return `#${subtractLight(color.substring(0, 2), amount)}${subtractLight( color.substring(2, 4), amount )}${subtractLight(color.substring(4, 6), amount)}`; -}; +} /** * Lightens a 6 char HEX color according to the passed percentage @@ -79,14 +79,14 @@ export const darken = (color: string, amount: number) => { * @param {number} amount The amount to change the color by * @returns {string} The processed color represented as HEX */ -export const lighten = (color: string, amount: number) => { +export function lighten(color: string, amount: number) { color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color; amount = Math.trunc((255 * amount) / 100); return `#${addLight(color.substring(0, 2), amount)}${addLight( color.substring(2, 4), amount )}${addLight(color.substring(4, 6), amount)}`; -}; +} /* Suma el porcentaje indicado a un color (RR, GG o BB) hexadecimal para aclararlo */ /** @@ -95,11 +95,11 @@ export const lighten = (color: string, amount: number) => { * @param {number} amount The amount to change the color by * @returns {string} The processed part of the color */ -const addLight = (color: string, amount: number) => { +function addLight(color: string, amount: number) { const cc = parseInt(color, 16) + amount; const c = cc > 255 ? 255 : cc; return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`; -}; +} /** * Calculates luminance of an rgb color @@ -107,33 +107,36 @@ const addLight = (color: string, amount: number) => { * @param {number} g green * @param {number} b blue */ -const luminanace = (r: number, g: number, b: number) => { +function luminanace(r: number, g: number, b: number) { const a = [r, g, b].map((v) => { v /= 255; return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); }); return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722; -}; +} /** * Calculates contrast between two rgb colors * @param {string} rgb1 rgb color 1 * @param {string} rgb2 rgb color 2 */ -const contrast = (rgb1: string[], rgb2: number[]) => - (luminanace(~~rgb1[0], ~~rgb1[1], ~~rgb1[2]) + 0.05) / - (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05); +function contrast(rgb1: string[], rgb2: number[]) { + return ( + (luminanace(~~rgb1[0], ~~rgb1[1], ~~rgb1[2]) + 0.05) / + (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05) + ); +} /** * Determines what the best text color is (black or white) based con the contrast with the background * @param hexColor - Last selected color by the user */ -export const calculateBestTextColor = (hexColor: string) => { +export function calculateBestTextColor(hexColor: string) { const rgbColor = hexToRGB(hexColor.substring(1)); const contrastWithBlack = contrast(rgbColor.split(','), [0, 0, 0]); return contrastWithBlack >= 12 ? '#000000' : '#FFFFFF'; -}; +} /** * Subtracts the indicated percentage to the R, G or B of a HEX color @@ -141,8 +144,8 @@ export const calculateBestTextColor = (hexColor: string) => { * @param {number} amount The amount to change the color by * @returns {string} The processed part of the color */ -const subtractLight = (color: string, amount: number) => { +function subtractLight(color: string, amount: number) { const cc = parseInt(color, 16) - amount; const c = cc < 0 ? 0 : cc; return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`; -}; +} diff --git a/src/utils/dateUtil.ts b/src/utils/dateUtil.ts index 0a9acf2d..ec784a66 100644 --- a/src/utils/dateUtil.ts +++ b/src/utils/dateUtil.ts @@ -14,7 +14,7 @@ export function formatToDate(date: moment.MomentInput = null, format = DATE_FORM return moment(date).format(format); } -export const formatAgo = (str: string | number) => { +export function formatAgo(str: string | number) { if (!str) return ''; const date = new Date(Number(str)); const time = new Date().getTime() - date.getTime(); // 现在的时间-传入的时间 = 相差的时间(单位 = 毫秒) @@ -35,6 +35,6 @@ export const formatAgo = (str: string | number) => { } else { return parseInt(String(time / 31536000000)) + '年前'; } -}; +} export const dateUtil = moment; diff --git a/src/utils/domUtils.ts b/src/utils/domUtils.ts index a1528a3f..e1f3f2d2 100644 --- a/src/utils/domUtils.ts +++ b/src/utils/domUtils.ts @@ -132,7 +132,7 @@ export function hackCss(attr: string, value: string) { } /* istanbul ignore next */ -export const on = function ( +export function on( element: Element | HTMLElement | Document | Window, event: string, handler: EventListenerOrEventListenerObject @@ -140,10 +140,10 @@ export const on = function ( if (element && event && handler) { element.addEventListener(event, handler, false); } -}; +} /* istanbul ignore next */ -export const off = function ( +export function off( element: Element | HTMLElement | Document | Window, event: string, handler: Fn @@ -151,10 +151,10 @@ export const off = function ( if (element && event && handler) { element.removeEventListener(event, handler, false); } -}; +} /* istanbul ignore next */ -export const once = function (el: HTMLElement, event: string, fn: EventListener): void { +export function once(el: HTMLElement, event: string, fn: EventListener): void { const listener = function (this: any, ...args: unknown[]) { if (fn) { fn.apply(this, args); @@ -162,4 +162,4 @@ export const once = function (el: HTMLElement, event: string, fn: EventListener) off(el, event, listener); }; on(el, event, listener); -}; +} diff --git a/src/utils/env.ts b/src/utils/env.ts index ff99a927..f64ff131 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -1,9 +1,9 @@ import type { GlobEnvConfig } from '/@/types/config'; -export const getGlobEnvConfig = (): GlobEnvConfig => { +export function getGlobEnvConfig(): GlobEnvConfig { const env = import.meta.env; return (env as unknown) as GlobEnvConfig; -}; +} /** * @description: 开发模式 @@ -20,25 +20,33 @@ export const prodMode = 'production'; * @returns: * @example: */ -export const getEnv = (): string => import.meta.env.MODE; +export function getEnv(): string { + return import.meta.env.MODE; +} /** * @description: 是否是开发模式 * @returns: * @example: */ -export const isDevMode = (): boolean => import.meta.env.DEV; +export function isDevMode(): boolean { + return import.meta.env.DEV; +} /** * @description: 是否是生产模式模式 * @returns: * @example: */ -export const isProdMode = (): boolean => import.meta.env.PROD; +export function isProdMode(): boolean { + return import.meta.env.PROD; +} /** * @description: 是否开启mock * @returns: * @example: */ -export const isUseMock = (): boolean => import.meta.env.VITE_USE_MOCK === 'true'; +export function isUseMock(): boolean { + return import.meta.env.VITE_USE_MOCK === 'true'; +} diff --git a/src/utils/event/resizeEvent.ts b/src/utils/event/resizeEvent.ts index 8d6333e8..9faa661b 100644 --- a/src/utils/event/resizeEvent.ts +++ b/src/utils/event/resizeEvent.ts @@ -3,7 +3,7 @@ import ResizeObserver from 'resize-observer-polyfill'; const isServer = typeof window === 'undefined'; /* istanbul ignore next */ -const resizeHandler = function (entries: any[]) { +function resizeHandler(entries: any[]) { for (const entry of entries) { const listeners = entry.target.__resizeListeners__ || []; if (listeners.length) { @@ -12,10 +12,10 @@ const resizeHandler = function (entries: any[]) { }); } } -}; +} /* istanbul ignore next */ -export const addResizeListener = function (element: any, fn: () => any) { +export function addResizeListener(element: any, fn: () => any) { if (isServer) return; if (!element.__resizeListeners__) { element.__resizeListeners__ = []; @@ -23,13 +23,13 @@ export const addResizeListener = function (element: any, fn: () => any) { element.__ro__.observe(element); } element.__resizeListeners__.push(fn); -}; +} /* istanbul ignore next */ -export const removeResizeListener = function (element: any, fn: () => any) { +export function removeResizeListener(element: any, fn: () => any) { if (!element || !element.__resizeListeners__) return; element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1); if (!element.__resizeListeners__.length) { element.__ro__.disconnect(); } -}; +} diff --git a/src/utils/event/triggerWindowResizeEvent.ts b/src/utils/event/triggerWindowResizeEvent.ts index 221de592..b5047dc3 100644 --- a/src/utils/event/triggerWindowResizeEvent.ts +++ b/src/utils/event/triggerWindowResizeEvent.ts @@ -4,6 +4,6 @@ export function triggerWindowResize() { const event = document.createEvent('HTMLEvents'); event.initEvent('resize', true, true); - (event as ChangeEvent).eventType = 'message'; + (event as any).eventType = 'message'; window.dispatchEvent(event); } diff --git a/src/utils/helper/envHelper.ts b/src/utils/helper/envHelper.ts index 0b98912a..68be4299 100644 --- a/src/utils/helper/envHelper.ts +++ b/src/utils/helper/envHelper.ts @@ -4,6 +4,6 @@ import pkg from '../../../package.json'; const globSetting = useGlobSetting(); // Generate cache key according to version -export const getStorageShortName = () => { +export function getStorageShortName() { return `${globSetting.shortName}__${getEnv()}${`__${pkg.version}`}__`.toUpperCase(); -}; +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 7ee332d6..4fa14d9e 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -85,3 +85,17 @@ export function getDynamicProps(props: T): Partial { return ret as Partial; } + +export function getLastItem(list: T) { + if (Array.isArray(list)) { + return list.slice(-1)[0]; + } + + if (list instanceof Set) { + return Array.from(list).slice(-1)[0]; + } + + if (list instanceof Map) { + return Array.from(list.values()).slice(-1)[0]; + } +} diff --git a/src/utils/is.ts b/src/utils/is.ts index fda839e9..3ea67d1f 100644 --- a/src/utils/is.ts +++ b/src/utils/is.ts @@ -4,17 +4,33 @@ export function is(val: unknown, type: string) { return toString.call(val) === `[object ${type}]`; } -export const isDef = (val?: T): val is T => { +export function isDef(val?: T): val is T { return typeof val !== 'undefined'; -}; +} -export const isUnDef = (val?: T): val is T => { +export function isUnDef(val?: T): val is T { return !isDef(val); -}; +} -export const isObject = (val: any): val is Record => { +export function isObject(val: any): val is Record { return val !== null && is(val, 'Object'); -}; +} + +export function isEmpty(val: T): val is T { + if (isArray(val) || isString(val)) { + return val.length === 0; + } + + if (val instanceof Map || val instanceof Set) { + return val.size === 0; + } + + if (isObject(val)) { + return Object.keys(val).length === 0; + } + + return false; +} export function isDate(val: unknown): val is Date { return is(val, 'Date'); @@ -40,7 +56,9 @@ export function isString(val: unknown): val is string { return is(val, 'String'); } -export const isFunction = (val: unknown): val is Function => typeof val === 'function'; +export function isFunction(val: unknown): val is Function { + return typeof val === 'function'; +} export function isBoolean(val: unknown): val is boolean { return is(val, 'Boolean'); @@ -54,13 +72,13 @@ export function isArray(val: any): val is Array { return val && Array.isArray(val); } -export const isWindow = (val: any): val is Window => { +export function isWindow(val: any): val is Window { return typeof window !== 'undefined' && is(val, 'Window'); -}; +} -export const isElement = (val: unknown): val is Element => { +export function isElement(val: unknown): val is Element { return isObject(val) && !!val.tagName; -}; +} export const isServer = typeof window === 'undefined'; @@ -70,17 +88,17 @@ export function isImageDom(o: Element) { return o && ['IMAGE', 'IMG'].includes(o.tagName); } -export const isTextarea = (element: Element | null): element is HTMLTextAreaElement => { +export function isTextarea(element: Element | null): element is HTMLTextAreaElement { return element !== null && element.tagName.toLowerCase() === 'textarea'; -}; +} -export const isMobile = (): boolean => { +export function isMobile(): boolean { return !!navigator.userAgent.match( /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i ); -}; +} -export const isUrl = (path: string): boolean => { +export function isUrl(path: string): boolean { const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/; return reg.test(path); -}; +} diff --git a/src/utils/scrollbarWidth.ts b/src/utils/scrollbarWidth.ts deleted file mode 100644 index 5b8432a9..00000000 --- a/src/utils/scrollbarWidth.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { isWindow } from '/@/utils/is'; - -let scrollBarWidth: number; - -export default function (): number { - if (!isWindow) return 0; - if (scrollBarWidth !== undefined) return scrollBarWidth; - - const outer = document.createElement('div'); - outer.className = 'scrollbar__wrap'; - outer.style.visibility = 'hidden'; - outer.style.width = '100px'; - outer.style.position = 'absolute'; - outer.style.top = '-9999px'; - document.body.appendChild(outer); - - const widthNoScroll = outer.offsetWidth; - outer.style.overflow = 'scroll'; - - const inner = document.createElement('div'); - inner.style.width = '100%'; - outer.appendChild(inner); - - const widthWithScroll = inner.offsetWidth; - const parentNode = outer.parentNode; - parentNode && parentNode.removeChild(outer); - scrollBarWidth = widthNoScroll - widthWithScroll; - - return scrollBarWidth; -}