[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\n    [\n      \"env\",\n      {\n        \"modules\": \"commonjs\",\n        \"targets\": {\n          \"browsers\": [\"last 2 versions\", \"safari >= 7\"]\n        }\n      }\n    ],\n    \"flow\",\n    \"react\",\n    \"stage-0\"\n  ],\n  \"plugins\": [\n    \"transform-es2015-destructuring\",\n    [\"transform-object-rest-spread\",\n      {\n        \"useBuiltIns\": true\n      }\n    ]\n  ],\n  \"env\": {\n    \"development\": {\n      \"plugins\": [\n        \"flow-react-proptypes\",\n        \"transform-react-jsx-source\"\n      ]\n    },\n    \"production\": {\n      \"plugins\": [\n        \"transform-react-remove-prop-types\",\n        \"transform-react-constant-elements\"\n      ]\n    },\n    \"test\": {\n      \"plugins\": [\n        \"transform-es2015-modules-commonjs\",\n        \"dynamic-import-node\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": ".eslintignore",
    "content": ".gitignore\n\ndist/**/*\nexample/**/*\ntest/**/*\n\ngulpfile.js\nrollup.config.js"
  },
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"parser\": \"babel-eslint\",\n  \"extends\": [\n    \"airbnb\",\n    \"plugin:flowtype/recommended\"\n  ],\n  \"globals\": {\n    \"API_URL\": false,\n    \"ENVIRONMENT\": false,\n    \"VERSION\": false\n  },\n  \"env\": {\n    \"browser\": true,\n    \"node\": true,\n    \"jest\": true,\n    \"es6\": true\n  },\n  \"plugins\": [\n    \"react\",\n    \"jsx-a11y\",\n    \"flowtype\"\n  ],\n  \"parserOptions\": {\n    \"ecmaVersion\": 6,\n    \"sourceType\": \"module\",\n    \"ecmaFeatures\": {\n      \"jsx\": true\n    }\n  },\n  \"rules\": {\n    \"eol-last\": 0,\n    \"jsx-quotes\": [\n      \"error\",\n      \"prefer-single\"\n    ],\n    \"no-plusplus\": 0,\n    \"no-nested-ternary\": 1,\n    \"arrow-parens\": 0,\n    \"object-curly-spacing\": [\n      2,\n      \"always\",\n      {\n        \"arraysInObjects\": false,\n        \"objectsInObjects\": false\n      }\n    ],\n    \"arrow-body-style\": [\n      2,\n      \"as-needed\"\n    ],\n    \"comma-dangle\": [\n      2,\n      \"always-multiline\"\n    ],\n    \"import/imports-first\": 0,\n    \"import/newline-after-import\": 0,\n    \"import/no-dynamic-require\": 0,\n    \"import/no-extraneous-dependencies\": 0,\n    \"import/no-named-as-default\": 0,\n    \"import/no-unresolved\": 2,\n    \"import/prefer-default-export\": 0,\n    \"indent\": [\n      2,\n      2,\n      {\n        \"SwitchCase\": 1\n      }\n    ],\n    \"jsx-a11y/aria-props\": 2,\n    \"jsx-a11y/heading-has-content\": 0,\n    \"jsx-a11y/href-no-hash\": \"off\",\n    \"jsx-a11y/anchor-is-valid\": [ \"error\", {\n      \"components\": [ \"Link\" ],\n      \"specialLink\": [ \"hrefLeft\", \"hrefRight\" ],\n      \"aspects\": [ \"noHref\", \"invalidHref\", \"preferButton\" ]\n    }],\n    \"jsx-a11y/label-has-for\": [ \"error\", {\n      \"required\": {\n        \"every\": [ \"nesting\", \"id\" ]\n      },\n      \"allowChildren\": true\n    }],\n    \"jsx-a11y/mouse-events-have-key-events\": 2,\n    \"jsx-a11y/role-has-required-aria-props\": 2,\n    \"jsx-a11y/role-supports-aria-props\": 2,\n    \"jsx-a11y/no-static-element-interactions\": 1,\n    \"jsx-a11y/img-has-alt\": 0,\n    \"max-len\": 0,\n    \"newline-per-chained-call\": 0,\n    \"no-confusing-arrow\": 0,\n    \"no-console\": 1,\n    \"no-use-before-define\": 0,\n    \"prefer-template\": 2,\n    \"class-methods-use-this\": 0,\n    \"react/sort-comp\": [\n      2,\n      {\n        \"order\": [\n          \"type-annotations\",\n          \"static-methods\",\n          \"lifecycle\",\n          \"everything-else\",\n          \"render\"\n        ]\n      }\n    ],\n    \"react/jsx-wrap-multilines\": 2,\n    \"react/forbid-prop-types\": 0,\n    \"react/jsx-first-prop-new-line\": [\n      2,\n      \"multiline\"\n    ],\n    \"react/jsx-filename-extension\": 0,\n    \"react/jsx-no-bind\": 1,\n    \"react/jsx-curly-spacing\": [\n      2,\n      \"always\"\n    ],\n    \"react/jsx-no-target-blank\": 0,\n    \"react/no-multi-comp\": 1,\n    \"react/require-extension\": 0,\n    \"react/self-closing-comp\": 0,\n    \"require-yield\": 0,\n    \"import/no-webpack-loader-syntax\": 0,\n    \"react/require-default-props\": 0,\n    \"react/no-array-index-key\": 0,\n    \"flowtype/object-type-delimiter\": [\n      2,\n      \"comma\"\n    ]\n  },\n  \"settings\": {\n    \"flowtype\": {\n      \"onlyFilesWithFlowAnnotation\": true\n    }\n  }\n}\n"
  },
  {
    "path": ".flowconfig",
    "content": "[ignore]\n<PROJECT_ROOT>/node_modules/babel-plugin-transform-react-remove-prop-types/.*\n<PROJECT_ROOT>/node_modules/babel-plugin-flow-runtime/.*\n<PROJECT_ROOT>/node_modules/eslint-plugin-jsx-a11y/.*\n<PROJECT_ROOT>/node_modules/flow-runtime/.*\n<PROJECT_ROOT>/node_modules/.staging/.*\n<PROJECT_ROOT>/node_modules/kefir/.*\n<PROJECT_ROOT>/example/.*\n<PROJECT_ROOT>/dist/.*\n\n[include]\n\n[libs]\n\n[lints]\n\n[options]\n# Support ES7 classes\nesproposal.class_static_fields=enable\nesproposal.class_instance_fields=enable\nesproposal.export_star_as=enable\n\n# Warn on @decorators\nesproposal.decorators=warn\n\n# Experimental settings\nexperimental.const_params=true\n\n[strict]"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\ntest-report\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Typescript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\n.idea\n*.iml\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: false\nlanguage: node_js\nnode_js:\n- stable\nscript:\n- npm test\nafter_success:\n- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js\n- cat ./coverage/lcov.info | ./node_modules/.bin/codacy-coverage\nenv:\n  global:\n    secure: o+QC0UCfqDo+Py3i6Wa0eOMGfPgqrXXMCrINxzvS8WNe5Lh9bIGbFfE4BctPu6YJfFFv8h1krHlA+oHFaFLzsWUqe70pEKu9+hFWWpR4WMdlRW5oHMjwmO3M5cKzcPYsvJ9YzjoEEU9wsLx/pt3i+8VRl0E+0F2B4Zo3PfcGny3WYhEO3r/B8shU81EE1sajhhesPj5dGWHRTfQhcQlyvtBlWqy+Atp+RFGDyB7N0KJhIjw15vLvPqCI0UsaWrpCIC8c3uV3um92ChKTaxbxBJS7cdG9wIMoardBY1UxxMahn9ohgO5/6obr9h6FTpfotblGpFQ6qKVySd2wic8eODPbplP+pq0TaMhw37GuE+ZEL3Yg7pwfeav2nKf8xwvGA5yxZLq8WKnTZZdOUMRT9DX3MFTKNkUusINv0ehWoVZSRsFqFrsXF0C9cx9/HjEpG33j3I2bH9fo0CPcgfwM9frGHcTfqirq86bwESIYfGXX7+e6qkV9yozEtpDDueaQK0hHWrEAMsNQgYX63r8lGF1oYCdeQ14I2xQjs0Yufi8TTcpDPy3977A68HkASj0mNLH24u4fK6zQYkDLCc7Q4BXIO1lVKeDjw7/obvjQ/+QM0BT1F757OWak5YjhAy9SAttZnsCYcBa9VVIlEQYrrHKzwR3RQDGhmE2g85dzWOc=\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"flow.useNPMPackagedFlow\": true,\n    \"javascript.validate.enable\": false,\n    \"files.associations\": {\n        \"*.overrides\": \"less\",\n        \"*.variables\": \"less\",\n        \"theme.config\": \"less\",\n    },\n    \"flow.showUncovered\": true,\n    \"flow.logLevel\": \"warn\"\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\n\n> I haven't been maintaining this changelog.\n>\n> Check out the [releases](https://github.com/aurbano/react-ds/releases) section instead.\n\n------\n\n# Old Changelog\n\n## [v1.7.0](https://github.com/aurbano/react-ds/tree/v1.7.0) (2017-07-27)\n[Full Changelog](https://github.com/aurbano/react-ds/compare/v1.6.0...v1.7.0)\n\n**Closed issues:**\n\n- Add option to ignore certain targets when initiating selects [\\#7](https://github.com/aurbano/react-ds/issues/7)\n\n## [v1.6.0](https://github.com/aurbano/react-ds/tree/v1.6.0) (2017-07-25)\n[Full Changelog](https://github.com/aurbano/react-ds/compare/v1.5.0...v1.6.0)\n\n## [v1.5.0](https://github.com/aurbano/react-ds/tree/v1.5.0) (2017-07-13)\n[Full Changelog](https://github.com/aurbano/react-ds/compare/v1.4.1...v1.5.0)\n\n## [v1.4.1](https://github.com/aurbano/react-ds/tree/v1.4.1) (2017-07-13)\n[Full Changelog](https://github.com/aurbano/react-ds/compare/v1.4.0...v1.4.1)\n\n**Closed issues:**\n\n- Move react to peerDependencies [\\#5](https://github.com/aurbano/react-ds/issues/5)\n\n## [v1.4.0](https://github.com/aurbano/react-ds/tree/v1.4.0) (2017-07-12)\n[Full Changelog](https://github.com/aurbano/react-ds/compare/v1.3.0...v1.4.0)\n\n**Fixed bugs:**\n\n- Items not properly selected when scrolled [\\#4](https://github.com/aurbano/react-ds/issues/4)\n\n## [v1.3.0](https://github.com/aurbano/react-ds/tree/v1.3.0) (2017-07-12)\n[Full Changelog](https://github.com/aurbano/react-ds/compare/v1.2.0...v1.3.0)\n\n## [v1.2.0](https://github.com/aurbano/react-ds/tree/v1.2.0) (2017-07-12)\n[Full Changelog](https://github.com/aurbano/react-ds/compare/v1.1.0...v1.2.0)\n\n**Implemented enhancements:**\n\n- Add examples [\\#1](https://github.com/aurbano/react-ds/issues/1)\n\n## [v1.1.0](https://github.com/aurbano/react-ds/tree/v1.1.0) (2017-07-12)\n[Full Changelog](https://github.com/aurbano/react-ds/compare/v1.0.3...v1.1.0)\n\n## [v1.0.3](https://github.com/aurbano/react-ds/tree/v1.0.3) (2017-07-12)\n[Full Changelog](https://github.com/aurbano/react-ds/compare/v1.0.2...v1.0.3)\n\n**Implemented enhancements:**\n\n- Setup CI [\\#3](https://github.com/aurbano/react-ds/issues/3)\n\n## [v1.0.2](https://github.com/aurbano/react-ds/tree/v1.0.2) (2017-07-12)\n[Full Changelog](https://github.com/aurbano/react-ds/compare/v1.0.1...v1.0.2)\n\n**Implemented enhancements:**\n\n- Generate dist folder with babel without flow [\\#2](https://github.com/aurbano/react-ds/issues/2)\n\n## [v1.0.1](https://github.com/aurbano/react-ds/tree/v1.0.1) (2017-07-12)\n\n\n\\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 Alejandro U. Alvarez\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# React DS\n> Tiny (7KB) React Drag-to-Select component (with no dependencies! with support for touch devices!\n\n[![Travis](https://img.shields.io/travis/aurbano/react-ds.svg)](https://travis-ci.org/aurbano/react-ds)\n[![npm](https://img.shields.io/npm/v/react-ds.svg)](https://www.npmjs.com/package/react-ds)\n[![Coverage Status](https://coveralls.io/repos/github/aurbano/react-ds/badge.svg?branch=master)](https://coveralls.io/github/aurbano/react-ds?branch=master)\n[![npm](https://img.shields.io/npm/dm/react-ds.svg)](https://www.npmjs.com/package/react-ds)\n[![npm](https://img.shields.io/npm/l/react-ds.svg)](https://www.npmjs.com/package/react-ds)\n[![Codacy grade](https://img.shields.io/codacy/grade/e2589a609bdc4c56bd49c232a65dab4e.svg)](https://www.codacy.com/app/aurbano/react-ds)\n\n[![react-ds gif](https://thumbs.gfycat.com/FatYellowKid-size_restricted.gif)](https://gfycat.com/gifs/detail/fatyellowkid)\n\nI wrote this library because I couldn't find any existing one to do selections without having to wrap the items in their component.\n\nIn some cases you really need an unobtrusive way to make items selectable, this will do just that.\n\n\n## Installation\n\n```console\n$ npm i react-ds\n```\nOr if you prefer yarn\n```console\n$ yarn add react-ds\n```\n\n## Usage\n\n```jsx\nimport Selection from 'react-ds';\n\n// target (ref) is the parent component (so that selects only happen when clicking and dragging on it)\n// elements (refs[]) is an array of refs to the components that are selectable\n<Selection\n    target={ ref }\n    elements={ refs[] }\n    onSelectionChange={ this.handleSelection }\n/>\n```\n\n### Props\n\n#### `target`\n\nElement where the selection should be applied to. This is to scope the mouse/touch event handlers and make sure that it doesn't affect your whole web app.\n\nIt must be a React `ref`, it should also exist, so you may want to check if it's already initialized before rendering the `Selection` component.\n\n#### `elements`\n\nArray of refs to the elements that are selectable. The `Selection` component will use this to get their location and sizes to determine whether they are within the selection area.\n\nThe should exist before rendering the `Selection` component.\n\n#### `onSelectionChange`\n\nFunction that will be executed when the selection changes. An array of element indexes will be passed (with the same indexes as the `elements` prop).\n\nThis is where you want to update your state, to highlight them as selected for example.\n\n#### `onHighlightChange`\n\nWhile dragging, `onHighlightChange` is called only when the highlighted elements have changed.\n\nWhen the mouse is released, it will be called with an empty array.\n\n![onHighlightChange Example](https://user-images.githubusercontent.com/1640952/61724315-0269b280-ad6e-11e9-899c-4466e090cb13.gif)\n\n#### `offset` *(Optional)*\n\nThis is used to calculate the coordinates of the mouse when drawing the Selection box, since the mouse events gives coordinates relative to the document, but the Selection box may have a different parent.\n\nEssentially you need to pass the offset of the parent element where the Selection is being rendered. If it's rendered in the same component as the items to be selected then the default value will work fine.\n\nIf passing your own offset keep in mind that `getBoundingClientRect()` depends on the scroll, so you may want to do something like this:\n\n```js\nconst boundingBox = target.getBoundingClientRect();\nconst offset = {\n  top: boundingBox.top + window.scrollY,\n  left: boundingBox.left + window.scrollX,\n};\n```\n\n#### `style` *(Optional)*\n\nIf you want to override the styles for the selection area, you can either pass any styles here, or use css and declare any styles on the `.react-ds-border` class.\n\nThe styles are merged, so you can override just one property if you need (typically the `zIndex`).\n\nThe default styles are:\n\n```js\nconst style = {\n  position: 'absolute',\n  background: 'rgba(159, 217, 255, 0.3)',\n  border: 'solid 1px rgba(123, 123, 123, 0.61)',\n  zIndex: 9,\n  cursor: 'crosshair',\n}\n```\n\n#### `ignoreTargets` *(Optional)*\n\nSpecify an array of CSS3 selectors for DOM targets that should be ignored when initiating a selection. i.e. `['div', 'div > p', '#someId']`\n\n>This is specially useful because `react-ds` uses native browser events that bypass React's event queue, so you won't be able to `stopPropagation` as usual.\n\n```jsx\n<Selection\n    target={ this.state.ref}\n    elements={ this.state.elRefs }\n    onSelectionChange={ this.handleSelection }\n    ignoreTargets={ ['.handle'] }\n/>\n```\n\n## Example\n\nThis example was taken from [`example/app/src/Example.js`](https://github.com/aurbano/react-ds/blob/master/example/app/src/Example.js) which you can see running at https://aurbano.eu/react-ds/\n\n```jsx\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport Selection from 'react-ds';\n\nexport default class Example extends React.PureComponent {\n\n  constructor() {\n    super();\n\n    this.state = {\n      ref: null,\n      elRefs: [],\n      selectedElements: [], // track the elements that are selected\n    };\n  }\n\n  handleSelection = (indexes) => {\n    this.setState({\n      selectedElements: indexes,\n    });\n  };\n\n  getStyle = (index) => {\n    if (this.state.selectedElements.indexOf(index) > -1) {\n      // Selected state\n      return {\n        background: '#2185d0',\n        borderColor: '#2185d0',\n        color: 'white',\n      };\n    }\n    return {};\n  };\n\n  addElementRef = (ref) => {\n    const elRefs = this.state.elRefs;\n    elRefs.push(ref);\n    this.setState({\n      elRefs,\n    });\n  };\n\n  renderSelection() {\n    if (!this.state.ref || !this.state.elRefs) {\n      return null;\n    }\n    return (\n      <Selection\n        target={ this.state.ref}\n        elements={ this.state.elRefs }\n        onSelectionChange={ this.handleSelection }\n        style={ this.props.style }\n      />\n    );\n  }\n\n  render() {\n    const selectableElements = [\n      'one',\n      'another',\n      'hey there',\n      'item',\n      'two',\n      'three',\n      'something longer?',\n      'last'\n    ];\n    return (\n      <div ref={ (ref) => { this.setState({ ref }); } } className='item-container'>\n        { selectableElements.map((el, index) => (\n          <div\n            key={ el }\n            ref={ this.addElementRef }\n            style={ this.getStyle(index) }\n            className='item'\n          >\n            { el }\n          </div>\n        )) }\n        { this.renderSelection() }\n      </div>\n    );\n  }\n}\n\nExample.PropTypes = {\n  style: PropTypes.object,\n};\n```\n\n## Contributing\n\nOnly edit the files in the `src` folder. I'll update `dist` manually before publishing new versions to npm.\n\nTo run the tests simply run `npm test`. Add tests as you see fit to the `test` folder, they must be called `{string}.test.js`.\n\n## Meta\n\nCopyright &copy; [Alejandro U. Alvarez](https:/aurbano.eu) 2017. MIT Licensed.\n"
  },
  {
    "path": "dist/index.js",
    "content": "\"use strict\";Object.defineProperty(exports,\"__esModule\",{value:true});var _jsx=function(){var REACT_ELEMENT_TYPE=typeof Symbol===\"function\"&&Symbol.for&&Symbol.for(\"react.element\")||60103;return function createRawReactElement(type,props,key,children){var defaultProps=type&&type.defaultProps;var childrenLength=arguments.length-3;if(!props&&childrenLength!==0){props={}}if(props&&defaultProps){for(var propName in defaultProps){if(props[propName]===void 0){props[propName]=defaultProps[propName]}}}else if(!props){props=defaultProps||{}}if(childrenLength===1){props.children=children}else if(childrenLength>1){var childArray=Array(childrenLength);for(var i=0;i<childrenLength;i++){childArray[i]=arguments[i+3]}props.children=childArray}return{$$typeof:REACT_ELEMENT_TYPE,type:type,key:key===undefined?null:\"\"+key,ref:null,props:props,_owner:null}}}();var _createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||false;descriptor.configurable=true;if(\"value\"in descriptor)descriptor.writable=true;Object.defineProperty(target,descriptor.key,descriptor)}}return function(Constructor,protoProps,staticProps){if(protoProps)defineProperties(Constructor.prototype,protoProps);if(staticProps)defineProperties(Constructor,staticProps);return Constructor}}();var _react=require(\"react\");var _react2=_interopRequireDefault(_react);var _propTypes=require(\"prop-types\");var _propTypes2=_interopRequireDefault(_propTypes);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _toConsumableArray(arr){if(Array.isArray(arr)){for(var i=0,arr2=Array(arr.length);i<arr.length;i++){arr2[i]=arr[i]}return arr2}else{return Array.from(arr)}}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError(\"Cannot call a class as a function\")}}function _possibleConstructorReturn(self,call){if(!self){throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\")}return call&&(typeof call===\"object\"||typeof call===\"function\")?call:self}function _inherits(subClass,superClass){if(typeof superClass!==\"function\"&&superClass!==null){throw new TypeError(\"Super expression must either be null or a function, not \"+typeof superClass)}subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,enumerable:false,writable:true,configurable:true}});if(superClass)Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__=superClass}function getOffset(props){var offset={top:0,left:0};if(props.offset){offset=Object.assign({},props.offset)}else if(props.target){var boundingBox=props.target.getBoundingClientRect();offset.top=boundingBox.top+window.scrollY;offset.left=boundingBox.left+window.scrollX}return offset}var Selection=function(_React$PureComponent){_inherits(Selection,_React$PureComponent);function Selection(props){_classCallCheck(this,Selection);var _this=_possibleConstructorReturn(this,(Selection.__proto__||Object.getPrototypeOf(Selection)).call(this,props));_this.bind=function(){_this.props.target.addEventListener(\"mousedown\",_this.onMouseDown);_this.props.target.addEventListener(\"touchstart\",_this.onTouchStart)};_this.reset=function(){if(_this.props.target){_this.props.target.removeEventListener(\"mousedown\",_this.onMouseDown)}};_this.init=function(e,x,y){if(_this.props.ignoreTargets){var Target=e.target;if(!Target.matches){var defaultMatches=function defaultMatches(s){return[].indexOf.call(window.document.querySelectorAll(s),_this)!==-1};Target.matches=Target.matchesSelector||Target.mozMatchesSelector||Target.msMatchesSelector||Target.oMatchesSelector||Target.webkitMatchesSelector||defaultMatches}if(Target.matches&&Target.matches(_this.props.ignoreTargets.join(\",\"))){return false}}var nextState={};nextState.mouseDown=true;nextState.startPoint={x:(x-_this.state.offset.left)/_this.state.zoom,y:(y-_this.state.offset.top)/_this.state.zoom};_this.setState(nextState);return true};_this.onMouseDown=function(e){if(_this.props.disabled||e.button===2||e.nativeEvent&&e.nativeEvent.which===2){return}if(_this.init(e,e.pageX,e.pageY)){window.document.addEventListener(\"mousemove\",_this.onMouseMove);window.document.addEventListener(\"mouseup\",_this.onMouseUp)}};_this.onTouchStart=function(e){if(_this.props.disabled||!e.touches||!e.touches[0]||e.touches.length>1){return}if(_this.init(e,e.touches[0].pageX,e.touches[0].pageY)){window.document.addEventListener(\"touchmove\",_this.onTouchMove);window.document.addEventListener(\"touchend\",_this.onMouseUp)}};_this.onMouseUp=function(){window.document.removeEventListener(\"touchmove\",_this.onTouchMove);window.document.removeEventListener(\"mousemove\",_this.onMouseMove);window.document.removeEventListener(\"mouseup\",_this.onMouseUp);window.document.removeEventListener(\"touchend\",_this.onMouseUp);_this.setState({mouseDown:false,startPoint:null,endPoint:null,selectionBox:null});if(_this.props.onSelectionChange){_this.props.onSelectionChange(_this.selectedChildren)}if(_this.props.onHighlightChange){_this.highlightedChildren=[];_this.props.onHighlightChange(_this.highlightedChildren)}_this.selectedChildren=[]};_this.onMouseMove=function(e){e.preventDefault();if(_this.state.mouseDown){var _endPoint={x:(e.pageX-_this.state.offset.left)/_this.state.zoom,y:(e.pageY-_this.state.offset.top)/_this.state.zoom};_this.setState({endPoint:_endPoint,selectionBox:_this.calculateSelectionBox(_this.state.startPoint,_endPoint)})}};_this.onTouchMove=function(e){e.preventDefault();if(_this.state.mouseDown){var _endPoint2={x:(e.touches[0].pageX-_this.state.offset.left)/_this.state.zoom,y:(e.touches[0].pageY-_this.state.offset.top)/_this.state.zoom};_this.setState({endPoint:_endPoint2,selectionBox:_this.calculateSelectionBox(_this.state.startPoint,_endPoint2)})}};_this.lineIntersects=function(lineA,lineB){return lineA[1]>=lineB[0]&&lineB[1]>=lineA[0]};_this.boxIntersects=function(boxA,boxB){var boxAProjection={x:[boxA.left,boxA.left+boxA.width],y:[boxA.top,boxA.top+boxA.height]};var boxBProjection={x:[boxB.left,boxB.left+boxB.width],y:[boxB.top,boxB.top+boxB.height]};return _this.lineIntersects(boxAProjection.x,boxBProjection.x)&&_this.lineIntersects(boxAProjection.y,boxBProjection.y)};_this.updateCollidingChildren=function(selectionBox){_this.selectedChildren=[];if(_this.props.elements){_this.props.elements.forEach(function(ref,$index){if(ref){var refBox=ref.getBoundingClientRect();var tmpBox={top:(refBox.top-_this.state.offset.top+window.scrollY)/_this.state.zoom,left:(refBox.left-_this.state.offset.left+window.scrollX)/_this.state.zoom,width:ref.clientWidth,height:ref.clientHeight};if(_this.boxIntersects(selectionBox,tmpBox)){_this.selectedChildren.push($index)}}})}if(_this.props.onHighlightChange&&JSON.stringify(_this.highlightedChildren)!==JSON.stringify(_this.selectedChildren)){var _onHighlightChange=_this.props.onHighlightChange;_this.highlightedChildren=[].concat(_toConsumableArray(_this.selectedChildren));if(window.requestAnimationFrame){window.requestAnimationFrame(function(){_onHighlightChange(_this.highlightedChildren)})}else{_onHighlightChange(_this.highlightedChildren)}}};_this.calculateSelectionBox=function(startPoint,endPoint){if(!_this.state.mouseDown||!startPoint||!endPoint){return null}var left=Math.min(startPoint.x,endPoint.x)-1;var top=Math.min(startPoint.y,endPoint.y)-1;var width=Math.abs(startPoint.x-endPoint.x)+1;var height=Math.abs(startPoint.y-endPoint.y)+1;return{left:left,top:top,width:width,height:height}};_this.state={mouseDown:false,startPoint:null,endPoint:null,selectionBox:null,offset:getOffset(props),zoom:props.zoom||1};_this.selectedChildren=[];_this.highlightedChildren=[];return _this}_createClass(Selection,[{key:\"componentDidMount\",value:function componentDidMount(){this.reset();this.bind()}},{key:\"componentWillReceiveProps\",value:function componentWillReceiveProps(nextProps){this.setState({offset:getOffset(nextProps)})}},{key:\"componentDidUpdate\",value:function componentDidUpdate(){this.reset();this.bind();if(this.state.mouseDown&&this.state.selectionBox){this.updateCollidingChildren(this.state.selectionBox)}}},{key:\"componentWillUnmount\",value:function componentWillUnmount(){this.reset();window.document.removeEventListener(\"mousemove\",this.onMouseMove);window.document.removeEventListener(\"mouseup\",this.onMouseUp)}},{key:\"render\",value:function render(){var style=Object.assign({position:\"absolute\",background:\"rgba(159, 217, 255, 0.3)\",border:\"solid 1px rgba(123, 123, 123, 0.61)\",zIndex:9,cursor:\"crosshair\"},this.props.style);if(this.state.selectionBox){style=Object.assign({},style,this.state.selectionBox)}if(!this.state.mouseDown||!this.state.endPoint||!this.state.startPoint){return null}return _jsx(\"div\",{className:\"react-ds-border\",style:style})}}]);return Selection}(_react2.default.PureComponent);exports.default=Selection;"
  },
  {
    "path": "dist/index.js.flow",
    "content": "// @flow\n\nimport React from 'react';\nimport PropTypes from 'prop-types';\n\nexport type Point = {\n  x: number,\n  y: number,\n}\n\nexport type Box = {\n  left: number,\n  top: number,\n  width: number,\n  height: number,\n}\n\ntype Props = {\n  disabled?: boolean,\n  target: HTMLElement,\n  onSelectionChange?: (elements: Array<any>) => void,\n  onHighlightChange?: (elements: Array<any>) => void,\n  elements: Array<HTMLElement>,\n  // eslint-disable-next-line react/no-unused-prop-types\n  offset?: {\n    // eslint-disable-next-line react/no-unused-prop-types\n    top: number,\n    // eslint-disable-next-line react/no-unused-prop-types\n    left: number,\n  },\n  style?: any,\n  zoom?: number,\n  ignoreTargets?: Array<string>,\n};\n\ntype State = {\n  mouseDown: boolean,\n  startPoint: ?Point,\n  endPoint: ?Point,\n  selectionBox: ?Box,\n  offset: {\n    top: number,\n    left: number,\n  },\n  zoom: number,\n};\n\nfunction getOffset(props: Props) {\n  let offset = {\n    top: 0,\n    left: 0,\n  };\n  if (props.offset) {\n    offset = {\n      ...props.offset,\n    };\n  } else if (props.target) {\n    const boundingBox = props.target.getBoundingClientRect();\n    offset.top = boundingBox.top + window.scrollY;\n    offset.left = boundingBox.left + window.scrollX;\n  }\n  return offset;\n}\n\nexport default class Selection extends React.PureComponent<Props, State> { // eslint-disable-line react/prefer-stateless-function\n  props: Props;\n  state: State;\n  selectedChildren: Array<number>;\n  highlightedChildren: Array<number>;\n\n  constructor(props: Props) {\n    super(props);\n\n    this.state = {\n      mouseDown: false,\n      startPoint: null,\n      endPoint: null,\n      selectionBox: null,\n      offset: getOffset(props),\n      zoom: props.zoom || 1,\n    };\n\n    this.selectedChildren = [];\n    this.highlightedChildren = [];\n  }\n\n  componentDidMount() {\n    this.reset();\n    this.bind();\n  }\n\n  componentWillReceiveProps(nextProps: Props) {\n    this.setState({\n      offset: getOffset(nextProps),\n    });\n  }\n\n  componentDidUpdate() {\n    this.reset();\n    this.bind();\n    if (this.state.mouseDown && this.state.selectionBox) {\n      this.updateCollidingChildren(this.state.selectionBox);\n    }\n  }\n\n  componentWillUnmount() {\n    this.reset();\n    window.document.removeEventListener('mousemove', this.onMouseMove);\n    window.document.removeEventListener('mouseup', this.onMouseUp);\n  }\n\n  bind = () => {\n    this.props.target.addEventListener('mousedown', this.onMouseDown);\n    this.props.target.addEventListener('touchstart', this.onTouchStart);\n  };\n\n  reset = () => {\n    if (this.props.target) {\n      this.props.target.removeEventListener('mousedown', this.onMouseDown);\n    }\n  };\n\n  init = (e: Event, x: number, y: number): boolean => {\n    if (this.props.ignoreTargets) {\n      const Target = (e.target: any);\n      if (!Target.matches) {\n        // polyfill matches\n        const defaultMatches = (s: string) => (\n          [].indexOf.call(window.document.querySelectorAll(s), this) !== -1\n        );\n        Target.matches =\n          Target.matchesSelector ||\n          Target.mozMatchesSelector ||\n          Target.msMatchesSelector ||\n          Target.oMatchesSelector ||\n          Target.webkitMatchesSelector ||\n          defaultMatches;\n      }\n      if (Target.matches && Target.matches(this.props.ignoreTargets.join(','))) {\n        return false;\n      }\n    }\n    const nextState = {};\n\n\n    nextState.mouseDown = true;\n    nextState.startPoint = {\n      x: (x - this.state.offset.left) / this.state.zoom,\n      y: (y - this.state.offset.top) / this.state.zoom,\n    };\n\n    this.setState(nextState);\n    return true;\n  };\n\n  /**\n   * On root element mouse down\n   * The event should be a MouseEvent | TouchEvent, but flow won't get it...\n   * @private\n   */\n  onMouseDown = (e: MouseEvent | any) => {\n    if (this.props.disabled || e.button === 2 || (e.nativeEvent && e.nativeEvent.which === 2)) {\n      return;\n    }\n\n    if (this.init(e, e.pageX, e.pageY)) {\n      window.document.addEventListener('mousemove', this.onMouseMove);\n      window.document.addEventListener('mouseup', this.onMouseUp);\n    }\n  };\n\n  onTouchStart = (e: TouchEvent) => {\n    if (this.props.disabled || !e.touches || !e.touches[0] || e.touches.length > 1) {\n      return;\n    }\n\n    if (this.init(e, e.touches[0].pageX, e.touches[0].pageY)) {\n      window.document.addEventListener('touchmove', this.onTouchMove);\n      window.document.addEventListener('touchend', this.onMouseUp);\n    }\n  };\n\n  /**\n   * On document element mouse up\n   * @private\n   */\n  onMouseUp = () => {\n    window.document.removeEventListener('touchmove', this.onTouchMove);\n    window.document.removeEventListener('mousemove', this.onMouseMove);\n    window.document.removeEventListener('mouseup', this.onMouseUp);\n    window.document.removeEventListener('touchend', this.onMouseUp);\n\n    this.setState({\n      mouseDown: false,\n      startPoint: null,\n      endPoint: null,\n      selectionBox: null,\n    });\n\n    if (this.props.onSelectionChange) {\n      this.props.onSelectionChange(this.selectedChildren);\n    }\n\n    if (this.props.onHighlightChange) {\n      this.highlightedChildren = [];\n      this.props.onHighlightChange(this.highlightedChildren);\n    }\n    this.selectedChildren = [];\n  };\n\n  /**\n   * On document element mouse move\n   * @private\n   */\n  onMouseMove = (e: MouseEvent) => {\n    e.preventDefault();\n    if (this.state.mouseDown) {\n      const endPoint: Point = {\n        x: (e.pageX - this.state.offset.left) / this.state.zoom,\n        y: (e.pageY - this.state.offset.top) / this.state.zoom,\n      };\n\n      this.setState({\n        endPoint,\n        selectionBox: this.calculateSelectionBox(\n          this.state.startPoint,\n          endPoint\n        ),\n      });\n    }\n  };\n\n  onTouchMove = (e: TouchEvent) => {\n    e.preventDefault();\n    if (this.state.mouseDown) {\n      const endPoint: Point = {\n        x: (e.touches[0].pageX - this.state.offset.left) / this.state.zoom,\n        y: (e.touches[0].pageY - this.state.offset.top) / this.state.zoom,\n      };\n\n      this.setState({\n        endPoint,\n        selectionBox: this.calculateSelectionBox(\n          this.state.startPoint,\n          endPoint\n        ),\n      });\n    }\n  };\n\n  /**\n   * Calculate if two segments overlap in 1D\n   * @param lineA [min, max]\n   * @param lineB [min, max]\n   */\n  lineIntersects = (lineA: [number, number], lineB: [number, number]): boolean => (\n    lineA[1] >= lineB[0] && lineB[1] >= lineA[0]\n  );\n\n  /**\n   * Detect 2D box intersection - the two boxes will intersect\n   * if their projections to both axis overlap\n   * @private\n   */\n  boxIntersects = (boxA: Box, boxB: Box): boolean => {\n    // calculate coordinates of all points\n    const boxAProjection = {\n      x: [boxA.left, boxA.left + boxA.width],\n      y: [boxA.top, boxA.top + boxA.height],\n    };\n\n    const boxBProjection = {\n      x: [boxB.left, boxB.left + boxB.width],\n      y: [boxB.top, boxB.top + boxB.height],\n    };\n\n    return this.lineIntersects(boxAProjection.x, boxBProjection.x) &&\n           this.lineIntersects(boxAProjection.y, boxBProjection.y);\n  };\n\n  /**\n   * Updates the selected items based on the\n   * collisions with selectionBox,\n   * also updates the highlighted items if they have changed\n   * @private\n   */\n  updateCollidingChildren = (selectionBox: Box) => {\n    this.selectedChildren = [];\n    if (this.props.elements) {\n      this.props.elements.forEach((ref, $index) => {\n        if (ref) {\n          const refBox = ref.getBoundingClientRect();\n          const tmpBox = {\n            top: ((refBox.top - this.state.offset.top) + window.scrollY) / this.state.zoom,\n            left: ((refBox.left - this.state.offset.left) + window.scrollX) / this.state.zoom,\n            width: ref.clientWidth,\n            height: ref.clientHeight,\n          };\n\n          if (this.boxIntersects(selectionBox, tmpBox)) {\n            this.selectedChildren.push($index);\n          }\n        }\n      });\n    }\n    if (this.props.onHighlightChange && JSON.stringify(this.highlightedChildren) !== JSON.stringify(this.selectedChildren)) {\n      const { onHighlightChange } = this.props;\n      this.highlightedChildren = [...this.selectedChildren];\n      if (window.requestAnimationFrame) {\n        window.requestAnimationFrame(() => {\n          onHighlightChange(this.highlightedChildren);\n        });\n      } else {\n        onHighlightChange(this.highlightedChildren);\n      }\n    }\n  };\n\n  /**\n   * Calculate selection box dimensions\n   * @private\n   */\n  calculateSelectionBox = (startPoint: ?Point, endPoint: ?Point) => {\n    if (!this.state.mouseDown || !startPoint || !endPoint) {\n      return null;\n    }\n\n    // The extra 1 pixel is to ensure that the mouse is on top\n    // of the selection box and avoids triggering clicks on the target.\n    const left = Math.min(startPoint.x, endPoint.x) - 1;\n    const top = Math.min(startPoint.y, endPoint.y) - 1;\n    const width = Math.abs(startPoint.x - endPoint.x) + 1;\n    const height = Math.abs(startPoint.y - endPoint.y) + 1;\n\n    return {\n      left,\n      top,\n      width,\n      height,\n    };\n  };\n\n  /**\n   * Render\n   */\n  render() {\n    let style: any = {\n      position: 'absolute',\n      background: 'rgba(159, 217, 255, 0.3)',\n      border: 'solid 1px rgba(123, 123, 123, 0.61)',\n      zIndex: 9,\n      cursor: 'crosshair',\n      ...this.props.style,\n    };\n\n    if (this.state.selectionBox) {\n      style = {\n        ...style,\n        ...this.state.selectionBox,\n      };\n    }\n\n    if (!this.state.mouseDown || !this.state.endPoint || !this.state.startPoint) {\n      return null;\n    }\n    return (\n      <div className='react-ds-border' style={ style } />\n    );\n  }\n}\n\nSelection.propTypes = {\n  target: PropTypes.object,\n  disabled: PropTypes.bool,\n  onSelectionChange: PropTypes.func.isRequired,\n  onHighlightChange: PropTypes.func,\n  elements: PropTypes.array.isRequired,\n  // eslint-disable-next-line react/no-unused-prop-types\n  offset: PropTypes.object,\n  zoom: PropTypes.number,\n  style: PropTypes.object,\n  ignoreTargets: PropTypes.array,\n};\n"
  },
  {
    "path": "example/app/.gitignore",
    "content": "# See https://help.github.com/ignore-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n\n# testing\n/coverage\n\n# production\n/build\n\n# misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n"
  },
  {
    "path": "example/app/README.md",
    "content": "Example application using `create-react-app`.\n\n## NPM Commands\n\n* `npm start` - to run locally\n* `npm build` - to generate a build\n\nUnless you're developing the component here you probably shouldn't be editing this."
  },
  {
    "path": "example/app/package.json",
    "content": "{\n  \"name\": \"app\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"homepage\": \"https://aurbano.eu/react-ds/\",\n  \"dependencies\": {\n    \"react\": \"^16.0.0\",\n    \"react-dom\": \"^16.0.0\",\n    \"react-ds\": \"file:../../\",\n    \"react-scripts\": \"1.0.14\"\n  },\n  \"scripts\": {\n    \"build:less\": \"./node_modules/.bin/lessc src/example.less src/example.css\",\n    \"start\": \"npm run build:less && react-scripts start\",\n    \"build\": \"npm run build:less && react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\",\n    \"postinstall\": \"linklocal\"\n  },\n  \"devDependencies\": {\n    \"less\": \"^2.7.2\"\n  }\n}\n"
  },
  {
    "path": "example/app/public/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"initial-scale=1.0,user-scalable=no,maximum-scale=1,width=device-width\">\n    <!--<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">-->\n    <meta name=\"keywords\" content=\"react,reactjs,react component,component,select,control,ui,javascript,drag,selection,items\">\n    <meta name=\"description\" content=\"Simple React Drag-to-Select component with mouse and touch support, and 0 dependencies\">\n    <meta property=\"og:locale\" content=\"en-us\">\n    <meta property=\"og:title\" content=\"React-DS\">\n    <meta property=\"og:description\" content=\"Simple React Drag-to-Select component with mouse and touch support, and 0 dependencies\">\n    <meta property=\"og:url\" content=\"https://aurbano.eu/react-ds\">\n    <meta property=\"og:site_name\" content=\"React-DS\">\n    <meta property=\"og:type\" content=\"article\">\n    <!--\n      manifest.json provides metadata used when your web app is added to the\n      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/\n    -->\n    <link rel=\"manifest\" href=\"%PUBLIC_URL%/manifest.json\">\n    <link rel=\"shortcut icon\" href=\"%PUBLIC_URL%/favicon.ico\">\n    <!--\n      Notice the use of %PUBLIC_URL% in the tags above.\n      It will be replaced with the URL of the `public` folder during the build.\n      Only files inside the `public` folder can be referenced from the HTML.\n\n      Unlike \"/favicon.ico\" or \"favicon.ico\", \"%PUBLIC_URL%/favicon.ico\" will\n      work correctly both with client-side routing and a non-root public URL.\n      Learn how to configure a non-root public URL by running `npm run build`.\n    -->\n    <title>React DS Example</title>\n  </head>\n  <body>\n    <noscript>\n      You need to enable JavaScript to run this app.\n    </noscript>\n    <header class=\"page-header\">\n      <div class=\"container\">\n        <h1>React DS</h1>\n        <p>Tiny (7KB) <a href=\"http://facebook.github.io/react/\" target=\"_blank\">ReactJS</a> Drag-to-Select component (with no dependencies! with support for touch devices!</p>\n      </div>\n    </header>\n    <div class=\"page-subheader\">\n      <div class=\"container\">\n        <a href=\"http://github.com/aurbano/react-ds\" class=\"page-subheader__link\" target=\"_blank\">Code and Docs on GitHub</a>\n        <span class=\"page-subheader__button\">\n\t\t\t\t<a id=\"github-stars-button\" class=\"github-button\" data-style=\"\" href=\"https://github.com/aurbano/react-ds\" data-count-href=\"/aurbano/react-ds/stargazers\" data-count-api=\"/repos/aurbano/react-ds#stargazers_count\" data-count-aria-label=\"# stargazers on GitHub\" aria-label=\"Star aurbano/react-ds on GitHub\">Star</a>\n\t\t\t</span>\n      </div>\n    </div>\n    <div class=\"page-body\">\n      <div class=\"container\">\n        <form>\n          <!-- the React application is loaded in the #root element -->\n          <div id=\"root\"></div>\n        </form>\n      </div>\n    </div>\n    <footer class=\"page-footer\">\n      <div class=\"container\">\n        <span>Copyright &copy; <a href=\"https://aurbano.eu\" target=\"_blank\">Alejandro U. Alvarez</a> 2016. MIT Licensed.</span>\n      </div>\n    </footer>\n    <!--\n      This HTML file is a template.\n      If you open it directly in the browser, you will see an empty page.\n\n      You can add webfonts, meta tags, or analytics to this file.\n      The build step will place the bundled scripts into the <body> tag.\n\n      To begin the development, run `npm start` or `yarn start`.\n      To create a production bundle, use `npm run build` or `yarn build`.\n    -->\n    <script async defer id=\"github-bjs\" src=\"https://buttons.github.io/buttons.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "example/app/public/manifest.json",
    "content": "{\n  \"short_name\": \"React DS\",\n  \"name\": \"React DS Example\",\n  \"icons\": [\n    {\n      \"src\": \"favicon.ico\",\n      \"sizes\": \"192x192\",\n      \"type\": \"image/png\"\n    }\n  ],\n  \"start_url\": \"./index.html\",\n  \"display\": \"standalone\",\n  \"theme_color\": \"#000000\",\n  \"background_color\": \"#ffffff\"\n}\n"
  },
  {
    "path": "example/app/src/Example.js",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport Selection from 'react-ds';\n\nexport default class Example extends React.PureComponent {\n\n  constructor() {\n    super();\n\n    this.state = {\n      ref: null,\n      elRefs: [],\n      selectedElements: [], // track the elements that are selected\n    };\n  }\n\n  handleSelection = (indexes) => { // eslint-disable-line no-undef\n    this.setState({\n      selectedElements: indexes,\n    });\n  };\n\n  getStyle = (index) => { // eslint-disable-line no-undef\n    if (this.state.selectedElements.indexOf(index) > -1) {\n      // Selected state\n      return {\n        background: '#2185d0',\n        borderColor: '#2185d0',\n        color: 'white',\n      };\n    }\n    return {};\n  };\n\n  addElementRef = (ref) => { // eslint-disable-line no-undef\n    const elRefs = this.state.elRefs;\n    elRefs.push(ref);\n    this.setState({\n      elRefs,\n    });\n  };\n\n  renderSelection() {\n    if (!this.state.ref || !this.state.elRefs) {\n      return null;\n    }\n    return (\n      <Selection\n        target={ this.state.ref}\n        elements={ this.state.elRefs }\n        onSelectionChange={ this.handleSelection }\n        style={ this.props.style }\n        ignoreTargets={ this.props.ignoreTargets }\n      />\n    );\n  }\n\n  render() {\n    const selectableElements = [\n      'one',\n      'another',\n      'hey there',\n      'item',\n      'two',\n      'three',\n      'something longer?',\n      'last'\n    ];\n    return (\n      <div ref={ (ref) => { this.setState({ ref }); } } className='item-container'>\n        { selectableElements.map((el, index) => (\n          <div\n            key={ el }\n            ref={ this.addElementRef }\n            style={ this.getStyle(index) }\n            className={ `item item-${el}` }\n          >\n            { el }\n          </div>\n        )) }\n        { this.renderSelection() }\n      </div>\n    );\n  }\n}\n\nExample.PropTypes = {\n  style: PropTypes.object,\n  ignoreTargets: PropTypes.array,\n};"
  },
  {
    "path": "example/app/src/Examples.js",
    "content": "import React from 'react';\nimport Example from './Example';\n\nexport default class Examples extends React.PureComponent {\n\n  render() {\n    return (\n      <div>\n        <h2>\n          <a href=\"https://github.com/aurbano/react-ds/blob/master/example/app/src/Example.js\" className=\"right\">\n            <code>&lt;Source&gt;</code>\n          </a>\n          Example\n        </h2>\n\n        <p>The box below is the <code>target</code> for the Selection component. You'll see that you can't initiate selections outside of it.</p>\n\n        <Example />\n\n        <h2 style={ { marginTop: '2em' } }>Custom styles</h2>\n\n        <p>Here's an example using custom styles for the selection box. These are simply passed on the <code>style</code> prop of the <code>Selection</code> component.</p>\n\n        <Example style={ { background: 'rgba(0,0,0,0.3)' } } />\n\n        <h2 style={ { marginTop: '2em' } }>Ignore certain targets</h2>\n\n        <p>You can also ignore certain targets from initiating selects. This is specially useful because <code>react-ds</code> uses native events which bypass React's synthetic event queue.</p>\n\n        <p>In this example, events initiated over the item <b>\"three\"</b> are ignored.</p>\n\n        <Example ignoreTargets={ ['.item-three'] }/>\n      </div>\n    );\n  }\n}"
  },
  {
    "path": "example/app/src/example.css",
    "content": "body {\n  color: #333;\n  font-family: Helvetica Neue, Helvetica, Arial, sans-serif;\n  font-size: 14px;\n  line-height: 1.4;\n  margin: 0;\n  padding: 0;\n}\na {\n  color: #007eff;\n  text-decoration: none;\n}\na:hover {\n  text-decoration: underline;\n}\ncode {\n  padding: 0.2em 0.5em;\n  margin: 0;\n  font-size: 85%;\n  background-color: rgba(27, 31, 35, 0.05);\n  border-radius: 3px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, Courier, monospace;\n}\n.container {\n  margin-left: auto;\n  margin-right: auto;\n  max-width: 600px;\n  padding: 0 30px;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n  color: black;\n  font-weight: 500;\n  line-height: 1;\n  margin-bottom: .66em;\n  margin-top: 0;\n}\nh1 .right,\nh2 .right,\nh3 .right,\nh4 .right,\nh5 .right,\nh6 .right,\n.h1 .right,\n.h2 .right,\n.h3 .right,\n.h4 .right,\n.h5 .right,\n.h6 .right {\n  font-size: 0.6em;\n}\nh1,\n.h1 {\n  font-size: 3em;\n}\nh2,\n.h2 {\n  font-size: 2em;\n  font-weight: 300;\n}\nh3,\n.h3 {\n  font-size: 1.25em;\n}\nh4,\n.h4 {\n  font-size: 1em;\n}\nh5,\n.h5 {\n  font-size: .85em;\n}\nh6,\n.h6 {\n  font-size: .75em;\n}\n.page-body,\n.page-footer,\n.page-header {\n  padding: 30px 0;\n}\n.page-header {\n  background: linear-gradient(135deg, #F97794 0%, #623AA2 100%);\n  color: #d8cee8;\n}\n.page-header h1,\n.page-header h2,\n.page-header h3 {\n  color: white;\n}\n.page-header p {\n  font-size: 1.2em;\n  margin: 0;\n}\n.page-header a {\n  border-bottom: 1px solid rgba(255, 255, 255, 0.3);\n  color: white;\n  text-decoration: none;\n}\n.page-header a:hover,\n.page-header a:focus {\n  border-bottom-color: white;\n  outline: none;\n  text-decoration: none;\n}\n.right {\n  float: right;\n  position: relative;\n  bottom: -0.2em;\n}\n.page-subheader {\n  background-color: #e6f2ff;\n  line-height: 20px;\n  padding: 30px 0;\n}\n.page-subheader__button {\n  float: right;\n}\n.page-subheader__link {\n  border-bottom: 1px solid rgba(0, 126, 255, 0.3);\n  outline: none;\n  text-decoration: none;\n}\n.page-subheader__link:hover,\n.page-subheader__link:focus {\n  border-bottom-color: #007eff;\n  outline: none;\n  text-decoration: none;\n}\n.page-footer {\n  background-color: #fafafa;\n  color: #999;\n  padding: 30px 0;\n  text-align: center;\n}\n.page-footer a {\n  color: black;\n}\n@media (min-width: 480px) {\n  .page-body,\n  .page-header {\n    padding: 60px 0;\n  }\n  .page-header {\n    font-size: 1.4em;\n  }\n  .page-subheader {\n    font-size: 1.125em;\n    line-height: 28px;\n  }\n}\n.switcher {\n  color: #999;\n  cursor: default;\n  font-size: 12px;\n  margin: 10px 0;\n  text-transform: uppercase;\n}\n.switcher .link {\n  color: #007eff;\n  cursor: pointer;\n  font-weight: bold;\n  margin-left: 10px;\n}\n.switcher .link:hover {\n  text-decoration: underline;\n}\n.switcher .active {\n  color: #666;\n  font-weight: bold;\n  margin-left: 10px;\n}\n.section {\n  margin-bottom: 40px;\n}\n.hint {\n  font-size: .85em;\n  margin: 15px 0;\n  color: #666;\n}\n.virtual-scroll {\n  z-index: 1;\n}\n"
  },
  {
    "path": "example/app/src/example.less",
    "content": "//\n// Common Example Styles\n// ------------------------------\n\n\n// Constants\n// ------------------------------\n\n// example site\n\n@heading-color:                  black;\n@text-color:                     #333;\n@link-color:                     #007eff;\n@gutter:                         30px;\n@headerStart: #F97794;\n@headerEnd: #623AA2;\n@headerBackground: linear-gradient( 135deg, @headerStart 0%, @headerEnd 100%);\n\n\n\n\n// Base\n// ------------------------------\n\nbody {\n  color: @text-color;\n  font-family: Helvetica Neue, Helvetica, Arial, sans-serif;\n  font-size: 14px;\n  line-height: 1.4;\n  margin: 0;\n  padding: 0;\n}\n\na {\n  color: @link-color;\n  text-decoration: none;\n\n  &:hover {\n    text-decoration: underline;\n  }\n}\n\ncode {\n  padding: 0.2em 0.5em;\n  margin: 0;\n  font-size: 85%;\n  background-color: rgba(27,31,35,0.05);\n  border-radius: 3px;\n  font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, Courier, monospace;\n}\n\n.container {\n  margin-left: auto;\n  margin-right: auto;\n  max-width: 600px;\n  padding: 0 @gutter;\n}\n\n\n\n\n// Headings\n// ------------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n  color: @heading-color;\n  font-weight: 500;\n  line-height: 1;\n  margin-bottom: .66em;\n  margin-top: 0;\n\n  .right {\n    font-size: 0.6em;\n  }\n}\n\nh1, .h1 {\n  font-size: 3em;\n}\nh2, .h2 {\n  font-size: 2em;\n  font-weight: 300;\n}\nh3, .h3 {\n  font-size: 1.25em;\n  // text-transform: uppercase;\n}\nh4, .h4 {\n  font-size: 1em;\n}\nh5, .h5 {\n  font-size: .85em;\n}\nh6, .h6 {\n  font-size: .75em;\n}\n\n\n\n\n// Layout\n// ------------------------------\n\n// common\n\n.page-body,\n.page-footer,\n.page-header {\n  padding: @gutter 0;\n}\n\n// header\n\n.page-header {\n  background: @headerBackground;\n  color: mix(white, @headerEnd, 75%);\n\n  h1, h2, h3 {\n    color: white;\n  }\n  p {\n    font-size: 1.2em;\n    margin: 0;\n  }\n  a {\n    border-bottom: 1px solid fade(white, 30%);\n    color: white;\n    text-decoration: none;\n\n    &:hover,\n    &:focus {\n      border-bottom-color: white;\n      outline: none;\n      text-decoration: none;\n    }\n  }\n}\n\n.right {\n  float: right;\n  position: relative;\n  bottom: -0.2em;\n}\n\n// subheader\n\n.page-subheader {\n  background-color: mix(@link-color, white, 10%);\n  line-height: 20px;\n  padding: @gutter 0;\n}\n.page-subheader__button {\n  float: right;\n}\n.page-subheader__link {\n  border-bottom: 1px solid fade(@link-color, 30%);\n  outline: none;\n  text-decoration: none;\n\n  &:hover,\n  &:focus {\n    border-bottom-color: @link-color;\n    outline: none;\n    text-decoration: none;\n  }\n}\n\n// footer\n\n.page-footer {\n  background-color: #fafafa;\n  // border-top: 1px solid #eee;\n  color: #999;\n  padding: @gutter 0;\n  text-align: center;\n\n  a {\n    color: black;\n  }\n}\n\n// layout changes based on screen dimensions\n\n@media (min-width: 480px) {\n  .page-body,\n  .page-header {\n    padding: (@gutter * 2) 0;\n  }\n  .page-header {\n    font-size: 1.4em;\n  }\n  .page-subheader {\n    font-size: 1.125em;\n    line-height: 28px;\n  }\n}\n\n\n\n\n\n// Switcher\n// ------------------------------\n\n.switcher {\n  color: #999;\n  cursor: default;\n  font-size: 12px;\n  margin: 10px 0;\n  text-transform: uppercase;\n\n  .link {\n    color: @link-color;\n    cursor: pointer;\n    font-weight: bold;\n    margin-left: 10px;\n\n    &:hover {\n      text-decoration: underline;\n    }\n  }\n  .active {\n    color: #666;\n    font-weight: bold;\n    margin-left: 10px;\n  }\n}\n\n\n\n\n// Miscellaneous\n// ------------------------------\n\n.section {\n  margin-bottom: 40px;\n}\n\n.hint {\n  font-size: .85em;\n  margin: 15px 0;\n  color: #666;\n}\n\n.virtual-scroll {\n  z-index: 1;\n}"
  },
  {
    "path": "example/app/src/index.js",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './example.css';\nimport './react-ds.css';\nimport Examples from './Examples';\nimport registerServiceWorker from './registerServiceWorker';\n\nReactDOM.render(<Examples />, document.getElementById('root'));\nregisterServiceWorker();\n"
  },
  {
    "path": "example/app/src/react-ds.css",
    "content": ".item-container {\n    border: solid 1px #ccc;\n    position: relative;\n    box-shadow: 0 1px 2px 0 rgba(34,36,38,.15);\n    margin: 1rem 0;\n    padding: 1em 1em;\n    border-radius: .28571429rem;\n}\n\n.item {\n    display: inline-block;\n    min-width: 40px;\n    min-height: 40px;\n    border: solid 1px #ccc;\n    text-align: center;\n    padding: 1em;\n    margin: 1em;\n    border-radius: .28571429rem;\n}"
  },
  {
    "path": "example/app/src/registerServiceWorker.js",
    "content": "// In production, we register a service worker to serve assets from local cache.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on the \"N+1\" visit to a page, since previously\n// cached resources are updated in the background.\n\n// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.\n// This link also includes instructions on opting out of this behavior.\n\nconst isLocalhost = Boolean(\n  window.location.hostname === 'localhost' ||\n    // [::1] is the IPv6 localhost address.\n    window.location.hostname === '[::1]' ||\n    // 127.0.0.1/8 is considered localhost for IPv4.\n    window.location.hostname.match(\n      /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n    )\n);\n\nexport default function register() {\n  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n    // The URL constructor is available in all browsers that support SW.\n    const publicUrl = new URL(process.env.PUBLIC_URL, window.location);\n    if (publicUrl.origin !== window.location.origin) {\n      // Our service worker won't work if PUBLIC_URL is on a different origin\n      // from what our page is served on. This might happen if a CDN is used to\n      // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374\n      return;\n    }\n\n    window.addEventListener('load', () => {\n      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n      if (!isLocalhost) {\n        // Is not local host. Just register service worker\n        registerValidSW(swUrl);\n      } else {\n        // This is running on localhost. Lets check if a service worker still exists or not.\n        checkValidServiceWorker(swUrl);\n      }\n    });\n  }\n}\n\nfunction registerValidSW(swUrl) {\n  navigator.serviceWorker\n    .register(swUrl)\n    .then(registration => {\n      registration.onupdatefound = () => {\n        const installingWorker = registration.installing;\n        installingWorker.onstatechange = () => {\n          if (installingWorker.state === 'installed') {\n            if (navigator.serviceWorker.controller) {\n              // At this point, the old content will have been purged and\n              // the fresh content will have been added to the cache.\n              // It's the perfect time to display a \"New content is\n              // available; please refresh.\" message in your web app.\n              console.log('New content is available; please refresh.');\n            } else {\n              // At this point, everything has been precached.\n              // It's the perfect time to display a\n              // \"Content is cached for offline use.\" message.\n              console.log('Content is cached for offline use.');\n            }\n          }\n        };\n      };\n    })\n    .catch(error => {\n      console.error('Error during service worker registration:', error);\n    });\n}\n\nfunction checkValidServiceWorker(swUrl) {\n  // Check if the service worker can be found. If it can't reload the page.\n  fetch(swUrl)\n    .then(response => {\n      // Ensure service worker exists, and that we really are getting a JS file.\n      if (\n        response.status === 404 ||\n        response.headers.get('content-type').indexOf('javascript') === -1\n      ) {\n        // No service worker found. Probably a different app. Reload the page.\n        navigator.serviceWorker.ready.then(registration => {\n          registration.unregister().then(() => {\n            window.location.reload();\n          });\n        });\n      } else {\n        // Service worker found. Proceed as normal.\n        registerValidSW(swUrl);\n      }\n    })\n    .catch(() => {\n      console.log(\n        'No internet connection found. App is running in offline mode.'\n      );\n    });\n}\n\nexport function unregister() {\n  if ('serviceWorker' in navigator) {\n    navigator.serviceWorker.ready.then(registration => {\n      registration.unregister();\n    });\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-ds\",\n  \"version\": \"1.13.0\",\n  \"description\": \"Simple React Drag-to-Select component\",\n  \"main\": \"dist/index.js\",\n  \"files\": [\n    \"dist\"\n  ],\n  \"scripts\": {\n    \"install:example\": \"cd example/app && npm install\",\n    \"build\": \"npm run build:clean && cross-env NODE_ENV=production npm run build:dist && npm run build:flow && npm run build:minify\",\n    \"build:dev\": \"npm run build:clean && cross-env NODE_ENV=production npm run build:dist && npm run build:flow\",\n    \"build:clean\": \"rimraf dist\",\n    \"build:dist\": \"babel -d dist src --ignore '**/__tests__/**'\",\n    \"build:flow\": \"flow-copy-source -v -i '**/__tests__/**' src dist\",\n    \"build:minify\": \"./node_modules/.bin/uglifyjs dist/index.js -o dist/index.js\",\n    \"lint\": \"npm run lint:js\",\n    \"lint:eslint\": \"eslint\",\n    \"lint:js\": \"npm run lint:eslint -- . \",\n    \"lint:fix\": \"npm run lint:eslint -- --fix . \",\n    \"lint:staged\": \"lint-staged && npm run flow\",\n    \"pretest\": \"npm run test:clean && npm run lint && npm run flow\",\n    \"test:clean\": \"rimraf ./coverage\",\n    \"test\": \"cross-env NODE_ENV=test jest --coverage\",\n    \"test:watch\": \"cross-env NODE_ENV=test jest --watchAll\",\n    \"flow\": \"flow status\",\n    \"changelog\": \"github_changelog_generator -u aurbano -p react-ds\",\n    \"start\": \"cd example/app && npm start\",\n    \"deploy\": \"cd example/app && npm run build && gh-pages -d build\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/aurbano/react-ds.git\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"select\",\n    \"drag\",\n    \"component\",\n    \"library\",\n    \"items\",\n    \"selection\"\n  ],\n  \"author\": \"Alejandro U. Alvarez\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/aurbano/react-ds/issues\"\n  },\n  \"homepage\": \"https://github.com/aurbano/react-ds#readme\",\n  \"peerDependencies\": {\n    \"react\": \"^16.3\"\n  },\n  \"devDependencies\": {\n    \"babel-cli\": \"6.26.0\",\n    \"babel-core\": \"6.26.3\",\n    \"babel-eslint\": \"8.2.2\",\n    \"babel-jest\": \"^23.0.0-alpha.0\",\n    \"babel-plugin-dynamic-import-node\": \"^1.2.0\",\n    \"babel-plugin-flow-react-proptypes\": \"^12.1.0\",\n    \"babel-plugin-flow-runtime\": \"^0.17.0\",\n    \"babel-plugin-react-intl\": \"^2.4.0\",\n    \"babel-plugin-react-transform\": \"3.0.0\",\n    \"babel-plugin-transform-es2015-destructuring\": \"^6.23.0\",\n    \"babel-plugin-transform-es2015-modules-commonjs\": \"^6.26.2\",\n    \"babel-plugin-transform-object-rest-spread\": \"^6.26.0\",\n    \"babel-plugin-transform-react-constant-elements\": \"^6.23.0\",\n    \"babel-plugin-transform-react-inline-elements\": \"^6.22.0\",\n    \"babel-plugin-transform-react-jsx-source\": \"^6.22.0\",\n    \"babel-plugin-transform-react-remove-prop-types\": \"^0.4.24\",\n    \"babel-preset-env\": \"^1.7.0\",\n    \"babel-preset-flow\": \"^6.23.0\",\n    \"babel-preset-react\": \"^6.24.1\",\n    \"babel-preset-stage-0\": \"^6.24.1\",\n    \"codacy-coverage\": \"^2.1.0\",\n    \"coveralls\": \"^3.0.8\",\n    \"cross-env\": \"^5.2.1\",\n    \"enzyme\": \"^3.10.0\",\n    \"enzyme-adapter-react-16\": \"^1.15.1\",\n    \"eslint\": \"^4.19.0\",\n    \"eslint-config-airbnb\": \"^16.1.0\",\n    \"eslint-plugin-flowtype\": \"^2.46.1\",\n    \"eslint-plugin-import\": \"^2.18.2\",\n    \"eslint-plugin-jsx-a11y\": \"^6.2.3\",\n    \"eslint-plugin-react\": \"^7.16.0\",\n    \"flow-bin\": \"^0.112.0\",\n    \"flow-copy-source\": \"^1.3.0\",\n    \"flow-runtime\": \"^0.17.0\",\n    \"gh-pages\": \"^1.1.0\",\n    \"gulp\": \"^3.9.1\",\n    \"jasmine\": \"^3.5.0\",\n    \"jasmine-reporters\": \"^2.3.2\",\n    \"jest\": \"^22.4.2\",\n    \"jest-cli\": \"^23.0.0-alpha.0\",\n    \"lint-staged\": \"^9.4.3\",\n    \"lodash\": \"^4.17.15\",\n    \"pre-commit\": \"^1.2.2\",\n    \"prop-types\": \"^15.7.2\",\n    \"react\": \"^16.12.0\",\n    \"react-dom\": \"^16.12.0\",\n    \"react-test-renderer\": \"^16.12.0\",\n    \"rimraf\": \"^2.6.2\",\n    \"sinon\": \"^4.4.6\",\n    \"uglify-es\": \"^3.3.9\"\n  },\n  \"lint-staged\": {\n    \"*.js\": \"npm run lint:eslint\"\n  },\n  \"pre-commit\": \"lint:staged\",\n  \"jest\": {\n    \"collectCoverageFrom\": [\n      \"src/**/*.{js,jsx}\"\n    ],\n    \"moduleDirectories\": [\n      \"node_modules\",\n      \"src\"\n    ],\n    \"unmockedModulePathPatterns\": [\n      \"./node_modules/react\",\n      \"./node_modules/react-addons-test-utils\",\n      \"./node_modules/jasmine-reporters\"\n    ],\n    \"setupTestFrameworkScriptFile\": \"<rootDir>/test/util/test-bundler.js\",\n    \"testRegex\": \".*\\\\.test\\\\.js$\"\n  }\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "// @flow\n\nimport React from 'react';\nimport PropTypes from 'prop-types';\n\nexport type Point = {\n  x: number,\n  y: number,\n}\n\nexport type Box = {\n  left: number,\n  top: number,\n  width: number,\n  height: number,\n}\n\ntype Props = {\n  disabled?: boolean,\n  confineSelectionBox?: boolean,\n  target: HTMLElement,\n  onSelectionChange?: (elements: Array<any>) => void,\n  onHighlightChange?: (elements: Array<any>) => void,\n  elements: Array<HTMLElement>,\n  // eslint-disable-next-line react/no-unused-prop-types\n  offset?: {\n    // eslint-disable-next-line react/no-unused-prop-types\n    top: number,\n    // eslint-disable-next-line react/no-unused-prop-types\n    left: number,\n  },\n  style?: any,\n  zoom?: number,\n  ignoreTargets?: Array<string>,\n};\n\ntype State = {\n  mouseDown: boolean,\n  startPoint: ?Point,\n  endPoint: ?Point,\n  selectionBox: ?Box,\n  offset: {\n    top: number,\n    left: number,\n  },\n  zoom: number,\n};\n\nfunction getOffset(props: Props) {\n  let offset = {\n    top: 0,\n    left: 0,\n  };\n  if (props.offset) {\n    offset = {\n      ...props.offset,\n    };\n  } else if (props.target) {\n    const boundingBox = props.target.getBoundingClientRect();\n    offset.top = boundingBox.top + window.scrollY;\n    offset.left = boundingBox.left + window.scrollX;\n  }\n  return offset;\n}\n\nexport default class Selection extends React.PureComponent<Props, State> { // eslint-disable-line react/prefer-stateless-function\n  props: Props;\n  state: State;\n  selectedChildren: Array<number>;\n  highlightedChildren: Array<number>;\n\n  constructor(props: Props) {\n    super(props);\n\n    this.state = {\n      mouseDown: false,\n      startPoint: null,\n      endPoint: null,\n      selectionBox: null,\n      offset: getOffset(props),\n      zoom: props.zoom || 1,\n    };\n\n    this.selectedChildren = [];\n    this.highlightedChildren = [];\n  }\n\n  static getDerivedStateFromProps(nextProps: Props) {\n    return {\n      offset: getOffset(nextProps),\n    };\n  }\n\n  componentDidMount() {\n    this.reset();\n    this.bind();\n  }\n\n  componentDidUpdate() {\n    this.reset();\n    this.bind();\n    if (this.state.mouseDown && this.state.selectionBox) {\n      this.updateCollidingChildren(this.state.selectionBox);\n    }\n  }\n\n  componentWillUnmount() {\n    this.reset();\n    window.document.removeEventListener('mousemove', this.onMouseMove);\n    window.document.removeEventListener('mouseup', this.onMouseUp);\n  }\n\n  bind = () => {\n    this.props.target.addEventListener('mousedown', this.onMouseDown);\n    this.props.target.addEventListener('touchstart', this.onTouchStart);\n  };\n\n  reset = () => {\n    if (this.props.target) {\n      this.props.target.removeEventListener('mousedown', this.onMouseDown);\n    }\n  };\n\n  init = (e: Event, x: number, y: number): boolean => {\n    if (this.props.ignoreTargets) {\n      const Target = (e.target: any);\n      if (!Target.matches) {\n        // polyfill matches\n        const defaultMatches = (s: string) => (\n          [].indexOf.call(window.document.querySelectorAll(s), this) !== -1\n        );\n        Target.matches =\n          Target.matchesSelector ||\n          Target.mozMatchesSelector ||\n          Target.msMatchesSelector ||\n          Target.oMatchesSelector ||\n          Target.webkitMatchesSelector ||\n          defaultMatches;\n      }\n      if (Target.matches && Target.matches(this.props.ignoreTargets.join(','))) {\n        return false;\n      }\n    }\n    const nextState = {};\n\n\n    nextState.mouseDown = true;\n    nextState.startPoint = {\n      x: (x - this.state.offset.left) / this.state.zoom,\n      y: (y - this.state.offset.top) / this.state.zoom,\n    };\n\n    this.setState(nextState);\n    return true;\n  };\n\n  /**\n   * On root element mouse down\n   * The event should be a MouseEvent | TouchEvent, but flow won't get it...\n   * @private\n   */\n  onMouseDown = (e: MouseEvent | any) => {\n    if (this.props.disabled || e.button === 2 || (e.nativeEvent && e.nativeEvent.which === 2)) {\n      return;\n    }\n\n    if (this.init(e, e.pageX, e.pageY)) {\n      window.document.addEventListener('mousemove', this.onMouseMove);\n      window.document.addEventListener('mouseup', this.onMouseUp);\n    }\n  };\n\n  onTouchStart = (e: TouchEvent) => {\n    if (this.props.disabled || !e.touches || !e.touches[0] || e.touches.length > 1) {\n      return;\n    }\n\n    if (this.init(e, e.touches[0].pageX, e.touches[0].pageY)) {\n      window.document.addEventListener('touchmove', this.onTouchMove);\n      window.document.addEventListener('touchend', this.onMouseUp);\n    }\n  };\n\n  /**\n   * On document element mouse up\n   * @private\n   */\n  onMouseUp = () => {\n    window.document.removeEventListener('touchmove', this.onTouchMove);\n    window.document.removeEventListener('mousemove', this.onMouseMove);\n    window.document.removeEventListener('mouseup', this.onMouseUp);\n    window.document.removeEventListener('touchend', this.onMouseUp);\n\n    this.setState({\n      mouseDown: false,\n      startPoint: null,\n      endPoint: null,\n      selectionBox: null,\n    });\n\n    if (this.props.onSelectionChange) {\n      this.props.onSelectionChange(this.selectedChildren);\n    }\n\n    if (this.props.onHighlightChange) {\n      this.highlightedChildren = [];\n      this.props.onHighlightChange(this.highlightedChildren);\n    }\n    this.selectedChildren = [];\n  };\n\n  /**\n   * On document element mouse move\n   * @private\n   */\n  onMouseMove = (e: MouseEvent) => {\n    e.preventDefault();\n    if (this.state.mouseDown) {\n      const endPoint: Point = {\n        x: (e.pageX - this.state.offset.left) / this.state.zoom,\n        y: (e.pageY - this.state.offset.top) / this.state.zoom,\n      };\n\n      this.setState({\n        endPoint,\n        selectionBox: this.calculateSelectionBox(\n          this.state.startPoint,\n          endPoint\n        ),\n      });\n    }\n  };\n\n  onTouchMove = (e: TouchEvent) => {\n    e.preventDefault();\n    if (this.state.mouseDown) {\n      const endPoint: Point = {\n        x: (e.touches[0].pageX - this.state.offset.left) / this.state.zoom,\n        y: (e.touches[0].pageY - this.state.offset.top) / this.state.zoom,\n      };\n\n      this.setState({\n        endPoint,\n        selectionBox: this.calculateSelectionBox(\n          this.state.startPoint,\n          endPoint\n        ),\n      });\n    }\n  };\n\n  /**\n   * Calculate if two segments overlap in 1D\n   * @param lineA [min, max]\n   * @param lineB [min, max]\n   */\n  lineIntersects = (lineA: [number, number], lineB: [number, number]): boolean => (\n    lineA[1] >= lineB[0] && lineB[1] >= lineA[0]\n  );\n\n  /**\n   * Detect 2D box intersection - the two boxes will intersect\n   * if their projections to both axis overlap\n   * @private\n   */\n  boxIntersects = (boxA: Box, boxB: Box): boolean => {\n    // calculate coordinates of all points\n    const boxAProjection = {\n      x: [boxA.left, boxA.left + boxA.width],\n      y: [boxA.top, boxA.top + boxA.height],\n    };\n\n    const boxBProjection = {\n      x: [boxB.left, boxB.left + boxB.width],\n      y: [boxB.top, boxB.top + boxB.height],\n    };\n\n    return this.lineIntersects(boxAProjection.x, boxBProjection.x) &&\n           this.lineIntersects(boxAProjection.y, boxBProjection.y);\n  };\n\n  /**\n   * Updates the selected items based on the\n   * collisions with selectionBox,\n   * also updates the highlighted items if they have changed\n   * @private\n   */\n  updateCollidingChildren = (selectionBox: Box) => {\n    this.selectedChildren = [];\n    if (this.props.elements) {\n      this.props.elements.forEach((ref, $index) => {\n        if (ref) {\n          const refBox = ref.getBoundingClientRect();\n          const tmpBox = {\n            top: ((refBox.top - this.state.offset.top) + window.scrollY) / this.state.zoom,\n            left: ((refBox.left - this.state.offset.left) + window.scrollX) / this.state.zoom,\n            width: ref.clientWidth,\n            height: ref.clientHeight,\n          };\n\n          if (this.boxIntersects(selectionBox, tmpBox)) {\n            this.selectedChildren.push($index);\n          }\n        }\n      });\n    }\n    if (this.props.onHighlightChange && JSON.stringify(this.highlightedChildren) !== JSON.stringify(this.selectedChildren)) {\n      const { onHighlightChange } = this.props;\n      this.highlightedChildren = [...this.selectedChildren];\n      if (window.requestAnimationFrame) {\n        window.requestAnimationFrame(() => {\n          onHighlightChange(this.highlightedChildren);\n        });\n      } else {\n        onHighlightChange(this.highlightedChildren);\n      }\n    }\n  };\n\n  /**\n   * Calculate selection box dimensions\n   * @private\n   */\n  calculateSelectionBox = (startPoint: ?Point, endPoint: ?Point) => {\n    if (!this.state.mouseDown || !startPoint || !endPoint) {\n      return null;\n    }\n    let left, top, width, height = 0;\n    if (this.props.confineSelectionBox) {\n      var refBox = this.props.target.getBoundingClientRect();\n      // The extra 1 pixel is to ensure that the mouse is on top\n      // of the selection box and avoids triggering clicks on the target.\n      left = Math.max(0, Math.min(startPoint.x, endPoint.x)) - 1;\n      top = Math.max(0, Math.min(startPoint.y, endPoint.y)) - 1;\n      width = (startPoint.x < endPoint.x ? Math.min(refBox.width - startPoint.x, Math.abs(startPoint.x - endPoint.x)) : Math.min(startPoint.x, Math.abs(startPoint.x - endPoint.x))) + 1;\n      height = (startPoint.y < endPoint.y ? Math.min(refBox.height - startPoint.y, Math.abs(startPoint.y - endPoint.y)) : Math.min(startPoint.y, Math.abs(startPoint.y - endPoint.y))) + 1;\n    } else {\n      // The extra 1 pixel is to ensure that the mouse is on top\n      // of the selection box and avoids triggering clicks on the target.\n      left = Math.min(startPoint.x, endPoint.x) - 1;\n      top = Math.min(startPoint.y, endPoint.y) - 1;\n      width = Math.abs(startPoint.x - endPoint.x) + 1;\n      height = Math.abs(startPoint.y - endPoint.y) + 1;\n    }\n    return {\n      left,\n      top,\n      width,\n      height,\n    };\n  };\n\n  /**\n   * Render\n   */\n  render() {\n    let style: any = {\n      position: 'absolute',\n      background: 'rgba(159, 217, 255, 0.3)',\n      border: 'solid 1px rgba(123, 123, 123, 0.61)',\n      zIndex: 9,\n      cursor: 'crosshair',\n      ...this.props.style,\n    };\n\n    if (this.state.selectionBox) {\n      style = {\n        ...style,\n        ...this.state.selectionBox,\n      };\n    }\n\n    if (!this.state.mouseDown || !this.state.endPoint || !this.state.startPoint) {\n      return null;\n    }\n    return (\n      <div className='react-ds-border' style={ style } />\n    );\n  }\n}\n\nSelection.propTypes = {\n  target: PropTypes.object,\n  confineSelectionBox: PropTypes.bool,\n  disabled: PropTypes.bool,\n  onSelectionChange: PropTypes.func.isRequired,\n  onHighlightChange: PropTypes.func,\n  elements: PropTypes.array.isRequired,\n  // eslint-disable-next-line react/no-unused-prop-types\n  offset: PropTypes.object,\n  zoom: PropTypes.number,\n  style: PropTypes.object,\n  ignoreTargets: PropTypes.array,\n};\n"
  },
  {
    "path": "test/index.test.js",
    "content": "import React from 'react';\nimport { shallow, mount } from 'enzyme';\nimport clone from 'lodash/clone';\n\nimport Selection from '../src/index';\n\nconst mockTarget = {\n  addEventListener: jest.fn(),\n  removeEventListener: jest.fn(),\n};\n\nconst defaultProps = {\n  target: mockTarget,\n  disabled: false,\n  onSelectionChange: () => {},\n  elements: [],\n  offset: {\n    top: 0,\n    left: 0,\n  },\n  zoom: 1,\n};\n\nconst initialState = {\n  mouseDown: false,\n  startPoint: null,\n  endPoint: null,\n  selectionBox: null,\n  offset: defaultProps.offset,\n  zoom: defaultProps.zoom,\n};\n\ndescribe('<Selection />', () => {\n  it('Should not render when not selecting', () => {\n    const props = defaultProps;\n\n    const renderedComponent = shallow(\n      <Selection { ...props } />\n    );\n    expect(renderedComponent.find('div.selection')).toHaveLength(0);\n  });\n\n  it('onMouseDown: Should start tracking', () => {\n    const mouseEvent = {\n      pageX: 100,\n      pageY: 400,\n      button: 1,\n      nativeEvent: {\n        which: 1,\n      },\n    };\n\n    mockTarget.addEventListener.mockClear();\n    mockTarget.removeEventListener.mockClear();\n\n    const renderedComponent = shallow(\n      <Selection { ...defaultProps } />\n    );\n    expect(renderedComponent.state()).toEqual(initialState);\n\n    renderedComponent.instance().onMouseDown(mouseEvent);\n\n    const mouseDownState = clone(initialState);\n    mouseDownState.startPoint = {\n      x: mouseEvent.pageX,\n      y: mouseEvent.pageY,\n    };\n    mouseDownState.mouseDown = true;\n\n    expect(renderedComponent.state()).toEqual(mouseDownState);\n  });\n\n  it('onMouseUp: Should stop tracking', () => {\n    const mouseEvent = {\n      pageX: 100,\n      pageY: 400,\n      button: 1,\n      nativeEvent: {\n        which: 1,\n      },\n    };\n\n    mockTarget.addEventListener.mockClear();\n    mockTarget.removeEventListener.mockClear();\n\n    const renderedComponent = shallow(\n      <Selection { ...defaultProps } />\n    );\n\n    renderedComponent.instance().onMouseDown(mouseEvent);\n    renderedComponent.instance().onMouseUp();\n\n    expect(renderedComponent.state()).toEqual(initialState);\n  });\n\n  it('onMouseMove: Should update selection box', () => {\n    const mouseEvent = {\n      pageX: 100,\n      pageY: 400,\n      button: 1,\n      nativeEvent: {\n        which: 1,\n      },\n    };\n\n    mockTarget.addEventListener.mockClear();\n    mockTarget.removeEventListener.mockClear();\n\n    const moveEvent = clone(mouseEvent);\n    moveEvent.pageX = -400;\n    moveEvent.pageY = -100;\n    moveEvent.preventDefault = jest.fn();\n\n    // Using mount because we need the selectionBox ref to exist\n    const renderedComponent = mount(\n      <Selection { ...defaultProps } />\n    );\n\n    renderedComponent.instance().onMouseDown(mouseEvent);\n    renderedComponent.instance().onMouseMove(moveEvent);\n\n    const moveState = clone(initialState);\n    moveState.endPoint = {\n      x: moveEvent.pageX,\n      y: moveEvent.pageY,\n    };\n    moveState.mouseDown = true;\n    moveState.selectionBox = {\n      height: Math.abs(moveEvent.pageX) + mouseEvent.pageX + 1,\n      width: Math.abs(moveEvent.pageY) + mouseEvent.pageY + 1,\n      left: moveEvent.pageX - 1,\n      top: moveEvent.pageY - 1,\n    };\n    moveState.startPoint = {\n      x: mouseEvent.pageX,\n      y: mouseEvent.pageY,\n    };\n\n    expect(renderedComponent.state()).toEqual(moveState);\n  });\n\n  it('boxIntersects: Calculates box intersections', () => {\n    const renderedComponent = shallow(\n      <Selection { ...defaultProps } />\n    );\n\n    const boxOne = {\n      left: 0,\n      top: 0,\n      width: 10,\n      height: 10,\n    };\n\n    const boxTwo = {\n      left: -5,\n      top: 0,\n      width: 10,\n      height: 10,\n    };\n\n    const boxThree = {\n      left: 40,\n      top: 10,\n      width: 10,\n      height: 10,\n    };\n\n    expect(renderedComponent.instance().boxIntersects(boxOne, boxTwo)).toBeTruthy();\n    expect(renderedComponent.instance().boxIntersects(boxOne, boxThree)).toBeFalsy();\n  });\n\n  it('updateCollidingChildren: Updates colliding children', () => {\n    const props = clone(defaultProps);\n    const refs = [];\n\n    const elementBoxes = [\n      {\n        top: 0,\n        left: 0,\n        width: 10,\n        height: 10,\n      },\n      {\n        top: 100,\n        left: 300,\n        width: 10,\n        height: 10,\n      },\n    ];\n\n    // render the elements to get boxes for them\n    mount(\n      <div>\n        { elementBoxes.map((box, $index) => (\n          <div ref={ (element) => { refs.push(element); } } key={ $index } style={ { position: 'absolute', ...box } } />\n        )) }\n      </div>\n    );\n\n    props.elements = refs;\n\n    const renderedComponent = shallow(\n      <Selection { ...props } />\n    );\n\n    const box = {\n      left: -50,\n      top: -50,\n      width: 100,\n      height: 100,\n    };\n\n    renderedComponent.instance().updateCollidingChildren(box);\n\n    expect(renderedComponent.instance().selectedChildren).toEqual([0, 1]);\n  });\n});\n"
  },
  {
    "path": "test/util/test-bundler.js",
    "content": "/* globals jasmine */\n// needed for regenerator-runtime\n// (ES7 generator support is required by redux-saga)\nimport 'babel-polyfill';\n// Enzyme setup for React 16\nimport { configure } from 'enzyme';\nimport Adapter from 'enzyme-adapter-react-16';\n\nconst reporters = require('jasmine-reporters');\njasmine.VERBOSE = true;\njasmine.getEnv().addReporter(\n  new reporters.JUnitXmlReporter({\n    savePath: 'test-report',\n  })\n);\n\nconfigure({ adapter: new Adapter() });\n"
  }
]