@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.
Files changed (211) hide show
  1. package/README.md +28 -7
  2. package/dist/client/beacons.d.ts +13 -4
  3. package/dist/client/beacons.js +14 -7
  4. package/dist/client/beacons.js.map +1 -1
  5. package/dist/client/client.d.ts +3 -0
  6. package/dist/client/client.js +29 -27
  7. package/dist/client/client.js.map +1 -1
  8. package/dist/client/codes.d.ts +6 -1
  9. package/dist/client/codes.js +4 -1
  10. package/dist/client/codes.js.map +1 -1
  11. package/dist/client/content/types.d.ts +4 -2
  12. package/dist/client/content/types.js.map +1 -1
  13. package/dist/client/discovery.js +3 -0
  14. package/dist/client/discovery.js.map +1 -1
  15. package/dist/client/errors.d.ts +6 -0
  16. package/dist/client/errors.js +6 -2
  17. package/dist/client/errors.js.map +1 -1
  18. package/dist/client/gallery.d.ts +3 -5
  19. package/dist/client/gallery.js +16 -20
  20. package/dist/client/gallery.js.map +1 -1
  21. package/dist/client/generated/error-codes.d.ts +1 -1
  22. package/dist/client/generated/error-codes.js +9 -1
  23. package/dist/client/generated/error-codes.js.map +1 -1
  24. package/dist/client/generated/routes.d.ts +29 -3
  25. package/dist/client/generated/routes.js +44 -3
  26. package/dist/client/generated/routes.js.map +1 -1
  27. package/dist/client/generated/wire.d.ts +60 -10
  28. package/dist/client/generated/wire.js.map +1 -1
  29. package/dist/client/index.d.ts +2 -1
  30. package/dist/client/media/queue.d.ts +4 -2
  31. package/dist/client/media/queue.js +18 -12
  32. package/dist/client/media/queue.js.map +1 -1
  33. package/dist/client/media/rules.d.ts +7 -1
  34. package/dist/client/media/rules.js +27 -21
  35. package/dist/client/media/rules.js.map +1 -1
  36. package/dist/client/media/windows.js +1 -1
  37. package/dist/client/media/windows.js.map +1 -1
  38. package/dist/client/stats.d.ts +32 -0
  39. package/dist/client/stats.js +35 -0
  40. package/dist/client/stats.js.map +1 -0
  41. package/dist/client/versions.d.ts +6 -4
  42. package/dist/client/versions.js +24 -26
  43. package/dist/client/versions.js.map +1 -1
  44. package/dist/client.js +30 -29
  45. package/dist/components/admin/content-manager.js +206 -206
  46. package/dist/components/admin/content-manager.js.map +1 -1
  47. package/dist/components/admin/group-manager.js +71 -70
  48. package/dist/components/admin/group-manager.js.map +1 -1
  49. package/dist/components/admin/media-item-panel.d.ts +24 -7
  50. package/dist/components/admin/media-item-panel.js +209 -197
  51. package/dist/components/admin/media-item-panel.js.map +1 -1
  52. package/dist/components/admin/poll-manager.js +25 -25
  53. package/dist/components/admin/stats-dashboard.d.ts +22 -0
  54. package/dist/components/admin/stats-dashboard.js +257 -0
  55. package/dist/components/admin/stats-dashboard.js.map +1 -0
  56. package/dist/components/content/card.d.ts +5 -3
  57. package/dist/components/content/card.js +155 -133
  58. package/dist/components/content/card.js.map +1 -1
  59. package/dist/components/content/download-button.d.ts +26 -0
  60. package/dist/components/content/download-button.js +51 -0
  61. package/dist/components/content/download-button.js.map +1 -0
  62. package/dist/components/content/editors.js +455 -427
  63. package/dist/components/content/editors.js.map +1 -1
  64. package/dist/components/content/grid.d.ts +8 -1
  65. package/dist/components/content/grid.js +153 -104
  66. package/dist/components/content/grid.js.map +1 -1
  67. package/dist/components/content/index.d.ts +4 -2
  68. package/dist/components/content/listing.d.ts +3 -1
  69. package/dist/components/content/listing.js +34 -34
  70. package/dist/components/content/listing.js.map +1 -1
  71. package/dist/components/content/paywall.d.ts +17 -5
  72. package/dist/components/content/paywall.js +115 -100
  73. package/dist/components/content/paywall.js.map +1 -1
  74. package/dist/components/content/reader-stats.d.ts +12 -0
  75. package/dist/components/content/reader-stats.js +47 -0
  76. package/dist/components/content/reader-stats.js.map +1 -0
  77. package/dist/components/groups/banner.js +33 -33
  78. package/dist/components/groups/directory.js +11 -11
  79. package/dist/components/groups/editor.js +293 -293
  80. package/dist/components/groups/editor.js.map +1 -1
  81. package/dist/components/groups/header.js +27 -27
  82. package/dist/components/groups/header.js.map +1 -1
  83. package/dist/components/groups/memberships.js +180 -180
  84. package/dist/components/groups/parts.d.ts +0 -2
  85. package/dist/components/groups/parts.js +9 -27
  86. package/dist/components/groups/parts.js.map +1 -1
  87. package/dist/components/media-folder-editor.js +239 -189
  88. package/dist/components/media-folder-editor.js.map +1 -1
  89. package/dist/components/media-gallery.d.ts +1 -4
  90. package/dist/components/media-gallery.js +19 -34
  91. package/dist/components/media-gallery.js.map +1 -1
  92. package/dist/components/notifications.js +17 -17
  93. package/dist/components/slot-upload.d.ts +2 -2
  94. package/dist/components/slot-upload.js +84 -79
  95. package/dist/components/slot-upload.js.map +1 -1
  96. package/dist/components/social/bans.js +34 -34
  97. package/dist/components/social/comments.js +1 -1
  98. package/dist/components/social/comments.js.map +1 -1
  99. package/dist/components/social/engagement.d.ts +2 -1
  100. package/dist/components/social/engagement.js +5 -3
  101. package/dist/components/social/engagement.js.map +1 -1
  102. package/dist/components/social/moderation.js +52 -48
  103. package/dist/components/social/moderation.js.map +1 -1
  104. package/dist/components/video-player.d.ts +2 -0
  105. package/dist/components/video-player.js +318 -314
  106. package/dist/components/video-player.js.map +1 -1
  107. package/dist/i18n/context.d.ts +2 -0
  108. package/dist/i18n/context.js +23 -5
  109. package/dist/i18n/context.js.map +1 -1
  110. package/dist/index.d.ts +2 -1
  111. package/dist/index.js +13 -10
  112. package/dist/locales/admin/de.js +27 -0
  113. package/dist/locales/admin/de.js.map +1 -1
  114. package/dist/locales/admin/en.d.ts +27 -0
  115. package/dist/locales/admin/en.js +27 -0
  116. package/dist/locales/admin/en.js.map +1 -1
  117. package/dist/locales/admin/es.js +27 -0
  118. package/dist/locales/admin/es.js.map +1 -1
  119. package/dist/locales/admin/ja.js +27 -0
  120. package/dist/locales/admin/ja.js.map +1 -1
  121. package/dist/locales/admin/ko.js +27 -0
  122. package/dist/locales/admin/ko.js.map +1 -1
  123. package/dist/locales/admin/zh.js +27 -0
  124. package/dist/locales/admin/zh.js.map +1 -1
  125. package/dist/locales/content/de.js +6 -0
  126. package/dist/locales/content/de.js.map +1 -1
  127. package/dist/locales/content/en.d.ts +6 -0
  128. package/dist/locales/content/en.js +6 -0
  129. package/dist/locales/content/en.js.map +1 -1
  130. package/dist/locales/content/es.js +6 -0
  131. package/dist/locales/content/es.js.map +1 -1
  132. package/dist/locales/content/ja.js +6 -0
  133. package/dist/locales/content/ja.js.map +1 -1
  134. package/dist/locales/content/ko.js +6 -0
  135. package/dist/locales/content/ko.js.map +1 -1
  136. package/dist/locales/content/zh.js +6 -0
  137. package/dist/locales/content/zh.js.map +1 -1
  138. package/dist/locales/de.js +14 -2
  139. package/dist/locales/de.js.map +1 -1
  140. package/dist/locales/discovery/de.js +3 -0
  141. package/dist/locales/discovery/de.js.map +1 -1
  142. package/dist/locales/discovery/en.d.ts +3 -0
  143. package/dist/locales/discovery/en.js +3 -0
  144. package/dist/locales/discovery/en.js.map +1 -1
  145. package/dist/locales/discovery/es.js +3 -0
  146. package/dist/locales/discovery/es.js.map +1 -1
  147. package/dist/locales/discovery/ja.js +3 -0
  148. package/dist/locales/discovery/ja.js.map +1 -1
  149. package/dist/locales/discovery/ko.js +3 -0
  150. package/dist/locales/discovery/ko.js.map +1 -1
  151. package/dist/locales/discovery/zh.js +3 -0
  152. package/dist/locales/discovery/zh.js.map +1 -1
  153. package/dist/locales/en.d.ts +50 -2
  154. package/dist/locales/en.js +14 -2
  155. package/dist/locales/en.js.map +1 -1
  156. package/dist/locales/es.js +14 -2
  157. package/dist/locales/es.js.map +1 -1
  158. package/dist/locales/ja.js +14 -2
  159. package/dist/locales/ja.js.map +1 -1
  160. package/dist/locales/ko.js +14 -2
  161. package/dist/locales/ko.js.map +1 -1
  162. package/dist/locales/zh.js +14 -2
  163. package/dist/locales/zh.js.map +1 -1
  164. package/dist/react/admin.js +22 -23
  165. package/dist/react/admin.js.map +1 -1
  166. package/dist/react/canonical.d.ts +4 -3
  167. package/dist/react/canonical.js +34 -33
  168. package/dist/react/canonical.js.map +1 -1
  169. package/dist/react/comments.d.ts +1 -1
  170. package/dist/react/comments.js +3 -2
  171. package/dist/react/comments.js.map +1 -1
  172. package/dist/react/contents.js +144 -144
  173. package/dist/react/contents.js.map +1 -1
  174. package/dist/react/context.d.ts +1 -1
  175. package/dist/react/context.js.map +1 -1
  176. package/dist/react/detail.d.ts +12 -6
  177. package/dist/react/detail.js +31 -24
  178. package/dist/react/detail.js.map +1 -1
  179. package/dist/react/discovery.d.ts +11 -0
  180. package/dist/react/discovery.js +49 -30
  181. package/dist/react/discovery.js.map +1 -1
  182. package/dist/react/engagement.d.ts +6 -2
  183. package/dist/react/engagement.js +2 -6
  184. package/dist/react/engagement.js.map +1 -1
  185. package/dist/react/folder.d.ts +7 -2
  186. package/dist/react/folder.js +165 -147
  187. package/dist/react/folder.js.map +1 -1
  188. package/dist/react/groups.d.ts +0 -1
  189. package/dist/react/groups.js +13 -12
  190. package/dist/react/groups.js.map +1 -1
  191. package/dist/react/index.d.ts +6 -4
  192. package/dist/react/location.d.ts +11 -0
  193. package/dist/react/location.js +18 -0
  194. package/dist/react/location.js.map +1 -0
  195. package/dist/react/public.d.ts +8 -0
  196. package/dist/react/public.js +32 -27
  197. package/dist/react/public.js.map +1 -1
  198. package/dist/react/stats.d.ts +24 -0
  199. package/dist/react/stats.js +28 -0
  200. package/dist/react/stats.js.map +1 -0
  201. package/dist/react/use-resource.js +8 -7
  202. package/dist/react/use-resource.js.map +1 -1
  203. package/dist/react/versions.d.ts +25 -5
  204. package/dist/react/versions.js +61 -40
  205. package/dist/react/versions.js.map +1 -1
  206. package/dist/react.js +26 -24
  207. package/dist/styles.css +1 -1
  208. package/dist/urls/index.d.ts +35 -10
  209. package/dist/urls.js +100 -70
  210. package/dist/urls.js.map +1 -1
  211. package/package.json +1 -1
@@ -1,9 +1,9 @@
1
- import { useMessages as e } from "../../i18n/context.js";
2
- import { ContentKitUiRoot as t } from "../../scope.js";
3
- import { Button as n } from "../../ui/button.js";
4
- import { Select as r, SelectContent as i, SelectItem as a, SelectTrigger as o, SelectValue as s } from "../../ui/select.js";
5
- import { useKinds as c } from "../../react/groups.js";
6
- import { labelOf as l, useLanguageName as u } from "../groups/parts.js";
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 = e(), { t: k } = O, A = u(), { kinds: j } = c({ client: D.client }), [M, N] = m({}), P = S ?? M, F = f((e) => {
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(t, {
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(r, {
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(o, {
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(s, { children: (e) => b(O, e) })
70
- }), /* @__PURE__ */ h(i, { children: w.map((e) => /* @__PURE__ */ h(a, {
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(r, {
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(o, {
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(s, { children: (e) => e === y ? k("listing.anyLanguage") : A(e) })
86
- }), /* @__PURE__ */ g(i, { children: [/* @__PURE__ */ h(a, {
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(a, {
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 c = H(e);
96
- return t.type === "bool" ? /* @__PURE__ */ h(n, {
95
+ let n = H(e);
96
+ return t.type === "bool" ? /* @__PURE__ */ h(i, {
97
97
  size: "sm",
98
- variant: c === "true" ? "default" : "outline",
99
- "aria-pressed": c === "true",
100
- onClick: () => U(e, c === "true" ? void 0 : "true"),
101
- children: l(D.labels, e)
102
- }, e) : /* @__PURE__ */ g(r, {
103
- value: c ?? y,
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(o, {
105
+ children: [/* @__PURE__ */ h(c, {
106
106
  size: "sm",
107
- "aria-label": l(D.labels, e),
107
+ "aria-label": u(D.labels, e),
108
108
  "data-ckui": "listing-attribute",
109
109
  "data-name": e,
110
- children: /* @__PURE__ */ h(s, { children: (t) => t === y ? l(D.labels, e) : l(D.labels, t) })
111
- }), /* @__PURE__ */ g(i, { children: [/* @__PURE__ */ h(a, {
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(a, {
114
+ }), t.values.map((e) => /* @__PURE__ */ h(s, {
115
115
  value: e,
116
- children: l(D.labels, e)
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(n, {
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: l(D.labels, e)
128
+ children: u(D.labels, e)
129
129
  }, e);
130
130
  }),
131
- G && /* @__PURE__ */ h(n, {
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: string | null | undefined;
45
- onChange: (version: string) => void;
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
- /** Chooses among a content's versions: a language first when they differ in language, then its variants (traits, labels). */
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[], value: string | null | undefined, onChange: (version: string) => void, labels?: Labels): PlayerMenuItem[];
65
+ export declare function useVersionMenuItems(versions: readonly Version[], o?: VersionMenuOptions): PlayerMenuItem[];
@@ -1,18 +1,20 @@
1
- import { useMessages as e } from "../../i18n/context.js";
2
- import { ContentKitUiRoot as t } from "../../scope.js";
3
- import { ContentKitContext as n, useContentKitClient as r } from "../../react/context.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 { ErrorLine as u, contentError as d, formatDate as f, useAction as p } from "../social/parts.js";
7
- import { useKinds as m } from "../../react/groups.js";
8
- import { labelOf as h, useLanguageName as g } from "../groups/parts.js";
9
- import { useContext as _, useMemo as v } from "react";
10
- import { jsx as y, jsxs as b } from "react/jsx-runtime";
11
- import { cn as x } from "cn";
12
- import { LockIcon as S } from "@hugeicons/core-free-icons";
13
- import { HugeiconsIcon as C } from "@hugeicons/react";
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 w(e, t) {
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 T({ decision: a, onCheckout: o, tiers: s, onSignIn: c, labels: l, children: g, client: v, onError: T, className: E, appearance: D }) {
35
- let O = e(), { t: k } = O, A = _(n), j = r(v), { kinds: M } = m({ client: v }), N = p("membership.join", T);
36
- if (a.allowed) return null;
37
- let P = c ?? A?.onSignIn, F = a.offers ?? [], I = new Set(F.map((e) => e.key)), L = M?.tiers ?? Object.keys(s ?? {}), R = (a.sell ?? []).filter((e) => !I.has(e) && (L.includes(e) || !!s?.[e])), z = a.requires, B = z && !F.some((e) => e.key === z) ? z : void 0, V = (e) => !!z && e !== z, H = a.preview;
38
- return /* @__PURE__ */ b(t, {
39
- appearance: D,
40
- className: x("grid gap-3 rounded-lg border border-border p-4 text-sm", E),
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__ */ b("p", {
45
+ /* @__PURE__ */ C("p", {
44
46
  className: "flex items-center gap-2 font-medium",
45
- children: [/* @__PURE__ */ y(C, {
46
- icon: S,
47
+ children: [/* @__PURE__ */ S(E, {
48
+ icon: T,
47
49
  strokeWidth: 2,
48
50
  className: "size-4",
49
51
  "aria-hidden": !0
50
- }), a.unknown ? k("paywall.unknown") : k(z ? "paywall.requiresFirst" : "paywall.locked")]
52
+ }), e.unknown ? k("paywall.unknown") : k(z ? "paywall.requiresFirst" : "paywall.locked")]
51
53
  }),
52
- H?.pages ? /* @__PURE__ */ y("p", {
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__ */ y("p", {
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
- a.free_after && /* @__PURE__ */ y("p", {
62
+ e.free_after && /* @__PURE__ */ S("p", {
61
63
  className: "text-muted-foreground",
62
- children: k("paywall.freeFrom", { date: f(O, a.free_after, !1) })
64
+ children: k("paywall.freeFrom", { date: m(O, e.free_after, !1) })
63
65
  }),
64
- !a.unknown && /* @__PURE__ */ b("div", {
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__ */ y(i, {
70
+ B && /* @__PURE__ */ S(o, {
69
71
  size: "sm",
70
72
  variant: "outline",
71
- onClick: () => o({ key: B }),
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__ */ y(i, {
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__ */ y(i, {
84
+ }, e.key) : /* @__PURE__ */ S(o, {
83
85
  size: "sm",
84
86
  disabled: V(e.key),
85
- onClick: () => o({
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: w(O, e.terms) })
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__ */ b(i, {
95
+ R.map((e) => /* @__PURE__ */ C(o, {
94
96
  size: "sm",
95
97
  variant: F.length ? "outline" : "default",
96
- onClick: () => o({ key: e }),
98
+ onClick: () => s({ key: e }),
97
99
  "data-ckui": "paywall-tier",
98
100
  "data-tier": e,
99
- children: [k("paywall.subscribe", { tier: s?.[e]?.label ?? h(l, e) }), s?.[e]?.price && /* @__PURE__ */ y("span", {
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: s[e].price
103
+ children: c[e].price
102
104
  })]
103
105
  }, e))
104
106
  ]
105
107
  }),
106
- N.error && /* @__PURE__ */ y(u, { children: d(O, N.error) }),
107
- A?.viewer === null && P && /* @__PURE__ */ b("p", {
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__ */ y("button", {
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
- g
122
+ d
121
123
  ]
122
124
  });
123
125
  }
124
- function E(e, t, n) {
125
- return e.label || (e.traits.length ? e.traits.map((e) => h(n, e)).join(" · ") : e.languages.map(t).join(", "));
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 D = (e) => e.languages[0] ?? "";
128
- function O(e, t) {
129
- let n = e.find((e) => e.id === t) ?? e[0], r = D(n);
130
- return {
131
- current: n,
132
- language: r,
133
- languages: [...new Set(e.map(D))],
134
- variants: e.filter((e) => D(e) === r)
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 k({ versions: n, value: r, onChange: u, labels: d, className: f, appearance: p }) {
138
- let { t: m } = e(), h = g();
139
- if (n.length < 2) return null;
140
- let { current: _, language: v, languages: w, variants: T } = O(n, r);
141
- return /* @__PURE__ */ b(t, {
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: [w.length > 1 && /* @__PURE__ */ b(a, {
154
+ children: [y.length > 1 && /* @__PURE__ */ C(s, {
146
155
  value: v,
147
156
  onValueChange: (e) => {
148
- let t = n.find((t) => D(t) === e);
149
- t && u(t.id);
157
+ let t = r.find((t) => A(t) === e);
158
+ t && x(t);
150
159
  },
151
- children: [/* @__PURE__ */ y(c, {
160
+ children: [/* @__PURE__ */ S(u, {
152
161
  size: "sm",
153
- "aria-label": m("versions.language"),
154
- children: /* @__PURE__ */ y(l, { children: (e) => h(e) })
155
- }), /* @__PURE__ */ y(o, { children: w.map((e) => /* @__PURE__ */ y(s, {
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: h(e)
166
+ children: g(e)
158
167
  }, e)) })]
159
- }), T.length > 1 && /* @__PURE__ */ y("div", {
168
+ }), b.length > 1 && /* @__PURE__ */ S("div", {
160
169
  role: "radiogroup",
161
- "aria-label": m("versions.version"),
170
+ "aria-label": h("versions.version"),
162
171
  className: "flex flex-wrap gap-1",
163
- children: T.map((e) => /* @__PURE__ */ b(i, {
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: () => u(e.id),
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__ */ y(C, {
172
- icon: S,
180
+ children: [!e.decision.allowed && /* @__PURE__ */ S(E, {
181
+ icon: T,
173
182
  strokeWidth: 2,
174
183
  className: "size-3.5",
175
- "aria-label": m("versions.locked")
176
- }), E(e, h, d)]
184
+ "aria-label": h("versions.locked")
185
+ }), k(e, g, f)]
177
186
  }, e.id))
178
187
  })]
179
188
  });
180
189
  }
181
- function A(t, n, r, i) {
182
- let { t: a } = e(), o = g();
183
- return v(() => {
184
- if (t.length < 2) return [];
185
- let { current: e, language: s, languages: c, variants: l } = O(t, n), u = [];
186
- return c.length > 1 && u.push({
187
- label: a("versions.language"),
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: o(e)
200
+ label: a(e)
192
201
  })),
193
202
  onChange: (e) => {
194
- let n = t.find((t) => D(t) === e);
195
- n && r(n.id);
203
+ let t = n.find((t) => A(t) === e);
204
+ t && u(t);
196
205
  }
197
- }), l.length > 1 && u.push({
198
- label: a("versions.version"),
199
- value: e.id,
206
+ }), l.length > 1 && e.push({
207
+ label: i("versions.version"),
208
+ value: o.id,
200
209
  options: l.map((e) => {
201
- let t = E(e, o, i);
210
+ let t = k(e, a, d);
202
211
  return {
203
212
  value: e.id,
204
- label: e.decision.allowed ? t : a("versions.lockedOption", { name: t })
213
+ label: e.decision.allowed ? t : i("versions.lockedOption", { name: t })
205
214
  };
206
215
  }),
207
- onChange: r
208
- }), u;
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
- r,
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 { T as Paywall, k as VersionPicker, w as priceLabel, A as useVersionMenuItems };
234
+ export { O as Paywall, M as VersionPicker, D as priceLabel, N as useVersionMenuItems };
220
235
 
221
236
  //# sourceMappingURL=paywall.js.map