Skip to content

Commit

Permalink
Compromise solution (1) instead of draggable resizable node + selecte…
Browse files Browse the repository at this point in the history
…d edge button minor fix included (#113)

* 1) Node width expands for entire text visibility. 2) Made selected edge button responsive and to design. 3) Minor node look changes

* max node width decreased
  • Loading branch information
RajveerSinghAnand authored Nov 6, 2024
1 parent 14f5681 commit 8ea2397
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 44 deletions.
123 changes: 101 additions & 22 deletions client/src/components/UMLClassNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,22 @@ const UMLClassNode: React.FC<UMLNodeProps> = ({ data, id }) => {

const attributesRef = useRef<HTMLTextAreaElement>(null);
const methodsRef = useRef<HTMLTextAreaElement>(null);
const labelRef = useRef<HTMLInputElement>(null);

const [nodeWidth, setNodeWidth] = useState<number>(200); // Initial minimum width

// Function to measure the width of text using canvas
const measureTextWidth = (text: string, font: string): number => {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
if (!context) return 0;
context.font = font;
const metrics = context.measureText(text);
return metrics.width;
};

useEffect(() => {
// Adjust the height of text areas
if (attributesRef.current) {
attributesRef.current.style.height = 'auto';
attributesRef.current.style.height = `${attributesRef.current.scrollHeight}px`;
Expand All @@ -32,8 +46,47 @@ const UMLClassNode: React.FC<UMLNodeProps> = ({ data, id }) => {
methodsRef.current.style.height = 'auto';
methodsRef.current.style.height = `${methodsRef.current.scrollHeight}px`;
}
}, [attributes, methods]);

// Initialize maxWidth with the minimum width
let maxWidth = 150;

// Define font settings
const fontSize = 14; // Adjust font size if needed
const fontFamily = 'Arial, sans-serif';
const font = `${fontSize}px ${fontFamily}`;

// Measure width of the label
let labelWidth = 0;
if (labelRef.current) {
const labelText = labelRef.current.value;
labelWidth = measureTextWidth(labelText, font);
}

// Measure the maximum width required by attributes
let attributesMaxWidth = 0;
const attributesLines = attributes.split('\n');
attributesLines.forEach(line => {
const lineWidth = measureTextWidth(line, font);
attributesMaxWidth = Math.max(attributesMaxWidth, lineWidth);
});

// Measure the maximum width required by methods
let methodsMaxWidth = 0;
const methodsLines = methods.split('\n');
methodsLines.forEach(line => {
const lineWidth = measureTextWidth(line, font);
methodsMaxWidth = Math.max(methodsMaxWidth, lineWidth);
});

// Determine the maximum width required
maxWidth = Math.max(maxWidth, labelWidth, attributesMaxWidth, methodsMaxWidth);

// Add padding to the calculated width
const padding = 30; // Adjust padding as needed
setNodeWidth(maxWidth + padding);
}, [label, attributes, methods]);

// Handlers for input changes
const handleLabelChange = (e) => {
const newLabel = e.target.value;
setLabel(newLabel);
Expand All @@ -53,31 +106,41 @@ const UMLClassNode: React.FC<UMLNodeProps> = ({ data, id }) => {
};

return (
<div style={{
border: '1px solid black',
borderRadius: '5px',
width: '200px',
fontFamily: 'Arial, sans-serif',
position: 'relative',
backgroundColor: 'white',
}}>
<div style={{
backgroundColor: headerColor,
padding: '10px',
borderBottom: '1px solid black',
textAlign: 'center',
fontWeight: 'bold',
position: 'relative'
}}>
<div
style={{
border: '1px solid black',
borderRadius: '5px',
width: nodeWidth,
fontFamily: 'Arial, sans-serif',
position: 'relative',
backgroundColor: 'white',
}}
>
<div
style={{
backgroundColor: headerColor,
padding: '10px',
borderBottom: '1px solid black',
textAlign: 'center',
fontWeight: 'bold',
position: 'relative',
}}
>
<input
ref={labelRef}
value={label}
onChange={handleLabelChange}
placeholder="Class Name"
style={{
width: '100%',
border: 'none',
backgroundColor: 'transparent',
textAlign: 'center',
fontWeight: 'bold'
fontWeight: 'bold',
fontSize: '14px',
fontFamily: 'Arial, sans-serif',
whiteSpace: 'pre', // Prevent text wrapping
overflow: 'hidden', // Hide overflow
}}
/>
<button
Expand All @@ -95,22 +158,38 @@ const UMLClassNode: React.FC<UMLNodeProps> = ({ data, id }) => {
X
</button>
</div>
<div style={{ padding: '10px' }}>
<div style={{ padding: '7px' }}>
<textarea
ref={attributesRef}
value={attributes}
onChange={handleAttributesChange}
placeholder="Attributes"
style={{ width: '100%', resize: 'none', overflow: 'hidden' }}
wrap="off"
style={{
width: '100%',
resize: 'none',
overflow: 'hidden',
whiteSpace: 'pre',
fontSize: '14px',
fontFamily: 'Arial, sans-serif',
}}
/>
</div>
<div style={{ padding: '10px' }}>
<div style={{ padding: '7px' }}>
<textarea
ref={methodsRef}
value={methods}
onChange={handleMethodsChange}
placeholder="Methods"
style={{ width: '100%', resize: 'none', overflow: 'hidden' }}
wrap="off"
style={{
width: '100%',
resize: 'none',
overflow: 'hidden',
whiteSpace: 'pre',
fontSize: '14px',
fontFamily: 'Arial, sans-serif',
}}
/>
</div>
{!data.isPreview && (
Expand Down
105 changes: 84 additions & 21 deletions client/src/components/UMLInterfaceNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,64 @@ interface UMLNodeData {
interface UMLNodeProps extends NodeProps<UMLNodeData> {}

const UMLInterfaceNode: React.FC<UMLNodeProps> = ({ data, id }) => {
const [label, setLabel] = useState<string>(data.label || 'Interface');
const [label, setLabel] = useState<string>(data.label || 'InterfaceName');
const [methods, setMethods] = useState<string>(data.methods?.join('\n') || '');
const headerColor = data.color || '#FFEE93';

const methodsRef = useRef<HTMLTextAreaElement>(null);
const labelRef = useRef<HTMLInputElement>(null);

const [nodeWidth, setNodeWidth] = useState<number>(200); // Initial minimum width

// Function to measure the width of text using canvas
const measureTextWidth = (text: string, font: string): number => {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
if (!context) return 0;
context.font = font;
const metrics = context.measureText(text);
return metrics.width;
};

useEffect(() => {
// Adjust the height of the methods text area
if (methodsRef.current) {
methodsRef.current.style.height = 'auto';
methodsRef.current.style.height = `${methodsRef.current.scrollHeight}px`;
}
}, [methods]);

// Initialize maxWidth with the minimum width
let maxWidth = 150;

// Define font settings
const fontSize = 14; // Adjust font size if needed
const fontFamily = 'Arial, sans-serif';
const font = `${fontSize}px ${fontFamily}`;

// Measure width of the label
let labelWidth = 0;
if (labelRef.current) {
const labelText = labelRef.current.value;
labelWidth = measureTextWidth(labelText, font);
}

// Measure the maximum width required by methods
let methodsMaxWidth = 0;
const methodsLines = methods.split('\n');
methodsLines.forEach(line => {
const lineWidth = measureTextWidth(line, font);
methodsMaxWidth = Math.max(methodsMaxWidth, lineWidth);
});

// Determine the maximum width required
maxWidth = Math.max(maxWidth, labelWidth, methodsMaxWidth);

// Add padding to the calculated width
const padding = 30; // Adjust padding as needed
setNodeWidth(maxWidth + padding);
}, [label, methods]);

// Handlers for input changes
const handleLabelChange = (e) => {
const newLabel = e.target.value;
setLabel(newLabel);
Expand All @@ -40,31 +85,41 @@ const UMLInterfaceNode: React.FC<UMLNodeProps> = ({ data, id }) => {
};

return (
<div style={{
border: '1px solid black',
borderRadius: '5px',
width: '200px',
fontFamily: 'Arial, sans-serif',
position: 'relative',
backgroundColor: 'white',
}}>
<div style={{
backgroundColor: headerColor,
padding: '10px',
borderBottom: '1px solid black',
textAlign: 'center',
fontWeight: 'bold',
position: 'relative'
}}>
<div
style={{
border: '1px solid black',
borderRadius: '5px',
width: nodeWidth,
fontFamily: 'Arial, sans-serif',
position: 'relative',
backgroundColor: 'white',
}}
>
<div
style={{
backgroundColor: headerColor,
padding: '10px',
borderBottom: '1px solid black',
textAlign: 'center',
fontWeight: 'bold',
position: 'relative',
}}
>
<input
ref={labelRef}
value={label}
onChange={handleLabelChange}
placeholder="Interface Name"
style={{
width: '100%',
border: 'none',
backgroundColor: 'transparent',
textAlign: 'center',
fontWeight: 'bold'
fontWeight: 'bold',
fontSize: '14px',
fontFamily: 'Arial, sans-serif',
whiteSpace: 'pre', // Prevent text wrapping
overflow: 'hidden', // Hide overflow
}}
/>
<button
Expand All @@ -82,13 +137,21 @@ const UMLInterfaceNode: React.FC<UMLNodeProps> = ({ data, id }) => {
X
</button>
</div>
<div style={{ padding: '10px' }}>
<div style={{ padding: '7px' }}>
<textarea
ref={methodsRef}
value={methods}
onChange={handleMethodsChange}
placeholder="Methods"
style={{ width: '100%', resize: 'none', overflow: 'hidden' }}
wrap="off"
style={{
width: '100%',
resize: 'none',
overflow: 'hidden',
whiteSpace: 'pre',
fontSize: '14px',
fontFamily: 'Arial, sans-serif',
}}
/>
</div>
{!data.isPreview && (
Expand Down
40 changes: 39 additions & 1 deletion client/src/pages/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,29 @@ const UMLDiagramEditor = ({ problemId }) => {
setShowInstructions(false);
};

const onEdgeClick = useCallback((event, edge) => {
event.stopPropagation(); // Prevent the click from bubbling up to the document
setSelectedEdge(edge.id);
}, []);

useEffect(() => {
const handleKeyPress = () => {
setSelectedEdge(null);
};

const handleClick = () => {
setSelectedEdge(null);
};

document.addEventListener("keydown", handleKeyPress);
document.addEventListener("click", handleClick);

return () => {
document.removeEventListener("keydown", handleKeyPress);
document.removeEventListener("click", handleClick);
};
}, []);

return (
<div
style={{ width: "100%", height: "100%", position: "relative" }}
Expand Down Expand Up @@ -478,6 +501,21 @@ const UMLDiagramEditor = ({ problemId }) => {
onClick={() => removeEdge(selectedEdge)}
className="delete-button"
disabled={!selectedEdge}
style={{
backgroundColor: selectedEdge ? "#ff4d4d" : "#ccc", // Red when active, gray when disabled
color: "#fff",
border: "none",
padding: "10px",
borderRadius: "5px",
cursor: selectedEdge ? "pointer" : "not-allowed",
transition: "background-color 0.3s",
}}
onMouseEnter={(e) => {
if (selectedEdge) e.target.style.backgroundColor = "#ff1a1a"; // Darker red on hover
}}
onMouseLeave={(e) => {
if (selectedEdge) e.target.style.backgroundColor = "#ff4d4d"; // Original red
}}
>
Delete Selected Edge
</button>
Expand Down Expand Up @@ -547,7 +585,7 @@ const UMLDiagramEditor = ({ problemId }) => {
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
onEdgeClick={(event, edge) => setSelectedEdge(edge.id)}
onEdgeClick={onEdgeClick}
nodeTypes={nodeTypes}
edgeTypes={{
Inheritance: UMLEdge,
Expand Down

0 comments on commit 8ea2397

Please sign in to comment.