style: configprovider
parent
a4b8c68533
commit
5da035d275
|
@ -1,9 +0,0 @@
|
|||
import type { ComputedRef } from 'vue';
|
||||
import { computed, inject } from 'vue';
|
||||
import { defaultConfigProvider } from '../../config-provider';
|
||||
|
||||
export default (name: string, props: Record<any, any>): ComputedRef<string> => {
|
||||
const configProvider = inject('configProvider', defaultConfigProvider);
|
||||
const prefixCls = computed(() => configProvider.getPrefixCls(name, props.prefixCls));
|
||||
return prefixCls;
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
import type { CSSProperties, ExtractPropTypes, PropType } from 'vue';
|
||||
import { inject, defineComponent, ref } from 'vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
|
||||
import CheckCircleOutlined from '@ant-design/icons-vue/CheckCircleOutlined';
|
||||
import ExclamationCircleOutlined from '@ant-design/icons-vue/ExclamationCircleOutlined';
|
||||
|
@ -13,10 +13,10 @@ import classNames from '../_util/classNames';
|
|||
import PropTypes from '../_util/vue-types';
|
||||
import { getTransitionProps, Transition } from '../_util/transition';
|
||||
import { isValidElement, getPropsSlot } from '../_util/props-util';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import { tuple, withInstall } from '../_util/type';
|
||||
import { cloneElement } from '../_util/vnode';
|
||||
import type { NodeMouseEventHandler } from '../vc-tree/contextTypes';
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
|
||||
function noop() {}
|
||||
|
||||
|
@ -69,7 +69,7 @@ const Alert = defineComponent({
|
|||
inheritAttrs: false,
|
||||
props: alertProps(),
|
||||
setup(props, { slots, emit, attrs, expose }) {
|
||||
const configProvider = inject('configProvider', defaultConfigProvider);
|
||||
const { prefixCls, direction } = useConfigInject('alert', props);
|
||||
const closing = ref(false);
|
||||
const closed = ref(false);
|
||||
const alertNode = ref();
|
||||
|
@ -97,13 +97,7 @@ const Alert = defineComponent({
|
|||
expose({ animationEnd });
|
||||
const motionStyle = ref<CSSProperties>({});
|
||||
return () => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
banner,
|
||||
closeIcon: customCloseIcon = slots.closeIcon?.(),
|
||||
} = props;
|
||||
const { getPrefixCls } = configProvider;
|
||||
const prefixCls = getPrefixCls('alert', customizePrefixCls);
|
||||
const { banner, closeIcon: customCloseIcon = slots.closeIcon?.() } = props;
|
||||
|
||||
let { closable, type, showIcon } = props;
|
||||
|
||||
|
@ -123,20 +117,26 @@ const Alert = defineComponent({
|
|||
if (closeText) {
|
||||
closable = true;
|
||||
}
|
||||
|
||||
const alertCls = classNames(prefixCls, {
|
||||
[`${prefixCls}-${type}`]: true,
|
||||
[`${prefixCls}-closing`]: closing.value,
|
||||
[`${prefixCls}-with-description`]: !!description,
|
||||
[`${prefixCls}-no-icon`]: !showIcon,
|
||||
[`${prefixCls}-banner`]: !!banner,
|
||||
[`${prefixCls}-closable`]: closable,
|
||||
const prefixClsValue = prefixCls.value;
|
||||
const alertCls = classNames(prefixClsValue, {
|
||||
[`${prefixClsValue}-${type}`]: true,
|
||||
[`${prefixClsValue}-closing`]: closing.value,
|
||||
[`${prefixClsValue}-with-description`]: !!description,
|
||||
[`${prefixClsValue}-no-icon`]: !showIcon,
|
||||
[`${prefixClsValue}-banner`]: !!banner,
|
||||
[`${prefixClsValue}-closable`]: closable,
|
||||
[`${prefixClsValue}-rtl`]: direction.value === 'rtl',
|
||||
});
|
||||
|
||||
const closeIcon = closable ? (
|
||||
<button type="button" onClick={handleClose} class={`${prefixCls}-close-icon`} tabindex={0}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClose}
|
||||
class={`${prefixClsValue}-close-icon`}
|
||||
tabindex={0}
|
||||
>
|
||||
{closeText ? (
|
||||
<span class={`${prefixCls}-close-text`}>{closeText}</span>
|
||||
<span class={`${prefixClsValue}-close-text`}>{closeText}</span>
|
||||
) : customCloseIcon === undefined ? (
|
||||
<CloseOutlined />
|
||||
) : (
|
||||
|
@ -148,13 +148,13 @@ const Alert = defineComponent({
|
|||
const iconNode = (icon &&
|
||||
(isValidElement(icon) ? (
|
||||
cloneElement(icon, {
|
||||
class: `${prefixCls}-icon`,
|
||||
class: `${prefixClsValue}-icon`,
|
||||
})
|
||||
) : (
|
||||
<span class={`${prefixCls}-icon`}>{icon}</span>
|
||||
))) || <IconType class={`${prefixCls}-icon`} />;
|
||||
<span class={`${prefixClsValue}-icon`}>{icon}</span>
|
||||
))) || <IconType class={`${prefixClsValue}-icon`} />;
|
||||
|
||||
const transitionProps = getTransitionProps(`${prefixCls}-motion`, {
|
||||
const transitionProps = getTransitionProps(`${prefixClsValue}-motion`, {
|
||||
appear: false,
|
||||
css: true,
|
||||
onAfterLeave: animationEnd,
|
||||
|
@ -177,9 +177,11 @@ const Alert = defineComponent({
|
|||
ref={alertNode}
|
||||
>
|
||||
{showIcon ? iconNode : null}
|
||||
<div class={`${prefixCls}-content`}>
|
||||
{message ? <div class={`${prefixCls}-message`}>{message}</div> : null}
|
||||
{description ? <div class={`${prefixCls}-description`}>{description}</div> : null}
|
||||
<div class={`${prefixClsValue}-content`}>
|
||||
{message ? <div class={`${prefixClsValue}-message`}>{message}</div> : null}
|
||||
{description ? (
|
||||
<div class={`${prefixClsValue}-description`}>{description}</div>
|
||||
) : null}
|
||||
</div>
|
||||
{closeIcon}
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import type { ExtractPropTypes, PropType } from 'vue';
|
||||
import {
|
||||
defineComponent,
|
||||
inject,
|
||||
nextTick,
|
||||
onActivated,
|
||||
onBeforeUnmount,
|
||||
|
@ -10,17 +9,16 @@ import {
|
|||
ref,
|
||||
watch,
|
||||
onDeactivated,
|
||||
computed,
|
||||
} from 'vue';
|
||||
import VerticalAlignTopOutlined from '@ant-design/icons-vue/VerticalAlignTopOutlined';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import addEventListener from '../vc-util/Dom/addEventListener';
|
||||
import getScroll from '../_util/getScroll';
|
||||
import { getTransitionProps, Transition } from '../_util/transition';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import scrollTo from '../_util/scrollTo';
|
||||
import { withInstall } from '../_util/type';
|
||||
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
|
||||
export const backTopProps = {
|
||||
visibilityHeight: PropTypes.number.def(400),
|
||||
|
@ -39,7 +37,7 @@ const BackTop = defineComponent({
|
|||
props: backTopProps,
|
||||
emits: ['click'],
|
||||
setup(props, { slots, attrs, emit }) {
|
||||
const configProvider = inject('configProvider', defaultConfigProvider);
|
||||
const { prefixCls, direction } = useConfigInject('back-top', props);
|
||||
const domRef = ref();
|
||||
const state = reactive({
|
||||
visible: false,
|
||||
|
@ -113,8 +111,6 @@ const BackTop = defineComponent({
|
|||
scrollRemove();
|
||||
});
|
||||
|
||||
const prefixCls = computed(() => configProvider.getPrefixCls('back-top', props.prefixCls));
|
||||
|
||||
return () => {
|
||||
const defaultElement = (
|
||||
<div class={`${prefixCls.value}-content`}>
|
||||
|
@ -129,7 +125,7 @@ const BackTop = defineComponent({
|
|||
class: {
|
||||
[`${prefixCls.value}`]: true,
|
||||
[`${attrs.class}`]: attrs.class,
|
||||
[`${prefixCls.value}-rtl`]: configProvider.direction === 'rtl',
|
||||
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
import type { ExtractPropTypes, InjectionKey, PropType, Ref } from 'vue';
|
||||
import { inject, provide } from 'vue';
|
||||
import type { ValidateMessages } from '../form/interface';
|
||||
import type { RequiredMark } from '../form/Form';
|
||||
import type { RenderEmptyHandler } from './renderEmpty';
|
||||
import type { TransformCellTextProps } from '../table/interface';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import type { Locale } from '../locale-provider';
|
||||
|
||||
type GlobalFormCOntextProps = {
|
||||
validateMessages?: Ref<ValidateMessages>;
|
||||
};
|
||||
export const GlobalFormContextKey: InjectionKey<GlobalFormCOntextProps> =
|
||||
Symbol('GlobalFormContextKey');
|
||||
|
||||
export const useProvideGlobalForm = (state: GlobalFormCOntextProps) => {
|
||||
provide(GlobalFormContextKey, state);
|
||||
};
|
||||
|
||||
export const useInjectGlobalForm = () => {
|
||||
return inject(GlobalFormContextKey, {});
|
||||
};
|
||||
|
||||
export const GlobalConfigContextKey: InjectionKey<GlobalFormCOntextProps> =
|
||||
Symbol('GlobalConfigContextKey');
|
||||
|
||||
export interface CSPConfig {
|
||||
nonce?: string;
|
||||
}
|
||||
export interface Theme {
|
||||
primaryColor?: string;
|
||||
infoColor?: string;
|
||||
successColor?: string;
|
||||
processingColor?: string;
|
||||
errorColor?: string;
|
||||
warningColor?: string;
|
||||
}
|
||||
|
||||
export type SizeType = 'small' | 'middle' | 'large' | undefined;
|
||||
|
||||
export type Direction = 'ltr' | 'rtl';
|
||||
|
||||
export interface ConfigConsumerProps {
|
||||
getTargetContainer?: () => HTMLElement;
|
||||
getPopupContainer?: (triggerNode?: HTMLElement) => HTMLElement;
|
||||
rootPrefixCls?: string;
|
||||
getPrefixCls: (suffixCls?: string, customizePrefixCls?: string) => string;
|
||||
renderEmpty: RenderEmptyHandler;
|
||||
transformCellText?: (tableProps: TransformCellTextProps) => any;
|
||||
csp?: CSPConfig;
|
||||
autoInsertSpaceInButton?: boolean;
|
||||
input?: {
|
||||
autocomplete?: string;
|
||||
};
|
||||
locale?: Locale;
|
||||
pageHeader?: {
|
||||
ghost: boolean;
|
||||
};
|
||||
componentSize?: SizeType;
|
||||
direction?: 'ltr' | 'rtl';
|
||||
space?: {
|
||||
size?: SizeType | number;
|
||||
};
|
||||
virtual?: boolean;
|
||||
dropdownMatchSelectWidth?: boolean | number;
|
||||
form?: {
|
||||
requiredMark?: RequiredMark;
|
||||
colon?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export const configProviderProps = () => ({
|
||||
getTargetContainer: {
|
||||
type: Function as PropType<() => HTMLElement>,
|
||||
},
|
||||
getPopupContainer: {
|
||||
type: Function as PropType<(triggerNode?: HTMLElement) => HTMLElement>,
|
||||
},
|
||||
prefixCls: String,
|
||||
getPrefixCls: {
|
||||
type: Function as PropType<(suffixCls?: string, customizePrefixCls?: string) => string>,
|
||||
},
|
||||
renderEmpty: {
|
||||
type: Function as PropType<RenderEmptyHandler>,
|
||||
},
|
||||
transformCellText: {
|
||||
type: Function as PropType<(tableProps: TransformCellTextProps) => any>,
|
||||
},
|
||||
csp: {
|
||||
type: Object as PropType<CSPConfig>,
|
||||
default: undefined as CSPConfig,
|
||||
},
|
||||
input: {
|
||||
type: Object as PropType<{ autocomplete: string }>,
|
||||
},
|
||||
autoInsertSpaceInButton: PropTypes.looseBool,
|
||||
locale: {
|
||||
type: Object as PropType<Locale>,
|
||||
default: undefined as Locale,
|
||||
},
|
||||
pageHeader: {
|
||||
type: Object as PropType<{ ghost: boolean }>,
|
||||
},
|
||||
componentSize: {
|
||||
type: String as PropType<SizeType>,
|
||||
},
|
||||
direction: {
|
||||
type: String as PropType<'ltr' | 'rtl'>,
|
||||
},
|
||||
space: {
|
||||
type: Object as PropType<{ size: SizeType | number }>,
|
||||
},
|
||||
virtual: PropTypes.looseBool,
|
||||
dropdownMatchSelectWidth: { type: [Number, Boolean], default: true },
|
||||
form: {
|
||||
type: Object as PropType<{
|
||||
validateMessages?: ValidateMessages;
|
||||
requiredMark?: RequiredMark;
|
||||
colon?: boolean;
|
||||
}>,
|
||||
default: undefined as {
|
||||
validateMessages?: ValidateMessages;
|
||||
requiredMark?: RequiredMark;
|
||||
colon?: boolean;
|
||||
},
|
||||
},
|
||||
// internal use
|
||||
notUpdateGlobalConfig: Boolean,
|
||||
});
|
||||
|
||||
export type ConfigProviderProps = Partial<ExtractPropTypes<ReturnType<typeof configProviderProps>>>;
|
|
@ -0,0 +1,97 @@
|
|||
/* eslint-disable import/prefer-default-export, prefer-destructuring */
|
||||
|
||||
import { TinyColor } from '@ctrl/tinycolor';
|
||||
import { generate } from '@ant-design/colors';
|
||||
import type { Theme } from './context';
|
||||
import { updateCSS } from '../vc-util/Dom/dynamicCSS';
|
||||
|
||||
const dynamicStyleMark = `-ant-${Date.now()}-${Math.random()}`;
|
||||
|
||||
export function registerTheme(globalPrefixCls: string, theme: Theme) {
|
||||
const variables: Record<string, string> = {};
|
||||
|
||||
const formatColor = (
|
||||
color: TinyColor,
|
||||
updater?: (cloneColor: TinyColor) => TinyColor | undefined,
|
||||
) => {
|
||||
let clone = color.clone();
|
||||
clone = updater?.(clone) || clone;
|
||||
return clone.toRgbString();
|
||||
};
|
||||
|
||||
const fillColor = (colorVal: string, type: string) => {
|
||||
const baseColor = new TinyColor(colorVal);
|
||||
const colorPalettes = generate(baseColor.toRgbString());
|
||||
|
||||
variables[`${type}-color`] = formatColor(baseColor);
|
||||
variables[`${type}-color-disabled`] = colorPalettes[1];
|
||||
variables[`${type}-color-hover`] = colorPalettes[4];
|
||||
variables[`${type}-color-active`] = colorPalettes[7];
|
||||
variables[`${type}-color-outline`] = baseColor.clone().setAlpha(0.2).toRgbString();
|
||||
variables[`${type}-color-deprecated-bg`] = colorPalettes[1];
|
||||
variables[`${type}-color-deprecated-border`] = colorPalettes[3];
|
||||
};
|
||||
|
||||
// ================ Primary Color ================
|
||||
if (theme.primaryColor) {
|
||||
fillColor(theme.primaryColor, 'primary');
|
||||
|
||||
const primaryColor = new TinyColor(theme.primaryColor);
|
||||
const primaryColors = generate(primaryColor.toRgbString());
|
||||
|
||||
// Legacy - We should use semantic naming standard
|
||||
primaryColors.forEach((color, index) => {
|
||||
variables[`primary-${index + 1}`] = color;
|
||||
});
|
||||
// Deprecated
|
||||
variables['primary-color-deprecated-l-35'] = formatColor(primaryColor, c => c.lighten(35));
|
||||
variables['primary-color-deprecated-l-20'] = formatColor(primaryColor, c => c.lighten(20));
|
||||
variables['primary-color-deprecated-t-20'] = formatColor(primaryColor, c => c.tint(20));
|
||||
variables['primary-color-deprecated-t-50'] = formatColor(primaryColor, c => c.tint(50));
|
||||
variables['primary-color-deprecated-f-12'] = formatColor(primaryColor, c =>
|
||||
c.setAlpha(c.getAlpha() * 0.12),
|
||||
);
|
||||
|
||||
const primaryActiveColor = new TinyColor(primaryColors[0]);
|
||||
variables['primary-color-active-deprecated-f-30'] = formatColor(primaryActiveColor, c =>
|
||||
c.setAlpha(c.getAlpha() * 0.3),
|
||||
);
|
||||
variables['primary-color-active-deprecated-d-02'] = formatColor(primaryActiveColor, c =>
|
||||
c.darken(2),
|
||||
);
|
||||
}
|
||||
|
||||
// ================ Success Color ================
|
||||
if (theme.successColor) {
|
||||
fillColor(theme.successColor, 'success');
|
||||
}
|
||||
|
||||
// ================ Warning Color ================
|
||||
if (theme.warningColor) {
|
||||
fillColor(theme.warningColor, 'warning');
|
||||
}
|
||||
|
||||
// ================= Error Color =================
|
||||
if (theme.errorColor) {
|
||||
fillColor(theme.errorColor, 'error');
|
||||
}
|
||||
|
||||
// ================= Info Color ==================
|
||||
if (theme.infoColor) {
|
||||
fillColor(theme.infoColor, 'info');
|
||||
}
|
||||
|
||||
// Convert to css variables
|
||||
const cssList = Object.keys(variables).map(
|
||||
key => `--${globalPrefixCls}-${key}: ${variables[key]};`,
|
||||
);
|
||||
|
||||
updateCSS(
|
||||
`
|
||||
:root {
|
||||
${cssList.join('\n')}
|
||||
}
|
||||
`,
|
||||
`${dynamicStyleMark}-dynamic-theme`,
|
||||
);
|
||||
}
|
|
@ -48,13 +48,20 @@ Some components use dynamic style to support wave effect. You can config `csp` p
|
|||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| autoInsertSpaceInButton | Set `false` to remove space between 2 chinese characters on Button | boolean | true | |
|
||||
| componentSize | Config antd component size | `small` \| `middle` \| `large` | - | 3.0 |
|
||||
| csp | Set [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) config | { nonce: string } | - | |
|
||||
| direction | Set direction of layout. See [demo](#components-config-provider-demo-direction) | `ltr` \| `rtl` | `ltr` | 3.0 |
|
||||
| dropdownMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width. Default set `min-width` same as input. Will ignore when value less than select width. `false` will disable virtual scroll | boolean \| number | - | 3.0 |
|
||||
| form | Set Form common props | { validateMessages?: [ValidateMessages](/components/form/#validateMessages), requiredMark?: boolean \| `optional` } | - | 3.0 |
|
||||
| renderEmpty | set empty content of components. Ref [Empty](/components/empty/) | slot-scope \| Function(componentName: string): ReactNode | - | |
|
||||
| getPopupContainer | to set the container of the popup element. The default is to create a `div` element in `body`. | Function(triggerNode, dialogContext) | `() => document.body` | |
|
||||
| getTargetContainer | Config Affix, Anchor scroll target container | () => HTMLElement | () => window | 3.0 |
|
||||
| input | Set Input common props | { autocomplete?: string } | - | 3.0 |
|
||||
| locale | language package setting, you can find the packages in [ant-design-vue/es/locale](http://unpkg.com/ant-design-vue/es/locale/) | object | - | 1.5.0 |
|
||||
| prefixCls | set prefix class | string | ant | |
|
||||
| pageHeader | Unify the ghost of pageHeader ,Ref [pageHeader](<(/components/page-header)> | { ghost:boolean } | 'true' | 1.5.0 |
|
||||
| transformCellText | Table data can be changed again before rendering. The default configuration of general user empty data. | Function({ text, column, record, index }) => any | - | 1.5.4 | |
|
||||
| space | Set Space `size`, ref [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number` } | - | 3.0 |
|
||||
| virtual | Disable virtual scroll when set to false | boolean | true | 3.0 |
|
||||
|
||||
### ConfigProvider.config() `3.0.0+`
|
||||
|
|
|
@ -1,66 +1,24 @@
|
|||
import type { PropType, ExtractPropTypes, UnwrapRef, App, Plugin, WatchStopHandle } from 'vue';
|
||||
import { reactive, provide, defineComponent, watch, watchEffect } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import type { UnwrapRef, App, Plugin, WatchStopHandle } from 'vue';
|
||||
import { computed, reactive, provide, defineComponent, watch, watchEffect } from 'vue';
|
||||
import defaultRenderEmpty from './renderEmpty';
|
||||
import type { RenderEmptyHandler } from './renderEmpty';
|
||||
import type { Locale } from '../locale-provider';
|
||||
import LocaleProvider, { ANT_MARK } from '../locale-provider';
|
||||
import type { TransformCellTextProps } from '../table/interface';
|
||||
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import type { RequiredMark } from '../form/Form';
|
||||
|
||||
import type { MaybeRef } from '../_util/type';
|
||||
import message from '../message';
|
||||
import notification from '../notification';
|
||||
import { registerTheme } from './cssVariables';
|
||||
import defaultLocale from '../locale/default';
|
||||
import type { ValidateMessages } from '../form/interface';
|
||||
|
||||
export type SizeType = 'small' | 'middle' | 'large' | undefined;
|
||||
|
||||
export interface CSPConfig {
|
||||
nonce?: string;
|
||||
}
|
||||
|
||||
export type { RenderEmptyHandler };
|
||||
|
||||
export type Direction = 'ltr' | 'rtl';
|
||||
|
||||
export interface ConfigConsumerProps {
|
||||
getTargetContainer?: () => HTMLElement;
|
||||
getPopupContainer?: (triggerNode: HTMLElement) => HTMLElement;
|
||||
rootPrefixCls?: string;
|
||||
getPrefixCls: (suffixCls?: string, customizePrefixCls?: string) => string;
|
||||
renderEmpty: RenderEmptyHandler;
|
||||
transformCellText?: (tableProps: TransformCellTextProps) => any;
|
||||
csp?: CSPConfig;
|
||||
autoInsertSpaceInButton?: boolean;
|
||||
input?: {
|
||||
autoComplete?: string;
|
||||
};
|
||||
locale?: Locale;
|
||||
pageHeader?: {
|
||||
ghost: boolean;
|
||||
};
|
||||
componentSize?: SizeType;
|
||||
direction?: 'ltr' | 'rtl';
|
||||
space?: {
|
||||
size?: SizeType | number;
|
||||
};
|
||||
virtual?: boolean;
|
||||
dropdownMatchSelectWidth?: boolean | number;
|
||||
}
|
||||
|
||||
export const configConsumerProps = [
|
||||
'getTargetContainer',
|
||||
'getPopupContainer',
|
||||
'rootPrefixCls',
|
||||
'getPrefixCls',
|
||||
'renderEmpty',
|
||||
'csp',
|
||||
'autoInsertSpaceInButton',
|
||||
'locale',
|
||||
'pageHeader',
|
||||
];
|
||||
import type { ConfigProviderProps, Theme } from './context';
|
||||
import { configProviderProps, useProvideGlobalForm } from './context';
|
||||
|
||||
export type { ConfigProviderProps, Theme, SizeType, Direction } from './context';
|
||||
export const defaultPrefixCls = 'ant';
|
||||
|
||||
function getGlobalPrefixCls() {
|
||||
return globalConfigForApi.prefixCls || defaultPrefixCls;
|
||||
}
|
||||
|
@ -107,13 +65,16 @@ type GlobalConfigProviderProps = {
|
|||
};
|
||||
|
||||
let stopWatchEffect: WatchStopHandle;
|
||||
const setGlobalConfig = (params: GlobalConfigProviderProps) => {
|
||||
const setGlobalConfig = (params: GlobalConfigProviderProps & { theme?: Theme }) => {
|
||||
if (stopWatchEffect) {
|
||||
stopWatchEffect();
|
||||
}
|
||||
stopWatchEffect = watchEffect(() => {
|
||||
Object.assign(globalConfigBySet, reactive(params));
|
||||
});
|
||||
if (params.theme) {
|
||||
registerTheme(getGlobalPrefixCls(), params.theme);
|
||||
}
|
||||
};
|
||||
|
||||
export const globalConfig = () => ({
|
||||
|
@ -142,61 +103,10 @@ export const globalConfig = () => ({
|
|||
},
|
||||
});
|
||||
|
||||
export const configProviderProps = {
|
||||
getTargetContainer: {
|
||||
type: Function as PropType<() => HTMLElement>,
|
||||
},
|
||||
getPopupContainer: {
|
||||
type: Function as PropType<(triggerNode: HTMLElement) => HTMLElement>,
|
||||
},
|
||||
prefixCls: String,
|
||||
getPrefixCls: {
|
||||
type: Function as PropType<(suffixCls?: string, customizePrefixCls?: string) => string>,
|
||||
},
|
||||
renderEmpty: {
|
||||
type: Function as PropType<RenderEmptyHandler>,
|
||||
},
|
||||
transformCellText: {
|
||||
type: Function as PropType<(tableProps: TransformCellTextProps) => any>,
|
||||
},
|
||||
csp: {
|
||||
type: Object as PropType<CSPConfig>,
|
||||
default: undefined as CSPConfig,
|
||||
},
|
||||
input: {
|
||||
type: Object as PropType<{ autocomplete: string }>,
|
||||
},
|
||||
autoInsertSpaceInButton: PropTypes.looseBool,
|
||||
locale: {
|
||||
type: Object as PropType<Locale>,
|
||||
},
|
||||
pageHeader: {
|
||||
type: Object as PropType<{ ghost: boolean }>,
|
||||
},
|
||||
componentSize: {
|
||||
type: String as PropType<SizeType>,
|
||||
},
|
||||
direction: {
|
||||
type: String as PropType<'ltr' | 'rtl'>,
|
||||
},
|
||||
space: {
|
||||
type: Object as PropType<{ size: SizeType | number }>,
|
||||
},
|
||||
virtual: PropTypes.looseBool,
|
||||
dropdownMatchSelectWidth: { type: [Number, Boolean], default: true },
|
||||
form: {
|
||||
type: Object as PropType<{ requiredMark?: RequiredMark }>,
|
||||
},
|
||||
// internal use
|
||||
notUpdateGlobalConfig: Boolean,
|
||||
};
|
||||
|
||||
export type ConfigProviderProps = Partial<ExtractPropTypes<typeof configProviderProps>>;
|
||||
|
||||
const ConfigProvider = defineComponent({
|
||||
name: 'AConfigProvider',
|
||||
inheritAttrs: false,
|
||||
props: configProviderProps,
|
||||
props: configProviderProps(),
|
||||
setup(props, { slots }) {
|
||||
const getPrefixCls = (suffixCls?: string, customizePrefixCls?: string) => {
|
||||
const { prefixCls = 'ant' } = props;
|
||||
|
@ -240,7 +150,24 @@ const ConfigProvider = defineComponent({
|
|||
Object.assign(globalConfigByCom, configProvider);
|
||||
});
|
||||
}
|
||||
const validateMessagesRef = computed(() => {
|
||||
// Additional Form provider
|
||||
let validateMessages: ValidateMessages = {
|
||||
...defaultLocale.Form?.defaultValidateMessages,
|
||||
};
|
||||
|
||||
if (props.locale) {
|
||||
validateMessages =
|
||||
props.locale.Form?.defaultValidateMessages ||
|
||||
defaultLocale.Form?.defaultValidateMessages ||
|
||||
{};
|
||||
}
|
||||
if (props.form && props.form.validateMessages) {
|
||||
validateMessages = { ...validateMessages, ...props.form.validateMessages };
|
||||
}
|
||||
return validateMessages;
|
||||
});
|
||||
useProvideGlobalForm({ validateMessages: validateMessagesRef });
|
||||
provide('configProvider', configProvider);
|
||||
|
||||
const renderProvider = (legacyLocale: Locale) => {
|
||||
|
|
|
@ -49,13 +49,20 @@ ConfigProvider 使用 Vue 的 [provide / inject](https://vuejs.org/v2/api/#provi
|
|||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| autoInsertSpaceInButton | 设置为 `false` 时,移除按钮中 2 个汉字之间的空格 | boolean | true | |
|
||||
| componentSize | 设置 antd 组件大小 | `small` \| `middle` \| `large` | - | 3.0 |
|
||||
| csp | 设置 [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) 配置 | { nonce: string } | - | |
|
||||
| renderEmpty | 自定义组件空状态。参考 [空状态](/components/empty/) | slot-scope \| Function(componentName: string): VNode | - | |
|
||||
| direction | 设置文本展示方向。 [示例](#components-config-provider-demo-direction) | `ltr` \| `rtl` | `ltr` | 3.0 |
|
||||
| dropdownMatchSelectWidth | 下拉菜单和选择器同宽。默认将设置 `min-width`,当值小于选择框宽度时会被忽略。`false` 时会关闭虚拟滚动 | boolean \| number | - | |
|
||||
| form | 设置 Form 组件的通用属性 | { validateMessages?: [ValidateMessages](/components/form/#validateMessages), requiredMark?: boolean \| `optional`, colon?: boolean} | - | 3.0 |
|
||||
| renderEmpty | 自定义组件空状态。参考 [空状态](/components/empty/) | slot \| Function(componentName: string): VNode | - | |
|
||||
| getPopupContainer | 弹出框(Select, Tooltip, Menu 等等)渲染父节点,默认渲染到 body 上。 | Function(triggerNode, dialogContext) | () => document.body | |
|
||||
| getTargetContainer | 配置 Affix、Anchor 滚动监听容器。 | () => HTMLElement | () => window | 3.0 |
|
||||
| input | 设置 Input 组件的通用属性 | { autocomplete?: string } | - | 3.0 |
|
||||
| locale | 语言包配置,语言包可到 [ant-design-vue/es/locale](http://unpkg.com/ant-design-vue/es/locale/) 目录下寻找 | object | - | 1.5.0 |
|
||||
| pageHeader | 统一设置 pageHeader 的 ghost,参考 [pageHeader](<(/components/page-header)>) | { ghost: boolean } | 'true' | 1.5.0 |
|
||||
| prefixCls | 设置统一样式前缀。注意:需要配合 `less` 变量 `@ant-prefix` 使用 | string | `ant` | |
|
||||
| transformCellText | Table 数据渲染前可以再次改变,一般用户空数据的默认配置 | Function({ text, column, record, index }) => any | - | 1.5.4 | |
|
||||
| space | 设置 Space 的 `size`,参考 [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number` } | - | 3.0 |
|
||||
| virtual | 设置 `false` 时关闭虚拟滚动 | boolean | - | 3.0 |
|
||||
|
||||
### ConfigProvider.config() `3.0.0+`
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
import { inject } from 'vue';
|
||||
import Empty from '../empty';
|
||||
import { defaultConfigProvider } from '.';
|
||||
import type { VueNode } from '../_util/type';
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
|
||||
export interface RenderEmptyProps {
|
||||
componentName?: string;
|
||||
}
|
||||
|
||||
const RenderEmpty = (props: RenderEmptyProps) => {
|
||||
const configProvider = inject('configProvider', defaultConfigProvider);
|
||||
const { prefixCls } = useConfigInject('empty', props);
|
||||
const renderHtml = (componentName?: string) => {
|
||||
const { getPrefixCls } = configProvider;
|
||||
const prefix = getPrefixCls('empty');
|
||||
switch (componentName) {
|
||||
case 'Table':
|
||||
case 'List':
|
||||
|
@ -22,7 +19,7 @@ const RenderEmpty = (props: RenderEmptyProps) => {
|
|||
case 'Cascader':
|
||||
case 'Transfer':
|
||||
case 'Mentions':
|
||||
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} class={`${prefix}-small`} />;
|
||||
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} class={`${prefixCls.value}-small`} />;
|
||||
|
||||
default:
|
||||
return <Empty />;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { flattenChildren } from '../_util/props-util';
|
||||
import type { ExtractPropTypes, PropType } from 'vue';
|
||||
import { computed, defineComponent, inject } from 'vue';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { withInstall } from '../_util/type';
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
|
||||
export const dividerProps = {
|
||||
prefixCls: String,
|
||||
|
@ -29,8 +29,7 @@ const Divider = defineComponent({
|
|||
name: 'ADivider',
|
||||
props: dividerProps,
|
||||
setup(props, { slots }) {
|
||||
const configProvider = inject('configProvider', defaultConfigProvider);
|
||||
const prefixClsRef = computed(() => configProvider.getPrefixCls('divider', props.prefixCls));
|
||||
const { prefixCls: prefixClsRef, direction } = useConfigInject('divider', props);
|
||||
|
||||
const classString = computed(() => {
|
||||
const { type, dashed, plain } = props;
|
||||
|
@ -40,7 +39,7 @@ const Divider = defineComponent({
|
|||
[`${prefixCls}-${type}`]: true,
|
||||
[`${prefixCls}-dashed`]: !!dashed,
|
||||
[`${prefixCls}-plain`]: !!plain,
|
||||
[`${prefixCls}-rtl`]: configProvider.direction === 'rtl',
|
||||
[`${prefixCls}-rtl`]: direction.value === 'rtl',
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { inject } from 'vue';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
|
||||
const Empty = () => {
|
||||
const { getPrefixCls } = inject('configProvider', defaultConfigProvider);
|
||||
const { getPrefixCls } = useConfigInject('empty', {});
|
||||
const prefixCls = getPrefixCls('empty-img-default');
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import type { CSSProperties, FunctionalComponent } from 'vue';
|
||||
import { inject } from 'vue';
|
||||
import classNames from '../_util/classNames';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import DefaultEmptyImg from './empty';
|
||||
import SimpleEmptyImg from './simple';
|
||||
|
@ -9,6 +7,7 @@ import { filterEmpty } from '../_util/props-util';
|
|||
import PropTypes from '../_util/vue-types';
|
||||
import type { VueNode } from '../_util/type';
|
||||
import { withInstall } from '../_util/type';
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
|
||||
const defaultEmptyImg = <DefaultEmptyImg />;
|
||||
const simpleEmptyImg = <SimpleEmptyImg />;
|
||||
|
@ -33,10 +32,10 @@ interface EmptyType extends FunctionalComponent<EmptyProps> {
|
|||
}
|
||||
|
||||
const Empty: EmptyType = (props, { slots = {}, attrs }) => {
|
||||
const configProvider = inject('configProvider', defaultConfigProvider);
|
||||
const { getPrefixCls, direction } = configProvider;
|
||||
const { direction, prefixCls: prefixClsRef } = useConfigInject('empty', props);
|
||||
const prefixCls = prefixClsRef.value;
|
||||
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
image = defaultEmptyImg,
|
||||
description = slots.description?.() || undefined,
|
||||
imageStyle,
|
||||
|
@ -48,7 +47,6 @@ const Empty: EmptyType = (props, { slots = {}, attrs }) => {
|
|||
<LocaleReceiver
|
||||
componentName="Empty"
|
||||
children={(locale: Locale) => {
|
||||
const prefixCls = getPrefixCls('empty', customizePrefixCls);
|
||||
const des = typeof description !== 'undefined' ? description : locale.description;
|
||||
const alt = typeof des === 'string' ? des : 'empty';
|
||||
let imageNode: EmptyProps['image'] = null;
|
||||
|
@ -63,7 +61,7 @@ const Empty: EmptyType = (props, { slots = {}, attrs }) => {
|
|||
<div
|
||||
class={classNames(prefixCls, className, {
|
||||
[`${prefixCls}-normal`]: image === simpleEmptyImg,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-rtl`]: direction.value === 'rtl',
|
||||
})}
|
||||
{...restProps}
|
||||
>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { inject } from 'vue';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
|
||||
const Simple = () => {
|
||||
const { getPrefixCls } = inject('configProvider', defaultConfigProvider);
|
||||
const { getPrefixCls } = useConfigInject('empty', {});
|
||||
const prefixCls = getPrefixCls('empty-img-simple');
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
import PreviewGroup from '../vc-image/src/PreviewGroup';
|
||||
import { computed, defineComponent, inject } from 'vue';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
|
||||
const InternalPreviewGroup = defineComponent({
|
||||
name: 'AImagePreviewGroup',
|
||||
inheritAttrs: false,
|
||||
props: { previewPrefixCls: PropTypes.string },
|
||||
setup(props, { attrs, slots }) {
|
||||
const configProvider = inject('configProvider', defaultConfigProvider);
|
||||
const prefixCls = computed(() =>
|
||||
configProvider.getPrefixCls('image-preview', props.previewPrefixCls),
|
||||
);
|
||||
const { getPrefixCls } = useConfigInject('image', props);
|
||||
const prefixCls = computed(() => getPrefixCls('image-preview', props.previewPrefixCls));
|
||||
return () => {
|
||||
return (
|
||||
<PreviewGroup
|
||||
|
|
|
@ -108,7 +108,6 @@ const TreeSelect = defineComponent({
|
|||
|
||||
const formItemContext = useInjectFormItemContext();
|
||||
const {
|
||||
configProvider,
|
||||
prefixCls,
|
||||
renderEmpty,
|
||||
direction,
|
||||
|
@ -125,12 +124,8 @@ const TreeSelect = defineComponent({
|
|||
const choiceTransitionName = computed(() =>
|
||||
getTransitionName(rootPrefixCls.value, '', props.choiceTransitionName),
|
||||
);
|
||||
const treePrefixCls = computed(() =>
|
||||
configProvider.getPrefixCls('select-tree', props.prefixCls),
|
||||
);
|
||||
const treeSelectPrefixCls = computed(() =>
|
||||
configProvider.getPrefixCls('tree-select', props.prefixCls),
|
||||
);
|
||||
const treePrefixCls = computed(() => getPrefixCls('select-tree', props.prefixCls));
|
||||
const treeSelectPrefixCls = computed(() => getPrefixCls('tree-select', props.prefixCls));
|
||||
|
||||
const mergedDropdownClassName = computed(() =>
|
||||
classNames(props.dropdownClassName, `${treeSelectPrefixCls.value}-dropdown`, {
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
import canUseDom from '../../_util/canUseDom';
|
||||
|
||||
const MARK_KEY = `vc-util-key` as any;
|
||||
|
||||
function getMark({ mark }: Options = {}) {
|
||||
if (mark) {
|
||||
return mark.startsWith('data-') ? mark : `data-${mark}`;
|
||||
}
|
||||
return MARK_KEY;
|
||||
}
|
||||
|
||||
interface Options {
|
||||
attachTo?: Element;
|
||||
csp?: { nonce?: string };
|
||||
prepend?: boolean;
|
||||
mark?: string;
|
||||
}
|
||||
|
||||
function getContainer(option: Options) {
|
||||
if (option.attachTo) {
|
||||
return option.attachTo;
|
||||
}
|
||||
|
||||
const head = document.querySelector('head');
|
||||
return head || document.body;
|
||||
}
|
||||
|
||||
export function injectCSS(css: string, option: Options = {}) {
|
||||
if (!canUseDom()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const styleNode = document.createElement('style');
|
||||
if (option.csp?.nonce) {
|
||||
styleNode.nonce = option.csp?.nonce;
|
||||
}
|
||||
styleNode.innerHTML = css;
|
||||
|
||||
const container = getContainer(option);
|
||||
const { firstChild } = container;
|
||||
|
||||
if (option.prepend && container.prepend) {
|
||||
// Use `prepend` first
|
||||
container.prepend(styleNode);
|
||||
} else if (option.prepend && firstChild) {
|
||||
// Fallback to `insertBefore` like IE not support `prepend`
|
||||
container.insertBefore(styleNode, firstChild);
|
||||
} else {
|
||||
container.appendChild(styleNode);
|
||||
}
|
||||
|
||||
return styleNode;
|
||||
}
|
||||
|
||||
const containerCache = new Map<Element, Node & ParentNode>();
|
||||
|
||||
function findExistNode(key: string, option: Options = {}) {
|
||||
const container = getContainer(option);
|
||||
|
||||
return Array.from(containerCache.get(container).children).find(
|
||||
node => node.tagName === 'STYLE' && node.getAttribute(getMark(option)) === key,
|
||||
) as HTMLStyleElement;
|
||||
}
|
||||
|
||||
export function removeCSS(key: string, option: Options = {}) {
|
||||
const existNode = findExistNode(key, option);
|
||||
|
||||
existNode?.parentNode?.removeChild(existNode);
|
||||
}
|
||||
|
||||
export function updateCSS(css: string, key: string, option: Options = {}) {
|
||||
const container = getContainer(option);
|
||||
|
||||
// Get real parent
|
||||
if (!containerCache.has(container)) {
|
||||
const placeholderStyle = injectCSS('', option);
|
||||
const { parentNode } = placeholderStyle;
|
||||
containerCache.set(container, parentNode);
|
||||
parentNode.removeChild(placeholderStyle);
|
||||
}
|
||||
|
||||
const existNode = findExistNode(key, option);
|
||||
|
||||
if (existNode) {
|
||||
if (option.csp?.nonce && existNode.nonce !== option.csp?.nonce) {
|
||||
existNode.nonce = option.csp?.nonce;
|
||||
}
|
||||
|
||||
if (existNode.innerHTML !== css) {
|
||||
existNode.innerHTML = css;
|
||||
}
|
||||
|
||||
return existNode;
|
||||
}
|
||||
|
||||
const newNode = injectCSS(css, option);
|
||||
newNode.setAttribute(getMark(option), key);
|
||||
return newNode;
|
||||
}
|
Loading…
Reference in New Issue