Left menu and chip links (#12294)

Small optimization for faster loading (gaining ~80ms - average time of a
click)

It might seem a little over-engineered but there are a lot of edge cases
and I couldn't find a simpler solution

I also tried to tackle Link Chips but it's more complex so this will be
for another PR
This commit is contained in:
Félix Malfait
2025-05-28 12:32:49 +02:00
committed by GitHub
parent 97d4ec96af
commit d4fac6793a
29 changed files with 203 additions and 60 deletions

View File

@ -18,7 +18,7 @@ import { isDefined } from 'twenty-shared/utils';
import { PREVIEWABLE_EXTENSIONS } from '@/activities/files/const/previewable-extensions.const';
import { IconCalendar, OverflowingTextWithTooltip } from 'twenty-ui/display';
import { isModifiedEvent } from 'twenty-ui/utilities';
import { isNavigationModifierPressed } from 'twenty-ui/utilities';
import { formatToHumanReadableDate } from '~/utils/date-utils';
import { getFileNameAndExtension } from '~/utils/file/getFileNameAndExtension';
@ -141,7 +141,7 @@ export const AttachmentRow = ({
const handleOpenDocument = (e: React.MouseEvent) => {
// Cmd/Ctrl+click opens new tab, right click opens context menu
if (isModifiedEvent(e) || e.button === 2) {
if (isNavigationModifierPressed(e) === true) {
return;
}

View File

@ -13,6 +13,7 @@ import styled from '@emotion/styled';
import { motion } from 'framer-motion';
import { useRef } from 'react';
import { useRecoilCallback } from 'recoil';
import { LINK_CHIP_CLICK_OUTSIDE_ID } from 'twenty-ui/components';
import { useIsMobile } from 'twenty-ui/utilities';
const StyledCommandMenu = styled(motion.div)`
@ -64,7 +65,10 @@ export const CommandMenuOpenContainer = ({
refs: [commandMenuRef],
callback: handleClickOutside,
listenerId: 'COMMAND_MENU_LISTENER_ID',
excludedClickOutsideIds: [PAGE_HEADER_COMMAND_MENU_BUTTON_CLICK_OUTSIDE_ID],
excludedClickOutsideIds: [
PAGE_HEADER_COMMAND_MENU_BUTTON_CLICK_OUTSIDE_ID,
LINK_CHIP_CLICK_OUTSIDE_ID,
],
});
return (

View File

@ -23,9 +23,15 @@ export const SentryInitEffect = () => {
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const [isSentryInitialized, setIsSentryInitialized] = useState(false);
const [isSentryInitializing, setIsSentryInitializing] = useState(false);
useEffect(() => {
if (isNonEmptyString(sentryConfig?.dsn) && !isSentryInitialized) {
if (
!isSentryInitializing &&
isNonEmptyString(sentryConfig?.dsn) &&
!isSentryInitialized
) {
setIsSentryInitializing(true);
init({
environment: sentryConfig?.environment ?? undefined,
release: sentryConfig?.release ?? undefined,
@ -38,6 +44,7 @@ export const SentryInitEffect = () => {
});
setIsSentryInitialized(true);
setIsSentryInitializing(false);
}
if (isDefined(currentUser)) {
@ -53,6 +60,7 @@ export const SentryInitEffect = () => {
}, [
sentryConfig,
isSentryInitialized,
isSentryInitializing,
currentUser,
currentWorkspace,
currentWorkspaceMember,

View File

@ -160,6 +160,7 @@ export const CurrentWorkspaceMemberFavorites = ({
rightOptions={rightOptions}
className="navigation-drawer-item"
isRightOptionsDropdownOpen={isFavoriteFolderEditDropdownOpen}
triggerEvent="CLICK"
/>
</FavoritesDroppable>
)}
@ -190,7 +191,7 @@ export const CurrentWorkspaceMemberFavorites = ({
label={favorite.labelIdentifier}
objectName={favorite.objectNameSingular}
Icon={() => <FavoriteIcon favorite={favorite} />}
to={favorite.link}
to={isDragging ? undefined : favorite.link}
active={index === selectedFavoriteIndex}
subItemState={getNavigationSubItemLeftAdornment({
index,
@ -205,6 +206,7 @@ export const CurrentWorkspaceMemberFavorites = ({
/>
}
isDragging={isDragging}
triggerEvent="CLICK"
/>
}
/>

View File

@ -50,7 +50,7 @@ export const CurrentWorkspaceMemberOrphanFavorites = () => {
currentViewPath,
favorite,
)}
to={favorite.link}
to={isDragging ? undefined : favorite.link}
rightOptions={
<LightIconButton
Icon={IconHeartOff}
@ -60,6 +60,7 @@ export const CurrentWorkspaceMemberOrphanFavorites = () => {
}
objectName={favorite.objectNameSingular}
isDragging={isDragging}
triggerEvent="CLICK"
/>
</StyledOrphanFavoritesContainer>
}

View File

@ -5,7 +5,7 @@ import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNaviga
import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useLingui } from '@lingui/react/macro';
import { useLocation } from 'react-router-dom';
import { useLocation, useNavigate } from 'react-router-dom';
import { useRecoilState, useSetRecoilState } from 'recoil';
import { IconSearch, IconSettings } from 'twenty-ui/display';
import { useIsMobile } from 'twenty-ui/utilities';
@ -24,6 +24,8 @@ export const MainNavigationDrawerFixedItems = () => {
navigationDrawerExpandedMemorizedState,
);
const navigate = useNavigate();
const { t } = useLingui();
const { openRecordsSearchPage } = useOpenRecordsSearchPageInCommandMenu();
@ -43,6 +45,7 @@ export const MainNavigationDrawerFixedItems = () => {
setNavigationDrawerExpandedMemorized(isNavigationDrawerExpanded);
setIsNavigationDrawerExpanded(true);
setNavigationMemorizedUrl(location.pathname + location.search);
navigate(getSettingsPath(SettingsPath.ProfilePage));
}}
Icon={IconSettings}
/>

View File

@ -5,6 +5,7 @@ import { useRecordChipData } from '@/object-record/hooks/useRecordChipData';
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
import { MouseEvent } from 'react';
import { useRecoilValue } from 'recoil';
import {
AvatarChip,
@ -13,7 +14,7 @@ import {
ChipVariant,
LinkAvatarChip,
} from 'twenty-ui/components';
import { isModifiedEvent } from 'twenty-ui/utilities';
import { TriggerEventType } from 'twenty-ui/utilities';
export type RecordChipProps = {
objectNameSingular: string;
@ -25,6 +26,7 @@ export type RecordChipProps = {
to?: string | undefined;
size?: ChipSize;
isLabelHidden?: boolean;
triggerEvent?: TriggerEventType;
};
export const RecordChip = ({
@ -37,6 +39,7 @@ export const RecordChip = ({
size,
forceDisableClick = false,
isLabelHidden = false,
triggerEvent = 'MOUSE_DOWN',
}: RecordChipProps) => {
const { recordChipData } = useRecordChipData({
objectNameSingular,
@ -47,6 +50,18 @@ export const RecordChip = ({
const recordIndexOpenRecordIn = useRecoilValue(recordIndexOpenRecordInState);
const isSidePanelViewOpenRecordInType =
recordIndexOpenRecordIn === ViewOpenRecordInType.SIDE_PANEL;
const handleCustomClick = isSidePanelViewOpenRecordInType
? (_event: MouseEvent<HTMLElement>) => {
openRecordInCommandMenu({
recordId: record.id,
objectNameSingular,
});
}
: undefined;
// TODO temporary until we create a record show page for Workspaces members
if (
@ -67,17 +82,6 @@ export const RecordChip = ({
);
}
const isSidePanelViewOpenRecordInType =
recordIndexOpenRecordIn === ViewOpenRecordInType.SIDE_PANEL;
const onClick = isSidePanelViewOpenRecordInType
? () =>
openRecordInCommandMenu({
recordId: record.id,
objectNameSingular,
})
: undefined;
return (
<LinkAvatarChip
size={size}
@ -95,16 +99,8 @@ export const RecordChip = ({
: AvatarChipVariant.Transparent)
}
to={to ?? getLinkToShowPage(objectNameSingular, record)}
onClick={(clickEvent) => {
// TODO refactor wrapper event listener to avoid colliding events
clickEvent.stopPropagation();
const isModifiedEventResult = isModifiedEvent(clickEvent);
if (isSidePanelViewOpenRecordInType && !isModifiedEventResult) {
clickEvent.preventDefault();
onClick?.();
}
}}
onClick={handleCustomClick}
triggerEvent={triggerEvent}
/>
);
};

View File

@ -40,6 +40,7 @@ import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
import { ViewType } from '@/views/types/ViewType';
import { LINK_CHIP_CLICK_OUTSIDE_ID } from 'twenty-ui/components';
import { getIndexNeighboursElementsFromArray } from '~/utils/array/getIndexNeighboursElementsFromArray';
const StyledContainer = styled.div`
@ -121,6 +122,7 @@ export const RecordBoard = () => {
MODAL_BACKDROP_CLICK_OUTSIDE_ID,
PAGE_ACTION_CONTAINER_CLICK_OUTSIDE_ID,
RECORD_BOARD_CARD_CLICK_OUTSIDE_ID,
LINK_CHIP_CLICK_OUTSIDE_ID,
],
listenerId: RECORD_BOARD_CLICK_OUTSIDE_LISTENER_ID,
refs: [],

View File

@ -68,6 +68,7 @@ export const RecordBoardCardBody = ({
},
useUpdateRecord: useUpdateOneRecordHook,
isDisplayModeFixHeight: true,
triggerEvent: 'CLICK',
}}
>
<RecordFieldComponentInstanceContext.Provider

View File

@ -20,8 +20,8 @@ import { Dispatch, SetStateAction, useContext } from 'react';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import { AvatarChipVariant } from 'twenty-ui/components';
import { Checkbox, CheckboxVariant, LightIconButton } from 'twenty-ui/input';
import { IconEye, IconEyeOff } from 'twenty-ui/display';
import { Checkbox, CheckboxVariant, LightIconButton } from 'twenty-ui/input';
const StyledCompactIconContainer = styled.div`
align-items: center;
@ -84,6 +84,7 @@ export const RecordBoardCardHeader = ({
? indexIdentifierUrl(recordId)
: undefined
}
triggerEvent="CLICK"
/>
)}
</StopPropagationContainer>

View File

@ -1,5 +1,6 @@
import { createContext } from 'react';
import { TriggerEventType } from 'twenty-ui/utilities';
import { FieldDefinition } from '../types/FieldDefinition';
import { FieldMetadata } from '../types/FieldMetadata';
@ -36,6 +37,7 @@ export type GenericFieldContextType = {
disableChipClick?: boolean;
onOpenEditMode?: () => void;
onCloseEditMode?: () => void;
triggerEvent?: TriggerEventType;
};
export const FieldContext = createContext<GenericFieldContextType>(

View File

@ -11,6 +11,7 @@ export const ChipFieldDisplay = () => {
isLabelIdentifierCompact,
disableChipClick,
maxWidth,
triggerEvent,
} = useChipFieldDisplay();
if (!isDefined(recordValue)) {
@ -26,6 +27,7 @@ export const ChipFieldDisplay = () => {
to={labelIdentifierLink}
isLabelHidden={isLabelIdentifierCompact}
forceDisableClick={disableChipClick}
triggerEvent={triggerEvent}
/>
);
};

View File

@ -15,7 +15,7 @@ export const RelationFromManyFieldDisplay = () => {
const { fieldValue, fieldDefinition, generateRecordChipData } =
useRelationFromManyFieldDisplay();
const { isFocused } = useFieldFocus();
const { disableChipClick } = useContext(FieldContext);
const { disableChipClick, triggerEvent } = useContext(FieldContext);
const { fieldName, objectMetadataNameSingular } = fieldDefinition.metadata;
@ -94,6 +94,7 @@ export const RelationFromManyFieldDisplay = () => {
objectNameSingular={recordChipData.objectNameSingular}
record={record}
forceDisableClick={disableChipClick}
triggerEvent={triggerEvent}
/>
);
})}

View File

@ -9,7 +9,7 @@ export const RelationToOneFieldDisplay = () => {
const { fieldValue, fieldDefinition, generateRecordChipData } =
useRelationToOneFieldDisplay();
const { disableChipClick } = useContext(FieldContext);
const { disableChipClick, triggerEvent } = useContext(FieldContext);
if (
!isDefined(fieldValue) ||
@ -31,6 +31,7 @@ export const RelationToOneFieldDisplay = () => {
forceDisableClick={
isWorkspaceMemberFieldMetadataRelation || disableChipClick
}
triggerEvent={triggerEvent}
/>
);
};

View File

@ -20,6 +20,7 @@ export const useChipFieldDisplay = () => {
isLabelIdentifierCompact,
disableChipClick,
maxWidth,
triggerEvent,
} = useContext(FieldContext);
const { chipGeneratorPerObjectPerField } = useContext(
@ -52,5 +53,6 @@ export const useChipFieldDisplay = () => {
isLabelIdentifierCompact,
disableChipClick,
maxWidth,
triggerEvent,
};
};

View File

@ -6,6 +6,7 @@ import { ReactElement } from 'react';
import { Link } from 'react-router-dom';
import { Pill } from 'twenty-ui/components';
import { Avatar, IconComponent } from 'twenty-ui/display';
import { useMouseDownNavigation } from 'twenty-ui/utilities';
type TabProps = {
id: string;
@ -87,6 +88,13 @@ export const Tab = ({
logo,
}: TabProps) => {
const theme = useTheme();
const { onClick: handleClick, onMouseDown: handleMouseDown } =
useMouseDownNavigation({
to,
onClick,
disabled,
});
const iconColor = active
? theme.font.color.primary
: disabled
@ -95,7 +103,8 @@ export const Tab = ({
return (
<StyledTab
onClick={onClick}
onClick={handleClick}
onMouseDown={handleMouseDown}
active={active}
className={className}
disabled={disabled}

View File

@ -93,11 +93,13 @@ export const TabList = ({
disabled={tab.disabled ?? loading}
pill={tab.pill}
to={behaveAsLinks ? `#${tab.id}` : undefined}
onClick={() => {
if (!behaveAsLinks) {
setActiveTabId(tab.id);
}
}}
onClick={
behaveAsLinks
? undefined
: () => {
setActiveTabId(tab.id);
}
}
/>
))}
</StyledContainer>

View File

@ -1,16 +1,17 @@
import { Meta, StoryObj } from '@storybook/react';
import { Tab } from '../Tab';
import { IconCheckbox } from 'twenty-ui/display';
import {
CatalogDecorator,
CatalogStory,
ComponentDecorator,
ComponentWithRouterDecorator,
} from 'twenty-ui/testing';
import { IconCheckbox } from 'twenty-ui/display';
import { Tab } from '../Tab';
const meta: Meta<typeof Tab> = {
title: 'UI/Layout/Tab/Tab',
component: Tab,
decorators: [ComponentWithRouterDecorator],
};
export default meta;
@ -23,8 +24,7 @@ export const Default: Story = {
Icon: IconCheckbox,
disabled: false,
},
decorators: [ComponentDecorator],
decorators: [ComponentWithRouterDecorator],
};
export const Catalog: CatalogStory<Story, typeof Tab> = {

View File

@ -11,10 +11,11 @@ import styled from '@emotion/styled';
import { ReactNode } from 'react';
import { Link } from 'react-router-dom';
import { useRecoilState } from 'recoil';
import { capitalize, isDefined } from 'twenty-shared/utils';
import { capitalize } from 'twenty-shared/utils';
import { Pill } from 'twenty-ui/components';
import { IconComponent, Label, TablerIconsProps } from 'twenty-ui/display';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
import { Pill } from 'twenty-ui/components';
import { TriggerEventType, useMouseDownNavigation } from 'twenty-ui/utilities';
const DEFAULT_INDENTATION_LEVEL = 1;
@ -37,6 +38,7 @@ export type NavigationDrawerItemProps = {
rightOptions?: ReactNode;
isDragging?: boolean;
isRightOptionsDropdownOpen?: boolean;
triggerEvent?: TriggerEventType;
};
type StyledItemProps = Pick<
@ -250,6 +252,7 @@ export const NavigationDrawerItem = ({
rightOptions,
isDragging,
isRightOptionsDropdownOpen,
triggerEvent,
}: NavigationDrawerItemProps) => {
const theme = useTheme();
const isMobile = useIsMobile();
@ -259,22 +262,26 @@ export const NavigationDrawerItem = ({
const showBreadcrumb = indentationLevel === 2;
const showStyledSpacer = !!soon || !!count || !!keyboard || !!rightOptions;
const handleItemClick = () => {
const handleMobileNavigation = () => {
if (isMobile) {
setIsNavigationDrawerExpanded(false);
}
if (isDefined(onClick)) {
onClick();
return;
}
};
const { onClick: handleClick, onMouseDown: handleMouseDown } =
useMouseDownNavigation({
to,
onClick,
onBeforeNavigation: handleMobileNavigation,
triggerEvent,
});
return (
<StyledNavigationDrawerItemContainer>
<StyledItem
className={`navigation-drawer-item ${className || ''}`}
onClick={handleItemClick}
onClick={handleClick}
onMouseDown={handleMouseDown}
active={active}
aria-selected={active}
danger={danger}

View File

@ -20,6 +20,7 @@ export const NavigationDrawerSubItem = ({
subItemState,
rightOptions,
isDragging,
triggerEvent,
}: NavigationDrawerSubItemProps) => {
return (
<NavigationDrawerItem
@ -38,6 +39,7 @@ export const NavigationDrawerSubItem = ({
keyboard={keyboard}
rightOptions={rightOptions}
isDragging={isDragging}
triggerEvent={triggerEvent}
/>
);
};

View File

@ -3,6 +3,7 @@ import { AvatarChipsCommonProps } from '@ui/components/avatar-chip/types/AvatarC
import { AvatarChipVariant } from '@ui/components/avatar-chip/types/AvatarChipsVariant.type';
import { ChipVariant } from '@ui/components/chip/Chip';
import { LinkChip, LinkChipProps } from '@ui/components/chip/LinkChip';
import { TriggerEventType } from '@ui/utilities';
export type LinkAvatarChipProps = Omit<
AvatarChipsCommonProps,
@ -10,8 +11,10 @@ export type LinkAvatarChipProps = Omit<
> & {
to: string;
onClick?: LinkChipProps['onClick'];
onMouseDown?: LinkChipProps['onMouseDown'];
variant?: AvatarChipVariant;
isLabelHidden?: boolean;
triggerEvent?: TriggerEventType;
};
export const LinkAvatarChip = ({
@ -29,6 +32,7 @@ export const LinkAvatarChip = ({
size,
variant,
isLabelHidden,
triggerEvent,
}: LinkAvatarChipProps) => (
<LinkChip
to={to}
@ -55,5 +59,6 @@ export const LinkAvatarChip = ({
}
className={className}
maxWidth={maxWidth}
triggerEvent={triggerEvent}
/>
);

View File

@ -6,6 +6,8 @@ import {
ChipSize,
ChipVariant,
} from '@ui/components/chip/Chip';
import { LINK_CHIP_CLICK_OUTSIDE_ID } from '@ui/components/chip/constants/LinkChipClickOutsideId';
import { TriggerEventType, useMouseDownNavigation } from '@ui/utilities';
import { MouseEvent } from 'react';
import { Link } from 'react-router-dom';
@ -14,7 +16,9 @@ export type LinkChipProps = Omit<
'onClick' | 'disabled' | 'clickable'
> & {
to: string;
onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;
onClick?: (event: MouseEvent<HTMLElement>) => void;
onMouseDown?: (event: MouseEvent<HTMLElement>) => void;
triggerEvent?: TriggerEventType;
};
const StyledLink = styled(Link)`
@ -34,9 +38,25 @@ export const LinkChip = ({
className,
maxWidth,
onClick,
triggerEvent,
}: LinkChipProps) => {
const { onClick: onClickHandler, onMouseDown: onMouseDownHandler } =
useMouseDownNavigation({
to: to,
onClick: onClick,
triggerEvent,
});
return (
<StyledLink to={to} onClick={onClick}>
<StyledLink
to={to}
onClick={(event) => {
event.stopPropagation();
onClickHandler(event);
}}
onMouseDown={onMouseDownHandler}
data-click-outside-id={LINK_CHIP_CLICK_OUTSIDE_ID}
>
<Chip
size={size}
label={label}

View File

@ -0,0 +1 @@
export const LINK_CHIP_CLICK_OUTSIDE_ID = 'link-chip-click-outside-id';

View File

@ -17,6 +17,7 @@ export type { AvatarChipsCommonProps } from './avatar-chip/types/AvatarChipsComm
export { AvatarChipVariant } from './avatar-chip/types/AvatarChipsVariant.type';
export type { ChipProps } from './chip/Chip';
export { ChipSize, ChipAccent, ChipVariant, Chip } from './chip/Chip';
export { LINK_CHIP_CLICK_OUTSIDE_ID } from './chip/constants/LinkChipClickOutsideId';
export type { LinkChipProps } from './chip/LinkChip';
export { LinkChip } from './chip/LinkChip';
export { Pill } from './Pill/Pill';

View File

@ -29,10 +29,6 @@ const StyledElementsCount = styled.span`
color: ${({ theme }) => theme.font.color.tertiary};
`;
const StyledEmptyState = styled.div`
color: ${({ theme }) => theme.font.color.tertiary};
`;
const StyledJsonList = styled(JsonList)``.withComponent(motion.ul);
export const JsonNestedNode = ({

View File

@ -20,7 +20,9 @@ export { getOsControlSymbol } from './device/getOsControlSymbol';
export { getOsShortcutSeparator } from './device/getOsShortcutSeparator';
export { getUserDevice } from './device/getUserDevice';
export { AutogrowWrapper } from './dimensions/components/AutogrowWrapper';
export { isModifiedEvent } from './events/isModifiedEvent';
export { useMouseDownNavigation } from './navigation/hooks/useMouseDownNavigation';
export { isNavigationModifierPressed } from './navigation/isNavigationModifierPressed';
export type { TriggerEventType } from './navigation/types/trigger-event.type';
export { useIsMobile } from './responsive/hooks/useIsMobile';
export { useScreenSize } from './screen-size/hooks/useScreenSize';
export { createState } from './state/utils/createState';

View File

@ -0,0 +1,71 @@
import { isNavigationModifierPressed } from '@ui/utilities/navigation/isNavigationModifierPressed';
import { TriggerEventType } from '@ui/utilities/navigation/types/trigger-event.type';
import { MouseEvent } from 'react';
import { useNavigate } from 'react-router-dom';
import { isDefined } from 'twenty-shared/utils';
type UseMouseDownNavigationProps = {
to?: string;
onClick?: (event: MouseEvent<HTMLElement>) => void;
disabled?: boolean;
onBeforeNavigation?: () => void;
triggerEvent?: TriggerEventType;
stopPropagation?: boolean;
};
export const useMouseDownNavigation = ({
to,
onClick,
disabled = false,
onBeforeNavigation,
triggerEvent = 'MOUSE_DOWN',
}: UseMouseDownNavigationProps) => {
const navigate = useNavigate();
const handleClick = (event: MouseEvent<HTMLElement>) => {
if (disabled) return;
// For modifier keys, let the default browser behavior handle it
if (isNavigationModifierPressed(event)) {
onBeforeNavigation?.();
if (isDefined(onClick) && !isDefined(to)) {
onClick(event);
}
// Don't prevent default for modifier keys to allow browser navigation
return;
}
if (triggerEvent === 'CLICK') {
onBeforeNavigation?.();
if (isDefined(onClick)) {
onClick(event);
} else if (isDefined(to)) {
navigate(to);
}
}
// For regular clicks, prevent default to avoid double navigation
event.preventDefault();
};
const handleMouseDown = (event: MouseEvent<HTMLElement>) => {
if (disabled || triggerEvent === 'CLICK') return;
if (isNavigationModifierPressed(event)) {
return;
}
onBeforeNavigation?.();
if (isDefined(onClick)) {
onClick(event);
} else if (isDefined(to)) {
navigate(to);
}
};
return {
onClick: handleClick,
onMouseDown: handleMouseDown,
};
};

View File

@ -3,7 +3,7 @@ type LimitedMouseEvent = Pick<
'button' | 'metaKey' | 'altKey' | 'ctrlKey' | 'shiftKey'
>;
export const isModifiedEvent = ({
export const isNavigationModifierPressed = ({
altKey,
ctrlKey,
shiftKey,

View File

@ -0,0 +1 @@
export type TriggerEventType = 'MOUSE_DOWN' | 'CLICK';