Skip to content

Commit 78c2611

Browse files
authored
feat: add 'horizontal-alternate' (#374)
* feat: add 'horizontal-alternate' * chore: bump version to 1.2.0-alpha.0 * test: update name * chore: support rail reverse * chore: bump version to 1.2.0-alpha.1 * chore: clean up * chore: support components * chore: bump version to 1.2.0-alpha.2 * test: add test case * chore: bump version to 1.2.0-alpha.3 * feat: item support semantic * chore: bump version to 1.2.0-alpha.4 * refactor: use ol instead * chore: bump version to 1.2.0-alpha.5
1 parent b8fc53e commit 78c2611

File tree

11 files changed

+409
-167
lines changed

11 files changed

+409
-167
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rc-component/steps",
3-
"version": "1.1.0",
3+
"version": "1.2.0-alpha.5",
44
"description": "steps ui component for react",
55
"keywords": [
66
"react",

src/Context.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as React from 'react';
2+
import type { StepsProps } from './Steps';
3+
4+
export interface StepsContextProps {
5+
prefixCls: string;
6+
classNames: NonNullable<StepsProps['classNames']>;
7+
styles: NonNullable<StepsProps['styles']>;
8+
}
9+
10+
export const StepsContext = React.createContext<StepsContextProps>(null!);

src/Rail.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
11
import * as React from 'react';
22
import cls from 'classnames';
3-
import type { Status, StepsProps } from './Steps';
3+
import type { Status } from './Steps';
44

55
export interface RailProps {
66
prefixCls: string;
7-
classNames: StepsProps['classNames'];
8-
styles: StepsProps['styles'];
7+
className: string;
8+
style: React.CSSProperties;
99
status: Status;
1010
}
1111

1212
export default function Rail(props: RailProps) {
13-
const { prefixCls, classNames, styles, status } = props;
13+
const { prefixCls, className, style, status } = props;
1414
const railCls = `${prefixCls}-rail`;
1515

1616
// ============================= render =============================
17-
return (
18-
<div
19-
className={cls(railCls, `${railCls}-${status}`, classNames.itemRail)}
20-
style={styles.itemRail}
21-
/>
22-
);
17+
return <div className={cls(railCls, `${railCls}-${status}`, className)} style={style} />;
2318
}

src/Step.tsx

Lines changed: 96 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ import cls from 'classnames';
44
import KeyCode from '@rc-component/util/lib/KeyCode';
55
import type { Status, StepItem, StepsProps } from './Steps';
66
import Rail from './Rail';
7+
import { UnstableContext } from './UnstableContext';
8+
import StepIcon, { StepIconSemanticContext } from './StepIcon';
9+
10+
function hasContent<T>(value: T) {
11+
return value !== undefined && value !== null;
12+
}
713

814
export interface StepProps {
915
// style
@@ -18,12 +24,6 @@ export interface StepProps {
1824
index: number;
1925
last: boolean;
2026

21-
// stepIndex?: number;
22-
// stepNumber?: number;
23-
// title?: React.ReactNode;
24-
// subTitle?: React.ReactNode;
25-
// description?: React.ReactNode;
26-
2727
// render
2828
iconRender?: StepsProps['iconRender'];
2929
icon?: React.ReactNode;
@@ -59,6 +59,9 @@ export default function Step(props: StepProps) {
5959

6060
const itemCls = `${prefixCls}-item`;
6161

62+
// ==================== Internal Context ====================
63+
const { railFollowPrevStatus } = React.useContext(UnstableContext);
64+
6265
// ========================== Data ==========================
6366
const {
6467
onClick: onItemClick,
@@ -72,6 +75,9 @@ export default function Step(props: StepProps) {
7275

7376
className,
7477
style,
78+
classNames: itemClassNames = {},
79+
styles: itemStyles = {},
80+
7581
...restItemProps
7682
} = data;
7783

@@ -92,8 +98,8 @@ export default function Step(props: StepProps) {
9298
const accessibilityProps: {
9399
role?: string;
94100
tabIndex?: number;
95-
onClick?: React.MouseEventHandler<HTMLDivElement>;
96-
onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;
101+
onClick?: React.MouseEventHandler<HTMLLIElement>;
102+
onKeyDown?: React.KeyboardEventHandler<HTMLLIElement>;
97103
} = {};
98104

99105
if (clickable) {
@@ -115,46 +121,109 @@ export default function Step(props: StepProps) {
115121
// ========================= Render =========================
116122
const mergedStatus = status || 'wait';
117123

124+
const hasTitle = hasContent(title);
125+
const hasSubTitle = hasContent(subTitle);
126+
118127
const classString = cls(
119128
itemCls,
120129
`${itemCls}-${mergedStatus}`,
121130
{
122131
[`${itemCls}-custom`]: icon,
123132
[`${itemCls}-active`]: active,
124133
[`${itemCls}-disabled`]: disabled === true,
134+
[`${itemCls}-empty-header`]: !hasTitle && !hasSubTitle,
125135
},
126136
className,
127137
classNames.item,
138+
itemClassNames.root,
128139
);
129140

141+
let iconNode = <StepIcon />;
142+
if (iconRender) {
143+
iconNode = iconRender(iconNode, {
144+
...renderInfo,
145+
components: {
146+
Icon: StepIcon,
147+
},
148+
}) as React.ReactElement;
149+
}
150+
130151
const wrapperNode = (
131-
<div className={cls(`${itemCls}-wrapper`, classNames.itemWrapper)} style={styles.itemWrapper}>
132-
<div className={cls(`${itemCls}-icon`, classNames.itemIcon)} style={styles.itemIcon}>
133-
{iconRender?.(renderInfo)}
134-
</div>
135-
<div className={cls(`${itemCls}-section`, classNames.itemSection)} style={styles.itemSection}>
136-
<div className={cls(`${itemCls}-header`, classNames.itemHeader)} style={styles.itemHeader}>
137-
<div className={cls(`${itemCls}-title`, classNames.itemTitle)} style={styles.itemTitle}>
138-
{title}
139-
</div>
140-
{subTitle && (
152+
<div
153+
className={cls(`${itemCls}-wrapper`, classNames.itemWrapper, itemClassNames.wrapper)}
154+
style={{
155+
...styles.itemWrapper,
156+
...itemStyles.wrapper,
157+
}}
158+
>
159+
{/* Icon */}
160+
<StepIconSemanticContext.Provider
161+
value={{
162+
className: itemClassNames.icon,
163+
style: itemStyles.icon,
164+
}}
165+
>
166+
{iconNode}
167+
</StepIconSemanticContext.Provider>
168+
169+
<div
170+
className={cls(`${itemCls}-section`, classNames.itemSection, itemClassNames.section)}
171+
style={{
172+
...styles.itemSection,
173+
...itemStyles.section,
174+
}}
175+
>
176+
<div
177+
className={cls(`${itemCls}-header`, classNames.itemHeader, itemClassNames.header)}
178+
style={{
179+
...styles.itemHeader,
180+
...itemStyles.header,
181+
}}
182+
>
183+
{hasTitle && (
184+
<div
185+
className={cls(`${itemCls}-title`, classNames.itemTitle, itemClassNames.title)}
186+
style={{
187+
...styles.itemTitle,
188+
...itemStyles.title,
189+
}}
190+
>
191+
{title}
192+
</div>
193+
)}
194+
{hasSubTitle && (
141195
<div
142196
title={typeof subTitle === 'string' ? subTitle : undefined}
143-
className={cls(`${itemCls}-subtitle`, classNames.itemSubtitle)}
144-
style={styles.itemSubtitle}
197+
className={cls(
198+
`${itemCls}-subtitle`,
199+
classNames.itemSubtitle,
200+
itemClassNames.subtitle,
201+
)}
202+
style={{
203+
...styles.itemSubtitle,
204+
...itemStyles.subtitle,
205+
}}
145206
>
146207
{subTitle}
147208
</div>
148209
)}
149210

150211
{!last && (
151-
<Rail prefixCls={itemCls} classNames={classNames} styles={styles} status={nextStatus} />
212+
<Rail
213+
prefixCls={itemCls}
214+
className={cls(classNames.itemRail, itemClassNames.rail)}
215+
style={{
216+
...styles.itemRail,
217+
...itemStyles.rail,
218+
}}
219+
status={railFollowPrevStatus ? status : nextStatus}
220+
/>
152221
)}
153222
</div>
154-
{mergedContent && (
223+
{hasContent(mergedContent) && (
155224
<div
156-
className={cls(`${itemCls}-content`, classNames.itemContent)}
157-
style={styles.itemContent}
225+
className={cls(`${itemCls}-content`, classNames.itemContent, itemClassNames.content)}
226+
style={{ ...styles.itemContent, ...itemStyles.content }}
158227
>
159228
{mergedContent}
160229
</div>
@@ -164,17 +233,18 @@ export default function Step(props: StepProps) {
164233
);
165234

166235
let stepNode: React.ReactNode = (
167-
<div
236+
<li
168237
{...restItemProps}
169238
{...accessibilityProps}
170239
className={classString}
171240
style={{
172241
...styles.item,
242+
...itemStyles.root,
173243
...style,
174244
}}
175245
>
176246
{itemWrapperRender ? itemWrapperRender(wrapperNode) : wrapperNode}
177-
</div>
247+
</li>
178248
);
179249

180250
if (itemRender) {

src/StepIcon.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import * as React from 'react';
2+
import cls from 'classnames';
3+
import { StepsContext } from './Context';
4+
import pickAttrs from '@rc-component/util/lib/pickAttrs';
5+
6+
export interface StepIconSemanticContextProps {
7+
className?: string;
8+
style?: React.CSSProperties;
9+
}
10+
11+
export const StepIconSemanticContext = React.createContext<StepIconSemanticContextProps>({});
12+
13+
export type StepIconProps = React.HTMLAttributes<HTMLDivElement>;
14+
15+
const StepIcon = React.forwardRef<HTMLDivElement, StepIconProps>((props, ref) => {
16+
const { className, style, children, ...restProps } = props;
17+
18+
const { prefixCls, classNames, styles } = React.useContext(StepsContext);
19+
const { className: itemClassName, style: itemStyle } = React.useContext(StepIconSemanticContext);
20+
21+
const itemCls = `${prefixCls}-item`;
22+
23+
return (
24+
<div
25+
{...pickAttrs(restProps, false)}
26+
ref={ref}
27+
className={cls(`${itemCls}-icon`, classNames.itemIcon, itemClassName, className)}
28+
style={{
29+
...styles.itemIcon,
30+
...itemStyle,
31+
...style,
32+
}}
33+
>
34+
{children}
35+
</div>
36+
);
37+
});
38+
39+
export default StepIcon;

0 commit comments

Comments
 (0)