mirror of
https://mirror.skon.top/github.com/langgenius/dify.git
synced 2026-04-20 15:20:15 +08:00
refactor(web): quality closure pass on base UI primitives (#35333)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
width: 4px;
|
||||
height: 12px;
|
||||
transform: translateX(-50%);
|
||||
background: linear-gradient(to bottom, var(--scroll-area-edge-hint-bg, var(--color-components-panel-bg)), transparent);
|
||||
background: linear-gradient(to bottom, var(--color-components-panel-bg), transparent);
|
||||
}
|
||||
|
||||
[data-dify-scrollbar][data-orientation='vertical']::after {
|
||||
@@ -24,7 +24,7 @@
|
||||
width: 4px;
|
||||
height: 12px;
|
||||
transform: translateX(-50%);
|
||||
background: linear-gradient(to top, var(--scroll-area-edge-hint-bg, var(--color-components-panel-bg)), transparent);
|
||||
background: linear-gradient(to top, var(--color-components-panel-bg), transparent);
|
||||
}
|
||||
|
||||
[data-dify-scrollbar][data-orientation='horizontal']::before {
|
||||
@@ -33,7 +33,7 @@
|
||||
width: 12px;
|
||||
height: 4px;
|
||||
transform: translateY(-50%);
|
||||
background: linear-gradient(to right, var(--scroll-area-edge-hint-bg, var(--color-components-panel-bg)), transparent);
|
||||
background: linear-gradient(to right, var(--color-components-panel-bg), transparent);
|
||||
}
|
||||
|
||||
[data-dify-scrollbar][data-orientation='horizontal']::after {
|
||||
@@ -42,7 +42,7 @@
|
||||
width: 12px;
|
||||
height: 4px;
|
||||
transform: translateY(-50%);
|
||||
background: linear-gradient(to left, var(--scroll-area-edge-hint-bg, var(--color-components-panel-bg)), transparent);
|
||||
background: linear-gradient(to left, var(--color-components-panel-bg), transparent);
|
||||
}
|
||||
|
||||
[data-dify-scrollbar][data-orientation='vertical']:not([data-overflow-y-start])::before {
|
||||
@@ -61,12 +61,6 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[data-dify-scrollbar][data-hovering] > [data-orientation],
|
||||
[data-dify-scrollbar][data-scrolling] > [data-orientation],
|
||||
[data-dify-scrollbar] > [data-orientation]:active {
|
||||
background-color: var(--scroll-area-thumb-bg-active, var(--color-state-base-handle-hover));
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
[data-dify-scrollbar]::before,
|
||||
[data-dify-scrollbar]::after {
|
||||
@@ -1,3 +1,4 @@
|
||||
@import '../themes/light.css' layer(base);
|
||||
@import '../themes/dark.css' layer(base);
|
||||
@import './utilities.css';
|
||||
@import './components.css';
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import type { CSSProperties } from 'react'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Slider } from '@/app/components/base/ui/slider'
|
||||
|
||||
const weightedScoreSliderStyle: CSSProperties & Record<'--slider-track' | '--slider-range', string> = {
|
||||
'--slider-track': 'var(--color-util-colors-teal-teal-500)',
|
||||
'--slider-range': 'var(--color-util-colors-blue-light-blue-light-500)',
|
||||
const weightedScoreSliderSlotClassNames = {
|
||||
track: 'bg-util-colors-teal-teal-500',
|
||||
indicator: 'bg-util-colors-blue-light-blue-light-500',
|
||||
}
|
||||
|
||||
const formatNumber = (value: number) => {
|
||||
@@ -37,7 +36,7 @@ const WeightedScore = ({
|
||||
return (
|
||||
<div>
|
||||
<div className="space-x-3 rounded-lg border border-components-panel-border px-3 pt-5 pb-2">
|
||||
<div className="grow" style={weightedScoreSliderStyle}>
|
||||
<div className="grow">
|
||||
<Slider
|
||||
className="grow"
|
||||
max={1.0}
|
||||
@@ -47,6 +46,7 @@ const WeightedScore = ({
|
||||
onValueChange={v => !readonly && onChange({ value: [v, (10 - v * 10) / 10] })}
|
||||
disabled={readonly}
|
||||
aria-label={t('weightedScore.semantic', { ns: 'dataset' })}
|
||||
slotClassNames={weightedScoreSliderSlotClassNames}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-3 flex justify-between">
|
||||
|
||||
@@ -14,16 +14,14 @@ export const AlertDialogDescription = BaseAlertDialog.Description
|
||||
type AlertDialogContentProps = {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
overlayClassName?: string
|
||||
popupProps?: Omit<BaseAlertDialog.Popup.Props, 'children' | 'className'>
|
||||
backdropClassName?: string
|
||||
backdropProps?: Omit<BaseAlertDialog.Backdrop.Props, 'className'>
|
||||
}
|
||||
|
||||
export function AlertDialogContent({
|
||||
children,
|
||||
className,
|
||||
overlayClassName,
|
||||
popupProps,
|
||||
backdropClassName,
|
||||
backdropProps,
|
||||
}: AlertDialogContentProps) {
|
||||
return (
|
||||
@@ -33,11 +31,10 @@ export function AlertDialogContent({
|
||||
className={cn(
|
||||
'fixed inset-0 z-1002 bg-background-overlay',
|
||||
'transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 motion-reduce:transition-none',
|
||||
overlayClassName,
|
||||
backdropClassName,
|
||||
)}
|
||||
/>
|
||||
<BaseAlertDialog.Popup
|
||||
{...popupProps}
|
||||
className={cn(
|
||||
'fixed top-1/2 left-1/2 z-1002 max-h-[calc(100vh-2rem)] w-[480px] 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',
|
||||
|
||||
@@ -42,14 +42,14 @@ export function DialogCloseButton({
|
||||
type DialogContentProps = {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
overlayClassName?: string
|
||||
backdropProps?: BaseDialog.Backdrop.Props
|
||||
backdropClassName?: string
|
||||
backdropProps?: Omit<BaseDialog.Backdrop.Props, 'className'>
|
||||
}
|
||||
|
||||
export function DialogContent({
|
||||
children,
|
||||
className,
|
||||
overlayClassName,
|
||||
backdropClassName,
|
||||
backdropProps,
|
||||
}: DialogContentProps) {
|
||||
return (
|
||||
@@ -59,8 +59,7 @@ export function DialogContent({
|
||||
className={cn(
|
||||
'fixed inset-0 z-1002 bg-background-overlay',
|
||||
'transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 motion-reduce:transition-none',
|
||||
overlayClassName,
|
||||
backdropProps?.className,
|
||||
backdropClassName,
|
||||
)}
|
||||
/>
|
||||
<BaseDialog.Popup
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { ScrollArea as BaseScrollArea } from '@base-ui/react/scroll-area'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import './scroll-area.css'
|
||||
|
||||
export const ScrollAreaRoot = BaseScrollArea.Root
|
||||
type ScrollAreaRootProps = BaseScrollArea.Root.Props
|
||||
@@ -25,7 +24,7 @@ type ScrollAreaProps = Omit<ScrollAreaRootProps, 'children'> & {
|
||||
}
|
||||
|
||||
const scrollAreaScrollbarClassName = cn(
|
||||
'flex touch-none overflow-clip p-1 opacity-100 transition-opacity select-none motion-reduce:transition-none',
|
||||
'group/scrollbar flex touch-none overflow-clip p-1 opacity-100 transition-opacity select-none motion-reduce:transition-none',
|
||||
'pointer-events-none data-hovering:pointer-events-auto',
|
||||
'data-scrolling:pointer-events-auto',
|
||||
'data-[orientation=vertical]:absolute data-[orientation=vertical]:inset-y-0 data-[orientation=vertical]:w-3 data-[orientation=vertical]:justify-center',
|
||||
@@ -36,6 +35,9 @@ const scrollAreaThumbClassName = cn(
|
||||
'shrink-0 rounded-sm bg-state-base-handle transition-[background-color] motion-reduce:transition-none',
|
||||
'data-[orientation=vertical]:w-1',
|
||||
'data-[orientation=horizontal]:h-1',
|
||||
'group-data-hovering/scrollbar:bg-state-base-handle-hover',
|
||||
'group-data-scrolling/scrollbar:bg-state-base-handle-hover',
|
||||
'active:bg-state-base-handle-hover',
|
||||
)
|
||||
|
||||
const scrollAreaViewportClassName = cn(
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import type { ReactNode } from 'react'
|
||||
import type { Placement } from '@/app/components/base/ui/placement'
|
||||
import { Select as BaseSelect } from '@base-ui/react/select'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { cva } from 'class-variance-authority'
|
||||
import {
|
||||
overlayLabelClassName,
|
||||
overlaySeparatorClassName,
|
||||
@@ -15,34 +17,43 @@ export const SelectValue = BaseSelect.Value
|
||||
/** @public */
|
||||
export const SelectGroup = BaseSelect.Group
|
||||
|
||||
const selectSizeClassName: Record<string, string> = {
|
||||
small: 'h-6 gap-px rounded-md px-2 py-1 system-xs-regular',
|
||||
medium: 'h-8 gap-0.5 rounded-lg px-3 py-2 system-sm-regular',
|
||||
large: 'h-9 gap-0.5 rounded-[10px] px-4 py-2 system-md-regular',
|
||||
}
|
||||
const selectTriggerVariants = cva(
|
||||
[
|
||||
'group flex w-full items-center border-0 bg-components-input-bg-normal text-left text-components-input-text-filled outline-hidden',
|
||||
'hover:bg-state-base-hover-alt focus-visible:bg-state-base-hover-alt',
|
||||
'data-placeholder:text-components-input-text-placeholder',
|
||||
'data-readonly:cursor-default data-readonly:bg-transparent data-readonly:hover:bg-transparent',
|
||||
'data-disabled:cursor-not-allowed data-disabled:bg-components-input-bg-disabled data-disabled:text-components-input-text-filled-disabled data-disabled:hover:bg-components-input-bg-disabled',
|
||||
'data-disabled:data-placeholder:text-components-input-text-disabled',
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
small: 'h-6 gap-px rounded-md px-2 py-1 system-xs-regular',
|
||||
medium: 'h-8 gap-0.5 rounded-lg px-3 py-2 system-sm-regular',
|
||||
large: 'h-9 gap-0.5 rounded-[10px] px-4 py-2 system-md-regular',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
size: 'medium',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
type SelectTriggerProps = BaseSelect.Trigger.Props & {
|
||||
size?: 'small' | 'medium' | 'large'
|
||||
}
|
||||
type SelectTriggerProps
|
||||
= Omit<BaseSelect.Trigger.Props, 'className'>
|
||||
& VariantProps<typeof selectTriggerVariants>
|
||||
& { className?: string }
|
||||
|
||||
export function SelectTrigger({
|
||||
className,
|
||||
children,
|
||||
size = 'medium',
|
||||
size,
|
||||
...props
|
||||
}: SelectTriggerProps) {
|
||||
return (
|
||||
<BaseSelect.Trigger
|
||||
className={cn(
|
||||
'group flex w-full items-center border-0 bg-components-input-bg-normal text-left text-components-input-text-filled outline-hidden',
|
||||
'hover:bg-state-base-hover-alt focus-visible:bg-state-base-hover-alt',
|
||||
'data-placeholder:text-components-input-text-placeholder',
|
||||
selectSizeClassName[size],
|
||||
'data-readonly:cursor-default data-readonly:bg-transparent data-readonly:hover:bg-transparent',
|
||||
'data-disabled:cursor-not-allowed data-disabled:bg-components-input-bg-disabled data-disabled:text-components-input-text-filled-disabled data-disabled:hover:bg-components-input-bg-disabled',
|
||||
'data-disabled:data-placeholder:text-components-input-text-disabled',
|
||||
className,
|
||||
)}
|
||||
className={cn(selectTriggerVariants({ size, className }))}
|
||||
{...props}
|
||||
>
|
||||
<span className="min-w-0 grow truncate">
|
||||
|
||||
@@ -2,16 +2,98 @@
|
||||
|
||||
import { Slider as BaseSlider } from '@base-ui/react/slider'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import * as React from 'react'
|
||||
|
||||
/** @public */
|
||||
export const SliderRoot = BaseSlider.Root
|
||||
|
||||
type SliderRootProps = BaseSlider.Root.Props<number>
|
||||
|
||||
const sliderControlClassName = cn(
|
||||
'relative flex h-5 w-full touch-none items-center select-none',
|
||||
'data-disabled:cursor-not-allowed',
|
||||
)
|
||||
|
||||
type SliderControlProps = BaseSlider.Control.Props
|
||||
|
||||
/** @public */
|
||||
export function SliderControl({ className, ...props }: SliderControlProps) {
|
||||
return (
|
||||
<BaseSlider.Control
|
||||
className={cn(sliderControlClassName, className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const sliderTrackClassName = cn(
|
||||
'relative h-1 w-full overflow-hidden rounded-full',
|
||||
'bg-components-slider-track',
|
||||
)
|
||||
|
||||
type SliderTrackProps = BaseSlider.Track.Props
|
||||
|
||||
/** @public */
|
||||
export function SliderTrack({ className, ...props }: SliderTrackProps) {
|
||||
return (
|
||||
<BaseSlider.Track
|
||||
className={cn(sliderTrackClassName, className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const sliderIndicatorClassName = cn(
|
||||
'h-full rounded-full',
|
||||
'bg-components-slider-range',
|
||||
)
|
||||
|
||||
type SliderIndicatorProps = BaseSlider.Indicator.Props
|
||||
|
||||
/** @public */
|
||||
export function SliderIndicator({ className, ...props }: SliderIndicatorProps) {
|
||||
return (
|
||||
<BaseSlider.Indicator
|
||||
className={cn(sliderIndicatorClassName, className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const sliderThumbClassName = cn(
|
||||
'block h-5 w-2 shrink-0 rounded-[3px] border-[0.5px]',
|
||||
'border-components-slider-knob-border bg-components-slider-knob shadow-sm',
|
||||
'transition-[background-color,border-color,box-shadow,opacity] motion-reduce:transition-none',
|
||||
'hover:bg-components-slider-knob-hover',
|
||||
'focus-visible:ring-2 focus-visible:ring-components-slider-knob-border-hover focus-visible:ring-offset-0 focus-visible:outline-hidden',
|
||||
'active:shadow-md',
|
||||
'group-data-disabled/slider:border-components-slider-knob-border group-data-disabled/slider:bg-components-slider-knob-disabled group-data-disabled/slider:shadow-none',
|
||||
)
|
||||
|
||||
type SliderThumbProps = BaseSlider.Thumb.Props
|
||||
|
||||
/** @public */
|
||||
export function SliderThumb({ className, ...props }: SliderThumbProps) {
|
||||
return (
|
||||
<BaseSlider.Thumb
|
||||
className={cn(sliderThumbClassName, className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
type SliderSlotClassNames = {
|
||||
control?: string
|
||||
track?: string
|
||||
indicator?: string
|
||||
thumb?: string
|
||||
}
|
||||
|
||||
type SliderBaseProps = Pick<
|
||||
SliderRootProps,
|
||||
'onValueChange' | 'min' | 'max' | 'step' | 'disabled' | 'name'
|
||||
> & Pick<SliderThumbProps, 'aria-label' | 'aria-labelledby'> & {
|
||||
className?: string
|
||||
slotClassNames?: SliderSlotClassNames
|
||||
}
|
||||
|
||||
type ControlledSliderProps = SliderBaseProps & {
|
||||
@@ -27,30 +109,6 @@ type UncontrolledSliderProps = SliderBaseProps & {
|
||||
type SliderProps = ControlledSliderProps | UncontrolledSliderProps
|
||||
|
||||
const sliderRootClassName = 'group/slider relative inline-flex w-full data-disabled:opacity-30'
|
||||
const sliderControlClassName = cn(
|
||||
'relative flex h-5 w-full touch-none items-center select-none',
|
||||
'data-disabled:cursor-not-allowed',
|
||||
)
|
||||
const sliderTrackClassName = cn(
|
||||
'relative h-1 w-full overflow-hidden rounded-full',
|
||||
'bg-(--slider-track,var(--color-components-slider-track))',
|
||||
)
|
||||
const sliderIndicatorClassName = cn(
|
||||
'h-full rounded-full',
|
||||
'bg-(--slider-range,var(--color-components-slider-range))',
|
||||
)
|
||||
const sliderThumbClassName = cn(
|
||||
'block h-5 w-2 shrink-0 rounded-[3px] border-[0.5px]',
|
||||
'border-(--slider-knob-border,var(--color-components-slider-knob-border))',
|
||||
'bg-(--slider-knob,var(--color-components-slider-knob)) shadow-sm',
|
||||
'transition-[background-color,border-color,box-shadow,opacity] motion-reduce:transition-none',
|
||||
'hover:bg-(--slider-knob-hover,var(--color-components-slider-knob-hover))',
|
||||
'focus-visible:ring-2 focus-visible:ring-components-slider-knob-border-hover focus-visible:ring-offset-0 focus-visible:outline-hidden',
|
||||
'active:shadow-md',
|
||||
'group-data-disabled/slider:bg-(--slider-knob-disabled,var(--color-components-slider-knob-disabled))',
|
||||
'group-data-disabled/slider:border-(--slider-knob-border,var(--color-components-slider-knob-border))',
|
||||
'group-data-disabled/slider:shadow-none',
|
||||
)
|
||||
|
||||
const getSafeValue = (value: number | undefined, min: number) => {
|
||||
if (value === undefined)
|
||||
@@ -69,11 +127,12 @@ export function Slider({
|
||||
disabled = false,
|
||||
name,
|
||||
className,
|
||||
slotClassNames,
|
||||
'aria-label': ariaLabel,
|
||||
'aria-labelledby': ariaLabelledby,
|
||||
}: SliderProps) {
|
||||
return (
|
||||
<BaseSlider.Root
|
||||
<SliderRoot
|
||||
value={getSafeValue(value, min)}
|
||||
defaultValue={getSafeValue(defaultValue, min)}
|
||||
onValueChange={onValueChange}
|
||||
@@ -85,16 +144,16 @@ export function Slider({
|
||||
thumbAlignment="edge-client-only"
|
||||
className={cn(sliderRootClassName, className)}
|
||||
>
|
||||
<BaseSlider.Control className={sliderControlClassName}>
|
||||
<BaseSlider.Track className={sliderTrackClassName}>
|
||||
<BaseSlider.Indicator className={sliderIndicatorClassName} />
|
||||
</BaseSlider.Track>
|
||||
<BaseSlider.Thumb
|
||||
<SliderControl className={slotClassNames?.control}>
|
||||
<SliderTrack className={slotClassNames?.track}>
|
||||
<SliderIndicator className={slotClassNames?.indicator} />
|
||||
</SliderTrack>
|
||||
<SliderThumb
|
||||
aria-label={ariaLabel}
|
||||
aria-labelledby={ariaLabelledby}
|
||||
className={sliderThumbClassName}
|
||||
className={slotClassNames?.thumb}
|
||||
/>
|
||||
</BaseSlider.Control>
|
||||
</BaseSlider.Root>
|
||||
</SliderControl>
|
||||
</SliderRoot>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ type PricingProps = {
|
||||
}
|
||||
|
||||
const pricingScrollAreaClassNames = {
|
||||
root: 'relative h-full w-full overflow-hidden [--scroll-area-edge-hint-bg:var(--color-saas-background)]',
|
||||
root: 'relative h-full w-full overflow-hidden',
|
||||
viewport: 'overscroll-contain',
|
||||
content: 'min-h-full min-w-[1200px]',
|
||||
verticalScrollbar: 'data-[orientation=vertical]:my-2 data-[orientation=vertical]:me-1',
|
||||
|
||||
@@ -27,7 +27,7 @@ const MenuDialog = ({
|
||||
}}
|
||||
>
|
||||
<DialogContent
|
||||
overlayClassName="bg-transparent"
|
||||
backdropClassName="bg-transparent"
|
||||
className={cn(
|
||||
'top-0 left-0 h-full max-h-none w-full max-w-none translate-x-0 translate-y-0 overflow-hidden rounded-none border-none bg-background-sidenav-bg p-0 shadow-none backdrop-blur-md',
|
||||
className,
|
||||
|
||||
@@ -24,7 +24,7 @@ const WorkflowOnboardingModal: FC<WorkflowOnboardingModalProps> = ({
|
||||
<Dialog open={isShow} onOpenChange={onClose} disablePointerDismissal>
|
||||
<DialogContent
|
||||
className="w-[618px] max-w-[618px] rounded-2xl border border-effects-highlight bg-background-default-subtle shadow-lg"
|
||||
overlayClassName="bg-workflow-canvas-canvas-overlay"
|
||||
backdropClassName="bg-workflow-canvas-canvas-overlay"
|
||||
>
|
||||
<DialogCloseButton />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user