@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.
- package/README.md +6 -6
- package/dist/client/beacons.js +4 -1
- package/dist/client/beacons.js.map +1 -1
- package/dist/client/contents.d.ts +9 -1
- package/dist/client/contents.js +73 -55
- package/dist/client/contents.js.map +1 -1
- package/dist/client/discovery.d.ts +6 -2
- package/dist/client/discovery.js +1 -1
- package/dist/client/discovery.js.map +1 -1
- package/dist/client/generated/error-codes.d.ts +1 -1
- package/dist/client/generated/error-codes.js +4 -0
- package/dist/client/generated/error-codes.js.map +1 -1
- package/dist/client/generated/routes.d.ts +43 -30
- package/dist/client/generated/routes.js +70 -63
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +36 -74
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- 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/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 +739 -380
- 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/reader-content-editor.js +537 -500
- 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 +300 -202
- 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 +14 -12
- 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 +1 -1
- package/dist/components/content/favorites.js +36 -22
- package/dist/components/content/favorites.js.map +1 -1
- package/dist/components/content/grid.d.ts +11 -4
- package/dist/components/content/grid.js +52 -43
- package/dist/components/content/grid.js.map +1 -1
- package/dist/components/content/history.js +83 -63
- package/dist/components/content/history.js.map +1 -1
- package/dist/components/content/index.d.ts +5 -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/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 +418 -230
- package/dist/components/media-folder-editor.js.map +1 -1
- package/dist/components/social/poll.js +57 -48
- package/dist/components/social/poll.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/index.d.ts +1 -1
- package/dist/index.js +41 -37
- package/dist/locales/admin/de.js +78 -45
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +74 -41
- package/dist/locales/admin/en.js +78 -45
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +78 -45
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +78 -45
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +78 -45
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +78 -45
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/de.js +2 -0
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/discovery/de.js +25 -1
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +24 -0
- package/dist/locales/discovery/en.js +25 -1
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +25 -1
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +25 -1
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +25 -1
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +25 -1
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +120 -43
- package/dist/locales/en.js +2 -0
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +2 -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 +2 -0
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +2 -0
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/zh.js +2 -0
- package/dist/locales/zh.js.map +1 -1
- package/dist/react/contents.js +1 -1
- package/dist/react/contents.js.map +1 -1
- package/dist/react/discovery.d.ts +16 -0
- package/dist/react/discovery.js +69 -37
- 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 +6 -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.js +2 -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 +2 -0
- package/dist/react/use-engagement.js +40 -34
- package/dist/react/use-engagement.js.map +1 -1
- package/dist/react.js +29 -27
- package/dist/styles.css +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
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import { ContentQuery, ListSort } from '../../client/discovery.js';
|
|
2
2
|
import { Translator } from '../../i18n/messages.js';
|
|
3
3
|
import { CardListProps, LockedCards } from './grid.js';
|
|
4
|
-
/** A sort's name: "Newest", "Popular, this week". */
|
|
5
|
-
export declare function sortLabel(m: Translator, sort: ListSort): string;
|
|
4
|
+
/** A sort's name: "Newest", "Popular, this week", "Best match". */
|
|
5
|
+
export declare function sortLabel(m: Translator, sort: ListSort | "relevance"): string;
|
|
6
|
+
export interface SortMenuProps<S extends string> {
|
|
7
|
+
value: S;
|
|
8
|
+
/** The sorts offered, in menu order. */
|
|
9
|
+
sorts: readonly S[];
|
|
10
|
+
onChange: (sort: S) => void;
|
|
11
|
+
/** A sort's name; default the list sorts' names. */
|
|
12
|
+
label?: (sort: S) => string;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
/** A list's sort control; hosts keep the choice (URL, remembered per page type). */
|
|
16
|
+
export declare function SortMenu<S extends string>({ value, sorts, onChange, label, className }: SortMenuProps<S>): import('react').JSX.Element;
|
|
6
17
|
export interface ContentListingProps extends CardListProps {
|
|
7
18
|
/** The listed kind: its version traits and filterable attributes become filters, its sort the default. */
|
|
8
19
|
kind?: string;
|
|
9
|
-
/** The sort and filters; with onQueryChange the host keeps them (e.g. in the URL). Other fields (groups, a tag page's) pass through. */
|
|
20
|
+
/** The sort and filters; with onQueryChange the host keeps them (e.g. in the URL). Other fields (groups, a tag page's) pass through. A changed query starts again at page 1: a host keeping the page drops it with the query. */
|
|
10
21
|
query?: ContentQuery;
|
|
11
22
|
onQueryChange?: (query: ContentQuery) => void;
|
|
12
23
|
/** The sorts offered; default newest, popular (week, month, all time), top rated, most favorited, title, oldest. */
|
|
@@ -26,26 +26,43 @@ function b(e, t) {
|
|
|
26
26
|
period: e.t(`listing.period_${r}`)
|
|
27
27
|
}) : i;
|
|
28
28
|
}
|
|
29
|
-
function x({
|
|
30
|
-
let
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
function x({ value: e, sorts: n, onChange: r, label: i, className: u }) {
|
|
30
|
+
let d = t(), f = i ?? ((e) => b(d, e));
|
|
31
|
+
return /* @__PURE__ */ g(a, {
|
|
32
|
+
value: e,
|
|
33
|
+
onValueChange: (e) => r(e),
|
|
34
|
+
children: [/* @__PURE__ */ h(c, {
|
|
35
|
+
size: "sm",
|
|
36
|
+
"aria-label": d.t("listing.sort"),
|
|
37
|
+
className: u,
|
|
38
|
+
"data-ckui": "listing-sort",
|
|
39
|
+
children: /* @__PURE__ */ h(l, { children: (e) => f(e) })
|
|
40
|
+
}), /* @__PURE__ */ h(o, { children: n.map((e) => /* @__PURE__ */ h(s, {
|
|
41
|
+
value: e,
|
|
42
|
+
children: f(e)
|
|
43
|
+
}, e)) })]
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function S({ kind: b, query: S, onQueryChange: C, sorts: w = v, languages: T, filters: E, ...D }) {
|
|
47
|
+
let { t: O } = t(), k = e(), { kinds: A } = r({ client: D.client }), [j, M] = m({}), N = S ?? j, P = f((e) => {
|
|
48
|
+
S === void 0 && M(e), C?.(e);
|
|
49
|
+
}, [S, C]), F = b ? A?.kinds[b] : void 0, I = N.sort ?? F?.sort ?? w[0] ?? "newest", L = (e) => !E || E.includes(e), R = (F?.versions.traits ?? []).filter(L), z = Object.entries(F?.attributes ?? {}).filter(([e, t]) => t.filterable && (t.values?.length || t.type === "bool") && L(e)), B = (T ?? (A?.languages.length ?? 0) > 1) && !!A?.languages.length, V = (e) => N.attribute?.find((t) => t.startsWith(`${e}:`))?.slice(e.length + 1), H = (e, t) => P({
|
|
50
|
+
...N,
|
|
51
|
+
attribute: [...(N.attribute ?? []).filter((t) => !t.startsWith(`${e}:`)), ...t === void 0 ? [] : [`${e}:${t}`]]
|
|
52
|
+
}), U = (e) => {
|
|
53
|
+
let t = N.traits?.includes(e);
|
|
54
|
+
P({
|
|
55
|
+
...N,
|
|
56
|
+
traits: t ? N.traits.filter((t) => t !== e) : [...N.traits ?? [], e]
|
|
40
57
|
});
|
|
41
|
-
},
|
|
42
|
-
...
|
|
43
|
-
kind:
|
|
44
|
-
sort:
|
|
58
|
+
}, W = !!(N.language || N.traits?.length || N.attribute?.length), G = p(() => ({
|
|
59
|
+
...N,
|
|
60
|
+
kind: b ?? N.kind,
|
|
61
|
+
sort: I
|
|
45
62
|
}), [
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
N,
|
|
64
|
+
b,
|
|
65
|
+
I
|
|
49
66
|
]);
|
|
50
67
|
return /* @__PURE__ */ g(n, {
|
|
51
68
|
appearance: D.appearance,
|
|
@@ -53,55 +70,47 @@ function x({ kind: x, query: S, onQueryChange: C, sorts: w = v, languages: T, fi
|
|
|
53
70
|
"data-ckui": "content-listing",
|
|
54
71
|
children: [/* @__PURE__ */ g("div", {
|
|
55
72
|
role: "toolbar",
|
|
56
|
-
"aria-label":
|
|
73
|
+
"aria-label": O("listing.filters"),
|
|
57
74
|
className: "flex flex-wrap items-center gap-2",
|
|
58
75
|
children: [
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
-
value:
|
|
61
|
-
|
|
62
|
-
|
|
76
|
+
/* @__PURE__ */ h(x, {
|
|
77
|
+
value: I,
|
|
78
|
+
sorts: w,
|
|
79
|
+
onChange: (e) => P({
|
|
80
|
+
...N,
|
|
63
81
|
sort: e
|
|
64
|
-
})
|
|
65
|
-
children: [/* @__PURE__ */ h(c, {
|
|
66
|
-
size: "sm",
|
|
67
|
-
"aria-label": k("listing.sort"),
|
|
68
|
-
"data-ckui": "listing-sort",
|
|
69
|
-
children: /* @__PURE__ */ h(l, { children: (e) => b(O, e) })
|
|
70
|
-
}), /* @__PURE__ */ h(o, { children: w.map((e) => /* @__PURE__ */ h(s, {
|
|
71
|
-
value: e,
|
|
72
|
-
children: b(O, e)
|
|
73
|
-
}, e)) })]
|
|
82
|
+
})
|
|
74
83
|
}),
|
|
75
|
-
|
|
76
|
-
value:
|
|
77
|
-
onValueChange: (e) =>
|
|
78
|
-
...
|
|
84
|
+
B && /* @__PURE__ */ g(a, {
|
|
85
|
+
value: N.language ?? y,
|
|
86
|
+
onValueChange: (e) => P({
|
|
87
|
+
...N,
|
|
79
88
|
language: e === y ? void 0 : e
|
|
80
89
|
}),
|
|
81
90
|
children: [/* @__PURE__ */ h(c, {
|
|
82
91
|
size: "sm",
|
|
83
|
-
"aria-label":
|
|
92
|
+
"aria-label": O("listing.language"),
|
|
84
93
|
"data-ckui": "listing-language",
|
|
85
|
-
children: /* @__PURE__ */ h(l, { children: (e) => e === y ?
|
|
94
|
+
children: /* @__PURE__ */ h(l, { children: (e) => e === y ? O("listing.anyLanguage") : k(e) })
|
|
86
95
|
}), /* @__PURE__ */ g(o, { children: [/* @__PURE__ */ h(s, {
|
|
87
96
|
value: y,
|
|
88
|
-
children:
|
|
89
|
-
}),
|
|
97
|
+
children: O("listing.anyLanguage")
|
|
98
|
+
}), A.languages.map((e) => /* @__PURE__ */ h(s, {
|
|
90
99
|
value: e,
|
|
91
|
-
children:
|
|
100
|
+
children: k(e)
|
|
92
101
|
}, e))] })]
|
|
93
102
|
}),
|
|
94
|
-
|
|
95
|
-
let n =
|
|
103
|
+
z.map(([e, t]) => {
|
|
104
|
+
let n = V(e);
|
|
96
105
|
return t.type === "bool" ? /* @__PURE__ */ h(i, {
|
|
97
106
|
size: "sm",
|
|
98
107
|
variant: n === "true" ? "default" : "outline",
|
|
99
108
|
"aria-pressed": n === "true",
|
|
100
|
-
onClick: () =>
|
|
109
|
+
onClick: () => H(e, n === "true" ? void 0 : "true"),
|
|
101
110
|
children: u(D.labels, e)
|
|
102
111
|
}, e) : /* @__PURE__ */ g(a, {
|
|
103
112
|
value: n ?? y,
|
|
104
|
-
onValueChange: (t) =>
|
|
113
|
+
onValueChange: (t) => H(e, t === y ? void 0 : t),
|
|
105
114
|
children: [/* @__PURE__ */ h(c, {
|
|
106
115
|
size: "sm",
|
|
107
116
|
"aria-label": u(D.labels, e),
|
|
@@ -110,41 +119,41 @@ function x({ kind: x, query: S, onQueryChange: C, sorts: w = v, languages: T, fi
|
|
|
110
119
|
children: /* @__PURE__ */ h(l, { children: (t) => t === y ? u(D.labels, e) : u(D.labels, t) })
|
|
111
120
|
}), /* @__PURE__ */ g(o, { children: [/* @__PURE__ */ h(s, {
|
|
112
121
|
value: y,
|
|
113
|
-
children:
|
|
122
|
+
children: O("listing.any")
|
|
114
123
|
}), t.values.map((e) => /* @__PURE__ */ h(s, {
|
|
115
124
|
value: e,
|
|
116
125
|
children: u(D.labels, e)
|
|
117
126
|
}, e))] })]
|
|
118
127
|
}, e);
|
|
119
128
|
}),
|
|
120
|
-
|
|
121
|
-
let t = !!
|
|
129
|
+
R.map((e) => {
|
|
130
|
+
let t = !!N.traits?.includes(e);
|
|
122
131
|
return /* @__PURE__ */ h(i, {
|
|
123
132
|
size: "sm",
|
|
124
133
|
variant: t ? "default" : "outline",
|
|
125
134
|
"aria-pressed": t,
|
|
126
|
-
onClick: () =>
|
|
135
|
+
onClick: () => U(e),
|
|
127
136
|
"data-ckui": "listing-trait",
|
|
128
137
|
children: u(D.labels, e)
|
|
129
138
|
}, e);
|
|
130
139
|
}),
|
|
131
|
-
|
|
140
|
+
W && /* @__PURE__ */ h(i, {
|
|
132
141
|
size: "sm",
|
|
133
142
|
variant: "ghost",
|
|
134
143
|
onClick: () => {
|
|
135
|
-
let { language: e, traits: t, attribute: n, ...r } =
|
|
136
|
-
|
|
144
|
+
let { language: e, traits: t, attribute: n, ...r } = N;
|
|
145
|
+
P(r);
|
|
137
146
|
},
|
|
138
|
-
children:
|
|
147
|
+
children: O("listing.clear")
|
|
139
148
|
})
|
|
140
149
|
]
|
|
141
150
|
}), /* @__PURE__ */ h(d, {
|
|
142
151
|
...D,
|
|
143
|
-
query:
|
|
152
|
+
query: G
|
|
144
153
|
})]
|
|
145
154
|
});
|
|
146
155
|
}
|
|
147
156
|
//#endregion
|
|
148
|
-
export {
|
|
157
|
+
export { S as ContentListing, x as SortMenu, b as sortLabel };
|
|
149
158
|
|
|
150
159
|
//# sourceMappingURL=listing.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listing.js","names":[],"sources":["../../../src/components/content/listing.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { ContentQuery, ListSort } from \"../../client/discovery.js\";\nimport { useLanguageName, useMessages } from \"../../i18n/context.js\";\nimport type { MessageKey, Translator } from \"../../i18n/messages.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 { labelOf } from \"../groups/parts.js\";\nimport { ContentGrid, type CardListProps, type LockedCards } from \"./grid.js\";\n\nconst SORTS: readonly ListSort[] = [\"newest\", \"popular:7d\", \"popular:30d\", \"popular\", \"top_rated\", \"most_favorited\", \"title\", \"oldest\"];\nconst ANY = \"*\";\n\n/** A sort's name: \"Newest\", \"Popular, this week\". */\nexport function sortLabel(m: Translator, sort: ListSort): string {\n const [base, period] = sort.split(\":\");\n const name = m.t(`listing.sort_${base}` as MessageKey);\n return period ? m.t(\"listing.sortPeriod\", { sort: name, period: m.t(`listing.period_${period}` as MessageKey) }) : name;\n}\n\nexport interface ContentListingProps extends CardListProps {\n /** The listed kind: its version traits and filterable attributes become filters, its sort the default. */\n kind?: string;\n /** The sort and filters; with onQueryChange the host keeps them (e.g. in the URL). Other fields (groups, a tag page's) pass through. */\n query?: ContentQuery;\n onQueryChange?: (query: ContentQuery) => void;\n /** The sorts offered; default newest, popular (week, month, all time), top rated, most favorited, title, oldest. */\n sorts?: readonly ListSort[];\n /** Cards the reader doesn't open: show (default), hide or collapse. */\n locked?: LockedCards;\n /** Offer a language filter; default when the site has several languages. */\n languages?: boolean;\n /** The traits and attributes offered; default every trait and filterable attribute of the kind. */\n filters?: readonly string[];\n}\n\n/** A listing page: sort, language, trait and attribute filters over a paged grid of a kind's contents. */\nexport function ContentListing({ kind, query: controlled, onQueryChange, sorts = SORTS, languages, filters, ...p }: ContentListingProps) {\n const m = useMessages();\n const { t } = m;\n const languageName = useLanguageName();\n const { kinds: model } = useKinds({ client: p.client });\n const [own, setOwn] = useState<ContentQuery>({});\n const query = controlled ?? own;\n const set = useCallback(\n (next: ContentQuery) => {\n if (controlled === undefined) setOwn(next);\n onQueryChange?.(next);\n },\n [controlled, onQueryChange],\n );\n const spec = kind ? model?.kinds[kind] : undefined;\n const sort = query.sort ?? (spec?.sort as ListSort | undefined) ?? sorts[0];\n const offered = (name: string) => !filters || filters.includes(name);\n const traits = (spec?.versions.traits ?? []).filter(offered);\n const attributes = Object.entries(spec?.attributes ?? {}).filter(([n, a]) => a.filterable && (a.values?.length || a.type === \"bool\") && offered(n));\n const languageFilter = (languages ?? (model?.languages.length ?? 0) > 1) && !!model?.languages.length;\n const attributeValue = (name: string) => query.attribute?.find((a) => a.startsWith(`${name}:`))?.slice(name.length + 1);\n const setAttribute = (name: string, value: string | undefined) =>\n set({ ...query, attribute: [...(query.attribute ?? []).filter((a) => !a.startsWith(`${name}:`)), ...(value === undefined ? [] : [`${name}:${value}`])] });\n const toggleTrait = (x: string) => {\n const on = query.traits?.includes(x);\n set({ ...query, traits: on ? query.traits!.filter((y) => y !== x) : [...(query.traits ?? []), x] });\n };\n const filtered = !!(query.language || query.traits?.length || query.attribute?.length);\n const list = useMemo(() => ({ ...query, kind: kind ?? query.kind, sort }), [query, kind, sort]);\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"content-listing\">\n <div role=\"toolbar\" aria-label={t(\"listing.filters\")} className=\"flex flex-wrap items-center gap-2\">\n <Select value={sort} onValueChange={(v) => set({ ...query, sort: v as ListSort })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"listing.sort\")} data-ckui=\"listing-sort\">\n <SelectValue>{(v: string) => sortLabel(m, v as ListSort)}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {sorts.map((s) => (\n <SelectItem key={s} value={s}>\n {sortLabel(m, s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {languageFilter && (\n <Select value={query.language ?? ANY} onValueChange={(v) => set({ ...query, language: v === ANY ? undefined : (v as string) })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"listing.language\")} data-ckui=\"listing-language\">\n <SelectValue>{(v: string) => (v === ANY ? t(\"listing.anyLanguage\") : languageName(v))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ANY}>{t(\"listing.anyLanguage\")}</SelectItem>\n {model!.languages.map((l) => (\n <SelectItem key={l} value={l}>\n {languageName(l)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {attributes.map(([name, a]) => {\n const value = attributeValue(name);\n if (a.type === \"bool\")\n return (\n <Button key={name} size=\"sm\" variant={value === \"true\" ? \"default\" : \"outline\"} aria-pressed={value === \"true\"} onClick={() => setAttribute(name, value === \"true\" ? undefined : \"true\")}>\n {labelOf(p.labels, name)}\n </Button>\n );\n return (\n <Select key={name} value={value ?? ANY} onValueChange={(v) => setAttribute(name, v === ANY ? undefined : (v as string))}>\n <SelectTrigger size=\"sm\" aria-label={labelOf(p.labels, name)} data-ckui=\"listing-attribute\" data-name={name}>\n <SelectValue>{(v: string) => (v === ANY ? labelOf(p.labels, name) : labelOf(p.labels, v))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ANY}>{t(\"listing.any\")}</SelectItem>\n {a.values!.map((v) => (\n <SelectItem key={v} value={v}>\n {labelOf(p.labels, v)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n })}\n {traits.map((x) => {\n const on = !!query.traits?.includes(x);\n return (\n <Button key={x} size=\"sm\" variant={on ? \"default\" : \"outline\"} aria-pressed={on} onClick={() => toggleTrait(x)} data-ckui=\"listing-trait\">\n {labelOf(p.labels, x)}\n </Button>\n );\n })}\n {filtered && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n const { language: _l, traits: _t, attribute: _a, ...rest } = query;\n set(rest);\n }}\n >\n {t(\"listing.clear\")}\n </Button>\n )}\n </div>\n <ContentGrid {...p} query={list} />\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"listing.js","names":[],"sources":["../../../src/components/content/listing.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { ContentQuery, ListSort } from \"../../client/discovery.js\";\nimport { useLanguageName, useMessages } from \"../../i18n/context.js\";\nimport type { MessageKey, Translator } from \"../../i18n/messages.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 { labelOf } from \"../groups/parts.js\";\nimport { ContentGrid, type CardListProps, type LockedCards } from \"./grid.js\";\n\nconst SORTS: readonly ListSort[] = [\"newest\", \"popular:7d\", \"popular:30d\", \"popular\", \"top_rated\", \"most_favorited\", \"title\", \"oldest\"];\nconst ANY = \"*\";\n\n/** A sort's name: \"Newest\", \"Popular, this week\", \"Best match\". */\nexport function sortLabel(m: Translator, sort: ListSort | \"relevance\"): string {\n const [base, period] = sort.split(\":\");\n const name = m.t(`listing.sort_${base}` as MessageKey);\n return period ? m.t(\"listing.sortPeriod\", { sort: name, period: m.t(`listing.period_${period}` as MessageKey) }) : name;\n}\n\nexport interface SortMenuProps<S extends string> {\n value: S;\n /** The sorts offered, in menu order. */\n sorts: readonly S[];\n onChange: (sort: S) => void;\n /** A sort's name; default the list sorts' names. */\n label?: (sort: S) => string;\n className?: string;\n}\n\n/** A list's sort control; hosts keep the choice (URL, remembered per page type). */\nexport function SortMenu<S extends string>({ value, sorts, onChange, label, className }: SortMenuProps<S>) {\n const m = useMessages();\n const name = label ?? ((s: S) => sortLabel(m, s as ListSort));\n return (\n <Select value={value} onValueChange={(v) => onChange(v as S)}>\n <SelectTrigger size=\"sm\" aria-label={m.t(\"listing.sort\")} className={className} data-ckui=\"listing-sort\">\n <SelectValue>{(v: string) => name(v as S)}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {sorts.map((s) => (\n <SelectItem key={s} value={s}>\n {name(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nexport interface ContentListingProps extends CardListProps {\n /** The listed kind: its version traits and filterable attributes become filters, its sort the default. */\n kind?: string;\n /** The sort and filters; with onQueryChange the host keeps them (e.g. in the URL). Other fields (groups, a tag page's) pass through. A changed query starts again at page 1: a host keeping the page drops it with the query. */\n query?: ContentQuery;\n onQueryChange?: (query: ContentQuery) => void;\n /** The sorts offered; default newest, popular (week, month, all time), top rated, most favorited, title, oldest. */\n sorts?: readonly ListSort[];\n /** Cards the reader doesn't open: show (default), hide or collapse. */\n locked?: LockedCards;\n /** Offer a language filter; default when the site has several languages. */\n languages?: boolean;\n /** The traits and attributes offered; default every trait and filterable attribute of the kind. */\n filters?: readonly string[];\n}\n\n/** A listing page: sort, language, trait and attribute filters over a paged grid of a kind's contents. */\nexport function ContentListing({ kind, query: controlled, onQueryChange, sorts = SORTS, languages, filters, ...p }: ContentListingProps) {\n const m = useMessages();\n const { t } = m;\n const languageName = useLanguageName();\n const { kinds: model } = useKinds({ client: p.client });\n const [own, setOwn] = useState<ContentQuery>({});\n const query = controlled ?? own;\n const set = useCallback(\n (next: ContentQuery) => {\n if (controlled === undefined) setOwn(next);\n onQueryChange?.(next);\n },\n [controlled, onQueryChange],\n );\n const spec = kind ? model?.kinds[kind] : undefined;\n const sort: ListSort = query.sort ?? (spec?.sort as ListSort | undefined) ?? sorts[0] ?? \"newest\";\n const offered = (name: string) => !filters || filters.includes(name);\n const traits = (spec?.versions.traits ?? []).filter(offered);\n const attributes = Object.entries(spec?.attributes ?? {}).filter(([n, a]) => a.filterable && (a.values?.length || a.type === \"bool\") && offered(n));\n const languageFilter = (languages ?? (model?.languages.length ?? 0) > 1) && !!model?.languages.length;\n const attributeValue = (name: string) => query.attribute?.find((a) => a.startsWith(`${name}:`))?.slice(name.length + 1);\n const setAttribute = (name: string, value: string | undefined) =>\n set({ ...query, attribute: [...(query.attribute ?? []).filter((a) => !a.startsWith(`${name}:`)), ...(value === undefined ? [] : [`${name}:${value}`])] });\n const toggleTrait = (x: string) => {\n const on = query.traits?.includes(x);\n set({ ...query, traits: on ? query.traits!.filter((y) => y !== x) : [...(query.traits ?? []), x] });\n };\n const filtered = !!(query.language || query.traits?.length || query.attribute?.length);\n const list = useMemo(() => ({ ...query, kind: kind ?? query.kind, sort }), [query, kind, sort]);\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"content-listing\">\n <div role=\"toolbar\" aria-label={t(\"listing.filters\")} className=\"flex flex-wrap items-center gap-2\">\n <SortMenu<ListSort> value={sort} sorts={sorts} onChange={(s) => set({ ...query, sort: s })} />\n {languageFilter && (\n <Select value={query.language ?? ANY} onValueChange={(v) => set({ ...query, language: v === ANY ? undefined : (v as string) })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"listing.language\")} data-ckui=\"listing-language\">\n <SelectValue>{(v: string) => (v === ANY ? t(\"listing.anyLanguage\") : languageName(v))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ANY}>{t(\"listing.anyLanguage\")}</SelectItem>\n {model!.languages.map((l) => (\n <SelectItem key={l} value={l}>\n {languageName(l)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {attributes.map(([name, a]) => {\n const value = attributeValue(name);\n if (a.type === \"bool\")\n return (\n <Button key={name} size=\"sm\" variant={value === \"true\" ? \"default\" : \"outline\"} aria-pressed={value === \"true\"} onClick={() => setAttribute(name, value === \"true\" ? undefined : \"true\")}>\n {labelOf(p.labels, name)}\n </Button>\n );\n return (\n <Select key={name} value={value ?? ANY} onValueChange={(v) => setAttribute(name, v === ANY ? undefined : (v as string))}>\n <SelectTrigger size=\"sm\" aria-label={labelOf(p.labels, name)} data-ckui=\"listing-attribute\" data-name={name}>\n <SelectValue>{(v: string) => (v === ANY ? labelOf(p.labels, name) : labelOf(p.labels, v))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ANY}>{t(\"listing.any\")}</SelectItem>\n {a.values!.map((v) => (\n <SelectItem key={v} value={v}>\n {labelOf(p.labels, v)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n })}\n {traits.map((x) => {\n const on = !!query.traits?.includes(x);\n return (\n <Button key={x} size=\"sm\" variant={on ? \"default\" : \"outline\"} aria-pressed={on} onClick={() => toggleTrait(x)} data-ckui=\"listing-trait\">\n {labelOf(p.labels, x)}\n </Button>\n );\n })}\n {filtered && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n const { language: _l, traits: _t, attribute: _a, ...rest } = query;\n set(rest);\n }}\n >\n {t(\"listing.clear\")}\n </Button>\n )}\n </div>\n <ContentGrid {...p} query={list} />\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
@@ -4,14 +4,14 @@ import { Badge as n } from "../../ui/badge.js";
|
|
|
4
4
|
import { Input as r } from "../../ui/input.js";
|
|
5
5
|
import { Label as i } from "../../ui/label.js";
|
|
6
6
|
import { Select as a, SelectContent as o, SelectItem as s, SelectTrigger as c, SelectValue as l } from "../../ui/select.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { priceLabel as u } from "./paywall.js";
|
|
8
|
+
import { useDraft as d } from "../../react/use-draft.js";
|
|
9
|
+
import { useEditorSaveTask as f } from "./editor-save.js";
|
|
10
10
|
import { useId as p, useMemo as m } from "react";
|
|
11
11
|
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
12
12
|
//#region src/components/content/offer-fields.tsx
|
|
13
13
|
function _({ offer: _, onSell: v, onWithdraw: y, pending: b }) {
|
|
14
|
-
let x = e(), { t: S } = x, C = p(), w = _ && (_.state ?? "active") === "active" ? _ : void 0, T =
|
|
14
|
+
let x = e(), { t: S } = x, C = p(), w = _ && (_.state ?? "active") === "active" ? _ : void 0, T = d(m(() => ({
|
|
15
15
|
amount: w?.terms.amount ?? "",
|
|
16
16
|
currency: w?.terms.currency ?? "USD",
|
|
17
17
|
mode: w?.terms.renew_hours ? "renew" : w?.terms.access_hours ? "rent" : "once",
|
|
@@ -33,7 +33,7 @@ function _({ offer: _, onSell: v, onWithdraw: y, pending: b }) {
|
|
|
33
33
|
days: k
|
|
34
34
|
}) : !1;
|
|
35
35
|
};
|
|
36
|
-
return
|
|
36
|
+
return f(`offer:${C}`, {
|
|
37
37
|
label: S("contentEditor.price"),
|
|
38
38
|
dirty: T.dirty.length > 0,
|
|
39
39
|
pending: b,
|
|
@@ -51,7 +51,7 @@ function _({ offer: _, onSell: v, onWithdraw: y, pending: b }) {
|
|
|
51
51
|
variant: "secondary",
|
|
52
52
|
children: S("contentEditor.onSale")
|
|
53
53
|
}),
|
|
54
|
-
/* @__PURE__ */ h("span", { children:
|
|
54
|
+
/* @__PURE__ */ h("span", { children: u(x, w.terms) }),
|
|
55
55
|
w.synced === !1 && /* @__PURE__ */ h("span", {
|
|
56
56
|
className: "text-xs text-muted-foreground",
|
|
57
57
|
children: S("contentEditor.syncing")
|
|
@@ -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=
|