Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. 'use client';
  2. import * as SelectPrimitive from '@radix-ui/react-select';
  3. import { Check, ChevronDown, ChevronUp, X } from 'lucide-react';
  4. import * as React from 'react';
  5. import { cn } from '@/lib/utils';
  6. import { ControllerRenderProps } from 'react-hook-form';
  7. import { FormControl } from '@/components/ui/form';
  8. import { forwardRef, useCallback, useEffect } from 'react';
  9. const Select = SelectPrimitive.Root;
  10. const SelectGroup = SelectPrimitive.Group;
  11. const SelectValue = SelectPrimitive.Value;
  12. const SelectTrigger = React.forwardRef<
  13. React.ElementRef<typeof SelectPrimitive.Trigger>,
  14. React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & {
  15. onReset?: () => void;
  16. allowClear?: boolean;
  17. }
  18. >(({ className, children, value, onReset, allowClear, ...props }, ref) => (
  19. <SelectPrimitive.Trigger
  20. ref={ref}
  21. className={cn(
  22. 'flex h-10 w-full items-center justify-between rounded-md border border-input bg-colors-background-inverse-weak px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
  23. className,
  24. )}
  25. {...props}
  26. >
  27. {children}
  28. <SelectPrimitive.Icon
  29. asChild
  30. onPointerDown={(event) => {
  31. event.stopPropagation();
  32. }}
  33. >
  34. {value && allowClear ? (
  35. <X className="h-4 w-4 opacity-50 cursor-pointer" onClick={onReset} />
  36. ) : (
  37. <ChevronDown className="h-4 w-4 opacity-50" />
  38. )}
  39. </SelectPrimitive.Icon>
  40. </SelectPrimitive.Trigger>
  41. ));
  42. SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
  43. const SelectScrollUpButton = React.forwardRef<
  44. React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
  45. React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
  46. >(({ className, ...props }, ref) => (
  47. <SelectPrimitive.ScrollUpButton
  48. ref={ref}
  49. className={cn(
  50. 'flex cursor-default items-center justify-center py-1',
  51. className,
  52. )}
  53. {...props}
  54. >
  55. <ChevronUp className="h-4 w-4" />
  56. </SelectPrimitive.ScrollUpButton>
  57. ));
  58. SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
  59. const SelectScrollDownButton = React.forwardRef<
  60. React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
  61. React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
  62. >(({ className, ...props }, ref) => (
  63. <SelectPrimitive.ScrollDownButton
  64. ref={ref}
  65. className={cn(
  66. 'flex cursor-default items-center justify-center py-1',
  67. className,
  68. )}
  69. {...props}
  70. >
  71. <ChevronDown className="h-4 w-4" />
  72. </SelectPrimitive.ScrollDownButton>
  73. ));
  74. SelectScrollDownButton.displayName =
  75. SelectPrimitive.ScrollDownButton.displayName;
  76. const SelectContent = React.forwardRef<
  77. React.ElementRef<typeof SelectPrimitive.Content>,
  78. React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
  79. >(({ className, children, position = 'popper', ...props }, ref) => (
  80. <SelectPrimitive.Portal>
  81. <SelectPrimitive.Content
  82. ref={ref}
  83. className={cn(
  84. 'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
  85. position === 'popper' &&
  86. 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
  87. className,
  88. )}
  89. position={position}
  90. {...props}
  91. >
  92. <SelectScrollUpButton />
  93. <SelectPrimitive.Viewport
  94. className={cn(
  95. 'p-1',
  96. position === 'popper' &&
  97. 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]',
  98. )}
  99. >
  100. {children}
  101. </SelectPrimitive.Viewport>
  102. <SelectScrollDownButton />
  103. </SelectPrimitive.Content>
  104. </SelectPrimitive.Portal>
  105. ));
  106. SelectContent.displayName = SelectPrimitive.Content.displayName;
  107. const SelectLabel = React.forwardRef<
  108. React.ElementRef<typeof SelectPrimitive.Label>,
  109. React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
  110. >(({ className, ...props }, ref) => (
  111. <SelectPrimitive.Label
  112. ref={ref}
  113. className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
  114. {...props}
  115. />
  116. ));
  117. SelectLabel.displayName = SelectPrimitive.Label.displayName;
  118. const SelectItem = React.forwardRef<
  119. React.ElementRef<typeof SelectPrimitive.Item>,
  120. React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
  121. >(({ className, children, ...props }, ref) => (
  122. <SelectPrimitive.Item
  123. ref={ref}
  124. className={cn(
  125. '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',
  126. className,
  127. )}
  128. {...props}
  129. >
  130. <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
  131. <SelectPrimitive.ItemIndicator>
  132. <Check className="h-4 w-4" />
  133. </SelectPrimitive.ItemIndicator>
  134. </span>
  135. <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
  136. </SelectPrimitive.Item>
  137. ));
  138. SelectItem.displayName = SelectPrimitive.Item.displayName;
  139. const SelectSeparator = React.forwardRef<
  140. React.ElementRef<typeof SelectPrimitive.Separator>,
  141. React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
  142. >(({ className, ...props }, ref) => (
  143. <SelectPrimitive.Separator
  144. ref={ref}
  145. className={cn('-mx-1 my-1 h-px bg-muted', className)}
  146. {...props}
  147. />
  148. ));
  149. SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
  150. export {
  151. Select,
  152. SelectContent,
  153. SelectGroup,
  154. SelectItem,
  155. SelectLabel,
  156. SelectScrollDownButton,
  157. SelectScrollUpButton,
  158. SelectSeparator,
  159. SelectTrigger,
  160. SelectValue,
  161. };
  162. export type RAGFlowSelectOptionType = {
  163. label: React.ReactNode;
  164. value: string;
  165. disabled?: boolean;
  166. };
  167. export type RAGFlowSelectGroupOptionType = {
  168. label: React.ReactNode;
  169. options: RAGFlowSelectOptionType[];
  170. };
  171. type RAGFlowSelectProps = Partial<ControllerRenderProps> & {
  172. FormControlComponent?: typeof FormControl;
  173. options?: (RAGFlowSelectOptionType | RAGFlowSelectGroupOptionType)[];
  174. allowClear?: boolean;
  175. };
  176. /**
  177. *
  178. * Reference:
  179. * https://github.com/shadcn-ui/ui/discussions/638
  180. * https://github.com/radix-ui/primitives/discussions/2645#discussioncomment-8343397
  181. *
  182. * @export
  183. * @param {(Partial<ControllerRenderProps> & {
  184. * FormControlComponent?: typeof FormControl;
  185. * })} {
  186. * value: initialValue,
  187. * onChange,
  188. * FormControlComponent,
  189. * }
  190. * @return {*}
  191. */
  192. export const RAGFlowSelect = forwardRef<
  193. React.ElementRef<typeof SelectPrimitive.Trigger>,
  194. RAGFlowSelectProps
  195. >(function (
  196. {
  197. value: initialValue,
  198. onChange,
  199. FormControlComponent,
  200. options = [],
  201. allowClear,
  202. },
  203. ref,
  204. ) {
  205. const [key, setKey] = React.useState(+new Date());
  206. const [value, setValue] = React.useState<string | undefined>(undefined);
  207. const FormControlWidget = FormControlComponent
  208. ? FormControlComponent
  209. : ({ children }: React.PropsWithChildren) => <div>{children}</div>;
  210. const handleChange = useCallback(
  211. (val?: string) => {
  212. setValue(val);
  213. onChange?.(val);
  214. },
  215. [onChange],
  216. );
  217. const handleReset = useCallback(() => {
  218. handleChange(undefined);
  219. setKey(+new Date());
  220. }, [handleChange]);
  221. useEffect(() => {
  222. setValue((preValue) => {
  223. if (preValue !== initialValue) {
  224. return initialValue;
  225. }
  226. return preValue;
  227. });
  228. }, [initialValue]);
  229. return (
  230. <Select onValueChange={handleChange} value={value} key={key}>
  231. <FormControlWidget>
  232. <SelectTrigger
  233. className="bg-colors-background-inverse-weak"
  234. value={value}
  235. onReset={handleReset}
  236. allowClear={allowClear}
  237. ref={ref}
  238. >
  239. <SelectValue placeholder="Select a verified email to display" />
  240. </SelectTrigger>
  241. </FormControlWidget>
  242. <SelectContent>
  243. {options.map((o, idx) => {
  244. if ('value' in o) {
  245. return (
  246. <SelectItem
  247. value={o.value as RAGFlowSelectOptionType['value']}
  248. key={o.value}
  249. disabled={o.disabled}
  250. >
  251. {o.label}
  252. </SelectItem>
  253. );
  254. }
  255. return (
  256. <SelectGroup key={idx}>
  257. <SelectLabel>{o.label}</SelectLabel>
  258. {o.options.map((x) => (
  259. <SelectItem value={x.value} key={x.value} disabled={x.disabled}>
  260. {x.label}
  261. </SelectItem>
  262. ))}
  263. </SelectGroup>
  264. );
  265. })}
  266. </SelectContent>
  267. </Select>
  268. );
  269. });