Files
claude-code/src/components/design-system/ListItem.tsx
claude-code-best 5b1a52b8e0 更新大量 tsx 原始文件; 已经迁移 login panel; 部分 (#121)
* style(B1-1): 格式化 ink/buddy/cli/context/screens/tasks/services/keybindings/state (43 files)

纯格式化:移除分号、React Compiler import、import 多行展开。
修复了 Box.tsx 和 ScrollBox.tsx 中无效的 global.d.ts import。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-2): 格式化 commands (79 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-3): 格式化 components/messages,permissions,mcp,sandbox,shell (104 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-4): 格式化 components/PromptInput,FeedbackSurvey,tasks,agents,skills,design-system,wizard (73 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-5): 格式化 components其余 + hooks + tools (232 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-6): 格式化 main/entrypoints/utils/moreright (21 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 更新 README,新增 Run.ps1/TODO.md,删除 V6.md

- README.md: 大幅重写,更详细版本历史和配置示例
- Run.ps1: 新增 Windows 启动脚本
- TODO.md: 新增包完成清单
- V6.md: 删除(架构重构规划已不适用)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: 修复以前的问题

* fix: 修复 login 面板的问题

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:24:27 +08:00

189 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import figures from 'figures'
import type { ReactNode } from 'react'
import React from 'react'
import { useDeclaredCursor } from '../../ink/hooks/use-declared-cursor.js'
import { Box, Text } from '../../ink.js'
type ListItemProps = {
/**
* Whether this item is currently focused (keyboard selection).
* Shows the pointer indicator () when true.
*/
isFocused: boolean
/**
* Whether this item is selected (chosen/checked).
* Shows the checkmark indicator (✓) when true.
* @default false
*/
isSelected?: boolean
/**
* The content to display for this item.
*/
children: ReactNode
/**
* Optional description text displayed below the main content.
*/
description?: string
/**
* Show a down arrow indicator instead of pointer (for scroll hints).
* Only applies when not focused.
*/
showScrollDown?: boolean
/**
* Show an up arrow indicator instead of pointer (for scroll hints).
* Only applies when not focused.
*/
showScrollUp?: boolean
/**
* Whether to apply automatic styling to the children based on focus/selection state.
* - When true (default): children are wrapped in Text with state-based colors
* - When false: children are rendered as-is, allowing custom styling
* @default true
*/
styled?: boolean
/**
* Whether this item is disabled. Disabled items show dimmed text and no indicators.
* @default false
*/
disabled?: boolean
/**
* Whether this ListItem should declare the terminal cursor position.
* Set false when a child (e.g. BaseTextInput) declares its own cursor.
* @default true
*/
declareCursor?: boolean
}
/**
* A list item component for selection UIs (dropdowns, multi-selects, menus).
*
* Handles the common pattern of:
* - Pointer indicator () for focused items
* - Checkmark indicator (✓) for selected items
* - Scroll indicators (↓↑) for truncated lists
* - Color states for focus/selection
*
* @example
* // Basic usage in a selection list
* {options.map((option, i) => (
* <ListItem
* key={option.id}
* isFocused={focusIndex === i}
* isSelected={selectedId === option.id}
* >
* {option.label}
* </ListItem>
* ))}
*
* @example
* // With scroll indicators
* <ListItem isFocused={false} showScrollUp>First visible item</ListItem>
* ...
* <ListItem isFocused={false} showScrollDown>Last visible item</ListItem>
*
* @example
* // With description
* <ListItem isFocused isSelected={false} description="Secondary text here">
* Primary text
* </ListItem>
*
* @example
* // Custom children styling (styled=false)
* <ListItem isFocused styled={false}>
* <Text color="claude">Custom styled content</Text>
* </ListItem>
*/
export function ListItem({
isFocused,
isSelected = false,
children,
description,
showScrollDown,
showScrollUp,
styled = true,
disabled = false,
declareCursor,
}: ListItemProps): React.ReactNode {
// Determine which indicator to show
function renderIndicator(): ReactNode {
if (disabled) {
return <Text> </Text>
}
if (isFocused) {
return <Text color="suggestion">{figures.pointer}</Text>
}
if (showScrollDown) {
return <Text dimColor>{figures.arrowDown}</Text>
}
if (showScrollUp) {
return <Text dimColor>{figures.arrowUp}</Text>
}
return <Text> </Text>
}
// Determine text color based on state
function getTextColor(): 'success' | 'suggestion' | 'inactive' | undefined {
if (disabled) {
return 'inactive'
}
if (!styled) {
return undefined
}
if (isSelected) {
return 'success'
}
if (isFocused) {
return 'suggestion'
}
return undefined
}
const textColor = getTextColor()
// Park the native terminal cursor on the pointer indicator so screen
// readers / magnifiers track the focused item. (0,0) is the top-left of
// this Box, where the pointer renders.
const cursorRef = useDeclaredCursor({
line: 0,
column: 0,
active: isFocused && !disabled && declareCursor !== false,
})
return (
<Box ref={cursorRef} flexDirection="column">
<Box flexDirection="row" gap={1}>
{renderIndicator()}
{styled ? (
<Text color={textColor} dimColor={disabled}>
{children}
</Text>
) : (
children
)}
{isSelected && !disabled && <Text color="success">{figures.tick}</Text>}
</Box>
{description && (
<Box paddingLeft={2}>
<Text color="inactive">{description}</Text>
</Box>
)}
</Box>
)
}