@xhub-short/ui 0.1.0-beta.1 → 0.1.0-beta.10
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.
- package/dist/CommentSheet.css-BeCrEaUG.d.ts +221 -0
- package/dist/{chunk-2PTMP65P.js → chunk-2FSDVYER.js} +8 -9
- package/dist/{chunk-WKX2WBVO.js → chunk-3XPJHUYL.js} +1 -39
- package/dist/{chunk-ANGBSV7L.js → chunk-AC2IFAJR.js} +10 -5
- package/dist/{chunk-4YDIRPIN.js → chunk-ANCP53F3.js} +3 -3
- package/dist/chunk-AQHD6LPS.js +430 -0
- package/dist/{chunk-HW4LXTFT.js → chunk-CL6BS7GB.js} +7 -5
- package/dist/{chunk-YW23IBKF.js → chunk-ECR42RKK.js} +46 -5
- package/dist/chunk-EDWS2IPH.js +1 -0
- package/dist/chunk-FNXTPQ6L.js +2573 -0
- package/dist/{chunk-DHQJBXQW.js → chunk-KWHMZ6H5.js} +1 -1
- package/dist/{chunk-UXMA4KJZ.js → chunk-RMLTPW5S.js} +3 -2
- package/dist/{chunk-SSJDO24Q.js → chunk-SZXFH334.js} +1 -1
- package/dist/{chunk-4MN72OZH.js → chunk-UNV3NWN6.js} +4 -4
- package/dist/{chunk-ZZDQKP4R.js → chunk-WCRDTBCZ.js} +94 -155
- package/dist/{chunk-XAOEHLOX.js → chunk-XDIH66C4.js} +245 -52
- package/dist/components/ActionBar/index.js +1 -1
- package/dist/components/AuthorInfo/index.d.ts +5 -1
- package/dist/components/AuthorInfo/index.js +1 -1
- package/dist/components/BlurhashPlaceholder/index.d.ts +67 -0
- package/dist/components/BlurhashPlaceholder/index.js +150 -0
- package/dist/components/CommentSheet/index.d.ts +164 -0
- package/dist/components/CommentSheet/index.js +1 -0
- package/dist/components/ErrorBoundary/index.js +1 -1
- package/dist/components/OfflineIndicator/index.d.ts +56 -0
- package/dist/components/OfflineIndicator/index.js +151 -0
- package/dist/components/ProgressBar/index.d.ts +30 -2
- package/dist/components/ProgressBar/index.js +1 -1
- package/dist/components/Skeleton/index.js +1 -1
- package/dist/components/SubtitleDisplay/index.d.ts +94 -0
- package/dist/components/SubtitleDisplay/index.js +165 -0
- package/dist/components/VideoFeed/index.d.ts +11 -0
- package/dist/components/VideoFeed/index.js +1 -1
- package/dist/components/VideoInfo/index.js +1 -1
- package/dist/components/VideoPlayer/index.d.ts +14 -41
- package/dist/components/VideoPlayer/index.js +1 -1
- package/dist/components/VideoSlot/index.d.ts +124 -64
- package/dist/components/VideoSlot/index.js +1 -1
- package/dist/components/VirtualSlider/index.d.ts +339 -0
- package/dist/components/VirtualSlider/index.js +1 -0
- package/dist/components/icons/index.js +1 -1
- package/dist/index.d.ts +76 -93
- package/dist/index.js +75 -27
- package/package.json +53 -8
- package/dist/use-gesture-react.esm-3SV4QLEJ.js +0 -1893
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { injectComponentCSS } from './chunk-
|
|
1
|
+
import { injectComponentCSS } from './chunk-RMLTPW5S.js';
|
|
2
2
|
import { clsx } from 'clsx';
|
|
3
3
|
import { createContext, useInsertionEffect, useMemo, useState, useContext } from 'react';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -20,11 +20,12 @@ function injectComponentCSS(componentId, css) {
|
|
|
20
20
|
const style = document.createElement("style");
|
|
21
21
|
style.id = styleId;
|
|
22
22
|
style.setAttribute("data-sv-component", componentId);
|
|
23
|
-
style.textContent =
|
|
23
|
+
style.textContent = `@layer sv {
|
|
24
|
+
${css}
|
|
25
|
+
}`;
|
|
24
26
|
document.head.appendChild(style);
|
|
25
27
|
injectedStyles.add(componentId);
|
|
26
28
|
return () => {
|
|
27
|
-
removeComponentCSS(componentId);
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
31
|
function removeComponentCSS(componentId) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { injectComponentCSS } from './chunk-
|
|
1
|
+
import { clsx2 } from './chunk-EDWS2IPH.js';
|
|
2
|
+
import { injectComponentCSS } from './chunk-RMLTPW5S.js';
|
|
3
3
|
import { useInsertionEffect } from 'react';
|
|
4
4
|
import { jsx } from 'react/jsx-runtime';
|
|
5
5
|
|
|
@@ -121,7 +121,7 @@ function Skeleton({
|
|
|
121
121
|
return /* @__PURE__ */ jsx(
|
|
122
122
|
"div",
|
|
123
123
|
{
|
|
124
|
-
className:
|
|
124
|
+
className: clsx2("sv-skeleton", animation !== "none" && `sv-skeleton--${animation}`, className),
|
|
125
125
|
style,
|
|
126
126
|
"aria-hidden": "true"
|
|
127
127
|
}
|
|
@@ -130,7 +130,7 @@ function Skeleton({
|
|
|
130
130
|
function SkeletonVideo({
|
|
131
131
|
className
|
|
132
132
|
}) {
|
|
133
|
-
return /* @__PURE__ */ jsx("div", { className:
|
|
133
|
+
return /* @__PURE__ */ jsx("div", { className: clsx2("sv-skeleton-video", className), children: /* @__PURE__ */ jsx(Skeleton, { width: "100%", height: "100%", animation: "wave" }) });
|
|
134
134
|
}
|
|
135
135
|
function SkeletonAvatar({
|
|
136
136
|
size = 40,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { VirtualSlider } from './chunk-AQHD6LPS.js';
|
|
2
|
+
import { clsx2 } from './chunk-EDWS2IPH.js';
|
|
3
|
+
import { injectComponentCSS } from './chunk-RMLTPW5S.js';
|
|
4
|
+
import { createContext, useContext, useInsertionEffect, useRef, useMemo, useCallback } from 'react';
|
|
4
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
6
|
|
|
6
7
|
// src/components/VideoFeed/VideoFeed.css.ts
|
|
@@ -193,6 +194,96 @@ function useVideoFeedContext() {
|
|
|
193
194
|
function useOptionalVideoFeedContext() {
|
|
194
195
|
return useContext(VideoFeedContext);
|
|
195
196
|
}
|
|
197
|
+
function VideoFeedLoading({ text = "Loading..." }) {
|
|
198
|
+
return /* @__PURE__ */ jsxs("div", { className: "sv-video-feed__loading", children: [
|
|
199
|
+
/* @__PURE__ */ jsx("div", { className: "sv-video-feed__loading-spinner" }),
|
|
200
|
+
/* @__PURE__ */ jsx("div", { className: "sv-video-feed__loading-text", children: text })
|
|
201
|
+
] });
|
|
202
|
+
}
|
|
203
|
+
function VideoFeedEmpty({ text = "No videos" }) {
|
|
204
|
+
return /* @__PURE__ */ jsxs("div", { className: "sv-video-feed__empty", children: [
|
|
205
|
+
/* @__PURE__ */ jsx("div", { className: "sv-video-feed__empty-icon", children: "\u{1F4ED}" }),
|
|
206
|
+
/* @__PURE__ */ jsx("div", { className: "sv-video-feed__empty-text", children: text })
|
|
207
|
+
] });
|
|
208
|
+
}
|
|
209
|
+
function VideoFeedEnd({ text = "You've reached the end" }) {
|
|
210
|
+
return /* @__PURE__ */ jsx("div", { className: "sv-video-feed__end", "aria-live": "polite", children: text });
|
|
211
|
+
}
|
|
212
|
+
function VideoFeedHeadless({
|
|
213
|
+
feedState,
|
|
214
|
+
swipeState,
|
|
215
|
+
height,
|
|
216
|
+
className,
|
|
217
|
+
renderSlot,
|
|
218
|
+
onIndexChange,
|
|
219
|
+
onEndReached,
|
|
220
|
+
endReachedThreshold = 2,
|
|
221
|
+
bufferSize = 1,
|
|
222
|
+
loadingText = "Loading...",
|
|
223
|
+
emptyText = "No videos",
|
|
224
|
+
endText = "You've reached the end",
|
|
225
|
+
disableInlineTransforms = false
|
|
226
|
+
}) {
|
|
227
|
+
useInsertionEffect(() => {
|
|
228
|
+
return injectComponentCSS("sv-video-feed", VIDEO_FEED_CSS);
|
|
229
|
+
}, []);
|
|
230
|
+
const { videos, isLoading, hasMore } = feedState;
|
|
231
|
+
const { activeIndex, isSwiping, dragOffset, goToIndex } = swipeState;
|
|
232
|
+
const containerHeightRef = useRef(
|
|
233
|
+
typeof window !== "undefined" ? window.innerHeight : 800
|
|
234
|
+
);
|
|
235
|
+
const contextValue = useMemo(
|
|
236
|
+
() => ({
|
|
237
|
+
videos,
|
|
238
|
+
activeIndex: Math.max(0, Math.min(videos.length - 1, activeIndex)),
|
|
239
|
+
isSwiping,
|
|
240
|
+
dragOffset,
|
|
241
|
+
containerHeight: containerHeightRef.current,
|
|
242
|
+
goToIndex,
|
|
243
|
+
totalCount: videos.length
|
|
244
|
+
}),
|
|
245
|
+
[videos, activeIndex, isSwiping, dragOffset, goToIndex]
|
|
246
|
+
);
|
|
247
|
+
const keyExtractor = useCallback((video) => video.id, []);
|
|
248
|
+
const renderItem = useCallback(
|
|
249
|
+
(video, index, _isActive) => {
|
|
250
|
+
return renderSlot(video, index);
|
|
251
|
+
},
|
|
252
|
+
[renderSlot]
|
|
253
|
+
);
|
|
254
|
+
const loadingComponent = useMemo(() => /* @__PURE__ */ jsx(VideoFeedLoading, { text: loadingText }), [loadingText]);
|
|
255
|
+
const emptyComponent = useMemo(() => /* @__PURE__ */ jsx(VideoFeedEmpty, { text: emptyText }), [emptyText]);
|
|
256
|
+
const endComponent = useMemo(() => /* @__PURE__ */ jsx(VideoFeedEnd, { text: endText }), [endText]);
|
|
257
|
+
const sliderProps = {
|
|
258
|
+
// Required
|
|
259
|
+
items: videos,
|
|
260
|
+
keyExtractor,
|
|
261
|
+
renderItem,
|
|
262
|
+
// Swipe state
|
|
263
|
+
activeIndex,
|
|
264
|
+
isSwiping,
|
|
265
|
+
dragOffset,
|
|
266
|
+
goToIndex,
|
|
267
|
+
// Callbacks
|
|
268
|
+
onIndexChange,
|
|
269
|
+
onEndReached,
|
|
270
|
+
endReachedThreshold,
|
|
271
|
+
bufferSize,
|
|
272
|
+
// Loading states
|
|
273
|
+
isLoading,
|
|
274
|
+
hasMore,
|
|
275
|
+
// Custom UI (video-specific styling)
|
|
276
|
+
loadingComponent,
|
|
277
|
+
emptyComponent,
|
|
278
|
+
endComponent,
|
|
279
|
+
// Styling - use sv-video-feed classes instead of sv-slider
|
|
280
|
+
height,
|
|
281
|
+
className: clsx2("sv-video-feed", className),
|
|
282
|
+
disableInlineTransforms
|
|
283
|
+
};
|
|
284
|
+
return /* @__PURE__ */ jsx(VideoFeedContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(VirtualSlider, { ...sliderProps }) });
|
|
285
|
+
}
|
|
286
|
+
VideoFeedHeadless.displayName = "VideoFeedHeadless";
|
|
196
287
|
function useVideoFeedPosition(options) {
|
|
197
288
|
const {
|
|
198
289
|
totalCount,
|
|
@@ -255,158 +346,6 @@ function getSlotIndexFromPosition(yPosition, containerHeight, totalCount) {
|
|
|
255
346
|
const rawIndex = Math.round(-yPosition / containerHeight);
|
|
256
347
|
return Math.max(0, Math.min(totalCount - 1, rawIndex));
|
|
257
348
|
}
|
|
258
|
-
function VideoFeedHeadless({
|
|
259
|
-
feedState,
|
|
260
|
-
swipeState,
|
|
261
|
-
height,
|
|
262
|
-
className,
|
|
263
|
-
renderSlot,
|
|
264
|
-
onIndexChange,
|
|
265
|
-
onEndReached,
|
|
266
|
-
endReachedThreshold = 2,
|
|
267
|
-
bufferSize = 1,
|
|
268
|
-
loadingText = "Loading...",
|
|
269
|
-
emptyText = "No videos",
|
|
270
|
-
endText = "You've reached the end",
|
|
271
|
-
disableInlineTransforms = false
|
|
272
|
-
}) {
|
|
273
|
-
useInsertionEffect(() => {
|
|
274
|
-
return injectComponentCSS("sv-video-feed", VIDEO_FEED_CSS);
|
|
275
|
-
}, []);
|
|
276
|
-
const containerRef = useRef(null);
|
|
277
|
-
const [containerHeight, setContainerHeight] = useState(0);
|
|
278
|
-
const [isResizing, setIsResizing] = useState(false);
|
|
279
|
-
const resizeTimeoutRef = useRef(null);
|
|
280
|
-
const prevHeightRef = useRef(0);
|
|
281
|
-
useEffect(() => {
|
|
282
|
-
const container = containerRef.current;
|
|
283
|
-
if (!container) return;
|
|
284
|
-
const updateHeight = () => {
|
|
285
|
-
const newHeight = container.clientHeight;
|
|
286
|
-
const heightChanged = newHeight !== prevHeightRef.current && prevHeightRef.current !== 0;
|
|
287
|
-
if (heightChanged) {
|
|
288
|
-
setIsResizing(true);
|
|
289
|
-
if (resizeTimeoutRef.current) {
|
|
290
|
-
clearTimeout(resizeTimeoutRef.current);
|
|
291
|
-
}
|
|
292
|
-
resizeTimeoutRef.current = setTimeout(() => {
|
|
293
|
-
setIsResizing(false);
|
|
294
|
-
}, 150);
|
|
295
|
-
}
|
|
296
|
-
prevHeightRef.current = newHeight;
|
|
297
|
-
setContainerHeight(newHeight);
|
|
298
|
-
};
|
|
299
|
-
updateHeight();
|
|
300
|
-
let cleanup;
|
|
301
|
-
if (typeof ResizeObserver !== "undefined") {
|
|
302
|
-
const observer = new ResizeObserver(updateHeight);
|
|
303
|
-
observer.observe(container);
|
|
304
|
-
cleanup = () => observer.disconnect();
|
|
305
|
-
} else {
|
|
306
|
-
window.addEventListener("resize", updateHeight);
|
|
307
|
-
cleanup = () => window.removeEventListener("resize", updateHeight);
|
|
308
|
-
}
|
|
309
|
-
return () => {
|
|
310
|
-
cleanup();
|
|
311
|
-
if (resizeTimeoutRef.current) {
|
|
312
|
-
clearTimeout(resizeTimeoutRef.current);
|
|
313
|
-
}
|
|
314
|
-
};
|
|
315
|
-
}, []);
|
|
316
|
-
const { videos, isLoading, hasMore } = feedState;
|
|
317
|
-
const { activeIndex, isSwiping, dragOffset, goToIndex } = swipeState;
|
|
318
|
-
const prevActiveIndexRef = useRef(activeIndex);
|
|
319
|
-
useEffect(() => {
|
|
320
|
-
if (prevActiveIndexRef.current !== activeIndex) {
|
|
321
|
-
onIndexChange?.(activeIndex);
|
|
322
|
-
prevActiveIndexRef.current = activeIndex;
|
|
323
|
-
}
|
|
324
|
-
}, [activeIndex, onIndexChange]);
|
|
325
|
-
useEffect(() => {
|
|
326
|
-
if (videos.length === 0) return;
|
|
327
|
-
if (!hasMore || isLoading) return;
|
|
328
|
-
const distanceToEnd = videos.length - 1 - activeIndex;
|
|
329
|
-
if (distanceToEnd <= endReachedThreshold) {
|
|
330
|
-
onEndReached?.();
|
|
331
|
-
}
|
|
332
|
-
}, [activeIndex, videos.length, hasMore, isLoading, endReachedThreshold, onEndReached]);
|
|
333
|
-
const { slots, isAtLastIndex, clampedActiveIndex } = useVideoFeedPosition({
|
|
334
|
-
totalCount: videos.length,
|
|
335
|
-
activeIndex,
|
|
336
|
-
containerHeight,
|
|
337
|
-
dragOffset,
|
|
338
|
-
isSwiping,
|
|
339
|
-
isResizing,
|
|
340
|
-
bufferSize
|
|
341
|
-
});
|
|
342
|
-
const contextValue = useMemo(
|
|
343
|
-
() => ({
|
|
344
|
-
videos,
|
|
345
|
-
activeIndex: clampedActiveIndex,
|
|
346
|
-
isSwiping,
|
|
347
|
-
dragOffset,
|
|
348
|
-
containerHeight,
|
|
349
|
-
goToIndex,
|
|
350
|
-
totalCount: videos.length
|
|
351
|
-
}),
|
|
352
|
-
[videos, clampedActiveIndex, isSwiping, dragOffset, containerHeight, goToIndex]
|
|
353
|
-
);
|
|
354
|
-
const renderSlots = useCallback(() => {
|
|
355
|
-
return slots.map((slot) => {
|
|
356
|
-
const video = videos[slot.index];
|
|
357
|
-
if (!video) return null;
|
|
358
|
-
const slotStyle = disableInlineTransforms ? { opacity: slot.opacity } : { transform: slot.transform, opacity: slot.opacity };
|
|
359
|
-
return /* @__PURE__ */ jsx(
|
|
360
|
-
"div",
|
|
361
|
-
{
|
|
362
|
-
className: cn(
|
|
363
|
-
"sv-video-feed__slot",
|
|
364
|
-
slot.isActive && "sv-video-feed__slot--active",
|
|
365
|
-
!slot.isActive && "sv-video-feed__slot--adjacent",
|
|
366
|
-
!isSwiping && "sv-video-feed__slot--transitioning"
|
|
367
|
-
),
|
|
368
|
-
style: slotStyle,
|
|
369
|
-
"data-slot-index": slot.index,
|
|
370
|
-
"data-slot-active": slot.isActive,
|
|
371
|
-
children: renderSlot(video, slot.index)
|
|
372
|
-
},
|
|
373
|
-
video.id
|
|
374
|
-
);
|
|
375
|
-
});
|
|
376
|
-
}, [slots, videos, isSwiping, renderSlot, disableInlineTransforms]);
|
|
377
|
-
const containerStyle = useMemo(() => {
|
|
378
|
-
const style = {};
|
|
379
|
-
if (height !== void 0) {
|
|
380
|
-
style.height = typeof height === "number" ? `${height}px` : height;
|
|
381
|
-
}
|
|
382
|
-
return style;
|
|
383
|
-
}, [height]);
|
|
384
|
-
if (isLoading && videos.length === 0) {
|
|
385
|
-
return /* @__PURE__ */ jsx("div", { ref: containerRef, className: cn("sv-video-feed", className), style: containerStyle, children: /* @__PURE__ */ jsxs("div", { className: "sv-video-feed__loading", children: [
|
|
386
|
-
/* @__PURE__ */ jsx("div", { className: "sv-video-feed__loading-spinner" }),
|
|
387
|
-
/* @__PURE__ */ jsx("div", { className: "sv-video-feed__loading-text", children: loadingText })
|
|
388
|
-
] }) });
|
|
389
|
-
}
|
|
390
|
-
if (!isLoading && videos.length === 0) {
|
|
391
|
-
return /* @__PURE__ */ jsx("div", { ref: containerRef, className: cn("sv-video-feed", className), style: containerStyle, children: /* @__PURE__ */ jsxs("div", { className: "sv-video-feed__empty", children: [
|
|
392
|
-
/* @__PURE__ */ jsx("div", { className: "sv-video-feed__empty-icon", children: "\u{1F4ED}" }),
|
|
393
|
-
/* @__PURE__ */ jsx("div", { className: "sv-video-feed__empty-text", children: emptyText })
|
|
394
|
-
] }) });
|
|
395
|
-
}
|
|
396
|
-
return /* @__PURE__ */ jsx(VideoFeedContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("sv-video-feed", className), style: containerStyle, children: [
|
|
397
|
-
/* @__PURE__ */ jsx(
|
|
398
|
-
"div",
|
|
399
|
-
{
|
|
400
|
-
className: cn(
|
|
401
|
-
"sv-video-feed__container",
|
|
402
|
-
isSwiping ? "sv-video-feed__container--swiping" : "sv-video-feed__container--snapping"
|
|
403
|
-
),
|
|
404
|
-
children: renderSlots()
|
|
405
|
-
}
|
|
406
|
-
),
|
|
407
|
-
isAtLastIndex && !hasMore && /* @__PURE__ */ jsx("div", { className: "sv-video-feed__end", "aria-live": "polite", children: endText })
|
|
408
|
-
] }) });
|
|
409
|
-
}
|
|
410
349
|
|
|
411
350
|
// src/components/VideoFeed/constants.ts
|
|
412
351
|
var SLOT_INDEX_ATTR = "data-slot-index";
|