@openrails/contentkit-ui 0.0.0-placeholder → 0.68.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 (367) hide show
  1. package/README.md +424 -1
  2. package/dist/appearance.d.ts +38 -0
  3. package/dist/appearance.js +15 -0
  4. package/dist/appearance.js.map +1 -0
  5. package/dist/assets/hash.worker-BYfo-7MQ.js.map +1 -0
  6. package/dist/client/aspect.d.ts +6 -0
  7. package/dist/client/aspect.js +17 -0
  8. package/dist/client/aspect.js.map +1 -0
  9. package/dist/client/client.d.ts +51 -0
  10. package/dist/client/client.js +35 -0
  11. package/dist/client/client.js.map +1 -0
  12. package/dist/client/codes.d.ts +12 -0
  13. package/dist/client/codes.js +19 -0
  14. package/dist/client/codes.js.map +1 -0
  15. package/dist/client/content/bans.d.ts +15 -0
  16. package/dist/client/content/bans.js +40 -0
  17. package/dist/client/content/bans.js.map +1 -0
  18. package/dist/client/content/comments.d.ts +36 -0
  19. package/dist/client/content/comments.js +93 -0
  20. package/dist/client/content/comments.js.map +1 -0
  21. package/dist/client/content/favorites.d.ts +14 -0
  22. package/dist/client/content/favorites.js +42 -0
  23. package/dist/client/content/favorites.js.map +1 -0
  24. package/dist/client/content/moderation.d.ts +18 -0
  25. package/dist/client/content/moderation.js +37 -0
  26. package/dist/client/content/moderation.js.map +1 -0
  27. package/dist/client/content/polls.d.ts +47 -0
  28. package/dist/client/content/polls.js +148 -0
  29. package/dist/client/content/polls.js.map +1 -0
  30. package/dist/client/content/posts.d.ts +44 -0
  31. package/dist/client/content/posts.js +91 -0
  32. package/dist/client/content/posts.js.map +1 -0
  33. package/dist/client/content/reactions.d.ts +12 -0
  34. package/dist/client/content/reactions.js +36 -0
  35. package/dist/client/content/reactions.js.map +1 -0
  36. package/dist/client/content/types.d.ts +91 -0
  37. package/dist/client/content/types.js +8 -0
  38. package/dist/client/content/types.js.map +1 -0
  39. package/dist/client/crop.d.ts +32 -0
  40. package/dist/client/crop.js +103 -0
  41. package/dist/client/crop.js.map +1 -0
  42. package/dist/client/errors.d.ts +54 -0
  43. package/dist/client/errors.js +80 -0
  44. package/dist/client/errors.js.map +1 -0
  45. package/dist/client/gallery.d.ts +43 -0
  46. package/dist/client/gallery.js +86 -0
  47. package/dist/client/gallery.js.map +1 -0
  48. package/dist/client/generated/error-codes.d.ts +5 -0
  49. package/dist/client/generated/error-codes.js +131 -0
  50. package/dist/client/generated/error-codes.js.map +1 -0
  51. package/dist/client/generated/routes.d.ts +1086 -0
  52. package/dist/client/generated/routes.js +1400 -0
  53. package/dist/client/generated/routes.js.map +1 -0
  54. package/dist/client/generated/wire.d.ts +630 -0
  55. package/dist/client/generated/wire.js +6 -0
  56. package/dist/client/generated/wire.js.map +1 -0
  57. package/dist/client/http.d.ts +79 -0
  58. package/dist/client/http.js +74 -0
  59. package/dist/client/http.js.map +1 -0
  60. package/dist/client/image.d.ts +24 -0
  61. package/dist/client/image.js +65 -0
  62. package/dist/client/image.js.map +1 -0
  63. package/dist/client/index.d.ts +33 -0
  64. package/dist/client/media/api.d.ts +36 -0
  65. package/dist/client/media/api.js +61 -0
  66. package/dist/client/media/api.js.map +1 -0
  67. package/dist/client/media/client.d.ts +247 -0
  68. package/dist/client/media/client.js +407 -0
  69. package/dist/client/media/client.js.map +1 -0
  70. package/dist/client/media/encode.d.ts +3 -0
  71. package/dist/client/media/encode.js +8 -0
  72. package/dist/client/media/encode.js.map +1 -0
  73. package/dist/client/media/hash-stream.d.ts +6 -0
  74. package/dist/client/media/hash-stream.js +16 -0
  75. package/dist/client/media/hash-stream.js.map +1 -0
  76. package/dist/client/media/hash.d.ts +12 -0
  77. package/dist/client/media/hash.js +48 -0
  78. package/dist/client/media/hash.js.map +1 -0
  79. package/dist/client/media/hash.worker-inline.js +18 -0
  80. package/dist/client/media/hash.worker-inline.js.map +1 -0
  81. package/dist/client/media/pacer.d.ts +21 -0
  82. package/dist/client/media/pacer.js +25 -0
  83. package/dist/client/media/pacer.js.map +1 -0
  84. package/dist/client/media/queue.d.ts +112 -0
  85. package/dist/client/media/queue.js +274 -0
  86. package/dist/client/media/queue.js.map +1 -0
  87. package/dist/client/media/ref.d.ts +6 -0
  88. package/dist/client/media/ref.js +10 -0
  89. package/dist/client/media/ref.js.map +1 -0
  90. package/dist/client/media/rules.d.ts +52 -0
  91. package/dist/client/media/rules.js +139 -0
  92. package/dist/client/media/rules.js.map +1 -0
  93. package/dist/client/media/transport.d.ts +13 -0
  94. package/dist/client/media/transport.js +33 -0
  95. package/dist/client/media/transport.js.map +1 -0
  96. package/dist/client/media/windows.d.ts +15 -0
  97. package/dist/client/media/windows.js +39 -0
  98. package/dist/client/media/windows.js.map +1 -0
  99. package/dist/client/playback.d.ts +200 -0
  100. package/dist/client/playback.js +142 -0
  101. package/dist/client/playback.js.map +1 -0
  102. package/dist/client/player.d.ts +102 -0
  103. package/dist/client/player.js +155 -0
  104. package/dist/client/player.js.map +1 -0
  105. package/dist/client/public.d.ts +25 -0
  106. package/dist/client/public.js +18 -0
  107. package/dist/client/public.js.map +1 -0
  108. package/dist/client/rendition.d.ts +19 -0
  109. package/dist/client/rendition.js +16 -0
  110. package/dist/client/rendition.js.map +1 -0
  111. package/dist/client/route.d.ts +39 -0
  112. package/dist/client/route.js +31 -0
  113. package/dist/client/route.js.map +1 -0
  114. package/dist/client/taxonomy.d.ts +30 -0
  115. package/dist/client/taxonomy.js +77 -0
  116. package/dist/client/taxonomy.js.map +1 -0
  117. package/dist/client.js +33 -0
  118. package/dist/components/encode-progress.d.ts +15 -0
  119. package/dist/components/encode-progress.js +49 -0
  120. package/dist/components/encode-progress.js.map +1 -0
  121. package/dist/components/hover-preview.d.ts +29 -0
  122. package/dist/components/hover-preview.js +49 -0
  123. package/dist/components/hover-preview.js.map +1 -0
  124. package/dist/components/image-crop-dialog.d.ts +48 -0
  125. package/dist/components/image-crop-dialog.js +359 -0
  126. package/dist/components/image-crop-dialog.js.map +1 -0
  127. package/dist/components/lazy-mount.d.ts +10 -0
  128. package/dist/components/lazy-mount.js +22 -0
  129. package/dist/components/lazy-mount.js.map +1 -0
  130. package/dist/components/media-folder-editor.d.ts +45 -0
  131. package/dist/components/media-folder-editor.js +564 -0
  132. package/dist/components/media-folder-editor.js.map +1 -0
  133. package/dist/components/media-gallery.d.ts +54 -0
  134. package/dist/components/media-gallery.js +597 -0
  135. package/dist/components/media-gallery.js.map +1 -0
  136. package/dist/components/media-readiness-notice.d.ts +24 -0
  137. package/dist/components/media-readiness-notice.js +61 -0
  138. package/dist/components/media-readiness-notice.js.map +1 -0
  139. package/dist/components/player-controls.d.ts +71 -0
  140. package/dist/components/player-controls.js +264 -0
  141. package/dist/components/player-controls.js.map +1 -0
  142. package/dist/components/progress.d.ts +5 -0
  143. package/dist/components/progress.js +14 -0
  144. package/dist/components/progress.js.map +1 -0
  145. package/dist/components/rendition-img.d.ts +34 -0
  146. package/dist/components/rendition-img.js +78 -0
  147. package/dist/components/rendition-img.js.map +1 -0
  148. package/dist/components/slot-editor.d.ts +95 -0
  149. package/dist/components/slot-editor.js +182 -0
  150. package/dist/components/slot-editor.js.map +1 -0
  151. package/dist/components/slot-image.d.ts +23 -0
  152. package/dist/components/slot-image.js +48 -0
  153. package/dist/components/slot-image.js.map +1 -0
  154. package/dist/components/slot-upload.d.ts +41 -0
  155. package/dist/components/slot-upload.js +213 -0
  156. package/dist/components/slot-upload.js.map +1 -0
  157. package/dist/components/social/bans.d.ts +42 -0
  158. package/dist/components/social/bans.js +300 -0
  159. package/dist/components/social/bans.js.map +1 -0
  160. package/dist/components/social/comments.d.ts +42 -0
  161. package/dist/components/social/comments.js +517 -0
  162. package/dist/components/social/comments.js.map +1 -0
  163. package/dist/components/social/engagement.d.ts +45 -0
  164. package/dist/components/social/engagement.js +129 -0
  165. package/dist/components/social/engagement.js.map +1 -0
  166. package/dist/components/social/index.d.ts +6 -0
  167. package/dist/components/social/moderation.d.ts +32 -0
  168. package/dist/components/social/moderation.js +383 -0
  169. package/dist/components/social/moderation.js.map +1 -0
  170. package/dist/components/social/parts.d.ts +50 -0
  171. package/dist/components/social/parts.js +106 -0
  172. package/dist/components/social/parts.js.map +1 -0
  173. package/dist/components/social/poll-editor.d.ts +34 -0
  174. package/dist/components/social/poll-editor.js +567 -0
  175. package/dist/components/social/poll-editor.js.map +1 -0
  176. package/dist/components/social/poll.d.ts +30 -0
  177. package/dist/components/social/poll.js +252 -0
  178. package/dist/components/social/poll.js.map +1 -0
  179. package/dist/components/sortable-list.d.ts +29 -0
  180. package/dist/components/sortable-list.js +121 -0
  181. package/dist/components/sortable-list.js.map +1 -0
  182. package/dist/components/video-mini-player.d.ts +35 -0
  183. package/dist/components/video-mini-player.js +236 -0
  184. package/dist/components/video-mini-player.js.map +1 -0
  185. package/dist/components/video-player.d.ts +146 -0
  186. package/dist/components/video-player.js +630 -0
  187. package/dist/components/video-player.js.map +1 -0
  188. package/dist/components/video-poster-picker.d.ts +59 -0
  189. package/dist/components/video-poster-picker.js +335 -0
  190. package/dist/components/video-poster-picker.js.map +1 -0
  191. package/dist/components/video-poster.d.ts +24 -0
  192. package/dist/components/video-poster.js +46 -0
  193. package/dist/components/video-poster.js.map +1 -0
  194. package/dist/i18n/context.d.ts +4 -0
  195. package/dist/i18n/context.js +11 -0
  196. package/dist/i18n/context.js.map +1 -0
  197. package/dist/i18n/messages.d.ts +40 -0
  198. package/dist/i18n/messages.js +94 -0
  199. package/dist/i18n/messages.js.map +1 -0
  200. package/dist/index.d.ts +23 -0
  201. package/dist/index.js +41 -0
  202. package/dist/locales/de.d.ts +2 -0
  203. package/dist/locales/de.js +271 -0
  204. package/dist/locales/de.js.map +1 -0
  205. package/dist/locales/en.d.ts +457 -0
  206. package/dist/locales/en.js +271 -0
  207. package/dist/locales/en.js.map +1 -0
  208. package/dist/locales/es.d.ts +2 -0
  209. package/dist/locales/es.js +271 -0
  210. package/dist/locales/es.js.map +1 -0
  211. package/dist/locales/ja.d.ts +2 -0
  212. package/dist/locales/ja.js +271 -0
  213. package/dist/locales/ja.js.map +1 -0
  214. package/dist/locales/ko.d.ts +2 -0
  215. package/dist/locales/ko.js +271 -0
  216. package/dist/locales/ko.js.map +1 -0
  217. package/dist/locales/social/de.d.ts +2 -0
  218. package/dist/locales/social/de.js +198 -0
  219. package/dist/locales/social/de.js.map +1 -0
  220. package/dist/locales/social/en.d.ts +195 -0
  221. package/dist/locales/social/en.js +198 -0
  222. package/dist/locales/social/en.js.map +1 -0
  223. package/dist/locales/social/es.d.ts +2 -0
  224. package/dist/locales/social/es.js +198 -0
  225. package/dist/locales/social/es.js.map +1 -0
  226. package/dist/locales/social/ja.d.ts +2 -0
  227. package/dist/locales/social/ja.js +198 -0
  228. package/dist/locales/social/ja.js.map +1 -0
  229. package/dist/locales/social/ko.d.ts +2 -0
  230. package/dist/locales/social/ko.js +198 -0
  231. package/dist/locales/social/ko.js.map +1 -0
  232. package/dist/locales/social/zh.d.ts +2 -0
  233. package/dist/locales/social/zh.js +198 -0
  234. package/dist/locales/social/zh.js.map +1 -0
  235. package/dist/locales/zh.d.ts +2 -0
  236. package/dist/locales/zh.js +271 -0
  237. package/dist/locales/zh.js.map +1 -0
  238. package/dist/provider.d.ts +22 -0
  239. package/dist/provider.js +59 -0
  240. package/dist/provider.js.map +1 -0
  241. package/dist/react/canonical.d.ts +33 -0
  242. package/dist/react/canonical.js +88 -0
  243. package/dist/react/canonical.js.map +1 -0
  244. package/dist/react/comments.d.ts +82 -0
  245. package/dist/react/comments.js +147 -0
  246. package/dist/react/comments.js.map +1 -0
  247. package/dist/react/config.d.ts +13 -0
  248. package/dist/react/config.js +14 -0
  249. package/dist/react/config.js.map +1 -0
  250. package/dist/react/context.d.ts +48 -0
  251. package/dist/react/context.js +37 -0
  252. package/dist/react/context.js.map +1 -0
  253. package/dist/react/crop.d.ts +49 -0
  254. package/dist/react/crop.js +107 -0
  255. package/dist/react/crop.js.map +1 -0
  256. package/dist/react/engagement.d.ts +38 -0
  257. package/dist/react/engagement.js +88 -0
  258. package/dist/react/engagement.js.map +1 -0
  259. package/dist/react/folder.d.ts +83 -0
  260. package/dist/react/folder.js +183 -0
  261. package/dist/react/folder.js.map +1 -0
  262. package/dist/react/gallery.d.ts +140 -0
  263. package/dist/react/gallery.js +477 -0
  264. package/dist/react/gallery.js.map +1 -0
  265. package/dist/react/index.d.ts +21 -0
  266. package/dist/react/inline-preview.d.ts +24 -0
  267. package/dist/react/inline-preview.js +76 -0
  268. package/dist/react/inline-preview.js.map +1 -0
  269. package/dist/react/player.d.ts +70 -0
  270. package/dist/react/player.js +214 -0
  271. package/dist/react/player.js.map +1 -0
  272. package/dist/react/polls.d.ts +74 -0
  273. package/dist/react/polls.js +176 -0
  274. package/dist/react/polls.js.map +1 -0
  275. package/dist/react/posts.d.ts +59 -0
  276. package/dist/react/posts.js +149 -0
  277. package/dist/react/posts.js.map +1 -0
  278. package/dist/react/provider.d.ts +23 -0
  279. package/dist/react/provider.js +35 -0
  280. package/dist/react/provider.js.map +1 -0
  281. package/dist/react/public.d.ts +39 -0
  282. package/dist/react/public.js +70 -0
  283. package/dist/react/public.js.map +1 -0
  284. package/dist/react/read.d.ts +52 -0
  285. package/dist/react/read.js +143 -0
  286. package/dist/react/read.js.map +1 -0
  287. package/dist/react/resources.d.ts +70 -0
  288. package/dist/react/resources.js +247 -0
  289. package/dist/react/resources.js.map +1 -0
  290. package/dist/react/slot.d.ts +102 -0
  291. package/dist/react/slot.js +181 -0
  292. package/dist/react/slot.js.map +1 -0
  293. package/dist/react/store.d.ts +51 -0
  294. package/dist/react/store.js +164 -0
  295. package/dist/react/store.js.map +1 -0
  296. package/dist/react/upload.d.ts +85 -0
  297. package/dist/react/upload.js +150 -0
  298. package/dist/react/upload.js.map +1 -0
  299. package/dist/react/use-resource.d.ts +43 -0
  300. package/dist/react/use-resource.js +66 -0
  301. package/dist/react/use-resource.js.map +1 -0
  302. package/dist/react/video.d.ts +118 -0
  303. package/dist/react/video.js +184 -0
  304. package/dist/react/video.js.map +1 -0
  305. package/dist/react/viewport.d.ts +19 -0
  306. package/dist/react/viewport.js +30 -0
  307. package/dist/react/viewport.js.map +1 -0
  308. package/dist/react.js +21 -0
  309. package/dist/scope.d.ts +14 -0
  310. package/dist/scope.js +30 -0
  311. package/dist/scope.js.map +1 -0
  312. package/dist/styles.css +3 -0
  313. package/dist/ui/alert-dialog.d.ts +18 -0
  314. package/dist/ui/alert-dialog.js +86 -0
  315. package/dist/ui/alert-dialog.js.map +1 -0
  316. package/dist/ui/alert.d.ts +10 -0
  317. package/dist/ui/alert.js +38 -0
  318. package/dist/ui/alert.js.map +1 -0
  319. package/dist/ui/avatar.d.ts +11 -0
  320. package/dist/ui/avatar.js +31 -0
  321. package/dist/ui/avatar.js.map +1 -0
  322. package/dist/ui/badge.d.ts +7 -0
  323. package/dist/ui/badge.js +31 -0
  324. package/dist/ui/badge.js.map +1 -0
  325. package/dist/ui/button.d.ts +8 -0
  326. package/dist/ui/button.js +46 -0
  327. package/dist/ui/button.js.map +1 -0
  328. package/dist/ui/dialog.d.ts +17 -0
  329. package/dist/ui/dialog.js +91 -0
  330. package/dist/ui/dialog.js.map +1 -0
  331. package/dist/ui/dropdown-menu.d.ts +15 -0
  332. package/dist/ui/dropdown-menu.js +105 -0
  333. package/dist/ui/dropdown-menu.js.map +1 -0
  334. package/dist/ui/input.d.ts +3 -0
  335. package/dist/ui/input.js +17 -0
  336. package/dist/ui/input.js.map +1 -0
  337. package/dist/ui/label.d.ts +3 -0
  338. package/dist/ui/label.js +15 -0
  339. package/dist/ui/label.js.map +1 -0
  340. package/dist/ui/progress.d.ts +7 -0
  341. package/dist/ui/progress.js +31 -0
  342. package/dist/ui/progress.js.map +1 -0
  343. package/dist/ui/select.d.ts +15 -0
  344. package/dist/ui/select.js +98 -0
  345. package/dist/ui/select.js.map +1 -0
  346. package/dist/ui/skeleton.d.ts +2 -0
  347. package/dist/ui/skeleton.js +14 -0
  348. package/dist/ui/skeleton.js.map +1 -0
  349. package/dist/ui/slider.d.ts +3 -0
  350. package/dist/ui/slider.js +35 -0
  351. package/dist/ui/slider.js.map +1 -0
  352. package/dist/ui/switch.d.ts +5 -0
  353. package/dist/ui/switch.js +20 -0
  354. package/dist/ui/switch.js.map +1 -0
  355. package/dist/ui/tabs.d.ts +10 -0
  356. package/dist/ui/tabs.js +47 -0
  357. package/dist/ui/tabs.js.map +1 -0
  358. package/dist/ui/textarea.d.ts +3 -0
  359. package/dist/ui/textarea.js +15 -0
  360. package/dist/ui/textarea.js.map +1 -0
  361. package/dist/ui/toggle-group.d.ts +5 -0
  362. package/dist/ui/toggle-group.js +23 -0
  363. package/dist/ui/toggle-group.js.map +1 -0
  364. package/dist/urls/index.d.ts +100 -0
  365. package/dist/urls.js +118 -0
  366. package/dist/urls.js.map +1 -0
  367. package/package.json +113 -3
@@ -0,0 +1,213 @@
1
+ import { ratio as e } from "../client/aspect.js";
2
+ import { useMessages as t } from "../i18n/context.js";
3
+ import { ContentKitUiRoot as n } from "../scope.js";
4
+ import { Button as r } from "../ui/button.js";
5
+ import { SlotImage as i } from "./slot-image.js";
6
+ import { SlotEditError as a, SlotEditor as o, useSlotEditor as s } from "./slot-editor.js";
7
+ import { useState as c } from "react";
8
+ import { Fragment as l, jsx as u, jsxs as d } from "react/jsx-runtime";
9
+ import { cn as f } from "cn";
10
+ import { Camera01Icon as p, CropIcon as m, Delete02Icon as h, ImageUpload01Icon as g, Loading03Icon as _ } from "@hugeicons/core-free-icons";
11
+ import { HugeiconsIcon as v } from "@hugeicons/react";
12
+ //#region src/components/slot-upload.tsx
13
+ function y(e) {
14
+ return /* @__PURE__ */ u(x, {
15
+ ...e,
16
+ variant: "avatar"
17
+ });
18
+ }
19
+ function b(e) {
20
+ return /* @__PURE__ */ u(x, {
21
+ ...e,
22
+ variant: "cover"
23
+ });
24
+ }
25
+ function x({ variant: e, ...t }) {
26
+ let r = t.path ?? e;
27
+ return /* @__PURE__ */ u(n, {
28
+ className: f("text-sm", t.className),
29
+ "data-ckui": e === "avatar" ? "avatar-upload" : "cover-upload",
30
+ children: /* @__PURE__ */ u(o, {
31
+ item: t.item,
32
+ path: r,
33
+ image: t.image,
34
+ client: t.client,
35
+ read: t.read,
36
+ onChange: t.onChange,
37
+ aspect: t.aspect || t.image?.aspect || (e === "avatar" ? "1:1" : "3:1"),
38
+ minWidth: t.minWidth,
39
+ animation: t.animation,
40
+ targetWidth: t.targetWidth ?? (e === "avatar" ? 512 : 3e3),
41
+ round: e === "avatar",
42
+ accept: t.accept,
43
+ disabled: t.disabled,
44
+ removable: t.removable ?? e === "avatar",
45
+ decode: t.decode,
46
+ children: /* @__PURE__ */ u(S, {
47
+ ...t,
48
+ variant: e
49
+ })
50
+ })
51
+ });
52
+ }
53
+ function S({ variant: n, ...o }) {
54
+ let { t: y } = t(), b = s(), { crop: x, has: S, busy: w, disabled: T } = b, E = {
55
+ preset: o.image?.preset ?? n,
56
+ aspect: b.image.aspect,
57
+ renditions: b.image.renditions
58
+ }, [D, O] = c(!1), k = x.aspect, A = o.targetWidth ?? (n === "avatar" ? 512 : 3e3), j = (e) => {
59
+ e.preventDefault(), O(!1);
60
+ let t = [...e.dataTransfer.files].find((e) => e.type.startsWith("image/")) ?? e.dataTransfer.files[0];
61
+ t && b.pick(t);
62
+ }, M = o.hint ?? (n === "avatar" ? y("avatar.hint", { size: A }) : y("cover.hint", {
63
+ width: A,
64
+ height: Math.round(A / (e(k) ?? 3))
65
+ })), N = o.label ?? y(n === "avatar" ? "avatar.label" : "cover.label"), P = (e) => /* @__PURE__ */ d(l, { children: [
66
+ /* @__PURE__ */ d(r, {
67
+ variant: e && S ? "secondary" : "outline",
68
+ size: "sm",
69
+ disabled: T,
70
+ onClick: b.choose,
71
+ "data-ckui": "change",
72
+ children: [/* @__PURE__ */ u(v, {
73
+ icon: w ? _ : p,
74
+ strokeWidth: 2,
75
+ "data-icon": "inline-start",
76
+ className: w ? "animate-spin" : void 0
77
+ }), y(S ? "common.change" : "common.upload")]
78
+ }),
79
+ S && x.canRecrop && /* @__PURE__ */ d(r, {
80
+ variant: e ? "secondary" : "ghost",
81
+ size: "sm",
82
+ disabled: T,
83
+ onClick: b.recrop,
84
+ "data-ckui": "edit-crop",
85
+ children: [/* @__PURE__ */ u(v, {
86
+ icon: m,
87
+ strokeWidth: 2,
88
+ "data-icon": "inline-start"
89
+ }), y("common.editCrop")]
90
+ }),
91
+ S && b.removable && /* @__PURE__ */ d(r, {
92
+ variant: e ? "secondary" : "ghost",
93
+ size: "sm",
94
+ disabled: T,
95
+ onClick: b.remove,
96
+ "data-ckui": "remove",
97
+ children: [/* @__PURE__ */ u(v, {
98
+ icon: h,
99
+ strokeWidth: 2,
100
+ "data-icon": "inline-start"
101
+ }), y("common.remove")]
102
+ })
103
+ ] });
104
+ return n === "avatar" ? /* @__PURE__ */ d("div", {
105
+ className: "flex items-center gap-4",
106
+ children: [/* @__PURE__ */ d("div", {
107
+ className: "relative size-20 shrink-0",
108
+ children: [/* @__PURE__ */ u(i, {
109
+ image: S ? E : null,
110
+ round: !0,
111
+ emptyLabel: y("avatar.empty"),
112
+ className: "size-full ring-1 ring-foreground/10"
113
+ }), w && /* @__PURE__ */ u(C, {
114
+ round: !0,
115
+ label: y(x.status === "decoding" ? "progress.decoding" : "common.saving")
116
+ })]
117
+ }), /* @__PURE__ */ d("div", {
118
+ className: "grid min-w-0 gap-1",
119
+ children: [
120
+ /* @__PURE__ */ u("div", {
121
+ className: "font-medium",
122
+ children: N
123
+ }),
124
+ /* @__PURE__ */ u("div", {
125
+ className: "text-xs text-muted-foreground",
126
+ children: M
127
+ }),
128
+ /* @__PURE__ */ u("div", {
129
+ className: "mt-1.5 flex flex-wrap gap-2",
130
+ children: P(!1)
131
+ }),
132
+ /* @__PURE__ */ u(a, {})
133
+ ]
134
+ })]
135
+ }) : /* @__PURE__ */ d("div", {
136
+ className: "grid gap-2",
137
+ children: [
138
+ /* @__PURE__ */ d("div", {
139
+ className: f("group/cover relative overflow-hidden rounded-xl ring-1 ring-foreground/10 transition-shadow", D && "ring-2 ring-ring"),
140
+ onDragOver: (e) => {
141
+ e.preventDefault(), T || O(!0);
142
+ },
143
+ onDragLeave: () => O(!1),
144
+ onDrop: j,
145
+ children: [
146
+ /* @__PURE__ */ u(i, {
147
+ image: S ? E : null,
148
+ aspect: k,
149
+ className: "w-full rounded-none",
150
+ fallback: /* @__PURE__ */ d("div", {
151
+ "aria-label": y("cover.empty"),
152
+ role: "group",
153
+ className: "absolute inset-0 flex flex-col items-center justify-center gap-1.5 bg-[repeating-linear-gradient(135deg,transparent_0_10px,color-mix(in_oklch,var(--ckui-foreground)_3%,transparent)_10px_20px)] p-4 text-center",
154
+ children: [
155
+ /* @__PURE__ */ u(v, {
156
+ icon: g,
157
+ strokeWidth: 1.5,
158
+ className: "size-7 text-muted-foreground"
159
+ }),
160
+ /* @__PURE__ */ u("span", {
161
+ className: "hidden text-xs text-muted-foreground sm:block",
162
+ children: y("cover.drop")
163
+ }),
164
+ /* @__PURE__ */ u("div", {
165
+ className: "mt-1 flex gap-2",
166
+ children: P(!1)
167
+ })
168
+ ]
169
+ })
170
+ }),
171
+ w && /* @__PURE__ */ u(C, { label: y(x.status === "decoding" ? "progress.decoding" : "common.saving") }),
172
+ S && /* @__PURE__ */ u("div", {
173
+ className: "absolute right-2 bottom-2 flex gap-2 max-sm:hidden [&_button]:bg-background/80 [&_button]:backdrop-blur-sm",
174
+ children: P(!0)
175
+ })
176
+ ]
177
+ }),
178
+ /* @__PURE__ */ d("div", {
179
+ className: "flex flex-wrap items-center justify-between gap-x-3 gap-y-1.5",
180
+ children: [/* @__PURE__ */ d("div", {
181
+ className: "grid min-w-0 gap-0.5 sm:flex sm:flex-1 sm:items-baseline sm:justify-between sm:gap-3",
182
+ children: [/* @__PURE__ */ u("div", {
183
+ className: "font-medium",
184
+ children: N
185
+ }), /* @__PURE__ */ u("div", {
186
+ className: "text-xs text-muted-foreground sm:truncate",
187
+ children: M
188
+ })]
189
+ }), S && /* @__PURE__ */ u("div", {
190
+ className: "flex gap-2 sm:hidden",
191
+ children: P(!1)
192
+ })]
193
+ }),
194
+ /* @__PURE__ */ u(a, {})
195
+ ]
196
+ });
197
+ }
198
+ function C({ round: e, label: t }) {
199
+ return /* @__PURE__ */ u("div", {
200
+ role: "status",
201
+ "aria-label": t,
202
+ className: f("absolute inset-0 flex items-center justify-center bg-background/60 backdrop-blur-[1px]", e && "rounded-full"),
203
+ children: /* @__PURE__ */ u(v, {
204
+ icon: _,
205
+ strokeWidth: 2,
206
+ className: "size-5 animate-spin text-foreground"
207
+ })
208
+ });
209
+ }
210
+ //#endregion
211
+ export { y as AvatarUpload, b as CoverUpload };
212
+
213
+ //# sourceMappingURL=slot-upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slot-upload.js","names":[],"sources":["../../src/components/slot-upload.tsx"],"sourcesContent":["import { ratio, type AspectRatio } from \"../client/aspect.js\";\nimport { Camera01Icon, CropIcon, Delete02Icon, ImageUpload01Icon, Loading03Icon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useState, type DragEvent, type ReactNode } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { useMessages } from \"../i18n/context.js\";\nimport type { CropSource } from \"../client/image.js\";\nimport { ContentKitUiRoot } from \"../scope.js\";\nimport type { PublicPreset } from \"../client/public.js\";\nimport type { FileInfo, ReadResult, RefBody } from \"../client/generated/wire.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { SlotEditError, SlotEditor, useSlotEditor } from \"./slot-editor.js\";\nimport { SlotImage } from \"./slot-image.js\";\n\nexport interface SlotUploadProps {\n /** The item that owns the upload. */\n item: RefBody;\n /** The upload path; default \"avatar\" or \"cover\". */\n path?: string;\n /** The public preset showing it. */\n image?: PublicPreset | null;\n client?: ContentKitClient;\n /** An editor read of the item from the host; otherwise fetched. */\n read?: ReadResult | null;\n /** Called with the processed upload after every save, null after a removal. */\n onChange?: (file: FileInfo | null) => void;\n /** The output's \"W:H\"; default the preset's aspect, else \"1:1\" (avatar) or \"3:1\" (cover). */\n aspect?: AspectRatio;\n /** The narrowest edit the server accepts (the preset's Image.MinWidth). */\n minWidth?: number;\n /** \"reject\" refuses animated images before uploading. */\n animation?: \"reject\";\n /** Crops narrower than this many source pixels get a sharpness warning; default 512 (avatar) or 3000 (cover). */\n targetWidth?: number;\n /** `accept` of the file input. Default \"image/*\". */\n accept?: string;\n disabled?: boolean;\n /** Offers Remove once set; default true for avatars, false for covers. */\n removable?: boolean;\n label?: ReactNode;\n hint?: ReactNode;\n className?: string;\n /** Replaces decodeImage (tests). */\n decode?: (file: File) => Promise<CropSource>;\n}\n\ntype Variant = \"avatar\" | \"cover\";\n\n/** Round 1:1 avatar field: image, label, hint, Change / Edit crop, crop dialog, progress, errors. For custom layouts use SlotEditor. */\nexport function AvatarUpload(p: SlotUploadProps) {\n return <SlotUpload {...p} variant=\"avatar\" />;\n}\n\n/** Wide cover field at the preset's aspect (e.g. 3:1), with drop-to-upload. For custom layouts use SlotEditor. */\nexport function CoverUpload(p: SlotUploadProps) {\n return <SlotUpload {...p} variant=\"cover\" />;\n}\n\nfunction SlotUpload({ variant, ...p }: SlotUploadProps & { variant: Variant }) {\n const path = p.path ?? variant;\n return (\n <ContentKitUiRoot className={cn(\"text-sm\", p.className)} data-ckui={variant === \"avatar\" ? \"avatar-upload\" : \"cover-upload\"}>\n <SlotEditor\n item={p.item}\n path={path}\n image={p.image}\n client={p.client}\n read={p.read}\n onChange={p.onChange}\n aspect={p.aspect || p.image?.aspect || (variant === \"avatar\" ? \"1:1\" : \"3:1\")}\n minWidth={p.minWidth}\n animation={p.animation}\n targetWidth={p.targetWidth ?? (variant === \"avatar\" ? 512 : 3000)}\n round={variant === \"avatar\"}\n accept={p.accept}\n disabled={p.disabled}\n removable={p.removable ?? variant === \"avatar\"}\n decode={p.decode}\n >\n <SlotUploadLayout {...p} variant={variant} />\n </SlotEditor>\n </ContentKitUiRoot>\n );\n}\n\nfunction SlotUploadLayout({ variant, ...p }: SlotUploadProps & { variant: Variant }) {\n const { t } = useMessages();\n const s = useSlotEditor();\n const { crop, has, busy, disabled } = s;\n const image = { preset: p.image?.preset ?? variant, aspect: s.image.aspect, renditions: s.image.renditions };\n const [dragging, setDragging] = useState(false);\n const aspect = crop.aspect;\n const target = p.targetWidth ?? (variant === \"avatar\" ? 512 : 3000);\n const onDrop = (e: DragEvent) => {\n e.preventDefault();\n setDragging(false);\n const f = [...e.dataTransfer.files].find((f) => f.type.startsWith(\"image/\")) ?? e.dataTransfer.files[0];\n if (f) s.pick(f);\n };\n\n const hint =\n p.hint ?? (variant === \"avatar\" ? t(\"avatar.hint\", { size: target }) : t(\"cover.hint\", { width: target, height: Math.round(target / (ratio(aspect) ?? 3)) }));\n const label = p.label ?? t(variant === \"avatar\" ? \"avatar.label\" : \"cover.label\");\n\n // overlay: buttons on top of the cover image; otherwise a plain row.\n const actions = (overlay: boolean) => (\n <>\n <Button variant={overlay && has ? \"secondary\" : \"outline\"} size=\"sm\" disabled={disabled} onClick={s.choose} data-ckui=\"change\">\n <HugeiconsIcon icon={busy ? Loading03Icon : Camera01Icon} strokeWidth={2} data-icon=\"inline-start\" className={busy ? \"animate-spin\" : undefined} />\n {has ? t(\"common.change\") : t(\"common.upload\")}\n </Button>\n {has && crop.canRecrop && (\n <Button variant={overlay ? \"secondary\" : \"ghost\"} size=\"sm\" disabled={disabled} onClick={s.recrop} data-ckui=\"edit-crop\">\n <HugeiconsIcon icon={CropIcon} strokeWidth={2} data-icon=\"inline-start\" />\n {t(\"common.editCrop\")}\n </Button>\n )}\n {has && s.removable && (\n <Button variant={overlay ? \"secondary\" : \"ghost\"} size=\"sm\" disabled={disabled} onClick={s.remove} data-ckui=\"remove\">\n <HugeiconsIcon icon={Delete02Icon} strokeWidth={2} data-icon=\"inline-start\" />\n {t(\"common.remove\")}\n </Button>\n )}\n </>\n );\n\n return variant === \"avatar\" ? (\n <div className=\"flex items-center gap-4\">\n <div className=\"relative size-20 shrink-0\">\n <SlotImage image={has ? image : null} round emptyLabel={t(\"avatar.empty\")} className=\"size-full ring-1 ring-foreground/10\" />\n {busy && <BusyOverlay round label={t(crop.status === \"decoding\" ? \"progress.decoding\" : \"common.saving\")} />}\n </div>\n <div className=\"grid min-w-0 gap-1\">\n <div className=\"font-medium\">{label}</div>\n <div className=\"text-xs text-muted-foreground\">{hint}</div>\n <div className=\"mt-1.5 flex flex-wrap gap-2\">{actions(false)}</div>\n <SlotEditError />\n </div>\n </div>\n ) : (\n <div className=\"grid gap-2\">\n <div\n className={cn(\n \"group/cover relative overflow-hidden rounded-xl ring-1 ring-foreground/10 transition-shadow\",\n dragging && \"ring-2 ring-ring\",\n )}\n onDragOver={(e) => {\n e.preventDefault();\n if (!disabled) setDragging(true);\n }}\n onDragLeave={() => setDragging(false)}\n onDrop={onDrop}\n >\n <SlotImage\n image={has ? image : null}\n aspect={aspect}\n className=\"w-full rounded-none\"\n fallback={\n <div aria-label={t(\"cover.empty\")} role=\"group\" className=\"absolute inset-0 flex flex-col items-center justify-center gap-1.5 bg-[repeating-linear-gradient(135deg,transparent_0_10px,color-mix(in_oklch,var(--ckui-foreground)_3%,transparent)_10px_20px)] p-4 text-center\">\n <HugeiconsIcon icon={ImageUpload01Icon} strokeWidth={1.5} className=\"size-7 text-muted-foreground\" />\n <span className=\"hidden text-xs text-muted-foreground sm:block\">{t(\"cover.drop\")}</span>\n <div className=\"mt-1 flex gap-2\">{actions(false)}</div>\n </div>\n }\n />\n {busy && <BusyOverlay label={t(crop.status === \"decoding\" ? \"progress.decoding\" : \"common.saving\")} />}\n {has && (\n <div className=\"absolute right-2 bottom-2 flex gap-2 max-sm:hidden [&_button]:bg-background/80 [&_button]:backdrop-blur-sm\">\n {actions(true)}\n </div>\n )}\n </div>\n <div className=\"flex flex-wrap items-center justify-between gap-x-3 gap-y-1.5\">\n <div className=\"grid min-w-0 gap-0.5 sm:flex sm:flex-1 sm:items-baseline sm:justify-between sm:gap-3\">\n <div className=\"font-medium\">{label}</div>\n <div className=\"text-xs text-muted-foreground sm:truncate\">{hint}</div>\n </div>\n {has && <div className=\"flex gap-2 sm:hidden\">{actions(false)}</div>}\n </div>\n <SlotEditError />\n </div>\n );\n}\n\nfunction BusyOverlay({ round, label }: { round?: boolean; label: string }) {\n return (\n <div role=\"status\" aria-label={label} className={cn(\"absolute inset-0 flex items-center justify-center bg-background/60 backdrop-blur-[1px]\", round && \"rounded-full\")}>\n <HugeiconsIcon icon={Loading03Icon} strokeWidth={2} className=\"size-5 animate-spin text-foreground\" />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { ContentKitClient } from '../../client/client.js';
2
+ import { BanScope } from '../../client/content/types.js';
3
+ import { BanNotice as BanNoticeData, CommentBan, PublicUser } from '../../client/generated/wire.js';
4
+ import { ContentKitUiAppearance } from '../../appearance.js';
5
+ import { ContentKitErrorHandler } from '../../react/context.js';
6
+ /** What a banned user is told: where, why and until when (never who). */
7
+ export declare function BanNotice({ ban, className }: {
8
+ ban: BanNoticeData;
9
+ className?: string;
10
+ }): import('react').JSX.Element;
11
+ /** "YYYY-MM-DDTHH:mm" in local time, for a datetime-local input. */
12
+ export declare function localInput(d: Date): string;
13
+ export interface CommentBanDialogProps {
14
+ open: boolean;
15
+ onOpenChange: (open: boolean) => void;
16
+ /** Who to ban: their id and the name to show. */
17
+ user: {
18
+ id: string;
19
+ name?: string;
20
+ };
21
+ /** The scopes the caller may ban in (CommentStanding.ban_scopes); the first is preselected. */
22
+ scopes: readonly BanScope[];
23
+ /** The current ban, when updating one. */
24
+ ban?: CommentBan | null;
25
+ onSaved?: (ban: CommentBan) => void;
26
+ client?: ContentKitClient;
27
+ onError?: ContentKitErrorHandler;
28
+ }
29
+ /** Bans a user from commenting, or replaces their ban: scope, duration presets or an end date, and a reason they see. */
30
+ export declare function CommentBanDialog({ open, onOpenChange, user, scopes, ban, onSaved, client: own, onError }: CommentBanDialogProps): import('react').JSX.Element;
31
+ export interface CommentBansProps {
32
+ /** "global": the site's bans (CommentBan); "owner": the caller's own. Default "global". */
33
+ scope?: BanScope;
34
+ pageSize?: number;
35
+ userHref?: (user: PublicUser) => string | undefined;
36
+ client?: ContentKitClient;
37
+ onError?: ContentKitErrorHandler;
38
+ className?: string;
39
+ appearance?: ContentKitUiAppearance;
40
+ }
41
+ /** A scope's comment bans, newest first: who, why, until when; ban a user by id, update or lift a ban. */
42
+ export declare function CommentBans({ scope, pageSize, userHref, client, onError, className, appearance }: CommentBansProps): import('react').JSX.Element;
@@ -0,0 +1,300 @@
1
+ import { useMessages as e } from "../../i18n/context.js";
2
+ import { ContentKitUiRoot as t } from "../../scope.js";
3
+ import { useContentKitClient as n } from "../../react/context.js";
4
+ import { Alert as r, AlertDescription as i } from "../../ui/alert.js";
5
+ import { Button as a } from "../../ui/button.js";
6
+ import { Dialog as o, DialogContent as s, DialogDescription as c, DialogFooter as l, DialogHeader as u, DialogTitle as d } from "../../ui/dialog.js";
7
+ import { useCommentBans as f } from "../../react/comments.js";
8
+ import { Badge as p } from "../../ui/badge.js";
9
+ import { Input as m } from "../../ui/input.js";
10
+ import { Label as h } from "../../ui/label.js";
11
+ import { Textarea as g } from "../../ui/textarea.js";
12
+ import { ConfirmDialog as _, ErrorLine as v, UserAvatar as y, UserName as b, formatDate as x, useAction as S } from "./parts.js";
13
+ import { useEffect as C, useId as w, useState as T } from "react";
14
+ import { jsx as E, jsxs as D } from "react/jsx-runtime";
15
+ import { cn as O } from "cn";
16
+ //#region src/components/social/bans.tsx
17
+ function k({ ban: t, className: n }) {
18
+ let a = e(), { t: o } = a;
19
+ return /* @__PURE__ */ E(r, {
20
+ variant: "destructive",
21
+ className: n,
22
+ "data-ckui": "ban-notice",
23
+ children: /* @__PURE__ */ D(i, {
24
+ className: "grid gap-1",
25
+ children: [
26
+ /* @__PURE__ */ E("p", {
27
+ className: "font-medium",
28
+ children: o(t.scope === "global" ? "bans.globalNotice" : "bans.ownerNotice")
29
+ }),
30
+ t.reason && /* @__PURE__ */ E("p", { children: o("bans.reason", { reason: t.reason }) }),
31
+ /* @__PURE__ */ E("p", { children: t.until ? o("bans.until", { date: x(a, t.until) }) : o("bans.untilLifted") })
32
+ ]
33
+ })
34
+ });
35
+ }
36
+ var A = 864e5, j = {
37
+ day: A,
38
+ week: 7 * A,
39
+ month: 30 * A,
40
+ forever: 0,
41
+ custom: 0
42
+ };
43
+ function M(e) {
44
+ let t = (e) => String(e).padStart(2, "0");
45
+ return `${e.getFullYear()}-${t(e.getMonth() + 1)}-${t(e.getDate())}T${t(e.getHours())}:${t(e.getMinutes())}`;
46
+ }
47
+ function N({ open: t, onOpenChange: r, user: i, scopes: f, ban: p, onSaved: _, client: y, onError: b }) {
48
+ let { t: x } = e(), O = n(y), k = w(), N = p ? p.scope === "global" ? "global" : "owner" : f[0] ?? "owner", [P, F] = T(N), [I, L] = T(p ? p.until ? "custom" : "forever" : "week"), [R, z] = T(p?.until ? M(new Date(p.until)) : M(new Date(Date.now() + 7 * A))), [B, V] = T(p?.reason ?? ""), { run: H, pending: U, error: W, setError: G } = S("ban.save", b);
49
+ C(() => {
50
+ t && (F(N), L(p ? p.until ? "custom" : "forever" : "week"), z(p?.until ? M(new Date(p.until)) : M(new Date(Date.now() + 7 * A))), V(p?.reason ?? ""), G(null));
51
+ }, [t]);
52
+ let K = i.name ?? i.id, q = async () => {
53
+ let e = I === "forever" ? void 0 : I === "custom" ? new Date(R) : new Date(Date.now() + j[I]), t = await H(() => O.bans.ban(P, i.id, {
54
+ reason: B.trim() || void 0,
55
+ until: e?.toISOString()
56
+ }));
57
+ t && (_?.(t), r(!1));
58
+ };
59
+ return /* @__PURE__ */ E(o, {
60
+ open: t,
61
+ onOpenChange: r,
62
+ children: /* @__PURE__ */ D(s, {
63
+ "data-ckui": "comment-ban-dialog",
64
+ children: [/* @__PURE__ */ D(u, { children: [/* @__PURE__ */ E(d, { children: x("bans.title", { name: K }) }), /* @__PURE__ */ E(c, { children: x(P === "global" ? "bans.globalHelp" : "bans.ownerHelp") })] }), /* @__PURE__ */ D("form", {
65
+ className: "grid gap-4",
66
+ onSubmit: (e) => {
67
+ e.preventDefault(), q();
68
+ },
69
+ children: [
70
+ f.length > 1 && !p && /* @__PURE__ */ D("fieldset", {
71
+ className: "grid gap-2",
72
+ children: [/* @__PURE__ */ E("legend", {
73
+ className: "mb-1 text-sm font-medium",
74
+ children: x("bans.scope")
75
+ }), /* @__PURE__ */ E("div", {
76
+ className: "flex flex-wrap gap-2",
77
+ children: f.map((e) => /* @__PURE__ */ E(a, {
78
+ type: "button",
79
+ size: "sm",
80
+ variant: P === e ? "default" : "outline",
81
+ "aria-pressed": P === e,
82
+ onClick: () => F(e),
83
+ children: x(e === "global" ? "bans.scopeGlobal" : "bans.scopeOwner")
84
+ }, e))
85
+ })]
86
+ }),
87
+ /* @__PURE__ */ D("fieldset", {
88
+ className: "grid gap-2",
89
+ children: [
90
+ /* @__PURE__ */ E("legend", {
91
+ className: "mb-1 text-sm font-medium",
92
+ children: x("bans.duration")
93
+ }),
94
+ /* @__PURE__ */ E("div", {
95
+ className: "flex flex-wrap gap-2",
96
+ children: [
97
+ "day",
98
+ "week",
99
+ "month",
100
+ "forever",
101
+ "custom"
102
+ ].map((e) => /* @__PURE__ */ E(a, {
103
+ type: "button",
104
+ size: "sm",
105
+ variant: I === e ? "default" : "outline",
106
+ "aria-pressed": I === e,
107
+ onClick: () => L(e),
108
+ children: x(`bans.${e}`)
109
+ }, e))
110
+ }),
111
+ I === "custom" && /* @__PURE__ */ D("div", {
112
+ className: "grid gap-1.5",
113
+ children: [/* @__PURE__ */ E(h, {
114
+ htmlFor: `${k}-until`,
115
+ children: x("bans.endsAt")
116
+ }), /* @__PURE__ */ E(m, {
117
+ id: `${k}-until`,
118
+ type: "datetime-local",
119
+ required: !0,
120
+ value: R,
121
+ min: M(/* @__PURE__ */ new Date()),
122
+ onChange: (e) => z(e.target.value)
123
+ })]
124
+ })
125
+ ]
126
+ }),
127
+ /* @__PURE__ */ D("div", {
128
+ className: "grid gap-1.5",
129
+ children: [/* @__PURE__ */ E(h, {
130
+ htmlFor: `${k}-reason`,
131
+ children: x("bans.reasonLabel")
132
+ }), /* @__PURE__ */ E(g, {
133
+ id: `${k}-reason`,
134
+ maxLength: 500,
135
+ rows: 3,
136
+ placeholder: x("bans.reasonPlaceholder"),
137
+ value: B,
138
+ onChange: (e) => V(e.target.value)
139
+ })]
140
+ }),
141
+ /* @__PURE__ */ E(v, { error: W }),
142
+ /* @__PURE__ */ D(l, { children: [/* @__PURE__ */ E(a, {
143
+ type: "button",
144
+ variant: "outline",
145
+ onClick: () => r(!1),
146
+ children: x("common.cancel")
147
+ }), /* @__PURE__ */ E(a, {
148
+ type: "submit",
149
+ variant: "destructive",
150
+ disabled: U,
151
+ children: x(U ? "common.saving" : p ? "bans.update" : "bans.save")
152
+ })] })
153
+ ]
154
+ })]
155
+ })
156
+ });
157
+ }
158
+ function P({ scope: n = "global", pageSize: r = 25, userHref: i, client: o, onError: s, className: c, appearance: l }) {
159
+ let u = e(), { t: d } = u, g = f({
160
+ scope: n,
161
+ pageSize: r,
162
+ client: o
163
+ }), C = w(), [k, A] = T(null), [j, M] = T(null), [P, F] = T(""), I = S("ban.lift", s);
164
+ return /* @__PURE__ */ D(t, {
165
+ appearance: l,
166
+ className: O("grid gap-4 text-sm", c),
167
+ "data-ckui": "comment-bans",
168
+ children: [
169
+ /* @__PURE__ */ D("form", {
170
+ className: "flex flex-wrap items-end gap-2",
171
+ onSubmit: (e) => {
172
+ e.preventDefault(), P.trim() && A({ user: { id: P.trim() } });
173
+ },
174
+ children: [/* @__PURE__ */ D("div", {
175
+ className: "grid min-w-48 flex-1 gap-1.5",
176
+ children: [/* @__PURE__ */ E(h, {
177
+ htmlFor: `${C}-user`,
178
+ children: d("bans.user")
179
+ }), /* @__PURE__ */ E(m, {
180
+ id: `${C}-user`,
181
+ value: P,
182
+ placeholder: d("bans.userPlaceholder"),
183
+ onChange: (e) => F(e.target.value)
184
+ })]
185
+ }), /* @__PURE__ */ E(a, {
186
+ type: "submit",
187
+ variant: "outline",
188
+ disabled: !P.trim(),
189
+ children: d("bans.add")
190
+ })]
191
+ }),
192
+ /* @__PURE__ */ E(v, { error: I.error }),
193
+ g.error && !g.items.length ? /* @__PURE__ */ D("div", {
194
+ className: "grid justify-items-start gap-2",
195
+ children: [/* @__PURE__ */ E(v, { children: d("bans.loadFailed") }), /* @__PURE__ */ E(a, {
196
+ size: "sm",
197
+ variant: "outline",
198
+ onClick: g.reload,
199
+ children: d("common.retry")
200
+ })]
201
+ }) : !g.loading && !g.items.length ? /* @__PURE__ */ E("p", {
202
+ className: "text-muted-foreground",
203
+ children: d("bans.empty")
204
+ }) : /* @__PURE__ */ E("ul", {
205
+ className: "grid divide-y divide-border rounded-lg border border-border",
206
+ "aria-busy": g.loading,
207
+ children: g.items.map((e) => {
208
+ let t = e.user?.username ?? e.user_id;
209
+ return /* @__PURE__ */ D("li", {
210
+ className: "flex flex-wrap items-start gap-3 p-3",
211
+ "data-ckui": "comment-ban",
212
+ children: [
213
+ /* @__PURE__ */ E(y, {
214
+ user: e.user,
215
+ name: t
216
+ }),
217
+ /* @__PURE__ */ D("div", {
218
+ className: "grid min-w-0 flex-1 gap-0.5",
219
+ children: [
220
+ /* @__PURE__ */ D("div", {
221
+ className: "flex flex-wrap items-center gap-2",
222
+ children: [/* @__PURE__ */ E(b, {
223
+ user: e.user,
224
+ name: t,
225
+ href: i
226
+ }), /* @__PURE__ */ E(p, {
227
+ variant: e.expired ? "outline" : "destructive",
228
+ children: d(e.expired ? "bans.expired" : "bans.active")
229
+ })]
230
+ }),
231
+ e.reason && /* @__PURE__ */ E("p", {
232
+ className: "break-words",
233
+ children: d("bans.reason", { reason: e.reason })
234
+ }),
235
+ /* @__PURE__ */ D("p", {
236
+ className: "text-muted-foreground",
237
+ children: [
238
+ e.until ? d("bans.until", { date: x(u, e.until) }) : d("bans.untilLifted"),
239
+ " · ",
240
+ d("bans.bannedAt", { date: x(u, e.banned_at, !1) })
241
+ ]
242
+ })
243
+ ]
244
+ }),
245
+ /* @__PURE__ */ D("div", {
246
+ className: "flex gap-2",
247
+ children: [/* @__PURE__ */ E(a, {
248
+ size: "sm",
249
+ variant: "outline",
250
+ onClick: () => A({
251
+ user: {
252
+ id: e.user_id,
253
+ name: t
254
+ },
255
+ ban: e
256
+ }),
257
+ children: d("bans.update")
258
+ }), /* @__PURE__ */ E(a, {
259
+ size: "sm",
260
+ variant: "destructive",
261
+ onClick: () => M(e),
262
+ children: d("bans.lift")
263
+ })]
264
+ })
265
+ ]
266
+ }, e.user_id);
267
+ })
268
+ }),
269
+ g.hasMore && /* @__PURE__ */ E(a, {
270
+ variant: "outline",
271
+ className: "justify-self-center",
272
+ disabled: g.loadingMore,
273
+ onClick: () => void g.loadMore(),
274
+ children: d("moderation.loadMore")
275
+ }),
276
+ k && /* @__PURE__ */ E(N, {
277
+ open: !0,
278
+ onOpenChange: (e) => !e && A(null),
279
+ user: k.user,
280
+ scopes: [n],
281
+ ban: k.ban,
282
+ client: o,
283
+ onError: s,
284
+ onSaved: () => F("")
285
+ }),
286
+ /* @__PURE__ */ E(_, {
287
+ open: !!j,
288
+ onOpenChange: (e) => !e && M(null),
289
+ title: d("bans.liftTitle", { name: j?.user?.username ?? j?.user_id ?? "" }),
290
+ description: d("bans.liftBody"),
291
+ confirm: d("bans.lift"),
292
+ onConfirm: () => j && void I.run(() => g.lift(j.user_id))
293
+ })
294
+ ]
295
+ });
296
+ }
297
+ //#endregion
298
+ export { k as BanNotice, N as CommentBanDialog, P as CommentBans, M as localInput };
299
+
300
+ //# sourceMappingURL=bans.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bans.js","names":[],"sources":["../../../src/components/social/bans.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useEffect, useId, useState } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { BanScope } from \"../../client/content/types.js\";\nimport type { BanNotice as BanNoticeData, CommentBan, PublicUser } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useCommentBans } from \"../../react/comments.js\";\nimport { useContentKitClient } from \"../../react/context.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Alert, AlertDescription } from \"#ckui/ui/alert\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from \"#ckui/ui/dialog\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Textarea } from \"#ckui/ui/textarea\";\nimport { ConfirmDialog, ErrorLine, formatDate, UserAvatar, UserName, useAction } from \"./parts.js\";\n\n/** What a banned user is told: where, why and until when (never who). */\nexport function BanNotice({ ban, className }: { ban: BanNoticeData; className?: string }) {\n const m = useMessages();\n const { t } = m;\n return (\n <Alert variant=\"destructive\" className={className} data-ckui=\"ban-notice\">\n <AlertDescription className=\"grid gap-1\">\n <p className=\"font-medium\">{t(ban.scope === \"global\" ? \"bans.globalNotice\" : \"bans.ownerNotice\")}</p>\n {ban.reason && <p>{t(\"bans.reason\", { reason: ban.reason })}</p>}\n <p>{ban.until ? t(\"bans.until\", { date: formatDate(m, ban.until) }) : t(\"bans.untilLifted\")}</p>\n </AlertDescription>\n </Alert>\n );\n}\n\nconst DAY = 86_400_000;\ntype Duration = \"day\" | \"week\" | \"month\" | \"forever\" | \"custom\";\nconst span: Record<Duration, number> = { day: DAY, week: 7 * DAY, month: 30 * DAY, forever: 0, custom: 0 };\n\n/** \"YYYY-MM-DDTHH:mm\" in local time, for a datetime-local input. */\nexport function localInput(d: Date): string {\n const p = (n: number) => String(n).padStart(2, \"0\");\n return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}`;\n}\n\nexport interface CommentBanDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Who to ban: their id and the name to show. */\n user: { id: string; name?: string };\n /** The scopes the caller may ban in (CommentStanding.ban_scopes); the first is preselected. */\n scopes: readonly BanScope[];\n /** The current ban, when updating one. */\n ban?: CommentBan | null;\n onSaved?: (ban: CommentBan) => void;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n}\n\n/** Bans a user from commenting, or replaces their ban: scope, duration presets or an end date, and a reason they see. */\nexport function CommentBanDialog({ open, onOpenChange, user, scopes, ban, onSaved, client: own, onError }: CommentBanDialogProps) {\n const m = useMessages();\n const { t } = m;\n const client = useContentKitClient(own);\n const ids = useId();\n const initialScope: BanScope = ban ? (ban.scope === \"global\" ? \"global\" : \"owner\") : (scopes[0] ?? \"owner\");\n const [scope, setScope] = useState<BanScope>(initialScope);\n const [duration, setDuration] = useState<Duration>(ban ? (ban.until ? \"custom\" : \"forever\") : \"week\");\n const [until, setUntil] = useState(ban?.until ? localInput(new Date(ban.until)) : localInput(new Date(Date.now() + 7 * DAY)));\n const [reason, setReason] = useState(ban?.reason ?? \"\");\n const { run, pending, error, setError } = useAction(\"ban.save\", onError);\n useEffect(() => {\n if (!open) return;\n setScope(initialScope);\n setDuration(ban ? (ban.until ? \"custom\" : \"forever\") : \"week\");\n setUntil(ban?.until ? localInput(new Date(ban.until)) : localInput(new Date(Date.now() + 7 * DAY)));\n setReason(ban?.reason ?? \"\");\n setError(null);\n // Reset when it opens.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n const name = user.name ?? user.id;\n const save = async () => {\n const end = duration === \"forever\" ? undefined : duration === \"custom\" ? new Date(until) : new Date(Date.now() + span[duration]);\n const saved = await run(() => client.bans.ban(scope, user.id, { reason: reason.trim() || undefined, until: end?.toISOString() }));\n if (saved) {\n onSaved?.(saved);\n onOpenChange(false);\n }\n };\n const durations: Duration[] = [\"day\", \"week\", \"month\", \"forever\", \"custom\"];\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent data-ckui=\"comment-ban-dialog\">\n <DialogHeader>\n <DialogTitle>{t(\"bans.title\", { name })}</DialogTitle>\n <DialogDescription>{t(scope === \"global\" ? \"bans.globalHelp\" : \"bans.ownerHelp\")}</DialogDescription>\n </DialogHeader>\n <form\n className=\"grid gap-4\"\n onSubmit={(e) => {\n e.preventDefault();\n void save();\n }}\n >\n {scopes.length > 1 && !ban && (\n <fieldset className=\"grid gap-2\">\n <legend className=\"mb-1 text-sm font-medium\">{t(\"bans.scope\")}</legend>\n <div className=\"flex flex-wrap gap-2\">\n {scopes.map((s) => (\n <Button key={s} type=\"button\" size=\"sm\" variant={scope === s ? \"default\" : \"outline\"} aria-pressed={scope === s} onClick={() => setScope(s)}>\n {t(s === \"global\" ? \"bans.scopeGlobal\" : \"bans.scopeOwner\")}\n </Button>\n ))}\n </div>\n </fieldset>\n )}\n <fieldset className=\"grid gap-2\">\n <legend className=\"mb-1 text-sm font-medium\">{t(\"bans.duration\")}</legend>\n <div className=\"flex flex-wrap gap-2\">\n {durations.map((d) => (\n <Button key={d} type=\"button\" size=\"sm\" variant={duration === d ? \"default\" : \"outline\"} aria-pressed={duration === d} onClick={() => setDuration(d)}>\n {t(`bans.${d}`)}\n </Button>\n ))}\n </div>\n {duration === \"custom\" && (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-until`}>{t(\"bans.endsAt\")}</Label>\n <Input id={`${ids}-until`} type=\"datetime-local\" required value={until} min={localInput(new Date())} onChange={(e) => setUntil(e.target.value)} />\n </div>\n )}\n </fieldset>\n <div className=\"grid gap-1.5\">\n <Label htmlFor={`${ids}-reason`}>{t(\"bans.reasonLabel\")}</Label>\n <Textarea id={`${ids}-reason`} maxLength={500} rows={3} placeholder={t(\"bans.reasonPlaceholder\")} value={reason} onChange={(e) => setReason(e.target.value)} />\n </div>\n <ErrorLine error={error} />\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => onOpenChange(false)}>\n {t(\"common.cancel\")}\n </Button>\n <Button type=\"submit\" variant=\"destructive\" disabled={pending}>\n {pending ? t(\"common.saving\") : t(ban ? \"bans.update\" : \"bans.save\")}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n\nexport interface CommentBansProps {\n /** \"global\": the site's bans (CommentBan); \"owner\": the caller's own. Default \"global\". */\n scope?: BanScope;\n pageSize?: number;\n userHref?: (user: PublicUser) => string | undefined;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** A scope's comment bans, newest first: who, why, until when; ban a user by id, update or lift a ban. */\nexport function CommentBans({ scope = \"global\", pageSize = 25, userHref, client, onError, className, appearance }: CommentBansProps) {\n const m = useMessages();\n const { t } = m;\n const bans = useCommentBans({ scope, pageSize, client });\n const ids = useId();\n const [editing, setEditing] = useState<{ user: { id: string; name?: string }; ban?: CommentBan } | null>(null);\n const [lifting, setLifting] = useState<CommentBan | null>(null);\n const [userId, setUserId] = useState(\"\");\n const lift = useAction(\"ban.lift\", onError);\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-4 text-sm\", className)} data-ckui=\"comment-bans\">\n <form\n className=\"flex flex-wrap items-end gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n if (userId.trim()) setEditing({ user: { id: userId.trim() } });\n }}\n >\n <div className=\"grid min-w-48 flex-1 gap-1.5\">\n <Label htmlFor={`${ids}-user`}>{t(\"bans.user\")}</Label>\n <Input id={`${ids}-user`} value={userId} placeholder={t(\"bans.userPlaceholder\")} onChange={(e) => setUserId(e.target.value)} />\n </div>\n <Button type=\"submit\" variant=\"outline\" disabled={!userId.trim()}>\n {t(\"bans.add\")}\n </Button>\n </form>\n <ErrorLine error={lift.error} />\n {bans.error && !bans.items.length ? (\n <div className=\"grid justify-items-start gap-2\">\n <ErrorLine>{t(\"bans.loadFailed\")}</ErrorLine>\n <Button size=\"sm\" variant=\"outline\" onClick={bans.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n ) : !bans.loading && !bans.items.length ? (\n <p className=\"text-muted-foreground\">{t(\"bans.empty\")}</p>\n ) : (\n <ul className=\"grid divide-y divide-border rounded-lg border border-border\" aria-busy={bans.loading}>\n {bans.items.map((b) => {\n const name = b.user?.username ?? b.user_id;\n return (\n <li key={b.user_id} className=\"flex flex-wrap items-start gap-3 p-3\" data-ckui=\"comment-ban\">\n <UserAvatar user={b.user} name={name} />\n <div className=\"grid min-w-0 flex-1 gap-0.5\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <UserName user={b.user} name={name} href={userHref} />\n <Badge variant={b.expired ? \"outline\" : \"destructive\"}>{t(b.expired ? \"bans.expired\" : \"bans.active\")}</Badge>\n </div>\n {b.reason && <p className=\"break-words\">{t(\"bans.reason\", { reason: b.reason })}</p>}\n <p className=\"text-muted-foreground\">\n {b.until ? t(\"bans.until\", { date: formatDate(m, b.until) }) : t(\"bans.untilLifted\")} · {t(\"bans.bannedAt\", { date: formatDate(m, b.banned_at, false) })}\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" variant=\"outline\" onClick={() => setEditing({ user: { id: b.user_id, name }, ban: b })}>\n {t(\"bans.update\")}\n </Button>\n <Button size=\"sm\" variant=\"destructive\" onClick={() => setLifting(b)}>\n {t(\"bans.lift\")}\n </Button>\n </div>\n </li>\n );\n })}\n </ul>\n )}\n {bans.hasMore && (\n <Button variant=\"outline\" className=\"justify-self-center\" disabled={bans.loadingMore} onClick={() => void bans.loadMore()}>\n {t(\"moderation.loadMore\")}\n </Button>\n )}\n {editing && (\n <CommentBanDialog\n open\n onOpenChange={(o) => !o && setEditing(null)}\n user={editing.user}\n scopes={[scope]}\n ban={editing.ban}\n client={client}\n onError={onError}\n onSaved={() => setUserId(\"\")}\n />\n )}\n <ConfirmDialog\n open={!!lifting}\n onOpenChange={(o) => !o && setLifting(null)}\n title={t(\"bans.liftTitle\", { name: lifting?.user?.username ?? lifting?.user_id ?? \"\" })}\n description={t(\"bans.liftBody\")}\n confirm={t(\"bans.lift\")}\n onConfirm={() => lifting && void lift.run(() => bans.lift(lifting.user_id))}\n />\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}