refactor(dify-ui): clarify overlay style semantics (#39016)

This commit is contained in:
yyh 2026-07-15 17:53:59 +08:00 committed by GitHub
parent 1121a17c11
commit dfabe4cb28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 107 additions and 97 deletions

View File

@ -5,6 +5,7 @@ import type { ButtonProps } from '../button'
import { AlertDialog as BaseAlertDialog } from '@base-ui/react/alert-dialog'
import { Button } from '../button'
import { cn } from '../cn'
import { modalBackdropClassName, modalPopupAnimationClassName } from '../overlay-shared'
export const AlertDialog = BaseAlertDialog.Root
export const AlertDialogTrigger = BaseAlertDialog.Trigger
@ -28,16 +29,12 @@ export function AlertDialogContent({
<BaseAlertDialog.Portal>
<BaseAlertDialog.Backdrop
{...backdropProps}
className={cn(
'absolute inset-0 z-50 bg-background-overlay',
'transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 motion-reduce:transition-none',
backdropClassName,
)}
className={cn(modalBackdropClassName, backdropClassName)}
/>
<BaseAlertDialog.Popup
className={cn(
'fixed top-1/2 left-1/2 z-50 max-h-[calc(100vh-2rem)] w-120 max-w-[calc(100vw-2rem)] -translate-x-1/2 -translate-y-1/2 overflow-y-auto overscroll-contain rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg',
'transition-[transform,scale,opacity] duration-150 data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none',
modalPopupAnimationClassName,
className,
)}
>

View File

@ -8,10 +8,10 @@ import { cva } from 'class-variance-authority'
import { cn } from '../cn'
import { textControlCompoundFocusClassName } from '../form-control-shared'
import {
overlayIndicatorClassName,
overlayLabelClassName,
overlayPopupAnimationClassName,
overlaySeparatorClassName,
floatingGroupLabelClassName,
floatingItemIndicatorClassName,
floatingPopupAnimationClassName,
floatingSeparatorClassName,
} from '../overlay-shared'
import { parsePlacement } from '../placement'
@ -276,7 +276,11 @@ export function AutocompleteContent({
{...positionerProps}
>
<BaseAutocomplete.Popup
className={cn(autocompletePopupClassName, overlayPopupAnimationClassName, popupClassName)}
className={cn(
autocompletePopupClassName,
floatingPopupAnimationClassName,
popupClassName,
)}
{...popupProps}
>
{children}
@ -303,12 +307,17 @@ export function AutocompleteItemText({ className, ...props }: AutocompleteItemTe
}
export function AutocompleteGroupLabel({ className, ...props }: BaseAutocomplete.GroupLabel.Props) {
return <BaseAutocomplete.GroupLabel className={cn(overlayLabelClassName, className)} {...props} />
return (
<BaseAutocomplete.GroupLabel
className={cn(floatingGroupLabelClassName, className)}
{...props}
/>
)
}
export function AutocompleteSeparator({ className, ...props }: BaseAutocomplete.Separator.Props) {
return (
<BaseAutocomplete.Separator className={cn(overlaySeparatorClassName, className)} {...props} />
<BaseAutocomplete.Separator className={cn(floatingSeparatorClassName, className)} {...props} />
)
}
@ -339,7 +348,7 @@ export function AutocompleteItemIndicator({
...props
}: React.ComponentProps<'span'>) {
return (
<span className={cn(overlayIndicatorClassName, className)} {...props}>
<span className={cn(floatingItemIndicatorClassName, className)} {...props}>
{children ?? <span className="i-ri-arrow-right-line size-4" aria-hidden="true" />}
</span>
)

View File

@ -8,10 +8,10 @@ import { cva } from 'class-variance-authority'
import { cn } from '../cn'
import { formLabelClassName, textControlCompoundFocusClassName } from '../form-control-shared'
import {
overlayIndicatorClassName,
overlayLabelClassName,
overlayPopupAnimationClassName,
overlaySeparatorClassName,
floatingGroupLabelClassName,
floatingItemIndicatorClassName,
floatingPopupAnimationClassName,
floatingSeparatorClassName,
} from '../overlay-shared'
import { parsePlacement } from '../placement'
@ -320,7 +320,7 @@ export function ComboboxContent({
{...positionerProps}
>
<BaseCombobox.Popup
className={cn(comboboxPopupClassName, overlayPopupAnimationClassName, popupClassName)}
className={cn(comboboxPopupClassName, floatingPopupAnimationClassName, popupClassName)}
{...popupProps}
>
{children}
@ -352,7 +352,10 @@ export function ComboboxItemIndicator({
...props
}: Omit<BaseCombobox.ItemIndicator.Props, 'children'> & { children?: React.ReactNode }) {
return (
<BaseCombobox.ItemIndicator className={cn(overlayIndicatorClassName, className)} {...props}>
<BaseCombobox.ItemIndicator
className={cn(floatingItemIndicatorClassName, className)}
{...props}
>
{children ?? <span className="i-ri-check-line h-4 w-4" aria-hidden="true" />}
</BaseCombobox.ItemIndicator>
)
@ -363,11 +366,13 @@ export function ComboboxLabel({ className, ...props }: BaseCombobox.Label.Props)
}
export function ComboboxGroupLabel({ className, ...props }: BaseCombobox.GroupLabel.Props) {
return <BaseCombobox.GroupLabel className={cn(overlayLabelClassName, className)} {...props} />
return (
<BaseCombobox.GroupLabel className={cn(floatingGroupLabelClassName, className)} {...props} />
)
}
export function ComboboxSeparator({ className, ...props }: BaseCombobox.Separator.Props) {
return <BaseCombobox.Separator className={cn(overlaySeparatorClassName, className)} {...props} />
return <BaseCombobox.Separator className={cn(floatingSeparatorClassName, className)} {...props} />
}
export function ComboboxEmpty({ className, ...props }: BaseCombobox.Empty.Props) {

View File

@ -1,18 +1,18 @@
'use client'
import type * as React from 'react'
import type { OverlayItemVariant } from '../overlay-shared'
import type { MenuItemVariant } from '../overlay-shared'
import type { Placement } from '../placement'
import { ContextMenu as BaseContextMenu } from '@base-ui/react/context-menu'
import { cn } from '../cn'
import {
overlayDestructiveClassName,
overlayIndicatorClassName,
overlayLabelClassName,
overlayPopupAnimationClassName,
overlayPopupBaseClassName,
overlayRowClassName,
overlaySeparatorClassName,
floatingGroupLabelClassName,
floatingItemIndicatorClassName,
floatingPopupAnimationClassName,
floatingSeparatorClassName,
menuItemClassName,
menuItemDestructiveClassName,
menuPopupClassName,
} from '../overlay-shared'
import { parsePlacement } from '../placement'
@ -73,7 +73,7 @@ function renderContextMenuPopup({
{...positionerProps}
>
<BaseContextMenu.Popup
className={cn(overlayPopupBaseClassName, overlayPopupAnimationClassName, popupClassName)}
className={cn(menuPopupClassName, floatingPopupAnimationClassName, popupClassName)}
{...popupProps}
>
{children}
@ -106,7 +106,7 @@ export function ContextMenuContent({
}
type ContextMenuItemProps = BaseContextMenu.Item.Props & {
variant?: OverlayItemVariant
variant?: MenuItemVariant
}
export function ContextMenuItem({
@ -117,14 +117,14 @@ export function ContextMenuItem({
return (
<BaseContextMenu.Item
data-variant={variant}
className={cn(overlayRowClassName, overlayDestructiveClassName, className)}
className={cn(menuItemClassName, menuItemDestructiveClassName, className)}
{...props}
/>
)
}
type ContextMenuLinkItemProps = BaseContextMenu.LinkItem.Props & {
variant?: OverlayItemVariant
variant?: MenuItemVariant
}
export function ContextMenuLinkItem({
@ -136,7 +136,7 @@ export function ContextMenuLinkItem({
return (
<BaseContextMenu.LinkItem
data-variant={variant}
className={cn(overlayRowClassName, overlayDestructiveClassName, className)}
className={cn(menuItemClassName, menuItemDestructiveClassName, className)}
closeOnClick={closeOnClick}
{...props}
/>
@ -144,14 +144,14 @@ export function ContextMenuLinkItem({
}
export function ContextMenuRadioItem({ className, ...props }: BaseContextMenu.RadioItem.Props) {
return <BaseContextMenu.RadioItem className={cn(overlayRowClassName, className)} {...props} />
return <BaseContextMenu.RadioItem className={cn(menuItemClassName, className)} {...props} />
}
export function ContextMenuCheckboxItem({
className,
...props
}: BaseContextMenu.CheckboxItem.Props) {
return <BaseContextMenu.CheckboxItem className={cn(overlayRowClassName, className)} {...props} />
return <BaseContextMenu.CheckboxItem className={cn(menuItemClassName, className)} {...props} />
}
export function ContextMenuCheckboxItemIndicator({
@ -160,7 +160,7 @@ export function ContextMenuCheckboxItemIndicator({
}: Omit<BaseContextMenu.CheckboxItemIndicator.Props, 'children'>) {
return (
<BaseContextMenu.CheckboxItemIndicator
className={cn(overlayIndicatorClassName, className)}
className={cn(floatingItemIndicatorClassName, className)}
{...props}
>
<span aria-hidden className="i-ri-check-line h-4 w-4" />
@ -174,7 +174,7 @@ export function ContextMenuRadioItemIndicator({
}: Omit<BaseContextMenu.RadioItemIndicator.Props, 'children'>) {
return (
<BaseContextMenu.RadioItemIndicator
className={cn(overlayIndicatorClassName, className)}
className={cn(floatingItemIndicatorClassName, className)}
{...props}
>
<span aria-hidden className="i-ri-check-line h-4 w-4" />
@ -183,7 +183,7 @@ export function ContextMenuRadioItemIndicator({
}
type ContextMenuSubTriggerProps = BaseContextMenu.SubmenuTrigger.Props & {
variant?: OverlayItemVariant
variant?: MenuItemVariant
}
export function ContextMenuSubTrigger({
@ -195,7 +195,7 @@ export function ContextMenuSubTrigger({
return (
<BaseContextMenu.SubmenuTrigger
data-variant={variant}
className={cn(overlayRowClassName, overlayDestructiveClassName, className)}
className={cn(menuItemClassName, menuItemDestructiveClassName, className)}
{...props}
>
{children}
@ -241,11 +241,13 @@ export function ContextMenuSubContent({
}
export function ContextMenuLabel({ className, ...props }: BaseContextMenu.GroupLabel.Props) {
return <BaseContextMenu.GroupLabel className={cn(overlayLabelClassName, className)} {...props} />
return (
<BaseContextMenu.GroupLabel className={cn(floatingGroupLabelClassName, className)} {...props} />
)
}
export function ContextMenuSeparator({ className, ...props }: BaseContextMenu.Separator.Props) {
return (
<BaseContextMenu.Separator className={cn(overlaySeparatorClassName, className)} {...props} />
<BaseContextMenu.Separator className={cn(floatingSeparatorClassName, className)} {...props} />
)
}

View File

@ -3,6 +3,7 @@
import type * as React from 'react'
import { Dialog as BaseDialog } from '@base-ui/react/dialog'
import { cn } from '../cn'
import { modalBackdropClassName, modalPopupAnimationClassName } from '../overlay-shared'
export const Dialog = BaseDialog.Root
export const DialogTrigger = BaseDialog.Trigger
@ -16,16 +17,7 @@ type DialogBackdropProps = Omit<BaseDialog.Backdrop.Props, 'className'> & {
}
export function DialogBackdrop({ className, ...props }: DialogBackdropProps) {
return (
<BaseDialog.Backdrop
{...props}
className={cn(
'absolute inset-0 z-50 bg-background-overlay',
'transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 motion-reduce:transition-none',
className,
)}
/>
)
return <BaseDialog.Backdrop {...props} className={cn(modalBackdropClassName, className)} />
}
type DialogViewportProps = Omit<BaseDialog.Viewport.Props, 'className'> & {
@ -45,7 +37,7 @@ export function DialogPopup({ className, ...props }: DialogPopupProps) {
<BaseDialog.Popup
className={cn(
'z-50 rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl',
'transition-[transform,scale,opacity] duration-150 data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none',
modalPopupAnimationClassName,
className,
)}
{...props}

View File

@ -1,18 +1,18 @@
'use client'
import type * as React from 'react'
import type { OverlayItemVariant } from '../overlay-shared'
import type { MenuItemVariant } from '../overlay-shared'
import type { Placement } from '../placement'
import { Menu } from '@base-ui/react/menu'
import { cn } from '../cn'
import {
overlayDestructiveClassName,
overlayIndicatorClassName,
overlayLabelClassName,
overlayPopupAnimationClassName,
overlayPopupBaseClassName,
overlayRowClassName,
overlaySeparatorClassName,
floatingGroupLabelClassName,
floatingItemIndicatorClassName,
floatingPopupAnimationClassName,
floatingSeparatorClassName,
menuItemClassName,
menuItemDestructiveClassName,
menuPopupClassName,
} from '../overlay-shared'
import { parsePlacement } from '../placement'
@ -25,7 +25,7 @@ export const DropdownMenuGroup = Menu.Group
export const DropdownMenuRadioGroup = Menu.RadioGroup
export function DropdownMenuRadioItem({ className, ...props }: Menu.RadioItem.Props) {
return <Menu.RadioItem className={cn(overlayRowClassName, className)} {...props} />
return <Menu.RadioItem className={cn(menuItemClassName, className)} {...props} />
}
export function DropdownMenuRadioItemIndicator({
@ -33,14 +33,14 @@ export function DropdownMenuRadioItemIndicator({
...props
}: Omit<Menu.RadioItemIndicator.Props, 'children'>) {
return (
<Menu.RadioItemIndicator className={cn(overlayIndicatorClassName, className)} {...props}>
<Menu.RadioItemIndicator className={cn(floatingItemIndicatorClassName, className)} {...props}>
<span aria-hidden className="i-ri-check-line h-4 w-4" />
</Menu.RadioItemIndicator>
)
}
export function DropdownMenuCheckboxItem({ className, ...props }: Menu.CheckboxItem.Props) {
return <Menu.CheckboxItem className={cn(overlayRowClassName, className)} {...props} />
return <Menu.CheckboxItem className={cn(menuItemClassName, className)} {...props} />
}
export function DropdownMenuCheckboxItemIndicator({
@ -48,14 +48,17 @@ export function DropdownMenuCheckboxItemIndicator({
...props
}: Omit<Menu.CheckboxItemIndicator.Props, 'children'>) {
return (
<Menu.CheckboxItemIndicator className={cn(overlayIndicatorClassName, className)} {...props}>
<Menu.CheckboxItemIndicator
className={cn(floatingItemIndicatorClassName, className)}
{...props}
>
<span aria-hidden className="i-ri-check-line h-4 w-4" />
</Menu.CheckboxItemIndicator>
)
}
export function DropdownMenuLabel({ className, ...props }: Menu.GroupLabel.Props) {
return <Menu.GroupLabel className={cn(overlayLabelClassName, className)} {...props} />
return <Menu.GroupLabel className={cn(floatingGroupLabelClassName, className)} {...props} />
}
type DropdownMenuContentProps = {
@ -105,7 +108,7 @@ function renderDropdownMenuPopup({
{...positionerProps}
>
<Menu.Popup
className={cn(overlayPopupBaseClassName, overlayPopupAnimationClassName, popupClassName)}
className={cn(menuPopupClassName, floatingPopupAnimationClassName, popupClassName)}
{...popupProps}
>
{children}
@ -138,7 +141,7 @@ export function DropdownMenuContent({
}
type DropdownMenuSubTriggerProps = Menu.SubmenuTrigger.Props & {
variant?: OverlayItemVariant
variant?: MenuItemVariant
}
export function DropdownMenuSubTrigger({
@ -150,7 +153,7 @@ export function DropdownMenuSubTrigger({
return (
<Menu.SubmenuTrigger
data-variant={variant}
className={cn(overlayRowClassName, overlayDestructiveClassName, className)}
className={cn(menuItemClassName, menuItemDestructiveClassName, className)}
{...props}
>
{children}
@ -196,7 +199,7 @@ export function DropdownMenuSubContent({
}
type DropdownMenuItemProps = Menu.Item.Props & {
variant?: OverlayItemVariant
variant?: MenuItemVariant
}
export function DropdownMenuItem({
@ -207,14 +210,14 @@ export function DropdownMenuItem({
return (
<Menu.Item
data-variant={variant}
className={cn(overlayRowClassName, overlayDestructiveClassName, className)}
className={cn(menuItemClassName, menuItemDestructiveClassName, className)}
{...props}
/>
)
}
type DropdownMenuLinkItemProps = Menu.LinkItem.Props & {
variant?: OverlayItemVariant
variant?: MenuItemVariant
}
export function DropdownMenuLinkItem({
@ -226,7 +229,7 @@ export function DropdownMenuLinkItem({
return (
<Menu.LinkItem
data-variant={variant}
className={cn(overlayRowClassName, overlayDestructiveClassName, className)}
className={cn(menuItemClassName, menuItemDestructiveClassName, className)}
closeOnClick={closeOnClick}
{...props}
/>
@ -234,5 +237,5 @@ export function DropdownMenuLinkItem({
}
export function DropdownMenuSeparator({ className, ...props }: Menu.Separator.Props) {
return <Menu.Separator className={cn(overlaySeparatorClassName, className)} {...props} />
return <Menu.Separator className={cn(floatingSeparatorClassName, className)} {...props} />
}

View File

@ -1,16 +1,18 @@
export type OverlayItemVariant = 'default' | 'destructive'
export type MenuItemVariant = 'default' | 'destructive'
export const overlayRowClassName =
export const menuItemClassName =
'mx-1 flex h-8 cursor-pointer select-none items-center gap-1 rounded-lg px-2 outline-hidden data-highlighted:bg-state-base-hover data-disabled:cursor-not-allowed data-disabled:opacity-30'
export const overlayDestructiveClassName =
export const menuItemDestructiveClassName =
'data-[variant=destructive]:text-text-destructive data-[variant=destructive]:data-highlighted:bg-state-destructive-hover'
export const overlayIndicatorClassName = 'ms-auto flex shrink-0 items-center text-text-accent'
export const overlayLabelClassName =
export const floatingItemIndicatorClassName = 'ms-auto flex shrink-0 items-center text-text-accent'
export const floatingGroupLabelClassName =
'px-3 pb-0.5 pt-1 text-text-tertiary system-xs-medium-uppercase'
export const overlaySeparatorClassName = 'my-1 h-px bg-divider-subtle'
export const overlayPopupBaseClassName =
export const floatingSeparatorClassName = 'my-1 h-px bg-divider-subtle'
export const menuPopupClassName =
'max-h-(--available-height) overflow-y-auto overflow-x-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur py-1 text-sm text-text-secondary shadow-lg outline-hidden focus:outline-hidden focus-visible:outline-hidden backdrop-blur-[5px]'
export const overlayPopupAnimationClassName =
export const floatingPopupAnimationClassName =
'origin-(--transform-origin) transition-[transform,scale,opacity] data-ending-style:scale-95 data-starting-style:scale-95 data-ending-style:opacity-0 data-starting-style:opacity-0 motion-reduce:transition-none'
export const overlayBackdropClassName =
'fixed inset-0 z-50 bg-transparent transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 motion-reduce:transition-none'
export const modalBackdropClassName =
'absolute inset-0 z-50 bg-background-overlay transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 motion-reduce:transition-none'
export const modalPopupAnimationClassName =
'transition-[transform,scale,opacity] duration-150 data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none'

View File

@ -4,6 +4,7 @@ import type * as React from 'react'
import type { Placement } from '../placement'
import { Popover as BasePopover } from '@base-ui/react/popover'
import { cn } from '../cn'
import { floatingPopupAnimationClassName } from '../overlay-shared'
import { parsePlacement } from '../placement'
export type { Placement }
@ -55,7 +56,7 @@ export function PopoverContent({
className={cn(
'rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg',
'outline-hidden focus:outline-hidden focus-visible:outline-hidden',
'origin-(--transform-origin) transition-[transform,scale,opacity] data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none',
floatingPopupAnimationClassName,
popupClassName,
)}
{...popupProps}

View File

@ -4,6 +4,7 @@ import type * as React from 'react'
import type { Placement } from '../placement'
import { PreviewCard as BasePreviewCard } from '@base-ui/react/preview-card'
import { cn } from '../cn'
import { floatingPopupAnimationClassName } from '../overlay-shared'
import { parsePlacement } from '../placement'
export type { Placement }
@ -65,7 +66,7 @@ export function PreviewCardContent({
<BasePreviewCard.Popup
className={cn(
'rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg',
'origin-(--transform-origin) transition-[transform,scale,opacity] data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none',
floatingPopupAnimationClassName,
popupClassName,
)}
{...popupProps}

View File

@ -8,9 +8,10 @@ import { cva } from 'class-variance-authority'
import { cn } from '../cn'
import { formLabelClassName } from '../form-control-shared'
import {
overlayLabelClassName,
overlayPopupAnimationClassName,
overlaySeparatorClassName,
floatingGroupLabelClassName,
floatingItemIndicatorClassName,
floatingPopupAnimationClassName,
floatingSeparatorClassName,
} from '../overlay-shared'
import { parsePlacement } from '../placement'
@ -73,11 +74,11 @@ export function SelectLabel({ className, ...props }: BaseSelect.Label.Props) {
}
export function SelectGroupLabel({ className, ...props }: BaseSelect.GroupLabel.Props) {
return <BaseSelect.GroupLabel className={cn(overlayLabelClassName, className)} {...props} />
return <BaseSelect.GroupLabel className={cn(floatingGroupLabelClassName, className)} {...props} />
}
export function SelectSeparator({ className, ...props }: BaseSelect.Separator.Props) {
return <BaseSelect.Separator className={cn(overlaySeparatorClassName, className)} {...props} />
return <BaseSelect.Separator className={cn(floatingSeparatorClassName, className)} {...props} />
}
type SelectContentProps = {
@ -124,7 +125,7 @@ export function SelectContent({
<BaseSelect.Popup
className={cn(
'min-w-(--anchor-width) rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg',
overlayPopupAnimationClassName,
floatingPopupAnimationClassName,
popupClassName,
)}
{...popupProps}
@ -165,10 +166,7 @@ export function SelectItemIndicator({
...props
}: Omit<BaseSelect.ItemIndicator.Props, 'children'>) {
return (
<BaseSelect.ItemIndicator
className={cn('ms-auto flex shrink-0 items-center text-text-accent', className)}
{...props}
>
<BaseSelect.ItemIndicator className={cn(floatingItemIndicatorClassName, className)} {...props}>
<span className="i-ri-check-line h-4 w-4" aria-hidden />
</BaseSelect.ItemIndicator>
)