@openrails/contentkit-ui 0.98.5 → 0.99.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.
- package/README.md +26 -0
- package/dist/client/content/comments.d.ts +4 -2
- package/dist/client/content/comments.js +10 -6
- package/dist/client/content/comments.js.map +1 -1
- package/dist/client/generated/routes.d.ts +28 -2
- package/dist/client/generated/routes.js +39 -2
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +85 -0
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/media/client.d.ts +4 -0
- package/dist/client/media/client.js +8 -4
- package/dist/client/media/client.js.map +1 -1
- package/dist/client/media/queue.js +42 -26
- package/dist/client/media/queue.js.map +1 -1
- package/dist/client/media/rules.d.ts +6 -0
- package/dist/client/media/rules.js +64 -10
- package/dist/client/media/rules.js.map +1 -1
- package/dist/client/versions.d.ts +6 -2
- package/dist/client/versions.js +26 -0
- package/dist/client/versions.js.map +1 -1
- package/dist/components/admin/page-review.d.ts +13 -0
- package/dist/components/admin/page-review.js +216 -0
- package/dist/components/admin/page-review.js.map +1 -0
- package/dist/components/content/card.js +98 -103
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/daily-releases.d.ts +8 -1
- package/dist/components/content/daily-releases.js +291 -133
- package/dist/components/content/daily-releases.js.map +1 -1
- package/dist/components/content/editors.js +216 -210
- package/dist/components/content/editors.js.map +1 -1
- package/dist/components/content/filter-dialog.d.ts +11 -0
- package/dist/components/content/filter-dialog.js +29 -0
- package/dist/components/content/filter-dialog.js.map +1 -0
- package/dist/components/content/filters.d.ts +10 -0
- package/dist/components/content/filters.js +5 -2
- package/dist/components/content/filters.js.map +1 -1
- package/dist/components/content/grid.js +19 -14
- package/dist/components/content/grid.js.map +1 -1
- package/dist/components/content/index.d.ts +2 -1
- package/dist/components/content/release-reveal.d.ts +5 -0
- package/dist/components/content/release-reveal.js +62 -0
- package/dist/components/content/release-reveal.js.map +1 -0
- package/dist/components/content/search.js +95 -107
- package/dist/components/content/search.js.map +1 -1
- package/dist/components/social/comments.d.ts +1 -1
- package/dist/components/social/comments.js +15 -15
- package/dist/components/social/comments.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +24 -22
- package/dist/locales/admin/de.js +23 -0
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +23 -0
- package/dist/locales/admin/en.js +23 -0
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +23 -0
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +23 -0
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +23 -0
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +23 -0
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/discovery/de.js +11 -0
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +11 -0
- package/dist/locales/discovery/en.js +11 -0
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +11 -0
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +11 -0
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +11 -0
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +11 -0
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +34 -0
- package/dist/locales/social/en.js +2 -2
- package/dist/locales/social/en.js.map +1 -1
- package/dist/react/comments.d.ts +2 -1
- package/dist/react/comments.js +10 -8
- package/dist/react/comments.js.map +1 -1
- package/dist/react/folder.js +39 -39
- package/dist/react/folder.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/page-review.d.ts +14 -0
- package/dist/react/page-review.js +36 -0
- package/dist/react/page-review.js.map +1 -0
- package/dist/react/public.d.ts +3 -1
- package/dist/react/public.js +76 -72
- package/dist/react/public.js.map +1 -1
- package/dist/react/resources.d.ts +5 -13
- package/dist/react/resources.js +58 -49
- package/dist/react/resources.js.map +1 -1
- package/dist/react/use-resource.d.ts +1 -1
- package/dist/react/use-resource.js +23 -13
- package/dist/react/use-resource.js.map +1 -1
- package/dist/react/versions.js +70 -42
- package/dist/react/versions.js.map +1 -1
- package/dist/react.js +8 -7
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.js","names":[],"sources":["../../../src/components/content/search.tsx"],"sourcesContent":["import { Cancel01Icon, Clock01Icon, File01Icon, Folder01Icon, Image01Icon, Search01Icon, TagsIcon, UserIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon, type IconSvgElement } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, type RefObject, type KeyboardEvent } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Card, GroupCard } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { useRecentSearches, useTypeahead } from \"../../react/discovery.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Input } from \"#ckui/ui/input\";\nimport { RenditionImg } from \"../rendition-img.js\";\nimport { labelOf, type Labels } from \"../groups/parts.js\";\n\nexport interface SearchBoxProps {\n /** Submits the text: the host shows its search page. */\n onSearch: (q: string) => void;\n /** Suggest contents of this kind only. */\n kind?: string;\n /** The group kinds suggested; every kind with a page by default. */\n groupKinds?: readonly string[];\n defaultValue?: string;\n placeholder?: string;\n /** A suggestion's link; default its canonical path. */\n href?: (item: Card | GroupCard) => string | undefined;\n labels?: Labels;\n /** The reader's recent searches while the box is empty. Default true. */\n recent?: boolean;\n /** A portal dropdown anchored to the header field; default stays inline. */\n presentation?: \"inline\" | \"header\";\n client?: ContentKitClient;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\ntype Suggestion = { type: \"content\"; card: Card } | { type: \"group\"; group: GroupCard } | { type: \"recent\"; query: string; searchedAt?: string };\n\nconst TYPE_ICONS: Record<string, { icon: IconSvgElement; color: string }> = {\n artist: { icon: UserIcon, color: \"text-blue-500 dark:text-blue-400\" },\n series: { icon: Folder01Icon, color: \"text-yellow-600 dark:text-yellow-400\" },\n gallery: { icon: Image01Icon, color: \"text-green-600 dark:text-green-400\" },\n post: { icon: File01Icon, color: \"text-purple-500 dark:text-purple-400\" },\n blog: { icon: File01Icon, color: \"text-purple-500 dark:text-purple-400\" },\n poll: { icon: File01Icon, color: \"text-purple-500 dark:text-purple-400\" },\n character: { icon: UserIcon, color: \"text-purple-500 dark:text-purple-400\" },\n tag: { icon: TagsIcon, color: \"text-orange-500 dark:text-orange-400\" },\n};\n\nfunction useHeaderAnchor(open: boolean, enabled: boolean, root: RefObject<HTMLDivElement | null>, input: RefObject<HTMLInputElement | null>) {\n const [position, setPosition] = useState({ top: 0, left: 0, width: 0 });\n useLayoutEffect(() => {\n if (!open || !enabled || !root.current) return;\n const anchor = input.current ?? root.current;\n const view = root.current.ownerDocument.defaultView;\n if (!view) return;\n const measure = () => {\n const rect = anchor.getBoundingClientRect();\n setPosition({ top: Math.max(0, Math.min(view.innerHeight, rect.bottom + 4)), left: rect.left, width: rect.width });\n };\n measure();\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(measure);\n observer?.observe(anchor);\n view.addEventListener(\"resize\", measure);\n view.addEventListener(\"scroll\", measure, true);\n view.visualViewport?.addEventListener(\"resize\", measure);\n view.visualViewport?.addEventListener(\"scroll\", measure);\n return () => {\n observer?.disconnect();\n view.removeEventListener(\"resize\", measure);\n view.removeEventListener(\"scroll\", measure, true);\n view.visualViewport?.removeEventListener(\"resize\", measure);\n view.visualViewport?.removeEventListener(\"scroll\", measure);\n };\n }, [open, enabled, root, input]);\n return position;\n}\n\nfunction SearchTypeIcon({ kind }: { kind: string }) {\n const type = TYPE_ICONS[kind];\n return <HugeiconsIcon icon={type?.icon ?? Search01Icon} strokeWidth={2} className={cn(\"size-4 shrink-0\", type?.color ?? \"text-muted-foreground\")} data-search-kind={kind} aria-hidden />;\n}\n\nfunction SearchDate({ value, formatter }: { value?: string; formatter: Intl.DateTimeFormat }) {\n if (!value || Number.isNaN(Date.parse(value))) return null;\n return <time dateTime={value} className=\"shrink-0 text-xs text-muted-foreground\">{formatter.format(new Date(value))}</time>;\n}\n\n/**\n * A search field suggesting contents and groups as the reader types, and\n * recent searches while it is empty; Enter searches, or opens the suggestion chosen.\n */\nexport function SearchBox(p: SearchBoxProps) {\n const { t, error, language } = useMessages();\n const id = useId();\n const navigate = useContext(ContentKitContext)?.navigate;\n const [text, setText] = useState(p.defaultValue ?? \"\");\n const [open, setOpen] = useState(false);\n const [active, setActive] = useState(-1);\n const root = useRef<HTMLDivElement>(null);\n const panel = useRef<HTMLDivElement>(null);\n const input = useRef<HTMLInputElement>(null);\n const header = p.presentation === \"header\";\n const anchor = useHeaderAnchor(open, header, root, input);\n const { top } = anchor;\n const dates = useMemo(() => new Intl.DateTimeFormat(language, { dateStyle: \"short\" }), [language]);\n const empty = !text.trim();\n const found = useTypeahead(text, { kind: p.kind, entity_kinds: p.groupKinds, client: p.client });\n const recent = useRecentSearches({ enabled: p.recent !== false && open && empty, client: p.client });\n const items = useMemo<Suggestion[]>(\n () =>\n empty\n ? recent.searches.map((r) => ({ type: \"recent\" as const, query: r.query, searchedAt: r.searched_at }))\n : [...found.contents.map((card) => ({ type: \"content\" as const, card })), ...found.entities.map((group) => ({ type: \"group\" as const, group }))],\n [empty, recent.searches, found.contents, found.entities],\n );\n const current = empty || found.query === text.trim();\n const visible = current ? items : [];\n const selected = visible[active];\n const loading = empty ? recent.loading : !current || found.loading;\n const showRecent = empty && p.recent !== false;\n const emptyMessage = empty ? !header && showRecent && !recent.error ? \"contents.noRecentSearches\" : \"contents.searchHint\" : \"contents.noResults\";\n useEffect(() => {\n if (!open) return;\n const outside = (event: PointerEvent) => {\n if (!root.current?.contains(event.target as Node) && !panel.current?.contains(event.target as Node)) setOpen(false);\n };\n document.addEventListener(\"pointerdown\", outside);\n return () => document.removeEventListener(\"pointerdown\", outside);\n }, [open]);\n const reveal = () => {\n setOpen(true);\n };\n const search = (q: string) => {\n setOpen(false);\n if (q) p.onSearch(q);\n };\n const go = (s: Suggestion) => {\n if (s.type === \"recent\") {\n setText(s.query);\n return search(s.query);\n }\n const url = p.href ? p.href(s.type === \"content\" ? s.card : s.group) : s.type === \"content\" ? s.card.path : s.group.path;\n setOpen(false);\n if (!url) return;\n if (navigate) navigate(url);\n else location.assign(url);\n };\n const panelKeyDown = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n input.current?.focus();\n setOpen(false);\n setActive(-1);\n };\n const results = (<>\n {showRecent && (\n <div className={cn(\"flex items-center justify-between gap-2 text-muted-foreground\", header ? \"mb-4 text-base font-semibold\" : \"px-2 py-1 text-xs\")}>\n <span id={`${id}-recent`} className=\"flex items-center gap-2\">{header && <HugeiconsIcon icon={Clock01Icon} strokeWidth={2} className=\"size-5\" aria-hidden />}{t(\"contents.recentSearches\")}</span>\n {visible.length > 0 && <button\n type=\"button\"\n className=\"rounded px-1 hover:text-foreground\"\n onMouseDown={(e) => {\n e.preventDefault();\n }}\n onClick={() => void recent.clear()}\n >\n {t(header ? \"contents.clearAllRecent\" : \"contents.clearRecent\")}\n </button>}\n </div>\n )}\n <ul id={`${id}-list`} role=\"listbox\" aria-label={empty ? t(\"contents.recentSearches\") : t(\"contents.suggestions\")} className={cn(\"grid\", header && \"gap-2\")}>\n {visible.map((s, i) => (\n <li\n key={s.type === \"content\" ? `c:${s.card.id}` : s.type === \"group\" ? `g:${s.group.id}` : `r:${s.query}`}\n id={`${id}-${i}`}\n role=\"option\"\n aria-selected={i === active}\n data-ckui=\"suggestion\"\n data-type={s.type}\n className={cn(\"flex cursor-pointer items-center\", header ? s.type === \"recent\" ? \"gap-2 rounded-md p-3 text-base hover:bg-foreground/10\" : \"gap-3 rounded-md px-4 py-1 text-base font-medium hover:bg-foreground/10\" : \"gap-2 px-2 py-1.5\", i === active && \"bg-accent text-accent-foreground\")}\n onMouseEnter={() => setActive(i)}\n onMouseDown={(e) => {\n e.preventDefault();\n go(s);\n }}\n >\n {header && s.type !== \"recent\" ? (\n <>\n <SearchTypeIcon kind={s.type === \"content\" ? s.card.kind : s.group.kind} />\n <span className=\"sr-only\">{labelOf(p.labels, s.type === \"content\" ? s.card.kind : s.group.kind)}</span>\n <span className=\"min-w-0 flex-1 truncate\">{s.type === \"content\" ? s.card.title || s.card.code : s.group.name || s.group.slug}</span>\n </>\n ) : s.type === \"content\" ? (\n <>\n <span className=\"h-10 w-8 shrink-0 overflow-hidden rounded bg-muted\">\n <RenditionImg outputs={s.card.cover} alt=\"\" fit=\"cover\" className=\"size-full\" />\n </span>\n <span className=\"min-w-0 truncate\">{s.card.title || s.card.code}</span>\n </>\n ) : s.type === \"group\" ? (\n <>\n <span className=\"min-w-0 flex-1 truncate\">{s.group.name || s.group.slug}</span>\n <span className=\"text-xs text-muted-foreground\">{labelOf(p.labels, s.group.kind)}</span>\n </>\n ) : (\n <>\n <HugeiconsIcon icon={header ? Search01Icon : Clock01Icon} strokeWidth={2} className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden />\n <span className=\"min-w-0 flex-1 truncate\">{s.query}</span>\n {header && anchor.width >= 320 && <SearchDate value={s.searchedAt} formatter={dates} />}\n <button\n type=\"button\"\n className=\"rounded p-0.5 text-muted-foreground hover:text-foreground\"\n aria-label={t(\"contents.removeRecent\", { q: s.query })}\n onMouseDown={(e) => {\n e.preventDefault();\n e.stopPropagation();\n }}\n onClick={() => void recent.clear(s.query)}\n >\n <HugeiconsIcon icon={Cancel01Icon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n </button>\n </>\n )}\n </li>\n ))}\n </ul>\n {loading ? <div role=\"status\" className={cn(\"text-muted-foreground\", header ? \"flex justify-center py-4\" : \"px-3 py-2\")}>{header ? <><span className=\"size-6 animate-spin rounded-full border-2 border-muted border-t-foreground motion-reduce:animate-none\" aria-hidden /><span className=\"sr-only\">{t(\"contents.loading\")}</span></> : t(\"contents.loading\")}</div>\n : !empty && found.error ? <p role=\"alert\" className={cn(\"px-3 py-2 text-destructive\", header && \"text-center\")}>{error(found.error)}</p>\n : visible.length === 0 && <div role=\"status\" className={cn(\"text-muted-foreground\", header ? \"flex flex-col items-center gap-2 py-8 text-center\" : \"px-3 py-2\")}>{header && <HugeiconsIcon icon={Search01Icon} strokeWidth={2} className=\"size-8\" aria-hidden />}<p>{t(emptyMessage, { q: text.trim() })}</p></div>}\n </>);\n return (\n <ContentKitUiRoot ref={root} appearance={p.appearance} className={cn(\"relative text-sm\", p.className)} data-ckui=\"search-box\" onBlur={(event) => { if (!event.currentTarget.contains(event.relatedTarget) && !panel.current?.contains(event.relatedTarget)) setOpen(false); }}>\n <form\n role=\"search\"\n className=\"relative\"\n onSubmit={(e) => {\n e.preventDefault();\n if (open && selected) return go(selected);\n search(text.trim());\n }}\n >\n {header ? <button type=\"submit\" className=\"absolute top-1/2 left-2.5 -translate-y-1/2 rounded text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring\" aria-label={t(\"contents.search\")}><HugeiconsIcon icon={Search01Icon} strokeWidth={2} className=\"size-4\" aria-hidden /></button>\n : <HugeiconsIcon icon={Search01Icon} strokeWidth={2} className=\"pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground\" aria-hidden />}\n <Input\n ref={input}\n type={header ? \"text\" : \"search\"}\n role=\"combobox\"\n aria-label={t(\"contents.search\")}\n aria-expanded={open}\n aria-controls={`${id}-list`}\n aria-activedescendant={open && selected ? `${id}-${active}` : undefined}\n aria-autocomplete=\"list\"\n autoComplete=\"off\"\n className={cn(\"h-9 ps-8\", header && \"rounded-full border-border/50 bg-foreground/10 pe-9 text-base shadow-none md:text-base\")}\n value={text}\n placeholder={p.placeholder ?? t(\"contents.searchPlaceholder\")}\n onChange={(e) => {\n setText(e.target.value);\n setOpen(true);\n setActive(-1);\n }}\n onFocus={reveal}\n onClick={reveal}\n onKeyDown={(e) => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n setOpen(false);\n setActive(-1);\n return;\n }\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n reveal();\n setActive((a) => open ? Math.min(visible.length - 1, a + 1) : 0);\n } else if (e.key === \"ArrowUp\" && open) {\n e.preventDefault();\n setActive((a) => Math.max(-1, a - 1));\n }\n }}\n />\n {header && (text || open) && (\n <button\n type=\"button\"\n className=\"absolute top-1/2 right-2.5 -translate-y-1/2 rounded p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring\"\n aria-label={text ? t(\"contents.clearSearch\") : t(\"common.close\")}\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => {\n input.current?.focus();\n setActive(-1);\n if (text) { setText(\"\"); setOpen(true); }\n else setOpen(false);\n }}\n ><HugeiconsIcon icon={Cancel01Icon} strokeWidth={2} className=\"size-4\" aria-hidden /></button>\n )}\n </form>\n {open && (header ? createPortal(\n <ContentKitUiRoot appearance={p.appearance} className=\"fixed inset-x-0 bottom-0 z-[70] text-sm\" style={{ top }} data-ckui=\"search-layer\">\n <div className=\"absolute inset-0 bg-black/30 backdrop-blur-sm\" data-ckui=\"search-backdrop\" aria-hidden />\n <div ref={panel} style={{ width: anchor.width, marginLeft: anchor.left }} className=\"relative origin-top animate-[search-expand_300ms_ease-out] rounded-lg border border-border bg-background text-foreground shadow-lg transition-none motion-reduce:animate-none\" data-ckui=\"search-panel\" onKeyDown={panelKeyDown}>\n <div className=\"w-full px-3 pt-3\">\n <div className=\"[--search-max-height:70vh] overflow-y-auto pb-7 md:[--search-max-height:60vh]\" style={{ maxHeight: `min(var(--search-max-height), calc(100dvh - ${top}px - 1.875rem))` }}>\n <div>{results}</div>\n </div>\n </div>\n </div>\n </ContentKitUiRoot>, document.body,\n ) : <div ref={panel} className=\"absolute top-full z-20 mt-1 grid max-h-96 w-full overflow-auto rounded-md border border-border bg-popover py-1 text-popover-foreground shadow-md\" data-ckui=\"search-panel\" onKeyDown={panelKeyDown}>{results}</div>)}\n\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"search.js","names":[],"sources":["../../../src/components/content/search.tsx"],"sourcesContent":["import { Cancel01Icon, Clock01Icon, File01Icon, Folder01Icon, Image01Icon, Search01Icon, TagsIcon, UserIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon, type IconSvgElement } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, type RefObject, type KeyboardEvent } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Card, GroupCard } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { useRecentSearches, useTypeahead } from \"../../react/discovery.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Input } from \"#ckui/ui/input\";\nimport { RenditionImg } from \"../rendition-img.js\";\nimport { labelOf, type Labels } from \"../groups/parts.js\";\n\nexport interface SearchBoxProps {\n /** Submits the text: the host shows its search page. */\n onSearch: (q: string) => void;\n /** Suggest contents of this kind only. */\n kind?: string;\n /** The group kinds suggested; every kind with a page by default. */\n groupKinds?: readonly string[];\n defaultValue?: string;\n placeholder?: string;\n /** A suggestion's link; default its canonical path. */\n href?: (item: Card | GroupCard) => string | undefined;\n labels?: Labels;\n /** The reader's recent searches while the box is empty. Default true. */\n recent?: boolean;\n /** A portal dropdown anchored to the header field; default stays inline. */\n presentation?: \"inline\" | \"header\";\n client?: ContentKitClient;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\ntype Suggestion = { type: \"content\"; card: Card } | { type: \"group\"; group: GroupCard } | { type: \"recent\"; query: string };\n\nconst TYPE_ICONS: Record<string, { icon: IconSvgElement; color: string }> = {\n artist: { icon: UserIcon, color: \"text-blue-500 dark:text-blue-400\" },\n series: { icon: Folder01Icon, color: \"text-yellow-600 dark:text-yellow-400\" },\n gallery: { icon: Image01Icon, color: \"text-green-600 dark:text-green-400\" },\n post: { icon: File01Icon, color: \"text-purple-500 dark:text-purple-400\" },\n blog: { icon: File01Icon, color: \"text-purple-500 dark:text-purple-400\" },\n poll: { icon: File01Icon, color: \"text-purple-500 dark:text-purple-400\" },\n character: { icon: UserIcon, color: \"text-purple-500 dark:text-purple-400\" },\n tag: { icon: TagsIcon, color: \"text-orange-500 dark:text-orange-400\" },\n};\n\nfunction useHeaderAnchor(open: boolean, enabled: boolean, root: RefObject<HTMLDivElement | null>, input: RefObject<HTMLInputElement | null>) {\n const [position, setPosition] = useState({ top: 0, left: 0, width: 0 });\n useLayoutEffect(() => {\n if (!open || !enabled || !root.current) return;\n const anchor = input.current ?? root.current;\n const view = root.current.ownerDocument.defaultView;\n if (!view) return;\n const measure = () => {\n const rect = anchor.getBoundingClientRect();\n setPosition({ top: Math.max(0, Math.min(view.innerHeight, rect.bottom + 4)), left: rect.left, width: rect.width });\n };\n measure();\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(measure);\n observer?.observe(anchor);\n view.addEventListener(\"resize\", measure);\n view.addEventListener(\"scroll\", measure, true);\n view.visualViewport?.addEventListener(\"resize\", measure);\n view.visualViewport?.addEventListener(\"scroll\", measure);\n return () => {\n observer?.disconnect();\n view.removeEventListener(\"resize\", measure);\n view.removeEventListener(\"scroll\", measure, true);\n view.visualViewport?.removeEventListener(\"resize\", measure);\n view.visualViewport?.removeEventListener(\"scroll\", measure);\n };\n }, [open, enabled, root, input]);\n return position;\n}\n\nfunction SearchTypeIcon({ kind }: { kind: string }) {\n const type = TYPE_ICONS[kind];\n return <HugeiconsIcon icon={type?.icon ?? Search01Icon} strokeWidth={2} className={cn(\"size-4 shrink-0\", type?.color ?? \"text-muted-foreground\")} data-search-kind={kind} aria-hidden />;\n}\n\n/**\n * A search field suggesting contents and groups as the reader types, and\n * recent searches while it is empty; Enter searches, or opens the suggestion chosen.\n */\nexport function SearchBox(p: SearchBoxProps) {\n const { t, error } = useMessages();\n const id = useId();\n const navigate = useContext(ContentKitContext)?.navigate;\n const [text, setText] = useState(p.defaultValue ?? \"\");\n const [open, setOpen] = useState(false);\n const [active, setActive] = useState(-1);\n const root = useRef<HTMLDivElement>(null);\n const panel = useRef<HTMLDivElement>(null);\n const input = useRef<HTMLInputElement>(null);\n const header = p.presentation === \"header\";\n const anchor = useHeaderAnchor(open, header, root, input);\n const { top } = anchor;\n const empty = !text.trim();\n const found = useTypeahead(text, { kind: p.kind, entity_kinds: p.groupKinds, client: p.client });\n const recent = useRecentSearches({ enabled: p.recent !== false && open && empty, client: p.client });\n const items = useMemo<Suggestion[]>(\n () =>\n empty\n ? recent.searches.map((r) => ({ type: \"recent\" as const, query: r.query }))\n : [...found.contents.map((card) => ({ type: \"content\" as const, card })), ...found.entities.map((group) => ({ type: \"group\" as const, group }))],\n [empty, recent.searches, found.contents, found.entities],\n );\n const current = empty || found.query === text.trim();\n const visible = current ? items : [];\n const selected = visible[active];\n const loading = empty ? recent.loading : !current || found.loading;\n const showRecent = empty && p.recent !== false;\n const emptyMessage = empty ? !header && showRecent && !recent.error ? \"contents.noRecentSearches\" : \"contents.searchHint\" : \"contents.noResults\";\n useEffect(() => {\n if (!open) return;\n const outside = (event: PointerEvent) => {\n if (!root.current?.contains(event.target as Node) && !panel.current?.contains(event.target as Node)) setOpen(false);\n };\n document.addEventListener(\"pointerdown\", outside);\n return () => document.removeEventListener(\"pointerdown\", outside);\n }, [open]);\n const reveal = () => {\n setOpen(true);\n };\n const search = (q: string) => {\n setOpen(false);\n if (q) p.onSearch(q);\n };\n const go = (s: Suggestion) => {\n if (s.type === \"recent\") {\n setText(s.query);\n return search(s.query);\n }\n const url = p.href ? p.href(s.type === \"content\" ? s.card : s.group) : s.type === \"content\" ? s.card.path : s.group.path;\n setOpen(false);\n if (!url) return;\n if (navigate) navigate(url);\n else location.assign(url);\n };\n const panelKeyDown = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n input.current?.focus();\n setOpen(false);\n setActive(-1);\n };\n const results = (<>\n {showRecent && (\n <div className={cn(\"flex items-center justify-between gap-2 text-muted-foreground\", header ? \"mb-4 text-base font-semibold\" : \"px-2 py-1 text-xs\")}>\n <span id={`${id}-recent`} className=\"flex items-center gap-2\">{header && <HugeiconsIcon icon={Clock01Icon} strokeWidth={2} className=\"size-5\" aria-hidden />}{t(\"contents.recentSearches\")}</span>\n {visible.length > 0 && <button\n type=\"button\"\n className=\"rounded px-1 hover:text-foreground\"\n onMouseDown={(e) => {\n e.preventDefault();\n }}\n onClick={() => void recent.clear()}\n >\n {t(header ? \"contents.clearAllRecent\" : \"contents.clearRecent\")}\n </button>}\n </div>\n )}\n <ul id={`${id}-list`} role=\"listbox\" aria-label={empty ? t(\"contents.recentSearches\") : t(\"contents.suggestions\")} className={cn(\"grid\", header && \"gap-2\")}>\n {visible.map((s, i) => (\n <li\n key={s.type === \"content\" ? `c:${s.card.id}` : s.type === \"group\" ? `g:${s.group.id}` : `r:${s.query}`}\n id={`${id}-${i}`}\n role=\"option\"\n aria-selected={i === active}\n data-ckui=\"suggestion\"\n data-type={s.type}\n className={cn(\"flex cursor-pointer items-center\", header ? s.type === \"recent\" ? \"gap-2 rounded-md p-3 text-base hover:bg-foreground/10\" : \"gap-3 rounded-md px-4 py-1 text-base font-medium hover:bg-foreground/10\" : \"gap-2 px-2 py-1.5\", i === active && \"bg-accent text-accent-foreground\")}\n onMouseEnter={() => setActive(i)}\n onMouseDown={(e) => {\n e.preventDefault();\n go(s);\n }}\n >\n {header && s.type !== \"recent\" ? (\n <>\n <SearchTypeIcon kind={s.type === \"content\" ? s.card.kind : s.group.kind} />\n <span className=\"sr-only\">{labelOf(p.labels, s.type === \"content\" ? s.card.kind : s.group.kind)}</span>\n <span className=\"min-w-0 flex-1 truncate\">{s.type === \"content\" ? s.card.title || s.card.code : s.group.name || s.group.slug}</span>\n </>\n ) : s.type === \"content\" ? (\n <>\n <span className=\"h-10 w-8 shrink-0 overflow-hidden rounded bg-muted\">\n <RenditionImg outputs={s.card.cover} alt=\"\" fit=\"cover\" className=\"size-full\" />\n </span>\n <span className=\"min-w-0 truncate\">{s.card.title || s.card.code}</span>\n </>\n ) : s.type === \"group\" ? (\n <>\n <span className=\"min-w-0 flex-1 truncate\">{s.group.name || s.group.slug}</span>\n <span className=\"text-xs text-muted-foreground\">{labelOf(p.labels, s.group.kind)}</span>\n </>\n ) : (\n <>\n <HugeiconsIcon icon={header ? Search01Icon : Clock01Icon} strokeWidth={2} className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden />\n <span className=\"min-w-0 flex-1 truncate\">{s.query}</span>\n <button\n type=\"button\"\n className=\"rounded p-0.5 text-muted-foreground hover:text-foreground\"\n aria-label={t(\"contents.removeRecent\", { q: s.query })}\n onMouseDown={(e) => {\n e.preventDefault();\n e.stopPropagation();\n }}\n onClick={() => void recent.clear(s.query)}\n >\n <HugeiconsIcon icon={Cancel01Icon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n </button>\n </>\n )}\n </li>\n ))}\n </ul>\n {loading ? <div role=\"status\" className={cn(\"text-muted-foreground\", header ? \"flex justify-center py-4\" : \"px-3 py-2\")}>{header ? <><span className=\"size-6 animate-spin rounded-full border-2 border-muted border-t-foreground motion-reduce:animate-none\" aria-hidden /><span className=\"sr-only\">{t(\"contents.loading\")}</span></> : t(\"contents.loading\")}</div>\n : !empty && found.error ? <p role=\"alert\" className={cn(\"px-3 py-2 text-destructive\", header && \"text-center\")}>{error(found.error)}</p>\n : visible.length === 0 && <div role=\"status\" className={cn(\"text-muted-foreground\", header ? \"flex flex-col items-center gap-2 py-8 text-center\" : \"px-3 py-2\")}>{header && <HugeiconsIcon icon={Search01Icon} strokeWidth={2} className=\"size-8\" aria-hidden />}<p>{t(emptyMessage, { q: text.trim() })}</p></div>}\n </>);\n return (\n <ContentKitUiRoot ref={root} appearance={p.appearance} className={cn(\"relative text-sm\", p.className)} data-ckui=\"search-box\" onBlur={(event) => { if (!event.currentTarget.contains(event.relatedTarget) && !panel.current?.contains(event.relatedTarget)) setOpen(false); }}>\n <form\n role=\"search\"\n className=\"relative\"\n onSubmit={(e) => {\n e.preventDefault();\n if (open && selected) return go(selected);\n search(text.trim());\n }}\n >\n {header ? <button type=\"submit\" className=\"absolute top-1/2 left-2.5 -translate-y-1/2 rounded text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring\" aria-label={t(\"contents.search\")}><HugeiconsIcon icon={Search01Icon} strokeWidth={2} className=\"size-4\" aria-hidden /></button>\n : <HugeiconsIcon icon={Search01Icon} strokeWidth={2} className=\"pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground\" aria-hidden />}\n <Input\n ref={input}\n type={header ? \"text\" : \"search\"}\n role=\"combobox\"\n aria-label={t(\"contents.search\")}\n aria-expanded={open}\n aria-controls={`${id}-list`}\n aria-activedescendant={open && selected ? `${id}-${active}` : undefined}\n aria-autocomplete=\"list\"\n autoComplete=\"off\"\n className={cn(\"h-9 ps-8\", header && \"rounded-full border-border/50 bg-foreground/10 pe-9 text-base shadow-none md:text-base\")}\n value={text}\n placeholder={p.placeholder ?? t(\"contents.searchPlaceholder\")}\n onChange={(e) => {\n setText(e.target.value);\n setOpen(true);\n setActive(-1);\n }}\n onFocus={reveal}\n onClick={reveal}\n onKeyDown={(e) => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n setOpen(false);\n setActive(-1);\n return;\n }\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n reveal();\n setActive((a) => open ? Math.min(visible.length - 1, a + 1) : 0);\n } else if (e.key === \"ArrowUp\" && open) {\n e.preventDefault();\n setActive((a) => Math.max(-1, a - 1));\n }\n }}\n />\n {header && (text || open) && (\n <button\n type=\"button\"\n className=\"absolute top-1/2 right-2.5 -translate-y-1/2 rounded p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring\"\n aria-label={text ? t(\"contents.clearSearch\") : t(\"common.close\")}\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => {\n input.current?.focus();\n setActive(-1);\n if (text) { setText(\"\"); setOpen(true); }\n else setOpen(false);\n }}\n ><HugeiconsIcon icon={Cancel01Icon} strokeWidth={2} className=\"size-4\" aria-hidden /></button>\n )}\n </form>\n {open && (header ? createPortal(\n <ContentKitUiRoot appearance={p.appearance} className=\"fixed inset-x-0 bottom-0 z-[70] text-sm\" style={{ top }} data-ckui=\"search-layer\">\n <div className=\"absolute inset-0 bg-black/30 backdrop-blur-sm\" data-ckui=\"search-backdrop\" aria-hidden />\n <div ref={panel} style={{ width: anchor.width, marginLeft: anchor.left }} className=\"relative origin-top animate-[search-expand_300ms_ease-out] rounded-lg border border-border bg-background text-foreground shadow-lg transition-none motion-reduce:animate-none\" data-ckui=\"search-panel\" onKeyDown={panelKeyDown}>\n <div className=\"w-full px-3 pt-3\">\n <div className=\"[--search-max-height:70vh] overflow-y-auto pb-7 md:[--search-max-height:60vh]\" style={{ maxHeight: `min(var(--search-max-height), calc(100dvh - ${top}px - 1.875rem))` }}>\n <div>{results}</div>\n </div>\n </div>\n </div>\n </ContentKitUiRoot>, document.body,\n ) : <div ref={panel} className=\"absolute top-full z-20 mt-1 grid max-h-96 w-full overflow-auto rounded-md border border-border bg-popover py-1 text-popover-foreground shadow-md\" data-ckui=\"search-panel\" onKeyDown={panelKeyDown}>{results}</div>)}\n\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ export interface CommentsProps {
|
|
|
9
9
|
item: Target;
|
|
10
10
|
/** One language's thread: shown, and joined by new comments. */
|
|
11
11
|
language?: string;
|
|
12
|
-
/** The item's comment count
|
|
12
|
+
/** The item's total comment count. Otherwise inferred when the whole thread is loaded. */
|
|
13
13
|
count?: number;
|
|
14
14
|
/** Initial order: newest (default) or best (top). */
|
|
15
15
|
sort?: Sort;
|
|
@@ -20,14 +20,17 @@ import { cn as H } from "cn";
|
|
|
20
20
|
import { MoreHorizontalIcon as U, ThumbsDownIcon as W, ThumbsUpIcon as G } from "@hugeicons/core-free-icons";
|
|
21
21
|
import { HugeiconsIcon as K } from "@hugeicons/react";
|
|
22
22
|
//#region src/components/social/comments.tsx
|
|
23
|
-
var q = N(null), J = () => P(q), Y = typeof navigator < "u" && /Mac|iP(hone|ad)/.test(navigator.platform), X = 300,
|
|
24
|
-
function
|
|
23
|
+
var q = N(null), J = () => P(q), Y = typeof navigator < "u" && /Mac|iP(hone|ad)/.test(navigator.platform), X = 300, ee = (e, t) => e.author?.username ?? e.anon_name ?? t;
|
|
24
|
+
function Z(e) {
|
|
25
25
|
let o = t(), { t: s } = o, c = P(r), [l, f] = R(e.sort ?? "newest"), h = p(e.item, {
|
|
26
26
|
sort: l,
|
|
27
27
|
language: e.language,
|
|
28
28
|
pageSize: e.pageSize ?? 20,
|
|
29
29
|
client: e.client
|
|
30
|
-
}), { standing: g, reload: _ } = u(e.item, {
|
|
30
|
+
}), { standing: g, reload: _ } = u(e.item, {
|
|
31
|
+
language: e.language,
|
|
32
|
+
client: e.client
|
|
33
|
+
}), { config: C } = m({ client: e.client }), w = i(e.onError), E = e.onSignIn ?? c?.onSignIn, D = c?.viewer, O = g ? !!g.user_id : D ? !0 : D !== null && void 0, k = I(() => g?.ban_scopes ?? [], [g]), A = d({
|
|
31
34
|
scope: "owner",
|
|
32
35
|
pageSize: 100,
|
|
33
36
|
enabled: k.includes("owner"),
|
|
@@ -71,7 +74,7 @@ function Q(e) {
|
|
|
71
74
|
G(e), _();
|
|
72
75
|
},
|
|
73
76
|
onLocked: _
|
|
74
|
-
}, X = g?.ban ?? W,
|
|
77
|
+
}, X = g?.ban ?? W, ee = !g || g.can_comment, Z = e.count ?? g?.count ?? (!h.loading && !h.error && !h.hasMore ? h.items.length : void 0), Q = e.heading === void 0 ? Z === void 0 ? s("comments.title") : o.plural("comments.count", Z) : e.heading;
|
|
75
78
|
return g?.comments === "hidden" ? null : g?.locked ? /* @__PURE__ */ V(n, {
|
|
76
79
|
appearance: e.appearance,
|
|
77
80
|
className: H("grid gap-4 text-sm", e.className),
|
|
@@ -125,7 +128,7 @@ function Q(e) {
|
|
|
125
128
|
}) : /* @__PURE__ */ B("p", {
|
|
126
129
|
className: "rounded-lg border border-border p-3 text-muted-foreground",
|
|
127
130
|
children: s("comments.signIn")
|
|
128
|
-
}) :
|
|
131
|
+
}) : ee ? /* @__PURE__ */ B($, {
|
|
129
132
|
placeholder: s("comments.placeholder"),
|
|
130
133
|
submit: s("comments.post"),
|
|
131
134
|
askName: O === !1,
|
|
@@ -135,7 +138,7 @@ function Q(e) {
|
|
|
135
138
|
className: "rounded-lg border border-border p-3 text-muted-foreground",
|
|
136
139
|
children: s("comments.locked")
|
|
137
140
|
}),
|
|
138
|
-
/* @__PURE__ */ B(
|
|
141
|
+
/* @__PURE__ */ B(te, {}),
|
|
139
142
|
F && /* @__PURE__ */ B(M, {
|
|
140
143
|
open: !0,
|
|
141
144
|
onOpenChange: (e) => !e && L(null),
|
|
@@ -159,7 +162,7 @@ function Q(e) {
|
|
|
159
162
|
})
|
|
160
163
|
});
|
|
161
164
|
}
|
|
162
|
-
function
|
|
165
|
+
function te() {
|
|
163
166
|
let { t: e } = t(), { comments: n } = J();
|
|
164
167
|
return n.loading && !n.items.length ? /* @__PURE__ */ B("div", {
|
|
165
168
|
className: "grid gap-4",
|
|
@@ -190,7 +193,7 @@ function ee() {
|
|
|
190
193
|
className: "grid divide-y divide-border",
|
|
191
194
|
children: n.items.map((e) => /* @__PURE__ */ B("li", {
|
|
192
195
|
className: "py-3 first:pt-0",
|
|
193
|
-
children: /* @__PURE__ */ B(
|
|
196
|
+
children: /* @__PURE__ */ B(Q, { comment: e })
|
|
194
197
|
}, e.id))
|
|
195
198
|
}),
|
|
196
199
|
n.error && /* @__PURE__ */ B(E, { error: n.error }),
|
|
@@ -201,12 +204,9 @@ function ee() {
|
|
|
201
204
|
onClick: () => void n.loadMore(),
|
|
202
205
|
children: n.loadingMore ? e("comments.loading") : e("comments.loadMore")
|
|
203
206
|
})
|
|
204
|
-
] }) :
|
|
205
|
-
className: "text-muted-foreground",
|
|
206
|
-
children: e("comments.empty")
|
|
207
|
-
});
|
|
207
|
+
] }) : null;
|
|
208
208
|
}
|
|
209
|
-
function
|
|
209
|
+
function Q({ comment: e }) {
|
|
210
210
|
let { t: n, plural: r } = t(), i = J(), [o, s] = R(!1), [c, l] = R(null), u = f(e.id, {
|
|
211
211
|
pageSize: i.repliesPageSize,
|
|
212
212
|
enabled: o
|
|
@@ -273,7 +273,7 @@ function te({ comment: e }) {
|
|
|
273
273
|
});
|
|
274
274
|
}
|
|
275
275
|
function ne({ comment: n, onReply: r }) {
|
|
276
|
-
let { t: i } = t(), u = J(), { standing: d } = u, f =
|
|
276
|
+
let { t: i } = t(), u = J(), { standing: d } = u, f = ee(n, i("comments.anonymous")), p = !!d?.user_id && n.user_id === d.user_id, m = !n.deleted && (p || !!d?.moderate), g = !n.deleted && !!n.user_id && !p && (d?.ban_scopes.length ?? 0) > 0, _ = n.user_id ? u.banned.get(n.user_id) : void 0, [v, y] = R(!1), [b, x] = R(!1), [S, C] = R(!1), [w, A] = R(null), j = async (t, n) => {
|
|
277
277
|
A(null);
|
|
278
278
|
try {
|
|
279
279
|
await n();
|
|
@@ -530,6 +530,6 @@ function $(n) {
|
|
|
530
530
|
});
|
|
531
531
|
}
|
|
532
532
|
//#endregion
|
|
533
|
-
export {
|
|
533
|
+
export { Z as Comments };
|
|
534
534
|
|
|
535
535
|
//# sourceMappingURL=comments.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comments.js","names":[],"sources":["../../../src/components/social/comments.tsx"],"sourcesContent":["import { MoreHorizontalIcon, ThumbsDownIcon, ThumbsUpIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { createContext, useContext, useId, useMemo, useRef, useState, type KeyboardEvent, type ReactNode } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { BanScope, Reaction, Sort } from \"../../client/content/types.js\";\nimport { toContentKitError, type ContentKitError } from \"../../client/errors.js\";\nimport type { Target } from \"../../client/content/types.js\";\nimport type { BanNotice as BanNoticeData, Comment, CommentBan, CommentStanding, PublicUser } from \"../../client/generated/wire.js\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useCanComment, useCommentBans, useCommentReplies, useComments, type UseComments } from \"../../react/comments.js\";\nimport { useContentConfig } from \"../../react/config.js\";\nimport { ContentKitContext, useErrorReporter, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from \"#ckui/ui/dropdown-menu\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { Textarea } from \"#ckui/ui/textarea\";\nimport { BanNotice, CommentBanDialog } from \"./bans.js\";\nimport { ConfirmDialog, contentError, ErrorLine, RelativeTime, UserAvatar, UserName } from \"./parts.js\";\n\nexport interface CommentsProps {\n /** The commented content item, version or group. */\n item: Target;\n /** One language's thread: shown, and joined by new comments. */\n language?: string;\n /** The item's comment count, for the heading; without it the heading has none. */\n count?: number;\n /** Initial order: newest (default) or best (top). */\n sort?: Sort;\n /** Top-level comments per page. Default 20. */\n pageSize?: number;\n /** Replies per page. Default 10. */\n repliesPageSize?: number;\n /**\n * Asks a signed-out visitor to sign in where the server takes no anonymous\n * comments or reactions (default the provider's). Where it does, they\n * comment under a name and react anonymously.\n */\n onSignIn?: () => void;\n /** A body as the host formats it (links, spoilers); default plain text. */\n renderBody?: (body: string, comment: Comment) => ReactNode;\n /** A profile URL for an author. */\n userHref?: (user: PublicUser) => string | undefined;\n /** The heading; false hides it. */\n heading?: ReactNode | false;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\ninterface Thread {\n item: Target;\n comments: UseComments;\n standing: CommentStanding | null;\n /** undefined: not known yet. */\n signedIn?: boolean;\n /** Runs fn, or asks to sign in where signed-out visitors may not comment or react. */\n gate: (kind: Act, fn: () => void) => void;\n /** false: signed out where the server refuses it, with no sign-in to offer. */\n can: (kind: Act) => boolean;\n /** The server's longest comment (the standing's); undefined until it loads. */\n maxLength?: number;\n repliesPageSize: number;\n renderBody?: CommentsProps[\"renderBody\"];\n userHref?: CommentsProps[\"userHref\"];\n report: (e: unknown, op: \"comment.post\" | \"comment.edit\" | \"comment.delete\" | \"comment.react\" | \"comment.restore\") => void;\n /** Users banned in each scope the caller holds, with the ban. */\n banned: Map<string, { scope: BanScope; ban: CommentBan }[]>;\n openBan: (user: { id: string; name: string }, ban?: CommentBan) => void;\n liftBan: (user: { id: string; name: string }) => void;\n /** A post was refused by a ban: its notice. */\n onBanned: (ban: BanNoticeData) => void;\n /** A write was refused as locked: the standing changed. */\n onLocked: () => void;\n}\n\ntype Act = \"comment\" | \"react\";\n\nconst ThreadContext = createContext<Thread | null>(null);\nconst useThread = () => useContext(ThreadContext)!;\n\nconst mac = typeof navigator !== \"undefined\" && /Mac|iP(hone|ad)/.test(navigator.platform);\nconst TRUNCATE = 300;\n\nconst authorName = (c: Comment, anonymous: string) => c.author?.username ?? c.anon_name ?? anonymous;\n\n/**\n * An item's comment thread: top-level comments a page at a time with\n * one-level replies, tombstones, held and rejected states shown to their\n * author, reactions, edit, delete, ban and rate-limit notices, and a sign-in\n * prompt. Moderators (as the server says) edit, delete and restore any\n * comment; an item's owner and site staff ban commenters.\n */\nexport function Comments(p: CommentsProps) {\n const m = useMessages();\n const { t } = m;\n const ctx = useContext(ContentKitContext);\n const [sort, setSort] = useState<Sort>(p.sort ?? \"newest\");\n const comments = useComments(p.item, { sort, language: p.language, pageSize: p.pageSize ?? 20, client: p.client });\n const { standing, reload: reloadStanding } = useCanComment(p.item, { client: p.client });\n const { config } = useContentConfig({ client: p.client });\n const report = useErrorReporter(p.onError);\n const signIn = p.onSignIn ?? ctx?.onSignIn;\n const viewer = ctx?.viewer;\n const signedIn = standing ? !!standing.user_id : viewer ? true : viewer === null ? false : undefined;\n const scopes = useMemo(() => standing?.ban_scopes ?? [], [standing]);\n const ownerBans = useCommentBans({ scope: \"owner\", pageSize: 100, enabled: scopes.includes(\"owner\"), client: p.client });\n const globalBans = useCommentBans({ scope: \"global\", pageSize: 100, enabled: scopes.includes(\"global\"), client: p.client });\n const [banning, setBanning] = useState<{ user: { id: string; name: string }; ban?: CommentBan } | null>(null);\n const [lifting, setLifting] = useState<{ id: string; name: string } | null>(null);\n const [refusal, setRefusal] = useState<BanNoticeData | null>(null);\n\n const banned = useMemo(() => {\n const out = new Map<string, { scope: BanScope; ban: CommentBan }[]>();\n const add = (scope: BanScope, list: CommentBan[]) => {\n for (const ban of list) if (!ban.expired) out.set(ban.user_id, [...(out.get(ban.user_id) ?? []), { scope, ban }]);\n };\n if (scopes.includes(\"owner\")) add(\"owner\", ownerBans.items);\n if (scopes.includes(\"global\")) add(\"global\", globalBans.items);\n return out;\n }, [scopes, ownerBans.items, globalBans.items]);\n\n // Signed out where the server refuses it: the standing says for comments, the config for reactions.\n const mustSignIn = (kind: Act) => signedIn === false && (kind === \"comment\" ? standing?.anonymous === false : config?.anonymous.reactions === false);\n const thread: Thread = {\n item: p.item,\n comments,\n standing,\n signedIn,\n gate: (kind, fn) => (mustSignIn(kind) ? signIn?.() : fn()),\n can: (kind) => !mustSignIn(kind) || !!signIn,\n maxLength: standing?.max_length,\n repliesPageSize: p.repliesPageSize ?? 10,\n renderBody: p.renderBody,\n userHref: p.userHref,\n report: (e, op) => report(e, op),\n banned,\n openBan: (user, ban) => setBanning({ user, ban }),\n liftBan: setLifting,\n onBanned: (ban) => {\n setRefusal(ban);\n reloadStanding();\n },\n onLocked: reloadStanding,\n };\n const ban = standing?.ban ?? refusal;\n const canWrite = standing ? standing.can_comment : true;\n const heading = p.heading === undefined ? (p.count === undefined ? t(\"comments.title\") : m.plural(\"comments.count\", p.count)) : p.heading;\n\n if (standing?.comments === \"hidden\") return null;\n if (standing?.locked)\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"comments\" data-locked=\"\">\n {heading !== false && <h2 className=\"text-base font-semibold\">{p.heading ?? t(\"comments.title\")}</h2>}\n <p className=\"rounded-lg border border-border p-3 text-muted-foreground\" data-ckui=\"comments-locked\">\n {t(\"comments.unlock\")}\n </p>\n </ContentKitUiRoot>\n );\n return (\n <ThreadContext.Provider value={thread}>\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"comments\">\n {(heading !== false || comments.items.length > 1) && (\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n {heading !== false && <h2 className=\"text-base font-semibold\">{heading}</h2>}\n <Select value={sort} onValueChange={(v) => setSort(v as Sort)}>\n <SelectTrigger size=\"sm\" aria-label={t(\"comments.sort\")} className=\"ms-auto\">\n <SelectValue>{(v: string) => t(v === \"best\" ? \"comments.best\" : \"comments.newest\")}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"newest\">{t(\"comments.newest\")}</SelectItem>\n <SelectItem value=\"best\">{t(\"comments.best\")}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n )}\n {standing?.comments === \"closed\" ? (\n <p className=\"rounded-lg border border-border p-3 text-muted-foreground\">{t(\"comments.closed\")}</p>\n ) : ban ? (\n <BanNotice ban={ban} />\n ) : mustSignIn(\"comment\") ? (\n signIn ? (\n <Button variant=\"outline\" className=\"justify-self-start\" onClick={signIn}>\n {t(\"comments.signIn\")}\n </Button>\n ) : (\n <p className=\"rounded-lg border border-border p-3 text-muted-foreground\">{t(\"comments.signIn\")}</p>\n )\n ) : !canWrite ? (\n <p className=\"rounded-lg border border-border p-3 text-muted-foreground\">{t(\"comments.locked\")}</p>\n ) : (\n <Composer\n placeholder={t(\"comments.placeholder\")}\n submit={t(\"comments.post\")}\n askName={signedIn === false}\n onSubmit={async (body, name) => (await comments.post(body, { anonName: name }), true)}\n operation=\"comment.post\"\n />\n )}\n <CommentList />\n {banning && (\n <CommentBanDialog\n open\n onOpenChange={(o) => !o && setBanning(null)}\n user={banning.user}\n scopes={banning.ban ? [banning.ban.scope === \"global\" ? \"global\" : \"owner\"] : scopes}\n ban={banning.ban}\n client={p.client}\n onError={p.onError}\n />\n )}\n <ConfirmDialog\n open={!!lifting}\n onOpenChange={(o) => !o && setLifting(null)}\n title={t(\"bans.liftTitle\", { name: lifting?.name ?? \"\" })}\n description={t(\"bans.liftBody\")}\n confirm={t(\"bans.lift\")}\n onConfirm={() => {\n if (!lifting) return;\n for (const b of banned.get(lifting.id) ?? []) {\n void (b.scope === \"global\" ? globalBans : ownerBans).lift(lifting.id).catch((e) => report(e, \"ban.lift\"));\n }\n }}\n />\n </ContentKitUiRoot>\n </ThreadContext.Provider>\n );\n}\n\nfunction CommentList() {\n const { t } = useMessages();\n const { comments } = useThread();\n if (comments.loading && !comments.items.length) {\n return (\n <div className=\"grid gap-4\" aria-busy aria-label={t(\"comments.loading\")}>\n {[0, 1, 2].map((i) => (\n <div key={i} className=\"flex gap-3\">\n <Skeleton className=\"size-8 rounded-full\" />\n <div className=\"grid flex-1 gap-2\">\n <Skeleton className=\"h-3 w-32\" />\n <Skeleton className=\"h-3 w-full\" />\n </div>\n </div>\n ))}\n </div>\n );\n }\n if (comments.error && !comments.items.length) {\n return (\n <div className=\"grid justify-items-start gap-2\" data-ckui=\"comments-error\">\n <ErrorLine>{t(\"comments.loadFailed\")}</ErrorLine>\n <Button size=\"sm\" variant=\"outline\" onClick={comments.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n );\n }\n if (!comments.items.length) return <p className=\"text-muted-foreground\">{t(\"comments.empty\")}</p>;\n return (\n <>\n <ul className=\"grid divide-y divide-border\">\n {comments.items.map((c) => (\n <li key={c.id} className=\"py-3 first:pt-0\">\n <TopLevel comment={c} />\n </li>\n ))}\n </ul>\n {comments.error && <ErrorLine error={comments.error} />}\n {comments.hasMore && (\n <Button variant=\"outline\" className=\"justify-self-center\" disabled={comments.loadingMore} onClick={() => void comments.loadMore()}>\n {comments.loadingMore ? t(\"comments.loading\") : t(\"comments.loadMore\")}\n </Button>\n )}\n </>\n );\n}\n\nfunction TopLevel({ comment }: { comment: Comment }) {\n const { t, plural } = useMessages();\n const thread = useThread();\n const [open, setOpen] = useState(false);\n const [replyTo, setReplyTo] = useState<string | null>(null);\n const replies = useCommentReplies(comment.id, { pageSize: thread.repliesPageSize, enabled: open });\n const startReply = (name: string) =>\n thread.gate(\"comment\", () => {\n setReplyTo(name);\n if (comment.reply_count > 0) setOpen(true);\n });\n return (\n <div className=\"grid gap-2\">\n <Row comment={comment} onReply={comment.deleted || comment.moderation || !thread.can(\"comment\") ? undefined : startReply} />\n {(comment.reply_count > 0 || (open && replies.items.length > 0)) && (\n <Button variant=\"link\" size=\"xs\" className=\"ms-11 justify-self-start px-0\" aria-expanded={open} onClick={() => setOpen(!open)}>\n {open ? t(\"comments.hideReplies\") : plural(\"comments.showReplies\", comment.reply_count)}\n </Button>\n )}\n {open && (\n <ul className=\"ms-11 grid gap-3 border-s border-border ps-3\" aria-busy={replies.loading}>\n {replies.loading && !replies.items.length && <li className=\"text-muted-foreground\">{t(\"comments.loadingReplies\")}</li>}\n {replies.items.map((r) => (\n <li key={r.id}>\n <Row comment={r} onReply={r.deleted || r.moderation || !thread.can(\"comment\") ? undefined : startReply} />\n </li>\n ))}\n {replies.error && (\n <li>\n <ErrorLine error={replies.error} />\n </li>\n )}\n {replies.hasMore && (\n <li>\n <Button variant=\"link\" size=\"xs\" className=\"px-0\" disabled={replies.loadingMore} onClick={() => void replies.loadMore()}>\n {t(\"comments.moreReplies\")}\n </Button>\n </li>\n )}\n </ul>\n )}\n {replyTo !== null && (\n <div className=\"ms-11 grid gap-1\">\n <p className=\"text-xs text-muted-foreground\">{t(\"comments.replyingTo\", { name: replyTo })}</p>\n <Composer\n autoFocus\n placeholder={t(\"comments.replyPlaceholder\")}\n submit={t(\"comments.reply\")}\n askName={thread.signedIn === false}\n onCancel={() => setReplyTo(null)}\n operation=\"comment.post\"\n onSubmit={async (body, name) => {\n await thread.comments.post(body, { replyTo: comment.id, anonName: name });\n setReplyTo(null);\n setOpen(true);\n return true;\n }}\n />\n </div>\n )}\n </div>\n );\n}\n\nfunction Row({ comment: c, onReply }: { comment: Comment; onReply?: (name: string) => void }) {\n const m = useMessages();\n const { t } = m;\n const thread = useThread();\n const { standing } = thread;\n const name = authorName(c, t(\"comments.anonymous\"));\n const own = !!standing?.user_id && c.user_id === standing.user_id;\n const canEdit = !c.deleted && (own || !!standing?.moderate);\n const canBan = !c.deleted && !!c.user_id && !own && (standing?.ban_scopes.length ?? 0) > 0;\n const bans = c.user_id ? thread.banned.get(c.user_id) : undefined;\n const [editing, setEditing] = useState(false);\n const [deleting, setDeleting] = useState(false);\n const [expanded, setExpanded] = useState(false);\n const [error, setError] = useState<ContentKitError | null>(null);\n const act = async (op: \"comment.delete\" | \"comment.react\" | \"comment.restore\", fn: () => Promise<unknown>) => {\n setError(null);\n try {\n await fn();\n } catch (e) {\n const err = toContentKitError(e);\n setError(err);\n thread.report(err, op);\n }\n };\n const react = (v: Reaction) => thread.gate(\"react\", () => void act(\"comment.react\", () => thread.comments.react(c, c.mine === v ? 0 : v)));\n const long = !thread.renderBody && c.body.length > TRUNCATE + 40;\n const body = long && !expanded ? c.body.slice(0, TRUNCATE).trimEnd() + \"…\" : c.body;\n\n return (\n <article className=\"flex gap-3\" data-ckui=\"comment\" data-comment={c.id} aria-label={name}>\n <UserAvatar user={c.deleted ? undefined : c.author} name={c.deleted ? \"?\" : name} />\n <div className=\"grid min-w-0 flex-1 gap-1\">\n <header className=\"flex flex-wrap items-center gap-x-2 gap-y-0.5\">\n {c.deleted ? <span className=\"text-muted-foreground\">{t(\"comments.anonymous\")}</span> : <UserName user={c.author} name={name} href={thread.userHref} />}\n <RelativeTime at={c.created_at} className=\"text-xs text-muted-foreground\" />\n {c.moderation === \"held\" && <Badge variant=\"secondary\">{t(\"comments.heldBadge\")}</Badge>}\n {c.moderation === \"rejected\" && <Badge variant=\"destructive\">{t(\"comments.rejectedBadge\")}</Badge>}\n {(canEdit || canBan || (c.deleted && standing?.moderate)) && (\n <DropdownMenu>\n <DropdownMenuTrigger render={<Button variant=\"ghost\" size=\"icon-xs\" className=\"ms-auto\" aria-label={t(\"comments.actions\")} />}>\n <HugeiconsIcon icon={MoreHorizontalIcon} strokeWidth={2} />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {canEdit && <DropdownMenuItem onClick={() => setEditing(true)}>{t(\"comments.edit\")}</DropdownMenuItem>}\n {canEdit && (\n <DropdownMenuItem className=\"text-destructive\" onClick={() => setDeleting(true)}>\n {t(\"comments.delete\")}\n </DropdownMenuItem>\n )}\n {c.deleted && standing?.moderate && <DropdownMenuItem onClick={() => void act(\"comment.restore\", () => thread.comments.restore(c.id))}>{t(\"comments.restore\")}</DropdownMenuItem>}\n {canBan && !bans && <DropdownMenuItem onClick={() => thread.openBan({ id: c.user_id!, name })}>{t(\"comments.ban\")}</DropdownMenuItem>}\n {canBan && bans && <DropdownMenuItem onClick={() => thread.liftBan({ id: c.user_id!, name })}>{t(\"comments.unban\")}</DropdownMenuItem>}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </header>\n {editing ? (\n <Composer\n autoFocus\n initial={c.body}\n submit={t(\"common.save\")}\n onCancel={() => setEditing(false)}\n operation=\"comment.edit\"\n onSubmit={async (next) => {\n if (next !== c.body) await thread.comments.edit(c.id, next);\n setEditing(false);\n return true;\n }}\n />\n ) : c.deleted ? (\n <p className=\"text-muted-foreground italic\">{t(\"comments.deleted\")}</p>\n ) : (\n <div className=\"break-words whitespace-pre-wrap\">\n {thread.renderBody ? thread.renderBody(c.body, c) : body}\n {long && (\n <Button variant=\"link\" size=\"xs\" className=\"ms-1 h-auto px-0\" onClick={() => setExpanded(!expanded)}>\n {expanded ? t(\"comments.showLess\") : t(\"comments.showMore\")}\n </Button>\n )}\n </div>\n )}\n {c.moderation === \"held\" && <p className=\"text-xs text-muted-foreground\">{t(\"comments.held\")}</p>}\n {c.moderation === \"rejected\" && (\n <p className=\"text-xs text-destructive\">{c.moderation_reason ? t(\"comments.rejected\", { reason: c.moderation_reason }) : t(\"comments.rejectedNoReason\")}</p>\n )}\n {!c.deleted && !c.moderation && !editing && (\n <div className=\"-ms-2 flex flex-wrap items-center gap-1\">\n <Button variant={c.mine === 1 ? \"secondary\" : \"ghost\"} size=\"xs\" aria-pressed={c.mine === 1} aria-label={t(\"comments.like\")} disabled={!thread.can(\"react\")} onClick={() => react(1)}>\n <HugeiconsIcon icon={ThumbsUpIcon} strokeWidth={2} />\n <span>{c.likes}</span>\n </Button>\n <Button variant={c.mine === -1 ? \"secondary\" : \"ghost\"} size=\"xs\" aria-pressed={c.mine === -1} aria-label={t(\"comments.dislike\")} disabled={!thread.can(\"react\")} onClick={() => react(-1)}>\n <HugeiconsIcon icon={ThumbsDownIcon} strokeWidth={2} />\n <span>{c.dislikes}</span>\n </Button>\n {onReply && (\n <Button variant=\"ghost\" size=\"xs\" onClick={() => onReply(name)}>\n {t(\"comments.reply\")}\n </Button>\n )}\n </div>\n )}\n <ErrorLine error={error} />\n </div>\n <ConfirmDialog\n open={deleting}\n onOpenChange={setDeleting}\n title={t(\"comments.deleteTitle\")}\n description={t(\"comments.deleteBody\")}\n confirm={t(\"comments.delete\")}\n onConfirm={() => void act(\"comment.delete\", () => thread.comments.remove(c.id))}\n />\n </article>\n );\n}\n\ninterface ComposerProps {\n initial?: string;\n placeholder?: string;\n submit: string;\n /** A signed-out visitor gives a name. */\n askName?: boolean;\n autoFocus?: boolean;\n onCancel?: () => void;\n operation: \"comment.post\" | \"comment.edit\";\n /** Resolves true to clear the field. */\n onSubmit: (body: string, name?: string) => Promise<boolean>;\n}\n\nfunction Composer(p: ComposerProps) {\n const m = useMessages();\n const { t } = m;\n const thread = useThread();\n const ids = useId();\n const [body, setBody] = useState(p.initial ?? \"\");\n const [name, setName] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [invalid, setInvalid] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const area = useRef<HTMLTextAreaElement>(null);\n const max = thread.maxLength;\n const left = max === undefined ? undefined : max - body.length;\n\n const send = async () => {\n const text = body.trim();\n if (!text) return setInvalid(t(\"comments.required\"));\n if (p.askName && !name.trim()) return setInvalid(t(\"comments.nameRequired\"));\n setInvalid(null);\n setError(null);\n setPending(true);\n try {\n if (await p.onSubmit(text, p.askName ? name.trim() : undefined)) setBody(\"\");\n } catch (e) {\n const err = toContentKitError(e);\n thread.report(err, p.operation);\n if (err.code === \"comment_banned\" && err.ban) thread.onBanned(err.ban);\n else if (err.code === \"comments_locked\") thread.onLocked();\n else if (err.code === \"rate_limited\") setError(err.retryAfter ? t(\"comments.rateLimited\", { seconds: err.retryAfter }) : t(\"comments.rateLimitedSoon\"));\n else setError(contentError(m, err));\n } finally {\n setPending(false);\n }\n };\n const onKey = (e: KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n void send();\n } else if (e.key === \"Escape\" && p.onCancel) p.onCancel();\n };\n return (\n <form\n className=\"grid gap-2\"\n data-ckui=\"comment-composer\"\n onSubmit={(e) => {\n e.preventDefault();\n void send();\n }}\n >\n {p.askName && (\n <div className=\"grid gap-1.5 sm:max-w-xs\">\n <Label htmlFor={`${ids}-name`}>{t(\"comments.name\")}</Label>\n <Input id={`${ids}-name`} value={name} maxLength={60} placeholder={t(\"comments.namePlaceholder\")} onChange={(e) => setName(e.target.value)} autoComplete=\"nickname\" />\n </div>\n )}\n <Textarea\n ref={area}\n value={body}\n rows={p.initial === undefined ? 2 : 3}\n maxLength={max}\n autoFocus={p.autoFocus}\n placeholder={p.placeholder}\n aria-label={p.placeholder ?? p.submit}\n aria-invalid={!!invalid}\n aria-describedby={`${ids}-hint`}\n disabled={pending}\n onChange={(e) => {\n setBody(e.target.value);\n if (invalid) setInvalid(null);\n }}\n onKeyDown={onKey}\n />\n <div className=\"flex flex-wrap items-center gap-2\">\n <span id={`${ids}-hint`} className=\"me-auto text-xs text-muted-foreground\" aria-live=\"polite\">\n {invalid ? (\n <span className=\"text-destructive\">{invalid}</span>\n ) : left !== undefined && max !== undefined && left <= max * 0.2 ? (\n m.plural(\"comments.charsLeft\", left)\n ) : (\n <span className=\"pointer-coarse:hidden\">{t(\"comments.submitHint\", { key: mac ? \"⌘\" : \"Ctrl\" })}</span>\n )}\n </span>\n {p.onCancel && (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={p.onCancel} disabled={pending}>\n {t(\"common.cancel\")}\n </Button>\n )}\n <Button type=\"submit\" size=\"sm\" disabled={pending}>\n {pending ? t(\"comments.posting\") : p.submit}\n </Button>\n </div>\n {error && <ErrorLine>{error}</ErrorLine>}\n </form>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"comments.js","names":[],"sources":["../../../src/components/social/comments.tsx"],"sourcesContent":["import { MoreHorizontalIcon, ThumbsDownIcon, ThumbsUpIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { createContext, useContext, useId, useMemo, useRef, useState, type KeyboardEvent, type ReactNode } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { BanScope, Reaction, Sort } from \"../../client/content/types.js\";\nimport { toContentKitError, type ContentKitError } from \"../../client/errors.js\";\nimport type { Target } from \"../../client/content/types.js\";\nimport type { BanNotice as BanNoticeData, Comment, CommentBan, CommentStanding, PublicUser } from \"../../client/generated/wire.js\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useCanComment, useCommentBans, useCommentReplies, useComments, type UseComments } from \"../../react/comments.js\";\nimport { useContentConfig } from \"../../react/config.js\";\nimport { ContentKitContext, useErrorReporter, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from \"#ckui/ui/dropdown-menu\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { Textarea } from \"#ckui/ui/textarea\";\nimport { BanNotice, CommentBanDialog } from \"./bans.js\";\nimport { ConfirmDialog, contentError, ErrorLine, RelativeTime, UserAvatar, UserName } from \"./parts.js\";\n\nexport interface CommentsProps {\n /** The commented content item, version or group. */\n item: Target;\n /** One language's thread: shown, and joined by new comments. */\n language?: string;\n /** The item's total comment count. Otherwise inferred when the whole thread is loaded. */\n count?: number;\n /** Initial order: newest (default) or best (top). */\n sort?: Sort;\n /** Top-level comments per page. Default 20. */\n pageSize?: number;\n /** Replies per page. Default 10. */\n repliesPageSize?: number;\n /**\n * Asks a signed-out visitor to sign in where the server takes no anonymous\n * comments or reactions (default the provider's). Where it does, they\n * comment under a name and react anonymously.\n */\n onSignIn?: () => void;\n /** A body as the host formats it (links, spoilers); default plain text. */\n renderBody?: (body: string, comment: Comment) => ReactNode;\n /** A profile URL for an author. */\n userHref?: (user: PublicUser) => string | undefined;\n /** The heading; false hides it. */\n heading?: ReactNode | false;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\ninterface Thread {\n item: Target;\n comments: UseComments;\n standing: CommentStanding | null;\n /** undefined: not known yet. */\n signedIn?: boolean;\n /** Runs fn, or asks to sign in where signed-out visitors may not comment or react. */\n gate: (kind: Act, fn: () => void) => void;\n /** false: signed out where the server refuses it, with no sign-in to offer. */\n can: (kind: Act) => boolean;\n /** The server's longest comment (the standing's); undefined until it loads. */\n maxLength?: number;\n repliesPageSize: number;\n renderBody?: CommentsProps[\"renderBody\"];\n userHref?: CommentsProps[\"userHref\"];\n report: (e: unknown, op: \"comment.post\" | \"comment.edit\" | \"comment.delete\" | \"comment.react\" | \"comment.restore\") => void;\n /** Users banned in each scope the caller holds, with the ban. */\n banned: Map<string, { scope: BanScope; ban: CommentBan }[]>;\n openBan: (user: { id: string; name: string }, ban?: CommentBan) => void;\n liftBan: (user: { id: string; name: string }) => void;\n /** A post was refused by a ban: its notice. */\n onBanned: (ban: BanNoticeData) => void;\n /** A write was refused as locked: the standing changed. */\n onLocked: () => void;\n}\n\ntype Act = \"comment\" | \"react\";\n\nconst ThreadContext = createContext<Thread | null>(null);\nconst useThread = () => useContext(ThreadContext)!;\n\nconst mac = typeof navigator !== \"undefined\" && /Mac|iP(hone|ad)/.test(navigator.platform);\nconst TRUNCATE = 300;\n\nconst authorName = (c: Comment, anonymous: string) => c.author?.username ?? c.anon_name ?? anonymous;\n\n/**\n * An item's comment thread: top-level comments a page at a time with\n * one-level replies, tombstones, held and rejected states shown to their\n * author, reactions, edit, delete, ban and rate-limit notices, and a sign-in\n * prompt. Moderators (as the server says) edit, delete and restore any\n * comment; an item's owner and site staff ban commenters.\n */\nexport function Comments(p: CommentsProps) {\n const m = useMessages();\n const { t } = m;\n const ctx = useContext(ContentKitContext);\n const [sort, setSort] = useState<Sort>(p.sort ?? \"newest\");\n const comments = useComments(p.item, { sort, language: p.language, pageSize: p.pageSize ?? 20, client: p.client });\n const { standing, reload: reloadStanding } = useCanComment(p.item, { language: p.language, client: p.client });\n const { config } = useContentConfig({ client: p.client });\n const report = useErrorReporter(p.onError);\n const signIn = p.onSignIn ?? ctx?.onSignIn;\n const viewer = ctx?.viewer;\n const signedIn = standing ? !!standing.user_id : viewer ? true : viewer === null ? false : undefined;\n const scopes = useMemo(() => standing?.ban_scopes ?? [], [standing]);\n const ownerBans = useCommentBans({ scope: \"owner\", pageSize: 100, enabled: scopes.includes(\"owner\"), client: p.client });\n const globalBans = useCommentBans({ scope: \"global\", pageSize: 100, enabled: scopes.includes(\"global\"), client: p.client });\n const [banning, setBanning] = useState<{ user: { id: string; name: string }; ban?: CommentBan } | null>(null);\n const [lifting, setLifting] = useState<{ id: string; name: string } | null>(null);\n const [refusal, setRefusal] = useState<BanNoticeData | null>(null);\n\n const banned = useMemo(() => {\n const out = new Map<string, { scope: BanScope; ban: CommentBan }[]>();\n const add = (scope: BanScope, list: CommentBan[]) => {\n for (const ban of list) if (!ban.expired) out.set(ban.user_id, [...(out.get(ban.user_id) ?? []), { scope, ban }]);\n };\n if (scopes.includes(\"owner\")) add(\"owner\", ownerBans.items);\n if (scopes.includes(\"global\")) add(\"global\", globalBans.items);\n return out;\n }, [scopes, ownerBans.items, globalBans.items]);\n\n // Signed out where the server refuses it: the standing says for comments, the config for reactions.\n const mustSignIn = (kind: Act) => signedIn === false && (kind === \"comment\" ? standing?.anonymous === false : config?.anonymous.reactions === false);\n const thread: Thread = {\n item: p.item,\n comments,\n standing,\n signedIn,\n gate: (kind, fn) => (mustSignIn(kind) ? signIn?.() : fn()),\n can: (kind) => !mustSignIn(kind) || !!signIn,\n maxLength: standing?.max_length,\n repliesPageSize: p.repliesPageSize ?? 10,\n renderBody: p.renderBody,\n userHref: p.userHref,\n report: (e, op) => report(e, op),\n banned,\n openBan: (user, ban) => setBanning({ user, ban }),\n liftBan: setLifting,\n onBanned: (ban) => {\n setRefusal(ban);\n reloadStanding();\n },\n onLocked: reloadStanding,\n };\n const ban = standing?.ban ?? refusal;\n const canWrite = standing ? standing.can_comment : true;\n // Standing counts the entire visible thread, even while only page one is\n // loaded. Older servers can infer a total only after the list is complete.\n const count = p.count ?? standing?.count ?? (!comments.loading && !comments.error && !comments.hasMore ? comments.items.length : undefined);\n const heading = p.heading === undefined ? (count === undefined ? t(\"comments.title\") : m.plural(\"comments.count\", count)) : p.heading;\n\n if (standing?.comments === \"hidden\") return null;\n if (standing?.locked)\n return (\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"comments\" data-locked=\"\">\n {heading !== false && <h2 className=\"text-base font-semibold\">{p.heading ?? t(\"comments.title\")}</h2>}\n <p className=\"rounded-lg border border-border p-3 text-muted-foreground\" data-ckui=\"comments-locked\">\n {t(\"comments.unlock\")}\n </p>\n </ContentKitUiRoot>\n );\n return (\n <ThreadContext.Provider value={thread}>\n <ContentKitUiRoot appearance={p.appearance} className={cn(\"grid gap-4 text-sm\", p.className)} data-ckui=\"comments\">\n {(heading !== false || comments.items.length > 1) && (\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n {heading !== false && <h2 className=\"text-base font-semibold\">{heading}</h2>}\n <Select value={sort} onValueChange={(v) => setSort(v as Sort)}>\n <SelectTrigger size=\"sm\" aria-label={t(\"comments.sort\")} className=\"ms-auto\">\n <SelectValue>{(v: string) => t(v === \"best\" ? \"comments.best\" : \"comments.newest\")}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"newest\">{t(\"comments.newest\")}</SelectItem>\n <SelectItem value=\"best\">{t(\"comments.best\")}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n )}\n {standing?.comments === \"closed\" ? (\n <p className=\"rounded-lg border border-border p-3 text-muted-foreground\">{t(\"comments.closed\")}</p>\n ) : ban ? (\n <BanNotice ban={ban} />\n ) : mustSignIn(\"comment\") ? (\n signIn ? (\n <Button variant=\"outline\" className=\"justify-self-start\" onClick={signIn}>\n {t(\"comments.signIn\")}\n </Button>\n ) : (\n <p className=\"rounded-lg border border-border p-3 text-muted-foreground\">{t(\"comments.signIn\")}</p>\n )\n ) : !canWrite ? (\n <p className=\"rounded-lg border border-border p-3 text-muted-foreground\">{t(\"comments.locked\")}</p>\n ) : (\n <Composer\n placeholder={t(\"comments.placeholder\")}\n submit={t(\"comments.post\")}\n askName={signedIn === false}\n onSubmit={async (body, name) => (await comments.post(body, { anonName: name }), true)}\n operation=\"comment.post\"\n />\n )}\n <CommentList />\n {banning && (\n <CommentBanDialog\n open\n onOpenChange={(o) => !o && setBanning(null)}\n user={banning.user}\n scopes={banning.ban ? [banning.ban.scope === \"global\" ? \"global\" : \"owner\"] : scopes}\n ban={banning.ban}\n client={p.client}\n onError={p.onError}\n />\n )}\n <ConfirmDialog\n open={!!lifting}\n onOpenChange={(o) => !o && setLifting(null)}\n title={t(\"bans.liftTitle\", { name: lifting?.name ?? \"\" })}\n description={t(\"bans.liftBody\")}\n confirm={t(\"bans.lift\")}\n onConfirm={() => {\n if (!lifting) return;\n for (const b of banned.get(lifting.id) ?? []) {\n void (b.scope === \"global\" ? globalBans : ownerBans).lift(lifting.id).catch((e) => report(e, \"ban.lift\"));\n }\n }}\n />\n </ContentKitUiRoot>\n </ThreadContext.Provider>\n );\n}\n\nfunction CommentList() {\n const { t } = useMessages();\n const { comments } = useThread();\n if (comments.loading && !comments.items.length) {\n return (\n <div className=\"grid gap-4\" aria-busy aria-label={t(\"comments.loading\")}>\n {[0, 1, 2].map((i) => (\n <div key={i} className=\"flex gap-3\">\n <Skeleton className=\"size-8 rounded-full\" />\n <div className=\"grid flex-1 gap-2\">\n <Skeleton className=\"h-3 w-32\" />\n <Skeleton className=\"h-3 w-full\" />\n </div>\n </div>\n ))}\n </div>\n );\n }\n if (comments.error && !comments.items.length) {\n return (\n <div className=\"grid justify-items-start gap-2\" data-ckui=\"comments-error\">\n <ErrorLine>{t(\"comments.loadFailed\")}</ErrorLine>\n <Button size=\"sm\" variant=\"outline\" onClick={comments.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n );\n }\n if (!comments.items.length) return null;\n return (\n <>\n <ul className=\"grid divide-y divide-border\">\n {comments.items.map((c) => (\n <li key={c.id} className=\"py-3 first:pt-0\">\n <TopLevel comment={c} />\n </li>\n ))}\n </ul>\n {comments.error && <ErrorLine error={comments.error} />}\n {comments.hasMore && (\n <Button variant=\"outline\" className=\"justify-self-center\" disabled={comments.loadingMore} onClick={() => void comments.loadMore()}>\n {comments.loadingMore ? t(\"comments.loading\") : t(\"comments.loadMore\")}\n </Button>\n )}\n </>\n );\n}\n\nfunction TopLevel({ comment }: { comment: Comment }) {\n const { t, plural } = useMessages();\n const thread = useThread();\n const [open, setOpen] = useState(false);\n const [replyTo, setReplyTo] = useState<string | null>(null);\n const replies = useCommentReplies(comment.id, { pageSize: thread.repliesPageSize, enabled: open });\n const startReply = (name: string) =>\n thread.gate(\"comment\", () => {\n setReplyTo(name);\n if (comment.reply_count > 0) setOpen(true);\n });\n return (\n <div className=\"grid gap-2\">\n <Row comment={comment} onReply={comment.deleted || comment.moderation || !thread.can(\"comment\") ? undefined : startReply} />\n {(comment.reply_count > 0 || (open && replies.items.length > 0)) && (\n <Button variant=\"link\" size=\"xs\" className=\"ms-11 justify-self-start px-0\" aria-expanded={open} onClick={() => setOpen(!open)}>\n {open ? t(\"comments.hideReplies\") : plural(\"comments.showReplies\", comment.reply_count)}\n </Button>\n )}\n {open && (\n <ul className=\"ms-11 grid gap-3 border-s border-border ps-3\" aria-busy={replies.loading}>\n {replies.loading && !replies.items.length && <li className=\"text-muted-foreground\">{t(\"comments.loadingReplies\")}</li>}\n {replies.items.map((r) => (\n <li key={r.id}>\n <Row comment={r} onReply={r.deleted || r.moderation || !thread.can(\"comment\") ? undefined : startReply} />\n </li>\n ))}\n {replies.error && (\n <li>\n <ErrorLine error={replies.error} />\n </li>\n )}\n {replies.hasMore && (\n <li>\n <Button variant=\"link\" size=\"xs\" className=\"px-0\" disabled={replies.loadingMore} onClick={() => void replies.loadMore()}>\n {t(\"comments.moreReplies\")}\n </Button>\n </li>\n )}\n </ul>\n )}\n {replyTo !== null && (\n <div className=\"ms-11 grid gap-1\">\n <p className=\"text-xs text-muted-foreground\">{t(\"comments.replyingTo\", { name: replyTo })}</p>\n <Composer\n autoFocus\n placeholder={t(\"comments.replyPlaceholder\")}\n submit={t(\"comments.reply\")}\n askName={thread.signedIn === false}\n onCancel={() => setReplyTo(null)}\n operation=\"comment.post\"\n onSubmit={async (body, name) => {\n await thread.comments.post(body, { replyTo: comment.id, anonName: name });\n setReplyTo(null);\n setOpen(true);\n return true;\n }}\n />\n </div>\n )}\n </div>\n );\n}\n\nfunction Row({ comment: c, onReply }: { comment: Comment; onReply?: (name: string) => void }) {\n const m = useMessages();\n const { t } = m;\n const thread = useThread();\n const { standing } = thread;\n const name = authorName(c, t(\"comments.anonymous\"));\n const own = !!standing?.user_id && c.user_id === standing.user_id;\n const canEdit = !c.deleted && (own || !!standing?.moderate);\n const canBan = !c.deleted && !!c.user_id && !own && (standing?.ban_scopes.length ?? 0) > 0;\n const bans = c.user_id ? thread.banned.get(c.user_id) : undefined;\n const [editing, setEditing] = useState(false);\n const [deleting, setDeleting] = useState(false);\n const [expanded, setExpanded] = useState(false);\n const [error, setError] = useState<ContentKitError | null>(null);\n const act = async (op: \"comment.delete\" | \"comment.react\" | \"comment.restore\", fn: () => Promise<unknown>) => {\n setError(null);\n try {\n await fn();\n } catch (e) {\n const err = toContentKitError(e);\n setError(err);\n thread.report(err, op);\n }\n };\n const react = (v: Reaction) => thread.gate(\"react\", () => void act(\"comment.react\", () => thread.comments.react(c, c.mine === v ? 0 : v)));\n const long = !thread.renderBody && c.body.length > TRUNCATE + 40;\n const body = long && !expanded ? c.body.slice(0, TRUNCATE).trimEnd() + \"…\" : c.body;\n\n return (\n <article className=\"flex gap-3\" data-ckui=\"comment\" data-comment={c.id} aria-label={name}>\n <UserAvatar user={c.deleted ? undefined : c.author} name={c.deleted ? \"?\" : name} />\n <div className=\"grid min-w-0 flex-1 gap-1\">\n <header className=\"flex flex-wrap items-center gap-x-2 gap-y-0.5\">\n {c.deleted ? <span className=\"text-muted-foreground\">{t(\"comments.anonymous\")}</span> : <UserName user={c.author} name={name} href={thread.userHref} />}\n <RelativeTime at={c.created_at} className=\"text-xs text-muted-foreground\" />\n {c.moderation === \"held\" && <Badge variant=\"secondary\">{t(\"comments.heldBadge\")}</Badge>}\n {c.moderation === \"rejected\" && <Badge variant=\"destructive\">{t(\"comments.rejectedBadge\")}</Badge>}\n {(canEdit || canBan || (c.deleted && standing?.moderate)) && (\n <DropdownMenu>\n <DropdownMenuTrigger render={<Button variant=\"ghost\" size=\"icon-xs\" className=\"ms-auto\" aria-label={t(\"comments.actions\")} />}>\n <HugeiconsIcon icon={MoreHorizontalIcon} strokeWidth={2} />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {canEdit && <DropdownMenuItem onClick={() => setEditing(true)}>{t(\"comments.edit\")}</DropdownMenuItem>}\n {canEdit && (\n <DropdownMenuItem className=\"text-destructive\" onClick={() => setDeleting(true)}>\n {t(\"comments.delete\")}\n </DropdownMenuItem>\n )}\n {c.deleted && standing?.moderate && <DropdownMenuItem onClick={() => void act(\"comment.restore\", () => thread.comments.restore(c.id))}>{t(\"comments.restore\")}</DropdownMenuItem>}\n {canBan && !bans && <DropdownMenuItem onClick={() => thread.openBan({ id: c.user_id!, name })}>{t(\"comments.ban\")}</DropdownMenuItem>}\n {canBan && bans && <DropdownMenuItem onClick={() => thread.liftBan({ id: c.user_id!, name })}>{t(\"comments.unban\")}</DropdownMenuItem>}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </header>\n {editing ? (\n <Composer\n autoFocus\n initial={c.body}\n submit={t(\"common.save\")}\n onCancel={() => setEditing(false)}\n operation=\"comment.edit\"\n onSubmit={async (next) => {\n if (next !== c.body) await thread.comments.edit(c.id, next);\n setEditing(false);\n return true;\n }}\n />\n ) : c.deleted ? (\n <p className=\"text-muted-foreground italic\">{t(\"comments.deleted\")}</p>\n ) : (\n <div className=\"break-words whitespace-pre-wrap\">\n {thread.renderBody ? thread.renderBody(c.body, c) : body}\n {long && (\n <Button variant=\"link\" size=\"xs\" className=\"ms-1 h-auto px-0\" onClick={() => setExpanded(!expanded)}>\n {expanded ? t(\"comments.showLess\") : t(\"comments.showMore\")}\n </Button>\n )}\n </div>\n )}\n {c.moderation === \"held\" && <p className=\"text-xs text-muted-foreground\">{t(\"comments.held\")}</p>}\n {c.moderation === \"rejected\" && (\n <p className=\"text-xs text-destructive\">{c.moderation_reason ? t(\"comments.rejected\", { reason: c.moderation_reason }) : t(\"comments.rejectedNoReason\")}</p>\n )}\n {!c.deleted && !c.moderation && !editing && (\n <div className=\"-ms-2 flex flex-wrap items-center gap-1\">\n <Button variant={c.mine === 1 ? \"secondary\" : \"ghost\"} size=\"xs\" aria-pressed={c.mine === 1} aria-label={t(\"comments.like\")} disabled={!thread.can(\"react\")} onClick={() => react(1)}>\n <HugeiconsIcon icon={ThumbsUpIcon} strokeWidth={2} />\n <span>{c.likes}</span>\n </Button>\n <Button variant={c.mine === -1 ? \"secondary\" : \"ghost\"} size=\"xs\" aria-pressed={c.mine === -1} aria-label={t(\"comments.dislike\")} disabled={!thread.can(\"react\")} onClick={() => react(-1)}>\n <HugeiconsIcon icon={ThumbsDownIcon} strokeWidth={2} />\n <span>{c.dislikes}</span>\n </Button>\n {onReply && (\n <Button variant=\"ghost\" size=\"xs\" onClick={() => onReply(name)}>\n {t(\"comments.reply\")}\n </Button>\n )}\n </div>\n )}\n <ErrorLine error={error} />\n </div>\n <ConfirmDialog\n open={deleting}\n onOpenChange={setDeleting}\n title={t(\"comments.deleteTitle\")}\n description={t(\"comments.deleteBody\")}\n confirm={t(\"comments.delete\")}\n onConfirm={() => void act(\"comment.delete\", () => thread.comments.remove(c.id))}\n />\n </article>\n );\n}\n\ninterface ComposerProps {\n initial?: string;\n placeholder?: string;\n submit: string;\n /** A signed-out visitor gives a name. */\n askName?: boolean;\n autoFocus?: boolean;\n onCancel?: () => void;\n operation: \"comment.post\" | \"comment.edit\";\n /** Resolves true to clear the field. */\n onSubmit: (body: string, name?: string) => Promise<boolean>;\n}\n\nfunction Composer(p: ComposerProps) {\n const m = useMessages();\n const { t } = m;\n const thread = useThread();\n const ids = useId();\n const [body, setBody] = useState(p.initial ?? \"\");\n const [name, setName] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [invalid, setInvalid] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const area = useRef<HTMLTextAreaElement>(null);\n const max = thread.maxLength;\n const left = max === undefined ? undefined : max - body.length;\n\n const send = async () => {\n const text = body.trim();\n if (!text) return setInvalid(t(\"comments.required\"));\n if (p.askName && !name.trim()) return setInvalid(t(\"comments.nameRequired\"));\n setInvalid(null);\n setError(null);\n setPending(true);\n try {\n if (await p.onSubmit(text, p.askName ? name.trim() : undefined)) setBody(\"\");\n } catch (e) {\n const err = toContentKitError(e);\n thread.report(err, p.operation);\n if (err.code === \"comment_banned\" && err.ban) thread.onBanned(err.ban);\n else if (err.code === \"comments_locked\") thread.onLocked();\n else if (err.code === \"rate_limited\") setError(err.retryAfter ? t(\"comments.rateLimited\", { seconds: err.retryAfter }) : t(\"comments.rateLimitedSoon\"));\n else setError(contentError(m, err));\n } finally {\n setPending(false);\n }\n };\n const onKey = (e: KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n void send();\n } else if (e.key === \"Escape\" && p.onCancel) p.onCancel();\n };\n return (\n <form\n className=\"grid gap-2\"\n data-ckui=\"comment-composer\"\n onSubmit={(e) => {\n e.preventDefault();\n void send();\n }}\n >\n {p.askName && (\n <div className=\"grid gap-1.5 sm:max-w-xs\">\n <Label htmlFor={`${ids}-name`}>{t(\"comments.name\")}</Label>\n <Input id={`${ids}-name`} value={name} maxLength={60} placeholder={t(\"comments.namePlaceholder\")} onChange={(e) => setName(e.target.value)} autoComplete=\"nickname\" />\n </div>\n )}\n <Textarea\n ref={area}\n value={body}\n rows={p.initial === undefined ? 2 : 3}\n maxLength={max}\n autoFocus={p.autoFocus}\n placeholder={p.placeholder}\n aria-label={p.placeholder ?? p.submit}\n aria-invalid={!!invalid}\n aria-describedby={`${ids}-hint`}\n disabled={pending}\n onChange={(e) => {\n setBody(e.target.value);\n if (invalid) setInvalid(null);\n }}\n onKeyDown={onKey}\n />\n <div className=\"flex flex-wrap items-center gap-2\">\n <span id={`${ids}-hint`} className=\"me-auto text-xs text-muted-foreground\" aria-live=\"polite\">\n {invalid ? (\n <span className=\"text-destructive\">{invalid}</span>\n ) : left !== undefined && max !== undefined && left <= max * 0.2 ? (\n m.plural(\"comments.charsLeft\", left)\n ) : (\n <span className=\"pointer-coarse:hidden\">{t(\"comments.submitHint\", { key: mac ? \"⌘\" : \"Ctrl\" })}</span>\n )}\n </span>\n {p.onCancel && (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={p.onCancel} disabled={pending}>\n {t(\"common.cancel\")}\n </Button>\n )}\n <Button type=\"submit\" size=\"sm\" disabled={pending}>\n {pending ? t(\"comments.posting\") : p.submit}\n </Button>\n </div>\n {error && <ErrorLine>{error}</ErrorLine>}\n </form>\n );\n}\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export { GroupManager, useGroupManagerAccess, type GroupManagerProps } from './c
|
|
|
29
29
|
export { MediaItemPanel, VideoMediaEditor, videoPaths, type MediaItemPanelProps, type VideoMediaEditorProps } from './components/admin/media-item-panel.js';
|
|
30
30
|
export { StatsDashboard, type StatsDashboardProps } from './components/admin/stats-dashboard.js';
|
|
31
31
|
export { ReaderManifestEditor, type ReaderManifestEditorProps } from './components/admin/reader-manifest.js';
|
|
32
|
+
export { PageReview, type PageReviewProps } from './components/admin/page-review.js';
|
|
32
33
|
export { UploadQueue, type UploadQueueProps } from './components/admin/upload-queue.js';
|
|
33
34
|
export { NoAccess, Pager, useConfirm, type AdminFlowProps, type AdminRouteProps, type ConfirmOptions, type ContentKitGuard } from './components/admin/shell.js';
|
|
34
35
|
export type { AdminNavigation, ContentKitAuth, StaffAccess, StaffArea } from './react/admin.js';
|