[
  {
    "path": ".editorconfig",
    "content": "# http://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_size = 2\nend_of_line = lf\nindent_style = space\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.md]\nmax_line_length = 0\ntrim_trailing_whitespace = false\n\n[COMMIT_EDITMSG]\nmax_line_length = 0"
  },
  {
    "path": ".eslintignore",
    "content": "dist\nnode_modules"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"parser\": \"@typescript-eslint/parser\",\n  \"extends\": [\n    \"plugin:react-hooks/recommended\",\n    \"plugin:@typescript-eslint/recommended\"\n  ],\n  \"plugins\": [\n    \"react\",\n    \"@typescript-eslint\",\n    \"prettier\"\n  ],\n  \"env\": {\n    \"browser\": true,\n    \"node\": true,\n    \"es6\": true,\n    \"jest\": true\n  },\n  \"rules\": {\n    \"react/prop-types\": 0,\n    \"react-hooks/rules-of-hooks\": \"error\",\n    \"react-hooks/exhaustive-deps\": \"warn\",\n    \"semi\": \"off\",\n    \"sort-keys\": \"off\",\n    \"global-require\": \"off\",\n    \"spaced-comment\": \"off\",\n    \"capitalized-comments\": \"off\",\n    \"padding-line-between-statements\": \"off\",\n    \"@typescript-eslint/no-var-requires\": 0,\n    \"@typescript-eslint/no-explicit-any\": 0,\n    \"@typescript-eslint/no-inferrable-types\": 0,\n    \"@typescript-eslint/no-non-null-assertion\": 0\n  },\n  \"parserOptions\": {\n    \"sourceType\": \"module\",\n    \"ecmaVersion\": 2020,\n    \"ecmaFeatures\": {\n      \"jsx\": true\n    }\n  },\n  \"settings\": {\n    \"react\": {\n      \"version\": \"detect\"\n    }\n  }\n}"
  },
  {
    "path": ".github/workflows/chromatic.yml",
    "content": "# .github/workflows/chromatic.yml\n\n# Workflow name\nname: 'Chromatic'\n\n# Event for the workflow\non: push\n\njobs:\n  chromatic-deployment:\n    # Operating System\n    runs-on: ubuntu-latest\n    # Job steps\n    steps:\n        # 👇 Adds Chromatic as a step in the workflow\n      - name: Publish to Chromatic\n        uses: chromaui/action@v1\n        # Options required to the GitHub chromatic action\n        with:\n          # 👇 Chromatic projectToken, refer to the manage page to obtain it.\n          projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}\n          autoAcceptChanges: true"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# Editor directories and files\n.idea\n.vs\n.vscode\n.cache\n\n# dependencies\nnode_modules\n\n# testing\ncoverage\n\n# production\nlib\ndist\nbuild\n\n# Log files\nlogs\n*.log\n\n# misc\n.DS_Store\nstorybook-static\npackage-lock.json\nchromatic-diagnostics.json"
  },
  {
    "path": ".npmrc",
    "content": "registry = \"https://registry.npmjs.com/\"\n"
  },
  {
    "path": ".prettierignore",
    "content": "dist\nnode_modules"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"bracketSpacing\": false,\n  \"printWidth\": 100,\n  \"trailingComma\": \"es5\",\n  \"tabWidth\": 2,\n  \"singleQuote\": true,\n  \"endOfLine\": \"auto\"\n}"
  },
  {
    "path": ".storybook/global-style/global-style.ts",
    "content": "import { createGlobalStyle } from 'styled-components';\nimport ReactToastifyOverride from './react-toastify-override';\n\nconst GlobalStyle = createGlobalStyle`\n  *,\n  *::before,\n  *::after {\n    box-sizing: border-box;\n  }\n\n  html {\n    line-height: 1.15;\n    text-size-adjust: 100%;\n    -moz-text-size-adjust: 100%;\n    -webkit-text-size-adjust: 100%;\n    text-rendering: optimizeLegibility;\n    -moz-osx-font-smoothing: grayscale;\n    -webkit-font-smoothing: antialiased;\n    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n  }\n\n  body {\n    flex: 1;\n    margin: 0;\n    display: flex;\n    color: #262626;\n    font-size: 1rem;\n    font-weight: 400;\n    text-align: left;\n    line-height: 1.5;\n    min-height: 120vh;\n    flex-direction: column;\n    background-color: #fff;\n    padding: 1rem 0 !important;\n    font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;\n  }\n\n  em {\n    font-weight: 600;\n  }\n\n  strong {\n    font-weight: 600;\n    font-size: 1.025em;\n  }\n\n  code {\n    font-size: 90%;\n    color: #476582;\n    line-height: 1.7;\n    border-radius: 4px;\n    padding: .175em .475em;\n    word-break: break-word;\n    background-color: #f1f1f1;\n    font-family: Consolas, Monaco, \"Andale Mono\", \"Ubuntu Mono\", monospace;\n  }\n\n  ${ReactToastifyOverride}\n`;\n\nexport default GlobalStyle;"
  },
  {
    "path": ".storybook/global-style/index.ts",
    "content": "export { default as GlobalStyle } from './global-style';"
  },
  {
    "path": ".storybook/global-style/react-toastify-override.ts",
    "content": "import { css, keyframes } from 'styled-components';\n\nconst TOASTIFY_BOUNCE_OUT = keyframes`\n  20% {\n    transform: scale3d(0.9, 0.9, 0.9);\n  } 50%,\n    55% {\n    opacity: 1;\n    transform: scale3d(1.1, 1.1, 1.1);\n  } to {\n    opacity: 0;\n    transform: scale3d(0.3, 0.3, 0.3);\n  }\n`;\n\nconst TOASTIFY_BOUNCE_IN = keyframes`\n  from,\n  20%,\n  40%,\n  60%,\n  80%,\n  to {\n    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);\n  }\n\n  0% {\n    opacity: 0;\n    transform: scale3d(0.3, 0.3, 0.3);\n  } 20% {\n    transform: scale3d(1.1, 1.1, 1.1);\n  } 40% {\n    transform: scale3d(0.9, 0.9, 0.9);\n  } 60% {\n    opacity: 1;\n    transform: scale3d(1.03, 1.03, 1.03);\n  } 80% {\n    transform: scale3d(0.97, 0.97, 0.97);\n  } to {\n    opacity: 1;\n    transform: scale3d(1, 1, 1);\n  }\n`;\n\nexport default css`\n  .Toastify__animate__bounceIn {\n    animation: ${TOASTIFY_BOUNCE_IN} 1s both;\n  }\n\n  .Toastify__animate__bounceOut {\n    animation: ${TOASTIFY_BOUNCE_OUT} 0.85s both;\n  }\n\n  .Toastify__toast-container {\n    .Toastify__toast {\n      background: #292d3e;\n\n      &-body {\n        color: #C3C9E6;\n      }\n\n      &-icon > svg {\n        fill: #85ADFF;\n      }\n    }\n\n    .Toastify__close-button {\n      color: #fff;\n    }\n\n    .Toastify__progress-bar {\n      background-color: #85ADFF;\n    }\n  }\n`;"
  },
  {
    "path": ".storybook/main.ts",
    "content": "import type { StorybookConfig } from '@storybook/react/types';\n\nconst config: StorybookConfig = {\n  framework: '@storybook/react',\n  addons: ['@storybook/addon-storysource'],\n  stories: ['../__stories__/**/*.stories.@(js|tsx|mdx)'],\n  core: {\n    builder: 'webpack5',\n    disableTelemetry: true,\n    enableCrashReports: false\n  }\n};\n\nexport default config;"
  },
  {
    "path": ".storybook/manager-head.html",
    "content": "<style lang=\"css\">\n  #panel-tab-content {\n    background: #292d3e !important;\n  }\n\n  #storybook-panel-root .os-content > pre {\n    line-height: 20px;\n  }\n\n  #storybook-panel-root .os-content > pre > div {\n    tab-size: 4;\n    hyphens: none;\n    text-align: left;\n    overflow-wrap: normal;\n    font-size: 14px;\n    line-height: 20px;\n    font-weight: 400;\n    white-space: pre;\n    word-spacing: normal;\n    word-break: normal;\n  }\n\n  #storybook-panel-root .os-content > pre > div > span > div {\n    background: inherit !important;\n    border-radius: inherit !important;\n  }\n\n  #storybook-panel-root .os-content > pre > div,\n  #storybook-panel-root .os-content > pre > div > span > span,\n  #storybook-panel-root .os-content > pre > div > span > a,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.script,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.parameter {\n    color: #A9AFD0;\n  }\n\n  #storybook-panel-root .os-content > pre > div,\n  #storybook-panel-root .os-content > pre > div:last-of-type .comment,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token {\n    font-family: Consolas, Monaco, \"Andale Mono\", \"Ubuntu Mono\", monospace;\n  }\n\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.comment,\n  #storybook-panel-root .os-content > pre > div:last-of-type .comment.linenumber {\n    color: #6C739A;\n  }\n\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.tag {\n    color:#F07178;\n  }\n\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.keyword.module,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.entity,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.url,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.operator,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.variable,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.script.operator,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.punctuation,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.attr-value.punctuation,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.script.punctuation {\n    color: #89DDFF;\n  }\n\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.arrow.operator,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.keyword,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.selector,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.builtin,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.keyword.nil,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.keyword.null,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.important,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.atrule,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.attr-name,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.tag.attr-name {\n    color: #c792ea;\n  }\n\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.property,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.deleted,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.function-name,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.number,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.constant,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.symbol {\n    color: #F78C6C;\n  }\n\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.boolean {\n    color: #FF9CAC;\n  }\n\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.class-name,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.atrule,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.namespace,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.maybe-class-name,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.known-class-name,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.hexdiv {\n    color: #FFCB6B;\n  }\n\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.function {\n    color: #85ADFF;\n  }\n\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.string,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.char,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.regex,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.attr-value,\n  #storybook-panel-root .os-content > pre > div:last-of-type .token.unit {\n    color: #C3E88D;\n  }\n</style>"
  },
  {
    "path": ".storybook/manager.ts",
    "content": "import { addons } from '@storybook/addons';\nimport { create } from '@storybook/theming';\n\nconst theme = create({\n  base: 'light',\n  appBg: '#E6E6E6',\n  barBg: '#E0E0E0',\n  barTextColor: '#7F7F7F',\n  colorSecondary: '#1ea7fd',\n  appBorderColor: '#D3D3D3',\n  brandUrl: 'https://master--625676b6922472003af898b4.chromatic.com'\n});\n\naddons.setConfig({\n  theme,\n  showNav: true,\n  showPanel: true\n});\n"
  },
  {
    "path": ".storybook/preview.tsx",
    "content": "import React, { Fragment } from 'react';\nimport { GlobalStyle } from './global-style';\nimport type { DecoratorFn } from '@storybook/react';\n\n// import react-toastify CSS files (overrides in react-toastify-override.ts)\nimport 'react-toastify/dist/ReactToastify.css';\n\nconst withGlobalStyle: DecoratorFn = (Story) => (\n  <Fragment>\n    <GlobalStyle />\n    <Story />\n  </Fragment>\n);\n\nexport const decorators = [withGlobalStyle];"
  },
  {
    "path": ".test/custom-test-env.ts",
    "content": "import Environment from 'jest-environment-jsdom';\n\n/**\n * A custom environment to set the TextEncoder that is required by react-dom/server\n */\nmodule.exports = class CustomTestEnvironment extends Environment {\n  async setup() {\n    await super.setup();\n    if (typeof this.global.TextEncoder === 'undefined') {\n      const { TextEncoder } = await import('util');\n      this.global.TextEncoder = TextEncoder;\n    }\n  }\n}"
  },
  {
    "path": ".test/setup-tests.ts",
    "content": "import '@testing-library/jest-dom';\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n  - node"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Matthew Areddia\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](https://img.shields.io/npm/v/react-functional-select.svg?style=flat-square)](https://www.npmjs.com/package/react-functional-select)\n[![npm downloads](https://img.shields.io/npm/dt/react-functional-select.svg?style=flat-square)](https://www.npmjs.com/package/react-functional-select)\n[![Issues](https://img.shields.io/github/issues/based-ghost/react-functional-select.svg?style=flat-square)](https://github.com/based-ghost/react-functional-select/issues)\n[![License](https://img.shields.io/badge/license-mit-red.svg?style=flat-square)](LICENSE)\n[![style: styled-components](https://img.shields.io/badge/style-%F0%9F%92%85%20styled--components-orange.svg?style=flat-square)](https://github.com/styled-components/styled-components)\n\n# react-functional-select\n\n> Micro-sized & micro-optimized select component for React.js\n\nSee the accompanying [Interactive Storybook UI Site](https://master--625676b6922472003af898b4.chromatic.com) for live demos and detailed docs.\n\n<strong>Key features:</strong>\n\n- Extremely lightweight: ~6 kB (gzipped)!\n- Advanced features like async mode, portal support, animations, and option virtualization\n- Fully-featured & customizable: API comparable to [`react-select`](https://github.com/JedWatson/react-select)\n- Engineered for ultimate performance: effortlessly scroll, filter, and key through datasets numbering in the tens of thousands using [`react-window`](https://github.com/bvaughn/react-window) + performance-first code. [Demo of handling 50,000 options here!](https://master--625676b6922472003af898b4.chromatic.com/?path=/story/react-functional-select-demos--virtualization)\n- Extensible styling API with [`styled-components`](https://github.com/styled-components/styled-components)\n- Accessible\n\n<strong>Peer dependencies:</strong>\n\n- [`styled-components`](https://github.com/styled-components/styled-components) for dynamic styling/theming via CSS-in-JS\n- [`react-window`](https://github.com/bvaughn/react-window) for integrated menu option data virtualization\n\n## Overview\n\nEssentially, this is a focused subset of [`react-select`](https://github.com/JedWatson/react-select)'s API that is engineered for ultimate performance and minimal bundle size. It is built entirely with the `React Hooks` API (no legacy class components). The primary design principal revolves around weighing the cost/benefits of adding a feature against the impact to performance and/or number of lines of code its addition would have.\n\nAny expected features not in the current API is likely due to the reason that such features would have added significant overhead to the package. In addition, if we expose the right public methods and/or callback properties, this feature should be trivial to add to wrapping components - proper decoupling and abstraction of code is key to keeping such channels open for similar customizations that can be kept out of this package. Please, feel free to offer enhancement ideas with/without technical solutions.\n\n## Installation\n\n```\n$ npm i react-window styled-components react-functional-select\n$ yarn add react-window styled-components react-functional-select\n```\n\n> <strong><em>Note that you need to be on a react version that supports hooks (>= 16.8.6)</em></strong>\n\n## Usage\n\n- [Demo](https://master--625676b6922472003af898b4.chromatic.com)\n- [Stories source code](./__stories__)\n\n```jsx\nimport { Select } from 'react-functional-select';\nimport React, { useState, useEffect, useCallback, type ComponentProps } from 'react';\nimport { Card, CardHeader, CardBody, Container, SelectContainer } from '../shared/components';\n\ntype SelectProps = ComponentProps<typeof Select>;\n\ntype Option = Readonly<{\n  id: number;\n  city: string;\n  state: string;\n}>;\n\nconst CITY_OPTIONS: Option[] = [\n  { id: 1, city: 'Austin', state: 'TX' },\n  { id: 2, city: 'Denver', state: 'CO' },\n  { id: 3, city: 'Chicago', state: 'IL' },\n  { id: 4, city: 'Phoenix', state: 'AZ' },\n  { id: 5, city: 'Houston', state: 'TX' }\n];\n\nconst SingleSelect: React.FC<SelectProps> = ({ isDisabled }) => {\n  const [isInvalid, setIsInvalid] = useState<boolean>(false);\n  const [selectedOption, setSelectedOption] = useState<Option | null>(null);\n\n  const getOptionValue = useCallback((opt: Option): number => opt.id, []);\n  const onOptionChange = useCallback((opt: Option | null): void => setSelectedOption(opt), []);\n  const getOptionLabel = useCallback((opt: Option): string => `${opt.city}, ${opt.state}`, []);\n\n  useEffect(() => {\n    if (isDisabled) {\n      setIsInvalid(false);\n    }\n  }, [isDisabled]);\n\n  return (\n    <Container>\n      <Card>\n        <CardHeader>\n          {`Selected Option: ${JSON.stringify(selectedOption || {})}`}\n        </CardHeader>\n        <CardBody>\n          <SelectContainer>\n            <Select\n              isClearable\n              isInvalid={isInvalid}\n              options={CITY_OPTIONS}\n              isDisabled={isDisabled}\n              onOptionChange={onOptionChange}\n              getOptionValue={getOptionValue}\n              getOptionLabel={getOptionLabel}\n            />\n          </SelectContainer>\n        </CardBody>\n      </Card>\n    </Container>\n  );\n};\n\nexport default SingleSelect;\n```\n\n## Properties\n\nAll properties are technically optional (with a few having default values). Very similar to [`react-select`](https://github.com/JedWatson/react-select)'s API.\n\n> <strong><em>Note that the following non-primitive properties should be properly memoized if defined:</em></strong><br>`clearIcon`, `caretIcon`, `options`, `renderOptionLabel`, `onMenuOpen`, `onOptionChange`, `onKeyDown`, `getOptionLabel`, `getOptionLabel`, `getOptionValue`, `onInputBlur`, `onInputFocus`, `onInputChange`, `onSearchChange`, `getIsOptionDisabled`, `getFilterOptionString`, `themeConfig`\n\n| Property | Type | Default | Description\n:---|:---|:---|:---\n| `inputId`| string | `undefined` | The id of the autosize search input control\n|`selectId`| string | `undefined` | The id of the parent select container element\n|`menuId`| string | `undefined` | The id of the menu container element\n|`ariaLabel`| string | `undefined` | Aria label (for assistive tech)\n|`isMulti`| bool | `false` | Does the control allow for multiple selections (defaults to single-value mode)\n|`async`| bool | `false` | Is the component in 'async' mode - when in 'async' mode, updates to the input search value will NOT cause the effect `useMenuOptions` to execute (this effect parses `options` into stateful value `menuOptions`)\n|`autoFocus`| bool | `false` | Focus the control following initial mount of component\n|`lazyLoadMenu`| bool | `false` | If `true`, the menu (wrapper & virtualized list components) will rendered in DOM only when `menuOpen` state is `true`\n|`isLoading`| bool | `false` | Is the select in a state of loading - shows loading dots animation\n|`isInvalid`| bool | `false` | Is the current value invalid - control recieves invalid styling\n|`inputDelay`| number | `undefined` | The debounce delay in for the input search (milliseconds)\n|`pageSize`| number | `5` | Number of options to jump in menu when page{up|down} keys are used\n|`isDisabled`| bool | `false` | Is the select control disabled - recieves disabled styling\n|`required`| bool | `false` | Is the select control required - applied to the `input` element. When `true`, the optionally specified CSS from the `themeConfig.input.cssRequired` field will be applied to the `input` element.\n|`placeholder`| string | `Select option..` | Placeholder text for the select value\n|`menuWidth`| string | number | `100%` | Width of the menu\n|`menuItemSize`| number | `35` | The height of each option in the menu (px)\n|`isClearable`| bool | `false` | Is the select value clearable\n|`noOptionsMsg`| string | `No options` | The text displayed in the menu when there are no options available (to hide menu when search returns no items, set to `null` or `''`)\n|`loadingMsg`| string | `Loading..` | The text displayed in the menu when `isLoading` === `true`\n|`clearIcon`| ReactNode OR ((state: any) => ReactNode) | `undefined` | Custom clear icon node - `state` forwarded to a function is `{ menuOpen, isLoading, isInvalid, isDisabled }`\n|`caretIcon`| ReactNode OR ((state: any) => ReactNode) | `undefined` | Custom caret icon node - `state` forwarded to a function is `{ menuOpen, isLoading, isInvalid, isDisabled }`\n|`loadingNode`| ReactNode | `undefined` | Custom loading node\n|`options`| array | `[]` | The menu options\n|`isSearchable`| bool | `true` | Whether to enable search functionality or not\n|`hideSelectedOptions`| bool | `false` | Hide the selected option from the menu (if undefined and isMulti = true, then defaults to true)\n|`openMenuOnClick`| bool | `true` | If true, the menu can be toggled by clicking anywhere on the select control; if false, the menu can only be toggled by clicking the 'caret' icon on the far right of the control\n|`menuMaxHeight`| number | `300` | Max height of the menu element - this effects how many options `react-window` will render\n|`menuOverscanCount`| number | `1` | correlates to `react-window` property `overscanCount`: The number of items (options) to render outside of the visible area. Increasing the number can impact performance, but is useful if the option label is complex and the `renderOptionLabel` prop is defined\n|`itemKeySelector`| string | number | `undefined` | If defined, will use the property in your original options as each option's key, rather than the parsed stateful value `menuOptions` index (this needs to be a unique property - so properties such as `id` or `value`). This relates to the `itemKey` property in dependency `react-window` - [more info here](https://react-window.now.sh/#/api/FixedSizeList)\n|`menuScrollDuration`| number | `300` | Duration of scroll menu into view animation\n|`menuItemDirection`| 'ltr' OR 'rtl' | `'ltr'` | The direction of text for each menu option and position of the menu's scroll bar (`react-window`'s `direction` prop)\n|`ariaLabelledBy`| string | `undefined` | HTML ID of an element that should be used as the label (for assistive tech)\n|`ariaLive`| 'off' OR 'polite' OR 'assertive' | `'polite'` | Used to set the priority with which screen reader should treat updates to live regions (translates to `aria-live` attribute)\n|`openMenuOnFocus`| bool | `false` | Open the menu when the select control recieves focus\n|`initialValue`| any | `undefined` | Initial select value\n|`tabSelectsOption`| bool | `true` | Select the currently focused option when the user presses tab\n|`blurInputOnSelect`| bool | `false` | Remove focus from the input when the user selects an option (useful for dismissing the keyboard on touch devices)\n|`closeMenuOnSelect`| bool | `true` | Close the select menu when the user selects an option\n|`isAriaLiveEnabled`| bool | `false` | Enables visually hidden div that reports stateful information (for assistive tech)\n|`scrollMenuIntoView`| bool | `true` | Performs animated scroll to show menu in view when menu is opened (if there is room to do so)\n|`backspaceClearsValue`| bool | `true` | Remove the currently focused option when the user presses backspace\n|`filterMatchFrom`| 'any' OR 'start' | `'any'` | Position in stringified option to match search input\n|`menuPosition`| 'top' OR 'auto' OR 'bottom' | `'bottom'` | Determines where menu will be placed in relation to the control - `'auto'` will first check if menu has space to open below the control, otherwise it will open above the control.\n|`filterIgnoreCase`| bool | `true` | Search input ignores case of characters when comparing\n|`filterIgnoreAccents`| bool | `false` | Search input will strip diacritics from string before comparing\n|`onMenuOpen`| (...args: any[]) => void | `undefined` | Callback function executed after the menu is opened\n|`onMenuClose`| (...args: any[]) => void | `undefined` | Callback function executed after the menu is closed\n|`onOptionChange`| (data: any) => void | `undefined` | Callback function executed after a new option is selected\n|`onKeyDown`| (e: KeyboardEvent, input?: string, focusedOption?: FocusedOption) => void | `undefined` | Callback function executed `onKeyDown` event\n|`getOptionLabel`| (data: any) => string | number | `undefined` | Resolves option data to string | number to be displayed as the label by components (by default will use option.label)\n|`getOptionValue`| (data: any) => string | number | `undefined` | Resolves option data to string | number to compare option values (by default will use option.value)\n|`onInputBlur`| (e: FocusEvent) => void | `undefined` | Handle blur events on the search input\n|`onInputFocus`| (e: FocusEvent) => void | `undefined` | Handle focus events on the search input\n|`onInputChange`| (value: string) => void | `undefined` | Handle change events on the search input\n|`onSearchChange`| (value: string) => void | `undefined` | Callback executed after the debounced search input value is persisted to the component's state - if no debounce is defined via the `inputDelay` property, it probably makes more sense to use `onInputChange` instead.\n|`renderOptionLabel`| (data: any) => ReactNode | `undefined` | Formats option labels in the menu and control as JSX.Elements or React Components (by default will use `getOptionLabel`)\n|`renderMultiOptions`| (params: any) => ReactNode | `undefined` | Allows for customization as to how multi-select options should be formatted. The `MultiParams` contains the array of selected options `{ selected: Array<{ data: any, value: string | number, label: string | number}>, renderOptionLabel: (data: any): ReactNode }`. Left and right arrow key navigation will also be disabled when this property is defined.\n|`getIsOptionDisabled`| (data: any) => boolean | `undefined` | When defined will evaluate each option to determine whether it is disabled or not (if not specified, each option will be evaluated as to whether or not it contains a property of `isDisabled` with a value of `true`)\n|`getFilterOptionString`| (option: any) => string | `undefined` | When defined will take each option and generate a string used in the filtering process (by default, will use option.label)\n|`themeConfig`| Partial\\<DefaultTheme\\> | `undefined` | Object that takes specified property key-value pairs and merges them into the theme object\n|`menuPortalTarget`| Element | `undefined` | Whether the menu should use a portal, and where it should attach\n|`memoOptions`| bool | `false` | Whether to memoize each `Option` component\n\n## Inspiration\n\nThis project was inspired by [`react-select`](https://github.com/JedWatson/react-select).\n\n## License\n\nMIT licensed. Copyright (c) [Matt Areddia](https://github.com/based-ghost) 2022.\n"
  },
  {
    "path": "__stories__/helpers/components/Checkbox.tsx",
    "content": "import React from 'react';\nimport { hexToRgba } from '../utils';\nimport styled, { css } from 'styled-components';\n\ntype CheckboxProps = Readonly<{\n  label?: string;\n  checked: boolean;\n  readOnly?: boolean;\n  onCheck: (checked: boolean) => void;\n}>;\n\nconst CHECK_COLOR = '#149DF3';\nconst CHECK_BORDER_COLOR = hexToRgba(CHECK_COLOR, 0.83);\n\nconst Label = styled.span`\n  user-select: none;\n  margin-left: 1.4rem;\n`;\n\nconst Input = styled.input`\n  z-index: 3;\n  opacity: 0;\n  width: 1em;\n  height: 1em;\n  cursor: pointer;\n  position: absolute;\n\n  :checked ~ i {\n    border-color: ${CHECK_BORDER_COLOR};\n\n    :after,\n    :before {\n      opacity: 1;\n      transition: height 0.34s ease;\n    }\n\n    :after {\n      height: 0.5rem;\n    }\n\n    :before {\n      height: 1.16rem;\n      transition-delay: 0.11s;\n    }\n  }\n`;\n\nconst CheckboxWrapper = styled.label<{ isReadOnly?: boolean }>`\n  user-select: none;\n  position: relative;\n  margin-top: 0.5rem;\n  align-items: center;\n  display: inline-flex;\n\n  ${({ isReadOnly }) =>\n    isReadOnly\n    && css`\n      cursor: default;\n      pointer-events: none;\n\n      > i {\n        opacity: 0.5;\n      }\n    `}\n`;\n\nconst CheckIcon = styled.i`\n  z-index: 0;\n  width: 1rem;\n  height: 1rem;\n  position: absolute;\n  border-style: solid;\n  border-width: 1.5px;\n  box-sizing: border-box;\n  border-radius: 0.0625rem;\n  background-color: transparent;\n  border-color: rgba(0, 0, 0, 0.5);\n  transition: border-color 0.34s ease;\n\n  :after,\n  :before {\n    height: 0;\n    opacity: 0;\n    content: \"\";\n    width: 0.2rem;\n    display: block;\n    position: absolute;\n    border-radius: 3px;\n    transform-origin: left top;\n    background-color: ${CHECK_COLOR};\n    transition: opacity 0.34s ease, height 0s linear 0.34s;\n  }\n\n  :after {\n    top: 0.33rem;\n    left: 0.01rem;\n    transform: rotate(-45deg);\n  }\n\n  :before {\n    top: 0.68rem;\n    left: 0.39rem;\n    transform: rotate(-135deg);\n  }\n`;\n\nconst Checkbox: React.FC<CheckboxProps> = ({\n  label,\n  onCheck,\n  checked,\n  readOnly\n}) => (\n  <CheckboxWrapper isReadOnly={readOnly}>\n    <Input\n      type='checkbox'\n      checked={checked}\n      onChange={(e) => onCheck(e.target.checked)}\n    />\n    <CheckIcon />\n    {label && <Label>{label}</Label>}\n  </CheckboxWrapper>\n);\n\nexport default Checkbox;"
  },
  {
    "path": "__stories__/helpers/components/CodeMarkup.tsx",
    "content": "import React, { memo } from 'react';\nimport styled from 'styled-components';\nimport { MEDIA_QUERY_IS_MOBILE } from '../styled';\nimport { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter';\n\n// Register light build of react-syntax-highlighter and register only what is needed\nconst dark = require('react-syntax-highlighter/dist/esm/styles/prism/dark').default;\nconst markup = require('react-syntax-highlighter/dist/esm/languages/prism/markup').default;\nconst javascript = require('react-syntax-highlighter/dist/esm/languages/prism/javascript').default;\n\nSyntaxHighlighter.registerLanguage('markup', markup);\nSyntaxHighlighter.registerLanguage('javascript', javascript);\n\ntype CodeMarkupProps = Readonly<{\n  data: any;\n  header: string;\n  language: string;\n  formatFn?: (data: any) => string;\n}>;\n\nconst CodeMarkupContainer = styled.div`\n  overflow: hidden;\n  border-radius: 5px;\n  margin: 1rem 1.5rem;\n  background: #292d3e;\n  ${MEDIA_QUERY_IS_MOBILE} {\n    margin: 1rem 0;\n  }\n`;\n\nconst Header = styled.div`\n  font-size: 14px;\n  padding: 0 .9rem;\n  font-weight: 700;\n  line-height: 2.95;\n  letter-spacing: 0.05em;\n  text-transform: uppercase;\n  color: rgba(235, 235, 235, 0.45);\n  background-color: rgba(0, 0, 0, 0.2);\n`;\n\nconst PreContainer = styled.div`\n  width: 100%;\n  height: 100%;\n  overflow: auto;\n  border-radius: 0;\n  min-height: 365px !important;\n  max-height: 365px !important;\n\n  pre {\n    line-height: 20px;\n    margin: 1rem !important;\n\n    > code {\n      padding: 0;\n      color: #A9AFD0 !important;\n      font-size: 14px !important;\n      font-weight: 400 !important;\n      line-height: 20px !important;\n      text-shadow: none !important;\n\n      .boolean {\n        color: #FF9CAC;\n      }\n\n      .function {\n        color: #85ADFF;\n      }\n\n      .tag,\n      .property {\n        color: #F07178;\n      }\n\n      .number,\n      .attr-name {\n        color: #c792ea;\n      }\n\n      .string,\n      .tag.attr-value {\n        color: #C3E88D;\n      }\n\n      .operator,\n      .token.punctuation,\n      .tag.punctuation,\n      .tag.attr-value.punctuation {\n        color: #89DDFF;\n      }\n    }\n  }\n`;\n\nconst CodeMarkup = memo<CodeMarkupProps>(({\n  data,\n  header,\n  language,\n  formatFn\n}) => (\n  <CodeMarkupContainer>\n    <Header>{header}</Header>\n    <PreContainer>\n      <SyntaxHighlighter\n        wrapLines\n        style={dark}\n        language={language}\n        useInlineStyles={false}\n      >\n        {formatFn ? formatFn(data) : data}\n      </SyntaxHighlighter>\n    </PreContainer>\n  </CodeMarkupContainer>\n));\n\nCodeMarkup.displayName = 'CodeMarkup';\n\nexport default CodeMarkup;"
  },
  {
    "path": "__stories__/helpers/components/OptionsCountButton.tsx",
    "content": "import React from 'react';\nimport { Button } from '../styled';\nimport { numberWithCommas } from '../utils';\nimport styled, { css } from 'styled-components';\n\ntype OptionsCountButtonProps = Readonly<{\n  count: number;\n  optionsCount: number;\n  setOptionsCount: (count: number) => void;\n}>;\n\nconst StyledButton = styled(Button)<{ isActive?: boolean }>`\n  width: 6rem;\n  transition: none;\n  min-width: 6rem !important;\n\n  ${({ isActive }) =>\n    isActive\n    && css`\n      color: #fff;\n      background-color: #149DF3;\n\n      :hover {\n        background-color: #0A93E9;\n      }\n    `}\n\n  :focus {\n    color: #fff !important;\n    background-color: #149DF3 !important;\n  }\n`;\n\nconst OptionsCountButton: React.FC<OptionsCountButtonProps> = ({\n  count,\n  optionsCount,\n  setOptionsCount\n}) => {\n  const isActive = count === optionsCount;\n  const onClick = () => !isActive && setOptionsCount(count);\n\n  return (\n    <StyledButton\n      onClick={onClick}\n      isActive={isActive}\n    >\n      {numberWithCommas(count)}\n    </StyledButton>\n  );\n};\n\nexport default OptionsCountButton;"
  },
  {
    "path": "__stories__/helpers/components/PackageLink.tsx",
    "content": "import React from 'react';\nimport styled from 'styled-components';\n\ntype PackageLinkProps = Readonly<{\n  name: string;\n  href: string;\n}>;\n\nconst Link = styled.a`\n  color: #1EA7FD;\n  cursor: pointer;\n  font-weight: 600;\n  line-height: 1.5;\n  text-decoration: none;\n\n  :hover {\n    text-decoration: underline;\n  }\n`;\n\nconst PackageLink: React.FC<PackageLinkProps> = ({ name, href }) => (\n  <Link\n    href={href}\n    target='_blank'\n    aria-label={name}\n    rel='noopener noreferrer'\n  >\n    {name}\n  </Link>\n);\n\nexport default PackageLink;"
  },
  {
    "path": "__stories__/helpers/components/index.ts",
    "content": "export { default as Checkbox } from './Checkbox';\nexport { default as CodeMarkup } from './CodeMarkup';\nexport { default as PackageLink } from './PackageLink';\nexport { default as OptionsCountButton } from './OptionsCountButton';"
  },
  {
    "path": "__stories__/helpers/constants/index.ts",
    "content": "export * from './theme';\nexport * from './markup';\nexport * from './svg-props';\nexport * from './npm-package';\nexport * from './options-data';\nexport * from './react-toastify';"
  },
  {
    "path": "__stories__/helpers/constants/markup.ts",
    "content": "import {\n  OPTION_CLS,\n  OPTION_FOCUSED_CLS,\n  OPTION_DISABLED_CLS,\n  OPTION_SELECTED_CLS,\n  CARET_ICON_CLS,\n  CLEAR_ICON_CLS,\n  AUTOSIZE_INPUT_CLS,\n  MENU_CONTAINER_CLS,\n  SELECT_CONTAINER_CLS,\n  CONTROL_CONTAINER_CLS,\n  PLACEHOLDER_DEFAULT\n} from '../../../src/constants';\n\nexport const CLASS_NAME_HTML =\n  `<div class=\"${SELECT_CONTAINER_CLS}\">\n  <div class=\"${CONTROL_CONTAINER_CLS}\">\n    <div>\n      <div>${PLACEHOLDER_DEFAULT}</div>\n      <div>\n        <input\n          value=\"\"\n          type=\"text\"\n          class=\"${AUTOSIZE_INPUT_CLS}\"\n        />\n        ::after\n      </div>\n    </div>\n    <div>\n      <div>\n        <svg\n          aria-hidden=\"true\"\n          viewBox=\"0 0 14 16\"\n          class=\"${CLEAR_ICON_CLS}\"\n        >\n          <path\n            fillRule=\"evenodd\"\n            d=\"M7.71 8.23l3.75 3.75-1.48...\"\n          />\n        </svg>\n      </div>\n      <span></span>\n      <div>\n        <div\n          aria-hidden=\"true\"\n          class=\"${CARET_ICON_CLS}\"\n        />\n      </div>\n    </div>\n  </div>\n  <div class=\"${MENU_CONTAINER_CLS}\">\n    <div>\n      <div>\n        <div class=\"${OPTION_CLS}\">\n          Option 1\n        </div>\n        <div class=\"${OPTION_CLS} ${OPTION_FOCUSED_CLS}\">\n          Option 2\n        </div>\n        <div class=\"${OPTION_CLS} ${OPTION_SELECTED_CLS}\">\n          Option 3\n        </div>\n        <div class=\"${OPTION_CLS} ${OPTION_DISABLED_CLS}\">\n          Option 4\n        </div>\n      </div>\n    </div>\n  </div>\n</div>`;"
  },
  {
    "path": "__stories__/helpers/constants/npm-package.ts",
    "content": "export const STYLED_COMPONENTS_PACKAGE = {\n  name: 'styled-components',\n  href: 'https://www.styled-components.com'\n} as const;\n\nexport const REACT_WINDOW_PACKAGE = {\n  name: 'react-window',\n  href: 'https://github.com/bvaughn/react-window'\n} as const;"
  },
  {
    "path": "__stories__/helpers/constants/options-data.ts",
    "content": "import type { CityOption, PackageOption } from '../../types';\n\nexport const PACKAGE_OPTIONS: PackageOption[] = [\n  { id: 1, name: 'react' },\n  { id: 2, name: 'react-dom' },\n  { id: 3, name: 'reactstrap' },\n  { id: 4, name: 'react-scripts' },\n  { id: 5, name: 'react-window' }\n];\n\nexport const CITY_OPTIONS: CityOption[] = [\n  { id: 1, city: 'Boston', state: 'MA' },\n  { id: 2, city: 'Austin', state: 'TX' },\n  { id: 3, city: 'Denver', state: 'CO' },\n  { id: 4, city: 'Chicago', state: 'IL' },\n  { id: 5, city: 'Phoenix', state: 'AZ' },\n  { id: 6, city: 'Houston', state: 'TX' },\n  { id: 7, city: 'Orlando', state: 'FL' },\n  { id: 8, city: 'Portland', state: 'OR' },\n  { id: 9, city: 'Milwaukee', state: 'WI' },\n  { id: 10, city: 'Louisville', state: 'KY' }\n];"
  },
  {
    "path": "__stories__/helpers/constants/react-toastify.ts",
    "content": "import { cssTransition, type ToastContainerProps } from 'react-toastify';\n\n// CSS transition config => 'transition' property\nconst transition = cssTransition({\n  enter: 'Toastify__animate__bounceIn',\n  exit: 'Toastify__animate__bounceOut'\n});\n\n// ToastContainerProps passed to the toast.configure() method\nexport const TOAST_CONTAINER_PROPS: ToastContainerProps = {\n  transition,\n  autoClose: 2500,\n  draggable: false,\n  newestOnTop: true,\n  position: 'top-right'\n} as const;"
  },
  {
    "path": "__stories__/helpers/constants/svg-props.ts",
    "content": "import type { SVGProps } from 'react';\n\nexport const CHEVRON_SVG_PROPS = {\n  'aria-hidden': true,\n  viewBox: '0 0 448 512'\n} as const;\n\nexport const CHEVRON_DOWN_PATH_PROPS: SVGProps<SVGPathElement> = {\n  d: 'M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z'\n} as const;\n\nexport const REACT_SVG_PROPS = {\n  'aria-hidden': true,\n  viewBox: '0 0 841.9 595.3'\n} as const;\n\nexport const REACT_SVG_PATH_PROPS: SVGProps<SVGPathElement> = {\n  d: 'M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z'\n} as const;\n\nexport const REACT_SVG_CIRCLE_PROPS: SVGProps<SVGCircleElement> = {\n  r: '45.7',\n  cx: '420.9',\n  cy: '296.5'\n} as const;"
  },
  {
    "path": "__stories__/helpers/constants/theme.ts",
    "content": "import type { Theme } from '../../../src';\nimport { createThemeOptions } from '../utils';\nimport { mergeDeep } from '../../../src/utils';\nimport { DEFAULT_THEME } from '../../../src/constants';\n\n// Normalize animation props as be default they are type of styled-component's \"FlattenSimpleInterpolation\"\nconst FADE_IN_KEYFRAMES_STR = 'FADE_IN_KEYFRAMES 0.25s ease-in-out';\nconst BOUNCE_KEYFRAMES_STR = 'BOUNCE_KEYFRAMES 1.19s ease-in-out infinite';\n\nconst THEME_ANIMATIONS: Theme = {\n  loader: {\n    animation: BOUNCE_KEYFRAMES_STR\n  },\n  menu: {\n    animation: FADE_IN_KEYFRAMES_STR\n  },\n  multiValue: {\n    animation: FADE_IN_KEYFRAMES_STR\n  },\n  icon: {\n    clear: {\n      animation: FADE_IN_KEYFRAMES_STR\n    }\n  },\n} as const;\n\nexport const ThemeEnum = {\n  DEFAULT: 'Default',\n  LARGE_TEXT: 'Large text',\n  DARK_COLORS: 'Dark colors',\n  ZERO_BORDER_RADIUS: 'No border-radius'\n} as const;\n\nexport const THEME_CONFIG: Theme = {\n  menu: {\n    option: {\n      selectedColor: '#515151',\n      focusedBgColor: '#F5F5F5',\n      selectedBgColor: '#F5F5F5'\n    }\n  }\n} as const;\n\nexport const ThemeConfigMap: Theme = {\n  [ThemeEnum.DEFAULT]: undefined as any,\n  [ThemeEnum.DARK_COLORS]: {\n    color: {\n      border: '#A8AEB4',\n      primary: '#555555'\n    },\n    select: {\n      css: 'color: #000;'\n    },\n    control: {\n      boxShadowColor: 'rgba(85, 85, 85, 0.25)',\n      focusedBorderColor: 'rgba(85, 85, 85, 0.75)'\n    },\n    icon: {\n      color: '#A6A6A6'\n    },\n    menu: {\n      option: {\n        selectedColor: '#fff',\n        selectedBgColor: '#555555',\n        focusedBgColor: 'rgba(85, 85, 85, 0.225)'\n      }\n    }\n  },\n  [ThemeEnum.LARGE_TEXT]: {\n    select: {\n      css: 'font-size: 1.25rem;'\n    }\n  },\n  [ThemeEnum.ZERO_BORDER_RADIUS]: {\n    control: {\n      borderRadius: '0'\n    },\n    menu: {\n      borderRadius: '0'\n    }\n  }\n} as const;\n\nexport const THEME_OPTIONS = createThemeOptions(ThemeEnum);\nexport const THEME_DEFAULTS = mergeDeep(DEFAULT_THEME, THEME_ANIMATIONS);"
  },
  {
    "path": "__stories__/helpers/hooks/index.ts",
    "content": "export { default as useCallbackState } from './useCallbackState';"
  },
  {
    "path": "__stories__/helpers/hooks/useCallbackState.ts",
    "content": "import { useCallback, useState } from 'react';\n\nconst useCallbackState = <T>(initState: T): [T, (newState: T) => void] => {\n  const [state, setState] = useState<T>(initState);\n  const setStateCallback = useCallback((newState: T): void => setState(newState), []);\n  return [state, setStateCallback];\n};\n\nexport default useCallbackState;"
  },
  {
    "path": "__stories__/helpers/index.ts",
    "content": "export * from './utils';\nexport * from './hooks';\nexport * from './styled';\nexport * from './constants';\nexport * from './components';"
  },
  {
    "path": "__stories__/helpers/styled/index.ts",
    "content": "import styled, { css, keyframes } from 'styled-components';\n\nexport const MEDIA_QUERY_IS_MOBILE = '@media only screen and (max-width: 768px)';\nexport const MEDIA_QUERY_IS_MOBILE_XS = '@media only screen and (max-width: 525px)';\nexport const MEDIA_QUERY_IS_TABLET_OR_DESKTOP = '@media only screen and (min-width: 992px)';\nexport const MEDIA_QUERY_IS_TABLET = '@media only screen and (max-width: 991px) and (min-width: 769px)';\n\n// Need to implement a div version of Paragraph since PrettyPrintJson contains an <pre> element\n// ...which cannot be a child of a <p> element\nconst PARAGRAPH_BASE_STYLE = css`\n  margin-top: 0;\n  display: block;\n  margin-bottom: 1rem;\n  margin-block-end: 1em;\n  margin-inline-end: 0px;\n  margin-block-start: 1em;\n  margin-inline-start: 0px;\n`;\n\nexport const Content = styled.p`\n  ${PARAGRAPH_BASE_STYLE}\n`;\n\nexport const Paragraph = styled.p`\n  ${PARAGRAPH_BASE_STYLE}\n\n  ${MEDIA_QUERY_IS_TABLET_OR_DESKTOP} {\n    max-width: 85%;\n  }\n`;\n\nexport const Container = styled.div`\n  width: 100%;\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n  padding: 0.25rem 1.75rem;\n\n  ${MEDIA_QUERY_IS_MOBILE} {\n    font-size: 0.96em;\n    padding: 0.25rem 1.25rem;\n  }\n`;\n\nexport const SelectContainer = styled.div`\n  width: 60%;\n  margin-top: 1rem;\n\n  ${MEDIA_QUERY_IS_TABLET} {\n    width: 75%;\n  }\n\n  ${MEDIA_QUERY_IS_MOBILE} {\n    width: 100%;\n  }\n`;\n\nexport const Hr = styled.hr`\n  border: 0;\n  margin-top: 1rem;\n  margin-bottom: 1rem;\n  padding-bottom: .225rem;\n  border-top: 1px solid #ddd;\n`;\n\nexport const Columns = styled.div`\n  width: 100%;\n\n  ${MEDIA_QUERY_IS_TABLET_OR_DESKTOP} {\n    display: flex;\n  }\n`;\n\nexport const Column = styled.div<{widthPercent?: number}>`\n  flex-grow: 1;\n  flex-basis: 0;\n  flex-shrink: 1;\n  display: block;\n  padding: 0.25rem;\n\n  ${MEDIA_QUERY_IS_MOBILE} {\n    padding: 0.25rem 0;\n    width: 100% !important;\n  }\n\n  ${({widthPercent}) =>\n    widthPercent &&\n    css`\n      flex: none;\n      width: ${widthPercent}%;\n    `}\n`;\n\nexport const ListWrapper = styled.div`\n  ${PARAGRAPH_BASE_STYLE}\n\n  ${MEDIA_QUERY_IS_TABLET_OR_DESKTOP} {\n    max-width: 85%;\n  }\n\n  &.is-class-list {\n    max-width: 100% !important;\n\n    ul {\n      li + li {\n        margin-top: 0.55em !important;\n      }\n    }\n  }\n`;\n\nexport const List = styled.ul`\n  display: block;\n  padding-left: 1.75rem;\n  margin-block-end: 1em;\n  list-style-type: disc;\n  margin-inline-end: 0px;\n  margin-block-start: 1em;\n  margin-inline-start: 0px;\n  padding-inline-start: 20px;\n\n  li + li {\n    margin-top: 0.6em;\n  }\n`;\n\nexport const Li = styled.li`\n  display: list-item;\n  text-align: match-parent;\n`;\n\nexport const TextHeader = styled.span`\n  color: #476582;\n  font-size: 90%;\n  line-height: 1.7;\n  border-radius: 4px;\n  padding: .175em .475em;\n  word-break: break-word;\n  background-color: #f1f1f1;\n  font-family: Consolas, Monaco, \"Andale Mono\", \"Ubuntu Mono\", monospace;\n`;\n\nexport const Title = styled.h2`\n  font-size: 2rem;\n  font-weight: 700;\n  line-height: 1.167;\n  margin-top: 0.5rem;\n  margin-bottom: .5rem;\n`;\n\nexport const SubTitle = styled.h4`\n  font-weight: 700;\n  line-height: 1.167;\n  font-size: 1.65rem;\n  margin-top: 1.25rem;\n  margin-bottom: 0.5rem;\n  letter-spacing: 0.00735em;\n`;\n\nexport const Button = styled.button`\n  border: 0;\n  color: #262626;\n  cursor: pointer;\n  font-size: 1rem;\n  font-weight: 500;\n  line-height: 1.5;\n  overflow: visible;\n  user-select: none;\n  text-align: center;\n  border-radius: 3px;\n  display: inline-block;\n  vertical-align: middle;\n  background-color: #eaebec;\n  padding: 0.375rem 0.75rem;\n  -webkit-appearance: button;\n  transition: color 0.2s ease-out, background-color 0.2s ease-out;\n\n  :focus {\n    outline: 0;\n  }\n\n  :hover, :focus {\n    background-color: #DDDEDF;\n  }\n\n  ${MEDIA_QUERY_IS_MOBILE} {\n    display: block;\n    width: 100% !important;\n  }\n\n  ${MEDIA_QUERY_IS_MOBILE_XS} {\n    font-size: 0.9em;\n  }\n`;\n\nexport const Buttons = styled.div`\n  > button {\n    min-width: 6.25rem;\n    margin-top: 0.5rem;\n\n    :not(:last-of-type) {\n      margin-right: 0.5rem;\n    }\n  }\n`;\n\nexport const Label = styled.label`\n  width: 100%;\n  font-weight: 600;\n  text-align: left;\n  user-select: none;\n  display: inline-block;\n  vertical-align: middle;\n  color: rgba(0, 0, 0, 0.45);\n  margin: 0.5rem auto 0.25rem 0;\n\n  ${MEDIA_QUERY_IS_MOBILE} {\n    margin: 0 auto 0.15rem 0;\n  }\n`;\n\nexport const Checkboxes = styled.div`\n  font-size: 1rem;\n\n  > label {\n    margin-top: 0.5rem;\n    margin-bottom: 0.5rem;\n\n    :not(:last-of-type) {\n      margin-right: 1.35rem;\n    }\n  }\n\n  ${MEDIA_QUERY_IS_MOBILE} {\n    text-align: left;\n\n    > label {\n      width: 100%;\n      margin-left: auto;\n      margin-top: 0.425rem;\n      margin-bottom: 0.425rem;\n    }\n  }\n`;\n\nexport const Card = styled.div`\n  min-width: 0;\n  display: flex;\n  margin: 1.25rem 0;\n  position: relative;\n  border-radius: 3px;\n  word-wrap: break-word;\n  background-color: #fff;\n  flex-direction: column;\n  background-clip: border-box;\n  border: 1px solid rgba(0, 0, 0, 0.125);\n  box-shadow: rgb(0 0 0 / 10%) 0px 1px 3px 0px, rgb(0 0 0 / 5%) 0px 5px 15px 0px;\n\n  ${MEDIA_QUERY_IS_MOBILE} {\n    border: none;\n    border-radius: 0;\n    box-shadow: none;\n    margin: 0;\n  }\n`;\n\nexport const CardHeader = styled.div`\n  display: flex;\n  font-size: 1.15rem;\n  flex-flow: row wrap;\n  background-color: #fff;\n  padding: 0.75rem 1.25rem;\n  border-top-left-radius: 0.25rem;\n  border-top-right-radius: 0.25rem;\n  border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n\n  ${MEDIA_QUERY_IS_MOBILE} {\n    font-size: 1.1rem;\n    text-align: center;\n    display: inline-block;\n    padding: 0 1.15rem 1rem;\n  }\n`;\n\nexport const CardBody = styled.div<{ multiComponents?: boolean }>`\n  flex: 1 1 auto;\n  min-height: 32rem;\n  padding: 0.75rem 1.25rem;\n\n  ${({ multiComponents }) =>\n    multiComponents &&\n    css`\n      > div {\n        margin-bottom: 3rem;\n\n        :first-of-type > label {\n          margin-top: 0;\n        }\n\n        > label {\n          font-size: 18px;\n          margin-bottom: 0.5rem;\n        }\n      }\n    `}\n\n  ${MEDIA_QUERY_IS_MOBILE} {\n    padding: 0.5rem 0;\n  }\n`;\n\nexport const OtherSpan = styled.span`\n  opacity: 0.75;\n  font-size: 0.75em;\n  margin-top: 0.075em;\n  margin-left: 0.45em;\n`;\n\nexport const MenuPortalElement = styled.div<{ menuOpen: boolean; }>`\n  width: 100%;\n  margin: 0.5rem 0;\n  min-height: 115px;\n  position: relative;\n  border-radius: 3px;\n  transition: background-color 0.2s ease-out;\n  background-color: ${({ menuOpen }) => menuOpen ? 'white' : 'whitesmoke'};\n\n  span {\n    font-weight: 700;\n    font-size: 1.5em;\n    text-align: center;\n    padding: 1.25em 1em;\n    color: rgba(0,0,0,0.6);\n    display: ${({ menuOpen }) => menuOpen ? 'none' : 'block'};\n  }\n`;\n\n// =======================================\n// Advanced story specific\n// =======================================\n\nconst SPIN_KEYFRAMES = keyframes`\n  from {\n    transform: rotate(0deg);\n  } to {\n    transform: rotate(360deg);\n  }\n`;\n\nconst SPIN_ANIMATION_CSS = css`animation: ${SPIN_KEYFRAMES} infinite 8s linear;`;\n\nexport const ReactSvg = styled.svg<{ isDisabled?: boolean }>`\n  width: 30px;\n  height: 30px;\n  color: #1ea7fd;\n  fill: currentColor;\n  display: inline-block;\n  ${({ isDisabled }) => !isDisabled && SPIN_ANIMATION_CSS}\n`;\n\nexport const ChevronDownSvg = styled.svg<{ menuOpen: boolean }>`\n  width: 14px;\n  height: 14px;\n  fill: currentColor;\n  transition: transform 0.25s ease-in-out;\n  ${({ menuOpen }) => menuOpen && css`transform: rotate(180deg);`}\n`;\n\nexport const OptionContainer = styled.div`\n  height: 100%;\n  display: flex;\n  align-items: center;\n  flex-direction: row;\n`;\n\nexport const OptionName = styled.span`\n  color: #515151;\n  font-weight: 600;\n  margin-left: 1px;\n`;"
  },
  {
    "path": "__stories__/helpers/utils/index.ts",
    "content": "import type { Option } from '../../types';\n\nexport const numberWithCommas = (value: number): string => {\n  return value.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n};\n\nexport const getRandomInt = (min: number, max: number): number => {\n  return Math.floor(Math.random() * (max - min + 1)) + min;\n};\n\nexport const stringifyJavaScriptObj = (data: any = {}): string => {\n  return JSON.stringify(data, null, 2).replace(/\"(\\w+)\"\\s*:/g, '$1:');\n};\n\nexport const mockHttpRequest = async (delay: number = 500): Promise<void> => {\n  await new Promise(resolve => setTimeout(resolve, delay));\n};\n\nexport const createOptions = (count: number): Option[] => {\n  const options: Option[] = [];\n\n  for (let i = 0; i < count; i++) {\n    const value = i + 1;\n    options.push({\n      value,\n      label: `Option ${value}`\n    });\n  }\n\n  return options;\n};\n\nexport const createThemeOptions = (themeEnum: any): Option[] => {\n  return Object.keys(themeEnum).map((key) => ({\n    value: themeEnum[key],\n    label: themeEnum[key]\n  }));\n};\n\nexport const createAsyncOptions = (count: number, lblSuffix: string = ''): Option[] => {\n  const options = createOptions(count);\n  return options.map(({ value, label }: Option) => ({\n    value,\n    label: `${label}${lblSuffix ? (' - ' + lblSuffix) : ''}`\n  }));\n};\n\nexport const hexToRgba = (hex: string, alpha: number = 1): string => {\n  const hexReplacer: string = hex.replace(\n    /^#?([a-f\\d])([a-f\\d])([a-f\\d])$/i,\n    (_m, r, g, b) => `#${r}${r}${g}${g}${b}${b}`\n  );\n\n  const alphaValid: number = Math.min(1, Math.max(0, alpha));\n  const rgbParts: number[] = hexReplacer.substring(1).match(/.{2}/g)!.map((x) => parseInt(x, 16));\n  const rgbaParts = [...rgbParts, alphaValid].join(',');\n\n  return `rgba(${rgbaParts})`;\n};"
  },
  {
    "path": "__stories__/index.stories.tsx",
    "content": "import { Select } from '../src';\nimport { useUpdateEffect } from '../src/hooks';\nimport type { SelectedOption } from '../src/types';\nimport { toast, ToastContainer } from 'react-toastify';\nimport type { CityOption, Option, PackageOption } from './types';\nimport type { MultiParams, MenuOption, SelectRef, Theme } from '../src';\nimport React, { useMemo, useRef, useState, useEffect, useCallback, Fragment } from 'react';\nimport {\n  OPTION_CLS,\n  OPTION_FOCUSED_CLS,\n  OPTION_DISABLED_CLS,\n  OPTION_SELECTED_CLS,\n  CARET_ICON_CLS,\n  CLEAR_ICON_CLS,\n  LOADING_DOTS_CLS,\n  AUTOSIZE_INPUT_CLS,\n  MENU_CONTAINER_CLS,\n  SELECT_CONTAINER_CLS,\n  CONTROL_CONTAINER_CLS,\n  LOADING_MSG_DEFAULT\n} from '../src/constants';\nimport {\n  Button,\n  Buttons,\n  Hr,\n  Title,\n  SubTitle,\n  Label,\n  Columns,\n  Column,\n  Content,\n  Container,\n  List,\n  Li,\n  ListWrapper,\n  SelectContainer,\n  Paragraph,\n  TextHeader,\n  Checkboxes,\n  Card,\n  CardHeader,\n  CardBody,\n  OtherSpan,\n  OptionContainer,\n  OptionName,\n  ReactSvg,\n  ChevronDownSvg,\n  MenuPortalElement,\n  ThemeEnum,\n  ThemeConfigMap,\n  Checkbox,\n  CodeMarkup,\n  PackageLink,\n  OptionsCountButton,\n  mockHttpRequest,\n  getRandomInt,\n  useCallbackState,\n  createAsyncOptions,\n  createOptions,\n  stringifyJavaScriptObj,\n  THEME_DEFAULTS,\n  THEME_OPTIONS,\n  THEME_CONFIG,\n  CITY_OPTIONS,\n  PACKAGE_OPTIONS,\n  CLASS_NAME_HTML,\n  REACT_WINDOW_PACKAGE,\n  TOAST_CONTAINER_PROPS,\n  STYLED_COMPONENTS_PACKAGE,\n  REACT_SVG_PROPS,\n  REACT_SVG_CIRCLE_PROPS,\n  REACT_SVG_PATH_PROPS,\n  CHEVRON_SVG_PROPS,\n  CHEVRON_DOWN_PATH_PROPS\n} from './helpers';\n\nexport default {\n  title: 'React Functional Select/Demos'\n};\n\nexport const SingleSelect = () => {\n  const [isInvalid, setIsInvalid] = useCallbackState(false);\n  const [isLoading, setIsLoading] = useCallbackState(false);\n  const [isDisabled, setIsDisabled] = useCallbackState(false);\n  const [isClearable, setIsClearable] = useCallbackState(true);\n  const [isSearchable, setIsSearchable] = useCallbackState(true);\n\n  const getOptionValue = useCallback((option: CityOption): number => option.id, []);\n  const getOptionLabel = useCallback((option: CityOption): string => `${option.city}, ${option.state}`, []);\n\n  useEffect(() => {\n    isDisabled && setIsInvalid(false);\n  }, [isDisabled, setIsInvalid]);\n\n  return (\n    <Container>\n      <Title>Single-Select</Title>\n      <Hr />\n      <Paragraph>\n        In this story's source code, notice that the callback function\n        properties <code>getOptionValue</code> and <code>getOptionLabel</code> are\n        wrapped in a <code>useCallback</code>. While not required, <em>strongly prefer </em>\n        memoization of any callback function property whenever possible. This will boost\n        performance and reduce the amount of renders as these properties are referenced\n        in the dependency arrays of <code>useCallbacks</code>, <code>useEffects</code>,\n        and <code>useMemos</code>. When defined in a functional component, wrap in\n        a <code>useCallback</code>; when defined in a legacy class component, ensure proper\n        binding to <em>this</em>. Alternatively, if there is no dependency on any state,\n        you can opt to hoist functions outside of the component entirely.\n      </Paragraph>\n      <Paragraph>\n        The <code>options</code> property should also be memoized. Either consume\n        it directly from a state management store, or make sure it is stable by\n        avoiding inline or render-based mutations.\n      </Paragraph>\n      <SubTitle>Demo</SubTitle>\n      <Hr />\n      <Card>\n        <CardHeader>\n          <Checkboxes>\n            <Checkbox\n              label='Searchable'\n              checked={isSearchable}\n              onCheck={setIsSearchable}\n            />\n            <Checkbox\n              label='Clearable'\n              checked={isClearable}\n              onCheck={setIsClearable}\n            />\n            <Checkbox\n              label='Disabled'\n              checked={isDisabled}\n              onCheck={setIsDisabled}\n            />\n            <Checkbox\n              label='Invalid'\n              checked={isInvalid}\n              readOnly={isDisabled}\n              onCheck={setIsInvalid}\n            />\n            <Checkbox\n              label='Loading'\n              checked={isLoading}\n              onCheck={setIsLoading}\n            />\n          </Checkboxes>\n        </CardHeader>\n        <CardBody>\n          <SelectContainer>\n            <Select\n              isLoading={isLoading}\n              isInvalid={isInvalid}\n              options={CITY_OPTIONS}\n              isDisabled={isDisabled}\n              isClearable={isClearable}\n              isSearchable={isSearchable}\n              getOptionValue={getOptionValue}\n              getOptionLabel={getOptionLabel}\n            />\n          </SelectContainer>\n        </CardBody>\n      </Card>\n    </Container>\n  );\n};\n\nexport const MultiSelect = () => {\n  const [openMenuOnClick, setOpenMenuOnClick] = useCallbackState(true);\n  const [closeMenuOnSelect, setCloseMenuOnSelect] = useCallbackState(true);\n  const [blurInputOnSelect, setBlurInputOnSelect] = useCallbackState(false);\n  const [hideSelectedOptions, setHideSelectedOptions] = useCallbackState(true);\n\n  const getOptionValue = useCallback(({ id }: CityOption): number => id, []);\n  const getOptionLabel = useCallback(({ city, state }: CityOption): string => `${city}, ${state}`, []);\n\n  // example \"renderMultiOptions\" property that can be used to further customize labeling for multi-option scenarios\n  const renderMultiOptions = useCallback(\n    ({ selected, renderOptionLabel }: MultiParams) => (\n      <Fragment>\n        {selected.length && renderOptionLabel(selected[0].data)}\n        {selected.length > 1 && (\n          <OtherSpan>\n            {`(+${selected.length - 1} other${selected.length > 2 ? 's' : ''})`}\n          </OtherSpan>\n        )}\n      </Fragment>\n    ),\n    []\n  );\n\n  return (\n    <Container>\n      <Title>Multi-Select</Title>\n      <Hr />\n      <ListWrapper>\n        Add the <code>isMulti</code> property to allow for multiple selections.\n        While in multi-select mode, some properties are now applicable and\n        others become more pertinent.\n        <List>\n          <Li>\n            <TextHeader>hideSelectedOptions?: boolean</TextHeader> - Hide the\n            selected option from the menu. Default value is <em>false</em>, however,\n            if undefined and <code>isMulti</code> is <em>true</em>, then its value\n            defaults to <em>true</em>.\n          </Li>\n          <Li>\n            <TextHeader>closeMenuOnSelect?: boolean</TextHeader> - Close the\n            menu of options when the user selects an option. Default value is\n            false, however, it may be benefical to set this property to true for\n            convenience in multi-select scenarios.\n          </Li>\n          <Li>\n            <TextHeader>renderMultiOptions(params: MultiParams) {'=>'} ReactNode</TextHeader> -\n            Optional callback function that can be used to further customize the selection\n            label in multi-select scenarios. <code>params</code> is an object that contains\n            the <code>selected</code> and <code>renderOptionLabel</code> properties (array\n            of selected options and function used to render individual option labels,\n            respectively). When this function is defined, left and right arrow navigation\n            of individual options is disabled. When using this property, it may be be a good\n            idea to set the property <code>backspaceClearsValue</code> to <em>false</em> in\n            order to avoid accidentally clearing all selections when searching.\n          </Li>\n        </List>\n      </ListWrapper>\n      <SubTitle>Demo</SubTitle>\n      <Hr />\n      <Card>\n        <CardHeader>\n          <Checkboxes>\n            <Checkbox\n              label='closeMenuOnSelect'\n              checked={closeMenuOnSelect}\n              onCheck={setCloseMenuOnSelect}\n            />\n            <Checkbox\n              label='hideSelectedOptions'\n              checked={hideSelectedOptions}\n              onCheck={setHideSelectedOptions}\n            />\n            <Checkbox\n              label='blurInputOnSelect'\n              checked={blurInputOnSelect}\n              onCheck={setBlurInputOnSelect}\n            />\n            <Checkbox\n              label='openMenuOnClick (click caret if false)'\n              checked={openMenuOnClick}\n              onCheck={setOpenMenuOnClick}\n            />\n          </Checkboxes>\n        </CardHeader>\n        <CardBody multiComponents>\n          <SelectContainer>\n            <Label>Default</Label>\n            <Select\n              isMulti\n              isClearable\n              isSearchable\n              backspaceClearsValue\n              options={CITY_OPTIONS}\n              getOptionValue={getOptionValue}\n              getOptionLabel={getOptionLabel}\n              openMenuOnClick={openMenuOnClick}\n              blurInputOnSelect={blurInputOnSelect}\n              closeMenuOnSelect={closeMenuOnSelect}\n              hideSelectedOptions={hideSelectedOptions}\n            />\n          </SelectContainer>\n          <SelectContainer>\n            <Label>Custom &quot;renderMultiOptions&quot;</Label>\n            <Select\n              isMulti\n              isClearable\n              isSearchable\n              options={CITY_OPTIONS}\n              getOptionValue={getOptionValue}\n              getOptionLabel={getOptionLabel}\n              openMenuOnClick={openMenuOnClick}\n              blurInputOnSelect={blurInputOnSelect}\n              closeMenuOnSelect={closeMenuOnSelect}\n              hideSelectedOptions={hideSelectedOptions}\n              renderMultiOptions={renderMultiOptions}\n            />\n          </SelectContainer>\n        </CardBody>\n      </Card>\n    </Container>\n  );\n};\n\nexport const Styling = () => {\n  const [themeConfig, setThemeConfig] = useState<Theme | undefined>(undefined);\n  const [selectedOption, setSelectedOption] = useCallbackState<SelectedOption | null>(null);\n\n  const memoizedMarkupNode = useMemo(() => (\n    <CodeMarkup\n      language='markup'\n      header='Class Markup'\n      data={CLASS_NAME_HTML}\n    />\n  ), []);\n\n  useEffect(() => {\n    if (selectedOption) {\n      const { value } = selectedOption;\n      setThemeConfig(ThemeConfigMap[value!]);\n    }\n  }, [selectedOption]);\n\n  const noteCodeStyle = { fontWeight: 500 };\n  const selectWrapperStyle = { marginTop: '1rem' };\n  const noteStyle = { fontSize: 'inherit', fontWeight: 700 };\n  const menuItemSize = selectedOption?.value === ThemeEnum.LARGE_TEXT ? 44 : 35;\n\n  return (\n    <Container>\n      <Title>Styling</Title>\n      <Hr />\n      <SubTitle>Theming</SubTitle>\n      <Columns>\n        <Column widthPercent={40}>\n          <Content>\n            react-functional-select uses <PackageLink {...STYLED_COMPONENTS_PACKAGE} /> to\n            handle its styling. The root node is wrapped in\n            styled-component's <code>ThemeProvider</code> wrapper component which gives all\n            child styled-components access to the provided theme via React's context API.\n            To override react-functional-select's default theme, pass an object to\n            the <code>themeConfig</code> property - any matching properties will replace\n            those in the default theme.\n          </Content>\n          <Content>\n            Starting in <strong>v2.0.0</strong>, some of the nested objects in\n            the <code>themeConfig</code> object contain a <code>css</code> property\n            of type <code>string | FlattenSimpleInterpolation | undefined</code> (default value\n            is undefined). This property can be used to pass raw CSS styles as a string or wrapped\n            in <PackageLink {...STYLED_COMPONENTS_PACKAGE} /> exported <code>css</code> function.\n            Those objects are: select, control, icon, menu, noOptions, multiValue, and input.\n          </Content>\n          <Content>\n            Starting in <strong>v2.7.0</strong>, the control object in <code>themeConfig</code> has\n            the property <code>focusedCss</code> - which is similar to the <code>css</code> property,\n            except that it is only applied when the select control is focused (and removed when blurred).\n          </Content>\n        </Column>\n        <Column widthPercent={60}>\n          <CodeMarkup\n            language='javascript'\n            data={THEME_DEFAULTS}\n            header='Theme Defaults'\n            formatFn={stringifyJavaScriptObj}\n          />\n        </Column>\n      </Columns>\n      <SubTitle>Using Classes</SubTitle>\n      <Columns>\n        <Column widthPercent={40}>\n          <Content>\n            There is also the option to handle styling via CSS classes.\n            These are the classes that are available:\n          </Content>\n          <ListWrapper className='is-class-list'>\n            <List>\n              <Li>{SELECT_CONTAINER_CLS}</Li>\n              <Li>{CONTROL_CONTAINER_CLS}</Li>\n              <Li>{MENU_CONTAINER_CLS}</Li>\n              <Li>{AUTOSIZE_INPUT_CLS}</Li>\n              <Li>{CARET_ICON_CLS}</Li>\n              <Li>{CLEAR_ICON_CLS}</Li>\n              <Li>{LOADING_DOTS_CLS}</Li>\n              <Li>{OPTION_CLS}, {OPTION_FOCUSED_CLS}, {OPTION_SELECTED_CLS}, {OPTION_DISABLED_CLS}</Li>\n            </List>\n          </ListWrapper>\n        </Column>\n        <Column widthPercent={60}>\n          {memoizedMarkupNode}\n        </Column>\n      </Columns>\n      <SubTitle>Demo</SubTitle>\n      <Hr />\n      <Card>\n        <CardHeader>\n          <Label>\n            <em style={noteStyle}>Note: </em>the <code style={noteCodeStyle}>themeConfig</code> property\n            value provided shoud be properly memoized!\n          </Label>\n        </CardHeader>\n        <CardBody>\n          <Columns>\n            <Column widthPercent={40}>\n              <div style={selectWrapperStyle}>\n                <Select\n                  isClearable={false}\n                  isSearchable={false}\n                  options={THEME_OPTIONS}\n                  themeConfig={themeConfig}\n                  menuItemSize={menuItemSize}\n                  initialValue={THEME_OPTIONS[0]}\n                  onOptionChange={setSelectedOption}\n                />\n              </div>\n            </Column>\n            <Column widthPercent={60}>\n              <CodeMarkup\n                data={themeConfig}\n                language='javascript'\n                header='theme-config'\n                formatFn={stringifyJavaScriptObj}\n              />\n            </Column>\n          </Columns>\n        </CardBody>\n      </Card>\n    </Container>\n  );\n};\n\nexport const Events = () => {\n  const options = useMemo<Option[]>(() => createOptions(5), []);\n\n  const [addOnKeyDown, setAddOnKeyDown] = useCallbackState(false);\n  const [addOnMenuOpen, setAddOnMenuOpen] = useCallbackState(true);\n  const [addOnMenuClose, setAddOnMenuClose] = useCallbackState(false);\n  const [addOnInputBlur, setAddOnInputBlur] = useCallbackState(false);\n  const [addOnInputFocus, setAddOnInputFocus] = useCallbackState(false);\n  const [addOnOptionChange, setAddOnOptionChange] = useCallbackState(true);\n\n  const onMenuOpen = useCallback(() => toast.info('Menu opened'), []);\n  const onMenuClose = useCallback(() => toast.info('Menu closed'), []);\n  const onInputBlur = useCallback(() => toast.info('Control blurred'), []);\n  const onInputFocus = useCallback(() => toast.info('Control focused'), []);\n  const onKeyDown = useCallback(() => toast.info('keydown event executed'), []);\n  const onOptionChange = useCallback((option: Option) => toast.info(`Selected option: \"${option?.value}\"`), []);\n\n  return (\n    <Fragment>\n      <ToastContainer {...TOAST_CONTAINER_PROPS} />\n      <Container>\n        <Title>Events</Title>\n        <Hr />\n        <ListWrapper>\n          There are various callback function properties that are executed following\n          their associated events:\n          <List>\n            <Li>\n              <TextHeader>onOptionChange(data: any) {'=>'} void</TextHeader> -\n              executed after an option is selected or removed\n            </Li>\n            <Li>\n              <TextHeader>onMenuOpen(...args: any[]) {'=>'} void</TextHeader> -\n              executed after the menu is opened\n            </Li>\n            <Li>\n              <TextHeader>onMenuClose(...args: any[]) {'=>'} void</TextHeader> -\n              executed after the menu is closed\n            </Li>\n            <Li>\n              <TextHeader>onInputChange(value: string) {'=>'} void</TextHeader> -\n              executed after the input control's value changes\n            </Li>\n            <Li>\n              <TextHeader>onInputBlur(e: FocusEvent{'<'}HTMLInputElement{'>'}) {'=>'} void</TextHeader> -\n              executed after the input control is blurred\n            </Li>\n            <Li>\n              <TextHeader>onInputFocus(e: FocusEvent{'<'}HTMLInputElement{'>'}) {'=>'} void</TextHeader> -\n              executed after the input control is focused\n            </Li>\n            <Li>\n              <TextHeader>\n                onKeyDown(e: KeyboardEvent{'<'}HTMLDivElement{'>'}, input?: string, focusedOption?: FocusedOption) {'=>'} void\n              </TextHeader> -\n              executed after the onKeyDown event\n            </Li>\n            <Li>\n              <TextHeader>onSearchChange(value: string) {'=>'} void</TextHeader> -\n              executed after the input value is persisted to state; this value also evaluates\n              the <code>inputDelay</code> property for debouncing - this callback is really\n              only useful when <code>inputDelay</code> is defined, and if not, it probably\n              makes more sense to use the <code>onInputChange</code> callback\n            </Li>\n          </List>\n        </ListWrapper>\n        <SubTitle>Demo</SubTitle>\n        <Hr />\n        <Card>\n          <CardHeader>\n            <Label>Events trigger a toast notification (demo only)</Label>\n            <Checkboxes>\n              <Checkbox\n                label='onOptionChange'\n                checked={addOnOptionChange}\n                onCheck={setAddOnOptionChange}\n              />\n              <Checkbox\n                label='onMenuOpen'\n                checked={addOnMenuOpen}\n                onCheck={setAddOnMenuOpen}\n              />\n              <Checkbox\n                label='onMenuClose'\n                checked={addOnMenuClose}\n                onCheck={setAddOnMenuClose}\n              />\n              <Checkbox\n                label='onInputBlur'\n                checked={addOnInputBlur}\n                onCheck={setAddOnInputBlur}\n              />\n              <Checkbox\n                label='onInputFocus'\n                checked={addOnInputFocus}\n                onCheck={setAddOnInputFocus}\n              />\n              <Checkbox\n                label='onKeyDown'\n                checked={addOnKeyDown}\n                onCheck={setAddOnKeyDown}\n              />\n            </Checkboxes>\n          </CardHeader>\n          <CardBody>\n            <SelectContainer>\n              <Select\n                options={options}\n                onKeyDown={addOnKeyDown ? onKeyDown : undefined}\n                onMenuOpen={addOnMenuOpen ? onMenuOpen : undefined}\n                onMenuClose={addOnMenuClose ? onMenuClose : undefined}\n                onInputBlur={addOnInputBlur ? onInputBlur : undefined}\n                onInputFocus={addOnInputFocus ? onInputFocus : undefined}\n                onOptionChange={addOnOptionChange ? onOptionChange : undefined}\n              />\n            </SelectContainer>\n          </CardBody>\n        </Card>\n      </Container>\n    </Fragment>\n  );\n};\n\nexport const Methods = () => {\n  const selectRef = useRef<SelectRef | null>(null);\n  const options = useMemo<Option[]>(() => createOptions(5), []);\n\n  const blurSelect = () => selectRef.current?.blur();\n  const focusSelect = () => selectRef.current?.focus();\n  const clearValue = () => selectRef.current?.clearValue();\n  const toggleMenuOpen = () => selectRef.current?.toggleMenu(true);\n  const updateSelectedOption = () => selectRef.current?.setValue(options[0]);\n\n  return (\n    <Container>\n      <Title>Methods & Properties</Title>\n      <Hr />\n      <ListWrapper>\n        <strong>5</strong> methods and <strong>1</strong> property are exposed to\n        wrapping components and are accessible via a forwarded <code>ref</code>.\n        <List>\n          <Li>\n            <TextHeader>blur() {'=>'} void</TextHeader> - blur the control\n            programatically\n          </Li>\n          <Li>\n            <TextHeader>focus() {'=>'} void</TextHeader> - focus the control\n            programatically\n          </Li>\n          <Li>\n            <TextHeader>toggleMenu(state?: boolean) {'=>'} void</TextHeader> -\n            toggle the menu programatically\n          </Li>\n          <Li>\n            <TextHeader>clearValue() {'=>'} void</TextHeader> - clear the current\n            value programatically <em>(if an option is selected)</em>\n          </Li>\n          <Li>\n            <TextHeader>setValue(option?: any) {'=>'} void</TextHeader> - set the\n            value programatically <em>(option will be validated)</em>\n          </Li>\n          <Li>\n            <TextHeader>menuOpen: boolean</TextHeader> - Open state of the menu\n          </Li>\n        </List>\n      </ListWrapper>\n      <SubTitle>Demo</SubTitle>\n      <Hr />\n      <Card>\n        <CardHeader>\n          <Label>Methods</Label>\n          <Buttons>\n            <Button onClick={focusSelect}>Focus</Button>\n            <Button onClick={blurSelect}>Blur</Button>\n            <Button onClick={toggleMenuOpen}>Open Menu</Button>\n            <Button onClick={clearValue}>Clear Value</Button>\n            <Button onClick={updateSelectedOption}>Set Value</Button>\n          </Buttons>\n        </CardHeader>\n        <CardBody>\n          <SelectContainer>\n            <Select\n              ref={selectRef}\n              options={options}\n              initialValue={options[0]}\n            />\n          </SelectContainer>\n        </CardBody>\n      </Card>\n    </Container>\n  );\n};\n\nexport const Filtering = () => {\n  const [filterIgnoreCase, setFilterIgnoreCase] = useCallbackState(true);\n  const [useCustomFilterFunc, setUseCustomFilterFunc] = useCallbackState(false);\n  const [filterIgnoreAccents, setFilterIgnoreAccents] = useCallbackState(false);\n  const [filterMatchFromStart, setFilterMatchFromStart] = useCallbackState(false);\n\n  const getOptionValue = useCallback(({ id }: CityOption): number => id, []);\n  const getOptionLabel = useCallback(({ city, state }: CityOption): string => `${city}, ${state}`, []);\n  const getFilterOptionString = useCallback((menuOption: MenuOption): string => menuOption.data.state, []);\n\n  const options = useMemo<CityOption[]>(() => [\n    ...CITY_OPTIONS,\n    { id: 11, city: 'São Paulo', state: 'BR' }\n  ], []);\n\n  return (\n    <Container>\n      <Title>Filter Customization</Title>\n      <Hr />\n      <ListWrapper>\n        The default filtering functionality can be customized via the following properties:\n        <List>\n          <Li>\n            <TextHeader>filterIgnoreCase?: boolean</TextHeader> - Filter ignores\n            case when matching strings. Default value is <em>true</em>.\n          </Li>\n          <Li>\n            <TextHeader>filterIgnoreAccents?: boolean</TextHeader> - Filter\n            ignores accents when matching strings. Default value is <em>false</em>.\n          </Li>\n          <Li>\n            <TextHeader>filterMatchFrom?: 'any' | 'start'</TextHeader> -\n            Position in source string to perform match. Default value is <em>'any'</em>.\n          </Li>\n          <Li>\n            <TextHeader>getFilterOptionString(option: MenuOption) {'=>'} string</TextHeader> -\n            When defined will take each option and generate a string used in\n            the filtering process. By default, the stringified version of what is\n            generated by <code>getOptionLabel</code>, if definded, or the option's label\n            as a fallback. The <code>MenuOption</code> typed parameter\n            that <code>getFilterOptionString</code> accepts contains a <code>data</code> property\n            that represents the objects that comprise your <code>options</code> property.\n          </Li>\n        </List>\n      </ListWrapper>\n      <SubTitle>Demo</SubTitle>\n      <Hr />\n      <Card>\n        <CardHeader>\n          <Checkboxes>\n            <Checkbox\n              label='Ignore Case'\n              checked={filterIgnoreCase}\n              onCheck={setFilterIgnoreCase}\n            />\n            <Checkbox\n              label='Ignore Accents'\n              checked={filterIgnoreAccents}\n              onCheck={setFilterIgnoreAccents}\n            />\n            <Checkbox\n              label='Match from the start'\n              checked={filterMatchFromStart}\n              onCheck={setFilterMatchFromStart}\n            />\n            <Checkbox\n              label='Use custom filter function (by state only)'\n              checked={useCustomFilterFunc}\n              onCheck={setUseCustomFilterFunc}\n            />\n          </Checkboxes>\n        </CardHeader>\n        <CardBody>\n          <SelectContainer>\n            <Select\n              isClearable\n              options={options}\n              getOptionValue={getOptionValue}\n              getOptionLabel={getOptionLabel}\n              filterIgnoreCase={filterIgnoreCase}\n              filterIgnoreAccents={filterIgnoreAccents}\n              filterMatchFrom={filterMatchFromStart ? 'start' : 'any'}\n              getFilterOptionString={useCustomFilterFunc ? getFilterOptionString : undefined}\n            />\n          </SelectContainer>\n        </CardBody>\n      </Card>\n    </Container>\n  );\n};\n\nexport const Virtualization = () => {\n  const selectRef = useRef<SelectRef | null>(null);\n  const optionCountList = useMemo(() => [100, 1000, 10000, 25000, 50000, 100000], []);\n  const [optionsCount, setOptionsCount] = useState(optionCountList[0]);\n  const options = useMemo<Option[]>(() => createOptions(optionsCount), [optionsCount]);\n\n  useUpdateEffect(() => {\n    selectRef.current?.clearValue();\n  }, [options]);\n\n  return (\n    <Container>\n      <Title>Menu List Virtualization</Title>\n      <Hr />\n      <ListWrapper>\n        Option list data is <em>virtualized</em> (or <em>windowed</em>) using\n        the <PackageLink {...REACT_WINDOW_PACKAGE} /> package. Aside from the\n        obvious benefits provided by only rendering a small subset of your\n        enumerable data (rather than bloating the DOM with an excessive amount\n        of nodes), list virtualization can also assist with:\n        <List>\n          <Li>\n            <strong>Efficient memory allocation</strong>. 'Windowing' naturally\n            lends itself to the dynamic generation of attributes/values as each\n            object comes into your renderer's scope (as opposed to allocating\n            this data upfront for each object in your list). This way you can\n            perform this work just when you absolutely need to and then can\n            immediately release it for the GC to cleanup. As an example I am\n            generating the <code>onClick</code>, <code>id</code>, and{' '}\n            <code>className</code> attributes for each <code>menuOption</code>{' '}\n            as they get passed to the <code>{'<'}Option /{'>'}</code> renderer\n            component.\n          </Li>\n          <Li>\n            <strong>Functional architecture</strong>. The flexibility provided\n            through only having to manage subsets of your list allows for a more\n            dynamic application. By breaking your code out into smaller, 'pure'\n            child components, you can write code that scales well and becomes\n            open to performance optimizations - most notably, memoization.\n            Simple components that rely on the props passed to it (rather than\n            its own managed state) to generate its JSX are likely candidates for\n            memoization (testing & debugging becomes much easier as well).\n          </Li>\n        </List>\n        <em>Note: </em>Potential performance degradation could be encountered during input\n        value mutations when the <code>options</code> count reaches the high tens of thousands.\n        To work around this, the <code>inputDelay</code> (in milliseconds) can be set to debounce\n        the input value. That way, the <code>menuOptions</code> will not be recalculated on every\n        keystroke. Although this is an extreme edge case, optimizations have been implemented to\n        handle such with ease. As proof, 50k and 100k option counts have been included in this\n        stress-test demo - but again, data sets this large should not be worked with in memory.\n        Instead, prefer to fetch subsets from a remote data store as needed. For example, using\n        the <code>async</code> functionality or custom logic in a parent component that accomplishes\n        something similar.\n      </ListWrapper>\n      <SubTitle>Demo</SubTitle>\n      <Hr />\n      <Card>\n        <CardHeader>\n          <Label>Options Count</Label>\n          <Buttons>\n            {optionCountList.map((count) => (\n              <OptionsCountButton\n                key={count}\n                count={count}\n                optionsCount={optionsCount}\n                setOptionsCount={setOptionsCount}\n              />\n            ))}\n          </Buttons>\n        </CardHeader>\n        <CardBody>\n          <SelectContainer>\n            <Select\n              ref={selectRef}\n              options={options}\n            />\n          </SelectContainer>\n        </CardBody>\n      </Card>\n    </Container>\n  );\n};\n\nexport const Advanced = () => {\n  const getOptionValue = useCallback(({ id }: PackageOption): number => id, []);\n  const getIsOptionDisabled = useCallback(({ name }: PackageOption): boolean => name === PACKAGE_OPTIONS[3].name, []);\n\n  const renderOptionLabel = useCallback(\n    (option: PackageOption) => (\n      <OptionContainer>\n        <ReactSvg\n          {...REACT_SVG_PROPS}\n          isDisabled={getIsOptionDisabled(option)}\n        >\n          <path {...REACT_SVG_PATH_PROPS} />\n          <circle {...REACT_SVG_CIRCLE_PROPS} />\n        </ReactSvg>\n        <OptionName>{option.name}</OptionName>\n      </OptionContainer>\n    ),\n    [getIsOptionDisabled]\n  );\n\n  const customCaretIcon = useCallback(\n    ({ menuOpen }) => (\n      <ChevronDownSvg\n        menuOpen={menuOpen}\n        {...CHEVRON_SVG_PROPS}\n      >\n        <path {...CHEVRON_DOWN_PATH_PROPS} />\n      </ChevronDownSvg>\n    ),\n    []\n  );\n\n  return (\n    <Container>\n      <Title>Advanced Customization</Title>\n      <Hr />\n      <ListWrapper>\n        Implementation using a couple of the more specialized properties.\n        <List>\n          <Li>\n            <TextHeader>renderOptionLabel(option: any) {'=>'} ReactNode</TextHeader> - Callback\n            function with a return type of <code>ReactNode</code>. Use this property in cases\n            where the standard <code>getOptionLabel</code> property will not meet your needs (for\n            instance, you want to render each option's label using custom JSX). More complex\n            option labels will likely equate to longer render durations - this can translate\n            into a flash of empty space when a user first starts scrolling. In order to prevent\n            this, the <code>menuOverscanCount</code> property can be increased to render additional\n            rows outside of the visible area. The default value for this property is 1 and it is\n            important to note that increasing this value can negatively impact performance.\n          </Li>\n          <Li>\n            <TextHeader>getIsOptionDisabled(option: any) {'=>'} boolean</TextHeader> - Callback\n            function with a return type of <code>Boolean</code>. When it evaluates to a value of\n            true, that option iteration will be rendered <em>disabled</em>. As an alternative, you\n            can also pass a property of <code>isDisabled</code> with each option. Use one of these two\n            options - they cannot both be specified.\n          </Li>\n          <Li>\n            <TextHeader>caretIcon: ReactNode | (...args: any[]) {'=>'} ReactNode</TextHeader> - A custom\n            node or a function that returns a node can used for the <code>caretIcon</code> property.\n            When using a function, an object containing stateful data is forwarded and can be used to style\n            your custom node accordingly. The state is <em>{'{ menuOpen, isLoading, isInvalid, isDisabled }'}</em> of\n            type <code>Record{'<'}string, boolean{'>'}</code>. The <code>clearIcon</code> property has an identical definition.\n          </Li>\n        </List>\n      </ListWrapper>\n      <SubTitle>Demo</SubTitle>\n      <Hr />\n      <Card>\n        <CardHeader>\n          <Label>JSX labels, custom caret icon, and disabled option</Label>\n        </CardHeader>\n        <CardBody>\n          <SelectContainer>\n            <Select\n              isSearchable={false}\n              options={PACKAGE_OPTIONS}\n              themeConfig={THEME_CONFIG}\n              caretIcon={customCaretIcon}\n              getOptionValue={getOptionValue}\n              renderOptionLabel={renderOptionLabel}\n              getIsOptionDisabled={getIsOptionDisabled}\n            />\n          </SelectContainer>\n        </CardBody>\n      </Card>\n    </Container>\n  );\n};\n\nexport const Portaling = () => {\n  const portalId = 'menu-portal-test';\n  const options = useMemo<Option[]>(() => createOptions(3), []);\n\n  const [menuOpen, setMenuOpen] = useState(false);\n  const [menuPortalTarget, setMenuPortalTarget] = useState<HTMLElement | undefined>(undefined);\n  const onMenuOpen = useCallback(() => setMenuOpen(true), []);\n  const onMenuClose = useCallback(() => setMenuOpen(false), []);\n\n  useEffect(() => {\n    const portalEl = document.getElementById(portalId) as HTMLElement;\n    setMenuPortalTarget(portalEl);\n  }, [portalId]);\n\n  return (\n    <Container>\n      <Title>Portaling</Title>\n      <Hr />\n      <Paragraph>\n        react-functional-select exposes a <code>menuPortalTarget</code> prop, that\n        allows you to portal the menu component to a dom node of your choosing. Styling\n        should be simple enough via normal theme overriding on the menu object and style\n        application to the wrapping portal element.\n      </Paragraph>\n      <SubTitle>Demo</SubTitle>\n      <Hr />\n      <Card>\n        <CardHeader>\n          <Label>Menu component portaled to an element below this text.</Label>\n          <MenuPortalElement\n            id={portalId}\n            menuOpen={menuOpen}\n          >\n            <span>portal node</span>\n          </MenuPortalElement>\n        </CardHeader>\n        <CardBody>\n          <SelectContainer>\n            <Select\n              options={options}\n              onMenuOpen={onMenuOpen}\n              onMenuClose={onMenuClose}\n              scrollMenuIntoView={false}\n              menuPortalTarget={menuPortalTarget}\n            />\n          </SelectContainer>\n        </CardBody>\n      </Card>\n    </Container>\n  );\n};\n\nexport const Async = () => {\n  const delay = 500;\n  const selectRef = useRef<SelectRef | null>(null);\n  const [isLoading, setIsLoading] = useState(false);\n  const [options, setOptions] = useState<Option[]>(() => createAsyncOptions(5));\n  const onInputChange = useCallback(() => setIsLoading(true), []);\n\n  const onSearchChange = useCallback(\n    async (value: string = '') => {\n      try {\n        await mockHttpRequest();\n        const nextOptions = createAsyncOptions(getRandomInt(1, 5), value && `'${value}'`);\n        selectRef.current?.clearValue();\n        setOptions(nextOptions);\n        setIsLoading(false);\n      } catch (e) {\n        console.error(e);\n        setIsLoading(false);\n      }\n    },\n    []\n  );\n\n  return (\n    <Container>\n      <Title>Async Mode</Title>\n      <Hr />\n      <ListWrapper>\n        Add the <code>async</code> property to enable async mode. There is one key\n        difference in core functionality with async mode - changes to search input\n        value will not cause the <code>useMenuOptions</code> effect to run. The rest\n        of hooking into async mode is achieved using some combination of the properties\n        found below. <em>Properties onInputChange and onSearchChange should be memoized.</em>\n        <List>\n          <Li>\n            <TextHeader>onInputChange(value: string) {'=>'} void</TextHeader> -\n            callback executed directly following the input control's <code>onChange</code> event.\n            This callback is not debounced, so it fires immediately. This is a good\n            place to set a stateful loading property in your parent component that is mapped to\n            react-functional-select's <code>isLoading</code> property.\n          </Li>\n          <Li>\n            <TextHeader>onSearchChange(value: string) {'=>'} void</TextHeader> -\n            callback executed following component state updates for\n            the <code>debouncedInputValue</code>. The debounce is set using\n            the <code>inputDelay</code> property. This callback is a good place for your\n            http fetch request and post-request logic (i.e. setting isLoading false).\n          </Li>\n          <Li>\n            <TextHeader>inputDelay?: number</TextHeader> - As mentioned above, this can be\n            set to a positive integer in order to debounce updates to the search input value\n            following input change events. This property directly maps to the <code>delay</code> in\n            milliseconds passed to the <code>setTimeout</code> method.\n          </Li>\n          <Li>\n            <TextHeader>isLoading?: boolean</TextHeader> - When true, a loading animation will\n            appear in the far-right of the control and take the place of the clear icon (if shown).\n            Additionally, it will hide options in the menu and instead, display a loading message.\n            The loading message text defaults to '{LOADING_MSG_DEFAULT}', but can be overriden via\n            the <code>loadingMsg</code> property.\n          </Li>\n        </List>\n      </ListWrapper>\n      <SubTitle>Demo</SubTitle>\n      <Hr />\n      <Card>\n        <CardHeader>\n          <Label>Search debounced 500ms and mock HTTP call resolves after {delay}ms</Label>\n        </CardHeader>\n        <CardBody>\n          <SelectContainer>\n            <Select\n              async\n              isClearable\n              ref={selectRef}\n              options={options}\n              inputDelay={delay}\n              isLoading={isLoading}\n              onInputChange={onInputChange}\n              onSearchChange={onSearchChange}\n            />\n          </SelectContainer>\n        </CardBody>\n      </Card>\n    </Container>\n  );\n};"
  },
  {
    "path": "__stories__/types/index.d.ts",
    "content": "export type Option = Readonly<{\n  label: string | number;\n  value: string | number;\n}>;\n\nexport type CityOption = Readonly<{\n  id: number;\n  city: string;\n  state: string;\n}>;\n\nexport type PackageOption = Readonly<{\n  id: number;\n  name: string;\n}>;"
  },
  {
    "path": "__tests__/AriaLiveRegion.test.tsx",
    "content": "import React, { type ComponentProps } from 'react';\nimport { render } from '@testing-library/react';\nimport AriaLiveRegion from '../src/components/AriaLiveRegion';\nimport { getSelectedOptionMulti, ThemeWrapper } from './helpers';\nimport { ARIA_LIVE_CONTEXT_ID, ARIA_LIVE_SELECTION_ID } from '../src/constants';\nimport type { AriaLiveAttribute, FocusedOption, SelectedOption } from '../src/types';\n\ntype AriaLiveRegionProps = ComponentProps<typeof AriaLiveRegion>;\n\n// ============================================\n// Helper functions for AriaLiveRegion component\n// ============================================\n\nconst renderAriaLiveRegion = (props: AriaLiveRegionProps) => {\n  return render(\n    <ThemeWrapper>\n      <AriaLiveRegion {...props} />\n    </ThemeWrapper>\n  );\n};\n\nconst selectedOption: SelectedOption[] = getSelectedOptionMulti();\nconst focusedOption: FocusedOption = { index: 0, ...selectedOption[0] };\n\nconst BASE_PROPS: AriaLiveRegionProps = {\n  focusedOption,\n  selectedOption,\n  menuOpen: true,\n  isFocused: true,\n  isSearchable: true,\n  ariaLive: 'polite',\n  inputValue: 'search query',\n  optionCount: selectedOption.length\n} as const;\n\n// ============================================\n// Test cases\n// ============================================\n\ntest('AriaLiveRegion component mounts and renders without error & can query childNodes by id attributes', () => {\n  const { container } = renderAriaLiveRegion(BASE_PROPS);\n  const childNodeIds = [ARIA_LIVE_CONTEXT_ID, ARIA_LIVE_SELECTION_ID];\n\n  childNodeIds.forEach((id) => {\n    const childNode = container.querySelector(`#${id}`);\n    expect(childNode).toBeInTheDocument();\n  });\n});\n\ntest('\"ariaLive\" prop can be passed as one of the accepted aria-live values and the root A11yText span element reflects it accordingly', () => {\n  const ariaLive: AriaLiveAttribute = 'assertive';\n  const props = { ...BASE_PROPS, ariaLive };\n  const { container } = renderAriaLiveRegion(props);\n  const a11yTextRootSpanEl = container.firstChild;\n  expect(a11yTextRootSpanEl).toHaveAttribute('aria-live', ariaLive);\n});"
  },
  {
    "path": "__tests__/AutosizeInput.test.tsx",
    "content": "import React, { type ComponentProps } from 'react';\nimport { ThemeWrapper } from './helpers';\nimport userEvent from '@testing-library/user-event';\nimport { render, fireEvent } from '@testing-library/react';\nimport AutosizeInput from '../src/components/AutosizeInput';\nimport { AUTOSIZE_INPUT_CLS, AUTOSIZE_INPUT_TESTID } from '../src/constants';\n\ntype AutosizeInputProps = ComponentProps<typeof AutosizeInput>;\n\n// ============================================\n// Helper functions for AutosizeInput component\n// ============================================\n\nconst renderAutosizeInput = (props: AutosizeInputProps) => {\n  return {\n    user: userEvent.setup(),\n    ...render(\n      <ThemeWrapper>\n        <AutosizeInput {...props} />\n      </ThemeWrapper>\n    )\n  };\n};\n\nconst onBlurSpy = jest.fn();\nconst onFocusSpy = jest.fn();\nconst onChangeSpy = jest.fn();\n\nconst BASE_PROPS: AutosizeInputProps = {\n  inputValue: '',\n  readOnly: false,\n  menuOpen: false,\n  onBlur: onBlurSpy,\n  onFocus: onFocusSpy,\n  onChange: onChangeSpy,\n  hasSelectedOptions: false\n} as const;\n\n// ============================================\n// Test cases\n// ============================================\n\ntest('input element has a static className (enables styling via classic CSS)', () => {\n  const { getByTestId } = renderAutosizeInput(BASE_PROPS);\n  expect(getByTestId(AUTOSIZE_INPUT_TESTID!)).toHaveClass(AUTOSIZE_INPUT_CLS);\n});\n\ntest('input has functional, optional ARIA attributes', () => {\n  const props = {\n    ...BASE_PROPS,\n    ariaLabel: 'test-label',\n    ariaLabelledBy: 'test-labelledby',\n  };\n\n  const { getByTestId } = renderAutosizeInput(props);\n  const ariaAttrs = ['aria-label', 'aria-labelledby', 'aria-autocomplete'];\n\n  ariaAttrs.forEach((attr: string) => {\n    expect(getByTestId(AUTOSIZE_INPUT_TESTID!)).toHaveAttribute(attr);\n  });\n});\n\ntest('when \"id\" has a non-empty string value, input element should get an \"id\" attribute reflecting that value', () => {\n  const inputId = 'test-input-id';\n  const props = { ...BASE_PROPS, id: inputId };\n  const { getByTestId } = renderAutosizeInput(props);\n  expect(getByTestId(AUTOSIZE_INPUT_TESTID!)).toHaveAttribute('id', inputId);\n});\n\ntest('when \"readOnly\" = true, the onChange event handler should not be attached to input and the \"readonly\" attribute is added', async () => {\n  const props = { ...BASE_PROPS, readOnly: true };\n  const { user, getByTestId } = renderAutosizeInput(props);\n  const inputElement = getByTestId(AUTOSIZE_INPUT_TESTID!);\n  await user.type(inputElement, 'no change');\n  expect(onChangeSpy).not.toBeCalled();\n  expect(inputElement).toHaveAttribute('readonly');\n});\n\ntest('\"blur\" and \"focus\" events with callback handlers are attached to the input element', () => {\n  const { getByTestId } = renderAutosizeInput(BASE_PROPS);\n  const inputElement = getByTestId(AUTOSIZE_INPUT_TESTID!);\n  fireEvent.blur(inputElement);\n  fireEvent.focus(inputElement);\n  expect(onBlurSpy).toBeCalled();\n  expect(onFocusSpy).toBeCalled();\n});"
  },
  {
    "path": "__tests__/IndicatorIcons.test.tsx",
    "content": "import React, { type ReactNode, type ComponentProps } from 'react';\nimport { ThemeWrapper } from './helpers';\nimport { render } from '@testing-library/react';\nimport userEvent from '@testing-library/user-event';\nimport IndicatorIcons from '../src/components/IndicatorIcons';\nimport { CLEAR_ICON_CLS, CLEAR_ICON_TESTID, CARET_ICON_TESTID } from '../src/constants';\n\ntype IndicatorIconsProps = ComponentProps<typeof IndicatorIcons>;\n\n// ============================================\n// Helper functions for IndicatorIcons component\n// ============================================\n\nconst renderIndicatorIcons = (props: IndicatorIconsProps) => {\n  return {\n    user: userEvent.setup(),\n    ...render(\n      <ThemeWrapper>\n        <IndicatorIcons {...props} />\n      </ThemeWrapper>\n    )\n  };\n};\n\nconst onClearMouseDownSpy = jest.fn();\nconst onCaretMouseDownSpy = jest.fn();\n\nconst BASE_PROPS: IndicatorIconsProps = {\n  menuOpen: false,\n  showClear: true,\n  onClearMouseDown: onClearMouseDownSpy,\n  onCaretMouseDown: onCaretMouseDownSpy\n} as const;\n\nconst customIconFn = (props: Partial<IndicatorIconsProps>): ReactNode => {\n  const { menuOpen, isLoading, isInvalid, isDisabled } = props;\n  const testIdText = `${menuOpen}-${isLoading}-${isInvalid}-${isDisabled}`;\n\n  return (\n    <span data-testid={testIdText}>\n      custom_icon\n    </span>\n  );\n};\n\n// ============================================\n// Test cases\n// ============================================\n\ntest('clear icon has a static className (enables styling via classic CSS)', () => {\n  const { getByTestId } = renderIndicatorIcons(BASE_PROPS);\n  const firstChildOfClearIconElement = getByTestId(CLEAR_ICON_TESTID!).firstChild;\n  expect(firstChildOfClearIconElement).toHaveClass(CLEAR_ICON_CLS);\n});\n\ntest('clear indicator has functioning \"click\" user interactions', async () => {\n  const { user, getByTestId } = renderIndicatorIcons(BASE_PROPS);\n  const clearIndicatorEl = getByTestId(CLEAR_ICON_TESTID!);\n  await user.click(clearIndicatorEl);\n  expect(onClearMouseDownSpy).toBeCalled();\n});\n\ntest('caret indicator has functioning \"click\" user interactions', async () => {\n  const { user, getByTestId } = renderIndicatorIcons(BASE_PROPS);\n  const caretIndicatorEl = getByTestId(CARET_ICON_TESTID!);\n  await user.click(caretIndicatorEl);\n  expect(onCaretMouseDownSpy).toBeCalled();\n});\n\ntest('clear icon is not rendered and loading animation is rendered when \"isLoading\" = true', () => {\n  const props = { ...BASE_PROPS, isLoading: true };\n  const { queryByTestId } = renderIndicatorIcons(props);\n  expect(queryByTestId(CLEAR_ICON_TESTID!)).toBeNull();\n});\n\ntest('loading can render as a custom node (instead of default LoadingDots.tsx component)', () => {\n  const loadingNodeText = 'loading-node';\n  const loadingNode = <span>{loadingNodeText}</span>;\n\n  const props = {\n    ...BASE_PROPS,\n    loadingNode,\n    isLoading: true,\n  };\n\n  const { getByText } = renderIndicatorIcons(props);\n  expect(getByText(loadingNodeText)).toBeInTheDocument();\n});\n\ntest('clear icon can render as a ReactNode', () => {\n  const clearIconText = 'clear-icon-node';\n  const clearIcon = <span>{clearIconText}</span>;\n  const props = { ...BASE_PROPS, clearIcon };\n  const { getByText } = renderIndicatorIcons(props);\n  expect(getByText(clearIconText)).toBeInTheDocument();\n});\n\ntest('clear icon can render as a callback function with return type of ReactNode - callback accepts forwarded state props from wrapping component.', () => {\n  const props = {\n    ...BASE_PROPS,\n    menuOpen: true,\n    clearIcon: customIconFn\n  };\n\n  const { getByTestId } = renderIndicatorIcons(props);\n\n  // Build test-id from forwarded state javascript object payload\n  const { menuOpen, isLoading, isInvalid, isDisabled } = props;\n  const forwardedStateId = `${menuOpen}-${isLoading}-${isInvalid}-${isDisabled}`\n  expect(getByTestId(forwardedStateId)).toBeInTheDocument();\n});\n\ntest('caret icon can render as a ReactNode', () => {\n  const caretIconText = 'caret-icon-node';\n  const caretIcon = <span>{caretIconText}</span>;\n  const props = { ...BASE_PROPS, caretIcon };\n  const { getByText } = renderIndicatorIcons(props);\n  expect(getByText(caretIconText)).toBeInTheDocument();\n});\n\ntest('caret icon can render as a callback function with return type of ReactNode - callback accepts forwarded state props from wrapping component.', () => {\n  const props = { ...BASE_PROPS, menuOpen: true, caretIcon: customIconFn };\n  const { getByTestId } = renderIndicatorIcons(props);\n\n  // Build test-id from forwarded state javascript object payload\n  const { menuOpen, isLoading, isInvalid, isDisabled } = props;\n  const forwardedStateId = `${menuOpen}-${isLoading}-${isInvalid}-${isDisabled}`\n  expect(getByTestId(forwardedStateId)).toBeInTheDocument();\n});"
  },
  {
    "path": "__tests__/LoadingDots.test.tsx",
    "content": "import React from 'react';\nimport { ThemeWrapper } from './helpers';\nimport { render } from '@testing-library/react';\nimport LoadingDots from '../src/components/IndicatorIcons/LoadingDots';\n\n// ============================================\n// Helper functions for AriaLiveRegion component\n// ============================================\n\nconst renderAriaLiveRegion = () => {\n  return render(\n    <ThemeWrapper>\n      <LoadingDots />\n    </ThemeWrapper>\n  );\n};\n\n// ============================================\n// Test cases\n// ============================================\n\ntest('LoadingDots component mounts and renders without error', () => {\n  const { container } = renderAriaLiveRegion();\n  expect(container.hasChildNodes()).toBeTruthy();\n});"
  },
  {
    "path": "__tests__/MenuList.test.tsx",
    "content": "import React, { type ComponentProps } from 'react';\nimport { ThemeWrapper } from './helpers';\nimport type { MenuOption } from '../src';\nimport { render } from '@testing-library/react';\nimport MenuList from '../src/components/Menu/MenuList';\nimport { MENU_OPTIONS, renderOptionLabelMock } from './helpers/utils';\nimport {\n  MENU_ITEM_SIZE_DEFAULT,\n  MENU_MAX_HEIGHT_DEFAULT,\n  LOADING_MSG_DEFAULT,\n  NO_OPTIONS_MSG_DEFAULT,\n  FOCUSED_OPTION_DEFAULT\n} from '../src/constants';\n\ntype MenuListProps = ComponentProps<typeof MenuList>;\n\n// ============================================\n// Helper functions for Menu component\n// ============================================\n\nconst renderMenuList = (props: MenuListProps) => {\n  return render(\n    <ThemeWrapper>\n      <MenuList {...props} />\n    </ThemeWrapper>\n  );\n};\n\nconst BASE_PROPS: MenuListProps = {\n  width: '100%',\n  memoOptions: false,\n  menuOptions: MENU_OPTIONS,\n  itemKeySelector: undefined,\n  fixedSizeListRef: undefined,\n  height: MENU_MAX_HEIGHT_DEFAULT,\n  loadingMsg: LOADING_MSG_DEFAULT,\n  itemSize: MENU_ITEM_SIZE_DEFAULT,\n  noOptionsMsg: NO_OPTIONS_MSG_DEFAULT,\n  selectOption: jest.fn(),\n  renderOptionLabel: renderOptionLabelMock,\n  focusedOptionIndex: FOCUSED_OPTION_DEFAULT.index\n} as const;\n\n// ============================================\n// Test cases\n// ============================================\n\ntest('MenuList component mounts and renders successfully when \"menuOptions\" array has items', () => {\n  const { getByText } = renderMenuList(BASE_PROPS);\n\n  // Assert react-window + Option.tsx renders each menuOption correctly\n  BASE_PROPS.menuOptions.forEach(({ label }: MenuOption) => {\n    expect(getByText(String(label))).toBeInTheDocument();\n  });\n});\n\ntest('The \"itemKeySelector\" property is used in \"react-window\" function property \"itemKey\" to select unqiue key based on property value rather than using default index for each option', () => {\n  const props = { ...BASE_PROPS, itemKeySelector: 'value' };\n  const { getByText } = renderMenuList(props);\n\n  // Assert react-window + Option.tsx renders each menuOption correctly\n  props.menuOptions.forEach(({ label }: MenuOption) => {\n    expect(getByText(String(label))).toBeInTheDocument();\n  });\n});\n\ntest('The \"No Options\" message element is NOT rendered when \"menuOptions\" length > 0', () => {\n  const { queryByText } = renderMenuList(BASE_PROPS);\n  expect(queryByText(BASE_PROPS.noOptionsMsg!)).toBeNull();\n});\n\ntest('The \"No Options\" message element is rendered when \"menuOptions\" length === 0', () => {\n  const props = { ...BASE_PROPS, menuOptions: [] };\n  const { getByText } = renderMenuList(props);\n  expect(getByText(props.noOptionsMsg!)).toBeInTheDocument();\n});\n\ntest('The \"Loading\" message element is NOT rendered when \"isLoading\" !== true', () => {\n  const { queryByText } = renderMenuList(BASE_PROPS);\n  expect(queryByText(BASE_PROPS.loadingMsg)).toBeNull();\n});\n\ntest('The \"Loading\" message element is rendered when \"isLoading\" === true', () => {\n  const props = { ...BASE_PROPS, isLoading: true };\n  const { getByText } = renderMenuList(props);\n  expect(getByText(props.loadingMsg)).toBeInTheDocument();\n});"
  },
  {
    "path": "__tests__/MultiValue.test.tsx",
    "content": "import React, { type ComponentProps } from 'react';\nimport { render } from '@testing-library/react';\nimport userEvent from '@testing-library/user-event';\nimport { CLEAR_ICON_MV_TESTID } from '../src/constants';\nimport MultiValue from '../src/components/Value/MultiValue';\nimport { renderOptionLabelMock, getOptionSingle, ThemeWrapper, type Option } from './helpers';\n\ntype MultiValueProps = ComponentProps<typeof MultiValue>;\n\n// ============================================\n// Helper functions for MultiValue component\n// ============================================\n\nconst renderMultiValue = (props: MultiValueProps) => {\n  return {\n    user: userEvent.setup(),\n    ...render(\n      <ThemeWrapper>\n        <MultiValue {...props} />\n      </ThemeWrapper>\n    )\n  };\n};\n\nconst removeSelectedOptionSpy = jest.fn();\nconst renderOptionLabelSpy = renderOptionLabelMock;\n\nconst data: Option = getOptionSingle();\n\nconst BASE_PROPS: MultiValueProps = {\n  data,\n  isFocused: false,\n  value: data.value,\n  renderOptionLabel: renderOptionLabelSpy,\n  removeSelectedOption: removeSelectedOptionSpy\n} as const;\n\n// ============================================\n// Test cases\n// ============================================\n\ntest('\"renderOptionLabel\" callback should be executed and should render the selected option label text', () => {\n  const { getByText } = renderMultiValue(BASE_PROPS);\n  expect(renderOptionLabelSpy).toBeCalled();\n  expect(getByText(BASE_PROPS.data.label)).toBeInTheDocument();\n});\n\ntest('clear indicator has functioning \"click\" user events', async () => {\n  const { user, getAllByTestId } = renderMultiValue(BASE_PROPS);\n  const firstClearIconEl = getAllByTestId(CLEAR_ICON_MV_TESTID!)[0];\n  await user.click(firstClearIconEl);\n  expect(removeSelectedOptionSpy).toBeCalled();\n});"
  },
  {
    "path": "__tests__/Option.test.tsx",
    "content": "import React, { type ComponentProps } from 'react';\nimport type { CSSProperties } from 'react';\nimport { render } from '@testing-library/react';\nimport Option from '../src/components/Menu/Option';\nimport userEvent from '@testing-library/user-event';\nimport { OPTION_DISABLED_CLS } from '../src/constants';\nimport { renderOptionLabelMock, stringifyCSSProperties, ThemeWrapper, MENU_OPTIONS } from './helpers';\n\ntype OptionProps = ComponentProps<typeof Option>;\n\n// ============================================\n// Helper functions & test data for Option.tsx component\n// ============================================\n\nconst renderOption = (props: OptionProps) => {\n  return {\n    user: userEvent.setup(),\n    ...render(\n      <ThemeWrapper>\n        <Option {...props} />\n      </ThemeWrapper>\n    )\n  };\n};\n\nconst OPTION_STYLE: CSSProperties = {\n  top: '0px',\n  left: '0px',\n  width: '100%',\n  height: '35px',\n  position: 'absolute'\n} as const;\n\nconst onClickSelectOptionSpy = jest.fn();\nconst renderOptionLabelSpy = renderOptionLabelMock;\n\nconst createOptionProps = (\n  index = 0,\n  focusedOptionIndex = 0,\n  memoOptions = false\n) => {\n  const props: OptionProps = {\n    index,\n    style: OPTION_STYLE,\n    data: {\n      memoOptions,\n      focusedOptionIndex,\n      menuOptions: MENU_OPTIONS,\n      selectOption: onClickSelectOptionSpy,\n      renderOptionLabel: renderOptionLabelSpy\n    }\n  };\n\n  return {\n    props,\n    renderOptionLabelSpy,\n    onClickSelectOptionSpy\n  };\n};\n\n// ============================================\n// Test cases\n// ============================================\n\ntest('option parent element renders dynamic style attribute correctly', () => {\n  const { props } = createOptionProps();\n  const { container } = renderOption(props);\n  const optionParentEl = container.querySelector('div');\n  const optionCssProps = stringifyCSSProperties(OPTION_STYLE);\n  expect(optionParentEl).toHaveAttribute('style', optionCssProps);\n});\n\ntest('\"renderOptionLabel\" callback should be executed and the result rendered to DOM', () => {\n  const { props, renderOptionLabelSpy } = createOptionProps();\n  const { label } = props.data.menuOptions[props.index];\n  const { getByText } = renderOption(props);\n  expect(renderOptionLabelSpy).toHaveBeenCalled();\n  expect(getByText(String(label))).toBeInTheDocument();\n});\n\ntest(`option with \"isDisabled\" = TRUE should have an onClick handler and the ${OPTION_DISABLED_CLS} class added to its classList`, async () => {\n  const firstDisabledIdx = MENU_OPTIONS.findIndex((x) => x.isDisabled);\n  const { props, onClickSelectOptionSpy } = createOptionProps(firstDisabledIdx);\n  const { user, container } = renderOption(props);\n  const optionParentEl = container.querySelector('div') as HTMLDivElement;\n  await user.click(optionParentEl);\n  expect(onClickSelectOptionSpy).toBeCalled();\n  expect(optionParentEl).toHaveClass(OPTION_DISABLED_CLS);\n});"
  },
  {
    "path": "__tests__/ReactSSR.test.tsx",
    "content": "import React from 'react';\nimport { Select } from '../src';\nimport { renderToString } from 'react-dom/server';\n\ntest('Select component can be rendered using react-dom/server', () => {\n  expect(() => renderToString(<Select />)).not.toThrowError();\n});\n"
  },
  {
    "path": "__tests__/Select.test.tsx",
    "content": "import { Select } from '../src';\nimport React, { type ComponentProps } from 'react';\nimport userEvent from '@testing-library/user-event';\nimport { render, fireEvent } from '@testing-library/react';\nimport {\n  MENU_CONTAINER_CLS,\n  SELECT_CONTAINER_CLS,\n  CONTROL_CONTAINER_CLS,\n  MENU_CONTAINER_TESTID,\n  AUTOSIZE_INPUT_TESTID,\n  SELECT_CONTAINER_TESTID,\n  CONTROL_CONTAINER_TESTID\n} from '../src/constants';\n\ntype SelectProps = ComponentProps<typeof Select>;\n\n// ============================================\n// Helper functions for Select component\n// ============================================\n\nconst renderSelect = (props?: SelectProps) => ({\n  user: userEvent.setup(),\n  ...render(<Select {...props} />)\n});\n\n// ============================================\n// Test cases\n// ============================================\n\ntest('container elements have static className value (enables styling via classic CSS)', () => {\n  const { getByTestId } = renderSelect();\n  expect(getByTestId(SELECT_CONTAINER_TESTID!)).toHaveClass(SELECT_CONTAINER_CLS);\n  expect(getByTestId(CONTROL_CONTAINER_TESTID!)).toHaveClass(CONTROL_CONTAINER_CLS);\n  expect(getByTestId(MENU_CONTAINER_TESTID!)).toHaveClass(MENU_CONTAINER_CLS);\n});\n\ntest('id attributes are added to DOM if defined (\"menuId\", \"selectId\", \"inputId\" props)', () => {\n  const props = {\n    menuId: 'test-menu-id',\n    inputId: 'test-input-id',\n    selectId: 'test-select-id'\n  };\n  const { getByTestId } = renderSelect(props);\n  expect(getByTestId(MENU_CONTAINER_TESTID!)).toHaveAttribute('id', props.menuId);\n  expect(getByTestId(AUTOSIZE_INPUT_TESTID!)).toHaveAttribute('id', props.inputId);\n  expect(getByTestId(SELECT_CONTAINER_TESTID!)).toHaveAttribute('id', props.selectId);\n});\n\ntest('\"onInputFocus\" callback should be fired when input is focused (if a defined function)', () => {\n  const onFocusSpy = jest.fn();\n  const props = { onInputFocus: onFocusSpy };\n  const { getByTestId } = renderSelect(props);\n  fireEvent.focus(getByTestId(AUTOSIZE_INPUT_TESTID!));\n  expect(onFocusSpy).toBeCalled();\n});\n\ntest('\"onInputBlur\" callback should be fired on blur (if a defined function)', () => {\n  const onBlurSpy = jest.fn();\n  const props = { onInputBlur: onBlurSpy };\n  const { getByTestId } = renderSelect(props);\n  fireEvent.blur(getByTestId(AUTOSIZE_INPUT_TESTID!));\n  expect(onBlurSpy).toBeCalled();\n});\n\ntest('toggling the menu to open/close fires corresponding callbacks \"onMenuOpen\" and \"onMenuClose\" (if they are defined functions)', async () => {\n  const onMenuOpenSpy = jest.fn();\n  const onMenuCloseSpy = jest.fn();\n\n  const props = {\n    onMenuOpen: onMenuOpenSpy,\n    onMenuClose: onMenuCloseSpy,\n  };\n\n  const { user, getByTestId } = renderSelect(props);\n  const controlWrapperEl = getByTestId(CONTROL_CONTAINER_TESTID!);\n  await user.click(controlWrapperEl);\n  await user.click(controlWrapperEl);\n  expect(onMenuOpenSpy).toBeCalled();\n  expect(onMenuCloseSpy).toBeCalled();\n});\n\ntest('When \"lazyLoadMenu\" property = true, then menu components are only rendered in DOM when \"menuOpen\" state = true', () => {\n  const props = { lazyLoadMenu: true };\n  const { queryByTestId } = renderSelect(props);\n  expect(queryByTestId(MENU_CONTAINER_TESTID!)).toBeNull();\n});"
  },
  {
    "path": "__tests__/Value.test.tsx",
    "content": "import React, { type ComponentProps } from 'react';\nimport Value from '../src/components/Value';\nimport { render } from '@testing-library/react';\nimport type { CallbackFn, SelectedOption } from '../src/types';\nimport { PLACEHOLDER_DEFAULT, EMPTY_ARRAY } from '../src/constants';\nimport { renderOptionLabelMock, renderMultiOptionsMock, getSelectedOptionSingle, ThemeWrapper } from './helpers';\n\ntype ValueProps = ComponentProps<typeof Value>;\n\n// ============================================\n// Helper functions for Value component\n// ============================================\n\nconst renderValue = (props: ValueProps) => {\n  return render(\n    <ThemeWrapper>\n      <Value {...props} />\n    </ThemeWrapper>\n  );\n};\n\nconst rerenderValue = (props: ValueProps, rerender: CallbackFn): void => {\n  rerender(\n    <ThemeWrapper>\n      <Value {...props} />\n    </ThemeWrapper>\n  );\n};\n\nconst removeSelectedOptionSpy = jest.fn();\nconst renderOptionLabelSpy = renderOptionLabelMock;\nconst renderMultiOptionsSpy = renderMultiOptionsMock;\n\nconst BASE_PROPS: ValueProps = {\n  isMulti: false,\n  hasInput: false,\n  focusedMultiValue: null,\n  selectedOption: EMPTY_ARRAY,\n  renderMultiOptions: undefined,\n  placeholder: PLACEHOLDER_DEFAULT,\n  renderOptionLabel: renderOptionLabelSpy,\n  removeSelectedOption: removeSelectedOptionSpy\n} as const;\n\n// ============================================\n// Test cases\n// ============================================\n\ntest('\"placeholder\" text displays when no option is selected', () => {\n  const { getByText } = renderValue(BASE_PROPS);\n  expect(getByText(PLACEHOLDER_DEFAULT)).toBeInTheDocument();\n});\n\ntest('component renders NULL if \"hasInput\" is true AND (\"isMulti\" !== true OR (\"isMulti\" === true AND \"selectedOptions\" is empty))', () => {\n  // Render with truthy \"inputValue\" and \"isMulti\" = false\n  const singleProps = { ...BASE_PROPS, hasInput: true };\n  const { container, rerender } = renderValue(singleProps);\n  expect(container.hasChildNodes()).toBeFalsy();\n\n  // Re-render with truthy \"inputValue\" and \"isMulti\" = true\n  const multiProps = { ...singleProps, isMulti: true };\n  rerenderValue(multiProps, rerender);\n  expect(container.hasChildNodes()).toBeFalsy();\n});\n\ntest('\"renderOptionLabel\" callback should be executed when an option is selected and should render the selected option label text', () => {\n  const selectedOption = getSelectedOptionSingle();\n  const props = { ...BASE_PROPS, selectedOption };\n  const { getByText } = renderValue(props);\n\n  expect(renderOptionLabelSpy).toHaveBeenCalledTimes(1);\n\n  selectedOption.forEach(({ label }: SelectedOption) => {\n    expect(getByText(String(label))).toBeInTheDocument();\n  });\n});\n\ntest('\"renderMultiOptions\" callback should be executed when \"isMulti\" = true and \"renderMultiOptions\" is a function and at least one option is selected', () => {\n  const props = {\n    ...BASE_PROPS,\n    isMulti: true,\n    selectedOption: getSelectedOptionSingle(),\n    renderMultiOptions: renderMultiOptionsSpy\n  };\n  renderValue(props);\n  expect(renderMultiOptionsSpy).toHaveBeenCalledTimes(1);\n});"
  },
  {
    "path": "__tests__/helpers/ThemeWrapper.tsx",
    "content": "import React from 'react';\nimport { ThemeProvider } from 'styled-components';\nimport { DEFAULT_THEME } from '../../src/constants';\n\nconst ThemeWrapper = ({ children }) => (\n  <ThemeProvider theme={DEFAULT_THEME}>\n    {children}\n  </ThemeProvider>\n);\n\nexport default ThemeWrapper;"
  },
  {
    "path": "__tests__/helpers/index.ts",
    "content": "export * from './utils';\nexport { default as ThemeWrapper } from './ThemeWrapper';"
  },
  {
    "path": "__tests__/helpers/utils.ts",
    "content": "import type { ReactNode, CSSProperties } from 'react';\nimport type { MultiParams, MenuOption } from '../../src';\nimport type { OptionData, SelectedOption } from '../../src/types';\n\n// ============================================\n// Basic \"options\" & \"selectedOption\" data\n// ============================================\n\nexport type Option = Readonly<{\n  label: string | number;\n  value: string | number;\n}>;\n\nexport const OPTIONS: Option[] = [\n  { value: 1, label: 'Option 1' },\n  { value: 2, label: 'Option 2' }\n];\n\nexport const getSelectedOptionMulti = (): SelectedOption[] => [...OPTIONS];\nexport const getOptionSingle = (index: number = 0): Option => ({ ...OPTIONS[index] });\n\nexport const getSelectedOptionSingle = (): SelectedOption[] => {\n  const data = getOptionSingle();\n  const { value, label } = data;\n  return [{ data, value, label }];\n};\n\n// ============================================\n// \"menuOptions\" data\n// ============================================\n\nexport const MENU_OPTION_SELECTED: MenuOption = {\n  isSelected: true,\n  isDisabled: false,\n  value: 1,\n  label: 'Option 1',\n  data: {\n    value: 1,\n    label: 'Option 1'\n  }\n} as const;\n\nexport const MENU_OPTION_DISABLED: MenuOption = {\n  isDisabled: true,\n  isSelected: false,\n  value: 2,\n  label: 'Option 2',\n  data: {\n    value: 2,\n    label: 'Option 2'\n  }\n} as const;\n\nexport const MENU_OPTIONS: MenuOption[] = [MENU_OPTION_SELECTED, MENU_OPTION_DISABLED];\n\n// ============================================\n// Generic utils & data\n// ============================================\n\nexport const stringifyCSSProperties = (obj: CSSProperties = {}): string => {\n  return Object.keys(obj)\n    .map((key) => `${key}: ${obj[key]};`)\n    .join(' ');\n};\n\nexport const renderMultiOptionsMock = jest.fn(\n  ({selected, renderOptionLabel}: MultiParams): ReactNode => {\n    return selected.map((x) => renderOptionLabel(x.data)).join(', ');\n  }\n);\n\nexport const renderOptionLabelMock = jest.fn(({ label }: OptionData): ReactNode => label);"
  },
  {
    "path": "babel.config.js",
    "content": "module.exports = (api) => {\n  const isTest = api.env('test');\n  const targets = isTest ? { node: 'current' } : undefined;\n\n  const presets = [\n    ['@babel/preset-env', {targets, loose: true}],\n    ['@babel/preset-react', {runtime: 'automatic'}],\n    '@babel/preset-typescript',\n  ];\n\n  const plugins = [\n    [\n      'babel-plugin-styled-components',\n      {\n        ssr: true,\n        pure: true,\n        fileName: false,\n        minify: !isTest,\n        displayName: !isTest,\n        transpileTemplateLiterals: true,\n      },\n    ],\n  ];\n\n  return {\n    presets,\n    plugins\n  };\n};"
  },
  {
    "path": "jest.config.js",
    "content": "module.exports = {\n  transform: {'\\\\.[jt]sx?$': 'babel-jest'},\n  testEnvironment: '<rootDir>/.test/custom-test-env.ts',\n  setupFilesAfterEnv: ['<rootDir>/.test/setup-tests.ts'],\n  testMatch: ['<rootDir>/__tests__/*?(*.)test.{ts,tsx}'],\n  testPathIgnorePatterns: ['/node_modules/', '/dist/', '/src/'],\n};"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-functional-select\",\n  \"version\": \"5.0.0\",\n  \"description\": \"Micro-sized and micro-optimized select component for React.js\",\n  \"main\": \"dist/index.cjs.js\",\n  \"module\": \"dist/index.esm.js\",\n  \"umd\": \"dist/index.umd.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"files\": [\n    \"dist\",\n    \"LICENSE\",\n    \"README.md\"\n  ],\n  \"sideEffects\": false,\n  \"license\": \"MIT\",\n  \"author\": {\n    \"name\": \"Matt Areddia\",\n    \"email\": \"mareddia@proton.me\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/based-ghost/react-functional-select/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/based-ghost/react-functional-select.git\"\n  },\n  \"homepage\": \"https://master--625676b6922472003af898b4.chromatic.com\",\n  \"keywords\": [\n    \"react\",\n    \"react-components\",\n    \"react-functional-select\",\n    \"select\",\n    \"dropdown\",\n    \"styled-components\",\n    \"virtualization\",\n    \"windowing\",\n    \"multi-select\",\n    \"performance\",\n    \"functional\"\n  ],\n  \"engines\": {\n    \"node\": \">= 14\"\n  },\n  \"devDependencies\": {\n    \"@babel/cli\": \"^7.20.7\",\n    \"@babel/core\": \"^7.20.7\",\n    \"@babel/plugin-transform-runtime\": \"^7.19.6\",\n    \"@babel/preset-env\": \"^7.20.2\",\n    \"@babel/preset-react\": \"^7.18.6\",\n    \"@babel/preset-typescript\": \"^7.18.6\",\n    \"@rollup/plugin-babel\": \"^6.0.3\",\n    \"@rollup/plugin-replace\": \"^5.0.2\",\n    \"@rollup/plugin-terser\": \"^0.2.1\",\n    \"@rollup/plugin-typescript\": \"^10.0.1\",\n    \"@storybook/addon-storysource\": \"^6.5.15\",\n    \"@storybook/addons\": \"^6.5.15\",\n    \"@storybook/builder-webpack5\": \"^6.5.15\",\n    \"@storybook/manager-webpack5\": \"^6.5.15\",\n    \"@storybook/react\": \"^6.5.15\",\n    \"@storybook/theming\": \"^6.5.15\",\n    \"@testing-library/jest-dom\": \"^5.16.5\",\n    \"@testing-library/react\": \"^13.4.0\",\n    \"@testing-library/user-event\": \"^14.4.3\",\n    \"@types/jest\": \"^29.2.4\",\n    \"@types/node\": \"^18.11.17\",\n    \"@types/react\": \"^18.0.26\",\n    \"@types/react-dom\": \"^18.0.10\",\n    \"@types/react-window\": \"^1.8.5\",\n    \"@types/styled-components\": \"^5.1.26\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.47.0\",\n    \"@typescript-eslint/parser\": \"^5.47.0\",\n    \"babel-jest\": \"^29.3.1\",\n    \"babel-loader\": \"^9.1.0\",\n    \"babel-plugin-styled-components\": \"^2.0.7\",\n    \"chromatic\": \"^6.14.0\",\n    \"cross-env\": \"^7.0.3\",\n    \"eslint\": \"^8.30.0\",\n    \"eslint-config-prettier\": \"^8.5.0\",\n    \"eslint-plugin-prettier\": \"^4.2.1\",\n    \"eslint-plugin-react\": \"^7.31.11\",\n    \"eslint-plugin-react-hooks\": \"^4.6.0\",\n    \"jest\": \"^29.3.1\",\n    \"jest-environment-jsdom\": \"^29.3.1\",\n    \"npm-run-all\": \"^4.1.5\",\n    \"prettier\": \"^2.8.1\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-syntax-highlighter\": \"^15.5.0\",\n    \"react-toastify\": \"^9.1.1\",\n    \"react-window\": \"^1.8.8\",\n    \"rimraf\": \"^3.0.2\",\n    \"rollup\": \"^3.8.1\",\n    \"styled-components\": \"^5.3.6\",\n    \"typescript\": \"^4.9.4\",\n    \"webpack\": \"^5.75.0\"\n  },\n  \"peerDependencies\": {\n    \"react\": \">=16.8.6\",\n    \"react-dom\": \">=16.8.6\",\n    \"react-window\": \">=1.8.5\",\n    \"styled-components\": \">=4.4.0\"\n  },\n  \"scripts\": {\n    \"clean-build\": \"npm-run-all clean build\",\n    \"build\": \"tsc --outDir dist --declarationDir dist --declaration true --emitDeclarationOnly true && rollup --bundleConfigAsCjs -c\",\n    \"build-watch\": \"rollup -c -w\",\n    \"clean\": \"rimraf dist\",\n    \"typecheck\": \"tsc -p --noEmit\",\n    \"type-check:watch\": \"npm run type-check -- --watch\",\n    \"test\": \"cross-env NODE_ENV=test jest -c jest.config.js\",\n    \"test:watch\": \"cross-env NODE_ENV=test jest -c jest.config.js --watch\",\n    \"start\": \"run-s storybook\",\n    \"storybook\": \"start-storybook -c .storybook -p 9009 --no-manager-cache\",\n    \"build-storybook\": \"build-storybook -c .storybook -o storybook-static\",\n    \"lint\": \"eslint \\\"+(.storybook|__stories__|__tests__|config|src)/**/*.{ts,js}\\\"\",\n    \"chromatic\": \"chromatic --force-rebuild --auto-accept-changes --exit-zero-on-changes\"\n  },\n  \"dependencies\": {\n    \"@babel/runtime\": \"^7.20.7\"\n  }\n}\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import path from 'path';\nimport babel from '@rollup/plugin-babel';\nimport terser from '@rollup/plugin-terser';\nimport replace from '@rollup/plugin-replace';\nimport { DEFAULT_EXTENSIONS } from '@babel/core';\nimport typescript from '@rollup/plugin-typescript';\nimport pkg from './package.json' assert { type: 'json' };\n\nconst globals = {\n  'react': 'React',\n  'react-dom': 'ReactDOM',\n  'styled-components': 'styled',\n  'react-window': 'ReactWindow'\n};\n\nconst input = './src/index.ts';\nconst name = 'ReactFunctionalSelect';\nconst external = (id) => !id.startsWith('\\0') && !id.startsWith('.') && !path.isAbsolute(id);\n\n/**\n * Replace Plugin config\n */\nconst replacePlugin = replace({\n  preventAssignment: true,\n  'process.env.NODE_ENV': JSON.stringify('production'),\n});\n\n// Remove data-testid attribute (since undefined in non-test environments)\n// Perform as final step in transformed, bundled code (for esm and cjs builds)\nconst removeTestIdPlugin = replace({\n  preventAssignment: true,\n  ',\"data-testid\":undefined': '',\n  delimiters: ['', '']\n});\n\n/**\n * Babel Plugin config (prevents use of root babel.config.js with babelrc and configFile as false)\n */\nconst babelPlugin = (useESModules) => {\n  const targets = useESModules ? { esmodules: true } : undefined;\n\n  return babel({\n    babelrc: false,\n    configFile: false,\n    babelHelpers: 'runtime',\n    exclude: 'node_modules/**',\n    extensions: [...DEFAULT_EXTENSIONS, '.ts', '.tsx'],\n    presets: [['@babel/preset-env', {targets, loose: true}], '@babel/preset-react'],\n    plugins: [\n      ['@babel/plugin-transform-runtime', {useESModules}],\n      [\n        'babel-plugin-styled-components',\n        {\n          ssr: true,\n          pure: true,\n          minify: true,\n          fileName: false,\n          displayName: true,\n          transpileTemplateLiterals: true,\n        },\n      ],\n    ],\n  });\n};\n\n/**\n * Common plugins in each build\n */\nconst commonPlugins = (useESModules = true) => [\n  typescript(),\n  replacePlugin,\n  babelPlugin(useESModules),\n  terser(),\n  removeTestIdPlugin,\n];\n\nexport default [\n  // COMMONJS\n  {\n    external,\n    input,\n    output: {\n      file: pkg.main,\n      format: 'cjs',\n      exports: 'named',\n    },\n    plugins: commonPlugins(false),\n  },\n\n  // MODULE\n  {\n    external,\n    input,\n    output: {\n      file: pkg.module,\n      format: 'esm',\n    },\n    plugins: commonPlugins(true),\n  },\n\n  // BROWSER/UMD\n  {\n    external: Object.keys(globals),\n    input,\n    output: {\n      file: pkg.umd,\n      format: 'umd',\n      globals,\n      name,\n    },\n    plugins: commonPlugins(true),\n  },\n];"
  },
  {
    "path": "src/Select.tsx",
    "content": "import React, {\n  useRef,\n  useMemo,\n  useState,\n  useEffect,\n  forwardRef,\n  useCallback,\n  useImperativeHandle,\n  type Ref,\n  type ReactNode,\n  type FormEvent,\n  type FocusEvent,\n  type KeyboardEvent,\n  type SyntheticEvent,\n  type FocusEventHandler\n} from 'react';\nimport {\n  FilterMatchEnum,\n  OptionIndexEnum,\n  MenuPositionEnum,\n  FUNCTIONS,\n  EMPTY_ARRAY,\n  DEFAULT_THEME,\n  PAGE_SIZE_DEFAULT,\n  PLACEHOLDER_DEFAULT,\n  LOADING_MSG_DEFAULT,\n  SELECT_CONTAINER_CLS,\n  CONTROL_CONTAINER_CLS,\n  FOCUSED_OPTION_DEFAULT,\n  NO_OPTIONS_MSG_DEFAULT,\n  MENU_ITEM_SIZE_DEFAULT,\n  MENU_MAX_HEIGHT_DEFAULT,\n  SELECT_CONTAINER_TESTID,\n  CONTROL_CONTAINER_TESTID\n} from './constants';\nimport type {\n  Theme,\n  SelectRef,\n  OptionData,\n  MenuOption,\n  MultiParams,\n  IconRenderer,\n  FocusedOption,\n  SelectedOption,\n  CallbackFn,\n  MouseOrTouchEvent,\n  AriaLiveAttribute,\n  OptionLabelCallback,\n  OptionValueCallback,\n  RenderLabelCallback\n} from './types';\nimport type { FixedSizeList } from 'react-window';\nimport styled, { css, ThemeProvider, type DefaultTheme } from 'styled-components';\nimport { Menu, Value, AriaLiveRegion, AutosizeInput, IndicatorIcons } from './components';\nimport { useDebounce, useLatestRef, useCallbackRef, useMenuOptions, useMountEffect, useUpdateEffect, useMenuPosition } from './hooks';\nimport { isBoolean, isFunction, isPlainObject, mergeDeep, suppressEvent, normalizeValue, isTouchDevice, isArrayWithLength } from './utils';\n\ntype SelectProps = Readonly<{\n  async?: boolean;\n  menuId?: string;\n  inputId?: string;\n  selectId?: string;\n  pageSize?: number;\n  isMulti?: boolean;\n  ariaLabel?: string;\n  required?: boolean;\n  loadingMsg?: string;\n  autoFocus?: boolean;\n  isLoading?: boolean;\n  isInvalid?: boolean;\n  inputDelay?: number;\n  themeConfig?: Theme;\n  isDisabled?: boolean;\n  placeholder?: string;\n  menuItemSize?: number;\n  isClearable?: boolean;\n  memoOptions?: boolean;\n  lazyLoadMenu?: boolean;\n  options?: OptionData[];\n  isSearchable?: boolean;\n  menuMaxHeight?: number;\n  loadingNode?: ReactNode;\n  ariaLabelledBy?: string;\n  clearIcon?: IconRenderer;\n  caretIcon?: IconRenderer;\n  openMenuOnClick?: boolean;\n  openMenuOnFocus?: boolean;\n  menuPortalTarget?: Element;\n  menuOverscanCount?: number;\n  tabSelectsOption?: boolean;\n  filterIgnoreCase?: boolean;\n  menuScrollDuration?: number;\n  blurInputOnSelect?: boolean;\n  closeMenuOnSelect?: boolean;\n  isAriaLiveEnabled?: boolean;\n  menuWidth?: string | number;\n  scrollMenuIntoView?: boolean;\n  noOptionsMsg?: string | null;\n  ariaLive?: AriaLiveAttribute;\n  hideSelectedOptions?: boolean;\n  filterIgnoreAccents?: boolean;\n  onMenuOpen?: CallbackFn;\n  onMenuClose?: CallbackFn;\n  backspaceClearsValue?: boolean;\n  menuPosition?: MenuPositionEnum;\n  filterMatchFrom?: FilterMatchEnum;\n  menuItemDirection?: 'ltr' | 'rtl';\n  itemKeySelector?: string | number;\n  getOptionLabel?: OptionLabelCallback;\n  getOptionValue?: OptionValueCallback;\n  initialValue?: OptionData | OptionData[];\n  onInputChange?: (value?: string) => unknown;\n  onSearchChange?: (value?: string) => unknown;\n  onOptionChange?: (data: OptionData) => unknown;\n  onInputBlur?: FocusEventHandler<HTMLInputElement>;\n  onInputFocus?: FocusEventHandler<HTMLInputElement>;\n  renderOptionLabel?: (data: OptionData) => ReactNode;\n  getIsOptionDisabled?: (data: OptionData) => boolean;\n  getFilterOptionString?: (option: MenuOption) => string;\n  renderMultiOptions?: (params: MultiParams) => ReactNode;\n  onKeyDown?: (e: KeyboardEvent<Element>, input?: string, focusedOption?: FocusedOption) => unknown;\n}>;\n\ntype ValueWrapperProps = Readonly<{\n  flex: boolean;\n}>;\n\ninterface ControlWrapperProps extends Pick<SelectProps, 'isInvalid' | 'isDisabled'> {\n  readonly isFocused: boolean;\n}\n\nconst SelectWrapper = styled.div`\n  position: relative;\n  box-sizing: border-box;\n  ${({ theme }) => theme.select.css}\n`;\n\nconst ValueWrapper = styled.div<ValueWrapperProps>`\n  flex: 1 1 0%;\n  flex-wrap: wrap;\n  overflow: hidden;\n  position: relative;\n  align-items: center;\n  box-sizing: border-box;\n  display: ${({ flex }) => flex ? 'flex' : 'grid'};\n  padding: ${({ theme }) => theme.control.padding};\n`;\n\nconst ControlWrapper = styled.div<ControlWrapperProps>`\n  outline: 0;\n  display: flex;\n  flex-wrap: wrap;\n  cursor: default;\n  position: relative;\n  align-items: center;\n  box-sizing: border-box;\n  justify-content: space-between;\n\n  ${({ isDisabled, isFocused, isInvalid, theme: { control, color } }) => css`\n    transition: ${control.transition};\n    border-style: ${control.borderStyle};\n    border-width: ${control.borderWidth};\n    border-radius: ${control.borderRadius};\n    min-height: ${control.height || control.minHeight};\n\n    border-color: ${isInvalid\n      ? color.danger\n      : isFocused\n      ? control.focusedBorderColor\n      : color.border};\n\n    ${control.height && `height: ${control.height};`}\n    ${isDisabled && 'pointer-events:none;user-select:none;'}\n    ${(control.backgroundColor || isDisabled) && `background-color: ${isDisabled ? color.disabled : control.backgroundColor};`}\n    ${isFocused && `box-shadow: ${control.boxShadow} ${isInvalid ? color.dangerLight : control.boxShadowColor};`}\n  `}\n\n  ${({ theme }) => theme.control.css}\n  ${({ isFocused, theme }) => isFocused && theme.control.focusedCss}\n`;\n\nconst Select = forwardRef<SelectRef, SelectProps>((\n  {\n    async,\n    menuId,\n    isMulti,\n    inputId,\n    selectId,\n    required,\n    ariaLive,\n    autoFocus,\n    isLoading,\n    onKeyDown,\n    clearIcon,\n    caretIcon,\n    isInvalid,\n    ariaLabel,\n    menuWidth,\n    isDisabled,\n    inputDelay,\n    onMenuOpen,\n    onMenuClose,\n    onInputBlur,\n    isClearable,\n    themeConfig,\n    loadingNode,\n    initialValue,\n    onInputFocus,\n    onInputChange,\n    ariaLabelledBy,\n    onOptionChange,\n    onSearchChange,\n    getOptionLabel,\n    getOptionValue,\n    itemKeySelector,\n    openMenuOnFocus,\n    menuPortalTarget,\n    isAriaLiveEnabled,\n    menuOverscanCount,\n    blurInputOnSelect,\n    menuItemDirection,\n    renderOptionLabel,\n    renderMultiOptions,\n    menuScrollDuration,\n    filterIgnoreAccents,\n    hideSelectedOptions,\n    getIsOptionDisabled,\n    getFilterOptionString,\n    isSearchable = true,\n    memoOptions = false,\n    lazyLoadMenu = false,\n    openMenuOnClick = true,\n    filterIgnoreCase = true,\n    tabSelectsOption = true,\n    closeMenuOnSelect = true,\n    scrollMenuIntoView = true,\n    backspaceClearsValue = true,\n    filterMatchFrom = FilterMatchEnum.ANY,\n    menuPosition = MenuPositionEnum.BOTTOM,\n    options = EMPTY_ARRAY,\n    pageSize = PAGE_SIZE_DEFAULT,\n    loadingMsg = LOADING_MSG_DEFAULT,\n    placeholder = PLACEHOLDER_DEFAULT,\n    noOptionsMsg = NO_OPTIONS_MSG_DEFAULT,\n    menuItemSize = MENU_ITEM_SIZE_DEFAULT,\n    menuMaxHeight = MENU_MAX_HEIGHT_DEFAULT\n  },\n  ref: Ref<SelectRef>\n) => {\n  // DOM element refs\n  const listRef = useRef<FixedSizeList | null>(null);\n  const menuRef = useRef<HTMLDivElement | null>(null);\n  const inputRef = useRef<HTMLInputElement | null>(null);\n  const controlRef = useRef<HTMLDivElement | null>(null);\n\n  // Local state values\n  const [inputValue, setInputValue] = useState<string>('');\n  const [menuOpen, setMenuOpen] = useState<boolean>(false);\n  const [isFocused, setIsFocused] = useState<boolean>(false);\n  const [focusedMultiValue, setFocusedMultiValue] = useState<string | number | null>(null);\n  const [focusedOption, setFocusedOption] = useState<FocusedOption>(FOCUSED_OPTION_DEFAULT);\n\n  // Memoized DefaultTheme object for styled-components ThemeProvider\n  const theme = useMemo<DefaultTheme>(() => {\n    return isPlainObject(themeConfig)\n      ? mergeDeep(DEFAULT_THEME, themeConfig)\n      : DEFAULT_THEME;\n  }, [themeConfig]);\n\n  // Memoized callback functions referencing optional function properties on Select.tsx\n  const getOptionLabelFn = useMemo<OptionLabelCallback>(() => getOptionLabel || FUNCTIONS.optionLabel, [getOptionLabel]);\n  const getOptionValueFn = useMemo<OptionValueCallback>(() => getOptionValue || FUNCTIONS.optionValue, [getOptionValue]);\n  const renderOptionLabelFn = useMemo<RenderLabelCallback>(() => renderOptionLabel || getOptionLabelFn, [renderOptionLabel, getOptionLabelFn]);\n\n  // Custom hook abstraction that debounces search input value (opt-in)\n  const debouncedInputValue = useDebounce<string>(inputValue, inputDelay);\n\n  // Custom ref objects\n  const onSearchChangeFn = useCallbackRef(onSearchChange);\n  const onOptionChangeFn = useCallbackRef(onOptionChange);\n  const onSearchChangeIsFn = useLatestRef<boolean>(isFunction(onSearchChange));\n  const onOptionChangeIsFn = useLatestRef<boolean>(isFunction(onOptionChange));\n  const menuOpenRef = useLatestRef<boolean>(menuOpen);\n  const onChangeEvtValue = useRef<boolean>(false);\n  const prevMenuOptionsLength = useRef<number>();\n\n  // If initialValue is specified attempt to initialize, otherwise default to []\n  const [selectedOption, setSelectedOption] = useState<SelectedOption[]>(\n    () => normalizeValue(\n      initialValue,\n      getOptionValueFn,\n      getOptionLabelFn\n    )\n  );\n\n  // Custom hook abstraction that handles the creation of menuOptions\n  const menuOptions = useMenuOptions(\n    options,\n    debouncedInputValue,\n    filterMatchFrom,\n    selectedOption,\n    getOptionValueFn,\n    getOptionLabelFn,\n    getIsOptionDisabled,\n    getFilterOptionString,\n    filterIgnoreCase,\n    filterIgnoreAccents,\n    isMulti,\n    async,\n    hideSelectedOptions\n  );\n\n  // Custom hook abstraction that handles calculating menuHeightCalc (defaults to menuMaxHeight) / handles executing callbacks/logic on menuOpen state change.\n  const { menuStyleTop, menuHeightCalc } = useMenuPosition(\n    menuRef,\n    controlRef,\n    menuOpen,\n    menuPosition,\n    menuItemSize,\n    menuMaxHeight,\n    menuOptions.length,\n    !!menuPortalTarget,\n    onMenuOpen,\n    onMenuClose,\n    menuScrollDuration,\n    scrollMenuIntoView\n  );\n\n  const blurInput = (): void => inputRef.current?.blur();\n  const focusInput = (): void => inputRef.current?.focus();\n  const scrollToItemIndex = (idx: number): void => listRef.current?.scrollToItem(idx);\n  const hasSelectedOptions = isArrayWithLength(selectedOption);\n\n  const openMenuAndFocusOption = useCallback((position: OptionIndexEnum): void => {\n    if (!isArrayWithLength(menuOptions)) {\n      setMenuOpen(true);\n      return;\n    }\n\n    const selectedIdx = !isMulti\n      ? menuOptions.findIndex((x) => x.isSelected)\n      : -1;\n\n    const index = selectedIdx > -1\n      ? selectedIdx\n      : position === OptionIndexEnum.FIRST\n        ? 0\n        : menuOptions.length - 1;\n\n    scrollToItemIndex(index);\n    setMenuOpen(true);\n    setFocusedMultiValue(null);\n    setFocusedOption({ index, ...menuOptions[index] });\n  }, [isMulti, menuOptions]);\n\n  const removeSelectedOption = useCallback((value?: string | number): void => {\n    setSelectedOption((so) => so.filter((x) => x.value !== value));\n  }, []);\n\n  const selectOption = useCallback((option: MenuOption): void => {\n    if (option.isDisabled) return;\n\n    if (option.isSelected) {\n      isMulti && removeSelectedOption(option.value);\n    } else {\n      const { isSelected, isDisabled, ...selectedOpt } = option;\n      setSelectedOption((so) => !isMulti ? [selectedOpt] : [...so, selectedOpt]);\n    }\n\n    const blurOrDefault = isBoolean(blurInputOnSelect) ? blurInputOnSelect : isTouchDevice();\n    if (blurOrDefault) {\n      blurInput();\n    } else if (closeMenuOnSelect) {\n      setInputValue('');\n      setMenuOpen(false);\n    }\n  }, [isMulti, closeMenuOnSelect, blurInputOnSelect, removeSelectedOption]);\n\n  /**\n   * `React.useImperativeHandle`\n   *\n   * Exposed API methods/properties available on a ref instance of this Select.tsx component.\n   * Dependency list passed as the third param to re-create the handle when one of them updates.\n   */\n  useImperativeHandle(\n    ref,\n    () => ({\n      menuOpen: menuOpenRef.current,\n      blur: blurInput,\n      focus: focusInput,\n      clearValue: () => {\n        setSelectedOption(EMPTY_ARRAY);\n        setFocusedOption(FOCUSED_OPTION_DEFAULT);\n      },\n      setValue: (option?: OptionData) => {\n        setSelectedOption(\n          normalizeValue(option, getOptionValueFn, getOptionLabelFn)\n        );\n      },\n      toggleMenu: (state?: boolean) => {\n        if (state || (state === undefined && !menuOpenRef.current)) {\n          focusInput();\n          openMenuAndFocusOption(OptionIndexEnum.FIRST);\n        } else {\n          blurInput();\n        }\n      }\n    }),\n    [menuOpenRef, getOptionValueFn, getOptionLabelFn, openMenuAndFocusOption]\n  );\n\n  /**\n   * `useMountEffect`\n   *\n   * If 'autoFocus' true, focus the control following initial mount\n   */\n  useMountEffect(() => {\n    if (autoFocus) {\n      focusInput();\n    }\n  });\n\n  /**\n   * `React.useEffect`\n   *\n   * If 'onSearchChange' function is defined, run as callback when the stateful debouncedInputValue\n   * updates check if onChangeEvtValue ref is set true, which indicates the inputValue change was triggered by input change event\n   */\n  useEffect(() => {\n    if (onSearchChangeIsFn.current && onChangeEvtValue.current) {\n      onChangeEvtValue.current = false;\n      onSearchChangeFn(debouncedInputValue);\n    }\n  }, [onSearchChangeFn, onSearchChangeIsFn, debouncedInputValue]);\n\n  /**\n   * `useUpdateEffect`\n   *\n   * Handle passing 'selectedOption' value(s) to onOptionChange callback function prop (if defined)\n   */\n  useUpdateEffect(() => {\n    if (onOptionChangeIsFn.current) {\n      const normalSelectedOpts = isMulti\n        ? selectedOption.map((x) => x.data)\n        : isArrayWithLength(selectedOption)\n          ? selectedOption[0].data\n          : null;\n\n      onOptionChangeFn(normalSelectedOpts);\n    }\n  }, [onOptionChangeFn, onOptionChangeIsFn, isMulti, selectedOption]);\n\n  /**\n   * `useUpdateEffect`\n   *\n   * Handle clearing focused option if menuOptions array has 0 length;\n   * Handle menuOptions changes - conditionally focus first option and do scroll to first option;\n   * Handle reseting scroll pos to first item after the previous search returned zero results (use prevMenuOptionsLen)\n   * ...or if there is a selected item and menuOptions is restored to include it, give it focus\n   */\n  useUpdateEffect(() => {\n    const curLength = menuOptions.length;\n    const { current: prevLength } = prevMenuOptionsLength;\n    const inputChanged = curLength > 0 && (async || curLength !== options.length || prevLength === 0);\n    const menuOpenAndOptionsGrew = menuOpenRef.current && prevLength !== undefined && prevLength < curLength;\n\n    if (curLength === 0) {\n      setFocusedOption(FOCUSED_OPTION_DEFAULT);\n    } else if (curLength === 1 || inputChanged || menuOpenAndOptionsGrew) {\n      const index = Math.max(0, menuOptions.findIndex((x) => x.isSelected));\n      scrollToItemIndex(index);\n      setFocusedOption({ index, ...menuOptions[index] });\n    }\n\n    prevMenuOptionsLength.current = curLength;\n  }, [async, options, menuOpenRef, menuOptions]);\n\n  const selectOptionFromFocused = (): void => {\n    const { index, ...menuOpt } = focusedOption;\n    if (menuOpt.data) {\n      selectOption(menuOpt as MenuOption);\n    }\n  };\n\n  // only Multiselect mode supports value focusing (ArrowRight || ArrowLeft)\n  const focusValueOnArrowKey = (key: string): void => {\n    if (!hasSelectedOptions) return;\n\n    let focusedIdx = -1;\n    const lastValueIdx = selectedOption.length - 1;\n    const curFocusedIdx = focusedMultiValue ? selectedOption.findIndex((x) => x.value === focusedMultiValue) : -1;\n\n    if (key === 'ArrowRight') {\n      focusedIdx = (curFocusedIdx > -1 && curFocusedIdx < lastValueIdx)\n        ? curFocusedIdx + 1\n        : -1;\n    } else {\n      focusedIdx = curFocusedIdx !== 0\n        ? curFocusedIdx === -1\n          ? lastValueIdx\n          : curFocusedIdx - 1\n        : 0;\n    }\n\n    const nextFocusedVal = focusedIdx > -1\n      ? selectedOption[focusedIdx].value!\n      : null;\n\n    if (focusedOption.data)\n      setFocusedOption(FOCUSED_OPTION_DEFAULT);\n    if (nextFocusedVal !== focusedMultiValue)\n      setFocusedMultiValue(nextFocusedVal);\n  };\n\n  const focusOptionOnArrowKey = (direction: OptionIndexEnum): void => {\n    if (!isArrayWithLength(menuOptions)) return;\n\n    let index = focusedOption.index;\n    switch (direction) {\n      case OptionIndexEnum.UP: {\n        index = (focusedOption.index > 0) ? focusedOption.index - 1 : menuOptions.length - 1;\n        break;\n      }\n      case OptionIndexEnum.DOWN: {\n        index = (focusedOption.index + 1) % menuOptions.length;\n        break;\n      }\n      case OptionIndexEnum.PAGEUP: {\n        const pageIdx = focusedOption.index - pageSize;\n        index = (pageIdx < 0) ? 0 : pageIdx;\n        break;\n      }\n      case OptionIndexEnum.PAGEDOWN: {\n        const pageIdx = focusedOption.index + pageSize;\n        index = (pageIdx > menuOptions.length - 1) ? menuOptions.length - 1 : pageIdx;\n        break;\n      }\n    }\n\n    scrollToItemIndex(index);\n    focusedMultiValue && setFocusedMultiValue(null);\n    setFocusedOption({ index, ...menuOptions[index] });\n  };\n\n  const handleOnKeyDown = (e: KeyboardEvent<HTMLElement>): void => {\n    if (isDisabled) return;\n\n    if (onKeyDown) {\n      onKeyDown(e, inputValue, focusedOption);\n      if (e.defaultPrevented) return;\n    }\n\n    switch (e.key) {\n      case 'ArrowDown': {\n        menuOpen ? focusOptionOnArrowKey(OptionIndexEnum.DOWN) : openMenuAndFocusOption(OptionIndexEnum.FIRST);\n        break;\n      }\n      case 'ArrowUp': {\n        menuOpen ? focusOptionOnArrowKey(OptionIndexEnum.UP) : openMenuAndFocusOption(OptionIndexEnum.LAST);\n        break;\n      }\n      case 'ArrowLeft':\n      case 'ArrowRight': {\n        if (!isMulti || inputValue || renderMultiOptions) return;\n        focusValueOnArrowKey(e.key);\n        break;\n      }\n      case 'PageUp': {\n        if (!menuOpen) return;\n        focusOptionOnArrowKey(OptionIndexEnum.PAGEUP);\n        break;\n      }\n      case 'PageDown': {\n        if (!menuOpen) return;\n        focusOptionOnArrowKey(OptionIndexEnum.PAGEDOWN);\n        break;\n      }\n      // handle spacebar keydown events\n      case ' ': {\n        if (inputValue) return;\n\n        if (!menuOpen) {\n          openMenuAndFocusOption(OptionIndexEnum.FIRST);\n        } else if (!focusedOption.data) {\n          return;\n        } else {\n          selectOptionFromFocused();\n        }\n\n        break;\n      }\n      case 'Enter': {\n        if (!menuOpen) return;\n        selectOptionFromFocused();\n        break;\n      }\n      case 'Escape': {\n        if (menuOpen) {\n          setMenuOpen(false);\n          setInputValue('');\n        }\n        break;\n      }\n      case 'Tab': {\n        if (e.shiftKey || !menuOpen || !tabSelectsOption || !focusedOption.data) {\n          return;\n        }\n        selectOptionFromFocused();\n        break;\n      }\n      case 'Delete':\n      case 'Backspace': {\n        if (inputValue) return;\n\n        if (focusedMultiValue) {\n          const focusedIdx = selectedOption.findIndex((x) => x.value === focusedMultiValue);\n          const nextFocusedMultiValue = (focusedIdx > -1 && (focusedIdx < (selectedOption.length - 1)))\n            ? selectedOption[focusedIdx + 1].value!\n            : null;\n\n          removeSelectedOption(focusedMultiValue);\n          setFocusedMultiValue(nextFocusedMultiValue);\n        } else {\n          if (!backspaceClearsValue) return;\n          if (!hasSelectedOptions) break;\n\n          if (isMulti && !renderMultiOptions) {\n            const { value } = selectedOption[selectedOption.length - 1];\n            removeSelectedOption(value);\n          } else if (isClearable) {\n            setSelectedOption(EMPTY_ARRAY);\n          }\n        }\n\n        break;\n      }\n      default:\n        return;\n    }\n\n    e.preventDefault();\n  };\n\n  const handleOnControlMouseDown = (e: MouseOrTouchEvent<HTMLElement>): void => {\n    if (isDisabled) return;\n    if (!isFocused) focusInput();\n\n    const isNotInput = (e.target as HTMLElement).nodeName !== 'INPUT';\n    if (!menuOpen) {\n      openMenuOnClick && openMenuAndFocusOption(OptionIndexEnum.FIRST);\n    } else if (isNotInput) {\n      setMenuOpen(false);\n      setInputValue('');\n    }\n\n    if (isNotInput) e.preventDefault();\n  };\n\n  const handleOnInputBlur = (e: FocusEvent<HTMLInputElement>): void => {\n    onInputBlur?.(e);\n    setIsFocused(false);\n    setMenuOpen(false);\n    setInputValue('');\n  };\n\n  const handleOnInputFocus = (e: FocusEvent<HTMLInputElement>): void => {\n    onInputFocus?.(e);\n    setIsFocused(true);\n    if (openMenuOnFocus) {\n      openMenuAndFocusOption(OptionIndexEnum.FIRST);\n    }\n  };\n\n  const handleOnInputChange = (e: FormEvent<HTMLInputElement>): void => {\n    onChangeEvtValue.current = true;\n    onInputChange?.(e.currentTarget.value);\n    setInputValue(e.currentTarget.value);\n    setMenuOpen(true);\n  };\n\n  // React Hooks linter rules require that this function be memoized in\n  // ...order to be referenced in deps array of callback functions below\n  const handleOnMouseDown = useCallback((e: SyntheticEvent<HTMLElement>): void => {\n    suppressEvent(e);\n    focusInput();\n  }, []);\n\n  const handleOnClearMouseDown = useCallback((e: MouseOrTouchEvent<HTMLElement>): void => {\n    handleOnMouseDown(e);\n    setSelectedOption(EMPTY_ARRAY);\n  }, [handleOnMouseDown]);\n\n  const handleOnCaretMouseDown = useCallback((e: MouseOrTouchEvent<HTMLElement>): void => {\n    if (!isDisabled && !openMenuOnClick) {\n      handleOnMouseDown(e);\n      menuOpenRef.current ? setMenuOpen(false) : openMenuAndFocusOption(OptionIndexEnum.FIRST);\n    }\n  }, [isDisabled, menuOpenRef, openMenuOnClick, handleOnMouseDown, openMenuAndFocusOption]);\n\n  const flexValueWrapper = !!isMulti && hasSelectedOptions;\n  const showClear = !!isClearable && !isDisabled && hasSelectedOptions;\n\n  return (\n    <ThemeProvider theme={theme}>\n      <SelectWrapper\n        id={selectId}\n        onKeyDown={handleOnKeyDown}\n        className={SELECT_CONTAINER_CLS}\n        data-testid={SELECT_CONTAINER_TESTID}\n      >\n        <ControlWrapper\n          ref={controlRef}\n          isInvalid={isInvalid}\n          isFocused={isFocused}\n          isDisabled={isDisabled}\n          className={CONTROL_CONTAINER_CLS}\n          onTouchEnd={handleOnControlMouseDown}\n          onMouseDown={handleOnControlMouseDown}\n          data-testid={CONTROL_CONTAINER_TESTID}\n        >\n          <ValueWrapper flex={flexValueWrapper}>\n            <Value\n              isMulti={isMulti}\n              hasInput={!!inputValue}\n              placeholder={placeholder}\n              selectedOption={selectedOption}\n              focusedMultiValue={focusedMultiValue}\n              renderMultiOptions={renderMultiOptions}\n              renderOptionLabel={renderOptionLabelFn}\n              removeSelectedOption={removeSelectedOption}\n            />\n            <AutosizeInput\n              id={inputId}\n              ref={inputRef}\n              menuId={menuId}\n              menuOpen={menuOpen}\n              required={required}\n              ariaLabel={ariaLabel}\n              isInvalid={isInvalid}\n              inputValue={inputValue}\n              onBlur={handleOnInputBlur}\n              onFocus={handleOnInputFocus}\n              onChange={handleOnInputChange}\n              ariaLabelledBy={ariaLabelledBy}\n              hasSelectedOptions={hasSelectedOptions}\n              readOnly={!isSearchable || !!focusedMultiValue}\n            />\n          </ValueWrapper>\n          <IndicatorIcons\n            menuOpen={menuOpen}\n            clearIcon={clearIcon}\n            caretIcon={caretIcon}\n            isInvalid={isInvalid}\n            isLoading={isLoading}\n            showClear={showClear}\n            isDisabled={isDisabled}\n            loadingNode={loadingNode}\n            onClearMouseDown={handleOnClearMouseDown}\n            onCaretMouseDown={handleOnCaretMouseDown}\n          />\n        </ControlWrapper>\n        <Menu\n          id={menuId}\n          menuRef={menuRef}\n          menuOpen={menuOpen}\n          isLoading={isLoading}\n          menuTop={menuStyleTop}\n          height={menuHeightCalc}\n          itemSize={menuItemSize}\n          loadingMsg={loadingMsg}\n          menuOptions={menuOptions}\n          memoOptions={memoOptions}\n          fixedSizeListRef={listRef}\n          lazyLoadMenu={lazyLoadMenu}\n          noOptionsMsg={noOptionsMsg}\n          selectOption={selectOption}\n          direction={menuItemDirection}\n          itemKeySelector={itemKeySelector}\n          overscanCount={menuOverscanCount}\n          menuPortalTarget={menuPortalTarget}\n          onMenuMouseDown={handleOnMouseDown}\n          width={menuWidth || theme.menu.width}\n          renderOptionLabel={renderOptionLabelFn}\n          focusedOptionIndex={focusedOption.index}\n        />\n        {isAriaLiveEnabled && (\n          <AriaLiveRegion\n            ariaLive={ariaLive}\n            menuOpen={menuOpen}\n            isFocused={isFocused}\n            ariaLabel={ariaLabel}\n            inputValue={inputValue}\n            isSearchable={isSearchable}\n            focusedOption={focusedOption}\n            selectedOption={selectedOption}\n            optionCount={menuOptions.length}\n          />\n        )}\n      </SelectWrapper>\n    </ThemeProvider>\n  );\n});\n\nSelect.displayName = 'Select';\n\nexport default Select;"
  },
  {
    "path": "src/components/AriaLiveRegion/index.tsx",
    "content": "import React from 'react';\nimport styled from 'styled-components';\nimport { ARIA_LIVE_SELECTION_ID, ARIA_LIVE_CONTEXT_ID } from '../../constants';\nimport type { FocusedOption, SelectedOption, AriaLiveAttribute } from '../../types';\n\ntype AriaLiveRegionProps = Readonly<{\n  menuOpen: boolean;\n  isFocused: boolean;\n  ariaLabel?: string;\n  inputValue: string;\n  optionCount: number;\n  isSearchable: boolean;\n  ariaLive?: AriaLiveAttribute;\n  focusedOption: FocusedOption;\n  selectedOption: SelectedOption[];\n}>;\n\nconst A11yText = styled.span`\n  border: 0;\n  padding: 0;\n  width: 1px;\n  height: 1px;\n  z-index: 9999;\n  overflow: hidden;\n  position: absolute;\n  white-space: nowrap;\n  clip-path: inset(50%);\n  clip: rect(1px, 1px, 1px, 1px);\n`;\n\nconst AriaLiveRegion: React.FC<AriaLiveRegionProps> = ({\n  menuOpen,\n  isFocused,\n  inputValue,\n  optionCount,\n  isSearchable,\n  focusedOption,\n  selectedOption,\n  ariaLive = 'polite',\n  ariaLabel = 'Select'\n}) => {\n  if (!isFocused) {\n    return null;\n  }\n\n  // message contents for \"aria-context\"\n  const { index, label, isDisabled, isSelected } = focusedOption;\n\n  const menuMsg = menuOpen\n    ? `Use Up and Down to choose options${isDisabled ? '' : ', press Enter or Tab to select the currently focused option'}, press Escape to close the menu.`\n    : `${ariaLabel} is focused${isSearchable ? ', type to filter options' : ''}, press Down arrow key to open the menu.`;\n\n  const focusedMsg = label\n    ? `Option ${label} is ${isSelected ? 'selected' : 'focused'}${isDisabled ? ' disabled' : ''}, ${index + 1} of ${optionCount}.`\n    : '';\n\n  const optionsMsg = `${optionCount} option(s) available${inputValue ? (' for search ' + inputValue) : ''}.`;\n  const ariaContextMsg = `${focusedMsg} ${optionsMsg} ${menuMsg}`.trimStart();\n\n  // message contents for \"aria-selection\" SPAN\n  const selectedLbls = selectedOption.length ? selectedOption.map((x) => x.label).join(' ') : 'N/A';\n  const selectionMsg = `Selected option: ${selectedLbls}`;\n\n  return (\n    <A11yText\n      aria-atomic=\"false\"\n      aria-live={ariaLive}\n      aria-relevant=\"additions text\"\n    >\n      <span id={ARIA_LIVE_SELECTION_ID}>\n        {selectionMsg}\n      </span>\n      <span id={ARIA_LIVE_CONTEXT_ID}>\n        {ariaContextMsg}\n      </span>\n    </A11yText>\n  );\n};\n\nexport default AriaLiveRegion;"
  },
  {
    "path": "src/components/AutosizeInput/index.tsx",
    "content": "import styled, { css } from 'styled-components';\nimport { AUTOSIZE_INPUT_ATTRS } from '../../constants';\nimport React, { forwardRef, type Ref, type FormEventHandler, type FocusEventHandler } from 'react';\n\ntype AutosizeInputProps = Readonly<{\n  id?: string;\n  menuId?: string;\n  menuOpen: boolean;\n  readOnly: boolean;\n  ariaLabel?: string;\n  inputValue: string;\n  required?: boolean;\n  isInvalid?: boolean;\n  ariaLabelledBy?: string;\n  hasSelectedOptions: boolean;\n  onBlur: FocusEventHandler<HTMLInputElement>;\n  onFocus: FocusEventHandler<HTMLInputElement>;\n  onChange: FormEventHandler<HTMLInputElement>;\n}>;\n\ntype InputProps = Pick<AutosizeInputProps, 'isInvalid'>;\n\nconst INPUT_BASE_STYLE = css`\n  border: 0;\n  margin: 0;\n  outline: 0;\n  padding: 0;\n  font: inherit;\n  min-width: 2px;\n  grid-area: 1 / 2 / auto / auto;\n`;\n\nconst InputWrapper = styled.div`\n  margin: 2px;\n  flex: 1 1 auto;\n  display: inline-grid;\n  box-sizing: border-box;\n  grid-area: 1 / 1 / 2 / 3;\n  grid-template-columns: 0 min-content;\n\n  :after {\n    white-space: pre;\n    visibility: hidden;\n    content: attr(data-value) \" \";\n    ${INPUT_BASE_STYLE}\n  }\n`;\n\nconst Input = styled.input.attrs(AUTOSIZE_INPUT_ATTRS)<InputProps>`\n  width: 100%;\n  background: 0;\n  color: inherit;\n  ${INPUT_BASE_STYLE}\n\n  :read-only {\n    cursor: default;\n  }\n\n  ${({ theme }) => theme.input.css}\n  ${({ theme, isInvalid }) => isInvalid && theme.input.cssRequired}\n`;\n\nconst AutosizeInput = forwardRef<HTMLInputElement, AutosizeInputProps>((\n  {\n    id,\n    menuId,\n    onBlur,\n    onFocus,\n    onChange,\n    readOnly,\n    required,\n    menuOpen,\n    ariaLabel,\n    isInvalid,\n    inputValue,\n    ariaLabelledBy\n  },\n  ref: Ref<HTMLInputElement>\n) => (\n  <InputWrapper data-value={inputValue}>\n    <Input\n      id={id}\n      ref={ref}\n      onBlur={onBlur}\n      onFocus={onFocus}\n      value={inputValue}\n      aria-owns={menuId}\n      readOnly={readOnly}\n      isInvalid={isInvalid}\n      aria-controls={menuId}\n      aria-label={ariaLabel}\n      aria-expanded={menuOpen}\n      aria-required={required}\n      aria-labelledby={ariaLabelledBy}\n      onChange={!readOnly ? onChange : undefined}\n    />\n  </InputWrapper>\n));\n\nAutosizeInput.displayName = 'AutosizeInput';\n\nexport default AutosizeInput;\n"
  },
  {
    "path": "src/components/IndicatorIcons/ClearIcon.tsx",
    "content": "import React from 'react';\nimport styled, { css } from 'styled-components';\nimport { CLEAR_ICON_CLS } from '../../constants';\n\nconst ClearSvg = styled.svg`\n  fill: currentColor;\n  ${({ theme }) => css`\n    width: ${theme.icon.clear.width};\n    height: ${theme.icon.clear.height};\n    animation: ${theme.icon.clear.animation};\n    transition: ${theme.icon.clear.transition};\n  `}\n`;\n\nconst ClearIcon: React.FC = () => (\n  <ClearSvg\n    aria-hidden\n    focusable=\"false\"\n    viewBox=\"0 0 14 16\"\n    className={CLEAR_ICON_CLS}\n  >\n    <path\n      fillRule=\"evenodd\"\n      d=\"M7.71 8.23l3.75 3.75-1.48 1.48-3.75-3.75-3.75 3.75L1 11.98l3.75-3.75L1 4.48 2.48 3l3.75 3.75L9.98 3l1.48 1.48-3.75 3.75z\"\n    />\n  </ClearSvg>\n);\n\nexport default ClearIcon;\n"
  },
  {
    "path": "src/components/IndicatorIcons/LoadingDots.tsx",
    "content": "import React from 'react';\nimport styled, { css } from 'styled-components';\nimport { LOADING_DOTS_CLS } from '../../constants';\n\nconst StyledLoadingDots = styled.div`\n  display: flex;\n  align-self: center;\n  text-align: center;\n  margin-right: 0.25rem;\n  padding: ${({ theme }) => theme.loader.padding};\n\n  > div {\n    border-radius: 100%;\n    display: inline-block;\n    ${({ theme }) => css`\n      width: ${theme.loader.size};\n      height: ${theme.loader.size};\n      animation: ${theme.loader.animation};\n      background-color: ${theme.loader.color};\n    `}\n\n    :nth-of-type(1) {\n      animation-delay: -0.272s;\n    }\n\n    :nth-of-type(2) {\n      animation-delay: -0.136s;\n    }\n  }\n`;\n\nconst LoadingDots: React.FC = () => (\n  <StyledLoadingDots\n    aria-hidden\n    className={LOADING_DOTS_CLS}\n  >\n    <div />\n    <div />\n    <div />\n  </StyledLoadingDots>\n);\n\nexport default LoadingDots;"
  },
  {
    "path": "src/components/IndicatorIcons/index.tsx",
    "content": "import React, { memo, type ReactNode } from 'react';\nimport ClearIcon from './ClearIcon';\nimport LoadingDots from './LoadingDots';\nimport { isFunction } from '../../utils';\nimport styled, { css } from 'styled-components';\nimport type { IconRenderer, MouseOrTouchEventHandler } from '../../types';\nimport { CARET_ICON_CLS, CLEAR_ICON_TESTID, CARET_ICON_TESTID } from '../../constants';\n\ntype IndicatorIconsProps = Readonly<{\n  menuOpen: boolean;\n  showClear: boolean;\n  isLoading?: boolean;\n  isInvalid?: boolean;\n  isDisabled?: boolean;\n  loadingNode?: ReactNode;\n  clearIcon?: IconRenderer;\n  caretIcon?: IconRenderer;\n  onClearMouseDown: MouseOrTouchEventHandler;\n  onCaretMouseDown: MouseOrTouchEventHandler;\n}>;\n\ntype CaretProps = Pick<IndicatorIconsProps, 'menuOpen' | 'isInvalid'>;\n\nconst IndicatorIconsWrapper = styled.div`\n  display: flex;\n  flex-shrink: 0;\n  align-items: center;\n  align-self: stretch;\n  box-sizing: border-box;\n\n  > span {\n    width: 1px;\n    margin: 8px 0;\n    align-self: stretch;\n    box-sizing: border-box;\n    background-color: ${({ theme }) => theme.color.iconSeparator || theme.color.border};\n  }\n`;\n\nconst IndicatorIcon = styled.div`\n  height: 100%;\n  display: flex;\n  align-items: center;\n  box-sizing: border-box;\n  color: ${({ theme }) => theme.icon.color};\n  padding: ${({ theme }) => theme.icon.padding};\n\n  :hover {\n    color: ${({ theme }) => theme.icon.hoverColor};\n  }\n\n  ${({ theme }) => theme.icon.css}\n`;\n\nconst Caret = styled.div<CaretProps>`\n  transition: ${({ theme }) => theme.icon.caret.transition};\n  border-top: ${({ theme }) => theme.icon.caret.size} dashed;\n  border-left: ${({ theme }) => theme.icon.caret.size} solid transparent;\n  border-right: ${({ theme }) => theme.icon.caret.size} solid transparent;\n  ${({ theme, menuOpen, isInvalid }) =>\n    menuOpen &&\n    css`\n      transform: rotate(180deg);\n      color: ${isInvalid ? theme.color.danger : theme.color.caretActive || theme.color.primary};\n    `}\n`;\n\nconst IndicatorIcons = memo<IndicatorIconsProps>(({\n  menuOpen,\n  clearIcon,\n  caretIcon,\n  isInvalid,\n  showClear,\n  isLoading,\n  isDisabled,\n  loadingNode,\n  onCaretMouseDown,\n  onClearMouseDown\n}) => {\n  const iconRenderer = (renderer: IconRenderer): ReactNode => {\n    return isFunction(renderer)\n      ? renderer({ menuOpen, isLoading, isInvalid, isDisabled })\n      : renderer;\n  };\n\n  return (\n    <IndicatorIconsWrapper>\n      {showClear && !isLoading && (\n        <IndicatorIcon\n          onTouchEnd={onClearMouseDown}\n          onMouseDown={onClearMouseDown}\n          data-testid={CLEAR_ICON_TESTID}\n        >\n          {iconRenderer(clearIcon) || <ClearIcon />}\n        </IndicatorIcon>\n      )}\n      {isLoading && (loadingNode || <LoadingDots />)}\n      <span />\n      <IndicatorIcon\n        onTouchEnd={onCaretMouseDown}\n        onMouseDown={onCaretMouseDown}\n        data-testid={CARET_ICON_TESTID}\n      >\n        {iconRenderer(caretIcon) || (\n          <Caret\n            aria-hidden\n            menuOpen={menuOpen}\n            isInvalid={isInvalid}\n            className={CARET_ICON_CLS}\n          />\n        )}\n      </IndicatorIcon>\n    </IndicatorIconsWrapper>\n  );\n});\n\nIndicatorIcons.displayName = 'IndicatorIcons';\n\nexport default IndicatorIcons;\n"
  },
  {
    "path": "src/components/Menu/MenuList.tsx",
    "content": "import React, { useMemo, Fragment, type MutableRefObject } from 'react';\r\nimport Option from './Option';\r\nimport styled from 'styled-components';\r\nimport { isArrayWithLength } from '../../utils';\r\nimport { FixedSizeList, type ListItemKeySelector } from 'react-window';\r\nimport type { MenuOption, ItemData, RenderLabelCallback } from '../../types';\r\n\r\nexport type MenuListProps = Readonly<{\r\n  height: number;\r\n  itemSize: number;\r\n  loadingMsg: string;\r\n  isLoading?: boolean;\r\n  memoOptions: boolean;\r\n  overscanCount?: number;\r\n  width: string | number;\r\n  direction?: 'ltr' | 'rtl';\r\n  menuOptions: MenuOption[];\r\n  focusedOptionIndex: number;\r\n  noOptionsMsg: string | null;\r\n  itemKeySelector?: string | number;\r\n  renderOptionLabel: RenderLabelCallback;\r\n  selectOption: (option: MenuOption) => void;\r\n  fixedSizeListRef: MutableRefObject<FixedSizeList | null> | undefined;\r\n}>;\r\n\r\nconst NoOptionsMsg = styled.div`\r\n  text-align: center;\r\n  color: ${({ theme }) => theme.noOptions.color};\r\n  margin: ${({ theme }) => theme.noOptions.margin};\r\n  padding: ${({ theme }) => theme.noOptions.padding};\r\n  font-size: ${({ theme }) => theme.noOptions.fontSize};\r\n  ${({ theme }) => theme.noOptions.css}\r\n`;\r\n\r\nconst MenuList: React.FC<MenuListProps> = ({\r\n  width,\r\n  height,\r\n  itemSize,\r\n  direction,\r\n  isLoading,\r\n  loadingMsg,\r\n  menuOptions,\r\n  memoOptions,\r\n  selectOption,\r\n  noOptionsMsg,\r\n  overscanCount,\r\n  itemKeySelector,\r\n  fixedSizeListRef,\r\n  renderOptionLabel,\r\n  focusedOptionIndex\r\n}) => {\r\n  const itemData = useMemo<ItemData>(() => ({\r\n    menuOptions,\r\n    memoOptions,\r\n    selectOption,\r\n    renderOptionLabel,\r\n    focusedOptionIndex\r\n  }), [menuOptions, memoOptions, focusedOptionIndex, selectOption, renderOptionLabel]);\r\n\r\n  if (isLoading) {\r\n    return <NoOptionsMsg>{loadingMsg}</NoOptionsMsg>;\r\n  }\r\n\r\n  const itemKey: ListItemKeySelector | undefined = itemKeySelector\r\n    ? (index, data) => data.menuOptions[index][itemKeySelector]\r\n    : undefined;\r\n\r\n  return (\r\n    <Fragment>\r\n      <FixedSizeList\r\n        width={width}\r\n        height={height}\r\n        itemKey={itemKey}\r\n        itemSize={itemSize}\r\n        itemData={itemData}\r\n        direction={direction}\r\n        ref={fixedSizeListRef}\r\n        overscanCount={overscanCount}\r\n        itemCount={menuOptions.length}\r\n      >\r\n        {Option}\r\n      </FixedSizeList>\r\n      {!isArrayWithLength(menuOptions) && noOptionsMsg && (\r\n        <NoOptionsMsg>{noOptionsMsg}</NoOptionsMsg>\r\n      )}\r\n    </Fragment>\r\n  );\r\n};\r\n\r\nexport default MenuList;\r\n"
  },
  {
    "path": "src/components/Menu/Option.tsx",
    "content": "import React, { memo, type CSSProperties } from 'react';\r\nimport { areEqual } from 'react-window';\r\nimport type { ItemData } from '../../types';\r\nimport { buildOptionClass } from '../../utils';\r\n\r\ntype OptionProps = Readonly<{\r\n  index: number;\r\n  data: ItemData;\r\n  style: CSSProperties;\r\n}>;\r\n\r\n// extends react-window 'areEqual'\r\nconst _areEqual = (\r\n  prevProps: OptionProps,\r\n  nextProps: OptionProps\r\n): boolean => {\r\n  const { memoOptions } = nextProps.data;\r\n  return memoOptions && areEqual(prevProps, nextProps);\r\n};\r\n\r\nconst Option = memo<OptionProps>(({\r\n  index,\r\n  style,\r\n  data: {\r\n    menuOptions,\r\n    selectOption,\r\n    renderOptionLabel,\r\n    focusedOptionIndex\r\n  }\r\n}) => {\r\n  const opt = menuOptions[index];\r\n\r\n  const className = buildOptionClass(\r\n    opt.isDisabled,\r\n    opt.isSelected,\r\n    index === focusedOptionIndex\r\n  );\r\n\r\n  return (\r\n    <div\r\n      style={style}\r\n      className={className}\r\n      onClick={() => selectOption(opt)}\r\n    >\r\n      {renderOptionLabel(opt.data)}\r\n    </div>\r\n  );\r\n}, _areEqual);\r\n\r\nOption.displayName = 'Option';\r\n\r\nexport default Option;\r\n"
  },
  {
    "path": "src/components/Menu/index.tsx",
    "content": "import React, { type MutableRefObject } from 'react';\nimport { createPortal } from 'react-dom';\nimport styled, { css } from 'styled-components';\nimport type { MouseOrTouchEvent } from '../../types';\nimport MenuList, { type MenuListProps } from './MenuList';\nimport {\n  OPTION_CLS,\n  OPTION_FOCUSED_CLS,\n  OPTION_DISABLED_CLS,\n  OPTION_SELECTED_CLS,\n  MENU_CONTAINER_CLS,\n  MENU_CONTAINER_TESTID\n} from '../../constants';\n\ninterface MenuProps extends MenuListProps {\n  readonly id?: string;\n  readonly menuTop?: string;\n  readonly menuOpen: boolean;\n  readonly lazyLoadMenu: boolean;\n  readonly menuPortalTarget?: Element;\n  readonly menuRef: MutableRefObject<HTMLDivElement | null>;\n  readonly onMenuMouseDown: (e: MouseOrTouchEvent<HTMLDivElement>) => void;\n}\n\ninterface MenuWrapperProps extends Pick<MenuProps, 'menuOpen' | 'menuTop'> {\n  readonly hideNoOptionsMsg: boolean;\n}\n\nconst MenuWrapper = styled.div<MenuWrapperProps>`\n  z-index: 999;\n  cursor: default;\n  position: absolute;\n\n  ${({ menuTop, menuOpen, hideNoOptionsMsg, theme: { menu } }) => css`\n    width: ${menu.width};\n    margin: ${menu.margin};\n    padding: ${menu.padding};\n    animation: ${menu.animation};\n    border-radius: ${menu.borderRadius};\n    background-color: ${menu.backgroundColor};\n    box-shadow: ${hideNoOptionsMsg ? 'none' : menu.boxShadow};\n    ${!menuOpen && 'display: none;'}\n    ${menuTop && `top: ${menuTop};`}\n  `}\n\n  ${({ theme }) => theme.menu.css}\n\n  .${OPTION_CLS} {\n    display: block;\n    overflow: hidden;\n    user-select: none;\n    white-space: nowrap;\n    text-overflow: ellipsis;\n    -webkit-tap-highlight-color: transparent;\n\t  padding: ${({ theme }) => theme.menu.option.padding};\n\t  text-align: ${({ theme }) => theme.menu.option.textAlign};\n\n    &.${OPTION_FOCUSED_CLS},\n    &:hover:not(.${OPTION_DISABLED_CLS}):not(.${OPTION_SELECTED_CLS}) {\n      background-color: ${({ theme }) => theme.menu.option.focusedBgColor};\n    }\n\n    &.${OPTION_SELECTED_CLS} {\n      color: ${({ theme }) => theme.menu.option.selectedColor};\n      background-color: ${({ theme }) => theme.menu.option.selectedBgColor};\n    }\n\n    &.${OPTION_DISABLED_CLS} {\n      opacity: 0.35;\n    }\n  }\n`;\n\nconst Menu: React.FC<MenuProps> = ({\n  id,\n  menuRef,\n  menuTop,\n  menuOpen,\n  lazyLoadMenu,\n  onMenuMouseDown,\n  menuPortalTarget,\n  ...menuListProps\n}) => {\n  if (lazyLoadMenu && !menuOpen) {\n    return null;\n  }\n\n  const { menuOptions, noOptionsMsg } = menuListProps;\n  const hideNoOptionsMsg = menuOpen && !noOptionsMsg && !menuOptions.length;\n\n  const menuNode = (\n    <MenuWrapper\n      id={id}\n      ref={menuRef}\n      menuTop={menuTop}\n      menuOpen={menuOpen}\n      onMouseDown={onMenuMouseDown}\n      className={MENU_CONTAINER_CLS}\n      data-testid={MENU_CONTAINER_TESTID}\n      hideNoOptionsMsg={hideNoOptionsMsg}\n    >\n      <MenuList {...menuListProps} />\n    </MenuWrapper>\n  );\n\n  return menuPortalTarget\n    ? createPortal(menuNode, menuPortalTarget)\n    : menuNode;\n};\n\nexport default Menu;\n"
  },
  {
    "path": "src/components/Value/MultiValue.tsx",
    "content": "import React from 'react';\r\nimport { suppressEvent } from '../../utils';\r\nimport styled, { css } from 'styled-components';\r\nimport { CLEAR_ICON_MV_TESTID } from '../../constants';\r\nimport type { RenderLabelCallback, SelectedOption } from '../../types';\r\n\r\ntype MultiValueProps = SelectedOption & Readonly<{\r\n  isFocused: boolean;\r\n  renderOptionLabel: RenderLabelCallback;\r\n  removeSelectedOption: (value?: string | number) => void;\r\n}>;\r\n\r\ntype ClearProps = Pick<MultiValueProps, 'isFocused'>;\r\n\r\nconst CLEAR_ICON_FOCUS_STYLE  = css`\r\n  color: ${({ theme }) => theme.multiValue.clear.focusColor};\r\n`;\r\n\r\nconst MultiValueWrapper = styled.div`\r\n  min-width: 0;\r\n  display: flex;\r\n  ${({ theme: { multiValue } }) => css`\r\n    margin: ${multiValue.margin};\r\n    animation: ${multiValue.animation};\r\n    border-radius: ${multiValue.borderRadius};\r\n    background-color: ${multiValue.backgroundColor};\r\n  `}\r\n  ${({ theme }) => theme.multiValue.css}\r\n`;\r\n\r\nconst Label = styled.div`\r\n  overflow: hidden;\r\n  white-space: nowrap;\r\n  text-overflow: ellipsis;\r\n  padding: ${({ theme }) => theme.multiValue.label.padding};\r\n  font-size: ${({ theme }) => theme.multiValue.label.fontSize};\r\n  border-radius: ${({ theme }) => theme.multiValue.label.borderRadius};\r\n`;\r\n\r\nconst Clear = styled.i<ClearProps>`\r\n  display: flex;\r\n  font-style: inherit;\r\n  ${({ theme: { multiValue: { clear } } }) => css`\r\n    color: ${clear.color};\r\n    padding: ${clear.padding};\r\n    font-size: ${clear.fontSize};\r\n    align-self: ${clear.alignSelf};\r\n    transition: ${clear.transition};\r\n    font-weight: ${clear.fontWeight};\r\n\r\n    &:hover {\r\n      ${CLEAR_ICON_FOCUS_STYLE}\r\n    }\r\n  `}\r\n   ${({ isFocused }) => isFocused && CLEAR_ICON_FOCUS_STYLE}\r\n`;\r\n\r\nconst MultiValue: React.FC<MultiValueProps> = ({\r\n  data,\r\n  value,\r\n  isFocused,\r\n  renderOptionLabel,\r\n  removeSelectedOption\r\n}) => {\r\n  const handleOnClear = () => removeSelectedOption(value);\r\n\r\n  return (\r\n    <MultiValueWrapper>\r\n      <Label>\r\n        {renderOptionLabel(data)}\r\n      </Label>\r\n      <Clear\r\n        isFocused={isFocused}\r\n        onClick={handleOnClear}\r\n        onTouchEnd={handleOnClear}\r\n        onMouseDown={suppressEvent}\r\n        data-testid={CLEAR_ICON_MV_TESTID}\r\n      >\r\n        &#10006;\r\n      </Clear>\r\n    </MultiValueWrapper>\r\n  );\r\n};\r\n\r\nexport default MultiValue;\r\n"
  },
  {
    "path": "src/components/Value/index.tsx",
    "content": "import React, { memo, Fragment, type ReactNode } from 'react';\nimport MultiValue from './MultiValue';\nimport styled from 'styled-components';\nimport type { MultiParams, SelectedOption, RenderLabelCallback } from '../../types';\n\ntype ValueProps = Readonly<{\n  isMulti?: boolean;\n  hasInput: boolean;\n  placeholder: string;\n  selectedOption: SelectedOption[];\n  focusedMultiValue: string | number | null;\n  renderOptionLabel: RenderLabelCallback;\n  removeSelectedOption: (value?: string | number) => void;\n  renderMultiOptions?: (params: MultiParams) => ReactNode;\n}>;\n\nconst SingleValue = styled.div`\n  margin: 0 2px;\n  max-width: 100%;\n  overflow: hidden;\n  white-space: nowrap;\n  box-sizing: border-box;\n  text-overflow: ellipsis;\n  grid-area: 1 / 1 / 2 / 3;\n`;\n\nconst Placeholder = styled(SingleValue)`\n  color: ${({ theme }) => theme.color.placeholder};\n`;\n\nconst Value = memo<ValueProps>(({\n  isMulti,\n  hasInput,\n  placeholder,\n  selectedOption,\n  focusedMultiValue,\n  renderOptionLabel,\n  renderMultiOptions,\n  removeSelectedOption\n}) => {\n  if (hasInput && (!isMulti || (isMulti && (!selectedOption.length || renderMultiOptions)))) {\n    return null;\n  }\n\n  if (!selectedOption.length) {\n    return <Placeholder>{placeholder}</Placeholder>;\n  }\n\n  if (!isMulti) {\n    return (\n      <SingleValue>\n        {renderOptionLabel(selectedOption[0].data)}\n      </SingleValue>\n    );\n  }\n\n  return (\n    <Fragment>\n      {renderMultiOptions\n        ? renderMultiOptions({ renderOptionLabel, selected: selectedOption })\n        : selectedOption.map(({ data, value }) => (\n            <MultiValue\n              key={value}\n              data={data}\n              value={value}\n              renderOptionLabel={renderOptionLabel}\n              isFocused={value === focusedMultiValue}\n              removeSelectedOption={removeSelectedOption}\n            />\n          ))}\n    </Fragment>\n  );\n});\n\nValue.displayName = 'Value';\n\nexport default Value;\n"
  },
  {
    "path": "src/components/index.ts",
    "content": "export { default as Menu } from './Menu';\nexport { default as Value } from './Value';\nexport { default as AutosizeInput } from './AutosizeInput';\nexport { default as AriaLiveRegion } from './AriaLiveRegion';\nexport { default as IndicatorIcons } from './IndicatorIcons';"
  },
  {
    "path": "src/constants/defaults.ts",
    "content": "import type {\n  FocusedOption,\n  OptionValueCallback,\n  OptionLabelCallback,\n  OptionFilterCallback,\n  OptionDisabledCallback\n} from '../types';\n\nexport const PAGE_SIZE_DEFAULT = 5;\nexport const MENU_ITEM_SIZE_DEFAULT = 35;\nexport const MENU_MAX_HEIGHT_DEFAULT = 300;\nexport const LOADING_MSG_DEFAULT = 'Loading..';\nexport const NO_OPTIONS_MSG_DEFAULT = 'No options';\nexport const PLACEHOLDER_DEFAULT = 'Select option..';\n\nexport const EMPTY_ARRAY: any[] = []; // Default for options and selectedOption props\nexport const FOCUSED_OPTION_DEFAULT: FocusedOption = { index: -1 };\n\nexport const FUNCTIONS = {\n  optionLabel: ((x) => x.label) as OptionLabelCallback,\n  optionValue: ((x) => x.value) as OptionValueCallback,\n  isOptionDisabled: ((x) => !!x.isDisabled) as OptionDisabledCallback,\n  optionFilter: ((x) => typeof x.label === 'string' ? x.label : '' + x.label) as OptionFilterCallback\n};"
  },
  {
    "path": "src/constants/dom.ts",
    "content": "import type { TestableElement } from '../types';\nimport type { InputHTMLAttributes } from 'react';\n\n// id attributes for AriaLiveRegion.tsx innerHTML content\nexport const ARIA_LIVE_CONTEXT_ID = 'aria-context';\nexport const ARIA_LIVE_SELECTION_ID = 'aria-selection';\n\n// classNames (menu options)\nexport const OPTION_CLS = 'rfs-option';\nexport const OPTION_FOCUSED_CLS = `${OPTION_CLS}-focused`;\nexport const OPTION_SELECTED_CLS = `${OPTION_CLS}-selected`;\nexport const OPTION_DISABLED_CLS = `${OPTION_CLS}-disabled`;\n\n// classNames (containers & icons)\nexport const CARET_ICON_CLS = 'rfs-caret-icon';\nexport const CLEAR_ICON_CLS = 'rfs-clear-icon';\nexport const LOADING_DOTS_CLS = 'rfs-loading-dots';\nexport const AUTOSIZE_INPUT_CLS = 'rfs-autosize-input';\nexport const MENU_CONTAINER_CLS = 'rfs-menu-container';\nexport const SELECT_CONTAINER_CLS = 'rfs-select-container';\nexport const CONTROL_CONTAINER_CLS = 'rfs-control-container';\n\n// data-testid attributes used for DOM element querying in unit test cases\n// ...this attribute gets rendered in development and test environments (removed in production)\nconst isTest = process.env.NODE_ENV === 'test';\nexport const CLEAR_ICON_TESTID = isTest ? CLEAR_ICON_CLS : undefined;\nexport const CARET_ICON_TESTID = isTest ? CARET_ICON_CLS : undefined;\nexport const AUTOSIZE_INPUT_TESTID = isTest ? AUTOSIZE_INPUT_CLS : undefined;\nexport const MENU_CONTAINER_TESTID = isTest ? MENU_CONTAINER_CLS : undefined;\nexport const CLEAR_ICON_MV_TESTID = isTest ? `${CLEAR_ICON_CLS}-mv` : undefined;\nexport const SELECT_CONTAINER_TESTID = isTest ? SELECT_CONTAINER_CLS : undefined;\nexport const CONTROL_CONTAINER_TESTID = isTest ? CONTROL_CONTAINER_CLS : undefined;\n\n/**\n * Static attributes for 'AutosizeInput' input element\n */\nexport const AUTOSIZE_INPUT_ATTRS: InputHTMLAttributes<HTMLInputElement> & TestableElement = {\n  tabIndex: 0,\n  type: 'text',\n  role: 'combobox',\n  spellCheck: false,\n  autoCorrect: 'off',\n  autoComplete: 'off',\n  'aria-haspopup': true,\n  autoCapitalize: 'none',\n  'aria-autocomplete': 'list',\n  className: AUTOSIZE_INPUT_CLS,\n  'data-testid': AUTOSIZE_INPUT_TESTID\n} as const;"
  },
  {
    "path": "src/constants/enums.ts",
    "content": "/**\n * Menu position in relation to the control.\n * Defaults to 'auto' - meaning, if not enough space below control, then place above.\n */\nexport const MenuPositionEnum = {\n  TOP: 'top',\n  AUTO: 'auto',\n  BOTTOM: 'bottom'\n} as const;\n\nexport type MenuPositionEnum = typeof MenuPositionEnum[keyof typeof MenuPositionEnum];\n\n/**\n * Property filterMatchFrom values. Defaults to 'any'.\n * Determines where to match search input in option during filter process.\n */\nexport const FilterMatchEnum = {\n  ANY: 'any',\n  START: 'start'\n} as const;\n\nexport type FilterMatchEnum = typeof FilterMatchEnum[keyof typeof FilterMatchEnum];\n\n/**\n * Arrow key direction OR position for cycling through menu options.\n */\nexport const OptionIndexEnum = {\n  UP: 0,\n  DOWN: 1,\n  LAST: 2,\n  FIRST: 3,\n  PAGEUP: 4,\n  PAGEDOWN: 5\n} as const;\n\nexport type OptionIndexEnum = typeof OptionIndexEnum[keyof typeof OptionIndexEnum];"
  },
  {
    "path": "src/constants/index.ts",
    "content": "export * from './dom';\nexport * from './enums';\nexport * from './theme';\nexport * from './styled';\nexport * from './defaults';"
  },
  {
    "path": "src/constants/styled.ts",
    "content": "import { css, keyframes } from 'styled-components';\n\nconst BOUNCE_KEYFRAMES = keyframes`\n  0%, 80%, 100% {\n    transform: scale(0);\n  } 40% {\n    transform: scale(1.0);\n  }\n`;\n\nconst FADE_IN_KEYFRAMES = keyframes`\n  from {\n    opacity: 0;\n  } to {\n    opacity: 1;\n  }\n`;\n\nexport const FADE_IN_ANIMATION_CSS = css`${FADE_IN_KEYFRAMES} 0.2s ease-in`;\nexport const BOUNCE_ANIMATION_CSS = css`${BOUNCE_KEYFRAMES} 1.19s ease-in-out infinite`;"
  },
  {
    "path": "src/constants/theme.ts",
    "content": "import type { DefaultTheme } from 'styled-components';\nimport { BOUNCE_ANIMATION_CSS, FADE_IN_ANIMATION_CSS } from './styled';\n\nconst color = {\n  border: '#ced4da',\n  danger: '#dc3545',\n  primary: '#007bff',\n  disabled: '#e9ecef',\n  placeholder: '#6e7276',\n  dangerLight: 'rgba(220, 53, 69, 0.25)'\n} as const;\n\nexport const DEFAULT_THEME: DefaultTheme = {\n  color,\n  input: {},\n  select: {},\n  loader: {\n    size: '0.625rem',\n    padding: '0.375rem 0.75rem',\n    animation: BOUNCE_ANIMATION_CSS,\n    color: 'rgba(0, 123, 255, 0.42)'\n  },\n  icon: {\n    color: '#ccc',\n    hoverColor: '#a6a6a6',\n    padding: '0 14px',\n    clear: {\n      width: '14px',\n      height: '16px',\n      animation: FADE_IN_ANIMATION_CSS,\n      transition: 'color 0.2s ease-out'\n    },\n    caret: {\n      size: '7px',\n      transition: 'transform 0.3s ease-in-out, color 0.2s ease-out'\n    }\n  },\n  control: {\n    minHeight: '38px',\n    borderWidth: '1px',\n    borderStyle: 'solid',\n    borderRadius: '3px',\n    padding: '2px 8px',\n    boxShadow: '0 0 0 0.2rem',\n    boxShadowColor: 'rgba(0, 123, 255, 0.25)',\n    focusedBorderColor: 'rgba(0, 123, 255, 0.75)',\n    transition: 'box-shadow 0.2s ease-out, border-color 0.2s ease-out'\n  },\n  menu: {\n    padding: '0',\n    width: '100%',\n    margin: '0.35rem 0',\n    borderRadius: '3px',\n    backgroundColor: '#fff',\n    animation: FADE_IN_ANIMATION_CSS,\n    boxShadow: '0 0.5em 1em -0.125em rgb(10 10 10 / 12%), 0 0 0 1px rgb(10 10 10 / 4%)',\n    option: {\n      textAlign: 'left',\n      selectedColor: '#fff',\n      padding: '0.375rem 0.75rem',\n      selectedBgColor: color.primary,\n      focusedBgColor: 'rgba(0, 123, 255, 0.15)'\n    }\n  },\n  noOptions: {\n    fontSize: '1.25rem',\n    margin: '0.25rem 0',\n    color: 'hsl(0, 0%, 60%)',\n    padding: '0.375rem 0.75rem'\n  },\n  multiValue: {\n    margin: '1px 2px',\n    borderRadius: '3px',\n    backgroundColor: '#e7edf3',\n    animation: FADE_IN_ANIMATION_CSS,\n    label: {\n      borderRadius: '3px',\n      fontSize: '0.825em',\n      padding: '1px 0 1px 6px'\n    },\n    clear: {\n      fontWeight: 600,\n      padding: '0 6px',\n      color: '#a6a6a6',\n      fontSize: '0.65em',\n      alignSelf: 'center',\n      focusColor: color.danger,\n      transition: 'color 0.2s ease-out'\n    }\n  }\n} as const;"
  },
  {
    "path": "src/globals.d.ts",
    "content": "import type { FlattenSimpleInterpolation } from 'styled-components';\n\ndeclare module 'styled-components' {\n  export interface DefaultTheme {\n    color: {\n      border: string;\n      danger: string;\n      primary: string;\n      disabled: string;\n      placeholder: string;\n      dangerLight: string;\n      caretActive?: string;\n      iconSeparator?: string;\n    };\n    select: {\n      css?: string | FlattenSimpleInterpolation;\n    };\n    loader: {\n      size: string;\n      color: string;\n      padding: string;\n      animation: string | FlattenSimpleInterpolation;\n    };\n    icon: {\n      color: string;\n      padding: string;\n      hoverColor: string;\n      css?: string | FlattenSimpleInterpolation;\n      clear: {\n        width: string;\n        height: string;\n        transition: string;\n        animation: string | FlattenSimpleInterpolation;\n      };\n      caret: {\n        size: string;\n        transition: string;\n      };\n    };\n    control: {\n      height?: string;\n      padding: string;\n      minHeight: string;\n      boxShadow: string;\n      transition: string;\n      borderWidth: string;\n      borderStyle: string;\n      borderRadius: string;\n      boxShadowColor: string;\n      backgroundColor?: string;\n      focusedBorderColor: string;\n      css?: string | FlattenSimpleInterpolation;\n      focusedCss?: string | FlattenSimpleInterpolation;\n    },\n    input: {\n      css?: string | FlattenSimpleInterpolation;\n      cssRequired?: string | FlattenSimpleInterpolation;\n    }\n    menu: {\n      margin: string;\n      padding: string;\n      boxShadow: string;\n      borderRadius: string;\n      width: string | number;\n      backgroundColor: string;\n      css?: string | FlattenSimpleInterpolation;\n      animation: string | FlattenSimpleInterpolation;\n      option: {\n        padding: string;\n        textAlign: string;\n        selectedColor: string;\n        focusedBgColor: string;\n        selectedBgColor: string;\n      };\n    };\n    noOptions: {\n      color: string;\n      margin: string;\n      padding: string;\n      fontSize: string;\n      css?: string | FlattenSimpleInterpolation;\n    };\n    multiValue: {\n      margin: string;\n      borderRadius: string;\n      backgroundColor: string;\n      css?: string | FlattenSimpleInterpolation;\n      animation: string | FlattenSimpleInterpolation;\n      label: {\n        padding: string;\n        fontSize: string;\n        borderRadius: string;\n      };\n      clear: {\n        color: string;\n        padding: string;\n        fontSize: string;\n        alignSelf: string;\n        transition: string;\n        focusColor: string;\n        fontWeight: string | number;\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/hooks/index.ts",
    "content": "export { default as useDebounce } from './useDebounce';\nexport { default as useLatestRef } from './useLatestRef';\nexport { default as useMountEffect } from './useMountEffect';\nexport { default as useMenuOptions } from './useMenuOptions';\nexport { default as useCallbackRef } from './useCallbackRef';\nexport { default as useUpdateEffect } from './useUpdateEffect';\nexport { default as useMenuPosition } from './useMenuPosition';"
  },
  {
    "path": "src/hooks/useCallbackRef.ts",
    "content": "import type { CallbackFn } from '../types';\nimport { useEffect, useRef, useCallback } from 'react';\n\n/**\n * Creates a stable callback function that has access to the latest\n * state and can be used within event handlers and effect callbacks.\n *\n * @param callback the callback to write to ref object\n */\nconst useCallbackRef = <T extends CallbackFn>(callback?: T): T => {\n  const ref = useRef(callback);\n\n  useEffect(() => {\n    ref.current = callback;\n  });\n\n  return useCallback<CallbackFn>((...args) => ref.current?.(...args), []) as T;\n};\n\nexport default useCallbackRef;"
  },
  {
    "path": "src/hooks/useDebounce.ts",
    "content": "import { useState } from 'react';\nimport useUpdateEffect from './useUpdateEffect';\n\n/**\n * Debouncer hook (hacky fix to prevent unecessary state mutations if no delay is passed).\n * If a number is passed for the delay parameter, use to debounce/set the value.\n *\n * @param value the value to debounce\n * @param delay the delay (ms) for the setTimeout\n */\nconst useDebounce = <T>(value: T, delay: number = 0): T => {\n  const [debouncedValue, setDebouncedValue] = useState<T>(value);\n\n  useUpdateEffect(() => {\n    if (delay <= 0) return;\n\n    const handler = setTimeout(() => {\n      setDebouncedValue(value);\n    }, delay);\n\n    return () => {\n      clearTimeout(handler);\n    };\n  }, [value, delay]);\n\n  return delay <= 0 ? value : debouncedValue;\n};\n\nexport default useDebounce;"
  },
  {
    "path": "src/hooks/useLatestRef.ts",
    "content": "import { useRef, type MutableRefObject } from \"react\"\n\n/**\n * Hook to persist value between renders - keeps it up-to-date on changes.\n *\n * @param value the value to persist\n */\nconst useLatestRef = <T>(value: T): MutableRefObject<T> => {\n  const ref = useRef<T>(value);\n  ref.current = value;\n  return ref;\n};\n\nexport default useLatestRef;"
  },
  {
    "path": "src/hooks/useMenuOptions.ts",
    "content": "import { useMemo } from 'react';\nimport useCallbackRef from './useCallbackRef';\nimport { FilterMatchEnum, FUNCTIONS } from '../constants';\nimport { isBoolean, trimAndFormatFilterStr } from '../utils';\nimport type {\n  MenuOption,\n  OptionData,\n  SelectedOption,\n  OptionValueCallback,\n  OptionLabelCallback,\n  OptionFilterCallback,\n  OptionDisabledCallback\n} from '../types';\n\n/**\n * Parse options to array of MenuOptions and perform filtering (if applicable).\n */\nconst useMenuOptions = (\n  options: OptionData[],\n  debouncedInputValue: string,\n  filterMatchFrom: FilterMatchEnum,\n  selectedOption: SelectedOption[],\n  getOptionValue: OptionValueCallback,\n  getOptionLabel: OptionLabelCallback,\n  getIsOptionDisabled?: OptionDisabledCallback,\n  getFilterOptionString?: OptionFilterCallback,\n  filterIgnoreCase: boolean = false,\n  filterIgnoreAccents: boolean = false,\n  isMulti: boolean = false,\n  async: boolean = false,\n  hideSelectedOptions?: boolean\n): MenuOption[] => {\n  const getIsOptionDisabledFn = useCallbackRef(getIsOptionDisabled || FUNCTIONS.isOptionDisabled);\n  const getFilterOptionStringFn = useCallbackRef(getFilterOptionString || FUNCTIONS.optionFilter);\n  const hideSelectedOptsOrDefault = isBoolean(hideSelectedOptions) ? hideSelectedOptions : isMulti;\n  const searchValue = !async ? debouncedInputValue : ''; // prevent recomputing on input mutations in async mode\n\n  const menuOptions = useMemo<MenuOption[]>(() => {\n    const selectedValues = selectedOption.map((x) => x.value);\n    const isFilterMatchAny = filterMatchFrom === FilterMatchEnum.ANY;\n    const matchVal = trimAndFormatFilterStr(searchValue, filterIgnoreCase, filterIgnoreAccents);\n\n    const isOptionFilterMatch = (option: MenuOption): boolean => {\n      if (!matchVal) return true;\n      const filterVal = getFilterOptionStringFn(option);\n      const normalFilterVal = trimAndFormatFilterStr(filterVal, filterIgnoreCase, filterIgnoreAccents);\n      return isFilterMatchAny\n        ? normalFilterVal.includes(matchVal)\n        : normalFilterVal.startsWith(matchVal);\n    };\n\n    const parseMenuOption = (data: OptionData): MenuOption | undefined => {\n      const value = getOptionValue(data);\n      const label = getOptionLabel(data);\n      const isDisabled = getIsOptionDisabledFn(data);\n      const isSelected = selectedValues.includes(value);\n      const menuOption: MenuOption = { data, value, label, isDisabled, isSelected };\n      return !isOptionFilterMatch(menuOption) || (hideSelectedOptsOrDefault && isSelected)\n        ? undefined\n        : menuOption;\n    };\n\n    return options.reduce((acc: MenuOption[], data: OptionData) => {\n      const menuOption = parseMenuOption(data);\n      menuOption && acc.push(menuOption);\n      return acc;\n    }, []);\n  }, [\n    options,\n    searchValue,\n    getOptionValue,\n    getOptionLabel,\n    selectedOption,\n    filterMatchFrom,\n    filterIgnoreCase,\n    filterIgnoreAccents,\n    getIsOptionDisabledFn,\n    getFilterOptionStringFn,\n    hideSelectedOptsOrDefault\n  ]);\n\n  return menuOptions;\n};\n\nexport default useMenuOptions;"
  },
  {
    "path": "src/hooks/useMenuPosition.ts",
    "content": "import useLatestRef from './useLatestRef';\nimport type { CallbackFn } from '../types';\nimport useCallbackRef from './useCallbackRef';\nimport useUpdateEffect from './useUpdateEffect';\nimport { MenuPositionEnum } from '../constants';\nimport { useState, useRef, type RefObject } from 'react';\nimport { calculateMenuTop, menuFitsBelowControl, scrollMenuIntoViewOnOpen } from '../utils';\n\ntype MenuPosition = Readonly<{\n  menuStyleTop?: string;\n  menuHeightCalc: number;\n}>;\n\n/**\n * Handle calculating and maintaining the menuHeight used by react-window.\n * Handle scroll animation and callback execution when menuOpen = true.\n * Handle resetting menuHeight back to the menuHeightDefault and callback execution when menuOpen = false.\n * Use ref to track if the menuHeight was resized, and if so, set the menu height back to default (avoids uncessary renders) with call to setMenuHeight.\n * Handle determining where to place the menu in relation to control - when menuPosition = 'top' or menuPosition = 'bottom' and there is not sufficient space below control, place on top.\n */\nconst useMenuPosition = (\n  menuRef: RefObject<HTMLElement | null>,\n  controlRef: RefObject<HTMLElement | null>,\n  menuOpen: boolean,\n  menuPosition: MenuPositionEnum,\n  menuItemSize: number,\n  menuHeightDefault: number,\n  menuOptionsLength: number,\n  isMenuPortaled: boolean,\n  onMenuOpen?: CallbackFn,\n  onMenuClose?: CallbackFn,\n  menuScrollDuration?: number,\n  scrollMenuIntoView?: boolean\n): MenuPosition => {\n  const isMenuTopPosition =\n    menuPosition === MenuPositionEnum.TOP ||\n    (menuPosition === MenuPositionEnum.AUTO && !menuFitsBelowControl(menuRef.current));\n\n  const onMenuOpenFn = useCallbackRef(onMenuOpen);\n  const onMenuCloseFn = useCallbackRef(onMenuClose);\n  const resetMenuHeightRef = useRef<boolean>(false);\n  const [menuHeight, setMenuHeight] = useState<number>(menuHeightDefault);\n  const shouldScrollRef = useLatestRef<boolean>(!isMenuTopPosition && !isMenuPortaled);\n\n  useUpdateEffect(() => {\n    if (menuOpen) {\n      const handleOnMenuOpen = (availableSpace?: number) => {\n        onMenuOpenFn();\n        if (availableSpace) {\n          resetMenuHeightRef.current = true;\n          setMenuHeight(availableSpace);\n        }\n      };\n\n      shouldScrollRef.current\n        ? scrollMenuIntoViewOnOpen(\n            menuRef.current,\n            menuScrollDuration,\n            scrollMenuIntoView,\n            handleOnMenuOpen\n          )\n        : handleOnMenuOpen();\n    } else {\n      onMenuCloseFn();\n      if (resetMenuHeightRef.current) {\n        resetMenuHeightRef.current = false;\n        setMenuHeight(menuHeightDefault);\n      }\n    }\n  }, [\n    menuRef,\n    menuOpen,\n    shouldScrollRef,\n    menuHeightDefault,\n    scrollMenuIntoView,\n    menuScrollDuration,\n    onMenuOpenFn,\n    onMenuCloseFn\n  ]);\n\n  // calculate menu height for react-window\n  // calculate MenuWrapper el 'top' css prop (if menu is positioned above control)\n  const menuHeightCalc = Math.min(menuHeight, menuOptionsLength * menuItemSize);\n  const menuStyleTop = isMenuTopPosition ? calculateMenuTop(menuHeightCalc, menuRef.current, controlRef.current) : undefined;\n\n  return {\n    menuStyleTop,\n    menuHeightCalc\n  };\n};\n\nexport default useMenuPosition;"
  },
  {
    "path": "src/hooks/useMountEffect.ts",
    "content": "import { useEffect, type EffectCallback } from 'react';\n\n/**\n * Run an effect only once (on initial mount).\n *\n * @param effect the effect to execute\n */\nconst useMountEffect = (effect: EffectCallback): void => {\n  // eslint-disable-next-line react-hooks/exhaustive-deps\n  useEffect(effect, []);\n};\n\nexport default useMountEffect;"
  },
  {
    "path": "src/hooks/useUpdateEffect.ts",
    "content": "import {useRef, useEffect, type EffectCallback, type DependencyList} from 'react';\n\n/**\n * `React.useEffect` that will not run on the first render.\n *\n * @param effect the effect to execute\n * @param deps the dependency list\n */\nconst useUpdateEffect = (effect: EffectCallback, deps?: DependencyList): void => {\n  const isFirstRender = useRef(true);\n\n  useEffect(() => {\n    if (isFirstRender.current) {\n      isFirstRender.current = false;\n    } else {\n      return effect();\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, deps);\n};\n\nexport default useUpdateEffect;\n"
  },
  {
    "path": "src/index.ts",
    "content": "export { default as Select } from './Select';\nexport type { Theme, SelectRef, MenuOption, MultiParams } from './types';"
  },
  {
    "path": "src/types.ts",
    "content": "import type { DefaultTheme } from 'styled-components';\nimport type { ReactNode, MouseEvent, TouchEvent, EventHandler } from 'react';\n\nexport type OptionData = any;\nexport type CallbackFn = (...args: any[]) => any;\nexport type AriaLiveAttribute = 'off' | 'polite' | 'assertive';\n\nexport type IconRenderer = ReactNode | ((...args: any[]) => ReactNode);\n\nexport type OptionValueCallback = (data: OptionData) => string | number;\nexport type OptionLabelCallback = OptionValueCallback;\nexport type RenderLabelCallback = (data: OptionData) => ReactNode;\nexport type OptionFilterCallback = (option: MenuOption) => string;\nexport type OptionDisabledCallback = (data: OptionData) => boolean;\n\nexport type MouseOrTouchEvent<T = Element> = MouseEvent<T> | TouchEvent<T>;\nexport type MouseOrTouchEventHandler<T = Element> = EventHandler<MouseOrTouchEvent<T>>;\n\nexport type PartialDeep<T> = {\n  [P in keyof T]?: PartialDeep<T[P]>;\n};\n\nexport type TestableElement = Readonly<{\n  'data-testid'?: string;\n}>;\n\nexport type SelectedOption = Readonly<{\n  data?: OptionData;\n  value?: string | number;\n  label?: string | number;\n}>;\n\nexport interface FocusedOption extends SelectedOption {\n  readonly index: number;\n  readonly isDisabled?: boolean;\n  readonly isSelected?: boolean;\n}\n\nexport type ItemData = Readonly<{\n  memoOptions: boolean;\n  menuOptions: MenuOption[];\n  focusedOptionIndex: number;\n  selectOption: (option: MenuOption) => void;\n  renderOptionLabel: (data: OptionData) => ReactNode;\n}>;\n\nexport type MultiParams = Readonly<{\n  selected: SelectedOption[];\n  renderOptionLabel: (data: OptionData) => ReactNode;\n}>;\n\nexport type MenuOption = Readonly<{\n  label: string | number;\n  value: string | number;\n  data: OptionData;\n  isDisabled: boolean;\n  isSelected: boolean;\n}>;\n\nexport type SelectRef = Readonly<{\n  menuOpen: boolean;\n  blur: () => void;\n  focus: () => void;\n  clearValue: () => void;\n  toggleMenu: (state?: boolean) => void;\n  setValue: (option?: OptionData) => void;\n}>;\n\nexport type Theme = PartialDeep<DefaultTheme>;"
  },
  {
    "path": "src/utils/common.ts",
    "content": "import type { SyntheticEvent } from 'react';\nimport type { SelectedOption, OptionValueCallback, OptionLabelCallback, CallbackFn } from '../types';\nimport { OPTION_CLS, EMPTY_ARRAY, OPTION_FOCUSED_CLS, OPTION_SELECTED_CLS, OPTION_DISABLED_CLS } from '../constants';\n\nconst DIACRITICS_REG_EXP = /[\\u0300-\\u036f]/g;\n\n/**\n * Strips all diacritics from a string.\n */\nconst stripDiacritics = (val: string): string => {\n  return val.normalize('NFD').replace(DIACRITICS_REG_EXP, '');\n};\n\nexport const isBoolean = (val: unknown): val is boolean => typeof val === 'boolean';\nexport const isFunction = (val: unknown): val is CallbackFn => typeof val === 'function';\nexport const isArrayWithLength = (val: unknown): boolean => Array.isArray(val) && !!val.length;\nexport const isPlainObject = (val: unknown): boolean => val !== null && typeof val === 'object' && !Array.isArray(val);\n\n/**\n * Prevent default behavior and propagation of an event\n */\nexport const suppressEvent = (e: SyntheticEvent): void => {\n  e.preventDefault();\n  e.stopPropagation();\n};\n\n/**\n * Apply regex to string, and if the value is NOT case sensitive,\n * call .toLowerCase() and return result\n */\nexport const trimAndFormatFilterStr = (\n  value: string,\n  filterIgnoreCase: boolean,\n  filterIgnoreAccents: boolean\n): string => {\n  let trimVal = value.trim();\n  if (filterIgnoreCase) trimVal = trimVal.toLowerCase();\n  return !filterIgnoreAccents ? trimVal : stripDiacritics(trimVal);\n};\n\n/**\n * Builds the className property in Option.tsx component\n */\nexport const buildOptionClass = (\n  isDisabled: boolean,\n  isSelected: boolean,\n  isFocused: boolean\n): string => {\n  let cx = OPTION_CLS;\n\n  if (isDisabled) cx += ' ' + OPTION_DISABLED_CLS;\n  if (isSelected) cx += ' ' + OPTION_SELECTED_CLS;\n  if (isFocused) cx += ' ' + OPTION_FOCUSED_CLS;\n\n  return cx;\n};\n\n/**\n * Parses an object or an array of objects into output of SelectedOption[]\n */\nexport const normalizeValue = (\n  value: unknown,\n  getOptionValue: OptionValueCallback,\n  getOptionLabel: OptionLabelCallback\n): SelectedOption[] => {\n  const initVals = Array.isArray(value)\n    ? value\n    : isPlainObject(value)\n      ? [value]\n      : EMPTY_ARRAY;\n\n  if (!isArrayWithLength(initVals)) {\n    return initVals;\n  }\n\n  return initVals.map((data) => ({\n    data,\n    value: getOptionValue(data),\n    label: getOptionLabel(data)\n  }));\n};\n\n/**\n * Immutable implementation of mergeDeep for two objects. Will return the merged result\n * In first condition - check that property is no 'animation', since we never want to\n * merge that complex styled-component object\n */\nexport const mergeDeep = <T>(target: any, source: any): T => {\n  const output = { ...target };\n\n  Object.keys(source).forEach((key) => {\n    const sourceProp = source[key];\n    output[key] =\n      (key !== 'animation' && isPlainObject(sourceProp))\n        ? target[key]\n          ? mergeDeep(target[key], sourceProp)\n          : sourceProp\n        : sourceProp;\n  });\n\n  return output;\n};"
  },
  {
    "path": "src/utils/device.ts",
    "content": "import { isBoolean } from './common';\n\nlet _isTouchDevice: boolean | undefined;\n\n/**\n * Determines if the current device is touch-enabled.\n * Global, lazy evaluation.\n */\nexport const isTouchDevice = (): boolean => {\n  if (isBoolean(_isTouchDevice)) {\n    return _isTouchDevice;\n  }\n\n  return _isTouchDevice = (() => {\n    try {\n      document.createEvent('TouchEvent');\n      return true;\n    } catch (e) {\n      return false;\n    }\n  })();\n};"
  },
  {
    "path": "src/utils/index.ts",
    "content": "export * from './menu';\nexport * from './common';\nexport * from './device';"
  },
  {
    "path": "src/utils/menu.ts",
    "content": "import type { CallbackFn } from '../types';\n\nconst getScrollTop = (el: HTMLElement): number => {\n  return isDocumentElement(el) ? window.pageYOffset : el.scrollTop;\n};\n\nconst scrollTo = (el: HTMLElement, top: number): void => {\n  isDocumentElement(el) ? window.scrollTo(0, top) : (el.scrollTop = top);\n};\n\nconst isDocumentElement = (el: HTMLElement | typeof window): boolean => {\n  return el === document.body || el === document.documentElement || el === window;\n};\n\nconst getScrollParent = (el: HTMLElement): HTMLElement => {\n  let style = getComputedStyle(el);\n\n  if (style.position === 'fixed') {\n    return document.documentElement;\n  }\n\n  const overflowRegExp = /(auto|scroll)/;\n  const isParentAbs = style.position === 'absolute';\n\n  for (let parent: HTMLElement | null = el; (parent = parent?.parentElement);) {\n    style = getComputedStyle(parent);\n    if (\n      !(isParentAbs && style.position === 'static') &&\n      overflowRegExp.test(`${style.overflow}${style.overflowX}${style.overflowY}`)\n    ) {\n      return parent;\n    }\n  }\n\n  return document.documentElement;\n};\n\nconst smoothScrollTo = (\n  el: HTMLElement,\n  to: number,\n  duration: number = 250,\n  callback?: CallbackFn\n): void => {\n  let currentTime = 0;\n  const start = getScrollTop(el);\n  const change = to - start;\n  const easeOutCubic = (t: number): number => change * ((t = t / duration - 1) * t * t + 1) + start;\n\n  const scrollFn = () => {\n    currentTime += 5;\n    const calcScrollTop = easeOutCubic(currentTime);\n    scrollTo(el, calcScrollTop);\n    (currentTime < duration) ? requestAnimationFrame(scrollFn) : callback?.();\n  };\n\n  requestAnimationFrame(scrollFn);\n};\n\n/**\n * Calculates the top property value for the MenuWrapper element\n * This property is only generated when the position of the menu is above the control\n */\nexport const calculateMenuTop = (\n  menuHeight: number,\n  menuEl: HTMLElement | null,\n  controlEl: HTMLElement | null\n): string => {\n  const menuElStyle = menuEl && getComputedStyle(menuEl);\n  const marginBottom = menuElStyle ? parseInt(menuElStyle.marginBottom, 10) : 0;\n  const marginTop = menuElStyle ? parseInt(menuElStyle.marginTop, 10) : 0;\n  const controlHeight = controlEl?.getBoundingClientRect().height ?? 0;\n  const menuHeightCalc = menuHeight > 0 ? menuHeight : (menuEl?.getBoundingClientRect().height ?? 0);\n  const basePx = -Math.abs(menuHeightCalc + controlHeight);\n  return `calc(${basePx}px + ${marginBottom + marginTop}px)`;\n};\n\nexport const menuFitsBelowControl = (el: HTMLElement | null): boolean => {\n  if (!el) return true;\n  const scrollParent = getScrollParent(el);\n  const { top, height } = el.getBoundingClientRect();\n  const spaceBelow = scrollParent.getBoundingClientRect().height - getScrollTop(scrollParent) - top;\n  return spaceBelow >= height;\n};\n\n/**\n * Calculate space around the control and menu to determine if an animated\n * scroll can performed to show the menu in full view. Also, execute a callback if defined\n */\nexport const scrollMenuIntoViewOnOpen = (\n  menuEl: HTMLElement | null,\n  menuScrollDuration: number | undefined,\n  scrollMenuIntoView: boolean | undefined,\n  handleOnMenuOpen: (availableSpace?: number) => void\n): void => {\n  if (!menuEl) {\n    handleOnMenuOpen();\n    return;\n  }\n\n  const { top, height, bottom } = menuEl.getBoundingClientRect();\n  const viewInner = window.innerHeight;\n  const viewSpaceBelow = viewInner - top;\n\n  // Menu will fit in available space - no need to do scroll\n  if (viewSpaceBelow >= height) {\n    handleOnMenuOpen();\n    return;\n  }\n\n  const scrollParent = getScrollParent(menuEl);\n  const scrollTop = getScrollTop(scrollParent);\n  const spaceBelow = scrollParent.getBoundingClientRect().height - scrollTop - top;\n  const notEnoughSpaceBelow = spaceBelow < height;\n\n  // Sufficient space does not exist to scroll menu fully into view\n  // Calculate available space and use that as the the new menuHeight (use scrollSpaceBelow for now)\n  // OR scrollMenuIntoView = false\n  if (notEnoughSpaceBelow || !scrollMenuIntoView) {\n    const condensedMenuHeight = notEnoughSpaceBelow ? spaceBelow : undefined;\n    handleOnMenuOpen(condensedMenuHeight);\n    return;\n  }\n\n  // Do scroll and upon scroll animation completion, execute the callback if defined\n  const marginBottom = parseInt(getComputedStyle(menuEl).marginBottom, 10);\n  const scrollDown = bottom - viewInner + scrollTop + marginBottom;\n\n  smoothScrollTo(\n    scrollParent,\n    scrollDown,\n    menuScrollDuration,\n    handleOnMenuOpen\n  );\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"jsx\": \"react\",\n    \"target\": \"ESNext\",\n    \"module\": \"ESNext\",\n    \"lib\": [\"DOM\", \"DOM.Iterable\", \"ESNext\"],\n    \"moduleResolution\": \"node\",\n    \"strict\": true,\n    \"skipLibCheck\": true,\n    \"noUnusedLocals\": true,\n    \"esModuleInterop\": true,\n    \"isolatedModules\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"forceConsistentCasingInFileNames\": true\n  },\n  \"include\": [\n    \"src\"\n  ],\n  \"exclude\": [\n    \"node_modules\"\n  ]\n}"
  }
]