Compare commits

...

316 Commits

Author SHA1 Message Date
thinkasany
03abfcbcbc refactor(cssinjs): simplify transition style generation (#56901) 2026-02-08 16:29:10 +08:00
github-actions[bot]
b34dd64fbe chore: auto merge branches (#56899)
chore: sync master into feature
2026-02-08 07:58:29 +00:00
thinkasany
353fd98d99 chore: sync master into feature 2026-02-08 15:45:42 +08:00
thinkasany
cee829816a fix: add drawer styles.content / classNames.content warning (#56898) 2026-02-08 15:44:06 +08:00
renovate[bot]
19d7bfb1d5 chore(deps): update dependency @types/node 25.2.2 (#56822)
* chore(deps): update dependency @types/react to v19.2.10

* Update package.json

Signed-off-by: thinkasany <480968828@qq.com>

* bump @types/node

---------

Signed-off-by: thinkasany <480968828@qq.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-02-08 14:27:13 +08:00
renovate[bot]
38be7055d1 chore(deps): update dependency @eslint-react/eslint-plugin to v2.12.2 (#56897)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-02-08 13:33:32 +08:00
lijianan
8e7ff042cd Revert "site(a11y): apply prefers-reduced-motion handling for transitions (#5…" (#56896)
This reverts commit 191db91774.
2026-02-08 13:32:54 +08:00
thinkasany
7cb9ca9336 chore: bump @antfu/eslint-config 7.3.0 (#56895) 2026-02-07 22:53:24 +08:00
github-actions[bot]
114efa023b chore: auto merge branches (#56892)
chore: sync master into feature
2026-02-07 09:13:51 +00:00
lijianan
72c0b2c8ae refactor(cssinjs): simplify transition style generation (#56894)
* refactor(cssinjs): simplify transition style generation

* refactor(cssinjs): simplify transition style generation
2026-02-07 16:31:57 +08:00
thinkasany
e9005a6260 Merge remote-tracking branch 'origin/master' into feature-merge-master 2026-02-07 15:17:41 +08:00
thinkasany
c9e3977d63 chore: bump eslint@10.0.0 (#56893) 2026-02-07 15:17:12 +08:00
thinkasany
6b0387fe3d chore: sync master into feature 2026-02-07 14:46:03 +08:00
thinkasany
efb64f513f fix: Allow skip demo image (#56890)
Co-authored-by: lijianan <574980606@qq.com>
2026-02-07 11:51:29 +08:00
github-actions[bot]
776aa384e8 chore: upgrade deps (#56891)
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
2026-02-07 02:37:10 +08:00
dependabot[bot]
827be4cceb chore: bump antd-img-crop from 4.27.0 to 4.28.0 in the dev-dependencies group (#56878)
* bump antd-img-crop

* refactor accessibilityDemoTest

* fix

---------

Co-authored-by: thinkasany <480968828@qq.com>
2026-02-06 23:36:54 +08:00
dependabot[bot]
1b36dafadc chore: bump @eslint-react/eslint-plugin from 2.9.4 to 2.11.0 (#56879)
* chore: bump @eslint-react/eslint-plugin from 2.9.4 to 2.11.0

Bumps [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react/tree/HEAD/packages/plugins/eslint-plugin) from 2.9.4 to 2.11.0.
- [Release notes](https://github.com/Rel1cx/eslint-react/releases)
- [Changelog](https://github.com/Rel1cx/eslint-react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Rel1cx/eslint-react/commits/v2.11.0/packages/plugins/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@eslint-react/eslint-plugin"
  dependency-version: 2.11.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* turn off react/no-implicit-key

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-02-06 21:11:17 +08:00
github-actions[bot]
6ae303e972 chore: auto merge branches (#56886)
chore: sync master into feature
2026-02-06 09:10:19 +00:00
二货机器人
ce4c939910 test(modal): add mouseDown event before click to fix mask click tests 2026-02-06 16:56:00 +08:00
thinkasany
d6f58cbe28 Merge remote-tracking branch 'origin/master' into feature-merge-master 2026-02-06 14:56:47 +08:00
thinkasany
b08961831d chore: bump @rc-component/dialog 2026-02-06 14:43:47 +08:00
二货爱吃白萝卜
2950b7da98 fix(tooltip): rename arrow-offset-horizontal to arrow-offset-x CSS variable to fix transformOrigin (#56887)
The variable name was inconsistent and caused transformOrigin to not properly reference
the arrow position for animations.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 14:37:55 +08:00
thinkasany
038759b7ee chore: sync master into feature 2026-02-06 14:19:29 +08:00
github-actions[bot]
8a16c49f5c chore: upgrade deps (#56877)
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-02-06 13:36:50 +08:00
二货爱吃白萝卜
bbb0683ab6 fix(button): add theme-aware preset color hover/active tokens (#56872)
* fix(button): add theme-aware preset color hover/active tokens

Add ${colorKey}Hover and ${colorKey}Active tokens that swap values based on dark/light mode for improved contrast and user experience.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add preset color hover/active tokens for consistent button interaction

- Add xxxHover and xxxActive tokens for preset colors in genColorMapToken
- Override these tokens in dark mode to swap hover/active values
- Update ButtonToken type to include PresetColorHoverActiveMap
- Update button variant styles to use new hover/active tokens
- Fix #56656: button hover/active state inconsistency in dark mode

* test: add @csstools to compileModules

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: support .mjs files in Jest configuration

- Updated .jest.js transform pattern to include .mjs files
- Simplified .jest.node.js transform patterns
- Added jest-mjs-transformer.js for babel-jest mjs handling

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: remove unused jest-mjs-transformer.js

- Removed jest-mjs-transformer.js as it's no longer used
- .mjs files are now handled by the updated transform patterns in Jest configs

* test: add .mjs support to .jest.image.js

- Updated .jest.image.js transform pattern to include .mjs files
- fixes image test failures due to ES module parsing errors

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 11:10:41 +08:00
二货爱吃白萝卜
99eb877828 test(modal): add mouseDown event before click in test (#56874)
* test(modal): add mouseDown event before click in test

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test(modal): add mouseDown event before click in confirm test

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test(modal): improve confirm test event firing and timer management

- Add mouseDown event before click for mask interaction
- Use fireEvent consistently instead of direct click()
- Properly setup and cleanup fake timers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 22:13:14 +08:00
luozz
c7b3b38cbd feat: Add global maskClosable configuration capability to Modal and Drawer (#56739)
* feat: Add global maskClosable configuration capability to Modal and Drawer

* Update components/modal/__tests__/Modal.test.tsx

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: luozz <15761695277@163.com>

* feat: Add mask.closable capability to Drawer and Modal

* fix: Preserve the original maskClosable capabilities of ConfigDialog

* feat: Added mask handling for Modal Hooks

* rerun

* fix

* docs: update version

* fix: improve mask config merging logic

- Move default values to the beginning of mergedConfig to ensure user props override them
- Validate maskClosable type with double negation
- Add maskClosable to mergedProps in Drawer and Modal

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: use normalizeMaskConfig in ConfirmDialog

- Export normalizeMaskConfig for external use
- Simplify mask config logic in ConfirmDialog using shared utility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: adjust

* fix: simplify closable default value assignment

removed: mergedConfig.closable = mergedConfig.closable ?? true;
added: closable: maskConfig.closable ?? maskClosable ?? contextMaskConfig.closable ?? true

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Signed-off-by: luozz <15761695277@163.com>
Co-authored-by: 罗忠泽 <victor.luo@spotterio.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
Co-authored-by: 二货机器人 <smith3816@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 16:23:18 +08:00
github-actions[bot]
b8b9938263 chore: auto merge branches (#56870)
chore: feature merge master
2026-02-05 07:40:39 +00:00
二货爱吃白萝卜
9673185fbf chore: lock antd-img-crop to ~4.27.0 (#56869)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 15:20:44 +08:00
Wanpan
d2d124d305 fix(upload): add height to picture card style (#56864) 2026-02-05 09:55:48 +08:00
thinkasany
8b3722f21f chore: improve spin (#56865)
* chore: improve spin

* fix

* fix

* fix
2026-02-05 09:54:36 +08:00
github-actions[bot]
d5457c75f9 chore: auto merge branches (#56863)
chore: sync master into feature
2026-02-04 13:07:40 +00:00
thinkasany
ee4220b676 chore: sync master into feature 2026-02-04 20:45:32 +08:00
二货爱吃白萝卜
6e91b7f8af docs: Use direct dumi code demo instead of sandpack (#56862)
* docs(replace): replace sandpack demos with code blocks in customize-theme.en-US.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs: update demo paths in theme customization docs

* docs: add first example demo and update documentation

* docs(demo): add Radio component to disable-motion example

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(demo): initialize timerRef with null for proper typing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 20:37:02 +08:00
Tarek BAZINE
d442e3e1a6 fix: Update reference URL in font specification (#56859)
* fix: Update reference URL in font specification

Signed-off-by: Tarek BAZINE <et_bazine@esi.dz>

* Update docs/spec/font.en-US.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: 二货爱吃白萝卜 <smith3816@gmail.com>

* Update references in font specification document

Signed-off-by: 二货爱吃白萝卜 <smith3816@gmail.com>

---------

Signed-off-by: Tarek BAZINE <et_bazine@esi.dz>
Signed-off-by: 二货爱吃白萝卜 <smith3816@gmail.com>
Co-authored-by: 二货爱吃白萝卜 <smith3816@gmail.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-04 17:46:35 +08:00
二货爱吃白萝卜
46fe122165 refactor: Spin support fully semantic (#56852)
* refactor: unique spin dom structure

* feat: update spin semantic preview and documentation

* test: update snapshot

* refactor: update Spin component with deprecation warnings

* refactor: replace spin tip prop with description and update tests

* refactor: deprecate wrapperClassName in favor of classNames.root

* fix: reorder spin component semantics

* test: update spin demo semantic snapshot

* test: add semantic test cases for spin component

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-02-04 15:30:56 +08:00
lijianan
1a789f1e5b refactor(cssinjs): simplify transition style generation (#56826)
* chore: code optimization

* update
2026-02-04 14:54:08 +08:00
dependabot[bot]
45d8f9a2d4 chore: bump @eslint-react/eslint-plugin from 2.9.3 to 2.9.4 (#56850)
Bumps [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react/tree/HEAD/packages/plugins/eslint-plugin) from 2.9.3 to 2.9.4.
- [Release notes](https://github.com/Rel1cx/eslint-react/releases)
- [Changelog](https://github.com/Rel1cx/eslint-react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Rel1cx/eslint-react/commits/v2.9.4/packages/plugins/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@eslint-react/eslint-plugin"
  dependency-version: 2.9.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-04 09:11:56 +08:00
Guo Yunhe
f44ec04609 feat(Grid): add xxxl (1920px) breakpoint for FHD screen (#56825)
* feat(Grid): add xxxl (1840px) breakpoint for FHD screen

* feat: update xxxl breakpoint from 1840px to 1920px for layout and list components

* feat(Grid): update xxxl breakpoint from 2000px to 1920px for responsive layout

* fix(Grid): 修正栅格间隔描述中的拼写错误

* feat(Grid): reverse

* test(Form): update grid

* size limit

---------

Co-authored-by: thinkasany <480968828@qq.com>
2026-02-04 00:01:55 +08:00
高艳兵
3810d1dc6a fix(button): make tokens take effect (#56842)
* refactor(button): rework component-token demo

* fix(button): linkHoverBg token not work

* fix(button): enable ghostBg for outlined and dashed ghost

* fix(button): make defaultBgDisabled take effect

* fix(button): apply paddingBlock tokens to vertical padding

* fix(button): apply contentLineHeight tokens to button line-height

* fix: token.defaultBgDisabled

* revert(button): drop contentLineHeight and paddingBlock changes

* chore(button): remove unused tokens from component-token demo
2026-02-03 14:18:27 +08:00
dependabot[bot]
ef32250465 chore: bump @eslint-react/eslint-plugin from 2.8.4 to 2.9.3 (#56843)
Bumps [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react/tree/HEAD/packages/plugins/eslint-plugin) from 2.8.4 to 2.9.3.
- [Release notes](https://github.com/Rel1cx/eslint-react/releases)
- [Changelog](https://github.com/Rel1cx/eslint-react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Rel1cx/eslint-react/commits/v2.9.3/packages/plugins/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@eslint-react/eslint-plugin"
  dependency-version: 2.9.3
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-02-03 11:25:31 +08:00
thinkasany
d71e9ffb0d chore: rm overrides deps (#56840)
* test

* test

* fix

* turn off ESLINT_CACHE

* rm
2026-02-03 10:55:29 +08:00
github-actions[bot]
8d925cbb02 chore: auto merge branches (#56839)
chore: sync master into feature
2026-02-02 15:23:25 +00:00
lijianan
955afb14f4 chore: update date (#56837)
* chore: update date

* update

* Update package.json

Signed-off-by: thinkasany <480968828@qq.com>

---------

Signed-off-by: thinkasany <480968828@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-02-02 23:05:22 +08:00
thinkasany
443abf2ebe chore: ci failed (#56838)
Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-02-02 23:04:44 +08:00
thinkasany
1931606b97 chore: bump jsdom@28 (#56835)
* chore: bump jsdom@28

* Update tests/setup.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: thinkasany <480968828@qq.com>

---------

Signed-off-by: thinkasany <480968828@qq.com>
Signed-off-by: meet-student <1875694521@qq.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-02 22:54:16 +08:00
thinkasany
ceddbda113 chore: lock @eslint-react/eslint-plugin 2.8.4 (#56836)
* chore: lock @eslint-react/eslint-plugin 2.9.1

* update

* fix

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-02-02 22:38:07 +08:00
huangkevin-apr
41a5b2fe9e chore(a11y): add title element to search bar icon for screen readers (#56521)
* fix(a11y): add title element to search bar icon for screen readers

* Revert "fix(a10y): add title element to search bar icon for screen readers"

This reverts commit 9caf0bbc4e.

* fix: the svg elements has no accessible name

---------

Co-authored-by: thinkasany <480968828@qq.com>
2026-02-02 21:35:51 +08:00
二货爱吃白萝卜
08f23516a0 fix: antd umd build (#56830)
* chore: debug of ci

* chore: debug of ci

* chore: clean up

* use ut

---------

Co-authored-by: thinkasany <480968828@qq.com>
2026-02-02 21:04:19 +08:00
dependabot[bot]
5a0f141ddc chore: bump dawidd6/action-download-artifact from 13 to 14 (#56834)
Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 13 to 14.
- [Release notes](https://github.com/dawidd6/action-download-artifact/releases)
- [Commits](https://github.com/dawidd6/action-download-artifact/compare/v13...v14)

---
updated-dependencies:
- dependency-name: dawidd6/action-download-artifact
  dependency-version: '14'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-02 20:52:33 +08:00
github-actions[bot]
3da7cf9033 chore: auto merge branches (#56832)
chore: sync master into feature
2026-02-02 12:35:01 +00:00
二货爱吃白萝卜
b1bebbb9c3 refactor: Simplify Select single mode dom structure (#56811)
* refactor: restructure select input styles and update dependencies

* chore: opt logic

* refactor: simplify select content structure in snapshots

* test: update semantic snapshots for @rc-component/select 1.6.1

Co-Authored-By: Claude (GLM-4.7) <noreply@anthropic.com>

* fix: update select input color styling and dependency

* test: update snapshot

---------

Co-authored-by: Claude (GLM-4.7) <noreply@anthropic.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-02-02 16:12:33 +08:00
afc163
20f8a9a9ed chore: bump version to 6.2.3 (#56813)
* chore: bump version to 6.2.3

* docs: update AGENTS.md with comprehensive development guidelines

* Update CHANGELOG.zh-CN.md

Co-authored-by: lijianan <574980606@qq.com>
Signed-off-by: afc163 <afc163@gmail.com>

* Update CHANGELOG.en-US.md

Signed-off-by: afc163 <afc163@gmail.com>

* Update CHANGELOG.zh-CN.md

Signed-off-by: afc163 <afc163@gmail.com>

---------

Signed-off-by: afc163 <afc163@gmail.com>
Co-authored-by: lijianan <574980606@qq.com>
2026-02-02 14:57:45 +08:00
dependabot[bot]
f20a3d6fba chore: bump eslint-plugin-react-refresh from 0.4.26 to 0.5.0 (#56828)
Bumps [eslint-plugin-react-refresh](https://github.com/ArnaudBarre/eslint-plugin-react-refresh) from 0.4.26 to 0.5.0.
- [Release notes](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/releases)
- [Changelog](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/compare/v0.4.26...v0.5.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-react-refresh
  dependency-version: 0.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-02 09:15:08 +08:00
lijianan
191db91774 site(a11y): apply prefers-reduced-motion handling for transitions (#56823)
* site(a11y): apply prefers-reduced-motion handling for transitions

* site(a11y): apply prefers-reduced-motion handling for transitions
2026-02-01 17:52:13 +08:00
renovate[bot]
a28f8aa863 chore(deps): update dependency @types/react to v19.2.9 (#56819)
* chore(deps): update dependency @types/react to v19.2.10

* improve type

* update

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-02-01 10:26:09 +08:00
renovate[bot]
4ab4a826c9 chore(deps): update dawidd6/action-download-artifact action to v13 (#56820)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-01 09:44:45 +08:00
github-actions[bot]
0c7ee7c6c5 chore: auto merge branches (#56816)
chore: sync master into feature
2026-01-31 02:31:12 +00:00
thinkasany
98bc0f0756 chore: sync master into feature 2026-01-31 10:09:18 +08:00
叶枫
7b323c5254 type: improve Alert semantic type (#56758)
* feat: introduce SemanticTypeV2 for improved type handling in Alert component

* feat: add SemanticResultTypeV2 for improved semantic handling in Alert component

* refactor(Alert): remove SemanticResultTypeV2 and simplify AlertResultType

* feat: test

* refactor: replace SemanticTypeV2 with SemanticType in Alert component
2026-01-30 18:58:01 +08:00
Rain120
ffbf6e3024 docs: style issues in site design tabs (#56810)
Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-30 17:27:22 +08:00
高艳兵
4b9085624b chore: merge duplicate debug demos (#56809)
* chore(Button): remove debug demo

* feat: update snap
2026-01-30 15:34:38 +08:00
ug
7dd385bb43 fix(Button): defaultBg, defaultColor, defaultHoverColor and defaultActiveColor token does not work (#56238)
* fix(Button): token does not work

* Update components/button/style/variant.ts

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: ug <62086147+ug-hero@users.noreply.github.com>

* update

* fix

* Update components/button/demo/component-token.tsx

Co-authored-by:  高艳兵 <66234749+QDyanbing@users.noreply.github.com>
Signed-off-by: thinkasany <480968828@qq.com>

* format

* Update components/button/style/variant.ts

Co-authored-by:  高艳兵 <66234749+QDyanbing@users.noreply.github.com>
Signed-off-by: thinkasany <480968828@qq.com>

* Update components/button/style/variant.ts

Co-authored-by:  高艳兵 <66234749+QDyanbing@users.noreply.github.com>
Signed-off-by: thinkasany <480968828@qq.com>

---------

Signed-off-by: ug <62086147+ug-hero@users.noreply.github.com>
Signed-off-by: thinkasany <480968828@qq.com>
Co-authored-by: liuqiang <qiang.liu@xinjifamily.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>
Co-authored-by: 高艳兵 <66234749+QDyanbing@users.noreply.github.com>
2026-01-30 14:44:38 +08:00
二货爱吃白萝卜
44a4161dd8 fix: Textarea ref miss nativeElement (#56803)
* fix: Textarea miss nativeElement

* chore: update snap

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-30 09:56:00 +08:00
github-actions[bot]
10e43e1405 chore: upgrade deps (#56805)
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
2026-01-30 07:00:34 +08:00
二货爱吃白萝卜
80b2250d6c refactor: Remove checkbox useless dom (#56783)
* docs: update demo

* test: update snapshot

* chore: adjust

* chore: adjust

* chore: upgrade @rc-component/checkbox to ~2.0.0 and update snapshots

Co-Authored-By: Claude (GLM-4.7) <noreply@anthropic.com>

* test: add semantic test for checked prop with defaultChecked

Co-Authored-By: Claude (GLM-4.7) <noreply@anthropic.com>

* test: update semantic snapshots

Co-Authored-By: Claude (GLM-4.7) <noreply@anthropic.com>

* refactor: restructure radio component styles and improve hover states

* refactor: simplify radio component styles and calculations

* refactor: update radio style-class demo with clsx and css-in-js

* fix: radio component checked state handling in groups

* test: update radio semantic test and table row selection selector

* test: update snapshots for semantic demo tests

* fix: adjust checkbox checkmark position

* fix: update checkbox styling calculations

---------

Co-authored-by: Claude (GLM-4.7) <noreply@anthropic.com>
2026-01-29 21:58:10 +08:00
高艳兵
93fb6c35e2 chore(test): merge semantic tests into jest and drop dedicated config (#56795)
* chore(test): merge semantic tests into jest and drop dedicated config

* chore(test): simplify SKIP_SEMANTIC env check
2026-01-29 15:30:07 +08:00
二货爱吃白萝卜
3e2b7546aa chore: bump tree version (#56799)
* chore: bump tree version

* test: update snapshot
2026-01-29 15:12:12 +08:00
thinkasany
3073cf188d docs: Why can't the Segmented be disabled? (#56786)
* docs: add Why can't Segmented be disabled

* Update components/form/index.en-US.md

Signed-off-by: thinkasany <480968828@qq.com>

* Update components/form/index.en-US.md

Signed-off-by: thinkasany <480968828@qq.com>

* Apply suggestion from @meet-student

Signed-off-by: 遇见同学 <1875694521@qq.com>

* Apply suggestion from @meet-student

Signed-off-by: 遇见同学 <1875694521@qq.com>

* docs: update faq

* Update components/form/__tests__/index.test.tsx

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: 二货爱吃白萝卜 <smith3816@gmail.com>

---------

Signed-off-by: thinkasany <480968828@qq.com>
Signed-off-by: 遇见同学 <1875694521@qq.com>
Signed-off-by: 二货爱吃白萝卜 <smith3816@gmail.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>
Co-authored-by: 二货机器人 <smith3816@gmail.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-01-29 12:09:01 +08:00
Yingtao Mo
f78e800519 fix(Flex): default align when use orientation (#55950)
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-29 11:14:33 +08:00
github-actions[bot]
35cd37b3d8 chore: auto merge branches (#56794)
chore: sync master into feature
2026-01-29 03:10:12 +00:00
thinkasany
a8b6e3d015 chore: sync master into feature 2026-01-29 10:41:28 +08:00
thinkasany
ebc17059de chore(test): turn off no-nested-component-definitions (#56793)
Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-29 10:39:51 +08:00
github-actions[bot]
a93b4122ff chore: upgrade deps (#56791)
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
2026-01-29 08:09:09 +08:00
aojunhao123
8743710da7 refactor: disable mask blur effect by default (#56781)
* feat: disable mask blur effect by default

Change the blur condition from `blur !== false` to `blur === true`,
making the blur effect opt-in rather than opt-out.

* Update components/_util/hooks/useMergedMask.ts

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: aojunhao123 <1844749591@qq.com>

* update snapshot

* update snapshot

* Update components/modal/demo/mask.tsx

Signed-off-by: thinkasany <480968828@qq.com>

---------

Signed-off-by: aojunhao123 <1844749591@qq.com>
Signed-off-by: thinkasany <480968828@qq.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-28 20:43:12 +08:00
github-actions[bot]
3b43f2d8e5 chore: auto merge branches (#56784)
chore: sync master into feature
2026-01-28 12:41:53 +00:00
thinkasany
9431db4dc1 chore: sync master into feature 2026-01-28 20:28:03 +08:00
unknowntocka
ead5840d90 fix(Button): default button token (#56719)
* fix(Button): default button token

* feat(Button): add default button token with custom colors in debug demo

---------

Co-authored-by: lijianan <574980606@qq.com>
2026-01-28 17:57:18 +08:00
高艳兵
166e9c88e5 fix(Typography): lower link selector specificity (#56759)
* fix(Typography): lower link selector specificity and add danger debug demo

* fix(Typography): scope link styles and raise type color priority

* chore: update snap

* chore: update snap

* fix(Typography): scope link styles and ensure type colors win

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-28 16:01:51 +08:00
Junbeom Park
b2d14dd478 fix(ColorPicker): restrict HEX input to valid hex characters (#56752)
* fix(color-picker): restrict HEX input to valid hex characters

* test(ColorPicker): add test invalid input like ff_00_gg

* Update components/color-picker/__tests__/components.test.tsx

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: 二货爱吃白萝卜 <smith3816@gmail.com>

---------

Signed-off-by: 二货爱吃白萝卜 <smith3816@gmail.com>
Co-authored-by: 二货爱吃白萝卜 <smith3816@gmail.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-01-28 15:38:43 +08:00
二货爱吃白萝卜
b02fe5018b fix: cascader should keep list when close with filter (#56764)
* fix: cascader should keep list when close with filter

* chore: clean up

* test: update snapshot

---------

Signed-off-by: thinkasany <480968828@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-28 12:00:21 +08:00
github-actions[bot]
076c84b181 chore: upgrade deps (#56773)
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
2026-01-28 08:49:49 +08:00
QdabuliuQ
350f73c89e fix(Cascader): optionRender usage for empty search results (#56756)
* fix(Cascader): update @rc-component/cascader version  and add test unit

* rerun

---------

Co-authored-by: thinkasany <480968828@qq.com>
2026-01-27 23:40:01 +08:00
github-actions[bot]
920b24f328 chore: auto merge branches (#56771)
chore: sync master into feature
2026-01-27 15:20:30 +00:00
thinkasany
50a3f30a72 chore: lock @types/react 19.2.7 (#56770) 2026-01-27 22:53:59 +08:00
afc163
5629062ba1 docs: remove community link from resources docs (#56760)
Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-27 20:35:29 +08:00
遇见同学
63e4d29760 docs: fix token table title style issue (#56766) 2026-01-27 18:30:58 +08:00
github-actions[bot]
4c12447bae chore: auto merge branches (#56763)
chore: feature merge master
2026-01-27 09:45:54 +00:00
遇见同学
94fe19feef chore: fix ci update snap (#56761)
* chore: fix ci update snap

* up

* chore up
2026-01-27 17:24:56 +08:00
aojunhao123
f1c70f8c54 feat(Tree): improve accessibility (#56716)
* feat(Tree): improve accessibility focus style for virtual list

- Update focus-visible style to apply outline on active node content wrapper
- Upgrade @rc-component/tree to ~1.2.0
- Upgrade @rc-component/tree-select to ~1.7.0
- Upgrade @rc-component/cascader to ~1.12.0

* fix test

* update snapshot

* update snapshot

* update snapshot

* update snapshot

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-27 14:36:14 +08:00
Anton Cherniavskyi
9df0d7656f fix(Button): solid variant borders inside Space Compact (#56486)
* fix(Button): solid variant borders inside Space Compact

* refactor(Button): use genCssVar hook to generate CSS variables

* chore: adjust logic of compact

* test: update demo

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
Co-authored-by: afc163 <afc163@gmail.com>
Co-authored-by: 二货机器人 <smith3816@gmail.com>
2026-01-27 14:29:34 +08:00
lijianan
dc8acf547d type: TypeScript definition improvement (#56753) 2026-01-27 10:30:14 +08:00
github-actions[bot]
4655434949 chore: auto merge branches (#56748)
chore: sync master into feature
2026-01-26 13:50:35 +00:00
Amumu
c880c2e806 chore: bunp x (#56747) 2026-01-26 20:50:04 +08:00
xrkffgg
58c5fc4625 docs: add changelog 6.2.2 (#56742)
* docs: add changelog 6.2.2

* Update CHANGELOG.en-US.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: xrkffgg <xrkffgg@vip.qq.com>

* Update CHANGELOG.en-US.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: xrkffgg <xrkffgg@vip.qq.com>

* Update CHANGELOG.en-US.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: xrkffgg <xrkffgg@vip.qq.com>

* docs: update

* docs: update

---------

Signed-off-by: xrkffgg <xrkffgg@vip.qq.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: afc163 <afc163@gmail.com>
2026-01-26 19:11:00 +08:00
二货爱吃白萝卜
250597ea0c test: update snapshot (#56745) 2026-01-26 18:15:37 +08:00
𝑾𝒖𝒙𝒉
3034888500 fix(Propover): Isolate Form.Item status from popup-type components (#56728)
* 🧪 add unit test

* fix(Propover): Isolate `Form.Item` status from popup-type components
2026-01-26 14:49:03 +08:00
QdabuliuQ
7f1bd84097 feat(Upload): upload support to setting upload button class names style (#56578)
* feat: add uploadButton support to Upload classNames and styles

* fix: rename uploadButton style key to trigger in Upload

* feat: support custom trigger class and style in Upload

* Update components/upload/demo/_semantic.tsx

Co-authored-by: 𝑾𝒖𝒙𝒉 <wxh16144@qq.com>
Signed-off-by: thinkasany <480968828@qq.com>

* chore: updare upload snapshot file

---------

Signed-off-by: thinkasany <480968828@qq.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>
Co-authored-by: 𝑾𝒖𝒙𝒉 <wxh16144@qq.com>
2026-01-26 14:24:31 +08:00
thinkasany
705c3642d3 chore: add deprecated comments (#56733) 2026-01-26 13:04:55 +08:00
renovate[bot]
fbe7864e10 chore(deps): update dependency env-paths to v4 (#56736)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-25 10:47:11 +08:00
github-actions[bot]
3c256d3f57 chore: auto merge branches (#56727)
chore: merge master into feature
2026-01-23 07:19:11 +00:00
Guo Yunhe
e5328f6464 feat(ConfigProvider,DatePicker,TimePicker): support suffixIcon config (#56709)
* feat(ConfigProvider,DatePicker,TimePicker): support suffixIcon config

* Update components/date-picker/generatePicker/generateSinglePicker.tsx

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: Guo Yunhe <i@guoyunhe.me>

---------

Signed-off-by: Guo Yunhe <i@guoyunhe.me>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-01-23 13:03:00 +08:00
afc163
2f6548209d test: fix test case skip logic (#56701)
* test: fix test case skip logic

* fix

* fix

* test: add snapshot tests for splitter and table demos

* test: remove skip options from splitter tests

* test: add snapshot for size-mix demo in splitter component
2026-01-23 13:02:04 +08:00
高艳兵
6f90801861 fix(Button): text variant tokens not working (#56291)
* fix(Button): text variant tokens not working

* test(Button): add debug demo for text variant component tokens

* test: update button debug color variant snapshots

* fix: getCssVar to varName

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-23 09:49:48 +08:00
高艳兵
b5b735a490 fix: align pagination container items vertically (#56715)
* fix: align pagination container items vertically

* fix: quick-jumper center
2026-01-23 09:08:44 +08:00
QdabuliuQ
8bebba7af8 docs(Drawer): add maxSize prop documentation to Drawer (#56704)
* fix: replace insetInline properties with left/right in drawer style

* docu: add maxSize prop documentation to Drawer

Documented the new `maxSize` property for the Drawer component in both English and Chinese docs, describing its purpose and usage when the Drawer is resizable.

* chore: update drawer markdown

* fix: resolve file content conflict

* doc: modify the order of maxSize property in markdown

* docs: improve drawer api table

* docs: improve drawer api table

* fix: update Drawer 'open' prop default value in docs

* fix: deprecate headerStyle prop in Drawer documentation

* Update components/drawer/index.en-US.md

Signed-off-by: thinkasany <480968828@qq.com>

* Update components/drawer/index.zh-CN.md

Signed-off-by: thinkasany <480968828@qq.com>

---------

Signed-off-by: thinkasany <480968828@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-22 22:39:22 +08:00
ug
6a74e0bed5 feat(Splitter): Support onDraggerDoubleClick (#56579)
* feat(Splitter): Support onDraggerDoubleClick

* update

* update

* update

* update

* Update components/splitter/index.en-US.md

Co-authored-by: lijianan <574980606@qq.com>
Signed-off-by: thinkasany <480968828@qq.com>

* Update components/splitter/index.zh-CN.md

Co-authored-by: lijianan <574980606@qq.com>
Signed-off-by: thinkasany <480968828@qq.com>

* Update components/splitter/index.en-US.md

Co-authored-by: thinkasany <480968828@qq.com>
Signed-off-by: ug <62086147+ug-hero@users.noreply.github.com>

* Update components/splitter/index.zh-CN.md

Co-authored-by: thinkasany <480968828@qq.com>
Signed-off-by: ug <62086147+ug-hero@users.noreply.github.com>

---------

Signed-off-by: thinkasany <480968828@qq.com>
Signed-off-by: ug <62086147+ug-hero@users.noreply.github.com>
Co-authored-by: liuqiang <qiang.liu@xinjifamily.com>
Co-authored-by: thinkasany <480968828@qq.com>
Co-authored-by: lijianan <574980606@qq.com>
2026-01-22 16:46:00 +08:00
高艳兵
1a15e8485e feat(llms): add raw markdown output support (#56561)
* refactor(theme): split monolithic dumi theme plugin into modular plugins

- extract tech stack logic into dedicated plugin
- extract changelog route extensions into routes plugin
- extract build-time asset processing (emotion css, entry injection) into build-assets plugin

No behavior change intended.

* feat(theme): add production raw markdown output for demos

* feat(theme): add ComponentTokenTable to markdown table conversion support

* feat: add semantic.md generation plugin for components

* feat: replace Semantic DOM code tags with links to semantic.md files

* docs(table): add content semantic (#56592)

* fix: add error logging in raw-md plugin catch block

* feat: update semantic-md plugin

* chore: trigger CI

* feat: add configurable options for raw-md plugin including enablePickLocaleBlock

* refactor: adjust codeAppend function signature in raw-md plugin

* feat: Optimize heading hierarchy

* feat: remove debug code

* fix: component name

* feat: add llms plugin

* feat: remove llms script

* fix:  high priority

* feat: update semantic-md plugin read locale

* feat: add md copy

* refactor(dumi): rework llms open/copy menu

* refactor(dumi): simplify llms menu items config and labels

* refactor(dumi): simplify llms menu item mapping

* refactor: simplify ComponentMeta LLMs link

* chore: refine review-related md handling and document semantic snapshot dependency

---------

Co-authored-by: thinkasany <480968828@qq.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-22 16:15:02 +08:00
二货爱吃白萝卜
5854f71b5a feat: add indicator semantic support to switch component (#56710)
* feat: add indicator semantic support to switch component

* test: update snapshot

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-22 16:10:38 +08:00
Lekë Dobruna
ae0d69547a feat(Input.OTP): add autoComplete prop support (#56674)
* feat(Input.OTP): add autoComplete prop support

Allow Input.OTP to set autoComplete attribute (e.g., 'one-time-code') for browser/password manager OTP autofill support.

Closes #53188

* fix: address review comments

- Improve test to verify all inputs receive autoComplete attribute

- Add version number (5.25.0) to documentation

* fix: correct version number to 6.3.0
2026-01-22 15:18:30 +08:00
Guo Yunhe
52ce51f2bf feat(Cascader,ConfigProvider): support expandIcon and loadingIcon config (#56482)
* feat(Cascader,ConfigProvider): support expandIcon and loadingIcon config

* Update components/config-provider/index.zh-CN.md

Co-authored-by: thinkasany <480968828@qq.com>
Signed-off-by: Guo Yunhe <i@guoyunhe.me>

* Update components/config-provider/index.en-US.md

Co-authored-by: thinkasany <480968828@qq.com>
Signed-off-by: Guo Yunhe <i@guoyunhe.me>

* refactor(cascader): extract icon logic into utility function

* feat(cascader): add support for custom expandIcon in Cascader component tests

* feat(cascader): enhance loadingIcon support in Cascader component and tests

* feat(cascader): add loadingIcon prop for customization in Cascader component documentation

* feat(cascader): simplify icon logic by using default icons in getIcons utility

* feat(cascader): refactor icon handling by introducing useIcons hook and updating imports

* refactor(cascader): remove unused icon utility functions following useIcons hook implementation

---------

Signed-off-by: Guo Yunhe <i@guoyunhe.me>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-22 14:53:59 +08:00
Clayton
568a398bc8 feat(ConfigProvider): add scroll prop support for Table config (#56628)
* chore: remove unnecessary unit in style (#56677)

* chore: update test script to include semantic snap (#56679)

* chore: update test script to include semantic snap

* update

* Update package.json

Co-authored-by: thinkasany <480968828@qq.com>
Signed-off-by: lijianan <574980606@qq.com>

---------

Signed-off-by: lijianan <574980606@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>

* feat(ConfigProvider): add scroll prop support for Table config

* Revert "chore: update test script to include semantic snap (#56679)"

This reverts commit bce3aeeba8.

* Revert "chore: remove unnecessary unit in style (#56677)"

This reverts commit 7a058ab005.

---------

Signed-off-by: lijianan <574980606@qq.com>
Co-authored-by: lijianan <574980606@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-22 10:54:40 +08:00
github-actions[bot]
e44f0063b1 chore: auto merge branches (#56700)
chore: sync master into feature
2026-01-21 16:13:39 +00:00
ug
3ac1fc78e7 perf(Empty/Skeleton): Reduce the size of the SVG (#56682)
* optimize(Empty): Reduce the size of the SVG.

* update

* update

* update

---------

Co-authored-by: liuqiang <qiang.liu@xinjifamily.com>
Co-authored-by: afc163 <afc163@gmail.com>
2026-01-21 23:59:13 +08:00
Copilot
f091c1e01b test: skip table sticky demo from visual regression (#56698)
* Initial plan

* test: skip table-sticky demo from UI comparison

Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
2026-01-21 23:43:47 +08:00
thinkasany
acc11dfccb chore(eslint): add fetch to polyfills configuration (#56697) 2026-01-21 23:05:52 +08:00
QdabuliuQ
964eb9d9d5 fix: replace insetInline properties with left/right in drawer style (#56693)
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-21 21:01:32 +08:00
thinkasany
0b36e295f0 type: rm any (#56696) 2026-01-21 20:54:42 +08:00
thinkasany
3ee9ff0811 chore: rm useless type (#56695) 2026-01-21 20:54:24 +08:00
QdabuliuQ
092e37861a fix(Typography): button with href wrapped by Typography showing incorrect color and flickering outline on hover (#56619)
* style: The typography component is showing styling issues, including with the button component

* style: refactor link styles for Button consistency

* style: modify mixins.ts file

* fix: reset border styles for Typography links

* fix: link style conflicts with ant-btn in typography component

* fix: link styles to avoid conflicts with Button component

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-21 18:45:12 +08:00
二货爱吃白萝卜
80dad184ee chore: dev theme switch only (#56688) 2026-01-21 17:59:42 +08:00
二货爱吃白萝卜
04dadedeb3 feature: Home page refactor and enhance ConfigProvider (#56573)
* chore: init

* chore: home color

* chore: offset

* chore: opacity bg

* chore: opacity bg

* chore: opacity bg

* chore: bg trans

* chore: trans position

* chore: ui

* chore: of it

* docs: adjust block

* chore: default theme

* chore: default themes

* chore: styles

* chore: app

* chore: shared props

* chore: of it

* chore: of it

* chore: of it

* chore: of it

* chore: current color

* chore: of it

* chore: adjust color

* chore: build with layer

* chore: comment

* chore: build in

* chore: of it

* chore: init

* chore: of it

* chore: of it

* chore: bootstrap

* chore: bootstrap

* chore: bootstrap

* chore: init

* chore: glass theme

* chore: adjust color

* chore: a little shadow

* chore: fix lint

* chore: prefetch image

* chore: default theme follow site

* chore: init cartoon

* chore: cartoon ui

* chore: init

* chore: adjust slider style

* chore: fix shadow

* chore: adjust order

* chore: adjust style

* chore: adjust style

* chore: fix name

---------

Co-authored-by: lijianan <574980606@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-21 17:42:38 +08:00
二货爱吃白萝卜
e0c9df2fed fix: button border width not follow design token (#56683)
* chore: fix button line width

* test: update demo
2026-01-21 14:14:07 +08:00
lijianan
01fdc86b39 chore: update test script to include semantic snap (#56679)
* chore: update test script to include semantic snap

* update

* Update package.json

Co-authored-by: thinkasany <480968828@qq.com>
Signed-off-by: lijianan <574980606@qq.com>

---------

Signed-off-by: lijianan <574980606@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-21 09:29:06 +08:00
lijianan
a3dcbdfae4 chore: remove unnecessary unit in style (#56677) 2026-01-21 09:02:36 +08:00
github-actions[bot]
1897cfd126 chore: auto merge branches (#56678)
chore: merge master into feature
2026-01-21 00:41:00 +00:00
lijianan
756975004b test: update snap 2026-01-21 07:55:26 +08:00
lijianan
4951f7e44c Merge branch master into master-merge-feature 2026-01-21 07:39:41 +08:00
github-actions[bot]
d5b3409660 chore: upgrade deps (#56675)
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
2026-01-21 03:49:10 +08:00
Peach
8b4235a53b docs: changelog for 6.2.1 (#56667)
* docs: changelog for 6.2.1

* chore: bump version to 6.2.1

* refactor: update changelog

* docs: update changelog
2026-01-20 14:59:30 +08:00
高艳兵
c46ce7fb86 fix: pagination size docs (#56668) 2026-01-20 14:59:26 +08:00
高艳兵
8574b941a7 feat: add semantic ci (#56670)
* feat: add semantic ci

* fix: version

* ci: move version step into test:semantic and run in test workflow
2026-01-20 14:30:35 +08:00
lijianan
edf65d3eeb chore: remove unnecessary brackets in style (#56665)
* refactor: remove unnecessary brackets in style

* update
2026-01-20 09:48:00 +08:00
github-actions[bot]
10d2f753f7 chore: auto merge branches (#56663)
chore: sync master into feature
2026-01-19 15:32:02 +00:00
高艳兵
c5e5ac7c3f feat: add semantic snap (#56600)
* feat: add semantic.md generation plugin for components

* refactor: extract locales from SelectSemanticTemplate to component props

* fix: remove unuse locales

* feat: multipleProps add defaultValue

* feat: update select snap

* feat: update table snap

* feat: tour add snap

* feat: delete semantic-md plugin

* feat: add sematic snap

* refactor: replace hardcoded button clicks with configurable postRenderFn in semantic demo test

* revert: locales

* fix: maxTagCount responsive

* fix: test  maxTagCount set 1

* feat: add test:semantic script

* fix: ci add collectCoverageFrom

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-19 22:10:33 +08:00
ug
937f5d0738 fix(card): Fixed the overflow issue in the Card component body (#56653)
* fix(card): Fixed the overflow issue in the Card component body

* update

---------

Co-authored-by: liuqiang <qiang.liu@xinjifamily.com>
2026-01-19 15:13:02 +08:00
Guo Yunhe
d6d7e90998 feat(Button,ConfigProvider): loadingIcon config (#56439)
* feat(Button,ConfigProvider): loadingIcon config

* test(Button): loadingIcon ConfigProvider

* Update components/config-provider/index.zh-CN.md

Signed-off-by: thinkasany <480968828@qq.com>

* Update components/config-provider/index.en-US.md

Signed-off-by: thinkasany <480968828@qq.com>

---------

Signed-off-by: thinkasany <480968828@qq.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-19 14:18:03 +08:00
Wanpan
3848b6bb82 docs: fix SemanticPreview not work (#56650) 2026-01-19 09:49:54 +08:00
thinkasany
bb5642f90b chore: improve tree DirectoryTree (#56648)
* chore: improve tree DirectoryTree

* fix
2026-01-18 18:16:19 +08:00
thinkasany
60442f9d44 chore: improve tooltip (#56645) 2026-01-18 16:16:03 +08:00
github-actions[bot]
41fd735434 chore: auto merge branches (#56644)
chore: merge master into feature
2026-01-18 01:50:57 +00:00
renovate[bot]
c8e79dda70 chore(deps): update dependency @eslint-react/eslint-plugin to v2.7.2 (#56643)
* chore(deps): update dependency @eslint-react/eslint-plugin to v2.7.2

* Update package.json

Signed-off-by: lijianan <574980606@qq.com>

---------

Signed-off-by: lijianan <574980606@qq.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: lijianan <574980606@qq.com>
2026-01-18 09:15:18 +08:00
renovate[bot]
2389eb8731 chore(deps): update dependency @eslint-react/eslint-plugin to v2.7.1 (#56641)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-18 09:02:13 +08:00
renovate[bot]
b9d23a1d96 chore(deps): update dependency jquery to v4 (#56642)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-18 09:01:28 +08:00
沈鸿飞
3eecc366bb fix: import individual icons from antd-icons (#56640) 2026-01-17 23:38:37 +08:00
hu
fecb9a04f6 refactor: replace SuffixIcon with useSuffixIcon in date-picker for prevent suffix icon rendering when user set suffixIcon to null or false (#56637)
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-17 23:08:28 +08:00
高艳兵
77f0f1a9fe fix(table): border radius not work (#56478)
* fix(table): border radius  not work

* fix(table): apply border radius to shadow pseudo elements
2026-01-17 22:57:06 +08:00
沈鸿飞
aeff7cab63 fix: import a single DownOutlined icon from antd-icons (#56639)
* fix: import a single DownOutlined icon from antd-icons

Signed-off-by: 沈鸿飞 <shen.hongfei@outlook.com>

* Update components/breadcrumb/Breadcrumb.tsx

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: 沈鸿飞 <shen.hongfei@outlook.com>

---------

Signed-off-by: 沈鸿飞 <shen.hongfei@outlook.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-01-17 22:55:02 +08:00
lijianan
5df2c59516 refactor(Grid): use genCssVar hook to generate CSS variables (#56635) 2026-01-17 10:44:40 +08:00
lijianan
247c41c79b fix: should skip appendStyle when value is null or undefined (#56636) 2026-01-17 10:42:45 +08:00
Guo Yunhe
18ed518fdd feat(ConfigProvider,DatePicker,TimePicker): support rangePicker.separator config (#56499)
* feat(ConfigProvider): support rangePicker.separator config

* fix: update rangePicker separator version in documentation to 6.3.0

* fix: update RangePicker snapshot to include separator span element
2026-01-16 11:26:16 +08:00
QdabuliuQ
4dc35000b1 docs: improve site locale detection (#56618)
* fix(Site): improve changelog header styles and locale detection

* style: fix link and button style conflicts in typography

* style: modify style file

* style: reset typography mixins.ts file

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-16 09:37:26 +08:00
github-actions[bot]
c33138840f chore: auto merge branches (#56629)
chore: merge master into feature
2026-01-16 00:04:04 +00:00
dependabot[bot]
a9e9c04cbc chore: bump @eslint-react/eslint-plugin from 2.6.2 to 2.6.4 (#56627)
Bumps [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react/tree/HEAD/packages/plugins/eslint-plugin) from 2.6.2 to 2.6.4.
- [Release notes](https://github.com/Rel1cx/eslint-react/releases)
- [Changelog](https://github.com/Rel1cx/eslint-react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Rel1cx/eslint-react/commits/v2.6.4/packages/plugins/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@eslint-react/eslint-plugin"
  dependency-version: 2.6.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-16 06:44:49 +08:00
github-actions[bot]
4f61124be4 chore: upgrade deps (#56625)
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
2026-01-16 04:20:32 +08:00
thinkasany
fccc092154 chore: improve collapse cloneElement (#56622)
Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-15 23:21:30 +08:00
二货爱吃白萝卜
a680d84865 test: fix test case (#56620)
* test: fix test case

* chore: fix lint
2026-01-15 18:15:27 +08:00
lijianan
f7446f3f1f refactor(css): replace background transition with background-color (#56598)
* fix(css): replace background transition with background-color

* chore: update

---------

Co-authored-by: thinkasany <480968828@qq.com>
2026-01-15 11:04:33 +08:00
lijianan
28d674445b fix: replace disabled with mergedDisabled (#56613)
* fix: replace disable with mergedDisabled

* update

* update

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-15 10:49:44 +08:00
github-actions[bot]
4ab542e7c3 chore: auto merge branches (#56614)
chore: sync master into feature
2026-01-15 02:22:27 +00:00
遇见同学
07f0db2ddc docs: refactor replace createStyles with createStaticStyles across multiple components (#56610)
* refactor: replace createStyles with createStaticStyles across multiple components

* chore: update

---------

Co-authored-by: thinkasany <480968828@qq.com>
2026-01-15 09:38:51 +08:00
dependabot[bot]
2e51288638 chore: bump @eslint-react/eslint-plugin from 2.5.0 to 2.6.2 (#56612)
Bumps [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react/tree/HEAD/packages/plugins/eslint-plugin) from 2.5.0 to 2.6.2.
- [Release notes](https://github.com/Rel1cx/eslint-react/releases)
- [Changelog](https://github.com/Rel1cx/eslint-react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Rel1cx/eslint-react/commits/v2.6.2/packages/plugins/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@eslint-react/eslint-plugin"
  dependency-version: 2.6.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-15 08:23:21 +08:00
thinkasany
258573ab93 chore: improve closablePlacement (#56611) 2026-01-14 23:55:32 +08:00
遇见同学
3c9bb681e7 docs: improve performance replace createStyles with createStaticStyles (#56605)
* perf: use createStaticStyles

* chore: update snap

* chore: adjust

* chore: snap
2026-01-14 17:58:47 +08:00
二货爱吃白萝卜
3e0b56dacf feat: ColorPicker semantic support ColorBlock customize (#56607)
* feat: ColorPicker missing semantic

* chore: fix lint

* test: update snapshot

* chore: fix lint
2026-01-14 17:53:23 +08:00
Guo Yunhe
7d35e112ae feat(ConfigProvider,Form): support tooltip prop and config (#56372)
* feat(ConfigProvider,Form): support tooltipIcon and tooltipProps

* feat(ConfigProvider,Form): support tooltipIcon and tooltipProps

* feat(ConfigProvider,Form): support tooltipIcon and tooltipProps

* feat(Form): support tooltip config

* feat(Form): support tooltip config

* fix(Form): remove unnecessary type assertion in tooltip prop

* refactor(Form): enhance tooltip prop handling by merging context with tooltip properties

---------

Co-authored-by: thinkasany <480968828@qq.com>
2026-01-14 16:23:30 +08:00
Guo Yunhe
6e55573b5c feat(Avatar): size default to 'medium' instead of 'default' for consistancy (#56440)
Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-14 13:18:27 +08:00
github-actions[bot]
0e4f60dd01 chore: auto merge branches (#56601)
chore: sync master into feature
2026-01-14 02:16:19 +00:00
dependabot[bot]
0b55335da9 chore: bump @antfu/eslint-config from 6.7.3 to 7.0.0 in the dev-dependencies group (#56599)
* chore: bump @antfu/eslint-config in the dev-dependencies group

Bumps the dev-dependencies group with 1 update: [@antfu/eslint-config](https://github.com/antfu/eslint-config).


Updates `@antfu/eslint-config` from 6.7.3 to 7.0.0
- [Release notes](https://github.com/antfu/eslint-config/releases)
- [Commits](https://github.com/antfu/eslint-config/compare/v6.7.3...v7.0.0)

---
updated-dependencies:
- dependency-name: "@antfu/eslint-config"
  dependency-version: 7.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

* update

* test lock

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-14 09:55:56 +08:00
lijianan
40c0bd6bb1 fix: improve Button child style order (#56597)
* fix: improvement style order

* update snap

* update undefined

* snap update
2026-01-14 03:42:40 +08:00
github-actions[bot]
98614197cb chore: auto merge branches (#56596)
chore: sync master into feature
2026-01-13 12:57:25 +00:00
QdabuliuQ
8fd30ab4a2 fix(Button): child element's className being cleared when rendering two Chinese characters. (#56593) 2026-01-13 18:14:54 +08:00
高艳兵
e05aa23faa docs(table): add content semantic (#56592) 2026-01-13 16:04:49 +08:00
二货机器人
050d1dfa42 chore: update post pub script 2026-01-13 14:45:52 +08:00
二货爱吃白萝卜
d90f8ed238 docs: Changelog of 6.2.0 (#56588)
* docs: init

* docs: en ver
2026-01-13 14:08:53 +08:00
lijianan
185398afdc update (#56589) 2026-01-13 11:58:06 +08:00
github-actions[bot]
b3adc9a69b chore: upgrade deps (#56584)
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
2026-01-13 03:09:16 +08:00
github-actions[bot]
fbd6fdafbe chore: auto merge branches (#56583)
chore: merge feature into master
2026-01-12 14:50:54 +00:00
afc163
5b9b6f801a feat: add marginSize prop for QRCode quiet zone (#56569)
* feat: add marginSize prop for QRCode quiet zone

* Apply suggestions from code review

Signed-off-by: afc163 <afc163@gmail.com>

* Update components/qr-code/index.en-US.md

Co-authored-by: thinkasany <480968828@qq.com>
Signed-off-by: afc163 <afc163@gmail.com>

* Apply suggestions from code review

Co-authored-by: thinkasany <480968828@qq.com>
Signed-off-by: afc163 <afc163@gmail.com>

---------

Signed-off-by: afc163 <afc163@gmail.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-12 21:44:25 +08:00
LeiZhang
27826a8136 feat(Tour): add keyboard prop to control keyboard shortcuts (#56581)
* feat(Tour): add keyboard prop to control keyboard shortcuts

* Update components/tour/index.en-US.md

Signed-off-by: thinkasany <480968828@qq.com>

* Update components/tour/index.zh-CN.md

Signed-off-by: thinkasany <480968828@qq.com>

---------

Signed-off-by: thinkasany <480968828@qq.com>
Co-authored-by: 路振凯 <l>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-12 17:58:43 +08:00
github-actions[bot]
4f53033269 chore: auto merge branches (#56580)
chore: Merge feature into master
2026-01-12 09:47:50 +00:00
QdabuliuQ
c235b1193b site: add keepAlive animation to theme transition fix flickering after switching themes (#56544)
* fix: add keepAlive animation to theme transition

* style: modify animation keyframes

---------

Co-authored-by: thinkasany <480968828@qq.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-12 15:22:52 +08:00
thinkasany
ff35ebb285 docs: rm useless file (#56575) 2026-01-12 13:32:58 +08:00
github-actions[bot]
0d603d44fa chore: auto merge branches (#56574)
chore: merge master into feature
2026-01-12 04:30:19 +00:00
lijianan
2a8a811762 chore: update genCssVar arguments (#56572) 2026-01-12 11:43:27 +08:00
Guo Yunhe
cb309acd4a feat(Tooltip): add maxWidth token (#56540) 2026-01-12 10:55:18 +08:00
lijianan
e2ce447c47 site: update site select style (#56567) 2026-01-12 10:22:48 +08:00
ug
f62d50533b docs: load version data dynamically JSON (#56464)
* docs: load version data dynamically via JSON

* update

* update

* Update .dumi/theme/slots/Header/index.tsx

Signed-off-by: lijianan <574980606@qq.com>

* Fix window location check in Header component

Update condition to check for window and location existence.

Signed-off-by: lijianan <574980606@qq.com>

* Refactor Header component for improved logic

Signed-off-by: lijianan <574980606@qq.com>

* Change import path for versions.json file

Signed-off-by: lijianan <574980606@qq.com>

* Refactor fetcher and update VersionItem interface

Signed-off-by: lijianan <574980606@qq.com>

* Fix version URL to use window.location.origin

Signed-off-by: lijianan <574980606@qq.com>

---------

Signed-off-by: lijianan <574980606@qq.com>
Co-authored-by: liuqiang <qiang.liu@xinjifamily.com>
Co-authored-by: lijianan <574980606@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-11 23:17:33 +08:00
github-actions[bot]
e15b8defc9 chore: auto merge branches (#56566)
chore: merge master into feature
2026-01-11 08:58:51 +00:00
lijianan
e33444368e fix(Steps): rm typo progress11 (#56565)
* fix(Steps): fix typo progress11 => progress

* fix: update

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-11 16:14:04 +08:00
lijianan
4a88f6a98a fix: update undefined padding to 0 (#56564) 2026-01-11 15:34:50 +08:00
lijianan
f324de2173 refactor(Steps/Timeline): use genCssVar hook to generate CSS variables (#56562)
* refactor(Steps): use genCssVar hook to generate CSS variables

* fix: update

* test: update snap

* fix: update

* fix: update

* update snap

* fix: update

* fix: update

* update

* update

* update
2026-01-11 15:09:09 +08:00
github-actions[bot]
0362603ae5 chore: auto merge branches (#56560)
chore: merge master into feature
2026-01-10 14:43:11 +00:00
lijianan
8def94703b refactor(Select): use genCssVar hook to generate CSS variables (#56559)
* refactor(Select): use genCssVar hook to generate CSS variables

* Update components/select/style/select-input-multiple.ts

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: lijianan <574980606@qq.com>

* fix: update

* update

---------

Signed-off-by: lijianan <574980606@qq.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-01-10 22:26:06 +08:00
lijianan
67a0e2fee7 refactor(Space): use genCssVar hook to generate CSS variables (#56557) 2026-01-10 17:09:58 +08:00
thinkasany
584a7be26f chore: Bump @ant-design/x to 2.x (#56556)
* chore: bump ant-desigin/x

* fix export

* fix lint

* chore: Bump @ant-design/x to 2.x

---------

Co-authored-by: yoyo837 <yoyo837@hotmail.com>
2026-01-10 12:41:37 +08:00
lijianan
3f5129ac44 refactor(Mentions): use genCssVar hook to generate CSS variables (#56550)
* update

* update

* update

* update

* update

* update
2026-01-10 11:58:32 +08:00
lijianan
d937a3930f refactor(Splitter): use genCssVar hook to generate CSS variables (#56555)
* refactor(Splitter): use genCssVar hook to generate CSS variables

* Update components/splitter/style/index.ts

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: lijianan <574980606@qq.com>

---------

Signed-off-by: lijianan <574980606@qq.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-01-10 11:03:22 +08:00
dependabot[bot]
c300c97a3e chore: bump @types/node from 25.0.1 to 25.0.3 (#55375)
* chore: bump jsdom from 27.0.0 to 27.0.1

Bumps [jsdom](https://github.com/jsdom/jsdom) from 27.0.0 to 27.0.1.
- [Release notes](https://github.com/jsdom/jsdom/releases)
- [Changelog](https://github.com/jsdom/jsdom/blob/main/Changelog.md)
- [Commits](https://github.com/jsdom/jsdom/compare/27.0.0...27.0.1)

---
updated-dependencies:
- dependency-name: jsdom
  dependency-version: 27.0.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* update

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: lijianan <574980606@qq.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: lijianan <574980606@qq.com>
2026-01-10 06:25:44 +08:00
github-actions[bot]
ec953cbffa chore: auto merge branches (#56549)
chore: merge master into feature
2026-01-09 15:09:46 +00:00
lijianan
caccb11d6f fix: update varRef 2026-01-09 22:57:25 +08:00
lijianan
a863062f7b Merge branch master into master-merge-feature 2026-01-09 22:54:46 +08:00
lijianan
584923d35b refactor(Masonry): 🛠 use genCssVar hook to generate CSS variables (#56547) 2026-01-09 22:46:31 +08:00
lijianan
75ddcdfd00 refactor: 🛠 use genCssVar hook to generate CSS variables (#56546)
* refactor: 🛠 use genCssVar hook to generate CSS variables

* update

* update

* update

* update

* update

* Update components/tooltip/style/index.ts

Co-authored-by: thinkasany <480968828@qq.com>
Signed-off-by: lijianan <574980606@qq.com>

* update

* fix: update

---------

Signed-off-by: lijianan <574980606@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-09 22:24:19 +08:00
github-actions[bot]
9b2054ffec chore: auto merge branches (#56543)
chore: merge master into feature
2026-01-09 07:40:31 +00:00
lijianan
b1bb15a753 update 2026-01-09 15:21:08 +08:00
lijianan
c78056d0a4 Merge branch master into master-merge-feature 2026-01-09 14:46:59 +08:00
lijianan
ba47850fa0 refactor: 🛠 use genCssVar hook to generate CSS variables (#56529) 2026-01-09 13:40:44 +08:00
baozj
ae98485a3e chore: fix typo (#56541)
Co-authored-by: baozj <www.1670370148@qq.com>
2026-01-09 11:24:38 +08:00
github-actions[bot]
9bb27c4100 chore: upgrade deps (#56538)
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
2026-01-09 03:33:02 +08:00
Guo Yunhe
64f7963395 feat(Form): support tel type validator (#56533)
* feat(Form): support tel type validator

* docs(Form): update tel validator
2026-01-08 16:40:00 +08:00
高艳兵
6fac5c24bf fix(Select): correct clear icon position in sm size (#56525)
* fix(Select): correct clear icon position in sm size

* fix: add test case and use --select-padding-horizontal

* test(Select): move test case to clear icon position

* test(Select): remove unused test case

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-08 15:06:11 +08:00
二货爱吃白萝卜
ee8cc27686 fix: Badge ref not working (#56532) 2026-01-08 15:05:28 +08:00
lijianan
364bcc74d1 type: export components SemanticName type (#56494)
* types: export components SemanticName type

* update

* update

* update

* update

* update
2026-01-08 09:25:17 +08:00
github-actions[bot]
7130056493 chore: auto merge branches (#56522)
chore: sync master into feature
2026-01-07 14:02:14 +00:00
thinkasany
8a4e89fc59 chore: sync master into feature 2026-01-07 19:23:51 +08:00
aojunhao123
6893402469 feat: Tooltips/Popovers/Popconfirm can be closed by pressing the Escape key by default (#56492)
* feat: Tooltips/Popovers/Popconfirm can be closed by pressing the Escape key by default

* chore: adjust

* chore: adjust
2026-01-07 10:51:53 +08:00
高艳兵
957b027fa1 feat: support partial locale overrides for calendar and date picker (#56376)
* feat: support partial locale overrides for calendar and date picker

* refactor: refine DeepPartial to skip builtins and support collections

* revert: rollback type definitions to original types

* test: add merge locale test

* test: update snapshots

* fix: type

* test: update snapshopt

* test: update test case

* chore: adjust limit

---------

Co-authored-by: thinkasany <480968828@qq.com>
Co-authored-by: 二货机器人 <smith3816@gmail.com>
2026-01-07 10:45:51 +08:00
lijianan
6c5036e285 demo(types): TypeScript definition improvement (#56516)
* ️perf: optimize odd/even check using bitwise operation

* update
2026-01-07 10:25:10 +08:00
Sean Parmelee
4888842fc4 fix(ConfigProvider): use correct cssVar key for icon styles (#56504)
Co-authored-by: lijianan <574980606@qq.com>
2026-01-07 09:46:27 +08:00
二货爱吃白萝卜
1463722eb5 fix: Collapse items semantic props (#56517) 2026-01-07 09:14:32 +08:00
github-actions[bot]
c3710c0102 chore: upgrade deps (#56518)
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
2026-01-07 09:08:59 +08:00
huangkevin-apr
ae9fc640c6 chore(a11y): remove redundant alt text from component icon (#56510)
Co-authored-by: afc163 <afc163@gmail.com>
2026-01-06 21:02:53 +08:00
github-actions[bot]
1a3b29ec8f chore: auto merge branches (#56495)
chore: merge master into feature
2026-01-06 08:25:10 +00:00
二货爱吃白萝卜
8ac5c5fde4 chore: fix ci (#56509) 2026-01-06 16:13:30 +08:00
二货爱吃白萝卜
d7aac85735 chore: add deprecated warning for Modal (#56507)
* test: add test case

* chore: fix lint
2026-01-06 15:12:39 +08:00
二货爱吃白萝卜
0cdfaf578a feat: Modal support focusable (#56500)
* feat: Modal support focusable config

* chore: doc & test

* chore: adjust logic

* test: add test case

* chore: update docs
2026-01-06 14:34:30 +08:00
github-actions[bot]
c4b105c66a chore: upgrade deps (#56493)
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
2026-01-06 02:21:00 +08:00
lijianan
1e9b8a997c site(types): update any to SenderRef (#56487)
Co-authored-by: afc163 <afc163@gmail.com>
2026-01-05 23:23:45 +08:00
afc163
82622f14d5 ci: update runner image to ubuntu-latest (#56488) 2026-01-05 19:51:06 +08:00
plus
f7a216aaa9 feat: ConfigProvider support pagination.totalBoundaryShowSizeChanger (#56475)
Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-05 19:25:23 +08:00
zoomdong
a6d83589db docs: changelog for 6.1.4 (#56470)
* docs: changelog for 6.1.4

* chore: update review

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-05 00:13:36 -08:00
Guo Yunhe
a01c1293cf docs(ConfigProvider): fix alert config (#56473)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2026-01-05 14:08:13 +08:00
plus
1721a1af9b docs: totalBoundaryShowSizeChanger for Pagination (#56471)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: afc163 <afc163@gmail.com>
2026-01-05 13:27:07 +08:00
github-actions[bot]
8e1bdc8121 chore: upgrade deps (#56468)
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
2026-01-05 07:13:41 +08:00
lijianan
283b2822e0 docs: update AGENTS.md (#56467) 2026-01-04 23:15:42 +08:00
Vara Rahul Rajana
d353c2949c docs: use React 18 UMD for CodePen demos (#56466) 2026-01-04 22:53:27 +08:00
二货爱吃白萝卜
1ec7cd744d feat: Drawer support focusable (#56463)
* feat: add focusable support to drawer component

* test: add test case

* docs: add focusable prop documentation for drawer component

* test: update test case
2026-01-04 16:39:07 +08:00
github-actions[bot]
50dc6acbb2 chore: auto merge branches (#56462)
chore: sync master into feature
2026-01-04 06:27:21 +00:00
thinkasany
cab5910713 ci(size-limit): uses bun 2026-01-04 14:12:43 +08:00
thinkasany
fbe0a46857 test: update snap 2026-01-04 13:22:59 +08:00
thinkasany
8af72472fa chore: sync master into feature 2026-01-04 13:15:00 +08:00
thinkasany
8ec34d6204 chore: rm useless yml (#56461) 2026-01-04 12:28:29 +08:00
aojunhao123
7e2f8ce1f0 refactor: esc handling logic and fix esc can't close image preview in modal (#56386)
* fix: optimize ESC key handling in nested portal scenarios

* fix confirm-modal tests

* remove keyCode in test

* chore: adjust
2026-01-04 11:35:13 +08:00
Guo Yunhe
9123bbbfd7 feat(Pagination): three size (#56009)
* feat(Pagination): three size

* feat(Pagination): three size

* feat(Pagination): three size

* feat(Pagination): three size

* feat(Pagination): three size

* feat(Pagination): three size

* feat(Pagination): three size

* test(Pagination): update snapshots

* test(Pagination): update snapshots

* fix(Pagination): use css var style

* fix(Pagination): use css var style

---------

Co-authored-by: thinkasany <480968828@qq.com>
2026-01-04 11:06:49 +08:00
lijianan
24bd6fd311 type: rm redundant any type (#56460) 2026-01-04 10:01:16 +08:00
thinkasany
d9eb4ccb4b chore: bump eslint (#56457) 2026-01-02 21:57:34 +08:00
elrrrrrrr
da8e1ccde2 fix: setup utoo (#56452)
* fix: setup utoo

* chore: size-limit to 617 KiB

---------

Co-authored-by: binghui.dbh <binghui.dbh@antgroup.com>
2026-01-02 09:28:59 +08:00
lijianan
89fa656149 docs: update DatePicker defaultValue (#56455) 2026-01-01 15:46:42 +08:00
QdabuliuQ
8399590542 docs: remove demo of Table hidden resizable column (#56447)
* doc: hidden table component resizable-column demo

* chore: 调整代码格式

* doc: remove resizable column demo from Table component

* chore: rm deps

* update: update snapshot file

* fix: package.json version number error

---------

Co-authored-by: thinkasany <480968828@qq.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>
2026-01-01 10:02:19 +08:00
二货爱吃白萝卜
e2f3cf6133 test: update test case (#56451) 2025-12-31 17:50:18 +08:00
叶枫
5471db8383 type: fix modal type (#56441)
* fix: fix modal type

* fix: add type

* feat: add warn deprecated

---------

Co-authored-by: 遇见同学 <1875694521@qq.com>
2025-12-31 17:03:15 +08:00
github-actions[bot]
d188f03bb8 chore: auto merge branches (#56446)
chore: sync master into feature
2025-12-31 08:45:02 +00:00
遇见同学
edb2bde901 chore: fix ci pin react-resizable version to 3.0.5 (#56445) 2025-12-31 15:41:16 +08:00
Wanpan
8fbae86149 docs: Improve cascader api description (#56433)
* docs: Improve cascader api description

* Update components/cascader/index.zh-CN.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: Wanpan <wanpan96@163.com>

* Update components/cascader/index.en-US.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: Wanpan <wanpan96@163.com>

* docs: update

---------

Signed-off-by: Wanpan <wanpan96@163.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-12-31 11:33:41 +08:00
github-actions[bot]
fba5f4f6cb chore: auto merge branches (#56432)
chore: sync master into feature
2025-12-31 02:18:36 +00:00
高艳兵
2ccab1e984 fix(Table): skip mounting FilterDropdown in measure header (#56425)
Co-authored-by: thinkasany <480968828@qq.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>
2025-12-31 09:41:14 +08:00
陈帅
acd131b7c4 docs: update AGENTS.md changelog part (#56420)
Co-authored-by: thinkasany <480968828@qq.com>
Co-authored-by: 遇见同学 <1875694521@qq.com>
2025-12-31 09:35:51 +08:00
lijianan
353a94f43d chore(deps): unlock jsdom (#56431) 2025-12-31 09:29:11 +08:00
github-actions[bot]
09b1107c35 chore: upgrade deps (#56428)
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
2025-12-31 09:06:49 +08:00
遇见同学
3abfb8c2dc chore: ci fix to remove parse5 (#56421)
* chore: ci fix

* chore: update

* chore: update

* chore: fix lint

* chore: remove outdated parse5 dependencies from package.json

---------

Co-authored-by: 二货机器人 <smith3816@gmail.com>
Co-authored-by: thinkasany <480968828@qq.com>
2025-12-30 23:05:56 +08:00
blacksmith-sh[bot]
38efd012a2 .github/workflows: Migrate workflows to Blacksmith runners (#56282)
* Migrate workflows to Blacksmith

* Update .github/workflows/preview-build.yml

Signed-off-by: thinkasany <480968828@qq.com>

* preview-build.yml use runs-on: ubuntu-latest (#56367)

* Initial plan

* fix: add named exports for ColorPalettes and ColorPaletteToolDark to fix SSR compatibility

Co-authored-by: thinkasany <117748716+thinkasany@users.noreply.github.com>

* test: preview-build use ubuntu-latest

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: thinkasany <117748716+thinkasany@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>

---------

Signed-off-by: thinkasany <480968828@qq.com>
Co-authored-by: blacksmith-sh[bot] <157653362+blacksmith-sh[bot]@users.noreply.github.com>
Co-authored-by: afc163 <afc163@gmail.com>
Co-authored-by: thinkasany <480968828@qq.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: thinkasany <117748716+thinkasany@users.noreply.github.com>
2025-12-30 23:04:15 +08:00
github-actions[bot]
49c4f56100 chore: auto merge branches (#56426)
chore: sync master into feature
2025-12-30 14:09:37 +00:00
遇见同学
8852b23742 Revert "chore: remove outdated parse5 dependencies from package.json (#56410)" (#56422)
This reverts commit eac8dde1e5.
2025-12-30 21:50:45 +08:00
Wanpan
f160384640 docs: Optimize the dark mode of the homepage. (#56418) 2025-12-30 13:35:21 +08:00
lijianan
1c5b04e47b site: replace isCancelled with cancelledRef (#56415) 2025-12-30 09:09:00 +08:00
dependabot[bot]
330c5be52e chore(deps-dev): bump father from 4.6.12 to 4.6.13 (#56416)
Bumps [father](https://github.com/umijs/father) from 4.6.12 to 4.6.13.
- [Release notes](https://github.com/umijs/father/releases)
- [Commits](https://github.com/umijs/father/compare/v4.6.12...v4.6.13)

---
updated-dependencies:
- dependency-name: father
  dependency-version: 4.6.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-30 08:31:52 +08:00
遇见同学
eac8dde1e5 chore: remove outdated parse5 dependencies from package.json (#56410) 2025-12-29 22:38:57 +08:00
elrrrrrrr
3a78aacb3c fix: setup utoo (#56409)
Co-authored-by: binghui.dbh <binghui.dbh@antgroup.com>
2025-12-29 21:53:28 +08:00
github-actions[bot]
2ff9d89b15 chore: auto merge branches (#56408)
chore: sync master into feature
2025-12-29 12:53:43 +00:00
Guo Yunhe
3d152a78fd feat(Breadcrumb,ConfigProvider): support dropdownIcon prop and config (#56250)
* feat(Breadcrumb,ConfigProvider): support dropdownIcon prop and config

* refactor(Breadcrumb): import order

* docs(Breadcrumb,ConfigProvider): dropdonIcon

---------

Co-authored-by: thinkasany <480968828@qq.com>
2025-12-29 16:03:33 +08:00
Guo Yunhe
0a89f9cf21 docs: fix sandpack template (#56405) 2025-12-29 14:14:14 +08:00
MadCcc
9af831fd92 docs: update changelog 6.1.3 (#56403) 2025-12-29 13:58:54 +08:00
MadCcc
3686df953a docs: changelog 6.1.3 (#56402)
* docs: changelog 6.1.3

* chore: update

* chore: update
2025-12-29 11:33:01 +08:00
renovate[bot]
d08de4e1de chore(deps): update dependency jsdom to v27.4.0 (#56395)
* chore(deps): update dependency jsdom to v27.4.0

* need transform @exodus

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2025-12-28 21:17:10 +08:00
遇见同学
ec0bdd2984 type: improve type (#56401) 2025-12-28 20:14:57 +08:00
Wanpan
6ed7b0f045 docs: update the components displayed on homepage (#56392) 2025-12-28 19:44:21 +08:00
renovate[bot]
44abdc4cda chore(deps): update dependency parse5-htmlparser2-tree-adapter to v8 (#56397)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2025-12-28 18:33:48 +08:00
renovate[bot]
b8b365c2d1 chore(deps): update dependency father to v4.6.12 (#56394)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2025-12-28 10:25:31 +08:00
renovate[bot]
0774e5a40a chore(deps): update actions-cool/issues-helper digest to e2ff998 (#56393)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2025-12-28 10:23:51 +08:00
renovate[bot]
a3d31746e3 chore(deps): update dependency parse5-parser-stream to v8 (#56398)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: thinkasany <480968828@qq.com>
2025-12-28 10:22:53 +08:00
renovate[bot]
7326f7b119 chore(deps): update dependency parse5 to v8 (#56396)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-28 10:21:48 +08:00
lijianan
886a1d19cd refactor(types): derive SemanticName from semantic maps (#56391) 2025-12-27 20:21:30 +08:00
Wanpan
5235b4fe32 fix: Drawer.PurePanel does not respond to mouse events (#56387)
Co-authored-by: 遇见同学 <1875694521@qq.com>
2025-12-27 19:41:55 +08:00
lijianan
5c52fea0bf refactor(types): derive SemanticName from semantic maps (#56389)
* refactor(types): derive SemanticName from semantic maps

* update type
2025-12-27 18:12:44 +08:00
lijianan
39b7edd337 site: use css logical attributes (#56388)
Co-authored-by: 遇见同学 <1875694521@qq.com>
2025-12-27 18:12:05 +08:00
lijianan
dbf71b5059 site: use cssVar (#56381)
* test: fix master CI fail

* fix: update

---------

Co-authored-by: thinkasany <480968828@qq.com>
2025-12-27 17:57:06 +08:00
github-actions[bot]
60cd020a35 chore: auto merge branches (#56380)
chore: merge master into feature
2025-12-27 09:14:24 +00:00
thinkasany
9108b85cf7 chore: biome lint format (#56384)
Co-authored-by: 遇见同学 <1875694521@qq.com>
2025-12-27 16:32:17 +08:00
遇见同学
1341d29312 docs: blog improve dart theme (#56383)
* docs: blog improve dart theme

* Update docs/blog/semantic-beauty/demos.tsx

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: thinkasany <480968828@qq.com>

* Update docs/blog/semantic-beauty/demos.tsx

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: thinkasany <480968828@qq.com>

---------

Signed-off-by: thinkasany <480968828@qq.com>
Co-authored-by: thinkasany <480968828@qq.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-12-27 16:10:53 +08:00
thinkasany
397cb98bd1 ci: fix parse5 esm only failed (#56382)
* test

* lock jsdom

* add limit

* revert

* test

* fix

* fix

* fix

* test

* fix

* revert ut

* Revert "revert ut"

This reverts commit e55aa13f90.

* revert ut
2025-12-27 15:47:42 +08:00
Guo Yunhe
56ff32425e feat(BackTop): mark component as deprecated in favor of FloatButton.BackTop (#56371) 2025-12-26 14:08:41 +08:00
afc163
8f616c018e fix: update menu tag labels and ordering (#56366)
Co-authored-by: thinkasany <480968828@qq.com>
2025-12-26 12:05:18 +08:00
Guo Yunhe
a2aadb3fa8 refactor(Breadcrumb): restructure Breadcrumb component to use compounded components for Item and Separator (#55892) 2025-12-26 10:51:07 +08:00
github-actions[bot]
5b8fd49fba chore: upgrade deps (#56368)
Co-authored-by: afc163 <507615+afc163@users.noreply.github.com>
2025-12-26 08:19:16 +08:00
dependabot[bot]
fb6d7405d0 chore(deps): bump dawidd6/action-download-artifact from 11 to 12 (#56362)
Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 11 to 12.
- [Release notes](https://github.com/dawidd6/action-download-artifact/releases)
- [Commits](https://github.com/dawidd6/action-download-artifact/compare/v11...v12)

---
updated-dependencies:
- dependency-name: dawidd6/action-download-artifact
  dependency-version: '12'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-25 22:55:54 +08:00
ug
0d0f88c71a feat(Drawer): The "size" supports the string type. (#56358)
Co-authored-by: liuqiang <qiang.liu@xinjifamily.com>
2025-12-25 22:08:29 +08:00
Guo Yunhe
9a62d148c7 docs: remove 5.x tags (#56353) 2025-12-25 12:17:44 +08:00
afc163
5efbb093bc fix: Select unknown props warning on dom element (#56341)
Signed-off-by: afc163 <afc163@gmail.com>
Co-authored-by: thinkasany <480968828@qq.com>
2025-12-25 11:36:06 +08:00
lijianan
ea4d320241 docs: update date (#56350) 2025-12-24 21:01:49 +08:00
二货爱吃白萝卜
2ffd8a7012 docs: add missing changelog (#56343) 2025-12-24 15:44:57 +08:00
Guo Yunhe
81e7f3fe98 feat(Alert, ConfigProvider): support global icon config (#56241)
* feat(Alert, ConfigProvider): support global icon config

* test(Form): update snapshots

* refactor(Alert): simplify icon rendering logic in IconNode component

* refactor(Alert): streamline icon rendering in IconNode component

* refactor(Alert): remove prefixCls from IconNode component for cleaner rendering

* feat(ConfigProvider): add success, info, warning, and error icon props to Anchor component documentation
2025-12-24 15:03:27 +08:00
陈帅
dfd962d350 docs: add 6.1.2 changeglog (#56332)
* docs:add 6.1.2 changeglog

* fix: 修复 Transfer 组件在禁用状态下的 className 问题

* Update CHANGELOG.zh-CN.md

Co-authored-by: afc163 <afc163@gmail.com>
Signed-off-by: 陈帅 <qixian.cs@outlook.com>

* Update CHANGELOG.en-US.md

Co-authored-by: afc163 <afc163@gmail.com>
Signed-off-by: 陈帅 <qixian.cs@outlook.com>

* Update CHANGELOG.zh-CN.md

Co-authored-by: afc163 <afc163@gmail.com>
Signed-off-by: 陈帅 <qixian.cs@outlook.com>

* Update CHANGELOG.zh-CN.md

Co-authored-by: afc163 <afc163@gmail.com>
Signed-off-by: 陈帅 <qixian.cs@outlook.com>

* 更新 6.1.2 更新日志,添加 Button 和 Breadcrumb 组件的修复说明

* 更新 6.1.2 更新日志,添加 Select 和 Tag 组件的修复说明

* Update CHANGELOG.en-US.md

Co-authored-by: afc163 <afc163@gmail.com>
Signed-off-by: 陈帅 <qixian.cs@outlook.com>

* Update CHANGELOG.zh-CN.md

Co-authored-by: afc163 <afc163@gmail.com>
Signed-off-by: 陈帅 <qixian.cs@outlook.com>

* Update CHANGELOG.en-US.md

Co-authored-by: afc163 <afc163@gmail.com>
Signed-off-by: 陈帅 <qixian.cs@outlook.com>

* 更新 6.1.2 更新日志,添加 Select 和 Tag 组件的修复说明

* 更新 6.1.2 更新日志,修正 Tag 组件背景对比度及 Segmented 组件属性描述

---------

Signed-off-by: 陈帅 <qixian.cs@outlook.com>
Co-authored-by: afc163 <afc163@gmail.com>
2025-12-24 13:36:56 +08:00
𝑾𝒖𝒙𝒉
742ecee166 chore(site): In order to better debug the online demo (#56072) 2025-12-24 10:19:26 +08:00
高艳兵
d680ea3d30 fix: fix cursor style when Select has showSearch and disabled props (#56340) 2025-12-24 10:14:19 +08:00
高艳兵
c0589e939d chore: adjust Tag default background contrast (#56326) 2025-12-24 10:11:56 +08:00
github-actions[bot]
d1b47edff4 chore: auto merge branches (#56323)
chore: merge master into feature
2025-12-23 03:12:41 +00:00
lijianan
631ee77e5b Merge branch master into master-merge-feature 2025-12-23 10:30:52 +08:00
lijianan
c3f41c19ba refactor(types): replace Record with explicit object for better DX (#56298) 2025-12-22 10:47:06 +08:00
github-actions[bot]
60efb8e170 chore: auto merge branches (#56288)
chore: merge master into feature
2025-12-20 04:23:10 +00:00
github-actions[bot]
9f45cb32c2 chore: auto merge branches (#56243)
chore: sync master to feature
2025-12-16 09:39:36 +00:00
github-actions[bot]
97ef4e304e chore: auto merge branches (#56233)
chore: merge master into feature
2025-12-16 00:38:52 +00:00
github-actions[bot]
1004fa28ea chore: auto merge branches (#56199)
chore: merge master into feature
2025-12-12 20:48:26 +00:00
Anton Cherniavskyi
af27ca20fb feat(Checkbox.Group, Radio.Group): add support for role prop (#56126)
Co-authored-by: 遇见同学 <1875694521@qq.com>
2025-12-12 10:49:25 +08:00
github-actions[bot]
bc1db71a0d chore: auto merge branches (#56161)
chore: merge master into feature
2025-12-11 05:09:16 +00:00
二货爱吃白萝卜
7a919eb971 refactor: simplify dialog dom & adjust focus logic (#56142) 2025-12-10 10:34:20 +08:00
874 changed files with 80994 additions and 29415 deletions

View File

@@ -7,7 +7,6 @@ const isNumber = (value: any): value is number => {
};
const fetcher = async (url: string): Promise<number> => {
// eslint-disable-next-line compat/compat
const res = await fetch(url, { headers: { Accept: 'application/vnd.github+json' } });
const data = await res.json();
const totalCount = isNumber(data?.total_count) ? data.total_count : 0;

View File

@@ -1,7 +1,7 @@
import React, { useMemo } from 'react';
import type { MenuProps } from 'antd';
import { Flex, Tag, version } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { clsx } from 'clsx';
import { useFullSidebarData, useSidebarData } from 'dumi';
@@ -12,29 +12,28 @@ import useLocation from './useLocation';
const locales = {
cn: {
deprecated: '废弃',
update: '更新',
updated: '更新',
new: '新增',
},
en: {
deprecated: 'DEPRECATED',
update: 'UPDATE',
updated: 'UPDATED',
new: 'NEW',
},
};
const getTagColor = (val?: string) => {
switch (val?.toUpperCase()) {
case 'UPDATE':
case 'UPDATED':
return 'processing';
case 'DEPRECATED':
return 'red';
default:
return 'success';
}
};
const useStyle = createStyles(({ css, cssVar }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
link: css`
display: flex;
align-items: center;
@@ -47,7 +46,7 @@ const useStyle = createStyles(({ css, cssVar }) => ({
font-weight: normal;
font-size: ${cssVar.fontSizeSM};
opacity: 0.8;
margin-left: 4px;
margin-inline-start: ${cssVar.marginSM};
`,
}));
@@ -63,10 +62,10 @@ interface MenuItemLabelProps {
}
const MenuItemLabelWithTag: React.FC<MenuItemLabelProps> = (props) => {
const { styles } = useStyle();
const { before, after, link, title, subtitle, search, tag, className } = props;
const [locale] = useLocale(locales);
const getLocale = (name: string) => {
return (locale as any)[name.toLowerCase()] ?? name;
};
@@ -74,7 +73,7 @@ const MenuItemLabelWithTag: React.FC<MenuItemLabelProps> = (props) => {
if (!before && !after) {
return (
<Link to={`${link}${search}`} className={clsx(className, { [styles.link]: tag })}>
<Flex justify="flex-start" align="center" gap="small">
<Flex justify="flex-start" align="center">
<span>{title}</span>
{subtitle && <span className={styles.subtitle}>{subtitle}</span>}
</Flex>

View File

@@ -3,10 +3,14 @@ import { removeCSS, updateCSS } from '@rc-component/util/lib/Dom/dynamicCSS';
import theme from '../../components/theme';
const duration = 0.5;
const viewTransitionStyle = `
@keyframes keepAlive {100% { z-index: -1 }}
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
animation: keepAlive ${duration}s linear;
animation-fill-mode: forwards;
mix-blend-mode: normal;
}
@@ -50,7 +54,7 @@ const useThemeAnimation = () => {
clipPath: isDark ? [...clipPath].reverse() : clipPath,
},
{
duration: 500,
duration: duration * 1000,
easing: 'ease-in',
pseudoElement: isDark ? '::view-transition-old(root)' : '::view-transition-new(root)',
},

3
.dumi/layer-import.less vendored Normal file
View File

@@ -0,0 +1,3 @@
@layer antd {
@import '~../components/style/antd.css';
}

View File

@@ -1,4 +1,5 @@
import React from 'react';
import raf from '@rc-component/util/lib/raf';
import { Alert, Badge, Carousel, Flex, Skeleton, Typography } from 'antd';
import { createStyles } from 'antd-style';
import { clsx } from 'clsx';
@@ -18,12 +19,46 @@ const useStyle = createStyles(({ cssVar, css, cx }) => {
align-items: stretch;
text-decoration: none;
background: ${cssVar.colorBgContainer};
background: color-mix(in srgb, ${cssVar.colorBgContainer} 30%, transparent);
backdrop-filter: blur(8px);
border: ${cssVar.lineWidth} solid ${cssVar.colorBorderSecondary};
border-radius: ${cssVar.borderRadiusLG};
transition: all ${cssVar.motionDurationSlow};
padding-block: ${cssVar.paddingMD};
padding-inline: ${cssVar.paddingLG};
box-sizing: border-box;
position: relative;
&:before {
content: '';
inset: calc(${cssVar.lineWidth} * -1);
position: absolute;
background: radial-gradient(
circle 150px at var(--mouse-x, 0) var(--mouse-y, 0),
${cssVar.colorPrimaryBorderHover},
${cssVar.colorBorderSecondary}
);
opacity: 0;
transition: all 0.3s ease;
mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
mask-composite: subtract;
-webkit-mask-composite: xor;
padding: 1px;
border-radius: inherit;
}
&:hover {
backdrop-filter: blur(0px);
background: color-mix(in srgb, ${cssVar.colorBgContainer} 90%, transparent);
&:before {
opacity: 1;
}
}
`;
return {
@@ -33,12 +68,6 @@ const useStyle = createStyles(({ cssVar, css, cx }) => {
height: 100%;
}
`,
cardItem: css`
&:hover {
box-shadow: ${cssVar.boxShadowCard};
border-color: transparent;
}
`,
sliderItem: css`
margin: 0 ${cssVar.margin};
text-align: start;
@@ -64,6 +93,9 @@ const useStyle = createStyles(({ cssVar, css, cx }) => {
};
});
// ======================================================================
// == Item ==
// ======================================================================
interface RecommendItemProps {
extra: Extra;
index: number;
@@ -73,9 +105,47 @@ interface RecommendItemProps {
const RecommendItem: React.FC<RecommendItemProps> = (props) => {
const { extra, index, icons, className } = props;
const cardRef = React.useRef<HTMLAnchorElement>(null);
const { styles } = useStyle();
// ====================== MousePos ======================
const [mousePosition, setMousePosition] = React.useState<[number, number]>([0, 0]);
const [transMousePosition, setTransMousePosition] = React.useState<[number, number]>([0, 0]);
const onMouseMove = (e: React.MouseEvent<HTMLAnchorElement>) => {
if (!cardRef.current) return;
const rect = cardRef.current.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
setMousePosition([x, y]);
};
// Transition mouse position
React.useEffect(() => {
const [targetX, targetY] = mousePosition;
const [currentX, currentY] = transMousePosition;
if (Math.abs(targetX - currentX) < 0.5 && Math.abs(targetY - currentY) < 0.5) {
return;
}
const rafId = raf(() => {
setTransMousePosition((ori) => {
const [curX, curY] = ori;
const deltaX = (targetX - curX) * 0.1;
const deltaY = (targetY - curY) * 0.1;
return [curX + deltaX, curY + deltaY];
});
});
return () => raf.cancel(rafId);
}, [mousePosition, transMousePosition]);
// ======================= Render =======================
if (!extra) {
return <Skeleton key={index} />;
}
@@ -84,11 +154,19 @@ const RecommendItem: React.FC<RecommendItemProps> = (props) => {
const card = (
<a
ref={cardRef}
key={extra?.title}
href={extra.href}
target="_blank"
className={clsx(styles.itemBase, className)}
style={
{
'--mouse-x': `${transMousePosition[0]}px`,
'--mouse-y': `${transMousePosition[1]}px`,
} as React.CSSProperties
}
rel="noreferrer"
onMouseMove={onMouseMove}
>
<Typography.Title level={5}>{extra?.title}</Typography.Title>
<Typography.Paragraph type="secondary" style={{ flex: 'auto' }}>
@@ -114,6 +192,9 @@ const RecommendItem: React.FC<RecommendItemProps> = (props) => {
return card;
};
// ======================================================================
// == Fallback ==
// ======================================================================
export const BannerRecommendsFallback: React.FC = () => {
const { isMobile } = React.use(SiteContext);
@@ -140,6 +221,9 @@ export const BannerRecommendsFallback: React.FC = () => {
);
};
// ======================================================================
// == Recommends ==
// ======================================================================
const BannerRecommends: React.FC = () => {
const { styles } = useStyle();
const [, lang] = useLocale();
@@ -186,13 +270,7 @@ const BannerRecommends: React.FC = () => {
return (
<div className={styles.container}>
{mergedExtras.map((extra, index) => (
<RecommendItem
key={`desktop-${index}`}
extra={extra}
index={index}
icons={data?.icons}
className={styles.cardItem}
/>
<RecommendItem key={`desktop-${index}`} extra={extra} index={index} icons={data?.icons} />
))}
</div>
);

View File

@@ -1,13 +1,13 @@
import React from 'react';
import { CustomerServiceOutlined, QuestionCircleOutlined, SyncOutlined } from '@ant-design/icons';
import {
Alert,
Card,
Carousel,
DatePicker,
Flex,
FloatButton,
Modal,
Progress,
Masonry,
Splitter,
Tag,
Tour,
Typography,
@@ -21,7 +21,6 @@ import SiteContext from '../../../theme/slots/SiteContext';
import { DarkContext } from './../../../hooks/useDark';
import { getCarouselStyle } from './util';
const { _InternalPanelDoNotUseOrYouWillBeFired: ModalDoNotUseOrYouWillBeFired } = Modal;
const { _InternalPanelDoNotUseOrYouWillBeFired: DatePickerDoNotUseOrYouWillBeFired } = DatePicker;
const { _InternalPanelDoNotUseOrYouWillBeFired: TourDoNotUseOrYouWillBeFired } = Tour;
const { _InternalPanelDoNotUseOrYouWillBeFired: FloatButtonDoNotUseOrYouWillBeFired } = FloatButton;
@@ -148,24 +147,26 @@ const ComponentsList: React.FC = () => {
const { styles } = useStyle();
const [locale] = useLocale(locales);
const { isMobile } = React.use(SiteContext);
const isDark = React.use(DarkContext);
const COMPONENTS = React.useMemo<Omit<ComponentItemProps, 'index'>[]>(
() => [
{
title: 'Modal',
type: 'update',
node: (
<ModalDoNotUseOrYouWillBeFired title="Ant Design" width={300}>
{locale.sampleContent}
</ModalDoNotUseOrYouWillBeFired>
),
},
// {
// title: 'Modal',
// type: 'update',
// node: (
// <ModalDoNotUseOrYouWillBeFired title="Ant Design" width={300}>
// {locale.sampleContent}
// </ModalDoNotUseOrYouWillBeFired>
// ),
// },
{
title: 'DatePicker',
type: 'update',
node: (
<DatePickerDoNotUseOrYouWillBeFired
value={dayjs('2022-11-18 14:00:00')}
value={dayjs('2025-11-22 00:00:00')}
// defaultValue={dayjs('2025-11-22 00:00:00')}
showToday={false}
presets={
isMobile
@@ -180,30 +181,30 @@ const ComponentsList: React.FC = () => {
/>
),
},
// {
// title: 'Progress',
// type: 'update',
// node: (
// <Flex gap="small" vertical>
// <Flex gap="small" align="center">
// <Progress type="circle" railColor="#e6f4ff" percent={60} size={14} />
// {locale.inProgress}
// </Flex>
// <Flex gap="small" align="center">
// <Progress type="circle" percent={100} size={14} />
// {locale.success}
// </Flex>
// <Flex gap="small" align="center">
// <Progress type="circle" status="exception" percent={88} size={14} />
// {locale.taskFailed}
// </Flex>
// </Flex>
// ),
// },
{
title: 'Progress',
type: 'update',
node: (
<Flex gap="small" vertical>
<Flex gap="small" align="center">
<Progress type="circle" railColor="#e6f4ff" percent={60} size={14} />
{locale.inProgress}
</Flex>
<Flex gap="small" align="center">
<Progress type="circle" percent={100} size={14} />
{locale.success}
</Flex>
<Flex gap="small" align="center">
<Progress type="circle" status="exception" percent={88} size={14} />
{locale.taskFailed}
</Flex>
</Flex>
),
},
{
title: 'Tour',
type: 'new',
type: 'update',
node: (
<TourDoNotUseOrYouWillBeFired
title="Ant Design"
@@ -214,9 +215,10 @@ const ComponentsList: React.FC = () => {
/>
),
},
{
title: 'FloatButton',
type: 'new',
type: 'update',
node: (
<Flex align="center" gap="large">
<FloatButtonDoNotUseOrYouWillBeFired
@@ -246,19 +248,83 @@ const ComponentsList: React.FC = () => {
// },
{
title: 'Alert',
type: 'update',
title: 'Splitter',
type: 'new',
node: (
<Alert
style={{ width: 400 }}
title="Ant Design"
description={locale.sampleContent}
closable={{ closeIcon: true, disabled: true }}
<Splitter
orientation="vertical"
style={{
height: 320,
width: 200,
background: isDark ? '#1f1f1f' : '#ffffff',
boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)',
}}
>
<Splitter.Panel defaultSize="40%" min="20%" max="70%">
<Flex justify="center" align="center" style={{ height: '100%' }}>
<Typography.Title type="secondary" level={5} style={{ whiteSpace: 'nowrap' }}>
First
</Typography.Title>
</Flex>
</Splitter.Panel>
<Splitter.Panel>
<Flex justify="center" align="center" style={{ height: '100%' }}>
<Typography.Title type="secondary" level={5} style={{ whiteSpace: 'nowrap' }}>
Second
</Typography.Title>
</Flex>
</Splitter.Panel>
</Splitter>
),
},
{
title: 'Masonry',
type: 'new',
node: (
<Masonry
columns={2}
gutter={8}
style={{
width: 300,
height: 320,
}}
items={[
{ key: '1', data: 80 },
{ key: '2', data: 60 },
{ key: '3', data: 40 },
{ key: '4', data: 120 },
{ key: '5', data: 90 },
{ key: '6', data: 40 },
{ key: '7', data: 60 },
{ key: '8', data: 70 },
{ key: '9', data: 120 },
]}
itemRender={({ data, index }) => (
<Card size="small" style={{ height: data }}>
{index + 1}
</Card>
)}
/>
),
},
// {
// title: 'Alert',
// type: 'update',
// node: (
// <Alert
// style={{ width: 400 }}
// title="Ant Design"
// description={locale.sampleContent}
// closable={{ closeIcon: true, disabled: true }}
// />
// ),
// },
],
[
isDark,
isMobile,
locale.inProgress,
locale.lastMonth,

View File

@@ -1,15 +1,18 @@
import * as React from 'react';
import { Typography } from 'antd';
import { createStyles, useTheme } from 'antd-style';
import { createStaticStyles, useTheme } from 'antd-style';
import { clsx } from 'clsx';
import SiteContext from '../../../theme/slots/SiteContext';
import GroupMaskLayer from './GroupMaskLayer';
const useStyle = createStyles(({ css, cssVar }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
box: css`
position: relative;
transition: all ${cssVar.motionDurationSlow};
background-size: cover;
background-position: 50% 0%;
background-repeat: no-repeat;
`,
container: css`
position: absolute;
@@ -41,15 +44,46 @@ export interface GroupProps {
/** 是否不使用两侧 margin */
collapse?: boolean;
decoration?: React.ReactNode;
/** 预加载的背景图片列表 */
backgroundPrefetchList?: string[];
}
const Group: React.FC<React.PropsWithChildren<GroupProps>> = (props) => {
const { id, title, titleColor, description, children, decoration, background, collapse } = props;
const {
id,
title,
titleColor,
description,
children,
decoration,
background,
collapse,
backgroundPrefetchList,
} = props;
// 预加载背景图片
React.useEffect(() => {
if (backgroundPrefetchList && backgroundPrefetchList.length > 0) {
backgroundPrefetchList.forEach((url) => {
if (url && url.startsWith('https')) {
const img = new Image();
img.src = url;
}
});
}
}, [backgroundPrefetchList]);
const token = useTheme();
const { styles } = useStyle();
const { isMobile } = React.use(SiteContext);
return (
<div style={{ backgroundColor: background }} className={styles.box}>
<div
style={
background?.startsWith('https')
? { backgroundImage: `url(${background})` }
: { backgroundColor: background }
}
className={styles.box}
>
<div className={styles.container}>{decoration}</div>
<GroupMaskLayer style={{ paddingBlock: token.marginFarSM }}>
<div className={styles.typographyWrapper}>

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { clsx } from 'clsx';
const useStyle = createStyles(({ css }) => ({
const classNames = createStaticStyles(({ css }) => ({
siteMask: css`
z-index: 1;
position: relative;
@@ -19,11 +19,10 @@ export interface GroupMaskLayerProps {
const GroupMaskLayer: React.FC<React.PropsWithChildren<GroupMaskLayerProps>> = (props) => {
const { children, className, style, onMouseMove, onMouseEnter, onMouseLeave } = props;
const { styles } = useStyle();
return (
<div
style={style}
className={clsx(className, styles.siteMask)}
className={clsx(className, classNames.siteMask)}
onMouseMove={onMouseMove}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}

View File

@@ -17,7 +17,7 @@ import {
Switch,
Tooltip,
} from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import useLocale from '../../../../hooks/useLocale';
import Tilt from './Tilt';
@@ -71,7 +71,7 @@ const locales = {
},
};
const useStyle = createStyles(({ cssVar, css }) => {
const styles = createStaticStyles(({ cssVar, css }) => {
const gap = cssVar.padding;
return {
holder: css`
@@ -106,7 +106,6 @@ const useStyle = createStyles(({ cssVar, css }) => {
const ComponentsBlock: React.FC = () => {
const [locale] = useLocale(locales);
const { styles } = useStyle();
return (
<Tilt options={{ max: 4, glare: false, scale: 0.98 }} className={styles.holder}>

View File

@@ -0,0 +1,115 @@
import React, { useEffect, useState } from 'react';
import { useEvent } from '@rc-component/util';
import { createStyles } from 'antd-style';
import { DarkContext } from '../../../../hooks/useDark';
interface BubbleProps {
size: number | string;
left?: number | string;
top?: number | string;
color: string;
offsetXMultiple?: number;
offsetYMultiple?: number;
defaultOpacity?: number;
}
const MAX_OFFSET = 200;
const Bubble = ({
size,
left,
top,
color,
offsetXMultiple = 1,
offsetYMultiple = 1,
defaultOpacity = 0.1,
}: BubbleProps) => {
const [offset, setOffset] = useState([0, 0]);
const [opacity, setOpacity] = useState(defaultOpacity);
const [sizeOffset, setSizeOffset] = useState(1);
const isDark = React.use(DarkContext);
const randomPos = useEvent(() => {
const baseOffsetX = (Math.random() - 0.5) * MAX_OFFSET * 2 * offsetXMultiple;
const baseOffsetY = (Math.random() - 0.5) * MAX_OFFSET * 2 * offsetYMultiple;
setOffset([baseOffsetX, baseOffsetY]);
setOpacity(isDark ? 0.1 + Math.random() * 0.2 : 0.1 + Math.random() * 0.05);
setSizeOffset(1 + Math.random() * 1);
});
useEffect(() => {
randomPos();
}, []);
useEffect(() => {
const randomTimeout = Math.random() * 2000 + 3000;
const id = setTimeout(randomPos, randomTimeout);
return () => clearTimeout(id);
}, [offset]);
return (
<div
aria-hidden="true"
data-desc="luminous-bubble"
style={{
opacity,
width: size,
height: size,
borderRadius: '50%',
background: color,
filter: 'blur(100px)',
left,
top,
transform: `translate(-50%, -50%) translate(${offset[0]}px, ${offset[1]}px) scale(${sizeOffset})`,
transition: 'all 5s ease-in-out',
position: 'absolute',
}}
/>
);
};
const useStyles = createStyles(({ css, cssVar }) => ({
container: css`
position: absolute;
inset: 0;
overflow: hidden;
background: ${cssVar.colorBgContainer};
`,
}));
interface LuminousBgProps {
className?: string;
}
export default function LuminousBg({ className }: LuminousBgProps) {
const { styles, cx } = useStyles();
return (
<div className={cx(styles.container, className)}>
{/* Left + Top */}
<Bubble
size={300}
color="#ee35f1"
left="0vw"
top="0vh"
offsetXMultiple={2}
defaultOpacity={0.2}
/>
{/* Left + Bottom */}
<Bubble size={300} color="#5939dc" left="30vw" top="80vh" defaultOpacity={0.1} />
{/* Right + Middle */}
<Bubble
size={300}
color="#00D6FF"
left="100vw"
top="50vh"
offsetYMultiple={2}
defaultOpacity={0.2}
/>
</div>
);
}

View File

@@ -1,48 +1,34 @@
import React, { Suspense, use } from 'react';
import { Flex, Typography } from 'antd';
import { createStyles } from 'antd-style';
import { clsx } from 'clsx';
import { useLocation } from 'dumi';
import useLocale from '../../../../hooks/useLocale';
import LinkButton from '../../../../theme/common/LinkButton';
import SiteContext from '../../../../theme/slots/SiteContext';
import type { SiteContextProps } from '../../../../theme/slots/SiteContext';
import * as utils from '../../../../theme/utils';
import GroupMaskLayer from '../GroupMaskLayer';
import '../SiteContext';
const ComponentsBlock = React.lazy(() => import('./ComponentsBlock'));
import LuminousBg from './LuminousBg';
const locales = {
cn: {
slogan: '助力设计开发者「更灵活」地搭建出「更美」的产品,让用户「快乐工作」~',
slogan: 'AI友好的「设计系统」让美与智能并进让工作充满「灵感」与「快乐」。',
start: '开始使用',
designLanguage: '设计语言',
},
en: {
slogan:
'Help designers/developers building beautiful products more flexible and working with happiness',
'AI friendly design system that combines beauty and intelligence, making work full of inspiration and joy.',
start: 'Getting Started',
designLanguage: 'Design Language',
},
};
const useStyle = createStyles(({ cssVar, css, cx }, siteConfig: SiteContextProps) => {
const useStyle = createStyles(({ cssVar, css, cx }) => {
const textShadow = `0 0 4px ${cssVar.colorBgContainer}`;
const mask = cx(css`
position: absolute;
inset: 0;
backdrop-filter: blur(2px);
opacity: 1;
background-color: rgba(255, 255, 255, 0.2);
transition: all 1s ease;
pointer-events: none;
[data-prefers-color='dark'] & {
background-color: rgba(0, 0, 0, 0.2);
}
`);
const block = cx(css`
position: absolute;
@@ -66,18 +52,12 @@ const useStyle = createStyles(({ cssVar, css, cx }, siteConfig: SiteContextProps
row-gap: ${cssVar.marginXL};
&:hover {
.${mask} {
opacity: 0;
}
.${block} {
transform: scale(0.96);
}
}
`,
mask,
typography: css`
text-align: center;
position: relative;
@@ -86,14 +66,15 @@ const useStyle = createStyles(({ cssVar, css, cx }, siteConfig: SiteContextProps
text-shadow: ${Array.from({ length: 5 }, () => textShadow).join(', ')};
h1 {
font-weight: 900 !important;
font-size: calc(${cssVar.fontSizeHeading2} * 2) !important;
line-height: ${cssVar.lineHeightHeading2} !important;
font-size: calc(${cssVar.fontSizeHeading1} * 2) !important;
line-height: ${cssVar.lineHeightHeading1} !important;
}
p {
font-size: ${cssVar.fontSizeLG} !important;
font-weight: normal !important;
font-size: calc(${cssVar.fontSizeLG} * 1.5) !important;
font-weight: 400 !important;
margin-bottom: 0;
color: ${cssVar.colorTextTertiary} !important;
}
`,
block,
@@ -107,18 +88,6 @@ const useStyle = createStyles(({ cssVar, css, cx }, siteConfig: SiteContextProps
btnWrap: css`
margin-bottom: ${cssVar.marginXL};
`,
bgImg: css`
position: absolute;
width: 240px;
`,
bgImgTop: css`
top: 0;
inset-inline-start: ${siteConfig.isMobile ? '-120px' : 0};
`,
bgImgBottom: css`
bottom: 120px;
inset-inline-end: ${siteConfig.isMobile ? 0 : '40%'};
`,
};
});
@@ -132,31 +101,11 @@ const PreviewBanner: React.FC<Readonly<React.PropsWithChildren>> = (props) => {
return (
<GroupMaskLayer>
{/* Image Left Top */}
<img
alt="bg"
src="https://gw.alipayobjects.com/zos/bmw-prod/49f963db-b2a8-4f15-857a-270d771a1204.svg"
draggable={false}
className={clsx(styles.bgImg, styles.bgImgTop)}
/>
{/* Image Right Top */}
<img
alt="bg"
src="https://gw.alipayobjects.com/zos/bmw-prod/e152223c-bcae-4913-8938-54fda9efe330.svg"
draggable={false}
className={clsx(styles.bgImg, styles.bgImgBottom)}
/>
<div className={styles.holder}>
{/* Mobile not show the component preview */}
<Suspense fallback={null}>
{siteConfig.isMobile ? null : (
<div className={styles.block}>
<ComponentsBlock />
</div>
)}
<LuminousBg />
</Suspense>
<div className={styles.mask} />
<Typography className={styles.typography}>
<h1>Ant Design</h1>
<p>{locale.slogan}</p>

View File

@@ -1,6 +1,6 @@
import React, { useMemo, useState } from 'react';
import { CSSMotionList } from '@rc-component/motion';
import { createStyles, css } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { clsx } from 'clsx';
import { COLOR_IMAGES, getClosetColor } from './colorUtil';
@@ -10,7 +10,7 @@ export interface BackgroundImageProps {
isLight?: boolean;
}
const useStyle = createStyles(({ cssVar }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
image: css`
transition: all ${cssVar.motionDurationSlow};
position: absolute;
@@ -29,7 +29,6 @@ const onHide = () => ({ opacity: 0 });
const BackgroundImage: React.FC<BackgroundImageProps> = ({ colorPrimary, isLight }) => {
const activeColor = useMemo(() => getClosetColor(colorPrimary), [colorPrimary]);
const { styles } = useStyle();
const [keyList, setKeyList] = useState<string[]>([]);

View File

@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import { ColorPicker, Flex, Input } from 'antd';
import type { ColorPickerProps, GetProp } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { generateColor } from 'antd/es/color-picker/util';
import { clsx } from 'clsx';
@@ -9,7 +9,7 @@ import { PRESET_COLORS } from './colorUtil';
type Color = Extract<GetProp<ColorPickerProps, 'value'>, string | { cleared: any }>;
const useStyle = createStyles(({ cssVar, css }) => ({
const styles = createStaticStyles(({ cssVar, css }) => ({
color: css`
width: calc(${cssVar.controlHeightLG} / 2);
height: calc(${cssVar.controlHeightLG} / 2);
@@ -69,8 +69,6 @@ const DebouncedColorPicker: React.FC<React.PropsWithChildren<ThemeColorPickerPro
};
const ThemeColorPicker: React.FC<ThemeColorPickerProps> = ({ value, onChange, id }) => {
const { styles } = useStyle();
const matchColors = React.useMemo(() => {
const valueStr = generateColor(value || '').toRgbString();
const colors = PRESET_COLORS.map((color) => {

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import { Flex } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { clsx } from 'clsx';
import useLocale from '../../../../hooks/useLocale';
@@ -32,7 +32,7 @@ const locales = {
},
};
const useStyle = createStyles(({ cssVar, css }) => ({
const styles = createStaticStyles(({ cssVar, css }) => ({
themeCard: css`
border-radius: ${cssVar.borderRadius};
cursor: pointer;
@@ -80,7 +80,6 @@ export interface ThemePickerProps {
const ThemePicker: React.FC<ThemePickerProps> = (props) => {
const { value, id, onChange } = props;
const { styles } = useStyle();
const [locale] = useLocale(locales);
return (
<Flex gap="large" wrap>

View File

@@ -20,7 +20,7 @@ import {
theme,
Typography,
} from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { generateColor } from 'antd/es/color-picker/util';
import { clsx } from 'clsx';
import { useLocation } from 'dumi';
@@ -92,7 +92,7 @@ const locales = {
};
// ============================= Style =============================
const useStyle = createStyles(({ cssVar, css, cx }) => {
const styles = createStaticStyles(({ cssVar, css, cx }) => {
const { carousel } = getCarouselStyle();
const demo = css`
overflow: hidden;
@@ -345,7 +345,6 @@ function rgbToColorMatrix(color: string) {
}
const Theme: React.FC = () => {
const { styles } = useStyle();
const [locale, lang] = useLocale(locales);
const isZhCN = lang === 'cn';
const { search } = useLocation();

View File

@@ -0,0 +1,198 @@
import React from 'react';
import { CheckOutlined, CloseOutlined, DownOutlined } from '@ant-design/icons';
import {
Alert,
App,
Button,
Card,
Checkbox,
ColorPicker,
ConfigProvider,
Dropdown,
Flex,
Modal,
Progress,
Radio,
Select,
Slider,
Space,
Steps,
Switch,
} from 'antd';
import type { ConfigProviderProps } from 'antd';
import { createStyles } from 'antd-style';
import clsx from 'clsx';
import useLocale from '../../../../hooks/useLocale';
const { _InternalPanelDoNotUseOrYouWillBeFired: ModalPanel } = Modal;
const locales = {
cn: {
range: '设置范围',
text: 'Ant Design 使用 CSS-in-JS 技术以提供动态与混合主题的能力。与此同时,我们使用组件级别的 CSS-in-JS 解决方案,让你的应用获得更好的性能。',
infoText: '信息内容展示',
dropdown: '下拉菜单',
finished: '已完成',
inProgress: '进行中',
waiting: '等待中',
option: '选项',
apple: '苹果',
banana: '香蕉',
orange: '橘子',
watermelon: '西瓜',
primary: '主要按钮',
danger: '危险按钮',
default: '默认按钮',
dashed: '虚线按钮',
icon: '图标按钮',
hello: '你好Ant Design!',
release: 'Ant Design 6.0 正式发布!',
},
en: {
range: 'Set Range',
text: 'Ant Design use CSS-in-JS technology to provide dynamic & mix theme ability. And which use component level CSS-in-JS solution get your application a better performance.',
infoText: 'Info Text',
dropdown: 'Dropdown',
finished: 'Finished',
inProgress: 'In Progress',
waiting: 'Waiting',
option: 'Option',
apple: 'Apple',
banana: 'Banana',
orange: 'Orange',
watermelon: 'Watermelon',
primary: 'Primary',
danger: 'Danger',
default: 'Default',
dashed: 'Dashed',
icon: 'Icon',
hello: 'Hello, Ant Design!',
release: 'Ant Design 6.0 is released!',
},
};
const useStyle = createStyles(({ css, cssVar }) => {
return {
container: css({
backgroundColor: `color-mix(in srgb, ${cssVar.colorBgContainer} 70%, transparent)`,
backdropFilter: 'blur(12px)',
}),
flexAuto: css({ flex: 'auto' }),
};
});
interface ComponentsBlockProps {
config?: ConfigProviderProps;
style?: React.CSSProperties;
className?: string;
containerClassName?: string;
}
const ComponentsBlock: React.FC<ComponentsBlockProps> = (props) => {
const [locale] = useLocale(locales);
const { styles } = useStyle();
const { config, style, className, containerClassName } = props;
return (
<ConfigProvider {...config}>
<Card className={clsx(containerClassName, styles.container)}>
<App>
<Flex vertical gap="middle" style={style} className={className}>
<ModalPanel title="Ant Design" width="100%">
{locale.text}
</ModalPanel>
<Alert title={locale.infoText} type="info" />
{/* Line */}
<Flex gap="middle">
<div style={{ flex: 'none' }}>
<Space.Compact>
<Button>{locale.dropdown}</Button>
<Dropdown
menu={{
items: Array.from({ length: 5 }).map((_, index) => ({
key: `opt${index}`,
label: `${locale.option} ${index}`,
})),
}}
>
<Button icon={<DownOutlined />} />
</Dropdown>
</Space.Compact>
</div>
<ColorPicker style={{ flex: 'none' }} />
<Select
style={{ flex: 'auto' }}
mode="multiple"
maxTagCount="responsive"
defaultValue={[{ value: 'apple' }, { value: 'banana' }]}
options={[
{ value: 'apple', label: locale.apple },
{ value: 'banana', label: locale.banana },
{ value: 'orange', label: locale.orange },
{ value: 'watermelon', label: locale.watermelon },
]}
/>
</Flex>
<Progress style={{ margin: 0 }} percent={60} />
<Steps
current={1}
items={[
{ title: locale.finished },
{ title: locale.inProgress },
{ title: locale.waiting },
]}
/>
{/* Line */}
<Slider
style={{ marginInline: 20 }}
range
marks={{
0: '0°C',
26: '26°C',
37: '37°C',
100: {
style: { color: '#f50' },
label: <strong>100°C</strong>,
},
}}
defaultValue={[26, 37]}
/>
{/* Line */}
<Flex gap="middle">
<Button type="primary" className={styles.flexAuto}>
{locale.primary}
</Button>
<Button type="primary" className={styles.flexAuto} danger>
{locale.danger}
</Button>
<Button className={styles.flexAuto}>{locale.default}</Button>
<Button className={styles.flexAuto} type="dashed">
{locale.dashed}
</Button>
</Flex>
{/* Line */}
<Flex gap="middle">
<Switch
defaultChecked
checkedChildren={<CheckOutlined />}
unCheckedChildren={<CloseOutlined />}
style={{ width: 48 }}
/>
<Checkbox.Group
options={[locale.apple, locale.banana, locale.orange]}
defaultValue={[locale.apple]}
/>
<Radio.Group defaultValue={locale.apple} options={[locale.apple, locale.banana]} />
</Flex>
</Flex>
</App>
</Card>
</ConfigProvider>
);
};
export default ComponentsBlock;

View File

@@ -0,0 +1,180 @@
import * as React from 'react';
import { ConfigProvider, Flex, theme } from 'antd';
import { createStyles } from 'antd-style';
import clsx from 'clsx';
import { DarkContext } from '../../../../hooks/useDark';
import useLocale from '../../../../hooks/useLocale';
import Group from '../Group';
import ComponentsBlock from './ComponentsBlock';
import usePreviewThemes from './previewThemes';
const locales = {
cn: {
themeTitle: '定制主题,随心所欲',
themeDesc: '开放样式算法与语义化结构,让你与 AI 一起轻松定制主题',
},
en: {
themeTitle: 'Flexible theme customization',
themeDesc:
'Open style algorithms and semantic structures make it easy for you and AI to customize themes',
},
};
const useStyles = createStyles(({ css, cssVar }) => ({
container: css({
width: '100%',
color: cssVar.colorText,
lineHeight: cssVar.lineHeight,
fontSize: cssVar.fontSize,
fontFamily: cssVar.fontFamily,
alignItems: 'stretch',
justifyContent: 'center',
}),
// List
list: css({
flex: 'auto',
margin: 0,
padding: 0,
listStyleType: 'none',
display: 'flex',
flexDirection: 'column',
gap: cssVar.paddingMD,
}),
listItem: css({
margin: 0,
fontSize: cssVar.fontSizeLG,
lineHeight: cssVar.lineHeightLG,
paddingBlock: cssVar.padding,
paddingInline: cssVar.paddingLG,
border: `${cssVar.lineWidth} ${cssVar.lineType} ${cssVar.colorBorderSecondary}`,
borderRadius: cssVar.borderRadius,
borderColor: 'transparent',
transition: `all ${cssVar.motionDurationMid} ${cssVar.motionEaseInOut}`,
'&:hover:not(.active)': {
borderColor: cssVar.colorPrimaryBorder,
backgroundColor: cssVar.colorPrimaryBg,
cursor: 'pointer',
},
'&:focus-visible': {
outline: `2px solid ${cssVar.colorPrimary}`,
outlineOffset: 2,
},
'&.active': {
borderColor: cssVar.colorPrimary,
backgroundColor: cssVar.colorPrimaryBg,
color: cssVar.colorPrimary,
},
// ========= Dark =========
'&.dark': {
color: cssVar.colorTextLightSolid,
backgroundColor: 'transparent',
'&:hover, &.active': {
borderColor: cssVar.colorTextLightSolid,
backgroundColor: 'transparent',
},
},
}),
// Components
componentsBlockContainer: css({
flex: 'auto',
display: 'flex',
padding: cssVar.paddingXL,
justifyContent: 'center',
border: `${cssVar.lineWidth} ${cssVar.lineType} ${cssVar.colorBorderSecondary}`,
borderRadius: cssVar.borderRadius,
boxShadow: cssVar.boxShadow,
}),
componentsBlock: css({
flex: 'none',
maxWidth: `calc(420px + ${cssVar.paddingXL} * 2)`,
}),
}));
export default function ThemePreview() {
const [locale] = useLocale(locales);
const { styles } = useStyles();
const isDark = React.use(DarkContext);
const previewThemes = usePreviewThemes();
const [activeName, setActiveName] = React.useState(() => previewThemes[0].name);
React.useEffect(() => {
const defaultThemeName = isDark ? 'dark' : 'light';
const targetTheme =
process.env.NODE_ENV !== 'production'
? previewThemes[previewThemes.length - 1].name
: previewThemes.find((theme) => theme.key === defaultThemeName)?.name ||
previewThemes[0].name;
setActiveName(targetTheme);
}, [isDark]);
// 收集所有背景图片用于预加载
const backgroundPrefetchList = React.useMemo(
() => previewThemes.map((theme) => theme.bgImg).filter((img): img is string => !!img),
[previewThemes],
);
const handleThemeClick = (name: string) => {
setActiveName(name);
};
const handleKeyDown = (event: React.KeyboardEvent, name: string) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
handleThemeClick(name);
}
};
const activeTheme = previewThemes.find((theme) => theme.name === activeName);
return (
<ConfigProvider theme={{ algorithm: theme.defaultAlgorithm }}>
<Group
title={locale.themeTitle}
description={locale.themeDesc}
background={activeTheme?.bgImg}
titleColor={activeTheme?.bgImgDark ? '#fff' : undefined}
backgroundPrefetchList={backgroundPrefetchList}
>
<Flex className={styles.container} gap="large">
<div className={styles.list} role="tablist" aria-label="Theme selection">
{previewThemes.map((theme) => (
<div
className={clsx(
styles.listItem,
activeName === theme.name && 'active',
activeTheme?.bgImgDark && 'dark',
)}
key={theme.name}
role="tab"
tabIndex={activeName === theme.name ? 0 : -1}
aria-selected={activeName === theme.name}
onClick={() => handleThemeClick(theme.name)}
onKeyDown={(event) => handleKeyDown(event, theme.name)}
>
{theme.name}
</div>
))}
</div>
<ComponentsBlock
key={activeName}
config={activeTheme?.props}
className={styles.componentsBlock}
containerClassName={styles.componentsBlockContainer}
/>
</Flex>
</Group>
</ConfigProvider>
);
}

View File

@@ -0,0 +1,188 @@
import { useMemo } from 'react';
import { theme } from 'antd';
import type { ConfigProviderProps } from 'antd';
import { createStyles } from 'antd-style';
import clsx from 'clsx';
import type { UseTheme } from '.';
const useStyles = createStyles(({ css, cssVar }) => {
return {
boxBorder: css({
border: `${cssVar.lineWidth} ${cssVar.lineType} color-mix(in srgb,${cssVar.colorBorder} 80%, #000)`,
}),
alertRoot: css({
color: cssVar.colorInfoText,
textShadow: `0 1px 0 rgba(255, 255, 255, 0.8)`,
}),
modalContainer: css({
padding: 0,
borderRadius: cssVar.borderRadiusLG,
}),
modalHeader: css({
borderBottom: `${cssVar.lineWidth} ${cssVar.lineType} ${cssVar.colorSplit}`,
padding: `${cssVar.padding} ${cssVar.paddingLG}`,
}),
modalBody: css({
padding: `${cssVar.padding} ${cssVar.paddingLG}`,
}),
modalFooter: css({
borderTop: `${cssVar.lineWidth} ${cssVar.lineType} ${cssVar.colorSplit}`,
padding: `${cssVar.padding} ${cssVar.paddingLG}`,
backgroundColor: cssVar.colorBgContainerDisabled,
boxShadow: `inset 0 1px 0 ${cssVar.colorBgContainer}`,
}),
buttonRoot: css({
backgroundImage: `linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.2))`,
boxShadow: `inset 0 1px 0 rgba(255, 255, 255, 0.15)`,
transition: 'none',
borderColor: `rgba(0, 0, 0, 0.3)`,
textShadow: `0 -1px 0 rgba(0, 0, 0, 0.2)`,
'&:hover, &:active': {
backgroundImage: `linear-gradient(rgba(0, 0, 0, 0.15) 100%)`,
},
'&:active': {
boxShadow: `inset 0 1px 3px rgba(0, 0, 0, 0.15)`,
},
}),
buttonColorDefault: css({
textShadow: 'none',
color: cssVar.colorText,
borderBottomColor: 'rgba(0, 0, 0, 0.5)',
}),
popupBox: css({
borderRadius: cssVar.borderRadiusLG,
backgroundColor: cssVar.colorBgContainer,
ul: {
paddingInline: 0,
},
}),
dropdownItem: css({
borderRadius: 0,
transition: 'none',
paddingBlock: cssVar.paddingXXS,
paddingInline: cssVar.padding,
'&:hover, &:active, &:focus': {
backgroundImage: `linear-gradient(to bottom, ${cssVar.colorPrimaryHover}, ${cssVar.colorPrimary})`,
color: cssVar.colorTextLightSolid,
},
}),
selectPopupRoot: css({
paddingInline: 0,
}),
switchRoot: css({
boxShadow: `inset 0 1px 3px rgba(0, 0, 0, 0.4)`,
}),
progressTrack: css({
backgroundImage: `linear-gradient(to bottom, ${cssVar.colorPrimaryHover}, ${cssVar.colorPrimary})`,
borderRadius: cssVar.borderRadiusSM,
}),
progressRail: css({
borderRadius: cssVar.borderRadiusSM,
}),
};
});
const useBootstrapTheme: UseTheme = () => {
const { styles } = useStyles();
return useMemo<ConfigProviderProps>(
() => ({
theme: {
algorithm: theme.defaultAlgorithm,
token: {
borderRadius: 4,
borderRadiusLG: 6,
colorInfo: '#3a87ad',
},
components: {
Tooltip: {
fontSize: 12,
},
Checkbox: {
colorBorder: '#666',
borderRadius: 2,
algorithm: true,
},
Radio: {
colorBorder: '#666',
borderRadius: 2,
algorithm: true,
},
},
},
wave: {
showEffect: () => {},
},
modal: {
classNames: {
container: clsx(styles.boxBorder, styles.modalContainer),
header: styles.modalHeader,
body: styles.modalBody,
footer: styles.modalFooter,
},
},
button: {
classNames: ({ props }) => ({
root: clsx(styles.buttonRoot, props.color === 'default' && styles.buttonColorDefault),
}),
},
alert: {
className: styles.alertRoot,
},
colorPicker: {
classNames: {
root: styles.boxBorder,
popup: {
root: clsx(styles.boxBorder, styles.popupBox),
},
},
arrow: false,
},
checkbox: {
classNames: {},
},
dropdown: {
classNames: {
root: clsx(styles.boxBorder, styles.popupBox),
item: styles.dropdownItem,
},
},
select: {
classNames: {
root: styles.boxBorder,
popup: {
root: clsx(styles.boxBorder, styles.selectPopupRoot),
listItem: styles.dropdownItem,
},
},
},
switch: {
classNames: {
root: styles.switchRoot,
},
},
progress: {
classNames: {
track: styles.progressTrack,
rail: styles.progressRail,
},
styles: {
rail: {
height: 20,
},
track: { height: 20 },
},
},
}),
[],
);
};
export default useBootstrapTheme;

View File

@@ -0,0 +1,104 @@
import { useMemo } from 'react';
import { theme } from 'antd';
import type { ConfigProviderProps } from 'antd';
import { createStyles } from 'antd-style';
import type { UseTheme } from '.';
const useStyles = createStyles(({ css, cssVar }) => {
const sharedBorder = {
border: `${cssVar.lineWidth} ${cssVar.lineType} ${cssVar.colorBorder}`,
};
return {
sharedBorder,
progressTrack: css({
...sharedBorder,
marginInlineStart: `calc(-1 * ${cssVar.lineWidth})`,
marginBlockStart: `calc(-1 * ${cssVar.lineWidth})`,
}),
};
});
const useCartoonTheme: UseTheme = () => {
const { styles } = useStyles();
return useMemo<ConfigProviderProps>(
() => ({
theme: {
algorithm: theme.defaultAlgorithm,
token: {
colorText: '#51463B',
colorPrimary: '#225555',
colorError: '#DA8787',
colorInfo: '#9CD3D3',
colorInfoBorder: '#225555',
colorBorder: '#225555',
colorBorderSecondary: '#225555',
lineWidth: 2,
lineWidthBold: 2,
borderRadius: 18,
borderRadiusLG: 18,
borderRadiusSM: 18,
controlHeightSM: 28,
controlHeight: 36,
colorBgBase: '#FAFAEE',
},
components: {
Button: {
primaryShadow: 'none',
dangerShadow: 'none',
defaultShadow: 'none',
},
Modal: {
boxShadow: 'none',
},
Card: {
colorBgContainer: '#BBAA99',
},
Tooltip: {
borderRadius: 6,
colorBorder: '#225555',
algorithm: true,
},
Select: {
optionSelectedBg: '#CBC4AF',
},
},
},
// app: {
// className: styles.app,
// },
modal: {
classNames: {
container: styles.sharedBorder,
},
},
colorPicker: {
arrow: false,
},
popover: {
classNames: {
container: styles.sharedBorder,
},
},
progress: {
classNames: {
rail: styles.sharedBorder,
track: styles.progressTrack,
},
styles: {
rail: {
height: 16,
},
track: {
height: 16,
},
},
},
}),
[],
);
};
export default useCartoonTheme;

View File

@@ -0,0 +1,157 @@
import { useMemo } from 'react';
import { theme } from 'antd';
import type { ConfigProviderProps } from 'antd';
import { createStyles } from 'antd-style';
import clsx from 'clsx';
import type { UseTheme } from '.';
const useStyles = createStyles(({ css, cssVar }) => {
const lightBorder = {
border: `${cssVar.lineWidth} solid ${cssVar.colorPrimary}`,
boxShadow: `0 0 3px ${cssVar.colorPrimary}, inset 0 0 10px ${cssVar.colorPrimary}`,
};
return {
lightBorder,
app: css({
textShadow: `0 0 5px color-mix(in srgb, currentColor 50%, transparent)`,
}),
modalContainer: css({
...lightBorder,
padding: 0,
}),
modalHeader: css({
padding: `${cssVar.padding} ${cssVar.paddingLG}`,
margin: 0,
position: 'relative',
'&:after': {
...lightBorder,
content: '""',
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
border: 0,
height: cssVar.lineWidth,
background: cssVar.colorPrimary,
},
}),
modalBody: css({
padding: `${cssVar.padding} ${cssVar.paddingLG}`,
}),
modalFooter: css({
padding: `${cssVar.padding} ${cssVar.paddingLG}`,
}),
buttonRoot: css({
...lightBorder,
border: undefined,
borderWidth: cssVar.lineWidth,
borderColor: cssVar.colorPrimary,
}),
buttonRootSolid: css({
color: cssVar.colorBgContainer,
border: 'none',
fontWeight: 'bolder',
}),
buttonRootSolidDanger: css({
boxShadow: `0 0 5px ${cssVar.colorError}`,
}),
colorPickerBody: css({
pointerEvents: 'none',
}),
tooltipRoot: css({
padding: cssVar.padding,
}),
tooltipContainer: css({
...lightBorder,
color: cssVar.colorPrimary,
}),
progressTrack: css({
backgroundColor: cssVar.colorPrimary,
}),
};
});
const useGeekTheme: UseTheme = () => {
const { styles } = useStyles();
return useMemo<ConfigProviderProps>(
() => ({
theme: {
algorithm: theme.darkAlgorithm,
token: {
borderRadius: 0,
lineWidth: 2,
colorPrimary: '#39ff14',
colorText: '#39ff14',
controlHeightSM: 26,
controlHeight: 34,
},
},
app: {
className: styles.app,
},
modal: {
classNames: {
container: styles.modalContainer,
header: styles.modalHeader,
body: styles.modalBody,
footer: styles.modalFooter,
},
},
button: {
classNames: ({ props }) => ({
root: clsx(
styles.buttonRoot,
props.variant === 'solid' && styles.buttonRootSolid,
props.variant === 'solid' && props.danger && styles.buttonRootSolidDanger,
),
}),
},
alert: {
className: styles.lightBorder,
},
colorPicker: {
classNames: {
root: styles.lightBorder,
body: styles.colorPickerBody,
},
arrow: false,
},
select: {
classNames: {
root: styles.lightBorder,
},
},
input: {
classNames: {
root: styles.lightBorder,
},
},
inputNumber: {
classNames: {
root: styles.lightBorder,
},
},
tooltip: {
arrow: false,
classNames: {
root: styles.tooltipRoot,
container: styles.tooltipContainer,
},
},
progress: {
classNames: {
track: styles.progressTrack,
},
},
}),
[],
);
};
export default useGeekTheme;

View File

@@ -0,0 +1,157 @@
import { useMemo } from 'react';
import { theme } from 'antd';
import type { ConfigProviderProps } from 'antd';
import { createStyles } from 'antd-style';
import clsx from 'clsx';
import type { UseTheme } from '.';
const useStyles = createStyles(({ css, cssVar }) => {
const glassBorder = {
border: `${cssVar.lineWidth} solid rgba(255,255,255,0.3)`,
boxShadow: [
`${cssVar.boxShadowSecondary}`,
`inset 0 0 5px 2px rgba(255, 255, 255, 0.3)`,
`inset 0 5px 2px rgba(255, 255, 255, 0.2)`,
].join(','),
};
const glassBox = {
...glassBorder,
background: `color-mix(in srgb, ${cssVar.colorBgContainer} 15%, transparent)`,
backdropFilter: 'blur(12px)',
};
return {
glassBorder,
glassBox,
notBackdropFilter: css({
backdropFilter: 'none',
}),
app: css({
textShadow: '0 1px rgba(0,0,0,0.1)',
}),
cardRoot: css({
...glassBox,
backgroundColor: `color-mix(in srgb, ${cssVar.colorBgContainer} 40%, transparent)`,
}),
modalContainer: css({
...glassBox,
backdropFilter: 'none',
}),
buttonRoot: css({
...glassBorder,
}),
buttonRootDefaultColor: css({
background: 'transparent',
color: cssVar.colorText,
'&:hover': {
background: 'rgba(255,255,255,0.2)',
color: `color-mix(in srgb, ${cssVar.colorText} 90%, transparent)`,
},
'&:active': {
background: 'rgba(255,255,255,0.1)',
color: `color-mix(in srgb, ${cssVar.colorText} 80%, transparent)`,
},
}),
dropdownRoot: css({
...glassBox,
borderRadius: cssVar.borderRadiusLG,
ul: {
background: 'transparent',
},
}),
switchRoot: css({ ...glassBorder, border: 'none' }),
};
});
const useGlassTheme: UseTheme = () => {
const { styles } = useStyles();
return useMemo<ConfigProviderProps>(
() => ({
theme: {
algorithm: theme.defaultAlgorithm,
token: {
borderRadius: 12,
borderRadiusLG: 12,
borderRadiusSM: 12,
borderRadiusXS: 12,
motionDurationSlow: '0.2s',
motionDurationMid: '0.1s',
motionDurationFast: '0.05s',
},
},
app: {
className: styles.app,
},
card: {
classNames: {
root: styles.cardRoot,
},
},
modal: {
classNames: {
container: styles.modalContainer,
},
},
button: {
classNames: ({ props }) => ({
root: clsx(
styles.buttonRoot,
(props.variant !== 'solid' || props.color === 'default' || props.type === 'default') &&
styles.buttonRootDefaultColor,
),
}),
},
alert: {
className: clsx(styles.glassBox, styles.notBackdropFilter),
},
colorPicker: {
arrow: false,
},
dropdown: {
classNames: {
root: styles.dropdownRoot,
},
},
select: {
classNames: {
root: clsx(styles.glassBox, styles.notBackdropFilter),
popup: {
root: styles.glassBox,
},
},
},
popover: {
classNames: {
container: styles.glassBox,
},
},
switch: {
classNames: {
root: styles.switchRoot,
},
},
progress: {
classNames: {
track: styles.glassBorder,
},
styles: {
track: {
height: 12,
},
rail: {
height: 12,
},
},
},
}),
[],
);
};
export default useGlassTheme;

View File

@@ -0,0 +1,184 @@
import { useMemo } from 'react';
import { theme } from 'antd';
import type { ConfigProviderProps } from 'antd';
import { createStyles } from 'antd-style';
import type { UseTheme } from '.';
const useStyles = createStyles(({ css, cssVar }) => {
const illustrationBorder = {
border: `${cssVar.lineWidth} solid ${cssVar.colorBorder}`,
};
const illustrationBox = {
...illustrationBorder,
boxShadow: `4px 4px 0 ${cssVar.colorBorder}`,
};
return {
illustrationBorder,
illustrationBox,
buttonRoot: css({
...illustrationBox,
fontWeight: 600,
textTransform: 'uppercase',
letterSpacing: '0.5px',
}),
modalContainer: css({
...illustrationBox,
}),
tooltipRoot: css({
padding: cssVar.padding,
}),
popupBox: css({
...illustrationBox,
borderRadius: cssVar.borderRadiusLG,
backgroundColor: cssVar.colorBgContainer,
}),
progressRail: css({
border: `${cssVar.lineWidth} solid ${cssVar.colorBorder}`,
boxShadow: `2px 2px 0 ${cssVar.colorBorder}`,
}),
progressTrack: css({
border: 'none',
}),
inputNumberActions: css({
width: 12,
}),
};
});
const useIllustrationTheme: UseTheme = () => {
const { styles } = useStyles();
return useMemo<ConfigProviderProps>(
() => ({
theme: {
algorithm: theme.defaultAlgorithm,
token: {
colorText: '#2C2C2C',
colorPrimary: '#52C41A',
colorSuccess: '#51CF66',
colorWarning: '#FFD93D',
colorError: '#FA5252',
colorInfo: '#4DABF7',
colorBorder: '#2C2C2C',
colorBorderSecondary: '#2C2C2C',
lineWidth: 3,
lineWidthBold: 3,
borderRadius: 12,
borderRadiusLG: 16,
borderRadiusSM: 8,
controlHeight: 40,
controlHeightSM: 34,
controlHeightLG: 48,
fontSize: 15,
fontWeightStrong: 600,
colorBgBase: '#FFF9F0',
colorBgContainer: '#FFFFFF',
},
components: {
Button: {
primaryShadow: 'none',
dangerShadow: 'none',
defaultShadow: 'none',
fontWeight: 600,
},
Modal: {
boxShadow: 'none',
},
Card: {
boxShadow: '4px 4px 0 #2C2C2C',
colorBgContainer: '#FFF0F6',
},
Tooltip: {
colorBorder: '#2C2C2C',
colorBgSpotlight: 'rgba(100, 100, 100, 0.95)',
borderRadius: 8,
},
Select: {
optionSelectedBg: 'transparent',
},
Slider: {
dotBorderColor: '#237804',
dotActiveBorderColor: '#237804',
colorPrimaryBorder: '#237804',
colorPrimaryBorderHover: '#237804',
},
},
},
button: {
classNames: {
root: styles.buttonRoot,
},
},
modal: {
classNames: {
container: styles.modalContainer,
},
},
alert: {
className: styles.illustrationBorder,
},
colorPicker: {
arrow: false,
classNames: {
root: styles.illustrationBox,
},
},
popover: {
classNames: {
container: styles.illustrationBox,
},
},
tooltip: {
arrow: false,
classNames: {
root: styles.tooltipRoot,
container: styles.illustrationBox,
},
},
dropdown: {
classNames: {
root: styles.popupBox,
},
},
select: {
classNames: {
root: styles.illustrationBox,
popup: {
root: styles.popupBox,
},
},
},
input: {
classNames: {
root: styles.illustrationBox,
},
},
inputNumber: {
classNames: {
root: styles.illustrationBox,
actions: styles.inputNumberActions,
},
},
progress: {
classNames: {
rail: styles.progressRail,
track: styles.progressTrack,
},
styles: {
rail: {
height: 16,
},
track: {
height: 10,
},
},
},
}),
[],
);
};
export default useIllustrationTheme;

View File

@@ -0,0 +1,132 @@
import React from 'react';
import type { ConfigProviderProps } from 'antd';
import { theme } from 'antd';
import useLocale from '../../../../../hooks/useLocale';
import useBootstrapTheme from './bootstrapTheme';
import useCartoonTheme from './cartoonTheme';
import useGeekTheme from './geekTheme';
import useGlassTheme from './glassTheme';
import useIllustrationTheme from './illustrationTheme';
import useMuiTheme from './muiTheme';
import useShadcnTheme from './shadcnTheme';
type PreviewThemeConfig = {
name: string;
key?: string;
props?: ConfigProviderProps;
bgImg?: string;
bgImgDark?: true;
};
const locales = {
cn: {
default: '默认风格',
dark: '暗黑风格',
geek: '极客风格',
glass: '玻璃风格',
mui: '类 MUI 风格',
shadcn: '类 shadcn 风格',
bootstrap: '类 Bootstrap 拟物化风格',
cartoon: '卡通风格',
illustration: '插画风格',
},
en: {
default: 'Default Style',
dark: 'Dark Style',
geek: 'Geek Style',
glass: 'Glass Style',
mui: 'MUI-like Style',
shadcn: 'shadcn-like Style',
bootstrap: 'Bootstrap Skeuomorphism',
cartoon: 'Cartoon Style',
illustration: 'Illustration Style',
},
};
export type UseTheme = () => ConfigProviderProps;
export default function usePreviewThemes() {
const [locale] = useLocale(locales);
const cartoonTheme = useCartoonTheme();
const illustrationTheme = useIllustrationTheme();
const geekTheme = useGeekTheme();
const glassTheme = useGlassTheme();
const muiTheme = useMuiTheme();
const shadcnTheme = useShadcnTheme();
const bootstrapTheme = useBootstrapTheme();
return React.useMemo<PreviewThemeConfig[]>(() => {
return [
{
name: locale.default,
key: 'light',
bgImg:
'https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*T8IlRaNez08AAAAARwAAAAgAegCCAQ/original',
props: {
theme: {
algorithm: theme.defaultAlgorithm,
},
},
},
{
name: locale.dark,
key: 'dark',
dark: true,
bgImg:
'https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*ETkNSJ-oUGwAAAAAQ_AAAAgAegCCAQ/original',
bgImgDark: true,
props: {
theme: {
algorithm: theme.darkAlgorithm,
},
},
},
{
name: locale.mui,
bgImg:
'https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*IFkZRpIKEEkAAAAAQzAAAAgAegCCAQ/original',
props: muiTheme,
},
{
name: locale.shadcn,
bgImg:
'https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*56tPQbwgFyEAAAAARuAAAAgAegCCAQ/original',
props: shadcnTheme,
},
{
name: locale.cartoon,
bgImg:
'https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*tgpBT7vYIUsAAAAAQ-AAAAgAegCCAQ/original',
props: cartoonTheme,
},
{
name: locale.illustration,
bgImg:
'https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*HuVGQKqOER0AAAAARsAAAAgAegCCAQ/original',
props: illustrationTheme,
},
{
name: locale.bootstrap,
bgImg:
'https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*ZrLfQIO34x4AAAAAS4AAAAgAegCCAQ/original',
props: bootstrapTheme,
},
{
name: locale.glass,
bgImg:
'https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*PbKXQLie7OAAAAAARTAAAAgAegCCAQ/original',
bgImgDark: true,
props: glassTheme,
},
{
name: locale.geek,
bgImg:
'https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*fzA2T4ms154AAAAARtAAAAgAegCCAQ/original',
bgImgDark: true,
props: geekTheme,
},
];
}, [locale]);
}

View File

@@ -0,0 +1,288 @@
import { useMemo } from 'react';
import raf from '@rc-component/util/lib/raf';
import { theme } from 'antd';
import type { ConfigProviderProps, GetProp } from 'antd';
import { createStyles } from 'antd-style';
import clsx from 'clsx';
import type { UseTheme } from '.';
type WaveConfig = GetProp<ConfigProviderProps, 'wave'>;
// Prepare effect holder
const createHolder = (node: HTMLElement) => {
const { borderWidth } = getComputedStyle(node);
const borderWidthNum = Number.parseInt(borderWidth, 10);
const div = document.createElement('div');
div.style.position = 'absolute';
div.style.inset = `-${borderWidthNum}px`;
div.style.borderRadius = 'inherit';
div.style.background = 'transparent';
div.style.zIndex = '999';
div.style.pointerEvents = 'none';
div.style.overflow = 'hidden';
node.appendChild(div);
return div;
};
const createDot = (holder: HTMLElement, color: string, left: number, top: number, size = 0) => {
const dot = document.createElement('div');
dot.style.position = 'absolute';
dot.style.left = `${left}px`;
dot.style.top = `${top}px`;
dot.style.width = `${size}px`;
dot.style.height = `${size}px`;
dot.style.borderRadius = '50%';
dot.style.background = color;
dot.style.transform = 'translate3d(-50%, -50%, 0)';
dot.style.transition = 'all 1s ease-out';
holder.appendChild(dot);
return dot;
};
// Inset Effect
const showInsetEffect: WaveConfig['showEffect'] = (node, { event, component }) => {
if (component !== 'Button') {
return;
}
const holder = createHolder(node);
const rect = holder.getBoundingClientRect();
const left = event.clientX - rect.left;
const top = event.clientY - rect.top;
const dot = createDot(holder, 'rgba(255, 255, 255, 0.65)', left, top);
// Motion
raf(() => {
dot.ontransitionend = () => {
holder.remove();
};
dot.style.width = '200px';
dot.style.height = '200px';
dot.style.opacity = '0';
});
};
const useStyles = createStyles(({ css }) => {
return {
buttonPrimary: css({
backgroundColor: '#1976d2',
color: '#ffffff',
border: 'none',
fontWeight: 500,
textTransform: 'uppercase',
letterSpacing: '0.02857em',
boxShadow:
'0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)',
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
}),
buttonDefault: css({
backgroundColor: '#ffffff',
color: 'rgba(0, 0, 0, 0.87)',
border: '1px solid rgba(0, 0, 0, 0.23)',
fontWeight: 500,
textTransform: 'uppercase',
letterSpacing: '0.02857em',
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
}),
buttonDanger: css({
backgroundColor: '#d32f2f',
color: '#ffffff',
border: 'none',
fontWeight: 500,
textTransform: 'uppercase',
letterSpacing: '0.02857em',
boxShadow:
'0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)',
}),
inputRoot: css({
borderColor: 'rgba(0, 0, 0, 0.23)',
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
}),
inputElement: css({
color: 'rgba(0, 0, 0, 0.87)',
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
}),
inputError: css({
borderColor: '#d32f2f',
}),
selectRoot: css({
borderColor: 'rgba(0, 0, 0, 0.23)',
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
}),
selectPopup: css({
borderRadius: '4px',
boxShadow:
'0px 5px 5px -3px rgba(0,0,0,0.2), 0px 8px 10px 1px rgba(0,0,0,0.14), 0px 3px 14px 2px rgba(0,0,0,0.12)',
}),
};
});
const useMuiTheme: UseTheme = () => {
const { styles } = useStyles();
return useMemo<ConfigProviderProps>(
() => ({
theme: {
algorithm: theme.defaultAlgorithm,
token: {
colorPrimary: '#1976d2',
colorSuccess: '#2e7d32',
colorWarning: '#ed6c02',
colorError: '#d32f2f',
colorInfo: '#0288d1',
colorTextBase: '#212121',
colorBgBase: '#fafafa',
colorPrimaryBg: '#e3f2fd',
colorPrimaryBgHover: '#bbdefb',
colorPrimaryBorder: '#90caf9',
colorPrimaryBorderHover: '#64b5f6',
colorPrimaryHover: '#42a5f5',
colorPrimaryActive: '#1565c0',
colorPrimaryText: '#1976d2',
colorPrimaryTextHover: '#42a5f5',
colorPrimaryTextActive: '#1565c0',
colorSuccessBg: '#e8f5e9',
colorSuccessBgHover: '#c8e6c9',
colorSuccessBorder: '#a5d6a7',
colorSuccessBorderHover: '#81c784',
colorSuccessHover: '#4caf50',
colorSuccessActive: '#1b5e20',
colorSuccessText: '#2e7d32',
colorSuccessTextHover: '#4caf50',
colorSuccessTextActive: '#1b5e20',
colorWarningBg: '#fff3e0',
colorWarningBgHover: '#ffe0b2',
colorWarningBorder: '#ffcc02',
colorWarningBorderHover: '#ffb74d',
colorWarningHover: '#ff9800',
colorWarningActive: '#e65100',
colorWarningText: '#ed6c02',
colorWarningTextHover: '#ff9800',
colorWarningTextActive: '#e65100',
colorErrorBg: '#ffebee',
colorErrorBgHover: '#ffcdd2',
colorErrorBorder: '#ef9a9a',
colorErrorBorderHover: '#e57373',
colorErrorHover: '#ef5350',
colorErrorActive: '#c62828',
colorErrorText: '#d32f2f',
colorErrorTextHover: '#ef5350',
colorErrorTextActive: '#c62828',
colorInfoBg: '#e1f5fe',
colorInfoBgHover: '#b3e5fc',
colorInfoBorder: '#81d4fa',
colorInfoBorderHover: '#4fc3f7',
colorInfoHover: '#03a9f4',
colorInfoActive: '#01579b',
colorInfoText: '#0288d1',
colorInfoTextHover: '#03a9f4',
colorInfoTextActive: '#01579b',
colorText: 'rgba(33, 33, 33, 0.87)',
colorTextSecondary: 'rgba(33, 33, 33, 0.6)',
colorTextTertiary: 'rgba(33, 33, 33, 0.38)',
colorTextQuaternary: 'rgba(33, 33, 33, 0.26)',
colorTextDisabled: 'rgba(33, 33, 33, 0.38)',
colorBgContainer: '#ffffff',
colorBgElevated: '#ffffff',
colorBgLayout: '#f5f5f5',
colorBgSpotlight: 'rgba(33, 33, 33, 0.85)',
colorBgMask: 'rgba(33, 33, 33, 0.5)',
colorBorder: '#e0e0e0',
colorBorderSecondary: '#eeeeee',
borderRadius: 4,
borderRadiusXS: 1,
borderRadiusSM: 2,
borderRadiusLG: 6,
padding: 16,
paddingSM: 8,
paddingLG: 24,
margin: 16,
marginSM: 8,
marginLG: 24,
boxShadow:
'0px 2px 1px -1px rgba(0,0,0,0.2),0px 1px 1px 0px rgba(0,0,0,0.14),0px 1px 3px 0px rgba(0,0,0,0.12)',
boxShadowSecondary:
'0px 3px 3px -2px rgba(0,0,0,0.2),0px 3px 4px 0px rgba(0,0,0,0.14),0px 1px 8px 0px rgba(0,0,0,0.12)',
},
components: {
Button: {
primaryShadow:
'0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)',
defaultShadow:
'0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)',
dangerShadow:
'0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)',
fontWeight: 500,
defaultBorderColor: 'rgba(0, 0, 0, 0.23)',
defaultColor: 'rgba(0, 0, 0, 0.87)',
defaultBg: '#ffffff',
defaultHoverBg: 'rgba(25, 118, 210, 0.04)',
defaultHoverBorderColor: 'rgba(0, 0, 0, 0.23)',
paddingInline: 16,
paddingBlock: 6,
contentFontSize: 14,
borderRadius: 4,
},
Alert: {
borderRadiusLG: 4,
},
Modal: {
borderRadiusLG: 4,
},
Progress: {
defaultColor: '#1976d2',
remainingColor: 'rgba(25, 118, 210, 0.12)',
},
Steps: {
iconSize: 24,
},
Checkbox: {
borderRadiusSM: 2,
},
Slider: {
trackBg: 'rgba(25, 118, 210, 0.26)',
trackHoverBg: 'rgba(25, 118, 210, 0.38)',
handleSize: 20,
handleSizeHover: 20,
railSize: 4,
},
ColorPicker: {
borderRadius: 4,
},
},
},
wave: {
showEffect: showInsetEffect,
},
button: {
classNames: ({ props }) => ({
root: clsx(
props.type === 'primary' && styles.buttonPrimary,
props.type === 'default' && styles.buttonDefault,
props.danger && styles.buttonDanger,
),
}),
},
input: {
classNames: ({ props }) => ({
root: clsx(styles.inputRoot, props.status === 'error' && styles.inputError),
input: styles.inputElement,
}),
},
select: {
classNames: {
root: styles.selectRoot,
},
},
}),
[styles],
);
};
export default useMuiTheme;

View File

@@ -0,0 +1,222 @@
import { useMemo } from 'react';
import { theme } from 'antd';
import type { ConfigProviderProps } from 'antd';
import { createStyles } from 'antd-style';
import clsx from 'clsx';
import type { UseTheme } from '.';
const useStyles = createStyles(({ css }) => {
return {
buttonPrimary: css({
backgroundColor: '#18181b',
color: '#ffffff',
border: '1px solid #18181b',
fontWeight: 500,
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
}),
buttonDefault: css({
backgroundColor: '#ffffff',
color: '#18181b',
border: '1px solid #e4e4e7',
fontWeight: 500,
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
}),
buttonDanger: css({
backgroundColor: '#dc2626',
color: '#ffffff',
border: '1px solid #dc2626',
fontWeight: 500,
}),
inputRoot: css({
borderColor: '#e4e4e7',
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
}),
inputElement: css({
color: '#18181b',
}),
inputError: css({
borderColor: '#dc2626',
}),
selectRoot: css({
borderColor: '#e4e4e7',
}),
selectPopup: css({
borderRadius: '8px',
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)',
}),
};
});
const useShadcnTheme: UseTheme = () => {
const { styles } = useStyles();
return useMemo<ConfigProviderProps>(
() => ({
theme: {
algorithm: theme.defaultAlgorithm,
token: {
colorPrimary: '#262626',
colorSuccess: '#22c55e',
colorWarning: '#f97316',
colorError: '#ef4444',
colorInfo: '#262626',
colorTextBase: '#262626',
colorBgBase: '#ffffff',
colorPrimaryBg: '#f5f5f5',
colorPrimaryBgHover: '#e5e5e5',
colorPrimaryBorder: '#d4d4d4',
colorPrimaryBorderHover: '#a3a3a3',
colorPrimaryHover: '#404040',
colorPrimaryActive: '#171717',
colorPrimaryText: '#262626',
colorPrimaryTextHover: '#404040',
colorPrimaryTextActive: '#171717',
colorSuccessBg: '#f0fdf4',
colorSuccessBgHover: '#dcfce7',
colorSuccessBorder: '#bbf7d0',
colorSuccessBorderHover: '#86efac',
colorSuccessHover: '#16a34a',
colorSuccessActive: '#15803d',
colorSuccessText: '#16a34a',
colorSuccessTextHover: '#16a34a',
colorSuccessTextActive: '#15803d',
colorWarningBg: '#fff7ed',
colorWarningBgHover: '#fed7aa',
colorWarningBorder: '#fdba74',
colorWarningBorderHover: '#fb923c',
colorWarningHover: '#ea580c',
colorWarningActive: '#c2410c',
colorWarningText: '#ea580c',
colorWarningTextHover: '#ea580c',
colorWarningTextActive: '#c2410c',
colorErrorBg: '#fef2f2',
colorErrorBgHover: '#fecaca',
colorErrorBorder: '#fca5a5',
colorErrorBorderHover: '#f87171',
colorErrorHover: '#dc2626',
colorErrorActive: '#b91c1c',
colorErrorText: '#dc2626',
colorErrorTextHover: '#dc2626',
colorErrorTextActive: '#b91c1c',
colorInfoBg: '#f5f5f5',
colorInfoBgHover: '#e5e5e5',
colorInfoBorder: '#d4d4d4',
colorInfoBorderHover: '#a3a3a3',
colorInfoHover: '#404040',
colorInfoActive: '#171717',
colorInfoText: '#262626',
colorInfoTextHover: '#404040',
colorInfoTextActive: '#171717',
colorText: '#262626',
colorTextSecondary: '#525252',
colorTextTertiary: '#737373',
colorTextQuaternary: '#a3a3a3',
colorTextDisabled: '#a3a3a3',
colorBgContainer: '#ffffff',
colorBgElevated: '#ffffff',
colorBgLayout: '#fafafa',
colorBgSpotlight: 'rgba(38, 38, 38, 0.85)',
colorBgMask: 'rgba(38, 38, 38, 0.45)',
colorBorder: '#e5e5e5',
colorBorderSecondary: '#f5f5f5',
borderRadius: 10,
borderRadiusXS: 2,
borderRadiusSM: 6,
borderRadiusLG: 14,
padding: 16,
paddingSM: 12,
paddingLG: 24,
margin: 16,
marginSM: 12,
marginLG: 24,
boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)',
boxShadowSecondary:
'0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)',
},
components: {
Button: {
primaryShadow: 'none',
defaultShadow: 'none',
dangerShadow: 'none',
defaultBorderColor: '#e4e4e7',
defaultColor: '#18181b',
defaultBg: '#ffffff',
defaultHoverBg: '#f4f4f5',
defaultHoverBorderColor: '#d4d4d8',
defaultHoverColor: '#18181b',
defaultActiveBg: '#e4e4e7',
defaultActiveBorderColor: '#d4d4d8',
borderRadius: 6,
},
Input: {
activeShadow: 'none',
hoverBorderColor: '#a1a1aa',
activeBorderColor: '#18181b',
borderRadius: 6,
},
Select: {
optionSelectedBg: '#f4f4f5',
optionActiveBg: '#fafafa',
optionSelectedFontWeight: 500,
borderRadius: 6,
},
Alert: {
borderRadiusLG: 8,
},
Modal: {
borderRadiusLG: 12,
},
Progress: {
defaultColor: '#18181b',
remainingColor: '#f4f4f5',
},
Steps: {
iconSize: 32,
},
Switch: {
trackHeight: 24,
trackMinWidth: 44,
innerMinMargin: 4,
innerMaxMargin: 24,
},
Checkbox: {
borderRadiusSM: 4,
},
Slider: {
trackBg: '#f4f4f5',
trackHoverBg: '#e4e4e7',
handleSize: 18,
handleSizeHover: 20,
railSize: 6,
},
ColorPicker: {
borderRadius: 6,
},
},
},
button: {
classNames: ({ props }) => ({
root: clsx(
props.type === 'primary' && styles.buttonPrimary,
props.type === 'default' && styles.buttonDefault,
props.danger && styles.buttonDanger,
),
}),
},
input: {
classNames: ({ props }) => ({
root: clsx(styles.inputRoot, props.status === 'error' && styles.inputError),
input: styles.inputElement,
}),
},
select: {
classNames: {
root: styles.selectRoot,
},
},
}),
[styles],
);
};
export default useShadcnTheme;

View File

@@ -1,4 +1,3 @@
/* eslint-disable compat/compat */
import { css } from 'antd-style';
import useSWR from 'swr';

View File

@@ -1,18 +1,19 @@
import React, { Suspense } from 'react';
import { ConfigProvider, theme } from 'antd';
import { createStyles, css } from 'antd-style';
import { theme } from 'antd';
import { createStaticStyles } from 'antd-style';
import useLocale from '../../hooks/useLocale';
import { DarkContext } from './../../hooks/useDark';
import BannerRecommends from './components/BannerRecommends';
import Group from './components/Group';
import PreviewBanner from './components/PreviewBanner';
import ThemePreview from './components/ThemePreview';
const ComponentsList = React.lazy(() => import('./components/ComponentsList'));
const DesignFramework = React.lazy(() => import('./components/DesignFramework'));
const Theme = React.lazy(() => import('./components/Theme'));
// const Theme = React.lazy(() => import('./components/Theme'));
const useStyle = createStyles(() => ({
const classNames = createStaticStyles(({ css }) => ({
image: css`
position: absolute;
inset-inline-start: 0;
@@ -38,7 +39,6 @@ const locales = {
const Homepage: React.FC = () => {
const [locale] = useLocale(locales);
const { styles } = useStyle();
const { token } = theme.useToken();
const isDark = React.use(DarkContext);
@@ -49,48 +49,96 @@ const Homepage: React.FC = () => {
<BannerRecommends />
</PreviewBanner>
<div>
{/* 定制主题 */}
<ConfigProvider theme={{ algorithm: theme.defaultAlgorithm }}>
<Suspense fallback={null}>
<Theme />
</Suspense>
</ConfigProvider>
{/* 定制主题 */}
{/* <ConfigProvider theme={{ algorithm: theme.defaultAlgorithm }}>
<Suspense fallback={null}>
<Theme />
</Suspense>
</ConfigProvider> */}
<ThemePreview />
{/* 组件列表 */}
<Group
background={token.colorBgElevated}
collapse
title={locale.assetsTitle}
description={locale.assetsDesc}
id="design"
>
<Suspense fallback={null}>
<ComponentsList />
</Suspense>
</Group>
{/* 组件列表 */}
<Group
background={token.colorBgElevated}
collapse
title={locale.assetsTitle}
description={locale.assetsDesc}
id="design"
>
<Suspense fallback={null}>
<ComponentsList />
</Suspense>
</Group>
{/* 设计语言 */}
<Group
title={locale.designTitle}
description={locale.designDesc}
background={isDark ? '#393F4A' : '#F5F8FF'}
decoration={
<img
draggable={false}
className={styles.image}
src="https://gw.alipayobjects.com/zos/bmw-prod/ba37a413-28e6-4be4-b1c5-01be1a0ebb1c.svg"
alt="bg"
/>
}
>
<Suspense fallback={null}>
<DesignFramework />
</Suspense>
</Group>
</div>
{/* 设计语言 */}
<Group
title={locale.designTitle}
description={locale.designDesc}
background={isDark ? '#393F4A' : '#F5F8FF'}
decoration={
<img
draggable={false}
className={classNames.image}
src="https://gw.alipayobjects.com/zos/bmw-prod/ba37a413-28e6-4be4-b1c5-01be1a0ebb1c.svg"
alt="bg"
/>
}
>
<Suspense fallback={null}>
<DesignFramework />
</Suspense>
</Group>
</section>
);
// return (
// <section>
// <PreviewBanner>
// <BannerRecommends />
// </PreviewBanner>
// <div>
// {/* 定制主题 */}
// <ConfigProvider theme={{ algorithm: theme.defaultAlgorithm }}>
// <Suspense fallback={null}>
// <Theme />
// </Suspense>
// </ConfigProvider>
// {/* 组件列表 */}
// <Group
// background={token.colorBgElevated}
// collapse
// title={locale.assetsTitle}
// description={locale.assetsDesc}
// id="design"
// >
// <Suspense fallback={null}>
// <ComponentsList />
// </Suspense>
// </Group>
// {/* 设计语言 */}
// <Group
// title={locale.designTitle}
// description={locale.designDesc}
// background={isDark ? '#393F4A' : '#F5F8FF'}
// decoration={
// <img
// draggable={false}
// className={classNames.image}
// src="https://gw.alipayobjects.com/zos/bmw-prod/ba37a413-28e6-4be4-b1c5-01be1a0ebb1c.svg"
// alt="bg"
// />
// }
// >
// <Suspense fallback={null}>
// <DesignFramework />
// </Suspense>
// </Group>
// </div>
// </section>
// );
};
export default Homepage;

View File

@@ -1,6 +1,6 @@
import React, { Suspense } from 'react';
import { App, Button, ConfigProvider, Skeleton, version } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { enUS, zhCN } from 'antd-token-previewer';
import type { ThemeConfig } from 'antd/es/config-provider/context';
import { Helmet } from 'dumi';
@@ -9,7 +9,7 @@ import useLocale from '../../hooks/useLocale';
const ThemeEditor = React.lazy(() => import('antd-token-previewer/lib/ThemeEditor'));
const useStyle = createStyles(({ css }) => ({
const classNames = createStaticStyles(({ css }) => ({
editor: css`
svg,
img {
@@ -49,7 +49,6 @@ const ANT_DESIGN_V5_THEME_EDITOR_THEME = `ant-design-v${antdMajor}-theme-editor-
const CustomTheme: React.FC = () => {
const { message } = App.useApp();
const [locale, lang] = useLocale(locales);
const { styles } = useStyle();
const [theme, setTheme] = React.useState<ThemeConfig>(() => {
try {
@@ -78,7 +77,7 @@ const CustomTheme: React.FC = () => {
hideAdvancedSwitcher
theme={{ name: 'Custom Theme', key: 'test', config: theme }}
style={{ height: 'calc(100vh - 64px)' }}
className={styles.editor}
className={classNames.editor}
onThemeChange={(newTheme) => {
setTheme(newTheme.config);
}}

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { SoundOutlined } from '@ant-design/icons';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
const useStyle = createStyles(({ css, cssVar }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
playBtn: css`
display: inline-flex;
@@ -30,7 +30,6 @@ interface AudioProps {
}
const AudioControl: React.FC<React.PropsWithChildren<AudioProps>> = ({ id, children }) => {
const { styles } = useStyle();
const onClick: React.MouseEventHandler<HTMLAnchorElement> = () => {
const audio = document.querySelector<HTMLAudioElement>(`#${id}`);
audio?.play();

View File

@@ -3,6 +3,7 @@ import {
BugOutlined,
CompassOutlined,
EditOutlined,
FileTextOutlined,
GithubOutlined,
HistoryOutlined,
IssuesCloseOutlined,
@@ -32,6 +33,7 @@ const locales = {
version: '版本',
issueNew: '提交问题',
issueOpen: '待解决',
copyError: '复制失败',
},
en: {
import: 'Import',
@@ -45,6 +47,7 @@ const locales = {
version: 'Version',
issueNew: 'Issue',
issueOpen: 'Open issues',
copyError: 'Copy failed',
},
};
@@ -128,21 +131,22 @@ const ComponentMeta: React.FC<ComponentMetaProps> = (props) => {
};
// ======================== Source ========================
const [filledSource, abbrSource] = React.useMemo(() => {
const [filledSource, abbrSource, llmsPath] = React.useMemo(() => {
if (String(source) === 'true') {
const kebabComponent = kebabCase(component);
return [
`https://github.com/${repo}/blob/master/components/${kebabComponent}`,
`components/${kebabComponent}`,
`/components/${kebabComponent}${isZhCN ? '-cn' : ''}.md`,
];
}
if (typeof source !== 'string') {
return [null, null];
return [null, null, null];
}
return [source, source];
}, [component, repo, source]);
return [source, source, null];
}, [component, repo, source, isZhCN]);
return (
<Descriptions
@@ -210,6 +214,15 @@ const ComponentMeta: React.FC<ComponentMetaProps> = (props) => {
<span>{locale.design}</span>
</Link>
)}
<Typography.Link
className={styles.code}
href={llmsPath || ''}
target="_blank"
rel="noreferrer"
>
<FileTextOutlined className={styles.icon} />
<span>LLMs.md</span>
</Typography.Link>
<ComponentChangelog>
<Typography.Link className={styles.code}>
<HistoryOutlined className={styles.icon} />

View File

@@ -2,7 +2,7 @@ import React, { memo, useMemo, useRef, useState } from 'react';
import type { CSSProperties } from 'react';
import { SearchOutlined } from '@ant-design/icons';
import { Affix, Card, Col, Divider, Flex, Input, Row, Tag, Typography } from 'antd';
import { createStyles, useTheme } from 'antd-style';
import { createStaticStyles, useTheme } from 'antd-style';
import { useIntl, useLocation, useSidebarData } from 'dumi';
import debounce from 'lodash/debounce';
import scrollIntoView from 'scroll-into-view-if-needed';
@@ -12,7 +12,7 @@ import SiteContext from '../../slots/SiteContext';
import type { Component } from './ProComponentsList';
import proComponentsList from './ProComponentsList';
const useStyle = createStyles(({ cssVar, css }) => ({
const styles = createStaticStyles(({ cssVar, css }) => ({
componentsOverviewGroupTitle: css`
margin-bottom: ${cssVar.marginLG} !important;
`,
@@ -78,7 +78,6 @@ const reportSearch = debounce<(value: string) => void>((value) => {
const { Title } = Typography;
const Overview: React.FC = () => {
const { styles } = useStyle();
const { isDark } = React.use(SiteContext);
const data = useSidebarData();
@@ -227,7 +226,7 @@ const Overview: React.FC = () => {
src={
isDark && component.coverDark ? component.coverDark : component.cover
}
alt={component.title}
alt=""
/>
</div>
</Card>

View File

@@ -1,13 +1,13 @@
import * as React from 'react';
import { App } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { useIntl } from 'dumi';
import CopyableIcon from './CopyableIcon';
import type { CategoriesKeys } from './fields';
import type { ThemeType } from './IconSearch';
const useStyle = createStyles(({ css, cssVar }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
anticonsList: css`
margin: ${cssVar.margin} 0;
overflow: hidden;
@@ -36,7 +36,6 @@ interface CategoryProps {
const Category: React.FC<CategoryProps> = (props) => {
const { message } = App.useApp();
const { icons, title, newIcons, theme } = props;
const { styles } = useStyle();
const intl = useIntl();
const [justCopied, setJustCopied] = React.useState<string | null>(null);
const copyId = React.useRef<ReturnType<typeof setTimeout> | null>(null);

View File

@@ -2,7 +2,7 @@ import type { CSSProperties } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import Icon, * as AntdIcons from '@ant-design/icons';
import { Affix, Empty, Input, Segmented } from 'antd';
import { createStyles, useTheme } from 'antd-style';
import { createStaticStyles, useTheme } from 'antd-style';
import type { SegmentedOptions } from 'antd/es/segmented';
import { useIntl } from 'dumi';
import debounce from 'lodash/debounce';
@@ -22,7 +22,7 @@ export enum ThemeType {
const allIcons: { [key: string]: any } = AntdIcons;
const useStyle = createStyles(({ css, cssVar }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
iconSearchAffix: css`
display: flex;
transition: all ${cssVar.motionDurationSlow};
@@ -39,7 +39,6 @@ const NEW_ICON_NAMES: ReadonlyArray<string> = [];
const IconSearch: React.FC = () => {
const intl = useIntl();
const { styles } = useStyle();
const [displayState, setDisplayState] = useState<IconSearchState>({
searchKey: '',
theme: ThemeType.Outlined,

View File

@@ -1,10 +1,10 @@
import React, { Suspense } from 'react';
import { Skeleton } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
const IconSearch = React.lazy(() => import('./IconSearch'));
const useStyle = createStyles(({ css, cssVar }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
searchWrapper: css`
display: flex;
gap: ${cssVar.padding};
@@ -34,8 +34,6 @@ const useStyle = createStyles(({ css, cssVar }) => ({
}));
const IconSearchFallback: React.FC = () => {
const { styles } = useStyle();
return (
<>
<div className={styles.searchWrapper}>

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { createStyles, css } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { clsx } from 'clsx';
interface IconProps {
@@ -7,7 +7,7 @@ interface IconProps {
style?: React.CSSProperties;
}
const useStyle = createStyles(() => ({
const classNames = createStaticStyles(({ css }) => ({
iconWrap: css`
display: inline-flex;
align-items: center;
@@ -19,9 +19,8 @@ const useStyle = createStyles(() => ({
const BunIcon: React.FC<IconProps> = (props) => {
const { className, style } = props;
const { styles } = useStyle();
return (
<span className={clsx(styles.iconWrap, className)} style={style}>
<span className={clsx(classNames.iconWrap, className)} style={style}>
<svg id="Bun" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 70" width="1em" height="1em">
<title>Bun Logo</title>
<path

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { createStyles, css } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { clsx } from 'clsx';
interface IconProps {
@@ -7,7 +7,7 @@ interface IconProps {
style?: React.CSSProperties;
}
const useStyle = createStyles(() => ({
const classNames = createStaticStyles(({ css }) => ({
iconWrap: css`
display: inline-flex;
align-items: center;
@@ -19,9 +19,8 @@ const useStyle = createStyles(() => ({
const NpmIcon: React.FC<IconProps> = (props) => {
const { className, style } = props;
const { styles } = useStyle();
return (
<span className={clsx(styles.iconWrap, className)} style={style}>
<span className={clsx(classNames.iconWrap, className)} style={style}>
<svg
fill="#E53E3E"
focusable="false"

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { createStyles, css } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { clsx } from 'clsx';
interface IconProps {
@@ -7,7 +7,7 @@ interface IconProps {
style?: React.CSSProperties;
}
const useStyle = createStyles(() => ({
const classNames = createStaticStyles(({ css }) => ({
iconWrap: css`
display: inline-flex;
align-items: center;
@@ -19,9 +19,8 @@ const useStyle = createStyles(() => ({
const PnpmIcon: React.FC<IconProps> = (props) => {
const { className, style } = props;
const { styles } = useStyle();
return (
<span className={clsx(styles.iconWrap, className)} style={style}>
<span className={clsx(classNames.iconWrap, className)} style={style}>
<svg
aria-hidden="true"
fill="#F69220"

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { createStyles, css } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { clsx } from 'clsx';
interface IconProps {
@@ -7,7 +7,7 @@ interface IconProps {
style?: React.CSSProperties;
}
const useStyle = createStyles(() => ({
const classNames = createStaticStyles(({ css }) => ({
iconWrap: css`
display: inline-flex;
align-items: center;
@@ -19,9 +19,8 @@ const useStyle = createStyles(() => ({
const YarnIcon: React.FC<IconProps> = (props) => {
const { className, style } = props;
const { styles } = useStyle();
return (
<span className={clsx(styles.iconWrap, className)} style={style}>
<span className={clsx(classNames.iconWrap, className)} style={style}>
<svg
aria-hidden="true"
fill="#2C8EBB"

View File

@@ -1,7 +1,8 @@
import React, { Suspense, useRef } from 'react';
import { LinkOutlined, ThunderboltOutlined } from '@ant-design/icons';
import { BugOutlined, ThunderboltOutlined } from '@ant-design/icons';
import stackblitzSdk from '@stackblitz/sdk';
import { Flex, Tooltip } from 'antd';
import type { MenuProps } from 'antd';
import { Button, Dropdown, Flex, Tooltip } from 'antd';
import { FormattedMessage, useSiteData } from 'dumi';
import LZString from 'lz-string';
@@ -28,8 +29,6 @@ function compress(string: string): string {
}
interface ActionsProps {
showOnlineUrl: boolean;
docsOnlineUrl?: string;
assetId: string;
title?: string;
pkgDependencyList: Record<PropertyKey, string>;
@@ -39,11 +38,10 @@ interface ActionsProps {
onCodeExpand: () => void;
entryCode: string;
styleCode: string;
debugOptions?: MenuProps['items'];
}
const Actions: React.FC<ActionsProps> = ({
showOnlineUrl,
docsOnlineUrl,
assetId,
title,
jsx,
@@ -53,6 +51,7 @@ const Actions: React.FC<ActionsProps> = ({
pkgDependencyList,
entryCode,
styleCode,
debugOptions,
}) => {
const [, lang] = useLocale();
const isZhCN = lang === 'cn';
@@ -128,8 +127,8 @@ const Actions: React.FC<ActionsProps> = ({
editors: '001',
css: '',
js_external: [
'react@19/cjs/react.development.js',
'react-dom@19/cjs/react-dom.development.js',
'react@18/umd/react.production.min.js',
'react-dom@18/umd/react-dom.production.min.js',
'dayjs@1/dayjs.min.js',
`antd@${pkg.version}/dist/antd-with-locales.min.js`,
`@ant-design/icons/dist/index.umd.js`,
@@ -213,21 +212,15 @@ createRoot(document.getElementById('container')).render(<Demo />);
});
return (
<Flex wrap gap="middle" className="code-box-actions">
{/* 在线文档按钮 */}
{showOnlineUrl && (
<Tooltip title={<FormattedMessage id="app.demo.online" />}>
<a
className="code-box-code-action"
aria-label="open in new tab"
target="_blank"
rel="noreferrer"
href={docsOnlineUrl || ''}
>
<LinkOutlined className="code-box-online" />
</a>
</Tooltip>
)}
<Flex wrap gap="middle" className="code-box-actions" align="center">
{
// 调试选项
debugOptions?.length ? (
<Dropdown menu={{ items: debugOptions }} arrow={{ pointAtCenter: true }}>
<Button icon={<BugOutlined />} color="purple" variant="filled" size="small" />
</Dropdown>
) : null
}
{/* CodeSandbox 按钮 */}
<form
className="code-box-code-action"

View File

@@ -1,10 +1,11 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { UpOutlined } from '@ant-design/icons';
import { Badge, Tooltip } from 'antd';
import { createStyles, css } from 'antd-style';
import type { MenuProps } from 'antd';
import { Badge, Tag, Tooltip } from 'antd';
import { createStaticStyles } from 'antd-style';
import { clsx } from 'clsx';
import { FormattedMessage, useLiveDemo } from 'dumi';
import { FormattedMessage, useLiveDemo, useSiteData } from 'dumi';
import { major, minVersion } from 'semver';
import type { AntdPreviewerProps } from '.';
import useLocation from '../../../hooks/useLocation';
import BrowserFrame from '../../common/BrowserFrame';
@@ -12,9 +13,11 @@ import ClientOnly from '../../common/ClientOnly';
import CodePreview from '../../common/CodePreview';
import EditButton from '../../common/EditButton';
import SiteContext from '../../slots/SiteContext';
import DemoContext from '../../slots/DemoContext';
import { isOfficialHost } from '../../utils';
import Actions from './Actions';
const useStyle = createStyles(({ cssVar }) => {
const styles = createStaticStyles(({ cssVar, css }) => {
return {
codeHideBtn: css`
position: sticky;
@@ -61,8 +64,10 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
clientOnly,
pkgDependencyList,
} = props;
const { showDebug } = React.use(DemoContext);
const { pkg } = useSiteData();
const location = useLocation();
const { styles } = useStyle();
const entryName = 'index.tsx';
const entryCode = asset.dependencies[entryName].value;
@@ -79,17 +84,15 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
const anchorRef = useRef<HTMLAnchorElement>(null);
const [codeExpand, setCodeExpand] = useState<boolean>(false);
const { isDark } = React.use(SiteContext);
const { hash, pathname, search } = location;
const docsOnlineUrl = `https://ant.design${pathname ?? ''}${search ?? ''}#${asset.id}`;
const [showOnlineUrl, setShowOnlineUrl] = useState<boolean>(false);
/**
* Record whether it is deployed on the official domain name.
* Note that window.location.hostname is not available on the server side due to hydration issues
*/
const [deployedOnOfficialHost, setDeployedOnOfficialHost] = useState<boolean>(true);
useEffect(() => {
const regexp = /preview-(\d+)-ant-design/; // matching PR preview addresses
setShowOnlineUrl(
process.env.NODE_ENV === 'development' || regexp.test(window.location.hostname),
);
setDeployedOnOfficialHost(isOfficialHost(window.location.hostname));
}, []);
useEffect(() => {
@@ -102,6 +105,33 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
setCodeExpand(expand);
}, [expand]);
const generateDocUrl = (domain = 'https://ant.design') =>
`${domain}${pathname ?? ''}${search ?? ''}#${asset.id}`;
// Enable "Go Online Docs" only when deployed on non-official domains
const enableDocsOnlineUrl = process.env.NODE_ENV === 'development' || !deployedOnOfficialHost;
// Previous version demos are only available during the maintenance window
const [supportsPreviousVersionDemo, previousVersionDomain, previousVersion] = useMemo(() => {
const maintenanceDeadline = new Date('2026/12/31');
if (new Date() > maintenanceDeadline) {
return [false, undefined, -1] as const;
}
const currentMajor = major(pkg.version);
const previousMajor = Math.min(currentMajor - 1, 5);
let enabled = true;
// If the demo specifies a version, perform an additional check;
if (version) {
const minVer = minVersion(version);
enabled = minVer?.major ? minVer.major < currentMajor : true;
}
return [enabled, `https://${previousMajor}x.ant.design`, previousMajor];
}, [version, pkg.version]);
const mergedChildren = !iframe && clientOnly ? <ClientOnly>{children}</ClientOnly> : children;
const demoUrlWithTheme = useMemo(() => {
return `${demoUrl}${isDark ? '?theme=dark' : ''}`;
@@ -143,6 +173,47 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
backgroundColor: background === 'grey' ? backgroundGrey : undefined,
};
const debugOptions: MenuProps['items'] = [
{
key: 'online',
label: (
<a
aria-label="Go to online documentation"
href={generateDocUrl()}
target="_blank"
rel="noreferrer"
>
<FormattedMessage id="app.demo.online" />
</a>
),
icon: (
<Tag variant="filled" color="blue">
ant.design
</Tag>
),
enabled: enableDocsOnlineUrl,
},
{
key: 'previousVersion',
label: (
<a
aria-label="Go to previous version documentation"
href={generateDocUrl(previousVersionDomain)}
target="_blank"
rel="noreferrer"
>
<FormattedMessage id="app.demo.previousVersion" values={{ version: previousVersion }} />
</a>
),
icon: (
<Tag variant="filled" color="purple">
v{previousVersion}
</Tag>
),
enabled: supportsPreviousVersionDemo,
},
].filter(({ enabled }) => showDebug && enabled);
const codeBox: React.ReactNode = (
<section className={codeBoxClass} id={asset.id}>
<section
@@ -174,8 +245,7 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
/>
)}
<Actions
showOnlineUrl={showOnlineUrl}
docsOnlineUrl={docsOnlineUrl}
debugOptions={debugOptions}
entryCode={entryCode}
styleCode={style}
pkgDependencyList={pkgDependencyList}

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { Skeleton } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
const useStyle = createStyles(({ css, cssVar }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
skeletonWrapper: css`
width: 100% !important;
height: 250px;
@@ -12,7 +12,6 @@ const useStyle = createStyles(({ css, cssVar }) => ({
}));
const DemoFallback = () => {
const { styles } = useStyle();
return (
<Skeleton.Node
active

View File

@@ -2,7 +2,7 @@ import type { FC } from 'react';
import React, { useEffect, useRef } from 'react';
import { CheckOutlined, SketchOutlined } from '@ant-design/icons';
import { App } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import copy from '../../../../components/_util/copy';
import { nodeToGroup } from 'html2sketch';
@@ -22,12 +22,12 @@ const locales = {
},
};
const useStyle = createStyles(({ cssVar, css }) => ({
const styles = createStaticStyles(({ cssVar, css }) => ({
wrapper: css`
position: relative;
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: ${cssVar.borderRadius};
padding: ${cssVar.paddingMD} ${cssVar.paddingLG} ${cssVar.paddingMD * 2};
padding: ${cssVar.paddingMD} ${cssVar.paddingLG} calc(${cssVar.paddingMD} * 2);
margin-bottom: ${cssVar.marginLG};
`,
title: css`
@@ -67,12 +67,11 @@ const useStyle = createStyles(({ cssVar, css }) => ({
`,
tip: css`
color: ${cssVar.colorTextTertiary};
margin-top: ${cssVar.marginMD * 2};
margin-top: calc(${cssVar.marginMD} * 2);
`,
}));
const DesignPreviewer: FC<AntdPreviewerProps> = ({ children, title, description, tip, asset }) => {
const { styles } = useStyle();
const demoRef = useRef<HTMLDivElement>(null);
const [copied, setCopied] = React.useState<boolean>(false);
const { message } = App.useApp();

View File

@@ -1,7 +1,7 @@
import * as React from 'react';
import { BugOutlined } from '@ant-design/icons';
import { Button, Flex, Popover, theme } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles, cx } from 'antd-style';
import dayjs from 'dayjs';
import type { Dayjs } from 'dayjs';
@@ -33,7 +33,7 @@ const locales = {
},
};
const useStyle = createStyles(({ cssVar, css }) => ({
const styles = createStaticStyles(({ cssVar, css }) => ({
container: css`
margin-block: ${cssVar.margin};
padding: ${cssVar.padding};
@@ -49,8 +49,6 @@ const useStyle = createStyles(({ cssVar, css }) => ({
const RefinedChangelog: React.FC<React.PropsWithChildren<RefinedChangelogProps>> = (props) => {
const { version, date, children } = props;
const { styles, cx } = useStyle();
const memoizedValue = React.useMemo<ContextProps>(() => {
const realVersion = version || '0.0.0';
const bugVersionInfo = matchDeprecated(realVersion);

View File

@@ -1,7 +1,7 @@
import React, { Suspense } from 'react';
import type { SandpackSetup } from '@codesandbox/sandpack-react';
import { Skeleton } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { useSearchParams } from 'dumi';
import { version } from '../../../../package.json';
@@ -17,7 +17,7 @@ const root = createRoot(document.getElementById("root"));
root.render(<App />);
`;
const useStyle = createStyles(({ css, cssVar }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
fallback: css`
width: 100%;
> * {
@@ -32,7 +32,6 @@ const useStyle = createStyles(({ css, cssVar }) => ({
}));
const SandpackFallback: React.FC = () => {
const { styles } = useStyle();
return (
<div className={styles.fallback}>
<Skeleton.Node active style={{ height: 500, width: '100%' }}>
@@ -83,6 +82,7 @@ const Sandpack: React.FC<React.PropsWithChildren<SandpackProps>> = (props) => {
<OriginSandpack
theme={searchParams.getAll('theme').includes('dark') ? 'dark' : undefined}
customSetup={setup}
template="vite-react-ts"
options={options}
files={{
'index.tsx': indexContent,

View File

@@ -2,13 +2,13 @@
import React from 'react';
import { FastColor } from '@ant-design/fast-color';
import { Flex, theme } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import tokenMeta from 'antd/es/version/token-meta.json';
import { clsx } from 'clsx';
import useLocale from '../../../hooks/useLocale';
const useStyle = createStyles(({ cssVar, css }) => {
const styles = createStaticStyles(({ cssVar, css }) => {
const height = cssVar.controlHeightLG;
const dotSize = height / 5;
@@ -63,7 +63,6 @@ interface ColorCircleProps {
}
const ColorCircle: React.FC<ColorCircleProps> = ({ color }) => {
const { styles } = useStyle();
return (
<Flex align="center" gap={4}>
<div className={styles.dot} style={{ background: color }} />
@@ -79,7 +78,6 @@ export interface TokenCompareProps {
const TokenCompare: React.FC<TokenCompareProps> = (props) => {
const { tokenNames = '' } = props;
const [, lang] = useLocale();
const { styles } = useStyle();
const tokenList = React.useMemo(() => {
const list = tokenNames.split('|');

View File

@@ -1,9 +1,9 @@
import React from 'react';
import { PauseCircleFilled, PlayCircleFilled } from '@ant-design/icons';
import { createStyles, css } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { clsx } from 'clsx';
const useStyles = createStyles(({ cx, cssVar }) => {
const styles = createStaticStyles(({ css, cx, cssVar }) => {
const play = css`
position: absolute;
inset-inline-end: ${cssVar.paddingLG};
@@ -45,7 +45,6 @@ const VideoPlayer: React.FC<React.HtmlHTMLAttributes<HTMLVideoElement>> = ({
className,
...restProps
}) => {
const { styles } = useStyles();
const videoRef = React.useRef<HTMLVideoElement>(null);
const [playing, setPlaying] = React.useState(false);

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useRef } from 'react';
import { createStyles, css } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { useRouteMeta } from 'dumi';
import useLocale from '../../../hooks/useLocale';
@@ -17,12 +17,12 @@ export interface BehaviorMapProps {
data: BehaviorMapItem;
}
const useStyle = createStyles(({ cssVar }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
width: 100%;
min-height: 600px;
height: fit-content;
background-color: #f5f5f5;
background-color: ${cssVar.colorBgLayout};
border: 1px solid #e8e8e8;
border-radius: ${cssVar.borderRadiusLG};
overflow: hidden;
@@ -54,7 +54,7 @@ const useStyle = createStyles(({ cssVar }) => ({
inset-inline-end: 20px;
z-index: 10;
border-radius: 4px;
font-size: 14px;
font-size: ${cssVar.fontSize};
`,
mvp: css`
margin-inline-end: ${cssVar.marginMD};
@@ -100,23 +100,27 @@ const locales = {
const BehaviorMap: React.FC<BehaviorMapProps> = ({ data }) => {
const chartRef = useRef<HTMLDivElement>(null);
const { styles } = useStyle();
const [locale] = useLocale(locales);
const meta = useRouteMeta();
const mermaidCode = useMermaidCode(data);
const cancelledRef = useRef<boolean>(false);
useEffect(() => {
let isCancelled = false;
cancelledRef.current = false;
const renderChart = async () => {
if (!chartRef.current || !mermaidCode) return;
if (!chartRef.current || !mermaidCode) {
return;
}
try {
const mermaidModule = await import('mermaid');
const mermaid = mermaidModule.default;
const mermaid = (await import('mermaid')).default;
if (isCancelled) return;
if (cancelledRef.current) {
return;
}
mermaid.initialize({
startOnLoad: false,
@@ -130,18 +134,15 @@ const BehaviorMap: React.FC<BehaviorMapProps> = ({ data }) => {
},
});
let mermaidChartCounter = 0;
mermaidChartCounter += 1;
const id = `mermaid-${Date.now()}-${mermaidChartCounter}`;
const id = `mermaid-${Date.now()}`;
const { svg } = await mermaid.render(id, mermaidCode);
if (!isCancelled && chartRef.current) {
if (!cancelledRef.current && chartRef.current) {
chartRef.current.innerHTML = svg;
}
} catch (error) {
if (!isCancelled && chartRef.current) {
console.error('Mermaid render error:', error);
} catch {
if (!cancelledRef.current && chartRef.current) {
chartRef.current.innerHTML = 'Render Error';
}
}
@@ -150,7 +151,7 @@ const BehaviorMap: React.FC<BehaviorMapProps> = ({ data }) => {
renderChart();
return () => {
isCancelled = true;
cancelledRef.current = true;
};
}, [mermaidCode]);

View File

@@ -1,14 +1,14 @@
import type { FC } from 'react';
import React, { Suspense } from 'react';
import { Skeleton } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import useLocale from '../../../hooks/useLocale';
import type { BehaviorMapProps } from './BehaviorMap';
const InternalBehaviorMap = React.lazy(() => import('./BehaviorMap'));
const useStyle = createStyles(({ cssVar, css }) => ({
const styles = createStaticStyles(({ cssVar, css }) => ({
fallback: css`
width: 100%;
> * {
@@ -32,7 +32,6 @@ const locales = {
};
const BehaviorMapFallback: React.FC = () => {
const { styles } = useStyle();
const [locale] = useLocale(locales);
return (
<div className={styles.fallback}>

View File

@@ -2,50 +2,45 @@ import { useMemo } from 'react';
import type { BehaviorMapItem } from './BehaviorMap';
export const useMermaidCode = (data: BehaviorMapItem): string => {
const generateMermaidCode = (root: BehaviorMapItem): string => {
const lines: string[] = [];
const generateMermaidCode = (root: BehaviorMapItem) => {
const lines: string[] = [];
lines.push('graph LR');
lines.push('graph LR');
lines.push(`classDef baseNode fill:#fff,stroke:none,stroke-width:0px,rx:5,ry:5,font-size:14px`);
lines.push(`classDef baseNode fill:#fff,stroke:none,stroke-width:0px,rx:5,ry:5,font-size:14px`);
const traverse = (node: BehaviorMapItem, parentId?: string) => {
const safeId = `node_${node.id.replace(/[^a-z0-9]/gi, '_')}`;
let labelText = node.label.replace(/"/g, "'");
const traverse = (node: BehaviorMapItem, parentId?: string) => {
const safeId = `node_${node.id.replace(/[^a-z0-9]/gi, '_')}`;
let labelText = node.label.replace(/"/g, "'");
if (!parentId) {
lines.push(`style ${safeId} font-size:16px`);
labelText = `**${labelText}**`;
} else if (node.targetType === 'mvp') {
const blueDot = `<span style="display:inline-block;width:8px;height:8px;background-color:rgb(22, 119, 255);border-radius:50%;margin-right:8px;vertical-align:middle;"></span>`;
labelText = `${blueDot}${labelText}`;
} else if (node.targetType === 'extension') {
const grayDot = `<span style="display:inline-block;width:8px;height:8px;background-color:rgb(160, 160, 160);border-radius:50%;margin-right:8px;vertical-align:middle;"></span>`;
labelText = `${grayDot}${labelText}`;
}
lines.push(`${safeId}["${labelText}"]:::baseNode`);
if (!parentId) {
lines.push(`style ${safeId} font-size:16px`);
labelText = `**${labelText}**`;
} else if (node.targetType === 'mvp') {
const blueDot = `<span style="display:inline-block;width:8px;height:8px;background-color:rgb(22, 119, 255);border-radius:50%;margin-inline-end:8px;vertical-align:middle;"></span>`;
labelText = `${blueDot}${labelText}`;
} else if (node.targetType === 'extension') {
const grayDot = `<span style="display:inline-block;width:8px;height:8px;background-color:rgb(160, 160, 160);border-radius:50%;margin-inline-end:8px;vertical-align:middle;"></span>`;
labelText = `${grayDot}${labelText}`;
}
lines.push(`${safeId}["${labelText}"]:::baseNode`);
if (node.link) {
lines.push(`click ${safeId} "#${node.link}"`);
}
if (node.link) {
lines.push(`click ${safeId} "#${node.link}"`);
}
if (parentId) {
lines.push(`${parentId} --> ${safeId}`);
}
if (parentId) {
lines.push(`${parentId} --> ${safeId}`);
}
if (node.children && node.children.length > 0) {
node.children.forEach((child) => traverse(child, safeId));
}
};
traverse(root);
return lines.join('\n');
if (node.children && node.children.length > 0) {
node.children.forEach((child) => traverse(child, safeId));
}
};
const mermaidCode = useMemo(() => {
return generateMermaidCode(data);
}, [data]);
return mermaidCode;
traverse(root);
return lines.join('\n');
};
export const useMermaidCode = (data: BehaviorMapItem) => {
return useMemo(() => generateMermaidCode(data), [data]);
};

View File

@@ -20,7 +20,7 @@ const locales = {
},
};
const BezierVisualizer = (props: BezierVisualizerProps) => {
const BezierVisualizer: React.FC<BezierVisualizerProps> = (props) => {
const { value } = props;
const [locale] = useLocale(locales);

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
const useStyle = createStyles(({ cssVar, css }) => ({
const styles = createStaticStyles(({ cssVar, css }) => ({
browserMockup: css`
position: relative;
border-top: 2em solid rgba(230, 230, 230, 0.7);
@@ -49,7 +49,6 @@ const useStyle = createStyles(({ cssVar, css }) => ({
}));
const BrowserFrame: React.FC<React.PropsWithChildren> = ({ children }) => {
const { styles } = useStyle();
return <div className={styles.browserMockup}>{children}</div>;
};

View File

@@ -72,6 +72,7 @@ const useStyle = createStyles(({ cssVar, token, css }) => ({
font-weight: 600;
font-size: 20px;
margin: 0 !important;
padding: 0;
`,
versionTag: css`
user-select: none;

View File

@@ -1,11 +1,11 @@
import type { ComponentProps, FC } from 'react';
import React from 'react';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import SourceCodeEditor from 'dumi/theme-default/slots/SourceCodeEditor';
import LiveError from '../slots/LiveError';
const useStyle = createStyles(({ cssVar, css }) => {
const styles = createStaticStyles(({ cssVar, css }) => {
return {
editor: css`
// override dumi editor styles
@@ -54,7 +54,6 @@ const LiveCode: FC<
error: Error | null;
} & Pick<ComponentProps<typeof SourceCodeEditor>, 'lang' | 'initialValue' | 'onChange'>
> = (props) => {
const { styles } = useStyle();
return (
<div className={styles.editor}>
<SourceCodeEditor

View File

@@ -1,12 +1,12 @@
import React from 'react';
import { StyleProvider } from '@ant-design/cssinjs';
import { ConfigProvider, Flex, Skeleton, Spin } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { useLocation } from 'dumi';
import { Common } from './styles';
const useStyle = createStyles(({ css, cssVar }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
skeletonWrapper: css`
width: 100%;
@@ -27,8 +27,6 @@ const useStyle = createStyles(({ css, cssVar }) => {
const Loading: React.FC = () => {
const { pathname } = useLocation();
const { styles } = useStyle();
let loadingNode: React.ReactNode = null;
if (

View File

@@ -5,36 +5,35 @@ import { clsx } from 'clsx';
const useStyle = createStyles(({ cssVar, cx }) => {
const duration = cssVar.motionDurationSlow;
const marker = css`
--mark-border-size: 1px;
position: absolute;
border: var(--mark-border-size) solid ${cssVar.colorWarning};
box-sizing: border-box;
z-index: 999999;
pointer-events: none;
left: calc(var(--rect-left) * 1px - var(--mark-border-size));
top: calc(var(--rect-top) * 1px - var(--mark-border-size));
width: calc(var(--rect-width) * 1px + var(--mark-border-size) * 2);
height: calc(var(--rect-height) * 1px + var(--mark-border-size) * 2);
const marker = css({
'--mark-border-size': '1px',
position: 'absolute',
border: `var(--mark-border-size) solid ${cssVar.colorWarning}`,
boxSizing: 'border-box',
zIndex: 999999,
pointerEvents: 'none',
left: 'calc(var(--rect-left) * 1px - var(--mark-border-size))',
top: 'calc(var(--rect-top) * 1px - var(--mark-border-size))',
width: 'calc(var(--rect-width) * 1px + var(--mark-border-size) * 2)',
height: 'calc(var(--rect-height) * 1px + var(--mark-border-size) * 2)',
opacity: 0,
transition: `all ${duration} ease`,
});
opacity: 0;
transition: all ${duration} ease;
`;
const markerActive = css({
[`&.${cx(marker)}`]: {
opacity: 0.875,
},
});
const markerActive = css`
&.${cx(marker)} {
opacity: 0.85;
}
`;
const markerPrimary = css`
&.${cx(marker)}.${cx(markerActive)} {
--mark-border-size: 2px;
opacity: 1;
box-shadow: 0 0 0 1px #fff;
z-index: 1000000;
}
`;
const markerPrimary = css({
[`&.${cx(marker)}.${cx(markerActive)}`]: {
'--mark-border-size': '2px',
opacity: 1,
boxShadow: '0 0 0 1px #fff',
zIndex: 1000000,
},
});
return {
marker,

View File

@@ -67,11 +67,14 @@ const Block: React.FC<BlockProps> = ({
...props
}) => {
const divRef = React.useRef<HTMLDivElement>(null);
const [value, setValue] = React.useState(defaultValue);
// 多选模式下,优先使用 multipleProps 中的 defaultValue
const multipleDefaultValue = (multipleProps as any)?.defaultValue;
const initialValue = mode === 'single' ? defaultValue : multipleDefaultValue;
const [value, setValue] = React.useState(initialValue);
React.useEffect(() => {
setValue(defaultValue);
}, [mode]);
setValue(mode === 'single' ? defaultValue : multipleDefaultValue);
}, [mode, defaultValue, multipleDefaultValue]);
return (
<Flex
@@ -101,7 +104,7 @@ const Block: React.FC<BlockProps> = ({
options={options}
{...(mode === 'multiple' ? multipleProps : {})}
styles={{ popup: { zIndex: 1 } }}
maxTagCount="responsive"
maxTagCount={process.env.NODE_ENV === 'test' ? 1 : 'responsive'}
placeholder="Please select"
allowClear
/>

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { InfoCircleOutlined, PushpinOutlined } from '@ant-design/icons';
import { get, set } from '@rc-component/util';
import { Button, Col, ConfigProvider, Flex, Popover, Row, Tag, theme, Typography } from 'antd';
import { createStyles, css } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { clsx } from 'clsx';
import Prism from 'prismjs';
@@ -12,7 +12,7 @@ export interface SemanticPreviewInjectionProps {
classNames?: Record<string, string>;
}
const useStyle = createStyles(({ cssVar }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
position: relative;
z-index: 0;
@@ -122,6 +122,7 @@ export interface SemanticPreviewProps {
height?: number;
padding?: false;
style?: React.CSSProperties;
motion?: boolean;
}
const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
@@ -133,6 +134,7 @@ const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
style,
componentName = 'Component',
itemsAPI,
motion = false,
} = props;
const { token } = theme.useToken();
@@ -155,8 +157,6 @@ const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
const mergedSemantic = pinSemantic || hoverSemantic;
const { styles } = useStyle();
const hoveredSemanticClassNames = React.useMemo(() => {
if (!mergedSemantic) {
return semanticClassNames;
@@ -185,7 +185,7 @@ const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
className={clsx(styles.colWrap, padding === false && styles.colWrapPaddingLess)}
style={style}
>
<ConfigProvider theme={{ token: { motion: false } }}>{cloneNode}</ConfigProvider>
<ConfigProvider theme={{ token: { motion } }}>{cloneNode}</ConfigProvider>
</Col>
<Col span={8}>
<ul className={clsx(styles.listWrap)}>

View File

@@ -1,6 +1,7 @@
import React, { useRef, useState } from 'react';
import { AntDesignOutlined, UserOutlined } from '@ant-design/icons';
import { Bubble, Sender } from '@ant-design/x';
import type { SenderRef } from '@ant-design/x/es/sender';
import { Drawer, Flex, Typography } from 'antd';
import type { GetProp } from 'antd';
@@ -28,7 +29,8 @@ export interface PromptDrawerProps {
const PromptDrawer: React.FC<PromptDrawerProps> = ({ open, onClose, onThemeChange }) => {
const [locale] = useLocale(locales);
const [inputValue, setInputValue] = useState('');
const senderRef = useRef<any>(null);
const senderRef = useRef<SenderRef>(null);
const [submitPrompt, loading, prompt, resText, cancelRequest] = usePromptTheme(onThemeChange);
@@ -51,17 +53,21 @@ const PromptDrawer: React.FC<PromptDrawerProps> = ({ open, onClose, onThemeChang
const nextItems: GetProp<typeof Bubble.List, 'items'> = [
{
key: 1,
role: 'user',
placement: 'end',
content: prompt,
avatar: { icon: <UserOutlined /> },
avatar: <UserOutlined />,
shape: 'corner',
},
{
key: 2,
role: 'system',
placement: 'start',
content: resText,
avatar: { icon: <AntDesignOutlined /> },
avatar: <AntDesignOutlined />,
loading: !resText,
messageRender: (content: string) => (
contentRender: (content: string) => (
<Typography>
<pre style={{ margin: 0 }}>{content}</pre>
</Typography>
@@ -71,9 +77,11 @@ const PromptDrawer: React.FC<PromptDrawerProps> = ({ open, onClose, onThemeChang
if (!loading) {
nextItems.push({
key: 3,
role: 'divider',
placement: 'start',
content: locale.finishTips,
avatar: { icon: <AntDesignOutlined /> },
avatar: <AntDesignOutlined />,
shape: 'corner',
});
}

View File

@@ -1,6 +1,5 @@
/* eslint-disable compat/compat */
import { useRef, useState } from 'react';
import { XStream } from '@ant-design/x';
import { XStream } from '@ant-design/x-sdk';
import type { SiteContextProps } from '../../../theme/slots/SiteContext';

View File

@@ -61,11 +61,14 @@ const Block: React.FC<BlockProps> = ({
...props
}) => {
const divRef = React.useRef<HTMLDivElement>(null);
const [value, setValue] = React.useState(defaultValue);
// 多选模式下,优先使用 multipleProps 中的 defaultValue
const multipleDefaultValue = (multipleProps as any)?.defaultValue;
const initialValue = mode === 'single' ? defaultValue : multipleDefaultValue;
const [value, setValue] = React.useState(initialValue);
React.useEffect(() => {
setValue(defaultValue);
}, [mode]);
setValue(mode === 'single' ? defaultValue : multipleDefaultValue);
}, [mode, defaultValue, multipleDefaultValue]);
return (
<Flex
@@ -93,7 +96,7 @@ const Block: React.FC<BlockProps> = ({
treeData={treeData}
{...(mode === 'multiple' ? multipleProps : {})}
styles={{ popup: { zIndex: 1 } }}
maxTagCount="responsive"
maxTagCount={process.env.NODE_ENV === 'test' ? 1 : 'responsive'}
placeholder="Please select"
/>
</Flex>

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import useLocale from '../../../hooks/useLocale';
import EN from './en-US.md';
@@ -7,7 +7,7 @@ import CN from './zh-CN.md';
const changeLog = { cn: CN, en: EN };
const useStyle = createStyles(({ css }) => ({
const classNames = createStaticStyles(({ css }) => ({
container: css`
max-height: max(62vh, 500px);
overflow-y: scroll;
@@ -25,13 +25,11 @@ const useStyle = createStyles(({ css }) => ({
const ChangeLog = () => {
const [, lang] = useLocale();
const { styles } = useStyle();
const validatedLanguage = Object.keys(changeLog).includes(lang) ? lang : 'en';
const C = changeLog[validatedLanguage];
return (
<div className={styles.container}>
<div className={classNames.container}>
<C />
</div>
);

View File

@@ -1,13 +1,13 @@
import type { PropsWithChildren } from 'react';
import React from 'react';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { useSearchParams } from 'dumi';
import CommonHelmet from '../../common/CommonHelmet';
import Content from '../../slots/Content';
import Sidebar from '../../slots/Sidebar';
const useStyle = createStyles(({ css, cssVar }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
main: css`
display: flex;
margin-top: ${cssVar.marginXL};
@@ -15,7 +15,6 @@ const useStyle = createStyles(({ css, cssVar }) => ({
}));
const SidebarLayout: React.FC<PropsWithChildren> = ({ children }) => {
const { styles } = useStyle();
const [searchParams] = useSearchParams();
const hideLayout = searchParams.get('layout') === 'false';
return (

View File

@@ -36,7 +36,8 @@
"app.demo.stackblitz": "Open in Stackblitz",
"app.demo.codeblock": "Open in Hitu (This feature is only available in the internal network environment)",
"app.demo.separate": "Open in a new window",
"app.demo.online": "Online Address",
"app.demo.online": "Official demo",
"app.demo.previousVersion": "Previous version",
"app.home.introduce": "A design system for enterprise-level products. Create an efficient and enjoyable work experience.",
"app.home.pr-welcome": "💡 It is an alpha version and still in progress. Contribution from community is welcome!",
"app.home.recommend": "Recommended",

View File

@@ -36,7 +36,8 @@
"app.demo.stackblitz": "在 Stackblitz 中打开",
"app.demo.codeblock": "在海兔中打开(此功能仅在内网环境可用)",
"app.demo.separate": "在新窗口打开",
"app.demo.online": "线上地址",
"app.demo.online": "官网示例",
"app.demo.previousVersion": "历史版本",
"app.home.introduce": "企业级产品设计体系,创造高效愉悦的工作体验",
"app.home.pr-welcome": "💡 当前为 alpha 版本,仍在开发中。欢迎社区一起共建,让 Ant Design 变得更好!",
"app.home.recommend": "精彩推荐",

View File

@@ -1,242 +1,17 @@
import { createHash } from 'crypto';
import fs from 'fs';
import path from 'path';
import createEmotionServer from '@emotion/server/create-instance';
import type { IApi, IRoute } from 'dumi';
import ReactTechStack from 'dumi/dist/techStacks/react';
import tsToJs from './utils/tsToJs';
import type { IApi } from 'dumi';
import { dependencies, devDependencies } from '../../package.json';
import buildAssetsPlugin from './plugins/build-assets';
import llmsPlugin from './plugins/llms';
import rawMdPlugin from './plugins/raw-md';
import routesPlugin from './plugins/routes';
import semanticMdPlugin from './plugins/semantic-md';
import techStackPlugin from './plugins/tech-stack';
function extractEmotionStyle(html: string) {
// copy from emotion ssr
// https://github.com/vercel/next.js/blob/deprecated-main/examples/with-emotion-vanilla/pages/_document.js
const styles = global.__ANTD_STYLE_CACHE_MANAGER_FOR_SSR__.getCacheList().map((cache) => {
const result = createEmotionServer(cache).extractCritical(html);
if (!result.css) {
return null;
}
const { css, ids } = result;
return {
key: cache.key,
css,
ids,
tag: `<style data-emotion="${cache.key} ${result.ids.join(' ')}">${result.css}</style>`,
};
});
return styles.filter(Boolean);
export default async function plugin(api: IApi) {
techStackPlugin(api);
routesPlugin(api);
await buildAssetsPlugin(api);
rawMdPlugin(api);
semanticMdPlugin(api);
llmsPlugin(api);
}
export const getHash = (str: string, length = 8) =>
createHash('md5').update(str).digest('hex').slice(0, length);
/**
* extends dumi internal tech stack, for customize previewer props
*/
class AntdReactTechStack extends ReactTechStack {
generatePreviewerProps(...[props, opts]: any) {
props.pkgDependencyList = { ...devDependencies, ...dependencies };
props.jsx ??= '';
if (opts.type === 'code-block') {
props.jsx = opts?.entryPointCode ? tsToJs(opts.entryPointCode) : '';
}
if (opts.type === 'external') {
// try to find md file with the same name as the demo tsx file
const locale = opts.mdAbsPath.match(/index\.([a-z-]+)\.md$/i)?.[1];
const mdPath = opts.fileAbsPath!.replace(/\.\w+$/, '.md');
const md = fs.existsSync(mdPath) ? fs.readFileSync(mdPath, 'utf-8') : '';
const codePath = opts.fileAbsPath!.replace(/\.\w+$/, '.tsx');
const code = fs.existsSync(codePath) ? fs.readFileSync(codePath, 'utf-8') : '';
props.jsx = tsToJs(code);
if (md) {
// extract description & css style from md file
const blocks: Record<string, string> = {};
const lines = md.split('\n');
let blockName = '';
let cacheList: string[] = [];
// Get block name
const getBlockName = (text: string) => {
if (text.startsWith('## ')) {
return text.replace('## ', '').trim();
}
if (text.startsWith('```css') || text.startsWith('<style>')) {
return 'style';
}
return null;
};
// Fill block content
const fillBlock = (name: string, lineList: string[]) => {
if (lineList.length) {
let fullText: string;
if (name === 'style') {
fullText = lineList
.join('\n')
.replace(/<\/?style>/g, '')
.replace(/```(\s*css)/g, '');
} else {
fullText = lineList.slice(1).join('\n');
}
blocks[name] = fullText;
}
};
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Mark as new block
const nextBlockName = getBlockName(line);
if (nextBlockName) {
fillBlock(blockName, cacheList);
// Next Block
blockName = nextBlockName;
cacheList = [line];
} else {
cacheList.push(line);
}
}
// Last block
fillBlock(blockName, cacheList);
props.description = blocks[locale];
props.style = blocks.style;
}
}
return props;
}
}
const resolve = (p: string): string => require.resolve(p);
const RoutesPlugin = async (api: IApi) => {
const chalk = await import('chalk').then((m) => m.default);
// const ssrCssFileName = `ssr-${Date.now()}.css`;
const writeCSSFile = (key: string, hashKey: string, cssString: string) => {
const fileName = `style-${key}.${getHash(hashKey)}.css`;
const filePath = path.join(api.paths.absOutputPath, fileName);
if (!fs.existsSync(filePath)) {
api.logger.event(chalk.grey(`write to: ${filePath}`));
fs.writeFileSync(filePath, cssString, 'utf8');
}
return fileName;
};
const addLinkStyle = (html: string, cssFile: string, prepend = false) => {
const prefix = api.userConfig.publicPath || api.config.publicPath;
if (prepend) {
return html.replace('<head>', `<head><link rel="stylesheet" href="${prefix + cssFile}">`);
}
return html.replace('</head>', `<link rel="stylesheet" href="${prefix + cssFile}"></head>`);
};
api.registerTechStack(() => new AntdReactTechStack());
api.modifyRoutes((routes) => {
// TODO: append extra routes, such as home, changelog, form-v3
/**
* **important!** Make sure that the `id` and `path` are consistent.
* see: https://github.com/ant-design/ant-design/issues/55960
*/
const extraRoutesList: IRoute[] = [
{
id: 'changelog',
path: 'changelog',
absPath: '/changelog',
parentId: 'DocLayout',
file: resolve('../../CHANGELOG.en-US.md'),
},
{
id: 'changelog-cn',
path: 'changelog-cn',
absPath: '/changelog-cn',
parentId: 'DocLayout',
file: resolve('../../CHANGELOG.zh-CN.md'),
},
{
id: 'components/changelog',
path: 'components/changelog',
absPath: '/components/changelog',
parentId: 'DocLayout',
file: resolve('../../CHANGELOG.en-US.md'),
},
{
id: 'components/changelog-cn',
path: 'components/changelog-cn',
absPath: '/components/changelog-cn',
parentId: 'DocLayout',
file: resolve('../../CHANGELOG.zh-CN.md'),
},
];
extraRoutesList.forEach((itemRoute) => {
routes[itemRoute.path] = itemRoute;
});
return routes;
});
api.modifyExportHTMLFiles((files) =>
files
// exclude dynamic route path, to avoid deploy failed by `:id` directory
.filter((f) => !f.path.includes(':'))
.map((file) => {
// 1. 提取 antd-style 样式
const styles = extractEmotionStyle(file.content);
// 2. 提取每个样式到独立 css 文件
styles.forEach((result) => {
api.logger.event(
`${chalk.yellow(file.path)} include ${chalk.blue`[${result!.key}]`} ${chalk.yellow(
result!.ids.length,
)} styles`,
);
const cssFile = writeCSSFile(result!.key, result!.ids.join(''), result!.css);
file.content = addLinkStyle(file.content, cssFile);
});
return file;
}),
);
// add ssr css file to html
api.modifyConfig((memo) => {
memo.styles ??= [];
// memo.styles.push(`/${ssrCssFileName}`);
return memo;
});
if (process.env.NODE_ENV === 'production') {
api.addEntryImportsAhead(() => ({
source: path.join(api.paths.cwd, 'components', 'style', 'antd.css'),
}));
}
};
export default RoutesPlugin;

View File

@@ -0,0 +1,98 @@
import { createHash } from 'crypto';
import fs from 'fs';
import path from 'path';
import createEmotionServer from '@emotion/server/create-instance';
import type { IApi } from 'dumi';
export const getHash = (str: string, length = 8) =>
createHash('md5').update(str).digest('hex').slice(0, length);
function extractEmotionStyle(html: string) {
// copy from emotion ssr
// https://github.com/vercel/next.js/blob/deprecated-main/examples/with-emotion-vanilla/pages/_document.js
const styles = global.__ANTD_STYLE_CACHE_MANAGER_FOR_SSR__.getCacheList().map((cache) => {
const result = createEmotionServer(cache).extractCritical(html);
if (!result.css) {
return null;
}
const { css, ids } = result;
return {
key: cache.key,
css,
ids,
tag: `<style data-emotion="${cache.key} ${result.ids.join(' ')}">${result.css}</style>`,
};
});
return styles.filter(Boolean);
}
export default async function buildAssetsPlugin(api: IApi) {
const chalk = await import('chalk').then((m) => m.default);
const writeCSSFile = (key: string, hashKey: string, cssString: string) => {
const fileName = `style-${key}.${getHash(hashKey)}.css`;
const filePath = path.join(api.paths.absOutputPath, fileName);
if (!fs.existsSync(filePath)) {
api.logger.event(chalk.grey(`write to: ${filePath}`));
fs.writeFileSync(filePath, cssString, 'utf8');
}
return fileName;
};
const addLinkStyle = (html: string, cssFile: string, prepend = false) => {
const prefix = api.userConfig.publicPath || api.config.publicPath;
if (prepend) {
return html.replace('<head>', `<head><link rel="stylesheet" href="${prefix + cssFile}">`);
}
return html.replace('</head>', `<link rel="stylesheet" href="${prefix + cssFile}"></head>`);
};
api.modifyExportHTMLFiles((files) =>
files
// exclude dynamic route path, to avoid deploy failed by `:id` directory
.filter((f) => !f.path.includes(':'))
.map((file) => {
// 1. 提取 antd-style 样式
const styles = extractEmotionStyle(file.content);
// 2. 提取每个样式到独立 css 文件
styles.forEach((result) => {
api.logger.event(
`${chalk.yellow(file.path)} include ${chalk.blue`[${result!.key}]`} ${chalk.yellow(
result!.ids.length,
)} styles`,
);
const cssFile = writeCSSFile(result!.key, result!.ids.join(''), result!.css);
file.content = addLinkStyle(file.content, cssFile);
});
return file;
}),
);
// add ssr css file to html
api.modifyConfig((memo) => {
memo.styles ??= [];
return memo;
});
if (process.env.NODE_ENV === 'production') {
// `addEntryImportsAhead` do not support compile,
// so it will build file content directly without compile.
// We add additional pre-site script for this,
// but this will not affect normal developer usage.
api.addEntryImportsAhead(() => ({
source: path.join(api.paths.cwd, 'components', 'style', '~antd.layer.css'),
}));
}
}

284
.dumi/theme/plugins/llms.ts Normal file
View File

@@ -0,0 +1,284 @@
import fs from 'fs';
import path from 'path';
import type { IApi } from 'dumi';
import { glob } from 'glob';
interface DocItem {
title: string;
url: string;
category: 'docs' | 'component' | 'semantic';
content?: string;
}
interface ProcessResult {
docs: DocItem[];
components: DocItem[];
semantics: DocItem[];
}
function processMarkdownFile(
markdownFile: string,
siteDir: string,
targetArrays: ProcessResult,
): void {
const mdPath = path.join(siteDir, markdownFile);
const content = fs.readFileSync(mdPath, 'utf-8').trim();
if (!content) {
return;
}
// Extract title from first H1 heading
const titleMatch = content.match(/^#\s+(.+)$/m);
const title = titleMatch ? titleMatch[1].trim() : path.basename(markdownFile, '.md');
// Generate URL from file path (use .md suffix)
let urlPath = markdownFile.replace(/\.md$/, '');
// Remove /index suffix for component pages
if (urlPath.endsWith('/index')) {
urlPath = urlPath.replace(/\/index$/, '');
}
const url = `https://ant.design/${urlPath}.md`;
// Categorize files
if (/\/semantic.*\.md$/.test(markdownFile)) {
// Component semantic files
const componentName = path.basename(path.dirname(markdownFile));
const semanticFileName = path.basename(markdownFile, '.md');
// 提取 semantic 后缀(如 semantic_ribbon -> Ribbon
const semanticSuffix = semanticFileName.replace(/^semantic/, '').replace(/^_/, '');
const displaySuffix = semanticSuffix
? semanticSuffix
.split('_')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join('')
: '';
targetArrays.semantics.push({
title: `${componentName}${displaySuffix ? ` ${displaySuffix}` : ''} Semantic`,
url,
category: 'semantic',
content,
});
} else if (markdownFile.startsWith('components/')) {
// Component documentation files
const componentName = path.basename(markdownFile, '.md');
targetArrays.components.push({
title: componentName,
url,
category: 'component',
content,
});
} else if (markdownFile.startsWith('docs/') || markdownFile.startsWith('changelog')) {
// Documentation files
targetArrays.docs.push({
title,
url,
category: 'docs',
content,
});
}
}
async function generateLLms(api: IApi) {
const siteDir = api.paths.absOutputPath;
// Ensure siteDir exists
if (!fs.existsSync(siteDir)) {
api.logger.error('Error: Output directory does not exist. Please run build first.');
return;
}
// Find all markdown files in _site, excluding llms files
const markdownFiles = await glob('**/*.md', {
cwd: siteDir,
ignore: ['llms*.md', 'llms*.txt'],
});
// Separate English and Chinese docs
const englishDocs = markdownFiles.filter(
(file) => !file.includes('-cn/') && !file.endsWith('-cn.md'),
);
const chineseDocs = markdownFiles.filter(
(file) => file.includes('-cn/') || file.endsWith('-cn.md'),
);
const englishResult: ProcessResult = {
docs: [],
components: [],
semantics: [],
};
const chineseResult: ProcessResult = {
docs: [],
components: [],
semantics: [],
};
// Process English docs
for (const markdownFile of englishDocs) {
try {
processMarkdownFile(markdownFile, siteDir, englishResult);
} catch (error) {
api.logger.warn(`Error processing ${markdownFile}:`, error);
}
}
// Process Chinese docs
for (const markdownFile of chineseDocs) {
try {
processMarkdownFile(markdownFile, siteDir, chineseResult);
} catch (error) {
api.logger.warn(`Error processing ${markdownFile}:`, error);
}
}
const { docs, components, semantics } = englishResult;
const { docs: docsCn, components: componentsCn, semantics: semanticsCn } = chineseResult;
// Sort by title
docs.sort((a, b) => a.title.localeCompare(b.title));
components.sort((a, b) => a.title.localeCompare(b.title));
semantics.sort((a, b) => a.title.localeCompare(b.title));
docsCn.sort((a, b) => a.title.localeCompare(b.title));
componentsCn.sort((a, b) => a.title.localeCompare(b.title));
semanticsCn.sort((a, b) => a.title.localeCompare(b.title));
// 1. Generate llms-semantic.md
const semanticContent = [
'# Ant Design Semantic Documentation',
'',
'This file contains aggregated semantic descriptions for all components.',
'',
`> Total ${semantics.length} components contain semantic descriptions`,
'',
...semantics.flatMap((semantic) => [
`# ${semantic.title}`,
'',
`Source: ${semantic.url}`,
'',
semantic.content || '',
'',
'---',
'',
]),
].join('\n');
// 2. Generate llms-semantic-cn.md
const semanticContentCn = [
'# Ant Design 组件语义化描述',
'',
'本文档包含了 Ant Design 组件库中所有组件的语义化描述信息。',
'',
`> 总计 ${semanticsCn.length} 个组件包含语义化描述`,
'',
...semanticsCn.flatMap((semantic) => [
`# ${semantic.title}`,
'',
`Source: ${semantic.url}`,
'',
semantic.content || '',
'',
'---',
'',
]),
].join('\n');
// 3. Generate llms-full.txt
const fullContent = [
'# Ant Design Component Documentation',
'',
'This file contains aggregated content from all component docs.',
'',
`> Total ${components.length} components`,
'',
...components.flatMap((component) => [
`## ${component.title}`,
'',
`Source: ${component.url}`,
'',
component.content || '',
'',
'---',
'',
]),
].join('\n');
// 4. Generate llms-full-cn.txt
const fullContentCn = [
'# Ant Design 组件文档',
'',
'本文件包含所有组件文档的聚合内容。',
'',
`> 总计 ${componentsCn.length} 个组件`,
'',
...componentsCn.flatMap((component) => [
`## ${component.title}`,
'',
`Source: ${component.url}`,
'',
component.content || '',
'',
'---',
'',
]),
].join('\n');
// 5. Generate llms.txt
const llmsNavContent = [
'# Ant Design - Enterprise-class React UI library',
'',
'- Ant Design, developed by Ant Group, is a React UI library that aims to provide a high-quality design language and development framework for enterprise-level backend management systems. It offers a rich set of components and design guidelines, helping developers build modern, responsive, and high-performance web applications.',
'',
'## Navigation',
'',
'- [Full Documentation (EN)](./llms-full.txt)',
'- [Full Documentation (CN)](./llms-full-cn.txt)',
'- [Semantic Documentation (EN)](./llms-semantic.md)',
'- [Semantic Documentation (CN)](./llms-semantic-cn.md)',
'',
'## Docs (EN)',
'',
...docs.map(({ title, url }) => `- [${title}](${url})`),
'',
'## Docs (CN)',
'',
...docsCn.map(({ title, url }) => `- [${title}](${url})`),
'',
'## Component Docs (EN)',
'',
...components.map(({ title, url }) => `- [${title}](${url})`),
'',
'## Component Docs (CN)',
'',
...componentsCn.map(({ title, url }) => `- [${title}](${url})`),
'',
'## Semantic (EN)',
'',
...semantics.map(({ title, url }) => `- [${title}](${url})`),
'',
'## Semantic (CN)',
'',
...semanticsCn.map(({ title, url }) => `- [${title}](${url})`),
'',
].join('\n');
// Write all files
fs.writeFileSync(path.join(siteDir, 'llms-semantic.md'), semanticContent);
fs.writeFileSync(path.join(siteDir, 'llms-semantic-cn.md'), semanticContentCn);
fs.writeFileSync(path.join(siteDir, 'llms-full.txt'), fullContent);
fs.writeFileSync(path.join(siteDir, 'llms-full-cn.txt'), fullContentCn);
fs.writeFileSync(path.join(siteDir, 'llms.txt'), llmsNavContent);
api.logger.event(
`Generated llms.txt (navigation), llms-full.txt (${components.length} components), llms-full-cn.txt (${componentsCn.length} components), llms-semantic.md (${semantics.length} semantics), llms-semantic-cn.md (${semanticsCn.length} semantics)`,
);
}
export default async function llmsPlugin(api: IApi) {
api.modifyExportHTMLFiles(async (files) => {
await generateLLms(api);
return files;
});
}

View File

@@ -0,0 +1,596 @@
import fs from 'fs';
import path from 'path';
import type { IApi } from 'dumi';
type TokenMeta = {
components?: Record<
string,
Array<{ token: string; desc?: string; descEn?: string; type?: string }>
>;
global?: Record<
string,
{
desc?: string;
descEn?: string;
type?: string;
name?: string;
nameEn?: string;
}
>;
};
type TokenData = Record<string, { component?: Record<string, unknown>; global?: string[] }>;
/**
* 路由信息
*/
interface RouteInfo {
absPath: string;
file: string;
}
/**
* 内容过滤器上下文信息
*/
interface ContentFilterContext {
route: RouteInfo;
file: string;
absPath: string;
relPath: string;
api: IApi;
}
/**
* 插件配置选项
*/
export interface PluginOptions {
/**
* 路由过滤器:决定哪些路由需要生成 markdown
* @param route - 路由信息
* @returns 返回 true 表示处理该路由false 表示跳过
*/
routeFilter?: (route: RouteInfo) => boolean;
/**
* 内容替换器数组:在处理内容前可以替换或修改内容,按顺序链式应用
* @param content - 原始 markdown 内容
* @param context - 替换器上下文信息包含路由、文件路径、API 等
* @returns 返回处理后的内容,如果返回 null 或空字符串则跳过该路由
*/
contentReplacers?: Array<(content: string, context: ContentFilterContext) => string | null>;
/**
* 是否启用替换 <code src> 标签功能,默认为 true
*/
enableReplaceCodeSrc?: boolean;
/**
* 代码追加函数:在替换 <code src> 标签时,用于追加额外的内容(如 demo 描述信息)
* @param docFileAbs - 文档文件的绝对路径
* @returns 返回要追加的 markdown 内容字符串
*/
codeAppend?: (docFileAbs: string, src: string) => string;
}
/**
* 收集的 raw markdown 路由列表
*/
let RAW_MD_ROUTES: Array<RouteInfo> = [];
/**
* 插件配置
*/
let PLUGIN_OPTIONS: PluginOptions = {};
/**
* 记录 raw markdown 文档是否已经输出过
*/
let RAW_MD_EMITTED = false;
/**
* Token 数据缓存
* 避免重复读取文件,提升性能
*/
let TOKEN_CACHE: { meta: TokenMeta; data: TokenData } | null | undefined;
/**
* 读取 JSON 文件,如果文件不存在或解析失败则返回 null
*
* @param abs - JSON 文件的绝对路径
* @returns 解析后的 JSON 对象,如果文件不存在或解析失败则返回 null
*/
function readJsonIfExists<T>(abs: string): T | null {
try {
if (!fs.existsSync(abs)) return null;
return JSON.parse(fs.readFileSync(abs, 'utf-8')) as T;
} catch {
return null;
}
}
/**
* 根据文档文件路径检测文档语言
* 通过文件名后缀判断是中文还是英文文档
*
* @param docFileAbs - 文档文件的绝对路径
* @returns 返回检测到的语言,默认为 'en-US'
*/
function detectDocLocale(docFileAbs: string): 'zh-CN' | 'en-US' {
if (/-cn\.md$/i.test(docFileAbs) || /\.zh-CN\.md$/i.test(docFileAbs)) {
return 'zh-CN';
}
return 'en-US';
}
/**
* 替换 markdown 中的 prettier-ignore 注释为空
* 用于清理文档内容,移除 `<!-- prettier-ignore -->` 格式的注释
*
* @param md - 原始 markdown 内容
* @returns 替换 prettier-ignore 注释后的 markdown 内容
*/
function replacePrettierIgnore(md: string) {
return md.replace(/<!--\s*prettier-ignore\s*-->\s*\n?/g, '');
}
/**
* 替换 markdown 中的 "Semantic DOM" 部分的 code 标签为指向生成的 semantic.md 文件的链接
*
* @param md - 原始 markdown 内容
* @param context - 内容过滤器上下文信息
* @returns 替换后的 markdown 内容
*/
function replaceSemanticDomSection(md: string, context: ContentFilterContext) {
// 从文档路径推断组件路径(用于生成链接)
// 例如components/card/index.en-US.md -> components/card/semantic.md
const componentPathMatch = context.file.match(/components\/([^/]+)\//);
if (!componentPathMatch) return md;
const componentName = componentPathMatch[1];
const isZhCN = /-cn\.md$/i.test(context.file) || /\.zh-CN\.md$/i.test(context.file);
const componentPath = `components/${componentName}${isZhCN ? '-cn' : ''}`;
// 匹配 <code src="./demo/_semantic*.tsx"> 标签并替换为 URL 地址
return md.replace(/<code[^>]*_semantic[^>]*>.*?<\/code>/g, (match) => {
// 从匹配的标签中提取文件名
const demoIndex = match.indexOf('./demo/');
if (demoIndex === -1) return match;
const start = demoIndex + './demo/'.length;
const end = match.indexOf('"', start);
if (end === -1) return match;
const semanticFile = match.substring(start, end);
// 生成对应的 semantic.md 文件名_semantic.tsx -> semantic.md, _semantic_meta.tsx -> semantic_meta.md
const semanticMdFileName = semanticFile
.replace(/^_semantic/, 'semantic')
.replace(/\.tsx$/, '.md');
return `https://ant.design/${componentPath}/${semanticMdFileName}`;
});
}
/**
* 获取文本中最大连续反引号的数量
* 用于确定代码块围栏所需的反引号数量,避免代码块内部的反引号与围栏冲突
*
* @param text - 待检查的文本内容
* @returns 文本中最大连续反引号的数量
*/
function getMaxBacktickRun(text: string) {
let max = 0;
const re = /`+/g;
let m: RegExpExecArray | null = re.exec(text);
while (m) {
if (m[0].length > max) max = m[0].length;
m = re.exec(text);
}
return max;
}
/**
* 将代码包装为 markdown 代码块格式
* 自动根据代码内容中的反引号数量确定围栏长度,避免代码块溢出
*
* @param code - 待包装的代码内容
* @param lang - 代码块的语言标识符(如 'tsx', 'js', 'css' 等),默认为空字符串
* @returns 格式化后的 markdown 代码块
*/
function wrapFencedCode(code: string, lang = '') {
const maxTicks = getMaxBacktickRun(code);
const fence = '`'.repeat(Math.max(3, maxTicks + 1));
const head = lang ? `${fence}${lang}` : fence;
return `${head}\n${code.trimEnd()}\n${fence}`;
}
/**
* 根据文件扩展名获取代码块语言标识
* @param filePath - 文件路径
* @returns 代码块语言标识
*/
function getCodeLang(filePath: string): string {
const ext = path.extname(filePath).toLowerCase();
const langMap: Record<string, string> = {
'.tsx': 'tsx',
'.ts': 'typescript',
'.jsx': 'jsx',
'.js': 'javascript',
'.vue': 'vue',
'.css': 'css',
'.less': 'less',
'.scss': 'scss',
'.sass': 'sass',
'.json': 'json',
'.html': 'html',
'.md': 'markdown',
};
return langMap[ext] || ext.slice(1) || '';
}
/**
* 从 markdown 内容中提取指定语言的块
* 用于处理多语言文档,提取特定语言版本的标题块内容
*
* @param docFileAbs - 文档文件的绝对路径
* @returns 提取的指定语言块内容,如果未找到则返回整个文档的 trim 结果
*/
function antdCodeAppend(docFileAbs: string, src: string): string {
const docDir = path.dirname(docFileAbs);
const locale = detectDocLocale(docFileAbs);
const demoAbs = path.resolve(docDir, src);
const demoMdAbs = demoAbs.replace(path.extname(src), '.md');
const demoMd = fs.existsSync(demoMdAbs) ? fs.readFileSync(demoMdAbs, 'utf-8') : '';
const other = locale === 'zh-CN' ? 'en-US' : 'zh-CN';
const re = new RegExp(
`(^|\\n)##\\s*${locale}\\s*\\n([\\s\\S]*?)(?=\\n##\\s*${other}\\s*\\n|$)`,
'i',
);
const match = demoMd.match(re);
if (!match) return demoMd.trim();
return (match[2] ?? '').trim();
}
/**
* 替换 <code src> 标签为 markdown 代码块
* 将 `<code src="./demo/basic.tsx" version="5.21.0">标题</code>` 替换为完整的 demo 代码块
* 支持读取对应的 .md 文件作为 demo 描述,并根据文档语言提取对应语言块
*
* @param md - 原始 markdown 内容
* @param docFileAbs - 文档文件的绝对路径,用于解析相对路径和检测语言
* @param enablePickLocaleBlock - 是否启用多语言块提取,可以是布尔值或函数,默认为 true
* @returns 替换后的 markdown 内容
*/
function replaceCodeSrcToMarkdown(
md: string,
docFileAbs: string,
codeAppend?: (docFileAbs: string, src: string) => string,
) {
const docDir = path.dirname(docFileAbs);
// 匹配 <code src="./demo/basic.tsx">标题</code> 格式的标签
const codeTagRE = /<code\s+[^>]*?src="([^"]+)"[^>]*>([\s\S]*?)<\/code>/g;
return md.replace(codeTagRE, (full, src, title) => {
try {
// 如果标记了 debug 属性,直接去除不显示
if (full.includes('debug')) {
return '';
}
const parts: string[] = [];
const demoAbs = path.resolve(docDir, src);
const demoTitle = String(title || '').trim() || path.basename(demoAbs);
const code = fs.existsSync(demoAbs) ? fs.readFileSync(demoAbs, 'utf-8') : '';
parts.push(`### ${demoTitle}`);
if (codeAppend) {
parts.push(codeAppend(docFileAbs, src));
}
if (code) {
parts.push(wrapFencedCode(code, getCodeLang(demoAbs)));
}
return `${parts.join('\n\n')}\n`;
} catch {
return full;
}
});
}
/**
* 从 components/version 目录读取 token 数据
* 支持懒加载和缓存,避免重复读取文件
*
* @param api - Dumi API 实例,用于获取项目路径
* @returns token 元数据和数据对象,如果文件不存在则返回 null
*/
function loadTokenFromRepo(api: IApi) {
if (TOKEN_CACHE !== undefined) return TOKEN_CACHE;
const cwd = api.paths.cwd;
const metaPath = path.join(cwd, 'components', 'version', 'token-meta.json');
const dataPath = path.join(cwd, 'components', 'version', 'token.json');
const meta = readJsonIfExists<TokenMeta>(metaPath);
const data = readJsonIfExists<TokenData>(dataPath);
if (meta && data) {
TOKEN_CACHE = { meta, data };
} else {
TOKEN_CACHE = null;
}
return TOKEN_CACHE;
}
/**
* 转义 markdown 表格单元格中的特殊字符
* 将换行符替换为空格,转义管道符,避免破坏表格结构
*
* @param v - 待转义的值
* @returns 转义后的字符串
*/
function escapeMdCell(v: unknown) {
return String(v ?? '')
.replace(/\r?\n/g, ' ')
.replace(/\|/g, '\\|')
.trim();
}
/**
* 规范化 token 值为字符串格式
* 用于在 markdown 表格中显示 token 的默认值
*
* @param v - 待规范化的值
* @returns 规范化后的字符串null/undefined 返回空字符串
*/
function normalizeValue(v: unknown) {
if (v === undefined || v === null) return '';
if (typeof v === 'string') return v.trim();
if (typeof v === 'number' || typeof v === 'boolean') return String(v);
try {
return JSON.stringify(v);
} catch {
return String(v);
}
}
/**
* 替换 <ComponentTokenTable component="Button" /> 标签为 markdown 表格
* 处理流程:
* 1. 为每个组件生成组件 Token 表格(包含 token 名称、描述、类型、默认值)
* 2. 收集所有组件的全局 Token合并去重后生成全局 Token 表格
* 3. 支持多组件,用逗号分隔(如 component="Button,Input"
* 4. 原位替换原标签,保持文档顺序
*
* @param md - 原始 markdown 内容
* @param context - 内容过滤器上下文信息
* @returns 替换后的 markdown 内容
*/
function replaceComponentTokenTable(md: string, context: ContentFilterContext) {
const tokens = loadTokenFromRepo(context.api);
if (!tokens) return md;
const { meta: tokenMeta, data: tokenData } = tokens;
const locale = detectDocLocale(context.file);
const labels =
locale === 'zh-CN'
? {
componentTitle: '组件 Token',
globalTitle: '全局 Token',
name: 'Token 名称',
desc: '描述',
type: '类型',
value: '默认值',
}
: {
componentTitle: 'Component Token',
globalTitle: 'Global Token',
name: 'Token Name',
desc: 'Description',
type: 'Type',
value: 'Default Value',
};
const re =
/<ComponentTokenTable\s+[^>]*component="([^"]+)"[^>]*(?:\/>|>(?:\s*<\/ComponentTokenTable>)?)/g;
return md.replace(re, (full, componentProp) => {
const comp = String(componentProp || '').trim();
if (!comp) return full;
const comps = comp
.split(',')
.map((s) => s.trim())
.filter(Boolean);
const out: string[] = [];
// 使用 Set 收集所有组件的全局 Token自动去重
const globalTokenSet = new Set<string>();
// 遍历每个组件,分别处理组件 Token 和收集全局 Token
comps.forEach((comp) => {
// 1. 处理组件 Token为每个组件生成独立的表格
const metaList = tokenMeta.components?.[comp];
if (Array.isArray(metaList) && metaList.length > 0) {
out.push(`## ${labels.componentTitle} (${comp})`);
out.push(`| ${labels.name} | ${labels.desc} | ${labels.type} | ${labels.value} |`);
out.push(`| --- | --- | --- | --- |`);
metaList.forEach((item) => {
const name = item.token;
// 根据文档语言选择对应的描述文本
const desc = locale === 'zh-CN' ? (item.desc ?? '') : (item.descEn ?? item.desc ?? '');
const type = item.type ?? '';
// 从 tokenData 中获取组件 Token 的默认值
const value = normalizeValue(tokenData?.[comp]?.component?.[name]);
out.push(
`| ${escapeMdCell(name)} | ${escapeMdCell(desc)} | ${escapeMdCell(type)} | ${escapeMdCell(
value,
)} |`,
);
});
out.push('');
}
// 2. 收集全局 Token从每个组件的 global 数组中收集 token 名称
const globalTokens = tokenData?.[comp]?.global;
if (Array.isArray(globalTokens)) {
globalTokens.forEach((token) => {
globalTokenSet.add(token);
});
}
});
// 3. 处理全局 Token合并所有组件的全局 Token生成统一的表格
if (globalTokenSet.size > 0) {
const globalTokenList = Array.from(globalTokenSet).sort();
if (globalTokenList.length > 0) {
out.push(`## ${labels.globalTitle}`);
out.push(`| ${labels.name} | ${labels.desc} | ${labels.type} | ${labels.value} |`);
out.push(`| --- | --- | --- | --- |`);
globalTokenList.forEach((tokenName) => {
const meta = tokenMeta.global?.[tokenName];
if (meta) {
// 根据文档语言选择对应的描述文本
const desc = locale === 'zh-CN' ? (meta.desc ?? '') : (meta.descEn ?? meta.desc ?? '');
const type = meta.type ?? '';
// 全局 Token 的默认值需要在运行时通过 getDesignToken() 计算
// 在静态 markdown 生成阶段无法获取,因此留空
const value = '';
out.push(
`| ${escapeMdCell(tokenName)} | ${escapeMdCell(desc)} | ${escapeMdCell(type)} | ${escapeMdCell(
value,
)} |`,
);
}
});
out.push('');
}
}
// 如果没有生成任何内容,则保留原标签
if (!out.length) return full;
// 返回生成的 markdown 表格,前后添加换行确保格式正确
return `\n\n${out.join('\n').trim()}\n\n`;
});
}
/**
* 输出处理后的 raw markdown 文件到构建输出目录
* 仅在 production 环境下执行,确保每个文档只处理一次。
* 对收集的路由进行处理:移除 prettier-ignore 注释、替换 `<code src>` 标签、
* 替换 `<ComponentTokenTable />` 组件,然后将处理后的 markdown 写入输出目录
*
* @param api - Dumi API 实例,用于获取输出路径等配置
*/
function emitRawMd(api: IApi) {
if (process.env.NODE_ENV !== 'production') return;
if (RAW_MD_EMITTED) return;
RAW_MD_EMITTED = true;
const outRoot = api.paths.absOutputPath;
RAW_MD_ROUTES.forEach((route) => {
try {
const { absPath, file } = route;
const relPath = absPath.replace(/^\//, '');
if (!relPath || !fs.existsSync(file)) return;
// 应用路由过滤器
if (PLUGIN_OPTIONS.routeFilter && !PLUGIN_OPTIONS.routeFilter(route)) {
return;
}
let content = fs.readFileSync(file, 'utf-8');
const filterContext: ContentFilterContext = {
route,
file,
absPath,
relPath,
api,
};
if (PLUGIN_OPTIONS.contentReplacers && PLUGIN_OPTIONS.contentReplacers.length > 0) {
for (const replacer of PLUGIN_OPTIONS.contentReplacers) {
const replacedContent = replacer(content, filterContext);
if (replacedContent === null || replacedContent === '') {
return;
}
content = replacedContent;
}
}
// 处理步骤:
// 1. 替换 Semantic DOM 部分为指向生成的 semantic.md 文件的链接
content = replaceSemanticDomSection(content, filterContext);
// 2. 替换 <ComponentTokenTable /> 组件为 markdown 表格
content = replaceComponentTokenTable(content, filterContext);
// 3. 替换 prettier-ignore 注释(可通过配置开关控制)
content = replacePrettierIgnore(content);
// 4. 替换 <code src> 标签为完整的代码块(可通过配置开关控制)
if (PLUGIN_OPTIONS.enableReplaceCodeSrc !== false) {
content = replaceCodeSrcToMarkdown(content, file, PLUGIN_OPTIONS.codeAppend);
}
const outMd = path.join(outRoot, `${relPath}.md`);
fs.mkdirSync(path.dirname(outMd), { recursive: true });
fs.writeFileSync(outMd, content, 'utf-8');
api.logger.event(`Build ${relPath}.md`);
} catch (e) {
api.logger.error(`Failed to emit raw markdown for ${route.file}:`, e);
}
});
}
/**
* 负责在构建过程中处理 markdown 文档,生成 flattened markdown 文件到输出目录。
* 主要功能包括:
* 1. 收集所有 markdown 路由
* 2. 在 HTML 文件导出阶段输出处理后的 raw markdown 文件
*
* @param api - Dumi API 实例
* @param options - 插件配置选项
*/
export default function rawMdPlugin(api: IApi) {
// 注册配置键,允许用户在配置中使用 rawMd 键
api.describe({
key: 'rawMd',
config: {
schema(joi) {
return joi.object({
routeFilter: joi.function(),
contentReplacers: joi.array().items(joi.function()),
enableReplaceCodeSrc: joi.boolean(),
codeAppend: joi.function(),
});
},
onChange: api.ConfigChangeType.reload,
},
});
const configOptions = api.userConfig.rawMd as PluginOptions | undefined;
PLUGIN_OPTIONS = configOptions || {
enableReplaceCodeSrc: true,
codeAppend: antdCodeAppend,
};
api.modifyRoutes((routes) => {
RAW_MD_ROUTES = Object.values(routes)
.filter((r) => typeof r?.file === 'string' && r.file.endsWith('.md'))
.filter((r) => typeof r?.absPath === 'string' && r.absPath && !r.absPath.includes(':'))
.map((r) => ({ absPath: r.absPath, file: r.file as string }));
return routes;
});
api.modifyExportHTMLFiles((files) => {
emitRawMd(api);
return files;
});
}

View File

@@ -0,0 +1,50 @@
import type { IApi, IRoute } from 'dumi';
const resolve = (p: string): string => require.resolve(p);
export default function routesPlugin(api: IApi) {
api.modifyRoutes((routes) => {
// TODO: append extra routes, such as home, changelog, form-v3
/**
* **important!** Make sure that the `id` and `path` are consistent.
* see: https://github.com/ant-design/ant-design/issues/55960
*/
const extraRoutesList: IRoute[] = [
{
id: 'changelog',
path: 'changelog',
absPath: '/changelog',
parentId: 'DocLayout',
file: resolve('../../../CHANGELOG.en-US.md'),
},
{
id: 'changelog-cn',
path: 'changelog-cn',
absPath: '/changelog-cn',
parentId: 'DocLayout',
file: resolve('../../../CHANGELOG.zh-CN.md'),
},
{
id: 'components/changelog',
path: 'components/changelog',
absPath: '/components/changelog',
parentId: 'DocLayout',
file: resolve('../../../CHANGELOG.en-US.md'),
},
{
id: 'components/changelog-cn',
path: 'components/changelog-cn',
absPath: '/components/changelog-cn',
parentId: 'DocLayout',
file: resolve('../../../CHANGELOG.zh-CN.md'),
},
];
extraRoutesList.forEach((itemRoute) => {
routes[itemRoute.path] = itemRoute;
});
return routes;
});
}

View File

@@ -0,0 +1,553 @@
import fs from 'fs';
import path from 'path';
import type { IApi } from 'dumi';
let COMPONENT_ROUTES: Array<{ absPath: string; componentName: string; outputPath: string }> = [];
let SEMANTIC_MD_EMITTED = false;
// 解析 locales 字符串片段为语义映射表
function extractSemantics(objContent: string): Record<string, string> {
const result: Record<string, string> = {};
const semanticMatches = objContent.matchAll(/['"]?([^'":\s]+)['"]?\s*:\s*['"]([^'"]+)['"],?/g);
for (const match of semanticMatches) {
const [, key, value] = match;
if (key && value) {
result[key] = value;
}
}
return result;
}
/**
* 从 _semantic*.tsx 文件中提取语义信息
* @param semanticFile - _semantic*.tsx 文件的绝对路径
* @returns 包含中文和英文语义描述的对象,失败返回 null
*/
function extractLocaleInfoFromContent(content: string): {
cn: Record<string, string>;
en: Record<string, string>;
} | null {
// 匹配 locales 对象定义
const localesMatch = content.match(/const locales = \{([\s\S]*?)\};/);
if (!localesMatch) return null;
// 提取中文和英文的语义描述
const cnMatch = content.match(/cn:\s*\{([\s\S]*?)\},?\s*en:/);
if (!cnMatch) return null;
const enMatch = content.match(/en:\s*\{([\s\S]*?)\}\s*[,;]/);
if (!enMatch) return null;
const cnContent = cnMatch[1];
const enContent = enMatch[1];
const cnSemantics = extractSemantics(cnContent);
const enSemantics = extractSemantics(enContent);
if (Object.keys(cnSemantics).length === 0) return null;
return { cn: cnSemantics, en: enSemantics };
}
// 根据 import 路径找到模板文件的实际路径
function resolveTemplateFilePath(semanticFile: string, importPath: string): string | null {
const basePath = path.resolve(path.dirname(semanticFile), importPath);
const candidates = [
basePath,
`${basePath}.tsx`,
`${basePath}.ts`,
path.join(basePath, 'index.tsx'),
path.join(basePath, 'index.ts'),
];
for (const candidate of candidates) {
if (fs.existsSync(candidate)) return candidate;
}
return null;
}
// 识别被 JSX 使用的模板组件导入
function parseTemplateUsage(content: string): Array<{ componentName: string; importPath: string }> {
const results: Array<{ componentName: string; importPath: string }> = [];
const importRegex = /import\s+([^;]+?)\s+from\s+['"]([^'"]+)['"];?/g;
for (const match of content.matchAll(importRegex)) {
const importClause = match[1].trim();
const importPath = match[2].trim();
if (!importPath.startsWith('.')) continue;
const componentNames: string[] = [];
if (importClause.startsWith('{')) {
const names = importClause
.replace(/[{}]/g, '')
.split(',')
.map((name) =>
name
.trim()
.split(/\s+as\s+/)[0]
.trim(),
)
.filter(Boolean);
componentNames.push(...names);
} else if (importClause.includes('{')) {
const [defaultName, namedPart] = importClause.split(',');
if (defaultName?.trim()) {
componentNames.push(defaultName.trim());
}
const names = namedPart
.replace(/[{}]/g, '')
.split(',')
.map((name) =>
name
.trim()
.split(/\s+as\s+/)[0]
.trim(),
)
.filter(Boolean);
componentNames.push(...names);
} else {
componentNames.push(importClause);
}
for (const componentName of componentNames) {
if (new RegExp(`<${componentName}\\b`).test(content)) {
results.push({ componentName, importPath });
}
}
}
return results;
}
// 解析 ignoreSemantics 属性值
function parseIgnoreSemantics(propsString: string): string[] {
const ignoreMatch = propsString.match(/ignoreSemantics\s*=\s*\{([\s\S]*?)\}/);
if (!ignoreMatch) return [];
const ignoreContent = ignoreMatch[1];
return Array.from(ignoreContent.matchAll(/['"]([^'"]+)['"]/g)).map((match) => match[1]);
}
// 解析 singleOnly 属性值
function parseSingleOnly(propsString: string): boolean {
const singleOnlyMatch = propsString.match(/singleOnly(\s*=\s*\{?([^}\s]+)\}?)?/);
if (!singleOnlyMatch) return false;
if (!singleOnlyMatch[1]) return true;
const value = singleOnlyMatch[2];
return value !== 'false';
}
// 抽取模板组件 JSX 的属性字符串
function extractTemplateProps(content: string, componentName: string): string {
const start = content.indexOf(`<${componentName}`);
if (start === -1) return '';
let index = start + componentName.length + 1;
const propsStart = index;
let braceDepth = 0;
let stringChar: string | null = null;
for (; index < content.length; index += 1) {
const ch = content[index];
const prev = content[index - 1];
if (stringChar) {
if (ch === stringChar && prev !== '\\') {
stringChar = null;
}
continue;
}
if (ch === '"' || ch === "'" || ch === '`') {
stringChar = ch;
continue;
}
if (ch === '{') {
braceDepth += 1;
continue;
}
if (ch === '}') {
if (braceDepth > 0) braceDepth -= 1;
continue;
}
if (ch === '>' && braceDepth === 0) {
return content.slice(propsStart, index).trim();
}
}
return '';
}
// 应用 ignoreSemantics 与 singleOnly 过滤规则
function filterSemantics(
semantics: Record<string, string>,
options: { ignoreSemantics: string[]; singleOnly: boolean; templatePath: string },
): Record<string, string> {
const ignoreSet = new Set(options.ignoreSemantics);
const filteredEntries = Object.entries(semantics).filter(([key]) => !ignoreSet.has(key));
if (options.singleOnly) {
const singleOnlyKeys = new Set(['item', 'itemContent', 'itemRemove']);
return Object.fromEntries(filteredEntries.filter(([key]) => !singleOnlyKeys.has(key)));
}
return Object.fromEntries(filteredEntries);
}
// 从模板组件的 locales 提取语义信息
function extractSemanticInfoFromTemplate(
semanticFile: string,
content: string,
): { cn: Record<string, string>; en: Record<string, string> } | null {
const templates = parseTemplateUsage(content);
if (templates.length === 0) return null;
for (const template of templates) {
const templatePath = resolveTemplateFilePath(semanticFile, template.importPath);
if (!templatePath) continue;
const templateContent = fs.readFileSync(templatePath, 'utf-8');
const templateLocales = extractLocaleInfoFromContent(templateContent);
if (!templateLocales) continue;
const propsString = extractTemplateProps(content, template.componentName);
const ignoreSemantics = parseIgnoreSemantics(propsString);
const singleOnly = parseSingleOnly(propsString);
return {
cn: filterSemantics(templateLocales.cn, {
ignoreSemantics,
singleOnly,
templatePath,
}),
en: filterSemantics(templateLocales.en, {
ignoreSemantics,
singleOnly,
templatePath,
}),
};
}
return null;
}
// 从 _semantic*.tsx 中提取语义信息
function extractSemanticInfo(semanticFile: string): {
cn: Record<string, string>;
en: Record<string, string>;
} | null {
try {
if (!fs.existsSync(semanticFile)) return null;
const content = fs.readFileSync(semanticFile, 'utf-8');
const localeInfo = extractLocaleInfoFromContent(content);
if (localeInfo) return localeInfo;
return extractSemanticInfoFromTemplate(semanticFile, content);
} catch (error) {
if (process.env.DEBUG) {
console.error(`Failed to extract semantic info from ${semanticFile}:`, error);
}
return null;
}
}
/**
* 生成语义名称列表的 markdown 格式
* @param semantics - 语义信息对象
* @returns markdown 格式的语义名称列表
*/
function generateSemanticParts(semantics: Record<string, string>): string {
const parts: string[] = [];
for (const [name, desc] of Object.entries(semantics)) {
// 将点号替换为连字符,匹配 DOM 中的实际 className 格式
const className = name.replace(/\./g, '-');
parts.push(`- ${name}\`semantic-mark-${className}\`: ${desc}`);
}
return parts.join('\n');
}
/**
* 生成使用案例代码模板
* @param componentName - 组件名(如 "button", "float-button"
* @param semanticSuffix - 语义文件后缀(如 "_group", "_input", "_search"
* @param semantics - 语义信息对象
* @returns 使用案例代码
*/
function generateUsageExample(
componentName: string,
semanticSuffix: string,
semantics: Record<string, string>,
): string {
// 将组件名转换为 PascalCase如 "float-button" -> "FloatButton"
const componentDisplayName = componentName
.split('-')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join('');
// 根据后缀生成组件名
let tagName = componentDisplayName;
if (semanticSuffix) {
// 移除开头的下划线,然后转换为 PascalCase
// 如 "_group" -> "Group", "_input" -> "Input", "_search" -> "Search"
const suffixParts = semanticSuffix
.replace(/^_/, '')
.split('_')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1));
const subComponentName = suffixParts.join('');
tagName = `${componentDisplayName}.${subComponentName}`;
}
// 生成 classNames 对象
const classNamesEntries: string[] = [];
for (const [name] of Object.entries(semantics)) {
// 将点号替换为连字符,匹配 DOM 中的实际 className 格式
const className = name.replace(/\./g, '-');
classNamesEntries.push(` ${name}: "semantic-mark-${className}"`);
}
return `<${tagName}
{...otherProps}
classNames={{
${classNamesEntries.join(',\n')}
}}
/>`;
}
/**
* 从测试快照文件中读取组件的 HTML snapshot并提取包含所有 semantic 元素的最小根元素
* 注意:此函数依赖 Jest 快照文件的路径与命名约定(含 snapshot key 规则)。
* 若测试目录结构或快照格式变化,文档构建可能失败或产生不完整结果,请同步调整此处逻辑。
* @param semanticFile - _semantic*.tsx 文件的绝对路径
* @param cwd - 项目根目录
* @returns HTML 字符串,失败返回 null
*/
function getComponentHTMLSnapshot(semanticFile: string, cwd: string): string | null {
try {
const relativePath = path.relative(cwd, semanticFile);
const pathMatch = relativePath.match(/^components\/([^/]+)\/demo\/([^/]+)\.tsx$/);
if (!pathMatch) return null;
const [, componentName, fileName] = pathMatch;
const snapshotPath = path.join(
cwd,
'components',
componentName,
'__tests__',
'__snapshots__',
'demo-semantic.test.tsx.snap',
);
if (!fs.existsSync(snapshotPath)) return null;
const snapshotContent = fs.readFileSync(snapshotPath, 'utf-8');
// 匹配快照 keyexports[`renders components/button/demo/_semantic.tsx correctly 1`] = `...`;
const snapshotKeyPattern = `components/${componentName}/demo/${fileName}.tsx correctly`;
const regex = new RegExp(
`exports\\[\\\`[^\\\`]*${snapshotKeyPattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[^\\\`]*\\\`\\]\\s*=\\s*\\\`([\\s\\S]*?)\\\`;`,
);
const snapshotMatch = snapshotContent.match(regex);
if (!snapshotMatch) return null;
let html = snapshotMatch[1].trim();
// 处理 JSON 格式的快照:{ type: 'demo', html: '...' }
if (html.startsWith('{') && html.includes('"html"')) {
try {
const parsed = JSON.parse(html);
if (parsed.html) {
html = parsed.html;
}
} catch {}
}
const { JSDOM } = require('jsdom');
const dom = new JSDOM(html);
const document = dom.window.document;
const semanticElements = document.querySelectorAll('[class*="semantic-"]');
if (semanticElements.length === 0) {
return html;
}
// 向上查找包含所有 semantic 元素的最小根元素(通常是组件根元素,如 button
const firstSemantic = semanticElements[0] as Element;
let rootElement: Element | null = firstSemantic;
while (rootElement && rootElement.parentElement) {
const parent: Element | null = rootElement.parentElement;
// 遇到布局容器时停止,避免包含 SemanticPreview 的外层容器
if (
!parent ||
parent.classList.contains('ant-row') ||
parent.classList.contains('ant-col') ||
parent.classList.contains('acss-') ||
parent === document.body ||
parent === document.documentElement
) {
break;
}
// 检查父元素是否包含所有 semantic 元素
let parentContainsAll = true;
for (let i = 0; i < semanticElements.length; i++) {
if (!parent.contains(semanticElements[i] as Element)) {
parentContainsAll = false;
break;
}
}
if (parentContainsAll) {
rootElement = parent;
} else {
break;
}
}
return rootElement ? rootElement.outerHTML : html;
} catch (error) {
if (process.env.DEBUG) {
console.warn(`[semantic-md] Failed to get HTML snapshot from ${semanticFile}:`, error);
}
return null;
}
}
/**
* 为每个组件生成 semantic*.md 文件
* @param api - Dumi API 实例
*/
function emitSemanticMd(api: IApi) {
if (process.env.NODE_ENV !== 'production') return;
if (SEMANTIC_MD_EMITTED) return;
SEMANTIC_MD_EMITTED = true;
const outRoot = api.paths.absOutputPath;
const cwd = api.paths.cwd;
COMPONENT_ROUTES.forEach(({ componentName, outputPath, absPath }) => {
try {
const componentDir = path.join(cwd, 'components', componentName);
const demoDir = path.join(componentDir, 'demo');
if (fs.existsSync(demoDir)) {
const demoFiles = fs.readdirSync(demoDir);
// 查找所有 _semantic*.tsx 文件(如 _semantic.tsx, _semantic_group.tsx
const semanticFiles = demoFiles.filter(
(demoFile) => demoFile.startsWith('_semantic') && demoFile.endsWith('.tsx'),
);
semanticFiles.forEach((semanticFile) => {
const semanticFilePath = path.join(demoDir, semanticFile);
const semanticInfo = extractSemanticInfo(semanticFilePath);
if (!semanticInfo) {
if (process.env.DEBUG) {
console.warn(
`[semantic-md] Failed to extract semantic info from ${semanticFilePath}`,
);
}
}
// 生成对应的 markdown 文件名_semantic.tsx -> semantic.md, _semantic_group.tsx -> semantic_group.md
const semanticSuffix = semanticFile.replace(/^_semantic/, '').replace(/\.tsx$/, '');
const semanticMdFileName = `semantic${semanticSuffix}.md`;
const semanticMdPath = path.join(outRoot, outputPath, semanticMdFileName);
const semanticMdDir = path.dirname(semanticMdPath);
if (!fs.existsSync(semanticMdDir)) {
fs.mkdirSync(semanticMdDir, { recursive: true });
}
// 根据路由路径判断语言(-cn 后缀表示中文)
const isZhCN = absPath.includes('-cn');
let semantics: Record<string, string> = {};
if (semanticInfo) {
semantics = isZhCN ? semanticInfo.cn : semanticInfo.en;
}
const displayName = componentName
.split('-')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join('');
const titleSuffix = semanticSuffix
? semanticSuffix
.split('_')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join('.')
: '';
const parts = [
`## ${displayName}${titleSuffix ? `${titleSuffix}` : ''}`,
'',
'### Semantic Parts',
'',
];
if (Object.keys(semantics).length > 0) {
parts.push(generateSemanticParts(semantics));
parts.push('');
parts.push(`### ${isZhCN ? '使用案例' : 'Usage Example'}`);
parts.push('');
parts.push('```tsx');
parts.push(generateUsageExample(componentName, semanticSuffix, semantics));
parts.push('```');
parts.push('');
parts.push('### Abstract DOM Structure');
parts.push('');
const htmlSnapshot = semanticInfo
? getComponentHTMLSnapshot(semanticFilePath, cwd)
: null;
if (htmlSnapshot) {
parts.push('```html');
parts.push(htmlSnapshot);
parts.push('```');
}
}
const content = `${parts.join('\n')}\n`;
fs.writeFileSync(semanticMdPath, content, 'utf-8');
});
}
} catch (e) {
api.logger.error(`Failed to generate semantic md for ${componentName}:`, e);
}
});
}
/**
* Semantic markdown 生成插件
* @param api - Dumi API 实例
*/
export default function semanticMdPlugin(api: IApi) {
// 收集组件路由信息(过滤出 /components/* 和 /components/*-cn 路由)
api.modifyRoutes((routes) => {
COMPONENT_ROUTES = Object.values(routes)
.filter((r) => typeof r?.absPath === 'string' && r.absPath && !r.absPath.includes(':'))
.filter((r) => {
const match = r.absPath.match(/^\/components\/([^/]+)\/?$/);
return !!match;
})
.map((r) => {
const match = r.absPath.match(/^\/components\/([^/]+)\/?$/);
const fullComponentName = match![1];
// 移除 -cn 后缀获取基础组件名
const baseComponentName = fullComponentName.replace(/-cn$/, '');
const outputPath = `components/${fullComponentName}`;
return {
absPath: r.absPath,
componentName: baseComponentName,
outputPath,
};
});
return routes;
});
// 在 HTML 文件导出阶段生成 semantic.md 文件
api.modifyExportHTMLFiles((files) => {
emitSemanticMd(api);
return files;
});
}

View File

@@ -0,0 +1,101 @@
import fs from 'fs';
import type { IApi } from 'dumi';
import ReactTechStack from 'dumi/dist/techStacks/react';
import tsToJs from '../utils/tsToJs';
import { dependencies, devDependencies } from '../../../package.json';
/**
* extends dumi internal tech stack, for customize previewer props
*/
class AntdReactTechStack extends ReactTechStack {
generatePreviewerProps(...[props, opts]: any) {
props.pkgDependencyList = { ...devDependencies, ...dependencies };
props.jsx ??= '';
if (opts.type === 'code-block') {
props.jsx = opts?.entryPointCode ? tsToJs(opts.entryPointCode) : '';
}
if (opts.type === 'external') {
// try to find md file with the same name as the demo tsx file
const locale = opts.mdAbsPath.match(/index\.([a-z-]+)\.md$/i)?.[1];
const mdPath = opts.fileAbsPath!.replace(/\.\w+$/, '.md');
const md = fs.existsSync(mdPath) ? fs.readFileSync(mdPath, 'utf-8') : '';
const codePath = opts.fileAbsPath!.replace(/\.\w+$/, '.tsx');
const code = fs.existsSync(codePath) ? fs.readFileSync(codePath, 'utf-8') : '';
props.jsx = tsToJs(code);
if (md) {
// extract description & css style from md file
const blocks: Record<string, string> = {};
const lines = md.split('\n');
let blockName = '';
let cacheList: string[] = [];
// Get block name
const getBlockName = (text: string) => {
if (text.startsWith('## ')) {
return text.replace('## ', '').trim();
}
if (text.startsWith('```css') || text.startsWith('<style>')) {
return 'style';
}
return null;
};
// Fill block content
const fillBlock = (name: string, lineList: string[]) => {
if (lineList.length) {
let fullText: string;
if (name === 'style') {
fullText = lineList
.join('\n')
.replace(/<\/?style>/g, '')
.replace(/```(\s*css)/g, '');
} else {
fullText = lineList.slice(1).join('\n');
}
blocks[name] = fullText;
}
};
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Mark as new block
const nextBlockName = getBlockName(line);
if (nextBlockName) {
fillBlock(blockName, cacheList);
// Next Block
blockName = nextBlockName;
cacheList = [line];
} else {
cacheList.push(line);
}
}
// Last block
fillBlock(blockName, cacheList);
props.description = blocks[locale];
props.style = blocks.style;
}
}
return props;
}
}
export default function techStackPlugin(api: IApi) {
api.registerTechStack(() => new AntdReactTechStack());
}

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { RightOutlined, YuqueOutlined, ZhihuOutlined } from '@ant-design/icons';
import { Button, Card, Divider } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { clsx } from 'clsx';
import useLocale from '../../../hooks/useLocale';
@@ -10,7 +10,7 @@ import JuejinIcon from '../../../theme/icons/JuejinIcon';
const ANTD_IMG_URL =
'https://picx.zhimg.com/v2-3b2bca09c2771e7a82a81562e806be4d.jpg?source=d16d100b';
const useStyle = createStyles(({ cssVar, css }) => ({
const styles = createStaticStyles(({ cssVar, css }) => ({
card: css`
width: 100%;
margin: calc(${cssVar.marginMD} * 2) 0;
@@ -113,19 +113,17 @@ interface Props {
const ColumnCard: React.FC<Props> = ({ zhihuLink, yuqueLink, juejinLink }) => {
const [locale] = useLocale(locales);
const {
styles: {
card,
bigTitle,
cardBody,
leftCard,
title,
subTitle,
logo,
arrowIcon,
zlBtn,
discussLogo,
},
} = useStyle();
card,
bigTitle,
cardBody,
leftCard,
title,
subTitle,
logo,
arrowIcon,
zlBtn,
discussLogo,
} = styles;
if (!zhihuLink && !yuqueLink && !juejinLink) {
return null;
}

View File

@@ -1,13 +1,13 @@
import React, { Suspense } from 'react';
import ContributorsList from '@qixian.cs/github-contributors-list';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { clsx } from 'clsx';
import { useIntl } from 'dumi';
import SiteContext from '../SiteContext';
import ContributorAvatar from './ContributorAvatar';
const useStyle = createStyles(({ cssVar, css }) => ({
const styles = createStaticStyles(({ cssVar, css }) => ({
listMobile: css`
margin: 1em 0 !important;
`,
@@ -48,7 +48,6 @@ const blockList = [
const Contributors: React.FC<ContributorsProps> = ({ filename }) => {
const { formatMessage } = useIntl();
const { styles } = useStyle();
const { isMobile } = React.use(SiteContext);
if (!filename) {

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import { removeCSS, updateCSS } from '@rc-component/util/lib/Dom/dynamicCSS';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import useLocale from '../../../hooks/useLocale';
@@ -20,7 +20,7 @@ const locales = {
},
};
const useStyle = createStyles(({ css, cssVar }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
position: fixed;
inset-inline-start: 0;
@@ -80,8 +80,6 @@ const InfoNewVersion: React.FC = () => {
removeCSS(whereCls);
}, []);
const { styles } = useStyle();
if (supportWhere) {
return null;
}

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { omit } from '@rc-component/util';
import { Button, Tooltip } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { clsx } from 'clsx';
export interface SwitchBtnProps {
@@ -18,7 +18,7 @@ export interface SwitchBtnProps {
const BASE_SIZE = '1.2em';
const useStyle = createStyles(({ cssVar, css }) => {
const styles = createStaticStyles(({ cssVar, css }) => {
return {
btn: css`
width: ${cssVar.controlHeight};
@@ -65,9 +65,7 @@ const useStyle = createStyles(({ cssVar, css }) => {
const SwitchBtn: React.FC<SwitchBtnProps> = (props) => {
const { label1, label2, tooltip1, tooltip2, value, pure, onClick, ...rest } = props;
const {
styles: { btn, innerDiv, labelStyle, label1Style, label2Style },
} = useStyle();
const { btn, innerDiv, labelStyle, label1Style, label2Style } = styles;
const node = (
<Button

View File

@@ -2,11 +2,14 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { GithubOutlined, MenuOutlined } from '@ant-design/icons';
import { Alert, Button, Col, ConfigProvider, Popover, Row, Select, Tooltip } from 'antd';
import { createStyles } from 'antd-style';
import type { DefaultOptionType } from 'antd/es/select';
import { clsx } from 'clsx';
import dayjs from 'dayjs';
import { useLocation, useSiteData } from 'dumi';
import DumiSearchBar from 'dumi/theme-default/slots/SearchBar';
import useSWR from 'swr';
import versionsFile from '../../../../public/versions.json';
import useLocale from '../../../hooks/useLocale';
import useLocalStorage from '../../../hooks/useLocalStorage';
import { getBannerData } from '../../../pages/index/components/util';
@@ -14,7 +17,6 @@ import ThemeSwitch from '../../common/ThemeSwitch';
import DirectionIcon from '../../icons/DirectionIcon';
import { ANT_DESIGN_NOT_SHOW_BANNER } from '../../layouts/GlobalLayout';
import * as utils from '../../utils';
import { getThemeConfig } from '../../utils';
import SiteContext from '../SiteContext';
import type { SharedProps } from './interface';
import Logo from './Logo';
@@ -132,7 +134,8 @@ const useStyle = createStyles(({ cssVar, token, css }) => {
}
`,
versionSelect: css`
min-width: 90px;
width: 112px;
min-width: 112px; // 这个宽度需要和 Empty 状态的宽度保持一致
.rc-virtual-list {
.rc-virtual-list-holder {
scrollbar-width: thin;
@@ -149,19 +152,56 @@ interface HeaderState {
searching: boolean;
}
interface VersionItem {
version: string;
url: string;
chineseMirrorUrl?: string;
}
const fetcher = (...args: Parameters<typeof fetch>) => {
return fetch(...args).then((res) => res.json());
};
// ================================= Header =================================
const Header: React.FC = () => {
const [, lang] = useLocale();
const { pkg } = useSiteData();
const themeConfig = getThemeConfig();
const isChineseMirror =
typeof window !== 'undefined' && typeof window.location !== 'undefined'
? window.location.hostname.includes('.antgroup.com')
: false;
const { data: versions = [], isLoading } = useSWR<VersionItem[]>(
process.env.NODE_ENV === 'production' && typeof window !== 'undefined'
? `${window.location.origin}/versions.json`
: null,
fetcher,
{
fallbackData: versionsFile,
errorRetryCount: 3,
},
);
const versionOptions = useMemo(() => {
if (isLoading) {
return [];
}
return versions.map<DefaultOptionType>((item) => {
const isMatch = item.version.startsWith(pkg.version[0]);
const label = isMatch ? pkg.version : item.version;
const value = isChineseMirror && item.chineseMirrorUrl ? item.chineseMirrorUrl : item.url;
return { value, label };
});
}, [versions, isLoading, pkg.version, isChineseMirror]);
const [headerState, setHeaderState] = useState<HeaderState>({
menuVisible: false,
windowWidth: 1400,
searching: false,
});
const { direction, isMobile, bannerVisible, updateSiteConfig } = React.use(SiteContext);
const pingTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const location = useLocation();
@@ -258,14 +298,6 @@ const Header: React.FC = () => {
);
const { menuVisible, windowWidth, searching } = headerState;
const docVersions: Record<string, string> = {
[pkg.version]: pkg.version,
...themeConfig?.docVersions,
};
const versionOptions = Object.keys(docVersions).map((version) => ({
value: docVersions[version],
label: version,
}));
const isHome = ['', 'index', 'index-cn'].includes(pathname);
const isZhCN = lang === 'cn';
@@ -308,6 +340,7 @@ const Header: React.FC = () => {
key="version"
size="small"
variant="filled"
loading={isLoading}
className={styles.versionSelect}
defaultValue={pkg.version}
onChange={handleVersionChange}

View File

@@ -1,6 +1,3 @@
const chineseMirror =
typeof location !== 'undefined' && location.hostname.includes('.antgroup.com');
export default {
categoryOrder: {
'Ant Design': 0,
@@ -45,15 +42,4 @@ export default {
模板文档: 3,
'Template Document': 3,
},
docVersions: {
'5.x': chineseMirror ? 'https://5x-ant-design.antgroup.com' : 'https://5x.ant.design',
'4.x': chineseMirror ? 'https://4x-ant-design.antgroup.com' : 'https://4x.ant.design',
'3.x': 'https://3x.ant.design',
'2.x': 'https://2x.ant.design',
'1.x': 'https://1x.ant.design',
'0.12.x': 'https://012x.ant.design',
'0.11.x': 'https://011x.ant.design',
'0.10.x': 'https://010x.ant.design',
'0.9.x': 'https://09x.ant.design',
},
};

View File

@@ -3,7 +3,6 @@ import flattenDeep from 'lodash/flattenDeep';
import semver from 'semver';
import deprecatedVersions from '../../../BUG_VERSIONS.json';
import themeConfig from '../themeConfig';
interface Meta {
skip?: boolean;
@@ -206,4 +205,12 @@ export function matchDeprecated(v: string): MatchDeprecatedResult {
};
}
export const getThemeConfig = () => themeConfig;
/**
* Determine if a hostname is an official domain.
* antd creates a temporary preview site for each PR for convenient preview and testing.
* Usually on platforms like surge.sh or Cloudflare Pages.
*/
export function isOfficialHost(hostname: string) {
const officialHostnames = ['ant.design', 'antgroup.com'];
return officialHostnames.some((official) => hostname.includes(official));
}

View File

@@ -160,18 +160,27 @@ export default defineConfig({
}
// 首页无视链接里面的语言设置 https://github.com/ant-design/ant-design/issues/4552
if (pathname === '/' || pathname === '/index-cn') {
const lang =
(window.localStorage && localStorage.getItem('locale')) ||
((navigator.language || navigator.browserLanguage).toLowerCase() === 'zh-cn'
const normalizedPathname = pathname || '/';
if (normalizedPathname === '/' || normalizedPathname === '/index-cn') {
let lang;
if (window.localStorage) {
const antLocale = localStorage.getItem('ANT_LOCAL_TYPE_KEY');
// 尝试解析 JSON因为可能是被序列化后存储的 "en-US" / en-US https://github.com/ant-design/ant-design/issues/56606
try {
lang = antLocale ? JSON.parse(antLocale) : localStorage.getItem('locale');
} catch (e) {
lang = antLocale ? antLocale : localStorage.getItem('locale');
}
}
lang = lang || ((navigator.language || navigator.browserLanguage).toLowerCase() === 'zh-cn'
? 'zh-CN'
: 'en-US');
// safari is 'zh-cn', while other browser is 'zh-CN';
if ((lang === 'zh-CN') !== isZhCN(pathname)) {
location.pathname = getLocalizedPathname(pathname, lang === 'zh-CN');
if ((lang === 'zh-CN') !== isZhCN(normalizedPathname)) {
location.pathname = getLocalizedPathname(normalizedPathname, lang === 'zh-CN');
}
}
document.documentElement.className += isZhCN(pathname) ? 'zh-cn' : 'en-us';
document.documentElement.className += isZhCN(normalizedPathname) ? 'zh-cn' : 'en-us';
})();
`,
],

View File

@@ -11,7 +11,7 @@ jobs:
discussion-create:
permissions:
contents: read # for visiky/dingtalk-release-notify to get latest release
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: send to dingtalk
uses: visiky/dingtalk-release-notify@main

View File

@@ -12,7 +12,7 @@ jobs:
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: check-inactive
uses: actions-cool/issues-helper@v3

View File

@@ -12,7 +12,7 @@ jobs:
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: need reproduce
uses: actions-cool/issues-helper@v3

View File

@@ -9,7 +9,7 @@ permissions:
jobs:
reminder_job:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Send reminders for inactive issues
uses: actions/github-script@v8

View File

@@ -14,7 +14,7 @@ jobs:
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: help wanted
if: github.event.label.name == 'help wanted'

View File

@@ -13,7 +13,7 @@ jobs:
contents: read # for visiky/dingtalk-release-notify to get latest release
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- uses: actions-cool/check-user-permission@v2
id: checkUser

View File

@@ -14,7 +14,7 @@ jobs:
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: remove inactive
if: github.event.issue.state == 'open' && github.actor == github.event.issue.user.login

View File

@@ -6,7 +6,7 @@ on:
jobs:
send-message:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Send Unconfirmed Issues to DingTalk
uses: actions/github-script@v8

View File

@@ -14,7 +14,7 @@ concurrency:
jobs:
pr-check-ci:
if: github.repository == 'ant-design/ant-design'
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
name: Build Project
steps:
- name: checkout

View File

@@ -3,7 +3,7 @@ on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout code

View File

@@ -16,7 +16,7 @@ jobs:
contents: write # for actions-cool/check-pr-ci to merge PRs
issues: write # for actions-cool/check-pr-ci to update issues
pull-requests: write # for actions-cool/check-pr-ci to update PRs
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- uses: actions-cool/check-pr-ci@v1
with:

View File

@@ -12,10 +12,10 @@ jobs:
permissions:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
if: (github.event.pull_request.head.ref == 'next' || github.event.pull_request.head.ref == 'feature' || github.event.pull_request.head.ref == 'master') && github.event.pull_request.head.user.login == 'ant-design'
steps:
- uses: actions-cool/issues-helper@d1d51fccf39469b5458203b1369060db0ff0c0db
- uses: actions-cool/issues-helper@e2ff99831a4f13625d35064e2b3dfe65c07a0396
with:
actions: create-comment
issue-number: ${{ github.event.number }}

View File

@@ -17,7 +17,7 @@ jobs:
issues: write # for actions-cool/maintain-one-comment to modify or create issue comments
pull-requests: write # for actions-cool/maintain-one-comment to modify or create PR comments
if: github.event.pull_request.merged == true && github.repository == 'ant-design/ant-design'
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: get commit count
id: get_commit_count

View File

@@ -12,7 +12,7 @@ jobs:
permissions:
issues: write # for actions-cool/pr-welcome to create, update & react on issues
pull-requests: write # for actions-cool/pr-welcome to request reviewer
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- uses: actions-cool/pr-welcome@4bd317d60ef3b40a3ccda39c22f66c3358010f92
with:
@@ -30,7 +30,7 @@ jobs:
check-changelog:
permissions:
pull-requests: write # for actions-cool/pr-check-fill to create or update PR comments
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: check fill
uses: actions-cool/pr-check-fill@35194e32fd717c88c4fde15fbde9005933c2d452

View File

@@ -9,7 +9,7 @@ permissions:
jobs:
send-to-dingtalk:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: send to dingtalk
uses: visiky/dingtalk-release-notify@64fcb0373782b6c2f6d9b9ea3c68af80ca189585

View File

@@ -14,7 +14,7 @@ permissions:
jobs:
upstream-workflow-summary:
name: upstream workflow summary
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
if: github.event.workflow_run.event == 'pull_request'
outputs:
jobs: ${{ steps.prep-summary.outputs.result }}
@@ -57,13 +57,13 @@ jobs:
actions: read # for dawidd6/action-download-artifact to query and download artifacts
issues: write # for actions-cool/maintain-one-comment to modify or create issue comments
pull-requests: write # for actions-cool/maintain-one-comment to modify or create PR comments
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: upstream-workflow-summary
if: github.event.workflow_run.event == 'pull_request'
steps:
# We need get PR id first
- name: download pr artifact
uses: dawidd6/action-download-artifact@v11
uses: dawidd6/action-download-artifact@v14
with:
workflow: ${{ github.event.workflow_run.workflow_id }}
run_id: ${{ github.event.workflow_run.id }}
@@ -83,7 +83,7 @@ jobs:
# Download site artifact
- name: download site artifact
if: ${{ fromJSON(needs.upstream-workflow-summary.outputs.build-success) }}
uses: dawidd6/action-download-artifact@v11
uses: dawidd6/action-download-artifact@v14
with:
workflow: ${{ github.event.workflow_run.workflow_id }}
run_id: ${{ github.event.workflow_run.id }}

View File

@@ -19,7 +19,7 @@ jobs:
issues: write # for actions-cool/maintain-one-comment to modify or create issue comments
pull-requests: write # for actions-cool/maintain-one-comment to modify or create PR comments
name: start preview info
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: update status comment
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b

View File

@@ -18,7 +18,7 @@ jobs:
permissions:
contents: write # for actions-cool/release-helper to create releases
if: github.event.ref_type == 'tag'
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Send to Ant Design DingGroup
uses: actions-cool/release-helper@v2

View File

@@ -5,7 +5,7 @@ on:
jobs:
tweet:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
if: ${{ github.event.ref_type == 'tag' && !contains(github.event.ref, 'alpha') }}
steps:
- name: Tweet

View File

@@ -57,7 +57,7 @@ jobs:
run: echo "VERSION=$(echo ${{ github.ref_name }} | sed 's/\./-/g')" >> $GITHUB_OUTPUT
deploy-to-pages:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: build-site
steps:
- uses: utooland/setup-utoo@v1
@@ -101,7 +101,7 @@ jobs:
# https://github.com/ant-design/ant-design/pull/49213/files#r1625446496
upload-to-release:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
# 仅在 tag 的时候工作,因为我们要将内容发布到以 tag 为版本号的 release 里
if: startsWith(github.ref, 'refs/tags/')
needs: build-site

View File

@@ -13,7 +13,7 @@ jobs:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v6

Some files were not shown because too many files have changed in this diff Show More