Skip to content

Commit 7d25144

Browse files
authored
feat: Tour support classnames and styles (#70)
* feat: Tour support classnames and styles * support classname and style * update content to section * update
1 parent 5c25fe0 commit 7d25144

File tree

7 files changed

+183
-72
lines changed

7 files changed

+183
-72
lines changed

docs/examples/basic.less

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
}
3030

3131
// Wrapper for the tour content
32-
.@{tour-prefix-cls}-inner {
32+
.@{tour-prefix-cls}-section {
3333
text-align: left;
3434
text-decoration: none;
3535
border-radius: 6px;;

src/Mask.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import classNames from 'classnames';
33
import Portal from '@rc-component/portal';
44
import type { PosInfo } from './hooks/useTarget';
55
import useId from 'rc-util/lib/hooks/useId';
6+
import { SemanticName } from './interface';
67

78
const COVER_PROPS = {
89
fill: 'transparent',
@@ -21,6 +22,8 @@ export interface MaskProps {
2122
animated?: boolean | { placeholder: boolean };
2223
zIndex?: number;
2324
disabledInteraction?: boolean;
25+
classNames?: Partial<Record<SemanticName, string>>;
26+
styles?: Partial<Record<SemanticName, React.CSSProperties>>;
2427
}
2528

2629
const Mask = (props: MaskProps) => {
@@ -34,7 +37,9 @@ const Mask = (props: MaskProps) => {
3437
open,
3538
animated,
3639
zIndex,
37-
disabledInteraction
40+
disabledInteraction,
41+
styles,
42+
classNames: tourClassNames,
3843
} = props;
3944

4045
const id = useId();
@@ -48,7 +53,7 @@ const Mask = (props: MaskProps) => {
4853
return (
4954
<Portal open={open} autoLock>
5055
<div
51-
className={classNames(`${prefixCls}-mask`, rootClassName)}
56+
className={classNames(`${prefixCls}-mask`, rootClassName, tourClassNames?.mask)}
5257
style={{
5358
position: 'fixed',
5459
left: 0,
@@ -57,7 +62,8 @@ const Mask = (props: MaskProps) => {
5762
bottom: 0,
5863
zIndex,
5964
pointerEvents: pos && !disabledInteraction ? 'none' : 'auto',
60-
...style
65+
...style,
66+
...styles?.mask,
6167
}}
6268
>
6369
{showMask ? (

src/Tour.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ const Tour: React.FC<TourProps> = props => {
5151
closable,
5252
builtinPlacements,
5353
disabledInteraction,
54+
styles,
55+
classNames: tourClassNames,
56+
className,
57+
style,
5458
...restProps
5559
} = props;
5660

@@ -66,7 +70,7 @@ const Tour: React.FC<TourProps> = props => {
6670
postState: origin =>
6771
mergedCurrent < 0 || mergedCurrent >= steps.length
6872
? false
69-
: origin ?? true,
73+
: (origin ?? true),
7074
});
7175

7276
// Record if already rended in the DOM to avoid `findDOMNode` issue
@@ -157,6 +161,8 @@ const Tour: React.FC<TourProps> = props => {
157161

158162
const getPopupElement = () => (
159163
<TourStep
164+
styles={styles}
165+
classNames={tourClassNames}
160166
arrow={mergedArrow}
161167
key="content"
162168
prefixCls={prefixCls}
@@ -192,6 +198,8 @@ const Tour: React.FC<TourProps> = props => {
192198
return (
193199
<>
194200
<Mask
201+
styles={styles}
202+
classNames={tourClassNames}
195203
zIndex={zIndex}
196204
prefixCls={prefixCls}
197205
pos={posInfo}
@@ -222,13 +230,15 @@ const Tour: React.FC<TourProps> = props => {
222230
<Portal open={mergedOpen} autoLock>
223231
<div
224232
className={classNames(
233+
className,
225234
rootClassName,
226235
`${prefixCls}-target-placeholder`,
227236
)}
228237
style={{
229238
...(posInfo || CENTER_PLACEHOLDER),
230239
position: 'fixed',
231240
pointerEvents: 'none',
241+
...style,
232242
}}
233243
/>
234244
</Portal>

src/TourStep/DefaultPanel.tsx

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import type { TourStepProps } from '../interface';
33
import classNames from 'classnames';
44
import pickAttrs from 'rc-util/lib/pickAttrs';
55

6-
export type DefaultPanelProps = Exclude<TourStepProps, "closable"> & {
7-
closable: Exclude<TourStepProps["closable"], boolean>;
6+
export type DefaultPanelProps = Exclude<TourStepProps, 'closable'> & {
7+
closable: Exclude<TourStepProps['closable'], boolean>;
88
};
99

1010
export default function DefaultPanel(props: DefaultPanelProps) {
@@ -20,14 +20,21 @@ export default function DefaultPanel(props: DefaultPanelProps) {
2020
onFinish,
2121
className,
2222
closable,
23+
classNames: tourClassNames,
24+
styles,
2325
} = props;
2426
const ariaProps = pickAttrs(closable || {}, true);
25-
const closeIcon = closable?.closeIcon ?? <span className={`${prefixCls}-close-x`}>&times;</span>;
27+
const closeIcon = closable?.closeIcon ?? (
28+
<span className={`${prefixCls}-close-x`}>&times;</span>
29+
);
2630
const mergedClosable = !!closable;
2731

2832
return (
29-
<div className={classNames(`${prefixCls}-content`, className)}>
30-
<div className={`${prefixCls}-inner`}>
33+
<div className={classNames(`${prefixCls}-pannel`, className)}>
34+
<div
35+
className={classNames(`${prefixCls}-section`, tourClassNames?.section)}
36+
style={styles?.section}
37+
>
3138
{mergedClosable && (
3239
<button
3340
type="button"
@@ -39,24 +46,49 @@ export default function DefaultPanel(props: DefaultPanelProps) {
3946
{closeIcon}
4047
</button>
4148
)}
42-
<div className={`${prefixCls}-header`}>
43-
<div className={`${prefixCls}-title`}>{title}</div>
49+
<div
50+
className={classNames(`${prefixCls}-header`, tourClassNames?.header)}
51+
style={styles?.header}
52+
>
53+
<div
54+
className={classNames(`${prefixCls}-title`, tourClassNames?.title)}
55+
style={styles?.title}
56+
>
57+
{title}
58+
</div>
4459
</div>
45-
<div className={`${prefixCls}-description`}>{description}</div>
46-
<div className={`${prefixCls}-footer`}>
60+
<div
61+
className={classNames(
62+
`${prefixCls}-description`,
63+
tourClassNames?.description,
64+
)}
65+
style={styles?.description}
66+
>
67+
{description}
68+
</div>
69+
<div
70+
className={classNames(`${prefixCls}-footer`, tourClassNames?.footer)}
71+
style={styles?.footer}
72+
>
4773
<div className={`${prefixCls}-sliders`}>
4874
{total > 1
4975
? [...Array.from({ length: total }).keys()].map((item, index) => {
50-
return (
51-
<span
52-
key={item}
53-
className={index === current ? 'active' : ''}
54-
/>
55-
);
56-
})
76+
return (
77+
<span
78+
key={item}
79+
className={index === current ? 'active' : ''}
80+
/>
81+
);
82+
})
5783
: null}
5884
</div>
59-
<div className={`${prefixCls}-buttons`}>
85+
<div
86+
className={classNames(
87+
`${prefixCls}-actions`,
88+
tourClassNames?.actions,
89+
)}
90+
style={styles?.actions}
91+
>
6092
{current !== 0 ? (
6193
<button className={`${prefixCls}-prev-btn`} onClick={onPrev}>
6294
Prev

src/interface.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ import type { CSSProperties, ReactNode } from 'react';
44
import type { Gap } from './hooks/useTarget';
55
import { type DefaultPanelProps } from './TourStep/DefaultPanel';
66

7+
export type SemanticName =
8+
| 'section'
9+
| 'footer'
10+
| 'actions'
11+
| 'header'
12+
| 'title'
13+
| 'description'
14+
| 'mask';
15+
716
export interface TourStepInfo {
817
arrow?: boolean | { pointAtCenter: boolean };
918
target?: HTMLElement | (() => HTMLElement) | null | (() => null);
@@ -33,9 +42,15 @@ export interface TourStepProps extends TourStepInfo {
3342
renderPanel?: (step: TourStepProps, current: number) => ReactNode;
3443
onPrev?: () => void;
3544
onNext?: () => void;
45+
classNames?: Partial<Record<SemanticName, string>>;
46+
styles?: Partial<Record<SemanticName, React.CSSProperties>>;
3647
}
3748

3849
export interface TourProps extends Pick<TriggerProps, 'onPopupAlign'> {
50+
classNames?: Partial<Record<SemanticName, string>>;
51+
styles?: Partial<Record<SemanticName, React.CSSProperties>>;
52+
className?: string;
53+
style?: React.CSSProperties;
3954
steps?: TourStepInfo[];
4055
open?: boolean;
4156
defaultCurrent?: number;

0 commit comments

Comments
 (0)