core/panel/ui/CodePreview.tsx

105 lines
3.3 KiB
TypeScript
Raw Normal View History

2025-07-02 19:30:21 +00:00
import { createMemo, JSX, Show } from 'solid-js'
2025-06-30 18:25:26 +00:00
import 'prismjs/themes/prism-tomorrow.css'
import styles from '../styles/CodePreview.module.css'
2025-07-02 19:30:21 +00:00
import { detectLanguage, formatCode, highlightCode } from '../utils/codeHelpers'
2025-06-30 18:25:26 +00:00
2025-07-02 19:30:21 +00:00
interface CodePreviewProps extends JSX.HTMLAttributes<HTMLDivElement> {
2025-06-30 18:25:26 +00:00
content: string
language?: string
maxHeight?: string
2025-07-02 19:30:21 +00:00
showLineNumbers?: boolean
autoFormat?: boolean
editable?: boolean
onEdit?: () => void
2025-06-30 18:25:26 +00:00
}
2025-07-02 19:30:21 +00:00
/**
* Компонент для отображения кода с подсветкой синтаксиса
*
* @example
* ```tsx
* <CodePreview
* content='{"key": "value"}'
* language="json"
* showLineNumbers={true}
* editable={true}
* onEdit={() => setIsEditing(true)}
* />
* ```
*/
2025-06-30 18:25:26 +00:00
const CodePreview = (props: CodePreviewProps) => {
2025-07-02 19:30:21 +00:00
// Реактивные вычисления
const language = createMemo(() => props.language || detectLanguage(props.content))
const formattedContent = createMemo(() =>
props.autoFormat ? formatCode(props.content, language()) : props.content
)
const highlightedCode = createMemo(() => highlightCode(formattedContent(), language()))
const isEmpty = createMemo(() => !props.content?.trim())
2025-06-30 18:25:26 +00:00
return (
2025-07-02 19:30:21 +00:00
<div
class={`${styles.codePreview} ${props.editable ? styles.codePreviewContainer : ''} ${props.class || ''}`}
style={`max-height: ${props.maxHeight || '500px'}; ${props.style || ''}`}
onClick={props.editable ? props.onEdit : undefined}
role={props.editable ? 'button' : 'presentation'}
tabindex={props.editable ? 0 : undefined}
onKeyDown={(e) => {
if (props.editable && (e.key === 'Enter' || e.key === ' ')) {
e.preventDefault()
props.onEdit?.()
}
}}
2025-06-30 18:25:26 +00:00
>
2025-07-02 19:30:21 +00:00
<div class={styles.codeContainer}>
{/* Область кода */}
<div class={styles.codeArea}>
<Show
when={!isEmpty()}
fallback={
<div class={`${styles.placeholder} ${props.editable ? styles.placeholderClickable : ''}`}>
{props.editable ? 'Нажмите для редактирования...' : 'Нет содержимого'}
</div>
}
>
<pre class={styles.codePreviewContent}>
<code class={`language-${language()}`} innerHTML={highlightedCode()} />
</pre>
</Show>
</div>
</div>
{/* Индикаторы */}
<div class={styles.controlsLeft}>
<span class={styles.languageBadge}>{language()}</span>
<Show when={props.editable}>
<div class={styles.statusIndicator}>
<div class={`${styles.statusDot} ${styles.idle}`} />
<span>Только чтение</span>
</div>
</Show>
</div>
{/* Кнопка редактирования */}
<Show when={props.editable && !isEmpty()}>
<div class={styles.controlsRight}>
<button
class={styles.editButton}
onClick={(e) => {
e.stopPropagation()
props.onEdit?.()
}}
title="Редактировать код"
>
Редактировать
</button>
</div>
</Show>
</div>
2025-06-30 18:25:26 +00:00
)
}
export default CodePreview
export { detectLanguage, formatCode }