@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.
Files changed (48) hide show
  1. package/README.md +58 -1
  2. package/dist/client/discovery.d.ts +14 -1
  3. package/dist/client/discovery.js +27 -14
  4. package/dist/client/discovery.js.map +1 -1
  5. package/dist/client/generated/routes.d.ts +18 -5
  6. package/dist/client/generated/routes.js +39 -0
  7. package/dist/client/generated/routes.js.map +1 -1
  8. package/dist/client/generated/wire.d.ts +10 -0
  9. package/dist/client/generated/wire.js.map +1 -1
  10. package/dist/client/index.d.ts +1 -1
  11. package/dist/client.js +17 -17
  12. package/dist/components/content/card.d.ts +28 -6
  13. package/dist/components/content/card.js +158 -132
  14. package/dist/components/content/card.js.map +1 -1
  15. package/dist/components/content/daily-releases.d.ts +15 -0
  16. package/dist/components/content/daily-releases.js +173 -0
  17. package/dist/components/content/daily-releases.js.map +1 -0
  18. package/dist/components/content/grid.d.ts +14 -1
  19. package/dist/components/content/grid.js +75 -34
  20. package/dist/components/content/grid.js.map +1 -1
  21. package/dist/components/content/index.d.ts +4 -2
  22. package/dist/components/content/popular.d.ts +11 -0
  23. package/dist/components/content/popular.js +49 -0
  24. package/dist/components/content/popular.js.map +1 -0
  25. package/dist/index.js +22 -20
  26. package/dist/locales/discovery/de.js +4 -0
  27. package/dist/locales/discovery/de.js.map +1 -1
  28. package/dist/locales/discovery/en.d.ts +4 -0
  29. package/dist/locales/discovery/en.js +4 -0
  30. package/dist/locales/discovery/en.js.map +1 -1
  31. package/dist/locales/discovery/es.js +4 -0
  32. package/dist/locales/discovery/es.js.map +1 -1
  33. package/dist/locales/discovery/ja.js +4 -0
  34. package/dist/locales/discovery/ja.js.map +1 -1
  35. package/dist/locales/discovery/ko.js +4 -0
  36. package/dist/locales/discovery/ko.js.map +1 -1
  37. package/dist/locales/discovery/zh.js +4 -0
  38. package/dist/locales/discovery/zh.js.map +1 -1
  39. package/dist/locales/en.d.ts +4 -0
  40. package/dist/react/discovery.d.ts +12 -2
  41. package/dist/react/discovery.js +32 -23
  42. package/dist/react/discovery.js.map +1 -1
  43. package/dist/react/index.d.ts +1 -1
  44. package/dist/react/resources.js +11 -11
  45. package/dist/react/resources.js.map +1 -1
  46. package/dist/react.js +10 -10
  47. package/dist/styles.css +1 -1
  48. package/package.json +1 -1
@@ -12,12 +12,12 @@ import { cn as _ } from "cn";
12
12
  import { FavouriteIcon as v, LockIcon as y, ThumbsUpIcon as b } from "@hugeicons/core-free-icons";
13
13
  import { HugeiconsIcon as x } from "@hugeicons/react";
14
14
  //#region src/components/content/card.tsx
15
- function S({ card: e, decorate: t }) {
16
- let n = t?.(e);
17
- return n == null || n === !1 ? null : /* @__PURE__ */ h("span", {
18
- className: "pointer-events-none absolute inset-0",
15
+ function S({ card: e, decorate: t, aboveTitle: n }) {
16
+ let r = t?.(e);
17
+ return r == null || r === !1 ? null : /* @__PURE__ */ h("span", {
18
+ className: _("pointer-events-none absolute inset-0", n && "z-20"),
19
19
  "data-ckui": "card-decorations",
20
- children: n
20
+ children: r
21
21
  });
22
22
  }
23
23
  var C = (e, t) => new Intl.NumberFormat(t, {
@@ -42,127 +42,148 @@ function T(e, t) {
42
42
  t
43
43
  ]);
44
44
  }
45
- function E({ card: r, href: o, onOpen: c, shape: l = "portrait", groupKinds: d, chips: p = 3, tiers: E, labels: k, owner: A = !0, ownerHref: j, decorate: M, children: N, className: P }) {
46
- let F = n(), { t: I } = F, L = t(), R = o ? o(r) : r.path, z = T(R, f(() => c?.(r), [c, r])), B = w(F, r.access, E, k), V = r.version, H = r.viewer && r.viewer.progress_max > 0 ? Math.min(1, r.viewer.progress / r.viewer.progress_max) : 0, U = d ?? Object.keys(r.entities), W = V.duration_ms ? Math.round(V.duration_ms / 6e4) : 0, G = [
47
- ...V.languages.map(L),
48
- ...V.traits.map((e) => u(k, e)),
49
- V.pages ? F.plural("contents.pages", V.pages) : null,
50
- W ? F.plural("contents.minutes", W) : null
51
- ].filter(Boolean), K = r.title || r.code, [q, J] = m(null);
45
+ function E({ card: r, href: o, onOpen: c, shape: l = "portrait", aspectRatio: d, titlePosition: p = "below", loadMedia: E = !0, groupKinds: k, chips: A = 3, filterEntity: j, entityStyle: M = "chips", metadata: N, showCounts: ee = !0, tiers: P, labels: F, owner: I = !0, ownerHref: L, decorate: R, children: z, className: te }) {
46
+ let B = n(), { t: V } = B, ne = t(), H = o ? o(r) : r.path, U = T(H, f(() => c?.(r), [c, r])), W = w(B, r.access, P, F), G = r.version, K = r.viewer && r.viewer.progress_max > 0 ? Math.min(1, r.viewer.progress / r.viewer.progress_max) : 0, q = (k ?? Object.keys(r.entities)).flatMap((e) => (r.entities[e] ?? []).filter((t) => !j || j(t, e)).slice(0, A).map((t) => ({
47
+ kind: e,
48
+ entity: t
49
+ }))), J = G.duration_ms ? Math.round(G.duration_ms / 6e4) : 0, Y = N?.traits, re = Y === !1 ? [] : Array.isArray(Y) ? G.traits.filter((e) => Y.includes(e)) : G.traits, X = [
50
+ ...N?.languages === !1 ? [] : G.languages.map(ne),
51
+ ...re.map((e) => u(F, e)),
52
+ N?.pages !== !1 && G.pages ? B.plural("contents.pages", G.pages) : null,
53
+ N?.duration !== !1 && J ? B.plural("contents.minutes", J) : null
54
+ ].filter(Boolean), Z = r.title || r.code, Q = q.length > 0 ? /* @__PURE__ */ h("ul", {
55
+ className: _("flex flex-wrap", M === "text" ? "gap-x-2 gap-y-1" : "gap-1"),
56
+ "data-ckui": "card-groups",
57
+ children: q.map(({ kind: e, entity: t }) => /* @__PURE__ */ h("li", {
58
+ "data-kind": e,
59
+ children: /* @__PURE__ */ h(O, {
60
+ url: t.path,
61
+ name: t.name || t.slug,
62
+ variant: M
63
+ })
64
+ }, `${e}:${t.id}`))
65
+ }) : null, $ = /* @__PURE__ */ g("a", {
66
+ href: H,
67
+ onClick: U,
68
+ className: _("font-medium break-words focus-visible:underline", p === "cover" ? "absolute inset-x-0 bottom-0 z-10 bg-linear-to-t from-black/90 via-black/60 to-transparent px-3 pt-10 pb-3 text-white hover:underline" : "hover:underline"),
69
+ "data-ckui": "card-title",
70
+ children: [/* @__PURE__ */ h("span", {
71
+ className: "line-clamp-2",
72
+ children: Z
73
+ }), W && /* @__PURE__ */ g("span", {
74
+ className: "sr-only",
75
+ children: [
76
+ " (",
77
+ W,
78
+ ")"
79
+ ]
80
+ })]
81
+ }), [ie, ae] = m(null);
52
82
  return /* @__PURE__ */ g("article", {
53
- ref: J,
54
- className: _("group/card grid content-start gap-2", P),
83
+ ref: ae,
84
+ className: _("group/card grid content-start gap-2", te),
55
85
  "data-ckui": "content-card",
56
86
  "data-id": r.id,
57
- "data-locked": B ? "" : void 0,
87
+ "data-locked": W ? "" : void 0,
58
88
  lang: r.language || void 0,
59
89
  children: [/* @__PURE__ */ g("div", {
60
90
  className: "relative overflow-hidden rounded-lg",
61
- children: [/* @__PURE__ */ g("a", {
62
- href: R,
63
- onClick: z,
64
- className: "relative block overflow-hidden rounded-lg bg-muted",
65
- tabIndex: -1,
66
- "aria-hidden": !0,
67
- children: [
68
- /* @__PURE__ */ h("div", {
69
- className: l === "landscape" ? "aspect-video" : "aspect-[3/4]",
70
- children: /* @__PURE__ */ h(e, {
71
- outputs: r.cover,
72
- alt: "",
73
- fit: "cover",
74
- skeleton: !0,
75
- className: "size-full transition-transform group-hover/card:scale-[1.02]",
76
- fallback: /* @__PURE__ */ h("span", {
77
- className: "grid size-full place-items-center text-2xl font-semibold text-muted-foreground",
78
- children: K.charAt(0).toUpperCase()
91
+ "data-ckui": "card-cover",
92
+ children: [
93
+ /* @__PURE__ */ g("a", {
94
+ href: H,
95
+ onClick: U,
96
+ className: "relative block overflow-hidden rounded-lg bg-muted",
97
+ tabIndex: -1,
98
+ "aria-hidden": !0,
99
+ children: [
100
+ /* @__PURE__ */ h("div", {
101
+ "data-ckui": "card-cover-frame",
102
+ className: d === void 0 ? l === "landscape" ? "aspect-video" : "aspect-[3/4]" : void 0,
103
+ style: d === void 0 ? void 0 : { aspectRatio: d },
104
+ children: E ? /* @__PURE__ */ h(e, {
105
+ outputs: r.cover,
106
+ alt: "",
107
+ fit: "cover",
108
+ skeleton: !0,
109
+ loading: "lazy",
110
+ className: "size-full transition-transform group-hover/card:scale-[1.02]",
111
+ fallback: /* @__PURE__ */ h("span", {
112
+ className: "grid size-full place-items-center text-2xl font-semibold text-muted-foreground",
113
+ children: Z.charAt(0).toUpperCase()
114
+ })
115
+ }) : /* @__PURE__ */ h("span", {
116
+ className: "block size-full bg-muted",
117
+ "aria-hidden": !0
118
+ })
119
+ }),
120
+ E && G.hls && /* @__PURE__ */ h(i, {
121
+ item: {
122
+ kind: r.kind,
123
+ id: r.id,
124
+ version: G.id
125
+ },
126
+ dir: G.hls,
127
+ duration: G.duration_ms ? G.duration_ms / 1e3 : void 0,
128
+ target: ie
129
+ }),
130
+ W && /* @__PURE__ */ g("span", {
131
+ className: "absolute top-2 left-2 inline-flex items-center gap-1 rounded-md bg-background/85 px-1.5 py-0.5 text-xs font-medium text-foreground shadow-sm",
132
+ "data-ckui": "card-lock",
133
+ children: [/* @__PURE__ */ h(x, {
134
+ icon: y,
135
+ strokeWidth: 2,
136
+ className: "size-3.5",
137
+ "aria-hidden": !0
138
+ }), W]
139
+ }),
140
+ K > 0 && /* @__PURE__ */ h("span", {
141
+ className: _("absolute inset-x-0 bottom-0 h-1 bg-background/60", p === "cover" && "z-20"),
142
+ "data-ckui": "card-progress",
143
+ children: /* @__PURE__ */ h("span", {
144
+ className: "block h-full bg-primary",
145
+ style: { width: `${Math.round(K * 100)}%` }
79
146
  })
80
147
  })
81
- }),
82
- V.hls && /* @__PURE__ */ h(i, {
83
- item: {
84
- kind: r.kind,
85
- id: r.id,
86
- version: V.id
87
- },
88
- dir: V.hls,
89
- duration: V.duration_ms ? V.duration_ms / 1e3 : void 0,
90
- target: q
91
- }),
92
- B && /* @__PURE__ */ g("span", {
93
- className: "absolute top-2 left-2 inline-flex items-center gap-1 rounded-md bg-background/85 px-1.5 py-0.5 text-xs font-medium text-foreground shadow-sm",
94
- "data-ckui": "card-lock",
95
- children: [/* @__PURE__ */ h(x, {
96
- icon: y,
97
- strokeWidth: 2,
98
- className: "size-3.5",
99
- "aria-hidden": !0
100
- }), B]
101
- }),
102
- H > 0 && /* @__PURE__ */ h("span", {
103
- className: "absolute inset-x-0 bottom-0 h-1 bg-background/60",
104
- "data-ckui": "card-progress",
105
- children: /* @__PURE__ */ h("span", {
106
- className: "block h-full bg-primary",
107
- style: { width: `${Math.round(H * 100)}%` }
108
- })
109
- })
110
- ]
111
- }), /* @__PURE__ */ h(S, {
112
- card: r,
113
- decorate: M
114
- })]
148
+ ]
149
+ }),
150
+ p === "cover" && $,
151
+ E && /* @__PURE__ */ h(S, {
152
+ card: r,
153
+ decorate: R,
154
+ aboveTitle: p === "cover"
155
+ })
156
+ ]
115
157
  }), /* @__PURE__ */ g("div", {
116
158
  className: "grid gap-1",
117
159
  children: [
118
- /* @__PURE__ */ g("a", {
119
- href: R,
120
- onClick: z,
121
- className: "line-clamp-2 font-medium break-words hover:underline",
122
- "data-ckui": "card-title",
123
- children: [K, B && /* @__PURE__ */ g("span", {
124
- className: "sr-only",
125
- children: [
126
- " (",
127
- B,
128
- ")"
129
- ]
130
- })]
131
- }),
132
- A && r.owner && /* @__PURE__ */ h(D, {
160
+ p === "below" && $,
161
+ M === "text" && Q,
162
+ I && r.owner && /* @__PURE__ */ h(D, {
133
163
  group: r.owner,
134
- href: j
164
+ href: L,
165
+ loadMedia: E
135
166
  }),
136
- G.length > 0 && /* @__PURE__ */ h("p", {
167
+ X.length > 0 && /* @__PURE__ */ h("p", {
137
168
  className: "text-xs text-muted-foreground",
138
- children: G.join(" · ")
169
+ children: X.join(" · ")
139
170
  }),
140
171
  r.viewer?.completed && /* @__PURE__ */ h(a, {
141
172
  variant: "secondary",
142
173
  className: "justify-self-start",
143
- children: I("contents.completed")
174
+ children: V("contents.completed")
144
175
  }),
145
176
  !r.access.allowed && r.access.free_after && /* @__PURE__ */ h("p", {
146
177
  className: "text-xs text-muted-foreground",
147
- children: I("contents.freeFrom", { date: s(F, r.access.free_after, !1) })
148
- }),
149
- U.some((e) => r.entities[e]?.length) && /* @__PURE__ */ h("ul", {
150
- className: "flex flex-wrap gap-1",
151
- "data-ckui": "card-groups",
152
- children: U.flatMap((e) => (r.entities[e] ?? []).slice(0, p).map((t) => /* @__PURE__ */ h("li", {
153
- "data-kind": e,
154
- children: /* @__PURE__ */ h(O, {
155
- url: t.path,
156
- name: t.name || t.slug
157
- })
158
- }, `${e}:${t.id}`)))
178
+ children: V("contents.freeFrom", { date: s(B, r.access.free_after, !1) })
159
179
  }),
160
- (r.counts.favorites !== void 0 || r.counts.likes !== void 0) && /* @__PURE__ */ g("p", {
180
+ M === "chips" && Q,
181
+ ee && (r.counts.favorites !== void 0 || r.counts.likes !== void 0) && /* @__PURE__ */ g("p", {
161
182
  className: "flex items-center gap-3 text-xs text-muted-foreground tabular-nums",
162
183
  "data-ckui": "card-counts",
163
184
  children: [r.counts.favorites !== void 0 && /* @__PURE__ */ g("span", {
164
185
  className: "inline-flex items-center gap-1",
165
- title: I("contents.favorites"),
186
+ title: V("contents.favorites"),
166
187
  children: [
167
188
  /* @__PURE__ */ h(x, {
168
189
  icon: v,
@@ -172,13 +193,13 @@ function E({ card: r, href: o, onOpen: c, shape: l = "portrait", groupKinds: d,
172
193
  }),
173
194
  /* @__PURE__ */ h("span", {
174
195
  className: "sr-only",
175
- children: I("contents.favorites")
196
+ children: V("contents.favorites")
176
197
  }),
177
- C(r.counts.favorites, F.language)
198
+ C(r.counts.favorites, B.language)
178
199
  ]
179
200
  }), r.counts.likes !== void 0 && /* @__PURE__ */ g("span", {
180
201
  className: "inline-flex items-center gap-1",
181
- title: I("contents.likes"),
202
+ title: V("contents.likes"),
182
203
  children: [
183
204
  /* @__PURE__ */ h(x, {
184
205
  icon: b,
@@ -188,18 +209,18 @@ function E({ card: r, href: o, onOpen: c, shape: l = "portrait", groupKinds: d,
188
209
  }),
189
210
  /* @__PURE__ */ h("span", {
190
211
  className: "sr-only",
191
- children: I("contents.likes")
212
+ children: V("contents.likes")
192
213
  }),
193
- C(r.counts.likes, F.language)
214
+ C(r.counts.likes, B.language)
194
215
  ]
195
216
  })]
196
217
  }),
197
- N
218
+ z
198
219
  ]
199
220
  })]
200
221
  });
201
222
  }
202
- function D({ group: e, href: t, className: n }) {
223
+ function D({ group: e, href: t, className: n, loadMedia: r = !0 }) {
203
224
  return /* @__PURE__ */ h("span", {
204
225
  className: _("flex min-w-0 text-xs text-muted-foreground", n),
205
226
  "data-ckui": "card-owner",
@@ -208,9 +229,12 @@ function D({ group: e, href: t, className: n }) {
208
229
  group: e,
209
230
  href: t,
210
231
  className: "inline-flex min-w-0 items-center gap-1.5 hover:text-foreground",
211
- children: [/* @__PURE__ */ h(c, {
232
+ children: [r ? /* @__PURE__ */ h(c, {
212
233
  group: e,
213
234
  className: "size-5 shrink-0 text-[0.625rem]"
235
+ }) : /* @__PURE__ */ h("span", {
236
+ className: "size-5 shrink-0 rounded-full bg-muted",
237
+ "aria-hidden": !0
214
238
  }), /* @__PURE__ */ h("span", {
215
239
  className: "truncate",
216
240
  children: e.name || e.slug
@@ -218,55 +242,57 @@ function D({ group: e, href: t, className: n }) {
218
242
  })
219
243
  });
220
244
  }
221
- function O({ url: e, name: t }) {
222
- let n = T(e), r = "inline-block rounded-md border border-border px-1.5 py-0.5 text-xs text-muted-foreground";
245
+ function O({ url: e, name: t, variant: n = "chips" }) {
246
+ let r = T(e), i = n === "text" ? "text-sm text-muted-foreground" : "inline-block rounded-md border border-border px-1.5 py-0.5 text-xs text-muted-foreground";
223
247
  return e ? /* @__PURE__ */ h("a", {
224
248
  href: e,
225
- onClick: n,
226
- className: _(r, "hover:bg-muted hover:text-foreground"),
249
+ onClick: r,
250
+ className: _(i, "hover:text-foreground", n === "text" ? "hover:underline" : "hover:bg-muted"),
227
251
  children: t
228
252
  }) : /* @__PURE__ */ h("span", {
229
- className: r,
253
+ className: i,
230
254
  children: t
231
255
  });
232
256
  }
233
- function k({ card: t, href: r, onOpen: i, tiers: a, labels: s, byline: c, owner: l = !0, ownerHref: u, decorate: d, children: p, className: m }) {
234
- let b = n(), E = r ? r(t) : t.path, O = T(E, f(() => i?.(t), [i, t])), k = w(b, t.access, a, s), A = t.title || t.code;
257
+ function k({ card: t, href: r, onOpen: i, tiers: a, labels: s, byline: c, owner: l = !0, ownerHref: u, loadMedia: d = !0, showCounts: p = !0, decorate: m, children: b, className: E }) {
258
+ let O = n(), k = r ? r(t) : t.path, A = T(k, f(() => i?.(t), [i, t])), j = w(O, t.access, a, s), M = t.title || t.code;
235
259
  return /* @__PURE__ */ g("article", {
236
- className: _("grid gap-3 rounded-lg border border-border p-3", m),
260
+ className: _("grid gap-3 rounded-lg border border-border p-3", E),
237
261
  "data-ckui": "post-card",
238
262
  "data-id": t.id,
239
- "data-locked": k ? "" : void 0,
263
+ "data-locked": j ? "" : void 0,
240
264
  lang: t.language || void 0,
241
265
  children: [
242
266
  c ?? (l && t.owner && /* @__PURE__ */ h(D, {
243
267
  group: t.owner,
244
268
  href: u,
269
+ loadMedia: d,
245
270
  className: "text-sm"
246
271
  })),
247
272
  /* @__PURE__ */ g("a", {
248
- href: E,
249
- onClick: O,
273
+ href: k,
274
+ onClick: A,
250
275
  className: "grid gap-2",
251
276
  children: [/* @__PURE__ */ h("span", {
252
277
  className: "font-medium break-words hover:underline",
253
278
  "data-ckui": "card-title",
254
- children: A
279
+ children: M
255
280
  }), t.cover.length > 0 && /* @__PURE__ */ g("span", {
256
281
  className: "relative block aspect-video overflow-hidden rounded-md bg-muted",
257
282
  children: [
258
- /* @__PURE__ */ h(e, {
283
+ d && /* @__PURE__ */ h(e, {
259
284
  outputs: t.cover,
260
285
  alt: "",
261
286
  fit: "cover",
262
287
  skeleton: !0,
263
- className: _("size-full", k && "blur-md")
288
+ loading: "lazy",
289
+ className: _("size-full", j && "blur-md")
264
290
  }),
265
- /* @__PURE__ */ h(S, {
291
+ d && /* @__PURE__ */ h(S, {
266
292
  card: t,
267
- decorate: d
293
+ decorate: m
268
294
  }),
269
- k && /* @__PURE__ */ h("span", {
295
+ j && /* @__PURE__ */ h("span", {
270
296
  className: "absolute inset-0 grid place-items-center",
271
297
  children: /* @__PURE__ */ g("span", {
272
298
  className: "inline-flex items-center gap-1 rounded-md bg-background/85 px-2 py-1 text-xs font-medium shadow-sm",
@@ -276,7 +302,7 @@ function k({ card: t, href: r, onOpen: i, tiers: a, labels: s, byline: c, owner:
276
302
  strokeWidth: 2,
277
303
  className: "size-3.5",
278
304
  "aria-hidden": !0
279
- }), k]
305
+ }), j]
280
306
  })
281
307
  })
282
308
  ]
@@ -286,11 +312,11 @@ function k({ card: t, href: r, onOpen: i, tiers: a, labels: s, byline: c, owner:
286
312
  className: "flex flex-wrap items-center gap-3 text-xs text-muted-foreground",
287
313
  children: [
288
314
  t.published_at && /* @__PURE__ */ h(o, { at: t.published_at }),
289
- !t.cover.length && k && /* @__PURE__ */ h("span", {
315
+ !t.cover.length && j && /* @__PURE__ */ h("span", {
290
316
  "data-ckui": "card-lock",
291
- children: k
317
+ children: j
292
318
  }),
293
- t.counts.favorites !== void 0 && /* @__PURE__ */ g("span", {
319
+ p && t.counts.favorites !== void 0 && /* @__PURE__ */ g("span", {
294
320
  className: "inline-flex items-center gap-1 tabular-nums",
295
321
  "data-ckui": "card-counts",
296
322
  children: [
@@ -302,14 +328,14 @@ function k({ card: t, href: r, onOpen: i, tiers: a, labels: s, byline: c, owner:
302
328
  }),
303
329
  /* @__PURE__ */ h("span", {
304
330
  className: "sr-only",
305
- children: b.t("contents.favorites")
331
+ children: O.t("contents.favorites")
306
332
  }),
307
- C(t.counts.favorites, b.language)
333
+ C(t.counts.favorites, O.language)
308
334
  ]
309
335
  })
310
336
  ]
311
337
  }),
312
- p
338
+ b
313
339
  ]
314
340
  });
315
341
  }
@@ -1 +1 @@
1
- {"version":3,"file":"card.js","names":[],"sources":["../../../src/components/content/card.tsx"],"sourcesContent":["import { FavouriteIcon, LockIcon, ThumbsUpIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useContext, useState, type MouseEvent, type ReactNode } from \"react\";\nimport type { Card, CardAccess } from \"../../client/generated/wire.js\";\nimport { useLanguageName, useMessages } from \"../../i18n/context.js\";\nimport type { Translator } from \"../../i18n/messages.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { HoverPreview } from \"../hover-preview.js\";\nimport { RenditionImg } from \"../rendition-img.js\";\nimport { formatDate, RelativeTime } from \"../social/parts.js\";\nimport { GroupAvatar, GroupLink, labelOf, type GroupLike, type Labels } from \"../groups/parts.js\";\nimport { priceLabel } from \"./paywall.js\";\n\nexport interface ContentCardProps {\n card: Card;\n /** The card's link; default its canonical path. */\n href?: (card: Card) => string | undefined;\n /** Before the card's link is followed: a click signal. */\n onOpen?: (card: Card) => void;\n /** Cover shape: \"portrait\" (3:4, readers) or \"landscape\" (16:9, players). Default portrait. */\n shape?: \"portrait\" | \"landscape\";\n /** The group kinds whose chips show, in order; default every kind the card names. */\n groupKinds?: readonly string[];\n /** Chips per group kind. Default 3. */\n chips?: number;\n /** Tiers the site sells, so a lock names them; others show as \"for sale\". */\n tiers?: readonly string[];\n /** Display names of kinds, traits and tiers by identifier. */\n labels?: Labels;\n /** Show the owner group (a channel) when the card has one; default true, false on that group's own feed. */\n owner?: boolean;\n /** The owner group's link; default its canonical path. */\n ownerHref?: (g: GroupLike) => string | undefined;\n /** Over the cover: the host's badges for the card, placed in the cover's box; clicks reach the card. */\n decorate?: (card: Card) => ReactNode;\n /** Below the meta line: the host's own. */\n children?: ReactNode;\n className?: string;\n}\n\n/** The host's badges over a cover. */\nfunction Decorations({ card, decorate }: { card: Card; decorate?: (card: Card) => ReactNode }) {\n const badges = decorate?.(card);\n if (badges == null || badges === false) return null;\n return (\n <span className=\"pointer-events-none absolute inset-0\" data-ckui=\"card-decorations\">\n {badges}\n </span>\n );\n}\n\nconst compact = (n: number, language?: string) => new Intl.NumberFormat(language, { notation: \"compact\", maximumFractionDigits: 1 }).format(n);\n\n/** Why a card is locked, in words (its price when it is on sale); null when it opens. */\nexport function lockLabel(m: Translator, a: CardAccess, tiers: readonly string[] = [], labels?: Labels): string | null {\n if (a.allowed) return null;\n if (a.unknown) return m.t(\"contents.unknownAccess\");\n const offer = a.offers?.[0];\n if (offer) return offer.target.kind === \"group\" && Number(offer.terms.amount) === 0 ? m.t(\"contents.joinFree\") : priceLabel(m, offer.terms);\n const tier = a.sell?.find((k) => tiers.includes(k));\n if (tier) return labelOf(labels, tier);\n if (a.sell?.some((k) => k.includes(\":group:\"))) return m.t(\"contents.members\");\n if (a.sell?.length) return m.t(\"contents.forSale\");\n return m.t(\"contents.locked\");\n}\n\n/** A link followed in-app through the provider's navigate. */\nfunction useLink(url: string | undefined, before?: () => void) {\n const navigate = useContext(ContentKitContext)?.navigate;\n return useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n before?.();\n if (!navigate || !url || e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n e.preventDefault();\n navigate(url);\n },\n [navigate, url, before],\n );\n}\n\n/** One content in a listing: cover (a video the reader opens previews over it on hover), title, version, groups, counts, the reader's progress and why it is locked; what its kind's locked view hides is absent. */\nexport function ContentCard({ card, href, onOpen, shape = \"portrait\", groupKinds, chips = 3, tiers, labels, owner = true, ownerHref, decorate, children, className }: ContentCardProps) {\n const m = useMessages();\n const { t } = m;\n const languageName = useLanguageName();\n const url = href ? href(card) : card.path;\n const open = useCallback(() => onOpen?.(card), [onOpen, card]);\n const onClick = useLink(url, open);\n const lock = lockLabel(m, card.access, tiers, labels);\n const v = card.version;\n const progress = card.viewer && card.viewer.progress_max > 0 ? Math.min(1, card.viewer.progress / card.viewer.progress_max) : 0;\n const kinds = groupKinds ?? Object.keys(card.entities);\n const minutes = v.duration_ms ? Math.round(v.duration_ms / 60_000) : 0;\n const meta = [\n ...v.languages.map(languageName),\n ...v.traits.map((x) => labelOf(labels, x)),\n v.pages ? m.plural(\"contents.pages\", v.pages) : null,\n minutes ? m.plural(\"contents.minutes\", minutes) : null,\n ].filter(Boolean);\n const title = card.title || card.code;\n const [article, setArticle] = useState<HTMLElement | null>(null);\n return (\n <article ref={setArticle} className={cn(\"group/card grid content-start gap-2\", className)} data-ckui=\"content-card\" data-id={card.id} data-locked={lock ? \"\" : undefined} lang={card.language || undefined}>\n <div className=\"relative overflow-hidden rounded-lg\">\n <a href={url} onClick={onClick} className=\"relative block overflow-hidden rounded-lg bg-muted\" tabIndex={-1} aria-hidden>\n <div className={shape === \"landscape\" ? \"aspect-video\" : \"aspect-[3/4]\"}>\n <RenditionImg\n outputs={card.cover}\n alt=\"\"\n fit=\"cover\"\n skeleton\n className=\"size-full transition-transform group-hover/card:scale-[1.02]\"\n fallback={<span className=\"grid size-full place-items-center text-2xl font-semibold text-muted-foreground\">{title.charAt(0).toUpperCase()}</span>}\n />\n </div>\n {v.hls && <HoverPreview item={{ kind: card.kind, id: card.id, version: v.id }} dir={v.hls} duration={v.duration_ms ? v.duration_ms / 1000 : undefined} target={article} />}\n {lock && (\n <span className=\"absolute top-2 left-2 inline-flex items-center gap-1 rounded-md bg-background/85 px-1.5 py-0.5 text-xs font-medium text-foreground shadow-sm\" data-ckui=\"card-lock\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n {lock}\n </span>\n )}\n {progress > 0 && (\n <span className=\"absolute inset-x-0 bottom-0 h-1 bg-background/60\" data-ckui=\"card-progress\">\n <span className=\"block h-full bg-primary\" style={{ width: `${Math.round(progress * 100)}%` }} />\n </span>\n )}\n </a>\n <Decorations card={card} decorate={decorate} />\n </div>\n <div className=\"grid gap-1\">\n <a href={url} onClick={onClick} className=\"line-clamp-2 font-medium break-words hover:underline\" data-ckui=\"card-title\">\n {title}\n {lock && <span className=\"sr-only\"> ({lock})</span>}\n </a>\n {owner && card.owner && <OwnerChip group={card.owner} href={ownerHref} />}\n {meta.length > 0 && <p className=\"text-xs text-muted-foreground\">{meta.join(\" · \")}</p>}\n {card.viewer?.completed && (\n <Badge variant=\"secondary\" className=\"justify-self-start\">\n {t(\"contents.completed\")}\n </Badge>\n )}\n {!card.access.allowed && card.access.free_after && <p className=\"text-xs text-muted-foreground\">{t(\"contents.freeFrom\", { date: formatDate(m, card.access.free_after, false) })}</p>}\n {kinds.some((k) => card.entities[k]?.length) && (\n <ul className=\"flex flex-wrap gap-1\" data-ckui=\"card-groups\">\n {kinds.flatMap((k) =>\n (card.entities[k] ?? []).slice(0, chips).map((g) => (\n <li key={`${k}:${g.id}`} data-kind={k}>\n <Chip url={g.path} name={g.name || g.slug} />\n </li>\n )),\n )}\n </ul>\n )}\n {(card.counts.favorites !== undefined || card.counts.likes !== undefined) && (\n <p className=\"flex items-center gap-3 text-xs text-muted-foreground tabular-nums\" data-ckui=\"card-counts\">\n {card.counts.favorites !== undefined && (\n <span className=\"inline-flex items-center gap-1\" title={t(\"contents.favorites\")}>\n <HugeiconsIcon icon={FavouriteIcon} strokeWidth={2} className={cn(\"size-3.5\", card.viewer?.favorited && \"fill-current text-primary\")} aria-hidden />\n <span className=\"sr-only\">{t(\"contents.favorites\")}</span>\n {compact(card.counts.favorites, m.language)}\n </span>\n )}\n {card.counts.likes !== undefined && (\n <span className=\"inline-flex items-center gap-1\" title={t(\"contents.likes\")}>\n <HugeiconsIcon icon={ThumbsUpIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n <span className=\"sr-only\">{t(\"contents.likes\")}</span>\n {compact(card.counts.likes, m.language)}\n </span>\n )}\n </p>\n )}\n {children}\n </div>\n </article>\n );\n}\n\n/** The group that owns a content (a channel): its avatar and name, linking to its page. */\nexport function OwnerChip({ group, href, className }: { group: GroupLike; href?: (g: GroupLike) => string | undefined; className?: string }) {\n return (\n <span className={cn(\"flex min-w-0 text-xs text-muted-foreground\", className)} data-ckui=\"card-owner\" data-id={group.id}>\n <GroupLink group={group} href={href} className=\"inline-flex min-w-0 items-center gap-1.5 hover:text-foreground\">\n <GroupAvatar group={group} className=\"size-5 shrink-0 text-[0.625rem]\" />\n <span className=\"truncate\">{group.name || group.slug}</span>\n </GroupLink>\n </span>\n );\n}\n\nfunction Chip({ url, name }: { url?: string; name: string }) {\n const onClick = useLink(url);\n const cls = \"inline-block rounded-md border border-border px-1.5 py-0.5 text-xs text-muted-foreground\";\n return url ? (\n <a href={url} onClick={onClick} className={cn(cls, \"hover:bg-muted hover:text-foreground\")}>\n {name}\n </a>\n ) : (\n <span className={cls}>{name}</span>\n );\n}\n\nexport interface PostCardProps extends Omit<ContentCardProps, \"shape\" | \"chips\" | \"groupKinds\"> {\n /** Above the title: the post's channel or author; default its owner group's chip. */\n byline?: ReactNode;\n}\n\n/** A post in a feed: a wide cover, its title, when it was published and what unlocking it costs. */\nexport function PostCard({ card, href, onOpen, tiers, labels, byline, owner = true, ownerHref, decorate, children, className }: PostCardProps) {\n const m = useMessages();\n const url = href ? href(card) : card.path;\n const open = useCallback(() => onOpen?.(card), [onOpen, card]);\n const onClick = useLink(url, open);\n const lock = lockLabel(m, card.access, tiers, labels);\n const title = card.title || card.code;\n return (\n <article className={cn(\"grid gap-3 rounded-lg border border-border p-3\", className)} data-ckui=\"post-card\" data-id={card.id} data-locked={lock ? \"\" : undefined} lang={card.language || undefined}>\n {byline ?? (owner && card.owner && <OwnerChip group={card.owner} href={ownerHref} className=\"text-sm\" />)}\n <a href={url} onClick={onClick} className=\"grid gap-2\">\n <span className=\"font-medium break-words hover:underline\" data-ckui=\"card-title\">\n {title}\n </span>\n {card.cover.length > 0 && (\n <span className=\"relative block aspect-video overflow-hidden rounded-md bg-muted\">\n <RenditionImg outputs={card.cover} alt=\"\" fit=\"cover\" skeleton className={cn(\"size-full\", lock && \"blur-md\")} />\n <Decorations card={card} decorate={decorate} />\n {lock && (\n <span className=\"absolute inset-0 grid place-items-center\">\n <span className=\"inline-flex items-center gap-1 rounded-md bg-background/85 px-2 py-1 text-xs font-medium shadow-sm\" data-ckui=\"card-lock\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n {lock}\n </span>\n </span>\n )}\n </span>\n )}\n </a>\n <p className=\"flex flex-wrap items-center gap-3 text-xs text-muted-foreground\">\n {card.published_at && <RelativeTime at={card.published_at} />}\n {!card.cover.length && lock && <span data-ckui=\"card-lock\">{lock}</span>}\n {card.counts.favorites !== undefined && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\" data-ckui=\"card-counts\">\n <HugeiconsIcon icon={FavouriteIcon} strokeWidth={2} className={cn(\"size-3.5\", card.viewer?.favorited && \"fill-current text-primary\")} aria-hidden />\n <span className=\"sr-only\">{m.t(\"contents.favorites\")}</span>\n {compact(card.counts.favorites, m.language)}\n </span>\n )}\n </p>\n {children}\n </article>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"card.js","names":[],"sources":["../../../src/components/content/card.tsx"],"sourcesContent":["import { FavouriteIcon, LockIcon, ThumbsUpIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useContext, useState, type CSSProperties, type MouseEvent, type ReactNode } from \"react\";\nimport type { Card, CardAccess, Chip as EntityChip } from \"../../client/generated/wire.js\";\nimport { useLanguageName, useMessages } from \"../../i18n/context.js\";\nimport type { Translator } from \"../../i18n/messages.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { HoverPreview } from \"../hover-preview.js\";\nimport { RenditionImg } from \"../rendition-img.js\";\nimport { formatDate, RelativeTime } from \"../social/parts.js\";\nimport { GroupAvatar, GroupLink, labelOf, type GroupLike, type Labels } from \"../groups/parts.js\";\nimport { priceLabel } from \"./paywall.js\";\n\nexport interface ContentCardProps {\n card: Card;\n /** The card's link; default its canonical path. */\n href?: (card: Card) => string | undefined;\n /** Before the card's link is followed: a click signal. */\n onOpen?: (card: Card) => void;\n /** Cover shape: \"portrait\" (3:4, readers) or \"landscape\" (16:9, players). Default portrait. */\n shape?: \"portrait\" | \"landscape\";\n /** A CSS aspect ratio overriding shape, such as \"182 / 260\". */\n aspectRatio?: CSSProperties[\"aspectRatio\"];\n /** Place the title below the cover (default), or over its bottom gradient. */\n titlePosition?: \"below\" | \"cover\";\n /** Mount images and previews. False keeps the card's text and reserved cover space. */\n loadMedia?: boolean;\n /** The group kinds whose chips show, in order; default every kind the card names. */\n groupKinds?: readonly string[];\n /** Chips per group kind. Default 3. */\n chips?: number;\n /** Select which entity chips appear, before the per-kind chip limit. */\n filterEntity?: (entity: EntityChip, kind: string) => boolean;\n /** Bordered chips (default) or plain linked text. */\n entityStyle?: \"chips\" | \"text\";\n /** Metadata fields to show; omitted fields keep their defaults. */\n metadata?: CardMetadata;\n /** Show like and favorite totals. Default true. */\n showCounts?: boolean;\n /** Tiers the site sells, so a lock names them; others show as \"for sale\". */\n tiers?: readonly string[];\n /** Display names of kinds, traits and tiers by identifier. */\n labels?: Labels;\n /** Show the owner group (a channel) when the card has one; default true, false on that group's own feed. */\n owner?: boolean;\n /** The owner group's link; default its canonical path. */\n ownerHref?: (g: GroupLike) => string | undefined;\n /** Over the cover: the host's badges for the card, placed in the cover's box; clicks reach the card. */\n decorate?: (card: Card) => ReactNode;\n /** Below the meta line: the host's own. */\n children?: ReactNode;\n className?: string;\n}\n\nexport interface CardMetadata {\n languages?: boolean;\n /** True shows all traits; an array selects specific trait identifiers. */\n traits?: boolean | readonly string[];\n pages?: boolean;\n duration?: boolean;\n}\n\n/** The host's badges over a cover. */\nfunction Decorations({ card, decorate, aboveTitle }: { card: Card; decorate?: (card: Card) => ReactNode; aboveTitle?: boolean }) {\n const badges = decorate?.(card);\n if (badges == null || badges === false) return null;\n return (\n <span className={cn(\"pointer-events-none absolute inset-0\", aboveTitle && \"z-20\")} data-ckui=\"card-decorations\">\n {badges}\n </span>\n );\n}\n\nconst compact = (n: number, language?: string) => new Intl.NumberFormat(language, { notation: \"compact\", maximumFractionDigits: 1 }).format(n);\n\n/** Why a card is locked, in words (its price when it is on sale); null when it opens. */\nexport function lockLabel(m: Translator, a: CardAccess, tiers: readonly string[] = [], labels?: Labels): string | null {\n if (a.allowed) return null;\n if (a.unknown) return m.t(\"contents.unknownAccess\");\n const offer = a.offers?.[0];\n if (offer) return offer.target.kind === \"group\" && Number(offer.terms.amount) === 0 ? m.t(\"contents.joinFree\") : priceLabel(m, offer.terms);\n const tier = a.sell?.find((k) => tiers.includes(k));\n if (tier) return labelOf(labels, tier);\n if (a.sell?.some((k) => k.includes(\":group:\"))) return m.t(\"contents.members\");\n if (a.sell?.length) return m.t(\"contents.forSale\");\n return m.t(\"contents.locked\");\n}\n\n/** A link followed in-app through the provider's navigate. */\nfunction useLink(url: string | undefined, before?: () => void) {\n const navigate = useContext(ContentKitContext)?.navigate;\n return useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n before?.();\n if (!navigate || !url || e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n e.preventDefault();\n navigate(url);\n },\n [navigate, url, before],\n );\n}\n\n/** One content in a listing: cover (a video the reader opens previews over it on hover), title, version, groups, counts, the reader's progress and why it is locked; what its kind's locked view hides is absent. */\nexport function ContentCard({ card, href, onOpen, shape = \"portrait\", aspectRatio, titlePosition = \"below\", loadMedia = true, groupKinds, chips = 3, filterEntity, entityStyle = \"chips\", metadata, showCounts = true, tiers, labels, owner = true, ownerHref, decorate, children, className }: ContentCardProps) {\n const m = useMessages();\n const { t } = m;\n const languageName = useLanguageName();\n const url = href ? href(card) : card.path;\n const open = useCallback(() => onOpen?.(card), [onOpen, card]);\n const onClick = useLink(url, open);\n const lock = lockLabel(m, card.access, tiers, labels);\n const v = card.version;\n const progress = card.viewer && card.viewer.progress_max > 0 ? Math.min(1, card.viewer.progress / card.viewer.progress_max) : 0;\n const kinds = groupKinds ?? Object.keys(card.entities);\n const entities = kinds.flatMap((kind) => (card.entities[kind] ?? []).filter((entity) => !filterEntity || filterEntity(entity, kind)).slice(0, chips).map((entity) => ({ kind, entity })));\n const minutes = v.duration_ms ? Math.round(v.duration_ms / 60_000) : 0;\n const selectedTraits = metadata?.traits;\n const traits = selectedTraits === false ? [] : Array.isArray(selectedTraits) ? v.traits.filter((trait) => selectedTraits.includes(trait)) : v.traits;\n const meta = [\n ...(metadata?.languages === false ? [] : v.languages.map(languageName)),\n ...traits.map((x) => labelOf(labels, x)),\n metadata?.pages !== false && v.pages ? m.plural(\"contents.pages\", v.pages) : null,\n metadata?.duration !== false && minutes ? m.plural(\"contents.minutes\", minutes) : null,\n ].filter(Boolean);\n const title = card.title || card.code;\n const entityList = entities.length > 0 ? <ul className={cn(\"flex flex-wrap\", entityStyle === \"text\" ? \"gap-x-2 gap-y-1\" : \"gap-1\")} data-ckui=\"card-groups\">\n {entities.map(({ kind, entity }) => <li key={`${kind}:${entity.id}`} data-kind={kind}><Chip url={entity.path} name={entity.name || entity.slug} variant={entityStyle} /></li>)}\n </ul> : null;\n const titleLink = <a href={url} onClick={onClick} className={cn(\"font-medium break-words focus-visible:underline\", titlePosition === \"cover\" ? \"absolute inset-x-0 bottom-0 z-10 bg-linear-to-t from-black/90 via-black/60 to-transparent px-3 pt-10 pb-3 text-white hover:underline\" : \"hover:underline\")} data-ckui=\"card-title\">\n <span className=\"line-clamp-2\">{title}</span>\n {lock && <span className=\"sr-only\"> ({lock})</span>}\n </a>;\n const [article, setArticle] = useState<HTMLElement | null>(null);\n return (\n <article ref={setArticle} className={cn(\"group/card grid content-start gap-2\", className)} data-ckui=\"content-card\" data-id={card.id} data-locked={lock ? \"\" : undefined} lang={card.language || undefined}>\n <div className=\"relative overflow-hidden rounded-lg\" data-ckui=\"card-cover\">\n <a href={url} onClick={onClick} className=\"relative block overflow-hidden rounded-lg bg-muted\" tabIndex={-1} aria-hidden>\n <div data-ckui=\"card-cover-frame\" className={aspectRatio === undefined ? shape === \"landscape\" ? \"aspect-video\" : \"aspect-[3/4]\" : undefined} style={aspectRatio === undefined ? undefined : { aspectRatio }}>\n {loadMedia ? <RenditionImg\n outputs={card.cover}\n alt=\"\"\n fit=\"cover\"\n skeleton\n loading=\"lazy\"\n className=\"size-full transition-transform group-hover/card:scale-[1.02]\"\n fallback={<span className=\"grid size-full place-items-center text-2xl font-semibold text-muted-foreground\">{title.charAt(0).toUpperCase()}</span>}\n /> : <span className=\"block size-full bg-muted\" aria-hidden />}\n </div>\n {loadMedia && v.hls && <HoverPreview item={{ kind: card.kind, id: card.id, version: v.id }} dir={v.hls} duration={v.duration_ms ? v.duration_ms / 1000 : undefined} target={article} />}\n {lock && (\n <span className=\"absolute top-2 left-2 inline-flex items-center gap-1 rounded-md bg-background/85 px-1.5 py-0.5 text-xs font-medium text-foreground shadow-sm\" data-ckui=\"card-lock\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n {lock}\n </span>\n )}\n {progress > 0 && (\n <span className={cn(\"absolute inset-x-0 bottom-0 h-1 bg-background/60\", titlePosition === \"cover\" && \"z-20\")} data-ckui=\"card-progress\">\n <span className=\"block h-full bg-primary\" style={{ width: `${Math.round(progress * 100)}%` }} />\n </span>\n )}\n </a>\n {titlePosition === \"cover\" && titleLink}\n {loadMedia && <Decorations card={card} decorate={decorate} aboveTitle={titlePosition === \"cover\"} />}\n </div>\n <div className=\"grid gap-1\">\n {titlePosition === \"below\" && titleLink}\n {entityStyle === \"text\" && entityList}\n {owner && card.owner && <OwnerChip group={card.owner} href={ownerHref} loadMedia={loadMedia} />}\n {meta.length > 0 && <p className=\"text-xs text-muted-foreground\">{meta.join(\" · \")}</p>}\n {card.viewer?.completed && (\n <Badge variant=\"secondary\" className=\"justify-self-start\">\n {t(\"contents.completed\")}\n </Badge>\n )}\n {!card.access.allowed && card.access.free_after && <p className=\"text-xs text-muted-foreground\">{t(\"contents.freeFrom\", { date: formatDate(m, card.access.free_after, false) })}</p>}\n {entityStyle === \"chips\" && entityList}\n {showCounts && (card.counts.favorites !== undefined || card.counts.likes !== undefined) && (\n <p className=\"flex items-center gap-3 text-xs text-muted-foreground tabular-nums\" data-ckui=\"card-counts\">\n {card.counts.favorites !== undefined && (\n <span className=\"inline-flex items-center gap-1\" title={t(\"contents.favorites\")}>\n <HugeiconsIcon icon={FavouriteIcon} strokeWidth={2} className={cn(\"size-3.5\", card.viewer?.favorited && \"fill-current text-primary\")} aria-hidden />\n <span className=\"sr-only\">{t(\"contents.favorites\")}</span>\n {compact(card.counts.favorites, m.language)}\n </span>\n )}\n {card.counts.likes !== undefined && (\n <span className=\"inline-flex items-center gap-1\" title={t(\"contents.likes\")}>\n <HugeiconsIcon icon={ThumbsUpIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n <span className=\"sr-only\">{t(\"contents.likes\")}</span>\n {compact(card.counts.likes, m.language)}\n </span>\n )}\n </p>\n )}\n {children}\n </div>\n </article>\n );\n}\n\n/** The group that owns a content (a channel): its avatar and name, linking to its page. */\nexport function OwnerChip({ group, href, className, loadMedia = true }: { group: GroupLike; href?: (g: GroupLike) => string | undefined; className?: string; loadMedia?: boolean }) {\n return (\n <span className={cn(\"flex min-w-0 text-xs text-muted-foreground\", className)} data-ckui=\"card-owner\" data-id={group.id}>\n <GroupLink group={group} href={href} className=\"inline-flex min-w-0 items-center gap-1.5 hover:text-foreground\">\n {loadMedia ? <GroupAvatar group={group} className=\"size-5 shrink-0 text-[0.625rem]\" /> : <span className=\"size-5 shrink-0 rounded-full bg-muted\" aria-hidden />}\n <span className=\"truncate\">{group.name || group.slug}</span>\n </GroupLink>\n </span>\n );\n}\n\nfunction Chip({ url, name, variant = \"chips\" }: { url?: string; name: string; variant?: \"chips\" | \"text\" }) {\n const onClick = useLink(url);\n const cls = variant === \"text\" ? \"text-sm text-muted-foreground\" : \"inline-block rounded-md border border-border px-1.5 py-0.5 text-xs text-muted-foreground\";\n return url ? (\n <a href={url} onClick={onClick} className={cn(cls, \"hover:text-foreground\", variant === \"text\" ? \"hover:underline\" : \"hover:bg-muted\")}>\n {name}\n </a>\n ) : (\n <span className={cls}>{name}</span>\n );\n}\n\nexport interface PostCardProps extends Omit<ContentCardProps, \"shape\" | \"aspectRatio\" | \"titlePosition\" | \"chips\" | \"groupKinds\" | \"filterEntity\" | \"entityStyle\" | \"metadata\"> {\n /** Above the title: the post's channel or author; default its owner group's chip. */\n byline?: ReactNode;\n}\n\n/** A post in a feed: a wide cover, its title, when it was published and what unlocking it costs. */\nexport function PostCard({ card, href, onOpen, tiers, labels, byline, owner = true, ownerHref, loadMedia = true, showCounts = true, decorate, children, className }: PostCardProps) {\n const m = useMessages();\n const url = href ? href(card) : card.path;\n const open = useCallback(() => onOpen?.(card), [onOpen, card]);\n const onClick = useLink(url, open);\n const lock = lockLabel(m, card.access, tiers, labels);\n const title = card.title || card.code;\n return (\n <article className={cn(\"grid gap-3 rounded-lg border border-border p-3\", className)} data-ckui=\"post-card\" data-id={card.id} data-locked={lock ? \"\" : undefined} lang={card.language || undefined}>\n {byline ?? (owner && card.owner && <OwnerChip group={card.owner} href={ownerHref} loadMedia={loadMedia} className=\"text-sm\" />)}\n <a href={url} onClick={onClick} className=\"grid gap-2\">\n <span className=\"font-medium break-words hover:underline\" data-ckui=\"card-title\">\n {title}\n </span>\n {card.cover.length > 0 && (\n <span className=\"relative block aspect-video overflow-hidden rounded-md bg-muted\">\n {loadMedia && <RenditionImg outputs={card.cover} alt=\"\" fit=\"cover\" skeleton loading=\"lazy\" className={cn(\"size-full\", lock && \"blur-md\")} />}\n {loadMedia && <Decorations card={card} decorate={decorate} />}\n {lock && (\n <span className=\"absolute inset-0 grid place-items-center\">\n <span className=\"inline-flex items-center gap-1 rounded-md bg-background/85 px-2 py-1 text-xs font-medium shadow-sm\" data-ckui=\"card-lock\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n {lock}\n </span>\n </span>\n )}\n </span>\n )}\n </a>\n <p className=\"flex flex-wrap items-center gap-3 text-xs text-muted-foreground\">\n {card.published_at && <RelativeTime at={card.published_at} />}\n {!card.cover.length && lock && <span data-ckui=\"card-lock\">{lock}</span>}\n {showCounts && card.counts.favorites !== undefined && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\" data-ckui=\"card-counts\">\n <HugeiconsIcon icon={FavouriteIcon} strokeWidth={2} className={cn(\"size-3.5\", card.viewer?.favorited && \"fill-current text-primary\")} aria-hidden />\n <span className=\"sr-only\">{m.t(\"contents.favorites\")}</span>\n {compact(card.counts.favorites, m.language)}\n </span>\n )}\n </p>\n {children}\n </article>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { DailyReleasesQuery } from '../../client/discovery.js';
2
+ import { CardListProps } from './grid.js';
3
+ export interface DailyReleasesProps extends Omit<CardListProps, "infinite" | "pageSize" | "renderCard" | "loadMedia"> {
4
+ /** One complete calendar window. Defaults to 10 days in the browser's time zone. */
5
+ query?: DailyReleasesQuery;
6
+ /** Cards initially revealed across the whole window, allocated newest first. Default 30. */
7
+ initialVisible?: number;
8
+ /** Additional cached cards revealed by a day's Show more button. Default 30. */
9
+ revealStep?: number;
10
+ }
11
+ /**
12
+ * One metadata request for a calendar window, with no requests when a day is
13
+ * opened or expanded. Only revealed, visible cards mount images and previews.
14
+ */
15
+ export declare function DailyReleases({ query, initialVisible, revealStep, ...p }: DailyReleasesProps): import('react').JSX.Element;