diff --git a/README.md b/README.md index 183871b..4dda4f3 100644 --- a/README.md +++ b/README.md @@ -137,13 +137,7 @@ class App extends React.Component { render() { const { animal } = this.state; - return ( - ; } } ``` @@ -168,13 +162,7 @@ const App = () => { setAnimal(value); }; - return ( - ; }; export default App; @@ -209,12 +197,7 @@ const App = () => { }; return ( - ); }; @@ -225,25 +208,27 @@ export default App; This table shows all the options available in react-tailwindcss-select. -| Option | Type | Default | Description | -|-----------------------------------------------|------------|--------------------|----------------------------------------------------------------------------------------| -| [`classNames`](#classNames) | `Object` | `undefined` | This prop allows you to style most of the components used by this library. | -| `isClearable` | `Boolean` | `true` | Indicates if you can empty the select field. | -| `isDisabled` | `Boolean` | `false` | Indicates if you can disable the select field. | -| `isMultiple` | `Boolean` | `false` | Indicates if you can do a multiple selection. | -| `isSearchable` | `Boolean` | `true` | Indicates if you can search the elements of the select field. | -| [`formatGroupLabel`](#formatGroupLabel) | `Function` | `null` | Allows you to use a custom rendering template for each subgroup title | -| [`formatOptionLabel`](#formatOptionLabel) | `Function` | `null` | Allows you to use a custom rendering template for each option in the list | -| `loading` | `Boolean` | `false` | Indicates if you want a loader to appear in the field. | -| `menuIsOpen` | `Boolean` | `false` | Indicates if you want the options menu to be displayed by default. | -| `noOptionsMessage` | `String` | `No results found` | Default message when there is no option in the select field. | -| [`onChange`](#onChange) | `Function` | | This callback, if present, is triggered when the select field value is modified. | -| [`onSearchInputChange`](#onSearchInputChange) | `Function` | | This callback, if present, is triggered when the search input field value is modified. | -| [`options`](#options) | `Array` | `[]` | All options or options groups available in the selection field. | -| `placeholder` | `String` | `Select...` | The placeholder shown for the select field. | -| `primaryColor` | `String` | `blue` | Default theme of the field. | -| `searchInputPlaceholder` | `String` | `Search...` | The placeholder shown for the search input field. | -| [`value`](#value) | `Object` | `null` | Current value of select field. | +| Option | Type | Default | Description | +| --------------------------------------------- | ---------- | ---------------------------------- | -------------------------------------------------------------------------------------- | +| [`classNames`](#classNames) | `Object` | `undefined` | This prop allows you to style most of the components used by this library. | +| `isClearable` | `Boolean` | `true` | Indicates if you can empty the select field. | +| `isDisabled` | `Boolean` | `false` | Indicates if you can disable the select field. | +| `isMultiple` | `Boolean` | `false` | Indicates if you can do a multiple selection. | +| `isSearchable` | `Boolean` | `true` | Indicates if you can search the elements of the select field. | +| [`formatGroupLabel`](#formatGroupLabel) | `Function` | `null` | Allows you to use a custom rendering template for each subgroup title | +| [`formatOptionLabel`](#formatOptionLabel) | `Function` | `null` | Allows you to use a custom rendering template for each option in the list | +| `loading` | `Boolean` | `false` | Indicates if you want a loader to appear in the field. | +| `menuIsOpen` | `Boolean` | `false` | Indicates if you want the options menu to be displayed by default. | +| `noOptionsMessage` | `String` | `No results found` | Default message when there is no option in the select field. | +| [`onChange`](#onChange) | `Function` | | This callback, if present, is triggered when the select field value is modified. | +| [`onSearchInputChange`](#onSearchInputChange) | `Function` | | This callback, if present, is triggered when the search input field value is modified. | +| [`options`](#options) | `Array` | `[]` | All options or options groups available in the selection field. | +| `placeholder` | `String` | `Select...` | The placeholder shown for the select field. | +| `primaryColor` | `String` | `blue` | Default theme of the field. | +| `searchInputPlaceholder` | `String` | `Search...` | The placeholder shown for the search input field. | +| [`value`](#value) | `Object` | `null` | Current value of select field. | +| [`autoScrollOnMobile`](#autoScrollOnMobile) | `Object` | `{enabled:false, scrollHeight:50}` | Allows you to use a custom options for auto scroll | +| [`noHighLigthLabel`] | `Boolean` | `false` | Indicates if you can disable the label highlight | ### onChange @@ -259,8 +244,8 @@ currentValue => { ### onSearchInputChange -This callback, if present, is triggered when the search input field value is modified. This callback takes -as parameter a `React.ChangeEvent`. +This callback, if present, is triggered when the search input field value is modified. This callback +takes as parameter a `React.ChangeEvent`. ```js e => { @@ -369,7 +354,7 @@ const App = () => {
// 👉 data represents each subgroup {data.label} - + {data.options.length}
@@ -416,9 +401,7 @@ const App = () => { formatOptionLabel={data => (
  • // data represents each option in the list @@ -442,11 +425,14 @@ As of version 1.6.0 of `react-tailwindcss-select` you can now use the `className > **Info** > -> 👉 Note: this is not to be confused with the className prop, which will add a class to the component. +> 👉 Note: this is not to be confused with the className prop, which will add a class to the +> component. -`classNames` takes an object with keys to represent the various inner components that `react-tailwindcss-select` is made up of. +`classNames` takes an object with keys to represent the various inner components that +`react-tailwindcss-select` is made up of. -Each key takes a callback function or a string. If a key is not filled in, the default classes of the component will be used. +Each key takes a callback function or a string. If a key is not filled in, the default classes of +the component will be used. #### All keys @@ -456,7 +442,7 @@ interface SelectProps { classNames?: { menuButton?: (value?: { isDisabled?: boolean }) => string; menu?: string; - tagItem?: (value?: { item?: Option, isDisabled?: boolean }) => string; + tagItem?: (value?: { item?: Option; isDisabled?: boolean }) => string; tagItemText?: string; tagItemIconContainer?: string; tagItemIcon?: string; @@ -487,34 +473,32 @@ const options = [ ]; const App = () => { - const[animal, setAnimal] =useState(null); + const [animal, setAnimal] = useState(null); const handleChange = value => { console.log("value:", value); setAnimal(value); }; - return( + return ( + ); +}; + +export default App; +``` + ## PlayGround Clone the `master` branch and run commands: @@ -542,7 +568,8 @@ Open a browser and navigate to `http://localhost:8888` Got ideas on how to make this better? Open an issue! -Don't forget to see [CONTRIBUTING.md](https://github.com/onesine/react-tailwindcss-select/blob/master/CONTRIBUTING.md) +Don't forget to see +[CONTRIBUTING.md](https://github.com/onesine/react-tailwindcss-select/blob/master/CONTRIBUTING.md) ## Thanks diff --git a/pages/index.js b/pages/index.js index 4d421b9..5de8903 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,13 +1,14 @@ import Head from "next/head"; -import Select from "../src"; -import Header from "../components/Header"; -import Button from "../components/Button"; -import SelectContainer from "../components/SelectContainer"; import { useCallback, useEffect, useState } from "react"; -import TailwindColors from "../components/TailwindColors"; -import Checkbox from "../components/Checkbox"; + import Alert from "../components/Alert"; +import Button from "../components/Button"; +import Checkbox from "../components/Checkbox"; +import Header from "../components/Header"; import { DarkLink, LightLink } from "../components/Link"; +import SelectContainer from "../components/SelectContainer"; +import TailwindColors from "../components/TailwindColors"; +import Select from "../src"; const MANGAS = [ { @@ -239,6 +240,8 @@ const Home = () => { isClearable={isClearable} isSearchable={isSearchable} isMultiple={isMultiple} + // noHighLigthLabel={true} + // autoScrollOnMobile={{ enabled: true, scrollHeight: 100 }} /*formatGroupLabel={(data) => (
    {data.label} diff --git a/src/components/GroupItem.tsx b/src/components/GroupItem.tsx index 6d5596c..e0a8e49 100644 --- a/src/components/GroupItem.tsx +++ b/src/components/GroupItem.tsx @@ -7,9 +7,16 @@ import { GroupOption } from "./type"; interface GroupItemProps { item: GroupOption; primaryColor: string; + searchInputValue?: string; + noHighLigthLabel: boolean; } -const GroupItem: React.FC = ({ item, primaryColor }) => { +const GroupItem: React.FC = ({ + item, + primaryColor, + searchInputValue, + noHighLigthLabel +}) => { const { classNames, formatGroupLabel } = useSelectContext(); return ( @@ -31,7 +38,13 @@ const GroupItem: React.FC = ({ item, primaryColor }) => { )} {item.options.map((item, index) => ( - + ))} )} diff --git a/src/components/Item.tsx b/src/components/Item.tsx index 58b4f41..2ad5eae 100755 --- a/src/components/Item.tsx +++ b/src/components/Item.tsx @@ -9,9 +9,11 @@ import { Option } from "./type"; interface ItemProps { item: Option; primaryColor: string; + searchInputValue?: string; + noHighLigthLabel: boolean; } -const Item: React.FC = ({ item, primaryColor }) => { +const Item: React.FC = ({ item, primaryColor, searchInputValue, noHighLigthLabel }) => { const { classNames, value, handleValueChange, formatOptionLabel } = useSelectContext(); const isSelected = useMemo(() => { @@ -57,6 +59,26 @@ const Item: React.FC = ({ item, primaryColor }) => { : `${baseClass} ${selectedClass}`; }, [bgColor, bgHoverColor, classNames, isSelected, textHoverColor]); + const getLabel = useCallback(() => { + if (!noHighLigthLabel && searchInputValue) { + const start = item.label.toUpperCase().indexOf(searchInputValue.toUpperCase()); + const end = start + searchInputValue.length; + return item.label.split("").map((label, idx) => { + if (idx >= start && idx < end) { + return ( + + {label} + + ); + } else { + return {label}; + } + }); + } else { + return {item.label}; + } + }, [item.label, noHighLigthLabel, primaryColor, searchInputValue]); + return ( <> {formatOptionLabel ? ( @@ -74,7 +96,7 @@ const Item: React.FC = ({ item, primaryColor }) => { onClick={() => handleValueChange(item)} className={getItemClass()} > - {item.label} + {getLabel()}
  • )} diff --git a/src/components/Options.tsx b/src/components/Options.tsx index 85e27d0..8e44947 100755 --- a/src/components/Options.tsx +++ b/src/components/Options.tsx @@ -15,6 +15,7 @@ interface OptionsProps { isMultiple: boolean; value: Option | Option[] | null; primaryColor: string; + noHighLigthLabel: boolean; } const Options: React.FC = ({ @@ -23,7 +24,8 @@ const Options: React.FC = ({ text, isMultiple, value, - primaryColor = DEFAULT_THEME + primaryColor = DEFAULT_THEME, + noHighLigthLabel }) => { const { classNames } = useContext(SelectContext); const filterByText = useCallback(() => { @@ -104,6 +106,8 @@ const Options: React.FC = ({ @@ -111,7 +115,12 @@ const Options: React.FC = ({ ) : (
    - +
    )} diff --git a/src/components/Select.tsx b/src/components/Select.tsx index e0b38ed..e4b4863 100755 --- a/src/components/Select.tsx +++ b/src/components/Select.tsx @@ -8,7 +8,14 @@ import Options from "./Options"; import SearchInput from "./SearchInput"; import SelectProvider from "./SelectProvider"; import Spinner from "./Spinner"; -import { ClassNames, GroupOption, Option, Options as ListOption, SelectValue } from "./type"; +import { + ClassNames, + GroupOption, + Option, + Options as ListOption, + SelectValue, + AutoScrollOption +} from "./type"; interface SelectProps { options: ListOption; @@ -24,7 +31,9 @@ interface SelectProps { menuIsOpen?: boolean; searchInputPlaceholder?: string; noOptionsMessage?: string; + noHighLigthLabel?: boolean; primaryColor: string; + autoScrollOnMobile?: AutoScrollOption; formatGroupLabel?: ((data: GroupOption) => JSX.Element) | null; formatOptionLabel?: ((data: Option) => JSX.Element) | null; classNames?: ClassNames; @@ -43,6 +52,11 @@ const Select: React.FC = ({ isDisabled = false, loading = false, menuIsOpen = false, + autoScrollOnMobile = { + enabled: false, + scrollHeight: 50 + }, + noHighLigthLabel = false, noOptionsMessage = "No options found", primaryColor = DEFAULT_THEME, formatGroupLabel = null, @@ -189,6 +203,17 @@ const Select: React.FC = ({ [classNames, isDisabled] ); + const scrollOnMobile = useCallback(() => { + if (autoScrollOnMobile.enabled && ref.current?.offsetTop) { + if (ref.current.offsetWidth < 640 && ref.current?.offsetTop / 2 >= window.scrollY) { + window.scrollTo({ + top: ref.current?.offsetTop / 2 + autoScrollOnMobile.scrollHeight, + behavior: "smooth" + }); + } + } + }, [autoScrollOnMobile.enabled, autoScrollOnMobile.scrollHeight]); + return ( = ({ value={value} handleValueChange={handleValueChange} > -
    +
    = ({ isMultiple={isMultiple} value={value} primaryColor={primaryColor || DEFAULT_THEME} + noHighLigthLabel={noHighLigthLabel} />
    )} diff --git a/src/components/type.ts b/src/components/type.ts index cd302d7..3875e76 100644 --- a/src/components/type.ts +++ b/src/components/type.ts @@ -10,6 +10,11 @@ export interface GroupOption { options: Option[]; } +export interface AutoScrollOption { + enabled: boolean; + scrollHeight: number; +} + export type Options = Array