Repository: based-ghost/react-functional-select Branch: master Commit: 8929ba9b8db3 Files: 90 Total size: 196.5 KB Directory structure: gitextract_i9kdersq/ ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github/ │ └── workflows/ │ └── chromatic.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── .storybook/ │ ├── global-style/ │ │ ├── global-style.ts │ │ ├── index.ts │ │ └── react-toastify-override.ts │ ├── main.ts │ ├── manager-head.html │ ├── manager.ts │ └── preview.tsx ├── .test/ │ ├── custom-test-env.ts │ └── setup-tests.ts ├── .travis.yml ├── LICENSE ├── README.md ├── __stories__/ │ ├── helpers/ │ │ ├── components/ │ │ │ ├── Checkbox.tsx │ │ │ ├── CodeMarkup.tsx │ │ │ ├── OptionsCountButton.tsx │ │ │ ├── PackageLink.tsx │ │ │ └── index.ts │ │ ├── constants/ │ │ │ ├── index.ts │ │ │ ├── markup.ts │ │ │ ├── npm-package.ts │ │ │ ├── options-data.ts │ │ │ ├── react-toastify.ts │ │ │ ├── svg-props.ts │ │ │ └── theme.ts │ │ ├── hooks/ │ │ │ ├── index.ts │ │ │ └── useCallbackState.ts │ │ ├── index.ts │ │ ├── styled/ │ │ │ └── index.ts │ │ └── utils/ │ │ └── index.ts │ ├── index.stories.tsx │ └── types/ │ └── index.d.ts ├── __tests__/ │ ├── AriaLiveRegion.test.tsx │ ├── AutosizeInput.test.tsx │ ├── IndicatorIcons.test.tsx │ ├── LoadingDots.test.tsx │ ├── MenuList.test.tsx │ ├── MultiValue.test.tsx │ ├── Option.test.tsx │ ├── ReactSSR.test.tsx │ ├── Select.test.tsx │ ├── Value.test.tsx │ └── helpers/ │ ├── ThemeWrapper.tsx │ ├── index.ts │ └── utils.ts ├── babel.config.js ├── jest.config.js ├── package.json ├── rollup.config.js ├── src/ │ ├── Select.tsx │ ├── components/ │ │ ├── AriaLiveRegion/ │ │ │ └── index.tsx │ │ ├── AutosizeInput/ │ │ │ └── index.tsx │ │ ├── IndicatorIcons/ │ │ │ ├── ClearIcon.tsx │ │ │ ├── LoadingDots.tsx │ │ │ └── index.tsx │ │ ├── Menu/ │ │ │ ├── MenuList.tsx │ │ │ ├── Option.tsx │ │ │ └── index.tsx │ │ ├── Value/ │ │ │ ├── MultiValue.tsx │ │ │ └── index.tsx │ │ └── index.ts │ ├── constants/ │ │ ├── defaults.ts │ │ ├── dom.ts │ │ ├── enums.ts │ │ ├── index.ts │ │ ├── styled.ts │ │ └── theme.ts │ ├── globals.d.ts │ ├── hooks/ │ │ ├── index.ts │ │ ├── useCallbackRef.ts │ │ ├── useDebounce.ts │ │ ├── useLatestRef.ts │ │ ├── useMenuOptions.ts │ │ ├── useMenuPosition.ts │ │ ├── useMountEffect.ts │ │ └── useUpdateEffect.ts │ ├── index.ts │ ├── types.ts │ └── utils/ │ ├── common.ts │ ├── device.ts │ ├── index.ts │ └── menu.ts └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # http://editorconfig.org root = true [*] charset = utf-8 indent_size = 2 end_of_line = lf indent_style = space insert_final_newline = true trim_trailing_whitespace = true [*.md] max_line_length = 0 trim_trailing_whitespace = false [COMMIT_EDITMSG] max_line_length = 0 ================================================ FILE: .eslintignore ================================================ dist node_modules ================================================ FILE: .eslintrc ================================================ { "parser": "@typescript-eslint/parser", "extends": [ "plugin:react-hooks/recommended", "plugin:@typescript-eslint/recommended" ], "plugins": [ "react", "@typescript-eslint", "prettier" ], "env": { "browser": true, "node": true, "es6": true, "jest": true }, "rules": { "react/prop-types": 0, "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn", "semi": "off", "sort-keys": "off", "global-require": "off", "spaced-comment": "off", "capitalized-comments": "off", "padding-line-between-statements": "off", "@typescript-eslint/no-var-requires": 0, "@typescript-eslint/no-explicit-any": 0, "@typescript-eslint/no-inferrable-types": 0, "@typescript-eslint/no-non-null-assertion": 0 }, "parserOptions": { "sourceType": "module", "ecmaVersion": 2020, "ecmaFeatures": { "jsx": true } }, "settings": { "react": { "version": "detect" } } } ================================================ FILE: .github/workflows/chromatic.yml ================================================ # .github/workflows/chromatic.yml # Workflow name name: 'Chromatic' # Event for the workflow on: push jobs: chromatic-deployment: # Operating System runs-on: ubuntu-latest # Job steps steps: # 👇 Adds Chromatic as a step in the workflow - name: Publish to Chromatic uses: chromaui/action@v1 # Options required to the GitHub chromatic action with: # 👇 Chromatic projectToken, refer to the manage page to obtain it. projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} autoAcceptChanges: true ================================================ FILE: .gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # Editor directories and files .idea .vs .vscode .cache # dependencies node_modules # testing coverage # production lib dist build # Log files logs *.log # misc .DS_Store storybook-static package-lock.json chromatic-diagnostics.json ================================================ FILE: .npmrc ================================================ registry = "https://registry.npmjs.com/" ================================================ FILE: .prettierignore ================================================ dist node_modules ================================================ FILE: .prettierrc ================================================ { "bracketSpacing": false, "printWidth": 100, "trailingComma": "es5", "tabWidth": 2, "singleQuote": true, "endOfLine": "auto" } ================================================ FILE: .storybook/global-style/global-style.ts ================================================ import { createGlobalStyle } from 'styled-components'; import ReactToastifyOverride from './react-toastify-override'; const GlobalStyle = createGlobalStyle` *, *::before, *::after { box-sizing: border-box; } html { line-height: 1.15; text-size-adjust: 100%; -moz-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; text-rendering: optimizeLegibility; -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } body { flex: 1; margin: 0; display: flex; color: #262626; font-size: 1rem; font-weight: 400; text-align: left; line-height: 1.5; min-height: 120vh; flex-direction: column; background-color: #fff; padding: 1rem 0 !important; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif; } em { font-weight: 600; } strong { font-weight: 600; font-size: 1.025em; } code { font-size: 90%; color: #476582; line-height: 1.7; border-radius: 4px; padding: .175em .475em; word-break: break-word; background-color: #f1f1f1; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; } ${ReactToastifyOverride} `; export default GlobalStyle; ================================================ FILE: .storybook/global-style/index.ts ================================================ export { default as GlobalStyle } from './global-style'; ================================================ FILE: .storybook/global-style/react-toastify-override.ts ================================================ import { css, keyframes } from 'styled-components'; const TOASTIFY_BOUNCE_OUT = keyframes` 20% { transform: scale3d(0.9, 0.9, 0.9); } 50%, 55% { opacity: 1; transform: scale3d(1.1, 1.1, 1.1); } to { opacity: 0; transform: scale3d(0.3, 0.3, 0.3); } `; const TOASTIFY_BOUNCE_IN = keyframes` from, 20%, 40%, 60%, 80%, to { animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } 0% { opacity: 0; transform: scale3d(0.3, 0.3, 0.3); } 20% { transform: scale3d(1.1, 1.1, 1.1); } 40% { transform: scale3d(0.9, 0.9, 0.9); } 60% { opacity: 1; transform: scale3d(1.03, 1.03, 1.03); } 80% { transform: scale3d(0.97, 0.97, 0.97); } to { opacity: 1; transform: scale3d(1, 1, 1); } `; export default css` .Toastify__animate__bounceIn { animation: ${TOASTIFY_BOUNCE_IN} 1s both; } .Toastify__animate__bounceOut { animation: ${TOASTIFY_BOUNCE_OUT} 0.85s both; } .Toastify__toast-container { .Toastify__toast { background: #292d3e; &-body { color: #C3C9E6; } &-icon > svg { fill: #85ADFF; } } .Toastify__close-button { color: #fff; } .Toastify__progress-bar { background-color: #85ADFF; } } `; ================================================ FILE: .storybook/main.ts ================================================ import type { StorybookConfig } from '@storybook/react/types'; const config: StorybookConfig = { framework: '@storybook/react', addons: ['@storybook/addon-storysource'], stories: ['../__stories__/**/*.stories.@(js|tsx|mdx)'], core: { builder: 'webpack5', disableTelemetry: true, enableCrashReports: false } }; export default config; ================================================ FILE: .storybook/manager-head.html ================================================ ================================================ FILE: .storybook/manager.ts ================================================ import { addons } from '@storybook/addons'; import { create } from '@storybook/theming'; const theme = create({ base: 'light', appBg: '#E6E6E6', barBg: '#E0E0E0', barTextColor: '#7F7F7F', colorSecondary: '#1ea7fd', appBorderColor: '#D3D3D3', brandUrl: 'https://master--625676b6922472003af898b4.chromatic.com' }); addons.setConfig({ theme, showNav: true, showPanel: true }); ================================================ FILE: .storybook/preview.tsx ================================================ import React, { Fragment } from 'react'; import { GlobalStyle } from './global-style'; import type { DecoratorFn } from '@storybook/react'; // import react-toastify CSS files (overrides in react-toastify-override.ts) import 'react-toastify/dist/ReactToastify.css'; const withGlobalStyle: DecoratorFn = (Story) => ( ); export const decorators = [withGlobalStyle]; ================================================ FILE: .test/custom-test-env.ts ================================================ import Environment from 'jest-environment-jsdom'; /** * A custom environment to set the TextEncoder that is required by react-dom/server */ module.exports = class CustomTestEnvironment extends Environment { async setup() { await super.setup(); if (typeof this.global.TextEncoder === 'undefined') { const { TextEncoder } = await import('util'); this.global.TextEncoder = TextEncoder; } } } ================================================ FILE: .test/setup-tests.ts ================================================ import '@testing-library/jest-dom'; ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - node ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021 Matthew Areddia Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [![NPM](https://img.shields.io/npm/v/react-functional-select.svg?style=flat-square)](https://www.npmjs.com/package/react-functional-select) [![npm downloads](https://img.shields.io/npm/dt/react-functional-select.svg?style=flat-square)](https://www.npmjs.com/package/react-functional-select) [![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) [![License](https://img.shields.io/badge/license-mit-red.svg?style=flat-square)](LICENSE) [![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) # react-functional-select > Micro-sized & micro-optimized select component for React.js See the accompanying [Interactive Storybook UI Site](https://master--625676b6922472003af898b4.chromatic.com) for live demos and detailed docs. Key features: - Extremely lightweight: ~6 kB (gzipped)! - Advanced features like async mode, portal support, animations, and option virtualization - Fully-featured & customizable: API comparable to [`react-select`](https://github.com/JedWatson/react-select) - 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) - Extensible styling API with [`styled-components`](https://github.com/styled-components/styled-components) - Accessible Peer dependencies: - [`styled-components`](https://github.com/styled-components/styled-components) for dynamic styling/theming via CSS-in-JS - [`react-window`](https://github.com/bvaughn/react-window) for integrated menu option data virtualization ## Overview Essentially, 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. Any 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. ## Installation ``` $ npm i react-window styled-components react-functional-select $ yarn add react-window styled-components react-functional-select ``` > Note that you need to be on a react version that supports hooks (>= 16.8.6) ## Usage - [Demo](https://master--625676b6922472003af898b4.chromatic.com) - [Stories source code](./__stories__) ```jsx import { Select } from 'react-functional-select'; import React, { useState, useEffect, useCallback, type ComponentProps } from 'react'; import { Card, CardHeader, CardBody, Container, SelectContainer } from '../shared/components'; type SelectProps = ComponentProps; type Option = Readonly<{ id: number; city: string; state: string; }>; const CITY_OPTIONS: Option[] = [ { id: 1, city: 'Austin', state: 'TX' }, { id: 2, city: 'Denver', state: 'CO' }, { id: 3, city: 'Chicago', state: 'IL' }, { id: 4, city: 'Phoenix', state: 'AZ' }, { id: 5, city: 'Houston', state: 'TX' } ]; const SingleSelect: React.FC = ({ isDisabled }) => { const [isInvalid, setIsInvalid] = useState(false); const [selectedOption, setSelectedOption] = useState