@openrails/contentkit-ui 0.104.0-rc.1 → 0.104.0-rc.3

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 (217) hide show
  1. package/README.md +6 -6
  2. package/dist/client/beacons.js +4 -1
  3. package/dist/client/beacons.js.map +1 -1
  4. package/dist/client/contents.d.ts +9 -1
  5. package/dist/client/contents.js +73 -55
  6. package/dist/client/contents.js.map +1 -1
  7. package/dist/client/discovery.d.ts +6 -2
  8. package/dist/client/discovery.js +1 -1
  9. package/dist/client/discovery.js.map +1 -1
  10. package/dist/client/generated/error-codes.d.ts +1 -1
  11. package/dist/client/generated/error-codes.js +4 -0
  12. package/dist/client/generated/error-codes.js.map +1 -1
  13. package/dist/client/generated/routes.d.ts +43 -30
  14. package/dist/client/generated/routes.js +70 -63
  15. package/dist/client/generated/routes.js.map +1 -1
  16. package/dist/client/generated/wire.d.ts +36 -74
  17. package/dist/client/generated/wire.js.map +1 -1
  18. package/dist/client/index.d.ts +1 -1
  19. package/dist/client/media/windows.d.ts +2 -0
  20. package/dist/client/media/windows.js +4 -1
  21. package/dist/client/media/windows.js.map +1 -1
  22. package/dist/client/stats.d.ts +4 -20
  23. package/dist/client/stats.js +0 -11
  24. package/dist/client/stats.js.map +1 -1
  25. package/dist/client/versions.d.ts +8 -6
  26. package/dist/client/versions.js +15 -10
  27. package/dist/client/versions.js.map +1 -1
  28. package/dist/components/admin/bulk-drafts.d.ts +17 -0
  29. package/dist/components/admin/bulk-drafts.js +41 -0
  30. package/dist/components/admin/bulk-drafts.js.map +1 -0
  31. package/dist/components/admin/content-admin.d.ts +1 -1
  32. package/dist/components/admin/content-admin.js.map +1 -1
  33. package/dist/components/admin/content-manager.d.ts +11 -3
  34. package/dist/components/admin/content-manager.js +739 -380
  35. package/dist/components/admin/content-manager.js.map +1 -1
  36. package/dist/components/admin/group-manager.d.ts +8 -3
  37. package/dist/components/admin/group-manager.js +218 -146
  38. package/dist/components/admin/group-manager.js.map +1 -1
  39. package/dist/components/admin/page-notices.d.ts +16 -0
  40. package/dist/components/admin/page-notices.js +113 -0
  41. package/dist/components/admin/page-notices.js.map +1 -0
  42. package/dist/components/admin/page-picker.d.ts +38 -0
  43. package/dist/components/admin/page-picker.js +159 -0
  44. package/dist/components/admin/page-picker.js.map +1 -0
  45. package/dist/components/admin/reader-content-editor.js +537 -500
  46. package/dist/components/admin/reader-content-editor.js.map +1 -1
  47. package/dist/components/admin/reader-manifest.js +181 -179
  48. package/dist/components/admin/reader-manifest.js.map +1 -1
  49. package/dist/components/admin/shell.d.ts +10 -2
  50. package/dist/components/admin/shell.js +114 -42
  51. package/dist/components/admin/shell.js.map +1 -1
  52. package/dist/components/admin/stats-dashboard.d.ts +4 -9
  53. package/dist/components/admin/stats-dashboard.js +300 -202
  54. package/dist/components/admin/stats-dashboard.js.map +1 -1
  55. package/dist/components/admin/upload-queue.d.ts +14 -0
  56. package/dist/components/admin/upload-queue.js +115 -98
  57. package/dist/components/admin/upload-queue.js.map +1 -1
  58. package/dist/components/content/card.d.ts +31 -3
  59. package/dist/components/content/card.js +324 -158
  60. package/dist/components/content/card.js.map +1 -1
  61. package/dist/components/content/daily-releases.js +14 -12
  62. package/dist/components/content/daily-releases.js.map +1 -1
  63. package/dist/components/content/editor-save.js +27 -25
  64. package/dist/components/content/editor-save.js.map +1 -1
  65. package/dist/components/content/editors.js +274 -253
  66. package/dist/components/content/editors.js.map +1 -1
  67. package/dist/components/content/favorites.d.ts +1 -1
  68. package/dist/components/content/favorites.js +36 -22
  69. package/dist/components/content/favorites.js.map +1 -1
  70. package/dist/components/content/grid.d.ts +11 -4
  71. package/dist/components/content/grid.js +52 -43
  72. package/dist/components/content/grid.js.map +1 -1
  73. package/dist/components/content/history.js +83 -63
  74. package/dist/components/content/history.js.map +1 -1
  75. package/dist/components/content/index.d.ts +5 -3
  76. package/dist/components/content/listing.d.ts +14 -3
  77. package/dist/components/content/listing.js +65 -56
  78. package/dist/components/content/listing.js.map +1 -1
  79. package/dist/components/content/offer-fields.js +6 -6
  80. package/dist/components/content/paged-grid.d.ts +10 -0
  81. package/dist/components/content/paged-grid.js +116 -0
  82. package/dist/components/content/paged-grid.js.map +1 -0
  83. package/dist/components/content/pagination.d.ts +19 -0
  84. package/dist/components/content/pagination.js +97 -0
  85. package/dist/components/content/pagination.js.map +1 -0
  86. package/dist/components/content/paywall.d.ts +9 -0
  87. package/dist/components/content/paywall.js +27 -3
  88. package/dist/components/content/paywall.js.map +1 -1
  89. package/dist/components/content/post-list.d.ts +37 -0
  90. package/dist/components/content/post-list.js +203 -0
  91. package/dist/components/content/post-list.js.map +1 -0
  92. package/dist/components/content/reader-settings.d.ts +2 -0
  93. package/dist/components/content/reader-settings.js +1 -1
  94. package/dist/components/content/reader-settings.js.map +1 -1
  95. package/dist/components/groups/banner.d.ts +6 -2
  96. package/dist/components/groups/banner.js +79 -61
  97. package/dist/components/groups/banner.js.map +1 -1
  98. package/dist/components/groups/directory.d.ts +12 -2
  99. package/dist/components/groups/directory.js +168 -105
  100. package/dist/components/groups/directory.js.map +1 -1
  101. package/dist/components/groups/editor.js +10 -10
  102. package/dist/components/groups/editor.js.map +1 -1
  103. package/dist/components/groups/entity-chips.d.ts +61 -0
  104. package/dist/components/groups/entity-chips.js +382 -0
  105. package/dist/components/groups/entity-chips.js.map +1 -0
  106. package/dist/components/groups/group-members.d.ts +29 -0
  107. package/dist/components/groups/group-members.js +474 -0
  108. package/dist/components/groups/group-members.js.map +1 -0
  109. package/dist/components/groups/header.js +53 -45
  110. package/dist/components/groups/header.js.map +1 -1
  111. package/dist/components/groups/index.d.ts +3 -1
  112. package/dist/components/groups/memberships.d.ts +3 -36
  113. package/dist/components/groups/memberships.js +105 -480
  114. package/dist/components/groups/memberships.js.map +1 -1
  115. package/dist/components/groups/scoped.d.ts +23 -0
  116. package/dist/components/groups/scoped.js +92 -0
  117. package/dist/components/groups/scoped.js.map +1 -0
  118. package/dist/components/media-folder-editor.d.ts +5 -0
  119. package/dist/components/media-folder-editor.js +418 -230
  120. package/dist/components/media-folder-editor.js.map +1 -1
  121. package/dist/components/social/poll.js +57 -48
  122. package/dist/components/social/poll.js.map +1 -1
  123. package/dist/components/sortable-list.d.ts +5 -1
  124. package/dist/components/sortable-list.js +97 -81
  125. package/dist/components/sortable-list.js.map +1 -1
  126. package/dist/index.d.ts +1 -1
  127. package/dist/index.js +41 -37
  128. package/dist/locales/admin/de.js +78 -45
  129. package/dist/locales/admin/de.js.map +1 -1
  130. package/dist/locales/admin/en.d.ts +74 -41
  131. package/dist/locales/admin/en.js +78 -45
  132. package/dist/locales/admin/en.js.map +1 -1
  133. package/dist/locales/admin/es.js +78 -45
  134. package/dist/locales/admin/es.js.map +1 -1
  135. package/dist/locales/admin/ja.js +78 -45
  136. package/dist/locales/admin/ja.js.map +1 -1
  137. package/dist/locales/admin/ko.js +78 -45
  138. package/dist/locales/admin/ko.js.map +1 -1
  139. package/dist/locales/admin/zh.js +78 -45
  140. package/dist/locales/admin/zh.js.map +1 -1
  141. package/dist/locales/de.js +2 -0
  142. package/dist/locales/de.js.map +1 -1
  143. package/dist/locales/discovery/de.js +25 -1
  144. package/dist/locales/discovery/de.js.map +1 -1
  145. package/dist/locales/discovery/en.d.ts +24 -0
  146. package/dist/locales/discovery/en.js +25 -1
  147. package/dist/locales/discovery/en.js.map +1 -1
  148. package/dist/locales/discovery/es.js +25 -1
  149. package/dist/locales/discovery/es.js.map +1 -1
  150. package/dist/locales/discovery/ja.js +25 -1
  151. package/dist/locales/discovery/ja.js.map +1 -1
  152. package/dist/locales/discovery/ko.js +25 -1
  153. package/dist/locales/discovery/ko.js.map +1 -1
  154. package/dist/locales/discovery/zh.js +25 -1
  155. package/dist/locales/discovery/zh.js.map +1 -1
  156. package/dist/locales/en.d.ts +120 -43
  157. package/dist/locales/en.js +2 -0
  158. package/dist/locales/en.js.map +1 -1
  159. package/dist/locales/es.js +2 -0
  160. package/dist/locales/es.js.map +1 -1
  161. package/dist/locales/groups/de.js +22 -4
  162. package/dist/locales/groups/de.js.map +1 -1
  163. package/dist/locales/groups/en.d.ts +20 -2
  164. package/dist/locales/groups/en.js +22 -4
  165. package/dist/locales/groups/en.js.map +1 -1
  166. package/dist/locales/groups/es.js +22 -4
  167. package/dist/locales/groups/es.js.map +1 -1
  168. package/dist/locales/groups/ja.js +22 -4
  169. package/dist/locales/groups/ja.js.map +1 -1
  170. package/dist/locales/groups/ko.js +22 -4
  171. package/dist/locales/groups/ko.js.map +1 -1
  172. package/dist/locales/groups/zh.js +22 -4
  173. package/dist/locales/groups/zh.js.map +1 -1
  174. package/dist/locales/ja.js +2 -0
  175. package/dist/locales/ja.js.map +1 -1
  176. package/dist/locales/ko.js +2 -0
  177. package/dist/locales/ko.js.map +1 -1
  178. package/dist/locales/zh.js +2 -0
  179. package/dist/locales/zh.js.map +1 -1
  180. package/dist/react/contents.js +1 -1
  181. package/dist/react/contents.js.map +1 -1
  182. package/dist/react/discovery.d.ts +16 -0
  183. package/dist/react/discovery.js +69 -37
  184. package/dist/react/discovery.js.map +1 -1
  185. package/dist/react/groups.d.ts +19 -3
  186. package/dist/react/groups.js +49 -22
  187. package/dist/react/groups.js.map +1 -1
  188. package/dist/react/index.d.ts +6 -4
  189. package/dist/react/page-notices.d.ts +13 -0
  190. package/dist/react/page-notices.js +38 -0
  191. package/dist/react/page-notices.js.map +1 -0
  192. package/dist/react/post-reads.d.ts +23 -0
  193. package/dist/react/post-reads.js +90 -0
  194. package/dist/react/post-reads.js.map +1 -0
  195. package/dist/react/remembered-sort.d.ts +5 -0
  196. package/dist/react/remembered-sort.js +29 -0
  197. package/dist/react/remembered-sort.js.map +1 -0
  198. package/dist/react/resources.js +2 -2
  199. package/dist/react/resources.js.map +1 -1
  200. package/dist/react/stats.d.ts +3 -7
  201. package/dist/react/stats.js +2 -6
  202. package/dist/react/stats.js.map +1 -1
  203. package/dist/react/use-engagement.d.ts +2 -0
  204. package/dist/react/use-engagement.js +40 -34
  205. package/dist/react/use-engagement.js.map +1 -1
  206. package/dist/react.js +29 -27
  207. package/dist/styles.css +1 -1
  208. package/dist/urls/index.d.ts +1 -1
  209. package/dist/urls.js +2 -2
  210. package/dist/urls.js.map +1 -1
  211. package/package.json +1 -1
  212. package/dist/components/admin/page-review.d.ts +0 -13
  213. package/dist/components/admin/page-review.js +0 -225
  214. package/dist/components/admin/page-review.js.map +0 -1
  215. package/dist/react/page-review.d.ts +0 -14
  216. package/dist/react/page-review.js +0 -37
  217. package/dist/react/page-review.js.map +0 -1
@@ -8,12 +8,13 @@ import { useNearViewport as o } from "../../react/viewport.js";
8
8
  import { Skeleton as s } from "../../ui/skeleton.js";
9
9
  import { contentError as c } from "../social/parts.js";
10
10
  import { useContents as l, useExposure as u, useHistory as d, useRecommendations as f, useSearch as p, useSimilar as m } from "../../react/discovery.js";
11
- import { ContentCard as h } from "./card.js";
12
- import { ContentFilterDialog as g } from "./filter-dialog.js";
13
- import { useReleaseReveal as _ } from "./release-reveal.js";
14
- import { ContentSummaryTile as v } from "./summary-tile.js";
15
- import { ContentHistory as y } from "./history.js";
16
- import { useCallback as b, useContext as x, useEffect as S, useLayoutEffect as C, useMemo as w, useRef as T, useState as E } from "react";
11
+ import { ContentFilterDialog as h } from "./filter-dialog.js";
12
+ import { useReleaseReveal as g } from "./release-reveal.js";
13
+ import { ContentSummaryTile as _ } from "./summary-tile.js";
14
+ import { ContentCard as v } from "./card.js";
15
+ import { PagedContentGrid as y, PagedSearchResults as b } from "./paged-grid.js";
16
+ import { ContentHistory as x } from "./history.js";
17
+ import { useCallback as S, useContext as C, useEffect as w, useLayoutEffect as ee, useMemo as T, useRef as te, useState as E } from "react";
17
18
  import { jsx as D, jsxs as O } from "react/jsx-runtime";
18
19
  import { cn as k } from "cn";
19
20
  //#region src/components/content/grid.tsx
@@ -30,7 +31,7 @@ function j(e) {
30
31
  };
31
32
  }
32
33
  function M(e) {
33
- let { client: t, viewer: i, language: a } = r(e.client), o = x(n), [s, c] = E({
34
+ let { client: t, viewer: i, language: a } = r(e.client), o = C(n), [s, c] = E({
34
35
  client: t,
35
36
  viewer: i,
36
37
  language: a,
@@ -48,23 +49,23 @@ function M(e) {
48
49
  }, c(l)), /* @__PURE__ */ D(N, { ...e }, l.key);
49
50
  }
50
51
  function N({ list: n, render: r, filterKinds: l, filterLanguage: d, horizontal: f, ...p }) {
51
- let m = e(), { t: y } = m, { kinds: x } = i({ client: p.client }), [A, M] = E(!1), [N, P] = E(!1), F = T(null), [I, L] = E(null), [R, te] = E(0), [z, ne] = E(() => globalThis.matchMedia?.("(max-width: 767px)").matches ?? !1);
52
- C(() => {
53
- if (!I) return;
52
+ let m = e(), { t: y } = m, { kinds: b } = i({ client: p.client }), [x, C] = E(!1), [A, M] = E(!1), N = te(null), [P, F] = E(null), [I, L] = E(0), [R, z] = E(() => globalThis.matchMedia?.("(max-width: 767px)").matches ?? !1);
53
+ ee(() => {
54
+ if (!P) return;
54
55
  let e = () => {
55
- let e = getComputedStyle(I).gridTemplateColumns.trim();
56
- te(e && e !== "none" ? e.split(/\s+/).length : 0), ne(globalThis.matchMedia?.("(max-width: 767px)").matches ?? !1);
56
+ let e = getComputedStyle(P).gridTemplateColumns.trim();
57
+ L(e && e !== "none" ? e.split(/\s+/).length : 0), z(globalThis.matchMedia?.("(max-width: 767px)").matches ?? !1);
57
58
  };
58
59
  e();
59
60
  let t = typeof ResizeObserver == "function" ? new ResizeObserver(e) : void 0;
60
- return t?.observe(I), addEventListener("resize", e), () => {
61
+ return t?.observe(P), addEventListener("resize", e), () => {
61
62
  t?.disconnect(), removeEventListener("resize", e);
62
63
  };
63
- }, [I]);
64
- let B = n.summary?.locked_count ?? 0, V = p.showFilteredSummary === !1 ? 0 : n.summary?.filtered_count ?? 0, H = p.locked === "collapse" && !A && B > 0, U = H && B > 0 && R >= Math.max(0, p.lockedSummaryMinColumns ?? 0), W = w(() => {
64
+ }, [P]);
65
+ let B = n.summary?.locked_count ?? 0, V = p.showFilteredSummary === !1 ? 0 : n.summary?.filtered_count ?? 0, H = p.locked === "collapse" && !x && B > 0, U = H && B > 0 && I >= Math.max(0, p.lockedSummaryMinColumns ?? 0), W = T(() => {
65
66
  let e = [], t = !1;
66
67
  for (let [r, i] of n.items.entries()) {
67
- if (ee(i) && (H || p.locked === "hide")) {
68
+ if (ne(i) && (H || p.locked === "hide")) {
68
69
  U && !t && (e.push({
69
70
  key: "premium-summary",
70
71
  premium: !0,
@@ -82,17 +83,17 @@ function N({ list: n, render: r, filterKinds: l, filterLanguage: d, horizontal:
82
83
  key: "premium-summary",
83
84
  premium: !0
84
85
  });
85
- let r = z && p.mobileCardLimit !== void 0 ? p.mobileCardLimit : p.cardLimit;
86
+ let r = R && p.mobileCardLimit !== void 0 ? p.mobileCardLimit : p.cardLimit;
86
87
  return r !== void 0 && Number.isFinite(r) ? e.slice(0, Math.max(0, Math.floor(r))) : e;
87
88
  }, [
88
89
  n.items,
89
90
  H,
90
91
  p.locked,
91
92
  U,
92
- z,
93
+ R,
93
94
  p.mobileCardLimit,
94
95
  p.cardLimit
95
- ]), re = w(() => W.flatMap((e) => "premium" in e ? [] : [{
96
+ ]), re = T(() => W.flatMap((e) => "premium" in e ? [] : [{
96
97
  ...e.card,
97
98
  position: e.position
98
99
  }]), [W]), G = u(re, {
@@ -103,7 +104,7 @@ function N({ list: n, render: r, filterKinds: l, filterLanguage: d, horizontal:
103
104
  margin: "600px 0px",
104
105
  leaveMargin: "1200px 0px"
105
106
  }), { hasMore: q, loadingMore: J, loadMore: Y, error: X } = n;
106
- S(() => {
107
+ w(() => {
107
108
  p.infinite && K && q && !J && !X && Y();
108
109
  }, [
109
110
  p.infinite,
@@ -113,8 +114,8 @@ function N({ list: n, render: r, filterKinds: l, filterLanguage: d, horizontal:
113
114
  Y,
114
115
  X
115
116
  ]);
116
- let { grid: Z, prepare: ae } = _(W.map((e) => e.key).join("\0")), oe = b((e) => {
117
- Z.current = e, L(e);
117
+ let { grid: Z, prepare: ae } = g(W.map((e) => e.key).join("\0")), oe = S((e) => {
118
+ Z.current = e, F(e);
118
119
  }, [Z]), Q = f ? { className: "relative flex snap-x gap-4 overflow-x-auto pb-2 [&>li]:w-36 [&>li]:shrink-0 [&>li]:snap-start sm:[&>li]:w-44" } : j(p.grid), $ = n.summary?.truncated ? "≥ " : "", se = $ + (p.summaryLabels?.premium?.(B) ?? m.plural("contents.lockedShow", B)), ce = $ + (p.summaryLabels?.filtered?.(V) ?? m.plural("releases.filteredMore", V)), le = W.length > 0 || V > 0 || H && B > 0;
119
120
  return /* @__PURE__ */ O(t, {
120
121
  appearance: p.appearance,
@@ -147,7 +148,7 @@ function N({ list: n, render: r, filterKinds: l, filterLanguage: d, horizontal:
147
148
  tabIndex: -1,
148
149
  className: Q.className,
149
150
  style: Q.style,
150
- children: [W.map((e) => "premium" in e ? /* @__PURE__ */ D(v, {
151
+ children: [W.map((e) => "premium" in e ? /* @__PURE__ */ D(_, {
151
152
  tileKey: e.key,
152
153
  kind: "premium",
153
154
  contentKind: l?.[0] ?? e.card?.kind,
@@ -156,12 +157,12 @@ function N({ list: n, render: r, filterKinds: l, filterLanguage: d, horizontal:
156
157
  cardProps: p,
157
158
  label: se,
158
159
  onClick: (e) => {
159
- ae(e.detail === 0), M(!0);
160
+ ae(e.detail === 0), C(!0);
160
161
  }
161
162
  }, e.key) : /* @__PURE__ */ D("li", {
162
163
  "data-release-key": e.key,
163
164
  ref: G.track(e.card.id, e.position),
164
- children: p.renderCard ? p.renderCard(e.card, e.position) : /* @__PURE__ */ D(h, {
165
+ children: p.renderCard ? p.renderCard(e.card, e.position) : /* @__PURE__ */ D(v, {
165
166
  card: e.card,
166
167
  client: p.client,
167
168
  href: p.href,
@@ -171,19 +172,21 @@ function N({ list: n, render: r, filterKinds: l, filterLanguage: d, horizontal:
171
172
  loadMedia: p.loadMedia,
172
173
  groupKinds: p.groupKinds,
173
174
  chips: p.chips,
175
+ chipOverflow: p.chipOverflow,
176
+ chipFit: p.chipFit,
174
177
  filterEntity: p.filterEntity,
175
178
  entityStyle: p.entityStyle,
176
179
  entityLayout: p.entityLayout,
177
180
  metadata: p.metadata,
178
181
  showCounts: p.showCounts,
179
- tiers: p.tiers ?? x?.tiers,
182
+ tiers: p.tiers ?? b?.tiers,
180
183
  labels: p.labels,
181
184
  owner: p.owner,
182
185
  ownerHref: p.ownerHref,
183
186
  decorate: p.decorate,
184
187
  onOpen: () => G.click(e.card.id, e.position)
185
188
  })
186
- }, e.key)), V > 0 && /* @__PURE__ */ D(v, {
189
+ }, e.key)), V > 0 && /* @__PURE__ */ D(_, {
187
190
  tileKey: "filtered-summary",
188
191
  kind: "filtered",
189
192
  contentKind: l?.[0] ?? n.items[0]?.kind,
@@ -191,7 +194,7 @@ function N({ list: n, render: r, filterKinds: l, filterLanguage: d, horizontal:
191
194
  cardProps: p,
192
195
  label: ce,
193
196
  onClick: (e) => {
194
- F.current = e.currentTarget, P(!0);
197
+ N.current = e.currentTarget, M(!0);
195
198
  }
196
199
  })]
197
200
  }) : /* @__PURE__ */ D("div", {
@@ -214,10 +217,10 @@ function N({ list: n, render: r, filterKinds: l, filterLanguage: d, horizontal:
214
217
  children: n.loadingMore ? y("contents.loading") : n.error ? y("common.retry") : y("contents.loadMore")
215
218
  })]
216
219
  }),
217
- /* @__PURE__ */ D(g, {
218
- open: N,
219
- onOpenChange: P,
220
- returnFocus: () => F.current?.isConnected ? F.current : I,
220
+ /* @__PURE__ */ D(h, {
221
+ open: A,
222
+ onOpenChange: M,
223
+ returnFocus: () => N.current?.isConnected ? N.current : P,
221
224
  client: p.client,
222
225
  kinds: l,
223
226
  language: d,
@@ -236,9 +239,12 @@ var F = [
236
239
  "group",
237
240
  "ppv",
238
241
  "group_ppv"
239
- ], ee = (e) => !e.access.allowed && !e.access.unknown && F.includes(e.access.level);
240
- function I({ query: e, locked: t = "show", ...n }) {
241
- let r = w(() => ({
242
+ ], ne = (e) => !e.access.allowed && !e.access.unknown && F.includes(e.access.level);
243
+ function I(e) {
244
+ return e.pagination === "pages" ? /* @__PURE__ */ D(y, { ...e }) : /* @__PURE__ */ D(L, { ...e });
245
+ }
246
+ function L({ query: e, locked: t = "show", ...n }) {
247
+ let r = T(() => ({
242
248
  ...e,
243
249
  summary: e.summary ?? !0,
244
250
  limit: e.limit ?? n.pageSize,
@@ -257,8 +263,11 @@ function I({ query: e, locked: t = "show", ...n }) {
257
263
  filterLanguage: e.display_language ?? e.language
258
264
  });
259
265
  }
260
- function L({ query: t, ...n }) {
261
- let { t: r } = e(), i = w(() => ({
266
+ function R(e) {
267
+ return e.pagination === "pages" ? /* @__PURE__ */ D(b, { ...e }) : /* @__PURE__ */ D(z, { ...e });
268
+ }
269
+ function z({ query: t, ...n }) {
270
+ let { t: r } = e(), i = T(() => ({
262
271
  ...t,
263
272
  mode: P(n.locked, t.mode),
264
273
  summary: t.summary ?? !0,
@@ -278,8 +287,8 @@ function L({ query: t, ...n }) {
278
287
  filterLanguage: t.display_language ?? t.language
279
288
  }) : null;
280
289
  }
281
- function R({ title: e, similar: n, recommendations: r, history: i, group: a, list: o, query: s = {}, more: c, ...u }) {
282
- let p = s.limit ?? u.pageSize ?? 12, h = w(() => ({
290
+ function B({ title: e, similar: n, recommendations: r, history: i, group: a, list: o, query: s = {}, more: c, ...u }) {
291
+ let p = s.limit ?? u.pageSize ?? 12, h = T(() => ({
283
292
  ...s,
284
293
  mode: P(u.locked, s.mode),
285
294
  summary: s.summary ?? !0,
@@ -288,7 +297,7 @@ function R({ title: e, similar: n, recommendations: r, history: i, group: a, lis
288
297
  s,
289
298
  p,
290
299
  u.locked
291
- ]), g = w(() => o ? {
300
+ ]), g = T(() => o ? {
292
301
  ...o,
293
302
  mode: P(u.locked, o.mode),
294
303
  summary: o.summary ?? !0,
@@ -326,7 +335,7 @@ function R({ title: e, similar: n, recommendations: r, history: i, group: a, lis
326
335
  loadingMore: !1,
327
336
  hasMore: !1,
328
337
  loadMore: async () => {}
329
- }, T = o?.kind ?? i?.kind ?? h.kind;
338
+ }, w = o?.kind ?? i?.kind ?? h.kind;
330
339
  return /* @__PURE__ */ O(t, {
331
340
  appearance: u.appearance,
332
341
  className: k("grid gap-3 text-sm", u.className),
@@ -352,12 +361,12 @@ function R({ title: e, similar: n, recommendations: r, history: i, group: a, lis
352
361
  g,
353
362
  h
354
363
  ]),
355
- filterKinds: T ? [T] : void 0,
364
+ filterKinds: w ? [w] : void 0,
356
365
  filterLanguage: o?.display_language ?? i?.display_language ?? h.display_language ?? o?.language ?? i?.language ?? h.language
357
366
  })]
358
367
  });
359
368
  }
360
369
  //#endregion
361
- export { M as CardGrid, I as ContentGrid, y as ContentHistory, R as ContentRail, L as SearchResults, j as cardGridLayout, P as cardListMode };
370
+ export { M as CardGrid, I as ContentGrid, x as ContentHistory, B as ContentRail, R as SearchResults, j as cardGridLayout, P as cardListMode };
362
371
 
363
372
  //# 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 { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, type CSSProperties, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { AccessMode, ContentQuery, HistoryQuery, RailQuery, SearchQuery } from \"../../client/discovery.js\";\nimport type { Card, ListingSummary } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useContents, useExposure, useHistory, useRecommendations, useSearch, useSimilar, type UseRail } from \"../../react/discovery.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport type { UseList } from \"../../react/use-resource.js\";\nimport { ContentKitContext } from \"../../react/context.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 { ContentFilterDialog } from \"./filter-dialog.js\";\nimport { useReleaseReveal } from \"./release-reveal.js\";\nimport { ContentSummaryTile } from \"./summary-tile.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 /** Collapse paid/locked cards into a counted tile, or hide them; default show. */\n locked?: LockedCards;\n /** Optional-filter summary tiles are on by default; hosts may suppress them in previews. */\n showFilteredSummary?: boolean;\n /** Show the collapsed locked tile only at this measured grid width. Default 0. */\n lockedSummaryMinColumns?: number;\n /** Limit rendered preview tiles without changing the matching selection or summary counts. */\n cardLimit?: number;\n /** Limit cards in mobile previews without mounting their images. */\n mobileCardLimit?: number;\n /** Host-specific nouns for summary controls. */\n summaryLabels?: { premium?: (count: number) => string; filtered?: (count: number) => string };\n /** Opt into a fluid grid, optionally with a 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: CSS pixels or a CSS length such as clamp(...). Cards fill the available space. */\n minCardWidth: number | string;\n /** Maximum desktop columns. Omit to use all available width without a ceiling. */\n maxColumns?: 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 =\n typeof layout.minCardWidth === \"string\" ? layout.minCardWidth.trim() || \"160px\" : `${Number.isFinite(layout.minCardWidth) ? Math.max(1, layout.minCardWidth) : 160}px`;\n const columns = layout.mobileColumns !== undefined && Number.isFinite(layout.mobileColumns) ? Math.max(1, Math.floor(layout.mobileColumns)) : 2;\n const maximum = layout.maxColumns !== undefined && Number.isFinite(layout.maxColumns) ? Math.max(1, Math.floor(layout.maxColumns)) : undefined;\n const trackWidth = maximum === undefined ? width : `max(${width}, calc((100% - ${maximum - 1}rem) / ${maximum}))`;\n return {\n className:\n \"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\": trackWidth, \"--ckui-card-mobile-columns\": columns } as CSSProperties,\n };\n}\n\n/** A list carries exact selection-wide counts when the API was asked for them. */\ntype SummaryList = UseList<Card> & { summary?: ListingSummary };\ntype GridProps = CardListProps & { list: SummaryList; render: string; filterKinds?: readonly string[]; filterLanguage?: string; horizontal?: boolean };\n\n/** A list's cards and summary controls share one responsive, animated grid. */\nexport function CardGrid(p: GridProps) {\n const { client, viewer, language } = useContentScope(p.client);\n const context = useContext(ContentKitContext);\n const [boundary, setBoundary] = useState({ client, viewer, language, access: context?.accessRevision, render: p.render, key: 0 });\n let session = boundary;\n if (boundary.client !== client || boundary.viewer !== viewer || boundary.language !== language || boundary.access !== context?.accessRevision || boundary.render !== p.render) {\n session = { client, viewer, language, access: context?.accessRevision, render: p.render, key: boundary.key + 1 };\n setBoundary(session);\n }\n return <CardGridBody key={session.key} {...p} />;\n}\n\nfunction CardGridBody({ list, render, filterKinds, filterLanguage, horizontal, ...p }: GridProps) {\n const m = useMessages();\n const { t } = m;\n const { kinds } = useKinds({ client: p.client });\n const [expanded, setExpanded] = useState(false);\n const [filtersOpen, setFiltersOpen] = useState(false);\n const filterTrigger = useRef<HTMLElement | null>(null);\n const [element, setElement] = useState<HTMLOListElement | null>(null);\n const [columns, setColumns] = useState(0);\n const [mobile, setMobile] = useState(() => globalThis.matchMedia?.(\"(max-width: 767px)\").matches ?? false);\n useLayoutEffect(() => {\n if (!element) return;\n const measure = () => {\n const tracks = getComputedStyle(element).gridTemplateColumns.trim();\n setColumns(tracks && tracks !== \"none\" ? tracks.split(/\\s+/).length : 0);\n setMobile(globalThis.matchMedia?.(\"(max-width: 767px)\").matches ?? false);\n };\n measure();\n const observer = typeof ResizeObserver === \"function\" ? new ResizeObserver(measure) : undefined;\n observer?.observe(element);\n addEventListener(\"resize\", measure);\n return () => {\n observer?.disconnect();\n removeEventListener(\"resize\", measure);\n };\n }, [element]);\n const lockedCount = list.summary?.locked_count ?? 0;\n const filteredCount = p.showFilteredSummary === false ? 0 : (list.summary?.filtered_count ?? 0);\n const collapse = p.locked === \"collapse\" && !expanded && lockedCount > 0;\n const lockedTile = collapse && lockedCount > 0 && columns >= Math.max(0, p.lockedSummaryMinColumns ?? 0);\n const entries = useMemo(() => {\n const result: ({ key: string; card: Card; position: number } | { key: string; premium: true; card?: Card })[] = [];\n let inserted = false;\n for (const [i, card] of list.items.entries()) {\n if (isLockedCard(card) && (collapse || p.locked === \"hide\")) {\n if (lockedTile && !inserted) {\n result.push({ key: \"premium-summary\", premium: true, card });\n inserted = true;\n }\n continue;\n }\n result.push({ key: card.id, card, position: i + 1 });\n }\n if (lockedTile && !inserted) result.push({ key: \"premium-summary\", premium: true });\n const limit = mobile && p.mobileCardLimit !== undefined ? p.mobileCardLimit : p.cardLimit;\n return limit !== undefined && Number.isFinite(limit) ? result.slice(0, Math.max(0, Math.floor(limit))) : result;\n }, [list.items, collapse, p.locked, lockedTile, mobile, p.mobileCardLimit, p.cardLimit]);\n const shown = useMemo(() => entries.flatMap((entry) => (\"premium\" in entry ? [] : [{ ...entry.card, position: entry.position }])), [entries]);\n const exposure = useExposure(shown, { surface: p.surface ?? \"list\", render, client: p.client });\n const [sentinel, near] = useNearViewport({ margin: \"600px 0px\", leaveMargin: \"1200px 0px\" });\n const { hasMore, loadingMore, loadMore, error } = list;\n useEffect(() => {\n if (p.infinite && near && hasMore && !loadingMore && !error) void loadMore();\n }, [p.infinite, near, hasMore, loadingMore, loadMore, error]);\n const { grid: animationGrid, prepare } = useReleaseReveal(entries.map((entry) => entry.key).join(\"\\0\"));\n const grid = useCallback(\n (node: HTMLOListElement | null) => {\n animationGrid.current = node;\n setElement(node);\n },\n [animationGrid],\n );\n const layout: ReturnType<typeof cardGridLayout> = horizontal\n ? { className: \"relative flex snap-x gap-4 overflow-x-auto pb-2 [&>li]:w-36 [&>li]:shrink-0 [&>li]:snap-start sm:[&>li]:w-44\" }\n : cardGridLayout(p.grid);\n const lowerBound = list.summary?.truncated ? \"≥ \" : \"\";\n const premiumLabel = lowerBound + (p.summaryLabels?.premium?.(lockedCount) ?? m.plural(\"contents.lockedShow\", lockedCount));\n const filteredLabel = lowerBound + (p.summaryLabels?.filtered?.(filteredCount) ?? m.plural(\"releases.filteredMore\", filteredCount));\n const showGrid = entries.length > 0 || filteredCount > 0 || (collapse && lockedCount > 0);\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 <ol aria-hidden className={layout.className} style={layout.style}>\n {Array.from({ length: 8 }, (_, i) => (\n <li key={i}>\n <Skeleton\n className={p.aspectRatio === undefined ? (p.shape === \"landscape\" ? \"aspect-video w-full\" : \"aspect-[3/4] w-full\") : \"w-full\"}\n style={p.aspectRatio === undefined ? undefined : { aspectRatio: p.aspectRatio }}\n />\n </li>\n ))}\n </ol>\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 ) : !showGrid ? (\n <div className=\"text-muted-foreground\" role=\"status\">\n {p.empty ?? t(\"contents.empty\")}\n </div>\n ) : (\n <ol ref={grid} tabIndex={-1} className={layout.className} style={layout.style}>\n {entries.map((entry) =>\n \"premium\" in entry ? (\n <ContentSummaryTile\n key={entry.key}\n tileKey={entry.key}\n kind=\"premium\"\n contentKind={filterKinds?.[0] ?? entry.card?.kind}\n preview={entry.card}\n open\n cardProps={p}\n label={premiumLabel}\n onClick={(event) => {\n prepare(event.detail === 0);\n setExpanded(true);\n }}\n />\n ) : (\n <li key={entry.key} data-release-key={entry.key} ref={exposure.track(entry.card.id, entry.position)}>\n {p.renderCard ? (\n p.renderCard(entry.card, entry.position)\n ) : (\n <ContentCard\n card={entry.card}\n client={p.client}\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 entityLayout={p.entityLayout}\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(entry.card.id, entry.position)}\n />\n )}\n </li>\n ),\n )}\n {filteredCount > 0 && (\n <ContentSummaryTile\n tileKey=\"filtered-summary\"\n kind=\"filtered\"\n contentKind={filterKinds?.[0] ?? list.items[0]?.kind}\n open\n cardProps={p}\n label={filteredLabel}\n onClick={(event) => {\n filterTrigger.current = event.currentTarget;\n setFiltersOpen(true);\n }}\n />\n )}\n </ol>\n )}\n {list.hasMore && (\n <div ref={sentinel} className=\"grid justify-items-center gap-2\">\n {list.error && list.items.length > 0 && (\n <p role=\"alert\" className=\"text-destructive\">\n {contentError(m, list.error)}\n </p>\n )}\n <Button variant=\"outline\" size=\"sm\" disabled={list.loadingMore} onClick={() => void list.loadMore()}>\n {list.loadingMore ? t(\"contents.loading\") : list.error ? t(\"common.retry\") : t(\"contents.loadMore\")}\n </Button>\n </div>\n )}\n <ContentFilterDialog\n open={filtersOpen}\n onOpenChange={setFiltersOpen}\n returnFocus={() => (filterTrigger.current?.isConnected ? filterTrigger.current : element)}\n client={p.client}\n kinds={filterKinds}\n language={filterLanguage}\n counts={list.summary?.filtered_entities}\n countsLowerBound={list.summary?.truncated}\n appearance={p.appearance}\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 replaces them with a counted tile\n * that reveals the cached previews in place (collapse).\n */\nexport type LockedCards = \"show\" | \"hide\" | \"collapse\";\n\n/** Settle locked-card admission before pagination, retaining explicit reader modes otherwise. */\nexport function cardListMode(locked: LockedCards | undefined, mode?: AccessMode): AccessMode | undefined {\n return locked === \"hide\" ? \"hide\" : locked === \"collapse\" ? \"lock\" : mode;\n}\n\nconst LOCKED_LEVELS: readonly string[] = [\"tier\", \"group\", \"ppv\", \"group_ppv\"];\nconst isLockedCard = (card: Card) => !card.access.allowed && !card.access.unknown && LOCKED_LEVELS.includes(card.access.level);\n\n/** Contents as cards (GET /v1/contents): any sort and filter, paged. */\nexport function ContentGrid({ query, locked = \"show\", ...p }: CardListProps & { query: ContentQuery }) {\n const q = useMemo<ContentQuery>(\n () => ({\n ...query,\n summary: query.summary ?? true,\n limit: query.limit ?? p.pageSize,\n mode: cardListMode(locked, query.mode),\n }),\n [query, p.pageSize, locked],\n );\n const list = useContents(q, { client: p.client });\n return (\n <CardGrid\n {...p}\n locked={locked}\n list={list}\n render={JSON.stringify(q)}\n filterKinds={query.kind ? [query.kind] : undefined}\n filterLanguage={query.display_language ?? query.language}\n />\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(\n () => ({ ...query, mode: cardListMode(p.locked, query.mode), summary: query.summary ?? true, limit: query.limit ?? p.pageSize }),\n [query, p.pageSize, p.locked],\n );\n const list = useSearch(q, { client: p.client });\n if (!query.q.trim()) return null;\n return (\n <CardGrid\n surface=\"search\"\n empty={t(\"contents.noResults\", { q: query.q.trim() })}\n {...p}\n list={list}\n render={JSON.stringify(q)}\n filterKinds={query.kind ? [query.kind] : undefined}\n filterLanguage={query.display_language ?? query.language}\n />\n );\n}\n\nexport { ContentHistory } from \"./history.js\";\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, mode: cardListMode(p.locked, query.mode), summary: query.summary ?? true, limit }), [query, limit, p.locked]);\n const listing = useMemo(\n () =>\n listQuery\n ? { ...listQuery, mode: cardListMode(p.locked, listQuery.mode), summary: listQuery.summary ?? true, limit: listQuery.limit ?? limit }\n : group\n ? { groups: [group], sort: \"position\" as const, mode: cardListMode(p.locked), summary: true, limit }\n : null,\n [listQuery, group, limit, p.locked],\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, mode: cardListMode(p.locked, history.mode), summary: history.summary ?? true, limit: history.limit ?? limit } : null, {\n client: p.client,\n });\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, summary: l.summary, loading: l.loading, error: l.error, unavailable: l.error?.code === \"not_configured\", reload: l.reload };\n })()\n : like;\n const surface = p.surface ?? (recommendations ? \"recommendations\" : history ? \"history\" : group ? \"featured\" : listing ? \"list\" : \"similar\");\n const source = similar || recommendations || history || group || listing;\n if (!source || rail.unavailable || (!rail.loading && !rail.items.length && !rail.summary?.filtered_count && !rail.summary?.locked_count)) return null;\n const list: SummaryList = { ...rail, loadingMore: false, hasMore: false, loadMore: async () => {} };\n const kind = listQuery?.kind ?? history?.kind ?? q.kind;\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 <CardGrid\n {...p}\n className={undefined}\n surface={surface}\n horizontal={!p.grid}\n list={list}\n render={JSON.stringify([similar, recommendations, history, group, listing, q])}\n filterKinds={kind ? [kind] : undefined}\n filterLanguage={listQuery?.display_language ?? history?.display_language ?? q.display_language ?? listQuery?.language ?? history?.language ?? q.language}\n />\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 { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, type CSSProperties, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { AccessMode, ContentQuery, HistoryQuery, RailQuery, SearchQuery } from \"../../client/discovery.js\";\nimport type { Card, ListingSummary } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useContents, useExposure, useHistory, useRecommendations, useSearch, useSimilar, type UseRail } from \"../../react/discovery.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport type { UseList } from \"../../react/use-resource.js\";\nimport { ContentKitContext } from \"../../react/context.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 { ContentFilterDialog } from \"./filter-dialog.js\";\nimport { useReleaseReveal } from \"./release-reveal.js\";\nimport { ContentSummaryTile } from \"./summary-tile.js\";\nimport { ContentCard, type ContentCardProps } from \"./card.js\";\nimport { PagedContentGrid, PagedSearchResults } from \"./paged-grid.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 /** Collapse paid/locked cards into a counted tile, or hide them; default show. */\n locked?: LockedCards;\n /** Optional-filter summary tiles are on by default; hosts may suppress them in previews. */\n showFilteredSummary?: boolean;\n /** Show the collapsed locked tile only at this measured grid width. Default 0. */\n lockedSummaryMinColumns?: number;\n /** Limit rendered preview tiles without changing the matching selection or summary counts. */\n cardLimit?: number;\n /** Limit cards in mobile previews without mounting their images. */\n mobileCardLimit?: number;\n /** Host-specific nouns for summary controls. */\n summaryLabels?: { premium?: (count: number) => string; filtered?: (count: number) => string };\n /** Opt into a fluid grid, optionally with a desktop column ceiling. */\n grid?: CardGridLayout;\n /** Numbered pages with a shown range instead of load more or infinite scroll. */\n pagination?: \"pages\";\n /** The numbered page, from 1; with onPageChange the host keeps it (in its URL). */\n page?: number;\n onPageChange?: (page: number) => void;\n /** Each numbered page's link, for crawlers and opening in a new tab. */\n pageHref?: (page: number) => string;\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: CSS pixels or a CSS length such as clamp(...). Cards fill the available space. */\n minCardWidth: number | string;\n /** Maximum desktop columns. Omit to use all available width without a ceiling. */\n maxColumns?: 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 =\n typeof layout.minCardWidth === \"string\" ? layout.minCardWidth.trim() || \"160px\" : `${Number.isFinite(layout.minCardWidth) ? Math.max(1, layout.minCardWidth) : 160}px`;\n const columns = layout.mobileColumns !== undefined && Number.isFinite(layout.mobileColumns) ? Math.max(1, Math.floor(layout.mobileColumns)) : 2;\n const maximum = layout.maxColumns !== undefined && Number.isFinite(layout.maxColumns) ? Math.max(1, Math.floor(layout.maxColumns)) : undefined;\n const trackWidth = maximum === undefined ? width : `max(${width}, calc((100% - ${maximum - 1}rem) / ${maximum}))`;\n return {\n className:\n \"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\": trackWidth, \"--ckui-card-mobile-columns\": columns } as CSSProperties,\n };\n}\n\n/** A list carries exact selection-wide counts when the API was asked for them. */\ntype SummaryList = UseList<Card> & { summary?: ListingSummary };\ntype GridProps = CardListProps & { list: SummaryList; render: string; filterKinds?: readonly string[]; filterLanguage?: string; horizontal?: boolean };\n\n/** A list's cards and summary controls share one responsive, animated grid. */\nexport function CardGrid(p: GridProps) {\n const { client, viewer, language } = useContentScope(p.client);\n const context = useContext(ContentKitContext);\n const [boundary, setBoundary] = useState({ client, viewer, language, access: context?.accessRevision, render: p.render, key: 0 });\n let session = boundary;\n if (boundary.client !== client || boundary.viewer !== viewer || boundary.language !== language || boundary.access !== context?.accessRevision || boundary.render !== p.render) {\n session = { client, viewer, language, access: context?.accessRevision, render: p.render, key: boundary.key + 1 };\n setBoundary(session);\n }\n return <CardGridBody key={session.key} {...p} />;\n}\n\nfunction CardGridBody({ list, render, filterKinds, filterLanguage, horizontal, ...p }: GridProps) {\n const m = useMessages();\n const { t } = m;\n const { kinds } = useKinds({ client: p.client });\n const [expanded, setExpanded] = useState(false);\n const [filtersOpen, setFiltersOpen] = useState(false);\n const filterTrigger = useRef<HTMLElement | null>(null);\n const [element, setElement] = useState<HTMLOListElement | null>(null);\n const [columns, setColumns] = useState(0);\n const [mobile, setMobile] = useState(() => globalThis.matchMedia?.(\"(max-width: 767px)\").matches ?? false);\n useLayoutEffect(() => {\n if (!element) return;\n const measure = () => {\n const tracks = getComputedStyle(element).gridTemplateColumns.trim();\n setColumns(tracks && tracks !== \"none\" ? tracks.split(/\\s+/).length : 0);\n setMobile(globalThis.matchMedia?.(\"(max-width: 767px)\").matches ?? false);\n };\n measure();\n const observer = typeof ResizeObserver === \"function\" ? new ResizeObserver(measure) : undefined;\n observer?.observe(element);\n addEventListener(\"resize\", measure);\n return () => {\n observer?.disconnect();\n removeEventListener(\"resize\", measure);\n };\n }, [element]);\n const lockedCount = list.summary?.locked_count ?? 0;\n const filteredCount = p.showFilteredSummary === false ? 0 : (list.summary?.filtered_count ?? 0);\n const collapse = p.locked === \"collapse\" && !expanded && lockedCount > 0;\n const lockedTile = collapse && lockedCount > 0 && columns >= Math.max(0, p.lockedSummaryMinColumns ?? 0);\n const entries = useMemo(() => {\n const result: ({ key: string; card: Card; position: number } | { key: string; premium: true; card?: Card })[] = [];\n let inserted = false;\n for (const [i, card] of list.items.entries()) {\n if (isLockedCard(card) && (collapse || p.locked === \"hide\")) {\n if (lockedTile && !inserted) {\n result.push({ key: \"premium-summary\", premium: true, card });\n inserted = true;\n }\n continue;\n }\n result.push({ key: card.id, card, position: i + 1 });\n }\n if (lockedTile && !inserted) result.push({ key: \"premium-summary\", premium: true });\n const limit = mobile && p.mobileCardLimit !== undefined ? p.mobileCardLimit : p.cardLimit;\n return limit !== undefined && Number.isFinite(limit) ? result.slice(0, Math.max(0, Math.floor(limit))) : result;\n }, [list.items, collapse, p.locked, lockedTile, mobile, p.mobileCardLimit, p.cardLimit]);\n const shown = useMemo(() => entries.flatMap((entry) => (\"premium\" in entry ? [] : [{ ...entry.card, position: entry.position }])), [entries]);\n const exposure = useExposure(shown, { surface: p.surface ?? \"list\", render, client: p.client });\n const [sentinel, near] = useNearViewport({ margin: \"600px 0px\", leaveMargin: \"1200px 0px\" });\n const { hasMore, loadingMore, loadMore, error } = list;\n useEffect(() => {\n if (p.infinite && near && hasMore && !loadingMore && !error) void loadMore();\n }, [p.infinite, near, hasMore, loadingMore, loadMore, error]);\n const { grid: animationGrid, prepare } = useReleaseReveal(entries.map((entry) => entry.key).join(\"\\0\"));\n const grid = useCallback(\n (node: HTMLOListElement | null) => {\n animationGrid.current = node;\n setElement(node);\n },\n [animationGrid],\n );\n const layout: ReturnType<typeof cardGridLayout> = horizontal\n ? { className: \"relative flex snap-x gap-4 overflow-x-auto pb-2 [&>li]:w-36 [&>li]:shrink-0 [&>li]:snap-start sm:[&>li]:w-44\" }\n : cardGridLayout(p.grid);\n const lowerBound = list.summary?.truncated ? \"≥ \" : \"\";\n const premiumLabel = lowerBound + (p.summaryLabels?.premium?.(lockedCount) ?? m.plural(\"contents.lockedShow\", lockedCount));\n const filteredLabel = lowerBound + (p.summaryLabels?.filtered?.(filteredCount) ?? m.plural(\"releases.filteredMore\", filteredCount));\n const showGrid = entries.length > 0 || filteredCount > 0 || (collapse && lockedCount > 0);\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 <ol aria-hidden className={layout.className} style={layout.style}>\n {Array.from({ length: 8 }, (_, i) => (\n <li key={i}>\n <Skeleton\n className={p.aspectRatio === undefined ? (p.shape === \"landscape\" ? \"aspect-video w-full\" : \"aspect-[3/4] w-full\") : \"w-full\"}\n style={p.aspectRatio === undefined ? undefined : { aspectRatio: p.aspectRatio }}\n />\n </li>\n ))}\n </ol>\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 ) : !showGrid ? (\n <div className=\"text-muted-foreground\" role=\"status\">\n {p.empty ?? t(\"contents.empty\")}\n </div>\n ) : (\n <ol ref={grid} tabIndex={-1} className={layout.className} style={layout.style}>\n {entries.map((entry) =>\n \"premium\" in entry ? (\n <ContentSummaryTile\n key={entry.key}\n tileKey={entry.key}\n kind=\"premium\"\n contentKind={filterKinds?.[0] ?? entry.card?.kind}\n preview={entry.card}\n open\n cardProps={p}\n label={premiumLabel}\n onClick={(event) => {\n prepare(event.detail === 0);\n setExpanded(true);\n }}\n />\n ) : (\n <li key={entry.key} data-release-key={entry.key} ref={exposure.track(entry.card.id, entry.position)}>\n {p.renderCard ? (\n p.renderCard(entry.card, entry.position)\n ) : (\n <ContentCard\n card={entry.card}\n client={p.client}\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 chipOverflow={p.chipOverflow}\n chipFit={p.chipFit}\n filterEntity={p.filterEntity}\n entityStyle={p.entityStyle}\n entityLayout={p.entityLayout}\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(entry.card.id, entry.position)}\n />\n )}\n </li>\n ),\n )}\n {filteredCount > 0 && (\n <ContentSummaryTile\n tileKey=\"filtered-summary\"\n kind=\"filtered\"\n contentKind={filterKinds?.[0] ?? list.items[0]?.kind}\n open\n cardProps={p}\n label={filteredLabel}\n onClick={(event) => {\n filterTrigger.current = event.currentTarget;\n setFiltersOpen(true);\n }}\n />\n )}\n </ol>\n )}\n {list.hasMore && (\n <div ref={sentinel} className=\"grid justify-items-center gap-2\">\n {list.error && list.items.length > 0 && (\n <p role=\"alert\" className=\"text-destructive\">\n {contentError(m, list.error)}\n </p>\n )}\n <Button variant=\"outline\" size=\"sm\" disabled={list.loadingMore} onClick={() => void list.loadMore()}>\n {list.loadingMore ? t(\"contents.loading\") : list.error ? t(\"common.retry\") : t(\"contents.loadMore\")}\n </Button>\n </div>\n )}\n <ContentFilterDialog\n open={filtersOpen}\n onOpenChange={setFiltersOpen}\n returnFocus={() => (filterTrigger.current?.isConnected ? filterTrigger.current : element)}\n client={p.client}\n kinds={filterKinds}\n language={filterLanguage}\n counts={list.summary?.filtered_entities}\n countsLowerBound={list.summary?.truncated}\n appearance={p.appearance}\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 replaces them with a counted tile\n * that reveals the cached previews in place (collapse).\n */\nexport type LockedCards = \"show\" | \"hide\" | \"collapse\";\n\n/** Settle locked-card admission before pagination, retaining explicit reader modes otherwise. */\nexport function cardListMode(locked: LockedCards | undefined, mode?: AccessMode): AccessMode | undefined {\n return locked === \"hide\" ? \"hide\" : locked === \"collapse\" ? \"lock\" : mode;\n}\n\nconst LOCKED_LEVELS: readonly string[] = [\"tier\", \"group\", \"ppv\", \"group_ppv\"];\nconst isLockedCard = (card: Card) => !card.access.allowed && !card.access.unknown && LOCKED_LEVELS.includes(card.access.level);\n\n/** Contents as cards (GET /v1/contents): any sort and filter, paged by load more, infinite scroll or numbered pages. */\nexport function ContentGrid(p: CardListProps & { query: ContentQuery }) {\n return p.pagination === \"pages\" ? <PagedContentGrid {...p} /> : <CursorContentGrid {...p} />;\n}\n\nfunction CursorContentGrid({ query, locked = \"show\", ...p }: CardListProps & { query: ContentQuery }) {\n const q = useMemo<ContentQuery>(\n () => ({\n ...query,\n summary: query.summary ?? true,\n limit: query.limit ?? p.pageSize,\n mode: cardListMode(locked, query.mode),\n }),\n [query, p.pageSize, locked],\n );\n const list = useContents(q, { client: p.client });\n return (\n <CardGrid\n {...p}\n locked={locked}\n list={list}\n render={JSON.stringify(q)}\n filterKinds={query.kind ? [query.kind] : undefined}\n filterLanguage={query.display_language ?? query.language}\n />\n );\n}\n\n/** Search results as cards; nothing until the query has text. */\nexport function SearchResults(p: CardListProps & { query: SearchQuery }) {\n return p.pagination === \"pages\" ? <PagedSearchResults {...p} /> : <CursorSearchResults {...p} />;\n}\n\nfunction CursorSearchResults({ query, ...p }: CardListProps & { query: SearchQuery }) {\n const { t } = useMessages();\n const q = useMemo(\n () => ({ ...query, mode: cardListMode(p.locked, query.mode), summary: query.summary ?? true, limit: query.limit ?? p.pageSize }),\n [query, p.pageSize, p.locked],\n );\n const list = useSearch(q, { client: p.client });\n if (!query.q.trim()) return null;\n return (\n <CardGrid\n surface=\"search\"\n empty={t(\"contents.noResults\", { q: query.q.trim() })}\n {...p}\n list={list}\n render={JSON.stringify(q)}\n filterKinds={query.kind ? [query.kind] : undefined}\n filterLanguage={query.display_language ?? query.language}\n />\n );\n}\n\nexport { ContentHistory } from \"./history.js\";\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, mode: cardListMode(p.locked, query.mode), summary: query.summary ?? true, limit }), [query, limit, p.locked]);\n const listing = useMemo(\n () =>\n listQuery\n ? { ...listQuery, mode: cardListMode(p.locked, listQuery.mode), summary: listQuery.summary ?? true, limit: listQuery.limit ?? limit }\n : group\n ? { groups: [group], sort: \"position\" as const, mode: cardListMode(p.locked), summary: true, limit }\n : null,\n [listQuery, group, limit, p.locked],\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, mode: cardListMode(p.locked, history.mode), summary: history.summary ?? true, limit: history.limit ?? limit } : null, {\n client: p.client,\n });\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, summary: l.summary, loading: l.loading, error: l.error, unavailable: l.error?.code === \"not_configured\", reload: l.reload };\n })()\n : like;\n const surface = p.surface ?? (recommendations ? \"recommendations\" : history ? \"history\" : group ? \"featured\" : listing ? \"list\" : \"similar\");\n const source = similar || recommendations || history || group || listing;\n if (!source || rail.unavailable || (!rail.loading && !rail.items.length && !rail.summary?.filtered_count && !rail.summary?.locked_count)) return null;\n const list: SummaryList = { ...rail, loadingMore: false, hasMore: false, loadMore: async () => {} };\n const kind = listQuery?.kind ?? history?.kind ?? q.kind;\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 <CardGrid\n {...p}\n className={undefined}\n surface={surface}\n horizontal={!p.grid}\n list={list}\n render={JSON.stringify([similar, recommendations, history, group, listing, q])}\n filterKinds={kind ? [kind] : undefined}\n filterLanguage={listQuery?.display_language ?? history?.display_language ?? q.display_language ?? listQuery?.language ?? history?.language ?? q.language}\n />\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
@@ -1,91 +1,111 @@
1
1
  import { useMessages as e } from "../../i18n/context.js";
2
- import { Button as t } from "../../ui/button.js";
3
- import { useAction as n } from "../social/parts.js";
4
- import { useHistory as r } from "../../react/discovery.js";
5
- import { ContentCard as i } from "./card.js";
6
- import { ContentFilterDialog as a } from "./filter-dialog.js";
7
- import { CardGrid as o } from "./grid.js";
8
- import { useMemo as s, useRef as c, useState as l } from "react";
9
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
2
+ import { useKinds as t } from "../../react/groups.js";
3
+ import { Button as n } from "../../ui/button.js";
4
+ import { formatDate as r, relativeTime as i, useAction as a } from "../social/parts.js";
5
+ import { useHistory as o } from "../../react/discovery.js";
6
+ import { ContentFilterDialog as s } from "./filter-dialog.js";
7
+ import { ContentCard as c } from "./card.js";
8
+ import { CardGrid as l } from "./grid.js";
9
+ import { useMemo as u, useRef as d, useState as f } from "react";
10
+ import { jsx as p, jsxs as m } from "react/jsx-runtime";
10
11
  //#region src/components/content/history.tsx
11
- function f({ query: f = {}, ...p }) {
12
- let { t: m } = e(), h = s(() => ({
13
- ...f,
12
+ function h({ query: r = {}, ...i }) {
13
+ let { t: h } = e(), _ = u(() => ({
14
+ ...r,
14
15
  summary: !0,
15
- mode: p.locked === "collapse" ? "lock" : p.locked === "hide" ? "hide" : f.mode,
16
- limit: f.limit ?? p.pageSize
16
+ mode: i.locked === "collapse" ? "lock" : i.locked === "hide" ? "hide" : r.mode,
17
+ limit: r.limit ?? i.pageSize
17
18
  }), [
18
- f,
19
- p.pageSize,
20
- p.locked
21
- ]), g = r(h, { client: p.client }), _ = n("history.forget"), [v, y] = l(!1), b = c(null);
22
- return /* @__PURE__ */ d("div", {
19
+ r,
20
+ i.pageSize,
21
+ i.locked
22
+ ]), v = o(_, { client: i.client }), y = a("history.forget"), { kinds: b } = t({ client: i.client }), [x, S] = f(!1), C = d(null);
23
+ return /* @__PURE__ */ m("div", {
23
24
  className: "grid gap-3",
24
25
  "data-ckui": "content-history",
25
26
  children: [
26
- g.items.length > 0 && /* @__PURE__ */ u(t, {
27
+ v.items.length > 0 && /* @__PURE__ */ p(n, {
27
28
  size: "sm",
28
29
  variant: "outline",
29
30
  className: "justify-self-end",
30
- disabled: _.pending,
31
- onClick: () => void _.run(() => g.forget(f.kind ? { kind: f.kind } : {})),
32
- children: m("contents.clearHistory")
31
+ disabled: y.pending,
32
+ onClick: () => void y.run(() => v.forget(r.kind ? { kind: r.kind } : {})),
33
+ children: h("contents.clearHistory")
33
34
  }),
34
- g.filterSummaryUnknown && p.showFilteredSummary !== !1 && /* @__PURE__ */ u(t, {
35
- ref: b,
35
+ v.filterSummaryUnknown && i.showFilteredSummary !== !1 && /* @__PURE__ */ p(n, {
36
+ ref: C,
36
37
  variant: "outline",
37
38
  className: "justify-self-start",
38
- onClick: () => y(!0),
39
- children: m("releases.manageFilters")
39
+ onClick: () => S(!0),
40
+ children: h("releases.manageFilters")
40
41
  }),
41
- /* @__PURE__ */ u(a, {
42
- client: p.client,
43
- language: h.language,
44
- open: v,
45
- onOpenChange: y,
46
- returnFocus: () => b.current
42
+ /* @__PURE__ */ p(s, {
43
+ client: i.client,
44
+ language: _.language,
45
+ open: x,
46
+ onOpenChange: S,
47
+ returnFocus: () => C.current
47
48
  }),
48
- /* @__PURE__ */ u(o, {
49
+ /* @__PURE__ */ p(l, {
49
50
  surface: "history",
50
- empty: m("contents.historyEmpty"),
51
- ...p,
52
- list: g,
53
- render: JSON.stringify(h),
54
- filterLanguage: h.language,
55
- renderCard: p.renderCard ?? ((e) => /* @__PURE__ */ u(i, {
51
+ empty: h("contents.historyEmpty"),
52
+ ...i,
53
+ list: v,
54
+ render: JSON.stringify(_),
55
+ filterLanguage: _.language,
56
+ renderCard: i.renderCard ?? ((e) => /* @__PURE__ */ m(c, {
56
57
  card: e,
57
- client: p.client,
58
- href: p.href,
59
- shape: p.shape,
60
- aspectRatio: p.aspectRatio,
61
- titlePosition: p.titlePosition,
62
- loadMedia: p.loadMedia,
63
- groupKinds: p.groupKinds,
64
- chips: p.chips,
65
- filterEntity: p.filterEntity,
66
- entityStyle: p.entityStyle,
67
- entityLayout: p.entityLayout,
68
- metadata: p.metadata,
69
- showCounts: p.showCounts,
70
- tiers: p.tiers,
71
- labels: p.labels,
72
- owner: p.owner,
73
- ownerHref: p.ownerHref,
74
- decorate: p.decorate,
75
- children: /* @__PURE__ */ u(t, {
58
+ client: i.client,
59
+ href: i.href,
60
+ shape: i.shape,
61
+ aspectRatio: i.aspectRatio,
62
+ titlePosition: i.titlePosition,
63
+ loadMedia: i.loadMedia,
64
+ groupKinds: i.groupKinds,
65
+ chips: i.chips,
66
+ chipOverflow: i.chipOverflow,
67
+ chipFit: i.chipFit,
68
+ filterEntity: i.filterEntity,
69
+ entityStyle: i.entityStyle,
70
+ entityLayout: i.entityLayout,
71
+ metadata: i.metadata,
72
+ showCounts: i.showCounts,
73
+ tiers: i.tiers,
74
+ labels: i.labels,
75
+ owner: i.owner,
76
+ ownerHref: i.ownerHref,
77
+ decorate: i.decorate,
78
+ children: [/* @__PURE__ */ p(g, {
79
+ card: e,
80
+ presentation: b?.kinds[e.kind]?.presentation
81
+ }), /* @__PURE__ */ p(n, {
76
82
  size: "xs",
77
83
  variant: "ghost",
78
84
  className: "justify-self-start",
79
- disabled: _.pending,
80
- onClick: () => void _.run(() => g.forget({ content_id: e.id })),
81
- children: m("contents.removeFromHistory")
82
- })
85
+ disabled: y.pending,
86
+ onClick: () => void y.run(() => v.forget({ content_id: e.id })),
87
+ children: h("contents.removeFromHistory")
88
+ })]
83
89
  }))
84
90
  })
85
91
  ]
86
92
  });
87
93
  }
94
+ function g({ card: t, presentation: n }) {
95
+ let a = e(), o = t.viewer;
96
+ if (!o) return null;
97
+ let s = o.completed ? o.progress_max : Math.min(o.progress, o.progress_max), c = o.progress_max > 0 && n === "reader" ? a.plural("contents.pageProgress", o.progress_max, { progress: s }) : o.progress_max > 0 && n === "player" ? a.plural("contents.minuteProgress", Math.max(1, Math.round(o.progress_max / 60)), { progress: Math.round(s / 60) }) : null;
98
+ return !o.last_read_at && !c ? null : /* @__PURE__ */ m("p", {
99
+ className: "flex flex-wrap gap-x-2 text-xs tabular-nums text-muted-foreground",
100
+ "data-ckui": "history-progress",
101
+ children: [o.last_read_at && /* @__PURE__ */ p("time", {
102
+ dateTime: o.last_read_at,
103
+ title: r(a, o.last_read_at),
104
+ children: a.t(n === "player" ? "contents.watchedAgo" : "contents.readAgo", { time: i(a, o.last_read_at) })
105
+ }), c && /* @__PURE__ */ p("span", { children: c })]
106
+ });
107
+ }
88
108
  //#endregion
89
- export { f as ContentHistory };
109
+ export { h as ContentHistory };
90
110
 
91
111
  //# sourceMappingURL=history.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"history.js","names":[],"sources":["../../../src/components/content/history.tsx"],"sourcesContent":["import { useMemo, useRef, useState } from \"react\";\nimport type { HistoryQuery } from \"../../client/discovery.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useHistory } from \"../../react/discovery.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { useAction } from \"../social/parts.js\";\nimport { ContentFilterDialog } from \"./filter-dialog.js\";\nimport { ContentCard } from \"./card.js\";\nimport { CardGrid, type CardListProps } from \"./grid.js\";\n\n/** Account history or this browser's guest history, each removable, with a clear-all. */\nexport function ContentHistory({ query = {}, ...p }: CardListProps & { query?: HistoryQuery }) {\n const { t } = useMessages();\n const q = useMemo(() => ({ ...query, summary: true, mode: p.locked === \"collapse\" ? \"lock\" as const : p.locked === \"hide\" ? \"hide\" as const : query.mode, limit: query.limit ?? p.pageSize }), [query, p.pageSize, p.locked]);\n const list = useHistory(q, { client: p.client });\n const forget = useAction(\"history.forget\");\n const [filtersOpen, setFiltersOpen] = useState(false);\n const filterTrigger = useRef<HTMLButtonElement | null>(null);\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 {list.filterSummaryUnknown && p.showFilteredSummary !== false && (\n <Button ref={filterTrigger} variant=\"outline\" className=\"justify-self-start\" onClick={() => setFiltersOpen(true)}>{t(\"releases.manageFilters\")}</Button>\n )}\n <ContentFilterDialog client={p.client} language={q.language} open={filtersOpen} onOpenChange={setFiltersOpen} returnFocus={() => filterTrigger.current} />\n <CardGrid\n surface=\"history\"\n empty={t(\"contents.historyEmpty\")}\n {...p}\n list={list}\n render={JSON.stringify(q)}\n filterLanguage={q.language}\n renderCard={\n p.renderCard ??\n ((card) => (\n <ContentCard\n card={card}\n client={p.client}\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 entityLayout={p.entityLayout}\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"],"mappings":";;;;;;;;;;AAWA,SAAgB,EAAe,EAAE,WAAQ,CAAC,GAAG,GAAG,KAA+C;CAC7F,IAAM,EAAE,SAAM,EAAY,GACpB,IAAI,SAAe;EAAE,GAAG;EAAO,SAAS;EAAM,MAAM,EAAE,WAAW,aAAa,SAAkB,EAAE,WAAW,SAAS,SAAkB,EAAM;EAAM,OAAO,EAAM,SAAS,EAAE;CAAS,IAAI;EAAC;EAAO,EAAE;EAAU,EAAE;CAAM,CAAC,GACtN,IAAO,EAAW,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,GACzC,IAAS,EAAU,gBAAgB,GACnC,CAAC,GAAa,KAAkB,EAAS,EAAK,GAC9C,IAAgB,EAAiC,IAAI;CAC3D,OACE,kBAAC,OAAD;EAAK,WAAU;EAAa,aAAU;EAAtC,UAAA;GACG,EAAK,MAAM,SAAS,KACnB,kBAAC,GAAD;IAAQ,MAAK;IAAK,SAAQ;IAAU,WAAU;IAAmB,UAAU,EAAO;IAAS,eAAe,KAAK,EAAO,UAAU,EAAK,OAAO,EAAM,OAAO,EAAE,MAAM,EAAM,KAAK,IAAI,CAAC,CAAC,CAAC;IAChL,UAAA,EAAE,uBAAuB;GACpB,CAAA;GAET,EAAK,wBAAwB,EAAE,wBAAwB,MACtD,kBAAC,GAAD;IAAQ,KAAK;IAAe,SAAQ;IAAU,WAAU;IAAqB,eAAe,EAAe,EAAI;IAAI,UAAA,EAAE,wBAAwB;GAAU,CAAA;GAEzJ,kBAAC,GAAD;IAAqB,QAAQ,EAAE;IAAQ,UAAU,EAAE;IAAU,MAAM;IAAa,cAAc;IAAgB,mBAAmB,EAAc;GAAU,CAAA;GACzJ,kBAAC,GAAD;IACE,SAAQ;IACR,OAAO,EAAE,uBAAuB;IAChC,GAAI;IACE;IACN,QAAQ,KAAK,UAAU,CAAC;IACxB,gBAAgB,EAAE;IAClB,YACE,EAAE,gBACA,MACA,kBAAC,GAAD;KACQ;KACN,QAAQ,EAAE;KACV,MAAM,EAAE;KACR,OAAO,EAAE;KACT,aAAa,EAAE;KACf,eAAe,EAAE;KACjB,WAAW,EAAE;KACb,YAAY,EAAE;KACd,OAAO,EAAE;KACT,cAAc,EAAE;KAChB,aAAa,EAAE;KACf,cAAc,EAAE;KAChB,UAAU,EAAE;KACZ,YAAY,EAAE;KACd,OAAO,EAAE;KACT,QAAQ,EAAE;KACV,OAAO,EAAE;KACT,WAAW,EAAE;KACb,UAAU,EAAE;KAEZ,UAAA,kBAAC,GAAD;MAAQ,MAAK;MAAK,SAAQ;MAAQ,WAAU;MAAqB,UAAU,EAAO;MAAS,eAAe,KAAK,EAAO,UAAU,EAAK,OAAO,EAAE,YAAY,EAAK,GAAG,CAAC,CAAC;MACjK,UAAA,EAAE,4BAA4B;KACzB,CAAA;IACG,CAAA;GAGlB,CAAA;EACE;;AAET"}
1
+ {"version":3,"file":"history.js","names":[],"sources":["../../../src/components/content/history.tsx"],"sourcesContent":["import { useMemo, useRef, useState } from \"react\";\nimport type { Card } from \"../../client/generated/wire.js\";\nimport type { HistoryQuery } from \"../../client/discovery.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useHistory } from \"../../react/discovery.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { formatDate, relativeTime, useAction } from \"../social/parts.js\";\nimport { ContentFilterDialog } from \"./filter-dialog.js\";\nimport { ContentCard } from \"./card.js\";\nimport { CardGrid, type CardListProps } from \"./grid.js\";\n\n/** Account history or this browser's guest history, each removable, with a clear-all. */\nexport function ContentHistory({ query = {}, ...p }: CardListProps & { query?: HistoryQuery }) {\n const { t } = useMessages();\n const q = useMemo(() => ({ ...query, summary: true, mode: p.locked === \"collapse\" ? \"lock\" as const : p.locked === \"hide\" ? \"hide\" as const : query.mode, limit: query.limit ?? p.pageSize }), [query, p.pageSize, p.locked]);\n const list = useHistory(q, { client: p.client });\n const forget = useAction(\"history.forget\");\n const { kinds } = useKinds({ client: p.client });\n const [filtersOpen, setFiltersOpen] = useState(false);\n const filterTrigger = useRef<HTMLButtonElement | null>(null);\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 {list.filterSummaryUnknown && p.showFilteredSummary !== false && (\n <Button ref={filterTrigger} variant=\"outline\" className=\"justify-self-start\" onClick={() => setFiltersOpen(true)}>{t(\"releases.manageFilters\")}</Button>\n )}\n <ContentFilterDialog client={p.client} language={q.language} open={filtersOpen} onOpenChange={setFiltersOpen} returnFocus={() => filterTrigger.current} />\n <CardGrid\n surface=\"history\"\n empty={t(\"contents.historyEmpty\")}\n {...p}\n list={list}\n render={JSON.stringify(q)}\n filterLanguage={q.language}\n renderCard={\n p.renderCard ??\n ((card) => (\n <ContentCard\n card={card}\n client={p.client}\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 chipOverflow={p.chipOverflow}\n chipFit={p.chipFit}\n filterEntity={p.filterEntity}\n entityStyle={p.entityStyle}\n entityLayout={p.entityLayout}\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 <HistoryProgress card={card} presentation={kinds?.kinds[card.kind]?.presentation} />\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\n/** When the reader last read or watched it, and how far: pages for a reader, minutes for a player. */\nfunction HistoryProgress({ card, presentation }: { card: Card; presentation?: string }) {\n const m = useMessages();\n const v = card.viewer;\n if (!v) return null;\n const done = v.completed ? v.progress_max : Math.min(v.progress, v.progress_max);\n const amount =\n v.progress_max > 0 && presentation === \"reader\"\n ? m.plural(\"contents.pageProgress\", v.progress_max, { progress: done })\n : v.progress_max > 0 && presentation === \"player\"\n ? m.plural(\"contents.minuteProgress\", Math.max(1, Math.round(v.progress_max / 60)), { progress: Math.round(done / 60) })\n : null;\n if (!v.last_read_at && !amount) return null;\n return (\n <p className=\"flex flex-wrap gap-x-2 text-xs tabular-nums text-muted-foreground\" data-ckui=\"history-progress\">\n {v.last_read_at && (\n <time dateTime={v.last_read_at} title={formatDate(m, v.last_read_at)}>\n {m.t(presentation === \"player\" ? \"contents.watchedAgo\" : \"contents.readAgo\", { time: relativeTime(m, v.last_read_at) })}\n </time>\n )}\n {amount && <span>{amount}</span>}\n </p>\n );\n}\n"],"mappings":";;;;;;;;;;;AAaA,SAAgB,EAAe,EAAE,WAAQ,CAAC,GAAG,GAAG,KAA+C;CAC7F,IAAM,EAAE,SAAM,EAAY,GACpB,IAAI,SAAe;EAAE,GAAG;EAAO,SAAS;EAAM,MAAM,EAAE,WAAW,aAAa,SAAkB,EAAE,WAAW,SAAS,SAAkB,EAAM;EAAM,OAAO,EAAM,SAAS,EAAE;CAAS,IAAI;EAAC;EAAO,EAAE;EAAU,EAAE;CAAM,CAAC,GACtN,IAAO,EAAW,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,GACzC,IAAS,EAAU,gBAAgB,GACnC,EAAE,aAAU,EAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,GACzC,CAAC,GAAa,KAAkB,EAAS,EAAK,GAC9C,IAAgB,EAAiC,IAAI;CAC3D,OACE,kBAAC,OAAD;EAAK,WAAU;EAAa,aAAU;EAAtC,UAAA;GACG,EAAK,MAAM,SAAS,KACnB,kBAAC,GAAD;IAAQ,MAAK;IAAK,SAAQ;IAAU,WAAU;IAAmB,UAAU,EAAO;IAAS,eAAe,KAAK,EAAO,UAAU,EAAK,OAAO,EAAM,OAAO,EAAE,MAAM,EAAM,KAAK,IAAI,CAAC,CAAC,CAAC;IAChL,UAAA,EAAE,uBAAuB;GACpB,CAAA;GAET,EAAK,wBAAwB,EAAE,wBAAwB,MACtD,kBAAC,GAAD;IAAQ,KAAK;IAAe,SAAQ;IAAU,WAAU;IAAqB,eAAe,EAAe,EAAI;IAAI,UAAA,EAAE,wBAAwB;GAAU,CAAA;GAEzJ,kBAAC,GAAD;IAAqB,QAAQ,EAAE;IAAQ,UAAU,EAAE;IAAU,MAAM;IAAa,cAAc;IAAgB,mBAAmB,EAAc;GAAU,CAAA;GACzJ,kBAAC,GAAD;IACE,SAAQ;IACR,OAAO,EAAE,uBAAuB;IAChC,GAAI;IACE;IACN,QAAQ,KAAK,UAAU,CAAC;IACxB,gBAAgB,EAAE;IAClB,YACE,EAAE,gBACA,MACA,kBAAC,GAAD;KACQ;KACN,QAAQ,EAAE;KACV,MAAM,EAAE;KACR,OAAO,EAAE;KACT,aAAa,EAAE;KACf,eAAe,EAAE;KACjB,WAAW,EAAE;KACb,YAAY,EAAE;KACd,OAAO,EAAE;KACT,cAAc,EAAE;KAChB,SAAS,EAAE;KACX,cAAc,EAAE;KAChB,aAAa,EAAE;KACf,cAAc,EAAE;KAChB,UAAU,EAAE;KACZ,YAAY,EAAE;KACd,OAAO,EAAE;KACT,QAAQ,EAAE;KACV,OAAO,EAAE;KACT,WAAW,EAAE;KACb,UAAU,EAAE;KArBd,UAAA,CAuBE,kBAAC,GAAD;MAAuB;MAAM,cAAc,GAAO,MAAM,EAAK,KAAK,EAAE;KAAe,CAAA,GACnF,kBAAC,GAAD;MAAQ,MAAK;MAAK,SAAQ;MAAQ,WAAU;MAAqB,UAAU,EAAO;MAAS,eAAe,KAAK,EAAO,UAAU,EAAK,OAAO,EAAE,YAAY,EAAK,GAAG,CAAC,CAAC;MACjK,UAAA,EAAE,4BAA4B;KACzB,CAAA,CACG;;GAGlB,CAAA;EACE;;AAET;AAGA,SAAS,EAAgB,EAAE,SAAM,mBAAuD;CACtF,IAAM,IAAI,EAAY,GAChB,IAAI,EAAK;CACf,IAAI,CAAC,GAAG,OAAO;CACf,IAAM,IAAO,EAAE,YAAY,EAAE,eAAe,KAAK,IAAI,EAAE,UAAU,EAAE,YAAY,GACzE,IACJ,EAAE,eAAe,KAAK,MAAiB,WACnC,EAAE,OAAO,yBAAyB,EAAE,cAAc,EAAE,UAAU,EAAK,CAAC,IACpE,EAAE,eAAe,KAAK,MAAiB,WACrC,EAAE,OAAO,2BAA2B,KAAK,IAAI,GAAG,KAAK,MAAM,EAAE,eAAe,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,MAAM,IAAO,EAAE,EAAE,CAAC,IACrH;CAER,OADI,CAAC,EAAE,gBAAgB,CAAC,IAAe,OAErC,kBAAC,KAAD;EAAG,WAAU;EAAoE,aAAU;EAA3F,UAAA,CACG,EAAE,gBACD,kBAAC,QAAD;GAAM,UAAU,EAAE;GAAc,OAAO,EAAW,GAAG,EAAE,YAAY;GAChE,UAAA,EAAE,EAAE,MAAiB,WAAW,wBAAwB,oBAAoB,EAAE,MAAM,EAAa,GAAG,EAAE,YAAY,EAAE,CAAC;EAClH,CAAA,GAEP,KAAU,kBAAC,QAAD,EAAA,UAAO,EAAa,CAAA,CAC9B;;AAEP"}
@@ -1,4 +1,4 @@
1
- export { ContentCard, OwnerChip, PostCard, lockLabel, type CardMetadata, type ContentCardProps, type PostCardProps } from './card.js';
1
+ export { CardStatusBadges, ContentCard, OwnerChip, PostCard, cardStatus, lockLabel, type CardMetadata, type CardStatus, type ContentCardProps, type PostCardProps } from './card.js';
2
2
  export { cardGridLayout, CardGrid, ContentGrid, ContentHistory, ContentRail, SearchResults, type CardGridLayout, type CardListProps, type ContentRailProps, type LockedCards } from './grid.js';
3
3
  export { DailyReleases, type DailyReleasesProps } from './daily-releases.js';
4
4
  export { ReleaseDatePicker, isReleaseDate, localReleaseDate, type ReleaseDatePickerProps } from './release-date-picker.js';
@@ -7,10 +7,12 @@ export { SearchBox, type SearchBoxProps } from './search.js';
7
7
  export { ReaderSettings, type ReaderSettingsProps } from './reader-settings.js';
8
8
  export { ContentFilterDialog, type ContentFilterDialogProps } from './filter-dialog.js';
9
9
  export { ContentFilterSettings, ReleaseDateIndex, type ContentFilterSettingsProps, type ContentFilterCount, type ReleaseDateIndexProps } from './filters.js';
10
- export { ContentListing, sortLabel, type ContentListingProps } from './listing.js';
11
- export { Paywall, VersionPicker, priceLabel, useVersionMenuItems, type PaywallProps, type VersionMenuOptions, type VersionPickerProps } from './paywall.js';
10
+ export { ContentListing, SortMenu, sortLabel, type ContentListingProps, type SortMenuProps } from './listing.js';
11
+ export { Pagination, pageWindow, type PaginationProps } from './pagination.js';
12
+ export { LanguageFallbackNotice, Paywall, VersionPicker, priceLabel, useVersionMenuItems, type LanguageFallbackNoticeProps, type PaywallProps, type VersionMenuOptions, type VersionPickerProps } from './paywall.js';
12
13
  export { AccessEditor, ContentDetailsEditor, ContentSections, SegmentsEditor, VersionCover, VersionsEditor, versionLabel } from './editors.js';
13
14
  export { OfferFields } from './offer-fields.js';
14
15
  export { ReaderStats, type ReaderStatsProps } from './reader-stats.js';
15
16
  export { VersionDownloadButton, type VersionDownloadButtonProps } from './download-button.js';
16
17
  export { ContentFavorites } from './favorites.js';
18
+ export { PostList, excerpt, type PostListProps } from './post-list.js';