⚡️ feat: separate select item to be a component
This commit is contained in:
parent
c950bd8d93
commit
3bca06b590
@ -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>;
|
@ -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>
|
||||
);
|
||||
};
|
@ -0,0 +1 @@
|
||||
export * from './SelectItem';
|
Loading…
Reference in New Issue
Block a user