diff --git a/.dumirc.ts b/.dumirc.ts index 81392343..f0dcfae4 100644 --- a/.dumirc.ts +++ b/.dumirc.ts @@ -1,7 +1,13 @@ import { defineConfig } from 'dumi'; import path from 'path'; +const basePath = process.env.GH_PAGES ? '/cascader/' : '/'; +const publicPath = basePath; + export default defineConfig({ + outputPath: 'docs-dist', + base: basePath, + publicPath, alias: { 'rc-cascader$': path.resolve('src'), 'rc-cascader/es': path.resolve('src'), diff --git a/.fatherrc.ts b/.fatherrc.ts new file mode 100644 index 00000000..96268ae1 --- /dev/null +++ b/.fatherrc.ts @@ -0,0 +1,5 @@ +import { defineConfig } from 'father'; + +export default defineConfig({ + plugins: ['@rc-component/father-plugin'], +}); diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 33b1999c..758659af 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,15 +1,2 @@ -# These are supported funding model platforms - -github: ant-design # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: ant-design # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry -polar: # Replace with a single Polar username -buy_me_a_coffee: # Replace with a single Buy Me a Coffee username -thanks_dev: # Replace with a single thanks.dev username -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] +github: ant-design +open_collective: ant-design diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a5cb97ff..3b730ef9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,37 +1,19 @@ version: 2 updates: -- package-ecosystem: npm - directory: "/" - schedule: - interval: daily - time: "21:00" - open-pull-requests-limit: 10 - ignore: - - dependency-name: np - versions: - - 7.2.0 - - 7.3.0 - - 7.4.0 - - dependency-name: "@types/react" - versions: - - 17.0.0 - - 17.0.1 - - 17.0.2 - - 17.0.3 - - dependency-name: "@types/jest" - versions: - - 26.0.20 - - 26.0.21 - - 26.0.22 - - dependency-name: "@types/react-dom" - versions: - - 17.0.0 - - 17.0.1 - - 17.0.2 - - dependency-name: typescript - versions: - - 4.1.3 - - 4.1.4 - - 4.1.5 - - 4.2.2 - - 4.2.3 + - package-ecosystem: npm + directory: '/' + schedule: + interval: weekly + day: monday + time: '21:00' + timezone: Asia/Shanghai + open-pull-requests-limit: 10 + + - package-ecosystem: github-actions + directory: '/' + schedule: + interval: weekly + day: monday + time: '21:00' + timezone: Asia/Shanghai + open-pull-requests-limit: 10 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9947b182..65d90eac 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,18 +24,20 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v7 + with: + persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e with: languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e with: category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1fe5da5f..8d135ed5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,9 @@ name: ✅ test on: [push, pull_request] +permissions: + contents: read jobs: test: - uses: react-component/rc-test/.github/workflows/test-npm.yml@main - secrets: inherit \ No newline at end of file + uses: react-component/rc-test/.github/workflows/test-utoo.yml@main + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/react-doctor.yml b/.github/workflows/react-doctor.yml new file mode 100644 index 00000000..01760580 --- /dev/null +++ b/.github/workflows/react-doctor.yml @@ -0,0 +1,26 @@ +name: React Doctor +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + push: + branches: [master] + +permissions: + contents: read + pull-requests: write + issues: write + statuses: write + +concurrency: + group: react-doctor-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + react-doctor: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + persist-credentials: false + - uses: millionco/react-doctor@0b4f4f4bd248a154e64eb508a48347f71154b3f3 diff --git a/.github/workflows/surge-preview.yml b/.github/workflows/surge-preview.yml new file mode 100644 index 00000000..394f2b02 --- /dev/null +++ b/.github/workflows/surge-preview.yml @@ -0,0 +1,48 @@ +name: Surge Preview + +on: + pull_request: + +permissions: + contents: read + pull-requests: write + checks: write + +jobs: + preview: + runs-on: ubuntu-latest + concurrency: + group: surge-preview-${{ github.event.pull_request.number }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - name: Check Surge token + id: surge-token + env: + SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }} + run: | + if [ -n "$SURGE_TOKEN" ]; then + echo "enabled=true" >> "$GITHUB_OUTPUT" + else + echo "enabled=false" >> "$GITHUB_OUTPUT" + fi + - name: Build preview + if: ${{ steps.surge-token.outputs.enabled == 'true' }} + run: | + npm install + npm run build + - uses: afc163/surge-preview@bf90a5a86111f6311ca42f0a5a0f80fb0fb03cec + if: ${{ steps.surge-token.outputs.enabled == 'true' }} + env: + SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }} + with: + surge_token: ${{ env.SURGE_TOKEN }} + github_token: ${{ secrets.GITHUB_TOKEN }} + dist: docs-dist + failOnError: false + setCommitStatus: false + - name: Skip Surge preview + if: ${{ steps.surge-token.outputs.enabled != 'true' }} + run: echo "SURGE_TOKEN is not configured; skip Surge preview." diff --git a/.gitignore b/.gitignore index 2565182b..cbe08662 100644 --- a/.gitignore +++ b/.gitignore @@ -38,7 +38,8 @@ coverage # dumi .dumi/tmp .dumi/tmp-production +docs-dist dist .vscode -bun.lockb \ No newline at end of file +bun.lockb diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..2312dc58 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..c466d872 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,14 @@ +node_modules +coverage +docs-dist +dist +es +lib +.dumi/tmp +.dumi/tmp-production +.vercel +package-lock.json +pnpm-lock.yaml +yarn.lock +bun.lockb +*.log diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..bd0a1f72 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019-present react-component + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index b4663546..bd393a23 100644 --- a/README.md +++ b/README.md @@ -1,319 +1,166 @@ -# @rc-component/cascader - -React Cascader Component. - -[![NPM version][npm-image]][npm-url] -[![npm download][download-image]][download-url] -[![build status][github-actions-image]][github-actions-url] -[![Codecov][codecov-image]][codecov-url] -[![bundle size][bundlephobia-image]][bundlephobia-url] -[![dumi][dumi-image]][dumi-url] - -[npm-image]: https://img.shields.io/npm/v/@rc-component/cascader.svg?style=flat-square -[npm-url]: https://npmjs.org/package/@rc-component/cascader -[travis-image]: https://img.shields.io/travis/react-component/cascader/master?style=flat-square -[travis-url]: https://travis-ci.com/react-component/cascader -[github-actions-image]: https://github.com/react-component/cascader/actions/workflows/main.yml/badge.svg -[github-actions-url]: https://github.com/react-component/cascader/actions/workflows/main.yml -[codecov-image]: https://img.shields.io/codecov/c/github/react-component/cascader/master.svg?style=flat-square -[codecov-url]: https://app.codecov.io/gh/react-component/cascader -[david-url]: https://david-dm.org/react-component/cascader -[david-image]: https://david-dm.org/react-component/cascader/status.svg?style=flat-square -[david-dev-url]: https://david-dm.org/react-component/cascader?type=dev -[david-dev-image]: https://david-dm.org/react-component/cascader/dev-status.svg?style=flat-square -[download-image]: https://img.shields.io/npm/dm/@rc-component/cascader.svg?style=flat-square -[download-url]: https://npmjs.org/package/@rc-component/cascader -[bundlephobia-url]: https://bundlephobia.com/package/@rc-component/cascader -[bundlephobia-image]: https://badgen.net/bundlephobia/minzip/@rc-component/cascader -[dumi-url]: https://github.com/umijs/dumi -[dumi-image]: https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square - -## Browser Support - -| [IE / Edge](http://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | [Electron](http://godban.github.io/browsers-support-badges/)
Electron | -| --- | --- | --- | --- | --- | -| IE11, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions | - -## Screenshots - - - -## Example - -https://cascader-react-component.vercel.app +
+

@rc-component/cascader

+

Ant Design Part of the Ant Design ecosystem.

+

🧭 React Cascader component for selecting values from hierarchical option trees, with search, multiple selection, async loading, and custom rendering.

+ +

+ NPM version + npm downloads + build status + Codecov + bundle size + dumi +

+
+ +

English | 简体中文

+ + +## Highlights + +| Area | Support | +| --------- | ---------------------------------------------------------------- | +| Data | Nested options, field name mapping, disabled nodes | +| Selection | Single, multiple, checkable, and change-on-select flows | +| Search | Controlled search, custom filter, custom sort, custom render | +| Loading | Async option loading with `loadData` | +| Rendering | Custom option label, dropdown content, icons, and expand trigger | ## Install -[![@rc-component/cascader](https://nodei.co/npm/@rc-component/cascader.png)](https://npmjs.org/package/@rc-component/cascader) - ```bash -$ npm install @rc-component/cascader --save +npm install @rc-component/cascader ``` ## Usage -```js -import React from 'react'; +```tsx | pure import Cascader from '@rc-component/cascader'; -const options = [{ - 'label': '福建', - 'value': 'fj', - 'children': [{ - 'label': '福州', - 'value': 'fuzhou', - 'children': [{ - 'label': '马尾', - 'value': 'mawei', - }], - }, { - 'label': '泉州', - 'value': 'quanzhou', - }], -}, { - 'label': '浙江', - 'value': 'zj', - 'children': [{ - 'label': '杭州', - 'value': 'hangzhou', - 'children': [{ - 'label': '余杭', - 'value': 'yuhang', - }], - }], -}, { - 'label': '北京', - 'value': 'bj', - 'children': [{ - 'label': '朝阳区', - 'value': 'chaoyang', - }, { - 'label': '海淀区', - 'value': 'haidian', - }], -}]; - -React.render( +const options = [ + { + label: 'Zhejiang', + value: 'zhejiang', + children: [ + { + label: 'Hangzhou', + value: 'hangzhou', + children: [{ label: 'Xihu', value: 'xihu' }], + }, + ], + }, + { + label: 'Jiangsu', + value: 'jiangsu', + children: [{ label: 'Nanjing', value: 'nanjing' }], + }, +]; + +export default () => ( - ... + -, container); +); +``` + +## Examples + +Run the local dumi site: + +```bash +npm install +npm start ``` +Then open `http://localhost:8000`. + ## API -### props - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
nametypedefaultdescription
optionsObjectThe data options of cascade
valueArrayselected value
defaultValueArrayinitial selected value
onChangeFunction(value, selectedOptions)callback when finishing cascader select
changeOnSelectBooleanfalsechange value on each selection
loadDataFunction(selectedOptions)callback when click any option, use for loading more options
expandTriggerString'click'expand current item when click or hover
openBooleanvisibility of popup overlay
onPopupVisibleChangeFunction(visible)callback when popup overlay's visibility changed
transitionNameStringtransition className like "slide-up"
prefixClsStringrc-cascaderprefix className of popup overlay
popupClassNameStringadditional className of popup overlay
popupPlacementStringbottomLeftuse preset popup align config from builtinPlacements:bottomRight topRight bottomLeft topLeft
getPopupContainerfunction(trigger:Node):Node() => document.bodycontainer which popup select menu rendered into
dropdownMenuColumnStyleObjectstyle object for each cascader pop menu
fieldNamesObject{ label: 'label', value: 'value', children: 'children' }custom field name for label and value and children
expandIconReactNode>specific the default expand icon
loadingIconReactNode>specific the default loading icon
hidePopupOnSelectBoolean>truehide popup on select
showSearchboolean | objectfalseWhether show search input in single mode
- -### showSearch - -| Property | Description | Type | Default | Version | -| --- | --- | --- | --- | --- | -| autoClearSearchValue | Whether the current search will be cleared on selecting an item. Only applies when checkable| boolean | true | -| filter | The function will receive two arguments, inputValue and option, if the function returns true, the option will be included in the filtered set; Otherwise, it will be excluded | function(inputValue, path): boolean | - | | -| limit | Set the count of filtered items | number \| false | 50 | | -| matchInputWidth | Whether the width of list matches input, ([how it looks](https://github.com/ant-design/ant-design/issues/25779)) | boolean | true | | -| render | Used to render filtered options | function(inputValue, path): ReactNode | - | | -| sort | Used to sort filtered options | function(a, b, inputValue) | - | | -| searchValue | The current input "search" text | string | - | - | -| onSearch | called when input changed | function | - | - | - -### option - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
nametypedefaultdescription
labelStringoption text to display
valueStringoption value as react key
disabledBooleandisabled option
childrenArraychildren options
+### Cascader + +| Property | Type | Default | Description | +| -------------------- | ------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------- | +| autoClearSearchValue | boolean | true | Deprecated. Use `showSearch.autoClearSearchValue` instead. | +| builtinPlacements | BuildInPlacements | - | Custom popup placements. | +| changeOnSelect | boolean | false | Trigger `onChange` when selecting each level. | +| checkable | boolean \| ReactNode | false | Enable multiple selection with checkbox UI. | +| children | ReactElement | - | Trigger element. | +| classNames | Semantic class name map | - | Semantic class names for selector and popup elements. | +| defaultValue | string[] \| number[] \| Array | - | Initial selected value. | +| displayRender | `(label, selectedOptions) => ReactNode` | - | Render selected labels. | +| expandIcon | ReactNode | `>` | Custom expand icon. | +| expandTrigger | `click` \| `hover` | `click` | Trigger action for expanding the next option level. | +| fieldNames | `{ label?: string; value?: string; children?: string }` | `{ label: 'label', value: 'value', children: 'children' }` | Custom option field names. | +| loadData | `(selectedOptions) => void` | - | Load child options asynchronously. | +| loadingIcon | ReactNode | - | Custom loading icon. | +| onChange | `(value, selectedOptions) => void` | - | Called when selection changes. | +| onPopupVisibleChange | `(open: boolean) => void` | - | Called when popup visibility changes. | +| onSearch | `(value: string) => void` | - | Deprecated. Use `showSearch.onSearch` instead. | +| open | boolean | - | Controlled popup visibility. | +| optionRender | `(option) => ReactNode` | - | Custom option renderer. | +| options | Option[] | - | Hierarchical option data. | +| placement | Select placement | - | Popup placement. | +| popupClassName | string | - | Popup class name. | +| popupMenuColumnStyle | CSSProperties | - | Style for each popup menu column. | +| prefixCls | string | `rc-cascader` | Class name prefix. | +| searchValue | string | - | Deprecated. Use `showSearch.searchValue` instead. | +| showCheckedStrategy | `SHOW_PARENT` \| `SHOW_CHILD` | `SHOW_PARENT` | Strategy for rendering checked values in multiple mode. | +| showSearch | boolean \| SearchConfig | false | Enable and configure search. | +| styles | Semantic style map | - | Semantic styles for selector and popup elements. | +| value | string[] \| number[] \| Array | - | Controlled selected value. | + +`Cascader` also accepts public props from `@rc-component/select` `BaseSelect`, except private select-only props such as `mode`, `labelInValue`, `showSearch`, and `tokenSeparators`. + +### SearchConfig + +| Property | Type | Default | Description | +| -------------------- | -------------------------------------------------------- | ------- | ---------------------------------------------------------- | +| autoClearSearchValue | boolean | true | Clear search text after selecting an item. | +| filter | `(inputValue, options, fieldNames) => boolean` | - | Return `true` to include an option path in search results. | +| limit | number \| false | 50 | Limit the number of filtered items. | +| matchInputWidth | boolean | true | Whether the search result width matches the input width. | +| onSearch | `(value: string) => void` | - | Called when search text changes. | +| render | `(inputValue, path, prefixCls, fieldNames) => ReactNode` | - | Render a filtered option path. | +| searchValue | string | - | Controlled search text. | +| sort | `(a, b, inputValue, fieldNames) => number` | - | Sort filtered option paths. | + +### Option + +| Property | Type | Default | Description | +| --------------- | ------------------------ | ------- | ------------------------------------------------ | +| children | Option[] | - | Child options. | +| disabled | boolean | false | Disable this option. | +| disableCheckbox | boolean | false | Disable this option's checkbox in multiple mode. | +| label | ReactNode | - | Display label. | +| value | string \| number \| null | - | Option value. | ## Development ```bash -$ npm install -$ npm start +npm install +npm start ``` -## Test Case +The dumi site runs at `http://localhost:8000` by default. + +Common commands: ```bash -$ npm test +npm run lint +npm test +npm run tsc +npm run lint:tsc +npm run compile ``` -## Coverage +## Release ```bash -$ npm run coverage +npm run prepublishOnly ``` -## License +The release flow is handled by `@rc-component/np` through the `rc-np` command after the package build. -@rc-component/cascader is released under the MIT license. - -## 🤝 Contributing +## License - - Contribution Leaderboard - +@rc-component/cascader is released under the [MIT](./LICENSE) license. diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 00000000..c94208fc --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,166 @@ +
+

@rc-component/cascader

+

Ant Design Ant Design 生态的一部分。

+

🧭 React 级联选择组件,支持层级选项、搜索、多选、异步加载和自定义渲染。

+ +

+ NPM version + npm downloads + build status + Codecov + bundle size + dumi +

+
+ +

English | 简体中文

+ + +## 特性 + +| 范围 | 支持 | +| --------- | ---------------------------------------------------------------- | +| 数据 | 嵌套选项、字段名映射和禁用节点 | +| 选择 | 单选、多选、勾选和逐级选择流程 | +| 搜索 | 受控搜索、自定义过滤、自定义排序和自定义渲染 | +| 加载 | 通过 `loadData` 异步加载选项 | +| 渲染 | 自定义选项标签、下拉内容、图标和展开触发方式 | + +## 安装 + +```bash +npm install @rc-component/cascader +``` + +## 使用 + +```tsx | pure +import Cascader from '@rc-component/cascader'; + +const options = [ + { + label: 'Zhejiang', + value: 'zhejiang', + children: [ + { + label: 'Hangzhou', + value: 'hangzhou', + children: [{ label: 'Xihu', value: 'xihu' }], + }, + ], + }, + { + label: 'Jiangsu', + value: 'jiangsu', + children: [{ label: 'Nanjing', value: 'nanjing' }], + }, +]; + +export default () => ( + + + +); +``` + +## 示例 + +运行本地 dumi 站点: + +```bash +npm install +npm start +``` + +然后打开 `http://localhost:8000`。 + +## API + +### Cascader + +| 参数 | 类型 | 默认值 | 说明 | +| -------------------- | ------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------- | +| autoClearSearchValue | boolean | true | 已弃用。请改用 `showSearch.autoClearSearchValue`。 | +| builtinPlacements | BuildInPlacements | - | 自定义弹层位置。 | +| changeOnSelect | boolean | false | 选择每个级别时触发 `onChange`。 | +| checkable | boolean \| ReactNode | false | 启用带复选框 UI 的多选。 | +| children | ReactElement | - | 触发元素。 | +| classNames | Semantic class name map | - | 选择器和弹层元素的语义 className。 | +| defaultValue | string[] \| number[] \| Array | - | 初始选中值。 | +| displayRender | `(label, selectedOptions) => ReactNode` | - | 渲染选定的标签。 | +| expandIcon | ReactNode | `>` | 自定义展开图标。 | +| expandTrigger | `click` \| `hover` | `click` | 触发扩展下一个选项级别的操作。 | +| fieldNames | `{ label?: string; value?: string; children?: string }` | `{ label: 'label', value: 'value', children: 'children' }` | 自定义选项字段名称。 | +| loadData | `(selectedOptions) => void` | - | 异步加载子选项。 | +| loadingIcon | ReactNode | - | 自定义加载图标。 | +| onChange | `(value, selectedOptions) => void` | - | 选择更改时调用。 | +| onPopupVisibleChange | `(open: boolean) => void` | - | 当弹层窗口可见性发生变化时调用。 | +| onSearch | `(value: string) => void` | - | 已弃用。请改用 `showSearch.onSearch`。 | +| open | boolean | - | 受控的弹层窗口可见性。 | +| optionRender | `(option) => ReactNode` | - | 自定义选项渲染器。 | +| options | Option[] | - | 分层选项数据。 | +| placement | Select placement | - | 弹层窗口放置。 | +| popupClassName | string | - | 弹层 className。 | +| popupMenuColumnStyle | CSSProperties | - | 每个弹出菜单列的样式。 | +| prefixCls | string | `rc-cascader` | className 前缀。 | +| searchValue | string | - | 已弃用。请改用 `showSearch.searchValue`。 | +| showCheckedStrategy | `SHOW_PARENT` \| `SHOW_CHILD` | `SHOW_PARENT` | 在多种模式下渲染检查值的策略。 | +| showSearch | boolean \| SearchConfig | false | 启用并配置搜索。 | +| styles | Semantic style map | - | 选择器和弹层元素的语义样式。 | +| value | string[] \| number[] \| Array | - | 受控选中值。 | + +`Cascader` 还接受来自 `@rc-component/select` `BaseSelect` 的公共属性,但私有仅选择属性除外,例如 `mode` 、 `labelInValue` 、 `showSearch` 和 `tokenSeparators`。 + +### SearchConfig + +| 参数 | 类型 | 默认值 | 说明 | +| -------------------- | -------------------------------------------------------- | ------- | ---------------------------------------------------------- | +| autoClearSearchValue | boolean | true | 选择项目后清除搜索文本。 | +| filter | `(inputValue, options, fieldNames) => boolean` | - | 返回 `true` 以在搜索结果中包含选项路径。 | +| limit | number \| false | 50 | 限制过滤项目的数量。 | +| matchInputWidth | boolean | true | 搜索结果宽度是否与输入宽度匹配。 | +| onSearch | `(value: string) => void` | - | 当搜索文本更改时调用。 | +| render | `(inputValue, path, prefixCls, fieldNames) => ReactNode` | - | 渲染过滤后的选项路径。 | +| searchValue | string | - | 受控搜索文本。 | +| sort | `(a, b, inputValue, fieldNames) => number` | - | 对过滤后的选项路径进行排序。 | + +### 选项 + +| 参数 | 类型 | 默认值 | 说明 | +| --------------- | ------------------------ | ------- | ------------------------------------------------ | +| children | Option[] | - | 子选项。 | +| disabled | boolean | false | 禁用此选项。 | +| disableCheckbox | boolean | false | 在多种模式下禁用此选项的复选框。 | +| label | ReactNode | - | 显示标签。 | +| value | string \| number \| null | - | 选项值。 | + +## 本地开发 + +```bash +npm install +npm start +``` + +dumi 站点默认运行在 `http://localhost:8000`。 + +Common commands: + +```bash +npm run lint +npm test +npm run tsc +npm run lint:tsc +npm run compile +``` + +## 发布 + +```bash +npm run prepublishOnly +``` + +包构建完成后,发布流程由 `@rc-component/np` 通过 `rc-np` 命令处理。 + +## 许可证 + +@rc-component/cascader 基于 [MIT](./LICENSE) 许可证发布。 diff --git a/package.json b/package.json index d1d1fb05..5b6f0ea2 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "cascade", "cascader" ], - "homepage": "https://github.com/react-component/cascader", + "homepage": "https://react-component.github.io/cascader", "bugs": { "url": "https://github.com/react-component/cascader/issues" }, @@ -33,14 +33,15 @@ "build": "dumi build", "compile": "father build", "coverage": "father test --coverage", - "tsc": "bunx tsc --noEmit", - "deploy": "UMI_ENV=gh npm run build && gh-pages -d dist", - "lint": "eslint src/ examples/ tests/ --ext .tsx,.ts,.jsx,.jsx", - "now-build": "npm run build", + "tsc": "tsc --noEmit", + "deploy": "UMI_ENV=gh npm run build && gh-pages -d docs-dist", + "lint": "eslint src/ examples/ tests/ --ext .tsx,.ts,.jsx,.js", "prepublishOnly": "npm run compile && rc-np", + "prettier": "prettier --write --ignore-unknown .", "lint:tsc": "tsc -p tsconfig.json --noEmit", "start": "dumi dev", - "test": "rc-test" + "test": "rc-test", + "prepare": "husky" }, "dependencies": { "@rc-component/select": "~1.8.0", @@ -51,35 +52,46 @@ "devDependencies": { "@rc-component/father-plugin": "^2.2.0", "@rc-component/form": "^1.4.0", - "@rc-component/np": "^1.0.3", + "@rc-component/np": "^1.0.4", "@rc-component/trigger": "^3.0.0", - "@testing-library/react": "^16.3.0", - "@types/jest": "^29.4.0", - "@types/node": "^24.5.2", - "@types/react": "^19.0.0", - "@types/react-dom": "^19.0.0", - "@types/warning": "^3.0.0", - "@umijs/fabric": "^4.0.0", + "@testing-library/react": "^15.0.7", + "@types/jest": "^29.5.14", + "@types/node": "^26.0.1", + "@types/react": "^18.3.31", + "@types/react-dom": "^18.3.7", + "@types/warning": "^3.0.4", + "@typescript-eslint/eslint-plugin": "^5.62.0", + "@typescript-eslint/parser": "^5.62.0", + "@umijs/fabric": "^4.0.1", "array-tree-filter": "^3.0.2", "cheerio": "1.0.0-rc.12", "core-js": "^3.40.0", - "cross-env": "^7.0.0", - "dumi": "^2.1.10", - "eslint": "^8.54.0", - "eslint-plugin-jest": "^28.8.3", + "cross-env": "^10.1.0", + "dumi": "^2.4.35", + "eslint": "^8.57.1", + "eslint-plugin-jest": "^27.9.0", "eslint-plugin-unicorn": "^56.0.1", - "father": "^4.0.0", - "gh-pages": "^6.1.1", - "glob": "^7.1.6", - "less": "^4.2.0", - "prettier": "^3.1.0", - "rc-test": "^7.1.2", - "react": "^19.0.0", - "react-dom": "^19.0.0", - "typescript": "^5.3.2" + "father": "^4.6.23", + "gh-pages": "^6.3.0", + "glob": "^13.0.6", + "less": "^4.6.7", + "prettier": "^3.9.0", + "rc-test": "^7.1.3", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "typescript": "^5.9.3", + "husky": "^9.1.7", + "lint-staged": "^16.4.0" }, "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" - } + }, + "publishConfig": { + "access": "public" + }, + "lint-staged": { + "*": "prettier --write --ignore-unknown" + }, + "types": "./es/index.d.ts" } diff --git a/src/OptionList/List.tsx b/src/OptionList/List.tsx index e5ed46c3..fef4b20f 100644 --- a/src/OptionList/List.tsx +++ b/src/OptionList/List.tsx @@ -1,6 +1,6 @@ /* eslint-disable default-case */ import { clsx } from 'clsx'; -import type { BaseSelectProps, useBaseProps } from '@rc-component/select'; +import type { useBaseProps } from '@rc-component/select'; import * as React from 'react'; import { useMemo } from '@rc-component/util'; import type { DefaultOptionType, LegacyKey, SingleValueType } from '../Cascader'; @@ -18,12 +18,10 @@ import Column, { FIX_LABEL } from './Column'; import useActive from './useActive'; import useKeyboard from './useKeyboard'; -export type RefOptionListProps = - NonNullable['ref']> extends React.Ref< - infer Ref - > - ? Ref - : never; +export interface RefOptionListProps { + onKeyDown: React.KeyboardEventHandler; + onKeyUp: React.KeyboardEventHandler; +} export type RawOptionListProps = Pick< ReturnType, diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index dcba9929..c97e533e 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -2,7 +2,7 @@ import { useBaseProps } from '@rc-component/select'; import * as React from 'react'; import RawOptionList, { type RefOptionListProps } from './List'; -const RefOptionList = React.forwardRef((props, ref) => { +const RefOptionList = React.forwardRef>((props, ref) => { const { lockOptions, ...baseProps } = useBaseProps(); // >>>>> Render diff --git a/tsconfig.json b/tsconfig.json index 299bbc7e..2b912c63 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,9 +9,16 @@ "strict": true, "esModuleInterop": true, "paths": { - "@/*": ["src/*"], - "@@/*": ["src/.umi/*"], - "@rc-component/cascader": ["src/index.ts"] - } + "@/*": [ + "src/*" + ], + "@@/*": [ + "src/.umi/*" + ], + "@rc-component/cascader": [ + "src/index.ts" + ] + }, + "ignoreDeprecations": "5.0" } } diff --git a/vercel.json b/vercel.json new file mode 100644 index 00000000..5f9139ef --- /dev/null +++ b/vercel.json @@ -0,0 +1,6 @@ +{ + "framework": "umijs", + "installCommand": "npm install", + "buildCommand": "npm run build", + "outputDirectory": "docs-dist" +}