@blocklet/ui-react
Version:
Some useful front-end web components that can be used in Blocklets.
94 lines (82 loc) • 3.13 kB
JSX
import { useMemo } from 'react';
import PropTypes from 'prop-types';
import { styled, useTheme, deepmerge, ThemeProvider } from '@arcblock/ux/lib/Theme';
import { withErrorBoundary } from 'react-error-boundary';
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import { ErrorFallback } from '@arcblock/ux/lib/ErrorBoundary';
import useBlockletLogo from '@arcblock/ux/lib/hooks/use-blocklet-logo';
import omit from 'lodash/omit';
import InternalFooter from './internal-footer';
import { mapRecursive } from '../utils';
import { formatBlockletInfo, getLocalizedNavigation } from '../blocklets';
import { BlockletMetaProps } from '../types';
import withHideWhenEmbed from '../libs/with-hide-when-embed';
/**
* 专门用于 (composable) blocklet 的 Footer 组件, 基于 blocklet meta 中的数据渲染
*/
function Footer({ meta = {}, theme: themeOverrides = null, ...rest }) {
const { locale } = useLocaleContext() || {};
const parentTheme = useTheme();
const formattedBlocklet = useMemo(() => {
const blocklet = Object.assign({}, window.blocklet, meta);
try {
return formatBlockletInfo(blocklet);
} catch (e) {
console.error('Failed to format blocklet info', e, blocklet);
return blocklet;
}
}, [meta]);
const mergeTheme = useMemo(() => deepmerge(parentTheme, themeOverrides), [parentTheme, themeOverrides]);
const appLogo = useBlockletLogo({
key: 'xs', // 始终优先长 logo
meta,
theme: themeOverrides,
});
if (!formattedBlocklet.appName) {
return null;
}
const { appName, appDescription, description, copyright } = formattedBlocklet;
const $bgColor = mergeTheme.palette.background.default;
const localized = {
footerNav: getLocalizedNavigation(formattedBlocklet?.navigation?.footer, locale) || [],
socialMedia: getLocalizedNavigation(formattedBlocklet?.navigation?.social, locale) || [],
links: getLocalizedNavigation(formattedBlocklet?.navigation?.bottom, locale) || [],
};
const props = {
brand: {
name: appName,
description: appDescription || description,
logo: appLogo,
},
navigation: mapRecursive(
localized.footerNav,
(item) => ({
...item,
label: item.title,
link: item.link,
}),
'items'
),
copyright,
socialMedia: localized.socialMedia,
links: localized.links.map((item) => ({ ...item, label: item.title })),
};
return (
<ThemeProvider theme={mergeTheme}>
<StyledInternalFooter {...props} {...omit(rest, ['bordered'])} $bordered={rest?.bordered} $bgcolor={$bgColor} />
</ThemeProvider>
);
}
Footer.propTypes = {
meta: BlockletMetaProps,
// 允许覆盖 footer 内置的 theme
theme: PropTypes.object,
};
const StyledInternalFooter = styled(InternalFooter)`
${({ $bordered, theme }) => `border-top: 1px solid ${$bordered && theme.palette.divider};`}
color: ${({ theme }) => theme.palette.text.secondary};
${({ $bgcolor }) => $bgcolor && `background-color: ${$bgcolor};`}
`;
export default withErrorBoundary(withHideWhenEmbed(Footer), {
FallbackComponent: ErrorFallback,
});