@ichigo_san/graphing
Version:
A lightweight UML-style diagram editor built with React Flow and Tailwind CSS
472 lines (469 loc) • 16.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactflow = require("reactflow");
var _gradientUtils = require("../utils/gradientUtils");
var _shadowUtils = require("../utils/shadowUtils");
var _transformUtils = require("../utils/transformUtils");
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); }
// Draw.io-style connection point styles
const drawioHandleStyle = {
background: '#3b82f6',
border: '2px solid #ffffff',
width: 14,
height: 14,
borderRadius: '50%',
zIndex: 10,
cursor: 'crosshair',
transition: 'all 0.2s ease',
opacity: 1,
transform: 'scale(1)',
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.2)'
};
const drawioHandleHoverStyle = {
...drawioHandleStyle,
opacity: 1,
transform: 'scale(1.3)',
boxShadow: '0 0 0 4px rgba(59, 130, 246, 0.4)',
background: '#2563eb'
};
const drawioHandleActiveStyle = {
...drawioHandleHoverStyle,
background: '#10b981',
boxShadow: '0 0 0 4px rgba(16, 185, 129, 0.4)'
};
const hiddenTargetHandleStyle = {
opacity: 0,
pointerEvents: 'none',
width: 1,
height: 1,
background: 'transparent',
border: 'none'
};
const ContainerNode = ({
data,
id,
selected,
isConnectable
}) => {
const [isEditing, setIsEditing] = (0, _react.useState)(false);
const [label, setLabel] = (0, _react.useState)(data.label || 'Container');
const [hoveredHandle, setHoveredHandle] = (0, _react.useState)(null);
const [activeHandle, setActiveHandle] = (0, _react.useState)(null);
(0, _react.useEffect)(() => {
setLabel(data.label || 'Container');
}, [data.label]);
const inputRef = (0, _react.useRef)(null);
const handleDoubleClick = (0, _react.useCallback)(e => {
e.stopPropagation();
setIsEditing(true);
}, []);
const handleLabelSubmit = (0, _react.useCallback)(() => {
setIsEditing(false);
if (data.onLabelChange && label !== data.label) {
data.onLabelChange(id, label);
}
}, [id, label, data]);
const handleKeyDown = (0, _react.useCallback)(e => {
if (e.key === 'Enter') {
handleLabelSubmit();
}
if (e.key === 'Escape') {
setLabel(data.label);
setIsEditing(false);
}
}, [handleLabelSubmit, data.label]);
// Enhanced connection point handlers
const handleConnectionPointMouseEnter = (0, _react.useCallback)(position => {
setHoveredHandle(position);
}, []);
const handleConnectionPointMouseLeave = (0, _react.useCallback)(() => {
setHoveredHandle(null);
}, []);
const getHandleStyle = (0, _react.useCallback)(position => {
const baseStyle = {
...drawioHandleStyle
};
// Always show handles more prominently when node is selected or hovered
if (selected || hoveredHandle === 'node') {
baseStyle.opacity = 1;
baseStyle.transform = 'scale(1.1)';
}
if (activeHandle === position) {
return {
...baseStyle,
...drawioHandleActiveStyle
};
}
if (hoveredHandle === position) {
return {
...baseStyle,
...drawioHandleHoverStyle
};
}
return baseStyle;
}, [hoveredHandle, activeHandle, selected]);
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_reactflow.NodeResizer, {
isVisible: selected,
minWidth: 200,
minHeight: 150,
handleClassName: "w-2.5 h-2.5 bg-gray-500 rounded-full border-2 border-white",
lineClassName: "border-2 border-dashed border-indigo-500 opacity-60",
nodeWidth: data.width,
nodeHeight: data.height
}), /*#__PURE__*/_react.default.createElement("div", {
style: {
background: (0, _gradientUtils.ensureBackwardCompatibility)(data.background || data.contentColor || data.color || '#ffffff'),
border: `${data.borderWidth || 2}px ${data.borderStyle || 'solid'} ${data.borderColor || '#ddd'}`,
borderRadius: `${data.borderRadius || 8}px`,
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
cursor: 'move',
transition: 'all 0.2s ease',
boxShadow: selected ? `${(0, _shadowUtils.ensureShadowCompatibility)(data.boxShadow || 'none')}, 0 0 0 2px #2196F3` : (0, _shadowUtils.ensureShadowCompatibility)(data.boxShadow || 'none'),
boxSizing: 'border-box',
position: 'relative',
fontSize: `${data.fontSize || 14}px`,
opacity: data.opacity ?? 1,
...(0, _transformUtils.createTransformStyles)(data, 'container')
},
onMouseEnter: () => setHoveredHandle('node'),
onMouseLeave: () => setHoveredHandle(null)
}, /*#__PURE__*/_react.default.createElement("div", {
className: "border-b border-gray-300 dark:border-gray-600 text-gray-800 dark:text-gray-100 flex items-center gap-2",
style: {
background: (0, _gradientUtils.ensureBackwardCompatibility)(data.headerColor || '#f9f9f9'),
height: `${data.headerHeight || 32}px`,
padding: `${data.padding || 8}px`,
borderRadius: `${(data.borderRadius || 8) - 1}px ${(data.borderRadius || 8) - 1}px 0 0`,
fontSize: `${data.headerFontSize || 14}px`,
minHeight: '24px',
alignItems: 'center'
},
onDoubleClick: handleDoubleClick
}, data.icon && /*#__PURE__*/_react.default.createElement("span", {
style: {
fontSize: '14px'
}
}, data.icon), isEditing ? /*#__PURE__*/_react.default.createElement("input", {
ref: inputRef,
value: label,
onChange: e => setLabel(e.target.value),
onBlur: handleLabelSubmit,
onKeyDown: handleKeyDown,
autoFocus: true,
style: {
fontSize: `${data.headerFontSize || 14}px`,
border: 'none',
borderBottom: '2px solid #2196F3',
outline: 'none',
background: 'transparent',
padding: '4px',
width: '100%',
color: data.textColor || '#000000'
}
}) : /*#__PURE__*/_react.default.createElement("span", {
style: {
fontSize: `${data.headerFontSize || 14}px`,
fontWeight: 'bold',
color: data.textColor || '#000000'
}
}, label)), /*#__PURE__*/_react.default.createElement("div", {
style: {
flex: 1,
padding: `${data.padding || 8}px`,
overflow: 'hidden',
position: 'relative',
fontSize: `${data.fontSize || 14}px`
}
}, data.description && /*#__PURE__*/_react.default.createElement("div", {
style: {
fontSize: `${Math.max((data.fontSize || 14) - 2, 10)}px`,
color: data.textColor || '#666',
marginBottom: '8px',
lineHeight: '1.4'
}
}, data.description)), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "source",
position: _reactflow.Position.Top,
id: "top-left-source",
style: {
...getHandleStyle('top'),
left: '25%'
},
isConnectable: isConnectable,
className: "drawio-connection-point",
onMouseEnter: () => handleConnectionPointMouseEnter('top'),
onMouseLeave: handleConnectionPointMouseLeave
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "source",
position: _reactflow.Position.Top,
id: "top-center-source",
style: {
...getHandleStyle('top'),
left: '50%'
},
isConnectable: isConnectable,
className: "drawio-connection-point",
onMouseEnter: () => handleConnectionPointMouseEnter('top'),
onMouseLeave: handleConnectionPointMouseLeave
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "source",
position: _reactflow.Position.Top,
id: "top-right-source",
style: {
...getHandleStyle('top'),
left: '75%'
},
isConnectable: isConnectable,
className: "drawio-connection-point",
onMouseEnter: () => handleConnectionPointMouseEnter('top'),
onMouseLeave: handleConnectionPointMouseLeave
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "source",
position: _reactflow.Position.Right,
id: "right-top-source",
style: {
...getHandleStyle('right'),
top: '25%'
},
isConnectable: isConnectable,
className: "drawio-connection-point",
onMouseEnter: () => handleConnectionPointMouseEnter('right'),
onMouseLeave: handleConnectionPointMouseLeave
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "source",
position: _reactflow.Position.Right,
id: "right-center-source",
style: {
...getHandleStyle('right'),
top: '50%'
},
isConnectable: isConnectable,
className: "drawio-connection-point",
onMouseEnter: () => handleConnectionPointMouseEnter('right'),
onMouseLeave: handleConnectionPointMouseLeave
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "source",
position: _reactflow.Position.Right,
id: "right-bottom-source",
style: {
...getHandleStyle('right'),
top: '75%'
},
isConnectable: isConnectable,
className: "drawio-connection-point",
onMouseEnter: () => handleConnectionPointMouseEnter('right'),
onMouseLeave: handleConnectionPointMouseLeave
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "source",
position: _reactflow.Position.Bottom,
id: "bottom-left-source",
style: {
...getHandleStyle('bottom'),
left: '25%'
},
isConnectable: isConnectable,
className: "drawio-connection-point",
onMouseEnter: () => handleConnectionPointMouseEnter('bottom'),
onMouseLeave: handleConnectionPointMouseLeave
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "source",
position: _reactflow.Position.Bottom,
id: "bottom-center-source",
style: {
...getHandleStyle('bottom'),
left: '50%'
},
isConnectable: isConnectable,
className: "drawio-connection-point",
onMouseEnter: () => handleConnectionPointMouseEnter('bottom'),
onMouseLeave: handleConnectionPointMouseLeave
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "source",
position: _reactflow.Position.Bottom,
id: "bottom-right-source",
style: {
...getHandleStyle('bottom'),
left: '75%'
},
isConnectable: isConnectable,
className: "drawio-connection-point",
onMouseEnter: () => handleConnectionPointMouseEnter('bottom'),
onMouseLeave: handleConnectionPointMouseLeave
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "source",
position: _reactflow.Position.Left,
id: "left-top-source",
style: {
...getHandleStyle('left'),
top: '25%'
},
isConnectable: isConnectable,
className: "drawio-connection-point",
onMouseEnter: () => handleConnectionPointMouseEnter('left'),
onMouseLeave: handleConnectionPointMouseLeave
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "source",
position: _reactflow.Position.Left,
id: "left-center-source",
style: {
...getHandleStyle('left'),
top: '50%'
},
isConnectable: isConnectable,
className: "drawio-connection-point",
onMouseEnter: () => handleConnectionPointMouseEnter('left'),
onMouseLeave: handleConnectionPointMouseLeave
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "source",
position: _reactflow.Position.Left,
id: "left-bottom-source",
style: {
...getHandleStyle('left'),
top: '75%'
},
isConnectable: isConnectable,
className: "drawio-connection-point",
onMouseEnter: () => handleConnectionPointMouseEnter('left'),
onMouseLeave: handleConnectionPointMouseLeave
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "target",
position: _reactflow.Position.Top,
id: "top-left-target",
style: hiddenTargetHandleStyle,
isConnectable: isConnectable
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "target",
position: _reactflow.Position.Top,
id: "top-center-target",
style: hiddenTargetHandleStyle,
isConnectable: isConnectable
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "target",
position: _reactflow.Position.Top,
id: "top-right-target",
style: hiddenTargetHandleStyle,
isConnectable: isConnectable
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "target",
position: _reactflow.Position.Right,
id: "right-top-target",
style: hiddenTargetHandleStyle,
isConnectable: isConnectable
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "target",
position: _reactflow.Position.Right,
id: "right-center-target",
style: hiddenTargetHandleStyle,
isConnectable: isConnectable
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "target",
position: _reactflow.Position.Right,
id: "right-bottom-target",
style: hiddenTargetHandleStyle,
isConnectable: isConnectable
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "target",
position: _reactflow.Position.Bottom,
id: "bottom-left-target",
style: hiddenTargetHandleStyle,
isConnectable: isConnectable
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "target",
position: _reactflow.Position.Bottom,
id: "bottom-center-target",
style: hiddenTargetHandleStyle,
isConnectable: isConnectable
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "target",
position: _reactflow.Position.Bottom,
id: "bottom-right-target",
style: hiddenTargetHandleStyle,
isConnectable: isConnectable
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "target",
position: _reactflow.Position.Left,
id: "left-top-target",
style: hiddenTargetHandleStyle,
isConnectable: isConnectable
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "target",
position: _reactflow.Position.Left,
id: "left-center-target",
style: hiddenTargetHandleStyle,
isConnectable: isConnectable
}), /*#__PURE__*/_react.default.createElement(_reactflow.Handle, {
type: "target",
position: _reactflow.Position.Left,
id: "left-bottom-target",
style: hiddenTargetHandleStyle,
isConnectable: isConnectable
}), hoveredHandle === 'node' && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", {
style: {
position: 'absolute',
top: '-6px',
left: '50%',
transform: 'translateX(-50%)',
width: '12px',
height: '12px',
background: '#3b82f6',
border: '2px solid #ffffff',
borderRadius: '50%',
opacity: 0.6,
pointerEvents: 'none',
zIndex: 5
}
}), /*#__PURE__*/_react.default.createElement("div", {
style: {
position: 'absolute',
right: '-6px',
top: '50%',
transform: 'translateY(-50%)',
width: '12px',
height: '12px',
background: '#3b82f6',
border: '2px solid #ffffff',
borderRadius: '50%',
opacity: 0.6,
pointerEvents: 'none',
zIndex: 5
}
}), /*#__PURE__*/_react.default.createElement("div", {
style: {
position: 'absolute',
bottom: '-6px',
left: '50%',
transform: 'translateX(-50%)',
width: '12px',
height: '12px',
background: '#3b82f6',
border: '2px solid #ffffff',
borderRadius: '50%',
opacity: 0.6,
pointerEvents: 'none',
zIndex: 5
}
}), /*#__PURE__*/_react.default.createElement("div", {
style: {
position: 'absolute',
left: '-6px',
top: '50%',
transform: 'translateY(-50%)',
width: '12px',
height: '12px',
background: '#3b82f6',
border: '2px solid #ffffff',
borderRadius: '50%',
opacity: 0.6,
pointerEvents: 'none',
zIndex: 5
}
}))));
};
var _default = exports.default = ContainerNode;