@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.
Files changed (45) hide show
  1. package/dist/CommentSheet.css-BeCrEaUG.d.ts +221 -0
  2. package/dist/{chunk-2PTMP65P.js → chunk-2FSDVYER.js} +8 -9
  3. package/dist/{chunk-WKX2WBVO.js → chunk-3XPJHUYL.js} +1 -39
  4. package/dist/{chunk-ANGBSV7L.js → chunk-AC2IFAJR.js} +10 -5
  5. package/dist/{chunk-4YDIRPIN.js → chunk-ANCP53F3.js} +3 -3
  6. package/dist/chunk-AQHD6LPS.js +430 -0
  7. package/dist/{chunk-HW4LXTFT.js → chunk-CL6BS7GB.js} +7 -5
  8. package/dist/{chunk-YW23IBKF.js → chunk-ECR42RKK.js} +46 -5
  9. package/dist/chunk-EDWS2IPH.js +1 -0
  10. package/dist/chunk-FNXTPQ6L.js +2573 -0
  11. package/dist/{chunk-DHQJBXQW.js → chunk-KWHMZ6H5.js} +1 -1
  12. package/dist/{chunk-UXMA4KJZ.js → chunk-RMLTPW5S.js} +3 -2
  13. package/dist/{chunk-SSJDO24Q.js → chunk-SZXFH334.js} +1 -1
  14. package/dist/{chunk-4MN72OZH.js → chunk-UNV3NWN6.js} +4 -4
  15. package/dist/{chunk-ZZDQKP4R.js → chunk-WCRDTBCZ.js} +94 -155
  16. package/dist/{chunk-XAOEHLOX.js → chunk-XDIH66C4.js} +245 -52
  17. package/dist/components/ActionBar/index.js +1 -1
  18. package/dist/components/AuthorInfo/index.d.ts +5 -1
  19. package/dist/components/AuthorInfo/index.js +1 -1
  20. package/dist/components/BlurhashPlaceholder/index.d.ts +67 -0
  21. package/dist/components/BlurhashPlaceholder/index.js +150 -0
  22. package/dist/components/CommentSheet/index.d.ts +164 -0
  23. package/dist/components/CommentSheet/index.js +1 -0
  24. package/dist/components/ErrorBoundary/index.js +1 -1
  25. package/dist/components/OfflineIndicator/index.d.ts +56 -0
  26. package/dist/components/OfflineIndicator/index.js +151 -0
  27. package/dist/components/ProgressBar/index.d.ts +30 -2
  28. package/dist/components/ProgressBar/index.js +1 -1
  29. package/dist/components/Skeleton/index.js +1 -1
  30. package/dist/components/SubtitleDisplay/index.d.ts +94 -0
  31. package/dist/components/SubtitleDisplay/index.js +165 -0
  32. package/dist/components/VideoFeed/index.d.ts +11 -0
  33. package/dist/components/VideoFeed/index.js +1 -1
  34. package/dist/components/VideoInfo/index.js +1 -1
  35. package/dist/components/VideoPlayer/index.d.ts +14 -41
  36. package/dist/components/VideoPlayer/index.js +1 -1
  37. package/dist/components/VideoSlot/index.d.ts +124 -64
  38. package/dist/components/VideoSlot/index.js +1 -1
  39. package/dist/components/VirtualSlider/index.d.ts +339 -0
  40. package/dist/components/VirtualSlider/index.js +1 -0
  41. package/dist/components/icons/index.js +1 -1
  42. package/dist/index.d.ts +76 -93
  43. package/dist/index.js +75 -27
  44. package/package.json +53 -8
  45. package/dist/use-gesture-react.esm-3SV4QLEJ.js +0 -1893
@@ -1,4 +1,4 @@
1
- import { injectComponentCSS } from './chunk-UXMA4KJZ.js';
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 = css;
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,4 +1,4 @@
1
- import { injectComponentCSS } from './chunk-UXMA4KJZ.js';
1
+ import { injectComponentCSS } from './chunk-RMLTPW5S.js';
2
2
  import { useInsertionEffect, Component } from 'react';
3
3
  import { jsx, jsxs } from 'react/jsx-runtime';
4
4
 
@@ -1,5 +1,5 @@
1
- import { cn } from './chunk-WKX2WBVO.js';
2
- import { injectComponentCSS } from './chunk-UXMA4KJZ.js';
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: cn("sv-skeleton", animation !== "none" && `sv-skeleton--${animation}`, 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: cn("sv-skeleton-video", className), children: /* @__PURE__ */ jsx(Skeleton, { width: "100%", height: "100%", animation: "wave" }) });
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 { cn } from './chunk-WKX2WBVO.js';
2
- import { injectComponentCSS } from './chunk-UXMA4KJZ.js';
3
- import { createContext, useContext, useMemo, useInsertionEffect, useRef, useState, useEffect, useCallback } from 'react';
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";