UNPKG

@opentiny/vue-renderless

Version:

An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.

509 lines (508 loc) 16.2 kB
import "../chunk-G2ADBYYC.js"; import { omitText } from "@opentiny/utils"; import { isEmptyObject, isObject } from "@opentiny/utils"; import { PopupManager } from "@opentiny/utils"; import { mapTree } from "../grid/static"; import { transformTreeData } from "@opentiny/utils"; import { on, off } from "@opentiny/utils"; import { xss } from "@opentiny/utils"; import { isServer } from "@opentiny/utils"; const { nextZIndex } = PopupManager; const computedIsShowMore = ({ props, state }) => () => !/^(retract|fixed|hidden)$/.test(props.overflow) && state.more && state.more.length; const computedPopClass = (state) => () => { const popClass = !state.showMore && state.subMenus && state.subMenus.length === 1 ? "single" : ""; return popClass + " " + state.menuClass; }; const computedSubMenus = (state) => () => { let arr = state.subMenu; if (state.subMenu && !isEmptyObject(state.subMenu)) { if (!state.subMenu.map((item) => item.children && !isEmptyObject(item.children)).reduce((pre, cur) => pre || cur)) { arr = [{ children: state.subMenu }]; } } return arr; }; const computedMenuStyle = ({ props, state }) => () => { let result = { maxWidth: `${state.width}px` }; if (props.overflow === "retract") { result.width = "0px"; } return result; }; const computedPopStyle = (state) => () => ({ top: `${state.popMenuTop}px` }); const watchWidth = ({ api, nextTick }) => () => nextTick(() => api.classify()); class CloneObject { constructor(json, props) { this.props = props; if (json) { this.data = this.getType(json); this.traverse(json, this.data); } } traverse(node, newObject) { if (/^(string|undefined)$/.test(typeof node) || node === null) { newObject = node; } else if (Array.isArray(node)) { this.traverseArray(node, newObject); } else if (typeof node === "object") { this.traverseObject(node, newObject); } return this; } traverseArray(node, newObject) { for (let i = 0; i < node.length; i++) { newObject.push(this.getType(node[i])); if (node[i] && typeof node[i] === "object") { this.traverse(node[i], newObject[i]); } } } traverseObject(node, newObject) { Object.keys(node).forEach((key) => { if (/^(name|url|route|title|name|children)$/.test(key)) { let alias = key; if (key === "url") { if (!(this.props.prevent || this.props.allowFullUrl)) { alias = "route"; } } if (key === "name") { alias = "title"; } newObject[alias] = this.getType(node[key]); if (newObject[alias] && typeof newObject[alias] === "object") { this.traverse(node[key], newObject[alias]); } } }); } getType(object) { let result; if (/^(string|undefined)$/.test(typeof object) || object === null) { result = object; } else if (Array.isArray(object)) { result = []; } else if (typeof object === "object") { result = {}; } return result; } } const initData = ({ fetchMenuData, fields, props, state }) => () => { if (props.defaultActive) { state.defaultActiveId = props.defaultActive; } const { textField = "title", urlField = "url", key = "id" } = fields || {}; const { parentKey, data } = props; const isFullUrl = (url) => /^(https?:\/\/|\/\/)[\s\S]+$/.test(url); const buildData = (item) => { const router = item[urlField] || item.route; return { title: item[textField], route: router ? router.replace(/^#\/?/, "") : null, url: item.url, id: item.id, pid: item.pid, isFullUrl: props.allowFullUrl && isFullUrl(router), target: item.target }; }; if (data) { state.data = mapTree(parentKey ? transformTreeData(data, key, parentKey) : data, buildData); return; } const menuData = props.fetchMenuData && fetchMenuData(); if (isObject(menuData) && (menuData == null ? void 0 : menuData.then)) { menuData.then((data2) => { state.data = mapTree(props.parentKey ? transformTreeData(data2, key, props.parentKey) : data2, buildData); }); return; } if (!menuData) { state.data = []; return; } let arr = []; if (menuData && menuData.length) { let getMenuData = new CloneObject(menuData, props).data; arr = typeof getMenuData === "object" && Array.isArray(getMenuData) ? getMenuData : [getMenuData]; } state.data = mapTree(parentKey ? transformTreeData(arr, key, parentKey) : arr, buildData); }; const mounted = ({ api, props, router, route, state }) => () => { api.calcWidth(); on(window, "resize", api.calcWidth); if (router) { state.afterEach = (to) => { api.setActiveMenu(api.getSelectedIndex(to.path)); }; router.afterEach(state.afterEach); } props.data && props.data.length && route && api.setActiveMenu(api.getSelectedIndex(route.path)); }; const unMounted = ({ api, state, router }) => () => { if (router && router.afterHooks) { const index = router.afterHooks.indexOf(state.afterEach); router.afterHooks.splice(index, 1); } state.afterEach = null; off(window, "resize", api.calcWidth); }; const getSelectedIndex = (state) => (path) => { if (!path) return; let length = state.data.length; let index = -1; if (path !== "/") { const queryIndex = path.indexOf("?"); if (queryIndex !== -1) { path = path.slice(0, queryIndex); } path = path.replace(/^#?\//, ""); let exp = new RegExp('("url":"#/?' + path + '"|"url":"/?' + path + '"|"route":"/?' + path + '")', "i"); for (let i = 0; i < length; i++) { if (exp.test(JSON.stringify(state.data[i]))) { index = i; break; } } } return index; }; const showSetting = ({ parent, state }) => () => { state.isShowSetting = true; const setting = parent.$el.querySelector(".more-setting"); setting.style.zIndex = nextZIndex(); }; const willHideSetting = (state) => () => state.isShowSetting = false; const showSubMenu = ({ api, nextTick, parent, state, vm }) => (list, { more, index }, event) => { if (event) { api.handleTitleMouseenter(event); } state.enterMenu = true; if (list || more) { state.subMenu !== list ? api.hideSubMenu() : api.stopHideSubMenu(); state.showMore = !!more; state.subMenu = list; state.showPopmenu = true; state.subItemSelectedIndex = -1; state.subIndex = -1; state.moreItemSelectedIndex = -1; nextTick(() => { const popmenu = parent.$el.querySelector(".popmenu"); if (state.isSaaSTheme) { setPopmenuStyleForSaas(popmenu, vm, event, state); } else { setPopmenuStyleForAurora(popmenu, event); } }); if (index !== void 0) { state.activeIndex = index; } if (more && list && list.length && state.subActiveIndex === -1) { state.subActiveIndex = 0; } } else { api.hideSubMenu(); } }; const setPopmenuStyleForSaas = (popmenu, vm, event, state) => { if (!popmenu) return; const navMenu = vm.$refs.navMenu; const itemWidth = 216; const margin = window.outerWidth >= 1440 ? 96 : 56; const safeMargin = 32; popmenu.style.zIndex = String(nextZIndex()); popmenu.style.display = "block"; popmenu.style.left = "32px"; const popmenuWidth = state.subMenus && state.subMenus.length ? state.subMenus.length * itemWidth + (state.subMenus.length - 1) * margin : 0; if (event) { if (popmenuWidth > navMenu.offsetWidth - safeMargin * 4) { state.menuClass = "display-gird"; } else { state.menuClass = "display-flex"; if (popmenuWidth + safeMargin * 2 > navMenu.offsetWidth - event.target.offsetLeft) { popmenu.style.right = "32px"; popmenu.style.left = "unset"; } else { popmenu.style.left = `${event.target.offsetLeft - 28}px`; popmenu.style.right = "unset"; } } } else { state.menuClass = "display-gird"; } }; const setPopmenuStyleForAurora = (popmenu, event) => { if (popmenu) { popmenu.style.zIndex = String(nextZIndex()); if (popmenu.classList.contains("single") && event) { popmenu.style.left = `${event.target.offsetLeft}px`; } else { popmenu.style.left = "0"; } popmenu.style.height = "auto"; popmenu.style.display = "block"; } }; const hideSubMenu = ({ api, parent, state }) => () => { api.stopHideSubMenu(); state.showMore = false; state.showPopmenu = false; state.activeIndex = -1; state.subActiveIndex = -1; const popmenu = parent.$el.querySelector(".popmenu"); popmenu.style.height = "auto"; popmenu.style.display = "none"; }; const willHideSubMenu = ({ api, state }) => () => { api.stopHideSubMenu(); api.handleTitleMouseleave(); state.enterMenu = false; state.timer = window.setTimeout(() => { api.hideSubMenu(); }, 20); }; const stopHideSubMenu = (state) => () => { clearTimeout(state.timer); }; const setSubMenu = (state) => (value, index) => { state.subActiveIndex = index; state.subMenu = value; state.enterMoreMenu = true; }; const leaveMoreMune = (state) => () => { state.enterMoreMenu = false; }; const isHide = ({ parent, state }) => (event) => !state.width || event.offsetTop >= parent.$el.offsetHeight; const hidePopmenu = (api) => (item) => { if (item.url || item.route) { api.setActiveMenu(api.getSelectedIndex(item.url || item.route)); api.hideSubMenu(); } }; const clickMenu = ({ api, props, state }) => (item, index, parentIndex) => { if (index === void 0) { return; } if (state.defaultActiveId) { state.defaultActiveId = item.id; state.selectedIndex = -1; } if (state.enterMenu) { state.subIndex = -1; state.subItemSelectedIndex = -1; api.setActiveMenu(index); } if (state.enterMoreMenu) { state.selectedIndex = -1; state.moreItemSelectedIndex = index; } else { state.subItemSelectedIndex = index; state.subIndex = parentIndex; } if (item.url || item.route) { if (props.beforeSkip) { props.beforeSkip(item) && api.skip(item, true); } else { api.skip(item, false); } api.hidePopmenu(item); } }; const skip = ({ api, router, fields }) => (item, flag = false) => { if (!router) return; if (item.isFullUrl) { const { urlField = "url" } = fields || {}; const router2 = item[urlField] || item.route; return window.open(xss.filterUrl(router2)).opener = null; } const address = !item.route || !flag ? api.getUrl(item).replace(/^#/, "") : `/${item.route || ""}`.replace(/^\/+/, "/").replace("#/", ""); if (address) { return router == null ? void 0 : router.push(address); } else { return ""; } }; const getPoint = ({ api, parent }) => () => { if (isServer) return 0; else { const items = parent.$el.querySelectorAll(".menu>li"); let index = 0; if (items) { index = items.length; for (let i = 0; i < items.length; i++) { if (api.isHide(items[i])) { index = index - (items.length - i); break; } } } return index; } }; const classify = ({ api, props, state }) => () => { var _a; const isRetractOrFixed = /^(retract|fixed)$/.test(props.overflow); const menuCount = isRetractOrFixed ? 0 : props.overflow === "hidden" ? (_a = props.data) == null ? void 0 : _a.length : api.getPoint(); state.more = state.data.slice(menuCount); }; const calcWidth = ({ parent, props, state }) => () => { let el = parent.$el; let logoWidth = parent.$slots.logo ? el.querySelector(".slot-logo").offsetWidth : 0; let toolbarWidth = parent.$slots.toolbar ? el.querySelector(".slot-toolbar").offsetWidth : 0; let menuWidth = el.offsetWidth; let width = props.overflow === "retract" ? 0 : menuWidth - toolbarWidth - logoWidth; width = width - 90; state.width = width < 200 ? 0 : width; state.popMenuTop = el.offsetHeight; }; const getTag = (props) => (item) => item.url && "a" || item.route && (!props.beforeSkip ? "router-link" : "a") || "span"; const getUrl = () => (item) => item.url || ""; const getRoute = (props) => (item) => !props.beforeSkip ? `/${item.route || ""}`.replace(/^\/+/, "/").replace("#/", "") : ""; const setActiveMenu = (state) => (index) => state.selectedIndex = typeof index !== "undefined" ? index : -1; const initService = ({ props, service }) => { const fetchMenuData = () => Promise.reject( new Error("[TINY Error][NavMenu] Prop fetchMenuData is mandatory when the framework service is not used") ); const { base = {}, setting = {} } = service || {}; const { options = {} } = setting; return { fetchMenuData: props.fetchMenuData || base.getMenuDataSync || fetchMenuData, fields: props.fields || options.NavMenu }; }; const handleTitleMouseenter = ({ state, vm }) => ($event) => { state.tooltipContent = ""; const target = $event.target; const text = target.textContent; const font = window.getComputedStyle(target).font; const rect = target.getBoundingClientRect(); const res = omitText(text, font, rect.width + 2); if (target && res.o) { const tooltip = vm.$refs.tooltip; tooltip.state.referenceElm = target; tooltip.state.popperElm && (tooltip.state.popperElm.style.display = "none"); tooltip.doDestroy(); state.tooltipVisible = true; state.tooltipContent = text; setTimeout(tooltip.updatePopper, 20); } }; const handleTitleMouseleave = ({ state }) => () => { state.tooltipVisible = false; }; const getMoreSelected = ({ state }) => () => { let isSelected = false; if (state == null ? void 0 : state.more.length) { state.more.forEach((item) => { var _a; if ((item == null ? void 0 : item.id) === state.defaultActiveId) { isSelected = true; } else if ((_a = item == null ? void 0 : item.children) == null ? void 0 : _a.length) { isSelected = getSelectedState(item.children, state.defaultActiveId); } }); } return isSelected; }; const getTabSelected = ({ state }) => (item, index) => { var _a; let isChildSelected = false; if ((item == null ? void 0 : item.id) && ((_a = item == null ? void 0 : item.children) == null ? void 0 : _a.length)) { isChildSelected = getSelectedState(item.children, state.defaultActiveId); } let isSelected = (item == null ? void 0 : item.id) === state.defaultActiveId || isChildSelected || index === state.selectedIndex; return isSelected; }; const getLeftSelected = ({ state }) => (item, index) => { var _a; let isLeftChildSelected = false; if ((item == null ? void 0 : item.id) && ((_a = item == null ? void 0 : item.children) == null ? void 0 : _a.length)) { isLeftChildSelected = getSelectedState(item.children, state.defaultActiveId); } let isSelected = (item == null ? void 0 : item.id) === state.defaultActiveId || isLeftChildSelected || index === state.moreItemSelectedIndex; return isSelected; }; const getLastChildSelected = ({ state }) => (item, i, index) => { let isSelected = (item == null ? void 0 : item.id) === state.defaultActiveId || i === state.subItemSelectedIndex && index === state.subIndex; return isSelected; }; const getSelectedState = (itemData, id) => { let isSelected = false; itemData.forEach((data) => { var _a; if ((data == null ? void 0 : data.id) === id) { isSelected = true; } else if ((_a = data == null ? void 0 : data.children) == null ? void 0 : _a.length) { data == null ? void 0 : data.children.forEach((dataChildren) => { if ((dataChildren == null ? void 0 : dataChildren.id) === id) { isSelected = true; } }); } }); return isSelected; }; export { calcWidth, classify, clickMenu, computedIsShowMore, computedMenuStyle, computedPopClass, computedPopStyle, computedSubMenus, getLastChildSelected, getLeftSelected, getMoreSelected, getPoint, getRoute, getSelectedIndex, getTabSelected, getTag, getUrl, handleTitleMouseenter, handleTitleMouseleave, hidePopmenu, hideSubMenu, initData, initService, isHide, leaveMoreMune, mounted, setActiveMenu, setSubMenu, showSetting, showSubMenu, skip, stopHideSubMenu, unMounted, watchWidth, willHideSetting, willHideSubMenu };