chore: refactor style combobox (#209)

* refactor: refactor combobox based on designs

* style: combobox with search separated
This commit is contained in:
Camila Sosa Morales 2023-04-12 11:03:15 -03:00 committed by GitHub
parent 09e50adefc
commit 361855946a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 79 additions and 85 deletions

View File

@ -3,13 +3,7 @@ import {
ComboboxInputProps as ComboboxLibInputProps,
Transition,
} from '@headlessui/react';
import React, {
forwardRef,
Fragment,
useEffect,
useRef,
useState,
} from 'react';
import React, { forwardRef, Fragment, useEffect, useState } from 'react';
import { Icon, IconName } from '@/components/core/icon';
import { Flex } from '@/components/layout';
@ -18,54 +12,28 @@ import { useDebounce } from '@/hooks/use-debounce';
import { Separator } from '../separator.styles';
import { cleanString } from './combobox.utils';
type ComboboxInputProps = {
/**
* If it's true, the list of options will be displayed
*/
open: boolean;
/**
* Name of the left icon to display in the input
*/
leftIcon: IconName;
/**
* Value to indicate it's invalid
*/
error?: boolean;
} & ComboboxLibInputProps<'input', ComboboxItem>;
type ComboboxInputProps = ComboboxLibInputProps<'input', ComboboxItem>;
const ComboboxInput: React.FC<ComboboxInputProps> = ({
open,
leftIcon,
error,
...props
}: ComboboxInputProps) => (
<div className="relative w-full">
<Icon
name={leftIcon}
name="search"
size="sm"
css={{
position: 'absolute',
left: '$3',
top: '$3',
fontSize: '$xl',
color: 'slate8',
color: '$slate8',
}}
/>
<ComboboxLib.Input
placeholder="Search"
className={`w-full border-solid border h-11 py-3 px-10 text-sm leading-5 text-slate11 outline-none ${
open
? 'border-b-0 rounded-t-xl bg-black border-slate6'
: `rounded-xl bg-transparent cursor-pointer ${
error ? 'border-red9' : 'border-slate7'
}`
}`}
displayValue={(selectedValue: ComboboxItem) => selectedValue.label}
className={`w-full h-9 py-3 px-10 text-sm bg-transparent leading-5 text-slate11 outline-none `}
{...props}
/>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-4">
<Icon name="chevron-down" css={{ fontSize: '$xs' }} />
</span>
</div>
);
@ -154,6 +122,7 @@ export type ComboboxProps = {
* Value to indicate it's invalid
*/
error?: boolean;
css?: string; //tailwind css
};
export const Combobox: React.FC<ComboboxProps> = ({
@ -164,6 +133,7 @@ export const Combobox: React.FC<ComboboxProps> = ({
onChange,
onBlur,
error = false,
css,
}) => {
const [filteredItems, setFilteredItems] = useState<ComboboxItem[]>([]);
const [autocompleteItems, setAutocompleteItems] = useState<ComboboxItem[]>(
@ -186,8 +156,6 @@ export const Combobox: React.FC<ComboboxProps> = ({
setFilteredItems(items);
}, [items]);
const buttonRef = useRef<HTMLButtonElement>(null);
const handleSearch = useDebounce((searchValue: string) => {
if (searchValue === '') {
setFilteredItems(items);
@ -216,10 +184,6 @@ export const Combobox: React.FC<ComboboxProps> = ({
handleSearch(event.target.value);
};
const handleInputClick = (): void => {
buttonRef.current?.click();
};
const handleComboboxChange = (optionSelected: ComboboxItem): void => {
onChange(optionSelected);
};
@ -239,16 +203,33 @@ export const Combobox: React.FC<ComboboxProps> = ({
onChange={handleComboboxChange}
>
{({ open }) => (
<div className="relative">
<ComboboxInput
onChange={handleInputChange}
onClick={handleInputClick}
open={open}
leftIcon={leftIcon}
onBlur={onBlur}
error={error}
/>
<ComboboxLib.Button ref={buttonRef} className="hidden" />
<div className={`relative w-full ${css ? css : ''}`}>
<div className="relative w-full">
<Icon
name={leftIcon}
size="sm"
css={{
position: 'absolute',
left: '$3',
top: '$3',
fontSize: '$xl',
color: 'slate8',
}}
/>
<ComboboxLib.Button
className={`w-full text-left border-solid border rounded-xl h-11 py-3 px-10 text-sm leading-5 text-slate11 outline-none ${
error ? 'border-red9' : 'border-slate7'
}`}
onBlur={onBlur}
>
{selectedValue && selectedValue.label
? selectedValue.label
: 'Search'}
</ComboboxLib.Button>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-4">
<Icon name="chevron-down" css={{ fontSize: '$xs' }} />
</span>
</div>
<Transition
show={open}
@ -259,28 +240,35 @@ export const Combobox: React.FC<ComboboxProps> = ({
leaveTo="opacity-0"
afterLeave={handleLeaveTransition}
>
<ComboboxLib.Options className="absolute max-h-60 w-full z-10 overflow-auto rounded-b-xl border-solid border-slate6 border bg-black pt-2 px-3 text-base focus:outline-none sm:text-sm">
{[...autocompleteItems, ...filteredItems].length === 0 ||
filteredItems === undefined ? (
<NoResults />
) : (
<>
{autocompleteItems.length > 0 && <span>Create new</span>}
{autocompleteItems.map((autocompleteOption: ComboboxItem) => (
<ComboboxOption
key={autocompleteOption.value}
option={autocompleteOption}
/>
))}
{autocompleteItems.length > 0 && filteredItems.length > 0 && (
<Separator css={{ mb: '$2' }} />
)}
{filteredItems.map((option: ComboboxItem) => (
<ComboboxOption key={option.value} option={option} />
))}
</>
)}
</ComboboxLib.Options>
<div className="absolute max-h-60 mt-2 w-full z-10 overflow-auto rounded-xl border-solid border-slate6 border bg-black pt-2 px-3 text-base focus:outline-none sm:text-sm">
<ComboboxInput onChange={handleInputChange} onBlur={onBlur} />
<Separator />
<ComboboxLib.Options className="mt-1">
{[...autocompleteItems, ...filteredItems].length === 0 ||
filteredItems === undefined ? (
<NoResults />
) : (
<>
{autocompleteItems.length > 0 && <span>Create new</span>}
{autocompleteItems.map(
(autocompleteOption: ComboboxItem) => (
<ComboboxOption
key={autocompleteOption.value}
option={autocompleteOption}
/>
)
)}
{autocompleteItems.length > 0 &&
filteredItems.length > 0 && (
<Separator css={{ mb: '$2' }} />
)}
{filteredItems.map((option: ComboboxItem) => (
<ComboboxOption key={option.value} option={option} />
))}
</>
)}
</ComboboxLib.Options>
</div>
</Transition>
</div>
)}

View File

@ -10,13 +10,14 @@ export const LogoFileInput = StyledInputFile;
type InputProps = {
leftIcon?: IconName;
css?: string; //tailwind css
} & React.ComponentPropsWithRef<typeof InputStyled>;
export const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => {
const { leftIcon, ...ownProps } = props;
const { leftIcon, css, ...ownProps } = props;
return (
<div className="relative">
<div className={`relative ${css ? css : ''}`}>
{leftIcon && (
<InputIconStyled name={leftIcon} css={{ fontSize: '$lg' }} />
)}

View File

@ -1,6 +1,6 @@
import { useState } from 'react';
import { Combobox, Flex } from '@/components';
import { Combobox, ComboboxItem, Flex } from '@/components';
const itemsCombobox = [
{ label: 'Item 1', value: 'item-1' },
@ -9,15 +9,16 @@ const itemsCombobox = [
];
export const ComboboxTest: React.FC = () => {
const [selectedValue, setSelectedValue] = useState('');
const [selectedValueAutocomplete, setSelectedValueAutocomplete] =
useState('');
const [selectedValue, setSelectedValue] = useState({} as ComboboxItem);
const [selectedValueAutocomplete, setSelectedValueAutocomplete] = useState(
{} as ComboboxItem
);
const handleComboboxChange = (value: string): void => {
const handleComboboxChange = (value: ComboboxItem): void => {
setSelectedValue(value);
};
const handleComboboxChangeAutocomplete = (value: string): void => {
const handleComboboxChangeAutocomplete = (value: ComboboxItem): void => {
setSelectedValueAutocomplete(value);
};
@ -32,12 +33,14 @@ export const ComboboxTest: React.FC = () => {
}}
>
<h1>Components Test</h1>
<Flex css={{ width: '400px', gap: '$2' }}>
<Flex css={{ width: '600px', gap: '$2' }}>
<Combobox
items={itemsCombobox}
selectedValue={selectedValue}
onChange={handleComboboxChange}
leftIcon="github"
/>
<Combobox
items={itemsCombobox}
selectedValue={selectedValueAutocomplete}

View File

@ -7,9 +7,9 @@ import { ToastTest } from './toast-test';
export const ComponentsTest: React.FC = () => {
return (
<Flex css={{ flexDirection: 'column' }}>
<ComboboxTest />
<ColorPickerTest />
<ToastTest />
<ComboboxTest />
</Flex>
);
};

View File

@ -85,6 +85,7 @@ export const GithubRepositoryConnection: React.FC = () => {
leftIcon="search"
placeholder="Search repo"
onChange={handleSearchChange}
css="flex-1"
/>
</Flex>
{queryLoading === 'loading' ||

View File

@ -55,6 +55,7 @@ export const UserOrgsCombobox: React.FC = () => {
selectedValue={selectedUserOrg}
onChange={handleUserOrgChange}
leftIcon="github"
css="flex-1"
/>
);
};