返回 DeepSeek-Reasonix
DisclosureRow.tsx
根目录 / desktop / frontend / src / components / harness-chat / DisclosureRow.tsx
1 // Adapted from DeepSeek Harness c291e7961a (MIT).
2 import { type KeyboardEvent, type MouseEvent, type ReactNode } from 'react'
3 const clsx = (...values: Array<string | false | undefined>) => values.filter(Boolean).join(' ')
4 import { ChevronDown as IconChevronDownOutline14 } from 'lucide-react'
5 import css from './DisclosureRow.styles'
6
7 /** Shared 24px disclosure chrome for compact flow rows. */
8 export interface DisclosureRowProps {
9 icon: ReactNode
10 title: string
11 open: boolean
12 expandable: boolean
13 onToggle: () => void
14 /** Makes the complete title row the disclosure target. */
15 expandOnRowClick?: boolean | undefined
16 /** Replaces the collapsed icon with a chevron while the row is hovered. */
17 previewChevron?: boolean | undefined
18 /** Keeps `collapsedContent` inline while open. */
19 keepContentWhenOpen?: boolean | undefined
20 collapsedContent?: ReactNode
21 children?: ReactNode
22 className?: string | undefined
23 rowClassName?: string | undefined
24 leadingClassName?: string | undefined
25 chevronClassName?: string | undefined
26 titleClassName?: string | undefined
27 }
28
29 /**
30 * Render one disclosure header and its controlled expanded content.
31 * @param props - Visual content, controlled state, and interaction policy.
32 * @returns the disclosure row.
33 */
34 export function DisclosureRow({
35 icon,
36 title,
37 open,
38 expandable,
39 onToggle,
40 expandOnRowClick = false,
41 previewChevron = expandable,
42 keepContentWhenOpen = false,
43 collapsedContent,
44 children,
45 className,
46 rowClassName,
47 leadingClassName,
48 chevronClassName,
49 titleClassName,
50 }: DisclosureRowProps) {
51 const rowExpands = expandable && expandOnRowClick
52 const toggleFromLeading = (event: MouseEvent<HTMLButtonElement>) => {
53 event.stopPropagation()
54 onToggle()
55 }
56 const toggleFromKeyboard = (event: KeyboardEvent<HTMLDivElement>) => {
57 if (!rowExpands || (event.key !== 'Enter' && event.key !== ' ')) return
58 event.preventDefault()
59 onToggle()
60 }
61 const collapsedLeading = previewChevron
62 ? (
63 <>
64 <span className={css.iconIdle}>{icon}</span>
65 <IconChevronDownOutline14 className={clsx(chevronClassName, css.chevronHover)} />
66 </>
67 )
68 : icon
69 const leading = open
70 ? <IconChevronDownOutline14 className={chevronClassName} />
71 : collapsedLeading
72
73 return (
74 <div className={clsx(css.root, className)} data-open={open || undefined}>
75 <div
76 className={clsx(css.row, rowClassName)}
77 data-disclosure-row
78 data-expandable={rowExpands || undefined}
79 role={rowExpands ? 'button' : undefined}
80 tabIndex={rowExpands ? 0 : undefined}
81 aria-expanded={rowExpands ? open : undefined}
82 onClick={rowExpands ? onToggle : undefined}
83 onKeyDown={rowExpands ? toggleFromKeyboard : undefined}
84 >
85 {expandable && !rowExpands ? (
86 <button
87 type="button"
88 className={clsx(css.leading, leadingClassName)}
89 aria-expanded={open}
90 onClick={toggleFromLeading}
91 >
92 {leading}
93 </button>
94 ) : (
95 <span className={clsx(css.leading, leadingClassName)}>
96 {leading}
97 </span>
98 )}
99 <span className={clsx(css.title, titleClassName)}>{title}</span>
100 {(keepContentWhenOpen || !open) && collapsedContent}
101 </div>
102 {open && children}
103 </div>
104 )
105 }
106
106 lines Plain Text