@xhub-short/ui 0.1.0-beta.1 → 0.1.0-beta.11

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 (76) hide show
  1. package/dist/CommentSheet.css-DyEc3Sro.d.ts +217 -0
  2. package/dist/VideoSlotPlayIndicator-DPs8Xt5C.d.ts +51 -0
  3. package/dist/chunk-3OB3OVYR.js +349 -0
  4. package/dist/{chunk-WKX2WBVO.js → chunk-3XPJHUYL.js} +1 -39
  5. package/dist/chunk-4RIMQOBR.js +58 -0
  6. package/dist/chunk-4TUBNA2X.js +180 -0
  7. package/dist/{chunk-4YDIRPIN.js → chunk-ANCP53F3.js} +3 -3
  8. package/dist/{chunk-UXMA4KJZ.js → chunk-CAWE42LH.js} +5 -3
  9. package/dist/{chunk-ANGBSV7L.js → chunk-CIIZ3IHV.js} +10 -5
  10. package/dist/chunk-DR7KR7OT.js +103 -0
  11. package/dist/chunk-DXLCQ4FH.js +102 -0
  12. package/dist/chunk-EDWS2IPH.js +1 -0
  13. package/dist/chunk-FR7UQSZP.js +570 -0
  14. package/dist/chunk-IWSBYOSS.js +91 -0
  15. package/dist/chunk-JEY6R4KJ.js +334 -0
  16. package/dist/chunk-KMJ3PQ7M.js +1262 -0
  17. package/dist/chunk-MFJS65C5.js +368 -0
  18. package/dist/{chunk-HW4LXTFT.js → chunk-OM4L7RE5.js} +18 -6
  19. package/dist/chunk-PBIH2F2Q.js +344 -0
  20. package/dist/chunk-PJ4NMVMY.js +326 -0
  21. package/dist/chunk-Q6MG7AVG.js +531 -0
  22. package/dist/chunk-QCKVF2DR.js +713 -0
  23. package/dist/chunk-QCRRF76W.js +75 -0
  24. package/dist/chunk-QUEJHA24.js +508 -0
  25. package/dist/chunk-VXW7AOGM.js +285 -0
  26. package/dist/chunk-YB7AXTX7.js +430 -0
  27. package/dist/chunk-ZGWSJ6Z5.js +601 -0
  28. package/dist/components/ActionBar/index.js +1 -1
  29. package/dist/components/AdvanceMenu/index.d.ts +78 -0
  30. package/dist/components/AdvanceMenu/index.js +1 -0
  31. package/dist/components/AuthorInfo/index.d.ts +5 -1
  32. package/dist/components/AuthorInfo/index.js +1 -1
  33. package/dist/components/BottomSheet/index.d.ts +82 -0
  34. package/dist/components/BottomSheet/index.js +1 -0
  35. package/dist/components/CleanModeOverlay/index.d.ts +60 -0
  36. package/dist/components/CleanModeOverlay/index.js +1 -0
  37. package/dist/components/CommentSheet/index.d.ts +164 -0
  38. package/dist/components/CommentSheet/index.js +1 -0
  39. package/dist/components/DetailView/index.d.ts +311 -0
  40. package/dist/components/DetailView/index.js +1 -0
  41. package/dist/components/ErrorBoundary/index.js +1 -1
  42. package/dist/components/ImageCarousel/index.d.ts +50 -0
  43. package/dist/components/ImageCarousel/index.js +1 -0
  44. package/dist/components/ImagePostSlot/index.d.ts +207 -0
  45. package/dist/components/ImagePostSlot/index.js +1 -0
  46. package/dist/components/ProgressBar/index.d.ts +30 -2
  47. package/dist/components/ProgressBar/index.js +1 -1
  48. package/dist/components/QualityPicker/index.d.ts +35 -0
  49. package/dist/components/QualityPicker/index.js +1 -0
  50. package/dist/components/ReportSheet/index.d.ts +68 -0
  51. package/dist/components/ReportSheet/index.js +1 -0
  52. package/dist/components/Skeleton/index.js +1 -1
  53. package/dist/components/SpeedPicker/index.d.ts +32 -0
  54. package/dist/components/SpeedPicker/index.js +1 -0
  55. package/dist/components/VideoFeed/index.d.ts +12 -1
  56. package/dist/components/VideoFeed/index.js +1 -1
  57. package/dist/components/VideoInfo/index.d.ts +4 -2
  58. package/dist/components/VideoInfo/index.js +1 -1
  59. package/dist/components/VideoPlayer/index.d.ts +14 -41
  60. package/dist/components/VideoPlayer/index.js +1 -1
  61. package/dist/components/VideoSlot/index.d.ts +84 -65
  62. package/dist/components/VideoSlot/index.js +2 -1
  63. package/dist/components/VirtualSlider/index.d.ts +339 -0
  64. package/dist/components/VirtualSlider/index.js +1 -0
  65. package/dist/components/icons/index.js +1 -1
  66. package/dist/index.d.ts +107 -95
  67. package/dist/index.js +84 -27
  68. package/package.json +51 -7
  69. package/dist/chunk-2PTMP65P.js +0 -738
  70. package/dist/chunk-4MN72OZH.js +0 -148
  71. package/dist/chunk-DHQJBXQW.js +0 -562
  72. package/dist/chunk-SSJDO24Q.js +0 -204
  73. package/dist/chunk-XAOEHLOX.js +0 -1326
  74. package/dist/chunk-YW23IBKF.js +0 -530
  75. package/dist/chunk-ZZDQKP4R.js +0 -418
  76. package/dist/use-gesture-react.esm-3SV4QLEJ.js +0 -1893
@@ -0,0 +1,601 @@
1
+ import { useDoubleTap, VideoSlotContext, useVideoSlotContext } from './chunk-MFJS65C5.js';
2
+ import { HeartFilledIcon } from './chunk-ANCP53F3.js';
3
+ import { clsx2 } from './chunk-EDWS2IPH.js';
4
+ import { injectComponentCSS } from './chunk-CAWE42LH.js';
5
+ import { memo, useMemo, useState, useCallback, useEffect, useInsertionEffect, useRef } from 'react';
6
+ import { jsx, jsxs } from 'react/jsx-runtime';
7
+
8
+ // src/components/VideoSlot/VideoSlot.css.ts
9
+ var VIDEO_SLOT_CSS = `.sv-video-slot{position:relative;width:100%;height:100%;overflow:hidden;background-color:var(--sv-bg-primary,#000);touch-action:none;user-select:none;-webkit-user-select:none;will-change:contents;contain:layout style paint}.sv-video-slot--active{z-index:1}.sv-video-slot--loading .sv-video-slot__video{opacity:0}.sv-video-slot--loading .sv-video-slot__poster{opacity:1}.sv-video-slot--playing .sv-video-slot__video{opacity:1}.sv-video-slot--playing .sv-video-slot__poster{opacity:0}.sv-video-slot--paused .sv-video-slot__video{opacity:1}.sv-video-slot--paused .sv-video-slot__poster{opacity:0}.sv-video-slot__poster--hidden{opacity:0;pointer-events:none}.sv-video-slot--error{display:flex;align-items:center;justify-content:center}.sv-video-slot__video-container{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.sv-video-slot__video{width:100%;height:100%;object-fit:cover;background-color:transparent;transition:opacity var(--sv-transition-duration,300ms)ease-out}.sv-video-slot__video[data-loaded="true"]{opacity:1}.sv-video-slot__poster{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background-color:var(--sv-bg-primary,#000);z-index:var(--sv-z-poster,3);transition:opacity var(--sv-transition-duration,300ms)ease-out;pointer-events:none}.sv-video-slot__poster-image{width:100%;height:100%;object-fit:cover}.sv-video-slot__poster--first-frame .sv-video-slot__poster-image{image-rendering:auto}.sv-video-slot__poster--skeleton{background:linear-gradient(110deg,var(--sv-skeleton-bg,rgba(255,255,255,.05))0%,var(--sv-skeleton-shimmer,rgba(255,255,255,.1))50%,var(--sv-skeleton-bg,rgba(255,255,255,.05))100%);background-size:200% 100%;animation:sv-slot-skeleton-shimmer 1.5s ease-in-out infinite}@keyframes sv-slot-skeleton-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}.sv-video-slot__overlay{position:absolute;inset:0;z-index:var(--sv-z-overlay,2);background:linear-gradient(to top,rgba(0,0,0,.6)0%,rgba(0,0,0,.3)20%,transparent 40%,transparent 60%,rgba(0,0,0,.2)80%,rgba(0,0,0,.4)100%);pointer-events:none}.sv-video-slot__overlay-top,.sv-video-slot__overlay-bottom,.sv-video-slot__overlay-left,.sv-video-slot__overlay-right,.sv-video-slot__actions,.sv-video-slot__author{pointer-events:none}.sv-video-slot__overlay button,.sv-video-slot__overlay a,.sv-video-slot__overlay [role="button"],.sv-video-slot__overlay input,.sv-video-slot__overlay label,.sv-video-slot__overlay .sv-progress-bar,.sv-video-slot__overlay .sv-author-info,.sv-video-slot__overlay .sv-video-info,.sv-video-slot__overlay .sv-action-bar{pointer-events:auto}.sv-video-slot__overlay-top{position:absolute;top:0;left:0;right:0;padding:var(--sv-spacing-md,16px);padding-top:calc(var(--sv-safe-area-top,0)+var(--sv-spacing-md,16px))}.sv-video-slot__overlay-bottom{position:absolute;bottom:0;left:0;right:0;padding:var(--sv-spacing-md,16px);padding-bottom:calc(var(--sv-safe-area-bottom,0)+var(--sv-spacing-md,16px));display:flex;flex-direction:column;gap:var(--sv-spacing-sm,8px)}.sv-video-slot__overlay-left{position:absolute;top:50%;left:0;transform:translateY(-50%);padding:var(--sv-spacing-md,16px)}.sv-video-slot__overlay-right{position:absolute;top:50%;right:0;transform:translateY(-50%);padding:var(--sv-spacing-md,16px)}.sv-video-slot__actions{position:absolute;right:var(--sv-spacing-sm,8px);bottom:calc(var(--sv-safe-area-bottom,0)+80px);display:flex;flex-direction:column;align-items:center}.sv-video-slot__author{position:absolute;left:var(--sv-spacing-md,16px);bottom:calc(var(--sv-safe-area-bottom,0)+100px);right:80px;max-width:calc(100% - 100px)}.sv-video-slot__error{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background-color:var(--sv-bg-primary,#000);color:var(--sv-text-secondary,#999);text-align:center;padding:var(--sv-spacing-lg,20px);gap:var(--sv-spacing-md,16px);z-index:var(--sv-z-indicator,3)}.sv-video-slot__error-icon{font-size:48px;opacity:.6}.sv-video-slot__error-message{font-size:var(--sv-font-size-md,14px);max-width:200px}.sv-video-slot__error-retry{padding:var(--sv-spacing-sm,8px)var(--sv-spacing-md,16px);background-color:var(--sv-color-primary,#fe2c55);color:#fff;border:0;border-radius:var(--sv-radius-md,8px);font-size:var(--sv-font-size-sm,12px);font-weight:600;cursor:pointer;transition:background-color 150ms ease}.sv-video-slot__error-retry:hover{background-color:var(--sv-color-primary-hover,#e02850)}.sv-video-slot__error-retry:active{transform:scale(.96)}.sv-video-slot__spinner{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:var(--sv-z-slot-spinner,2)}.sv-video-slot__spinner-circle{width:40px;height:40px;border:3px solid rgba(255,255,255,.2);border-top-color:var(--sv-color-primary,#fe2c55);border-radius:50%;animation:sv-slot-spin .8s linear infinite}@keyframes sv-slot-spin{to{transform:rotate(360deg)}}.sv-video-slot__play-indicator{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:64px;height:64px;display:flex;align-items:center;justify-content:center;border-radius:50%;z-index:var(--sv-z-indicator,3);opacity:0;transition:opacity 200ms ease-out;pointer-events:none}.sv-video-slot__play-indicator--visible{opacity:.65}.sv-video-slot__play-indicator--persist{opacity:.65}.sv-video-slot__play-indicator--animating-out{animation:sv-slot-indicator-out var(--sv-indicator-duration,200ms)ease-out forwards}@keyframes sv-slot-indicator-out{0%{opacity:1;transform:translate(-50%,-50%)scale(1)}100%{opacity:0;transform:translate(-50%,-50%)scale(1.5)}}.sv-video-slot__play-indicator-icon{color:#fff;font-size:28px;width:100%;height:100%;display:flex;align-items:center;justify-content:center}.sv-video-slot__progress{position:absolute;bottom:0;left:0;right:0;height:3px;background-color:rgba(255,255,255,.2);z-index:var(--sv-z-progress,4)}.sv-video-slot__progress-bar{height:100%;background-color:var(--sv-color-primary,#fe2c55);transition:width 100ms linear}.sv-video-slot__progress-buffered{position:absolute;top:0;left:0;height:100%;background-color:rgba(255,255,255,.3)}`;
10
+
11
+ // src/components/VideoSlot/constants.ts
12
+ var VIDEO_ID_ATTR = "data-video-id";
13
+ var VIDEO_ID_DATASET_KEY = "videoId";
14
+ var SLOT_ACTIVE_ATTR = "data-slot-active";
15
+ var SLOT_ACTIVE_DATASET_KEY = "slotActive";
16
+ var PLAYER_STATE_ATTR = "data-player-state";
17
+ var PLAYER_STATE_DATASET_KEY = "playerState";
18
+ var LOADING_ATTR = "data-loading";
19
+ var LOADING_DATASET_KEY = "loading";
20
+ var SLOT_CLASS = "sv-video-slot";
21
+ var SLOT_ACTIVE_CLASS = "sv-video-slot--active";
22
+ var SLOT_LOADING_CLASS = "sv-video-slot--loading";
23
+ var SLOT_ERROR_CLASS = "sv-video-slot--error";
24
+ var SLOT_PLAYING_CLASS = "sv-video-slot--playing";
25
+ var SLOT_PAUSED_CLASS = "sv-video-slot--paused";
26
+ var Z_INDEX = {
27
+ /** Base layer - slot container */
28
+ SLOT: 0,
29
+ /** Video element */
30
+ VIDEO: 1,
31
+ /** Loading spinner */
32
+ SPINNER: 2,
33
+ /** Poster/thumbnail (above spinner so thumbnail shows during loading) */
34
+ POSTER: 3,
35
+ /** Error UI and play/pause indicator */
36
+ INDICATOR: 4,
37
+ /** Progress bar (always on top) */
38
+ PROGRESS: 5
39
+ };
40
+ var Z_INDEX_CSS_VARS = {
41
+ POSTER: "--sv-z-poster",
42
+ SPINNER: "--sv-z-slot-spinner",
43
+ INDICATOR: "--sv-z-indicator",
44
+ PROGRESS: "--sv-z-progress"
45
+ };
46
+ var ANIMATION_SELECTOR = '[data-like-animation="true"]';
47
+ function VideoSlotHeadlessBase({
48
+ video,
49
+ resourceState,
50
+ playerState,
51
+ playerControls,
52
+ className,
53
+ children,
54
+ // TODO: Implement visibility detection with IntersectionObserver in v2
55
+ // These callbacks are reserved for future implementation:
56
+ // - onVisible: Called when slot enters viewport (for analytics, autoplay)
57
+ // - onHidden: Called when slot leaves viewport (for pause, cleanup)
58
+ onVisible: _onVisible,
59
+ onHidden: _onHidden,
60
+ onTap,
61
+ onDoubleTap,
62
+ onLongPress,
63
+ disableTap,
64
+ renderError,
65
+ renderLoading,
66
+ restoreFrame
67
+ }) {
68
+ useInsertionEffect(() => {
69
+ return injectComponentCSS("video-slot", VIDEO_SLOT_CSS);
70
+ }, []);
71
+ const containerRef = useRef(null);
72
+ const animationElementRef = useRef(null);
73
+ const prevVideoIdRef = useRef(video.id);
74
+ const [firstFrame, setFirstFrame] = useState(null);
75
+ const [lastUserToggleTime, setLastUserToggleTime] = useState(0);
76
+ if (prevVideoIdRef.current !== video.id) {
77
+ prevVideoIdRef.current = video.id;
78
+ animationElementRef.current = null;
79
+ if (firstFrame !== null) {
80
+ setFirstFrame(null);
81
+ }
82
+ }
83
+ const getAnimationElement = useCallback(() => {
84
+ if (animationElementRef.current) {
85
+ return animationElementRef.current;
86
+ }
87
+ const container = containerRef.current;
88
+ if (!container) return null;
89
+ const element = container.querySelector(ANIMATION_SELECTOR);
90
+ if (element) {
91
+ animationElementRef.current = element;
92
+ }
93
+ return element;
94
+ }, []);
95
+ const triggerLikeAnimation = useCallback(
96
+ (position) => {
97
+ const animationElement = getAnimationElement();
98
+ if (animationElement?.__likeAnimation) {
99
+ animationElement.__likeAnimation.triggerHeart(position.x, position.y);
100
+ }
101
+ },
102
+ [getAnimationElement]
103
+ );
104
+ const shouldShowAnimationWhenLiked = useCallback(() => {
105
+ const animationElement = getAnimationElement();
106
+ return animationElement?.__showWhenAlreadyLiked ?? true;
107
+ }, [getAnimationElement]);
108
+ const handleSingleTap = useCallback(() => {
109
+ if (disableTap) return;
110
+ setLastUserToggleTime(Date.now());
111
+ if (onTap) {
112
+ onTap();
113
+ } else {
114
+ playerControls.toggle();
115
+ }
116
+ }, [disableTap, onTap, playerControls]);
117
+ const handleDoubleTap = useCallback(
118
+ (position) => {
119
+ if (shouldShowAnimationWhenLiked() || !playerState.isPlaying) {
120
+ triggerLikeAnimation(position);
121
+ }
122
+ onDoubleTap?.();
123
+ },
124
+ [onDoubleTap, shouldShowAnimationWhenLiked, playerState.isPlaying, triggerLikeAnimation]
125
+ );
126
+ const handleLongPress = useCallback(
127
+ (_position) => {
128
+ onLongPress?.();
129
+ },
130
+ [onLongPress]
131
+ );
132
+ const { handlers: tapHandlers } = useDoubleTap({
133
+ containerRef,
134
+ onSingleTap: handleSingleTap,
135
+ onDoubleTap: handleDoubleTap,
136
+ onLongPress: onLongPress ? handleLongPress : void 0
137
+ });
138
+ const handleRetry = useCallback(() => {
139
+ playerControls.play();
140
+ }, [playerControls]);
141
+ const contextValue = useMemo(
142
+ () => ({
143
+ video,
144
+ resourceState,
145
+ playerState,
146
+ playerControls,
147
+ firstFrame,
148
+ setFirstFrame,
149
+ lastUserToggleTime,
150
+ setLastUserToggleTime
151
+ }),
152
+ [video, resourceState, playerState, playerControls, firstFrame, lastUserToggleTime]
153
+ );
154
+ const stateClasses = useMemo(() => {
155
+ const classes = [SLOT_CLASS];
156
+ if (resourceState.isActive) {
157
+ classes.push(SLOT_ACTIVE_CLASS);
158
+ }
159
+ if (playerState.isLoading) {
160
+ classes.push(SLOT_LOADING_CLASS);
161
+ }
162
+ if (playerState.error) {
163
+ classes.push(SLOT_ERROR_CLASS);
164
+ }
165
+ if (playerState.isPlaying) {
166
+ classes.push(SLOT_PLAYING_CLASS);
167
+ } else {
168
+ classes.push(SLOT_PAUSED_CLASS);
169
+ }
170
+ return classes;
171
+ }, [resourceState.isActive, playerState.isLoading, playerState.error, playerState.isPlaying]);
172
+ const playerStateString = useMemo(() => {
173
+ if (playerState.error) return "error";
174
+ if (playerState.isLoading) return "loading";
175
+ if (playerState.isPlaying) return "playing";
176
+ return "paused";
177
+ }, [playerState]);
178
+ return /* @__PURE__ */ jsx(VideoSlotContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs(
179
+ "div",
180
+ {
181
+ ref: containerRef,
182
+ className: clsx2(...stateClasses, className),
183
+ ...tapHandlers,
184
+ ...{
185
+ [VIDEO_ID_ATTR]: video.id,
186
+ [SLOT_ACTIVE_ATTR]: resourceState.isActive ? "true" : "false",
187
+ [PLAYER_STATE_ATTR]: playerStateString,
188
+ [LOADING_ATTR]: playerState.isLoading ? "true" : "false"
189
+ },
190
+ children: [
191
+ restoreFrame && !playerState.isPlaying && !playerState.error && /* @__PURE__ */ jsx(
192
+ "div",
193
+ {
194
+ className: "sv-video-slot__restore-frame",
195
+ style: {
196
+ position: "absolute",
197
+ inset: 0,
198
+ zIndex: 5,
199
+ // Above video, below overlay
200
+ backgroundImage: `url(${restoreFrame})`,
201
+ backgroundSize: "contain",
202
+ backgroundPosition: "center",
203
+ backgroundRepeat: "no-repeat",
204
+ backgroundColor: "#000"
205
+ },
206
+ "aria-hidden": "true"
207
+ }
208
+ ),
209
+ playerState.error && (renderError ? renderError(playerState.error, handleRetry) : /* @__PURE__ */ jsx(DefaultErrorUI, { error: playerState.error, onRetry: handleRetry })),
210
+ playerState.isLoading && !playerState.error && !restoreFrame && (renderLoading ? renderLoading() : /* @__PURE__ */ jsx(DefaultLoadingUI, {})),
211
+ children
212
+ ]
213
+ }
214
+ ) });
215
+ }
216
+ function getUserFriendlyErrorMessage(error) {
217
+ const message = error.message?.toLowerCase() ?? "";
218
+ if (message.includes("network") || message.includes("fetch") || message.includes("offline")) {
219
+ return "Unable to connect. Please check your internet connection.";
220
+ }
221
+ if (message.includes("timeout") || message.includes("timed out")) {
222
+ return "Video took too long to load. Please try again.";
223
+ }
224
+ if (message.includes("format") || message.includes("codec") || message.includes("unsupported") || message.includes("media")) {
225
+ return "This video format is not supported.";
226
+ }
227
+ if (message.includes("blocked") || message.includes("denied") || message.includes("cors")) {
228
+ return "Video playback is restricted.";
229
+ }
230
+ if (message.includes("404") || message.includes("not found")) {
231
+ return "Video not available.";
232
+ }
233
+ if (message.includes("500") || message.includes("server")) {
234
+ return "Server error. Please try again later.";
235
+ }
236
+ if (message.includes("hls") || message.includes("manifest") || message.includes("segment")) {
237
+ return "Video streaming error. Please try again.";
238
+ }
239
+ return "Video failed to load. Tap to retry.";
240
+ }
241
+ function DefaultErrorUI({
242
+ error,
243
+ onRetry
244
+ }) {
245
+ const displayMessage = getUserFriendlyErrorMessage(error);
246
+ return /* @__PURE__ */ jsxs("div", { className: "sv-video-slot__error", children: [
247
+ /* @__PURE__ */ jsx("div", { className: "sv-video-slot__error-icon", children: "\u26A0\uFE0F" }),
248
+ /* @__PURE__ */ jsx("div", { className: "sv-video-slot__error-message", children: displayMessage }),
249
+ /* @__PURE__ */ jsx(
250
+ "button",
251
+ {
252
+ type: "button",
253
+ className: "sv-video-slot__error-retry",
254
+ onClick: (e) => {
255
+ e.stopPropagation();
256
+ onRetry();
257
+ },
258
+ children: "Retry"
259
+ }
260
+ )
261
+ ] });
262
+ }
263
+ function DefaultLoadingUI() {
264
+ return /* @__PURE__ */ jsx("div", { className: "sv-video-slot__spinner", children: /* @__PURE__ */ jsx("div", { className: "sv-video-slot__spinner-circle", "aria-label": "Loading video" }) });
265
+ }
266
+ var VideoSlotHeadless = memo(VideoSlotHeadlessBase);
267
+ VideoSlotHeadless.displayName = "VideoSlotHeadless";
268
+ var VideoSlotPosterInner = memo(function VideoSlotPosterInner2({
269
+ className,
270
+ forceSkeleton = false,
271
+ posterUrl,
272
+ loading = "eager",
273
+ alt,
274
+ onError,
275
+ videoPoster,
276
+ videoTitle,
277
+ firstFrame,
278
+ isLoading,
279
+ currentTime
280
+ }) {
281
+ const imageSource = useMemo(() => {
282
+ if (forceSkeleton) return null;
283
+ if (posterUrl) return posterUrl;
284
+ if (firstFrame) return firstFrame;
285
+ if (videoPoster) return videoPoster;
286
+ return null;
287
+ }, [forceSkeleton, posterUrl, firstFrame, videoPoster]);
288
+ const sourceKey = imageSource ?? "skeleton";
289
+ const [errorForSource, setErrorForSource] = useState(null);
290
+ const imageError = errorForSource === sourceKey;
291
+ const handleImageError = useCallback(() => {
292
+ setErrorForSource(sourceKey);
293
+ onError?.(new Error("Failed to load poster image"));
294
+ }, [sourceKey, onError]);
295
+ const showSkeleton = !imageSource || imageError;
296
+ const posterType = useMemo(() => {
297
+ if (showSkeleton) return "skeleton";
298
+ if (imageSource === firstFrame) return "first-frame";
299
+ return "poster";
300
+ }, [showSkeleton, imageSource, firstFrame]);
301
+ const imageAlt = alt ?? videoTitle ?? "";
302
+ const isVisible = isLoading && currentTime === 0;
303
+ return /* @__PURE__ */ jsx(
304
+ "div",
305
+ {
306
+ className: clsx2(
307
+ "sv-video-slot__poster",
308
+ `sv-video-slot__poster--${posterType}`,
309
+ !isVisible && "sv-video-slot__poster--hidden",
310
+ className
311
+ ),
312
+ "aria-hidden": !isVisible,
313
+ children: imageSource && !imageError ? /* @__PURE__ */ jsx(
314
+ "img",
315
+ {
316
+ className: "sv-video-slot__poster-image",
317
+ src: imageSource,
318
+ alt: imageAlt,
319
+ loading,
320
+ decoding: "async",
321
+ onError: handleImageError
322
+ }
323
+ ) : (
324
+ // Skeleton shimmer - pure CSS animation, no JS
325
+ /* @__PURE__ */ jsx("div", { className: "sv-video-slot__poster-skeleton", "aria-hidden": "true" })
326
+ )
327
+ }
328
+ );
329
+ });
330
+ VideoSlotPosterInner.displayName = "VideoSlotPosterInner";
331
+ function VideoSlotPoster(props) {
332
+ const { video, firstFrame, playerState } = useVideoSlotContext();
333
+ return /* @__PURE__ */ jsx(
334
+ VideoSlotPosterInner,
335
+ {
336
+ ...props,
337
+ videoPoster: video.poster,
338
+ videoTitle: video.title,
339
+ firstFrame,
340
+ isLoading: playerState.isLoading,
341
+ currentTime: playerState.currentTime
342
+ }
343
+ );
344
+ }
345
+ VideoSlotPoster.displayName = "VideoSlotPoster";
346
+ function VideoSlotSkeleton({
347
+ className
348
+ }) {
349
+ return /* @__PURE__ */ jsx(
350
+ "div",
351
+ {
352
+ className: clsx2("sv-video-slot__poster", "sv-video-slot__poster--skeleton", className),
353
+ "aria-hidden": "true",
354
+ children: /* @__PURE__ */ jsx("div", { className: "sv-video-slot__poster-skeleton" })
355
+ }
356
+ );
357
+ }
358
+ VideoSlotSkeleton.displayName = "VideoSlotSkeleton";
359
+ function VideoSlotOverlayRoot({
360
+ children,
361
+ className,
362
+ noGradient = false
363
+ }) {
364
+ return /* @__PURE__ */ jsx(
365
+ "div",
366
+ {
367
+ className: clsx2(
368
+ "sv-video-slot__overlay",
369
+ noGradient && "sv-video-slot__overlay--no-gradient",
370
+ className
371
+ ),
372
+ children
373
+ }
374
+ );
375
+ }
376
+ function VideoSlotOverlayTop({
377
+ children,
378
+ className
379
+ }) {
380
+ return /* @__PURE__ */ jsx("div", { className: clsx2("sv-video-slot__overlay-top", className), children });
381
+ }
382
+ function VideoSlotOverlayBottom({
383
+ children,
384
+ className
385
+ }) {
386
+ return /* @__PURE__ */ jsx("div", { className: clsx2("sv-video-slot__overlay-bottom", className), children });
387
+ }
388
+ function VideoSlotOverlayLeft({
389
+ children,
390
+ className
391
+ }) {
392
+ return /* @__PURE__ */ jsx("div", { className: clsx2("sv-video-slot__overlay-left", className), children });
393
+ }
394
+ function VideoSlotOverlayRight({
395
+ children,
396
+ className
397
+ }) {
398
+ return /* @__PURE__ */ jsx("div", { className: clsx2("sv-video-slot__overlay-right", className), children });
399
+ }
400
+ function VideoSlotOverlayActions({
401
+ children,
402
+ className
403
+ }) {
404
+ return /* @__PURE__ */ jsx("div", { className: clsx2("sv-video-slot__actions", className), children });
405
+ }
406
+ function VideoSlotOverlayAuthor({
407
+ children,
408
+ className
409
+ }) {
410
+ return /* @__PURE__ */ jsx("div", { className: clsx2("sv-video-slot__author", className), children });
411
+ }
412
+ VideoSlotOverlayRoot.displayName = "VideoSlotOverlay";
413
+ VideoSlotOverlayTop.displayName = "VideoSlotOverlay.Top";
414
+ VideoSlotOverlayBottom.displayName = "VideoSlotOverlay.Bottom";
415
+ VideoSlotOverlayLeft.displayName = "VideoSlotOverlay.Left";
416
+ VideoSlotOverlayRight.displayName = "VideoSlotOverlay.Right";
417
+ VideoSlotOverlayActions.displayName = "VideoSlotOverlay.Actions";
418
+ VideoSlotOverlayAuthor.displayName = "VideoSlotOverlay.Author";
419
+ var VideoSlotOverlay = Object.assign(VideoSlotOverlayRoot, {
420
+ Top: VideoSlotOverlayTop,
421
+ Bottom: VideoSlotOverlayBottom,
422
+ Left: VideoSlotOverlayLeft,
423
+ Right: VideoSlotOverlayRight,
424
+ Actions: VideoSlotOverlayActions,
425
+ Author: VideoSlotOverlayAuthor
426
+ });
427
+
428
+ // src/components/VideoSlot/VideoSlotLikeAnimation.css.ts
429
+ var VIDEO_SLOT_LIKE_ANIMATION_CSS = `.sv-like-animation-container{position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:var(--sv-z-indicator,3)}.sv-like-heart{position:absolute;left:var(--sv-heart-x,50%);top:var(--sv-heart-y,50%);transform:translate(-50%,-50%);pointer-events:none;will-change:transform,opacity}.sv-like-heart__icon{display:flex;align-items:center;justify-content:center;width:var(--sv-heart-size,80px);height:var(--sv-heart-size,80px);transform-origin:center;animation-duration:var(--sv-heart-duration,1000ms);animation-fill-mode:forwards;animation-timing-function:cubic-bezier(.25,.46,.45,.94)}.sv-like-heart--float .sv-like-heart__icon{animation-name:sv-heart-float;animation-timing-function:ease-out}@keyframes sv-heart-float{0%{opacity:0;transform:scale(0)rotate(var(--sv-heart-rotation,0deg))}10%{opacity:1;transform:scale(calc(var(--sv-heart-scale,1)* 1.2))rotate(var(--sv-heart-rotation,0deg))}20%{transform:scale(var(--sv-heart-scale,1))rotate(var(--sv-heart-rotation,0deg))translateY(-20px)translateX(10px)}40%{transform:scale(var(--sv-heart-scale,1))rotate(calc(var(--sv-heart-rotation,0deg)- 10deg))translateY(-60px)translateX(-15px)}60%{opacity:1;transform:scale(var(--sv-heart-scale,1))rotate(calc(var(--sv-heart-rotation,0deg)+15deg))translateY(-100px)translateX(10px)}80%{opacity:.6;transform:scale(calc(var(--sv-heart-scale,1)* .9))rotate(calc(var(--sv-heart-rotation,0deg)- 5deg))translateY(-140px)translateX(-5px)}100%{opacity:0;transform:scale(calc(var(--sv-heart-scale,1)* .7))rotate(var(--sv-heart-rotation,0deg))translateY(-180px)translateX(0)}}.sv-like-heart--burst .sv-like-heart__icon{animation-name:sv-heart-burst;animation-timing-function:cubic-bezier(.175,.885,.32,1.275)}@keyframes sv-heart-burst{0%{opacity:0;transform:scale(0)rotate(var(--sv-heart-rotation,0deg))}20%{opacity:1;transform:scale(calc(var(--sv-heart-scale,1)* 1.5))rotate(calc(var(--sv-heart-rotation,0deg)+15deg))}40%{transform:scale(calc(var(--sv-heart-scale,1)* 1.2))rotate(calc(var(--sv-heart-rotation,0deg)- 10deg))}60%{opacity:1;transform:scale(var(--sv-heart-scale,1))rotate(var(--sv-heart-rotation,0deg))translateY(-30px)}80%{opacity:.5;transform:scale(calc(var(--sv-heart-scale,1)* .9))rotate(var(--sv-heart-rotation,0deg))translateY(-60px)}100%{opacity:0;transform:scale(calc(var(--sv-heart-scale,1)* .6))rotate(var(--sv-heart-rotation,0deg))translateY(-80px)}}.sv-like-heart--scale .sv-like-heart__icon{animation-name:sv-heart-scale;animation-timing-function:ease-out}@keyframes sv-heart-scale{0%{opacity:0;transform:scale(0)}20%{opacity:1;transform:scale(calc(var(--sv-heart-scale,1)* 1.3))}40%{transform:scale(var(--sv-heart-scale,1))}70%{opacity:1;transform:scale(var(--sv-heart-scale,1))}100%{opacity:0;transform:scale(calc(var(--sv-heart-scale,1)* 1.1))}}.sv-like-heart--bounce .sv-like-heart__icon{animation-name:sv-heart-bounce;animation-timing-function:cubic-bezier(.68,-.55,.265,1.55)}@keyframes sv-heart-bounce{0%{opacity:0;transform:scale(0)rotate(var(--sv-heart-rotation,0deg))translateY(0)}15%{opacity:1;transform:scale(calc(var(--sv-heart-scale,1)* 1.4))rotate(calc(var(--sv-heart-rotation,0deg)+10deg))translateY(-30px)}30%{transform:scale(calc(var(--sv-heart-scale,1)* .9))rotate(calc(var(--sv-heart-rotation,0deg)- 5deg))translateY(10px)}45%{transform:scale(calc(var(--sv-heart-scale,1)* 1.15))rotate(calc(var(--sv-heart-rotation,0deg)+5deg))translateY(-15px)}60%{opacity:1;transform:scale(var(--sv-heart-scale,1))rotate(var(--sv-heart-rotation,0deg))translateY(0)}80%{opacity:.7;transform:scale(var(--sv-heart-scale,1))rotate(var(--sv-heart-rotation,0deg))translateY(-10px)}100%{opacity:0;transform:scale(calc(var(--sv-heart-scale,1)* .8))rotate(var(--sv-heart-rotation,0deg))translateY(-20px)}}@media(prefers-reduced-motion:reduce){.sv-like-heart__icon{animation-duration:.5s !important}.sv-like-heart--float .sv-like-heart__icon,.sv-like-heart--burst .sv-like-heart__icon,.sv-like-heart--bounce .sv-like-heart__icon{animation-name:sv-heart-scale}}`;
430
+ var SingleHeart = memo(function SingleHeart2({
431
+ heart,
432
+ icon,
433
+ size,
434
+ animation,
435
+ duration,
436
+ onComplete
437
+ }) {
438
+ useEffect(() => {
439
+ const timer = setTimeout(() => {
440
+ onComplete(heart.id);
441
+ }, duration);
442
+ return () => clearTimeout(timer);
443
+ }, [heart.id, duration, onComplete]);
444
+ return /* @__PURE__ */ jsx(
445
+ "div",
446
+ {
447
+ className: clsx2("sv-like-heart", `sv-like-heart--${animation}`),
448
+ style: {
449
+ "--sv-heart-x": `${heart.x}%`,
450
+ "--sv-heart-y": `${heart.y}%`,
451
+ "--sv-heart-rotation": `${heart.rotation}deg`,
452
+ "--sv-heart-scale": heart.scale,
453
+ "--sv-heart-duration": `${duration}ms`,
454
+ "--sv-heart-size": `${size}px`
455
+ },
456
+ "aria-hidden": "true",
457
+ children: /* @__PURE__ */ jsx("div", { className: "sv-like-heart__icon", children: icon })
458
+ }
459
+ );
460
+ });
461
+ function VideoSlotLikeAnimation({
462
+ icon,
463
+ duration = 1e3,
464
+ animation = "float",
465
+ size = 80,
466
+ color = "#fe2c55",
467
+ showWhenAlreadyLiked = true,
468
+ maxHearts = 10,
469
+ className,
470
+ onHeartCreated
471
+ }) {
472
+ useInsertionEffect(() => {
473
+ return injectComponentCSS("VideoSlotLikeAnimation", VIDEO_SLOT_LIKE_ANIMATION_CSS);
474
+ }, []);
475
+ const heartIdCounterRef = useRef(0);
476
+ const [hearts, setHearts] = useState([]);
477
+ const containerRef = useRef(null);
478
+ const triggerHeart = useCallback(
479
+ (x, y) => {
480
+ heartIdCounterRef.current += 1;
481
+ const newHeart = {
482
+ id: heartIdCounterRef.current,
483
+ x,
484
+ y,
485
+ createdAt: Date.now(),
486
+ rotation: Math.random() * 60 - 30,
487
+ // -30 to 30 degrees
488
+ scale: 0.8 + Math.random() * 0.4
489
+ // 0.8 to 1.2
490
+ };
491
+ setHearts((prev) => {
492
+ const updated = prev.length >= maxHearts ? prev.slice(1) : prev;
493
+ return [...updated, newHeart];
494
+ });
495
+ onHeartCreated?.(newHeart);
496
+ },
497
+ [maxHearts, onHeartCreated]
498
+ );
499
+ const handleHeartComplete = useCallback((id) => {
500
+ setHearts((prev) => prev.filter((h) => h.id !== id));
501
+ }, []);
502
+ const clearAll = useCallback(() => {
503
+ setHearts([]);
504
+ }, []);
505
+ useEffect(() => {
506
+ const container = containerRef.current;
507
+ if (!container) {
508
+ if (process.env.NODE_ENV !== "production") {
509
+ console.warn("[VideoSlotLikeAnimation] Container ref is null. Animation will not work.");
510
+ }
511
+ return;
512
+ }
513
+ const el = container;
514
+ el.__likeAnimation = {
515
+ triggerHeart,
516
+ clearAll
517
+ };
518
+ el.__showWhenAlreadyLiked = showWhenAlreadyLiked;
519
+ return () => {
520
+ el.__likeAnimation = void 0;
521
+ el.__showWhenAlreadyLiked = void 0;
522
+ };
523
+ }, [triggerHeart, clearAll, showWhenAlreadyLiked]);
524
+ const iconElement = icon ?? (typeof HeartFilledIcon === "function" ? /* @__PURE__ */ jsx(
525
+ HeartFilledIcon,
526
+ {
527
+ width: size,
528
+ height: size,
529
+ style: { color, filter: "drop-shadow(0 2px 4px rgba(0,0,0,0.3))" }
530
+ }
531
+ ) : /* @__PURE__ */ jsx("span", { style: { fontSize: size, color, textShadow: "0 2px 4px rgba(0,0,0,0.3)" }, children: "\u2764\uFE0F" }));
532
+ return /* @__PURE__ */ jsx(
533
+ "div",
534
+ {
535
+ ref: containerRef,
536
+ className: clsx2("sv-like-animation-container", className),
537
+ "data-like-animation": "true",
538
+ "aria-hidden": "true",
539
+ children: hearts.map((heart) => /* @__PURE__ */ jsx(
540
+ SingleHeart,
541
+ {
542
+ heart,
543
+ icon: iconElement,
544
+ size,
545
+ animation,
546
+ duration,
547
+ onComplete: handleHeartComplete
548
+ },
549
+ heart.id
550
+ ))
551
+ }
552
+ );
553
+ }
554
+ VideoSlotLikeAnimation.displayName = "VideoSlotLikeAnimation";
555
+
556
+ // src/components/VideoSlot/ReportedVideoOverlay.css.ts
557
+ var REPORTED_VIDEO_OVERLAY_CSS = `.sv-reported-overlay{position:absolute;top:0;left:0;right:0;bottom:0;z-index:var(--sv-z-reported-overlay,10);background:rgba(0,0,0,.85);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;padding:24px}.sv-reported-overlay .sv-reported-content{display:flex;flex-direction:column;align-items:center;text-align:center;max-width:300px;gap:16px}.sv-reported-overlay .sv-reported-icon{width:64px;height:64px;display:flex;align-items:center;justify-content:center;color:var(--sv-color-warning,#ffa500);opacity:.9}.sv-reported-overlay .sv-reported-icon svg{width:100%;height:100%}.sv-reported-overlay .sv-reported-title{margin:0;font-size:18px;font-weight:600;color:#fff}.sv-reported-overlay .sv-reported-message{margin:0;font-size:14px;color:rgba(255,255,255,.7);line-height:1.5}.sv-reported-overlay .sv-reported-button{display:inline-block;margin-top:8px;padding:12px 24px;font-size:14px;font-weight:600;color:#fff;background:transparent;border:1px solid rgba(255,255,255,.3);border-radius:8px;cursor:pointer;transition:all 150ms ease;text-decoration:none;text-transform:none;letter-spacing:normal;line-height:1.2;box-sizing:border-box;font-family:inherit;outline:none}.sv-reported-overlay .sv-reported-button:hover{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.5)}.sv-reported-overlay .sv-reported-button:active{transform:scale(.98)}.sv-reported-overlay .sv-reported-button:focus{border-color:rgba(255,255,255,.6);box-shadow:0 0 0 2px rgba(255,255,255,.2)}`;
558
+ function ReportedIcon() {
559
+ return /* @__PURE__ */ jsxs(
560
+ "svg",
561
+ {
562
+ "aria-hidden": "true",
563
+ width: "48",
564
+ height: "48",
565
+ viewBox: "0 0 24 24",
566
+ fill: "none",
567
+ stroke: "currentColor",
568
+ strokeWidth: "1.5",
569
+ strokeLinecap: "round",
570
+ strokeLinejoin: "round",
571
+ children: [
572
+ /* @__PURE__ */ jsx("path", { d: "M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z" }),
573
+ /* @__PURE__ */ jsx("line", { x1: "4", y1: "22", x2: "4", y2: "15" })
574
+ ]
575
+ }
576
+ );
577
+ }
578
+ function ReportedVideoOverlayComponent({
579
+ onWatchVideo,
580
+ title = "Video Reported",
581
+ message = "You have reported this video. It will be reviewed by our team.",
582
+ buttonText = "Watch Video",
583
+ icon,
584
+ className
585
+ }) {
586
+ useEffect(() => {
587
+ return injectComponentCSS("reported-video-overlay", REPORTED_VIDEO_OVERLAY_CSS, {
588
+ layered: false
589
+ });
590
+ }, []);
591
+ return /* @__PURE__ */ jsx("div", { className: `sv-reported-overlay ${className ?? ""}`, children: /* @__PURE__ */ jsxs("div", { className: "sv-reported-content", children: [
592
+ /* @__PURE__ */ jsx("div", { className: "sv-reported-icon", children: icon ?? /* @__PURE__ */ jsx(ReportedIcon, {}) }),
593
+ /* @__PURE__ */ jsx("h3", { className: "sv-reported-title", children: title }),
594
+ /* @__PURE__ */ jsx("p", { className: "sv-reported-message", children: message }),
595
+ /* @__PURE__ */ jsx("button", { type: "button", className: "sv-reported-button", onClick: onWatchVideo, children: buttonText })
596
+ ] }) });
597
+ }
598
+ var ReportedVideoOverlay = memo(ReportedVideoOverlayComponent);
599
+ ReportedVideoOverlay.displayName = "ReportedVideoOverlay";
600
+
601
+ export { LOADING_ATTR, LOADING_DATASET_KEY, PLAYER_STATE_ATTR, PLAYER_STATE_DATASET_KEY, ReportedVideoOverlay, SLOT_ACTIVE_ATTR, SLOT_ACTIVE_CLASS, SLOT_ACTIVE_DATASET_KEY, SLOT_CLASS, SLOT_ERROR_CLASS, SLOT_LOADING_CLASS, SLOT_PAUSED_CLASS, SLOT_PLAYING_CLASS, VIDEO_ID_ATTR, VIDEO_ID_DATASET_KEY, VIDEO_SLOT_CSS, VIDEO_SLOT_LIKE_ANIMATION_CSS, VideoSlotHeadless, VideoSlotLikeAnimation, VideoSlotOverlay, VideoSlotPoster, VideoSlotSkeleton, Z_INDEX, Z_INDEX_CSS_VARS };
@@ -1 +1 @@
1
- export { ACTION_BAR_CSS, CompoundBookmark as ActionBarBookmark, CompoundComment as ActionBarComment, ActionBarHeadless, CompoundLike as ActionBarLike, CompoundShare as ActionBarShare, ActionButton, BookmarkButton, CommentButton, LikeButton, ShareButton } from '../../chunk-ANGBSV7L.js';
1
+ export { ACTION_BAR_CSS, CompoundBookmark as ActionBarBookmark, CompoundComment as ActionBarComment, ActionBarHeadless, CompoundLike as ActionBarLike, CompoundShare as ActionBarShare, ActionButton, BookmarkButton, CommentButton, LikeButton, ShareButton } from '../../chunk-CIIZ3IHV.js';