Начал переводить на consta UI

This commit is contained in:
NikitaKalitov
2026-07-20 16:42:34 +03:00
parent d823b3df47
commit 52e28e8dc6
10 changed files with 687 additions and 257 deletions
+107 -63
View File
@@ -1,6 +1,28 @@
import { useRef, useState, type RefObject } from 'react';
import { Button } from '@consta/uikit/Button';
import { TextField } from '@consta/uikit/TextField';
import { ColorControl, ColorPicker, hexModel } from '@consta/uikit/ColorPicker';
import { Text } from '@consta/uikit/Text';
import { DEFAULT_NODE_COLOR } from './nodes';
import type { DiagramNodeData, NodeKind } from './types';
// "Дополнительные цвета" from palette.css — the section the palette itself
// marks as meant for single-color accents (as opposed to the gradients used
// for shape defaults, which a flat-hex picker can't represent).
const NODE_COLOR_PRESETS = [
'#B5309A',
'#EC5665',
'#FFAB66',
'#C2DC5B',
'#32CA88',
'#22926D',
'#0EBAD5',
'#4EE7F4',
'#9152E6',
'#9D9AF7',
'#E88FDB',
];
const TYPE_LABELS: Record<NodeKind, string> = {
task: 'Задача',
startEvent: 'Начальное событие',
@@ -38,17 +60,29 @@ export function NodeInspector({
onBringToFront,
onSendToBack,
}: NodeInspectorProps) {
const [colorPickerOpen, setColorPickerOpen] = useState(false);
// Consta's ref props still use the pre-React-19 non-nullable RefObject shape.
const colorMarkerRef = useRef<HTMLButtonElement>(null) as RefObject<HTMLButtonElement>;
if (mode === 'view') {
const hasMeta = Boolean(data.responsible || data.duration);
return (
<div className="node-inspector">
<div className="node-inspector__header">
<span className="node-inspector__type">{TYPE_LABELS[nodeType]}</span>
<Text size="s" weight="semibold">
{TYPE_LABELS[nodeType]}
</Text>
</div>
<h3 className="node-inspector__label">{data.label}</h3>
<Text size="l" className="node-inspector__label">
{data.label}
</Text>
{data.description && <p className="node-inspector__description">{data.description}</p>}
{data.description && (
<Text size="s" view="secondary" className="node-inspector__description">
{data.description}
</Text>
)}
{hasMeta && (
<dl className="node-inspector__meta">
@@ -68,9 +102,12 @@ export function NodeInspector({
)}
{childCount > 0 && (
<button type="button" className="node-inspector__action" onClick={() => onEnterSubProcess(nodeId)}>
Открыть вложенный процесс →
</button>
<Button
size="s"
view="ghost"
label="Открыть вложенный процесс →"
onClick={() => onEnterSubProcess(nodeId)}
/>
)}
</div>
);
@@ -79,81 +116,88 @@ export function NodeInspector({
return (
<div className="node-inspector">
<div className="node-inspector__header">
<span className="node-inspector__type">{TYPE_LABELS[nodeType]}</span>
<code className="node-inspector__id">{nodeId}</code>
<Text size="s" weight="semibold">
{TYPE_LABELS[nodeType]}
</Text>
<Text size="xs" view="secondary" font="mono">
{nodeId}
</Text>
</div>
<div className="node-inspector__toolbar">
<button type="button" onClick={onDuplicate}>
Дублировать
</button>
<button type="button" onClick={onDelete}>
Удалить
</button>
<Button size="s" view="secondary" label="Дублировать" onClick={onDuplicate} />
<Button size="s" view="secondary" label="Удалить" onClick={onDelete} />
</div>
<div className="node-inspector__toolbar">
<button type="button" onClick={onBringToFront}>
На передний план
</button>
<button type="button" onClick={onSendToBack}>
На задний план
</button>
<Button size="s" view="secondary" label="На передний план" onClick={onBringToFront} />
<Button size="s" view="secondary" label="На задний план" onClick={onSendToBack} />
</div>
<label className="node-inspector__field node-inspector__field--color">
Цвет фона
<input
type="color"
<div className="node-inspector__field">
<Text size="xs" view="secondary">
Цвет фона
</Text>
<ColorControl
markerRef={colorMarkerRef}
model={hexModel}
value={data.color ?? DEFAULT_NODE_COLOR[nodeType]}
onChange={(e) => onChange({ color: e.target.value })}
onChange={(value) => onChange({ color: value })}
size="s"
onClick={() => setColorPickerOpen(true)}
/>
</label>
{colorPickerOpen && (
<ColorPicker
model={hexModel}
value={data.color ?? DEFAULT_NODE_COLOR[nodeType]}
onChange={(value) => onChange({ color: value })}
anchorRef={colorMarkerRef}
open={colorPickerOpen}
onOpen={setColorPickerOpen}
palette={NODE_COLOR_PRESETS}
paletteTitle="Пресеты"
/>
)}
</div>
<label className="node-inspector__field">
Название
<textarea
rows={2}
value={data.label}
onChange={(e) => onChange({ label: e.target.value })}
/>
</label>
<TextField
label="Название"
type="textarea"
rows={2}
size="s"
value={data.label}
onChange={(value) => onChange({ label: value ?? '' })}
/>
<label className="node-inspector__field">
Описание
<textarea
rows={3}
value={data.description ?? ''}
onChange={(e) => onChange({ description: e.target.value })}
/>
</label>
<TextField
label="Описание"
type="textarea"
rows={3}
size="s"
value={data.description ?? ''}
onChange={(value) => onChange({ description: value ?? '' })}
/>
<label className="node-inspector__field">
Ответственный
<input
type="text"
value={data.responsible ?? ''}
onChange={(e) => onChange({ responsible: e.target.value })}
/>
</label>
<TextField
label="Ответственный"
size="s"
value={data.responsible ?? ''}
onChange={(value) => onChange({ responsible: value ?? '' })}
/>
<label className="node-inspector__field">
Длительность
<input
type="text"
value={data.duration ?? ''}
onChange={(e) => onChange({ duration: e.target.value })}
/>
</label>
<TextField
label="Длительность"
size="s"
value={data.duration ?? ''}
onChange={(value) => onChange({ duration: value ?? '' })}
/>
<button type="button" className="node-inspector__action" onClick={() => onEnterSubProcess(nodeId)}>
Войти в под-процесс →
</button>
<p className="node-inspector__hint">
<Button size="s" view="ghost" label="Войти в под-процесс →" onClick={() => onEnterSubProcess(nodeId)} />
<Text size="xs" view="secondary" className="node-inspector__hint">
{childCount > 0
? `Внутри уже ${childCount} узл(ов).`
: 'Пока пусто — зайдя внутрь, можно добавить узлы детального разбора этого шага.'}
</p>
</Text>
</div>
);
}