Merge remote-tracking branch 'origin/master' into generate-form
Some checks failed
Publish Any Commit / build (push) Has been cancelled
✅ test / lint (push) Has been cancelled
✅ test / test-react-legacy (16, 1/2) (push) Has been cancelled
✅ test / test-react-legacy (16, 2/2) (push) Has been cancelled
✅ test / test-react-legacy (17, 1/2) (push) Has been cancelled
✅ test / test-react-legacy (17, 2/2) (push) Has been cancelled
✅ test / test-node (push) Has been cancelled
✅ test / test-react-latest (dom, 1/2) (push) Has been cancelled
✅ test / test-react-latest (dom, 2/2) (push) Has been cancelled
✅ test / build (push) Has been cancelled
✅ test / test lib/es module (es, 1/2) (push) Has been cancelled
✅ test / test lib/es module (es, 2/2) (push) Has been cancelled
✅ test / test lib/es module (lib, 1/2) (push) Has been cancelled
✅ test / test lib/es module (lib, 2/2) (push) Has been cancelled
✅ test / test-react-latest-dist (dist, 1/2) (push) Has been cancelled
✅ test / test-react-latest-dist (dist, 2/2) (push) Has been cancelled
✅ test / test-react-latest-dist (dist-min, 1/2) (push) Has been cancelled
✅ test / test-react-latest-dist (dist-min, 2/2) (push) Has been cancelled
✅ test / test-coverage (push) Has been cancelled

This commit is contained in:
crazyair 2024-11-21 18:11:41 +08:00
commit 5d607146d3
43 changed files with 5438 additions and 1083 deletions

View File

@ -8,9 +8,6 @@ import SiteContext from '../SiteContext';
import ContributorAvatar from './ContributorAvatar';
const useStyle = createStyles(({ token, css }) => ({
contributorsList: css`
margin-top: 120px !important;
`,
listMobile: css`
margin: 1em 0 !important;
`,
@ -50,7 +47,7 @@ const Contributors: React.FC<ContributorsProps> = ({ filename }) => {
}
return (
<div className={classNames(styles.contributorsList, { [styles.listMobile]: isMobile })}>
<div className={classNames({ [styles.listMobile]: isMobile })}>
<div className={styles.title}>{formatMessage({ id: 'app.content.contributors' })}</div>
<ContributorsList
cache

View File

@ -94,9 +94,11 @@ const Content: React.FC<React.PropsWithChildren> = ({ children }) => {
juejinLink={meta.frontmatter.juejin_url}
/>
</InViewSuspense>
<InViewSuspense fallback={<div style={{ height: 50, marginTop: 120 }} />}>
<Contributors filename={meta.frontmatter.filename} />
</InViewSuspense>
<div style={{ marginTop: 120 }}>
<InViewSuspense fallback={<div style={{ height: 50 }} />}>
<Contributors filename={meta.frontmatter.filename} />
</InViewSuspense>
</div>
</article>
<InViewSuspense fallback={null}>
<PrevAndNext rtl={isRTL} />

View File

@ -77,15 +77,26 @@ jobs:
const mergedStatus = diffPassed ? 'success' : hasDiffFailed ? 'failure' : 'pending';
console.log('Status:', mergedStatus);
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: prHeadSha,
state: mergedStatus,
context: 'Visual Regression Diff Wait Approve',
description: diffPassed ? 'Visual diff is acceptable' : 'Visual diff is not pass',
const { data: currentStatuses } = await github.rest.repos.listCommitStatusesForRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: prHeadSha,
});
const currentStatus = currentStatuses.find(status => status.context === 'Visual Regression Diff Wait Approve');
if (currentStatus && currentStatus.state === mergedStatus) {
console.log('Status has not changed, no need to update:', currentStatus.state);
} else {
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: prHeadSha,
state: mergedStatus,
context: 'Visual Regression Diff Wait Approve',
description: diffPassed ? 'Visual diff is acceptable' : 'Visual diff is not pass',
});
}
if (hasDiffSuccess || (hasDiffFailed && hasMemberApprove)) {
return 'success';
} else if (hasDiffFailed) {

View File

@ -62,5 +62,17 @@
"https://github.com/ant-design/ant-design/issues/50960",
"https://github.com/ant-design/ant-design/issues/50969"
],
"5.22.0": ["https://github.com/ant-design/ant-design/issues/51601"]
"5.21.6": [
"https://github.com/ant-design/ant-design/issues/51420",
"https://github.com/ant-design/ant-design/issues/51430"
],
"5.22.0": [
"https://github.com/ant-design/ant-design/issues/51601",
"https://github.com/ant-design/ant-design/issues/51420",
"https://github.com/ant-design/ant-design/issues/51430"
],
"5.22.1": [
"https://github.com/ant-design/ant-design/issues/51420",
"https://github.com/ant-design/ant-design/issues/51430"
]
}

View File

@ -15,6 +15,23 @@ tag: vVERSION
---
## 5.22.2
`2024-11-21`
- 🐞 Fix Input.OTP focus from advancing when previous input is empty. [#51664](https://github.com/ant-design/ant-design/pull/51664) [@thecodesalim](https://github.com/thecodesalim)
- 🐞 Adjust Modal function call not to scroll the confirm button when it get auto focused. [#51647](https://github.com/ant-design/ant-design/pull/51647) [@zombieJ](https://github.com/zombieJ)
- 🐞 Fix Form `rules` with same error content will cause React render warning. [#51636](https://github.com/ant-design/ant-design/pull/51636) [@zombieJ](https://github.com/zombieJ)
- 🐞 Refactor Button `focus` logic trigger with `useEffect` to resolve some async load case not get `autoFocus`. [#51624](https://github.com/ant-design/ant-design/pull/51624) [@zombieJ](https://github.com/zombieJ)
- 🐞 Fix Button custom icon not center-aligned. [#51652](https://github.com/ant-design/ant-design/pull/51652) [@afc163](https://github.com/afc163)
- 🐞 Fix Table `getCheckboxProps` event handlers being overridden by internal selection logic. [#51661](https://github.com/ant-design/ant-design/pull/51661) [@Zyf665](https://github.com/Zyf665)
- 🐞 Fix Tree that `onCheck` and `onSelect` were not properly triggered. [#51448](https://github.com/ant-design/ant-design/pull/51448) [@Wxh16144](https://github.com/Wxh16144)
- 🐞 Fix vertical alignment of clear icon in Input component. [#51700](https://github.com/ant-design/ant-design/pull/51700) [@jynxio](https://github.com/jynxio)
- 🐞 Fix Select with `prefix` style issue with color, line break, status error. [#51694](https://github.com/ant-design/ant-design/pull/51694) [@zombieJ](https://github.com/zombieJ)
- 🌐 Localization
- 🇷🇺 Add support for Russian translation. [#51619](https://github.com/ant-design/ant-design/pull/51619) [@avvakumovid](https://github.com/avvakumovid)
- 🇮🇹 Add support for Italian translation in TimePicker. [#51685](https://github.com/ant-design/ant-design/pull/51685) [@LorenzoCardinali](https://github.com/LorenzoCardinali)
## 5.22.1
`2024-11-13`

View File

@ -15,6 +15,23 @@ tag: vVERSION
---
## 5.22.2
`2024-11-21`
- 🐞 修复 Input.OTP 组件在有非法输入时仍会切换到下一个输入框的问题。[#51664](https://github.com/ant-design/ant-design/pull/51664) [@thecodesalim](https://github.com/thecodesalim)
- 🐞 调整 Modal 确认函数,使其在弹出后聚焦确认按钮时不要滚动窗体。[#51647](https://github.com/ant-design/ant-design/pull/51647) [@zombieJ](https://github.com/zombieJ)
- 🐞 修复 Form `rules` 生成多条相同错误时会报 React 渲染错误的问题。[#51636](https://github.com/ant-design/ant-design/pull/51636) [@zombieJ](https://github.com/zombieJ)
- 🐞 调整 Button 使用 `useEffect` 来触发 `autoFocus` 逻辑,以解决一些异步渲染场景下 Button 无法自动聚焦的问题。[#51624](https://github.com/ant-design/ant-design/pull/51624) [@zombieJ](https://github.com/zombieJ)
- 🐞 修复 Button 中使用自定义三方图标库时图标未居中的问题。[#51652](https://github.com/ant-design/ant-design/pull/51652) [@afc163](https://github.com/afc163)
- 🐞 修复 Table 组件 `getCheckboxProps` 中的事件处理器被内部选择逻辑覆盖的问题。[#51661](https://github.com/ant-design/ant-design/pull/51661) [@Zyf665](https://github.com/Zyf665)
- 🐞 修复 Tree 组件的 `onCheck``onSelect` 事件没有被正确触发的问题。[#51448](https://github.com/ant-design/ant-design/pull/51448) [@Wxh16144](https://github.com/Wxh16144)
- 🐞 修复 Input 组件的清除按钮未能垂直居中的问题。[#51700](https://github.com/ant-design/ant-design/pull/51700) [@jynxio](https://github.com/jynxio)
- 🐞 修复 Select 组件的 `prefix` 组合导致的颜色、折行、状态等一系列样式问题。[#51694](https://github.com/ant-design/ant-design/pull/51694) [@zombieJ](https://github.com/zombieJ)
- 🌐 本地化
- 🇷🇺 添加了俄语翻译支持。[#51619](https://github.com/ant-design/ant-design/pull/51619) [@avvakumovid](https://github.com/avvakumovid)
- 🇮🇹 为 TimePicker 添加了意大利语翻译。[#51685](https://github.com/ant-design/ant-design/pull/51685) [@LorenzoCardinali](https://github.com/LorenzoCardinali)
## 5.22.1
`2024-11-13`

View File

@ -22,7 +22,7 @@ import { isTwoCNChar, isUnBorderedButtonVariant, spaceChildren } from './buttonH
import IconWrapper from './IconWrapper';
import LoadingIcon from './LoadingIcon';
import useStyle from './style';
import CompactCmp from './style/compactCmp';
import Compact from './style/compact';
export type LegacyButtonType = ButtonType | 'danger';
@ -337,8 +337,7 @@ const InternalCompoundedButton = React.forwardRef<
>
{iconNode}
{kids}
{/* Styles: compact */}
{!!compactItemClassnames && <CompactCmp key="compact" prefixCls={prefixCls} />}
{compactItemClassnames && <Compact prefixCls={prefixCls} />}
</button>
);

View File

@ -0,0 +1,50 @@
// Style as inline component
import type { CSSObject } from '@ant-design/cssinjs';
import { genCompactItemStyle } from '../../style/compact-item';
import { genCompactItemVerticalStyle } from '../../style/compact-item-vertical';
import type { GenerateStyle } from '../../theme/internal';
import { genSubStyleComponent } from '../../theme/internal';
import type { ButtonToken } from './token';
import { prepareComponentToken, prepareToken } from './token';
const genButtonCompactStyle: GenerateStyle<ButtonToken> = (token) => {
const { componentCls, colorPrimaryHover, lineWidth, calc } = token;
const insetOffset = calc(lineWidth).mul(-1).equal();
const getCompactBorderStyle = (vertical?: boolean) =>
({
[`${componentCls}-compact${vertical ? '-vertical' : ''}-item${componentCls}-primary:not([disabled])`]:
{
'& + &::before': {
position: 'absolute',
top: vertical ? insetOffset : 0,
insetInlineStart: vertical ? 0 : insetOffset,
backgroundColor: colorPrimaryHover,
content: '""',
width: vertical ? '100%' : lineWidth,
height: vertical ? lineWidth : '100%',
},
},
}) as CSSObject;
// Special styles for Primary Button
return {
...getCompactBorderStyle(),
...getCompactBorderStyle(true),
};
};
// ============================== Export ==============================
export default genSubStyleComponent(
['Button', 'compact'],
(token) => {
const buttonToken = prepareToken(token);
return [
// Space Compact
genCompactItemStyle(buttonToken),
genCompactItemVerticalStyle(buttonToken),
genButtonCompactStyle(buttonToken),
];
},
prepareComponentToken,
);

View File

@ -1,72 +0,0 @@
// Style as inline component
import { unit } from '@ant-design/cssinjs';
import { genCompactItemStyle } from '../../style/compact-item';
import { genCompactItemVerticalStyle } from '../../style/compact-item-vertical';
import type { GenerateStyle } from '../../theme/internal';
import { genSubStyleComponent } from '../../theme/internal';
import type { ButtonToken } from './token';
import { prepareComponentToken, prepareToken } from './token';
const genButtonCompactStyle: GenerateStyle<ButtonToken> = (token) => {
const { componentCls, calc } = token;
return {
[componentCls]: {
// Special styles for Primary Button
[`&-compact-item${componentCls}-primary`]: {
[`&:not([disabled]) + ${componentCls}-compact-item${componentCls}-primary:not([disabled])`]:
{
position: 'relative',
'&:before': {
position: 'absolute',
top: calc(token.lineWidth).mul(-1).equal(),
insetInlineStart: calc(token.lineWidth).mul(-1).equal(),
display: 'inline-block',
width: token.lineWidth,
height: `calc(100% + ${unit(token.lineWidth)} * 2)`,
backgroundColor: token.colorPrimaryHover,
content: '""',
},
},
},
// Special styles for Primary Button
'&-compact-vertical-item': {
[`&${componentCls}-primary`]: {
[`&:not([disabled]) + ${componentCls}-compact-vertical-item${componentCls}-primary:not([disabled])`]:
{
position: 'relative',
'&:before': {
position: 'absolute',
top: calc(token.lineWidth).mul(-1).equal(),
insetInlineStart: calc(token.lineWidth).mul(-1).equal(),
display: 'inline-block',
width: `calc(100% + ${unit(token.lineWidth)} * 2)`,
height: token.lineWidth,
backgroundColor: token.colorPrimaryHover,
content: '""',
},
},
},
},
},
};
};
// ============================== Export ==============================
export default genSubStyleComponent(
['Button', 'compact'],
(token) => {
const buttonToken = prepareToken(token);
return [
// Space Compact
genCompactItemStyle(buttonToken),
genCompactItemVerticalStyle(buttonToken),
genButtonCompactStyle(buttonToken),
];
},
prepareComponentToken,
);

View File

@ -38,12 +38,8 @@ const genSharedButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token): CSS
pointerEvents: 'none',
},
'> span': {
display: 'inline-block',
},
[`${componentCls}-icon`]: {
lineHeight: 1,
[`> span, ${componentCls}-icon`]: {
display: 'inline-flex',
},
'> a': {
@ -529,10 +525,11 @@ const genButtonStyle = (token: ButtonToken, prefixCls = ''): CSSInterpolation =>
buttonPaddingHorizontal,
iconCls,
buttonPaddingVertical,
motionDurationSlow,
motionEaseInOut,
buttonIconOnlyFontSize,
opacityLoading,
} = token;
const iconOnlyCls = `${componentCls}-icon-only`;
return [
{
[prefixCls]: {
@ -542,7 +539,7 @@ const genButtonStyle = (token: ButtonToken, prefixCls = ''): CSSInterpolation =>
padding: `${unit(buttonPaddingVertical!)} ${unit(buttonPaddingHorizontal!)}`,
borderRadius,
[`&${iconOnlyCls}`]: {
[`&${componentCls}-icon-only`]: {
width: controlHeight,
paddingInline: 0,
@ -556,22 +553,21 @@ const genButtonStyle = (token: ButtonToken, prefixCls = ''): CSSInterpolation =>
},
[iconCls]: {
fontSize: token.buttonIconOnlyFontSize,
fontSize: buttonIconOnlyFontSize,
},
},
// Loading
[`&${componentCls}-loading`]: {
opacity: token.opacityLoading,
opacity: opacityLoading,
cursor: 'default',
},
[`${componentCls}-loading-icon`]: {
transition: `width ${token.motionDurationSlow} ${token.motionEaseInOut}, opacity ${token.motionDurationSlow} ${token.motionEaseInOut}`,
transition: `width ${motionDurationSlow} ${motionEaseInOut}, opacity ${motionDurationSlow} ${motionEaseInOut}`,
},
},
},
// Shape - patch prefixCls again to override solid border radius style
{
[`${componentCls}${componentCls}-circle${prefixCls}`]: genCircleButtonStyle(token),

View File

@ -31,7 +31,7 @@ demo:
<code src="./demo/search.tsx">Search</code>
<code src="./demo/lazy.tsx">Load Options Lazily</code>
<code src="./demo/fields-name.tsx">Custom Field Names</code>
<code src="./demo/suffix.tsx" debug>Prefix and Suffix</code>
<code src="./demo/suffix.tsx" version="5.22.0">Prefix and Suffix</code>
<code src="./demo/custom-dropdown.tsx">Custom dropdown</code>
<code src="./demo/placement.tsx">Placement</code>
<code src="./demo/status.tsx">Status</code>
@ -67,7 +67,7 @@ Common props ref[Common props](/docs/react/common-props)
| maxTagCount | Max tag count to show. `responsive` will cost render performance | number \| `responsive` | - | 4.17.0 |
| maxTagPlaceholder | Placeholder for not showing tags | ReactNode \| function(omittedValues) | - | 4.17.0 |
| maxTagTextLength | Max tag text length to show | number | - | 4.17.0 |
| notFoundContent | Specify content to show when no result matches | string | `Not Found` | |
| notFoundContent | Specify content to show when no result matches | ReactNode | `Not Found` | |
| open | Set visible of cascader popup | boolean | - | 4.17.0 |
| options | The data options of cascade | [Option](#option)\[] | - | |
| placeholder | The input placeholder | string | - | |

View File

@ -32,7 +32,7 @@ demo:
<code src="./demo/search.tsx">搜索</code>
<code src="./demo/lazy.tsx">动态加载选项</code>
<code src="./demo/fields-name.tsx">自定义字段名</code>
<code src="./demo/suffix.tsx" debug>前后缀</code>
<code src="./demo/suffix.tsx" version="5.22.0">前后缀</code>
<code src="./demo/custom-dropdown.tsx">扩展菜单</code>
<code src="./demo/placement.tsx">弹出位置</code>
<code src="./demo/status.tsx">自定义状态</code>
@ -68,7 +68,7 @@ demo:
| maxTagCount | 最多显示多少个 tag响应式模式会对性能产生损耗 | number \| `responsive` | - | 4.17.0 |
| maxTagPlaceholder | 隐藏 tag 时显示的内容 | ReactNode \| function(omittedValues) | - | 4.17.0 |
| maxTagTextLength | 最大显示的 tag 文本长度 | number | - | 4.17.0 |
| notFoundContent | 当下拉列表为空时显示的内容 | string | `Not Found` | |
| notFoundContent | 当下拉列表为空时显示的内容 | ReactNode | `Not Found` | |
| open | 控制浮层显隐 | boolean | - | 4.17.0 |
| options | 可选项数据源 | [Option](#option)\[] | - | |
| placeholder | 输入框占位文本 | string | - | |

View File

@ -208,7 +208,7 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
const nextCells = patchValue(index, txt);
const nextIndex = Math.min(index + txt.length, length - 1);
if (nextIndex !== index) {
if (nextIndex !== index && nextCells[index] !== undefined) {
refs.current[nextIndex]?.focus();
}

View File

@ -1,10 +1,8 @@
import * as React from 'react';
import { forwardRef } from 'react';
import classNames from 'classnames';
import type { TextAreaRef as RcTextAreaRef } from 'rc-textarea';
import type { TextAreaRef as RcTextAreaRef, TextAreaProps as RcTextAreaProps } from 'rc-textarea';
import RcTextArea from 'rc-textarea';
import type { TextAreaProps as RcTextAreaProps } from 'rc-textarea/lib/interface';
import getAllowClear from '../_util/getAllowClear';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';

View File

@ -421,6 +421,7 @@ const genAllowClearStyle = (token: InputToken): CSSObject => {
// ========================= Input =========================
[`${componentCls}-clear-icon`]: {
margin: 0,
lineHeight: 0,
color: token.colorTextQuaternary,
fontSize: token.fontSizeIcon,
verticalAlign: -1,

View File

@ -25,17 +25,17 @@ const ContainerHeight = 400;
const App: React.FC = () => {
const [data, setData] = useState<UserItem[]>([]);
const appendData = () => {
const appendData = (showMessage = true) => {
fetch(fakeDataUrl)
.then((res) => res.json())
.then((body) => {
setData(data.concat(body.results));
message.success(`${body.results.length} more items loaded!`);
showMessage && message.success(`${body.results.length} more items loaded!`);
});
};
useEffect(() => {
appendData();
appendData(false);
}, []);
const onScroll = (e: React.UIEvent<HTMLElement, UIEvent>) => {

View File

@ -34,7 +34,7 @@ export const sortGradient = (gradients: StringGradients) => {
let tempArr: { key: number; value?: string }[] = [];
Object.keys(gradients).forEach((key) => {
const formattedKey = parseFloat(key.replace(/%/g, ''));
if (!isNaN(formattedKey)) {
if (!Number.isNaN(formattedKey)) {
tempArr.push({ key: formattedKey, value: gradients[key] });
}
});

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,15 @@
import React from 'react';
import { Cascader, Select, Space, TreeSelect, Typography } from 'antd';
import {
AutoComplete,
Cascader,
Flex,
Form,
Input,
Select,
Space,
TreeSelect,
Typography,
} from 'antd';
const options = [
{ value: 'long', label: <Typography>long, long, long piece of text</Typography> },
@ -8,69 +18,172 @@ const options = [
];
const App: React.FC = () => (
<Space wrap>
<Select
defaultValue="long, long, long piece of text"
style={{ width: 120 }}
allowClear
options={options}
/>
<>
<Space wrap>
<Select
defaultValue="long, long, long piece of text"
style={{ width: 120 }}
allowClear
options={options}
/>
<Select
placeholder="Select a option"
style={{ width: 120, height: 60 }}
allowClear
options={options}
/>
<Select
placeholder="Select a option"
style={{ width: 120, height: 60 }}
allowClear
options={options}
/>
<Select
defaultValue="normal"
placeholder="Select a option"
style={{ width: 120 }}
allowClear
options={options}
/>
<Select
defaultValue="normal"
placeholder="Select a option"
style={{ width: 120 }}
allowClear
options={options}
/>
<Select
defaultValue={['normal']}
mode="multiple"
placeholder="Select a option"
style={{ width: 120 }}
allowClear
options={options}
/>
<Select
defaultValue={['normal']}
mode="multiple"
placeholder="Select a option"
style={{ width: 120 }}
allowClear
options={options}
/>
<Select
mode="multiple"
placeholder="Select a option"
style={{ width: 120, height: 60 }}
allowClear
options={options}
/>
<Select
mode="multiple"
placeholder="Select a option"
style={{ width: 120, height: 60 }}
allowClear
options={options}
/>
<Cascader
placeholder="Select a option"
style={{ width: 120, height: 60 }}
allowClear
options={options}
/>
<Cascader
placeholder="Select a option"
style={{ width: 120, height: 60 }}
allowClear
options={options}
/>
<TreeSelect
showSearch
style={{ width: 120, height: 60 }}
placeholder="Please select"
allowClear
popupMatchSelectWidth={false}
treeDefaultExpandAll
treeData={[
{
value: 'parent 1',
title: 'parent 1',
children: options,
},
]}
/>
</Space>
<TreeSelect
showSearch
style={{ width: 120, height: 60 }}
placeholder="Please select"
allowClear
popupMatchSelectWidth={false}
treeDefaultExpandAll
treeData={[
{
value: 'parent 1',
title: 'parent 1',
children: options,
},
]}
/>
<Select
prefix="Hello World"
mode="multiple"
allowClear
placeholder="Select"
style={{ minWidth: 200, height: 200 }}
defaultValue={['long']}
options={options}
/>
<Select
mode="multiple"
style={{ width: 200 }}
placeholder="请选择"
maxTagCount="responsive"
prefix="城市"
options={options}
/>
<Select
style={{ width: 200 }}
placeholder="请选择"
prefix="城市"
options={options}
showSearch
allowClear
status="error"
/>
<Select
style={{ width: 100 }}
prefix="Hi"
options={options}
showSearch
allowClear
status="warning"
variant="filled"
defaultValue="Bamboo"
/>
<Select
style={{ width: 100 }}
prefix="Hi"
options={options}
showSearch
allowClear
status="error"
variant="borderless"
defaultValue="Bamboo"
/>
<Form style={{ width: 200 }} layout="vertical">
<Form.Item
label="Label"
name="bamboo"
initialValue="Bamboo"
style={{
boxShadow: '0 0 0 1px red',
}}
>
<Select options={options} allowClear placeholder="bamboo" />
</Form.Item>
<Form.Item
label="Label"
name="bamboo"
initialValue="Bamboo"
style={{
boxShadow: '0 0 0 1px red',
}}
>
<AutoComplete options={options} allowClear placeholder="bamboo" />
</Form.Item>
</Form>
</Space>
{/* Test for align */}
<Flex vertical style={{ width: 200 }}>
{/* Single */}
<Input prefix="Hi" placeholder="Input" allowClear />
<Select prefix="Hi" placeholder="Single" options={options} allowClear showSearch />
<Select
prefix="Hi"
placeholder="Single"
options={options}
allowClear
showSearch
defaultValue="Bamboo"
/>
{/* Multiple */}
<Select placeholder="Multiple" options={options} allowClear mode="multiple" />
<Select prefix="Hi" placeholder="Multiple" options={options} allowClear mode="multiple" />
<Select
prefix="Hi"
placeholder="Multiple"
options={options}
allowClear
mode="multiple"
defaultValue={['Bamboo']}
/>
<Select
placeholder="Multiple"
options={options}
allowClear
mode="multiple"
defaultValue={['Bamboo']}
/>
</Flex>
</>
);
export default App;

View File

@ -52,7 +52,7 @@ return (
<code src="./demo/label-in-value.tsx">Get value of selected item</code>
<code src="./demo/automatic-tokenization.tsx">Automatic tokenization</code>
<code src="./demo/select-users.tsx">Search and Select Users</code>
<code src="./demo/suffix.tsx" debug>Prefix and Suffix</code>
<code src="./demo/suffix.tsx" version="5.22.0">Prefix and Suffix</code>
<code src="./demo/custom-dropdown-menu.tsx">Custom dropdown</code>
<code src="./demo/hide-selected.tsx">Hide Already Selected</code>
<code src="./demo/variant.tsx" version="5.13.0">Variants</code>

View File

@ -53,7 +53,7 @@ return (
<code src="./demo/label-in-value.tsx">获得选项的文本</code>
<code src="./demo/automatic-tokenization.tsx">自动分词</code>
<code src="./demo/select-users.tsx">搜索用户</code>
<code src="./demo/suffix.tsx" debug>前后缀</code>
<code src="./demo/suffix.tsx" version="5.22.0">前后缀</code>
<code src="./demo/custom-dropdown-menu.tsx">扩展菜单</code>
<code src="./demo/hide-selected.tsx">隐藏已选择选项</code>
<code src="./demo/variant.tsx" version="5.13.0">多种形态</code>

View File

@ -146,6 +146,26 @@ const genBaseStyle: GenerateStyle<SelectToken> = (token) => {
},
},
// ========================== Wrap ===========================
[`${componentCls}-selection-wrap`]: {
display: 'flex',
width: '100%',
position: 'relative',
// https://github.com/ant-design/ant-design/issues/51669
'&:after': {
content: '"\\a0"',
width: 0,
overflow: 'hidden',
},
},
// ========================= Prefix ==========================
[`${componentCls}-prefix`]: {
flex: 'none',
marginInlineEnd: token.selectAffixPadding,
},
// ========================== Clear ==========================
[`${componentCls}-clear`]: {
position: 'absolute',

View File

@ -232,27 +232,46 @@ const genSelectionStyle = (
},
},
[`${componentCls}-selection-wrap`]: {
width: '100%',
overflow: 'hidden',
},
// ======================== Selections ========================
[`${componentCls}-selection-item`]: {
height: multipleSelectorUnit.itemHeight,
lineHeight: unit(multipleSelectorUnit.itemLineHeight),
},
// ========================== Wrap ===========================
[`${componentCls}-selection-wrap`]: {
alignSelf: 'flex-start',
'&:after': {
lineHeight: unit(selectItemHeight),
marginBlock: INTERNAL_FIXED_ITEM_MARGIN,
},
},
// ========================== Input ==========================
[`${selectOverflowPrefixCls}-item + ${selectOverflowPrefixCls}-item`]: {
[`${componentCls}-prefix`]: {
marginInlineStart: token
.calc(token.inputPaddingHorizontalBase)
.sub(multipleSelectorUnit.basePadding)
.equal(),
},
[`${selectOverflowPrefixCls}-item + ${selectOverflowPrefixCls}-item,
${componentCls}-prefix + ${componentCls}-selection-wrap
`]: {
[`${componentCls}-selection-search`]: {
marginInlineStart: 0,
},
[`${componentCls}-selection-placeholder`]: {
insetInlineStart: 0,
},
},
// https://github.com/ant-design/ant-design/issues/44754
// Same as `wrap:after`
[`${selectOverflowPrefixCls}-item-suffix`]: {
height: '100%',
minHeight: multipleSelectorUnit.itemHeight,
marginBlock: INTERNAL_FIXED_ITEM_MARGIN,
},
[`${componentCls}-selection-search`]: {
@ -291,18 +310,14 @@ const genSelectionStyle = (
[`${componentCls}-selection-placeholder`]: {
position: 'absolute',
top: '50%',
insetInlineStart: token.inputPaddingHorizontalBase,
insetInlineStart: token
.calc(token.inputPaddingHorizontalBase)
.sub(multipleSelectorUnit.basePadding)
.equal(),
insetInlineEnd: token.inputPaddingHorizontalBase,
transform: 'translateY(-50%)',
transition: `all ${token.motionDurationSlow}`,
},
[`${componentCls}-prefix`]: {
height: multipleSelectorUnit.itemHeight,
lineHeight: unit(multipleSelectorUnit.itemLineHeight),
marginInlineStart: `calc(${unit(token.inputPaddingHorizontalBase)} - ${unit(multipleSelectorUnit.basePadding)})`,
marginInlineEnd: token.selectAffixPadding,
},
},
};
};

View File

@ -6,7 +6,7 @@ import { mergeToken } from '../../theme/internal';
import type { SelectToken } from './token';
function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
const { componentCls, inputPaddingHorizontalBase, borderRadius, selectAffixPadding } = token;
const { componentCls, inputPaddingHorizontalBase, borderRadius } = token;
const selectHeightWithoutBorder = token
.calc(token.controlHeight)
@ -28,12 +28,6 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
borderRadius,
flex: '1 1 auto',
[`${componentCls}-selection-wrap`]: {
display: 'flex',
width: '100%',
position: 'relative',
},
[`${componentCls}-selection-search`]: {
position: 'absolute',
inset: 0,
@ -47,8 +41,7 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
[`
${componentCls}-selection-item,
${componentCls}-selection-placeholder,
${componentCls}-prefix
${componentCls}-selection-placeholder
`]: {
display: 'block',
padding: 0,
@ -62,10 +55,6 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
pointerEvents: 'none',
},
[`${componentCls}-prefix`]: {
marginInlineEnd: selectAffixPadding,
},
// For common baseline align
[[
'&:after',
@ -101,6 +90,7 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject {
[`${componentCls}-selector`]: {
width: '100%',
height: '100%',
alignItems: 'center',
padding: `0 ${unit(inputPaddingHorizontalBase)}`,
[`${componentCls}-selection-search-input`]: {

View File

@ -13,6 +13,7 @@ const genBaseOutlinedStyle = (
hoverBorderHover: string;
activeBorderColor: string;
activeOutlineColor: string;
color: string;
},
): CSSObject => {
const { componentCls, antCls, controlOutlineWidth } = token;
@ -33,6 +34,9 @@ const genBaseOutlinedStyle = (
boxShadow: `0 0 0 ${unit(controlOutlineWidth)} ${options.activeOutlineColor}`,
outline: 0,
},
[`${componentCls}-prefix`]: {
color: options.color,
},
},
};
};
@ -45,6 +49,7 @@ const genOutlinedStatusStyle = (
hoverBorderHover: string;
activeBorderColor: string;
activeOutlineColor: string;
color: string;
},
): CSSObject => ({
[`&${token.componentCls}-status-${options.status}`]: {
@ -59,6 +64,7 @@ const genOutlinedStyle = (token: SelectToken): CSSObject => ({
hoverBorderHover: token.hoverBorderColor,
activeBorderColor: token.activeBorderColor,
activeOutlineColor: token.activeOutlineColor,
color: token.colorText,
}),
...genOutlinedStatusStyle(token, {
@ -67,6 +73,7 @@ const genOutlinedStyle = (token: SelectToken): CSSObject => ({
hoverBorderHover: token.colorErrorHover,
activeBorderColor: token.colorError,
activeOutlineColor: token.colorErrorOutline,
color: token.colorError,
}),
...genOutlinedStatusStyle(token, {
@ -75,6 +82,7 @@ const genOutlinedStyle = (token: SelectToken): CSSObject => ({
hoverBorderHover: token.colorWarningHover,
activeBorderColor: token.colorWarning,
activeOutlineColor: token.colorWarningOutline,
color: token.colorWarning,
}),
[`&${token.componentCls}-disabled`]: {
@ -188,7 +196,7 @@ const genBorderlessStyle = (token: SelectToken): CSSObject => ({
'&-borderless': {
[`${token.componentCls}-selector`]: {
background: 'transparent',
borderColor: 'transparent',
border: `${unit(token.lineWidth)} ${token.lineType} transparent`,
},
[`&${token.componentCls}-disabled`]: {
@ -204,13 +212,13 @@ const genBorderlessStyle = (token: SelectToken): CSSObject => ({
// Status
[`&${token.componentCls}-status-error`]: {
[`${token.componentCls}-selection-item`]: {
[`${token.componentCls}-prefix, ${token.componentCls}-selection-item`]: {
color: token.colorError,
},
},
[`&${token.componentCls}-status-warning`]: {
[`${token.componentCls}-selection-item`]: {
[`${token.componentCls}-prefix, ${token.componentCls}-selection-item`]: {
color: token.colorWarning,
},
},

View File

@ -1905,4 +1905,76 @@ describe('Table.rowSelection', () => {
);
});
});
it('should trigger both custom and internal checkbox events', () => {
const onClickMock = jest.fn();
const onChangeMock = jest.fn();
const getCheckboxProps = () => ({
onClick: onClickMock,
onChange: onChangeMock,
});
const { container } = render(
<Table
rowSelection={{
type: 'checkbox',
getCheckboxProps,
}}
columns={columns}
dataSource={data}
/>,
);
const firstRowCheckbox = container.querySelector('tbody tr:first-child input[type="checkbox"]');
expect(firstRowCheckbox).toBeTruthy();
fireEvent.click(firstRowCheckbox!);
expect(onClickMock).toHaveBeenCalled();
expect(onClickMock.mock.calls.length).toBe(1);
expect(onChangeMock).toHaveBeenCalled();
expect(onChangeMock.mock.calls.length).toBe(1);
const changeEvent = onChangeMock.mock.calls[0][0];
expect(changeEvent).toHaveProperty('target');
expect(changeEvent.target).toHaveProperty('checked');
});
it('should trigger both custom and internal radio events', () => {
const onClickMock = jest.fn();
const onChangeMock = jest.fn();
const getCheckboxProps = () => ({
onClick: onClickMock,
onChange: onChangeMock,
});
const { container } = render(
<Table
rowSelection={{
type: 'radio',
getCheckboxProps,
}}
columns={columns}
dataSource={data}
/>,
);
const firstRowRadio = container.querySelector('tbody tr:first-child input[type="radio"]');
expect(firstRowRadio).toBeTruthy();
fireEvent.click(firstRowRadio!);
expect(onClickMock).toHaveBeenCalled();
expect(onClickMock.mock.calls.length).toBe(1);
expect(onChangeMock).toHaveBeenCalled();
expect(onChangeMock.mock.calls.length).toBe(1);
const changeEvent = onChangeMock.mock.calls[0][0];
expect(changeEvent).toHaveProperty('target');
expect(changeEvent.target).toHaveProperty('checked');
});
});

View File

@ -496,17 +496,21 @@ const useSelection = <RecordType extends AnyObject = AnyObject>(
renderCell = (_, record, index) => {
const key = getRowKey(record, index);
const checked = keySet.has(key);
const checkboxProps = checkboxPropsMap.get(key);
return {
node: (
<Radio
{...checkboxPropsMap.get(key)}
{...checkboxProps}
checked={checked}
onClick={(e) => e.stopPropagation()}
onClick={(e) => {
e.stopPropagation();
checkboxProps?.onClick?.(e);
}}
onChange={(event) => {
if (!keySet.has(key)) {
triggerSingleSelection(key, true, [key], event.nativeEvent);
}
checkboxProps?.onChange?.(event);
}}
/>
),
@ -538,8 +542,12 @@ const useSelection = <RecordType extends AnyObject = AnyObject>(
indeterminate={mergedIndeterminate}
checked={checked}
skipGroup
onClick={(e) => e.stopPropagation()}
onChange={({ nativeEvent }) => {
onClick={(e) => {
e.stopPropagation();
checkboxProps?.onClick?.(e);
}}
onChange={(event) => {
const { nativeEvent } = event;
const { shiftKey } = nativeEvent;
const currentSelectedIndex = recordKeys.findIndex((item) => item === key);
const isMultiple = derivedSelectedKeys.some((item) => recordKeys.includes(item));
@ -595,6 +603,7 @@ const useSelection = <RecordType extends AnyObject = AnyObject>(
} else {
updatePrevSelectedIndex(currentSelectedIndex);
}
checkboxProps?.onChange?.(event);
}}
/>
),

View File

@ -2,6 +2,7 @@ import type { TimePickerLocale } from '../index';
const locale: TimePickerLocale = {
placeholder: "Selezionare l'orario",
rangePlaceholder: ['Inizio orario', 'Fine orario'],
};
export default locale;

View File

@ -24,7 +24,7 @@ demo:
<code src="./demo/treeLine.tsx">Show Tree Line</code>
<code src="./demo/placement.tsx">Placement</code>
<code src="./demo/status.tsx">Status</code>
<code src="./demo/suffix.tsx" debug>Prefix and Suffix</code>
<code src="./demo/suffix.tsx" version="5.22.0">Prefix and Suffix</code>
<code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code>
<code src="./demo/component-token.tsx" debug>Component Token</code>

View File

@ -25,7 +25,7 @@ demo:
<code src="./demo/treeLine.tsx">线性样式</code>
<code src="./demo/placement.tsx">弹出位置</code>
<code src="./demo/status.tsx">自定义状态</code>
<code src="./demo/suffix.tsx" debug>前后缀</code>
<code src="./demo/suffix.tsx" version="5.22.0">前后缀</code>
<code src="./demo/render-panel.tsx" debug>\_InternalPanelDoNotUseOrYouWillBeFired</code>
<code src="./demo/component-token.tsx" debug>组件 Token</code>

View File

@ -1,3 +1,5 @@
import { extendTest } from '../../../tests/shared/demoTest';
extendTest('tree', { skip: ['big-data.tsx', 'virtual-scroll.tsx', 'component-token.tsx'] });
extendTest('tree', {
skip: ['big-data.tsx', 'virtual-scroll.tsx', 'component-token.tsx', 'directory-debug.tsx'],
});

View File

@ -1,3 +1,5 @@
import demoTest from '../../../tests/shared/demoTest';
demoTest('tree', { skip: ['big-data.tsx', 'virtual-scroll.tsx', 'component-token.tsx'] });
demoTest('tree', {
skip: ['big-data.tsx', 'virtual-scroll.tsx', 'component-token.tsx', 'directory-debug.tsx'],
});

View File

@ -0,0 +1,7 @@
## zh-CN
调试 [#51210](https://github.com/ant-design/ant-design/pull/51210), [#51448](https://github.com/ant-design/ant-design/pull/51448#issuecomment-2449144872)
## en-US
Debugging [#51210](https://github.com/ant-design/ant-design/pull/51210), [#51448](https://github.com/ant-design/ant-design/pull/51448#issuecomment-2449144872)

View File

@ -0,0 +1,65 @@
import React from 'react';
import { Flex, Tree } from 'antd';
import type { GetProps, TreeDataNode } from 'antd';
const { DirectoryTree } = Tree;
const treeData: TreeDataNode[] = [
{
title: 'parent 0',
key: '0-0',
children: [
{ title: 'leaf 0-0', key: '0-0-0', isLeaf: true },
{ title: 'leaf 0-1', key: '0-0-1', isLeaf: true },
],
},
{
title: 'parent 1',
key: '0-1',
children: [
{ title: 'leaf 1-0', key: '0-1-0', isLeaf: true },
{ title: 'leaf 1-1', key: '0-1-1', isLeaf: true },
],
},
];
const sharedProps: GetProps<typeof DirectoryTree> = {
treeData,
defaultExpandAll: true,
onSelect: (keys, info) => {
console.log('Trigger Select', keys, info);
},
onExpand: (keys, info) => {
console.log('Trigger Expand', keys, info);
},
};
const DemoOne = () => <DirectoryTree draggable defaultSelectedKeys={['0-0-0']} />;
const DemoTwo = () => <DirectoryTree {...sharedProps} checkable defaultSelectedKeys={['0-1-0']} />;
const DemoThree = () => (
<DirectoryTree {...sharedProps} draggable checkable defaultSelectedKeys={['0-1']} />
);
const BasicDemo = () => <DirectoryTree {...sharedProps} multiple treeData={treeData} />;
const NormalDemo = () => <Tree {...sharedProps} defaultSelectedKeys={['0-1']} />;
const NormalCheckDemo = () => <Tree {...sharedProps} checkable defaultSelectedKeys={['0-1']} />;
const NormalDragDemo = () => <Tree {...sharedProps} draggable defaultSelectedKeys={['0-1-0']} />;
const App = () => (
<Flex wrap gap="large">
<DemoOne />
<DemoTwo />
<DemoThree />
<BasicDemo />
<NormalDemo />
<NormalCheckDemo />
<NormalDragDemo />
</Flex>
);
export default App;

View File

@ -24,6 +24,7 @@ Almost anything can be represented in a tree structure. Examples include directo
<code src="./demo/line.tsx">Tree with line</code>
<code src="./demo/customized-icon.tsx">Customize Icon</code>
<code src="./demo/directory.tsx">directory</code>
<code src="./demo/directory-debug.tsx" debug>Directory Debug</code>
<code src="./demo/switcher-icon.tsx">Customize collapse/expand icon</code>
<code src="./demo/virtual-scroll.tsx">Virtual scroll</code>
<code src="./demo/drag-debug.tsx" debug>Drag Debug</code>

View File

@ -25,6 +25,7 @@ demo:
<code src="./demo/line.tsx">连接线</code>
<code src="./demo/customized-icon.tsx">自定义图标</code>
<code src="./demo/directory.tsx">目录</code>
<code src="./demo/directory-debug.tsx" debug>目录 Debug</code>
<code src="./demo/switcher-icon.tsx">自定义展开/折叠图标</code>
<code src="./demo/virtual-scroll.tsx">虚拟滚动</code>
<code src="./demo/drag-debug.tsx" debug>Drag Debug</code>

View File

@ -38,15 +38,14 @@ export const genDirectoryStyle = ({
},
},
[`${treeCls}-switcher`]: {
marginInlineEnd: 0,
[`${treeCls}-switcher, ${treeCls}-checkbox, ${treeCls}-draggable-icon`]: {
zIndex: 1,
},
// ============= Selected =============
'&-selected': {
[`${treeCls}-switcher, ${treeCls}-draggable-icon`]: {
color: directoryNodeSelectedColor,
zIndex: 1,
},
// >>> Title

View File

@ -120,7 +120,6 @@ export const genBaseStyle = (prefixCls: string, token: TreeToken): CSSObject =>
nodeHoverBg,
colorTextQuaternary,
} = token;
const treeCheckBoxMarginHorizontal = token.marginXXS;
return {
[treeCls]: {
@ -253,6 +252,14 @@ export const genBaseStyle = (prefixCls: string, token: TreeToken): CSSObject =>
visibility: 'hidden',
},
// Switcher / Checkbox
[`${treeCls}-switcher, ${treeCls}-checkbox`]: {
marginInlineEnd: token
.calc(token.calc(titleHeight).sub(token.controlInteractiveSize))
.div(2)
.equal(),
},
// >>> Switcher
[`${treeCls}-switcher`]: {
...getSwitchStyle(prefixCls, token),
@ -260,15 +267,10 @@ export const genBaseStyle = (prefixCls: string, token: TreeToken): CSSObject =>
flex: 'none',
alignSelf: 'stretch',
width: titleHeight,
margin: 0,
textAlign: 'center',
cursor: 'pointer',
userSelect: 'none',
transition: `all ${token.motionDurationSlow}`,
marginInlineEnd: token
.calc(token.calc(titleHeight).sub(token.controlInteractiveSize))
.div(2)
.equal(),
'&-noop': {
cursor: 'unset',
@ -329,14 +331,6 @@ export const genBaseStyle = (prefixCls: string, token: TreeToken): CSSObject =>
},
},
// >>> Checkbox
[`${treeCls}-checkbox`]: {
top: 'initial',
marginInlineEnd: treeCheckBoxMarginHorizontal,
alignSelf: 'flex-start',
marginTop: token.marginXXS,
},
// >>> Title
// add `${treeCls}-checkbox + span` to cover checkbox `${checkboxCls} + span`
[`${treeCls}-node-content-wrapper`]: {

View File

@ -5,11 +5,10 @@ import { clearFix, textEllipsis } from '../../style';
import type { GenerateStyle } from '../../theme/internal';
const genListStyle: GenerateStyle<UploadToken> = (token) => {
const { componentCls, antCls, iconCls, fontSize, lineHeight, calc } = token;
const { componentCls, iconCls, fontSize, lineHeight, calc } = token;
const itemCls = `${componentCls}-list-item`;
const actionsCls = `${itemCls}-actions`;
const actionCls = `${itemCls}-action`;
const listItemHeightSM = token.fontHeightSM;
return {
[`${componentCls}-wrapper`]: {
@ -25,6 +24,7 @@ const genListStyle: GenerateStyle<UploadToken> = (token) => {
display: 'flex',
alignItems: 'center',
transition: `background-color ${token.motionDurationSlow}`,
borderRadius: token.borderRadiusSM,
'&:hover': {
backgroundColor: token.controlItemBgHover,
@ -56,12 +56,6 @@ const genListStyle: GenerateStyle<UploadToken> = (token) => {
`]: {
opacity: 1,
},
[`${actionCls}${antCls}-btn`]: {
height: listItemHeightSM,
border: 0,
lineHeight: 1,
},
},
[`${componentCls}-icon ${iconCls}`]: {

View File

@ -1,6 +1,6 @@
{
"name": "antd",
"version": "5.22.1",
"version": "5.22.2",
"description": "An enterprise-class UI design language and React components implementation",
"license": "MIT",
"funding": {
@ -343,5 +343,11 @@
"title": "Ant Design",
"tnpm": {
"mode": "npm"
},
"overrides": {
"@swc/types": "0.1.12"
},
"resolutions": {
"@swc/types": "0.1.12"
}
}

View File

@ -18,6 +18,8 @@ const blockStatus = ['failure', 'cancelled', 'timed_out'] as const;
const spinner = { interval: 80, frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] };
const spinnies = new Spinnies({ spinner });
const IGNORE_ACTIONS = ['Check Virtual Regression Approval', 'issue-remove-inactive'];
let spinniesId = 0;
// `spinnies` 为按条目进度,需要做简单的封装变成接近 `ora` 的形态
@ -142,14 +144,18 @@ const runPrePublish = async () => {
showMessage(`开始检查远程分支 ${currentBranch} 的 CI 状态`, true);
const failureUrlList: string[] = [];
const {
let {
data: { check_runs },
} = await octokit.checks.listForRef({
owner,
repo,
ref: sha,
filter: 'all',
});
showMessage(`远程分支 CI 状态(${check_runs.length})`, 'succeed');
check_runs = check_runs.filter((run) =>
IGNORE_ACTIONS.every((action) => !run.name.includes(action)),
);
check_runs.forEach((run) => {
showMessage(` ${run.name.padEnd(36)} ${emojify(run.status)} ${emojify(run.conclusion || '')}`);
if (blockStatus.some((status) => run.conclusion === status)) {

View File

@ -29,6 +29,11 @@
border-collapse: collapse;
}
table > thead {
position: sticky;
top: 0;
}
th,
td {
padding: 8px;
@ -61,13 +66,22 @@
border: 0;
}
table > thead {
position: unset;
}
th,
td {
width: 100%;
display: block;
padding: 6px;
border: none;
}
td:last-child {
border-bottom: 3px dashed darkgray;
}
th {
text-align: left;
background-color: transparent;