├── .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
├── Rail.tsx
├── Step.tsx
├── Steps.tsx
├── index.ts
└── interface.ts
├── tests
├── __snapshots__
│ └── index.test.tsx.snap
├── index.test.tsx
├── semantic.test.tsx
└── setup.js
└── tsconfig.json
/.dumirc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'dumi';
2 | import path from 'path';
3 |
4 | export default defineConfig({
5 | alias: {
6 | '@rc-component/steps$': path.resolve('src'),
7 | '@rc-component/steps/es': path.resolve('src'),
8 | },
9 | mfsu: false,
10 | favicons: ['https://avatars0.githubusercontent.com/u/9441414?s=200&v=4'],
11 | themeConfig: {
12 | name: 'Steps',
13 | logo: 'https://avatars0.githubusercontent.com/u/9441414?s=200&v=4',
14 | },
15 | });
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # top-most EditorConfig file
2 | root = true
3 |
4 | # Unix-style newlines with a newline ending every file
5 | [*.{js,css}]
6 | end_of_line = lf
7 | insert_final_newline = true
8 | indent_style = space
9 | indent_size = 2
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | const base = require('@umijs/fabric/dist/eslint');
2 |
3 | module.exports = {
4 | ...base,
5 | rules: {
6 | ...base.rules,
7 | 'arrow-parens': 0,
8 | 'react/sort-comp': 0,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'father';
2 |
3 | export default defineConfig({
4 | plugins: ['@rc-component/father-plugin'],
5 | });
6 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | time: "21:00"
8 | open-pull-requests-limit: 10
9 | ignore:
10 | - dependency-name: "@types/react-dom"
11 | versions:
12 | - 17.0.0
13 | - 17.0.1
14 | - 17.0.2
15 | - dependency-name: "@types/react"
16 | versions:
17 | - 17.0.0
18 | - 17.0.1
19 | - 17.0.2
20 | - 17.0.3
21 | - dependency-name: less
22 | versions:
23 | - 4.1.0
24 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: ✅ test
2 | on: [push, pull_request]
3 | jobs:
4 | test:
5 | uses: react-component/rc-test/.github/workflows/test.yml@main
6 | secrets: inherit
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .iml
2 | *.log
3 | .idea/
4 | .ipr
5 | .iws
6 | *~
7 | ~*
8 | *.diff
9 | *.patch
10 | *.bak
11 | .DS_Store
12 | Thumbs.db
13 | .project
14 | .*proj
15 | .svn/
16 | *.swp
17 | *.swo
18 | *.pyc
19 | *.pyo
20 | .build
21 | node_modules
22 | .cache
23 | dist
24 | assets/**/*.css
25 | build
26 | lib
27 | es
28 | coverage
29 | package-lock.json
30 | yarn.lock
31 | .doc
32 | .umi
33 | .npmrc
34 |
35 | # dumi
36 | .dumi/tmp
37 | .dumi/tmp-test
38 | .dumi/tmp-production
39 | .env.local
40 |
41 | bun.lockb
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "proseWrap": "never",
5 | "printWidth": 100
6 | }
--------------------------------------------------------------------------------
/HISTORY.md:
--------------------------------------------------------------------------------
1 | # History
2 | ----
3 |
4 | ## 3.6.0
5 |
6 | - Remove babel-runtime and prop-types
7 | - Fix icon missing #85
8 |
9 | ## 3.5.0
10 |
11 | - Support `navigation` type & `disabled` prop.
12 |
13 | ## 3.4.0
14 |
15 | - Support `onChange` event.
16 |
17 | ## 3.3.0
18 |
19 | - Add `icons` prop for change preset icon.
20 |
21 | ## 3.2.0
22 |
23 | - Add `initial` prop.
24 |
25 | ## 3.1.0
26 |
27 | - Add `tailContent`.
28 |
29 | ## 3.0.0
30 |
31 | - Rewrite from bottom.
32 |
33 | ## 2.5.1
34 |
35 | * Support react@15.5
36 |
37 | ## 2.5.0
38 |
39 | * Refactor for last tail style.
40 |
41 | ## 2.4.0
42 |
43 | * Refactor for extra width of tail. https://github.com/ant-design/ant-design/issues/5083
44 |
45 | ## 2.3.0
46 |
47 | * Add new step style of prop `progressDot`.
48 |
49 | ## 2.2.0
50 |
51 | * `icon` can be React.Node now.
52 |
53 | ## 2.1.0
54 |
55 | * Add `labelPlacement`, support vertial title and description
56 |
57 | ## 2.0.0
58 |
59 | * Refactor for better layout
60 |
61 | ## 1.5
62 |
63 | * add `status` property of `Steps`
64 |
65 | ## 1.4
66 |
67 | * update react to 0.14
68 |
69 | ## 1.3
70 |
71 | * add `current` property of `Steps`
72 |
73 | ## 1.2.3
74 |
75 | * fix publish
76 |
77 | ## 1.2.2
78 |
79 | * remove vertical `maxDescriptionWidth`
80 |
81 | ## 1.2.1
82 |
83 | * fix vertical `maxDescriptionWidth`
84 |
85 | ## 1.2.0
86 |
87 | * add vertical steps
88 |
89 | ## 1.1.4
90 |
91 | * fix layout algorithm
92 |
93 | ## 1.1.3
94 |
95 | * support `iconPrefix` property, default is `rc`
96 |
97 | ## 1.1.2
98 |
99 | * fix bugs
100 |
101 | ## 1.1.1
102 |
103 | * support `maxDescriptionWidth` property, default is 120
104 |
105 | ## 1.1.0
106 |
107 | * support `prefixCls` property, default is `rc-steps`
108 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014-present yiminghe
4 |
5 | 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:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | 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.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @rc-component/steps
2 |
3 | ---
4 |
5 | React steps component.
6 |
7 | [![NPM version][npm-image]][npm-url]
8 | [![build status][travis-image]][travis-url]
9 | [![Test coverage][codecov-image]][codecov-url]
10 | [![npm download][download-image]][download-url]
11 | [![bundle size][bundlephobia-image]][bundlephobia-url]
12 |
13 | [npm-image]: http://img.shields.io/npm/v/@rc-component/steps.svg?style=flat-square
14 | [npm-url]: http://npmjs.org/package/@rc-component/steps
15 | [travis-image]: https://img.shields.io/travis/react-component/steps.svg?style=flat-square
16 | [travis-url]: https://travis-ci.org/react-component/steps
17 | [codecov-image]: https://img.shields.io/codecov/c/github/react-component/steps/master.svg?style=flat-square
18 | [codecov-url]: https://codecov.io/gh/react-component/steps/branch/master
19 | [download-image]: https://img.shields.io/npm/dm/@rc-component/steps.svg?style=flat-square
20 | [download-url]: https://npmjs.org/package/@rc-component/steps
21 | [bundlephobia-url]: https://bundlephobia.com/result?p=@rc-component/steps
22 | [bundlephobia-image]: https://badgen.net/bundlephobia/minzip/@rc-component/steps
23 |
24 | ## Usage
25 |
26 | ```bash
27 | npm install @rc-component/steps
28 | ```
29 |
30 |
31 |
32 | ```jsx | pure
33 |
34 |
35 |
36 |
37 |
38 | ```
39 |
40 | ## Example
41 |
42 | https://steps.vercel.app/
43 |
44 | ## API
45 |
46 |
47 |
48 |
49 | name |
50 | type |
51 | default |
52 | description |
53 |
54 |
55 |
56 |
57 | type |
58 | string |
59 | default |
60 | diretypetion of Steps, could be `default` `navigation` `inline` |
61 |
62 |
63 | direction |
64 | string |
65 | horizontal |
66 | direction of Steps, enum: `horizontal` or `vertical` |
67 |
68 |
69 | current |
70 | number |
71 | 0 |
72 | index of current step |
73 |
74 |
75 | initial |
76 | number |
77 | 0 |
78 | index initial |
79 |
80 |
81 | size |
82 | string |
83 | |
84 | size of Steps, could be `small` |
85 |
86 |
87 | titlePlacement |
88 | string |
89 | |
90 | placement of step title, could be `vertical` |
91 |
92 |
93 | status |
94 | string |
95 | wait |
96 | status of current Steps, could be `error` `process` `finish` `wait` |
97 |
98 |
99 | icons |
100 | { finish: ReactNode, error: ReactNode } |
101 | |
102 | specify the default finish icon and error icon |
103 |
104 |
105 | itemRender |
106 | (item: StepProps, stepItem: React.ReactNode) => React.ReactNode |
107 | |
108 | custom step item renderer |
109 |
110 |
111 | onChange |
112 | (current: number) => void |
113 | |
114 | Trigger when Step changed |
115 |
116 |
117 |
118 |
119 | ### Steps.Step
120 |
121 |
122 |
123 |
124 | name |
125 | type |
126 | default |
127 | description |
128 |
129 |
130 |
131 |
132 | title |
133 | ReactNode |
134 | |
135 | title of step item |
136 |
137 |
138 | subTitle |
139 | ReactNode |
140 | |
141 | subTitle of step item |
142 |
143 |
144 | description |
145 | ReactNode |
146 | |
147 | description of step item |
148 |
149 |
150 | icon |
151 | ReactNode |
152 | |
153 | set icon of step item |
154 |
155 |
156 | status |
157 | string |
158 | |
159 | status of current Steps, could be `error` `process` `finish` `wait` |
160 |
161 |
162 | tailContent |
163 | ReactNode |
164 | |
165 | content above tail |
166 |
167 |
168 | disabled |
169 | bool |
170 | false |
171 | disabled step when onChange exist |
172 |
173 |
174 | render |
175 | (stepItem: React.ReactNode) => React.ReactNode |
176 | |
177 | custom step item renderer |
178 |
179 |
180 |
181 |
182 | ## Development
183 |
184 | ```bash
185 | npm install
186 | npm start
187 | ```
188 |
189 | ## License
190 |
191 | @rc-component/steps is released under the MIT license.
192 |
--------------------------------------------------------------------------------
/assets/custom-icon.less:
--------------------------------------------------------------------------------
1 | @import 'variables';
2 |
3 | .@{stepsPrefixClass}-item-custom {
4 | .@{stepsPrefixClass}-item-icon {
5 | background: none;
6 | border: 0;
7 | width: auto;
8 | height: auto;
9 | > .@{stepsPrefixClass}-icon {
10 | font-size: 20px;
11 | top: 1px;
12 | width: 20px;
13 | height: 20px;
14 | }
15 | }
16 | &.@{stepsPrefixClass}-item-process {
17 | .@{stepsPrefixClass}-item-icon > .@{stepsPrefixClass}-icon {
18 | color: @process-icon-color;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/assets/iconfont.less:
--------------------------------------------------------------------------------
1 | @icon-url : "//at.alicdn.com/t/font_1434092639_4910953";
2 | .ie-rotate(@rotation) {
3 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation);
4 | }
5 | .rotate(@degrees) {
6 | -webkit-transform: rotate(@degrees);
7 | -ms-transform: rotate(@degrees); // IE9 only
8 | -o-transform: rotate(@degrees);
9 | transform: rotate(@degrees);
10 | }
11 | .animation(@animation) {
12 | -webkit-animation: @animation;
13 | -o-animation: @animation;
14 | animation: @animation;
15 | }
16 | // font-face
17 | // @icon-url: 字体源文件的地址
18 | @font-face {
19 | font-family: 'anticon';
20 | src: url('@{icon-url}.eot'); /* IE9*/
21 | src: url('@{icon-url}.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
22 | url('@{icon-url}.woff') format('woff'), /* chrome、firefox */
23 | url('@{icon-url}.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
24 | url('@{icon-url}.svg#iconfont') format('svg'); /* iOS 4.1- */
25 | }
26 |
27 | .rcicon {
28 | position: relative;
29 | display: inline-block;
30 | font-style: normal;
31 | vertical-align: baseline;
32 | text-align: center;
33 | text-transform: none;
34 | text-rendering: auto;
35 | // 更好地渲染字体
36 | -webkit-font-smoothing: antialiased;
37 | -webkit-text-stroke-width: 0px;
38 | -moz-osx-font-smoothing: grayscale;
39 |
40 | &:before {
41 | display: block;
42 | font-family: "anticon" !important;
43 | }
44 | }
45 | // 方向性图标
46 | .rcicon-step-backward:before {content:"\e662";}
47 | .rcicon-step-forward {.ie-rotate(2);}
48 | .rcicon-step-forward:before {content:"\e662";.rotate(180deg);}
49 | .rcicon-fast-backward:before {content:"\e62a";}
50 | .rcicon-fast-forward {.ie-rotate(2);}
51 | .rcicon-fast-forward:before {content:"\e62a";.rotate(180deg);}
52 | .rcicon-shrink:before {content:"\e65f";}
53 | .rcicon-arrow-salt:before {content:"\e608";}
54 | .rcicon-caret-down:before {content:"\e60f";}
55 | .rcicon-caret-left {.ie-rotate(1);}
56 | .rcicon-caret-left:before {content:"\e60f";.rotate(90deg);}
57 | .rcicon-caret-up {.ie-rotate(2);}
58 | .rcicon-caret-up:before {content:"\e60f";.rotate(180deg);}
59 | .rcicon-caret-right {.ie-rotate(3);}
60 | .rcicon-caret-right:before {content:"\e60f";.rotate(270deg);}
61 | .rcicon-caret-circle-right:before {content:"\e60d";}
62 | .rcicon-caret-circle-left {.ie-rotate(2);}
63 | .rcicon-caret-circle-left:before {content:"\e60d";.rotate(180deg);}
64 | .rcicon-caret-circle-o-right:before {content:"\e60e";}
65 | .rcicon-caret-circle-o-left {.ie-rotate(2);}
66 | .rcicon-caret-circle-o-left:before {content:"\e60e";.rotate(180deg);}
67 | .rcicon-circle-right:before {content:"\e602";}
68 | .rcicon-circle-left {.ie-rotate(2);}
69 | .rcicon-circle-left:before {content:"\e602";.rotate(180deg);}
70 | .rcicon-circle-o-right:before {content:"\e603";}
71 | .rcicon-circle-o-left {.ie-rotate(2);}
72 | .rcicon-circle-o-left:before {content:"\e603";.rotate(180deg);}
73 | .rcicon-double-right:before {content:"\e604";}
74 | .rcicon-double-left {.ie-rotate(2);}
75 | .rcicon-double-left:before {content:"\e604";.rotate(180deg);}
76 | .rcicon-verticle-right:before {content:"\e605";}
77 | .rcicon-verticle-left {.ie-rotate(2);}
78 | .rcicon-verticle-left:before {content:"\e605";.rotate(180deg);}
79 | .rcicon-forward:before {content:"\e630";}
80 | .rcicon-backward {.ie-rotate(2);}
81 | .rcicon-backward:before {content:"\e630";.rotate(180deg);}
82 | .rcicon-rollback:before {content:"\e65a";}
83 | .rcicon-retweet:before {content:"\e659";}
84 |
85 | .rcicon-right:before {content:"\e611";}
86 | .rcicon-down {.ie-rotate(1);}
87 | .rcicon-down:before {content:"\e611";.rotate(90deg);}
88 | .rcicon-left {.ie-rotate(2);}
89 | .rcicon-left:before {content:"\e611";.rotate(180deg);}
90 | .rcicon-up {.ie-rotate(3);}
91 | .rcicon-up:before {content:"\e611";.rotate(270deg);}
92 |
93 | // 提示性图标
94 | .rcicon-question:before {content:"\e655";}
95 | .rcicon-question-circle:before {content:"\e656";}
96 | .rcicon-question-circle-o:before {content:"\e657";}
97 | .rcicon-plus:before {content:"\e651";}
98 | .rcicon-plus-circle:before {content:"\e652";}
99 | .rcicon-plus-circle-o:before {content:"\e653";}
100 | .rcicon-pause:before {content:"\e64c";}
101 | .rcicon-pause-circle:before {content:"\e64d";}
102 | .rcicon-pause-circle-o:before {content:"\e64e";}
103 | .rcicon-minus:before {content:"\e646";}
104 | .rcicon-minus-circle:before {content:"\e647";}
105 | .rcicon-minus-circle-o:before {content:"\e648";}
106 | .rcicon-info-circle:before {content:"\e637";}
107 | .rcicon-info-circle-o:before {content:"\e638";}
108 | .rcicon-info:before {content:"\e63a";}
109 | .rcicon-exclamation:before {content:"\e627";}
110 | .rcicon-exclamation-circle:before {content:"\e628";}
111 | .rcicon-exclamation-circle-o:before {content:"\e629";}
112 | .rcicon-cross:before {content:"\e61e";}
113 | .rcicon-cross-circle:before {content:"\e61f";}
114 | .rcicon-cross-circle-o:before {content:"\e620";}
115 | .rcicon-check:before {content:"\e613";}
116 | .rcicon-check-circle:before {content:"\e614";}
117 | .rcicon-check-circle-o:before {content:"\e615";}
118 | .rcicon-clock-circle:before {content:"\e616";}
119 | .rcicon-clock-circle-o:before {content:"\e617";}
120 |
121 | // 网站通用图标
122 | .rcicon-lock:before {content:"\e641";}
123 | .rcicon-android:before {content:"\e601";}
124 | .rcicon-apple:before {content:"\e606";}
125 | .rcicon-area-chart:before {content:"\e607";}
126 | .rcicon-bar-chart:before {content:"\e609";}
127 | .rcicon-bars:before {content:"\e60a";}
128 | .rcicon-book:before {content:"\e60b";}
129 | .rcicon-calendar:before {content:"\e60c";}
130 | .rcicon-cloud:before {content:"\e618";}
131 | .rcicon-cloud-download:before {content:"\e619";}
132 | .rcicon-code:before {content:"\e61a";}
133 | .rcicon-copy:before {content:"\e61c";}
134 | .rcicon-credit-card:before {content:"\e61d";}
135 | .rcicon-delete:before {content:"\e621";}
136 | .rcicon-desktop:before {content:"\e622";}
137 | .rcicon-download-line:before {content:"\e623";}
138 | .rcicon-edit:before {content:"\e624";}
139 | .rcicon-ellipsis:before {content:"\e625";}
140 | .rcicon-environment:before {content:"\e626";}
141 | .rcicon-file:before {content:"\e62c";}
142 | .rcicon-file-text:before {content:"\e62d";}
143 | .rcicon-folder:before {content:"\e62e";}
144 | .rcicon-folder-open:before {content:"\e62f";}
145 | .rcicon-github:before {content:"\e631";}
146 | .rcicon-hdd:before {content:"\e632";}
147 | .rcicon-frown:before {content:"\e633";}
148 | .rcicon-meh:before {content:"\e634";}
149 | .rcicon-inbox:before {content:"\e635";}
150 | .rcicon-laptop:before {content:"\e63d";}
151 | .rcicon-large:before {content:"\e63e";}
152 | .rcicon-line-chart:before {content:"\e63f";}
153 | .rcicon-link:before {content:"\e640";}
154 | .rcicon-logout:before {content:"\e642";}
155 | .rcicon-mail:before {content:"\e643";}
156 | .rcicon-menu-fold:before {content:"\e644";}
157 | .rcicon-menu-unfold:before {content:"\e645";}
158 | .rcicon-mobile:before {content:"\e649";}
159 | .rcicon-notification:before {content:"\e64a";}
160 | .rcicon-paper-clip:before {content:"\e64b";}
161 | .rcicon-picture:before {content:"\e64f";}
162 | .rcicon-pie-chart:before {content:"\e650";}
163 | .rcicon-poweroff:before {content:"\e654";}
164 | .rcicon-reload:before {content:"\e658";}
165 | .rcicon-search:before {content:"\e65b";}
166 | .rcicon-setting:before {content:"\e65c";}
167 | .rcicon-share-alt:before {content:"\e65d";}
168 | .rcicon-shopping-cart:before {content:"\e65e";}
169 | .rcicon-smile:before {content:"\e661";}
170 | .rcicon-tablet:before {content:"\e664";}
171 | .rcicon-tag:before {content:"\e665";}
172 | .rcicon-tags:before {content:"\e666";}
173 | .rcicon-to-top:before {content:"\e667";}
174 | .rcicon-unlock:before {content:"\e668";}
175 | .rcicon-upload:before {content:"\e669";}
176 | .rcicon-user:before {content:"\e66a";}
177 | .rcicon-video-camera:before {content:"\e66b";}
178 | .rcicon-windows:before {content:"\e66c";}
179 | .rcicon-loading:before {
180 | display: inline-block;
181 | .animation(loadingCircle 1.0s infinite linear);
182 | content:"\e610";
183 | }
184 |
185 | :root {
186 | .rcicon-step-forward,
187 | .rcicon-fast-forward,
188 | .rcicon-left,
189 | .rcicon-up,
190 | .rcicon-down,
191 | .rcicon-caret-left,
192 | .rcicon-caret-up,
193 | .rcicon-caret-right,
194 | .rcicon-caret-circle-left,
195 | .rcicon-caret-circle-o-left,
196 | .rcicon-circle-left,
197 | .rcicon-circle-o-left,
198 | .rcicon-double-left,
199 | .rcicon-verticle-left,
200 | .rcicon-backward {
201 | filter: none;
202 | }
203 | }
204 |
--------------------------------------------------------------------------------
/assets/index.less:
--------------------------------------------------------------------------------
1 | @import 'variables';
2 |
3 | .@{stepsPrefixClass} {
4 | font-size: 0;
5 | width: 100%;
6 | line-height: 1.5;
7 | display: flex;
8 |
9 | &,
10 | * {
11 | box-sizing: border-box;
12 | }
13 | }
14 |
15 | .@{stepsPrefixClass}-item {
16 | position: relative;
17 | display: inline-block;
18 | vertical-align: top;
19 | flex: 1;
20 | // overflow: hidden;
21 |
22 | &:last-child {
23 | flex: none;
24 | }
25 |
26 | &:last-child &-title:after {
27 | display: none;
28 | }
29 |
30 | &-icon,
31 | &-section {
32 | display: inline-block;
33 | vertical-align: top;
34 | }
35 |
36 | &-icon {
37 | flex: none;
38 | border: 1px solid @wait-icon-color;
39 | width: 26px;
40 | height: 26px;
41 | line-height: 26px;
42 | text-align: center;
43 | border-radius: 26px;
44 | font-size: 14px;
45 | transition:
46 | background-color 0.3s,
47 | border-color 0.3s;
48 |
49 | > .@{stepsPrefixClass}-icon {
50 | line-height: 1;
51 | top: -1px;
52 | color: @primary-color;
53 | position: relative;
54 |
55 | &.rcicon {
56 | font-size: 12px;
57 | position: relative;
58 | top: -2px;
59 | }
60 | }
61 | }
62 |
63 | &-section {
64 | margin-top: 3px;
65 | }
66 | &-title {
67 | font-size: 14px;
68 | color: #666;
69 | font-weight: bold;
70 | display: inline-block;
71 | position: relative;
72 | }
73 | &-subtitle {
74 | font-size: 12px;
75 | display: inline-block;
76 | color: #999;
77 | }
78 | &-description {
79 | font-size: 12px;
80 | color: #999;
81 | }
82 | .step-item-status(wait);
83 | .step-item-status(process);
84 | &-process &-icon {
85 | background: @process-icon-color;
86 | > .@{stepsPrefixClass}-icon {
87 | color: #fff;
88 | }
89 | }
90 | .step-item-status(finish);
91 | .step-item-status(error);
92 |
93 | &.@{stepsPrefixClass}-next-error .@{stepsPrefixClass}-item-title:after {
94 | background: @error-icon-color;
95 | }
96 | }
97 |
98 | .@{stepsPrefixClass}-horizontal:not(.@{stepsPrefixClass}-label-vertical) {
99 | .@{stepsPrefixClass}-item {
100 | &-description {
101 | max-width: @stepDescriptionMaxWidth;
102 | }
103 | }
104 | }
105 |
106 | .step-item-status(@status) {
107 | @icon-color: '@{status}-icon-color';
108 | @title-color: '@{status}-title-color';
109 | @description-color: '@{status}-description-color';
110 | @tail-color: '@{status}-tail-color';
111 | &-@{status} &-icon {
112 | border-color: @@icon-color;
113 | background-color: #fff;
114 | > .@{stepsPrefixClass}-icon {
115 | color: @@icon-color;
116 | .@{stepsPrefixClass}-icon-dot {
117 | background: @@icon-color;
118 | }
119 | }
120 | }
121 | &-@{status} &-title {
122 | color: @@title-color;
123 | }
124 | &-@{status} &-description {
125 | color: @@description-color;
126 | }
127 | }
128 |
129 | // @import 'custom-icon';
130 | // @import 'small';
131 | // @import 'vertical';
132 | // @import 'label-placement';
133 | // @import 'progress-dot';
134 | // @import 'nav';
135 | // @import 'inline';
136 |
137 | // ======================= Horizontal =======================
138 | .verticalFlex() {
139 | display: flex;
140 | flex-direction: column;
141 | align-items: center;
142 | }
143 |
144 | .@{stepsPrefixClass} {
145 | .@{stepsPrefixClass}-item {
146 | &-section {
147 | min-width: 0;
148 | }
149 |
150 | &-header {
151 | display: flex;
152 | gap: 8px;
153 | align-items: center;
154 | }
155 |
156 | // Ellipsis
157 | &-title,
158 | &-subtitle,
159 | &-description {
160 | overflow: hidden;
161 | text-overflow: ellipsis;
162 | white-space: nowrap;
163 | }
164 | }
165 | }
166 |
167 | .@{stepsPrefixClass}-horizontal {
168 | .@{stepsPrefixClass}-item {
169 | flex: 1;
170 | position: relative;
171 | min-width: 0;
172 |
173 | &-rail {
174 | height: 1px;
175 | background: @process-tail-color;
176 | }
177 | }
178 |
179 | // Label Vertical
180 | &.@{stepsPrefixClass}-label-vertical {
181 | .@{stepsPrefixClass}-item {
182 | .verticalFlex();
183 | padding-inline: 8px;
184 |
185 | &-section {
186 | .verticalFlex();
187 | }
188 |
189 | &-rail {
190 | position: absolute;
191 | top: 13px;
192 | left: calc(50% + 13px);
193 | width: 100%;
194 | }
195 | }
196 | }
197 |
198 | // Label Horizontal
199 | &.@{stepsPrefixClass}-label-horizontal {
200 | .@{stepsPrefixClass}-item {
201 | display: flex;
202 |
203 | &:last-child {
204 | flex: none;
205 | }
206 |
207 | &-section {
208 | flex: 1;
209 | }
210 |
211 | &-rail {
212 | flex: 1;
213 | min-width: 0;
214 | }
215 | }
216 | }
217 | }
218 |
219 | // ======================== Vertical ========================
220 | .@{stepsPrefixClass}-vertical {
221 | }
222 |
--------------------------------------------------------------------------------
/assets/inline.less:
--------------------------------------------------------------------------------
1 | @import 'variables';
2 |
3 | .@{stepsPrefixClass}-inline {
4 | width: auto;
5 | display: inline-flex;
6 |
7 | .@{stepsPrefixClass}-item {
8 | flex: none;
9 |
10 |
11 | &-icon {
12 | width: 6px;
13 | height: 6px;
14 | margin-left: calc(50% - 3px);
15 | > .@{stepsPrefixClass}-icon {
16 | top: 0;
17 | }
18 | .@{stepsPrefixClass}-icon-dot {
19 | border-radius: 3px;
20 | }
21 | }
22 |
23 | &-section {
24 | width: auto;
25 | margin-top: 7px;
26 | }
27 | &-title {
28 | color: rgba(0, 0, 0, 0.25);
29 | font-size: 12px;
30 | line-height: 20px;
31 | font-weight: normal;
32 | margin-bottom: 2px;
33 | }
34 | &-description {
35 | display: none;
36 | }
37 |
38 | &-finish {
39 | .@{stepsPrefixClass}-item-icon .@{stepsPrefixClass}-icon .@{stepsPrefixClass}-icon-dot {
40 | background-color: @process-tail-color;
41 | }
42 | }
43 | &-wait {
44 | .@{stepsPrefixClass}-item-icon .@{stepsPrefixClass}-icon .@{stepsPrefixClass}-icon-dot {
45 | background-color: #fff;
46 | border: 1px solid @process-tail-color;
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/assets/label-placement.less:
--------------------------------------------------------------------------------
1 | @import 'variables';
2 |
3 | .@{stepsPrefixClass}-label-vertical {
4 | .@{stepsPrefixClass}-item {
5 | overflow: visible;
6 | &-section {
7 | display: block;
8 | text-align: center;
9 | margin-top: 8px;
10 | width: @stepDescriptionMaxWidth;
11 | }
12 | &-icon {
13 | display: inline-block;
14 | margin-left: 36px;
15 | }
16 | &-title {
17 | padding-right: 0;
18 | &:after {
19 | display: none;
20 | }
21 | }
22 | &-description {
23 | text-align: left;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/assets/nav.less:
--------------------------------------------------------------------------------
1 | @import 'variables';
2 |
3 | .@{stepsPrefixClass}-navigation {
4 | padding-top: 8px;
5 | &.@{stepsPrefixClass}-horizontal {
6 | .@{stepsPrefixClass}-item-description {
7 | max-width: @stepNavContentMaxWidth;
8 | }
9 | }
10 |
11 | .@{stepsPrefixClass}-item {
12 | box-sizing: border-box;
13 | text-align: center;
14 | overflow: visible;
15 |
16 |
17 | &-title {
18 | max-width: @stepNavContentMaxWidth;
19 | white-space: nowrap;
20 | overflow: hidden;
21 | text-overflow: ellipsis;
22 |
23 | &:after {
24 | display: none;
25 | }
26 | }
27 |
28 | &:last-child {
29 | flex: 1;
30 | &:after {
31 | display: none;
32 | }
33 | }
34 |
35 | &:after {
36 | content: '';
37 | display: inline-block;
38 | width: 16px;
39 | height: 16px;
40 | border: 1px solid #ccc;
41 | border-bottom: none;
42 | border-left: none;
43 | transform: rotate(45deg);
44 | position: absolute;
45 | top: 50%;
46 | left: 100%;
47 | margin-top: -12px;
48 | margin-left: -8px;
49 | }
50 |
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/assets/progress-dot.less:
--------------------------------------------------------------------------------
1 | @import 'variables';
2 |
3 | .@{stepsPrefixClass}-dot {
4 | .@{stepsPrefixClass}-item {
5 | &-icon {
6 | padding-right: 0;
7 | width: 5px;
8 | height: 5px;
9 | line-height: 5px;
10 | border: 0;
11 | margin-left: 48px;
12 | .@{stepsPrefixClass}-icon-dot {
13 | float: left;
14 | width: 100%;
15 | height: 100%;
16 | border-radius: 2.5px;
17 | }
18 | }
19 | &-process &-icon {
20 | top: -1px;
21 | width: 7px;
22 | height: 7px;
23 | line-height: 7px;
24 | .@{stepsPrefixClass}-icon-dot {
25 | border-radius: 3.5px;
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/assets/small.less:
--------------------------------------------------------------------------------
1 | @import 'variables';
2 |
3 | .@{stepsPrefixClass}-small {
4 | .@{stepsPrefixClass}-item-icon {
5 | width: 18px;
6 | height: 18px;
7 | line-height: 18px;
8 | text-align: center;
9 | border-radius: 18px;
10 | font-size: 12px;
11 | margin-right: 10px;
12 | > .@{stepsPrefixClass}-icon {
13 | font-size: 12px;
14 | font-size: ~"9px \9"; // ie8-9
15 | transform: scale(.75);
16 | top: -1px;
17 | }
18 | }
19 | .@{stepsPrefixClass}-item-section {
20 | margin-top: 0;
21 | }
22 | .@{stepsPrefixClass}-item-title {
23 | font-size: 12px;
24 | margin-bottom: 4px;
25 | color: #666;
26 | font-weight: bold;
27 | }
28 | .@{stepsPrefixClass}-item-description {
29 | font-size: 12px;
30 | color: #999;
31 | }
32 |
33 | .@{stepsPrefixClass}-item-custom .@{stepsPrefixClass}-item-icon {
34 | width: inherit;
35 | height: inherit;
36 | line-height: inherit;
37 | border-radius: 0;
38 | border: 0;
39 | background: none;
40 | > .@{stepsPrefixClass}-icon {
41 | font-size: 20px;
42 | top: -2.5px;
43 | transform: none;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/assets/variables.less:
--------------------------------------------------------------------------------
1 | @stepsPrefixClass: ~"rc-steps";
2 | @stepDescriptionMaxWidth: 100px;
3 | @stepNavContentMaxWidth: 140px;
4 | @primary-color: #108ee9;
5 | @process-icon-color: @primary-color;
6 | @process-title-color: rgba(0,0,0,.65);
7 | @process-description-color: @process-title-color;
8 | @process-tail-color: #e9e9e9;
9 | @wait-icon-color: #ccc;
10 | @wait-title-color: rgba(0,0,0,.43);
11 | @wait-description-color: @wait-title-color;
12 | @wait-tail-color: @process-tail-color;
13 | @finish-icon-color: @process-icon-color;
14 | @finish-title-color: @wait-title-color;
15 | @finish-description-color: @finish-title-color;
16 | @finish-tail-color: @process-icon-color;
17 | @error-icon-color: #f50;
18 | @error-title-color: @error-icon-color;
19 | @error-description-color: @error-icon-color;
20 | @error-tail-color: @process-tail-color;
21 |
--------------------------------------------------------------------------------
/assets/vertical.less:
--------------------------------------------------------------------------------
1 | @import 'variables';
2 |
3 | .@{stepsPrefixClass}-vertical {
4 | display: block;
5 | .@{stepsPrefixClass}-item {
6 | display: block;
7 | overflow: visible;
8 | &-icon {
9 | float: left;
10 | &-inner {
11 | margin-right: 16px;
12 | }
13 | }
14 | &-section {
15 | min-height: 48px;
16 | overflow: hidden;
17 | display: block;
18 | }
19 | &-title {
20 | line-height: 26px;
21 | &:after {
22 | display: none;
23 | }
24 | }
25 | &-description {
26 | padding-bottom: 12px;
27 | }
28 | }
29 |
30 | &.@{stepsPrefixClass}-small {
31 | .@{stepsPrefixClass}-item-title {
32 | line-height: 18px;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/bunfig.toml:
--------------------------------------------------------------------------------
1 | [install]
2 | peer = false
--------------------------------------------------------------------------------
/docs/demo/alternativeLabel.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: alternativeLabel
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/demo/composable.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: composable
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/demo/custom-svg-icon.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: custom-svg-icon
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/demo/customIcon.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: customIcon
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/demo/dynamic.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: dynamic
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/demo/errorStep.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: errorStep
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/demo/inline.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: inline
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/demo/nav-base.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: nav-base
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/demo/nextStep.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: nextStep
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/demo/progressDot.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: progressDot
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/demo/simple.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: simple
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/demo/smallSize.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: smallSize
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/demo/stepIcon.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: stepIcon
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/demo/vertical.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: vertical
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/demo/verticalSmall.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: verticalSmall
3 | nav:
4 | title: Demo
5 | path: /demo
6 | ---
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/examples/alternativeLabel.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import '../../assets/iconfont.less';
3 | import React from 'react';
4 | import Steps from '@rc-component/steps';
5 |
6 | const description =
7 | '这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊';
8 |
9 | export default () => (
10 |
41 | );
42 |
--------------------------------------------------------------------------------
/docs/examples/composable.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import '../../assets/iconfont.less';
3 | import React from 'react';
4 | import Steps from '@rc-component/steps';
5 |
6 | const description =
7 | '这里是多信息的描述啊描述啊描述啊描述啊哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶';
8 |
9 | export default () => (
10 |
32 | );
33 |
--------------------------------------------------------------------------------
/docs/examples/custom-svg-icon.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import '../../assets/iconfont.less';
3 | import React from 'react';
4 | import Steps from '@rc-component/steps';
5 |
6 | function getFinishIcon() {
7 | const path =
8 | 'M923 283.6c-13.4-31.1-32.6-58.9-56.9-82.8-24.3-23.8-52.' +
9 | '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' +
10 | '.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' +
11 | '-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 ' +
12 | '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' +
13 | '.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' +
14 | '.9 133.9 151.6 92.8 85.7 184.7 144.9 188.6 147.3l23.7 15.2c10.5 6.' +
15 | '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' +
16 | '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 ' +
17 | '20.3-103.3 0.1-35.3-7-69.6-20.9-101.9z';
18 | return (
19 |
28 | );
29 | }
30 |
31 | function getErrorIcon() {
32 | const path1 =
33 | 'M512 0C229.2 0 0 229.2 0 512s229.2 512 512 512 512-229' +
34 | '.2 512-512S794.8 0 512 0zm311.1 823.1c-40.4 40.4-87.5 72.2-139.9 9' +
35 | '4.3C629 940.4 571.4 952 512 952s-117-11.6-171.2-34.5c-52.4-22.2-99' +
36 | '.4-53.9-139.9-94.3-40.4-40.4-72.2-87.5-94.3-139.9C83.6 629 72 571.' +
37 | '4 72 512s11.6-117 34.5-171.2c22.2-52.4 53.9-99.4 94.3-139.9 40.4-4' +
38 | '0.4 87.5-72.2 139.9-94.3C395 83.6 452.6 72 512 72s117 11.6 171.2 3' +
39 | '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' +
40 | '940.4 395 952 452.6 952 512s-11.6 117-34.5 171.2c-22.2 52.4-53.9 9' +
41 | '9.5-94.4 139.9z';
42 | const path2 =
43 | 'M640.3 765.5c-19.9 0-36-16.1-36-36 0-50.9-41.4-92.3-92' +
44 | '.3-92.3s-92.3 41.4-92.3 92.3c0 19.9-16.1 36-36 36s-36-16.1-36-36c0' +
45 | '-90.6 73.7-164.3 164.3-164.3s164.3 73.7 164.3 164.3c0 19.9-16.1 36' +
46 | '-36 36zM194.2 382.4a60 60 0 1 0 120 0 60 60 0 1 0-120 0zM709.5 382' +
47 | '.4a60 60 0 1 0 120 0 60 60 0 1 0-120 0z';
48 | return (
49 |
59 | );
60 | }
61 |
62 | const icons = {
63 | finish: getFinishIcon(),
64 | error: getErrorIcon(),
65 | };
66 |
67 | const description = 'This is a description';
68 |
69 | export default () => (
70 |
80 | );
81 |
--------------------------------------------------------------------------------
/docs/examples/customIcon.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import '../../assets/iconfont.less';
3 | import React from 'react';
4 | import Steps from '@rc-component/steps';
5 |
6 | // eslint-disable-next-line react/prop-types
7 | const Icon = ({ type }) => ;
8 |
9 | export default () => (
10 | },
14 | { title: '步骤2', icon: 'apple' },
15 | { title: '步骤1', icon: 'github' },
16 | ]}
17 | />
18 | );
19 |
--------------------------------------------------------------------------------
/docs/examples/dynamic.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import '../../assets/iconfont.less';
3 | import React, { useState } from 'react';
4 | import Steps from '@rc-component/steps';
5 |
6 | export default () => {
7 | const [items, setItems] = useState([
8 | {
9 | title: '已完成',
10 | description:
11 | '这里是多信息的描述啊描述啊描述啊描述啊哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶',
12 | },
13 | {
14 | title: '进行中',
15 | description:
16 | '这里是多信息的描述啊描述啊描述啊描述啊哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶',
17 | },
18 | {
19 | title: '待运行',
20 | description:
21 | '这里是多信息的描述啊描述啊描述啊描述啊哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶',
22 | },
23 | {
24 | title: '待运行',
25 | description:
26 | '这里是多信息的描述啊描述啊描述啊描述啊哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶',
27 | },
28 | ]);
29 |
30 | const addStep = () => {
31 | const newSteps = [...items];
32 | newSteps.push({
33 | title: '待运行',
34 | description: '新的节点',
35 | });
36 | setItems(newSteps);
37 | };
38 | return (
39 |
40 |
43 |
44 |
45 | );
46 | };
47 |
--------------------------------------------------------------------------------
/docs/examples/errorStep.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import '../../assets/iconfont.less';
3 | import React from 'react';
4 | import Steps from '@rc-component/steps';
5 |
6 | const description =
7 | '这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊';
8 |
9 | export default () => (
10 |
32 | );
33 |
--------------------------------------------------------------------------------
/docs/examples/inline.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import React, { useState } from 'react';
3 | import Steps from '@rc-component/steps';
4 |
5 | export default () => {
6 | const [current, setCurrent] = useState(0);
7 |
8 | return (
9 | <>
10 |
17 |
18 |
19 |
20 | React.cloneElement(stepItem, { title: item.description })}
43 | />
44 | >
45 | );
46 | };
47 |
--------------------------------------------------------------------------------
/docs/examples/nav-base.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import '../../assets/iconfont.less';
3 | import React, { useState } from 'react';
4 | import Steps from '@rc-component/steps';
5 |
6 | export default () => {
7 | const [current, setCurrent] = useState(0);
8 |
9 | const onChange = (current) => {
10 | // eslint-disable-next-line no-console
11 | console.log('onChange:', current);
12 | setCurrent(current);
13 | };
14 |
15 | const containerStyle = {
16 | border: '1px solid rgb(235, 237, 240)',
17 | marginBottom: 24,
18 | };
19 |
20 | const description = 'This is a description.';
21 |
22 | return (
23 |
24 |
49 |
74 |
75 | );
76 | };
77 |
--------------------------------------------------------------------------------
/docs/examples/nextStep.css:
--------------------------------------------------------------------------------
1 | .my-step-form {
2 | width: 100%;
3 | }
4 | .my-step-form > div {
5 | margin-bottom: 20px;
6 | }
7 | .my-step-container {
8 | width: 100%;
9 | }
10 |
--------------------------------------------------------------------------------
/docs/examples/nextStep.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import '../../assets/iconfont.less';
3 | import './nextStep.css';
4 | import React from 'react';
5 | import Steps from '@rc-component/steps';
6 |
7 | function generateRandomSteps() {
8 | const n = Math.floor(Math.random() * 3) + 3;
9 | const arr = [];
10 | for (let i = 0; i < n; i++) {
11 | arr.push({
12 | title: `步骤${i + 1}`,
13 | });
14 | }
15 | return arr;
16 | }
17 | const steps = generateRandomSteps();
18 |
19 | class MyForm extends React.Component {
20 | state = {
21 | currentStep: Math.floor(Math.random() * steps.length),
22 | };
23 |
24 | nextStep = () => {
25 | const { currentStep } = this.state;
26 | let s = currentStep + 1;
27 | if (s === steps.length) {
28 | s = 0;
29 | }
30 | this.setState({
31 | currentStep: s,
32 | });
33 | };
34 |
35 | render() {
36 | const { currentStep: cs } = this.state;
37 | this.stepsRefs = [];
38 | return (
39 |
61 | );
62 | }
63 | }
64 |
65 | export default MyForm;
66 |
--------------------------------------------------------------------------------
/docs/examples/progressDot.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import '../../assets/iconfont.less';
3 | import React from 'react';
4 | import Steps from '@rc-component/steps';
5 |
6 | const description =
7 | '这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊';
8 |
9 | export default () => (
10 |
37 | );
38 |
--------------------------------------------------------------------------------
/docs/examples/simple.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import '../../assets/iconfont.less';
3 | import React from 'react';
4 | import Steps from '@rc-component/steps';
5 |
6 | const description =
7 | '这里是多信息的描述啊描述啊描述啊描述啊哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶哦耶';
8 |
9 | const ControlSteps = () => {
10 | const [current, setCurrent] = React.useState(0);
11 | return (
12 | {
15 | // eslint-disable-next-line no-console
16 | console.log('Change:', val);
17 | setCurrent(val);
18 | }}
19 | items={[
20 | {
21 | title: '已完成',
22 | },
23 | {
24 | title: '进行中',
25 | },
26 | {
27 | title: '待运行',
28 | description: 'Hello World!',
29 | },
30 | {
31 | title: '待运行',
32 | },
33 | ]}
34 | />
35 | );
36 | };
37 |
38 | export default () => (
39 |
40 |
57 |
80 |
104 |
105 |
106 | );
107 |
--------------------------------------------------------------------------------
/docs/examples/smallSize.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import '../../assets/iconfont.less';
3 | import React from 'react';
4 | import Steps from '@rc-component/steps';
5 |
6 | // eslint-disable-next-line react/prop-types
7 | const Icon = ({ type }) => ;
8 |
9 | export default () => (
10 |
11 |
29 | ,
40 | },
41 | {
42 | title: '步骤3',
43 | icon: 'apple',
44 | },
45 | {
46 | title: '待运行',
47 | icon: 'github',
48 | },
49 | ]}
50 | />
51 |
52 | );
53 |
--------------------------------------------------------------------------------
/docs/examples/stepIcon.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import '../../assets/iconfont.less';
3 | import React from 'react';
4 | import Steps from '@rc-component/steps';
5 |
6 | function stepIcon({ status, node }) {
7 | const isProcessing = status === 'process';
8 | return isProcessing ? {node}
: node;
9 | }
10 |
11 | export default () => {
12 | const [current, setCurrent] = React.useState(0);
13 | return (
14 | <>
15 |
23 |
44 | >
45 | );
46 | };
47 |
--------------------------------------------------------------------------------
/docs/examples/vertical.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import '../../assets/iconfont.less';
3 | import React from 'react';
4 | import Steps from '@rc-component/steps';
5 |
6 | const description =
7 | '这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊';
8 |
9 | export default () => (
10 |
31 | );
32 |
--------------------------------------------------------------------------------
/docs/examples/verticalSmall.jsx:
--------------------------------------------------------------------------------
1 | import '../../assets/index.less';
2 | import '../../assets/iconfont.less';
3 | import React from 'react';
4 | import Steps from '@rc-component/steps';
5 |
6 | const description =
7 | '这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊这里是多信息的描述啊';
8 |
9 | export default () => (
10 |
32 | );
33 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | hero:
3 | title: "@rc-component/steps"
4 | description: React Steps Component
5 | ---
6 |
7 |
8 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = require('./src/');
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | setupFiles: ['./tests/setup.js'],
3 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@rc-component/steps",
3 | "version": "1.1.0",
4 | "description": "steps ui component for react",
5 | "keywords": [
6 | "react",
7 | "react-component",
8 | "react-steps"
9 | ],
10 | "homepage": "http://github.com/react-component/steps",
11 | "bugs": {
12 | "url": "http://github.com/react-component/steps/issues"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": " git+ssh://git@github.com/react-component/steps.git"
17 | },
18 | "license": "MIT",
19 | "maintainers": [
20 | {
21 | "name": "afc163",
22 | "email": "afc163@gmail.com"
23 | }
24 | ],
25 | "main": "./lib/index",
26 | "module": "./es/index",
27 | "types": "./lib/index.d.ts",
28 | "files": [
29 | "assets/*.css",
30 | "dist",
31 | "es",
32 | "lib"
33 | ],
34 | "scripts": {
35 | "compile": "father build && lessc assets/index.less assets/index.css",
36 | "coverage": "rc-test --coverage",
37 | "docs:build": "dumi build",
38 | "docs:deploy": "gh-pages -d .doc",
39 | "gh-pages": "npm run docs:build && npm run docs:deploy",
40 | "lint": "eslint src/ --ext .ts,.tsx,.jsx,.js,.md",
41 | "prepare": "husky install && dumi setup",
42 | "prepublishOnly": "npm run compile && rc-np",
43 | "prettier": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
44 | "postpublish": "npm run gh-pages",
45 | "start": "dumi dev",
46 | "test": "rc-test",
47 | "now-build": "npm run docs:build"
48 | },
49 | "lint-staged": {
50 | "**/*.{js,jsx,tsx,ts,md,json}": [
51 | "prettier --write"
52 | ]
53 | },
54 | "dependencies": {
55 | "classnames": "^2.2.3",
56 | "@rc-component/util": "^1.2.1"
57 | },
58 | "devDependencies": {
59 | "@rc-component/father-plugin": "^2.0.2",
60 | "@rc-component/np": "^1.0.0",
61 | "@testing-library/jest-dom": "^6.4.5",
62 | "@testing-library/react": "^15.0.6",
63 | "@types/classnames": "^2.2.9",
64 | "@types/jest": "^29.4.0",
65 | "@types/react": "^18.0.28",
66 | "@types/react-dom": "^18.0.11",
67 | "@umijs/fabric": "^4.0.1",
68 | "cross-env": "^7.0.0",
69 | "dumi": "^2.0.0",
70 | "eslint": "^8.55.0",
71 | "eslint-plugin-jest": "^27.6.0",
72 | "eslint-plugin-unicorn": "^50.0.1",
73 | "father": "^4",
74 | "gh-pages": "^6.1.0",
75 | "glob": "^10.0.0",
76 | "husky": "^8.0.1",
77 | "less": "^4.1.3",
78 | "lint-staged": "^15.2.0",
79 | "prettier": "^3.1.0",
80 | "querystring": "^0.2.0",
81 | "rc-dialog": "8.x",
82 | "rc-test": "^7.0.9",
83 | "react": "^18.0.0",
84 | "react-dom": "^18.0.0",
85 | "typescript": "^5.0.0"
86 | },
87 | "peerDependencies": {
88 | "react": ">=16.9.0",
89 | "react-dom": ">=16.9.0"
90 | },
91 | "engines": {
92 | "node": ">=8.x"
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/script/update-content.js:
--------------------------------------------------------------------------------
1 | /*
2 | 用于 dumi 改造使用,
3 | 可用于将 examples 的文件批量修改为 demo 引入形式,
4 | 其他项目根据具体情况使用。
5 | */
6 |
7 | const fs = require('fs');
8 | const glob = require('glob');
9 |
10 | const paths = glob.sync('./docs/examples/*.jsx');
11 |
12 | paths.forEach(path => {
13 | const name = path.split('/').pop().split('.')[0];
14 | fs.writeFile(
15 | `./docs/demo/${name}.md`,
16 | `---
17 | title: ${name}
18 | nav:
19 | title: Demo
20 | path: /demo
21 | ---
22 |
23 |
24 | `,
25 | 'utf8',
26 | function(error) {
27 | if(error){
28 | console.log(error);
29 | return false;
30 | }
31 | console.log(`${name} 更新成功~`);
32 | }
33 | )
34 | });
35 |
--------------------------------------------------------------------------------
/src/Rail.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import cls from 'classnames';
3 | import type { Status, StepsProps } from './Steps';
4 |
5 | export interface RailProps {
6 | prefixCls: string;
7 | classNames: StepsProps['classNames'];
8 | styles: StepsProps['styles'];
9 | status: Status;
10 | }
11 |
12 | export default function Rail(props: RailProps) {
13 | const { prefixCls, classNames, styles, status } = props;
14 | const railCls = `${prefixCls}-rail`;
15 |
16 | // ============================= render =============================
17 | return (
18 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/Step.tsx:
--------------------------------------------------------------------------------
1 | /* eslint react/prop-types: 0 */
2 | import * as React from 'react';
3 | import cls from 'classnames';
4 | import KeyCode from '@rc-component/util/lib/KeyCode';
5 | import type { Status, StepItem, StepsProps } from './Steps';
6 | import Rail from './Rail';
7 |
8 | export interface StepProps {
9 | // style
10 | prefixCls?: string;
11 | classNames: StepsProps['classNames'];
12 | styles: StepsProps['styles'];
13 |
14 | // data
15 | data: StepItem;
16 | nextStatus?: Status;
17 | active?: boolean;
18 | index: number;
19 | last: boolean;
20 |
21 | // stepIndex?: number;
22 | // stepNumber?: number;
23 | // title?: React.ReactNode;
24 | // subTitle?: React.ReactNode;
25 | // description?: React.ReactNode;
26 |
27 | // render
28 | iconRender?: StepsProps['iconRender'];
29 | icon?: React.ReactNode;
30 | itemRender?: StepsProps['itemRender'];
31 | itemWrapperRender?: StepsProps['itemWrapperRender'];
32 |
33 | // Event
34 | onClick: (index: number) => void;
35 | }
36 |
37 | export default function Step(props: StepProps) {
38 | const {
39 | // style
40 | prefixCls,
41 | classNames,
42 | styles,
43 |
44 | // data
45 | data,
46 | last,
47 | nextStatus,
48 | active,
49 | index,
50 |
51 | // render
52 | itemRender,
53 | iconRender,
54 | itemWrapperRender,
55 |
56 | // events
57 | onClick,
58 | } = props;
59 |
60 | const itemCls = `${prefixCls}-item`;
61 |
62 | // ========================== Data ==========================
63 | const {
64 | onClick: onItemClick,
65 | title,
66 | subTitle,
67 | content,
68 | description,
69 | disabled,
70 | icon,
71 | status,
72 |
73 | className,
74 | style,
75 | ...restItemProps
76 | } = data;
77 |
78 | const mergedContent = content ?? description;
79 |
80 | const renderInfo = {
81 | item: {
82 | ...data,
83 | content: mergedContent,
84 | },
85 | index,
86 | active,
87 | };
88 |
89 | // ========================= Click ==========================
90 | const clickable = !!(onClick || onItemClick) && !disabled;
91 |
92 | const accessibilityProps: {
93 | role?: string;
94 | tabIndex?: number;
95 | onClick?: React.MouseEventHandler;
96 | onKeyDown?: React.KeyboardEventHandler;
97 | } = {};
98 |
99 | if (clickable) {
100 | accessibilityProps.role = 'button';
101 | accessibilityProps.tabIndex = 0;
102 | accessibilityProps.onClick = (e) => {
103 | onItemClick?.(e);
104 | onClick(index);
105 | };
106 |
107 | accessibilityProps.onKeyDown = (e) => {
108 | const { which } = e;
109 | if (which === KeyCode.ENTER || which === KeyCode.SPACE) {
110 | onClick(index);
111 | }
112 | };
113 | }
114 |
115 | // ========================= Render =========================
116 | const mergedStatus = status || 'wait';
117 |
118 | const classString = cls(
119 | itemCls,
120 | `${itemCls}-${mergedStatus}`,
121 | {
122 | [`${itemCls}-custom`]: icon,
123 | [`${itemCls}-active`]: active,
124 | [`${itemCls}-disabled`]: disabled === true,
125 | },
126 | className,
127 | classNames.item,
128 | );
129 |
130 | const wrapperNode = (
131 |
132 |
133 | {iconRender?.(renderInfo)}
134 |
135 |
136 |
137 |
138 | {title}
139 |
140 | {subTitle && (
141 |
146 | {subTitle}
147 |
148 | )}
149 |
150 | {!last && (
151 |
152 | )}
153 |
154 | {mergedContent && (
155 |
159 | {mergedContent}
160 |
161 | )}
162 |
163 |
164 | );
165 |
166 | let stepNode: React.ReactNode = (
167 |
176 | {itemWrapperRender ? itemWrapperRender(wrapperNode) : wrapperNode}
177 |
178 | );
179 |
180 | if (itemRender) {
181 | stepNode = (itemRender(stepNode, renderInfo) || null) as React.ReactElement;
182 | }
183 |
184 | return stepNode;
185 | }
186 |
--------------------------------------------------------------------------------
/src/Steps.tsx:
--------------------------------------------------------------------------------
1 | /* eslint react/no-did-mount-set-state: 0, react/prop-types: 0 */
2 | import cls from 'classnames';
3 | import React from 'react';
4 | import Step from './Step';
5 |
6 | export type Status = 'error' | 'process' | 'finish' | 'wait';
7 |
8 | export type SemanticName =
9 | | 'root'
10 | | 'item'
11 | | 'itemWrapper'
12 | | 'itemHeader'
13 | | 'itemTitle'
14 | | 'itemSubtitle'
15 | | 'itemSection'
16 | | 'itemContent'
17 | | 'itemIcon'
18 | | 'itemRail';
19 |
20 | export type StepItem = {
21 | /** @deprecated Please use `content` instead. */
22 | description?: React.ReactNode;
23 | content?: React.ReactNode;
24 | disabled?: boolean;
25 | icon?: React.ReactNode;
26 | status?: Status;
27 | subTitle?: React.ReactNode;
28 | title?: React.ReactNode;
29 | } & Pick, 'onClick' | 'className' | 'style'>;
30 |
31 | export type StepIconRender = (info: {
32 | index: number;
33 | status: Status;
34 | title: React.ReactNode;
35 | // @deprecated Please use `content` instead.
36 | description: React.ReactNode;
37 | content: React.ReactNode;
38 | node: React.ReactNode;
39 | }) => React.ReactNode;
40 |
41 | export type RenderInfo = {
42 | index: number;
43 | active: boolean;
44 | item: StepItem;
45 | };
46 |
47 | export interface StepsProps {
48 | // style
49 | prefixCls?: string;
50 | style?: React.CSSProperties;
51 | className?: string;
52 | classNames?: Partial>;
53 | styles?: Partial>;
54 | rootClassName?: string;
55 |
56 | // layout
57 | orientation?: 'horizontal' | 'vertical';
58 | titlePlacement?: 'horizontal' | 'vertical';
59 |
60 | // data
61 | status?: Status;
62 | current?: number;
63 | initial?: number;
64 | items?: StepItem[];
65 | onChange?: (current: number) => void;
66 |
67 | // render
68 | iconRender?: (info: RenderInfo) => React.ReactNode;
69 | itemRender?: (originNode: React.ReactElement, info: RenderInfo) => React.ReactNode;
70 | itemWrapperRender?: (originNode: React.ReactElement) => React.ReactNode;
71 | }
72 |
73 | export default function Steps(props: StepsProps) {
74 | const {
75 | // style
76 | prefixCls = 'rc-steps',
77 | style,
78 | className,
79 | classNames = {},
80 | styles = {},
81 | rootClassName,
82 |
83 | // layout
84 | orientation = 'horizontal',
85 | titlePlacement = 'horizontal',
86 |
87 | // data
88 | status = 'process',
89 | current = 0,
90 | initial = 0,
91 | onChange,
92 | items,
93 |
94 | // render
95 | iconRender,
96 | itemRender,
97 | itemWrapperRender,
98 |
99 | ...restProps
100 | } = props;
101 |
102 | // ============================= layout =============================
103 | const isVertical = orientation === 'vertical';
104 | const mergedOrientation = isVertical ? 'vertical' : 'horizontal';
105 | const mergeTitlePlacement =
106 | !isVertical && titlePlacement === 'vertical' ? 'vertical' : 'horizontal';
107 |
108 | // ============================= styles =============================
109 | const classString = cls(
110 | prefixCls,
111 | `${prefixCls}-${mergedOrientation}`,
112 | `${prefixCls}-title-${mergeTitlePlacement}`,
113 | rootClassName,
114 | className,
115 | classNames.root,
116 | );
117 |
118 | // ============================== Data ==============================
119 | const mergedItems = React.useMemo(() => (items || []).filter(Boolean), [items]);
120 | const statuses = React.useMemo(
121 | () =>
122 | mergedItems.map(({ status: itemStatus }, index) => {
123 | const stepNumber = initial + index;
124 |
125 | if (!itemStatus) {
126 | if (stepNumber === current) {
127 | return status;
128 | } else if (stepNumber < current) {
129 | return 'finish';
130 | }
131 | return 'wait';
132 | }
133 |
134 | return itemStatus;
135 | }),
136 | [mergedItems, status, current, initial],
137 | );
138 |
139 | // ============================= events =============================
140 | const onStepClick = (next: number) => {
141 | if (onChange && current !== next) {
142 | onChange(next);
143 | }
144 | };
145 |
146 | // ============================= render =============================
147 | const renderStep = (item: StepItem, index: number) => {
148 | const stepIndex = initial + index;
149 |
150 | const itemStatus = statuses[index];
151 | const nextStatus = statuses[index + 1];
152 |
153 | const data = {
154 | ...item,
155 | status: itemStatus,
156 | };
157 |
158 | return (
159 |
177 | );
178 | };
179 |
180 | return (
181 |
189 | {mergedItems.map(renderStep)}
190 |
191 | );
192 | }
193 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import Steps, { type StepsProps } from './Steps';
2 | import Step from './Step';
3 |
4 | export { Step };
5 | export type { StepsProps };
6 | export default Steps;
7 |
--------------------------------------------------------------------------------
/src/interface.ts:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/__snapshots__/index.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Steps render renders correctly 1`] = `
4 |
7 |
34 |
61 |
88 |
91 |
94 |
97 |
100 |
109 |
110 |
111 |
112 |
113 | `;
114 |
115 | exports[`Steps render renders current correctly 1`] = `
116 |
119 |
122 |
125 |
128 |
131 |
143 |
144 |
145 |
146 |
149 |
152 |
155 |
158 |
170 |
171 |
172 |
173 |
176 |
179 |
182 |
185 |
197 |
198 |
199 |
200 |
203 |
206 |
209 |
212 |
221 |
222 |
223 |
224 |
225 | `;
226 |
227 | exports[`Steps render renders progressDot correctly 1`] = `
228 |
231 |
234 |
237 |
240 |
243 |
255 |
256 |
257 |
258 |
261 |
264 |
267 |
270 |
282 |
283 |
284 |
285 |
288 |
291 |
294 |
297 |
309 |
310 |
311 |
312 |
315 |
318 |
321 |
324 |
333 |
334 |
335 |
336 |
337 | `;
338 |
339 | exports[`Steps render renders progressDot function correctly 1`] = `
340 |
343 |
346 |
349 |
352 |
355 |
367 |
368 |
369 |
370 |
373 |
376 |
379 |
382 |
394 |
395 |
396 |
397 |
400 |
403 |
406 |
409 |
421 |
422 |
423 |
424 |
427 |
430 |
433 |
436 |
445 |
446 |
447 |
448 |
449 | `;
450 |
451 | exports[`Steps render renders status correctly 1`] = `
452 |
455 |
458 |
461 |
464 |
467 |
479 |
480 |
481 |
482 |
485 |
488 |
491 |
494 |
506 |
507 |
508 |
509 |
512 |
515 |
518 |
521 |
533 |
534 |
535 |
536 |
539 |
542 |
545 |
548 |
557 |
558 |
559 |
560 |
561 | `;
562 |
563 | exports[`Steps render renders step with description 1`] = `
564 |
567 |
570 |
573 |
576 |
579 |
591 |
594 | xx
595 |
596 |
597 |
598 |
599 |
602 |
605 |
608 |
611 |
623 |
626 | xx
627 |
628 |
629 |
630 |
631 |
634 |
637 |
640 |
643 |
655 |
658 | xx
659 |
660 |
661 |
662 |
663 |
666 |
669 |
672 |
675 |
684 |
687 | xx
688 |
689 |
690 |
691 |
692 |
693 | `;
694 |
695 | exports[`Steps render renders step with description and status 1`] = `
696 |
699 |
702 |
705 |
708 |
711 |
723 |
726 | xx
727 |
728 |
729 |
730 |
731 |
734 |
737 |
740 |
743 |
755 |
758 | xx
759 |
760 |
761 |
762 |
763 |
766 |
769 |
772 |
775 |
787 |
790 | xx
791 |
792 |
793 |
794 |
795 |
798 |
801 |
804 |
807 |
816 |
819 | xx
820 |
821 |
822 |
823 |
824 |
825 | `;
826 |
827 | exports[`Steps render renders stepIcon function correctly 1`] = `
828 |
831 |
834 |
837 |
840 |
843 |
855 |
856 |
857 |
858 |
861 |
864 |
867 |
870 |
882 |
883 |
884 |
885 |
888 |
891 |
894 |
897 |
909 |
910 |
911 |
912 |
915 |
918 |
921 |
924 |
933 |
934 |
935 |
936 |
937 | `;
938 |
939 | exports[`Steps render renders titlePlacement correctly 1`] = `
940 |
943 |
946 |
949 |
952 |
955 |
967 |
968 |
969 |
970 |
973 |
976 |
979 |
982 |
994 |
995 |
996 |
997 |
1000 |
1003 |
1006 |
1009 |
1021 |
1022 |
1023 |
1024 |
1027 |
1030 |
1033 |
1036 |
1045 |
1046 |
1047 |
1048 |
1049 | `;
1050 |
1051 | exports[`Steps render renders vertical correctly 1`] = `
1052 |
1056 |
1059 |
1062 |
1065 |
1068 |
1080 |
1081 |
1082 |
1083 |
1086 |
1089 |
1092 |
1095 |
1107 |
1108 |
1109 |
1110 |
1113 |
1116 |
1119 |
1122 |
1134 |
1135 |
1136 |
1137 |
1140 |
1143 |
1146 |
1149 |
1158 |
1159 |
1160 |
1161 |
1162 | `;
1163 |
1164 | exports[`Steps render renders with falsy children 1`] = `
1165 |
1168 |
1171 |
1174 |
1177 |
1180 |
1192 |
1195 | xx
1196 |
1197 |
1198 |
1199 |
1200 |
1203 |
1206 |
1209 |
1212 |
1230 |
1233 | xx
1234 |
1235 |
1236 |
1237 |
1238 |
1241 |
1244 |
1247 |
1250 |
1262 |
1265 | xx
1266 |
1267 |
1268 |
1269 |
1270 |
1273 |
1276 |
1279 |
1282 |
1291 |
1294 | xx
1295 |
1296 |
1297 |
1298 |
1299 |
1300 | `;
1301 |
1302 | exports[`Steps should render customIcon correctly 1`] = `
1303 |
1306 |
1309 |
1312 |
1315 |
1318 |
1330 |
1331 |
1332 |
1333 |
1336 |
1339 |
1342 |
1345 |
1357 |
1358 |
1359 |
1360 |
1363 |
1366 |
1369 |
1372 |
1381 |
1382 |
1383 |
1384 |
1385 | `;
1386 |
--------------------------------------------------------------------------------
/tests/index.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, fireEvent } from '@testing-library/react';
3 | import Steps from '../src';
4 |
5 | describe('Steps', () => {
6 | describe('render', () => {
7 | const description = 'xx';
8 |
9 | const setSteps = (props) => (
10 |
27 | );
28 |
29 | it('renders correctly', () => {
30 | const { container } = render(setSteps({}));
31 | expect(container.firstChild).toMatchSnapshot();
32 | });
33 |
34 | it('renders without items', () => {
35 | expect(() => {
36 | render(setSteps({ items: undefined }));
37 | }).not.toThrow();
38 | });
39 |
40 | it('renders current correctly', () => {
41 | const { container } = render(setSteps({ current: 2 }));
42 | expect(container.firstChild).toMatchSnapshot();
43 | });
44 |
45 | it('renders status correctly', () => {
46 | const { container } = render(setSteps({ current: 2, status: 'error' }));
47 | expect(container.firstChild).toMatchSnapshot();
48 | });
49 |
50 | it('renders vertical correctly', () => {
51 | const { container } = render(setSteps({ direction: 'vertical' }));
52 | expect(container.firstChild).toMatchSnapshot();
53 | });
54 |
55 | it('renders titlePlacement correctly', () => {
56 | const { container } = render(setSteps({ titlePlacement: 'vertical' }));
57 | expect(container.firstChild).toMatchSnapshot();
58 | });
59 |
60 | it('renders progressDot correctly', () => {
61 | const { container } = render(setSteps({ progressDot: true }));
62 | expect(container.firstChild).toMatchSnapshot();
63 | });
64 |
65 | it('renders progressDot function correctly', () => {
66 | const { container } = render(setSteps({ progressDot: () => a }));
67 | expect(container.firstChild).toMatchSnapshot();
68 | });
69 |
70 | it('renders stepIcon function correctly', () => {
71 | const { container } = render(setSteps({ stepIcon: () => a }));
72 | expect(container.firstChild).toMatchSnapshot();
73 | });
74 |
75 | it('renders step with description', () => {
76 | const { container } = render(
77 | ,
97 | );
98 | expect(container.firstChild).toMatchSnapshot();
99 | });
100 |
101 | it('renders step with description and status', () => {
102 | const { container } = render(
103 | ,
127 | );
128 | expect(container.firstChild).toMatchSnapshot();
129 | });
130 |
131 | it('renders with falsy children', () => {
132 | const { container } = render(
133 | ,
163 | );
164 | expect(container.firstChild).toMatchSnapshot();
165 | });
166 | });
167 |
168 | it('should render customIcon correctly', () => {
169 | const Icon = ({ type }) => ;
170 | const { container } = render(
171 | ,
177 | },
178 | {
179 | title: '步骤2',
180 | icon: 'apple',
181 | },
182 | {
183 | title: '步骤3',
184 | icon: 'github',
185 | },
186 | ]}
187 | />,
188 | );
189 | expect(container.firstChild).toMatchSnapshot();
190 | });
191 |
192 | it('onChange', () => {
193 | const onChange = jest.fn();
194 | const { container } = render(
195 | ,
212 | );
213 | const items = container.querySelectorAll('.rc-steps-item');
214 | fireEvent.click(items[1]);
215 | expect(onChange).toHaveBeenCalledTimes(1);
216 | });
217 |
218 | it('items out of render function', () => {
219 | const items = [
220 | {
221 | title: '已完成',
222 | },
223 | {
224 | title: '进行中',
225 | },
226 | ];
227 |
228 | let current = 0;
229 | const onChange = (val) => {
230 | current = val;
231 | };
232 | const { container, rerender } = render(
233 | ,
234 | );
235 |
236 | const step = container.querySelectorAll('.rc-steps-item')[1];
237 | fireEvent.click(step);
238 | rerender();
239 | expect(container.querySelectorAll('.rc-steps-item')[1].classList).toContain(
240 | 'rc-steps-item-process',
241 | );
242 | });
243 |
244 | it('onClick', () => {
245 | const onClick = jest.fn();
246 | const onChange = jest.fn();
247 | const { container } = render(
248 | ,
266 | );
267 |
268 | const btn = container.querySelectorAll('.rc-steps-item')[0];
269 | fireEvent.click(btn);
270 | expect(onClick).toHaveBeenCalled();
271 | });
272 |
273 | it('disabled', () => {
274 | const onChange = jest.fn();
275 | const { container } = render(
276 | ,
277 | );
278 |
279 | const items = container.querySelectorAll('.rc-steps-item');
280 | fireEvent.click(items[2]);
281 | expect(onChange).not.toBeCalled();
282 | });
283 |
284 | it('key board support', () => {
285 | const onChange = jest.fn();
286 | const { container } = render(
287 | ,
301 | );
302 |
303 | const button = container.querySelectorAll('[role="button"]')[1];
304 | fireEvent.keyDown(button, { key: 'Enter', keyCode: 13, which: 13 });
305 |
306 | expect(onChange).toHaveBeenCalledWith(1);
307 | });
308 |
309 | it('itemRender', () => {
310 | const { container } = render(
311 | {
318 | return {oriNode}
;
319 | }}
320 | />,
321 | );
322 |
323 | expect(container.querySelector('.bamboo')).toBeTruthy();
324 | expect(container.querySelectorAll('.bamboo')).toHaveLength(1);
325 | expect(container.querySelector('.rc-steps-item')).toBeTruthy();
326 | });
327 |
328 | it('itemWrapperRender', () => {
329 | const { container } = render(
330 | {
337 | return {oriNode}
;
338 | }}
339 | />,
340 | );
341 |
342 | expect(container.querySelector('.bamboo')).toBeTruthy();
343 | expect(container.querySelectorAll('.bamboo')).toHaveLength(1);
344 | expect(container.querySelector('.rc-steps-item')).toBeTruthy();
345 | expect(container.querySelector('.rc-steps-item > .bamboo')).toBeTruthy();
346 | });
347 | });
348 |
--------------------------------------------------------------------------------
/tests/semantic.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, fireEvent } from '@testing-library/react';
3 | import Steps, { type StepsProps } from '../src';
4 | import type { SemanticName } from '../src/Steps';
5 |
6 | describe('Steps.Semantic', () => {
7 | const renderSteps = (props: Partial) => (
8 | ({
10 | title: `Step ${index + 1}`,
11 | subTitle: `SubTitle ${index + 1}`,
12 | description: `Description ${index + 1}`,
13 | }))}
14 | {...props}
15 | />
16 | );
17 |
18 | it('semantic structure', () => {
19 | const classNames: Record = {
20 | root: 'custom-root',
21 | item: 'custom-item',
22 | itemWrapper: 'custom-item-wrapper',
23 | itemIcon: 'custom-item-icon',
24 | itemSection: 'custom-item-section',
25 | itemHeader: 'custom-item-header',
26 | itemTitle: 'custom-item-title',
27 | itemSubtitle: 'custom-item-subtitle',
28 | itemContent: 'custom-item-content',
29 | itemRail: 'custom-item-rail',
30 | };
31 |
32 | const classNamesTargets: Record = {
33 | root: 'rc-steps',
34 | item: 'rc-steps-item',
35 | itemWrapper: 'rc-steps-item-wrapper',
36 | itemIcon: 'rc-steps-item-icon',
37 | itemSection: 'rc-steps-item-section',
38 | itemHeader: 'rc-steps-item-header',
39 | itemTitle: 'rc-steps-item-title',
40 | itemSubtitle: 'rc-steps-item-subtitle',
41 | itemContent: 'rc-steps-item-content',
42 | itemRail: 'rc-steps-item-rail',
43 | };
44 |
45 | const styles: Record> = {
46 | root: { color: 'red' },
47 | item: { color: 'blue' },
48 | itemWrapper: { color: 'green' },
49 | itemIcon: { color: 'yellow' },
50 | itemSection: { color: 'purple' },
51 | itemHeader: { color: 'orange' },
52 | itemTitle: { color: 'pink' },
53 | itemSubtitle: { color: 'cyan' },
54 | itemContent: { color: 'magenta' },
55 | itemRail: { color: 'lime' },
56 | };
57 |
58 | const { container } = render(
59 | renderSteps({
60 | classNames,
61 | styles,
62 | }),
63 | );
64 |
65 | Object.keys(classNames).forEach((key) => {
66 | const className = classNames[key as SemanticName];
67 | const oriClassName = classNamesTargets[key as SemanticName];
68 | const style = styles[key as SemanticName];
69 |
70 | const element = container.querySelector(`.${className}`);
71 | expect(element).toBeTruthy();
72 | expect(element).toHaveClass(oriClassName);
73 | expect(element).toHaveStyle(style);
74 | });
75 | });
76 | });
77 |
--------------------------------------------------------------------------------
/tests/setup.js:
--------------------------------------------------------------------------------
1 | global.requestAnimationFrame = global.requestAnimationFrame || function raf(cb) {
2 | return setTimeout(cb, 0);
3 | };
4 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "moduleResolution": "node",
5 | "baseUrl": "./",
6 | "jsx": "preserve",
7 | "declaration": true,
8 | "skipLibCheck": true,
9 | "esModuleInterop": true,
10 | "paths": {
11 | "@/*": ["src/*"],
12 | "@@/*": ["dumi/tmp/*"],
13 | "@rc-component/steps": ["src/index.ts"]
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------