feat: лассо-выделение по кнопке, разнесение портов карт/устройств, удвоение мока
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
- Лассо (rubberband) отключено по умолчанию, включается кнопкой в тулбаре - Порты устройств с картами позиционируются ниже карт (absolute positioning) - Высота устройств с картами и портами: сумма вместо max - Мок данные удвоены: +4 сайта, +26 устройств, +6 карт, ~130 портов, ~30 линий Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@ -166,7 +166,7 @@ export function initGraph(
|
||||
new Selection({
|
||||
enabled: true,
|
||||
multiple: true,
|
||||
rubberband: true,
|
||||
rubberband: false,
|
||||
movable: true,
|
||||
showNodeSelectionBox: true,
|
||||
}),
|
||||
|
||||
@ -53,7 +53,10 @@ function getDeviceSize(
|
||||
}
|
||||
const portHeight = Math.max(portCount * 22, 60);
|
||||
const cardsHeight = cardCount > 0 ? cardCount * (CARD_HEIGHT + 6) + 8 : 0;
|
||||
const bodyHeight = Math.max(portHeight, cardsHeight);
|
||||
// When device has both cards and ports, stack them vertically to avoid overlap
|
||||
const bodyHeight = cardCount > 0 && portCount > 0
|
||||
? cardsHeight + portHeight
|
||||
: Math.max(portHeight, cardsHeight);
|
||||
return {
|
||||
width: DEVICE_MIN_WIDTH,
|
||||
height: Math.max(DEVICE_MIN_HEIGHT, DEVICE_HEADER_HEIGHT + bodyHeight + 10),
|
||||
@ -112,12 +115,42 @@ export function buildGraphData(
|
||||
const deviceCards = data.cards.filter((c) => c.deviceId === device.id && c.visible);
|
||||
const size = getDeviceSize(device.category, device.name, device.marking, devicePorts.length, deviceCards.length);
|
||||
|
||||
// When device has cards, position device-level ports below cards area
|
||||
const hasCards = deviceCards.length > 0;
|
||||
const cardsEndY = hasCards
|
||||
? DEVICE_HEADER_HEIGHT + deviceCards.length * (CARD_HEIGHT + 6) + 8
|
||||
: 0;
|
||||
|
||||
// Group device ports by resolved side for Y offset calculation
|
||||
const leftDevicePorts = devicePorts.filter(
|
||||
(p) => (portSideMap.get(p.id) ?? p.side) === 'left',
|
||||
);
|
||||
const rightDevicePorts = devicePorts.filter(
|
||||
(p) => (portSideMap.get(p.id) ?? p.side) === 'right',
|
||||
);
|
||||
|
||||
const portItems = devicePorts.map((port) => {
|
||||
const resolvedSide = portSideMap.get(port.id) ?? port.side;
|
||||
const label = port.slotName ? `${port.slotName}:${port.name}` : port.name;
|
||||
return createPortItem(port.id, resolvedSide, label, port.labelColor || undefined);
|
||||
|
||||
let portArgs: { x: number; y: number } | undefined;
|
||||
if (hasCards) {
|
||||
const sameSidePorts = resolvedSide === 'left' ? leftDevicePorts : rightDevicePorts;
|
||||
const indexInSide = sameSidePorts.indexOf(port);
|
||||
const portSpacing = 22;
|
||||
portArgs = {
|
||||
x: resolvedSide === 'left' ? 0 : size.width,
|
||||
y: cardsEndY + portSpacing * (indexInSide + 0.5),
|
||||
};
|
||||
}
|
||||
|
||||
return createPortItem(port.id, resolvedSide, label, port.labelColor || undefined, portArgs);
|
||||
});
|
||||
|
||||
// Use absolute positioning when device has cards to avoid overlap with card ports
|
||||
const leftPosition = hasCards ? 'absolute' : 'left';
|
||||
const rightPosition = hasCards ? 'absolute' : 'right';
|
||||
|
||||
const node: GraphNodeConfig = {
|
||||
id: device.id,
|
||||
shape,
|
||||
@ -141,7 +174,7 @@ export function buildGraphData(
|
||||
ports: {
|
||||
groups: {
|
||||
left: {
|
||||
position: 'left',
|
||||
position: leftPosition,
|
||||
attrs: {
|
||||
circle: {
|
||||
r: 6,
|
||||
@ -156,7 +189,7 @@ export function buildGraphData(
|
||||
},
|
||||
},
|
||||
right: {
|
||||
position: 'right',
|
||||
position: rightPosition,
|
||||
attrs: {
|
||||
circle: {
|
||||
r: 6,
|
||||
|
||||
@ -38,7 +38,10 @@ function getDeviceSize(device: Device, portCount: number, cardCount: number): {
|
||||
// Dynamic height based on port count + header + cards
|
||||
const portHeight = Math.max(portCount * 22, 60);
|
||||
const cardsHeight = cardCount > 0 ? cardCount * (CARD_HEIGHT + 6) + 8 : 0;
|
||||
const bodyHeight = Math.max(portHeight, cardsHeight);
|
||||
// When device has both cards and ports, stack them vertically to avoid overlap
|
||||
const bodyHeight = cardCount > 0 && portCount > 0
|
||||
? cardsHeight + portHeight
|
||||
: Math.max(portHeight, cardsHeight);
|
||||
return {
|
||||
width: DEVICE_MIN_WIDTH,
|
||||
height: Math.max(DEVICE_MIN_HEIGHT, DEVICE_HEADER_HEIGHT + bodyHeight + 10),
|
||||
|
||||
@ -44,10 +44,12 @@ export function createPortItem(
|
||||
side: 'left' | 'right',
|
||||
label: string,
|
||||
labelColor?: string,
|
||||
args?: { x: number; y: number },
|
||||
) {
|
||||
return {
|
||||
id: portId,
|
||||
group: side,
|
||||
args,
|
||||
attrs: {
|
||||
text: {
|
||||
text: label,
|
||||
|
||||
Reference in New Issue
Block a user