@openrails/contentkit-ui 0.85.1 → 0.87.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -7
- package/dist/client/beacons.d.ts +13 -4
- package/dist/client/beacons.js +14 -7
- package/dist/client/beacons.js.map +1 -1
- package/dist/client/client.d.ts +3 -0
- package/dist/client/client.js +29 -27
- package/dist/client/client.js.map +1 -1
- package/dist/client/codes.d.ts +6 -1
- package/dist/client/codes.js +4 -1
- package/dist/client/codes.js.map +1 -1
- package/dist/client/content/types.d.ts +4 -2
- package/dist/client/content/types.js.map +1 -1
- package/dist/client/discovery.js +3 -0
- package/dist/client/discovery.js.map +1 -1
- package/dist/client/errors.d.ts +6 -0
- package/dist/client/errors.js +6 -2
- package/dist/client/errors.js.map +1 -1
- package/dist/client/gallery.d.ts +3 -5
- package/dist/client/gallery.js +16 -20
- package/dist/client/gallery.js.map +1 -1
- package/dist/client/generated/error-codes.d.ts +1 -1
- package/dist/client/generated/error-codes.js +9 -1
- package/dist/client/generated/error-codes.js.map +1 -1
- package/dist/client/generated/routes.d.ts +29 -3
- package/dist/client/generated/routes.js +44 -3
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +60 -10
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/media/queue.d.ts +4 -2
- package/dist/client/media/queue.js +18 -12
- package/dist/client/media/queue.js.map +1 -1
- package/dist/client/media/rules.d.ts +7 -1
- package/dist/client/media/rules.js +27 -21
- package/dist/client/media/rules.js.map +1 -1
- package/dist/client/media/windows.js +1 -1
- package/dist/client/media/windows.js.map +1 -1
- package/dist/client/stats.d.ts +32 -0
- package/dist/client/stats.js +35 -0
- package/dist/client/stats.js.map +1 -0
- package/dist/client/versions.d.ts +6 -4
- package/dist/client/versions.js +24 -26
- package/dist/client/versions.js.map +1 -1
- package/dist/client.js +30 -29
- package/dist/components/admin/content-manager.js +206 -206
- package/dist/components/admin/content-manager.js.map +1 -1
- package/dist/components/admin/group-manager.js +71 -70
- package/dist/components/admin/group-manager.js.map +1 -1
- package/dist/components/admin/media-item-panel.d.ts +24 -7
- package/dist/components/admin/media-item-panel.js +209 -197
- package/dist/components/admin/media-item-panel.js.map +1 -1
- package/dist/components/admin/poll-manager.js +25 -25
- package/dist/components/admin/stats-dashboard.d.ts +22 -0
- package/dist/components/admin/stats-dashboard.js +257 -0
- package/dist/components/admin/stats-dashboard.js.map +1 -0
- package/dist/components/content/card.d.ts +5 -3
- package/dist/components/content/card.js +155 -133
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/download-button.d.ts +26 -0
- package/dist/components/content/download-button.js +51 -0
- package/dist/components/content/download-button.js.map +1 -0
- package/dist/components/content/editors.js +455 -427
- package/dist/components/content/editors.js.map +1 -1
- package/dist/components/content/grid.d.ts +8 -1
- package/dist/components/content/grid.js +153 -104
- package/dist/components/content/grid.js.map +1 -1
- package/dist/components/content/index.d.ts +4 -2
- package/dist/components/content/listing.d.ts +3 -1
- package/dist/components/content/listing.js +34 -34
- package/dist/components/content/listing.js.map +1 -1
- package/dist/components/content/paywall.d.ts +17 -5
- package/dist/components/content/paywall.js +115 -100
- package/dist/components/content/paywall.js.map +1 -1
- package/dist/components/content/reader-stats.d.ts +12 -0
- package/dist/components/content/reader-stats.js +47 -0
- package/dist/components/content/reader-stats.js.map +1 -0
- package/dist/components/groups/banner.js +33 -33
- package/dist/components/groups/directory.js +11 -11
- package/dist/components/groups/editor.js +293 -293
- package/dist/components/groups/editor.js.map +1 -1
- package/dist/components/groups/header.js +27 -27
- package/dist/components/groups/header.js.map +1 -1
- package/dist/components/groups/memberships.js +180 -180
- package/dist/components/groups/parts.d.ts +0 -2
- package/dist/components/groups/parts.js +9 -27
- package/dist/components/groups/parts.js.map +1 -1
- package/dist/components/media-folder-editor.js +239 -189
- package/dist/components/media-folder-editor.js.map +1 -1
- package/dist/components/media-gallery.d.ts +1 -4
- package/dist/components/media-gallery.js +19 -34
- package/dist/components/media-gallery.js.map +1 -1
- package/dist/components/notifications.js +17 -17
- package/dist/components/slot-upload.d.ts +2 -2
- package/dist/components/slot-upload.js +84 -79
- package/dist/components/slot-upload.js.map +1 -1
- package/dist/components/social/bans.js +34 -34
- package/dist/components/social/comments.js +1 -1
- package/dist/components/social/comments.js.map +1 -1
- package/dist/components/social/engagement.d.ts +2 -1
- package/dist/components/social/engagement.js +5 -3
- package/dist/components/social/engagement.js.map +1 -1
- package/dist/components/social/moderation.js +52 -48
- package/dist/components/social/moderation.js.map +1 -1
- package/dist/components/video-player.d.ts +2 -0
- package/dist/components/video-player.js +318 -314
- package/dist/components/video-player.js.map +1 -1
- package/dist/i18n/context.d.ts +2 -0
- package/dist/i18n/context.js +23 -5
- package/dist/i18n/context.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +13 -10
- package/dist/locales/admin/de.js +27 -0
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +27 -0
- package/dist/locales/admin/en.js +27 -0
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +27 -0
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +27 -0
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +27 -0
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +27 -0
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/content/de.js +6 -0
- package/dist/locales/content/de.js.map +1 -1
- package/dist/locales/content/en.d.ts +6 -0
- package/dist/locales/content/en.js +6 -0
- package/dist/locales/content/en.js.map +1 -1
- package/dist/locales/content/es.js +6 -0
- package/dist/locales/content/es.js.map +1 -1
- package/dist/locales/content/ja.js +6 -0
- package/dist/locales/content/ja.js.map +1 -1
- package/dist/locales/content/ko.js +6 -0
- package/dist/locales/content/ko.js.map +1 -1
- package/dist/locales/content/zh.js +6 -0
- package/dist/locales/content/zh.js.map +1 -1
- package/dist/locales/de.js +14 -2
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/discovery/de.js +3 -0
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +3 -0
- package/dist/locales/discovery/en.js +3 -0
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +3 -0
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +3 -0
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +3 -0
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +3 -0
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +50 -2
- package/dist/locales/en.js +14 -2
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +14 -2
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/ja.js +14 -2
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +14 -2
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/zh.js +14 -2
- package/dist/locales/zh.js.map +1 -1
- package/dist/react/admin.js +22 -23
- package/dist/react/admin.js.map +1 -1
- package/dist/react/canonical.d.ts +4 -3
- package/dist/react/canonical.js +34 -33
- package/dist/react/canonical.js.map +1 -1
- package/dist/react/comments.d.ts +1 -1
- package/dist/react/comments.js +3 -2
- package/dist/react/comments.js.map +1 -1
- package/dist/react/contents.js +144 -144
- package/dist/react/contents.js.map +1 -1
- package/dist/react/context.d.ts +1 -1
- package/dist/react/context.js.map +1 -1
- package/dist/react/detail.d.ts +12 -6
- package/dist/react/detail.js +31 -24
- package/dist/react/detail.js.map +1 -1
- package/dist/react/discovery.d.ts +11 -0
- package/dist/react/discovery.js +49 -30
- package/dist/react/discovery.js.map +1 -1
- package/dist/react/engagement.d.ts +6 -2
- package/dist/react/engagement.js +2 -6
- package/dist/react/engagement.js.map +1 -1
- package/dist/react/folder.d.ts +7 -2
- package/dist/react/folder.js +165 -147
- package/dist/react/folder.js.map +1 -1
- package/dist/react/groups.d.ts +0 -1
- package/dist/react/groups.js +13 -12
- package/dist/react/groups.js.map +1 -1
- package/dist/react/index.d.ts +6 -4
- package/dist/react/location.d.ts +11 -0
- package/dist/react/location.js +18 -0
- package/dist/react/location.js.map +1 -0
- package/dist/react/public.d.ts +8 -0
- package/dist/react/public.js +32 -27
- package/dist/react/public.js.map +1 -1
- package/dist/react/stats.d.ts +24 -0
- package/dist/react/stats.js +28 -0
- package/dist/react/stats.js.map +1 -0
- package/dist/react/use-resource.js +8 -7
- package/dist/react/use-resource.js.map +1 -1
- package/dist/react/versions.d.ts +25 -5
- package/dist/react/versions.js +61 -40
- package/dist/react/versions.js.map +1 -1
- package/dist/react.js +26 -24
- package/dist/styles.css +1 -1
- package/dist/urls/index.d.ts +35 -10
- package/dist/urls.js +100 -70
- package/dist/urls.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ContentKitUiRoot as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { labelOf as
|
|
1
|
+
import { useLanguageName as e, useMessages as t } from "../../i18n/context.js";
|
|
2
|
+
import { ContentKitUiRoot as n } from "../../scope.js";
|
|
3
|
+
import { useKinds as r } from "../../react/groups.js";
|
|
4
|
+
import { Button as i } from "../../ui/button.js";
|
|
5
|
+
import { Select as a, SelectContent as o, SelectItem as s, SelectTrigger as c, SelectValue as l } from "../../ui/select.js";
|
|
6
|
+
import { labelOf as u } from "../groups/parts.js";
|
|
7
7
|
import { ContentGrid as d } from "./grid.js";
|
|
8
8
|
import { useCallback as f, useMemo as p, useState as m } from "react";
|
|
9
9
|
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
@@ -27,7 +27,7 @@ function b(e, t) {
|
|
|
27
27
|
}) : i;
|
|
28
28
|
}
|
|
29
29
|
function x({ kind: x, query: S, onQueryChange: C, sorts: w = v, languages: T, filters: E, ...D }) {
|
|
30
|
-
let O =
|
|
30
|
+
let O = t(), { t: k } = O, A = e(), { kinds: j } = r({ client: D.client }), [M, N] = m({}), P = S ?? M, F = f((e) => {
|
|
31
31
|
S === void 0 && N(e), C?.(e);
|
|
32
32
|
}, [S, C]), I = x ? j?.kinds[x] : void 0, L = P.sort ?? I?.sort ?? w[0], R = (e) => !E || E.includes(e), z = (I?.versions.traits ?? []).filter(R), B = Object.entries(I?.attributes ?? {}).filter(([e, t]) => t.filterable && (t.values?.length || t.type === "bool") && R(e)), V = (T ?? (j?.languages.length ?? 0) > 1) && !!j?.languages.length, H = (e) => P.attribute?.find((t) => t.startsWith(`${e}:`))?.slice(e.length + 1), U = (e, t) => F({
|
|
33
33
|
...P,
|
|
@@ -47,7 +47,7 @@ function x({ kind: x, query: S, onQueryChange: C, sorts: w = v, languages: T, fi
|
|
|
47
47
|
x,
|
|
48
48
|
L
|
|
49
49
|
]);
|
|
50
|
-
return /* @__PURE__ */ g(
|
|
50
|
+
return /* @__PURE__ */ g(n, {
|
|
51
51
|
appearance: D.appearance,
|
|
52
52
|
className: _("grid gap-4 text-sm", D.className),
|
|
53
53
|
"data-ckui": "content-listing",
|
|
@@ -56,79 +56,79 @@ function x({ kind: x, query: S, onQueryChange: C, sorts: w = v, languages: T, fi
|
|
|
56
56
|
"aria-label": k("listing.filters"),
|
|
57
57
|
className: "flex flex-wrap items-center gap-2",
|
|
58
58
|
children: [
|
|
59
|
-
/* @__PURE__ */ g(
|
|
59
|
+
/* @__PURE__ */ g(a, {
|
|
60
60
|
value: L,
|
|
61
61
|
onValueChange: (e) => F({
|
|
62
62
|
...P,
|
|
63
63
|
sort: e
|
|
64
64
|
}),
|
|
65
|
-
children: [/* @__PURE__ */ h(
|
|
65
|
+
children: [/* @__PURE__ */ h(c, {
|
|
66
66
|
size: "sm",
|
|
67
67
|
"aria-label": k("listing.sort"),
|
|
68
68
|
"data-ckui": "listing-sort",
|
|
69
|
-
children: /* @__PURE__ */ h(
|
|
70
|
-
}), /* @__PURE__ */ h(
|
|
69
|
+
children: /* @__PURE__ */ h(l, { children: (e) => b(O, e) })
|
|
70
|
+
}), /* @__PURE__ */ h(o, { children: w.map((e) => /* @__PURE__ */ h(s, {
|
|
71
71
|
value: e,
|
|
72
72
|
children: b(O, e)
|
|
73
73
|
}, e)) })]
|
|
74
74
|
}),
|
|
75
|
-
V && /* @__PURE__ */ g(
|
|
75
|
+
V && /* @__PURE__ */ g(a, {
|
|
76
76
|
value: P.language ?? y,
|
|
77
77
|
onValueChange: (e) => F({
|
|
78
78
|
...P,
|
|
79
79
|
language: e === y ? void 0 : e
|
|
80
80
|
}),
|
|
81
|
-
children: [/* @__PURE__ */ h(
|
|
81
|
+
children: [/* @__PURE__ */ h(c, {
|
|
82
82
|
size: "sm",
|
|
83
83
|
"aria-label": k("listing.language"),
|
|
84
84
|
"data-ckui": "listing-language",
|
|
85
|
-
children: /* @__PURE__ */ h(
|
|
86
|
-
}), /* @__PURE__ */ g(
|
|
85
|
+
children: /* @__PURE__ */ h(l, { children: (e) => e === y ? k("listing.anyLanguage") : A(e) })
|
|
86
|
+
}), /* @__PURE__ */ g(o, { children: [/* @__PURE__ */ h(s, {
|
|
87
87
|
value: y,
|
|
88
88
|
children: k("listing.anyLanguage")
|
|
89
|
-
}), j.languages.map((e) => /* @__PURE__ */ h(
|
|
89
|
+
}), j.languages.map((e) => /* @__PURE__ */ h(s, {
|
|
90
90
|
value: e,
|
|
91
91
|
children: A(e)
|
|
92
92
|
}, e))] })]
|
|
93
93
|
}),
|
|
94
94
|
B.map(([e, t]) => {
|
|
95
|
-
let
|
|
96
|
-
return t.type === "bool" ? /* @__PURE__ */ h(
|
|
95
|
+
let n = H(e);
|
|
96
|
+
return t.type === "bool" ? /* @__PURE__ */ h(i, {
|
|
97
97
|
size: "sm",
|
|
98
|
-
variant:
|
|
99
|
-
"aria-pressed":
|
|
100
|
-
onClick: () => U(e,
|
|
101
|
-
children:
|
|
102
|
-
}, e) : /* @__PURE__ */ g(
|
|
103
|
-
value:
|
|
98
|
+
variant: n === "true" ? "default" : "outline",
|
|
99
|
+
"aria-pressed": n === "true",
|
|
100
|
+
onClick: () => U(e, n === "true" ? void 0 : "true"),
|
|
101
|
+
children: u(D.labels, e)
|
|
102
|
+
}, e) : /* @__PURE__ */ g(a, {
|
|
103
|
+
value: n ?? y,
|
|
104
104
|
onValueChange: (t) => U(e, t === y ? void 0 : t),
|
|
105
|
-
children: [/* @__PURE__ */ h(
|
|
105
|
+
children: [/* @__PURE__ */ h(c, {
|
|
106
106
|
size: "sm",
|
|
107
|
-
"aria-label":
|
|
107
|
+
"aria-label": u(D.labels, e),
|
|
108
108
|
"data-ckui": "listing-attribute",
|
|
109
109
|
"data-name": e,
|
|
110
|
-
children: /* @__PURE__ */ h(
|
|
111
|
-
}), /* @__PURE__ */ g(
|
|
110
|
+
children: /* @__PURE__ */ h(l, { children: (t) => t === y ? u(D.labels, e) : u(D.labels, t) })
|
|
111
|
+
}), /* @__PURE__ */ g(o, { children: [/* @__PURE__ */ h(s, {
|
|
112
112
|
value: y,
|
|
113
113
|
children: k("listing.any")
|
|
114
|
-
}), t.values.map((e) => /* @__PURE__ */ h(
|
|
114
|
+
}), t.values.map((e) => /* @__PURE__ */ h(s, {
|
|
115
115
|
value: e,
|
|
116
|
-
children:
|
|
116
|
+
children: u(D.labels, e)
|
|
117
117
|
}, e))] })]
|
|
118
118
|
}, e);
|
|
119
119
|
}),
|
|
120
120
|
z.map((e) => {
|
|
121
121
|
let t = !!P.traits?.includes(e);
|
|
122
|
-
return /* @__PURE__ */ h(
|
|
122
|
+
return /* @__PURE__ */ h(i, {
|
|
123
123
|
size: "sm",
|
|
124
124
|
variant: t ? "default" : "outline",
|
|
125
125
|
"aria-pressed": t,
|
|
126
126
|
onClick: () => W(e),
|
|
127
127
|
"data-ckui": "listing-trait",
|
|
128
|
-
children:
|
|
128
|
+
children: u(D.labels, e)
|
|
129
129
|
}, e);
|
|
130
130
|
}),
|
|
131
|
-
G && /* @__PURE__ */ h(
|
|
131
|
+
G && /* @__PURE__ */ h(i, {
|
|
132
132
|
size: "sm",
|
|
133
133
|
variant: "ghost",
|
|
134
134
|
onClick: () => {
|
|
@@ -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 { 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, useLanguageName } from \"../groups/parts.js\";\nimport { ContentGrid, type CardListProps } 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 /** 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\". */\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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"}
|
|
@@ -40,14 +40,26 @@ export declare function Paywall({ decision, onCheckout, tiers, onSignIn, labels,
|
|
|
40
40
|
export interface VersionPickerProps {
|
|
41
41
|
/** The content's live versions (useContentDetail().versions). */
|
|
42
42
|
versions: readonly Version[];
|
|
43
|
-
/** The version shown. */
|
|
44
|
-
value
|
|
45
|
-
|
|
43
|
+
/** The version shown, by code or id; default the page's ?v=, else the reader's language's default. */
|
|
44
|
+
value?: string | null;
|
|
45
|
+
/** Receives the chosen version; default writes its code to the page's ?v=. */
|
|
46
|
+
onChange?: (version: Version) => void;
|
|
46
47
|
labels?: Labels;
|
|
47
48
|
className?: string;
|
|
48
49
|
appearance?: ContentKitUiAppearance;
|
|
49
50
|
}
|
|
50
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Chooses among a content's versions: a language first when they differ in
|
|
53
|
+
* language, then its variants (traits, labels). The choice is the page's ?v=
|
|
54
|
+
* unless value and onChange say otherwise.
|
|
55
|
+
*/
|
|
51
56
|
export declare function VersionPicker({ versions, value, onChange, labels, className, appearance }: VersionPickerProps): import('react').JSX.Element | null;
|
|
57
|
+
export interface VersionMenuOptions {
|
|
58
|
+
/** The version shown, by code or id; default the page's ?v=, else the reader's language's default. */
|
|
59
|
+
value?: string | null;
|
|
60
|
+
/** Receives the chosen version; default writes its code to the page's ?v=. */
|
|
61
|
+
onChange?: (version: Version) => void;
|
|
62
|
+
labels?: Labels;
|
|
63
|
+
}
|
|
52
64
|
/** VersionPicker as VideoPlayer settings entries (menuItems), for a watch page. */
|
|
53
|
-
export declare function useVersionMenuItems(versions: readonly Version[],
|
|
65
|
+
export declare function useVersionMenuItems(versions: readonly Version[], o?: VersionMenuOptions): PlayerMenuItem[];
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ContentKitUiRoot as
|
|
3
|
-
import { ContentKitContext as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
1
|
+
import { useLanguageName as e, useMessages as t } from "../../i18n/context.js";
|
|
2
|
+
import { ContentKitUiRoot as n } from "../../scope.js";
|
|
3
|
+
import { ContentKitContext as r, useContentKitClient as i } from "../../react/context.js";
|
|
4
|
+
import { useKinds as a } from "../../react/groups.js";
|
|
5
|
+
import { Button as o } from "../../ui/button.js";
|
|
6
|
+
import { Select as s, SelectContent as c, SelectItem as l, SelectTrigger as u, SelectValue as d } from "../../ui/select.js";
|
|
7
|
+
import { ErrorLine as f, contentError as p, formatDate as m, useAction as h } from "../social/parts.js";
|
|
8
|
+
import { useVersionParam as g } from "../../react/location.js";
|
|
9
|
+
import { labelOf as _ } from "../groups/parts.js";
|
|
10
|
+
import { findVersion as v } from "../../react/detail.js";
|
|
11
|
+
import { useCallback as y, useContext as b, useMemo as x } from "react";
|
|
12
|
+
import { jsx as S, jsxs as C } from "react/jsx-runtime";
|
|
13
|
+
import { cn as w } from "cn";
|
|
14
|
+
import { LockIcon as T } from "@hugeicons/core-free-icons";
|
|
15
|
+
import { HugeiconsIcon as E } from "@hugeicons/react";
|
|
14
16
|
//#region src/components/content/paywall.tsx
|
|
15
|
-
function
|
|
17
|
+
function D(e, t) {
|
|
16
18
|
let n;
|
|
17
19
|
try {
|
|
18
20
|
n = new Intl.NumberFormat(e.language, {
|
|
@@ -31,47 +33,47 @@ function w(e, t) {
|
|
|
31
33
|
period: r(t.access_hours)
|
|
32
34
|
}) : n;
|
|
33
35
|
}
|
|
34
|
-
function
|
|
35
|
-
let O =
|
|
36
|
-
if (
|
|
37
|
-
let P =
|
|
38
|
-
return /* @__PURE__ */
|
|
39
|
-
appearance:
|
|
40
|
-
className:
|
|
36
|
+
function O({ decision: e, onCheckout: s, tiers: c, onSignIn: l, labels: u, children: d, client: g, onError: v, className: y, appearance: x }) {
|
|
37
|
+
let O = t(), { t: k } = O, A = b(r), j = i(g), { kinds: M } = a({ client: g }), N = h("membership.join", v);
|
|
38
|
+
if (e.allowed) return null;
|
|
39
|
+
let P = l ?? A?.onSignIn, F = e.offers ?? [], I = new Set(F.map((e) => e.key)), L = M?.tiers ?? Object.keys(c ?? {}), R = (e.sell ?? []).filter((e) => !I.has(e) && (L.includes(e) || !!c?.[e])), z = e.requires, B = z && !F.some((e) => e.key === z) ? z : void 0, V = (e) => !!z && e !== z, H = e.preview;
|
|
40
|
+
return /* @__PURE__ */ C(n, {
|
|
41
|
+
appearance: x,
|
|
42
|
+
className: w("grid gap-3 rounded-lg border border-border p-4 text-sm", y),
|
|
41
43
|
"data-ckui": "paywall",
|
|
42
44
|
children: [
|
|
43
|
-
/* @__PURE__ */
|
|
45
|
+
/* @__PURE__ */ C("p", {
|
|
44
46
|
className: "flex items-center gap-2 font-medium",
|
|
45
|
-
children: [/* @__PURE__ */
|
|
46
|
-
icon:
|
|
47
|
+
children: [/* @__PURE__ */ S(E, {
|
|
48
|
+
icon: T,
|
|
47
49
|
strokeWidth: 2,
|
|
48
50
|
className: "size-4",
|
|
49
51
|
"aria-hidden": !0
|
|
50
|
-
}),
|
|
52
|
+
}), e.unknown ? k("paywall.unknown") : k(z ? "paywall.requiresFirst" : "paywall.locked")]
|
|
51
53
|
}),
|
|
52
|
-
H?.pages ? /* @__PURE__ */
|
|
54
|
+
H?.pages ? /* @__PURE__ */ S("p", {
|
|
53
55
|
className: "text-muted-foreground",
|
|
54
56
|
children: O.plural("paywall.previewPages", H.pages)
|
|
55
57
|
}) : null,
|
|
56
|
-
H?.segments ? /* @__PURE__ */
|
|
58
|
+
H?.segments ? /* @__PURE__ */ S("p", {
|
|
57
59
|
className: "text-muted-foreground",
|
|
58
60
|
children: O.plural("paywall.previewSegments", H.segments)
|
|
59
61
|
}) : null,
|
|
60
|
-
|
|
62
|
+
e.free_after && /* @__PURE__ */ S("p", {
|
|
61
63
|
className: "text-muted-foreground",
|
|
62
|
-
children: k("paywall.freeFrom", { date:
|
|
64
|
+
children: k("paywall.freeFrom", { date: m(O, e.free_after, !1) })
|
|
63
65
|
}),
|
|
64
|
-
!
|
|
66
|
+
!e.unknown && /* @__PURE__ */ C("div", {
|
|
65
67
|
className: "flex flex-wrap gap-2",
|
|
66
68
|
"data-ckui": "paywall-offers",
|
|
67
69
|
children: [
|
|
68
|
-
B && /* @__PURE__ */
|
|
70
|
+
B && /* @__PURE__ */ S(o, {
|
|
69
71
|
size: "sm",
|
|
70
72
|
variant: "outline",
|
|
71
|
-
onClick: () =>
|
|
73
|
+
onClick: () => s({ key: B }),
|
|
72
74
|
children: k("paywall.joinFirst")
|
|
73
75
|
}),
|
|
74
|
-
F.map((e) => e.target.kind === "group" && Number(e.terms.amount) === 0 ? /* @__PURE__ */
|
|
76
|
+
F.map((e) => e.target.kind === "group" && Number(e.terms.amount) === 0 ? /* @__PURE__ */ S(o, {
|
|
75
77
|
size: "sm",
|
|
76
78
|
disabled: V(e.key) || N.pending,
|
|
77
79
|
onClick: () => A?.viewer === null && P ? P() : void N.run(() => j.offers.join(e.target.id)),
|
|
@@ -79,37 +81,37 @@ function T({ decision: a, onCheckout: o, tiers: s, onSignIn: c, labels: l, child
|
|
|
79
81
|
"data-target": "group",
|
|
80
82
|
"data-free": "",
|
|
81
83
|
children: k("paywall.joinFirst")
|
|
82
|
-
}, e.key) : /* @__PURE__ */
|
|
84
|
+
}, e.key) : /* @__PURE__ */ S(o, {
|
|
83
85
|
size: "sm",
|
|
84
86
|
disabled: V(e.key),
|
|
85
|
-
onClick: () =>
|
|
87
|
+
onClick: () => s({
|
|
86
88
|
key: e.key,
|
|
87
89
|
offer: e
|
|
88
90
|
}),
|
|
89
91
|
"data-ckui": "paywall-offer",
|
|
90
92
|
"data-target": e.target.kind,
|
|
91
|
-
children: k(e.target.kind === "group" ? "paywall.joinFor" : e.target.kind === "version" ? "paywall.unlockVersionFor" : "paywall.unlockFor", { price:
|
|
93
|
+
children: k(e.target.kind === "group" ? "paywall.joinFor" : e.target.kind === "version" ? "paywall.unlockVersionFor" : "paywall.unlockFor", { price: D(O, e.terms) })
|
|
92
94
|
}, e.key)),
|
|
93
|
-
R.map((e) => /* @__PURE__ */
|
|
95
|
+
R.map((e) => /* @__PURE__ */ C(o, {
|
|
94
96
|
size: "sm",
|
|
95
97
|
variant: F.length ? "outline" : "default",
|
|
96
|
-
onClick: () =>
|
|
98
|
+
onClick: () => s({ key: e }),
|
|
97
99
|
"data-ckui": "paywall-tier",
|
|
98
100
|
"data-tier": e,
|
|
99
|
-
children: [k("paywall.subscribe", { tier:
|
|
101
|
+
children: [k("paywall.subscribe", { tier: c?.[e]?.label ?? _(u, e) }), c?.[e]?.price && /* @__PURE__ */ S("span", {
|
|
100
102
|
className: "ms-1 opacity-80",
|
|
101
|
-
children:
|
|
103
|
+
children: c[e].price
|
|
102
104
|
})]
|
|
103
105
|
}, e))
|
|
104
106
|
]
|
|
105
107
|
}),
|
|
106
|
-
N.error && /* @__PURE__ */
|
|
107
|
-
A?.viewer === null && P && /* @__PURE__ */
|
|
108
|
+
N.error && /* @__PURE__ */ S(f, { children: p(O, N.error) }),
|
|
109
|
+
A?.viewer === null && P && /* @__PURE__ */ C("p", {
|
|
108
110
|
className: "text-muted-foreground",
|
|
109
111
|
children: [
|
|
110
112
|
k("paywall.haveIt"),
|
|
111
113
|
" ",
|
|
112
|
-
/* @__PURE__ */
|
|
114
|
+
/* @__PURE__ */ S("button", {
|
|
113
115
|
type: "button",
|
|
114
116
|
className: "font-medium text-foreground underline-offset-4 hover:underline",
|
|
115
117
|
onClick: P,
|
|
@@ -117,105 +119,118 @@ function T({ decision: a, onCheckout: o, tiers: s, onSignIn: c, labels: l, child
|
|
|
117
119
|
})
|
|
118
120
|
]
|
|
119
121
|
}),
|
|
120
|
-
|
|
122
|
+
d
|
|
121
123
|
]
|
|
122
124
|
});
|
|
123
125
|
}
|
|
124
|
-
function
|
|
125
|
-
return e.label || (e.traits.length ? e.traits.map((e) =>
|
|
126
|
+
function k(e, t, n) {
|
|
127
|
+
return e.label || (e.traits.length ? e.traits.map((e) => _(n, e)).join(" · ") : e.languages.map(t).join(", "));
|
|
126
128
|
}
|
|
127
|
-
var
|
|
128
|
-
function
|
|
129
|
-
let
|
|
130
|
-
return {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
129
|
+
var A = (e) => e.languages[0] ?? "";
|
|
130
|
+
function j(e, t, n) {
|
|
131
|
+
let [i, a] = g(), o = b(r)?.language, s = t === void 0 ? i : t, c = y((e) => n ? n(e) : a(e.code), [n, a]);
|
|
132
|
+
return x(() => {
|
|
133
|
+
let t = v(e, s) ?? (o ? e.find((e) => e.languages.includes(o)) : void 0) ?? e[0], n = t ? A(t) : "";
|
|
134
|
+
return {
|
|
135
|
+
current: t,
|
|
136
|
+
language: n,
|
|
137
|
+
languages: [...new Set(e.map(A))],
|
|
138
|
+
variants: e.filter((e) => A(e) === n),
|
|
139
|
+
change: c
|
|
140
|
+
};
|
|
141
|
+
}, [
|
|
142
|
+
e,
|
|
143
|
+
s,
|
|
144
|
+
o,
|
|
145
|
+
c
|
|
146
|
+
]);
|
|
136
147
|
}
|
|
137
|
-
function
|
|
138
|
-
let { t:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
appearance: p,
|
|
143
|
-
className: x("flex flex-wrap items-center gap-2 text-sm", f),
|
|
148
|
+
function M({ versions: r, value: i, onChange: a, labels: f, className: p, appearance: m }) {
|
|
149
|
+
let { t: h } = t(), g = e(), { current: _, language: v, languages: y, variants: b, change: x } = j(r, i, a);
|
|
150
|
+
return r.length < 2 || !_ ? null : /* @__PURE__ */ C(n, {
|
|
151
|
+
appearance: m,
|
|
152
|
+
className: w("flex flex-wrap items-center gap-2 text-sm", p),
|
|
144
153
|
"data-ckui": "version-picker",
|
|
145
|
-
children: [
|
|
154
|
+
children: [y.length > 1 && /* @__PURE__ */ C(s, {
|
|
146
155
|
value: v,
|
|
147
156
|
onValueChange: (e) => {
|
|
148
|
-
let t =
|
|
149
|
-
t &&
|
|
157
|
+
let t = r.find((t) => A(t) === e);
|
|
158
|
+
t && x(t);
|
|
150
159
|
},
|
|
151
|
-
children: [/* @__PURE__ */
|
|
160
|
+
children: [/* @__PURE__ */ S(u, {
|
|
152
161
|
size: "sm",
|
|
153
|
-
"aria-label":
|
|
154
|
-
children: /* @__PURE__ */
|
|
155
|
-
}), /* @__PURE__ */
|
|
162
|
+
"aria-label": h("versions.language"),
|
|
163
|
+
children: /* @__PURE__ */ S(d, { children: (e) => g(e) })
|
|
164
|
+
}), /* @__PURE__ */ S(c, { children: y.map((e) => /* @__PURE__ */ S(l, {
|
|
156
165
|
value: e,
|
|
157
|
-
children:
|
|
166
|
+
children: g(e)
|
|
158
167
|
}, e)) })]
|
|
159
|
-
}),
|
|
168
|
+
}), b.length > 1 && /* @__PURE__ */ S("div", {
|
|
160
169
|
role: "radiogroup",
|
|
161
|
-
"aria-label":
|
|
170
|
+
"aria-label": h("versions.version"),
|
|
162
171
|
className: "flex flex-wrap gap-1",
|
|
163
|
-
children:
|
|
172
|
+
children: b.map((e) => /* @__PURE__ */ C(o, {
|
|
164
173
|
size: "sm",
|
|
165
174
|
role: "radio",
|
|
166
175
|
"aria-checked": e.id === _.id,
|
|
167
176
|
variant: e.id === _.id ? "default" : "outline",
|
|
168
|
-
onClick: () =>
|
|
177
|
+
onClick: () => x(e),
|
|
169
178
|
"data-ckui": "version-option",
|
|
170
179
|
"data-locked": e.decision.allowed ? void 0 : "",
|
|
171
|
-
children: [!e.decision.allowed && /* @__PURE__ */
|
|
172
|
-
icon:
|
|
180
|
+
children: [!e.decision.allowed && /* @__PURE__ */ S(E, {
|
|
181
|
+
icon: T,
|
|
173
182
|
strokeWidth: 2,
|
|
174
183
|
className: "size-3.5",
|
|
175
|
-
"aria-label":
|
|
176
|
-
}),
|
|
184
|
+
"aria-label": h("versions.locked")
|
|
185
|
+
}), k(e, g, f)]
|
|
177
186
|
}, e.id))
|
|
178
187
|
})]
|
|
179
188
|
});
|
|
180
189
|
}
|
|
181
|
-
function
|
|
182
|
-
let { t:
|
|
183
|
-
return
|
|
184
|
-
if (
|
|
185
|
-
let
|
|
186
|
-
return c.length > 1 &&
|
|
187
|
-
label:
|
|
190
|
+
function N(n, r = {}) {
|
|
191
|
+
let { t: i } = t(), a = e(), { current: o, language: s, languages: c, variants: l, change: u } = j(n, r.value, r.onChange), { labels: d } = r;
|
|
192
|
+
return x(() => {
|
|
193
|
+
if (n.length < 2 || !o) return [];
|
|
194
|
+
let e = [];
|
|
195
|
+
return c.length > 1 && e.push({
|
|
196
|
+
label: i("versions.language"),
|
|
188
197
|
value: s,
|
|
189
198
|
options: c.map((e) => ({
|
|
190
199
|
value: e,
|
|
191
|
-
label:
|
|
200
|
+
label: a(e)
|
|
192
201
|
})),
|
|
193
202
|
onChange: (e) => {
|
|
194
|
-
let
|
|
195
|
-
|
|
203
|
+
let t = n.find((t) => A(t) === e);
|
|
204
|
+
t && u(t);
|
|
196
205
|
}
|
|
197
|
-
}), l.length > 1 &&
|
|
198
|
-
label:
|
|
199
|
-
value:
|
|
206
|
+
}), l.length > 1 && e.push({
|
|
207
|
+
label: i("versions.version"),
|
|
208
|
+
value: o.id,
|
|
200
209
|
options: l.map((e) => {
|
|
201
|
-
let t =
|
|
210
|
+
let t = k(e, a, d);
|
|
202
211
|
return {
|
|
203
212
|
value: e.id,
|
|
204
|
-
label: e.decision.allowed ? t :
|
|
213
|
+
label: e.decision.allowed ? t : i("versions.lockedOption", { name: t })
|
|
205
214
|
};
|
|
206
215
|
}),
|
|
207
|
-
onChange:
|
|
208
|
-
|
|
216
|
+
onChange: (e) => {
|
|
217
|
+
let t = n.find((t) => t.id === e);
|
|
218
|
+
t && u(t);
|
|
219
|
+
}
|
|
220
|
+
}), e;
|
|
209
221
|
}, [
|
|
210
|
-
t,
|
|
211
222
|
n,
|
|
212
|
-
|
|
223
|
+
o,
|
|
224
|
+
s,
|
|
225
|
+
c,
|
|
226
|
+
l,
|
|
227
|
+
u,
|
|
228
|
+
d,
|
|
213
229
|
i,
|
|
214
|
-
a
|
|
215
|
-
o
|
|
230
|
+
a
|
|
216
231
|
]);
|
|
217
232
|
}
|
|
218
233
|
//#endregion
|
|
219
|
-
export {
|
|
234
|
+
export { O as Paywall, M as VersionPicker, D as priceLabel, N as useVersionMenuItems };
|
|
220
235
|
|
|
221
236
|
//# sourceMappingURL=paywall.js.map
|