[
  {
    "path": ".eslintignore",
    "content": "*.json\nbuild\nconfig\ndist\n**node_modules**\n./node_modules/**\n"
  },
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"env\": {\n    \"node\": true,\n    \"browser\": true,\n    \"es6\": true,\n    \"commonjs\": true\n  },\n  \"plugins\": [\"react\", \"react-hooks\", \"prettier\"],\n  \"extends\": [\n    \"eslint:recommended\",\n    \"plugin:react/recommended\",\n    \"plugin:react-hooks/recommended\",\n    \"plugin:prettier/recommended\",\n    \"plugin:@typescript-eslint/recommended\"\n  ],\n  \"settings\": {\n    \"react\": {\n      \"version\": \"18.2.0\"\n    },\n    \"import/resolver\": {\n      \"node\": {\n        \"paths\": [\"./\"]\n      }\n    }\n  },\n  \"ignorePatterns\": [\"temp.js\", \"node_modules\"],\n  \"parser\": \"@typescript-eslint/parser\",\n  \"parserOptions\": {\n    \"ecmaVersion\": 9,\n    \"sourceType\": \"module\",\n    \"requireConfigFile\": false,\n    \"plugins\": [\"@typescript-eslint\"],\n    \"ecmaFeatures\": {\n      \"jsx\": true,\n      \"experimentalObjectRestSpread\": true,\n      \"modules\": true\n    }\n  },\n  \"rules\": {\n    \"linebreak-style\": 0,\n    \"no-underscore-dangle\": 0,\n    \"no-nested-ternary\": 0,\n    \"prettier/prettier\": \"error\",\n    \"react-hooks/exhaustive-deps\": \"warn\",\n    \"react/react-in-jsx-scope\": \"off\",\n    \"react-hooks/rules-of-hooks\": \"error\",\n    \"react/jsx-uses-react\": \"off\",\n    \"react/jsx-uses-vars\": \"error\",\n    \"react/no-unescaped-entities\": 0,\n    \"react/prefer-stateless-function\": 1\n  },\n  \"globals\": {\n    \"grecaptcha\": \"readonly\"\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\n.cache\nnpm-debug.log\n.DS_Store\n.cache\n.tmp\n*.log\n.parcel-cache\ndist"
  },
  {
    "path": ".npmignore",
    "content": ".DS_Store\n.babelrc\n.cache\n.parcel-cache\n.tmp\n*.log\n.gitignore\nnode_modules\nnpm-debug.log\ndemo\ndocs\nlib\nsrc\n\n"
  },
  {
    "path": ".parcelrc",
    "content": "{\n  \"extends\": \"@parcel/config-default\",\n  \"transformers\": {\n    \"*.{js,mjs,jsx,cjs,ts,tsx}\": [\n      \"@parcel/transformer-js\",\n      \"@parcel/transformer-react-refresh-wrap\"\n    ]\n  }\n}"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"printWidth\": 80,\n  \"trailingComma\": \"none\",\n  \"singleQuote\": true,\n  \"tabWidth\": 2,\n  \"semi\": false,\n  \"jsxSingleQuote\": false,\n  \"quoteProps\": \"as-needed\",\n  \"bracketSpacing\": true,\n  \"jsxBracketSameLine\": false\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "ISC License\n\nCopyright 2021 Stephen Scaff\n\nPermission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n"
  },
  {
    "path": "docs/src/App.js",
    "content": "import { React, useState, useEffect } from 'react'\nimport AnimatedCursor from '../../lib'\nimport DemoContent from './DemoContent'\nimport DemoCustomTest from './DemoCustomTest'\nimport DemoHeader from './DemoHeader'\nimport DemoFooter from './DemoFooter'\nimport './demo-styles.css'\n\nexport default function App() {\n  const [state, setState] = useState('donut')\n  const searchParams = new URLSearchParams(document.location.search)\n  const cursorParam = searchParams.get('cursor')\n\n  useEffect(() => {\n    if (cursorParam) setState(cursorParam)\n  }, [cursorParam])\n\n  return (\n    <div className=\"App\">\n      {state === 'default' && <AnimatedCursor />}\n      {state === 'donut' && (\n        <AnimatedCursor\n          innerSize={8}\n          outerSize={35}\n          innerScale={1}\n          outerScale={2}\n          outerAlpha={0}\n          showSystemCursor={false}\n          hasBlendMode={true}\n          outerStyle={{\n            border: '3px solid var(--cursor-color)'\n          }}\n          innerStyle={{\n            backgroundColor: 'var(--cursor-color)'\n          }}\n        />\n      )}\n      {state === 'blendmode' && (\n        <AnimatedCursor\n          color=\"255,255,255\"\n          innerSize={8}\n          outerSize={35}\n          innerScale={1}\n          outerScale={2}\n          outerAlpha={1}\n          hasBlendMode={true}\n          outerStyle={{\n            mixBlendMode: 'exclusion'\n          }}\n          innerStyle={{\n            backgroundColor: 'var(--cursor-color)',\n            mixBlendMode: 'exclusion'\n          }}\n        />\n      )}\n      {state === 'custom' && (\n        <AnimatedCursor\n          clickables={[\n            {\n              target: '.small',\n              innerScale: 3,\n              outerScale: 1\n            },\n            {\n              target: '.big',\n              innerScale: 9,\n              outerScale: 7\n            },\n            {\n              target: '.blue',\n              color: 'blue',\n              innerStyle: {\n                backgroundColor: 'blue'\n              },\n              outerStyle: {\n                backgroundColor: 'rgb(0,0,255,0.4)'\n              }\n            },\n            {\n              target: '#blueDonut',\n              innerSize: 8,\n              outerSize: 35,\n              innerScale: 1,\n              outerScale: 2,\n              outerAlpha: 0,\n              showSystemCursor: true,\n              hasBlendMode: true,\n              outerStyle: {\n                border: '3px solid blue'\n              },\n              innerStyle: {\n                backgroundColor: 'blue'\n              }\n            },\n            'a',\n            'input[type=\"text\"]',\n            'input[type=\"email\"]',\n            'input[type=\"number\"]',\n            'input[type=\"submit\"]',\n            'input[type=\"image\"]',\n            'label[for]',\n            'select',\n            'textarea',\n            'button',\n            '.link'\n          ]}\n          color={'220, 90, 90'}\n          innerScale={0.6}\n          innerSize={8}\n          outerAlpha={0.4}\n          outerScale={6}\n          outerSize={8}\n          showSystemCursor={false}\n          trailingSpeed={8}\n        />\n      )}\n      <DemoHeader />\n      <DemoContent />\n      {state === 'custom' && <DemoCustomTest />}\n      <DemoFooter />\n    </div>\n  )\n}\n"
  },
  {
    "path": "docs/src/DemoContent.js",
    "content": "import React from 'react'\n\nconst s = {\n  section: {\n    paddingTop: '6em',\n    width: '80%',\n    maxWidth: '36em',\n    margin: '0 auto 1em'\n  },\n  title: {\n    marginBottom: '1em',\n    fontSize: '3em',\n    fontWeight: 800,\n    textAlign: 'center',\n    lineHeight: 1\n  },\n  pretitle: {\n    textAlign: 'center'\n  },\n  subtitle: {\n    textAlign: 'center'\n  },\n  sep: {\n    border: 0,\n    margin: '2em auto',\n    height: 2,\n    width: '3em',\n    backgroundColor: 'rgba(255, 255, 255, 0.5)'\n  }\n}\n\nexport default function Content() {\n  return (\n    <section style={s.section}>\n      <p style={s.pretitle}>Demos</p>\n      <h1 style={s.title}>React Animated Cursor</h1>\n      <p style={s.subtitle}>\n        A component by <a href=\"http://stephenscaff.com/\">Stephen Scaff</a>\n      </p>\n      <hr style={s.sep} />\n      <p>\n        React animated cursor is a React component that creates a custom cursor\n        experience. You can craft a variety of cursor types, and animate\n        movement, hover and clicking properties.\n      </p>\n      <p>\n        Hover over these <a>links</a> and see how that animated cursor does it's\n        thing. Kinda nifty, right? Not applicable to most projects, but a nice\n        move for more interactive/immersive stuff... if you're into that kinda\n        thing? Here's another <a href=\"\">link to nowhere.</a>\n      </p>\n      <p>Essentially, the cursor consists:</p>\n      <ul>\n        <li>\n          An inner dot (<code>cursorInner</code>)\n        </li>\n        <li>\n          An outer, outlining circle (<code>cursorOuter</code>), with slight\n          opacity based on the dot/primary color\n        </li>\n        <li>\n          An inversely scaling effect between the inner and outer cursor parts\n          on click or link hover\n        </li>\n      </ul>\n      <p>\n        Style props exist for in the inner and outer cursor allow you to easily\n        create unique cursor types. Play with <a>css variables</a> to influence\n        the cursor, cursor outline size, and amount of scale on target hover.\n      </p>\n\n      <h3>Demo Cursors</h3>\n      <p>Here's a few cursor types you can create to test</p>\n      <ul>\n        <li>\n          <a href=\"?cursor=default\">Default</a>\n        </li>\n        <li>\n          <a href=\"?cursor=donut\">Donut</a>\n        </li>\n        <li>\n          <a href=\"?cursor=blendmode\">Blendmode</a>\n        </li>\n        <li>\n          <a href=\"?cursor=custom\">Custom</a>\n        </li>\n      </ul>\n\n      <h3>Test Clickables</h3>\n      <p>Here's a collection of test clickable elements to hover over:</p>\n      <ul>\n        <li>\n          <a>Basic Link Tag</a>\n        </li>\n        <li>\n          <button>Buttons</button>\n        </li>\n        <li>\n          <input type=\"submit\" value=\"Submit\" />\n        </li>\n        <li>\n          <select>\n            <option>Select</option>\n          </select>\n        </li>\n        <li>\n          <input\n            type=\"image\"\n            id=\"image-input\"\n            alt=\"Image Input\"\n            src=\"https://cdn2.iconfinder.com/data/icons/button-v1/30/25-512.png\"\n            width=\"30px\"\n          />\n        </li>\n        <li>\n          <label htmlFor=\"label_for\">Label For</label>\n          <input type=\"radio\" name=\"gender\" id=\"label_for\" value=\"label_for\" />\n        </li>\n        <li>\n          <div className=\"link\">Class name =\"link\"</div>\n        </li>\n      </ul>\n    </section>\n  )\n}\n"
  },
  {
    "path": "docs/src/DemoCustomTest.js",
    "content": "import React from 'react'\n\nconst s = {\n  section: {\n    paddingBottom: '6em',\n    width: '80%',\n    maxWidth: '36em',\n    margin: '0 auto 1em'\n  }\n}\n\nexport default function Content() {\n  return (\n    <section style={s.section}>\n      <h3>Test custom Clickables</h3>\n      <p>\n        Here's a collection of additional elements to test custom behaviors:\n      </p>\n      <ul>\n        <li>\n          <div className=\"small\">Class name =\"small\"</div>\n        </li>\n        <li>\n          <div className=\"big\">Class name =\"big\"</div>\n        </li>\n        <li>\n          <div className=\"blue\">Class name =\"blue\"</div>\n        </li>\n        <li>\n          <div id=\"blueDonut\">Id =\"blueDonut\"</div>\n        </li>\n      </ul>\n    </section>\n  )\n}\n"
  },
  {
    "path": "docs/src/DemoFooter.js",
    "content": "import React from 'react'\n\nconst s = {\n  footer: {\n    position: 'relative',\n    width: '100%',\n    padding: '6em 0 3em',\n    backgroundColor: '#2f2c2c',\n    textAlign: 'center'\n  },\n  footer__grid: {\n    position: 'relative',\n    maxWidth: '95%',\n    margin: '0 auto'\n  },\n  footer__border: {\n    height: '1px',\n    width: '100%',\n    marginBottom: '4em',\n    border: '0',\n    backgroundColor: 'rgba(255,255,255,0.4)'\n  },\n  footer__copy: {\n    fontSize: '0.8em'\n  },\n  footer__icon: {\n    width: '2em',\n    margin: '0 auto',\n    textAlign: 'center'\n  },\n  footer__icon_vector: {\n    fill: '#fff'\n  }\n}\n\nexport default function DemoHeader() {\n  return (\n    <section style={s.footer}>\n      <div style={s.footer__grid}>\n        <hr style={s.footer__border} />\n        <div style={s.footer__icon}>\n          <svg\n            style={s.footer__icon_vector}\n            xmlns=\"http://www.w3.org/2000/svg\"\n            viewBox=\"0 0 512 512\"\n          >\n            <path d=\"M262.468 0H251.85C153.818 0 72.902 79.089 72.902 177.126v324.407c0 4.026 2.884 7.684 6.511 9.431 3.617 1.747 8.214 1.257 11.354-1.257l45.149-36.007 35.303 35.23c4.087 4.087 10.744 4.087 14.831 0l34.471-34.454 34.462 34.454c4.087 4.087 10.713 4.087 14.8 0l34.456-34.454 34.454 34.454c4.087 4.087 10.709 4.087 14.796 0l34.454-34.454 34.453 34.454a10.452 10.452 0 0 0 7.398 3.065c1.349 0 2.129-.255 3.427-.797 3.908-1.614 5.878-5.436 5.878-9.666V177.126C439.098 79.089 360.499 0 262.468 0zm155.705 476.275-23.411-23.991c-4.087-4.087-10.418-4.087-14.505 0l-34.309 34.454-34.381-34.454a10.35 10.35 0 0 0-7.362-3.065 10.392 10.392 0 0 0-7.38 3.065l-34.444 34.454-34.449-34.454a10.455 10.455 0 0 0-14.792 0l-34.454 34.454-34.452-34.454c-3.77-3.77-10.357-4.107-14.51-.777l-35.897 28.252V177.126c0-86.502 71.527-156.201 158.023-156.201h10.617c86.495 0 155.705 69.699 155.705 156.201v299.149z\" />\n            <path d=\"M194.383 156.262c-14.423 0-26.157 11.73-26.157 26.157s11.733 26.157 26.157 26.157c14.421 0 26.156-11.73 26.156-26.157s-11.735-26.157-26.156-26.157zm0 31.388a5.237 5.237 0 0 1-5.231-5.231 5.237 5.237 0 0 1 5.231-5.231 5.239 5.239 0 0 1 5.231 5.231 5.238 5.238 0 0 1-5.231 5.231zM319.935 156.262c-14.422 0-26.157 11.73-26.157 26.157s11.735 26.157 26.157 26.157 26.157-11.73 26.157-26.157-11.735-26.157-26.157-26.157zm0 31.388a5.238 5.238 0 0 1-5.231-5.231c0-2.881 2.345-5.231 5.231-5.231s5.231 2.35 5.231 5.231a5.238 5.238 0 0 1-5.231 5.231z\" />\n          </svg>\n        </div>\n        <p style={s.footer__copy}>\n          A little thing by{' '}\n          <a href=\"https://stephenscaff.com\" target=\"_blank\" rel=\"noreferrer\">\n            Stephen Scaff\n          </a>\n        </p>\n      </div>\n    </section>\n  )\n}\n"
  },
  {
    "path": "docs/src/DemoHeader.js",
    "content": "import React from 'react'\n\nconst s = {\n  header: {\n    position: 'fixed',\n    top: 0,\n    left: 0,\n    width: '100%',\n    backgroundColor: '#2f2c2c'\n  },\n  header__grid: {\n    position: 'relative',\n    display: 'flex',\n    justifyContent: 'space-between',\n    alignItems: 'center',\n    width: '100%',\n    height: '4em',\n    maxWidth: '95%',\n    margin: '0 auto'\n  },\n  nav: {\n    marginLeft: 'auto'\n  },\n  nav__link: {\n    marginLeft: '1em',\n    fontSize: '0.8em',\n    fontWeight: '400'\n  },\n  brand: {\n    display: 'flex',\n    alignItems: 'center'\n  },\n  brand__icon_inner: {\n    position: 'relative',\n    right: '-5px',\n    display: 'block',\n    height: '10px',\n    width: '10px',\n    borderRadius: '100%',\n    backgroundColor: '#fff'\n  },\n  brand__icon_outer: {\n    position: 'relative',\n    left: '-10px',\n    top: '-8px',\n    display: 'block',\n    height: '6px',\n    width: '6px',\n    borderRadius: '100%',\n    backgroundColor: 'rgba(255,255,255,0.4)'\n  }\n}\n\nexport default function DemoHeader() {\n  return (\n    <section style={s.header}>\n      <div style={s.header__grid}>\n        <div style={s.brand}>\n          <span style={s.brand__icon_inner}></span>\n          <span style={s.brand__icon_outer}></span>\n        </div>\n        <nav style={s.nav}>\n          <a\n            style={s.nav__link}\n            href=\"https://github.com/stephenscaff/react-animated-cursor\"\n          >\n            Repo\n          </a>\n          <a\n            style={s.nav__link}\n            href=\"https://github.com/stephenscaff/react-animated-cursor/blob/master/readme.md\"\n          >\n            Docs\n          </a>\n          <a\n            style={s.nav__link}\n            href=\"https://stephenscaff.github.io/react-animated-cursor/\"\n          >\n            Demos\n          </a>\n        </nav>\n      </div>\n    </section>\n  )\n}\n"
  },
  {
    "path": "docs/src/demo-styles.css",
    "content": "/* Cursor vars if you wanna use css over css in js */\n\n:root {\n  --cursor-color: #fff;\n}\nhtml,\nbody {\n  background-color: #2f2c2c;\n  color: #fff;\n  font-family: 'Inter', sans-serif;\n}\n\n/* Demo Content */\na {\n  text-decoration: none;\n  color: #fff;\n  font-weight: 600;\n  border-bottom: 1px solid rgba(255, 255, 255, 0.7);\n  transition: 0.5s ease;\n}\n\na:hover {\n  color: rgba(255, 255, 255, 0.5);\n  border-bottom-color: rgba(255, 255, 255, 0.1);\n}\n\nsection {\n  line-height: 1.7;\n  font-weight: 300;\n}\n\nh3 {\n  font-size: 1.3em;\n  margin: 2em 0 1em;\n}\n\nul {\n  margin: 2em 0 1em;\n  padding-left: 1em;\n}\n\nul li {\n  padding-bottom: 1.25em;\n  padding-left: 0;\n}\n"
  },
  {
    "path": "docs/src/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link\n      href=\"https://fonts.googleapis.com/css2?family=Inter:wght@300;500;800&display=swap\"\n      rel=\"stylesheet\"\n    />\n\n    <title>React Animated Cursor - by Stephen Scaff</title>\n    <meta name=\"description\" content=\"React Animated Cursor is a React component that allows you to craft a custom cursor experience. Created by Stephen Scaff.\"/>\n  </head>\n\n  <body>\n    <noscript>\n      You need to enable JavaScript to run this app.\n    </noscript>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"index.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/src/index.js",
    "content": "import React from 'react'\nimport { createRoot } from 'react-dom/client'\nimport App from './App'\n\nconst container = document.getElementById('app')\nconst root = createRoot(container)\nroot.render(<App />)\n"
  },
  {
    "path": "lib/AnimatedCursor.tsx",
    "content": "import {\n  useState,\n  useEffect,\n  useCallback,\n  useRef,\n  CSSProperties,\n  useMemo\n} from 'react'\nimport { useEventListener } from './hooks/useEventListener'\nimport type {\n  AnimatedCursorProps,\n  AnimatedCursorCoordinates,\n  AnimatedCursorOptions,\n  Clickable\n} from './AnimatedCursor.types'\nimport find from './helpers/find'\nimport useIsTouchdevice from './hooks/useIsTouchdevice'\n\n/**\n * Cursor Core\n * Replaces the native cursor with a custom animated cursor, consisting\n * of an inner and outer dot that scale inversely based on hover or click.\n *\n * @author Stephen Scaff (github.com/stephenscaff)\n *\n * @param {object} obj\n * @param {array}  obj.clickables - array of clickable selectors\n * @param {string} obj.children - element that is shown instead of the inner dot\n * @param {string} obj.color - rgb color value\n * @param {number} obj.innerScale - inner cursor scale amount\n * @param {number} obj.innerSize - inner cursor size in px\n * @param {object} obj.innerStyle - style object for inner cursor\n * @param {number} obj.outerAlpha - level of alpha transparency for color\n * @param {number} obj.outerScale - outer cursor scale amount\n * @param {number} obj.outerSize - outer cursor size in px\n * @param {object} obj.outerStyle - style object for outer cursor\n * @param {bool}   obj.showSystemCursor - show/hide system cursor1\n * @param {number} obj.trailingSpeed - speed the outer cursor trails at\n */\nfunction CursorCore({\n  clickables = [\n    'a',\n    'input[type=\"text\"]',\n    'input[type=\"email\"]',\n    'input[type=\"number\"]',\n    'input[type=\"submit\"]',\n    'input[type=\"image\"]',\n    'label[for]',\n    'select',\n    'textarea',\n    'button',\n    '.link'\n  ],\n  children,\n  color = '220, 90, 90',\n  innerScale = 0.6,\n  innerSize = 8,\n  innerStyle,\n  outerAlpha = 0.4,\n  outerScale = 6,\n  outerSize = 8,\n  outerStyle,\n  showSystemCursor = false,\n  trailingSpeed = 8\n}: AnimatedCursorProps) {\n  const defaultOptions = useMemo(\n    () => ({\n      children,\n      color,\n      innerScale,\n      innerSize,\n      innerStyle,\n      outerAlpha,\n      outerScale,\n      outerSize,\n      outerStyle\n    }),\n    [\n      children,\n      color,\n      innerScale,\n      innerSize,\n      innerStyle,\n      outerAlpha,\n      outerScale,\n      outerSize,\n      outerStyle\n    ]\n  )\n\n  const cursorOuterRef = useRef<HTMLDivElement>(null)\n  const cursorInnerRef = useRef<HTMLDivElement>(null)\n  const requestRef = useRef<number | null>(null)\n  const previousTimeRef = useRef<number | null>(null)\n  const [coords, setCoords] = useState<AnimatedCursorCoordinates>({\n    x: 0,\n    y: 0\n  })\n  const [isVisible, setIsVisible] = useState(false)\n  const [options, setOptions] = useState(defaultOptions)\n  const [isActive, setIsActive] = useState<boolean | AnimatedCursorOptions>(\n    false\n  )\n  const [isActiveClickable, setIsActiveClickable] = useState(false)\n  const endX = useRef(0)\n  const endY = useRef(0)\n\n  /**\n   * Primary Mouse move event\n   * @param {number} clientX - MouseEvent.clientX\n   * @param {number} clientY - MouseEvent.clientY\n   */\n  const onMouseMove = useCallback((event: MouseEvent) => {\n    const { clientX, clientY } = event\n    setCoords({ x: clientX, y: clientY })\n    if (cursorInnerRef.current !== null) {\n      cursorInnerRef.current.style.top = `${clientY}px`\n      cursorInnerRef.current.style.left = `${clientX}px`\n    }\n    endX.current = clientX\n    endY.current = clientY\n  }, [])\n\n  // Outer Cursor Animation Delay\n  const animateOuterCursor = useCallback(\n    (time: number) => {\n      if (previousTimeRef.current !== undefined) {\n        coords.x += (endX.current - coords.x) / trailingSpeed\n        coords.y += (endY.current - coords.y) / trailingSpeed\n        if (cursorOuterRef.current !== null) {\n          cursorOuterRef.current.style.top = `${coords.y}px`\n          cursorOuterRef.current.style.left = `${coords.x}px`\n        }\n      }\n      previousTimeRef.current = time\n      requestRef.current = requestAnimationFrame(animateOuterCursor)\n    },\n    [requestRef] // eslint-disable-line\n  )\n\n  // Outer cursor RAF setup / cleanup\n  useEffect(() => {\n    requestRef.current = requestAnimationFrame(animateOuterCursor)\n    return () => {\n      if (requestRef.current !== null) {\n        cancelAnimationFrame(requestRef.current)\n      }\n    }\n  }, [animateOuterCursor])\n\n  /**\n   * Calculates amount to scale cursor in px3\n   * @param {number} orignalSize - starting size\n   * @param {number} scaleAmount - Amount to scale\n   * @returns {String} Scale amount in px\n   */\n  const getScaleAmount = (orignalSize: number, scaleAmount: number) => {\n    return `${parseInt(String(orignalSize * scaleAmount))}px`\n  }\n\n  // Scales cursor by HxW\n  const scaleBySize = useCallback(\n    (\n      cursorRef: HTMLDivElement | null,\n      orignalSize: number,\n      scaleAmount: number\n    ) => {\n      if (cursorRef) {\n        cursorRef.style.height = getScaleAmount(orignalSize, scaleAmount)\n        cursorRef.style.width = getScaleAmount(orignalSize, scaleAmount)\n      }\n    },\n    []\n  )\n\n  // Mouse Events State updates\n  const onMouseDown = useCallback(() => setIsActive(true), [])\n  const onMouseUp = useCallback(() => setIsActive(false), [])\n  const onMouseEnterViewport = useCallback(() => setIsVisible(true), [])\n  const onMouseLeaveViewport = useCallback(() => setIsVisible(false), [])\n\n  useEventListener('mousemove', onMouseMove)\n  useEventListener('mousedown', onMouseDown)\n  useEventListener('mouseup', onMouseUp)\n  useEventListener('mouseover', onMouseEnterViewport)\n  useEventListener('mouseout', onMouseLeaveViewport)\n\n  // Cursors Hover/Active State\n  useEffect(() => {\n    if (isActive) {\n      scaleBySize(cursorInnerRef.current, options.innerSize, options.innerScale)\n      scaleBySize(cursorOuterRef.current, options.outerSize, options.outerScale)\n    } else {\n      scaleBySize(cursorInnerRef.current, options.innerSize, 1)\n      scaleBySize(cursorOuterRef.current, options.outerSize, 1)\n    }\n  }, [\n    options.innerSize,\n    options.innerScale,\n    options.outerSize,\n    options.outerScale,\n    scaleBySize,\n    isActive\n  ])\n\n  // Cursors Click States\n  useEffect(() => {\n    if (isActiveClickable) {\n      scaleBySize(\n        cursorInnerRef.current,\n        options.innerSize,\n        options.innerScale * 1.2\n      )\n      scaleBySize(\n        cursorOuterRef.current,\n        options.outerSize,\n        options.outerScale * 1.4\n      )\n    }\n  }, [\n    options.innerSize,\n    options.innerScale,\n    options.outerSize,\n    options.outerScale,\n    scaleBySize,\n    isActiveClickable\n  ])\n\n  // Cursor Visibility Statea\n  useEffect(() => {\n    if (cursorInnerRef.current == null || cursorOuterRef.current == null) return\n\n    if (isVisible) {\n      cursorInnerRef.current.style.opacity = '1'\n      cursorOuterRef.current.style.opacity = '1'\n    } else {\n      cursorInnerRef.current.style.opacity = '0'\n      cursorOuterRef.current.style.opacity = '0'\n    }\n  }, [isVisible])\n\n  // Click event state updates\n  useEffect(() => {\n    const clickableEls = document.querySelectorAll<HTMLElement>(\n      clickables\n        .map((clickable) =>\n          typeof clickable === 'object' && clickable?.target\n            ? clickable.target\n            : clickable ?? ''\n        )\n        .join(',')\n    )\n\n    clickableEls.forEach((el) => {\n      if (!showSystemCursor) el.style.cursor = 'none'\n\n      const clickableOptions =\n        typeof clickables === 'object'\n          ? find(\n              clickables,\n              (clickable: Clickable) =>\n                typeof clickable === 'object' && el.matches(clickable.target)\n            )\n          : {}\n\n      const options = {\n        ...defaultOptions,\n        ...clickableOptions\n      }\n\n      el.addEventListener('mouseover', () => {\n        setIsActive(true)\n        setOptions(options)\n      })\n      el.addEventListener('click', () => {\n        setIsActive(true)\n        setIsActiveClickable(false)\n      })\n      el.addEventListener('mousedown', () => {\n        setIsActiveClickable(true)\n      })\n      el.addEventListener('mouseup', () => {\n        setIsActive(true)\n      })\n      el.addEventListener('mouseout', () => {\n        setIsActive(false)\n        setIsActiveClickable(false)\n        setOptions(defaultOptions)\n      })\n    })\n\n    return () => {\n      clickableEls.forEach((el) => {\n        const clickableOptions =\n          typeof clickables === 'object'\n            ? find(\n                clickables,\n                (clickable: Clickable) =>\n                  typeof clickable === 'object' && el.matches(clickable.target)\n              )\n            : {}\n\n        const options = {\n          ...defaultOptions,\n          ...clickableOptions\n        }\n\n        el.removeEventListener('mouseover', () => {\n          setIsActive(true)\n          setOptions(options)\n        })\n        el.removeEventListener('click', () => {\n          setIsActive(true)\n          setIsActiveClickable(false)\n        })\n        el.removeEventListener('mousedown', () => {\n          setIsActiveClickable(true)\n        })\n        el.removeEventListener('mouseup', () => {\n          setIsActive(true)\n        })\n        el.removeEventListener('mouseout', () => {\n          setIsActive(false)\n          setIsActiveClickable(false)\n          setOptions(defaultOptions)\n        })\n      })\n    }\n  }, [isActive, clickables, showSystemCursor, defaultOptions])\n\n  useEffect(() => {\n    if (typeof window === 'object' && !showSystemCursor) {\n      document.body.style.cursor = 'none'\n    }\n  }, [showSystemCursor])\n\n  const coreStyles: CSSProperties = {\n    zIndex: 999,\n    display: 'flex',\n    justifyContent: 'center',\n    alignItems: 'center',\n    position: 'fixed',\n    borderRadius: '50%',\n    pointerEvents: 'none',\n    transform: 'translate(-50%, -50%)',\n    transition:\n      'opacity 0.15s ease-in-out, height 0.2s ease-in-out, width 0.2s ease-in-out'\n  }\n\n  // Cursor Styles\n  const styles = {\n    cursorInner: {\n      width: !options.children ? options.innerSize : 'auto',\n      height: !options.children ? options.innerSize : 'auto',\n      backgroundColor: !options.children\n        ? `rgba(${options.color}, 1)`\n        : 'transparent',\n      ...coreStyles,\n      ...(options.innerStyle && options.innerStyle)\n    },\n    cursorOuter: {\n      width: options.outerSize,\n      height: options.outerSize,\n      backgroundColor: `rgba(${options.color}, ${options.outerAlpha})`,\n      ...coreStyles,\n      ...(options.outerStyle && options.outerStyle)\n    }\n  }\n\n  return (\n    <>\n      <div ref={cursorOuterRef} style={styles.cursorOuter} />\n      <div ref={cursorInnerRef} style={styles.cursorInner}>\n        <div\n          style={{\n            opacity: !options.children ? 0 : 1,\n            transition: 'opacity 0.3s ease-in-out'\n          }}\n        >\n          {options.children}\n        </div>\n      </div>\n    </>\n  )\n}\n\n/**\n * AnimatedCursor\n * Calls and passes props to CursorCore if not a touch/mobile device.\n */\nfunction AnimatedCursor({\n  children,\n  clickables,\n  color,\n  innerScale,\n  innerSize,\n  innerStyle,\n  outerAlpha,\n  outerScale,\n  outerSize,\n  outerStyle,\n  showSystemCursor,\n  trailingSpeed\n}: AnimatedCursorProps) {\n  const isTouchdevice = useIsTouchdevice()\n  if (typeof window !== 'undefined' && isTouchdevice) {\n    return <></>\n  }\n  return (\n    <CursorCore\n      clickables={clickables}\n      color={color}\n      innerScale={innerScale}\n      innerSize={innerSize}\n      innerStyle={innerStyle}\n      outerAlpha={outerAlpha}\n      outerScale={outerScale}\n      outerSize={outerSize}\n      outerStyle={outerStyle}\n      showSystemCursor={showSystemCursor}\n      trailingSpeed={trailingSpeed}\n    >\n      {children}\n    </CursorCore>\n  )\n}\n\nexport default AnimatedCursor\n"
  },
  {
    "path": "lib/AnimatedCursor.types.ts",
    "content": "import { CSSProperties, ReactNode } from 'react'\n\nexport interface AnimatedCursorOptions {\n  children?: ReactNode\n  color?: string\n  innerScale?: number\n  innerSize?: number\n  innerStyle?: CSSProperties\n  outerAlpha?: number\n  outerScale?: number\n  outerSize?: number\n  outerStyle?: CSSProperties\n}\n\nexport type Clickable = string | ({ target: string } & AnimatedCursorOptions)\n\nexport interface AnimatedCursorProps extends AnimatedCursorOptions {\n  clickables?: Clickable[]\n  showSystemCursor?: boolean\n  trailingSpeed?: number\n}\n\nexport interface AnimatedCursorCoordinates {\n  x: number\n  y: number\n}\n"
  },
  {
    "path": "lib/helpers/find.ts",
    "content": "export default function findInArray<T>(\n  arr: T[],\n  callback: (element: T, index: number, array: T[]) => boolean,\n  ...args\n): T | undefined {\n  if (typeof callback !== 'function') {\n    throw new TypeError('callback must be a function')\n  }\n\n  const list = Object(arr)\n  // Makes sure it always has a positive integer as length.\n  const length = list.length >>> 0\n  const thisArg = args[2]\n\n  for (let i = 0; i < length; i++) {\n    const element = list[i]\n    if (callback.call(thisArg, element, i, list)) {\n      return element\n    }\n  }\n\n  return undefined\n}\n"
  },
  {
    "path": "lib/hooks/useEventListener.ts",
    "content": "import { useEffect, useRef } from 'react'\n\ntype AllEventMaps = HTMLElementEventMap & DocumentEventMap & WindowEventMap\n\nexport function useEventListener<K extends keyof HTMLElementEventMap>(\n  type: K,\n  listener: (event: HTMLElementEventMap[K]) => void,\n  element: HTMLElement\n): void\n\nexport function useEventListener<K extends keyof DocumentEventMap>(\n  type: K,\n  listener: (event: DocumentEventMap[K]) => void,\n  element: Document\n): void\n\nexport function useEventListener<K extends keyof WindowEventMap>(\n  type: K,\n  listener: (event: WindowEventMap[K]) => void,\n  element?: Window\n): void\n\nexport function useEventListener<K extends keyof AllEventMaps>(\n  type: K,\n  listener: (event: AllEventMaps[K]) => void,\n  element?: HTMLElement | Document | Window | null\n) {\n  const listenerRef = useRef(listener)\n\n  useEffect(() => {\n    listenerRef.current = listener\n  })\n\n  useEffect(() => {\n    const el = element === undefined ? window : element\n\n    const internalListener = (ev: AllEventMaps[K]) => {\n      return listenerRef.current(ev)\n    }\n\n    el?.addEventListener(\n      type,\n      internalListener as EventListenerOrEventListenerObject\n    )\n\n    return () => {\n      el?.removeEventListener(\n        type,\n        internalListener as EventListenerOrEventListenerObject\n      )\n    }\n  }, [type, element])\n}\n"
  },
  {
    "path": "lib/hooks/useIsTouchdevice.ts",
    "content": "import { useEffect, useState } from 'react'\n\nconst useIsTouchdevice = (): boolean => {\n  const [isTouchdevice, setIsTouchdevice] = useState<boolean>()\n\n  useEffect(() => {\n    if (typeof window !== 'undefined') {\n      setIsTouchdevice(window.matchMedia('(hover: none)').matches)\n    }\n  }, [])\n\n  return isTouchdevice\n}\n\nexport default useIsTouchdevice\n"
  },
  {
    "path": "lib/index.ts",
    "content": "import AnimatedCursor from './AnimatedCursor'\nexport default AnimatedCursor\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-animated-cursor\",\n  \"version\": \"2.11.1\",\n  \"description\": \"An animated custom cursor component in React.\",\n  \"author\": \"Stephen Scaff <stephenscaff.com>\",\n  \"homepage\": \"https://stephenscaff.github.io/react-animated-cursor/\",\n  \"main\": \"dist/index.js\",\n  \"module\": \"dist/index.es.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"browser\": \"dist/index.umd.js\",\n  \"files\": [\n    \"dist/index.js\",\n    \"dist/index.es.js\",\n    \"dist/index.umd.js\",\n    \"dist/index.d.ts\"\n  ],\n  \"targets\": {\n    \"main\": false,\n    \"module\": false,\n    \"browser\": false,\n    \"types\": false\n  },\n  \"scripts\": {\n    \"clean\": \"rm -rf ./dist\",\n    \"build\": \"rollup -c\",\n    \"dev\": \"parcel ./docs/src/index.html --dist-dir ./docs/dist\",\n    \"demo:clean\": \"rm -rf ./docs/dist\",\n    \"demo:start\": \"parcel ./docs/src/index.html --dist-dir ./docs/dist\",\n    \"demo:build\": \"parcel build ./docs/src/index.html --dist-dir ./docs/dist --public-url ./\",\n    \"demo:deploy\": \"npm run demo:build && gh-pages -d ./docs/dist\",\n    \"prepare\": \"npm run build\",\n    \"prepublish\": \"rm -rf ./dist && npm run build\",\n    \"lint\": \"eslint \\\"lib/**/*.+(ts|tsx)\\\" --fix \",\n    \"format\": \"prettier --write  \\\"lib/**/*.+(ts|tsx)\\\"\"\n  },\n  \"keywords\": [\n    \"react cursor\",\n    \"custom cursor\",\n    \"animated cursor\"\n  ],\n  \"license\": \"ISC\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/stephenscaff/react-animated-cursor\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/stephenscaff/react-animated-cursor/issues\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\"\n  },\n  \"devDependencies\": {\n    \"@rollup/plugin-commonjs\": \"^25.0.0\",\n    \"@rollup/plugin-node-resolve\": \"^15.0.2\",\n    \"@rollup/plugin-replace\": \"^5.0.2\",\n    \"@rollup/plugin-typescript\": \"^11.1.1\",\n    \"@types/react\": \"^18.2.6\",\n    \"@types/react-dom\": \"^18.2.4\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.59.7\",\n    \"@typescript-eslint/parser\": \"^5.36.2\",\n    \"eslint\": \"^8.41.0\",\n    \"eslint-config-prettier\": \"^8.8.0\",\n    \"eslint-plugin-prettier\": \"^4.2.1\",\n    \"eslint-plugin-react\": \"^7.32.2\",\n    \"eslint-plugin-react-hooks\": \"^4.6.0\",\n    \"gh-pages\": \"^5.0.0\",\n    \"parcel\": \"^2.3.2\",\n    \"prettier\": \"^2.0.5\",\n    \"process\": \"^0.11.10\",\n    \"react\": \"18.2.0\",\n    \"react-dom\": \"18.2.0\",\n    \"rollup\": \"^3.22.0\",\n    \"rollup-plugin-dts\": \"^5.3.0\",\n    \"rollup-plugin-peer-deps-external\": \"^2.2.2\",\n    \"typescript\": \"^5.0.4\"\n  }\n}\n"
  },
  {
    "path": "readme.md",
    "content": "# React Animated Cursor\n\nA React component that replaces the native cursor with a custom animated [jawn](https://www.urbandictionary.com/define.php?term=Jawn). Available options and props allow you to easily craft a unique cursor experience.\n\n## Contents\n\n1. [📌 Features](#-features)\n2. [🎯 Quickstart](#-quickstart)\n3. [🤖 Commands](#-commands)\n4. [🧬 Options](#-options)\n5. [🕹️ Usage](#-usage)\n6. [🎨 Cursor Types](#-cursor-types)\n7. [📓 Notes](#-notes)\n8. [📅 To Dos](#-to-dos)\n\n<br/>\n\n## 📌 Features\n\n### The custom cursor is comprised of\n\n- An inner dot (`cursorInner`)\n- An outer, outlining circle (`cursorOuter`), with slight opacity based on the dot/primary color\n- A slight trailing animation of the outer outline\n- An inversely scaling effect between the inner and outer cursor parts on click or link hover\n\nOptions exist for modifying the color and scaling of the cursor elements (see props/options below). Style props for in the inner and outer cursor allow you to easily create unique cursor types.\n\n[Live Demo→](https://stephenscaff.github.io/react-animated-cursor/)\n\n<br/>\n\n## 🎯 Quickstart\n\n### Install package from npm\n\n`npm i react-animated-cursor`\n\n### Add to you project\n\nAdd to a global location, like `_app.js`\n\n```\nimport React from \"react\";\nimport AnimatedCursor from \"react-animated-cursor\"\n\nexport default function App() {\n  return (\n    <div className=\"App\">\n      <AnimatedCursor />\n    </div>\n  );\n}\n```\n\n<br>\n\n## 🤖 Commands\n\n**Install** `npm i react-animated-cursor` <br/>\n**Build**: `npm run build` <br/>\n**Dev**: `npm run dev` <br/>\n**Demo Run**: `npm run demo:start` <br/>\n**Demo Build**: `npm run demo:build` <br/>\n**Demo Clean**: `npm run demo:clean` <br/>\n\n### Demo\n\nThe demo is bundled with [`Parcel.js`](https://parceljs.org/) and served up at [http://localhost:1234/](http://localhost:1234/).\n\n### Dist\n\nOn build, `lib` populates `dist` with commonjs, es, umd versions of the component.\n\n<br/>\n\n## 🕹️ Usage\n\n```\nimport React from \"react\";\nimport AnimatedCursor from \"react-animated-cursor\"\n\n\nexport default function App() {\n  return (\n    <div className=\"App\">\n      <AnimatedCursor />\n    </div>\n  );\n}\n```\n\n### Example Usage - with options\n\n```\nimport React from \"react\";\nimport AnimatedCursor from \"react-animated-cursor\"\n\nexport default function App() {\n  return (\n    <div className=\"App\">\n    <AnimatedCursor\n      innerSize={8}\n      outerSize={8}\n      color='193, 11, 111'\n      outerAlpha={0.2}\n      innerScale={0.7}\n      outerScale={5}\n      clickables={[\n        'a',\n        'input[type=\"text\"]',\n        'input[type=\"email\"]',\n        'input[type=\"number\"]',\n        'input[type=\"submit\"]',\n        'input[type=\"image\"]',\n        'label[for]',\n        'select',\n        'textarea',\n        'button',\n        '.link'\n      ]}\n    />\n    </div>\n  );\n}\n```\n\n### Example Usage - with simple options and custom config for one class\n\n```\nimport React from \"react\";\nimport AnimatedCursor from \"react-animated-cursor\"\n\nexport default function App() {\n  return (\n    <div className=\"App\">\n    <AnimatedCursor\n      innerSize={8}\n      outerSize={8}\n      color='193, 11, 111'\n      outerAlpha={0.2}\n      innerScale={0.7}\n      outerScale={5}\n      clickables={[\n        'a',\n        'input[type=\"text\"]',\n        'input[type=\"email\"]',\n        'input[type=\"number\"]',\n        'input[type=\"submit\"]',\n        'input[type=\"image\"]',\n        'label[for]',\n        'select',\n        'textarea',\n        'button',\n        '.link',\n        {\n          target: '.custom',\n          options: {\n            innerSize: 12,\n            outerSize: 12,\n            color: '255, 255, 255',\n            outerAlpha: 0.3,\n            innerScale: 0.7,\n            outerScale: 5\n          }\n        }\n      ]}\n    />\n    </div>\n  );\n}\n```\n\n### Client Components, Next.js, SSR\n\nIn previous versions of the component, integration with Next's SSR environment required using a `Dynamic Import`.\nHowever, as of version `2.10.1`, **you _should_ be good to go with a simple `import`.**\n\nRelevant updates:\n\n- Included module directive `'use client'` to indicate a client side component.\n- Updated `useEventListener` hook with `window` checks.\n- Wrapped the `document` use in a check.\n\nHowever, if you do run into any issues, you could try including with Dynamic Import.\n\n**Next's Dynamic Import**\n\n```\n'use client'; // indicates Client Component\n\n// Import with next's dynamic import\nimport dynamic from 'next/dynamic';\n\nconst AnimatedCursor = dynamic(() => import('react-animated-cursor'), {\n    ssr: false,\n});\n\n<AnimatedCursor/>\n```\n\n<br/>\n\n## 🧬 Options\n\n<!-- prettier-ignore -->\n| Option | Type | Description      | Default |\n| ----   | ---- | -------- | -------|\n| `clickables`    | array  | Collection of selectors cursor that trigger cursor interaction or object with single target and possibly the rest of the options listed below | `['a', 'input[type=\"text\"]', 'input[type=\"email\"]', 'input[type=\"number\"]', 'input[type=\"submit\"]', 'input[type=\"image\"]', 'label[for]', 'select', 'textarea', 'button', '.link']` |\n| `color`      | string | rgb value  | `220, 90, 90` |\n| `innerScale` | number | amount dot scales on click or link hover | `0.7` |\n| `innerSize`  | number | Size (px) of inner cursor dot | `8` |\n| `innerStyle` | object | provides custom styles / css to inner cursor  | `null` |\n| `outerAlpha` | number | amount of alpha transparency for outer cursor dot    | `0.4`  |\n| `outerScale` | number | amount outer dot scales on click or link hover  | `5`  |\n| `outerSize`  | number | Size (px) of outer cursor outline  | `8` |\n| `outerStyle` | object | provides custom styles / css to outer cursor  | `null` |\n| `showSystemCursor` | boolean | Show system/brower cursor | `false` |\n| `trailingSpeed` | number | Outer dot's trailing speed | `8` |\n\n<br/>\n\n## 🎨 Cursor Types\n\nYou can use the `innerStyle` and `outerStyle` props to provide custom styles and create a variery of custom cursor types. Additionally, you can pass custom styles and css vars to create unique cursors or update style based on events.\n\n### Dynamic Styles\n\nUse CSS variables with `innerStyle` and `outerStyle` props to create dynamic styles that you can easily update.\nFor example, perhaps you have a light and dark mode experience and what your cursor to also adapt it's colors.\n\n**CSS Vars**\n\n```\nhtml {\n  --cursor-color: #333\n}\n\nhtml.dark-mode {\n  --cursor-color: #fff\n}\n```\n\n**Pass CSS Var as Style Props**\n\n```\n<AnimatedCursor\n  innerSize={8}\n  outerSize={35}\n  innerScale={1}\n  outerScale={1.7}\n  outerAlpha={0}\n  outerStyle={{\n    border: '3px solid var(--cursor-color)'\n  }}\n  innerStyle={{\n    backgroundColor: 'var(--cursor-color)'\n  }}\n/>\n```\n\n### Donut Cursor\n\nA donut style cursor basically resembles a donut. You can easily create on by applying using the `outerStyle` props to apply an outer border\n\n```\n<AnimatedCursor\n  innerSize={8}\n  outerSize={35}\n  innerScale={1}\n  outerScale={2}\n  outerAlpha={0}\n  hasBlendMode={true}\n  innerStyle={{\n    backgroundColor: 'var(--cursor-color)'\n  }}\n  outerStyle={{\n    border: '3px solid var(--cursor-color)'\n  }}\n/>\n```\n\n[Donut Demo→](https://stephenscaff.github.io/react-animated-cursor?cursor=donut)\n\n<br/>\n\n### Blend Mode Cursor\n\nYou can use CSS mix-blend-mode with the style props to create an intersting cursor effect on hover that inverts the content's color. Works best with white / black cursors.\n\n```\n<AnimatedCursor\n  color=\"#fff\"\n  innerSize={8}\n  outerSize={35}\n  innerScale={1}\n  outerScale={1.7}\n  outerAlpha={0}\n  outerStyle={{\n    mixBlendMode: 'exclusion'\n  }}\n/>\n```\n\n[Blend Mode Demo→](https://stephenscaff.github.io/react-animated-cursor?cursor=blendmode)\n\n<br/>\n\n## 📓 Notes\n\n### Mobile / Touch\n\n`helpers/isDevice.js` uses UA sniffing to determine if on a common device so we can avoid rendering cursors. Yes... I know, there are other and probably better ways to handle this. Whatevers.\n\n<br/>\n\n## 📅 To Dos\n\n- ~~Either remove on mobile, or provide touch events.~~\n- ~~Separate click and hover scalings to provide a different scaling when clicking on links/clickables~~\n- ~~Fix transform blur in Safari, which may mean migrating from `scale` to a `width` &`height` update~~ 4/4/23\n- ~~Make clickables (cursor targets / selectors) a prop~~\n- ~~Add PropType checks~~\n- ~~Open cursor styles as props~~\n- ~~Add ability to maintain system cursor for the squeamish~~ 4/4/23\n- ~~Migrate to TS~~\n- ~~Allow for different behavior based on the element hovered~~\n- Options to control cursor transition speed and bezier\n- Solution for impacting state during route changes\n\n- Add some proper tests\n\n<br/>\n\nHave fun ya'll.\n"
  },
  {
    "path": "rollup.config.mjs",
    "content": "import commonjs from '@rollup/plugin-commonjs'\nimport external from 'rollup-plugin-peer-deps-external'\nimport resolve from '@rollup/plugin-node-resolve'\nimport typescript from '@rollup/plugin-typescript'\nimport dts from 'rollup-plugin-dts'\nimport pkg from './package.json' assert { type: 'json' }\n\nconst umdGlobals = {\n  react: 'React',\n  'react-animated-cursor': 'AnimatedCursor',\n  'react/jsx-runtime': 'jsxRuntime'\n}\n\nconst config = [\n  {\n    external: ['react', 'react-dom'],\n    input: 'lib/index.ts',\n    output: [\n      {\n        file: pkg.main,\n        format: 'cjs',\n        banner: \"'use client';\"\n      },\n      {\n        file: pkg.module,\n        format: 'esm',\n        banner: \"'use client';\"\n      },\n      {\n        file: pkg.browser,\n        format: 'umd',\n        name: 'AnimatedCursor',\n        globals: umdGlobals,\n        banner: \"'use client';\"\n      }\n    ],\n    plugins: [\n      external(),\n      resolve(),\n      commonjs(),\n      typescript({\n        exclude: 'node_modules'\n      })\n    ]\n  },\n  {\n    external: ['react', 'react-dom'],\n    input: 'lib/index.ts',\n    output: [{ file: pkg.types, format: 'es' }],\n    plugins: [external(), resolve(), dts()]\n  }\n]\n\nexport default config\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"jsx\": \"react-jsx\",\n    \"moduleResolution\": \"node\",\n    \"target\": \"es5\",\n    \"esModuleInterop\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"allowJs\": true\n  },\n  \"include\": [\"lib\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  }
]