fix: DatePicker itemHeight calculation (#47874)

* chore: init

* chore: clean up

* chore: clean up
This commit is contained in:
二货爱吃白萝卜 2024-03-15 10:54:54 +08:00 committed by GitHub
parent 1491b0c624
commit 7884eba28e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 252 additions and 142 deletions

View File

@ -1,51 +1,66 @@
import React from 'react';
import type { DatePickerProps } from 'antd';
import { ConfigProvider, DatePicker, Space, TimePicker } from 'antd';
import { ConfigProvider, DatePicker, Divider, Flex, Space, TimePicker } from 'antd';
import dayjs from 'dayjs';
/** Test usage. Do not use in your production. */
const { RangePicker } = DatePicker;
const onChange: DatePickerProps['onChange'] = (date, dateString) => {
console.log(date, dateString);
};
const App: React.FC = () => (
<ConfigProvider
theme={{
components: {
DatePicker: {
presetsWidth: 160,
zIndexPopup: 888,
cellHoverWithRangeBg: '#f0f0f0',
cellActiveWithRangeBg: '#e6bbff',
cellRangeBorderColor: 'green',
timeColumnWidth: 80,
timeColumnHeight: 250,
timeCellHeight: 30,
cellWidth: 64,
cellHeight: 40,
textHeight: 45,
withoutTimeCellHeight: 70,
<>
<ConfigProvider
theme={{
components: {
DatePicker: {
presetsWidth: 160,
zIndexPopup: 888,
cellHoverWithRangeBg: '#f0f0f0',
cellActiveWithRangeBg: '#e6bbff',
cellRangeBorderColor: 'green',
timeColumnWidth: 80,
timeColumnHeight: 250,
timeCellHeight: 30,
cellWidth: 64,
cellHeight: 40,
textHeight: 45,
withoutTimeCellHeight: 70,
},
},
},
}}
>
<Space direction="vertical">
<DatePicker
presets={[
{ label: 'Yesterday', value: dayjs().add(-1, 'd') },
{ label: 'Last Week', value: dayjs().add(-7, 'd') },
{ label: 'Last Month', value: dayjs().add(-1, 'month') },
]}
onChange={onChange}
/>
<RangePicker />
<TimePicker onChange={onChange} />
<DatePicker onChange={onChange} picker="month" />
</Space>
</ConfigProvider>
}}
>
<Space direction="vertical">
<DatePicker
presets={[
{ label: 'Yesterday', value: dayjs().add(-1, 'd') },
{ label: 'Last Week', value: dayjs().add(-7, 'd') },
{ label: 'Last Month', value: dayjs().add(-1, 'month') },
]}
/>
<RangePicker />
<TimePicker />
<DatePicker picker="month" />
</Space>
</ConfigProvider>
<Divider />
<ConfigProvider
theme={{
components: {
DatePicker: {
controlHeightSM: 32,
controlHeight: 40,
},
},
}}
>
<Flex vertical gap={8}>
<DatePicker multiple size="small" />
<DatePicker multiple />
<DatePicker multiple size="large" />
</Flex>
</ConfigProvider>
</>
);
export default App;

View File

@ -1,35 +1,30 @@
import type { CSSInterpolation } from '@ant-design/cssinjs';
import { unit } from '@ant-design/cssinjs';
import { FIXED_ITEM_MARGIN, genSelectionStyle } from '../../select/style/multiple';
import { genOverflowStyle, getMultipleSelectorUnit } from '../../select/style/multiple';
import { mergeToken, type GenerateStyle } from '../../theme/internal';
import type { PickerToken } from './token';
const genSize = (token: PickerToken, suffix?: string): CSSInterpolation => {
const { componentCls, selectHeight, fontHeight, lineWidth, controlHeight, calc } = token;
const { componentCls, controlHeight } = token;
const suffixCls = suffix ? `${componentCls}-${suffix}` : '';
const height = token.calc(fontHeight).add(2).equal();
const restHeight = () => calc(selectHeight).sub(height).sub(calc(lineWidth).mul(2));
const paddingBase = token.max(restHeight().div(2).equal(), 0);
const paddingTop = token.max(token.calc(paddingBase).sub(FIXED_ITEM_MARGIN).equal(), 0);
const paddingBottom = token.max(
restHeight()
.sub(paddingTop)
.sub(FIXED_ITEM_MARGIN * 2)
.equal(),
0,
);
const multipleSelectorUnit = getMultipleSelectorUnit(token);
return [
genSelectionStyle(token, suffix),
// genSelectionStyle(token, suffix),
{
[`${componentCls}-multiple${suffixCls}`]: {
paddingTop,
paddingBottom,
paddingInlineStart: paddingBase,
paddingBlock: multipleSelectorUnit.containerPadding,
paddingInlineStart: multipleSelectorUnit.basePadding,
minHeight: controlHeight,
// ======================== Selections ========================
[`${componentCls}-selection-item`]: {
height: multipleSelectorUnit.itemHeight,
lineHeight: unit(multipleSelectorUnit.itemLineHeight),
},
},
},
];
@ -41,7 +36,7 @@ const genPickerMultipleStyle: GenerateStyle<PickerToken> = (token) => {
const smallToken = mergeToken<PickerToken>(token, {
fontHeight: token.fontSize,
selectHeight: token.controlHeightSM,
multipleSelectItemHeight: token.controlHeightXS,
multipleSelectItemHeight: token.multipleItemHeightSM,
borderRadius: token.borderRadiusSM,
borderRadiusSM: token.borderRadiusXS,
controlHeight: token.controlHeightSM,
@ -66,7 +61,6 @@ const genPickerMultipleStyle: GenerateStyle<PickerToken> = (token) => {
genSize(largeToken, 'large'),
// ====================== Selection ======================
genSelectionStyle(token),
{
[`${componentCls}${componentCls}-multiple`]: {
width: '100%',
@ -81,6 +75,9 @@ const genPickerMultipleStyle: GenerateStyle<PickerToken> = (token) => {
},
},
// ===================== Overflow ====================
...genOverflowStyle(token),
// ====================== Input ======================
// Input is `readonly`, which is used for a11y only
[`${componentCls}-multiple-input`]: {

View File

@ -139,7 +139,9 @@ export const initPickerPanelToken = (token: TokenWithCommonCls<GlobalToken>): Pi
};
export const initPanelComponentToken = (token: GlobalToken): PanelComponentToken => {
const { colorBgContainerDisabled, controlHeightSM, controlHeightLG } = token;
const { colorBgContainerDisabled, controlHeight, controlHeightSM, controlHeightLG, paddingXXS } =
token;
return {
cellHoverBg: token.controlItemBgHover,
cellActiveWithRangeBg: token.controlItemBgActive,
@ -155,8 +157,9 @@ export const initPanelComponentToken = (token: GlobalToken): PanelComponentToken
withoutTimeCellHeight: controlHeightLG * 1.65,
multipleItemBg: token.colorFillSecondary,
multipleItemBorderColor: 'transparent',
multipleItemHeight: controlHeightSM,
multipleItemHeightLG: token.controlHeight,
multipleItemHeight: controlHeight - paddingXXS * 2,
multipleItemHeightSM: controlHeightSM - paddingXXS * 2,
multipleItemHeightLG: controlHeightLG - paddingXXS * 2,
multipleSelectorBgDisabled: colorBgContainerDisabled,
multipleItemColorDisabled: token.colorTextDisabled,
multipleItemBorderColorDisabled: 'transparent',

View File

@ -20,6 +20,44 @@ type SelectItemToken = Pick<
| 'inputPaddingHorizontalBase'
>;
/**
* Get multiple selector needed style. The calculation:
*
* ContainerPadding = BasePadding - ItemMargin
*
* Border:
* ContainerPadding:
*
* Item Margin:
*
* Item(multipleItemHeight): BasePadding Item Overflow Container(ControlHeight)
*
* Item Margin:
*
* ContainerPadding:
* Border:
*/
export const getMultipleSelectorUnit = (
token: Pick<
SelectToken,
'max' | 'calc' | 'multipleSelectItemHeight' | 'paddingXXS' | 'lineWidth'
>,
) => {
const { multipleSelectItemHeight, paddingXXS, lineWidth } = token;
const basePadding = token.max(token.calc(paddingXXS).sub(lineWidth).equal(), 0);
const containerPadding = token.max(token.calc(basePadding).sub(FIXED_ITEM_MARGIN).equal(), 0);
return {
basePadding,
containerPadding,
itemHeight: unit(multipleSelectItemHeight),
itemLineHeight: unit(
token.calc(multipleSelectItemHeight).sub(token.calc(token.lineWidth).mul(2)).equal(),
),
};
};
const getSelectItemStyle = (token: SelectItemToken): number | string => {
const { multipleSelectItemHeight, selectHeight, lineWidth } = token;
const selectItemDist = token
@ -31,11 +69,118 @@ const getSelectItemStyle = (token: SelectItemToken): number | string => {
return selectItemDist;
};
export const genSelectionStyle = (
/**
* Get the `rc-overflow` needed style.
* It's a share style which means not affected by `size`.
*/
export const genOverflowStyle = (
token: Pick<
SelectToken,
| 'calc'
| 'componentCls'
| 'iconCls'
| 'borderRadiusSM'
| 'motionDurationSlow'
| 'paddingXS'
| 'multipleItemColorDisabled'
| 'multipleItemBorderColorDisabled'
| 'colorIcon'
| 'colorIconHover'
>,
): CSSObject => {
const {
componentCls,
iconCls,
borderRadiusSM,
motionDurationSlow,
paddingXS,
multipleItemColorDisabled,
multipleItemBorderColorDisabled,
colorIcon,
colorIconHover,
} = token;
const selectOverflowPrefixCls = `${componentCls}-selection-overflow`;
return {
/**
* Do not merge `height` & `line-height` under style with `selection` & `search`, since chrome
* may update to redesign with its align logic.
*/
// =========================== Overflow ===========================
[selectOverflowPrefixCls]: {
position: 'relative',
display: 'flex',
flex: 'auto',
flexWrap: 'wrap',
maxWidth: '100%',
'&-item': {
flex: 'none',
alignSelf: 'center',
maxWidth: '100%',
display: 'inline-flex',
},
// ======================== Selections ==========================
[`${componentCls}-selection-item`]: {
display: 'flex',
alignSelf: 'center',
flex: 'none',
boxSizing: 'border-box',
maxWidth: '100%',
marginBlock: FIXED_ITEM_MARGIN,
borderRadius: borderRadiusSM,
cursor: 'default',
transition: `font-size ${motionDurationSlow}, line-height ${motionDurationSlow}, height ${motionDurationSlow}`,
marginInlineEnd: token.calc(FIXED_ITEM_MARGIN).mul(2).equal(),
paddingInlineStart: paddingXS,
paddingInlineEnd: token.calc(paddingXS).div(2).equal(),
[`${componentCls}-disabled&`]: {
color: multipleItemColorDisabled,
borderColor: multipleItemBorderColorDisabled,
cursor: 'not-allowed',
},
// It's ok not to do this, but 24px makes bottom narrow in view should adjust
'&-content': {
display: 'inline-block',
marginInlineEnd: token.calc(paddingXS).div(2).equal(),
overflow: 'hidden',
whiteSpace: 'pre', // fix whitespace wrapping. custom tags display all whitespace within.
textOverflow: 'ellipsis',
},
'&-remove': {
...resetIcon(),
display: 'inline-flex',
alignItems: 'center',
color: colorIcon,
fontWeight: 'bold',
fontSize: 10,
lineHeight: 'inherit',
cursor: 'pointer',
[`> ${iconCls}`]: {
verticalAlign: '-0.2em',
},
'&:hover': {
color: colorIconHover,
},
},
},
},
};
};
const genSelectionStyle = (
token: TokenWithCommonCls<AliasToken> & SelectItemToken,
suffix?: string,
): CSSObject => {
const { componentCls, iconCls } = token;
const { componentCls } = token;
const selectOverflowPrefixCls = `${componentCls}-selection-overflow`;
@ -44,27 +189,12 @@ export const genSelectionStyle = (
const suffixCls = suffix ? `${componentCls}-${suffix}` : '';
const multipleSelectorUnit = getMultipleSelectorUnit(token);
return {
[`${componentCls}-multiple${suffixCls}`]: {
/**
* Do not merge `height` & `line-height` under style with `selection` & `search`, since chrome
* may update to redesign with its align logic.
*/
// =========================== Overflow ===========================
[selectOverflowPrefixCls]: {
position: 'relative',
display: 'flex',
flex: 'auto',
flexWrap: 'wrap',
maxWidth: '100%',
'&-item': {
flex: 'none',
alignSelf: 'center',
maxWidth: '100%',
display: 'inline-flex',
},
},
// ========================= Overflow =========================
...genOverflowStyle(token),
// ========================= Selector =========================
[`${componentCls}-selector`]: {
@ -73,8 +203,8 @@ export const genSelectionStyle = (
alignItems: 'center',
height: '100%',
// Multiple is little different that horizontal is follow the vertical
paddingInline: token.calc(FIXED_ITEM_MARGIN).mul(2).equal(),
paddingBlock: token.calc(selectItemDist).sub(FIXED_ITEM_MARGIN).equal(),
paddingInline: multipleSelectorUnit.basePadding,
paddingBlock: multipleSelectorUnit.containerPadding,
borderRadius: token.borderRadius,
[`${componentCls}-disabled&`]: {
@ -94,58 +224,8 @@ export const genSelectionStyle = (
// ======================== Selections ========================
[`${componentCls}-selection-item`]: {
display: 'flex',
alignSelf: 'center',
flex: 'none',
boxSizing: 'border-box',
maxWidth: '100%',
height: selectItemHeight,
marginTop: FIXED_ITEM_MARGIN,
marginBottom: FIXED_ITEM_MARGIN,
lineHeight: unit(
token.calc(selectItemHeight).sub(token.calc(token.lineWidth).mul(2)).equal(),
),
borderRadius: token.borderRadiusSM,
cursor: 'default',
transition: `font-size ${token.motionDurationSlow}, line-height ${token.motionDurationSlow}, height ${token.motionDurationSlow}`,
marginInlineEnd: token.calc(FIXED_ITEM_MARGIN).mul(2).equal(),
paddingInlineStart: token.paddingXS,
paddingInlineEnd: token.calc(token.paddingXS).div(2).equal(),
[`${componentCls}-disabled&`]: {
color: token.multipleItemColorDisabled,
borderColor: token.multipleItemBorderColorDisabled,
cursor: 'not-allowed',
},
// It's ok not to do this, but 24px makes bottom narrow in view should adjust
'&-content': {
display: 'inline-block',
marginInlineEnd: token.calc(token.paddingXS).div(2).equal(),
overflow: 'hidden',
whiteSpace: 'pre', // fix whitespace wrapping. custom tags display all whitespace within.
textOverflow: 'ellipsis',
},
'&-remove': {
...resetIcon(),
display: 'inline-flex',
alignItems: 'center',
color: token.colorIcon,
fontWeight: 'bold',
fontSize: 10,
lineHeight: 'inherit',
cursor: 'pointer',
[`> ${iconCls}`]: {
verticalAlign: '-0.2em',
},
'&:hover': {
color: token.colorIconHover,
},
},
height: multipleSelectorUnit.itemHeight,
lineHeight: unit(multipleSelectorUnit.itemLineHeight),
},
// ========================== Input ==========================
@ -241,7 +321,7 @@ const genMultipleStyle = (token: SelectToken): CSSInterpolation => {
const smallToken = mergeToken<SelectToken>(token, {
selectHeight: token.controlHeightSM,
multipleSelectItemHeight: token.controlHeightXS,
multipleSelectItemHeight: token.multipleItemHeightSM,
borderRadius: token.borderRadiusSM,
borderRadiusSM: token.borderRadiusXS,
});

View File

@ -17,6 +17,11 @@ export interface MultipleSelectorToken {
* @descEN Height of multiple tag
*/
multipleItemHeight: number;
/**
* @desc
* @descEN Height of multiple tag with small size
*/
multipleItemHeightSM: number;
/**
* @desc
* @descEN Height of multiple tag with large size
@ -121,7 +126,13 @@ export const prepareComponentToken: GetDefaultToken<'Select'> = (token) => {
const {
fontSize,
lineHeight,
controlHeight,
controlHeightSM,
controlHeightLG,
paddingXXS,
controlPaddingHorizontal,
zIndexPopupBase,
colorText,
@ -130,12 +141,15 @@ export const prepareComponentToken: GetDefaultToken<'Select'> = (token) => {
controlItemBgHover,
colorBgContainer,
colorFillSecondary,
controlHeightLG,
controlHeightSM,
colorBgContainerDisabled,
colorTextDisabled,
} = token;
const multipleItemHeight = controlHeight - paddingXXS * 2;
const multipleItemHeightSM = controlHeightSM - paddingXXS * 2;
const multipleItemHeightLG = controlHeightLG - paddingXXS * 2;
return {
zIndexPopup: zIndexPopupBase + 50,
optionSelectedColor: colorText,
@ -151,8 +165,9 @@ export const prepareComponentToken: GetDefaultToken<'Select'> = (token) => {
singleItemHeightLG: controlHeightLG,
multipleItemBg: colorFillSecondary,
multipleItemBorderColor: 'transparent',
multipleItemHeight: controlHeightSM,
multipleItemHeightLG: controlHeight,
multipleItemHeight,
multipleItemHeightSM,
multipleItemHeightLG,
multipleSelectorBgDisabled: colorBgContainerDisabled,
multipleItemColorDisabled: colorTextDisabled,
multipleItemBorderColorDisabled: 'transparent',