@blocklet/ui-react
Version:
Some useful front-end web components that can be used in Blocklets.
178 lines (162 loc) • 4.7 kB
JavaScript
import { mapRecursive, filterRecursive, isUrl } from './utils';
export const publicPath = window?.blocklet?.groupPrefix || window?.blocklet?.prefix || '/';
/**
* 格式化 theme (目前仅考虑 background)
*/
export const formatTheme = (theme) => {
const formatted = { ...theme };
const background = theme?.background;
if (typeof background === 'string') {
formatted.background = { header: background, footer: background, default: background };
} else if (background && typeof background === 'object') {
formatted.background = {
header: background.header || background.default,
footer: background.footer || background.default,
default: background.default,
};
}
return formatted;
};
export const getLink = (link, locale = 'en') => {
if (typeof link === 'string') {
// http[s] 开头的 url
if (isUrl(link)) {
const url = new URL(link);
url.searchParams.set('locale', locale);
return url.href;
}
const url = new URL(link, window.location.origin);
url.searchParams.set('locale', locale);
return url.pathname + url.search;
}
if (typeof link === 'object') {
return link[locale] || link?.en || link?.zh;
}
return link;
};
/**
* 获取指定 locale 对应的 navigation 数据, 仅考虑 zh/en
*/
export const getLocalizedNavigation = (navigation, locale = 'en') => {
if (!navigation?.length) {
return navigation;
}
const trans = (text, _locale) => {
if (text && typeof text === 'object') {
return text[_locale] || text?.en || text?.zh;
}
return text;
};
return mapRecursive(
navigation,
(item) => {
return {
...item,
title: trans(item.title, locale),
description: trans(item.description, locale),
// 仅对叶结点进行处理
link: !item.items?.length ? getLink(item.link, locale) : item.link,
_rawLink: item.link,
};
},
'items'
);
};
/**
* 格式化 navigation
*
* - role 统一为数组形式
*/
export const formatNavigation = (navigation) => {
return mapRecursive(
navigation,
(item) => {
if (item.role) {
return {
...item,
role: Array.isArray(item.role) ? item.role : [item.role],
};
}
return item;
},
'items'
);
};
export const parseNavigation = (navigation) => {
if (!navigation?.length) {
return null;
}
const formattedNav = formatNavigation(navigation);
const sections = {
header: [],
footer: [],
// 对应 footer social media
social: [],
// 对应 footer 底部 links
bottom: [],
// 对应 dashboard#sidenav 导航
dashboard: [],
// session manager menus
sessionManager: [],
userCenter: [],
};
// 对 navigation 顶层元素按 section 分组
formattedNav.forEach((item) => {
// item#section 为空时, 表示只存在于 header
if (!item.section) {
sections.header.push(item);
// item 出现在指定几个 section 中 (array)
} else if (Array.isArray(item.section)) {
item.section.forEach((sectionKey) => {
sections[sectionKey]?.push(item);
});
// item 出现在指定的一个 section 中 (string)
} else if (typeof item.section === 'string') {
sections[item.section]?.push(item);
}
});
return sections;
};
/**
* 格式化 blocklet info 数据
*/
export const formatBlockletInfo = (blockletInfo) => {
if (!blockletInfo) {
return null;
}
const formatted = { ...blockletInfo };
// theme
formatted.theme = formatTheme(formatted.theme);
// navigation
formatted.navigation = parseNavigation(filterValidNavItems(formatted.navigation));
return formatted;
};
/**
* 过滤掉无效结点 (无 link 且子元素为空)
*/
export const filterValidNavItems = (navigation = []) => {
return filterRecursive(navigation, (item, context) => !!item.link || context.filteredChildren?.length, 'items');
};
/**
* 根据 role 筛选 nav, 规则:
* - 如果是枝结点, 必须至少存在一个符合条件的子结点
* - role 未定义, 符合条件
* - role 定义且包括当前的 userRole, 符合条件
*
* @param {object[]} nav 导航菜单数据
* @param {string} userRole 当前用户 role
* @returns 符合 role 权限的导航菜单数据
*/
export const filterNavByRole = (nav, userRole) => {
return filterRecursive(
nav,
(item, context) => {
const isRoleMatched = !item.role || (userRole && (item.role.includes(userRole) || item.role.includes('guest')));
if (!context.isLeaf) {
return isRoleMatched && context.filteredChildren?.length;
}
return isRoleMatched;
},
'items'
);
};