@rxdrag/website-lib-core 0.0.126 → 0.0.128

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 (138) hide show
  1. package/README.md +1 -1
  2. package/index.ts +1 -1
  3. package/package.json +2 -2
  4. package/src/astro/animation.ts +146 -146
  5. package/src/astro/background.ts +82 -53
  6. package/src/astro/base.ts +7 -0
  7. package/src/astro/business.ts +13 -0
  8. package/src/astro/grid/consts.ts +80 -80
  9. package/src/astro/grid/index.ts +2 -2
  10. package/src/astro/grid/types.ts +35 -35
  11. package/src/astro/image.ts +239 -239
  12. package/src/astro/index.ts +12 -9
  13. package/src/astro/link.ts +20 -0
  14. package/src/astro/media.ts +123 -123
  15. package/src/astro/nav.ts +13 -13
  16. package/src/astro/section/index.ts +7 -12
  17. package/src/component-logic/index.ts +1 -1
  18. package/src/component-logic/link-client.ts +32 -32
  19. package/src/component-logic/link.ts +61 -61
  20. package/src/design-tokens.ts +160 -160
  21. package/src/entify/Entify.ts +533 -530
  22. package/src/entify/IEntify.ts +151 -177
  23. package/src/entify/index.ts +4 -4
  24. package/src/entify/lib/collectCategoryIds.ts +20 -20
  25. package/src/entify/lib/fulltextSearch.ts +63 -62
  26. package/src/entify/lib/langFields.ts +14 -14
  27. package/src/entify/lib/listToTree.ts +23 -23
  28. package/src/entify/lib/newAvatarQueryOptions.ts +4 -4
  29. package/src/entify/lib/newOgImageQueryOptions.ts +5 -5
  30. package/src/entify/lib/newQueryPostOptions.ts +42 -45
  31. package/src/entify/lib/newQueryProductOptions.ts +96 -98
  32. package/src/entify/lib/newQueryProductsMediaOptions.ts +28 -28
  33. package/src/entify/lib/queryAllProducts.ts +40 -40
  34. package/src/entify/lib/queryBulletin.ts +28 -16
  35. package/src/entify/lib/queryEntityList.ts +41 -41
  36. package/src/entify/lib/queryFeaturedProducts.ts +69 -68
  37. package/src/entify/lib/queryLangs.ts +36 -60
  38. package/src/entify/lib/queryLatestPosts.ts +92 -91
  39. package/src/entify/lib/queryOneEntity.ts +63 -63
  40. package/src/entify/lib/queryOneMedia.ts +27 -27
  41. package/src/entify/lib/queryOnePostById.ts +21 -21
  42. package/src/entify/lib/queryOnePostBySlug.ts +40 -39
  43. package/src/entify/lib/queryOnePostCategoryBySlug.ts +35 -35
  44. package/src/entify/lib/queryOneProductById.ts +20 -20
  45. package/src/entify/lib/queryOneProductBySlug.ts +53 -52
  46. package/src/entify/lib/queryOneProductCategoryBySlug.ts +50 -49
  47. package/src/entify/lib/queryOneTheme.ts +54 -72
  48. package/src/entify/lib/queryOneUser.ts +38 -38
  49. package/src/entify/lib/queryPostCategories.ts +67 -67
  50. package/src/entify/lib/queryPostSlugs.ts +37 -37
  51. package/src/entify/lib/queryPosts.ts +175 -174
  52. package/src/entify/lib/queryProductCategories.ts +59 -59
  53. package/src/entify/lib/queryProducts.ts +145 -144
  54. package/src/entify/lib/queryProductsInMenu.ts +45 -45
  55. package/src/entify/lib/queryTagCategories.ts +58 -58
  56. package/src/entify/lib/queryTags.ts +57 -57
  57. package/src/entify/lib/queryUserIds.ts +24 -24
  58. package/src/entify/lib/queryUserPosts.ts +80 -79
  59. package/src/entify/lib/queryWebSiteSettings.ts +28 -28
  60. package/src/entify/lib/queryWebsite.ts +43 -43
  61. package/src/entify/lib/sendEmail.ts +7 -7
  62. package/src/entify/lib/toQueryOptions.ts +19 -19
  63. package/src/entify/lib/upsertEntity.ts +8 -8
  64. package/src/entify/types/index.ts +1 -1
  65. package/src/entify/types/utils.ts +11 -6
  66. package/src/entify/types/variables.ts +5 -6
  67. package/src/entify/view-model/funcs.ts +230 -230
  68. package/src/entify/view-model/index.ts +1 -1
  69. package/src/entify/view-model/models.ts +135 -135
  70. package/src/global.d.ts +7 -7
  71. package/src/index.ts +8 -8
  72. package/src/lib/formatDate.ts +15 -15
  73. package/src/lib/index.ts +3 -3
  74. package/src/lib/pagination.ts +114 -114
  75. package/src/lib/utils.ts +135 -135
  76. package/src/react/components/Analytics/eventHandlers.ts +173 -173
  77. package/src/react/components/Analytics/index.tsx +21 -21
  78. package/src/react/components/Analytics/singleton.ts +214 -214
  79. package/src/react/components/Analytics/tracking.ts +221 -221
  80. package/src/react/components/Analytics/types.ts +60 -60
  81. package/src/react/components/Analytics/utils.ts +95 -95
  82. package/src/react/components/AttachmentIcon/index.tsx +53 -53
  83. package/src/react/components/BackgroundHlsVideoPlayer.tsx +97 -97
  84. package/src/react/components/BackgroundVideoPlayer.tsx +32 -32
  85. package/src/react/components/Bulletin.tsx +30 -30
  86. package/src/react/components/ContactForm/ContactForm.tsx +289 -289
  87. package/src/react/components/ContactForm/Input.tsx +48 -48
  88. package/src/react/components/ContactForm/Input2.tsx +59 -59
  89. package/src/react/components/ContactForm/Submit.tsx +48 -48
  90. package/src/react/components/ContactForm/TelInput.tsx +215 -215
  91. package/src/react/components/ContactForm/Textarea.tsx +48 -48
  92. package/src/react/components/ContactForm/Textarea2.tsx +89 -89
  93. package/src/react/components/ContactForm/funcs.ts +64 -64
  94. package/src/react/components/ContactForm/index.ts +7 -7
  95. package/src/react/components/ContactForm/types.ts +68 -68
  96. package/src/react/components/GoogleConsent/CookieItemPanel.tsx +80 -80
  97. package/src/react/components/GoogleConsent/CumtomizedModal.tsx +148 -148
  98. package/src/react/components/GoogleConsent/GoogleConsent.tsx +100 -100
  99. package/src/react/components/GoogleConsent/gtags.ts +67 -67
  100. package/src/react/components/GoogleConsent/index.ts +2 -2
  101. package/src/react/components/GoogleConsent/types.ts +18 -18
  102. package/src/react/components/GoogleConsent//345/217/202/350/200/203.md +4 -4
  103. package/src/react/components/Icon/index.tsx +19 -19
  104. package/src/react/components/Medias/MainMedia.tsx +257 -257
  105. package/src/react/components/Medias/Thumbnail.tsx +62 -62
  106. package/src/react/components/Medias/VideoPlayer.tsx +114 -114
  107. package/src/react/components/Medias/index.tsx +271 -271
  108. package/src/react/components/ProductCard/ProductCard.tsx +24 -24
  109. package/src/react/components/ProductCard/ProductCta/index.tsx +28 -28
  110. package/src/react/components/ProductCard/ProductCta/style.css +3 -3
  111. package/src/react/components/ProductCard/ProductDescription/index.tsx +12 -12
  112. package/src/react/components/ProductCard/ProductDescription/style.css +5 -5
  113. package/src/react/components/ProductCard/ProductMedia/index.tsx +35 -35
  114. package/src/react/components/ProductCard/ProductMedia/style.css +5 -5
  115. package/src/react/components/ProductCard/ProductTitle/index.tsx +7 -7
  116. package/src/react/components/ProductCard/ProductTitle/style.css +3 -3
  117. package/src/react/components/ProductCard/ProductView.tsx +36 -36
  118. package/src/react/components/ProductCard/index.ts +4 -4
  119. package/src/react/components/ProductCard/useQueryProduct.ts +32 -32
  120. package/src/react/components/ReactModalTrigger.tsx +28 -28
  121. package/src/react/components/ReactVideoPlayer.tsx +29 -52
  122. package/src/react/components/RichTextOutline/index.tsx +75 -75
  123. package/src/react/components/RichTextOutline/useAnchorScroll.ts +23 -23
  124. package/src/react/components/Scroller.tsx +39 -39
  125. package/src/react/components/SearchInput.tsx +21 -21
  126. package/src/react/components/Share/index.tsx +86 -86
  127. package/src/react/components/Share/socials.tsx +79 -77
  128. package/src/react/components/Share//350/265/204/346/226/231.md +7 -7
  129. package/src/react/components/ToTop.tsx +72 -72
  130. package/src/react/components/VideoPlayIcon.tsx +43 -0
  131. package/src/react/components/all.ts +38 -38
  132. package/src/react/components/index.ts +16 -16
  133. package/src/robots.ts +4 -4
  134. package/src/entify/lib/newPageMetaOptions.ts +0 -18
  135. package/src/entify/lib/newQueryPageOptions.ts +0 -14
  136. package/src/entify/lib/queryOneIcon.ts +0 -27
  137. package/src/entify/lib/queryPageBySlug.ts +0 -43
  138. package/src/entify/lib/queryPageByType.ts +0 -44
@@ -1,271 +1,271 @@
1
- import clsx from "clsx";
2
- import { forwardRef, useEffect, useState, useCallback } from "react";
3
- import { TMedia } from "../../../entify";
4
- import { MainMedia } from "./MainMedia";
5
- import { Thumbnail } from "./Thumbnail";
6
-
7
- export type MediasProps = {
8
- value?: TMedia[];
9
- className?: string;
10
- children?: React.ReactNode;
11
- // Aspect ratio, format is `aspect-[width/height]`
12
- aspect?: string;
13
- thumbnailAspect?: string;
14
- enableZoom?: boolean;
15
- thumbnailPosition?: "bottom" | "left";
16
- visibleCount?: number;
17
- classNames?: {
18
- mainArea?: string;
19
- navigation?: string;
20
- thumbnail?: string;
21
- thumbnailImage?: string;
22
- arrowButton?: string;
23
- arrowIcon?: string;
24
- playButton?: string;
25
- navigationInner?: string;
26
- };
27
- };
28
-
29
- export const Medias = forwardRef<HTMLDivElement, MediasProps>((props, ref) => {
30
- const {
31
- value,
32
- className,
33
- children,
34
- aspect = "aspect-[1/1]",
35
- thumbnailAspect = "aspect-[1/1]",
36
- enableZoom = true,
37
- thumbnailPosition = "bottom",
38
- visibleCount = 6,
39
- classNames,
40
- ...rest
41
- } = props;
42
- const {
43
- mainArea,
44
- navigation,
45
- thumbnail,
46
- thumbnailImage,
47
- arrowButton,
48
- arrowIcon,
49
- playButton,
50
- navigationInner,
51
- } = classNames || {};
52
- const [selectedId, setSelectedId] = useState<string | undefined | null>(
53
- value?.[0]?.id || ""
54
- );
55
-
56
- useEffect(() => {
57
- setSelectedId(value?.[0]?.id || "");
58
- }, [value]);
59
-
60
- const selectedIndex =
61
- value?.findIndex((media) => media.id === selectedId) || 0;
62
-
63
- const totalItems = value?.length || 0;
64
-
65
- const handlePrevious = useCallback(() => {
66
- if (selectedIndex > 0) {
67
- const prevIndex = selectedIndex - 1;
68
- setSelectedId(value?.[prevIndex]?.id || "");
69
- }
70
- }, [selectedIndex, value]);
71
-
72
- const handleNext = useCallback(() => {
73
- if (selectedIndex < totalItems - 1) {
74
- const currentMediaIndex =
75
- value?.findIndex((media) => media.id === selectedId) || 0;
76
- const nextIndex = currentMediaIndex + 1;
77
- setSelectedId(value?.[nextIndex]?.id || "");
78
- }
79
- }, [selectedIndex, totalItems, value, selectedId]);
80
-
81
- const handleKeyDown = useCallback(
82
- (e: KeyboardEvent) => {
83
- if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
84
- handlePrevious();
85
- } else if (e.key === "ArrowRight" || e.key === "ArrowDown") {
86
- handleNext();
87
- }
88
- },
89
- [handleNext, handlePrevious]
90
- );
91
-
92
- useEffect(() => {
93
- window.addEventListener("keydown", handleKeyDown);
94
- return () => window.removeEventListener("keydown", handleKeyDown);
95
- }, [handleKeyDown]);
96
-
97
- const canPrevious = selectedIndex > 0;
98
- const canNext = selectedIndex < totalItems - 1;
99
-
100
- const isVerticalThumbs = thumbnailPosition === "left";
101
-
102
- const actualVisibleCount = visibleCount;
103
- const halfVisible = Math.floor(actualVisibleCount / 2);
104
- const startIndex = Math.max(
105
- 0,
106
- Math.min(selectedIndex - halfVisible, totalItems - actualVisibleCount)
107
- );
108
- const endIndex = Math.min(startIndex + actualVisibleCount, totalItems);
109
-
110
- return (
111
- <div
112
- ref={ref}
113
- className={clsx(
114
- "flex gap-4",
115
- isVerticalThumbs ? "flex-col md:flex-row-reverse" : "flex-col",
116
- className
117
- )}
118
- {...rest}
119
- >
120
- {children}
121
- {/* Main display area */}
122
- <MainMedia
123
- value={value}
124
- selectedId={selectedId}
125
- aspect={aspect}
126
- enableZoom={enableZoom}
127
- className={clsx(
128
- isVerticalThumbs ? "w-full md:flex-1" : "w-full",
129
- mainArea
130
- )}
131
- arrowButtonClass={arrowButton}
132
- arrowIconClass={arrowIcon}
133
- playButtonClass={playButton}
134
- onPrevious={handlePrevious}
135
- onNext={handleNext}
136
- canPrevious={canPrevious}
137
- canNext={canNext}
138
- />
139
-
140
- {/* Thumbnail navigation */}
141
- {totalItems > 1 && (
142
- <div
143
- className={clsx(
144
- "relative group/thumbs",
145
- isVerticalThumbs
146
- ? "w-full mt-4 md:w-24 md:h-full md:px-1"
147
- : "w-full mt-4",
148
- navigation
149
- )}
150
- >
151
- {/* Previous Arrow */}
152
- {totalItems > actualVisibleCount && (
153
- <button
154
- onClick={handlePrevious}
155
- disabled={!canPrevious}
156
- className={clsx(
157
- "absolute z-10 w-8 h-8 flex items-center justify-center rounded-full",
158
- "bg-white/60 dark:bg-black/50 shadow-sm backdrop-blur-sm",
159
- "text-gray-700 dark:text-gray-200 transition-all duration-200",
160
- isVerticalThumbs
161
- ? "left-2 top-1/2 -translate-y-1/2 md:top-2 md:left-1/2 md:-translate-x-1/2"
162
- : "left-2 top-1/2 -translate-y-1/2",
163
- !canPrevious
164
- ? "opacity-0 pointer-events-none"
165
- : "opacity-0 group-hover/thumbs:opacity-100 hover:bg-white/90 dark:hover:bg-black/80",
166
- arrowButton
167
- )}
168
- aria-label="Previous"
169
- >
170
- <svg
171
- xmlns="http://www.w3.org/2000/svg"
172
- className={clsx(
173
- "h-4 w-4",
174
- isVerticalThumbs && "md:rotate-90",
175
- arrowIcon
176
- )}
177
- fill="none"
178
- viewBox="0 0 24 24"
179
- stroke="currentColor"
180
- >
181
- <path
182
- strokeLinecap="round"
183
- strokeLinejoin="round"
184
- strokeWidth={2}
185
- d="M15 19l-7-7 7-7"
186
- />
187
- </svg>
188
- </button>
189
- )}
190
-
191
- <div
192
- className={clsx(
193
- "flex-1",
194
- isVerticalThumbs
195
- ? "w-full px-0.5 md:h-full md:py-0.5"
196
- : "w-full px-0.5"
197
- )}
198
- >
199
- <div
200
- className={clsx(
201
- "gap-2 md:gap-3",
202
- navigationInner,
203
- isVerticalThumbs ? "grid md:flex md:flex-col" : "grid"
204
- )}
205
- style={{
206
- gridTemplateColumns: `repeat(${actualVisibleCount}, minmax(0, 1fr))`,
207
- }}
208
- >
209
- {value?.map((media, index) => {
210
- if (index < startIndex || index >= endIndex) return null;
211
- const isSelected = selectedId === media.id;
212
- return (
213
- <Thumbnail
214
- key={media.id}
215
- media={media}
216
- isSelected={isSelected}
217
- onClick={() => setSelectedId(media.id)}
218
- aspect={thumbnailAspect}
219
- className={thumbnail}
220
- imageClass={thumbnailImage}
221
- playButtonClass={playButton}
222
- />
223
- );
224
- })}
225
- </div>
226
- </div>
227
-
228
- {/* Next Arrow */}
229
- {totalItems > actualVisibleCount && (
230
- <button
231
- onClick={handleNext}
232
- disabled={!canNext}
233
- className={clsx(
234
- "absolute z-10 w-8 h-8 flex items-center justify-center rounded-full",
235
- "bg-white/60 dark:bg-black/50 shadow-sm backdrop-blur-sm",
236
- "text-gray-700 dark:text-gray-200 transition-all duration-200",
237
- isVerticalThumbs
238
- ? "right-2 top-1/2 -translate-y-1/2 md:bottom-2 md:left-1/2 md:-translate-x-1/2"
239
- : "right-2 top-1/2 -translate-y-1/2",
240
- !canNext
241
- ? "opacity-0 pointer-events-none"
242
- : "opacity-0 group-hover/thumbs:opacity-100 hover:bg-white/90 dark:hover:bg-black/80",
243
- arrowButton
244
- )}
245
- aria-label="Next"
246
- >
247
- <svg
248
- xmlns="http://www.w3.org/2000/svg"
249
- className={clsx(
250
- "h-4 w-4",
251
- isVerticalThumbs && "md:rotate-90",
252
- arrowIcon
253
- )}
254
- fill="none"
255
- viewBox="0 0 24 24"
256
- stroke="currentColor"
257
- >
258
- <path
259
- strokeLinecap="round"
260
- strokeLinejoin="round"
261
- strokeWidth={2}
262
- d="M9 5l7 7-7 7"
263
- />
264
- </svg>
265
- </button>
266
- )}
267
- </div>
268
- )}
269
- </div>
270
- );
271
- });
1
+ import clsx from "clsx";
2
+ import { forwardRef, useEffect, useState, useCallback } from "react";
3
+ import { TMedia } from "../../../entify";
4
+ import { MainMedia } from "./MainMedia";
5
+ import { Thumbnail } from "./Thumbnail";
6
+
7
+ export type MediasProps = {
8
+ value?: TMedia[];
9
+ className?: string;
10
+ children?: React.ReactNode;
11
+ // Aspect ratio, format is `aspect-[width/height]`
12
+ aspect?: string;
13
+ thumbnailAspect?: string;
14
+ enableZoom?: boolean;
15
+ thumbnailPosition?: "bottom" | "left";
16
+ visibleCount?: number;
17
+ classNames?: {
18
+ mainArea?: string;
19
+ navigation?: string;
20
+ thumbnail?: string;
21
+ thumbnailImage?: string;
22
+ arrowButton?: string;
23
+ arrowIcon?: string;
24
+ playButton?: string;
25
+ navigationInner?: string;
26
+ };
27
+ };
28
+
29
+ export const Medias = forwardRef<HTMLDivElement, MediasProps>((props, ref) => {
30
+ const {
31
+ value,
32
+ className,
33
+ children,
34
+ aspect = "aspect-[1/1]",
35
+ thumbnailAspect = "aspect-[1/1]",
36
+ enableZoom = true,
37
+ thumbnailPosition = "bottom",
38
+ visibleCount = 6,
39
+ classNames,
40
+ ...rest
41
+ } = props;
42
+ const {
43
+ mainArea,
44
+ navigation,
45
+ thumbnail,
46
+ thumbnailImage,
47
+ arrowButton,
48
+ arrowIcon,
49
+ playButton,
50
+ navigationInner,
51
+ } = classNames || {};
52
+ const [selectedId, setSelectedId] = useState<string | undefined | null>(
53
+ value?.[0]?.id || ""
54
+ );
55
+
56
+ useEffect(() => {
57
+ setSelectedId(value?.[0]?.id || "");
58
+ }, [value]);
59
+
60
+ const selectedIndex =
61
+ value?.findIndex((media) => media.id === selectedId) || 0;
62
+
63
+ const totalItems = value?.length || 0;
64
+
65
+ const handlePrevious = useCallback(() => {
66
+ if (selectedIndex > 0) {
67
+ const prevIndex = selectedIndex - 1;
68
+ setSelectedId(value?.[prevIndex]?.id || "");
69
+ }
70
+ }, [selectedIndex, value]);
71
+
72
+ const handleNext = useCallback(() => {
73
+ if (selectedIndex < totalItems - 1) {
74
+ const currentMediaIndex =
75
+ value?.findIndex((media) => media.id === selectedId) || 0;
76
+ const nextIndex = currentMediaIndex + 1;
77
+ setSelectedId(value?.[nextIndex]?.id || "");
78
+ }
79
+ }, [selectedIndex, totalItems, value, selectedId]);
80
+
81
+ const handleKeyDown = useCallback(
82
+ (e: KeyboardEvent) => {
83
+ if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
84
+ handlePrevious();
85
+ } else if (e.key === "ArrowRight" || e.key === "ArrowDown") {
86
+ handleNext();
87
+ }
88
+ },
89
+ [handleNext, handlePrevious]
90
+ );
91
+
92
+ useEffect(() => {
93
+ window.addEventListener("keydown", handleKeyDown);
94
+ return () => window.removeEventListener("keydown", handleKeyDown);
95
+ }, [handleKeyDown]);
96
+
97
+ const canPrevious = selectedIndex > 0;
98
+ const canNext = selectedIndex < totalItems - 1;
99
+
100
+ const isVerticalThumbs = thumbnailPosition === "left";
101
+
102
+ const actualVisibleCount = visibleCount;
103
+ const halfVisible = Math.floor(actualVisibleCount / 2);
104
+ const startIndex = Math.max(
105
+ 0,
106
+ Math.min(selectedIndex - halfVisible, totalItems - actualVisibleCount)
107
+ );
108
+ const endIndex = Math.min(startIndex + actualVisibleCount, totalItems);
109
+
110
+ return (
111
+ <div
112
+ ref={ref}
113
+ className={clsx(
114
+ "flex gap-4",
115
+ isVerticalThumbs ? "flex-col md:flex-row-reverse" : "flex-col",
116
+ className
117
+ )}
118
+ {...rest}
119
+ >
120
+ {children}
121
+ {/* Main display area */}
122
+ <MainMedia
123
+ value={value}
124
+ selectedId={selectedId}
125
+ aspect={aspect}
126
+ enableZoom={enableZoom}
127
+ className={clsx(
128
+ isVerticalThumbs ? "w-full md:flex-1" : "w-full",
129
+ mainArea
130
+ )}
131
+ arrowButtonClass={arrowButton}
132
+ arrowIconClass={arrowIcon}
133
+ playButtonClass={playButton}
134
+ onPrevious={handlePrevious}
135
+ onNext={handleNext}
136
+ canPrevious={canPrevious}
137
+ canNext={canNext}
138
+ />
139
+
140
+ {/* Thumbnail navigation */}
141
+ {totalItems > 1 && (
142
+ <div
143
+ className={clsx(
144
+ "relative group/thumbs",
145
+ isVerticalThumbs
146
+ ? "w-full mt-4 md:w-24 md:h-full md:px-1"
147
+ : "w-full mt-4",
148
+ navigation
149
+ )}
150
+ >
151
+ {/* Previous Arrow */}
152
+ {totalItems > actualVisibleCount && (
153
+ <button
154
+ onClick={handlePrevious}
155
+ disabled={!canPrevious}
156
+ className={clsx(
157
+ "absolute z-10 w-8 h-8 flex items-center justify-center rounded-full",
158
+ "bg-white/60 dark:bg-black/50 shadow-sm backdrop-blur-sm",
159
+ "text-gray-700 dark:text-gray-200 transition-all duration-200",
160
+ isVerticalThumbs
161
+ ? "left-2 top-1/2 -translate-y-1/2 md:top-2 md:left-1/2 md:-translate-x-1/2"
162
+ : "left-2 top-1/2 -translate-y-1/2",
163
+ !canPrevious
164
+ ? "opacity-0 pointer-events-none"
165
+ : "opacity-0 group-hover/thumbs:opacity-100 hover:bg-white/90 dark:hover:bg-black/80",
166
+ arrowButton
167
+ )}
168
+ aria-label="Previous"
169
+ >
170
+ <svg
171
+ xmlns="http://www.w3.org/2000/svg"
172
+ className={clsx(
173
+ "h-4 w-4",
174
+ isVerticalThumbs && "md:rotate-90",
175
+ arrowIcon
176
+ )}
177
+ fill="none"
178
+ viewBox="0 0 24 24"
179
+ stroke="currentColor"
180
+ >
181
+ <path
182
+ strokeLinecap="round"
183
+ strokeLinejoin="round"
184
+ strokeWidth={2}
185
+ d="M15 19l-7-7 7-7"
186
+ />
187
+ </svg>
188
+ </button>
189
+ )}
190
+
191
+ <div
192
+ className={clsx(
193
+ "flex-1",
194
+ isVerticalThumbs
195
+ ? "w-full px-0.5 md:h-full md:py-0.5"
196
+ : "w-full px-0.5"
197
+ )}
198
+ >
199
+ <div
200
+ className={clsx(
201
+ "gap-2 md:gap-3",
202
+ navigationInner,
203
+ isVerticalThumbs ? "grid md:flex md:flex-col" : "grid"
204
+ )}
205
+ style={{
206
+ gridTemplateColumns: `repeat(${actualVisibleCount}, minmax(0, 1fr))`,
207
+ }}
208
+ >
209
+ {value?.map((media, index) => {
210
+ if (index < startIndex || index >= endIndex) return null;
211
+ const isSelected = selectedId === media.id;
212
+ return (
213
+ <Thumbnail
214
+ key={media.id}
215
+ media={media}
216
+ isSelected={isSelected}
217
+ onClick={() => setSelectedId(media.id)}
218
+ aspect={thumbnailAspect}
219
+ className={thumbnail}
220
+ imageClass={thumbnailImage}
221
+ playButtonClass={playButton}
222
+ />
223
+ );
224
+ })}
225
+ </div>
226
+ </div>
227
+
228
+ {/* Next Arrow */}
229
+ {totalItems > actualVisibleCount && (
230
+ <button
231
+ onClick={handleNext}
232
+ disabled={!canNext}
233
+ className={clsx(
234
+ "absolute z-10 w-8 h-8 flex items-center justify-center rounded-full",
235
+ "bg-white/60 dark:bg-black/50 shadow-sm backdrop-blur-sm",
236
+ "text-gray-700 dark:text-gray-200 transition-all duration-200",
237
+ isVerticalThumbs
238
+ ? "right-2 top-1/2 -translate-y-1/2 md:bottom-2 md:left-1/2 md:-translate-x-1/2"
239
+ : "right-2 top-1/2 -translate-y-1/2",
240
+ !canNext
241
+ ? "opacity-0 pointer-events-none"
242
+ : "opacity-0 group-hover/thumbs:opacity-100 hover:bg-white/90 dark:hover:bg-black/80",
243
+ arrowButton
244
+ )}
245
+ aria-label="Next"
246
+ >
247
+ <svg
248
+ xmlns="http://www.w3.org/2000/svg"
249
+ className={clsx(
250
+ "h-4 w-4",
251
+ isVerticalThumbs && "md:rotate-90",
252
+ arrowIcon
253
+ )}
254
+ fill="none"
255
+ viewBox="0 0 24 24"
256
+ stroke="currentColor"
257
+ >
258
+ <path
259
+ strokeLinecap="round"
260
+ strokeLinejoin="round"
261
+ strokeWidth={2}
262
+ d="M9 5l7 7-7 7"
263
+ />
264
+ </svg>
265
+ </button>
266
+ )}
267
+ </div>
268
+ )}
269
+ </div>
270
+ );
271
+ });
@@ -1,24 +1,24 @@
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
+ 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 +1,28 @@
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
+ 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 +1,4 @@
1
- .product-cta-button {
2
- width: 100%;
3
- padding: 8px 16px;
1
+ .product-cta-button {
2
+ width: 100%;
3
+ padding: 8px 16px;
4
4
  }