feat(workflow): Monaco editor для proposed schema (C)
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
import { ArrowsClockwiseIcon, CheckSquareIcon } from '@phosphor-icons/react'
|
||||
import type { DictionaryDetail, SchemaDraft } from '@/api/client'
|
||||
import { useCreateSchemaDraft, useUpdateSchemaDraft } from '@/api/mutations'
|
||||
import { JsonEditor } from '@/components/workflow/JsonEditor'
|
||||
|
||||
type Props = {
|
||||
open: boolean
|
||||
@@ -247,18 +248,18 @@ export function CreateSchemaDraftModal({ open, onClose, detail, editDraft, onCre
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/* Custom monospace textarea — для full Monaco editor'а Phase 1d.
|
||||
Высота min-h-[400px] чтобы средняя schema ~30-50 полей помещалась
|
||||
без скролла. */}
|
||||
<textarea
|
||||
{/* Monaco editor — lazy-loaded (~1.2MB gzip). Syntax highlight,
|
||||
bracket matching, fold/unfold по структуре JSON. Высота 420px —
|
||||
средняя schema (~30-50 полей) помещается без вертикального
|
||||
скролла. */}
|
||||
<JsonEditor
|
||||
value={json}
|
||||
onChange={(e) => {
|
||||
setJson(e.target.value)
|
||||
onChange={(v) => {
|
||||
setJson(v)
|
||||
if (parseError) setParseError(null)
|
||||
}}
|
||||
spellCheck={false}
|
||||
disabled={pending}
|
||||
className="w-full min-h-[400px] max-h-[60vh] text-mono text-cell rounded-md border border-line bg-[#0c0a23] text-[#f0eaff] p-3 focus:outline-none focus:ring-1 focus:ring-accent"
|
||||
height={420}
|
||||
/>
|
||||
{parseError && (
|
||||
<Alert variant="error" className="mt-2">
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import { lazy, Suspense } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { LoadingBlock } from '@/ui'
|
||||
|
||||
const MonacoEditor = lazy(() => import('@monaco-editor/react').then((m) => ({ default: m.Editor })))
|
||||
|
||||
type Props = {
|
||||
value: string
|
||||
onChange: (v: string) => void
|
||||
disabled?: boolean
|
||||
/** Pixel height; default 400. Используется minHeight CSS — editor сам
|
||||
* растягивается до этого значения. */
|
||||
height?: number | string
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy-loaded Monaco editor для JSON. Bundle ~1.2MB gzip — lazy через
|
||||
* dynamic import. Suspense fallback показывает loading block.
|
||||
*
|
||||
* <p>Theme подставляется fixed 'vs-dark' чтобы match'илось с нашим
|
||||
* space-theme background (#0c0a23). Monaco поддерживает custom themes
|
||||
* через monaco.editor.defineTheme — это можно добавить отдельно если
|
||||
* захочется precise color match.
|
||||
*
|
||||
* <p>Options:
|
||||
* <ul>
|
||||
* <li>JSON language с syntax highlighting + bracket matching.</li>
|
||||
* <li>Auto-format on paste/type, format on save (через
|
||||
* monaco.languages.json.jsonDefaults).</li>
|
||||
* <li>Minimap скрыт — narrow modal viewport, minimap занимает место.</li>
|
||||
* <li>Read-only режим через disabled prop.</li>
|
||||
* </ul>
|
||||
*/
|
||||
export function JsonEditor({ value, onChange, disabled = false, height = 400 }: Props) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<Suspense fallback={<LoadingBlock size="md" label={t('loading')} />}>
|
||||
<div
|
||||
className="rounded-md border border-line overflow-hidden bg-[#0c0a23]"
|
||||
style={{ height: typeof height === 'number' ? `${height}px` : height }}
|
||||
>
|
||||
<MonacoEditor
|
||||
height="100%"
|
||||
defaultLanguage="json"
|
||||
language="json"
|
||||
value={value}
|
||||
onChange={(v) => onChange(v ?? '')}
|
||||
theme="vs-dark"
|
||||
options={{
|
||||
readOnly: disabled,
|
||||
minimap: { enabled: false },
|
||||
fontSize: 13,
|
||||
fontFamily:
|
||||
'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
|
||||
lineNumbers: 'on',
|
||||
renderLineHighlight: 'line',
|
||||
scrollBeyondLastLine: false,
|
||||
wordWrap: 'on',
|
||||
tabSize: 2,
|
||||
insertSpaces: true,
|
||||
formatOnPaste: true,
|
||||
formatOnType: true,
|
||||
bracketPairColorization: { enabled: true },
|
||||
// Monaco autoclosing для JSON: brackets/quotes на open auto-close,
|
||||
// помогает писать большие schema'ы без потери indentation.
|
||||
autoClosingBrackets: 'always',
|
||||
autoClosingQuotes: 'always',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user