️ feat: separate select item to be a component

This commit is contained in:
Wahyu Kurniawan 2024-02-24 11:38:16 +07:00
parent c950bd8d93
commit 3bca06b590
No known key found for this signature in database
GPG Key ID: 040A1549143A8E33
3 changed files with 143 additions and 0 deletions

View File

@ -0,0 +1,56 @@
import { tv, VariantProps } from 'tailwind-variants';
export const selectItemTheme = tv({
slots: {
wrapper: [
'p-2',
'gap-3',
'flex',
'items-start',
'justify-between',
'rounded-lg',
'group',
'data-[disabled]:cursor-not-allowed',
],
icon: ['h-4.5', 'w-4.5', 'text-elements-high-em'],
content: ['flex', 'flex-1', 'whitespace-nowrap'],
label: [
'text-sm',
'text-elements-high-em',
'tracking-[-0.006em]',
'data-[disabled]:text-elements-disabled',
],
description: [
'text-xs',
'text-elements-low-em',
'data-[disabled]:text-elements-disabled',
],
dot: ['h-1', 'w-1', 'rounded-full', 'bg-border-interactive-hovered/[0.14]'],
},
variants: {
orientation: {
horizontal: {
wrapper: ['items-center'],
content: ['flex-row', 'items-center', 'gap-2'],
},
vertical: {
content: ['flex-col', 'gap-0.5'],
},
},
variant: {
default: {
wrapper: [],
},
danger: {
wrapper: [],
},
},
active: {
true: {
wrapper: ['bg-base-bg-emphasized', 'data-[disabled]:bg-transparent'],
},
},
},
});
export type SelectItemTheme = VariantProps<typeof selectItemTheme>;

View File

@ -0,0 +1,86 @@
import React, { ComponentPropsWithoutRef, useMemo } from 'react';
import { Overwrite, UseComboboxGetItemPropsReturnValue } from 'downshift';
import { SelectOption, SelectOrientation } from 'components/shared/Select';
import { selectItemTheme, SelectItemTheme } from './SelectItem.theme';
import { cloneIcon } from 'utils/cloneIcon';
import { cn } from 'utils/classnames';
import { CheckRadioIcon } from 'components/shared/CustomIcon';
import { OmitCommon } from 'types/common';
/**
* Represents a type that merges ComponentPropsWithoutRef<'li'> with certain exclusions.
* @type {MergedComponentPropsWithoutRef}
*/
type MergedComponentPropsWithoutRef = OmitCommon<
ComponentPropsWithoutRef<'li'>,
Omit<
Overwrite<UseComboboxGetItemPropsReturnValue, SelectOption[]>,
'index' | 'item'
>
>;
export interface SelectItemProps
extends MergedComponentPropsWithoutRef,
SelectItemTheme {
selected: boolean;
option: SelectOption;
orientation?: SelectOrientation;
hovered?: boolean;
empty?: boolean;
}
export const SelectItem = ({
className,
selected,
option,
orientation,
hovered,
empty,
variant,
...props
}: SelectItemProps) => {
const theme = selectItemTheme({ active: hovered, orientation, variant });
const { label, description, leftIcon, rightIcon, disabled } = option;
const renderRightIcon = useMemo(() => {
if (rightIcon) {
return cloneIcon(rightIcon, { className: theme.icon() });
} else if (selected) {
return (
<CheckRadioIcon className={cn(theme.icon(), 'text-controls-primary')} />
);
}
return null;
}, [rightIcon]);
if (empty) {
return (
<li {...props} className={theme.wrapper()}>
No results found
</li>
);
}
return (
<li
{...props}
className={theme.wrapper({ className })}
data-disabled={disabled}
>
{leftIcon && cloneIcon(leftIcon, { className: theme.icon() })}
<div className={theme.content()}>
<p className={theme.label()} data-disabled={disabled}>
{label}
</p>
{orientation === 'horizontal' && <span className={theme.dot()} />}
{description && (
<p className={theme.description()} data-disabled={disabled}>
{description}
</p>
)}
</div>
{renderRightIcon}
</li>
);
};

View File

@ -0,0 +1 @@
export * from './SelectItem';