import type * as React from 'react'; import type { FormValidityHandle } from '../utils/formControl.js'; import type { MentionToken } from '../utils/mentions.js'; import type { PassThroughProps } from '../utils/passThrough.js'; import type { PopperContentProps } from '../utils/popper.js'; import type { PortalProps } from '../utils/portal.js'; import type { PrimitivePropsWithRef } from '../utils/primitive.js'; import type { FocusOutsideEvent, InteractOutsideEvent } from '../utils/useDismiss.js'; import type { OptionEntry } from '../utils/useOptionCollection.js'; /** One mentionable entry for the {@link MentionsProps.options} recipe prop, rendered as a `MentionsItem`. */ export interface MentionsOptionData { /** Identifier carried on the value-change detail when this entry is inserted. */ value: string; /** * Text the filter matches and the default `serialize` writes into the field. * @default undefined — falls back to `value`. */ label?: string | undefined; /** * Trigger character this entry belongs to, so one field addresses several collections. * @default undefined — the first configured trigger. */ trigger?: string | undefined; /** * Excludes this entry from insertion and from pointer or keyboard highlight. * @default false */ disabled?: boolean | undefined; } /** * Narrows Items against the active query — the substring between the trigger * character and the caret, never the whole field value. `'manual'` filters * nothing; a predicate filters, and a number both filters and ranks (`0` * excludes, positive keeps and orders by score descending). */ export type MentionsFilter = 'manual' | ((itemText: string, query: string) => boolean | number); /** What drove a {@link Mentions} value change: a user gesture, or the application. */ export type MentionsValueChangeReason = 'keyboard' | 'pointer' | 'programmatic'; /** The mention just inserted and the range of field text it replaced. */ export interface MentionsInsertedMention { /** Trigger character of the token that was replaced. */ trigger: string; /** The chosen Item's `value`, which is not what was written into the text. */ value: string; /** The text `serialize` produced and the field now contains. */ text: string; /** Offset of the replaced trigger character in the previous value. */ start: number; /** Offset just past the replaced query in the previous value. */ end: number; } /** The payload `onValueChange` receives; `mention` is null for an ordinary keystroke. */ export interface MentionsValueChangeDetail { /** The field's whole new text, never the inserted mention alone. */ value: string; /** The field's text before this change, or null when there was none. */ previousValue: string | null; /** What drove the change. */ reason: MentionsValueChangeReason; /** The entry just inserted, or null for ordinary typing. */ mention: MentionsInsertedMention | null; } /** The active token `onQueryChange` reports: its trigger, query and offsets. */ export type MentionsQueryChangeDetail = MentionToken; /** What moved virtual focus: a key, a pointer, or the component itself. */ export type MentionsHighlightReason = 'keyboard' | 'pointer' | 'auto'; /** The payload `onHighlightChange` receives whenever virtual focus moves. */ export interface MentionsHighlightChangeDetail { /** Value of the item now highlighted, or null when none is. */ value: string | null; /** Value of the item highlighted before, or null when none was. */ previousValue: string | null; /** What moved the highlight. */ reason: MentionsHighlightReason; } /** What {@link MentionsProps.serialize} is handed for the entry being inserted. */ export interface MentionsSerializeInput { /** The chosen Item's identifier, as declared on the part. */ value: string; /** The Item's `textValue`, or the text it rendered. */ textValue: string; /** Trigger character of the token being replaced now. */ trigger: string; } /** Constraint-validation handle for the {@link Mentions} ref, delegating to the real textarea. */ export type MentionsHandle = FormValidityHandle; /** @internal Shared state distributed from {@link Mentions} to its parts via context. */ export interface MentionsContextValue { inputRef: React.RefObject; contentRef: React.RefObject; /** Id of Content, which the Input's `aria-controls` names while open. */ contentId: string; /** The field's whole text. */ value: string; /** Writes the field's text for an ordinary keystroke, tagged `'keyboard'`. */ onValueChange: (value: string) => void; /** The token under the caret, or null when no mention is being typed. */ token: MentionToken | null; /** Recomputes the token from the field's text and caret, and reopens or closes. */ detect: (field: HTMLTextAreaElement) => void; /** Replaces the active token with the serialized entry and moves the caret past it. */ insertItem: (value: string, text: string, reason: MentionsValueChangeReason) => void; /** Closes the popup and suppresses the current token until it is abandoned. */ dismiss: () => void; open: boolean; onOpenChange: (open: boolean) => void; activeId: string | null; /** CSS `order` for score-ranked Items when `filter` ranks; null leaves order untouched. */ itemOrder: ReadonlyMap | null; /** Moves virtual focus and tags the move with a reason. */ setActiveId: (id: string | null, reason?: MentionsHighlightReason) => void; /** Moves the highlight one item forward or back, wrapping when `loop`. */ moveActive: (direction: 1 | -1) => string | null; itemCount: number; registerItem: (id: string, entry: OptionEntry) => void; unregisterItem: (id: string) => void; getItem: (id: string) => OptionEntry | undefined; /** Whether an item's text survives the current `filter` and query. */ matchesFilter: (itemText: string) => boolean; /** Trigger character an Item with no `trigger` prop belongs to. */ defaultTrigger: string; autoResize: boolean; minRows: number; maxRows: number | undefined; disabled: boolean; readOnly: boolean; invalid: boolean; loading: boolean; required: boolean; name: string | undefined; form: string | undefined; } /** * Props for the {@link Mentions} root. Standard attributes ({@link * PassThroughProps}) land on the derived Input, the underlying `