@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
@@ -9,8 +9,6 @@ export type GroupLike = Pick<GroupCard, "id" | "kind" | "name" | "slug" | "path"
9
9
  /** Display names of kinds, attributes, roles and values by identifier; a missing one shows the identifier humanized. */
10
10
  export type Labels = Readonly<Record<string, string>>;
11
11
  export declare const labelOf: (labels: Labels | undefined, id: string) => string;
12
- /** A language's name in the UI's language ("ja" → "Japanese"). */
13
- export declare function useLanguageName(): (code: string) => string;
14
12
  export interface GroupCoverProps {
15
13
  group: GroupLike;
16
14
  /** Shown instead, e.g. a cover just uploaded. */
@@ -18,9 +16,9 @@ export interface GroupCoverProps {
18
16
  className?: string;
19
17
  }
20
18
  /** A group's cover: its renditions, else its initial on a muted tile. */
21
- export declare function GroupCover({ group, src, className }: GroupCoverProps): import('react').JSX.Element;
19
+ export declare function GroupCover(p: GroupCoverProps): import('react').JSX.Element;
22
20
  /** A group's avatar: its renditions, else its initial in a circle. */
23
- export declare function GroupAvatar({ group, src, className }: GroupCoverProps): import('react').JSX.Element;
21
+ export declare function GroupAvatar(p: GroupCoverProps): import('react').JSX.Element;
24
22
  /** A link to a group's page: `href`, else its canonical path; in-app through the provider's navigate. */
25
23
  export declare function GroupLink({ group, href, className, children }: {
26
24
  group: GroupLike;
@@ -1,111 +1,92 @@
1
1
  import { RenditionImg as e } from "../rendition-img.js";
2
2
  import { useMessages as t } from "../../i18n/context.js";
3
- import { ContentKitContext as n, useContentKitClient as r } from "../../react/context.js";
4
- import { Input as i } from "../../ui/input.js";
5
- import { ErrorLine as a } from "../social/parts.js";
6
- import { humanize as o } from "../../client/group-fields.js";
7
- import { useTypeahead as s } from "../../react/discovery.js";
8
- import { useCallback as c, useContext as l, useEffect as u, useId as d, useMemo as f, useState as p } from "react";
9
- import { jsx as m, jsxs as h } from "react/jsx-runtime";
10
- import { cn as g } from "cn";
3
+ import { useScopeProps as n } from "../../scope.js";
4
+ import { ContentKitContext as r, useContentKitClient as i } from "../../react/context.js";
5
+ import { Input as a } from "../../ui/input.js";
6
+ import { ErrorLine as o } from "../social/parts.js";
7
+ import { humanize as s } from "../../client/group-fields.js";
8
+ import { useTypeahead as c } from "../../react/discovery.js";
9
+ import { useCallback as l, useContext as u, useEffect as d, useId as f, useMemo as p, useState as m } from "react";
10
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
11
+ import { cn as _ } from "cn";
11
12
  //#region src/components/groups/parts.tsx
12
- var _ = (e, t) => e?.[t] ?? o(t);
13
- function v() {
14
- let { language: e } = t();
15
- return f(() => {
16
- let t;
17
- try {
18
- t = new Intl.DisplayNames(e ? [e] : void 0, { type: "language" });
19
- } catch {
20
- t = void 0;
21
- }
22
- return (e) => {
23
- try {
24
- return t?.of(e) ?? e;
25
- } catch {
26
- return e;
27
- }
28
- };
29
- }, [e]);
30
- }
31
- function y({ group: t, src: n, className: r }) {
32
- let i = g("overflow-hidden rounded-md bg-muted", r), a = /* @__PURE__ */ m("span", {
33
- className: g(i, "grid place-items-center text-lg font-semibold text-muted-foreground"),
13
+ var v = (e, t) => e?.[t] ?? s(t);
14
+ function y({ group: t, src: r, className: i, shape: a, slot: o }) {
15
+ let s = n(), c = _(s.className, "overflow-hidden bg-muted", a, i), l = {
16
+ "data-ckui-theme": s["data-ckui-theme"],
17
+ style: s.style,
18
+ "data-ckui": o
19
+ }, u = /* @__PURE__ */ h("span", {
20
+ ...l,
21
+ className: _(c, "grid place-items-center font-semibold text-muted-foreground", o === "group-cover" && "text-lg"),
34
22
  children: (t.name || t.slug).trim().charAt(0).toUpperCase() || "?"
35
23
  });
36
- if (n) return /* @__PURE__ */ m("img", {
37
- src: n,
24
+ if (r) return /* @__PURE__ */ h("img", {
25
+ ...l,
26
+ src: r,
38
27
  alt: "",
39
- className: g(i, "object-cover"),
40
- "data-ckui": "group-cover"
28
+ className: _(c, "object-cover")
41
29
  });
42
- let o = t.cover?.renditions;
43
- return o?.length ? /* @__PURE__ */ m(e, {
44
- outputs: o,
30
+ let d = (o === "group-cover" ? t.cover : t.avatar)?.renditions;
31
+ return d?.length ? /* @__PURE__ */ h(e, {
32
+ ...l,
33
+ outputs: d,
45
34
  fit: "cover",
46
35
  skeleton: !0,
47
- className: i,
48
- fallback: a,
49
- "data-ckui": "group-cover"
50
- }) : a;
36
+ className: c,
37
+ fallback: u
38
+ }) : u;
51
39
  }
52
- function b({ group: t, src: n, className: r }) {
53
- let i = g("aspect-square overflow-hidden rounded-full bg-muted", r), a = /* @__PURE__ */ m("span", {
54
- className: g(i, "grid place-items-center font-semibold text-muted-foreground"),
55
- children: (t.name || t.slug).trim().charAt(0).toUpperCase() || "?"
40
+ function b(e) {
41
+ return /* @__PURE__ */ h(y, {
42
+ ...e,
43
+ shape: "rounded-md",
44
+ slot: "group-cover"
56
45
  });
57
- if (n) return /* @__PURE__ */ m("img", {
58
- src: n,
59
- alt: "",
60
- className: g(i, "object-cover"),
61
- "data-ckui": "group-avatar"
46
+ }
47
+ function x(e) {
48
+ return /* @__PURE__ */ h(y, {
49
+ ...e,
50
+ shape: "aspect-square rounded-full",
51
+ slot: "group-avatar"
62
52
  });
63
- let o = t.avatar?.renditions;
64
- return o?.length ? /* @__PURE__ */ m(e, {
65
- outputs: o,
66
- fit: "cover",
67
- skeleton: !0,
68
- className: i,
69
- fallback: a,
70
- "data-ckui": "group-avatar"
71
- }) : a;
72
53
  }
73
- function x({ group: e, href: t, className: r, children: i }) {
74
- let a = l(n)?.navigate, o = t ? t(e) : e.path, s = c((e) => {
54
+ function S({ group: e, href: t, className: n, children: i }) {
55
+ let a = u(r)?.navigate, o = t ? t(e) : e.path, s = l((e) => {
75
56
  !a || !o || e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || (e.preventDefault(), a(o));
76
57
  }, [a, o]);
77
- return o ? /* @__PURE__ */ m("a", {
58
+ return o ? /* @__PURE__ */ h("a", {
78
59
  href: o,
79
- className: r,
60
+ className: n,
80
61
  onClick: s,
81
62
  children: i
82
- }) : /* @__PURE__ */ m("span", {
83
- className: r,
63
+ }) : /* @__PURE__ */ h("span", {
64
+ className: n,
84
65
  children: i
85
66
  });
86
67
  }
87
- function S({ kind: e, language: n, exclude: r, label: o, onPick: c, client: l, autoFocus: _, className: v }) {
88
- let { t: y } = t(), b = d(), [x, S] = p(""), [C, w] = p(0), T = s(x, {
68
+ function C({ kind: e, language: n, exclude: r, label: i, onPick: s, client: l, autoFocus: u, className: v }) {
69
+ let { t: y } = t(), b = f(), [x, S] = m(""), [C, w] = m(0), T = c(x, {
89
70
  group_kinds: [e],
90
71
  display_language: n,
91
72
  limit: 10,
92
73
  client: l
93
- }), E = f(() => T.groups.filter((t) => t.kind === e && !r?.includes(t.id)).slice(0, 8), [
74
+ }), E = p(() => T.groups.filter((t) => t.kind === e && !r?.includes(t.id)).slice(0, 8), [
94
75
  T.groups,
95
76
  e,
96
77
  r
97
78
  ]), D = x.trim();
98
- u(() => w(0), [D]);
79
+ d(() => w(0), [D]);
99
80
  let O = (e) => {
100
- S(""), c(e);
81
+ S(""), s(e);
101
82
  }, k = E.length > 0;
102
- return /* @__PURE__ */ h("div", {
103
- className: g("relative grid min-w-48 flex-1 gap-1", v),
83
+ return /* @__PURE__ */ g("div", {
84
+ className: _("relative grid min-w-48 flex-1 gap-1", v),
104
85
  "data-ckui": "group-picker",
105
86
  children: [
106
- /* @__PURE__ */ m(i, {
87
+ /* @__PURE__ */ h(a, {
107
88
  role: "combobox",
108
- "aria-label": o,
89
+ "aria-label": i,
109
90
  "aria-expanded": k,
110
91
  "aria-controls": `${b}-list`,
111
92
  "aria-activedescendant": k ? `${b}-${C}` : void 0,
@@ -114,47 +95,47 @@ function S({ kind: e, language: n, exclude: r, label: o, onPick: c, client: l, a
114
95
  className: "h-8",
115
96
  value: x,
116
97
  placeholder: y("groups.searchPlaceholder"),
117
- autoFocus: _,
98
+ autoFocus: u,
118
99
  onChange: (e) => S(e.target.value),
119
100
  onKeyDown: (e) => {
120
101
  k && (e.key === "ArrowDown" ? (e.preventDefault(), w((e) => Math.min(E.length - 1, e + 1))) : e.key === "ArrowUp" ? (e.preventDefault(), w((e) => Math.max(0, e - 1))) : e.key === "Enter" ? (e.preventDefault(), O(E[C])) : e.key === "Escape" && S(""));
121
102
  }
122
103
  }),
123
- k && /* @__PURE__ */ m("ul", {
104
+ k && /* @__PURE__ */ h("ul", {
124
105
  id: `${b}-list`,
125
106
  role: "listbox",
126
- "aria-label": o,
107
+ "aria-label": i,
127
108
  className: "absolute top-full z-10 mt-1 grid w-full overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md",
128
- children: E.map((e, t) => /* @__PURE__ */ h("li", {
109
+ children: E.map((e, t) => /* @__PURE__ */ g("li", {
129
110
  id: `${b}-${t}`,
130
111
  role: "option",
131
112
  "aria-selected": t === C,
132
- className: g("flex cursor-pointer items-baseline justify-between gap-2 px-3 py-1.5", t === C && "bg-accent text-accent-foreground"),
113
+ className: _("flex cursor-pointer items-baseline justify-between gap-2 px-3 py-1.5", t === C && "bg-accent text-accent-foreground"),
133
114
  onMouseEnter: () => w(t),
134
115
  onMouseDown: (t) => {
135
116
  t.preventDefault(), O(e);
136
117
  },
137
- children: [/* @__PURE__ */ m("span", {
118
+ children: [/* @__PURE__ */ h("span", {
138
119
  className: "min-w-0 truncate",
139
120
  children: e.name || e.slug
140
- }), /* @__PURE__ */ m("span", {
121
+ }), /* @__PURE__ */ h("span", {
141
122
  className: "text-xs text-muted-foreground",
142
123
  children: e.slug
143
124
  })]
144
125
  }, e.id))
145
126
  }),
146
- D && !k && T.query === D && !T.loading && !T.error && /* @__PURE__ */ m("p", {
127
+ D && !k && T.query === D && !T.loading && !T.error && /* @__PURE__ */ h("p", {
147
128
  className: "text-xs text-muted-foreground",
148
129
  role: "status",
149
130
  children: y("groups.noMatch")
150
131
  }),
151
- /* @__PURE__ */ m(a, { error: T.error })
132
+ /* @__PURE__ */ h(o, { error: T.error })
152
133
  ]
153
134
  });
154
135
  }
155
- function C(e, t = {}) {
156
- let n = r(t.client), [i, a] = p(/* @__PURE__ */ new Map()), o = [...new Set(e)].sort().join(",");
157
- return u(() => {
136
+ function w(e, t = {}) {
137
+ let n = i(t.client), [r, a] = m(/* @__PURE__ */ new Map()), o = [...new Set(e)].sort().join(",");
138
+ return d(() => {
158
139
  if (!o) return;
159
140
  let e = new AbortController(), r = o.split(",");
160
141
  return Promise.all(r.map((r) => n.groups.get(r, t.language, e.signal).catch(() => null))).then((t) => {
@@ -164,9 +145,9 @@ function C(e, t = {}) {
164
145
  o,
165
146
  n,
166
147
  t.language
167
- ]), i;
148
+ ]), r;
168
149
  }
169
150
  //#endregion
170
- export { b as GroupAvatar, y as GroupCover, x as GroupLink, S as GroupPicker, _ as labelOf, C as useGroupNames, v as useLanguageName };
151
+ export { x as GroupAvatar, b as GroupCover, S as GroupLink, C as GroupPicker, v as labelOf, w as useGroupNames };
171
152
 
172
153
  //# sourceMappingURL=parts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"parts.js","names":[],"sources":["../../../src/components/groups/parts.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useCallback, useContext, useEffect, useId, useMemo, useState, type MouseEvent, type ReactNode } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport { humanize } from \"../../client/group-fields.js\";\nimport type { Avatar, GroupCard, GroupDetail, GroupsCover } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext, useContentKitClient } from \"../../react/context.js\";\nimport { useTypeahead } from \"../../react/discovery.js\";\nimport { Input } from \"#ckui/ui/input\";\nimport { RenditionImg } from \"../rendition-img.js\";\nimport { ErrorLine } from \"../social/parts.js\";\n\n/** A group as covers and links need it. */\nexport type GroupLike = Pick<GroupCard, \"id\" | \"kind\" | \"name\" | \"slug\" | \"path\"> & { cover?: GroupsCover; avatar?: Avatar };\n\n/** Display names of kinds, attributes, roles and values by identifier; a missing one shows the identifier humanized. */\nexport type Labels = Readonly<Record<string, string>>;\n\nexport const labelOf = (labels: Labels | undefined, id: string) => labels?.[id] ?? humanize(id);\n\n/** A language's name in the UI's language (\"ja\" → \"Japanese\"). */\nexport function useLanguageName(): (code: string) => string {\n const { language } = useMessages();\n return useMemo(() => {\n let names: Intl.DisplayNames | undefined;\n try {\n names = new Intl.DisplayNames(language ? [language] : undefined, { type: \"language\" });\n } catch {\n names = undefined;\n }\n return (code: string) => {\n try {\n return names?.of(code) ?? code;\n } catch {\n return code;\n }\n };\n }, [language]);\n}\n\nexport interface GroupCoverProps {\n group: GroupLike;\n /** Shown instead, e.g. a cover just uploaded. */\n src?: string;\n className?: string;\n}\n\n/** A group's cover: its renditions, else its initial on a muted tile. */\nexport function GroupCover({ group, src, className }: GroupCoverProps) {\n const box = cn(\"overflow-hidden rounded-md bg-muted\", className);\n const initial = <span className={cn(box, \"grid place-items-center text-lg font-semibold text-muted-foreground\")}>{(group.name || group.slug).trim().charAt(0).toUpperCase() || \"?\"}</span>;\n if (src) return <img src={src} alt=\"\" className={cn(box, \"object-cover\")} data-ckui=\"group-cover\" />;\n const renditions = group.cover?.renditions;\n if (renditions?.length) return <RenditionImg outputs={renditions} fit=\"cover\" skeleton className={box} fallback={initial} data-ckui=\"group-cover\" />;\n return initial;\n}\n\n/** A group's avatar: its renditions, else its initial in a circle. */\nexport function GroupAvatar({ group, src, className }: GroupCoverProps) {\n const box = cn(\"aspect-square overflow-hidden rounded-full bg-muted\", className);\n const initial = <span className={cn(box, \"grid place-items-center font-semibold text-muted-foreground\")}>{(group.name || group.slug).trim().charAt(0).toUpperCase() || \"?\"}</span>;\n if (src) return <img src={src} alt=\"\" className={cn(box, \"object-cover\")} data-ckui=\"group-avatar\" />;\n const renditions = group.avatar?.renditions;\n if (renditions?.length) return <RenditionImg outputs={renditions} fit=\"cover\" skeleton className={box} fallback={initial} data-ckui=\"group-avatar\" />;\n return initial;\n}\n\n/** A link to a group's page: `href`, else its canonical path; in-app through the provider's navigate. */\nexport function GroupLink({ group, href, className, children }: { group: GroupLike; href?: (g: GroupLike) => string | undefined; className?: string; children: ReactNode }) {\n const navigate = useContext(ContentKitContext)?.navigate;\n const url = href ? href(group) : group.path;\n const onClick = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n if (!navigate || !url || e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n e.preventDefault();\n navigate(url);\n },\n [navigate, url],\n );\n if (!url) return <span className={className}>{children}</span>;\n return (\n <a href={url} className={className} onClick={onClick}>\n {children}\n </a>\n );\n}\n\nexport interface GroupPickerProps {\n kind: string;\n /** Names in this language. */\n language?: string;\n /** Groups not offered, by id. */\n exclude?: readonly string[];\n label: string;\n onPick: (group: GroupCard) => void;\n client?: ContentKitClient;\n autoFocus?: boolean;\n className?: string;\n}\n\n/** A typeahead over one kind's groups (GET /v1/typeahead). */\nexport function GroupPicker({ kind, language, exclude, label, onPick, client, autoFocus, className }: GroupPickerProps) {\n const { t } = useMessages();\n const id = useId();\n const [text, setText] = useState(\"\");\n const [active, setActive] = useState(0);\n const found = useTypeahead(text, { group_kinds: [kind], display_language: language, limit: 10, client });\n const results = useMemo(() => found.groups.filter((g) => g.kind === kind && !exclude?.includes(g.id)).slice(0, 8), [found.groups, kind, exclude]);\n const q = text.trim();\n useEffect(() => setActive(0), [q]);\n const pick = (g: GroupCard) => {\n setText(\"\");\n onPick(g);\n };\n const open = results.length > 0;\n return (\n <div className={cn(\"relative grid min-w-48 flex-1 gap-1\", className)} data-ckui=\"group-picker\">\n <Input\n role=\"combobox\"\n aria-label={label}\n aria-expanded={open}\n aria-controls={`${id}-list`}\n aria-activedescendant={open ? `${id}-${active}` : undefined}\n aria-autocomplete=\"list\"\n autoComplete=\"off\"\n className=\"h-8\"\n value={text}\n placeholder={t(\"groups.searchPlaceholder\")}\n autoFocus={autoFocus}\n onChange={(e) => setText(e.target.value)}\n onKeyDown={(e) => {\n if (!open) return;\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n setActive((a) => Math.min(results.length - 1, a + 1));\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n setActive((a) => Math.max(0, a - 1));\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n pick(results[active]!);\n } else if (e.key === \"Escape\") setText(\"\");\n }}\n />\n {open && (\n <ul id={`${id}-list`} role=\"listbox\" aria-label={label} className=\"absolute top-full z-10 mt-1 grid w-full overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md\">\n {results.map((g, i) => (\n <li\n key={g.id}\n id={`${id}-${i}`}\n role=\"option\"\n aria-selected={i === active}\n className={cn(\"flex cursor-pointer items-baseline justify-between gap-2 px-3 py-1.5\", i === active && \"bg-accent text-accent-foreground\")}\n onMouseEnter={() => setActive(i)}\n onMouseDown={(e) => {\n e.preventDefault();\n pick(g);\n }}\n >\n <span className=\"min-w-0 truncate\">{g.name || g.slug}</span>\n <span className=\"text-xs text-muted-foreground\">{g.slug}</span>\n </li>\n ))}\n </ul>\n )}\n {q && !open && found.query === q && !found.loading && !found.error && (\n <p className=\"text-xs text-muted-foreground\" role=\"status\">\n {t(\"groups.noMatch\")}\n </p>\n )}\n <ErrorLine error={found.error} />\n </div>\n );\n}\n\n/** Groups read by id, for names where only ids are at hand (edges); a missing or deleted one is left out. */\nexport function useGroupNames(ids: readonly string[], o: { language?: string; client?: ContentKitClient } = {}): ReadonlyMap<string, GroupDetail> {\n const client = useContentKitClient(o.client);\n const [found, setFound] = useState<ReadonlyMap<string, GroupDetail>>(new Map());\n const key = [...new Set(ids)].sort().join(\",\");\n useEffect(() => {\n if (!key) return;\n const ctl = new AbortController();\n const list = key.split(\",\");\n // A merged group answers as its survivor: keep it under the id asked for.\n Promise.all(list.map((id) => client.groups.get(id, o.language, ctl.signal).catch(() => null))).then((all) => {\n if (!ctl.signal.aborted) setFound(new Map(all.flatMap((g, i) => (g ? [[list[i]!, g] as const] : []))));\n });\n return () => ctl.abort();\n }, [key, client, o.language]);\n return found;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"parts.js","names":[],"sources":["../../../src/components/groups/parts.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useCallback, useContext, useEffect, useId, useMemo, useState, type MouseEvent, type ReactNode } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport { humanize } from \"../../client/group-fields.js\";\nimport type { Avatar, GroupCard, GroupDetail, GroupsCover } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext, useContentKitClient } from \"../../react/context.js\";\nimport { useTypeahead } from \"../../react/discovery.js\";\nimport { Input } from \"#ckui/ui/input\";\nimport { useScopeProps } from \"../../scope.js\";\nimport { RenditionImg } from \"../rendition-img.js\";\nimport { ErrorLine } from \"../social/parts.js\";\n\n/** A group as covers and links need it. */\nexport type GroupLike = Pick<GroupCard, \"id\" | \"kind\" | \"name\" | \"slug\" | \"path\"> & { cover?: GroupsCover; avatar?: Avatar };\n\n/** Display names of kinds, attributes, roles and values by identifier; a missing one shows the identifier humanized. */\nexport type Labels = Readonly<Record<string, string>>;\n\nexport const labelOf = (labels: Labels | undefined, id: string) => labels?.[id] ?? humanize(id);\n\n\nexport interface GroupCoverProps {\n group: GroupLike;\n /** Shown instead, e.g. a cover just uploaded. */\n src?: string;\n className?: string;\n}\n\n/** A group's cover or avatar image; its own styling root, so it renders anywhere in the host. */\nfunction GroupImage({ group, src, className, shape, slot }: GroupCoverProps & { shape: string; slot: \"group-cover\" | \"group-avatar\" }) {\n const scope = useScopeProps();\n const box = cn(scope.className, \"overflow-hidden bg-muted\", shape, className);\n const root = { \"data-ckui-theme\": scope[\"data-ckui-theme\"], style: scope.style, \"data-ckui\": slot };\n const initial = (\n <span {...root} className={cn(box, \"grid place-items-center font-semibold text-muted-foreground\", slot === \"group-cover\" && \"text-lg\")}>\n {(group.name || group.slug).trim().charAt(0).toUpperCase() || \"?\"}\n </span>\n );\n if (src) return <img {...root} src={src} alt=\"\" className={cn(box, \"object-cover\")} />;\n const renditions = (slot === \"group-cover\" ? group.cover : group.avatar)?.renditions;\n if (renditions?.length) return <RenditionImg {...root} outputs={renditions} fit=\"cover\" skeleton className={box} fallback={initial} />;\n return initial;\n}\n\n/** A group's cover: its renditions, else its initial on a muted tile. */\nexport function GroupCover(p: GroupCoverProps) {\n return <GroupImage {...p} shape=\"rounded-md\" slot=\"group-cover\" />;\n}\n\n/** A group's avatar: its renditions, else its initial in a circle. */\nexport function GroupAvatar(p: GroupCoverProps) {\n return <GroupImage {...p} shape=\"aspect-square rounded-full\" slot=\"group-avatar\" />;\n}\n\n/** A link to a group's page: `href`, else its canonical path; in-app through the provider's navigate. */\nexport function GroupLink({ group, href, className, children }: { group: GroupLike; href?: (g: GroupLike) => string | undefined; className?: string; children: ReactNode }) {\n const navigate = useContext(ContentKitContext)?.navigate;\n const url = href ? href(group) : group.path;\n const onClick = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n if (!navigate || !url || e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n e.preventDefault();\n navigate(url);\n },\n [navigate, url],\n );\n if (!url) return <span className={className}>{children}</span>;\n return (\n <a href={url} className={className} onClick={onClick}>\n {children}\n </a>\n );\n}\n\nexport interface GroupPickerProps {\n kind: string;\n /** Names in this language. */\n language?: string;\n /** Groups not offered, by id. */\n exclude?: readonly string[];\n label: string;\n onPick: (group: GroupCard) => void;\n client?: ContentKitClient;\n autoFocus?: boolean;\n className?: string;\n}\n\n/** A typeahead over one kind's groups (GET /v1/typeahead). */\nexport function GroupPicker({ kind, language, exclude, label, onPick, client, autoFocus, className }: GroupPickerProps) {\n const { t } = useMessages();\n const id = useId();\n const [text, setText] = useState(\"\");\n const [active, setActive] = useState(0);\n const found = useTypeahead(text, { group_kinds: [kind], display_language: language, limit: 10, client });\n const results = useMemo(() => found.groups.filter((g) => g.kind === kind && !exclude?.includes(g.id)).slice(0, 8), [found.groups, kind, exclude]);\n const q = text.trim();\n useEffect(() => setActive(0), [q]);\n const pick = (g: GroupCard) => {\n setText(\"\");\n onPick(g);\n };\n const open = results.length > 0;\n return (\n <div className={cn(\"relative grid min-w-48 flex-1 gap-1\", className)} data-ckui=\"group-picker\">\n <Input\n role=\"combobox\"\n aria-label={label}\n aria-expanded={open}\n aria-controls={`${id}-list`}\n aria-activedescendant={open ? `${id}-${active}` : undefined}\n aria-autocomplete=\"list\"\n autoComplete=\"off\"\n className=\"h-8\"\n value={text}\n placeholder={t(\"groups.searchPlaceholder\")}\n autoFocus={autoFocus}\n onChange={(e) => setText(e.target.value)}\n onKeyDown={(e) => {\n if (!open) return;\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n setActive((a) => Math.min(results.length - 1, a + 1));\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n setActive((a) => Math.max(0, a - 1));\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n pick(results[active]!);\n } else if (e.key === \"Escape\") setText(\"\");\n }}\n />\n {open && (\n <ul id={`${id}-list`} role=\"listbox\" aria-label={label} className=\"absolute top-full z-10 mt-1 grid w-full overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md\">\n {results.map((g, i) => (\n <li\n key={g.id}\n id={`${id}-${i}`}\n role=\"option\"\n aria-selected={i === active}\n className={cn(\"flex cursor-pointer items-baseline justify-between gap-2 px-3 py-1.5\", i === active && \"bg-accent text-accent-foreground\")}\n onMouseEnter={() => setActive(i)}\n onMouseDown={(e) => {\n e.preventDefault();\n pick(g);\n }}\n >\n <span className=\"min-w-0 truncate\">{g.name || g.slug}</span>\n <span className=\"text-xs text-muted-foreground\">{g.slug}</span>\n </li>\n ))}\n </ul>\n )}\n {q && !open && found.query === q && !found.loading && !found.error && (\n <p className=\"text-xs text-muted-foreground\" role=\"status\">\n {t(\"groups.noMatch\")}\n </p>\n )}\n <ErrorLine error={found.error} />\n </div>\n );\n}\n\n/** Groups read by id, for names where only ids are at hand (edges); a missing or deleted one is left out. */\nexport function useGroupNames(ids: readonly string[], o: { language?: string; client?: ContentKitClient } = {}): ReadonlyMap<string, GroupDetail> {\n const client = useContentKitClient(o.client);\n const [found, setFound] = useState<ReadonlyMap<string, GroupDetail>>(new Map());\n const key = [...new Set(ids)].sort().join(\",\");\n useEffect(() => {\n if (!key) return;\n const ctl = new AbortController();\n const list = key.split(\",\");\n // A merged group answers as its survivor: keep it under the id asked for.\n Promise.all(list.map((id) => client.groups.get(id, o.language, ctl.signal).catch(() => null))).then((all) => {\n if (!ctl.signal.aborted) setFound(new Map(all.flatMap((g, i) => (g ? [[list[i]!, g] as const] : []))));\n });\n return () => ctl.abort();\n }, [key, client, o.language]);\n return found;\n}\n"],"mappings":"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"}