@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,349 @@
1
+ import { PlusIcon } from './chunk-ANCP53F3.js';
2
+ import { injectComponentCSS } from './chunk-CAWE42LH.js';
3
+ import { clsx } from 'clsx';
4
+ import { createContext, useInsertionEffect, useMemo, useState, useEffect, useContext } from 'react';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
+
7
+ var AuthorInfoContext = createContext(null);
8
+ AuthorInfoContext.displayName = "AuthorInfoContext";
9
+ function useAuthorInfoContext() {
10
+ const context = useContext(AuthorInfoContext);
11
+ if (!context) {
12
+ throw new Error(
13
+ "[AuthorInfo] useAuthorInfoContext must be used within <AuthorInfoHeadless>. Make sure you wrap your component with <AuthorInfoHeadless>."
14
+ );
15
+ }
16
+ return context;
17
+ }
18
+ function useOptionalAuthorInfoContext() {
19
+ return useContext(AuthorInfoContext);
20
+ }
21
+ function DefaultCheckIcon() {
22
+ return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" }) });
23
+ }
24
+ function DefaultUserIcon() {
25
+ return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" }) });
26
+ }
27
+ var SIZE_CLASS_MAP = {
28
+ small: "sv-author-info__avatar--small",
29
+ large: "sv-author-info__avatar--large",
30
+ default: ""
31
+ };
32
+ function AuthorAvatar({
33
+ src,
34
+ alt,
35
+ size = "default",
36
+ showVerified = false,
37
+ verifiedIcon,
38
+ onClick,
39
+ className,
40
+ children
41
+ }) {
42
+ const [imgError, setImgError] = useState(false);
43
+ const context = useOptionalAuthorInfoContext();
44
+ const avatarSrc = src ?? context?.author.avatar;
45
+ const avatarAlt = alt ?? context?.author.name ?? "Author avatar";
46
+ const isVerified = context?.author.isVerified ?? false;
47
+ const handleClick = onClick ?? context?.openProfile;
48
+ useEffect(() => {
49
+ setImgError(false);
50
+ }, [avatarSrc]);
51
+ const handleImgError = () => setImgError(true);
52
+ const sizeClass = SIZE_CLASS_MAP[size];
53
+ const showImage = avatarSrc && !imgError;
54
+ const handleKeyDown = (e) => {
55
+ if ((e.key === "Enter" || e.key === " ") && handleClick) {
56
+ e.preventDefault();
57
+ e.stopPropagation();
58
+ handleClick();
59
+ }
60
+ };
61
+ return /* @__PURE__ */ jsxs(
62
+ "div",
63
+ {
64
+ className: clsx("sv-author-info__avatar", sizeClass, className),
65
+ onClick: (e) => {
66
+ e.stopPropagation();
67
+ handleClick?.();
68
+ },
69
+ onKeyDown: handleKeyDown,
70
+ role: handleClick ? "button" : void 0,
71
+ tabIndex: handleClick ? 0 : void 0,
72
+ "aria-label": handleClick ? `View ${avatarAlt}'s profile` : void 0,
73
+ children: [
74
+ children ?? (showImage && avatarSrc ? /* @__PURE__ */ jsx(
75
+ "img",
76
+ {
77
+ src: avatarSrc,
78
+ alt: avatarAlt,
79
+ className: "sv-author-info__avatar-img",
80
+ loading: "lazy",
81
+ onError: handleImgError
82
+ }
83
+ ) : /* @__PURE__ */ jsx("div", { className: "sv-author-info__avatar-placeholder", children: /* @__PURE__ */ jsx(DefaultUserIcon, {}) })),
84
+ showVerified && isVerified && /* @__PURE__ */ jsx("div", { className: "sv-author-info__verified-badge", "aria-label": "Verified", children: verifiedIcon ?? /* @__PURE__ */ jsx(DefaultCheckIcon, {}) })
85
+ ]
86
+ }
87
+ );
88
+ }
89
+ function AuthorDescription({
90
+ description,
91
+ singleLine = false,
92
+ className,
93
+ children
94
+ }) {
95
+ const context = useOptionalAuthorInfoContext();
96
+ const displayDescription = description ?? context?.author.description;
97
+ if (!children && !displayDescription) {
98
+ return null;
99
+ }
100
+ return /* @__PURE__ */ jsx(
101
+ "p",
102
+ {
103
+ className: clsx(
104
+ "sv-author-info__description",
105
+ singleLine && "sv-author-info__description--single-line",
106
+ className
107
+ ),
108
+ children: children ?? displayDescription
109
+ }
110
+ );
111
+ }
112
+
113
+ // src/components/AuthorInfo/AuthorInfo.css.ts
114
+ var AUTHOR_INFO_CSS = `.sv-author-info{display:flex;align-items:center;gap:var(--sv-author-gap,12px);width:100%}.sv-author-info--vertical{flex-direction:column;align-items:flex-start}.sv-author-info--horizontal{flex-direction:row;align-items:center}.sv-author-info--compact{gap:var(--sv-author-gap-compact,8px)}.sv-author-info__avatar{position:relative;flex-shrink:0;width:var(--sv-author-avatar-size,48px);height:var(--sv-author-avatar-size,48px);border-radius:50%;overflow:hidden;background:var(--sv-author-avatar-bg,rgba(255,255,255,.1));cursor:pointer;transition:transform .15s ease}.sv-author-info__avatar:hover{transform:scale(1.05)}.sv-author-info__avatar:active{transform:scale(.98)}.sv-author-info__avatar-img{width:100%;height:100%;object-fit:cover;border-radius:50%}.sv-author-info__avatar-placeholder{width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:var(--sv-author-avatar-placeholder-bg,linear-gradient(135deg,#667eea 0%,#764ba2 100%));color:var(--sv-author-avatar-placeholder-color,#fff);font-size:calc(var(--sv-author-avatar-size,48px)* .4);font-weight:600;text-transform:uppercase}.sv-author-info__avatar-placeholder svg{width:60%;height:60%;opacity:.9}.sv-author-info__avatar--small{width:var(--sv-author-avatar-size-small,32px);height:var(--sv-author-avatar-size-small,32px)}.sv-author-info__avatar--large{width:var(--sv-author-avatar-size-large,64px);height:var(--sv-author-avatar-size-large,64px)}.sv-author-info__verified-badge{position:absolute;bottom:-2px;right:-2px;width:calc(var(--sv-author-avatar-size,48px)* .35);height:calc(var(--sv-author-avatar-size,48px)* .35);min-width:14px;min-height:14px;border-radius:50%;background:var(--sv-verified-badge-bg,#1DA1F2);display:flex;align-items:center;justify-content:center;border:2px solid var(--sv-verified-badge-border,#000)}.sv-author-info__verified-badge svg{width:60%;height:60%;color:var(--sv-verified-badge-color,#fff)}.sv-author-info__content{display:flex;flex-direction:column;gap:var(--sv-author-content-gap,2px);flex:1;min-width:0}.sv-author-info__name{display:flex;align-items:center;gap:var(--sv-author-name-gap,4px);font-size:var(--sv-author-name-size,16px);font-weight:var(--sv-author-name-weight,600);color:var(--sv-author-name-color,#fff);cursor:pointer;transition:opacity .15s ease}.sv-author-info__name:hover{opacity:.8}.sv-author-info__name-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sv-author-info__name--with-at::before{content:'@';opacity:.8}.sv-author-info__name-verified{flex-shrink:0;width:var(--sv-author-name-verified-size,14px);height:var(--sv-author-name-verified-size,14px);color:var(--sv-verified-badge-bg,#1DA1F2)}.sv-author-info__description{font-size:var(--sv-author-desc-size,13px);color:var(--sv-author-desc-color,rgba(255,255,255,.7));line-height:var(--sv-author-desc-line-height,1.4);overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:var(--sv-author-desc-lines,2);-webkit-box-orient:vertical}.sv-author-info__description--single-line{-webkit-line-clamp:1;white-space:nowrap;display:block}.sv-author-info__follow-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--sv-follow-btn-gap,6px);padding:var(--sv-follow-btn-padding,8px 16px);min-width:var(--sv-follow-btn-min-width,88px);border:0;border-radius:var(--sv-follow-btn-radius,4px);font-size:var(--sv-follow-btn-size,14px);font-weight:var(--sv-follow-btn-weight,600);cursor:pointer;transition:all .2s ease;outline:none;user-select:none;-webkit-tap-highlight-color:transparent}.sv-author-info__follow-btn--default{background:var(--sv-follow-btn-bg,#fe2c55);color:var(--sv-follow-btn-color,#fff)}.sv-author-info__follow-btn--default:hover:not(:disabled){background:var(--sv-follow-btn-bg-hover,#f46);transform:scale(1.02)}.sv-author-info__follow-btn--default:active:not(:disabled){transform:scale(.98)}.sv-author-info__follow-btn--following{background:var(--sv-follow-btn-following-bg,transparent);color:var(--sv-follow-btn-following-color,rgba(255,255,255,.9));border:1px solid var(--sv-follow-btn-following-border,rgba(255,255,255,.3))}.sv-author-info__follow-btn--following:hover:not(:disabled){background:var(--sv-follow-btn-following-bg-hover,rgba(255,255,255,.1));border-color:var(--sv-follow-btn-following-border-hover,rgba(255,255,255,.5))}.sv-author-info__follow-btn--pending{opacity:.6;pointer-events:none;cursor:wait}.sv-author-info__follow-btn:disabled{opacity:.5;cursor:not-allowed}.sv-author-info__follow-btn-icon{width:var(--sv-follow-btn-icon-size,16px);height:var(--sv-follow-btn-icon-size,16px);flex-shrink:0}.sv-author-info__follow-btn-spinner{width:var(--sv-follow-btn-icon-size,16px);height:var(--sv-follow-btn-icon-size,16px);border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:sv-author-info-spin .6s linear infinite}@keyframes sv-author-info-spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}.sv-author-info__follow-btn--small{padding:var(--sv-follow-btn-padding-small,6px 12px);font-size:var(--sv-follow-btn-size-small,12px);min-width:var(--sv-follow-btn-min-width-small,72px)}.sv-author-info__follow-btn--large{padding:var(--sv-follow-btn-padding-large,10px 20px);font-size:var(--sv-follow-btn-size-large,16px);min-width:var(--sv-follow-btn-min-width-large,100px)}.sv-author-info__follow-btn--icon-only{min-width:unset;padding:var(--sv-follow-btn-icon-padding,8px);border-radius:50%}.sv-author-info--inline{justify-content:space-between}.sv-author-info--inline .sv-author-info__content{flex:1}.sv-author-info--stacked{flex-direction:column;align-items:center;text-align:center}.sv-author-info--stacked .sv-author-info__content{align-items:center}.sv-author-info--overlay{text-shadow:var(--sv-author-overlay-shadow,0 1px 2px rgba(0,0,0,.5))}.sv-author-info--overlay .sv-author-info__avatar{border:2px solid var(--sv-author-overlay-avatar-border,rgba(255,255,255,.5))}.sv-author-info--avatar-badge{position:relative;display:flex;flex-direction:column;align-items:center;width:auto;gap:0}.sv-author-info--avatar-badge .sv-author-info__avatar{width:var(--sv-avatar-badge-size,48px);height:var(--sv-avatar-badge-size,48px);border:2px solid var(--sv-avatar-badge-border,#fff)}.sv-author-info--avatar-badge .sv-author-info__follow-btn{position:absolute;bottom:var(--sv-avatar-badge-btn-bottom,-10px);left:50%;transform:translateX(-50%);min-width:unset;width:var(--sv-avatar-badge-btn-size,24px);height:var(--sv-avatar-badge-btn-size,24px);padding:0;border-radius:50%;background:var(--sv-color-primary,#fe2c55);border:2px solid var(--sv-bg-primary,#000);z-index:1}.sv-author-info--avatar-badge .sv-author-info__follow-btn:hover:not(:disabled){transform:translateX(-50%)scale(1.1)}.sv-author-info--avatar-badge .sv-author-info__follow-btn:active:not(:disabled){transform:translateX(-50%)scale(.95)}.sv-author-info--avatar-badge .sv-author-info__follow-btn-icon{width:var(--sv-avatar-badge-icon-size,14px);height:var(--sv-avatar-badge-icon-size,14px)}.sv-author-info--avatar-badge .sv-author-info__follow-btn--following{display:none}.sv-author-info--avatar-badge .sv-author-info__content{display:none}.sv-author-info--avatar-badge{margin-bottom:var(--sv-avatar-badge-margin,16px)}`;
115
+ function DefaultVerifiedIcon() {
116
+ return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M22.5 12.5c0-1.58-.875-2.95-2.148-3.6.154-.435.238-.905.238-1.4 0-2.21-1.71-3.998-3.818-3.998-.47 0-.92.084-1.336.25C14.818 2.415 13.51 1.5 12 1.5s-2.816.917-3.437 2.25c-.415-.165-.866-.25-1.336-.25-2.11 0-3.818 1.79-3.818 4 0 .494.083.964.237 1.4-1.272.65-2.147 2.018-2.147 3.6 0 1.495.782 2.798 1.942 3.486-.02.17-.032.34-.032.514 0 2.21 1.708 4 3.818 4 .47 0 .92-.086 1.335-.25.62 1.334 1.926 2.25 3.437 2.25 1.512 0 2.818-.916 3.437-2.25.415.163.865.248 1.336.248 2.11 0 3.818-1.79 3.818-4 0-.174-.012-.344-.033-.513 1.158-.687 1.943-1.99 1.943-3.484zm-6.616-3.334l-4.334 6.5c-.145.217-.382.334-.625.334-.143 0-.288-.04-.416-.126l-.115-.094-2.415-2.415c-.293-.293-.293-.768 0-1.06s.768-.294 1.06 0l1.77 1.767 3.825-5.74c.23-.345.696-.436 1.04-.207.346.23.44.696.21 1.04z" }) });
117
+ }
118
+ function AuthorName({
119
+ name,
120
+ showAtPrefix = false,
121
+ showVerified = false,
122
+ verifiedIcon,
123
+ onClick,
124
+ className,
125
+ children
126
+ }) {
127
+ const context = useOptionalAuthorInfoContext();
128
+ const displayName = name ?? context?.author.name ?? "Unknown";
129
+ const isVerified = context?.author.isVerified ?? false;
130
+ const handleClick = onClick ?? context?.openProfile;
131
+ const handleKeyDown = (e) => {
132
+ if ((e.key === "Enter" || e.key === " ") && handleClick) {
133
+ e.preventDefault();
134
+ e.stopPropagation();
135
+ handleClick();
136
+ }
137
+ };
138
+ return /* @__PURE__ */ jsxs(
139
+ "div",
140
+ {
141
+ className: clsx(
142
+ "sv-author-info__name",
143
+ showAtPrefix && "sv-author-info__name--with-at",
144
+ className
145
+ ),
146
+ onClick: (e) => {
147
+ e.stopPropagation();
148
+ handleClick?.();
149
+ },
150
+ onKeyDown: handleKeyDown,
151
+ role: handleClick ? "button" : void 0,
152
+ tabIndex: handleClick ? 0 : void 0,
153
+ "aria-label": handleClick ? `View ${displayName}'s profile` : void 0,
154
+ children: [
155
+ children ?? /* @__PURE__ */ jsx("span", { className: "sv-author-info__name-text", children: displayName }),
156
+ showVerified && isVerified && /* @__PURE__ */ jsx("span", { className: "sv-author-info__name-verified", "aria-label": "Verified", children: verifiedIcon ?? /* @__PURE__ */ jsx(DefaultVerifiedIcon, {}) })
157
+ ]
158
+ }
159
+ );
160
+ }
161
+ function DefaultFollowIcon() {
162
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", children: [
163
+ /* @__PURE__ */ jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" }),
164
+ /* @__PURE__ */ jsx("circle", { cx: "8.5", cy: "7", r: "4" }),
165
+ /* @__PURE__ */ jsx("line", { x1: "20", y1: "8", x2: "20", y2: "14" }),
166
+ /* @__PURE__ */ jsx("line", { x1: "23", y1: "11", x2: "17", y2: "11" })
167
+ ] });
168
+ }
169
+ function DefaultFollowingIcon() {
170
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", children: [
171
+ /* @__PURE__ */ jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" }),
172
+ /* @__PURE__ */ jsx("circle", { cx: "8.5", cy: "7", r: "4" }),
173
+ /* @__PURE__ */ jsx("polyline", { points: "17 11 19 13 23 9" })
174
+ ] });
175
+ }
176
+ var SIZE_CLASS_MAP2 = {
177
+ small: "sv-author-info__follow-btn--small",
178
+ default: "",
179
+ large: "sv-author-info__follow-btn--large"
180
+ };
181
+ function ButtonIcon({
182
+ isPending,
183
+ isFollowing,
184
+ followIcon,
185
+ followingIcon
186
+ }) {
187
+ if (isPending) {
188
+ return /* @__PURE__ */ jsx("span", { className: "sv-author-info__follow-btn-spinner" });
189
+ }
190
+ const icon = isFollowing ? followingIcon ?? /* @__PURE__ */ jsx(DefaultFollowingIcon, {}) : followIcon ?? /* @__PURE__ */ jsx(DefaultFollowIcon, {});
191
+ return /* @__PURE__ */ jsx("span", { className: "sv-author-info__follow-btn-icon", children: icon });
192
+ }
193
+ function FollowButton({
194
+ isFollowing: isFollowingProp,
195
+ isPending: isPendingProp,
196
+ onClick,
197
+ size = "default",
198
+ iconOnly = false,
199
+ followIcon,
200
+ followingIcon,
201
+ followText = "Follow",
202
+ followingText = "Following",
203
+ disabled = false,
204
+ className,
205
+ children,
206
+ followAriaLabel = "Follow",
207
+ unfollowAriaLabel = "Unfollow"
208
+ }) {
209
+ const context = useOptionalAuthorInfoContext();
210
+ const isFollowing = isFollowingProp ?? context?.isFollowing ?? false;
211
+ const isPending = isPendingProp ?? context?.isFollowPending ?? false;
212
+ const handleClick = onClick ?? context?.toggleFollow;
213
+ const isDisabled = disabled || isPending;
214
+ const stateClass = isFollowing ? "sv-author-info__follow-btn--following" : "sv-author-info__follow-btn--default";
215
+ const buttonText = isFollowing ? followingText : followText;
216
+ const handleButtonClick = (e) => {
217
+ e.stopPropagation();
218
+ e.preventDefault();
219
+ if (!isDisabled) {
220
+ handleClick?.();
221
+ }
222
+ };
223
+ const handleKeyDown = (e) => {
224
+ if ((e.key === "Enter" || e.key === " ") && !isDisabled) {
225
+ e.preventDefault();
226
+ e.stopPropagation();
227
+ handleClick?.();
228
+ }
229
+ };
230
+ const stopPropagation = (e) => e.stopPropagation();
231
+ return /* @__PURE__ */ jsx(
232
+ "button",
233
+ {
234
+ type: "button",
235
+ className: clsx(
236
+ "sv-author-info__follow-btn",
237
+ stateClass,
238
+ isPending && "sv-author-info__follow-btn--pending",
239
+ iconOnly && "sv-author-info__follow-btn--icon-only",
240
+ SIZE_CLASS_MAP2[size],
241
+ className
242
+ ),
243
+ onClick: handleButtonClick,
244
+ onKeyDown: handleKeyDown,
245
+ onPointerDown: stopPropagation,
246
+ onPointerUp: stopPropagation,
247
+ onTouchStart: stopPropagation,
248
+ onTouchEnd: stopPropagation,
249
+ disabled: isDisabled,
250
+ "aria-pressed": isFollowing,
251
+ "aria-label": isFollowing ? unfollowAriaLabel : followAriaLabel,
252
+ "aria-busy": isPending,
253
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
254
+ /* @__PURE__ */ jsx(
255
+ ButtonIcon,
256
+ {
257
+ isPending,
258
+ isFollowing,
259
+ followIcon: followIcon ?? /* @__PURE__ */ jsx(PlusIcon, { size: 14 }),
260
+ followingIcon
261
+ }
262
+ ),
263
+ !iconOnly && /* @__PURE__ */ jsx("span", { children: buttonText })
264
+ ] })
265
+ }
266
+ );
267
+ }
268
+ var CSS_COMPONENT_ID = "author-info";
269
+ function AuthorInfoContent({
270
+ className,
271
+ children
272
+ }) {
273
+ return /* @__PURE__ */ jsx("div", { className: clsx("sv-author-info__content", className), children });
274
+ }
275
+ function AuthorInfoHeadlessRoot({
276
+ authorState,
277
+ authorActions,
278
+ showFollowButton = true,
279
+ variant = "horizontal",
280
+ overlay = false,
281
+ className,
282
+ children
283
+ }) {
284
+ useInsertionEffect(() => {
285
+ return injectComponentCSS(CSS_COMPONENT_ID, AUTHOR_INFO_CSS);
286
+ }, []);
287
+ const contextValue = useMemo(
288
+ () => ({
289
+ author: authorState.author,
290
+ isFollowing: authorState.isFollowing,
291
+ isFollowPending: authorState.isFollowPending,
292
+ toggleFollow: () => {
293
+ authorActions.toggleFollow();
294
+ },
295
+ openProfile: () => {
296
+ authorActions.openProfile();
297
+ }
298
+ }),
299
+ [authorState, authorActions]
300
+ );
301
+ const variantClass = `sv-author-info--${variant}`;
302
+ const stopPropagation = (e) => {
303
+ e.stopPropagation();
304
+ };
305
+ return /* @__PURE__ */ jsx(AuthorInfoContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
306
+ "div",
307
+ {
308
+ className: clsx(
309
+ "sv-author-info",
310
+ variantClass,
311
+ overlay && "sv-author-info--overlay",
312
+ className
313
+ ),
314
+ onClick: stopPropagation,
315
+ onPointerDown: stopPropagation,
316
+ onPointerUp: stopPropagation,
317
+ onTouchStart: stopPropagation,
318
+ onTouchEnd: stopPropagation,
319
+ children: children ? children : variant === "avatar-badge" ? (
320
+ // Default layout for avatar-badge variant
321
+ /* @__PURE__ */ jsxs(Fragment, { children: [
322
+ /* @__PURE__ */ jsx(AuthorAvatar, {}),
323
+ showFollowButton && /* @__PURE__ */ jsx(FollowButton, { iconOnly: true })
324
+ ] })
325
+ ) : (
326
+ // Default layout when no children provided
327
+ /* @__PURE__ */ jsxs(Fragment, { children: [
328
+ /* @__PURE__ */ jsx(AuthorAvatar, { showVerified: true }),
329
+ /* @__PURE__ */ jsx(AuthorInfoContent, { children: /* @__PURE__ */ jsx(AuthorName, { showAtPrefix: true, showVerified: true }) }),
330
+ showFollowButton && /* @__PURE__ */ jsx(FollowButton, {})
331
+ ] })
332
+ )
333
+ }
334
+ ) });
335
+ }
336
+ var AuthorInfoHeadless = Object.assign(AuthorInfoHeadlessRoot, {
337
+ /** Avatar sub-component */
338
+ Avatar: AuthorAvatar,
339
+ /** Name sub-component */
340
+ Name: AuthorName,
341
+ /** Description/bio sub-component */
342
+ Description: AuthorDescription,
343
+ /** Follow button sub-component */
344
+ FollowButton,
345
+ /** Content wrapper for name + description */
346
+ Content: AuthorInfoContent
347
+ });
348
+
349
+ export { AUTHOR_INFO_CSS, AuthorAvatar, AuthorDescription, AuthorInfoContext, AuthorInfoHeadless, AuthorName, FollowButton, useAuthorInfoContext, useOptionalAuthorInfoContext };
@@ -1,30 +1,3 @@
1
- // src/utils/lazyGesture.ts
2
- var gestureModule = null;
3
- var loadPromise = null;
4
- async function loadGesture() {
5
- if (gestureModule) {
6
- return gestureModule;
7
- }
8
- if (loadPromise) {
9
- return loadPromise;
10
- }
11
- loadPromise = import('./use-gesture-react.esm-3SV4QLEJ.js').then((mod) => {
12
- gestureModule = mod;
13
- return mod;
14
- }).catch((error) => {
15
- loadPromise = null;
16
- throw error;
17
- });
18
- return loadPromise;
19
- }
20
- function isGestureLoaded() {
21
- return gestureModule !== null;
22
- }
23
- function preloadGesture() {
24
- loadGesture().catch(() => {
25
- });
26
- }
27
-
28
1
  // src/utils/formatCount.ts
29
2
  var DEFAULT_SUFFIXES = {
30
3
  thousand: "K",
@@ -83,16 +56,5 @@ function parseFormattedCount(formatted) {
83
56
  return num;
84
57
  }
85
58
  }
86
- function cn(...inputs) {
87
- return inputs.filter(Boolean).map((input) => {
88
- if (typeof input === "string") {
89
- return input;
90
- }
91
- if (typeof input === "object" && input !== null) {
92
- return Object.entries(input).filter(([, value]) => value).map(([key]) => key).join(" ");
93
- }
94
- return "";
95
- }).join(" ").trim();
96
- }
97
59
 
98
- export { cn, formatCount, formatCountWithSeparators, isGestureLoaded, loadGesture, parseFormattedCount, preloadGesture };
60
+ export { formatCount, formatCountWithSeparators, parseFormattedCount };
@@ -0,0 +1,58 @@
1
+ import { clsx2 } from './chunk-EDWS2IPH.js';
2
+ import { injectComponentCSS } from './chunk-CAWE42LH.js';
3
+ import { useInsertionEffect } from 'react';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ // src/components/Skeleton/Skeleton.css.ts
7
+ var SKELETON_CSS = `.sv-skeleton{display:block;background:var(--sv-skeleton-bg,rgba(255,255,255,.1));border-radius:var(--sv-border-radius-sm,4px)}.sv-skeleton--pulse{animation:sv-skeleton-pulse 1.5s ease-in-out infinite}@keyframes sv-skeleton-pulse{0%,100%{opacity:1}50%{opacity:.4}}.sv-skeleton--wave{position:relative;overflow:hidden}.sv-skeleton--wave::after{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(90deg,transparent,rgba(255,255,255,.1),transparent);animation:sv-skeleton-wave 1.5s ease-in-out infinite}@keyframes sv-skeleton-wave{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}}.sv-skeleton-video{width:100%;height:100%;background:var(--sv-bg-primary,#000)}:root[data-theme="light"] .sv-skeleton,.light .sv-skeleton{background:var(--sv-skeleton-bg-light,rgba(0,0,0,.1))}:root[data-theme="light"] .sv-skeleton--wave::after,.light .sv-skeleton--wave::after{background:linear-gradient(90deg,transparent,rgba(0,0,0,.05),transparent)}`;
8
+ function Skeleton({
9
+ width,
10
+ height,
11
+ borderRadius,
12
+ animation = "pulse",
13
+ circle = false,
14
+ className
15
+ }) {
16
+ useInsertionEffect(() => {
17
+ return injectComponentCSS("skeleton", SKELETON_CSS);
18
+ }, []);
19
+ const style = {};
20
+ if (width !== void 0) {
21
+ style.width = typeof width === "number" ? `${width}px` : width;
22
+ }
23
+ if (height !== void 0) {
24
+ style.height = typeof height === "number" ? `${height}px` : height;
25
+ }
26
+ if (borderRadius !== void 0) {
27
+ style.borderRadius = typeof borderRadius === "number" ? `${borderRadius}px` : borderRadius;
28
+ } else if (circle) {
29
+ style.borderRadius = "50%";
30
+ }
31
+ return /* @__PURE__ */ jsx(
32
+ "div",
33
+ {
34
+ className: clsx2("sv-skeleton", animation !== "none" && `sv-skeleton--${animation}`, className),
35
+ style,
36
+ "aria-hidden": "true"
37
+ }
38
+ );
39
+ }
40
+ function SkeletonVideo({
41
+ className
42
+ }) {
43
+ return /* @__PURE__ */ jsx("div", { className: clsx2("sv-skeleton-video", className), children: /* @__PURE__ */ jsx(Skeleton, { width: "100%", height: "100%", animation: "wave" }) });
44
+ }
45
+ function SkeletonAvatar({
46
+ size = 40,
47
+ className
48
+ }) {
49
+ return /* @__PURE__ */ jsx(Skeleton, { width: size, height: size, circle: true, className });
50
+ }
51
+ function SkeletonText({
52
+ width = "100%",
53
+ className
54
+ }) {
55
+ return /* @__PURE__ */ jsx(Skeleton, { width, height: 14, borderRadius: 4, className });
56
+ }
57
+
58
+ export { Skeleton, SkeletonAvatar, SkeletonText, SkeletonVideo };
@@ -0,0 +1,180 @@
1
+ import { VirtualSlider } from './chunk-YB7AXTX7.js';
2
+ import { clsx2 } from './chunk-EDWS2IPH.js';
3
+ import { injectComponentCSS } from './chunk-CAWE42LH.js';
4
+ import { createContext, useContext, useInsertionEffect, useRef, useMemo, useCallback } from 'react';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
+
7
+ // src/components/VideoFeed/VideoFeed.css.ts
8
+ var VIDEO_FEED_CSS = `.sv-video-feed{position:relative;width:100%;height:100%;overflow:hidden;background:var(--sv-bg-primary,#000);touch-action:pan-y;user-select:none;-webkit-user-select:none}.sv-video-feed__container{position:relative;width:100%;height:100%;will-change:transform}.sv-video-feed__container--snapping{transition:transform .3s cubic-bezier(.25,.46,.45,.94)}.sv-video-feed__container--swiping{transition:none}.sv-video-feed__slot{position:absolute;top:0;left:0;width:100%;height:100%;will-change:transform,opacity;backface-visibility:hidden;-webkit-backface-visibility:hidden}.sv-video-feed__slot--active{z-index:2}.sv-video-feed__slot--adjacent{z-index:1;pointer-events:none}.sv-video-feed__slot--transitioning{transition:transform .175s cubic-bezier(.25,.46,.45,.94),opacity .2s ease}.sv-video-feed__loading{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:flex;flex-direction:column;align-items:center;gap:var(--sv-spacing-md,16px);color:var(--sv-text-secondary,#999)}.sv-video-feed__loading-spinner{width:32px;height:32px;border:3px solid rgba(255,255,255,.2);border-top-color:var(--sv-color-primary,#fe2c55);border-radius:50%;animation:sv-feed-spinner .8s linear infinite}@keyframes sv-feed-spinner{to{transform:rotate(360deg)}}.sv-video-feed__loading-text{font-size:var(--sv-font-size-sm,13px);font-family:var(--sv-font-family,'Urbanist',sans-serif)}.sv-video-feed__empty{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;color:var(--sv-text-secondary,#999);font-family:var(--sv-font-family,'Urbanist',sans-serif)}.sv-video-feed__empty-icon{font-size:48px;margin-bottom:var(--sv-spacing-md,16px)}.sv-video-feed__empty-text{font-size:var(--sv-font-size-md,14px)}.sv-video-feed__end{position:absolute;bottom:calc(var(--sv-spacing-xl,32px)+env(safe-area-inset-bottom,0));left:50%;transform:translateX(-50%);z-index:10;padding:var(--sv-spacing-sm,8px)var(--sv-spacing-md,16px);background:rgba(0,0,0,.7);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border-radius:var(--sv-border-radius-md,8px);color:var(--sv-text-secondary,#999);font-size:var(--sv-font-size-sm,13px);font-family:var(--sv-font-family,'Urbanist',sans-serif);pointer-events:none;animation:sv-feed-end-fade-in .3s ease-out}@keyframes sv-feed-end-fade-in{from{opacity:0;transform:translateX(-50%)translateY(10px)}to{opacity:1;transform:translateX(-50%)translateY(0)}}@media(prefers-reduced-motion:reduce){.sv-video-feed__container--snapping{transition:none}.sv-video-feed__loading-spinner{animation:none}}`;
9
+ var VideoFeedContext = createContext(null);
10
+ function useVideoFeedContext() {
11
+ const context = useContext(VideoFeedContext);
12
+ if (!context) {
13
+ throw new Error("useVideoFeedContext must be used within a VideoFeed");
14
+ }
15
+ return context;
16
+ }
17
+ function useOptionalVideoFeedContext() {
18
+ return useContext(VideoFeedContext);
19
+ }
20
+ function VideoFeedLoading({ text = "Loading..." }) {
21
+ return /* @__PURE__ */ jsxs("div", { className: "sv-video-feed__loading", children: [
22
+ /* @__PURE__ */ jsx("div", { className: "sv-video-feed__loading-spinner" }),
23
+ /* @__PURE__ */ jsx("div", { className: "sv-video-feed__loading-text", children: text })
24
+ ] });
25
+ }
26
+ function VideoFeedEmpty({ text = "No videos" }) {
27
+ return /* @__PURE__ */ jsxs("div", { className: "sv-video-feed__empty", children: [
28
+ /* @__PURE__ */ jsx("div", { className: "sv-video-feed__empty-icon", children: "\u{1F4ED}" }),
29
+ /* @__PURE__ */ jsx("div", { className: "sv-video-feed__empty-text", children: text })
30
+ ] });
31
+ }
32
+ function VideoFeedEnd({ text = "You've reached the end" }) {
33
+ return /* @__PURE__ */ jsx("div", { className: "sv-video-feed__end", "aria-live": "polite", children: text });
34
+ }
35
+ function VideoFeedHeadless({
36
+ feedState,
37
+ swipeState,
38
+ height,
39
+ className,
40
+ renderSlot,
41
+ onIndexChange,
42
+ onEndReached,
43
+ endReachedThreshold = 2,
44
+ bufferSize = 1,
45
+ loadingText = "Loading...",
46
+ emptyText = "No videos",
47
+ endText = "You've reached the end",
48
+ disableInlineTransforms = false
49
+ }) {
50
+ useInsertionEffect(() => {
51
+ return injectComponentCSS("sv-video-feed", VIDEO_FEED_CSS);
52
+ }, []);
53
+ const { videos, isLoading, hasMore } = feedState;
54
+ const { activeIndex, isSwiping, dragOffset, goToIndex } = swipeState;
55
+ const containerHeightRef = useRef(
56
+ typeof window !== "undefined" ? window.innerHeight : 800
57
+ );
58
+ const contextValue = useMemo(
59
+ () => ({
60
+ videos,
61
+ activeIndex: Math.max(0, Math.min(videos.length - 1, activeIndex)),
62
+ isSwiping,
63
+ dragOffset,
64
+ containerHeight: containerHeightRef.current,
65
+ goToIndex,
66
+ totalCount: videos.length
67
+ }),
68
+ [videos, activeIndex, isSwiping, dragOffset, goToIndex]
69
+ );
70
+ const keyExtractor = useCallback((video) => video.id, []);
71
+ const renderItem = useCallback(
72
+ (video, index, isActive) => {
73
+ return renderSlot(video, index, isActive);
74
+ },
75
+ [renderSlot]
76
+ );
77
+ const loadingComponent = useMemo(() => /* @__PURE__ */ jsx(VideoFeedLoading, { text: loadingText }), [loadingText]);
78
+ const emptyComponent = useMemo(() => /* @__PURE__ */ jsx(VideoFeedEmpty, { text: emptyText }), [emptyText]);
79
+ const endComponent = useMemo(() => /* @__PURE__ */ jsx(VideoFeedEnd, { text: endText }), [endText]);
80
+ const sliderProps = {
81
+ // Required
82
+ items: videos,
83
+ keyExtractor,
84
+ renderItem,
85
+ // Swipe state
86
+ activeIndex,
87
+ isSwiping,
88
+ dragOffset,
89
+ goToIndex,
90
+ // Callbacks
91
+ onIndexChange,
92
+ onEndReached,
93
+ endReachedThreshold,
94
+ bufferSize,
95
+ // Loading states
96
+ isLoading,
97
+ hasMore,
98
+ // Custom UI (video-specific styling)
99
+ loadingComponent,
100
+ emptyComponent,
101
+ endComponent,
102
+ // Styling - use sv-video-feed classes instead of sv-slider
103
+ height,
104
+ className: clsx2("sv-video-feed", className),
105
+ disableInlineTransforms
106
+ };
107
+ return /* @__PURE__ */ jsx(VideoFeedContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(VirtualSlider, { ...sliderProps }) });
108
+ }
109
+ VideoFeedHeadless.displayName = "VideoFeedHeadless";
110
+ function useVideoFeedPosition(options) {
111
+ const {
112
+ totalCount,
113
+ activeIndex,
114
+ containerHeight,
115
+ dragOffset,
116
+ isSwiping,
117
+ isResizing = false,
118
+ bufferSize = 1
119
+ } = options;
120
+ return useMemo(() => {
121
+ if (totalCount === 0) {
122
+ return {
123
+ slots: [],
124
+ scrollOffset: 0,
125
+ isAtStart: true,
126
+ isAtLastIndex: true,
127
+ clampedActiveIndex: 0
128
+ };
129
+ }
130
+ const clampedActiveIndex = Math.max(0, Math.min(totalCount - 1, activeIndex));
131
+ const slots = [];
132
+ const startIndex = Math.max(0, clampedActiveIndex - bufferSize);
133
+ const endIndex = Math.min(totalCount - 1, clampedActiveIndex + bufferSize);
134
+ const baseOffset = -clampedActiveIndex * containerHeight;
135
+ const scrollOffset = baseOffset + dragOffset;
136
+ for (let i = startIndex; i <= endIndex; i++) {
137
+ const signedDistance = i - clampedActiveIndex;
138
+ const distance = Math.abs(signedDistance);
139
+ const isActive = i === clampedActiveIndex;
140
+ const top = i * containerHeight;
141
+ const transform = `translateY(${top + scrollOffset}px)`;
142
+ let opacity = 1;
143
+ if (!isActive && !isResizing) {
144
+ opacity = isSwiping ? 0.7 : 0.3;
145
+ }
146
+ slots.push({
147
+ index: i,
148
+ top,
149
+ isActive,
150
+ distance,
151
+ signedDistance,
152
+ transform,
153
+ opacity
154
+ });
155
+ }
156
+ return {
157
+ slots,
158
+ scrollOffset,
159
+ isAtStart: clampedActiveIndex === 0,
160
+ isAtLastIndex: clampedActiveIndex >= totalCount - 1,
161
+ clampedActiveIndex
162
+ };
163
+ }, [totalCount, activeIndex, containerHeight, dragOffset, isSwiping, isResizing, bufferSize]);
164
+ }
165
+ function getSlotIndexFromPosition(yPosition, containerHeight, totalCount) {
166
+ if (totalCount === 0 || containerHeight === 0) {
167
+ return 0;
168
+ }
169
+ const rawIndex = Math.round(-yPosition / containerHeight);
170
+ return Math.max(0, Math.min(totalCount - 1, rawIndex));
171
+ }
172
+
173
+ // src/components/VideoFeed/constants.ts
174
+ var SLOT_INDEX_ATTR = "data-slot-index";
175
+ var SLOT_ACTIVE_ATTR = "data-slot-active";
176
+ var SLOT_INDEX_DATASET_KEY = "slotIndex";
177
+ var SLOT_ACTIVE_DATASET_KEY = "slotActive";
178
+ var VIDEO_FEED_CLASS_PREFIX = "sv-video-feed";
179
+
180
+ export { SLOT_ACTIVE_ATTR, SLOT_ACTIVE_DATASET_KEY, SLOT_INDEX_ATTR, SLOT_INDEX_DATASET_KEY, VIDEO_FEED_CLASS_PREFIX, VIDEO_FEED_CSS, VideoFeedContext, VideoFeedHeadless, getSlotIndexFromPosition, useOptionalVideoFeedContext, useVideoFeedContext, useVideoFeedPosition };