mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-12-02 12:09:14 +08:00
commit
1b9706f22d
@ -181,6 +181,26 @@ const RoutesPlugin = (api: IApi) => {
|
||||
// exclude dynamic route path, to avoid deploy failed by `:id` directory
|
||||
.filter((f) => !f.path.includes(':'))
|
||||
.map((file) => {
|
||||
let globalStyles = '';
|
||||
|
||||
// Debug for file content: uncomment this if need check raw out
|
||||
// const tmpFileName = `_${file.path.replace(/\//g, '-')}`;
|
||||
// const tmpFilePath = path.join(api.paths.absOutputPath, tmpFileName);
|
||||
// fs.writeFileSync(tmpFilePath, file.content, 'utf8');
|
||||
|
||||
// extract all emotion style tags from body
|
||||
file.content = file.content.replace(
|
||||
/<style (data-emotion|data-sandpack)[\S\s]+?<\/style>/g,
|
||||
(s) => {
|
||||
globalStyles += s;
|
||||
|
||||
return '';
|
||||
},
|
||||
);
|
||||
|
||||
// insert emotion style tags to head
|
||||
file.content = file.content.replace('</head>', `${globalStyles}</head>`);
|
||||
|
||||
// 1. 提取 antd-style 样式
|
||||
const styles = extractEmotionStyle(file.content);
|
||||
|
||||
@ -197,6 +217,30 @@ const RoutesPlugin = (api: IApi) => {
|
||||
file.content = addLinkStyle(file.content, cssFile);
|
||||
});
|
||||
|
||||
// Insert antd style to head
|
||||
const matchRegex = /<style data-type="antd-cssinjs">([\S\s]+?)<\/style>/;
|
||||
const matchList = file.content.match(matchRegex) || [];
|
||||
|
||||
// Init to order the `@layer`
|
||||
let antdStyle = '@layer global, antd;';
|
||||
|
||||
matchList.forEach((text) => {
|
||||
file.content = file.content.replace(text, '');
|
||||
antdStyle += text.replace(matchRegex, '$1');
|
||||
});
|
||||
|
||||
const cssFile = writeCSSFile('antd', antdStyle, antdStyle);
|
||||
file.content = addLinkStyle(file.content, cssFile, true);
|
||||
|
||||
// Insert antd cssVar to head
|
||||
const cssVarMatchRegex = /<style data-type="antd-css-var"[\S\s]+?<\/style>/;
|
||||
const cssVarMatchList = file.content.match(cssVarMatchRegex) || [];
|
||||
|
||||
cssVarMatchList.forEach((text) => {
|
||||
file.content = file.content.replace(text, '');
|
||||
file.content = file.content.replace('<head>', `<head>${text}`);
|
||||
});
|
||||
|
||||
return file;
|
||||
}),
|
||||
);
|
||||
|
@ -30,16 +30,7 @@ const locales = {
|
||||
|
||||
// ============================= Style =============================
|
||||
const useStyle = createStyles(({ token }) => {
|
||||
const {
|
||||
antCls,
|
||||
iconCls,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
headerHeight,
|
||||
menuItemBorder,
|
||||
colorPrimary,
|
||||
colorText,
|
||||
} = token;
|
||||
const { antCls, iconCls, fontFamily, fontSize, headerHeight, colorPrimary } = token;
|
||||
|
||||
return {
|
||||
nav: css`
|
||||
@ -57,25 +48,6 @@ const useStyle = createStyles(({ token }) => {
|
||||
padding-inline-end: ${token.paddingSM}px;
|
||||
padding-inline-start: ${token.paddingSM}px;
|
||||
line-height: ${headerHeight}px;
|
||||
|
||||
&::after {
|
||||
top: 0;
|
||||
right: 12px;
|
||||
bottom: auto;
|
||||
left: 12px;
|
||||
border-width: ${menuItemBorder}px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: ${colorText};
|
||||
}
|
||||
|
||||
a:before {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: transparent;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
& ${antCls}-menu-submenu-title ${iconCls} {
|
||||
|
2
.github/workflows/size-limit.yml
vendored
2
.github/workflows/size-limit.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- name: size-limit
|
||||
uses: ant-design/size-limit-action@master
|
||||
with:
|
||||
|
@ -7,6 +7,7 @@ import { Button, InputNumber, Select } from '../..';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
import { render } from '../../../tests/utils';
|
||||
import theme from '../../theme';
|
||||
import type { GlobalToken } from '../../theme';
|
||||
import { useToken } from '../../theme/internal';
|
||||
|
||||
const { defaultAlgorithm, darkAlgorithm, compactAlgorithm } = theme;
|
||||
@ -32,13 +33,13 @@ describe('ConfigProvider.Theme', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const styles: any[] = Array.from(document.querySelectorAll('style'));
|
||||
const styles = Array.from(document.querySelectorAll<HTMLStyleElement>('style'));
|
||||
const themeStyle = styles.find((style) =>
|
||||
style.getAttribute('rc-util-key').includes('-dynamic-theme'),
|
||||
style.getAttribute('rc-util-key')?.includes('-dynamic-theme'),
|
||||
);
|
||||
expect(themeStyle).toBeTruthy();
|
||||
|
||||
expect(themeStyle.innerHTML).toContain(`--bamboo-${kebabCase(colorName)}: rgb(0, 0, 255)`);
|
||||
expect(themeStyle?.innerHTML).toContain(`--bamboo-${kebabCase(colorName)}: rgb(0, 0, 255)`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -62,7 +63,7 @@ describe('ConfigProvider.Theme', () => {
|
||||
});
|
||||
|
||||
it('algorithm should work', () => {
|
||||
let tokenRef: any;
|
||||
let tokenRef: Partial<GlobalToken> = {};
|
||||
const Demo = () => {
|
||||
const [, token] = useToken();
|
||||
tokenRef = token;
|
||||
@ -77,7 +78,7 @@ describe('ConfigProvider.Theme', () => {
|
||||
});
|
||||
|
||||
it('compactAlgorithm should work', () => {
|
||||
let tokenRef: any;
|
||||
let tokenRef: Partial<GlobalToken> = {};
|
||||
const Demo = () => {
|
||||
const [, token] = useToken();
|
||||
tokenRef = token;
|
||||
@ -104,7 +105,7 @@ describe('ConfigProvider.Theme', () => {
|
||||
});
|
||||
|
||||
it('should support algorithm array', () => {
|
||||
let tokenRef: any;
|
||||
let tokenRef: Partial<GlobalToken> = {};
|
||||
const Demo = () => {
|
||||
const [, token] = useToken();
|
||||
tokenRef = token;
|
||||
@ -126,9 +127,9 @@ describe('ConfigProvider.Theme', () => {
|
||||
<InputNumber />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
const dynamicStyles = Array.from(document.querySelectorAll('style[data-css-hash]')).map(
|
||||
(item) => item?.innerHTML ?? '',
|
||||
);
|
||||
const dynamicStyles = Array.from(
|
||||
document.querySelectorAll<HTMLStyleElement>('style[data-css-hash]'),
|
||||
).map((item) => item?.innerHTML ?? '');
|
||||
expect(
|
||||
dynamicStyles.some(
|
||||
(style) => style.includes('.ant-input-number') && style.includes('width:50.1234px'),
|
||||
@ -159,7 +160,7 @@ describe('ConfigProvider.Theme', () => {
|
||||
});
|
||||
|
||||
it('The order does not affect the result', () => {
|
||||
const tokens = {
|
||||
const tokens: Record<'a' | 'b', Partial<GlobalToken>> = {
|
||||
a: {},
|
||||
b: {},
|
||||
};
|
||||
@ -182,7 +183,7 @@ describe('ConfigProvider.Theme', () => {
|
||||
});
|
||||
|
||||
it('theme separated should work', () => {
|
||||
let tokenRef: any;
|
||||
let tokenRef: Partial<GlobalToken> = {};
|
||||
const Demo = () => {
|
||||
const [, token] = useToken();
|
||||
tokenRef = token;
|
||||
@ -200,7 +201,7 @@ describe('ConfigProvider.Theme', () => {
|
||||
|
||||
it('theme inherit should not affect hashed and cssVar', () => {
|
||||
let hashId = 'hashId';
|
||||
let cssVar;
|
||||
let cssVar: any;
|
||||
|
||||
const Demo = () => {
|
||||
const [, , hash, , cssVarConfig] = useToken();
|
||||
|
@ -4,8 +4,17 @@ import type { ConfigConsumerProps } from '.';
|
||||
import { ConfigContext } from '.';
|
||||
import Empty from '../empty';
|
||||
|
||||
type ComponentName =
|
||||
| 'Table'
|
||||
| 'List'
|
||||
| 'Select'
|
||||
| 'TreeSelect'
|
||||
| 'Cascader'
|
||||
| 'Transfer'
|
||||
| 'Mentions';
|
||||
|
||||
interface EmptyProps {
|
||||
componentName?: string;
|
||||
componentName?: ComponentName;
|
||||
}
|
||||
|
||||
const DefaultRenderEmpty: React.FC<EmptyProps> = (props) => {
|
||||
@ -29,6 +38,6 @@ const DefaultRenderEmpty: React.FC<EmptyProps> = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
export type RenderEmptyHandler = (componentName?: string) => React.ReactNode;
|
||||
export type RenderEmptyHandler = (componentName?: ComponentName) => React.ReactNode;
|
||||
|
||||
export default DefaultRenderEmpty;
|
||||
|
@ -59,6 +59,7 @@ const localeValues: Locale = {
|
||||
selectCurrent: 'เลือกทั้งหมดในหน้านี้',
|
||||
removeCurrent: 'นำออกทั้งหมดในหน้านี้',
|
||||
selectAll: 'เลือกข้อมูลทั้งหมด',
|
||||
deselectAll: 'ยกเลิกการเลือกทั้งหมด',
|
||||
removeAll: 'นำข้อมูลออกทั้งหมด',
|
||||
selectInvert: 'กลับสถานะการเลือกในหน้านี้',
|
||||
},
|
||||
@ -80,6 +81,7 @@ const localeValues: Locale = {
|
||||
copy: 'คัดลอก',
|
||||
copied: 'คัดลอกแล้ว',
|
||||
expand: 'ขยาย',
|
||||
collapse: 'ย่อ',
|
||||
},
|
||||
Form: {
|
||||
optional: '(ไม่จำเป็น)',
|
||||
@ -137,6 +139,10 @@ const localeValues: Locale = {
|
||||
QRCode: {
|
||||
expired: 'คิวอาร์โค้ดหมดอายุ',
|
||||
refresh: 'รีเฟรช',
|
||||
scanned: 'สแกนแล้ว',
|
||||
},
|
||||
ColorPicker: {
|
||||
presetEmpty: 'ไม่มีข้อมูล',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -547,9 +547,10 @@ const InternalTable = <RecordType extends AnyObject = AnyObject>(
|
||||
|
||||
const mergedStyle: React.CSSProperties = { ...table?.style, ...style };
|
||||
|
||||
const emptyText = locale?.emptyText || renderEmpty?.('Table') || (
|
||||
<DefaultRenderEmpty componentName="Table" />
|
||||
);
|
||||
const emptyText =
|
||||
typeof locale?.emptyText !== 'undefined'
|
||||
? locale.emptyText
|
||||
: renderEmpty?.('Table') || <DefaultRenderEmpty componentName="Table" />;
|
||||
|
||||
// ========================== Render ==========================
|
||||
const TableComponent = virtual ? RcVirtualTable : RcTable;
|
||||
|
@ -147,6 +147,104 @@ exports[`Table renders empty table 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Table renders empty table when emptyText is null 1`] = `
|
||||
<div
|
||||
class="ant-table-wrapper"
|
||||
>
|
||||
<div
|
||||
class="ant-spin-nested-loading"
|
||||
>
|
||||
<div
|
||||
class="ant-spin-container"
|
||||
>
|
||||
<div
|
||||
class="ant-table ant-table-empty"
|
||||
>
|
||||
<div
|
||||
class="ant-table-container"
|
||||
>
|
||||
<div
|
||||
class="ant-table-content"
|
||||
>
|
||||
<table
|
||||
style="table-layout: auto;"
|
||||
>
|
||||
<colgroup />
|
||||
<thead
|
||||
class="ant-table-thead"
|
||||
>
|
||||
<tr>
|
||||
<th
|
||||
class="ant-table-cell"
|
||||
scope="col"
|
||||
>
|
||||
Column 1
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell"
|
||||
scope="col"
|
||||
>
|
||||
Column 2
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell"
|
||||
scope="col"
|
||||
>
|
||||
Column 3
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell"
|
||||
scope="col"
|
||||
>
|
||||
Column 4
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell"
|
||||
scope="col"
|
||||
>
|
||||
Column 5
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell"
|
||||
scope="col"
|
||||
>
|
||||
Column 6
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell"
|
||||
scope="col"
|
||||
>
|
||||
Column 7
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-cell"
|
||||
scope="col"
|
||||
>
|
||||
Column 8
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody
|
||||
class="ant-table-tbody"
|
||||
>
|
||||
<tr
|
||||
class="ant-table-placeholder"
|
||||
>
|
||||
<td
|
||||
class="ant-table-cell"
|
||||
colspan="8"
|
||||
/>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Table renders empty table with custom emptyText 1`] = `
|
||||
<div
|
||||
class="ant-table-wrapper"
|
||||
|
@ -89,6 +89,18 @@ describe('Table', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('renders empty table when emptyText is null', () => {
|
||||
const { container, asFragment } = render(
|
||||
<Table dataSource={[]} columns={columns} pagination={false} locale={{ emptyText: null }} />,
|
||||
);
|
||||
|
||||
expect(container.querySelector('.ant-table-placeholder>.ant-table-cell')?.hasChildNodes()).toBe(
|
||||
false,
|
||||
);
|
||||
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders empty table with custom emptyText', () => {
|
||||
const { asFragment } = render(
|
||||
<Table
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import type { DragEndEvent } from '@dnd-kit/core';
|
||||
import { DndContext, PointerSensor, useSensor } from '@dnd-kit/core';
|
||||
import { DndContext, PointerSensor, closestCenter, useSensor } from '@dnd-kit/core';
|
||||
import {
|
||||
arrayMove,
|
||||
horizontalListSortingStrategy,
|
||||
@ -69,7 +69,7 @@ const App: React.FC = () => {
|
||||
<Tabs
|
||||
items={items}
|
||||
renderTabBar={(tabBarProps, DefaultTabBar) => (
|
||||
<DndContext sensors={[sensor]} onDragEnd={onDragEnd}>
|
||||
<DndContext sensors={[sensor]} onDragEnd={onDragEnd} collisionDetection={closestCenter}>
|
||||
<SortableContext items={items.map((i) => i.key)} strategy={horizontalListSortingStrategy}>
|
||||
<DefaultTabBar {...tabBarProps}>
|
||||
{(node) => (
|
||||
|
@ -63,11 +63,12 @@ const InternalUploadList: React.ForwardRefRenderFunction<UploadListRef, UploadLi
|
||||
typeof window === 'undefined' ||
|
||||
!(window as any).FileReader ||
|
||||
!(window as any).File ||
|
||||
!(file.originFileObj instanceof File || file.originFileObj) ||
|
||||
!(file.originFileObj instanceof File || (file.originFileObj as any) instanceof Blob) ||
|
||||
file.thumbUrl !== undefined
|
||||
) {
|
||||
return;
|
||||
}
|
||||
file.thumbUrl = '';
|
||||
if (previewFile) {
|
||||
previewFile(file.originFileObj as File).then((previewDataUrl: string) => {
|
||||
// Need append '' to avoid dead loop
|
||||
|
@ -25,7 +25,7 @@ $ cd demo
|
||||
$ npm run dev
|
||||
```
|
||||
|
||||
此时访问浏览器 http://localhost:3000,看到 `Rsbuild with React` 的界面就算成功了。
|
||||
此时访问浏览器 http://localhost:3000 ,看到 `Rsbuild with React` 的界面就算成功了。
|
||||
|
||||
## 引入 antd
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user