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

@ -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,
};
};