UNPKG

antd

Version:

An enterprise-class UI design language and React components implementation

436 lines (435 loc) 12.8 kB
"use client"; import * as React from 'react'; import { createTheme, StyleContext as CssInJsStyleContext } from '@ant-design/cssinjs'; import IconContext from "@ant-design/icons/es/components/Context"; import { merge } from '@rc-component/util'; import useMemo from "@rc-component/util/es/hooks/useMemo"; import warning, { devUseWarning, WarningContext } from '../_util/warning'; import ValidateMessagesContext from '../form/validateMessagesContext'; import LocaleProvider, { ANT_MARK } from '../locale'; import LocaleContext from '../locale/context'; import defaultLocale from '../locale/en_US'; import { defaultTheme, DesignTokenContext } from '../theme/context'; import defaultSeedToken from '../theme/themes/seed'; import UniqueProvider from '../tooltip/UniqueProvider'; import { ConfigConsumer, ConfigContext, defaultIconPrefixCls, defaultPrefixCls, Variants } from './context'; import { DisabledContextProvider } from './DisabledContext'; import useConfig from './hooks/useConfig'; import useTheme from './hooks/useTheme'; import MotionWrapper from './MotionWrapper'; import PropWarning from './PropWarning'; import SizeContext, { SizeContextProvider } from './SizeContext'; import useStyle from './style'; export { Variants }; /** * Since too many feedback using static method like `Modal.confirm` not getting theme, we record the * theme register info here to help developer get warning info. */ let existThemeConfig = false; export const warnContext = process.env.NODE_ENV !== 'production' ? componentName => { process.env.NODE_ENV !== "production" ? warning(!existThemeConfig, componentName, `Static function can not consume context like dynamic theme. Please use 'App' component instead.`) : void 0; } : /* istanbul ignore next */ null; export { ConfigConsumer, ConfigContext, defaultIconPrefixCls, defaultPrefixCls }; export const configConsumerProps = ['getTargetContainer', 'getPopupContainer', 'rootPrefixCls', 'getPrefixCls', 'renderEmpty', 'csp', 'autoInsertSpaceInButton', 'locale']; // These props is used by `useContext` directly in sub component const PASSED_PROPS = ['getTargetContainer', 'getPopupContainer', 'renderEmpty', 'input', 'pagination', 'form', 'select', 'button']; let globalPrefixCls; let globalIconPrefixCls; let globalTheme; let globalHolderRender; function getGlobalPrefixCls() { return globalPrefixCls || defaultPrefixCls; } function getGlobalIconPrefixCls() { return globalIconPrefixCls || defaultIconPrefixCls; } const setGlobalConfig = props => { const { prefixCls, iconPrefixCls, theme, holderRender } = props; if (prefixCls !== undefined) { globalPrefixCls = prefixCls; } if (iconPrefixCls !== undefined) { globalIconPrefixCls = iconPrefixCls; } if ('holderRender' in props) { globalHolderRender = holderRender; } if (theme) { globalTheme = theme; } }; export const globalConfig = () => ({ getPrefixCls: (suffixCls, customizePrefixCls) => { if (customizePrefixCls) { return customizePrefixCls; } return suffixCls ? `${getGlobalPrefixCls()}-${suffixCls}` : getGlobalPrefixCls(); }, getIconPrefixCls: getGlobalIconPrefixCls, getRootPrefixCls: () => { // If Global prefixCls provided, use this if (globalPrefixCls) { return globalPrefixCls; } // Fallback to default prefixCls return getGlobalPrefixCls(); }, getTheme: () => globalTheme, holderRender: globalHolderRender }); const ProviderChildren = props => { const { children, csp: customCsp, autoInsertSpaceInButton, alert, affix, anchor, form, locale, componentSize, direction, space, splitter, virtual, dropdownMatchSelectWidth, popupMatchSelectWidth, popupOverflow, legacyLocale, parentContext, iconPrefixCls: customIconPrefixCls, theme, componentDisabled, segmented, statistic, spin, calendar, carousel, cascader, collapse, typography, checkbox, descriptions, divider, drawer, skeleton, steps, image, layout, list, mentions, modal, progress, result, slider, breadcrumb, masonry, menu, pagination, input, textArea, otp, empty, badge, radio, rate, ribbon, switch: SWITCH, transfer, avatar, message, tag, table, card, cardMeta, tabs, timeline, timePicker, upload, notification, tree, colorPicker, datePicker, rangePicker, flex, wave, dropdown, warning: warningConfig, tour, tooltip, popover, popconfirm, qrcode, floatButton, floatButtonGroup, variant, inputNumber, treeSelect, watermark } = props; // =================================== Context =================================== const getPrefixCls = React.useCallback((suffixCls, customizePrefixCls) => { const { prefixCls } = props; if (customizePrefixCls) { return customizePrefixCls; } const mergedPrefixCls = prefixCls || parentContext.getPrefixCls(''); return suffixCls ? `${mergedPrefixCls}-${suffixCls}` : mergedPrefixCls; }, [parentContext.getPrefixCls, props.prefixCls]); const iconPrefixCls = customIconPrefixCls || parentContext.iconPrefixCls || defaultIconPrefixCls; const csp = customCsp || parentContext.csp; useStyle(iconPrefixCls, csp); const mergedTheme = useTheme(theme, parentContext.theme, { prefixCls: getPrefixCls('') }); if (process.env.NODE_ENV !== 'production') { existThemeConfig = existThemeConfig || !!mergedTheme; } const baseConfig = { csp, autoInsertSpaceInButton, alert, affix, anchor, locale: locale || legacyLocale, direction, space, splitter, virtual, popupMatchSelectWidth: popupMatchSelectWidth ?? dropdownMatchSelectWidth, popupOverflow, getPrefixCls, iconPrefixCls, theme: mergedTheme, segmented, statistic, spin, calendar, carousel, cascader, collapse, typography, checkbox, descriptions, divider, drawer, skeleton, steps, image, input, textArea, otp, layout, list, mentions, modal, progress, result, slider, breadcrumb, masonry, menu, pagination, empty, badge, radio, rate, ribbon, switch: SWITCH, transfer, avatar, message, tag, table, card, cardMeta, tabs, timeline, timePicker, upload, notification, tree, colorPicker, datePicker, rangePicker, flex, wave, dropdown, warning: warningConfig, tour, tooltip, popover, popconfirm, qrcode, floatButton, floatButtonGroup, variant, inputNumber, treeSelect, watermark }; if (process.env.NODE_ENV !== 'production') { const warningFn = devUseWarning('ConfigProvider'); warningFn(!('autoInsertSpaceInButton' in props), 'deprecated', '`autoInsertSpaceInButton` is deprecated. Please use `{ button: { autoInsertSpace: boolean }}` instead.'); } const config = { ...parentContext }; Object.keys(baseConfig).forEach(key => { if (baseConfig[key] !== undefined) { config[key] = baseConfig[key]; } }); // Pass the props used by `useContext` directly with child component. // These props should merged into `config`. PASSED_PROPS.forEach(propName => { const propValue = props[propName]; if (propValue) { config[propName] = propValue; } }); if (typeof autoInsertSpaceInButton !== 'undefined') { // merge deprecated api config.button = { autoInsertSpace: autoInsertSpaceInButton, ...config.button }; } // https://github.com/ant-design/ant-design/issues/27617 const memoedConfig = useMemo(() => config, config, (prevConfig, currentConfig) => { const prevKeys = Object.keys(prevConfig); const currentKeys = Object.keys(currentConfig); return prevKeys.length !== currentKeys.length || prevKeys.some(key => prevConfig[key] !== currentConfig[key]); }); const { layer } = React.useContext(CssInJsStyleContext); const memoIconContextValue = React.useMemo(() => ({ prefixCls: iconPrefixCls, csp, layer: layer ? 'antd' : undefined }), [iconPrefixCls, csp, layer]); let childNode = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(PropWarning, { dropdownMatchSelectWidth: dropdownMatchSelectWidth }), children); const validateMessages = React.useMemo(() => merge(defaultLocale.Form?.defaultValidateMessages || {}, memoedConfig.locale?.Form?.defaultValidateMessages || {}, memoedConfig.form?.validateMessages || {}, form?.validateMessages || {}), [memoedConfig, form?.validateMessages]); if (Object.keys(validateMessages).length > 0) { childNode = /*#__PURE__*/React.createElement(ValidateMessagesContext.Provider, { value: validateMessages }, childNode); } if (locale) { childNode = /*#__PURE__*/React.createElement(LocaleProvider, { locale: locale, _ANT_MARK__: ANT_MARK }, childNode); } if (iconPrefixCls || csp) { childNode = /*#__PURE__*/React.createElement(IconContext.Provider, { value: memoIconContextValue }, childNode); } if (componentSize) { childNode = /*#__PURE__*/React.createElement(SizeContextProvider, { size: componentSize }, childNode); } // =================================== Motion =================================== childNode = /*#__PURE__*/React.createElement(MotionWrapper, null, childNode); // ================================ Tooltip Unique =============================== if (tooltip?.unique) { childNode = /*#__PURE__*/React.createElement(UniqueProvider, null, childNode); } // ================================ Dynamic theme ================================ const memoTheme = React.useMemo(() => { const { algorithm, token, components, cssVar, ...rest } = mergedTheme || {}; const themeObj = algorithm && (!Array.isArray(algorithm) || algorithm.length > 0) ? createTheme(algorithm) : defaultTheme; const parsedComponents = {}; Object.entries(components || {}).forEach(([componentName, componentToken]) => { const parsedToken = { ...componentToken }; if ('algorithm' in parsedToken) { if (parsedToken.algorithm === true) { parsedToken.theme = themeObj; } else if (Array.isArray(parsedToken.algorithm) || typeof parsedToken.algorithm === 'function') { parsedToken.theme = createTheme(parsedToken.algorithm); } delete parsedToken.algorithm; } parsedComponents[componentName] = parsedToken; }); const mergedToken = { ...defaultSeedToken, ...token }; return { ...rest, theme: themeObj, token: mergedToken, components: parsedComponents, override: { override: mergedToken, ...parsedComponents }, cssVar }; }, [mergedTheme]); if (theme) { childNode = /*#__PURE__*/React.createElement(DesignTokenContext.Provider, { value: memoTheme }, childNode); } // ================================== Warning =================================== if (memoedConfig.warning) { childNode = /*#__PURE__*/React.createElement(WarningContext.Provider, { value: memoedConfig.warning }, childNode); } // =================================== Render =================================== if (componentDisabled !== undefined) { childNode = /*#__PURE__*/React.createElement(DisabledContextProvider, { disabled: componentDisabled }, childNode); } return /*#__PURE__*/React.createElement(ConfigContext.Provider, { value: memoedConfig }, childNode); }; const ConfigProvider = props => { const context = React.useContext(ConfigContext); const antLocale = React.useContext(LocaleContext); return /*#__PURE__*/React.createElement(ProviderChildren, { parentContext: context, legacyLocale: antLocale, ...props }); }; ConfigProvider.ConfigContext = ConfigContext; ConfigProvider.SizeContext = SizeContext; ConfigProvider.config = setGlobalConfig; ConfigProvider.useConfig = useConfig; Object.defineProperty(ConfigProvider, 'SizeContext', { get: () => { process.env.NODE_ENV !== "production" ? warning(false, 'ConfigProvider', 'ConfigProvider.SizeContext is deprecated. Please use `ConfigProvider.useConfig().componentSize` instead.') : void 0; return SizeContext; } }); if (process.env.NODE_ENV !== 'production') { ConfigProvider.displayName = 'ConfigProvider'; } export default ConfigProvider;