| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  | import type { FC } from 'react' | 
					
						
							|  |  |  | import { | 
					
						
							|  |  |  |   Fragment, | 
					
						
							|  |  |  |   memo, | 
					
						
							|  |  |  |   useCallback, | 
					
						
							|  |  |  |   useState, | 
					
						
							|  |  |  | } from 'react' | 
					
						
							| 
									
										
										
										
											2024-06-20 11:05:08 +08:00
										 |  |  | import { | 
					
						
							|  |  |  |   RiZoomInLine, | 
					
						
							|  |  |  |   RiZoomOutLine, | 
					
						
							|  |  |  | } from '@remixicon/react' | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  | import { useTranslation } from 'react-i18next' | 
					
						
							|  |  |  | import { | 
					
						
							|  |  |  |   useReactFlow, | 
					
						
							|  |  |  |   useViewport, | 
					
						
							|  |  |  | } from 'reactflow' | 
					
						
							|  |  |  | import { | 
					
						
							|  |  |  |   useNodesSyncDraft, | 
					
						
							|  |  |  |   useWorkflowReadOnly, | 
					
						
							|  |  |  | } from '../hooks' | 
					
						
							| 
									
										
										
										
											2024-12-17 12:20:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  | import ShortcutsName from '../shortcuts-name' | 
					
						
							| 
									
										
										
										
											2024-12-17 12:20:49 +08:00
										 |  |  | import Divider from '../../base/divider' | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  | import TipPopup from './tip-popup' | 
					
						
							| 
									
										
										
										
											2024-07-09 15:05:40 +08:00
										 |  |  | import cn from '@/utils/classnames' | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  | import { | 
					
						
							|  |  |  |   PortalToFollowElem, | 
					
						
							|  |  |  |   PortalToFollowElemContent, | 
					
						
							|  |  |  |   PortalToFollowElemTrigger, | 
					
						
							|  |  |  | } from '@/app/components/base/portal-to-follow-elem' | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | enum ZoomType { | 
					
						
							|  |  |  |   zoomIn = 'zoomIn', | 
					
						
							|  |  |  |   zoomOut = 'zoomOut', | 
					
						
							|  |  |  |   zoomToFit = 'zoomToFit', | 
					
						
							|  |  |  |   zoomTo25 = 'zoomTo25', | 
					
						
							|  |  |  |   zoomTo50 = 'zoomTo50', | 
					
						
							|  |  |  |   zoomTo75 = 'zoomTo75', | 
					
						
							|  |  |  |   zoomTo100 = 'zoomTo100', | 
					
						
							|  |  |  |   zoomTo200 = 'zoomTo200', | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | const ZoomInOut: FC = () => { | 
					
						
							|  |  |  |   const { t } = useTranslation() | 
					
						
							|  |  |  |   const { | 
					
						
							|  |  |  |     zoomIn, | 
					
						
							|  |  |  |     zoomOut, | 
					
						
							|  |  |  |     zoomTo, | 
					
						
							|  |  |  |     fitView, | 
					
						
							|  |  |  |   } = useReactFlow() | 
					
						
							|  |  |  |   const { zoom } = useViewport() | 
					
						
							|  |  |  |   const { handleSyncWorkflowDraft } = useNodesSyncDraft() | 
					
						
							|  |  |  |   const [open, setOpen] = useState(false) | 
					
						
							|  |  |  |   const { | 
					
						
							|  |  |  |     workflowReadOnly, | 
					
						
							|  |  |  |     getWorkflowReadOnly, | 
					
						
							|  |  |  |   } = useWorkflowReadOnly() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const ZOOM_IN_OUT_OPTIONS = [ | 
					
						
							|  |  |  |     [ | 
					
						
							|  |  |  |       { | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |         key: ZoomType.zoomTo200, | 
					
						
							|  |  |  |         text: '200%', | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |         key: ZoomType.zoomTo100, | 
					
						
							|  |  |  |         text: '100%', | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |         key: ZoomType.zoomTo75, | 
					
						
							|  |  |  |         text: '75%', | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |         key: ZoomType.zoomTo50, | 
					
						
							|  |  |  |         text: '50%', | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         key: ZoomType.zoomTo25, | 
					
						
							|  |  |  |         text: '25%', | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |     ], | 
					
						
							|  |  |  |     [ | 
					
						
							|  |  |  |       { | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |         key: ZoomType.zoomToFit, | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |         text: t('workflow.operator.zoomToFit'), | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     ], | 
					
						
							|  |  |  |   ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const handleZoom = (type: string) => { | 
					
						
							|  |  |  |     if (workflowReadOnly) | 
					
						
							|  |  |  |       return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |     if (type === ZoomType.zoomToFit) | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |       fitView() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |     if (type === ZoomType.zoomTo25) | 
					
						
							|  |  |  |       zoomTo(0.25) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (type === ZoomType.zoomTo50) | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |       zoomTo(0.5) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |     if (type === ZoomType.zoomTo75) | 
					
						
							|  |  |  |       zoomTo(0.75) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (type === ZoomType.zoomTo100) | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |       zoomTo(1) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |     if (type === ZoomType.zoomTo200) | 
					
						
							|  |  |  |       zoomTo(2) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |     handleSyncWorkflowDraft() | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const handleTrigger = useCallback(() => { | 
					
						
							|  |  |  |     if (getWorkflowReadOnly()) | 
					
						
							|  |  |  |       return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setOpen(v => !v) | 
					
						
							|  |  |  |   }, [getWorkflowReadOnly]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return ( | 
					
						
							|  |  |  |     <PortalToFollowElem | 
					
						
							|  |  |  |       placement='top-start' | 
					
						
							|  |  |  |       open={open} | 
					
						
							|  |  |  |       onOpenChange={setOpen} | 
					
						
							|  |  |  |       offset={{ | 
					
						
							|  |  |  |         mainAxis: 4, | 
					
						
							|  |  |  |         crossAxis: -2, | 
					
						
							|  |  |  |       }} | 
					
						
							|  |  |  |     > | 
					
						
							| 
									
										
										
										
											2024-12-25 13:30:51 +08:00
										 |  |  |       <PortalToFollowElemTrigger asChild> | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |         <div className={`
 | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |           h-9 cursor-pointer rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg | 
					
						
							| 
									
										
										
										
											2025-04-29 18:04:33 +08:00
										 |  |  |           p-0.5 text-[13px] shadow-lg backdrop-blur-[5px] | 
					
						
							| 
									
										
										
										
											2024-12-17 12:20:49 +08:00
										 |  |  |           hover:bg-state-base-hover | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |           ${workflowReadOnly && '!cursor-not-allowed opacity-50'} | 
					
						
							|  |  |  |         `}>
 | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |           <div className={cn( | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |             'flex h-8 w-[98px] items-center justify-between rounded-lg', | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |           )}> | 
					
						
							|  |  |  |             <TipPopup | 
					
						
							|  |  |  |               title={t('workflow.operator.zoomOut')} | 
					
						
							|  |  |  |               shortcuts={['ctrl', '-']} | 
					
						
							|  |  |  |             > | 
					
						
							|  |  |  |               <div | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |                 className={`flex h-8 w-8 items-center justify-center rounded-lg ${zoom <= 0.25 ? 'cursor-not-allowed' : 'cursor-pointer hover:bg-black/5'}`} | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |                 onClick={(e) => { | 
					
						
							| 
									
										
										
										
											2024-12-25 13:30:51 +08:00
										 |  |  |                   if (zoom <= 0.25) | 
					
						
							|  |  |  |                     return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |                   e.stopPropagation() | 
					
						
							|  |  |  |                   zoomOut() | 
					
						
							|  |  |  |                 }} | 
					
						
							|  |  |  |               > | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |                 <RiZoomOutLine className='h-4 w-4 text-text-tertiary hover:text-text-secondary' /> | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |               </div> | 
					
						
							|  |  |  |             </TipPopup> | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |             <div onClick={handleTrigger} className={cn('system-sm-medium w-[34px] text-text-tertiary hover:text-text-secondary')}>{Number.parseFloat(`${zoom * 100}`).toFixed(0)}%</div> | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |             <TipPopup | 
					
						
							|  |  |  |               title={t('workflow.operator.zoomIn')} | 
					
						
							|  |  |  |               shortcuts={['ctrl', '+']} | 
					
						
							|  |  |  |             > | 
					
						
							|  |  |  |               <div | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |                 className={`flex h-8 w-8 items-center justify-center rounded-lg ${zoom >= 2 ? 'cursor-not-allowed' : 'cursor-pointer hover:bg-black/5'}`} | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |                 onClick={(e) => { | 
					
						
							| 
									
										
										
										
											2024-12-25 13:30:51 +08:00
										 |  |  |                   if (zoom >= 2) | 
					
						
							|  |  |  |                     return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |                   e.stopPropagation() | 
					
						
							|  |  |  |                   zoomIn() | 
					
						
							|  |  |  |                 }} | 
					
						
							|  |  |  |               > | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |                 <RiZoomInLine className='h-4 w-4 text-text-tertiary hover:text-text-secondary' /> | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  |               </div> | 
					
						
							|  |  |  |             </TipPopup> | 
					
						
							|  |  |  |           </div> | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |         </div> | 
					
						
							|  |  |  |       </PortalToFollowElemTrigger> | 
					
						
							|  |  |  |       <PortalToFollowElemContent className='z-10'> | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |         <div className='w-[145px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-[5px]'> | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |           { | 
					
						
							|  |  |  |             ZOOM_IN_OUT_OPTIONS.map((options, i) => ( | 
					
						
							|  |  |  |               <Fragment key={i}> | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   i !== 0 && ( | 
					
						
							| 
									
										
										
										
											2024-12-17 12:20:49 +08:00
										 |  |  |                     <Divider className='m-0' /> | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |                   ) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 <div className='p-1'> | 
					
						
							|  |  |  |                   { | 
					
						
							|  |  |  |                     options.map(option => ( | 
					
						
							|  |  |  |                       <div | 
					
						
							|  |  |  |                         key={option.key} | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |                         className='system-md-regular flex h-8 cursor-pointer items-center justify-between space-x-1 rounded-lg py-1.5 pl-3 pr-2 text-text-secondary hover:bg-state-base-hover' | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |                         onClick={() => handleZoom(option.key)} | 
					
						
							|  |  |  |                       > | 
					
						
							| 
									
										
										
										
											2024-12-17 12:20:49 +08:00
										 |  |  |                         <span>{option.text}</span> | 
					
						
							|  |  |  |                         <div className='flex items-center space-x-0.5'> | 
					
						
							|  |  |  |                           { | 
					
						
							|  |  |  |                             option.key === ZoomType.zoomToFit && ( | 
					
						
							|  |  |  |                               <ShortcutsName keys={['ctrl', '1']} /> | 
					
						
							|  |  |  |                             ) | 
					
						
							|  |  |  |                           } | 
					
						
							|  |  |  |                           { | 
					
						
							|  |  |  |                             option.key === ZoomType.zoomTo50 && ( | 
					
						
							|  |  |  |                               <ShortcutsName keys={['shift', '5']} /> | 
					
						
							|  |  |  |                             ) | 
					
						
							|  |  |  |                           } | 
					
						
							|  |  |  |                           { | 
					
						
							|  |  |  |                             option.key === ZoomType.zoomTo100 && ( | 
					
						
							|  |  |  |                               <ShortcutsName keys={['shift', '1']} /> | 
					
						
							|  |  |  |                             ) | 
					
						
							|  |  |  |                           } | 
					
						
							|  |  |  |                         </div> | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |                       </div> | 
					
						
							|  |  |  |                     )) | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                 </div> | 
					
						
							|  |  |  |               </Fragment> | 
					
						
							|  |  |  |             )) | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |       </PortalToFollowElemContent> | 
					
						
							|  |  |  |     </PortalToFollowElem> | 
					
						
							|  |  |  |   ) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default memo(ZoomInOut) |