mirror of
https://github.com/ant-design/ant-design.git
synced 2025-08-05 23:46:28 +08:00
fix merge
This commit is contained in:
commit
f8dc201218
2
.github/workflows/issue-check-inactive.yml
vendored
2
.github/workflows/issue-check-inactive.yml
vendored
@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: check-inactive
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'check-inactive'
|
||||
inactive-label: 'Inactive'
|
||||
|
4
.github/workflows/issue-close-require.yml
vendored
4
.github/workflows/issue-close-require.yml
vendored
@ -9,14 +9,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: need reproduce
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'close-issues'
|
||||
labels: '🤔 Need Reproduce'
|
||||
inactive-day: 3
|
||||
|
||||
- name: needs more info
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'close-issues'
|
||||
labels: 'needs-more-info'
|
||||
|
12
.github/workflows/issue-labeled.yml
vendored
12
.github/workflows/issue-labeled.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
steps:
|
||||
- name: help wanted
|
||||
if: github.event.label.name == 'help wanted'
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
- name: 🤔 Need Reproduce
|
||||
if: github.event.label.name == '🤔 Need Reproduce'
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
- name: Usage
|
||||
if: github.event.label.name == 'Usage' || github.event.label.name == 'Question'
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment,close-issue'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@ -52,7 +52,7 @@ jobs:
|
||||
|
||||
- name: 3.x
|
||||
if: github.event.label.name == '3.x'
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment,close-issue'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@ -64,7 +64,7 @@ jobs:
|
||||
|
||||
- name: invalid
|
||||
if: github.event.label.name == 'Invalid'
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment,close-issue'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@ -76,7 +76,7 @@ jobs:
|
||||
|
||||
- name: rtl
|
||||
if: github.event.label.name == 'rtl'
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'add-assignees'
|
||||
assignees: 'xrkffgg'
|
||||
|
14
.github/workflows/issue-open-check.yml
vendored
14
.github/workflows/issue-open-check.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
- name: check invalid
|
||||
if: (contains(github.event.issue.body, 'ant-design-issue-helper') == false) && (steps.checkUser.outputs.result == 'false')
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment,add-labels,close-issue'
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
@ -27,7 +27,7 @@ jobs:
|
||||
你好 @${{ github.event.issue.user.login }},为了能够进行高效沟通,我们对 issue 有一定的格式要求,你的 issue 因为不符合要求而被自动关闭。你可以通过 [issue 助手](http://new-issue.ant.design) 来创建 issue 以方便我们定位错误。谢谢配合!
|
||||
|
||||
- name: check website
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
id: checkid
|
||||
with:
|
||||
actions: 'check-issue'
|
||||
@ -37,7 +37,7 @@ jobs:
|
||||
|
||||
- name: deal website
|
||||
if: steps.checkid.outputs.check-result == 'true'
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment,close-issue'
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
@ -63,15 +63,15 @@ jobs:
|
||||
|
||||
- name: check ie
|
||||
if: contains(github.event.issue.body, 'ant-design-issue-helper') == true && contains(github.event.issue.title, 'IE9') == true || contains(github.event.issue.title, 'IE 9') == true || contains(github.event.issue.title, 'IE10') == true || contains(github.event.issue.title, 'IE 10') == true || contains(github.event.issue.title, 'IE11') == true || contains(github.event.issue.title, 'IE 11') == true || contains(github.event.issue.title, 'Internet Explorer') == true || contains(github.event.issue.body, 'IE9') == true || contains(github.event.issue.body, 'IE 9') == true || contains(github.event.issue.body, 'IE10') == true || contains(github.event.issue.body, 'IE 10') == true || contains(github.event.issue.body, 'IE11') == true || contains(github.event.issue.body, 'IE 11') == true || contains(github.event.issue.body, 'Internet Explorer') == true
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'add-labels'
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
labels: 'IE | Firefox | Safari,Internet Explorer'
|
||||
|
||||
- name: check ie
|
||||
if: contains(github.event.issue.body, 'ant-design-issue-helper') == true && contains(github.event.issue.title, 'IE9') == true || contains(github.event.issue.title, 'IE 9') == true || contains(github.event.issue.title, 'IE10') == true || contains(github.event.issue.title, 'IE 10') == true || contains(github.event.issue.title, 'IE11') == true || contains(github.event.issue.title, 'IE 11') == true || contains(github.event.issue.title, 'Internet Explorer') == true || contains(github.event.issue.body, 'IE9') == true || contains(github.event.issue.body, 'IE 9') == true || contains(github.event.issue.body, 'IE10') == true || contains(github.event.issue.body, 'IE 10') == true
|
||||
uses: actions-cool/issues-helper@v2
|
||||
- name: check ie11-
|
||||
if: contains(github.event.issue.body, 'ant-design-issue-helper') == true && contains(github.event.issue.title, 'IE9') == true || contains(github.event.issue.title, 'IE 9') == true || contains(github.event.issue.title, 'IE10') == true || contains(github.event.issue.title, 'IE 10') == true || contains(github.event.issue.body, 'IE9') == true || contains(github.event.issue.body, 'IE 9') == true || contains(github.event.issue.body, 'IE10') == true || contains(github.event.issue.body, 'IE 10') == true
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment, close-issue'
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
|
2
.github/workflows/issue-remove-inactive.yml
vendored
2
.github/workflows/issue-remove-inactive.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
steps:
|
||||
- name: remove inactive
|
||||
if: github.event.issue.state == 'open' && github.actor == github.event.issue.user.login
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'remove-labels'
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
|
2
.github/workflows/pr-check-ci.yml
vendored
2
.github/workflows/pr-check-ci.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
with:
|
||||
filter-label: 'BranchAutoMerge'
|
||||
filter-creator-authority: 'write'
|
||||
filter-head-ref: 'master, feature, master-merge-feature, feature-merge-master'
|
||||
filter-head-ref: 'master, feature, next, master-merge-feature, feature-merge-master, next-merge-master'
|
||||
filter-support-fork: false
|
||||
skip-run-names: 'deploy preview, pr-check-ci, build preview failed, suggest-related-links'
|
||||
conflict-review-body: '😅 This branch has conflicts that must be resolved!'
|
||||
|
2
.github/workflows/pr-check-merge.yml
vendored
2
.github/workflows/pr-check-merge.yml
vendored
@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: (github.event.pull_request.head.ref == 'feature' || github.event.pull_request.head.ref == 'master') && github.event.pull_request.head.user.login == 'ant-design'
|
||||
steps:
|
||||
- uses: actions-cool/issues-helper@v2
|
||||
- uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
issue-number: ${{ github.event.number }}
|
||||
|
17
.github/workflows/release-helper.yml
vendored
17
.github/workflows/release-helper.yml
vendored
@ -16,25 +16,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: make release
|
||||
uses: actions-cool/release-helper@v1
|
||||
uses: actions-cool/release-helper@v2
|
||||
with:
|
||||
triger: 'tag'
|
||||
changelogs: 'CHANGELOG.en-US.md, CHANGELOG.zh-CN.md'
|
||||
branch: 'master'
|
||||
dingding-token: ${{ secrets.DINGDING_BOT_TOKEN }}
|
||||
dingding-msg: 'CHANGELOG.zh-CN.md'
|
||||
msg-poster: 'https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*zx7LTI_ECSAAAAAAAAAAAABkARQnAQ'
|
||||
msg-footer: '💬 前往 [**Ant Design Releases**]({{url}}) 查看更新日志'
|
||||
prettier: true
|
||||
prerelease-filter: '-, a, b, A, B'
|
||||
|
||||
- name: make release - Bigfish
|
||||
uses: actions-cool/release-helper@v1
|
||||
with:
|
||||
triger: 'tag'
|
||||
changelogs: 'CHANGELOG.en-US.md, CHANGELOG.zh-CN.md'
|
||||
branch: 'master'
|
||||
dingding-token: ${{ secrets.DINGDING_BOT_BIGFISH_TOKEN }}
|
||||
dingding-token: ${{ secrets.DINGDING_BOT_TOKEN }} ${{ secrets.DINGDING_BOT_BIGFISH_TOKEN }}
|
||||
dingding-msg: 'CHANGELOG.zh-CN.md'
|
||||
msg-poster: 'https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*zx7LTI_ECSAAAAAAAAAAAABkARQnAQ'
|
||||
msg-footer: '💬 前往 [**Ant Design Releases**]({{url}}) 查看更新日志'
|
||||
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -492,7 +492,7 @@ jobs:
|
||||
run: npx lessc --js ./dist/antd.variable.less
|
||||
|
||||
- name: lessc component
|
||||
run: npx lessc --js ./es/button/style/index.less
|
||||
run: npx lessc --js ./es/input/style/index.less
|
||||
|
||||
- name: lessc mixins
|
||||
run: npx lessc --js ./es/style/mixins/index.less
|
||||
|
3
.github/workflows/verify-files-modify.yml
vendored
3
.github/workflows/verify-files-modify.yml
vendored
@ -14,10 +14,11 @@ jobs:
|
||||
forbid-paths: '.github/, scripts/'
|
||||
forbid-files: 'CHANGELOG.zh-CN.md, CHANGELOG.en-US.md, LICENSE'
|
||||
skip-verify-authority: 'write'
|
||||
assignees: 'afc163, zombieJ, xrkffgg'
|
||||
assignees: 'afc163, zombieJ, xrkffgg, MadCcc'
|
||||
comment: |
|
||||
Hi @${{ github.event.pull_request.user.login }}. Thanks for your contribution. The path `.github/` or `scripts/` and `CHANGELOG` `package.json` is only maintained by team members. This current PR will be closed and team members will help on this.
|
||||
close: true
|
||||
set-failed: false
|
||||
|
||||
- name: verify-less
|
||||
uses: actions-cool/verify-files-modify@v1
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -67,3 +67,5 @@ __image_snapshots__/
|
||||
/jest-stare
|
||||
/imageSnapshots
|
||||
/imageDiffSnapshots
|
||||
|
||||
.devcontainer*
|
||||
|
@ -8,6 +8,25 @@
|
||||
"plugins": ["stylelint-declaration-block-no-ignored-properties"],
|
||||
"rules": {
|
||||
"function-name-case": ["lower", { "ignoreFunctions": ["/colorPalette/"] }],
|
||||
"function-no-unknown": [
|
||||
true,
|
||||
{
|
||||
"ignoreFunctions": [
|
||||
"fade",
|
||||
"tint",
|
||||
"darken",
|
||||
"ceil",
|
||||
"fadein",
|
||||
"floor",
|
||||
"unit",
|
||||
"shade",
|
||||
"lighten",
|
||||
"percentage",
|
||||
"-",
|
||||
"~`colorPalette"
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-descending-specificity": null,
|
||||
"no-invalid-position-at-import-rule": null,
|
||||
"declaration-empty-line-before": null,
|
||||
|
@ -15,6 +15,125 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.19.2
|
||||
|
||||
`2022-03-13`
|
||||
|
||||
- 🐞 Fix Dropdown not auto adjust placement when position on the edge of window. [#34390](https://github.com/ant-design/ant-design/pull/34390)
|
||||
- 💄 Change PageHeader elements margin from `12px` to `8px` inside `extra`. [#34428](https://github.com/ant-design/ant-design/pull/34428)
|
||||
- 🛠 Export css variable function in `antd/es/config-provider` folder to enable ssr requirement. [#34436](https://github.com/ant-design/ant-design/pull/34436)
|
||||
- 🛠 Refactor Menu with React hooks. [#34433](https://github.com/ant-design/ant-design/pull/34433)
|
||||
- Input
|
||||
- 💄 Fix Input font-size when `size` is large. [#34381](https://github.com/ant-design/ant-design/pull/34381)
|
||||
- 💄 Fix Input.Group wrong border when status is error. [#34412](https://github.com/ant-design/ant-design/pull/34412)
|
||||
- Form
|
||||
- 🐞 Fix Form.Item removed in `form.validateFields` throw `Can't perform a React state update on an unmounted component` warning. [#34405](https://github.com/ant-design/ant-design/pull/34405)
|
||||
- 🐞 Fix to Form that `initialValues` would change if `preserve` is false. [#34411](https://github.com/ant-design/ant-design/pull/34411)
|
||||
- Tooltip
|
||||
- 💄 Fix Tooltip width in Safari. [#34415](https://github.com/ant-design/ant-design/pull/34415) [@jiandandkl](https://github.com/jiandandkl)
|
||||
- 💄 Fix arrow size of Tooltip/Popover/Popconfirm. [#34407](https://github.com/ant-design/ant-design/pull/34407)
|
||||
- 💄 Remove Collapse bottom border in simple style. [#34366](https://github.com/ant-design/ant-design/pull/34366) [@PanStar](https://github.com/PanStar)
|
||||
- TypeScript
|
||||
- 🤖 Fix Input `data-*` type definition. [#34410](https://github.com/ant-design/ant-design/pull/34410) [@GitKou](https://github.com/GitKou)
|
||||
- 🤖 Fix Transfer `footer` type definition. [#34337](https://github.com/ant-design/ant-design/pull/34337) [@zomixi](https://github.com/zomixi)
|
||||
|
||||
## 4.19.1
|
||||
|
||||
`2022-03-08`
|
||||
|
||||
- 🐞 Fix less compile error related to custom status. [#34350](https://github.com/ant-design/ant-design/pull/34350)
|
||||
- 🐞 Fix error `ReferenceError: colorPalette is not defined` when customize theme.
|
||||
- 🐞 Fix error `Error: Invalid class or id selector syntax` when import `antd/dist/antd.css`.
|
||||
- 🐞 Fix Input.Passowrd icon color in site. [#34354](https://github.com/ant-design/ant-design/pull/34354)
|
||||
- 🐞 Fix ConfigProvider `csp` sometime not effect on Icon. [#34356](https://github.com/ant-design/ant-design/pull/34356)
|
||||
|
||||
## 4.19.0
|
||||
|
||||
`2022-03-08`
|
||||
|
||||
- 💄 Optimize arrow style for some components. [#33710](https://github.com/ant-design/ant-design/pull/33710)
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/27722486/157088587-ca49cc29-bf25-42d1-8c14-020b5501c62e.png" width="500" />
|
||||
|
||||
- Input
|
||||
- 🛠 Refactor Input with rc-input. [#34206](https://github.com/ant-design/ant-design/pull/34206)
|
||||
- Attention: The type and value of `ref` is modified because of refactoring from class component to function component. You can still get DOM node from `input`, and other methods such as `focus` and `blur` mentioned in document are also supported.
|
||||
- 🆕 Support `clearIcon` prop for customizing clear icon. [#34325](https://github.com/ant-design/ant-design/pull/34325)
|
||||
- Table
|
||||
- 🆕 `filterSearch` now support passing function to customize search. [#34085](https://github.com/ant-design/ant-design/pull/34085) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- 🆕 `column.filterDropdown({ clearFilters })` support `clearFilters({ confirm: false, closeDropdown: false })`. [#34120](https://github.com/ant-design/ant-design/pull/34120) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- ⌨️ Table adds `aria-sort` attribute for screen readers. [#33603](https://github.com/ant-design/ant-design/pull/33603) [@dgreene1](https://github.com/dgreene1)
|
||||
- 🐞 Fix Table filters select-all Checkbox not changed when select item. [#34295](https://github.com/ant-design/ant-design/pull/34295)
|
||||
- 🆕 Data entry components add `status` prop to support custom status.
|
||||
|
||||
- Transfer [#34098](https://github.com/ant-design/ant-design/pull/34098)
|
||||
- AutoComplete [#34096](https://github.com/ant-design/ant-design/pull/34096)
|
||||
- TreeSelect [#34093](https://github.com/ant-design/ant-design/pull/34093)
|
||||
- Cascader [#34086](https://github.com/ant-design/ant-design/pull/34086)
|
||||
- Select [#34084](https://github.com/ant-design/ant-design/pull/34084)
|
||||
- DatePicker and TimePicker [#34073](https://github.com/ant-design/ant-design/pull/34073)
|
||||
- Mentions [#34071](https://github.com/ant-design/ant-design/pull/34071)
|
||||
- InputNumber [#34042](https://github.com/ant-design/ant-design/pull/34042)
|
||||
- Input [#33995](https://github.com/ant-design/ant-design/pull/33995)
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/27722486/157089015-f96b0153-2cc4-4e04-94d6-e0e4b195d5d1.png" width="500" />
|
||||
|
||||
- 🆕 InputNumber supports `controls={{ upIcon, downIcon }}` to customize icon up and down. [#33914](https://github.com/ant-design/ant-design/pull/33914) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- 🆕 Notification `placement` support `top` / `bottom` [#33871](https://github.com/ant-design/ant-design/pull/33871) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- 🆕 Select, Cascades, DatePicker now support `placement` property. [#33641](https://github.com/ant-design/ant-design/pull/33541) [@ONLY-yours](https://github.com/ONLY-yours)
|
||||
- 🆕 Dropdown support `arrow={{ pointAtCenter: true }}` to make arrow point at center. And `top` `bottom` placement are also supported. [#33658](https://github.com/ant-design/ant-design/pull/33658)
|
||||
- 🆕 Skeleton.Input Adds `block` prop. [#33672](https://github.com/ant-design/ant-design/pull/33672) [@woochanleee](https://github.com/woochanleee)
|
||||
- 🆕 Move TimePicker `disabledHours`, `disabledMinutes`, `disabledSeconds` into `disabledTime` to align with DatePicker. [#33503](https://github.com/ant-design/ant-design/pull/33503)
|
||||
- 💄 Modify the color of some borders and the background color of the progress bar to be transparent to suit the colored background. [#33506](https://github.com/ant-design/ant-design/pull/33506)
|
||||
- 💄 Space support custom children `key`. [#33607](https://github.com/ant-design/ant-design/pull/33607) [@qin20](https://github.com/qin20)
|
||||
- 🐞 Fix `Typography.Title` didn't keep font size when become editable. [#34169](https://github.com/ant-design/ant-design/pull/34169) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- 🐞 Fix Form.Item throw warning `React does not recognize the requiredMark prop on a DOM element`. [#34323](https://github.com/ant-design/ant-design/pull/34323)
|
||||
|
||||
## 4.18.9
|
||||
|
||||
`2022-02-28`
|
||||
|
||||
- 🆕 New theme less variable for Radio, Divider, Modal, Dropdown, Drawer. [#34194](https://github.com/ant-design/ant-design/pull/34194) [#34187](https://github.com/ant-design/ant-design/pull/34187) [#34191](https://github.com/ant-design/ant-design/pull/34191) [#34189](https://github.com/ant-design/ant-design/pull/34189) [#34188](https://github.com/ant-design/ant-design/pull/34188) [@qdzhaoxiaodao](https://github.com/qdzhaoxiaodao)
|
||||
- 💄 Fix Dropdown item wrap style when text is too long. [#34177](https://github.com/ant-design/ant-design/pull/34177)
|
||||
- TypeScript
|
||||
- 🐞 Fix Upload `onChange` parameter generic passing. [#34161](https://github.com/ant-design/ant-design/pull/34161) [@wangcch](https://github.com/wangcch)
|
||||
|
||||
## 4.18.8
|
||||
|
||||
`2022-02-21`
|
||||
|
||||
- 🐞 Fix `getContainer` config not working bug when called multi-times via `message.config`. [#34123](https://github.com/ant-design/ant-design/pull/34123) [@TrickyPi](https://github.com/TrickyPi)
|
||||
- 🐞 Fix invalid context value cache in Menu component. [#34121](https://github.com/ant-design/ant-design/pull/34121) [@mrwd2009](https://github.com/mrwd2009)
|
||||
- 🐞 Fix ConfigProvider config theme on server side crash, and warning for useless in SSR instead. [#34118](https://github.com/ant-design/ant-design/pull/34118)
|
||||
- Table
|
||||
- ⚡️ Fix Table render twice on first mount. [#34106](https://github.com/ant-design/ant-design/pull/34106)
|
||||
- ⚡️ Optimized Table rendering performance, now will skip useless rendering when deprecated `column.render: () => { children, props }` method is not used. [#34075](https://github.com/ant-design/ant-design/pull/34075)
|
||||
- 🐞 Fix incorrect copy text of Typography after children is updated when enable `copyable`. [#34034](https://github.com/ant-design/ant-design/pull/34034) [@opopeieie](https://github.com/opopeieie)
|
||||
- ⚡️ Optimize Avatar, List, Pagination, Steps to avoid additional render on mount if unnecessary. [34122](https://github.com/ant-design/ant-design/pull/34122)
|
||||
- 💄 Fix Form broken style when Select item is too long in horizontal layout. [#34117](https://github.com/ant-design/ant-design/pull/34117)
|
||||
- 🇸🇰 Improve texts for Table, Form and Modal in `sk_SK`. [#34061](https://github.com/ant-design/ant-design/pull/34061) [@xseman](https://github.com/xseman)
|
||||
- TypeScript
|
||||
- 🤖 Export `SiderProps` type from Layout component. [#34137](https://github.com/ant-design/ant-design/pull/34137) [@Picsong](https://github.com/Picsong)
|
||||
|
||||
## 4.18.7
|
||||
|
||||
`2022-02-14`
|
||||
|
||||
- Typography
|
||||
- 🛠 Fix Typography `useLayoutEffect` warning in SSR. [#33818](https://github.com/ant-design/ant-design/pull/33818) [@SoYoung210](https://github.com/SoYoung210)
|
||||
- 🐞 Fix Typography with `ellipsis` makes screen show the scroll bar in some case. [#34007](https://github.com/ant-design/ant-design/pull/34007)
|
||||
- 🐞 Typography copy click event is now `stopPropagation` by default. [#33998](https://github.com/ant-design/ant-design/pull/33998) [@linxianxi](https://github.com/linxianxi)
|
||||
- 🐞 Fix Typography edit & copy button not trigger by enter key. [#33976](https://github.com/ant-design/ant-design/pull/33976) [@mrwd2009](https://github.com/mrwd2009)
|
||||
- 🐞 Fix Form `undefined` text of min/max validators in pl_PL locale. [#34024](https://github.com/ant-design/ant-design/pull/34024) [@MichalPodeszwa](https://github.com/MichalPodeszwa)
|
||||
- 🐞 Fix Input.TextArea cut text logic when `maxLength` configured. [#33910](https://github.com/ant-design/ant-design/pull/33910) [@chenyizhongx](https://github.com/chenyizhongx)
|
||||
- 💄 Button with `type=default` will provide `.ant-btn-default` className. [#34013](https://github.com/ant-design/ant-design/pull/34013)
|
||||
- 💄 Improve Menu `:focus-visible` style. [#34008](https://github.com/ant-design/ant-design/pull/34008)
|
||||
- 💄 Fix Pagination and Rate style problem in Safari. [#34002](https://github.com/ant-design/ant-design/pull/34002)
|
||||
- 💄 Fix Row and Col component styles when using prefixCls. [#33969](https://github.com/ant-design/ant-design/pull/33969) [@mic-web](https://github.com/mic-web)
|
||||
- 🐞 Fix Timeline icons with custom color not working. [#33951](https://github.com/ant-design/ant-design/pull/33951) [@MadCcc](https://github.com/MadCcc)
|
||||
- TypeScript
|
||||
- 🤖 Optimize Cascader `onChange` definition with `multiple` prop. [#33947](https://github.com/ant-design/ant-design/pull/33947) [@babycannotsay](https://github.com/babycannotsay)
|
||||
|
||||
## 4.18.6
|
||||
|
||||
`2022-02-07`
|
||||
|
@ -13,7 +13,127 @@ timeline: true
|
||||
- 次版本号:每月发布一个带有新特性的向下兼容的版本。
|
||||
- 主版本号:含有破坏性更新和新特性,不在发布周期内。
|
||||
|
||||
---
|
||||
--
|
||||
|
||||
## 4.19.2
|
||||
|
||||
`2022-03-13`
|
||||
|
||||
- 🐞 修复 Dropdown 在边界情况下不会自动调整展示位置的问题。[#34390](https://github.com/ant-design/ant-design/pull/34390)
|
||||
- 💄 缩小 PageHeader `extra` 内元素间距为 `8px`。[#34428](https://github.com/ant-design/ant-design/pull/34428)
|
||||
- 🛠 导出 antd/es/config-provider 目录下的 css variable 函数以支持 ssr 的需求。[#34436](https://github.com/ant-design/ant-design/pull/34436)
|
||||
- 🛠 使用 React hooks 重构 Menu。[#34433](https://github.com/ant-design/ant-design/pull/34433)
|
||||
- Input
|
||||
- 💄 修复大尺寸 Input 的字号问题。[#34381](https://github.com/ant-design/ant-design/pull/34381)
|
||||
- 💄 修复 Input.Group 多余的错误边框样式。[#34412](https://github.com/ant-design/ant-design/pull/34412)
|
||||
- Form
|
||||
- 🐞 修复 Form.Item 在 `form.validateFields` 中移除时抛出 `Can't perform a React state update on an unmounted component` 警告的问题。[#34405](https://github.com/ant-design/ant-design/pull/34405)
|
||||
- 🐞 修复 Form 组件当 `preserve` 为 `false` 时 `initialValues` 会被更改的问题。[#34411](https://github.com/ant-design/ant-design/pull/34411)
|
||||
- Tooltip
|
||||
- 💄 修复 Tooltip 在 Safari 下的内容宽度问题。[#34415](https://github.com/ant-design/ant-design/pull/34415) [@jiandandkl](https://github.com/jiandandkl)
|
||||
- 💄 修复 Tooltip/Popover/Popconfirm 等组件箭头大小问题。[#34407](https://github.com/ant-design/ant-design/pull/34407)
|
||||
- 💄 优化 Collapse 简洁模式的底边框。[#34366](https://github.com/ant-design/ant-design/pull/34366) [@PanStar](https://github.com/PanStar)
|
||||
- TypeScript
|
||||
- 🤖 修复 Input 不支持 `data-*` TS 定义的问题。[#34410](https://github.com/ant-design/ant-design/pull/34410) [@GitKou](https://github.com/GitKou)
|
||||
- 🤖 修复 Transfer 的 `footer` 类型定义。[#34337](https://github.com/ant-design/ant-design/pull/34337) [@zomixi](https://github.com/zomixi)
|
||||
|
||||
## 4.19.1
|
||||
|
||||
`2022-03-08`
|
||||
|
||||
- 🐞 修复自定义状态相关的 less 编译错误。[#34350](htps://github.com/ant-dign/ant-design/pull/34350)
|
||||
- 🐞 修复使用定制主题时 less 编译提示 `ReferenceError: colorPalette is not defined` 错误。
|
||||
- 🐞 修复引入 `antd/dist/antd.css` 时提示 `Error: Invalid class or id selector syntax` 错误。
|
||||
- 🐞 修复 Input.Passowrd 图标颜色错误。[#34354](https://github.com/ant-design/ant-design/pull/34354)
|
||||
- 🐞 修复 ConfigProvider `csp` 有时在 Icon 上不会生效的问题。[#34356](https://github.com/ant-design/ant-design/pull/34356)
|
||||
|
||||
## 4.19.0
|
||||
|
||||
`2022-03-08`
|
||||
|
||||
- 💄 优化部分组件箭头样式。[#33710](https://github.com/ant-design/ant-design/pull/33710)
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/27722486/157088587-ca49cc29-bf25-42d1-8c14-020b5501c62e.png" width="500" />
|
||||
|
||||
- Input
|
||||
- 🛠 引入 rc-input 重构 Input 组件为 function component。[#34206](https://github.com/ant-design/ant-design/pull/34206)
|
||||
- 注意:由于从 class component 变为 function component,Input 组件的 `ref` 类型及内容已经更新,可以通过 `import { InputRef } from 'antd'` 引入。其中的 `input` 属性作为获取 DOM 的途径被保留,同时支持 `focus` 和 `blur` 等文档中支持的方法。
|
||||
- 🆕 新增 `clearIcon` 属性,支持自定义清除按钮。[#34325](https://github.com/ant-design/ant-design/pull/34325)
|
||||
- Table
|
||||
- 🆕 `column.filterSearch` 属性现在支持返回一个函数用于自定义搜索条件。[#34085](https://github.com/ant-design/ant-design/pull/34085) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- 🆕 `column.filterDropdown({ clearFilters })` 支持参数 `clearFilters({ confirm: false, closeDropdown: false })` 控制筛选。[#34120](https://github.com/ant-design/ant-design/pull/34120) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- ⌨️ 增加 `aria-sort` 属性以优化屏幕阅读器的使用体验。[#33603](https://github.com/ant-design/ant-design/pull/33603) [@dgreene1](https://github.com/dgreene1)
|
||||
- 🐞 修复 Table 列筛选器中选择全部 Checkbox 状态问题。[#34295](https://github.com/ant-design/ant-design/pull/34295)
|
||||
- 🆕 表单组件新增 `status` 属性以支持自定义状态。
|
||||
|
||||
- Transfer [#34098](https://github.com/ant-design/ant-design/pull/34098)
|
||||
- AutoComplete [#34096](https://github.com/ant-design/ant-design/pull/34096)
|
||||
- TreeSelect [#34093](https://github.com/ant-design/ant-design/pull/34093)
|
||||
- Cascader [#34086](https://github.com/ant-design/ant-design/pull/34086)
|
||||
- Select [#34084](https://github.com/ant-design/ant-design/pull/34084)
|
||||
- DatePicker 和 TimePicker [#34073](https://github.com/ant-design/ant-design/pull/34073)
|
||||
- Mentions [#34071](https://github.com/ant-design/ant-design/pull/34071)
|
||||
- InputNumber [#34042](https://github.com/ant-design/ant-design/pull/34042)
|
||||
- Input [#33995](https://github.com/ant-design/ant-design/pull/33995)
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/27722486/157089015-f96b0153-2cc4-4e04-94d6-e0e4b195d5d1.png" width="500" />
|
||||
|
||||
- 🆕 InputNumber 组件支持 `controls={{ upIcon, downIcon }}` 用于自定义上下图标。[#33914](https://github.com/ant-design/ant-design/pull/33914) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- 🆕 Notification 组件弹窗位置新增支持 `top` / `bottom`。[#33871](https://github.com/ant-design/ant-design/pull/33871) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- 🆕 Select、Cascader、DatePicker 等组件新增 `placement` 用于自定义弹层方向。[#33641](https://github.com/ant-design/ant-design/pull/33541) [@ONLY-yours](https://github.com/ONLY-yours)
|
||||
- 🆕 Dropdown 组件支持 `arrow={{ pointAtCenter: true }}` 用于指向元素正中间,并且新增 `top` `bottom` 两种 `placement` 位置。[#33658](https://github.com/ant-design/ant-design/pull/33658)
|
||||
- 🆕 Skeleton.Input 添加 `block` 属性。[#33672](https://github.com/ant-design/ant-design/pull/33672) [@woochanleee](https://github.com/woochanleee)
|
||||
- 🆕 合并 TimePicker `disabledHours`、`disabledMinutes`、`disabledSeconds` 至 `disabledTime` 以保持与 DatePicker 接口一致性。[#33503](https://github.com/ant-design/ant-design/pull/33503)
|
||||
- 💄 修改部分边框颜色和进度条的背景色为透明色以适应有色背景。[#33506](https://github.com/ant-design/ant-design/pull/33506)
|
||||
- 💄 Space 支持自定义 children 的 `key`。[#33607](https://github.com/ant-design/ant-design/pull/33607) [@qin20](https://github.com/qin20)
|
||||
- 🐞 修复 Typography.Title 进入编辑模式时大小不一致的问题。[#34169](https://github.com/ant-design/ant-design/pull/34169) [@heiyu4585](https://github.com/heiyu4585)
|
||||
- 🐞 修复 Form.Item 抛出 `React does not recognize the requiredMark prop on a DOM element` 的问题。[#34323](https://github.com/ant-design/ant-design/pull/34323)
|
||||
|
||||
## 4.18.9
|
||||
|
||||
`2022-02-28`
|
||||
|
||||
- 🆕 新增 Radio、Divider、Modal、Dropdown、Drawer 主题变量。[#34194](https://github.com/ant-design/ant-design/pull/34194) [#34187](https://github.com/ant-design/ant-design/pull/34187) [#34191](https://github.com/ant-design/ant-design/pull/34191) [#34189](https://github.com/ant-design/ant-design/pull/34189) [#34188](https://github.com/ant-design/ant-design/pull/34188) [@qdzhaoxiaodao](https://github.com/qdzhaoxiaodao)
|
||||
- 🐞 修复 Form 组件当 `preserve` 为 `false` 时 `initialValues` 会被更改的问题。[#34153](https://github.com/ant-design/ant-design/pull/34153)
|
||||
- 💄 修复 Dropdown 菜单项文本太长没有换行的问题。[#34177](https://github.com/ant-design/ant-design/pull/3417)
|
||||
- TypeScript
|
||||
- 🐞 修复 Upload `onChange` 参数泛型传递。[#34161](https://github.com/ant-design/ant-design/pull/34161) [@wangcch](https://github.com/wangcch)
|
||||
|
||||
## 4.18.8
|
||||
|
||||
`2022-02-21`
|
||||
|
||||
- 🐞 修复 `message.config` 多次配置 `getContainer` 时无法生效的问题。[#34123](https://github.com/ant-design/ant-design/pull/34123) [@TrickyPi](https://github.com/TrickyPi)
|
||||
- 🐞 修复 Menu 组件中无效的缓存逻辑。[#34121](https://github.com/ant-design/ant-design/pull/34121) [@mrwd2009](https://github.com/mrwd2009)
|
||||
- 🐞 修复 ConfigProvider 在服务端配置主题会崩溃的问题,同时现在会提示动态主题于 SSR 上无效。[#34118](https://github.com/ant-design/ant-design/pull/34118)
|
||||
- Table
|
||||
- ⚡️ 修复 Table 在首次加载时会渲染两次的问题。[#34106](https://github.com/ant-design/ant-design/pull/34106)
|
||||
- ⚡️ 优化 Table 渲染性能,现在不使用废弃 `column.render: () => { children, props }` 方法时默认会跳过无用渲染。[#34075](https://github.com/ant-design/ant-design/pull/34075)
|
||||
- 🐞 修复 Typography 启用 `copyable` 时 `children` 内容变化后复制内容没变的问题。[#34034](https://github.com/ant-design/ant-design/pull/34034) [@opopeieie](https://github.com/opopeieie)
|
||||
- ⚡️ 优化 Avatar、List、Pagination、Steps 以防止初始化时非必要的额外渲染。[34122](https://github.com/ant-design/ant-design/pull/34122)
|
||||
- 💄 修复 Form 下 Select 内容太长导致布局换行的问题。[#34117](https://github.com/ant-design/ant-design/pull/34117)
|
||||
- 🇸🇰 完善 `sk-SK` 中 Table、Form、Modal 的文案。[#34061](https://github.com/ant-design/ant-design/pull/34061) [@xseman](https://github.com/xseman)
|
||||
- TypeScript
|
||||
- 🤖 导出 Layout 组件的 `SiderProps` 类型。[#34137](https://github.com/ant-design/ant-design/pull/34137) [@Picsong](https://github.com/Picsong)
|
||||
|
||||
## 4.18.7
|
||||
|
||||
`2022-02-14`
|
||||
|
||||
- Typography
|
||||
- 🛠 修复 Typography 在 SSR 渲染时警告 `useLayoutEffect` 的问题。[#33818](https://github.com/ant-design/ant-design/pull/33818) [@SoYoung210](https://github.com/SoYoung210)
|
||||
- 🐞 修复 Typography 配置 `ellipsis` 后在某些情况下会出现滚动条的问题。[#34007](https://github.com/ant-design/ant-design/pull/34007)
|
||||
- 🐞 Typography 复制按钮点击事件不在冒泡。[#33998](https://github.com/ant-design/ant-design/pull/33998) [@linxianxi](https://github.com/linxianxi)
|
||||
- 🐞 修复 Typography 中编辑和拷贝按钮无法响应 Enter 按键的问题。[#33976](https://github.com/ant-design/ant-design/pull/33976) [@mrwd2009](https://github.com/mrwd2009)
|
||||
- 🐞 修复 Form 波兰语中表单校验部分文案未定义的问题。[#34024](https://github.com/ant-design/ant-design/pull/34024) [@MichalPodeszwa](https://github.com/MichalPodeszwa)
|
||||
- 🐞 修复 Input.TextArea 设置 `maxLength` 时光标位置会影响超出部分截取的问题。[#33910](https://github.com/ant-design/ant-design/pull/33910) [@chenyizhongx](https://github.com/chenyizhongx)
|
||||
- 💄 Button 对于 `type=default` 也会提供 `.ant-btn-default` 的样式类名。[#34013](https://github.com/ant-design/ant-design/pull/34013)
|
||||
- 💄 优化 Menu `:focus-visible` 的样式。[#34008](https://github.com/ant-design/ant-design/pull/34008)
|
||||
- 💄 修复 Pagination 和 Rate 在 Safari 下部分样式丢失的问题,比如分页按钮禁用样式失效。[#34002](https://github.com/ant-design/ant-design/pull/34002)
|
||||
- 💄 修复 Row 与 Col 在配置 `prefixCls` 的样式问题。[#33969](https://github.com/ant-design/ant-design/pull/33969) [@mic-web](https://github.com/mic-web)
|
||||
- 🐞 修复 Timeline 的自定义图标颜色无效的问题。[#33951](https://github.com/ant-design/ant-design/pull/33951) [@MadCcc](https://github.com/MadCcc)
|
||||
- TypeScript
|
||||
- 🤖 优化 Cascader `multiple` 属性对应的 `onChange` 类型推断。[#33947](https://github.com/ant-design/ant-design/pull/33947) [@babycannotsay](https://github.com/babycannotsay)
|
||||
|
||||
## 4.18.6
|
||||
|
||||
@ -48,7 +168,7 @@ timeline: true
|
||||
|
||||
- Typography
|
||||
- ⚡️ 优化 Typography 在配置 `tooltip` 时优先使用原生省略样式以提升性能。[#33669](https://github.com/ant-design/ant-design/pull/33669)
|
||||
- 🐞 重构 Typography `ellipsis` 逻辑以修复 `children` 如果消费上游 Context 会报错的问题。 [#33725](https://github.com/ant-design/ant-design/pull/33725)
|
||||
- 🐞 重构 Typography `ellipsis` 逻辑以修复 `children` 如果消费上游 Context 会报错的问题。[#33725](https://github.com/ant-design/ant-design/pull/33725)
|
||||
- Icon
|
||||
- 🐞 修复 `<Icon component={HomeOutlined} />` 和 `<HomeOutlined />` 不对齐的问题。[#33709](https://github.com/ant-design/ant-design/pull/33709)
|
||||
- 🐞 修复 `<Icon component={SyncOutlined} spin />` 抖动的问题。[#33726](https://github.com/ant-design/ant-design/pull/33726) [@JX-Zhuang](https://github.com/JX-Zhuang)
|
||||
@ -274,7 +394,7 @@ timeline: true
|
||||
- 🐞 修复 Button `ghost` 鼠标悬停样式。[#32289](https://github.com/ant-design/ant-design/pull/32289)
|
||||
- 🐞 修复 Button 配置 `loading` 时,无法触发 Tooltip 的问题。[#32158](https://github.com/ant-design/ant-design/pull/32158)
|
||||
- Pagination
|
||||
- 🆕 Pagination 支持定制 `selectComponentClass`。 [#32132](https://github.com/ant-design/ant-design/pull/32132) [@JounQin](https://github.com/JounQin)
|
||||
- 🆕 Pagination 支持定制 `selectComponentClass`。[#32132](https://github.com/ant-design/ant-design/pull/32132) [@JounQin](https://github.com/JounQin)
|
||||
- 💄 Pagination `simple` 属性下中翻页 input 增加 box-shadow。[#32528](https://github.com/ant-design/ant-design/pull/32528) [@chen-jingjie](https://github.com/chen-jingjie)
|
||||
- Upload
|
||||
- 🐞 修复 Upload `listStyle="picture"` 下加载中样式错位的问题。[#32664](https://github.com/ant-design/ant-design/pull/32664)
|
||||
@ -327,7 +447,7 @@ timeline: true
|
||||
- 🤖 修复 Switch `id` 属性定义。[#32237](https://github.com/ant-design/ant-design/pull/32237) [@M-ZubairAhmed](https://github.com/M-ZubairAhmed)
|
||||
- 🤖 修复 Button 的 `type` 的 TS 类型定义。[#32004](https://github.com/ant-design/ant-design/pull/32004) [@jaredleechn](https://github.com/jaredleechn)
|
||||
- 🤖 完备 Pagination 的 `locale` TS 类型定义。[[#32128](https://github.com/ant-design/ant-design/pull/32128) [@JounQin](https://github.com/JounQin)
|
||||
- 🤖 完善并导出 DropdownButton 的 `DropdownButtonType` TS 类型定义。 [[#31957](https://github.com/ant-design/ant-design/pull/31957) [@Dreamerryao](https://github.com/Dreamerryao)
|
||||
- 🤖 完善并导出 DropdownButton 的 `DropdownButtonType` TS 类型定义。[[#31957](https://github.com/ant-design/ant-design/pull/31957) [@Dreamerryao](https://github.com/Dreamerryao)
|
||||
- 🤖 调整 List 组件 `rowKey` 类型为 React.key。[#32033](https://github.com/ant-design/ant-design/pull/32033) [@lironhl](https://github.com/lironhl)
|
||||
- 🐞 修复 DatePicker `ref` 类型。[#31993](https://github.com/ant-design/ant-design/pull/31993) [@acfasj](https://github.com/acfasj)
|
||||
- 🤖 更新 Drawer 中 `levelMove` 类型定义。[#30714](https://github.com/ant-design/ant-design/pull/30714) [@g0shed](https://github.com/g0shed)
|
||||
@ -376,7 +496,7 @@ timeline: true
|
||||
- 🐞 修复 Progress 环形进度条 `success.strokeColor` 不生效的问题。[#31589](https://github.com/ant-design/ant-design/pull/31589)
|
||||
- 🐞 修复 Select 组件没有忽略 `getRawInputElement` 属性导致的类型报错问题。[#31566](https://github.com/ant-design/ant-design/pull/31566) [@aoilti](https://github.com/aoilti)
|
||||
- 🐞 修复 Pagination 的 `totalBoundaryShowSizeChanger` 属性类型错误。[#31549](https://github.com/ant-design/ant-design/pull/31549) [@Monty-Ma](https://github.com/Monty-Ma)
|
||||
- 🐞 修复 Skeleton.Avatar `className` 重复应用的问题。 [#31536](https://github.com/ant-design/ant-design/pull/31536) [@Greatshock](https://github.com/Greatshock)
|
||||
- 🐞 修复 Skeleton.Avatar `className` 重复应用的问题。[#31536](https://github.com/ant-design/ant-design/pull/31536) [@Greatshock](https://github.com/Greatshock)
|
||||
- 🌐 国际化
|
||||
- 🇹🇷 为 Image 组件中 `预览` 文案增加土耳其语翻译。[#31593](https://github.com/ant-design/ant-design/pull/31593) [@mburakkalkan](https://github.com/mburakkalkan)
|
||||
- 🇰🇷 修复韩语中的错别字。[#31575](https://github.com/ant-design/ant-design/pull/31575) [@chatoo2412](https://github.com/chatoo2412)
|
||||
@ -697,7 +817,7 @@ timeline: true
|
||||
- 🐞 修复 Modal 页脚里使用 href 按钮导致的间距丢失问题。[#29681](https://github.com/ant-design/ant-design/pull/29681) [@n0ruSh](https://github.com/n0ruSh)
|
||||
- 💄 修复 Input 组件配置附件元素时禁用样式异常的问题。[#29670](https://github.com/ant-design/ant-design/pull/29670)
|
||||
- 💄 优化 Form.Item 提示信息的鼠标显示样式。[#29650](https://github.com/ant-design/ant-design/pull/29650)
|
||||
- 🇨🇿 修复 cs_CZ 语言环境中的错字。 [#29675](https://github.com/ant-design/ant-design/pull/29675) [@jvaclavik](https://github.com/jvaclavik)
|
||||
- 🇨🇿 修复 cs_CZ 语言环境中的错字。[#29675](https://github.com/ant-design/ant-design/pull/29675) [@jvaclavik](https://github.com/jvaclavik)
|
||||
- 🇨🇦 添加 fr_CA 语言。[#29748](https://github.com/ant-design/ant-design/pull/29748) [@liufenghua808](https://github.com/liufenghua808)
|
||||
|
||||
## 4.13.1
|
||||
@ -797,7 +917,7 @@ timeline: true
|
||||
- TypeScript
|
||||
- 🤖 更新 Table TypeScript 定义 `dataSource` 至 `readonly`。[#29084](https://github.com/ant-design/ant-design/pull/29084)
|
||||
- Less
|
||||
- 💄 增加 less 变量 `@progress-info-text-color`。 [#28981](https://github.com/ant-design/ant-design/pull/28981) [@yuxuan](https://github.com/yuxuan)
|
||||
- 💄 增加 less 变量 `@progress-info-text-color`。[#28981](https://github.com/ant-design/ant-design/pull/28981) [@yuxuan](https://github.com/yuxuan)
|
||||
|
||||
## 4.11.3
|
||||
|
||||
@ -936,7 +1056,7 @@ timeline: true
|
||||
- 🆕 多选模式下 `maxTagCount` 支持 `responsive`。[#28520](https://github.com/ant-design/ant-design/pull/28520)
|
||||
- 🆕 Slider 新增 range.draggableTrack 以支持范围刻度整体可拖拽。[#28592](https://github.com/ant-design/ant-design/pull/28592)
|
||||
- 🆕 `message` 新增 `onClick` 回调,会在消息被点击时触发。[#28148](https://github.com/ant-design/ant-design/pull/28148) [@ZeroTo0ne](https://github.com/ant-design/ant-design/pull/28148)
|
||||
- 🆕 Descriptions 上可以统一设置 `labelStyle` 和 `contentStyle`。 [#28613](https://github.com/ant-design/ant-design/pull/28613)
|
||||
- 🆕 Descriptions 上可以统一设置 `labelStyle` 和 `contentStyle`。[#28613](https://github.com/ant-design/ant-design/pull/28613)
|
||||
- 🆕 Form 的 `scrollToFirstError` 属性支持设置滚动的位置参数。[#28272](https://github.com/ant-design/ant-design/pull/28272) [@vouis](https://github.com/vouis)
|
||||
- 🆕 Steps 新增 reponsive 属性用于关闭响应式样式。[#28459](https://github.com/ant-design/ant-design/pull/28459)
|
||||
- 🌐 国际化
|
||||
@ -1327,7 +1447,7 @@ timeline: true
|
||||
- 🐞 修复 Form 使用 `help` 时出现的同构问题。[#26542](https://github.com/ant-design/ant-design/pull/26542)
|
||||
- 🐞 修复 Avatar 在 `display: none` 时不会正确缩放 fallback 文字的问题。[#26522](https://github.com/ant-design/ant-design/pull/26522) [@zhangyu1818](https://github.com/zhangyu1818)
|
||||
- TypeScript
|
||||
- 🤖 Col 增加 `ColSize` 增加 `flex` 的定义。 [#26578](https://github.com/ant-design/ant-design/pull/26578) [@blaiz](https://github.com/blaiz)
|
||||
- 🤖 Col 增加 `ColSize` 增加 `flex` 的定义。[#26578](https://github.com/ant-design/ant-design/pull/26578) [@blaiz](https://github.com/blaiz)
|
||||
- 🤖 修复 Tooltip/Popover `children` 定义不接受 ReactNode 的问题。[#26534](https://github.com/ant-design/ant-design/pull/26534)
|
||||
|
||||
## 4.6.2
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import useState from 'rc-util/lib/hooks/useState';
|
||||
import Button from '../button';
|
||||
import { LegacyButtonType, ButtonProps, convertLegacyProps } from '../button/button';
|
||||
import useDestroyed from './hooks/useDestroyed';
|
||||
|
||||
export interface ActionButtonProps {
|
||||
type?: LegacyButtonType;
|
||||
@ -21,8 +21,7 @@ function isThenable(thing?: PromiseLike<any>): boolean {
|
||||
const ActionButton: React.FC<ActionButtonProps> = props => {
|
||||
const clickedRef = React.useRef<boolean>(false);
|
||||
const ref = React.useRef<any>();
|
||||
const isDestroyed = useDestroyed();
|
||||
const [loading, setLoading] = React.useState<ButtonProps['loading']>(false);
|
||||
const [loading, setLoading] = useState<ButtonProps['loading']>(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
let timeoutId: any;
|
||||
@ -45,9 +44,7 @@ const ActionButton: React.FC<ActionButtonProps> = props => {
|
||||
setLoading(true);
|
||||
returnValueOfOnOk!.then(
|
||||
(...args: any[]) => {
|
||||
if (!isDestroyed()) {
|
||||
setLoading(false);
|
||||
}
|
||||
setLoading(false, true);
|
||||
close(...args);
|
||||
clickedRef.current = false;
|
||||
},
|
||||
@ -56,9 +53,7 @@ const ActionButton: React.FC<ActionButtonProps> = props => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e);
|
||||
// See: https://github.com/ant-design/ant-design/issues/6183
|
||||
if (!isDestroyed()) {
|
||||
setLoading(false);
|
||||
}
|
||||
setLoading(false, true);
|
||||
clickedRef.current = false;
|
||||
},
|
||||
);
|
||||
|
17
components/_util/__tests__/capitalize.test.ts
Normal file
17
components/_util/__tests__/capitalize.test.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import capitalize from '../capitalize';
|
||||
|
||||
describe('capitalize', () => {
|
||||
it('should capitalize the first character of a string', () => {
|
||||
expect(capitalize('antd')).toBe('Antd');
|
||||
expect(capitalize('Antd')).toBe('Antd');
|
||||
expect(capitalize(' antd')).toBe(' antd');
|
||||
expect(capitalize('')).toBe('');
|
||||
});
|
||||
|
||||
it('should return the original value when is not a string', () => {
|
||||
expect(capitalize(1 as any)).toBe(1);
|
||||
expect(capitalize(true as any)).toBe(true);
|
||||
expect(capitalize(undefined as any)).toBe(undefined);
|
||||
expect(capitalize(null as any)).toBe(null);
|
||||
});
|
||||
});
|
@ -1,20 +0,0 @@
|
||||
import { mount } from 'enzyme';
|
||||
import React from 'react';
|
||||
import useDestroyed from '../hooks/useDestroyed';
|
||||
|
||||
describe('useMounted', () => {
|
||||
it('should work properly', () => {
|
||||
let isDestroyed = null;
|
||||
|
||||
const AutoUnmounted = () => {
|
||||
isDestroyed = useDestroyed();
|
||||
|
||||
return <div>Mounted</div>;
|
||||
};
|
||||
|
||||
const wrapper = mount(<AutoUnmounted />);
|
||||
expect(isDestroyed()).toBeFalsy();
|
||||
wrapper.unmount();
|
||||
expect(isDestroyed()).toBeTruthy();
|
||||
});
|
||||
});
|
8
components/_util/capitalize.ts
Normal file
8
components/_util/capitalize.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export default function capitalize<T extends string>(str: T): Capitalize<T> {
|
||||
if (typeof str !== 'string') {
|
||||
return str;
|
||||
}
|
||||
|
||||
const ret = str.charAt(0).toUpperCase() + str.slice(1);
|
||||
return ret as Capitalize<T>;
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
export default function getDataOrAriaProps(props: any) {
|
||||
return Object.keys(props).reduce((prev: any, key: string) => {
|
||||
if (
|
||||
(key.substr(0, 5) === 'data-' || key.substr(0, 5) === 'aria-' || key === 'role') &&
|
||||
key.substr(0, 7) !== 'data-__'
|
||||
(key.startsWith('data-') || key.startsWith('aria-') || key === 'role') &&
|
||||
!key.startsWith('data-__')
|
||||
) {
|
||||
prev[key] = props[key];
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export default function useDestroyed() {
|
||||
const mountedRef = React.useRef<boolean>(true);
|
||||
|
||||
React.useEffect(
|
||||
() => () => {
|
||||
mountedRef.current = false;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return () => !mountedRef.current;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { CSSMotionProps, MotionEventHandler, MotionEndEventHandler } from 'rc-motion';
|
||||
import { MotionEvent } from 'rc-motion/lib/interface';
|
||||
import { tuple } from './type';
|
||||
|
||||
// ================== Collapse Motion ==================
|
||||
const getCollapsedHeight: MotionEventHandler = () => ({ height: 0, opacity: 0 });
|
||||
@ -25,11 +26,22 @@ const collapseMotion: CSSMotionProps = {
|
||||
motionDeadline: 500,
|
||||
};
|
||||
|
||||
const SelectPlacements = tuple('bottomLeft', 'bottomRight', 'topLeft', 'topRight');
|
||||
export type SelectCommonPlacement = typeof SelectPlacements[number];
|
||||
|
||||
const getTransitionDirection = (placement: SelectCommonPlacement | undefined) => {
|
||||
if (placement !== undefined && (placement === 'topLeft' || placement === 'topRight')) {
|
||||
return `slide-down`;
|
||||
}
|
||||
return `slide-up`;
|
||||
};
|
||||
|
||||
const getTransitionName = (rootPrefixCls: string, motion: string, transitionName?: string) => {
|
||||
if (transitionName !== undefined) {
|
||||
return transitionName;
|
||||
}
|
||||
return `${rootPrefixCls}-${motion}`;
|
||||
};
|
||||
export { getTransitionName };
|
||||
|
||||
export { getTransitionName, getTransitionDirection };
|
||||
export default collapseMotion;
|
||||
|
@ -42,6 +42,7 @@ export default function getPlacements(config: PlacementsConfig) {
|
||||
horizontalArrowShift = 16,
|
||||
verticalArrowShift = 8,
|
||||
autoAdjustOverflow,
|
||||
arrowPointAtCenter,
|
||||
} = config;
|
||||
const placementMap: BuildInPlacements = {
|
||||
left: {
|
||||
@ -94,7 +95,7 @@ export default function getPlacements(config: PlacementsConfig) {
|
||||
},
|
||||
};
|
||||
Object.keys(placementMap).forEach(key => {
|
||||
placementMap[key] = config.arrowPointAtCenter
|
||||
placementMap[key] = arrowPointAtCenter
|
||||
? {
|
||||
...placementMap[key],
|
||||
overflow: getOverflowOptions(autoAdjustOverflow),
|
44
components/_util/statusUtils.tsx
Normal file
44
components/_util/statusUtils.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
|
||||
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
|
||||
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import classNames from 'classnames';
|
||||
import { ValidateStatus } from '../form/FormItem';
|
||||
import { tuple } from './type';
|
||||
|
||||
const InputStatuses = tuple('warning', 'error', '');
|
||||
export type InputStatus = typeof InputStatuses[number];
|
||||
|
||||
const iconMap = {
|
||||
success: CheckCircleFilled,
|
||||
warning: ExclamationCircleFilled,
|
||||
error: CloseCircleFilled,
|
||||
validating: LoadingOutlined,
|
||||
};
|
||||
|
||||
export const getFeedbackIcon = (prefixCls: string, status?: ValidateStatus) => {
|
||||
const IconNode = status && iconMap[status];
|
||||
return IconNode ? (
|
||||
<span className={`${prefixCls}-feedback-icon`}>
|
||||
<IconNode />
|
||||
</span>
|
||||
) : null;
|
||||
};
|
||||
|
||||
export function getStatusClassNames(
|
||||
prefixCls: string,
|
||||
status?: ValidateStatus,
|
||||
hasFeedback?: boolean,
|
||||
) {
|
||||
return classNames({
|
||||
[`${prefixCls}-status-success`]: status === 'success',
|
||||
[`${prefixCls}-status-warning`]: status === 'warning',
|
||||
[`${prefixCls}-status-error`]: status === 'error',
|
||||
[`${prefixCls}-status-validating`]: status === 'validating',
|
||||
[`${prefixCls}-has-feedback`]: hasFeedback,
|
||||
});
|
||||
}
|
||||
|
||||
export const getMergedStatus = (contextStatus?: ValidateStatus, customStatus?: InputStatus) =>
|
||||
customStatus || contextStatus;
|
86
components/_util/theme/default.tsx
Normal file
86
components/_util/theme/default.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
import { TinyColor } from '@ctrl/tinycolor';
|
||||
|
||||
import type { DesignToken, PresetColorType } from '.';
|
||||
|
||||
const presetColors: PresetColorType = {
|
||||
blue: '#1890FF',
|
||||
purple: '#722ED1',
|
||||
cyan: '#13C2C2',
|
||||
green: '#52C41A',
|
||||
magenta: '#EB2F96',
|
||||
pink: '#eb2f96',
|
||||
red: '#F5222D',
|
||||
orange: '#FA8C16',
|
||||
yellow: '#FADB14',
|
||||
volcano: '#FA541C',
|
||||
geekblue: '#2F54EB',
|
||||
gold: '#FAAD14',
|
||||
lime: '#A0D911',
|
||||
};
|
||||
|
||||
const defaultDesignToken: DesignToken = {
|
||||
primaryColor: '#1890ff',
|
||||
successColor: '#52c41a',
|
||||
warningColor: '#faad14',
|
||||
errorColor: '#ff4d4f',
|
||||
infoColor: '#1890ff',
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/20210
|
||||
lineHeight: 1.5715,
|
||||
|
||||
borderWidth: 1,
|
||||
borderStyle: 'solid',
|
||||
borderRadius: 2,
|
||||
borderColor: new TinyColor({ h: 0, s: 0, v: 85 }).toHexString(),
|
||||
borderColorSplit: new TinyColor({ h: 0, s: 0, v: 94 }).toHexString(),
|
||||
|
||||
easeInOut: `cubic-bezier(0.645, 0.045, 0.355, 1)`,
|
||||
easeInOutCirc: `cubic-bezier(0.78, 0.14, 0.15, 0.86)`,
|
||||
easeOutBack: `cubic-bezier(0.12, 0.4, 0.29, 1.46)`,
|
||||
easeInQuint: `cubic-bezier(0.755, 0.05, 0.855, 0.06)`,
|
||||
easeOutQuint: `cubic-bezier(0.23, 1, 0.32, 1)`,
|
||||
|
||||
outlineWidth: 2,
|
||||
outlineBlurSize: 0,
|
||||
|
||||
fontSize: 14,
|
||||
fontFamily: `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
|
||||
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
||||
'Noto Color Emoji'`,
|
||||
textColor: new TinyColor('#000').setAlpha(0.85).toRgbString(),
|
||||
textColorSecondary: new TinyColor('#000').setAlpha(0.45).toRgbString(),
|
||||
textColorDisabled: new TinyColor('#000').setAlpha(0.25).toRgbString(),
|
||||
textColorInverse: '#fff',
|
||||
placeholderColor: new TinyColor({ h: 0, s: 0, v: 75 }).setAlpha(0.5).toRgbString(),
|
||||
|
||||
disabledColor: new TinyColor('#000').setAlpha(0.25).toRgbString(),
|
||||
|
||||
headingColor: new TinyColor('#000').setAlpha(0.85).toRgbString(),
|
||||
|
||||
iconColorHover: new TinyColor('#000').setAlpha(0.75).toRgbString(),
|
||||
|
||||
itemHoverBackground: '#f5f5f5',
|
||||
|
||||
controlHeight: 32,
|
||||
|
||||
padding: 16,
|
||||
margin: 16,
|
||||
|
||||
// Default grey background color
|
||||
background: new TinyColor({ h: 0, s: 0, v: 96 }).toHexString(),
|
||||
|
||||
// background of header and selected item
|
||||
backgroundLight: new TinyColor({ h: 0, s: 0, v: 98 }).toHexString(),
|
||||
|
||||
componentBackground: '#fff',
|
||||
componentBackgroundDisabled: new TinyColor({ h: 0, s: 0, v: 96 }).toHexString(),
|
||||
|
||||
duration: 0.3,
|
||||
|
||||
zIndexDropdown: 1050,
|
||||
|
||||
// preset color palettes
|
||||
...presetColors,
|
||||
};
|
||||
|
||||
export default defaultDesignToken;
|
364
components/_util/theme/index.tsx
Normal file
364
components/_util/theme/index.tsx
Normal file
@ -0,0 +1,364 @@
|
||||
import React from 'react';
|
||||
import { generate } from '@ant-design/colors';
|
||||
import { TinyColor } from '@ctrl/tinycolor';
|
||||
import {
|
||||
CSSInterpolation,
|
||||
CSSObject,
|
||||
Theme,
|
||||
useCacheToken,
|
||||
useStyleRegister,
|
||||
} from '@ant-design/cssinjs';
|
||||
import defaultDesignToken from './default';
|
||||
import version from '../../version';
|
||||
import { resetComponent, resetIcon, clearFix } from './util';
|
||||
import {
|
||||
initSlideMotion,
|
||||
slideUpIn,
|
||||
slideUpOut,
|
||||
slideDownIn,
|
||||
slideDownOut,
|
||||
slideLeftIn,
|
||||
slideLeftOut,
|
||||
slideRightIn,
|
||||
slideRightOut,
|
||||
} from './util/slide';
|
||||
|
||||
export {
|
||||
resetComponent,
|
||||
resetIcon,
|
||||
clearFix,
|
||||
initSlideMotion,
|
||||
slideUpIn,
|
||||
slideUpOut,
|
||||
slideDownIn,
|
||||
slideDownOut,
|
||||
slideLeftIn,
|
||||
slideLeftOut,
|
||||
slideRightIn,
|
||||
slideRightOut,
|
||||
};
|
||||
|
||||
export interface PresetColorType {
|
||||
blue: string;
|
||||
purple: string;
|
||||
cyan: string;
|
||||
green: string;
|
||||
magenta: string;
|
||||
pink: string;
|
||||
red: string;
|
||||
orange: string;
|
||||
yellow: string;
|
||||
volcano: string;
|
||||
geekblue: string;
|
||||
lime: string;
|
||||
gold: string;
|
||||
}
|
||||
|
||||
export const PresetColorKeys: ReadonlyArray<keyof PresetColorType> = [
|
||||
'blue',
|
||||
'purple',
|
||||
'cyan',
|
||||
'green',
|
||||
'magenta',
|
||||
'pink',
|
||||
'red',
|
||||
'orange',
|
||||
'yellow',
|
||||
'volcano',
|
||||
'geekblue',
|
||||
'lime',
|
||||
'gold',
|
||||
];
|
||||
|
||||
export interface DesignToken extends PresetColorType {
|
||||
primaryColor: string;
|
||||
successColor: string;
|
||||
warningColor: string;
|
||||
errorColor: string;
|
||||
infoColor: string;
|
||||
|
||||
lineHeight: number;
|
||||
borderWidth: number;
|
||||
borderStyle: string;
|
||||
borderRadius: number;
|
||||
borderColor: string;
|
||||
borderColorSplit: string;
|
||||
|
||||
easeInOut: string;
|
||||
easeInOutCirc: string;
|
||||
easeOutBack: string;
|
||||
easeInQuint: string;
|
||||
easeOutQuint: string;
|
||||
|
||||
outlineWidth: number;
|
||||
outlineBlurSize: number;
|
||||
|
||||
fontSize: number;
|
||||
fontFamily: string;
|
||||
textColor: string;
|
||||
textColorSecondary: string;
|
||||
textColorDisabled: string;
|
||||
textColorInverse: string;
|
||||
placeholderColor: string;
|
||||
|
||||
disabledColor: string;
|
||||
|
||||
iconColorHover: string;
|
||||
|
||||
headingColor: string;
|
||||
|
||||
itemHoverBackground: string;
|
||||
|
||||
controlHeight: number;
|
||||
|
||||
padding: number;
|
||||
margin: number;
|
||||
|
||||
background: string;
|
||||
backgroundLight: string;
|
||||
|
||||
componentBackground: string;
|
||||
componentBackgroundDisabled: string;
|
||||
|
||||
duration: number;
|
||||
|
||||
zIndexDropdown: number;
|
||||
|
||||
boxShadow?: string;
|
||||
}
|
||||
|
||||
type ColorPaletteKeyIndexes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
|
||||
type ColorPalettes = {
|
||||
[key in `${keyof PresetColorType}-${ColorPaletteKeyIndexes[number]}`]: string;
|
||||
};
|
||||
|
||||
/** This is temporary token definition since final token definition is not ready yet. */
|
||||
export interface DerivativeToken extends ColorPalettes, Omit<DesignToken, 'duration'> {
|
||||
primaryHoverColor: string;
|
||||
primaryActiveColor: string;
|
||||
primaryOutlineColor: string;
|
||||
errorHoverColor: string;
|
||||
errorActiveColor: string;
|
||||
errorOutlineColor: string;
|
||||
warningHoverColor: string;
|
||||
warningOutlineColor: string;
|
||||
itemActiveBackground: string;
|
||||
|
||||
highlightColor: string;
|
||||
|
||||
linkColor: string;
|
||||
linkHoverColor: string;
|
||||
linkActiveColor: string;
|
||||
linkDecoration: CSSObject['textDecoration'];
|
||||
linkHoverDecoration: CSSObject['textDecoration'];
|
||||
linkFocusDecoration: CSSObject['textDecoration'];
|
||||
|
||||
fontSizeSM: number;
|
||||
fontSizeLG: number;
|
||||
/** @private Only Used for control inside component like Multiple Select inner selection item */
|
||||
controlHeightXS: number;
|
||||
controlHeightSM: number;
|
||||
controlHeightLG: number;
|
||||
controlPaddingHorizontal: number;
|
||||
controlPaddingHorizontalSM: number;
|
||||
paddingSM: number;
|
||||
paddingXS: number;
|
||||
paddingXXS: number;
|
||||
paddingLG: number;
|
||||
marginXS: number;
|
||||
marginLG: number;
|
||||
marginXXS: number;
|
||||
|
||||
duration: string;
|
||||
durationMid: string;
|
||||
durationFast: string;
|
||||
|
||||
heading1Size: number;
|
||||
heading2Size: number;
|
||||
heading3Size: number;
|
||||
heading4Size: number;
|
||||
heading5Size: number;
|
||||
|
||||
primaryColors: string[];
|
||||
errorColors: string[];
|
||||
warningColors: string[];
|
||||
|
||||
// TMP
|
||||
tmpPrimaryColorWeak: string;
|
||||
tmpPrimaryHoverColorWeak: string;
|
||||
// Checked background for Checkable Tag
|
||||
tmpPrimaryColor6: string;
|
||||
// Active background for Checkable Tag
|
||||
tmpPrimaryColor7: string;
|
||||
|
||||
tmpSuccessColorDeprecatedBg: string;
|
||||
tmpWarningColorDeprecatedBg: string;
|
||||
tmpErrorColorDeprecatedBg: string;
|
||||
tmpInfoColorDeprecatedBg: string;
|
||||
|
||||
tmpSuccessColorDeprecatedBorder: string;
|
||||
tmpWarningColorDeprecatedBorder: string;
|
||||
tmpErrorColorDeprecatedBorder: string;
|
||||
tmpInfoColorDeprecatedBorder: string;
|
||||
}
|
||||
|
||||
export { useStyleRegister };
|
||||
|
||||
// =============================== Derivative ===============================
|
||||
function derivative(designToken: DesignToken): DerivativeToken {
|
||||
const { primaryColor, errorColor, warningColor, infoColor, successColor } = designToken;
|
||||
|
||||
const primaryColors = generate(primaryColor);
|
||||
const errorColors = generate(errorColor);
|
||||
const warningColors = generate(warningColor);
|
||||
const infoColors = generate(infoColor);
|
||||
const successColors = generate(successColor);
|
||||
|
||||
const paddingSM = (designToken.padding / 4) * 3;
|
||||
const paddingXS = designToken.padding * 0.5;
|
||||
|
||||
const colorPalettes = PresetColorKeys.map((colorKey: keyof PresetColorType) => {
|
||||
const colors = generate(designToken[colorKey]);
|
||||
|
||||
const ret = new Array(10).fill(1).reduce((prev, _, i) => {
|
||||
prev[`${colorKey}-${i + 1}`] = colors[i];
|
||||
return prev;
|
||||
}, {}) as ColorPalettes;
|
||||
return ret;
|
||||
}).reduce((prev, cur) => {
|
||||
prev = {
|
||||
...prev,
|
||||
...cur,
|
||||
};
|
||||
return prev;
|
||||
}, {} as ColorPalettes);
|
||||
|
||||
return {
|
||||
// FIXME: Need design token
|
||||
boxShadow: `
|
||||
0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
||||
0 6px 16px 0 rgba(0, 0, 0, 0.08),
|
||||
0 9px 28px 8px rgba(0, 0, 0, 0.05)`,
|
||||
|
||||
...designToken,
|
||||
|
||||
primaryHoverColor: primaryColors[4],
|
||||
primaryActiveColor: primaryColors[6],
|
||||
primaryOutlineColor: new TinyColor(primaryColor).setAlpha(0.2).toRgbString(),
|
||||
|
||||
errorHoverColor: errorColors[4],
|
||||
errorActiveColor: errorColors[6],
|
||||
errorOutlineColor: new TinyColor(errorColor).setAlpha(0.2).toRgbString(),
|
||||
|
||||
warningHoverColor: warningColors[4],
|
||||
warningOutlineColor: new TinyColor(warningColor).setAlpha(0.2).toRgbString(),
|
||||
|
||||
highlightColor: errorColors[4], // FIXME: Should not align with error color
|
||||
|
||||
itemActiveBackground: primaryColors[0],
|
||||
|
||||
linkColor: primaryColor,
|
||||
linkHoverColor: primaryColors[4],
|
||||
linkActiveColor: primaryColors[6],
|
||||
linkDecoration: 'none',
|
||||
linkHoverDecoration: 'none',
|
||||
linkFocusDecoration: 'none',
|
||||
|
||||
fontSizeSM: designToken.fontSize - 2,
|
||||
fontSizeLG: designToken.fontSize + 2,
|
||||
controlHeightXS: designToken.controlHeight / 2,
|
||||
controlHeightSM: designToken.controlHeight * 0.75,
|
||||
controlHeightLG: designToken.controlHeight * 1.25,
|
||||
controlPaddingHorizontal: paddingSM,
|
||||
controlPaddingHorizontalSM: paddingXS,
|
||||
paddingSM,
|
||||
paddingXS,
|
||||
paddingXXS: designToken.padding * 0.25,
|
||||
paddingLG: designToken.padding * 1.5,
|
||||
marginXS: designToken.margin * 0.5,
|
||||
marginLG: designToken.margin * 1.5,
|
||||
marginXXS: designToken.margin * 0.25,
|
||||
|
||||
duration: `${designToken.duration}s`,
|
||||
durationMid: `${(designToken.duration / 3) * 2}s`,
|
||||
durationFast: `${designToken.duration / 3}s`,
|
||||
|
||||
...colorPalettes,
|
||||
|
||||
heading1Size: Math.ceil(designToken.fontSize * 2.71),
|
||||
heading2Size: Math.ceil(designToken.fontSize * 2.14),
|
||||
heading3Size: Math.ceil(designToken.fontSize * 1.71),
|
||||
heading4Size: Math.ceil(designToken.fontSize * 1.42),
|
||||
heading5Size: Math.ceil(designToken.fontSize * 1.14),
|
||||
|
||||
primaryColors,
|
||||
errorColors,
|
||||
warningColors,
|
||||
|
||||
// TMP
|
||||
tmpPrimaryColorWeak: primaryColors[2],
|
||||
tmpPrimaryHoverColorWeak: primaryColors[0],
|
||||
tmpPrimaryColor6: primaryColors[5],
|
||||
tmpPrimaryColor7: primaryColors[6],
|
||||
|
||||
tmpSuccessColorDeprecatedBg: successColors[0],
|
||||
tmpWarningColorDeprecatedBg: warningColors[0],
|
||||
tmpErrorColorDeprecatedBg: errorColors[0],
|
||||
tmpInfoColorDeprecatedBg: infoColors[0],
|
||||
|
||||
tmpSuccessColorDeprecatedBorder: successColors[2],
|
||||
tmpWarningColorDeprecatedBorder: warningColors[2],
|
||||
tmpErrorColorDeprecatedBorder: errorColors[2],
|
||||
tmpInfoColorDeprecatedBorder: infoColors[2],
|
||||
};
|
||||
}
|
||||
|
||||
// ================================ Context =================================
|
||||
export const ThemeContext = React.createContext(
|
||||
new Theme<DesignToken, DerivativeToken>(derivative),
|
||||
);
|
||||
|
||||
export const DesignTokenContext = React.createContext<{
|
||||
token: Partial<DesignToken>;
|
||||
hashed?: string | boolean;
|
||||
}>({
|
||||
token: defaultDesignToken,
|
||||
});
|
||||
|
||||
// ================================== Hook ==================================
|
||||
export function useToken(): [Theme<DesignToken, DerivativeToken>, DerivativeToken, string] {
|
||||
const { token: rootDesignToken, hashed } = React.useContext(DesignTokenContext);
|
||||
const theme = React.useContext(ThemeContext);
|
||||
|
||||
const salt = `${version}-${hashed || ''}`;
|
||||
|
||||
const [token, hashId] = useCacheToken<DerivativeToken, DesignToken>(
|
||||
theme,
|
||||
[defaultDesignToken, rootDesignToken],
|
||||
{
|
||||
salt,
|
||||
},
|
||||
);
|
||||
return [theme, token, hashed ? hashId : ''];
|
||||
}
|
||||
|
||||
export type UseComponentStyleResult = [(node: React.ReactNode) => React.ReactElement, string];
|
||||
|
||||
export type GenerateStyle<ComponentToken extends object, ReturnType = CSSInterpolation> = (
|
||||
token: ComponentToken,
|
||||
hashId?: string,
|
||||
) => ReturnType;
|
||||
|
||||
// ================================== Util ==================================
|
||||
export function withPrefix(
|
||||
style: CSSObject,
|
||||
prefixCls: string,
|
||||
additionalClsList: string[] = [],
|
||||
): CSSObject {
|
||||
const fullClsList = [prefixCls, ...additionalClsList].filter(cls => cls).map(cls => `.${cls}`);
|
||||
|
||||
return {
|
||||
[fullClsList.join('')]: style,
|
||||
};
|
||||
}
|
56
components/_util/theme/util/index.tsx
Normal file
56
components/_util/theme/util/index.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { CSSObject } from '@ant-design/cssinjs';
|
||||
import type { DerivativeToken } from '..';
|
||||
|
||||
export const resetComponent = (token: DerivativeToken): CSSObject => ({
|
||||
boxSizing: 'border-box',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
color: token.textColor,
|
||||
fontSize: token.fontSize,
|
||||
// font-variant: @font-variant-base;
|
||||
lineHeight: token.lineHeight,
|
||||
listStyle: 'none',
|
||||
// font-feature-settings: @font-feature-settings-base;
|
||||
});
|
||||
|
||||
export const resetIcon = (): CSSObject => ({
|
||||
display: 'inline-block',
|
||||
color: 'inherit',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 0,
|
||||
textAlign: 'center',
|
||||
textTransform: 'none',
|
||||
// for SVG icon, see https://blog.prototypr.io/align-svg-icons-to-text-and-say-goodbye-to-font-icons-d44b3d7b26b4
|
||||
verticalAlign: '-0.125em',
|
||||
textRendering: 'optimizeLegibility',
|
||||
'-webkit-font-smoothing': 'antialiased',
|
||||
'-moz-osx-font-smoothing': 'grayscale',
|
||||
|
||||
'> *': {
|
||||
lineHeight: 1,
|
||||
},
|
||||
|
||||
svg: {
|
||||
display: 'inline-block',
|
||||
},
|
||||
|
||||
'& &-icon': {
|
||||
display: 'block',
|
||||
},
|
||||
});
|
||||
|
||||
export const clearFix = (): CSSObject => ({
|
||||
// https://github.com/ant-design/ant-design/issues/21301#issuecomment-583955229
|
||||
'&::before': {
|
||||
display: 'table',
|
||||
content: '""',
|
||||
},
|
||||
|
||||
'&::after': {
|
||||
// https://github.com/ant-design/ant-design/issues/21864
|
||||
display: 'table',
|
||||
clear: 'both',
|
||||
content: '""',
|
||||
},
|
||||
});
|
52
components/_util/theme/util/motion.tsx
Normal file
52
components/_util/theme/util/motion.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { CSSObject, Keyframes } from '@ant-design/cssinjs';
|
||||
|
||||
const initMotionCommon = (duration: string): CSSObject => ({
|
||||
animationDuration: duration,
|
||||
animationFillMode: 'both',
|
||||
});
|
||||
|
||||
// FIXME: origin less code seems same as initMotionCommon. Maybe we can safe remove
|
||||
const initMotionCommonLeave = (duration: string): CSSObject => ({
|
||||
animationDuration: duration,
|
||||
animationFillMode: 'both',
|
||||
});
|
||||
|
||||
export const initMotion = (
|
||||
hashId: string,
|
||||
motionName: string,
|
||||
inKeyframes: Keyframes,
|
||||
outKeyframes: Keyframes,
|
||||
duration: string,
|
||||
): CSSObject => {
|
||||
const motionCls = `.${motionName}`;
|
||||
|
||||
return {
|
||||
[`
|
||||
${motionCls}-enter,
|
||||
${motionCls}-appear
|
||||
`]: {
|
||||
...initMotionCommon(duration),
|
||||
animationPlayState: 'paused',
|
||||
},
|
||||
|
||||
[`${motionCls}-leave`]: {
|
||||
...initMotionCommonLeave(duration),
|
||||
animationPlayState: 'paused',
|
||||
},
|
||||
|
||||
[`
|
||||
${motionCls}-enter${motionCls}-enter-active,
|
||||
${motionCls}-appear${motionCls}-appear-active
|
||||
`]: {
|
||||
animationName: inKeyframes.getName(hashId),
|
||||
animationPlayState: 'running',
|
||||
},
|
||||
|
||||
[`${motionCls}-leave${motionCls}-leave-active`]: {
|
||||
animationName: outKeyframes.getName(hashId),
|
||||
animationPlayState: 'running',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
};
|
||||
};
|
19
components/_util/theme/util/operationUnit.tsx
Normal file
19
components/_util/theme/util/operationUnit.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import type { CSSObject } from '@ant-design/cssinjs';
|
||||
import { DerivativeToken } from '..';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const operationUnit = (token: DerivativeToken): CSSObject => ({
|
||||
color: token.linkColor,
|
||||
textDecoration: 'none',
|
||||
outline: 'none',
|
||||
cursor: 'pointer',
|
||||
transition: `color ${token.duration}`,
|
||||
|
||||
'&:focus, &:hover': {
|
||||
color: token.linkHoverColor,
|
||||
},
|
||||
|
||||
'&:active': {
|
||||
color: token.linkActiveColor,
|
||||
},
|
||||
});
|
145
components/_util/theme/util/slide.tsx
Normal file
145
components/_util/theme/util/slide.tsx
Normal file
@ -0,0 +1,145 @@
|
||||
import { CSSInterpolation, Keyframes } from '@ant-design/cssinjs';
|
||||
import type { DerivativeToken } from '..';
|
||||
import { initMotion } from './motion';
|
||||
|
||||
export const initSlideMotion = (
|
||||
hashId: string,
|
||||
rootPrefixCls: string,
|
||||
motionName: string,
|
||||
inKeyframes: Keyframes,
|
||||
outKeyframes: Keyframes,
|
||||
token: DerivativeToken,
|
||||
): CSSInterpolation => {
|
||||
const rootMotionName = `${rootPrefixCls}-${motionName}`;
|
||||
const motionCls = `.${rootMotionName}`;
|
||||
|
||||
return [
|
||||
initMotion(hashId, rootMotionName, inKeyframes, outKeyframes, token.durationMid),
|
||||
|
||||
{
|
||||
[`
|
||||
${motionCls}-enter,
|
||||
${motionCls}-appear
|
||||
`]: {
|
||||
opacity: 0,
|
||||
animationTimingFunction: token.easeOutQuint,
|
||||
},
|
||||
|
||||
[`${motionCls}-leave`]: {
|
||||
animationTimingFunction: token.easeInQuint,
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export const slideUpIn = new Keyframes('antSlideUpIn', {
|
||||
'0%': {
|
||||
transform: 'scaleY(0.8)',
|
||||
transformOrigin: '0% 0%',
|
||||
opacity: 0,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
transform: 'scaleY(1)',
|
||||
transformOrigin: '0% 0%',
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export const slideUpOut = new Keyframes('antSlideUpOut', {
|
||||
'0%': {
|
||||
transform: 'scaleY(1)',
|
||||
transformOrigin: '0% 0%',
|
||||
opacity: 1,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
transform: 'scaleY(0.8)',
|
||||
transformOrigin: '0% 0%',
|
||||
opacity: 0,
|
||||
},
|
||||
});
|
||||
|
||||
export const slideDownIn = new Keyframes('antSlideDownIn', {
|
||||
'0%': {
|
||||
transform: 'scaleY(0.8)',
|
||||
transformOrigin: '100% 100%',
|
||||
opacity: 0,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
transform: 'scaleY(1)',
|
||||
transformOrigin: '100% 100%',
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export const slideDownOut = new Keyframes('antSlideDownOut', {
|
||||
'0%': {
|
||||
transform: 'scaleY(1)',
|
||||
transformOrigin: '100% 100%',
|
||||
opacity: 1,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
transform: 'scaleY(0.8)',
|
||||
transformOrigin: '100% 100%',
|
||||
opacity: 0,
|
||||
},
|
||||
});
|
||||
|
||||
export const slideLeftIn = new Keyframes('antSlideLeftIn', {
|
||||
'0%': {
|
||||
transform: 'scaleX(0.8)',
|
||||
transformOrigin: '0% 0%',
|
||||
opacity: 0,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
transform: 'scaleX(1)',
|
||||
transformOrigin: '0% 0%',
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export const slideLeftOut = new Keyframes('antSlideLeftOut', {
|
||||
'0%': {
|
||||
transform: 'scaleX(1)',
|
||||
transformOrigin: '0% 0%',
|
||||
opacity: 1,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
transform: 'scaleX(0.8)',
|
||||
transformOrigin: '0% 0%',
|
||||
opacity: 0,
|
||||
},
|
||||
});
|
||||
|
||||
export const slideRightIn = new Keyframes('antSlideRightIn', {
|
||||
'0%': {
|
||||
transform: 'scaleX(0.8)',
|
||||
transformOrigin: '100% 0%',
|
||||
opacity: 0,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
transform: 'scaleX(1)',
|
||||
transformOrigin: '100% 0%',
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export const slideRightOut = new Keyframes('antSlideRightOut', {
|
||||
'0%': {
|
||||
transform: 'scaleX(1)',
|
||||
transformOrigin: '100% 0%',
|
||||
opacity: 1,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
transform: 'scaleX(0.8)',
|
||||
transformOrigin: '100% 0%',
|
||||
opacity: 0,
|
||||
},
|
||||
});
|
@ -17,7 +17,7 @@ class AffixMounter extends React.Component<{
|
||||
}> {
|
||||
private container: HTMLDivElement;
|
||||
|
||||
public affix: Affix;
|
||||
public affix: React.Component<AffixProps, AffixState>;
|
||||
|
||||
componentDidMount() {
|
||||
this.container.addEventListener = jest
|
||||
@ -58,7 +58,7 @@ describe('Affix Render', () => {
|
||||
|
||||
const domMock = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect');
|
||||
let affixMounterWrapper: ReactWrapper<unknown, unknown, AffixMounter>;
|
||||
let affixWrapper: ReactWrapper<AffixProps, AffixState, Affix>;
|
||||
let affixWrapper: ReactWrapper<AffixProps, AffixState, React.Component<AffixProps, AffixState>>;
|
||||
|
||||
const classRect: Record<string, DOMRect> = {
|
||||
container: {
|
||||
@ -157,9 +157,9 @@ describe('Affix Render', () => {
|
||||
const getTarget = () => container;
|
||||
affixWrapper = mount(<Affix target={getTarget}>{null}</Affix>);
|
||||
affixWrapper.setProps({ target: () => null });
|
||||
expect(affixWrapper.instance().state.status).toBe(0);
|
||||
expect(affixWrapper.instance().state.affixStyle).toBe(undefined);
|
||||
expect(affixWrapper.instance().state.placeholderStyle).toBe(undefined);
|
||||
expect(affixWrapper.find('Affix').last().state().status).toBe(0);
|
||||
expect(affixWrapper.find('Affix').last().state().affixStyle).toBe(undefined);
|
||||
expect(affixWrapper.find('Affix').last().state().placeholderStyle).toBe(undefined);
|
||||
});
|
||||
|
||||
it('instance change', async () => {
|
||||
|
@ -33,6 +33,10 @@ export interface AffixProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface InternalAffixProps extends AffixProps {
|
||||
affixPrefixCls: string;
|
||||
}
|
||||
|
||||
enum AffixStatus {
|
||||
None,
|
||||
Prepare,
|
||||
@ -47,7 +51,7 @@ export interface AffixState {
|
||||
prevTarget: Window | HTMLElement | null;
|
||||
}
|
||||
|
||||
class Affix extends React.Component<AffixProps, AffixState> {
|
||||
class Affix extends React.Component<InternalAffixProps, AffixState> {
|
||||
static contextType = ConfigContext;
|
||||
|
||||
state: AffixState = {
|
||||
@ -126,7 +130,7 @@ class Affix extends React.Component<AffixProps, AffixState> {
|
||||
|
||||
getOffsetTop = () => {
|
||||
const { offsetBottom, offsetTop } = this.props;
|
||||
return (offsetBottom === undefined && offsetTop === undefined) ? 0 : offsetTop;
|
||||
return offsetBottom === undefined && offsetTop === undefined ? 0 : offsetTop;
|
||||
};
|
||||
|
||||
getOffsetBottom = () => this.props.offsetBottom;
|
||||
@ -250,14 +254,20 @@ class Affix extends React.Component<AffixProps, AffixState> {
|
||||
|
||||
// =================== Render ===================
|
||||
render() {
|
||||
const { getPrefixCls } = this.context;
|
||||
const { affixStyle, placeholderStyle } = this.state;
|
||||
const { prefixCls, children } = this.props;
|
||||
const { affixPrefixCls, children } = this.props;
|
||||
const className = classNames({
|
||||
[getPrefixCls('affix', prefixCls)]: !!affixStyle,
|
||||
[affixPrefixCls]: !!affixStyle,
|
||||
});
|
||||
|
||||
let props = omit(this.props, ['prefixCls', 'offsetTop', 'offsetBottom', 'target', 'onChange']);
|
||||
let props = omit(this.props, [
|
||||
'prefixCls',
|
||||
'offsetTop',
|
||||
'offsetBottom',
|
||||
'target',
|
||||
'onChange',
|
||||
'affixPrefixCls',
|
||||
]);
|
||||
// Omit this since `onTestUpdatePosition` only works on test.
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
props = omit(props as typeof props & { onTestUpdatePosition: any }, ['onTestUpdatePosition']);
|
||||
@ -286,4 +296,23 @@ class Affix extends React.Component<AffixProps, AffixState> {
|
||||
}
|
||||
}
|
||||
|
||||
export default Affix;
|
||||
const AffixFC = React.forwardRef<Affix, AffixProps>((props, ref) => {
|
||||
const { prefixCls: customizePrefixCls } = props;
|
||||
const { getPrefixCls } = React.useContext(ConfigContext);
|
||||
|
||||
const affixPrefixCls = getPrefixCls('affix', customizePrefixCls);
|
||||
|
||||
const AffixProps: InternalAffixProps = {
|
||||
...props,
|
||||
|
||||
affixPrefixCls,
|
||||
};
|
||||
|
||||
return <Affix {...AffixProps} ref={ref} />;
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
AffixFC.displayName = 'Affix';
|
||||
}
|
||||
|
||||
export default AffixFC;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
||||
import Affix from '.';
|
||||
|
||||
export type BindElement = HTMLElement | Window | null | undefined;
|
||||
|
||||
@ -41,7 +40,7 @@ const TRIGGER_EVENTS = [
|
||||
|
||||
interface ObserverEntity {
|
||||
target: HTMLElement | Window;
|
||||
affixList: Affix[];
|
||||
affixList: any[];
|
||||
eventHandlers: { [eventName: string]: any };
|
||||
}
|
||||
|
||||
@ -52,7 +51,7 @@ export function getObserverEntities() {
|
||||
return observerEntities;
|
||||
}
|
||||
|
||||
export function addObserveTarget(target: HTMLElement | Window | null, affix: Affix): void {
|
||||
export function addObserveTarget<T>(target: HTMLElement | Window | null, affix: T): void {
|
||||
if (!target) return;
|
||||
|
||||
let entity: ObserverEntity | undefined = observerEntities.find(item => item.target === target);
|
||||
@ -78,7 +77,7 @@ export function addObserveTarget(target: HTMLElement | Window | null, affix: Aff
|
||||
}
|
||||
}
|
||||
|
||||
export function removeObserveTarget(affix: Affix): void {
|
||||
export function removeObserveTarget<T>(affix: T): void {
|
||||
const observerEntity = observerEntities.find(oriObserverEntity => {
|
||||
const hasAffix = oriObserverEntity.affixList.some(item => item === affix);
|
||||
if (hasAffix) {
|
||||
|
@ -21,7 +21,7 @@ Alert component for feedback.
|
||||
| banner | Whether to show as banner | boolean | false | |
|
||||
| closable | Whether Alert can be closed | boolean | - | |
|
||||
| closeText | Close text to show | ReactNode | - | |
|
||||
| closeIcon | Custom close icon | ReactNode | <CloseOutlined /> | 4.17.0 |
|
||||
| closeIcon | Custom close icon | ReactNode | `<CloseOutlined />` | 4.17.0 |
|
||||
| description | Additional content of Alert | ReactNode | - | |
|
||||
| icon | Custom icon, effective when `showIcon` is true | ReactNode | - | |
|
||||
| message | Content of Alert | ReactNode | - | |
|
||||
|
@ -16,6 +16,9 @@ import getDataOrAriaProps from '../_util/getDataOrAriaProps';
|
||||
import ErrorBoundary from './ErrorBoundary';
|
||||
import { replaceElement } from '../_util/reactNode';
|
||||
|
||||
// CSSINJS
|
||||
import useStyle from './style';
|
||||
|
||||
export interface AlertProps {
|
||||
/** Type of Alert styles, options:`success`, `info`, `warning`, `error` */
|
||||
type?: 'success' | 'info' | 'warning' | 'error';
|
||||
@ -87,8 +90,9 @@ const Alert: AlertInterface = ({
|
||||
const [closed, setClosed] = React.useState(false);
|
||||
|
||||
const ref = React.useRef<HTMLElement>();
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const { getPrefixCls, direction, iconPrefixCls } = React.useContext(ConfigContext);
|
||||
const prefixCls = getPrefixCls('alert', customizePrefixCls);
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls, iconPrefixCls);
|
||||
|
||||
const handleClose = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setClosed(true);
|
||||
@ -147,11 +151,12 @@ const Alert: AlertInterface = ({
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
hashId,
|
||||
);
|
||||
|
||||
const dataOrAriaProps = getDataOrAriaProps(props);
|
||||
|
||||
return (
|
||||
return wrapSSR(
|
||||
<CSSMotion
|
||||
visible={!closed}
|
||||
motionName={`${prefixCls}-motion`}
|
||||
@ -183,7 +188,7 @@ const Alert: AlertInterface = ({
|
||||
{renderCloseIcon()}
|
||||
</div>
|
||||
)}
|
||||
</CSSMotion>
|
||||
</CSSMotion>,
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -22,7 +22,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/8emPa3fjl/Alert.svg
|
||||
| banner | 是否用作顶部公告 | boolean | false | |
|
||||
| closable | 默认不显示关闭按钮 | boolean | - | |
|
||||
| closeText | 自定义关闭按钮 | ReactNode | - | |
|
||||
| closeIcon | 自定义关闭 Icon | ReactNode | <CloseOutlined /> | 4.17.0 |
|
||||
| closeIcon | 自定义关闭 Icon | ReactNode | `<CloseOutlined />` | 4.17.0 |
|
||||
| description | 警告提示的辅助性文字介绍 | ReactNode | - | |
|
||||
| icon | 自定义图标,`showIcon` 为 true 时有效 | ReactNode | - | |
|
||||
| message | 警告提示内容 | ReactNode | - | |
|
||||
|
@ -1,155 +1,155 @@
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
// @import '../../style/themes/index';
|
||||
// @import '../../style/mixins/index';
|
||||
|
||||
@alert-prefix-cls: ~'@{ant-prefix}-alert';
|
||||
// @alert-prefix-cls: ~'@{ant-prefix}-alert';
|
||||
|
||||
.@{alert-prefix-cls} {
|
||||
.reset-component();
|
||||
// .@{alert-prefix-cls} {
|
||||
// .reset-component();
|
||||
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 15px;
|
||||
word-wrap: break-word;
|
||||
border-radius: @border-radius-base;
|
||||
// position: relative;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// padding: 8px 15px;
|
||||
// word-wrap: break-word;
|
||||
// border-radius: @border-radius-base;
|
||||
|
||||
&-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
// &-content {
|
||||
// flex: 1;
|
||||
// min-width: 0;
|
||||
// }
|
||||
|
||||
&-icon {
|
||||
margin-right: @margin-xs;
|
||||
}
|
||||
// &-icon {
|
||||
// margin-right: @margin-xs;
|
||||
// }
|
||||
|
||||
&-description {
|
||||
display: none;
|
||||
font-size: @font-size-base;
|
||||
line-height: @font-size-base + 8px;
|
||||
}
|
||||
// &-description {
|
||||
// display: none;
|
||||
// font-size: @font-size-base;
|
||||
// line-height: @font-size-base + 8px;
|
||||
// }
|
||||
|
||||
&-success {
|
||||
background-color: @alert-success-bg-color;
|
||||
border: @border-width-base @border-style-base @alert-success-border-color;
|
||||
.@{alert-prefix-cls}-icon {
|
||||
color: @alert-success-icon-color;
|
||||
}
|
||||
}
|
||||
// &-success {
|
||||
// background-color: @alert-success-bg-color;
|
||||
// border: @border-width-base @border-style-base @alert-success-border-color;
|
||||
// .@{alert-prefix-cls}-icon {
|
||||
// color: @alert-success-icon-color;
|
||||
// }
|
||||
// }
|
||||
|
||||
&-info {
|
||||
background-color: @alert-info-bg-color;
|
||||
border: @border-width-base @border-style-base @alert-info-border-color;
|
||||
.@{alert-prefix-cls}-icon {
|
||||
color: @alert-info-icon-color;
|
||||
}
|
||||
}
|
||||
// &-info {
|
||||
// background-color: @alert-info-bg-color;
|
||||
// border: @border-width-base @border-style-base @alert-info-border-color;
|
||||
// .@{alert-prefix-cls}-icon {
|
||||
// color: @alert-info-icon-color;
|
||||
// }
|
||||
// }
|
||||
|
||||
&-warning {
|
||||
background-color: @alert-warning-bg-color;
|
||||
border: @border-width-base @border-style-base @alert-warning-border-color;
|
||||
.@{alert-prefix-cls}-icon {
|
||||
color: @alert-warning-icon-color;
|
||||
}
|
||||
}
|
||||
// &-warning {
|
||||
// background-color: @alert-warning-bg-color;
|
||||
// border: @border-width-base @border-style-base @alert-warning-border-color;
|
||||
// .@{alert-prefix-cls}-icon {
|
||||
// color: @alert-warning-icon-color;
|
||||
// }
|
||||
// }
|
||||
|
||||
&-error {
|
||||
background-color: @alert-error-bg-color;
|
||||
border: @border-width-base @border-style-base @alert-error-border-color;
|
||||
// &-error {
|
||||
// background-color: @alert-error-bg-color;
|
||||
// border: @border-width-base @border-style-base @alert-error-border-color;
|
||||
|
||||
.@{alert-prefix-cls}-icon {
|
||||
color: @alert-error-icon-color;
|
||||
}
|
||||
// .@{alert-prefix-cls}-icon {
|
||||
// color: @alert-error-icon-color;
|
||||
// }
|
||||
|
||||
.@{alert-prefix-cls}-description > pre {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
// .@{alert-prefix-cls}-description > pre {
|
||||
// margin: 0;
|
||||
// padding: 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
&-action {
|
||||
margin-left: @margin-xs;
|
||||
}
|
||||
// &-action {
|
||||
// margin-left: @margin-xs;
|
||||
// }
|
||||
|
||||
&-close-icon {
|
||||
margin-left: @margin-xs;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
font-size: @font-size-sm;
|
||||
line-height: @font-size-sm;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
// &-close-icon {
|
||||
// margin-left: @margin-xs;
|
||||
// padding: 0;
|
||||
// overflow: hidden;
|
||||
// font-size: @font-size-sm;
|
||||
// line-height: @font-size-sm;
|
||||
// background-color: transparent;
|
||||
// border: none;
|
||||
// outline: none;
|
||||
// cursor: pointer;
|
||||
|
||||
.@{iconfont-css-prefix}-close {
|
||||
color: @alert-close-color;
|
||||
transition: color 0.3s;
|
||||
// .@{iconfont-css-prefix}-close {
|
||||
// color: @alert-close-color;
|
||||
// transition: color 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: @alert-close-hover-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
// &:hover {
|
||||
// color: @alert-close-hover-color;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
&-close-text {
|
||||
color: @alert-close-color;
|
||||
transition: color 0.3s;
|
||||
// &-close-text {
|
||||
// color: @alert-close-color;
|
||||
// transition: color 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: @alert-close-hover-color;
|
||||
}
|
||||
}
|
||||
// &:hover {
|
||||
// color: @alert-close-hover-color;
|
||||
// }
|
||||
// }
|
||||
|
||||
&-with-description {
|
||||
align-items: flex-start;
|
||||
padding: @alert-with-description-padding;
|
||||
}
|
||||
// &-with-description {
|
||||
// align-items: flex-start;
|
||||
// padding: @alert-with-description-padding;
|
||||
// }
|
||||
|
||||
&-with-description&-no-icon {
|
||||
padding: @alert-with-description-no-icon-padding-vertical 15px;
|
||||
}
|
||||
// &-with-description&-no-icon {
|
||||
// padding: @alert-with-description-no-icon-padding-vertical 15px;
|
||||
// }
|
||||
|
||||
&-with-description &-icon {
|
||||
margin-right: @alert-with-description-padding-vertical;
|
||||
font-size: @alert-with-description-icon-size;
|
||||
}
|
||||
// &-with-description &-icon {
|
||||
// margin-right: @alert-with-description-padding-vertical;
|
||||
// font-size: @alert-with-description-icon-size;
|
||||
// }
|
||||
|
||||
&-with-description &-message {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
color: @alert-message-color;
|
||||
font-size: @font-size-lg;
|
||||
}
|
||||
// &-with-description &-message {
|
||||
// display: block;
|
||||
// margin-bottom: 4px;
|
||||
// color: @alert-message-color;
|
||||
// font-size: @font-size-lg;
|
||||
// }
|
||||
|
||||
&-message {
|
||||
color: @alert-message-color;
|
||||
}
|
||||
// &-message {
|
||||
// color: @alert-message-color;
|
||||
// }
|
||||
|
||||
&-with-description &-description {
|
||||
display: block;
|
||||
}
|
||||
// &-with-description &-description {
|
||||
// display: block;
|
||||
// }
|
||||
|
||||
&&-motion-leave {
|
||||
overflow: hidden;
|
||||
opacity: 1;
|
||||
transition: max-height 0.3s @ease-in-out-circ, opacity 0.3s @ease-in-out-circ,
|
||||
padding-top 0.3s @ease-in-out-circ, padding-bottom 0.3s @ease-in-out-circ,
|
||||
margin-bottom 0.3s @ease-in-out-circ;
|
||||
}
|
||||
// &&-motion-leave {
|
||||
// overflow: hidden;
|
||||
// opacity: 1;
|
||||
// transition: max-height 0.3s @ease-in-out-circ, opacity 0.3s @ease-in-out-circ,
|
||||
// padding-top 0.3s @ease-in-out-circ, padding-bottom 0.3s @ease-in-out-circ,
|
||||
// margin-bottom 0.3s @ease-in-out-circ;
|
||||
// }
|
||||
|
||||
&&-motion-leave-active {
|
||||
max-height: 0;
|
||||
margin-bottom: 0 !important;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
// &&-motion-leave-active {
|
||||
// max-height: 0;
|
||||
// margin-bottom: 0 !important;
|
||||
// padding-top: 0;
|
||||
// padding-bottom: 0;
|
||||
// opacity: 0;
|
||||
// }
|
||||
|
||||
&-banner {
|
||||
margin-bottom: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
// &-banner {
|
||||
// margin-bottom: 0;
|
||||
// border: 0;
|
||||
// border-radius: 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
@import './rtl';
|
||||
// @import './rtl';
|
||||
|
@ -1,2 +1,342 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
// import '../../style/index.less';
|
||||
// import './index.less';
|
||||
|
||||
// deps-lint-skip-all
|
||||
import { generate } from '@ant-design/colors';
|
||||
import { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
|
||||
|
||||
import {
|
||||
DerivativeToken,
|
||||
useStyleRegister,
|
||||
useToken,
|
||||
resetComponent,
|
||||
UseComponentStyleResult,
|
||||
GenerateStyle,
|
||||
} from '../../_util/theme';
|
||||
|
||||
// FIXME: missing token
|
||||
type AlertToken = DerivativeToken & {
|
||||
alertCls: string;
|
||||
iconPrefixCls: string;
|
||||
|
||||
alertMessageColor: string;
|
||||
alertCloseColor: string;
|
||||
alertCloseHoverColor: string;
|
||||
|
||||
alertInfoBgColor: string;
|
||||
alertInfoIconColor: string;
|
||||
alertInfoBorderColor: string;
|
||||
|
||||
alertSuccessBgColor: string;
|
||||
alertSuccessIconColor: string;
|
||||
alertSuccessBorderColor: string;
|
||||
|
||||
alertWarningBgColor: string;
|
||||
alertWarningIconColor: string;
|
||||
alertWarningBorderColor: string;
|
||||
|
||||
alertErrorBgColor: string;
|
||||
alertErrorIconColor: string;
|
||||
alertErrorBorderColor: string;
|
||||
|
||||
alertWithDescriptionIconSize: number;
|
||||
alertWithDescriptionPaddingVertical: number;
|
||||
alertWithDescriptionNoIconPaddingVertical: number;
|
||||
};
|
||||
|
||||
const genAlertTypeStyle = (
|
||||
bgColor: string,
|
||||
borderColor: string,
|
||||
iconColor: string,
|
||||
token: AlertToken,
|
||||
alertCls: string,
|
||||
): CSSObject => ({
|
||||
backgroundColor: bgColor,
|
||||
border: `${token.borderWidth}px ${token.borderStyle} ${borderColor}`,
|
||||
[`${alertCls}-icon`]: {
|
||||
color: iconColor,
|
||||
},
|
||||
});
|
||||
|
||||
export const genBaseStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSObject => {
|
||||
const {
|
||||
alertCls,
|
||||
duration,
|
||||
marginXS,
|
||||
fontSize,
|
||||
fontSizeLG,
|
||||
borderRadius,
|
||||
easeInOutCirc,
|
||||
alertMessageColor,
|
||||
alertWithDescriptionIconSize,
|
||||
alertWithDescriptionPaddingVertical,
|
||||
alertWithDescriptionNoIconPaddingVertical,
|
||||
} = token;
|
||||
|
||||
return {
|
||||
[alertCls]: {
|
||||
...resetComponent(token),
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: '8px 15px',
|
||||
wordWrap: 'break-word',
|
||||
borderRadius,
|
||||
|
||||
'&&-rtl': {
|
||||
direction: 'rtl',
|
||||
},
|
||||
|
||||
[`${alertCls}-content`]: {
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
},
|
||||
|
||||
[`${alertCls}-icon`]: {
|
||||
marginInlineEnd: marginXS,
|
||||
},
|
||||
|
||||
[`&-description`]: {
|
||||
display: 'none',
|
||||
fontSize,
|
||||
lineHeight: `${fontSize + 8}px`,
|
||||
},
|
||||
|
||||
'&-message': {
|
||||
color: alertMessageColor,
|
||||
},
|
||||
|
||||
'&&-motion-leave': {
|
||||
overflow: 'hidden',
|
||||
opacity: 1,
|
||||
transition: `max-height ${duration} ${easeInOutCirc}, opacity ${duration} ${easeInOutCirc},
|
||||
padding-top ${duration} ${easeInOutCirc}, padding-bottom ${duration} ${easeInOutCirc},
|
||||
margin-bottom ${duration} ${easeInOutCirc}`,
|
||||
},
|
||||
|
||||
'&&-motion-leave-active': {
|
||||
maxHeight: 0,
|
||||
marginBottom: '0 !important',
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
|
||||
[`${alertCls}-with-description`]: {
|
||||
alignItems: 'flex-start',
|
||||
paddingInlineStart: alertWithDescriptionIconSize,
|
||||
paddingInlineEnd: alertWithDescriptionPaddingVertical,
|
||||
paddingBlock: alertWithDescriptionPaddingVertical,
|
||||
|
||||
[`&${alertCls}-no-icon`]: {
|
||||
padding: `${alertWithDescriptionNoIconPaddingVertical}px 15px`,
|
||||
},
|
||||
|
||||
[`${alertCls}-icon`]: {
|
||||
marginInlineEnd: alertWithDescriptionPaddingVertical,
|
||||
fontSize: alertWithDescriptionIconSize,
|
||||
},
|
||||
|
||||
[`${alertCls}-message`]: {
|
||||
display: 'block',
|
||||
marginBottom: '4px',
|
||||
color: alertMessageColor,
|
||||
fontSize: fontSizeLG,
|
||||
},
|
||||
|
||||
[`${alertCls}-description`]: {
|
||||
display: 'block',
|
||||
},
|
||||
},
|
||||
|
||||
[`${alertCls}-banner`]: {
|
||||
marginBottom: 0,
|
||||
border: '0 !important',
|
||||
borderRadius: 0,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const genTypeStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSObject => {
|
||||
const {
|
||||
alertCls,
|
||||
alertInfoBgColor,
|
||||
alertInfoIconColor,
|
||||
alertInfoBorderColor,
|
||||
|
||||
alertSuccessBgColor,
|
||||
alertSuccessIconColor,
|
||||
alertSuccessBorderColor,
|
||||
|
||||
alertWarningBgColor,
|
||||
alertWarningIconColor,
|
||||
alertWarningBorderColor,
|
||||
|
||||
alertErrorBgColor,
|
||||
alertErrorIconColor,
|
||||
alertErrorBorderColor,
|
||||
} = token;
|
||||
|
||||
return {
|
||||
[alertCls]: {
|
||||
'&-success': genAlertTypeStyle(
|
||||
alertSuccessBgColor,
|
||||
alertSuccessBorderColor,
|
||||
alertSuccessIconColor,
|
||||
token,
|
||||
alertCls,
|
||||
),
|
||||
'&-info': genAlertTypeStyle(
|
||||
alertInfoBgColor,
|
||||
alertInfoBorderColor,
|
||||
alertInfoIconColor,
|
||||
token,
|
||||
alertCls,
|
||||
),
|
||||
'&-warning': genAlertTypeStyle(
|
||||
alertWarningBgColor,
|
||||
alertWarningBorderColor,
|
||||
alertWarningIconColor,
|
||||
token,
|
||||
alertCls,
|
||||
),
|
||||
'&-error': {
|
||||
...genAlertTypeStyle(
|
||||
alertErrorBgColor,
|
||||
alertErrorBorderColor,
|
||||
alertErrorIconColor,
|
||||
token,
|
||||
alertCls,
|
||||
),
|
||||
[`${alertCls}-description > pre`]: {
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const genActionStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSObject => {
|
||||
const {
|
||||
alertCls,
|
||||
iconPrefixCls,
|
||||
duration,
|
||||
marginXS,
|
||||
fontSizeSM,
|
||||
alertCloseColor,
|
||||
alertCloseHoverColor,
|
||||
} = token;
|
||||
|
||||
return {
|
||||
[alertCls]: {
|
||||
[`&-action`]: {
|
||||
marginInlineStart: marginXS,
|
||||
},
|
||||
|
||||
[`${alertCls}-close-icon`]: {
|
||||
marginInlineStart: marginXS,
|
||||
padding: 0,
|
||||
overflow: 'hidden',
|
||||
fontSize: fontSizeSM,
|
||||
lineHeight: `${fontSizeSM}px`,
|
||||
backgroundColor: 'transparent',
|
||||
border: 'none',
|
||||
outline: 'none',
|
||||
cursor: 'pointer',
|
||||
|
||||
[`.${iconPrefixCls}-close`]: {
|
||||
color: alertCloseColor,
|
||||
transition: `color ${duration}`,
|
||||
'&:hover': {
|
||||
color: alertCloseHoverColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
'&-close-text': {
|
||||
color: alertCloseColor,
|
||||
transition: `color ${duration}`,
|
||||
'&:hover': {
|
||||
color: alertCloseHoverColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const genAlertStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSInterpolation => [
|
||||
genBaseStyle(token),
|
||||
genTypeStyle(token),
|
||||
genActionStyle(token),
|
||||
];
|
||||
|
||||
export default function useStyle(
|
||||
prefixCls: string,
|
||||
iconPrefixCls: string,
|
||||
): UseComponentStyleResult {
|
||||
const [theme, token, hashId] = useToken();
|
||||
|
||||
const alertCls = `.${prefixCls}`;
|
||||
|
||||
const alertMessageColor = token.headingColor;
|
||||
const alertCloseColor = token.textColorSecondary;
|
||||
const alertCloseHoverColor = token.iconColorHover;
|
||||
// FIXME
|
||||
const alertWithDescriptionIconSize = 24;
|
||||
const alertWithDescriptionPaddingVertical = token.padding - 1;
|
||||
const alertWithDescriptionNoIconPaddingVertical = token.padding - 1;
|
||||
|
||||
// FIXME
|
||||
const infoColors = generate(token.infoColor);
|
||||
const alertInfoBgColor = infoColors[0];
|
||||
const alertInfoIconColor = token.infoColor;
|
||||
const alertInfoBorderColor = infoColors[2];
|
||||
|
||||
const successColors = generate(token.successColor);
|
||||
const alertSuccessBgColor = successColors[0];
|
||||
const alertSuccessIconColor = token.successColor;
|
||||
const alertSuccessBorderColor = successColors[2];
|
||||
|
||||
const warningColors = generate(token.warningColor);
|
||||
const alertWarningBgColor = warningColors[0];
|
||||
const alertWarningIconColor = token.warningColor;
|
||||
const alertWarningBorderColor = warningColors[2];
|
||||
|
||||
const errorColors = generate(token.errorColor);
|
||||
const alertErrorBgColor = errorColors[0];
|
||||
const alertErrorIconColor = token.errorColor;
|
||||
const alertErrorBorderColor = errorColors[2];
|
||||
|
||||
const alertToken: AlertToken = {
|
||||
...token,
|
||||
alertCls,
|
||||
iconPrefixCls,
|
||||
alertInfoBgColor,
|
||||
alertInfoIconColor,
|
||||
alertInfoBorderColor,
|
||||
alertSuccessBgColor,
|
||||
alertSuccessIconColor,
|
||||
alertSuccessBorderColor,
|
||||
alertWarningBgColor,
|
||||
alertWarningIconColor,
|
||||
alertWarningBorderColor,
|
||||
alertErrorBgColor,
|
||||
alertErrorIconColor,
|
||||
alertErrorBorderColor,
|
||||
alertMessageColor,
|
||||
alertCloseColor,
|
||||
alertCloseHoverColor,
|
||||
alertWithDescriptionIconSize,
|
||||
alertWithDescriptionPaddingVertical,
|
||||
alertWithDescriptionNoIconPaddingVertical,
|
||||
};
|
||||
|
||||
return [
|
||||
useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => [
|
||||
genAlertStyle(alertToken),
|
||||
]),
|
||||
hashId,
|
||||
];
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ exports[`renders ./components/auto-complete/demo/certain-category.md extend cont
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<span
|
||||
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-large ant-select-selection-search-input"
|
||||
class="ant-input-group-wrapper ant-input-search ant-input-search-large ant-select-selection-search-input ant-input-group-wrapper-lg"
|
||||
>
|
||||
<span
|
||||
class="ant-input-wrapper ant-input-group"
|
||||
@ -1876,6 +1876,107 @@ exports[`renders ./components/auto-complete/demo/options.md extend context corre
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/auto-complete/demo/status.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-select-status-error ant-select-auto-complete ant-select-single ant-select-show-search"
|
||||
style="width:200px"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-activedescendant="undefined_list_0"
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
role="combobox"
|
||||
type="search"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-dropdown ant-select-dropdown-empty"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-item-empty"
|
||||
id="undefined_list"
|
||||
role="listbox"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-select-status-warning ant-select-auto-complete ant-select-single ant-select-show-search"
|
||||
style="width:200px"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-activedescendant="undefined_list_0"
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
role="combobox"
|
||||
type="search"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-dropdown ant-select-dropdown-empty"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-item-empty"
|
||||
id="undefined_list"
|
||||
role="listbox"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/auto-complete/demo/uncertain-category.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-select ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
|
||||
@ -1888,7 +1989,7 @@ exports[`renders ./components/auto-complete/demo/uncertain-category.md extend co
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<span
|
||||
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-large ant-input-search-with-button ant-select-selection-search-input"
|
||||
class="ant-input-group-wrapper ant-input-search ant-input-search-large ant-input-search-with-button ant-select-selection-search-input ant-input-group-wrapper-lg"
|
||||
>
|
||||
<span
|
||||
class="ant-input-wrapper ant-input-group"
|
||||
|
@ -79,7 +79,7 @@ exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<span
|
||||
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-large ant-select-selection-search-input"
|
||||
class="ant-input-group-wrapper ant-input-search ant-input-search-large ant-select-selection-search-input ant-input-group-wrapper-lg"
|
||||
>
|
||||
<span
|
||||
class="ant-input-wrapper ant-input-group"
|
||||
@ -1033,6 +1033,79 @@ exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/auto-complete/demo/status.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-select-status-error ant-select-auto-complete ant-select-single ant-select-show-search"
|
||||
style="width:200px"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-activedescendant="undefined_list_0"
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
role="combobox"
|
||||
type="search"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-select-status-warning ant-select-auto-complete ant-select-single ant-select-show-search"
|
||||
style="width:200px"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-activedescendant="undefined_list_0"
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
role="combobox"
|
||||
type="search"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-select ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
|
||||
@ -1045,7 +1118,7 @@ exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<span
|
||||
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-search ant-input-search-large ant-input-search-with-button ant-select-selection-search-input"
|
||||
class="ant-input-group-wrapper ant-input-search ant-input-search-large ant-input-search-with-button ant-select-selection-search-input ant-input-group-wrapper-lg"
|
||||
>
|
||||
<span
|
||||
class="ant-input-wrapper ant-input-group"
|
||||
|
42
components/auto-complete/demo/status.md
Normal file
42
components/auto-complete/demo/status.md
Normal file
@ -0,0 +1,42 @@
|
||||
---
|
||||
order: 19
|
||||
version: 4.19.0
|
||||
title:
|
||||
zh-CN: 自定义状态
|
||||
en-US: Status
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `status` 为 AutoComplete 添加状态,可选 `error` 或者 `warning`。
|
||||
|
||||
## en-US
|
||||
|
||||
Add status to AutoComplete with `status`, which could be `error` or `warning`.
|
||||
|
||||
```tsx
|
||||
import React, { useState } from 'react';
|
||||
import { AutoComplete, Space } from 'antd';
|
||||
|
||||
const mockVal = (str: string, repeat: number = 1) => ({
|
||||
value: str.repeat(repeat),
|
||||
});
|
||||
const ValidateInputs: React.FC = () => {
|
||||
const [options, setOptions] = useState<{ value: string }[]>([]);
|
||||
|
||||
const onSearch = (searchText: string) => {
|
||||
setOptions(
|
||||
!searchText ? [] : [mockVal(searchText), mockVal(searchText, 2), mockVal(searchText, 3)],
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<AutoComplete options={options} onSearch={onSearch} status="error" style={{ width: 200 }} />
|
||||
<AutoComplete options={options} onSearch={onSearch} status="warning" style={{ width: 200 }} />
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<ValidateInputs />, mountNode);
|
||||
```
|
@ -38,6 +38,7 @@ The differences with Select are:
|
||||
| open | Controlled open state of dropdown | boolean | - | |
|
||||
| options | Select options. Will get better perf than jsx definition | { label, value }\[] | - | |
|
||||
| placeholder | The placeholder of input | string | - | |
|
||||
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| value | Selected option | string | - | |
|
||||
| onBlur | Called when leaving the component | function() | - | |
|
||||
| onChange | Called when select an option or input value change, or value of input is changed | function(value) | - | |
|
||||
@ -45,13 +46,14 @@ The differences with Select are:
|
||||
| onFocus | Called when entering the component | function() | - | |
|
||||
| onSearch | Called when searching items | function(value) | - | |
|
||||
| onSelect | Called when a option is selected. param is option's value and option instance | function(value, option) | - | |
|
||||
| onClear | Called when clear | function | - | 4.6.0 |
|
||||
|
||||
## Methods
|
||||
|
||||
| Name | Description | Version |
|
||||
| --- | --- | --- |
|
||||
| blur() | Remove focus | |
|
||||
| focus() | Get focus | |
|
||||
| Name | Description | Version |
|
||||
| ------- | ------------ | ------- |
|
||||
| blur() | Remove focus | |
|
||||
| focus() | Get focus | |
|
||||
|
||||
## FAQ
|
||||
|
||||
|
@ -20,6 +20,7 @@ import Select, {
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import devWarning from '../_util/devWarning';
|
||||
import { isValidElement } from '../_util/reactNode';
|
||||
import { InputStatus } from '../_util/statusUtils';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
@ -37,6 +38,7 @@ export interface AutoCompleteProps<
|
||||
'inputIcon' | 'loading' | 'mode' | 'optionLabelProp' | 'labelInValue'
|
||||
> {
|
||||
dataSource?: DataSourceItemType[];
|
||||
status?: InputStatus;
|
||||
}
|
||||
|
||||
function isSelectOptionOrSelectOptGroup(child: any): Boolean {
|
||||
|
@ -40,6 +40,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/qtJm4yt45/AutoComplete.svg
|
||||
| open | 是否展开下拉菜单 | boolean | - | |
|
||||
| options | 数据化配置选项内容,相比 jsx 定义会获得更好的渲染性能 | { label, value }\[] | - | |
|
||||
| placeholder | 输入框提示 | string | - | |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| value | 指定当前选中的条目 | string | - | |
|
||||
| onBlur | 失去焦点时的回调 | function() | - | |
|
||||
| onChange | 选中 option,或 input 的 value 变化时,调用此函数 | function(value) | - | |
|
||||
@ -47,13 +48,14 @@ cover: https://gw.alipayobjects.com/zos/alicdn/qtJm4yt45/AutoComplete.svg
|
||||
| onFocus | 获得焦点时的回调 | function() | - | |
|
||||
| onSearch | 搜索补全项的时候调用 | function(value) | - | |
|
||||
| onSelect | 被选中时调用,参数为选中项的 value 值 | function(value, option) | - | |
|
||||
| onClear | 清除内容时回调 | function | - | 4.6.0 |
|
||||
|
||||
## 方法
|
||||
|
||||
| 名称 | 描述 | 版本 |
|
||||
| --- | --- | --- |
|
||||
| blur() | 移除焦点 | |
|
||||
| focus() | 获取焦点 | |
|
||||
| 名称 | 描述 | 版本 |
|
||||
| ------- | -------- | ---- |
|
||||
| blur() | 移除焦点 | |
|
||||
| focus() | 获取焦点 | |
|
||||
|
||||
## FAQ
|
||||
|
||||
|
@ -102,7 +102,10 @@ const InternalAvatar: React.ForwardRefRenderFunction<unknown, AvatarProps> = (pr
|
||||
|
||||
const size = customSize === 'default' ? groupSize : customSize;
|
||||
|
||||
const screens = useBreakpoint();
|
||||
const needResponsive = Object.keys(typeof size === 'object' ? size || {} : {}).some(key =>
|
||||
['xs', 'sm', 'md', 'lg', 'xl', 'xxl'].includes(key),
|
||||
);
|
||||
const screens = useBreakpoint(needResponsive);
|
||||
const responsiveSizeStyle: React.CSSProperties = React.useMemo(() => {
|
||||
if (typeof size !== 'object') {
|
||||
return {};
|
||||
|
@ -30,7 +30,7 @@ const BreadcrumbItem: BreadcrumbItemInterface = ({
|
||||
const renderBreadcrumbNode = (breadcrumbItem: React.ReactNode) => {
|
||||
if (overlay) {
|
||||
return (
|
||||
<DropDown overlay={overlay} placement="bottomCenter" {...dropdownProps}>
|
||||
<DropDown overlay={overlay} placement="bottom" {...dropdownProps}>
|
||||
<span className={`${prefixCls}-overlay-link`}>
|
||||
{breadcrumbItem}
|
||||
<DownOutlined />
|
||||
@ -62,9 +62,7 @@ const BreadcrumbItem: BreadcrumbItemInterface = ({
|
||||
return (
|
||||
<span>
|
||||
{link}
|
||||
{separator && (
|
||||
<span className={`${prefixCls}-separator`}>{separator}</span>
|
||||
)}
|
||||
{separator && <span className={`${prefixCls}-separator`}>{separator}</span>}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
2
components/button/_style/index.tsx
Normal file
2
components/button/_style/index.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
@ -12,6 +12,9 @@ import SizeContext, { SizeType } from '../config-provider/SizeContext';
|
||||
import LoadingIcon from './LoadingIcon';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
|
||||
// CSSINJS
|
||||
import useStyle from './style';
|
||||
|
||||
const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/;
|
||||
const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar);
|
||||
function isString(str: any) {
|
||||
@ -151,10 +154,16 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const { getPrefixCls, autoInsertSpaceInButton, direction, iconPrefixCls } =
|
||||
React.useContext(ConfigContext);
|
||||
const prefixCls = getPrefixCls('btn', customizePrefixCls);
|
||||
|
||||
// Style
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls, iconPrefixCls);
|
||||
|
||||
const size = React.useContext(SizeContext);
|
||||
const [innerLoading, setLoading] = React.useState<Loading>(!!loading);
|
||||
const [hasTwoCNChar, setHasTwoCNChar] = React.useState(false);
|
||||
const { getPrefixCls, autoInsertSpaceInButton, direction } = React.useContext(ConfigContext);
|
||||
const buttonRef = (ref as any) || React.createRef<HTMLElement>();
|
||||
|
||||
const isNeedInserted = () =>
|
||||
@ -225,7 +234,6 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
|
||||
"`link` or `text` button can't be a `ghost` button.",
|
||||
);
|
||||
|
||||
const prefixCls = getPrefixCls('btn', customizePrefixCls);
|
||||
const autoInsertSpace = autoInsertSpaceInButton !== false;
|
||||
|
||||
const sizeClassNameMap = { large: 'lg', small: 'sm', middle: undefined };
|
||||
@ -236,6 +244,7 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
|
||||
|
||||
const classes = classNames(
|
||||
prefixCls,
|
||||
hashId,
|
||||
{
|
||||
[`${prefixCls}-${shape}`]: shape !== 'default' && shape, // Note: Shape also has `default`
|
||||
[`${prefixCls}-${type}`]: type,
|
||||
@ -265,15 +274,15 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
|
||||
|
||||
const linkButtonRestProps = omit(rest as AnchorButtonProps & { navigate: any }, ['navigate']);
|
||||
if (linkButtonRestProps.href !== undefined) {
|
||||
return (
|
||||
return wrapSSR(
|
||||
<a {...linkButtonRestProps} className={classes} onClick={handleClick} ref={buttonRef}>
|
||||
{iconNode}
|
||||
{kids}
|
||||
</a>
|
||||
</a>,
|
||||
);
|
||||
}
|
||||
|
||||
const buttonNode = (
|
||||
let buttonNode = (
|
||||
<button
|
||||
{...(rest as NativeButtonProps)}
|
||||
type={htmlType}
|
||||
@ -286,11 +295,11 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
|
||||
</button>
|
||||
);
|
||||
|
||||
if (isUnborderedButtonType(type)) {
|
||||
return buttonNode;
|
||||
if (!isUnborderedButtonType(type)) {
|
||||
buttonNode = <Wave disabled={!!innerLoading}>{buttonNode}</Wave>;
|
||||
}
|
||||
|
||||
return <Wave disabled={!!innerLoading}>{buttonNode}</Wave>;
|
||||
return wrapSSR(buttonNode);
|
||||
};
|
||||
|
||||
const Button = React.forwardRef<unknown, ButtonProps>(InternalButton) as CompoundedComponent;
|
||||
|
@ -1,2 +1,403 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
// deps-lint-skip-all
|
||||
import { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
|
||||
import { TinyColor } from '@ctrl/tinycolor';
|
||||
import {
|
||||
DerivativeToken,
|
||||
UseComponentStyleResult,
|
||||
useStyleRegister,
|
||||
useToken,
|
||||
withPrefix,
|
||||
} from '../../_util/theme';
|
||||
|
||||
// ============================== Shared ==============================
|
||||
const genSharedButtonStyle = (
|
||||
prefixCls: string,
|
||||
iconPrefixCls: string,
|
||||
token: DerivativeToken,
|
||||
): CSSObject => ({
|
||||
outline: 'none',
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
fontWeight: 400,
|
||||
whiteSpace: 'nowrap',
|
||||
textAlign: 'center',
|
||||
backgroundImage: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
border: `${token.borderWidth}px ${token.borderStyle} transparent`,
|
||||
cursor: 'pointer',
|
||||
transition: `all ${token.duration} ${token.easeInOut}`,
|
||||
userSelect: 'none',
|
||||
touchAction: 'manipulation',
|
||||
lineHeight: token.lineHeight,
|
||||
color: token.textColor,
|
||||
|
||||
'> span': {
|
||||
display: 'inline-block',
|
||||
},
|
||||
|
||||
// Leave a space between icon and text.
|
||||
[`> .${iconPrefixCls} + span, > span + .${iconPrefixCls}`]: {
|
||||
marginInlineStart: token.marginXS,
|
||||
},
|
||||
|
||||
[`&.${prefixCls}-block`]: {
|
||||
width: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
const genHoverActiveButtonStyle = (hoverStyle: CSSObject, activeStyle: CSSObject): CSSObject => ({
|
||||
'&:not(:disabled)': {
|
||||
'&:hover, &:focus': hoverStyle,
|
||||
'&:active': activeStyle,
|
||||
},
|
||||
});
|
||||
|
||||
// ============================== Shape ===============================
|
||||
const genCircleButtonStyle = (token: DerivativeToken): CSSObject => ({
|
||||
minWidth: token.controlHeight,
|
||||
paddingInlineStart: 0,
|
||||
paddingInlineEnd: 0,
|
||||
borderRadius: '50%',
|
||||
});
|
||||
|
||||
const genRoundButtonStyle = (token: DerivativeToken): CSSObject => ({
|
||||
borderRadius: token.controlHeight,
|
||||
paddingInlineStart: token.controlHeight / 2,
|
||||
paddingInlineEnd: token.controlHeight / 2,
|
||||
width: 'auto',
|
||||
});
|
||||
|
||||
// =============================== Type ===============================
|
||||
const genGhostButtonStyle = (
|
||||
prefixCls: string,
|
||||
textColor: string | false,
|
||||
borderColor: string | false,
|
||||
textColorDisabled: string | false,
|
||||
borderColorDisabled: string | false,
|
||||
): CSSObject => ({
|
||||
[`&.${prefixCls}-background-ghost`]: {
|
||||
color: textColor || undefined,
|
||||
backgroundColor: 'transparent',
|
||||
borderColor: borderColor || undefined,
|
||||
|
||||
'&:disabled': {
|
||||
cursor: 'not-allowed',
|
||||
color: textColorDisabled || undefined,
|
||||
borderColor: borderColorDisabled || undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const genSolidDisabledButtonStyle = (token: DerivativeToken): CSSObject => ({
|
||||
'&:disabled': {
|
||||
cursor: 'not-allowed',
|
||||
borderColor: token.borderColor,
|
||||
color: token.textColorDisabled,
|
||||
backgroundColor: token.componentBackgroundDisabled,
|
||||
boxShadow: 'none',
|
||||
},
|
||||
});
|
||||
|
||||
const genSolidButtonStyle = (token: DerivativeToken): CSSObject => ({
|
||||
borderRadius: token.borderRadius,
|
||||
|
||||
...genSolidDisabledButtonStyle(token),
|
||||
});
|
||||
|
||||
const genPureDisabledButtonStyle = (token: DerivativeToken): CSSObject => ({
|
||||
'&:disabled': {
|
||||
cursor: 'not-allowed',
|
||||
color: token.textColorDisabled,
|
||||
},
|
||||
});
|
||||
|
||||
// Type: Default
|
||||
const genDefaultButtonStyle = (prefixCls: string, token: DerivativeToken): CSSObject => ({
|
||||
...genSolidButtonStyle(token),
|
||||
|
||||
backgroundColor: token.componentBackground,
|
||||
borderColor: token.borderColor,
|
||||
|
||||
boxShadow: '0 2px 0 rgba(0, 0, 0, 0.015)',
|
||||
|
||||
...genHoverActiveButtonStyle(
|
||||
{
|
||||
color: token.primaryHoverColor,
|
||||
borderColor: token.primaryHoverColor,
|
||||
},
|
||||
{
|
||||
color: token.primaryActiveColor,
|
||||
borderColor: token.primaryActiveColor,
|
||||
},
|
||||
),
|
||||
|
||||
...genGhostButtonStyle(
|
||||
prefixCls,
|
||||
token.componentBackground,
|
||||
token.componentBackground,
|
||||
token.textColorDisabled,
|
||||
token.borderColor,
|
||||
),
|
||||
|
||||
[`&.${prefixCls}-dangerous`]: {
|
||||
color: token.errorColor,
|
||||
borderColor: token.errorColor,
|
||||
|
||||
...genHoverActiveButtonStyle(
|
||||
{
|
||||
color: token.errorHoverColor,
|
||||
borderColor: token.errorHoverColor,
|
||||
},
|
||||
{
|
||||
color: token.errorActiveColor,
|
||||
borderColor: token.errorActiveColor,
|
||||
},
|
||||
),
|
||||
|
||||
...genGhostButtonStyle(
|
||||
prefixCls,
|
||||
token.errorColor,
|
||||
token.errorColor,
|
||||
token.textColorDisabled,
|
||||
token.borderColor,
|
||||
),
|
||||
...genSolidDisabledButtonStyle(token),
|
||||
},
|
||||
});
|
||||
|
||||
// Type: Primary
|
||||
const genPrimaryButtonStyle = (prefixCls: string, token: DerivativeToken): CSSObject => ({
|
||||
...genSolidButtonStyle(token),
|
||||
|
||||
color: '#FFF',
|
||||
backgroundColor: token.primaryColor,
|
||||
|
||||
boxShadow: '0 2px 0 rgba(0, 0, 0, 0.045)',
|
||||
|
||||
...genHoverActiveButtonStyle(
|
||||
{
|
||||
backgroundColor: token.primaryHoverColor,
|
||||
},
|
||||
{
|
||||
backgroundColor: token.primaryActiveColor,
|
||||
},
|
||||
),
|
||||
|
||||
...genGhostButtonStyle(
|
||||
prefixCls,
|
||||
token.primaryColor,
|
||||
token.primaryColor,
|
||||
token.textColorDisabled,
|
||||
token.borderColor,
|
||||
),
|
||||
|
||||
[`&.${prefixCls}-dangerous`]: {
|
||||
backgroundColor: token.errorColor,
|
||||
|
||||
...genHoverActiveButtonStyle(
|
||||
{
|
||||
backgroundColor: token.errorHoverColor,
|
||||
},
|
||||
{
|
||||
backgroundColor: token.errorActiveColor,
|
||||
},
|
||||
),
|
||||
|
||||
...genGhostButtonStyle(
|
||||
prefixCls,
|
||||
token.errorColor,
|
||||
token.errorColor,
|
||||
token.textColorDisabled,
|
||||
token.borderColor,
|
||||
),
|
||||
...genSolidDisabledButtonStyle(token),
|
||||
},
|
||||
});
|
||||
|
||||
// Type: Dashed
|
||||
const genDashedButtonStyle = (prefixCls: string, token: DerivativeToken): CSSObject => ({
|
||||
...genDefaultButtonStyle(prefixCls, token),
|
||||
|
||||
borderStyle: 'dashed',
|
||||
});
|
||||
|
||||
// Type: Link
|
||||
const genLinkButtonStyle = (prefixCls: string, token: DerivativeToken): CSSObject => ({
|
||||
color: token.linkColor,
|
||||
|
||||
...genHoverActiveButtonStyle(
|
||||
{
|
||||
color: token.primaryHoverColor,
|
||||
},
|
||||
{
|
||||
color: token.primaryActiveColor,
|
||||
},
|
||||
),
|
||||
|
||||
...genPureDisabledButtonStyle(token),
|
||||
|
||||
[`&.${prefixCls}-dangerous`]: {
|
||||
color: token.errorColor,
|
||||
|
||||
...genHoverActiveButtonStyle(
|
||||
{
|
||||
color: token.errorHoverColor,
|
||||
},
|
||||
{
|
||||
color: token.errorActiveColor,
|
||||
},
|
||||
),
|
||||
|
||||
...genPureDisabledButtonStyle(token),
|
||||
},
|
||||
});
|
||||
|
||||
// Type: Text
|
||||
const genTextButtonStyle = (prefixCls: string, token: DerivativeToken): CSSObject => {
|
||||
const backgroundColor = new TinyColor({ r: 0, g: 0, b: 0, a: 0.018 });
|
||||
|
||||
return {
|
||||
...genHoverActiveButtonStyle(
|
||||
{
|
||||
backgroundColor: backgroundColor.toRgbString(),
|
||||
},
|
||||
{
|
||||
backgroundColor: backgroundColor
|
||||
.clone()
|
||||
.setAlpha(backgroundColor.getAlpha() * 1.5)
|
||||
.toRgbString(),
|
||||
},
|
||||
),
|
||||
|
||||
...genPureDisabledButtonStyle(token),
|
||||
|
||||
[`&.${prefixCls}-dangerous`]: {
|
||||
color: token.errorColor,
|
||||
|
||||
...genPureDisabledButtonStyle(token),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genTypeButtonStyle = (prefixCls: string, token: DerivativeToken): CSSInterpolation => [
|
||||
withPrefix(genDefaultButtonStyle(prefixCls, token), `${prefixCls}-default`, []),
|
||||
withPrefix(genPrimaryButtonStyle(prefixCls, token), `${prefixCls}-primary`, []),
|
||||
withPrefix(genDashedButtonStyle(prefixCls, token), `${prefixCls}-dashed`, []),
|
||||
withPrefix(genLinkButtonStyle(prefixCls, token), `${prefixCls}-link`, []),
|
||||
withPrefix(genTextButtonStyle(prefixCls, token), `${prefixCls}-text`, []),
|
||||
];
|
||||
|
||||
// =============================== Size ===============================
|
||||
const genSizeButtonStyle = (
|
||||
prefixCls: string,
|
||||
iconPrefixCls: string,
|
||||
sizePrefixCls: string,
|
||||
token: DerivativeToken,
|
||||
): CSSInterpolation => {
|
||||
const paddingVertical = Math.max(
|
||||
0,
|
||||
(token.controlHeight - token.fontSize * token.lineHeight) / 2 - token.borderWidth,
|
||||
);
|
||||
const paddingHorizontal = token.padding - token.borderWidth;
|
||||
|
||||
const iconOnlyCls = `.${prefixCls}-icon-only`;
|
||||
|
||||
return [
|
||||
// Size
|
||||
withPrefix(
|
||||
{
|
||||
fontSize: token.fontSize,
|
||||
height: token.controlHeight,
|
||||
padding: `${paddingVertical}px ${paddingHorizontal}px`,
|
||||
|
||||
[`&${iconOnlyCls}`]: {
|
||||
width: token.controlHeight,
|
||||
paddingInlineStart: 0,
|
||||
paddingInlineEnd: 0,
|
||||
|
||||
'> span': {
|
||||
transform: 'scale(1.143)', // 14px -> 16px
|
||||
},
|
||||
},
|
||||
|
||||
// Loading
|
||||
[`&.${prefixCls}-loading`]: {
|
||||
opacity: 0.65,
|
||||
cursor: 'default',
|
||||
},
|
||||
|
||||
[`.${prefixCls}-loading-icon`]: {
|
||||
transition: `width ${token.duration} ${token.easeInOut}, opacity ${token.duration} ${token.easeInOut}`,
|
||||
},
|
||||
|
||||
[`&:not(${iconOnlyCls}) .${prefixCls}-loading-icon > .${iconPrefixCls}`]: {
|
||||
marginInlineEnd: token.marginXS,
|
||||
},
|
||||
},
|
||||
prefixCls,
|
||||
[sizePrefixCls],
|
||||
),
|
||||
|
||||
// Shape - patch prefixCls again to override solid border radius style
|
||||
withPrefix(genCircleButtonStyle(token), `${prefixCls}-circle`, [prefixCls, sizePrefixCls]),
|
||||
withPrefix(genRoundButtonStyle(token), `${prefixCls}-round`, [prefixCls, sizePrefixCls]),
|
||||
];
|
||||
};
|
||||
|
||||
const genSizeBaseButtonStyle = (
|
||||
prefixCls: string,
|
||||
iconPrefixCls: string,
|
||||
token: DerivativeToken,
|
||||
): CSSInterpolation => genSizeButtonStyle(prefixCls, iconPrefixCls, '', token);
|
||||
|
||||
const genSizeSmallButtonStyle = (
|
||||
prefixCls: string,
|
||||
iconPrefixCls: string,
|
||||
token: DerivativeToken,
|
||||
): CSSInterpolation => {
|
||||
const largeToken: DerivativeToken = {
|
||||
...token,
|
||||
controlHeight: token.controlHeightSM,
|
||||
padding: token.paddingXS,
|
||||
};
|
||||
|
||||
return genSizeButtonStyle(prefixCls, iconPrefixCls, `${prefixCls}-sm`, largeToken);
|
||||
};
|
||||
|
||||
const genSizeLargeButtonStyle = (
|
||||
prefixCls: string,
|
||||
iconPrefixCls: string,
|
||||
token: DerivativeToken,
|
||||
): CSSInterpolation => {
|
||||
const largeToken: DerivativeToken = {
|
||||
...token,
|
||||
controlHeight: token.controlHeightLG,
|
||||
fontSize: token.fontSizeLG,
|
||||
};
|
||||
|
||||
return genSizeButtonStyle(prefixCls, iconPrefixCls, `${prefixCls}-lg`, largeToken);
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default function useStyle(
|
||||
prefixCls: string,
|
||||
iconPrefixCls: string,
|
||||
): UseComponentStyleResult {
|
||||
const [theme, token, hashId] = useToken();
|
||||
|
||||
return [
|
||||
useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => [
|
||||
// Shared
|
||||
withPrefix(genSharedButtonStyle(prefixCls, iconPrefixCls, token), prefixCls),
|
||||
|
||||
// Size
|
||||
genSizeSmallButtonStyle(prefixCls, iconPrefixCls, token),
|
||||
genSizeBaseButtonStyle(prefixCls, iconPrefixCls, token),
|
||||
genSizeLargeButtonStyle(prefixCls, iconPrefixCls, token),
|
||||
|
||||
// Group (type, ghost, danger, disabled, loading)
|
||||
genTypeButtonStyle(prefixCls, token),
|
||||
]),
|
||||
hashId,
|
||||
];
|
||||
}
|
||||
|
@ -1674,6 +1674,241 @@ exports[`renders ./components/cascader/demo/multiple.md extend context correctly
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/placement.md extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-radio-group ant-radio-group-outline"
|
||||
>
|
||||
<label
|
||||
class="ant-radio-button-wrapper ant-radio-button-wrapper-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button ant-radio-button-checked"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="topLeft"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
topLeft
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="topRight"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
topRight
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="bottomLeft"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
bottomLeft
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="bottomRight"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
bottomRight
|
||||
</span>
|
||||
</label>
|
||||
</div>,
|
||||
<br />,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
style="opacity:0"
|
||||
type="search"
|
||||
unselectable="on"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
>
|
||||
Please select
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-dropdown ant-cascader-dropdown"
|
||||
style="opacity:0;pointer-events:none;min-width:auto"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-cascader-menus"
|
||||
>
|
||||
<ul
|
||||
class="ant-cascader-menu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
aria-checked="false"
|
||||
class="ant-cascader-menu-item ant-cascader-menu-item-expand"
|
||||
data-path-key="zhejiang"
|
||||
role="menuitemcheckbox"
|
||||
title="Zhejiang"
|
||||
>
|
||||
<div
|
||||
class="ant-cascader-menu-item-content"
|
||||
>
|
||||
Zhejiang
|
||||
</div>
|
||||
<div
|
||||
class="ant-cascader-menu-item-expand-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-checked="false"
|
||||
class="ant-cascader-menu-item ant-cascader-menu-item-expand"
|
||||
data-path-key="jiangsu"
|
||||
role="menuitemcheckbox"
|
||||
title="Jiangsu"
|
||||
>
|
||||
<div
|
||||
class="ant-cascader-menu-item-content"
|
||||
>
|
||||
Jiangsu
|
||||
</div>
|
||||
<div
|
||||
class="ant-cascader-menu-item-expand-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-select-suffix"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/search.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-show-search"
|
||||
@ -2267,6 +2502,276 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/status.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-status-error ant-select-single ant-select-allow-clear ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
style="opacity:0"
|
||||
type="search"
|
||||
unselectable="on"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
>
|
||||
Error
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-dropdown ant-cascader-dropdown ant-select-dropdown-empty"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-cascader-menus ant-cascader-menu-empty"
|
||||
>
|
||||
<ul
|
||||
class="ant-cascader-menu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
aria-checked="false"
|
||||
class="ant-cascader-menu-item ant-cascader-menu-item-disabled"
|
||||
data-path-key="__EMPTY__"
|
||||
role="menuitemcheckbox"
|
||||
>
|
||||
<div
|
||||
class="ant-cascader-menu-item-content"
|
||||
>
|
||||
<div
|
||||
class="ant-empty ant-empty-normal ant-empty-small"
|
||||
>
|
||||
<div
|
||||
class="ant-empty-image"
|
||||
>
|
||||
<svg
|
||||
class="ant-empty-img-simple"
|
||||
height="41"
|
||||
viewBox="0 0 64 41"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
transform="translate(0 1)"
|
||||
>
|
||||
<ellipse
|
||||
class="ant-empty-img-simple-ellipse"
|
||||
cx="32"
|
||||
cy="33"
|
||||
rx="32"
|
||||
ry="7"
|
||||
/>
|
||||
<g
|
||||
class="ant-empty-img-simple-g"
|
||||
fill-rule="nonzero"
|
||||
>
|
||||
<path
|
||||
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
|
||||
/>
|
||||
<path
|
||||
class="ant-empty-img-simple-path"
|
||||
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="ant-empty-description"
|
||||
>
|
||||
No Data
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-select-suffix"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-status-warning ant-select-multiple ant-select-allow-clear"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection-overflow"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix"
|
||||
style="opacity:1"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection-search"
|
||||
style="width:0"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
style="opacity:0"
|
||||
type="search"
|
||||
unselectable="on"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-selection-search-mirror"
|
||||
>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
>
|
||||
Warning multiple
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-dropdown ant-cascader-dropdown ant-select-dropdown-empty"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-cascader-menus ant-cascader-menu-empty"
|
||||
>
|
||||
<ul
|
||||
class="ant-cascader-menu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
aria-checked="false"
|
||||
class="ant-cascader-menu-item ant-cascader-menu-item-disabled"
|
||||
data-path-key="__EMPTY__"
|
||||
role="menuitemcheckbox"
|
||||
>
|
||||
<div
|
||||
class="ant-cascader-menu-item-content"
|
||||
>
|
||||
<div
|
||||
class="ant-empty ant-empty-normal ant-empty-small"
|
||||
>
|
||||
<div
|
||||
class="ant-empty-image"
|
||||
>
|
||||
<svg
|
||||
class="ant-empty-img-simple"
|
||||
height="41"
|
||||
viewBox="0 0 64 41"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
transform="translate(0 1)"
|
||||
>
|
||||
<ellipse
|
||||
class="ant-empty-img-simple-ellipse"
|
||||
cx="32"
|
||||
cy="33"
|
||||
rx="32"
|
||||
ry="7"
|
||||
/>
|
||||
<g
|
||||
class="ant-empty-img-simple-g"
|
||||
fill-rule="nonzero"
|
||||
>
|
||||
<path
|
||||
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
|
||||
/>
|
||||
<path
|
||||
class="ant-empty-img-simple-path"
|
||||
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="ant-empty-description"
|
||||
>
|
||||
No Data
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/suffix.md extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
|
@ -658,6 +658,151 @@ exports[`renders ./components/cascader/demo/multiple.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/placement.md correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-radio-group ant-radio-group-outline"
|
||||
>
|
||||
<label
|
||||
class="ant-radio-button-wrapper ant-radio-button-wrapper-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button ant-radio-button-checked"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="topLeft"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
topLeft
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="topRight"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
topRight
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="bottomLeft"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
bottomLeft
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="bottomRight"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
bottomRight
|
||||
</span>
|
||||
</label>
|
||||
</div>,
|
||||
<br />,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
style="opacity:0"
|
||||
type="search"
|
||||
unselectable="on"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
>
|
||||
Please select
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-select-suffix"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/search.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-show-search"
|
||||
@ -891,6 +1036,126 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/status.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-status-error ant-select-single ant-select-allow-clear ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
style="opacity:0"
|
||||
type="search"
|
||||
unselectable="on"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
>
|
||||
Error
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-select-suffix"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-status-warning ant-select-multiple ant-select-allow-clear"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection-overflow"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix"
|
||||
style="opacity:1"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection-search"
|
||||
style="width:0"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
style="opacity:0"
|
||||
type="search"
|
||||
unselectable="on"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-selection-search-mirror"
|
||||
>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
>
|
||||
Warning multiple
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/suffix.md correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
|
@ -368,6 +368,23 @@ describe('Cascader', () => {
|
||||
expect(wrapper.find('.ant-select-selection-placeholder').text()).toEqual(customPlaceholder);
|
||||
});
|
||||
|
||||
it('placement work correctly', () => {
|
||||
const customerOptions = [
|
||||
{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [
|
||||
{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const wrapper = mount(<Cascader options={customerOptions} placement="topRight" />);
|
||||
expect(wrapper.find('Trigger').prop('popupPlacement')).toEqual('topRight');
|
||||
});
|
||||
|
||||
it('popup correctly with defaultValue RTL', () => {
|
||||
const wrapper = mount(
|
||||
<ConfigProvider direction="rtl">
|
||||
@ -484,10 +501,12 @@ describe('Cascader', () => {
|
||||
describe('legacy props', () => {
|
||||
it('popupClassName', () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const wrapper = mount(<Cascader open popupPlacement="topRight" popupClassName="mock-cls" />);
|
||||
const wrapper = mount(
|
||||
<Cascader open popupPlacement="bottomLeft" popupClassName="mock-cls" />,
|
||||
);
|
||||
|
||||
expect(wrapper.exists('.mock-cls')).toBeTruthy();
|
||||
expect(wrapper.find('Trigger').prop('popupPlacement')).toEqual('topRight');
|
||||
expect(wrapper.find('Trigger').prop('popupPlacement')).toEqual('bottomLeft');
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Cascader] `popupClassName` is deprecated. Please use `dropdownClassName` instead.',
|
||||
|
@ -74,4 +74,18 @@ describe('Cascader.typescript', () => {
|
||||
);
|
||||
expect(wrapper).toBeTruthy();
|
||||
});
|
||||
|
||||
it('single onChange', () => {
|
||||
const wrapper = mount(
|
||||
<Cascader multiple={false} onChange={(values: (string | number)[]) => values} />,
|
||||
);
|
||||
expect(wrapper).toBeTruthy();
|
||||
});
|
||||
|
||||
it('multiple onChange', () => {
|
||||
const wrapper = mount(
|
||||
<Cascader multiple onChange={(values: (string | number)[][]) => values} />,
|
||||
);
|
||||
expect(wrapper).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
77
components/cascader/demo/placement.md
Normal file
77
components/cascader/demo/placement.md
Normal file
@ -0,0 +1,77 @@
|
||||
---
|
||||
order: 13
|
||||
title:
|
||||
zh-CN: 弹出位置
|
||||
en-US: Placement
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
可以通过 `placement` 手动指定弹出的位置。
|
||||
|
||||
## en-US
|
||||
|
||||
You can manually specify the position of the popup via `placement`.
|
||||
|
||||
```jsx
|
||||
import { Cascader, Radio } from 'antd';
|
||||
|
||||
const options = [
|
||||
{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [
|
||||
{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [
|
||||
{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [
|
||||
{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [
|
||||
{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const SetPlacementDemo = () => {
|
||||
const [placement, SetPlacement] = React.useState('topLeft');
|
||||
|
||||
const placementChange = e => {
|
||||
SetPlacement(e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Radio.Group value={placement} onChange={placementChange}>
|
||||
<Radio.Button value="topLeft">topLeft</Radio.Button>
|
||||
<Radio.Button value="topRight">topRight</Radio.Button>
|
||||
<Radio.Button value="bottomLeft">bottomLeft</Radio.Button>
|
||||
<Radio.Button value="bottomRight">bottomRight</Radio.Button>
|
||||
</Radio.Group>
|
||||
<br />
|
||||
<br />
|
||||
<Cascader options={options} placeholder="Please select" placement={placement} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<SetPlacementDemo />, mountNode);
|
||||
```
|
28
components/cascader/demo/status.md
Normal file
28
components/cascader/demo/status.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
order: 16
|
||||
version: 4.19.0
|
||||
title:
|
||||
zh-CN: 自定义状态
|
||||
en-US: Status
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `status` 为 Cascader 添加状态,可选 `error` 或者 `warning`。
|
||||
|
||||
## en-US
|
||||
|
||||
Add status to Cascader with `status`, which could be `error` or `warning`.
|
||||
|
||||
```tsx
|
||||
import { Cascader, Space } from 'antd';
|
||||
|
||||
const Validation: React.FC = () => (
|
||||
<Space direction="vertical">
|
||||
<Cascader status="error" placeholder="Error" />
|
||||
<Cascader status="warning" multiple placeholder="Warning multiple" />
|
||||
</Space>
|
||||
);
|
||||
|
||||
ReactDOM.render(<Validation />, mountNode);
|
||||
```
|
@ -24,11 +24,12 @@ Cascade selection box.
|
||||
| allowClear | Whether allow clear | boolean | true | |
|
||||
| autoFocus | If get focus when component mounted | boolean | false | |
|
||||
| bordered | Whether has border style | boolean | true | |
|
||||
| clearIcon | The custom clear icon | ReactNode | - | |
|
||||
| changeOnSelect | (Work on single select) Change value on each selection if set to true, see above demo for details | boolean | false | |
|
||||
| className | The additional css class | string | - | |
|
||||
| defaultValue | Initial selected value | string\[] \| number\[] | \[] | |
|
||||
| disabled | Whether disabled select | boolean | false | |
|
||||
| displayRender | The render function of displaying single selected options. You can use tagRender for multiple mode | (label, selectedOptions) => ReactNode | label => label.join(`/`) | |
|
||||
| displayRender | The render function of displaying selected options | (label, selectedOptions) => ReactNode | label => label.join(`/`) | `multiple`: 4.18.0 |
|
||||
| dropdownClassName | The additional className of popup overlay | string | - | 4.17.0 |
|
||||
| dropdownRender | Customize dropdown content | (menus: ReactNode) => ReactNode | - | 4.4.0 |
|
||||
| expandIcon | Customize the current item expand icon | ReactNode | - | 4.4.0 |
|
||||
@ -42,9 +43,10 @@ Cascade selection box.
|
||||
| open | Set visible of cascader popup | boolean | - | 4.17.0 |
|
||||
| options | The data options of cascade | [Option](#Option)\[] | - | |
|
||||
| placeholder | The input placeholder | string | `Please select` | |
|
||||
| placement | Use preset popup align config from builtinPlacements:`bottomLeft` `bottomRight` `topLeft` `topRight` | string | `bottomLeft` | 4.17.0 |
|
||||
| placement | Use preset popup align config from builtinPlacements | `bottomLeft` `bottomRight` `topLeft` `topRight` | `bottomLeft` | 4.17.0 |
|
||||
| showSearch | Whether show search input in single mode | boolean \| [Object](#showSearch) | false | |
|
||||
| size | The input size | `large` \| `middle` \| `small` | - | |
|
||||
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| style | The additional style | CSSProperties | - | |
|
||||
| suffixIcon | The custom suffix icon | ReactNode | - | |
|
||||
| tagRender | Customize tag render when `multiple` | (props) => ReactNode | - | 4.17.0 |
|
||||
@ -52,6 +54,7 @@ Cascade selection box.
|
||||
| onChange | Callback when finishing cascader select | (value, selectedOptions) => void | - | |
|
||||
| onDropdownVisibleChange | Callback when popup shown or hidden | (value) => void | - | 4.17.0 |
|
||||
| multiple | Support multiple or not | boolean | - | 4.17.0 |
|
||||
| removeIcon | The custom remove icon | ReactNode | - | |
|
||||
| searchValue | Set search value,Need work with `showSearch` | string | - | 4.17.0 |
|
||||
| onSearch | The callback function triggered when input changed | (search: string) => void | - | 4.17.0 |
|
||||
| dropdownMenuColumnStyle | The style of the drop-down menu column | CSSProperties | - | |
|
||||
@ -75,6 +78,10 @@ interface Option {
|
||||
label?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
children?: Option[];
|
||||
// Determines if this is a leaf node(effective when `loadData` is specified).
|
||||
// `false` will force trade TreeNode as a parent node.
|
||||
// Show expand icon even if the current node has no children.
|
||||
isLeaf?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -2,7 +2,8 @@ import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import RcCascader from 'rc-cascader';
|
||||
import type {
|
||||
CascaderProps as RcCascaderProps,
|
||||
SingleCascaderProps as RcSingleCascaderProps,
|
||||
MultipleCascaderProps as RcMultipleCascaderProps,
|
||||
ShowSearchType,
|
||||
FieldNames,
|
||||
BaseOptionType,
|
||||
@ -12,12 +13,17 @@ import omit from 'rc-util/lib/omit';
|
||||
import RightOutlined from '@ant-design/icons/RightOutlined';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import LeftOutlined from '@ant-design/icons/LeftOutlined';
|
||||
import { useContext } from 'react';
|
||||
import devWarning from '../_util/devWarning';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
import SizeContext from '../config-provider/SizeContext';
|
||||
import getIcons from '../select/utils/iconUtil';
|
||||
import { getTransitionName } from '../_util/motion';
|
||||
import { getTransitionName, getTransitionDirection, SelectCommonPlacement } from '../_util/motion';
|
||||
import { FormItemStatusContext } from '../form/context';
|
||||
import { getMergedStatus, getStatusClassNames, InputStatus } from '../_util/statusUtils';
|
||||
import useStyle from './style';
|
||||
import useSelectStyle from '../select/style';
|
||||
|
||||
// Align the design since we use `rc-select` in root. This help:
|
||||
// - List search content will show all content
|
||||
@ -80,15 +86,24 @@ const defaultSearchRender: ShowSearchType['render'] = (inputValue, path, prefixC
|
||||
return optionList;
|
||||
};
|
||||
|
||||
export interface CascaderProps<DataNodeType>
|
||||
extends Omit<RcCascaderProps, 'checkable' | 'options'> {
|
||||
type SingleCascaderProps = Omit<RcSingleCascaderProps, 'checkable' | 'options'> & {
|
||||
multiple?: false;
|
||||
};
|
||||
type MultipleCascaderProps = Omit<RcMultipleCascaderProps, 'checkable' | 'options'> & {
|
||||
multiple: true;
|
||||
};
|
||||
|
||||
type UnionCascaderProps = SingleCascaderProps | MultipleCascaderProps;
|
||||
|
||||
export type CascaderProps<DataNodeType> = UnionCascaderProps & {
|
||||
multiple?: boolean;
|
||||
size?: SizeType;
|
||||
bordered?: boolean;
|
||||
|
||||
placement?: SelectCommonPlacement;
|
||||
suffixIcon?: React.ReactNode;
|
||||
options?: DataNodeType[];
|
||||
}
|
||||
status?: InputStatus;
|
||||
};
|
||||
|
||||
export interface CascaderRef {
|
||||
focus: () => void;
|
||||
@ -107,11 +122,14 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
popupClassName,
|
||||
dropdownClassName,
|
||||
expandIcon,
|
||||
placement,
|
||||
showSearch,
|
||||
allowClear = true,
|
||||
notFoundContent,
|
||||
direction,
|
||||
getPopupContainer,
|
||||
status: customStatus,
|
||||
showArrow,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
@ -124,11 +142,16 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
direction: rootDirection,
|
||||
// virtual,
|
||||
// dropdownMatchSelectWidth,
|
||||
} = React.useContext(ConfigContext);
|
||||
iconPrefixCls,
|
||||
} = useContext(ConfigContext);
|
||||
|
||||
const mergedDirection = direction || rootDirection;
|
||||
const isRtl = mergedDirection === 'rtl';
|
||||
|
||||
// =================== Status =====================
|
||||
const { status: contextStatus, hasFeedback } = useContext(FormItemStatusContext);
|
||||
const mergedStatus = getMergedStatus(contextStatus, customStatus);
|
||||
|
||||
// =================== Warning =====================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
devWarning(
|
||||
@ -152,6 +175,9 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
const prefixCls = getPrefixCls('select', customizePrefixCls);
|
||||
const cascaderPrefixCls = getPrefixCls('cascader', customizePrefixCls);
|
||||
|
||||
const [wrapSelectSSR, hashId] = useSelectStyle(rootPrefixCls, prefixCls, iconPrefixCls);
|
||||
const [wrapCascaderSSR] = useStyle(cascaderPrefixCls);
|
||||
|
||||
// =================== Dropdown ====================
|
||||
const mergedDropdownClassName = classNames(
|
||||
dropdownClassName || popupClassName,
|
||||
@ -159,6 +185,7 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
{
|
||||
[`${cascaderPrefixCls}-dropdown-rtl`]: mergedDirection === 'rtl',
|
||||
},
|
||||
hashId,
|
||||
);
|
||||
|
||||
// ==================== Search =====================
|
||||
@ -204,14 +231,28 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
);
|
||||
|
||||
// ===================== Icons =====================
|
||||
const mergedShowArrow = showArrow !== undefined ? showArrow : props.loading || !multiple;
|
||||
const { suffixIcon, removeIcon, clearIcon } = getIcons({
|
||||
...props,
|
||||
status: mergedStatus,
|
||||
hasFeedback,
|
||||
showArrow: mergedShowArrow,
|
||||
multiple,
|
||||
prefixCls,
|
||||
});
|
||||
|
||||
// ===================== Placement =====================
|
||||
const getPlacement = () => {
|
||||
if (placement !== undefined) {
|
||||
return placement;
|
||||
}
|
||||
return direction === 'rtl'
|
||||
? ('bottomRight' as SelectCommonPlacement)
|
||||
: ('bottomLeft' as SelectCommonPlacement);
|
||||
};
|
||||
|
||||
// ==================== Render =====================
|
||||
return (
|
||||
const renderNode = (
|
||||
<RcCascader
|
||||
prefixCls={prefixCls}
|
||||
className={classNames(
|
||||
@ -222,10 +263,13 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
[`${prefixCls}-rtl`]: isRtl,
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
getStatusClassNames(prefixCls, mergedStatus, hasFeedback),
|
||||
className,
|
||||
hashId,
|
||||
)}
|
||||
{...(restProps as any)}
|
||||
direction={mergedDirection}
|
||||
placement={getPlacement()}
|
||||
notFoundContent={mergedNotFoundContent}
|
||||
allowClear={allowClear}
|
||||
showSearch={mergedShowSearch}
|
||||
@ -238,11 +282,18 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
dropdownClassName={mergedDropdownClassName}
|
||||
dropdownPrefixCls={customizePrefixCls || cascaderPrefixCls}
|
||||
choiceTransitionName={getTransitionName(rootPrefixCls, '', choiceTransitionName)}
|
||||
transitionName={getTransitionName(rootPrefixCls, 'slide-up', transitionName)}
|
||||
transitionName={getTransitionName(
|
||||
rootPrefixCls,
|
||||
getTransitionDirection(placement),
|
||||
transitionName,
|
||||
)}
|
||||
getPopupContainer={getPopupContainer || getContextPopupContainer}
|
||||
ref={ref}
|
||||
showArrow={hasFeedback || showArrow}
|
||||
/>
|
||||
);
|
||||
|
||||
return wrapCascaderSSR(wrapSelectSSR(renderNode));
|
||||
}) as (<OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType>(
|
||||
props: React.PropsWithChildren<CascaderProps<OptionType>> & { ref?: React.Ref<CascaderRef> },
|
||||
) => React.ReactElement) & {
|
||||
|
@ -25,11 +25,12 @@ cover: https://gw.alipayobjects.com/zos/alicdn/UdS8y8xyZ/Cascader.svg
|
||||
| allowClear | 是否支持清除 | boolean | true | |
|
||||
| autoFocus | 自动获取焦点 | boolean | false | |
|
||||
| bordered | 是否有边框 | boolean | true | |
|
||||
| clearIcon | 自定义的选择框清空图标 | ReactNode | - | |
|
||||
| changeOnSelect | (单选时生效)当此项为 true 时,点选每级菜单选项值都会发生变化,具体见上面的演示 | boolean | false | |
|
||||
| className | 自定义类名 | string | - | |
|
||||
| defaultValue | 默认的选中项 | string\[] \| number\[] | \[] | |
|
||||
| disabled | 禁用 | boolean | false | |
|
||||
| displayRender | 单选模式下选择后展示的渲染函数,多选请使用 tagRender | (label, selectedOptions) => ReactNode | label => label.join(`/`) | |
|
||||
| displayRender | 选择后展示的渲染函数 | (label, selectedOptions) => ReactNode | label => label.join(`/`) | `multiple`: 4.18.0 |
|
||||
| dropdownClassName | 自定义浮层类名 | string | - | 4.17.0 |
|
||||
| dropdownRender | 自定义下拉框内容 | (menus: ReactNode) => ReactNode | - | 4.4.0 |
|
||||
| expandIcon | 自定义次级菜单展开图标 | ReactNode | - | 4.4.0 |
|
||||
@ -43,9 +44,10 @@ cover: https://gw.alipayobjects.com/zos/alicdn/UdS8y8xyZ/Cascader.svg
|
||||
| open | 控制浮层显隐 | boolean | - | 4.17.0 |
|
||||
| options | 可选项数据源 | [Option](#Option)\[] | - | |
|
||||
| placeholder | 输入框占位文本 | string | `请选择` | |
|
||||
| placement | 浮层预设位置:`bottomLeft` `bottomRight` `topLeft` `topRight` | string | `bottomLeft` | 4.17.0 |
|
||||
| placement | 浮层预设位置 | `bottomLeft` `bottomRight` `topLeft` `topRight` | `bottomLeft` | 4.17.0 |
|
||||
| showSearch | 在选择框中显示搜索框 | boolean \| [Object](#showSearch) | false | |
|
||||
| size | 输入框大小 | `large` \| `middle` \| `small` | - | |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| style | 自定义样式 | CSSProperties | - | |
|
||||
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
|
||||
| tagRender | 自定义 tag 内容,多选时生效 | (props) => ReactNode | - | 4.17.0 |
|
||||
@ -53,6 +55,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/UdS8y8xyZ/Cascader.svg
|
||||
| onChange | 选择完成后的回调 | (value, selectedOptions) => void | - | |
|
||||
| onDropdownVisibleChange | 显示/隐藏浮层的回调 | (value) => void | - | 4.17.0 |
|
||||
| multiple | 支持多选节点 | boolean | - | 4.17.0 |
|
||||
| removeIcon | 自定义的多选框清除图标 | ReactNode | - | |
|
||||
| searchValue | 设置搜索的值,需要与 `showSearch` 配合使用 | string | - | 4.17.0 |
|
||||
| onSearch | 监听搜索,返回输入的值 | (search: string) => void | - | 4.17.0 |
|
||||
| dropdownMenuColumnStyle | 下拉菜单列的样式 | CSSProperties | - | |
|
||||
@ -77,6 +80,9 @@ interface Option {
|
||||
label?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
children?: Option[];
|
||||
// 标记是否为叶子节点,设置了 `loadData` 时有效
|
||||
// 设为 `false` 时会强制标记为父节点,即使当前节点没有 children,也会显示展开图标
|
||||
isLeaf?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1,104 +1,104 @@
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
@import '../../input/style/mixin';
|
||||
@import '../../checkbox/style/mixin';
|
||||
// @import '../../style/themes/index';
|
||||
// @import '../../style/mixins/index';
|
||||
// @import '../../input/style/mixin';
|
||||
// @import '../../checkbox/style/mixin';
|
||||
|
||||
@cascader-prefix-cls: ~'@{ant-prefix}-cascader';
|
||||
// @cascader-prefix-cls: ~'@{ant-prefix}-cascader';
|
||||
|
||||
.antCheckboxFn(@checkbox-prefix-cls: ~'@{cascader-prefix-cls}-checkbox');
|
||||
// .antCheckboxFn(@checkbox-prefix-cls: ~'@{cascader-prefix-cls}-checkbox');
|
||||
|
||||
.@{cascader-prefix-cls} {
|
||||
width: 184px;
|
||||
// .@{cascader-prefix-cls} {
|
||||
// width: 184px;
|
||||
|
||||
&-checkbox {
|
||||
top: 0;
|
||||
margin-right: @padding-xs;
|
||||
}
|
||||
// &-checkbox {
|
||||
// top: 0;
|
||||
// margin-right: @padding-xs;
|
||||
// }
|
||||
|
||||
&-menus {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: flex-start;
|
||||
// &-menus {
|
||||
// display: flex;
|
||||
// flex-wrap: nowrap;
|
||||
// align-items: flex-start;
|
||||
|
||||
&.@{cascader-prefix-cls}-menu-empty {
|
||||
.@{cascader-prefix-cls}-menu {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
// &.@{cascader-prefix-cls}-menu-empty {
|
||||
// .@{cascader-prefix-cls}-menu {
|
||||
// width: 100%;
|
||||
// height: auto;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
&-menu {
|
||||
min-width: 111px;
|
||||
height: 180px;
|
||||
margin: 0;
|
||||
margin: -@dropdown-edge-child-vertical-padding 0;
|
||||
padding: @cascader-dropdown-edge-child-vertical-padding 0;
|
||||
overflow: auto;
|
||||
vertical-align: top;
|
||||
list-style: none;
|
||||
border-right: @border-width-base @border-style-base @cascader-menu-border-color-split;
|
||||
-ms-overflow-style: -ms-autohiding-scrollbar; // https://github.com/ant-design/ant-design/issues/11857
|
||||
// &-menu {
|
||||
// min-width: 111px;
|
||||
// height: 180px;
|
||||
// margin: 0;
|
||||
// margin: -@dropdown-edge-child-vertical-padding 0;
|
||||
// padding: @cascader-dropdown-edge-child-vertical-padding 0;
|
||||
// overflow: auto;
|
||||
// vertical-align: top;
|
||||
// list-style: none;
|
||||
// border-right: @border-width-base @border-style-base @cascader-menu-border-color-split;
|
||||
// -ms-overflow-style: -ms-autohiding-scrollbar; // https://github.com/ant-design/ant-design/issues/11857
|
||||
|
||||
&-item {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
padding: @cascader-dropdown-vertical-padding @control-padding-horizontal;
|
||||
overflow: hidden;
|
||||
line-height: @cascader-dropdown-line-height;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
// &-item {
|
||||
// display: flex;
|
||||
// flex-wrap: nowrap;
|
||||
// align-items: center;
|
||||
// padding: @cascader-dropdown-vertical-padding @control-padding-horizontal;
|
||||
// overflow: hidden;
|
||||
// line-height: @cascader-dropdown-line-height;
|
||||
// white-space: nowrap;
|
||||
// text-overflow: ellipsis;
|
||||
// cursor: pointer;
|
||||
// transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: @item-hover-bg;
|
||||
}
|
||||
// &:hover {
|
||||
// background: @item-hover-bg;
|
||||
// }
|
||||
|
||||
&-disabled {
|
||||
color: @disabled-color;
|
||||
cursor: not-allowed;
|
||||
// &-disabled {
|
||||
// color: @disabled-color;
|
||||
// cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
// &:hover {
|
||||
// background: transparent;
|
||||
// }
|
||||
// }
|
||||
|
||||
.@{cascader-prefix-cls}-menu-empty & {
|
||||
color: @disabled-color;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
// .@{cascader-prefix-cls}-menu-empty & {
|
||||
// color: @disabled-color;
|
||||
// cursor: default;
|
||||
// pointer-events: none;
|
||||
// }
|
||||
|
||||
&-active:not(&-disabled) {
|
||||
&,
|
||||
&:hover {
|
||||
font-weight: @select-item-selected-font-weight;
|
||||
background-color: @cascader-item-selected-bg;
|
||||
}
|
||||
}
|
||||
// &-active:not(&-disabled) {
|
||||
// &,
|
||||
// &:hover {
|
||||
// font-weight: @select-item-selected-font-weight;
|
||||
// background-color: @cascader-item-selected-bg;
|
||||
// }
|
||||
// }
|
||||
|
||||
&-content {
|
||||
flex: auto;
|
||||
}
|
||||
// &-content {
|
||||
// flex: auto;
|
||||
// }
|
||||
|
||||
&-expand &-expand-icon,
|
||||
&-loading-icon {
|
||||
margin-left: @padding-xss;
|
||||
color: @text-color-secondary;
|
||||
font-size: 10px;
|
||||
// &-expand &-expand-icon,
|
||||
// &-loading-icon {
|
||||
// margin-left: @padding-xss;
|
||||
// color: @text-color-secondary;
|
||||
// font-size: 10px;
|
||||
|
||||
.@{cascader-prefix-cls}-menu-item-disabled& {
|
||||
color: @disabled-color;
|
||||
}
|
||||
}
|
||||
// .@{cascader-prefix-cls}-menu-item-disabled& {
|
||||
// color: @disabled-color;
|
||||
// }
|
||||
// }
|
||||
|
||||
&-keyword {
|
||||
color: @highlight-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// &-keyword {
|
||||
// color: @highlight-color;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@import './rtl';
|
||||
// @import './rtl';
|
||||
|
@ -1,6 +1,177 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
// import '../../style/index.less';
|
||||
// import './index.less';
|
||||
|
||||
// style dependencies
|
||||
import '../../empty/style';
|
||||
import '../../select/style';
|
||||
// // style dependencies
|
||||
// import '../../empty/style';
|
||||
// import '../../select/style';
|
||||
|
||||
// // deps-lint-skip: form
|
||||
|
||||
// deps-lint-skip-all
|
||||
import {
|
||||
DerivativeToken,
|
||||
useStyleRegister,
|
||||
useToken,
|
||||
UseComponentStyleResult,
|
||||
GenerateStyle,
|
||||
} from '../../_util/theme';
|
||||
import { getStyle as getCheckboxStyle } from '../../checkbox/style';
|
||||
|
||||
interface CascaderToken extends DerivativeToken {
|
||||
prefixCls: string;
|
||||
cascaderCls: string;
|
||||
}
|
||||
|
||||
// =============================== Base ===============================
|
||||
const genBaseStyle: GenerateStyle<CascaderToken> = (token, hashId) => {
|
||||
const { prefixCls, cascaderCls } = token;
|
||||
const cascaderMenuItemCls = `${cascaderCls}-menu-item`;
|
||||
const iconCls = `
|
||||
${cascaderMenuItemCls}-expand ${cascaderMenuItemCls}-expand-icon,
|
||||
${cascaderMenuItemCls}-loading-icon
|
||||
`;
|
||||
|
||||
const itemPaddingVertical = Math.round(
|
||||
(token.controlHeight - token.fontSize * token.lineHeight) / 2,
|
||||
);
|
||||
|
||||
return [
|
||||
// =====================================================
|
||||
// == Control ==
|
||||
// =====================================================
|
||||
{
|
||||
[cascaderCls]: {
|
||||
width: 184, // FIXME: hardcode in v4
|
||||
},
|
||||
},
|
||||
|
||||
// =====================================================
|
||||
// == Popup ==
|
||||
// =====================================================
|
||||
{
|
||||
[`${cascaderCls}-dropdown`]: [
|
||||
// ==================== Checkbox ====================
|
||||
getCheckboxStyle(`${prefixCls}-checkbox`, token, hashId!),
|
||||
{
|
||||
[cascaderCls]: {
|
||||
// ================== Checkbox ==================
|
||||
'&-checkbox': {
|
||||
top: 0,
|
||||
marginInlineEnd: token.paddingXS,
|
||||
},
|
||||
|
||||
// ==================== Menu ====================
|
||||
// >>> Menus
|
||||
'&-menus': {
|
||||
display: 'flex',
|
||||
flexWrap: 'nowrap',
|
||||
alignItems: 'flex-start',
|
||||
|
||||
[`&${cascaderCls}-menu-empty`]: {
|
||||
[`${cascaderCls}-menu`]: {
|
||||
width: '100%',
|
||||
height: 'auto',
|
||||
|
||||
[cascaderMenuItemCls]: {
|
||||
color: token.textColorDisabled,
|
||||
cursor: 'default',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// >>> Menu
|
||||
'&-menu': {
|
||||
minWidth: 111, // FIXME: hardcode in v4
|
||||
height: 180, // FIXME: hardcode in v4
|
||||
margin: `-${token.paddingXS}px 0`,
|
||||
padding: `${token.paddingXS}px 0`,
|
||||
overflow: 'auto',
|
||||
verticalAlign: 'top',
|
||||
listStyle: 'none',
|
||||
borderInlineEnd: `${token.borderWidth}px ${token.borderStyle} ${token.borderColorSplit}`,
|
||||
'-ms-overflow-style': '-ms-autohiding-scrollbar', // https://github.com/ant-design/ant-design/issues/11857
|
||||
|
||||
'&-item': {
|
||||
display: 'flex',
|
||||
flexWrap: 'nowrap',
|
||||
alignItems: 'center',
|
||||
padding: `${itemPaddingVertical}px ${token.paddingSM}px`,
|
||||
overflow: 'hidden',
|
||||
lineHeight: token.lineHeight,
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
cursor: 'pointer',
|
||||
transition: `all ${token.duration}`,
|
||||
|
||||
'&:hover': {
|
||||
background: token.itemHoverBackground,
|
||||
},
|
||||
' &-disabled': {
|
||||
color: token.textColorDisabled,
|
||||
cursor: 'not-allowed',
|
||||
|
||||
'&:hover': {
|
||||
background: 'transparent',
|
||||
},
|
||||
|
||||
[iconCls]: {
|
||||
color: token.textColorDisabled,
|
||||
},
|
||||
},
|
||||
|
||||
[`&-active:not(${cascaderMenuItemCls}-disabled)`]: {
|
||||
[`&, &:hover`]: {
|
||||
fontWeight: 600, // FIXME: hardcode
|
||||
backgroundColor: token.itemActiveBackground,
|
||||
},
|
||||
},
|
||||
|
||||
'&-content': {
|
||||
flex: 'auto',
|
||||
},
|
||||
|
||||
[iconCls]: {
|
||||
marginInlineStart: token.paddingXXS,
|
||||
color: token.textColorSecondary,
|
||||
fontSize: 10, // FIXME: hardcode in v4
|
||||
},
|
||||
|
||||
'&-keyword': {
|
||||
color: token.highlightColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// =====================================================
|
||||
// == RTL ==
|
||||
// =====================================================
|
||||
{
|
||||
[`${cascaderCls}-dropdown-rtl`]: {
|
||||
direction: 'rtl',
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default function useStyle(prefixCls: string): UseComponentStyleResult {
|
||||
const [theme, token, hashId] = useToken();
|
||||
|
||||
const cascaderToken: CascaderToken = {
|
||||
...token,
|
||||
prefixCls,
|
||||
cascaderCls: `.${prefixCls}`,
|
||||
};
|
||||
|
||||
return [
|
||||
useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => [
|
||||
genBaseStyle(cascaderToken, hashId),
|
||||
]),
|
||||
hashId,
|
||||
];
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import RcCheckbox from 'rc-checkbox';
|
||||
import { GroupContext } from './Group';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import devWarning from '../_util/devWarning';
|
||||
import useStyle from './style';
|
||||
|
||||
export interface AbstractCheckboxProps<T> {
|
||||
prefixCls?: string;
|
||||
@ -84,6 +85,8 @@ const InternalCheckbox: React.ForwardRefRenderFunction<HTMLInputElement, Checkbo
|
||||
}, [restProps.value]);
|
||||
|
||||
const prefixCls = getPrefixCls('checkbox', customizePrefixCls);
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
|
||||
const checkboxProps: CheckboxProps = { ...restProps };
|
||||
if (checkboxGroup && !skipGroup) {
|
||||
checkboxProps.onChange = (...args) => {
|
||||
@ -106,11 +109,15 @@ const InternalCheckbox: React.ForwardRefRenderFunction<HTMLInputElement, Checkbo
|
||||
[`${prefixCls}-wrapper-disabled`]: checkboxProps.disabled,
|
||||
},
|
||||
className,
|
||||
hashId,
|
||||
);
|
||||
const checkboxClass = classNames({
|
||||
[`${prefixCls}-indeterminate`]: indeterminate,
|
||||
});
|
||||
return (
|
||||
const checkboxClass = classNames(
|
||||
{
|
||||
[`${prefixCls}-indeterminate`]: indeterminate,
|
||||
},
|
||||
hashId,
|
||||
);
|
||||
return wrapSSR(
|
||||
// eslint-disable-next-line jsx-a11y/label-has-associated-control
|
||||
<label
|
||||
className={classString}
|
||||
@ -120,7 +127,7 @@ const InternalCheckbox: React.ForwardRefRenderFunction<HTMLInputElement, Checkbo
|
||||
>
|
||||
<RcCheckbox {...checkboxProps} prefixCls={prefixCls} className={checkboxClass} ref={ref} />
|
||||
{children !== undefined && <span>{children}</span>}
|
||||
</label>
|
||||
</label>,
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@ import classNames from 'classnames';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import Checkbox, { CheckboxChangeEvent } from './Checkbox';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import useStyle from './style';
|
||||
|
||||
export type CheckboxValueType = string | number | boolean;
|
||||
|
||||
@ -112,6 +113,8 @@ const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, Chec
|
||||
const prefixCls = getPrefixCls('checkbox', customizePrefixCls);
|
||||
const groupPrefixCls = `${prefixCls}-group`;
|
||||
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
|
||||
const domProps = omit(restProps, ['value', 'disabled']);
|
||||
|
||||
if (options && options.length > 0) {
|
||||
@ -147,11 +150,12 @@ const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, Chec
|
||||
[`${groupPrefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
hashId,
|
||||
);
|
||||
return (
|
||||
return wrapSSR(
|
||||
<div className={classString} style={style} {...domProps} ref={ref}>
|
||||
<GroupContext.Provider value={context}>{children}</GroupContext.Provider>
|
||||
</div>
|
||||
</div>,
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
@import '../../style/themes/index';
|
||||
@import './mixin';
|
||||
// @import '../../style/themes/index';
|
||||
// @import './mixin';
|
||||
|
||||
.antCheckboxFn();
|
||||
// .antCheckboxFn();
|
||||
|
||||
@import './rtl';
|
||||
// @import './rtl';
|
||||
|
@ -1,2 +1,256 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
// deps-lint-skip-all
|
||||
import { Keyframes } from '@ant-design/cssinjs';
|
||||
import {
|
||||
DerivativeToken,
|
||||
useStyleRegister,
|
||||
useToken,
|
||||
resetComponent,
|
||||
UseComponentStyleResult,
|
||||
GenerateStyle,
|
||||
} from '../../_util/theme';
|
||||
|
||||
interface CheckboxToken extends DerivativeToken {
|
||||
checkboxCls: string;
|
||||
}
|
||||
|
||||
// ============================== Motion ==============================
|
||||
const antCheckboxEffect = new Keyframes('antCheckboxEffect', {
|
||||
'0%': {
|
||||
transform: 'scale(1)',
|
||||
opacity: 0.5,
|
||||
},
|
||||
|
||||
'100%': {
|
||||
transform: 'scale(1.6)',
|
||||
opacity: 0,
|
||||
},
|
||||
});
|
||||
|
||||
// ============================== Styles ==============================
|
||||
export const genCheckboxStyle: GenerateStyle<CheckboxToken> = (token, hashId) => {
|
||||
const { checkboxCls } = token;
|
||||
const wrapperCls = `${checkboxCls}-wrapper`;
|
||||
|
||||
return [
|
||||
// ===================== Basic =====================
|
||||
{
|
||||
// Group
|
||||
[`${checkboxCls}-group`]: {
|
||||
...resetComponent(token),
|
||||
|
||||
display: 'inline-flex',
|
||||
},
|
||||
|
||||
// Wrapper
|
||||
[wrapperCls]: {
|
||||
...resetComponent(token),
|
||||
|
||||
display: 'inline-flex',
|
||||
alignItems: 'baseline',
|
||||
lineHeight: 'unset',
|
||||
cursor: 'pointer',
|
||||
|
||||
// Fix checkbox & radio in flex align #30260
|
||||
'&:after': {
|
||||
display: 'inline-block',
|
||||
width: 0,
|
||||
overflow: 'hidden',
|
||||
content: "'\\a0'",
|
||||
},
|
||||
|
||||
// Checkbox near checkbox
|
||||
'& + &': {
|
||||
marginInlineStart: token.marginXS,
|
||||
},
|
||||
},
|
||||
|
||||
// Wrapper > Checkbox
|
||||
[checkboxCls]: {
|
||||
...resetComponent(token),
|
||||
|
||||
top: '0.2em',
|
||||
position: 'relative',
|
||||
whiteSpace: 'nowrap',
|
||||
lineHeight: 1,
|
||||
cursor: 'pointer',
|
||||
|
||||
// Wrapper > Checkbox > input
|
||||
[`${checkboxCls}-input`]: {
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
zIndex: 1,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
cursor: 'pointer',
|
||||
opacity: 0,
|
||||
},
|
||||
|
||||
// Wrapper > Checkbox > inner
|
||||
[`${checkboxCls}-inner`]: {
|
||||
position: 'relative',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
display: 'block',
|
||||
width: token.fontSizeLG,
|
||||
height: token.fontSizeLG,
|
||||
direction: 'ltr',
|
||||
backgroundColor: token.componentBackground,
|
||||
border: `${token.borderWidth}px ${token.borderStyle} ${token.borderColor}`,
|
||||
borderRadius: token.borderRadius,
|
||||
borderCollapse: 'separate',
|
||||
transition: `all ${token.duration}`,
|
||||
|
||||
'&:after': {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
insetInlineStart: '21.5%',
|
||||
display: 'table',
|
||||
width: (token.fontSizeLG / 14) * 5,
|
||||
height: (token.fontSizeLG / 14) * 8,
|
||||
border: `2px solid ${token.componentBackground}`,
|
||||
borderTop: 0,
|
||||
borderInlineStart: 0,
|
||||
transform: 'rotate(45deg) scale(0) translate(-50%,-50%)',
|
||||
opacity: 0,
|
||||
transition: `all ${token.durationFast} cubic-bezier(.71,-.46,.88,.6), opacity ${token.durationFast}`,
|
||||
content: '""',
|
||||
},
|
||||
},
|
||||
|
||||
// Wrapper > Checkbox + Text
|
||||
'& + span': {
|
||||
paddingInlineStart: token.paddingXS,
|
||||
paddingInlineEnd: token.paddingXS,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ================= Indeterminate =================
|
||||
{
|
||||
[checkboxCls]: {
|
||||
'&-indeterminate': {
|
||||
// Wrapper > Checkbox > inner
|
||||
[`${checkboxCls}-inner`]: {
|
||||
'&:after': {
|
||||
top: '50%',
|
||||
insetInlineStart: '50%',
|
||||
width: token.fontSizeLG / 2,
|
||||
height: token.fontSizeLG / 2,
|
||||
backgroundColor: token.primaryColor,
|
||||
border: 0,
|
||||
transform: 'translate(-50%, -50%) scale(1)',
|
||||
opacity: 1,
|
||||
content: '""',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ===================== Hover =====================
|
||||
{
|
||||
// Wrapper
|
||||
[`${wrapperCls}:hover ${checkboxCls}:after`]: {
|
||||
visibility: 'visible',
|
||||
},
|
||||
|
||||
// Wrapper & Wrapper > Checkbox
|
||||
[`
|
||||
${wrapperCls}:hover:not(${wrapperCls}-disabled),
|
||||
${checkboxCls}:hover:not(${checkboxCls}-disabled),
|
||||
${checkboxCls}-input:focus +
|
||||
`]: {
|
||||
[`${checkboxCls}-inner`]: {
|
||||
borderColor: token.primaryColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ==================== Checked ====================
|
||||
{
|
||||
// Wrapper > Checkbox
|
||||
[`${checkboxCls}-checked`]: {
|
||||
[`${checkboxCls}-inner`]: {
|
||||
backgroundColor: token.primaryColor,
|
||||
borderColor: token.primaryColor,
|
||||
|
||||
'&:after': {
|
||||
opacity: 1,
|
||||
transform: 'rotate(45deg) scale(1) translate(-50%,-50%)',
|
||||
transition: `all ${token.duration} ${token.easeOutBack} 0.1s`,
|
||||
},
|
||||
},
|
||||
|
||||
// Checked Effect
|
||||
'&:after': {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
border: `${token.borderWidth}px ${token.borderStyle} ${token.primaryColor}`,
|
||||
borderRadius: token.borderRadius,
|
||||
visibility: 'hidden',
|
||||
animation: `${antCheckboxEffect.getName(hashId)} ${token.duration} ease-in-out`,
|
||||
animationFillMode: 'backwards',
|
||||
content: '""',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ==================== Disable ====================
|
||||
{
|
||||
// Wrapper
|
||||
[`${wrapperCls}-disabled`]: {
|
||||
cursor: 'not-allowed',
|
||||
},
|
||||
|
||||
// Wrapper > Checkbox
|
||||
[`${checkboxCls}-disabled`]: {
|
||||
// Wrapper > Checkbox > input
|
||||
[`&, ${checkboxCls}-input`]: {
|
||||
cursor: 'not-allowed',
|
||||
},
|
||||
|
||||
// Wrapper > Checkbox > inner
|
||||
[`${checkboxCls}-inner`]: {
|
||||
background: token.background,
|
||||
borderColor: token.borderColor,
|
||||
|
||||
'&:after': {
|
||||
borderColor: token.borderColor,
|
||||
},
|
||||
},
|
||||
|
||||
'&:after': {
|
||||
display: 'none',
|
||||
},
|
||||
|
||||
'& + span': {
|
||||
color: token.textColorDisabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export function getStyle(prefixCls: string, token: DerivativeToken, hashId: string) {
|
||||
const checkboxToken: CheckboxToken = {
|
||||
...token,
|
||||
checkboxCls: `.${prefixCls}`,
|
||||
};
|
||||
|
||||
return [genCheckboxStyle(checkboxToken, hashId), antCheckboxEffect];
|
||||
}
|
||||
|
||||
export default function useStyle(prefixCls: string): UseComponentStyleResult {
|
||||
const [theme, token, hashId] = useToken();
|
||||
|
||||
return [
|
||||
useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () =>
|
||||
getStyle(prefixCls, token, hashId),
|
||||
),
|
||||
hashId,
|
||||
];
|
||||
}
|
||||
|
@ -120,6 +120,11 @@
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
// hide the last border-bottom in borderless mode
|
||||
&-borderless > &-item:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
&-borderless > &-item > &-content {
|
||||
background-color: transparent;
|
||||
border-top: 0;
|
||||
|
@ -13367,7 +13367,7 @@ exports[`ConfigProvider components Form configProvider 1`] = `
|
||||
class="config-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="config-input"
|
||||
class="config-input config-input-status-error"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
@ -13405,7 +13405,7 @@ exports[`ConfigProvider components Form configProvider componentSize large 1`] =
|
||||
class="config-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="config-input config-input-lg"
|
||||
class="config-input config-input-lg config-input-status-error"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
@ -13443,7 +13443,7 @@ exports[`ConfigProvider components Form configProvider componentSize middle 1`]
|
||||
class="config-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="config-input"
|
||||
class="config-input config-input-status-error"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
@ -13481,7 +13481,7 @@ exports[`ConfigProvider components Form configProvider virtual and dropdownMatch
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="ant-input"
|
||||
class="ant-input ant-input-status-error"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
@ -13519,7 +13519,7 @@ exports[`ConfigProvider components Form normal 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="ant-input"
|
||||
class="ant-input ant-input-status-error"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
@ -13557,7 +13557,7 @@ exports[`ConfigProvider components Form prefixCls 1`] = `
|
||||
class="prefix-Form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="prefix-Form"
|
||||
class="prefix-Form prefix-Form-status-error"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
@ -13744,7 +13744,7 @@ exports[`ConfigProvider components Input configProvider componentSize large 1`]
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="config-input-group-wrapper config-input-group-wrapper-lg config-input-search config-input-search-large"
|
||||
class="config-input-group-wrapper config-input-search config-input-search-large config-input-group-wrapper-lg"
|
||||
>
|
||||
<span
|
||||
class="config-input-wrapper config-input-group"
|
||||
@ -13786,7 +13786,7 @@ exports[`ConfigProvider components Input configProvider componentSize large 1`]
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="config-input-affix-wrapper config-input-affix-wrapper-lg config-input-password"
|
||||
class="config-input-affix-wrapper config-input-password config-input-affix-wrapper-lg"
|
||||
>
|
||||
<input
|
||||
action="click"
|
||||
@ -21043,6 +21043,7 @@ exports[`ConfigProvider components Select configProvider 1`] = `
|
||||
>
|
||||
<div
|
||||
class="config-select-item config-select-item-group"
|
||||
title="grp"
|
||||
>
|
||||
grp
|
||||
</div>
|
||||
@ -21170,6 +21171,7 @@ exports[`ConfigProvider components Select configProvider componentSize large 1`]
|
||||
>
|
||||
<div
|
||||
class="config-select-item config-select-item-group"
|
||||
title="grp"
|
||||
>
|
||||
grp
|
||||
</div>
|
||||
@ -21297,6 +21299,7 @@ exports[`ConfigProvider components Select configProvider componentSize middle 1`
|
||||
>
|
||||
<div
|
||||
class="config-select-item config-select-item-group"
|
||||
title="grp"
|
||||
>
|
||||
grp
|
||||
</div>
|
||||
@ -21424,6 +21427,7 @@ exports[`ConfigProvider components Select configProvider virtual and dropdownMat
|
||||
>
|
||||
<div
|
||||
class="ant-select-item ant-select-item-group"
|
||||
title="grp"
|
||||
>
|
||||
grp
|
||||
</div>
|
||||
@ -21551,6 +21555,7 @@ exports[`ConfigProvider components Select normal 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-select-item ant-select-item-group"
|
||||
title="grp"
|
||||
>
|
||||
grp
|
||||
</div>
|
||||
@ -21678,6 +21683,7 @@ exports[`ConfigProvider components Select prefixCls 1`] = `
|
||||
>
|
||||
<div
|
||||
class="prefix-Select-item prefix-Select-item-group"
|
||||
title="grp"
|
||||
>
|
||||
grp
|
||||
</div>
|
||||
@ -21904,14 +21910,14 @@ exports[`ConfigProvider components Skeleton prefixCls 1`] = `
|
||||
|
||||
exports[`ConfigProvider components Slider configProvider 1`] = `
|
||||
<div
|
||||
class="config-slider"
|
||||
class="config-slider config-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="config-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="config-slider-track"
|
||||
style="left:0%;right:auto;width:0%"
|
||||
style="left:0%;width:0%"
|
||||
/>
|
||||
<div
|
||||
class="config-slider-step"
|
||||
@ -21923,7 +21929,7 @@ exports[`ConfigProvider components Slider configProvider 1`] = `
|
||||
aria-valuenow="0"
|
||||
class="config-slider-handle config-tooltip-open"
|
||||
role="slider"
|
||||
style="left:0%;right:auto;transform:translateX(-50%)"
|
||||
style="left:0%;transform:translateX(-50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div>
|
||||
@ -21950,22 +21956,19 @@ exports[`ConfigProvider components Slider configProvider 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="config-slider-mark"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ConfigProvider components Slider configProvider componentSize large 1`] = `
|
||||
<div
|
||||
class="config-slider"
|
||||
class="config-slider config-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="config-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="config-slider-track"
|
||||
style="left:0%;right:auto;width:0%"
|
||||
style="left:0%;width:0%"
|
||||
/>
|
||||
<div
|
||||
class="config-slider-step"
|
||||
@ -21977,7 +21980,7 @@ exports[`ConfigProvider components Slider configProvider componentSize large 1`]
|
||||
aria-valuenow="0"
|
||||
class="config-slider-handle config-tooltip-open"
|
||||
role="slider"
|
||||
style="left:0%;right:auto;transform:translateX(-50%)"
|
||||
style="left:0%;transform:translateX(-50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div>
|
||||
@ -22004,22 +22007,19 @@ exports[`ConfigProvider components Slider configProvider componentSize large 1`]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="config-slider-mark"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ConfigProvider components Slider configProvider componentSize middle 1`] = `
|
||||
<div
|
||||
class="config-slider"
|
||||
class="config-slider config-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="config-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="config-slider-track"
|
||||
style="left:0%;right:auto;width:0%"
|
||||
style="left:0%;width:0%"
|
||||
/>
|
||||
<div
|
||||
class="config-slider-step"
|
||||
@ -22031,7 +22031,7 @@ exports[`ConfigProvider components Slider configProvider componentSize middle 1`
|
||||
aria-valuenow="0"
|
||||
class="config-slider-handle config-tooltip-open"
|
||||
role="slider"
|
||||
style="left:0%;right:auto;transform:translateX(-50%)"
|
||||
style="left:0%;transform:translateX(-50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div>
|
||||
@ -22058,22 +22058,19 @@ exports[`ConfigProvider components Slider configProvider componentSize middle 1`
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="config-slider-mark"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ConfigProvider components Slider configProvider virtual and dropdownMatchSelectWidth 1`] = `
|
||||
<div
|
||||
class="ant-slider"
|
||||
class="ant-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="left:0%;right:auto;width:0%"
|
||||
style="left:0%;width:0%"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
@ -22085,7 +22082,7 @@ exports[`ConfigProvider components Slider configProvider virtual and dropdownMat
|
||||
aria-valuenow="0"
|
||||
class="ant-slider-handle ant-tooltip-open"
|
||||
role="slider"
|
||||
style="left:0%;right:auto;transform:translateX(-50%)"
|
||||
style="left:0%;transform:translateX(-50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div>
|
||||
@ -22112,22 +22109,19 @@ exports[`ConfigProvider components Slider configProvider virtual and dropdownMat
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-slider-mark"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ConfigProvider components Slider normal 1`] = `
|
||||
<div
|
||||
class="ant-slider"
|
||||
class="ant-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="left:0%;right:auto;width:0%"
|
||||
style="left:0%;width:0%"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
@ -22139,7 +22133,7 @@ exports[`ConfigProvider components Slider normal 1`] = `
|
||||
aria-valuenow="0"
|
||||
class="ant-slider-handle ant-tooltip-open"
|
||||
role="slider"
|
||||
style="left:0%;right:auto;transform:translateX(-50%)"
|
||||
style="left:0%;transform:translateX(-50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div>
|
||||
@ -22166,22 +22160,19 @@ exports[`ConfigProvider components Slider normal 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-slider-mark"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ConfigProvider components Slider prefixCls 1`] = `
|
||||
<div
|
||||
class="prefix-Slider"
|
||||
class="prefix-Slider prefix-Slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="prefix-Slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="prefix-Slider-track"
|
||||
style="left:0%;right:auto;width:0%"
|
||||
style="left:0%;width:0%"
|
||||
/>
|
||||
<div
|
||||
class="prefix-Slider-step"
|
||||
@ -22193,7 +22184,7 @@ exports[`ConfigProvider components Slider prefixCls 1`] = `
|
||||
aria-valuenow="0"
|
||||
class="prefix-Slider-handle prefix-Slider-tooltip-open"
|
||||
role="slider"
|
||||
style="left:0%;right:auto;transform:translateX(-50%)"
|
||||
style="left:0%;transform:translateX(-50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div>
|
||||
@ -22220,9 +22211,6 @@ exports[`ConfigProvider components Slider prefixCls 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="prefix-Slider-mark"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
49
components/config-provider/__tests__/cssinjs.test.tsx
Normal file
49
components/config-provider/__tests__/cssinjs.test.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import * as React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import ConfigProvider from '..';
|
||||
import Button from '../../button';
|
||||
|
||||
describe('ConfigProvider.DynamicTheme', () => {
|
||||
beforeEach(() => {
|
||||
Array.from(document.querySelectorAll('style')).forEach(style => {
|
||||
style.parentNode?.removeChild(style);
|
||||
});
|
||||
});
|
||||
|
||||
it('customize primary color', () => {
|
||||
mount(
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
token: {
|
||||
primaryColor: '#f00',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Button />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
const dynamicStyles = Array.from(document.querySelectorAll('style[data-css-hash]'));
|
||||
|
||||
expect(
|
||||
dynamicStyles.some(style => {
|
||||
const { innerHTML } = style;
|
||||
return (
|
||||
innerHTML.includes('.ant-btn-primary') && innerHTML.includes('background-color:#f00')
|
||||
);
|
||||
}),
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('not crash on null token', () => {
|
||||
expect(() => {
|
||||
mount(
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
token: null as any,
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
@ -16,17 +16,30 @@ describe('ConfigProvider.Icon', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('basic', () => {
|
||||
const wrapper = mount(
|
||||
<ConfigProvider iconPrefixCls="bamboo" csp={{ nonce: 'light' }}>
|
||||
<SmileOutlined />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
describe('csp', () => {
|
||||
it('raw', () => {
|
||||
mount(
|
||||
<ConfigProvider csp={{ nonce: 'little' }}>
|
||||
<SmileOutlined />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
const styleNode = document.querySelector('style');
|
||||
const styleNode = document.querySelector('style');
|
||||
expect(styleNode.nonce).toEqual('little');
|
||||
});
|
||||
|
||||
expect(wrapper.exists('.bamboo-smile')).toBeTruthy();
|
||||
expect(styleNode.nonce).toEqual('light');
|
||||
it('mix with iconPrefixCls', () => {
|
||||
const wrapper = mount(
|
||||
<ConfigProvider iconPrefixCls="bamboo" csp={{ nonce: 'light' }}>
|
||||
<SmileOutlined />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
const styleNode = document.querySelector('style');
|
||||
|
||||
expect(wrapper.exists('.bamboo-smile')).toBeTruthy();
|
||||
expect(styleNode.nonce).toEqual('light');
|
||||
});
|
||||
});
|
||||
|
||||
it('nest', () => {
|
||||
|
@ -1,7 +1,17 @@
|
||||
import { kebabCase } from 'lodash';
|
||||
import canUseDom from 'rc-util/lib/Dom/canUseDom';
|
||||
import ConfigProvider from '..';
|
||||
import { resetWarned } from '../../_util/devWarning';
|
||||
|
||||
let mockCanUseDom = true;
|
||||
|
||||
jest.mock('rc-util/lib/Dom/canUseDom', () => () => mockCanUseDom);
|
||||
|
||||
describe('ConfigProvider.Theme', () => {
|
||||
beforeEach(() => {
|
||||
mockCanUseDom = true;
|
||||
});
|
||||
|
||||
const colorList = ['primaryColor', 'successColor', 'warningColor', 'errorColor', 'infoColor'];
|
||||
|
||||
colorList.forEach(colorName => {
|
||||
@ -14,10 +24,29 @@ describe('ConfigProvider.Theme', () => {
|
||||
});
|
||||
|
||||
const styles: any[] = Array.from(document.querySelectorAll('style'));
|
||||
const themeStyle = styles.find(style => style['rc-util-key'].includes('-dynamic-theme'));
|
||||
const themeStyle = styles.find(style =>
|
||||
style.getAttribute('rc-util-key').includes('-dynamic-theme'),
|
||||
);
|
||||
expect(themeStyle).toBeTruthy();
|
||||
|
||||
expect(themeStyle.innerHTML).toContain(`--bamboo-${kebabCase(colorName)}: rgb(0, 0, 255)`);
|
||||
});
|
||||
});
|
||||
|
||||
it('warning for SSR', () => {
|
||||
resetWarned();
|
||||
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
mockCanUseDom = false;
|
||||
expect(canUseDom()).toBeFalsy();
|
||||
|
||||
ConfigProvider.config({
|
||||
theme: {},
|
||||
});
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: ConfigProvider] SSR do not support dynamic theme with css variables.',
|
||||
);
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
@ -4,6 +4,8 @@ import { Locale } from '../locale-provider';
|
||||
import { SizeType } from './SizeContext';
|
||||
import { RequiredMark } from '../form/Form';
|
||||
|
||||
export const defaultIconPrefixCls = 'anticon';
|
||||
|
||||
export interface Theme {
|
||||
primaryColor?: string;
|
||||
infoColor?: string;
|
||||
@ -23,7 +25,7 @@ export interface ConfigConsumerProps {
|
||||
getTargetContainer?: () => HTMLElement;
|
||||
getPopupContainer?: (triggerNode?: HTMLElement) => HTMLElement;
|
||||
rootPrefixCls?: string;
|
||||
iconPrefixCls?: string;
|
||||
iconPrefixCls: string;
|
||||
getPrefixCls: (suffixCls?: string, customizePrefixCls?: string) => string;
|
||||
renderEmpty: RenderEmptyHandler;
|
||||
csp?: CSPConfig;
|
||||
@ -58,6 +60,8 @@ export const ConfigContext = React.createContext<ConfigConsumerProps>({
|
||||
getPrefixCls: defaultGetPrefixCls,
|
||||
|
||||
renderEmpty: defaultRenderEmpty,
|
||||
|
||||
iconPrefixCls: defaultIconPrefixCls,
|
||||
});
|
||||
|
||||
export const ConfigConsumer = ConfigContext.Consumer;
|
||||
|
@ -1,13 +1,15 @@
|
||||
/* eslint-disable import/prefer-default-export, prefer-destructuring */
|
||||
|
||||
import { updateCSS } from 'rc-util/lib/Dom/dynamicCSS';
|
||||
import canUseDom from 'rc-util/lib/Dom/canUseDom';
|
||||
import { TinyColor } from '@ctrl/tinycolor';
|
||||
import { generate } from '@ant-design/colors';
|
||||
import { Theme } from './context';
|
||||
import devWarning from '../_util/devWarning';
|
||||
|
||||
const dynamicStyleMark = `-ant-${Date.now()}-${Math.random()}`;
|
||||
|
||||
export function registerTheme(globalPrefixCls: string, theme: Theme) {
|
||||
export function getStyle(globalPrefixCls: string, theme: Theme) {
|
||||
const variables: Record<string, string> = {};
|
||||
|
||||
const formatColor = (
|
||||
@ -86,12 +88,19 @@ export function registerTheme(globalPrefixCls: string, theme: Theme) {
|
||||
key => `--${globalPrefixCls}-${key}: ${variables[key]};`,
|
||||
);
|
||||
|
||||
updateCSS(
|
||||
`
|
||||
return `
|
||||
:root {
|
||||
${cssList.join('\n')}
|
||||
}
|
||||
`,
|
||||
`${dynamicStyleMark}-dynamic-theme`,
|
||||
);
|
||||
`.trim();
|
||||
}
|
||||
|
||||
export function registerTheme(globalPrefixCls: string, theme: Theme) {
|
||||
const style = getStyle(globalPrefixCls, theme);
|
||||
|
||||
if (canUseDom()) {
|
||||
updateCSS(style, `${dynamicStyleMark}-dynamic-theme`);
|
||||
} else {
|
||||
devWarning(false, 'ConfigProvider', 'SSR do not support dynamic theme with css variables.');
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
DirectionType,
|
||||
ConfigConsumerProps,
|
||||
Theme,
|
||||
defaultIconPrefixCls,
|
||||
} from './context';
|
||||
import SizeContext, { SizeContextProvider, SizeType } from './SizeContext';
|
||||
import message from '../message';
|
||||
@ -20,6 +21,8 @@ import notification from '../notification';
|
||||
import { RequiredMark } from '../form/Form';
|
||||
import { registerTheme } from './cssVariables';
|
||||
import defaultLocale from '../locale/default';
|
||||
import { DesignToken, DesignTokenContext } from '../_util/theme';
|
||||
import defaultThemeToken from '../_util/theme/default';
|
||||
|
||||
export {
|
||||
RenderEmptyHandler,
|
||||
@ -80,6 +83,10 @@ export interface ConfigProviderProps {
|
||||
};
|
||||
virtual?: boolean;
|
||||
dropdownMatchSelectWidth?: boolean;
|
||||
theme?: {
|
||||
token?: Partial<DesignToken>;
|
||||
hashed?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface ProviderChildrenProps extends ConfigProviderProps {
|
||||
@ -88,7 +95,7 @@ interface ProviderChildrenProps extends ConfigProviderProps {
|
||||
}
|
||||
|
||||
export const defaultPrefixCls = 'ant';
|
||||
export const defaultIconPrefixCls = 'anticon';
|
||||
export { defaultIconPrefixCls };
|
||||
let globalPrefixCls: string;
|
||||
let globalIconPrefixCls: string;
|
||||
|
||||
@ -159,6 +166,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = props => {
|
||||
legacyLocale,
|
||||
parentContext,
|
||||
iconPrefixCls,
|
||||
theme = {},
|
||||
} = props;
|
||||
|
||||
const getPrefixCls = React.useCallback(
|
||||
@ -211,7 +219,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = props => {
|
||||
|
||||
const memoIconContextValue = React.useMemo(
|
||||
() => ({ prefixCls: iconPrefixCls, csp }),
|
||||
[iconPrefixCls],
|
||||
[iconPrefixCls, csp],
|
||||
);
|
||||
|
||||
let childNode = children;
|
||||
@ -238,7 +246,7 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = props => {
|
||||
);
|
||||
}
|
||||
|
||||
if (iconPrefixCls) {
|
||||
if (iconPrefixCls || csp) {
|
||||
childNode = (
|
||||
<IconContext.Provider value={memoIconContextValue}>{childNode}</IconContext.Provider>
|
||||
);
|
||||
@ -248,10 +256,30 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = props => {
|
||||
childNode = <SizeContextProvider size={componentSize}>{childNode}</SizeContextProvider>;
|
||||
}
|
||||
|
||||
// ================================ Dynamic theme ================================
|
||||
const memoTheme = React.useMemo(
|
||||
() => ({
|
||||
token: {
|
||||
...defaultThemeToken,
|
||||
...theme?.token,
|
||||
},
|
||||
hashed: theme?.hashed,
|
||||
}),
|
||||
[theme?.token, theme?.hashed],
|
||||
);
|
||||
|
||||
if (theme?.token || theme?.hashed) {
|
||||
childNode = (
|
||||
<DesignTokenContext.Provider value={memoTheme}>{childNode}</DesignTokenContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
// =================================== Render ===================================
|
||||
return <ConfigContext.Provider value={memoedConfig}>{childNode}</ConfigContext.Provider>;
|
||||
};
|
||||
|
||||
const ConfigProvider: React.FC<ConfigProviderProps> & {
|
||||
/** @private internal Usage. do not use in your production */
|
||||
ConfigContext: typeof ConfigContext;
|
||||
SizeContext: typeof SizeContext;
|
||||
config: typeof setGlobalConfig;
|
||||
@ -284,7 +312,6 @@ const ConfigProvider: React.FC<ConfigProviderProps> & {
|
||||
);
|
||||
};
|
||||
|
||||
/** @private internal Usage. do not use in your production */
|
||||
ConfigProvider.ConfigContext = ConfigContext;
|
||||
ConfigProvider.SizeContext = SizeContext;
|
||||
ConfigProvider.config = setGlobalConfig;
|
||||
|
@ -202,4 +202,36 @@ describe('DatePicker', () => {
|
||||
expect(year).toBe(startDate.format('YYYY'));
|
||||
expect(wrapper.find('.ant-picker-time-panel').length).toBe(1);
|
||||
});
|
||||
|
||||
it('placement api work correctly ', () => {
|
||||
const popupAlignDefault = (points = ['tl', 'bl'], offset = [0, 4]) => ({
|
||||
points,
|
||||
offset,
|
||||
overflow: {
|
||||
adjustX: 1,
|
||||
adjustY: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const wrapper = mount(
|
||||
<DatePicker.RangePicker defaultValue={moment()} placement="bottomLeft" />,
|
||||
);
|
||||
expect(wrapper.find('Trigger').prop('popupAlign')).toEqual(popupAlignDefault(['tl', 'bl']));
|
||||
wrapper.setProps({
|
||||
placement: 'bottomRight',
|
||||
});
|
||||
expect(wrapper.find('Trigger').prop('popupAlign')).toEqual(popupAlignDefault(['tr', 'br']));
|
||||
wrapper.setProps({
|
||||
placement: 'topLeft',
|
||||
});
|
||||
expect(wrapper.find('Trigger').prop('popupAlign')).toEqual(
|
||||
popupAlignDefault(['bl', 'tl'], [0, -4]),
|
||||
);
|
||||
wrapper.setProps({
|
||||
placement: 'topRight',
|
||||
});
|
||||
expect(wrapper.find('Trigger').prop('popupAlign')).toEqual(
|
||||
popupAlignDefault(['br', 'tr'], [0, -4]),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -6,6 +6,7 @@ import DatePicker from '..';
|
||||
import { setMockDate, resetMockDate } from '../../../tests/utils';
|
||||
import { openPicker, selectCell, closePicker } from './utils';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import enUS from '../locale/en_US';
|
||||
|
||||
dayjs.extend(customParseFormat);
|
||||
|
||||
@ -99,4 +100,10 @@ describe('RangePicker', () => {
|
||||
expect(wrapper.find('input').first().props().placeholder).toEqual('Start date');
|
||||
expect(wrapper.find('input').last().props().placeholder).toEqual('End date');
|
||||
});
|
||||
|
||||
it('RangePicker picker quarter placeholder', () => {
|
||||
const wrapper = mount(<RangePicker picker="quarter" locale={enUS} />);
|
||||
expect(wrapper.find('input').at(0).props().placeholder).toEqual('Start quarter');
|
||||
expect(wrapper.find('input').at(1).props().placeholder).toEqual('End quarter');
|
||||
});
|
||||
});
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2349,6 +2349,216 @@ exports[`renders ./components/date-picker/demo/mode.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/date-picker/demo/placement.md correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-radio-group ant-radio-group-outline"
|
||||
>
|
||||
<label
|
||||
class="ant-radio-button-wrapper ant-radio-button-wrapper-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button ant-radio-button-checked"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="topLeft"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
topLeft
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="topRight"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
topRight
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="bottomLeft"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
bottomLeft
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="bottomRight"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
bottomRight
|
||||
</span>
|
||||
</label>
|
||||
</div>,
|
||||
<br />,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-picker"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Select date"
|
||||
readonly=""
|
||||
size="12"
|
||||
title=""
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-picker-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="calendar"
|
||||
class="anticon anticon-calendar"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="calendar"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>,
|
||||
<br />,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-picker ant-picker-range"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-input ant-picker-input-active"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Start date"
|
||||
readonly=""
|
||||
size="12"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-range-separator"
|
||||
>
|
||||
<span
|
||||
aria-label="to"
|
||||
class="ant-picker-separator"
|
||||
>
|
||||
<span
|
||||
aria-label="swap-right"
|
||||
class="anticon anticon-swap-right"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="swap-right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M873.1 596.2l-164-208A32 32 0 00684 376h-64.8c-6.7 0-10.4 7.7-6.3 13l144.3 183H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h695.9c26.8 0 41.7-30.8 25.2-51.8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="End date"
|
||||
readonly=""
|
||||
size="12"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-active-bar"
|
||||
style="left:0;width:0;position:absolute"
|
||||
/>
|
||||
<span
|
||||
class="ant-picker-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="calendar"
|
||||
class="anticon anticon-calendar"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="calendar"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/date-picker/demo/presetted-ranges.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
@ -2875,6 +3085,92 @@ exports[`renders ./components/date-picker/demo/range-picker.md correctly 1`] = `
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:12px"
|
||||
>
|
||||
<div
|
||||
class="ant-picker ant-picker-range"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-input ant-picker-input-active"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Start quarter"
|
||||
readonly=""
|
||||
size="12"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-range-separator"
|
||||
>
|
||||
<span
|
||||
aria-label="to"
|
||||
class="ant-picker-separator"
|
||||
>
|
||||
<span
|
||||
aria-label="swap-right"
|
||||
class="anticon anticon-swap-right"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="swap-right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M873.1 596.2l-164-208A32 32 0 00684 376h-64.8c-6.7 0-10.4 7.7-6.3 13l144.3 183H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h695.9c26.8 0 41.7-30.8 25.2-51.8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="End quarter"
|
||||
readonly=""
|
||||
size="12"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-active-bar"
|
||||
style="left:0;width:0;position:absolute"
|
||||
/>
|
||||
<span
|
||||
class="ant-picker-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="calendar"
|
||||
class="anticon anticon-calendar"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="calendar"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
@ -3432,6 +3728,277 @@ exports[`renders ./components/date-picker/demo/start-end.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/date-picker/demo/status.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-picker ant-picker-status-error"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Select date"
|
||||
readonly=""
|
||||
size="12"
|
||||
title=""
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-picker-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="calendar"
|
||||
class="anticon anticon-calendar"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="calendar"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-picker ant-picker-status-warning"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Select date"
|
||||
readonly=""
|
||||
size="12"
|
||||
title=""
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-picker-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="calendar"
|
||||
class="anticon anticon-calendar"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="calendar"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-picker ant-picker-range ant-picker-status-error"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-input ant-picker-input-active"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Start date"
|
||||
readonly=""
|
||||
size="12"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-range-separator"
|
||||
>
|
||||
<span
|
||||
aria-label="to"
|
||||
class="ant-picker-separator"
|
||||
>
|
||||
<span
|
||||
aria-label="swap-right"
|
||||
class="anticon anticon-swap-right"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="swap-right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M873.1 596.2l-164-208A32 32 0 00684 376h-64.8c-6.7 0-10.4 7.7-6.3 13l144.3 183H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h695.9c26.8 0 41.7-30.8 25.2-51.8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="End date"
|
||||
readonly=""
|
||||
size="12"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-active-bar"
|
||||
style="left:0;width:0;position:absolute"
|
||||
/>
|
||||
<span
|
||||
class="ant-picker-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="calendar"
|
||||
class="anticon anticon-calendar"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="calendar"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-picker ant-picker-range ant-picker-status-warning"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-input ant-picker-input-active"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Start date"
|
||||
readonly=""
|
||||
size="12"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-range-separator"
|
||||
>
|
||||
<span
|
||||
aria-label="to"
|
||||
class="ant-picker-separator"
|
||||
>
|
||||
<span
|
||||
aria-label="swap-right"
|
||||
class="anticon anticon-swap-right"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="swap-right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M873.1 596.2l-164-208A32 32 0 00684 376h-64.8c-6.7 0-10.4 7.7-6.3 13l144.3 183H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h695.9c26.8 0 41.7-30.8 25.2-51.8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="End date"
|
||||
readonly=""
|
||||
size="12"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-active-bar"
|
||||
style="left:0;width:0;position:absolute"
|
||||
/>
|
||||
<span
|
||||
class="ant-picker-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="calendar"
|
||||
class="anticon anticon-calendar"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="calendar"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/date-picker/demo/suffix.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
|
47
components/date-picker/demo/placement.md
Normal file
47
components/date-picker/demo/placement.md
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
order: 28
|
||||
title:
|
||||
zh-CN: 基本
|
||||
en-US: Basic
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
可以通过 `placement` 手动指定弹出的位置。
|
||||
|
||||
## en-US
|
||||
|
||||
You can manually specify the position of the popup via `placement`.
|
||||
|
||||
```jsx
|
||||
import { DatePicker, Space, Radio } from 'antd';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
const SetPlacementDemo = () => {
|
||||
const [placement, SetPlacement] = React.useState('topLeft');
|
||||
|
||||
const placementChange = e => {
|
||||
SetPlacement(e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Radio.Group value={placement} onChange={placementChange}>
|
||||
<Radio.Button value="topLeft">topLeft</Radio.Button>
|
||||
<Radio.Button value="topRight">topRight</Radio.Button>
|
||||
<Radio.Button value="bottomLeft">bottomLeft</Radio.Button>
|
||||
<Radio.Button value="bottomRight">bottomRight</Radio.Button>
|
||||
</Radio.Group>
|
||||
<br />
|
||||
<br />
|
||||
<DatePicker placement={placement} />
|
||||
<br />
|
||||
<br />
|
||||
<RangePicker placement={placement} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<SetPlacementDemo />, mountNode);
|
||||
```
|
@ -24,6 +24,7 @@ ReactDOM.render(
|
||||
<RangePicker showTime />
|
||||
<RangePicker picker="week" />
|
||||
<RangePicker picker="month" />
|
||||
<RangePicker picker="quarter" />
|
||||
<RangePicker picker="year" />
|
||||
</Space>,
|
||||
mountNode,
|
||||
|
30
components/date-picker/demo/status.md
Normal file
30
components/date-picker/demo/status.md
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
order: 19
|
||||
version: 4.19.0
|
||||
title:
|
||||
zh-CN: 自定义状态
|
||||
en-US: Status
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `status` 为 DatePicker 添加状态,可选 `error` 或者 `warning`。
|
||||
|
||||
## en-US
|
||||
|
||||
Add status to DatePicker with `status`, which could be `error` or `warning`.
|
||||
|
||||
```tsx
|
||||
import { DatePicker, Space } from 'antd';
|
||||
|
||||
const Status: React.FC = () => (
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<DatePicker status="error" style={{ width: '100%' }} />
|
||||
<DatePicker status="warning" style={{ width: '100%' }} />
|
||||
<DatePicker.RangePicker status="error" style={{ width: '100%' }} />
|
||||
<DatePicker.RangePicker status="warning" style={{ width: '100%' }} />
|
||||
</Space>
|
||||
);
|
||||
|
||||
ReactDOM.render(<Status />, mountNode);
|
||||
```
|
@ -6,13 +6,16 @@ import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
import SwapRightOutlined from '@ant-design/icons/SwapRightOutlined';
|
||||
import { RangePicker as RCRangePicker } from 'rc-picker';
|
||||
import { GenerateConfig } from 'rc-picker/lib/generate/index';
|
||||
import { PickerMode } from 'rc-picker/lib/interface';
|
||||
import enUS from '../locale/en_US';
|
||||
import { ConfigContext, ConfigConsumerProps } from '../../config-provider';
|
||||
import SizeContext from '../../config-provider/SizeContext';
|
||||
import LocaleReceiver from '../../locale-provider/LocaleReceiver';
|
||||
import { getRangePlaceholder } from '../util';
|
||||
import { getRangePlaceholder, transPlacement2DropdownAlign } from '../util';
|
||||
import { RangePickerProps, PickerLocale, getTimeProps, Components } from '.';
|
||||
import { PickerComponentClass } from './interface';
|
||||
import { FormItemStatusContext } from '../../form/context';
|
||||
import { getFeedbackIcon, getMergedStatus, getStatusClassNames } from '../../_util/statusUtils';
|
||||
|
||||
export default function generateRangePicker<DateType>(
|
||||
generateConfig: GenerateConfig<DateType>,
|
||||
@ -36,6 +39,23 @@ export default function generateRangePicker<DateType>(
|
||||
}
|
||||
};
|
||||
|
||||
renderFeedback = (prefixCls: string) => (
|
||||
<FormItemStatusContext.Consumer>
|
||||
{({ hasFeedback, status: contextStatus }) => {
|
||||
const { status: customStatus } = this.props;
|
||||
const status = getMergedStatus(contextStatus, customStatus);
|
||||
return hasFeedback && getFeedbackIcon(prefixCls, status);
|
||||
}}
|
||||
</FormItemStatusContext.Consumer>
|
||||
);
|
||||
|
||||
renderSuffix = (prefixCls: string, mergedPicker?: PickerMode) => (
|
||||
<>
|
||||
{mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
|
||||
{this.renderFeedback(prefixCls)}
|
||||
</>
|
||||
);
|
||||
|
||||
renderPicker = (contextLocale: PickerLocale) => {
|
||||
const locale = { ...contextLocale, ...this.props.locale };
|
||||
const { getPrefixCls, direction, getPopupContainer } = this.context;
|
||||
@ -43,9 +63,11 @@ export default function generateRangePicker<DateType>(
|
||||
prefixCls: customizePrefixCls,
|
||||
getPopupContainer: customGetPopupContainer,
|
||||
className,
|
||||
placement,
|
||||
size: customizeSize,
|
||||
bordered = true,
|
||||
placeholder,
|
||||
status: customStatus,
|
||||
...restProps
|
||||
} = this.props;
|
||||
const { format, showTime, picker } = this.props as any;
|
||||
@ -66,38 +88,48 @@ export default function generateRangePicker<DateType>(
|
||||
const mergedSize = customizeSize || size;
|
||||
|
||||
return (
|
||||
<RCRangePicker<DateType>
|
||||
separator={
|
||||
<span aria-label="to" className={`${prefixCls}-separator`}>
|
||||
<SwapRightOutlined />
|
||||
</span>
|
||||
}
|
||||
ref={this.pickerRef}
|
||||
placeholder={getRangePlaceholder(picker, locale, placeholder)}
|
||||
suffixIcon={picker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
|
||||
clearIcon={<CloseCircleFilled />}
|
||||
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
|
||||
nextIcon={<span className={`${prefixCls}-next-icon`} />}
|
||||
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
|
||||
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
|
||||
allowClear
|
||||
transitionName={`${rootPrefixCls}-slide-up`}
|
||||
{...restProps}
|
||||
{...additionalOverrideProps}
|
||||
className={classNames(
|
||||
{
|
||||
[`${prefixCls}-${mergedSize}`]: mergedSize,
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
className,
|
||||
<FormItemStatusContext.Consumer>
|
||||
{({ hasFeedback, status: contextStatus }) => (
|
||||
<RCRangePicker<DateType>
|
||||
separator={
|
||||
<span aria-label="to" className={`${prefixCls}-separator`}>
|
||||
<SwapRightOutlined />
|
||||
</span>
|
||||
}
|
||||
ref={this.pickerRef}
|
||||
dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
|
||||
placeholder={getRangePlaceholder(picker, locale, placeholder)}
|
||||
suffixIcon={this.renderSuffix(prefixCls, picker)}
|
||||
clearIcon={<CloseCircleFilled />}
|
||||
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
|
||||
nextIcon={<span className={`${prefixCls}-next-icon`} />}
|
||||
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
|
||||
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
|
||||
allowClear
|
||||
transitionName={`${rootPrefixCls}-slide-up`}
|
||||
{...restProps}
|
||||
{...additionalOverrideProps}
|
||||
className={classNames(
|
||||
{
|
||||
[`${prefixCls}-${mergedSize}`]: mergedSize,
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
getStatusClassNames(
|
||||
prefixCls,
|
||||
getMergedStatus(contextStatus, customStatus),
|
||||
hasFeedback,
|
||||
),
|
||||
className,
|
||||
)}
|
||||
locale={locale!.lang}
|
||||
prefixCls={prefixCls}
|
||||
getPopupContainer={customGetPopupContainer || getPopupContainer}
|
||||
generateConfig={generateConfig}
|
||||
components={Components}
|
||||
direction={direction}
|
||||
/>
|
||||
)}
|
||||
locale={locale!.lang}
|
||||
prefixCls={prefixCls}
|
||||
getPopupContainer={customGetPopupContainer || getPopupContainer}
|
||||
generateConfig={generateConfig}
|
||||
components={Components}
|
||||
direction={direction}
|
||||
/>
|
||||
</FormItemStatusContext.Consumer>
|
||||
);
|
||||
}}
|
||||
</SizeContext.Consumer>
|
||||
|
@ -7,7 +7,7 @@ import RCPicker from 'rc-picker';
|
||||
import { PickerMode } from 'rc-picker/lib/interface';
|
||||
import { GenerateConfig } from 'rc-picker/lib/generate/index';
|
||||
import enUS from '../locale/en_US';
|
||||
import { getPlaceholder } from '../util';
|
||||
import { getPlaceholder, transPlacement2DropdownAlign } from '../util';
|
||||
import devWarning from '../../_util/devWarning';
|
||||
import { ConfigContext, ConfigConsumerProps } from '../../config-provider';
|
||||
import LocaleReceiver from '../../locale-provider/LocaleReceiver';
|
||||
@ -21,9 +21,18 @@ import {
|
||||
Components,
|
||||
} from '.';
|
||||
import { PickerComponentClass } from './interface';
|
||||
import { FormItemStatusContext } from '../../form/context';
|
||||
import {
|
||||
getFeedbackIcon,
|
||||
getMergedStatus,
|
||||
getStatusClassNames,
|
||||
InputStatus,
|
||||
} from '../../_util/statusUtils';
|
||||
|
||||
export default function generatePicker<DateType>(generateConfig: GenerateConfig<DateType>) {
|
||||
type DatePickerProps = PickerProps<DateType>;
|
||||
type DatePickerProps = PickerProps<DateType> & {
|
||||
status?: InputStatus;
|
||||
};
|
||||
|
||||
function getPicker<InnerPickerProps extends DatePickerProps>(
|
||||
picker?: PickerMode,
|
||||
@ -59,6 +68,23 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
|
||||
}
|
||||
};
|
||||
|
||||
renderFeedback = (prefixCls: string) => (
|
||||
<FormItemStatusContext.Consumer>
|
||||
{({ hasFeedback, status: contextStatus }) => {
|
||||
const { status: customStatus } = this.props;
|
||||
const status = getMergedStatus(contextStatus, customStatus);
|
||||
return hasFeedback && getFeedbackIcon(prefixCls, status);
|
||||
}}
|
||||
</FormItemStatusContext.Consumer>
|
||||
);
|
||||
|
||||
renderSuffix = (prefixCls: string, mergedPicker?: PickerMode) => (
|
||||
<>
|
||||
{mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
|
||||
{this.renderFeedback(prefixCls)}
|
||||
</>
|
||||
);
|
||||
|
||||
renderPicker = (contextLocale: PickerLocale) => {
|
||||
const locale = { ...contextLocale, ...this.props.locale };
|
||||
const { getPrefixCls, direction, getPopupContainer } = this.context;
|
||||
@ -68,7 +94,9 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
|
||||
className,
|
||||
size: customizeSize,
|
||||
bordered = true,
|
||||
placement,
|
||||
placeholder,
|
||||
status: customStatus,
|
||||
...restProps
|
||||
} = this.props;
|
||||
const { format, showTime } = this.props as any;
|
||||
@ -99,36 +127,44 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
|
||||
const mergedSize = customizeSize || size;
|
||||
|
||||
return (
|
||||
<RCPicker<DateType>
|
||||
ref={this.pickerRef}
|
||||
placeholder={getPlaceholder(mergedPicker, locale, placeholder)}
|
||||
suffixIcon={
|
||||
mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />
|
||||
}
|
||||
clearIcon={<CloseCircleFilled />}
|
||||
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
|
||||
nextIcon={<span className={`${prefixCls}-next-icon`} />}
|
||||
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
|
||||
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
|
||||
allowClear
|
||||
transitionName={`${rootPrefixCls}-slide-up`}
|
||||
{...additionalProps}
|
||||
{...restProps}
|
||||
{...additionalOverrideProps}
|
||||
locale={locale!.lang}
|
||||
className={classNames(
|
||||
{
|
||||
[`${prefixCls}-${mergedSize}`]: mergedSize,
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
className,
|
||||
<FormItemStatusContext.Consumer>
|
||||
{({ hasFeedback, status: contextStatus }) => (
|
||||
<RCPicker<DateType>
|
||||
ref={this.pickerRef}
|
||||
placeholder={getPlaceholder(mergedPicker, locale, placeholder)}
|
||||
suffixIcon={this.renderSuffix(prefixCls, mergedPicker)}
|
||||
dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
|
||||
clearIcon={<CloseCircleFilled />}
|
||||
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
|
||||
nextIcon={<span className={`${prefixCls}-next-icon`} />}
|
||||
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
|
||||
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
|
||||
allowClear
|
||||
transitionName={`${rootPrefixCls}-slide-up`}
|
||||
{...additionalProps}
|
||||
{...restProps}
|
||||
{...additionalOverrideProps}
|
||||
locale={locale!.lang}
|
||||
className={classNames(
|
||||
{
|
||||
[`${prefixCls}-${mergedSize}`]: mergedSize,
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
getStatusClassNames(
|
||||
prefixCls,
|
||||
getMergedStatus(contextStatus, customStatus),
|
||||
hasFeedback,
|
||||
),
|
||||
className,
|
||||
)}
|
||||
prefixCls={prefixCls}
|
||||
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
|
||||
generateConfig={generateConfig}
|
||||
components={Components}
|
||||
direction={direction}
|
||||
/>
|
||||
)}
|
||||
prefixCls={prefixCls}
|
||||
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
|
||||
generateConfig={generateConfig}
|
||||
components={Components}
|
||||
direction={direction}
|
||||
/>
|
||||
</FormItemStatusContext.Consumer>
|
||||
);
|
||||
}}
|
||||
</SizeContext.Consumer>
|
||||
|
@ -17,6 +17,8 @@ import PickerTag from '../PickerTag';
|
||||
import { TimePickerLocale } from '../../time-picker';
|
||||
import generateSinglePicker from './generateSinglePicker';
|
||||
import generateRangePicker from './generateRangePicker';
|
||||
import { tuple } from '../../_util/type';
|
||||
import { InputStatus } from '../../_util/statusUtils';
|
||||
|
||||
export const Components = { button: PickerButton, rangeItem: PickerTag };
|
||||
|
||||
@ -27,13 +29,18 @@ function toArray<T>(list: T | T[]): T[] {
|
||||
return Array.isArray(list) ? list : [list];
|
||||
}
|
||||
|
||||
export function getTimeProps<DateType>(
|
||||
props: { format?: string; picker?: PickerMode } & SharedTimeProps<DateType>,
|
||||
export function getTimeProps<DateType, DisabledTime>(
|
||||
props: { format?: string; picker?: PickerMode } & Omit<
|
||||
SharedTimeProps<DateType>,
|
||||
'disabledTime'
|
||||
> & {
|
||||
disabledTime?: DisabledTime;
|
||||
},
|
||||
) {
|
||||
const { format, picker, showHour, showMinute, showSecond, use12Hours } = props;
|
||||
|
||||
const firstFormat = toArray(format)[0];
|
||||
const showTimeObj: SharedTimeProps<DateType> = { ...props };
|
||||
const showTimeObj = { ...props };
|
||||
|
||||
if (firstFormat && typeof firstFormat === 'string') {
|
||||
if (!firstFormat.includes('s') && showSecond === undefined) {
|
||||
@ -64,17 +71,18 @@ export function getTimeProps<DateType>(
|
||||
showTime: showTimeObj,
|
||||
};
|
||||
}
|
||||
const DataPickerPlacements = tuple('bottomLeft', 'bottomRight', 'topLeft', 'topRight');
|
||||
type DataPickerPlacement = typeof DataPickerPlacements[number];
|
||||
|
||||
type InjectDefaultProps<Props> = Omit<
|
||||
Props,
|
||||
| 'locale'
|
||||
| 'generateConfig'
|
||||
| 'hideHeader'
|
||||
| 'components'
|
||||
'locale' | 'generateConfig' | 'hideHeader' | 'components'
|
||||
> & {
|
||||
locale?: PickerLocale;
|
||||
size?: SizeType;
|
||||
placement?: DataPickerPlacement;
|
||||
bordered?: boolean;
|
||||
status?: InputStatus;
|
||||
};
|
||||
|
||||
export type PickerLocale = {
|
||||
@ -96,6 +104,7 @@ export type AdditionalPickerLocaleLangProps = {
|
||||
monthPlaceholder?: string;
|
||||
weekPlaceholder?: string;
|
||||
rangeYearPlaceholder?: [string, string];
|
||||
rangeQuarterPlaceholder?: [string, string];
|
||||
rangeMonthPlaceholder?: [string, string];
|
||||
rangeWeekPlaceholder?: [string, string];
|
||||
rangePlaceholder?: [string, string];
|
||||
|
@ -69,9 +69,11 @@ The following APIs are shared by DatePicker, RangePicker.
|
||||
| panelRender | Customize panel render | (panelNode) => ReactNode | - | 4.5.0 |
|
||||
| picker | Set picker type | `date` \| `week` \| `month` \| `quarter` \| `year` | `date` | `quarter`: 4.1.0 |
|
||||
| placeholder | The placeholder of date input | string \| \[string,string] | - | |
|
||||
| placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | |
|
||||
| popupStyle | To customize the style of the popup calendar | CSSProperties | {} | |
|
||||
| prevIcon | The custom prev icon | ReactNode | - | 4.17.0 |
|
||||
| size | To determine the size of the input box, the height of `large` and `small`, are 40px and 24px respectively, while default size is 32px | `large` \| `middle` \| `small` | - | |
|
||||
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| style | To customize the style of the input box | CSSProperties | {} | |
|
||||
| suffixIcon | The custom suffix icon | ReactNode | - | |
|
||||
| superNextIcon | The custom super next icon | ReactNode | - | 4.17.0 |
|
||||
|
@ -70,9 +70,11 @@ import locale from 'antd/lib/locale/zh_CN';
|
||||
| panelRender | 自定义渲染面板 | (panelNode) => ReactNode | - | 4.5.0 |
|
||||
| picker | 设置选择器类型 | `date` \| `week` \| `month` \| `quarter` \| `year` | `date` | `quarter`: 4.1.0 |
|
||||
| placeholder | 输入框提示文字 | string \| \[string, string] | - | |
|
||||
| placement | 选择框弹出的位置 | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | |
|
||||
| popupStyle | 额外的弹出日历样式 | CSSProperties | {} | |
|
||||
| prevIcon | 自定义上一个图标 | ReactNode | - | 4.17.0 |
|
||||
| size | 输入框大小,`large` 高度为 40px,`small` 为 24px,默认是 32px | `large` \| `middle` \| `small` | - | |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| style | 自定义输入框样式 | CSSProperties | {} | |
|
||||
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
|
||||
| superNextIcon | 自定义 `<<` 切换图标 | ReactNode | - | 4.17.0 |
|
||||
|
@ -12,6 +12,7 @@ const locale: PickerLocale = {
|
||||
weekPlaceholder: 'Select week',
|
||||
rangePlaceholder: ['Start date', 'End date'],
|
||||
rangeYearPlaceholder: ['Start year', 'End year'],
|
||||
rangeQuarterPlaceholder: ['Start quarter', 'End quarter'],
|
||||
rangeMonthPlaceholder: ['Start month', 'End month'],
|
||||
rangeWeekPlaceholder: ['Start week', 'End week'],
|
||||
...CalendarLocale,
|
||||
|
@ -12,6 +12,7 @@ const locale: PickerLocale = {
|
||||
weekPlaceholder: 'Select week',
|
||||
rangePlaceholder: ['Start date', 'End date'],
|
||||
rangeYearPlaceholder: ['Start year', 'End year'],
|
||||
rangeQuarterPlaceholder: ['Start quarter', 'End quarter'],
|
||||
rangeMonthPlaceholder: ['Start month', 'End month'],
|
||||
rangeWeekPlaceholder: ['Start week', 'End week'],
|
||||
...CalendarLocale,
|
||||
|
@ -13,6 +13,7 @@ const locale: PickerLocale = {
|
||||
rangePlaceholder: ['开始日期', '结束日期'],
|
||||
rangeYearPlaceholder: ['开始年份', '结束年份'],
|
||||
rangeMonthPlaceholder: ['开始月份', '结束月份'],
|
||||
rangeQuarterPlaceholder: ['开始季度', '结束季度'],
|
||||
rangeWeekPlaceholder: ['开始周', '结束周'],
|
||||
...CalendarLocale,
|
||||
},
|
||||
|
@ -13,6 +13,7 @@ const locale: PickerLocale = {
|
||||
rangePlaceholder: ['開始日期', '結束日期'],
|
||||
rangeYearPlaceholder: ['開始年份', '結束年份'],
|
||||
rangeMonthPlaceholder: ['開始月份', '結束月份'],
|
||||
rangeQuarterPlaceholder: ['開始季度', '結束季度'],
|
||||
rangeWeekPlaceholder: ['開始周', '結束周'],
|
||||
...CalendarLocale,
|
||||
},
|
||||
|
@ -1,6 +1,7 @@
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
@import '../../input/style/mixin';
|
||||
@import './status';
|
||||
|
||||
@picker-prefix-cls: ~'@{ant-prefix}-picker';
|
||||
|
||||
@ -13,7 +14,7 @@
|
||||
}
|
||||
|
||||
.@{picker-prefix-cls} {
|
||||
@arrow-size: 10px;
|
||||
@arrow-size: @popover-arrow-width;
|
||||
|
||||
.reset-component();
|
||||
.picker-padding(@input-height-base, @font-size-base, @input-padding-horizontal-base);
|
||||
@ -106,6 +107,8 @@
|
||||
}
|
||||
|
||||
&-suffix {
|
||||
display: flex;
|
||||
flex: none;
|
||||
align-self: center;
|
||||
margin-left: (@padding-xs / 2);
|
||||
color: @disabled-color;
|
||||
@ -114,6 +117,10 @@
|
||||
|
||||
> * {
|
||||
vertical-align: top;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,17 +228,17 @@
|
||||
|
||||
&-placement-bottomLeft {
|
||||
.@{picker-prefix-cls}-range-arrow {
|
||||
top: (@arrow-size / 2) - (@arrow-size / 3);
|
||||
top: (@arrow-size / 2) - (@arrow-size / 3) + 0.7px;
|
||||
display: block;
|
||||
transform: rotate(-45deg);
|
||||
transform: rotate(-135deg) translateY(1px);
|
||||
}
|
||||
}
|
||||
|
||||
&-placement-topLeft {
|
||||
.@{picker-prefix-cls}-range-arrow {
|
||||
bottom: (@arrow-size / 2) - (@arrow-size / 3);
|
||||
bottom: (@arrow-size / 2) - (@arrow-size / 3) + 0.7px;
|
||||
display: block;
|
||||
transform: rotate(135deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -311,19 +318,14 @@
|
||||
width: @arrow-size;
|
||||
height: @arrow-size;
|
||||
margin-left: @input-padding-horizontal-base * 1.5;
|
||||
box-shadow: 2px -2px 6px fade(@black, 6%);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
transparent 40%,
|
||||
@calendar-bg 40%
|
||||
); // Use linear-gradient to prevent arrow from covering text
|
||||
box-shadow: 2px 2px 6px -2px fade(@black, 10%); // use spread radius to hide shadow over popover
|
||||
transition: left @animation-duration-slow ease-out;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: @border-width-base;
|
||||
right: @border-width-base;
|
||||
width: @arrow-size;
|
||||
height: @arrow-size;
|
||||
border: (@arrow-size / 2) solid @border-color-split;
|
||||
border-color: @calendar-bg @calendar-bg transparent transparent;
|
||||
content: '';
|
||||
}
|
||||
.roundedArrow(@arrow-size, 5px, @calendar-bg);
|
||||
}
|
||||
|
||||
&-panel-container {
|
||||
|
@ -3,3 +3,5 @@ import './index.less';
|
||||
// style dependencies
|
||||
import '../../tag/style';
|
||||
import '../../button/style';
|
||||
|
||||
// deps-lint-skip: form
|
||||
|
52
components/date-picker/style/status.less
Normal file
52
components/date-picker/style/status.less
Normal file
@ -0,0 +1,52 @@
|
||||
@import '../../input/style/mixin';
|
||||
|
||||
@picker-prefix-cls: ~'@{ant-prefix}-picker';
|
||||
|
||||
.picker-status-color(
|
||||
@text-color: @input-color;
|
||||
@border-color: @input-border-color;
|
||||
@background-color: @input-bg;
|
||||
@hoverBorderColor: @primary-color-hover;
|
||||
@outlineColor: @primary-color-outline;
|
||||
) {
|
||||
&.@{picker-prefix-cls} {
|
||||
&,
|
||||
&:not([disabled]):hover {
|
||||
background-color: @background-color;
|
||||
border-color: @border-color;
|
||||
}
|
||||
|
||||
&-focused,
|
||||
&:focus {
|
||||
.active(@text-color, @hoverBorderColor, @outlineColor);
|
||||
}
|
||||
}
|
||||
|
||||
.@{picker-prefix-cls}-feedback-icon {
|
||||
color: @text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.@{picker-prefix-cls} {
|
||||
&-status-error {
|
||||
.picker-status-color(@error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
|
||||
}
|
||||
|
||||
&-status-warning {
|
||||
.picker-status-color(@warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
|
||||
}
|
||||
|
||||
&-status-validating {
|
||||
.@{picker-prefix-cls}-feedback-icon {
|
||||
display: inline-block;
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-status-success {
|
||||
.@{picker-prefix-cls}-feedback-icon {
|
||||
color: @success-color;
|
||||
animation-name: diffZoomIn1 !important;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
import { PickerMode } from 'rc-picker/lib/interface';
|
||||
import { DirectionType } from '../config-provider';
|
||||
import { SelectCommonPlacement } from '../_util/motion';
|
||||
import { PickerLocale } from './generatePicker';
|
||||
|
||||
export function getPlaceholder(
|
||||
@ -40,6 +42,9 @@ export function getRangePlaceholder(
|
||||
if (picker === 'year' && locale.lang.yearPlaceholder) {
|
||||
return locale.lang.rangeYearPlaceholder;
|
||||
}
|
||||
if (picker === 'quarter' && locale.lang.quarterPlaceholder) {
|
||||
return locale.lang.rangeQuarterPlaceholder;
|
||||
}
|
||||
if (picker === 'month' && locale.lang.monthPlaceholder) {
|
||||
return locale.lang.rangeMonthPlaceholder;
|
||||
}
|
||||
@ -51,3 +56,56 @@ export function getRangePlaceholder(
|
||||
}
|
||||
return locale.lang.rangePlaceholder;
|
||||
}
|
||||
|
||||
export function transPlacement2DropdownAlign(
|
||||
direction: DirectionType,
|
||||
placement?: SelectCommonPlacement,
|
||||
) {
|
||||
const overflow = {
|
||||
adjustX: 1,
|
||||
adjustY: 1,
|
||||
};
|
||||
switch (placement) {
|
||||
case 'bottomLeft': {
|
||||
return {
|
||||
points: ['tl', 'bl'],
|
||||
offset: [0, 4],
|
||||
overflow,
|
||||
};
|
||||
}
|
||||
case 'bottomRight': {
|
||||
return {
|
||||
points: ['tr', 'br'],
|
||||
offset: [0, 4],
|
||||
overflow,
|
||||
};
|
||||
}
|
||||
case 'topLeft': {
|
||||
return {
|
||||
points: ['bl', 'tl'],
|
||||
offset: [0, -4],
|
||||
overflow,
|
||||
};
|
||||
}
|
||||
case 'topRight': {
|
||||
return {
|
||||
points: ['br', 'tr'],
|
||||
offset: [0, -4],
|
||||
overflow,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return direction === 'rtl'
|
||||
? {
|
||||
points: ['tr', 'br'],
|
||||
offset: [0, 4],
|
||||
overflow,
|
||||
}
|
||||
: {
|
||||
points: ['tl', 'bl'],
|
||||
offset: [0, 4],
|
||||
overflow,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
|
||||
import useStyle from './style';
|
||||
|
||||
export interface DividerProps {
|
||||
prefixCls?: string;
|
||||
@ -14,56 +16,57 @@ export interface DividerProps {
|
||||
plain?: boolean;
|
||||
}
|
||||
|
||||
const Divider: React.FC<DividerProps> = props => (
|
||||
<ConfigConsumer>
|
||||
{({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
type = 'horizontal',
|
||||
orientation = 'center',
|
||||
orientationMargin,
|
||||
className,
|
||||
children,
|
||||
dashed,
|
||||
plain,
|
||||
...restProps
|
||||
} = props;
|
||||
const prefixCls = getPrefixCls('divider', customizePrefixCls);
|
||||
const orientationPrefix = orientation.length > 0 ? `-${orientation}` : orientation;
|
||||
const hasChildren = !!children;
|
||||
const hasCustomMarginLeft = orientation === 'left' && orientationMargin != null;
|
||||
const hasCustomMarginRight = orientation === 'right' && orientationMargin != null;
|
||||
const classString = classNames(
|
||||
prefixCls,
|
||||
`${prefixCls}-${type}`,
|
||||
{
|
||||
[`${prefixCls}-with-text`]: hasChildren,
|
||||
[`${prefixCls}-with-text${orientationPrefix}`]: hasChildren,
|
||||
[`${prefixCls}-dashed`]: !!dashed,
|
||||
[`${prefixCls}-plain`]: !!plain,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-no-default-orientation-margin-left`]: hasCustomMarginLeft,
|
||||
[`${prefixCls}-no-default-orientation-margin-right`]: hasCustomMarginRight,
|
||||
},
|
||||
className,
|
||||
);
|
||||
const Divider: React.FC<DividerProps> = props => {
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
|
||||
const innerStyle = {
|
||||
...(hasCustomMarginLeft && { marginLeft: orientationMargin }),
|
||||
...(hasCustomMarginRight && { marginRight: orientationMargin }),
|
||||
};
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
type = 'horizontal',
|
||||
orientation = 'center',
|
||||
orientationMargin,
|
||||
className,
|
||||
children,
|
||||
dashed,
|
||||
plain,
|
||||
...restProps
|
||||
} = props;
|
||||
const prefixCls = getPrefixCls('divider', customizePrefixCls);
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
|
||||
return (
|
||||
<div className={classString} {...restProps} role="separator">
|
||||
{children && (
|
||||
<span className={`${prefixCls}-inner-text`} style={innerStyle}>
|
||||
{children}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</ConfigConsumer>
|
||||
);
|
||||
const orientationPrefix = orientation.length > 0 ? `-${orientation}` : orientation;
|
||||
const hasChildren = !!children;
|
||||
const hasCustomMarginLeft = orientation === 'left' && orientationMargin != null;
|
||||
const hasCustomMarginRight = orientation === 'right' && orientationMargin != null;
|
||||
const classString = classNames(
|
||||
prefixCls,
|
||||
hashId,
|
||||
`${prefixCls}-${type}`,
|
||||
{
|
||||
[`${prefixCls}-with-text`]: hasChildren,
|
||||
[`${prefixCls}-with-text${orientationPrefix}`]: hasChildren,
|
||||
[`${prefixCls}-dashed`]: !!dashed,
|
||||
[`${prefixCls}-plain`]: !!plain,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-no-default-orientation-margin-left`]: hasCustomMarginLeft,
|
||||
[`${prefixCls}-no-default-orientation-margin-right`]: hasCustomMarginRight,
|
||||
},
|
||||
className,
|
||||
);
|
||||
|
||||
const innerStyle = {
|
||||
...(hasCustomMarginLeft && { marginLeft: orientationMargin }),
|
||||
...(hasCustomMarginRight && { marginRight: orientationMargin }),
|
||||
};
|
||||
|
||||
return wrapSSR(
|
||||
<div className={classString} {...restProps} role="separator">
|
||||
{children && (
|
||||
<span className={`${prefixCls}-inner-text`} style={innerStyle}>
|
||||
{children}
|
||||
</span>
|
||||
)}
|
||||
</div>,
|
||||
);
|
||||
};
|
||||
|
||||
export default Divider;
|
||||
|
@ -1,137 +1,137 @@
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
// @import '../../style/themes/index';
|
||||
// @import '../../style/mixins/index';
|
||||
|
||||
@divider-prefix-cls: ~'@{ant-prefix}-divider';
|
||||
// @divider-prefix-cls: ~'@{ant-prefix}-divider';
|
||||
|
||||
.@{divider-prefix-cls} {
|
||||
.reset-component();
|
||||
// .@{divider-prefix-cls} {
|
||||
// .reset-component();
|
||||
|
||||
border-top: @border-width-base solid @divider-color;
|
||||
// border-top: @border-width-base solid @divider-color;
|
||||
|
||||
&-vertical {
|
||||
position: relative;
|
||||
top: -0.06em;
|
||||
display: inline-block;
|
||||
height: 0.9em;
|
||||
margin: 0 8px;
|
||||
vertical-align: middle;
|
||||
border-top: 0;
|
||||
border-left: @border-width-base solid @divider-color;
|
||||
}
|
||||
// &-vertical {
|
||||
// position: relative;
|
||||
// top: -0.06em;
|
||||
// display: inline-block;
|
||||
// height: 0.9em;
|
||||
// margin: 0 @divider-vertical-gutter;
|
||||
// vertical-align: middle;
|
||||
// border-top: 0;
|
||||
// border-left: @border-width-base solid @divider-color;
|
||||
// }
|
||||
|
||||
&-horizontal {
|
||||
display: flex;
|
||||
clear: both;
|
||||
width: 100%;
|
||||
min-width: 100%; // Fix https://github.com/ant-design/ant-design/issues/10914
|
||||
margin: 24px 0;
|
||||
}
|
||||
// &-horizontal {
|
||||
// display: flex;
|
||||
// clear: both;
|
||||
// width: 100%;
|
||||
// min-width: 100%; // Fix https://github.com/ant-design/ant-design/issues/10914
|
||||
// margin: 24px 0;
|
||||
// }
|
||||
|
||||
&-horizontal&-with-text {
|
||||
display: flex;
|
||||
margin: 16px 0;
|
||||
color: @heading-color;
|
||||
font-weight: 500;
|
||||
font-size: @font-size-lg;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
border-top: 0;
|
||||
border-top-color: @divider-color;
|
||||
// &-horizontal&-with-text {
|
||||
// display: flex;
|
||||
// margin: 16px 0;
|
||||
// color: @heading-color;
|
||||
// font-weight: 500;
|
||||
// font-size: @font-size-lg;
|
||||
// white-space: nowrap;
|
||||
// text-align: center;
|
||||
// border-top: 0;
|
||||
// border-top-color: @divider-color;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
width: 50%;
|
||||
border-top: @border-width-base solid transparent;
|
||||
// Chrome not accept `inherit` in `border-top`
|
||||
border-top-color: inherit;
|
||||
border-bottom: 0;
|
||||
transform: translateY(50%);
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
// &::before,
|
||||
// &::after {
|
||||
// position: relative;
|
||||
// top: 50%;
|
||||
// width: 50%;
|
||||
// border-top: @border-width-base solid transparent;
|
||||
// // Chrome not accept `inherit` in `border-top`
|
||||
// border-top-color: inherit;
|
||||
// border-bottom: 0;
|
||||
// transform: translateY(50%);
|
||||
// content: '';
|
||||
// }
|
||||
// }
|
||||
|
||||
&-horizontal&-with-text-left {
|
||||
&::before {
|
||||
top: 50%;
|
||||
width: @divider-orientation-margin;
|
||||
}
|
||||
// &-horizontal&-with-text-left {
|
||||
// &::before {
|
||||
// top: 50%;
|
||||
// width: @divider-orientation-margin;
|
||||
// }
|
||||
|
||||
&::after {
|
||||
top: 50%;
|
||||
width: 100% - @divider-orientation-margin;
|
||||
}
|
||||
}
|
||||
// &::after {
|
||||
// top: 50%;
|
||||
// width: 100% - @divider-orientation-margin;
|
||||
// }
|
||||
// }
|
||||
|
||||
&-horizontal&-with-text-right {
|
||||
&::before {
|
||||
top: 50%;
|
||||
width: 100% - @divider-orientation-margin;
|
||||
}
|
||||
// &-horizontal&-with-text-right {
|
||||
// &::before {
|
||||
// top: 50%;
|
||||
// width: 100% - @divider-orientation-margin;
|
||||
// }
|
||||
|
||||
&::after {
|
||||
top: 50%;
|
||||
width: @divider-orientation-margin;
|
||||
}
|
||||
}
|
||||
// &::after {
|
||||
// top: 50%;
|
||||
// width: @divider-orientation-margin;
|
||||
// }
|
||||
// }
|
||||
|
||||
&-inner-text {
|
||||
display: inline-block;
|
||||
padding: 0 @divider-text-padding;
|
||||
}
|
||||
// &-inner-text {
|
||||
// display: inline-block;
|
||||
// padding: 0 @divider-text-padding;
|
||||
// }
|
||||
|
||||
&-dashed {
|
||||
background: none;
|
||||
border-color: @divider-color;
|
||||
border-style: dashed;
|
||||
border-width: @border-width-base 0 0;
|
||||
}
|
||||
// &-dashed {
|
||||
// background: none;
|
||||
// border-color: @divider-color;
|
||||
// border-style: dashed;
|
||||
// border-width: @border-width-base 0 0;
|
||||
// }
|
||||
|
||||
&-horizontal&-with-text&-dashed {
|
||||
&::before,
|
||||
&::after {
|
||||
border-style: dashed none none;
|
||||
}
|
||||
}
|
||||
// &-horizontal&-with-text&-dashed {
|
||||
// &::before,
|
||||
// &::after {
|
||||
// border-style: dashed none none;
|
||||
// }
|
||||
// }
|
||||
|
||||
&-vertical&-dashed {
|
||||
border-width: 0 0 0 @border-width-base;
|
||||
}
|
||||
// &-vertical&-dashed {
|
||||
// border-width: 0 0 0 @border-width-base;
|
||||
// }
|
||||
|
||||
&-plain&-with-text {
|
||||
color: @text-color;
|
||||
font-weight: normal;
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
// &-plain&-with-text {
|
||||
// color: @text-color;
|
||||
// font-weight: normal;
|
||||
// font-size: @font-size-base;
|
||||
// }
|
||||
|
||||
&-horizontal&-with-text-left&-no-default-orientation-margin-left {
|
||||
&::before {
|
||||
width: 0;
|
||||
}
|
||||
// &-horizontal&-with-text-left&-no-default-orientation-margin-left {
|
||||
// &::before {
|
||||
// width: 0;
|
||||
// }
|
||||
|
||||
&::after {
|
||||
width: 100%;
|
||||
}
|
||||
// &::after {
|
||||
// width: 100%;
|
||||
// }
|
||||
|
||||
.ant-divider-inner-text {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
// .ant-divider-inner-text {
|
||||
// padding-left: 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
&-horizontal&-with-text-right&-no-default-orientation-margin-right {
|
||||
&::before {
|
||||
width: 100%;
|
||||
}
|
||||
// &-horizontal&-with-text-right&-no-default-orientation-margin-right {
|
||||
// &::before {
|
||||
// width: 100%;
|
||||
// }
|
||||
|
||||
&::after {
|
||||
width: 0;
|
||||
}
|
||||
// &::after {
|
||||
// width: 0;
|
||||
// }
|
||||
|
||||
.ant-divider-inner-text {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// .ant-divider-inner-text {
|
||||
// padding-right: 0;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@import './rtl';
|
||||
// @import './rtl';
|
||||
|
@ -1,2 +1,195 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
// deps-lint-skip-all
|
||||
import { CSSObject } from '@ant-design/cssinjs';
|
||||
import {
|
||||
DerivativeToken,
|
||||
useStyleRegister,
|
||||
useToken,
|
||||
UseComponentStyleResult,
|
||||
resetComponent,
|
||||
GenerateStyle,
|
||||
} from '../../_util/theme';
|
||||
|
||||
interface DividerToken extends DerivativeToken {
|
||||
dividerCls: string;
|
||||
|
||||
dividerBorderColor: string;
|
||||
|
||||
dividerBorderWidth: number;
|
||||
|
||||
dividerNotDefaultTextPadding: number;
|
||||
dividerVerticalGutterMargin: number;
|
||||
dividerHorizontalWithTextGutterMargin: number;
|
||||
dividerHorizontalGutterMargin: number;
|
||||
}
|
||||
|
||||
// ============================== Shared ==============================
|
||||
const genSharedDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject => {
|
||||
const { dividerCls } = token;
|
||||
|
||||
return {
|
||||
[dividerCls]: {
|
||||
...resetComponent(token),
|
||||
borderBlockStart: `${token.dividerBorderWidth}px solid ${token.dividerBorderColor}`,
|
||||
|
||||
// vertical
|
||||
'&-vertical': {
|
||||
position: 'relative',
|
||||
top: '-0.06em',
|
||||
display: 'inline-block',
|
||||
height: '0.9em',
|
||||
margin: `0 ${token.dividerVerticalGutterMargin}px`,
|
||||
verticalAlign: 'middle',
|
||||
borderTop: 0,
|
||||
borderInlineStart: `${token.dividerBorderWidth}px solid ${token.dividerBorderColor}`,
|
||||
},
|
||||
|
||||
'&-horizontal': {
|
||||
display: 'flex',
|
||||
clear: 'both',
|
||||
width: '100%',
|
||||
minWidth: '100%', // Fix https://github.com/ant-design/ant-design/issues/10914
|
||||
margin: `${token.dividerHorizontalGutterMargin}px 0`,
|
||||
},
|
||||
|
||||
'&-horizontal&-with-text': {
|
||||
display: 'flex',
|
||||
margin: `${token.dividerHorizontalWithTextGutterMargin}px 0`,
|
||||
color: token.headingColor,
|
||||
fontWeight: 500,
|
||||
fontSize: token.fontSizeLG,
|
||||
whiteSpace: 'nowrap',
|
||||
textAlign: 'center',
|
||||
borderBlockStart: `0 ${token.dividerBorderColor}`,
|
||||
|
||||
'&::before, &::after': {
|
||||
position: 'relative',
|
||||
top: '50%',
|
||||
width: '50%',
|
||||
borderBlockStart: `${token.dividerBorderWidth}px solid transparent`,
|
||||
// Chrome not accept `inherit` in `border-top`
|
||||
borderBlockStartColor: 'inherit',
|
||||
borderBlockEnd: 0,
|
||||
transform: 'translateY(50%)',
|
||||
content: "''",
|
||||
},
|
||||
},
|
||||
|
||||
'&-horizontal&-with-text-left': {
|
||||
'&::before': {
|
||||
top: '50%',
|
||||
width: '5%',
|
||||
},
|
||||
|
||||
'&::after': {
|
||||
top: '50%',
|
||||
width: '95%',
|
||||
},
|
||||
},
|
||||
|
||||
'&-horizontal&-with-text-right': {
|
||||
'&::before': {
|
||||
top: '50%',
|
||||
width: '95%',
|
||||
},
|
||||
|
||||
'&::after': {
|
||||
top: '50%',
|
||||
width: '5%',
|
||||
},
|
||||
},
|
||||
|
||||
[`${dividerCls}-inner-text`]: {
|
||||
display: 'inline-block',
|
||||
padding: '0 1em',
|
||||
},
|
||||
|
||||
'&-dashed': {
|
||||
background: 'none',
|
||||
borderColor: token.dividerBorderColor,
|
||||
borderStyle: 'dashed',
|
||||
borderWidth: 0,
|
||||
borderBlockStart: `${token.dividerBorderWidth}px`,
|
||||
},
|
||||
|
||||
'&-horizontal&-with-text&-dashed': {
|
||||
'&::before, &::after': {
|
||||
borderStyle: 'dashed none none',
|
||||
},
|
||||
},
|
||||
|
||||
'&-vertical&-dashed': {
|
||||
borderWidth: `0 0 0 ${token.dividerBorderWidth}px`,
|
||||
},
|
||||
|
||||
'&-plain&-with-text': {
|
||||
color: token.textColor,
|
||||
fontWeight: 'normal',
|
||||
fontSize: token.fontSize,
|
||||
},
|
||||
|
||||
'&-horizontal&-with-text-left&-no-default-orientation-margin-left': {
|
||||
'&::before': {
|
||||
width: 0,
|
||||
},
|
||||
|
||||
'&::after': {
|
||||
width: '100%',
|
||||
},
|
||||
|
||||
'.ant-divider-inner-text': {
|
||||
paddingInlineStart: `${token.dividerNotDefaultTextPadding}px`,
|
||||
},
|
||||
},
|
||||
|
||||
'&-horizontal&-with-text-right&-no-default-orientation-margin-right': {
|
||||
'&::before': {
|
||||
width: '100%',
|
||||
},
|
||||
|
||||
'&::after': {
|
||||
width: 0,
|
||||
},
|
||||
|
||||
'.ant-divider-inner-text': {
|
||||
paddingInlineEnd: `${token.dividerNotDefaultTextPadding}px`,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default function useStyle(prefixCls: string): UseComponentStyleResult {
|
||||
const [theme, token, hashId] = useToken();
|
||||
// FIXME
|
||||
const dividerBorderColor = 'rgba(0, 0, 0, 6%)';
|
||||
|
||||
const dividerBorderWidth = token.borderWidth;
|
||||
|
||||
const dividerNotDefaultTextPadding = 0;
|
||||
const dividerVerticalGutterMargin = token.marginXS;
|
||||
const dividerHorizontalWithTextGutterMargin = token.margin;
|
||||
const dividerHorizontalGutterMargin = token.marginLG;
|
||||
|
||||
const dividerToken: DividerToken = {
|
||||
...token,
|
||||
|
||||
dividerCls: `.${prefixCls}`,
|
||||
|
||||
dividerBorderColor,
|
||||
|
||||
dividerBorderWidth,
|
||||
|
||||
dividerNotDefaultTextPadding,
|
||||
dividerVerticalGutterMargin,
|
||||
dividerHorizontalWithTextGutterMargin,
|
||||
dividerHorizontalGutterMargin,
|
||||
};
|
||||
|
||||
return [
|
||||
useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => [
|
||||
genSharedDividerStyle(dividerToken),
|
||||
]),
|
||||
hashId,
|
||||
];
|
||||
}
|
||||
|
@ -134,8 +134,8 @@
|
||||
margin: 0;
|
||||
color: @heading-color;
|
||||
font-weight: 500;
|
||||
font-size: @font-size-lg;
|
||||
line-height: 22px;
|
||||
font-size: @drawer-title-font-size;
|
||||
line-height: @drawer-title-line-height;
|
||||
}
|
||||
|
||||
&-content {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,7 @@ Array [
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
bottomCenter
|
||||
bottom
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
@ -40,7 +40,61 @@ Array [
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
topCenter
|
||||
top
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
class="ant-btn ant-btn-default ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
topRight
|
||||
</span>
|
||||
</button>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/dropdown/demo/arrow-center.md correctly 1`] = `
|
||||
Array [
|
||||
<button
|
||||
class="ant-btn ant-btn-default ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
bottomLeft
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
class="ant-btn ant-btn-default ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
bottom
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
class="ant-btn ant-btn-default ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
bottomRight
|
||||
</span>
|
||||
</button>,
|
||||
<br />,
|
||||
<button
|
||||
class="ant-btn ant-btn-default ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
topLeft
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
class="ant-btn ant-btn-default ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
top
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
@ -658,7 +712,7 @@ exports[`renders ./components/dropdown/demo/placement.md correctly 1`] = `
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
bottomCenter
|
||||
bottom
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
@ -706,7 +760,7 @@ exports[`renders ./components/dropdown/demo/placement.md correctly 1`] = `
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
topCenter
|
||||
top
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user