Приводил к стилу consta UI
This commit is contained in:
+135
-30
@@ -1,26 +1,50 @@
|
||||
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 { ColorPicker, hexModel } from '@consta/uikit/ColorPicker';
|
||||
import { Text } from '@consta/uikit/Text';
|
||||
import { DEFAULT_NODE_COLOR } from './nodes';
|
||||
import {
|
||||
DEFAULT_CAPTION_TEXT_COLOR,
|
||||
DEFAULT_NODE_BACKGROUND,
|
||||
DEFAULT_NODE_COLOR,
|
||||
DEFAULT_NODE_FONT_SIZE,
|
||||
DEFAULT_NODE_TEXT_COLOR,
|
||||
DEFAULT_TASK_BORDER_RADIUS,
|
||||
} 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',
|
||||
// Every gradient defined in palette.css, across its three "for diagrams /
|
||||
// flowcharts / semantic highlights" sections. Consta's ColorPicker `palette`
|
||||
// prop can't render these — it goes through hexModel, which only understands
|
||||
// flat colors — so these render as our own swatch row instead.
|
||||
const GRADIENT_PRESETS: { name: string; label: string }[] = [
|
||||
{ name: 'navy', label: 'Тёмно-синий' },
|
||||
{ name: 'azure', label: 'Лазурный' },
|
||||
{ name: 'sky', label: 'Небесный' },
|
||||
{ name: 'teal-dark', label: 'Тёмная бирюза' },
|
||||
{ name: 'teal-light', label: 'Светлая бирюза' },
|
||||
{ name: 'navy-dark', label: 'Глубокий синий' },
|
||||
{ name: 'ice', label: 'Лёд' },
|
||||
{ name: 'frost', label: 'Иней' },
|
||||
{ name: 'purple', label: 'Пурпурный' },
|
||||
{ name: 'red', label: 'Красный' },
|
||||
{ name: 'orange', label: 'Оранжевый' },
|
||||
{ name: 'olive', label: 'Оливковый' },
|
||||
{ name: 'green', label: 'Зелёный' },
|
||||
{ name: 'green-dark', label: 'Тёмно-зелёный' },
|
||||
{ name: 'blue', label: 'Синий' },
|
||||
{ name: 'cyan', label: 'Циан' },
|
||||
{ name: 'violet', label: 'Виолет' },
|
||||
{ name: 'lavender', label: 'Лавандовый' },
|
||||
{ name: 'pink', label: 'Розовый' },
|
||||
];
|
||||
|
||||
// "Цвета текста" from palette.css.
|
||||
const TEXT_COLOR_PRESETS: { hex: string; label: string }[] = [
|
||||
{ hex: '#0079C2', label: 'Синий' },
|
||||
{ hex: '#525A64', label: 'Тёмный' },
|
||||
{ hex: '#929AA4', label: 'Серый' },
|
||||
{ hex: '#FFFFFF', label: 'Белый' },
|
||||
];
|
||||
|
||||
const TYPE_LABELS: Record<NodeKind, string> = {
|
||||
@@ -62,7 +86,18 @@ export function NodeInspector({
|
||||
}: 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>;
|
||||
const colorButtonRef = useRef<HTMLDivElement>(null) as RefObject<HTMLDivElement>;
|
||||
// data.color can be a `var(--gradient-x)` reference from the preset row —
|
||||
// hexModel tries to parse that as a hex color and produces NaN, so the
|
||||
// hex-only ColorControl/ColorPicker always need a real flat hex fallback.
|
||||
const flatColor = data.color && !data.color.startsWith('var(') ? data.color : DEFAULT_NODE_COLOR[nodeType];
|
||||
|
||||
const [textColorPickerOpen, setTextColorPickerOpen] = useState(false);
|
||||
const textColorButtonRef = useRef<HTMLDivElement>(null) as RefObject<HTMLDivElement>;
|
||||
// Text sits directly on the colored fill for tasks (needs a light default)
|
||||
// but on the plain canvas for every other shape's caption (needs a dark one).
|
||||
const defaultTextColor = nodeType === 'task' ? DEFAULT_NODE_TEXT_COLOR : DEFAULT_CAPTION_TEXT_COLOR;
|
||||
const currentTextColor = data.textColor ?? defaultTextColor;
|
||||
|
||||
if (mode === 'view') {
|
||||
const hasMeta = Boolean(data.responsible || data.duration);
|
||||
@@ -138,28 +173,83 @@ export function NodeInspector({
|
||||
<Text size="xs" view="secondary">
|
||||
Цвет фона
|
||||
</Text>
|
||||
<ColorControl
|
||||
markerRef={colorMarkerRef}
|
||||
model={hexModel}
|
||||
value={data.color ?? DEFAULT_NODE_COLOR[nodeType]}
|
||||
onChange={(value) => onChange({ color: value })}
|
||||
size="s"
|
||||
onClick={() => setColorPickerOpen(true)}
|
||||
/>
|
||||
<div className="node-inspector__presets">
|
||||
{GRADIENT_PRESETS.map(({ name, label }) => {
|
||||
const value = `var(--gradient-${name})`;
|
||||
const isActive = (data.color ?? DEFAULT_NODE_BACKGROUND[nodeType]) === value;
|
||||
return (
|
||||
<button
|
||||
key={name}
|
||||
type="button"
|
||||
title={label}
|
||||
aria-label={label}
|
||||
className={`node-inspector__preset${isActive ? ' node-inspector__preset--active' : ''}`}
|
||||
style={{ background: value }}
|
||||
onClick={() => onChange({ color: value })}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div ref={colorButtonRef} className="node-inspector__color-trigger">
|
||||
<Button size="s" view="secondary" label="Настроить цвет" onClick={() => setColorPickerOpen(true)} />
|
||||
</div>
|
||||
{colorPickerOpen && (
|
||||
<ColorPicker
|
||||
model={hexModel}
|
||||
value={data.color ?? DEFAULT_NODE_COLOR[nodeType]}
|
||||
value={flatColor}
|
||||
onChange={(value) => onChange({ color: value })}
|
||||
anchorRef={colorMarkerRef}
|
||||
anchorRef={colorButtonRef}
|
||||
open={colorPickerOpen}
|
||||
onOpen={setColorPickerOpen}
|
||||
palette={NODE_COLOR_PRESETS}
|
||||
paletteTitle="Пресеты"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="node-inspector__field">
|
||||
<Text size="xs" view="secondary">
|
||||
Цвет текста
|
||||
</Text>
|
||||
<div className="node-inspector__presets">
|
||||
{TEXT_COLOR_PRESETS.map(({ hex, label: presetLabel }) => (
|
||||
<button
|
||||
key={hex}
|
||||
type="button"
|
||||
title={presetLabel}
|
||||
aria-label={presetLabel}
|
||||
className={`node-inspector__preset${currentTextColor === hex ? ' node-inspector__preset--active' : ''}`}
|
||||
style={{ background: hex }}
|
||||
onClick={() => onChange({ textColor: hex })}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div ref={textColorButtonRef} className="node-inspector__color-trigger">
|
||||
<Button size="s" view="secondary" label="Настроить цвет" onClick={() => setTextColorPickerOpen(true)} />
|
||||
</div>
|
||||
{textColorPickerOpen && (
|
||||
<ColorPicker
|
||||
model={hexModel}
|
||||
value={currentTextColor}
|
||||
onChange={(value) => onChange({ textColor: value })}
|
||||
anchorRef={textColorButtonRef}
|
||||
open={textColorPickerOpen}
|
||||
onOpen={setTextColorPickerOpen}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<TextField
|
||||
label="Размер текста"
|
||||
type="number"
|
||||
size="s"
|
||||
min={8}
|
||||
max={32}
|
||||
value={String(data.fontSize ?? DEFAULT_NODE_FONT_SIZE)}
|
||||
onChange={(value) => {
|
||||
const n = Number(value);
|
||||
if (value && !Number.isNaN(n)) onChange({ fontSize: n });
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Название"
|
||||
type="textarea"
|
||||
@@ -192,6 +282,21 @@ export function NodeInspector({
|
||||
onChange={(value) => onChange({ duration: value ?? '' })}
|
||||
/>
|
||||
|
||||
{nodeType === 'task' && (
|
||||
<TextField
|
||||
label="Скругление углов"
|
||||
type="number"
|
||||
size="s"
|
||||
min={0}
|
||||
max={25}
|
||||
value={String(data.borderRadius ?? DEFAULT_TASK_BORDER_RADIUS)}
|
||||
onChange={(value) => {
|
||||
const n = Number(value);
|
||||
if (value && !Number.isNaN(n)) onChange({ borderRadius: n });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button size="s" view="ghost" label="Войти в под-процесс →" onClick={() => onEnterSubProcess(nodeId)} />
|
||||
<Text size="xs" view="secondary" className="node-inspector__hint">
|
||||
{childCount > 0
|
||||
|
||||
Reference in New Issue
Block a user