@krizaka/ui 3.19.0 → 3.20.0
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/avatar/defaults.js +6 -1
- package/dist/avatar/defaults.js.map +1 -1
- package/dist/avatar.js +6 -1
- package/dist/avatar.js.map +1 -1
- package/dist/chat.js +1 -1
- package/dist/{chunk-276PTQMQ.js → chunk-3BC4XQ6C.js} +2 -1
- package/dist/{chunk-YIBKJ36L.js → chunk-KB7OLYIJ.js} +7 -2
- package/dist/{chunk-YIBKJ36L.js.map → chunk-KB7OLYIJ.js.map} +1 -1
- package/dist/data-table.js +1 -1
- package/dist/file-upload.js +2 -2
- package/dist/file-upload.js.map +1 -1
- package/dist/match-dialog.js +2 -2
- package/dist/media-grid.d.ts +66 -3
- package/dist/media-grid.js +312 -65
- package/dist/media-grid.js.map +1 -1
- package/dist/native.js +6 -1
- package/dist/native.js.map +1 -1
- package/dist/progress.js +2 -2
- package/dist/progress.js.map +1 -1
- package/dist/vertical-video.d.ts +7 -1
- package/dist/vertical-video.js +7 -5
- package/dist/vertical-video.js.map +1 -1
- package/package.json +6 -6
- package/registry/achievement.json +16 -2
- package/registry/action-bar.json +16 -2
- package/registry/action-sheet.json +15 -0
- package/registry/activity-feed.json +13 -2
- package/registry/alert.json +15 -2
- package/registry/app-shell.json +15 -2
- package/registry/argument-card.json +16 -2
- package/registry/audio-player.json +15 -2
- package/registry/auth-layout.json +15 -2
- package/registry/avatar.json +14 -3
- package/registry/badge.json +13 -2
- package/registry/button.json +13 -2
- package/registry/card-grid.json +16 -2
- package/registry/card.json +13 -2
- package/registry/catalogue-page.json +15 -2
- package/registry/changelog-page.json +15 -2
- package/registry/chat.json +15 -4
- package/registry/checkbox.json +13 -2
- package/registry/chip-link.json +15 -2
- package/registry/chip.json +13 -2
- package/registry/choice-grid.json +16 -2
- package/registry/cn.json +12 -0
- package/registry/code-block.json +15 -2
- package/registry/combobox.json +13 -2
- package/registry/command.json +15 -2
- package/registry/component-page.json +15 -2
- package/registry/confirm-button.json +15 -2
- package/registry/contact-page.json +15 -2
- package/registry/continue-prompt.json +16 -2
- package/registry/countdown.json +13 -2
- package/registry/cta-panel.json +16 -2
- package/registry/dashboard-layout.json +15 -2
- package/registry/data-table.json +15 -2
- package/registry/date-picker.json +13 -2
- package/registry/decision-list.json +16 -2
- package/registry/description-list.json +13 -2
- package/registry/detail-layout.json +15 -2
- package/registry/dialog.json +13 -2
- package/registry/docs-article-page.json +15 -2
- package/registry/docs-hub-page.json +15 -2
- package/registry/docs-layout.json +15 -2
- package/registry/document-viewer.json +15 -2
- package/registry/dropdown-menu.json +15 -2
- package/registry/empty-state.json +13 -2
- package/registry/error-page.json +14 -2
- package/registry/examples/chat/conversation.js +2 -2
- package/registry/examples/chat/conversation.tsx +4 -3
- package/registry/examples/glow-card/plans.js +9 -12
- package/registry/examples/glow-card/plans.tsx +18 -14
- package/registry/examples/media-grid/loading.d.ts +4 -0
- package/registry/examples/media-grid/loading.js +28 -0
- package/registry/examples/media-grid/loading.tsx +25 -0
- package/registry/examples/vertical-video/rail.js +3 -2
- package/registry/examples/vertical-video/rail.tsx +4 -3
- package/registry/fab.json +15 -0
- package/registry/field.json +13 -2
- package/registry/file-upload.json +15 -2
- package/registry/filter-bar.json +15 -2
- package/registry/form.json +13 -2
- package/registry/gallery-page.json +15 -2
- package/registry/glow-card.json +18 -4
- package/registry/goal-gauge.json +13 -2
- package/registry/header.json +15 -0
- package/registry/how-it-works-page.json +15 -2
- package/registry/image-cropper.json +15 -2
- package/registry/image-viewer.json +15 -0
- package/registry/index.json +1771 -8
- package/registry/iso.json +15 -2
- package/registry/kanban.json +15 -2
- package/registry/kbd.json +14 -2
- package/registry/landing-page.json +15 -2
- package/registry/landscape.json +15 -2
- package/registry/legal-page.json +15 -2
- package/registry/link-card.json +16 -2
- package/registry/list.json +13 -2
- package/registry/loader.json +13 -2
- package/registry/longform-page.json +15 -2
- package/registry/map.json +14 -3
- package/registry/markdown.json +13 -2
- package/registry/marketing-layout.json +15 -2
- package/registry/marquee.json +15 -2
- package/registry/mascot-quote.json +16 -2
- package/registry/mascot-state.json +13 -2
- package/registry/mascots.json +13 -2
- package/registry/match-dialog.json +13 -2
- package/registry/media-capture.json +13 -2
- package/registry/media-grid.json +341 -13
- package/registry/media-picker.json +15 -0
- package/registry/meter.json +13 -2
- package/registry/nav-bar.json +17 -3
- package/registry/number-ticker.json +13 -2
- package/registry/onboarding.json +15 -0
- package/registry/otp-input.json +15 -0
- package/registry/page-header.json +15 -2
- package/registry/page-hero.json +16 -2
- package/registry/page.json +16 -2
- package/registry/pagination.json +13 -2
- package/registry/permission-prompt.json +15 -0
- package/registry/photo-grid.json +13 -2
- package/registry/pointer-light.json +15 -2
- package/registry/popover.json +15 -2
- package/registry/pricing-page.json +15 -2
- package/registry/product-page.json +15 -2
- package/registry/profile-card.json +13 -2
- package/registry/progress.json +14 -3
- package/registry/promise-card.json +16 -2
- package/registry/pull-to-refresh.json +15 -0
- package/registry/radio-group.json +13 -2
- package/registry/recipes.json +14 -2
- package/registry/screen.json +15 -0
- package/registry/scroll-stage.json +15 -2
- package/registry/search-field.json +13 -2
- package/registry/section-backdrop.json +15 -2
- package/registry/segmented.json +15 -0
- package/registry/separator.json +15 -2
- package/registry/settings-layout.json +15 -2
- package/registry/showcase.json +13 -2
- package/registry/site-footer.json +16 -3
- package/registry/sites.json +15 -2
- package/registry/skeleton.json +13 -2
- package/registry/skip-link.json +14 -2
- package/registry/slider.json +13 -2
- package/registry/slot.json +14 -2
- package/registry/spinner.json +13 -2
- package/registry/splash-handoff.json +15 -0
- package/registry/split-view.json +15 -2
- package/registry/stage-banner.json +15 -2
- package/registry/stat.json +15 -2
- package/registry/status-page.json +15 -2
- package/registry/step-progress.json +13 -2
- package/registry/stepper.json +13 -2
- package/registry/swipe-deck.json +13 -2
- package/registry/swipeable-row.json +15 -0
- package/registry/switch.json +13 -2
- package/registry/tab-bar.json +15 -0
- package/registry/tabs.json +13 -2
- package/registry/tag-input.json +15 -2
- package/registry/tag-selector.json +13 -2
- package/registry/terminal-replay.json +15 -2
- package/registry/theme.json +13 -2
- package/registry/toast.json +13 -2
- package/registry/toc.json +15 -2
- package/registry/tooltip.json +14 -2
- package/registry/tree-view.json +15 -2
- package/registry/txt.json +13 -0
- package/registry/verified-badge.json +13 -2
- package/registry/vertical-video.json +32 -6
- package/registry/video-player.json +13 -2
- package/registry/video-trimmer.json +15 -2
- package/registry/virtual.json +15 -2
- /package/dist/{chunk-276PTQMQ.js.map → chunk-3BC4XQ6C.js.map} +0 -0
package/dist/media-grid.d.ts
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
+
interface LoadMoreProps extends Omit<React.ComponentProps<"div">, "children" | "onError"> {
|
|
4
|
+
/** Asks for the next page. Not called while `loading`, nor once `hasMore` is false. */
|
|
5
|
+
onLoadMore: () => void;
|
|
6
|
+
/** There is a next page. */
|
|
7
|
+
hasMore: boolean;
|
|
8
|
+
/** A page is on its way. */
|
|
9
|
+
loading?: boolean;
|
|
10
|
+
/** The last page failed: the button reads `retryLabel`, and nothing loads by itself until it is pressed. */
|
|
11
|
+
failed?: boolean;
|
|
12
|
+
/** The button, passed translated ("Load more"). */
|
|
13
|
+
label: string;
|
|
14
|
+
/** The wait, passed translated ("Loading more videos"). */
|
|
15
|
+
loadingLabel: string;
|
|
16
|
+
/** The button after a failure, passed translated ("Try again"). Defaults to `label`. */
|
|
17
|
+
retryLabel?: string;
|
|
18
|
+
/** Shown instead of the button when the list is over, passed translated ("You have seen everything"). Nothing if absent. */
|
|
19
|
+
endLabel?: string;
|
|
20
|
+
/**
|
|
21
|
+
* How far below the viewport the page is already requested (an IntersectionObserver `rootMargin`).
|
|
22
|
+
* @default "900px 0px"
|
|
23
|
+
*/
|
|
24
|
+
rootMargin?: string;
|
|
25
|
+
}
|
|
26
|
+
/** Infinite scroll with a visible "Load more" under it. */
|
|
27
|
+
declare function LoadMore({ onLoadMore, hasMore, loading, failed, label, loadingLabel, retryLabel, endLabel, rootMargin, className, ...props }: LoadMoreProps): React.JSX.Element;
|
|
28
|
+
|
|
3
29
|
/** The app's link component (Next's `Link`, React Router's `Link`…): receives `href`, `className` and the children. */
|
|
4
30
|
type LinkComponent = React.ElementType<{
|
|
5
31
|
href: string;
|
|
@@ -81,8 +107,22 @@ interface VideoCardProps extends Omit<React.ComponentProps<"article">, "title">
|
|
|
81
107
|
layout?: "stack" | "row";
|
|
82
108
|
/** The picture. */
|
|
83
109
|
poster?: string | null;
|
|
84
|
-
/**
|
|
110
|
+
/**
|
|
111
|
+
* An animated picture (WebP, a few hundred kB) that fades in over the poster once the pointer has rested on the card
|
|
112
|
+
* (or it is focused) for `previewDelay`, and is removed again on leave. It is never requested before. On a phone (no
|
|
113
|
+
* hover) only the card most inside the viewport plays it, unless the person asked for less data or less motion.
|
|
114
|
+
*/
|
|
85
115
|
previewSrc?: string | null;
|
|
116
|
+
/**
|
|
117
|
+
* How long the pointer must rest before the preview is requested, in ms: a pointer crossing the grid loads nothing.
|
|
118
|
+
* @default 200
|
|
119
|
+
*/
|
|
120
|
+
previewDelay?: number;
|
|
121
|
+
/**
|
|
122
|
+
* `fetchPriority` of the poster: `high` for the first row (the largest contentful paint).
|
|
123
|
+
* @default "auto"
|
|
124
|
+
*/
|
|
125
|
+
posterPriority?: "high" | "low" | "auto";
|
|
86
126
|
/**
|
|
87
127
|
* `cover` fills the frame; `contain` draws the whole picture over a blurred copy of itself (a source of the other
|
|
88
128
|
* orientation: no cropped face, no black bar).
|
|
@@ -109,7 +149,7 @@ interface VideoCardProps extends Omit<React.ComponentProps<"article">, "title">
|
|
|
109
149
|
loading?: "lazy" | "eager";
|
|
110
150
|
}
|
|
111
151
|
/** A video card whose frame, body and footer have one height per aspect, so a grid row or a shelf never staggers. */
|
|
112
|
-
declare function VideoCard({ title, href, linkAs, aspect, layout, poster, previewSrc, fit, duration, badges, creator, meta, action, fallback, blurred, overlay, loading, className, onMouseEnter, onMouseLeave, onFocus, onBlur, ...props }: VideoCardProps): React.JSX.Element;
|
|
152
|
+
declare function VideoCard({ title, href, linkAs, aspect, layout, poster, previewSrc, previewDelay, posterPriority, fit, duration, badges, creator, meta, action, fallback, blurred, overlay, loading, className, onMouseEnter, onMouseLeave, onFocus, onBlur, ref, ...props }: VideoCardProps): React.JSX.Element;
|
|
113
153
|
|
|
114
154
|
interface MediaGridState {
|
|
115
155
|
/** The frame the card is drawn in (`aspect` of `VideoCard`). */
|
|
@@ -143,4 +183,27 @@ interface MediaGridProps<T> extends Omit<React.ComponentProps<"div">, "children"
|
|
|
143
183
|
/** A media grid that never mixes heights badly: a shelf for the 9:16, a uniform grid for the rest. */
|
|
144
184
|
declare function MediaGrid<T>({ items, getId, orientationOf, renderItem, mode, frame, layout, shelf, className, ...props }: MediaGridProps<T>): React.JSX.Element;
|
|
145
185
|
|
|
146
|
-
|
|
186
|
+
interface VideoCardSkeletonProps extends React.ComponentProps<"div"> {
|
|
187
|
+
/** `landscape` (16:9) or `portrait` (9:16), as the card it stands for. @default "landscape" */
|
|
188
|
+
aspect?: VideoCardAspect;
|
|
189
|
+
/** `stack` or `row`, as the card it stands for. @default "stack" */
|
|
190
|
+
layout?: "stack" | "row";
|
|
191
|
+
}
|
|
192
|
+
/** The placeholder of a `VideoCard` (decorative: announce the wait on the list, with `MediaGridSkeleton`). */
|
|
193
|
+
declare function VideoCardSkeleton({ aspect, layout, className, ...props }: VideoCardSkeletonProps): React.JSX.Element;
|
|
194
|
+
interface MediaGridSkeletonProps extends Omit<React.ComponentProps<"div">, "children"> {
|
|
195
|
+
/** What is loading, passed translated ("Loading videos"): announced once, the cards are hidden from assistive technology. */
|
|
196
|
+
label: string;
|
|
197
|
+
/** Cards in the grid. @default 8 */
|
|
198
|
+
count?: number;
|
|
199
|
+
/** Cards on the Shorts shelf above the grid (`split` mode); 0 for none. @default 0 */
|
|
200
|
+
shelfCount?: number;
|
|
201
|
+
/** `grid` or `list`. @default "grid" */
|
|
202
|
+
layout?: "grid" | "list";
|
|
203
|
+
/** The frame of the grid. @default "landscape" */
|
|
204
|
+
frame?: VideoCardAspect;
|
|
205
|
+
}
|
|
206
|
+
/** The placeholder of a `MediaGrid`: the same shelf and grid, skeleton cards, one polite announcement. */
|
|
207
|
+
declare function MediaGridSkeleton({ label, count, shelfCount, layout, frame, className, ...props }: MediaGridSkeletonProps): React.JSX.Element;
|
|
208
|
+
|
|
209
|
+
export { LoadMore, type LoadMoreProps, MediaGrid, type MediaGridProps, MediaGridSkeleton, type MediaGridSkeletonProps, type MediaGridState, ShortsShelf, type ShortsShelfProps, VideoCard, type VideoCardAspect, type VideoCardCreator, type VideoCardProps, VideoCardSkeleton, type VideoCardSkeletonProps };
|
package/dist/media-grid.js
CHANGED
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
import "./chunk-NPF3OHE2.js";
|
|
3
3
|
import {
|
|
4
4
|
Avatar
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-KB7OLYIJ.js";
|
|
6
|
+
import "./chunk-JXLDUDTD.js";
|
|
7
|
+
import {
|
|
8
|
+
Button,
|
|
9
|
+
LoaderGlyph
|
|
10
|
+
} from "./chunk-3BC4XQ6C.js";
|
|
6
11
|
import {
|
|
7
12
|
defineRecipe
|
|
8
13
|
} from "./chunk-D3T54RSL.js";
|
|
@@ -10,11 +15,58 @@ import {
|
|
|
10
15
|
cn
|
|
11
16
|
} from "./chunk-2FRWNSRO.js";
|
|
12
17
|
|
|
13
|
-
// src/media-grid/
|
|
18
|
+
// src/media-grid/load-more.tsx
|
|
14
19
|
import * as React from "react";
|
|
15
20
|
|
|
16
|
-
// src/
|
|
21
|
+
// src/spinner/spinner.tsx
|
|
22
|
+
import { tv } from "tailwind-variants";
|
|
17
23
|
import { jsx } from "react/jsx-runtime";
|
|
24
|
+
var spinnerVariants = tv({
|
|
25
|
+
base: "shrink-0 text-accent",
|
|
26
|
+
variants: { size: { sm: "h-4 w-4", md: "h-6 w-6", lg: "h-10 w-10" } },
|
|
27
|
+
defaultVariants: { size: "md" }
|
|
28
|
+
});
|
|
29
|
+
function Spinner({ label, size, value, className, ...props }) {
|
|
30
|
+
return /* @__PURE__ */ jsx(
|
|
31
|
+
LoaderGlyph,
|
|
32
|
+
{
|
|
33
|
+
role: "status",
|
|
34
|
+
"aria-label": label,
|
|
35
|
+
"aria-hidden": void 0,
|
|
36
|
+
value,
|
|
37
|
+
"data-size": size ?? "md",
|
|
38
|
+
className: spinnerVariants({ size, className }),
|
|
39
|
+
...props
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/media-grid/load-more.tsx
|
|
45
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
46
|
+
function LoadMore({ onLoadMore, hasMore, loading = false, failed = false, label, loadingLabel, retryLabel, endLabel, rootMargin = "900px 0px", className, ...props }) {
|
|
47
|
+
const sentinel = React.useRef(null);
|
|
48
|
+
const ask = React.useRef(onLoadMore);
|
|
49
|
+
React.useEffect(() => {
|
|
50
|
+
ask.current = onLoadMore;
|
|
51
|
+
});
|
|
52
|
+
React.useEffect(() => {
|
|
53
|
+
const el = sentinel.current;
|
|
54
|
+
if (!el || !hasMore || loading || failed || typeof IntersectionObserver === "undefined") return;
|
|
55
|
+
const io = new IntersectionObserver((entries) => entries.some((e) => e.isIntersecting) && ask.current(), { rootMargin });
|
|
56
|
+
io.observe(el);
|
|
57
|
+
return () => io.disconnect();
|
|
58
|
+
}, [hasMore, loading, failed, rootMargin]);
|
|
59
|
+
return /* @__PURE__ */ jsxs("div", { "data-load-more": "", "data-state": loading ? "loading" : failed ? "failed" : hasMore ? "idle" : "end", className: cn("relative flex min-h-14 items-center justify-center py-6", className), ...props, children: [
|
|
60
|
+
/* @__PURE__ */ jsx2("div", { ref: sentinel, "aria-hidden": true, className: "pointer-events-none absolute inset-x-0 top-0 h-px" }),
|
|
61
|
+
loading ? /* @__PURE__ */ jsx2(Spinner, { label: loadingLabel, size: "md" }) : hasMore ? /* @__PURE__ */ jsx2(Button, { type: "button", variant: "secondary", size: "md", onClick: () => onLoadMore(), children: failed ? retryLabel ?? label : label }) : endLabel ? /* @__PURE__ */ jsx2("p", { className: "m-0 text-sm text-fg-secondary", children: endLabel }) : null
|
|
62
|
+
] });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/media-grid/shorts-shelf.tsx
|
|
66
|
+
import * as React2 from "react";
|
|
67
|
+
|
|
68
|
+
// src/block-link.tsx
|
|
69
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
18
70
|
var isExternal = (href) => /^[a-z][a-z0-9+.-]*:/i.test(href);
|
|
19
71
|
function BlockLink({
|
|
20
72
|
href,
|
|
@@ -23,38 +75,38 @@ function BlockLink({
|
|
|
23
75
|
rel,
|
|
24
76
|
...props
|
|
25
77
|
}) {
|
|
26
|
-
if (external) return /* @__PURE__ */
|
|
78
|
+
if (external) return /* @__PURE__ */ jsx3("a", { href, target: "_blank", rel: rel ?? "noopener noreferrer", ...props });
|
|
27
79
|
const Comp = Link;
|
|
28
|
-
return /* @__PURE__ */
|
|
80
|
+
return /* @__PURE__ */ jsx3(Comp, { href, ...props });
|
|
29
81
|
}
|
|
30
82
|
|
|
31
83
|
// src/media-grid/shorts-shelf.tsx
|
|
32
|
-
import { jsx as
|
|
33
|
-
var Chevron = ({ flip }) => /* @__PURE__ */
|
|
84
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
85
|
+
var Chevron = ({ flip }) => /* @__PURE__ */ jsx4("svg", { viewBox: "0 0 24 24", width: "18", height: "18", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, className: flip ? "rotate-180" : void 0, children: /* @__PURE__ */ jsx4("path", { d: "m9 6 6 6-6 6" }) });
|
|
34
86
|
var ARROW = "hidden h-9 w-9 items-center justify-center rounded-full border border-border-default bg-surface-1 text-fg shadow-sm transition-colors hover:bg-surface-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-40 [@media(hover:hover)]:inline-flex";
|
|
35
87
|
function ShortsShelf({ label, title, items, getId, renderItem, seeAll, previousLabel, nextLabel, className, ...props }) {
|
|
36
|
-
const row =
|
|
37
|
-
const [edge, setEdge] =
|
|
38
|
-
const measure =
|
|
88
|
+
const row = React2.useRef(null);
|
|
89
|
+
const [edge, setEdge] = React2.useState({ start: true, end: false });
|
|
90
|
+
const measure = React2.useCallback(() => {
|
|
39
91
|
const el = row.current;
|
|
40
92
|
if (!el) return;
|
|
41
93
|
setEdge({ start: el.scrollLeft <= 2, end: el.scrollLeft + el.clientWidth >= el.scrollWidth - 2 });
|
|
42
94
|
}, []);
|
|
43
|
-
|
|
95
|
+
React2.useEffect(measure, [measure, items.length]);
|
|
44
96
|
const page = (dir) => {
|
|
45
97
|
const el = row.current;
|
|
46
98
|
if (el) el.scrollBy({ left: dir * el.clientWidth * 0.85, behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth" });
|
|
47
99
|
};
|
|
48
|
-
const headingId =
|
|
100
|
+
const headingId = React2.useId();
|
|
49
101
|
if (items.length === 0) return null;
|
|
50
|
-
return /* @__PURE__ */
|
|
51
|
-
/* @__PURE__ */
|
|
52
|
-
/* @__PURE__ */
|
|
53
|
-
seeAll && /* @__PURE__ */
|
|
54
|
-
/* @__PURE__ */
|
|
55
|
-
/* @__PURE__ */
|
|
102
|
+
return /* @__PURE__ */ jsxs2("section", { "aria-labelledby": headingId, "data-shorts-shelf": true, className: cn("min-w-0", className), ...props, children: [
|
|
103
|
+
/* @__PURE__ */ jsxs2("div", { className: "mb-3 flex items-center gap-2", children: [
|
|
104
|
+
/* @__PURE__ */ jsx4("h2", { id: headingId, className: "m-0 mr-auto text-base font-bold text-fg", children: title ?? label }),
|
|
105
|
+
seeAll && /* @__PURE__ */ jsx4(BlockLink, { href: seeAll.href, linkAs: seeAll.linkAs, className: "text-sm font-semibold text-fg-accent hover:underline", children: seeAll.label }),
|
|
106
|
+
/* @__PURE__ */ jsx4("button", { type: "button", "aria-label": previousLabel, disabled: edge.start, onClick: () => page(-1), className: ARROW, children: /* @__PURE__ */ jsx4(Chevron, { flip: true }) }),
|
|
107
|
+
/* @__PURE__ */ jsx4("button", { type: "button", "aria-label": nextLabel, disabled: edge.end, onClick: () => page(1), className: ARROW, children: /* @__PURE__ */ jsx4(Chevron, {}) })
|
|
56
108
|
] }),
|
|
57
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ jsx4(
|
|
58
110
|
"ul",
|
|
59
111
|
{
|
|
60
112
|
ref: row,
|
|
@@ -62,14 +114,14 @@ function ShortsShelf({ label, title, items, getId, renderItem, seeAll, previousL
|
|
|
62
114
|
tabIndex: 0,
|
|
63
115
|
"aria-labelledby": headingId,
|
|
64
116
|
className: "m-0 flex list-none snap-x snap-mandatory items-stretch gap-3 overflow-x-auto overscroll-x-contain p-0 pb-2 [scrollbar-width:none] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&::-webkit-scrollbar]:hidden",
|
|
65
|
-
children: items.map((item, i) => /* @__PURE__ */
|
|
117
|
+
children: items.map((item, i) => /* @__PURE__ */ jsx4("li", { className: "w-[9.5rem] shrink-0 snap-start sm:w-44", children: renderItem(item, i) }, getId(item)))
|
|
66
118
|
}
|
|
67
119
|
)
|
|
68
120
|
] });
|
|
69
121
|
}
|
|
70
122
|
|
|
71
123
|
// src/media-grid/media-grid.tsx
|
|
72
|
-
import { jsx as
|
|
124
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
73
125
|
var GRID = {
|
|
74
126
|
landscape: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
|
|
75
127
|
portrait: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 xl:grid-cols-6"
|
|
@@ -81,18 +133,18 @@ function MediaGrid({ items, getId, orientationOf, renderItem, mode = "split", fr
|
|
|
81
133
|
const rest = split ? items.filter((i) => orientationOf(i) !== "portrait") : items;
|
|
82
134
|
const gridFrame = split ? "landscape" : frame;
|
|
83
135
|
const state = (item, aspect) => ({ aspect, layout: row ? "row" : "stack", mismatch: orientationOf(item) !== aspect });
|
|
84
|
-
return /* @__PURE__ */
|
|
85
|
-
verticals.length > 0 && /* @__PURE__ */
|
|
86
|
-
rest.length > 0 && /* @__PURE__ */
|
|
136
|
+
return /* @__PURE__ */ jsxs3("div", { "data-media-grid": mode, className: cn("flex min-w-0 flex-col gap-8", className), ...props, children: [
|
|
137
|
+
verticals.length > 0 && /* @__PURE__ */ jsx5(ShortsShelf, { ...shelf, items: verticals, getId, renderItem: (item) => renderItem(item, { ...state(item, "portrait"), layout: "stack" }) }),
|
|
138
|
+
rest.length > 0 && /* @__PURE__ */ jsx5("ul", { "data-grid-frame": gridFrame, className: cn("m-0 grid list-none auto-rows-fr gap-4 p-0", row ? "grid-cols-1" : GRID[gridFrame]), children: rest.map((item) => /* @__PURE__ */ jsx5("li", { className: "min-w-0", children: renderItem(item, state(item, gridFrame)) }, getId(item))) })
|
|
87
139
|
] });
|
|
88
140
|
}
|
|
89
141
|
|
|
90
142
|
// src/media-grid/video-card.tsx
|
|
91
|
-
import * as
|
|
143
|
+
import * as React3 from "react";
|
|
92
144
|
|
|
93
145
|
// src/card/card.tsx
|
|
94
146
|
import { Slot } from "radix-ui";
|
|
95
|
-
import { tv } from "tailwind-variants";
|
|
147
|
+
import { tv as tv2 } from "tailwind-variants";
|
|
96
148
|
|
|
97
149
|
// src/card/recipe.ts
|
|
98
150
|
var cardRecipe = defineRecipe({
|
|
@@ -120,13 +172,13 @@ var cardRecipe = defineRecipe({
|
|
|
120
172
|
});
|
|
121
173
|
|
|
122
174
|
// src/card/card.tsx
|
|
123
|
-
import { jsx as
|
|
124
|
-
var card =
|
|
175
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
176
|
+
var card = tv2(cardRecipe);
|
|
125
177
|
var slots = card();
|
|
126
178
|
function CardRoot({ asChild, reveal, radius, tone, interactive, className, style, ...props }) {
|
|
127
179
|
const Comp = asChild ? Slot.Root : "div";
|
|
128
180
|
const revealStyle = reveal === void 0 ? style : { ...style, "--kz-delay": `${Math.min(Math.max(reveal, 0), 8) * 50}ms` };
|
|
129
|
-
return /* @__PURE__ */
|
|
181
|
+
return /* @__PURE__ */ jsx6(
|
|
130
182
|
Comp,
|
|
131
183
|
{
|
|
132
184
|
"data-reveal": reveal === void 0 ? void 0 : "",
|
|
@@ -139,13 +191,13 @@ function CardRoot({ asChild, reveal, radius, tone, interactive, className, style
|
|
|
139
191
|
}
|
|
140
192
|
var ASPECT = { video: "aspect-video", square: "aspect-square", portrait: "aspect-[3/4]", auto: "" };
|
|
141
193
|
function CardMedia({ aspect = "video", className, ...props }) {
|
|
142
|
-
return /* @__PURE__ */
|
|
194
|
+
return /* @__PURE__ */ jsx6("div", { "data-aspect": aspect, className: slots.media({ className: cn(ASPECT[aspect], className) }), ...props });
|
|
143
195
|
}
|
|
144
196
|
function CardImage({ src, alt = "", fallback, loading = "lazy", className, ...props }) {
|
|
145
197
|
if (!src) {
|
|
146
|
-
return /* @__PURE__ */
|
|
198
|
+
return /* @__PURE__ */ jsx6("div", { "data-fallback": "", "aria-hidden": true, className: slots.fallback({ className }), children: fallback });
|
|
147
199
|
}
|
|
148
|
-
return /* @__PURE__ */
|
|
200
|
+
return /* @__PURE__ */ jsx6("img", { src, alt, loading, decoding: "async", className: slots.image({ className }), ...props });
|
|
149
201
|
}
|
|
150
202
|
var CORNER = {
|
|
151
203
|
"top-left": "left-2.5 top-2.5",
|
|
@@ -154,25 +206,25 @@ var CORNER = {
|
|
|
154
206
|
"bottom-right": "bottom-2.5 right-2.5"
|
|
155
207
|
};
|
|
156
208
|
function CardOverlay({ corner = "top-left", className, ...props }) {
|
|
157
|
-
return /* @__PURE__ */
|
|
209
|
+
return /* @__PURE__ */ jsx6("div", { "data-corner": corner, className: slots.overlay({ className: cn(CORNER[corner], className) }), ...props });
|
|
158
210
|
}
|
|
159
211
|
function CardBody({ padding, className, ...props }) {
|
|
160
|
-
return /* @__PURE__ */
|
|
212
|
+
return /* @__PURE__ */ jsx6("div", { className: card({ padding }).body({ className }), ...props });
|
|
161
213
|
}
|
|
162
214
|
function CardTitle({ as: Tag = "h3", className, ...props }) {
|
|
163
|
-
return /* @__PURE__ */
|
|
215
|
+
return /* @__PURE__ */ jsx6(Tag, { className: slots.title({ className }), ...props });
|
|
164
216
|
}
|
|
165
217
|
function CardDescription({ className, ...props }) {
|
|
166
|
-
return /* @__PURE__ */
|
|
218
|
+
return /* @__PURE__ */ jsx6("p", { className: slots.description({ className }), ...props });
|
|
167
219
|
}
|
|
168
220
|
function CardStat({ label, className, children, ...props }) {
|
|
169
|
-
return /* @__PURE__ */
|
|
170
|
-
/* @__PURE__ */
|
|
171
|
-
/* @__PURE__ */
|
|
221
|
+
return /* @__PURE__ */ jsxs4("div", { className: slots.stat({ className }), ...props, children: [
|
|
222
|
+
/* @__PURE__ */ jsx6("p", { className: slots.statLabel(), children: label }),
|
|
223
|
+
/* @__PURE__ */ jsx6("p", { className: slots.statValue(), children })
|
|
172
224
|
] });
|
|
173
225
|
}
|
|
174
226
|
function CardFooter({ className, ...props }) {
|
|
175
|
-
return /* @__PURE__ */
|
|
227
|
+
return /* @__PURE__ */ jsx6("div", { className: slots.footer({ className }), ...props });
|
|
176
228
|
}
|
|
177
229
|
var Card = {
|
|
178
230
|
Root: CardRoot,
|
|
@@ -186,8 +238,48 @@ var Card = {
|
|
|
186
238
|
Footer: CardFooter
|
|
187
239
|
};
|
|
188
240
|
|
|
241
|
+
// src/media-grid/preview-focus.ts
|
|
242
|
+
var MIN_RATIO = 0.6;
|
|
243
|
+
var ratios = /* @__PURE__ */ new Map();
|
|
244
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
245
|
+
var observer = null;
|
|
246
|
+
var leader = null;
|
|
247
|
+
function elect() {
|
|
248
|
+
let best = null;
|
|
249
|
+
let bestRatio = MIN_RATIO - Number.EPSILON;
|
|
250
|
+
for (const [el, ratio] of ratios) {
|
|
251
|
+
if (ratio > bestRatio) {
|
|
252
|
+
best = el;
|
|
253
|
+
bestRatio = ratio;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (best === leader) return;
|
|
257
|
+
leader = best;
|
|
258
|
+
for (const l of listeners) l();
|
|
259
|
+
}
|
|
260
|
+
function trackVisibility(el) {
|
|
261
|
+
if (typeof IntersectionObserver === "undefined") return () => {
|
|
262
|
+
};
|
|
263
|
+
observer ??= new IntersectionObserver(
|
|
264
|
+
(entries) => {
|
|
265
|
+
for (const e of entries) ratios.set(e.target, e.isIntersecting ? e.intersectionRatio : 0);
|
|
266
|
+
elect();
|
|
267
|
+
},
|
|
268
|
+
{ threshold: [0, 0.25, 0.5, 0.6, 0.75, 1] }
|
|
269
|
+
);
|
|
270
|
+
ratios.set(el, 0);
|
|
271
|
+
observer.observe(el);
|
|
272
|
+
return () => {
|
|
273
|
+
observer?.unobserve(el);
|
|
274
|
+
ratios.delete(el);
|
|
275
|
+
elect();
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
var subscribeLeader = (l) => (listeners.add(l), () => void listeners.delete(l));
|
|
279
|
+
var isLeader = (el) => el !== null && leader === el;
|
|
280
|
+
|
|
189
281
|
// src/media-grid/video-card.tsx
|
|
190
|
-
import { jsx as
|
|
282
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
191
283
|
var FRAME = { landscape: "aspect-video", portrait: "aspect-[9/16]" };
|
|
192
284
|
function VideoCard({
|
|
193
285
|
title,
|
|
@@ -197,6 +289,8 @@ function VideoCard({
|
|
|
197
289
|
layout = "stack",
|
|
198
290
|
poster,
|
|
199
291
|
previewSrc,
|
|
292
|
+
previewDelay = 200,
|
|
293
|
+
posterPriority = "auto",
|
|
200
294
|
fit = "cover",
|
|
201
295
|
duration,
|
|
202
296
|
badges,
|
|
@@ -212,41 +306,63 @@ function VideoCard({
|
|
|
212
306
|
onMouseLeave,
|
|
213
307
|
onFocus,
|
|
214
308
|
onBlur,
|
|
309
|
+
ref,
|
|
215
310
|
...props
|
|
216
311
|
}) {
|
|
217
|
-
const [
|
|
218
|
-
const
|
|
312
|
+
const [node, setNode] = React3.useState(null);
|
|
313
|
+
const setRef = React3.useCallback(
|
|
314
|
+
(el) => {
|
|
315
|
+
setNode(el);
|
|
316
|
+
if (typeof ref === "function") ref(el);
|
|
317
|
+
else if (ref) ref.current = el;
|
|
318
|
+
},
|
|
319
|
+
[ref]
|
|
320
|
+
);
|
|
321
|
+
const canPreview = Boolean(previewSrc) && !blurred;
|
|
322
|
+
const { live, arm, disarm } = usePreviewIntent(node, canPreview, previewDelay);
|
|
219
323
|
const row = layout === "row";
|
|
220
324
|
const tall = aspect === "portrait" && !row;
|
|
221
|
-
return /* @__PURE__ */
|
|
325
|
+
return /* @__PURE__ */ jsx7(
|
|
222
326
|
"article",
|
|
223
327
|
{
|
|
328
|
+
ref: setRef,
|
|
224
329
|
"data-aspect": aspect,
|
|
225
330
|
"data-layout": layout,
|
|
226
331
|
className: cn("group relative h-full min-w-0", className),
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
332
|
+
"data-preview": live ? "on" : void 0,
|
|
333
|
+
onMouseEnter: (e) => (arm("pointer"), onMouseEnter?.(e)),
|
|
334
|
+
onMouseLeave: (e) => (disarm("pointer"), onMouseLeave?.(e)),
|
|
335
|
+
onFocus: (e) => (arm("focus"), onFocus?.(e)),
|
|
336
|
+
onBlur: (e) => (disarm("focus"), onBlur?.(e)),
|
|
231
337
|
...props,
|
|
232
|
-
children: /* @__PURE__ */
|
|
233
|
-
/* @__PURE__ */
|
|
234
|
-
fit === "contain" &&
|
|
235
|
-
/* @__PURE__ */
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
338
|
+
children: /* @__PURE__ */ jsxs5(Card.Root, { interactive: true, tone: "glass", className: cn("h-full w-full max-w-full", row ? "flex-row" : "flex-col"), children: [
|
|
339
|
+
/* @__PURE__ */ jsxs5(Card.Media, { aspect: "auto", className: cn("shrink-0", FRAME[aspect], row && (aspect === "portrait" ? "w-24 sm:w-28" : "w-40 sm:w-56")), children: [
|
|
340
|
+
fit === "contain" && poster && /* @__PURE__ */ jsx7("img", { src: poster, alt: "", "aria-hidden": true, loading: "lazy", className: "absolute inset-0 h-full w-full scale-125 object-cover opacity-70 blur-2xl" }),
|
|
341
|
+
/* @__PURE__ */ jsx7(
|
|
342
|
+
Card.Image,
|
|
343
|
+
{
|
|
344
|
+
src: poster,
|
|
345
|
+
loading,
|
|
346
|
+
fetchPriority: posterPriority === "auto" ? void 0 : posterPriority,
|
|
347
|
+
fallback,
|
|
348
|
+
className: cn(fit === "contain" && "relative object-contain", blurred && "scale-110 blur-xl")
|
|
349
|
+
}
|
|
350
|
+
),
|
|
351
|
+
live && previewSrc && /* @__PURE__ */ jsx7(PreviewLayer, { src: previewSrc, contain: fit === "contain" }),
|
|
352
|
+
overlay && /* @__PURE__ */ jsx7("div", { className: "absolute inset-0 z-20 flex", children: overlay }),
|
|
353
|
+
badges && /* @__PURE__ */ jsx7(Card.Overlay, { corner: "top-left", className: "theme-dark pointer-events-none flex-wrap", children: badges }),
|
|
354
|
+
duration && /* @__PURE__ */ jsx7(Card.Overlay, { corner: "bottom-right", className: "pointer-events-none", children: /* @__PURE__ */ jsx7("span", { className: "rounded-sm bg-scrim px-1.5 py-0.5 font-mono text-[11px] text-fg-on-media", children: duration }) })
|
|
239
355
|
] }),
|
|
240
|
-
/* @__PURE__ */
|
|
241
|
-
/* @__PURE__ */
|
|
242
|
-
/* @__PURE__ */
|
|
243
|
-
creator && /* @__PURE__ */
|
|
244
|
-
/* @__PURE__ */
|
|
245
|
-
creator.href ? /* @__PURE__ */
|
|
356
|
+
/* @__PURE__ */ jsxs5(Card.Body, { className: "static min-w-0 flex-1 justify-between gap-2 p-3", children: [
|
|
357
|
+
/* @__PURE__ */ jsx7("h3", { className: "m-0 min-h-[2.5em] text-sm font-semibold leading-tight text-fg", children: /* @__PURE__ */ jsx7(BlockLink, { href, linkAs, className: "line-clamp-2 outline-none after:absolute after:inset-0 after:z-0 after:content-[''] focus-visible:after:ring-2 focus-visible:after:ring-ring", children: title }) }),
|
|
358
|
+
/* @__PURE__ */ jsxs5("div", { className: cn("relative z-10 text-xs text-fg-secondary", tall ? "flex flex-col gap-1" : "flex h-8 items-center gap-2"), children: [
|
|
359
|
+
creator && /* @__PURE__ */ jsxs5("div", { className: "flex h-6 min-w-0 items-center gap-2", children: [
|
|
360
|
+
/* @__PURE__ */ jsx7(Avatar, { src: creator.avatarSrc, defaultAvatar: creator.defaultAvatar, alt: "", fallback: creator.name.charAt(0), size: "xs", className: "shrink-0" }),
|
|
361
|
+
creator.href ? /* @__PURE__ */ jsx7(BlockLink, { href: creator.href, linkAs, className: "min-w-0 truncate hover:text-fg", children: creator.name }) : /* @__PURE__ */ jsx7("span", { className: "min-w-0 truncate", children: creator.name })
|
|
246
362
|
] }),
|
|
247
|
-
(meta || action) && /* @__PURE__ */
|
|
248
|
-
meta && /* @__PURE__ */
|
|
249
|
-
action && /* @__PURE__ */
|
|
363
|
+
(meta || action) && /* @__PURE__ */ jsxs5("div", { className: cn("flex items-center gap-2", tall ? "h-7 justify-between" : "ml-auto shrink-0"), children: [
|
|
364
|
+
meta && /* @__PURE__ */ jsx7("span", { className: "shrink-0 font-mono text-[11px] text-fg-muted", children: meta }),
|
|
365
|
+
action && /* @__PURE__ */ jsx7("span", { className: "shrink-0", children: action })
|
|
250
366
|
] })
|
|
251
367
|
] })
|
|
252
368
|
] })
|
|
@@ -254,9 +370,140 @@ function VideoCard({
|
|
|
254
370
|
}
|
|
255
371
|
);
|
|
256
372
|
}
|
|
373
|
+
function PreviewLayer({ src, contain }) {
|
|
374
|
+
const [ready, setReady] = React3.useState(false);
|
|
375
|
+
return /* @__PURE__ */ jsx7(
|
|
376
|
+
"img",
|
|
377
|
+
{
|
|
378
|
+
src,
|
|
379
|
+
alt: "",
|
|
380
|
+
"aria-hidden": true,
|
|
381
|
+
decoding: "async",
|
|
382
|
+
"data-preview-layer": "",
|
|
383
|
+
"data-ready": ready ? "" : void 0,
|
|
384
|
+
onLoad: () => setReady(true),
|
|
385
|
+
className: cn("pointer-events-none absolute inset-0 h-full w-full opacity-0 transition-opacity duration-300 data-[ready]:opacity-100 motion-reduce:transition-none", contain ? "object-contain" : "object-cover")
|
|
386
|
+
}
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
var matches = (query) => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(query).matches;
|
|
390
|
+
var hoverCapable = () => matches("(hover: hover)");
|
|
391
|
+
var saveData = () => typeof navigator !== "undefined" && navigator.connection?.saveData === true;
|
|
392
|
+
function usePreviewIntent(node, enabled, delay) {
|
|
393
|
+
const [causes, setCauses] = React3.useState(/* @__PURE__ */ new Set());
|
|
394
|
+
const timers = React3.useRef(/* @__PURE__ */ new Map());
|
|
395
|
+
const arm = React3.useCallback(
|
|
396
|
+
(cause) => {
|
|
397
|
+
if (!enabled || saveData()) return;
|
|
398
|
+
if (cause === "pointer" && !hoverCapable()) return;
|
|
399
|
+
clearTimeout(timers.current.get(cause));
|
|
400
|
+
timers.current.set(
|
|
401
|
+
cause,
|
|
402
|
+
setTimeout(() => setCauses((c) => new Set(c).add(cause)), delay)
|
|
403
|
+
);
|
|
404
|
+
},
|
|
405
|
+
[enabled, delay]
|
|
406
|
+
);
|
|
407
|
+
const disarm = React3.useCallback((cause) => {
|
|
408
|
+
clearTimeout(timers.current.get(cause));
|
|
409
|
+
timers.current.delete(cause);
|
|
410
|
+
setCauses((c) => {
|
|
411
|
+
if (!c.has(cause)) return c;
|
|
412
|
+
const next = new Set(c);
|
|
413
|
+
next.delete(cause);
|
|
414
|
+
return next;
|
|
415
|
+
});
|
|
416
|
+
}, []);
|
|
417
|
+
React3.useEffect(() => {
|
|
418
|
+
const pending = timers.current;
|
|
419
|
+
return () => {
|
|
420
|
+
for (const t of pending.values()) clearTimeout(t);
|
|
421
|
+
pending.clear();
|
|
422
|
+
};
|
|
423
|
+
}, []);
|
|
424
|
+
const touch = enabled && !hoverCapable();
|
|
425
|
+
const leading = React3.useSyncExternalStore(
|
|
426
|
+
subscribeLeader,
|
|
427
|
+
() => isLeader(node),
|
|
428
|
+
() => false
|
|
429
|
+
);
|
|
430
|
+
React3.useEffect(() => {
|
|
431
|
+
if (!node || !touch) return;
|
|
432
|
+
return trackVisibility(node);
|
|
433
|
+
}, [node, touch]);
|
|
434
|
+
const [ready, setReady] = React3.useState(false);
|
|
435
|
+
const [wasLeading, setWasLeading] = React3.useState(false);
|
|
436
|
+
if (leading !== wasLeading) {
|
|
437
|
+
setWasLeading(leading);
|
|
438
|
+
if (!leading) setReady(false);
|
|
439
|
+
}
|
|
440
|
+
const wanted = touch && leading && !saveData() && !matches("(prefers-reduced-motion: reduce)");
|
|
441
|
+
React3.useEffect(() => {
|
|
442
|
+
if (!wanted) return;
|
|
443
|
+
const t = setTimeout(() => setReady(true), delay);
|
|
444
|
+
return () => clearTimeout(t);
|
|
445
|
+
}, [wanted, delay]);
|
|
446
|
+
return { live: enabled && (causes.size > 0 || wanted && ready), arm, disarm };
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// src/skeleton/skeleton.tsx
|
|
450
|
+
import { tv as tv3 } from "tailwind-variants";
|
|
451
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
452
|
+
var skeletonVariants = tv3({
|
|
453
|
+
base: "kz-skeleton block bg-surface-3",
|
|
454
|
+
variants: {
|
|
455
|
+
shape: { text: "h-3 w-full rounded-sm", circle: "aspect-square h-10 rounded-full", rect: "h-24 w-full rounded-lg" }
|
|
456
|
+
},
|
|
457
|
+
defaultVariants: { shape: "text" }
|
|
458
|
+
});
|
|
459
|
+
function Skeleton({ shape, className, ...props }) {
|
|
460
|
+
return /* @__PURE__ */ jsx8("span", { "aria-hidden": true, "data-shape": shape ?? "text", className: skeletonVariants({ shape, className }), ...props });
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// src/media-grid/video-card-skeleton.tsx
|
|
464
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
465
|
+
var FRAME2 = { landscape: "aspect-video", portrait: "aspect-[9/16]" };
|
|
466
|
+
function VideoCardSkeleton({ aspect = "landscape", layout = "stack", className, ...props }) {
|
|
467
|
+
const row = layout === "row";
|
|
468
|
+
const tall = aspect === "portrait" && !row;
|
|
469
|
+
return /* @__PURE__ */ jsx9("div", { "aria-hidden": true, "data-skeleton": "video-card", "data-aspect": aspect, "data-layout": layout, className: cn("h-full min-w-0", className), ...props, children: /* @__PURE__ */ jsxs6(Card.Root, { tone: "glass", className: cn("h-full w-full max-w-full", row ? "flex-row" : "flex-col"), children: [
|
|
470
|
+
/* @__PURE__ */ jsx9(Card.Media, { aspect: "auto", className: cn("shrink-0", FRAME2[aspect], row && (aspect === "portrait" ? "w-24 sm:w-28" : "w-40 sm:w-56")), children: /* @__PURE__ */ jsx9(Skeleton, { shape: "rect", className: "h-full w-full rounded-none" }) }),
|
|
471
|
+
/* @__PURE__ */ jsxs6(Card.Body, { className: "static min-w-0 flex-1 justify-between gap-2 p-3", children: [
|
|
472
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex min-h-[2.5em] flex-col justify-center gap-1.5", children: [
|
|
473
|
+
/* @__PURE__ */ jsx9(Skeleton, { className: "h-3" }),
|
|
474
|
+
/* @__PURE__ */ jsx9(Skeleton, { className: "h-3 w-3/5" })
|
|
475
|
+
] }),
|
|
476
|
+
/* @__PURE__ */ jsxs6("div", { className: cn("flex text-xs", tall ? "flex-col gap-1" : "h-8 items-center gap-2"), children: [
|
|
477
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex h-6 min-w-0 items-center gap-2", children: [
|
|
478
|
+
/* @__PURE__ */ jsx9(Skeleton, { shape: "circle", className: "h-6 w-6 shrink-0" }),
|
|
479
|
+
/* @__PURE__ */ jsx9(Skeleton, { className: "h-2.5 w-20" })
|
|
480
|
+
] }),
|
|
481
|
+
/* @__PURE__ */ jsx9("div", { className: cn("flex items-center", tall ? "h-7" : "ml-auto"), children: /* @__PURE__ */ jsx9(Skeleton, { className: "h-2.5 w-14" }) })
|
|
482
|
+
] })
|
|
483
|
+
] })
|
|
484
|
+
] }) });
|
|
485
|
+
}
|
|
486
|
+
var GRID2 = {
|
|
487
|
+
landscape: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
|
|
488
|
+
portrait: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 xl:grid-cols-6"
|
|
489
|
+
};
|
|
490
|
+
function MediaGridSkeleton({ label, count = 8, shelfCount = 0, layout = "grid", frame = "landscape", className, ...props }) {
|
|
491
|
+
const row = layout === "list";
|
|
492
|
+
return /* @__PURE__ */ jsxs6("div", { role: "status", "aria-busy": "true", "data-media-grid-skeleton": "", className: cn("flex min-w-0 flex-col gap-8", className), ...props, children: [
|
|
493
|
+
/* @__PURE__ */ jsx9("span", { className: "sr-only", children: label }),
|
|
494
|
+
shelfCount > 0 && /* @__PURE__ */ jsxs6("div", { "aria-hidden": true, className: "min-w-0", children: [
|
|
495
|
+
/* @__PURE__ */ jsx9(Skeleton, { className: "mb-3 h-4 w-24" }),
|
|
496
|
+
/* @__PURE__ */ jsx9("ul", { className: "m-0 flex list-none items-stretch gap-3 overflow-hidden p-0", children: Array.from({ length: shelfCount }, (_, i) => /* @__PURE__ */ jsx9("li", { className: "w-[9.5rem] shrink-0 sm:w-44", children: /* @__PURE__ */ jsx9(VideoCardSkeleton, { aspect: "portrait" }) }, i)) })
|
|
497
|
+
] }),
|
|
498
|
+
/* @__PURE__ */ jsx9("ul", { "aria-hidden": true, className: cn("m-0 grid list-none auto-rows-fr gap-4 p-0", row ? "grid-cols-1" : GRID2[frame]), children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsx9("li", { className: "min-w-0", children: /* @__PURE__ */ jsx9(VideoCardSkeleton, { aspect: frame, layout: row ? "row" : "stack" }) }, i)) })
|
|
499
|
+
] });
|
|
500
|
+
}
|
|
257
501
|
export {
|
|
502
|
+
LoadMore,
|
|
258
503
|
MediaGrid,
|
|
504
|
+
MediaGridSkeleton,
|
|
259
505
|
ShortsShelf,
|
|
260
|
-
VideoCard
|
|
506
|
+
VideoCard,
|
|
507
|
+
VideoCardSkeleton
|
|
261
508
|
};
|
|
262
509
|
//# sourceMappingURL=media-grid.js.map
|