@ichigo_san/graphing
Version:
A lightweight UML-style diagram editor built with React Flow and Tailwind CSS
242 lines (236 loc) • 14.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _lucideReact = require("lucide-react");
var _technicalDetailsParser = require("../utils/technicalDetailsParser");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
const TechnicalDetailsPanel = ({
selectedElement,
onClose,
isOpen,
position
}) => {
var _selectedElement$data, _selectedElement$data2, _selectedElement$data3;
const [isVisible, setIsVisible] = (0, _react.useState)(false);
const [isDragging, setIsDragging] = (0, _react.useState)(false);
const [dragOffset, setDragOffset] = (0, _react.useState)({
x: 0,
y: 0
});
const [manualPosition, setManualPosition] = (0, _react.useState)(null);
const panelRef = (0, _react.useRef)(null);
const positionTimeoutRef = (0, _react.useRef)(null);
(0, _react.useEffect)(() => {
if (isOpen && selectedElement) {
setIsVisible(true);
setManualPosition(null); // Reset manual position on new selection
} else {
setIsVisible(false);
}
}, [isOpen, selectedElement]);
// Debounced position update to reduce ResizeObserver calls
(0, _react.useEffect)(() => {
if (position && !manualPosition) {
if (positionTimeoutRef.current) {
clearTimeout(positionTimeoutRef.current);
}
positionTimeoutRef.current = setTimeout(() => {
setManualPosition(position);
}, 25); // Reduced delay for more responsive movement
}
return () => {
if (positionTimeoutRef.current) {
clearTimeout(positionTimeoutRef.current);
}
};
}, [position, manualPosition]);
// Immediate position update for better responsiveness
(0, _react.useEffect)(() => {
if (position && !isDragging) {
// Update position immediately if not dragging
setManualPosition(position);
}
}, [position, isDragging]);
// Dragging functionality
const handleMouseDown = (0, _react.useCallback)(e => {
if (e.target.closest('button')) return; // Don't drag if clicking buttons
setIsDragging(true);
const rect = panelRef.current.getBoundingClientRect();
setDragOffset({
x: e.clientX - rect.left,
y: e.clientY - rect.top
});
e.preventDefault();
}, []);
const handleMouseMove = (0, _react.useCallback)(e => {
if (!isDragging) return;
const newX = e.clientX - dragOffset.x;
const newY = e.clientY - dragOffset.y;
// Keep panel within viewport bounds
const maxX = window.innerWidth - 320;
const maxY = window.innerHeight - 400;
setManualPosition({
x: Math.max(10, Math.min(newX, maxX)),
y: Math.max(10, Math.min(newY, maxY))
});
}, [isDragging, dragOffset]);
const handleMouseUp = (0, _react.useCallback)(() => {
setIsDragging(false);
}, []);
(0, _react.useEffect)(() => {
if (isDragging) {
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
return () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
}
}, [isDragging, handleMouseMove, handleMouseUp]);
if (!isOpen || !selectedElement) return null;
const isNode = selectedElement.type === 'node';
const technicalDetails = isNode ? (0, _technicalDetailsParser.extractTechnicalDetails)(selectedElement) : (0, _technicalDetailsParser.extractConnectionTechnicalDetails)(selectedElement);
const elementLabel = ((_selectedElement$data = selectedElement.data) === null || _selectedElement$data === void 0 ? void 0 : _selectedElement$data.label) || selectedElement.label || 'Unknown Element';
// Get icon based on element type and technical details
const getElementIcon = () => {
if (isNode) {
if (technicalDetails.infrastructure.includes('Kubernetes')) return /*#__PURE__*/_react.default.createElement(_lucideReact.Server, {
className: "w-4 h-4"
});
if (technicalDetails.protocol.includes('HTTP')) return /*#__PURE__*/_react.default.createElement(_lucideReact.Network, {
className: "w-4 h-4"
});
if (technicalDetails.monitoring.includes('Prometheus')) return /*#__PURE__*/_react.default.createElement(_lucideReact.Activity, {
className: "w-4 h-4"
});
return /*#__PURE__*/_react.default.createElement(_lucideReact.Database, {
className: "w-4 h-4"
});
} else {
return /*#__PURE__*/_react.default.createElement(_lucideReact.Network, {
className: "w-4 h-4"
});
}
};
// Use manual position if set, otherwise use the position prop directly
const panelPos = manualPosition || position || {
x: 100,
y: 100
};
return /*#__PURE__*/_react.default.createElement("div", {
ref: panelRef,
className: `fixed z-40 transition-all duration-150 ease-out ${isVisible ? 'opacity-100 scale-100' : 'opacity-0 scale-95'} ${isDragging ? 'cursor-grabbing' : 'cursor-grab'}`,
style: {
left: panelPos.x,
top: panelPos.y,
transform: isVisible ? 'translate(0, 0)' : 'translate(10px, -10px)',
userSelect: isDragging ? 'none' : 'auto',
willChange: 'transform, opacity' // Optimize for animations
},
onMouseDown: handleMouseDown
}, /*#__PURE__*/_react.default.createElement("div", {
className: "w-80 bg-white/95 dark:bg-gray-900/95 backdrop-blur-md border border-gray-200/50 dark:border-gray-700/50 rounded-xl shadow-xl"
}, /*#__PURE__*/_react.default.createElement("div", {
className: "flex items-center justify-between p-4 border-b border-gray-100 dark:border-gray-800"
}, /*#__PURE__*/_react.default.createElement("div", {
className: "flex items-center gap-3"
}, /*#__PURE__*/_react.default.createElement("div", {
className: "p-1.5 bg-blue-100 dark:bg-blue-900/30 rounded-lg"
}, getElementIcon()), /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("h3", {
className: "text-sm font-semibold text-gray-900 dark:text-white"
}, elementLabel), /*#__PURE__*/_react.default.createElement("p", {
className: "text-xs text-gray-500 dark:text-gray-400"
}, "Technical Details"))), /*#__PURE__*/_react.default.createElement("button", {
onClick: onClose,
className: "p-1 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
}, /*#__PURE__*/_react.default.createElement(_lucideReact.X, {
size: 16,
className: "text-gray-400"
}))), /*#__PURE__*/_react.default.createElement("div", {
className: "p-4 space-y-3 max-h-64 overflow-y-auto"
}, technicalDetails.protocol !== 'N/A' && /*#__PURE__*/_react.default.createElement("div", {
className: "flex items-center gap-2 p-2 bg-blue-50 dark:bg-blue-900/20 rounded-lg"
}, /*#__PURE__*/_react.default.createElement(_lucideReact.Network, {
className: "w-3 h-3 text-blue-500"
}), /*#__PURE__*/_react.default.createElement("span", {
className: "text-xs font-medium text-blue-700 dark:text-blue-300"
}, technicalDetails.protocol)), (technicalDetails.performance.latency || technicalDetails.performance.throughput || technicalDetails.performance.timeout) && /*#__PURE__*/_react.default.createElement("div", {
className: "flex items-center gap-2 p-2 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg"
}, /*#__PURE__*/_react.default.createElement(_lucideReact.Zap, {
className: "w-3 h-3 text-yellow-500"
}), /*#__PURE__*/_react.default.createElement("div", {
className: "flex flex-wrap gap-1"
}, technicalDetails.performance.latency && /*#__PURE__*/_react.default.createElement("span", {
className: "text-xs bg-yellow-100 dark:bg-yellow-800/30 text-yellow-800 dark:text-yellow-200 px-2 py-0.5 rounded-full"
}, technicalDetails.performance.latency), technicalDetails.performance.throughput && /*#__PURE__*/_react.default.createElement("span", {
className: "text-xs bg-purple-100 dark:bg-purple-800/30 text-purple-800 dark:text-purple-200 px-2 py-0.5 rounded-full"
}, technicalDetails.performance.throughput, " req/s"), technicalDetails.performance.timeout && /*#__PURE__*/_react.default.createElement("span", {
className: "text-xs bg-orange-100 dark:bg-orange-800/30 text-orange-800 dark:text-orange-200 px-2 py-0.5 rounded-full"
}, technicalDetails.performance.timeout, "s"))), technicalDetails.security !== 'N/A' && /*#__PURE__*/_react.default.createElement("div", {
className: "flex items-center gap-2 p-2 bg-green-50 dark:bg-green-900/20 rounded-lg"
}, /*#__PURE__*/_react.default.createElement(_lucideReact.Shield, {
className: "w-3 h-3 text-green-500"
}), /*#__PURE__*/_react.default.createElement("div", {
className: "flex flex-wrap gap-1"
}, technicalDetails.security.split(', ').map((sec, index) => /*#__PURE__*/_react.default.createElement("span", {
key: index,
className: "text-xs bg-green-100 dark:bg-green-800/30 text-green-800 dark:text-green-200 px-2 py-0.5 rounded-full"
}, sec.trim())))), isNode && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, technicalDetails.scaling !== 'N/A' && /*#__PURE__*/_react.default.createElement("div", {
className: "flex items-center gap-2 p-2 bg-indigo-50 dark:bg-indigo-900/20 rounded-lg"
}, /*#__PURE__*/_react.default.createElement(_lucideReact.Activity, {
className: "w-3 h-3 text-indigo-500"
}), /*#__PURE__*/_react.default.createElement("div", {
className: "flex flex-wrap gap-1"
}, technicalDetails.scaling.split(', ').map((scale, index) => /*#__PURE__*/_react.default.createElement("span", {
key: index,
className: "text-xs bg-indigo-100 dark:bg-indigo-800/30 text-indigo-800 dark:text-indigo-200 px-2 py-0.5 rounded-full"
}, scale.trim())))), technicalDetails.infrastructure !== 'N/A' && /*#__PURE__*/_react.default.createElement("div", {
className: "flex items-center gap-2 p-2 bg-cyan-50 dark:bg-cyan-900/20 rounded-lg"
}, /*#__PURE__*/_react.default.createElement(_lucideReact.Server, {
className: "w-3 h-3 text-cyan-500"
}), /*#__PURE__*/_react.default.createElement("div", {
className: "flex flex-wrap gap-1"
}, technicalDetails.infrastructure.split(', ').map((infra, index) => /*#__PURE__*/_react.default.createElement("span", {
key: index,
className: "text-xs bg-cyan-100 dark:bg-cyan-800/30 text-cyan-800 dark:text-cyan-200 px-2 py-0.5 rounded-full"
}, infra.trim())))), technicalDetails.monitoring !== 'N/A' && /*#__PURE__*/_react.default.createElement("div", {
className: "flex items-center gap-2 p-2 bg-pink-50 dark:bg-pink-900/20 rounded-lg"
}, /*#__PURE__*/_react.default.createElement(_lucideReact.Activity, {
className: "w-3 h-3 text-pink-500"
}), /*#__PURE__*/_react.default.createElement("div", {
className: "flex flex-wrap gap-1"
}, technicalDetails.monitoring.split(', ').map((monitor, index) => /*#__PURE__*/_react.default.createElement("span", {
key: index,
className: "text-xs bg-pink-100 dark:bg-pink-800/30 text-pink-800 dark:text-pink-200 px-2 py-0.5 rounded-full"
}, monitor.trim()))))), !isNode && technicalDetails.failureHandling !== 'N/A' && /*#__PURE__*/_react.default.createElement("div", {
className: "flex items-center gap-2 p-2 bg-red-50 dark:bg-red-900/20 rounded-lg"
}, /*#__PURE__*/_react.default.createElement(_lucideReact.AlertTriangle, {
className: "w-3 h-3 text-red-500"
}), /*#__PURE__*/_react.default.createElement("div", {
className: "flex flex-wrap gap-1"
}, technicalDetails.failureHandling.split(', ').map((failure, index) => /*#__PURE__*/_react.default.createElement("span", {
key: index,
className: "text-xs bg-red-100 dark:bg-red-800/30 text-red-800 dark:text-red-200 px-2 py-0.5 rounded-full"
}, failure.trim())))), ((_selectedElement$data2 = selectedElement.data) === null || _selectedElement$data2 === void 0 ? void 0 : _selectedElement$data2.description) && /*#__PURE__*/_react.default.createElement("div", {
className: "flex items-start gap-2 p-2 bg-gray-50 dark:bg-gray-800 rounded-lg"
}, /*#__PURE__*/_react.default.createElement(_lucideReact.Info, {
className: "w-3 h-3 text-gray-500 mt-0.5 flex-shrink-0"
}), /*#__PURE__*/_react.default.createElement("p", {
className: "text-xs text-gray-700 dark:text-gray-300 leading-relaxed"
}, selectedElement.data.description)), technicalDetails.protocol === 'N/A' && technicalDetails.security === 'N/A' && !technicalDetails.performance.latency && !technicalDetails.performance.throughput && !technicalDetails.performance.timeout && technicalDetails.scaling === 'N/A' && technicalDetails.infrastructure === 'N/A' && technicalDetails.monitoring === 'N/A' && technicalDetails.failureHandling === 'N/A' && !((_selectedElement$data3 = selectedElement.data) !== null && _selectedElement$data3 !== void 0 && _selectedElement$data3.description) && /*#__PURE__*/_react.default.createElement("div", {
className: "text-center py-4"
}, /*#__PURE__*/_react.default.createElement("p", {
className: "text-xs text-gray-500 dark:text-gray-400"
}, "No technical details available")))), /*#__PURE__*/_react.default.createElement("div", {
className: "absolute w-3 h-3 bg-white/95 dark:bg-gray-900/95 border-l border-t border-gray-200/50 dark:border-gray-700/50 transform rotate-45",
style: {
left: panelPos.x < window.innerWidth / 2 ? '-6px' : 'auto',
right: panelPos.x >= window.innerWidth / 2 ? '-6px' : 'auto',
top: '20px'
}
}));
};
var _default = exports.default = TechnicalDetailsPanel;