⚡️ 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