@openrails/contentkit-ui 0.103.2 → 0.104.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -49
- package/dist/client/beacons.d.ts +3 -13
- package/dist/client/beacons.js +8 -46
- package/dist/client/beacons.js.map +1 -1
- package/dist/client/client.d.ts +3 -0
- package/dist/client/client.js +45 -43
- package/dist/client/client.js.map +1 -1
- package/dist/client/content/favorites.d.ts +2 -0
- package/dist/client/content/favorites.js.map +1 -1
- package/dist/client/content/types.d.ts +3 -0
- package/dist/client/content/types.js.map +1 -1
- package/dist/client/contents.d.ts +7 -1
- package/dist/client/contents.js +8 -0
- package/dist/client/contents.js.map +1 -1
- package/dist/client/discovery.d.ts +34 -5
- package/dist/client/discovery.js +23 -2
- package/dist/client/discovery.js.map +1 -1
- package/dist/client/engagement.d.ts +70 -0
- package/dist/client/engagement.js +191 -0
- package/dist/client/engagement.js.map +1 -0
- package/dist/client/generated/routes.d.ts +74 -35
- package/dist/client/generated/routes.js +119 -60
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +88 -85
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/guest-reading-history.d.ts +40 -0
- package/dist/client/guest-reading-history.js +144 -0
- package/dist/client/guest-reading-history.js.map +1 -0
- package/dist/client/index.d.ts +5 -3
- package/dist/client/media/windows.d.ts +2 -0
- package/dist/client/media/windows.js +4 -1
- package/dist/client/media/windows.js.map +1 -1
- package/dist/client/stats.d.ts +4 -20
- package/dist/client/stats.js +0 -11
- package/dist/client/stats.js.map +1 -1
- package/dist/client/versions.d.ts +8 -6
- package/dist/client/versions.js +15 -10
- package/dist/client/versions.js.map +1 -1
- package/dist/client.js +24 -22
- package/dist/components/admin/bulk-drafts.d.ts +17 -0
- package/dist/components/admin/bulk-drafts.js +41 -0
- package/dist/components/admin/bulk-drafts.js.map +1 -0
- package/dist/components/admin/content-admin.d.ts +1 -1
- package/dist/components/admin/content-admin.js.map +1 -1
- package/dist/components/admin/content-manager.d.ts +11 -3
- package/dist/components/admin/content-manager.js +751 -394
- package/dist/components/admin/content-manager.js.map +1 -1
- package/dist/components/admin/group-manager.d.ts +8 -3
- package/dist/components/admin/group-manager.js +218 -146
- package/dist/components/admin/group-manager.js.map +1 -1
- package/dist/components/admin/page-notices.d.ts +16 -0
- package/dist/components/admin/page-notices.js +113 -0
- package/dist/components/admin/page-notices.js.map +1 -0
- package/dist/components/admin/page-picker.d.ts +38 -0
- package/dist/components/admin/page-picker.js +159 -0
- package/dist/components/admin/page-picker.js.map +1 -0
- package/dist/components/admin/quick-search.d.ts +14 -0
- package/dist/components/admin/quick-search.js +60 -0
- package/dist/components/admin/quick-search.js.map +1 -0
- package/dist/components/admin/reader-content-editor.js +382 -321
- package/dist/components/admin/reader-content-editor.js.map +1 -1
- package/dist/components/admin/reader-manifest.js +181 -179
- package/dist/components/admin/reader-manifest.js.map +1 -1
- package/dist/components/admin/shell.d.ts +10 -2
- package/dist/components/admin/shell.js +114 -42
- package/dist/components/admin/shell.js.map +1 -1
- package/dist/components/admin/stats-dashboard.d.ts +4 -9
- package/dist/components/admin/stats-dashboard.js +303 -207
- package/dist/components/admin/stats-dashboard.js.map +1 -1
- package/dist/components/admin/upload-queue.d.ts +14 -0
- package/dist/components/admin/upload-queue.js +115 -98
- package/dist/components/admin/upload-queue.js.map +1 -1
- package/dist/components/content/card.d.ts +31 -3
- package/dist/components/content/card.js +324 -158
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/daily-releases.js +144 -190
- package/dist/components/content/daily-releases.js.map +1 -1
- package/dist/components/content/editor-save.js +27 -25
- package/dist/components/content/editor-save.js.map +1 -1
- package/dist/components/content/editors.js +274 -253
- package/dist/components/content/editors.js.map +1 -1
- package/dist/components/content/favorites.d.ts +6 -0
- package/dist/components/content/favorites.js +47 -0
- package/dist/components/content/favorites.js.map +1 -0
- package/dist/components/content/filter-dialog.js +19 -15
- package/dist/components/content/filter-dialog.js.map +1 -1
- package/dist/components/content/filters.d.ts +2 -0
- package/dist/components/content/filters.js +5 -1
- package/dist/components/content/filters.js.map +1 -1
- package/dist/components/content/grid.d.ts +44 -16
- package/dist/components/content/grid.js +308 -270
- package/dist/components/content/grid.js.map +1 -1
- package/dist/components/content/history.d.ts +6 -0
- package/dist/components/content/history.js +111 -0
- package/dist/components/content/history.js.map +1 -0
- package/dist/components/content/index.d.ts +6 -3
- package/dist/components/content/listing.d.ts +14 -3
- package/dist/components/content/listing.js +65 -56
- package/dist/components/content/listing.js.map +1 -1
- package/dist/components/content/offer-fields.js +6 -6
- package/dist/components/content/paged-grid.d.ts +10 -0
- package/dist/components/content/paged-grid.js +116 -0
- package/dist/components/content/paged-grid.js.map +1 -0
- package/dist/components/content/pagination.d.ts +19 -0
- package/dist/components/content/pagination.js +97 -0
- package/dist/components/content/pagination.js.map +1 -0
- package/dist/components/content/paywall.d.ts +9 -0
- package/dist/components/content/paywall.js +27 -3
- package/dist/components/content/paywall.js.map +1 -1
- package/dist/components/content/post-list.d.ts +37 -0
- package/dist/components/content/post-list.js +203 -0
- package/dist/components/content/post-list.js.map +1 -0
- package/dist/components/content/reader-settings.d.ts +2 -0
- package/dist/components/content/reader-settings.js +1 -1
- package/dist/components/content/reader-settings.js.map +1 -1
- package/dist/components/content/search.d.ts +11 -4
- package/dist/components/content/search.js +22 -11
- package/dist/components/content/search.js.map +1 -1
- package/dist/components/content/summary-tile.d.ts +14 -0
- package/dist/components/content/summary-tile.js +63 -0
- package/dist/components/content/summary-tile.js.map +1 -0
- package/dist/components/groups/banner.d.ts +6 -2
- package/dist/components/groups/banner.js +79 -61
- package/dist/components/groups/banner.js.map +1 -1
- package/dist/components/groups/directory.d.ts +12 -2
- package/dist/components/groups/directory.js +168 -105
- package/dist/components/groups/directory.js.map +1 -1
- package/dist/components/groups/editor.js +10 -10
- package/dist/components/groups/editor.js.map +1 -1
- package/dist/components/groups/entity-chips.d.ts +61 -0
- package/dist/components/groups/entity-chips.js +382 -0
- package/dist/components/groups/entity-chips.js.map +1 -0
- package/dist/components/groups/group-members.d.ts +29 -0
- package/dist/components/groups/group-members.js +474 -0
- package/dist/components/groups/group-members.js.map +1 -0
- package/dist/components/groups/header.js +53 -45
- package/dist/components/groups/header.js.map +1 -1
- package/dist/components/groups/index.d.ts +3 -1
- package/dist/components/groups/memberships.d.ts +3 -36
- package/dist/components/groups/memberships.js +105 -480
- package/dist/components/groups/memberships.js.map +1 -1
- package/dist/components/groups/scoped.d.ts +23 -0
- package/dist/components/groups/scoped.js +92 -0
- package/dist/components/groups/scoped.js.map +1 -0
- package/dist/components/media-folder-editor.d.ts +5 -0
- package/dist/components/media-folder-editor.js +419 -231
- package/dist/components/media-folder-editor.js.map +1 -1
- package/dist/components/sortable-list.d.ts +5 -1
- package/dist/components/sortable-list.js +97 -81
- package/dist/components/sortable-list.js.map +1 -1
- package/dist/components/video-mini-player.d.ts +4 -1
- package/dist/components/video-mini-player.js +77 -71
- package/dist/components/video-mini-player.js.map +1 -1
- package/dist/components/video-player.d.ts +4 -1
- package/dist/components/video-player.js +280 -276
- package/dist/components/video-player.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +41 -34
- package/dist/locales/admin/de.js +80 -47
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +74 -41
- package/dist/locales/admin/en.js +80 -47
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +80 -47
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +80 -47
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +80 -47
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +80 -47
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/de.js +1 -0
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/discovery/de.js +25 -2
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +24 -1
- package/dist/locales/discovery/en.js +25 -2
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +25 -2
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +25 -2
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +25 -2
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +25 -2
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +119 -44
- package/dist/locales/en.js +1 -0
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +1 -0
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/groups/de.js +22 -4
- package/dist/locales/groups/de.js.map +1 -1
- package/dist/locales/groups/en.d.ts +20 -2
- package/dist/locales/groups/en.js +22 -4
- package/dist/locales/groups/en.js.map +1 -1
- package/dist/locales/groups/es.js +22 -4
- package/dist/locales/groups/es.js.map +1 -1
- package/dist/locales/groups/ja.js +22 -4
- package/dist/locales/groups/ja.js.map +1 -1
- package/dist/locales/groups/ko.js +22 -4
- package/dist/locales/groups/ko.js.map +1 -1
- package/dist/locales/groups/zh.js +22 -4
- package/dist/locales/groups/zh.js.map +1 -1
- package/dist/locales/ja.js +1 -0
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +1 -0
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/zh.js +1 -0
- package/dist/locales/zh.js.map +1 -1
- package/dist/react/discovery.d.ts +36 -17
- package/dist/react/discovery.js +305 -221
- package/dist/react/discovery.js.map +1 -1
- package/dist/react/groups.d.ts +19 -3
- package/dist/react/groups.js +49 -22
- package/dist/react/groups.js.map +1 -1
- package/dist/react/index.d.ts +7 -4
- package/dist/react/page-notices.d.ts +13 -0
- package/dist/react/page-notices.js +38 -0
- package/dist/react/page-notices.js.map +1 -0
- package/dist/react/post-reads.d.ts +23 -0
- package/dist/react/post-reads.js +90 -0
- package/dist/react/post-reads.js.map +1 -0
- package/dist/react/remembered-sort.d.ts +5 -0
- package/dist/react/remembered-sort.js +29 -0
- package/dist/react/remembered-sort.js.map +1 -0
- package/dist/react/resources.d.ts +3 -1
- package/dist/react/resources.js +6 -2
- package/dist/react/resources.js.map +1 -1
- package/dist/react/stats.d.ts +3 -7
- package/dist/react/stats.js +2 -6
- package/dist/react/stats.js.map +1 -1
- package/dist/react/use-engagement.d.ts +28 -0
- package/dist/react/use-engagement.js +226 -0
- package/dist/react/use-engagement.js.map +1 -0
- package/dist/react/use-resource.d.ts +4 -3
- package/dist/react/use-resource.js +2 -1
- package/dist/react/use-resource.js.map +1 -1
- package/dist/react.js +29 -26
- package/dist/styles.css +1 -1
- package/dist/ui/dialog.d.ts +2 -1
- package/dist/ui/dialog.js +23 -20
- package/dist/ui/dialog.js.map +1 -1
- package/dist/urls/index.d.ts +1 -1
- package/dist/urls.js +2 -2
- package/dist/urls.js.map +1 -1
- package/package.json +1 -1
- package/dist/components/admin/page-review.d.ts +0 -13
- package/dist/components/admin/page-review.js +0 -225
- package/dist/components/admin/page-review.js.map +0 -1
- package/dist/react/page-review.d.ts +0 -14
- package/dist/react/page-review.js +0 -37
- package/dist/react/page-review.js.map +0 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ContentQuery, SearchQuery } from '../../client/discovery.js';
|
|
2
|
+
import { CardListProps } from './grid.js';
|
|
3
|
+
/** ContentGrid's numbered pages: each page with the selection total. */
|
|
4
|
+
export declare function PagedContentGrid({ query, locked, ...p }: CardListProps & {
|
|
5
|
+
query: ContentQuery;
|
|
6
|
+
}): import('react').JSX.Element;
|
|
7
|
+
/** SearchResults' numbered pages; nothing until the query has text. */
|
|
8
|
+
export declare function PagedSearchResults({ query, ...p }: CardListProps & {
|
|
9
|
+
query: SearchQuery;
|
|
10
|
+
}): import('react').JSX.Element | null;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { useMessages as e } from "../../i18n/context.js";
|
|
2
|
+
import { ContentKitUiRoot as t } from "../../scope.js";
|
|
3
|
+
import { useContentsPage as n, useSearchPage as r } from "../../react/discovery.js";
|
|
4
|
+
import { Pagination as i } from "./pagination.js";
|
|
5
|
+
import { CardGrid as a, cardListMode as o } from "./grid.js";
|
|
6
|
+
import { useMemo as s, useRef as c, useState as l } from "react";
|
|
7
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
8
|
+
import { cn as f } from "cn";
|
|
9
|
+
//#region src/components/content/paged-grid.tsx
|
|
10
|
+
var p = 24;
|
|
11
|
+
function m(e, t) {
|
|
12
|
+
let n = JSON.stringify(t), [r, i] = l({
|
|
13
|
+
key: n,
|
|
14
|
+
page: 1
|
|
15
|
+
}), a = r.key === n ? r.page : 1;
|
|
16
|
+
return [Math.max(1, Math.floor(e.page ?? a)), (t) => {
|
|
17
|
+
e.page === void 0 && i({
|
|
18
|
+
key: n,
|
|
19
|
+
page: t
|
|
20
|
+
}), e.onPageChange?.(t);
|
|
21
|
+
}];
|
|
22
|
+
}
|
|
23
|
+
function h({ result: e, page: n, setPage: r, render: o, filterKinds: l, filterLanguage: m, ...h }) {
|
|
24
|
+
let g = c(null), _ = h.pageSize ?? p, v = e.summary?.total ?? 0, y = s(() => ({
|
|
25
|
+
...e,
|
|
26
|
+
summary: e.summary,
|
|
27
|
+
loadingMore: !1,
|
|
28
|
+
hasMore: !1,
|
|
29
|
+
loadMore: async () => {}
|
|
30
|
+
}), [e]), b = (e, t) => {
|
|
31
|
+
r(e), t && g.current?.scrollIntoView({
|
|
32
|
+
block: "start",
|
|
33
|
+
behavior: "smooth"
|
|
34
|
+
});
|
|
35
|
+
}, x = (t) => !e.loading || v > 0 ? /* @__PURE__ */ u(i, {
|
|
36
|
+
page: n,
|
|
37
|
+
total: v,
|
|
38
|
+
atLeast: e.summary?.truncated,
|
|
39
|
+
pageSize: _,
|
|
40
|
+
range: !t,
|
|
41
|
+
pageHref: h.pageHref,
|
|
42
|
+
onPageChange: (e) => b(e, t)
|
|
43
|
+
}) : null;
|
|
44
|
+
return /* @__PURE__ */ d(t, {
|
|
45
|
+
appearance: h.appearance,
|
|
46
|
+
className: f("grid gap-4 text-sm", h.className),
|
|
47
|
+
"data-ckui": "numbered-grid",
|
|
48
|
+
children: [
|
|
49
|
+
/* @__PURE__ */ u("div", {
|
|
50
|
+
ref: g,
|
|
51
|
+
className: "scroll-mt-20",
|
|
52
|
+
children: x(!1)
|
|
53
|
+
}),
|
|
54
|
+
/* @__PURE__ */ u(a, {
|
|
55
|
+
...h,
|
|
56
|
+
className: void 0,
|
|
57
|
+
list: y,
|
|
58
|
+
render: o,
|
|
59
|
+
filterKinds: l,
|
|
60
|
+
filterLanguage: m
|
|
61
|
+
}),
|
|
62
|
+
y.items.length > 0 && x(!0)
|
|
63
|
+
]
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function g({ query: e, locked: t = "show", ...r }) {
|
|
67
|
+
let { page: i, cursor: a, ...c } = e, l = s(() => ({
|
|
68
|
+
...c,
|
|
69
|
+
summary: !0,
|
|
70
|
+
limit: c.limit ?? r.pageSize ?? p,
|
|
71
|
+
mode: o(t, c.mode)
|
|
72
|
+
}), [
|
|
73
|
+
JSON.stringify(c),
|
|
74
|
+
r.pageSize,
|
|
75
|
+
t
|
|
76
|
+
]), [d, f] = m(r, l), g = n(l, d, { client: r.client });
|
|
77
|
+
return /* @__PURE__ */ u(h, {
|
|
78
|
+
...r,
|
|
79
|
+
pageSize: l.limit,
|
|
80
|
+
locked: t,
|
|
81
|
+
result: g,
|
|
82
|
+
page: d,
|
|
83
|
+
setPage: f,
|
|
84
|
+
render: JSON.stringify([l, d]),
|
|
85
|
+
filterKinds: e.kind ? [e.kind] : void 0,
|
|
86
|
+
filterLanguage: e.display_language ?? e.language
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function _({ query: t, ...n }) {
|
|
90
|
+
let { t: i } = e(), { page: a, cursor: c, ...l } = t, d = s(() => ({
|
|
91
|
+
...l,
|
|
92
|
+
summary: !0,
|
|
93
|
+
limit: l.limit ?? n.pageSize ?? p,
|
|
94
|
+
mode: o(n.locked, l.mode)
|
|
95
|
+
}), [
|
|
96
|
+
JSON.stringify(l),
|
|
97
|
+
n.pageSize,
|
|
98
|
+
n.locked
|
|
99
|
+
]), [f, g] = m(n, d), _ = r(d, f, { client: n.client });
|
|
100
|
+
return t.q.trim() ? /* @__PURE__ */ u(h, {
|
|
101
|
+
surface: "search",
|
|
102
|
+
empty: i("contents.noResults", { q: t.q.trim() }),
|
|
103
|
+
...n,
|
|
104
|
+
pageSize: d.limit,
|
|
105
|
+
result: _,
|
|
106
|
+
page: f,
|
|
107
|
+
setPage: g,
|
|
108
|
+
render: JSON.stringify([d, f]),
|
|
109
|
+
filterKinds: t.kind ? [t.kind] : void 0,
|
|
110
|
+
filterLanguage: t.display_language ?? t.language
|
|
111
|
+
}) : null;
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { g as PagedContentGrid, _ as PagedSearchResults };
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=paged-grid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paged-grid.js","names":[],"sources":["../../../src/components/content/paged-grid.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useMemo, useRef, useState } from \"react\";\nimport type { ContentQuery, SearchQuery } from \"../../client/discovery.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useContentsPage, useSearchPage, type UseContentsPage } from \"../../react/discovery.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { CardGrid, cardListMode, type CardListProps } from \"./grid.js\";\nimport { Pagination } from \"./pagination.js\";\n\nconst NUMBERED_PAGE_SIZE = 24;\n\n/** The page a list shows: the host's, else its own, which starts again when the query changes. */\nfunction usePage(p: CardListProps, query: unknown): [number, (page: number) => void] {\n const key = JSON.stringify(query);\n const [own, setOwn] = useState({ key, page: 1 });\n const ownPage = own.key === key ? own.page : 1;\n const page = Math.max(1, Math.floor(p.page ?? ownPage));\n return [\n page,\n (next) => {\n if (p.page === undefined) setOwn({ key, page: next });\n p.onPageChange?.(next);\n },\n ];\n}\n\nfunction NumberedCards({\n result,\n page,\n setPage,\n render,\n filterKinds,\n filterLanguage,\n ...p\n}: CardListProps & { result: UseContentsPage; page: number; setPage: (page: number) => void; render: string; filterKinds?: readonly string[]; filterLanguage?: string }) {\n const top = useRef<HTMLDivElement>(null);\n const size = p.pageSize ?? NUMBERED_PAGE_SIZE;\n const total = result.summary?.total ?? 0;\n const list = useMemo(\n () => ({ ...result, summary: result.summary, loadingMore: false, hasMore: false, loadMore: async () => {} }),\n [result],\n );\n const go = (next: number, scroll: boolean) => {\n setPage(next);\n if (scroll) top.current?.scrollIntoView({ block: \"start\", behavior: \"smooth\" });\n };\n const controls = (bottom: boolean) =>\n !result.loading || total > 0 ? (\n <Pagination page={page} total={total} atLeast={result.summary?.truncated} pageSize={size} range={!bottom} pageHref={p.pageHref} onPageChange={(n) => go(n, bottom)} />\n ) : null;\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"numbered-grid\">\n <div ref={top} className=\"scroll-mt-20\">\n {controls(false)}\n </div>\n <CardGrid {...p} className={undefined} list={list} render={render} filterKinds={filterKinds} filterLanguage={filterLanguage} />\n {list.items.length > 0 && controls(true)}\n </ContentKitUiRoot>\n );\n}\n\n/** ContentGrid's numbered pages: each page with the selection total. */\nexport function PagedContentGrid({ query, locked = \"show\", ...p }: CardListProps & { query: ContentQuery }) {\n const { page: _page, cursor: _cursor, ...base } = query;\n const q = useMemo<ContentQuery>(\n () => ({ ...base, summary: true, limit: base.limit ?? p.pageSize ?? NUMBERED_PAGE_SIZE, mode: cardListMode(locked, base.mode) }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [JSON.stringify(base), p.pageSize, locked],\n );\n const [page, setPage] = usePage(p, q);\n const result = useContentsPage(q, page, { client: p.client });\n return (\n <NumberedCards\n {...p}\n pageSize={q.limit}\n locked={locked}\n result={result}\n page={page}\n setPage={setPage}\n render={JSON.stringify([q, page])}\n filterKinds={query.kind ? [query.kind] : undefined}\n filterLanguage={query.display_language ?? query.language}\n />\n );\n}\n\n/** SearchResults' numbered pages; nothing until the query has text. */\nexport function PagedSearchResults({ query, ...p }: CardListProps & { query: SearchQuery }) {\n const { t } = useMessages();\n const { page: _page, cursor: _cursor, ...base } = query;\n const q = useMemo<SearchQuery>(\n () => ({ ...base, summary: true, limit: base.limit ?? p.pageSize ?? NUMBERED_PAGE_SIZE, mode: cardListMode(p.locked, base.mode) }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [JSON.stringify(base), p.pageSize, p.locked],\n );\n const [page, setPage] = usePage(p, q);\n const result = useSearchPage(q, page, { client: p.client });\n if (!query.q.trim()) return null;\n return (\n <NumberedCards\n surface=\"search\"\n empty={t(\"contents.noResults\", { q: query.q.trim() })}\n {...p}\n pageSize={q.limit}\n result={result}\n page={page}\n setPage={setPage}\n render={JSON.stringify([q, page])}\n filterKinds={query.kind ? [query.kind] : undefined}\n filterLanguage={query.display_language ?? query.language}\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface PaginationProps {
|
|
2
|
+
/** The current page, from 1. */
|
|
3
|
+
page: number;
|
|
4
|
+
/** Every item the pages hold; a lower bound with atLeast. */
|
|
5
|
+
total: number;
|
|
6
|
+
pageSize: number;
|
|
7
|
+
/** total is a lower bound: more pages may follow the last known one. */
|
|
8
|
+
atLeast?: boolean;
|
|
9
|
+
onPageChange: (page: number) => void;
|
|
10
|
+
/** Each page's link, for crawlers and opening in a new tab; clicks still call onPageChange. */
|
|
11
|
+
pageHref?: (page: number) => string;
|
|
12
|
+
/** Show "Showing X–Y of Z"; default true. */
|
|
13
|
+
range?: boolean;
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
/** The page numbers shown: first, last and two either side of the current one, with gaps. */
|
|
17
|
+
export declare function pageWindow(page: number, last: number): (number | "gap")[];
|
|
18
|
+
/** Numbered page controls with the shown range, for lists whose page lives in the host's URL. */
|
|
19
|
+
export declare function Pagination(p: PaginationProps): import('react').JSX.Element | null;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { useMessages as e } from "../../i18n/context.js";
|
|
2
|
+
import { buttonVariants as t } from "../../ui/button.js";
|
|
3
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
4
|
+
import { cn as i } from "cn";
|
|
5
|
+
import { ArrowLeft01Icon as a, ArrowRight01Icon as o } from "@hugeicons/core-free-icons";
|
|
6
|
+
import { HugeiconsIcon as s } from "@hugeicons/react";
|
|
7
|
+
//#region src/components/content/pagination.tsx
|
|
8
|
+
function c(e, t) {
|
|
9
|
+
let n = new Set([
|
|
10
|
+
1,
|
|
11
|
+
t,
|
|
12
|
+
e - 2,
|
|
13
|
+
e - 1,
|
|
14
|
+
e,
|
|
15
|
+
e + 1,
|
|
16
|
+
e + 2
|
|
17
|
+
].filter((e) => e >= 1 && e <= t)), r = [], i = 0;
|
|
18
|
+
for (let e of [...n].sort((e, t) => e - t)) e - i === 2 ? r.push(e - 1) : e - i > 2 && r.push("gap"), r.push(e), i = e;
|
|
19
|
+
return r;
|
|
20
|
+
}
|
|
21
|
+
function l(l) {
|
|
22
|
+
let u = e(), { t: d } = u, f = Math.max(1, l.pageSize), p = Math.max(1, Math.ceil(l.total / f)), m = Math.max(1, l.page), h = Math.max(p, m), g = (e) => new Intl.NumberFormat(u.language).format(e);
|
|
23
|
+
if (l.total === 0 && m === 1) return null;
|
|
24
|
+
let _ = Math.min(l.total, (m - 1) * f + 1), v = Math.min(l.total, m * f), y = (e, r, a = {}) => {
|
|
25
|
+
let o = i(t({
|
|
26
|
+
variant: a.current ? "default" : "ghost",
|
|
27
|
+
size: "sm"
|
|
28
|
+
}), "min-w-8 tabular-nums", a.disabled && "pointer-events-none opacity-50"), s = (t) => {
|
|
29
|
+
t.defaultPrevented || t.metaKey || t.ctrlKey || t.shiftKey || t.altKey || t.button !== 0 || (t.preventDefault(), !a.disabled && !a.current && l.onPageChange(e));
|
|
30
|
+
};
|
|
31
|
+
return l.pageHref && !a.disabled ? /* @__PURE__ */ n("a", {
|
|
32
|
+
href: l.pageHref(e),
|
|
33
|
+
className: o,
|
|
34
|
+
"aria-current": a.current ? "page" : void 0,
|
|
35
|
+
"aria-label": a.aria,
|
|
36
|
+
onClick: s,
|
|
37
|
+
children: r
|
|
38
|
+
}) : /* @__PURE__ */ n("button", {
|
|
39
|
+
type: "button",
|
|
40
|
+
className: o,
|
|
41
|
+
"aria-current": a.current ? "page" : void 0,
|
|
42
|
+
"aria-label": a.aria,
|
|
43
|
+
disabled: a.disabled,
|
|
44
|
+
onClick: s,
|
|
45
|
+
children: r
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
return /* @__PURE__ */ r("nav", {
|
|
49
|
+
"aria-label": d("listing.pages"),
|
|
50
|
+
className: i("flex flex-wrap items-center justify-between gap-2 text-sm", l.className),
|
|
51
|
+
"data-ckui": "pagination",
|
|
52
|
+
children: [l.range !== !1 && l.total > 0 && /* @__PURE__ */ n("p", {
|
|
53
|
+
className: "text-muted-foreground tabular-nums",
|
|
54
|
+
role: "status",
|
|
55
|
+
children: d(l.atLeast ? "listing.showingAtLeast" : "listing.showing", {
|
|
56
|
+
from: g(_),
|
|
57
|
+
to: g(v),
|
|
58
|
+
total: g(l.total)
|
|
59
|
+
})
|
|
60
|
+
}), (h > 1 || l.atLeast) && /* @__PURE__ */ r("div", {
|
|
61
|
+
className: "ms-auto flex flex-wrap items-center gap-1",
|
|
62
|
+
children: [
|
|
63
|
+
y(m - 1, /* @__PURE__ */ n(s, {
|
|
64
|
+
icon: a,
|
|
65
|
+
strokeWidth: 2
|
|
66
|
+
}), {
|
|
67
|
+
disabled: m <= 1,
|
|
68
|
+
aria: d("listing.previous")
|
|
69
|
+
}),
|
|
70
|
+
c(m, h).map((e, t) => e === "gap" ? /* @__PURE__ */ n("span", {
|
|
71
|
+
"aria-hidden": !0,
|
|
72
|
+
className: "px-1 text-muted-foreground",
|
|
73
|
+
children: "…"
|
|
74
|
+
}, `gap-${t}`) : /* @__PURE__ */ n("span", { children: y(e, g(e), {
|
|
75
|
+
current: e === m,
|
|
76
|
+
aria: d("listing.goToPage", { page: g(e) })
|
|
77
|
+
}) }, e)),
|
|
78
|
+
l.atLeast && /* @__PURE__ */ n("span", {
|
|
79
|
+
"aria-hidden": !0,
|
|
80
|
+
className: "px-1 text-muted-foreground",
|
|
81
|
+
children: "…"
|
|
82
|
+
}),
|
|
83
|
+
y(m + 1, /* @__PURE__ */ n(s, {
|
|
84
|
+
icon: o,
|
|
85
|
+
strokeWidth: 2
|
|
86
|
+
}), {
|
|
87
|
+
disabled: m >= h && !l.atLeast,
|
|
88
|
+
aria: d("listing.next")
|
|
89
|
+
})
|
|
90
|
+
]
|
|
91
|
+
})]
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
export { l as Pagination, c as pageWindow };
|
|
96
|
+
|
|
97
|
+
//# sourceMappingURL=pagination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pagination.js","names":[],"sources":["../../../src/components/content/pagination.tsx"],"sourcesContent":["import { ArrowLeft01Icon, ArrowRight01Icon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport type { MouseEvent, ReactNode } from \"react\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { buttonVariants } from \"#ckui/ui/button\";\n\nexport interface PaginationProps {\n /** The current page, from 1. */\n page: number;\n /** Every item the pages hold; a lower bound with atLeast. */\n total: number;\n pageSize: number;\n /** total is a lower bound: more pages may follow the last known one. */\n atLeast?: boolean;\n onPageChange: (page: number) => void;\n /** Each page's link, for crawlers and opening in a new tab; clicks still call onPageChange. */\n pageHref?: (page: number) => string;\n /** Show \"Showing X–Y of Z\"; default true. */\n range?: boolean;\n className?: string;\n}\n\n/** The page numbers shown: first, last and two either side of the current one, with gaps. */\nexport function pageWindow(page: number, last: number): (number | \"gap\")[] {\n const shown = new Set([1, last, page - 2, page - 1, page, page + 1, page + 2].filter((n) => n >= 1 && n <= last));\n const out: (number | \"gap\")[] = [];\n let previous = 0;\n for (const n of [...shown].sort((a, b) => a - b)) {\n if (n - previous === 2) out.push(n - 1);\n else if (n - previous > 2) out.push(\"gap\");\n out.push(n);\n previous = n;\n }\n return out;\n}\n\n/** Numbered page controls with the shown range, for lists whose page lives in the host's URL. */\nexport function Pagination(p: PaginationProps) {\n const m = useMessages();\n const { t } = m;\n const size = Math.max(1, p.pageSize);\n const known = Math.max(1, Math.ceil(p.total / size));\n const page = Math.max(1, p.page);\n const last = Math.max(known, page);\n const format = (n: number) => new Intl.NumberFormat(m.language).format(n);\n if (p.total === 0 && page === 1) return null;\n const from = Math.min(p.total, (page - 1) * size + 1);\n const to = Math.min(p.total, page * size);\n const control = (target: number, label: ReactNode, o: { current?: boolean; disabled?: boolean; aria?: string } = {}) => {\n const className = cn(buttonVariants({ variant: o.current ? \"default\" : \"ghost\", size: \"sm\" }), \"min-w-8 tabular-nums\", o.disabled && \"pointer-events-none opacity-50\");\n const go = (event: MouseEvent) => {\n if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0) return;\n event.preventDefault();\n if (!o.disabled && !o.current) p.onPageChange(target);\n };\n return p.pageHref && !o.disabled ? (\n <a href={p.pageHref(target)} className={className} aria-current={o.current ? \"page\" : undefined} aria-label={o.aria} onClick={go}>\n {label}\n </a>\n ) : (\n <button type=\"button\" className={className} aria-current={o.current ? \"page\" : undefined} aria-label={o.aria} disabled={o.disabled} onClick={go}>\n {label}\n </button>\n );\n };\n return (\n <nav aria-label={t(\"listing.pages\")} className={cn(\"flex flex-wrap items-center justify-between gap-2 text-sm\", p.className)} data-ckui=\"pagination\">\n {p.range !== false && p.total > 0 && (\n <p className=\"text-muted-foreground tabular-nums\" role=\"status\">\n {t(p.atLeast ? \"listing.showingAtLeast\" : \"listing.showing\", { from: format(from), to: format(to), total: format(p.total) })}\n </p>\n )}\n {(last > 1 || p.atLeast) && (\n <div className=\"ms-auto flex flex-wrap items-center gap-1\">\n {control(page - 1, <HugeiconsIcon icon={ArrowLeft01Icon} strokeWidth={2} />, { disabled: page <= 1, aria: t(\"listing.previous\") })}\n {pageWindow(page, last).map((n, i) =>\n n === \"gap\" ? (\n <span key={`gap-${i}`} aria-hidden className=\"px-1 text-muted-foreground\">\n …\n </span>\n ) : (\n <span key={n}>{control(n, format(n), { current: n === page, aria: t(\"listing.goToPage\", { page: format(n) }) })}</span>\n ),\n )}\n {p.atLeast && <span aria-hidden className=\"px-1 text-muted-foreground\">…</span>}\n {control(page + 1, <HugeiconsIcon icon={ArrowRight01Icon} strokeWidth={2} />, { disabled: page >= last && !p.atLeast, aria: t(\"listing.next\") })}\n </div>\n )}\n </nav>\n );\n}\n"],"mappings":"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"}
|
|
@@ -48,6 +48,15 @@ export interface VersionPickerProps {
|
|
|
48
48
|
className?: string;
|
|
49
49
|
appearance?: ContentKitUiAppearance;
|
|
50
50
|
}
|
|
51
|
+
export interface LanguageFallbackNoticeProps extends Omit<VersionPickerProps, "labels"> {
|
|
52
|
+
/** The page's language: the notice shows when the shown version doesn't serve it. */
|
|
53
|
+
language: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* A quiet note that the content isn't available in the page's language, which
|
|
57
|
+
* version it shows instead, and each language it does serve to switch to.
|
|
58
|
+
*/
|
|
59
|
+
export declare function LanguageFallbackNotice({ versions, value, onChange, language, className, appearance }: LanguageFallbackNoticeProps): import('react').JSX.Element | null;
|
|
51
60
|
/**
|
|
52
61
|
* Chooses among a content's versions: a language first when they differ in
|
|
53
62
|
* language, then its variants (traits, labels). The choice is the page's ?v=
|
|
@@ -145,7 +145,31 @@ function M(e, t, n) {
|
|
|
145
145
|
c
|
|
146
146
|
]);
|
|
147
147
|
}
|
|
148
|
-
function N({ versions: r, value: i, onChange: a,
|
|
148
|
+
function N({ versions: r, value: i, onChange: a, language: s, className: c, appearance: l }) {
|
|
149
|
+
let { t: u } = t(), d = e(), { current: f, languages: p, change: m } = M(r, i, a);
|
|
150
|
+
if (!f || !s || f.languages.includes(s)) return null;
|
|
151
|
+
let h = [...r].sort((e, t) => e.position - t.position);
|
|
152
|
+
return /* @__PURE__ */ w(n, {
|
|
153
|
+
appearance: l,
|
|
154
|
+
role: "note",
|
|
155
|
+
className: T("flex flex-wrap items-center gap-2 text-sm text-muted-foreground", c),
|
|
156
|
+
"data-ckui": "language-fallback",
|
|
157
|
+
children: [/* @__PURE__ */ C("span", { children: u("contents.notInLanguage", {
|
|
158
|
+
language: d(s),
|
|
159
|
+
shown: d(j(f))
|
|
160
|
+
}) }), p.length > 1 && p.map((e) => {
|
|
161
|
+
let t = h.find((t) => j(t) === e);
|
|
162
|
+
return /* @__PURE__ */ C(o, {
|
|
163
|
+
size: "xs",
|
|
164
|
+
variant: e === j(f) ? "secondary" : "outline",
|
|
165
|
+
"aria-pressed": e === j(f),
|
|
166
|
+
onClick: () => m(t),
|
|
167
|
+
children: d(e)
|
|
168
|
+
}, e);
|
|
169
|
+
})]
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function P({ versions: r, value: i, onChange: a, labels: f, className: p, appearance: m }) {
|
|
149
173
|
let { t: h } = t(), g = e(), { current: _, language: v, languages: y, variants: b, change: x } = M(r, i, a);
|
|
150
174
|
return r.length < 2 || !_ ? null : /* @__PURE__ */ w(n, {
|
|
151
175
|
appearance: m,
|
|
@@ -187,7 +211,7 @@ function N({ versions: r, value: i, onChange: a, labels: f, className: p, appear
|
|
|
187
211
|
})]
|
|
188
212
|
});
|
|
189
213
|
}
|
|
190
|
-
function
|
|
214
|
+
function F(n, r = {}) {
|
|
191
215
|
let { t: i } = t(), a = e(), { current: o, language: s, languages: c, variants: l, change: u } = M(n, r.value, r.onChange), { labels: d } = r;
|
|
192
216
|
return S(() => {
|
|
193
217
|
if (n.length < 2 || !o) return [];
|
|
@@ -231,6 +255,6 @@ function P(n, r = {}) {
|
|
|
231
255
|
]);
|
|
232
256
|
}
|
|
233
257
|
//#endregion
|
|
234
|
-
export { k as Paywall,
|
|
258
|
+
export { N as LanguageFallbackNotice, k as Paywall, P as VersionPicker, O as priceLabel, F as useVersionMenuItems };
|
|
235
259
|
|
|
236
260
|
//# sourceMappingURL=paywall.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paywall.js","names":[],"sources":["../../../src/components/content/paywall.tsx"],"sourcesContent":["import { LockIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useContext, useMemo, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Decision, Sale, Terms, Version } from \"../../client/generated/wire.js\";\nimport { useLanguageName, useMessages } from \"../../i18n/context.js\";\nimport type { Translator } from \"../../i18n/messages.js\";\nimport { ContentKitContext, useContentKitClient, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { editionOf, findVersion } from \"../../react/detail.js\";\nimport { useVersionParam } from \"../../react/location.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { contentError, ErrorLine, formatDate, useAction } from \"../social/parts.js\";\nimport { labelOf, type Labels } from \"../groups/parts.js\";\nimport type { PlayerMenuItem } from \"../video-player.js\";\n\n/** An offer's price and period in words: \"$2.99\", \"$4.99 / 30 days\", \"$1.99 for 48 hours\". */\nexport function priceLabel(m: Translator, terms: Terms): string {\n let price: string;\n try {\n price = new Intl.NumberFormat(m.language, { style: \"currency\", currency: terms.currency }).format(Number(terms.amount));\n } catch {\n price = `${terms.amount} ${terms.currency}`;\n }\n const period = (hours: number) => (hours % 24 === 0 ? m.plural(\"paywall.days\", hours / 24) : m.plural(\"paywall.hours\", hours));\n if (terms.renew_hours) return m.t(\"paywall.renews\", { price, period: period(terms.renew_hours) });\n if (terms.access_hours) return m.t(\"paywall.rents\", { price, period: period(terms.access_hours) });\n return price;\n}\n\nexport interface PaywallProps {\n /** The locked version's (or card's) decision. */\n decision: Decision;\n /**\n * Starts a purchase: the host creates the order through billing-ui\n * (createOrder with offer.price_id) and shows OrderCheckout; for a tier\n * (no offer), its subscription page. A free group offer joins here instead.\n */\n onCheckout: (o: { key: string; offer?: Sale }) => void;\n /** Tiers' names and prices from the host's catalog, by tier key. */\n tiers?: Readonly<Record<string, { label?: string; price?: string }>>;\n /** Asks a signed-out reader to sign in (they may hold it already); default the provider's. */\n onSignIn?: () => void;\n labels?: Labels;\n /** Below the offers: the host's own. */\n children?: ReactNode;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** Why content is locked and how to open it: its offers, the tiers that include it, previews and when it turns free. */\nexport function Paywall({ decision, onCheckout, tiers, onSignIn, labels, children, client: own, onError, className, appearance }: PaywallProps) {\n const m = useMessages();\n const { t } = m;\n const ctx = useContext(ContentKitContext);\n const client = useContentKitClient(own);\n const { kinds } = useKinds({ client: own });\n const join = useAction(\"membership.join\", onError);\n if (decision.allowed) return null;\n const signIn = onSignIn ?? ctx?.onSignIn;\n const offers = decision.offers ?? [];\n const sold = new Set(offers.map((o) => o.key));\n const siteTiers = kinds?.tiers ?? Object.keys(tiers ?? {});\n const tierKeys = (decision.sell ?? []).filter((k) => !sold.has(k) && (siteTiers.includes(k) || !!tiers?.[k]));\n const required = decision.requires;\n // A required key nothing here offers gets its own button; the others wait for it.\n const requires = required && !offers.some((o) => o.key === required) ? required : undefined;\n const waits = (key: string) => !!required && key !== required;\n const preview = decision.preview;\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-3 rounded-lg border border-border p-4 text-sm\", className)} data-ckui=\"paywall\">\n <p className=\"flex items-center gap-2 font-medium\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-4\" aria-hidden />\n {decision.unknown ? t(\"paywall.unknown\") : required ? t(\"paywall.requiresFirst\") : t(\"paywall.locked\")}\n </p>\n {preview?.pages ? <p className=\"text-muted-foreground\">{m.plural(\"paywall.previewPages\", preview.pages)}</p> : null}\n {preview?.segments ? <p className=\"text-muted-foreground\">{m.plural(\"paywall.previewSegments\", preview.segments)}</p> : null}\n {decision.free_after && <p className=\"text-muted-foreground\">{t(\"paywall.freeFrom\", { date: formatDate(m, decision.free_after, false) })}</p>}\n {!decision.unknown && (\n <div className=\"flex flex-wrap gap-2\" data-ckui=\"paywall-offers\">\n {requires && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => onCheckout({ key: requires })}>\n {t(\"paywall.joinFirst\")}\n </Button>\n )}\n {offers.map((o) =>\n o.target.kind === \"group\" && Number(o.terms.amount) === 0 ? (\n <Button\n key={o.key}\n size=\"sm\"\n disabled={waits(o.key) || join.pending}\n onClick={() => (ctx?.viewer === null && signIn ? signIn() : void join.run(() => client.offers.join(o.target.id)))}\n data-ckui=\"paywall-offer\"\n data-target=\"group\"\n data-free=\"\"\n >\n {t(\"paywall.joinFirst\")}\n </Button>\n ) : (\n <Button key={o.key} size=\"sm\" disabled={waits(o.key)} onClick={() => onCheckout({ key: o.key, offer: o })} data-ckui=\"paywall-offer\" data-target={o.target.kind}>\n {t(o.target.kind === \"group\" ? \"paywall.joinFor\" : o.target.kind === \"version\" ? \"paywall.unlockVersionFor\" : \"paywall.unlockFor\", { price: priceLabel(m, o.terms) })}\n </Button>\n ),\n )}\n {tierKeys.map((k) => (\n <Button key={k} size=\"sm\" variant={offers.length ? \"outline\" : \"default\"} onClick={() => onCheckout({ key: k })} data-ckui=\"paywall-tier\" data-tier={k}>\n {t(\"paywall.subscribe\", { tier: tiers?.[k]?.label ?? labelOf(labels, k) })}\n {tiers?.[k]?.price && <span className=\"ms-1 opacity-80\">{tiers[k]!.price}</span>}\n </Button>\n ))}\n </div>\n )}\n {join.error && <ErrorLine>{contentError(m, join.error)}</ErrorLine>}\n {ctx?.viewer === null && signIn && (\n <p className=\"text-muted-foreground\">\n {t(\"paywall.haveIt\")}{\" \"}\n <button type=\"button\" className=\"font-medium text-foreground underline-offset-4 hover:underline\" onClick={signIn}>\n {t(\"paywall.signIn\")}\n </button>\n </p>\n )}\n {children}\n </ContentKitUiRoot>\n );\n}\n\nexport interface VersionPickerProps {\n /** The content's live versions (useContentDetail().versions). */\n versions: readonly Version[];\n /** The version shown, by code or id; default the page's ?v=, else the reader's language's default. */\n value?: string | null;\n /** Receives the chosen version; default writes its code to the page's ?v=. */\n onChange?: (version: Version) => void;\n labels?: Labels;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** A version's name: its label, else its traits, else its languages. */\nfunction versionName(v: Version, languageName: (l: string) => string, labels?: Labels): string {\n return v.label || (v.traits.length ? v.traits.map((x) => labelOf(labels, x)).join(\" · \") : v.languages.map(languageName).join(\", \"));\n}\n\nconst primary = (v: Version) => v.languages[0] ?? \"\";\n\n/**\n * The shown version (value, else the page's ?v=, else the reader's\n * language's default), the versions' languages, the shown language's\n * variants, and the change handler (default: write ?v=).\n */\nfunction useChoices(versions: readonly Version[], value: string | null | undefined, onChange: ((v: Version) => void) | undefined) {\n const [param, setParam] = useVersionParam();\n const reader = useContext(ContentKitContext)?.language;\n const selected = value === undefined ? param : value;\n const change = useCallback((v: Version) => (onChange ? onChange(v) : setParam(v.code)), [onChange, setParam]);\n return useMemo(() => {\n const sorted = [...versions].sort((a, b) => a.position - b.position);\n const current = findVersion(versions, selected) ?? editionOf(sorted, reader ?? undefined);\n const language = current ? primary(current) : \"\";\n return { current, language, languages: [...new Set(sorted.map(primary))], variants: sorted.filter((v) => primary(v) === language), change };\n }, [versions, selected, reader, change]);\n}\n\n/**\n * Chooses among a content's versions: a language first when they differ in\n * language, then its variants (traits, labels). The choice is the page's ?v=\n * unless value and onChange say otherwise.\n */\nexport function VersionPicker({ versions, value, onChange, labels, className, appearance }: VersionPickerProps) {\n const { t } = useMessages();\n const languageName = useLanguageName();\n const { current, language, languages, variants, change } = useChoices(versions, value, onChange);\n if (versions.length < 2 || !current) return null;\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"flex flex-wrap items-center gap-2 text-sm\", className)} data-ckui=\"version-picker\">\n {languages.length > 1 && (\n <Select\n value={language}\n onValueChange={(l) => {\n const v = [...versions].sort((a, b) => a.position - b.position).find((x) => primary(x) === l);\n if (v) change(v);\n }}\n >\n <SelectTrigger size=\"sm\" aria-label={t(\"versions.language\")}>\n <SelectValue>{(l: string) => languageName(l)}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {languages.map((l) => (\n <SelectItem key={l} value={l}>\n {languageName(l)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {variants.length > 1 && (\n <div role=\"radiogroup\" aria-label={t(\"versions.version\")} className=\"flex flex-wrap gap-1\">\n {variants.map((v) => (\n <Button\n key={v.id}\n size=\"sm\"\n role=\"radio\"\n aria-checked={v.id === current.id}\n variant={v.id === current.id ? \"default\" : \"outline\"}\n onClick={() => change(v)}\n data-ckui=\"version-option\"\n data-locked={v.decision.allowed ? undefined : \"\"}\n >\n {!v.decision.allowed && <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-label={t(\"versions.locked\")} />}\n {versionName(v, languageName, labels)}\n </Button>\n ))}\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n\nexport interface VersionMenuOptions {\n /** The version shown, by code or id; default the page's ?v=, else the reader's language's default. */\n value?: string | null;\n /** Receives the chosen version; default writes its code to the page's ?v=. */\n onChange?: (version: Version) => void;\n labels?: Labels;\n}\n\n/** VersionPicker as VideoPlayer settings entries (menuItems), for a watch page. */\nexport function useVersionMenuItems(versions: readonly Version[], o: VersionMenuOptions = {}): PlayerMenuItem[] {\n const { t } = useMessages();\n const languageName = useLanguageName();\n const { current, language, languages, variants, change } = useChoices(versions, o.value, o.onChange);\n const { labels } = o;\n return useMemo(() => {\n if (versions.length < 2 || !current) return [];\n const out: PlayerMenuItem[] = [];\n if (languages.length > 1)\n out.push({\n label: t(\"versions.language\"),\n value: language,\n options: languages.map((l) => ({ value: l, label: languageName(l) })),\n onChange: (l) => {\n const v = versions.find((x) => primary(x) === l);\n if (v) change(v);\n },\n });\n if (variants.length > 1)\n out.push({\n label: t(\"versions.version\"),\n value: current.id,\n options: variants.map((v) => {\n const name = versionName(v, languageName, labels);\n return { value: v.id, label: v.decision.allowed ? name : t(\"versions.lockedOption\", { name }) };\n }),\n onChange: (id) => {\n const v = versions.find((x) => x.id === id);\n if (v) change(v);\n },\n });\n return out;\n }, [versions, current, language, languages, variants, change, labels, t, languageName]);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"paywall.js","names":[],"sources":["../../../src/components/content/paywall.tsx"],"sourcesContent":["import { LockIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useContext, useMemo, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Decision, Sale, Terms, Version } from \"../../client/generated/wire.js\";\nimport { useLanguageName, useMessages } from \"../../i18n/context.js\";\nimport type { Translator } from \"../../i18n/messages.js\";\nimport { ContentKitContext, useContentKitClient, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { editionOf, findVersion } from \"../../react/detail.js\";\nimport { useVersionParam } from \"../../react/location.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { contentError, ErrorLine, formatDate, useAction } from \"../social/parts.js\";\nimport { labelOf, type Labels } from \"../groups/parts.js\";\nimport type { PlayerMenuItem } from \"../video-player.js\";\n\n/** An offer's price and period in words: \"$2.99\", \"$4.99 / 30 days\", \"$1.99 for 48 hours\". */\nexport function priceLabel(m: Translator, terms: Terms): string {\n let price: string;\n try {\n price = new Intl.NumberFormat(m.language, { style: \"currency\", currency: terms.currency }).format(Number(terms.amount));\n } catch {\n price = `${terms.amount} ${terms.currency}`;\n }\n const period = (hours: number) => (hours % 24 === 0 ? m.plural(\"paywall.days\", hours / 24) : m.plural(\"paywall.hours\", hours));\n if (terms.renew_hours) return m.t(\"paywall.renews\", { price, period: period(terms.renew_hours) });\n if (terms.access_hours) return m.t(\"paywall.rents\", { price, period: period(terms.access_hours) });\n return price;\n}\n\nexport interface PaywallProps {\n /** The locked version's (or card's) decision. */\n decision: Decision;\n /**\n * Starts a purchase: the host creates the order through billing-ui\n * (createOrder with offer.price_id) and shows OrderCheckout; for a tier\n * (no offer), its subscription page. A free group offer joins here instead.\n */\n onCheckout: (o: { key: string; offer?: Sale }) => void;\n /** Tiers' names and prices from the host's catalog, by tier key. */\n tiers?: Readonly<Record<string, { label?: string; price?: string }>>;\n /** Asks a signed-out reader to sign in (they may hold it already); default the provider's. */\n onSignIn?: () => void;\n labels?: Labels;\n /** Below the offers: the host's own. */\n children?: ReactNode;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** Why content is locked and how to open it: its offers, the tiers that include it, previews and when it turns free. */\nexport function Paywall({ decision, onCheckout, tiers, onSignIn, labels, children, client: own, onError, className, appearance }: PaywallProps) {\n const m = useMessages();\n const { t } = m;\n const ctx = useContext(ContentKitContext);\n const client = useContentKitClient(own);\n const { kinds } = useKinds({ client: own });\n const join = useAction(\"membership.join\", onError);\n if (decision.allowed) return null;\n const signIn = onSignIn ?? ctx?.onSignIn;\n const offers = decision.offers ?? [];\n const sold = new Set(offers.map((o) => o.key));\n const siteTiers = kinds?.tiers ?? Object.keys(tiers ?? {});\n const tierKeys = (decision.sell ?? []).filter((k) => !sold.has(k) && (siteTiers.includes(k) || !!tiers?.[k]));\n const required = decision.requires;\n // A required key nothing here offers gets its own button; the others wait for it.\n const requires = required && !offers.some((o) => o.key === required) ? required : undefined;\n const waits = (key: string) => !!required && key !== required;\n const preview = decision.preview;\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-3 rounded-lg border border-border p-4 text-sm\", className)} data-ckui=\"paywall\">\n <p className=\"flex items-center gap-2 font-medium\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-4\" aria-hidden />\n {decision.unknown ? t(\"paywall.unknown\") : required ? t(\"paywall.requiresFirst\") : t(\"paywall.locked\")}\n </p>\n {preview?.pages ? <p className=\"text-muted-foreground\">{m.plural(\"paywall.previewPages\", preview.pages)}</p> : null}\n {preview?.segments ? <p className=\"text-muted-foreground\">{m.plural(\"paywall.previewSegments\", preview.segments)}</p> : null}\n {decision.free_after && <p className=\"text-muted-foreground\">{t(\"paywall.freeFrom\", { date: formatDate(m, decision.free_after, false) })}</p>}\n {!decision.unknown && (\n <div className=\"flex flex-wrap gap-2\" data-ckui=\"paywall-offers\">\n {requires && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => onCheckout({ key: requires })}>\n {t(\"paywall.joinFirst\")}\n </Button>\n )}\n {offers.map((o) =>\n o.target.kind === \"group\" && Number(o.terms.amount) === 0 ? (\n <Button\n key={o.key}\n size=\"sm\"\n disabled={waits(o.key) || join.pending}\n onClick={() => (ctx?.viewer === null && signIn ? signIn() : void join.run(() => client.offers.join(o.target.id)))}\n data-ckui=\"paywall-offer\"\n data-target=\"group\"\n data-free=\"\"\n >\n {t(\"paywall.joinFirst\")}\n </Button>\n ) : (\n <Button key={o.key} size=\"sm\" disabled={waits(o.key)} onClick={() => onCheckout({ key: o.key, offer: o })} data-ckui=\"paywall-offer\" data-target={o.target.kind}>\n {t(o.target.kind === \"group\" ? \"paywall.joinFor\" : o.target.kind === \"version\" ? \"paywall.unlockVersionFor\" : \"paywall.unlockFor\", { price: priceLabel(m, o.terms) })}\n </Button>\n ),\n )}\n {tierKeys.map((k) => (\n <Button key={k} size=\"sm\" variant={offers.length ? \"outline\" : \"default\"} onClick={() => onCheckout({ key: k })} data-ckui=\"paywall-tier\" data-tier={k}>\n {t(\"paywall.subscribe\", { tier: tiers?.[k]?.label ?? labelOf(labels, k) })}\n {tiers?.[k]?.price && <span className=\"ms-1 opacity-80\">{tiers[k]!.price}</span>}\n </Button>\n ))}\n </div>\n )}\n {join.error && <ErrorLine>{contentError(m, join.error)}</ErrorLine>}\n {ctx?.viewer === null && signIn && (\n <p className=\"text-muted-foreground\">\n {t(\"paywall.haveIt\")}{\" \"}\n <button type=\"button\" className=\"font-medium text-foreground underline-offset-4 hover:underline\" onClick={signIn}>\n {t(\"paywall.signIn\")}\n </button>\n </p>\n )}\n {children}\n </ContentKitUiRoot>\n );\n}\n\nexport interface VersionPickerProps {\n /** The content's live versions (useContentDetail().versions). */\n versions: readonly Version[];\n /** The version shown, by code or id; default the page's ?v=, else the reader's language's default. */\n value?: string | null;\n /** Receives the chosen version; default writes its code to the page's ?v=. */\n onChange?: (version: Version) => void;\n labels?: Labels;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** A version's name: its label, else its traits, else its languages. */\nfunction versionName(v: Version, languageName: (l: string) => string, labels?: Labels): string {\n return v.label || (v.traits.length ? v.traits.map((x) => labelOf(labels, x)).join(\" · \") : v.languages.map(languageName).join(\", \"));\n}\n\nconst primary = (v: Version) => v.languages[0] ?? \"\";\n\n/**\n * The shown version (value, else the page's ?v=, else the reader's\n * language's default), the versions' languages, the shown language's\n * variants, and the change handler (default: write ?v=).\n */\nfunction useChoices(versions: readonly Version[], value: string | null | undefined, onChange: ((v: Version) => void) | undefined) {\n const [param, setParam] = useVersionParam();\n const reader = useContext(ContentKitContext)?.language;\n const selected = value === undefined ? param : value;\n const change = useCallback((v: Version) => (onChange ? onChange(v) : setParam(v.code)), [onChange, setParam]);\n return useMemo(() => {\n const sorted = [...versions].sort((a, b) => a.position - b.position);\n const current = findVersion(versions, selected) ?? editionOf(sorted, reader ?? undefined);\n const language = current ? primary(current) : \"\";\n return { current, language, languages: [...new Set(sorted.map(primary))], variants: sorted.filter((v) => primary(v) === language), change };\n }, [versions, selected, reader, change]);\n}\n\nexport interface LanguageFallbackNoticeProps extends Omit<VersionPickerProps, \"labels\"> {\n /** The page's language: the notice shows when the shown version doesn't serve it. */\n language: string;\n}\n\n/**\n * A quiet note that the content isn't available in the page's language, which\n * version it shows instead, and each language it does serve to switch to.\n */\nexport function LanguageFallbackNotice({ versions, value, onChange, language, className, appearance }: LanguageFallbackNoticeProps) {\n const { t } = useMessages();\n const languageName = useLanguageName();\n const { current, languages, change } = useChoices(versions, value, onChange);\n if (!current || !language || current.languages.includes(language)) return null;\n const sorted = [...versions].sort((a, b) => a.position - b.position);\n return (\n <ContentKitUiRoot appearance={appearance} role=\"note\" className={cn(\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\", className)} data-ckui=\"language-fallback\">\n <span>{t(\"contents.notInLanguage\", { language: languageName(language), shown: languageName(primary(current)) })}</span>\n {languages.length > 1 &&\n languages.map((l) => {\n const v = sorted.find((x) => primary(x) === l)!;\n return (\n <Button key={l} size=\"xs\" variant={l === primary(current) ? \"secondary\" : \"outline\"} aria-pressed={l === primary(current)} onClick={() => change(v)}>\n {languageName(l)}\n </Button>\n );\n })}\n </ContentKitUiRoot>\n );\n}\n\n/**\n * Chooses among a content's versions: a language first when they differ in\n * language, then its variants (traits, labels). The choice is the page's ?v=\n * unless value and onChange say otherwise.\n */\nexport function VersionPicker({ versions, value, onChange, labels, className, appearance }: VersionPickerProps) {\n const { t } = useMessages();\n const languageName = useLanguageName();\n const { current, language, languages, variants, change } = useChoices(versions, value, onChange);\n if (versions.length < 2 || !current) return null;\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"flex flex-wrap items-center gap-2 text-sm\", className)} data-ckui=\"version-picker\">\n {languages.length > 1 && (\n <Select\n value={language}\n onValueChange={(l) => {\n const v = [...versions].sort((a, b) => a.position - b.position).find((x) => primary(x) === l);\n if (v) change(v);\n }}\n >\n <SelectTrigger size=\"sm\" aria-label={t(\"versions.language\")}>\n <SelectValue>{(l: string) => languageName(l)}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {languages.map((l) => (\n <SelectItem key={l} value={l}>\n {languageName(l)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {variants.length > 1 && (\n <div role=\"radiogroup\" aria-label={t(\"versions.version\")} className=\"flex flex-wrap gap-1\">\n {variants.map((v) => (\n <Button\n key={v.id}\n size=\"sm\"\n role=\"radio\"\n aria-checked={v.id === current.id}\n variant={v.id === current.id ? \"default\" : \"outline\"}\n onClick={() => change(v)}\n data-ckui=\"version-option\"\n data-locked={v.decision.allowed ? undefined : \"\"}\n >\n {!v.decision.allowed && <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-label={t(\"versions.locked\")} />}\n {versionName(v, languageName, labels)}\n </Button>\n ))}\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n\nexport interface VersionMenuOptions {\n /** The version shown, by code or id; default the page's ?v=, else the reader's language's default. */\n value?: string | null;\n /** Receives the chosen version; default writes its code to the page's ?v=. */\n onChange?: (version: Version) => void;\n labels?: Labels;\n}\n\n/** VersionPicker as VideoPlayer settings entries (menuItems), for a watch page. */\nexport function useVersionMenuItems(versions: readonly Version[], o: VersionMenuOptions = {}): PlayerMenuItem[] {\n const { t } = useMessages();\n const languageName = useLanguageName();\n const { current, language, languages, variants, change } = useChoices(versions, o.value, o.onChange);\n const { labels } = o;\n return useMemo(() => {\n if (versions.length < 2 || !current) return [];\n const out: PlayerMenuItem[] = [];\n if (languages.length > 1)\n out.push({\n label: t(\"versions.language\"),\n value: language,\n options: languages.map((l) => ({ value: l, label: languageName(l) })),\n onChange: (l) => {\n const v = versions.find((x) => primary(x) === l);\n if (v) change(v);\n },\n });\n if (variants.length > 1)\n out.push({\n label: t(\"versions.version\"),\n value: current.id,\n options: variants.map((v) => {\n const name = versionName(v, languageName, labels);\n return { value: v.id, label: v.decision.allowed ? name : t(\"versions.lockedOption\", { name }) };\n }),\n onChange: (id) => {\n const v = versions.find((x) => x.id === id);\n if (v) change(v);\n },\n });\n return out;\n }, [versions, current, language, languages, variants, change, labels, t, languageName]);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ContentKitUiAppearance } from '../../appearance.js';
|
|
3
|
+
import { ContentKitClient } from '../../client/client.js';
|
|
4
|
+
import { Period } from '../../client/discovery.js';
|
|
5
|
+
import { Card } from '../../client/generated/wire.js';
|
|
6
|
+
import { PostCardProps } from './card.js';
|
|
7
|
+
export interface PostListProps {
|
|
8
|
+
/** The post kind. */
|
|
9
|
+
kind: string;
|
|
10
|
+
/** Display language for titles, excerpts and paths; default the provider's. */
|
|
11
|
+
language?: string;
|
|
12
|
+
href?: (card: Card) => string;
|
|
13
|
+
/** Posts per page; default 10. */
|
|
14
|
+
pageSize?: number;
|
|
15
|
+
/** Trending window for the sidebar, or false to leave it out; default 30d. */
|
|
16
|
+
trending?: Period | false;
|
|
17
|
+
/** Posts per sidebar section; default 5, 0 hides the sidebar. */
|
|
18
|
+
sidebarSize?: number;
|
|
19
|
+
/** Excerpt length in characters; default 220. */
|
|
20
|
+
excerptLength?: number;
|
|
21
|
+
/** Signed in, posts unread within this many days are marked; default 30. */
|
|
22
|
+
unreadDays?: number;
|
|
23
|
+
tiers?: readonly string[];
|
|
24
|
+
labels?: PostCardProps["labels"];
|
|
25
|
+
empty?: ReactNode;
|
|
26
|
+
client?: ContentKitClient;
|
|
27
|
+
appearance?: ContentKitUiAppearance;
|
|
28
|
+
className?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Posts newest first with cover, date, excerpt and an unread mark, beside a
|
|
32
|
+
* Newest/Trending sidebar. Trending is the kind's popular window; it never
|
|
33
|
+
* counts views.
|
|
34
|
+
*/
|
|
35
|
+
export declare function PostList({ kind, language, href, pageSize, trending, sidebarSize, excerptLength, unreadDays, tiers, labels, empty, client, appearance, className }: PostListProps): import('react').JSX.Element;
|
|
36
|
+
/** At most length characters, cut at a word boundary with an ellipsis. */
|
|
37
|
+
export declare function excerpt(text: string, length: number): string;
|