@gm-pc/tour 1.27.0 → 1.27.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/package.json +5 -5
- package/src/components/action.tsx +29 -29
- package/src/components/guide.tsx +140 -140
- package/src/components/index.ts +1 -1
- package/src/components/portal.tsx +23 -23
- package/src/components/svg_mask.tsx +168 -168
- package/src/hook.ts +19 -19
- package/src/index.ts +2 -2
- package/src/reducer.ts +55 -55
- package/src/stories.tsx +310 -310
- package/src/tour.tsx +276 -276
- package/src/types.ts +52 -52
- package/src/utils.ts +172 -172
package/src/tour.tsx
CHANGED
|
@@ -1,276 +1,276 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
forwardRef,
|
|
3
|
-
memo,
|
|
4
|
-
MouseEvent,
|
|
5
|
-
useEffect,
|
|
6
|
-
useImperativeHandle,
|
|
7
|
-
useReducer,
|
|
8
|
-
useRef,
|
|
9
|
-
useState,
|
|
10
|
-
ReactText,
|
|
11
|
-
} from 'react'
|
|
12
|
-
import _ from 'lodash'
|
|
13
|
-
import { TourRefOptions, TourProps, TourStepItem } from './types'
|
|
14
|
-
import reducer, { initialState, ReducerState } from './reducer'
|
|
15
|
-
import { useMutationObserver } from './hook'
|
|
16
|
-
import { Portal } from './components'
|
|
17
|
-
import SvgMask from './components/svg_mask'
|
|
18
|
-
import Guide from './components/guide'
|
|
19
|
-
import Action from './components/action'
|
|
20
|
-
import {
|
|
21
|
-
getNodeRect,
|
|
22
|
-
GetNodeRectOptions,
|
|
23
|
-
getWindow,
|
|
24
|
-
inView,
|
|
25
|
-
isBody,
|
|
26
|
-
scrollParent,
|
|
27
|
-
scrollSmooth,
|
|
28
|
-
} from './utils'
|
|
29
|
-
|
|
30
|
-
const Tour = forwardRef<TourRefOptions, TourProps>(
|
|
31
|
-
(
|
|
32
|
-
{
|
|
33
|
-
children,
|
|
34
|
-
isOpen,
|
|
35
|
-
startAt = 0,
|
|
36
|
-
steps = [],
|
|
37
|
-
scrollDuration = 1,
|
|
38
|
-
className,
|
|
39
|
-
closeWithMask = false,
|
|
40
|
-
onRequestClose,
|
|
41
|
-
onAfterOpen,
|
|
42
|
-
onBeforeClose,
|
|
43
|
-
disableButtons = false,
|
|
44
|
-
disableInteraction = true,
|
|
45
|
-
disableInteractionClassName,
|
|
46
|
-
maskClassName,
|
|
47
|
-
rounded = 3,
|
|
48
|
-
maskSpace = 10,
|
|
49
|
-
},
|
|
50
|
-
ref
|
|
51
|
-
) => {
|
|
52
|
-
const [current, setCurrent] = useState(0)
|
|
53
|
-
const [started, setStarted] = useState(false)
|
|
54
|
-
const [state, dispatch] = useReducer(reducer, initialState)
|
|
55
|
-
const helper = useRef<HTMLDivElement>(null)
|
|
56
|
-
const observer = useRef<HTMLDivElement>()
|
|
57
|
-
|
|
58
|
-
useMutationObserver(observer, (mutations, observer) => {
|
|
59
|
-
if (isOpen) {
|
|
60
|
-
showStep()
|
|
61
|
-
mutations.forEach((mutation) => {
|
|
62
|
-
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
|
|
63
|
-
setTimeout(() => {
|
|
64
|
-
makeCalculation(getNodeRect(mutation.addedNodes[0] as HTMLElement))
|
|
65
|
-
}, 500)
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
} else {
|
|
69
|
-
observer.disconnect()
|
|
70
|
-
}
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
const debouncedShowStep = _.debounce(() => {
|
|
75
|
-
showStep()
|
|
76
|
-
}, 100)
|
|
77
|
-
window.addEventListener('resize', debouncedShowStep, false)
|
|
78
|
-
|
|
79
|
-
if (isOpen) {
|
|
80
|
-
if (!started) {
|
|
81
|
-
setStarted(true)
|
|
82
|
-
makeCalculation(
|
|
83
|
-
{
|
|
84
|
-
width: maskSpace * -1,
|
|
85
|
-
height: maskSpace * -1,
|
|
86
|
-
top: rounded * -1,
|
|
87
|
-
left: rounded * -1,
|
|
88
|
-
},
|
|
89
|
-
'center'
|
|
90
|
-
)
|
|
91
|
-
setCurrent(startAt)
|
|
92
|
-
showStep(startAt)
|
|
93
|
-
} else {
|
|
94
|
-
showStep()
|
|
95
|
-
}
|
|
96
|
-
if (helper.current) {
|
|
97
|
-
helper.current.focus()
|
|
98
|
-
document.body.style.overflowY = 'hidden'
|
|
99
|
-
if (onAfterOpen && typeof onAfterOpen === 'function') {
|
|
100
|
-
onAfterOpen(helper.current)
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return () => {
|
|
106
|
-
window.removeEventListener('resize', debouncedShowStep)
|
|
107
|
-
}
|
|
108
|
-
}, [current, isOpen])
|
|
109
|
-
|
|
110
|
-
useImperativeHandle(ref, () => ({
|
|
111
|
-
apiToNextStep() {
|
|
112
|
-
handleNextStep()
|
|
113
|
-
},
|
|
114
|
-
apiClose() {
|
|
115
|
-
close()
|
|
116
|
-
},
|
|
117
|
-
apiRecalculate() {
|
|
118
|
-
calculatePosition()
|
|
119
|
-
},
|
|
120
|
-
}))
|
|
121
|
-
|
|
122
|
-
async function showStep(nextStep?: number) {
|
|
123
|
-
const step = steps[nextStep!] ?? steps[current]
|
|
124
|
-
if (step.actionBefore && typeof step.actionBefore === 'function') {
|
|
125
|
-
await step.actionBefore()
|
|
126
|
-
}
|
|
127
|
-
if (step.observe) {
|
|
128
|
-
observer.current = document.querySelector(step.observe) as HTMLDivElement
|
|
129
|
-
}
|
|
130
|
-
calculatePosition(nextStep)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function calculatePosition(nextStep?: number) {
|
|
134
|
-
const step = steps[nextStep!] ?? steps[current]
|
|
135
|
-
const node = getNode(step)
|
|
136
|
-
const { w, h } = getWindow()
|
|
137
|
-
if (node) {
|
|
138
|
-
const nodeRect = getNodeRect(node as HTMLElement)
|
|
139
|
-
if (!inView({ ...nodeRect, w, h })) {
|
|
140
|
-
const parentScroll = scrollParent(node)
|
|
141
|
-
const offset = nodeRect.height > h ? -25 : -(h / 2) + nodeRect.height / 2
|
|
142
|
-
scrollSmooth(node as HTMLElement, {
|
|
143
|
-
context: isBody(parentScroll as HTMLElement)
|
|
144
|
-
? window
|
|
145
|
-
: (parentScroll as HTMLElement),
|
|
146
|
-
duration: scrollDuration,
|
|
147
|
-
offset,
|
|
148
|
-
callback(target: ReactText | HTMLElement) {
|
|
149
|
-
makeCalculation(getNodeRect(target as HTMLElement), step.position)
|
|
150
|
-
},
|
|
151
|
-
})
|
|
152
|
-
} else {
|
|
153
|
-
makeCalculation(nodeRect, step.position)
|
|
154
|
-
}
|
|
155
|
-
} else {
|
|
156
|
-
dispatch({
|
|
157
|
-
type: 'NO_DOM_NODE',
|
|
158
|
-
helperPosition: step.position as ReducerState['helperPosition'],
|
|
159
|
-
w,
|
|
160
|
-
h,
|
|
161
|
-
inDOM: false,
|
|
162
|
-
})
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function makeCalculation(
|
|
167
|
-
nodeRect: Partial<GetNodeRectOptions>,
|
|
168
|
-
helperPosition?: TourStepItem['position']
|
|
169
|
-
) {
|
|
170
|
-
const { w, h } = getWindow()
|
|
171
|
-
const { width: helperWidth, height: helperHeight } = getNodeRect(
|
|
172
|
-
helper.current as HTMLDivElement
|
|
173
|
-
)
|
|
174
|
-
dispatch({
|
|
175
|
-
type: 'HAS_DOM_NODE',
|
|
176
|
-
...nodeRect,
|
|
177
|
-
helperWidth,
|
|
178
|
-
helperHeight,
|
|
179
|
-
helperPosition: helperPosition as ReducerState['helperPosition'],
|
|
180
|
-
w,
|
|
181
|
-
h,
|
|
182
|
-
inDOM: true,
|
|
183
|
-
})
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function getNode(step: TourStepItem) {
|
|
187
|
-
return step.selector ? document.querySelector(step.selector) : null
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async function runCurrentActionAfter() {
|
|
191
|
-
const step = steps[current]
|
|
192
|
-
if (step.actionAfter && typeof step.actionAfter === 'function') {
|
|
193
|
-
await step.actionAfter(getNode(step))
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
async function close(event?: MouseEvent) {
|
|
198
|
-
document.body.style.overflowY = 'auto'
|
|
199
|
-
await runCurrentActionAfter()
|
|
200
|
-
if (onBeforeClose && typeof onBeforeClose === 'function') {
|
|
201
|
-
onBeforeClose(helper.current)
|
|
202
|
-
}
|
|
203
|
-
onRequestClose && onRequestClose(event)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const handleMaskClickHandler = (event: MouseEvent<HTMLDivElement>): void => {
|
|
207
|
-
if (closeWithMask) {
|
|
208
|
-
close(event)
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async function goTo(index: number) {
|
|
213
|
-
await runCurrentActionAfter()
|
|
214
|
-
setCurrent(index)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const handleNextStep = (): void => {
|
|
218
|
-
goTo(current < steps?.length - 1 ? current + 1 : current)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return isOpen ? (
|
|
222
|
-
<Portal>
|
|
223
|
-
<SvgMask
|
|
224
|
-
onClick={handleMaskClickHandler}
|
|
225
|
-
windowWidth={state.w}
|
|
226
|
-
windowHeight={state.h}
|
|
227
|
-
targetWidth={state.width}
|
|
228
|
-
targetHeight={state.height}
|
|
229
|
-
targetTop={state.top}
|
|
230
|
-
targetLeft={state.left}
|
|
231
|
-
padding={maskSpace}
|
|
232
|
-
rounded={rounded}
|
|
233
|
-
className={maskClassName}
|
|
234
|
-
disableInteraction={disableInteraction || steps[current].stepInteraction}
|
|
235
|
-
disableInteractionClassName={disableInteractionClassName}
|
|
236
|
-
/>
|
|
237
|
-
<Guide
|
|
238
|
-
ref={helper}
|
|
239
|
-
windowWidth={state.w}
|
|
240
|
-
windowHeight={state.h}
|
|
241
|
-
targetWidth={state.width}
|
|
242
|
-
targetHeight={state.height}
|
|
243
|
-
targetTop={state.top}
|
|
244
|
-
targetLeft={state.left}
|
|
245
|
-
targetRight={state.right}
|
|
246
|
-
targetBottom={state.bottom}
|
|
247
|
-
helperWidth={state.helperWidth!}
|
|
248
|
-
helperHeight={state.helperHeight!}
|
|
249
|
-
helperPosition={state.helperPosition}
|
|
250
|
-
padding={maskSpace}
|
|
251
|
-
tabIndex={-1}
|
|
252
|
-
current={current}
|
|
253
|
-
style={steps[current].style ?? {}}
|
|
254
|
-
rounded={rounded}
|
|
255
|
-
className={className}
|
|
256
|
-
>
|
|
257
|
-
<>
|
|
258
|
-
{children}
|
|
259
|
-
{steps[current].content}
|
|
260
|
-
{!disableButtons && (
|
|
261
|
-
<Action
|
|
262
|
-
isLastItem={current === steps?.length - 1}
|
|
263
|
-
onNextStep={handleNextStep}
|
|
264
|
-
onClose={(event) => close(event)}
|
|
265
|
-
/>
|
|
266
|
-
)}
|
|
267
|
-
</>
|
|
268
|
-
</Guide>
|
|
269
|
-
</Portal>
|
|
270
|
-
) : null
|
|
271
|
-
}
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
Tour.displayName = 'Tour'
|
|
275
|
-
|
|
276
|
-
export default memo(Tour)
|
|
1
|
+
import React, {
|
|
2
|
+
forwardRef,
|
|
3
|
+
memo,
|
|
4
|
+
MouseEvent,
|
|
5
|
+
useEffect,
|
|
6
|
+
useImperativeHandle,
|
|
7
|
+
useReducer,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
ReactText,
|
|
11
|
+
} from 'react'
|
|
12
|
+
import _ from 'lodash'
|
|
13
|
+
import { TourRefOptions, TourProps, TourStepItem } from './types'
|
|
14
|
+
import reducer, { initialState, ReducerState } from './reducer'
|
|
15
|
+
import { useMutationObserver } from './hook'
|
|
16
|
+
import { Portal } from './components'
|
|
17
|
+
import SvgMask from './components/svg_mask'
|
|
18
|
+
import Guide from './components/guide'
|
|
19
|
+
import Action from './components/action'
|
|
20
|
+
import {
|
|
21
|
+
getNodeRect,
|
|
22
|
+
GetNodeRectOptions,
|
|
23
|
+
getWindow,
|
|
24
|
+
inView,
|
|
25
|
+
isBody,
|
|
26
|
+
scrollParent,
|
|
27
|
+
scrollSmooth,
|
|
28
|
+
} from './utils'
|
|
29
|
+
|
|
30
|
+
const Tour = forwardRef<TourRefOptions, TourProps>(
|
|
31
|
+
(
|
|
32
|
+
{
|
|
33
|
+
children,
|
|
34
|
+
isOpen,
|
|
35
|
+
startAt = 0,
|
|
36
|
+
steps = [],
|
|
37
|
+
scrollDuration = 1,
|
|
38
|
+
className,
|
|
39
|
+
closeWithMask = false,
|
|
40
|
+
onRequestClose,
|
|
41
|
+
onAfterOpen,
|
|
42
|
+
onBeforeClose,
|
|
43
|
+
disableButtons = false,
|
|
44
|
+
disableInteraction = true,
|
|
45
|
+
disableInteractionClassName,
|
|
46
|
+
maskClassName,
|
|
47
|
+
rounded = 3,
|
|
48
|
+
maskSpace = 10,
|
|
49
|
+
},
|
|
50
|
+
ref
|
|
51
|
+
) => {
|
|
52
|
+
const [current, setCurrent] = useState(0)
|
|
53
|
+
const [started, setStarted] = useState(false)
|
|
54
|
+
const [state, dispatch] = useReducer(reducer, initialState)
|
|
55
|
+
const helper = useRef<HTMLDivElement>(null)
|
|
56
|
+
const observer = useRef<HTMLDivElement>()
|
|
57
|
+
|
|
58
|
+
useMutationObserver(observer, (mutations, observer) => {
|
|
59
|
+
if (isOpen) {
|
|
60
|
+
showStep()
|
|
61
|
+
mutations.forEach((mutation) => {
|
|
62
|
+
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
makeCalculation(getNodeRect(mutation.addedNodes[0] as HTMLElement))
|
|
65
|
+
}, 500)
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
} else {
|
|
69
|
+
observer.disconnect()
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
const debouncedShowStep = _.debounce(() => {
|
|
75
|
+
showStep()
|
|
76
|
+
}, 100)
|
|
77
|
+
window.addEventListener('resize', debouncedShowStep, false)
|
|
78
|
+
|
|
79
|
+
if (isOpen) {
|
|
80
|
+
if (!started) {
|
|
81
|
+
setStarted(true)
|
|
82
|
+
makeCalculation(
|
|
83
|
+
{
|
|
84
|
+
width: maskSpace * -1,
|
|
85
|
+
height: maskSpace * -1,
|
|
86
|
+
top: rounded * -1,
|
|
87
|
+
left: rounded * -1,
|
|
88
|
+
},
|
|
89
|
+
'center'
|
|
90
|
+
)
|
|
91
|
+
setCurrent(startAt)
|
|
92
|
+
showStep(startAt)
|
|
93
|
+
} else {
|
|
94
|
+
showStep()
|
|
95
|
+
}
|
|
96
|
+
if (helper.current) {
|
|
97
|
+
helper.current.focus()
|
|
98
|
+
document.body.style.overflowY = 'hidden'
|
|
99
|
+
if (onAfterOpen && typeof onAfterOpen === 'function') {
|
|
100
|
+
onAfterOpen(helper.current)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return () => {
|
|
106
|
+
window.removeEventListener('resize', debouncedShowStep)
|
|
107
|
+
}
|
|
108
|
+
}, [current, isOpen])
|
|
109
|
+
|
|
110
|
+
useImperativeHandle(ref, () => ({
|
|
111
|
+
apiToNextStep() {
|
|
112
|
+
handleNextStep()
|
|
113
|
+
},
|
|
114
|
+
apiClose() {
|
|
115
|
+
close()
|
|
116
|
+
},
|
|
117
|
+
apiRecalculate() {
|
|
118
|
+
calculatePosition()
|
|
119
|
+
},
|
|
120
|
+
}))
|
|
121
|
+
|
|
122
|
+
async function showStep(nextStep?: number) {
|
|
123
|
+
const step = steps[nextStep!] ?? steps[current]
|
|
124
|
+
if (step.actionBefore && typeof step.actionBefore === 'function') {
|
|
125
|
+
await step.actionBefore()
|
|
126
|
+
}
|
|
127
|
+
if (step.observe) {
|
|
128
|
+
observer.current = document.querySelector(step.observe) as HTMLDivElement
|
|
129
|
+
}
|
|
130
|
+
calculatePosition(nextStep)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function calculatePosition(nextStep?: number) {
|
|
134
|
+
const step = steps[nextStep!] ?? steps[current]
|
|
135
|
+
const node = getNode(step)
|
|
136
|
+
const { w, h } = getWindow()
|
|
137
|
+
if (node) {
|
|
138
|
+
const nodeRect = getNodeRect(node as HTMLElement)
|
|
139
|
+
if (!inView({ ...nodeRect, w, h })) {
|
|
140
|
+
const parentScroll = scrollParent(node)
|
|
141
|
+
const offset = nodeRect.height > h ? -25 : -(h / 2) + nodeRect.height / 2
|
|
142
|
+
scrollSmooth(node as HTMLElement, {
|
|
143
|
+
context: isBody(parentScroll as HTMLElement)
|
|
144
|
+
? window
|
|
145
|
+
: (parentScroll as HTMLElement),
|
|
146
|
+
duration: scrollDuration,
|
|
147
|
+
offset,
|
|
148
|
+
callback(target: ReactText | HTMLElement) {
|
|
149
|
+
makeCalculation(getNodeRect(target as HTMLElement), step.position)
|
|
150
|
+
},
|
|
151
|
+
})
|
|
152
|
+
} else {
|
|
153
|
+
makeCalculation(nodeRect, step.position)
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
dispatch({
|
|
157
|
+
type: 'NO_DOM_NODE',
|
|
158
|
+
helperPosition: step.position as ReducerState['helperPosition'],
|
|
159
|
+
w,
|
|
160
|
+
h,
|
|
161
|
+
inDOM: false,
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function makeCalculation(
|
|
167
|
+
nodeRect: Partial<GetNodeRectOptions>,
|
|
168
|
+
helperPosition?: TourStepItem['position']
|
|
169
|
+
) {
|
|
170
|
+
const { w, h } = getWindow()
|
|
171
|
+
const { width: helperWidth, height: helperHeight } = getNodeRect(
|
|
172
|
+
helper.current as HTMLDivElement
|
|
173
|
+
)
|
|
174
|
+
dispatch({
|
|
175
|
+
type: 'HAS_DOM_NODE',
|
|
176
|
+
...nodeRect,
|
|
177
|
+
helperWidth,
|
|
178
|
+
helperHeight,
|
|
179
|
+
helperPosition: helperPosition as ReducerState['helperPosition'],
|
|
180
|
+
w,
|
|
181
|
+
h,
|
|
182
|
+
inDOM: true,
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getNode(step: TourStepItem) {
|
|
187
|
+
return step.selector ? document.querySelector(step.selector) : null
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function runCurrentActionAfter() {
|
|
191
|
+
const step = steps[current]
|
|
192
|
+
if (step.actionAfter && typeof step.actionAfter === 'function') {
|
|
193
|
+
await step.actionAfter(getNode(step))
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function close(event?: MouseEvent) {
|
|
198
|
+
document.body.style.overflowY = 'auto'
|
|
199
|
+
await runCurrentActionAfter()
|
|
200
|
+
if (onBeforeClose && typeof onBeforeClose === 'function') {
|
|
201
|
+
onBeforeClose(helper.current)
|
|
202
|
+
}
|
|
203
|
+
onRequestClose && onRequestClose(event)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const handleMaskClickHandler = (event: MouseEvent<HTMLDivElement>): void => {
|
|
207
|
+
if (closeWithMask) {
|
|
208
|
+
close(event)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function goTo(index: number) {
|
|
213
|
+
await runCurrentActionAfter()
|
|
214
|
+
setCurrent(index)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const handleNextStep = (): void => {
|
|
218
|
+
goTo(current < steps?.length - 1 ? current + 1 : current)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return isOpen ? (
|
|
222
|
+
<Portal>
|
|
223
|
+
<SvgMask
|
|
224
|
+
onClick={handleMaskClickHandler}
|
|
225
|
+
windowWidth={state.w}
|
|
226
|
+
windowHeight={state.h}
|
|
227
|
+
targetWidth={state.width}
|
|
228
|
+
targetHeight={state.height}
|
|
229
|
+
targetTop={state.top}
|
|
230
|
+
targetLeft={state.left}
|
|
231
|
+
padding={maskSpace}
|
|
232
|
+
rounded={rounded}
|
|
233
|
+
className={maskClassName}
|
|
234
|
+
disableInteraction={disableInteraction || steps[current].stepInteraction}
|
|
235
|
+
disableInteractionClassName={disableInteractionClassName}
|
|
236
|
+
/>
|
|
237
|
+
<Guide
|
|
238
|
+
ref={helper}
|
|
239
|
+
windowWidth={state.w}
|
|
240
|
+
windowHeight={state.h}
|
|
241
|
+
targetWidth={state.width}
|
|
242
|
+
targetHeight={state.height}
|
|
243
|
+
targetTop={state.top}
|
|
244
|
+
targetLeft={state.left}
|
|
245
|
+
targetRight={state.right}
|
|
246
|
+
targetBottom={state.bottom}
|
|
247
|
+
helperWidth={state.helperWidth!}
|
|
248
|
+
helperHeight={state.helperHeight!}
|
|
249
|
+
helperPosition={state.helperPosition}
|
|
250
|
+
padding={maskSpace}
|
|
251
|
+
tabIndex={-1}
|
|
252
|
+
current={current}
|
|
253
|
+
style={steps[current].style ?? {}}
|
|
254
|
+
rounded={rounded}
|
|
255
|
+
className={className}
|
|
256
|
+
>
|
|
257
|
+
<>
|
|
258
|
+
{children}
|
|
259
|
+
{steps[current].content}
|
|
260
|
+
{!disableButtons && (
|
|
261
|
+
<Action
|
|
262
|
+
isLastItem={current === steps?.length - 1}
|
|
263
|
+
onNextStep={handleNextStep}
|
|
264
|
+
onClose={(event) => close(event)}
|
|
265
|
+
/>
|
|
266
|
+
)}
|
|
267
|
+
</>
|
|
268
|
+
</Guide>
|
|
269
|
+
</Portal>
|
|
270
|
+
) : null
|
|
271
|
+
}
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
Tour.displayName = 'Tour'
|
|
275
|
+
|
|
276
|
+
export default memo(Tour)
|
package/src/types.ts
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
import { CSSProperties, MouseEvent, ReactNode } from 'react'
|
|
2
|
-
|
|
3
|
-
interface TourStepItem {
|
|
4
|
-
selector?: string
|
|
5
|
-
content: ReactNode
|
|
6
|
-
observe?: string
|
|
7
|
-
position?: number[] | 'top' | 'right' | 'bottom' | 'left' | 'center'
|
|
8
|
-
actionAfter?(element: Element | null): void | Promise<void>
|
|
9
|
-
actionBefore?(): void | Promise<void>
|
|
10
|
-
style?: CSSProperties
|
|
11
|
-
stepInteraction?: boolean
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface TourProps {
|
|
15
|
-
className?: string
|
|
16
|
-
isOpen: boolean
|
|
17
|
-
|
|
18
|
-
/* 生命周期 */
|
|
19
|
-
/* 打开之后的回调 */
|
|
20
|
-
onAfterOpen?(dom: HTMLDivElement): void
|
|
21
|
-
/* 关闭之前的回调 */
|
|
22
|
-
onBeforeClose?(dom: HTMLDivElement | null): void
|
|
23
|
-
/* 请求之后关闭的回调 */
|
|
24
|
-
onRequestClose?(event?: MouseEvent): void
|
|
25
|
-
|
|
26
|
-
/* 延迟 */
|
|
27
|
-
scrollDuration?: number
|
|
28
|
-
/* 开始 */
|
|
29
|
-
startAt?: number
|
|
30
|
-
/* 引导与内容的间隙 */
|
|
31
|
-
maskSpace?: number
|
|
32
|
-
maskClassName?: string
|
|
33
|
-
/* 蒙层是否触发关闭 */
|
|
34
|
-
closeWithMask?: boolean
|
|
35
|
-
/* 步骤设置 */
|
|
36
|
-
steps?: TourStepItem[]
|
|
37
|
-
/* 禁用按钮 */
|
|
38
|
-
disableButtons?: boolean
|
|
39
|
-
/* 禁用互动 */
|
|
40
|
-
disableInteraction?: boolean
|
|
41
|
-
/* 禁用互动样式 */
|
|
42
|
-
disableInteractionClassName?: string
|
|
43
|
-
rounded?: number
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface TourRefOptions {
|
|
47
|
-
apiToNextStep(): void
|
|
48
|
-
apiClose(): void
|
|
49
|
-
apiRecalculate(): void
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export type { TourProps, TourStepItem, TourRefOptions }
|
|
1
|
+
import { CSSProperties, MouseEvent, ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
interface TourStepItem {
|
|
4
|
+
selector?: string
|
|
5
|
+
content: ReactNode
|
|
6
|
+
observe?: string
|
|
7
|
+
position?: number[] | 'top' | 'right' | 'bottom' | 'left' | 'center'
|
|
8
|
+
actionAfter?(element: Element | null): void | Promise<void>
|
|
9
|
+
actionBefore?(): void | Promise<void>
|
|
10
|
+
style?: CSSProperties
|
|
11
|
+
stepInteraction?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface TourProps {
|
|
15
|
+
className?: string
|
|
16
|
+
isOpen: boolean
|
|
17
|
+
|
|
18
|
+
/* 生命周期 */
|
|
19
|
+
/* 打开之后的回调 */
|
|
20
|
+
onAfterOpen?(dom: HTMLDivElement): void
|
|
21
|
+
/* 关闭之前的回调 */
|
|
22
|
+
onBeforeClose?(dom: HTMLDivElement | null): void
|
|
23
|
+
/* 请求之后关闭的回调 */
|
|
24
|
+
onRequestClose?(event?: MouseEvent): void
|
|
25
|
+
|
|
26
|
+
/* 延迟 */
|
|
27
|
+
scrollDuration?: number
|
|
28
|
+
/* 开始 */
|
|
29
|
+
startAt?: number
|
|
30
|
+
/* 引导与内容的间隙 */
|
|
31
|
+
maskSpace?: number
|
|
32
|
+
maskClassName?: string
|
|
33
|
+
/* 蒙层是否触发关闭 */
|
|
34
|
+
closeWithMask?: boolean
|
|
35
|
+
/* 步骤设置 */
|
|
36
|
+
steps?: TourStepItem[]
|
|
37
|
+
/* 禁用按钮 */
|
|
38
|
+
disableButtons?: boolean
|
|
39
|
+
/* 禁用互动 */
|
|
40
|
+
disableInteraction?: boolean
|
|
41
|
+
/* 禁用互动样式 */
|
|
42
|
+
disableInteractionClassName?: string
|
|
43
|
+
rounded?: number
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface TourRefOptions {
|
|
47
|
+
apiToNextStep(): void
|
|
48
|
+
apiClose(): void
|
|
49
|
+
apiRecalculate(): void
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type { TourProps, TourStepItem, TourRefOptions }
|