@rxdrag/website-lib-core 0.0.11 → 0.0.13

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 (36) hide show
  1. package/package.json +4 -4
  2. package/src/component-logic/modal.ts +11 -2
  3. package/src/controller/OpenableController.ts +5 -0
  4. package/src/controller/PageLoader.ts +1 -1
  5. package/src/entify/Entify.ts +56 -0
  6. package/src/entify/IEntify.ts +43 -3
  7. package/src/entify/lib/langFields.ts +2 -0
  8. package/src/entify/lib/newPageMetaOptions.ts +4 -6
  9. package/src/entify/lib/newQueryPageOptions.ts +14 -0
  10. package/src/entify/lib/queryAllProducts.ts +5 -0
  11. package/src/entify/lib/queryFeaturedProducts.ts +5 -0
  12. package/src/entify/lib/queryLangs.ts +8 -21
  13. package/src/entify/lib/queryLatestPosts.ts +6 -1
  14. package/src/entify/lib/queryOnePostBySlug.ts +27 -10
  15. package/src/entify/lib/queryOnePostCategoryBySlug.ts +5 -0
  16. package/src/entify/lib/queryOneProductBySlug.ts +13 -3
  17. package/src/entify/lib/queryOneProductCategoryBySlug.ts +24 -5
  18. package/src/entify/lib/queryOneTheme.ts +2 -1
  19. package/src/entify/lib/queryPageBySlug.ts +43 -0
  20. package/src/entify/lib/queryPageByType.ts +44 -0
  21. package/src/entify/lib/queryPostCategories.ts +7 -0
  22. package/src/entify/lib/queryPostSlugs.ts +5 -0
  23. package/src/entify/lib/queryPosts.ts +56 -39
  24. package/src/entify/lib/queryProductCategories.ts +7 -0
  25. package/src/entify/lib/queryProducts.ts +5 -0
  26. package/src/entify/lib/queryProductsInMenu.ts +14 -12
  27. package/src/entify/lib/queryUserPosts.ts +5 -0
  28. package/src/entify/lib/queryWebSiteSettings.ts +2 -3
  29. package/src/entify/lib/queryWebsite.ts +43 -0
  30. package/src/entify/lib/searchProducts.ts +7 -0
  31. package/src/entify/view-model/models.ts +0 -8
  32. package/src/react/components/ContactForm/index.tsx +6 -29
  33. package/src/react/components/Medias/index.tsx +270 -273
  34. package/src/react/components/ProductCard/ProductCta/index.tsx +1 -1
  35. package/src/react/components/RichTextOutline/index.tsx +4 -5
  36. package/src/react/components/RichTextOutline/useAcitviedHeading.ts +81 -54
@@ -8,146 +8,227 @@ export type MediasProps = {
8
8
  children?: React.ReactNode;
9
9
  // Aspect ratio, format is `aspect-[width/height]`
10
10
  aspect?: string;
11
+ thumbnailAspect?: string;
11
12
  };
12
13
 
13
- export const Medias = forwardRef<HTMLDivElement, MediasProps>(
14
- (props, ref) => {
15
- const {
16
- value,
17
- className,
18
- children,
19
- aspect = "aspect-[5/4]",
20
- ...rest
21
- } = props;
22
- const [selectedId, setSelectedId] = useState<string | undefined | null>(
14
+ export const Medias = forwardRef<HTMLDivElement, MediasProps>((props, ref) => {
15
+ const {
16
+ value,
17
+ className,
18
+ children,
19
+ aspect = "aspect-[1/1]",
20
+ thumbnailAspect = "aspect-[5/4]",
21
+ ...rest
22
+ } = props;
23
+ const [selectedId, setSelectedId] = useState<string | undefined | null>(
24
+ value?.externalVideoUrl ? "video" : value?.medias?.[0]?.id || ""
25
+ );
26
+ const [videoUrl, setVideoUrl] = useState<string>("");
27
+ const [thumbnailUrl, setThumbnailUrl] = useState<string>("");
28
+
29
+ useEffect(() => {
30
+ setSelectedId(
23
31
  value?.externalVideoUrl ? "video" : value?.medias?.[0]?.id || ""
24
32
  );
25
- const [videoUrl, setVideoUrl] = useState<string>("");
26
- const [thumbnailUrl, setThumbnailUrl] = useState<string>("");
33
+ }, [value]);
27
34
 
28
- useEffect(() => {
29
- setSelectedId(
30
- value?.externalVideoUrl ? "video" : value?.medias?.[0]?.id || ""
35
+ useEffect(() => {
36
+ if (value?.externalVideoUrl) {
37
+ const baseUrl = value.externalVideoUrl.replace(
38
+ "https://youtu.be/",
39
+ "https://www.youtube.com/embed/"
31
40
  );
32
- }, [value]);
33
-
34
- useEffect(() => {
35
- if (value?.externalVideoUrl) {
36
- const baseUrl = value.externalVideoUrl.replace(
37
- "https://youtu.be/",
38
- "https://www.youtube.com/embed/"
39
- );
40
- const separator = baseUrl.includes("?") ? "&" : "?";
41
- setVideoUrl(
42
- `${baseUrl}${separator}autoplay=1&muted=1&modestbranding=1&rel=0&controls=1&playsinline=1&enablejsapi=1&origin=${encodeURIComponent(
43
- window.location.origin
44
- )}`
45
- );
46
- }
47
- }, [value?.externalVideoUrl]);
41
+ const separator = baseUrl.includes("?") ? "&" : "?";
42
+ setVideoUrl(
43
+ `${baseUrl}${separator}autoplay=1&muted=1&modestbranding=1&rel=0&controls=1&playsinline=1&enablejsapi=1&origin=${encodeURIComponent(
44
+ window.location.origin
45
+ )}`
46
+ );
47
+ }
48
+ }, [value?.externalVideoUrl]);
48
49
 
49
- useEffect(() => {
50
- if (value?.externalVideoUrl) {
51
- const videoId = value.externalVideoUrl.includes("youtu.be/")
52
- ? value.externalVideoUrl.split("youtu.be/")[1].split("?")[0]
53
- : value.externalVideoUrl.split("v=")[1]?.split("&")[0];
50
+ useEffect(() => {
51
+ if (value?.externalVideoUrl) {
52
+ const videoId = value.externalVideoUrl.includes("youtu.be/")
53
+ ? value.externalVideoUrl.split("youtu.be/")[1].split("?")[0]
54
+ : value.externalVideoUrl.split("v=")[1]?.split("&")[0];
54
55
 
55
- if (videoId) {
56
- setThumbnailUrl(
57
- `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`
58
- );
59
- }
56
+ if (videoId) {
57
+ setThumbnailUrl(`https://img.youtube.com/vi/${videoId}/hqdefault.jpg`);
60
58
  }
61
- }, [value?.externalVideoUrl]);
59
+ }
60
+ }, [value?.externalVideoUrl]);
62
61
 
63
- const selectedIndex = value?.externalVideoUrl
64
- ? selectedId === "video"
65
- ? 0
66
- : (value?.medias?.findIndex((media) => media.id === selectedId) || 0) +
67
- 1
68
- : value?.medias?.findIndex((media) => media.id === selectedId) || 0;
62
+ const selectedIndex = value?.externalVideoUrl
63
+ ? selectedId === "video"
64
+ ? 0
65
+ : (value?.medias?.findIndex((media) => media.id === selectedId) || 0) + 1
66
+ : value?.medias?.findIndex((media) => media.id === selectedId) || 0;
69
67
 
70
- const totalItems =
71
- (value?.externalVideoUrl ? 1 : 0) + (value?.medias?.length || 0);
68
+ const totalItems =
69
+ (value?.externalVideoUrl ? 1 : 0) + (value?.medias?.length || 0);
72
70
 
73
- // Calculate visible thumbnails (show 6 items)
74
- const visibleCount = 6;
75
- const halfVisible = Math.floor(visibleCount / 2);
76
- const startIndex = Math.max(
77
- 0,
78
- Math.min(selectedIndex - halfVisible, totalItems - visibleCount)
79
- );
80
- const endIndex = Math.min(startIndex + visibleCount, totalItems);
71
+ // Calculate visible thumbnails (show 6 items)
72
+ const visibleCount = 6;
73
+ const halfVisible = Math.floor(visibleCount / 2);
74
+ const startIndex = Math.max(
75
+ 0,
76
+ Math.min(selectedIndex - halfVisible, totalItems - visibleCount)
77
+ );
78
+ const endIndex = Math.min(startIndex + visibleCount, totalItems);
81
79
 
82
- const handlePrevious = useCallback(() => {
83
- if (selectedIndex > 0) {
84
- if (value?.externalVideoUrl) {
85
- if (selectedIndex === 1) {
86
- setSelectedId("video");
87
- } else {
88
- const prevIndex = selectedIndex - 2;
89
- setSelectedId(value.medias?.[prevIndex]?.id || "");
90
- }
80
+ const handlePrevious = useCallback(() => {
81
+ if (selectedIndex > 0) {
82
+ if (value?.externalVideoUrl) {
83
+ if (selectedIndex === 1) {
84
+ setSelectedId("video");
91
85
  } else {
92
- const prevIndex = selectedIndex - 1;
93
- setSelectedId(value?.medias?.[prevIndex]?.id || "");
86
+ const prevIndex = selectedIndex - 2;
87
+ setSelectedId(value.medias?.[prevIndex]?.id || "");
94
88
  }
89
+ } else {
90
+ const prevIndex = selectedIndex - 1;
91
+ setSelectedId(value?.medias?.[prevIndex]?.id || "");
95
92
  }
96
- }, [selectedIndex, value]);
93
+ }
94
+ }, [selectedIndex, value]);
97
95
 
98
- const handleNext = useCallback(() => {
99
- if (selectedIndex < totalItems - 1) {
100
- if (value?.externalVideoUrl) {
101
- if (selectedId === "video") {
102
- setSelectedId(value.medias?.[0]?.id || "");
103
- } else {
104
- const currentMediaIndex =
105
- value.medias?.findIndex((media) => media.id === selectedId) || 0;
106
- const nextIndex = currentMediaIndex + 1;
107
- setSelectedId(value.medias?.[nextIndex]?.id || "");
108
- }
96
+ const handleNext = useCallback(() => {
97
+ if (selectedIndex < totalItems - 1) {
98
+ if (value?.externalVideoUrl) {
99
+ if (selectedId === "video") {
100
+ setSelectedId(value.medias?.[0]?.id || "");
109
101
  } else {
110
102
  const currentMediaIndex =
111
- value?.medias?.findIndex((media) => media.id === selectedId) || 0;
103
+ value.medias?.findIndex((media) => media.id === selectedId) || 0;
112
104
  const nextIndex = currentMediaIndex + 1;
113
- setSelectedId(value?.medias?.[nextIndex]?.id || "");
105
+ setSelectedId(value.medias?.[nextIndex]?.id || "");
114
106
  }
107
+ } else {
108
+ const currentMediaIndex =
109
+ value?.medias?.findIndex((media) => media.id === selectedId) || 0;
110
+ const nextIndex = currentMediaIndex + 1;
111
+ setSelectedId(value?.medias?.[nextIndex]?.id || "");
115
112
  }
116
- }, [selectedIndex, totalItems, value, selectedId]);
113
+ }
114
+ }, [selectedIndex, totalItems, value, selectedId]);
117
115
 
118
- const handleKeyDown = useCallback(
119
- (e: KeyboardEvent) => {
120
- if (e.key === "ArrowLeft") {
121
- handlePrevious();
122
- } else if (e.key === "ArrowRight") {
123
- handleNext();
124
- }
125
- },
126
- [handleNext, handlePrevious]
127
- );
116
+ const handleKeyDown = useCallback(
117
+ (e: KeyboardEvent) => {
118
+ if (e.key === "ArrowLeft") {
119
+ handlePrevious();
120
+ } else if (e.key === "ArrowRight") {
121
+ handleNext();
122
+ }
123
+ },
124
+ [handleNext, handlePrevious]
125
+ );
128
126
 
129
- useEffect(() => {
130
- window.addEventListener("keydown", handleKeyDown);
131
- return () => window.removeEventListener("keydown", handleKeyDown);
132
- }, [handleKeyDown]);
127
+ useEffect(() => {
128
+ window.addEventListener("keydown", handleKeyDown);
129
+ return () => window.removeEventListener("keydown", handleKeyDown);
130
+ }, [handleKeyDown]);
133
131
 
134
- const canPrevious = selectedIndex > 0;
135
- const canNext = selectedIndex < totalItems - 1;
132
+ const canPrevious = selectedIndex > 0;
133
+ const canNext = selectedIndex < totalItems - 1;
134
+
135
+ return (
136
+ <div ref={ref} className={clsx("flex flex-col", className)} {...rest}>
137
+ {children}
138
+ {/* Main display area */}
139
+ <div className={clsx("relative group")}>
140
+ {canPrevious && (
141
+ <button
142
+ onClick={handlePrevious}
143
+ className="absolute left-4 top-1/2 transform -translate-y-1/2 z-10 bg-black/50 hover:bg-black/70 text-white rounded-full p-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200"
144
+ aria-label="Previous slide"
145
+ >
146
+ <svg
147
+ xmlns="http://www.w3.org/2000/svg"
148
+ className="h-6 w-6"
149
+ fill="none"
150
+ viewBox="0 0 24 24"
151
+ stroke="currentColor"
152
+ >
153
+ <path
154
+ strokeLinecap="round"
155
+ strokeLinejoin="round"
156
+ strokeWidth={2}
157
+ d="M15 19l-7-7 7-7"
158
+ />
159
+ </svg>
160
+ </button>
161
+ )}
162
+ {canNext && (
163
+ <button
164
+ onClick={handleNext}
165
+ className="absolute right-4 top-1/2 transform -translate-y-1/2 z-10 bg-black/50 hover:bg-black/70 text-white rounded-full p-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200"
166
+ aria-label="Next slide"
167
+ >
168
+ <svg
169
+ xmlns="http://www.w3.org/2000/svg"
170
+ className="h-6 w-6"
171
+ fill="none"
172
+ viewBox="0 0 24 24"
173
+ stroke="currentColor"
174
+ >
175
+ <path
176
+ strokeLinecap="round"
177
+ strokeLinejoin="round"
178
+ strokeWidth={2}
179
+ d="M9 5l7 7-7 7"
180
+ />
181
+ </svg>
182
+ </button>
183
+ )}
184
+ {value?.externalVideoUrl && selectedId === "video" && (
185
+ <div className={clsx("w-full rounded-md overflow-hidden", aspect)}>
186
+ <iframe
187
+ src={videoUrl}
188
+ className="w-full h-full"
189
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; autoplay"
190
+ referrerPolicy="strict-origin-when-cross-origin"
191
+ allowFullScreen
192
+ />
193
+ </div>
194
+ )}
195
+ {value?.medias?.map((media) => (
196
+ <div
197
+ key={media.id}
198
+ className={clsx(
199
+ "transition-opacity duration-300 overflow-hidden rounded-md",
200
+ media.id === selectedId ? "opacity-100" : "opacity-0 hidden",
201
+ aspect
202
+ )}
203
+ >
204
+ <img
205
+ src={media?.resize || media?.url}
206
+ alt={media?.alt}
207
+ className="w-full h-full object-cover object-center"
208
+ />
209
+ </div>
210
+ ))}
211
+ </div>
136
212
 
137
- return (
138
- <div ref={ref} className={clsx("flex flex-col", className)} {...rest}>
139
- {children}
140
- {/* Main display area */}
141
- <div className={clsx("relative group")}>
142
- {canPrevious && (
213
+ {/* Thumbnail navigation */}
214
+ <div className="relative mt-4">
215
+ <div className="flex items-stretch gap-2">
216
+ {totalItems > 6 && (
143
217
  <button
144
218
  onClick={handlePrevious}
145
- className="absolute left-4 top-1/2 transform -translate-y-1/2 z-10 bg-black/50 hover:bg-black/70 text-white rounded-full p-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200"
146
- aria-label="Previous slide"
219
+ disabled={!canPrevious}
220
+ className={clsx(
221
+ "flex items-center justify-center w-6",
222
+ "transition-colors duration-200 rounded-l-md",
223
+ !canPrevious
224
+ ? "bg-gray-100 text-gray-400 cursor-not-allowed"
225
+ : "bg-gray-200 hover:bg-gray-300 text-gray-700"
226
+ )}
227
+ aria-label="Previous"
147
228
  >
148
229
  <svg
149
230
  xmlns="http://www.w3.org/2000/svg"
150
- className="h-6 w-6"
231
+ className="h-4 w-4 md:h-5 md:w-5"
151
232
  fill="none"
152
233
  viewBox="0 0 24 24"
153
234
  stroke="currentColor"
@@ -161,15 +242,88 @@ export const Medias = forwardRef<HTMLDivElement, MediasProps>(
161
242
  </svg>
162
243
  </button>
163
244
  )}
164
- {canNext && (
245
+ <div className="flex-1">
246
+ <div className="grid grid-cols-6 gap-2">
247
+ {value?.externalVideoUrl && startIndex === 0 && (
248
+ <div
249
+ className={clsx(
250
+ "relative cursor-pointer overflow-hidden rounded-sm border-2",
251
+ thumbnailAspect,
252
+ selectedId === "video"
253
+ ? "border-primary-500"
254
+ : "border-transparent hover:border-primary-300"
255
+ )}
256
+ onClick={() => setSelectedId("video")}
257
+ >
258
+ <img
259
+ src={thumbnailUrl}
260
+ alt="Video thumbnail"
261
+ className="w-full h-full object-cover"
262
+ />
263
+ <div className="absolute inset-0 flex items-center justify-center bg-black/30">
264
+ <svg
265
+ xmlns="http://www.w3.org/2000/svg"
266
+ className="h-8 w-8 text-white"
267
+ viewBox="0 0 24 24"
268
+ >
269
+ <path
270
+ fill="currentColor"
271
+ fill-rule="evenodd"
272
+ d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2S2 6.477 2 12s4.477 10 10 10"
273
+ clipRule="evenodd"
274
+ opacity="0.5"
275
+ />
276
+ <path
277
+ fill="currentColor"
278
+ d="m15.414 13.059l-4.72 2.787C9.934 16.294 9 15.71 9 14.786V9.214c0-.924.934-1.507 1.694-1.059l4.72 2.787c.781.462.781 1.656 0 2.118"
279
+ />
280
+ </svg>
281
+ </div>
282
+ </div>
283
+ )}
284
+ {value?.medias?.map((media, index) => {
285
+ const adjustedIndex = value?.externalVideoUrl
286
+ ? index + 1
287
+ : index;
288
+ return adjustedIndex >= startIndex &&
289
+ adjustedIndex < endIndex ? (
290
+ <div
291
+ key={media.id}
292
+ className={clsx(
293
+ "relative cursor-pointer overflow-hidden rounded-sm border-2",
294
+ thumbnailAspect,
295
+ selectedId === media.id
296
+ ? "border-primary-500"
297
+ : "border-transparent hover:border-primary-300"
298
+ )}
299
+ onClick={() => setSelectedId(media.id)}
300
+ >
301
+ <img
302
+ src={media?.resize || media?.url}
303
+ alt={media?.alt}
304
+ className="w-full h-full object-cover"
305
+ />
306
+ </div>
307
+ ) : null;
308
+ })}
309
+ </div>
310
+ </div>
311
+ {totalItems > 6 && (
165
312
  <button
166
313
  onClick={handleNext}
167
- className="absolute right-4 top-1/2 transform -translate-y-1/2 z-10 bg-black/50 hover:bg-black/70 text-white rounded-full p-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200"
168
- aria-label="Next slide"
314
+ disabled={!canNext}
315
+ className={clsx(
316
+ "flex items-center justify-center w-6",
317
+ "transition-colors duration-200 rounded-r-md",
318
+ !canNext
319
+ ? "bg-gray-100 text-gray-400 cursor-not-allowed"
320
+ : "bg-gray-200 hover:bg-gray-300 text-gray-700"
321
+ )}
322
+ aria-label="Next"
169
323
  >
170
324
  <svg
171
325
  xmlns="http://www.w3.org/2000/svg"
172
- className="h-6 w-6"
326
+ className="h-4 w-4 md:h-5 md:w-5"
173
327
  fill="none"
174
328
  viewBox="0 0 24 24"
175
329
  stroke="currentColor"
@@ -183,165 +337,8 @@ export const Medias = forwardRef<HTMLDivElement, MediasProps>(
183
337
  </svg>
184
338
  </button>
185
339
  )}
186
- {value?.externalVideoUrl && selectedId === "video" && (
187
- <div className={clsx("w-full rounded-md overflow-hidden", aspect)}>
188
- <iframe
189
- src={videoUrl}
190
- className="w-full h-full"
191
- allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; autoplay"
192
- referrerPolicy="strict-origin-when-cross-origin"
193
- allowFullScreen
194
- />
195
- </div>
196
- )}
197
- {value?.medias?.map((media) => (
198
- <div
199
- key={media.id}
200
- className={clsx(
201
- "transition-opacity duration-300 overflow-hidden rounded-md",
202
- media.id === selectedId ? "opacity-100" : "opacity-0 hidden",
203
- aspect
204
- )}
205
- >
206
- <img
207
- src={media?.resize || media?.url}
208
- alt={media?.alt}
209
- className="w-full h-full object-cover object-center"
210
- />
211
- </div>
212
- ))}
213
- </div>
214
-
215
- {/* Thumbnail navigation */}
216
- <div className="relative mt-4">
217
- <div className="flex items-stretch gap-2">
218
- {totalItems > 6 && (
219
- <button
220
- onClick={handlePrevious}
221
- disabled={!canPrevious}
222
- className={clsx(
223
- "flex items-center justify-center w-6",
224
- "transition-colors duration-200 rounded-l-md",
225
- !canPrevious
226
- ? "bg-gray-100 text-gray-400 cursor-not-allowed"
227
- : "bg-gray-200 hover:bg-gray-300 text-gray-700"
228
- )}
229
- aria-label="Previous"
230
- >
231
- <svg
232
- xmlns="http://www.w3.org/2000/svg"
233
- className="h-4 w-4 md:h-5 md:w-5"
234
- fill="none"
235
- viewBox="0 0 24 24"
236
- stroke="currentColor"
237
- >
238
- <path
239
- strokeLinecap="round"
240
- strokeLinejoin="round"
241
- strokeWidth={2}
242
- d="M15 19l-7-7 7-7"
243
- />
244
- </svg>
245
- </button>
246
- )}
247
- <div className="flex-1">
248
- <div className="grid grid-cols-6 gap-2">
249
- {value?.externalVideoUrl && startIndex === 0 && (
250
- <div
251
- className={clsx(
252
- "relative cursor-pointer overflow-hidden rounded-sm border-2",
253
- aspect,
254
- selectedId === "video"
255
- ? "border-primary-500"
256
- : "border-transparent hover:border-primary-300"
257
- )}
258
- onClick={() => setSelectedId("video")}
259
- >
260
- <img
261
- src={thumbnailUrl}
262
- alt="Video thumbnail"
263
- className="w-full h-full object-cover"
264
- />
265
- <div className="absolute inset-0 flex items-center justify-center bg-black/30">
266
- <svg
267
- xmlns="http://www.w3.org/2000/svg"
268
- className="h-8 w-8 text-white"
269
- viewBox="0 0 24 24"
270
- >
271
- <path
272
- fill="currentColor"
273
- fill-rule="evenodd"
274
- d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2S2 6.477 2 12s4.477 10 10 10"
275
- clipRule="evenodd"
276
- opacity="0.5"
277
- />
278
- <path
279
- fill="currentColor"
280
- d="m15.414 13.059l-4.72 2.787C9.934 16.294 9 15.71 9 14.786V9.214c0-.924.934-1.507 1.694-1.059l4.72 2.787c.781.462.781 1.656 0 2.118"
281
- />
282
- </svg>
283
- </div>
284
- </div>
285
- )}
286
- {value?.medias?.map((media, index) => {
287
- const adjustedIndex = value?.externalVideoUrl
288
- ? index + 1
289
- : index;
290
- return adjustedIndex >= startIndex &&
291
- adjustedIndex < endIndex ? (
292
- <div
293
- key={media.id}
294
- className={clsx(
295
- "relative cursor-pointer overflow-hidden rounded-sm border-2",
296
- aspect,
297
- selectedId === media.id
298
- ? "border-primary-500"
299
- : "border-transparent hover:border-primary-300"
300
- )}
301
- onClick={() => setSelectedId(media.id)}
302
- >
303
- <img
304
- src={media?.resize || media?.url}
305
- alt={media?.alt}
306
- className="w-full h-full object-cover"
307
- />
308
- </div>
309
- ) : null;
310
- })}
311
- </div>
312
- </div>
313
- {totalItems > 6 && (
314
- <button
315
- onClick={handleNext}
316
- disabled={!canNext}
317
- className={clsx(
318
- "flex items-center justify-center w-6",
319
- "transition-colors duration-200 rounded-r-md",
320
- !canNext
321
- ? "bg-gray-100 text-gray-400 cursor-not-allowed"
322
- : "bg-gray-200 hover:bg-gray-300 text-gray-700"
323
- )}
324
- aria-label="Next"
325
- >
326
- <svg
327
- xmlns="http://www.w3.org/2000/svg"
328
- className="h-4 w-4 md:h-5 md:w-5"
329
- fill="none"
330
- viewBox="0 0 24 24"
331
- stroke="currentColor"
332
- >
333
- <path
334
- strokeLinecap="round"
335
- strokeLinejoin="round"
336
- strokeWidth={2}
337
- d="M9 5l7 7-7 7"
338
- />
339
- </svg>
340
- </button>
341
- )}
342
- </div>
343
340
  </div>
344
341
  </div>
345
- );
346
- }
347
- );
342
+ </div>
343
+ );
344
+ });
@@ -25,7 +25,7 @@ export function ProductCta(props: {
25
25
 
26
26
  const handleClick = () => {
27
27
  if (ref.current) {
28
- popover.open(roleProps[DATA_POPUP_CTA] || openableKey, ref.current);
28
+ popover.open(openableKey, ref.current);
29
29
  }
30
30
  };
31
31
 
@@ -1,5 +1,5 @@
1
1
  import { extractOutline, mdxToSlate } from "@rxdrag/slate-preview";
2
- import { forwardRef, ReactNode, useEffect } from "react";
2
+ import { forwardRef, useEffect } from "react";
3
3
  import { useAcitviedHeading } from "./useAcitviedHeading";
4
4
  import clsx from "clsx";
5
5
 
@@ -16,8 +16,8 @@ export const RichTextOutline = forwardRef<
16
16
  HTMLUListElement,
17
17
  RichTextOutlineProps
18
18
  >((props, ref) => {
19
- const { className, itemClassName, value, yOffset = 100, ...rest } = props;
20
- const activiedId = useAcitviedHeading();
19
+ const { className, itemClassName, value, yOffset = 200, ...rest } = props;
20
+ const activiedId = useAcitviedHeading(yOffset);
21
21
  const nodes = mdxToSlate(value ?? "");
22
22
  const outline = extractOutline(nodes ?? []);
23
23
 
@@ -38,7 +38,7 @@ export const RichTextOutline = forwardRef<
38
38
  return () => {
39
39
  window.removeEventListener("hashchange", handleHashChange);
40
40
  };
41
- }, []);
41
+ }, [yOffset]);
42
42
 
43
43
  return outline?.length ? (
44
44
  <ul ref={ref} className={className} {...rest}>
@@ -57,7 +57,6 @@ export const RichTextOutline = forwardRef<
57
57
  e.preventDefault();
58
58
  const element = document.getElementById(item?.key);
59
59
  if (element) {
60
- const yOffset = -100; // 偏移量
61
60
  const y =
62
61
  element.getBoundingClientRect().top +
63
62
  window.scrollY +