@rainbow-oh/yee-c 0.2.0 → 0.3.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/dist/cjs/Button/button.js +2 -2
- package/dist/cjs/Button/style/index.less +23 -8
- package/dist/cjs/Card/demo/collapsible.js +2 -1
- package/dist/cjs/Carousel/demo/arrows.js +1 -1
- package/dist/cjs/Carousel/demo/autoplay.js +1 -1
- package/dist/cjs/Carousel/demo/effect.js +1 -1
- package/dist/cjs/Carousel/demo/position.js +1 -1
- package/dist/cjs/Config-Provider/interface.d.ts +4 -0
- package/dist/cjs/Form/Field.js +32 -10
- package/dist/cjs/Form/interface.d.ts +17 -1
- package/dist/cjs/Menu/style/index.less +2 -2
- package/dist/cjs/Radio/demo/button.js +1 -0
- package/dist/cjs/Radio/interface.d.ts +4 -0
- package/dist/cjs/Radio/radio-group.js +28 -14
- package/dist/cjs/Radio/radio.js +3 -1
- package/dist/cjs/Radio/style/index.less +19 -5
- package/dist/cjs/Segmented/demo/basic.d.ts +3 -0
- package/dist/cjs/Segmented/demo/basic.js +51 -0
- package/dist/cjs/Segmented/demo/block.d.ts +3 -0
- package/dist/cjs/Segmented/demo/block.js +49 -0
- package/dist/cjs/Segmented/demo/disabled.d.ts +3 -0
- package/dist/cjs/Segmented/demo/disabled.js +47 -0
- package/dist/cjs/Segmented/demo/size.d.ts +3 -0
- package/dist/cjs/Segmented/demo/size.js +37 -0
- package/dist/cjs/Segmented/index.d.ts +3 -0
- package/dist/cjs/Segmented/index.js +36 -0
- package/dist/cjs/Segmented/interface.d.ts +82 -0
- package/dist/cjs/Segmented/interface.js +17 -0
- package/dist/cjs/Segmented/segmented.d.ts +5 -0
- package/dist/cjs/Segmented/segmented.js +177 -0
- package/dist/cjs/Segmented/style/index.less +104 -0
- package/dist/cjs/Spin/spin.js +2 -1
- package/dist/cjs/Steps/step.js +1 -1
- package/dist/cjs/Steps/style/index.less +8 -2
- package/dist/cjs/Switch/demo/content.js +22 -1
- package/dist/cjs/Switch/style/index.less +16 -0
- package/dist/cjs/Switch/switch.js +9 -2
- package/dist/cjs/Table/interface.d.ts +5 -1
- package/dist/cjs/Table/theader/cell.js +4 -5
- package/dist/cjs/Tag/demo/size.js +1 -1
- package/dist/cjs/Tag/style/index.less +1 -0
- package/dist/cjs/Tour/demo/basic.d.ts +3 -0
- package/dist/cjs/Tour/demo/basic.js +69 -0
- package/dist/cjs/Tour/demo/indicators.d.ts +3 -0
- package/dist/cjs/Tour/demo/indicators.js +76 -0
- package/dist/cjs/Tour/demo/placement.d.ts +3 -0
- package/dist/cjs/Tour/demo/placement.js +80 -0
- package/dist/cjs/Tour/demo/scroll.d.ts +3 -0
- package/dist/cjs/Tour/demo/scroll.js +72 -0
- package/dist/cjs/Tour/index.d.ts +3 -0
- package/dist/cjs/Tour/index.js +36 -0
- package/dist/cjs/Tour/interface.d.ts +118 -0
- package/dist/cjs/Tour/interface.js +17 -0
- package/dist/cjs/Tour/style/index.less +103 -0
- package/dist/cjs/Tour/tour.d.ts +5 -0
- package/dist/cjs/Tour/tour.js +403 -0
- package/dist/cjs/Tree/demo/checkable.js +6 -6
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.js +6 -0
- package/dist/cjs/locale/interface.d.ts +7 -0
- package/dist/cjs/locale/lang/en_US.js +6 -0
- package/dist/cjs/locale/lang/ja_JP.js +6 -0
- package/dist/cjs/locale/lang/zh_CN.js +6 -0
- package/dist/cjs/locale/lang/zh_TW.js +6 -0
- package/dist/cjs/style/color.blue.css +1 -1
- package/dist/cjs/style/color.crimson.css +1 -1
- package/dist/cjs/style/color.css +18 -1
- package/dist/cjs/style/size.css +38 -0
- package/dist/cjs/style/themes.css +3 -3
- package/dist/cjs/style/variables.css +57 -2
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/Button/button.js +2 -2
- package/dist/esm/Button/style/index.less +23 -8
- package/dist/esm/Card/demo/collapsible.js +2 -1
- package/dist/esm/Carousel/demo/arrows.js +1 -1
- package/dist/esm/Carousel/demo/autoplay.js +1 -1
- package/dist/esm/Carousel/demo/effect.js +1 -1
- package/dist/esm/Carousel/demo/position.js +1 -1
- package/dist/esm/Config-Provider/interface.d.ts +4 -0
- package/dist/esm/Form/Field.js +11 -4
- package/dist/esm/Form/interface.d.ts +17 -1
- package/dist/esm/Menu/style/index.less +2 -2
- package/dist/esm/Radio/demo/button.js +1 -0
- package/dist/esm/Radio/interface.d.ts +4 -0
- package/dist/esm/Radio/radio-group.js +9 -2
- package/dist/esm/Radio/radio.js +3 -1
- package/dist/esm/Radio/style/index.less +19 -5
- package/dist/esm/Segmented/demo/basic.d.ts +3 -0
- package/dist/esm/Segmented/demo/basic.js +33 -0
- package/dist/esm/Segmented/demo/block.d.ts +3 -0
- package/dist/esm/Segmented/demo/block.js +29 -0
- package/dist/esm/Segmented/demo/disabled.d.ts +3 -0
- package/dist/esm/Segmented/demo/disabled.js +22 -0
- package/dist/esm/Segmented/demo/size.d.ts +3 -0
- package/dist/esm/Segmented/demo/size.js +15 -0
- package/dist/esm/Segmented/index.d.ts +3 -0
- package/dist/esm/Segmented/index.js +2 -0
- package/dist/esm/Segmented/interface.d.ts +82 -0
- package/dist/esm/Segmented/interface.js +1 -0
- package/dist/esm/Segmented/segmented.d.ts +5 -0
- package/dist/esm/Segmented/segmented.js +110 -0
- package/dist/esm/Segmented/style/index.less +104 -0
- package/dist/esm/Spin/spin.js +2 -1
- package/dist/esm/Steps/step.js +4 -1
- package/dist/esm/Steps/style/index.less +8 -2
- package/dist/esm/Switch/demo/content.js +12 -3
- package/dist/esm/Switch/style/index.less +16 -0
- package/dist/esm/Switch/switch.js +7 -5
- package/dist/esm/Table/interface.d.ts +5 -1
- package/dist/esm/Table/theader/cell.js +2 -3
- package/dist/esm/Tag/demo/size.js +6 -2
- package/dist/esm/Tag/style/index.less +1 -0
- package/dist/esm/Tour/demo/basic.d.ts +3 -0
- package/dist/esm/Tour/demo/basic.js +69 -0
- package/dist/esm/Tour/demo/indicators.d.ts +3 -0
- package/dist/esm/Tour/demo/indicators.js +73 -0
- package/dist/esm/Tour/demo/placement.d.ts +3 -0
- package/dist/esm/Tour/demo/placement.js +53 -0
- package/dist/esm/Tour/demo/scroll.d.ts +3 -0
- package/dist/esm/Tour/demo/scroll.js +58 -0
- package/dist/esm/Tour/index.d.ts +3 -0
- package/dist/esm/Tour/index.js +2 -0
- package/dist/esm/Tour/interface.d.ts +118 -0
- package/dist/esm/Tour/interface.js +1 -0
- package/dist/esm/Tour/style/index.less +103 -0
- package/dist/esm/Tour/tour.d.ts +5 -0
- package/dist/esm/Tour/tour.js +383 -0
- package/dist/esm/Tree/demo/checkable.js +6 -6
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/locale/interface.d.ts +7 -0
- package/dist/esm/locale/lang/en_US.js +6 -0
- package/dist/esm/locale/lang/ja_JP.js +6 -0
- package/dist/esm/locale/lang/zh_CN.js +6 -0
- package/dist/esm/locale/lang/zh_TW.js +6 -0
- package/dist/esm/style/color.blue.css +1 -1
- package/dist/esm/style/color.crimson.css +1 -1
- package/dist/esm/style/color.css +18 -1
- package/dist/esm/style/size.css +38 -0
- package/dist/esm/style/themes.css +3 -3
- package/dist/esm/style/variables.css +57 -2
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/umd/yee-c.min.css +3 -3
- package/dist/umd/yee-c.min.js +1 -1
- package/package.json +1 -1
- package/src/Button/button.tsx +2 -2
- package/src/Button/style/index.less +23 -8
- package/src/Card/demo/collapsible.tsx +1 -0
- package/src/Carousel/demo/arrows.tsx +1 -1
- package/src/Carousel/demo/autoplay.tsx +1 -1
- package/src/Carousel/demo/effect.tsx +1 -1
- package/src/Carousel/demo/position.tsx +1 -1
- package/src/Config-Provider/index.en-US.md +2 -0
- package/src/Config-Provider/index.zh-CN.md +2 -0
- package/src/Config-Provider/interface.ts +4 -0
- package/src/Form/Field.tsx +30 -11
- package/src/Form/index.en-US.md +4 -0
- package/src/Form/index.zh-CN.md +4 -0
- package/src/Form/interface.ts +17 -1
- package/src/Menu/style/index.less +2 -2
- package/src/Radio/demo/button.tsx +1 -0
- package/src/Radio/index.en-US.md +2 -0
- package/src/Radio/index.zh-CN.md +2 -0
- package/src/Radio/interface.ts +4 -0
- package/src/Radio/radio-group.tsx +9 -1
- package/src/Radio/radio.tsx +2 -1
- package/src/Radio/style/index.less +19 -5
- package/src/Segmented/demo/basic.tsx +24 -0
- package/src/Segmented/demo/block.tsx +17 -0
- package/src/Segmented/demo/disabled.tsx +18 -0
- package/src/Segmented/demo/size.tsx +10 -0
- package/src/Segmented/index.en-US.md +47 -0
- package/src/Segmented/index.ts +12 -0
- package/src/Segmented/index.zh-CN.md +47 -0
- package/src/Segmented/interface.ts +87 -0
- package/src/Segmented/segmented.tsx +134 -0
- package/src/Segmented/style/index.less +104 -0
- package/src/Spin/spin.tsx +1 -0
- package/src/Steps/step.tsx +1 -1
- package/src/Steps/style/index.less +8 -2
- package/src/Switch/demo/content.tsx +15 -5
- package/src/Switch/style/index.less +16 -0
- package/src/Switch/switch.tsx +14 -6
- package/src/Table/index.en-US.md +1 -0
- package/src/Table/index.zh-CN.md +1 -0
- package/src/Table/interface.ts +5 -1
- package/src/Table/theader/cell.tsx +2 -9
- package/src/Tag/demo/size.tsx +2 -2
- package/src/Tag/style/index.less +1 -0
- package/src/Tour/demo/basic.tsx +52 -0
- package/src/Tour/demo/indicators.tsx +63 -0
- package/src/Tour/demo/placement.tsx +55 -0
- package/src/Tour/demo/scroll.tsx +49 -0
- package/src/Tour/index.en-US.md +61 -0
- package/src/Tour/index.ts +10 -0
- package/src/Tour/index.zh-CN.md +61 -0
- package/src/Tour/interface.ts +126 -0
- package/src/Tour/style/index.less +103 -0
- package/src/Tour/tour.tsx +399 -0
- package/src/Tree/demo/checkable.tsx +6 -6
- package/src/index.ts +6 -0
- package/src/locale/interface.ts +9 -0
- package/src/locale/lang/en_US.ts +7 -0
- package/src/locale/lang/ja_JP.ts +7 -0
- package/src/locale/lang/zh_CN.ts +7 -0
- package/src/locale/lang/zh_TW.ts +7 -0
- package/src/style/color.blue.css +1 -1
- package/src/style/color.crimson.css +1 -1
- package/src/style/color.css +18 -1
- package/src/style/size.css +38 -0
- package/src/version.ts +1 -1
- package/dist/cjs/utils/x.d.ts +0 -11
- package/dist/cjs/utils/x.js +0 -51
- package/dist/esm/utils/x.d.ts +0 -11
- package/dist/esm/utils/x.js +0 -49
- package/src/utils/x.ts +0 -33
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import clsx from 'clsx';
|
|
2
|
+
import { X } from 'lucide-react';
|
|
3
|
+
import { motion } from 'motion/react';
|
|
4
|
+
import React, {
|
|
5
|
+
useCallback,
|
|
6
|
+
useContext,
|
|
7
|
+
useLayoutEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useRef,
|
|
10
|
+
useState,
|
|
11
|
+
} from 'react';
|
|
12
|
+
import { createPortal } from 'react-dom';
|
|
13
|
+
import Button from '../Button';
|
|
14
|
+
import { GlobalContext } from '../Config-Provider';
|
|
15
|
+
import type { Placement } from '../Trigger/interface';
|
|
16
|
+
import useEsc from '../hooks/useEsc';
|
|
17
|
+
import useLatest from '../hooks/useLatest';
|
|
18
|
+
import useMergedState from '../hooks/useMergedState';
|
|
19
|
+
import { useLocale } from '../locale';
|
|
20
|
+
import mergeContextToProps from '../utils/mergeContextToProps';
|
|
21
|
+
import type { TourProps, TourTarget } from './interface';
|
|
22
|
+
|
|
23
|
+
import './style/index.less';
|
|
24
|
+
|
|
25
|
+
const GAP = 12;
|
|
26
|
+
|
|
27
|
+
const resolveTarget = (target: TourTarget | undefined): HTMLElement | null => {
|
|
28
|
+
if (!target) return null;
|
|
29
|
+
if (typeof target === 'function') {
|
|
30
|
+
try {
|
|
31
|
+
return target();
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (typeof target === 'string') {
|
|
37
|
+
if (typeof document === 'undefined') return null;
|
|
38
|
+
return document.querySelector<HTMLElement>(target);
|
|
39
|
+
}
|
|
40
|
+
return target;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type TourSide = 'top' | 'bottom' | 'left' | 'right';
|
|
44
|
+
type TourAlign = 'start' | 'center' | 'end';
|
|
45
|
+
|
|
46
|
+
const SIDE_PREFIXES: readonly TourSide[] = ['top', 'bottom', 'left', 'right'];
|
|
47
|
+
|
|
48
|
+
// Split a 12-way Placement into a base side + cross-axis alignment.
|
|
49
|
+
// 'bottom' -> { side: 'bottom', align: 'center' }
|
|
50
|
+
// 'topLeft' -> { side: 'top', align: 'start' }
|
|
51
|
+
// 'leftBottom' -> { side: 'left', align: 'end' }
|
|
52
|
+
const parsePlacement = (
|
|
53
|
+
p: Placement | undefined,
|
|
54
|
+
): { side: TourSide; align: TourAlign } => {
|
|
55
|
+
if (!p) return { side: 'bottom', align: 'center' };
|
|
56
|
+
const side = SIDE_PREFIXES.find((s) => p.startsWith(s)) ?? 'bottom';
|
|
57
|
+
const rest = p.slice(side.length);
|
|
58
|
+
let align: TourAlign = 'center';
|
|
59
|
+
if (side === 'top' || side === 'bottom') {
|
|
60
|
+
if (rest === 'Left') align = 'start';
|
|
61
|
+
else if (rest === 'Right') align = 'end';
|
|
62
|
+
} else if (rest === 'Top') {
|
|
63
|
+
align = 'start';
|
|
64
|
+
} else if (rest === 'Bottom') {
|
|
65
|
+
align = 'end';
|
|
66
|
+
}
|
|
67
|
+
return { side, align };
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const OPPOSITE_SIDE: Record<TourSide, TourSide> = {
|
|
71
|
+
top: 'bottom',
|
|
72
|
+
bottom: 'top',
|
|
73
|
+
left: 'right',
|
|
74
|
+
right: 'left',
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Compute the card's fixed position from the highlight rect + placement, with
|
|
78
|
+
// viewport flip + clamp. Returning rect's origin while size is unknown lets the
|
|
79
|
+
// caller keep the card hidden until measured.
|
|
80
|
+
const computeCardPos = (
|
|
81
|
+
rect: DOMRect,
|
|
82
|
+
placement: Placement | undefined,
|
|
83
|
+
size: { width: number; height: number },
|
|
84
|
+
gap: number,
|
|
85
|
+
): { top: number; left: number } => {
|
|
86
|
+
const { width: cw, height: ch } = size;
|
|
87
|
+
const { top, left, width, height } = rect;
|
|
88
|
+
|
|
89
|
+
if (cw === 0 || ch === 0) {
|
|
90
|
+
return { top, left };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const vw = window.innerWidth;
|
|
94
|
+
const vh = window.innerHeight;
|
|
95
|
+
|
|
96
|
+
const { side: preferredSide, align } = parsePlacement(placement);
|
|
97
|
+
|
|
98
|
+
// Lay the card out for a given side; align shifts it along the cross axis.
|
|
99
|
+
const layoutFor = (side: TourSide): { top: number; left: number } => {
|
|
100
|
+
if (side === 'top' || side === 'bottom') {
|
|
101
|
+
const main = side === 'top' ? top - ch - gap : top + height + gap;
|
|
102
|
+
let cross: number;
|
|
103
|
+
if (align === 'start') cross = left;
|
|
104
|
+
else if (align === 'end') cross = left + width - cw;
|
|
105
|
+
else cross = left + width / 2 - cw / 2;
|
|
106
|
+
return { top: main, left: cross };
|
|
107
|
+
}
|
|
108
|
+
const main = side === 'left' ? left - cw - gap : left + width + gap;
|
|
109
|
+
let cross: number;
|
|
110
|
+
if (align === 'start') cross = top;
|
|
111
|
+
else if (align === 'end') cross = top + height - ch;
|
|
112
|
+
else cross = top + height / 2 - ch / 2;
|
|
113
|
+
return { top: cross, left: main };
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// True when the card fits on the main axis for this side.
|
|
117
|
+
const fits = (
|
|
118
|
+
side: TourSide,
|
|
119
|
+
pos: { top: number; left: number },
|
|
120
|
+
): boolean => {
|
|
121
|
+
if (side === 'top') return pos.top >= gap;
|
|
122
|
+
if (side === 'bottom') return pos.top + ch <= vh - gap;
|
|
123
|
+
if (side === 'left') return pos.left >= gap;
|
|
124
|
+
return pos.left + cw <= vw - gap;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
let side = preferredSide;
|
|
128
|
+
let pos = layoutFor(side);
|
|
129
|
+
|
|
130
|
+
// Flip to the opposite side when the preferred one overflows and the
|
|
131
|
+
// opposite fits. Alignment is preserved across the flip.
|
|
132
|
+
const flippedSide = OPPOSITE_SIDE[side];
|
|
133
|
+
const flipped = layoutFor(flippedSide);
|
|
134
|
+
if (!fits(side, pos) && fits(flippedSide, flipped)) {
|
|
135
|
+
side = flippedSide;
|
|
136
|
+
pos = flipped;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Clamp into the viewport (mostly fixes cross-axis overflow).
|
|
140
|
+
return {
|
|
141
|
+
top: Math.max(gap, Math.min(pos.top, vh - ch - gap)),
|
|
142
|
+
left: Math.max(gap, Math.min(pos.left, vw - cw - gap)),
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const Tour: React.FC<TourProps> = (baseprops) => {
|
|
147
|
+
const { tour: tourCtx } = useContext(GlobalContext);
|
|
148
|
+
const props = mergeContextToProps<TourProps>(baseprops, tourCtx);
|
|
149
|
+
const { locale } = useLocale();
|
|
150
|
+
const tourLocale = locale?.tour ?? {
|
|
151
|
+
previous: 'Previous',
|
|
152
|
+
next: 'Next',
|
|
153
|
+
finish: 'Finish',
|
|
154
|
+
skip: 'Skip',
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const {
|
|
158
|
+
prefixCls = 'yee-tour',
|
|
159
|
+
className,
|
|
160
|
+
style,
|
|
161
|
+
classNames,
|
|
162
|
+
styles,
|
|
163
|
+
open,
|
|
164
|
+
steps = [],
|
|
165
|
+
current,
|
|
166
|
+
defaultCurrent = 0,
|
|
167
|
+
onClose,
|
|
168
|
+
onFinish,
|
|
169
|
+
onChange,
|
|
170
|
+
placement = 'bottom',
|
|
171
|
+
mask = true,
|
|
172
|
+
closable = true,
|
|
173
|
+
closeIcon,
|
|
174
|
+
maskClosable = false,
|
|
175
|
+
indicatorsRender,
|
|
176
|
+
scrollIntoViewOptions = true,
|
|
177
|
+
...rest
|
|
178
|
+
} = props;
|
|
179
|
+
|
|
180
|
+
const [mergedCurrent, setMergedCurrent] = useMergedState<number>(
|
|
181
|
+
defaultCurrent,
|
|
182
|
+
{
|
|
183
|
+
value: current,
|
|
184
|
+
defaultValue: defaultCurrent,
|
|
185
|
+
},
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
const step = steps[mergedCurrent];
|
|
189
|
+
const total = steps.length;
|
|
190
|
+
const isLast = mergedCurrent >= total - 1;
|
|
191
|
+
const stepPlacement = step?.placement ?? placement;
|
|
192
|
+
|
|
193
|
+
const targetEl = useMemo<HTMLElement | null>(
|
|
194
|
+
() => resolveTarget(step?.target),
|
|
195
|
+
[step, open],
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const [rect, setRect] = useState<DOMRect | null>(null);
|
|
199
|
+
const rafRef = useRef<number | null>(null);
|
|
200
|
+
|
|
201
|
+
// Keep the latest scrollIntoViewOptions in a ref so the tracking effect below
|
|
202
|
+
// doesn't re-run (and re-scroll) when the caller passes an inline object on
|
|
203
|
+
// every render — that would loop with rAF → setRect → re-render forever.
|
|
204
|
+
const scrollIntoViewOptionsRef = useLatest(scrollIntoViewOptions);
|
|
205
|
+
|
|
206
|
+
// Track target rect: recompute on open / step / scroll / resize (rAF throttled).
|
|
207
|
+
useLayoutEffect(() => {
|
|
208
|
+
if (!open || !targetEl) {
|
|
209
|
+
setRect(null);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const update = () => {
|
|
213
|
+
if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
|
|
214
|
+
rafRef.current = requestAnimationFrame(() => {
|
|
215
|
+
setRect(targetEl.getBoundingClientRect() as DOMRect);
|
|
216
|
+
});
|
|
217
|
+
};
|
|
218
|
+
update();
|
|
219
|
+
// Bring the target into view, but only when it isn't already — the default
|
|
220
|
+
// (`true` ≈ `{ block: 'start' }`) would otherwise yank an already-visible
|
|
221
|
+
// element to the top of the viewport on every step change. A custom options
|
|
222
|
+
// object is honored as-is (its own `nearest` alignment handles the visible
|
|
223
|
+
// case), and `false` disables auto-scroll entirely.
|
|
224
|
+
const scrollTargetIfNeeded = () => {
|
|
225
|
+
const opts = scrollIntoViewOptionsRef.current;
|
|
226
|
+
if (opts === false) return;
|
|
227
|
+
if (opts === true) {
|
|
228
|
+
const vr = targetEl.getBoundingClientRect();
|
|
229
|
+
const inViewport =
|
|
230
|
+
vr.top >= 0 &&
|
|
231
|
+
vr.left >= 0 &&
|
|
232
|
+
vr.bottom <= window.innerHeight &&
|
|
233
|
+
vr.right <= window.innerWidth;
|
|
234
|
+
if (inViewport) return;
|
|
235
|
+
}
|
|
236
|
+
try {
|
|
237
|
+
targetEl.scrollIntoView?.(opts as ScrollIntoViewOptions);
|
|
238
|
+
} catch {
|
|
239
|
+
// noop
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
scrollTargetIfNeeded();
|
|
243
|
+
window.addEventListener('resize', update);
|
|
244
|
+
window.addEventListener('scroll', update, true);
|
|
245
|
+
return () => {
|
|
246
|
+
if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
|
|
247
|
+
window.removeEventListener('resize', update);
|
|
248
|
+
window.removeEventListener('scroll', update, true);
|
|
249
|
+
};
|
|
250
|
+
}, [open, targetEl, mergedCurrent]);
|
|
251
|
+
|
|
252
|
+
useEsc({ enabled: open, onEsc: () => onClose?.() });
|
|
253
|
+
|
|
254
|
+
const handleNext = useCallback(() => {
|
|
255
|
+
if (isLast) {
|
|
256
|
+
onFinish?.();
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const nextCur = mergedCurrent + 1;
|
|
260
|
+
setMergedCurrent(nextCur);
|
|
261
|
+
onChange?.(nextCur);
|
|
262
|
+
}, [isLast, mergedCurrent, onChange, onFinish, setMergedCurrent]);
|
|
263
|
+
|
|
264
|
+
const handlePrev = useCallback(() => {
|
|
265
|
+
if (mergedCurrent <= 0) return;
|
|
266
|
+
const prevCur = mergedCurrent - 1;
|
|
267
|
+
setMergedCurrent(prevCur);
|
|
268
|
+
onChange?.(prevCur);
|
|
269
|
+
}, [mergedCurrent, onChange, setMergedCurrent]);
|
|
270
|
+
|
|
271
|
+
// Measure the card so we can position it against the highlight rect.
|
|
272
|
+
const cardRef = useRef<HTMLDivElement>(null);
|
|
273
|
+
const [cardSize, setCardSize] = useState({ width: 0, height: 0 });
|
|
274
|
+
useLayoutEffect(() => {
|
|
275
|
+
if (!open) return;
|
|
276
|
+
const node = cardRef.current;
|
|
277
|
+
if (!node) return;
|
|
278
|
+
const r = node.getBoundingClientRect();
|
|
279
|
+
setCardSize({ width: r.width, height: r.height });
|
|
280
|
+
}, [open, step]);
|
|
281
|
+
|
|
282
|
+
const cardPos = useMemo(() => {
|
|
283
|
+
if (!cardSize.width || !cardSize.height) return null;
|
|
284
|
+
if (rect) return computeCardPos(rect, stepPlacement, cardSize, GAP);
|
|
285
|
+
// No target: center the card in the viewport.
|
|
286
|
+
return {
|
|
287
|
+
top: window.innerHeight / 2 - cardSize.height / 2,
|
|
288
|
+
left: window.innerWidth / 2 - cardSize.width / 2,
|
|
289
|
+
};
|
|
290
|
+
}, [rect, stepPlacement, cardSize]);
|
|
291
|
+
|
|
292
|
+
if (!open || typeof document === 'undefined' || !step) {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return createPortal(
|
|
297
|
+
<div className={clsx(prefixCls, className)} style={style} {...rest}>
|
|
298
|
+
{/* 1 click-intercept mask (box-shadow itself receives no pointer events).
|
|
299
|
+
When there's no target to cut out, the mask dims the full screen. */}
|
|
300
|
+
{mask ? (
|
|
301
|
+
<div
|
|
302
|
+
className={clsx(
|
|
303
|
+
`${prefixCls}-mask`,
|
|
304
|
+
!rect && `${prefixCls}-mask-full`,
|
|
305
|
+
)}
|
|
306
|
+
onClick={maskClosable ? onClose : undefined}
|
|
307
|
+
/>
|
|
308
|
+
) : null}
|
|
309
|
+
|
|
310
|
+
{/* 2 highlight cutout via box-shadow, animated between steps */}
|
|
311
|
+
{mask && rect ? (
|
|
312
|
+
<motion.div
|
|
313
|
+
className={clsx(`${prefixCls}-highlight`, classNames?.highlight)}
|
|
314
|
+
initial={false}
|
|
315
|
+
animate={{
|
|
316
|
+
top: rect.top,
|
|
317
|
+
left: rect.left,
|
|
318
|
+
width: rect.width,
|
|
319
|
+
height: rect.height,
|
|
320
|
+
}}
|
|
321
|
+
transition={{ type: 'spring', visualDuration: 0.3, bounce: 0.2 }}
|
|
322
|
+
style={styles?.highlight}
|
|
323
|
+
/>
|
|
324
|
+
) : null}
|
|
325
|
+
|
|
326
|
+
{/* 3 card: position computed from the highlight rect + placement */}
|
|
327
|
+
<motion.div
|
|
328
|
+
ref={cardRef}
|
|
329
|
+
className={clsx(
|
|
330
|
+
`${prefixCls}-content`,
|
|
331
|
+
classNames?.content,
|
|
332
|
+
step?.className,
|
|
333
|
+
)}
|
|
334
|
+
initial={false}
|
|
335
|
+
animate={
|
|
336
|
+
cardPos
|
|
337
|
+
? { top: cardPos.top, left: cardPos.left }
|
|
338
|
+
: { top: 0, left: 0 }
|
|
339
|
+
}
|
|
340
|
+
transition={{ type: 'spring', visualDuration: 0.3, bounce: 0.2 }}
|
|
341
|
+
style={{
|
|
342
|
+
position: 'fixed',
|
|
343
|
+
visibility: cardPos ? 'visible' : 'hidden',
|
|
344
|
+
...(styles?.content as React.CSSProperties),
|
|
345
|
+
}}
|
|
346
|
+
>
|
|
347
|
+
{closable ? (
|
|
348
|
+
<Button
|
|
349
|
+
type="text"
|
|
350
|
+
className={`${prefixCls}-close`}
|
|
351
|
+
onClick={onClose}
|
|
352
|
+
size="small"
|
|
353
|
+
icon={closeIcon ?? <X size={16} />}
|
|
354
|
+
aria-label={tourLocale.skip}
|
|
355
|
+
/>
|
|
356
|
+
) : null}
|
|
357
|
+
{step?.title ? (
|
|
358
|
+
<div
|
|
359
|
+
className={clsx(`${prefixCls}-header`, classNames?.header)}
|
|
360
|
+
style={styles?.header}
|
|
361
|
+
>
|
|
362
|
+
{step.title}
|
|
363
|
+
</div>
|
|
364
|
+
) : null}
|
|
365
|
+
<div
|
|
366
|
+
className={clsx(`${prefixCls}-body`, classNames?.body)}
|
|
367
|
+
style={styles?.body}
|
|
368
|
+
>
|
|
369
|
+
{step?.description}
|
|
370
|
+
</div>
|
|
371
|
+
<div
|
|
372
|
+
className={clsx(`${prefixCls}-footer`, classNames?.footer)}
|
|
373
|
+
style={styles?.footer}
|
|
374
|
+
>
|
|
375
|
+
<span className={`${prefixCls}-indicators`}>
|
|
376
|
+
{indicatorsRender
|
|
377
|
+
? indicatorsRender(mergedCurrent, total)
|
|
378
|
+
: `${mergedCurrent + 1} / ${total}`}
|
|
379
|
+
</span>
|
|
380
|
+
<span className={`${prefixCls}-buttons`}>
|
|
381
|
+
{mergedCurrent > 0 ? (
|
|
382
|
+
<Button size="small" onClick={handlePrev}>
|
|
383
|
+
{tourLocale.previous}
|
|
384
|
+
</Button>
|
|
385
|
+
) : null}
|
|
386
|
+
<Button type="primary" size="small" onClick={handleNext}>
|
|
387
|
+
{isLast ? tourLocale.finish : tourLocale.next}
|
|
388
|
+
</Button>
|
|
389
|
+
</span>
|
|
390
|
+
</div>
|
|
391
|
+
</motion.div>
|
|
392
|
+
</div>,
|
|
393
|
+
document.body,
|
|
394
|
+
);
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
Tour.displayName = 'Tour';
|
|
398
|
+
|
|
399
|
+
export default Tour;
|
|
@@ -7,29 +7,29 @@ export default () => {
|
|
|
7
7
|
const treeData = [
|
|
8
8
|
{
|
|
9
9
|
label: 'parent 1',
|
|
10
|
-
key:
|
|
10
|
+
key: 1,
|
|
11
11
|
children: [
|
|
12
12
|
{
|
|
13
13
|
label: 'parent 1-0',
|
|
14
|
-
key:
|
|
14
|
+
key: 10,
|
|
15
15
|
children: [
|
|
16
16
|
{
|
|
17
17
|
label: 'leaf',
|
|
18
|
-
key:
|
|
18
|
+
key: 100,
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
label: 'leaf',
|
|
22
|
-
key:
|
|
22
|
+
key: 101,
|
|
23
23
|
},
|
|
24
24
|
],
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
label: 'parent 1-1',
|
|
28
|
-
key:
|
|
28
|
+
key: 20,
|
|
29
29
|
children: [
|
|
30
30
|
{
|
|
31
31
|
label: 'leaf',
|
|
32
|
-
key:
|
|
32
|
+
key: 201,
|
|
33
33
|
},
|
|
34
34
|
],
|
|
35
35
|
},
|
package/src/index.ts
CHANGED
|
@@ -53,6 +53,9 @@ export * from './Radio';
|
|
|
53
53
|
export { default as Radio } from './Radio';
|
|
54
54
|
export * from './Select';
|
|
55
55
|
export { default as Select } from './Select';
|
|
56
|
+
|
|
57
|
+
export * from './Segmented';
|
|
58
|
+
export { default as Segmented } from './Segmented';
|
|
56
59
|
export * from './Skeleton';
|
|
57
60
|
export { default as Skeleton } from './Skeleton';
|
|
58
61
|
export * from './Space';
|
|
@@ -99,6 +102,9 @@ export { default as Steps } from './Steps';
|
|
|
99
102
|
export * from './Tooltip';
|
|
100
103
|
export { default as Tooltip } from './Tooltip';
|
|
101
104
|
|
|
105
|
+
export * from './Tour';
|
|
106
|
+
export { default as Tour } from './Tour';
|
|
107
|
+
|
|
102
108
|
export * from './Table';
|
|
103
109
|
export { default as Table } from './Table';
|
|
104
110
|
|
package/src/locale/interface.ts
CHANGED
|
@@ -109,6 +109,14 @@ export interface LocaleForm {
|
|
|
109
109
|
defaultValidateMessage?: string; // "Invalid format"
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
// Tour component
|
|
113
|
+
export interface LocaleTour {
|
|
114
|
+
previous: string; // "Previous"
|
|
115
|
+
next: string; // "Next"
|
|
116
|
+
finish: string; // "Finish"
|
|
117
|
+
skip: string; // "Skip"
|
|
118
|
+
}
|
|
119
|
+
|
|
112
120
|
export interface Locale {
|
|
113
121
|
locale: string;
|
|
114
122
|
global: LocaleGlobal;
|
|
@@ -125,6 +133,7 @@ export interface Locale {
|
|
|
125
133
|
popconfirm: LocalePopconfirm;
|
|
126
134
|
qrcode: LocaleQRCode;
|
|
127
135
|
form: LocaleForm;
|
|
136
|
+
tour: LocaleTour;
|
|
128
137
|
}
|
|
129
138
|
|
|
130
139
|
// ============ Translation key type inference ============
|
package/src/locale/lang/en_US.ts
CHANGED
package/src/locale/lang/ja_JP.ts
CHANGED
package/src/locale/lang/zh_CN.ts
CHANGED
package/src/locale/lang/zh_TW.ts
CHANGED
package/src/style/color.blue.css
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
--yee-text-color-description: #7c7c7c;
|
|
31
31
|
--yee-text-color-disabled: #bfbfbf;
|
|
32
32
|
--yee-text-color-highlight: #f58220;
|
|
33
|
-
--yee-text-color-auxiliary: #
|
|
33
|
+
--yee-text-color-auxiliary: #f5f5f5;
|
|
34
34
|
--yee-color-split: #e7e7e7;
|
|
35
35
|
--yee-border-color: #cbd6e2;
|
|
36
36
|
--yee-border-color-hover-focus: #75b9f0;
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
--yee-text-color-description: #8d8d8d;
|
|
31
31
|
--yee-text-color-disabled: #bfbfbf;
|
|
32
32
|
--yee-text-color-highlight: #bc463e;
|
|
33
|
-
--yee-text-color-auxiliary: #
|
|
33
|
+
--yee-text-color-auxiliary: #f5f5f5;
|
|
34
34
|
--yee-color-split: #eeeeee;
|
|
35
35
|
--yee-border-color: #cccccc;
|
|
36
36
|
--yee-border-color-hover-focus: #7aadc2;
|
package/src/style/color.css
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
--yee-text-color-description: #7c7c7c;
|
|
31
31
|
--yee-text-color-disabled: #bfbfbf;
|
|
32
32
|
--yee-text-color-highlight: #f58220;
|
|
33
|
-
--yee-text-color-auxiliary: #
|
|
33
|
+
--yee-text-color-auxiliary: #f5f5f5;
|
|
34
34
|
--yee-color-split: #e7e7e7;
|
|
35
35
|
--yee-border-color: #cbd6e2;
|
|
36
36
|
--yee-border-color-hover-focus: #75b9f0;
|
|
@@ -598,6 +598,15 @@
|
|
|
598
598
|
--yee-select-multi-disabled-tag-border-color: var(--yee-color-split);
|
|
599
599
|
--yee-select-multi-disabled-tag-bg-color: var(--yee-color-split);
|
|
600
600
|
|
|
601
|
+
/* --Segmented-- */
|
|
602
|
+
--yee-segmented-bg-color: var(--yee-color-bg-layout);
|
|
603
|
+
--yee-segmented-item-color: var(--yee-text-color);
|
|
604
|
+
--yee-segmented-item-hover-color: var(--yee-primary-color);
|
|
605
|
+
--yee-segmented-item-selected-color: var(--yee-text-color);
|
|
606
|
+
--yee-segmented-item-disabled-color: var(--yee-text-color-disabled);
|
|
607
|
+
--yee-segmented-thumb-bg-color: var(--yee-color-bg-container);
|
|
608
|
+
--yee-segmented-thumb-box-shadow: 0 2px 8px var(--yee-box-shadow-color);
|
|
609
|
+
|
|
601
610
|
/* --Slider-- */
|
|
602
611
|
--yee-slider-track-bg-color: var(--yee-primary-color);
|
|
603
612
|
--yee-slider-track-bg-hover-color: var(--yee-primary-color-hover);
|
|
@@ -954,6 +963,14 @@
|
|
|
954
963
|
--yee-tooltip-error-color: #fff;
|
|
955
964
|
--yee-tooltip-error-bg-color: var(--yee-error-color);
|
|
956
965
|
|
|
966
|
+
/* --Tour-- */
|
|
967
|
+
--yee-tour-mask-color: rgba(0, 0, 0, 45%);
|
|
968
|
+
--yee-tour-content-bg-color: var(--yee-color-bg-container);
|
|
969
|
+
--yee-tour-content-box-shadow: 0 6px 16px var(--yee-box-shadow-color);
|
|
970
|
+
--yee-tour-close-color: var(--yee-text-color);
|
|
971
|
+
--yee-tour-title-color: var(--yee-text-color);
|
|
972
|
+
--yee-tour-description-color: var(--yee-text-color-description);
|
|
973
|
+
|
|
957
974
|
/* --Tree-- */
|
|
958
975
|
--yee-tree-node-text-hover-bg-color: var(--yee-color-option-hover-bg);
|
|
959
976
|
--yee-tree-node-text-hover-color: var(--yee-color-option-hover);
|
package/src/style/size.css
CHANGED
|
@@ -418,6 +418,10 @@
|
|
|
418
418
|
--yee-radio-inner-border-width: 1px;
|
|
419
419
|
--yee-radio-inner-after-width: 10px;
|
|
420
420
|
--yee-radio-inner-after-height: 10px;
|
|
421
|
+
--yee-radio-font-small-size: 12px;
|
|
422
|
+
--yee-radio-small-padding: 2px;
|
|
423
|
+
--yee-radio-font-large-size: 16px;
|
|
424
|
+
--yee-radio-large-padding: 6px;
|
|
421
425
|
|
|
422
426
|
/* --Rate-- */
|
|
423
427
|
--yee-rate-star-width: 24px;
|
|
@@ -493,6 +497,20 @@
|
|
|
493
497
|
--yee-skeleton-active-translate-start: -37.5%;
|
|
494
498
|
--yee-skeleton-active-translate-end: 37.5%;
|
|
495
499
|
|
|
500
|
+
/* --Segmented-- */
|
|
501
|
+
--yee-segmented-padding: 2px;
|
|
502
|
+
--yee-segmented-border-radius: 8px;
|
|
503
|
+
--yee-segmented-item-padding: 4px 12px;
|
|
504
|
+
--yee-segmented-item-border-radius: 6px;
|
|
505
|
+
--yee-segmented-item-gap: 6px;
|
|
506
|
+
--yee-segmented-font-size: 14px;
|
|
507
|
+
--yee-segmented-item-small-padding: 2px 8px;
|
|
508
|
+
--yee-segmented-font-small-size: 12px;
|
|
509
|
+
--yee-segmented-item-large-padding: 6px 16px;
|
|
510
|
+
--yee-segmented-font-large-size: 16px;
|
|
511
|
+
--yee-segmented-thumb-border-radius: 6px;
|
|
512
|
+
--yee-segmented-transition-duration: 0.2s;
|
|
513
|
+
|
|
496
514
|
/* --Slider-- */
|
|
497
515
|
--yee-slider-height: 12px;
|
|
498
516
|
--yee-slider-padding: 4px 0;
|
|
@@ -783,6 +801,26 @@
|
|
|
783
801
|
--yee-tooltip-arrow-translate-y: 50%;
|
|
784
802
|
--yee-tooltip-arrow-rotate: -135deg;
|
|
785
803
|
|
|
804
|
+
/* --Tour-- */
|
|
805
|
+
--yee-tour-mask-z-index: var(--yee-z-index-mask);
|
|
806
|
+
--yee-tour-content-width: 400px;
|
|
807
|
+
--yee-tour-content-padding: 16px;
|
|
808
|
+
--yee-tour-content-border-radius: 8px;
|
|
809
|
+
--yee-tour-highlight-border-radius: 6px;
|
|
810
|
+
--yee-tour-close-top: 12px;
|
|
811
|
+
--yee-tour-close-right: 12px;
|
|
812
|
+
--yee-tour-close-size: 20px;
|
|
813
|
+
--yee-tour-header-margin-bottom: 8px;
|
|
814
|
+
--yee-tour-title-font-size: 16px;
|
|
815
|
+
--yee-tour-title-font-weight: 600;
|
|
816
|
+
--yee-tour-title-line-height: 24px;
|
|
817
|
+
--yee-tour-description-font-size: 14px;
|
|
818
|
+
--yee-tour-description-line-height: 22px;
|
|
819
|
+
--yee-tour-footer-margin-top: 16px;
|
|
820
|
+
--yee-tour-indicator-gap: 6px;
|
|
821
|
+
--yee-tour-button-gap: 8px;
|
|
822
|
+
--yee-tour-transition-duration: 0.2s;
|
|
823
|
+
|
|
786
824
|
/* --Tree-- */
|
|
787
825
|
--yee-tree-node-height: 32px;
|
|
788
826
|
--yee-tree-node-expand-icon-margin-left: 4px;
|
package/src/version.ts
CHANGED
package/dist/cjs/utils/x.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
declare class Constanter {
|
|
2
|
-
private constants;
|
|
3
|
-
init(constants: Record<string, any>): void;
|
|
4
|
-
set(key: string, value: any): void;
|
|
5
|
-
get(key: string): any;
|
|
6
|
-
getAll(): Record<string, any>;
|
|
7
|
-
remove(key: string): any;
|
|
8
|
-
removeAll(): void;
|
|
9
|
-
}
|
|
10
|
-
declare const constanter: Constanter;
|
|
11
|
-
export default constanter;
|