@blocklet/ui-react
Version:
Some useful front-end web components that can be used in Blocklets.
140 lines (131 loc) • 3.85 kB
JSX
import PropTypes from 'prop-types';
import { Box } from '@mui/material';
import clsx from 'clsx';
import Brand from './brand';
import Links from './links';
import SocialMedia from './social-media';
import Copyright from './copyright';
import StandardLayout from './layout/standard';
import PlainLayout from './layout/plain';
const layouts = [
{
name: 'plain',
// 没有 navigation 和 socialMedia, 使用一个简单的两端对齐布局
support: (_, data) => !data.navigation?.length && !data.socialMedia?.length,
component: PlainLayout,
},
{
name: 'standard',
// 默认标准布局
support: () => true,
component: StandardLayout,
},
];
const layoutsKeyByName = layouts.reduce((acc, cur) => ({ ...acc, [cur.name]: cur }), {});
/**
* 通用的内部 footer 组件, 定义并渲染常见的几种 footer 元素: brand/navigation/social medial 等
*/
function InternalFooter({ ...props }) {
const {
brand = null,
navigation = null,
socialMedia = null,
copyright = null,
links = null,
layout = 'auto',
...rest
} = props;
const renderBrand = () => {
return brand ? <Brand {...brand} /> : null;
};
const renderNavigation = () => {
return navigation?.length ? <Links links={navigation} columns={3} /> : null;
};
const renderSocialMedia = () => {
return socialMedia?.length ? <SocialMedia items={socialMedia} /> : null;
};
const renderCopyright = () => {
// 如果 copyright.owner 不存在, 则使用 brand.name, 如果 brand.name 也不存在, copyright 元素为空
const copyrightOwner = copyright?.owner || brand?.name;
if (!copyrightOwner) {
return null;
}
return <Copyright owner={copyrightOwner} year={copyright?.year || undefined} />;
};
const renderLinks = () => {
return links?.length ? <Links flowLayout links={links} /> : null;
};
const elements = {
brand: renderBrand(),
navigation: renderNavigation(),
socialMedia: renderSocialMedia(),
copyright: renderCopyright(),
links: renderLinks(),
};
let LayoutComponent = null;
if (layout === 'auto') {
LayoutComponent = layouts.find((item) => item.support(elements, props)).component;
} else {
LayoutComponent = layoutsKeyByName[layout]?.component;
}
if (!LayoutComponent) {
throw new Error(`layout ${layout} is not supported.`);
}
return (
<Box
{...rest}
className={clsx('blocklet__footer', rest.className)}
sx={[
{
position: 'relative',
},
...(Array.isArray(rest.sx) ? rest.sx : [rest.sx]),
]}>
<LayoutComponent elements={elements} data={props} />
<Box
sx={{
position: 'absolute',
right: 16,
bottom: 0,
fontSize: 12,
color: 'transparent',
'::selection': { background: '#000', color: '#fff' },
}}>
{window?.blocklet?.version}
</Box>
</Box>
);
}
InternalFooter.propTypes = {
brand: PropTypes.shape({
name: PropTypes.node,
description: PropTypes.string,
logo: PropTypes.node,
}),
navigation: PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.node,
link: PropTypes.string,
})
),
socialMedia: PropTypes.arrayOf(
PropTypes.shape({
icon: PropTypes.node,
link: PropTypes.string,
})
),
copyright: PropTypes.shape({
owner: PropTypes.string,
year: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}),
// privacy/legal 等链接, 常放于 footer 右下侧或最底部
links: PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.node,
link: PropTypes.string,
})
),
// 可显式指定 footer layout, 默认根据内容自动决定 layout
layout: PropTypes.oneOf(['auto', 'standard', 'plain']),
};
export default InternalFooter;