From 33ebac510c9a9326adb9cc168380f833f0e0ea0a Mon Sep 17 00:00:00 2001 From: Camila Sosa Morales Date: Wed, 8 Feb 2023 09:10:59 -0500 Subject: [PATCH] chore: dropdown component (#90) * wip: add dropdown component * wip: added search functionality on dropdown * chore: dropdown component with properties * style: set width pase on parent * refactor: remove old dropdown component and add headless ui * chore: remove unsued radix component dependency * chore: add yarn.lock on root fodler * refactor: remove old folders from root project * chore: add import on index * chore: apply PR review --- ui/package.json | 1 + ui/src/components/core/combobox/combobox.tsx | 160 +++++++++++++++++++ ui/src/components/core/combobox/dropdown.tsx | 91 +++++++++++ ui/src/components/core/combobox/index.ts | 2 + ui/src/components/core/icon/icon-library.tsx | 5 +- ui/src/components/core/index.ts | 2 + ui/src/theme/foundations/spacing.ts | 1 + ui/src/theme/utils.ts | 8 + ui/tailwind.config.js | 14 +- ui/yarn.lock | 12 ++ 10 files changed, 294 insertions(+), 2 deletions(-) create mode 100644 ui/src/components/core/combobox/combobox.tsx create mode 100644 ui/src/components/core/combobox/dropdown.tsx create mode 100644 ui/src/components/core/combobox/index.ts diff --git a/ui/package.json b/ui/package.json index c0bebc3..d03e969 100644 --- a/ui/package.json +++ b/ui/package.json @@ -14,6 +14,7 @@ "author": "Fleek", "dependencies": { "@ethersproject/providers": "^5.7.2", + "@headlessui/react": "^1.7.8", "@radix-ui/colors": "^0.1.8", "@react-icons/all-files": "^4.1.0", "@reduxjs/toolkit": "^1.9.1", diff --git a/ui/src/components/core/combobox/combobox.tsx b/ui/src/components/core/combobox/combobox.tsx new file mode 100644 index 0000000..8cde662 --- /dev/null +++ b/ui/src/components/core/combobox/combobox.tsx @@ -0,0 +1,160 @@ +import { Fragment, useRef, useState } from 'react'; +import { Combobox as ComboboxLib, Transition } from '@headlessui/react'; +import { Icon, IconName } from '@/components/core/icon'; +import { Flex } from '@/components/layout'; + +type ComboboxInputProps = { + open: boolean; + handleInputChange: (event: React.ChangeEvent) => void; + handleInputClick: () => void; +}; + +const ComboboxInput = ({ + open, + handleInputChange, + handleInputClick, +}: ComboboxInputProps) => ( +
+ + selectedValue.label} + onChange={handleInputChange} + onClick={handleInputClick} + /> +
+); + +type ComboboxOptionProps = { + option: ComboboxItem; +}; + +const ComboboxOption = ({ option }: ComboboxOptionProps) => ( + + `relative cursor-default select-none py-2 px-3.5 text-slate11 rounded-xl mb-2 text-sm ${ + active ? 'bg-slate5 text-slate12' : 'bg-transparent' + }` + } + > + {({ selected, active }) => ( + + + {option.icon && } + + {option.label} + + + {selected && } + + )} + +); + +const NoResults = () => ( +
+ Nothing found. +
+); + +export type ComboboxItem = { + value: string; + label: string; + icon?: IconName; +}; + +export type ComboboxProps = { + items: ComboboxItem[]; + selectedValue: ComboboxItem | undefined; + onChange(option: ComboboxItem): void; +}; + +export const Combobox: React.FC = ({ + items, + selectedValue = { value: '', label: '' }, + onChange, +}) => { + const [query, setQuery] = useState(''); + + const buttonRef = useRef(null); + + const filteredItems = + query === '' + ? items + : items.filter((person) => + person.label + .toLowerCase() + .replace(/\s+/g, '') + .includes(query.toLowerCase().replace(/\s+/g, '')) + ); + + const handleInputChange = (event: React.ChangeEvent) => { + setQuery(event.target.value); + }; + + const handleInputClick = () => { + buttonRef.current?.click(); + }; + + const handleComboboxChange = (option: ComboboxItem) => { + onChange(option); + }; + + const handleLeaveTransition = () => { + setQuery(''); + }; + + return ( + + {({ open }) => ( + <> +
+ + +
+ + + {filteredItems.length === 0 && query !== '' ? ( + + ) : ( + filteredItems.map((option) => { + return ; + }) + )} + + + + )} +
+ ); +}; diff --git a/ui/src/components/core/combobox/dropdown.tsx b/ui/src/components/core/combobox/dropdown.tsx new file mode 100644 index 0000000..78071f2 --- /dev/null +++ b/ui/src/components/core/combobox/dropdown.tsx @@ -0,0 +1,91 @@ +import { Fragment } from 'react'; +import { Listbox, Transition } from '@headlessui/react'; +import { Icon } from '@/components/core/icon'; +import { Flex } from '@/components'; + +type DropdownOptionProps = { + option: DropdownItem; +}; + +const DropdownOption = ({ option }: DropdownOptionProps) => ( + + `relative cursor-default select-none py-2 px-3.5 text-slate11 rounded-xl mb-2 text-sm ${ + active ? 'bg-slate5 text-slate12' : 'bg-transparent' + }` + } + value={option} + > + {({ selected, active }) => ( + + + {option.label} + + {selected && } + + )} + +); + +type DropdownButtonProps = { + selectedValue: DropdownItem | undefined; + open: boolean; +}; + +const DropdownButton = ({ selectedValue, open }: DropdownButtonProps) => ( + + + {selectedValue ? selectedValue.label : 'Select'} + + + + + +); + +export type DropdownItem = { + value: string; + label: string; +}; + +export type DropdownProps = { + items: DropdownItem[]; + selectedValue: DropdownItem | undefined; + onChange(option: DropdownItem): void; +}; + +export const Dropdown: React.FC = ({ + items, + selectedValue, + onChange, +}) => { + const handleDropdownChange = (option: DropdownItem) => { + onChange(option); + }; + return ( + + {({ open }) => ( +
+ + + + {items.map((option: DropdownItem) => ( + + ))} + + +
+ )} +
+ ); +}; diff --git a/ui/src/components/core/combobox/index.ts b/ui/src/components/core/combobox/index.ts new file mode 100644 index 0000000..76459f6 --- /dev/null +++ b/ui/src/components/core/combobox/index.ts @@ -0,0 +1,2 @@ +export * from './combobox'; +export * from './dropdown'; diff --git a/ui/src/components/core/icon/icon-library.tsx b/ui/src/components/core/icon/icon-library.tsx index cc9bddb..2fa3ba1 100644 --- a/ui/src/components/core/icon/icon-library.tsx +++ b/ui/src/components/core/icon/icon-library.tsx @@ -1,14 +1,17 @@ import { IoLogoGithub } from '@react-icons/all-files/io5/IoLogoGithub'; import { IoArrowBackCircleSharp } from '@react-icons/all-files/io5/IoArrowBackCircleSharp'; import { IoInformationCircleSharp } from '@react-icons/all-files/io5/IoInformationCircleSharp'; -import { IoCloudUploadSharp } from '@react-icons/all-files/io5/IoCloudUploadSharp'; import { AiOutlineCheck } from '@react-icons/all-files/ai/AiOutlineCheck'; +import { AiOutlineDown } from '@react-icons/all-files/ai/AiOutlineDown'; +import { BiSearch } from '@react-icons/all-files/bi/BiSearch'; +import { IoCloudUploadSharp } from '@react-icons/all-files/io5/IoCloudUploadSharp'; import { MetamaskIcon, EthereumIcon } from './custom'; import { BiSearch } from '@react-icons/all-files/bi/BiSearch'; export const IconLibrary = Object.freeze({ back: IoArrowBackCircleSharp, check: AiOutlineCheck, + 'chevron-down': AiOutlineDown, ethereum: EthereumIcon, github: IoLogoGithub, info: IoInformationCircleSharp, diff --git a/ui/src/components/core/index.ts b/ui/src/components/core/index.ts index 8c49368..02d5f36 100644 --- a/ui/src/components/core/index.ts +++ b/ui/src/components/core/index.ts @@ -1,2 +1,4 @@ export * from './button'; +export * from './combobox'; export * from './icon'; +export * from './input'; diff --git a/ui/src/theme/foundations/spacing.ts b/ui/src/theme/foundations/spacing.ts index e96c63c..cb592d0 100644 --- a/ui/src/theme/foundations/spacing.ts +++ b/ui/src/theme/foundations/spacing.ts @@ -32,6 +32,7 @@ export const spacing = { 52: '13rem', 56: '14rem', 60: '15rem', + 62: '15.5rem', 64: '16rem', 72: '18rem', 80: '20rem', diff --git a/ui/src/theme/utils.ts b/ui/src/theme/utils.ts index 1bd2738..25360d4 100644 --- a/ui/src/theme/utils.ts +++ b/ui/src/theme/utils.ts @@ -64,6 +64,14 @@ export const utils = { btlr: (value: Stitches.PropertyValue<'borderTopLeftRadius'>) => ({ borderTopLeftRadius: value, }), + bt: (value: Stitches.PropertyValue<'borderTopLeftRadius'>) => ({ + borderTopLeftRadius: value, + borderTopRightRadius: value, + }), + bb: (value: Stitches.PropertyValue<'borderBottomLeftRadius'>) => ({ + borderBottomRightRadius: value, + borderBottomLeftRadius: value, + }), ox: (value: Stitches.PropertyValue<'overflowX'>) => ({ overflowX: value }), oy: (value: Stitches.PropertyValue<'overflowY'>) => ({ overflowY: value }), diff --git a/ui/tailwind.config.js b/ui/tailwind.config.js index b47e431..1297dba 100644 --- a/ui/tailwind.config.js +++ b/ui/tailwind.config.js @@ -2,7 +2,19 @@ module.exports = { content: ['./src/**/*.tsx'], theme: { - extend: {}, + extend: { + colors: { + //TODO if we're gonna have ligth mode we should add also the light colors cause tailwind doesn't have them + slate5: 'rgba(43, 47, 49, 1)', + slate6: 'rgba(49, 53, 56, 1)', + slate7: 'rgba(58, 63, 66, 1)', + slate11: 'rgba(155, 161, 166, 1)', + slate12: 'rgba(236, 237, 238, 1)', + }, + width: { + inherit: 'inherit', + }, + }, }, plugins: [], }; diff --git a/ui/yarn.lock b/ui/yarn.lock index 7f22ebd..dc48409 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -1585,6 +1585,13 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== +"@headlessui/react@^1.7.8": + version "1.7.8" + resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.8.tgz#d7b4a95d50f9a386f7d1259d5ff8a6d7eb552ef0" + integrity sha512-zcwb0kd7L05hxmoAMIioEaOn235Dg0fUO+iGbLPgLVSjzl/l39V6DTpC2Df49PE5aG5/f5q0PZ9ZHZ78ENNV+A== + dependencies: + client-only "^0.0.1" + "@humanwhocodes/config-array@^0.11.8": version "0.11.8" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" @@ -4611,6 +4618,11 @@ cli-table3@^0.6.1: optionalDependencies: "@colors/colors" "1.5.0" +client-only@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" + integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"