├── src ├── index.css ├── index.tsx ├── components │ ├── DisabledItem.tsx │ ├── Spinner.tsx │ ├── GroupItem.tsx │ ├── type.ts │ ├── SearchInput.tsx │ ├── Icons.tsx │ ├── SelectProvider.tsx │ ├── Item.tsx │ ├── Options.tsx │ └── Select.tsx ├── hooks │ └── use-onclick-outside.ts └── constants │ └── index.ts ├── .prettierignore ├── .eslintignore ├── next.config.js ├── assets └── img │ └── Screen_Shot_2022-08-04_at_17.04.09.png ├── pages ├── _app.js └── index.js ├── postcss.config.js ├── next-env.d.ts ├── page-components ├── Header.jsx ├── SelectContainer.jsx ├── Checkbox.jsx ├── Link.jsx ├── Alert.jsx ├── Button.jsx └── TailwindColors.jsx ├── .prettierrc ├── tailwind.config.js ├── .npmignore ├── .gitignore ├── rollup.config.js ├── tsconfig.json ├── LICENSE ├── .eslintrc.json ├── CONTRIBUTING.md ├── package.json ├── README.md └── yarn.lock /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import Select from "./components/Select"; 2 | 3 | export default Select; 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Folders 2 | dist/ 3 | assets/ 4 | .next/ 5 | .rollup.cache/ 6 | 7 | # Files 8 | README.md -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Folders 2 | dist/ 3 | assets/ 4 | pages/ 5 | components/ 6 | styles/ 7 | 8 | # Files 9 | README.md -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true 4 | }; 5 | 6 | module.exports = nextConfig; 7 | -------------------------------------------------------------------------------- /assets/img/Screen_Shot_2022-08-04_at_17.04.09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onesine/react-tailwindcss-select/HEAD/assets/img/Screen_Shot_2022-08-04_at_17.04.09.png -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import "../src/index.css"; 2 | 3 | const App = ({ Component, pageProps }) => { 4 | return ; 5 | }; 6 | 7 | export default App; 8 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | ...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {}) 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /page-components/Header.jsx: -------------------------------------------------------------------------------- 1 | const Header = ({ children }) => { 2 | return ( 3 |
4 |
{children}
5 |
6 | ); 7 | }; 8 | 9 | export default Header; 10 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "tabWidth": 4, 4 | "printWidth": 100, 5 | "singleQuote": false, 6 | "trailingComma": "none", 7 | "quoteProps": "as-needed", 8 | "jsxSingleQuote": false, 9 | "bracketSpacing": true, 10 | "arrowParens": "avoid", 11 | "proseWrap": "always" 12 | } 13 | -------------------------------------------------------------------------------- /page-components/SelectContainer.jsx: -------------------------------------------------------------------------------- 1 | const SelectContainer = ({ children }) => { 2 | return ( 3 |
4 |
{children}
5 |
6 | ); 7 | }; 8 | 9 | export default SelectContainer; 10 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./pages/**/*.{js,ts,jsx,tsx}", 5 | "./page-components/**/*.{js,ts,jsx,tsx}", 6 | "./src/**/*.{js,ts,jsx,tsx}" 7 | ], 8 | theme: { 9 | extend: {} 10 | }, 11 | plugins: [require("@tailwindcss/forms")] 12 | }; 13 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | ## Folders 2 | src 3 | node_modules 4 | .vscode 5 | .idea 6 | assets 7 | .git 8 | pages 9 | page-components 10 | styles 11 | .next 12 | .rollup.cache 13 | ## Files 14 | babel.config.json 15 | tsconfig.json 16 | rollup.config.js 17 | next.config.js 18 | next-env.d.ts 19 | .gitignore 20 | .eslintignore 21 | .eslintrc.json 22 | .prettierrc 23 | .prettierignore 24 | .DS_Store 25 | npm-debug.log 26 | package-lock.json 27 | yarn.lock 28 | tailwind.config.js 29 | postcss.config.js 30 | tsconfig.tsbuildinfo 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | /.next/ 6 | /.rollup.cache/ 7 | 8 | # test coverage 9 | coverage 10 | 11 | # builds 12 | build 13 | dist 14 | .rpt2_cache 15 | .eslintcache 16 | tsconfig.tsbuildinfo 17 | 18 | # misc 19 | .DS_Store 20 | .env 21 | .env.local 22 | .env.development.local 23 | .env.test.local 24 | .env.production.local 25 | .yarn 26 | .yarnrc.yml 27 | 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | .vscode 33 | .idea -------------------------------------------------------------------------------- /page-components/Checkbox.jsx: -------------------------------------------------------------------------------- 1 | const Checkbox = ({ children, checked, onChange, id }) => { 2 | return ( 3 | 13 | ); 14 | }; 15 | 16 | export default Checkbox; 17 | -------------------------------------------------------------------------------- /page-components/Link.jsx: -------------------------------------------------------------------------------- 1 | export const DarkLink = ({ children, url }) => { 2 | return ( 3 | 9 | {children} 10 | 11 | ); 12 | }; 13 | 14 | export const LightLink = ({ children, url }) => { 15 | return ( 16 | 22 | {children} 23 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/components/DisabledItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | 3 | import { SelectContext } from "./SelectProvider"; 4 | 5 | interface DisabledItemProps { 6 | children: JSX.Element | string; 7 | } 8 | 9 | const DisabledItem: React.FC = ({ children }) => { 10 | const { classNames } = useContext(SelectContext); 11 | return ( 12 |
19 | {children} 20 |
21 | ); 22 | }; 23 | 24 | export default DisabledItem; 25 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import commonjs from "@rollup/plugin-commonjs"; 2 | import resolve from "@rollup/plugin-node-resolve"; 3 | import typescript from "@rollup/plugin-typescript"; 4 | 5 | const packageJson = require("./package.json"); 6 | const options = require("./tsconfig.json"); 7 | 8 | module.exports = { 9 | input: "src/index.tsx", 10 | output: [ 11 | { 12 | file: packageJson.main, 13 | format: "cjs", 14 | exports: "auto", 15 | sourcemap: true 16 | }, 17 | { 18 | file: packageJson.module, 19 | format: "esm", 20 | exports: "auto", 21 | sourcemap: true 22 | } 23 | ], 24 | external: ["react"], 25 | plugins: [resolve(), commonjs(), typescript({ ...options.compilerOptions, jsx: "react" })] 26 | }; 27 | -------------------------------------------------------------------------------- /src/hooks/use-onclick-outside.ts: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | 3 | export default function useOnClickOutside( 4 | ref: React.RefObject, 5 | handler: (e?: MouseEvent | TouchEvent) => void 6 | ) { 7 | useEffect(() => { 8 | const listener = (event: MouseEvent | TouchEvent) => { 9 | if (!ref.current || ref.current.contains(event.target as Node)) { 10 | return; 11 | } 12 | 13 | handler(event); 14 | }; 15 | 16 | document.addEventListener("mousedown", listener); 17 | document.addEventListener("touchstart", listener); 18 | 19 | return () => { 20 | document.removeEventListener("mousedown", listener); 21 | document.removeEventListener("touchstart", listener); 22 | }; 23 | }, [ref, handler]); 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "lib": ["dom", "esnext"], 5 | "module": "esnext", 6 | "jsx": "preserve", 7 | "moduleResolution": "node", 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "noImplicitReturns": true, 11 | "allowSyntheticDefaultImports": true, 12 | "esModuleInterop": true, 13 | "baseUrl": "src/", 14 | "declaration": true, 15 | "outDir": "./dist", 16 | "inlineSources": true, 17 | "sourceMap": true, 18 | "rootDir": "src", 19 | "allowJs": true, 20 | "skipLibCheck": true, 21 | "noEmit": true, 22 | "incremental": true, 23 | "resolveJsonModule": true, 24 | "isolatedModules": true 25 | }, 26 | "include": ["src/**/*"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Onesine 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/components/Spinner.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | 3 | import { COLORS, DEFAULT_THEME, THEME_DATA } from "../constants"; 4 | 5 | interface Props { 6 | primaryColor?: string; 7 | } 8 | 9 | const Spinner: React.FC = ({ primaryColor = DEFAULT_THEME }) => { 10 | const spinnerColor = useMemo(() => { 11 | if (COLORS.includes(primaryColor)) { 12 | return THEME_DATA.text[primaryColor as keyof typeof THEME_DATA.text]; 13 | } 14 | return THEME_DATA.text[DEFAULT_THEME]; 15 | }, [primaryColor]); 16 | 17 | return ( 18 | 24 | 32 | 37 | 38 | ); 39 | }; 40 | 41 | export default Spinner; 42 | -------------------------------------------------------------------------------- /page-components/Alert.jsx: -------------------------------------------------------------------------------- 1 | const Alert = ({ children, title, type = "info" }) => { 2 | return ( 3 |
7 |
8 |
9 | {type === "info" && ( 10 | 17 | 23 | 24 | )} 25 |
26 | 27 |
28 |

{title}

29 | 30 | {children} 31 |
32 |
33 |
34 | ); 35 | }; 36 | 37 | export default Alert; 38 | -------------------------------------------------------------------------------- /src/components/GroupItem.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Item from "./Item"; 4 | import { useSelectContext } from "./SelectProvider"; 5 | import { GroupOption } from "./type"; 6 | 7 | interface GroupItemProps { 8 | item: GroupOption; 9 | primaryColor: string; 10 | } 11 | 12 | const GroupItem: React.FC = ({ item, primaryColor }) => { 13 | const { classNames, formatGroupLabel } = useSelectContext(); 14 | 15 | return ( 16 | <> 17 | {item.options.length > 0 && ( 18 | <> 19 | {formatGroupLabel ? ( 20 | <>{formatGroupLabel(item)} 21 | ) : ( 22 |
29 | {item.label} 30 |
31 | )} 32 | 33 | {item.options.map((item, index) => ( 34 | 35 | ))} 36 | 37 | )} 38 | 39 | ); 40 | }; 41 | 42 | export default GroupItem; 43 | -------------------------------------------------------------------------------- /src/components/type.ts: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export interface Option { 4 | value: string; 5 | label: string; 6 | disabled?: boolean; 7 | isSelected?: boolean; 8 | } 9 | 10 | export interface GroupOption { 11 | label: string; 12 | options: Option[]; 13 | } 14 | 15 | export type Options = Array