@openrails/contentkit-ui 0.83.0 → 0.86.0

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 (219) hide show
  1. package/README.md +18 -7
  2. package/dist/client/beacons.d.ts +18 -6
  3. package/dist/client/beacons.js +26 -11
  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/moderation.d.ts +14 -2
  12. package/dist/client/content/moderation.js +34 -0
  13. package/dist/client/content/moderation.js.map +1 -1
  14. package/dist/client/content/types.d.ts +13 -0
  15. package/dist/client/content/types.js.map +1 -1
  16. package/dist/client/discovery.d.ts +6 -0
  17. package/dist/client/discovery.js.map +1 -1
  18. package/dist/client/generated/error-codes.d.ts +1 -1
  19. package/dist/client/generated/error-codes.js +9 -1
  20. package/dist/client/generated/error-codes.js.map +1 -1
  21. package/dist/client/generated/routes.d.ts +91 -13
  22. package/dist/client/generated/routes.js +123 -2
  23. package/dist/client/generated/routes.js.map +1 -1
  24. package/dist/client/generated/wire.d.ts +81 -0
  25. package/dist/client/generated/wire.js.map +1 -1
  26. package/dist/client/index.d.ts +4 -2
  27. package/dist/client/media/queue.d.ts +4 -2
  28. package/dist/client/media/queue.js +18 -12
  29. package/dist/client/media/queue.js.map +1 -1
  30. package/dist/client/media/rules.d.ts +7 -1
  31. package/dist/client/media/rules.js +27 -21
  32. package/dist/client/media/rules.js.map +1 -1
  33. package/dist/client/reader-meta.d.ts +27 -0
  34. package/dist/client/reader-meta.js +99 -0
  35. package/dist/client/reader-meta.js.map +1 -0
  36. package/dist/client/stats.d.ts +32 -0
  37. package/dist/client/stats.js +35 -0
  38. package/dist/client/stats.js.map +1 -0
  39. package/dist/client/versions.d.ts +1 -1
  40. package/dist/client/versions.js +7 -2
  41. package/dist/client/versions.js.map +1 -1
  42. package/dist/client.js +30 -28
  43. package/dist/components/admin/content-manager.js +206 -206
  44. package/dist/components/admin/content-manager.js.map +1 -1
  45. package/dist/components/admin/group-manager.js +71 -70
  46. package/dist/components/admin/group-manager.js.map +1 -1
  47. package/dist/components/admin/media-item-panel.d.ts +24 -7
  48. package/dist/components/admin/media-item-panel.js +209 -197
  49. package/dist/components/admin/media-item-panel.js.map +1 -1
  50. package/dist/components/admin/poll-manager.js +25 -25
  51. package/dist/components/admin/reader-manifest.d.ts +18 -0
  52. package/dist/components/admin/reader-manifest.js +490 -0
  53. package/dist/components/admin/reader-manifest.js.map +1 -0
  54. package/dist/components/admin/stats-dashboard.d.ts +22 -0
  55. package/dist/components/admin/stats-dashboard.js +257 -0
  56. package/dist/components/admin/stats-dashboard.js.map +1 -0
  57. package/dist/components/content/card.js +43 -43
  58. package/dist/components/content/card.js.map +1 -1
  59. package/dist/components/content/editors.d.ts +2 -1
  60. package/dist/components/content/editors.js +479 -416
  61. package/dist/components/content/editors.js.map +1 -1
  62. package/dist/components/content/grid.js +47 -47
  63. package/dist/components/content/index.d.ts +2 -1
  64. package/dist/components/content/listing.js +34 -34
  65. package/dist/components/content/listing.js.map +1 -1
  66. package/dist/components/content/paywall.d.ts +17 -5
  67. package/dist/components/content/paywall.js +115 -100
  68. package/dist/components/content/paywall.js.map +1 -1
  69. package/dist/components/content/reader-stats.d.ts +12 -0
  70. package/dist/components/content/reader-stats.js +47 -0
  71. package/dist/components/content/reader-stats.js.map +1 -0
  72. package/dist/components/groups/banner.d.ts +1 -1
  73. package/dist/components/groups/banner.js +34 -34
  74. package/dist/components/groups/banner.js.map +1 -1
  75. package/dist/components/groups/directory.js +11 -11
  76. package/dist/components/groups/editor.js +294 -294
  77. package/dist/components/groups/editor.js.map +1 -1
  78. package/dist/components/groups/header.js +27 -27
  79. package/dist/components/groups/header.js.map +1 -1
  80. package/dist/components/groups/memberships.js +180 -180
  81. package/dist/components/groups/parts.d.ts +2 -4
  82. package/dist/components/groups/parts.js +69 -88
  83. package/dist/components/groups/parts.js.map +1 -1
  84. package/dist/components/media-folder-editor.js +239 -189
  85. package/dist/components/media-folder-editor.js.map +1 -1
  86. package/dist/components/notifications.js +17 -17
  87. package/dist/components/slot-upload.d.ts +2 -2
  88. package/dist/components/slot-upload.js +84 -79
  89. package/dist/components/slot-upload.js.map +1 -1
  90. package/dist/components/social/bans.js +34 -34
  91. package/dist/components/social/comments.js +59 -45
  92. package/dist/components/social/comments.js.map +1 -1
  93. package/dist/components/social/index.d.ts +1 -0
  94. package/dist/components/social/moderation.d.ts +5 -4
  95. package/dist/components/social/moderation.js +177 -160
  96. package/dist/components/social/moderation.js.map +1 -1
  97. package/dist/components/social/parts.js +22 -14
  98. package/dist/components/social/parts.js.map +1 -1
  99. package/dist/components/social/rules.d.ts +23 -0
  100. package/dist/components/social/rules.js +261 -0
  101. package/dist/components/social/rules.js.map +1 -0
  102. package/dist/components/video-player.d.ts +2 -0
  103. package/dist/components/video-player.js +318 -314
  104. package/dist/components/video-player.js.map +1 -1
  105. package/dist/i18n/context.d.ts +2 -0
  106. package/dist/i18n/context.js +23 -5
  107. package/dist/i18n/context.js.map +1 -1
  108. package/dist/index.d.ts +3 -1
  109. package/dist/index.js +28 -24
  110. package/dist/locales/admin/de.js +105 -0
  111. package/dist/locales/admin/de.js.map +1 -1
  112. package/dist/locales/admin/en.d.ts +105 -0
  113. package/dist/locales/admin/en.js +105 -0
  114. package/dist/locales/admin/en.js.map +1 -1
  115. package/dist/locales/admin/es.js +105 -0
  116. package/dist/locales/admin/es.js.map +1 -1
  117. package/dist/locales/admin/ja.js +105 -0
  118. package/dist/locales/admin/ja.js.map +1 -1
  119. package/dist/locales/admin/ko.js +105 -0
  120. package/dist/locales/admin/ko.js.map +1 -1
  121. package/dist/locales/admin/zh.js +105 -0
  122. package/dist/locales/admin/zh.js.map +1 -1
  123. package/dist/locales/content/de.js +6 -0
  124. package/dist/locales/content/de.js.map +1 -1
  125. package/dist/locales/content/en.d.ts +6 -0
  126. package/dist/locales/content/en.js +6 -0
  127. package/dist/locales/content/en.js.map +1 -1
  128. package/dist/locales/content/es.js +6 -0
  129. package/dist/locales/content/es.js.map +1 -1
  130. package/dist/locales/content/ja.js +6 -0
  131. package/dist/locales/content/ja.js.map +1 -1
  132. package/dist/locales/content/ko.js +6 -0
  133. package/dist/locales/content/ko.js.map +1 -1
  134. package/dist/locales/content/zh.js +6 -0
  135. package/dist/locales/content/zh.js.map +1 -1
  136. package/dist/locales/de.js +4 -1
  137. package/dist/locales/de.js.map +1 -1
  138. package/dist/locales/discovery/de.js +1 -0
  139. package/dist/locales/discovery/de.js.map +1 -1
  140. package/dist/locales/discovery/en.d.ts +1 -0
  141. package/dist/locales/discovery/en.js +1 -0
  142. package/dist/locales/discovery/en.js.map +1 -1
  143. package/dist/locales/discovery/es.js +1 -0
  144. package/dist/locales/discovery/es.js.map +1 -1
  145. package/dist/locales/discovery/ja.js +1 -0
  146. package/dist/locales/discovery/ja.js.map +1 -1
  147. package/dist/locales/discovery/ko.js +1 -0
  148. package/dist/locales/discovery/ko.js.map +1 -1
  149. package/dist/locales/discovery/zh.js +1 -0
  150. package/dist/locales/discovery/zh.js.map +1 -1
  151. package/dist/locales/en.d.ts +118 -0
  152. package/dist/locales/en.js +4 -1
  153. package/dist/locales/en.js.map +1 -1
  154. package/dist/locales/es.js +4 -1
  155. package/dist/locales/es.js.map +1 -1
  156. package/dist/locales/ja.js +4 -1
  157. package/dist/locales/ja.js.map +1 -1
  158. package/dist/locales/ko.js +4 -1
  159. package/dist/locales/ko.js.map +1 -1
  160. package/dist/locales/social/de.js +3 -0
  161. package/dist/locales/social/de.js.map +1 -1
  162. package/dist/locales/social/en.d.ts +3 -0
  163. package/dist/locales/social/en.js +3 -0
  164. package/dist/locales/social/en.js.map +1 -1
  165. package/dist/locales/social/es.js +3 -0
  166. package/dist/locales/social/es.js.map +1 -1
  167. package/dist/locales/social/ja.js +3 -0
  168. package/dist/locales/social/ja.js.map +1 -1
  169. package/dist/locales/social/ko.js +3 -0
  170. package/dist/locales/social/ko.js.map +1 -1
  171. package/dist/locales/social/zh.js +3 -0
  172. package/dist/locales/social/zh.js.map +1 -1
  173. package/dist/locales/zh.js +4 -1
  174. package/dist/locales/zh.js.map +1 -1
  175. package/dist/react/admin.d.ts +1 -1
  176. package/dist/react/admin.js +24 -23
  177. package/dist/react/admin.js.map +1 -1
  178. package/dist/react/canonical.d.ts +4 -3
  179. package/dist/react/canonical.js +34 -33
  180. package/dist/react/canonical.js.map +1 -1
  181. package/dist/react/comments.d.ts +29 -1
  182. package/dist/react/comments.js +30 -2
  183. package/dist/react/comments.js.map +1 -1
  184. package/dist/react/contents.js +144 -144
  185. package/dist/react/contents.js.map +1 -1
  186. package/dist/react/context.d.ts +1 -1
  187. package/dist/react/context.js.map +1 -1
  188. package/dist/react/detail.d.ts +12 -6
  189. package/dist/react/detail.js +31 -24
  190. package/dist/react/detail.js.map +1 -1
  191. package/dist/react/discovery.js +1 -1
  192. package/dist/react/discovery.js.map +1 -1
  193. package/dist/react/folder.d.ts +7 -2
  194. package/dist/react/folder.js +165 -147
  195. package/dist/react/folder.js.map +1 -1
  196. package/dist/react/groups.d.ts +0 -1
  197. package/dist/react/groups.js +13 -12
  198. package/dist/react/groups.js.map +1 -1
  199. package/dist/react/index.d.ts +5 -3
  200. package/dist/react/location.d.ts +11 -0
  201. package/dist/react/location.js +18 -0
  202. package/dist/react/location.js.map +1 -0
  203. package/dist/react/public.d.ts +8 -0
  204. package/dist/react/public.js +32 -27
  205. package/dist/react/public.js.map +1 -1
  206. package/dist/react/resources.js +6 -0
  207. package/dist/react/resources.js.map +1 -1
  208. package/dist/react/stats.d.ts +24 -0
  209. package/dist/react/stats.js +28 -0
  210. package/dist/react/stats.js.map +1 -0
  211. package/dist/react/use-resource.js +8 -7
  212. package/dist/react/use-resource.js.map +1 -1
  213. package/dist/react/versions.js +20 -20
  214. package/dist/react.js +26 -24
  215. package/dist/styles.css +1 -1
  216. package/dist/urls/index.d.ts +35 -10
  217. package/dist/urls.js +100 -70
  218. package/dist/urls.js.map +1 -1
  219. package/package.json +1 -1
@@ -2,22 +2,22 @@ import { useMessages as e } from "../../i18n/context.js";
2
2
  import { ContentKitUiRoot as t } from "../../scope.js";
3
3
  import { ContentKitContext as n, useContentKitClient as r } from "../../react/context.js";
4
4
  import { decodeImage as i } from "../../client/image.js";
5
- import { Button as a } from "../../ui/button.js";
6
- import { ImageCropDialog as o } from "../image-crop-dialog.js";
7
- import { Badge as s } from "../../ui/badge.js";
8
- import { Skeleton as c } from "../../ui/skeleton.js";
9
- import { ErrorLine as l, contentError as u, useAction as d } from "../social/parts.js";
10
- import { useGroup as f } from "../../react/groups.js";
5
+ import { useGroup as a } from "../../react/groups.js";
6
+ import { Button as o } from "../../ui/button.js";
7
+ import { ImageCropDialog as s } from "../image-crop-dialog.js";
8
+ import { Badge as c } from "../../ui/badge.js";
9
+ import { Skeleton as l } from "../../ui/skeleton.js";
10
+ import { ErrorLine as u, contentError as d, useAction as f } from "../social/parts.js";
11
11
  import { GroupAvatar as p, GroupCover as m } from "./parts.js";
12
12
  import { priceLabel as h } from "../content/paywall.js";
13
13
  import { useContext as g, useRef as _, useState as v } from "react";
14
14
  import { jsx as y, jsxs as b } from "react/jsx-runtime";
15
15
  import { cn as x } from "cn";
16
16
  //#region src/components/groups/banner.tsx
17
- function S({ group: i, onCheckout: o, onSignIn: c, client: f, onError: p, className: m, appearance: _ }) {
18
- let v = e(), { t: S } = v, C = r(f), w = g(n), T = d("membership.join", p), E = i.access;
19
- if (!E || E.editor) return null;
20
- let D = w?.viewer === null, O = c ?? w?.onSignIn, k = E.offer && Number(E.offer.terms.amount) > 0 ? E.offer : void 0;
17
+ function S({ group: i, onCheckout: a, onSignIn: s, client: l, onError: p, className: m, appearance: _ }) {
18
+ let v = e(), { t: S } = v, C = r(l), w = g(n), T = f("membership.join", p), E = i.access;
19
+ if (!E || E.editor || !E.offer && !E.member && !E.unknown) return null;
20
+ let D = w?.viewer === null, O = s ?? w?.onSignIn, k = E.offer && Number(E.offer.terms.amount) > 0 ? E.offer : void 0;
21
21
  return /* @__PURE__ */ b(t, {
22
22
  appearance: _,
23
23
  className: x("grid gap-2 rounded-lg border border-border p-3 text-sm", m),
@@ -28,7 +28,7 @@ function S({ group: i, onCheckout: o, onSignIn: c, client: f, onError: p, classN
28
28
  children: S("membership.unknown")
29
29
  }) : E.member ? /* @__PURE__ */ b("div", {
30
30
  className: "flex flex-wrap items-center gap-2",
31
- children: [/* @__PURE__ */ y(s, { children: S("membership.member") }), !k && /* @__PURE__ */ y(a, {
31
+ children: [/* @__PURE__ */ y(c, { children: S("membership.member") }), !k && /* @__PURE__ */ y(o, {
32
32
  size: "sm",
33
33
  variant: "ghost",
34
34
  disabled: T.pending,
@@ -40,13 +40,13 @@ function S({ group: i, onCheckout: o, onSignIn: c, client: f, onError: p, classN
40
40
  children: [/* @__PURE__ */ y("p", {
41
41
  className: "text-muted-foreground",
42
42
  children: S(k ? "membership.paidPitch" : "membership.freePitch")
43
- }), /* @__PURE__ */ y(a, {
43
+ }), /* @__PURE__ */ y(o, {
44
44
  size: "sm",
45
45
  className: "justify-self-start",
46
- disabled: T.pending || !!k && !o,
46
+ disabled: T.pending || !!k && !a,
47
47
  onClick: () => {
48
48
  if (D && O) return O();
49
- if (k) return o?.({
49
+ if (k) return a?.({
50
50
  key: E.key,
51
51
  offer: k
52
52
  });
@@ -54,69 +54,69 @@ function S({ group: i, onCheckout: o, onSignIn: c, client: f, onError: p, classN
54
54
  },
55
55
  children: k ? S("membership.joinFor", { price: h(v, k.terms) }) : S("membership.join")
56
56
  })]
57
- }), T.error && /* @__PURE__ */ y(l, { children: u(v, T.error) })]
57
+ }), T.error && /* @__PURE__ */ y(u, { children: d(v, T.error) })]
58
58
  });
59
59
  }
60
60
  function C(n) {
61
- let r = e(), i = f(n.group ? null : n.id, {
61
+ let r = e(), i = a(n.group ? null : n.id, {
62
62
  language: n.language,
63
63
  client: n.client
64
- }), a = n.group ?? i.group;
65
- if (!a) return i.error ? /* @__PURE__ */ y(l, { children: u(r, i.error) }) : /* @__PURE__ */ y(c, { className: "h-48 w-full" });
66
- let o = a.text.find((e) => e.language === (n.language ?? a.name_language)) ?? a.text.find((e) => e.description), s = a.counts.filter((e) => e.language === (n.language ?? "")).reduce((e, t) => e + t.count, 0);
64
+ }), o = n.group ?? i.group;
65
+ if (!o) return i.error ? /* @__PURE__ */ y(u, { children: d(r, i.error) }) : /* @__PURE__ */ y(l, { className: "h-48 w-full" });
66
+ let s = o.text.find((e) => e.language === (n.language ?? o.name_language)) ?? o.text.find((e) => e.description), c = o.counts.filter((e) => e.language === (n.language ?? "")).reduce((e, t) => e + t.count, 0);
67
67
  return /* @__PURE__ */ b(t, {
68
68
  appearance: n.appearance,
69
69
  className: x("grid gap-3 text-sm", n.className),
70
70
  "data-ckui": "group-banner",
71
- "data-kind": a.kind,
71
+ "data-kind": o.kind,
72
72
  children: [
73
73
  /* @__PURE__ */ y(m, {
74
- group: a,
74
+ group: o,
75
75
  className: "aspect-[3/1] w-full"
76
76
  }),
77
77
  /* @__PURE__ */ b("div", {
78
78
  className: "flex flex-wrap items-end gap-4 px-2 sm:px-4",
79
79
  children: [
80
80
  /* @__PURE__ */ y(p, {
81
- group: a,
81
+ group: o,
82
82
  className: "-mt-12 size-20 border-4 border-background sm:-mt-16 sm:size-28"
83
83
  }),
84
84
  /* @__PURE__ */ b("div", {
85
85
  className: "grid min-w-0 flex-1 gap-1",
86
86
  children: [/* @__PURE__ */ y("h1", {
87
87
  className: "text-xl font-semibold break-words",
88
- children: a.name || a.slug
88
+ children: o.name || o.slug
89
89
  }), /* @__PURE__ */ y("p", {
90
90
  className: "text-xs text-muted-foreground tabular-nums",
91
- children: r.plural("groups.items", s)
91
+ children: r.plural("groups.items", c)
92
92
  })]
93
93
  }),
94
94
  /* @__PURE__ */ b("div", {
95
95
  className: "flex flex-wrap items-start gap-2",
96
96
  children: [/* @__PURE__ */ y(S, {
97
- group: a,
97
+ group: o,
98
98
  onCheckout: n.onCheckout,
99
99
  client: n.client
100
100
  }), n.children]
101
101
  })
102
102
  ]
103
103
  }),
104
- o?.description && /* @__PURE__ */ y("p", {
104
+ s?.description && /* @__PURE__ */ y("p", {
105
105
  className: "px-2 break-words whitespace-pre-line sm:px-4",
106
- children: o.description
106
+ children: s.description
107
107
  })
108
108
  ]
109
109
  });
110
110
  }
111
- function w({ group: t, slot: n, crop: r = !0, client: s, onError: c, className: u }) {
112
- let { t: h } = e(), g = f(t.id, { client: s }), S = _(null), C = d("group.save", c), [w, T] = v(null), [E, D] = v(null), O = g.group ?? t, k = n === "cover" ? !!O.cover?.image || !!O.cover?.content_id && !O.cover.auto : !!O.avatar?.image, A = (e, t) => C.run(() => g.uploadImage(n, e, {
111
+ function w({ group: t, slot: n, crop: r = !0, client: c, onError: l, className: d }) {
112
+ let { t: h } = e(), g = a(t.id, { client: c }), S = _(null), C = f("group.save", l), [w, T] = v(null), [E, D] = v(null), O = g.group ?? t, k = n === "cover" ? !!O.cover?.image || !!O.cover?.content_id && !O.cover.auto : !!O.avatar?.image, A = (e, t) => C.run(() => g.uploadImage(n, e, {
113
113
  wait: !1,
114
114
  edit: t
115
115
  })), j = () => {
116
116
  w?.revoke?.(), T(null), D(null);
117
117
  };
118
118
  return /* @__PURE__ */ b("div", {
119
- className: x("flex flex-wrap items-center gap-3", u),
119
+ className: x("flex flex-wrap items-center gap-3", d),
120
120
  "data-ckui": "group-image-editor",
121
121
  "data-slot": n,
122
122
  children: [
@@ -134,14 +134,14 @@ function w({ group: t, slot: n, crop: r = !0, client: s, onError: c, className:
134
134
  children: [
135
135
  /* @__PURE__ */ b("div", {
136
136
  className: "flex flex-wrap gap-2",
137
- children: [/* @__PURE__ */ y(a, {
137
+ children: [/* @__PURE__ */ y(o, {
138
138
  type: "button",
139
139
  size: "sm",
140
140
  variant: "outline",
141
141
  disabled: C.pending,
142
142
  onClick: () => S.current?.click(),
143
143
  children: C.pending ? h("groupEditor.uploading") : h(n === "avatar" ? "groupEditor.uploadAvatar" : "groupEditor.uploadCover")
144
- }), k && /* @__PURE__ */ y(a, {
144
+ }), k && /* @__PURE__ */ y(o, {
145
145
  type: "button",
146
146
  size: "sm",
147
147
  variant: "ghost",
@@ -166,10 +166,10 @@ function w({ group: t, slot: n, crop: r = !0, client: s, onError: c, className:
166
166
  }
167
167
  }
168
168
  }),
169
- /* @__PURE__ */ y(l, { error: C.error })
169
+ /* @__PURE__ */ y(u, { error: C.error })
170
170
  ]
171
171
  }),
172
- /* @__PURE__ */ y(o, {
172
+ /* @__PURE__ */ y(s, {
173
173
  open: !!w,
174
174
  onOpenChange: (e) => !e && j(),
175
175
  source: w,
@@ -1 +1 @@
1
- {"version":3,"file":"banner.js","names":[],"sources":["../../../src/components/groups/banner.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useContext, useRef, useState, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Edit, GroupAccess, GroupDetail, Sale } from \"../../client/generated/wire.js\";\nimport type { GroupImageSlot } from \"../../client/groups.js\";\nimport { decodeImage, type CropSource } from \"../../client/image.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext, useContentKitClient, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useGroup } from \"../../react/groups.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { ImageCropDialog } from \"../image-crop-dialog.js\";\nimport { contentError, ErrorLine, useAction } from \"../social/parts.js\";\nimport { priceLabel } from \"../content/paywall.js\";\nimport { GroupAvatar, GroupCover, type GroupLike } from \"./parts.js\";\n\nexport interface MembershipBoxProps {\n group: Pick<GroupDetail, \"id\" | \"kind\" | \"name\"> & { access?: GroupAccess };\n /** Buys a sold membership: the host creates the order through billing-ui. */\n onCheckout?: (o: { key: string; offer: Sale }) => void;\n /** Asks a signed-out visitor to sign in; default the provider's. */\n onSignIn?: () => void;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** A group's membership for the reader: member, join free, or join for its price. */\nexport function MembershipBox({ group, onCheckout, onSignIn, client: own, onError, className, appearance }: MembershipBoxProps) {\n const m = useMessages();\n const { t } = m;\n const client = useContentKitClient(own);\n const ctx = useContext(ContentKitContext);\n const join = useAction(\"membership.join\", onError);\n const a = group.access;\n if (!a || a.editor) return null;\n const signedOut = ctx?.viewer === null;\n const signIn = onSignIn ?? ctx?.onSignIn;\n const offer: Sale | undefined = a.offer && Number(a.offer.terms.amount) > 0 ? a.offer : undefined;\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-2 rounded-lg border border-border p-3 text-sm\", className)} data-ckui=\"membership-box\" data-member={a.member}>\n {a.unknown ? (\n <p className=\"text-muted-foreground\">{t(\"membership.unknown\")}</p>\n ) : a.member ? (\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge>{t(\"membership.member\")}</Badge>\n {!offer && (\n <Button size=\"sm\" variant=\"ghost\" disabled={join.pending} onClick={() => void join.run(() => client.offers.join(group.id, false))}>\n {t(\"membership.leave\")}\n </Button>\n )}\n </div>\n ) : (\n <div className=\"grid gap-2\">\n <p className=\"text-muted-foreground\">{t(offer ? \"membership.paidPitch\" : \"membership.freePitch\")}</p>\n <Button\n size=\"sm\"\n className=\"justify-self-start\"\n disabled={join.pending || (!!offer && !onCheckout)}\n onClick={() => {\n if (signedOut && signIn) return signIn();\n if (offer) return onCheckout?.({ key: a.key, offer });\n void join.run(() => client.offers.join(group.id));\n }}\n >\n {offer ? t(\"membership.joinFor\", { price: priceLabel(m, offer.terms) }) : t(\"membership.join\")}\n </Button>\n </div>\n )}\n {join.error && <ErrorLine>{contentError(m, join.error)}</ErrorLine>}\n </ContentKitUiRoot>\n );\n}\n\nexport interface GroupBannerProps {\n /** The group the host already read; or give id. */\n group?: GroupDetail;\n id?: string;\n language?: string;\n onCheckout?: MembershipBoxProps[\"onCheckout\"];\n /** Beside the membership box: the host's actions (follow, share, edit). */\n children?: ReactNode;\n client?: ContentKitClient;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** A group's page head: its cover across, its avatar, name, description, content count and membership. */\nexport function GroupBanner(p: GroupBannerProps) {\n const m = useMessages();\n const read = useGroup(p.group ? null : p.id, { language: p.language, client: p.client });\n const g = p.group ?? read.group;\n if (!g) return read.error ? <ErrorLine>{contentError(m, read.error)}</ErrorLine> : <Skeleton className=\"h-48 w-full\" />;\n const text = g.text.find((x) => x.language === (p.language ?? g.name_language)) ?? g.text.find((x) => x.description);\n const count = g.counts.filter((c) => c.language === (p.language ?? \"\")).reduce((n, c) => n + c.count, 0);\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-3 text-sm\", p.className)} data-ckui=\"group-banner\" data-kind={g.kind}>\n <GroupCover group={g} className=\"aspect-[3/1] w-full\" />\n <div className=\"flex flex-wrap items-end gap-4 px-2 sm:px-4\">\n <GroupAvatar group={g} className=\"-mt-12 size-20 border-4 border-background sm:-mt-16 sm:size-28\" />\n <div className=\"grid min-w-0 flex-1 gap-1\">\n <h1 className=\"text-xl font-semibold break-words\">{g.name || g.slug}</h1>\n <p className=\"text-xs text-muted-foreground tabular-nums\">{m.plural(\"groups.items\", count)}</p>\n </div>\n <div className=\"flex flex-wrap items-start gap-2\">\n <MembershipBox group={g} onCheckout={p.onCheckout} client={p.client} />\n {p.children}\n </div>\n </div>\n {text?.description && <p className=\"px-2 break-words whitespace-pre-line sm:px-4\">{text.description}</p>}\n </ContentKitUiRoot>\n );\n}\n\nexport interface GroupImageEditorProps {\n group: GroupLike;\n /** The cover, or a user-owned kind's avatar. */\n slot: GroupImageSlot;\n /** Crop before uploading. Default true. */\n crop?: boolean;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n}\n\n/** A group's cover or avatar for its editors: pick (and crop) an image, upload it and select it; or go back to none (a cover: automatic). */\nexport function GroupImageEditor({ group, slot, crop = true, client, onError, className }: GroupImageEditorProps) {\n const { t } = useMessages();\n const g = useGroup(group.id, { client });\n const input = useRef<HTMLInputElement>(null);\n const save = useAction(\"group.save\", onError);\n const [source, setSource] = useState<CropSource | null>(null);\n const [file, setFile] = useState<File | null>(null);\n const shown = g.group ?? group;\n const set = slot === \"cover\" ? !!shown.cover?.image || (!!shown.cover?.content_id && !shown.cover.auto) : !!shown.avatar?.image;\n const upload = (f: Blob & { name?: string }, edit?: Edit) => save.run(() => g.uploadImage(slot, f, { wait: false, edit }));\n const close = () => {\n source?.revoke?.();\n setSource(null);\n setFile(null);\n };\n return (\n <div className={cn(\"flex flex-wrap items-center gap-3\", className)} data-ckui=\"group-image-editor\" data-slot={slot}>\n {slot === \"avatar\" ? <GroupAvatar group={shown} src={g.previews.avatar} className=\"size-16\" /> : <GroupCover group={shown} src={g.previews.cover} className=\"aspect-[3/1] w-48\" />}\n <div className=\"grid gap-2\">\n <div className=\"flex flex-wrap gap-2\">\n <Button type=\"button\" size=\"sm\" variant=\"outline\" disabled={save.pending} onClick={() => input.current?.click()}>\n {save.pending ? t(\"groupEditor.uploading\") : t(slot === \"avatar\" ? \"groupEditor.uploadAvatar\" : \"groupEditor.uploadCover\")}\n </Button>\n {set && (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" disabled={save.pending} onClick={() => void save.run(() => g.update(slot === \"cover\" ? { cover: {} } : { avatar: {} }))}>\n {t(slot === \"avatar\" ? \"groupEditor.removeAvatar\" : \"groupEditor.coverUseAuto\")}\n </Button>\n )}\n </div>\n <input\n ref={input}\n type=\"file\"\n accept=\"image/*\"\n hidden\n aria-label={t(slot === \"avatar\" ? \"groupEditor.uploadAvatar\" : \"groupEditor.uploadCover\")}\n onChange={(e) => {\n const f = e.target.files?.[0];\n e.target.value = \"\";\n if (!f) return;\n if (!crop) return void upload(f);\n decodeImage(f).then(\n (s) => {\n setFile(f);\n setSource(s);\n },\n () => void upload(f),\n );\n }}\n />\n <ErrorLine error={save.error} />\n </div>\n <ImageCropDialog\n open={!!source}\n onOpenChange={(open) => !open && close()}\n source={source}\n aspect={slot === \"avatar\" ? \"1:1\" : \"3:1\"}\n aspects={slot === \"avatar\" ? undefined : [\"3:1\", \"16:9\", \"\"]}\n round={slot === \"avatar\"}\n onConfirm={(edit) => {\n const f = file;\n close();\n if (f) void upload(f, edit ?? undefined);\n }}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"banner.js","names":[],"sources":["../../../src/components/groups/banner.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useContext, useRef, useState, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Edit, GroupAccess, GroupDetail, Sale } from \"../../client/generated/wire.js\";\nimport type { GroupImageSlot } from \"../../client/groups.js\";\nimport { decodeImage, type CropSource } from \"../../client/image.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext, useContentKitClient, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useGroup } from \"../../react/groups.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { ImageCropDialog } from \"../image-crop-dialog.js\";\nimport { contentError, ErrorLine, useAction } from \"../social/parts.js\";\nimport { priceLabel } from \"../content/paywall.js\";\nimport { GroupAvatar, GroupCover, type GroupLike } from \"./parts.js\";\n\nexport interface MembershipBoxProps {\n group: Pick<GroupDetail, \"id\" | \"kind\" | \"name\"> & { access?: GroupAccess };\n /** Buys a sold membership: the host creates the order through billing-ui. */\n onCheckout?: (o: { key: string; offer: Sale }) => void;\n /** Asks a signed-out visitor to sign in; default the provider's. */\n onSignIn?: () => void;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** A group's membership for the reader: member, join free, or join for its price; nothing to join while it sells no membership. */\nexport function MembershipBox({ group, onCheckout, onSignIn, client: own, onError, className, appearance }: MembershipBoxProps) {\n const m = useMessages();\n const { t } = m;\n const client = useContentKitClient(own);\n const ctx = useContext(ContentKitContext);\n const join = useAction(\"membership.join\", onError);\n const a = group.access;\n if (!a || a.editor || (!a.offer && !a.member && !a.unknown)) return null;\n const signedOut = ctx?.viewer === null;\n const signIn = onSignIn ?? ctx?.onSignIn;\n const offer: Sale | undefined = a.offer && Number(a.offer.terms.amount) > 0 ? a.offer : undefined;\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-2 rounded-lg border border-border p-3 text-sm\", className)} data-ckui=\"membership-box\" data-member={a.member}>\n {a.unknown ? (\n <p className=\"text-muted-foreground\">{t(\"membership.unknown\")}</p>\n ) : a.member ? (\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge>{t(\"membership.member\")}</Badge>\n {!offer && (\n <Button size=\"sm\" variant=\"ghost\" disabled={join.pending} onClick={() => void join.run(() => client.offers.join(group.id, false))}>\n {t(\"membership.leave\")}\n </Button>\n )}\n </div>\n ) : (\n <div className=\"grid gap-2\">\n <p className=\"text-muted-foreground\">{t(offer ? \"membership.paidPitch\" : \"membership.freePitch\")}</p>\n <Button\n size=\"sm\"\n className=\"justify-self-start\"\n disabled={join.pending || (!!offer && !onCheckout)}\n onClick={() => {\n if (signedOut && signIn) return signIn();\n if (offer) return onCheckout?.({ key: a.key, offer });\n void join.run(() => client.offers.join(group.id));\n }}\n >\n {offer ? t(\"membership.joinFor\", { price: priceLabel(m, offer.terms) }) : t(\"membership.join\")}\n </Button>\n </div>\n )}\n {join.error && <ErrorLine>{contentError(m, join.error)}</ErrorLine>}\n </ContentKitUiRoot>\n );\n}\n\nexport interface GroupBannerProps {\n /** The group the host already read; or give id. */\n group?: GroupDetail;\n id?: string;\n language?: string;\n onCheckout?: MembershipBoxProps[\"onCheckout\"];\n /** Beside the membership box: the host's actions (follow, share, edit). */\n children?: ReactNode;\n client?: ContentKitClient;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** A group's page head: its cover across, its avatar, name, description, content count and membership. */\nexport function GroupBanner(p: GroupBannerProps) {\n const m = useMessages();\n const read = useGroup(p.group ? null : p.id, { language: p.language, client: p.client });\n const g = p.group ?? read.group;\n if (!g) return read.error ? <ErrorLine>{contentError(m, read.error)}</ErrorLine> : <Skeleton className=\"h-48 w-full\" />;\n const text = g.text.find((x) => x.language === (p.language ?? g.name_language)) ?? g.text.find((x) => x.description);\n const count = g.counts.filter((c) => c.language === (p.language ?? \"\")).reduce((n, c) => n + c.count, 0);\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-3 text-sm\", p.className)} data-ckui=\"group-banner\" data-kind={g.kind}>\n <GroupCover group={g} className=\"aspect-[3/1] w-full\" />\n <div className=\"flex flex-wrap items-end gap-4 px-2 sm:px-4\">\n <GroupAvatar group={g} className=\"-mt-12 size-20 border-4 border-background sm:-mt-16 sm:size-28\" />\n <div className=\"grid min-w-0 flex-1 gap-1\">\n <h1 className=\"text-xl font-semibold break-words\">{g.name || g.slug}</h1>\n <p className=\"text-xs text-muted-foreground tabular-nums\">{m.plural(\"groups.items\", count)}</p>\n </div>\n <div className=\"flex flex-wrap items-start gap-2\">\n <MembershipBox group={g} onCheckout={p.onCheckout} client={p.client} />\n {p.children}\n </div>\n </div>\n {text?.description && <p className=\"px-2 break-words whitespace-pre-line sm:px-4\">{text.description}</p>}\n </ContentKitUiRoot>\n );\n}\n\nexport interface GroupImageEditorProps {\n group: GroupLike;\n /** The cover, or a user-owned kind's avatar. */\n slot: GroupImageSlot;\n /** Crop before uploading. Default true. */\n crop?: boolean;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n}\n\n/** A group's cover or avatar for its editors: pick (and crop) an image, upload it and select it; or go back to none (a cover: automatic). */\nexport function GroupImageEditor({ group, slot, crop = true, client, onError, className }: GroupImageEditorProps) {\n const { t } = useMessages();\n const g = useGroup(group.id, { client });\n const input = useRef<HTMLInputElement>(null);\n const save = useAction(\"group.save\", onError);\n const [source, setSource] = useState<CropSource | null>(null);\n const [file, setFile] = useState<File | null>(null);\n const shown = g.group ?? group;\n const set = slot === \"cover\" ? !!shown.cover?.image || (!!shown.cover?.content_id && !shown.cover.auto) : !!shown.avatar?.image;\n const upload = (f: Blob & { name?: string }, edit?: Edit) => save.run(() => g.uploadImage(slot, f, { wait: false, edit }));\n const close = () => {\n source?.revoke?.();\n setSource(null);\n setFile(null);\n };\n return (\n <div className={cn(\"flex flex-wrap items-center gap-3\", className)} data-ckui=\"group-image-editor\" data-slot={slot}>\n {slot === \"avatar\" ? <GroupAvatar group={shown} src={g.previews.avatar} className=\"size-16\" /> : <GroupCover group={shown} src={g.previews.cover} className=\"aspect-[3/1] w-48\" />}\n <div className=\"grid gap-2\">\n <div className=\"flex flex-wrap gap-2\">\n <Button type=\"button\" size=\"sm\" variant=\"outline\" disabled={save.pending} onClick={() => input.current?.click()}>\n {save.pending ? t(\"groupEditor.uploading\") : t(slot === \"avatar\" ? \"groupEditor.uploadAvatar\" : \"groupEditor.uploadCover\")}\n </Button>\n {set && (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" disabled={save.pending} onClick={() => void save.run(() => g.update(slot === \"cover\" ? { cover: {} } : { avatar: {} }))}>\n {t(slot === \"avatar\" ? \"groupEditor.removeAvatar\" : \"groupEditor.coverUseAuto\")}\n </Button>\n )}\n </div>\n <input\n ref={input}\n type=\"file\"\n accept=\"image/*\"\n hidden\n aria-label={t(slot === \"avatar\" ? \"groupEditor.uploadAvatar\" : \"groupEditor.uploadCover\")}\n onChange={(e) => {\n const f = e.target.files?.[0];\n e.target.value = \"\";\n if (!f) return;\n if (!crop) return void upload(f);\n decodeImage(f).then(\n (s) => {\n setFile(f);\n setSource(s);\n },\n () => void upload(f),\n );\n }}\n />\n <ErrorLine error={save.error} />\n </div>\n <ImageCropDialog\n open={!!source}\n onOpenChange={(open) => !open && close()}\n source={source}\n aspect={slot === \"avatar\" ? \"1:1\" : \"3:1\"}\n aspects={slot === \"avatar\" ? undefined : [\"3:1\", \"16:9\", \"\"]}\n round={slot === \"avatar\"}\n onConfirm={(edit) => {\n const f = file;\n close();\n if (f) void upload(f, edit ?? undefined);\n }}\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,17 +1,17 @@
1
1
  import { useMessages as e } from "../../i18n/context.js";
2
2
  import { ContentKitUiRoot as t } from "../../scope.js";
3
- import { Button as n } from "../../ui/button.js";
4
- import { Skeleton as r } from "../../ui/skeleton.js";
5
- import { contentError as i } from "../social/parts.js";
6
- import { LETTERS as a } from "../../client/group-fields.js";
7
- import { useGroups as o } from "../../react/groups.js";
3
+ import { useGroups as n } from "../../react/groups.js";
4
+ import { Button as r } from "../../ui/button.js";
5
+ import { Skeleton as i } from "../../ui/skeleton.js";
6
+ import { contentError as a } from "../social/parts.js";
7
+ import { LETTERS as o } from "../../client/group-fields.js";
8
8
  import { GroupCover as s, GroupLink as c } from "./parts.js";
9
9
  import { useState as l } from "react";
10
10
  import { jsx as u, jsxs as d } from "react/jsx-runtime";
11
11
  import { cn as f } from "cn";
12
12
  //#region src/components/groups/directory.tsx
13
13
  function p(p) {
14
- let m = e(), { t: h } = m, [g, _] = l(null), v = p.letter === void 0 ? g : p.letter, y = (e) => p.onLetterChange ? p.onLetterChange(e) : _(e), b = o({
14
+ let m = e(), { t: h } = m, [g, _] = l(null), v = p.letter === void 0 ? g : p.letter, y = (e) => p.onLetterChange ? p.onLetterChange(e) : _(e), b = n({
15
15
  kind: p.kind,
16
16
  language: p.language,
17
17
  contentKind: p.contentKind,
@@ -31,7 +31,7 @@ function p(p) {
31
31
  "aria-label": h("groups.byLetter"),
32
32
  className: "flex flex-wrap gap-1",
33
33
  "data-ckui": "letters",
34
- children: [null, ...a].map((e) => /* @__PURE__ */ u(n, {
34
+ children: [null, ...o].map((e) => /* @__PURE__ */ u(r, {
35
35
  size: "xs",
36
36
  variant: v === e ? "default" : "ghost",
37
37
  "aria-pressed": v === e,
@@ -44,14 +44,14 @@ function p(p) {
44
44
  b.loading && !b.items.length ? /* @__PURE__ */ u("div", {
45
45
  className: x ? "grid grid-cols-[repeat(auto-fill,minmax(9rem,1fr))] gap-3" : "grid gap-2",
46
46
  "aria-busy": !0,
47
- children: Array.from({ length: 6 }, (e, t) => /* @__PURE__ */ u(r, { className: x ? "aspect-[3/4] w-full" : "h-6 w-48" }, t))
47
+ children: Array.from({ length: 6 }, (e, t) => /* @__PURE__ */ u(i, { className: x ? "aspect-[3/4] w-full" : "h-6 w-48" }, t))
48
48
  }) : b.error && !b.items.length ? /* @__PURE__ */ d("div", {
49
49
  role: "alert",
50
50
  className: "grid justify-items-start gap-2",
51
51
  children: [/* @__PURE__ */ u("p", {
52
52
  className: "text-destructive",
53
- children: i(m, b.error)
54
- }), /* @__PURE__ */ u(n, {
53
+ children: a(m, b.error)
54
+ }), /* @__PURE__ */ u(r, {
55
55
  size: "sm",
56
56
  variant: "outline",
57
57
  onClick: b.reload,
@@ -100,7 +100,7 @@ function p(p) {
100
100
  role: "status",
101
101
  children: h("groups.empty")
102
102
  }),
103
- b.hasMore && /* @__PURE__ */ u(n, {
103
+ b.hasMore && /* @__PURE__ */ u(r, {
104
104
  variant: "outline",
105
105
  size: "sm",
106
106
  className: "justify-self-center",