@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,74 @@
1
+ import { ContentKitClient } from '../client/client.js';
2
+ import { ContentKitError } from '../client/errors.js';
3
+ import { Poll, PollInput, PollOption, PollUpdate } from '../client/generated/wire.js';
4
+ import { UseList } from './use-resource.js';
5
+ /** The poll after the caller votes for option; a vote is final. */
6
+ export declare function withVote(p: Poll, option: string): Poll;
7
+ export interface UsePoll {
8
+ /** null: no such poll (or no live one). */
9
+ poll: Poll | null;
10
+ loading: boolean;
11
+ error?: ContentKitError;
12
+ reload: () => void;
13
+ /** A vote or answer is in flight. */
14
+ pending: boolean;
15
+ /** Votes at once (a vote is final); rolled back if the server refuses it. */
16
+ vote: (option: string) => Promise<void>;
17
+ /** Stores or replaces the signed-in caller's free-text answer. */
18
+ answer: (text: string) => Promise<void>;
19
+ }
20
+ /**
21
+ * A poll with the caller's vote or answer. Without an id it is the newest
22
+ * live poll (in language): the one a home page shows.
23
+ */
24
+ export declare function usePoll(id?: string | null, o?: {
25
+ initial?: Poll;
26
+ language?: string;
27
+ client?: ContentKitClient;
28
+ }): UsePoll;
29
+ export interface PollFilter {
30
+ language?: string;
31
+ /** YYYY-MM */
32
+ month?: string;
33
+ /** YYYY-MM-DD */
34
+ date?: string;
35
+ /** Staff: every poll, scheduled and inactive ones included (PollWrite). */
36
+ admin?: boolean;
37
+ pageSize?: number;
38
+ }
39
+ /** Polls, newest first, a page at a time. */
40
+ export declare function usePolls(f?: PollFilter, o?: {
41
+ client?: ContentKitClient;
42
+ }): UseList<Poll>;
43
+ /** Images for a new poll, uploaded once it exists: the question's, and each option's by index. */
44
+ export interface PollImages {
45
+ question?: Blob | null;
46
+ options?: readonly (Blob | null | undefined)[];
47
+ }
48
+ export interface UsePollEditor {
49
+ /** null before create (or while loading). */
50
+ poll: Poll | null;
51
+ loading: boolean;
52
+ error?: ContentKitError;
53
+ /** Writes in flight. */
54
+ saving: boolean;
55
+ /** Creates the poll, then uploads its images; the editor then edits it. Image failures are returned, not thrown. */
56
+ create: (input: PollInput, images?: PollImages) => Promise<{
57
+ poll: Poll;
58
+ imageErrors: ContentKitError[];
59
+ }>;
60
+ update: (patch: PollUpdate) => Promise<Poll>;
61
+ remove: () => Promise<void>;
62
+ addOption: (label: string, image?: Blob | null) => Promise<PollOption>;
63
+ renameOption: (option: string, label: string) => Promise<void>;
64
+ /** Removes an option and its votes; a poll keeps at least two. */
65
+ removeOption: (option: string) => Promise<void>;
66
+ /** Moves an option to index to, at once; refetched if a write fails. */
67
+ moveOption: (option: string, to: number) => Promise<void>;
68
+ setImage: (file: Blob | null) => Promise<void>;
69
+ setOptionImage: (option: string, file: Blob | null) => Promise<void>;
70
+ }
71
+ /** Staff: one poll's editor (PollWrite); null creates a new poll. */
72
+ export declare function usePollEditor(id: string | null | undefined, o?: {
73
+ client?: ContentKitClient;
74
+ }): UsePollEditor;
@@ -0,0 +1,176 @@
1
+ import { toContentKitError as e } from "../client/errors.js";
2
+ import { keyOf as t, offsetPages as n, useContentScope as r, useList as i, useResource as a } from "./use-resource.js";
3
+ import { useCallback as o, useState as s } from "react";
4
+ //#region src/react/polls.ts
5
+ function c(e, t) {
6
+ return e.voted ? e : {
7
+ ...e,
8
+ voted: !0,
9
+ my_option: t,
10
+ total_votes: e.total_votes + 1,
11
+ options: e.options.map((e) => e.id === t ? {
12
+ ...e,
13
+ vote_count: e.vote_count + 1
14
+ } : e)
15
+ };
16
+ }
17
+ function l(n, i = {}) {
18
+ let { client: l, store: u, viewer: d } = r(i.client), f = n ? t("poll", d, n) : t("poll", d, "latest", i.language ?? ""), p = a(u, f, {
19
+ type: "poll",
20
+ id: n ?? void 0
21
+ }, async (e) => n ? l.polls.get(n, e) : (await l.polls.list({
22
+ language: i.language,
23
+ limit: 1
24
+ }, e))[0] ?? null, { initial: i.initial }), [m, h] = s(!1), g = p.data ?? null, _ = o(async (t) => {
25
+ let n = u.snapshot(f).data ?? g;
26
+ if (!n) return;
27
+ let r = u.mark(f);
28
+ u.set(f, c(n, t)), h(!0);
29
+ try {
30
+ await l.polls.vote(n.id, t);
31
+ } catch (t) {
32
+ throw u.rollback(f, r, n), e(t);
33
+ } finally {
34
+ h(!1);
35
+ }
36
+ }, [
37
+ l,
38
+ u,
39
+ f,
40
+ g
41
+ ]), v = o(async (e) => {
42
+ if (g) {
43
+ h(!0);
44
+ try {
45
+ await l.polls.answer(g.id, e);
46
+ } finally {
47
+ h(!1);
48
+ }
49
+ }
50
+ }, [l, g]);
51
+ return {
52
+ poll: g,
53
+ loading: p.loading || !p.loaded && i.initial === void 0,
54
+ error: p.error,
55
+ reload: p.reload,
56
+ pending: m,
57
+ vote: _,
58
+ answer: v
59
+ };
60
+ }
61
+ function u(e = {}, a = {}) {
62
+ let { client: o, store: s, viewer: c } = r(a.client), l = e.pageSize ?? 20, u = {
63
+ language: e.language,
64
+ month: e.month,
65
+ date: e.date
66
+ };
67
+ return i(s, t("polls", c, !!e.admin, u.language, u.month, u.date, l), { type: "polls" }, n(l, (t, n) => e.admin ? o.polls.adminList({
68
+ ...u,
69
+ ...t
70
+ }, n) : o.polls.list({
71
+ ...u,
72
+ ...t
73
+ }, n)));
74
+ }
75
+ function d(n, i = {}) {
76
+ let { client: c, store: l, viewer: u } = r(i.client), [d, f] = s(null), p = n ?? d, m = p ? t("poll", u, p) : null, h = a(l, m, {
77
+ type: "poll",
78
+ id: p ?? void 0
79
+ }, (e) => c.polls.get(p, e)), g = h.data ?? null, [_, v] = s(0), y = o(async (e) => {
80
+ v((e) => e + 1);
81
+ try {
82
+ return await e();
83
+ } finally {
84
+ v((e) => e - 1);
85
+ }
86
+ }, []), b = o(() => {
87
+ if (!p) throw Error("contentkit-ui: create the poll first");
88
+ return p;
89
+ }, [p]), x = o((t, n = {}) => y(async () => {
90
+ let r = await c.polls.create(t), i = [], a = [];
91
+ n.question && a.push(c.polls.uploadImage(r.id, n.question));
92
+ let o = [...r.options].sort((e, t) => e.position - t.position);
93
+ (t.options ?? []).forEach((e, t) => {
94
+ let i = n.options?.[t], s = o.find((t) => t.position === e.position) ?? o[t];
95
+ i && s && a.push(c.polls.uploadOptionImage(r.id, s.id, i));
96
+ });
97
+ for (let t of await Promise.allSettled(a)) t.status === "rejected" && i.push(e(t.reason));
98
+ return f(r.id), {
99
+ poll: a.length ? await c.polls.get(r.id) : r,
100
+ imageErrors: i
101
+ };
102
+ }), [c, y]), S = o((e) => y(() => c.polls.update(b(), e)), [
103
+ c,
104
+ y,
105
+ b
106
+ ]), C = o(() => y(() => c.polls.delete(b())), [
107
+ c,
108
+ y,
109
+ b
110
+ ]), w = o((e, t) => y(async () => {
111
+ let n = await c.polls.addOption(b(), { label: e });
112
+ return t && (n.image_url = await c.polls.uploadOptionImage(b(), n.id, t) ?? void 0), n;
113
+ }), [
114
+ c,
115
+ y,
116
+ b
117
+ ]), T = o((e, t) => y(async () => void await c.polls.updateOption(b(), e, { label: t })), [
118
+ c,
119
+ y,
120
+ b
121
+ ]), E = o((e) => y(() => c.polls.deleteOption(b(), e)), [
122
+ c,
123
+ y,
124
+ b
125
+ ]), D = o((e, t) => y(async () => {
126
+ let n = l.snapshot(m).data;
127
+ if (!n) return;
128
+ let r = [...n.options].sort((e, t) => e.position - t.position), i = r.findIndex((t) => t.id === e);
129
+ if (!(i < 0 || t < 0 || t >= r.length || i === t)) {
130
+ r.splice(t, 0, ...r.splice(i, 1)), l.set(m, {
131
+ ...n,
132
+ options: r.map((e, t) => ({
133
+ ...e,
134
+ position: t
135
+ }))
136
+ });
137
+ try {
138
+ await c.polls.reorderOptions(n.id, r);
139
+ } catch (e) {
140
+ throw l.load(m, !0), e;
141
+ }
142
+ }
143
+ }), [
144
+ c,
145
+ l,
146
+ m,
147
+ y
148
+ ]), O = o((e) => y(async () => void await c.polls.uploadImage(b(), e)), [
149
+ c,
150
+ y,
151
+ b
152
+ ]), k = o((e, t) => y(async () => void await c.polls.uploadOptionImage(b(), e, t)), [
153
+ c,
154
+ y,
155
+ b
156
+ ]);
157
+ return {
158
+ poll: g,
159
+ loading: !!m && (h.loading || !h.loaded),
160
+ error: h.error,
161
+ saving: _ > 0,
162
+ create: x,
163
+ update: S,
164
+ remove: C,
165
+ addOption: w,
166
+ renameOption: T,
167
+ removeOption: E,
168
+ moveOption: D,
169
+ setImage: O,
170
+ setOptionImage: k
171
+ };
172
+ }
173
+ //#endregion
174
+ export { l as usePoll, d as usePollEditor, u as usePolls, c as withVote };
175
+
176
+ //# sourceMappingURL=polls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polls.js","names":[],"sources":["../../src/react/polls.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { toContentKitError, type ContentKitError } from \"../client/errors.js\";\nimport type { Poll, PollInput, PollOption, PollUpdate } from \"../client/generated/wire.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\n/** The poll after the caller votes for option; a vote is final. */\nexport function withVote(p: Poll, option: string): Poll {\n if (p.voted) return p;\n return { ...p, voted: true, my_option: option, total_votes: p.total_votes + 1, options: p.options.map((o) => (o.id === option ? { ...o, vote_count: o.vote_count + 1 } : o)) };\n}\n\nexport interface UsePoll {\n /** null: no such poll (or no live one). */\n poll: Poll | null;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n /** A vote or answer is in flight. */\n pending: boolean;\n /** Votes at once (a vote is final); rolled back if the server refuses it. */\n vote: (option: string) => Promise<void>;\n /** Stores or replaces the signed-in caller's free-text answer. */\n answer: (text: string) => Promise<void>;\n}\n\n/**\n * A poll with the caller's vote or answer. Without an id it is the newest\n * live poll (in language): the one a home page shows.\n */\nexport function usePoll(id?: string | null, o: { initial?: Poll; language?: string; client?: ContentKitClient } = {}): UsePoll {\n const { client, store, viewer } = useContentScope(o.client);\n const key = id ? keyOf(\"poll\", viewer, id) : keyOf(\"poll\", viewer, \"latest\", o.language ?? \"\");\n const r = useResource<Poll | null>(\n store,\n key,\n { type: \"poll\", id: id ?? undefined },\n async (s) => (id ? client.polls.get(id, s) : ((await client.polls.list({ language: o.language, limit: 1 }, s))[0] ?? null)),\n { initial: o.initial },\n );\n const [pending, setPending] = useState(false);\n const poll = r.data ?? null;\n const vote = useCallback(\n async (option: string) => {\n const prev = store.snapshot<Poll | null>(key).data ?? poll;\n if (!prev) return;\n const token = store.mark(key);\n store.set(key, withVote(prev, option));\n setPending(true);\n try {\n await client.polls.vote(prev.id, option);\n } catch (e) {\n store.rollback(key, token, prev);\n throw toContentKitError(e);\n } finally {\n setPending(false);\n }\n },\n [client, store, key, poll],\n );\n const answer = useCallback(\n async (text: string) => {\n if (!poll) return;\n setPending(true);\n try {\n await client.polls.answer(poll.id, text);\n } finally {\n setPending(false);\n }\n },\n [client, poll],\n );\n return { poll, loading: r.loading || (!r.loaded && o.initial === undefined), error: r.error, reload: r.reload, pending, vote, answer };\n}\n\nexport interface PollFilter {\n language?: string;\n /** YYYY-MM */\n month?: string;\n /** YYYY-MM-DD */\n date?: string;\n /** Staff: every poll, scheduled and inactive ones included (PollWrite). */\n admin?: boolean;\n pageSize?: number;\n}\n\n/** Polls, newest first, a page at a time. */\nexport function usePolls(f: PollFilter = {}, o: { client?: ContentKitClient } = {}): UseList<Poll> {\n const { client, store, viewer } = useContentScope(o.client);\n const size = f.pageSize ?? 20;\n const q = { language: f.language, month: f.month, date: f.date };\n return useList(\n store,\n keyOf(\"polls\", viewer, !!f.admin, q.language, q.month, q.date, size),\n { type: \"polls\" },\n offsetPages(size, (page, signal) => (f.admin ? client.polls.adminList({ ...q, ...page }, signal) : client.polls.list({ ...q, ...page }, signal))),\n );\n}\n\n/** Images for a new poll, uploaded once it exists: the question's, and each option's by index. */\nexport interface PollImages {\n question?: Blob | null;\n options?: readonly (Blob | null | undefined)[];\n}\n\nexport interface UsePollEditor {\n /** null before create (or while loading). */\n poll: Poll | null;\n loading: boolean;\n error?: ContentKitError;\n /** Writes in flight. */\n saving: boolean;\n /** Creates the poll, then uploads its images; the editor then edits it. Image failures are returned, not thrown. */\n create: (input: PollInput, images?: PollImages) => Promise<{ poll: Poll; imageErrors: ContentKitError[] }>;\n update: (patch: PollUpdate) => Promise<Poll>;\n remove: () => Promise<void>;\n addOption: (label: string, image?: Blob | null) => Promise<PollOption>;\n renameOption: (option: string, label: string) => Promise<void>;\n /** Removes an option and its votes; a poll keeps at least two. */\n removeOption: (option: string) => Promise<void>;\n /** Moves an option to index to, at once; refetched if a write fails. */\n moveOption: (option: string, to: number) => Promise<void>;\n setImage: (file: Blob | null) => Promise<void>;\n setOptionImage: (option: string, file: Blob | null) => Promise<void>;\n}\n\n/** Staff: one poll's editor (PollWrite); null creates a new poll. */\nexport function usePollEditor(id: string | null | undefined, o: { client?: ContentKitClient } = {}): UsePollEditor {\n const { client, store, viewer } = useContentScope(o.client);\n const [created, setCreated] = useState<string | null>(null);\n const pid = id ?? created;\n const key = pid ? keyOf(\"poll\", viewer, pid) : null;\n const r = useResource<Poll | null>(store, key, { type: \"poll\", id: pid ?? undefined }, (s) => client.polls.get(pid!, s));\n const poll = r.data ?? null;\n const [inflight, setInflight] = useState(0);\n const run = useCallback(async <T>(fn: () => Promise<T>): Promise<T> => {\n setInflight((n) => n + 1);\n try {\n return await fn();\n } finally {\n setInflight((n) => n - 1);\n }\n }, []);\n const need = useCallback(() => {\n if (!pid) throw new Error(\"contentkit-ui: create the poll first\");\n return pid;\n }, [pid]);\n\n const create = useCallback<UsePollEditor[\"create\"]>(\n (input, images = {}) =>\n run(async () => {\n const made = await client.polls.create(input);\n const imageErrors: ContentKitError[] = [];\n const tries: Promise<unknown>[] = [];\n if (images.question) tries.push(client.polls.uploadImage(made.id, images.question));\n const byPosition = [...made.options].sort((a, b) => a.position - b.position);\n (input.options ?? []).forEach((opt, i) => {\n const file = images.options?.[i];\n const target = byPosition.find((x) => x.position === opt.position) ?? byPosition[i];\n if (file && target) tries.push(client.polls.uploadOptionImage(made.id, target.id, file));\n });\n for (const res of await Promise.allSettled(tries)) if (res.status === \"rejected\") imageErrors.push(toContentKitError(res.reason));\n setCreated(made.id);\n return { poll: tries.length ? await client.polls.get(made.id) : made, imageErrors };\n }),\n [client, run],\n );\n const update = useCallback((patch: PollUpdate) => run(() => client.polls.update(need(), patch)), [client, run, need]);\n const remove = useCallback(() => run(() => client.polls.delete(need())), [client, run, need]);\n const addOption = useCallback(\n (label: string, image?: Blob | null) =>\n run(async () => {\n const opt = await client.polls.addOption(need(), { label });\n if (image) opt.image_url = (await client.polls.uploadOptionImage(need(), opt.id, image)) ?? undefined;\n return opt;\n }),\n [client, run, need],\n );\n const renameOption = useCallback((option: string, label: string) => run(async () => void (await client.polls.updateOption(need(), option, { label }))), [client, run, need]);\n const removeOption = useCallback((option: string) => run(() => client.polls.deleteOption(need(), option)), [client, run, need]);\n const moveOption = useCallback(\n (option: string, to: number) =>\n run(async () => {\n const current = store.snapshot<Poll | null>(key!).data;\n if (!current) return;\n const order = [...current.options].sort((a, b) => a.position - b.position);\n const from = order.findIndex((x) => x.id === option);\n if (from < 0 || to < 0 || to >= order.length || from === to) return;\n order.splice(to, 0, ...order.splice(from, 1));\n store.set<Poll | null>(key!, { ...current, options: order.map((x, position) => ({ ...x, position })) });\n try {\n await client.polls.reorderOptions(current.id, order);\n } catch (e) {\n store.load(key!, true);\n throw e;\n }\n }),\n [client, store, key, run],\n );\n const setImage = useCallback((file: Blob | null) => run(async () => void (await client.polls.uploadImage(need(), file))), [client, run, need]);\n const setOptionImage = useCallback(\n (option: string, file: Blob | null) => run(async () => void (await client.polls.uploadOptionImage(need(), option, file))),\n [client, run, need],\n );\n return {\n poll,\n loading: !!key && (r.loading || !r.loaded),\n error: r.error,\n saving: inflight > 0,\n create,\n update,\n remove,\n addOption,\n renameOption,\n removeOption,\n moveOption,\n setImage,\n setOptionImage,\n };\n}\n"],"mappings":";;;;AAOA,SAAgB,EAAS,GAAS,GAAsB;CAEtD,OADI,EAAE,QAAc,IACb;EAAE,GAAG;EAAG,OAAO;EAAM,WAAW;EAAQ,aAAa,EAAE,cAAc;EAAG,SAAS,EAAE,QAAQ,KAAK,MAAO,EAAE,OAAO,IAAS;GAAE,GAAG;GAAG,YAAY,EAAE,aAAa;EAAE,IAAI,CAAE;CAAE;AAC/K;AAoBA,SAAgB,EAAQ,GAAoB,IAAsE,CAAC,GAAY;CAC7H,IAAM,EAAE,WAAQ,UAAO,cAAW,EAAgB,EAAE,MAAM,GACpD,IAAM,IAAK,EAAM,QAAQ,GAAQ,CAAE,IAAI,EAAM,QAAQ,GAAQ,UAAU,EAAE,YAAY,EAAE,GACvF,IAAI,EACR,GACA,GACA;EAAE,MAAM;EAAQ,IAAI,KAAM,KAAA;CAAU,GACpC,OAAO,MAAO,IAAK,EAAO,MAAM,IAAI,GAAI,CAAC,KAAM,MAAM,EAAO,MAAM,KAAK;EAAE,UAAU,EAAE;EAAU,OAAO;CAAE,GAAG,CAAC,EAAA,CAAG,MAAM,MACrH,EAAE,SAAS,EAAE,QAAQ,CACvB,GACM,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,IAAO,EAAE,QAAQ,MACjB,IAAO,EACX,OAAO,MAAmB;EACxB,IAAM,IAAO,EAAM,SAAsB,CAAG,CAAC,CAAC,QAAQ;EACtD,IAAI,CAAC,GAAM;EACX,IAAM,IAAQ,EAAM,KAAK,CAAG;EAE5B,AADA,EAAM,IAAI,GAAK,EAAS,GAAM,CAAM,CAAC,GACrC,EAAW,EAAI;EACf,IAAI;GACF,MAAM,EAAO,MAAM,KAAK,EAAK,IAAI,CAAM;EACzC,SAAS,GAAG;GAEV,MADA,EAAM,SAAS,GAAK,GAAO,CAAI,GACzB,EAAkB,CAAC;EAC3B,UAAU;GACR,EAAW,EAAK;EAClB;CACF,GACA;EAAC;EAAQ;EAAO;EAAK;CAAI,CAC3B,GACM,IAAS,EACb,OAAO,MAAiB;EACjB,OACL;KAAW,EAAI;GACf,IAAI;IACF,MAAM,EAAO,MAAM,OAAO,EAAK,IAAI,CAAI;GACzC,UAAU;IACR,EAAW,EAAK;GAClB;EALe;CAMjB,GACA,CAAC,GAAQ,CAAI,CACf;CACA,OAAO;EAAE;EAAM,SAAS,EAAE,WAAY,CAAC,EAAE,UAAU,EAAE,YAAY,KAAA;EAAY,OAAO,EAAE;EAAO,QAAQ,EAAE;EAAQ;EAAS;EAAM;CAAO;AACvI;AAcA,SAAgB,EAAS,IAAgB,CAAC,GAAG,IAAmC,CAAC,GAAkB;CACjG,IAAM,EAAE,WAAQ,UAAO,cAAW,EAAgB,EAAE,MAAM,GACpD,IAAO,EAAE,YAAY,IACrB,IAAI;EAAE,UAAU,EAAE;EAAU,OAAO,EAAE;EAAO,MAAM,EAAE;CAAK;CAC/D,OAAO,EACL,GACA,EAAM,SAAS,GAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAI,GACnE,EAAE,MAAM,QAAQ,GAChB,EAAY,IAAO,GAAM,MAAY,EAAE,QAAQ,EAAO,MAAM,UAAU;EAAE,GAAG;EAAG,GAAG;CAAK,GAAG,CAAM,IAAI,EAAO,MAAM,KAAK;EAAE,GAAG;EAAG,GAAG;CAAK,GAAG,CAAM,CAAE,CAClJ;AACF;AA8BA,SAAgB,EAAc,GAA+B,IAAmC,CAAC,GAAkB;CACjH,IAAM,EAAE,WAAQ,UAAO,cAAW,EAAgB,EAAE,MAAM,GACpD,CAAC,GAAS,KAAc,EAAwB,IAAI,GACpD,IAAM,KAAM,GACZ,IAAM,IAAM,EAAM,QAAQ,GAAQ,CAAG,IAAI,MACzC,IAAI,EAAyB,GAAO,GAAK;EAAE,MAAM;EAAQ,IAAI,KAAO,KAAA;CAAU,IAAI,MAAM,EAAO,MAAM,IAAI,GAAM,CAAC,CAAC,GACjH,IAAO,EAAE,QAAQ,MACjB,CAAC,GAAU,KAAe,EAAS,CAAC,GACpC,IAAM,EAAY,OAAU,MAAqC;EACrE,GAAa,MAAM,IAAI,CAAC;EACxB,IAAI;GACF,OAAO,MAAM,EAAG;EAClB,UAAU;GACR,GAAa,MAAM,IAAI,CAAC;EAC1B;CACF,GAAG,CAAC,CAAC,GACC,IAAO,QAAkB;EAC7B,IAAI,CAAC,GAAK,MAAU,MAAM,sCAAsC;EAChE,OAAO;CACT,GAAG,CAAC,CAAG,CAAC,GAEF,IAAS,GACZ,GAAO,IAAS,CAAC,MAChB,EAAI,YAAY;EACd,IAAM,IAAO,MAAM,EAAO,MAAM,OAAO,CAAK,GACtC,IAAiC,CAAC,GAClC,IAA4B,CAAC;EACnC,AAAI,EAAO,YAAU,EAAM,KAAK,EAAO,MAAM,YAAY,EAAK,IAAI,EAAO,QAAQ,CAAC;EAClF,IAAM,IAAa,CAAC,GAAG,EAAK,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;EAC3E,CAAC,EAAM,WAAW,CAAC,EAAA,CAAG,SAAS,GAAK,MAAM;GACxC,IAAM,IAAO,EAAO,UAAU,IACxB,IAAS,EAAW,MAAM,MAAM,EAAE,aAAa,EAAI,QAAQ,KAAK,EAAW;GACjF,AAAI,KAAQ,KAAQ,EAAM,KAAK,EAAO,MAAM,kBAAkB,EAAK,IAAI,EAAO,IAAI,CAAI,CAAC;EACzF,CAAC;EACD,KAAK,IAAM,KAAO,MAAM,QAAQ,WAAW,CAAK,GAAG,AAAI,EAAI,WAAW,cAAY,EAAY,KAAK,EAAkB,EAAI,MAAM,CAAC;EAEhI,OADA,EAAW,EAAK,EAAE,GACX;GAAE,MAAM,EAAM,SAAS,MAAM,EAAO,MAAM,IAAI,EAAK,EAAE,IAAI;GAAM;EAAY;CACpF,CAAC,GACH,CAAC,GAAQ,CAAG,CACd,GACM,IAAS,GAAa,MAAsB,QAAU,EAAO,MAAM,OAAO,EAAK,GAAG,CAAK,CAAC,GAAG;EAAC;EAAQ;EAAK;CAAI,CAAC,GAC9G,IAAS,QAAkB,QAAU,EAAO,MAAM,OAAO,EAAK,CAAC,CAAC,GAAG;EAAC;EAAQ;EAAK;CAAI,CAAC,GACtF,IAAY,GACf,GAAe,MACd,EAAI,YAAY;EACd,IAAM,IAAM,MAAM,EAAO,MAAM,UAAU,EAAK,GAAG,EAAE,SAAM,CAAC;EAE1D,OADI,MAAO,EAAI,YAAa,MAAM,EAAO,MAAM,kBAAkB,EAAK,GAAG,EAAI,IAAI,CAAK,KAAM,KAAA,IACrF;CACT,CAAC,GACH;EAAC;EAAQ;EAAK;CAAI,CACpB,GACM,IAAe,GAAa,GAAgB,MAAkB,EAAI,YAAY,KAAM,MAAM,EAAO,MAAM,aAAa,EAAK,GAAG,GAAQ,EAAE,SAAM,CAAC,CAAE,GAAG;EAAC;EAAQ;EAAK;CAAI,CAAC,GACrK,IAAe,GAAa,MAAmB,QAAU,EAAO,MAAM,aAAa,EAAK,GAAG,CAAM,CAAC,GAAG;EAAC;EAAQ;EAAK;CAAI,CAAC,GACxH,IAAa,GAChB,GAAgB,MACf,EAAI,YAAY;EACd,IAAM,IAAU,EAAM,SAAsB,CAAI,CAAC,CAAC;EAClD,IAAI,CAAC,GAAS;EACd,IAAM,IAAQ,CAAC,GAAG,EAAQ,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ,GACnE,IAAO,EAAM,WAAW,MAAM,EAAE,OAAO,CAAM;EAC/C,UAAO,KAAK,IAAK,KAAK,KAAM,EAAM,UAAU,MAAS,IAEzD;GADA,EAAM,OAAO,GAAI,GAAG,GAAG,EAAM,OAAO,GAAM,CAAC,CAAC,GAC5C,EAAM,IAAiB,GAAM;IAAE,GAAG;IAAS,SAAS,EAAM,KAAK,GAAG,OAAc;KAAE,GAAG;KAAG;IAAS,EAAE;GAAE,CAAC;GACtG,IAAI;IACF,MAAM,EAAO,MAAM,eAAe,EAAQ,IAAI,CAAK;GACrD,SAAS,GAAG;IAEV,MADA,EAAM,KAAK,GAAM,EAAI,GACf;GACR;EANsG;CAOxG,CAAC,GACH;EAAC;EAAQ;EAAO;EAAK;CAAG,CAC1B,GACM,IAAW,GAAa,MAAsB,EAAI,YAAY,KAAM,MAAM,EAAO,MAAM,YAAY,EAAK,GAAG,CAAI,CAAE,GAAG;EAAC;EAAQ;EAAK;CAAI,CAAC,GACvI,IAAiB,GACpB,GAAgB,MAAsB,EAAI,YAAY,KAAM,MAAM,EAAO,MAAM,kBAAkB,EAAK,GAAG,GAAQ,CAAI,CAAE,GACxH;EAAC;EAAQ;EAAK;CAAI,CACpB;CACA,OAAO;EACL;EACA,SAAS,CAAC,CAAC,MAAQ,EAAE,WAAW,CAAC,EAAE;EACnC,OAAO,EAAE;EACT,QAAQ,IAAW;EACnB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
@@ -0,0 +1,59 @@
1
+ import { ContentKitClient } from '../client/client.js';
2
+ import { ContentKitError } from '../client/errors.js';
3
+ import { Sort } from '../client/content/types.js';
4
+ import { Post, PostInput } from '../client/generated/wire.js';
5
+ import { UseList } from './use-resource.js';
6
+ export interface PostFilter {
7
+ language?: string;
8
+ sort?: Sort;
9
+ /** Staff: every post, drafts and scheduled ones included (PostWrite). */
10
+ admin?: boolean;
11
+ /** With admin: true only drafts, false no drafts. */
12
+ draft?: boolean;
13
+ /** With admin: the deleted posts instead of the live ones. */
14
+ deleted?: boolean;
15
+ /** With admin: only posts whose title, excerpt or body contains this text. */
16
+ q?: string;
17
+ pageSize?: number;
18
+ }
19
+ /** Posts, a page at a time: the published ones, or every one for staff. */
20
+ export declare function usePosts(f?: PostFilter, o?: {
21
+ client?: ContentKitClient;
22
+ }): UseList<Post>;
23
+ export interface UsePost {
24
+ /** null before create (or while loading). */
25
+ post: Post | null;
26
+ loading: boolean;
27
+ error?: ContentKitError;
28
+ /** Writes in flight. */
29
+ saving: boolean;
30
+ /** Creates the post; the hook then edits it. */
31
+ create: (input: PostInput) => Promise<Post>;
32
+ /** Updates the given fields; a body from the editor is stored with its images' public URLs (storedBody). */
33
+ update: (patch: PostInput) => Promise<Post>;
34
+ remove: () => Promise<void>;
35
+ /** Restores the deleted post (PostWrite). */
36
+ restore: () => Promise<Post>;
37
+ /** Uploads the cover (null clears it); resolves with a URL that shows it now. */
38
+ setCover: (file: Blob | null) => Promise<string | null>;
39
+ /** Uploads an image for the body; resolves with a URL that shows it now, to place in the editor. */
40
+ uploadImage: (file: Blob) => Promise<string>;
41
+ /**
42
+ * A URL that shows one of the post's images now (the cover, a body image):
43
+ * an unpublished post's images are not public yet, so its editors see them
44
+ * through signed URLs of an editor read; a fresh upload shows from the
45
+ * file itself. Any other URL is returned as is.
46
+ */
47
+ imageSrc: (url: string | null | undefined) => string | undefined;
48
+ /** post.body with its images through imageSrc: what a rich-text editor shows. */
49
+ editorBody: string | undefined;
50
+ /** HTML from the editor with the URLs imageSrc gave back to the images' public URLs. */
51
+ storedBody: (html: string) => string;
52
+ }
53
+ /** Whether readers see the post, and so its images' public files exist. */
54
+ export declare function isPublished(post: Post, now?: number): boolean;
55
+ /** One post: read, and for staff (PostWrite) create, update, delete, restore, cover and body images. null creates a new post. */
56
+ export declare function usePost(id: string | null | undefined, o?: {
57
+ initial?: Post;
58
+ client?: ContentKitClient;
59
+ }): UsePost;
@@ -0,0 +1,149 @@
1
+ import { useMediaRead as e } from "./read.js";
2
+ import { keyOf as t, offsetPages as n, useContentScope as r, useList as i, useResource as a } from "./use-resource.js";
3
+ import { useCallback as o, useEffect as s, useMemo as c, useRef as l, useState as u } from "react";
4
+ //#region src/react/posts.ts
5
+ function d(e = {}, a = {}) {
6
+ let { client: o, store: s, viewer: c } = r(a.client), l = e.pageSize ?? 20;
7
+ return i(s, t("posts", c, !!e.admin, e.language, e.sort, e.draft, e.deleted, e.q, l), {
8
+ type: "posts",
9
+ scope: e.admin && e.deleted ? "deleted" : void 0
10
+ }, n(l, (t, n) => e.admin ? o.posts.adminList({
11
+ ...t,
12
+ language: e.language,
13
+ draft: e.draft,
14
+ deleted: e.deleted,
15
+ q: e.q
16
+ }, n) : o.posts.list({
17
+ ...t,
18
+ language: e.language,
19
+ sort: e.sort
20
+ }, n)));
21
+ }
22
+ var f = "i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", p = (e) => e.match(new RegExp(f))?.[0], m = RegExp(`[^\\s"'()<>]*${f}[^\\s"'()<>]*`, "g");
23
+ function h(e, t = Date.now()) {
24
+ return !e.is_draft && !e.moderation && (!e.live_at || Date.parse(e.live_at) <= t);
25
+ }
26
+ function g(n, i = {}) {
27
+ let { client: d, store: f, viewer: g } = r(i.client), [_, v] = u(null), y = n ?? _, b = y ? t("post", g, y) : null, x = a(f, b, {
28
+ type: "post",
29
+ id: y ?? void 0
30
+ }, (e) => d.posts.get(y, e), { initial: i.initial }), S = x.data ?? null, [C, w] = u(0), T = o(async (e) => {
31
+ w((e) => e + 1);
32
+ try {
33
+ return await e();
34
+ } finally {
35
+ w((e) => e - 1);
36
+ }
37
+ }, []), E = o(() => {
38
+ if (!y) throw Error("contentkit-ui: create the post first");
39
+ return y;
40
+ }, [y]), [D, O] = u(/* @__PURE__ */ new Map()), k = l(/* @__PURE__ */ new Map());
41
+ s(() => {
42
+ let e = k.current;
43
+ return () => {
44
+ for (let t of e.keys()) t.startsWith("blob:") && URL.revokeObjectURL(t);
45
+ };
46
+ }, []);
47
+ let A = c(() => {
48
+ let e = /* @__PURE__ */ new Map();
49
+ for (let t of [...S?.body.match(m) ?? [], S?.cover_url ?? ""]) {
50
+ let n = p(t);
51
+ n && !e.has(n) && e.set(n, t);
52
+ }
53
+ return e;
54
+ }, [S?.body, S?.cover_url]), j = !!S && !h(S) && [...A.keys()].some((e) => !D.has(e)), M = c(() => d.media.postRef(y ?? "none"), [d, y]), N = e(M, {
55
+ editor: !0,
56
+ read: j ? void 0 : null,
57
+ client: d
58
+ }), P = c(() => {
59
+ let e = /* @__PURE__ */ new Map();
60
+ for (let t of N.read?.files ?? []) {
61
+ let n = t.upload && t.editor_url ? p(t.path) : void 0;
62
+ n && e.set(n, t.editor_url);
63
+ }
64
+ return e;
65
+ }, [N.read]), F = j && !!N.read && [...A.keys()].some((e) => !D.has(e) && !P.has(e) && !N.read.files.some((t) => t.failed && p(t.path) === e)), I = l(0), { reload: L } = N;
66
+ s(() => {
67
+ if (!F) {
68
+ I.current = 0;
69
+ return;
70
+ }
71
+ if (I.current >= 6) return;
72
+ let e = setTimeout(() => {
73
+ I.current++, L();
74
+ }, 1e3 * 2 ** I.current);
75
+ return () => clearTimeout(e);
76
+ }, [
77
+ F,
78
+ N.read,
79
+ L
80
+ ]);
81
+ let R = o((e) => {
82
+ if (!e) return;
83
+ let t = p(e), n = t ? D.get(t) ?? P.get(t) : void 0;
84
+ return n ? (k.current.set(n, e), n) : e;
85
+ }, [D, P]), z = c(() => S?.body.replace(m, (e) => R(e) ?? e), [S?.body, R]), B = o((e) => {
86
+ let t = e;
87
+ for (let [e, n] of k.current) t = t.split(e).join(n).split(e.replaceAll("&", "&amp;")).join(n);
88
+ return t;
89
+ }, []), V = o((e, t, n) => {
90
+ if (typeof URL.createObjectURL != "function") return t;
91
+ let r = URL.createObjectURL(n);
92
+ return k.current.set(r, t), O((t) => new Map(t).set(e, r)), r;
93
+ }, []), H = o((e) => T(async () => {
94
+ let t = await d.posts.create(e);
95
+ return v(t.id), t;
96
+ }), [d, T]), U = o((e) => T(() => d.posts.update(E(), e.body === void 0 ? e : {
97
+ ...e,
98
+ body: B(e.body)
99
+ })), [
100
+ d,
101
+ T,
102
+ E,
103
+ B
104
+ ]), W = o(() => T(() => d.posts.delete(E())), [
105
+ d,
106
+ T,
107
+ E
108
+ ]), G = o(() => T(() => d.posts.restore(E())), [
109
+ d,
110
+ T,
111
+ E
112
+ ]), K = o((e) => T(async () => {
113
+ if (!e) return d.posts.setCover(E(), null);
114
+ let t = await d.media.uploadNamed(d.media.postRef(E()), e), n = await d.posts.setCover(E(), t.name);
115
+ return n && V(t.name, n, e);
116
+ }), [
117
+ d,
118
+ T,
119
+ E,
120
+ V
121
+ ]), q = o((e) => T(async () => {
122
+ let t = await d.media.uploadInline(E(), e);
123
+ return V(t.name, t.url, e);
124
+ }), [
125
+ d,
126
+ T,
127
+ E,
128
+ V
129
+ ]);
130
+ return {
131
+ post: S,
132
+ loading: !!b && (x.loading || !x.loaded && i.initial === void 0),
133
+ error: x.error,
134
+ saving: C > 0,
135
+ create: H,
136
+ update: U,
137
+ remove: W,
138
+ restore: G,
139
+ setCover: K,
140
+ uploadImage: q,
141
+ imageSrc: R,
142
+ editorBody: z,
143
+ storedBody: B
144
+ };
145
+ }
146
+ //#endregion
147
+ export { h as isPublished, g as usePost, d as usePosts };
148
+
149
+ //# sourceMappingURL=posts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"posts.js","names":[],"sources":["../../src/react/posts.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { Sort } from \"../client/content/types.js\";\nimport type { Post, PostInput } from \"../client/generated/wire.js\";\nimport { useMediaRead } from \"./read.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\nexport interface PostFilter {\n language?: string;\n sort?: Sort;\n /** Staff: every post, drafts and scheduled ones included (PostWrite). */\n admin?: boolean;\n /** With admin: true only drafts, false no drafts. */\n draft?: boolean;\n /** With admin: the deleted posts instead of the live ones. */\n deleted?: boolean;\n /** With admin: only posts whose title, excerpt or body contains this text. */\n q?: string;\n pageSize?: number;\n}\n\n/** Posts, a page at a time: the published ones, or every one for staff. */\nexport function usePosts(f: PostFilter = {}, o: { client?: ContentKitClient } = {}): UseList<Post> {\n const { client, store, viewer } = useContentScope(o.client);\n const size = f.pageSize ?? 20;\n return useList(\n store,\n keyOf(\"posts\", viewer, !!f.admin, f.language, f.sort, f.draft, f.deleted, f.q, size),\n { type: \"posts\", scope: f.admin && f.deleted ? \"deleted\" : undefined },\n offsetPages(size, (q, signal) =>\n f.admin\n ? client.posts.adminList({ ...q, language: f.language, draft: f.draft, deleted: f.deleted, q: f.q }, signal)\n : client.posts.list({ ...q, language: f.language, sort: f.sort }, signal),\n ),\n );\n}\n\nexport interface UsePost {\n /** null before create (or while loading). */\n post: Post | null;\n loading: boolean;\n error?: ContentKitError;\n /** Writes in flight. */\n saving: boolean;\n /** Creates the post; the hook then edits it. */\n create: (input: PostInput) => Promise<Post>;\n /** Updates the given fields; a body from the editor is stored with its images' public URLs (storedBody). */\n update: (patch: PostInput) => Promise<Post>;\n remove: () => Promise<void>;\n /** Restores the deleted post (PostWrite). */\n restore: () => Promise<Post>;\n /** Uploads the cover (null clears it); resolves with a URL that shows it now. */\n setCover: (file: Blob | null) => Promise<string | null>;\n /** Uploads an image for the body; resolves with a URL that shows it now, to place in the editor. */\n uploadImage: (file: Blob) => Promise<string>;\n /**\n * A URL that shows one of the post's images now (the cover, a body image):\n * an unpublished post's images are not public yet, so its editors see them\n * through signed URLs of an editor read; a fresh upload shows from the\n * file itself. Any other URL is returned as is.\n */\n imageSrc: (url: string | null | undefined) => string | undefined;\n /** post.body with its images through imageSrc: what a rich-text editor shows. */\n editorBody: string | undefined;\n /** HTML from the editor with the URLs imageSrc gave back to the images' public URLs. */\n storedBody: (html: string) => string;\n}\n\nconst NAME = \"i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\";\nconst nameIn = (url: string) => url.match(new RegExp(NAME))?.[0];\n// An image URL in HTML: everything around a name up to a quote, a space or a bracket.\nconst imageURLs = new RegExp(`[^\\\\s\"'()<>]*${NAME}[^\\\\s\"'()<>]*`, \"g\");\n\n/** Whether readers see the post, and so its images' public files exist. */\nexport function isPublished(post: Post, now = Date.now()): boolean {\n return !post.is_draft && !post.moderation && (!post.live_at || Date.parse(post.live_at) <= now);\n}\n\n/** One post: read, and for staff (PostWrite) create, update, delete, restore, cover and body images. null creates a new post. */\nexport function usePost(id: string | null | undefined, o: { initial?: Post; client?: ContentKitClient } = {}): UsePost {\n const { client, store, viewer } = useContentScope(o.client);\n const [created, setCreated] = useState<string | null>(null);\n const pid = id ?? created;\n const key = pid ? keyOf(\"post\", viewer, pid) : null;\n const r = useResource<Post | null>(store, key, { type: \"post\", id: pid ?? undefined }, (s) => client.posts.get(pid!, s), { initial: o.initial });\n const post = r.data ?? null;\n const [inflight, setInflight] = useState(0);\n const run = useCallback(async <T>(fn: () => Promise<T>): Promise<T> => {\n setInflight((n) => n + 1);\n try {\n return await fn();\n } finally {\n setInflight((n) => n - 1);\n }\n }, []);\n const need = useCallback(() => {\n if (!pid) throw new Error(\"contentkit-ui: create the post first\");\n return pid;\n }, [pid]);\n\n // Images: fresh uploads show from their file; an unpublished post's from an editor read.\n const [previews, setPreviews] = useState<ReadonlyMap<string, string>>(new Map());\n const given = useRef(new Map<string, string>()); // each URL imageSrc gave -> the image's public URL\n useEffect(() => {\n const urls = given.current;\n return () => {\n for (const u of urls.keys()) if (u.startsWith(\"blob:\")) URL.revokeObjectURL(u);\n };\n }, []);\n const names = useMemo(() => {\n const out = new Map<string, string>();\n for (const u of [...(post?.body.match(imageURLs) ?? []), post?.cover_url ?? \"\"]) {\n const n = nameIn(u);\n if (n && !out.has(n)) out.set(n, u);\n }\n return out;\n }, [post?.body, post?.cover_url]);\n const unread = !!post && !isPublished(post) && [...names.keys()].some((n) => !previews.has(n));\n const folder = useMemo(() => client.media.postRef(pid ?? \"none\"), [client, pid]);\n const read = useMediaRead(folder, { editor: true, read: unread ? undefined : null, client });\n const views = useMemo(() => {\n const out = new Map<string, string>();\n for (const f of read.read?.files ?? []) {\n const n = f.upload && f.editor_url ? nameIn(f.path) : undefined;\n if (n) out.set(n, f.editor_url!);\n }\n return out;\n }, [read.read]);\n // Editor views render on the first editor read that misses them: read again, backing off.\n const missing = unread && !!read.read && [...names.keys()].some((n) => !previews.has(n) && !views.has(n) && !read.read!.files.some((f) => f.failed && nameIn(f.path) === n));\n const tries = useRef(0);\n const { reload } = read;\n useEffect(() => {\n if (!missing) {\n tries.current = 0;\n return;\n }\n if (tries.current >= 6) return;\n const t = setTimeout(() => {\n tries.current++;\n reload();\n }, 1000 * 2 ** tries.current);\n return () => clearTimeout(t);\n }, [missing, read.read, reload]);\n\n const imageSrc = useCallback(\n (url: string | null | undefined) => {\n if (!url) return undefined;\n const n = nameIn(url);\n const shown = n ? (previews.get(n) ?? views.get(n)) : undefined;\n if (!shown) return url;\n given.current.set(shown, url);\n return shown;\n },\n [previews, views],\n );\n const editorBody = useMemo(() => post?.body.replace(imageURLs, (u) => imageSrc(u) ?? u), [post?.body, imageSrc]);\n const storedBody = useCallback((html: string) => {\n let out = html;\n for (const [shown, url] of given.current) out = out.split(shown).join(url).split(shown.replaceAll(\"&\", \"&amp;\")).join(url);\n return out;\n }, []);\n const preview = useCallback((name: string, url: string, file: Blob) => {\n if (typeof URL.createObjectURL !== \"function\") return url;\n const shown = URL.createObjectURL(file);\n given.current.set(shown, url);\n setPreviews((m) => new Map(m).set(name, shown));\n return shown;\n }, []);\n\n const create = useCallback(\n (input: PostInput) =>\n run(async () => {\n const post = await client.posts.create(input);\n setCreated(post.id);\n return post;\n }),\n [client, run],\n );\n const update = useCallback(\n (patch: PostInput) => run(() => client.posts.update(need(), patch.body === undefined ? patch : { ...patch, body: storedBody(patch.body) })),\n [client, run, need, storedBody],\n );\n const remove = useCallback(() => run(() => client.posts.delete(need())), [client, run, need]);\n const restore = useCallback(() => run(() => client.posts.restore(need())), [client, run, need]);\n const setCover = useCallback(\n (file: Blob | null) =>\n run(async () => {\n if (!file) return client.posts.setCover(need(), null);\n const up = await client.media.uploadNamed(client.media.postRef(need()), file);\n const url = await client.posts.setCover(need(), up.name);\n return url && preview(up.name, url, file);\n }),\n [client, run, need, preview],\n );\n const uploadImage = useCallback(\n (file: Blob) =>\n run(async () => {\n const up = await client.media.uploadInline(need(), file);\n return preview(up.name, up.url, file);\n }),\n [client, run, need, preview],\n );\n return {\n post,\n loading: !!key && (r.loading || (!r.loaded && o.initial === undefined)),\n error: r.error,\n saving: inflight > 0,\n create,\n update,\n remove,\n restore,\n setCover,\n uploadImage,\n imageSrc,\n editorBody,\n storedBody,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { ReactNode } from 'react';
2
+ import { ContentKitClient } from '../client/client.js';
3
+ import { ContentKitChange } from '../client/http.js';
4
+ import { ContentURLs } from '../urls/index.js';
5
+ import { ContentKitErrorHandler, Navigate } from './context.js';
6
+ export interface ContentKitProviderProps {
7
+ client: ContentKitClient;
8
+ /** The signed-in user's id as ContentKit sees it (the actor id), or null when signed out. */
9
+ viewer?: string | null;
10
+ /** Asks the visitor to sign in: comments, reactions, favorites and votes prompt with it rather than act anonymously. */
11
+ onSignIn?: () => void;
12
+ /** Content URL helpers (createContentURLs from `/urls`). */
13
+ urls?: ContentURLs;
14
+ /** Host router for in-app navigation (a canonical replace). */
15
+ navigate?: Navigate;
16
+ /** Fires after each successful mutation: the host's cache-invalidation hook. */
17
+ onChange?: (change: ContentKitChange) => void;
18
+ /** Receives every failure the components show (a component's own `onError` wins). */
19
+ onError?: ContentKitErrorHandler;
20
+ children?: ReactNode;
21
+ }
22
+ /** Client, URL config, callbacks and the shared read store for every hook below. Renders no DOM. */
23
+ export declare function ContentKitProvider({ client, viewer, onSignIn, urls, navigate, onChange, onError, children }: ContentKitProviderProps): import('react').JSX.Element;
@@ -0,0 +1,35 @@
1
+ import { ContentKitContext as e } from "./context.js";
2
+ import { storeFor as t } from "./store.js";
3
+ import { useEffect as n, useMemo as r, useRef as i } from "react";
4
+ import { jsx as a } from "react/jsx-runtime";
5
+ //#region src/react/provider.tsx
6
+ function o({ client: o, viewer: s, onSignIn: c, urls: l, navigate: u, onChange: d, onError: f, children: p }) {
7
+ let m = i(d);
8
+ n(() => {
9
+ m.current = d;
10
+ }), n(() => o.subscribe((e) => m.current?.(e)), [o]);
11
+ let h = r(() => ({
12
+ client: o,
13
+ viewer: s,
14
+ onSignIn: c,
15
+ urls: l,
16
+ navigate: u,
17
+ onError: f,
18
+ store: t(o)
19
+ }), [
20
+ o,
21
+ s,
22
+ c,
23
+ l,
24
+ u,
25
+ f
26
+ ]);
27
+ return /* @__PURE__ */ a(e.Provider, {
28
+ value: h,
29
+ children: p
30
+ });
31
+ }
32
+ //#endregion
33
+ export { o as ContentKitProvider };
34
+
35
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","names":[],"sources":["../../src/react/provider.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, type ReactNode } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitChange } from \"../client/http.js\";\nimport type { ContentURLs } from \"../urls/index.js\";\nimport { ContentKitContext, type ContentKitErrorHandler, type Navigate } from \"./context.js\";\nimport { storeFor } from \"./store.js\";\n\nexport interface ContentKitProviderProps {\n client: ContentKitClient;\n /** The signed-in user's id as ContentKit sees it (the actor id), or null when signed out. */\n viewer?: string | null;\n /** Asks the visitor to sign in: comments, reactions, favorites and votes prompt with it rather than act anonymously. */\n onSignIn?: () => void;\n /** Content URL helpers (createContentURLs from `/urls`). */\n urls?: ContentURLs;\n /** Host router for in-app navigation (a canonical replace). */\n navigate?: Navigate;\n /** Fires after each successful mutation: the host's cache-invalidation hook. */\n onChange?: (change: ContentKitChange) => void;\n /** Receives every failure the components show (a component's own `onError` wins). */\n onError?: ContentKitErrorHandler;\n children?: ReactNode;\n}\n\n/** Client, URL config, callbacks and the shared read store for every hook below. Renders no DOM. */\nexport function ContentKitProvider({ client, viewer, onSignIn, urls, navigate, onChange, onError, children }: ContentKitProviderProps) {\n const changed = useRef(onChange);\n useEffect(() => {\n changed.current = onChange;\n });\n useEffect(() => client.subscribe((c) => changed.current?.(c)), [client]);\n const value = useMemo(() => ({ client, viewer, onSignIn, urls, navigate, onError, store: storeFor(client) }), [client, viewer, onSignIn, urls, navigate, onError]);\n return <ContentKitContext.Provider value={value}>{children}</ContentKitContext.Provider>;\n}\n"],"mappings":";;;;;AAyBA,SAAgB,EAAmB,EAAE,WAAQ,WAAQ,aAAU,SAAM,aAAU,aAAU,YAAS,eAAqC;CACrI,IAAM,IAAU,EAAO,CAAQ;CAI/B,AAHA,QAAgB;EACd,EAAQ,UAAU;CACpB,CAAC,GACD,QAAgB,EAAO,WAAW,MAAM,EAAQ,UAAU,CAAC,CAAC,GAAG,CAAC,CAAM,CAAC;CACvE,IAAM,IAAQ,SAAe;EAAE;EAAQ;EAAQ;EAAU;EAAM;EAAU;EAAS,OAAO,EAAS,CAAM;CAAE,IAAI;EAAC;EAAQ;EAAQ;EAAU;EAAM;EAAU;CAAO,CAAC;CACjK,OAAO,kBAAC,EAAkB,UAAnB;EAAmC;EAAQ;CAAqC,CAAA;AACzF"}
@@ -0,0 +1,39 @@
1
+ import { ContentKitClient } from '../client/client.js';
2
+ import { ContentKitError } from '../client/errors.js';
3
+ import { ReadResult, PresetRule } from '../client/generated/wire.js';
4
+ import { PublicPreset } from '../client/public.js';
5
+ export interface UsePresets {
6
+ /** Every kind's public presets; empty until they load (or when the server has none). */
7
+ presets: PresetRule[];
8
+ loaded: boolean;
9
+ error?: ContentKitError;
10
+ }
11
+ /** The public presets (`GET /media/presets`), fetched once per client. */
12
+ export declare function usePresets(o?: {
13
+ client?: ContentKitClient | null;
14
+ }): UsePresets;
15
+ export interface PublicImageOptions {
16
+ /** The host's listing of the item's image (its API's): shown without a read. */
17
+ image?: PublicPreset | null;
18
+ /** A read of the item the host already has. */
19
+ read?: ReadResult | null;
20
+ /** Overrides the provider's client. */
21
+ client?: ContentKitClient | null;
22
+ }
23
+ export interface UsePublicImage {
24
+ /** The item's current renditions; the kind's default image when it has none; null while unknown. */
25
+ image: PublicPreset | null;
26
+ /** The preset's rules (aspect, widths, min_width) once the presets load. */
27
+ rule?: PresetRule;
28
+ /** image is the kind's default, not the item's own. */
29
+ isDefault: boolean;
30
+ loading: boolean;
31
+ error?: ContentKitError;
32
+ reload: () => void;
33
+ }
34
+ /**
35
+ * An item's public image for a preset ("avatar", "cover"): the exact
36
+ * renditions from a read's `public` (or the host's listing), else the kind's
37
+ * default image. Reads refresh after commits through the same client.
38
+ */
39
+ export declare function usePublicImage(kind: string, id: string, preset: string, o?: PublicImageOptions): UsePublicImage;