|
|
@@ -1,7 +1,6 @@ |
|
|
|
import * as React from 'react'; |
|
|
|
import { TextControl, tailwind } from '@tesseract-design/web-base'; |
|
|
|
import { useFallbackId } from '@modal-sh/react-utils'; |
|
|
|
import { PluginCreator } from 'tailwindcss/types/config'; |
|
|
|
|
|
|
|
const { tw } = tailwind; |
|
|
|
|
|
|
@@ -54,11 +53,39 @@ export interface MenuSelectProps extends Omit<React.HTMLProps<MenuSelectDerivedE |
|
|
|
* Starting height of the component. |
|
|
|
*/ |
|
|
|
startingHeight?: number | string, |
|
|
|
/** |
|
|
|
* Should the component be resizable? |
|
|
|
*/ |
|
|
|
resizable?: boolean, |
|
|
|
} |
|
|
|
|
|
|
|
export const menuSelectPlugin: PluginCreator = ({ addComponents }) => { |
|
|
|
export const menuSelectPlugin: tailwind.PluginCreator = ({ addComponents }) => { |
|
|
|
addComponents({ |
|
|
|
'.menu-select': { |
|
|
|
'&[data-variant="alternate"][data-size="small"]': { |
|
|
|
'--td-padding-top': '1.25rem', |
|
|
|
}, |
|
|
|
'&[data-variant="alternate"][data-size="medium"]': { |
|
|
|
'--td-padding-top': '1.5rem', |
|
|
|
}, |
|
|
|
'&[data-variant="alternate"][data-size="large"]': { |
|
|
|
'--td-padding-top': '2rem', |
|
|
|
}, |
|
|
|
'&[data-variant="default"][data-size="small"]': { |
|
|
|
'--td-padding-top': '0.625rem', |
|
|
|
'--td-padding-bottom': '0.625rem', |
|
|
|
}, |
|
|
|
'&[data-variant="default"][data-size="medium"]': { |
|
|
|
'--td-padding-top': '0.75rem', |
|
|
|
'--td-padding-bottom': '0.75rem', |
|
|
|
}, |
|
|
|
'&[data-variant="default"][data-size="large"]': { |
|
|
|
'--td-padding-top': '1.25rem', |
|
|
|
'--td-padding-bottom': '1.25rem', |
|
|
|
}, |
|
|
|
'clip-path': 'inset(var(--td-padding-top, 0) 0 var(--td-padding-bottom, 0) 0)', |
|
|
|
'padding-top': 'var(--td-padding-top, 0)', |
|
|
|
'padding-bottom': 'var(--td-padding-bottom, 0)', |
|
|
|
'& optgroup': { |
|
|
|
'color': 'rgb(var(--color-positive) / 50%)', |
|
|
|
'text-transform': 'uppercase', |
|
|
@@ -87,13 +114,14 @@ export const MenuSelect = React.forwardRef<MenuSelectDerivedElement, MenuSelectP |
|
|
|
hint, |
|
|
|
indicator, |
|
|
|
size = 'medium' as const, |
|
|
|
border = false, |
|
|
|
block = false, |
|
|
|
border = false as const, |
|
|
|
block = false as const, |
|
|
|
variant = 'default' as const, |
|
|
|
hiddenLabel = false, |
|
|
|
hiddenLabel = false as const, |
|
|
|
className, |
|
|
|
startingHeight = '15rem', |
|
|
|
startingHeight = '15rem' as const, |
|
|
|
id: idProp, |
|
|
|
resizable = false as const, |
|
|
|
...etcProps |
|
|
|
}: MenuSelectProps, |
|
|
|
forwardedRef, |
|
|
@@ -114,138 +142,136 @@ export const MenuSelect = React.forwardRef<MenuSelectDerivedElement, MenuSelectP |
|
|
|
)} |
|
|
|
data-testid="base" |
|
|
|
> |
|
|
|
{label && ( |
|
|
|
<> |
|
|
|
<label |
|
|
|
data-testid="label" |
|
|
|
htmlFor={id} |
|
|
|
id={labelId} |
|
|
|
className={tw( |
|
|
|
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold group-focus-within:text-secondary text-primary leading-none bg-negative select-none', |
|
|
|
{ |
|
|
|
'sr-only': hiddenLabel, |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pr-1': !indicator, |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pr-10': indicator && size === 'small', |
|
|
|
'pr-12': indicator && size === 'medium', |
|
|
|
'pr-16': indicator && size === 'large', |
|
|
|
}, |
|
|
|
)} |
|
|
|
> |
|
|
|
<span className="block w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> |
|
|
|
{label} |
|
|
|
</span> |
|
|
|
</label> |
|
|
|
{' '} |
|
|
|
</> |
|
|
|
)} |
|
|
|
<MenuSelectDerivedElementComponent |
|
|
|
{...etcProps} |
|
|
|
ref={forwardedRef} |
|
|
|
id={id} |
|
|
|
aria-labelledby={labelId} |
|
|
|
data-testid="input" |
|
|
|
size={2} |
|
|
|
style={{ |
|
|
|
height: startingHeight, |
|
|
|
}} |
|
|
|
className={tw( |
|
|
|
'menu-select bg-negative rounded-inherit w-full peer block overflow-auto cursor-pointer font-inherit', |
|
|
|
'focus:outline-0', |
|
|
|
'disabled:opacity-50 disabled:cursor-not-allowed', |
|
|
|
{ |
|
|
|
'resize': !block, |
|
|
|
'resize-y': block, |
|
|
|
}, |
|
|
|
{ |
|
|
|
'text-xxs': size === 'small', |
|
|
|
'text-xs': size === 'medium', |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pl-4': variant === 'default', |
|
|
|
'pl-1.5': variant === 'alternate', |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pt-4': variant === 'alternate' && size === 'small', |
|
|
|
'pt-5': variant === 'alternate' && size === 'medium', |
|
|
|
'pt-8': variant === 'alternate' && size === 'large', |
|
|
|
}, |
|
|
|
{ |
|
|
|
'py-2.5': variant === 'default' && size === 'small', |
|
|
|
'py-3': variant === 'default' && size === 'medium', |
|
|
|
'py-5': variant === 'default' && size === 'large', |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pr-4': variant === 'default' && !indicator, |
|
|
|
'pr-1.5': variant === 'alternate' && !indicator, |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pr-10': indicator && size === 'small', |
|
|
|
'pr-12': indicator && size === 'medium', |
|
|
|
'pr-16': indicator && size === 'large', |
|
|
|
}, |
|
|
|
{ |
|
|
|
'min-h-10': size === 'small', |
|
|
|
'min-h-12': size === 'medium', |
|
|
|
'min-h-16': size === 'large', |
|
|
|
}, |
|
|
|
)} |
|
|
|
/> |
|
|
|
{hint && ( |
|
|
|
<div |
|
|
|
data-testid="hint" |
|
|
|
className={tw( |
|
|
|
'absolute left-0 px-1 pointer-events-none text-xxs leading-none w-full bg-negative select-none', |
|
|
|
{ |
|
|
|
'bottom-0 pl-4 pb-1': variant === 'default', |
|
|
|
'top-0.5': variant === 'alternate', |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pt-2': variant === 'alternate' && size === 'small', |
|
|
|
'pt-3': variant === 'alternate' && size !== 'small', |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pr-4': !indicator && variant === 'default', |
|
|
|
'pr-1': !indicator && variant === 'alternate', |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pr-10': indicator && size === 'small', |
|
|
|
'pr-12': indicator && size === 'medium', |
|
|
|
'pr-16': indicator && size === 'large', |
|
|
|
}, |
|
|
|
)} |
|
|
|
> |
|
|
|
<div |
|
|
|
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis" |
|
|
|
> |
|
|
|
{hint} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
)} |
|
|
|
{indicator && ( |
|
|
|
<div |
|
|
|
data-testid="indicator" |
|
|
|
className={tw( |
|
|
|
'text-center flex items-center justify-center aspect-square absolute bottom-0 right-0 pointer-events-none select-none', |
|
|
|
{ |
|
|
|
'w-10': size === 'small', |
|
|
|
'w-12': size === 'medium', |
|
|
|
'w-16': size === 'large', |
|
|
|
}, |
|
|
|
)} |
|
|
|
> |
|
|
|
{indicator} |
|
|
|
</div> |
|
|
|
)} |
|
|
|
{border && ( |
|
|
|
<span |
|
|
|
data-testid="border" |
|
|
|
className="absolute z-[1] inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" |
|
|
|
/> |
|
|
|
)} |
|
|
|
<div |
|
|
|
className={tw( |
|
|
|
'w-full overflow-hidden min-h-16 relativemin-h-16 rounded-inherit', |
|
|
|
{ |
|
|
|
'resize': !block && resizable, |
|
|
|
'resize-y': block && resizable, |
|
|
|
}, |
|
|
|
{ |
|
|
|
'min-h-10': size === 'small', |
|
|
|
'min-h-12': size === 'medium', |
|
|
|
'min-h-16': size === 'large', |
|
|
|
}, |
|
|
|
)} |
|
|
|
style={{ |
|
|
|
height: startingHeight, |
|
|
|
}} |
|
|
|
> |
|
|
|
{label && ( |
|
|
|
<> |
|
|
|
<label |
|
|
|
data-testid="label" |
|
|
|
htmlFor={id} |
|
|
|
id={labelId} |
|
|
|
className={tw( |
|
|
|
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold group-focus-within:text-secondary text-primary leading-none select-none', |
|
|
|
{ |
|
|
|
'sr-only': hiddenLabel, |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pr-1': !indicator, |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pr-10': indicator && size === 'small', |
|
|
|
'pr-12': indicator && size === 'medium', |
|
|
|
'pr-16': indicator && size === 'large', |
|
|
|
}, |
|
|
|
)} |
|
|
|
> |
|
|
|
<span className="block w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> |
|
|
|
{label} |
|
|
|
</span> |
|
|
|
</label> |
|
|
|
{' '} |
|
|
|
</> |
|
|
|
)} |
|
|
|
<MenuSelectDerivedElementComponent |
|
|
|
{...etcProps} |
|
|
|
ref={forwardedRef} |
|
|
|
id={id} |
|
|
|
aria-labelledby={labelId} |
|
|
|
data-testid="input" |
|
|
|
size={2} |
|
|
|
data-variant={variant} |
|
|
|
data-size={size} |
|
|
|
className={tw( |
|
|
|
'menu-select bg-transparent rounded-inherit w-full h-full peer block overflow-auto cursor-pointer font-inherit', |
|
|
|
'focus:outline-0', |
|
|
|
'disabled:opacity-50 disabled:cursor-not-allowed', |
|
|
|
{ |
|
|
|
'text-xxs': size === 'small', |
|
|
|
'text-xs': size === 'medium', |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pl-4': variant === 'default', |
|
|
|
'pl-1.5': variant === 'alternate', |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pr-4': variant === 'default' && !indicator, |
|
|
|
'pr-1.5': variant === 'alternate' && !indicator, |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pr-10': indicator && size === 'small', |
|
|
|
'pr-12': indicator && size === 'medium', |
|
|
|
'pr-16': indicator && size === 'large', |
|
|
|
}, |
|
|
|
)} |
|
|
|
/> |
|
|
|
{hint && ( |
|
|
|
<div |
|
|
|
data-testid="hint" |
|
|
|
className={tw( |
|
|
|
'absolute left-0 px-1 pointer-events-none text-xxs leading-none w-full select-none', |
|
|
|
{ |
|
|
|
'bottom-0 pl-4 pb-1': variant === 'default', |
|
|
|
'top-0.5': variant === 'alternate', |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pt-2': variant === 'alternate' && size === 'small', |
|
|
|
'pt-3': variant === 'alternate' && size !== 'small', |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pr-4': !indicator && variant === 'default', |
|
|
|
'pr-1': !indicator && variant === 'alternate', |
|
|
|
}, |
|
|
|
{ |
|
|
|
'pr-10': indicator && size === 'small', |
|
|
|
'pr-12': indicator && size === 'medium', |
|
|
|
'pr-16': indicator && size === 'large', |
|
|
|
}, |
|
|
|
)} |
|
|
|
> |
|
|
|
<div |
|
|
|
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis" |
|
|
|
> |
|
|
|
{hint} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
)} |
|
|
|
{indicator && ( |
|
|
|
<div |
|
|
|
data-testid="indicator" |
|
|
|
className={tw( |
|
|
|
'text-center flex items-center justify-center aspect-square absolute bottom-0 right-0 pointer-events-none select-none', |
|
|
|
{ |
|
|
|
'w-10': size === 'small', |
|
|
|
'w-12': size === 'medium', |
|
|
|
'w-16': size === 'large', |
|
|
|
}, |
|
|
|
)} |
|
|
|
> |
|
|
|
{indicator} |
|
|
|
</div> |
|
|
|
)} |
|
|
|
{border && ( |
|
|
|
<span |
|
|
|
data-testid="border" |
|
|
|
className="absolute z-[1] inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" |
|
|
|
/> |
|
|
|
)} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
); |
|
|
|
}); |
|
|
@@ -262,4 +288,5 @@ MenuSelect.defaultProps = { |
|
|
|
variant: 'default' as const, |
|
|
|
hiddenLabel: false as const, |
|
|
|
startingHeight: '15rem' as const, |
|
|
|
resizable: false as const, |
|
|
|
}; |