Severian's picture
initial commit
a8b3f00
raw
history blame
5.91 kB
import type { FC } from 'react'
import {
Fragment,
memo,
useCallback,
useState,
} from 'react'
import {
RiZoomInLine,
RiZoomOutLine,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import {
useReactFlow,
useViewport,
} from 'reactflow'
import {
useNodesSyncDraft,
useWorkflowReadOnly,
} from '../hooks'
import {
getKeyboardKeyNameBySystem,
} from '../utils'
import ShortcutsName from '../shortcuts-name'
import TipPopup from './tip-popup'
import cn from '@/utils/classnames'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
enum ZoomType {
zoomIn = 'zoomIn',
zoomOut = 'zoomOut',
zoomToFit = 'zoomToFit',
zoomTo25 = 'zoomTo25',
zoomTo50 = 'zoomTo50',
zoomTo75 = 'zoomTo75',
zoomTo100 = 'zoomTo100',
zoomTo200 = 'zoomTo200',
}
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 = [
[
{
key: ZoomType.zoomTo200,
text: '200%',
},
{
key: ZoomType.zoomTo100,
text: '100%',
},
{
key: ZoomType.zoomTo75,
text: '75%',
},
{
key: ZoomType.zoomTo50,
text: '50%',
},
{
key: ZoomType.zoomTo25,
text: '25%',
},
],
[
{
key: ZoomType.zoomToFit,
text: t('workflow.operator.zoomToFit'),
},
],
]
const handleZoom = (type: string) => {
if (workflowReadOnly)
return
if (type === ZoomType.zoomToFit)
fitView()
if (type === ZoomType.zoomTo25)
zoomTo(0.25)
if (type === ZoomType.zoomTo50)
zoomTo(0.5)
if (type === ZoomType.zoomTo75)
zoomTo(0.75)
if (type === ZoomType.zoomTo100)
zoomTo(1)
if (type === ZoomType.zoomTo200)
zoomTo(2)
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,
}}
>
<PortalToFollowElemTrigger asChild onClick={handleTrigger}>
<div className={`
p-0.5 h-9 cursor-pointer text-[13px] text-gray-500 font-medium rounded-lg bg-white shadow-lg border-[0.5px] border-gray-100
${workflowReadOnly && '!cursor-not-allowed opacity-50'}
`}>
<div className={cn(
'flex items-center justify-between w-[98px] h-8 hover:bg-gray-50 rounded-lg',
open && 'bg-gray-50',
)}>
<TipPopup
title={t('workflow.operator.zoomOut')}
shortcuts={['ctrl', '-']}
>
<div
className='flex items-center justify-center w-8 h-8 rounded-lg cursor-pointer hover:bg-black/5'
onClick={(e) => {
e.stopPropagation()
zoomOut()
}}
>
<RiZoomOutLine className='w-4 h-4' />
</div>
</TipPopup>
<div className='w-[34px]'>{parseFloat(`${zoom * 100}`).toFixed(0)}%</div>
<TipPopup
title={t('workflow.operator.zoomIn')}
shortcuts={['ctrl', '+']}
>
<div
className='flex items-center justify-center w-8 h-8 rounded-lg cursor-pointer hover:bg-black/5'
onClick={(e) => {
e.stopPropagation()
zoomIn()
}}
>
<RiZoomInLine className='w-4 h-4' />
</div>
</TipPopup>
</div>
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent className='z-10'>
<div className='w-[145px] rounded-lg border-[0.5px] border-gray-200 bg-white shadow-lg'>
{
ZOOM_IN_OUT_OPTIONS.map((options, i) => (
<Fragment key={i}>
{
i !== 0 && (
<div className='h-[1px] bg-gray-100' />
)
}
<div className='p-1'>
{
options.map(option => (
<div
key={option.key}
className='flex items-center justify-between px-3 h-8 rounded-lg hover:bg-gray-50 cursor-pointer text-sm text-gray-700'
onClick={() => handleZoom(option.key)}
>
{option.text}
{
option.key === ZoomType.zoomToFit && (
<ShortcutsName keys={[`${getKeyboardKeyNameBySystem('ctrl')}`, '1']} />
)
}
{
option.key === ZoomType.zoomTo50 && (
<ShortcutsName keys={['shift', '5']} />
)
}
{
option.key === ZoomType.zoomTo100 && (
<ShortcutsName keys={['shift', '1']} />
)
}
</div>
))
}
</div>
</Fragment>
))
}
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
)
}
export default memo(ZoomInOut)