Hone logo
Hone
Problems

Interactive Diagram Editor in React with TypeScript

This challenge tasks you with building a basic, interactive diagram editor using React and TypeScript. The editor should allow users to create, move, and delete simple shapes (circles and rectangles) on a canvas. This is a common requirement in many applications, from workflow design tools to data visualization platforms, and provides a good exercise in state management, event handling, and basic UI design.

Problem Description

You are to create a React component that renders a diagram editor. The editor should:

  1. Shape Creation: Allow users to add circles and rectangles to the canvas. Clicking a "Add Circle" or "Add Rectangle" button should create a new shape at the mouse click coordinates.
  2. Shape Rendering: Display the created shapes on the canvas. Each shape should be visually distinct (e.g., different colors or outlines).
  3. Shape Movement: Allow users to drag and drop shapes around the canvas. Clicking and dragging a shape should update its position in real-time.
  4. Shape Deletion: Provide a mechanism (e.g., a delete button on each shape or a context menu) to remove shapes from the canvas.
  5. State Management: Maintain the state of the shapes (position, type, size) within the React component.
  6. Canvas Boundaries: Shapes should not be draggable outside the visible canvas area.

Key Requirements:

  • Use React and TypeScript.
  • Implement a clear and maintainable component structure.
  • Handle mouse events (click, drag, mousemove) appropriately.
  • Use appropriate styling (CSS or a CSS-in-JS library) to visually represent the shapes.
  • The editor should be responsive and work well on different screen sizes.

Expected Behavior:

  • Clicking "Add Circle" creates a circle at the mouse position.
  • Clicking "Add Rectangle" creates a rectangle at the mouse position.
  • Clicking a shape selects it for dragging.
  • Dragging a selected shape moves it smoothly.
  • Clicking the delete button (or using the context menu) removes the shape.
  • The canvas should have a defined size.

Edge Cases to Consider:

  • What happens when the user clicks outside of the canvas area?
  • How do you handle overlapping shapes? (For this challenge, overlapping shapes are acceptable, but consider how you might handle them in a more complex editor.)
  • What happens when the user tries to delete a shape that doesn't exist?
  • How do you prevent shapes from being dragged completely off-screen?

Examples

Example 1:

Input: Initially empty canvas. User clicks "Add Circle", then clicks on the canvas at coordinates (100, 150). User then clicks and drags the circle to coordinates (200, 250).
Output: A circle is rendered at (100, 150) initially, then moves to (200, 250) when dragged.
Explanation: The component updates the circle's position in its state based on the mouse drag event.

Example 2:

Input: Canvas with one circle and one rectangle. User clicks "Add Rectangle", then clicks on the canvas at coordinates (50, 75). User then clicks the delete button on the rectangle.
Output: The rectangle disappears from the canvas, and only the circle remains.
Explanation: The component removes the rectangle from its state and re-renders the canvas.

Example 3: (Edge Case)

Input: Canvas with a circle. User clicks and drags the circle towards the edge of the canvas.
Output: The circle stops moving when it reaches the edge of the canvas and does not go beyond it.
Explanation: The component prevents the circle from being dragged outside the canvas boundaries.

Constraints

  • Canvas Size: The canvas should be 800px wide and 600px high.
  • Shape Size: Circles should have a default radius of 25px. Rectangles should have a default width of 50px and a default height of 30px.
  • Performance: The editor should remain responsive even with a moderate number of shapes (e.g., up to 20). Avoid unnecessary re-renders.
  • Shape Types: Only circles and rectangles are supported.
  • Input Format: User interaction is solely through mouse clicks and drags.

Notes

  • Consider using a library like uuid to generate unique IDs for each shape. This can be helpful for identifying shapes during deletion or other operations.
  • Think about how you will manage the state of the selected shape.
  • You can use CSS or a CSS-in-JS library (like styled-components or emotion) for styling.
  • Focus on the core functionality of creating, moving, and deleting shapes. Advanced features like shape resizing, connecting shapes, or custom shape types are not required for this challenge.
  • Break down the problem into smaller, manageable components. For example, you could have a Shape component that renders a single shape and handles its dragging behavior.
  • Consider using React's useRef hook to access the DOM element of the canvas.
Loading editor...
typescript