@rxdrag/website-lib-react 0.0.6 → 0.0.8

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 (78) hide show
  1. package/dist/ReactModalTrigger-9207e763.js.map +1 -1
  2. package/dist/components/ContactForm/ContactForm.d.ts +2 -1
  3. package/dist/components/Icon/index.d.ts +2 -1
  4. package/dist/forms.mjs +5 -3
  5. package/dist/forms.mjs.map +1 -1
  6. package/dist/ui.mjs.map +1 -1
  7. package/package.json +22 -23
  8. package/forms.ts +0 -1
  9. package/index.ts +0 -1
  10. package/media.ts +0 -1
  11. package/richtext.ts +0 -1
  12. package/src/components/Analytics/eventHandlers.ts +0 -173
  13. package/src/components/Analytics/index.tsx +0 -21
  14. package/src/components/Analytics/singleton.ts +0 -214
  15. package/src/components/Analytics/tracking.ts +0 -221
  16. package/src/components/Analytics/types.ts +0 -60
  17. package/src/components/Analytics/utils.ts +0 -95
  18. package/src/components/AttachmentIcon/index.tsx +0 -53
  19. package/src/components/BackgroundHlsVideoPlayer.tsx +0 -97
  20. package/src/components/BackgroundVideoPlayer.tsx +0 -32
  21. package/src/components/Bulletin.tsx +0 -30
  22. package/src/components/ContactForm/ContactForm.tsx +0 -290
  23. package/src/components/ContactForm/FileUpload2.tsx +0 -423
  24. package/src/components/ContactForm/Input.tsx +0 -48
  25. package/src/components/ContactForm/Input2.tsx +0 -59
  26. package/src/components/ContactForm/Submit.tsx +0 -48
  27. package/src/components/ContactForm/TelInput.tsx +0 -215
  28. package/src/components/ContactForm/TelInput2.tsx +0 -213
  29. package/src/components/ContactForm/Textarea.tsx +0 -48
  30. package/src/components/ContactForm/Textarea2.tsx +0 -89
  31. package/src/components/ContactForm/countryDialCodes.ts +0 -243
  32. package/src/components/ContactForm/factory.tsx +0 -60
  33. package/src/components/ContactForm/funcs.ts +0 -64
  34. package/src/components/ContactForm/hooks/useInlineLabelPadding.ts +0 -43
  35. package/src/components/ContactForm/hooks/useTelControl.ts +0 -81
  36. package/src/components/ContactForm/index.ts +0 -7
  37. package/src/components/ContactForm/types.ts +0 -68
  38. package/src/components/Icon/index.tsx +0 -20
  39. package/src/components/Medias/MainMedia.tsx +0 -257
  40. package/src/components/Medias/Thumbnail.tsx +0 -62
  41. package/src/components/Medias/VideoPlayer.tsx +0 -114
  42. package/src/components/Medias/index.tsx +0 -271
  43. package/src/components/ProductCard/ProductCard.tsx +0 -24
  44. package/src/components/ProductCard/ProductCta/index.tsx +0 -28
  45. package/src/components/ProductCard/ProductCta/style.css +0 -4
  46. package/src/components/ProductCard/ProductDescription/index.tsx +0 -13
  47. package/src/components/ProductCard/ProductDescription/style.css +0 -6
  48. package/src/components/ProductCard/ProductMedia/index.tsx +0 -35
  49. package/src/components/ProductCard/ProductMedia/style.css +0 -6
  50. package/src/components/ProductCard/ProductTitle/index.tsx +0 -7
  51. package/src/components/ProductCard/ProductTitle/style.css +0 -4
  52. package/src/components/ProductCard/ProductView.tsx +0 -36
  53. package/src/components/ProductCard/index.ts +0 -5
  54. package/src/components/ProductCard/useQueryProduct.ts +0 -32
  55. package/src/components/ReactModalTrigger.tsx +0 -28
  56. package/src/components/ReactVideoPlayer.tsx +0 -332
  57. package/src/components/RichTextOutline/index.tsx +0 -74
  58. package/src/components/RichTextOutline/parseOutline.ts +0 -63
  59. package/src/components/RichTextOutline/useAcitviedHeading.ts +0 -142
  60. package/src/components/RichTextOutline/useAnchorScroll.ts +0 -24
  61. package/src/components/Scroller.tsx +0 -39
  62. package/src/components/SearchInput.tsx +0 -21
  63. package/src/components/Share/index.tsx +0 -86
  64. package/src/components/Share/socials.tsx +0 -80
  65. package/src/components/Share//350/265/204/346/226/231.md +0 -7
  66. package/src/components/ToTop.tsx +0 -72
  67. package/src/components/VideoPlayIcon.tsx +0 -43
  68. package/src/components/all.ts +0 -25
  69. package/src/components/index.ts +0 -12
  70. package/src/forms.ts +0 -1
  71. package/src/index.ts +0 -1
  72. package/src/media.ts +0 -1
  73. package/src/richtext.ts +0 -1
  74. package/src/types/view-model.ts +0 -37
  75. package/src/ui.ts +0 -10
  76. package/src/video.ts +0 -2
  77. package/ui.ts +0 -1
  78. package/video.ts +0 -1
@@ -1,24 +0,0 @@
1
- import { CSSProperties } from "react";
2
- import { TSlateResizable } from "@rxdrag/tiptap-preview";
3
- import { ProductView } from "./ProductView";
4
- import { Product } from "@rxdrag/rxcms-models";
5
-
6
- export type TSlateProduct = TSlateResizable & {
7
- product?: Product;
8
- title?: string;
9
- description?: string;
10
- aspect?: string;
11
- ctaTitle?: string;
12
- };
13
-
14
- export const PRODUCT_KEY = "Product";
15
-
16
- export type ProductCardProps = {
17
- node?: TSlateProduct;
18
- style?: CSSProperties;
19
- };
20
-
21
- // Slate用的渲染组件
22
- export const ProductCard = ({ node }: ProductCardProps) => {
23
- return <ProductView product={node?.product} node={node} ctaTitle={node?.ctaTitle}/>;
24
- };
@@ -1,28 +0,0 @@
1
- import { Product } from "@rxdrag/rxcms-models";
2
- import "./style.css";
3
- import type { ReactNode } from "react";
4
-
5
- //TODO: 跟询盘触发器同样的实现原理,给询盘对话框发消息
6
- export function ProductCta(props: {
7
- product?: Product;
8
- openableKey?: string;
9
- children?: ReactNode;
10
- }) {
11
- const { product, openableKey = "enquiry-modal", children } = props;
12
- const roleProps = {
13
- "data-modal-open": openableKey,
14
- "data-call-to-action": `From RichText ProductCard-${product?.title}-${product?.id}`,
15
- };
16
-
17
- return (
18
- <div className="flex justify-center items-center">
19
- <button
20
- {...roleProps}
21
- className="product-cta-button relative flex-1 mt-2 flex items-center justify-center rounded-md border border-transparent bg-sky-600 text-md font-smibold text-white hover:bg-sky-700"
22
- type="button"
23
- >
24
- {children || "Get a quote"}
25
- </button>
26
- </div>
27
- );
28
- }
@@ -1,4 +0,0 @@
1
- .product-cta-button {
2
- width: 100%;
3
- padding: 8px 16px;
4
- }
@@ -1,13 +0,0 @@
1
- import clsx from "clsx"
2
- import { HTMLAttributes } from "react"
3
- import "./style.css"
4
-
5
- export function ProductDescription(props: HTMLAttributes<HTMLParagraphElement>) {
6
- const { className, ...rest } = props
7
- return (
8
- <p
9
- className={clsx("product-description", className)}
10
- {...rest}
11
- />
12
- )
13
- }
@@ -1,6 +0,0 @@
1
- .x-figure .product-description {
2
- margin-top: 0;
3
- margin-bottom: 0;
4
- padding-top: 0;
5
- padding-bottom: 0.4rem;
6
- }
@@ -1,35 +0,0 @@
1
- import { HtmlHTMLAttributes } from "react";
2
- import "./style.css";
3
- import { TProduct } from "../../../types/view-model";
4
-
5
- export function ProductMedia(
6
- props: {
7
- product?: TProduct;
8
- width?: number;
9
- aspect?: string;
10
- } & HtmlHTMLAttributes<HTMLImageElement>
11
- ) {
12
- const { product, style, width, className, aspect, ...rest } = props;
13
-
14
- return (
15
- <div
16
- className={"product-media " + className}
17
- style={{
18
- aspectRatio: aspect || "4 / 3",
19
- borderRadius: "1rem",
20
- overflow: "hidden",
21
- ...style,
22
- width,
23
- }}
24
- {...rest}
25
- >
26
- <img
27
- style={{
28
- width: "100%",
29
- }}
30
- src={product?.coverUrl}
31
- alt={product?.title}
32
- />
33
- </div>
34
- );
35
- }
@@ -1,6 +0,0 @@
1
- .x-figure .product-media {
2
- overflow: hidden;
3
- display: flex;
4
- align-items: center;
5
- justify-content: center;
6
- }
@@ -1,7 +0,0 @@
1
- import { HTMLAttributes } from "react";
2
- import "./style.css";
3
-
4
- export function ProductTitle(props: HTMLAttributes<HTMLHeadingElement>) {
5
- const { className, ...rest } = props;
6
- return <h3 className={"product-title " + className} {...rest} />;
7
- }
@@ -1,4 +0,0 @@
1
- .x-figure .product-title{
2
- margin-top: 0.6rem;
3
- padding-top: 0;
4
- }
@@ -1,36 +0,0 @@
1
- import { ProductCardProps } from "./ProductCard";
2
- import { Figcaption, Figure, FigureContent } from "@rxdrag/tiptap-preview";
3
- import { ProductMedia } from "./ProductMedia";
4
- import { ProductTitle } from "./ProductTitle";
5
- import { ProductDescription } from "./ProductDescription";
6
- import { ProductCta } from "./ProductCta";
7
- import { TProduct } from "../../types/view-model";
8
-
9
- export function ProductView(
10
- props: {
11
- product?: TProduct;
12
- ctaTitle?: string;
13
- } & ProductCardProps
14
- ) {
15
- const { product, ctaTitle, node } = props;
16
-
17
- const title = node?.title || product?.title;
18
- const description = node?.description || product?.description;
19
-
20
- return (
21
- <Figure>
22
- <FigureContent width={node?.width} align={node?.align}>
23
- <a href={`/products/${product?.slug}`}>
24
- <ProductMedia product={product} aspect={node?.aspect} />
25
- </a>
26
- </FigureContent>
27
- <Figcaption width={node?.width} align={node?.align}>
28
- <a href={`/products/${product?.slug}`}>
29
- <ProductTitle>{title}</ProductTitle>
30
- </a>
31
- <ProductDescription>{description}</ProductDescription>
32
- <ProductCta product={product}>{ctaTitle}</ProductCta>
33
- </Figcaption>
34
- </Figure>
35
- );
36
- }
@@ -1,5 +0,0 @@
1
- export * from "./ProductCta";
2
- export * from "./ProductTitle";
3
- export * from "./ProductDescription";
4
- export * from "./ProductCard";
5
- export * from "./ProductMedia";
@@ -1,32 +0,0 @@
1
- import { useEffect, useRef, useState } from "react";
2
- import { Product } from "@rxdrag/rxcms-models";
3
-
4
- export function useQueryProduct(productId?: string, websiteId?: string) {
5
- const active = useRef<boolean>(false);
6
- const [product, setProduct] = useState<Product | undefined>({});
7
-
8
- const url = websiteId
9
- ? `/${websiteId}/api/product/${productId}`
10
- : `/api/product/${productId}`;
11
-
12
- useEffect(() => {
13
- if (active.current && !productId) {
14
- return;
15
- }
16
-
17
- fetch(url)
18
- .then((res) => res.json())
19
- .then((data) => {
20
- setProduct(data);
21
- active.current = true;
22
- });
23
- }, [productId]);
24
-
25
- useEffect(() => {
26
- return () => {
27
- active.current = false;
28
- };
29
- });
30
-
31
- return product;
32
- }
@@ -1,28 +0,0 @@
1
- import type { ReactNode } from "react";
2
-
3
- export function ReactModalTrigger(props: {
4
- className?: string;
5
- openableKey?: string;
6
- callToAction?: string;
7
- children?: ReactNode;
8
- }) {
9
- const {
10
- className,
11
- callToAction,
12
- openableKey = "enquiry-modal",
13
- children,
14
- } = props;
15
- const roleProps = {
16
- "data-modal-open": openableKey,
17
- "data-call-to-action": callToAction,
18
- };
19
- return (
20
- <button
21
- {...roleProps}
22
- className={className}
23
- type="button"
24
- >
25
- {children || "Get a quote"}
26
- </button>
27
- );
28
- }
@@ -1,332 +0,0 @@
1
- import React, {
2
- useCallback,
3
- useEffect,
4
- useRef,
5
- useState,
6
- forwardRef,
7
- } from "react";
8
- import Hls from "hls.js";
9
- import type { Media } from "@rxdrag/rxcms-models";
10
- import { ReactModalTrigger } from "./ReactModalTrigger";
11
- import clsx from "clsx";
12
- import { VideoPlayIcon } from "./VideoPlayIcon";
13
-
14
- export type ID = string | number;
15
-
16
- export type VideoPlayerClassNames = {
17
- container?: string;
18
- aspect?: string;
19
- video?: string;
20
- playButton?: string;
21
- playButtonOuter?: string;
22
- playButtonInner?: string;
23
- playIcon?: string;
24
- loadingOverlay?: string;
25
- loadingContent?: string;
26
- overlay?: string;
27
- overlayTitle?: string;
28
- ctaButton?: string;
29
- overlayReplayButton?: string;
30
- };
31
-
32
- export const ReactVideoPlayer = forwardRef<
33
- HTMLDivElement,
34
- {
35
- endTitle?: string;
36
- media?: Media;
37
- posterUrl?: string;
38
- eagerLoad?: boolean;
39
- classNames?: VideoPlayerClassNames;
40
- callToAction?: string;
41
- onToggleSelect?: (id: ID) => void;
42
- designMode?: boolean;
43
- children?: React.ReactNode;
44
-
45
- }
46
- >((props, ref) => {
47
- const {
48
- endTitle,
49
- media,
50
- onToggleSelect,
51
- posterUrl,
52
- eagerLoad,
53
- classNames,
54
- callToAction,
55
- designMode,
56
- children,
57
- ...rest
58
- } = props;
59
- const videoRef = useRef<HTMLVideoElement>(null);
60
- const hlsRef = useRef<Hls | null>(null);
61
- const [isPlaying, setIsPlaying] = useState(false);
62
- const [isEnded, setIsEnded] = useState(false);
63
- const [isSourceReady, setIsSourceReady] = useState(false);
64
- const sourceReadyRef = useRef(false);
65
- const [isLoading, setIsLoading] = useState(false);
66
- const isEndedRef = useRef(false);
67
- const [hasStarted, setHasStarted] = useState(false);
68
-
69
- const ensureSourceReady = useCallback(() => {
70
- const video = videoRef.current;
71
- const url = media?.file?.original;
72
- if (!video || !url) return false;
73
- if (sourceReadyRef.current) return true;
74
-
75
- if (media?.storageType === "cloudflare_stream") {
76
- if (!Hls.isSupported()) {
77
- if (video.canPlayType("application/vnd.apple.mpegurl")) {
78
- video.src = url;
79
- sourceReadyRef.current = true;
80
- setIsSourceReady(true);
81
- return true;
82
- }
83
- return false;
84
- }
85
-
86
- const hls = new Hls({ enableWorker: true });
87
- hls.on(Hls.Events.ERROR, (_event, data) => {
88
- if (data.fatal) {
89
- switch (data.type) {
90
- case Hls.ErrorTypes.NETWORK_ERROR:
91
- hls.startLoad();
92
- break;
93
- case Hls.ErrorTypes.MEDIA_ERROR:
94
- hls.recoverMediaError();
95
- break;
96
- default:
97
- hls.destroy();
98
- break;
99
- }
100
- }
101
- });
102
- hls.loadSource(url);
103
- hls.attachMedia(video);
104
- hlsRef.current = hls;
105
- sourceReadyRef.current = true;
106
- setIsSourceReady(true);
107
- return true;
108
- }
109
-
110
- video.src = url;
111
- sourceReadyRef.current = true;
112
- setIsSourceReady(true);
113
- return true;
114
- }, [media?.file?.original, media?.storageType]);
115
-
116
- const handleContainerClick = useCallback(
117
- (e: React.MouseEvent) => {
118
- if (designMode) return;
119
- if (isPlaying && videoRef.current) {
120
- e.stopPropagation();
121
- videoRef.current.pause();
122
- return;
123
- }
124
- if (media?.id != null) {
125
- onToggleSelect?.(media.id);
126
- }
127
- },
128
- [isPlaying, media?.id, onToggleSelect, designMode],
129
- );
130
-
131
- const handlePlayClick = useCallback(
132
- (e: React.MouseEvent) => {
133
- if (designMode) return;
134
- e.stopPropagation();
135
- const v = videoRef.current;
136
- if (!v) return;
137
- if (isPlaying) {
138
- v.pause();
139
- return;
140
- }
141
-
142
- const ok = ensureSourceReady();
143
- if (!ok) return;
144
-
145
- setIsLoading(true);
146
-
147
- v.play().catch((error) => {
148
- console.log("ReactVideoPlayer play() failed:", error);
149
- setIsLoading(false);
150
- });
151
- },
152
- [isPlaying, designMode, ensureSourceReady],
153
- );
154
-
155
- useEffect(() => {
156
- const video = videoRef.current;
157
- if (!video) return;
158
-
159
- video.pause();
160
- if (hlsRef.current) {
161
- hlsRef.current.destroy();
162
- hlsRef.current = null;
163
- }
164
- video.removeAttribute("src");
165
- video.load();
166
- setIsSourceReady(false);
167
- sourceReadyRef.current = false;
168
-
169
- const onPlay = () => {
170
- setIsPlaying(true);
171
- setIsEnded(false);
172
- isEndedRef.current = false;
173
- setIsLoading(false);
174
- setHasStarted(true);
175
- };
176
- const onPause = () => setIsPlaying(false);
177
- const onEnded = () => {
178
- setIsPlaying(false);
179
- setIsEnded(true);
180
- isEndedRef.current = true;
181
- setIsLoading(false);
182
- };
183
- const onWaiting = () => {
184
- if (!isEndedRef.current) setIsLoading(true);
185
- };
186
- const onCanPlay = () => {
187
- setIsLoading(false);
188
- };
189
-
190
- video.addEventListener("play", onPlay);
191
- video.addEventListener("pause", onPause);
192
- video.addEventListener("ended", onEnded);
193
- video.addEventListener("waiting", onWaiting);
194
- video.addEventListener("canplay", onCanPlay);
195
- video.addEventListener("playing", onCanPlay);
196
-
197
- if (eagerLoad) {
198
- ensureSourceReady();
199
- }
200
-
201
- return () => {
202
- video.removeEventListener("play", onPlay);
203
- video.removeEventListener("pause", onPause);
204
- video.removeEventListener("ended", onEnded);
205
- video.removeEventListener("waiting", onWaiting);
206
- video.removeEventListener("canplay", onCanPlay);
207
- video.removeEventListener("playing", onCanPlay);
208
-
209
- if (hlsRef.current) {
210
- hlsRef.current.destroy();
211
- hlsRef.current = null;
212
- }
213
- };
214
- }, [eagerLoad, ensureSourceReady, media?.file?.original, media?.storageType]);
215
-
216
- return (
217
- <div
218
- ref={ref}
219
- className={clsx(
220
- "relative w-full rounded-2xl",
221
- classNames?.aspect ?? "aspect-video",
222
- classNames?.container,
223
- )}
224
- onClick={handleContainerClick}
225
- {...rest}
226
- >
227
- <video
228
- ref={videoRef}
229
- onClick={(e) => !designMode && e.stopPropagation()}
230
- preload={eagerLoad ? "metadata" : "none"}
231
- poster={posterUrl ?? media?.file?.thumbnail}
232
- className={clsx(
233
- "w-full h-full rounded-lg object-cover",
234
- classNames?.video,
235
- )}
236
- playsInline
237
- controls={!designMode && hasStarted}
238
- disablePictureInPicture
239
- controlsList="nodownload noremoteplayback noplaybackrate"
240
- >
241
- {!media?.storageType &&
242
- media?.file?.original &&
243
- (eagerLoad || isSourceReady) ? (
244
- <source src={media.file.original} />
245
- ) : null}
246
- Your browser does not support video playback.
247
- </video>
248
-
249
- {!isPlaying && !isEnded && !isLoading && (
250
- <button
251
- type="button"
252
- onClick={handlePlayClick}
253
- className={clsx(
254
- "absolute inset-0 items-center justify-center w-full h-full transition rounded-lg z-10",
255
- hasStarted ? "hidden md:flex" : "flex",
256
- classNames?.playButton,
257
- )}
258
- >
259
- {children || <VideoPlayIcon classNames={classNames} />}
260
- </button>
261
- )}
262
-
263
- {!isPlaying && !isEnded && isLoading && (
264
- <div
265
- className={clsx(
266
- "absolute inset-0 flex items-center justify-center w-full h-full rounded-lg z-10 bg-black/20",
267
- classNames?.loadingOverlay,
268
- )}
269
- onClick={(e) => e.stopPropagation()}
270
- >
271
- <div
272
- className={clsx(
273
- "flex items-center gap-3 rounded-full bg-black/50 px-4 py-2 text-white",
274
- classNames?.loadingContent,
275
- )}
276
- >
277
- <div className="h-4 w-4 animate-spin rounded-full border-2 border-white/30 border-t-white" />
278
- <span className="text-sm">Loading...</span>
279
- </div>
280
- </div>
281
- )}
282
-
283
- <div
284
- className={clsx(
285
- "absolute inset-0 bg-black/50 flex-col gap-6 items-center justify-center text-center text-white hidden z-10 rounded-2xl",
286
- classNames?.overlay,
287
- )}
288
- style={{ display: isEnded ? "flex" : "none" }}
289
- >
290
- <p
291
- className={clsx("text-4xl drop-shadow-lg", classNames?.overlayTitle)}
292
- >
293
- {endTitle ?? "Contact Us Now"}
294
- </p>
295
- <div className="flex items-center gap-4">
296
- <ReactModalTrigger
297
- className={clsx(
298
- "btn btn-primary px-10 py-2.5 rounded-full ripple",
299
- classNames?.ctaButton,
300
- )}
301
- callToAction={callToAction}
302
- >
303
- Consult Now
304
- </ReactModalTrigger>
305
- <button
306
- type="button"
307
- className={clsx(
308
- "px-10 py-2.5 bg-white/20 hover:bg-white/30 rounded-full transition text-sm",
309
- classNames?.overlayReplayButton,
310
- )}
311
- onClick={(e) => {
312
- e.stopPropagation();
313
- const v = videoRef.current;
314
- if (!v) return;
315
- const ok = ensureSourceReady();
316
- if (!ok) return;
317
- setIsLoading(true);
318
- v.currentTime = 0;
319
- setIsEnded(false);
320
- v.play().catch((error) => {
321
- console.log("ReactVideoPlayer play() failed:", error);
322
- setIsLoading(false);
323
- });
324
- }}
325
- >
326
- Replay
327
- </button>
328
- </div>
329
- </div>
330
- </div>
331
- );
332
- });
@@ -1,74 +0,0 @@
1
- import { forwardRef, useEffect } from "react";
2
- import { useAcitviedHeading } from "./useAcitviedHeading";
3
- import clsx from "clsx";
4
- import { parseOutline } from "./parseOutline";
5
-
6
- export type RichTextOutlineProps = {
7
- className?: string;
8
- itemClassName?: string;
9
- value?: string;
10
- //滚动偏移量
11
- yOffset?: number;
12
- style?: React.CSSProperties;
13
- };
14
-
15
- export const RichTextOutline = forwardRef<
16
- HTMLUListElement,
17
- RichTextOutlineProps
18
- >((props, ref) => {
19
- const { className, itemClassName, value, yOffset = 200, ...rest } = props;
20
- const activiedId = useAcitviedHeading(yOffset);
21
- const outline = parseOutline(value);
22
-
23
- useEffect(() => {
24
- const handleHashChange = () => {
25
- const element = document.getElementById(
26
- window.location.hash.substring(1)
27
- );
28
- if (element) {
29
- const y =
30
- element.getBoundingClientRect().top + window.scrollY - yOffset;
31
- window.scrollTo({ top: y, behavior: "smooth" });
32
- }
33
- };
34
-
35
- window.addEventListener("hashchange", handleHashChange);
36
-
37
- return () => {
38
- window.removeEventListener("hashchange", handleHashChange);
39
- };
40
- }, [yOffset]);
41
-
42
- return outline?.length ? (
43
- <ul ref={ref} className={className} {...rest}>
44
- {outline?.map((item, index) => {
45
- return (
46
- <li
47
- key={item?.key + index}
48
- className={clsx(
49
- activiedId === item?.key ? "actived" : "",
50
- itemClassName
51
- )}
52
- >
53
- <a
54
- href={`#${item?.key}`}
55
- onClick={(e) => {
56
- e.preventDefault();
57
- const element = document.getElementById(item?.key);
58
- if (element) {
59
- const y =
60
- element.getBoundingClientRect().top +
61
- window.scrollY +
62
- yOffset;
63
- window.scrollTo({ top: y, behavior: "smooth" });
64
- }
65
- }}
66
- >
67
- {item?.text}
68
- </a>
69
- </li>
70
- );
71
- })}
72
- </ul>
73
- ) : null;
74
- });