core/panel/ui/CodePreview.tsx
Untone 82111ed0f6
All checks were successful
Deploy on push / deploy (push) Successful in 7s
Squashed new RBAC
2025-07-02 22:30:21 +03:00

105 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 { createMemo, JSX, Show } from 'solid-js'
import 'prismjs/themes/prism-tomorrow.css'
import styles from '../styles/CodePreview.module.css'
import { detectLanguage, formatCode, highlightCode } from '../utils/codeHelpers'
interface CodePreviewProps extends JSX.HTMLAttributes<HTMLDivElement> {
content: string
language?: string
maxHeight?: string
showLineNumbers?: boolean
autoFormat?: boolean
editable?: boolean
onEdit?: () => void
}
/**
* Компонент для отображения кода с подсветкой синтаксиса
*
* @example
* ```tsx
* <CodePreview
* content='{"key": "value"}'
* language="json"
* showLineNumbers={true}
* editable={true}
* onEdit={() => setIsEditing(true)}
* />
* ```
*/
const CodePreview = (props: CodePreviewProps) => {
// Реактивные вычисления
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())
return (
<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?.()
}
}}
>
<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>
)
}
export default CodePreview
export { detectLanguage, formatCode }