[
  {
    "path": ".github/workflows/pull-request.yml",
    "content": "name: Run tests\n\non: pull_request\n\njobs:\n  run-tests:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v2\n        with:\n          node-version: 16\n      - run: npm i -g npm@latest\n      - run: npm ci\n      - run: npm test\n\n  run-eslint:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v2\n        with:\n          node-version: 16\n      - run: npm i -g npm@latest\n      - run: npm ci\n      - run: npm run eslint\n"
  },
  {
    "path": ".github/workflows/release-package.yml",
    "content": "name: Release Package\n\non:\n  release:\n    types: [created]\n\njobs:\n  run-tests:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v2\n        with:\n          node-version: 16\n      - run: npm i -g npm@latest\n      - run: npm ci\n      - run: npm test\n\n  run-eslint:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v2\n        with:\n          node-version: 16\n      - run: npm i -g npm@latest\n      - run: npm ci\n      - run: npm run eslint\n\n  publish-gpr:\n    needs:\n      - run-tests\n      - run-eslint\n    runs-on: ubuntu-latest\n    permissions:\n      packages: write\n      contents: read\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v2\n        with:\n          node-version: 16\n          registry-url: https://npm.pkg.github.com/\n      - run: npm i -g npm@latest\n      - run: npm ci\n      - run: npm publish\n        env:\n          NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}\n"
  },
  {
    "path": ".gitignore",
    "content": "# OS\n.DS_Store\n\n# Cache\n.cache\n.playwright\n.tmp\n*.tsbuildinfo\n.eslintcache\n\n# Yarn\n.pnp.*\n**/.yarn/*\n!**/.yarn/patches\n!**/.yarn/plugins\n!**/.yarn/releases\n!**/.yarn/sdks\n!**/.yarn/versions\n\n# Project-generated directories and files\ncoverage\ndist\nnode_modules\nplaywright-report\ntest-results\npackage.tgz\n\n# Logs\nnpm-debug.log\nyarn-error.log\n\n# .env files\n**/.env\n**/.env.*\n!**/.env.example\n"
  },
  {
    "path": ".npmignore",
    "content": "/*.log\r\n/*.DS_Store\r\n/.idea\r\n/.gitignore\r\n/node_modules/\r\n/.github/\r\n/src/\r\n/babel.config.js\r\n**/*.spec.js\r\n/.eslintignore\r\n/.prettierrc\r\n/.eslintrc.js\r\n"
  },
  {
    "path": ".nvmrc",
    "content": "v16\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\r\n\t\"endOfLine\": \"lf\",\r\n\t\"semi\": false,\r\n\t\"singleQuote\": true,\r\n\t\"useTabs\": false,\r\n\t\"trailingComma\": \"es5\"\r\n}\r\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Harry Fox\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": "[![Npm Version][npm-version-image]][npm-version-url]\n[![Downloads][downloads-image]][downloads-url]\n[![License][license-image]][license-url]\n\n\n# react-best-gradient-color-picker\n- Customizable, easy to use color and gradient picker for React.js\n- Simply pass in an rgba or css gradient string as value and an onChange handler \n- Variety of optional tools like eye dropper, advanced color settings, and color guide\n- use the useColorPicker hook for complete control over of the picker\n- You can completly customize the UI by hiding the included elements and using the hook to build your own\n- You can also customize preset options by passing in an array of rgba colors (see custom presets below)\n\n<br /> \n\n<img alt=\"\" src=\"https://gradient-package-demo.web.app/gradientPickerImg.png\" width=\"100%\" />\n\n<br />\n\n<a id=\"item-one\"></a>\n## Install\n```\nnpm install react-best-gradient-color-picker\n```\n\n```\nyarn add react-best-gradient-color-picker\n```\n\n<a id=\"item-two\"></a>\n## Demo\nSee the picker in action [here](https://gradient-package-demo.web.app/)\n\n<br />\n\n**Table of Contents**\n- [Basic Example](#item-three)\n- [Props](#item-four)\n- [API](#item-five)\n- [useColorPicker Hook](#item-six)\n- [Hook Basic Example](#item-seven)\n- [Hook Functions](#item-eight)\n- [Hook State](#item-nine)\n- [More Hook Examples](#item-ten)\n- [Styling](#item-fifteen)\n- [Legacy Options](#item-eleven)\n- [Roadmap](#item-twelve)\n- [License](#item-thirteen)\n- [Acknowledgments](#item-fourteen)\n\n<br />\n\n<a id=\"item-three\"></a>\n## Basic Example \n```js\nimport React from 'react'\nimport ColorPicker from 'react-best-gradient-color-picker'\n\nfunction MyApp() {\n  const [color, setColor] = useState('rgba(255,255,255,1)');\n\n  return <ColorPicker value={color} onChange={setColor} />\n}\n```\n\n<br />\n\n<a id=\"item-four\"></a>\n### Props\n\n| Name                | Type         | Default                 | Description                                                               |\n|---------------------|--------------| ----------------------- |---------------------------------------------------------------------------|\n| value               | `string`     | 'rgba(175, 51, 242, 1)' | The starting color                                                        |\n| width               | `int`        | 294                     | (optional) The width of the picker                                        |\n| height              | `int`        | 294                     | (optional) The height of the picker                                       |\n| hideInputs          | `boolean`    | `false`                 | (optional) hide the hex and rgba inputs                                   |\n| hideOpacity         | `boolean`    | `false`                 | (optional) hide the opacity bar                                           |\n| hideHue             | `boolean`    | `false`                 | (optional) hide the hue bar                                               |\n| hideControls        | `boolean`    | `false`                 | (optional) hide the entire top row of various control btns                |\n| hideColorTypeBtns   | `boolean`    | `false`                 | (optional) hide the solid/gradient buttons                                |\n| hidePresets         | `boolean`    | `false`                 | (optional) hide the preset color options                                  |\n| hideEyeDrop         | `boolean`    | `false`                 | (optional) hide (and disable the eye dropper tool                         |\n| hideAdvancedSliders | `boolean`    | `false`                 | (optional) hide the additional sliders (saturation, luminence, brightness |\n| hideColorGuide      | `boolean`    | `false`                 | (optional) hide the color guide, a tool that shows color pairings         |\n| hideInputType       | `boolean`    | `false`                 | (optional) hide the input type selector, looking the type                 |\n| hideGradientType    | `boolean`    | `false`                 | (optional) hide the linear/circular gradient type toggle (only relevant in gradient mode)|\n| hideGradientAngle   | `boolean`    | `false`                 | (optional) hide the gradient angle input (only relevant in gradient mode with a linear gradient)|\n| hideGradientStop    | `boolean`    | `false`                 | (optional) hide the gradient point stop input (only relevant in gradient mode)|\n| hideGradientControls| `boolean`    | `false`                 | (optional) hide the all gradient controls (the bar that appears below top controls when in gradient mode)|\n| hidePickerSquare    | `boolean`    | `false`                 | (optional) hide the main picker color swatch (the square that appears at the top)|\n| showHexAlpha        | `boolean`    | `false`                 | (optional) add alpha (AA) channel to hex value which represents the opacity of the color|\n| presets             | `array`      | ['rgba(0,0,0,1)', ...]  | (optional) pass in custom preset options ['rgba()', 'rgba()', ..]         |\n| locales             | `object`     | { CONTROLS: { SOLID: 'Solid', GRADIENT: 'Gradient' }}  | (optional) pass in custom locales |\n| disableDarkMode     | `boolean`    | false                    | (optional) disable automatic dark mode style adjustments                 |\n| disableLightMode    | `boolean`    | false                    | (optional) force the component to only use the dark mode styles          |\n| className           | `string`     | ''                       | (optional) a CSS class for the picker parent (see styling for more options)|\n| idSuffix            | `string`     | ''                       | (optional) Adds a suffix to all the ids of the picker elements, useful when using multiple pickers on the same page|\n\n<a id=\"item-five\"></a>\n### API\n\n| Name             | Description                                                      |\n| ---------------- | ---------------------------------------------------------------- |\n| onChange         | A function to update color value                                 |\n\n<br />\n\n<a id=\"item-six\"></a>\n# useColorPicker \n\n- Take complete control of the picker\n- Get current state\n- Convert between color types\n\n<a id=\"item-seven\"></a>\n## Basic Example \n\n- Initialize the hook by passing in the same color value and onChange handler\n\n```js\nimport React from 'react'\nimport ColorPicker, { useColorPicker } from 'react-best-gradient-color-picker'\n\nfunction MyApp() {\n  const [color, setColor] = useState('linear-gradient(90deg, rgba(96,93,93,1) 0%, rgba(255,255,255,1) 100%)');\n  const { setSolid, setGradient } = useColorPicker(color, setColor);\n\n  return(\n    <div>\n      <button onClick={setSolid}>Solid</button>\n      <button onClick={setGradient}>Gradient</button>\n      <ColorPicker value={color} onChange={setColor} />\n    </div>\n   )\n}\n```\n\n<a id=\"item-eight\"></a>\n### Included Functions \n\n| Name             | Arguments        | Description                                                      |\n| ---------------- | ---------------- | ---------------------------------------------------------------- |\n| handleChange     | value (rgba string)| Most useful for setting color value of the selectedPoint without overwriting entire gradient string. Only pass this function a single color value, not a gradient |\n| setLinear        |                  | Change the type of gradient to linear                            |\n| setRadial        |                  | Change the type of gradient to radial                            |\n| setDegrees       | degrees (num, 0 - 360)| Change the degrees of a linear gradient                     |\n| setSolid         | (optional) new solid color (rgba string) | Change the pickers color mode from gradient to solid |\n| setGradient      | (optional) new gradient (CSS gradient) | Change the pickers color mode from solid to gradient |\n| setR             | value (num, 0 - 255) | Update the red value of the color                            |\n| setG             | value (num, 0 - 255) | Update the green value of the color                          |\n| setB             | value (num, 0 - 255) | Update the blue value of the color                           |\n| setA             | value (num, 0 - 100) | Update the opacity (alpha) of a color                        |\n| setHue           | value (num, 0 - 360) | Update the hue of a color                                    |\n| setSaturation    | value (num, 0 - 100) | Update the saturation of a color                             |\n| setLightness     | value (num, 0 - 100) | Update the lightness of a color                              |\n| valueToHSL       |                  | Get the current value in HSL                                     |\n| valueToHSV       |                  | Get the current value in HSV                                     |\n| valueToHex       |                  | Get the current value in HEX                                     |\n| valueToCmyk      |                  | Get the current value in CMYK                                    |\n| setSelectedPoint | index of point (num) | Update which individual color of a gradient is in focus      |\n| deletePoint      | index of point (num) | Delete one of the gradients colors                           |\n| addPoint         | position of point (num, 0 - 100) | Add a new color to the gradient                  |\n| setPointLeft     | value (num, 0 - 100) | Update the position (left) of the currently selected gradient color |\n| getGradientObject|                      | get the gradients value parsed into a key/value object (see example below)|\n\n<a id=\"item-nine\"></a>\n### Available State\n\n| Name             | Description                                                      |\n| ---------------- | ---------------------------------------------------------------- |\n| selectedPoint    | returns index of which color point of a gradient is currently selected |\n| isGradient       | returns which mode the picker is in, solid or gradient           |\n| gradientType     | which gradient type is currently selected, linear or radial      |\n| degrees          | current degrees of a radial gradient                             |\n| currentLeft      | the position of the selected gradient color                      |\n| rgbaArr          | get the current rgba values in an array                          |\n| hslArr           | get the current hsl values in an array                           |\n\n<a id=\"item-ten\"></a>\n## Various Customization Examples\n\n### Custom Gradient Controls\n\n```js\nimport React from 'react'\nimport ColorPicker, { useColorPicker } from 'react-best-gradient-color-picker'\n\nfunction MyApp() {\n  const [color, setColor] = useState('linear-gradient(90deg, rgba(96,93,93,1) 0%, rgba(255,255,255,1) 100%)');\n  const { gradientType, setLinear, setRadial, addPoint, deletePoint, degrees, setDegrees, setPointLeft, currentLeft, selectedPoint } = useColorPicker(color, setColor);\n\n  return(\n    <div>\n      <button onClick={setLinear}>Linear</button>\n      <button onClick={setRadial}>Radial</button>\n      {gradientType === 'linear-gradient' && <input value={degrees} onChange={(e) => setDegrees(e.target.value)} />}\n      <input value={currentLeft} onChange={(e) => setPointLeft(e.target.value)} />\n      <button onClick={() => addPoint(50)}>Add Color</button>\n      <button onClick={() => deletePoint(selectedPoint)}>Delete Color</button>\n      <ColorPicker value={color} onChange={setColor} hideControls={true} />\n    </div>\n   )\n}\n```\n\n### Custom RGBA Inputs\n\n```js\nimport React from 'react'\nimport ColorPicker, { useColorPicker } from 'react-best-gradient-color-picker'\n\nfunction MyApp() {\n  const [color, setColor] = useState('linear-gradient(90deg, rgba(96,93,93,1) 0%, rgba(255,255,255,1) 100%)');\n  const { setR, setG, setB, setA, rgbaArr } = useColorPicker(color, setColor);\n\n  return(\n    <div>\n      <input value={rgbaArr[0]} onChange={(e) => setR(e.target.value)} />\n      <input value={rgbaArr[1]} onChange={(e) => setG(e.target.value)} />\n      <input value={rgbaArr[2]} onChange={(e) => setB(e.target.value)} />\n      <input value={rgbaArr[3]} onChange={(e) => setA(e.target.value)} />\n      <ColorPicker value={color} onChange={setColor} hideInputs={true} />\n    </div>\n   )\n}\n```\n\n\n### Conversions\n\n```js\nimport React from 'react'\nimport ColorPicker, { useColorPicker } from 'react-best-gradient-color-picker'\n\nfunction MyApp() {\n  const [color, setColor] = useState('linear-gradient(90deg, rgba(96,93,93,1) 0%, rgba(255,255,255,1) 100%)');\n  const { valueToHSL, valueToHSV, valueToHex, valueToCmyk, rgbaArr, hslArr } = useColorPicker(color, setColor);\n\n  const hslString = valueToHSL();\n  const hsvString = valueToHSV();\n  const hexString = valueToHex();\n  const cmykString = valueToCmyk();\n  const rgbaArray = rgbaArr;\n  const hslArray = hslArr;\n\n  return(\n    <div>\n      <ColorPicker value={color} onChange={setColor} />\n    </div>\n   )\n}\n```\n\n### Custom Presets Example \n\n```js\nimport React from 'react'\nimport ColorPicker from 'react-best-gradient-color-picker'\n\nconst customPresets = [\n  'rgba(34, 164, 65, 1)',\n  'rgba(210, 18, 40, .5)',\n  'rgba(90, 110, 232, 1)',\n  'rgba(65, 89, 56, 1)',\n  'rgba(98, 189, 243, 1)',\n  'rgba(255, 210, 198, 1)',\n  'rgba(94, 94, 94, 1)'\n] //max 18 colors, you can pass in more but the array will be sliced to the first 18\n\nfunction MyApp() {\n  const [color, setColor] = useState('rgba(255,255,255,1');\n\n  return <ColorPicker value={color} onChange={setColor} presets={customPresets} />\n}\n```\n\nYou may also want to provide the users recently used colors in lieu of preset options. This can be easily accomplished use the hook. \n\n```js\nimport React from 'react'\nimport ColorPicker, { useColorPicker } from 'react-best-gradient-color-picker'\n\nfunction MyApp() {\n  const [color, setColor] = useState('linear-gradient(90deg, rgba(96,93,93,1) 0%, rgba(255,255,255,1) 100%)');\n  const { previousColors } = useColorPicker(color, setColor);\n\n  return(\n    <div>\n      <ColorPicker value={color} onChange={setColor} presets={previousColors} />\n    </div>\n   )\n}\n```\n\n### Custom Locales Example \nYou can pass custom locales via `locales` prop.\n\n```js\nimport React from 'react'\nimport ColorPicker, { useColorPicker } from 'react-best-gradient-color-picker'\n\nfunction MyApp() {\n  const customLocales = {\n    CONTROLS: {\n      SOLID: 'Obične',\n      GRADIENT: 'Gradijent',\n    },\n  }\n  return (\n    <div>\n      <ColorPicker locales={customLocales} />\n    </div>\n  )\n}\n```\n\n### Getting Value in Object Form\nThe picker returns the new value as a css gradient string but you may need it parsed as an object. This can easily be accomplised by using the getGradientObject function returned by the useColorPicker hook like so:\n\n```js\nimport React from 'react'\nimport ColorPicker, { useColorPicker } from 'react-best-gradient-color-picker'\n\nfunction MyApp() {\n  const [color, setColor] = useState('linear-gradient(90deg, rgba(96,93,93,1) 0%, rgba(255,255,255,1) 100%)');\n  const { getGradientObject } = useColorPicker(color, setColor);\n  const gradientObject = getGradientObject();\n  \n  // example value\n  // {\n  //   \"isGradient\": true,\n  //   \"gradientType\": \"linear-gradient\",\n  //   \"degrees\": \"40deg\",\n  //   \"colors\": [\n  //       {\n  //           \"value\": \"rgba(27,107,235,1)\",\n  //           \"left\": 0\n  //       },\n  //       {\n  //           \"value\": \"rgba(25,245,157,1)\",\n  //           \"left\": 100\n  //       }\n  //     ]\n  // }\n\n  return(\n    <div>\n      <ColorPicker value={color} onChange={setColor} presets={previousColors} />\n    </div>\n   )\n}\n```\n\n### Only Gradients Example\nIf you would like to not allow selection of solid colors disable the color type buttons and feed in the initial value as a gradient like below:\n\nNOTE: the same can be done in reverse to only allow selection of solid colors\n\n```js\nimport React from 'react'\nimport ColorPicker, { useColorPicker } from 'react-best-gradient-color-picker'\n\nfunction MyApp() {\n  const [color, setColor] = useState('linear-gradient(90deg, rgba(96,93,93,1) 0%, rgba(255,255,255,1) 100%)');  \n\n  return(\n    <div>\n      <ColorPicker\n        value={color}\n        onChange={setColor}\n        hideColorTypeBtns={true}\n      />\n    </div>\n   )\n}\n```\n\n<br />\n\n<a id=\"item-fifteen\"></a>\n## Styling\nMost of the pickers components have inline styles applied to them, these essential function as classNames. In order to update, identify the key being applied to the desired component and add any styles as a nested object to the `style` object which can be passed into the picker. See below list of style keys and example.\n\n<br />\n\n| Key              | Description                                                      |\n| -------------------------------- | ------------------------------------------------ |\n| body                             | ComingSoon |\n| rbgcpControlBtnWrapper           | ComingSoon |\n| rbgcpControlBtn                  | ComingSoon |\n| rbgcpControlIconBtn              | ComingSoon |\n| rbgcpControlIcon                 | ComingSoon |\n| rbgcpColorModelDropdown          | ComingSoon |\n| rbgcpEyedropperCover             | ComingSoon |\n| rbgcpControlInputWrap            | ComingSoon |\n| rbgcpControlInput                | ComingSoon |\n| rbgcpInputLabel                  | ComingSoon |\n| rbgcpInput                       | ComingSoon |\n| rbgcpHandle                      | ComingSoon |\n| rbgcpCanvasWrapper               | ComingSoon |\n| rbgcpOpacityOverlay              | ComingSoon |\n| rbgcpGradientHandleWrap          | ComingSoon |\n| rbgcpGradientHandle              | ComingSoon |\n| rbgcpControlIcon2                | ComingSoon |\n| rbgcpControlBtnSelected          | ComingSoon |\n| rbgcpComparibleLabel             | ComingSoon |\n| rbgcpColorModelDropdownBtn       | ComingSoon |\n| rbgcpStopInputWrap               | ComingSoon |\n| rbgcpStopInput                   | ComingSoon |\n| rbgcpDegreeInputWrap             | ComingSoon |\n| rbgcpDegreeInput                 | ComingSoon |\n| rbgcpDegreeIcon                  | ComingSoon |\n| rbgcpEyedropperBtn               | ComingSoon |\n| rbgcpHexInput                    | ComingSoon |\n| rbgcpInputsWrap                  | ComingSoon |\n\n<br />\n\n<a id=\"item-eleven\"></a>\n## LEGACY V1 - Manual Control - Customizing UI\n\nThis still works, although most functions are available through the useColorPicker hook, if there is something you need that is not available you could use the below methods to create your desired functionality.\n\nThe state of the picker is determined by parsing the value string. You can update props like colorType (solid/gradient), gradientType (linear/radial), gradientDegrees, hex, rgba, opacity and hue simply by updating the value you are passing into the component. Let's say you want to change the colorType from gradient to solid: \n\n```js\nimport React from 'react'\nimport ColorPicker from 'react-best-gradient-color-picker'\n\nfunction MyApp() {\n  const [color, setColor] = useState('linear-gradient(90deg, rgba(96,93,93,1) 0%, rgba(255,255,255,1) 100%)');\n  \n  const setSolid = () => {\n    setColor('rgba(255,255,255,1)') //color could be any rgba value\n  }\n\n  return(\n    <div>\n      <button onClick={setSolid}>Solid</button>\n      <ColorPicker value={color} onChange={setColor} />\n    </div>\n   )\n}\n```\nThe same can be done in inverse to change colorType from solid to gradient:\n\n```js  \n  const setGradient = () => {\n    setColor('linear-gradient(90deg, rgba(96,93,93,1) 0%, rgba(255,255,255,1) 100%)')\n  }\n```\n\nExample toggling gradientType \n\n```js  \n  const setLinear = () => {\n    setColor('linear-gradient(90deg, rgba(96,93,93,1) 0%, rgba(255,255,255,1) 100%)')\n  }\n  \n  const setRadial = () => {\n    setColor('radial-gradient(circle, rgba(96,93,93,1) 0%, rgba(255,255,255,1) 100%)')\n  }\n```\n\nCustom linear-gradient degrees input \n\n```js\nimport React from 'react'\nimport ColorPicker from 'react-best-gradient-color-picker'\n\nfunction MyApp() {\n  const [color, setColor] = useState('linear-gradient(90deg, rgba(96,93,93,1) 0%, rgba(255,255,255,1) 100%)');\n  const degrees = parseInt(value?.split(',')[0]?.split('(')[1])\n  \n  const handleDegrees = (val) => {\n    let num = parseInt(val)\n    let nans = isNaN(num) ? 0 : num\n    let min = Math.max(nans, 0)\n    let max = Math.min(min, 360)\n    const remaining = value.split(/,(.+)/)[1]\n    setColor(`linear-gradient(${max}deg, ${remaining}`)\n  }\n\n  return(\n    <div>\n      <input value={degrees} onChange={(e) => handleDegrees(e.target.value)} />\n      <ColorPicker value={color} onChange={setColor} />\n    </div>\n   )\n}\n```\n\n<a id=\"item-twelve\"></a>\n## Roadmap\n1. enhanced mobile support\n2. cross browser eye dropper issue\n3. enhanced gradient parsing to allow additional gradient types\n\n<a id=\"item-thirteen\"></a>\n## License\n\nCode released under the [MIT](https://github.com/hxf31891/react-gradient-color-picker/blob/main/LICENSE) license.\n\n[build-image]: https://img.shields.io/github/checks-status/hxf31891/react-gradient-color-picker/main?color=%23498af2\n[license-image]: https://img.shields.io/npm/l/react-best-gradient-color-picker.svg?color=%23498af2\n[license-url]: LICENSE\n[downloads-image]: http://img.shields.io/npm/dm/react-best-gradient-color-picker.svg?color=%23498af2\n[downloads-url]: http://npm-stat.com/charts.html?package=react-best-gradient-color-picker\n[npm-version-image]: https://img.shields.io/npm/v/react-best-gradient-color-picker.svg?color=%23498af2\n[npm-version-url]: https://www.npmjs.com/package/react-best-gradient-color-picker\n\n<a id=\"item-fourteen\"></a>\n## Acknowledgments\n\nVery special thank you to [Rafael Carício](https://github.com/rafaelcaricio) for his amazing work parsing gradient strings.\n"
  },
  {
    "path": "biome.json",
    "content": "{\n  \"$schema\": \"https://biomejs.dev/schemas/1.8.3/schema.json\",\n  \"files\": {\n    \"ignore\": [\n      \".tsimp\",\n      \".yarn\",\n      \"coverage\",\n      \"dist\",\n      \".pnp.cjs\",\n      \".pnp.loader.mjs\"\n    ]\n  },\n  \"formatter\": {\n    \"lineWidth\": 100,\n    \"indentStyle\": \"space\"\n  },\n  \"linter\": {\n    \"rules\": {\n      \"complexity\": {\n        \"noUselessSwitchCase\": \"off\"\n      },\n      \"suspicious\": {\n        \"noConsoleLog\": \"warn\"\n      }\n    }\n  },\n  \"css\": {\n    \"formatter\": {\n      \"enabled\": true,\n      \"indentStyle\": \"space\",\n      \"indentWidth\": 2,\n      \"lineWidth\": 100,\n      \"quoteStyle\": \"single\"\n    }\n  },\n  \"javascript\": {\n    \"formatter\": {\n      \"quoteStyle\": \"single\"\n    }\n  },\n  \"overrides\": [\n    {\n      \"include\": [\"**/package.json\"],\n      \"formatter\": {\n        \"lineWidth\": 1\n      }\n    },\n    {\n      \"include\": [\"**/vite.config.ts\"],\n      \"linter\": {\n        \"rules\": {\n          \"suspicious\": {\n            \"noConsoleLog\": \"off\"\n          }\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-best-gradient-color-picker\",\n  \"version\": \"3.0.14\",\n  \"description\": \"An easy to use color/gradient picker for React.js\",\n  \"type\": \"module\",\n  \"sideEffects\": [\n    \"*.css\"\n  ],\n  \"main\": \"./dist/cjs/index.js\",\n  \"module\": \"./dist/esm/index.js\",\n  \"source\": \"./src/index.ts\",\n  \"types\": \"./dist/cjs/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"import\": \"./dist/esm/index.js\",\n      \"require\": \"./dist/cjs/index.js\"\n    }\n  },\n  \"scripts\": {\n    \"build\": \"yarn build-js\",\n    \"build-js\": \"yarn build-js-esm && yarn build-js-cjs && yarn build-js-cjs-package\",\n    \"build-js-esm\": \"tsc --project tsconfig.build.json --outDir dist/esm\",\n    \"build-js-cjs\": \"tsc --project tsconfig.build.json --outDir dist/cjs --module commonjs --moduleResolution node --verbatimModuleSyntax false\",\n    \"build-js-cjs-package\": \"echo '{\\n  \\\"type\\\": \\\"commonjs\\\"\\n}' > dist/cjs/package.json\",\n    \"clean\": \"rimraf dist\",\n    \"format\": \"biome format\",\n    \"lint\": \"biome lint\",\n    \"prepack\": \"yarn clean && yarn build\",\n    \"test\": \"yarn lint && yarn tsc && yarn format && yarn unit\",\n    \"tsc\": \"tsc\",\n    \"unit\": \"vitest\",\n    \"watch\": \"yarn build-js-esm --watch & yarn build-js-cjs --watch & node --eval\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/hxf31891/react-gradient-color-picker.git\"\n  },\n  \"keywords\": [\n    \"gradient\",\n    \"react\",\n    \"color\",\n    \"picker\",\n    \"react.js\",\n    \"tool\",\n    \"editor\"\n  ],\n  \"author\": {\n    \"name\": \"Harry Fox\",\n    \"email\": \"hxfox1@gmail.com\"\n  },\n  \"dependencies\": {\n    \"html2canvas\": \"^1.4.1\",\n    \"lodash.throttle\": \"^4.1.1\",\n    \"tinycolor2\": \"1.4.2\"\n  },\n  \"devDependencies\": {\n    \"@testing-library/jest-dom\": \"^6.0.0\",\n    \"@testing-library/react\": \"^14.0.0\",\n    \"@types/lodash.throttle\": \"*\",\n    \"@types/node\": \"*\",\n    \"@types/react\": \"*\",\n    \"@types/reactcss\": \"^1.2.11\",\n    \"@types/tinycolor2\": \"^1.4.6\",\n    \"cpy-cli\": \"^3.1.1\",\n    \"happy-dom\": \"^12.6.0\",\n    \"nodemon\": \"^3.0.0\",\n    \"prettier\": \"^3.0.0\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"rimraf\": \"^3.0.0\",\n    \"typescript\": \"^5.3.2\",\n    \"vitest\": \"^1.0.2\"\n  },\n  \"peerDependencies\": {\n    \"@types/react\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\",\n    \"react\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\",\n    \"react-dom\": \"^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@types/react\": {\n      \"optional\": true\n    }\n  },\n  \"files\": [\n    \"dist\",\n    \"src\"\n  ],\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/hxf31891/react-gradient-color-picker/issues\"\n  },\n  \"homepage\": \"https://gradient-package-demo.web.app/\",\n  \"publishConfig\": {\n    \"@hxf31891:registry\": \"https://npm.pkg.github.com\"\n  }\n}"
  },
  {
    "path": "src/components/AdvancedControls.tsx",
    "content": "import React, { useState, useRef, useEffect } from 'react'\nimport { Styles, Config } from '../shared/types.js'\nimport { getHandleValue } from '../utils/utils.js'\nimport { usePicker } from '../context.js'\nimport {\n  usePaintSat,\n  usePaintLight,\n  usePaintBright,\n} from '../hooks/usePaintHue.js'\nimport tinycolor from 'tinycolor2'\n\nconst AdvBar = ({\n  value,\n  reffy,\n  label,\n  config,\n  callback,\n  squareWidth,\n  openAdvanced,\n  defaultStyles,\n  pickerIdSuffix,\n}: {\n  reffy: any\n  value: number\n  label: string\n  config: Config\n  squareWidth: number\n  openAdvanced: boolean\n  defaultStyles: Styles\n  pickerIdSuffix: string\n  callback: (arg0: number) => void\n}) => {\n  const { barSize } = config\n  const [dragging, setDragging] = useState<boolean>(false)\n  const [handleTop, setHandleTop] = useState<number>(2)\n  const left = value * (squareWidth - 18)\n\n  useEffect(() => {\n    setHandleTop(reffy?.current?.offsetTop - 2)\n  }, [openAdvanced, reffy])\n\n  const stopDragging = () => {\n    setDragging(false)\n  }\n\n  const handleMove = (e: any) => {\n    if (dragging) {\n      callback(getHandleValue(e, barSize))\n    }\n  }\n\n  const handleClick = (e: any) => {\n    if (!dragging) {\n      callback(getHandleValue(e, barSize))\n    }\n  }\n\n  const handleDown = () => {\n    setDragging(true)\n  }\n\n  useEffect(() => {\n    const handleUp = () => {\n      stopDragging()\n    }\n\n    window.addEventListener('mouseup', handleUp)\n\n    return () => {\n      window.removeEventListener('mouseup', handleUp)\n    }\n  }, [])\n\n  return (\n    <div style={{ width: '100%', padding: '3px 0px 3px 0px' }}>\n      <div\n        onMouseMove={(e) => handleMove(e)}\n        // className=\"rbgcp-advanced-bar-wrap\"\n        style={{ cursor: 'resize', position: 'relative' }}\n        id={`rbgcp-advanced-bar-${label}-wrapper${pickerIdSuffix}`}\n      >\n        <div\n          style={{ left, top: handleTop, ...defaultStyles.rbgcpHandle }}\n          id={`rbgcp-advanced-bar-${label}-handle${pickerIdSuffix}`}\n          // className=\"rbgcp-advanced-bar-handle\"\n          onMouseDown={handleDown}\n          role=\"button\"\n          tabIndex={0}\n        />\n        <div\n          style={{\n            textAlign: 'center',\n            color: '#fff',\n            fontSize: 12,\n            fontWeight: 500,\n            lineHeight: 1,\n            position: 'absolute',\n            left: '50%',\n            transform: 'translate(-50%, 0%)',\n            top: handleTop + 2,\n            zIndex: 10,\n            textShadow: '1px 1px 1px rgba(0,0,0,.6)',\n          }}\n          id={`rbgcp-advanced-bar-${label}-label${pickerIdSuffix}`}\n          // className=\"rbgcp-advanced-bar-label\"\n          onMouseMove={(e) => handleMove(e)}\n          onClick={(e) => handleClick(e)}\n          tabIndex={0}\n          role=\"button\"\n          onKeyDown={() => {\n            return\n          }}\n        >\n          {label}\n        </div>\n        <canvas\n          ref={reffy}\n          height=\"14px\"\n          width={`${squareWidth}px`}\n          onClick={(e) => handleClick(e)}\n          // className=\"rbgcp-advanced-bar-canvas\"\n          style={{ position: 'relative', borderRadius: 14 }}\n          id={`rbgcp-advanced-bar-${label}-canvas${pickerIdSuffix}`}\n        />\n      </div>\n    </div>\n  )\n}\n\nconst AdvancedControls = ({ openAdvanced }: { openAdvanced: boolean }) => {\n  const { config, tinyColor, handleChange, squareWidth, hc, defaultStyles, pickerIdSuffix } = usePicker()\n  const { s, l } = tinyColor.toHsl()\n\n  const satRef = useRef(null)\n  const lightRef = useRef(null)\n  const brightRef = useRef(null)\n  usePaintSat(satRef, hc?.h, l * 100, squareWidth)\n  usePaintLight(lightRef, hc?.h, s * 100, squareWidth)\n  usePaintBright(brightRef, hc?.h, s * 100, squareWidth)\n\n  const satDesat = (value: number) => {\n    const { r, g, b } = tinycolor({ h: hc?.h, s: value / 100, l }).toRgb()\n    handleChange(`rgba(${r},${g},${b},${hc?.a})`)\n  }\n\n  const setLight = (value: number) => {\n    const { r, g, b } = tinycolor({ h: hc?.h, s, l: value / 100 }).toRgb()\n    handleChange(`rgba(${r},${g},${b},${hc?.a})`)\n  }\n\n  const setBright = (value: number) => {\n    const { r, g, b } = tinycolor({\n      h: hc?.h,\n      s: hc?.s * 100,\n      v: value,\n    }).toRgb()\n    handleChange(`rgba(${r},${g},${b},${hc?.a})`)\n  }\n\n  return (\n    <div\n      style={{\n        width: '100%',\n        height: openAdvanced ? 98 : 0,\n        transition: 'all 120ms linear',\n      }}\n      id={`rbgcp-advanced-controls-wrapper${pickerIdSuffix}`}\n      // className=\"rbgcp-advanced-controls-wrap\"\n    >\n      <div\n        style={{\n          paddingTop: 11,\n          display: openAdvanced ? 'flex' : 'none',\n          flexDirection: 'column',\n          justifyContent: 'space-between',\n          height: openAdvanced ? 98 : 0,\n          overflow: 'hidden',\n          transition: 'height 100ms linear',\n        }}\n        id={`rbgcp-advanced-controls-inner${pickerIdSuffix}`}\n        // className=\"rbgcp-advanced-controls-inner\"\n      >\n        <AdvBar\n          value={s}\n          reffy={satRef}\n          config={config}\n          label=\"Saturation\"\n          callback={satDesat}\n          squareWidth={squareWidth}\n          openAdvanced={openAdvanced}\n          defaultStyles={defaultStyles}\n          pickerIdSuffix={pickerIdSuffix}\n        />\n        <AdvBar\n          value={l}\n          config={config}\n          reffy={lightRef}\n          label=\"Lightness\"\n          callback={setLight}\n          squareWidth={squareWidth}\n          openAdvanced={openAdvanced}\n          defaultStyles={defaultStyles}\n          pickerIdSuffix={pickerIdSuffix}\n        />\n        <AdvBar\n          value={hc?.v}\n          config={config}\n          reffy={brightRef}\n          label=\"Brightness\"\n          callback={setBright}\n          squareWidth={squareWidth}\n          openAdvanced={openAdvanced}\n          defaultStyles={defaultStyles}\n          pickerIdSuffix={pickerIdSuffix}\n        />\n      </div>\n    </div>\n  )\n}\n\nexport default AdvancedControls\n"
  },
  {
    "path": "src/components/ComparibleColors.tsx",
    "content": "import React from 'react'\nimport { usePicker } from '../context.js'\n\nconst ComparibleColors = ({\n  openComparibles,\n}: {\n  openComparibles: boolean\n}) => {\n  const { tinyColor, handleChange, defaultStyles, pickerIdSuffix } = usePicker()\n\n  const analogous = tinyColor.analogous()\n  const monochromatic = tinyColor.monochromatic()\n  const triad = tinyColor.triad()\n  const tetrad = tinyColor.tetrad()\n\n  const handleClick = (tiny: any) => {\n    const { r, g, b, a } = tiny.toRgb()\n    handleChange(`rgba(${r},${g},${b},${a})`)\n  }\n\n  return (\n    <div\n      style={{\n        width: '100%',\n        transition: 'all 120ms linear',\n        height: openComparibles ? 216 : 0,\n      }}\n      id={`rbgcp-comparible-colors-wrapper${pickerIdSuffix}`}\n      // className=\"rbgcp-comparible-colors-wrap\"\n    >\n      <div\n        style={{\n          paddingTop: 11,\n          display: openComparibles ? '' : 'none',\n          position: 'relative',\n        }}\n        id={`rbgcp-comparible-colors-inner${pickerIdSuffix}`}\n        // className=\"rbgcp-comparible-colors-inner\"\n      >\n        <div\n          style={{\n            textAlign: 'center',\n            fontSize: 13,\n            fontWeight: 600,\n            position: 'absolute',\n            top: 6.5,\n            left: 2,\n            ...defaultStyles.rbgcpComparibleLabel,\n          }}\n          id={`rbgcp-comparible-color-guide-label${pickerIdSuffix}`}\n          // className=\"rbgcp-comparible-colors-label\"\n        >\n          Color Guide\n        </div>\n        <div\n          style={{\n            textAlign: 'center',\n            fontSize: 12,\n            fontWeight: 500,\n            marginTop: 3,\n            ...defaultStyles.rbgcpComparibleLabel,\n          }}\n          id={`rbgcp-comparible-analogous-colors-label${pickerIdSuffix}`}\n          // className=\"rbgcp-comparible-colors-label\"\n        >\n          Analogous\n        </div>\n        <div\n          style={{ borderRadius: 5, overflow: 'hidden', display: 'flex' }}\n          id={`rbgcp-comparible-analogous-colors${pickerIdSuffix}`}\n          // className=\"rbgcp-comparible-colors-colors\"\n        >\n          {analogous?.map((c: any, key: number) => (\n            <div\n              key={key}\n              id={`rbgcp-comparible-analogous-color-${key}${pickerIdSuffix}`}\n              style={{ width: '20%', height: 30, background: c.toHexString() }}\n              onClick={() => handleClick(c)}\n            />\n          ))}\n        </div>\n        <div\n          style={{\n            textAlign: 'center',\n            fontSize: 12,\n            fontWeight: 500,\n            marginTop: 3,\n            ...defaultStyles.rbgcpComparibleLabel,\n          }}\n          id={`rbgcp-comparible-monochromatic-colors-label${pickerIdSuffix}`}\n          // className=\"rbgcp-comparible-colors-label\"\n        >\n          Monochromatic\n        </div>\n        <div\n          style={{\n            borderRadius: 5,\n            overflow: 'hidden',\n            display: 'flex',\n            justifyContent: 'flex-end',\n          }}\n          id={`rbgcp-comparible-monochromatic-colors${pickerIdSuffix}`}\n          // className=\"rbgcp-comparible-colors-colors\"\n        >\n          {monochromatic?.map((c: any, key: number) => (\n            <div\n              key={key}\n              id={`rbgcp-comparible-monochromatic-color-${key}${pickerIdSuffix}`}\n              style={{ width: '20%', height: 30, background: c.toHexString() }}\n              onClick={() => handleClick(c)}\n            />\n          ))}\n        </div>\n        <div\n          style={{\n            textAlign: 'center',\n            fontSize: 12,\n            fontWeight: 500,\n            marginTop: 3,\n            ...defaultStyles.rbgcpComparibleLabel,\n          }}\n          id={`rbgcp-comparible-triad-colors-label${pickerIdSuffix}`}\n          // className=\"rbgcp-comparible-colors-label\"\n        >\n          Triad\n        </div>\n        <div\n          style={{\n            borderRadius: 5,\n            overflow: 'hidden',\n            display: 'flex',\n            justifyContent: 'flex-end',\n          }}\n          id={`rbgcp-comparible-triad-colors${pickerIdSuffix}`}\n          // className=\"rbgcp-comparible-colors-colors\"\n        >\n          {triad?.map((c: any, key: number) => (\n            <div\n              key={key}\n              id={`rbgcp-comparible-triad-color-${key}${pickerIdSuffix}`}\n              style={{\n                width: 'calc(100% / 3)',\n                height: 28,\n                background: c.toHexString(),\n              }}\n              onClick={() => handleClick(c)}\n            />\n          ))}\n        </div>\n        <div\n          style={{\n            textAlign: 'center',\n            fontSize: 12,\n            fontWeight: 500,\n            marginTop: 3,\n            ...defaultStyles.rbgcpComparibleLabel,\n          }}\n          id={`rbgcp-comparible-tetrad-colors-label${pickerIdSuffix}`}\n          // className=\"rbgcp-comparible-colors-label\"\n        >\n          Tetrad\n        </div>\n        <div\n          style={{\n            borderRadius: 5,\n            overflow: 'hidden',\n            display: 'flex',\n            justifyContent: 'flex-end',\n          }}\n          id={`rbgcp-comparible-tetrad-colors${pickerIdSuffix}`}\n          // className=\"rbgcp-comparible-colors-colors\"\n        >\n          {tetrad?.map((c: any, key: number) => (\n            <div\n              key={key}\n              id={`rbgcp-comparible-tetrad-color-${key}${pickerIdSuffix}`}\n              style={{ width: '25%', height: 28, background: c.toHexString() }}\n              onClick={() => handleClick(c)}\n            />\n          ))}\n        </div>\n      </div>\n    </div>\n  )\n}\n\nexport default ComparibleColors\n"
  },
  {
    "path": "src/components/Controls.tsx",
    "content": "/* eslint-disable react/jsx-no-leaked-render */\n/* eslint-disable jsx-a11y/no-static-element-interactions */\nimport React, { useState } from 'react'\nimport { SlidersIcon, InputsIcon, PaletteIcon } from './icon.js'\nimport { usePicker } from '../context.js'\nimport EyeDropper from './EyeDropper.js'\nimport AdvancedControls from './AdvancedControls.js'\nimport ComparibleColors from './ComparibleColors.js'\nimport GradientControls from './GradientControls.js'\nimport { LocalesProps } from '../shared/types.js'\nimport { colorTypeBtnStyles, controlBtnStyles, modalBtnStyles } from '../styles/styles.js'\n\nconst ColorTypeBtns = ({\n  hideColorTypeBtns,\n  setGradient,\n  isGradient,\n  setSolid,\n  locales,\n}: {\n  hideColorTypeBtns?: boolean\n  isGradient?: boolean\n  setSolid: () => void\n  setGradient: () => void\n  locales?: LocalesProps\n}) => {\n  const { defaultStyles, pickerIdSuffix } = usePicker()\n\n  if (hideColorTypeBtns) {\n    return <div style={{ width: 1 }} />\n  } else {\n    return (\n      <div\n        style={{\n          display: 'flex',\n          alignItems: 'center',\n          ...defaultStyles.rbgcpControlBtnWrapper,\n        }}\n        id={`rbgcp-color-type-btns${pickerIdSuffix}`}\n      >\n        <div\n          onClick={setSolid}\n          id={`rbgcp-solid-btn${pickerIdSuffix}`}\n          style={colorTypeBtnStyles(!isGradient, defaultStyles)}\n          // className=\"rbgcp-control-btn rbgcp-solid-btn\"\n        >\n          {locales?.CONTROLS?.SOLID}\n        </div>\n        <div\n          onClick={setGradient}\n          id={`rbgcp-gradient-btn${pickerIdSuffix}`}\n          style={colorTypeBtnStyles(isGradient ?? false, defaultStyles)}\n          // className=\"rbgcp-control-btn rbgcp-gradient-btn\"\n        >\n          {locales?.CONTROLS?.GRADIENT}\n        </div>\n      </div>\n    )\n  }\n}\n\nconst InputTypeDropdown = ({\n  openInputType,\n  setOpenInputType,\n}: {\n  openInputType?: boolean\n  setOpenInputType: (arg0: boolean) => void\n}) => {\n  const { inputType, setInputType, defaultStyles, pickerIdSuffix } = usePicker()\n  const vTrans = openInputType\n    ? 'visibility 0ms linear'\n    : 'visibility 100ms linear 150ms'\n  const zTrans = openInputType\n    ? 'z-index 0ms linear'\n    : 'z-index 100ms linear 150ms'\n  const oTrans = openInputType\n    ? 'opacity 120ms linear'\n    : 'opacity 150ms linear 50ms'\n\n  const handleInputType = (e: any, val: string) => {\n    if (openInputType) {\n      e.stopPropagation()\n      setInputType(val)\n      setOpenInputType(false)\n    }\n  }\n\n  return (\n    <div\n      // className=\"rbgcp-color-model-dropdown\"\n      style={{\n        visibility: openInputType ? 'visible' : 'hidden',\n        zIndex: openInputType ? '' : -100,\n        opacity: openInputType ? 1 : 0,\n        transition: `${oTrans}, ${vTrans}, ${zTrans}`,\n        ...defaultStyles.rbgcpColorModelDropdown,\n      }}\n      id={`rbgcp-color-model-dropdown${pickerIdSuffix}`}\n    >\n      <div\n        id={`rbgcp-color-model-rgb-btn${pickerIdSuffix}`}\n        onClick={(e) => handleInputType(e, 'rgb')}\n        style={modalBtnStyles(inputType === 'rgb', defaultStyles)}\n      >\n        RGB\n      </div>\n      <div\n        id={`rbgcp-color-model-hsl-btn${pickerIdSuffix}`}\n        onClick={(e) => handleInputType(e, 'hsl')}\n        style={modalBtnStyles(inputType === 'hsl', defaultStyles)}\n      >\n        HSL\n      </div>\n      <div\n        id={`rbgcp-color-model-hsv-btn${pickerIdSuffix}`}\n        onClick={(e) => handleInputType(e, 'hsv')}\n        style={modalBtnStyles(inputType === 'hsv', defaultStyles)}\n      >\n        HSV\n      </div>\n      <div\n        id={`rbgcp-color-model-cmyk-btn${pickerIdSuffix}`}\n        onClick={(e) => handleInputType(e, 'cmyk')}\n        style={modalBtnStyles(inputType === 'cmyk', defaultStyles)}\n      >\n        CMYK\n      </div>\n    </div>\n  )\n}\n\nconst Controls = ({\n  locales,\n  hideEyeDrop = false,\n  hideAdvancedSliders = false,\n  hideColorGuide = false,\n  hideInputType = false,\n  hideColorTypeBtns = false,\n  hideGradientControls = false,\n  hideGradientType = false,\n  hideGradientAngle = false,\n  hideGradientStop = false,\n}: {\n  locales?: LocalesProps\n  hideEyeDrop?: boolean\n  hideAdvancedSliders?: boolean\n  hideColorGuide?: boolean\n  hideInputType?: boolean\n  hideColorTypeBtns?: boolean\n  hideGradientControls?: boolean\n  hideGradientType?: boolean\n  hideGradientAngle?: boolean\n  hideGradientStop?: boolean\n}) => {\n  const { config, onChange, isGradient, handleChange, previous, defaultStyles, pickerIdSuffix } =\n    usePicker()\n  const { defaultColor, defaultGradient } = config\n  const [openComparibles, setOpenComparibles] = useState(false)\n  const [openInputType, setOpenInputType] = useState(false)\n  const [openAdvanced, setOpenAdvanced] = useState(false)\n\n  const noTools =\n    hideEyeDrop && hideAdvancedSliders && hideColorGuide && hideInputType\n\n  const solidColor = previous?.color ?? defaultColor\n  const gradientColor = previous?.gradient ?? defaultGradient\n\n  const setSolid = () => {\n    onChange(solidColor)\n  }\n\n  const setGradient = () => {\n    onChange(gradientColor)\n  }\n\n  const allRightControlsHidden =\n    hideEyeDrop && hideAdvancedSliders && hideColorGuide && hideInputType\n  const allControlsHidden = allRightControlsHidden && hideColorTypeBtns\n\n  if (allControlsHidden) {\n    if (isGradient && !hideGradientControls) {\n      return (\n        <GradientControls\n          hideGradientType={hideGradientType}\n          hideGradientAngle={hideGradientAngle}\n          hideGradientStop={hideGradientStop}\n        />\n      )\n    } else {\n      return null\n    }\n  } else {\n    return (\n      <div style={{ paddingBottom: 4 }}>\n        <div\n          style={{\n            width: '100%',\n            display: 'flex',\n            alignItems: 'center',\n            justifyContent: 'space-between',\n          }}\n          id={`rbgcp-controls-wrapper${pickerIdSuffix}`}\n          // className=\"rbgcp-controls-wrapper\"\n        >\n          <ColorTypeBtns\n            hideColorTypeBtns={hideColorTypeBtns}\n            setGradient={setGradient}\n            isGradient={isGradient}\n            setSolid={setSolid}\n            locales={locales}\n          />\n\n          {!allRightControlsHidden && (\n            <div\n              style={{\n                display: noTools ? 'none' : '',\n                ...defaultStyles.rbgcpControlBtnWrapper,\n              }}\n              id={`rbgcp-control-rightside-wrapper${pickerIdSuffix}`}\n              // className=\"rbgcp-control-btn-wrapper\"\n            >\n              {!hideEyeDrop && <EyeDropper onSelect={handleChange} />}\n              {!hideAdvancedSliders && (\n                <div\n                  id={`rbgcp-advanced-btn${pickerIdSuffix}`}\n                  onClick={() => setOpenAdvanced(!openAdvanced)}\n                  // className=\"rbgcp-control-btn rbgcp-advanced-btn\"\n                  style={controlBtnStyles(openAdvanced, defaultStyles)}\n                >\n                  <SlidersIcon color={openAdvanced ? '#568CF5' : ''} />\n                </div>\n              )}\n              {!hideColorGuide && (\n                <div\n                  style={controlBtnStyles(openComparibles, defaultStyles)}\n                  onClick={() => setOpenComparibles(!openComparibles)}\n                  // className=\"rbgcp-control-btn rbgcp-comparibles-btn\"\n                  id={`rbgcp-comparibles-btn${pickerIdSuffix}`}\n                >\n                  <PaletteIcon color={openComparibles ? '#568CF5' : ''} />\n                </div>\n              )}\n              {!hideInputType && (\n                <div\n                  id={`rbgcp-color-model-btn${pickerIdSuffix}`}\n                  onClick={() => setOpenInputType(!openInputType)}\n                  // className=\"rbgcp-control-btn rbgcp-color-model-btn\"\n                  style={controlBtnStyles(openInputType, defaultStyles)}\n                >\n                  <InputsIcon color={openInputType ? '#568CF5' : ''} />\n                  <InputTypeDropdown\n                    openInputType={openInputType}\n                    setOpenInputType={setOpenInputType}\n                  />\n                </div>\n              )}\n            </div>\n          )}\n        </div>\n        {!hideAdvancedSliders && (\n          <AdvancedControls openAdvanced={openAdvanced} />\n        )}\n        {!hideColorGuide && (\n          <ComparibleColors openComparibles={openComparibles} />\n        )}\n        {isGradient && !hideGradientControls && (\n          <GradientControls\n            hideGradientType={hideGradientType}\n            hideGradientAngle={hideGradientAngle}\n            hideGradientStop={hideGradientStop}\n          />\n        )}\n      </div>\n    )\n  }\n}\n\nexport default Controls;\n"
  },
  {
    "path": "src/components/EyeDropper.tsx",
    "content": "/* eslint-disable react/jsx-no-leaked-render */\n/* eslint-disable jsx-a11y/no-static-element-interactions */\nimport React, { useState } from 'react'\nimport Portal from './Portal.js'\nimport html2canvas from 'html2canvas'\nimport { controlBtnStyles } from '../styles/styles.js'\nimport tc from 'tinycolor2'\nimport { usePicker } from '../context.js'\n\nconst DropperIcon = ({ color }: { color: string }) => {\n  const { defaultStyles } = usePicker()\n  const col = color ?? ''\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 20 20\"\n      style={{ width: 16 }}\n    >\n      <path\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{\n          fill: 'none',\n          strokeWidth: '1.4px',\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col }),\n        }}\n        d=\"M15.6,7h0L7.78,14.86c-.37.37-1.61.38-2,.75s-.5,1.53-.76,2a3.53,3.53,0,0,1-.52.52,1.6,1.6,0,0,1-2.27-.06l-.32-.32a1.61,1.61,0,0,1-.06-2.27A3.25,3.25,0,0,1,2.4,15c.47-.26,1.65-.35,2-.73s.34-1.64.71-2c1.68-1.73,5.61-5.65,7.91-7.93h0l1.14,1.38L15.6,7Z\"\n      />\n      <polygon\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{\n          strokeWidth: '1.4px',\n          ...defaultStyles.rbgcpControlIcon2,\n          ...(col && { stroke: col, fill: col }),\n        }}\n        points=\"15.7 8.87 11.13 4.29 12.69 2.73 17.25 7.31 15.7 8.87\"\n      />\n      <path\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{\n          strokeWidth: '1.4px',\n          ...defaultStyles.rbgcpControlIcon2,\n          ...(col && { stroke: col, fill: col }),\n        }}\n        d=\"M18.18,3.71,16.36,5.53a1.33,1.33,0,0,1-1.88,0h0a1.34,1.34,0,0,1,0-1.89l1.81-1.82a1.34,1.34,0,0,1,1.89,0h0A1.34,1.34,0,0,1,18.18,3.71Z\"\n      />\n    </svg>\n  )\n}\n\nconst Dropper = ({ onSelect }: { onSelect: (arg0: string) => void }) => {\n  const { defaultStyles } = usePicker()\n  const [pickerCanvas, setPickerCanvas] =\n    useState<CanvasRenderingContext2D | null>(null)\n  const [coverUp, setCoverUp] = useState(false)\n  const [isPicking, setIsPicking] = useState(false)\n\n  const takePick = () => {\n    const root = document.getElementById('root')\n    setCoverUp(true)\n\n    // @ts-expect-error some error with this imported packages types\n    html2canvas(root).then((canvas: any) => {\n      const blankCanvas = document.createElement('canvas')\n      const ctx = blankCanvas.getContext('2d', { willReadFrequently: true })\n\n      if (root && ctx) {\n        blankCanvas.width = root.offsetWidth * 2\n        blankCanvas.height = root.offsetHeight * 2\n        ctx.drawImage(canvas, 0, 0)\n      }\n\n      setPickerCanvas(ctx)\n    })\n  }\n\n  const getColorLegacy = (e: any) => {\n    e.stopPropagation()\n    if (pickerCanvas) {\n      const { pageX, pageY } = e\n      const x1 = pageX * 2\n      const y1 = pageY * 2\n      const rgb = pickerCanvas.getImageData(x1, y1, 1, 1).data\n      onSelect(`rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, 1)`)\n    }\n    setIsPicking(false)\n    setCoverUp(false)\n  }\n\n  const getEyeDrop = () => {\n    setIsPicking(true)\n    // @ts-expect-error - ts does not evaluate for window.EyeDropper\n    if (!window.EyeDropper) {\n      takePick()\n    } else {\n      // @ts-expect-error - ts does not evaluate for window.EyeDropper\n      const eyeDropper = new window.EyeDropper()\n      const abortController = new window.AbortController()\n\n      eyeDropper\n        .open({ signal: abortController.signal })\n        .then((result: any) => {\n          const tinyHex = tc(result.sRGBHex)\n          const { r, g, b } = tinyHex.toRgb()\n          onSelect(`rgba(${r}, ${g}, ${b}, 1)`)\n          setIsPicking(false)\n        })\n        .catch((e: any) => {\n          console.log(e)\n          setIsPicking(false)\n        })\n    }\n  }\n\n  return (\n    <div>\n      <div\n        onClick={getEyeDrop}\n        id=\"rbgcp-eyedropper-btn\"\n        style={{\n          ...defaultStyles.rbgcpEyedropperBtn,\n          ...controlBtnStyles(coverUp, defaultStyles),\n        }}\n      >\n        <DropperIcon color={isPicking ? 'rgb(86, 140, 245)' : ''} />\n      </div>\n\n      {coverUp && (\n        <Portal>\n          <div\n            onClick={(e) => getColorLegacy(e)}\n            style={defaultStyles.rbgcpEyedropperCover}\n          />\n        </Portal>\n      )}\n    </div>\n  )\n}\n\nexport default Dropper\n"
  },
  {
    "path": "src/components/GradientBar.tsx",
    "content": "/* eslint-disable react/no-array-index-key */\n/* eslint-disable react/jsx-no-leaked-render */\n/* eslint-disable jsx-a11y/no-static-element-interactions */\nimport React, { useState, useEffect } from 'react'\nimport { getHandleValue } from '../utils/utils.js'\nimport { usePicker } from '../context.js'\nimport { low, high } from '../utils/formatters.js'\nimport { GradientProps } from '../shared/types.js'\n\nexport const Handle = ({\n  left,\n  i,\n  setDragging,\n}: {\n  left?: number\n  i: number\n  setDragging: (arg0: boolean) => void\n}) => {\n  const {\n    colors,\n    squareWidth,\n    selectedColor,\n    defaultStyles,\n    pickerIdSuffix,\n    createGradientStr,\n  } = usePicker()\n  const isSelected = selectedColor === i\n  const leftMultiplyer = (squareWidth - 18) / 100\n\n  const setSelectedColor = (index: number) => {\n    const newGradStr = colors?.map((cc: GradientProps, i: number) => ({\n      ...cc,\n      value: i === index ? high(cc) : low(cc),\n    }))\n    createGradientStr(newGradStr)\n  }\n\n  const handleDown = (e: any) => {\n    e.stopPropagation()\n    setSelectedColor(i)\n    setDragging(true)\n  }\n\n  // const handleFocus = () => {\n  //   setInFocus('gpoint')\n  //   setSelectedColor(i)\n  // }\n\n  // const handleBlur = () => {\n  //   setInFocus(null)\n  // }\n\n  return (\n    <div\n      // tabIndex={0}\n      // onBlur={handleBlur}\n      // onFocus={handleFocus}\n      onMouseDown={(e) => handleDown(e)}\n      id={`rbgcp-gradient-handle-${i}${pickerIdSuffix}`}\n      // className=\"rbgcp-gradient-handle-wrap\"\n      style={{\n        ...defaultStyles.rbgcpGradientHandleWrap,\n        left: (left ?? 0) * leftMultiplyer,\n      }}\n    >\n      <div\n        // className=\"rbgcp-gradient-handle\"\n        style={{\n          ...defaultStyles.rbgcpGradientHandle,\n          ...(isSelected\n            ? {\n                boxShadow: '0px 0px 5px 1px rgba(86, 140, 245,.95)',\n                border: '2px solid white',\n              }\n            : {}),\n        }}\n        id={`rbgcp-gradient-handle-${i}-dot${pickerIdSuffix}`}\n      >\n        {isSelected && (\n          <div\n            style={{\n              width: 5,\n              height: 5,\n              borderRadius: '50%',\n              background: 'white',\n            }}\n            id={`rbgcp-gradient-handle-${i}-selected-dot${pickerIdSuffix}`}\n          />\n        )}\n      </div>\n    </div>\n  )\n}\n\nconst GradientBar = () => {\n  const {\n    value,\n    colors,\n    config,\n    squareWidth,\n    currentColor,\n    handleGradient,\n    pickerIdSuffix,\n    createGradientStr,\n  } = usePicker()\n  const { barSize } = config\n  const [dragging, setDragging] = useState(false)\n  // const [inFocus, setInFocus] = useState<string | null>(null)\n\n  function force90degLinear(color: string) {\n    return color.replace(\n      /(radial|linear)-gradient\\([^,]+,/,\n      'linear-gradient(90deg,'\n    )\n  }\n\n  const addPoint = (e: any) => {\n    const left = getHandleValue(e, barSize)\n    const newColors = [\n      ...colors.map((c: any) => ({ ...c, value: low(c) })),\n      { value: currentColor, left: left },\n    ]?.sort((a, b) => a.left - b.left)\n    createGradientStr(newColors)\n  }\n\n  // useEffect(() => {\n  //   const selectedEl = window?.document?.getElementById(\n  //     `gradient-handle-${selectedColor}`\n  //   )\n  //   if (selectedEl) selectedEl.focus()\n  // }, [selectedColor])\n\n  const stopDragging = () => {\n    setDragging(false)\n  }\n\n  const handleDown = (e: any) => {\n    if (dragging) return;\n    addPoint(e)\n    setDragging(true)\n  }\n\n  const handleMove = (e: any) => {\n    if (dragging) handleGradient(currentColor, getHandleValue(e, barSize))\n  }\n\n  // const handleKeyboard = (e: any) => {\n  //   if (isGradient) {\n  //     if (e.keyCode === 8) {\n  //       if (inFocus === 'gpoint') {\n  //         deletePoint()\n  //       }\n  //     }\n  //   }\n  // }\n\n  const handleUp = () => {\n    stopDragging()\n  }\n\n  useEffect(() => {\n    window.addEventListener('mouseup', handleUp)\n    // window?.addEventListener('keydown', handleKeyboard)\n\n    return () => {\n      window.removeEventListener('mouseup', handleUp)\n      // window?.removeEventListener('keydown', handleKeyboard)\n    }\n  })\n\n  return (\n    <div\n      style={{\n        width: '100%',\n        marginTop: 17,\n        marginBottom: 4,\n        position: 'relative',\n      }}\n      id={`rbgcp-gradient-bar${pickerIdSuffix}`}\n      // className=\"rbgcp-gradient-bar\"\n    >\n      <div\n        style={{\n          height: 14,\n          borderRadius: 10,\n          width: squareWidth,\n          backgroundImage: force90degLinear(value),\n        }}\n        onMouseDown={(e) => handleDown(e)}\n        onMouseMove={(e) => handleMove(e)}\n        id={`rbgcp-gradient-bar-canvas${pickerIdSuffix}`}\n        // className=\"rbgcp-gradient-bar-canvas\"\n      />\n      {colors?.map((c: any, i) => (\n        <Handle\n          i={i}\n          left={c.left}\n          key={`${i}-${c}`}\n          setDragging={setDragging}\n        />\n      ))}\n    </div>\n  )\n}\n\nexport default GradientBar\n"
  },
  {
    "path": "src/components/GradientControls.tsx",
    "content": "import React from 'react'\nimport { usePicker } from '../context.js'\nimport { formatInputValues, low, high } from '../utils/formatters.js'\nimport { controlBtnStyles } from '../styles/styles.js'\nimport TrashIcon, {\n  LinearIcon,\n  RadialIcon,\n  DegreesIcon,\n  StopIcon,\n} from './icon.js'\n\nconst GradientType = () => {\n  const { gradientType, onChange, value, defaultStyles, pickerIdSuffix } =\n    usePicker()\n  const isLinear = gradientType === 'linear-gradient'\n  const isRadial = gradientType === 'radial-gradient'\n\n  const handleLinear = () => {\n    const remaining = value.split(/,(.+)/)[1]\n    onChange(`linear-gradient(90deg, ${remaining}`)\n  }\n\n  const handleRadial = () => {\n    const remaining = value.split(/,(.+)/)[1]\n    onChange(`radial-gradient(circle, ${remaining}`)\n  }\n\n  return (\n    <div style={defaultStyles.rbgcpControlBtnWrapper}>\n      <div\n        onClick={handleLinear}\n        id={`rbgcp-linear-btn${pickerIdSuffix}`}\n        // className=\"rbgcp-control-icon-btn rbgcp-linear-btn\"\n        style={{\n          ...defaultStyles.rbgcpControlBtn,\n          ...(isLinear && defaultStyles.rbgcpControlBtnSelected),\n        }}\n        tabIndex={0}\n        role=\"button\"\n        onKeyDown={() => {\n          return\n        }}\n      >\n        <LinearIcon color={isLinear ? '#568CF5' : ''} />\n      </div>\n      <div\n        onClick={handleRadial}\n        id={`rbgcp-radial-btn${pickerIdSuffix}`}\n        // className=\"rbgcp-control-icon-btn rbgcp-radial-btn\"\n        style={{\n          ...defaultStyles.rbgcpControlBtn,\n          ...(isRadial && defaultStyles.rbgcpControlBtnSelected),\n        }}\n        tabIndex={0}\n        role=\"button\"\n        onKeyDown={() => {\n          return\n        }}\n      >\n        <RadialIcon color={isRadial ? '#568CF5' : ''} />\n      </div>\n    </div>\n  )\n}\n\nconst StopPicker = () => {\n  const {\n    currentLeft,\n    currentColor,\n    defaultStyles,\n    handleGradient,\n    pickerIdSuffix,\n  } = usePicker()\n\n  const handleMove = (newVal: string) => {\n    handleGradient(currentColor, formatInputValues(parseInt(newVal), 0, 100))\n  }\n\n  return (\n    <div\n      // className=\"rbgcp-stop-input-wrap\"\n      style={{\n        ...defaultStyles.rbgcpControlBtnWrapper,\n        ...defaultStyles.rbgcpControlInputWrap,\n        ...defaultStyles.rbgcpStopInputWrap,\n        paddingLeft: 8,\n      }}\n      id={`rbgcp-stop-input-wrapper${pickerIdSuffix}`}\n    >\n      <StopIcon />\n      <input\n        value={currentLeft}\n        id={`rbgcp-stop-input${pickerIdSuffix}`}\n        onChange={(e) => handleMove(e.target.value)}\n        style={{\n          ...defaultStyles.rbgcpControlInput,\n          ...defaultStyles.rbgcpStopInput,\n        }}\n        // className=\"rbgcp-control-input rbgcp-stop-input\"\n      />\n    </div>\n  )\n}\n\nconst DegreePicker = () => {\n  const { degrees, onChange, value, defaultStyles, pickerIdSuffix } =\n    usePicker()\n\n  const handleDegrees = (e: any) => {\n    const newValue = formatInputValues(e.target.value, 0, 360)\n    const remaining = value.split(/,(.+)/)[1]\n    onChange(`linear-gradient(${newValue ?? 0}deg, ${remaining}`)\n  }\n\n  return (\n    <div\n      // className=\"rbgcp-degree-input-wrap\"\n      style={{\n        ...defaultStyles.rbgcpControlBtnWrapper,\n        ...defaultStyles.rbgcpControlInputWrap,\n        ...defaultStyles.rbgcpDegreeInputWrap,\n      }}\n      id={`rbgcp-degree-input-wrapper${pickerIdSuffix}`}\n    >\n      <DegreesIcon />\n      <input\n        value={degrees}\n        onChange={(e) => handleDegrees(e)}\n        id={`rbgcp-degree-input${pickerIdSuffix}`}\n        // className=\"rbgcp-control-input rbgcp-degree-input\"\n        style={{\n          ...defaultStyles.rbgcpControlInput,\n          ...defaultStyles.rbgcpDegreeInput,\n        }}\n      />\n      <div\n        // className=\"rbgcp-degree-circle-icon\"\n        style={{\n          ...defaultStyles.rbgcpDegreeIcon,\n          position: 'absolute',\n          right: degrees > 99 ? 0 : degrees < 10 ? 7 : 3,\n          top: 1,\n          fontWeight: 400,\n          fontSize: 13,\n        }}\n      >\n        °\n      </div>\n    </div>\n  )\n}\n\nconst DeleteBtn = () => {\n  const { colors, selectedColor, createGradientStr, defaultStyles, pickerIdSuffix } =\n    usePicker()\n\n  const deletePoint = () => {\n    if (colors?.length > 2) {\n      const formatted = colors?.map((fc: any, i: number) => ({\n        ...fc,\n        value: i === selectedColor - 1 ? high(fc) : low(fc),\n      }))\n      const remaining = formatted?.filter(\n        (_: any, i: number) => i !== selectedColor\n      )\n      createGradientStr(remaining)\n    }\n  }\n\n  return (\n    <div\n      onClick={deletePoint}\n      style={{ ...controlBtnStyles(false, defaultStyles), width: 28 }}\n      id={`rbgcp-point-delete-btn${pickerIdSuffix}`}\n      // className=\"rbgcp-control-btn rbgcp-point-delete-btn\"\n      tabIndex={0}\n      role=\"button\"\n      onKeyDown={() => {\n        return\n      }}\n    >\n      <TrashIcon />\n    </div>\n  )\n}\n\nconst GradientControls = ({\n  hideGradientType,\n  hideGradientAngle,\n  hideGradientStop,\n}: {\n  hideGradientType?: boolean\n  hideGradientAngle?: boolean\n  hideGradientStop?: boolean\n}) => {\n  const { gradientType, defaultStyles, pickerIdSuffix } = usePicker()\n  return (\n    <div\n      style={{\n        ...defaultStyles.rbgcpControlBtnWrapper,\n        marginTop: 12,\n        marginBottom: -4,\n        justifyContent: 'space-between',\n        paddingLeft: hideGradientType ? 4 : 0,\n      }}\n      id={`rbgcp-gradient-controls-wrap${pickerIdSuffix}`}\n      // className=\"rbgcp-gradient-controls-wrap\"\n    >\n      {!hideGradientType && <GradientType />}\n      <div style={{ width: 53 }}>\n        {!hideGradientAngle && gradientType === 'linear-gradient' && (\n          <DegreePicker />\n        )}\n      </div>\n      {!hideGradientStop && <StopPicker />}\n      <DeleteBtn />\n    </div>\n  )\n}\n\nexport default GradientControls\n"
  },
  {
    "path": "src/components/Hue.tsx",
    "content": "import React, { useRef, useState, useEffect } from 'react'\nimport { usePicker } from '../context.js'\nimport usePaintHue from '../hooks/usePaintHue.js'\nimport { getHandleValue } from '../utils/utils.js'\nimport tinycolor from 'tinycolor2'\n\nconst Hue = () => {\n  const barRef = useRef<HTMLCanvasElement>(null)\n  const { config, handleChange, squareWidth, hc, setHc, pickerIdSuffix } = usePicker()\n  const [dragging, setDragging] = useState(false)\n  const { barSize } = config\n  usePaintHue(barRef, squareWidth)\n\n  const stopDragging = () => {\n    setDragging(false)\n  }\n\n  const handleDown = () => {\n    setDragging(true)\n  }\n\n  const handleHue = (e: any) => {\n    const newHue = getHandleValue(e, barSize) * 3.6\n    const tinyHsv = tinycolor({ h: newHue, s: hc?.s, v: hc?.v })\n    const { r, g, b } = tinyHsv.toRgb()\n    handleChange(`rgba(${r}, ${g}, ${b}, ${hc.a})`)\n    setHc({ ...hc, h: newHue })\n  }\n\n  const handleMove = (e: any) => {\n    if (dragging) {\n      handleHue(e)\n    }\n  }\n\n  const handleClick = (e: any) => {\n    if (!dragging) {\n      handleHue(e)\n    }\n  }\n\n  useEffect(() => {\n    const handleUp = () => {\n      stopDragging()\n    }\n\n    window.addEventListener('mouseup', handleUp)\n\n    return () => {\n      window.removeEventListener('mouseup', handleUp)\n    }\n  }, [])\n\n  return (\n    <div\n      style={{\n        height: 14,\n        marginTop: 17,\n        marginBottom: 4,\n        cursor: 'ew-resize',\n        position: 'relative',\n      }}\n      onMouseMove={(e) => handleMove(e)}\n      id={`rbgcp-hue-wrap${pickerIdSuffix}`}\n      // className=\"rbgcp-hue-wrap\"\n    >\n      <div\n        tabIndex={0}\n        role=\"button\"\n        // className=\"rbgcp-handle rbgcp-handle-hue\"\n        style={{\n          border: '2px solid white',\n          borderRadius: '50%',\n          boxShadow: '0px 0px 3px rgba(0, 0, 0, 0.5)',\n          width: '18px',\n          height: '18px',\n          zIndex: 1000,\n          transition: 'all 10ms linear',\n          position: 'absolute',\n          left: hc?.h * ((squareWidth - 18) / 360),\n          top: -2,\n          cursor: 'ew-resize',\n          boxSizing: 'border-box',\n        }}\n        onMouseDown={handleDown}\n        id={`rbgcp-hue-handle${pickerIdSuffix}`}\n      />\n      <canvas\n        ref={barRef}\n        height=\"14px\"\n        // className=\"rbgcp-hue-bar\"\n        width={`${squareWidth}px`}\n        onClick={(e) => handleClick(e)}\n        id={`rbgcp-hue-bar${pickerIdSuffix}`}\n        style={{\n          borderRadius: 14,\n          position: 'relative',\n          verticalAlign: 'top',\n        }}\n      />\n    </div>\n  )\n}\n\nexport default Hue\n"
  },
  {
    "path": "src/components/Inputs.tsx",
    "content": "import React, { useState, useEffect } from 'react'\nimport { Styles } from '../shared/types.js'\nimport { formatInputValues, round } from '../utils/formatters.js'\nimport { rgb2cmyk, cmykToRgb, getHexAlpha } from '../utils/converters.js'\nimport { usePicker } from '../context.js'\nimport tc from 'tinycolor2'\n\nconst Input = ({\n  label,\n  value,\n  callback,\n  max = 100,\n  hideOpacity,\n  defaultStyles,\n  pickerIdSuffix,\n}: {\n  max?: number\n  label: string\n  value: number\n  hideOpacity: boolean\n  defaultStyles: Styles\n  pickerIdSuffix: string\n  callback: (arg0: number) => void\n}) => {\n  const [temp, setTemp] = useState(value)\n  const width = hideOpacity ? '25%' : '20%'\n\n  useEffect(() => {\n    setTemp(value)\n  }, [value])\n\n  const onChange = (e: any) => {\n    const newVal = formatInputValues(parseFloat(e.target.value), 0, max)\n    setTemp(newVal)\n    callback(newVal)\n  }\n\n  return (\n    <div\n      style={{ width: width, flexShrink: 1 }}\n      id={`rbgcp-${label}-input-wrapper${pickerIdSuffix}`}\n    >\n      <input\n        value={temp}\n        onChange={(e) => onChange(e)}\n        style={{ ...defaultStyles.rbgcpInput }}\n        id={`rbgcp-${label}-input${pickerIdSuffix}`}\n        // className=\"rbgcp-input\"\n      />\n      <div style={{ ...defaultStyles.rbgcpInputLabel }}>{label}</div>\n    </div>\n  )\n}\n\nconst HexInput = ({\n  opacity,\n  tinyColor,\n  showHexAlpha,\n  handleChange,\n  defaultStyles,\n  pickerIdSuffix,\n}: {\n  tinyColor: any\n  opacity: number\n  showHexAlpha: boolean\n  defaultStyles: Styles\n  pickerIdSuffix: string\n  handleChange: (arg0: string) => void\n}) => {\n  const [disable, setDisable] = useState('')\n  const hex = tinyColor.toHex()\n  const [newHex, setNewHex] = useState(hex)\n\n  useEffect(() => {\n    if (disable !== 'hex') {\n      setNewHex(hex)\n    }\n  }, [tinyColor, disable, hex])\n\n  const hexFocus = () => {\n    setDisable('hex')\n  }\n\n  const hexBlur = () => {\n    setDisable('')\n  }\n\n  const handleHex = (e: any) => {\n    const tinyHex = tc(e.target.value)\n    setNewHex(e.target.value)\n    if (tinyHex.isValid()) {\n      const { r, g, b } = tinyHex.toRgb()\n      const newColor = `rgba(${r}, ${g}, ${b}, ${opacity})`\n      handleChange(newColor)\n    }\n  }\n\n  const displayValue = showHexAlpha ? `${newHex}${getHexAlpha(opacity)}` : newHex\n  const label = showHexAlpha ? 'HEXA' : 'HEX'\n  const width = showHexAlpha ? 88 : 76\n\n  return (\n    <div\n      style={{ width, flexShrink: 0 }}\n      id={`rbgcp-hex-input-wrapper${pickerIdSuffix}`}\n    >\n      <input\n        onBlur={hexBlur}\n        onFocus={hexFocus}\n        onChange={(e) => handleHex(e)}\n        value={displayValue?.toUpperCase()}\n        id={`rbgcp-hex-input${pickerIdSuffix}`}\n        style={{ ...defaultStyles.rbgcpInput, ...defaultStyles.rbgcpHexInput }}\n      />\n      <div style={{ ...defaultStyles.rbgcpInputLabel }}>{label}</div>\n    </div>\n  )\n}\n\nconst RGBInputs = ({\n  hc,\n  hideOpacity,\n  handleChange,\n  defaultStyles,\n  pickerIdSuffix,\n}: {\n  hc: any\n  hideOpacity: boolean\n  defaultStyles: Styles\n  pickerIdSuffix: string\n  handleChange: (arg0: string) => void\n}) => {\n  const handleRgb = ({ r, g, b }: { r: number; g: number; b: number }) => {\n    handleChange(`rgba(${r}, ${g}, ${b}, ${hc?.a})`)\n  }\n\n  return (\n    <>\n      <Input\n        label=\"R\"\n        max={255}\n        value={hc?.r}\n        hideOpacity={hideOpacity}\n        defaultStyles={defaultStyles}\n        pickerIdSuffix={pickerIdSuffix}\n        callback={(newVal) => handleRgb({ r: newVal, g: hc?.g, b: hc?.b })}\n      />\n      <Input\n        label=\"G\"\n        max={255}\n        value={hc?.g}\n        hideOpacity={hideOpacity}\n        defaultStyles={defaultStyles}\n        pickerIdSuffix={pickerIdSuffix}\n        callback={(newVal) => handleRgb({ r: hc?.r, g: newVal, b: hc?.b })}\n      />\n      <Input\n        label=\"B\"\n        max={255}\n        value={hc?.b}\n        hideOpacity={hideOpacity}\n        defaultStyles={defaultStyles}\n        pickerIdSuffix={pickerIdSuffix}\n        callback={(newVal) => handleRgb({ r: hc?.r, g: hc?.g, b: newVal })}\n      />\n    </>\n  )\n}\n\nconst HSLInputs = ({\n  hc,\n  setHc,\n  tinyColor,\n  hideOpacity,\n  handleChange,\n  defaultStyles,\n  pickerIdSuffix,\n}: {\n  hc: any\n  tinyColor: any\n  hideOpacity: boolean\n  defaultStyles: Styles\n  pickerIdSuffix: string\n  setHc: (arg0: any) => void\n  handleChange: (arg0: string) => void\n}) => {\n  const { s, l } = tinyColor.toHsl()\n\n  const handleH = (h: number, s: number, l: number) => {\n    const { r, g, b } = tc({ h: h, s: s, l: l }).toRgb()\n    handleChange(`rgba(${r}, ${g}, ${b}, ${hc?.a})`)\n    setHc({ ...hc, h })\n  }\n\n  const handleSl = (value: any) => {\n    const { r, g, b } = tc(value).toRgb()\n    handleChange(`rgba(${r}, ${g}, ${b}, ${hc?.a})`)\n  }\n\n  return (\n    <>\n      <Input\n        label=\"H\"\n        max={360}\n        value={round(hc?.h)}\n        hideOpacity={hideOpacity}\n        defaultStyles={defaultStyles}\n        pickerIdSuffix={pickerIdSuffix}\n        callback={(newVal) => handleH(newVal, s, l)}\n      />\n      <Input\n        label=\"S\"\n        value={round(s * 100)}\n        hideOpacity={hideOpacity}\n        defaultStyles={defaultStyles}\n        pickerIdSuffix={pickerIdSuffix}\n        callback={(newVal) => handleSl({ h: hc?.h, s: newVal, l: l })}\n      />\n      <Input\n        label=\"L\"\n        value={round(l * 100)}\n        hideOpacity={hideOpacity}\n        defaultStyles={defaultStyles}\n        pickerIdSuffix={pickerIdSuffix}\n        callback={(newVal) => handleSl({ h: hc?.h, s: s, l: newVal })}\n      />\n    </>\n  )\n}\n\nconst HSVInputs = ({\n  hc,\n  setHc,\n  hideOpacity,\n  handleChange,\n  defaultStyles,\n  pickerIdSuffix,\n}: {\n  hc: any\n  hideOpacity: boolean\n  defaultStyles: Styles\n  pickerIdSuffix: string\n  setHc: (arg0: any) => void\n  handleChange: (arg0: string) => void\n}) => {\n  const handleH = (h: number, s: number, v: number) => {\n    const { r, g, b } = tc({ h: h, s: s, v: v }).toRgb()\n    handleChange(`rgba(${r}, ${g}, ${b}, ${hc?.a})`)\n    setHc({ ...hc, h })\n  }\n\n  const handleSV = (value: any) => {\n    const { r, g, b } = tc(value).toRgb()\n    handleChange(`rgba(${r}, ${g}, ${b}, ${hc?.a})`)\n  }\n\n  return (\n    <>\n      <Input\n        label=\"H\"\n        max={360}\n        value={round(hc?.h)}\n        hideOpacity={hideOpacity}\n        defaultStyles={defaultStyles}\n        pickerIdSuffix={pickerIdSuffix}\n        callback={(newVal) => handleH(newVal, hc?.s, hc?.v)}\n      />\n      <Input\n        label=\"S\"\n        hideOpacity={hideOpacity}\n        value={round(hc?.s * 100)}\n        defaultStyles={defaultStyles}\n        pickerIdSuffix={pickerIdSuffix}\n        callback={(newVal) => handleSV({ h: hc?.h, s: newVal, v: hc?.v })}\n      />\n      <Input\n        label=\"V\"\n        hideOpacity={hideOpacity}\n        value={round(hc?.v * 100)}\n        defaultStyles={defaultStyles}\n        pickerIdSuffix={pickerIdSuffix}\n        callback={(newVal) => handleSV({ h: hc?.h, s: hc?.s, v: newVal })}\n      />\n    </>\n  )\n}\n\nconst CMKYInputs = ({\n  hc,\n  hideOpacity,\n  handleChange,\n  defaultStyles,\n\n  pickerIdSuffix,\n}: {\n  hc: any\n  hideOpacity: boolean\n  defaultStyles: Styles\n  pickerIdSuffix: string\n  handleChange: (arg0: string) => void\n}) => {\n  const { c, m, y, k } = rgb2cmyk(hc?.r, hc?.g, hc?.b)\n\n  const handleCmyk = (value: any) => {\n    const { r, g, b } = cmykToRgb(value)\n    handleChange(`rgba(${r}, ${g}, ${b}, ${hc?.a})`)\n  }\n\n  return (\n    <>\n      <Input\n        label=\"C\"\n        value={round(c * 100)}\n        hideOpacity={hideOpacity}\n        defaultStyles={defaultStyles}\n        pickerIdSuffix={pickerIdSuffix}\n        callback={(newVal) => handleCmyk({ c: newVal / 100, m: m, y: y, k: k })}\n      />\n      <Input\n        label=\"M\"\n        value={round(m * 100)}\n        hideOpacity={hideOpacity}\n        defaultStyles={defaultStyles}\n        pickerIdSuffix={pickerIdSuffix}\n        callback={(newVal) => handleCmyk({ c: c, m: newVal / 100, y: y, k: k })}\n      />\n      <Input\n        label=\"Y\"\n        value={round(y * 100)}\n        hideOpacity={hideOpacity}\n        defaultStyles={defaultStyles}\n        pickerIdSuffix={pickerIdSuffix}\n        callback={(newVal) => handleCmyk({ c: c, m: m, y: newVal / 100, k: k })}\n      />\n      <Input\n        label=\"K\"\n        value={round(k * 100)}\n        hideOpacity={hideOpacity}\n        defaultStyles={defaultStyles}\n        pickerIdSuffix={pickerIdSuffix}\n        callback={(newVal) => handleCmyk({ c: c, m: m, y: y, k: newVal / 100 })}\n      />\n    </>\n  )\n}\n\nconst Inputs = () => {\n  const {\n    hc,\n    setHc,\n    inputType,\n    tinyColor,\n    hideOpacity,\n    showHexAlpha,\n    handleChange,\n    defaultStyles,\n    pickerIdSuffix,\n  } = usePicker()\n\n  return (\n    <div\n      style={{\n        columnGap: 6,\n        paddingTop: 14,\n        display: 'flex',\n        justifyContent: 'space-between',\n        ...defaultStyles.rbgcpInputsWrap,\n      }}\n      id={`rbgcp-inputs-wrap${pickerIdSuffix}`}\n    >\n      {inputType !== 'cmyk' && (\n        <HexInput\n          opacity={hc?.a}\n          tinyColor={tinyColor}\n          showHexAlpha={showHexAlpha}\n          handleChange={handleChange}\n          defaultStyles={defaultStyles}\n          pickerIdSuffix={pickerIdSuffix}\n        />\n      )}\n      {inputType === 'hsl' && (\n        <HSLInputs\n          hc={hc}\n          setHc={setHc}\n          tinyColor={tinyColor}\n          hideOpacity={hideOpacity}\n          handleChange={handleChange}\n          defaultStyles={defaultStyles}\n          pickerIdSuffix={pickerIdSuffix}\n        />\n      )}\n      {inputType === 'rgb' && (\n        <RGBInputs\n          hc={hc}\n          hideOpacity={hideOpacity}\n          handleChange={handleChange}\n          defaultStyles={defaultStyles}\n          pickerIdSuffix={pickerIdSuffix}\n        />\n      )}\n      {inputType === 'hsv' && (\n        <HSVInputs\n          hc={hc}\n          setHc={setHc}\n          hideOpacity={hideOpacity}\n          handleChange={handleChange}\n          defaultStyles={defaultStyles}\n          pickerIdSuffix={pickerIdSuffix}\n        />\n      )}\n      {inputType === 'cmyk' && (\n        <CMKYInputs\n          hc={hc}\n          hideOpacity={hideOpacity}\n          handleChange={handleChange}\n          defaultStyles={defaultStyles}\n          pickerIdSuffix={pickerIdSuffix}\n        />\n      )}\n\n      {!hideOpacity && (\n        <Input\n          label=\"A\"\n          hideOpacity={hideOpacity}\n          defaultStyles={defaultStyles}\n          value={Math.round(hc?.a * 100)}\n          pickerIdSuffix={pickerIdSuffix}\n          callback={(newVal: number) =>\n            handleChange(`rgba(${hc?.r}, ${hc?.g}, ${hc?.b}, ${newVal / 100})`)\n          }\n        />\n      )}\n    </div>\n  )\n}\n\nexport default Inputs\n"
  },
  {
    "path": "src/components/Opacity.tsx",
    "content": "/* eslint-disable jsx-a11y/no-static-element-interactions */\nimport React, { useState, useEffect } from 'react'\nimport { usePicker } from '../context.js'\nimport { getHandleValue } from '../utils/utils.js'\n\nconst Opacity = () => {\n  const {\n    config,\n    hc = {},\n    squareWidth,\n    handleChange,\n    defaultStyles,\n    pickerIdSuffix,\n  } = usePicker()\n  const [dragging, setDragging] = useState(false)\n  const { r, g, b } = hc\n  const bg = `linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(${r},${g},${b},.5) 100%)`\n  const { barSize } = config\n\n  const stopDragging = () => {\n    setDragging(false)\n  }\n\n  const handleDown = () => {\n    setDragging(true)\n  }\n\n  const handleOpacity = (e: any) => {\n    const newO = getHandleValue(e, barSize) / 100\n    const newColor = `rgba(${r}, ${g}, ${b}, ${newO})`\n    handleChange(newColor)\n  }\n\n  const handleMove = (e: any) => {\n    if (dragging) {\n      handleOpacity(e)\n    }\n  }\n\n  const handleClick = (e: any) => {\n    if (!dragging) {\n      handleOpacity(e)\n    }\n  }\n\n  const left = squareWidth - 18\n\n  useEffect(() => {\n    const handleUp = () => {\n      stopDragging()\n    }\n\n    window.addEventListener('mouseup', handleUp)\n\n    return () => {\n      window.removeEventListener('mouseup', handleUp)\n    }\n  }, [])\n\n  return (\n    <div\n      onMouseDown={handleDown}\n      onMouseMove={(e) => handleMove(e)}\n      style={{\n        height: 14,\n        marginTop: 17,\n        marginBottom: 4,\n        cursor: 'ew-resize',\n        position: 'relative',\n      }}\n      id={`rbgcp-opacity-wrapper${pickerIdSuffix}`}\n      // className=\"rbgcp-opacity-wrap\"\n    >\n      <div\n        // className=\"rbgcp-opacity-checkered\"\n        id={`rbgcp-opacity-checkered-bg${pickerIdSuffix}`}\n        style={{ ...defaultStyles.rbgcpCheckered, width: '100%', height: 14 }}\n      />\n      <div\n        // className=\"rbgcp-handle rbgcp-handle-opacity\"\n        id={`rbgcp-opacity-handle${pickerIdSuffix}`}\n        style={{ ...defaultStyles.rbgcpHandle, left: left * hc?.a, top: -2 }}\n      />\n      <div\n        style={{ ...defaultStyles.rbgcpOpacityOverlay, background: bg }}\n        id={`rbgcp-opacity-overlay${pickerIdSuffix}`}\n        // className=\"rbgcp-opacity-overlay\"\n        onClick={(e) => handleClick(e)}\n      />\n    </div>\n  )\n}\n\nexport default Opacity\n"
  },
  {
    "path": "src/components/Picker.tsx",
    "content": "import React from 'react'\nimport Hue from './Hue.js'\nimport Inputs from './Inputs.js'\nimport Square from './Square.js'\nimport Opacity from './Opacity.js'\nimport Presets from './Presets.js'\nimport Controls from './Controls.js'\nimport { usePicker } from '../context.js'\nimport GradientBar from './GradientBar.js'\nimport { LocalesProps } from '../shared/types.js'\n\nconst Picker = ({\n  locales,\n  presets,\n  hideHue,\n  hideInputs,\n  hidePresets,\n  hideOpacity,\n  hideEyeDrop,\n  hideControls,\n  hideInputType,\n  hideColorGuide,\n  hidePickerSquare,\n  hideGradientType,\n  hideGradientStop,\n  hideGradientAngle,\n  hideColorTypeBtns,\n  hideAdvancedSliders,\n  hideGradientControls,\n}: PickerProps) => {\n  const { isGradient, pickerIdSuffix } = usePicker()\n\n  return (\n    <div style={{ userSelect: 'none' }} id={`rbgcp-color-picker${pickerIdSuffix}`}>\n      {!hidePickerSquare && <Square />}\n      {!hideControls && (\n        <Controls\n          locales={locales}\n          hideEyeDrop={hideEyeDrop}\n          hideInputType={hideInputType}\n          hideColorGuide={hideColorGuide}\n          hideGradientType={hideGradientType}\n          hideGradientStop={hideGradientStop}\n          hideColorTypeBtns={hideColorTypeBtns}\n          hideGradientAngle={hideGradientAngle}\n          hideAdvancedSliders={hideAdvancedSliders}\n          hideGradientControls={hideGradientControls}\n        />\n      )}\n      {isGradient && <GradientBar />}\n      {!hideHue && <Hue />}\n      {!hideOpacity && <Opacity />}\n      {!hideInputs && <Inputs />}\n      {!hidePresets && <Presets presets={presets} />}\n    </div>\n  )\n}\n\nexport default Picker\n\ntype PickerProps = {\n  hideControls?: boolean\n  hideInputs?: boolean\n  hidePresets?: boolean\n  hideOpacity?: boolean\n  hideHue?: boolean\n  presets?: string[]\n  hideEyeDrop?: boolean\n  hideAdvancedSliders?: boolean\n  hideColorGuide?: boolean\n  hideInputType?: boolean\n  hideColorTypeBtns?: boolean\n  hideGradientType?: boolean\n  hideGradientAngle?: boolean\n  hideGradientStop?: boolean\n  hideGradientControls?: boolean\n  locales?: LocalesProps\n  hidePickerSquare?: boolean\n}\n"
  },
  {
    "path": "src/components/Portal.tsx",
    "content": "import { memo, useEffect, useRef, useState, ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\n\nconst Portal = ({ children }: { children: ReactNode }) => {\n  const id = 'id' + Math.random().toString(16).slice(2)\n  const el = useRef(\n    document.getElementById(id) ?? document.createElement('div')\n  )\n  const [dynamic] = useState(!el.current.parentElement)\n\n  useEffect(() => {\n    const refValue = el.current\n    if (dynamic) {\n      el.current.id = id\n      document.body.appendChild(el.current)\n    }\n    return () => {\n      if (dynamic && refValue.parentElement) {\n        refValue.parentElement.removeChild(refValue)\n      }\n    }\n    //eslint-disable-next-line\n  }, [id])\n  return createPortal(children, el.current)\n}\n\nexport default memo(Portal)\n"
  },
  {
    "path": "src/components/Presets.tsx",
    "content": "/* eslint-disable react/no-array-index-key */\n/* eslint-disable jsx-a11y/no-static-element-interactions */\nimport React from 'react'\nimport { usePicker } from '../context.js'\nimport { fakePresets } from '../constants.js'\n\nconst Presets = ({ presets = [] }: { presets?: string[] }) => {\n  const {\n    value,\n    onChange,\n    isDarkMode,\n    squareWidth,\n    handleChange,\n    pickerIdSuffix,\n  } = usePicker()\n\n  const getPresets = () => {\n    if (presets?.length > 0) {\n      return presets?.slice(0, 18)\n    } else {\n      return fakePresets\n    }\n  }\n\n  const handlePresetClick = (preset: string) => {\n    if (preset?.includes('gradient')) {\n      onChange(preset)\n    } else {\n      handleChange(preset)\n    }\n  }\n\n  const getBorder = (p: string) => {\n    if (!p || isDarkMode) return ''\n    const c = p?.replace(' ', '');\n    if (c === 'rgba(255,255,255,1)') {\n      return '1px solid #96959c'\n    }\n    return ''\n  }\n\n  return (\n    <div\n      style={{\n        marginTop: 14,\n        display: 'flex',\n        justifyContent: 'space-between',\n      }}\n      id={`rbgcp-footer-wrapper${pickerIdSuffix}`}\n      // className=\"rbgcp-presets-wrap\"\n    >\n      <div\n        style={{\n          width: 50,\n          height: 50,\n          flexShrink: 0,\n          borderRadius: 6,\n          background: value,\n          border: getBorder(value),\n        }}\n        id={`rbgcp-preview${pickerIdSuffix}`}\n        // className=\"rbgcp-preset-color-preview\"\n      />\n      <div\n        style={{\n          rowGap: 3,\n          display: 'flex',\n          flexWrap: 'wrap',\n          width: squareWidth - 57,\n          justifyContent: 'space-between',\n        }}\n        id={`rbgcp-presets-wrapper${pickerIdSuffix}`}\n        // className=\"rbgcp-presets-list\"\n      >\n        {getPresets().map((p: any, key: number) => (\n          <div\n            key={`${p}-${key}`}\n            id={`rbgcp-preset-${key}-wrapper${pickerIdSuffix}`}\n            style={{ width: `calc(100% / 9)`, paddingLeft: 3 }}\n          >\n            <div\n              style={{\n                height: 23.5,\n                width: '100%',\n                background: p,\n                borderRadius: 4,\n                border: getBorder(p),\n              }}\n              // className=\"rbgcp-preset-color\"\n              onClick={() => handlePresetClick(p)}\n              id={`rbgcp-preset-${key}${pickerIdSuffix}`}\n            />\n          </div>\n        ))}\n      </div>\n    </div>\n  )\n}\n\nexport default Presets\n"
  },
  {
    "path": "src/components/Square.tsx",
    "content": "/* eslint-disable jsx-a11y/no-static-element-interactions */\nimport { computePickerPosition, computeSquareXY } from '../utils/utils.js'\nimport React, { useRef, useState, useEffect } from 'react'\nimport usePaintSquare from '../hooks/usePaintSquare.js'\nimport { usePicker } from '../context.js'\nimport throttle from 'lodash.throttle'\nimport tinycolor from 'tinycolor2'\n\nconst Square = () => {\n  const {\n    hc,\n    config,\n    squareWidth,\n    squareHeight,\n    handleChange,\n    defaultStyles,\n    pickerIdSuffix,\n  } = usePicker()\n  const { crossSize } = config\n  const [dragging, setDragging] = useState(false)\n  const canvas = useRef<HTMLCanvasElement>(null)\n  const [x, y] = computeSquareXY(hc?.s, hc?.v * 100, squareWidth, squareHeight, crossSize)\n  const [dragPos, setDragPos] = useState({ x, y })\n\n  usePaintSquare(canvas, hc?.h, squareWidth, squareHeight)\n\n  useEffect(() => {\n    if (!dragging) {\n      setDragPos({ x: hc?.v === 0 ? dragPos.x : x, y })\n    }\n  }, [x, y])\n\n  const handleColor = (e: any) => {\n    const onMouseMove = throttle(() => {\n      const [x, y] = computePickerPosition(e, crossSize)\n      if (x && y) {\n        const x1 = Math.min(x + crossSize / 2, squareWidth - 1)\n        const y1 = Math.min(y + crossSize / 2, squareHeight - 1)\n        const newS = (x1 / squareWidth) * 100\n        const newY = 100 - (y1 / squareHeight) * 100\n        setDragPos({ x: newY === 0 ? dragPos?.x : x, y })\n        const updated = tinycolor(\n          `hsva(${hc?.h}, ${newS}%, ${newY}%, ${hc?.a})`\n        )\n        handleChange(updated.toRgbString())\n      }\n    }, 250)\n\n    onMouseMove()\n  }\n\n  const stopDragging = () => {\n    setDragging(false)\n  }\n\n  const handleMove = (e: any) => {\n    if (dragging) {\n      handleColor(e)\n    }\n  }\n\n  // const handleTouchMove = (e: any) => {\n  //   if (dragging && isMobile) {\n  //     document.body.style.overflow = 'hidden'\n  //     handleColor(e)\n  //   }\n  // }\n\n  const handleClick = (e: any) => {\n    if (!dragging) {\n      handleColor(e)\n    }\n  }\n\n  const handleMouseDown = () => {\n    setDragging(true)\n  }\n\n  const handleCanvasDown = (e: any) => {\n    setDragging(true)\n    handleColor(e)\n  }\n\n  useEffect(() => {\n    const handleUp = () => {\n      stopDragging()\n    }\n\n    window.addEventListener('mouseup', handleUp)\n\n    return () => {\n      window.removeEventListener('mouseup', handleUp)\n    }\n  }, [])\n\n  return (\n    <div\n      style={{ position: 'relative', marginBottom: 12 }}\n      id={`rbgcp-square-wrapper${pickerIdSuffix}`}\n    >\n      <div\n        onMouseUp={stopDragging}\n        onTouchEnd={stopDragging}\n        onMouseDown={handleCanvasDown}\n        onTouchStart={handleCanvasDown}\n        onMouseMove={(e) => handleMove(e)}\n        id={`rbgcp-square${pickerIdSuffix}`}\n        style={{ position: 'relative', cursor: 'ew-cross' }}\n        // className=\"rbgcp-square-wrap\"\n      >\n        <div\n          style={{\n            ...defaultStyles.rbgcpHandle,\n            transform: `translate(${dragPos?.x ?? 0}px, ${dragPos?.y ?? 0}px)`,\n            ...(dragging ? { transition: '' } : {}),\n          }}\n          onMouseDown={handleMouseDown}\n          id={`rbgcp-square-handle${pickerIdSuffix}`}\n          // className=\"rbgcp-handle rbgcp-handle-square\"\n        />\n        <div\n          style={{ ...defaultStyles.rbgcpCanvasWrapper, height: squareHeight }}\n          id={`rbgcp-square-canvas-wrapper${pickerIdSuffix}`}\n          // className=\"rbgcp-canvas-wrapper\"\n          onClick={(e) => handleClick(e)}\n        >\n          <canvas\n            ref={canvas}\n            // className=\"rbgcp-canvas\"\n            width={`${squareWidth}px`}\n            height={`${squareHeight}px`}\n            id={`rbgcp-square-canvas${pickerIdSuffix}`}\n          />\n        </div>\n      </div>\n    </div>\n  )\n}\n\nexport default Square\n"
  },
  {
    "path": "src/components/icon.tsx",
    "content": "import React from 'react'\nimport { usePicker } from '../context.js'\n\ntype ColorProps = {\n  color: string\n}\n\nconst TrashIcon = () => {\n  const { defaultStyles } = usePicker()\n\n  const styles = {\n    fill: 'none',\n    strokeWidth: '1.8px',\n  }\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 20 20\"\n      style={{ width: 15 }}\n    >\n      <polyline\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{ ...styles, ...defaultStyles.rbgcpControlIcon }}\n        points=\"17.96 4.31 2.04 4.3 3.75 4.3 4.81 17.29 5.16 17.96 5.74 18.47 6.59 18.62 13.64 18.62 14.52 18.32 15.07 17.68 15.29 17.12 16.28 4.3 12.87 4.3 12.87 2.38 12.48 1.75 11.83 1.46 8.4 1.46 7.64 1.68 7.26 2.21 7.16 2.52 7.17 4.23\"\n      />\n    </svg>\n  )\n}\n\nexport default TrashIcon\n\nexport const LinearIcon = ({ color }: ColorProps) => {\n  const { defaultStyles } = usePicker()\n\n  const col = color ?? ''\n  const styles = {\n    fill: 'none',\n    strokeWidth: '1.8px',\n  }\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 20 20\"\n      style={{ width: 14 }}\n    >\n      <polyline\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{\n          ...styles,\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col }),\n        }}\n        points=\"0.9 12.73 0.9 19.1 7.27 19.1 0.9 19.1 19.1 0.9 12.73 0.9 19.1 0.9 19.1 7.27\"\n      />\n    </svg>\n  )\n}\n\nexport const RadialIcon = ({ color }: ColorProps) => {\n  const { defaultStyles } = usePicker()\n\n  const col = color ?? ''\n  const styles = {\n    fill: 'none',\n    strokeMiterlimit: 10,\n    strokeWidth: '1.8px',\n  }\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 20 20\"\n      style={{ width: 15 }}\n    >\n      <circle\n        style={{\n          ...styles,\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col }),\n        }}\n        cx=\"10\"\n        cy=\"10\"\n        r=\"9\"\n      />\n      <circle\n        style={{\n          ...styles,\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col }),\n        }}\n        cx=\"10\"\n        cy=\"10\"\n        r=\"5\"\n      />\n    </svg>\n  )\n}\n\nexport const SlidersIcon = ({ color }: ColorProps) => {\n  const { defaultStyles } = usePicker()\n\n  const col = color ?? ''\n  const style1 = {\n    fill: 'none',\n    strokeWidth: '1.8px',\n  }\n  const style2 = {\n    strokeWidth: '1.8px',\n  }\n\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 20 20\"\n      style={{ width: 17 }}\n    >\n      <polyline\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{\n          ...style1,\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col }),\n        }}\n        points=\"3.74 2.75 3.74 12.69 0.9 12.71 6.59 12.71\"\n      />\n      <line\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{\n          ...style2,\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col, fill: col }),\n        }}\n        x1=\"3.74\"\n        y1=\"17.26\"\n        x2=\"3.74\"\n        y2=\"15.21\"\n      />\n      <polyline\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{\n          ...style1,\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col }),\n        }}\n        points=\"10.1 17.25 10.1 7.31 12.95 7.29 7.26 7.29\"\n      />\n      <line\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{\n          ...style2,\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col, fill: col }),\n        }}\n        x1=\"10.1\"\n        y1=\"2.74\"\n        x2=\"10.1\"\n        y2=\"4.79\"\n      />\n      <polyline\n        fillRule=\"evenodd\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{\n          ...style1,\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col }),\n        }}\n        points=\"16.26 2.75 16.26 12.69 13.41 12.71 19.1 12.71\"\n      />\n      <line\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{\n          ...style2,\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col, fill: col }),\n        }}\n        x1=\"16.26\"\n        y1=\"17.26\"\n        x2=\"16.26\"\n        y2=\"15.21\"\n      />\n    </svg>\n  )\n}\n\nexport const InputsIcon = ({ color }: ColorProps) => {\n  const { defaultStyles } = usePicker()\n\n  const col = color ?? ''\n  const style1 = {\n    fill: 'none',\n    strokeWidth: '1.8px',\n  }\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 20 20\"\n      style={{ width: 17 }}\n    >\n      <path\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{\n          ...style1,\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col }),\n        }}\n        d=\"M6.35,2.72a4.85,4.85,0,0,1,1.86.16,7.94,7.94,0,0,1,.88.43,3.66,3.66,0,0,0,.85.49c.25,0,.58-.27.81-.39A8.25,8.25,0,0,1,11.7,3a4,4,0,0,1,1.79-.23,3.21,3.21,0,0,0-1.34.09,6.39,6.39,0,0,0-1.47.63c-.45.25-.7.3-.7.86s0,1.18,0,1.78c0,1.3,0,2.61,0,3.92h0v5.63a2.46,2.46,0,0,1,0,.47c-.07.28-.43.42-.7.57a5.29,5.29,0,0,1-2.94.61A9.3,9.3,0,0,0,8,17.15l1.09-.37.89-.52c.06,0,.48.21.56.25.32.14.64.27,1,.38a8.54,8.54,0,0,0,2.12.4\"\n      />\n      <path\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{\n          ...style1,\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col }),\n        }}\n        d=\"M7.57,5.73C6,5.7,4.5,5.65,3,5.77a2.28,2.28,0,0,0-1.76.74A2.3,2.3,0,0,0,.94,7.83l0,3.82A4.73,4.73,0,0,0,1,12.9a1.64,1.64,0,0,0,.68,1,2.44,2.44,0,0,0,1,.27,25,25,0,0,0,4.74.09\"\n      />\n      <path\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{\n          ...style1,\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col }),\n        }}\n        d=\"M12.43,14.32a44.12,44.12,0,0,0,4.6,0,2.24,2.24,0,0,0,1.76-.74,2.29,2.29,0,0,0,.27-1.32l0-3.81A4.81,4.81,0,0,0,19,7.15a1.62,1.62,0,0,0-.68-1,2.31,2.31,0,0,0-1-.28,26.8,26.8,0,0,0-4.74-.09\"\n      />\n    </svg>\n  )\n}\n\nexport const PaletteIcon = ({ color }: ColorProps) => {\n  const { defaultStyles } = usePicker()\n\n  const col = color ?? ''\n  const style2 = {\n    strokeMiterlimit: 10,\n    strokeWidth: '0.5px',\n  }\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 20 20\"\n      style={{ width: 17 }}\n    >\n      <circle\n        style={{\n          ...defaultStyles.rbgcpControlIcon2,\n          ...(col && { fill: col }),\n        }}\n        cx=\"9.36\"\n        cy=\"5.07\"\n        r=\"1.71\"\n      />\n      <circle\n        style={{\n          ...defaultStyles.rbgcpControlIcon2,\n          ...(col && { fill: col }),\n        }}\n        cx=\"13.93\"\n        cy=\"6.91\"\n        r=\"1.71\"\n      />\n      <circle\n        style={{\n          ...defaultStyles.rbgcpControlIcon2,\n          ...(col && { fill: col }),\n        }}\n        cx=\"5.8\"\n        cy=\"7.55\"\n        r=\"1.71\"\n      />\n      <circle\n        style={{\n          ...defaultStyles.rbgcpControlIcon2,\n          ...(col && { fill: col }),\n        }}\n        cx=\"5.45\"\n        cy=\"12.04\"\n        r=\"1.71\"\n      />\n      <path\n        style={{\n          ...style2,\n          ...defaultStyles.rbgcpControlIcon,\n          ...defaultStyles.rbgcpControlIcon2,\n          ...(col && { fill: col, stroke: col }),\n        }}\n        d=\"M19.1,10c0,3.58-2.12,2.94-4.06,2.35-1.15-.34-2.24-.67-2.77-.08-.68.78-.54,2.07-.39,3.33.2,1.79.39,3.5-1.88,3.5A9.1,9.1,0,1,1,19.1,10ZM10,18c.7,0,.74-.19.75-.2a2.67,2.67,0,0,0,.07-1.27c0-.19,0-.42-.06-.67-.06-.53-.13-1.15-.14-1.67a3.82,3.82,0,0,1,.8-2.63,2.14,2.14,0,0,1,1.45-.7,4.36,4.36,0,0,1,1.32.12c.39.08.8.21,1.16.32h0c.39.12.74.23,1.08.3.74.17,1,.1,1.13,0S18,11.32,18,10a8,8,0,1,0-8,8Z\"\n      />\n    </svg>\n  )\n}\n\nexport const DegreesIcon = ({ color }: { color?: string }) => {\n  const { defaultStyles } = usePicker()\n\n  const col = color ?? ''\n  const style2 = {\n    fill: 'none',\n    strokeMiterlimit: 10,\n    strokeWidth: '1.8px',\n  }\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 20 20\"\n      style={{ width: 15 }}\n    >\n      <polyline\n        strokeLinecap=\"round\"\n        style={{\n          ...style2,\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col }),\n        }}\n        points=\"13.86 2.01 1.7 16.99 18.77 16.99\"\n      />\n      <polyline\n        strokeLinecap=\"round\"\n        style={{\n          ...style2,\n          ...defaultStyles.rbgcpControlIcon,\n          ...(col && { stroke: col }),\n        }}\n        points=\"10.96 16.38 10.96 16.38 10.74 15.7 10.44 14.97 10.06 14.21 9.72 13.63 9.21 12.89 8.85 12.44 8.41 11.95 7.91 11.45 7.51 11.1\"\n      />\n    </svg>\n  )\n}\n\nexport const StopIcon = () => {\n  const { defaultStyles } = usePicker()\n\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 20 20\"\n      style={{ width: 20, marginRight: 1 }}\n    >\n      <path\n        style={{ ...defaultStyles.rbgcpControlIcon2 }}\n        d=\"M2.39,8c-.63,0-1,.21-1,.63A.49.49,0,0,0,1.67,9a6.48,6.48,0,0,0,1.11.43A3,3,0,0,1,4,10.09a1.47,1.47,0,0,1,.35,1.09,1.75,1.75,0,0,1-.57,1.42,2.21,2.21,0,0,1-1.48.48,8.32,8.32,0,0,1-1.68-.21l-.31-.06.12-.94a13.7,13.7,0,0,0,1.8.16c.61,0,.92-.26.92-.77a.52.52,0,0,0-.21-.44,3.13,3.13,0,0,0-.85-.34A3.32,3.32,0,0,1,.66,9.79a1.43,1.43,0,0,1-.42-1.1A1.6,1.6,0,0,1,.78,7.36a2.32,2.32,0,0,1,1.49-.44,10.46,10.46,0,0,1,1.64.17l.32.07-.1.95C3.31,8,2.73,8,2.39,8Z\"\n      />\n      <path\n        style={{ ...defaultStyles.rbgcpControlIcon2 }}\n        d=\"M4.79,8.09V7H9.16V8.09H7.59V13H6.38V8.09Z\"\n      />\n      <path\n        style={{ ...defaultStyles.rbgcpControlIcon2 }}\n        d=\"M14,12.34a2.25,2.25,0,0,1-1.91.74,2.24,2.24,0,0,1-1.91-.74A3.85,3.85,0,0,1,9.61,10a4,4,0,0,1,.56-2.34,2.2,2.2,0,0,1,1.91-.77A2.21,2.21,0,0,1,14,7.69,4,4,0,0,1,14.55,10,3.85,3.85,0,0,1,14,12.34Zm-2.88-.77a1,1,0,0,0,1,.46,1,1,0,0,0,1-.46A3.25,3.25,0,0,0,13.3,10,3.45,3.45,0,0,0,13,8.46a1,1,0,0,0-1-.49,1,1,0,0,0-1,.49A3.43,3.43,0,0,0,10.85,10,3.38,3.38,0,0,0,11.11,11.57Z\"\n      />\n      <path\n        style={{ ...defaultStyles.rbgcpControlIcon2 }}\n        d=\"M17.77,11.24h-1V13H15.58V7h2.19a1.85,1.85,0,0,1,2.11,2.07,2.21,2.21,0,0,1-.54,1.6A2.07,2.07,0,0,1,17.77,11.24Zm-1-1h1c.6,0,.9-.37.9-1.12a1.18,1.18,0,0,0-.22-.79.88.88,0,0,0-.68-.24h-1Z\"\n      />\n    </svg>\n  )\n}\n"
  },
  {
    "path": "src/components/index.tsx",
    "content": "'use client'\nimport React from 'react'\nimport PickerContextWrapper from '../context.js'\nimport Picker from './Picker.js'\nimport { ColorPickerProps } from '../shared/types.js'\nimport { defaultLocales } from '../constants.js'\nimport { objectToString } from '../utils/utils.js'\nimport { getStyles } from '../styles/styles.js'\n\nexport function ColorPicker({\n  idSuffix,\n  value = 'rgba(175, 51, 242, 1)',\n  onChange,\n  hideControls = false,\n  hideInputs = false,\n  hideOpacity = false,\n  hidePresets = false,\n  hideHue = false,\n  presets = [],\n  hideEyeDrop = false,\n  hideAdvancedSliders = false,\n  hideColorGuide = false,\n  hideInputType = false,\n  hideColorTypeBtns = false,\n  hideGradientType = false,\n  hideGradientAngle = false,\n  hideGradientStop = false,\n  hideGradientControls = false,\n  locales = defaultLocales,\n  width = 294,\n  height = 294,\n  style = {},\n  className,\n  disableDarkMode = false,\n  disableLightMode = false,\n  hidePickerSquare = false,\n  showHexAlpha = false,\n  config = {},\n}: ColorPickerProps) {\n  const safeValue = objectToString(value)\n  const isDarkMode =\n    typeof window === 'undefined' || disableDarkMode\n      ? false\n      : window.matchMedia('(prefers-color-scheme: dark)').matches ||\n          disableLightMode\n        ? true\n        : false\n  // const contRef = useRef<HTMLDivElement>(null)\n  const defaultStyles = getStyles(isDarkMode, style)\n  const pickerIdSuffix = isDarkMode\n    ? `-dark${idSuffix ? `-${idSuffix}` : ''}`\n    : idSuffix\n      ? `-${idSuffix}`\n      : ''\n\n  return (\n    <div\n      // ref={contRef}\n      className={className}\n      style={{ ...defaultStyles.body, width: width }}\n    >\n      <PickerContextWrapper\n        value={safeValue}\n        onChange={onChange}\n        squareWidth={width}\n        passedConfig={config}\n        squareHeight={height}\n        isDarkMode={isDarkMode}\n        hideOpacity={hideOpacity}\n        showHexAlpha={showHexAlpha}\n        defaultStyles={defaultStyles}\n        pickerIdSuffix={pickerIdSuffix}\n      >\n        <Picker\n          hideControls={hideControls}\n          hideInputs={hideInputs}\n          hidePresets={hidePresets}\n          hideOpacity={hideOpacity}\n          hideHue={hideHue}\n          presets={presets}\n          hideEyeDrop={hideEyeDrop}\n          hideAdvancedSliders={hideAdvancedSliders}\n          hideColorGuide={hideColorGuide}\n          hideInputType={hideInputType}\n          hideColorTypeBtns={hideColorTypeBtns}\n          hideGradientType={hideGradientType}\n          hideGradientAngle={hideGradientAngle}\n          hideGradientStop={hideGradientStop}\n          hideGradientControls={hideGradientControls}\n          hidePickerSquare={hidePickerSquare}\n          locales={locales}\n        />\n      </PickerContextWrapper>\n    </div>\n  )\n}\n"
  },
  {
    "path": "src/constants.ts",
    "content": "export const defaultLocales = {\n  CONTROLS: {\n    SOLID: 'Solid',\n    GRADIENT: 'Gradient',\n  },\n}\n\nexport const fakePresets = [\n  'rgba(0,0,0,1)',\n  'rgba(128,128,128, 1)',\n  'rgba(192,192,192, 1)',\n  'rgba(255,255,255, 1)',\n  'rgba(0,0,128,1)',\n  'rgba(0,0,255,1)',\n  'rgba(0,255,255, 1)',\n  'rgba(0,128,0,1)',\n  'rgba(128,128,0, 1)',\n  'rgba(0,128,128,1)',\n  'rgba(0,255,0, 1)',\n  'rgba(128,0,0, 1)',\n  'rgba(128,0,128, 1)',\n  'rgba(175, 51, 242, 1)',\n  'rgba(255,0,255, 1)',\n  'rgba(255,0,0, 1)',\n  'rgba(240, 103, 46, 1)',\n  'rgba(255,255,0, 1)',\n]\n"
  },
  {
    "path": "src/context.tsx",
    "content": "import React, {\n  createContext,\n  useContext,\n  ReactNode,\n  useEffect,\n  useState,\n} from 'react'\nimport { GradientProps, Styles, PassedConfig, Config } from './shared/types.js'\nimport { isUpperCase, getColorObj, getDetails } from './utils/utils.js'\nimport { low, high, getColors } from './utils/formatters.js'\nimport tinycolor from 'tinycolor2'\n\nconst PickerContext = createContext<PickerContextProps | null>(null)\n\nexport default function PickerContextWrapper({\n  value,\n  children,\n  onChange,\n  isDarkMode,\n  squareWidth,\n  hideOpacity,\n  showHexAlpha,\n  squareHeight,\n  passedConfig,\n  defaultStyles,\n  pickerIdSuffix,\n}: PCWProps) {\n  const config: Config = {\n    barSize: passedConfig.barSize ?? defaultConfig.barSize,\n    crossSize: passedConfig.crossSize ?? defaultConfig.crossSize,\n    defaultColor: passedConfig.defaultColor ?? defaultConfig.defaultColor,\n    defaultGradient:\n      passedConfig.defaultGradient ?? defaultConfig.defaultGradient,\n  }\n\n  const colors = getColors(value, config.defaultColor, config.defaultGradient)\n  const { degrees, degreeStr, isGradient, gradientType } = getDetails(value)\n  const { currentColor, selectedColor, currentLeft } = getColorObj(colors, config.defaultGradient)\n  const [inputType, setInputType] = useState('rgb')\n  const [previous, setPrevious] = useState({})\n  const tinyColor = tinycolor(currentColor)\n  const rgba = tinyColor.toRgb()\n  const hsv = tinyColor.toHsv()\n  const [hc, setHc] = useState({ ...rgba, ...hsv })\n\n  useEffect(() => {\n    if (hsv?.s === 0) {\n      setHc({ ...rgba, ...hsv, h: hc?.h })\n    } else {\n      setHc({ ...rgba, ...hsv })\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [currentColor])\n\n  const createGradientStr = (newColors: GradientProps[]) => {\n    const sorted = newColors.sort(\n      (a: GradientProps, b: GradientProps) => a.left - b.left\n    )\n    const colorString = sorted?.map((cc: any) => `${cc?.value} ${cc.left}%`)\n    const newGrade = `${gradientType}(${degreeStr}, ${colorString.join(', ')})`\n    setPrevious({ ...previous, gradient: newGrade })\n    onChange(newGrade)\n  }\n\n  const handleGradient = (newColor: string, left?: number) => {\n    const remaining = colors?.filter(\n      (c: GradientProps) => !isUpperCase(c.value)\n    )\n    const newColors = [\n      { value: newColor.toUpperCase(), left: left ?? currentLeft },\n      ...remaining,\n    ]\n    createGradientStr(newColors)\n  }\n\n  const handleChange = (newColor: string) => {\n    if (isGradient) {\n      handleGradient(newColor)\n    } else {\n      setPrevious({ ...previous, color: newColor })\n      onChange(newColor)\n    }\n  }\n\n  const deletePoint = () => {\n    if (colors?.length > 2) {\n      const formatted = colors?.map((fc: GradientProps, i: number) => ({\n        ...fc,\n        value: i === selectedColor - 1 ? high(fc) : low(fc),\n      }))\n      const remaining = formatted?.filter(\n        (_: any, i: number) => i !== selectedColor\n      )\n      createGradientStr(remaining)\n    }\n  }\n\n  const pickerContext = {\n    hc,\n    setHc,\n    value,\n    colors,\n    config,\n    degrees,\n    onChange,\n    previous,\n    inputType,\n    tinyColor,\n    isDarkMode,\n    isGradient,\n    squareWidth,\n    hideOpacity,\n    currentLeft,\n    deletePoint,\n    showHexAlpha,\n    squareHeight,\n    setInputType,\n    gradientType,\n    handleChange,\n    currentColor,\n    selectedColor,\n    defaultStyles,\n    handleGradient,\n    pickerIdSuffix,\n    createGradientStr,\n  }\n\n  return (\n    <PickerContext.Provider value={pickerContext}>\n      {children}\n    </PickerContext.Provider>\n  )\n}\n\nexport function usePicker() {\n  const pickerContext = useContext(PickerContext)\n\n  if (!pickerContext) {\n    throw new Error('usePicker has to be used within <PickerContext.Provider>')\n  }\n\n  return pickerContext\n}\n\ntype PCWProps = {\n  value: string\n  squareWidth: number\n  children: ReactNode\n  squareHeight: number\n  hideOpacity: boolean\n  onChange: (arg0: string) => void\n  defaultStyles: Styles\n  isDarkMode: boolean\n  pickerIdSuffix: string\n  showHexAlpha: boolean\n  passedConfig: PassedConfig\n}\n\nexport type PickerContextProps = {\n  hc: any\n  config: Config\n  value: string\n  colors: GradientProps[]\n  degrees: number\n  onChange: (arg0: string) => void\n  inputType: string\n  tinyColor: any\n  isGradient: boolean\n  squareWidth: number\n  hideOpacity: boolean\n  currentLeft: number\n  deletePoint: () => void\n  squareHeight: number\n  setInputType: (arg0: string) => void\n  gradientType?: string\n  handleChange: (arg0: string) => void\n  currentColor: string\n  selectedColor: number\n  setHc: (arg0: any) => void\n  handleGradient: (arg0: string, arg1?: number) => void\n  createGradientStr: (arg0: GradientProps[]) => void\n  defaultStyles: Styles\n  previous: {\n    color?: string\n    gradient?: string\n  }\n  isDarkMode: boolean\n  pickerIdSuffix: string\n  showHexAlpha: boolean\n}\n\nconst defaultConfig = {\n  barSize: 18,\n  crossSize: 18,\n  inputSize: 40,\n  delay: 150,\n  defaultColor: 'rgba(175, 51, 242, 1)',\n  defaultGradient:\n    'linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(9,9,121,1) 35%, rgba(0,212,255,1) 100%)',\n}"
  },
  {
    "path": "src/hooks/useColorPicker.ts",
    "content": "import tc from 'tinycolor2'\nimport { useState, useEffect } from 'react'\nimport { rgb2cmyk } from '../utils/converters.js'\nimport { ColorsProps, GradientProps, Config } from '../shared/types.js'\nimport { isUpperCase, getDetails, getColorObj } from '../utils/utils.js'\nimport { low, high, getColors, formatInputValues } from '../utils/formatters.js'\n\nexport const useColorPicker = (\n  value: string,\n  onChange: (arg0: string) => void,\n  config?: Config\n) => {\n  const {\n    defaultColor = 'rgba(175, 51, 242, 1)',\n    defaultGradient = 'linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(9,9,121,1) 35%, rgba(0,212,255,1) 100%)',\n  } = config ?? {}\n\n  let colors = getColors(value, defaultColor, defaultGradient)\n  const { degrees, degreeStr, isGradient, gradientType } = getDetails(value)\n  const { currentColor, selectedColor, currentLeft } = getColorObj(\n    colors,\n    defaultGradient\n  )\n  const [previousColors, setPreviousColors] = useState([])\n\n  const getGradientObject = (currentValue: string) => {\n    if (currentValue) {\n      colors = getColors(currentValue, defaultColor, defaultGradient)\n    }\n    if (value) {\n      if (isGradient) {\n        return {\n          isGradient: true,\n          gradientType: gradientType,\n          degrees: degreeStr,\n          colors: colors?.map((c: ColorsProps) => ({\n            ...c,\n            value: c.value?.toLowerCase(),\n          })),\n        }\n      } else {\n        return {\n          isGradient: false,\n          gradientType: null,\n          degrees: null,\n          colors: colors?.map((c: ColorsProps) => ({\n            ...c,\n            value: c.value?.toLowerCase(),\n          })),\n        }\n      }\n    } else {\n      console.log(\n        'RBGCP ERROR - YOU MUST PASS A VALUE AND CALLBACK TO THE useColorPicker HOOK'\n      )\n    }\n  }\n\n  const tiny = tc(currentColor)\n  const { r, g, b, a } = tiny.toRgb()\n  const { h, s, l } = tiny.toHsl()\n\n  useEffect(() => {\n    if (tc(currentColor)?.isValid() && previousColors[0] !== currentColor) {\n      // @ts-expect-error - currentColor type issue\n      setPreviousColors([currentColor, ...previousColors.slice(0, 19)])\n    }\n  }, [currentColor, previousColors])\n\n  const setLinear = () => {\n    const remaining = value.split(/,(.+)/)[1]\n    onChange(`linear-gradient(90deg, ${remaining}`)\n  }\n\n  const setRadial = () => {\n    const remaining = value.split(/,(.+)/)[1]\n    onChange(`radial-gradient(circle, ${remaining}`)\n  }\n\n  const setDegrees = (newDegrees: number) => {\n    const remaining = value.split(/,(.+)/)[1]\n    onChange(\n      `linear-gradient(${formatInputValues(\n        newDegrees,\n        0,\n        360\n      )}deg, ${remaining}`\n    )\n    if (gradientType !== 'linear-gradient') {\n      console.log(\n        'Warning: you are updating degrees when the gradient type is not linear. This will change the gradients type which may be undesired'\n      )\n    }\n  }\n\n  const setSolid = (startingColor: string) => {\n    const newValue = startingColor ?? defaultColor ?? 'rgba(175, 51, 242, 1)'\n    onChange(newValue)\n  }\n\n  const setGradient = (startingGradiant: string) => {\n    const newValue =\n      startingGradiant ??\n      defaultGradient ??\n      'linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(9,9,121,1) 35%, rgba(0,212,255,1) 100%)'\n    onChange(newValue)\n  }\n\n  const createGradientStr = (newColors: GradientProps[]) => {\n    const sorted = newColors.sort(\n      (a: GradientProps, b: GradientProps) => a.left - b.left\n    )\n    const colorString = sorted?.map(\n      (cc: ColorsProps) => `${cc?.value} ${cc.left}%`\n    )\n    onChange(`${gradientType}(${degreeStr}, ${colorString.join(', ')})`)\n  }\n\n  const handleGradient = (newColor: string, left?: number) => {\n    const remaining = colors?.filter((c: ColorsProps) => !isUpperCase(c.value))\n    const newColors = [\n      { value: newColor.toUpperCase(), left: left ?? currentLeft },\n      ...remaining,\n    ]\n    createGradientStr(newColors)\n  }\n\n  const handleChange = (newColor: string) => {\n    newColor = newColor?.replace(/\\s+/g, '')\n    if (isGradient) {\n      handleGradient(newColor)\n    } else {\n      onChange(newColor)\n    }\n  }\n\n  const setR = (newR: number) => {\n    const newVal = formatInputValues(newR, 0, 255)\n    handleChange(`rgba(${newVal}, ${g}, ${b}, ${a})`)\n  }\n\n  const setG = (newG: number) => {\n    const newVal = formatInputValues(newG, 0, 255)\n    handleChange(`rgba(${r}, ${newVal}, ${b}, ${a})`)\n  }\n\n  const setB = (newB: number) => {\n    const newVal = formatInputValues(newB, 0, 255)\n    handleChange(`rgba(${r}, ${g}, ${newVal}, ${a})`)\n  }\n\n  const setA = (newA: number) => {\n    const newVal = formatInputValues(newA, 0, 100)\n    handleChange(`rgba(${r}, ${g}, ${b}, ${newVal / 100})`)\n  }\n\n  const setHue = (newHue: number) => {\n    const newVal = formatInputValues(newHue, 0, 360)\n    const tinyNew = tc({ h: newVal, s: s, l: l })\n    const { r, g, b } = tinyNew.toRgb()\n    handleChange(`rgba(${r}, ${g}, ${b}, ${a})`)\n  }\n\n  const setSaturation = (newSat: number) => {\n    const newVal = formatInputValues(newSat, 0, 100)\n    const tinyNew = tc({ h: h, s: newVal / 100, l: l })\n    const { r, g, b } = tinyNew.toRgb()\n    handleChange(`rgba(${r}, ${g}, ${b}, ${a})`)\n  }\n\n  const setLightness = (newLight: number) => {\n    const newVal = formatInputValues(newLight, 0, 100)\n    const tinyNew = tc({ h: h, s: s, l: newVal / 100 })\n    if (tinyNew?.isValid()) {\n      const { r, g, b } = tinyNew.toRgb()\n      handleChange(`rgba(${r}, ${g}, ${b}, ${a})`)\n    } else {\n      console.log(\n        'The new color was invalid, perhaps the lightness you passed in was a decimal? Please pass the new value between 0 - 100'\n      )\n    }\n  }\n\n  const valueToHSL = () => {\n    return tiny.toHslString()\n  }\n\n  const valueToHSV = () => {\n    return tiny.toHsvString()\n  }\n\n  const valueToHex = () => {\n    return tiny.toHexString()\n  }\n\n  const valueToCmyk = () => {\n    const { c, m, y, k } = rgb2cmyk(r, g, b)\n    return `cmyk(${c}, ${m}, ${y}, ${k})`\n  }\n\n  const setSelectedPoint = (index: number) => {\n    if (isGradient) {\n      const newGradStr = colors?.map((cc: GradientProps, i: number) => ({\n        ...cc,\n        value: i === index ? high(cc) : low(cc),\n      }))\n      createGradientStr(newGradStr)\n    } else {\n      console.log(\n        'This function is only relevant when the picker is in gradient mode'\n      )\n    }\n  }\n\n  const addPoint = (left: number) => {\n    const newColors = [\n      ...colors.map((c: GradientProps) => ({ ...c, value: low(c) })),\n      { value: currentColor, left: left },\n    ]\n    createGradientStr(newColors)\n    if (!left) {\n      console.log(\n        'You did not pass a stop value (left amount) for the new color point so it defaulted to 50'\n      )\n    }\n  }\n\n  const deletePoint = (index: number) => {\n    if (colors?.length > 2) {\n      const pointToDelete = index ?? selectedColor\n      const remaining = colors?.filter(\n        (rc: ColorsProps, i: number) => i !== pointToDelete\n      )\n      createGradientStr(remaining)\n      if (!index) {\n        console.log(\n          'You did not pass in the index of the point you wanted to delete so the function default to the currently selected point'\n        )\n      }\n    } else {\n      console.log(\n        'A gradient must have atleast two colors, disable your delete button when necessary'\n      )\n    }\n  }\n\n  const setPointLeft = (left: number) => {\n    handleGradient(currentColor, formatInputValues(left, 0, 100))\n  }\n\n  const rgbaArr = [r, g, b, a]\n  const hslArr = [h, s, l]\n\n  return {\n    setR,\n    setG,\n    setB,\n    setA,\n    setHue,\n    addPoint,\n    setSolid,\n    setLinear,\n    setRadial,\n    valueToHSL,\n    valueToHSV,\n    valueToHex,\n    valueToCmyk,\n    setDegrees,\n    setGradient,\n    setLightness,\n    setSaturation,\n    setSelectedPoint,\n    deletePoint,\n    isGradient,\n    gradientType,\n    degrees,\n    setPointLeft,\n    currentLeft,\n    rgbaArr,\n    hslArr,\n    handleChange,\n    previousColors,\n    getGradientObject,\n    selectedPoint: selectedColor,\n  }\n}\n"
  },
  {
    "path": "src/hooks/usePaintHue.ts",
    "content": "import { useEffect, RefObject } from 'react'\nimport tinycolor from 'tinycolor2'\n\nconst usePaintHue = (\n  canvas: RefObject<HTMLCanvasElement>,\n  squareWidth: number\n) => {\n  useEffect(() => {\n    const ctx = canvas?.current?.getContext('2d', { willReadFrequently: true })\n    if (ctx) {\n      ctx.rect(0, 0, squareWidth, 14)\n\n      const gradient = ctx.createLinearGradient(0, 0, squareWidth, 0)\n      for (let i = 0; i <= 360; i += 30) {\n        gradient.addColorStop(i / 360, `hsl(${i}, 100%, 50%)`)\n      }\n      ctx.fillStyle = gradient\n      ctx.fill()\n    }\n  }, [canvas, squareWidth])\n}\n\nexport default usePaintHue\n\nexport const usePaintSat = (\n  canvas: RefObject<HTMLCanvasElement>,\n  h: number,\n  l: number,\n  squareWidth: number\n) => {\n  useEffect(() => {\n    const ctx = canvas?.current?.getContext('2d', { willReadFrequently: true })\n    if (ctx) {\n      ctx.rect(0, 0, squareWidth, 14)\n\n      const gradient = ctx.createLinearGradient(0, 0, squareWidth, 0)\n      for (let i = 0; i <= 100; i += 10) {\n        gradient.addColorStop(i / 100, `hsl(${h}, ${i}%, ${l}%)`)\n      }\n      ctx.fillStyle = gradient\n      ctx.fill()\n    }\n  }, [canvas, h, l, squareWidth])\n}\n\nexport const usePaintLight = (\n  canvas: RefObject<HTMLCanvasElement>,\n  h: number,\n  s: number,\n  squareWidth: number\n) => {\n  useEffect(() => {\n    const ctx = canvas?.current?.getContext('2d', { willReadFrequently: true })\n    if (ctx) {\n      ctx.rect(0, 0, squareWidth, 14)\n\n      const gradient = ctx.createLinearGradient(0, 0, squareWidth, 0)\n      for (let i = 0; i <= 100; i += 10) {\n        gradient.addColorStop(i / 100, `hsl(${h}, ${s}%, ${i}%)`)\n      }\n      ctx.fillStyle = gradient\n      ctx.fill()\n    }\n  }, [canvas, h, s, squareWidth])\n}\n\nexport const usePaintBright = (\n  canvas: RefObject<HTMLCanvasElement>,\n  h: number,\n  s: number,\n  squareWidth: number\n) => {\n  useEffect(() => {\n    const ctx = canvas?.current?.getContext('2d', { willReadFrequently: true })\n    if (ctx) {\n      ctx.rect(0, 0, squareWidth, 14)\n\n      const gradient = ctx.createLinearGradient(0, 0, squareWidth, 0)\n      for (let i = 0; i <= 100; i += 10) {\n        const hsl = tinycolor({ h: h, s: s, v: i })\n        gradient.addColorStop(i / 100, hsl.toHslString())\n      }\n      ctx.fillStyle = gradient\n      ctx.fill()\n    }\n  }, [canvas, h, s, squareWidth])\n}\n"
  },
  {
    "path": "src/hooks/usePaintSquare.ts",
    "content": "import { useEffect, RefObject } from 'react'\n\nconst usePaintSquare = (\n  canvas: RefObject<HTMLCanvasElement>,\n  hue: number,\n  squareWidth: number,\n  squareHeight: number\n) => {\n  useEffect(() => {\n    if (canvas.current) {\n      const ctx = canvas.current.getContext('2d', { willReadFrequently: true })\n      if (ctx) {\n        ctx.fillStyle = `hsl(${hue}, 100%, 50%)`\n        ctx.fillRect(0, 0, squareWidth, squareHeight)\n        const gradientWhite = ctx.createLinearGradient(0, 0, squareWidth, 0)\n        gradientWhite.addColorStop(0, `rgba(255, 255, 255, 1)`)\n        gradientWhite.addColorStop(1, `rgba(255, 255, 255, 0)`)\n        ctx.fillStyle = gradientWhite\n        ctx.fillRect(0, 0, squareWidth, squareHeight)\n        const gradientBlack = ctx.createLinearGradient(0, 0, 0, squareHeight)\n        gradientBlack.addColorStop(0, `rgba(0, 0, 0, 0)`)\n        gradientBlack.addColorStop(1, `rgba(0, 0, 0, 1)`)\n        ctx.fillStyle = gradientBlack\n        ctx.fillRect(0, 0, squareWidth, squareHeight)\n      }\n    }\n  }, [canvas, hue, squareWidth, squareHeight])\n}\n\nexport default usePaintSquare\n"
  },
  {
    "path": "src/index.ts",
    "content": "import { ColorPicker } from './components/index.js'\nexport { useColorPicker } from './hooks/useColorPicker.js'\n\nexport type {\n  Styles,\n  ColorsProps,\n  PassedConfig,\n  LocalesProps,\n  GradientProps,\n  ColorPickerProps,\n} from './shared/types.js'\n\nexport default ColorPicker\n"
  },
  {
    "path": "src/shared/types.ts",
    "content": "export type ColorPickerProps = {\n  idSuffix?: string\n  value?: string\n  onChange: (value: string) => void\n  hideControls?: boolean\n  hideInputs?: boolean\n  hideOpacity?: boolean\n  hidePresets?: boolean\n  hideHue?: boolean\n  presets?: string[]\n  hideEyeDrop?: boolean\n  hideAdvancedSliders?: boolean\n  hideColorGuide?: boolean\n  hideInputType?: boolean\n  hideColorTypeBtns?: boolean\n  hideGradientType?: boolean\n  hideGradientAngle?: boolean\n  hideGradientStop?: boolean\n  hideGradientControls?: boolean\n  width?: number\n  height?: number\n  style?: Styles\n  className?: any\n  locales?: LocalesProps\n  disableDarkMode?: boolean\n  disableLightMode?: boolean\n  hidePickerSquare?: boolean\n  showHexAlpha?: boolean\n  config?: PassedConfig\n}\n\nexport type ColorsProps = {\n  value: string\n  index?: number\n  left?: number\n}\n\nexport type GradientProps = {\n  value: string\n  index: number\n  left: number\n}\n\nexport type LocalesProps = {\n  CONTROLS: controlsProps\n}\n\ntype controlsProps = {\n  SOLID: string\n  GRADIENT: string\n}\n\nexport type ThemeProps = {\n  light: ThemeMode\n  dark: ThemeMode\n}\n\nexport type ThemeMode = {\n  color?: string\n  background?: string\n  highlights?: string\n  accent?: string\n}\n\nexport type Styles = Partial<{\n  body: React.CSSProperties\n  rbgcpControlBtn: React.CSSProperties\n  rbgcpControlIcon: React.CSSProperties\n  rbgcpControlIconBtn: React.CSSProperties\n  rbgcpControlBtnWrapper: React.CSSProperties\n  rbgcpColorModelDropdown: React.CSSProperties\n  rbgcpEyedropperCover: React.CSSProperties\n  rbgcpControlInput: React.CSSProperties\n  rbgcpInputLabel: React.CSSProperties\n  rbgcpInput: React.CSSProperties\n  rbgcpHandle: React.CSSProperties\n  rbgcpCanvasWrapper: React.CSSProperties\n  rbgcpCheckered: React.CSSProperties\n  rbgcpOpacityOverlay: React.CSSProperties\n  rbgcpGradientHandleWrap: React.CSSProperties\n  rbgcpGradientHandle: React.CSSProperties\n  rbgcpControlIcon2: React.CSSProperties\n  rbgcpControlBtnSelected: React.CSSProperties\n  rbgcpComparibleLabel: React.CSSProperties\n  rbgcpColorModelDropdownBtn: React.CSSProperties\n  rbgcpControlInputWrap: React.CSSProperties\n  rbgcpStopInputWrap: React.CSSProperties\n  rbgcpStopInput: React.CSSProperties\n  rbgcpDegreeInputWrap: React.CSSProperties\n  rbgcpDegreeInput: React.CSSProperties\n  rbgcpDegreeIcon: React.CSSProperties\n  rbgcpEyedropperBtn: React.CSSProperties\n  rbgcpHexInput: React.CSSProperties\n  rbgcpInputsWrap: React.CSSProperties\n}>\n\nexport type PassedConfig = {\n  barSize?: number\n  crossSize?: number\n  defaultColor?: string\n  defaultGradient?: string\n}\n\nexport type Config = {\n  barSize: number\n  crossSize: number\n  defaultColor: string\n  defaultGradient: string\n}\n"
  },
  {
    "path": "src/styles/darkStyles.ts",
    "content": "export const darkStyles: Record<string, Record<string, string | number>> = {\n    body: {\n      background: 'rgb(32, 32, 32)',\n    },\n    rbgcpInputLabel: {\n      color: 'rgb(212, 212, 212)',\n    },\n    rbgcpControlBtnWrapper: {\n      background: 'rgb(54, 54, 54)',\n    },\n    rbgcpInput: {\n      border: 'none',\n      color: 'white',\n      background: 'rgb(54, 54, 54)',\n    },\n    rbgcpControlBtn: {\n      color: 'rgb(212, 212, 212)',\n    },\n    rbgcpControlIcon: {\n      stroke: 'rgb(212, 212, 212)',\n    },\n    rbgcpControlIcon2: {\n      fill: 'rgb(212, 212, 212)',\n    },\n    rbgcpControlInput: {\n      color: 'white',\n    },\n    rbgcpControlBtnSelected: {\n      background: 'black',\n      color: '#568cf5',\n    },\n    rbgcpDegreeIcon: {\n      color: 'rgb(212, 212, 212)',\n    },\n    rbgcpColorModelDropdown: {\n      background: 'rgb(32, 32, 32)',\n    },\n    rbgcpComparibleLabel: {\n      color: 'rgb(212, 212, 212)',\n    }\n}"
  },
  {
    "path": "src/styles/styles.ts",
    "content": "import { darkStyles } from './darkStyles.js';\nimport { Styles } from '../shared/types.js';\n\nconst styles: Styles = {\n  body: {\n    boxSizing: 'border-box',\n    background: 'rgb(255, 255, 255)',\n  },\n  rbgcpControlBtn: {\n    paddingLeft: '8px',\n    paddingRight: '8px',\n    lineHeight: '1',\n    borderRadius: '4px',\n    fontWeight: 700,\n    fontSize: '12px',\n    height: '24px',\n    transition: 'all 160ms ease',\n    display: 'flex',\n    alignItems: 'center',\n    justifyContent: 'center',\n    background: 'rgba(255, 255, 255, 0)',\n    boxShadow: '1px 1px 3px rgba(0, 0, 0, 0)',\n    color: 'rgb(86, 86, 86)',\n  },\n  rbgcpControlIcon: {\n    stroke: 'rgb(50, 49, 54)',\n  },\n  rbgcpControlIconBtn: {\n    width: '30px',\n    height: '24px',\n    borderRadius: '4px',\n    display: 'flex',\n    alignItems: 'center',\n    justifyContent: 'center',\n    position: 'relative',\n  },\n  rbgcpControlBtnWrapper: {\n    height: '28px',\n    background: '#e9e9f5',\n    borderRadius: '6px',\n    padding: '2px',\n    display: 'flex',\n    justifyContent: 'center',\n    alignItems: 'center',\n    position: 'relative',\n  },\n  rbgcpColorModelDropdown: {\n    position: 'absolute',\n    right: '-2px',\n    top: '34px',\n    padding: '5px',\n    background: '#e9e9f5',\n    zIndex: 100000000,\n    borderRadius: '6px',\n    boxShadow: '1px 1px 14px 1px rgba(0, 0, 0, 0.25)',\n  },\n  rbgcpEyedropperCover: {\n    position: 'fixed',\n    left: '0px',\n    top: '0px',\n    zIndex: 100000000,\n    width: '100vw',\n    height: '100vh',\n    cursor: 'copy',\n  },\n  rbgcpControlInput: {\n    height: '24px',\n    borderRadius: '4px',\n    border: 'none',\n    outline: 'none',\n    textAlign: 'center',\n    width: '34px',\n    fontWeight: 500,\n    color: 'rgb(50, 49, 54)',\n    fontSize: '13px',\n    background: 'transparent',\n  },\n  rbgcpInputLabel: {\n    textAlign: 'center',\n    lineHeight: '1.2',\n    fontWeight: 700,\n    color: 'rgb(86, 86, 86)',\n    fontSize: '11px',\n  },\n  rbgcpInput: {\n    height: '32px',\n    borderRadius: '6px',\n    border: '1px solid #bebebe',\n    width: '100%',\n    padding: '2px',\n    outline: 'none',\n    color: 'black',\n    fontWeight: 400,\n    textAlign: 'center',\n    background: 'transparent',\n    fontSize: '15px',\n  },\n  rbgcpHandle: {\n    position: 'absolute',\n    border: '2px solid white',\n    borderRadius: '50%',\n    boxShadow: '0px 0px 3px rgba(0, 0, 0, 0.5)',\n    width: '18px',\n    height: '18px',\n    zIndex: 1000,\n    transition: 'all 30ms linear',\n    boxSizing: 'border-box',\n    willChange: 'transform',\n    outline: 'none',\n  },\n  rbgcpCanvasWrapper: {\n    borderRadius: '6px',\n    overflow: 'hidden',\n    height: '294px',\n  },\n  rbgcpCheckered: {\n    background: `linear-gradient(\n      45deg,\n      rgba(0, 0, 0, 0.18) 25%,\n      transparent 25%,\n      transparent 75%,\n      rgba(0, 0, 0, 0.18) 75%,\n      rgba(0, 0, 0, 0.18) 0\n    ),\n    linear-gradient(\n      45deg,\n      rgba(0, 0, 0, 0.18) 25%,\n      transparent 25%,\n      transparent 75%,\n      rgba(0, 0, 0, 0.18) 75%,\n      rgba(0, 0, 0, 0.18) 0\n    ),\n    white`,\n    backgroundRepeat: 'repeat, repeat',\n    backgroundPosition: '0px 0, 7px 7px',\n    transformOrigin: '0 0 0',\n    backgroundOrigin: 'padding-box, padding-box',\n    backgroundClip: 'border-box, border-box',\n    backgroundSize: '14px 14px, 14px 14px',\n    boxShadow: 'none',\n    textShadow: 'none',\n    transition: 'none',\n    transform: 'scaleX(1) scaleY(1) scaleZ(1)',\n    borderRadius: '10px',\n  },\n  rbgcpOpacityOverlay: {\n    position: 'absolute',\n    left: '0px',\n    top: '0px',\n    width: '100%',\n    height: '100%',\n    borderRadius: '10px',\n  },\n  rbgcpGradientHandleWrap: {\n    position: 'absolute',\n    zIndex: 10000,\n    top: '-2px',\n    outline: 'none',\n  },\n  rbgcpGradientHandle: {\n    border: '2px solid white',\n    borderRadius: '50%',\n    boxShadow: '0px 0px 3px rgba(0, 0, 0, 0.5)',\n    width: '18px',\n    height: '18px',\n    display: 'flex',\n    alignItems: 'center',\n    justifyContent: 'center',\n  },\n  rbgcpControlIcon2: {\n    fill: '#323136',\n  },\n  rbgcpControlBtnSelected: {\n    background: 'white',\n    color: '#568cf5',\n    boxShadow: '1px 1px 3px rgba(0, 0, 0, 0.2)',\n  },\n  rbgcpComparibleLabel: {\n    color: '#323136',\n  }\n};\n\nexport const getStyles = (isDarkMode: boolean, passedStyles: Styles) => {\n  const mergedStyles = { ...styles }\n\n  if (isDarkMode) {\n    for (const key in darkStyles) {\n      if (Object.prototype.hasOwnProperty.call(darkStyles, key)) {\n        ;(mergedStyles as Record<string, any>)[key] = {\n          ...(Object.prototype.hasOwnProperty.call(mergedStyles, key)\n            ? (mergedStyles as Record<string, any>)[key]\n            : {}),\n          ...(darkStyles as Record<string, any>)[key],\n        }\n      }\n    }\n  }\n\n  for (const key in passedStyles) {\n    if (Object.prototype.hasOwnProperty.call(passedStyles, key)) {\n      ;(mergedStyles as Record<string, any>)[key] = {\n        ...(Object.prototype.hasOwnProperty.call(mergedStyles, key)\n          ? (mergedStyles as Record<string, any>)[key]\n          : {}),\n        ...(passedStyles as Record<string, any>)[key],\n      }\n    }\n  }\n\n  return mergedStyles\n}\n\nexport const colorTypeBtnStyles = (selected: boolean, styles: Styles): React.CSSProperties => {\n  if (selected) {\n    return {...styles.rbgcpControlBtn, ...styles.rbgcpControlBtnSelected}\n  } else {\n    return { ...styles.rbgcpControlBtn }\n  }\n}\n\nexport const controlBtnStyles = (selected: boolean, styles: Styles): React.CSSProperties => {\n  if (selected) {\n    return { ...styles.rbgcpControlIconBtn, ...styles.rbgcpControlBtnSelected }\n  } else {\n    return { ...styles.rbgcpControlIconBtn }\n  }\n}\n\nexport const modalBtnStyles = (selected: boolean, styles: Styles): React.CSSProperties => {\n  if (selected) {\n    return { ...styles.rbgcpControlBtn, ...styles.rbgcpColorModelDropdownBtn, ...styles.rbgcpControlBtnSelected }\n  } else {\n    return { ...styles.rbgcpControlBtn, ...styles.rbgcpColorModelDropdownBtn }\n  }\n}"
  },
  {
    "path": "src/utils/converters.ts",
    "content": "export function rgb2cmyk(r: number, g: number, b: number) {\n  let computedC = 0\n  let computedM = 0\n  let computedY = 0\n  let computedK = 0\n\n  if (\n    r === null ||\n    g === null ||\n    b === null ||\n    isNaN(r) ||\n    isNaN(g) ||\n    isNaN(b)\n  ) {\n    console.log('Please enter numeric RGB values!')\n    return { c: 0, m: 0, k: 0, y: 1 }\n  }\n  if (r < 0 || g < 0 || b < 0 || r > 255 || g > 255 || b > 255) {\n    console.log('RGB values must be in the range 0 to 255.')\n    return { c: 0, m: 0, k: 0, y: 1 }\n  }\n\n  if (r === 0 && g === 0 && b === 0) {\n    computedK = 1\n    return { c: 0, m: 0, k: 0, y: 1 }\n  }\n\n  computedC = 1 - r / 255\n  computedM = 1 - g / 255\n  computedY = 1 - b / 255\n\n  const minCMY = Math.min(computedC, Math.min(computedM, computedY))\n  computedC = (computedC - minCMY) / (1 - minCMY)\n  computedM = (computedM - minCMY) / (1 - minCMY)\n  computedY = (computedY - minCMY) / (1 - minCMY)\n  computedK = minCMY\n\n  return { c: computedC, m: computedM, y: computedY, k: computedK }\n}\n\nexport const cmykToRgb = ({\n  c,\n  m,\n  y,\n  k,\n}: {\n  c: number\n  m: number\n  y: number\n  k: number\n}) => {\n  const r = 255 * (1 - c) * (1 - k)\n  const g = 255 * (1 - m) * (1 - k)\n  const b = 255 * (1 - y) * (1 - k)\n\n  return { r: r, g: g, b: b }\n}\n\nexport const getHexAlpha = (opacityPercent: number): string => {\n  if (typeof opacityPercent !== 'number') {\n    return 'FF'\n  }\n\n  if (opacityPercent < 0) {\n    return '00'\n  }\n\n  if (opacityPercent > 1) {\n    return 'FF'\n  }\n\n  return Math.round(opacityPercent * 255)\n    .toString(16)\n    .padStart(2, '0')\n    .toUpperCase()\n}\n"
  },
  {
    "path": "src/utils/formatters.ts",
    "content": "import { ColorsProps } from '../shared/types.js'\nimport { gradientParser } from './gradientParser.js'\n\nexport const low = (color: ColorsProps) => {\n  return color.value.toLowerCase()\n}\n\nexport const high = (color: ColorsProps) => {\n  return color.value.toUpperCase()\n}\n\nexport const getColors = (value: string, defaultColor: string, defaultGradient: string) => {\n  const isGradient = value?.includes('gradient')\n  if (isGradient) {\n    const isConic = value?.includes('conic')\n    const safeValue = !isConic ? value : defaultGradient\n    if (isConic) {\n      console.log('Sorry we cant handle conic gradients yet')\n    }\n    const obj = gradientParser(safeValue)\n    return obj?.colorStops\n  } else {\n    const safeValue = value || defaultColor\n    return [{ value: safeValue }]\n  }\n}\n\nexport const formatInputValues = (value: number, min: number, max: number) => {\n  return isNaN(value) ? min : value < min ? min : value > max ? max : value\n}\n\nexport const round = (val: number) => {\n  return Math.round(val)\n}\n"
  },
  {
    "path": "src/utils/gradientParser.ts",
    "content": "import { high, low } from './formatters.js'\nimport { isUpperCase } from './utils.js'\nimport tinycolor from 'tinycolor2'\n\nexport const gradientParser = (input = '') => {\n  const tokens = {\n    linearGradient: /^(-(webkit|o|ms|moz)-)?(linear-gradient)/i,\n    repeatingLinearGradient:\n      /^(-(webkit|o|ms|moz)-)?(repeating-linear-gradient)/i,\n    radialGradient: /^(-(webkit|o|ms|moz)-)?(radial-gradient)/i,\n    repeatingRadialGradient:\n      /^(-(webkit|o|ms|moz)-)?(repeating-radial-gradient)/i,\n    sideOrCorner:\n      /^to (left (top|bottom)|right (top|bottom)|top (left|right)|bottom (left|right)|left|right|top|bottom)/i,\n    extentKeywords:\n      /^(closest-side|closest-corner|farthest-side|farthest-corner|contain|cover)/,\n    positionKeywords: /^(left|center|right|top|bottom)/i,\n    pixelValue: /^(-?(([0-9]*\\.[0-9]+)|([0-9]+\\.?)))px/,\n    percentageValue: /^(-?(([0-9]*\\.[0-9]+)|([0-9]+\\.?)))%/,\n    emValue: /^(-?(([0-9]*\\.[0-9]+)|([0-9]+\\.?)))em/,\n    angleValue: /^(-?(([0-9]*\\.[0-9]+)|([0-9]+\\.?)))deg/,\n    startCall: /^\\(/,\n    endCall: /^\\)/,\n    comma: /^,/,\n    hexColor: /^#([0-9a-fA-F]+)/,\n    literalColor: /^([a-zA-Z]+)/,\n    rgbColor: /^rgb/i,\n    spacedRgbColor: /^(\\d{1,3})\\s+(\\d{1,3})\\s+(\\d{1,3})\\s+\\/\\s+([0-1](\\.\\d+)?)/,\n    rgbaColor: /^rgba/i,\n    hslColor: /^hsl/i,\n    hsvColor: /^hsv/i,\n    number: /^(([0-9]*\\.[0-9]+)|([0-9]+\\.?))/,\n  }\n\n  function error(msg: any) {\n    const err = new Error(input + ': ' + msg)\n    // err.source = input\n    throw err\n  }\n\n  function consume(size: any) {\n    input = input.substr(size)\n  }\n\n  function scan(regexp: any) {\n    const blankCaptures = /^[\\n\\r\\t\\s]+/.exec(input)\n    if (blankCaptures) {\n      consume(blankCaptures[0].length)\n    }\n\n    const captures = regexp.exec(input)\n    if (captures) {\n      consume(captures[0].length)\n    }\n\n    return captures\n  }\n\n  function matchListing(matcher: any) {\n    let captures = matcher()\n    const result = []\n\n    if (captures) {\n      result.push(captures)\n      while (scan(tokens.comma)) {\n        captures = matcher()\n        if (captures) {\n          result.push(captures)\n        } else {\n          error('One extra comma')\n        }\n      }\n    }\n\n    return result\n  }\n\n  function match(type: any, pattern: any, captureIndex: any) {\n    const captures = scan(pattern)\n    if (captures) {\n      return {\n        type: type,\n        value: captures[captureIndex],\n      }\n    }\n  }\n\n  function matchHexColor() {\n    const hexObj = match('hex', tokens.hexColor, 1)\n    if (hexObj?.value) {\n      const { r, g, b, a } = tinycolor(hexObj?.value).toRgb()\n      return {\n        value: `rgba(${r}, ${g}, ${b}, ${a})`,\n      }\n    }\n  }\n\n  const checkCaps = (val: any) => {\n    const capIt = isUpperCase(val?.[0])\n    return {\n      value: `${capIt ? 'RGBA' : 'rgba'}(${matchListing(matchNumber)})`,\n    }\n  }\n\n  function matchCall(pattern: any, callback: any) {\n    const captures = scan(pattern)\n\n    if (captures) {\n      if (!scan(tokens.startCall)) {\n        error('Missing (')\n      }\n\n      const result = callback(captures)\n\n      if (!scan(tokens.endCall)) {\n        error('Missing )')\n      }\n\n      return result\n    }\n  }\n\n  function matchHSLColor() {\n    return matchCall(tokens.hslColor, convertHsl)\n  }\n\n  function matchRGBAColor() {\n    return matchCall(tokens.rgbaColor, checkCaps)\n  }\n\n  function matchRGBColor() {\n    return matchCall(tokens.rgbColor, convertRgb)\n  }\n\n  function matchLiteralColor() {\n    const litObj = match('literal', tokens.literalColor, 0)\n    if (litObj?.value) {\n      const { r, g, b, a } = tinycolor(litObj?.value).toRgb()\n      return {\n        value: `rgba(${r}, ${g}, ${b}, ${a})`,\n      }\n    }\n  }\n\n  function matchHSVColor() {\n    return matchCall(tokens.hsvColor, convertHsv)\n  }\n\n  function matchColor() {\n    return (\n      matchHexColor() ||\n      matchHSLColor() ||\n      matchRGBAColor() ||\n      matchRGBColor() ||\n      matchLiteralColor() ||\n      matchHSVColor()\n    )\n  }\n\n  function matchColorStop() {\n    const color = matchColor()\n\n    if (!color) {\n      error('Expected color definition')\n    }\n\n    color.left = parseInt(matchDistance()?.value)\n    return color\n  }\n\n  function matchGradient(\n    gradientType: any,\n    pattern: any,\n    orientationMatcher: any\n  ) {\n    return matchCall(pattern, function () {\n      const orientation = orientationMatcher()\n      if (orientation) {\n        if (!scan(tokens.comma)) {\n          error('Missing comma before color stops')\n        }\n      }\n\n      return {\n        type: gradientType,\n        orientation: orientation,\n        colorStops: matchListing(matchColorStop),\n      }\n    })\n  }\n\n  function matchLinearOrientation() {\n    return matchSideOrCorner() || matchAngle()\n  }\n\n  function matchDefinition() {\n    return (\n      matchGradient(\n        'linear-gradient',\n        tokens.linearGradient,\n        matchLinearOrientation\n      ) ||\n      matchGradient(\n        'repeating-linear-gradient',\n        tokens.repeatingLinearGradient,\n        matchLinearOrientation\n      ) ||\n      matchGradient(\n        'radial-gradient',\n        tokens.radialGradient,\n        matchListRadialOrientations\n      ) ||\n      matchGradient(\n        'repeating-radial-gradient',\n        tokens.repeatingRadialGradient,\n        matchListRadialOrientations\n      )\n    )\n  }\n\n  function matchListDefinitions() {\n    return matchListing(matchDefinition)\n  }\n\n  function getAST() {\n    const ast = matchListDefinitions()\n\n    if (input.length > 0) {\n      error('Invalid input not EOF')\n    }\n\n    const ast0 = ast[0]\n    const checkSelected = ast0?.colorStops?.filter((c: any) =>\n      isUpperCase(c.value)\n    ).length\n\n    const getGradientObj = () => {\n      if (checkSelected > 0) {\n        return ast0\n      } else {\n        const val = (c: any, i: number) => (i === 0 ? high(c) : low(c))\n        return {\n          ...ast0,\n          colorStops: ast0.colorStops.map((c: any, i: number) => ({\n            ...c,\n            value: val(c, i),\n          })),\n        }\n      }\n    }\n\n    return getGradientObj()\n  }\n\n  function matchSideOrCorner() {\n    return match('directional', tokens.sideOrCorner, 1)\n  }\n\n  function matchAngle() {\n    return match('angular', tokens.angleValue, 1)\n  }\n\n  function matchListRadialOrientations() {\n    var radialOrientations,\n      radialOrientation = matchRadialOrientation(),\n      lookaheadCache\n\n    if (radialOrientation) {\n      radialOrientations = []\n      radialOrientations.push(radialOrientation)\n\n      lookaheadCache = input\n      if (scan(tokens.comma)) {\n        radialOrientation = matchRadialOrientation()\n        if (radialOrientation) {\n          radialOrientations.push(radialOrientation)\n        } else {\n          input = lookaheadCache\n        }\n      }\n    }\n\n    return radialOrientations\n  }\n\n  function matchRadialOrientation() {\n    let radialType = matchCircle() || matchEllipse()\n\n    if (radialType) {\n      // @ts-expect-error - need to circle back for these types\n      radialType.at = matchAtPosition()\n    } else {\n      const extent = matchExtentKeyword()\n      if (extent) {\n        radialType = extent\n        const positionAt = matchAtPosition()\n        if (positionAt) {\n          // @ts-expect-error - need to circle back for these types\n          radialType.at = positionAt\n        }\n      } else {\n        const defaultPosition = matchPositioning()\n        if (defaultPosition) {\n          radialType = {\n            type: 'default-radial',\n            // @ts-expect-error - need to circle back for these types\n            at: defaultPosition,\n          }\n        }\n      }\n    }\n\n    return radialType\n  }\n\n  function matchLength() {\n    return match('px', tokens.pixelValue, 1) || match('em', tokens.emValue, 1)\n  }\n\n  function matchCircle() {\n    const circle = match('shape', /^(circle)/i, 0)\n\n    if (circle) {\n      // @ts-expect-error - need to circle back for these types\n      circle.style = matchLength() || matchExtentKeyword()\n    }\n\n    return circle\n  }\n\n  function matchEllipse() {\n    const ellipse = match('shape', /^(ellipse)/i, 0)\n\n    if (ellipse) {\n      // @ts-expect-error - need to circle back for these types\n      ellipse.style = matchDistance() || matchExtentKeyword()\n    }\n\n    return ellipse\n  }\n\n  function matchExtentKeyword() {\n    return match('extent-keyword', tokens.extentKeywords, 1)\n  }\n\n  function matchAtPosition() {\n    if (match('position', /^at/, 0)) {\n      const positioning = matchPositioning()\n\n      if (!positioning) {\n        error('Missing positioning value')\n      }\n\n      return positioning\n    }\n  }\n\n  function matchPositioning() {\n    const location = matchCoordinates()\n\n    if (location.x || location.y) {\n      return {\n        type: 'position',\n        value: location,\n      }\n    }\n  }\n\n  function matchCoordinates() {\n    return {\n      x: matchDistance(),\n      y: matchDistance(),\n    }\n  }\n\n  function matchNumber() {\n    return scan(tokens.number)[1]\n  }\n\n  const convertHsl = (val: any) => {\n    const capIt = isUpperCase(val?.[0])\n    const hsl = matchListing(matchNumber)\n    const { r, g, b, a } = tinycolor({\n      h: hsl[0],\n      s: hsl[1],\n      l: hsl[2],\n      a: hsl[3] || 1,\n    }).toRgb()\n    return {\n      value: `${capIt ? 'RGBA' : 'rgba'}(${r}, ${g}, ${b}, ${a})`,\n    }\n  }\n\n  const convertHsv = (val: any) => {\n    const capIt = isUpperCase(val?.[0])\n    const hsv = matchListing(matchNumber)\n    const { r, g, b, a } = tinycolor({\n      h: hsv[0],\n      s: hsv[1],\n      v: hsv[2],\n      a: hsv[3] || 1,\n    }).toRgb()\n    return {\n      value: `${capIt ? 'RGBA' : 'rgba'}(${r}, ${g}, ${b}, ${a})`,\n    }\n  }\n\n  const convertRgb = (val: any) => {\n    const capIt = isUpperCase(val?.[0])\n    const captures = scan(tokens.spacedRgbColor)\n    const [, r, g, b, a = 1] = captures || [null, ...matchListing(matchNumber)]\n    return {\n      value: `${capIt ? 'RGBA' : 'rgba'}(${r}, ${g}, ${b}, ${a})`,\n    }\n  }\n\n  function matchDistance() {\n    return (\n      match('%', tokens.percentageValue, 1) ||\n      matchPositionKeyword() ||\n      matchLength()\n    )\n  }\n\n  function matchPositionKeyword() {\n    return match('position-keyword', tokens.positionKeywords, 1)\n  }\n\n  return getAST()\n}\n"
  },
  {
    "path": "src/utils/utils.ts",
    "content": "import { formatInputValues } from './formatters.js'\nimport { ColorsProps } from '../shared/types.js'\n\nexport const safeBounds = (e: any) => {\n  const client = e.target.parentNode.getBoundingClientRect()\n  const className = e.target.className\n  const adjuster = className === 'c-resize ps-rl' ? 15 : 0\n  return {\n    offsetLeft: client?.x + adjuster,\n    offsetTop: client?.y,\n    clientWidth: client?.width,\n    clientHeight: client?.height,\n  }\n}\n\nexport function getHandleValue(e: any, barSize: number) {\n  const { offsetLeft, clientWidth } = safeBounds(e)\n  const pos = e.clientX - offsetLeft - barSize / 2\n  const adjuster = clientWidth - 18\n  const bounded = formatInputValues(pos, 0, adjuster)\n  return Math.round(bounded / (adjuster / 100))\n}\n\nexport function computeSquareXY(\n  s: number,\n  v: number,\n  squareWidth: number,\n  squareHeight: number,\n  crossSize: number\n) {\n  const x = s * squareWidth - crossSize / 2\n  const y = ((100 - v) / 100) * squareHeight - crossSize / 2\n  return [x, y]\n}\n\nconst getClientXY = (e: any) => {\n  if (e.clientX) {\n    return { clientX: e.clientX, clientY: e.clientY }\n  } else {\n    const touch = e.touches[0] || {}\n    return { clientX: touch.clientX, clientY: touch.clientY }\n  }\n}\n\nexport function computePickerPosition(e: any, crossSize: number) {\n  const { offsetLeft, offsetTop, clientWidth, clientHeight } = safeBounds(e)\n  const { clientX, clientY } = getClientXY(e)\n\n  const getX = () => {\n    const xPos = clientX - offsetLeft - crossSize / 2\n    return formatInputValues(xPos, -9, clientWidth - 10)\n  }\n  const getY = () => {\n    const yPos = clientY - offsetTop - crossSize / 2\n    return formatInputValues(yPos, -9, clientHeight - 10)\n  }\n\n  return [getX(), getY()]\n}\n\n// export const getGradientType = (value: string) => {\n//   return value?.split('(')[0]\n// }\n\nexport const isUpperCase = (str: string) => {\n  return str?.[0] === str?.[0]?.toUpperCase()\n}\n\n// export const compareGradients = (g1: string, g2: string) => {\n//   const ng1 = g1?.toLowerCase()?.replaceAll(' ', '')\n//   const ng2 = g2?.toLowerCase()?.replaceAll(' ', '')\n//   if (ng1 === ng2) {\n//     return true\n//   } else {\n//     return false\n//   }\n// }\n\nconst convertShortHandDeg = (dir: any) => {\n  if (dir === 'to top') {\n    return 0\n  } else if (dir === 'to bottom') {\n    return 180\n  } else if (dir === 'to left') {\n    return 270\n  } else if (dir === 'to right') {\n    return 90\n  } else if (dir === 'to top right') {\n    return 45\n  } else if (dir === 'to bottom right') {\n    return 135\n  } else if (dir === 'to bottom left') {\n    return 225\n  } else if (dir === 'to top left') {\n    return 315\n  } else {\n    const safeDir = dir || 0\n    return parseInt(safeDir)\n  }\n}\n\nexport const objectToString = (value: any) => {\n  if (typeof value === 'string') {\n    return value\n  } else {\n    if (value?.type?.includes('gradient')) {\n      const sorted = value?.colorStops?.sort(\n        (a: any, b: any) => a?.left - b?.left\n      )\n      const string = sorted\n        ?.map((c: any) => `${c?.value} ${c?.left}%`)\n        ?.join(', ')\n      const type = value?.type\n      const degs = convertShortHandDeg(value?.orientation?.value)\n      const gradientStr = type === 'linear-gradient' ? `${degs}deg` : 'circle'\n      return `${type}(${gradientStr}, ${string})`\n    } else {\n      const color = value?.colorStops[0]?.value || 'rgba(175, 51, 242, 1)'\n      return color\n    }\n  }\n}\n\nexport const getColorObj = (colors: ColorsProps[], defaultGradient: string) => {\n  const idxCols = colors?.map((c: ColorsProps, i: number) => ({\n    ...c,\n    index: i,\n  }))\n\n  const upperObj = idxCols?.find((c: ColorsProps) => isUpperCase(c.value))\n  const ccObj = upperObj || idxCols[0]\n\n  return {\n    currentColor: ccObj?.value || defaultGradient,\n    selectedColor: ccObj?.index || 0,\n    currentLeft: ccObj?.left || 0,\n  }\n}\n\nconst getDegrees = (value: string) => {\n  const s1 = value?.split(',')[0]\n  const s2 = s1?.split('(')[1]?.replace('deg', '')\n  return convertShortHandDeg(s2)\n}\n\nexport const getDetails = (value: string) => {\n  const isGradient = value?.includes('gradient')\n  const gradientType = value?.split('(')[0]\n  const degrees = getDegrees(value)\n  const degreeStr =\n    gradientType === 'linear-gradient' ? `${degrees}deg` : 'circle'\n\n  return {\n    degrees,\n    degreeStr,\n    isGradient,\n    gradientType,\n  }\n}\n"
  },
  {
    "path": "test/gradientParser.spec.js",
    "content": "import { gradientParser } from '../src/utils/gradientParser'\nimport { describe, expect, it } from 'vitest'\n\ndescribe('gradientParser', () => {\n  it('should parse linear gradient with hex colors', () => {\n    const gradient = 'linear-gradient(45deg, #012345 0%, #6789AB 100%)'\n\n    expect(gradientParser(gradient)).toEqual({\n      colorStops: [\n        { left: 0, type: 'hex', value: '012345' },\n        { left: 100, type: 'hex', value: '6789AB' },\n      ],\n      orientation: { type: 'angular', value: '45' },\n      type: 'linear-gradient',\n    })\n  })\n\n  it('should parse linear gradient with comma-separated rgba colors', () => {\n    const gradient =\n      'linear-gradient(45deg, rgba(1, 2, 3, 0.123) 0%, rgba(4, 5, 6, 0.456) 100%)'\n\n    expect(gradientParser(gradient)).toEqual({\n      colorStops: [\n        { left: 0, value: 'RGBA(1,2,3,0.123)' },\n        { left: 100, value: 'rgba(4,5,6,0.456)' },\n      ],\n      orientation: { type: 'angular', value: '45' },\n      type: 'linear-gradient',\n    })\n  })\n\n  it('should parse linear gradient with rgb + alpha colors', () => {\n    const gradient =\n      'linear-gradient(45deg, rgb(1 2 3 / 0.123) 0%, rgb(4 5 6 / 0.456) 100%)'\n\n    expect(gradientParser(gradient)).toEqual({\n      colorStops: [\n        { left: 0, value: 'RGBA(1, 2, 3, 0.123)' },\n        { left: 100, value: 'rgba(4, 5, 6, 0.456)' },\n      ],\n      orientation: { type: 'angular', value: '45' },\n      type: 'linear-gradient',\n    })\n  })\n\n  it('should parse linear gradient with comma-separated rgb colors', () => {\n    const gradient =\n      'linear-gradient(45deg, rgb(1, 2, 3) 0%, rgb(4, 5, 6) 100%)'\n\n    expect(gradientParser(gradient)).toEqual({\n      colorStops: [\n        { left: 0, value: 'RGBA(1, 2, 3, 1)' },\n        { left: 100, value: 'rgba(4, 5, 6, 1)' },\n      ],\n      orientation: { type: 'angular', value: '45' },\n      type: 'linear-gradient',\n    })\n  })\n\n  // TODO: Figure out pattern of \"HSV\" colors\n\n  // TODO: Make it accept percentages\n  // it('should parse linear gradient with hsl colors', () => {\n  //   const gradient = 'linear-gradient(45deg, hsl(0, 100%, 50%) 0%, hsl(100, 50%, 85%) 100%)'\n  //\n  //   expect(gradientParser(gradient)).toEqual({})\n  // })\n})\n"
  },
  {
    "path": "test/utils.spec.js",
    "content": "import {\n  isUpperCase,\n  getNewHsl,\n  getGradientType,\n  getDegrees,\n} from '../src/utils/utils'\nimport { describe, expect, it } from 'vitest'\n\ndescribe('isUpperCase', () => {\n  it('should return true when the first letter of the string is upper-cased', () => {\n    expect(isUpperCase('Aloha oe')).toBe(true)\n  })\n\n  it('should return false when the first letter of the string is lower-cased', () => {\n    expect(isUpperCase('aLOHA OE')).toBe(false)\n  })\n})\n\ndescribe('getNewHsl', () => {\n  it('should return correct RGBA color for given HSL color', () => {\n    const callback = () => {}\n    const output = getNewHsl(116, 79, 19, 0.5, callback)\n\n    expect(output).toEqual('rgba(15, 87, 10, 0.5)')\n  })\n\n  it('should trigger callback with correct arguments', () => {\n    const callback = jest.fn()\n    getNewHsl(116, 79, 19, 0.5, callback)\n\n    expect(callback).toHaveBeenCalledWith(116)\n  })\n})\n\ndescribe('getGradientType', () => {\n  it('should pick the correct prefix of gradient values', () => {\n    const assertionMap = [\n      ['linear-gradient(30deg, #6789AB 0%, #012345 100%)', 'linear-gradient'],\n      [\n        '-webkit-linear-gradient(30deg, #6789AB 0%, #012345 100%)',\n        '-webkit-linear-gradient',\n      ],\n      [\n        '-o-linear-gradient(30deg, #6789AB 0%, #012345 100%)',\n        '-o-linear-gradient',\n      ],\n      [\n        '-ms-linear-gradient(30deg, #6789AB 0%, #012345 100%)',\n        '-ms-linear-gradient',\n      ],\n      [\n        '-moz-linear-gradient(30deg, #6789AB 0%, #012345 100%)',\n        '-moz-linear-gradient',\n      ],\n\n      [\n        'repeating-linear-gradient(30deg, #6789AB 0%, #012345 100%)',\n        'repeating-linear-gradient',\n      ],\n      [\n        '-webkit-repeating-linear-gradient(30deg, #6789AB 0%, #012345 100%)',\n        '-webkit-repeating-linear-gradient',\n      ],\n      [\n        '-o-repeating-linear-gradient(30deg, #6789AB 0%, #012345 100%)',\n        '-o-repeating-linear-gradient',\n      ],\n      [\n        '-ms-repeating-linear-gradient(30deg, #6789AB 0%, #012345 100%)',\n        '-ms-repeating-linear-gradient',\n      ],\n      [\n        '-moz-repeating-linear-gradient(30deg, #6789AB 0%, #012345 100%)',\n        '-moz-repeating-linear-gradient',\n      ],\n\n      ['radial-gradient(#6789AB 0%, #012345 100%)', 'radial-gradient'],\n      [\n        '-webkit-radial-gradient(#6789AB 0%, #012345 100%)',\n        '-webkit-radial-gradient',\n      ],\n      ['-o-radial-gradient(#6789AB 0%, #012345 100%)', '-o-radial-gradient'],\n      ['-ms-radial-gradient(#6789AB 0%, #012345 100%)', '-ms-radial-gradient'],\n      [\n        '-moz-radial-gradient(#6789AB 0%, #012345 100%)',\n        '-moz-radial-gradient',\n      ],\n\n      [\n        'repeating-radial-gradient(#6789AB 0%, #012345 100%)',\n        'repeating-radial-gradient',\n      ],\n      [\n        '-webkit-repeating-radial-gradient(#6789AB 0%, #012345 100%)',\n        '-webkit-repeating-radial-gradient',\n      ],\n      [\n        '-o-repeating-radial-gradient(#6789AB 0%, #012345 100%)',\n        '-o-repeating-radial-gradient',\n      ],\n      [\n        '-ms-repeating-radial-gradient(#6789AB 0%, #012345 100%)',\n        '-ms-repeating-radial-gradient',\n      ],\n      [\n        '-moz-repeating-radial-gradient(#6789AB 0%, #012345 100%)',\n        '-moz-repeating-radial-gradient',\n      ],\n    ]\n    const outputs = []\n    const expected = []\n\n    assertionMap.forEach(([value, expectedValue]) => {\n      outputs.push(getGradientType(value))\n      expected.push(expectedValue)\n    })\n\n    expect(outputs).toEqual(expected)\n  })\n})\n\ndescribe('getDegrees', () => {\n  it('should pick the correct degree from linear gradient values', () => {\n    const assertionMap = [\n      ['linear-gradient(0deg, #6789AB 0%, #012345 100%)', 0],\n      ['-webkit-linear-gradient(1deg, #6789AB 0%, #012345 100%)', 1],\n      ['-o-linear-gradient(2deg, #6789AB 0%, #012345 100%)', 2],\n      ['-ms-linear-gradient(3deg, #6789AB 0%, #012345 100%)', 3],\n      ['-moz-linear-gradient(4deg, #6789AB 0%, #012345 100%)', 4],\n\n      ['repeating-linear-gradient(5deg, #6789AB 0%, #012345 100%)', 5],\n      ['-webkit-repeating-linear-gradient(6deg, #6789AB 0%, #012345 100%)', 6],\n      ['-o-repeating-linear-gradient(7deg, #6789AB 0%, #012345 100%)', 7],\n      ['-ms-repeating-linear-gradient(8deg, #6789AB 0%, #012345 100%)', 8],\n      ['-moz-repeating-linear-gradient(9deg, #6789AB 0%, #012345 100%)', 9],\n    ]\n    const outputs = []\n    const expected = []\n\n    assertionMap.forEach(([value, expectedValue]) => {\n      outputs.push(getDegrees(value))\n      expected.push(expectedValue)\n    })\n\n    expect(outputs).toEqual(expected)\n  })\n})\n"
  },
  {
    "path": "tsconfig.build.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"noEmit\": false,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\"\n  },\n  \"include\": [\"src\"],\n  \"exclude\": [\"src/**/*.spec.ts\", \"src/**/*.spec.tsx\"]\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"declaration\": true,\n    \"esModuleInterop\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"react\",\n    \"module\": \"nodenext\",\n    \"noEmit\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"outDir\": \"dist\",\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"es5\",\n    \"lib\": [\"es2015\", \"dom\"],\n    \"downlevelIteration\": false,\n  },\n  \"exclude\": [\"dist\"]\n}\n\n\n"
  }
]