@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.
Files changed (218) hide show
  1. package/dist/cjs/Button/button.js +2 -2
  2. package/dist/cjs/Button/style/index.less +23 -8
  3. package/dist/cjs/Card/demo/collapsible.js +2 -1
  4. package/dist/cjs/Carousel/demo/arrows.js +1 -1
  5. package/dist/cjs/Carousel/demo/autoplay.js +1 -1
  6. package/dist/cjs/Carousel/demo/effect.js +1 -1
  7. package/dist/cjs/Carousel/demo/position.js +1 -1
  8. package/dist/cjs/Config-Provider/interface.d.ts +4 -0
  9. package/dist/cjs/Form/Field.js +32 -10
  10. package/dist/cjs/Form/interface.d.ts +17 -1
  11. package/dist/cjs/Menu/style/index.less +2 -2
  12. package/dist/cjs/Radio/demo/button.js +1 -0
  13. package/dist/cjs/Radio/interface.d.ts +4 -0
  14. package/dist/cjs/Radio/radio-group.js +28 -14
  15. package/dist/cjs/Radio/radio.js +3 -1
  16. package/dist/cjs/Radio/style/index.less +19 -5
  17. package/dist/cjs/Segmented/demo/basic.d.ts +3 -0
  18. package/dist/cjs/Segmented/demo/basic.js +51 -0
  19. package/dist/cjs/Segmented/demo/block.d.ts +3 -0
  20. package/dist/cjs/Segmented/demo/block.js +49 -0
  21. package/dist/cjs/Segmented/demo/disabled.d.ts +3 -0
  22. package/dist/cjs/Segmented/demo/disabled.js +47 -0
  23. package/dist/cjs/Segmented/demo/size.d.ts +3 -0
  24. package/dist/cjs/Segmented/demo/size.js +37 -0
  25. package/dist/cjs/Segmented/index.d.ts +3 -0
  26. package/dist/cjs/Segmented/index.js +36 -0
  27. package/dist/cjs/Segmented/interface.d.ts +82 -0
  28. package/dist/cjs/Segmented/interface.js +17 -0
  29. package/dist/cjs/Segmented/segmented.d.ts +5 -0
  30. package/dist/cjs/Segmented/segmented.js +177 -0
  31. package/dist/cjs/Segmented/style/index.less +104 -0
  32. package/dist/cjs/Spin/spin.js +2 -1
  33. package/dist/cjs/Steps/step.js +1 -1
  34. package/dist/cjs/Steps/style/index.less +8 -2
  35. package/dist/cjs/Switch/demo/content.js +22 -1
  36. package/dist/cjs/Switch/style/index.less +16 -0
  37. package/dist/cjs/Switch/switch.js +9 -2
  38. package/dist/cjs/Table/interface.d.ts +5 -1
  39. package/dist/cjs/Table/theader/cell.js +4 -5
  40. package/dist/cjs/Tag/demo/size.js +1 -1
  41. package/dist/cjs/Tag/style/index.less +1 -0
  42. package/dist/cjs/Tour/demo/basic.d.ts +3 -0
  43. package/dist/cjs/Tour/demo/basic.js +69 -0
  44. package/dist/cjs/Tour/demo/indicators.d.ts +3 -0
  45. package/dist/cjs/Tour/demo/indicators.js +76 -0
  46. package/dist/cjs/Tour/demo/placement.d.ts +3 -0
  47. package/dist/cjs/Tour/demo/placement.js +80 -0
  48. package/dist/cjs/Tour/demo/scroll.d.ts +3 -0
  49. package/dist/cjs/Tour/demo/scroll.js +72 -0
  50. package/dist/cjs/Tour/index.d.ts +3 -0
  51. package/dist/cjs/Tour/index.js +36 -0
  52. package/dist/cjs/Tour/interface.d.ts +118 -0
  53. package/dist/cjs/Tour/interface.js +17 -0
  54. package/dist/cjs/Tour/style/index.less +103 -0
  55. package/dist/cjs/Tour/tour.d.ts +5 -0
  56. package/dist/cjs/Tour/tour.js +403 -0
  57. package/dist/cjs/Tree/demo/checkable.js +6 -6
  58. package/dist/cjs/index.d.ts +4 -0
  59. package/dist/cjs/index.js +6 -0
  60. package/dist/cjs/locale/interface.d.ts +7 -0
  61. package/dist/cjs/locale/lang/en_US.js +6 -0
  62. package/dist/cjs/locale/lang/ja_JP.js +6 -0
  63. package/dist/cjs/locale/lang/zh_CN.js +6 -0
  64. package/dist/cjs/locale/lang/zh_TW.js +6 -0
  65. package/dist/cjs/style/color.blue.css +1 -1
  66. package/dist/cjs/style/color.crimson.css +1 -1
  67. package/dist/cjs/style/color.css +18 -1
  68. package/dist/cjs/style/size.css +38 -0
  69. package/dist/cjs/style/themes.css +3 -3
  70. package/dist/cjs/style/variables.css +57 -2
  71. package/dist/cjs/version.d.ts +1 -1
  72. package/dist/cjs/version.js +1 -1
  73. package/dist/esm/Button/button.js +2 -2
  74. package/dist/esm/Button/style/index.less +23 -8
  75. package/dist/esm/Card/demo/collapsible.js +2 -1
  76. package/dist/esm/Carousel/demo/arrows.js +1 -1
  77. package/dist/esm/Carousel/demo/autoplay.js +1 -1
  78. package/dist/esm/Carousel/demo/effect.js +1 -1
  79. package/dist/esm/Carousel/demo/position.js +1 -1
  80. package/dist/esm/Config-Provider/interface.d.ts +4 -0
  81. package/dist/esm/Form/Field.js +11 -4
  82. package/dist/esm/Form/interface.d.ts +17 -1
  83. package/dist/esm/Menu/style/index.less +2 -2
  84. package/dist/esm/Radio/demo/button.js +1 -0
  85. package/dist/esm/Radio/interface.d.ts +4 -0
  86. package/dist/esm/Radio/radio-group.js +9 -2
  87. package/dist/esm/Radio/radio.js +3 -1
  88. package/dist/esm/Radio/style/index.less +19 -5
  89. package/dist/esm/Segmented/demo/basic.d.ts +3 -0
  90. package/dist/esm/Segmented/demo/basic.js +33 -0
  91. package/dist/esm/Segmented/demo/block.d.ts +3 -0
  92. package/dist/esm/Segmented/demo/block.js +29 -0
  93. package/dist/esm/Segmented/demo/disabled.d.ts +3 -0
  94. package/dist/esm/Segmented/demo/disabled.js +22 -0
  95. package/dist/esm/Segmented/demo/size.d.ts +3 -0
  96. package/dist/esm/Segmented/demo/size.js +15 -0
  97. package/dist/esm/Segmented/index.d.ts +3 -0
  98. package/dist/esm/Segmented/index.js +2 -0
  99. package/dist/esm/Segmented/interface.d.ts +82 -0
  100. package/dist/esm/Segmented/interface.js +1 -0
  101. package/dist/esm/Segmented/segmented.d.ts +5 -0
  102. package/dist/esm/Segmented/segmented.js +110 -0
  103. package/dist/esm/Segmented/style/index.less +104 -0
  104. package/dist/esm/Spin/spin.js +2 -1
  105. package/dist/esm/Steps/step.js +4 -1
  106. package/dist/esm/Steps/style/index.less +8 -2
  107. package/dist/esm/Switch/demo/content.js +12 -3
  108. package/dist/esm/Switch/style/index.less +16 -0
  109. package/dist/esm/Switch/switch.js +7 -5
  110. package/dist/esm/Table/interface.d.ts +5 -1
  111. package/dist/esm/Table/theader/cell.js +2 -3
  112. package/dist/esm/Tag/demo/size.js +6 -2
  113. package/dist/esm/Tag/style/index.less +1 -0
  114. package/dist/esm/Tour/demo/basic.d.ts +3 -0
  115. package/dist/esm/Tour/demo/basic.js +69 -0
  116. package/dist/esm/Tour/demo/indicators.d.ts +3 -0
  117. package/dist/esm/Tour/demo/indicators.js +73 -0
  118. package/dist/esm/Tour/demo/placement.d.ts +3 -0
  119. package/dist/esm/Tour/demo/placement.js +53 -0
  120. package/dist/esm/Tour/demo/scroll.d.ts +3 -0
  121. package/dist/esm/Tour/demo/scroll.js +58 -0
  122. package/dist/esm/Tour/index.d.ts +3 -0
  123. package/dist/esm/Tour/index.js +2 -0
  124. package/dist/esm/Tour/interface.d.ts +118 -0
  125. package/dist/esm/Tour/interface.js +1 -0
  126. package/dist/esm/Tour/style/index.less +103 -0
  127. package/dist/esm/Tour/tour.d.ts +5 -0
  128. package/dist/esm/Tour/tour.js +383 -0
  129. package/dist/esm/Tree/demo/checkable.js +6 -6
  130. package/dist/esm/index.d.ts +4 -0
  131. package/dist/esm/index.js +4 -0
  132. package/dist/esm/locale/interface.d.ts +7 -0
  133. package/dist/esm/locale/lang/en_US.js +6 -0
  134. package/dist/esm/locale/lang/ja_JP.js +6 -0
  135. package/dist/esm/locale/lang/zh_CN.js +6 -0
  136. package/dist/esm/locale/lang/zh_TW.js +6 -0
  137. package/dist/esm/style/color.blue.css +1 -1
  138. package/dist/esm/style/color.crimson.css +1 -1
  139. package/dist/esm/style/color.css +18 -1
  140. package/dist/esm/style/size.css +38 -0
  141. package/dist/esm/style/themes.css +3 -3
  142. package/dist/esm/style/variables.css +57 -2
  143. package/dist/esm/version.d.ts +1 -1
  144. package/dist/esm/version.js +1 -1
  145. package/dist/umd/yee-c.min.css +3 -3
  146. package/dist/umd/yee-c.min.js +1 -1
  147. package/package.json +1 -1
  148. package/src/Button/button.tsx +2 -2
  149. package/src/Button/style/index.less +23 -8
  150. package/src/Card/demo/collapsible.tsx +1 -0
  151. package/src/Carousel/demo/arrows.tsx +1 -1
  152. package/src/Carousel/demo/autoplay.tsx +1 -1
  153. package/src/Carousel/demo/effect.tsx +1 -1
  154. package/src/Carousel/demo/position.tsx +1 -1
  155. package/src/Config-Provider/index.en-US.md +2 -0
  156. package/src/Config-Provider/index.zh-CN.md +2 -0
  157. package/src/Config-Provider/interface.ts +4 -0
  158. package/src/Form/Field.tsx +30 -11
  159. package/src/Form/index.en-US.md +4 -0
  160. package/src/Form/index.zh-CN.md +4 -0
  161. package/src/Form/interface.ts +17 -1
  162. package/src/Menu/style/index.less +2 -2
  163. package/src/Radio/demo/button.tsx +1 -0
  164. package/src/Radio/index.en-US.md +2 -0
  165. package/src/Radio/index.zh-CN.md +2 -0
  166. package/src/Radio/interface.ts +4 -0
  167. package/src/Radio/radio-group.tsx +9 -1
  168. package/src/Radio/radio.tsx +2 -1
  169. package/src/Radio/style/index.less +19 -5
  170. package/src/Segmented/demo/basic.tsx +24 -0
  171. package/src/Segmented/demo/block.tsx +17 -0
  172. package/src/Segmented/demo/disabled.tsx +18 -0
  173. package/src/Segmented/demo/size.tsx +10 -0
  174. package/src/Segmented/index.en-US.md +47 -0
  175. package/src/Segmented/index.ts +12 -0
  176. package/src/Segmented/index.zh-CN.md +47 -0
  177. package/src/Segmented/interface.ts +87 -0
  178. package/src/Segmented/segmented.tsx +134 -0
  179. package/src/Segmented/style/index.less +104 -0
  180. package/src/Spin/spin.tsx +1 -0
  181. package/src/Steps/step.tsx +1 -1
  182. package/src/Steps/style/index.less +8 -2
  183. package/src/Switch/demo/content.tsx +15 -5
  184. package/src/Switch/style/index.less +16 -0
  185. package/src/Switch/switch.tsx +14 -6
  186. package/src/Table/index.en-US.md +1 -0
  187. package/src/Table/index.zh-CN.md +1 -0
  188. package/src/Table/interface.ts +5 -1
  189. package/src/Table/theader/cell.tsx +2 -9
  190. package/src/Tag/demo/size.tsx +2 -2
  191. package/src/Tag/style/index.less +1 -0
  192. package/src/Tour/demo/basic.tsx +52 -0
  193. package/src/Tour/demo/indicators.tsx +63 -0
  194. package/src/Tour/demo/placement.tsx +55 -0
  195. package/src/Tour/demo/scroll.tsx +49 -0
  196. package/src/Tour/index.en-US.md +61 -0
  197. package/src/Tour/index.ts +10 -0
  198. package/src/Tour/index.zh-CN.md +61 -0
  199. package/src/Tour/interface.ts +126 -0
  200. package/src/Tour/style/index.less +103 -0
  201. package/src/Tour/tour.tsx +399 -0
  202. package/src/Tree/demo/checkable.tsx +6 -6
  203. package/src/index.ts +6 -0
  204. package/src/locale/interface.ts +9 -0
  205. package/src/locale/lang/en_US.ts +7 -0
  206. package/src/locale/lang/ja_JP.ts +7 -0
  207. package/src/locale/lang/zh_CN.ts +7 -0
  208. package/src/locale/lang/zh_TW.ts +7 -0
  209. package/src/style/color.blue.css +1 -1
  210. package/src/style/color.crimson.css +1 -1
  211. package/src/style/color.css +18 -1
  212. package/src/style/size.css +38 -0
  213. package/src/version.ts +1 -1
  214. package/dist/cjs/utils/x.d.ts +0 -11
  215. package/dist/cjs/utils/x.js +0 -51
  216. package/dist/esm/utils/x.d.ts +0 -11
  217. package/dist/esm/utils/x.js +0 -49
  218. 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: '0-0',
10
+ key: 1,
11
11
  children: [
12
12
  {
13
13
  label: 'parent 1-0',
14
- key: '0-0-0',
14
+ key: 10,
15
15
  children: [
16
16
  {
17
17
  label: 'leaf',
18
- key: '0-0-0-0',
18
+ key: 100,
19
19
  },
20
20
  {
21
21
  label: 'leaf',
22
- key: '0-0-0-1',
22
+ key: 101,
23
23
  },
24
24
  ],
25
25
  },
26
26
  {
27
27
  label: 'parent 1-1',
28
- key: '0-0-1',
28
+ key: 20,
29
29
  children: [
30
30
  {
31
31
  label: 'leaf',
32
- key: '0-0-1-0',
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
 
@@ -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 ============
@@ -116,6 +116,13 @@ const locale: Locale = {
116
116
  cancelText: 'Cancel',
117
117
  },
118
118
 
119
+ tour: {
120
+ previous: 'Previous',
121
+ next: 'Next',
122
+ finish: 'Finish',
123
+ skip: 'Skip',
124
+ },
125
+
119
126
  popconfirm: {
120
127
  okText: 'OK',
121
128
  cancelText: 'Cancel',
@@ -117,6 +117,13 @@ const locale: Locale = {
117
117
  cancelText: 'キャンセル',
118
118
  },
119
119
 
120
+ tour: {
121
+ previous: '前へ',
122
+ next: '次へ',
123
+ finish: '完了',
124
+ skip: 'スキップ',
125
+ },
126
+
120
127
  popconfirm: {
121
128
  okText: 'OK',
122
129
  cancelText: 'キャンセル',
@@ -107,6 +107,13 @@ const locale: Locale = {
107
107
  cancelText: '取消',
108
108
  },
109
109
 
110
+ tour: {
111
+ previous: '上一步',
112
+ next: '下一步',
113
+ finish: '完成',
114
+ skip: '跳过',
115
+ },
116
+
110
117
  popconfirm: {
111
118
  okText: '确定',
112
119
  cancelText: '取消',
@@ -107,6 +107,13 @@ const locale: Locale = {
107
107
  cancelText: '取消',
108
108
  },
109
109
 
110
+ tour: {
111
+ previous: '上一步',
112
+ next: '下一步',
113
+ finish: '完成',
114
+ skip: '跳過',
115
+ },
116
+
110
117
  popconfirm: {
111
118
  okText: '確定',
112
119
  cancelText: '取消',
@@ -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: #bfbfbf;
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: #bfbfbf;
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;
@@ -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: #bfbfbf;
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);
@@ -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
@@ -4,7 +4,7 @@ declare global {
4
4
  }
5
5
  }
6
6
 
7
- export const version = "0.2.0";
7
+ export const version = "0.3.0";
8
8
 
9
9
  if (typeof window !== 'undefined') {
10
10
  window.__yee_c_version__ = version;
@@ -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;