@openrails/contentkit-ui 0.94.0 → 0.95.1
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/README.md +58 -1
- package/dist/client/discovery.d.ts +14 -1
- package/dist/client/discovery.js +27 -14
- package/dist/client/discovery.js.map +1 -1
- package/dist/client/generated/routes.d.ts +18 -5
- package/dist/client/generated/routes.js +39 -0
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +10 -0
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client.js +17 -17
- package/dist/components/content/card.d.ts +28 -6
- package/dist/components/content/card.js +158 -132
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/daily-releases.d.ts +15 -0
- package/dist/components/content/daily-releases.js +173 -0
- package/dist/components/content/daily-releases.js.map +1 -0
- package/dist/components/content/grid.d.ts +14 -1
- package/dist/components/content/grid.js +75 -34
- package/dist/components/content/grid.js.map +1 -1
- package/dist/components/content/index.d.ts +4 -2
- package/dist/components/content/popular.d.ts +11 -0
- package/dist/components/content/popular.js +49 -0
- package/dist/components/content/popular.js.map +1 -0
- package/dist/index.js +22 -20
- package/dist/locales/discovery/de.js +4 -0
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +4 -0
- package/dist/locales/discovery/en.js +4 -0
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +4 -0
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +4 -0
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +4 -0
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +4 -0
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +4 -0
- package/dist/react/discovery.d.ts +12 -2
- package/dist/react/discovery.js +32 -23
- package/dist/react/discovery.js.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/resources.js +11 -11
- package/dist/react/resources.js.map +1 -1
- package/dist/react.js +10 -10
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { useMessages as e } from "../../i18n/context.js";
|
|
2
|
+
import { ContentKitUiRoot as t } from "../../scope.js";
|
|
3
|
+
import { useContentScope as n } from "../../react/use-resource.js";
|
|
4
|
+
import { Button as r } from "../../ui/button.js";
|
|
5
|
+
import { useNearViewport as i } from "../../react/viewport.js";
|
|
6
|
+
import { Skeleton as a } from "../../ui/skeleton.js";
|
|
7
|
+
import { contentError as o } from "../social/parts.js";
|
|
8
|
+
import { useDailyReleases as s, useExposure as c } from "../../react/discovery.js";
|
|
9
|
+
import { ContentCard as l } from "./card.js";
|
|
10
|
+
import { cardGridLayout as u } from "./grid.js";
|
|
11
|
+
import { useCallback as d, useId as f, useMemo as p, useState as m } from "react";
|
|
12
|
+
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
13
|
+
import { cn as _ } from "cn";
|
|
14
|
+
import { ArrowDown01Icon as v } from "@hugeicons/core-free-icons";
|
|
15
|
+
import { HugeiconsIcon as y } from "@hugeicons/react";
|
|
16
|
+
//#region src/components/content/daily-releases.tsx
|
|
17
|
+
var b = (e, t) => Number.isFinite(e) ? Math.max(0, Math.floor(e)) : t;
|
|
18
|
+
function x({ query: i, initialVisible: c = 30, revealStep: l = 30, ...d }) {
|
|
19
|
+
let f = e(), { scope: m } = n(d.client), v = p(() => ({
|
|
20
|
+
days: 10,
|
|
21
|
+
time_zone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
22
|
+
...i
|
|
23
|
+
}), [i]), y = s(v, { client: d.client }), x = u(d.grid), C = p(() => y.data?.days.filter((e) => e.contents.length).sort((e, t) => t.date.localeCompare(e.date)) ?? [], [y.data]), w = b(c, 30);
|
|
24
|
+
return /* @__PURE__ */ h(t, {
|
|
25
|
+
appearance: d.appearance,
|
|
26
|
+
className: _("grid gap-6 text-sm", d.className),
|
|
27
|
+
"data-ckui": "daily-releases",
|
|
28
|
+
"aria-busy": y.loading,
|
|
29
|
+
children: y.loading && !y.data ? /* @__PURE__ */ h("div", {
|
|
30
|
+
className: x.className,
|
|
31
|
+
style: x.style,
|
|
32
|
+
children: Array.from({ length: 6 }, (e, t) => /* @__PURE__ */ h(a, {
|
|
33
|
+
className: d.aspectRatio === void 0 ? d.shape === "landscape" ? "aspect-video" : "aspect-[3/4]" : void 0,
|
|
34
|
+
style: d.aspectRatio === void 0 ? void 0 : { aspectRatio: d.aspectRatio }
|
|
35
|
+
}, t))
|
|
36
|
+
}) : y.error && !y.data ? /* @__PURE__ */ g("div", {
|
|
37
|
+
role: "alert",
|
|
38
|
+
className: "grid justify-items-start gap-2",
|
|
39
|
+
children: [/* @__PURE__ */ h("p", {
|
|
40
|
+
className: "text-destructive",
|
|
41
|
+
children: o(f, y.error)
|
|
42
|
+
}), /* @__PURE__ */ h(r, {
|
|
43
|
+
size: "sm",
|
|
44
|
+
variant: "outline",
|
|
45
|
+
onClick: y.reload,
|
|
46
|
+
children: f.t("common.retry")
|
|
47
|
+
})]
|
|
48
|
+
}) : C.length ? C.map((e) => {
|
|
49
|
+
let t = Math.min(w, e.contents.length);
|
|
50
|
+
return w -= t, /* @__PURE__ */ h(S, {
|
|
51
|
+
day: e,
|
|
52
|
+
initial: t,
|
|
53
|
+
step: Math.max(1, b(l, 30)),
|
|
54
|
+
cardProps: d
|
|
55
|
+
}, JSON.stringify([
|
|
56
|
+
m,
|
|
57
|
+
v,
|
|
58
|
+
y.data?.from_date,
|
|
59
|
+
e.date
|
|
60
|
+
]));
|
|
61
|
+
}) : /* @__PURE__ */ h("p", {
|
|
62
|
+
role: "status",
|
|
63
|
+
className: "text-muted-foreground",
|
|
64
|
+
children: d.empty ?? f.t("contents.empty")
|
|
65
|
+
})
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function S({ day: t, initial: n, step: i, cardProps: a }) {
|
|
69
|
+
let o = e(), s = f(), [l, d] = m(n > 0), [b, x] = m(n), S = p(() => t.contents.slice(0, b), [t.contents, b]), w = p(() => l ? S : [], [l, S]), T = c(w, {
|
|
70
|
+
surface: a.surface ?? "releases",
|
|
71
|
+
render: t.date,
|
|
72
|
+
client: a.client
|
|
73
|
+
}), E = u(a.grid), D = new Intl.DateTimeFormat(o.language, {
|
|
74
|
+
dateStyle: "full",
|
|
75
|
+
timeZone: "UTC"
|
|
76
|
+
}).format(/* @__PURE__ */ new Date(`${t.date}T12:00:00Z`));
|
|
77
|
+
return /* @__PURE__ */ g("section", {
|
|
78
|
+
"data-ckui": "release-day",
|
|
79
|
+
"data-date": t.date,
|
|
80
|
+
children: [/* @__PURE__ */ h("h2", { children: /* @__PURE__ */ g(r, {
|
|
81
|
+
id: `${s}-heading`,
|
|
82
|
+
type: "button",
|
|
83
|
+
variant: "ghost",
|
|
84
|
+
className: "h-auto w-full justify-start gap-3 px-0 py-3 text-start text-lg font-semibold",
|
|
85
|
+
"aria-expanded": l,
|
|
86
|
+
"aria-controls": `${s}-contents`,
|
|
87
|
+
onClick: () => {
|
|
88
|
+
!l && !b && x(Math.min(i, t.contents.length)), d(!l);
|
|
89
|
+
},
|
|
90
|
+
children: [
|
|
91
|
+
/* @__PURE__ */ h(y, {
|
|
92
|
+
icon: v,
|
|
93
|
+
strokeWidth: 2,
|
|
94
|
+
className: _("size-5 shrink-0 transition-transform duration-200 motion-reduce:transition-none", !l && "-rotate-90"),
|
|
95
|
+
"aria-hidden": !0
|
|
96
|
+
}),
|
|
97
|
+
/* @__PURE__ */ h("time", {
|
|
98
|
+
dateTime: t.date,
|
|
99
|
+
children: D
|
|
100
|
+
}),
|
|
101
|
+
/* @__PURE__ */ h("span", {
|
|
102
|
+
className: "ml-auto shrink-0 text-sm font-normal text-muted-foreground",
|
|
103
|
+
children: o.plural("contents.items", t.contents.length)
|
|
104
|
+
})
|
|
105
|
+
]
|
|
106
|
+
}) }), /* @__PURE__ */ h("div", {
|
|
107
|
+
id: `${s}-contents`,
|
|
108
|
+
role: "region",
|
|
109
|
+
"aria-labelledby": `${s}-heading`,
|
|
110
|
+
"aria-hidden": !l,
|
|
111
|
+
inert: !l,
|
|
112
|
+
className: _("grid transition-[grid-template-rows,opacity] duration-250 ease-out motion-reduce:transition-none", l ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"),
|
|
113
|
+
children: /* @__PURE__ */ g("div", {
|
|
114
|
+
className: "min-h-0 overflow-hidden",
|
|
115
|
+
children: [/* @__PURE__ */ h("ol", {
|
|
116
|
+
className: _(E.className, "pt-2"),
|
|
117
|
+
style: E.style,
|
|
118
|
+
children: S.map((e, t) => /* @__PURE__ */ h(C, {
|
|
119
|
+
card: e,
|
|
120
|
+
open: l,
|
|
121
|
+
position: t + 1,
|
|
122
|
+
exposure: T,
|
|
123
|
+
cardProps: a
|
|
124
|
+
}, e.id))
|
|
125
|
+
}), b < t.contents.length && /* @__PURE__ */ h("div", {
|
|
126
|
+
className: "flex justify-center pt-5 pb-1",
|
|
127
|
+
children: /* @__PURE__ */ h(r, {
|
|
128
|
+
type: "button",
|
|
129
|
+
variant: "outline",
|
|
130
|
+
onClick: () => x((e) => Math.min(e + i, t.contents.length)),
|
|
131
|
+
children: o.t("contents.showMore")
|
|
132
|
+
})
|
|
133
|
+
})]
|
|
134
|
+
})
|
|
135
|
+
})]
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
function C({ card: e, open: t, position: n, exposure: r, cardProps: a }) {
|
|
139
|
+
let [o, s] = i({ margin: "0px" }), c = p(() => r.track(e.id, n), [
|
|
140
|
+
r,
|
|
141
|
+
e.id,
|
|
142
|
+
n
|
|
143
|
+
]), u = d((e) => {
|
|
144
|
+
o(e), c(e);
|
|
145
|
+
}, [o, c]);
|
|
146
|
+
return /* @__PURE__ */ h("li", {
|
|
147
|
+
ref: u,
|
|
148
|
+
children: /* @__PURE__ */ h(l, {
|
|
149
|
+
card: e,
|
|
150
|
+
href: a.href,
|
|
151
|
+
shape: a.shape,
|
|
152
|
+
aspectRatio: a.aspectRatio,
|
|
153
|
+
titlePosition: a.titlePosition,
|
|
154
|
+
loadMedia: t && s,
|
|
155
|
+
groupKinds: a.groupKinds,
|
|
156
|
+
chips: a.chips,
|
|
157
|
+
filterEntity: a.filterEntity,
|
|
158
|
+
entityStyle: a.entityStyle,
|
|
159
|
+
metadata: a.metadata,
|
|
160
|
+
showCounts: a.showCounts,
|
|
161
|
+
tiers: a.tiers,
|
|
162
|
+
labels: a.labels,
|
|
163
|
+
owner: a.owner,
|
|
164
|
+
ownerHref: a.ownerHref,
|
|
165
|
+
decorate: a.decorate,
|
|
166
|
+
onOpen: () => r.click(e.id, n)
|
|
167
|
+
})
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
//#endregion
|
|
171
|
+
export { x as DailyReleases };
|
|
172
|
+
|
|
173
|
+
//# sourceMappingURL=daily-releases.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daily-releases.js","names":[],"sources":["../../../src/components/content/daily-releases.tsx"],"sourcesContent":["import { ArrowDown01Icon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useId, useMemo, useState } from \"react\";\nimport type { DailyReleasesQuery } from \"../../client/discovery.js\";\nimport type { Card, ReleaseDay } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useDailyReleases, useExposure } from \"../../react/discovery.js\";\nimport { useContentScope } from \"../../react/use-resource.js\";\nimport { useNearViewport } from \"../../react/viewport.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { contentError } from \"../social/parts.js\";\nimport { ContentCard } from \"./card.js\";\nimport { cardGridLayout, type CardListProps } from \"./grid.js\";\n\nexport interface DailyReleasesProps extends Omit<CardListProps, \"infinite\" | \"pageSize\" | \"renderCard\" | \"loadMedia\"> {\n /** One complete calendar window. Defaults to 10 days in the browser's time zone. */\n query?: DailyReleasesQuery;\n /** Cards initially revealed across the whole window, allocated newest first. Default 30. */\n initialVisible?: number;\n /** Additional cached cards revealed by a day's Show more button. Default 30. */\n revealStep?: number;\n}\n\nconst count = (value: number, fallback: number) => Number.isFinite(value) ? Math.max(0, Math.floor(value)) : fallback;\n\n/**\n * One metadata request for a calendar window, with no requests when a day is\n * opened or expanded. Only revealed, visible cards mount images and previews.\n */\nexport function DailyReleases({ query, initialVisible = 30, revealStep = 30, ...p }: DailyReleasesProps) {\n const m = useMessages();\n const { scope } = useContentScope(p.client);\n const request = useMemo(() => ({ days: 10, time_zone: Intl.DateTimeFormat().resolvedOptions().timeZone, ...query }), [query]);\n const releases = useDailyReleases(request, { client: p.client });\n const layout = cardGridLayout(p.grid);\n const days = useMemo(() => releases.data?.days.filter((day) => day.contents.length).sort((a, b) => b.date.localeCompare(a.date)) ?? [], [releases.data]);\n let remaining = count(initialVisible, 30);\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-6 text-sm\", p.className)} data-ckui=\"daily-releases\" aria-busy={releases.loading}>\n {releases.loading && !releases.data ? <div className={layout.className} style={layout.style}>{Array.from({ length: 6 }, (_, i) => <Skeleton key={i} className={p.aspectRatio === undefined ? p.shape === \"landscape\" ? \"aspect-video\" : \"aspect-[3/4]\" : undefined} style={p.aspectRatio === undefined ? undefined : { aspectRatio: p.aspectRatio }} />)}</div> : releases.error && !releases.data ? (\n <div role=\"alert\" className=\"grid justify-items-start gap-2\">\n <p className=\"text-destructive\">{contentError(m, releases.error)}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={releases.reload}>{m.t(\"common.retry\")}</Button>\n </div>\n ) : !days.length ? <p role=\"status\" className=\"text-muted-foreground\">{p.empty ?? m.t(\"contents.empty\")}</p> : days.map((day) => {\n const initial = Math.min(remaining, day.contents.length);\n remaining -= initial;\n return <ReleaseDaySection key={JSON.stringify([scope, request, releases.data?.from_date, day.date])} day={day} initial={initial} step={Math.max(1, count(revealStep, 30))} cardProps={p} />;\n })}\n </ContentKitUiRoot>\n );\n}\n\nfunction ReleaseDaySection({ day, initial, step, cardProps: p }: { day: ReleaseDay; initial: number; step: number; cardProps: Omit<DailyReleasesProps, \"query\" | \"initialVisible\" | \"revealStep\"> }) {\n const m = useMessages();\n const id = useId();\n const [open, setOpen] = useState(initial > 0);\n const [visible, setVisible] = useState(initial);\n const cards = useMemo(() => day.contents.slice(0, visible), [day.contents, visible]);\n const exposed = useMemo(() => open ? cards : [], [open, cards]);\n const exposure = useExposure(exposed, { surface: p.surface ?? \"releases\", render: day.date, client: p.client });\n const layout = cardGridLayout(p.grid);\n // The server already assigned the calendar day in the requested time zone.\n const label = new Intl.DateTimeFormat(m.language, { dateStyle: \"full\", timeZone: \"UTC\" }).format(new Date(`${day.date}T12:00:00Z`));\n const toggle = () => {\n if (!open && !visible) setVisible(Math.min(step, day.contents.length));\n setOpen(!open);\n };\n return (\n <section data-ckui=\"release-day\" data-date={day.date}>\n <h2>\n <Button id={`${id}-heading`} type=\"button\" variant=\"ghost\" className=\"h-auto w-full justify-start gap-3 px-0 py-3 text-start text-lg font-semibold\" aria-expanded={open} aria-controls={`${id}-contents`} onClick={toggle}>\n <HugeiconsIcon icon={ArrowDown01Icon} strokeWidth={2} className={cn(\"size-5 shrink-0 transition-transform duration-200 motion-reduce:transition-none\", !open && \"-rotate-90\")} aria-hidden />\n <time dateTime={day.date}>{label}</time>\n <span className=\"ml-auto shrink-0 text-sm font-normal text-muted-foreground\">{m.plural(\"contents.items\", day.contents.length)}</span>\n </Button>\n </h2>\n <div id={`${id}-contents`} role=\"region\" aria-labelledby={`${id}-heading`} aria-hidden={!open} inert={!open} className={cn(\"grid transition-[grid-template-rows,opacity] duration-250 ease-out motion-reduce:transition-none\", open ? \"grid-rows-[1fr] opacity-100\" : \"grid-rows-[0fr] opacity-0\")}>\n <div className=\"min-h-0 overflow-hidden\">\n <ol className={cn(layout.className, \"pt-2\")} style={layout.style}>\n {cards.map((card, i) => <ReleaseCard key={card.id} card={card} open={open} position={i + 1} exposure={exposure} cardProps={p} />)}\n </ol>\n {visible < day.contents.length && <div className=\"flex justify-center pt-5 pb-1\"><Button type=\"button\" variant=\"outline\" onClick={() => setVisible((n) => Math.min(n + step, day.contents.length))}>{m.t(\"contents.showMore\")}</Button></div>}\n </div>\n </div>\n </section>\n );\n}\n\nfunction ReleaseCard({ card, open, position, exposure, cardProps: p }: { card: Card; open: boolean; position: number; exposure: ReturnType<typeof useExposure>; cardProps: Omit<DailyReleasesProps, \"query\" | \"initialVisible\" | \"revealStep\"> }) {\n const [viewport, visible] = useNearViewport({ margin: \"0px\" });\n const tracked = useMemo(() => exposure.track(card.id, position), [exposure, card.id, position]);\n const ref = useCallback((element: HTMLLIElement | null) => { viewport(element); tracked(element); }, [viewport, tracked]);\n return <li ref={ref}>\n <ContentCard card={card} href={p.href} shape={p.shape} aspectRatio={p.aspectRatio} titlePosition={p.titlePosition} loadMedia={open && visible} groupKinds={p.groupKinds} chips={p.chips} filterEntity={p.filterEntity} entityStyle={p.entityStyle} metadata={p.metadata} showCounts={p.showCounts} tiers={p.tiers} labels={p.labels} owner={p.owner} ownerHref={p.ownerHref} decorate={p.decorate} onOpen={() => exposure.click(card.id, position)} />\n </li>;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
1
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
2
2
|
import { ContentKitUiAppearance } from '../../appearance.js';
|
|
3
3
|
import { ContentKitClient } from '../../client/client.js';
|
|
4
4
|
import { ContentQuery, HistoryQuery, RailQuery, SearchQuery } from '../../client/discovery.js';
|
|
@@ -18,7 +18,20 @@ export interface CardListProps extends Omit<ContentCardProps, "card" | "onOpen"
|
|
|
18
18
|
client?: ContentKitClient;
|
|
19
19
|
className?: string;
|
|
20
20
|
appearance?: ContentKitUiAppearance;
|
|
21
|
+
/** Opt into a fluid grid with no desktop column ceiling. */
|
|
22
|
+
grid?: CardGridLayout;
|
|
21
23
|
}
|
|
24
|
+
export interface CardGridLayout {
|
|
25
|
+
/** Minimum desktop card width in CSS pixels. Cards grow to fill available space. */
|
|
26
|
+
minCardWidth: number;
|
|
27
|
+
/** Equal-width columns below the desktop breakpoint. Default 2. */
|
|
28
|
+
mobileColumns?: number;
|
|
29
|
+
}
|
|
30
|
+
/** Shared by complete lists, release-day sections and grid-style previews. */
|
|
31
|
+
export declare function cardGridLayout(layout?: CardGridLayout): {
|
|
32
|
+
className: string;
|
|
33
|
+
style?: CSSProperties;
|
|
34
|
+
};
|
|
22
35
|
/** A list's cards in a responsive grid, paged, with exposures and click signals. */
|
|
23
36
|
export declare function CardGrid({ list, render, ...p }: CardListProps & {
|
|
24
37
|
list: UseList<Card>;
|
|
@@ -13,7 +13,18 @@ import { Fragment as x, jsx as S, jsxs as C } from "react/jsx-runtime";
|
|
|
13
13
|
import { cn as w } from "cn";
|
|
14
14
|
//#region src/components/content/grid.tsx
|
|
15
15
|
var T = "grid grid-cols-[repeat(auto-fill,minmax(min(100%,10rem),1fr))] gap-x-4 gap-y-6";
|
|
16
|
-
function E(
|
|
16
|
+
function E(e) {
|
|
17
|
+
if (!e) return { className: T };
|
|
18
|
+
let t = Number.isFinite(e.minCardWidth) ? Math.max(1, e.minCardWidth) : 160, n = e.mobileColumns !== void 0 && Number.isFinite(e.mobileColumns) ? Math.max(1, Math.floor(e.mobileColumns)) : 2;
|
|
19
|
+
return {
|
|
20
|
+
className: "grid grid-cols-[repeat(var(--ckui-card-mobile-columns),minmax(0,1fr))] gap-x-4 gap-y-6 md:grid-cols-[repeat(auto-fill,minmax(min(100%,var(--ckui-card-min-width)),1fr))]",
|
|
21
|
+
style: {
|
|
22
|
+
"--ckui-card-min-width": `${t}px`,
|
|
23
|
+
"--ckui-card-mobile-columns": n
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function D({ list: n, render: c, ...l }) {
|
|
17
28
|
let d = e(), { t: f } = d, { kinds: p } = r({ client: l.client }), m = u(n.items, {
|
|
18
29
|
surface: l.surface ?? "list",
|
|
19
30
|
render: c,
|
|
@@ -31,15 +42,19 @@ function E({ list: n, render: c, ...l }) {
|
|
|
31
42
|
b,
|
|
32
43
|
x
|
|
33
44
|
]);
|
|
34
|
-
let
|
|
45
|
+
let T = l.tiers ?? p?.tiers, D = E(l.grid);
|
|
35
46
|
return /* @__PURE__ */ C(t, {
|
|
36
47
|
appearance: l.appearance,
|
|
37
48
|
className: w("grid gap-4 text-sm", l.className),
|
|
38
49
|
"data-ckui": "content-grid",
|
|
39
50
|
"aria-busy": n.loading,
|
|
40
51
|
children: [n.loading && !n.items.length ? /* @__PURE__ */ S("div", {
|
|
41
|
-
className:
|
|
42
|
-
|
|
52
|
+
className: D.className,
|
|
53
|
+
style: D.style,
|
|
54
|
+
children: Array.from({ length: 8 }, (e, t) => /* @__PURE__ */ S(o, {
|
|
55
|
+
className: l.aspectRatio === void 0 ? l.shape === "landscape" ? "aspect-video w-full" : "aspect-[3/4] w-full" : "w-full",
|
|
56
|
+
style: l.aspectRatio === void 0 ? void 0 : { aspectRatio: l.aspectRatio }
|
|
57
|
+
}, t))
|
|
43
58
|
}) : n.error && !n.items.length ? /* @__PURE__ */ C("div", {
|
|
44
59
|
role: "alert",
|
|
45
60
|
className: "grid justify-items-start gap-2",
|
|
@@ -53,16 +68,24 @@ function E({ list: n, render: c, ...l }) {
|
|
|
53
68
|
children: f("common.retry")
|
|
54
69
|
})]
|
|
55
70
|
}) : n.items.length ? /* @__PURE__ */ S("ol", {
|
|
56
|
-
className:
|
|
71
|
+
className: D.className,
|
|
72
|
+
style: D.style,
|
|
57
73
|
children: n.items.map((e, t) => /* @__PURE__ */ S("li", {
|
|
58
74
|
ref: m.track(e.id, t + 1),
|
|
59
75
|
children: l.renderCard ? l.renderCard(e, t + 1) : /* @__PURE__ */ S(g, {
|
|
60
76
|
card: e,
|
|
61
77
|
href: l.href,
|
|
62
78
|
shape: l.shape,
|
|
79
|
+
aspectRatio: l.aspectRatio,
|
|
80
|
+
titlePosition: l.titlePosition,
|
|
81
|
+
loadMedia: l.loadMedia,
|
|
63
82
|
groupKinds: l.groupKinds,
|
|
64
83
|
chips: l.chips,
|
|
65
|
-
|
|
84
|
+
filterEntity: l.filterEntity,
|
|
85
|
+
entityStyle: l.entityStyle,
|
|
86
|
+
metadata: l.metadata,
|
|
87
|
+
showCounts: l.showCounts,
|
|
88
|
+
tiers: T,
|
|
66
89
|
labels: l.labels,
|
|
67
90
|
owner: l.owner,
|
|
68
91
|
ownerHref: l.ownerHref,
|
|
@@ -87,13 +110,13 @@ function E({ list: n, render: c, ...l }) {
|
|
|
87
110
|
})]
|
|
88
111
|
});
|
|
89
112
|
}
|
|
90
|
-
var
|
|
113
|
+
var O = [
|
|
91
114
|
"tier",
|
|
92
115
|
"group",
|
|
93
116
|
"ppv",
|
|
94
117
|
"group_ppv"
|
|
95
118
|
];
|
|
96
|
-
function
|
|
119
|
+
function k({ query: e, locked: t = "show", ...n }) {
|
|
97
120
|
let r = y(() => ({
|
|
98
121
|
...e,
|
|
99
122
|
limit: e.limit ?? n.pageSize,
|
|
@@ -103,20 +126,20 @@ function O({ query: e, locked: t = "show", ...n }) {
|
|
|
103
126
|
n.pageSize,
|
|
104
127
|
t
|
|
105
128
|
]), i = l(r, { client: n.client });
|
|
106
|
-
return /* @__PURE__ */ C(x, { children: [t === "collapse" && /* @__PURE__ */ S(
|
|
129
|
+
return /* @__PURE__ */ C(x, { children: [t === "collapse" && /* @__PURE__ */ S(A, {
|
|
107
130
|
...n,
|
|
108
131
|
query: e
|
|
109
|
-
}), /* @__PURE__ */ S(
|
|
132
|
+
}), /* @__PURE__ */ S(D, {
|
|
110
133
|
...n,
|
|
111
134
|
list: i,
|
|
112
135
|
render: JSON.stringify(r)
|
|
113
136
|
})] });
|
|
114
137
|
}
|
|
115
|
-
function
|
|
138
|
+
function A({ query: n, ...r }) {
|
|
116
139
|
let a = e(), [o, s] = b(!1), { count: c } = f(n, { client: r.client }), u = y(() => o ? {
|
|
117
140
|
...n,
|
|
118
141
|
mode: "lock",
|
|
119
|
-
access: n.access ??
|
|
142
|
+
access: n.access ?? O,
|
|
120
143
|
limit: n.limit ?? r.pageSize
|
|
121
144
|
} : null, [
|
|
122
145
|
o,
|
|
@@ -134,7 +157,7 @@ function k({ query: n, ...r }) {
|
|
|
134
157
|
"aria-expanded": o,
|
|
135
158
|
onClick: () => s(!o),
|
|
136
159
|
children: o ? a.t("contents.lockedHide") : a.plural("contents.lockedShow", c)
|
|
137
|
-
}), o && /* @__PURE__ */ S(
|
|
160
|
+
}), o && /* @__PURE__ */ S(D, {
|
|
138
161
|
...r,
|
|
139
162
|
list: {
|
|
140
163
|
...d,
|
|
@@ -144,12 +167,12 @@ function k({ query: n, ...r }) {
|
|
|
144
167
|
})]
|
|
145
168
|
}) : null;
|
|
146
169
|
}
|
|
147
|
-
function
|
|
170
|
+
function j({ query: t, ...n }) {
|
|
148
171
|
let { t: r } = e(), i = y(() => ({
|
|
149
172
|
...t,
|
|
150
173
|
limit: t.limit ?? n.pageSize
|
|
151
174
|
}), [t, n.pageSize]), a = m(i, { client: n.client });
|
|
152
|
-
return t.q.trim() ? /* @__PURE__ */ S(
|
|
175
|
+
return t.q.trim() ? /* @__PURE__ */ S(D, {
|
|
153
176
|
surface: "search",
|
|
154
177
|
empty: r("contents.noResults", { q: t.q.trim() }),
|
|
155
178
|
...n,
|
|
@@ -157,7 +180,7 @@ function A({ query: t, ...n }) {
|
|
|
157
180
|
render: JSON.stringify(i)
|
|
158
181
|
}) : null;
|
|
159
182
|
}
|
|
160
|
-
function
|
|
183
|
+
function M({ query: t = {}, ...r }) {
|
|
161
184
|
let { t: a } = e(), o = _(n), s = y(() => ({
|
|
162
185
|
...t,
|
|
163
186
|
limit: t.limit ?? r.pageSize
|
|
@@ -175,7 +198,7 @@ function j({ query: t = {}, ...r }) {
|
|
|
175
198
|
disabled: u.pending,
|
|
176
199
|
onClick: () => void u.run(() => l.forget(t.kind ? { kind: t.kind } : {})),
|
|
177
200
|
children: a("contents.clearHistory")
|
|
178
|
-
}), /* @__PURE__ */ S(
|
|
201
|
+
}), /* @__PURE__ */ S(D, {
|
|
179
202
|
surface: "history",
|
|
180
203
|
empty: a("contents.historyEmpty"),
|
|
181
204
|
...r,
|
|
@@ -185,8 +208,15 @@ function j({ query: t = {}, ...r }) {
|
|
|
185
208
|
card: e,
|
|
186
209
|
href: r.href,
|
|
187
210
|
shape: r.shape,
|
|
211
|
+
aspectRatio: r.aspectRatio,
|
|
212
|
+
titlePosition: r.titlePosition,
|
|
213
|
+
loadMedia: r.loadMedia,
|
|
188
214
|
groupKinds: r.groupKinds,
|
|
189
215
|
chips: r.chips,
|
|
216
|
+
filterEntity: r.filterEntity,
|
|
217
|
+
entityStyle: r.entityStyle,
|
|
218
|
+
metadata: r.metadata,
|
|
219
|
+
showCounts: r.showCounts,
|
|
190
220
|
tiers: r.tiers,
|
|
191
221
|
labels: r.labels,
|
|
192
222
|
owner: r.owner,
|
|
@@ -204,7 +234,7 @@ function j({ query: t = {}, ...r }) {
|
|
|
204
234
|
})]
|
|
205
235
|
});
|
|
206
236
|
}
|
|
207
|
-
function
|
|
237
|
+
function N({ title: e, similar: n, recommendations: i, history: a, group: s, list: c, query: f = {}, more: m, ..._ }) {
|
|
208
238
|
let v = f.limit ?? _.pageSize ?? 12, b = y(() => ({
|
|
209
239
|
...f,
|
|
210
240
|
limit: v
|
|
@@ -219,11 +249,11 @@ function M({ title: e, similar: n, recommendations: i, history: a, group: s, lis
|
|
|
219
249
|
c,
|
|
220
250
|
s,
|
|
221
251
|
v
|
|
222
|
-
]), T = h(i || a || x ? null : n, b, { client: _.client }),
|
|
252
|
+
]), T = h(i || a || x ? null : n, b, { client: _.client }), D = p(i ? b : null, { client: _.client }), O = d(a ? {
|
|
223
253
|
...a,
|
|
224
254
|
limit: a.limit ?? v
|
|
225
|
-
} : null, { client: _.client }),
|
|
226
|
-
let e = a ?
|
|
255
|
+
} : null, { client: _.client }), k = l(x, { client: _.client }), A = i ? D : a || x ? (() => {
|
|
256
|
+
let e = a ? O : k;
|
|
227
257
|
return {
|
|
228
258
|
items: e.items,
|
|
229
259
|
loading: e.loading,
|
|
@@ -231,8 +261,8 @@ function M({ title: e, similar: n, recommendations: i, history: a, group: s, lis
|
|
|
231
261
|
unavailable: e.error?.code === "not_configured",
|
|
232
262
|
reload: e.reload
|
|
233
263
|
};
|
|
234
|
-
})() : T, { kinds:
|
|
235
|
-
surface:
|
|
264
|
+
})() : T, { kinds: j } = r({ client: _.client }), M = _.surface ?? (i ? "recommendations" : a ? "history" : s ? "featured" : x ? "list" : "similar"), N = u(A.items, {
|
|
265
|
+
surface: M,
|
|
236
266
|
render: JSON.stringify([
|
|
237
267
|
n,
|
|
238
268
|
i,
|
|
@@ -242,12 +272,12 @@ function M({ title: e, similar: n, recommendations: i, history: a, group: s, lis
|
|
|
242
272
|
b
|
|
243
273
|
]),
|
|
244
274
|
client: _.client
|
|
245
|
-
});
|
|
246
|
-
return !
|
|
275
|
+
}), P = n || i || a || s || x, F = _.grid ? E(_.grid) : void 0;
|
|
276
|
+
return !P || A.unavailable || !A.loading && !A.items.length ? null : /* @__PURE__ */ C(t, {
|
|
247
277
|
appearance: _.appearance,
|
|
248
278
|
className: w("grid gap-3 text-sm", _.className),
|
|
249
279
|
"data-ckui": "content-rail",
|
|
250
|
-
"data-surface":
|
|
280
|
+
"data-surface": M,
|
|
251
281
|
children: [/* @__PURE__ */ C("div", {
|
|
252
282
|
className: "flex items-baseline justify-between gap-2",
|
|
253
283
|
children: [/* @__PURE__ */ S("h2", {
|
|
@@ -255,28 +285,39 @@ function M({ title: e, similar: n, recommendations: i, history: a, group: s, lis
|
|
|
255
285
|
children: e
|
|
256
286
|
}), m]
|
|
257
287
|
}), /* @__PURE__ */ S("ol", {
|
|
258
|
-
className: "flex snap-x gap-4 overflow-x-auto pb-2",
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
288
|
+
className: F?.className ?? "flex snap-x gap-4 overflow-x-auto pb-2",
|
|
289
|
+
style: F?.style,
|
|
290
|
+
children: (A.loading && !A.items.length ? Array.from({ length: 6 }, () => null) : A.items).map((e, t) => /* @__PURE__ */ S("li", {
|
|
291
|
+
className: F ? void 0 : "w-36 shrink-0 snap-start sm:w-44",
|
|
292
|
+
ref: e ? N.track(e.id, t + 1) : void 0,
|
|
262
293
|
children: e ? _.renderCard ? _.renderCard(e, t + 1) : /* @__PURE__ */ S(g, {
|
|
263
294
|
card: e,
|
|
264
295
|
href: _.href,
|
|
265
296
|
shape: _.shape,
|
|
297
|
+
aspectRatio: _.aspectRatio,
|
|
298
|
+
titlePosition: _.titlePosition,
|
|
299
|
+
loadMedia: _.loadMedia,
|
|
266
300
|
groupKinds: _.groupKinds,
|
|
267
301
|
chips: _.chips,
|
|
268
|
-
|
|
302
|
+
filterEntity: _.filterEntity,
|
|
303
|
+
entityStyle: _.entityStyle,
|
|
304
|
+
metadata: _.metadata,
|
|
305
|
+
showCounts: _.showCounts,
|
|
306
|
+
tiers: _.tiers ?? j?.tiers,
|
|
269
307
|
labels: _.labels,
|
|
270
308
|
owner: _.owner,
|
|
271
309
|
ownerHref: _.ownerHref,
|
|
272
310
|
decorate: _.decorate,
|
|
273
|
-
onOpen: () =>
|
|
274
|
-
}) : /* @__PURE__ */ S(o, {
|
|
311
|
+
onOpen: () => N.click(e.id, t + 1)
|
|
312
|
+
}) : /* @__PURE__ */ S(o, {
|
|
313
|
+
className: _.aspectRatio === void 0 ? _.shape === "landscape" ? "aspect-video w-full" : "aspect-[3/4] w-full" : "w-full",
|
|
314
|
+
style: _.aspectRatio === void 0 ? void 0 : { aspectRatio: _.aspectRatio }
|
|
315
|
+
})
|
|
275
316
|
}, e?.id ?? t))
|
|
276
317
|
})]
|
|
277
318
|
});
|
|
278
319
|
}
|
|
279
320
|
//#endregion
|
|
280
|
-
export {
|
|
321
|
+
export { D as CardGrid, k as ContentGrid, M as ContentHistory, N as ContentRail, j as SearchResults, E as cardGridLayout };
|
|
281
322
|
|
|
282
323
|
//# sourceMappingURL=grid.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grid.js","names":[],"sources":["../../../src/components/content/grid.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useContext, useEffect, useMemo, useState, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { ContentQuery, HistoryQuery, RailQuery, SearchQuery } from \"../../client/discovery.js\";\nimport type { Card } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { useContents, useExposure, useHistory, useLockedCount, useRecommendations, useSearch, useSimilar, type UseRail } from \"../../react/discovery.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport type { UseList } from \"../../react/use-resource.js\";\nimport { useNearViewport } from \"../../react/viewport.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { contentError, useAction } from \"../social/parts.js\";\nimport { ContentCard, type ContentCardProps } from \"./card.js\";\n\nexport interface CardListProps extends Omit<ContentCardProps, \"card\" | \"onOpen\" | \"children\" | \"className\"> {\n /** Where the list is, for exposures and click attribution: \"home\", \"search\", \"similar\". */\n surface?: string;\n /** Loads the next page as the end nears, instead of a button. */\n infinite?: boolean;\n /** A card's entry instead of ContentCard. */\n renderCard?: (card: Card, position: number) => ReactNode;\n /** Shown when nothing matches. */\n empty?: ReactNode;\n pageSize?: number;\n client?: ContentKitClient;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\nconst GRID = \"grid grid-cols-[repeat(auto-fill,minmax(min(100%,10rem),1fr))] gap-x-4 gap-y-6\";\n\n/** A list's cards in a responsive grid, paged, with exposures and click signals. */\nexport function CardGrid({ list, render, ...p }: CardListProps & { list: UseList<Card>; render: string }) {\n const m = useMessages();\n const { t } = m;\n const { kinds } = useKinds({ client: p.client });\n const exposure = useExposure(list.items, { surface: p.surface ?? \"list\", render, client: p.client });\n const [sentinel, near] = useNearViewport({ margin: \"600px 0px\", leaveMargin: \"1200px 0px\" });\n const { hasMore, loadingMore, loadMore } = list;\n useEffect(() => {\n if (p.infinite && near && hasMore && !loadingMore) void loadMore();\n }, [p.infinite, near, hasMore, loadingMore, loadMore]);\n const tiers = p.tiers ?? kinds?.tiers;\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"content-grid\" aria-busy={list.loading}>\n {list.loading && !list.items.length ? (\n <div className={GRID}>\n {Array.from({ length: 8 }, (_, i) => (\n <Skeleton key={i} className={p.shape === \"landscape\" ? \"aspect-video w-full\" : \"aspect-[3/4] w-full\"} />\n ))}\n </div>\n ) : list.error && !list.items.length ? (\n <div role=\"alert\" className=\"grid justify-items-start gap-2\">\n <p className=\"text-destructive\">{contentError(m, list.error)}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={list.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n ) : !list.items.length ? (\n <div className=\"text-muted-foreground\" role=\"status\">\n {p.empty ?? t(\"contents.empty\")}\n </div>\n ) : (\n <ol className={GRID}>\n {list.items.map((card, i) => (\n <li key={card.id} ref={exposure.track(card.id, i + 1)}>\n {p.renderCard ? (\n p.renderCard(card, i + 1)\n ) : (\n <ContentCard\n card={card}\n href={p.href}\n shape={p.shape}\n groupKinds={p.groupKinds}\n chips={p.chips}\n tiers={tiers}\n labels={p.labels}\n owner={p.owner}\n ownerHref={p.ownerHref}\n decorate={p.decorate}\n onOpen={() => exposure.click(card.id, i + 1)}\n />\n )}\n </li>\n ))}\n </ol>\n )}\n {list.hasMore && (\n <div ref={sentinel} className=\"grid justify-items-center\">\n <Button variant=\"outline\" size=\"sm\" disabled={list.loadingMore} onClick={() => void list.loadMore()}>\n {list.loadingMore ? t(\"contents.loading\") : t(\"contents.loadMore\")}\n </Button>\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n\n/**\n * What a list does with cards the reader doesn't open: shows them marked\n * (show), leaves them out (hide), or lists them apart, counted, behind a\n * toggle that loads them when opened (collapse).\n */\nexport type LockedCards = \"show\" | \"hide\" | \"collapse\";\n\nconst LOCKED_LEVELS = [\"tier\", \"group\", \"ppv\", \"group_ppv\"] as const;\n\n/** Contents as cards (GET /v1/contents): any sort and filter, paged. */\nexport function ContentGrid({ query, locked = \"show\", ...p }: CardListProps & { query: ContentQuery; locked?: LockedCards }) {\n const q = useMemo<ContentQuery>(\n () => ({ ...query, limit: query.limit ?? p.pageSize, ...(locked === \"show\" ? {} : { mode: \"hide\" }) }),\n [query, p.pageSize, locked],\n );\n const list = useContents(q, { client: p.client });\n return (\n <>\n {locked === \"collapse\" && <LockedSection {...p} query={query} />}\n <CardGrid {...p} list={list} render={JSON.stringify(q)} />\n </>\n );\n}\n\n/** A list's locked cards, counted, loaded and shown on opening. */\nfunction LockedSection({ query, ...p }: CardListProps & { query: ContentQuery }) {\n const m = useMessages();\n const [open, setOpen] = useState(false);\n const { count } = useLockedCount(query, { client: p.client });\n const q = useMemo<ContentQuery | null>(\n () => (open ? { ...query, mode: \"lock\", access: query.access ?? LOCKED_LEVELS, limit: query.limit ?? p.pageSize } : null),\n [open, query, p.pageSize],\n );\n const list = useContents(q, { client: p.client });\n const items = useMemo(() => list.items.filter((c) => !c.access.allowed), [list.items]);\n if (!count) return null;\n return (\n <ContentKitUiRoot appearance={p.appearance} className=\"grid gap-3 text-sm\" data-ckui=\"locked-cards\">\n <Button variant=\"outline\" size=\"sm\" className=\"justify-self-start\" aria-expanded={open} onClick={() => setOpen(!open)}>\n {open ? m.t(\"contents.lockedHide\") : m.plural(\"contents.lockedShow\", count)}\n </Button>\n {open && <CardGrid {...p} list={{ ...list, items }} render={JSON.stringify(q)} />}\n </ContentKitUiRoot>\n );\n}\n\n/** Search results as cards; nothing until the query has text. */\nexport function SearchResults({ query, ...p }: CardListProps & { query: SearchQuery }) {\n const { t } = useMessages();\n const q = useMemo(() => ({ ...query, limit: query.limit ?? p.pageSize }), [query, p.pageSize]);\n const list = useSearch(q, { client: p.client });\n if (!query.q.trim()) return null;\n return <CardGrid surface=\"search\" empty={t(\"contents.noResults\", { q: query.q.trim() })} {...p} list={list} render={JSON.stringify(q)} />;\n}\n\n/** The signed-in reader's history as cards, each removable, with a clear-all. */\nexport function ContentHistory({ query = {}, ...p }: CardListProps & { query?: HistoryQuery }) {\n const { t } = useMessages();\n const ctx = useContext(ContentKitContext);\n const q = useMemo(() => ({ ...query, limit: query.limit ?? p.pageSize }), [query, p.pageSize]);\n const list = useHistory(q, { client: p.client });\n const forget = useAction(\"history.forget\");\n if (ctx?.viewer === null) return <p className=\"text-muted-foreground\">{t(\"contents.historySignedOut\")}</p>;\n return (\n <div className=\"grid gap-3\" data-ckui=\"content-history\">\n {list.items.length > 0 && (\n <Button size=\"sm\" variant=\"outline\" className=\"justify-self-end\" disabled={forget.pending} onClick={() => void forget.run(() => list.forget(query.kind ? { kind: query.kind } : {}))}>\n {t(\"contents.clearHistory\")}\n </Button>\n )}\n <CardGrid\n surface=\"history\"\n empty={t(\"contents.historyEmpty\")}\n {...p}\n list={list}\n render={JSON.stringify(q)}\n renderCard={\n p.renderCard ??\n ((card) => (\n <ContentCard\n card={card}\n href={p.href}\n shape={p.shape}\n groupKinds={p.groupKinds}\n chips={p.chips}\n tiers={p.tiers}\n labels={p.labels}\n owner={p.owner}\n ownerHref={p.ownerHref}\n decorate={p.decorate}\n >\n <Button size=\"xs\" variant=\"ghost\" className=\"justify-self-start\" disabled={forget.pending} onClick={() => void forget.run(() => list.forget({ content_id: card.id }))}>\n {t(\"contents.removeFromHistory\")}\n </Button>\n </ContentCard>\n ))\n }\n />\n </div>\n );\n}\n\nexport interface ContentRailProps extends Omit<CardListProps, \"infinite\" | \"empty\"> {\n /** The rail's heading. */\n title: ReactNode;\n /** Contents like this one. */\n similar?: string;\n /** For the signed-in reader. */\n recommendations?: boolean;\n /** The reader's history: { status: \"in_progress\" } is a continue-watching rail. */\n history?: HistoryQuery;\n /** An ordered group's members in its order: a featured rail (its windows apply). */\n group?: string;\n /** Any list: a sort and filters. */\n list?: ContentQuery;\n /** Filters and reader options of a similar or recommended rail. */\n query?: RailQuery;\n /** A link beside the heading, e.g. to the whole list. */\n more?: ReactNode;\n}\n\n/**\n * A horizontal row of contents: similar, recommended, the reader's history,\n * an ordered group's members or any list. It hides itself when it has none or\n * the site runs without what it needs.\n */\nexport function ContentRail({ title, similar, recommendations, history, group, list: listQuery, query = {}, more, ...p }: ContentRailProps) {\n const limit = query.limit ?? p.pageSize ?? 12;\n const q = useMemo(() => ({ ...query, limit }), [query, limit]);\n const listing = useMemo(\n () => (listQuery ? { ...listQuery, limit: listQuery.limit ?? limit } : group ? { groups: [group], sort: \"position\" as const, limit } : null),\n [listQuery, group, limit],\n );\n const like = useSimilar(recommendations || history || listing ? null : similar, q, { client: p.client });\n const mine = useRecommendations(recommendations ? q : null, { client: p.client });\n const seen = useHistory(history ? { ...history, limit: history.limit ?? limit } : null, { client: p.client });\n const listed = useContents(listing, { client: p.client });\n const rail: UseRail = recommendations\n ? mine\n : history || listing\n ? (() => {\n const l = history ? seen : listed;\n return { items: l.items, loading: l.loading, error: l.error, unavailable: l.error?.code === \"not_configured\", reload: l.reload };\n })()\n : like;\n const { kinds } = useKinds({ client: p.client });\n const surface = p.surface ?? (recommendations ? \"recommendations\" : history ? \"history\" : group ? \"featured\" : listing ? \"list\" : \"similar\");\n const exposure = useExposure(rail.items, { surface, render: JSON.stringify([similar, recommendations, history, group, listing, q]), client: p.client });\n const source = similar || recommendations || history || group || listing;\n if (!source || rail.unavailable || (!rail.loading && !rail.items.length)) return null;\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-3 text-sm\", p.className)} data-ckui=\"content-rail\" data-surface={surface}>\n <div className=\"flex items-baseline justify-between gap-2\">\n <h2 className=\"text-base font-semibold\">{title}</h2>\n {more}\n </div>\n <ol className=\"flex snap-x gap-4 overflow-x-auto pb-2\">\n {(rail.loading && !rail.items.length ? Array.from({ length: 6 }, () => null) : rail.items).map((card, i) => (\n <li key={card?.id ?? i} className=\"w-36 shrink-0 snap-start sm:w-44\" ref={card ? exposure.track(card.id, i + 1) : undefined}>\n {!card ? (\n <Skeleton className={p.shape === \"landscape\" ? \"aspect-video w-full\" : \"aspect-[3/4] w-full\"} />\n ) : p.renderCard ? (\n p.renderCard(card, i + 1)\n ) : (\n <ContentCard\n card={card}\n href={p.href}\n shape={p.shape}\n groupKinds={p.groupKinds}\n chips={p.chips}\n tiers={p.tiers ?? kinds?.tiers}\n labels={p.labels}\n owner={p.owner}\n ownerHref={p.ownerHref}\n decorate={p.decorate}\n onOpen={() => exposure.click(card.id, i + 1)}\n />\n )}\n </li>\n ))}\n </ol>\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"grid.js","names":[],"sources":["../../../src/components/content/grid.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useContext, useEffect, useMemo, useState, type CSSProperties, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { ContentQuery, HistoryQuery, RailQuery, SearchQuery } from \"../../client/discovery.js\";\nimport type { Card } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { useContents, useExposure, useHistory, useLockedCount, useRecommendations, useSearch, useSimilar, type UseRail } from \"../../react/discovery.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport type { UseList } from \"../../react/use-resource.js\";\nimport { useNearViewport } from \"../../react/viewport.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { contentError, useAction } from \"../social/parts.js\";\nimport { ContentCard, type ContentCardProps } from \"./card.js\";\n\nexport interface CardListProps extends Omit<ContentCardProps, \"card\" | \"onOpen\" | \"children\" | \"className\"> {\n /** Where the list is, for exposures and click attribution: \"home\", \"search\", \"similar\". */\n surface?: string;\n /** Loads the next page as the end nears, instead of a button. */\n infinite?: boolean;\n /** A card's entry instead of ContentCard. */\n renderCard?: (card: Card, position: number) => ReactNode;\n /** Shown when nothing matches. */\n empty?: ReactNode;\n pageSize?: number;\n client?: ContentKitClient;\n className?: string;\n appearance?: ContentKitUiAppearance;\n /** Opt into a fluid grid with no desktop column ceiling. */\n grid?: CardGridLayout;\n}\n\nconst GRID = \"grid grid-cols-[repeat(auto-fill,minmax(min(100%,10rem),1fr))] gap-x-4 gap-y-6\";\n\nexport interface CardGridLayout {\n /** Minimum desktop card width in CSS pixels. Cards grow to fill available space. */\n minCardWidth: number;\n /** Equal-width columns below the desktop breakpoint. Default 2. */\n mobileColumns?: number;\n}\n\n/** Shared by complete lists, release-day sections and grid-style previews. */\nexport function cardGridLayout(layout?: CardGridLayout): { className: string; style?: CSSProperties } {\n if (!layout) return { className: GRID };\n const width = Number.isFinite(layout.minCardWidth) ? Math.max(1, layout.minCardWidth) : 160;\n const columns = layout.mobileColumns !== undefined && Number.isFinite(layout.mobileColumns) ? Math.max(1, Math.floor(layout.mobileColumns)) : 2;\n return {\n className: \"grid grid-cols-[repeat(var(--ckui-card-mobile-columns),minmax(0,1fr))] gap-x-4 gap-y-6 md:grid-cols-[repeat(auto-fill,minmax(min(100%,var(--ckui-card-min-width)),1fr))]\",\n style: { \"--ckui-card-min-width\": `${width}px`, \"--ckui-card-mobile-columns\": columns } as CSSProperties,\n };\n}\n\n/** A list's cards in a responsive grid, paged, with exposures and click signals. */\nexport function CardGrid({ list, render, ...p }: CardListProps & { list: UseList<Card>; render: string }) {\n const m = useMessages();\n const { t } = m;\n const { kinds } = useKinds({ client: p.client });\n const exposure = useExposure(list.items, { surface: p.surface ?? \"list\", render, client: p.client });\n const [sentinel, near] = useNearViewport({ margin: \"600px 0px\", leaveMargin: \"1200px 0px\" });\n const { hasMore, loadingMore, loadMore } = list;\n useEffect(() => {\n if (p.infinite && near && hasMore && !loadingMore) void loadMore();\n }, [p.infinite, near, hasMore, loadingMore, loadMore]);\n const tiers = p.tiers ?? kinds?.tiers;\n const layout = cardGridLayout(p.grid);\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"content-grid\" aria-busy={list.loading}>\n {list.loading && !list.items.length ? (\n <div className={layout.className} style={layout.style}>\n {Array.from({ length: 8 }, (_, i) => (\n <Skeleton key={i} className={p.aspectRatio === undefined ? p.shape === \"landscape\" ? \"aspect-video w-full\" : \"aspect-[3/4] w-full\" : \"w-full\"} style={p.aspectRatio === undefined ? undefined : { aspectRatio: p.aspectRatio }} />\n ))}\n </div>\n ) : list.error && !list.items.length ? (\n <div role=\"alert\" className=\"grid justify-items-start gap-2\">\n <p className=\"text-destructive\">{contentError(m, list.error)}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={list.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n ) : !list.items.length ? (\n <div className=\"text-muted-foreground\" role=\"status\">\n {p.empty ?? t(\"contents.empty\")}\n </div>\n ) : (\n <ol className={layout.className} style={layout.style}>\n {list.items.map((card, i) => (\n <li key={card.id} ref={exposure.track(card.id, i + 1)}>\n {p.renderCard ? (\n p.renderCard(card, i + 1)\n ) : (\n <ContentCard\n card={card}\n href={p.href}\n shape={p.shape}\n aspectRatio={p.aspectRatio}\n titlePosition={p.titlePosition}\n loadMedia={p.loadMedia}\n groupKinds={p.groupKinds}\n chips={p.chips}\n filterEntity={p.filterEntity}\n entityStyle={p.entityStyle}\n metadata={p.metadata}\n showCounts={p.showCounts}\n tiers={tiers}\n labels={p.labels}\n owner={p.owner}\n ownerHref={p.ownerHref}\n decorate={p.decorate}\n onOpen={() => exposure.click(card.id, i + 1)}\n />\n )}\n </li>\n ))}\n </ol>\n )}\n {list.hasMore && (\n <div ref={sentinel} className=\"grid justify-items-center\">\n <Button variant=\"outline\" size=\"sm\" disabled={list.loadingMore} onClick={() => void list.loadMore()}>\n {list.loadingMore ? t(\"contents.loading\") : t(\"contents.loadMore\")}\n </Button>\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n\n/**\n * What a list does with cards the reader doesn't open: shows them marked\n * (show), leaves them out (hide), or lists them apart, counted, behind a\n * toggle that loads them when opened (collapse).\n */\nexport type LockedCards = \"show\" | \"hide\" | \"collapse\";\n\nconst LOCKED_LEVELS = [\"tier\", \"group\", \"ppv\", \"group_ppv\"] as const;\n\n/** Contents as cards (GET /v1/contents): any sort and filter, paged. */\nexport function ContentGrid({ query, locked = \"show\", ...p }: CardListProps & { query: ContentQuery; locked?: LockedCards }) {\n const q = useMemo<ContentQuery>(\n () => ({ ...query, limit: query.limit ?? p.pageSize, ...(locked === \"show\" ? {} : { mode: \"hide\" }) }),\n [query, p.pageSize, locked],\n );\n const list = useContents(q, { client: p.client });\n return (\n <>\n {locked === \"collapse\" && <LockedSection {...p} query={query} />}\n <CardGrid {...p} list={list} render={JSON.stringify(q)} />\n </>\n );\n}\n\n/** A list's locked cards, counted, loaded and shown on opening. */\nfunction LockedSection({ query, ...p }: CardListProps & { query: ContentQuery }) {\n const m = useMessages();\n const [open, setOpen] = useState(false);\n const { count } = useLockedCount(query, { client: p.client });\n const q = useMemo<ContentQuery | null>(\n () => (open ? { ...query, mode: \"lock\", access: query.access ?? LOCKED_LEVELS, limit: query.limit ?? p.pageSize } : null),\n [open, query, p.pageSize],\n );\n const list = useContents(q, { client: p.client });\n const items = useMemo(() => list.items.filter((c) => !c.access.allowed), [list.items]);\n if (!count) return null;\n return (\n <ContentKitUiRoot appearance={p.appearance} className=\"grid gap-3 text-sm\" data-ckui=\"locked-cards\">\n <Button variant=\"outline\" size=\"sm\" className=\"justify-self-start\" aria-expanded={open} onClick={() => setOpen(!open)}>\n {open ? m.t(\"contents.lockedHide\") : m.plural(\"contents.lockedShow\", count)}\n </Button>\n {open && <CardGrid {...p} list={{ ...list, items }} render={JSON.stringify(q)} />}\n </ContentKitUiRoot>\n );\n}\n\n/** Search results as cards; nothing until the query has text. */\nexport function SearchResults({ query, ...p }: CardListProps & { query: SearchQuery }) {\n const { t } = useMessages();\n const q = useMemo(() => ({ ...query, limit: query.limit ?? p.pageSize }), [query, p.pageSize]);\n const list = useSearch(q, { client: p.client });\n if (!query.q.trim()) return null;\n return <CardGrid surface=\"search\" empty={t(\"contents.noResults\", { q: query.q.trim() })} {...p} list={list} render={JSON.stringify(q)} />;\n}\n\n/** The signed-in reader's history as cards, each removable, with a clear-all. */\nexport function ContentHistory({ query = {}, ...p }: CardListProps & { query?: HistoryQuery }) {\n const { t } = useMessages();\n const ctx = useContext(ContentKitContext);\n const q = useMemo(() => ({ ...query, limit: query.limit ?? p.pageSize }), [query, p.pageSize]);\n const list = useHistory(q, { client: p.client });\n const forget = useAction(\"history.forget\");\n if (ctx?.viewer === null) return <p className=\"text-muted-foreground\">{t(\"contents.historySignedOut\")}</p>;\n return (\n <div className=\"grid gap-3\" data-ckui=\"content-history\">\n {list.items.length > 0 && (\n <Button size=\"sm\" variant=\"outline\" className=\"justify-self-end\" disabled={forget.pending} onClick={() => void forget.run(() => list.forget(query.kind ? { kind: query.kind } : {}))}>\n {t(\"contents.clearHistory\")}\n </Button>\n )}\n <CardGrid\n surface=\"history\"\n empty={t(\"contents.historyEmpty\")}\n {...p}\n list={list}\n render={JSON.stringify(q)}\n renderCard={\n p.renderCard ??\n ((card) => (\n <ContentCard\n card={card}\n href={p.href}\n shape={p.shape}\n aspectRatio={p.aspectRatio}\n titlePosition={p.titlePosition}\n loadMedia={p.loadMedia}\n groupKinds={p.groupKinds}\n chips={p.chips}\n filterEntity={p.filterEntity}\n entityStyle={p.entityStyle}\n metadata={p.metadata}\n showCounts={p.showCounts}\n tiers={p.tiers}\n labels={p.labels}\n owner={p.owner}\n ownerHref={p.ownerHref}\n decorate={p.decorate}\n >\n <Button size=\"xs\" variant=\"ghost\" className=\"justify-self-start\" disabled={forget.pending} onClick={() => void forget.run(() => list.forget({ content_id: card.id }))}>\n {t(\"contents.removeFromHistory\")}\n </Button>\n </ContentCard>\n ))\n }\n />\n </div>\n );\n}\n\nexport interface ContentRailProps extends Omit<CardListProps, \"infinite\" | \"empty\"> {\n /** The rail's heading. */\n title: ReactNode;\n /** Contents like this one. */\n similar?: string;\n /** For the signed-in reader. */\n recommendations?: boolean;\n /** The reader's history: { status: \"in_progress\" } is a continue-watching rail. */\n history?: HistoryQuery;\n /** An ordered group's members in its order: a featured rail (its windows apply). */\n group?: string;\n /** Any list: a sort and filters. */\n list?: ContentQuery;\n /** Filters and reader options of a similar or recommended rail. */\n query?: RailQuery;\n /** A link beside the heading, e.g. to the whole list. */\n more?: ReactNode;\n}\n\n/**\n * A horizontal row of contents: similar, recommended, the reader's history,\n * an ordered group's members or any list. It hides itself when it has none or\n * the site runs without what it needs.\n */\nexport function ContentRail({ title, similar, recommendations, history, group, list: listQuery, query = {}, more, ...p }: ContentRailProps) {\n const limit = query.limit ?? p.pageSize ?? 12;\n const q = useMemo(() => ({ ...query, limit }), [query, limit]);\n const listing = useMemo(\n () => (listQuery ? { ...listQuery, limit: listQuery.limit ?? limit } : group ? { groups: [group], sort: \"position\" as const, limit } : null),\n [listQuery, group, limit],\n );\n const like = useSimilar(recommendations || history || listing ? null : similar, q, { client: p.client });\n const mine = useRecommendations(recommendations ? q : null, { client: p.client });\n const seen = useHistory(history ? { ...history, limit: history.limit ?? limit } : null, { client: p.client });\n const listed = useContents(listing, { client: p.client });\n const rail: UseRail = recommendations\n ? mine\n : history || listing\n ? (() => {\n const l = history ? seen : listed;\n return { items: l.items, loading: l.loading, error: l.error, unavailable: l.error?.code === \"not_configured\", reload: l.reload };\n })()\n : like;\n const { kinds } = useKinds({ client: p.client });\n const surface = p.surface ?? (recommendations ? \"recommendations\" : history ? \"history\" : group ? \"featured\" : listing ? \"list\" : \"similar\");\n const exposure = useExposure(rail.items, { surface, render: JSON.stringify([similar, recommendations, history, group, listing, q]), client: p.client });\n const source = similar || recommendations || history || group || listing;\n const layout = p.grid ? cardGridLayout(p.grid) : undefined;\n if (!source || rail.unavailable || (!rail.loading && !rail.items.length)) return null;\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-3 text-sm\", p.className)} data-ckui=\"content-rail\" data-surface={surface}>\n <div className=\"flex items-baseline justify-between gap-2\">\n <h2 className=\"text-base font-semibold\">{title}</h2>\n {more}\n </div>\n <ol className={layout?.className ?? \"flex snap-x gap-4 overflow-x-auto pb-2\"} style={layout?.style}>\n {(rail.loading && !rail.items.length ? Array.from({ length: 6 }, () => null) : rail.items).map((card, i) => (\n <li key={card?.id ?? i} className={layout ? undefined : \"w-36 shrink-0 snap-start sm:w-44\"} ref={card ? exposure.track(card.id, i + 1) : undefined}>\n {!card ? (\n <Skeleton className={p.aspectRatio === undefined ? p.shape === \"landscape\" ? \"aspect-video w-full\" : \"aspect-[3/4] w-full\" : \"w-full\"} style={p.aspectRatio === undefined ? undefined : { aspectRatio: p.aspectRatio }} />\n ) : p.renderCard ? (\n p.renderCard(card, i + 1)\n ) : (\n <ContentCard\n card={card}\n href={p.href}\n shape={p.shape}\n aspectRatio={p.aspectRatio}\n titlePosition={p.titlePosition}\n loadMedia={p.loadMedia}\n groupKinds={p.groupKinds}\n chips={p.chips}\n filterEntity={p.filterEntity}\n entityStyle={p.entityStyle}\n metadata={p.metadata}\n showCounts={p.showCounts}\n tiers={p.tiers ?? kinds?.tiers}\n labels={p.labels}\n owner={p.owner}\n ownerHref={p.ownerHref}\n decorate={p.decorate}\n onOpen={() => exposure.click(card.id, i + 1)}\n />\n )}\n </li>\n ))}\n </ol>\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export { ContentCard, OwnerChip, PostCard, lockLabel, type ContentCardProps, type PostCardProps } from './card.js';
|
|
2
|
-
export { CardGrid, ContentGrid, ContentHistory, ContentRail, SearchResults, type CardListProps, type ContentRailProps, type LockedCards } from './grid.js';
|
|
1
|
+
export { ContentCard, OwnerChip, PostCard, lockLabel, type CardMetadata, type ContentCardProps, type PostCardProps } from './card.js';
|
|
2
|
+
export { CardGrid, ContentGrid, ContentHistory, ContentRail, SearchResults, type CardGridLayout, type CardListProps, type ContentRailProps, type LockedCards } from './grid.js';
|
|
3
|
+
export { DailyReleases, type DailyReleasesProps } from './daily-releases.js';
|
|
4
|
+
export { PopularContentGrid, type PopularContentGridProps } from './popular.js';
|
|
3
5
|
export { SearchBox, type SearchBoxProps } from './search.js';
|
|
4
6
|
export { ReaderSettings, type ReaderSettingsProps } from './reader-settings.js';
|
|
5
7
|
export { ContentFilterSettings, ReleaseDateIndex, type ContentFilterSettingsProps, type ReleaseDateIndexProps } from './filters.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PopularPeriod, PopularQuery } from '../../client/discovery.js';
|
|
2
|
+
import { CardListProps, LockedCards } from './grid.js';
|
|
3
|
+
export interface PopularContentGridProps extends Omit<CardListProps, "infinite"> {
|
|
4
|
+
query?: PopularQuery;
|
|
5
|
+
/** Controlled period; otherwise the selector starts at 7d. */
|
|
6
|
+
period?: PopularPeriod;
|
|
7
|
+
onPeriodChange?: (period: PopularPeriod) => void;
|
|
8
|
+
locked?: LockedCards;
|
|
9
|
+
}
|
|
10
|
+
/** A popularity listing with matched score/age windows and infinite paging. */
|
|
11
|
+
export declare function PopularContentGrid({ query, period, onPeriodChange, locked, ...p }: PopularContentGridProps): import('react').JSX.Element;
|