Add variable.less to support css variable (#31496)

* chore: use varaible.less

* chore: basic primary varaible

* chore: Move to variable

* chore: align active color

* chore: global fix of css variable

* chore: primary colors

* chore: button danger

* chore: btn default error color

* chore: button series

* chore: More examples

* chore: More components

* chore: Form demo

* chore: form style

* fix: Tag & Alert variable

* chore: update footer

* chore: rm tmp code

* chore: transfer

* fix: picker column active color

* chore: Adjust active bg color

* chore: table hover color

* chore: all css variables

* chore: Global using variables

* chore: Test case

* chore: Update test logic

* chore: back of default less

* chore: entry of site use proxy style

* chore: update entry

* chore: split of variables

* refactor: quick dist speed

* fix: site use variable version

* chore: Update less config

* chore: add mv script

* chore: Update repalcement script

* chore: Add inject variables

* chore: Update script

* fix: script path

* chore: Move to component instead

* chore: fix condition

* chore: update config

* chore: Update in less transform

* chore: Modify logic

* chore: change to variables

* chore: Update name

* fix: script name

* chore: do inject

* revert: back of path

* chore: 2 way of generate

* bump tools

* chore: Add auto replacement script

* chore: auto genrate less file

* chore: fix test

* test: More test case

* chore: Update limit config

* test: coverage

* docs: Update doc
This commit is contained in:
二货机器人 2021-09-01 10:56:50 +08:00 committed by GitHub
parent 9c17f94cab
commit 36bcaaef85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 2760 additions and 274 deletions

View File

@ -90,6 +90,7 @@ function finalizeDist() {
buildThemeFile('default', defaultVars); buildThemeFile('default', defaultVars);
buildThemeFile('dark', darkVars); buildThemeFile('dark', darkVars);
buildThemeFile('compact', compactVars); buildThemeFile('compact', compactVars);
buildThemeFile('variable', {});
fs.writeFileSync( fs.writeFileSync(
path.join(process.cwd(), 'dist', `theme.js`), path.join(process.cwd(), 'dist', `theme.js`),
` `
@ -125,8 +126,56 @@ module.exports = {
} }
} }
function isComponentStyle(file) {
return file.path.match(/style(\/|\\)index\.tsx/);
}
function needTransformStyle(content) {
return content.includes('./index.less');
}
module.exports = { module.exports = {
compile: { compile: {
transformTSFile(file) {
if (isComponentStyle(file)) {
let content = file.contents.toString();
if (needTransformStyle(content)) {
const cloneFile = file.clone();
// Origin
content = content.replace('./index.less', './index-default.less');
cloneFile.contents = Buffer.from(content);
return cloneFile;
}
}
},
transformFile(file) {
if (isComponentStyle(file)) {
const content = file.contents.toString();
if (needTransformStyle(content)) {
const cloneFile = file.clone();
cloneFile.contents = Buffer.from(
[
// Inject variable
'@root-entry-name: default',
// Point to origin file
"@import './index';",
].join('\n\n'),
);
cloneFile.path = cloneFile.path.replace('index.tsx', 'index-default.less');
return cloneFile;
}
}
return [];
},
lessConfig: {
modifyVars: {
'root-entry-name': 'default',
},
},
finalize: finalizeCompile, finalize: finalizeCompile,
}, },
dist: { dist: {

View File

@ -206,11 +206,8 @@ describe('Affix Render', () => {
// Mock trigger resize // Mock trigger resize
updateCalled.mockReset(); updateCalled.mockReset();
const resizeObserverInstance: ReactWrapper< const resizeObserverInstance: ReactWrapper<HTMLAttributes, unknown, ResizeObserverImpl> =
HTMLAttributes, affixMounterWrapper.find('ResizeObserver') as any;
unknown,
ResizeObserverImpl
> = affixMounterWrapper.find('ResizeObserver') as any;
resizeObserverInstance resizeObserverInstance
.at(index) .at(index)
.instance() .instance()
@ -225,7 +222,7 @@ describe('Affix Render', () => {
contentBoxSize: [], contentBoxSize: [],
}, },
], ],
({} as unknown) as ResizeObserver, {} as unknown as ResizeObserver,
); );
await sleep(20); await sleep(20);

View File

@ -207,19 +207,19 @@
} }
&-background-ghost&-primary { &-background-ghost&-primary {
.button-variant-ghost(@btn-primary-bg); .button-variant-ghost(@btn-primary-bg, @btn-primary-bg, @primary-color-hover, @primary-color-active);
} }
&-background-ghost&-danger { &-background-ghost&-danger {
.button-variant-ghost(@btn-danger-border); .button-variant-ghost(@btn-danger-border, @btn-danger-border, @error-color-hover, @error-color-active);
} }
&-background-ghost&-dangerous { &-background-ghost&-dangerous {
.button-variant-ghost(@btn-danger-border); .button-variant-ghost(@btn-danger-border, @btn-danger-border, @error-color-hover, @error-color-active);
} }
&-background-ghost&-dangerous&-link { &-background-ghost&-dangerous&-link {
.button-variant-ghost(@btn-danger-border, transparent); .button-variant-ghost(@btn-danger-border, transparent, @error-color-hover, @error-color-active);
} }
&-two-chinese-chars::first-letter { &-two-chinese-chars::first-letter {

View File

@ -11,134 +11,6 @@
border-radius: @border-radius; border-radius: @border-radius;
} }
.button-disabled(@color: @btn-disable-color; @background: @btn-disable-bg; @border: @btn-disable-border) {
&[disabled] {
&,
&:hover,
&:focus,
&:active {
.button-color(@color; @background; @border);
text-shadow: none;
box-shadow: none;
}
}
}
.button-variant-primary(@color; @background) {
.button-color(@color; @background; @background);
text-shadow: @btn-text-shadow;
box-shadow: @btn-primary-shadow;
&:hover,
&:focus {
& when (@theme = dark) {
.button-color(
@color; ~`colorPalette('@{background}', 7) `; ~`colorPalette('@{background}', 7) `
);
}
& when not (@theme = dark) {
.button-color(
@color; ~`colorPalette('@{background}', 5) `; ~`colorPalette('@{background}', 5) `
);
}
}
&:active {
& when (@theme = dark) {
.button-color(
@color; ~`colorPalette('@{background}', 5) `; ~`colorPalette('@{background}', 5) `
);
}
& when not (@theme = dark) {
.button-color(
@color; ~`colorPalette('@{background}', 7) `; ~`colorPalette('@{background}', 7) `
);
}
}
.button-disabled();
}
.button-variant-other(@color; @background; @border) {
.button-color(@color; @background; @border);
&:hover,
&:focus {
& when (@theme = dark) {
.button-color(@primary-5; @background; @primary-5);
}
& when not (@theme = dark) {
.button-color(
~`colorPalette('@{btn-primary-bg}', 5) `; @background;
~`colorPalette('@{btn-primary-bg}', 5) `
);
}
}
&:active {
& when (@theme = dark) {
.button-color(@primary-7; @background; @primary-7);
}
& when not (@theme = dark) {
.button-color(
~`colorPalette('@{btn-primary-bg}', 7) `; @background;
~`colorPalette('@{btn-primary-bg}', 7) `
);
}
}
.button-disabled();
}
.button-variant-ghost(@color; @border: @color) {
.button-color(@color; null; @border);
text-shadow: none;
&:hover,
&:focus {
& when (@border = transparent) {
& when (@theme = dark) {
.button-color(~`colorPalette('@{color}', 7) `; null; transparent);
}
& when not (@theme = dark) {
.button-color(~`colorPalette('@{color}', 5) `; null; transparent);
}
}
& when not (@border = transparent) {
& when (@theme = dark) {
.button-color(
~`colorPalette('@{color}', 7) `; null; ~`colorPalette('@{color}', 7) `
);
}
& when not (@theme = dark) {
.button-color(
~`colorPalette('@{color}', 5) `; null; ~`colorPalette('@{color}', 5) `
);
}
}
}
&:active {
& when (@border = transparent) {
& when (@theme = dark) {
.button-color(~`colorPalette('@{color}', 5) `; null; transparent);
}
& when not (@theme = dark) {
.button-color(~`colorPalette('@{color}', 7) `; null; transparent);
}
}
& when not (@border = transparent) {
& when (@theme = dark) {
.button-color(
~`colorPalette('@{color}', 5) `; null; ~`colorPalette('@{color}', 5) `
);
}
& when not (@theme = dark) {
.button-color(
~`colorPalette('@{color}', 7) `; null; ~`colorPalette('@{color}', 7) `
);
}
}
}
.button-disabled();
}
.button-color(@color; @background; @border) { .button-color(@color; @background; @border) {
color: @color; color: @color;
border-color: @border; // a inside Button which only work in Chrome border-color: @border; // a inside Button which only work in Chrome
@ -159,6 +31,161 @@
} }
} }
} }
.button-disabled(@color: @btn-disable-color; @background: @btn-disable-bg; @border: @btn-disable-border) {
&[disabled] {
&,
&:hover,
&:focus,
&:active {
.button-color(@color; @background; @border);
text-shadow: none;
box-shadow: none;
}
}
}
.button-variant-primary(@color; @background; @backgroundHover: yellow; @backgroundActive: yellow) {
.button-color(@color; @background; @background);
text-shadow: @btn-text-shadow;
box-shadow: @btn-primary-shadow;
&:hover,
&:focus {
& when (@theme = dark) {
.button-color(
@color; ~`colorPalette('@{background}', 7) `; ~`colorPalette('@{background}', 7) `
);
}
& when (not (@theme = dark) and not (@theme = variable)) {
.button-color(
@color; ~`colorPalette('@{background}', 5) `; ~`colorPalette('@{background}', 5) `
);
}
& when (@theme = variable) {
.button-color(@color; @backgroundHover; @backgroundHover);
}
}
&:active {
& when (@theme = dark) {
.button-color(
@color; ~`colorPalette('@{background}', 5) `; ~`colorPalette('@{background}', 5) `
);
}
& when (not (@theme = dark) and not (@theme = variable)) {
.button-color(
@color; ~`colorPalette('@{background}', 7) `; ~`colorPalette('@{background}', 7) `
);
}
& when (@theme = variable) {
.button-color(@color; @backgroundActive; @backgroundActive);
}
}
.button-disabled();
}
.button-variant-other(@color; @background; @border) {
.button-color(@color; @background; @border);
&:hover,
&:focus {
& when (@theme = dark) {
.button-color(@primary-5; @background; @primary-5);
}
& when (not (@theme = dark) and not (@theme = variable)) {
.button-color(
~`colorPalette('@{btn-primary-bg}', 5) `; @background;
~`colorPalette('@{btn-primary-bg}', 5) `
);
}
& when (@theme = variable) {
.button-color(@primary-color-hover; @background; @primary-color-hover);
}
}
&:active {
& when (@theme = dark) {
.button-color(@primary-7; @background; @primary-7);
}
& when (not (@theme = dark) and not (@theme = variable)) {
.button-color(
~`colorPalette('@{btn-primary-bg}', 7) `; @background;
~`colorPalette('@{btn-primary-bg}', 7) `
);
}
& when (@theme = variable) {
.button-color(@primary-color-active; @background; @primary-color-active);
}
}
.button-disabled();
}
.button-variant-ghost(@color; @border; @borderHover: yellow; @borderActive: yellow) {
.button-color(@color; null; @border);
text-shadow: none;
&:hover,
&:focus {
& when (@border = transparent) {
& when (@theme = dark) {
.button-color(~`colorPalette('@{color}', 7) `; null; transparent);
}
& when (not (@theme = dark) and not (@theme = variable)) {
.button-color(~`colorPalette('@{color}', 5) `; null; transparent);
}
& when (@theme = variable) {
.button-color(@borderActive; transparent; transparent);
}
}
& when not (@border = transparent) {
& when (@theme = dark) {
.button-color(
~`colorPalette('@{color}', 7) `; null; ~`colorPalette('@{color}', 7) `
);
}
& when (not (@theme = dark) and not (@theme = variable)) {
.button-color(
~`colorPalette('@{color}', 5) `; null; ~`colorPalette('@{color}', 5) `
);
}
& when (@theme = variable) {
.button-color(@borderHover; transparent; @borderHover);
}
}
}
&:active {
& when (@border = transparent) {
& when (@theme = dark) {
.button-color(~`colorPalette('@{color}', 5) `; null; transparent);
}
& when (not (@theme = dark) and not (@theme = variable)) {
.button-color(~`colorPalette('@{color}', 7) `; null; transparent);
}
& when (@theme = variable) {
.button-color(@borderActive; transparent; transparent);
}
}
& when not (@border = transparent) {
& when (@theme = dark) {
.button-color(
~`colorPalette('@{color}', 5) `; null; ~`colorPalette('@{color}', 5) `
);
}
& when (not (@theme = dark) and not (@theme = variable)) {
.button-color(
~`colorPalette('@{color}', 7) `; null; ~`colorPalette('@{color}', 7) `
);
}
& when (@theme = variable) {
.button-color(@borderActive; transparent; @borderActive);
}
}
}
.button-disabled();
}
.button-group-base(@btnClassName) { .button-group-base(@btnClassName) {
position: relative; position: relative;
display: inline-flex; display: inline-flex;
@ -252,11 +279,11 @@
} }
// primary button style // primary button style
.btn-primary() { .btn-primary() {
.button-variant-primary(@btn-primary-color; @btn-primary-bg); .button-variant-primary(@btn-primary-color; @btn-primary-bg; @primary-color-hover; @primary-color-active);
} }
// default button style // default button style
.btn-default() { .btn-default() {
.button-variant-other(@btn-default-color; @btn-default-bg; @btn-default-border); .button-variant-other(@btn-default-color; @btn-default-bg; @btn-default-border; );
&:hover, &:hover,
&:focus, &:focus,
&:active { &:active {
@ -275,7 +302,7 @@
} }
// danger button style // danger button style
.btn-danger() { .btn-danger() {
.button-variant-primary(@btn-danger-color, @btn-danger-bg); .button-variant-primary(@btn-danger-color, @btn-danger-bg, @error-color-hover, @error-color-active);
} }
// danger default button style // danger default button style
.btn-danger-default() { .btn-danger-default() {
@ -288,12 +315,15 @@
` `
); );
} }
& when not (@theme = dark) { & when (not (@theme = dark) and not (@theme = variable)) {
.button-color( .button-color(
~`colorPalette('@{error-color}', 5) `; @btn-default-bg; ~`colorPalette('@{error-color}', 5) ~`colorPalette('@{error-color}', 5) `; @btn-default-bg; ~`colorPalette('@{error-color}', 5)
` `
); );
} }
& when (@theme = variable) {
.button-color(@error-color-hover, @btn-default-bg, @error-color-hover);
}
} }
&:active { &:active {
& when (@theme = dark) { & when (@theme = dark) {
@ -302,12 +332,15 @@
` `
); );
} }
& when not (@theme = dark) { & when (not (@theme = dark) and not (@theme = variable)) {
.button-color( .button-color(
~`colorPalette('@{error-color}', 7) `; @btn-default-bg; ~`colorPalette('@{error-color}', 7) ~`colorPalette('@{error-color}', 7) `; @btn-default-bg; ~`colorPalette('@{error-color}', 7)
` `
); );
} }
& when (@theme = variable) {
.button-color(@error-color-active, @btn-default-bg, @error-color-active);
}
} }
.button-disabled(); .button-disabled();
} }
@ -320,17 +353,23 @@
& when (@theme = dark) { & when (@theme = dark) {
.button-color(~`colorPalette('@{error-color}', 7) `; transparent; transparent); .button-color(~`colorPalette('@{error-color}', 7) `; transparent; transparent);
} }
& when not (@theme = dark) { & when (not (@theme = dark) and not (@theme = variable)) {
.button-color(~`colorPalette('@{error-color}', 5) `; transparent; transparent); .button-color(~`colorPalette('@{error-color}', 5) `; transparent; transparent);
} }
& when (@theme = variable) {
.button-color(@error-color-hover; transparent; transparent);
}
} }
&:active { &:active {
& when (@theme = dark) { & when (@theme = dark) {
.button-color(~`colorPalette('@{error-color}', 5) `; transparent; transparent); .button-color(~`colorPalette('@{error-color}', 5) `; transparent; transparent);
} }
& when not (@theme = dark) { & when (not (@theme = dark) and not (@theme = variable)) {
.button-color(~`colorPalette('@{error-color}', 7) `; transparent; transparent); .button-color(~`colorPalette('@{error-color}', 7) `; transparent; transparent);
} }
& when (@theme = variable) {
.button-color(@error-color-active; transparent; transparent);
}
} }
.button-disabled(@disabled-color; transparent; transparent); .button-disabled(@disabled-color; transparent; transparent);
} }
@ -375,18 +414,24 @@
& when (@theme = dark) { & when (@theme = dark) {
.button-color(~`colorPalette('@{error-color}', 7) `; @btn-text-hover-bg; transparent); .button-color(~`colorPalette('@{error-color}', 7) `; @btn-text-hover-bg; transparent);
} }
& when not (@theme = dark) { & when (not (@theme = dark) and not (@theme = variable)) {
.button-color(~`colorPalette('@{error-color}', 5) `; @btn-text-hover-bg; transparent); .button-color(~`colorPalette('@{error-color}', 5) `; @btn-text-hover-bg; transparent);
} }
& when (@theme = variable) {
.button-color(@error-color-hover; @btn-text-hover-bg; transparent);
}
} }
&:active { &:active {
& when (@theme = dark) { & when (@theme = dark) {
.button-color(~`colorPalette('@{error-color}', 5) `; fadein(@btn-text-hover-bg, 1%); transparent); .button-color(~`colorPalette('@{error-color}', 5) `; fadein(@btn-text-hover-bg, 1%); transparent);
} }
& when not (@theme = dark) { & when (not (@theme = dark) and not (@theme = variable)) {
.button-color(~`colorPalette('@{error-color}', 7) `; fadein(@btn-text-hover-bg, 1%); transparent); .button-color(~`colorPalette('@{error-color}', 7) `; fadein(@btn-text-hover-bg, 1%); transparent);
} }
& when (@theme = variable) {
.button-color(@error-color-active; fadein(@btn-text-hover-bg, 1%); transparent);
}
} }
.button-disabled(@disabled-color; transparent; transparent); .button-disabled(@disabled-color; transparent; transparent);
} }

View File

@ -0,0 +1,23 @@
import { kebabCase } from 'lodash';
import ConfigProvider from '..';
describe('ConfigProvider.Theme', () => {
const colorList = ['primaryColor', 'successColor', 'warningColor', 'errorColor', 'infoColor'];
colorList.forEach(colorName => {
it(colorName, () => {
ConfigProvider.config({
prefixCls: 'bamboo',
theme: {
[colorName]: '#0000FF',
},
});
const styles: any[] = Array.from(document.querySelectorAll('style'));
const themeStyle = styles.find(style => style['rc-util-key'].includes('-dynamic-theme'));
expect(themeStyle).toBeTruthy();
expect(themeStyle.innerHTML).toContain(`--bamboo-${kebabCase(colorName)}: rgb(0, 0, 255)`);
});
});
});

View File

@ -4,6 +4,15 @@ import { Locale } from '../locale-provider';
import { SizeType } from './SizeContext'; import { SizeType } from './SizeContext';
import { RequiredMark } from '../form/Form'; import { RequiredMark } from '../form/Form';
export interface Theme {
primaryColor?: string;
infoColor?: string;
successColor?: string;
processingColor?: string;
errorColor?: string;
warningColor?: string;
}
export interface CSPConfig { export interface CSPConfig {
nonce?: string; nonce?: string;
} }

View File

@ -0,0 +1,97 @@
/* eslint-disable import/prefer-default-export, prefer-destructuring */
import { updateCSS } from 'rc-util/lib/Dom/dynamicCSS';
import { TinyColor } from '@ctrl/tinycolor';
import { generate } from '@ant-design/colors';
import { Theme } from './context';
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`,
);
}

View File

@ -0,0 +1,633 @@
---
order: 5
title:
zh-CN: 全局样式
en-US: Global Theme
---
## zh-CN
通过 css variable 修改全局主题色(你可以切换到组件页面查看更详细的样式展示),不支持 IE。自动生成的变量可能会根据设计调整请勿直接依赖。详细配置请[点击查看](/docs/react/customize-theme-variable)。
## en-US
Modify global theme color by css variable which IE not support. Css variable depends on the design, it may adjust so please do not directly use it. You can go to other components page for more detail style. [Check this](/docs/react/customize-theme-variable) to view detail.
```jsx
import { SketchPicker } from 'react-color';
import React, { useState } from 'react';
import {
DownOutlined,
MailOutlined,
SettingOutlined,
ClockCircleOutlined,
} from '@ant-design/icons';
import {
ConfigProvider,
Tag,
Mentions,
Steps,
Button,
Radio,
Space,
Form,
Input,
Row,
Col,
Typography,
Menu,
Dropdown,
Divider,
Pagination,
Select,
Checkbox,
DatePicker,
TimePicker,
InputNumber,
Slider,
Switch,
TreeSelect,
Card,
Table,
Tabs,
Timeline,
Tree,
Alert,
Progress,
Spin,
Transfer,
} from 'antd';
const SplitSpace = props => <Space split={<Divider type="vertical" />} size={4} {...props} />;
const inputProps = {
style: { width: 128 },
};
const selectProps = {
...inputProps,
options: [
{ value: 'light', label: 'Light' },
{ value: 'bamboo', label: 'Bamboo' },
{ value: 'little', label: 'Little' },
],
};
const treeData = [
{
value: 'little',
key: 'little',
label: 'Little',
title: 'Little',
children: [
{ value: 'light', key: 'light', label: 'Light', title: 'Light' },
{ value: 'bamboo', key: 'bamboo', label: 'Bamboo', title: 'Bamboo' },
],
},
];
const treeSelectProps = {
...inputProps,
treeCheckable: true,
maxTagCount: 'responsive',
treeData,
};
const carTabListNoTitle = [
{
key: 'article',
tab: 'article',
},
{
key: 'app',
tab: 'app',
},
{
key: 'project',
tab: 'project',
},
];
const MyTransfer = () => {
const mockData = [];
for (let i = 0; i < 20; i++) {
mockData.push({
key: i.toString(),
title: `content${i + 1}`,
description: `description of content${i + 1}`,
});
}
return (
<Transfer
dataSource={mockData}
targetKeys={['18']}
selectedKeys={['3']}
render={item => item.title}
/>
);
};
const FormSizeDemo = () => {
const [color, setColor] = useState({
primaryColor: '#1890ff',
errorColor: '#ff4d4f',
warningColor: '#faad14',
successColor: '#52c41a',
infoColor: '#1890ff',
});
function onColorChange(nextColor) {
const mergedNextColor = {
...color,
...nextColor,
};
setColor(mergedNextColor);
ConfigProvider.config({
theme: mergedNextColor,
});
}
return (
<Row gutter={16} wrap={false}>
<Col flex="none">
<Space direction="vertical" align="center">
{/* Primary Color */}
<SketchPicker
presetColors={['#1890ff', '#25b864', '#ff6f00']}
color={color.primaryColor}
onChange={({ hex }) => {
onColorChange({
primaryColor: hex,
});
}}
/>
<span style={{ color: 'var(--ant-primary-color)' }}>var(`--ant-primary-color`)</span>
{/* Error Color */}
<SketchPicker
presetColors={['#ff4d4f']}
color={color.errorColor}
onChange={({ hex }) => {
onColorChange({
errorColor: hex,
});
}}
/>
<span style={{ color: 'var(--ant-error-color)' }}>var(`--ant-error-color`)</span>
{/* Warning Color */}
<SketchPicker
presetColors={['#faad14']}
color={color.warningColor}
onChange={({ hex }) => {
onColorChange({
warningColor: hex,
});
}}
/>
<span style={{ color: 'var(--ant-warning-color)' }}>var(`--ant-warning-color`)</span>
{/* Success Color */}
<SketchPicker
presetColors={['#52c41a']}
color={color.successColor}
onChange={({ hex }) => {
onColorChange({
successColor: hex,
});
}}
/>
<span style={{ color: 'var(--ant-success-color)' }}>var(`--ant-success-color`)</span>
{/* Info Color */}
<SketchPicker
presetColors={['#1890ff']}
color={color.infoColor}
onChange={({ hex }) => {
onColorChange({
infoColor: hex,
});
}}
/>
<span style={{ color: 'var(--ant-info-color)' }}>var(`--ant-info-color`)</span>
</Space>
</Col>
<Col flex="auto">
<Space direction="vertical" split={<Divider />} style={{ width: '100%' }} size={0}>
{/* Primary Button */}
<SplitSpace>
<Button type="primary">Primary</Button>
<Button>Default</Button>
<Button type="dashed">Dashed</Button>
<Button type="text">Text</Button>
<Button type="link">Link</Button>
</SplitSpace>
{/* Danger Button */}
<SplitSpace>
<Button danger type="primary">
Primary
</Button>
<Button danger>Default</Button>
<Button danger type="dashed">
Dashed
</Button>
<Button danger type="text">
Text
</Button>
<Button danger type="link">
Link
</Button>
</SplitSpace>
{/* Ghost Button */}
<SplitSpace style={{ background: 'rgb(190, 200, 200)' }}>
<Button type="primary" ghost>
Primary
</Button>
<Button ghost>Default</Button>
<Button type="dashed" ghost>
Dashed
</Button>
<Button type="primary" ghost danger>
Primary
</Button>
<Button ghost danger>
Default
</Button>
<Button type="dashed" ghost danger>
Dashed
</Button>
</SplitSpace>
{/* Typography */}
<SplitSpace>
<Typography.Text type="success">Text (success)</Typography.Text>
<Typography.Text type="warning">Text(warning)</Typography.Text>
<Typography.Text type="danger">Text(danger)</Typography.Text>
<Typography.Link href="https://ant.design" target="_blank">
Link
</Typography.Link>
<Typography.Text copyable>Text</Typography.Text>
{/* Dropdown */}
<Dropdown
overlay={
<Menu>
<Menu.Item>1st menu item</Menu.Item>
<Menu.Item danger>a danger item</Menu.Item>
</Menu>
}
>
<a className="ant-dropdown-link" onClick={e => e.preventDefault()}>
Hover me <DownOutlined />
</a>
</Dropdown>
{/* Spin */}
<Spin />
</SplitSpace>
{/* Menu - horizontal */}
<Row gutter={16}>
<Col span={12}>
<Menu mode="horizontal" defaultSelectedKeys={['mail']}>
<Menu.Item key="mail" icon={<MailOutlined />}>
Mail
</Menu.Item>
<Menu.SubMenu key="SubMenu" icon={<SettingOutlined />} title="Submenu">
<Menu.ItemGroup title="Item 1">
<Menu.Item key="setting:1">Option 1</Menu.Item>
<Menu.Item key="setting:2">Option 2</Menu.Item>
</Menu.ItemGroup>
</Menu.SubMenu>
</Menu>
</Col>
<Col span={12}>
<Menu mode="horizontal" theme="dark" defaultSelectedKeys={['mail']}>
<Menu.Item key="mail" icon={<MailOutlined />}>
Mail
</Menu.Item>
<Menu.SubMenu key="SubMenu" icon={<SettingOutlined />} title="Submenu">
<Menu.ItemGroup title="Item 1">
<Menu.Item key="setting:1">Option 1</Menu.Item>
<Menu.Item key="setting:2">Option 2</Menu.Item>
</Menu.ItemGroup>
</Menu.SubMenu>
</Menu>
</Col>
</Row>
{/* Menu - vertical */}
<Row gutter={16}>
<Col span={12}>
<Menu mode="inline" defaultSelectedKeys={['mail']}>
<Menu.Item key="mail" icon={<MailOutlined />}>
Mail
</Menu.Item>
<Menu.SubMenu key="SubMenu" icon={<SettingOutlined />} title="Submenu">
<Menu.ItemGroup title="Item 1">
<Menu.Item key="setting:1">Option 1</Menu.Item>
<Menu.Item key="setting:2">Option 2</Menu.Item>
</Menu.ItemGroup>
</Menu.SubMenu>
</Menu>
</Col>
<Col span={12}>
<Menu mode="vertical" theme="dark" defaultSelectedKeys={['mail']}>
<Menu.Item key="mail" icon={<MailOutlined />}>
Mail
</Menu.Item>
<Menu.SubMenu key="SubMenu" icon={<SettingOutlined />} title="Submenu">
<Menu.ItemGroup title="Item 1">
<Menu.Item key="setting:1">Option 1</Menu.Item>
<Menu.Item key="setting:2">Option 2</Menu.Item>
</Menu.ItemGroup>
</Menu.SubMenu>
</Menu>
</Col>
</Row>
{/* Pagination */}
<Pagination showQuickJumper defaultCurrent={2} total={500} />
{/* Steps */}
<Steps current={1} percent={60}>
<Steps.Step title="Finished" description="This is a description." />
<Steps.Step
title="In Progress"
subTitle="Left 00:00:08"
description="This is a description."
/>
<Steps.Step title="Waiting" description="This is a description." />
</Steps>
{/* Steps - dot */}
<Steps current={2} status="error" progressDot>
<Steps.Step title="Finished" description="You can hover on the dot." />
<Steps.Step title="In Progress" description="You can hover on the dot." />
<Steps.Step title="Error" description="You can hover on the dot." />
<Steps.Step title="Waiting" description="You can hover on the dot." />
</Steps>
{/* Form - Input */}
<Form>
<SplitSpace>
<Form.Item>
<Input {...inputProps} />
</Form.Item>
<Form.Item hasFeedback validateStatus="success">
<Input {...inputProps} />
</Form.Item>
<Form.Item hasFeedback validateStatus="warning">
<Input {...inputProps} />
</Form.Item>
<Form.Item hasFeedback validateStatus="error">
<Input {...inputProps} />
</Form.Item>
<Form.Item hasFeedback validateStatus="validating">
<Input {...inputProps} />
</Form.Item>
</SplitSpace>
</Form>
{/* Form - Select */}
<Form>
<SplitSpace>
<Form.Item>
<Select {...selectProps} />
</Form.Item>
<Form.Item hasFeedback validateStatus="success">
<Select {...selectProps} />
</Form.Item>
<Form.Item hasFeedback validateStatus="warning">
<Select {...selectProps} />
</Form.Item>
<Form.Item hasFeedback validateStatus="error">
<Select {...selectProps} />
</Form.Item>
<Form.Item hasFeedback validateStatus="validating">
<Select {...selectProps} />
</Form.Item>
</SplitSpace>
</Form>
{/* Form - TreeSelect */}
<Form>
<SplitSpace>
<Form.Item>
<TreeSelect {...treeSelectProps} />
</Form.Item>
<Form.Item hasFeedback validateStatus="success">
<TreeSelect {...treeSelectProps} />
</Form.Item>
<Form.Item hasFeedback validateStatus="warning">
<TreeSelect {...treeSelectProps} />
</Form.Item>
<Form.Item hasFeedback validateStatus="error">
<TreeSelect {...treeSelectProps} />
</Form.Item>
<Form.Item hasFeedback validateStatus="validating">
<TreeSelect {...treeSelectProps} />
</Form.Item>
</SplitSpace>
</Form>
{/* Form - InputNumber */}
<Form>
<SplitSpace>
<Form.Item>
<InputNumber />
</Form.Item>
<Form.Item hasFeedback validateStatus="success">
<InputNumber />
</Form.Item>
<Form.Item hasFeedback validateStatus="warning">
<InputNumber />
</Form.Item>
<Form.Item hasFeedback validateStatus="error">
<InputNumber />
</Form.Item>
<Form.Item hasFeedback validateStatus="validating">
<InputNumber />
</Form.Item>
</SplitSpace>
</Form>
{/* Form - DatePicker */}
<Form>
<SplitSpace>
<Form.Item>
<DatePicker />
</Form.Item>
<Form.Item hasFeedback validateStatus="success">
<DatePicker />
</Form.Item>
<Form.Item hasFeedback validateStatus="warning">
<DatePicker />
</Form.Item>
<Form.Item hasFeedback validateStatus="error">
<DatePicker />
</Form.Item>
<Form.Item hasFeedback validateStatus="validating">
<DatePicker />
</Form.Item>
</SplitSpace>
</Form>
<SplitSpace>
<Checkbox>Checkbox</Checkbox>
<Radio.Group defaultValue="bamboo">
<Radio value="bamboo">Bamboo</Radio>
<Radio value="light">Light</Radio>
<Radio value="little">Little</Radio>
</Radio.Group>
<Mentions placeholder="Mention by @">
<Mentions.Option value="afc163">afc163</Mentions.Option>
<Mentions.Option value="zombieJ">zombieJ</Mentions.Option>
<Mentions.Option value="yesmeck">yesmeck</Mentions.Option>
</Mentions>
<Slider defaultValue={30} style={{ width: 100 }} />
<Switch defaultChecked />
</SplitSpace>
<SplitSpace>
<DatePicker.RangePicker />
<TimePicker.RangePicker />
</SplitSpace>
<Row gutter={16}>
<Col span={8}>
{/* Card */}
<Card
style={{ width: '100%' }}
tabList={carTabListNoTitle}
tabBarExtraContent={<a href="#">More</a>}
/>
</Col>
<Col span={8}>
{/* Table */}
<Table
size="small"
bordered
rowSelection={{}}
columns={[
{
title: 'Key',
dataIndex: 'key',
filters: [
{
text: 'Little',
value: 'little',
},
],
sorter: (a, b) => a.key.length - b.key.length,
},
]}
dataSource={[
{
key: 'Bamboo',
},
{
key: 'Light',
},
{
key: 'Little',
},
]}
/>
</Col>
<Col span={8}>
{/* Table */}
<Tabs defaultActiveKey="1">
<Tabs.TabPane tab="Tab 1" key="1">
Content of Tab Pane 1
</Tabs.TabPane>
<Tabs.TabPane tab="Tab 2" key="2">
Content of Tab Pane 2
</Tabs.TabPane>
<Tabs.TabPane tab="Tab 3" key="3">
Content of Tab Pane 3
</Tabs.TabPane>
</Tabs>
</Col>
</Row>
<SplitSpace>
<Tag color="success">success</Tag>
<Tag color="processing">processing</Tag>
<Tag color="error">error</Tag>
<Tag color="warning">warning</Tag>
<Tag color="default">default</Tag>
<Tag.CheckableTag checked>CheckableTag</Tag.CheckableTag>
</SplitSpace>
<Row gutter={16}>
<Col span={16}>
<Timeline mode="alternate">
<Timeline.Item>Create a services site 2015-09-01</Timeline.Item>
<Timeline.Item color="gray">
Solve initial network problems 2015-09-01
</Timeline.Item>
<Timeline.Item dot={<ClockCircleOutlined style={{ fontSize: '16px' }} />}>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium
doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore
veritatis et quasi architecto beatae vitae dicta sunt explicabo.
</Timeline.Item>
</Timeline>
</Col>
<Col span={8}>
<Tree treeData={treeData} height={200} defaultExpandAll checkable />
</Col>
</Row>
{/* Alert */}
<Row gutter={16}>
<Col span={6}>
<Alert showIcon message="Success Text" type="success" />
</Col>
<Col span={6}>
<Alert showIcon message="Info Text" type="info" />
</Col>
<Col span={6}>
<Alert showIcon message="Warning Text" type="warning" />
</Col>
<Col span={6}>
<Alert showIcon message="Error Text" type="error" />
</Col>
</Row>
{/* Progress */}
<Row gutter={16}>
<Col flex="auto">
<Progress percent={30} />
<Progress percent={70} status="exception" />
<Progress percent={100} />
</Col>
<Col flex="none">
<Progress type="circle" percent={75} />
<Progress type="circle" percent={70} status="exception" />
<Progress type="circle" percent={100} />
</Col>
</Row>
<MyTransfer />
</Space>
</Col>
</Row>
);
};
ReactDOM.render(<FormSizeDemo />, mountNode);
```

View File

@ -12,11 +12,13 @@ import {
CSPConfig, CSPConfig,
DirectionType, DirectionType,
ConfigConsumerProps, ConfigConsumerProps,
Theme,
} from './context'; } from './context';
import SizeContext, { SizeContextProvider, SizeType } from './SizeContext'; import SizeContext, { SizeContextProvider, SizeType } from './SizeContext';
import message from '../message'; import message from '../message';
import notification from '../notification'; import notification from '../notification';
import { RequiredMark } from '../form/Form'; import { RequiredMark } from '../form/Form';
import { registerTheme } from './cssVariables';
import defaultLocale from '../locale/default'; import defaultLocale from '../locale/default';
export { export {
@ -89,18 +91,6 @@ export const defaultIconPrefixCls = 'anticon';
let globalPrefixCls: string; let globalPrefixCls: string;
let globalIconPrefixCls: string; let globalIconPrefixCls: string;
const setGlobalConfig = ({
prefixCls,
iconPrefixCls,
}: Pick<ConfigProviderProps, 'prefixCls' | 'iconPrefixCls'>) => {
if (prefixCls !== undefined) {
globalPrefixCls = prefixCls;
}
if (iconPrefixCls !== undefined) {
globalIconPrefixCls = iconPrefixCls;
}
};
function getGlobalPrefixCls() { function getGlobalPrefixCls() {
return globalPrefixCls || defaultPrefixCls; return globalPrefixCls || defaultPrefixCls;
} }
@ -109,6 +99,23 @@ function getGlobalIconPrefixCls() {
return globalIconPrefixCls || defaultIconPrefixCls; return globalIconPrefixCls || defaultIconPrefixCls;
} }
const setGlobalConfig = ({
prefixCls,
iconPrefixCls,
theme,
}: Pick<ConfigProviderProps, 'prefixCls' | 'iconPrefixCls'> & { theme?: Theme }) => {
if (prefixCls !== undefined) {
globalPrefixCls = prefixCls;
}
if (iconPrefixCls !== undefined) {
globalIconPrefixCls = iconPrefixCls;
}
if (theme) {
registerTheme(getGlobalPrefixCls(), theme);
}
};
export const globalConfig = () => ({ export const globalConfig = () => ({
getPrefixCls: (suffixCls?: string, customizePrefixCls?: string) => { getPrefixCls: (suffixCls?: string, customizePrefixCls?: string) => {
if (customizePrefixCls) return customizePrefixCls; if (customizePrefixCls) return customizePrefixCls;

View File

@ -610,7 +610,7 @@
} }
&-active { &-active {
background: fade(@calendar-item-active-bg, 20%); background: @calendar-column-active-bg;
} }
&:hover { &:hover {

View File

@ -31,7 +31,7 @@ Commonly displayed on the details page.
### DescriptionItem ### DescriptionItem
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | ------------ | ------------------------------ | ------------- | ------- | ------- |
| contentStyle | Customize content style | CSSProperties | - | 4.9.0 | | contentStyle | Customize content style | CSSProperties | - | 4.9.0 |
| label | The description of the content | ReactNode | - | | | label | The description of the content | ReactNode | - | |
| labelStyle | Customize label style | CSSProperties | - | 4.9.0 | | labelStyle | Customize label style | CSSProperties | - | 4.9.0 |

View File

@ -1,6 +1,12 @@
@import '../../input/style/mixin'; @import '../../input/style/mixin';
.form-control-validation(@text-color: @input-color; @border-color: @input-border-color; @background-color: @input-bg) { .form-control-validation(
@text-color: @input-color;
@border-color: @input-border-color;
@background-color: @input-bg;
@hoverBorderColor: @primary-color-hover;
@outlineColor: @primary-color-outline;
) {
.@{ant-prefix}-form-item-split { .@{ant-prefix}-form-item-split {
color: @text-color; color: @text-color;
} }
@ -15,12 +21,12 @@
&:focus, &:focus,
&-focused { &-focused {
.active(@border-color); .active(@border-color, @hoverBorderColor, @outlineColor);
} }
} }
.@{ant-prefix}-calendar-picker-open .@{ant-prefix}-calendar-picker-input { .@{ant-prefix}-calendar-picker-open .@{ant-prefix}-calendar-picker-input {
.active(@border-color); .active(@border-color, @hoverBorderColor, @outlineColor);
} }
.@{ant-prefix}-input-prefix { .@{ant-prefix}-input-prefix {

View File

@ -127,7 +127,7 @@
// ======================== Warning ======================== // ======================== Warning ========================
&-has-warning { &-has-warning {
.form-control-validation(@warning-color; @warning-color; @form-warning-input-bg); .form-control-validation(@warning-color; @warning-color; @form-warning-input-bg; @warning-color-hover; @warning-color-outline);
&.@{form-item-prefix-cls}-has-feedback .@{form-item-prefix-cls}-children-icon { &.@{form-item-prefix-cls}-has-feedback .@{form-item-prefix-cls}-children-icon {
color: @warning-color; color: @warning-color;
@ -142,7 +142,7 @@
} }
&.@{ant-prefix}-select-open .@{ant-prefix}-select-selector, &.@{ant-prefix}-select-open .@{ant-prefix}-select-selector,
&.@{ant-prefix}-select-focused .@{ant-prefix}-select-selector { &.@{ant-prefix}-select-focused .@{ant-prefix}-select-selector {
.active(@warning-color); .active(@warning-color, @warning-color-hover, @warning-color-outline);
} }
} }
@ -153,7 +153,7 @@
border-color: @warning-color; border-color: @warning-color;
&-focused, &-focused,
&:focus { &:focus {
.active(@warning-color); .active(@warning-color, @warning-color-hover, @warning-color-outline);
} }
&:not([disabled]):hover { &:not([disabled]):hover {
background-color: @form-warning-input-bg; background-color: @form-warning-input-bg;
@ -162,13 +162,13 @@
} }
.@{ant-prefix}-cascader-picker:focus .@{ant-prefix}-cascader-input { .@{ant-prefix}-cascader-picker:focus .@{ant-prefix}-cascader-input {
.active(@warning-color); .active(@warning-color, @warning-color-hover, @warning-color-outline);
} }
} }
// ========================= Error ========================= // ========================= Error =========================
&-has-error { &-has-error {
.form-control-validation(@error-color; @error-color; @form-error-input-bg); .form-control-validation(@error-color; @error-color; @form-error-input-bg; @error-color-hover; @error-color-outline);
&.@{form-item-prefix-cls}-has-feedback .@{form-item-prefix-cls}-children-icon { &.@{form-item-prefix-cls}-has-feedback .@{form-item-prefix-cls}-children-icon {
color: @error-color; color: @error-color;
@ -183,7 +183,7 @@
} }
&.@{ant-prefix}-select-open .@{ant-prefix}-select-selector, &.@{ant-prefix}-select-open .@{ant-prefix}-select-selector,
&.@{ant-prefix}-select-focused .@{ant-prefix}-select-selector { &.@{ant-prefix}-select-focused .@{ant-prefix}-select-selector {
.active(@error-color); .active(@error-color, @error-color-hover, @error-color-outline);
} }
} }
@ -213,7 +213,7 @@
border-color: @error-color; border-color: @error-color;
&-focused, &-focused,
&:focus { &:focus {
.active(@error-color); .active(@error-color, @error-color-hover, @error-color-outline);
} }
&:not([disabled]):hover { &:not([disabled]):hover {
background-color: @form-error-input-bg; background-color: @form-error-input-bg;
@ -231,7 +231,7 @@
} }
&.@{ant-prefix}-mention-active:not([disabled]) .@{ant-prefix}-mention-editor, &.@{ant-prefix}-mention-active:not([disabled]) .@{ant-prefix}-mention-editor,
.@{ant-prefix}-mention-editor:not([disabled]):focus { .@{ant-prefix}-mention-editor:not([disabled]):focus {
.active(@error-color); .active(@error-color, @error-color-hover, @error-color-outline);
} }
} }
@ -245,7 +245,7 @@
&:focus .@{ant-prefix}-cascader-input { &:focus .@{ant-prefix}-cascader-input {
background-color: @form-error-input-bg; background-color: @form-error-input-bg;
.active(@error-color); .active(@error-color, @error-color-hover, @error-color-outline);
} }
} }

View File

@ -14,20 +14,27 @@
} }
// input status // input status
// == when focus or actived // == when focus or active
.active(@color: @outline-color) { .active(@borderColor: @primary-color; @hoverBorderColor: @primary-color-hover; @outlineColor: @primary-color-outline) {
& when (@theme = dark) { & when (@theme = dark) {
border-color: @color; border-color: @borderColor;
} }
& when not (@theme = dark) { & when (not (@theme = dark) and not (@theme = variable)) {
border-color: ~`colorPalette('@{color}', 5) `; border-color: @hoverBorderColor;
}
& when not (@theme = variable) {
box-shadow: @input-outline-offset @outline-blur-size @outline-width
fade(@borderColor, @outline-fade);
}
& when (@theme = variable) {
border-color: @hoverBorderColor;
box-shadow: @input-outline-offset @outline-blur-size @outline-width @outlineColor;
} }
border-right-width: @border-width-base !important; border-right-width: @border-width-base !important;
outline: 0; outline: 0;
box-shadow: @input-outline-offset @outline-blur-size @outline-width fade(@color, @outline-fade);
} }
// == when hoverd // == when hover
.hover(@color: @input-hover-border-color) { .hover(@color: @input-hover-border-color) {
border-color: @color; border-color: @color;
border-right-width: @border-width-base !important; border-right-width: @border-width-base !important;

View File

@ -6,7 +6,7 @@
@menu-animation-duration-normal: 0.15s; @menu-animation-duration-normal: 0.15s;
.accessibility-focus() { .accessibility-focus() {
box-shadow: 0 0 0 2px fade(@primary-color, 20%); box-shadow: 0 0 0 2px @primary-1;
} }
// TODO: Should remove icon style compatible in v5 // TODO: Should remove icon style compatible in v5

View File

@ -5,7 +5,7 @@
@radio-group-prefix-cls: ~'@{radio-prefix-cls}-group'; @radio-group-prefix-cls: ~'@{radio-prefix-cls}-group';
@radio-inner-prefix-cls: ~'@{radio-prefix-cls}-inner'; @radio-inner-prefix-cls: ~'@{radio-prefix-cls}-inner';
@radio-duration: 0.3s; @radio-duration: 0.3s;
@radio-focus-shadow: 0 0 0 3px fade(@radio-dot-color, 8%); @radio-focus-shadow: 0 0 0 3px @primary-1;
@radio-button-focus-shadow: @radio-focus-shadow; @radio-button-focus-shadow: @radio-focus-shadow;
.@{radio-group-prefix-cls} { .@{radio-group-prefix-cls} {

View File

@ -1,4 +1,4 @@
@import '../../style/themes/default'; @import '../../style/themes/index';
@import '../../style/mixins/index'; @import '../../style/mixins/index';
@result-prefix-cls: ~'@{ant-prefix}-result'; @result-prefix-cls: ~'@{ant-prefix}-result';

View File

@ -1,2 +1,4 @@
@root-entry-name: default;
@import './themes/compact.less'; @import './themes/compact.less';
@import './core/index'; @import './core/index';

View File

@ -1,2 +1,4 @@
@root-entry-name: default;
@import './themes/dark.less'; @import './themes/dark.less';
@import './core/index'; @import './core/index';

View File

@ -1,4 +1,4 @@
@import '../../style/themes/default'; @import (reference) '../../style/themes/index';
.operation-unit() { .operation-unit() {
color: @link-color; color: @link-color;

View File

@ -9,14 +9,48 @@
// An override for the html selector for theme prefixes // An override for the html selector for theme prefixes
@html-selector: html; @html-selector: html;
// [CSS-VARIABLE-REPLACE-BEGIN: html-variables]
// [CSS-VARIABLE-REPLACE-END: html-variables]
// -------- Colors ----------- // -------- Colors -----------
// >>> Primary
@primary-color: @blue-6; @primary-color: @blue-6;
@info-color: @primary-color; @primary-color-hover: color(~`colorPalette('@{primary-color}', 5) `);
@success-color: @green-6; @primary-color-active: color(~`colorPalette('@{primary-color}', 7) `);
@primary-color-outline: fade(@primary-color, @outline-fade);
@processing-color: @blue-6; @processing-color: @blue-6;
@error-color: @red-5;
@highlight-color: @red-5; // >>> Info
@info-color: @primary-color;
@info-color-deprecated-bg: color(~`colorPalette('@{info-color}', 1) `);
@info-color-deprecated-border: color(~`colorPalette('@{info-color}', 3) `);
// >>> Success
@success-color: @green-6;
@success-color-hover: color(~`colorPalette('@{success-color}', 5) `);
@success-color-active: color(~`colorPalette('@{success-color}', 7) `);
@success-color-outline: fade(@success-color, @outline-fade);
@success-color-deprecated-bg: color(~`colorPalette('@{success-color}', 1) `);
@success-color-deprecated-border: color(~`colorPalette('@{success-color}', 3) `);
// >>> Warning
@warning-color: @gold-6; @warning-color: @gold-6;
@warning-color-hover: color(~`colorPalette('@{warning-color}', 5) `);
@warning-color-active: color(~`colorPalette('@{warning-color}', 7) `);
@warning-color-outline: fade(@warning-color, @outline-fade);
@warning-color-deprecated-bg: color(~`colorPalette('@{warning-color}', 1) `);
@warning-color-deprecated-border: color(~`colorPalette('@{warning-color}', 3) `);
// >>> Error
@error-color: @red-5;
@error-color-hover: color(~`colorPalette('@{error-color}', 5) `);
@error-color-active: color(~`colorPalette('@{error-color}', 7) `);
@error-color-outline: fade(@error-color, @outline-fade);
@error-color-deprecated-bg: color(~`colorPalette('@{error-color}', 1) `);
@error-color-deprecated-border: color(~`colorPalette('@{error-color}', 3) `);
@highlight-color: @red-5;
@normal-color: #d9d9d9; @normal-color: #d9d9d9;
@white: #fff; @white: #fff;
@black: #000; @black: #000;
@ -140,7 +174,7 @@
// Outline // Outline
@outline-blur-size: 0; @outline-blur-size: 0;
@outline-width: 2px; @outline-width: 2px;
@outline-color: @primary-color; @outline-color: @primary-color; // No use anymore
@outline-fade: 20%; @outline-fade: 20%;
@background-color-light: hsv(0, 0, 98%); // background of header and selected item @background-color-light: hsv(0, 0, 98%); // background of header and selected item
@ -659,6 +693,7 @@
@calendar-input-bg: @input-bg; @calendar-input-bg: @input-bg;
@calendar-border-color: @border-color-inverse; @calendar-border-color: @border-color-inverse;
@calendar-item-active-bg: @item-active-bg; @calendar-item-active-bg: @item-active-bg;
@calendar-column-active-bg: fade(@calendar-item-active-bg, 20%);
@calendar-full-bg: @calendar-bg; @calendar-full-bg: @calendar-bg;
@calendar-full-panel-bg: @calendar-full-bg; @calendar-full-panel-bg: @calendar-full-bg;
@ -887,6 +922,7 @@
@transfer-disabled-bg: @disabled-bg; @transfer-disabled-bg: @disabled-bg;
@transfer-list-height: 200px; @transfer-list-height: 200px;
@transfer-item-hover-bg: @item-hover-bg; @transfer-item-hover-bg: @item-hover-bg;
@transfer-item-selected-hover-bg: darken(@item-active-bg, 2%);
@transfer-item-padding-vertical: 6px; @transfer-item-padding-vertical: 6px;
@transfer-list-search-icon-top: 12px; @transfer-list-search-icon-top: 12px;

View File

@ -1 +1,7 @@
@import './default.less'; // Default using variable as entry to support site variable version
// This will be replaced in webpack bundle
// @root-entry-name: variable;
// @import './default.less';
// @import './variable.less';
@import './@{root-entry-name}.less';

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
@root-entry-name: variable;
@import './themes/variable.less';
@import './core/index';

View File

@ -30,7 +30,7 @@
} }
&-checked:focus { &-checked:focus {
box-shadow: 0 0 0 2px fade(@switch-color, 20%); box-shadow: 0 0 0 2px @primary-1;
} }
&:focus:hover { &:focus:hover {

View File

@ -97,23 +97,23 @@
} }
} }
.make-status-color-classes(@color, @status) { .make-status-color-classes(@status, @cssVariableType) {
@lightColor: '@{color}-1'; @bgColor: '@{cssVariableType}-color-deprecated-bg';
@lightBorderColor: '@{color}-3'; @borderColor: '@{cssVariableType}-color-deprecated-border';
@darkColor: '@{color}-6'; @textColor: '@{cssVariableType}-color';
&-@{status} { &-@{status} {
color: @@darkColor; color: @@textColor;
background: @@lightColor; background: @@bgColor;
border-color: @@lightBorderColor; border-color: @@borderColor;
} }
} }
.make-color-classes(); .make-color-classes();
.make-status-color-classes('green', success); .make-status-color-classes(success, success);
.make-status-color-classes('blue', processing); .make-status-color-classes(processing, info);
.make-status-color-classes('red', error); .make-status-color-classes(error, error);
.make-status-color-classes('orange', warning); .make-status-color-classes(warning, warning);
// To ensure that a space will be placed between character and `Icon`. // To ensure that a space will be placed between character and `Icon`.
> .@{iconfont-css-prefix} + span, > .@{iconfont-css-prefix} + span,

View File

@ -152,7 +152,7 @@
} }
&.@{transfer-prefix-cls}-list-content-item-checked:hover { &.@{transfer-prefix-cls}-list-content-item-checked:hover {
background-color: darken(@item-active-bg, 2%); background-color: @transfer-item-selected-hover-bg;
} }
} }

View File

@ -0,0 +1,72 @@
---
order: 7.1
title: Dynamic Theme (experience)
---
Except [less customize theme](/docs/react/customize-theme), We also provide CSS Variable version to enable dynamic theme. You can check on [ConfigProvider](/components/config-provider/#components-config-provider-demo-theme) demo.
## Notice
This function need CSS Variable support which mean it can not support IE. Please make sure your browser requirement.
## How to use
### Import antd.variable.min.css
Replace your import style file with CSS Variable version:
```diff
-- import 'antd/dist/antd.min.css';
++ import 'antd/dist/antd.variable.min.css';
```
### Static config
Call ConfigProvider static function to modify theme color:
```ts
import { ConfigProvider } from 'antd';
ConfigProvider.config({
theme: {
primaryColor: '#25b864',
},
});
```
## Conflict resolve
CSS Variable use `--ant` prefix by default. When exist multiple antd style file in your project, you can modify prefix to fix it.
### Adjust
Modify `prefixCls` on the root of ConfigProvider:
```tsx
import { ConfigProvider } from 'antd';
export default () => (
<ConfigProvider prefixCls="custom">
<MyApp />
</ConfigProvider>
);
```
Also need call the static function to modify `prefixCls`:
```ts
ConfigProvider.config({
theme: {
prefixCls: 'custom',
primaryColor: '#25b864',
},
});
```
### Compile less
Since prefix modified. Origin `antd.variable.css` should also be replaced:
```bash
lessc --modify-var="ant-prefix=custom" antd/dist/antd.variable.less modified.css
```

View File

@ -0,0 +1,72 @@
---
order: 7.1
title: 动态主题(实验性)
---
除了 [less 定制主题外](/docs/react/customize-theme) 外,我们还提供了 CSS Variable 版本以支持动态切换主题能力。你可以在 [ConfigProvider](/components/config-provider/#components-config-provider-demo-theme) 进行体验。
## 注意事项
该功能通过动态修改 CSS Variable 实现,因而在 IE 中页面将无法正常展示。请先确认你的用户环境是否需要支持 IE。
## 如何使用
### 引入 antd.variable.min.css
替换当前项目引入样式文件为 CSS Variable 版本:
```diff
-- import 'antd/dist/antd.min.css';
++ import 'antd/dist/antd.variable.min.css';
```
### 静态方法配置
调用 ConfigProvider 配置方法设置主题色:
```ts
import { ConfigProvider } from 'antd';
ConfigProvider.config({
theme: {
primaryColor: '#25b864',
},
});
```
## 冲突解决
默认情况下CSS Variable 会以 `--ant` 作为前缀。当你的项目中引用多份 css 文件时,可以通过修改前缀的方式避免冲突。
### 代码调整
通过 ConfigProvider 在顶层修改 `prefixCls`
```tsx
import { ConfigProvider } from 'antd';
export default () => (
<ConfigProvider prefixCls="custom">
<MyApp />
</ConfigProvider>
);
```
通过静态方法设置主题色以及对应 `prefixCls`
```ts
ConfigProvider.config({
theme: {
prefixCls: 'custom',
primaryColor: '#25b864',
},
});
```
### 编译 less
由于前缀变更,你需要重新生成一份对应的 css 文件。
```bash
lessc --modify-var="ant-prefix=custom" antd/dist/antd.variable.less modified.css
```

View File

@ -1,5 +1,5 @@
--- ---
order: 7.1 order: 7.5
title: Replace Moment.js title: Replace Moment.js
--- ---

View File

@ -1,5 +1,5 @@
--- ---
order: 7.1 order: 7.5
title: 替换 Moment.js title: 替换 Moment.js
--- ---

24
index-style-only.js Normal file
View File

@ -0,0 +1,24 @@
function pascalCase(name) {
return name.charAt(0).toUpperCase() + name.slice(1).replace(/-(\w)/g, (m, n) => n.toUpperCase());
}
// Just import style for https://github.com/ant-design/ant-design/issues/3745
const req = require.context('./components', true, /^\.\/[^_][\w-]+\/style\/index\.tsx?$/);
req.keys().forEach(mod => {
let v = req(mod);
if (v && v.default) {
v = v.default;
}
const match = mod.match(/^\.\/([^_][\w-]+)\/index\.tsx?$/);
if (match && match[1]) {
if (match[1] === 'message' || match[1] === 'notification') {
// message & notification should not be capitalized
exports[match[1]] = v;
} else {
exports[pascalCase(match[1])] = v;
}
}
});
module.exports = exports;

View File

@ -1,25 +1,3 @@
/* eslint no-console:0 */ require('./index-style-only');
function pascalCase(name) {
return name.charAt(0).toUpperCase() + name.slice(1).replace(/-(\w)/g, (m, n) => n.toUpperCase());
}
// Just import style for https://github.com/ant-design/ant-design/issues/3745
const req = require.context('./components', true, /^\.\/[^_][\w-]+\/style\/index\.tsx?$/);
req.keys().forEach(mod => {
let v = req(mod);
if (v && v.default) {
v = v.default;
}
const match = mod.match(/^\.\/([^_][\w-]+)\/index\.tsx?$/);
if (match && match[1]) {
if (match[1] === 'message' || match[1] === 'notification') {
// message & notification should not be capitalized
exports[match[1]] = v;
} else {
exports[pascalCase(match[1])] = v;
}
}
});
module.exports = require('./components'); module.exports = require('./components');

View File

@ -62,7 +62,6 @@
"presite": "npm run version", "presite": "npm run version",
"color-less": "node ./scripts/generate-color-less", "color-less": "node ./scripts/generate-color-less",
"compile": "npm run clean && antd-tools run compile", "compile": "npm run clean && antd-tools run compile",
"compile:less": "antd-tools run compile:less",
"changelog": "node ./scripts/print-changelog", "changelog": "node ./scripts/print-changelog",
"predeploy": "antd-tools run clean && npm run site && cp CNAME _site && npm run site:test", "predeploy": "antd-tools run clean && npm run site && cp CNAME _site && npm run site:test",
"deploy": "bisheng gh-pages --push-only --dotfiles", "deploy": "bisheng gh-pages --push-only --dotfiles",
@ -94,7 +93,7 @@
"test-node": "jest --config .jest.node.js --cache=false", "test-node": "jest --config .jest.node.js --cache=false",
"tsc": "tsc --noEmit", "tsc": "tsc --noEmit",
"site:test": "jest --config .jest.site.js --cache=false --force-exit", "site:test": "jest --config .jest.site.js --cache=false --force-exit",
"test-image": "npm run compile:less && docker-compose run tests", "test-image": "npm run dist && docker-compose run tests",
"version": "node ./scripts/generate-version", "version": "node ./scripts/generate-version",
"install-react-16": "npm i --no-save react@16 react-dom@16 react-test-renderer@16 enzyme-adapter-react-16", "install-react-16": "npm i --no-save react@16 react-dom@16 react-test-renderer@16 enzyme-adapter-react-16",
"argos": "argos upload imageSnapshots" "argos": "argos upload imageSnapshots"
@ -112,6 +111,7 @@
"@ant-design/icons": "^4.6.3", "@ant-design/icons": "^4.6.3",
"@ant-design/react-slick": "~0.28.1", "@ant-design/react-slick": "~0.28.1",
"@babel/runtime": "^7.12.5", "@babel/runtime": "^7.12.5",
"@ctrl/tinycolor": "^3.4.0",
"array-tree-filter": "^2.1.0", "array-tree-filter": "^2.1.0",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"copy-to-clipboard": "^3.2.0", "copy-to-clipboard": "^3.2.0",
@ -153,7 +153,7 @@
"devDependencies": { "devDependencies": {
"@ant-design/bisheng-plugin": "^2.3.0", "@ant-design/bisheng-plugin": "^2.3.0",
"@ant-design/hitu": "^0.0.0-alpha.13", "@ant-design/hitu": "^0.0.0-alpha.13",
"@ant-design/tools": "^13.6.2", "@ant-design/tools": "^14.0.0-alpha.2",
"@docsearch/css": "^3.0.0-alpha.39", "@docsearch/css": "^3.0.0-alpha.39",
"@docsearch/react": "^3.0.0-alpha.39", "@docsearch/react": "^3.0.0-alpha.39",
"@qixian.cs/github-contributors-list": "^1.0.3", "@qixian.cs/github-contributors-list": "^1.0.3",
@ -291,7 +291,7 @@
"bundlesize": [ "bundlesize": [
{ {
"path": "./dist/antd.min.js", "path": "./dist/antd.min.js",
"maxSize": "270 kB" "maxSize": "275 kB"
}, },
{ {
"path": "./dist/antd.min.css", "path": "./dist/antd.min.css",
@ -304,6 +304,10 @@
{ {
"path": "./dist/antd.compact.min.css", "path": "./dist/antd.compact.min.css",
"maxSize": "65 kB" "maxSize": "65 kB"
},
{
"path": "./dist/antd.variable.min.css",
"maxSize": "65 kB"
} }
], ],
"tnpm": { "tnpm": {

View File

@ -0,0 +1,221 @@
/**
* ZombieJ: Since we still need mainly maintain the `default.less`. Create a script that generate
* `variable.less` from the `default.less`
*/
const fse = require('fs-extra');
const path = require('path');
const chalk = require('chalk');
const folderPath = path.resolve(__dirname, '..', 'components', 'style', 'themes');
const targetPath = path.resolve(folderPath, 'variable.less');
const defaultContent = fse.readFileSync(path.resolve(folderPath, 'default.less'), 'utf8');
// const variableContent = fse.readFileSync(
// path.resolve(__dirname, '..', 'components', 'style', 'themes', 'variable.less'),
// 'utf8',
// );
let variableContent = defaultContent;
function replaceVariable(key, value) {
variableContent = variableContent.replace(new RegExp(`@${key}:[^;]*;`), `@${key}: ${value};`);
}
function replaceVariableContent(key, content) {
const lines = variableContent.split(/\n/);
const startIndex = lines.findIndex(line => line.includes(`[CSS-VARIABLE-REPLACE-BEGIN: ${key}]`));
const endIndex = lines.findIndex(line => line.includes(`[CSS-VARIABLE-REPLACE-END: ${key}]`));
if (startIndex !== -1 && endIndex !== -1) {
variableContent = [...lines.slice(0, startIndex), content, ...lines.slice(endIndex + 1)].join(
'\n',
);
}
}
replaceVariable('theme', 'variable');
replaceVariableContent(
'html-variables',
`
html {
@base-primary: @blue-6;
// ========= Primary Color =========
--@{ant-prefix}-primary-color: @base-primary;
--@{ant-prefix}-primary-color-hover: color(~\`colorPalette('@{base-primary}', 5) \`);
--@{ant-prefix}-primary-color-active: color(~\`colorPalette('@{base-primary}', 7) \`);
--@{ant-prefix}-primary-color-outline: fade(@base-primary, @outline-fade);
// Legacy
@legacy-primary-1: color(~\`colorPalette('@{base-primary}', 1) \`);
--@{ant-prefix}-primary-1: @legacy-primary-1;
--@{ant-prefix}-primary-2: color(~\`colorPalette('@{base-primary}', 2) \`);
--@{ant-prefix}-primary-3: color(~\`colorPalette('@{base-primary}', 3) \`);
--@{ant-prefix}-primary-4: color(~\`colorPalette('@{base-primary}', 4) \`);
--@{ant-prefix}-primary-5: color(~\`colorPalette('@{base-primary}', 5) \`);
--@{ant-prefix}-primary-6: @base-primary;
--@{ant-prefix}-primary-7: color(~\`colorPalette('@{base-primary}', 7) \`);
// Deprecated
--@{ant-prefix}-primary-color-deprecated-pure: ~'';
--@{ant-prefix}-primary-color-deprecated-l-35: lighten(@base-primary, 35%);
--@{ant-prefix}-primary-color-deprecated-l-20: lighten(@base-primary, 20%);
--@{ant-prefix}-primary-color-deprecated-t-20: tint(@base-primary, 20%);
--@{ant-prefix}-primary-color-deprecated-t-50: tint(@base-primary, 50%);
--@{ant-prefix}-primary-color-deprecated-f-12: fade(@base-primary, 12%);
--@{ant-prefix}-primary-color-active-deprecated-f-30: fade(@legacy-primary-1, 30%);
--@{ant-prefix}-primary-color-active-deprecated-d-02: darken(@legacy-primary-1, 2%);
// ========= Success Color =========
--@{ant-prefix}-success-color: @green-6;
--@{ant-prefix}-success-color-hover: color(~\`colorPalette('@{green-6}', 5) \`);
--@{ant-prefix}-success-color-active: color(~\`colorPalette('@{green-6}', 7) \`);
--@{ant-prefix}-success-color-outline: fade(@green-6, @outline-fade);
--@{ant-prefix}-success-color-deprecated-bg: ~\`colorPalette('@{green-6}', 1) \`;
--@{ant-prefix}-success-color-deprecated-border: ~\`colorPalette('@{green-6}', 3) \`;
// ========== Error Color ==========
--@{ant-prefix}-error-color: @red-5;
--@{ant-prefix}-error-color-hover: color(~\`colorPalette('@{red-5}', 5) \`);
--@{ant-prefix}-error-color-active: color(~\`colorPalette('@{red-5}', 7) \`);
--@{ant-prefix}-error-color-outline: fade(@red-5, @outline-fade);
--@{ant-prefix}-error-color-deprecated-bg: ~\`colorPalette('@{red-5}', 1) \`;
--@{ant-prefix}-error-color-deprecated-border: ~\`colorPalette('@{red-5}', 3) \`;
// ========= Warning Color =========
--@{ant-prefix}-warning-color: @gold-6;
--@{ant-prefix}-warning-color-hover: color(~\`colorPalette('@{gold-6}', 5) \`);
--@{ant-prefix}-warning-color-active: color(~\`colorPalette('@{gold-6}', 7) \`);
--@{ant-prefix}-warning-color-outline: fade(@gold-6, @outline-fade);
--@{ant-prefix}-warning-color-deprecated-bg: ~\`colorPalette('@{gold-6}', 1) \`;
--@{ant-prefix}-warning-color-deprecated-border: ~\`colorPalette('@{gold-6}', 3) \`;
// ========== Info Color ===========
--@{ant-prefix}-info-color: @base-primary;
--@{ant-prefix}-info-color-deprecated-bg: ~\`colorPalette('@{base-primary}', 1) \`;
--@{ant-prefix}-info-color-deprecated-border: ~\`colorPalette('@{base-primary}', 3) \`;
}
`.trim(),
);
// >>> Primary
replaceVariable('primary-color', "~'var(--@{ant-prefix}-primary-color)'");
replaceVariable('primary-color-hover', "~'var(--@{ant-prefix}-primary-color-hover)'");
replaceVariable('primary-color-active', "~'var(--@{ant-prefix}-primary-color-active)'");
replaceVariable('primary-color-outline', "~'var(--@{ant-prefix}-primary-color-outline)'");
replaceVariable('processing-color', '@primary-color');
// >>> Info
replaceVariable('info-color', "~'var(--@{ant-prefix}-info-color)'");
replaceVariable('info-color-deprecated-bg', "~'var(--@{ant-prefix}-info-color-deprecated-bg)'");
replaceVariable(
'info-color-deprecated-border',
"~'var(--@{ant-prefix}-info-color-deprecated-border)'",
);
// >>> Success
replaceVariable('success-color', "~'var(--@{ant-prefix}-success-color)'");
replaceVariable('success-color-hover', "~'var(--@{ant-prefix}-success-color-hover)'");
replaceVariable('success-color-active', "~'var(--@{ant-prefix}-success-color-active)'");
replaceVariable('success-color-outline', "~'var(--@{ant-prefix}-success-color-outline)'");
replaceVariable(
'success-color-deprecated-bg',
"~'var(--@{ant-prefix}-success-color-deprecated-bg)'",
);
replaceVariable(
'success-color-deprecated-border',
"~'var(--@{ant-prefix}-success-color-deprecated-border)'",
);
// >>> Warning
replaceVariable('warning-color', "~'var(--@{ant-prefix}-warning-color)'");
replaceVariable('warning-color-hover', "~'var(--@{ant-prefix}-warning-color-hover)'");
replaceVariable('warning-color-active', "~'var(--@{ant-prefix}-warning-color-active)'");
replaceVariable('warning-color-outline', "~'var(--@{ant-prefix}-warning-color-outline)'");
replaceVariable(
'warning-color-deprecated-bg',
"~'var(--@{ant-prefix}-warning-color-deprecated-bg)'",
);
replaceVariable(
'warning-color-deprecated-border',
"~'var(--@{ant-prefix}-warning-color-deprecated-border)'",
);
// >>> Error
replaceVariable('error-color', "~'var(--@{ant-prefix}-error-color)'");
replaceVariable('error-color-hover', "~'var(--@{ant-prefix}-error-color-hover)'");
replaceVariable('error-color-active', "~'var(--@{ant-prefix}-error-color-active)'");
replaceVariable('error-color-outline', "~'var(--@{ant-prefix}-error-color-outline)'");
replaceVariable('error-color-deprecated-bg', "~'var(--@{ant-prefix}-error-color-deprecated-bg)'");
replaceVariable(
'error-color-deprecated-border',
"~'var(--@{ant-prefix}-error-color-deprecated-border)'",
);
// >>> Primary Level Color
replaceVariable('primary-1', "~'var(--@{ant-prefix}-primary-1)'");
replaceVariable('primary-2', "~'var(--@{ant-prefix}-primary-2)'");
replaceVariable('primary-3', "~'var(--@{ant-prefix}-primary-3)'");
replaceVariable('primary-4', "~'var(--@{ant-prefix}-primary-4)'");
replaceVariable('primary-5', "~'var(--@{ant-prefix}-primary-5)'");
replaceVariable('primary-6', "~'var(--@{ant-prefix}-primary-6)'");
replaceVariable('primary-7', "~'var(--@{ant-prefix}-primary-7)'");
// Link
replaceVariable('link-hover-color', '@primary-color-hover');
replaceVariable('link-active-color', '@primary-color-active');
replaceVariable(
'table-selected-row-hover-bg',
"~'var(--@{ant-prefix}-primary-color-active-deprecated-d-02)'",
);
replaceVariable(
'picker-basic-cell-hover-with-range-color',
"~'var(--@{ant-prefix}-primary-color-deprecated-l-35)'",
);
replaceVariable(
'picker-date-hover-range-border-color',
"~'var(--@{ant-prefix}-primary-color-deprecated-l-20)'",
);
replaceVariable(
'calendar-column-active-bg',
"~'var(--@{ant-prefix}-primary-color-active-deprecated-f-30)'",
);
replaceVariable(
'slider-handle-color-focus',
"~'var(--@{ant-prefix}-primary-color-deprecated-t-20)'",
);
replaceVariable(
'slider-handle-color-focus-shadow',
"~'var(--@{ant-prefix}-primary-color-deprecated-f-12)'",
);
replaceVariable(
'slider-dot-border-color-active',
"~'var(--@{ant-prefix}-primary-color-deprecated-t-50)'",
);
replaceVariable(
'transfer-item-selected-hover-bg',
"~'var(--@{ant-prefix}-primary-color-active-deprecated-d-02)'",
);
replaceVariable('alert-success-border-color', '@success-color-deprecated-border');
replaceVariable('alert-success-bg-color', '@success-color-deprecated-bg');
replaceVariable('alert-info-border-color', '@info-color-deprecated-border');
replaceVariable('alert-info-bg-color', '@info-color-deprecated-bg');
replaceVariable('alert-warning-border-color', '@warning-color-deprecated-border');
replaceVariable('alert-warning-bg-color', '@warning-color-deprecated-bg');
replaceVariable('alert-error-border-color', '@error-color-deprecated-border');
replaceVariable('alert-error-bg-color', '@error-color-deprecated-bg');
fse.writeFileSync(targetPath, variableContent, 'utf8');
console.log(chalk.green('Success! Replaced path:'), targetPath);

29
site/antd.js Normal file
View File

@ -0,0 +1,29 @@
/* eslint no-console:0 */
function pascalCase(name) {
return name.charAt(0).toUpperCase() + name.slice(1).replace(/-(\w)/g, (m, n) => n.toUpperCase());
}
// Import all the component less file.
// This is mostly like index.js but we do not need root `themes/index`
const req = require.context('../components', true, /^\.\/[^_][\w-]+\/style\/index\.less$/);
req.keys().forEach(mod => {
let v = req(mod);
if (v && v.default) {
v = v.default;
}
const match = mod.match(/^\.\/([^_][\w-]+)\/index\.less$/);
if (match && match[1]) {
if (match[1] === 'message' || match[1] === 'notification') {
// message & notification should not be capitalized
exports[match[1]] = v;
} else {
exports[pascalCase(match[1])] = v;
}
}
});
// Need import for the additional core style
exports.styleCore = require('../components/style/core/index.less');
module.exports = require('../components');

View File

@ -56,12 +56,16 @@ module.exports = {
}, },
lessConfig: { lessConfig: {
javascriptEnabled: true, javascriptEnabled: true,
modifyVars: {
'root-entry-name': 'variable',
},
}, },
webpackConfig(config) { webpackConfig(config) {
config.resolve.alias = { config.resolve.alias = {
'antd/lib': path.join(process.cwd(), 'components'), 'antd/lib': path.join(process.cwd(), 'components'),
'antd/es': path.join(process.cwd(), 'components'), 'antd/es': path.join(process.cwd(), 'components'),
antd: path.join(process.cwd(), 'index'), // Change antd from `index.js` to `site/antd.js` to remove deps of root style
antd: path.join(process.cwd(), 'site', 'antd'),
site: path.join(process.cwd(), 'site'), site: path.join(process.cwd(), 'site'),
'react-router': 'react-router/umd/ReactRouter', 'react-router': 'react-router/umd/ReactRouter',
}; };

View File

@ -88,6 +88,7 @@ module.exports = {
'app.footer.community': 'Community', 'app.footer.community': 'Community',
'app.footer.help': 'Help', 'app.footer.help': 'Help',
'app.footer.change-log': 'Change Log', 'app.footer.change-log': 'Change Log',
'app.footer.theme': 'Change theme color',
'app.footer.faq': 'FAQ', 'app.footer.faq': 'FAQ',
'app.footer.feedback': 'Feedback', 'app.footer.feedback': 'Feedback',
'app.footer.stackoverflow': 'StackOverflow', 'app.footer.stackoverflow': 'StackOverflow',

View File

@ -1,4 +1,4 @@
@import '../../../components/style/themes/default.less'; @import (reference) '../../../components/style/themes/variable.less';
#header { #header {
// ===================== Home Page ===================== // ===================== Home Page =====================

View File

@ -1,5 +1,5 @@
@import './reset.less'; @import './reset.less';
@import '../../../components/style/themes/default.less'; @import '../../../components/style/themes/variable.less';
@import './common'; @import './common';
@import './header'; @import './header';
@import './footer'; @import './footer';

View File

@ -1,4 +1,4 @@
@import '../../../components/style/themes/default.less'; @import (reference) '../../../components/style/themes/variable.less';
.nav-phone-icon { .nav-phone-icon {
position: absolute; position: absolute;

View File

@ -1,4 +1,4 @@
@import '../../../components/style/themes/default.less'; @import (reference) '../../../components/style/themes/variable.less';
@import './colors.less'; @import './colors.less';
@import './home.less'; @import './home.less';

View File

@ -1,4 +1,4 @@
@import '../../../../components/style/themes/default.less'; @import (reference) '../../../../components/style/themes/variable.less';
.components-overview { .components-overview {
padding: 0; padding: 0;

View File

@ -1,4 +1,4 @@
@import '../../../../../components/style/themes/default.less'; @import (reference) '../../../../../components/style/themes/variable.less';
.home-banner-background { .home-banner-background {
position: absolute; position: absolute;

View File

@ -1,4 +1,4 @@
@import '../../../../../components/style/themes/default.less'; @import (reference) '../../../../../components/style/themes/variable.less';
.home-card-logo { .home-card-logo {
position: relative; position: relative;

View File

@ -1,4 +1,4 @@
@import '../../../../../components/style/themes/default.less'; @import (reference) '../../../../../components/style/themes/variable.less';
@home-color: #0170fe; @home-color: #0170fe;

View File

@ -1,4 +1,4 @@
@import '../../../../../components/style/themes/default.less'; @import (reference) '../../../../../components/style/themes/variable.less';
.design-card { .design-card {
position: relative; position: relative;

View File

@ -1,4 +1,4 @@
@import '../../../../components/style/themes/default.less'; @import (reference) '../../../../components/style/themes/variable.less';
.more-card { .more-card {
&:hover { &:hover {

View File

@ -1,4 +1,4 @@
@import '../../../../components/style/themes/default.less'; @import (reference) '../../../../components/style/themes/variable.less';
.linear-gradient(@mid-pos, @end-pos) { .linear-gradient(@mid-pos, @end-pos) {
background: linear-gradient( background: linear-gradient(

View File

@ -1,4 +1,4 @@
@import '../../../../components/style/themes/default.less'; @import (reference) '../../../../components/style/themes/variable.less';
.home-container { .home-container {
h1, h1,

View File

@ -16,6 +16,7 @@ import {
BugOutlined, BugOutlined,
IssuesCloseOutlined, IssuesCloseOutlined,
QuestionCircleOutlined, QuestionCircleOutlined,
BgColorsOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import ColorPicker from '../Color/ColorPicker'; import ColorPicker from '../Color/ColorPicker';
import { loadScript, getLocalizedPathname } from '../utils'; import { loadScript, getLocalizedPathname } from '../utils';
@ -306,7 +307,13 @@ class Footer extends React.Component<WrappedComponentProps & { location: any }>
openExternal: true, openExternal: true,
}, },
{ {
title: this.renderThemeChanger(), icon: <BgColorsOutlined />,
title: <FormattedMessage id="app.footer.theme" />,
url: getLinkHash('/components/config-provider/', {
zhCN: 'components-config-provider-demo-theme',
enUS: 'components-config-provider-demo-theme',
}),
LinkComponent: Link,
style: { style: {
marginTop: 20, marginTop: 20,
}, },

View File

@ -1,5 +1,5 @@
@import '../../../static/theme.less'; @import '../../../static/theme.less';
@import '../../../../../components/style/themes/default.less'; @import (reference) '../../../../../components/style/themes/variable.less';
#github-btn { #github-btn {
display: flex; display: flex;

View File

@ -1,5 +1,5 @@
@import '../../../static/theme.less'; @import '../../../static/theme.less';
@import '../../../../../components/style/themes/default.less'; @import (reference) '../../../../../components/style/themes/variable.less';
@import './index.less'; @import './index.less';
#logo { #logo {

View File

@ -1,5 +1,5 @@
@import '../../../static/theme.less'; @import '../../../static/theme.less';
@import '../../../../../components/style/themes/default.less'; @import (reference) '../../../../../components/style/themes/variable.less';
@import './index.less'; @import './index.less';
#nav { #nav {

View File

@ -1,5 +1,5 @@
@import '../../../static/theme.less'; @import '../../../static/theme.less';
@import '../../../../../components/style/themes/default.less'; @import (reference) '../../../../../components/style/themes/variable.less';
@import './index.less'; @import './index.less';
@import './DocSearch.less'; @import './DocSearch.less';

View File

@ -1,5 +1,5 @@
@import '../../../static/theme.less'; @import '../../../static/theme.less';
@import '../../../../../components/style/themes/default.less'; @import (reference) '../../../../../components/style/themes/variable.less';
@header-height: 64px; @header-height: 64px;
@menu-item-border: 2px; @menu-item-border: 2px;

View File

@ -1,4 +1,4 @@
@import '../../../../components/style/themes/default.less'; @import (reference) '../../../../components/style/themes/variable.less';
@import './index.less'; @import './index.less';
.resource-affix-tabs { .resource-affix-tabs {

View File

@ -1,4 +1,4 @@
@import '../../../../components/style/themes/default.less'; @import (reference) '../../../../components/style/themes/variable.less';
@ArticleMaxWidth: 1208px; @ArticleMaxWidth: 1208px;
@resource-padding: 40px; @resource-padding: 40px;

View File

@ -86,6 +86,7 @@ module.exports = {
'app.footer.community': '社区', 'app.footer.community': '社区',
'app.footer.help': '帮助', 'app.footer.help': '帮助',
'app.footer.change-log': '更新日志', 'app.footer.change-log': '更新日志',
'app.footer.theme': '切换主题色',
'app.footer.faq': '常见问题', 'app.footer.faq': '常见问题',
'app.footer.feedback': '反馈和建议', 'app.footer.feedback': '反馈和建议',
'app.footer.stackoverflow': 'StackOverflow', 'app.footer.stackoverflow': 'StackOverflow',

View File

@ -1,5 +1,6 @@
/* eslint no-param-reassign: 0 */ /* eslint no-param-reassign: 0 */
// This config is for building dist files // This config is for building dist files
const chalk = require('chalk');
const getWebpackConfig = require('@ant-design/tools/lib/getWebpackConfig'); const getWebpackConfig = require('@ant-design/tools/lib/getWebpackConfig');
const IgnoreEmitPlugin = require('ignore-emit-webpack-plugin'); const IgnoreEmitPlugin = require('ignore-emit-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
@ -10,6 +11,30 @@ const compactVars = require('./scripts/compact-vars');
const { webpack } = getWebpackConfig; const { webpack } = getWebpackConfig;
function injectLessVariables(config, variables) {
(Array.isArray(config) ? config : [config]).forEach(conf => {
conf.module.rules.forEach(rule => {
// filter less rule
if (rule.test instanceof RegExp && rule.test.test('.less')) {
const lessRule = rule.use[rule.use.length - 1];
if (lessRule.options.lessOptions) {
lessRule.options.lessOptions.modifyVars = {
...lessRule.options.lessOptions.modifyVars,
...variables,
};
} else {
lessRule.options.modifyVars = {
...lessRule.options.modifyVars,
...variables,
};
}
}
});
});
return config;
}
// noParse still leave `require('./locale' + name)` in dist files // noParse still leave `require('./locale' + name)` in dist files
// ignore is better: http://stackoverflow.com/q/25384360 // ignore is better: http://stackoverflow.com/q/25384360
function ignoreMomentLocale(webpackConfig) { function ignoreMomentLocale(webpackConfig) {
@ -60,22 +85,22 @@ function processWebpackThemeConfig(themeConfig, theme, vars) {
// rename default entry to ${theme} entry // rename default entry to ${theme} entry
Object.keys(config.entry).forEach(entryName => { Object.keys(config.entry).forEach(entryName => {
config.entry[entryName.replace('antd', `antd.${theme}`)] = config.entry[entryName]; const originPath = config.entry[entryName];
let replacedPath = [...originPath];
// We will replace `./index` to `./index-style-only` since theme dist only use style file
if (originPath.length === 1 && originPath[0] === './index') {
replacedPath = ['./index-style-only'];
} else {
console.log(chalk.red('🆘 Seems entry has changed! It should be `./index`'));
}
config.entry[entryName.replace('antd', `antd.${theme}`)] = replacedPath;
delete config.entry[entryName]; delete config.entry[entryName];
}); });
// apply ${theme} less variables // apply ${theme} less variables
config.module.rules.forEach(rule => { injectLessVariables(config, vars);
// filter less rule
if (rule.test instanceof RegExp && rule.test.test('.less')) {
const lessRule = rule.use[rule.use.length - 1];
if (lessRule.options.lessOptions) {
lessRule.options.lessOptions.modifyVars = vars;
} else {
lessRule.options.modifyVars = vars;
}
}
});
const themeReg = new RegExp(`${theme}(.min)?\\.js(\\.map)?$`); const themeReg = new RegExp(`${theme}(.min)?\\.js(\\.map)?$`);
// ignore emit ${theme} entry js & js.map file // ignore emit ${theme} entry js & js.map file
@ -83,9 +108,15 @@ function processWebpackThemeConfig(themeConfig, theme, vars) {
}); });
} }
const webpackConfig = getWebpackConfig(false); const legacyEntryVars = {
const webpackDarkConfig = getWebpackConfig(false); 'root-entry-name': 'default',
const webpackCompactConfig = getWebpackConfig(false); };
const webpackConfig = injectLessVariables(getWebpackConfig(false), legacyEntryVars);
const webpackDarkConfig = injectLessVariables(getWebpackConfig(false), legacyEntryVars);
const webpackCompactConfig = injectLessVariables(getWebpackConfig(false), legacyEntryVars);
const webpackVariableConfig = injectLessVariables(getWebpackConfig(false), {
'root-entry-name': 'variable',
});
webpackConfig.forEach(config => { webpackConfig.forEach(config => {
injectWarningCondition(config); injectWarningCondition(config);
@ -124,6 +155,12 @@ if (process.env.RUN_ENV === 'PRODUCTION') {
processWebpackThemeConfig(webpackDarkConfig, 'dark', darkVars); processWebpackThemeConfig(webpackDarkConfig, 'dark', darkVars);
processWebpackThemeConfig(webpackCompactConfig, 'compact', compactVars); processWebpackThemeConfig(webpackCompactConfig, 'compact', compactVars);
processWebpackThemeConfig(webpackVariableConfig, 'variable', {});
} }
module.exports = [...webpackConfig, ...webpackDarkConfig, ...webpackCompactConfig]; module.exports = [
...webpackConfig,
...webpackDarkConfig,
...webpackCompactConfig,
...webpackVariableConfig,
];