UNPKG

react-annotator-lite

Version:

A lightweight React-based annotation tool for images. Designed for performance and ease of use — perfect for visual markup, design feedback, and review workflows.

202 lines (156 loc) 9.71 kB
# 🖊️ react-annotator-lite A lightweight, extensible React component for image annotation. **react-annotator-lite** enables freehand drawing and threaded comments directly on images or canvases, with a responsive layout, rich metadata handling, and built-in UI components for action bars, comment sidebars, and overlays. --- ## ✨ Features - **Threaded Comments:** Add comments and replies directly on images. - **Draggable Annotations:** Move comments anywhere over the image. - **Drawing Tools:** Freehand drawing with undo/redo, eraser, and color/stroke selection. - **Nearest Tag Visibility:** Show the closest tag on hover for context. - **Layer Control:** Toggle visibility of comments and drawing layers independently. - **Responsive Design:** Works seamlessly on both desktop and mobile. - **Customizable UI:** Replace built-in components or styles with your own. - **Performance Optimized:** Uses React memoization and efficient event handling. --- ## 🚀 Quick Start ### Installation ```sh npm install react-annotator-lite # or yarn add react-annotator-lite ``` ### Usage Example ```js import React, { useState } from "react"; import Annotator from "react-annotator-lite"; //add the following line to include default styles import 'react-annotator-lite/dist/index.css' const App = () => { const [annotations, setAnnotations] = useState([]); const [paths, setPaths] = useState([]); const handleCommentAdd = (comment) => { setAnnotations((prev) => [...prev, comment]); }; const handleReplyAdd = (reply, parentId) => { // Add reply logic }; const handleSave = (metaData, canvasPaths) => { // Save logic }; const handleDelete = (metadataId) => { // Delete logic }; // Example: Custom tooltip for drawing paths const renderPathTooltip = (path, index) => ( <div style={{ background: '#222', color: '#fff', padding: 4, borderRadius: 4 }}> <strong>Drawn by:</strong> {path.user?.userName || 'Unknown'}<br /> <strong>Path #{index + 1}</strong> </div> ); return ( <div style={{ maxWidth: 800, margin: "0 auto" }}> <Annotator image_url="https://example.com/sample-image.jpg" initial_Annotations={annotations} initial_Paths={paths} onCommentAdd={handleCommentAdd} onReplyAdd={handleReplyAdd} onSave={handleSave} onDelete={handleDelete} enableDrawing={true} currentUserData={{ userId: "user-123", userName: "Alice" }} maxWidth={800} maxHeight={600} drawingOptions={{ strokeColor: '#00f', strokeWidth: 8 }} renderPathTooltip={renderPathTooltip} /> </div> ); }; export default App; ``` --- ## 📚 API Reference ### `<Annotator />` Props | Prop | Type | Description | Default | |-----------------------------|-------------------------------------------|------------------------------------------------------------------|--------------------| | `image_url` | `string` | URL of the image to annotate (**required**) | — | | `initial_Annotations` | `MetaData[]` | Initial comment annotations | `[]` | | `initial_Paths` | `UserCanvasPath[]` | Initial drawing paths | `[]` | | `onCommentAdd` | `(comment: MetaData) => void` | Callback when a new comment is added | — | | `onReplyAdd` | `(reply: MetaData, parentId: string) => void` | Callback when a reply to a comment is added | — | | `onSave` | `(metaData: MetaData[], paths: UserCanvasPath[]) => void` | Callback when save action is triggered | — | | `onDelete` | `(metadataId: string) => void` | Callback when a comment or reply is deleted | — | | `enableDrawing` | `boolean` | Enable or disable drawing mode | `true` | | `currentUserData` | `{ userId: string; userName?: string }` | Current user info for created comments | `{ userId: 'Unknown' }` | | `maxWidth` | `number` | Max width of image container | Responsive default | | `maxHeight` | `number` | Max height of image container | Responsive default | | `commentPillStyle` | `React.CSSProperties` | Custom style for comment pills | — | | `commentHoverMenuStyle` | `React.CSSProperties` | Style for comment hover menu | — | | `commentSidebarStyle` | `React.CSSProperties` | Style for the comment sidebar | — | | `commentOptionMenuStyle` | `React.CSSProperties` | Style for comment option menus | — | | `actionIcons` | `Record<string, React.ReactNode>` | Custom action icons for toolbar | — | | `actionToolbarStyle` | `React.CSSProperties` | Style for the action toolbar | — | | `sketchCanvasStyle` | `React.CSSProperties` | Style for the drawing canvas | — | | `imageContainerStyle` | `React.CSSProperties` | Style for the image container wrapper | — | | `commentItems` | `React.ReactNode` | Custom comment item renderer | — | | `disableAnnotationDragging` | `boolean` | Disable dragging of comment annotations | `false` | | `drawingOptions` | `{ strokeColor?: string; strokeWidth?: number; }` | Drawing tool options (color, width) | — | | `renderPathTooltip` | `(path: UserCanvasPath, index: number) => React.ReactNode` | Custom tooltip renderer for drawing paths | — | #### Callback Details - **onCommentAdd(comment: MetaData):** Called when a new comment is added. Receives the comment object. - **onReplyAdd(reply: MetaData, parentId: string):** Called when a reply is added to a comment. Receives the reply and parent comment ID. - **onSave(metaData: MetaData[], paths: UserCanvasPath[]):** Called when the user triggers a save action. Receives all metadata and drawing paths. - **onDelete(metadataId: string):** Called when a comment or reply is deleted. Receives the metadata ID. --- ## 🧩 Custom Drawing Path Tooltip (`renderPathTooltip`) You can provide a custom tooltip for each drawing path using the `renderPathTooltip` prop. This function receives the path object and its index, and should return a React node to display as a tooltip when hovering over a path. **Example:** ```jsx const renderPathTooltip = (path, index) => ( <div style={{ background: '#333', color: '#fff', padding: 6, borderRadius: 4 }}> <div>Drawn by: <b>{path.user?.userName || 'Unknown'}</b></div> <div>Path #{index + 1}</div> <div>Points: {path.paths.length}</div> </div> ); <Annotator image_url="..." // ...other props renderPathTooltip={renderPathTooltip} /> ``` --- ## 🏗️ Architecture Overview - **Component Structure:** - `Annotator` is the main component, managing state and context for annotation, drawing, and UI. - Modular subcomponents: `SketchCanvas`, `MetaDataLayer`, `ActionBar`, `MetaDataSideBar`, `BottomMetaDataMenu`, etc. - Uses React Context for state sharing and hooks for responsive layout and performance. - **Drawing Engine:** - Freehand drawing with undo/redo, eraser, and color/stroke selection. - Drawing paths are normalized for responsive scaling. - **Comment System:** - Threaded comments with drag-and-drop, reply, and delete support. - Sidebar and overlay UI for managing and viewing comments. - **Customization:** - All UI elements can be styled or replaced via props. - Supports custom icons, toolbars, and comment rendering. --- ## 🤝 Contributing Contributions are welcome! To contribute: 1. Fork the repository and create your branch from `main`. 2. Run `npm install` to install dependencies. 3. Make your changes and add tests if applicable. 4. Run `npm run lint` and `npm run build` to ensure code quality. 5. Submit a pull request with a clear description of your changes. ### Guidelines - Follow the existing code style and naming conventions. - Write clear, concise commit messages. - Add or update documentation as needed. - Ensure all tests pass before submitting. --- ## 📄 License MIT