diff --git a/packages/twenty-front/src/modules/activities/calendar/components/CalendarEventDetails.tsx b/packages/twenty-front/src/modules/activities/calendar/components/CalendarEventDetails.tsx
index 641a99d68..762cc0b97 100644
--- a/packages/twenty-front/src/modules/activities/calendar/components/CalendarEventDetails.tsx
+++ b/packages/twenty-front/src/modules/activities/calendar/components/CalendarEventDetails.tsx
@@ -125,7 +125,7 @@ export const CalendarEventDetails = ({
size={ChipSize.Large}
variant={ChipVariant.Highlighted}
clickable={false}
- leftComponent={}
+ leftComponent={() => }
label="Event"
/>
diff --git a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersChip.tsx b/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersChip.tsx
index 6ef97d07b..a10b37781 100644
--- a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersChip.tsx
+++ b/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersChip.tsx
@@ -51,16 +51,20 @@ export const MessageThreadSubscribersChip = ({
- ) : (
+ leftComponent={() => {
+ if (isOnlyOneSubscriber) {
+ return (
+
+ );
+ }
+
+ return (
(
))}
/>
- )
- }
- rightComponent={}
+ );
+ }}
+ rightComponent={() => }
clickable
/>
);
diff --git a/packages/twenty-front/src/modules/activities/files/components/AttachmentRow.tsx b/packages/twenty-front/src/modules/activities/files/components/AttachmentRow.tsx
index a54d74681..404cb9403 100644
--- a/packages/twenty-front/src/modules/activities/files/components/AttachmentRow.tsx
+++ b/packages/twenty-front/src/modules/activities/files/components/AttachmentRow.tsx
@@ -16,7 +16,11 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useMemo, useState } from 'react';
import { isDefined } from 'twenty-shared';
-import { IconCalendar, OverflowingTextWithTooltip } from 'twenty-ui';
+import {
+ IconCalendar,
+ OverflowingTextWithTooltip,
+ isModifiedEvent,
+} from 'twenty-ui';
import { formatToHumanReadableDate } from '~/utils/date-utils';
import { getFileNameAndExtension } from '~/utils/file/getFileNameAndExtension';
@@ -145,7 +149,7 @@ export const AttachmentRow = ({
const handleOpenDocument = (e: React.MouseEvent) => {
// Cmd/Ctrl+click opens new tab, right click opens context menu
- if (e.metaKey || e.ctrlKey || e.button === 2) {
+ if (isModifiedEvent(e) || e.button === 2) {
return;
}
diff --git a/packages/twenty-front/src/modules/object-record/components/RecordChip.tsx b/packages/twenty-front/src/modules/object-record/components/RecordChip.tsx
index cb7acb410..bbc54d04f 100644
--- a/packages/twenty-front/src/modules/object-record/components/RecordChip.tsx
+++ b/packages/twenty-front/src/modules/object-record/components/RecordChip.tsx
@@ -1,4 +1,10 @@
-import { AvatarChip, AvatarChipVariant } from 'twenty-ui';
+import {
+ AvatarChip,
+ AvatarChipVariant,
+ ChipSize,
+ LinkAvatarChip,
+ isModifiedEvent,
+} from 'twenty-ui';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { getLinkToShowPage } from '@/object-metadata/utils/getLinkToShowPage';
@@ -6,14 +12,16 @@ import { useRecordChipData } from '@/object-record/hooks/useRecordChipData';
import { recordIndexOpenRecordInSelector } from '@/object-record/record-index/states/selectors/recordIndexOpenRecordInSelector';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
-import { MouseEvent } from 'react';
import { useRecoilValue } from 'recoil';
-
export type RecordChipProps = {
objectNameSingular: string;
record: ObjectRecord;
className?: string;
variant?: AvatarChipVariant;
+ forceDisableClick?: boolean;
+ maxWidth?: number;
+ to?: string | undefined;
+ size?: ChipSize;
};
export const RecordChip = ({
@@ -21,6 +29,10 @@ export const RecordChip = ({
record,
className,
variant,
+ maxWidth,
+ to,
+ size,
+ forceDisableClick = false,
}: RecordChipProps) => {
const { recordChipData } = useRecordChipData({
objectNameSingular,
@@ -33,30 +45,52 @@ export const RecordChip = ({
recordIndexOpenRecordInSelector,
);
- const handleClick = (e: MouseEvent) => {
- e.stopPropagation();
- if (recordIndexOpenRecordIn === ViewOpenRecordInType.SIDE_PANEL) {
- openRecordInCommandMenu({
- recordId: record.id,
- objectNameSingular,
- });
- }
- };
+ // TODO temporary until we create a record show page for Workspaces members
+ if (forceDisableClick) {
+ return (
+
+ );
+ }
+
+ const isSidePanelViewOpenRecordInType =
+ recordIndexOpenRecordIn === ViewOpenRecordInType.SIDE_PANEL;
+ const onClick = isSidePanelViewOpenRecordInType
+ ? () =>
+ openRecordInCommandMenu({
+ recordId: record.id,
+ objectNameSingular,
+ })
+ : undefined;
return (
- {
+ // TODO refactor wrapper event listener to avoid colliding events
+ clickEvent.stopPropagation();
+
+ const isModifiedEventResult = isModifiedEvent(clickEvent);
+ if (isSidePanelViewOpenRecordInType && !isModifiedEventResult) {
+ clickEvent.preventDefault();
+ onClick?.();
+ }
+ }}
/>
);
};
diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx
index 71b6764f3..3ba3df1bd 100644
--- a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx
@@ -8,8 +8,11 @@ import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/r
import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
+import { RecordBoardCardBody } from '@/object-record/record-board/record-board-card/components/RecordBoardCardBody';
+import { RecordBoardCardHeader } from '@/object-record/record-board/record-board-card/components/RecordBoardCardHeader';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { RecordValueSetterEffect } from '@/object-record/record-store/components/RecordValueSetterEffect';
+import { AppPath } from '@/types/AppPath';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { RecordBoardScrollWrapperContext } from '@/ui/utilities/scroll/contexts/ScrollWrapperContexts';
import { useRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2';
@@ -19,12 +22,10 @@ import styled from '@emotion/styled';
import { useContext, useState } from 'react';
import { InView, useInView } from 'react-intersection-observer';
import { useSetRecoilState } from 'recoil';
+import { isDefined } from 'twenty-shared';
import { AnimatedEaseInOut } from 'twenty-ui';
import { useDebouncedCallback } from 'use-debounce';
-import { RecordBoardCardBody } from '@/object-record/record-board/record-board-card/components/RecordBoardCardBody';
-import { RecordBoardCardHeader } from '@/object-record/record-board/record-board-card/components/RecordBoardCardHeader';
import { useNavigateApp } from '~/hooks/useNavigateApp';
-import { AppPath } from '@/types/AppPath';
const StyledBoardCard = styled.div<{ selected: boolean }>`
background-color: ${({ theme, selected }) =>
@@ -169,7 +170,7 @@ export const RecordBoardCard = ({
onMouseLeave={onMouseLeaveBoard}
onClick={handleCardClick}
>
- {labelIdentifierField && (
+ {isDefined(labelIdentifierField) && (
@@ -156,7 +153,7 @@ export const RecordBoardCardHeader = ({
) : isIdentifierEmpty ? (
) : (
- {
- openRecordInCommandMenu({
- recordId,
- objectNameSingular: objectMetadataItem.nameSingular,
- });
- }
- : undefined
- }
- to={
- recordIndexOpenRecordIn === ViewOpenRecordInType.RECORD_PAGE
- ? indexIdentifierUrl(recordId)
- : undefined
- }
- />
+ isDefined(record) && (
+
+ )
)}
diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/ChipFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/ChipFieldDisplay.tsx
index ac125fcd0..285c0217d 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/ChipFieldDisplay.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/ChipFieldDisplay.tsx
@@ -1,52 +1,22 @@
-import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { RecordChip } from '@/object-record/components/RecordChip';
import { useChipFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useChipFieldDisplay';
-import { RecordIdentifierChip } from '@/object-record/record-index/components/RecordIndexRecordChip';
-import { recordIndexOpenRecordInSelector } from '@/object-record/record-index/states/selectors/recordIndexOpenRecordInSelector';
-import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
-import { useRecoilValue } from 'recoil';
+import { isDefined } from 'twenty-shared';
import { ChipSize } from 'twenty-ui';
export const ChipFieldDisplay = () => {
- const {
- recordValue,
- objectNameSingular,
- isLabelIdentifier,
- labelIdentifierLink,
- } = useChipFieldDisplay();
+ const { recordValue, objectNameSingular, labelIdentifierLink } =
+ useChipFieldDisplay();
- const recordIndexOpenRecordIn = useRecoilValue(
- recordIndexOpenRecordInSelector,
- );
-
- const { openRecordInCommandMenu } = useCommandMenu();
-
- if (!recordValue) {
+ if (!isDefined(recordValue)) {
return null;
}
- return isLabelIdentifier ? (
- {
- openRecordInCommandMenu({
- recordId: recordValue.id,
- objectNameSingular,
- });
- }
- : undefined
- }
+ to={labelIdentifierLink}
/>
- ) : (
-
);
};
diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RelationToOneFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RelationToOneFieldDisplay.tsx
index dede0f879..cec3be5b1 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RelationToOneFieldDisplay.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RelationToOneFieldDisplay.tsx
@@ -1,17 +1,22 @@
+import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { RecordChip } from '@/object-record/components/RecordChip';
import { useRelationToOneFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useRelationToOneFieldDisplay';
+import { isDefined } from 'twenty-shared';
export const RelationToOneFieldDisplay = () => {
const { fieldValue, fieldDefinition, generateRecordChipData } =
useRelationToOneFieldDisplay();
if (
- !fieldValue ||
- !fieldDefinition?.metadata.relationObjectMetadataNameSingular
+ !isDefined(fieldValue) ||
+ !isDefined(fieldDefinition?.metadata.relationObjectMetadataNameSingular)
) {
return null;
}
+ const isWorkspaceMemberFieldMetadataRelation =
+ fieldDefinition.metadata.relationObjectMetadataNameSingular ===
+ CoreObjectNameSingular.WorkspaceMember;
const recordChipData = generateRecordChipData(fieldValue);
return (
@@ -19,6 +24,7 @@ export const RelationToOneFieldDisplay = () => {
key={recordChipData.recordId}
objectNameSingular={recordChipData.objectNameSingular}
record={fieldValue}
+ forceDisableClick={isWorkspaceMemberFieldMetadataRelation}
/>
);
};
diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexRecordChip.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexRecordChip.tsx
deleted file mode 100644
index a0b749e7f..000000000
--- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexRecordChip.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { useGetStandardObjectIcon } from '@/object-metadata/hooks/useGetStandardObjectIcon';
-import { useRecordChipData } from '@/object-record/hooks/useRecordChipData';
-import { ObjectRecord } from '@/object-record/types/ObjectRecord';
-import { isNonEmptyString } from '@sniptt/guards';
-import { AvatarChip, AvatarChipVariant, ChipSize } from 'twenty-ui';
-
-export type RecordIdentifierChipProps = {
- objectNameSingular: string;
- record: ObjectRecord;
- variant?: AvatarChipVariant;
- size?: ChipSize;
- to?: string;
- maxWidth?: number;
- onClick?: () => void;
-};
-
-export const RecordIdentifierChip = ({
- objectNameSingular,
- record,
- variant,
- size,
- onClick,
- to,
- maxWidth,
-}: RecordIdentifierChipProps) => {
- const { recordChipData } = useRecordChipData({
- objectNameSingular,
- record,
- });
-
- const { Icon: LeftIcon, IconColor: LeftIconColor } =
- useGetStandardObjectIcon(objectNameSingular);
-
- if (!isNonEmptyString(recordChipData.name.trim())) {
- return null;
- }
-
- return (
-
- );
-};
diff --git a/packages/twenty-front/src/modules/ui/field/display/components/ActorDisplay.tsx b/packages/twenty-front/src/modules/ui/field/display/components/ActorDisplay.tsx
index e758e7174..88cbe2fc1 100644
--- a/packages/twenty-front/src/modules/ui/field/display/components/ActorDisplay.tsx
+++ b/packages/twenty-front/src/modules/ui/field/display/components/ActorDisplay.tsx
@@ -4,7 +4,6 @@ import { ConnectedAccountProvider } from 'twenty-shared';
import { useMemo } from 'react';
import {
AvatarChip,
- AvatarChipVariant,
IconApi,
IconCalendar,
IconCsv,
@@ -71,7 +70,6 @@ export const ActorDisplay = ({
LeftIcon={LeftIcon}
avatarUrl={avatarUrl ?? undefined}
isIconInverted={isIconInverted}
- variant={AvatarChipVariant.Transparent}
/>
);
};
diff --git a/packages/twenty-front/src/modules/ui/layout/expandable-list/components/ExpandableList.tsx b/packages/twenty-front/src/modules/ui/layout/expandable-list/components/ExpandableList.tsx
index 83e3714f3..c7a60019f 100644
--- a/packages/twenty-front/src/modules/ui/layout/expandable-list/components/ExpandableList.tsx
+++ b/packages/twenty-front/src/modules/ui/layout/expandable-list/components/ExpandableList.tsx
@@ -1,6 +1,10 @@
import styled from '@emotion/styled';
import { ReactElement, useCallback, useEffect, useRef, useState } from 'react';
-import { AnimatedContainer, Chip, ChipVariant } from 'twenty-ui';
+import {
+ AnimatedContainer,
+ ChipSize,
+ OverflowingTextWithTooltip,
+} from 'twenty-ui';
import { ExpandedListDropdown } from '@/ui/layout/expandable-list/components/ExpandedListDropdown';
import { isFirstOverflowingChildElement } from '@/ui/layout/expandable-list/utils/isFirstOverflowingChildElement';
@@ -34,7 +38,7 @@ const StyledChildContainer = styled.div`
}
`;
-const StyledChipCount = styled(Chip)`
+const StyledUnShrinkableContainer = styled.div`
flex-shrink: 0;
`;
@@ -150,11 +154,12 @@ export const ExpandableList = ({
{canDisplayChipCount && (
-
+
+
+
)}
{isListExpanded && (
diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerTopBar.tsx b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerTopBar.tsx
index 1283073a3..6f0d07113 100644
--- a/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerTopBar.tsx
+++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawerTopBar.tsx
@@ -102,7 +102,7 @@ export const RightDrawerTopBar = () => {
}
+ leftComponent={() => }
size={ChipSize.Large}
accent={ChipAccent.TextSecondary}
clickable={false}
diff --git a/packages/twenty-ui/src/display/avatar-chip/components/AvatarChip.tsx b/packages/twenty-ui/src/display/avatar-chip/components/AvatarChip.tsx
new file mode 100644
index 000000000..5f9086bfd
--- /dev/null
+++ b/packages/twenty-ui/src/display/avatar-chip/components/AvatarChip.tsx
@@ -0,0 +1,37 @@
+import { AvatarChipsLeftComponent } from '@ui/display/avatar-chip/components/AvatarChipLeftComponent';
+import { AvatarChipsCommonProps } from '@ui/display/avatar-chip/types/AvatarChipsCommonProps.type';
+import { Chip, ChipVariant } from '@ui/display/chip/components/Chip';
+
+export type AvatarChipProps = AvatarChipsCommonProps;
+export const AvatarChip = ({
+ name,
+ LeftIcon,
+ LeftIconColor,
+ avatarType,
+ avatarUrl,
+ className,
+ isIconInverted,
+ maxWidth,
+ placeholderColorSeed,
+ size,
+}: AvatarChipProps) => (
+ (
+
+ )}
+ clickable={false}
+ className={className}
+ maxWidth={maxWidth}
+ />
+);
diff --git a/packages/twenty-ui/src/display/avatar-chip/components/AvatarChipLeftComponent.tsx b/packages/twenty-ui/src/display/avatar-chip/components/AvatarChipLeftComponent.tsx
new file mode 100644
index 000000000..8c12fc523
--- /dev/null
+++ b/packages/twenty-ui/src/display/avatar-chip/components/AvatarChipLeftComponent.tsx
@@ -0,0 +1,74 @@
+import { useTheme } from '@emotion/react';
+import styled from '@emotion/styled';
+import { Avatar } from '@ui/display/avatar/components/Avatar';
+import { AvatarType } from '@ui/display/avatar/types/AvatarType';
+import { IconComponent } from '@ui/display/icon/types/IconComponent';
+import { isDefined } from 'twenty-shared';
+import { Nullable } from 'vitest';
+
+const StyledInvertedIconContainer = styled.div<{ backgroundColor: string }>`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 14px;
+ height: 14px;
+ border-radius: 4px;
+ background-color: ${({ backgroundColor }) => backgroundColor};
+`;
+
+export type AvatarChipsLeftComponentProps = {
+ name: string;
+ avatarUrl?: string;
+ avatarType?: Nullable;
+ LeftIcon?: IconComponent;
+ LeftIconColor?: string;
+ isIconInverted?: boolean;
+ placeholderColorSeed?: string;
+};
+
+export const AvatarChipsLeftComponent: React.FC<
+ AvatarChipsLeftComponentProps
+> = ({
+ LeftIcon,
+ placeholderColorSeed,
+ avatarType,
+ avatarUrl,
+ name,
+ isIconInverted = false,
+ LeftIconColor,
+}) => {
+ const theme = useTheme();
+ if (!isDefined(LeftIcon)) {
+ return (
+
+ );
+ }
+
+ if (isIconInverted) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+ );
+};
diff --git a/packages/twenty-ui/src/display/avatar-chip/components/LinkAvatarChip.tsx b/packages/twenty-ui/src/display/avatar-chip/components/LinkAvatarChip.tsx
new file mode 100644
index 000000000..2a7edc484
--- /dev/null
+++ b/packages/twenty-ui/src/display/avatar-chip/components/LinkAvatarChip.tsx
@@ -0,0 +1,53 @@
+import { AvatarChipsLeftComponent } from '@ui/display/avatar-chip/components/AvatarChipLeftComponent';
+import { AvatarChipsCommonProps } from '@ui/display/avatar-chip/types/AvatarChipsCommonProps.type';
+import { AvatarChipVariant } from '@ui/display/avatar-chip/types/AvatarChipsVariant.type';
+import { ChipVariant } from '@ui/display/chip/components/Chip';
+import { LinkChip, LinkChipProps } from '@ui/display/chip/components/LinkChip';
+
+export type LinkAvatarChipProps = Omit & {
+ to: string;
+ onClick?: LinkChipProps['onClick'];
+ variant?: AvatarChipVariant;
+};
+
+export const LinkAvatarChip = ({
+ to,
+ onClick,
+ name,
+ LeftIcon,
+ LeftIconColor,
+ avatarType,
+ avatarUrl,
+ className,
+ isIconInverted,
+ maxWidth,
+ placeholderColorSeed,
+ size,
+ variant,
+}: LinkAvatarChipProps) => (
+ missleading
+ variant === AvatarChipVariant.Regular
+ ? ChipVariant.Highlighted
+ : ChipVariant.Regular
+ }
+ size={size}
+ leftComponent={() => (
+
+ )}
+ className={className}
+ maxWidth={maxWidth}
+ />
+);
diff --git a/packages/twenty-ui/src/display/avatar-chip/types/AvatarChipsCommonProps.type.ts b/packages/twenty-ui/src/display/avatar-chip/types/AvatarChipsCommonProps.type.ts
new file mode 100644
index 000000000..dc7528896
--- /dev/null
+++ b/packages/twenty-ui/src/display/avatar-chip/types/AvatarChipsCommonProps.type.ts
@@ -0,0 +1,8 @@
+import { AvatarChipsLeftComponentProps } from '@ui/display/avatar-chip/components/AvatarChipLeftComponent';
+import { ChipSize } from '@ui/display/chip/components/Chip';
+
+export type AvatarChipsCommonProps = {
+ size?: ChipSize;
+ className?: string;
+ maxWidth?: number;
+} & AvatarChipsLeftComponentProps;
diff --git a/packages/twenty-ui/src/display/avatar-chip/types/AvatarChipsVariant.type.ts b/packages/twenty-ui/src/display/avatar-chip/types/AvatarChipsVariant.type.ts
new file mode 100644
index 000000000..d7ff78f72
--- /dev/null
+++ b/packages/twenty-ui/src/display/avatar-chip/types/AvatarChipsVariant.type.ts
@@ -0,0 +1,4 @@
+export enum AvatarChipVariant {
+ Regular = 'regular',
+ Transparent = 'transparent',
+}
diff --git a/packages/twenty-ui/src/display/chip/components/AvatarChip.tsx b/packages/twenty-ui/src/display/chip/components/AvatarChip.tsx
deleted file mode 100644
index eeb251187..000000000
--- a/packages/twenty-ui/src/display/chip/components/AvatarChip.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-import { styled } from '@linaria/react';
-import { Avatar } from '@ui/display/avatar/components/Avatar';
-import { AvatarType } from '@ui/display/avatar/types/AvatarType';
-import { Chip, ChipSize, ChipVariant } from '@ui/display/chip/components/Chip';
-import { IconComponent } from '@ui/display/icon/types/IconComponent';
-import { ThemeContext } from '@ui/theme';
-import { Nullable } from '@ui/utilities/types/Nullable';
-import { MouseEvent, useContext } from 'react';
-import { isDefined } from 'twenty-shared';
-
-// Import Link from react-router-dom instead of UndecoratedLink
-import { Link } from 'react-router-dom';
-
-export type AvatarChipProps = {
- name: string;
- avatarUrl?: string;
- avatarType?: Nullable;
- variant?: AvatarChipVariant;
- size?: ChipSize;
- LeftIcon?: IconComponent;
- LeftIconColor?: string;
- isIconInverted?: boolean;
- className?: string;
- placeholderColorSeed?: string;
- onClick?: (event: MouseEvent) => void;
- to?: string;
- maxWidth?: number;
-};
-
-export enum AvatarChipVariant {
- Regular = 'regular',
- Transparent = 'transparent',
-}
-
-const StyledInvertedIconContainer = styled.div<{ backgroundColor: string }>`
- display: flex;
- align-items: center;
- justify-content: center;
- width: 14px;
- height: 14px;
- border-radius: 4px;
- background-color: ${({ backgroundColor }) => backgroundColor};
-`;
-
-// Ideally we would use the UndecoratedLink component from @ui/navigation
-// but it led to a bug probably linked to circular dependencies, which was hard to solve
-const StyledLink = styled(Link)`
- text-decoration: none;
-`;
-
-export const AvatarChip = ({
- name,
- avatarUrl,
- avatarType = 'rounded',
- variant = AvatarChipVariant.Regular,
- LeftIcon,
- LeftIconColor,
- isIconInverted,
- className,
- placeholderColorSeed,
- onClick,
- to,
- size = ChipSize.Small,
- maxWidth,
-}: AvatarChipProps) => {
- const { theme } = useContext(ThemeContext);
-
- const chip = (
-
-
-
- ) : (
-
- )
- ) : (
-
- )
- }
- clickable={isDefined(onClick) || isDefined(to)}
- onClick={to ? undefined : onClick}
- className={className}
- maxWidth={maxWidth}
- />
- );
-
- if (!isDefined(to)) return chip;
- return (
-
- {chip}
-
- );
-};
diff --git a/packages/twenty-ui/src/display/chip/components/Chip.tsx b/packages/twenty-ui/src/display/chip/components/Chip.tsx
index 8807d25dc..29d0516df 100644
--- a/packages/twenty-ui/src/display/chip/components/Chip.tsx
+++ b/packages/twenty-ui/src/display/chip/components/Chip.tsx
@@ -1,6 +1,6 @@
import { Theme, withTheme } from '@emotion/react';
import { styled } from '@linaria/react';
-import { MouseEvent, ReactNode } from 'react';
+import { ReactNode } from 'react';
import { OverflowingTextWithTooltip } from '@ui/display/tooltip/OverflowingTextWithTooltip';
@@ -21,7 +21,7 @@ export enum ChipVariant {
Rounded = 'rounded',
}
-type ChipProps = {
+export type ChipProps = {
size?: ChipSize;
disabled?: boolean;
clickable?: boolean;
@@ -29,10 +29,9 @@ type ChipProps = {
maxWidth?: number;
variant?: ChipVariant;
accent?: ChipAccent;
- leftComponent?: ReactNode;
- rightComponent?: ReactNode;
+ leftComponent?: (() => ReactNode) | null;
+ rightComponent?: (() => ReactNode) | null;
className?: string;
- onClick?: (event: MouseEvent) => void;
};
const StyledContainer = withTheme(styled.div<
@@ -128,10 +127,9 @@ export const Chip = ({
disabled = false,
clickable = true,
variant = ChipVariant.Regular,
- leftComponent,
- rightComponent,
+ leftComponent = null,
+ rightComponent = null,
accent = ChipAccent.TextPrimary,
- onClick,
className,
maxWidth,
}: ChipProps) => {
@@ -143,13 +141,12 @@ export const Chip = ({
disabled={disabled}
size={size}
variant={variant}
- onClick={onClick}
className={className}
maxWidth={maxWidth}
>
- {leftComponent}
+ {leftComponent?.()}
- {rightComponent}
+ {rightComponent?.()}
);
};
diff --git a/packages/twenty-ui/src/display/chip/components/LinkChip.tsx b/packages/twenty-ui/src/display/chip/components/LinkChip.tsx
new file mode 100644
index 000000000..897c1adb1
--- /dev/null
+++ b/packages/twenty-ui/src/display/chip/components/LinkChip.tsx
@@ -0,0 +1,53 @@
+import styled from '@emotion/styled';
+import {
+ Chip,
+ ChipAccent,
+ ChipProps,
+ ChipSize,
+ ChipVariant,
+} from '@ui/display/chip/components/Chip';
+import { MouseEvent } from 'react';
+import { Link } from 'react-router-dom';
+
+export type LinkChipProps = Omit<
+ ChipProps,
+ 'onClick' | 'disabled' | 'clickable'
+> & {
+ to: string;
+ onClick?: (event: MouseEvent) => void;
+};
+
+// Ideally we would use the UndecoratedLink component from @ui/navigation
+// but it led to a bug probably linked to circular dependencies, which was hard to solve
+const StyledLink = styled(Link)`
+ text-decoration: none;
+`;
+
+export const LinkChip = ({
+ to,
+ size = ChipSize.Small,
+ label,
+ variant = ChipVariant.Regular,
+ leftComponent = null,
+ rightComponent = null,
+ accent = ChipAccent.TextPrimary,
+ className,
+ maxWidth,
+ onClick,
+}: LinkChipProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/packages/twenty-ui/src/display/chip/components/__stories__/EntityChip.stories.tsx b/packages/twenty-ui/src/display/chip/components/__stories__/EntityChip.stories.tsx
index 2682eec04..b70d6df46 100644
--- a/packages/twenty-ui/src/display/chip/components/__stories__/EntityChip.stories.tsx
+++ b/packages/twenty-ui/src/display/chip/components/__stories__/EntityChip.stories.tsx
@@ -1,5 +1,5 @@
import { Meta, StoryObj } from '@storybook/react';
-import { AvatarChip } from '@ui/display/chip/components/AvatarChip';
+import { AvatarChip } from '@ui/display/avatar-chip/components/AvatarChip';
import { ComponentDecorator, RouterDecorator } from '@ui/testing';
diff --git a/packages/twenty-ui/src/display/index.ts b/packages/twenty-ui/src/display/index.ts
index b8dca47f3..f3c95251a 100644
--- a/packages/twenty-ui/src/display/index.ts
+++ b/packages/twenty-ui/src/display/index.ts
@@ -1,3 +1,8 @@
+export * from './avatar-chip/components/AvatarChip';
+export * from './avatar-chip/components/AvatarChipLeftComponent';
+export * from './avatar-chip/components/LinkAvatarChip';
+export * from './avatar-chip/types/AvatarChipsCommonProps.type';
+export * from './avatar-chip/types/AvatarChipsVariant.type';
export * from './avatar/components/Avatar';
export * from './avatar/components/AvatarGroup';
export * from './avatar/components/states/isInvalidAvatarUrlState';
@@ -7,8 +12,8 @@ export * from './avatar/types/AvatarType';
export * from './banner/components/Banner';
export * from './checkmark/components/AnimatedCheckmark';
export * from './checkmark/components/Checkmark';
-export * from './chip/components/AvatarChip';
export * from './chip/components/Chip';
+export * from './chip/components/LinkChip';
export * from './color/components/ColorSample';
export * from './icon/components/IconAddressBook';
export * from './icon/components/IconGmail';
diff --git a/packages/twenty-ui/src/utilities/events/isModifiedEvent.ts b/packages/twenty-ui/src/utilities/events/isModifiedEvent.ts
new file mode 100644
index 000000000..18600b84e
--- /dev/null
+++ b/packages/twenty-ui/src/utilities/events/isModifiedEvent.ts
@@ -0,0 +1,16 @@
+type LimitedMouseEvent = Pick<
+ MouseEvent,
+ 'button' | 'metaKey' | 'altKey' | 'ctrlKey' | 'shiftKey'
+>;
+
+export const isModifiedEvent = ({
+ altKey,
+ ctrlKey,
+ shiftKey,
+ metaKey,
+ button,
+}: LimitedMouseEvent) => {
+ const pressedKey = [altKey, ctrlKey, shiftKey, metaKey].some((key) => key);
+ const isLeftClick = button === 0;
+ return pressedKey || !isLeftClick;
+};
diff --git a/packages/twenty-ui/src/utilities/index.ts b/packages/twenty-ui/src/utilities/index.ts
index d9ed28a8d..79fca2a78 100644
--- a/packages/twenty-ui/src/utilities/index.ts
+++ b/packages/twenty-ui/src/utilities/index.ts
@@ -10,6 +10,7 @@ export * from './device/getOsControlSymbol';
export * from './device/getOsShortcutSeparator';
export * from './device/getUserDevice';
export * from './dimensions/components/AutogrowWrapper';
+export * from './events/isModifiedEvent';
export * from './responsive/hooks/useIsMobile';
export * from './screen-size/hooks/useScreenSize';
export * from './state/utils/createState';