├── .DS_Store ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── demo.gif ├── demo ├── .DS_Store ├── public │ └── 404.html └── src │ ├── App.js │ ├── components │ ├── Button │ │ └── index.js │ ├── Docs │ │ └── index.js │ ├── Footer │ │ └── index.js │ ├── GithubStarLink │ │ └── index.js │ ├── Highlight │ │ └── index.js │ ├── Home │ │ ├── index.js │ │ └── simple.txt │ ├── NavBar │ │ └── index.js │ ├── Root │ │ └── index.js │ └── Samples │ │ ├── Custom │ │ └── index.js │ │ ├── Linked │ │ ├── index.js │ │ └── index.txt │ │ ├── Multiple │ │ ├── index.js │ │ └── index.txt │ │ ├── Simple │ │ └── index.js │ │ ├── Threaded │ │ └── index.js │ │ └── Touch │ │ ├── index.js │ │ └── index.txt │ ├── img.jpeg │ ├── index.css │ ├── index.html │ ├── index.js │ ├── mocks.js │ └── registerServiceWorker.js ├── nwb.config.js ├── package.json ├── public └── 404.html ├── src ├── components │ ├── Annotation.js │ ├── Content │ │ └── index.js │ ├── Editor │ │ └── index.js │ ├── FancyRectangle │ │ └── index.js │ ├── Oval │ │ └── index.js │ ├── Overlay │ │ └── index.js │ ├── Point │ │ └── index.js │ ├── Rectangle │ │ └── index.js │ ├── TextEditor │ │ └── index.js │ └── defaultProps.js ├── hocs │ ├── OvalSelector.js │ ├── PointSelector.js │ └── RectangleSelector.js ├── index.js ├── selectors.js ├── types │ └── index.d.ts └── utils │ ├── compose.js │ ├── isMouseHovering.js │ ├── offsetCoordinates.js │ └── withRelativeMousePos.js ├── tests ├── .eslintrc ├── Annotation.spec.js ├── index.test.js └── selectors │ ├── OvalSelector.spec.js │ ├── PointSelector.spec.js │ └── RectangleSelector.spec.js └── yarn.lock /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Secretmapper/react-image-annotation/3ddde7c7f52073c7f977485456e825b5592afb1d/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /demo/dist 3 | /es 4 | /lib 5 | /node_modules 6 | /umd 7 | npm-debug.log* 8 | .vscode/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | node_js: 5 | - 8 6 | 7 | before_install: 8 | - npm install codecov.io coveralls 9 | 10 | after_success: 11 | - cat ./coverage/lcov.info | ./node_modules/codecov.io/bin/codecov.io.js 12 | - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js 13 | 14 | branches: 15 | only: 16 | - master 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.9.8 2 | 3 | ### Improvements 4 | 5 | - Add Type Definitions for Typescript (#12) (thanks @danilofuchs) 6 | - Add support for `children` property (#13) (thanks @federico-bohn) 7 | 8 | ## 0.9.7 9 | 10 | ### Fixes 11 | 12 | - [Interaction] Fix bug where point annotation would fail abort (#8) (thanks @joshuadeguzman) 13 | 14 | ## 0.9.6 15 | 16 | ### Breaking change 17 | 18 | - [Interaction] Change annotation click action to click and drag (#6) 19 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Prerequisites 2 | 3 | [Node.js](http://nodejs.org/) >= v4 must be installed. 4 | 5 | ## Installation 6 | 7 | - Running `npm install` in the component's root directory will install everything you need for development. 8 | 9 | ## Demo Development Server 10 | 11 | - `npm start` will run a development server with the component's demo app at [http://localhost:3000](http://localhost:3000) with hot module reloading. 12 | 13 | ## Running Tests 14 | 15 | - `npm test` will run the tests once. 16 | 17 | - `npm run test:coverage` will run the tests and produce a coverage report in `coverage/`. 18 | 19 | - `npm run test:watch` will run the tests on every change. 20 | 21 | ## Building 22 | 23 | - `npm run build` will build the component for publishing to npm and also bundle the demo app. 24 | 25 | - `npm run clean` will delete built resources. 26 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-present, Arian Allenson Valdez. 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | React Image Annotation 2 | ========================= 3 | 4 | An infinitely customizable image annotation library built on React 5 | 6 | ![Annotation demo](demo.gif) 7 | 8 | ## Installation 9 | 10 | ``` 11 | npm install --save react-image-annotation 12 | # or 13 | yarn add react-image-annotation 14 | ``` 15 | 16 | ## Usage 17 | 18 | ```js 19 | export default class Simple extends Component { 20 | state = { 21 | annotations: [], 22 | annotation: {} 23 | } 24 | 25 | onChange = (annotation) => { 26 | this.setState({ annotation }) 27 | } 28 | 29 | onSubmit = (annotation) => { 30 | const { geometry, data } = annotation 31 | 32 | this.setState({ 33 | annotation: {}, 34 | annotations: this.state.annotations.concat({ 35 | geometry, 36 | data: { 37 | ...data, 38 | id: Math.random() 39 | } 40 | }) 41 | }) 42 | } 43 | 44 | render () { 45 | return ( 46 | 47 | 58 | 59 | ) 60 | } 61 | } 62 | ``` 63 | 64 | 65 | ### Props 66 | 67 | Prop | Description | Default 68 | ---- | ----------- | ------- 69 | `src` | Image src attribute | 70 | `alt` | Image alt attribute | 71 | `annotations` | Array of annotations | 72 | `value` | Annotation object currently being created. See [annotation object](#annotation-object) | 73 | `onChange` | `onChange` handler for annotation object | 74 | `onSubmit` | `onSubmit` handler for annotation object | 75 | `type` | Selector type. See [custom shapes](#using-custom-shapes) | `RECTANGLE` 76 | `allowTouch` | Set to `true` to allow the target to handle touch events. This disables one-finger scrolling | `false` 77 | `selectors` | An array of selectors. See [adding custom selector logic](#adding-custom-selector-logic) | `[RectangleSelector, PointSelector, OvalSelector]` 78 | `activeAnnotations` | Array of annotations that will be passed as 'active' (active highlight and shows content) | 79 | `activeAnnotationComparator` | Method to compare annotation and `activeAnnotation` item (from `props.activeAnnotations`). Return `true` if it's the annotations are equal | `(a, b) => a === b` 80 | `disableAnnotation` | Set to `true` to disable creating of annotations (note that no callback methods will be called if this is `true`) | `false` 81 | `disableSelector` | Set to `true` to not render `Selector` | `false` 82 | `disableEditor` | Set to `true` to not render `Editor` | `false` 83 | `disableOverlay` | Set to `true` to not render `Overlay` | `false` 84 | `renderSelector` | Function that renders `Selector` Component | See [custom components](#using-custom-components) 85 | `renderEditor` | Function that renders `Editor` Component | See [custom components](#using-custom-components) 86 | `renderHighlight` | Function that renders `Highlight` Component | See [custom components](#using-custom-components) 87 | `renderContent` | Function that renders `Content` | See [custom components](#using-custom-components) 88 | `renderOverlay` | Function that renders `Overlay` | See [custom components](#using-custom-components) 89 | `onMouseUp` | `onMouseUp` handler on annotation target | 90 | `onMouseDown` | `onMouseDown` handler on annotation target | 91 | `onMouseMove` | `onMouseMove` handler on annotation target | 92 | `onClick` | `onClick` handler on annotation target | 93 | 94 | #### Annotation object 95 | 96 | An Annotation object is an object that conforms to the object shape 97 | 98 | ```js 99 | ({ 100 | selection: T.object, // temporary object for selector logic 101 | geometry: T.shape({ // geometry data for annotation 102 | type: T.string.isRequired // type is used to resolve Highlighter/Selector renderer 103 | }), 104 | // auxiliary data object for application. 105 | // Content data can be stored here (text, image, primary key, etc.) 106 | data: T.object 107 | }) 108 | ``` 109 | 110 | ## Using custom components 111 | 112 | `Annotation` supports `renderProp`s for almost every internal component. 113 | 114 | This allows you to customize everything about the the look of the annotation interface, and you can even use canvas elements for performance or more complex interaction models. 115 | 116 | - `renderSelector` - used for selecting annotation area (during annotation creation) 117 | - `renderEditor` - appears after annotation area has been selected (during annotation creation) 118 | - `renderHighlight` - used to render current annotations in the annotation interface. It is passed an object that contains the property `active`, which is true if the mouse is hovering over the higlight 119 | - `renderComponent` - auxiliary component that appears when mouse is hovering over the highlight. It is passed an object that contains the annotation being hovered over. `{ annotation }` 120 | - `renderOverlay` - Component overlay for Annotation (i.e. 'Click and Drag to Annotate') 121 | 122 | You can view the default renderProps [here](src/components/defaultProps.js) 123 | 124 | **Note**: You cannot use `:hover` selectors in css for components returned by `renderSelector` and `renderHighlight`. This is due to the fact that `Annotation` places DOM layers on top of these components, preventing triggering of `:hover` 125 | 126 | ## Using custom shapes 127 | 128 | `Annotation` supports three shapes by default, `RECTANGLE`, `POINT` and `OVAL`. 129 | 130 | You can switch the shape selector by passing the appropriate `type` as a property. Default shape `TYPE`s are accessible on their appropriate selectors: 131 | 132 | ```js 133 | import { 134 | PointSelector, 135 | RectangleSelector, 136 | OvalSelector 137 | } from 'react-image-annotation/lib/selectors' 138 | 139 | 142 | ``` 143 | 144 | ### Adding custom selector logic 145 | 146 | #### This is an Advanced Topic 147 | 148 | The Annotation API allows support for custom shapes that use custom logic such as polygon or freehand selection. This is done by defining your own selection logic and passing it as a selector in the `selectors` property. 149 | 150 | Selectors are objects that must have the following properties: 151 | 152 | - `TYPE` - string that uniquely identifies this selector (i.e. `RECTANGLE`) 153 | - `intersects` - method that returns true if the mouse point intersects with the annotation geometry 154 | - `area` - method that calculates and returns the area of the annotation geometry 155 | - `methods` - object that can contain various listener handlers (`onMouseUp`, `onMouseDown`, `onMouseMove`, `onClick`). These listener handlers are called when triggered in the annotation area. These handlers must be reducer-like methods - returning a new annotation object depending on the change of the method 156 | 157 | You can view a defined `RectangleSelector` [here](src/hocs/RectangleSelector.js) 158 | 159 | ### Connecting selector logic to Redux/MobX 160 | 161 | First see [Selectors](#adding-custom-selector-logic) 162 | 163 | You can use `Selector` methods to connect these method logic to your stores. This is due to the fact that selector methods function as reducers, returning new state depending on the event. 164 | 165 | ***Note that it is not necessary to connect the selector logic with redux/mobx. Connecting the annotation and annotations state is more than enough for most use cases.*** 166 | 167 | ## License 168 | 169 | MIT 170 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Secretmapper/react-image-annotation/3ddde7c7f52073c7f977485456e825b5592afb1d/demo.gif -------------------------------------------------------------------------------- /demo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Secretmapper/react-image-annotation/3ddde7c7f52073c7f977485456e825b5592afb1d/demo/.DS_Store -------------------------------------------------------------------------------- /demo/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Single Page Apps for GitHub Pages 6 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /demo/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { BrowserRouter as Router, Route } from 'react-router-dom' 3 | import styled from 'styled-components' 4 | 5 | import NavBar from './components/NavBar' 6 | import Root from './components/Root' 7 | import Home from './components/Home' 8 | import Docs from './components/Docs' 9 | import Footer from './components/Footer' 10 | 11 | const Main = styled.main` 12 | margin: 0 16px; 13 | margin-top: 51px; 14 | ` 15 | 16 | export default () => ( 17 | 18 | 19 | 22 |
23 | 28 | 32 |
33 |