feat(ui): fill capability gaps in tooltip, dialog, and select primitives

Tooltip: add enter/exit opacity animation with data-[instant] support
Dialog: add overflow-y-auto for long content, optional closable button
Select: add clearable/loading props to trigger, disable alignItemWithTrigger
This commit is contained in:
yyh
2026-03-03 15:49:29 +08:00
parent 8234bced70
commit 4c8877044c
3 changed files with 50 additions and 5 deletions

View File

@ -21,12 +21,14 @@ type DialogContentProps = {
children: React.ReactNode
className?: string
overlayClassName?: string
closable?: boolean
}
export function DialogContent({
children,
className,
overlayClassName,
closable = false,
}: DialogContentProps) {
return (
<BaseDialog.Portal>
@ -39,11 +41,16 @@ export function DialogContent({
/>
<BaseDialog.Popup
className={cn(
'fixed left-1/2 top-1/2 z-50 max-h-[80dvh] w-[480px] max-w-[calc(100vw-2rem)] -translate-x-1/2 -translate-y-1/2 rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg p-6 shadow-xl',
'fixed left-1/2 top-1/2 z-50 max-h-[80dvh] w-[480px] max-w-[calc(100vw-2rem)] -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg p-6 shadow-xl',
'transition-[transform,scale,opacity] duration-150 data-[ending-style]:scale-95 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0 motion-reduce:transition-none',
className,
)}
>
{closable && (
<BaseDialog.Close aria-label="Close" className="absolute right-6 top-6 z-10 flex h-5 w-5 cursor-pointer items-center justify-center rounded-2xl hover:bg-state-base-hover">
<span className="i-ri-close-line h-4 w-4 text-text-tertiary" />
</BaseDialog.Close>
)}
{children}
</BaseDialog.Popup>
</BaseDialog.Portal>

View File

@ -12,11 +12,22 @@ export const SelectGroup = BaseSelect.Group
export const SelectGroupLabel = BaseSelect.GroupLabel
export const SelectSeparator = BaseSelect.Separator
type SelectTriggerProps = React.ComponentPropsWithoutRef<typeof BaseSelect.Trigger> & {
clearable?: boolean
onClear?: () => void
loading?: boolean
}
export function SelectTrigger({
className,
children,
clearable = false,
onClear,
loading = false,
...props
}: React.ComponentPropsWithoutRef<typeof BaseSelect.Trigger>) {
}: SelectTriggerProps) {
const showClear = clearable && !loading
return (
<BaseSelect.Trigger
className={cn(
@ -27,9 +38,34 @@ export function SelectTrigger({
{...props}
>
<span className="grow truncate">{children}</span>
<BaseSelect.Icon className="ml-1 shrink-0 text-text-quaternary transition-colors group-hover:text-text-secondary data-[open]:text-text-secondary">
<span className="i-ri-arrow-down-s-line h-4 w-4" />
</BaseSelect.Icon>
{loading
? (
<span className="ml-1 shrink-0 text-text-quaternary">
<span className="i-ri-loader-4-line h-3.5 w-3.5 animate-spin" />
</span>
)
: showClear
? (
<span
role="button"
tabIndex={-1}
className="ml-1 shrink-0 cursor-pointer text-text-quaternary hover:text-text-secondary"
onClick={(e) => {
e.stopPropagation()
onClear?.()
}}
onMouseDown={(e) => {
e.stopPropagation()
}}
>
<span className="i-ri-close-circle-fill h-3.5 w-3.5" />
</span>
)
: (
<BaseSelect.Icon className="ml-1 shrink-0 text-text-quaternary transition-colors group-hover:text-text-secondary data-[open]:text-text-secondary">
<span className="i-ri-arrow-down-s-line h-4 w-4" />
</BaseSelect.Icon>
)}
</BaseSelect.Trigger>
)
}
@ -77,6 +113,7 @@ export function SelectContent({
align={align}
sideOffset={sideOffset}
alignOffset={alignOffset}
alignItemWithTrigger={false}
className={cn('z-50 outline-none', className)}
{...positionerProps}
>

View File

@ -42,6 +42,7 @@ export function TooltipContent({
<BaseTooltip.Popup
className={cn(
variant === 'default' && 'max-w-[300px] break-words rounded-md bg-components-panel-bg px-3 py-2 text-left text-text-tertiary shadow-lg system-xs-regular',
'origin-[var(--transform-origin)] transition-[opacity] data-[ending-style]:opacity-0 data-[starting-style]:opacity-0 data-[instant]:transition-none motion-reduce:transition-none',
popupClassName,
)}
{...props}