mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-24 02:59:58 +08:00
Merge branch 'master' of https://github.com/ant-design/ant-design
This commit is contained in:
commit
6900322a22
@ -1,6 +1,6 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import type { MenuProps } from 'antd';
|
||||
import { Tag, version } from 'antd';
|
||||
import { Space, Tag, version } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import classnames from 'classnames';
|
||||
import { useFullSidebarData, useSidebarData } from 'dumi';
|
||||
@ -22,7 +22,6 @@ const useStyle = createStyles(({ css, token }) => ({
|
||||
margin-inline-end: 0;
|
||||
`,
|
||||
subtitle: css`
|
||||
margin-inline-start: ${token.marginXS}px;
|
||||
font-weight: normal;
|
||||
font-size: ${token.fontSizeSM}px;
|
||||
opacity: 0.8;
|
||||
@ -46,10 +45,10 @@ const MenuItemLabelWithTag: React.FC<MenuItemLabelProps> = (props) => {
|
||||
if (!before && !after) {
|
||||
return (
|
||||
<Link to={`${link}${search}`} className={classnames(className, { [styles.link]: tag })}>
|
||||
<span>
|
||||
{title}
|
||||
<Space>
|
||||
<span>{title}</span>
|
||||
{subtitle && <span className={styles.subtitle}>{subtitle}</span>}
|
||||
</span>
|
||||
</Space>
|
||||
{tag && (
|
||||
<Tag
|
||||
bordered={false}
|
||||
|
@ -5,7 +5,7 @@ import type { AnchorLinkItemProps } from 'antd/es/anchor/Anchor';
|
||||
import classNames from 'classnames';
|
||||
import { useRouteMeta, useTabMeta } from 'dumi';
|
||||
|
||||
const useStyle = createStyles(({ token, css }) => {
|
||||
export const useStyle = createStyles(({ token, css }) => {
|
||||
const { antCls } = token;
|
||||
return {
|
||||
anchorToc: css`
|
||||
@ -19,13 +19,13 @@ const useStyle = createStyles(({ token, css }) => {
|
||||
`,
|
||||
tocWrapper: css`
|
||||
position: fixed;
|
||||
top: ${token.headerHeight + token.contentMarginTop - 8}px;
|
||||
top: ${token.headerHeight + token.contentMarginTop - 4}px;
|
||||
inset-inline-end: 0;
|
||||
width: 160px;
|
||||
padding: ${token.paddingXS}px;
|
||||
width: 148px;
|
||||
padding: 0;
|
||||
border-radius: ${token.borderRadius}px;
|
||||
box-sizing: border-box;
|
||||
margin-inline-end: calc(16px - 100vw + 100%);
|
||||
margin-inline-end: calc(8px - 100vw + 100%);
|
||||
z-index: 10;
|
||||
.toc-debug {
|
||||
color: ${token.purple6};
|
||||
@ -48,15 +48,11 @@ const useStyle = createStyles(({ token, css }) => {
|
||||
}
|
||||
`,
|
||||
articleWrapper: css`
|
||||
padding: 0 170px 32px 64px;
|
||||
|
||||
&.rtl {
|
||||
padding: 0 64px 144px 170px;
|
||||
}
|
||||
padding-inline: 48px 164px;
|
||||
padding-block: 0 32px;
|
||||
|
||||
@media only screen and (max-width: ${token.screenLG}px) {
|
||||
&,
|
||||
&.rtl {
|
||||
& {
|
||||
padding: 0 ${token.paddingLG * 2}px;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { useContext, useLayoutEffect, useMemo, useState } from 'react';
|
||||
import { Col, Flex, Space, Typography } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import classNames from 'classnames';
|
||||
import { FormattedMessage, useRouteMeta } from 'dumi';
|
||||
|
||||
@ -11,6 +10,7 @@ import type { DemoContextProps } from '../DemoContext';
|
||||
import DemoContext from '../DemoContext';
|
||||
import SiteContext from '../SiteContext';
|
||||
import InViewSuspense from './InViewSuspense';
|
||||
import { useStyle } from './DocAnchor';
|
||||
|
||||
const Contributors = React.lazy(() => import('./Contributors'));
|
||||
const ColumnCard = React.lazy(() => import('./ColumnCard'));
|
||||
@ -21,21 +21,6 @@ const PrevAndNext = React.lazy(() => import('../../common/PrevAndNext'));
|
||||
const ComponentChangelog = React.lazy(() => import('../../common/ComponentChangelog'));
|
||||
const EditButton = React.lazy(() => import('../../common/EditButton'));
|
||||
|
||||
const useStyle = createStyles(({ token, css }) => ({
|
||||
articleWrapper: css`
|
||||
padding: 0 170px 32px 64px;
|
||||
&.rtl {
|
||||
padding: 0 64px 144px 170px;
|
||||
}
|
||||
@media only screen and (max-width: ${token.screenLG}px) {
|
||||
&,
|
||||
&.rtl {
|
||||
padding: 0 ${token.paddingLG * 2}px;
|
||||
}
|
||||
}
|
||||
`,
|
||||
}));
|
||||
|
||||
const Content: React.FC<React.PropsWithChildren> = ({ children }) => {
|
||||
const meta = useRouteMeta();
|
||||
const { pathname, hash } = useLocation();
|
||||
@ -107,9 +92,7 @@ const Content: React.FC<React.PropsWithChildren> = ({ children }) => {
|
||||
version={meta.frontmatter.tag}
|
||||
/>
|
||||
)}
|
||||
<div style={{ minHeight: 'calc(100vh - 64px)', width: 'calc(100% - 10px)' }}>
|
||||
{children}
|
||||
</div>
|
||||
<div style={{ minHeight: 'calc(100vh - 64px)' }}>{children}</div>
|
||||
<InViewSuspense>
|
||||
<ColumnCard
|
||||
zhihuLink={meta.frontmatter.zhihu_url}
|
||||
|
@ -53,12 +53,7 @@ const useStyle = createStyles(({ token, css }) => {
|
||||
> ${antCls}-menu-item-group
|
||||
> ${antCls}-menu-item-group-list
|
||||
> ${antCls}-menu-item {
|
||||
padding-inline-start: 40px !important;
|
||||
|
||||
${antCls}-row-rtl & {
|
||||
padding-inline-end: 40px !important;
|
||||
padding-inline-start: ${token.padding}px !important;
|
||||
}
|
||||
padding-inline: 36px 12px !important;
|
||||
}
|
||||
|
||||
// Nest Category > Type > Article
|
||||
@ -96,8 +91,6 @@ const useStyle = createStyles(({ token, css }) => {
|
||||
`,
|
||||
mainMenu: css`
|
||||
z-index: 1;
|
||||
|
||||
.main-menu-inner {
|
||||
position: sticky;
|
||||
top: ${token.headerHeight + token.contentMarginTop}px;
|
||||
width: 100%;
|
||||
@ -106,9 +99,12 @@ const useStyle = createStyles(({ token, css }) => {
|
||||
overflow: hidden;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: unset;
|
||||
|
||||
.ant-menu {
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
&:hover .main-menu-inner {
|
||||
&:hover {
|
||||
overflow-y: auto;
|
||||
}
|
||||
`,
|
||||
@ -144,7 +140,7 @@ const Sidebar: React.FC = () => {
|
||||
<MobileMenu key="Mobile-menu">{menuChild}</MobileMenu>
|
||||
) : (
|
||||
<Col xxl={4} xl={5} lg={6} md={6} sm={24} xs={24} className={styles.mainMenu}>
|
||||
<section className="main-menu-inner">{menuChild}</section>
|
||||
{menuChild}
|
||||
</Col>
|
||||
);
|
||||
};
|
||||
|
@ -23,6 +23,7 @@ export default defineConfig({
|
||||
mfsu: false,
|
||||
mako: {},
|
||||
crossorigin: {},
|
||||
runtimePublicPath: {},
|
||||
outputPath: '_site',
|
||||
favicons: ['https://gw.alipayobjects.com/zos/rmsportal/rlpTLlbMzTNYuZGGCVYM.png'],
|
||||
resolve: {
|
||||
|
6
.github/workflows/mock-project-build.yml
vendored
6
.github/workflows/mock-project-build.yml
vendored
@ -20,9 +20,7 @@ jobs:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
@ -54,7 +52,7 @@ jobs:
|
||||
|
||||
- name: 🎨 Diff Report
|
||||
if: ${{ failure() }}
|
||||
run: npx diff-yarn-lock --source=~tmpProj/yarn.lock --target=~tmpProj/yarn.lock.failed
|
||||
run: bunx diff-yarn-lock --source=~tmpProj/yarn.lock --target=~tmpProj/yarn.lock.failed
|
||||
|
||||
- uses: actions-cool/ci-notice@v1
|
||||
if: ${{ failure() }}
|
||||
|
12
.github/workflows/pkg.pr.new.yml
vendored
12
.github/workflows/pkg.pr.new.yml
vendored
@ -10,15 +10,13 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
run: bun install
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
run: bun run build
|
||||
|
||||
# ========== Prepare examples ==========
|
||||
- name: Clear examples
|
||||
@ -31,6 +29,6 @@ jobs:
|
||||
path: examples
|
||||
|
||||
- name: Modify examples
|
||||
run: npx tsx scripts/prepare-examples.ts
|
||||
run: bunx tsx scripts/prepare-examples.ts
|
||||
|
||||
- run: npx pkg-pr-new publish --template './examples/examples/*'
|
||||
- run: bunx pkg-pr-new publish --template './examples/examples/*'
|
||||
|
2
.github/workflows/pr-contributor-welcome.yml
vendored
2
.github/workflows/pr-contributor-welcome.yml
vendored
@ -36,7 +36,7 @@ jobs:
|
||||
|
||||
🎉 感谢您的贡献!如果您还没有加入钉钉社区群,请扫描下方二维码加入我们(加群时请提供此 PR 链接)。
|
||||
|
||||
<img src="https://github.com/ant-design/ant-design/assets/5378891/e24c6080-bf38-4523-b1cd-f6c43ad7375f" height="200" />
|
||||
<img src="https://github.com/user-attachments/assets/cfee105e-8731-481f-a336-92b79a84d35a" height="200" />
|
||||
|
||||
<!-- WELCOME_CONTRIBUTION -->
|
||||
body-include: '<!-- WELCOME_CONTRIBUTION -->'
|
||||
|
90
.github/workflows/preview-build.yml
vendored
90
.github/workflows/preview-build.yml
vendored
@ -15,73 +15,26 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# Prepare node modules. Reuse cache if available
|
||||
setup:
|
||||
name: prepare preview
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: cache package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: create package-lock.json
|
||||
run: npm i --package-lock-only --ignore-scripts
|
||||
|
||||
- name: hack for single file
|
||||
run: |
|
||||
if [ ! -d "package-temp-dir" ]; then
|
||||
mkdir package-temp-dir
|
||||
fi
|
||||
cp package-lock.json package-temp-dir
|
||||
- name: cache node_modules
|
||||
id: node_modules_cache_id
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: install
|
||||
if: steps.node_modules_cache_id.outputs.cache-hit != 'true'
|
||||
run: npm ci
|
||||
|
||||
build-site:
|
||||
name: build preview
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: npm run site
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- run: bun install
|
||||
- name: bun run site
|
||||
id: site
|
||||
run: npm run site
|
||||
run: bun run site
|
||||
env:
|
||||
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||
|
||||
- name: run e2e test
|
||||
run: bun run test:site
|
||||
- name: upload site artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: site
|
||||
path: _site/
|
||||
retention-days: 5
|
||||
|
||||
# Upload PR id for next workflow use
|
||||
- name: Save PR number
|
||||
if: ${{ always() }}
|
||||
@ -93,32 +46,3 @@ jobs:
|
||||
with:
|
||||
name: pr
|
||||
path: ./pr-id.txt
|
||||
|
||||
site-test:
|
||||
name: site E2E test
|
||||
runs-on: ubuntu-latest
|
||||
needs: [setup, build-site]
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: download site artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: site
|
||||
path: _site
|
||||
|
||||
- name: run e2e test
|
||||
run: npm run test:site
|
||||
|
55
.github/workflows/site-deploy.yml
vendored
55
.github/workflows/site-deploy.yml
vendored
@ -11,69 +11,24 @@ permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
if: (startsWith(github.ref, 'refs/tags/') && (contains(github.ref_name, '-') == false)) || github.event_name == 'workflow_dispatch'
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: cache package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
|
||||
- name: create package-lock.json
|
||||
run: npm i --package-lock-only --ignore-scripts
|
||||
|
||||
- name: hack for single file
|
||||
run: |
|
||||
if [ ! -d "package-temp-dir" ]; then
|
||||
mkdir package-temp-dir
|
||||
fi
|
||||
cp package-lock.json package-temp-dir
|
||||
- name: cache node_modules
|
||||
id: node_modules_cache_id
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: install
|
||||
if: steps.node_modules_cache_id.outputs.cache-hit != 'true'
|
||||
run: npm ci
|
||||
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
- run: bun install
|
||||
|
||||
- name: build site
|
||||
run: npm run predeploy
|
||||
run: bun run predeploy
|
||||
env:
|
||||
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||
|
||||
- name: build dist and bundle analyzer report
|
||||
run: npm run dist
|
||||
run: bun run dist
|
||||
env:
|
||||
ANALYZER: 1
|
||||
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||
|
1
.github/workflows/size-limit.yml
vendored
1
.github/workflows/size-limit.yml
vendored
@ -27,5 +27,4 @@ jobs:
|
||||
env:
|
||||
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||
PRODUCTION_ONLY: 1
|
||||
NO_DUP_CHECK: 1
|
||||
CI_JOB_NUMBER: 1
|
||||
|
250
.github/workflows/test.yml
vendored
250
.github/workflows/test.yml
vendored
@ -13,67 +13,13 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: cache package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: create package-lock.json
|
||||
run: npm i --package-lock-only --ignore-scripts
|
||||
|
||||
- name: hack for single file
|
||||
run: |
|
||||
if [ ! -d "package-temp-dir" ]; then
|
||||
mkdir package-temp-dir
|
||||
fi
|
||||
cp package-lock.json package-temp-dir
|
||||
- name: cache node_modules
|
||||
id: node_modules_cache_id
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: install
|
||||
if: steps.node_modules_cache_id.outputs.cache-hit != 'true'
|
||||
run: npm ci
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: lint
|
||||
run: npm run lint
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- run: bun install
|
||||
- run: bun run lint
|
||||
|
||||
################################ Test ################################
|
||||
test-react-legacy:
|
||||
@ -85,68 +31,28 @@ jobs:
|
||||
env:
|
||||
REACT: ${{ matrix.react }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- run: bun install
|
||||
- name: install react 16
|
||||
if: ${{ matrix.react == '16' }}
|
||||
run: npm run install-react-16
|
||||
|
||||
run: bun run bun-install-react-16
|
||||
- name: install react 17
|
||||
if: ${{ matrix.react == '17' }}
|
||||
run: npm run install-react-17
|
||||
|
||||
run: bun run bun-install-react-17
|
||||
# dom test
|
||||
- name: dom test
|
||||
run: npm test -- --maxWorkers=2 --shard=${{matrix.shard}} --coverage
|
||||
run: bun run test -- --maxWorkers=2 --shard=${{matrix.shard}} --coverage
|
||||
|
||||
test-node:
|
||||
name: test-node
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: install react 18
|
||||
run: npm run install-react-18
|
||||
|
||||
- name: node test
|
||||
run: npm run test:node
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- run: bun install
|
||||
- run: bun run test:node
|
||||
|
||||
test-react-latest:
|
||||
name: test-react-latest
|
||||
@ -155,33 +61,14 @@ jobs:
|
||||
module: ['dom']
|
||||
shard: ['1/2', '2/2']
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: install react 18
|
||||
run: npm run install-react-18
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- run: bun install
|
||||
|
||||
# dom test
|
||||
- name: dom test
|
||||
run: npm test -- --maxWorkers=2 --shard=${{matrix.shard}} --coverage
|
||||
run: bun run test -- --maxWorkers=2 --shard=${{matrix.shard}} --coverage
|
||||
|
||||
- name: persist coverages
|
||||
run: |
|
||||
@ -203,27 +90,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: install react 18
|
||||
run: npm run install-react-18
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- run: bun install
|
||||
|
||||
- name: restore cache from dist
|
||||
uses: actions/cache@v4
|
||||
@ -233,13 +102,13 @@ jobs:
|
||||
|
||||
- name: dist-min test
|
||||
if: ${{ matrix.module == 'dist-min' }}
|
||||
run: npm test
|
||||
run: bun run test
|
||||
env:
|
||||
LIB_DIR: dist-min
|
||||
|
||||
- name: dist test
|
||||
if: ${{ matrix.module == 'dist' }}
|
||||
run: npm test
|
||||
run: bun run test
|
||||
env:
|
||||
LIB_DIR: dist
|
||||
|
||||
@ -250,10 +119,8 @@ jobs:
|
||||
needs: test-react-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- run: bun install
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
@ -262,8 +129,8 @@ jobs:
|
||||
path: persist-coverage
|
||||
- name: Merge Code Coverage
|
||||
run: |
|
||||
npx nyc merge persist-coverage/ coverage/coverage-final.json
|
||||
npx nyc report --reporter text -t coverage --report-dir coverage
|
||||
bunx nyc merge persist-coverage/ coverage/coverage-final.json
|
||||
bunx nyc report --reporter text -t coverage --report-dir coverage
|
||||
rm -rf persist-coverage
|
||||
- name: Upload coverage to codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
@ -274,26 +141,10 @@ jobs:
|
||||
########################### Compile & Test ###########################
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- run: bun install
|
||||
|
||||
- name: cache lib
|
||||
uses: actions/cache@v4
|
||||
@ -308,7 +159,7 @@ jobs:
|
||||
key: es-${{ github.sha }}
|
||||
|
||||
- name: compile
|
||||
run: npm run compile
|
||||
run: bun run compile
|
||||
|
||||
- name: cache dist
|
||||
uses: actions/cache@v4
|
||||
@ -317,10 +168,9 @@ jobs:
|
||||
key: dist-${{ github.sha }}
|
||||
|
||||
- name: dist
|
||||
run: npm run dist
|
||||
run: bun run dist
|
||||
env:
|
||||
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
CI: 1
|
||||
|
||||
- name: check build files
|
||||
@ -351,36 +201,14 @@ jobs:
|
||||
test-lib-es:
|
||||
name: test lib/es module
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
strategy:
|
||||
matrix:
|
||||
module: [lib, es]
|
||||
shard: ['1/2', '2/2']
|
||||
steps:
|
||||
- name: checkout
|
||||
# lib only run in master branch not in pull request
|
||||
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
# lib only run in master branch not in pull request
|
||||
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
# lib only run in master branch not in pull request
|
||||
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- run: bun install
|
||||
|
||||
- name: restore cache from ${{ matrix.module }}
|
||||
# lib only run in master branch not in pull request
|
||||
@ -393,15 +221,11 @@ jobs:
|
||||
- name: compile
|
||||
# lib only run in master branch not in pull request
|
||||
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
|
||||
run: npm run compile
|
||||
|
||||
- name: install react 18
|
||||
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
|
||||
run: npm run install-react-18
|
||||
run: bun run compile
|
||||
|
||||
- name: test
|
||||
# lib only run in master branch not in pull request
|
||||
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
|
||||
run: npm test -- --maxWorkers=2 --shard=${{matrix.shard}}
|
||||
run: bun run test -- --maxWorkers=2 --shard=${{matrix.shard}}
|
||||
env:
|
||||
LIB_DIR: ${{ matrix.module }}
|
||||
|
10
.github/workflows/upgrade-deps.yml
vendored
10
.github/workflows/upgrade-deps.yml
vendored
@ -21,10 +21,8 @@ jobs:
|
||||
.ncurc.js
|
||||
package.json
|
||||
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- name: setup bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
|
||||
- name: upgrade deps
|
||||
id: upgrade
|
||||
@ -32,7 +30,7 @@ jobs:
|
||||
if [ ! -d .tmp ] ; then
|
||||
mkdir .tmp
|
||||
fi
|
||||
$(npx npm-check-updates -u > .tmp/upgrade-deps-logs.txt) 2>&1 || true
|
||||
$(bunx npm-check-updates -u > .tmp/upgrade-deps-logs.txt) 2>&1 || true
|
||||
if [ -s .tmp/upgrade-deps-logs.txt ]; then
|
||||
cat .tmp/upgrade-deps-logs.txt
|
||||
echo "logs<<EOF" >> $GITHUB_OUTPUT
|
||||
@ -42,7 +40,7 @@ jobs:
|
||||
|
||||
- name: create pull request
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }} # Cannot be default!!!
|
||||
assignees: 'afc163, zombieJ, xrkffgg, MadCcc'
|
||||
|
@ -16,41 +16,6 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# Prepare node modules. Reuse cache if available
|
||||
setup:
|
||||
name: prepare node_modules
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: cache package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: create package-lock.json
|
||||
run: npm i --package-lock-only --ignore-scripts
|
||||
|
||||
- name: hack for single file
|
||||
run: |
|
||||
if [ ! -d "package-temp-dir" ]; then
|
||||
mkdir package-temp-dir
|
||||
fi
|
||||
cp package-lock.json package-temp-dir
|
||||
|
||||
- name: cache node_modules
|
||||
id: node_modules_cache_id
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: install
|
||||
if: steps.node_modules_cache_id.outputs.cache-hit != 'true'
|
||||
run: npm ci
|
||||
|
||||
############################ Generate Snapshot ###########################
|
||||
visual-diff-snapshot:
|
||||
name: visual-diff snapshot
|
||||
@ -58,33 +23,17 @@ jobs:
|
||||
matrix:
|
||||
shard: ['1/2', '2/2']
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- run: bun install
|
||||
|
||||
- name: generate image snapshots
|
||||
id: test-image
|
||||
run: |
|
||||
node node_modules/puppeteer/install.mjs
|
||||
npm run version
|
||||
npm run test:image -- --shard=${{matrix.shard}}
|
||||
bun run version
|
||||
bun run test:image -- --shard=${{matrix.shard}}
|
||||
env:
|
||||
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||
|
||||
@ -102,22 +51,8 @@ jobs:
|
||||
needs: visual-diff-snapshot
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- run: bun install
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
@ -132,7 +67,7 @@ jobs:
|
||||
EVENT_NUMBER: ${{ github.event.number }}
|
||||
BASE_REF: ${{ github.base_ref }}
|
||||
run: |
|
||||
npm run test:visual-regression -- --pr-id=$EVENT_NUMBER --base-ref=$BASE_REF --max-workers=2
|
||||
bun run test:visual-regression -- --pr-id=$EVENT_NUMBER --base-ref=$BASE_REF --max-workers=2
|
||||
|
||||
# Upload report in `visualRegressionReport`
|
||||
- name: upload report artifact
|
||||
|
@ -63,8 +63,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [upstream-workflow-summary]
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
|
||||
# We need get persist-index first
|
||||
- name: download image snapshot artifact
|
||||
@ -110,7 +110,7 @@ jobs:
|
||||
echo "✅ Uncompress Finished"
|
||||
|
||||
rm package.json
|
||||
npm i ali-oss --no-save
|
||||
bun add ali-oss --no-save
|
||||
echo "✅ Install `ali-oss` Finished"
|
||||
|
||||
echo "🤖 Uploading"
|
||||
|
@ -12,65 +12,18 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# Prepare node modules. Reuse cache if available
|
||||
setup:
|
||||
name: prepare node_modules
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: cache package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: create package-lock.json
|
||||
run: npm i --package-lock-only --ignore-scripts
|
||||
|
||||
- name: hack for single file
|
||||
run: |
|
||||
if [ ! -d "package-temp-dir" ]; then
|
||||
mkdir package-temp-dir
|
||||
fi
|
||||
cp package-lock.json package-temp-dir
|
||||
- name: cache node_modules
|
||||
id: node_modules_cache_id
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- name: install
|
||||
if: steps.node_modules_cache_id.outputs.cache-hit != 'true'
|
||||
run: npm ci
|
||||
|
||||
test-image:
|
||||
name: test image
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: restore cache from package-lock.json
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: package-temp-dir
|
||||
key: lock-${{ github.sha }}
|
||||
|
||||
- name: restore cache from node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- run: bun install
|
||||
- name: generate image snapshots
|
||||
run: |
|
||||
node node_modules/puppeteer/install.mjs
|
||||
npm run version
|
||||
npm run test:image
|
||||
bun run version
|
||||
bun run test:image
|
||||
tar -czvf imageSnapshots.tar.gz imageSnapshots/*
|
||||
env:
|
||||
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"*.{ts,tsx,js,jsx,json,css}": ["biome check --write"],
|
||||
"*.{ts,tsx,js,jsx,css}": ["biome check --write"],
|
||||
"*.{md,yml}": ["prettier --ignore-unknown --write"]
|
||||
}
|
||||
|
@ -56,5 +56,6 @@
|
||||
">= 5.16.0 <= 5.16.1": ["https://github.com/ant-design/ant-design/issues/48200"],
|
||||
"5.16.3": ["https://github.com/ant-design/ant-design/issues/48568"],
|
||||
"5.17.1": ["https://github.com/ant-design/ant-design/issues/48913"],
|
||||
"5.18.2": ["https://github.com/ant-design/ant-design/pull/49487"]
|
||||
"5.18.2": ["https://github.com/ant-design/ant-design/pull/49487"],
|
||||
"5.20.4": ["https://github.com/ant-design/ant-design/issues/50687"]
|
||||
}
|
||||
|
@ -16,6 +16,49 @@ tag: vVERSION
|
||||
|
||||
---
|
||||
|
||||
## 5.20.5
|
||||
|
||||
`2024-09-03`
|
||||
|
||||
- 🛠 Adjust Tree & TreeSelect `defaultExpandAll` logic to only add internal `expandedKeys` which `treeNode` has children instead to avoid perf issue when with large data or `loadData` case. [#50689](https://github.com/ant-design/ant-design/pull/50689) [@zombieJ](https://github.com/zombieJ)
|
||||
- 🐞 Fix Cascader not show parent option in search when using `multiple`. [#50689](https://github.com/ant-design/ant-design/pull/50689)
|
||||
- 🐞 Fix Typography `ellipsis.tooltip.title` with ReactNode will cause dead loop. [#50688](https://github.com/ant-design/ant-design/pull/50688) [@zombieJ](https://github.com/zombieJ)
|
||||
|
||||
## 5.20.4
|
||||
|
||||
`2024-09-02`
|
||||
|
||||
- Menu
|
||||
- 🐞 Fix Menu token `itemPaddingInline inoperative` not working. [#50663](https://github.com/ant-design/ant-design/pull/50663) [@coding-ice](https://github.com/coding-ice)
|
||||
- 🐞 Fix Menu missing `hover` transition style. [#50624](https://github.com/ant-design/ant-design/pull/50624) [@afc163](https://github.com/afc163)
|
||||
- 💄 Badge add transition effect to count node. [#50607](https://github.com/ant-design/ant-design/pull/50607) [@afc163](https://github.com/afc163)
|
||||
- 💄 Fix Table column header move with unexpected transition. [#50605](https://github.com/ant-design/ant-design/pull/50605) [@afc163](https://github.com/afc163)
|
||||
- 🛠 Refactor Typography code to optimize internal logic. [#50561](https://github.com/ant-design/ant-design/pull/50561) [@afc163](https://github.com/afc163)
|
||||
- 🐞 Disable the Rate component within Form.Item when the form is disabled. [#50594](https://github.com/ant-design/ant-design/pull/50594) [@nikzanda](https://github.com/nikzanda)
|
||||
- 🌐 Patch tr_TR `Transfer.deselectAll` locale. [#50672](https://github.com/ant-design/ant-design/pull/50672) [@coding-ice](https://github.com/coding-ice)
|
||||
|
||||
## 5.20.3
|
||||
|
||||
`2024-08-26`
|
||||
|
||||
- 🐞 Refactor Typography native css ellipsis measure logic to handle precision edge case. [#50514](https://github.com/ant-design/ant-design/pull/50514) [@zombieJ](https://github.com/zombieJ)
|
||||
- 🐞 Fix ColorPicker `onChangeComplete` not correct when click directly without move on the picker panel. [#50501](https://github.com/ant-design/ant-design/pull/50501) [@zombieJ](https://github.com/zombieJ)
|
||||
- 🐞 Fix FloatButton.Group with controlled mode warning for nest updating issue. [#50500](https://github.com/ant-design/ant-design/pull/50500) [@zombieJ](https://github.com/zombieJ)
|
||||
|
||||
## 5.20.2
|
||||
|
||||
`2024-08-19`
|
||||
|
||||
- 💄 Fix the suffix style problem of InputNumber without control. [#50450](https://github.com/ant-design/ant-design/pull/50450) [@coding-ice](https://github.com/coding-ice)
|
||||
- 🆕 Form `rule.message` supports skipping variable substitution through `\\${}`. [#50412](https://github.com/ant-design/ant-design/pull/50412) [@zombieJ](https://github.com/zombieJ)
|
||||
- 🐞 Fixed the issue where the rounded corners of the trigger element are missing when the FloatButton component has shape="square" and in menu mode when the menu pops up. [#50408](https://github.com/ant-design/ant-design/pull/50408) [@li-jia-nan](https://github.com/li-jia-nan)
|
||||
- 🐞 Fixed the problem that Upload.Dragger does not work when dragging and dropping upload folders. [#50394](https://github.com/ant-design/ant-design/pull/50394) [@huiliangShen](https://github.com/huiliangShen)
|
||||
- 🐞 Fixed the issue where the arrow icon disappears after hovering when Select specifies `getPopcontainer={node=node.parentNode}`. [#50382](https://github.com/ant-design/ant-design/pull/50382) [@afc163](https://github.com/afc163)
|
||||
- 🐞 Fixed the arrow misalignment error when Popover sets the `arrow.pointAtCenter` property. [#50260](https://github.com/ant-design/ant-design/pull/50260) [@Wxh16144](https://github.com/Wxh16144)
|
||||
- 📖 Transfer adds Russian and Ukrainian localization copy. [#50429](https://github.com/ant-design/ant-design/pull/50429) [@alexlag](https://github.com/alexlag)
|
||||
- TypeScript
|
||||
- 🤖 Roll back the Table partial generic constraint object to any to reduce break changes caused by [#50351](https://github.com/ant-design/ant-design/pull/50351). [#50372](https://github.com/ant-design/ant-design/pull/50372) [@crazyair](https://github.com/crazyair)
|
||||
|
||||
## 5.20.1
|
||||
|
||||
`2024-08-11`
|
||||
|
@ -15,6 +15,49 @@ tag: vVERSION
|
||||
|
||||
---
|
||||
|
||||
## 5.20.5
|
||||
|
||||
`2024-09-03`
|
||||
|
||||
- 🛠 调整 Tree 与 TreeSelect 的 `defaultExpandAll` 的行为,仅将有子节点的 `treeNode` 加入 `expandedKeys` 以防止在大数据与 `loadData` 异步的情况下引发的性能问题。[#50689](https://github.com/ant-design/ant-design/pull/50689) [@zombieJ](https://github.com/zombieJ)
|
||||
- 🐞 修复 Cascader 在 `multiple` 下搜索不会显示父节点作为选项的问题。[#50689](https://github.com/ant-design/ant-design/pull/50689)
|
||||
- 🐞 修复 Typography `ellipsis.tooltip.title` 配置 ReactNode 会导致死循环的问题。[#50688](https://github.com/ant-design/ant-design/pull/50688) [@zombieJ](https://github.com/zombieJ)
|
||||
|
||||
## 5.20.4
|
||||
|
||||
`2024-09-02`
|
||||
|
||||
- Menu
|
||||
- 🐞 修复 Menu 的 `itemPaddingInline` token 不生效的问题。[#50663](https://github.com/ant-design/ant-design/pull/50663) [@coding-ice](https://github.com/coding-ice)
|
||||
- 🐞 修复 Menu `hover` 时背景色切换渐变效果丢失的问题。[#50624](https://github.com/ant-design/ant-design/pull/50624) [@afc163](https://github.com/afc163)
|
||||
- 💄 给 Badge 增加一个动画缓动效果。[#50607](https://github.com/ant-design/ant-design/pull/50607) [@afc163](https://github.com/afc163)
|
||||
- 💄 修复 Table 列头切换状态时多余的的移动缓动动画。[#50605](https://github.com/ant-design/ant-design/pull/50605) [@afc163](https://github.com/afc163)
|
||||
- 🛠 重构 Typography 代码以优化内部实现逻辑。[#50561](https://github.com/ant-design/ant-design/pull/50561) [@afc163](https://github.com/afc163)
|
||||
- 🐞 当表单被禁用时,禁用 Form.Item 中的 Rate 组件。[#50594](https://github.com/ant-design/ant-design/pull/50594) [@nikzanda](https://github.com/nikzanda)
|
||||
- 🌐 补充土耳其 `Transfer.deselectAll` 本地化文本。[#50672](https://github.com/ant-design/ant-design/pull/50672) [@coding-ice](https://github.com/coding-ice)
|
||||
|
||||
## 5.20.3
|
||||
|
||||
`2024-08-26`
|
||||
|
||||
- 🐞 重构 Typography 在使用 css 原生省略时的检查逻辑,以解决屏幕缩放等情况下的精度问题。[#50514](https://github.com/ant-design/ant-design/pull/50514) [@zombieJ](https://github.com/zombieJ)
|
||||
- 🐞 修复 ColorPicker 组件在面板上不拖拽直接点击的时候,`onChangeComplete` 返回值不正确的问题。[#50501](https://github.com/ant-design/ant-design/pull/50501) [@zombieJ](https://github.com/zombieJ)
|
||||
- 🐞 修复 FloatButton.Group 在受控模式下 React 会警告递归更新的问题。[#50500](https://github.com/ant-design/ant-design/pull/50500) [@zombieJ](https://github.com/zombieJ)
|
||||
|
||||
## 5.20.2
|
||||
|
||||
`2024-08-19`
|
||||
|
||||
- 💄 修复 InputNumber 没有控件的后缀样式问题。[#50450](https://github.com/ant-design/ant-design/pull/50450) [@coding-ice](https://github.com/coding-ice)
|
||||
- 🆕 Form `rule.message` 支持通过 `\\${}` 跳过变量替换。[#50412](https://github.com/ant-design/ant-design/pull/50412) [@zombieJ](https://github.com/zombieJ)
|
||||
- 🐞 修复了 FloatButton 组件当 shape=“square” 时,并且在菜单模式下,菜单弹出时 trigger 元素圆角缺失的问题。[#50408](https://github.com/ant-design/ant-design/pull/50408) [@li-jia-nan](https://github.com/li-jia-nan)
|
||||
- 🐞 修复 Upload.Dragger 拖拽上传文件夹时不工作问题。[#50394](https://github.com/ant-design/ant-design/pull/50394) [@huiliangShen](https://github.com/huiliangShen)
|
||||
- 🐞 修复 Select 指定 `getPopcontainer={node=node.parentNode}` 时箭头图标 hover 后会消失的问题。[#50382](https://github.com/ant-design/ant-design/pull/50382) [@afc163](https://github.com/afc163)
|
||||
- 🐞 修复 Popover 设置 `arrow.pointAtCenter` 属性时箭头未对齐错误。[#50260](https://github.com/ant-design/ant-design/pull/50260) [@Wxh16144](https://github.com/Wxh16144)
|
||||
- 📖 Transfer 补充俄罗斯语和乌克兰语本地化文案。[#50429](https://github.com/ant-design/ant-design/pull/50429) [@alexlag](https://github.com/alexlag)
|
||||
- TypeScript
|
||||
- 🤖 将 Table 部分泛型约束 object 回滚为 any,以减少 [#50351](https://github.com/ant-design/ant-design/pull/50351) 造成的 break change。[#50372](https://github.com/ant-design/ant-design/pull/50372) [@crazyair](https://github.com/crazyair)
|
||||
|
||||
## 5.20.1
|
||||
|
||||
`2024-08-11`
|
||||
|
@ -57,6 +57,8 @@
|
||||
|
||||
## 🖥 兼容环境
|
||||
|
||||
支持范围:https://browsersl.ist/#q=defaults
|
||||
|
||||
- 现代浏览器。
|
||||
- 支持服务端渲染。
|
||||
- [Electron](https://www.electronjs.org/)
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"files": {
|
||||
"ignoreUnknown": true,
|
||||
"ignore": [
|
||||
".dumi/tmp*",
|
||||
".dumi/scripts/clarity.js",
|
||||
@ -9,7 +10,8 @@
|
||||
"_site/**/*",
|
||||
"node_modules",
|
||||
"server",
|
||||
"scripts/previewEditor/**/*"
|
||||
"scripts/previewEditor/**/*",
|
||||
"package.json"
|
||||
]
|
||||
},
|
||||
"formatter": {
|
||||
|
6
codecov.yml
Normal file
6
codecov.yml
Normal file
@ -0,0 +1,6 @@
|
||||
coverage:
|
||||
status:
|
||||
project: #add everything under here, more options at https://docs.codecov.com/docs/commit-status
|
||||
default:
|
||||
target: 100%
|
||||
threshold: 0%
|
@ -6,10 +6,12 @@ import pickAttrs from 'rc-util/lib/pickAttrs';
|
||||
export type BaseClosableType = { closeIcon?: React.ReactNode } & React.AriaAttributes;
|
||||
export type ClosableType = boolean | BaseClosableType;
|
||||
|
||||
export type ContextClosable<T extends { closable?: ClosableType; closeIcon?: ReactNode } = any> =
|
||||
Partial<Pick<T, 'closable' | 'closeIcon'>>;
|
||||
export type BaseContextClosable = { closable?: ClosableType; closeIcon?: ReactNode };
|
||||
export type ContextClosable<T extends BaseContextClosable = any> = Partial<
|
||||
Pick<T, 'closable' | 'closeIcon'>
|
||||
>;
|
||||
|
||||
export function pickClosable<T extends { closable?: ClosableType; closeIcon?: ReactNode }>(
|
||||
export function pickClosable<T extends BaseContextClosable>(
|
||||
context?: ContextClosable<T>,
|
||||
): ContextClosable<T> | undefined {
|
||||
if (!context) {
|
||||
|
@ -344,6 +344,7 @@ const genSharedBadgeStyle: GenerateStyle<BadgeToken> = (token) => {
|
||||
},
|
||||
[numberPrefixCls]: {
|
||||
overflow: 'hidden',
|
||||
transition: `all ${token.motionDurationMid} ${token.motionEaseOutBack}`,
|
||||
[`${numberPrefixCls}-only`]: {
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
|
@ -2343,7 +2343,7 @@ exports[`renders components/button/demo/linear-gradient.tsx extend context corre
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-lg css-ykstnd"
|
||||
class="ant-btn ant-btn-primary ant-btn-lg acss-9mi5l3"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
@ -2378,7 +2378,7 @@ exports[`renders components/button/demo/linear-gradient.tsx extend context corre
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-default ant-btn-lg css-ykstnd"
|
||||
class="ant-btn ant-btn-default ant-btn-lg acss-9mi5l3"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
|
@ -1973,7 +1973,7 @@ exports[`renders components/button/demo/linear-gradient.tsx correctly 1`] = `
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-lg css-ykstnd"
|
||||
class="ant-btn ant-btn-primary ant-btn-lg acss-9mi5l3"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
@ -2008,7 +2008,7 @@ exports[`renders components/button/demo/linear-gradient.tsx correctly 1`] = `
|
||||
class="ant-space-item"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-default ant-btn-lg css-ykstnd"
|
||||
class="ant-btn ant-btn-default ant-btn-lg acss-9mi5l3"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
|
@ -1,13 +1,11 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { Button, ConfigProvider, Space } from 'antd';
|
||||
import React from 'react';
|
||||
import { AntDesignOutlined } from '@ant-design/icons';
|
||||
import { css } from '@emotion/css';
|
||||
import { Button, ConfigProvider, Space } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
|
||||
const rootPrefixCls = getPrefixCls();
|
||||
const linearGradientButton = css`
|
||||
&.${rootPrefixCls}-btn-primary:not([disabled]):not(.${rootPrefixCls}-btn-dangerous) {
|
||||
const useStyle = createStyles(({ prefixCls, css }) => ({
|
||||
linearGradientButton: css`
|
||||
&.${prefixCls}-btn-primary:not([disabled]):not(.${prefixCls}-btn-dangerous) {
|
||||
border-width: 0;
|
||||
|
||||
> span {
|
||||
@ -16,7 +14,7 @@ const App: React.FC = () => {
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
background: linear-gradient(135deg, #6253E1, #04BEFE);
|
||||
background: linear-gradient(135deg, #6253e1, #04befe);
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 1;
|
||||
@ -28,11 +26,16 @@ const App: React.FC = () => {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
`,
|
||||
}));
|
||||
|
||||
const App: React.FC = () => {
|
||||
const { styles } = useStyle();
|
||||
|
||||
return (
|
||||
<ConfigProvider
|
||||
button={{
|
||||
className: linearGradientButton,
|
||||
className: styles.linearGradientButton,
|
||||
}}
|
||||
>
|
||||
<Space>
|
||||
|
@ -51,7 +51,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| allowClear | Show clear button | boolean \| { clearIcon?: ReactNode } | true | 5.8.0: Support object type |
|
||||
| autoClearSearchValue | Whether the current search will be cleared on selecting an item. Only applies when `multiple` is `true` | boolean | true | 5.9.0 |
|
||||
| autoFocus | If get focus when component mounted | boolean | false | |
|
||||
| changeOnSelect | (Work on single select) Change value on each selection if set to true, see above demo for details | boolean | false | |
|
||||
| changeOnSelect | 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 | |
|
||||
|
@ -52,7 +52,7 @@ demo:
|
||||
| allowClear | 支持清除 | boolean \| { clearIcon?: ReactNode } | true | 5.8.0: 支持对象形式 |
|
||||
| autoClearSearchValue | 是否在选中项后清空搜索框,只在 `multiple` 为 `true` 时有效 | boolean | true | 5.9.0 |
|
||||
| autoFocus | 自动获取焦点 | boolean | false | |
|
||||
| changeOnSelect | (单选时生效)当此项为 true 时,点选每级菜单选项值都会发生变化,具体见上面的演示 | boolean | false | |
|
||||
| changeOnSelect | 单选时生效(multiple 下始终都可以选择),点选每级菜单选项值都会发生变化。 | boolean | false | |
|
||||
| className | 自定义类名 | string | - | |
|
||||
| defaultValue | 默认的选中项 | string\[] \| number\[] | \[] | |
|
||||
| disabled | 禁用 | boolean | false | |
|
||||
|
@ -34,12 +34,15 @@ function doMouseMove(
|
||||
});
|
||||
|
||||
fireEvent(ele, mouseDown);
|
||||
|
||||
// Drag
|
||||
if (start !== end) {
|
||||
const mouseMove: any = new Event('mousemove');
|
||||
mouseMove.pageX = end;
|
||||
mouseMove.pageY = end;
|
||||
|
||||
fireEvent(document, mouseMove);
|
||||
}
|
||||
|
||||
const mouseUp = createEvent.mouseUp(document);
|
||||
fireEvent(document, mouseUp);
|
||||
@ -848,4 +851,32 @@ describe('ColorPicker', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('onChangeComplete with default empty color should not be alpha', async () => {
|
||||
const spyRect = spyElementPrototypes(HTMLElement, {
|
||||
getBoundingClientRect: () => ({
|
||||
x: 0,
|
||||
y: 100,
|
||||
width: 100,
|
||||
height: 100,
|
||||
}),
|
||||
});
|
||||
|
||||
const handleChangeComplete = jest.fn();
|
||||
const { container } = render(<ColorPicker open onChangeComplete={handleChangeComplete} />);
|
||||
|
||||
// Move
|
||||
doMouseMove(container, 50, 50);
|
||||
expect(handleChangeComplete).toHaveBeenCalledTimes(1);
|
||||
|
||||
const color = handleChangeComplete.mock.calls[0][0];
|
||||
expect(color.toRgb()).toEqual({
|
||||
r: 255,
|
||||
g: 128,
|
||||
b: 128,
|
||||
a: 1,
|
||||
});
|
||||
|
||||
spyRect.mockRestore();
|
||||
});
|
||||
});
|
||||
|
@ -17,6 +17,11 @@ const components = {
|
||||
slider: ColorSlider,
|
||||
};
|
||||
|
||||
type Info = {
|
||||
type?: 'hue' | 'alpha';
|
||||
value?: number;
|
||||
};
|
||||
|
||||
const PanelPicker: FC = () => {
|
||||
const panelPickerContext = useContext(PanelPickerContext);
|
||||
|
||||
@ -81,32 +86,10 @@ const PanelPicker: FC = () => {
|
||||
}, [value, activeIndex, isSingle, lockedColor, gradientDragging]);
|
||||
|
||||
// ============================ Change ============================
|
||||
const fillColor = (nextColor: AggregationColor) => {
|
||||
if (mode === 'single') {
|
||||
return nextColor;
|
||||
}
|
||||
|
||||
const nextColors = [...colors];
|
||||
nextColors[activeIndex] = {
|
||||
...nextColors[activeIndex],
|
||||
color: nextColor,
|
||||
};
|
||||
|
||||
return new AggregationColor(nextColors);
|
||||
};
|
||||
|
||||
const onInternalChange = (
|
||||
colorValue: AggregationColor | Color,
|
||||
fromPicker?: boolean,
|
||||
info?: {
|
||||
type?: 'hue' | 'alpha';
|
||||
value?: number;
|
||||
},
|
||||
) => {
|
||||
const nextColor = generateColor(colorValue);
|
||||
|
||||
let submitColor = nextColor;
|
||||
const fillColor = (nextColor: AggregationColor | Color, info?: Info) => {
|
||||
let submitColor = generateColor(nextColor);
|
||||
|
||||
// Fill alpha color to 100% if origin is cleared color
|
||||
if (value.cleared) {
|
||||
const rgb = submitColor.toRgb();
|
||||
|
||||
@ -125,11 +108,29 @@ const PanelPicker: FC = () => {
|
||||
}
|
||||
}
|
||||
|
||||
onChange(fillColor(submitColor), fromPicker);
|
||||
if (mode === 'single') {
|
||||
return submitColor;
|
||||
}
|
||||
|
||||
const nextColors = [...colors];
|
||||
nextColors[activeIndex] = {
|
||||
...nextColors[activeIndex],
|
||||
color: submitColor,
|
||||
};
|
||||
|
||||
const onInternalChangeComplete = (nextColor: AggregationColor) => {
|
||||
onChangeComplete(fillColor(nextColor));
|
||||
return new AggregationColor(nextColors);
|
||||
};
|
||||
|
||||
const onInternalChange = (
|
||||
colorValue: AggregationColor | Color,
|
||||
fromPicker?: boolean,
|
||||
info?: Info,
|
||||
) => {
|
||||
onChange(fillColor(colorValue, info), fromPicker);
|
||||
};
|
||||
|
||||
const onInternalChangeComplete = (nextColor: Color, info?: Info) => {
|
||||
onChangeComplete(fillColor(nextColor, info));
|
||||
};
|
||||
|
||||
// ============================ Render ============================
|
||||
@ -170,8 +171,8 @@ const PanelPicker: FC = () => {
|
||||
onChange={(colorValue, info) => {
|
||||
onInternalChange(colorValue, true, info);
|
||||
}}
|
||||
onChangeComplete={(colorValue) => {
|
||||
onInternalChangeComplete(generateColor(colorValue));
|
||||
onChangeComplete={(colorValue, info) => {
|
||||
onInternalChangeComplete(colorValue, info);
|
||||
}}
|
||||
components={components}
|
||||
/>
|
||||
|
@ -60,7 +60,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| size | Setting the trigger size | `large` \| `middle` \| `small` | `middle` | 5.7.0 |
|
||||
| trigger | ColorPicker trigger mode | `hover` \| `click` | `click` | |
|
||||
| value | Value of color | string \| `Color` | - | |
|
||||
| onChange | Callback when `value` is changed | `(value: Color, hex: string) => void` | - | |
|
||||
| onChange | Callback when `value` is changed | `(value: Color, css: string) => void` | - | |
|
||||
| onChangeComplete | Called when color pick ends | `(value: Color) => void` | - | 5.7.0 |
|
||||
| onFormatChange | Callback when `format` is changed | `(format: 'hex' \| 'rgb' \| 'hsb') => void` | - | |
|
||||
| onOpenChange | Callback when `open` is changed | `(open: boolean) => void` | - | |
|
||||
|
@ -61,7 +61,7 @@ group:
|
||||
| size | 设置触发器大小 | `large` \| `middle` \| `small` | `middle` | 5.7.0 |
|
||||
| trigger | 颜色选择器的触发模式 | `hover` \| `click` | `click` | |
|
||||
| value | 颜色的值 | string \| `Color` | - | |
|
||||
| onChange | 颜色变化的回调 | `(value: Color, hex: string) => void` | - | |
|
||||
| onChange | 颜色变化的回调 | `(value: Color, css: string) => void` | - | |
|
||||
| onChangeComplete | 颜色选择完成的回调 | `(value: Color) => void` | - | 5.7.0 |
|
||||
| onFormatChange | 颜色格式变化的回调 | `(format: 'hex' \| 'rgb' \| 'hsb') => void` | - | |
|
||||
| onOpenChange | 当 `open` 被改变时的回调 | `(open: boolean) => void` | - | |
|
||||
|
@ -85,7 +85,7 @@ export type ColorPickerProps = Omit<
|
||||
[key: `data-${string}`]: string;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
onFormatChange?: (format?: ColorFormatType) => void;
|
||||
onChange?: (value: AggregationColor, hex: string) => void;
|
||||
onChange?: (value: AggregationColor, css: string) => void;
|
||||
onClear?: () => void;
|
||||
onChangeComplete?: (value: AggregationColor) => void;
|
||||
} & Pick<PopoverProps, 'getPopupContainer' | 'autoAdjustOverflow' | 'destroyTooltipOnHide'>;
|
||||
|
@ -22680,9 +22680,9 @@ exports[`ConfigProvider components Rate configProvider 1`] = `
|
||||
|
||||
exports[`ConfigProvider components Rate configProvider componentDisabled 1`] = `
|
||||
<ul
|
||||
class="config-rate"
|
||||
class="config-rate config-rate-disabled"
|
||||
role="radiogroup"
|
||||
tabindex="0"
|
||||
tabindex="-1"
|
||||
>
|
||||
<li
|
||||
class="config-rate-star config-rate-star-zero"
|
||||
@ -22692,7 +22692,7 @@ exports[`ConfigProvider components Rate configProvider componentDisabled 1`] = `
|
||||
aria-posinset="1"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="config-rate-star-first"
|
||||
@ -22750,7 +22750,7 @@ exports[`ConfigProvider components Rate configProvider componentDisabled 1`] = `
|
||||
aria-posinset="2"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="config-rate-star-first"
|
||||
@ -22808,7 +22808,7 @@ exports[`ConfigProvider components Rate configProvider componentDisabled 1`] = `
|
||||
aria-posinset="3"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="config-rate-star-first"
|
||||
@ -22866,7 +22866,7 @@ exports[`ConfigProvider components Rate configProvider componentDisabled 1`] = `
|
||||
aria-posinset="4"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="config-rate-star-first"
|
||||
@ -22924,7 +22924,7 @@ exports[`ConfigProvider components Rate configProvider componentDisabled 1`] = `
|
||||
aria-posinset="5"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="config-rate-star-first"
|
||||
|
@ -45,8 +45,8 @@ A Drawer is a panel that is typically overlaid on top of a page and slides in fr
|
||||
Common props ref:[Common props](/docs/react/common-props)
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
:::info{title=注意}
|
||||
v5 use `rootClassName` & `rootStyle` to config wrapper style instead of `className` & `style` in v4 to align the API with Modal.
|
||||
:::info{title=Note}
|
||||
v5 uses `rootClassName` & `rootStyle` to configure the outermost element style, instead of `className` & `style` from v4. This is done to align the API with Modal.
|
||||
:::
|
||||
|
||||
| Props | Description | Type | Default | Version |
|
||||
|
@ -16,7 +16,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*obM7S5lIxeMAAA
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/basic.tsx">Basic</code>
|
||||
<code src="./demo/simple.tsx">Chose image</code>
|
||||
<code src="./demo/simple.tsx">Choose image</code>
|
||||
<code src="./demo/customize.tsx">Customize</code>
|
||||
<code src="./demo/config-provider.tsx">ConfigProvider</code>
|
||||
<code src="./demo/description.tsx">No description</code>
|
||||
|
@ -1,8 +1,9 @@
|
||||
import React, { memo, useCallback, useContext, useEffect } from 'react';
|
||||
import React, { memo, useContext } from 'react';
|
||||
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
||||
import FileTextOutlined from '@ant-design/icons/FileTextOutlined';
|
||||
import classNames from 'classnames';
|
||||
import CSSMotion from 'rc-motion';
|
||||
import { useEvent } from 'rc-util';
|
||||
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
||||
|
||||
import { devUseWarning } from '../_util/warning';
|
||||
@ -11,7 +12,7 @@ import { ConfigContext } from '../config-provider';
|
||||
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
|
||||
import { FloatButtonGroupProvider } from './context';
|
||||
import FloatButton, { floatButtonPrefixCls } from './FloatButton';
|
||||
import type { FloatButtonGroupProps, FloatButtonRef } from './interface';
|
||||
import type { FloatButtonGroupProps } from './interface';
|
||||
import useStyle from './style';
|
||||
|
||||
const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
|
||||
@ -28,6 +29,7 @@ const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
|
||||
children,
|
||||
onOpenChange,
|
||||
open: customOpen,
|
||||
onClick: onTriggerButtonClick,
|
||||
...floatButtonProps
|
||||
} = props;
|
||||
|
||||
@ -53,53 +55,61 @@ const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
|
||||
|
||||
const floatButtonGroupRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
const floatButtonRef = React.useRef<FloatButtonRef['nativeElement']>(null);
|
||||
// ========================== Open ==========================
|
||||
const hoverTrigger = trigger === 'hover';
|
||||
const clickTrigger = trigger === 'click';
|
||||
|
||||
const hoverAction = React.useMemo<React.DOMAttributes<HTMLDivElement>>(() => {
|
||||
const hoverTypeAction = {
|
||||
onMouseEnter() {
|
||||
setOpen(true);
|
||||
onOpenChange?.(true);
|
||||
},
|
||||
onMouseLeave() {
|
||||
setOpen(false);
|
||||
onOpenChange?.(false);
|
||||
},
|
||||
};
|
||||
return trigger === 'hover' ? hoverTypeAction : {};
|
||||
}, [trigger]);
|
||||
|
||||
const handleOpenChange = () => {
|
||||
setOpen((prevState) => {
|
||||
onOpenChange?.(!prevState);
|
||||
return !prevState;
|
||||
});
|
||||
};
|
||||
|
||||
const onClick = useCallback(
|
||||
(e: MouseEvent) => {
|
||||
if (floatButtonGroupRef.current?.contains(e.target as Node)) {
|
||||
if (floatButtonRef.current?.contains(e.target as Node)) {
|
||||
handleOpenChange();
|
||||
const triggerOpen = useEvent((nextOpen: boolean) => {
|
||||
if (open !== nextOpen) {
|
||||
setOpen(nextOpen);
|
||||
onOpenChange?.(nextOpen);
|
||||
}
|
||||
});
|
||||
|
||||
// ===================== Trigger: Hover =====================
|
||||
const onMouseEnter = () => {
|
||||
if (hoverTrigger) {
|
||||
triggerOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseLeave = () => {
|
||||
if (hoverTrigger) {
|
||||
triggerOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
// ===================== Trigger: Click =====================
|
||||
const onInternalTriggerButtonClick: FloatButtonGroupProps['onClick'] = (e) => {
|
||||
if (clickTrigger) {
|
||||
triggerOpen(!open);
|
||||
}
|
||||
onTriggerButtonClick?.(e);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (clickTrigger) {
|
||||
const onDocClick = (e: MouseEvent) => {
|
||||
// Skip if click on the group
|
||||
if (floatButtonGroupRef.current?.contains(e.target as Node)) {
|
||||
return;
|
||||
}
|
||||
setOpen(false);
|
||||
onOpenChange?.(false);
|
||||
},
|
||||
[trigger],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (trigger === 'click') {
|
||||
document.addEventListener('click', onClick);
|
||||
triggerOpen(false);
|
||||
};
|
||||
|
||||
document.addEventListener('click', onDocClick, {
|
||||
capture: true,
|
||||
});
|
||||
return () => {
|
||||
document.removeEventListener('click', onClick);
|
||||
document.removeEventListener('click', onDocClick, {
|
||||
capture: true,
|
||||
});
|
||||
};
|
||||
}
|
||||
}, [trigger]);
|
||||
}, [clickTrigger]);
|
||||
|
||||
// =================== Warning =====================
|
||||
// ======================== Warning =========================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const warning = devUseWarning('FloatButton.Group');
|
||||
|
||||
@ -110,9 +120,17 @@ const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
|
||||
);
|
||||
}
|
||||
|
||||
// ========================= Render =========================
|
||||
return wrapCSSVar(
|
||||
<FloatButtonGroupProvider value={shape}>
|
||||
<div ref={floatButtonGroupRef} className={groupCls} style={style} {...hoverAction}>
|
||||
<div
|
||||
ref={floatButtonGroupRef}
|
||||
className={groupCls}
|
||||
style={style}
|
||||
// Hover trigger
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
{trigger && ['click', 'hover'].includes(trigger) ? (
|
||||
<>
|
||||
<CSSMotion visible={open} motionName={`${groupPrefixCls}-wrap`}>
|
||||
@ -121,12 +139,12 @@ const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
|
||||
)}
|
||||
</CSSMotion>
|
||||
<FloatButton
|
||||
ref={floatButtonRef}
|
||||
type={type}
|
||||
icon={open ? mergedCloseIcon : icon}
|
||||
description={description}
|
||||
aria-label={props['aria-label']}
|
||||
className={`${groupPrefixCls}-trigger`}
|
||||
onClick={onInternalTriggerButtonClick}
|
||||
{...floatButtonProps}
|
||||
/>
|
||||
</>
|
||||
|
@ -5650,6 +5650,332 @@ Array [
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-4 ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class=""
|
||||
title="Rate"
|
||||
>
|
||||
Rate
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-14 ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<ul
|
||||
class="ant-rate ant-rate-disabled"
|
||||
role="radiogroup"
|
||||
tabindex="-1"
|
||||
>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="1"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="2"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="3"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="4"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="5"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>,
|
||||
]
|
||||
`;
|
||||
@ -30400,6 +30726,89 @@ exports[`renders components/form/demo/variant.tsx extend context correctly 1`] =
|
||||
class="ant-form ant-form-horizontal"
|
||||
style="max-width: 600px;"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
|
||||
>
|
||||
<label
|
||||
class=""
|
||||
for="variant"
|
||||
title="Form variant"
|
||||
>
|
||||
Form variant
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-form-item-control ant-col-xs-24 ant-col-sm-14"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-segmented"
|
||||
id="variant"
|
||||
>
|
||||
<div
|
||||
class="ant-segmented-group"
|
||||
>
|
||||
<label
|
||||
class="ant-segmented-item"
|
||||
>
|
||||
<input
|
||||
class="ant-segmented-item-input"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="ant-segmented-item-label"
|
||||
title="outlined"
|
||||
>
|
||||
outlined
|
||||
</div>
|
||||
</label>
|
||||
<label
|
||||
class="ant-segmented-item ant-segmented-item-selected"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-segmented-item-input"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="ant-segmented-item-label"
|
||||
title="filled"
|
||||
>
|
||||
filled
|
||||
</div>
|
||||
</label>
|
||||
<label
|
||||
class="ant-segmented-item"
|
||||
>
|
||||
<input
|
||||
class="ant-segmented-item-input"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="ant-segmented-item-label"
|
||||
title="borderless"
|
||||
>
|
||||
borderless
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
|
@ -2765,6 +2765,332 @@ Array [
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-4 ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class=""
|
||||
title="Rate"
|
||||
>
|
||||
Rate
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-14 ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<ul
|
||||
class="ant-rate ant-rate-disabled"
|
||||
role="radiogroup"
|
||||
tabindex="-1"
|
||||
>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="1"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="2"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="3"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="4"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="5"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="star"
|
||||
class="anticon anticon-star"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="star"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M908.1 353.1l-253.9-36.9L540.7 86.1c-3.1-6.3-8.2-11.4-14.5-14.5-15.8-7.8-35-1.3-42.9 14.5L369.8 316.2l-253.9 36.9c-7 1-13.4 4.3-18.3 9.3a32.05 32.05 0 00.6 45.3l183.7 179.1-43.4 252.9a31.95 31.95 0 0046.4 33.7L512 754l227.1 119.4c6.2 3.3 13.4 4.4 20.3 3.2 17.4-3 29.1-19.5 26.1-36.9l-43.4-252.9 183.7-179.1c5-4.9 8.3-11.3 9.3-18.3 2.7-17.5-9.5-33.7-27-36.3z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>,
|
||||
]
|
||||
`;
|
||||
@ -12619,6 +12945,89 @@ exports[`renders components/form/demo/variant.tsx correctly 1`] = `
|
||||
class="ant-form ant-form-horizontal"
|
||||
style="max-width:600px"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-6"
|
||||
>
|
||||
<label
|
||||
class=""
|
||||
for="variant"
|
||||
title="Form variant"
|
||||
>
|
||||
Form variant
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-form-item-control ant-col-xs-24 ant-col-sm-14"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-segmented"
|
||||
id="variant"
|
||||
>
|
||||
<div
|
||||
class="ant-segmented-group"
|
||||
>
|
||||
<label
|
||||
class="ant-segmented-item"
|
||||
>
|
||||
<input
|
||||
class="ant-segmented-item-input"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="ant-segmented-item-label"
|
||||
title="outlined"
|
||||
>
|
||||
outlined
|
||||
</div>
|
||||
</label>
|
||||
<label
|
||||
class="ant-segmented-item ant-segmented-item-selected"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-segmented-item-input"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="ant-segmented-item-label"
|
||||
title="filled"
|
||||
>
|
||||
filled
|
||||
</div>
|
||||
</label>
|
||||
<label
|
||||
class="ant-segmented-item"
|
||||
>
|
||||
<input
|
||||
class="ant-segmented-item-input"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="ant-segmented-item-label"
|
||||
title="borderless"
|
||||
>
|
||||
borderless
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
Input,
|
||||
InputNumber,
|
||||
Radio,
|
||||
Rate,
|
||||
Select,
|
||||
Slider,
|
||||
Switch,
|
||||
@ -117,6 +118,9 @@ const FormDisabledDemo: React.FC = () => {
|
||||
<Form.Item label="ColorPicker">
|
||||
<ColorPicker />
|
||||
</Form.Item>
|
||||
<Form.Item label="Rate">
|
||||
<Rate />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Cascader,
|
||||
@ -9,7 +9,9 @@ import {
|
||||
Mentions,
|
||||
Select,
|
||||
TreeSelect,
|
||||
Segmented,
|
||||
} from 'antd';
|
||||
import type { FormProps } from 'antd';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
@ -24,8 +26,24 @@ const formItemLayout = {
|
||||
},
|
||||
};
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Form {...formItemLayout} variant="filled" style={{ maxWidth: 600 }}>
|
||||
const App: React.FC = () => {
|
||||
const [componentVariant, setComponentVariant] = useState<FormProps['variant']>('filled');
|
||||
|
||||
const onFormVariantChange = ({ variant }: { variant: FormProps['variant'] }) => {
|
||||
setComponentVariant(variant);
|
||||
};
|
||||
return (
|
||||
<Form
|
||||
{...formItemLayout}
|
||||
onValuesChange={onFormVariantChange}
|
||||
variant={componentVariant}
|
||||
style={{ maxWidth: 600 }}
|
||||
initialValues={{ variant: componentVariant }}
|
||||
>
|
||||
<Form.Item label="Form variant" name="variant">
|
||||
<Segmented options={['outlined', 'filled', 'borderless']} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Input" name="Input" rules={[{ required: true, message: 'Please input!' }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
@ -54,7 +72,11 @@ const App: React.FC = () => (
|
||||
<Mentions />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Select" name="Select" rules={[{ required: true, message: 'Please input!' }]}>
|
||||
<Form.Item
|
||||
label="Select"
|
||||
name="Select"
|
||||
rules={[{ required: true, message: 'Please input!' }]}
|
||||
>
|
||||
<Select />
|
||||
</Form.Item>
|
||||
|
||||
@ -97,5 +119,6 @@ const App: React.FC = () => (
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
@ -16,4 +16,12 @@ describe('suffix', () => {
|
||||
fireEvent.click(container.querySelector('i')!);
|
||||
expect(mockFocus).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should has classname when without controls', () => {
|
||||
const { container } = render(<InputNumber suffix={<i>antd</i>} controls={false} />);
|
||||
|
||||
expect(
|
||||
container.querySelector('.ant-input-number-affix-wrapper-without-controls'),
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -171,6 +171,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
[`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large',
|
||||
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-affix-wrapper-without-controls`]: controls === false,
|
||||
},
|
||||
hashId,
|
||||
),
|
||||
|
@ -469,7 +469,7 @@ const genAffixWrapperStyles: GenerateStyle<InputNumberToken> = (token: InputNumb
|
||||
width: token.handleWidth,
|
||||
opacity: 1,
|
||||
},
|
||||
[`&:hover ${componentCls}-suffix`]: {
|
||||
[`&:not(${componentCls}-affix-wrapper-without-controls):hover ${componentCls}-suffix`]: {
|
||||
marginInlineEnd: token.calc(token.handleWidth).add(paddingInline).equal(),
|
||||
},
|
||||
},
|
||||
|
@ -11,6 +11,7 @@ import { ConfigContext } from '../config-provider';
|
||||
import useRemovePasswordTimeout from './hooks/useRemovePasswordTimeout';
|
||||
import type { InputProps, InputRef } from './Input';
|
||||
import Input from './Input';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
|
||||
const defaultIconRender = (visible: boolean): React.ReactNode =>
|
||||
visible ? <EyeOutlined /> : <EyeInvisibleOutlined />;
|
||||
@ -36,12 +37,16 @@ type IconPropsType = React.HTMLAttributes<HTMLSpanElement> & React.Attributes;
|
||||
|
||||
const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
|
||||
const {
|
||||
disabled,
|
||||
disabled: customDisabled,
|
||||
action = 'click',
|
||||
visibilityToggle = true,
|
||||
iconRender = defaultIconRender,
|
||||
} = props;
|
||||
|
||||
// ===================== Disabled =====================
|
||||
const disabled = React.useContext(DisabledContext);
|
||||
const mergedDisabled = customDisabled ?? disabled;
|
||||
|
||||
const visibilityControlled =
|
||||
typeof visibilityToggle === 'object' && visibilityToggle.visible !== undefined;
|
||||
const [visible, setVisible] = useState(() =>
|
||||
@ -59,7 +64,7 @@ const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
|
||||
const removePasswordTimeout = useRemovePasswordTimeout(inputRef);
|
||||
|
||||
const onVisibleChange = () => {
|
||||
if (disabled) {
|
||||
if (mergedDisabled) {
|
||||
return;
|
||||
}
|
||||
if (visible) {
|
||||
|
@ -10601,6 +10601,48 @@ exports[`renders components/input/demo/password-input.tsx extend context correct
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-disabled ant-input-affix-wrapper-disabled ant-input-outlined ant-input-password"
|
||||
>
|
||||
<input
|
||||
class="ant-input ant-input-disabled"
|
||||
disabled=""
|
||||
placeholder="disabled input password"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="eye-invisible"
|
||||
class="anticon anticon-eye-invisible ant-input-password-icon"
|
||||
role="img"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye-invisible"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2Q889.47 375.11 816.7 305l-50.88 50.88C807.31 395.53 843.45 447.4 874.7 512 791.5 684.2 673.4 766 512 766q-72.67 0-133.87-22.38L323 798.75Q408 838 512 838q288.3 0 430.2-300.3a60.29 60.29 0 000-51.5zm-63.57-320.64L836 122.88a8 8 0 00-11.32 0L715.31 232.2Q624.86 186 512 186q-288.3 0-430.2 300.3a60.3 60.3 0 000 51.5q56.69 119.4 136.5 191.41L112.48 835a8 8 0 000 11.31L155.17 889a8 8 0 0011.31 0l712.15-712.12a8 8 0 000-11.32zM149.3 512C232.6 339.8 350.7 258 512 258c54.54 0 104.13 9.36 149.12 28.39l-70.3 70.3a176 176 0 00-238.13 238.13l-83.42 83.42C223.1 637.49 183.3 582.28 149.3 512zm246.7 0a112.11 112.11 0 01146.2-106.69L401.31 546.2A112 112 0 01396 512z"
|
||||
/>
|
||||
<path
|
||||
d="M508 624c-3.46 0-6.87-.16-10.25-.47l-52.82 52.82a176.09 176.09 0 00227.42-227.42l-52.82 52.82c.31 3.38.47 6.79.47 10.25a111.94 111.94 0 01-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -3947,6 +3947,48 @@ exports[`renders components/input/demo/password-input.tsx correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-disabled ant-input-affix-wrapper-disabled ant-input-outlined ant-input-password"
|
||||
>
|
||||
<input
|
||||
class="ant-input ant-input-disabled"
|
||||
disabled=""
|
||||
placeholder="disabled input password"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-input-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="eye-invisible"
|
||||
class="anticon anticon-eye-invisible ant-input-password-icon"
|
||||
role="img"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye-invisible"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2Q889.47 375.11 816.7 305l-50.88 50.88C807.31 395.53 843.45 447.4 874.7 512 791.5 684.2 673.4 766 512 766q-72.67 0-133.87-22.38L323 798.75Q408 838 512 838q288.3 0 430.2-300.3a60.29 60.29 0 000-51.5zm-63.57-320.64L836 122.88a8 8 0 00-11.32 0L715.31 232.2Q624.86 186 512 186q-288.3 0-430.2 300.3a60.3 60.3 0 000 51.5q56.69 119.4 136.5 191.41L112.48 835a8 8 0 000 11.31L155.17 889a8 8 0 0011.31 0l712.15-712.12a8 8 0 000-11.32zM149.3 512C232.6 339.8 350.7 258 512 258c54.54 0 104.13 9.36 149.12 28.39l-70.3 70.3a176 176 0 00-238.13 238.13l-83.42 83.42C223.1 637.49 183.3 582.28 149.3 512zm246.7 0a112.11 112.11 0 01146.2-106.69L401.31 546.2A112 112 0 01396 512z"
|
||||
/>
|
||||
<path
|
||||
d="M508 624c-3.46 0-6.87-.16-10.25-.47l-52.82 52.82a176.09 176.09 0 00227.42-227.42l-52.82 52.82c.31 3.38.47 6.79.47 10.25a111.94 111.94 0 01-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -21,6 +21,7 @@ const App: React.FC = () => {
|
||||
{passwordVisible ? 'Hide' : 'Show'}
|
||||
</Button>
|
||||
</Space>
|
||||
<Input.Password disabled placeholder="disabled input password" />
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
@ -466,6 +466,7 @@ const genAffixStyle: GenerateStyle<InputToken> = (token: InputToken) => {
|
||||
} = token;
|
||||
|
||||
const affixCls = `${componentCls}-affix-wrapper`;
|
||||
const affixClsDisabled = `${componentCls}-affix-wrapper-disabled`;
|
||||
|
||||
return {
|
||||
[affixCls]: {
|
||||
@ -552,6 +553,17 @@ const genAffixStyle: GenerateStyle<InputToken> = (token: InputToken) => {
|
||||
},
|
||||
},
|
||||
},
|
||||
[affixClsDisabled]: {
|
||||
// password disabled
|
||||
[`${iconCls}${componentCls}-password-icon`]: {
|
||||
color: colorIcon,
|
||||
cursor: 'not-allowed',
|
||||
|
||||
'&:hover': {
|
||||
color: colorIcon,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -50,6 +50,7 @@ const localeValues: Locale = {
|
||||
selectCurrent: 'Tüm sayfayı seç',
|
||||
removeCurrent: 'Sayfayı kaldır',
|
||||
selectAll: 'Tümünü seç',
|
||||
deselectAll: 'Tümünün seçimini kaldır',
|
||||
removeAll: 'Tümünü kaldır',
|
||||
selectInvert: 'Tersini seç',
|
||||
},
|
||||
|
@ -15,6 +15,7 @@ const getVerticalInlineStyle: GenerateStyle<MenuToken, CSSObject> = (token) => {
|
||||
marginXS,
|
||||
itemMarginBlock,
|
||||
itemWidth,
|
||||
itemPaddingInline,
|
||||
} = token;
|
||||
|
||||
const paddingWithArrow = token.calc(menuArrowSize).add(padding).add(marginXS).equal();
|
||||
@ -28,7 +29,7 @@ const getVerticalInlineStyle: GenerateStyle<MenuToken, CSSObject> = (token) => {
|
||||
[`${componentCls}-item, ${componentCls}-submenu-title`]: {
|
||||
height: itemHeight,
|
||||
lineHeight: unit(itemHeight),
|
||||
paddingInline: padding,
|
||||
paddingInline: itemPaddingInline,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
marginInline: itemMarginInline,
|
||||
@ -128,9 +129,6 @@ const getVerticalStyle: GenerateStyle<MenuToken> = (token) => {
|
||||
`border-color ${motionDurationSlow}`,
|
||||
`background ${motionDurationSlow}`,
|
||||
`padding ${motionDurationMid} ${motionEaseOut}`,
|
||||
`padding-inline calc(50% - ${unit(token.calc(fontSizeLG).div(2).equal())} - ${unit(
|
||||
itemMarginInline,
|
||||
)})`,
|
||||
].join(','),
|
||||
|
||||
[`> ${componentCls}-title-content`]: {
|
||||
|
@ -8,8 +8,8 @@ import { act, fireEvent, render, waitFakeTimer } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
|
||||
describe('Popconfirm', () => {
|
||||
mountTest(Popconfirm as any);
|
||||
rtlTest(Popconfirm as any);
|
||||
mountTest(() => <Popconfirm title="test" />);
|
||||
rtlTest(() => <Popconfirm title="test" />);
|
||||
|
||||
const eventObject = expect.objectContaining({
|
||||
target: expect.anything(),
|
||||
|
@ -8,6 +8,7 @@ import type { StarProps as RcStarProps } from 'rc-rate/lib/Star';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import Tooltip from '../tooltip';
|
||||
import useStyle from './style';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
|
||||
export interface RateProps extends RcRateProps {
|
||||
rootClassName?: string;
|
||||
@ -22,6 +23,7 @@ const Rate = React.forwardRef<RateRef, RateProps>((props, ref) => {
|
||||
style,
|
||||
tooltips,
|
||||
character = <StarFilled />,
|
||||
disabled: customDisabled,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
@ -40,11 +42,16 @@ const Rate = React.forwardRef<RateRef, RateProps>((props, ref) => {
|
||||
|
||||
const mergedStyle: React.CSSProperties = { ...rate?.style, ...style };
|
||||
|
||||
// ===================== Disabled =====================
|
||||
const disabled = React.useContext(DisabledContext);
|
||||
const mergedDisabled = customDisabled ?? disabled;
|
||||
|
||||
return wrapCSSVar(
|
||||
<RcRate
|
||||
ref={ref}
|
||||
character={character}
|
||||
characterRender={characterRender}
|
||||
disabled={mergedDisabled}
|
||||
{...rest}
|
||||
className={classNames(className, rootClassName, hashId, cssVarCls, rate?.className)}
|
||||
style={mergedStyle}
|
||||
|
@ -29,8 +29,8 @@ function expectMatchChecked(container: HTMLElement, checkedList: boolean[]) {
|
||||
}
|
||||
|
||||
describe('Segmented', () => {
|
||||
mountTest(Segmented as any);
|
||||
rtlTest(Segmented as any);
|
||||
mountTest(() => <Segmented options={[]} />);
|
||||
rtlTest(() => <Segmented options={[]} />);
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
|
@ -8459,7 +8459,7 @@ exports[`renders components/select/demo/option-label-center.tsx extend context c
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -8490,7 +8490,7 @@ exports[`renders components/select/demo/option-label-center.tsx extend context c
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -8521,7 +8521,7 @@ exports[`renders components/select/demo/option-label-center.tsx extend context c
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open ant-select-tree-treenode-leaf-last"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close ant-select-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
|
@ -8539,7 +8539,7 @@ exports[`renders components/space/demo/compact.tsx extend context correctly 1`]
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open ant-select-tree-treenode-selected"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close ant-select-tree-treenode-selected"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -8569,7 +8569,7 @@ exports[`renders components/space/demo/compact.tsx extend context correctly 1`]
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open ant-select-tree-treenode-leaf-last"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close ant-select-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -8646,7 +8646,7 @@ exports[`renders components/space/demo/compact.tsx extend context correctly 1`]
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open ant-select-tree-treenode-leaf-last"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close ant-select-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
|
@ -10324,7 +10324,7 @@ exports[`renders components/table/demo/filter-in-tree.tsx extend context correct
|
||||
>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -10404,7 +10404,7 @@ exports[`renders components/table/demo/filter-in-tree.tsx extend context correct
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -10438,7 +10438,7 @@ exports[`renders components/table/demo/filter-in-tree.tsx extend context correct
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -10522,7 +10522,7 @@ exports[`renders components/table/demo/filter-in-tree.tsx extend context correct
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -10556,7 +10556,7 @@ exports[`renders components/table/demo/filter-in-tree.tsx extend context correct
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -11264,7 +11264,7 @@ exports[`renders components/table/demo/filter-search.tsx extend context correctl
|
||||
>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -11294,7 +11294,7 @@ exports[`renders components/table/demo/filter-search.tsx extend context correctl
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -11324,7 +11324,7 @@ exports[`renders components/table/demo/filter-search.tsx extend context correctl
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
|
@ -10,7 +10,9 @@ const genSorterStyle: GenerateStyle<TableToken, CSSObject> = (token) => {
|
||||
[`${componentCls}-thead th${componentCls}-column-has-sorters`]: {
|
||||
outline: 'none',
|
||||
cursor: 'pointer',
|
||||
transition: `all ${token.motionDurationSlow}`,
|
||||
// why left 0s? Avoid column header move with transition when left is changed
|
||||
// https://github.com/ant-design/ant-design/issues/50588
|
||||
transition: `all ${token.motionDurationSlow}, left 0s`,
|
||||
|
||||
'&:hover': {
|
||||
background: token.tableHeaderSortHoverBg,
|
||||
|
@ -38,7 +38,7 @@ export interface ComponentToken {
|
||||
*/
|
||||
cardPaddingLG: string;
|
||||
/**
|
||||
* @desc 标齐页标题文本大小
|
||||
* @desc 标签页标题文本大小
|
||||
* @descEN Font size of title
|
||||
*/
|
||||
titleFontSize: number;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { closestCenter, DndContext, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
|
||||
import type { DragEndEvent } from '@dnd-kit/core/dist/types/index';
|
||||
import type { DragEndEvent } from '@dnd-kit/core';
|
||||
import {
|
||||
arrayMove,
|
||||
horizontalListSortingStrategy,
|
||||
|
@ -4,6 +4,7 @@ import omit from 'rc-util/lib/omit';
|
||||
|
||||
import type { PresetColorType, PresetStatusColorType } from '../_util/colors';
|
||||
import { isPresetColor, isPresetStatusColor } from '../_util/colors';
|
||||
import type { ClosableType } from '../_util/hooks/useClosable';
|
||||
import useClosable, { pickClosable } from '../_util/hooks/useClosable';
|
||||
import { replaceElement } from '../_util/reactNode';
|
||||
import type { LiteralUnion } from '../_util/type';
|
||||
@ -22,8 +23,8 @@ export interface TagProps extends React.HTMLAttributes<HTMLSpanElement> {
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
color?: LiteralUnion<PresetColorType | PresetStatusColorType>;
|
||||
closable?: boolean | ({ closeIcon?: React.ReactNode } & React.AriaAttributes);
|
||||
/** Advised to use closeIcon instead. */
|
||||
closable?: ClosableType;
|
||||
closeIcon?: React.ReactNode;
|
||||
/** @deprecated `visible` will be removed in next major version. */
|
||||
visible?: boolean;
|
||||
|
@ -1,7 +1,11 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import getDesignToken from './getDesignToken';
|
||||
import type { GlobalToken, MappingAlgorithm } from './interface';
|
||||
import { defaultConfig, useToken as useInternalToken } from './internal';
|
||||
import {
|
||||
defaultConfig,
|
||||
DesignTokenContext as InternalDesignTokenContext,
|
||||
useToken as useInternalToken,
|
||||
} from './internal';
|
||||
import compactAlgorithm from './themes/compact';
|
||||
import darkAlgorithm from './themes/dark';
|
||||
import defaultAlgorithm from './themes/default';
|
||||
@ -19,15 +23,21 @@ function useToken() {
|
||||
export type { GlobalToken, MappingAlgorithm };
|
||||
|
||||
export default {
|
||||
/** @private Test Usage. Do not use in production. */
|
||||
defaultConfig,
|
||||
|
||||
/** Default seedToken */
|
||||
defaultSeed: defaultConfig.token,
|
||||
|
||||
useToken,
|
||||
defaultAlgorithm,
|
||||
darkAlgorithm,
|
||||
compactAlgorithm,
|
||||
getDesignToken,
|
||||
/**
|
||||
* @private Private variable
|
||||
* @warring 🔥 Do not use in production. 🔥
|
||||
*/
|
||||
defaultConfig,
|
||||
/**
|
||||
* @private Private variable
|
||||
* @warring 🔥 Do not use in production. 🔥
|
||||
*/
|
||||
_internalContext: InternalDesignTokenContext,
|
||||
};
|
||||
|
@ -172,6 +172,366 @@ Array [
|
||||
|
||||
exports[`renders components/tour/demo/basic.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/tour/demo/gap.tsx extend context correctly 1`] = `
|
||||
<div>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Begin Tour
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small"
|
||||
style="display: flex; margin-top: 12px;"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-6"
|
||||
>
|
||||
<span
|
||||
class="ant-typography"
|
||||
>
|
||||
Radius:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-12"
|
||||
>
|
||||
<div
|
||||
class="ant-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="left: 0%; width: 8%;"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="8"
|
||||
class="ant-slider-handle"
|
||||
role="slider"
|
||||
style="left: 8%; transform: translateX(-50%);"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-slider-tooltip ant-tooltip-placement-top"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
style="position: absolute; bottom: 0px; left: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
>
|
||||
8
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-6"
|
||||
>
|
||||
<span
|
||||
class="ant-typography"
|
||||
>
|
||||
offset:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-12"
|
||||
>
|
||||
<div
|
||||
class="ant-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="left: 0%; width: 4%;"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="50"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="2"
|
||||
class="ant-slider-handle"
|
||||
role="slider"
|
||||
style="left: 4%; transform: translateX(-50%);"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-slider-tooltip ant-tooltip-placement-top"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
style="position: absolute; bottom: 0px; left: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
>
|
||||
2
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-6"
|
||||
>
|
||||
<span
|
||||
class="ant-typography"
|
||||
>
|
||||
Horizontal offset:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-12"
|
||||
>
|
||||
<div
|
||||
class="ant-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="left: 0%; width: 4%;"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="50"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="2"
|
||||
class="ant-slider-handle"
|
||||
role="slider"
|
||||
style="left: 4%; transform: translateX(-50%);"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-slider-tooltip ant-tooltip-placement-top"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
style="position: absolute; bottom: 0px; left: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
>
|
||||
2
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-6"
|
||||
>
|
||||
<span
|
||||
class="ant-typography"
|
||||
>
|
||||
Vertical offset:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-12"
|
||||
>
|
||||
<div
|
||||
class="ant-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="left: 0%; width: 4%;"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="50"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="2"
|
||||
class="ant-slider-handle"
|
||||
role="slider"
|
||||
style="left: 4%; transform: translateX(-50%);"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-slider-tooltip ant-tooltip-placement-top"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
style="position: absolute; bottom: 0px; left: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
>
|
||||
2
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tour ant-tour-placement-bottom"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box; z-index: 1001;"
|
||||
>
|
||||
<div
|
||||
class="ant-tour-arrow"
|
||||
style="position: absolute; top: 0px; left: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-tour-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tour-inner"
|
||||
>
|
||||
<button
|
||||
class="ant-tour-close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close ant-tour-close-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M799.86 166.31c.02 0 .04.02.08.06l57.69 57.7c.04.03.05.05.06.08a.12.12 0 010 .06c0 .03-.02.05-.06.09L569.93 512l287.7 287.7c.04.04.05.06.06.09a.12.12 0 010 .07c0 .02-.02.04-.06.08l-57.7 57.69c-.03.04-.05.05-.07.06a.12.12 0 01-.07 0c-.03 0-.05-.02-.09-.06L512 569.93l-287.7 287.7c-.04.04-.06.05-.09.06a.12.12 0 01-.07 0c-.02 0-.04-.02-.08-.06l-57.69-57.7c-.04-.03-.05-.05-.06-.07a.12.12 0 010-.07c0-.03.02-.05.06-.09L454.07 512l-287.7-287.7c-.04-.04-.05-.06-.06-.09a.12.12 0 010-.07c0-.02.02-.04.06-.08l57.7-57.69c.03-.04.05-.05.07-.06a.12.12 0 01.07 0c.03 0 .05.02.09.06L512 454.07l287.7-287.7c.04-.04.06-.05.09-.06a.12.12 0 01.07 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="ant-tour-cover"
|
||||
>
|
||||
<img
|
||||
alt="tour.png"
|
||||
src="https://user-images.githubusercontent.com/5378891/197385811-55df8480-7ff4-44bd-9d43-a7dade598d70.png"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tour-header"
|
||||
>
|
||||
<div
|
||||
class="ant-tour-title"
|
||||
>
|
||||
Upload File
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tour-description"
|
||||
>
|
||||
Put your files here.
|
||||
</div>
|
||||
<div
|
||||
class="ant-tour-footer"
|
||||
>
|
||||
<div
|
||||
class="ant-tour-buttons"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-sm ant-tour-next-btn"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Finish
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/tour/demo/gap.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/tour/demo/indicator.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<button
|
||||
|
@ -77,6 +77,208 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/tour/demo/gap.tsx correctly 1`] = `
|
||||
<div>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Begin Tour
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small"
|
||||
style="display:flex;margin-top:12px"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-6"
|
||||
>
|
||||
<span
|
||||
class="ant-typography"
|
||||
>
|
||||
Radius:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-12"
|
||||
>
|
||||
<div
|
||||
class="ant-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="left:0%;width:8%"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="8"
|
||||
class="ant-slider-handle"
|
||||
role="slider"
|
||||
style="left:8%;transform:translateX(-50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-6"
|
||||
>
|
||||
<span
|
||||
class="ant-typography"
|
||||
>
|
||||
offset:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-12"
|
||||
>
|
||||
<div
|
||||
class="ant-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="left:0%;width:4%"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="50"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="2"
|
||||
class="ant-slider-handle"
|
||||
role="slider"
|
||||
style="left:4%;transform:translateX(-50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-6"
|
||||
>
|
||||
<span
|
||||
class="ant-typography"
|
||||
>
|
||||
Horizontal offset:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-12"
|
||||
>
|
||||
<div
|
||||
class="ant-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="left:0%;width:4%"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="50"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="2"
|
||||
class="ant-slider-handle"
|
||||
role="slider"
|
||||
style="left:4%;transform:translateX(-50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-6"
|
||||
>
|
||||
<span
|
||||
class="ant-typography"
|
||||
>
|
||||
Vertical offset:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-12"
|
||||
>
|
||||
<div
|
||||
class="ant-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="left:0%;width:4%"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="50"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="2"
|
||||
class="ant-slider-handle"
|
||||
role="slider"
|
||||
style="left:4%;transform:translateX(-50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/tour/demo/indicator.tsx correctly 1`] = `
|
||||
Array [
|
||||
<button
|
||||
|
@ -430,6 +430,169 @@ exports[`Tour custom step pre btn & next btn className & style 1`] = `
|
||||
|
||||
exports[`Tour rtl render component should be rendered correctly in RTL direction 1`] = `null`;
|
||||
|
||||
exports[`Tour should support gap.offset 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
>
|
||||
Show
|
||||
</button>
|
||||
<div
|
||||
class="ant-tour ant-tour-placement-bottom"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box; z-index: 1001;"
|
||||
>
|
||||
<div
|
||||
class="ant-tour-arrow"
|
||||
style="position: absolute; top: 0px; left: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-tour-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tour-inner"
|
||||
>
|
||||
<button
|
||||
class="ant-tour-close"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close ant-tour-close-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M799.86 166.31c.02 0 .04.02.08.06l57.69 57.7c.04.03.05.05.06.08a.12.12 0 010 .06c0 .03-.02.05-.06.09L569.93 512l287.7 287.7c.04.04.05.06.06.09a.12.12 0 010 .07c0 .02-.02.04-.06.08l-57.7 57.69c-.03.04-.05.05-.07.06a.12.12 0 01-.07 0c-.03 0-.05-.02-.09-.06L512 569.93l-287.7 287.7c-.04.04-.06.05-.09.06a.12.12 0 01-.07 0c-.02 0-.04-.02-.08-.06l-57.69-57.7c-.04-.03-.05-.05-.06-.07a.12.12 0 010-.07c0-.03.02-.05.06-.09L454.07 512l-287.7-287.7c-.04-.04-.05-.06-.06-.09a.12.12 0 010-.07c0-.02.02-.04.06-.08l57.7-57.69c.03-.04.05-.05.07-.06a.12.12 0 01.07 0c.03 0 .05.02.09.06L512 454.07l287.7-287.7c.04-.04.06-.05.09-.06a.12.12 0 01.07 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="ant-tour-header"
|
||||
>
|
||||
<div
|
||||
class="ant-tour-title"
|
||||
>
|
||||
Show in Center
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tour-description"
|
||||
>
|
||||
Here is the content of Tour.
|
||||
</div>
|
||||
<div
|
||||
class="ant-tour-footer"
|
||||
>
|
||||
<div
|
||||
class="ant-tour-buttons"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary ant-btn-sm ant-tour-next-btn"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Finish
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tour-mask"
|
||||
style="position: fixed; left: 0px; right: 0px; top: 0px; bottom: 0px; z-index: 1001; pointer-events: none;"
|
||||
>
|
||||
<svg
|
||||
style="width: 100%; height: 100%;"
|
||||
>
|
||||
<defs>
|
||||
<mask
|
||||
id="ant-tour-mask-test-id"
|
||||
>
|
||||
<rect
|
||||
fill="white"
|
||||
height="100vh"
|
||||
width="100vw"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<rect
|
||||
class="ant-tour-placeholder-animated"
|
||||
fill="black"
|
||||
height="200"
|
||||
rx="2"
|
||||
width="250"
|
||||
x="90"
|
||||
y="190"
|
||||
/>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect
|
||||
fill="rgba(0,0,0,0.5)"
|
||||
height="100%"
|
||||
mask="url(#ant-tour-mask-test-id)"
|
||||
width="100%"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<rect
|
||||
fill="transparent"
|
||||
height="190"
|
||||
pointer-events="auto"
|
||||
width="100%"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<rect
|
||||
fill="transparent"
|
||||
height="100%"
|
||||
pointer-events="auto"
|
||||
width="90"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<rect
|
||||
fill="transparent"
|
||||
height="calc(100vh - 390px)"
|
||||
pointer-events="auto"
|
||||
width="100%"
|
||||
x="0"
|
||||
y="390"
|
||||
/>
|
||||
<rect
|
||||
fill="transparent"
|
||||
height="100%"
|
||||
pointer-events="auto"
|
||||
width="calc(100vw - 340px)"
|
||||
x="340"
|
||||
y="0"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tour-target-placeholder"
|
||||
style="left: 90px; top: 190px; width: 250px; height: 200px; position: fixed; pointer-events: none;"
|
||||
/>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`Tour single 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* eslint-disable react/no-unstable-nested-components */
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
|
||||
|
||||
import Tour from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
@ -7,6 +8,24 @@ import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render, screen } from '../../../tests/utils';
|
||||
import type { TourProps } from '../interface';
|
||||
|
||||
const mockBtnRect = (
|
||||
rect: { x: number; y: number; width: number; height: number },
|
||||
scrollIntoViewCb?: () => void,
|
||||
) => {
|
||||
spyElementPrototypes(HTMLButtonElement, {
|
||||
getBoundingClientRect: {
|
||||
get(): any {
|
||||
return () => ({ ...rect, left: rect.x, top: rect.y });
|
||||
},
|
||||
},
|
||||
scrollIntoView: {
|
||||
get(): any {
|
||||
scrollIntoViewCb?.();
|
||||
return (val: boolean | ScrollIntoViewOptions) => val;
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
describe('Tour', () => {
|
||||
mountTest(Tour);
|
||||
rtlTest(Tour);
|
||||
@ -590,6 +609,86 @@ describe('Tour', () => {
|
||||
expect(onClose).toHaveBeenLastCalledWith(1);
|
||||
});
|
||||
|
||||
it('should support gap.radius', () => {
|
||||
const App: React.FC<{ gap: TourProps['gap'] }> = ({ gap }) => {
|
||||
const ref = useRef<HTMLButtonElement>(null);
|
||||
const [show, setShow] = React.useState<boolean>();
|
||||
const steps: TourProps['steps'] = [
|
||||
{
|
||||
title: 'Show in Center',
|
||||
description: 'Here is the content of Tour.',
|
||||
target: () => ref.current!,
|
||||
},
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<button type="button" onClick={() => setShow(true)} ref={ref}>
|
||||
Show
|
||||
</button>
|
||||
|
||||
<Tour open={show} steps={steps} gap={gap} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
const { rerender, baseElement } = render(<App gap={{ radius: 4 }} />);
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Show' }));
|
||||
|
||||
expect(baseElement.querySelector('.ant-tour-placeholder-animated')).toBeTruthy();
|
||||
expect(baseElement.querySelector('.ant-tour-placeholder-animated')).toHaveAttribute('rx', '4');
|
||||
rerender(<App gap={{ radius: 0 }} />);
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Show' }));
|
||||
expect(baseElement.querySelector('.ant-tour-placeholder-animated')).toBeTruthy();
|
||||
expect(baseElement.querySelector('.ant-tour-placeholder-animated')).toHaveAttribute('rx', '0');
|
||||
});
|
||||
it('should support gap.offset', () => {
|
||||
const gap = { offset: 10 };
|
||||
const pos = { x: 100, y: 200, width: 230, height: 180 };
|
||||
mockBtnRect(pos);
|
||||
const App: React.FC = () => {
|
||||
const ref = useRef<HTMLButtonElement>(null);
|
||||
const [show, setShow] = React.useState<boolean>();
|
||||
const steps: TourProps['steps'] = [
|
||||
{
|
||||
title: 'Show in Center',
|
||||
description: 'Here is the content of Tour.',
|
||||
target: () => ref.current!,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<button type="button" onClick={() => setShow(true)} ref={ref}>
|
||||
Show
|
||||
</button>
|
||||
|
||||
<Tour steps={steps} gap={gap} open={show} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const { baseElement } = render(<App />);
|
||||
const targetBtn = screen.getByRole('button', { name: 'Show' });
|
||||
fireEvent.click(targetBtn);
|
||||
|
||||
expect(baseElement.querySelector('.ant-tour-placeholder-animated')).toHaveAttribute(
|
||||
'width',
|
||||
String(pos.width + gap.offset * 2),
|
||||
);
|
||||
expect(baseElement.querySelector('.ant-tour-placeholder-animated')).toHaveAttribute(
|
||||
'height',
|
||||
String(pos.height + gap.offset * 2),
|
||||
);
|
||||
expect(baseElement.querySelector('.ant-tour-placeholder-animated')).toHaveAttribute(
|
||||
'x',
|
||||
String(pos.x - gap.offset),
|
||||
);
|
||||
expect(baseElement.querySelector('.ant-tour-placeholder-animated')).toHaveAttribute(
|
||||
'y',
|
||||
String(pos.y - gap.offset),
|
||||
);
|
||||
|
||||
expect(baseElement).toMatchSnapshot();
|
||||
});
|
||||
// This test is for PurePanel which means safe to remove.
|
||||
describe('PurePanel', () => {
|
||||
const PurePanel = Tour._InternalPanelDoNotUseOrYouWillBeFired;
|
||||
|
11
components/tour/demo/gap.md
Normal file
11
components/tour/demo/gap.md
Normal file
@ -0,0 +1,11 @@
|
||||
## zh-CN
|
||||
|
||||
使用 `gap` 参数来控制高亮区域的边距和圆角。
|
||||
|
||||
- `5.9.0` 之前不支持单独设置两个方向上的边距和数组类型的 `offset` 参数。
|
||||
|
||||
## en-US
|
||||
|
||||
Using `gap` to control the radius of highlight area and the offset between highlight area and the element.
|
||||
|
||||
- Setting offset in two directions individually and `offset` with array type is not supported until `5.9.0`.
|
100
components/tour/demo/gap.tsx
Normal file
100
components/tour/demo/gap.tsx
Normal file
@ -0,0 +1,100 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { Button, Col, Row, Slider, Space, Tour, Typography } from 'antd';
|
||||
import type { TourProps } from 'antd';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
const App: React.FC = () => {
|
||||
const tourNodeRef = useRef(null);
|
||||
const [radius, setRadius] = useState(8);
|
||||
const [offsetX, setOffsetX] = useState(2);
|
||||
const [offsetY, setOffsetY] = useState(2);
|
||||
const [offset, setOffset] = useState(2);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [offsetDirection, setOffsetDirection] = useState<'both' | 'individual'>('individual');
|
||||
|
||||
const steps: TourProps['steps'] = [
|
||||
{
|
||||
title: 'Upload File',
|
||||
description: 'Put your files here.',
|
||||
cover: (
|
||||
<img
|
||||
alt="tour.png"
|
||||
src="https://user-images.githubusercontent.com/5378891/197385811-55df8480-7ff4-44bd-9d43-a7dade598d70.png"
|
||||
/>
|
||||
),
|
||||
target: () => tourNodeRef.current,
|
||||
},
|
||||
];
|
||||
|
||||
const offsetGap =
|
||||
offsetDirection === 'both'
|
||||
? { offset }
|
||||
: {
|
||||
offset: [offsetX, offsetY] as [number, number],
|
||||
};
|
||||
return (
|
||||
<div ref={tourNodeRef}>
|
||||
<Button type="primary" onClick={() => setOpen(true)}>
|
||||
Begin Tour
|
||||
</Button>
|
||||
<Space style={{ display: 'flex', marginTop: 12 }} direction="vertical">
|
||||
<Row>
|
||||
<Col span={6}>
|
||||
<Text>Radius:</Text>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Slider value={radius} onChange={(val) => val && setRadius(val)} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={6}>
|
||||
<Text> offset:</Text>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Slider
|
||||
value={offset}
|
||||
max={50}
|
||||
onChange={(val) => val && setOffset(val)}
|
||||
onFocus={() => setOffsetDirection('both')}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={6}>
|
||||
<Text>Horizontal offset:</Text>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Slider
|
||||
value={offsetX}
|
||||
max={50}
|
||||
onChange={(val) => val && setOffsetX(val)}
|
||||
onFocus={() => setOffsetDirection('individual')}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={6}>
|
||||
<Text>Vertical offset:</Text>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Slider
|
||||
value={offsetY}
|
||||
max={50}
|
||||
onChange={(val) => val && setOffsetY(val)}
|
||||
onFocus={() => setOffsetDirection('individual')}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Space>
|
||||
<Tour
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
steps={steps}
|
||||
gap={{ ...offsetGap, radius }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
@ -22,6 +22,7 @@ Use when you want to guide users through a product.
|
||||
<code src="./demo/placement.tsx">Placement</code>
|
||||
<code src="./demo/mask.tsx">Custom mask style</code>
|
||||
<code src="./demo/indicator.tsx">Custom indicator</code>
|
||||
<code src="./demo/gap.tsx">Custom highlighted area style</code>
|
||||
<code src="./demo/render-panel.tsx" debug>\_InternalPanelDoNotUseOrYouWillBeFired</code>
|
||||
|
||||
## API
|
||||
@ -35,6 +36,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| arrow | Whether to show the arrow, including the configuration whether to point to the center of the element | `boolean`\|`{ pointAtCenter: boolean}` | `true` | |
|
||||
| closeIcon | Customize close icon | `React.ReactNode` | `true` | 5.9.0 |
|
||||
| disabledInteraction | Disable interaction on highlighted area. | `boolean` | `false` | 5.13.0 |
|
||||
| gap | Control the radius of the highlighted area and the offset between highlighted area and the element. | `{ offset?: number \| [number, number]; radius?: number }` | `{ offset?: 6 ; radius?: 2 }` | 5.0.0 (array type `offset`: 5.9.0) |
|
||||
| placement | Position of the guide card relative to the target element | `center` `left` `leftTop` `leftBottom` `right` `rightTop` `rightBottom` `top` `topLeft` `topRight` `bottom` `bottomLeft` `bottomRight` | `bottom` | |
|
||||
| onClose | Callback function on shutdown | `Function` | - | |
|
||||
| mask | Whether to enable masking, change mask style and fill color by pass custom props | `boolean \| { style?: React.CSSProperties; color?: string; }` | `true` | |
|
||||
|
@ -23,6 +23,7 @@ tag: 5.0.0
|
||||
<code src="./demo/placement.tsx">位置</code>
|
||||
<code src="./demo/mask.tsx">自定义遮罩样式</code>
|
||||
<code src="./demo/indicator.tsx">自定义指示器</code>
|
||||
<code src="./demo/gap.tsx">自定义高亮区域的样式</code>
|
||||
<code src="./demo/render-panel.tsx" debug>\_InternalPanelDoNotUseOrYouWillBeFired</code>
|
||||
|
||||
## API
|
||||
@ -36,6 +37,7 @@ tag: 5.0.0
|
||||
| arrow | 是否显示箭头,包含是否指向元素中心的配置 | `boolean` \| `{ pointAtCenter: boolean}` | `true` | |
|
||||
| closeIcon | 自定义关闭按钮 | `React.ReactNode` | `true` | 5.9.0 |
|
||||
| disabledInteraction | 禁用高亮区域交互 | `boolean` | `false` | 5.13.0 |
|
||||
| gap | 控制高亮区域的圆角边框和显示间距 | `{ offset?: number \| [number, number]; radius?: number }` | `{ offset?: 6 ; radius?: 2 }` | 5.0.0 (数组类型的 `offset`: 5.9.0 ) |
|
||||
| placement | 引导卡片相对于目标元素的位置 | `center` `left` `leftTop` `leftBottom` `right` `rightTop` `rightBottom` `top` `topLeft` `topRight` `bottom` `bottomLeft` `bottomRight` | `bottom` | |
|
||||
| onClose | 关闭引导时的回调函数 | `Function` | - | |
|
||||
| onFinish | 引导完成时的回调 | `Function` | - | |
|
||||
|
@ -11861,7 +11861,7 @@ exports[`renders components/transfer/demo/tree-transfer.tsx extend context corre
|
||||
>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -11941,7 +11941,7 @@ exports[`renders components/transfer/demo/tree-transfer.tsx extend context corre
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -11975,7 +11975,7 @@ exports[`renders components/transfer/demo/tree-transfer.tsx extend context corre
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -12009,7 +12009,7 @@ exports[`renders components/transfer/demo/tree-transfer.tsx extend context corre
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -12039,7 +12039,7 @@ exports[`renders components/transfer/demo/tree-transfer.tsx extend context corre
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -12069,7 +12069,7 @@ exports[`renders components/transfer/demo/tree-transfer.tsx extend context corre
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
|
@ -7907,7 +7907,7 @@ exports[`renders components/transfer/demo/tree-transfer.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -7987,7 +7987,7 @@ exports[`renders components/transfer/demo/tree-transfer.tsx correctly 1`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -8021,7 +8021,7 @@ exports[`renders components/transfer/demo/tree-transfer.tsx correctly 1`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -8055,7 +8055,7 @@ exports[`renders components/transfer/demo/tree-transfer.tsx correctly 1`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -8085,7 +8085,7 @@ exports[`renders components/transfer/demo/tree-transfer.tsx correctly 1`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -8115,7 +8115,7 @@ exports[`renders components/transfer/demo/tree-transfer.tsx correctly 1`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
|
@ -393,7 +393,7 @@ exports[`renders components/tree-select/demo/basic.tsx extend context correctly
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -423,7 +423,7 @@ exports[`renders components/tree-select/demo/basic.tsx extend context correctly
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -453,7 +453,7 @@ exports[`renders components/tree-select/demo/basic.tsx extend context correctly
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -483,7 +483,7 @@ exports[`renders components/tree-select/demo/basic.tsx extend context correctly
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -513,7 +513,7 @@ exports[`renders components/tree-select/demo/basic.tsx extend context correctly
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -543,7 +543,7 @@ exports[`renders components/tree-select/demo/basic.tsx extend context correctly
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open ant-select-tree-treenode-leaf-last"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close ant-select-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -620,7 +620,7 @@ exports[`renders components/tree-select/demo/basic.tsx extend context correctly
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open ant-select-tree-treenode-leaf-last"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close ant-select-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -1145,7 +1145,7 @@ exports[`renders components/tree-select/demo/multiple.tsx extend context correct
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -1175,7 +1175,7 @@ exports[`renders components/tree-select/demo/multiple.tsx extend context correct
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open ant-select-tree-treenode-leaf-last"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close ant-select-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -1252,7 +1252,7 @@ exports[`renders components/tree-select/demo/multiple.tsx extend context correct
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open ant-select-tree-treenode-leaf-last"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close ant-select-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -1573,7 +1573,7 @@ Array [
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -1603,7 +1603,7 @@ Array [
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open ant-select-tree-treenode-leaf-last"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close ant-select-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -1680,7 +1680,7 @@ Array [
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open ant-select-tree-treenode-leaf-last"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close ant-select-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -2388,7 +2388,7 @@ exports[`renders components/tree-select/demo/suffix.tsx extend context correctly
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -2418,7 +2418,7 @@ exports[`renders components/tree-select/demo/suffix.tsx extend context correctly
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open ant-select-tree-treenode-leaf-last"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close ant-select-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -2495,7 +2495,7 @@ exports[`renders components/tree-select/demo/suffix.tsx extend context correctly
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open ant-select-tree-treenode-leaf-last"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close ant-select-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -2689,7 +2689,7 @@ exports[`renders components/tree-select/demo/treeData.tsx extend context correct
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -2716,7 +2716,7 @@ exports[`renders components/tree-select/demo/treeData.tsx extend context correct
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open ant-select-tree-treenode-leaf-last"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close ant-select-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -2743,7 +2743,7 @@ exports[`renders components/tree-select/demo/treeData.tsx extend context correct
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-open ant-select-tree-treenode-leaf-last"
|
||||
class="ant-select-tree-treenode ant-select-tree-treenode-switcher-close ant-select-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
|
@ -951,7 +951,7 @@ exports[`renders components/tree/demo/block-node.tsx extend context correctly 1`
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-disabled ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-disabled ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -985,7 +985,7 @@ exports[`renders components/tree/demo/block-node.tsx extend context correctly 1`
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-selected ant-tree-treenode-leaf-last"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-selected ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -1132,7 +1132,7 @@ exports[`renders components/tree/demo/customized-icon.tsx extend context correct
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-selected"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-selected"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -1182,7 +1182,7 @@ exports[`renders components/tree/demo/customized-icon.tsx extend context correct
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
|
@ -947,7 +947,7 @@ exports[`renders components/tree/demo/block-node.tsx correctly 1`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-disabled ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-disabled ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -981,7 +981,7 @@ exports[`renders components/tree/demo/block-node.tsx correctly 1`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-selected ant-tree-treenode-leaf-last"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-selected ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -1126,7 +1126,7 @@ exports[`renders components/tree/demo/customized-icon.tsx correctly 1`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-selected"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-selected"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -1176,7 +1176,7 @@ exports[`renders components/tree/demo/customized-icon.tsx correctly 1`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
|
@ -698,7 +698,7 @@ exports[`Tree switcherIcon in Tree could be string 1`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -725,7 +725,7 @@ exports[`Tree switcherIcon in Tree could be string 1`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-leaf-last"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -822,7 +822,7 @@ exports[`Tree switcherIcon should be loading icon when loadData 1`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-loading"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -834,35 +834,14 @@ exports[`Tree switcherIcon should be loading icon when loadData 1`] = `
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
>
|
||||
<span
|
||||
aria-label="loading"
|
||||
class="anticon anticon-loading anticon-spin ant-tree-switcher-loading-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="loading"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
switcherIcon
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"
|
||||
title="node1"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__open ant-tree-icon_loading"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
@ -872,7 +851,7 @@ exports[`Tree switcherIcon should be loading icon when loadData 1`] = `
|
||||
</div>
|
||||
<div
|
||||
aria-grabbed="false"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-open ant-tree-treenode-loading ant-tree-treenode-leaf-last"
|
||||
class="ant-tree-treenode ant-tree-treenode-switcher-close ant-tree-treenode-leaf-last"
|
||||
draggable="false"
|
||||
>
|
||||
<span
|
||||
@ -884,35 +863,14 @@ exports[`Tree switcherIcon should be loading icon when loadData 1`] = `
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-switcher ant-tree-switcher_open"
|
||||
class="ant-tree-switcher ant-tree-switcher_close"
|
||||
>
|
||||
<span
|
||||
aria-label="loading"
|
||||
class="anticon anticon-loading anticon-spin ant-tree-switcher-loading-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="loading"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
switcherIcon
|
||||
</span>
|
||||
<span
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"
|
||||
class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-close"
|
||||
title="node2"
|
||||
>
|
||||
<span
|
||||
class="ant-tree-iconEle ant-tree-icon__open ant-tree-icon_loading"
|
||||
/>
|
||||
<span
|
||||
class="ant-tree-title"
|
||||
>
|
||||
|
@ -125,7 +125,7 @@ describe('Tree', () => {
|
||||
<Tree
|
||||
switcherIcon="switcherIcon"
|
||||
loadData={onLoadData}
|
||||
defaultExpandAll
|
||||
defaultExpandedKeys={['0-0-2', '0-0-3']}
|
||||
switcherLoadingIcon={<div>loading...</div>}
|
||||
>
|
||||
<TreeNode icon="icon">
|
||||
|
@ -29,7 +29,10 @@ const App: React.FC = () => (
|
||||
treeData={treeData}
|
||||
height={233}
|
||||
defaultExpandAll
|
||||
titleRender={(item) => <MemoTooltip title={item.title as any}>{item.title as any}</MemoTooltip>}
|
||||
titleRender={(item) => {
|
||||
const title = item.title as React.ReactNode;
|
||||
return <MemoTooltip title={title}>{title}</MemoTooltip>;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -19,32 +19,26 @@ export interface CopyBtnProps extends Omit<CopyConfig, 'onCopy'> {
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
const CopyBtn: React.FC<CopyBtnProps> = (props) => {
|
||||
const {
|
||||
const CopyBtn: React.FC<CopyBtnProps> = ({
|
||||
prefixCls,
|
||||
copied,
|
||||
locale,
|
||||
iconOnly,
|
||||
tooltips,
|
||||
icon,
|
||||
loading: btnLoading,
|
||||
tabIndex,
|
||||
onCopy,
|
||||
} = props;
|
||||
|
||||
loading: btnLoading,
|
||||
}) => {
|
||||
const tooltipNodes = toList(tooltips);
|
||||
const iconNodes = toList(icon);
|
||||
|
||||
const { copied: copiedText, copy: copyText } = locale ?? {};
|
||||
|
||||
const copyTitle = copied
|
||||
? getNode(tooltipNodes[1], copiedText)
|
||||
: getNode(tooltipNodes[0], copyText);
|
||||
const systemStr = copied ? copiedText : copyText;
|
||||
const copyTitle = getNode(tooltipNodes[copied ? 1 : 0], systemStr);
|
||||
const ariaLabel = typeof copyTitle === 'string' ? copyTitle : systemStr;
|
||||
|
||||
return (
|
||||
<Tooltip key="copy" title={copyTitle}>
|
||||
<Tooltip title={copyTitle}>
|
||||
<TransButton
|
||||
className={classNames(`${prefixCls}-copy`, {
|
||||
[`${prefixCls}-copy-success`]: copied,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import toArray from 'rc-util/lib/Children/toArray';
|
||||
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
|
||||
import { isValidText } from './util';
|
||||
|
||||
interface MeasureTextProps {
|
||||
style?: React.CSSProperties;
|
||||
@ -44,24 +45,8 @@ const MeasureText = React.forwardRef<MeasureTextRef, MeasureTextProps>(
|
||||
},
|
||||
);
|
||||
|
||||
function cuttable(node: React.ReactElement) {
|
||||
const type = typeof node;
|
||||
return type === 'string' || type === 'number';
|
||||
}
|
||||
|
||||
function getNodesLen(nodeList: React.ReactElement[]) {
|
||||
let totalLen = 0;
|
||||
|
||||
nodeList.forEach((node) => {
|
||||
if (cuttable(node)) {
|
||||
totalLen += String(node).length;
|
||||
} else {
|
||||
totalLen += 1;
|
||||
}
|
||||
});
|
||||
|
||||
return totalLen;
|
||||
}
|
||||
const getNodesLen = (nodeList: React.ReactElement[]) =>
|
||||
nodeList.reduce((totalLen, node) => totalLen + (isValidText(node) ? String(node).length : 1), 0);
|
||||
|
||||
function sliceNodes(nodeList: React.ReactElement[], len: number) {
|
||||
let currLen = 0;
|
||||
@ -74,7 +59,7 @@ function sliceNodes(nodeList: React.ReactElement[], len: number) {
|
||||
}
|
||||
|
||||
const node = nodeList[i];
|
||||
const canCut = cuttable(node);
|
||||
const canCut = isValidText(node);
|
||||
const nodeLen = canCut ? String(node).length : 1;
|
||||
const nextLen = currLen + nodeLen;
|
||||
|
||||
@ -97,7 +82,6 @@ export interface EllipsisProps {
|
||||
enableMeasure?: boolean;
|
||||
text?: React.ReactNode;
|
||||
width: number;
|
||||
// fontSize: number;
|
||||
rows: number;
|
||||
children: (
|
||||
cutChildren: React.ReactNode[],
|
||||
@ -142,9 +126,7 @@ export default function EllipsisMeasure(props: EllipsisProps) {
|
||||
|
||||
// ========================= NeedEllipsis =========================
|
||||
const measureWhiteSpaceRef = React.useRef<HTMLElement>(null);
|
||||
|
||||
const needEllipsisRef = React.useRef<MeasureTextRef>(null);
|
||||
|
||||
// Measure for `rows-1` height, to avoid operation exceed the line height
|
||||
const descRowsEllipsisRef = React.useRef<MeasureTextRef>(null);
|
||||
const symbolRowEllipsisRef = React.useRef<MeasureTextRef>(null);
|
||||
@ -187,9 +169,11 @@ export default function EllipsisMeasure(props: EllipsisProps) {
|
||||
// Get the height of `rows - 1` + symbol height
|
||||
const descRowsEllipsisHeight = rows === 1 ? 0 : descRowsEllipsisRef.current?.getHeight() || 0;
|
||||
const symbolRowEllipsisHeight = symbolRowEllipsisRef.current?.getHeight() || 0;
|
||||
const rowsWithEllipsisHeight = descRowsEllipsisHeight + symbolRowEllipsisHeight;
|
||||
|
||||
const maxRowsHeight = Math.max(baseRowsEllipsisHeight, rowsWithEllipsisHeight);
|
||||
const maxRowsHeight = Math.max(
|
||||
baseRowsEllipsisHeight,
|
||||
// height of rows with ellipsis
|
||||
descRowsEllipsisHeight + symbolRowEllipsisHeight,
|
||||
);
|
||||
|
||||
setEllipsisHeight(maxRowsHeight + 1);
|
||||
|
||||
@ -209,34 +193,31 @@ export default function EllipsisMeasure(props: EllipsisProps) {
|
||||
|
||||
const isOverflow = midHeight > ellipsisHeight;
|
||||
let targetMidIndex = cutMidIndex;
|
||||
|
||||
if (maxIndex - minIndex === 1) {
|
||||
targetMidIndex = isOverflow ? minIndex : maxIndex;
|
||||
}
|
||||
|
||||
if (isOverflow) {
|
||||
setEllipsisCutIndex([minIndex, targetMidIndex]);
|
||||
} else {
|
||||
setEllipsisCutIndex([targetMidIndex, maxIndex]);
|
||||
}
|
||||
setEllipsisCutIndex(isOverflow ? [minIndex, targetMidIndex] : [targetMidIndex, maxIndex]);
|
||||
}
|
||||
}, [ellipsisCutIndex, cutMidIndex]);
|
||||
|
||||
// ========================= Text Content =========================
|
||||
const finalContent = React.useMemo(() => {
|
||||
// Skip everything if `enableMeasure` is disabled
|
||||
if (!enableMeasure) {
|
||||
return children(nodeList, false);
|
||||
}
|
||||
|
||||
if (
|
||||
needEllipsis !== STATUS_MEASURE_NEED_ELLIPSIS ||
|
||||
!ellipsisCutIndex ||
|
||||
ellipsisCutIndex[0] !== ellipsisCutIndex[1]
|
||||
) {
|
||||
const content = children(nodeList, false);
|
||||
|
||||
// Limit the max line count to avoid scrollbar blink
|
||||
// Limit the max line count to avoid scrollbar blink unless no need ellipsis
|
||||
// https://github.com/ant-design/ant-design/issues/42958
|
||||
if (
|
||||
needEllipsis !== STATUS_MEASURE_NO_NEED_ELLIPSIS &&
|
||||
needEllipsis !== STATUS_MEASURE_NONE
|
||||
) {
|
||||
if ([STATUS_MEASURE_NO_NEED_ELLIPSIS, STATUS_MEASURE_NONE].includes(needEllipsis)) {
|
||||
return content;
|
||||
}
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
@ -249,9 +230,6 @@ export default function EllipsisMeasure(props: EllipsisProps) {
|
||||
);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
return children(expanded ? nodeList : sliceNodes(nodeList, ellipsisCutIndex[0]), canEllipsis);
|
||||
}, [expanded, needEllipsis, ellipsisCutIndex, nodeList, ...miscDeps]);
|
||||
|
||||
|
@ -4,7 +4,7 @@ import classNames from 'classnames';
|
||||
import ResizeObserver from 'rc-resize-observer';
|
||||
import type { AutoSizeType } from 'rc-textarea';
|
||||
import toArray from 'rc-util/lib/Children/toArray';
|
||||
import useIsomorphicLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
|
||||
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
|
||||
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
@ -19,13 +19,13 @@ import Editable from '../Editable';
|
||||
import useCopyClick from '../hooks/useCopyClick';
|
||||
import useMergedConfig from '../hooks/useMergedConfig';
|
||||
import usePrevious from '../hooks/usePrevious';
|
||||
import useUpdatedEffect from '../hooks/useUpdatedEffect';
|
||||
import useTooltipProps from '../hooks/useTooltipProps';
|
||||
import type { TypographyProps } from '../Typography';
|
||||
import Typography from '../Typography';
|
||||
import CopyBtn from './CopyBtn';
|
||||
import Ellipsis from './Ellipsis';
|
||||
import EllipsisTooltip from './EllipsisTooltip';
|
||||
import { getEleSize } from './util';
|
||||
import { isEleEllipsis, isValidText } from './util';
|
||||
|
||||
export type BaseType = 'secondary' | 'success' | 'warning' | 'danger';
|
||||
|
||||
@ -162,7 +162,7 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
|
||||
|
||||
// Focus edit icon when back
|
||||
const prevEditing = usePrevious(editing);
|
||||
useUpdatedEffect(() => {
|
||||
useLayoutEffect(() => {
|
||||
if (!editing && prevEditing) {
|
||||
editIconRef.current?.focus();
|
||||
}
|
||||
@ -223,7 +223,7 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
|
||||
[mergedEnableEllipsis, ellipsisConfig, enableEdit, enableCopy],
|
||||
);
|
||||
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
useLayoutEffect(() => {
|
||||
if (enableEllipsis && !needMeasureEllipsis) {
|
||||
setIsLineClampSupport(isStyleSupport('webkitLineClamp'));
|
||||
setIsTextOverflowSupport(isStyleSupport('textOverflow'));
|
||||
@ -246,7 +246,7 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
|
||||
|
||||
// We use effect to change from css ellipsis to js ellipsis.
|
||||
// To make SSR still can see the ellipsis.
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
useLayoutEffect(() => {
|
||||
setCssEllipsis(canUseCssEllipsis && mergedEnableEllipsis);
|
||||
}, [canUseCssEllipsis, mergedEnableEllipsis]);
|
||||
|
||||
@ -281,11 +281,7 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
|
||||
const textEle = typographyRef.current;
|
||||
|
||||
if (enableEllipsis && cssEllipsis && textEle) {
|
||||
const [offsetWidth, offsetHeight] = getEleSize(textEle);
|
||||
|
||||
const currentEllipsis = cssLineClamp
|
||||
? offsetHeight < textEle.scrollHeight
|
||||
: offsetWidth < textEle.scrollWidth;
|
||||
const currentEllipsis = isEleEllipsis(textEle);
|
||||
|
||||
if (isNativeEllipsis !== currentEllipsis) {
|
||||
setIsNativeEllipsis(currentEllipsis);
|
||||
@ -318,40 +314,13 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
|
||||
}, [cssEllipsis, mergedEnableEllipsis]);
|
||||
|
||||
// ========================== Tooltip ===========================
|
||||
let tooltipProps: TooltipProps = {};
|
||||
if (ellipsisConfig.tooltip === true) {
|
||||
tooltipProps = { title: editConfig.text ?? children };
|
||||
} else if (React.isValidElement(ellipsisConfig.tooltip)) {
|
||||
tooltipProps = { title: ellipsisConfig.tooltip };
|
||||
} else if (typeof ellipsisConfig.tooltip === 'object') {
|
||||
tooltipProps = { title: editConfig.text ?? children, ...ellipsisConfig.tooltip };
|
||||
} else {
|
||||
tooltipProps = { title: ellipsisConfig.tooltip };
|
||||
}
|
||||
const topAriaLabel = React.useMemo(() => {
|
||||
const isValid = (val: any): val is string | number => ['string', 'number'].includes(typeof val);
|
||||
const tooltipProps = useTooltipProps(ellipsisConfig.tooltip, editConfig.text, children);
|
||||
|
||||
const topAriaLabel = React.useMemo(() => {
|
||||
if (!enableEllipsis || cssEllipsis) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (isValid(editConfig.text)) {
|
||||
return editConfig.text;
|
||||
}
|
||||
|
||||
if (isValid(children)) {
|
||||
return children;
|
||||
}
|
||||
|
||||
if (isValid(title)) {
|
||||
return title;
|
||||
}
|
||||
|
||||
if (isValid(tooltipProps.title)) {
|
||||
return tooltipProps.title;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return [editConfig.text, children, title, tooltipProps.title].find(isValidText);
|
||||
}, [enableEllipsis, cssEllipsis, title, tooltipProps.title, isMergedEllipsis]);
|
||||
|
||||
// =========================== Render ===========================
|
||||
@ -379,15 +348,7 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
|
||||
// Expand
|
||||
const renderExpand = () => {
|
||||
const { expandable, symbol } = ellipsisConfig;
|
||||
|
||||
if (!expandable) {
|
||||
return null;
|
||||
}
|
||||
if (expanded && expandable !== 'collapsible') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
return expandable ? (
|
||||
<TransButton
|
||||
key="expand"
|
||||
className={`${prefixCls}-${expanded ? 'collapse' : 'expand'}`}
|
||||
@ -396,7 +357,7 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
|
||||
>
|
||||
{typeof symbol === 'function' ? symbol(expanded) : symbol}
|
||||
</TransButton>
|
||||
);
|
||||
) : null;
|
||||
};
|
||||
|
||||
// Edit
|
||||
@ -446,7 +407,6 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
|
||||
};
|
||||
|
||||
const renderOperations = (canEllipsis: boolean) => [
|
||||
// (renderExpanded || ellipsisConfig.collapsible) && renderExpand(),
|
||||
canEllipsis && renderExpand(),
|
||||
renderEdit(),
|
||||
renderCopy(),
|
||||
|
@ -13,20 +13,37 @@ export function getNode(dom: React.ReactNode, defaultNode: React.ReactNode, need
|
||||
}
|
||||
|
||||
/**
|
||||
* Get React of element with precision.
|
||||
* ref: https://github.com/ant-design/ant-design/issues/50143
|
||||
* Check for element is native ellipsis
|
||||
* ref:
|
||||
* - https://github.com/ant-design/ant-design/issues/50143
|
||||
* - https://github.com/ant-design/ant-design/issues/50414
|
||||
*/
|
||||
export function getEleSize(ele: HTMLElement): [width: number, height: number] {
|
||||
export function isEleEllipsis(ele: HTMLElement): boolean {
|
||||
// Create a new div to get the size
|
||||
const childDiv = document.createElement('em');
|
||||
ele.appendChild(childDiv);
|
||||
|
||||
// For test case
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
childDiv.className = 'ant-typography-css-ellipsis-content-measure';
|
||||
}
|
||||
|
||||
const rect = ele.getBoundingClientRect();
|
||||
const { offsetWidth, offsetHeight } = ele;
|
||||
const childRect = childDiv.getBoundingClientRect();
|
||||
|
||||
let returnWidth = offsetWidth;
|
||||
let returnHeight = offsetHeight;
|
||||
// Reset
|
||||
ele.removeChild(childDiv);
|
||||
|
||||
if (Math.abs(offsetWidth - rect.width) < 1 && Math.abs(offsetHeight - rect.height) < 1) {
|
||||
returnWidth = rect.width;
|
||||
returnHeight = rect.height;
|
||||
// Range checker
|
||||
return (
|
||||
// Horizontal out of range
|
||||
rect.left > childRect.left ||
|
||||
childRect.right > rect.right ||
|
||||
// Vertical out of range
|
||||
rect.top > childRect.top ||
|
||||
childRect.bottom > rect.bottom
|
||||
);
|
||||
}
|
||||
|
||||
return [returnWidth, returnHeight];
|
||||
}
|
||||
export const isValidText = (val: any): val is string | number =>
|
||||
['string', 'number'].includes(typeof val);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import EnterOutlined from '@ant-design/icons/EnterOutlined';
|
||||
import classNames from 'classnames';
|
||||
import type { AutoSizeType } from 'rc-textarea';
|
||||
import type { TextAreaProps } from 'rc-textarea';
|
||||
import KeyCode from 'rc-util/lib/KeyCode';
|
||||
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
@ -21,7 +21,7 @@ interface EditableProps {
|
||||
style?: React.CSSProperties;
|
||||
direction?: DirectionType;
|
||||
maxLength?: number;
|
||||
autoSize?: boolean | AutoSizeType;
|
||||
autoSize?: TextAreaProps['autoSize'];
|
||||
enterIcon?: React.ReactNode;
|
||||
component?: string;
|
||||
}
|
||||
@ -94,28 +94,27 @@ const Editable: React.FC<EditableProps> = (props) => {
|
||||
}) => {
|
||||
// Check if it's a real key
|
||||
if (
|
||||
lastKeyCode.current === keyCode &&
|
||||
!inComposition.current &&
|
||||
!ctrlKey &&
|
||||
!altKey &&
|
||||
!metaKey &&
|
||||
!shiftKey
|
||||
lastKeyCode.current !== keyCode ||
|
||||
inComposition.current ||
|
||||
ctrlKey ||
|
||||
altKey ||
|
||||
metaKey ||
|
||||
shiftKey
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (keyCode === KeyCode.ENTER) {
|
||||
confirmChange();
|
||||
onEnd?.();
|
||||
} else if (keyCode === KeyCode.ESC) {
|
||||
onCancel();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onBlur: React.FocusEventHandler<HTMLTextAreaElement> = () => {
|
||||
confirmChange();
|
||||
};
|
||||
|
||||
const textClassName = component ? `${prefixCls}-${component}` : '';
|
||||
|
||||
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);
|
||||
|
||||
const textAreaClassName = classNames(
|
||||
@ -123,9 +122,9 @@ const Editable: React.FC<EditableProps> = (props) => {
|
||||
`${prefixCls}-edit-content`,
|
||||
{
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-${component}`]: !!component,
|
||||
},
|
||||
className,
|
||||
textClassName,
|
||||
hashId,
|
||||
cssVarCls,
|
||||
);
|
||||
|
@ -19,7 +19,6 @@ const Text: React.ForwardRefRenderFunction<HTMLSpanElement, TextProps> = (
|
||||
if (ellipsis && typeof ellipsis === 'object') {
|
||||
return omit(ellipsis as EllipsisConfig, ['expandable', 'rows']);
|
||||
}
|
||||
|
||||
return ellipsis;
|
||||
}, [ellipsis]);
|
||||
|
||||
|
@ -17,8 +17,6 @@ export interface TitleProps
|
||||
|
||||
const Title = React.forwardRef<HTMLElement, TitleProps>((props, ref) => {
|
||||
const { level = 1, ...restProps } = props;
|
||||
let component: keyof JSX.IntrinsicElements;
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const warning = devUseWarning('Typography.Title');
|
||||
|
||||
@ -28,13 +26,9 @@ const Title = React.forwardRef<HTMLElement, TitleProps>((props, ref) => {
|
||||
'Title only accept `1 | 2 | 3 | 4 | 5` as `level` value. And `5` need 4.6.0+ version.',
|
||||
);
|
||||
}
|
||||
|
||||
if (TITLE_ELE_LIST.includes(level)) {
|
||||
component = `h${level}`;
|
||||
} else {
|
||||
component = 'h1';
|
||||
}
|
||||
|
||||
const component: keyof JSX.IntrinsicElements = TITLE_ELE_LIST.includes(level)
|
||||
? `h${level}`
|
||||
: `h1`;
|
||||
return <Base ref={ref} {...restProps} component={component} />;
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
|
||||
import { devUseWarning } from '../_util/warning';
|
||||
import type { ConfigConsumerProps, DirectionType } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
@ -42,6 +41,7 @@ const Typography = React.forwardRef<
|
||||
style,
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
const {
|
||||
getPrefixCls,
|
||||
direction: contextDirection,
|
||||
@ -49,23 +49,16 @@ const Typography = React.forwardRef<
|
||||
} = React.useContext<ConfigConsumerProps>(ConfigContext);
|
||||
|
||||
const direction = typographyDirection ?? contextDirection;
|
||||
|
||||
let mergedRef = ref;
|
||||
if (setContentRef) {
|
||||
mergedRef = composeRef(ref, setContentRef);
|
||||
}
|
||||
const mergedRef = setContentRef ? composeRef(ref, setContentRef) : ref;
|
||||
const prefixCls = getPrefixCls('typography', customizePrefixCls);
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const warning = devUseWarning('Typography');
|
||||
|
||||
warning.deprecated(!setContentRef, 'setContentRef', 'ref');
|
||||
}
|
||||
|
||||
const prefixCls = getPrefixCls('typography', customizePrefixCls);
|
||||
|
||||
// Style
|
||||
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);
|
||||
|
||||
const componentClassName = classNames(
|
||||
prefixCls,
|
||||
typography?.className,
|
||||
@ -92,5 +85,4 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
Typography.displayName = 'Typography';
|
||||
}
|
||||
|
||||
// es default export should use const instead of let
|
||||
export default Typography;
|
||||
|
@ -364,29 +364,48 @@ describe('Typography.Ellipsis', () => {
|
||||
describe('should tooltip support', () => {
|
||||
let domSpy: ReturnType<typeof spyElementPrototypes>;
|
||||
|
||||
let containerWidth = 100;
|
||||
let contentWidth = 200;
|
||||
let rectContainerWidth = 100;
|
||||
let containerRect = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 100,
|
||||
bottom: 22,
|
||||
};
|
||||
let measureRect = {
|
||||
left: 200,
|
||||
top: 0,
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
domSpy = spyElementPrototypes(HTMLElement, {
|
||||
offsetWidth: {
|
||||
get: () => containerWidth,
|
||||
getBoundingClientRect() {
|
||||
if (
|
||||
(this as unknown as HTMLElement).classList.contains(
|
||||
'ant-typography-css-ellipsis-content-measure',
|
||||
)
|
||||
) {
|
||||
return {
|
||||
...measureRect,
|
||||
right: measureRect.left,
|
||||
bottom: measureRect.top + 22,
|
||||
};
|
||||
}
|
||||
|
||||
return containerRect;
|
||||
},
|
||||
scrollWidth: {
|
||||
get: () => contentWidth,
|
||||
},
|
||||
getBoundingClientRect: () => ({
|
||||
width: rectContainerWidth,
|
||||
height: 0,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
containerWidth = 100;
|
||||
contentWidth = 200;
|
||||
rectContainerWidth = 100;
|
||||
containerRect = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 100,
|
||||
bottom: 22,
|
||||
};
|
||||
measureRect = {
|
||||
left: 200,
|
||||
top: 0,
|
||||
};
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
@ -453,11 +472,11 @@ describe('Typography.Ellipsis', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('precision', () => {
|
||||
// https://github.com/ant-design/ant-design/issues/50143
|
||||
it('precision', async () => {
|
||||
containerWidth = 100;
|
||||
contentWidth = 100;
|
||||
rectContainerWidth = 99.9;
|
||||
it('should show', async () => {
|
||||
containerRect.right = 99.9;
|
||||
measureRect.left = 100;
|
||||
|
||||
const { container, baseElement } = await getWrapper({
|
||||
title: true,
|
||||
@ -465,10 +484,28 @@ describe('Typography.Ellipsis', () => {
|
||||
});
|
||||
fireEvent.mouseEnter(container.firstChild!);
|
||||
|
||||
await waitFor(() => {
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(container.querySelector('.tooltip-class-name')).toBeTruthy();
|
||||
expect(baseElement.querySelector('.ant-tooltip-open')).not.toBeNull();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/50414
|
||||
it('should not show', async () => {
|
||||
containerRect.right = 48.52;
|
||||
measureRect.left = 48.52;
|
||||
|
||||
const { container, baseElement } = await getWrapper({
|
||||
title: true,
|
||||
className: 'tooltip-class-name',
|
||||
});
|
||||
fireEvent.mouseEnter(container.firstChild!);
|
||||
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(container.querySelector('.tooltip-class-name')).toBeTruthy();
|
||||
expect(baseElement.querySelector('.ant-tooltip-open')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -490,22 +527,26 @@ describe('Typography.Ellipsis', () => {
|
||||
|
||||
it('should display tooltip if line clamp', async () => {
|
||||
mockRectSpy = spyElementPrototypes(HTMLElement, {
|
||||
scrollHeight: {
|
||||
get() {
|
||||
let html = (this as any).innerHTML;
|
||||
html = html.replace(/<[^>]*>/g, '');
|
||||
const lines = Math.ceil(html.length / LINE_STR_COUNT);
|
||||
return lines * 16;
|
||||
},
|
||||
},
|
||||
offsetHeight: {
|
||||
get: () => 32,
|
||||
},
|
||||
offsetWidth: {
|
||||
get: () => 100,
|
||||
},
|
||||
scrollWidth: {
|
||||
get: () => 100,
|
||||
getBoundingClientRect() {
|
||||
if (
|
||||
(this as unknown as HTMLElement).classList.contains(
|
||||
'ant-typography-css-ellipsis-content-measure',
|
||||
)
|
||||
) {
|
||||
return {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 100,
|
||||
bottom: 122,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
left: 0,
|
||||
right: 100,
|
||||
top: 0,
|
||||
bottom: 22 * 3,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@ -527,6 +568,32 @@ describe('Typography.Ellipsis', () => {
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/46580
|
||||
it('dynamic to be ellipsis should show tooltip', async () => {
|
||||
let dynamicWidth = 100;
|
||||
|
||||
mockRectSpy = spyElementPrototypes(HTMLElement, {
|
||||
getBoundingClientRect() {
|
||||
if (
|
||||
(this as unknown as HTMLElement).classList.contains(
|
||||
'ant-typography-css-ellipsis-content-measure',
|
||||
)
|
||||
) {
|
||||
return {
|
||||
left: 0,
|
||||
right: dynamicWidth,
|
||||
top: 0,
|
||||
bottom: 22,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
left: 100,
|
||||
right: 100,
|
||||
top: 0,
|
||||
bottom: 22,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const ref = React.createRef<HTMLElement>();
|
||||
render(
|
||||
<Base ellipsis={{ tooltip: 'bamboo' }} component="p" ref={ref}>
|
||||
@ -535,8 +602,7 @@ describe('Typography.Ellipsis', () => {
|
||||
);
|
||||
|
||||
// Force to narrow
|
||||
offsetWidth = 1;
|
||||
scrollWidth = 100;
|
||||
dynamicWidth = 50;
|
||||
triggerResize(ref.current!);
|
||||
|
||||
await waitFakeTimer();
|
||||
@ -544,6 +610,8 @@ describe('Typography.Ellipsis', () => {
|
||||
fireEvent.mouseEnter(ref.current!);
|
||||
await waitFakeTimer();
|
||||
expect(document.querySelector('.ant-tooltip')).toBeTruthy();
|
||||
|
||||
mockRectSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('not force single line if expanded', async () => {
|
||||
@ -565,4 +633,15 @@ describe('Typography.Ellipsis', () => {
|
||||
rerender(renderDemo(true));
|
||||
expect(container.querySelector('.ant-typography-collapse')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('no dead loop', () => {
|
||||
const tooltipObj: any = {};
|
||||
tooltipObj.loop = tooltipObj;
|
||||
|
||||
render(
|
||||
<Base ellipsis={{ tooltip: tooltipObj }} component="p">
|
||||
{fullStr}
|
||||
</Base>,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
22
components/typography/hooks/useTooltipProps.ts
Normal file
22
components/typography/hooks/useTooltipProps.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { isValidElement, useMemo } from 'react';
|
||||
import type { TooltipProps } from '../../tooltip';
|
||||
|
||||
const useTooltipProps = (
|
||||
tooltip: React.ReactNode | TooltipProps,
|
||||
editConfigText: React.ReactNode,
|
||||
children: React.ReactNode,
|
||||
) =>
|
||||
useMemo(() => {
|
||||
if (tooltip === true) {
|
||||
return { title: editConfigText ?? children };
|
||||
}
|
||||
if (isValidElement(tooltip)) {
|
||||
return { title: tooltip };
|
||||
}
|
||||
if (typeof tooltip === 'object') {
|
||||
return { title: editConfigText ?? children, ...tooltip };
|
||||
}
|
||||
return { title: tooltip };
|
||||
}, [tooltip, editConfigText, children]);
|
||||
|
||||
export default useTooltipProps;
|
@ -1,16 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
/** Similar with `useEffect` but only trigger after mounted */
|
||||
const useUpdatedEffect = (callback: () => void, conditions?: React.DependencyList) => {
|
||||
const mountRef = React.useRef(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (mountRef.current) {
|
||||
callback();
|
||||
} else {
|
||||
mountRef.current = true;
|
||||
}
|
||||
}, conditions);
|
||||
};
|
||||
|
||||
export default useUpdatedEffect;
|
@ -176,8 +176,8 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
- name: Setup bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
|
@ -180,7 +180,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js (设置 node 版本)
|
||||
uses: actions/setup-node@v3
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
|
@ -5,37 +5,24 @@ order: 1
|
||||
title: CSS Compatible
|
||||
---
|
||||
|
||||
Ant Design supports the last 2 versions of modern browsers. If you need to be compatible with legacy browsers, please perform downgrade processing according to actual needs:
|
||||
### Default Style Compatibility
|
||||
|
||||
## StyleProvider
|
||||
Ant Design supports the [last 2 versions of modern browsers](https://browsersl.ist/#q=defaults). If you need to be compatible with legacy browsers, please perform downgrade processing according to actual needs:
|
||||
|
||||
Please ref [`@ant-design/cssinjs`](https://github.com/ant-design/cssinjs#styleprovider).
|
||||
| Feature | antd version | Compatibility | Minimum Chrome Version | Compatibility workaround |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| [:where Selector](https://developer.mozilla.org/en-US/docs/Web/CSS/:where) | `>=5.0.0` | [caniuse](https://caniuse.com/?search=%3Awhere) | Chrome 88 | `<StyleProvider hashPriority="high">` |
|
||||
| [CSS Logical Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties) | `>=5.0.0` | [caniuse](https://caniuse.com/css-logical-props) | Chrome 89 | `<StyleProvider transformers={[legacyLogicalPropertiesTransformer]}>` |
|
||||
|
||||
## `layer` Downgrade
|
||||
If you need to support older browsers, please use [StyleProvider](https://github.com/ant-design/cssinjs#styleprovider) for degradation handling according to your actual requirements.
|
||||
|
||||
Ant Design supports configuring `layer` for unified downgrade since `5.17.0`. After the downgrade, the style of antd will always be lower than the default CSS selector priority, so that users can override the style (please be sure to check the browser compatibility of `@layer`):
|
||||
## `:where` in selector
|
||||
|
||||
```tsx
|
||||
import { StyleProvider } from '@ant-design/cssinjs';
|
||||
|
||||
export default () => (
|
||||
<StyleProvider layer>
|
||||
<MyApp />
|
||||
</StyleProvider>
|
||||
);
|
||||
```
|
||||
|
||||
antd styles will be encapsulated in `@layer` to lower the priority:
|
||||
|
||||
```diff
|
||||
++ @layer antd {
|
||||
:where(.css-bAMboO).ant-btn {
|
||||
color: #fff;
|
||||
}
|
||||
++ }
|
||||
```
|
||||
|
||||
## Compatible adjustment
|
||||
- antd version: `>=5.0.0`
|
||||
- MDN: [:where](https://developer.mozilla.org/en-US/docs/Web/CSS/:where)
|
||||
- Browser Compatibility: [caniuse](https://caniuse.com/?search=%3Awhere)
|
||||
- Minimum Chrome Version Supported: 88
|
||||
- Default Enabled: Yes
|
||||
|
||||
The CSS-in-JS feature of Ant Design uses the ":where" selector by default to lower the CSS selector specificity, reducing the additional cost of adjusting custom styles when upgrading for users. However, the compatibility of the ":where" syntax is relatively poor in older browsers ([compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/:where#browser_compatibility)). In certain scenarios, if you need to support older browsers, you can use `@ant-design/cssinjs` to disable the default lowering of specificity (please ensure version consistency with antd).
|
||||
|
||||
@ -77,6 +64,12 @@ Raise priority through plugin:
|
||||
|
||||
## CSS Logical Properties
|
||||
|
||||
- antd version: `>=5.0.0`
|
||||
- MDN:[CSS Logical Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties)
|
||||
- Browser Compatibility: [caniuse](https://caniuse.com/css-logical-props)
|
||||
- Minimum Chrome Version Supported: 89
|
||||
- Default Enabled: Yes
|
||||
|
||||
To unify LTR and RTL styles, Ant Design uses CSS logical properties. For example, the original `margin-left` is replaced by `margin-inline-start`, so that it is the starting position spacing under both LTR and RTL. If you need to be compatible with older browsers, you can configure `transformers` through the `StyleProvider` of `@ant-design/cssinjs`:
|
||||
|
||||
```tsx
|
||||
@ -102,6 +95,36 @@ When toggled, styles will downgrade CSS logical properties:
|
||||
}
|
||||
```
|
||||
|
||||
## `@layer`
|
||||
|
||||
- antd version: `>=5.17.0`
|
||||
- MDN:[CSS @layer](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer)
|
||||
- Browser Compatibility: [caniuse](https://caniuse.com/css-at-rule-layer)
|
||||
- Minimum Chrome Version Supported: 99
|
||||
- Default Enabled: No
|
||||
|
||||
Ant Design supports configuring `@layer` for unified css priority downgrade since `5.17.0`. After the downgrade, the style of antd will always be lower than the default CSS selector priority, so that users can override the style (please be sure to check the browser compatibility of `@layer`):
|
||||
|
||||
```tsx
|
||||
import { StyleProvider } from '@ant-design/cssinjs';
|
||||
|
||||
export default () => (
|
||||
<StyleProvider layer>
|
||||
<MyApp />
|
||||
</StyleProvider>
|
||||
);
|
||||
```
|
||||
|
||||
antd styles will be encapsulated in `@layer` to lower the priority:
|
||||
|
||||
```diff
|
||||
++ @layer antd {
|
||||
:where(.css-bAMboO).ant-btn {
|
||||
color: #fff;
|
||||
}
|
||||
++ }
|
||||
```
|
||||
|
||||
## Rem Adaptation
|
||||
|
||||
In responsive web development, there is a need for a convenient and flexible way to achieve page adaptation and responsive design. The `px2remTransformer` transformer can quickly and accurately convert pixel units in style sheets to rem units relative to the root element (HTML tag), enabling the implementation of adaptive and responsive layouts.
|
||||
|
@ -5,38 +5,25 @@ order: 1
|
||||
title: 样式兼容
|
||||
---
|
||||
|
||||
Ant Design 支持最近 2 个版本的现代浏览器。如果你需要兼容旧版浏览器,请根据实际需求进行降级处理:
|
||||
## 默认样式兼容性说明
|
||||
|
||||
## StyleProvider
|
||||
Ant Design 5.x 支持[最近 2 个版本的现代浏览器](https://browsersl.ist/#q=defaults)。默认情况下,我们使用了一些现代 CSS 特性来提高样式的可维护性和可扩展性,这些特性在旧版浏览器中可能不被支持,好在我们可以通过一些降级兼容方案来解决。
|
||||
|
||||
查看 [`@ant-design/cssinjs`](https://github.com/ant-design/cssinjs#styleprovider).
|
||||
| 特性 | antd 版本 | 兼容性 | 最低 Chrome 版本 | 降级兼容方案 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| [:where 选择器](https://developer.mozilla.org/en-US/docs/Web/CSS/:where) | `>=5.0.0` | [caniuse](https://caniuse.com/?search=%3Awhere) | Chrome 88 | `<StyleProvider hashPriority="high">` |
|
||||
| [CSS 逻辑属性](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties) | `>=5.0.0` | [caniuse](https://caniuse.com/css-logical-props) | Chrome 89 | `<StyleProvider transformers={[legacyLogicalPropertiesTransformer]}>` |
|
||||
|
||||
## `layer` 降权
|
||||
|
||||
Ant Design 从 `5.17.0` 起支持配置 `layer` 进行统一降权。经过降权后,antd 的样式将始终低于默认的 CSS 选择器优先级,以便于用户进行样式覆盖(请务必注意检查 `@layer` 浏览器兼容性):
|
||||
|
||||
```tsx
|
||||
import { StyleProvider } from '@ant-design/cssinjs';
|
||||
|
||||
export default () => (
|
||||
<StyleProvider layer>
|
||||
<MyApp />
|
||||
</StyleProvider>
|
||||
);
|
||||
```
|
||||
|
||||
antd 的样式会被封装在 `@layer` 中,以降低优先级:
|
||||
|
||||
```diff
|
||||
++ @layer antd {
|
||||
:where(.css-bAMboO).ant-btn {
|
||||
color: #fff;
|
||||
}
|
||||
++ }
|
||||
```
|
||||
如果你需要兼容旧版浏览器,请根据实际需求使用 [StyleProvider](https://github.com/ant-design/cssinjs#styleprovider) 降级处理。
|
||||
|
||||
## `:where` 选择器
|
||||
|
||||
- 支持版本:`>=5.0.0`
|
||||
- MDN 文档:[:where](https://developer.mozilla.org/en-US/docs/Web/CSS/:where)
|
||||
- 浏览器兼容性:[caniuse](https://caniuse.com/?search=%3Awhere)
|
||||
- Chrome 最低支持版本:88
|
||||
- 默认启用:是
|
||||
|
||||
Ant Design 的 CSS-in-JS 默认通过 `:where` 选择器降低 CSS Selector 优先级,以减少用户升级时额外调整自定义样式的成本,不过 `:where` 语法的[兼容性](https://developer.mozilla.org/en-US/docs/Web/CSS/:where#browser_compatibility)在低版本浏览器比较差。在某些场景下你如果需要支持旧版浏览器,你可以使用 `@ant-design/cssinjs` 取消默认的降权操作(请注意版本保持与 antd 一致):
|
||||
|
||||
```tsx
|
||||
@ -77,9 +64,15 @@ export default () => (
|
||||
|
||||
## CSS 逻辑属性
|
||||
|
||||
- 支持版本:`>=5.0.0`
|
||||
- MDN 文档:[:where](https://developer.mozilla.org/en-US/docs/Web/CSS/:where)
|
||||
- 浏览器兼容性:[caniuse](https://caniuse.com/css-logical-props)
|
||||
- Chrome 最低支持版本:89
|
||||
- 默认启用:是
|
||||
|
||||
为了统一 LTR 和 RTL 样式,Ant Design 使用了 CSS 逻辑属性。例如原 `margin-left` 使用 `margin-inline-start` 代替,使其在 LTR 和 RTL 下都为起始位置间距。如果你需要兼容旧版浏览器(如 360 浏览器、QQ 浏览器 等等),可以通过 `@ant-design/cssinjs` 的 `StyleProvider` 配置 `transformers` 将其转换:
|
||||
|
||||
```tsx
|
||||
```tsx | pure
|
||||
import { legacyLogicalPropertiesTransformer, StyleProvider } from '@ant-design/cssinjs';
|
||||
|
||||
// `transformers` 提供预处理功能将样式进行转换
|
||||
@ -102,11 +95,41 @@ export default () => (
|
||||
}
|
||||
```
|
||||
|
||||
## `@layer` 样式优先级降权
|
||||
|
||||
- 支持版本:`>=5.17.0`
|
||||
- MDN 文档:[@layer](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer)
|
||||
- 浏览器兼容性:[caniuse](https://caniuse.com/?search=%40layer)
|
||||
- Chrome 最低支持版本:99
|
||||
- 默认启用:否
|
||||
|
||||
Ant Design 从 `5.17.0` 起支持配置 `layer` 进行统一降权。经过降权后,antd 的样式将始终低于默认的 CSS 选择器优先级,以便于用户进行样式覆盖(请务必注意检查 `@layer` 浏览器兼容性):
|
||||
|
||||
```tsx | pure
|
||||
import { StyleProvider } from '@ant-design/cssinjs';
|
||||
|
||||
export default () => (
|
||||
<StyleProvider layer>
|
||||
<MyApp />
|
||||
</StyleProvider>
|
||||
);
|
||||
```
|
||||
|
||||
antd 的样式会被封装在 `@layer` 中,以降低优先级:
|
||||
|
||||
```diff
|
||||
++ @layer antd {
|
||||
:where(.css-bAMboO).ant-btn {
|
||||
color: #fff;
|
||||
}
|
||||
++ }
|
||||
```
|
||||
|
||||
## rem 适配
|
||||
|
||||
在响应式网页开发中,需要一种方便且灵活的方式来实现页面的适配和响应式设计。`px2remTransformer` 转换器可以快速而准确地将样式表中的像素单位转换为相对于根元素(HTML 标签)的 rem 单位,实现页面的自适应和响应式布局。
|
||||
|
||||
```tsx
|
||||
```tsx | pure
|
||||
import { px2remTransformer, StyleProvider } from '@ant-design/cssinjs';
|
||||
|
||||
const px2rem = px2remTransformer({
|
||||
|
@ -58,7 +58,7 @@ However, after enabling CSS variables, the component styles of the same antd ver
|
||||
</ConfigProvider>
|
||||
```
|
||||
|
||||
By the way, we strongly recommend using `extractStyle` to extract static styles, which will bring a certain amount of performance improvement to the application.
|
||||
By the way, we strongly recommend using [extractStyle](/docs/react/server-side-rendering) to extract static styles, which will bring a certain amount of performance improvement to the application.
|
||||
|
||||
### Customize Theme
|
||||
|
||||
|
@ -58,7 +58,7 @@ hash 是 Ant Design 5.0 以来的特性之一,其功能是为每一份主题
|
||||
</ConfigProvider>
|
||||
```
|
||||
|
||||
同时我们非常推荐使用 `extractStyle` 来抽取静态样式,这样会为应用性能带来一定量的提升。
|
||||
同时我们非常推荐使用 [extractStyle](/docs/react/server-side-rendering-cn) 来抽取静态样式,这样会为应用性能带来一定量的提升。
|
||||
|
||||
### 修改主题
|
||||
|
||||
|
@ -221,7 +221,7 @@ const App: React.FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ColorPicker showText value={primary} onChangeComplete={(color) => setPrimary(color.toHexString())} />
|
||||
<ColorPicker showText value={primary} onChange={(color) => setPrimary(color.toHexString())} />
|
||||
<Divider />
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
|
@ -221,7 +221,7 @@ const App: React.FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ColorPicker showText value={primary} onChangeComplete={(color) => setPrimary(color.toHexString())} />
|
||||
<ColorPicker showText value={primary} onChange={(color) => setPrimary(color.toHexString())} />
|
||||
<Divider />
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user