mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-11-30 02:59:04 +08:00
Merge branch 'feature' into refactor-z-index-context
This commit is contained in:
commit
d51de67bc8
@ -63,6 +63,7 @@ const CustomTheme = () => {
|
||||
<Suspense fallback={<Skeleton style={{ margin: 24 }} />}>
|
||||
<ThemeEditor
|
||||
advanced
|
||||
hideAdvancedSwitcher
|
||||
theme={{ name: 'Custom Theme', key: 'test', config: theme }}
|
||||
style={{ height: 'calc(100vh - 64px)' }}
|
||||
onThemeChange={(newTheme) => {
|
||||
|
122
.github/workflows/preview-build.yml
vendored
Normal file
122
.github/workflows/preview-build.yml
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
# Each PR will build preview site that help to check code is work as expect.
|
||||
|
||||
name: Preview Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
# Cancel prev CI if new commit come
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# Prepare node modules. Reuse cache if available
|
||||
setup:
|
||||
name: prepare preview
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: cache package-lock.json
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: create package-lock.json
|
||||
run: npm i --package-lock-only --ignore-scripts
|
||||
|
||||
- name: hack for single file
|
||||
run: |
|
||||
if [ ! -d "package-temp-dir" ]; then
|
||||
mkdir package-temp-dir
|
||||
fi
|
||||
cp package-lock.json package-temp-dir
|
||||
- name: cache node_modules
|
||||
id: node_modules_cache_id
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: install
|
||||
if: steps.node_modules_cache_id.outputs.cache-hit != 'true'
|
||||
run: npm ci
|
||||
|
||||
build-site:
|
||||
name: build preview
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: npm run site
|
||||
id: site
|
||||
run: npm run site
|
||||
env:
|
||||
SITE_ENV: development
|
||||
NODE_OPTIONS: "--max_old_space_size=4096 --openssl-legacy-provider"
|
||||
|
||||
- name: upload site artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: site
|
||||
path: _site/
|
||||
retention-days: 5
|
||||
|
||||
# Upload PR id for next workflow use
|
||||
- name: Save PR number
|
||||
if: ${{ always() }}
|
||||
run: echo ${{ github.event.number }} > ./pr-id.txt
|
||||
|
||||
- name: Upload PR number
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: pr
|
||||
path: ./pr-id.txt
|
||||
|
||||
site-test:
|
||||
name: site E2E test
|
||||
runs-on: ubuntu-latest
|
||||
needs: [setup, build-site]
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: download site artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: site
|
||||
path: _site
|
106
.github/workflows/preview-deploy.yml
vendored
Normal file
106
.github/workflows/preview-deploy.yml
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
# Each PR will build preview site that help to check code is work as expect.
|
||||
|
||||
name: Preview Deploy
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Preview Build"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy-site:
|
||||
permissions:
|
||||
actions: read # for dawidd6/action-download-artifact to query and download artifacts
|
||||
issues: write # for actions-cool/maintain-one-comment to modify or create issue comments
|
||||
pull-requests: write # for actions-cool/maintain-one-comment to modify or create PR comments
|
||||
name: deploy preview
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
steps:
|
||||
# We need get PR id first
|
||||
- name: download pr artifact
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
name: pr
|
||||
|
||||
# Save PR id to output
|
||||
- name: save PR id
|
||||
id: pr
|
||||
run: echo "id=$(<pr-id.txt)" >> $GITHUB_OUTPUT
|
||||
|
||||
# Download site artifact
|
||||
- name: download site artifact
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
name: site
|
||||
|
||||
- name: upload surge service
|
||||
id: deploy
|
||||
run: |
|
||||
export DEPLOY_DOMAIN=https://preview-${{ steps.pr.outputs.id }}-ant-design.surge.sh
|
||||
npx surge --project ./ --domain $DEPLOY_DOMAIN --token ${{ secrets.SURGE_TOKEN }}
|
||||
|
||||
- name: update status comment
|
||||
uses: actions-cool/maintain-one-comment@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
body: |
|
||||
[<img width="306" src="https://user-images.githubusercontent.com/5378891/72400743-23dbb200-3785-11ea-9d13-1a2d92743846.png">](https://preview-${{ steps.pr.outputs.id }}-ant-design.surge.sh)
|
||||
<!-- AUTO_PREVIEW_HOOK -->
|
||||
body-include: '<!-- AUTO_PREVIEW_HOOK -->'
|
||||
number: ${{ steps.pr.outputs.id }}
|
||||
|
||||
- name: The job has failed
|
||||
if: ${{ failure() }}
|
||||
uses: actions-cool/maintain-one-comment@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
body: |
|
||||
<img width="534" src="https://user-images.githubusercontent.com/5378891/75333447-1e63a280-58c1-11ea-975d-235367fd1522.png">
|
||||
<!-- AUTO_PREVIEW_HOOK -->
|
||||
body-include: '<!-- AUTO_PREVIEW_HOOK -->'
|
||||
number: ${{ steps.pr.outputs.id }}
|
||||
|
||||
build-site-failed:
|
||||
permissions:
|
||||
actions: read # for dawidd6/action-download-artifact to query and download artifacts
|
||||
issues: write # for actions-cool/maintain-one-comment to modify or create issue comments
|
||||
pull-requests: write # for actions-cool/maintain-one-comment to modify or create PR comments
|
||||
name: build preview failed
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion == 'failure'
|
||||
steps:
|
||||
# We need get PR id first
|
||||
- name: download pr artifact
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
name: pr
|
||||
|
||||
# Save PR id to output
|
||||
- name: save PR id
|
||||
id: pr
|
||||
run: echo "id=$(<pr-id.txt)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: The job has failed
|
||||
uses: actions-cool/maintain-one-comment@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
body: |
|
||||
<img width="534" src="https://user-images.githubusercontent.com/5378891/75333447-1e63a280-58c1-11ea-975d-235367fd1522.png">
|
||||
<!-- AUTO_PREVIEW_HOOK -->
|
||||
body-include: '<!-- AUTO_PREVIEW_HOOK -->'
|
||||
number: ${{ steps.pr.outputs.id }}
|
31
.github/workflows/preview-start.yml
vendored
Normal file
31
.github/workflows/preview-start.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
# When `preview-build` start. Leave a message on the PR
|
||||
#
|
||||
# 🚨🚨🚨 Important 🚨🚨🚨
|
||||
# Never do any `checkout` or `npm install` action!
|
||||
# `pull_request_target` will enable PR to access the secrets!
|
||||
|
||||
name: Preview Start
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
preview-start:
|
||||
permissions:
|
||||
issues: write # for actions-cool/maintain-one-comment to modify or create issue comments
|
||||
pull-requests: write # for actions-cool/maintain-one-comment to modify or create PR comments
|
||||
name: start preview info
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: update status comment
|
||||
uses: actions-cool/maintain-one-comment@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
body: |
|
||||
![Prepare preview](https://user-images.githubusercontent.com/5378891/72351368-2c979e00-371b-11ea-9652-eb4e825d745e.gif)
|
||||
<!-- AUTO_PREVIEW_HOOK -->
|
||||
body-include: '<!-- AUTO_PREVIEW_HOOK -->'
|
208
.github/workflows/preview.yml
vendored
208
.github/workflows/preview.yml
vendored
@ -1,208 +0,0 @@
|
||||
name: PR Preview
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
# Cancel prev CI if new commit come
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
preview-start:
|
||||
permissions:
|
||||
issues: write # for actions-cool/maintain-one-comment to modify or create issue comments
|
||||
pull-requests: write # for actions-cool/maintain-one-comment to modify or create PR comments
|
||||
name: Prepare preview
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: update status comment
|
||||
uses: actions-cool/maintain-one-comment@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
body: |
|
||||
![Prepare preview](https://user-images.githubusercontent.com/5378891/72351368-2c979e00-371b-11ea-9652-eb4e825d745e.gif)
|
||||
<!-- AUTO_PREVIEW_HOOK -->
|
||||
body-include: "<!-- AUTO_PREVIEW_HOOK -->"
|
||||
|
||||
setup:
|
||||
name: Setup
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: cache package-lock.json
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: create package-lock.json
|
||||
run: npm i --package-lock-only --ignore-scripts
|
||||
|
||||
- name: hack for single file
|
||||
run: |
|
||||
if [ ! -d "package-temp-dir" ]; then
|
||||
mkdir package-temp-dir
|
||||
fi
|
||||
cp package-lock.json package-temp-dir
|
||||
- name: cache node_modules
|
||||
id: node_modules_cache_id
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: install
|
||||
if: steps.node_modules_cache_id.outputs.cache-hit != 'true'
|
||||
run: npm ci
|
||||
|
||||
build-site:
|
||||
name: Build Preview Site
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: npm run site
|
||||
id: site
|
||||
run: npm run site
|
||||
env:
|
||||
SITE_ENV: development
|
||||
NODE_OPTIONS: "--max_old_space_size=4096 --openssl-legacy-provider"
|
||||
|
||||
- name: upload site artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: site
|
||||
path: _site/
|
||||
retention-days: 5
|
||||
|
||||
# Upload PR id for next workflow use
|
||||
- name: Save PR number
|
||||
if: ${{ always() }}
|
||||
run: echo ${{ github.event.number }} > ./pr-id.txt
|
||||
|
||||
- name: Upload PR number
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: pr
|
||||
path: ./pr-id.txt
|
||||
|
||||
site-test:
|
||||
name: Site E2E Test
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-site
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: download site artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: site
|
||||
path: _site
|
||||
|
||||
- name: run e2e test
|
||||
run: npm run site:test
|
||||
|
||||
preview-deploy:
|
||||
name: Deploy Preview
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-site
|
||||
steps:
|
||||
# We need get PR id first
|
||||
- name: download pr artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: pr
|
||||
|
||||
# Save PR id to output
|
||||
- name: save PR id
|
||||
id: pr
|
||||
run: echo "id=$(<pr-id.txt)" >> $GITHUB_OUTPUT
|
||||
|
||||
# Download site artifact
|
||||
- name: download site artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: site
|
||||
|
||||
- name: upload surge service
|
||||
id: deploy
|
||||
run: |
|
||||
export DEPLOY_DOMAIN=https://preview-${{ steps.pr.outputs.id }}-ant-design.surge.sh
|
||||
npx surge --project ./ --domain $DEPLOY_DOMAIN --token ${{ secrets.SURGE_TOKEN }}
|
||||
|
||||
preview-end:
|
||||
name: Preview End
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-site
|
||||
- preview-deploy
|
||||
if: always()
|
||||
permissions:
|
||||
issues: write # for actions-cool/maintain-one-comment to modify or create issue comments
|
||||
pull-requests: write # for actions-cool/maintain-one-comment to modify or create PR comments
|
||||
steps:
|
||||
- name: download pr artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: pr
|
||||
|
||||
- name: save PR id
|
||||
id: pr
|
||||
run: echo "id=$(<pr-id.txt)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: success comment
|
||||
if: needs.build-site.result == 'success' && needs.preview-deploy.result == 'success'
|
||||
uses: actions-cool/maintain-one-comment@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
body: |
|
||||
[<img width="306" alt="Preview Is ready" src="https://user-images.githubusercontent.com/5378891/72400743-23dbb200-3785-11ea-9d13-1a2d92743846.png">](https://preview-${{ steps.pr.outputs.id }}-ant-design.surge.sh)
|
||||
<!-- AUTO_PREVIEW_HOOK -->
|
||||
body-include: "<!-- AUTO_PREVIEW_HOOK -->"
|
||||
number: ${{ steps.pr.outputs.id }}
|
||||
|
||||
- name: failed comment
|
||||
if: needs.build-site.result == 'failure' || needs.preview-deploy.result == 'failure'
|
||||
uses: actions-cool/maintain-one-comment@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
body: |
|
||||
<img width="534" alt="Preview Failed" src="https://user-images.githubusercontent.com/5378891/75333447-1e63a280-58c1-11ea-975d-235367fd1522.png">
|
||||
<!-- AUTO_PREVIEW_HOOK -->
|
||||
body-include: "<!-- AUTO_PREVIEW_HOOK -->"
|
||||
number: ${{ steps.pr.outputs.id }}
|
@ -90,7 +90,7 @@ exports[`renders components/affix/demo/on-change.tsx extend context correctly 2`
|
||||
|
||||
exports[`renders components/affix/demo/target.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
style="width: 100%; height: 100px; border-radius: 6px; overflow: auto; border: 1px solid #40a9ff;"
|
||||
style="width: 100%; height: 100px; overflow: auto; border: 1px solid #40a9ff;"
|
||||
>
|
||||
<div
|
||||
style="width: 100%; height: 1000px;"
|
||||
|
@ -84,7 +84,7 @@ exports[`renders components/affix/demo/on-change.tsx correctly 1`] = `
|
||||
|
||||
exports[`renders components/affix/demo/target.tsx correctly 1`] = `
|
||||
<div
|
||||
style="width:100%;height:100px;border-radius:6px;overflow:auto;border:1px solid #40a9ff"
|
||||
style="width:100%;height:100px;overflow:auto;border:1px solid #40a9ff"
|
||||
>
|
||||
<div
|
||||
style="width:100%;height:1000px"
|
||||
|
@ -4,7 +4,6 @@ import { Affix, Button } from 'antd';
|
||||
const containerStyle: React.CSSProperties = {
|
||||
width: '100%',
|
||||
height: 100,
|
||||
borderRadius: 6,
|
||||
overflow: 'auto',
|
||||
border: '1px solid #40a9ff',
|
||||
};
|
||||
|
@ -264,4 +264,28 @@ describe('Dropdown', () => {
|
||||
|
||||
expect(divRef.current).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should trigger open event when click on item', () => {
|
||||
const onOpenChange = jest.fn();
|
||||
|
||||
render(
|
||||
<Dropdown
|
||||
onOpenChange={onOpenChange}
|
||||
open
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
label: <div className="bamboo" />,
|
||||
key: 1,
|
||||
},
|
||||
],
|
||||
}}
|
||||
>
|
||||
<a />
|
||||
</Dropdown>,
|
||||
);
|
||||
|
||||
fireEvent.click(document.body.querySelector('.bamboo')!);
|
||||
expect(onOpenChange).toHaveBeenCalledWith(false, { source: 'menu' });
|
||||
});
|
||||
});
|
||||
|
@ -45,7 +45,7 @@ export interface DropdownProps {
|
||||
arrow?: boolean | DropdownArrowOptions;
|
||||
trigger?: ('click' | 'hover' | 'contextMenu')[];
|
||||
dropdownRender?: (originNode: React.ReactNode) => React.ReactNode;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
onOpenChange?: (open: boolean, info: { source: 'trigger' | 'menu' }) => void;
|
||||
open?: boolean;
|
||||
disabled?: boolean;
|
||||
destroyPopupOnHide?: boolean;
|
||||
@ -194,7 +194,7 @@ const Dropdown: CompoundedComponent = (props) => {
|
||||
});
|
||||
|
||||
const onInnerOpenChange = useEvent((nextOpen: boolean) => {
|
||||
onOpenChange?.(nextOpen);
|
||||
onOpenChange?.(nextOpen, { source: 'trigger' });
|
||||
onVisibleChange?.(nextOpen);
|
||||
setOpen(nextOpen);
|
||||
});
|
||||
@ -213,6 +213,7 @@ const Dropdown: CompoundedComponent = (props) => {
|
||||
});
|
||||
|
||||
const onMenuClick = React.useCallback(() => {
|
||||
onOpenChange?.(false, { source: 'menu' });
|
||||
setOpen(false);
|
||||
}, []);
|
||||
|
||||
|
@ -42,7 +42,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
### Dropdown
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| arrow | Whether the dropdown arrow should be visible | boolean \| { pointAtCenter: boolean } | false | |
|
||||
| autoAdjustOverflow | Whether to adjust dropdown placement automatically when dropdown is off screen | boolean | true | 5.2.0 |
|
||||
| autoFocus | Focus element in `overlay` when opened | boolean | false | 4.21.0 |
|
||||
@ -56,7 +56,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| placement | Placement of popup menu: `bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
|
||||
| trigger | The trigger mode which executes the dropdown action. Note that hover can't be used on touchscreens | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
|
||||
| open | Whether the dropdown menu is currently open. Use `visible` under 4.23.0 ([why?](/docs/react/faq#why-open)) | boolean | - | 4.23.0 |
|
||||
| onOpenChange | Called when the open state is changed. Not trigger when hidden by click item. Use `onVisibleChange` under 4.23.0 ([why?](/docs/react/faq#why-open)) | (open: boolean) => void | - | 4.23.0 |
|
||||
| onOpenChange | Called when the open state is changed. Not trigger when hidden by click item. Use `onVisibleChange` under 4.23.0 ([why?](/docs/react/faq#why-open)) | (open: boolean, info: { source: 'trigger' | 'menu' }) => void | - | `info.source`: 5.11.0 |
|
||||
|
||||
### Dropdown.Button
|
||||
|
||||
|
@ -46,7 +46,7 @@ demo:
|
||||
### Dropdown
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| arrow | 下拉框箭头是否显示 | boolean \| { pointAtCenter: boolean } | false | |
|
||||
| autoAdjustOverflow | 下拉框被遮挡时自动调整位置 | boolean | true | 5.2.0 |
|
||||
| autoFocus | 打开后自动聚焦下拉框 | boolean | false | 4.21.0 |
|
||||
@ -60,7 +60,7 @@ demo:
|
||||
| placement | 菜单弹出位置:`bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
|
||||
| trigger | 触发下拉的行为,移动端不支持 hover | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
|
||||
| open | 菜单是否显示,小于 4.23.0 使用 `visible`([为什么?](/docs/react/faq#弹层类组件为什么要统一至-open-属性)) | boolean | - | 4.23.0 |
|
||||
| onOpenChange | 菜单显示状态改变时调用,点击菜单按钮导致的消失不会触发。小于 4.23.0 使用 `onVisibleChange`([为什么?](/docs/react/faq#弹层类组件为什么要统一至-open-属性)) | (open: boolean) => void | - | 4.23.0 |
|
||||
| onOpenChange | 菜单显示状态改变时调用,点击菜单按钮导致的消失不会触发。小于 4.23.0 使用 `onVisibleChange`([为什么?](/docs/react/faq#弹层类组件为什么要统一至-open-属性)) | (open: boolean, info: { source: 'trigger' | 'menu' }) => void | - | `info.source`: 5.11.0 |
|
||||
|
||||
### Dropdown.Button
|
||||
|
||||
|
@ -308,9 +308,23 @@ Provide linkage between forms. If a sub form with `name` prop update, it will au
|
||||
| setFieldValue | Set fields value(Will directly pass to form store and **reset validation message**. If you do not want to modify passed object, please clone first) | (name: [NamePath](#namepath), value: any) => void | 4.22.0 |
|
||||
| setFieldsValue | Set fields value(Will directly pass to form store and **reset validation message**. If you do not want to modify passed object, please clone first). Use `setFieldValue` instead if you want to only config single value in Form.List | (values) => void | |
|
||||
| submit | Submit the form. It's same as click `submit` button | () => void | |
|
||||
| validateFields | Validate fields. Use `recursive` to validate all the field in the path | (nameList?: [NamePath](#namepath)\[], { validateOnly?: boolean }) => Promise | `validateOnly`: 5.5.0, `recursive`: 5.9.0 |
|
||||
| validateFields | Validate fields. Use `recursive` to validate all the field in the path | (nameList?: [NamePath](#namepath)\[], config?: [ValidateConfig](#validateFields)) => Promise | |
|
||||
|
||||
#### validateFields return sample
|
||||
#### validateFields
|
||||
|
||||
```tsx
|
||||
export interface ValidateConfig {
|
||||
// New in 5.5.0. Only validate content and not show error message on UI.
|
||||
validateOnly?: boolean;
|
||||
// New in 5.9.0. Recursively validate the provided `nameList` and its sub-paths.
|
||||
recursive?: boolean;
|
||||
// New in 5.11.0. Validate dirty fields (touched + validated).
|
||||
// It's useful to validate fields only when they are touched or validated.
|
||||
dirty?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
return sample:
|
||||
|
||||
```jsx
|
||||
validateFields()
|
||||
|
@ -307,9 +307,23 @@ Form.List 渲染表单相关操作函数。
|
||||
| setFieldValue | 设置表单的值(该值将直接传入 form store 中并且**重置错误信息**。如果你不希望传入对象被修改,请克隆后传入) | (name: [NamePath](#namepath), value: any) => void | 4.22.0 |
|
||||
| setFieldsValue | 设置表单的值(该值将直接传入 form store 中并且**重置错误信息**。如果你不希望传入对象被修改,请克隆后传入)。如果你只想修改 Form.List 中单项值,请通过 `setFieldValue` 进行指定 | (values) => void | |
|
||||
| submit | 提交表单,与点击 `submit` 按钮效果相同 | () => void | |
|
||||
| validateFields | 触发表单验证,设置 `recursive` 时会递归校验所有包含的路径 | (nameList?: [NamePath](#namepath)\[], { validateOnly?: boolean, recursive?: boolean }) => Promise | `validateOnly`: 5.5.0, `recursive`: 5.9.0 |
|
||||
| validateFields | 触发表单验证,设置 `recursive` 时会递归校验所有包含的路径 | (nameList?: [NamePath](#namepath)\[], config?: [ValidateConfig](#validateFields)) => Promise | |
|
||||
|
||||
#### validateFields 返回示例
|
||||
#### validateFields
|
||||
|
||||
```tsx
|
||||
export interface ValidateConfig {
|
||||
// 5.5.0 新增。仅校验内容而不会将错误信息展示到 UI 上。
|
||||
validateOnly?: boolean;
|
||||
// 5.9.0 新增。对提供的 `nameList` 与其子路径进行递归校验。
|
||||
recursive?: boolean;
|
||||
// 5.11.0 新增。校验 dirty 的字段(touched + validated)。
|
||||
// 使用 `dirty` 可以很方便的仅校验用户操作过和被校验过的字段。
|
||||
dirty?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
返回示例:
|
||||
|
||||
```jsx
|
||||
validateFields()
|
||||
|
@ -42,6 +42,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| addonBefore | The label text displayed before (on the left side of) the input field | ReactNode | - | |
|
||||
| autoFocus | If get focus when component mounted | boolean | false | - |
|
||||
| bordered | Whether has border style | boolean | true | 4.12.0 |
|
||||
| changeOnBlur | Trigger `onChange` when blur. e.g. reset value in range by blur | boolean | true | 5.11.0 |
|
||||
| controls | Whether to show `+-` controls, or set custom arrows icon | boolean \| { upIcon?: React.ReactNode; downIcon?: React.ReactNode; } | - | 4.19.0 |
|
||||
| decimalSeparator | Decimal separator | string | - | - |
|
||||
| defaultValue | The initial value | number | - | - |
|
||||
|
@ -43,6 +43,7 @@ demo:
|
||||
| addonBefore | 带标签的 input,设置前置标签 | ReactNode | - | 4.17.0 |
|
||||
| autoFocus | 自动获取焦点 | boolean | false | - |
|
||||
| bordered | 是否有边框 | boolean | true | 4.12.0 |
|
||||
| changeOnBlur | 是否在失去焦点时,触发 `onChange` 事件(例如值超出范围时,重新限制回范围并触发事件) | boolean | true | 5.11.0 |
|
||||
| controls | 是否显示增减按钮,也可设置自定义箭头图标 | boolean \| { upIcon?: React.ReactNode; downIcon?: React.ReactNode; } | - | 4.19.0 |
|
||||
| decimalSeparator | 小数点 | string | - | - |
|
||||
| defaultValue | 初始值 | number | - | - |
|
||||
|
@ -1,14 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import { useContext, useEffect, useRef, useState } from 'react';
|
||||
import BarsOutlined from '@ant-design/icons/BarsOutlined';
|
||||
import LeftOutlined from '@ant-design/icons/LeftOutlined';
|
||||
import RightOutlined from '@ant-design/icons/RightOutlined';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import * as React from 'react';
|
||||
import { useContext, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import isNumeric from '../_util/isNumeric';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { LayoutContext } from './layout';
|
||||
import { LayoutContext } from './context';
|
||||
|
||||
const dimensionMaxMap = {
|
||||
xs: '479.98px',
|
||||
|
@ -115,7 +115,7 @@ exports[`renders components/layout/demo/basic.tsx correctly 1`] = `
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-layout"
|
||||
class="ant-layout ant-layout-has-sider"
|
||||
>
|
||||
<aside
|
||||
class="ant-layout-sider ant-layout-sider-dark"
|
||||
@ -172,7 +172,7 @@ exports[`renders components/layout/demo/component-token.tsx correctly 1`] = `
|
||||
</div>
|
||||
</header>
|
||||
<div
|
||||
class="ant-layout"
|
||||
class="ant-layout ant-layout-has-sider"
|
||||
>
|
||||
<aside
|
||||
class="ant-layout-sider ant-layout-sider-dark"
|
||||
@ -429,7 +429,7 @@ exports[`renders components/layout/demo/component-token.tsx correctly 1`] = `
|
||||
|
||||
exports[`renders components/layout/demo/custom-trigger.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-layout"
|
||||
class="ant-layout ant-layout-has-sider"
|
||||
>
|
||||
<aside
|
||||
class="ant-layout-sider ant-layout-sider-dark"
|
||||
@ -1256,7 +1256,7 @@ exports[`renders components/layout/demo/fixed-sider.tsx correctly 1`] = `
|
||||
|
||||
exports[`renders components/layout/demo/responsive.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-layout"
|
||||
class="ant-layout ant-layout-has-sider"
|
||||
>
|
||||
<aside
|
||||
class="ant-layout-sider ant-layout-sider-dark"
|
||||
@ -1434,7 +1434,7 @@ exports[`renders components/layout/demo/responsive.tsx correctly 1`] = `
|
||||
|
||||
exports[`renders components/layout/demo/side.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-layout"
|
||||
class="ant-layout ant-layout-has-sider"
|
||||
style="min-height:100vh"
|
||||
>
|
||||
<aside
|
||||
@ -2154,7 +2154,7 @@ exports[`renders components/layout/demo/top-side.tsx correctly 1`] = `
|
||||
</ol>
|
||||
</nav>
|
||||
<div
|
||||
class="ant-layout"
|
||||
class="ant-layout ant-layout-has-sider"
|
||||
style="padding:24px 0;background:#ffffff"
|
||||
>
|
||||
<aside
|
||||
@ -2469,7 +2469,7 @@ exports[`renders components/layout/demo/top-side-2.tsx correctly 1`] = `
|
||||
/>
|
||||
</header>
|
||||
<div
|
||||
class="ant-layout"
|
||||
class="ant-layout ant-layout-has-sider"
|
||||
>
|
||||
<aside
|
||||
class="ant-layout-sider ant-layout-sider-dark"
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { UserOutlined } from '@ant-design/icons';
|
||||
import React, { useState } from 'react';
|
||||
import { UserOutlined } from '@ant-design/icons';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
|
||||
import Layout from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
@ -332,4 +334,16 @@ describe('Sider', () => {
|
||||
expect(ref.current instanceof HTMLElement).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('auto check hasSider', () => {
|
||||
const htmlContent = renderToString(
|
||||
<Layout>
|
||||
<div />
|
||||
<Sider />
|
||||
<div />
|
||||
</Layout>,
|
||||
);
|
||||
|
||||
expect(htmlContent).toContain('ant-layout-has-sider');
|
||||
});
|
||||
});
|
||||
|
15
components/layout/context.ts
Normal file
15
components/layout/context.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface LayoutContextProps {
|
||||
siderHook: {
|
||||
addSider: (id: string) => void;
|
||||
removeSider: (id: string) => void;
|
||||
};
|
||||
}
|
||||
|
||||
export const LayoutContext = React.createContext<LayoutContextProps>({
|
||||
siderHook: {
|
||||
addSider: () => null,
|
||||
removeSider: () => null,
|
||||
},
|
||||
});
|
22
components/layout/hooks/useHasSider.ts
Normal file
22
components/layout/hooks/useHasSider.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import type * as React from 'react';
|
||||
import toArray from 'rc-util/lib/Children/toArray';
|
||||
|
||||
import Sider from '../Sider';
|
||||
|
||||
export default function useHasSider(
|
||||
siders: string[],
|
||||
children?: React.ReactNode,
|
||||
hasSider?: boolean,
|
||||
) {
|
||||
if (typeof hasSider === 'boolean') {
|
||||
return hasSider;
|
||||
}
|
||||
|
||||
if (siders.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const childNodes = toArray(children);
|
||||
|
||||
return childNodes.some((node) => node.type === Sider);
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { LayoutContext } from './context';
|
||||
import useHasSider from './hooks/useHasSider';
|
||||
import useStyle from './style';
|
||||
|
||||
export interface GeneratorProps {
|
||||
@ -16,19 +19,6 @@ export interface BasicProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
hasSider?: boolean;
|
||||
}
|
||||
|
||||
export interface LayoutContextProps {
|
||||
siderHook: {
|
||||
addSider: (id: string) => void;
|
||||
removeSider: (id: string) => void;
|
||||
};
|
||||
}
|
||||
export const LayoutContext = React.createContext<LayoutContextProps>({
|
||||
siderHook: {
|
||||
addSider: () => null,
|
||||
removeSider: () => null,
|
||||
},
|
||||
});
|
||||
|
||||
interface BasicPropsWithTagName extends BasicProps {
|
||||
tagName: 'header' | 'footer' | 'main' | 'div';
|
||||
}
|
||||
@ -91,11 +81,13 @@ const BasicLayout = React.forwardRef<HTMLDivElement, BasicPropsWithTagName>((pro
|
||||
const { getPrefixCls, layout } = React.useContext(ConfigContext);
|
||||
const prefixCls = getPrefixCls('layout', customizePrefixCls);
|
||||
|
||||
const mergedHasSider = useHasSider(siders, children, hasSider);
|
||||
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
const classString = classNames(
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}-has-sider`]: typeof hasSider === 'boolean' ? hasSider : siders.length > 0,
|
||||
[`${prefixCls}-has-sider`]: mergedHasSider,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
layout?.className,
|
||||
|
@ -118,6 +118,19 @@ exports[`renders components/spin/demo/delayAndDebounce.tsx extend context correc
|
||||
|
||||
exports[`renders components/spin/demo/delayAndDebounce.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/spin/demo/fullscreen.tsx extend context correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Show fullscreen for 2s
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders components/spin/demo/fullscreen.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/spin/demo/inside.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="example"
|
||||
|
@ -112,6 +112,17 @@ exports[`renders components/spin/demo/delayAndDebounce.tsx correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/spin/demo/fullscreen.tsx correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Show fullscreen for 2s
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`renders components/spin/demo/inside.tsx correctly 1`] = `
|
||||
<div
|
||||
class="example"
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import Spin from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
@ -19,6 +20,15 @@ describe('Spin', () => {
|
||||
expect(container.querySelector<HTMLElement>('.ant-spin')?.style.background).toBe('red');
|
||||
});
|
||||
|
||||
it('should not apply nested styles when full screen', () => {
|
||||
const { container } = render(
|
||||
<Spin fullscreen>
|
||||
<div>content</div>
|
||||
</Spin>,
|
||||
);
|
||||
expect(container.querySelector<HTMLElement>('ant-spin-nested-loading')).toBeNull();
|
||||
});
|
||||
|
||||
it("should render custom indicator when it's set", () => {
|
||||
const customIndicator = <div className="custom-indicator" />;
|
||||
const { asFragment } = render(<Spin indicator={customIndicator} />);
|
||||
|
7
components/spin/demo/fullscreen.md
Normal file
7
components/spin/demo/fullscreen.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
`fullscreen` 属性非常适合创建流畅的页面加载器。它添加了半透明覆盖层,并在其中心放置了一个旋转加载符号。
|
||||
|
||||
## en-US
|
||||
|
||||
The `fullscreen` mode is perfect for creating page loaders. It adds a dimmed overlay with a centered spinner.
|
23
components/spin/demo/fullscreen.tsx
Normal file
23
components/spin/demo/fullscreen.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button, Spin } from 'antd';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
const showLoader = () => {
|
||||
setShow(true);
|
||||
|
||||
setTimeout(() => {
|
||||
setShow(false);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={showLoader}>Show fullscreen for 2s</Button>
|
||||
{show && <Spin fullscreen size="large" />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
@ -17,26 +17,28 @@ When part of the page is waiting for asynchronous data or during a rendering pro
|
||||
## Examples
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/basic.tsx">basic Usage</code>
|
||||
<code src="./demo/basic.tsx">Basic Usage</code>
|
||||
<code src="./demo/size.tsx">Size</code>
|
||||
<code src="./demo/inside.tsx">Inside a container</code>
|
||||
<code src="./demo/nested.tsx">Embedded mode</code>
|
||||
<code src="./demo/tip.tsx">Customized description</code>
|
||||
<code src="./demo/delayAndDebounce.tsx">delay</code>
|
||||
<code src="./demo/delayAndDebounce.tsx">Delay</code>
|
||||
<code src="./demo/custom-indicator.tsx">Custom spinning indicator</code>
|
||||
<code src="./demo/fullscreen.tsx">Fullscreen</code>
|
||||
|
||||
## API
|
||||
|
||||
Common props ref:[Common props](/docs/react/common-props)
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| delay | Specifies a delay in milliseconds for loading state (prevent flush) | number (milliseconds) | - |
|
||||
| indicator | React node of the spinning indicator | ReactNode | - |
|
||||
| size | The size of Spin, options: `small`, `default` and `large` | string | `default` |
|
||||
| spinning | Whether Spin is visible | boolean | true |
|
||||
| tip | Customize description content when Spin has children | ReactNode | - |
|
||||
| wrapperClassName | The className of wrapper when Spin has children | string | - |
|
||||
| fullscreen | Display a backdrop with the `Spin` component | boolean | false | 5.11.0 |
|
||||
|
||||
### Static Method
|
||||
|
||||
|
@ -25,6 +25,7 @@ export interface SpinProps {
|
||||
wrapperClassName?: string;
|
||||
indicator?: SpinIndicator;
|
||||
children?: React.ReactNode;
|
||||
fullscreen?: boolean;
|
||||
}
|
||||
|
||||
export interface SpinClassProps extends SpinProps {
|
||||
@ -87,6 +88,7 @@ const Spin: React.FC<SpinClassProps> = (props) => {
|
||||
style,
|
||||
children,
|
||||
hashId,
|
||||
fullscreen,
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
@ -108,7 +110,10 @@ const Spin: React.FC<SpinClassProps> = (props) => {
|
||||
setSpinning(false);
|
||||
}, [delay, customSpinning]);
|
||||
|
||||
const isNestedPattern = React.useMemo<boolean>(() => typeof children !== 'undefined', [children]);
|
||||
const isNestedPattern = React.useMemo<boolean>(
|
||||
() => typeof children !== 'undefined' && !fullscreen,
|
||||
[children, fullscreen],
|
||||
);
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const warning = devUseWarning('Spin');
|
||||
@ -126,6 +131,7 @@ const Spin: React.FC<SpinClassProps> = (props) => {
|
||||
[`${prefixCls}-lg`]: size === 'large',
|
||||
[`${prefixCls}-spinning`]: spinning,
|
||||
[`${prefixCls}-show-text`]: !!tip,
|
||||
[`${prefixCls}-fullscreen`]: fullscreen,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
@ -151,7 +157,9 @@ const Spin: React.FC<SpinClassProps> = (props) => {
|
||||
aria-busy={spinning}
|
||||
>
|
||||
{renderIndicator(prefixCls, props)}
|
||||
{tip && isNestedPattern ? <div className={`${prefixCls}-text`}>{tip}</div> : null}
|
||||
{tip && (isNestedPattern || fullscreen) ? (
|
||||
<div className={`${prefixCls}-text`}>{tip}</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -25,19 +25,21 @@ demo:
|
||||
<code src="./demo/tip.tsx">自定义描述文案</code>
|
||||
<code src="./demo/delayAndDebounce.tsx">延迟</code>
|
||||
<code src="./demo/custom-indicator.tsx">自定义指示符</code>
|
||||
<code src="./demo/fullscreen.tsx">全屏</code>
|
||||
|
||||
## API
|
||||
|
||||
通用属性参考:[通用属性](/docs/react/common-props)
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| ---------------- | -------------------------------------------- | ------------- | --------- |
|
||||
| delay | 延迟显示加载效果的时间(防止闪烁) | number (毫秒) | - |
|
||||
| indicator | 加载指示符 | ReactNode | - |
|
||||
| size | 组件大小,可选值为 `small` `default` `large` | string | `default` |
|
||||
| spinning | 是否为加载中状态 | boolean | true |
|
||||
| tip | 当作为包裹元素时,可以自定义描述文案 | ReactNode | - |
|
||||
| wrapperClassName | 包装器的类属性 | string | - |
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| delay | 延迟显示加载效果的时间(防止闪烁) | number (毫秒) | - |
|
||||
| indicator | 加载指示符 | ReactNode | - |
|
||||
| size | 组件大小,可选值为 `small` `default` `large` | string | `default` |
|
||||
| spinning | 是否为加载中状态 | boolean | true |
|
||||
| tip | 当作为包裹元素时,可以自定义描述文案 | ReactNode | - |
|
||||
| wrapperClassName | 包装器的类属性 | string | - |
|
||||
| fullscreen | 显示带有 `Spin` 组件的背景 | boolean | false | 5.11.0 |
|
||||
|
||||
### 静态方法
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import type { CSSObject } from '@ant-design/cssinjs';
|
||||
import { Keyframes } from '@ant-design/cssinjs';
|
||||
|
||||
import { resetComponent } from '../../style';
|
||||
import type { FullToken, GenerateStyle } from '../../theme/internal';
|
||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
||||
@ -39,6 +40,8 @@ const antRotate = new Keyframes('antRotate', {
|
||||
to: { transform: 'rotate(405deg)' },
|
||||
});
|
||||
|
||||
const dotPadding = (token: SpinToken) => (token.dotSize - token.fontSize) / 2 + 2;
|
||||
|
||||
const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject => ({
|
||||
[`${token.componentCls}`]: {
|
||||
...resetComponent(token),
|
||||
@ -57,6 +60,30 @@ const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject =>
|
||||
opacity: 1,
|
||||
},
|
||||
|
||||
[`${token.componentCls}-text`]: {
|
||||
fontSize: token.fontSize,
|
||||
paddingTop: dotPadding(token),
|
||||
},
|
||||
|
||||
'&-fullscreen': {
|
||||
position: 'fixed',
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
backgroundColor: token.colorBgMask,
|
||||
zIndex: token.zIndexPopupBase,
|
||||
inset: 0,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
[`${token.componentCls}-dot ${token.componentCls}-dot-item`]: {
|
||||
backgroundColor: token.colorWhite,
|
||||
},
|
||||
[`${token.componentCls}-text`]: {
|
||||
color: token.colorTextLightSolid,
|
||||
},
|
||||
},
|
||||
|
||||
'&-nested-loading': {
|
||||
position: 'relative',
|
||||
[`> div > ${token.componentCls}`]: {
|
||||
@ -80,9 +107,7 @@ const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject =>
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
width: '100%',
|
||||
paddingTop: (token.dotSize - token.fontSize) / 2 + 2,
|
||||
textShadow: `0 1px 2px ${token.colorBgContainer}`, // FIXME: shadow
|
||||
fontSize: token.fontSize,
|
||||
},
|
||||
|
||||
[`&${token.componentCls}-show-text ${token.componentCls}-dot`]: {
|
||||
|
@ -11,7 +11,7 @@ import Button from '../../../button';
|
||||
import type { CheckboxChangeEvent } from '../../../checkbox';
|
||||
import Checkbox from '../../../checkbox';
|
||||
import { ConfigContext } from '../../../config-provider/context';
|
||||
import Dropdown from '../../../dropdown';
|
||||
import Dropdown, { type DropdownProps } from '../../../dropdown';
|
||||
import Empty from '../../../empty';
|
||||
import type { MenuProps } from '../../../menu';
|
||||
import Menu from '../../../menu';
|
||||
@ -295,17 +295,19 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
||||
internalTriggerFilter(getFilteredKeysSync());
|
||||
};
|
||||
|
||||
const onVisibleChange = (newVisible: boolean) => {
|
||||
if (newVisible && propFilteredKeys !== undefined) {
|
||||
// Sync filteredKeys on appear in controlled mode (propFilteredKeys !== undefined)
|
||||
setFilteredKeysSync(wrapStringListType(propFilteredKeys));
|
||||
}
|
||||
const onVisibleChange: DropdownProps['onOpenChange'] = (newVisible, info) => {
|
||||
if (info.source === 'trigger') {
|
||||
if (newVisible && propFilteredKeys !== undefined) {
|
||||
// Sync filteredKeys on appear in controlled mode (propFilteredKeys !== undefined)
|
||||
setFilteredKeysSync(wrapStringListType(propFilteredKeys));
|
||||
}
|
||||
|
||||
triggerVisible(newVisible);
|
||||
triggerVisible(newVisible);
|
||||
|
||||
// Default will filter when closed
|
||||
if (!newVisible && !column.filterDropdown) {
|
||||
onConfirm();
|
||||
// Default will filter when closed
|
||||
if (!newVisible && !column.filterDropdown) {
|
||||
onConfirm();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -74,15 +74,15 @@ More option at [rc-tabs tabs](https://github.com/react-component/tabs#tabs)
|
||||
|
||||
### TabItemType
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| closeIcon | Customize close icon in TabPane's head. Only works while `type="editable-card"`. 5.7.0: close button will be hidden when setting to `null` or `false` | boolean \| ReactNode | - |
|
||||
| closeIcon | Customize close icon in TabPane's head. Only works while `type="editable-card"`. 5.7.0: close button will be hidden when setting to `null` or `false` | boolean \| ReactNode | - | |
|
||||
| destroyInactiveTabPane | Whether destroy inactive TabPane when change tab | boolean | false | 5.11.0 |
|
||||
| disabled | Set TabPane disabled | boolean | false |
|
||||
| forceRender | Forced render of content in tabs, not lazy render after clicking on tabs | boolean | false |
|
||||
| key | TabPane's key | string | - |
|
||||
| label | TabPane's head display text | ReactNode | - |
|
||||
| children | TabPane's head display content | ReactNode | - |
|
||||
| disabled | Set TabPane disabled | boolean | false | |
|
||||
| forceRender | Forced render of content in tabs, not lazy render after clicking on tabs | boolean | false | |
|
||||
| key | TabPane's key | string | - | |
|
||||
| label | TabPane's head display text | ReactNode | - | |
|
||||
| children | TabPane's head display content | ReactNode | - | |
|
||||
|
||||
## Design Token
|
||||
|
||||
|
@ -76,15 +76,15 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。
|
||||
|
||||
### TabItemType
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| closeIcon | 自定义关闭图标,在 `type="editable-card"` 时有效。5.7.0:设置为 `null` 或 `false` 时隐藏关闭按钮 | boolean \| ReactNode | - |
|
||||
| closeIcon | 自定义关闭图标,在 `type="editable-card"` 时有效。5.7.0:设置为 `null` 或 `false` 时隐藏关闭按钮 | boolean \| ReactNode | - | |
|
||||
| destroyInactiveTabPane | 被隐藏时是否销毁 DOM 结构 | boolean | false | 5.11.0 |
|
||||
| disabled | 禁用某一项 | boolean | false |
|
||||
| forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false |
|
||||
| key | 对应 activeKey | string | - |
|
||||
| label | 选项卡头显示文字 | ReactNode | - |
|
||||
| children | 选项卡头显示内容 | ReactNode | - |
|
||||
| disabled | 禁用某一项 | boolean | false | |
|
||||
| forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false | |
|
||||
| key | 对应 activeKey | string | - | |
|
||||
| label | 选项卡头显示文字 | ReactNode | - | |
|
||||
| children | 选项卡头显示内容 | ReactNode | - | |
|
||||
|
||||
## 主题变量(Design Token)
|
||||
|
||||
|
11
package.json
11
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "antd",
|
||||
"version": "5.10.1",
|
||||
"packageManager": "^npm@9.0.0",
|
||||
"packageManager": "npm@9.0.0",
|
||||
"description": "An enterprise-class UI design language and React components implementation",
|
||||
"title": "Ant Design",
|
||||
"keywords": [
|
||||
@ -95,6 +95,7 @@
|
||||
"test-node": "npm run version && jest --config .jest.node.js --no-cache",
|
||||
"tsc": "tsc --noEmit",
|
||||
"site:test": "jest --config .jest.site.js",
|
||||
"site:test:update": "npm run site && npm run site:test -- -u",
|
||||
"test-image": "jest --config .jest.image.js --no-cache -i -u",
|
||||
"argos": "tsx scripts/argos-upload.ts",
|
||||
"version": "tsx scripts/generate-version.ts",
|
||||
@ -129,10 +130,10 @@
|
||||
"rc-dialog": "~9.3.3",
|
||||
"rc-drawer": "~6.5.2",
|
||||
"rc-dropdown": "~4.1.0",
|
||||
"rc-field-form": "~1.39.0",
|
||||
"rc-field-form": "~1.40.0",
|
||||
"rc-image": "~7.3.1",
|
||||
"rc-input": "~1.3.5",
|
||||
"rc-input-number": "~8.3.0",
|
||||
"rc-input-number": "~8.4.0",
|
||||
"rc-mentions": "~2.9.1",
|
||||
"rc-menu": "~9.12.2",
|
||||
"rc-motion": "^2.9.0",
|
||||
@ -148,7 +149,7 @@
|
||||
"rc-steps": "~6.0.1",
|
||||
"rc-switch": "~4.1.0",
|
||||
"rc-table": "~7.35.1",
|
||||
"rc-tabs": "~12.13.0",
|
||||
"rc-tabs": "~12.13.1",
|
||||
"rc-textarea": "~1.5.1",
|
||||
"rc-tooltip": "~6.1.1",
|
||||
"rc-tree": "~5.7.12",
|
||||
@ -209,7 +210,7 @@
|
||||
"@typescript-eslint/parser": "^6.5.0",
|
||||
"antd-img-crop": "^4.9.0",
|
||||
"antd-style": "^3.5.0",
|
||||
"antd-token-previewer": "^2.0.1",
|
||||
"antd-token-previewer": "^2.0.4",
|
||||
"chalk": "^4.0.0",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"circular-dependency-plugin": "^5.2.2",
|
||||
|
Loading…
Reference in New Issue
Block a user