Repository: react-component/steps
Branch: master
Commit: 674846852bbb
Files: 71
Total size: 101.5 KB
Directory structure:
gitextract_zrdxiemy/
├── .dumirc.ts
├── .editorconfig
├── .eslintrc.js
├── .fatherrc.ts
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ └── main.yml
├── .gitignore
├── .prettierrc
├── HISTORY.md
├── LICENSE.md
├── README.md
├── assets/
│ ├── custom-icon.less
│ ├── iconfont.less
│ ├── index.less
│ ├── inline.less
│ ├── label-placement.less
│ ├── nav.less
│ ├── progress-dot.less
│ ├── small.less
│ ├── variables.less
│ └── vertical.less
├── bunfig.toml
├── docs/
│ ├── demo/
│ │ ├── alternativeLabel.md
│ │ ├── composable.md
│ │ ├── custom-svg-icon.md
│ │ ├── customIcon.md
│ │ ├── dynamic.md
│ │ ├── errorStep.md
│ │ ├── inline.md
│ │ ├── nav-base.md
│ │ ├── nextStep.md
│ │ ├── progressDot.md
│ │ ├── simple.md
│ │ ├── smallSize.md
│ │ ├── stepIcon.md
│ │ ├── vertical.md
│ │ └── verticalSmall.md
│ ├── examples/
│ │ ├── alternativeLabel.jsx
│ │ ├── composable.jsx
│ │ ├── custom-svg-icon.jsx
│ │ ├── customIcon.jsx
│ │ ├── dynamic.jsx
│ │ ├── errorStep.jsx
│ │ ├── inline.jsx
│ │ ├── nav-base.jsx
│ │ ├── nextStep.css
│ │ ├── nextStep.jsx
│ │ ├── progressDot.jsx
│ │ ├── simple.jsx
│ │ ├── smallSize.jsx
│ │ ├── stepIcon.jsx
│ │ ├── vertical.jsx
│ │ └── verticalSmall.jsx
│ └── index.md
├── index.js
├── jest.config.js
├── package.json
├── script/
│ └── update-content.js
├── src/
│ ├── Context.ts
│ ├── Rail.tsx
│ ├── Step.tsx
│ ├── StepIcon.tsx
│ ├── Steps.tsx
│ ├── UnstableContext.ts
│ ├── index.ts
│ └── interface.ts
├── tests/
│ ├── __snapshots__/
│ │ └── index.test.tsx.snap
│ ├── index.test.tsx
│ ├── semantic.test.tsx
│ └── setup.js
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .dumirc.ts
================================================
import { defineConfig } from 'dumi';
import path from 'path';
export default defineConfig({
alias: {
'@rc-component/steps$': path.resolve('src'),
'@rc-component/steps/es': path.resolve('src'),
},
mfsu: false,
favicons: ['https://avatars0.githubusercontent.com/u/9441414?s=200&v=4'],
themeConfig: {
name: 'Steps',
logo: 'https://avatars0.githubusercontent.com/u/9441414?s=200&v=4',
},
});
================================================
FILE: .editorconfig
================================================
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*.{js,css}]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
================================================
FILE: .eslintrc.js
================================================
const base = require('@umijs/fabric/dist/eslint');
module.exports = {
...base,
rules: {
...base.rules,
'arrow-parens': 0,
'react/sort-comp': 0,
},
};
================================================
FILE: .fatherrc.ts
================================================
import { defineConfig } from 'father';
export default defineConfig({
plugins: ['@rc-component/father-plugin'],
});
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
time: "21:00"
open-pull-requests-limit: 10
ignore:
- dependency-name: "@types/react-dom"
versions:
- 17.0.0
- 17.0.1
- 17.0.2
- dependency-name: "@types/react"
versions:
- 17.0.0
- 17.0.1
- 17.0.2
- 17.0.3
- dependency-name: less
versions:
- 4.1.0
================================================
FILE: .github/workflows/main.yml
================================================
name: ✅ test
on: [push, pull_request]
jobs:
test:
uses: react-component/rc-test/.github/workflows/test.yml@main
secrets: inherit
================================================
FILE: .gitignore
================================================
.iml
*.log
.idea/
.ipr
.iws
*~
~*
*.diff
*.patch
*.bak
.DS_Store
Thumbs.db
.project
.*proj
.svn/
*.swp
*.swo
*.pyc
*.pyo
.build
node_modules
.cache
dist
assets/**/*.css
build
lib
es
coverage
package-lock.json
pnpm-lock.yaml
yarn.lock
.doc
.umi
.npmrc
# dumi
.dumi/tmp
.dumi/tmp-test
.dumi/tmp-production
.env.local
bun.lockb
================================================
FILE: .prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all",
"proseWrap": "never",
"printWidth": 100
}
================================================
FILE: HISTORY.md
================================================
# History
----
## 3.6.0
- Remove babel-runtime and prop-types
- Fix icon missing #85
## 3.5.0
- Support `navigation` type & `disabled` prop.
## 3.4.0
- Support `onChange` event.
## 3.3.0
- Add `icons` prop for change preset icon.
## 3.2.0
- Add `initial` prop.
## 3.1.0
- Add `tailContent`.
## 3.0.0
- Rewrite from bottom.
## 2.5.1
* Support react@15.5
## 2.5.0
* Refactor for last tail style.
## 2.4.0
* Refactor for extra width of tail. https://github.com/ant-design/ant-design/issues/5083
## 2.3.0
* Add new step style of prop `progressDot`.
## 2.2.0
* `icon` can be React.Node now.
## 2.1.0
* Add `labelPlacement`, support vertial title and description
## 2.0.0
* Refactor for better layout
## 1.5
* add `status` property of `Steps`
## 1.4
* update react to 0.14
## 1.3
* add `current` property of `Steps`
## 1.2.3
* fix publish
## 1.2.2
* remove vertical `maxDescriptionWidth`
## 1.2.1
* fix vertical `maxDescriptionWidth`
## 1.2.0
* add vertical steps
## 1.1.4
* fix layout algorithm
## 1.1.3
* support `iconPrefix` property, default is `rc`
## 1.1.2
* fix bugs
## 1.1.1
* support `maxDescriptionWidth` property, default is 120
## 1.1.0
* support `prefixCls` property, default is `rc-steps`
================================================
FILE: LICENSE.md
================================================
The MIT License (MIT)
Copyright (c) 2014-present yiminghe
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.
================================================
FILE: README.md
================================================
# @rc-component/steps
---
React steps component.
[![NPM version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]
[![Test coverage][codecov-image]][codecov-url]
[![npm download][download-image]][download-url]
[![bundle size][bundlephobia-image]][bundlephobia-url]
[npm-image]: http://img.shields.io/npm/v/@rc-component/steps.svg?style=flat-square
[npm-url]: http://npmjs.org/package/@rc-component/steps
[travis-image]: https://img.shields.io/travis/react-component/steps.svg?style=flat-square
[travis-url]: https://travis-ci.org/react-component/steps
[codecov-image]: https://img.shields.io/codecov/c/github/react-component/steps/master.svg?style=flat-square
[codecov-url]: https://codecov.io/gh/react-component/steps/branch/master
[download-image]: https://img.shields.io/npm/dm/@rc-component/steps.svg?style=flat-square
[download-url]: https://npmjs.org/package/@rc-component/steps
[bundlephobia-url]: https://bundlephobia.com/result?p=@rc-component/steps
[bundlephobia-image]: https://badgen.net/bundlephobia/minzip/@rc-component/steps
## Usage
```bash
npm install @rc-component/steps
```
<br>
```jsx | pure
<Steps current={1}>
<Steps.Step title="first" />
<Steps.Step title="second" />
<Steps.Step title="third" />
</Steps>
```
## Example
https://steps.vercel.app/
## API
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="width: 100px;">name</th>
<th style="width: 50px;">type</th>
<th style="width: 50px;">default</th>
<th>description</th>
</tr>
</thead>
<tbody>
<tr>
<td>type</td>
<td>string</td>
<td>default</td>
<td>diretypetion of Steps, could be `default` `navigation` `inline`</td>
</tr>
<tr>
<td>direction</td>
<td>string</td>
<td>horizontal</td>
<td>direction of Steps, enum: `horizontal` or `vertical`</td>
</tr>
<tr>
<td>current</td>
<td>number</td>
<td>0</td>
<td>index of current step</td>
</tr>
<tr>
<td>initial</td>
<td>number</td>
<td>0</td>
<td>index initial</td>
</tr>
<tr>
<td>size</td>
<td>string</td>
<td></td>
<td>size of Steps, could be `small`</td>
</tr>
<tr>
<td>titlePlacement</td>
<td>string</td>
<td></td>
<td>placement of step title, could be `vertical`</td>
</tr>
<tr>
<td>status</td>
<td>string</td>
<td>wait</td>
<td>status of current Steps, could be `error` `process` `finish` `wait`</td>
</tr>
<tr>
<td>icons</td>
<td>{ finish: ReactNode, error: ReactNode }</td>
<td></td>
<td>specify the default finish icon and error icon</td>
</tr>
<tr>
<td>itemRender</td>
<td>(item: StepProps, stepItem: React.ReactNode) => React.ReactNode</td>
<td></td>
<td>custom step item renderer</td>
</tr>
<tr>
<td>onChange</td>
<td>(current: number) => void</td>
<td></td>
<td>Trigger when Step changed</td>
</tr>
</tbody>
</table>
### Steps.Step
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="width: 100px;">name</th>
<th style="width: 50px;">type</th>
<th style="width: 50px;">default</th>
<th>description</th>
</tr>
</thead>
<tbody>
<tr>
<td>title</td>
<td>ReactNode</td>
<td></td>
<td>title of step item</td>
</tr>
<tr>
<td>subTitle</td>
<td>ReactNode</td>
<td></td>
<td>subTitle of step item</td>
</tr>
<tr>
<td>description</td>
<td>ReactNode</td>
<td></td>
<td>description of step item</td>
</tr>
<tr>
<td>icon</td>
<td>ReactNode</td>
<td></td>
<td>set icon of step item</td>
</tr>
<tr>
<td>status</td>
<td>string</td>
<td></td>
<td>status of current Steps, could be `error` `process` `finish` `wait`</td>
</tr>
<tr>
<td>tailContent</td>
<td>ReactNode</td>
<td></td>
<td>content above tail</td>
</tr>
<tr>
<td>disabled</td>
<td>bool</td>
<td>false</td>
<td>disabled step when onChange exist</td>
</tr>
<tr>
<td>render</td>
<td>(stepItem: React.ReactNode) => React.ReactNode</td>
<td></td>
<td>custom step item renderer</td>
</tr>
</tbody>
</table>
## Development
```bash
npm install
npm start
```
## License
@rc-component/steps is released under the MIT license.
================================================
FILE: assets/custom-icon.less
================================================
@import 'variables';
.@{stepsPrefixClass}-item-custom {
.@{stepsPrefixClass}-item-icon {
background: none;
border: 0;
width: auto;
height: auto;
> .@{stepsPrefixClass}-icon {
font-size: 20px;
top: 1px;
width: 20px;
height: 20px;
}
}
&.@{stepsPrefixClass}-item-process {
.@{stepsPrefixClass}-item-icon > .@{stepsPrefixClass}-icon {
color: @process-icon-color;
}
}
}
================================================
FILE: assets/iconfont.less
================================================
@icon-url : "//at.alicdn.com/t/font_1434092639_4910953";
.ie-rotate(@rotation) {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation);
}
.rotate(@degrees) {
-webkit-transform: rotate(@degrees);
-ms-transform: rotate(@degrees); // IE9 only
-o-transform: rotate(@degrees);
transform: rotate(@degrees);
}
.animation(@animation) {
-webkit-animation: @animation;
-o-animation: @animation;
animation: @animation;
}
// font-face
// @icon-url: 字体源文件的地址
@font-face {
font-family: 'anticon';
src: url('@{icon-url}.eot'); /* IE9*/
src: url('@{icon-url}.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('@{icon-url}.woff') format('woff'), /* chrome、firefox */
url('@{icon-url}.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
url('@{icon-url}.svg#iconfont') format('svg'); /* iOS 4.1- */
}
.rcicon {
position: relative;
display: inline-block;
font-style: normal;
vertical-align: baseline;
text-align: center;
text-transform: none;
text-rendering: auto;
// 更好地渲染字体
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0px;
-moz-osx-font-smoothing: grayscale;
&:before {
display: block;
font-family: "anticon" !important;
}
}
// 方向性图标
.rcicon-step-backward:before {content:"\e662";}
.rcicon-step-forward {.ie-rotate(2);}
.rcicon-step-forward:before {content:"\e662";.rotate(180deg);}
.rcicon-fast-backward:before {content:"\e62a";}
.rcicon-fast-forward {.ie-rotate(2);}
.rcicon-fast-forward:before {content:"\e62a";.rotate(180deg);}
.rcicon-shrink:before {content:"\e65f";}
.rcicon-arrow-salt:before {content:"\e608";}
.rcicon-caret-down:before {content:"\e60f";}
.rcicon-caret-left {.ie-rotate(1);}
.rcicon-caret-left:before {content:"\e60f";.rotate(90deg);}
.rcicon-caret-up {.ie-rotate(2);}
.rcicon-caret-up:before {content:"\e60f";.rotate(180deg);}
.rcicon-caret-right {.ie-rotate(3);}
.rcicon-caret-right:before {content:"\e60f";.rotate(270deg);}
.rcicon-caret-circle-right:before {content:"\e60d";}
.rcicon-caret-circle-left {.ie-rotate(2);}
.rcicon-caret-circle-left:before {content:"\e60d";.rotate(180deg);}
.rcicon-caret-circle-o-right:before {content:"\e60e";}
.rcicon-caret-circle-o-left {.ie-rotate(2);}
.rcicon-caret-circle-o-left:before {content:"\e60e";.rotate(180deg);}
.rcicon-circle-right:before {content:"\e602";}
.rcicon-circle-left {.ie-rotate(2);}
.rcicon-circle-left:before {content:"\e602";.rotate(180deg);}
.rcicon-circle-o-right:before {content:"\e603";}
.rcicon-circle-o-left {.ie-rotate(2);}
.rcicon-circle-o-left:before {content:"\e603";.rotate(180deg);}
.rcicon-double-right:before {content:"\e604";}
.rcicon-double-left {.ie-rotate(2);}
.rcicon-double-left:before {content:"\e604";.rotate(180deg);}
.rcicon-verticle-right:before {content:"\e605";}
.rcicon-verticle-left {.ie-rotate(2);}
.rcicon-verticle-left:before {content:"\e605";.rotate(180deg);}
.rcicon-forward:before {content:"\e630";}
.rcicon-backward {.ie-rotate(2);}
.rcicon-backward:before {content:"\e630";.rotate(180deg);}
.rcicon-rollback:before {content:"\e65a";}
.rcicon-retweet:before {content:"\e659";}
.rcicon-right:before {content:"\e611";}
.rcicon-down {.ie-rotate(1);}
.rcicon-down:before {content:"\e611";.rotate(90deg);}
.rcicon-left {.ie-rotate(2);}
.rcicon-left:before {content:"\e611";.rotate(180deg);}
.rcicon-up {.ie-rotate(3);}
.rcicon-up:before {content:"\e611";.rotate(270deg);}
// 提示性图标
.rcicon-question:before {content:"\e655";}
.rcicon-question-circle:before {content:"\e656";}
.rcicon-question-circle-o:before {content:"\e657";}
.rcicon-plus:before {content:"\e651";}
.rcicon-plus-circle:before {content:"\e652";}
.rcicon-plus-circle-o:before {content:"\e653";}
.rcicon-pause:before {content:"\e64c";}
.rcicon-pause-circle:before {content:"\e64d";}
.rcicon-pause-circle-o:before {content:"\e64e";}
.rcicon-minus:before {content:"\e646";}
.rcicon-minus-circle:before {content:"\e647";}
.rcicon-minus-circle-o:before {content:"\e648";}
.rcicon-info-circle:before {content:"\e637";}
.rcicon-info-circle-o:before {content:"\e638";}
.rcicon-info:before {content:"\e63a";}
.rcicon-exclamation:before {content:"\e627";}
.rcicon-exclamation-circle:before {content:"\e628";}
.rcicon-exclamation-circle-o:before {content:"\e629";}
.rcicon-cross:before {content:"\e61e";}
.rcicon-cross-circle:before {content:"\e61f";}
.rcicon-cross-circle-o:before {content:"\e620";}
.rcicon-check:before {content:"\e613";}
.rcicon-check-circle:before {content:"\e614";}
.rcicon-check-circle-o:before {content:"\e615";}
.rcicon-clock-circle:before {content:"\e616";}
.rcicon-clock-circle-o:before {content:"\e617";}
// 网站通用图标
.rcicon-lock:before {content:"\e641";}
.rcicon-android:before {content:"\e601";}
.rcicon-apple:before {content:"\e606";}
.rcicon-area-chart:before {content:"\e607";}
.rcicon-bar-chart:before {content:"\e609";}
.rcicon-bars:before {content:"\e60a";}
.rcicon-book:before {content:"\e60b";}
.rcicon-calendar:before {content:"\e60c";}
.rcicon-cloud:before {content:"\e618";}
.rcicon-cloud-download:before {content:"\e619";}
.rcicon-code:before {content:"\e61a";}
.rcicon-copy:before {content:"\e61c";}
.rcicon-credit-card:before {content:"\e61d";}
.rcicon-delete:before {content:"\e621";}
.rcicon-desktop:before {content:"\e622";}
.rcicon-download-line:before {content:"\e623";}
.rcicon-edit:before {content:"\e624";}
.rcicon-ellipsis:before {content:"\e625";}
.rcicon-environment:before {content:"\e626";}
.rcicon-file:before {content:"\e62c";}
.rcicon-file-text:before {content:"\e62d";}
.rcicon-folder:before {content:"\e62e";}
.rcicon-folder-open:before {content:"\e62f";}
.rcicon-github:before {content:"\e631";}
.rcicon-hdd:before {content:"\e632";}
.rcicon-frown:before {content:"\e633";}
.rcicon-meh:before {content:"\e634";}
.rcicon-inbox:before {content:"\e635";}
.rcicon-laptop:before {content:"\e63d";}
.rcicon-large:before {content:"\e63e";}
.rcicon-line-chart:before {content:"\e63f";}
.rcicon-link:before {content:"\e640";}
.rcicon-logout:before {content:"\e642";}
.rcicon-mail:before {content:"\e643";}
.rcicon-menu-fold:before {content:"\e644";}
.rcicon-menu-unfold:before {content:"\e645";}
.rcicon-mobile:before {content:"\e649";}
.rcicon-notification:before {content:"\e64a";}
.rcicon-paper-clip:before {content:"\e64b";}
.rcicon-picture:before {content:"\e64f";}
.rcicon-pie-chart:before {content:"\e650";}
.rcicon-poweroff:before {content:"\e654";}
.rcicon-reload:before {content:"\e658";}
.rcicon-search:before {content:"\e65b";}
.rcicon-setting:before {content:"\e65c";}
.rcicon-share-alt:before {content:"\e65d";}
.rcicon-shopping-cart:before {content:"\e65e";}
.rcicon-smile:before {content:"\e661";}
.rcicon-tablet:before {content:"\e664";}
.rcicon-tag:before {content:"\e665";}
.rcicon-tags:before {content:"\e666";}
.rcicon-to-top:before {content:"\e667";}
.rcicon-unlock:before {content:"\e668";}
.rcicon-upload:before {content:"\e669";}
.rcicon-user:before {content:"\e66a";}
.rcicon-video-camera:before {content:"\e66b";}
.rcicon-windows:before {content:"\e66c";}
.rcicon-loading:before {
display: inline-block;
.animation(loadingCircle 1.0s infinite linear);
content:"\e610";
}
:root {
.rcicon-step-forward,
.rcicon-fast-forward,
.rcicon-left,
.rcicon-up,
.rcicon-down,
.rcicon-caret-left,
.rcicon-caret-up,
.rcicon-caret-right,
.rcicon-caret-circle-left,
.rcicon-caret-circle-o-left,
.rcicon-circle-left,
.rcicon-circle-o-left,
.rcicon-double-left,
.rcicon-verticle-left,
.rcicon-backward {
filter: none;
}
}
================================================
FILE: assets/index.less
================================================
@import 'variables';
.@{stepsPrefixClass} {
font-size: 0;
width: 100%;
line-height: 1.5;
display: flex;
&,
* {
box-sizing: border-box;
}
}
.@{stepsPrefixClass}-item {
position: relative;
display: inline-block;
vertical-align: top;
flex: 1;
// overflow: hidden;
&:last-child {
flex: none;
}
&:last-child &-title:after {
display: none;
}
&-icon,
&-section {
display: inline-block;
vertical-align: top;
}
&-icon {
flex: none;
border: 1px solid @wait-icon-color;
width: 26px;
height: 26px;
line-height: 26px;
text-align: center;
border-radius: 26px;
font-size: 14px;
transition:
background-color 0.3s,
border-color 0.3s;
> .@{stepsPrefixClass}-icon {
line-height: 1;
top: -1px;
color: @primary-color;
position: relative;
&.rcicon {
font-size: 12px;
position: relative;
top: -2px;
}
}
}
&-section {
margin-top: 3px;
}
&-title {
font-size: 14px;
color: #666;
font-weight: bold;
display: inline-block;
position: relative;
}
&-subtitle {
font-size: 12px;
display: inline-block;
color: #999;
}
&-description {
font-size: 12px;
color: #999;
}
.step-item-status(wait);
.step-item-status(process);
&-process &-icon {
background: @process-icon-color;
> .@{stepsPrefixClass}-icon {
color: #fff;
}
}
.step-item-status(finish);
.step-item-status(error);
&.@{stepsPrefixClass}-next-error .@{stepsPrefixClass}-item-title:after {
background: @error-icon-color;
}
}
.@{stepsPrefixClass}-horizontal:not(.@{stepsPrefixClass}-label-vertical) {
.@{stepsPrefixClass}-item {
&-description {
max-width: @stepDescriptionMaxWidth;
}
}
}
.step-item-status(@status) {
@icon-color: '@{status}-icon-color';
@title-color: '@{status}-title-color';
@description-color: '@{status}-description-color';
@tail-color: '@{status}-tail-color';
&-@{status} &-icon {
border-color: @@icon-color;
background-color: #fff;
> .@{stepsPrefixClass}-icon {
color: @@icon-color;
.@{stepsPrefixClass}-icon-dot {
background: @@icon-color;
}
}
}
&-@{status} &-title {
color: @@title-color;
}
&-@{status} &-description {
color: @@description-color;
}
}
// @import 'custom-icon';
// @import 'small';
// @import 'vertical';
// @import 'label-placement';
// @import 'progress-dot';
// @import 'nav';
// @import 'inline';
// ======================= Horizontal =======================
.verticalFlex() {
display: flex;
flex-direction: column;
align-items: center;
}
.@{stepsPrefixClass} {
.@{stepsPrefixClass}-item {
&-section {
min-width: 0;
}
&-header {
display: flex;
gap: 8px;
align-items: center;
}
// Ellipsis
&-title,
&-subtitle,
&-description {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.@{stepsPrefixClass}-horizontal {
.@{stepsPrefixClass}-item {
flex: 1;
position: relative;
min-width: 0;
&-rail {
height: 1px;
background: @process-tail-color;
}
}
// Label Vertical
&.@{stepsPrefixClass}-label-vertical {
.@{stepsPrefixClass}-item {
.verticalFlex();
padding-inline: 8px;
&-section {
.verticalFlex();
}
&-rail {
position: absolute;
top: 13px;
left: calc(50% + 13px);
width: 100%;
}
}
}
// Label Horizontal
&.@{stepsPrefixClass}-label-horizontal {
.@{stepsPrefixClass}-item {
display: flex;
&:last-child {
flex: none;
}
&-section {
flex: 1;
}
&-rail {
flex: 1;
min-width: 0;
}
}
}
}
// ======================== Vertical ========================
.@{stepsPrefixClass}-vertical {
}
================================================
FILE: assets/inline.less
================================================
@import 'variables';
.@{stepsPrefixClass}-inline {
width: auto;
display: inline-flex;
.@{stepsPrefixClass}-item {
flex: none;
&-icon {
width: 6px;
height: 6px;
margin-left: calc(50% - 3px);
> .@{stepsPrefixClass}-icon {
top: 0;
}
.@{stepsPrefixClass}-icon-dot {
border-radius: 3px;
}
}
&-section {
width: auto;
margin-top: 7px;
}
&-title {
color: rgba(0, 0, 0, 0.25);
font-size: 12px;
line-height: 20px;
font-weight: normal;
margin-bottom: 2px;
}
&-description {
display: none;
}
&-finish {
.@{stepsPrefixClass}-item-icon .@{stepsPrefixClass}-icon .@{stepsPrefixClass}-icon-dot {
background-color: @process-tail-color;
}
}
&-wait {
.@{stepsPrefixClass}-item-icon .@{stepsPrefixClass}-icon .@{stepsPrefixClass}-icon-dot {
background-color: #fff;
border: 1px solid @process-tail-color;
}
}
}
}
================================================
FILE: assets/label-placement.less
================================================
@import 'variables';
.@{stepsPrefixClass}-label-vertical {
.@{stepsPrefixClass}-item {
overflow: visible;
&-section {
display: block;
text-align: center;
margin-top: 8px;
width: @stepDescriptionMaxWidth;
}
&-icon {
display: inline-block;
margin-left: 36px;
}
&-title {
padding-right: 0;
&:after {
display: none;
}
}
&-description {
text-align: left;
}
}
}
================================================
FILE: assets/nav.less
================================================
@import 'variables';
.@{stepsPrefixClass}-navigation {
padding-top: 8px;
&.@{stepsPrefixClass}-horizontal {
.@{stepsPrefixClass}-item-description {
max-width: @stepNavContentMaxWidth;
}
}
.@{stepsPrefixClass}-item {
box-sizing: border-box;
text-align: center;
overflow: visible;
&-title {
max-width: @stepNavContentMaxWidth;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&:after {
display: none;
}
}
&:last-child {
flex: 1;
&:after {
display: none;
}
}
&:after {
content: '';
display: inline-block;
width: 16px;
height: 16px;
border: 1px solid #ccc;
border-bottom: none;
border-left: none;
transform: rotate(45deg);
position: absolute;
top: 50%;
left: 100%;
margin-top: -12px;
margin-left: -8px;
}
}
}
================================================
FILE: assets/progress-dot.less
================================================
@import 'variables';
.@{stepsPrefixClass}-dot {
.@{stepsPrefixClass}-item {
&-icon {
padding-right: 0;
width: 5px;
height: 5px;
line-height: 5px;
border: 0;
margin-left: 48px;
.@{stepsPrefixClass}-icon-dot {
float: left;
width: 100%;
height: 100%;
border-radius: 2.5px;
}
}
&-process &-icon {
top: -1px;
width: 7px;
height: 7px;
line-height: 7px;
.@{stepsPrefixClass}-icon-dot {
border-radius: 3.5px;
}
}
}
}
================================================
FILE: assets/small.less
================================================
@import 'variables';
.@{stepsPrefixClass}-small {
.@{stepsPrefixClass}-item-icon {
width: 18px;
height: 18px;
line-height: 18px;
text-align: center;
border-radius: 18px;
font-size: 12px;
margin-right: 10px;
> .@{stepsPrefixClass}-icon {
font-size: 12px;
font-size: ~"9px \9"; // ie8-9
transform: scale(.75);
top: -1px;
}
}
.@{stepsPrefixClass}-item-section {
margin-top: 0;
}
.@{stepsPrefixClass}-item-title {
font-size: 12px;
margin-bottom: 4px;
color: #666;
font-weight: bold;
}
.@{stepsPrefixClass}-item-description {
font-size: 12px;
color: #999;
}
.@{stepsPrefixClass}-item-custom .@{stepsPrefixClass}-item-icon {
width: inherit;
height: inherit;
line-height: inherit;
border-radius: 0;
border: 0;
background: none;
> .@{stepsPrefixClass}-icon {
font-size: 20px;
top: -2.5px;
transform: none;
}
}
}
================================================
FILE: assets/variables.less
================================================
@stepsPrefixClass: ~"rc-steps";
@stepDescriptionMaxWidth: 100px;
@stepNavContentMaxWidth: 140px;
@primary-color: #108ee9;
@process-icon-color: @primary-color;
@process-title-color: rgba(0,0,0,.65);
@process-description-color: @process-title-color;
@process-tail-color: #e9e9e9;
@wait-icon-color: #ccc;
@wait-title-color: rgba(0,0,0,.43);
@wait-description-color: @wait-title-color;
@wait-tail-color: @process-tail-color;
@finish-icon-color: @process-icon-color;
@finish-title-color: @wait-title-color;
@finish-description-color: @finish-title-color;
@finish-tail-color: @process-icon-color;
@error-icon-color: #f50;
@error-title-color: @error-icon-color;
@error-description-color: @error-icon-color;
@error-tail-color: @process-tail-color;
================================================
FILE: assets/vertical.less
================================================
@import 'variables';
.@{stepsPrefixClass}-vertical {
display: block;
.@{stepsPrefixClass}-item {
display: block;
overflow: visible;
&-icon {
float: left;
&-inner {
margin-right: 16px;
}
}
&-section {
min-height: 48px;
overflow: hidden;
display: block;
}
&-title {
line-height: 26px;
&:after {
display: none;
}
}
&-description {
padding-bottom: 12px;
}
}
&.@{stepsPrefixClass}-small {
.@{stepsPrefixClass}-item-title {
line-height: 18px;
}
}
}
================================================
FILE: bunfig.toml
================================================
[install]
peer = false
================================================
FILE: docs/demo/alternativeLabel.md
================================================
---
title: alternativeLabel
nav:
title: Demo
path: /demo
---
<code src="../examples/alternativeLabel.jsx"></code>
================================================
FILE: docs/demo/composable.md
================================================
---
title: composable
nav:
title: Demo
path: /demo
---
<code src="../examples/composable.jsx"></code>
================================================
FILE: docs/demo/custom-svg-icon.md
================================================
---
title: custom-svg-icon
nav:
title: Demo
path: /demo
---
<code src="../examples/custom-svg-icon.jsx"></code>
================================================
FILE: docs/demo/customIcon.md
================================================
---
title: customIcon
nav:
title: Demo
path: /demo
---
<code src="../examples/customIcon.jsx"></code>
================================================
FILE: docs/demo/dynamic.md
================================================
---
title: dynamic
nav:
title: Demo
path: /demo
---
<code src="../examples/dynamic.jsx"></code>
================================================
FILE: docs/demo/errorStep.md
================================================
---
title: errorStep
nav:
title: Demo
path: /demo
---
<code src="../examples/errorStep.jsx"></code>
================================================
FILE: docs/demo/inline.md
================================================
---
title: inline
nav:
title: Demo
path: /demo
---
<code src="../examples/inline.jsx"></code>
================================================
FILE: docs/demo/nav-base.md
================================================
---
title: nav-base
nav:
title: Demo
path: /demo
---
<code src="../examples/nav-base.jsx"></code>
================================================
FILE: docs/demo/nextStep.md
================================================
---
title: nextStep
nav:
title: Demo
path: /demo
---
<code src="../examples/nextStep.jsx"></code>
================================================
FILE: docs/demo/progressDot.md
================================================
---
title: progressDot
nav:
title: Demo
path: /demo
---
<code src="../examples/progressDot.jsx"></code>
================================================
FILE: docs/demo/simple.md
================================================
---
title: simple
nav:
title: Demo
path: /demo
---
<code src="../examples/simple.jsx"></code>
================================================
FILE: docs/demo/smallSize.md
================================================
---
title: smallSize
nav:
title: Demo
path: /demo
---
<code src="../examples/smallSize.jsx"></code>
================================================
FILE: docs/demo/stepIcon.md
================================================
---
title: stepIcon
nav:
title: Demo
path: /demo
---
<code src="../examples/stepIcon.jsx"></code>
================================================
FILE: docs/demo/vertical.md
================================================
---
title: vertical
nav:
title: Demo
path: /demo
---
<code src="../examples/vertical.jsx"></code>
================================================
FILE: docs/demo/verticalSmall.md
================================================
---
title: verticalSmall
nav:
title: Demo
path: /demo
---
<code src="../examples/verticalSmall.jsx"></code>
================================================
FILE: docs/examples/alternativeLabel.jsx
================================================
import '../../assets/index.less';
import '../../assets/iconfont.less';
import React from 'react';
import Steps from '@rc-component/steps';
const description =
'这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊';
export default () => (
<Steps
titlePlacement="vertical"
current={1}
items={[
{
title: '已完成',
description,
status: 'wait',
},
{
title: '进行中',
description,
status: 'wait',
subTitle: '剩余 00:00:07',
},
undefined,
{
title: '待运行',
description,
status: 'process',
},
false,
{
title: '待运行',
description,
status: 'finish',
disabled: true,
},
null,
]}
/>
);
================================================
FILE: docs/examples/composable.jsx
================================================
import '../../assets/index.less';
import '../../assets/iconfont.less';
import React from 'react';
import Steps from '@rc-component/steps';
const description =
'这里是多信息的描述啊描述啊描述啊描述啊哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶';
export default () => (
<Steps
current={1}
items={[
{
title: '已完成',
description,
},
{
title: '进行中',
description,
},
{
title: '进行中',
description,
style: { fontWeight: 'bold', fontStyle: 'italic' },
},
{
title: '待运行',
description,
},
]}
/>
);
================================================
FILE: docs/examples/custom-svg-icon.jsx
================================================
import '../../assets/index.less';
import '../../assets/iconfont.less';
import React from 'react';
import Steps from '@rc-component/steps';
function getFinishIcon() {
const path =
'M923 283.6c-13.4-31.1-32.6-58.9-56.9-82.8-24.3-23.8-52.' +
'5-42.4-84-55.5-32.5-13.5-66.9-20.3-102.4-20.3-49.3 0-97.4 13.5-139' +
'.2 39-10 6.1-19.5 12.8-28.5 20.1-9-7.3-18.5-14-28.5-20.1-41.8-25.5' +
'-89.9-39-139.2-39-35.5 0-69.9 6.8-102.4 20.3-31.4 13-59.7 31.7-84 ' +
'55.5-24.4 23.9-43.5 51.7-56.9 82.8-13.9 32.3-21 66.6-21 101.9 0 33' +
'.3 6.8 68 20.3 103.3 11.3 29.5 27.5 60.1 48.2 91 32.8 48.9 77.9 99' +
'.9 133.9 151.6 92.8 85.7 184.7 144.9 188.6 147.3l23.7 15.2c10.5 6.' +
'7 24 6.7 34.5 0l23.7-15.2c3.9-2.5 95.7-61.6 188.6-147.3 56-51.7 10' +
'1.1-102.7 133.9-151.6 20.7-30.9 37-61.5 48.2-91 13.5-35.3 20.3-70 ' +
'20.3-103.3 0.1-35.3-7-69.6-20.9-101.9z';
return (
<svg
width="1em"
height="1em"
fill="currentColor"
viewBox="0 0 1024 1024"
style={{ verticalAlign: '-.125em' }}
>
<path d={path} />
</svg>
);
}
function getErrorIcon() {
const path1 =
'M512 0C229.2 0 0 229.2 0 512s229.2 512 512 512 512-229' +
'.2 512-512S794.8 0 512 0zm311.1 823.1c-40.4 40.4-87.5 72.2-139.9 9' +
'4.3C629 940.4 571.4 952 512 952s-117-11.6-171.2-34.5c-52.4-22.2-99' +
'.4-53.9-139.9-94.3-40.4-40.4-72.2-87.5-94.3-139.9C83.6 629 72 571.' +
'4 72 512s11.6-117 34.5-171.2c22.2-52.4 53.9-99.4 94.3-139.9 40.4-4' +
'0.4 87.5-72.2 139.9-94.3C395 83.6 452.6 72 512 72s117 11.6 171.2 3' +
'4.5c52.4 22.2 99.4 53.9 139.9 94.3 40.4 40.4 72.2 87.5 94.3 139.9C' +
'940.4 395 952 452.6 952 512s-11.6 117-34.5 171.2c-22.2 52.4-53.9 9' +
'9.5-94.4 139.9z';
const path2 =
'M640.3 765.5c-19.9 0-36-16.1-36-36 0-50.9-41.4-92.3-92' +
'.3-92.3s-92.3 41.4-92.3 92.3c0 19.9-16.1 36-36 36s-36-16.1-36-36c0' +
'-90.6 73.7-164.3 164.3-164.3s164.3 73.7 164.3 164.3c0 19.9-16.1 36' +
'-36 36zM194.2 382.4a60 60 0 1 0 120 0 60 60 0 1 0-120 0zM709.5 382' +
'.4a60 60 0 1 0 120 0 60 60 0 1 0-120 0z';
return (
<svg
width="1em"
height="1em"
fill="currentColor"
viewBox="0 0 1024 1024"
style={{ verticalAlign: '-.125em' }}
>
<path d={path1} />
<path d={path2} />
</svg>
);
}
const icons = {
finish: getFinishIcon(),
error: getErrorIcon(),
};
const description = 'This is a description';
export default () => (
<Steps
current={1}
status="error"
icons={icons}
items={[
{ title: 'Finished', description },
{ title: 'In Process', description },
{ title: 'Waiting', description },
]}
/>
);
================================================
FILE: docs/examples/customIcon.jsx
================================================
import '../../assets/index.less';
import '../../assets/iconfont.less';
import React from 'react';
import Steps from '@rc-component/steps';
// eslint-disable-next-line react/prop-types
const Icon = ({ type }) => <i className={`rcicon rcicon-${type}`} />;
export default () => (
<Steps
current={1}
items={[
{ title: '步骤1', icon: <Icon type="cloud" /> },
{ title: '步骤2', icon: 'apple' },
{ title: '步骤1', icon: 'github' },
]}
/>
);
================================================
FILE: docs/examples/dynamic.jsx
================================================
import '../../assets/index.less';
import '../../assets/iconfont.less';
import React, { useState } from 'react';
import Steps from '@rc-component/steps';
export default () => {
const [items, setItems] = useState([
{
title: '已完成',
description:
'这里是多信息的描述啊描述啊描述啊描述啊哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶',
},
{
title: '进行中',
description:
'这里是多信息的描述啊描述啊描述啊描述啊哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶',
},
{
title: '待运行',
description:
'这里是多信息的描述啊描述啊描述啊描述啊哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶',
},
{
title: '待运行',
description:
'这里是多信息的描述啊描述啊描述啊描述啊哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶',
},
]);
const addStep = () => {
const newSteps = [...items];
newSteps.push({
title: '待运行',
description: '新的节点',
});
setItems(newSteps);
};
return (
<div>
<button type="button" onClick={addStep}>
Add new step
</button>
<Steps items={items} />
</div>
);
};
================================================
FILE: docs/examples/errorStep.jsx
================================================
import '../../assets/index.less';
import '../../assets/iconfont.less';
import React from 'react';
import Steps from '@rc-component/steps';
const description =
'这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊';
export default () => (
<Steps
current={2}
status="error"
items={[
{
title: '已完成',
description,
},
{
title: '进行中',
description,
},
{
title: '待运行',
description,
},
{
title: '待运行',
description,
},
]}
/>
);
================================================
FILE: docs/examples/inline.jsx
================================================
import '../../assets/index.less';
import React, { useState } from 'react';
import Steps from '@rc-component/steps';
export default () => {
const [current, setCurrent] = useState(0);
return (
<>
<button
onClick={() => {
setCurrent(0);
}}
>
Current: {current}
</button>
<br />
<Steps
type="inline"
current={current}
onChange={setCurrent}
items={[
{
title: '开发',
description: '开发阶段:开发中',
},
{
title: '测试',
description: '测试阶段:测试中',
},
{
title: '预发',
description: '预发阶段:预发中',
},
{
title: '发布',
description: '发布阶段:发布中',
},
]}
itemRender={(item, stepItem) => React.cloneElement(stepItem, { title: item.description })}
/>
</>
);
};
================================================
FILE: docs/examples/nav-base.jsx
================================================
import '../../assets/index.less';
import '../../assets/iconfont.less';
import React, { useState } from 'react';
import Steps from '@rc-component/steps';
export default () => {
const [current, setCurrent] = useState(0);
const onChange = (current) => {
// eslint-disable-next-line no-console
console.log('onChange:', current);
setCurrent(current);
};
const containerStyle = {
border: '1px solid rgb(235, 237, 240)',
marginBottom: 24,
};
const description = 'This is a description.';
return (
<div>
<Steps
style={containerStyle}
type="navigation"
current={current}
onChange={onChange}
items={[
{
title: 'Step 1',
status: 'finish',
subTitle: '剩余 00:00:05 超长隐藏',
description,
},
{
title: 'Step 2',
status: 'process',
description,
},
{
title: 'Step 3',
status: 'wait',
description,
disabled: true,
},
]}
/>
<Steps
style={containerStyle}
type="navigation"
current={current}
onChange={onChange}
items={[
{
title: 'Step 1',
status: 'finish',
subTitle: '剩余 00:00:05 超长隐藏',
},
{
title: 'Step 2',
status: 'process',
},
{
title: 'Step 3',
status: 'wait',
},
{
title: 'Step 3',
status: 'wait',
},
]}
/>
</div>
);
};
================================================
FILE: docs/examples/nextStep.css
================================================
.my-step-form {
width: 100%;
}
.my-step-form > div {
margin-bottom: 20px;
}
.my-step-container {
width: 100%;
}
================================================
FILE: docs/examples/nextStep.jsx
================================================
import '../../assets/index.less';
import '../../assets/iconfont.less';
import './nextStep.css';
import React from 'react';
import Steps from '@rc-component/steps';
function generateRandomSteps() {
const n = Math.floor(Math.random() * 3) + 3;
const arr = [];
for (let i = 0; i < n; i++) {
arr.push({
title: `步骤${i + 1}`,
});
}
return arr;
}
const steps = generateRandomSteps();
class MyForm extends React.Component {
state = {
currentStep: Math.floor(Math.random() * steps.length),
};
nextStep = () => {
const { currentStep } = this.state;
let s = currentStep + 1;
if (s === steps.length) {
s = 0;
}
this.setState({
currentStep: s,
});
};
render() {
const { currentStep: cs } = this.state;
this.stepsRefs = [];
return (
<form className="my-step-form">
<div>这个demo随机生成3~6个步骤,初始随机进行到其中一个步骤</div>
<div>当前正在执行第{cs + 1}步</div>
<div className="my-step-container">
<Steps
current={cs}
items={steps.map((s, i) => ({
ref: (c) => {
this.stepsRefs[i] = c;
},
key: i,
title: s.title,
}))}
/>
</div>
<div>
<button type="button" onClick={this.nextStep}>
下一步
</button>
</div>
</form>
);
}
}
export default MyForm;
================================================
FILE: docs/examples/progressDot.jsx
================================================
import '../../assets/index.less';
import '../../assets/iconfont.less';
import React from 'react';
import Steps from '@rc-component/steps';
const description =
'这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊';
export default () => (
<Steps
progressDot
size="small"
current={1}
items={[
{
title: '已完成',
description,
},
{
title: '进行中',
description,
},
{
title: '待运行',
description,
},
{
title: '待运行',
description,
},
{
title: '待运行',
description,
},
]}
/>
);
================================================
FILE: docs/examples/simple.jsx
================================================
import '../../assets/index.less';
import '../../assets/iconfont.less';
import React from 'react';
import Steps from '@rc-component/steps';
const description =
'这里是多信息的描述啊描述啊描述啊描述啊哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶';
const ControlSteps = () => {
const [current, setCurrent] = React.useState(0);
return (
<Steps
current={current}
onChange={(val) => {
// eslint-disable-next-line no-console
console.log('Change:', val);
setCurrent(val);
}}
items={[
{
title: '已完成',
},
{
title: '进行中',
},
{
title: '待运行',
description: 'Hello World!',
},
{
title: '待运行',
},
]}
/>
);
};
export default () => (
<div>
<Steps
current={1}
items={[
{
title: '已完成',
},
{
title: '进行中',
},
{
title: '待运行',
},
{
title: '待运行',
},
]}
/>
<Steps
current={1}
style={{ marginTop: 40 }}
items={[
{
title: '已完成',
description,
},
{
title: '进行中',
subTitle: '剩余 00:00:07',
description,
},
{
title: '待运行',
description,
},
{
title: '待运行',
description,
},
]}
/>
<Steps
current={1}
style={{ marginTop: 40 }}
status="error"
items={[
{
title: '已完成',
description,
},
{
title: '进行中',
subTitle: '剩余 00:00:07',
description,
},
{
title: '待运行',
description,
},
{
title: '待运行',
description,
},
]}
/>
<ControlSteps />
</div>
);
================================================
FILE: docs/examples/smallSize.jsx
================================================
import '../../assets/index.less';
import '../../assets/iconfont.less';
import React from 'react';
import Steps from '@rc-component/steps';
// eslint-disable-next-line react/prop-types
const Icon = ({ type }) => <i className={`rcicon rcicon-${type}`} />;
export default () => (
<div>
<Steps
size="small"
current={1}
items={[
{
title: '已完成',
},
{
title: '进行中',
},
{
title: '待运行',
},
{
title: '待运行',
},
]}
/>
<Steps
size="small"
current={1}
style={{ marginTop: 40 }}
items={[
{
title: '步骤1',
},
{
title: '步骤2',
icon: <Icon type="cloud" />,
},
{
title: '步骤3',
icon: 'apple',
},
{
title: '待运行',
icon: 'github',
},
]}
/>
</div>
);
================================================
FILE: docs/examples/stepIcon.jsx
================================================
import '../../assets/index.less';
import '../../assets/iconfont.less';
import React from 'react';
import Steps from '@rc-component/steps';
function stepIcon({ status, node }) {
const isProcessing = status === 'process';
return isProcessing ? <div style={{ backgroundColor: 'blue' }}>{node}</div> : node;
}
export default () => {
const [current, setCurrent] = React.useState(0);
return (
<>
<button
type="button"
onClick={() => {
setCurrent((current + 1) % 5);
}}
>
loop
</button>
<Steps
stepIcon={stepIcon}
current={current}
items={[
{
title: '已完成',
},
{
title: '进行中',
},
{
title: '待运行',
},
{
title: '待运行',
},
{
title: '待运行',
},
]}
/>
</>
);
};
================================================
FILE: docs/examples/vertical.jsx
================================================
import '../../assets/index.less';
import '../../assets/iconfont.less';
import React from 'react';
import Steps from '@rc-component/steps';
const description =
'这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊';
export default () => (
<Steps
direction="vertical"
items={[
{
title: '已完成',
description,
},
{
title: '进行中',
description,
},
{
title: '待运行',
description,
},
{
title: '待运行',
description,
},
]}
/>
);
================================================
FILE: docs/examples/verticalSmall.jsx
================================================
import '../../assets/index.less';
import '../../assets/iconfont.less';
import React from 'react';
import Steps from '@rc-component/steps';
const description =
'这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊';
export default () => (
<Steps
direction="vertical"
size="small"
items={[
{
title: '已完成',
description,
},
{
title: '进行中',
description,
},
{
title: '待运行',
description,
},
{
title: '待运行',
description,
},
]}
/>
);
================================================
FILE: docs/index.md
================================================
---
hero:
title: "@rc-component/steps"
description: React Steps Component
---
<embed src="../README.md"></embed>
================================================
FILE: index.js
================================================
'use strict';
module.exports = require('./src/');
================================================
FILE: jest.config.js
================================================
module.exports = {
setupFiles: ['./tests/setup.js'],
};
================================================
FILE: package.json
================================================
{
"name": "@rc-component/steps",
"version": "1.2.2",
"description": "steps ui component for react",
"keywords": [
"react",
"react-component",
"react-steps"
],
"homepage": "http://github.com/react-component/steps",
"bugs": {
"url": "http://github.com/react-component/steps/issues"
},
"repository": {
"type": "git",
"url": " git+ssh://git@github.com/react-component/steps.git"
},
"license": "MIT",
"maintainers": [
{
"name": "afc163",
"email": "afc163@gmail.com"
}
],
"main": "./lib/index",
"module": "./es/index",
"types": "./lib/index.d.ts",
"files": [
"assets/*.css",
"es",
"lib"
],
"scripts": {
"compile": "father build && lessc assets/index.less assets/index.css",
"coverage": "rc-test --coverage",
"docs:build": "dumi build",
"docs:deploy": "gh-pages -d .doc",
"gh-pages": "npm run docs:build && npm run docs:deploy",
"lint": "eslint src/ --ext .ts,.tsx,.jsx,.js,.md",
"prepare": "husky install && dumi setup",
"prepublishOnly": "npm run compile && rc-np",
"prettier": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
"postpublish": "npm run gh-pages",
"start": "dumi dev",
"test": "rc-test",
"now-build": "npm run docs:build"
},
"lint-staged": {
"**/*.{js,jsx,tsx,ts,md,json}": [
"prettier --write"
]
},
"dependencies": {
"@rc-component/util": "^1.2.1",
"clsx": "^2.1.1"
},
"devDependencies": {
"@rc-component/father-plugin": "^2.0.2",
"@rc-component/np": "^1.0.0",
"@testing-library/jest-dom": "^6.4.5",
"@testing-library/react": "^15.0.6",
"@types/jest": "^29.4.0",
"@types/node": "^24.5.2",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@umijs/fabric": "^4.0.1",
"dumi": "^2.0.0",
"eslint": "^8.55.0",
"eslint-plugin-jest": "^27.6.0",
"eslint-plugin-unicorn": "^50.0.1",
"father": "^4",
"gh-pages": "^6.1.0",
"glob": "^10.0.0",
"husky": "^8.0.1",
"less": "^4.1.3",
"lint-staged": "^15.2.0",
"prettier": "^3.1.0",
"rc-test": "^7.0.9",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"typescript": "^5.0.0"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
},
"engines": {
"node": ">=8.x"
}
}
================================================
FILE: script/update-content.js
================================================
/*
用于 dumi 改造使用,
可用于将 examples 的文件批量修改为 demo 引入形式,
其他项目根据具体情况使用。
*/
const fs = require('fs');
const glob = require('glob');
const paths = glob.sync('./docs/examples/*.jsx');
paths.forEach(path => {
const name = path.split('/').pop().split('.')[0];
fs.writeFile(
`./docs/demo/${name}.md`,
`---
title: ${name}
nav:
title: Demo
path: /demo
---
<code src="../examples/${name}.jsx"></code>
`,
'utf8',
function(error) {
if(error){
console.log(error);
return false;
}
console.log(`${name} 更新成功~`);
}
)
});
================================================
FILE: src/Context.ts
================================================
import * as React from 'react';
import type { ComponentType, StepsProps } from './Steps';
export interface StepsContextProps {
prefixCls: string;
classNames: NonNullable<StepsProps['classNames']>;
styles: NonNullable<StepsProps['styles']>;
ItemComponent: ComponentType;
}
export const StepsContext = React.createContext<StepsContextProps>(null!);
================================================
FILE: src/Rail.tsx
================================================
import * as React from 'react';
import { clsx } from 'clsx';
import type { Status } from './Steps';
export interface RailProps {
prefixCls: string;
className: string;
style: React.CSSProperties;
status: Status;
}
export default function Rail(props: RailProps) {
const { prefixCls, className, style, status } = props;
const railCls = `${prefixCls}-rail`;
// ============================= render =============================
return <div className={clsx(railCls, `${railCls}-${status}`, className)} style={style} />;
}
================================================
FILE: src/Step.tsx
================================================
/* eslint react/prop-types: 0 */
import * as React from 'react';
import { clsx } from 'clsx';
import KeyCode from '@rc-component/util/lib/KeyCode';
import type { Status, StepItem, StepsProps } from './Steps';
import Rail from './Rail';
import { UnstableContext } from './UnstableContext';
import StepIcon, { StepIconSemanticContext } from './StepIcon';
import { StepsContext } from './Context';
function hasContent<T>(value: T) {
return value !== undefined && value !== null;
}
export interface StepProps {
// style
prefixCls?: string;
classNames: StepsProps['classNames'];
styles: StepsProps['styles'];
// data
data: StepItem;
nextStatus?: Status;
active?: boolean;
index: number;
last: boolean;
// render
iconRender?: StepsProps['iconRender'];
icon?: React.ReactNode;
itemRender?: StepsProps['itemRender'];
itemWrapperRender?: StepsProps['itemWrapperRender'];
// Event
onClick: (index: number) => void;
}
export default function Step(props: StepProps) {
const {
// style
prefixCls,
classNames,
styles,
// data
data,
last,
nextStatus,
active,
index,
// render
itemRender,
iconRender,
itemWrapperRender,
// events
onClick,
} = props;
const itemCls = `${prefixCls}-item`;
// ======================== Contexts ========================
const { railFollowPrevStatus } = React.useContext(UnstableContext);
const { ItemComponent } = React.useContext(StepsContext);
// ========================== Data ==========================
const {
onClick: onItemClick,
title,
subTitle,
content,
description,
disabled,
icon,
status,
className,
style,
classNames: itemClassNames = {},
styles: itemStyles = {},
...restItemProps
} = data;
const mergedContent = content ?? description;
const renderInfo = {
item: {
...data,
content: mergedContent,
},
index,
active,
};
// ========================= Click ==========================
const clickable = !!(onClick || onItemClick) && !disabled;
const accessibilityProps: {
role?: string;
tabIndex?: number;
onClick?: React.MouseEventHandler<HTMLLIElement>;
onKeyDown?: React.KeyboardEventHandler<HTMLLIElement>;
} = {};
if (clickable) {
accessibilityProps.role = 'button';
accessibilityProps.tabIndex = 0;
accessibilityProps.onClick = (e) => {
onItemClick?.(e);
onClick(index);
};
accessibilityProps.onKeyDown = (e) => {
const { which } = e;
if (which === KeyCode.ENTER || which === KeyCode.SPACE) {
onClick(index);
}
};
}
// ========================= Render =========================
const mergedStatus = status || 'wait';
const hasTitle = hasContent(title);
const hasSubTitle = hasContent(subTitle);
const classString = clsx(
itemCls,
`${itemCls}-${mergedStatus}`,
{
[`${itemCls}-custom`]: icon,
[`${itemCls}-active`]: active,
[`${itemCls}-disabled`]: disabled === true,
[`${itemCls}-empty-header`]: !hasTitle && !hasSubTitle,
},
className,
classNames.item,
itemClassNames.root,
);
let iconNode = <StepIcon />;
if (iconRender) {
iconNode = iconRender(iconNode, {
...renderInfo,
components: {
Icon: StepIcon,
},
}) as React.ReactElement;
}
const wrapperNode = (
<div
className={clsx(`${itemCls}-wrapper`, classNames.itemWrapper, itemClassNames.wrapper)}
style={{ ...styles.itemWrapper, ...itemStyles.wrapper }}
>
{/* Icon */}
<StepIconSemanticContext.Provider
value={{
className: itemClassNames.icon,
style: itemStyles.icon,
}}
>
{iconNode}
</StepIconSemanticContext.Provider>
<div
className={clsx(`${itemCls}-section`, classNames.itemSection, itemClassNames.section)}
style={{ ...styles.itemSection, ...itemStyles.section }}
>
<div
className={clsx(`${itemCls}-header`, classNames.itemHeader, itemClassNames.header)}
style={{ ...styles.itemHeader, ...itemStyles.header }}
>
{hasTitle && (
<div
className={clsx(`${itemCls}-title`, classNames.itemTitle, itemClassNames.title)}
style={{ ...styles.itemTitle, ...itemStyles.title }}
>
{title}
</div>
)}
{hasSubTitle && (
<div
title={typeof subTitle === 'string' ? subTitle : undefined}
className={clsx(
`${itemCls}-subtitle`,
classNames.itemSubtitle,
itemClassNames.subtitle,
)}
style={{ ...styles.itemSubtitle, ...itemStyles.subtitle }}
>
{subTitle}
</div>
)}
{!last && (
<Rail
prefixCls={itemCls}
className={clsx(classNames.itemRail, itemClassNames.rail)}
style={{ ...styles.itemRail, ...itemStyles.rail }}
status={railFollowPrevStatus ? status : nextStatus}
/>
)}
</div>
{hasContent(mergedContent) && (
<div
className={clsx(`${itemCls}-content`, classNames.itemContent, itemClassNames.content)}
style={{ ...styles.itemContent, ...itemStyles.content }}
>
{mergedContent}
</div>
)}
</div>
</div>
);
let stepNode: React.ReactNode = (
<ItemComponent
{...restItemProps}
{...accessibilityProps}
className={classString}
style={{
...styles.item,
...itemStyles.root,
...style,
}}
>
{itemWrapperRender ? itemWrapperRender(wrapperNode) : wrapperNode}
</ItemComponent>
);
if (itemRender) {
stepNode = (itemRender(stepNode, renderInfo) || null) as React.ReactElement;
}
return stepNode;
}
================================================
FILE: src/StepIcon.tsx
================================================
import * as React from 'react';
import { clsx } from 'clsx';
import { StepsContext } from './Context';
import pickAttrs from '@rc-component/util/lib/pickAttrs';
export interface StepIconSemanticContextProps {
className?: string;
style?: React.CSSProperties;
}
export const StepIconSemanticContext = React.createContext<StepIconSemanticContextProps>({});
export type StepIconProps = React.HTMLAttributes<HTMLDivElement>;
const StepIcon = React.forwardRef<HTMLDivElement, StepIconProps>((props, ref) => {
const { className, style, children, ...restProps } = props;
const { prefixCls, classNames, styles } = React.useContext(StepsContext);
const { className: itemClassName, style: itemStyle } = React.useContext(StepIconSemanticContext);
const itemCls = `${prefixCls}-item`;
return (
<div
{...pickAttrs(restProps, false)}
ref={ref}
className={clsx(`${itemCls}-icon`, classNames.itemIcon, itemClassName, className)}
style={{ ...styles.itemIcon, ...itemStyle, ...style }}
>
{children}
</div>
);
});
export default StepIcon;
================================================
FILE: src/Steps.tsx
================================================
/* eslint react/no-did-mount-set-state: 0, react/prop-types: 0 */
import { clsx } from 'clsx';
import React from 'react';
import Step from './Step';
import { StepsContext, type StepsContextProps } from './Context';
import type StepIcon from './StepIcon';
export type Status = 'error' | 'process' | 'finish' | 'wait';
const EmptyObject = {};
export type SemanticName =
| 'root'
| 'item'
| 'itemWrapper'
| 'itemHeader'
| 'itemTitle'
| 'itemSubtitle'
| 'itemSection'
| 'itemContent'
| 'itemIcon'
| 'itemRail';
export type ItemSemanticName =
| 'root'
| 'wrapper'
| 'header'
| 'title'
| 'subtitle'
| 'section'
| 'content'
| 'icon'
| 'rail';
export type ComponentType = React.ComponentType<any> | string;
export type StepItem = {
/** @deprecated Please use `content` instead. */
description?: React.ReactNode;
content?: React.ReactNode;
disabled?: boolean;
icon?: React.ReactNode;
status?: Status;
subTitle?: React.ReactNode;
title?: React.ReactNode;
classNames?: Partial<Record<ItemSemanticName, string>>;
styles?: Partial<Record<ItemSemanticName, React.CSSProperties>>;
} & Pick<React.HtmlHTMLAttributes<HTMLLIElement>, 'onClick' | 'className' | 'style'>;
export type StepIconRender = (info: {
index: number;
status: Status;
title: React.ReactNode;
// @deprecated Please use `content` instead.
description: React.ReactNode;
content: React.ReactNode;
node: React.ReactNode;
}) => React.ReactNode;
export type RenderInfo = {
index: number;
active: boolean;
item: StepItem;
};
export interface StepsProps {
// style
prefixCls?: string;
style?: React.CSSProperties;
className?: string;
classNames?: Partial<Record<SemanticName, string>>;
styles?: Partial<Record<SemanticName, React.CSSProperties>>;
rootClassName?: string;
// layout
orientation?: 'horizontal' | 'vertical';
titlePlacement?: 'horizontal' | 'vertical';
// a11y
/** Internal usage of antd. Do not deps on this. */
components?: {
root?: ComponentType;
item?: ComponentType;
};
// data
status?: Status;
current?: number;
initial?: number;
items?: StepItem[];
onChange?: (current: number) => void;
// render
iconRender?: (
originNode: React.ReactElement,
info: RenderInfo & {
components: {
Icon: typeof StepIcon;
};
},
) => React.ReactNode;
itemRender?: (originNode: React.ReactElement, info: RenderInfo) => React.ReactNode;
itemWrapperRender?: (originNode: React.ReactElement) => React.ReactNode;
}
export default function Steps(props: StepsProps) {
const {
// style
prefixCls = 'rc-steps',
style,
className,
classNames = EmptyObject as NonNullable<StepsProps['classNames']>,
styles = EmptyObject as NonNullable<StepsProps['styles']>,
rootClassName,
// layout
orientation,
titlePlacement,
components,
// data
status = 'process',
current = 0,
initial = 0,
onChange,
items,
// render
iconRender,
itemRender,
itemWrapperRender,
...restProps
} = props;
// ============================= layout =============================
const isVertical = orientation === 'vertical';
const mergedOrientation = isVertical ? 'vertical' : 'horizontal';
const mergeTitlePlacement =
!isVertical && titlePlacement === 'vertical' ? 'vertical' : 'horizontal';
// ============================= styles =============================
const classString = clsx(
prefixCls,
`${prefixCls}-${mergedOrientation}`,
`${prefixCls}-title-${mergeTitlePlacement}`,
rootClassName,
className,
classNames.root,
);
// ============================== Data ==============================
const mergedItems = React.useMemo(() => (items || []).filter(Boolean), [items]);
const statuses = React.useMemo(
() =>
mergedItems.map(({ status: itemStatus }, index) => {
const stepNumber = initial + index;
if (!itemStatus) {
if (stepNumber === current) {
return status;
} else if (stepNumber < current) {
return 'finish';
}
return 'wait';
}
return itemStatus;
}),
[mergedItems, status, current, initial],
);
// ============================= events =============================
const onStepClick = (next: number) => {
if (onChange && current !== next) {
onChange(next);
}
};
// =========================== components ===========================
const { root: RootComponent = 'div', item: ItemComponent = 'div' } = components || {};
// ============================ contexts ============================
const stepIconContext = React.useMemo<StepsContextProps>(
() => ({
prefixCls,
classNames,
styles,
ItemComponent,
}),
[prefixCls, classNames, styles, ItemComponent],
);
// ============================= render =============================
const renderStep = (item: StepItem, index: number) => {
const stepIndex = initial + index;
const itemStatus = statuses[index];
const nextStatus = statuses[index + 1];
const data = {
...item,
status: itemStatus,
};
return (
<Step
key={stepIndex}
// Style
prefixCls={prefixCls}
classNames={classNames}
styles={styles}
// Data
data={data}
nextStatus={nextStatus}
active={stepIndex === current}
index={stepIndex}
last={mergedItems.length - 1 === index}
// Render
iconRender={iconRender}
itemRender={itemRender}
itemWrapperRender={itemWrapperRender}
onClick={onChange && onStepClick}
/>
);
};
return (
<RootComponent
className={classString}
style={{
...style,
...styles?.root,
}}
{...restProps}
>
<StepsContext.Provider value={stepIconContext}>
{mergedItems.map<React.ReactNode>(renderStep)}
</StepsContext.Provider>
</RootComponent>
);
}
================================================
FILE: src/UnstableContext.ts
================================================
import * as React from 'react';
export interface UnstableContextProps {
/**
* Used for Timeline component `reverse` prop.
* Safe to remove if refactor.
*/
railFollowPrevStatus?: boolean;
}
export const UnstableContext = React.createContext<UnstableContextProps>({});
================================================
FILE: src/index.ts
================================================
import Steps, { type StepsProps } from './Steps';
import Step from './Step';
export { Step };
export type { StepsProps };
export { UnstableContext } from './UnstableContext';
export default Steps;
================================================
FILE: src/interface.ts
================================================
================================================
FILE: tests/__snapshots__/index.test.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Steps components 1`] = `
<ol
class="rc-steps rc-steps-horizontal rc-steps-title-horizontal"
>
<li
class="rc-steps-item rc-steps-item-process rc-steps-item-active"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
test
</div>
</div>
</div>
</div>
</li>
</ol>
`;
exports[`Steps render renders correctly 1`] = `
<div
class="rc-steps rc-steps-horizontal rc-steps-title-horizontal"
>
<div
class="rc-steps-item rc-steps-item-process rc-steps-item-active"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
已完成
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
进行中
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`Steps render renders current correctly 1`] = `
<div
class="rc-steps rc-steps-horizontal rc-steps-title-horizontal"
>
<div
class="rc-steps-item rc-steps-item-finish"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
已完成
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-finish"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-finish"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
进行中
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-process"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-process rc-steps-item-active"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`Steps render renders progressDot correctly 1`] = `
<div
class="rc-steps rc-steps-horizontal rc-steps-title-horizontal"
>
<div
class="rc-steps-item rc-steps-item-process rc-steps-item-active"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
已完成
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
进行中
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`Steps render renders progressDot function correctly 1`] = `
<div
class="rc-steps rc-steps-horizontal rc-steps-title-horizontal"
>
<div
class="rc-steps-item rc-steps-item-process rc-steps-item-active"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
已完成
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
进行中
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`Steps render renders status correctly 1`] = `
<div
class="rc-steps rc-steps-horizontal rc-steps-title-horizontal"
>
<div
class="rc-steps-item rc-steps-item-finish"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
已完成
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-finish"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-finish"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
进行中
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-error"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-error rc-steps-item-active"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`Steps render renders step with description 1`] = `
<div
class="rc-steps rc-steps-horizontal rc-steps-title-horizontal"
>
<div
class="rc-steps-item rc-steps-item-process rc-steps-item-active"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
已完成
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
<div
class="rc-steps-item-content"
>
xx
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
进行中
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
<div
class="rc-steps-item-content"
>
xx
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
<div
class="rc-steps-item-content"
>
xx
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
</div>
<div
class="rc-steps-item-content"
>
xx
</div>
</div>
</div>
</div>
</div>
`;
exports[`Steps render renders step with description and status 1`] = `
<div
class="rc-steps rc-steps-horizontal rc-steps-title-horizontal"
>
<div
class="rc-steps-item rc-steps-item-wait rc-steps-item-active"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
已完成
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
<div
class="rc-steps-item-content"
>
xx
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
进行中
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-process"
/>
</div>
<div
class="rc-steps-item-content"
>
xx
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-process"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-finish"
/>
</div>
<div
class="rc-steps-item-content"
>
xx
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-finish"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
</div>
<div
class="rc-steps-item-content"
>
xx
</div>
</div>
</div>
</div>
</div>
`;
exports[`Steps render renders stepIcon function correctly 1`] = `
<div
class="rc-steps rc-steps-horizontal rc-steps-title-horizontal"
>
<div
class="rc-steps-item rc-steps-item-process rc-steps-item-active"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
已完成
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
进行中
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`Steps render renders titlePlacement correctly 1`] = `
<div
class="rc-steps rc-steps-horizontal rc-steps-title-vertical"
>
<div
class="rc-steps-item rc-steps-item-process rc-steps-item-active"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
已完成
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
进行中
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`Steps render renders vertical correctly 1`] = `
<div
class="rc-steps rc-steps-horizontal rc-steps-title-horizontal"
direction="vertical"
>
<div
class="rc-steps-item rc-steps-item-process rc-steps-item-active"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
已完成
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
进行中
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`Steps render renders with falsy children 1`] = `
<div
class="rc-steps rc-steps-horizontal rc-steps-title-horizontal"
>
<div
class="rc-steps-item rc-steps-item-wait rc-steps-item-active"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
已完成
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
<div
class="rc-steps-item-content"
>
xx
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
进行中
</div>
<div
class="rc-steps-item-subtitle"
title="剩余 00:00:07"
>
剩余 00:00:07
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-process"
/>
</div>
<div
class="rc-steps-item-content"
>
xx
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-process"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-finish"
/>
</div>
<div
class="rc-steps-item-content"
>
xx
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-finish rc-steps-item-disabled"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
待运行
</div>
</div>
<div
class="rc-steps-item-content"
>
xx
</div>
</div>
</div>
</div>
</div>
`;
exports[`Steps should render customIcon correctly 1`] = `
<div
class="rc-steps rc-steps-horizontal rc-steps-title-horizontal"
>
<div
class="rc-steps-item rc-steps-item-finish rc-steps-item-custom"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
步骤1
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-process"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-process rc-steps-item-custom rc-steps-item-active"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
步骤2
</div>
<div
class="rc-steps-item-rail rc-steps-item-rail-wait"
/>
</div>
</div>
</div>
</div>
<div
class="rc-steps-item rc-steps-item-wait rc-steps-item-custom"
>
<div
class="rc-steps-item-wrapper"
>
<div
class="rc-steps-item-icon"
/>
<div
class="rc-steps-item-section"
>
<div
class="rc-steps-item-header"
>
<div
class="rc-steps-item-title"
>
步骤3
</div>
</div>
</div>
</div>
</div>
</div>
`;
================================================
FILE: tests/index.test.tsx
================================================
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import Steps from '../src';
describe('Steps', () => {
describe('render', () => {
const description = 'xx';
const setSteps = (props) => (
<Steps
items={[
{
title: '已完成',
},
{
title: '进行中',
},
{
title: '待运行',
},
{
title: '待运行',
},
]}
{...props}
/>
);
it('renders correctly', () => {
const { container } = render(setSteps({}));
expect(container.firstChild).toMatchSnapshot();
});
it('renders without items', () => {
expect(() => {
render(setSteps({ items: undefined }));
}).not.toThrow();
});
it('renders current correctly', () => {
const { container } = render(setSteps({ current: 2 }));
expect(container.firstChild).toMatchSnapshot();
});
it('renders status correctly', () => {
const { container } = render(setSteps({ current: 2, status: 'error' }));
expect(container.firstChild).toMatchSnapshot();
});
it('renders vertical correctly', () => {
const { container } = render(setSteps({ direction: 'vertical' }));
expect(container.firstChild).toMatchSnapshot();
});
it('renders titlePlacement correctly', () => {
const { container } = render(setSteps({ titlePlacement: 'vertical' }));
expect(container.firstChild).toMatchSnapshot();
});
it('renders progressDot correctly', () => {
const { container } = render(setSteps({ progressDot: true }));
expect(container.firstChild).toMatchSnapshot();
});
it('renders progressDot function correctly', () => {
const { container } = render(setSteps({ progressDot: () => <span>a</span> }));
expect(container.firstChild).toMatchSnapshot();
});
it('renders stepIcon function correctly', () => {
const { container } = render(setSteps({ stepIcon: () => <span>a</span> }));
expect(container.firstChild).toMatchSnapshot();
});
it('renders step with description', () => {
const { container } = render(
<Steps
items={[
{
title: '已完成',
description,
},
{
title: '进行中',
description,
},
{
title: '待运行',
description,
},
{
title: '待运行',
description,
},
]}
/>,
);
expect(container.firstChild).toMatchSnapshot();
});
it('renders step with description and status', () => {
const { container } = render(
<Steps
items={[
{
title: '已完成',
description,
status: 'wait',
},
{
title: '进行中',
description,
status: 'wait',
},
{
title: '待运行',
description,
status: 'process',
},
{
title: '待运行',
description,
status: 'finish',
},
]}
/>,
);
expect(container.firstChild).toMatchSnapshot();
});
it('renders with falsy children', () => {
const { container } = render(
<Steps
items={[
{
title: '已完成',
description: 'xx',
status: 'wait',
},
{
title: '进行中',
description: 'xx',
status: 'wait',
subTitle: '剩余 00:00:07',
},
undefined,
{
title: '待运行',
description: 'xx',
status: 'process',
},
// @ts-ignore
false,
{
title: '待运行',
description: 'xx',
status: 'finish',
disabled: true,
},
null,
]}
/>,
);
expect(container.firstChild).toMatchSnapshot();
});
});
it('should render customIcon correctly', () => {
const Icon = ({ type }) => <i className={`rcicon rcicon-${type}`} />;
const { container } = render(
<Steps
current={1}
items={[
{
title: '步骤1',
icon: <Icon type="cloud" />,
},
{
title: '步骤2',
icon: 'apple',
},
{
title: '步骤3',
icon: 'github',
},
]}
/>,
);
expect(container.firstChild).toMatchSnapshot();
});
it('onChange', () => {
const onChange = jest.fn();
const { container } = render(
<Steps
onChange={onChange}
items={[
{
title: '已完成',
},
{
title: '进行中',
},
{
title: '待运行',
},
{
title: '待运行',
},
]}
/>,
);
const items = container.querySelectorAll('.rc-steps-item');
fireEvent.click(items[1]);
expect(onChange).toHaveBeenCalledTimes(1);
});
it('items out of render function', () => {
const items = [
{
title: '已完成',
},
{
title: '进行中',
},
];
let current = 0;
const onChange = (val) => {
current = val;
};
const { container, rerender } = render(
<Steps current={current} onChange={onChange} items={items} key={current} />,
);
const step = container.querySelectorAll('.rc-steps-item')[1];
fireEvent.click(step);
rerender(<Steps current={current} onChange={onChange} items={items} key={current} />);
expect(container.querySelectorAll('.rc-steps-item')[1].classList).toContain(
'rc-steps-item-process',
);
});
it('onClick', () => {
const onClick = jest.fn();
const onChange = jest.fn();
const { container } = render(
<Steps
onChange={onChange}
items={[
{
title: '已完成',
onClick,
},
{
title: '进行中',
},
{
title: '待运行',
},
{
title: '待运行',
},
]}
/>,
);
const btn = container.querySelectorAll('.rc-steps-item')[0];
fireEvent.click(btn);
expect(onClick).toHaveBeenCalled();
});
it('disabled', () => {
const onChange = jest.fn();
const { container } = render(
<Steps onChange={onChange} items={[{}, {}, { disabled: true }]} />,
);
const items = container.querySelectorAll('.rc-steps-item');
fireEvent.click(items[2]);
expect(onChange).not.toBeCalled();
});
it('key board support', () => {
const onChange = jest.fn();
const { container } = render(
<Steps
current={0}
onChange={onChange}
items={[
{
title: 'Finished',
description: 'This is a description',
},
{
title: 'Waiting',
description: 'This is a description',
},
]}
/>,
);
const button = container.querySelectorAll('[role="button"]')[1];
fireEvent.keyDown(button, { key: 'Enter', keyCode: 13, which: 13 });
expect(onChange).toHaveBeenCalledWith(1);
});
it('itemRender', () => {
const { container } = render(
<Steps
items={[
{
title: 'test',
},
]}
itemRender={(oriNode) => {
return <div className="bamboo">{oriNode}</div>;
}}
/>,
);
expect(container.querySelector('.bamboo')).toBeTruthy();
expect(container.querySelectorAll('.bamboo')).toHaveLength(1);
expect(container.querySelector('.rc-steps-item')).toBeTruthy();
});
it('itemWrapperRender', () => {
const { container } = render(
<Steps
items={[
{
title: 'test',
},
]}
itemWrapperRender={(oriNode) => {
return <div className="bamboo">{oriNode}</div>;
}}
/>,
);
expect(container.querySelector('.bamboo')).toBeTruthy();
expect(container.querySelectorAll('.bamboo')).toHaveLength(1);
expect(container.querySelector('.rc-steps-item')).toBeTruthy();
expect(container.querySelector('.rc-steps-item > .bamboo')).toBeTruthy();
});
it('iconRender', () => {
const { container } = render(
<Steps
items={[
{
title: 'test',
},
]}
iconRender={(_, { components: { Icon } }) => {
return <Icon className="bamboo">little</Icon>;
}}
/>,
);
const iconEle = container.querySelector('.rc-steps-item-icon')!;
expect(iconEle).toHaveClass('bamboo');
expect(iconEle.textContent).toBe('little');
});
it('components', () => {
const { container } = render(
<Steps
components={{
root: 'ol',
item: 'li',
}}
items={[
{
title: 'test',
},
]}
/>,
);
expect(container.firstChild).toMatchSnapshot();
});
});
================================================
FILE: tests/semantic.test.tsx
================================================
import React from 'react';
import { render } from '@testing-library/react';
import Steps, { type StepsProps } from '../src';
import type { ItemSemanticName, SemanticName } from '../src/Steps';
describe('Steps.Semantic', () => {
const renderSteps = (props: Partial<StepsProps>) => (
<Steps
items={Array.from({ length: 3 }, (_, index) => ({
title: `Step ${index + 1}`,
subTitle: `SubTitle ${index + 1}`,
content: `Content ${index + 1}`,
}))}
{...props}
/>
);
it('semantic structure', () => {
const classNames: Record<SemanticName, string> = {
root: 'custom-root',
item: 'custom-item',
itemWrapper: 'custom-item-wrapper',
itemIcon: 'custom-item-icon',
itemSection: 'custom-item-section',
itemHeader: 'custom-item-header',
itemTitle: 'custom-item-title',
itemSubtitle: 'custom-item-subtitle',
itemContent: 'custom-item-content',
itemRail: 'custom-item-rail',
};
const classNamesTargets: Record<SemanticName, string> = {
root: 'rc-steps',
item: 'rc-steps-item',
itemWrapper: 'rc-steps-item-wrapper',
itemIcon: 'rc-steps-item-icon',
itemSection: 'rc-steps-item-section',
itemHeader: 'rc-steps-item-header',
itemTitle: 'rc-steps-item-title',
itemSubtitle: 'rc-steps-item-subtitle',
itemContent: 'rc-steps-item-content',
itemRail: 'rc-steps-item-rail',
};
const styles: Record<SemanticName, Record<string, any>> = {
root: { color: 'red' },
item: { color: 'blue' },
itemWrapper: { color: 'green' },
itemIcon: { color: 'yellow' },
itemSection: { color: 'purple' },
itemHeader: { color: 'orange' },
itemTitle: { color: 'pink' },
itemSubtitle: { color: 'cyan' },
itemContent: { color: 'magenta' },
itemRail: { color: 'lime' },
};
const { container } = render(
renderSteps({
classNames,
styles,
}),
);
Object.keys(classNames).forEach((key) => {
const className = classNames[key as SemanticName];
const oriClassName = classNamesTargets[key as SemanticName];
const style = styles[key as SemanticName];
const element = container.querySelector<HTMLElement>(`.${className}`);
expect(element).toBeTruthy();
expect(element).toHaveClass(oriClassName);
expect(element).toHaveStyle(style);
});
});
it('item semantic structure', () => {
const classNames: Record<ItemSemanticName, string> = {
root: 'custom-root',
wrapper: 'custom-wrapper',
header: 'custom-header',
title: 'custom-title',
subtitle: 'custom-subtitle',
section: 'custom-section',
content: 'custom-content',
icon: 'custom-icon',
rail: 'custom-rail',
};
const classNamesTargets: Record<ItemSemanticName, string> = {
root: 'rc-steps-item',
wrapper: 'rc-steps-item-wrapper',
header: 'rc-steps-item-header',
title: 'rc-steps-item-title',
subtitle: 'rc-steps-item-subtitle',
section: 'rc-steps-item-section',
content: 'rc-steps-item-content',
icon: 'rc-steps-item-icon',
rail: 'rc-steps-item-rail',
};
const styles: Record<ItemSemanticName, Record<string, any>> = {
root: { color: 'red' },
wrapper: { color: 'green' },
header: { color: 'orange' },
title: { color: 'pink' },
subtitle: { color: 'cyan' },
section: { color: 'purple' },
content: { color: 'magenta' },
icon: { color: 'yellow' },
rail: { color: 'lime' },
};
const { container } = render(
renderSteps({
items: Array.from({ length: 2 }, (_, index) => ({
title: `Title ${index + 1}`,
subTitle: `SubTitle ${index + 1}`,
content: `Content ${index + 1}`,
classNames,
styles,
})),
}),
);
Object.keys(classNames).forEach((key) => {
const className = classNames[key as SemanticName];
const oriClassName = classNamesTargets[key as SemanticName];
const style = styles[key as SemanticName];
const element = container.querySelector<HTMLElement>(`.${className}`);
expect(element).toBeTruthy();
expect(element).toHaveClass(oriClassName);
expect(element).toHaveStyle(style);
});
});
});
================================================
FILE: tests/setup.js
================================================
global.requestAnimationFrame = global.requestAnimationFrame || function raf(cb) {
return setTimeout(cb, 0);
};
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"target": "esnext",
"moduleResolution": "node",
"baseUrl": "./",
"jsx": "preserve",
"declaration": true,
"skipLibCheck": true,
"esModuleInterop": true,
"paths": {
"@/*": ["src/*"],
"@@/*": ["dumi/tmp/*"],
"@rc-component/steps": ["src/index.ts"]
}
}
}
gitextract_zrdxiemy/ ├── .dumirc.ts ├── .editorconfig ├── .eslintrc.js ├── .fatherrc.ts ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ └── main.yml ├── .gitignore ├── .prettierrc ├── HISTORY.md ├── LICENSE.md ├── README.md ├── assets/ │ ├── custom-icon.less │ ├── iconfont.less │ ├── index.less │ ├── inline.less │ ├── label-placement.less │ ├── nav.less │ ├── progress-dot.less │ ├── small.less │ ├── variables.less │ └── vertical.less ├── bunfig.toml ├── docs/ │ ├── demo/ │ │ ├── alternativeLabel.md │ │ ├── composable.md │ │ ├── custom-svg-icon.md │ │ ├── customIcon.md │ │ ├── dynamic.md │ │ ├── errorStep.md │ │ ├── inline.md │ │ ├── nav-base.md │ │ ├── nextStep.md │ │ ├── progressDot.md │ │ ├── simple.md │ │ ├── smallSize.md │ │ ├── stepIcon.md │ │ ├── vertical.md │ │ └── verticalSmall.md │ ├── examples/ │ │ ├── alternativeLabel.jsx │ │ ├── composable.jsx │ │ ├── custom-svg-icon.jsx │ │ ├── customIcon.jsx │ │ ├── dynamic.jsx │ │ ├── errorStep.jsx │ │ ├── inline.jsx │ │ ├── nav-base.jsx │ │ ├── nextStep.css │ │ ├── nextStep.jsx │ │ ├── progressDot.jsx │ │ ├── simple.jsx │ │ ├── smallSize.jsx │ │ ├── stepIcon.jsx │ │ ├── vertical.jsx │ │ └── verticalSmall.jsx │ └── index.md ├── index.js ├── jest.config.js ├── package.json ├── script/ │ └── update-content.js ├── src/ │ ├── Context.ts │ ├── Rail.tsx │ ├── Step.tsx │ ├── StepIcon.tsx │ ├── Steps.tsx │ ├── UnstableContext.ts │ ├── index.ts │ └── interface.ts ├── tests/ │ ├── __snapshots__/ │ │ └── index.test.tsx.snap │ ├── index.test.tsx │ ├── semantic.test.tsx │ └── setup.js └── tsconfig.json
SYMBOL INDEX (24 symbols across 9 files)
FILE: docs/examples/custom-svg-icon.jsx
function getFinishIcon (line 6) | function getFinishIcon() {
function getErrorIcon (line 31) | function getErrorIcon() {
FILE: docs/examples/nextStep.jsx
function generateRandomSteps (line 7) | function generateRandomSteps() {
class MyForm (line 19) | class MyForm extends React.Component {
method render (line 35) | render() {
FILE: docs/examples/stepIcon.jsx
function stepIcon (line 6) | function stepIcon({ status, node }) {
FILE: src/Context.ts
type StepsContextProps (line 4) | interface StepsContextProps {
FILE: src/Rail.tsx
type RailProps (line 5) | interface RailProps {
function Rail (line 12) | function Rail(props: RailProps) {
FILE: src/Step.tsx
function hasContent (line 11) | function hasContent<T>(value: T) {
type StepProps (line 15) | interface StepProps {
function Step (line 38) | function Step(props: StepProps) {
FILE: src/StepIcon.tsx
type StepIconSemanticContextProps (line 6) | interface StepIconSemanticContextProps {
type StepIconProps (line 13) | type StepIconProps = React.HTMLAttributes<HTMLDivElement>;
FILE: src/Steps.tsx
type Status (line 8) | type Status = 'error' | 'process' | 'finish' | 'wait';
type SemanticName (line 12) | type SemanticName =
type ItemSemanticName (line 24) | type ItemSemanticName =
type ComponentType (line 35) | type ComponentType = React.ComponentType<any> | string;
type StepItem (line 37) | type StepItem = {
type StepIconRender (line 50) | type StepIconRender = (info: {
type RenderInfo (line 60) | type RenderInfo = {
type StepsProps (line 66) | interface StepsProps {
function Steps (line 106) | function Steps(props: StepsProps) {
FILE: src/UnstableContext.ts
type UnstableContextProps (line 3) | interface UnstableContextProps {
Condensed preview — 71 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (116K chars).
[
{
"path": ".dumirc.ts",
"chars": 416,
"preview": "import { defineConfig } from 'dumi';\nimport path from 'path';\n\nexport default defineConfig({\n alias: {\n '@rc-compone"
},
{
"path": ".editorconfig",
"chars": 191,
"preview": "# top-most EditorConfig file\nroot = true\n\n# Unix-style newlines with a newline ending every file\n[*.{js,css}]\nend_of_lin"
},
{
"path": ".eslintrc.js",
"chars": 169,
"preview": "const base = require('@umijs/fabric/dist/eslint');\n\nmodule.exports = {\n ...base,\n rules: {\n ...base.rules,\n 'arr"
},
{
"path": ".fatherrc.ts",
"chars": 118,
"preview": "import { defineConfig } from 'father';\n\nexport default defineConfig({\n plugins: ['@rc-component/father-plugin'],\n});\n"
},
{
"path": ".github/dependabot.yml",
"chars": 400,
"preview": "version: 2\nupdates:\n- package-ecosystem: npm\n directory: \"/\"\n schedule:\n interval: daily\n time: \"21:00\"\n open-p"
},
{
"path": ".github/workflows/main.yml",
"chars": 138,
"preview": "name: ✅ test\non: [push, pull_request]\njobs:\n test:\n uses: react-component/rc-test/.github/workflows/test.yml@main\n "
},
{
"path": ".gitignore",
"chars": 326,
"preview": ".iml\n*.log\n.idea/\n.ipr\n.iws\n*~\n~*\n*.diff\n*.patch\n*.bak\n.DS_Store\nThumbs.db\n.project\n.*proj\n.svn/\n*.swp\n*.swo\n*.pyc\n*.pyo"
},
{
"path": ".prettierrc",
"chars": 96,
"preview": "{\n \"singleQuote\": true,\n \"trailingComma\": \"all\",\n \"proseWrap\": \"never\",\n \"printWidth\": 100\n}"
},
{
"path": "HISTORY.md",
"chars": 1249,
"preview": "# History\n----\n\n## 3.6.0\n\n- Remove babel-runtime and prop-types\n- Fix icon missing #85\n\n## 3.5.0\n\n- Support `navigation`"
},
{
"path": "LICENSE.md",
"chars": 1083,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2014-present yiminghe\n\nPermission is hereby granted, free of charge, to any person "
},
{
"path": "README.md",
"chars": 4535,
"preview": "# @rc-component/steps\n\n---\n\nReact steps component.\n\n[![NPM version][npm-image]][npm-url]\n[![build status][travis-image]]"
},
{
"path": "assets/custom-icon.less",
"chars": 436,
"preview": "@import 'variables';\n\n.@{stepsPrefixClass}-item-custom {\n .@{stepsPrefixClass}-item-icon {\n background: none;\n bo"
},
{
"path": "assets/iconfont.less",
"chars": 9287,
"preview": "@icon-url : \"//at.alicdn.com/t/font_1434092639_4910953\";\n.ie-rotate(@rotation) {\n filter: progid:DXImageT"
},
{
"path": "assets/index.less",
"chars": 3946,
"preview": "@import 'variables';\n\n.@{stepsPrefixClass} {\n font-size: 0;\n width: 100%;\n line-height: 1.5;\n display: flex;\n\n &,\n "
},
{
"path": "assets/inline.less",
"chars": 1016,
"preview": "@import 'variables';\n\n.@{stepsPrefixClass}-inline {\n width: auto;\n display: inline-flex;\n\n .@{stepsPrefixClass}-item "
},
{
"path": "assets/label-placement.less",
"chars": 465,
"preview": "@import 'variables';\n\n.@{stepsPrefixClass}-label-vertical {\n .@{stepsPrefixClass}-item {\n overflow: visible;\n &-s"
},
{
"path": "assets/nav.less",
"chars": 938,
"preview": "@import 'variables';\n\n.@{stepsPrefixClass}-navigation {\n padding-top: 8px;\n &.@{stepsPrefixClass}-horizontal {\n .@{"
},
{
"path": "assets/progress-dot.less",
"chars": 554,
"preview": "@import 'variables';\n\n.@{stepsPrefixClass}-dot {\n .@{stepsPrefixClass}-item {\n &-icon {\n padding-right: 0;\n "
},
{
"path": "assets/small.less",
"chars": 965,
"preview": "@import 'variables';\n\n.@{stepsPrefixClass}-small {\n .@{stepsPrefixClass}-item-icon {\n width: 18px;\n height: 18px;"
},
{
"path": "assets/variables.less",
"chars": 740,
"preview": "@stepsPrefixClass: ~\"rc-steps\";\n@stepDescriptionMaxWidth: 100px;\n@stepNavContentMaxWidth: 140px;\n@primary-color: #108ee9"
},
{
"path": "assets/vertical.less",
"chars": 585,
"preview": "@import 'variables';\n\n.@{stepsPrefixClass}-vertical {\n display: block;\n .@{stepsPrefixClass}-item {\n display: block"
},
{
"path": "bunfig.toml",
"chars": 22,
"preview": "[install]\npeer = false"
},
{
"path": "docs/demo/alternativeLabel.md",
"chars": 119,
"preview": "---\ntitle: alternativeLabel\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/alternativeLabel.jsx\"></code>\n"
},
{
"path": "docs/demo/composable.md",
"chars": 107,
"preview": "---\ntitle: composable\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/composable.jsx\"></code>\n"
},
{
"path": "docs/demo/custom-svg-icon.md",
"chars": 117,
"preview": "---\ntitle: custom-svg-icon\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/custom-svg-icon.jsx\"></code>\n"
},
{
"path": "docs/demo/customIcon.md",
"chars": 107,
"preview": "---\ntitle: customIcon\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/customIcon.jsx\"></code>\n"
},
{
"path": "docs/demo/dynamic.md",
"chars": 101,
"preview": "---\ntitle: dynamic\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/dynamic.jsx\"></code>\n"
},
{
"path": "docs/demo/errorStep.md",
"chars": 105,
"preview": "---\ntitle: errorStep\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/errorStep.jsx\"></code>\n"
},
{
"path": "docs/demo/inline.md",
"chars": 99,
"preview": "---\ntitle: inline\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/inline.jsx\"></code>\n"
},
{
"path": "docs/demo/nav-base.md",
"chars": 103,
"preview": "---\ntitle: nav-base\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/nav-base.jsx\"></code>\n"
},
{
"path": "docs/demo/nextStep.md",
"chars": 103,
"preview": "---\ntitle: nextStep\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/nextStep.jsx\"></code>\n"
},
{
"path": "docs/demo/progressDot.md",
"chars": 109,
"preview": "---\ntitle: progressDot\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/progressDot.jsx\"></code>\n"
},
{
"path": "docs/demo/simple.md",
"chars": 99,
"preview": "---\ntitle: simple\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/simple.jsx\"></code>\n"
},
{
"path": "docs/demo/smallSize.md",
"chars": 105,
"preview": "---\ntitle: smallSize\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/smallSize.jsx\"></code>\n"
},
{
"path": "docs/demo/stepIcon.md",
"chars": 103,
"preview": "---\ntitle: stepIcon\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/stepIcon.jsx\"></code>\n"
},
{
"path": "docs/demo/vertical.md",
"chars": 103,
"preview": "---\ntitle: vertical\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/vertical.jsx\"></code>\n"
},
{
"path": "docs/demo/verticalSmall.md",
"chars": 113,
"preview": "---\ntitle: verticalSmall\nnav:\n title: Demo\n path: /demo\n---\n\n<code src=\"../examples/verticalSmall.jsx\"></code>\n"
},
{
"path": "docs/examples/alternativeLabel.jsx",
"chars": 763,
"preview": "import '../../assets/index.less';\nimport '../../assets/iconfont.less';\nimport React from 'react';\nimport Steps from '@rc"
},
{
"path": "docs/examples/composable.jsx",
"chars": 586,
"preview": "import '../../assets/index.less';\nimport '../../assets/iconfont.less';\nimport React from 'react';\nimport Steps from '@rc"
},
{
"path": "docs/examples/custom-svg-icon.jsx",
"chars": 2690,
"preview": "import '../../assets/index.less';\nimport '../../assets/iconfont.less';\nimport React from 'react';\nimport Steps from '@rc"
},
{
"path": "docs/examples/customIcon.jsx",
"chars": 464,
"preview": "import '../../assets/index.less';\nimport '../../assets/iconfont.less';\nimport React from 'react';\nimport Steps from '@rc"
},
{
"path": "docs/examples/dynamic.jsx",
"chars": 968,
"preview": "import '../../assets/index.less';\nimport '../../assets/iconfont.less';\nimport React, { useState } from 'react';\nimport S"
},
{
"path": "docs/examples/errorStep.jsx",
"chars": 552,
"preview": "import '../../assets/index.less';\nimport '../../assets/iconfont.less';\nimport React from 'react';\nimport Steps from '@rc"
},
{
"path": "docs/examples/inline.jsx",
"chars": 936,
"preview": "import '../../assets/index.less';\nimport React, { useState } from 'react';\nimport Steps from '@rc-component/steps';\n\nexp"
},
{
"path": "docs/examples/nav-base.jsx",
"chars": 1651,
"preview": "import '../../assets/index.less';\nimport '../../assets/iconfont.less';\nimport React, { useState } from 'react';\nimport S"
},
{
"path": "docs/examples/nextStep.css",
"chars": 118,
"preview": ".my-step-form {\n width: 100%;\n}\n.my-step-form > div {\n margin-bottom: 20px;\n}\n.my-step-container {\n width: 100%;\n}\n"
},
{
"path": "docs/examples/nextStep.jsx",
"chars": 1416,
"preview": "import '../../assets/index.less';\nimport '../../assets/iconfont.less';\nimport './nextStep.css';\nimport React from 'react"
},
{
"path": "docs/examples/progressDot.jsx",
"chars": 626,
"preview": "import '../../assets/index.less';\nimport '../../assets/iconfont.less';\nimport React from 'react';\nimport Steps from '@rc"
},
{
"path": "docs/examples/simple.jsx",
"chars": 1863,
"preview": "import '../../assets/index.less';\nimport '../../assets/iconfont.less';\nimport React from 'react';\nimport Steps from '@rc"
},
{
"path": "docs/examples/smallSize.jsx",
"chars": 939,
"preview": "import '../../assets/index.less';\nimport '../../assets/iconfont.less';\nimport React from 'react';\nimport Steps from '@rc"
},
{
"path": "docs/examples/stepIcon.jsx",
"chars": 929,
"preview": "import '../../assets/index.less';\nimport '../../assets/iconfont.less';\nimport React from 'react';\nimport Steps from '@rc"
},
{
"path": "docs/examples/vertical.jsx",
"chars": 542,
"preview": "import '../../assets/index.less';\nimport '../../assets/iconfont.less';\nimport React from 'react';\nimport Steps from '@rc"
},
{
"path": "docs/examples/verticalSmall.jsx",
"chars": 559,
"preview": "import '../../assets/index.less';\nimport '../../assets/iconfont.less';\nimport React from 'react';\nimport Steps from '@rc"
},
{
"path": "docs/index.md",
"chars": 118,
"preview": "---\nhero:\n title: \"@rc-component/steps\"\n description: React Steps Component\n---\n\n<embed src=\"../README.md\"></embed>\n"
},
{
"path": "index.js",
"chars": 50,
"preview": "'use strict';\n\nmodule.exports = require('./src/');"
},
{
"path": "jest.config.js",
"chars": 57,
"preview": "module.exports = {\n setupFiles: ['./tests/setup.js'],\n};"
},
{
"path": "package.json",
"chars": 2332,
"preview": "{\n \"name\": \"@rc-component/steps\",\n \"version\": \"1.2.2\",\n \"description\": \"steps ui component for react\",\n \"keywords\": "
},
{
"path": "script/update-content.js",
"chars": 573,
"preview": "/*\n 用于 dumi 改造使用,\n 可用于将 examples 的文件批量修改为 demo 引入形式,\n 其他项目根据具体情况使用。\n*/\n\nconst fs = require('fs');\nconst glob = requir"
},
{
"path": "src/Context.ts",
"chars": 357,
"preview": "import * as React from 'react';\nimport type { ComponentType, StepsProps } from './Steps';\n\nexport interface StepsContext"
},
{
"path": "src/Rail.tsx",
"chars": 536,
"preview": "import * as React from 'react';\nimport { clsx } from 'clsx';\nimport type { Status } from './Steps';\n\nexport interface Ra"
},
{
"path": "src/Step.tsx",
"chars": 5989,
"preview": "/* eslint react/prop-types: 0 */\nimport * as React from 'react';\nimport { clsx } from 'clsx';\nimport KeyCode from '@rc-c"
},
{
"path": "src/StepIcon.tsx",
"chars": 1085,
"preview": "import * as React from 'react';\nimport { clsx } from 'clsx';\nimport { StepsContext } from './Context';\nimport pickAttrs "
},
{
"path": "src/Steps.tsx",
"chars": 6054,
"preview": "/* eslint react/no-did-mount-set-state: 0, react/prop-types: 0 */\nimport { clsx } from 'clsx';\nimport React from 'react'"
},
{
"path": "src/UnstableContext.ts",
"chars": 282,
"preview": "import * as React from 'react';\n\nexport interface UnstableContextProps {\n /**\n * Used for Timeline component `reverse"
},
{
"path": "src/index.ts",
"chars": 198,
"preview": "import Steps, { type StepsProps } from './Steps';\nimport Step from './Step';\n\nexport { Step };\nexport type { StepsProps "
},
{
"path": "src/interface.ts",
"chars": 1,
"preview": "\n"
},
{
"path": "tests/__snapshots__/index.test.tsx.snap",
"chars": 27338,
"preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Steps components 1`] = `\n<ol\n class=\"rc-steps rc-steps-horizontal "
},
{
"path": "tests/index.test.tsx",
"chars": 9290,
"preview": "import React from 'react';\nimport { render, fireEvent } from '@testing-library/react';\nimport Steps from '../src';\n\ndesc"
},
{
"path": "tests/semantic.test.tsx",
"chars": 4353,
"preview": "import React from 'react';\nimport { render } from '@testing-library/react';\nimport Steps, { type StepsProps } from '../s"
},
{
"path": "tests/setup.js",
"chars": 113,
"preview": "global.requestAnimationFrame = global.requestAnimationFrame || function raf(cb) {\n return setTimeout(cb, 0);\n};\n"
},
{
"path": "tsconfig.json",
"chars": 331,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"esnext\",\n \"moduleResolution\": \"node\",\n \"baseUrl\": \"./\",\n \"jsx\": \"preser"
}
]
About this extraction
This page contains the full source code of the react-component/steps GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 71 files (101.5 KB), approximately 30.3k tokens, and a symbol index with 24 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.