### What problem does this PR solve? Feat: The value selected in the Select component only displays the icon #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.19.1
| icons: {}, | icons: {}, | ||||
| hash: true, | hash: true, | ||||
| favicons: ['/logo.svg'], | favicons: ['/logo.svg'], | ||||
| headScripts: [{ src: '/iconfont.js', defer: true }], | |||||
| clickToComponent: {}, | clickToComponent: {}, | ||||
| history: { | history: { | ||||
| type: 'browser', | type: 'browser', |
| <SelectPrimitive.Item | <SelectPrimitive.Item | ||||
| ref={ref} | ref={ref} | ||||
| className={cn( | className={cn( | ||||
| 'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', | |||||
| 'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', | |||||
| className, | className, | ||||
| )} | )} | ||||
| {...props} | {...props} | ||||
| > | > | ||||
| <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> | |||||
| <span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center"> | |||||
| <SelectPrimitive.ItemIndicator> | <SelectPrimitive.ItemIndicator> | ||||
| <Check className="h-4 w-4" /> | <Check className="h-4 w-4" /> | ||||
| </SelectPrimitive.ItemIndicator> | </SelectPrimitive.ItemIndicator> | ||||
| </span> | </span> | ||||
| <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText> | <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText> | ||||
| </SelectPrimitive.Item> | </SelectPrimitive.Item> | ||||
| )); | )); | ||||
| label: React.ReactNode; | label: React.ReactNode; | ||||
| value: string; | value: string; | ||||
| disabled?: boolean; | disabled?: boolean; | ||||
| icon?: React.ReactNode; | |||||
| }; | }; | ||||
| export type RAGFlowSelectGroupOptionType = { | export type RAGFlowSelectGroupOptionType = { | ||||
| placeholder?: React.ReactNode; | placeholder?: React.ReactNode; | ||||
| contentProps?: React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>; | contentProps?: React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>; | ||||
| triggerClassName?: string; | triggerClassName?: string; | ||||
| onlyShowSelectedIcon?: boolean; | |||||
| } & SelectPrimitive.SelectProps; | } & SelectPrimitive.SelectProps; | ||||
| /** | /** | ||||
| contentProps = {}, | contentProps = {}, | ||||
| defaultValue, | defaultValue, | ||||
| triggerClassName, | triggerClassName, | ||||
| onlyShowSelectedIcon = false, | |||||
| }, | }, | ||||
| ref, | ref, | ||||
| ) { | ) { | ||||
| }); | }); | ||||
| }, [initialValue]); | }, [initialValue]); | ||||
| // The value selected in the drop-down box only displays the icon | |||||
| const label = React.useMemo(() => { | |||||
| let nextOptions = options; | |||||
| if (options.some((x) => !('value' in x))) { | |||||
| nextOptions = (options as RAGFlowSelectGroupOptionType[]).reduce< | |||||
| RAGFlowSelectOptionType[] | |||||
| >((pre, cur) => { | |||||
| return pre.concat(cur?.options ?? []); | |||||
| }, []); | |||||
| } | |||||
| const option = (nextOptions as RAGFlowSelectOptionType[]).find( | |||||
| (x) => x.value === value, | |||||
| ); | |||||
| return onlyShowSelectedIcon ? option?.icon : option?.label; | |||||
| }, [onlyShowSelectedIcon, options, value]); | |||||
| return ( | return ( | ||||
| <Select onValueChange={handleChange} value={value} key={key}> | <Select onValueChange={handleChange} value={value} key={key}> | ||||
| <FormControlWidget> | <FormControlWidget> | ||||
| ref={ref} | ref={ref} | ||||
| className={triggerClassName} | className={triggerClassName} | ||||
| > | > | ||||
| <SelectValue placeholder={placeholder} /> | |||||
| <SelectValue placeholder={placeholder}>{label}</SelectValue> | |||||
| </SelectTrigger> | </SelectTrigger> | ||||
| </FormControlWidget> | </FormControlWidget> | ||||
| <SelectContent {...contentProps}> | <SelectContent {...contentProps}> | ||||
| key={o.value} | key={o.value} | ||||
| disabled={o.disabled} | disabled={o.disabled} | ||||
| > | > | ||||
| {o.label} | |||||
| <div className="flex items-center gap-1"> | |||||
| {o.icon} | |||||
| {o.label} | |||||
| </div> | |||||
| </SelectItem> | </SelectItem> | ||||
| ); | ); | ||||
| } | } | ||||
| return ( | return ( | ||||
| <SelectGroup key={idx}> | <SelectGroup key={idx}> | ||||
| <SelectLabel>{o.label}</SelectLabel> | |||||
| <SelectLabel className="pl-2">{o.label}</SelectLabel> | |||||
| {o.options.map((x) => ( | {o.options.map((x) => ( | ||||
| <SelectItem value={x.value} key={x.value} disabled={x.disabled}> | <SelectItem value={x.value} key={x.value} disabled={x.disabled}> | ||||
| {x.label} | {x.label} |
| export const SwitchElseTo = 'end_cpn_id'; | export const SwitchElseTo = 'end_cpn_id'; | ||||
| export const SwitchOperatorOptions = [ | export const SwitchOperatorOptions = [ | ||||
| { value: '=', label: 'equal' }, | |||||
| { value: '≠', label: 'notEqual' }, | |||||
| { value: '>', label: 'gt' }, | |||||
| { value: '≥', label: 'ge' }, | |||||
| { value: '<', label: 'lt' }, | |||||
| { value: '≤', label: 'le' }, | |||||
| { value: 'contains', label: 'contains' }, | |||||
| { value: 'not contains', label: 'notContains' }, | |||||
| { value: 'start with', label: 'startWith' }, | |||||
| { value: 'end with', label: 'endWith' }, | |||||
| { value: 'empty', label: 'empty' }, | |||||
| { value: 'not empty', label: 'notEmpty' }, | |||||
| { value: '=', label: 'equal', icon: 'equal' }, | |||||
| { value: '≠', label: 'notEqual', icon: 'not-equals' }, | |||||
| { value: '>', label: 'gt', icon: 'Less' }, | |||||
| { value: '≥', label: 'ge', icon: 'Greater-or-equal' }, | |||||
| { value: '<', label: 'lt', icon: 'Less' }, | |||||
| { value: '≤', label: 'le', icon: 'less-or-equal' }, | |||||
| { value: 'contains', label: 'contains', icon: 'Contains' }, | |||||
| { value: 'not contains', label: 'notContains', icon: 'not-contains' }, | |||||
| { value: 'start with', label: 'startWith', icon: 'list-start' }, | |||||
| { value: 'end with', label: 'endWith', icon: 'list-end' }, | |||||
| // { value: 'empty', label: 'empty', icon: '' }, | |||||
| // { value: 'not empty', label: 'notEmpty', icon: '' }, | |||||
| ]; | ]; | ||||
| export const SwitchLogicOperatorOptions = ['and', 'or']; | export const SwitchLogicOperatorOptions = ['and', 'or']; |
| import { FormContainer } from '@/components/form-container'; | import { FormContainer } from '@/components/form-container'; | ||||
| import { IconFont } from '@/components/icon-font'; | |||||
| import { BlockButton, Button } from '@/components/ui/button'; | import { BlockButton, Button } from '@/components/ui/button'; | ||||
| import { Card, CardContent } from '@/components/ui/card'; | import { Card, CardContent } from '@/components/ui/card'; | ||||
| import { | import { | ||||
| FormMessage, | FormMessage, | ||||
| } from '@/components/ui/form'; | } from '@/components/ui/form'; | ||||
| import { RAGFlowSelect } from '@/components/ui/select'; | import { RAGFlowSelect } from '@/components/ui/select'; | ||||
| import { Separator } from '@/components/ui/separator'; | |||||
| import { Textarea } from '@/components/ui/textarea'; | import { Textarea } from '@/components/ui/textarea'; | ||||
| import { ISwitchForm } from '@/interfaces/database/flow'; | import { ISwitchForm } from '@/interfaces/database/flow'; | ||||
| import { cn } from '@/lib/utils'; | |||||
| import { zodResolver } from '@hookform/resolvers/zod'; | import { zodResolver } from '@hookform/resolvers/zod'; | ||||
| import { X } from 'lucide-react'; | import { X } from 'lucide-react'; | ||||
| import { useMemo } from 'react'; | import { useMemo } from 'react'; | ||||
| const switchOperatorOptions = useMemo(() => { | const switchOperatorOptions = useMemo(() => { | ||||
| return SwitchOperatorOptions.map((x) => ({ | return SwitchOperatorOptions.map((x) => ({ | ||||
| value: x.value, | value: x.value, | ||||
| icon: ( | |||||
| <IconFont | |||||
| name={x.icon} | |||||
| className={cn('size-4', { 'rotate-180': x.value === '>' })} | |||||
| ></IconFont> | |||||
| ), | |||||
| label: t(`flow.switchOperatorOptions.${x.label}`), | label: t(`flow.switchOperatorOptions.${x.label}`), | ||||
| })); | })); | ||||
| }, [t]); | }, [t]); | ||||
| return ( | return ( | ||||
| <div key={field.id} className="flex"> | <div key={field.id} className="flex"> | ||||
| <Card className="bg-transparent border-input-border border flex-1"> | <Card className="bg-transparent border-input-border border flex-1"> | ||||
| <section className="p-2 bg-background-card flex justify-between"> | |||||
| <section className="p-2 bg-background-card flex justify-between items-center"> | |||||
| <FormField | <FormField | ||||
| control={form.control} | control={form.control} | ||||
| name={`${name}.${index}.cpn_id`} | name={`${name}.${index}.cpn_id`} | ||||
| <RAGFlowSelect | <RAGFlowSelect | ||||
| {...field} | {...field} | ||||
| options={componentIdOptions} | options={componentIdOptions} | ||||
| triggerClassName="w-30 text-background-checked" | |||||
| /> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| <FormField | |||||
| control={form.control} | |||||
| name={`${name}.${index}.operator`} | |||||
| render={({ field }) => ( | |||||
| <FormItem> | |||||
| <FormControl> | |||||
| <RAGFlowSelect | |||||
| {...field} | |||||
| options={switchOperatorOptions} | |||||
| triggerClassName="w-30" | |||||
| placeholder={t('common.pleaseSelect')} | |||||
| triggerClassName="w-30 text-background-checked bg-transparent border-none" | |||||
| /> | /> | ||||
| </FormControl> | </FormControl> | ||||
| <FormMessage /> | <FormMessage /> | ||||
| </FormItem> | </FormItem> | ||||
| )} | )} | ||||
| /> | /> | ||||
| <div className="flex items-center"> | |||||
| <Separator orientation="vertical" className="h-2.5" /> | |||||
| <FormField | |||||
| control={form.control} | |||||
| name={`${name}.${index}.operator`} | |||||
| render={({ field }) => ( | |||||
| <FormItem> | |||||
| <FormControl> | |||||
| <RAGFlowSelect | |||||
| {...field} | |||||
| options={switchOperatorOptions} | |||||
| onlyShowSelectedIcon | |||||
| triggerClassName="w-30 bg-transparent border-none" | |||||
| /> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| </div> | |||||
| </section> | </section> | ||||
| <CardContent className="p-4 "> | <CardContent className="p-4 "> | ||||
| <FormField | <FormField | ||||
| return conditions?.filter((x) => !!x).map((x) => x?.to) ?? []; | return conditions?.filter((x) => !!x).map((x) => x?.to) ?? []; | ||||
| }; | }; | ||||
| const switchOperatorOptions = useMemo(() => { | |||||
| return SwitchOperatorOptions.map((x) => ({ | |||||
| value: x.value, | |||||
| label: t(`flow.switchOperatorOptions.${x.label}`), | |||||
| })); | |||||
| }, [t]); | |||||
| const switchLogicOperatorOptions = useMemo(() => { | const switchLogicOperatorOptions = useMemo(() => { | ||||
| return SwitchLogicOperatorOptions.map((x) => ({ | return SwitchLogicOperatorOptions.map((x) => ({ | ||||
| value: x, | value: x, |