import "./comments_thread.css"; import type * as React from "react"; import { useRender } from "@base-ui/react/use-render"; import { type StyleProps } from "./style_props"; export interface ThreadMember { id: string; name: string | null; image?: string | null; email?: string | null; } export interface ThreadFile { id: string; filename: string; mime_type: string; url?: string; thumbnail_url?: string; } export interface ThreadComment { id: string; member_id: string; content: string; files?: ThreadFile[] | null; created_at: string; updated_at: string; } export interface CommentThreadLabels { empty: string; edit: string; delete: string; edited: string; save: string; cancel: string; /** Accessible name for the per-comment actions trigger. */ actions: string; /** Shown when a comment's author can't be resolved. */ unknownMember: string; editPlaceholder: string; /** What the one entry that ANSWERS the thread is called, worn beside its author. */ answer: string; /** Making one entry that answer — named by WHOSE entry it is, because a list of * identical "mark as the answer" presses is one control to a screen reader. */ markAnswer: (author: string) => string; } /** Render-prop the consumer can supply to drive its own edit UI (e.g. with file editing). */ export interface CommentEditFormProps { comment: ThreadComment; onSave: (content: string, files?: ThreadFile[] | null) => void; onCancel: () => void; submitting: boolean; labels: CommentThreadLabels; } /** * WHICH ENTRY OF THE THREAD IS THE ANSWER, and how one is made it. * * IT IS THE THREAD'S CONTRACT AND NOT THE ROW'S, because exclusivity is a fact * about the SET: only what holds every candidate can draw the standing on one * entry and the way to move it on the others, and a per-row flag would let two * replies both read as the answer — which is the one thing a thread that has one * exists to say. * * `onMark` is what makes it writable. Without it the standing is DRAWN and not * offered, which is a thread read by somebody who may not close it. */ export interface ThreadAnswer { /** The entry that answers, or `null` while the question stands open. */ id: string | null; onMark?: (id: string) => void | Promise; } export interface CommentThreadProps extends StyleProps { comments: ThreadComment[]; /** Which entry answers the thread — omitted where these are notes rather than a question. */ answer?: ThreadAnswer; /** The viewer's member id — drives the author-only edit/delete affordance. Null hides it. */ currentMemberId: string | null; resolveMember: (memberId: string) => ThreadMember | null; /** Called on inline-edit save. Omit to make comments read-only (no edit affordance). */ onEdit?: (id: string, content: string, files?: ThreadFile[] | null) => void | Promise; /** Called on delete. Omit to hide the delete affordance. */ onDelete?: (id: string) => void | Promise; /** Render a comment's attachments. Defaults to a row of file badges + names. */ renderFiles?: (files: ThreadFile[]) => React.ReactNode; /** * Render a comment's own words. Defaults to the text as it was stored. * * WHAT NOTATION A BODY IS WRITTEN IN IS THE CONSUMER'S, not the thread's: a * message kept as markdown and printed as text shows its own asterisks, and a * thread that decided to parse everything would render a plain comment's * `**` as emphasis nobody typed. */ renderContent?: (comment: ThreadComment) => React.ReactNode; /** Render the inline edit form. Defaults to a text-only editor (preserves existing files). */ renderEditForm?: (props: CommentEditFormProps) => React.ReactNode; /** Format a comment's `created_at`. Defaults to the locale date-time string. */ formatTimestamp?: (iso: string) => string; /** Shown when there are no comments. Defaults to the `empty` label. */ emptyState?: React.ReactNode; labels?: Partial; testID?: string; ref?: React.Ref; render?: useRender.RenderProp; } /** * The thread — one author/avatar/timestamp/content/files row per comment, * oldest first, with an author-only edit/delete menu. Pair it with THE kit * `Composer` for the full thread (attach = `actionsButton` + `files` slots) — * never a bespoke comment box. */ export declare function CommentThread(props: CommentThreadProps): React.ReactElement>;