| 
									
										
										
										
											2024-10-21 10:32:37 +08:00
										 |  |  | import type { FC } from 'react' | 
					
						
							|  |  |  | import { useMemo, useState } from 'react' | 
					
						
							|  |  |  | import { RiArrowDownSLine, RiCheckLine, RiCloseCircleFill, RiFilter3Line } from '@remixicon/react' | 
					
						
							|  |  |  | import cn from '@/utils/classnames' | 
					
						
							|  |  |  | import { | 
					
						
							|  |  |  |   PortalToFollowElem, | 
					
						
							|  |  |  |   PortalToFollowElemContent, | 
					
						
							|  |  |  |   PortalToFollowElemTrigger, | 
					
						
							|  |  |  | } from '@/app/components/base/portal-to-follow-elem' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export type Item = { | 
					
						
							|  |  |  |   value: number | string | 
					
						
							|  |  |  |   name: string | 
					
						
							|  |  |  | } & Record<string, any> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Props = { | 
					
						
							|  |  |  |   className?: string | 
					
						
							|  |  |  |   panelClassName?: string | 
					
						
							|  |  |  |   showLeftIcon?: boolean | 
					
						
							|  |  |  |   leftIcon?: any | 
					
						
							|  |  |  |   value: number | string | 
					
						
							|  |  |  |   items: Item[] | 
					
						
							|  |  |  |   onSelect: (item: any) => void | 
					
						
							|  |  |  |   onClear: () => void | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | const Chip: FC<Props> = ({ | 
					
						
							|  |  |  |   className, | 
					
						
							|  |  |  |   panelClassName, | 
					
						
							|  |  |  |   showLeftIcon = true, | 
					
						
							|  |  |  |   leftIcon, | 
					
						
							|  |  |  |   value, | 
					
						
							|  |  |  |   items, | 
					
						
							|  |  |  |   onSelect, | 
					
						
							|  |  |  |   onClear, | 
					
						
							|  |  |  | }) => { | 
					
						
							|  |  |  |   const [open, setOpen] = useState(false) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const triggerContent = useMemo(() => { | 
					
						
							|  |  |  |     return items.find(item => item.value === value)?.name || '' | 
					
						
							|  |  |  |   }, [items, value]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return ( | 
					
						
							|  |  |  |     <PortalToFollowElem | 
					
						
							|  |  |  |       open={open} | 
					
						
							|  |  |  |       onOpenChange={setOpen} | 
					
						
							|  |  |  |       placement='bottom-start' | 
					
						
							|  |  |  |       offset={4} | 
					
						
							|  |  |  |     > | 
					
						
							|  |  |  |       <div className='relative'> | 
					
						
							|  |  |  |         <PortalToFollowElemTrigger | 
					
						
							|  |  |  |           onClick={() => setOpen(v => !v)} | 
					
						
							|  |  |  |           className='block' | 
					
						
							|  |  |  |         > | 
					
						
							|  |  |  |           <div className={cn( | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |             'flex min-h-8 cursor-pointer items-center rounded-lg border-[0.5px] border-transparent bg-components-input-bg-normal px-2 py-1 hover:bg-state-base-hover-alt', | 
					
						
							| 
									
										
										
										
											2024-10-21 10:32:37 +08:00
										 |  |  |             open && !value && '!bg-state-base-hover-alt hover:bg-state-base-hover-alt', | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |             !open && !!value && '!border-components-button-secondary-border !bg-components-button-secondary-bg shadow-xs hover:border-components-button-secondary-border-hover hover:!bg-components-button-secondary-bg-hover', | 
					
						
							|  |  |  |             open && !!value && '!border-components-button-secondary-border-hover !bg-components-button-secondary-bg-hover shadow-xs hover:border-components-button-secondary-border-hover hover:!bg-components-button-secondary-bg-hover', | 
					
						
							| 
									
										
										
										
											2024-10-21 10:32:37 +08:00
										 |  |  |             className, | 
					
						
							|  |  |  |           )}> | 
					
						
							|  |  |  |             {showLeftIcon && ( | 
					
						
							|  |  |  |               <div className='p-0.5'> | 
					
						
							|  |  |  |                 {leftIcon || ( | 
					
						
							|  |  |  |                   <RiFilter3Line className={cn('h-4 w-4 text-text-tertiary', !!value && 'text-text-secondary')} /> | 
					
						
							|  |  |  |                 )} | 
					
						
							|  |  |  |               </div> | 
					
						
							|  |  |  |             )} | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |             <div className='flex grow items-center gap-0.5 first-line:p-1'> | 
					
						
							| 
									
										
										
										
											2024-10-21 10:32:37 +08:00
										 |  |  |               <div className={cn('system-sm-regular text-text-tertiary', !!value && 'text-text-secondary')}> | 
					
						
							|  |  |  |                 {triggerContent} | 
					
						
							|  |  |  |               </div> | 
					
						
							|  |  |  |             </div> | 
					
						
							|  |  |  |             {!value && <RiArrowDownSLine className='h-4 w-4 text-text-tertiary' />} | 
					
						
							|  |  |  |             {!!value && ( | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |               <div className='group/clear cursor-pointer p-[1px]' onClick={(e) => { | 
					
						
							| 
									
										
										
										
											2024-10-21 10:32:37 +08:00
										 |  |  |                 e.stopPropagation() | 
					
						
							|  |  |  |                 onClear() | 
					
						
							|  |  |  |               }}> | 
					
						
							|  |  |  |                 <RiCloseCircleFill className='h-3.5 w-3.5 text-text-quaternary group-hover/clear:text-text-tertiary' /> | 
					
						
							|  |  |  |               </div> | 
					
						
							|  |  |  |             )} | 
					
						
							|  |  |  |           </div> | 
					
						
							|  |  |  |         </PortalToFollowElemTrigger> | 
					
						
							|  |  |  |         <PortalToFollowElemContent className='z-[1002]'> | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |           <div className={cn('relative w-[240px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg', panelClassName)}> | 
					
						
							|  |  |  |             <div className='max-h-72 overflow-auto p-1'> | 
					
						
							| 
									
										
										
										
											2024-10-21 10:32:37 +08:00
										 |  |  |               {items.map(item => ( | 
					
						
							|  |  |  |                 <div | 
					
						
							|  |  |  |                   key={item.value} | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |                   className='flex cursor-pointer items-center gap-2 rounded-lg px-2 py-[6px] pl-3 hover:bg-state-base-hover' | 
					
						
							| 
									
										
										
										
											2024-10-21 10:32:37 +08:00
										 |  |  |                   onClick={() => { | 
					
						
							|  |  |  |                     onSelect(item) | 
					
						
							|  |  |  |                     setOpen(false) | 
					
						
							|  |  |  |                   }} | 
					
						
							|  |  |  |                 > | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |                   <div title={item.name} className='system-sm-medium grow truncate text-text-secondary'>{item.name}</div> | 
					
						
							|  |  |  |                   {value === item.value && <RiCheckLine className='h-4 w-4 shrink-0 text-util-colors-blue-light-blue-light-600' />} | 
					
						
							| 
									
										
										
										
											2024-10-21 10:32:37 +08:00
										 |  |  |                 </div> | 
					
						
							|  |  |  |               ))} | 
					
						
							|  |  |  |             </div> | 
					
						
							|  |  |  |           </div> | 
					
						
							|  |  |  |         </PortalToFollowElemContent> | 
					
						
							|  |  |  |       </div> | 
					
						
							|  |  |  |     </PortalToFollowElem> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default Chip |