import type { FC } from '../../lib/teact/teact'; import React, { useEffect, useLayoutEffect, useRef } from '../../lib/teact/teact'; import { requestForcedReflow, requestMutation } from '../../lib/fasterdom/fasterdom'; import type { MenuItemContextAction } from './ListItem'; import { MouseButton } from '../../util/windowEnvironment'; import forceReflow from '../../util/forceReflow'; import buildClassName from '../../util/buildClassName'; import renderText from '../common/helpers/renderText'; import useMenuPosition from '../../hooks/useMenuPosition'; import useContextMenuHandlers from '../../hooks/useContextMenuHandlers'; import { useFastClick } from '../../hooks/useFastClick'; import useLastCallback from '../../hooks/useLastCallback'; import Menu from './Menu'; import MenuItem from './MenuItem'; import MenuSeparator from './MenuSeparator'; import './Tab.scss'; type OwnProps = { className?: string; title: string; isActive?: boolean; isBlocked?: boolean; badgeCount?: number; isBadgeActive?: boolean; previousActiveTab?: number; onClick?: (arg: number) => void; clickArg?: number; contextActions?: MenuItemContextAction[]; contextRootElementSelector?: string; }; const classNames = { active: 'Tab--active', badgeActive: 'Tab__badge--active', }; const Tab: FC = ({ className, title, isActive, isBlocked, badgeCount, isBadgeActive, previousActiveTab, onClick, clickArg, contextActions, contextRootElementSelector, }) => { // eslint-disable-next-line no-null/no-null const tabRef = useRef(null); useLayoutEffect(() => { // Set initial active state if (isActive && previousActiveTab === undefined && tabRef.current) { tabRef.current!.classList.add(classNames.active); } }, [isActive, previousActiveTab]); useEffect(() => { if (!isActive || previousActiveTab === undefined) { return; } const tabEl = tabRef.current!; const prevTabEl = tabEl.parentElement!.children[previousActiveTab]; if (!prevTabEl) { // The number of tabs in the parent component has decreased. It is necessary to add the active tab class name. if (isActive && !tabEl.classList.contains(classNames.active)) { requestMutation(() => { tabEl.classList.add(classNames.active); }); } return; } const platformEl = tabEl.querySelector('.platform')!; const prevPlatformEl = prevTabEl.querySelector('.platform')!; // We move and resize the platform, so it repeats the position and size of the previous one const shiftLeft = prevPlatformEl.parentElement!.offsetLeft - platformEl.parentElement!.offsetLeft; const scaleFactor = prevPlatformEl.clientWidth / platformEl.clientWidth; requestMutation(() => { prevPlatformEl.classList.remove('animate'); platformEl.classList.remove('animate'); platformEl.style.transform = `translate3d(${shiftLeft}px, 0, 0) scale3d(${scaleFactor}, 1, 1)`; requestForcedReflow(() => { forceReflow(platformEl); return () => { platformEl.classList.add('animate'); platformEl.style.transform = 'none'; prevTabEl.classList.remove(classNames.active); tabEl.classList.add(classNames.active); }; }); }); }, [isActive, previousActiveTab]); const { contextMenuPosition, handleContextMenu, handleBeforeContextMenu, handleContextMenuClose, handleContextMenuHide, isContextMenuOpen, } = useContextMenuHandlers(tabRef, !contextActions); const { handleClick, handleMouseDown } = useFastClick((e: React.MouseEvent) => { if (contextActions && (e.button === MouseButton.Secondary || !onClick)) { handleBeforeContextMenu(e); } if (e.type === 'mousedown' && e.button !== MouseButton.Main) { return; } onClick?.(clickArg!); }); const getTriggerElement = useLastCallback(() => tabRef.current); const getRootElement = useLastCallback( () => (contextRootElementSelector ? tabRef.current!.closest(contextRootElementSelector) : document.body), ); const getMenuElement = useLastCallback( () => document.querySelector('#portals')!.querySelector('.Tab-context-menu .bubble'), ); const getLayout = useLastCallback(() => ({ withPortal: true })); const { positionX, positionY, transformOriginX, transformOriginY, style: menuStyle, } = useMenuPosition( contextMenuPosition, getTriggerElement, getRootElement, getMenuElement, getLayout, ); return (
{renderText(title)} {Boolean(badgeCount) && ( {badgeCount} )} {isBlocked && } {contextActions && contextMenuPosition !== undefined && ( {contextActions.map((action) => ( ('isSeparator' in action) ? ( ) : ( {action.title} ) ))} )}
); }; export default Tab;