Расширил возможности и поправил границы фигур

This commit is contained in:
NikitaKalitov
2026-08-28 09:22:12 +03:00
parent 762f43dad4
commit 6a4f463bf2
4 changed files with 271 additions and 83 deletions
+75 -34
View File
@@ -3,6 +3,8 @@ import { Button } from "@consta/uikit/Button";
import { TextField } from "@consta/uikit/TextField";
import { ColorPicker, hexModel } from "@consta/uikit/ColorPicker";
import { Text } from "@consta/uikit/Text";
import { IconCancel } from "@consta/icons/IconCancel";
import { TRANSPARENT_COLOR, toPickerHex } from "./colors";
import {
DEFAULT_CAPTION_TEXT_COLOR,
DEFAULT_NODE_BACKGROUND,
@@ -16,7 +18,12 @@ import {
TASK_SUBTYPE_ICON,
TASK_SUBTYPE_LABEL,
} from "./nodes";
import type { DiagramNodeData, EventSubtype, NodeKind, TaskSubtype } from "./types";
import type {
DiagramNodeData,
EventSubtype,
NodeKind,
TaskSubtype,
} from "./types";
const TASK_SUBTYPES: TaskSubtype[] = [
"none",
@@ -77,7 +84,11 @@ const TYPE_LABELS: Record<NodeKind, string> = {
dataObject: "Документ (Data Object)",
};
const EVENT_KINDS = new Set<NodeKind>(["startEvent", "intermediateEvent", "endEvent"]);
const EVENT_KINDS = new Set<NodeKind>([
"startEvent",
"intermediateEvent",
"endEvent",
]);
interface NodeInspectorProps {
nodeId: string;
@@ -111,13 +122,9 @@ export function NodeInspector({
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];
// data.color бывает не-hex: `var(--gradient-x)` от пресета-градиента или
// `transparent` — hexModel такое не разбирает, нужен hex-дефолт.
const flatColor = toPickerHex(data.color, DEFAULT_NODE_COLOR[nodeType]);
const [textColorPickerOpen, setTextColorPickerOpen] = useState(false);
const textColorButtonRef = useRef<HTMLDivElement>(
@@ -132,11 +139,13 @@ export function NodeInspector({
if (mode === "view") {
const hasMeta = Boolean(data.responsible || data.duration);
const subtypeLabel =
nodeType === "task"
? TASK_SUBTYPE_LABEL[(data.subtype as TaskSubtype | undefined) ?? "none"]
: EVENT_KINDS.has(nodeType)
? EVENT_SUBTYPE_LABEL[(data.subtype as EventSubtype | undefined) ?? "none"]
: undefined;
nodeType === "task" ?
TASK_SUBTYPE_LABEL[(data.subtype as TaskSubtype | undefined) ?? "none"]
: EVENT_KINDS.has(nodeType) ?
EVENT_SUBTYPE_LABEL[
(data.subtype as EventSubtype | undefined) ?? "none"
]
: undefined;
return (
<div className="node-inspector">
<div className="node-inspector__header">
@@ -245,7 +254,9 @@ export function NodeInspector({
className={`node-inspector__subtype${isActive ? " node-inspector__subtype--active" : ""}`}
onClick={() => onChange({ subtype })}
>
{Icon ? <Icon size="s" /> : <Text size="xs">—</Text>}
{Icon ?
<Icon size="s" />
: <Text size="xs">—</Text>}
</button>
);
})}
@@ -259,24 +270,26 @@ export function NodeInspector({
Тип триггера
</Text>
<div className="node-inspector__presets">
{EVENT_SUBTYPES_BY_KIND[nodeType as "startEvent" | "intermediateEvent" | "endEvent"].map(
(subtype: EventSubtype) => {
const Icon = EVENT_SUBTYPE_ICON[subtype];
const isActive = (data.subtype ?? "none") === subtype;
return (
<button
key={subtype}
type="button"
title={EVENT_SUBTYPE_LABEL[subtype]}
aria-label={EVENT_SUBTYPE_LABEL[subtype]}
className={`node-inspector__subtype${isActive ? " node-inspector__subtype--active" : ""}`}
onClick={() => onChange({ subtype })}
>
{Icon ? <Icon size="s" /> : <Text size="xs">—</Text>}
</button>
);
},
)}
{EVENT_SUBTYPES_BY_KIND[
nodeType as "startEvent" | "intermediateEvent" | "endEvent"
].map((subtype: EventSubtype) => {
const Icon = EVENT_SUBTYPE_ICON[subtype];
const isActive = (data.subtype ?? "none") === subtype;
return (
<button
key={subtype}
type="button"
title={EVENT_SUBTYPE_LABEL[subtype]}
aria-label={EVENT_SUBTYPE_LABEL[subtype]}
className={`node-inspector__subtype${isActive ? " node-inspector__subtype--active" : ""}`}
onClick={() => onChange({ subtype })}
>
{Icon ?
<Icon size="s" />
: <Text size="xs">—</Text>}
</button>
);
})}
</div>
</div>
)}
@@ -302,6 +315,19 @@ export function NodeInspector({
/>
);
})}
<button
type="button"
title="Прозрачный"
aria-label="Прозрачный"
className={`node-inspector__preset node-inspector__preset--none${
data.color === TRANSPARENT_COLOR ?
" node-inspector__preset--active"
: ""
}`}
onClick={() => onChange({ color: TRANSPARENT_COLOR })}
>
<IconCancel size="xs" />
</button>
</div>
<div ref={colorButtonRef} className="node-inspector__color-trigger">
<Button
@@ -319,6 +345,7 @@ export function NodeInspector({
anchorRef={colorButtonRef}
open={colorPickerOpen}
onOpen={setColorPickerOpen}
style={{ zIndex: 100 }}
/>
)}
</div>
@@ -339,6 +366,19 @@ export function NodeInspector({
onClick={() => onChange({ textColor: hex })}
/>
))}
<button
type="button"
title="Прозрачный"
aria-label="Прозрачный"
className={`node-inspector__preset node-inspector__preset--none${
currentTextColor === TRANSPARENT_COLOR ?
" node-inspector__preset--active"
: ""
}`}
onClick={() => onChange({ textColor: TRANSPARENT_COLOR })}
>
<IconCancel size="xs" />
</button>
</div>
<div ref={textColorButtonRef} className="node-inspector__color-trigger">
<Button
@@ -351,11 +391,12 @@ export function NodeInspector({
{textColorPickerOpen && (
<ColorPicker
model={hexModel}
value={currentTextColor}
value={toPickerHex(currentTextColor, defaultTextColor)}
onChange={(value) => onChange({ textColor: value })}
anchorRef={textColorButtonRef}
open={textColorPickerOpen}
onOpen={setTextColorPickerOpen}
style={{ zIndex: 100 }}
/>
)}
</div>