fix: fix dataset-page's bugs (#8786)

### What problem does this PR solve?

fix dataset-page's bugs,Input component supports icon, added Radio
component, and removed antd from chunk-result-bar page [#3221
](https://github.com/infiniflow/ragflow/issues/3221)

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2025-07-11 11:34:36 +08:00
committed by GitHub
parent 07208e519b
commit 52dce4329d
23 changed files with 399 additions and 153 deletions

View File

@ -4,10 +4,12 @@ import { cn } from '@/lib/utils';
import { Search } from 'lucide-react';
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
extends React.InputHTMLAttributes<HTMLInputElement> {
value?: string | number | readonly string[] | undefined;
}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
({ className, type, value, ...props }, ref) => {
return (
<input
type={type}
@ -16,6 +18,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
className,
)}
ref={ref}
value={value ?? ''}
{...props}
/>
);

View File

@ -5,7 +5,27 @@ import * as React from 'react';
import { cn } from '@/lib/utils';
const Popover = PopoverPrimitive.Root;
const Popover = (props: PopoverPrimitive.PopoverProps) => {
const { children, open: openState, onOpenChange } = props;
const [open, setOpen] = React.useState(true);
React.useEffect(() => {
setOpen(!!openState);
}, [openState]);
const handleOnOpenChange = React.useCallback(
(e: boolean) => {
if (onOpenChange) {
onOpenChange?.(e);
}
setOpen(e);
},
[onOpenChange],
);
return (
<PopoverPrimitive.Root open={open} onOpenChange={handleOnOpenChange}>
{children}
</PopoverPrimitive.Root>
);
};
const PopoverTrigger = PopoverPrimitive.Trigger;

View File

@ -0,0 +1,133 @@
import { cn } from '@/lib/utils';
import { Radio as LucideRadio } from 'lucide-react';
import React, { useContext, useState } from 'react';
const RadioGroupContext = React.createContext<{
value: string | number;
onChange: (value: string | number) => void;
disabled?: boolean;
} | null>(null);
type RadioProps = {
value: string | number;
checked?: boolean;
disabled?: boolean;
onChange?: (checked: boolean) => void;
children?: React.ReactNode;
};
function Radio({ value, checked, disabled, onChange, children }: RadioProps) {
const groupContext = useContext(RadioGroupContext);
const isControlled = checked !== undefined;
// const [internalChecked, setInternalChecked] = useState(false);
const isChecked = isControlled ? checked : groupContext?.value === value;
const mergedDisabled = disabled || groupContext?.disabled;
const handleClick = () => {
if (mergedDisabled) return;
// if (!isControlled) {
// setInternalChecked(!isChecked);
// }
if (onChange) {
onChange(!isChecked);
}
if (groupContext && !groupContext.disabled) {
groupContext.onChange(value);
}
};
return (
<label
className={cn(
'flex items-center cursor-pointer gap-2 text-sm',
mergedDisabled && 'cursor-not-allowed opacity-50',
)}
>
<span
className={cn(
'flex h-4 w-4 items-center justify-center rounded-full border border-input transition-colors',
'peer ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
isChecked && 'border-primary bg-primary/10',
mergedDisabled && 'border-muted',
)}
onClick={handleClick}
>
{isChecked && (
<LucideRadio className="h-3 w-3 fill-primary text-primary" />
)}
</span>
{children && <span className="text-foreground">{children}</span>}
</label>
);
}
type RadioGroupProps = {
value?: string | number;
defaultValue?: string | number;
onChange?: (value: string | number) => void;
disabled?: boolean;
children: React.ReactNode;
className?: string;
direction?: 'horizontal' | 'vertical';
};
function Group({
value,
defaultValue,
onChange,
disabled,
children,
className,
direction = 'horizontal',
}: RadioGroupProps) {
const [internalValue, setInternalValue] = useState(defaultValue || '');
const isControlled = value !== undefined;
const mergedValue = isControlled ? value : internalValue;
const handleChange = (val: string | number) => {
if (disabled) return;
if (!isControlled) {
setInternalValue(val);
}
if (onChange) {
onChange(val);
}
};
return (
<RadioGroupContext.Provider
value={{
value: mergedValue,
onChange: handleChange,
disabled,
}}
>
<div
className={cn(
'flex gap-4',
direction === 'vertical' ? 'flex-col' : 'flex-row',
className,
)}
>
{React.Children.map(children, (child) =>
React.cloneElement(child as React.ReactElement, {
disabled: disabled || child?.props?.disabled,
}),
)}
</div>
</RadioGroupContext.Provider>
);
}
const RadioComponent = Object.assign(Radio, {
Group,
});
export { RadioComponent as Radio };

View File

@ -31,6 +31,15 @@ export function Segmented({
onChange,
className,
}: SegmentedProps) {
const [selectedValue, setSelectedValue] = React.useState<
SegmentedValue | undefined
>(value);
const handleOnChange = (e: SegmentedValue) => {
if (onChange) {
onChange(e);
}
setSelectedValue(e);
};
return (
<div
className={cn(
@ -48,11 +57,11 @@ export function Segmented({
className={cn(
'inline-flex items-center px-6 py-2 text-base font-normal rounded-3xl cursor-pointer text-text-badge',
{
'bg-text-title': value === actualValue,
'text-text-title-invert': value === actualValue,
'bg-text-title': selectedValue === actualValue,
'text-text-title-invert': selectedValue === actualValue,
},
)}
onClick={() => onChange?.(actualValue)}
onClick={() => handleOnChange(actualValue)}
>
{isObject ? option.label : option}
</div>