import React, { useEffect, useMemo, useState } from "react"; import { STATUS_VARIANT } from "../../types"; import { FormGroup } from "../FormGroup"; import { Icon, ICON_TYPE } from "../Icon"; import { Label } from "../Label"; import { Stack } from "../Stack"; import { Text } from "../Text"; import { SelectOptionCategory } from "./shared/SelectOptionCategory"; import { SingleSelect } from "./SingleSelect"; import { SelectOption, SelectOptionsByCategory, SingleSelectedOption, } from "./shared/types"; import { useDisclosure } from "../../hooks"; import { Button } from "../Button"; import { Modal } from "../Modal"; export const DemoSingleSelectSimple = () => { const [selected, setSelected] = useState(null); const OPTIONS = [ { label: "Butter Pecan", value: "butter-pecan" }, { label: "Chocolate", value: "chocolate" }, { label: "Chocolate Chip Cookie Dough", value: "chocolate-chip-cookie-dough", }, { label: "Cookie Dough", value: "cookie-dough" }, { label: "Cookies and Cream", value: "cookies-and-cream" }, { label: "Mint Chocolate Chip", value: "mint-chocolate-chip" }, { label: "Neapolitan", value: "neapolitan" }, { label: "Moose Tracks", value: "moose-tracks" }, { label: "Rocky Road", value: "rocky-road" }, { label: "Strawberry", value: "strawberry" }, { label: "Vanilla", value: "vanilla" }, ]; return ( {OPTIONS.map((o) => ( ))} ); }; interface IceCreamExtended extends SelectOption { dairyFree: boolean; } export const DemoSingleSelectCategories = () => { const OPTIONS_WITH_CATEGORIES: SelectOptionsByCategory[] = useMemo( () => [ { title: "Nuts", options: [ { label: "Maple Walnut", value: "maple-walnut", dairyFree: false }, { label: "Moose Tracks", value: "moose-tracks", dairyFree: false }, { label: "Pistachio", value: "pistachio", dairyFree: false }, { label: "Rocky Road", value: "rocky-road", dairyFree: false }, ], }, { title: "No Nuts", options: [ { label: "Butter Pecan", value: "butter-pecan", dairyFree: false }, { label: "Chocolate", value: "chocolate", dairyFree: true }, { label: "Chocolate Chip Cookie Dough", value: "chocolate-chip-cookie-dough", dairyFree: false, }, { label: "Cookie Dough", value: "cookie-dough", dairyFree: false }, { label: "Cookies and Cream", value: "cookies-and-cream", dairyFree: true, }, { label: "Strawberry", value: "strawberry", dairyFree: true }, { label: "Vanilla", value: "vanilla", dairyFree: false }, ], }, ], [], ); const renderIceCream = (option: IceCreamExtended) => option.dairyFree ? `${option.label} - Dairy Free` : `${option.label} 🐮`; const [selected, setSelected] = useState< SingleSelectedOption >(null); const [search, setSearch] = useState(""); const filteredOptions: SelectOptionsByCategory[] = useMemo(() => { if (!search) return OPTIONS_WITH_CATEGORIES; return OPTIONS_WITH_CATEGORIES.reduce< SelectOptionsByCategory[] >((acc, cur) => { const filteredCatOptions = cur.options.filter( (o) => o.label.toLowerCase().indexOf(search.toLowerCase()) >= 0, ); return filteredCatOptions.length > 0 ? [...acc, { ...cur, options: filteredCatOptions }] : acc; }, []); }, [search, OPTIONS_WITH_CATEGORIES]); return ( label="Favorite Ice Cream" optionsDisplay="categories" options={filteredOptions} selected={selected} onChange={setSelected} renderSelected={({ selectedItem }) => { if (!selectedItem) return null; return renderIceCream(selectedItem); }} inputValue={search} onInputChange={setSearch} isClearable > {filteredOptions.map((cat) => ( {cat.options.map((o) => ( {renderIceCream(o)} ))} ))} ); }; export const DemoSingleSelectForm = () => { const [selected, setSelected] = useState(null); const OPTIONS = [ { label: "Chocolate", value: "chocolate" }, { label: "Strawberry", value: "strawberry" }, { label: "Vanilla", value: "vanilla" }, ]; return ( {OPTIONS.map((o) => ( {`👉 ${o.label}`} ))} ); }; export const DemoSingleSelectNoResults = () => { const [selected, setSelected] = useState(null); const [search, setSearch] = useState(""); const OPTIONS = useMemo( () => [ { label: "Chocolate", value: "chocolate" }, { label: "Strawberry", value: "strawberry" }, { label: "Vanilla", value: "vanilla" }, ], [], ); const filteredOptions = useMemo( () => OPTIONS.filter( (o) => o.label.toLowerCase().indexOf(search.toLowerCase()) >= 0, ), [search, OPTIONS], ); return ( {filteredOptions.map((o) => ( ))} ); }; export const DemoSingleSelectNoResultsCustom = () => { const [selected, setSelected] = useState(null); const [search, setSearch] = useState(""); const OPTIONS = useMemo( () => [ { label: "Chocolate", value: "chocolate" }, { label: "Strawberry", value: "strawberry" }, { label: "Vanilla", value: "vanilla" }, ], [], ); const filteredOptions = useMemo( () => OPTIONS.filter( (o) => o.label.toLowerCase().indexOf(search.toLowerCase()) >= 0, ), [search, OPTIONS], ); return ( {filteredOptions.length > 0 ? ( filteredOptions.map((o) => ( )) ) : ( No ice cream found 🍦❌ )} ); }; export const DemoSingleSelectLoading = () => { return ( {}} isLoading > {[]} ); }; export const DemoSingleSelectLoadingCustom = () => { return ( {}} isLoading > brb fetching ice cream... 🏃‍♀️ ); }; export const DemoSingleSelectAsyncExample = () => { const [selected, setSelected] = useState(null); const [search, setSearch] = useState(""); const [options, setOptions] = useState([]); const [isLoading, setIsLoading] = useState(false); const OPTIONS = useMemo( () => [ { label: "Chocolate", value: "chocolate" }, { label: "Strawberry", value: "strawberry" }, { label: "Vanilla", value: "vanilla" }, ], [], ); // Emulate server request useEffect(() => { if (search) { setIsLoading(true); setTimeout(() => { setIsLoading(false); setOptions(OPTIONS); }, 1000); } }, [search, OPTIONS]); const filteredOptions = useMemo( () => options.filter( (o) => o.label.toLowerCase().indexOf(search.toLowerCase()) >= 0, ), [options, search], ); return ( {options.length === 0 && !isLoading ? ( Search for ice cream ) : ( filteredOptions.map((o) => ( )) )} ); }; export const DemoSingleSelectPlaceholder = () => { const [selected, setSelected] = useState(null); const OPTIONS = [ { label: "Chocolate", value: "chocolate" }, { label: "Strawberry", value: "strawberry" }, { label: "Vanilla", value: "vanilla" }, ]; return ( {OPTIONS.map((o) => ( ))} ); }; export const DemoSingleSelectPlaceholderElements = () => { const [selected, setSelected] = useState(null); const OPTIONS = [ { label: "Chocolate", value: "chocolate" }, { label: "Strawberry", value: "strawberry" }, { label: "Vanilla", value: "vanilla" }, ]; return ( Favorite ice cream? } > {OPTIONS.map((o) => ( ))} ); }; export const DemoSingleSelectClearable = () => { const [selected, setSelected] = useState(null); const OPTIONS = [ { label: "Chocolate", value: "chocolate" }, { label: "Strawberry", value: "strawberry" }, { label: "Vanilla", value: "vanilla" }, ]; return ( {OPTIONS.map((o) => ( ))} ); }; export const DemoSingleSelectCreatable = () => { const [selected, setSelected] = useState(null); const [search, setSearch] = useState(""); const [isLoading, setIsLoading] = useState(false); const OPTIONS = [ { label: "Chocolate", value: "chocolate" }, { label: "Strawberry", value: "strawberry" }, { label: "Vanilla", value: "vanilla" }, ]; const [options, setOptions] = useState(OPTIONS); return ( { // Create the new option and format the value however you want const newOption = { label: newLabel, value: newLabel.toLowerCase().replace(/\W/g, ""), }; // Create the new options array and sort it const newOptions = [...options, newOption].sort((a, b) => a.label.toLowerCase().localeCompare(b.label.toLowerCase()), ); // set selected state setSelected(newOption); // emulate creating option on server setIsLoading(true); setTimeout(() => { setIsLoading(false); // set the new options setOptions(newOptions); }, 1000); }} > {options.map((o) => ( ))} ); }; export const DemoSingleSelectCategoriesCreatable = () => { const OPTIONS_WITH_CATEGORIES: SelectOptionsByCategory[] = [ { title: "Nuts", options: [ { label: "Maple Walnut", value: "maple-walnut", dairyFree: false }, { label: "Moose Tracks", value: "moose-tracks", dairyFree: false }, { label: "Pistachio", value: "pistachio", dairyFree: false }, { label: "Rocky Road", value: "rocky-road", dairyFree: false }, ], }, { title: "No Nuts", options: [ { label: "Butter Pecan", value: "butter-pecan", dairyFree: false }, { label: "Chocolate", value: "chocolate", dairyFree: true }, { label: "Chocolate Chip Cookie Dough", value: "chocolate-chip-cookie-dough", dairyFree: false, }, { label: "Cookie Dough", value: "cookie-dough", dairyFree: false }, { label: "Cookies and Cream", value: "cookies-and-cream", dairyFree: true, }, { label: "Strawberry", value: "strawberry", dairyFree: true }, { label: "Vanilla", value: "vanilla", dairyFree: false }, ], }, ]; const [options, setOptions] = useState(OPTIONS_WITH_CATEGORIES); const [isLoading, setIsLoading] = useState(false); const renderIceCream = (option: IceCreamExtended) => option.dairyFree ? `${option.label} - Dairy Free` : `${option.label} 🐮`; const [selected, setSelected] = useState< SingleSelectedOption >(null); const [search, setSearch] = useState(""); const filteredOptions: SelectOptionsByCategory[] = useMemo(() => { if (!search) return options; return options.reduce[]>( (acc, cur) => { const filteredCatOptions = cur.options.filter( (o) => o.label.toLowerCase().indexOf(search.toLowerCase()) >= 0, ); return filteredCatOptions.length > 0 ? [...acc, { ...cur, options: filteredCatOptions }] : acc; }, [], ); }, [search, options]); return ( label="Favorite Ice Cream" optionsDisplay="categories" options={filteredOptions} selected={selected} onChange={setSelected} renderSelected={({ selectedItem }) => { if (!selectedItem) return null; return renderIceCream(selectedItem); }} inputValue={search} onInputChange={setSearch} isClearable isLoading={isLoading} onCreateOption={(newLabel) => { const hasNuts = newLabel.includes("nuts"); const hasDairy = newLabel.includes("dairy"); let trimmedLabel = newLabel; if (hasNuts) { trimmedLabel = trimmedLabel.replace("nuts", ""); } if (hasDairy) { trimmedLabel = trimmedLabel.replace("dairy", ""); } // Create the new option and format the value however you want const newOption: IceCreamExtended = { label: trimmedLabel, value: trimmedLabel.toLowerCase().replace(/\W/g, ""), dairyFree: !hasDairy, }; const newOptions = [...options]; const categoryTitle = hasNuts ? "Nuts" : "No Nuts"; const category = newOptions.find((c) => c.title === categoryTitle); if (category) { category.options = [...category.options, newOption].sort((a, b) => a.label.toLowerCase().localeCompare(b.label.toLowerCase()), ); } // set selected state locally setSelected(newOption); // emulate creating option on server setIsLoading(true); setTimeout(() => { setIsLoading(false); // set the new options setOptions(newOptions); }, 1000); }} > {filteredOptions.map((cat) => ( {cat.options.map((o) => ( {renderIceCream(o)} ))} ))} ); }; export const DemoSingleSelectVariants = () => { return Object.values(STATUS_VARIANT).map((v) => ( {}} variant={v} placeholder={v} > No options here 🤷‍♀️ )); }; export const DemoSingleSelectDisabledOption = () => { interface IceCreamWithStock extends SelectOption { inStock: boolean; } const [selected, setSelected] = useState< SingleSelectedOption >(null); const OPTIONS: IceCreamWithStock[] = [ { label: "Butter Pecan", value: "butter-pecan", inStock: true }, { label: "Chocolate", value: "chocolate", inStock: true }, { label: "Chocolate Chip Cookie Dough", value: "chocolate-chip-cookie-dough", inStock: false, }, { label: "Cookie Dough", value: "cookie-dough", inStock: true }, { label: "Cookies and Cream", value: "cookies-and-cream", inStock: true }, { label: "Mint Chocolate Chip", value: "mint-chocolate-chip", inStock: true, }, { label: "Neapolitan", value: "neapolitan", inStock: true }, { label: "Moose Tracks", value: "moose-tracks", inStock: false }, { label: "Rocky Road", value: "rocky-road", inStock: true }, { label: "Strawberry", value: "strawberry", inStock: false }, { label: "Vanilla", value: "vanilla", inStock: true }, ]; return ( {OPTIONS.map((o) => ( {`${o.label}${o.inStock ? "" : " ❌ Sold out"}`} ))} ); }; export const DemoSingleSelectDisabled = () => { return ( {}} isDisabled > {[]} ); }; export const DemoSelectInModal = () => { const { isOpen, onOpen, onClose } = useDisclosure(); const [selected, setSelected] = useState(null); const OPTIONS = [ { label: "Butter Pecan", value: "butter-pecan" }, { label: "Chocolate", value: "chocolate" }, { label: "Chocolate Chip Cookie Dough", value: "chocolate-chip-cookie-dough", }, { label: "Cookie Dough", value: "cookie-dough" }, { label: "Cookies and Cream", value: "cookies-and-cream" }, { label: "Mint Chocolate Chip", value: "mint-chocolate-chip" }, { label: "Neapolitan", value: "neapolitan" }, { label: "Moose Tracks", value: "moose-tracks" }, { label: "Rocky Road", value: "rocky-road" }, { label: "Strawberry", value: "strawberry" }, { label: "Vanilla", value: "vanilla" }, ]; return ( <> {OPTIONS.map((o) => ( ))} ); };