From 34a903b4fad17fdabc3a9df0679446856be999ce Mon Sep 17 00:00:00 2001 From: Abdul Rahman <81605929+abdulrahmancodes@users.noreply.github.com> Date: Fri, 10 Apr 2026 15:44:20 +0530 Subject: [PATCH] Object icon visual parity (#19374) ## Summary Aligns **object metadata** icons with the **tinted tile** look everywhere we show a workspace object, and **retires** the navigation-only `NavigationMenuItemStyleIcon` wrapper in favor of **shared** UI primitives under `@/ui/display` and `@/object-metadata`. ## What changed ### Global tinted icon building blocks (`@/ui/display`) - **`TintedIconTile`** / **`StyledTintedIconTileContainer`** support optional **`size`** and **`stroke`**, and grow the tile when **`size`** is set so layouts match previous `theme.icon` usage. - Shared helpers and constants for theme color parsing and tinted backgrounds/borders/icon color (e.g. **`getTintedIconTileStyleFromColor`**, **`parseThemeColor`**, **`getColorFromTheme`**, related constants). ### Object metadata icon (`@/object-metadata`) - **`ObjectMetadataIcon`** composes **`TintedIconTile`** with **`getObjectColorWithFallback`**, forwards optional **`size`** / **`stroke`** for **visual parity** with old `getIcon` + explicit sizing. - **`getSelectOptionIconFromObjectMetadataItem`** returns an **`IconComponent`** for selects/menus that expect a component, not a React node. ### Navigation module cleanup - **Removed** **`NavigationMenuItemStyleIcon`**; call sites use **`ObjectMetadataIcon`**, **`TintedIconTile`**, and/or the shared **`getTintedIconTileStyleFromColor`** pipeline so the same treatment is **not** tied to the navigation package. - **`NavigationMenuItemIcon`**, view/link overlays, DnD handle, and sidebar editor flows updated to use the shared pattern where they render object (or tinted) icons. ### Product surfaces updated (non-exhaustive) - **Settings:** role object picker/rows, data model tables/graph/overview, object preview summary, webhooks entity list, morph relation multiselect. - **Workflows:** create/update/delete/upsert/find records, triggers, filters, variables dropdowns, AI agent object rows, object dropdowns. - **Shell:** side panel object filter / data sources / folder chrome where object icons appear. - **Records:** index header icon, show breadcrumb styling. - **Activity:** timeline event icon when linked object metadata applies. - **`NavigationDrawerItem`:** tinted branch aligned with shared **`TintedIconTile`** behavior. --------- Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com> --- .../components/EventIconDynamicComponent.tsx | 7 +- ...gationMenuItemDefaultIconColor.constant.ts | 3 - .../utils/getNavigationMenuItemColor.ts | 9 +- ...getNavigationMenuItemIconStyleFromColor.ts | 17 ---- ...igationMenuItemIconColorShadeBackground.ts | 1 - .../navigationMenuItemIconColorShadeBorder.ts | 1 - .../navigationMenuItemIconColorShadeIcon.ts | 1 - .../utils/navigationMenuItemIconStyle.ts | 5 -- .../common/utils/parseThemeColor.ts | 11 --- .../components/NavigationMenuItemIcon.tsx | 27 +++--- .../NavigationMenuItemStyleIcon.tsx | 30 ------- .../components/AddToNavigationDragHandle.tsx | 11 ++- .../components/LinkIconWithLinkOverlay.tsx | 5 +- .../components/ObjectIconWithViewOverlay.tsx | 6 +- .../SidePanelEditFolderPickerSubPage.tsx | 10 +-- .../SidePanelEditObjectViewBase.tsx | 2 +- .../SidePanelNavigationMenuItemEditPage.tsx | 2 +- .../SidePanelNewSidebarItemMainMenu.tsx | 17 ++-- ...lNewSidebarItemViewObjectPickerSubView.tsx | 13 +-- ...dePanelNewSidebarItemViewSystemSubView.tsx | 10 +-- .../components/SidePanelObjectMenuItem.tsx | 8 +- .../components/SidePanelObjectPickerItem.tsx | 11 +-- .../SidePanelObjectPickerSubView.tsx | 7 +- .../components/ObjectMetadataIcon.tsx | 32 +++++++ .../hooks/useObjectMetadataSelectHelpers.ts | 23 +++++ .../utils/getObjectColorWithFallback.ts | 13 ++- .../components/RecordIndexPageHeaderIcon.tsx | 13 +-- .../ObjectRecordShowPageBreadcrumb.tsx | 24 +++--- .../record-show/hooks/useRecordShowPage.ts | 4 - .../SettingsMorphRelationMultiSelect.tsx | 9 +- .../SettingsDataModelOverviewField.tsx | 20 +++-- .../SettingsDataModelOverviewObject.tsx | 17 ++-- ...ngsAvailableStandardObjectItemTableRow.tsx | 12 +-- .../components/SettingsObjectItemTableRow.tsx | 26 ++---- ...SettingsUpdateDataModelObjectAboutForm.tsx | 2 +- .../SettingsDataModelObjectSummary.tsx | 32 ++++--- .../SettingsDataModelObjectAboutForm.tsx | 2 +- .../components/WebhookEntitySelect.tsx | 13 +-- ...RolePermissionsObjectLevelObjectPicker.tsx | 84 +++++++++---------- ...ingsRolePermissionsObjectLevelTableRow.tsx | 25 ++---- .../components/SidePanelFolderInfo.tsx | 15 ++-- .../SidePanelObjectFilterDropdownContent.tsx | 18 ++-- .../ChartDataSourceDropdownContent.tsx | 15 ++-- .../RecordTableDataSourceDropdownContent.tsx | 9 +- .../input/components/MultiSelectControl.tsx | 24 ++++-- .../modules/ui/input/components/Select.tsx | 2 + .../ui/input/components/SelectControl.tsx | 27 ++++-- .../components/NavigationDrawerItem.tsx | 4 +- .../WorkflowDropdownStepOutputItems.tsx | 14 ++-- .../WorkflowStepFilterFieldSelect.tsx | 15 +++- .../WorkflowAiAgentPermissionsObjectRow.tsx | 18 ++-- .../WorkflowAiAgentPermissionsObjectsList.tsx | 12 +-- .../WorkflowEditActionCreateRecord.tsx | 11 +-- .../WorkflowEditActionDeleteRecord.tsx | 11 +-- .../WorkflowEditActionUpdateRecord.tsx | 9 +- .../WorkflowEditActionUpsertRecord.tsx | 9 +- .../WorkflowEditActionFindRecords.tsx | 15 ++-- .../WorkflowObjectDropdownContent.tsx | 22 ++--- .../WorkflowFormFieldSettingsRecordPicker.tsx | 10 +-- .../WorkflowEditTriggerDatabaseEventForm.tsx | 10 ++- .../components/WorkflowEditTriggerManual.tsx | 7 +- .../WorkflowVariablesDropdownStepItems.tsx | 18 ++-- .../ai/components/SettingsToolIcon.tsx | 8 +- packages/twenty-ui/src/display/index.ts | 5 ++ .../src/display/status/components/Status.tsx | 8 +- .../StyledTintedIconTileContainer.tsx} | 9 +- .../components/TintedIconTile.tsx | 39 +++++++++ .../tintedIconTileColorShades.constant.ts | 5 ++ .../internal}/utils/getColorFromTheme.ts | 4 +- .../utils/getIconTileColorShades.ts | 29 +++++++ .../twenty-ui/src/input/types/SelectOption.ts | 1 + .../menu/menu-item/components/MenuItem.tsx | 4 + .../components/MenuItemMultiSelect.tsx | 5 +- .../menu-item/components/MenuItemSelect.tsx | 6 +- .../internals/components/MenuItemIcon.tsx | 17 +++- .../components/MenuItemIconWithGripSwap.tsx | 23 ++++- .../components/MenuItemLeftContent.tsx | 5 ++ .../constants/DefaultThemeColorFallback.ts | 3 + packages/twenty-ui/src/theme/index.ts | 1 + .../utilities/color/utils/parseThemeColor.ts | 10 +++ packages/twenty-ui/src/utilities/index.ts | 1 + 81 files changed, 587 insertions(+), 456 deletions(-) delete mode 100644 packages/twenty-front/src/modules/navigation-menu-item/common/constants/NavigationMenuItemDefaultIconColor.constant.ts delete mode 100644 packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemIconStyleFromColor.ts delete mode 100644 packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeBackground.ts delete mode 100644 packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeBorder.ts delete mode 100644 packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeIcon.ts delete mode 100644 packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconStyle.ts delete mode 100644 packages/twenty-front/src/modules/navigation-menu-item/common/utils/parseThemeColor.ts delete mode 100644 packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemStyleIcon.tsx create mode 100644 packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataIcon.tsx create mode 100644 packages/twenty-front/src/modules/object-metadata/hooks/useObjectMetadataSelectHelpers.ts rename packages/{twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemIconContainer.tsx => twenty-ui/src/display/tinted-icon-tile/components/StyledTintedIconTileContainer.tsx} (58%) create mode 100644 packages/twenty-ui/src/display/tinted-icon-tile/components/TintedIconTile.tsx create mode 100644 packages/twenty-ui/src/display/tinted-icon-tile/internal/constants/tintedIconTileColorShades.constant.ts rename packages/{twenty-front/src/modules/navigation-menu-item/common => twenty-ui/src/display/tinted-icon-tile/internal}/utils/getColorFromTheme.ts (75%) create mode 100644 packages/twenty-ui/src/display/tinted-icon-tile/utils/getIconTileColorShades.ts create mode 100644 packages/twenty-ui/src/theme/constants/DefaultThemeColorFallback.ts create mode 100644 packages/twenty-ui/src/utilities/color/utils/parseThemeColor.ts diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/rows/components/EventIconDynamicComponent.tsx b/packages/twenty-front/src/modules/activities/timeline-activities/rows/components/EventIconDynamicComponent.tsx index c28e0f8822..ba60995bdd 100644 --- a/packages/twenty-front/src/modules/activities/timeline-activities/rows/components/EventIconDynamicComponent.tsx +++ b/packages/twenty-front/src/modules/activities/timeline-activities/rows/components/EventIconDynamicComponent.tsx @@ -1,11 +1,11 @@ import { type TimelineActivity } from '@/activities/timeline-activities/types/TimelineActivity'; +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { IconCirclePlus, IconEditCircle, IconRestore, IconTrash, - useIcons, } from 'twenty-ui/display'; export const EventIconDynamicComponent = ({ @@ -15,7 +15,6 @@ export const EventIconDynamicComponent = ({ event: TimelineActivity; linkedObjectMetadataItem: EnrichedObjectMetadataItem | null; }) => { - const { getIcon } = useIcons(); const [, eventAction] = event.name.split('.'); if (eventAction === 'created') { @@ -31,7 +30,5 @@ export const EventIconDynamicComponent = ({ return ; } - const IconComponent = getIcon(linkedObjectMetadataItem?.icon); - - return ; + return ; }; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/constants/NavigationMenuItemDefaultIconColor.constant.ts b/packages/twenty-front/src/modules/navigation-menu-item/common/constants/NavigationMenuItemDefaultIconColor.constant.ts deleted file mode 100644 index d420bcb653..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/common/constants/NavigationMenuItemDefaultIconColor.constant.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { ThemeColor } from 'twenty-ui/theme'; - -export const DEFAULT_NAV_ITEM_ICON_COLOR: ThemeColor = 'gray'; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemColor.ts b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemColor.ts index 0000178d40..3430671326 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemColor.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemColor.ts @@ -2,11 +2,10 @@ import { isNonEmptyString } from '@sniptt/guards'; import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER } from '@/navigation-menu-item/common/constants/NavigationMenuItemDefaultColorFolder'; import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK } from '@/navigation-menu-item/common/constants/NavigationMenuItemDefaultColorLink'; -import { DEFAULT_NAV_ITEM_ICON_COLOR } from '@/navigation-menu-item/common/constants/NavigationMenuItemDefaultIconColor.constant'; -import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; +import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; import { NavigationMenuItemType } from 'twenty-shared/types'; -import { type ThemeColor } from 'twenty-ui/theme'; +import { DEFAULT_THEME_COLOR_FALLBACK, type ThemeColor } from 'twenty-ui/theme'; export const getNavigationMenuItemColor = ( navigationMenuItem: { @@ -35,8 +34,8 @@ export const getNavigationMenuItemColor = ( if (objectMetadataItem) { return getObjectColorWithFallback(objectMetadataItem); } - return DEFAULT_NAV_ITEM_ICON_COLOR; + return DEFAULT_THEME_COLOR_FALLBACK; } - return DEFAULT_NAV_ITEM_ICON_COLOR; + return DEFAULT_THEME_COLOR_FALLBACK; }; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemIconStyleFromColor.ts b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemIconStyleFromColor.ts deleted file mode 100644 index e775304c2e..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemIconStyleFromColor.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { getColorFromTheme } from '@/navigation-menu-item/common/utils/getColorFromTheme'; -import { COLOR_SHADE_BACKGROUND } from '@/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeBackground'; -import { COLOR_SHADE_BORDER } from '@/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeBorder'; -import { COLOR_SHADE_ICON } from '@/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeIcon'; -import { type NavigationMenuItemIconStyle } from '@/navigation-menu-item/common/utils/navigationMenuItemIconStyle'; -import { parseThemeColor } from '@/navigation-menu-item/common/utils/parseThemeColor'; - -export const getNavigationMenuItemIconStyleFromColor = ( - color: string | null | undefined, -): NavigationMenuItemIconStyle => { - const themeColor = parseThemeColor(color); - return { - backgroundColor: getColorFromTheme(themeColor, COLOR_SHADE_BACKGROUND), - iconColor: getColorFromTheme(themeColor, COLOR_SHADE_ICON), - borderColor: getColorFromTheme(themeColor, COLOR_SHADE_BORDER), - }; -}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeBackground.ts b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeBackground.ts deleted file mode 100644 index 13bf6ae5a1..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeBackground.ts +++ /dev/null @@ -1 +0,0 @@ -export const COLOR_SHADE_BACKGROUND = 5; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeBorder.ts b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeBorder.ts deleted file mode 100644 index 8167e2df31..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeBorder.ts +++ /dev/null @@ -1 +0,0 @@ -export const COLOR_SHADE_BORDER = 6; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeIcon.ts b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeIcon.ts deleted file mode 100644 index fabc31d4e9..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeIcon.ts +++ /dev/null @@ -1 +0,0 @@ -export const COLOR_SHADE_ICON = 11; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconStyle.ts b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconStyle.ts deleted file mode 100644 index 5fbf0b7ea8..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/navigationMenuItemIconStyle.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type NavigationMenuItemIconStyle = { - backgroundColor: string; - iconColor: string; - borderColor: string; -}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/parseThemeColor.ts b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/parseThemeColor.ts deleted file mode 100644 index 847a242720..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/parseThemeColor.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { type ThemeColor } from 'twenty-ui/theme'; -import { themeColorSchema } from 'twenty-ui/utilities'; - -import { DEFAULT_NAV_ITEM_ICON_COLOR } from '@/navigation-menu-item/common/constants/NavigationMenuItemDefaultIconColor.constant'; - -export const parseThemeColor = ( - color: string | null | undefined, -): ThemeColor => { - const result = themeColorSchema.safeParse(color ?? ''); - return result.success ? result.data : DEFAULT_NAV_ITEM_ICON_COLOR; -}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemIcon.tsx b/packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemIcon.tsx index 95b257b555..831ad46e56 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemIcon.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemIcon.tsx @@ -1,18 +1,23 @@ import { NavigationMenuItemType } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; -import { Avatar, IconLink, IconWorld, useIcons } from 'twenty-ui/display'; +import { + Avatar, + IconLink, + IconWorld, + StyledTintedIconTileContainer, + getIconTileColorShades, + useIcons, +} from 'twenty-ui/display'; import { themeCssVariables } from 'twenty-ui/theme-constants'; import { type NavigationMenuItem } from '~/generated-metadata/graphql'; -import { LinkIconWithLinkOverlay } from '@/navigation-menu-item/display/link/components/LinkIconWithLinkOverlay'; -import { StyledNavigationMenuItemIconContainer } from '@/navigation-menu-item/display/components/NavigationMenuItemIconContainer'; -import { ObjectIconWithViewOverlay } from '@/navigation-menu-item/display/view/components/ObjectIconWithViewOverlay'; import { getNavigationMenuItemColor } from '@/navigation-menu-item/common/utils/getNavigationMenuItemColor'; -import { getNavigationMenuItemComputedLink } from '@/navigation-menu-item/display/utils/getNavigationMenuItemComputedLink'; -import { getNavigationMenuItemIconStyleFromColor } from '@/navigation-menu-item/common/utils/getNavigationMenuItemIconStyleFromColor'; -import { getNavigationMenuItemLabel } from '@/navigation-menu-item/display/utils/getNavigationMenuItemLabel'; -import { getNavigationMenuItemObjectNameSingular } from '@/navigation-menu-item/display/object/utils/getNavigationMenuItemObjectNameSingular'; import { recordIdentifierToObjectRecordIdentifier } from '@/navigation-menu-item/common/utils/recordIdentifierToObjectRecordIdentifier'; +import { LinkIconWithLinkOverlay } from '@/navigation-menu-item/display/link/components/LinkIconWithLinkOverlay'; +import { getNavigationMenuItemObjectNameSingular } from '@/navigation-menu-item/display/object/utils/getNavigationMenuItemObjectNameSingular'; +import { getNavigationMenuItemComputedLink } from '@/navigation-menu-item/display/utils/getNavigationMenuItemComputedLink'; +import { getNavigationMenuItemLabel } from '@/navigation-menu-item/display/utils/getNavigationMenuItemLabel'; +import { ObjectIconWithViewOverlay } from '@/navigation-menu-item/display/view/components/ObjectIconWithViewOverlay'; import { useGetStandardObjectIcon } from '@/object-metadata/hooks/useGetStandardObjectIcon'; import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; @@ -98,7 +103,7 @@ export const NavigationMenuItemIcon = ({ ); const useStyledIcon = !isRecord; const iconStyle = useStyledIcon - ? getNavigationMenuItemIconStyleFromColor(effectiveColor) + ? getIconTileColorShades(effectiveColor) : null; const iconColorToUse = iconStyle @@ -140,11 +145,11 @@ export const NavigationMenuItemIcon = ({ } return ( - {avatar} - + ); }; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemStyleIcon.tsx b/packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemStyleIcon.tsx deleted file mode 100644 index 7a8d57047a..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemStyleIcon.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import type { IconComponent } from 'twenty-ui/display'; -import { StyledNavigationMenuItemIconContainer } from '@/navigation-menu-item/display/components/NavigationMenuItemIconContainer'; -import { getNavigationMenuItemIconStyleFromColor } from '@/navigation-menu-item/common/utils/getNavigationMenuItemIconStyleFromColor'; -import { useContext } from 'react'; -import { ThemeContext } from 'twenty-ui/theme-constants'; - -export type NavigationMenuItemStyleIconProps = { - Icon: IconComponent; - color?: string | null; -}; - -export const NavigationMenuItemStyleIcon = ({ - Icon, - color, -}: NavigationMenuItemStyleIconProps) => { - const { theme } = useContext(ThemeContext); - const style = getNavigationMenuItemIconStyleFromColor(color); - return ( - - - - ); -}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/display/dnd/components/AddToNavigationDragHandle.tsx b/packages/twenty-front/src/modules/navigation-menu-item/display/dnd/components/AddToNavigationDragHandle.tsx index 0804248746..452df60f1a 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/display/dnd/components/AddToNavigationDragHandle.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/display/dnd/components/AddToNavigationDragHandle.tsx @@ -1,11 +1,14 @@ import { styled } from '@linaria/react'; import { useContext, type ReactNode } from 'react'; +import { NavigationMenuItemType } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; -import { IconGripVertical, type IconComponent } from 'twenty-ui/display'; +import { + IconGripVertical, + TintedIconTile, + type IconComponent, +} from 'twenty-ui/display'; import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; -import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; -import { NavigationMenuItemType } from 'twenty-shared/types'; import type { AddToNavigationDragPayload } from '@/navigation-menu-item/common/types/add-to-navigation-drag-payload'; import { getNavigationMenuItemColor } from '@/navigation-menu-item/common/utils/getNavigationMenuItemColor'; @@ -110,7 +113,7 @@ export const AddToNavigationDragHandle = ({ ) : showCustomContentWithoutWrapper ? ( customIconContent ) : hasBackgroundColor && icon ? ( - + ) : ( (); @@ -83,7 +82,7 @@ export const LinkIconWithLinkOverlay = ({ const showFavicon = isDefined(faviconUrl) && !isKnownFailed && localFailedLink !== linkKey; - const linkStyle = getNavigationMenuItemIconStyleFromColor( + const linkStyle = getIconTileColorShades( navItemColor ?? DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK, ); diff --git a/packages/twenty-front/src/modules/navigation-menu-item/display/view/components/ObjectIconWithViewOverlay.tsx b/packages/twenty-front/src/modules/navigation-menu-item/display/view/components/ObjectIconWithViewOverlay.tsx index 544862fc3e..e12a37e297 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/display/view/components/ObjectIconWithViewOverlay.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/display/view/components/ObjectIconWithViewOverlay.tsx @@ -1,10 +1,8 @@ import { styled } from '@linaria/react'; import { useContext } from 'react'; -import type { IconComponent } from 'twenty-ui/display'; +import { type IconComponent, getIconTileColorShades } from 'twenty-ui/display'; import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; -import { getNavigationMenuItemIconStyleFromColor } from '@/navigation-menu-item/common/utils/getNavigationMenuItemIconStyleFromColor'; - const StyledCompositeContainer = styled.div` align-items: center; border-radius: 4px; @@ -58,7 +56,7 @@ export const ObjectIconWithViewOverlay = ({ objectColor, }: ObjectIconWithViewOverlayProps) => { const { theme } = useContext(ThemeContext); - const objectStyle = getNavigationMenuItemIconStyleFromColor(objectColor); + const objectStyle = getIconTileColorShades(objectColor); return ( diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelEditFolderPickerSubPage.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelEditFolderPickerSubPage.tsx index 4499538d6d..c6f5997159 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelEditFolderPickerSubPage.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelEditFolderPickerSubPage.tsx @@ -1,15 +1,14 @@ import { useLingui } from '@lingui/react/macro'; import { useState } from 'react'; -import { useIcons } from 'twenty-ui/display'; +import { TintedIconTile, useIcons } from 'twenty-ui/display'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; -import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/common/constants/FolderIconDefault'; import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER } from '@/navigation-menu-item/common/constants/NavigationMenuItemDefaultColorFolder'; +import { useFolderPickerSelectionData } from '@/navigation-menu-item/edit/side-panel/hooks/useFolderPickerSelectionData'; import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup'; import { SidePanelList } from '@/side-panel/components/SidePanelList'; import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch'; -import { useFolderPickerSelectionData } from '@/navigation-menu-item/edit/side-panel/hooks/useFolderPickerSelectionData'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { filterBySearchQuery } from '~/utils/filterBySearchQuery'; @@ -71,10 +70,7 @@ export const SidePanelEditFolderPickerSubPage = () => { > + } label={folder.name} id={folder.id} diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelEditObjectViewBase.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelEditObjectViewBase.tsx index a04a24b087..db0632a351 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelEditObjectViewBase.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelEditObjectViewBase.tsx @@ -1,6 +1,5 @@ import { navigationMenuItemsSelector } from '@/navigation-menu-item/common/states/navigationMenuItemsSelector'; import { getNavigationMenuItemColor } from '@/navigation-menu-item/common/utils/getNavigationMenuItemColor'; -import { parseThemeColor } from '@/navigation-menu-item/common/utils/parseThemeColor'; import { getNavigationMenuItemObjectNameSingular } from '@/navigation-menu-item/display/object/utils/getNavigationMenuItemObjectNameSingular'; import { SidePanelEditColorOption } from '@/navigation-menu-item/edit/side-panel/components/SidePanelEditColorOption'; import { @@ -16,6 +15,7 @@ import { viewsSelector } from '@/views/states/selectors/viewsSelector'; import { useLingui } from '@lingui/react/macro'; import { isNonEmptyString } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; +import { parseThemeColor } from 'twenty-ui/utilities'; import { type NavigationMenuItem } from '~/generated-metadata/graphql'; type SidePanelEditObjectViewBaseProps = OrganizeActionsProps & { diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNavigationMenuItemEditPage.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNavigationMenuItemEditPage.tsx index b201525ba3..6c93cec625 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNavigationMenuItemEditPage.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNavigationMenuItemEditPage.tsx @@ -6,7 +6,6 @@ import { useSelectedNavigationMenuItemEditItem } from '@/navigation-menu-item/ed import { NavigationMenuItemType } from 'twenty-shared/types'; import { selectedNavigationMenuItemIdInEditModeState } from '@/navigation-menu-item/common/states/selectedNavigationMenuItemIdInEditModeState'; -import { parseThemeColor } from '@/navigation-menu-item/common/utils/parseThemeColor'; import { useSelectedNavigationMenuItemEditItemLabel } from '@/navigation-menu-item/edit/hooks/useSelectedNavigationMenuItemEditItemLabel'; import { useUpdateLinkInDraft } from '@/navigation-menu-item/edit/link/hooks/useUpdateLinkInDraft'; import { SidePanelEditColorOption } from '@/navigation-menu-item/edit/side-panel/components/SidePanelEditColorOption'; @@ -28,6 +27,7 @@ import { useLingui } from '@lingui/react/macro'; import { isDefined } from 'twenty-shared/utils'; import { IconPlus } from 'twenty-ui/display'; import { themeCssVariables } from 'twenty-ui/theme-constants'; +import { parseThemeColor } from 'twenty-ui/utilities'; const ADD_ITEM_TO_FOLDER_ACTION_ID = 'add-item-to-folder'; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx index 3466f20cd1..546d70c69d 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx @@ -1,4 +1,5 @@ import { useLingui } from '@lingui/react/macro'; +import { NavigationMenuItemType } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { Avatar, @@ -6,19 +7,18 @@ import { IconFolder, IconLink, IconTable, + TintedIconTile, } from 'twenty-ui/display'; import { themeCssVariables } from 'twenty-ui/theme-constants'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; -import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; -import { NavigationMenuItemType } from 'twenty-shared/types'; import { pendingInsertionNavigationMenuItemState } from '@/navigation-menu-item/common/states/pendingInsertionNavigationMenuItemState'; +import { useAddFolderToNavigationMenu } from '@/navigation-menu-item/edit/side-panel/hooks/useAddFolderToNavigationMenu'; +import { useAddLinkToNavigationMenu } from '@/navigation-menu-item/edit/side-panel/hooks/useAddLinkToNavigationMenu'; import { SidePanelAddToNavigationDroppable } from '@/side-panel/components/SidePanelAddToNavigationDroppable'; import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup'; import { SidePanelItemWithAddToNavigationDrag } from '@/side-panel/components/SidePanelItemWithAddToNavigationDrag'; import { SidePanelList } from '@/side-panel/components/SidePanelList'; -import { useAddFolderToNavigationMenu } from '@/navigation-menu-item/edit/side-panel/hooks/useAddFolderToNavigationMenu'; -import { useAddLinkToNavigationMenu } from '@/navigation-menu-item/edit/side-panel/hooks/useAddLinkToNavigationMenu'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; @@ -70,7 +70,7 @@ export const SidePanelNewSidebarItemMainMenu = ({ > ( - @@ -86,12 +86,7 @@ export const SidePanelNewSidebarItemMainMenu = ({ onEnter={onSelectView} > ( - - )} + Icon={() => } label={t`View`} id={NavigationMenuItemType.VIEW} hasSubMenu={true} diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewObjectPickerSubView.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewObjectPickerSubView.tsx index 8326e2f028..33ff84f1f4 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewObjectPickerSubView.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewObjectPickerSubView.tsx @@ -1,9 +1,8 @@ import { useLingui } from '@lingui/react/macro'; -import { IconSettings, useIcons } from 'twenty-ui/display'; +import { IconSettings, TintedIconTile } from 'twenty-ui/display'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; -import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; -import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup'; import { SidePanelList } from '@/side-panel/components/SidePanelList'; @@ -29,7 +28,6 @@ export const SidePanelNewSidebarItemViewObjectPickerSubView = ({ showSystemObjectsOption = true, }: SidePanelNewSidebarItemViewObjectPickerSubViewProps) => { const { t } = useLingui(); - const { getIcon } = useIcons(); const { filteredItems, selectableItemIds, isEmpty, hasSearchQuery } = useSidePanelFilteredPickerItems({ items: objects, @@ -61,10 +59,7 @@ export const SidePanelNewSidebarItemViewObjectPickerSubView = ({ > ( - + )} label={objectMetadataItem.labelPlural} id={objectMetadataItem.id} @@ -76,7 +71,7 @@ export const SidePanelNewSidebarItemViewObjectPickerSubView = ({ {showSystemObjectsOption && ( } + Icon={() => } label={t`System objects`} id="system" hasSubMenu={true} diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewSystemSubView.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewSystemSubView.tsx index 1a5c301b68..db2bce8a27 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewSystemSubView.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewSystemSubView.tsx @@ -1,9 +1,7 @@ import { useLingui } from '@lingui/react/macro'; -import { useIcons } from 'twenty-ui/display'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; -import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; -import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup'; import { SidePanelList } from '@/side-panel/components/SidePanelList'; @@ -25,7 +23,6 @@ export const SidePanelNewSidebarItemViewSystemSubView = ({ onSelectObject, }: SidePanelNewSidebarItemViewSystemSubViewProps) => { const { t } = useLingui(); - const { getIcon } = useIcons(); const { filteredItems: filteredSystemObjectMetadataItems, selectableItemIds, @@ -60,10 +57,7 @@ export const SidePanelNewSidebarItemViewSystemSubView = ({ > ( - + )} label={objectMetadataItem.labelPlural} id={objectMetadataItem.id} diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectMenuItem.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectMenuItem.tsx index 9ef0898d99..db71ddb503 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectMenuItem.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectMenuItem.tsx @@ -1,9 +1,7 @@ import { useLingui } from '@lingui/react/macro'; import { isDefined } from 'twenty-shared/utils'; -import { useIcons } from 'twenty-ui/display'; - import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; -import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { NavigationMenuItemType } from 'twenty-shared/types'; import { useDraftNavigationMenuItems } from '@/navigation-menu-item/edit/hooks/useDraftNavigationMenuItems'; import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/edit/hooks/useNavigationMenuObjectMetadataFromDraft'; @@ -30,7 +28,6 @@ export const SidePanelObjectMenuItem = ({ disableDrag = false, }: SidePanelObjectMenuItemProps) => { const { t } = useLingui(); - const { getIcon } = useIcons(); const { currentDraft } = useDraftNavigationMenuItems(); const { objectMetadataIdsInWorkspace } = useNavigationMenuObjectMetadataFromDraft(currentDraft); @@ -41,7 +38,6 @@ export const SidePanelObjectMenuItem = ({ indexViewIdFromObjectMetadataItemFamilySelector, { objectMetadataItemId: objectMetadataItem.id }, ); - const Icon = getIcon(objectMetadataItem.icon); const iconColor = getObjectColorWithFallback(objectMetadataItem); const isDisabled = isAlreadyInNavbar || !isDefined(defaultViewId); @@ -53,7 +49,7 @@ export const SidePanelObjectMenuItem = ({ }; const styledIcon = () => ( - + ); return ( diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerItem.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerItem.tsx index 5a6684b99a..09b7a78380 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerItem.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerItem.tsx @@ -1,9 +1,7 @@ import { isDefined } from 'twenty-shared/utils'; -import { useIcons } from 'twenty-ui/display'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; -import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; -import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { SidePanelObjectMenuItem } from '@/navigation-menu-item/edit/side-panel/components/SidePanelObjectMenuItem'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; @@ -29,8 +27,6 @@ export const SidePanelObjectPickerItem = ({ dragIndex, disableDrag = false, }: SidePanelObjectPickerItemProps) => { - const { getIcon } = useIcons(); - if (isViewItem && isDefined(onSelectObjectForViewEdit)) { return ( ( - + )} label={objectMetadataItem.labelPlural} id={objectMetadataItem.id} diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerSubView.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerSubView.tsx index 7a2d8bec7d..045a2bcb25 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerSubView.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerSubView.tsx @@ -1,15 +1,14 @@ import { useLingui } from '@lingui/react/macro'; -import { IconSettings } from 'twenty-ui/display'; +import { IconSettings, TintedIconTile } from 'twenty-ui/display'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; -import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; +import { SidePanelObjectPickerItem } from '@/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerItem'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { SidePanelAddToNavigationDroppable } from '@/side-panel/components/SidePanelAddToNavigationDroppable'; import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup'; import { SidePanelList } from '@/side-panel/components/SidePanelList'; import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch'; import { useSidePanelFilteredPickerItems } from '@/side-panel/hooks/useSidePanelFilteredPickerItems'; -import { SidePanelObjectPickerItem } from '@/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerItem'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; type SidePanelObjectPickerSubViewProps = { @@ -70,7 +69,7 @@ export const SidePanelObjectPickerSubView = ({ ))} } + Icon={() => } label={t`System objects`} id="system" hasSubMenu diff --git a/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataIcon.tsx b/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataIcon.tsx new file mode 100644 index 0000000000..47b35c962a --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataIcon.tsx @@ -0,0 +1,32 @@ +import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; +import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; +import { TintedIconTile, useIcons } from 'twenty-ui/display'; + +export type ObjectMetadataIconInput = Pick< + EnrichedObjectMetadataItem, + 'icon' | 'nameSingular' | 'color' | 'isSystem' +>; + +export type ObjectMetadataIconProps = { + objectMetadataItem?: ObjectMetadataIconInput | null; + size?: number; + stroke?: number; +}; + +export const ObjectMetadataIcon = ({ + objectMetadataItem, + size, + stroke, +}: ObjectMetadataIconProps) => { + const { getIcon } = useIcons(); + const Icon = getIcon(objectMetadataItem?.icon); + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useObjectMetadataSelectHelpers.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useObjectMetadataSelectHelpers.ts new file mode 100644 index 0000000000..084ca3e2e9 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useObjectMetadataSelectHelpers.ts @@ -0,0 +1,23 @@ +import { type IconComponent, useIcons } from 'twenty-ui/display'; +import { type ThemeColor } from 'twenty-ui/theme'; + +import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; +import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; + +type ObjectMetadataItemForSelectIcon = Pick< + EnrichedObjectMetadataItem, + 'icon' | 'nameSingular' | 'color' | 'isSystem' +>; + +export const useObjectMetadataSelectHelpers = () => { + const { getIcon } = useIcons(); + + const getSelectIconPropsFromObjectMetadataItem = ( + objectMetadataItem: ObjectMetadataItemForSelectIcon, + ): { Icon: IconComponent; iconThemeColor: ThemeColor } => ({ + Icon: getIcon(objectMetadataItem.icon), + iconThemeColor: getObjectColorWithFallback(objectMetadataItem), + }); + + return { getSelectIconPropsFromObjectMetadataItem }; +}; diff --git a/packages/twenty-front/src/modules/object-metadata/utils/getObjectColorWithFallback.ts b/packages/twenty-front/src/modules/object-metadata/utils/getObjectColorWithFallback.ts index 35e60e4290..464ffe97b6 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/getObjectColorWithFallback.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/getObjectColorWithFallback.ts @@ -2,6 +2,7 @@ import { isNonEmptyString } from '@sniptt/guards'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { CoreObjectNameSingular } from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; import { type ThemeColor, MAIN_COLOR_NAMES } from 'twenty-ui/theme'; const SYSTEM_OBJECT_COLOR: ThemeColor = 'gray'; @@ -37,11 +38,15 @@ const getColorForCustomObject = (seed: string): ThemeColor => { }; export const getObjectColorWithFallback = ( - objectMetadataItem: Pick< - EnrichedObjectMetadataItem, - 'nameSingular' | 'color' | 'isSystem' - >, + objectMetadataItem: + | Pick + | null + | undefined, ): ThemeColor => { + if (!isDefined(objectMetadataItem)) { + return SYSTEM_OBJECT_COLOR; + } + if (objectMetadataItem.isSystem) { return SYSTEM_OBJECT_COLOR; } diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeaderIcon.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeaderIcon.tsx index 86c69e4805..13c09117e4 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeaderIcon.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeaderIcon.tsx @@ -1,22 +1,15 @@ -import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; -import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { isDefined } from 'twenty-shared/utils'; -import { useIcons } from 'twenty-ui/display'; export const RecordIndexPageHeaderIcon = ({ objectMetadataItem, }: { objectMetadataItem?: EnrichedObjectMetadataItem; }) => { - const { getIcon } = useIcons(); - const ObjectIcon = getIcon(objectMetadataItem?.icon); - - if (!isDefined(ObjectIcon) || !isDefined(objectMetadataItem)) { + if (!isDefined(objectMetadataItem)) { return null; } - const iconColor = getObjectColorWithFallback(objectMetadataItem); - - return ; + return ; }; diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/ObjectRecordShowPageBreadcrumb.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/ObjectRecordShowPageBreadcrumb.tsx index bce68d646d..0350dc0936 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/components/ObjectRecordShowPageBreadcrumb.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/components/ObjectRecordShowPageBreadcrumb.tsx @@ -1,18 +1,15 @@ -import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; -import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; import { useIsRecordFieldReadOnly } from '@/object-record/read-only/hooks/useIsRecordFieldReadOnly'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { useRecordShowContainerActions } from '@/object-record/record-show/hooks/useRecordShowContainerActions'; -import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage'; import { useRecordShowPagePagination } from '@/object-record/record-show/hooks/useRecordShowPagePagination'; import { RecordTitleCell } from '@/object-record/record-title-cell/components/RecordTitleCell'; import { RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType'; import { styled } from '@linaria/react'; import { FieldMetadataType } from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; import { themeCssVariables } from 'twenty-ui/theme-constants'; const StyledEditableTitleContainer = styled.div` @@ -32,6 +29,12 @@ const StyledEditableTitlePrefix = styled.div` gap: ${themeCssVariables.spacing[1]}; `; +const StyledBreadcrumbPrefixObjectIcon = styled.div` + display: flex; + flex-shrink: 0; + opacity: 0.64; +`; + const StyledTitle = styled.div` max-width: 100%; overflow: hidden; @@ -78,13 +81,6 @@ export const ObjectRecordShowPageBreadcrumb = ({ const { navigateToIndexView, rankInView, totalCount } = useRecordShowPagePagination(objectNameSingular, objectRecordId); - const { headerIcon: HeaderIcon } = useRecordShowPage( - objectNameSingular, - objectRecordId, - ); - - const iconColor = getObjectColorWithFallback(objectMetadataItem); - if (loading) { return null; } @@ -96,9 +92,9 @@ export const ObjectRecordShowPageBreadcrumb = ({ navigateToIndexView(); }} > - {isDefined(HeaderIcon) && ( - - )} + + + {objectLabel} {' / '} diff --git a/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPage.ts b/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPage.ts index fb3a92ebd5..3fbd1fe38c 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPage.ts +++ b/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPage.ts @@ -2,7 +2,6 @@ import { useParams } from 'react-router-dom'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { isDefined } from 'twenty-shared/utils'; -import { useIcons } from 'twenty-ui/display'; export const useRecordShowPage = ( propsObjectNameSingular: string, @@ -21,13 +20,10 @@ export const useRecordShowPage = ( } const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular }); - const { getIcon } = useIcons(); - const headerIcon = getIcon(objectMetadataItem?.icon); return { objectNameSingular, objectRecordId, - headerIcon, objectMetadataItem, }; }; diff --git a/packages/twenty-front/src/modules/settings/components/SettingsMorphRelationMultiSelect.tsx b/packages/twenty-front/src/modules/settings/components/SettingsMorphRelationMultiSelect.tsx index d0708041e5..95dbafd974 100644 --- a/packages/twenty-front/src/modules/settings/components/SettingsMorphRelationMultiSelect.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsMorphRelationMultiSelect.tsx @@ -8,6 +8,7 @@ import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/Dropdow import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; +import { useObjectMetadataSelectHelpers } from '@/object-metadata/hooks/useObjectMetadataSelectHelpers'; import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation'; import { MultiSelectControl } from '@/ui/input/components/MultiSelectControl'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; @@ -21,7 +22,7 @@ import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue'; import { isNonEmptyString } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; -import { IconBox, useIcons, type IconComponent } from 'twenty-ui/display'; +import { IconBox, type IconComponent } from 'twenty-ui/display'; import { MenuItem, MenuItemMultiSelect } from 'twenty-ui/navigation'; import { themeCssVariables } from 'twenty-ui/theme-constants'; @@ -101,12 +102,13 @@ export const SettingsMorphRelationMultiSelect = ({ const [searchInputValue, setSearchInputValue] = useState(''); + const { getSelectIconPropsFromObjectMetadataItem } = + useObjectMetadataSelectHelpers(); const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); const [localSelectedObjectMetadataIds, setLocalSelectedObjectMetadataIds] = useState(selectedObjectMetadataIds); - const { getIcon } = useIcons(); const options = activeObjectMetadataItems .filter(isObjectMetadataAvailableForRelation) .sort((item1, item2) => @@ -114,8 +116,8 @@ export const SettingsMorphRelationMultiSelect = ({ ) .map((objectMetadataItem) => ({ label: objectMetadataItem.labelSingular, - Icon: getIcon(objectMetadataItem.icon), objectMetadataId: objectMetadataItem.id, + ...getSelectIconPropsFromObjectMetadataItem(objectMetadataItem), })); const selectedOptions = options.filter((option) => @@ -256,6 +258,7 @@ export const SettingsMorphRelationMultiSelect = ({ diff --git a/packages/twenty-front/src/modules/settings/data-model/graph-overview/components/SettingsDataModelOverviewField.tsx b/packages/twenty-front/src/modules/settings/data-model/graph-overview/components/SettingsDataModelOverviewField.tsx index 46ecf92c82..1e8824d82c 100644 --- a/packages/twenty-front/src/modules/settings/data-model/graph-overview/components/SettingsDataModelOverviewField.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/graph-overview/components/SettingsDataModelOverviewField.tsx @@ -1,11 +1,11 @@ -import { isDefined } from 'twenty-shared/utils'; +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; +import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector'; +import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { styled } from '@linaria/react'; import { Handle, Position } from '@xyflow/react'; -import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector'; -import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; -import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; -import { useIcons } from 'twenty-ui/display'; import { useContext } from 'react'; +import { isDefined } from 'twenty-shared/utils'; import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; import { RelationType } from '~/generated-metadata/graphql'; @@ -29,7 +29,6 @@ const StyledFieldName = styled.div` export const ObjectFieldRow = ({ field }: ObjectFieldRowProps) => { const { theme } = useContext(ThemeContext); const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector); - const { getIcon } = useIcons(); const relatedObjectId = field.relation?.targetObjectMetadata.id; @@ -37,11 +36,14 @@ export const ObjectFieldRow = ({ field }: ObjectFieldRowProps) => { (x) => x.id === relatedObjectId, ); - const Icon = getIcon(relatedObject?.icon); - return ( - {isDefined(Icon) && } + {isDefined(relatedObject) && ( + + )} {relatedObject?.labelPlural ?? ''} { const { theme } = useContext(ThemeContext); - const { getIcon } = useIcons(); const [otherFieldsExpanded, setOtherFieldsExpanded] = useState(false); const { totalCount } = useFindManyRecords({ @@ -122,8 +122,6 @@ export const SettingsDataModelOverviewObject = ({ (x) => x.type !== FieldMetadataType.RELATION, ).length; - const Icon = getIcon(objectMetadataItem.icon); - return ( @@ -134,7 +132,10 @@ export const SettingsDataModelOverviewObject = ({ objectNamePlural: objectMetadataItem.namePlural, })} > - {isDefined(Icon) && } + {objectMetadataItem.labelPlural} diff --git a/packages/twenty-front/src/modules/settings/data-model/new-object/components/SettingsAvailableStandardObjectItemTableRow.tsx b/packages/twenty-front/src/modules/settings/data-model/new-object/components/SettingsAvailableStandardObjectItemTableRow.tsx index b3fcc26b2e..c56d0fc3ad 100644 --- a/packages/twenty-front/src/modules/settings/data-model/new-object/components/SettingsAvailableStandardObjectItemTableRow.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/new-object/components/SettingsAvailableStandardObjectItemTableRow.tsx @@ -1,11 +1,10 @@ -import { isDefined } from 'twenty-shared/utils'; +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { TableCell } from '@/ui/layout/table/components/TableCell'; import { TableRow } from '@/ui/layout/table/components/TableRow'; -import { Checkbox } from 'twenty-ui/input'; -import { useIcons } from 'twenty-ui/display'; import { styled } from '@linaria/react'; import { useContext } from 'react'; +import { Checkbox } from 'twenty-ui/input'; import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; export const AVAILABLE_STANDARD_OBJECTS_GRID_TEMPLATE_COLUMNS = @@ -29,8 +28,6 @@ export const SettingsAvailableStandardObjectItemTableRow = ({ onClick, }: SettingsAvailableStandardObjectItemTableRowProps) => { const { theme } = useContext(ThemeContext); - const { getIcon } = useIcons(); - const Icon = getIcon(objectItem.icon); return ( - {isDefined(Icon) && } + {objectItem.labelPlural} diff --git a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectItemTableRow.tsx b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectItemTableRow.tsx index fbcbdeeb4c..a7d0e479b4 100644 --- a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectItemTableRow.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectItemTableRow.tsx @@ -1,12 +1,11 @@ -import { isDefined } from 'twenty-shared/utils'; -import { type ReactNode, useContext } from 'react'; import { styled } from '@linaria/react'; import { useLingui } from '@lingui/react/macro'; +import { type ReactNode, useContext } from 'react'; +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { isHiddenSystemField } from '@/object-metadata/utils/isHiddenSystemField'; import { SettingsItemTypeTag } from '@/settings/components/SettingsItemTypeTag'; -import { TableRow } from '@/ui/layout/table/components/TableRow'; import { SETTINGS_OBJECT_TABLE_ROW_GRID_TEMPLATE_COLUMNS, StyledActionTableCell, @@ -14,7 +13,7 @@ import { StyledStickyFirstCell, } from '@/settings/data-model/object-details/components/SettingsObjectItemTableRowStyledComponents'; import { TableCell } from '@/ui/layout/table/components/TableCell'; -import { useIcons } from 'twenty-ui/display'; +import { TableRow } from '@/ui/layout/table/components/TableRow'; import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; export type SettingsObjectMetadataItemTableRowProps = { @@ -59,11 +58,8 @@ export const SettingsObjectMetadataItemTableRow = ({ link, totalObjectCount, }: SettingsObjectMetadataItemTableRowProps) => { - const { theme } = useContext(ThemeContext); const { t } = useLingui(); - - const { getIcon } = useIcons(); - const Icon = getIcon(objectMetadataItem.icon); + const { theme } = useContext(ThemeContext); return ( - {isDefined(Icon) && ( - - )} + {objectMetadataItem.labelPlural} diff --git a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsUpdateDataModelObjectAboutForm.tsx b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsUpdateDataModelObjectAboutForm.tsx index a794bcd54c..212298358b 100644 --- a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsUpdateDataModelObjectAboutForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsUpdateDataModelObjectAboutForm.tsx @@ -1,5 +1,4 @@ import { isDDLLockedState } from '@/client-config/states/isDDLLockedState'; -import { parseThemeColor } from '@/navigation-menu-item/common/utils/parseThemeColor'; import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { isObjectMetadataReadOnly } from '@/object-record/read-only/utils/isObjectMetadataReadOnly'; @@ -16,6 +15,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { FormProvider, useForm } from 'react-hook-form'; import { SettingsPath } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; +import { parseThemeColor } from 'twenty-ui/utilities'; import { useNavigateSettings } from '~/hooks/useNavigateSettings'; import { updatedObjectNamePluralState } from '~/pages/settings/data-model/states/updatedObjectNamePluralState'; diff --git a/packages/twenty-front/src/modules/settings/data-model/objects/components/SettingsDataModelObjectSummary.tsx b/packages/twenty-front/src/modules/settings/data-model/objects/components/SettingsDataModelObjectSummary.tsx index 179c1d7f69..2ba54b143f 100644 --- a/packages/twenty-front/src/modules/settings/data-model/objects/components/SettingsDataModelObjectSummary.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/objects/components/SettingsDataModelObjectSummary.tsx @@ -1,21 +1,25 @@ -import { t } from '@lingui/core/macro'; import { styled } from '@linaria/react'; +import { t } from '@lingui/core/macro'; import { useContext } from 'react'; +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; -import { - IconBox, - OverflowingTextWithTooltip, - useIcons, -} from 'twenty-ui/display'; import { SettingsItemTypeTag } from '@/settings/components/SettingsItemTypeTag'; +import { IconBox, OverflowingTextWithTooltip } from 'twenty-ui/display'; import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; export type SettingsDataModelObjectPreviewProps = { className?: string; objectMetadataItems: Pick< EnrichedObjectMetadataItem, - 'icon' | 'labelSingular' | 'labelPlural' | 'isCustom' | 'isRemote' + | 'icon' + | 'labelSingular' + | 'labelPlural' + | 'isCustom' + | 'isRemote' + | 'nameSingular' + | 'color' + | 'isSystem' >[]; pluralizeLabel?: boolean; }; @@ -56,7 +60,14 @@ const StyledSeparator = styled.div` type SettingsDataModelObjectPreviewItemProps = { objectMetadataItem: Pick< EnrichedObjectMetadataItem, - 'icon' | 'labelSingular' | 'labelPlural' | 'isCustom' | 'isRemote' + | 'icon' + | 'labelSingular' + | 'labelPlural' + | 'isCustom' + | 'isRemote' + | 'nameSingular' + | 'color' + | 'isSystem' >; pluralizeLabel: boolean; index: number; @@ -68,8 +79,6 @@ const SettingsDataModelObjectPreviewItem = ({ index, }: SettingsDataModelObjectPreviewItemProps) => { const { theme } = useContext(ThemeContext); - const { getIcon } = useIcons(); - const ObjectIcon = getIcon(objectMetadataItem.icon); return ( <> @@ -77,7 +86,8 @@ const SettingsDataModelObjectPreviewItem = ({ - diff --git a/packages/twenty-front/src/modules/settings/data-model/objects/forms/components/SettingsDataModelObjectAboutForm.tsx b/packages/twenty-front/src/modules/settings/data-model/objects/forms/components/SettingsDataModelObjectAboutForm.tsx index 72643819f2..9cef109831 100644 --- a/packages/twenty-front/src/modules/settings/data-model/objects/forms/components/SettingsDataModelObjectAboutForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/objects/forms/components/SettingsDataModelObjectAboutForm.tsx @@ -1,4 +1,3 @@ -import { parseThemeColor } from '@/navigation-menu-item/common/utils/parseThemeColor'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { AdvancedSettingsWrapper } from '@/settings/components/AdvancedSettingsWrapper'; import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle'; @@ -24,6 +23,7 @@ import { import { Button } from 'twenty-ui/input'; import { Card } from 'twenty-ui/layout'; import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; +import { parseThemeColor } from 'twenty-ui/utilities'; import { type StringKeyOf } from 'type-fest'; import { useNavigateSettings } from '~/hooks/useNavigateSettings'; import { computeMetadataNamesFromLabels } from '~/pages/settings/data-model/utils/computeMetadataNamesFromLabels'; diff --git a/packages/twenty-front/src/modules/settings/developers/components/WebhookEntitySelect.tsx b/packages/twenty-front/src/modules/settings/developers/components/WebhookEntitySelect.tsx index 8b09142d3e..086e6b9727 100644 --- a/packages/twenty-front/src/modules/settings/developers/components/WebhookEntitySelect.tsx +++ b/packages/twenty-front/src/modules/settings/developers/components/WebhookEntitySelect.tsx @@ -1,4 +1,5 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { useObjectMetadataSelectHelpers } from '@/object-metadata/hooks/useObjectMetadataSelectHelpers'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; @@ -11,9 +12,9 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue'; -import { useContext, useState } from 'react'; import { styled } from '@linaria/react'; import { t } from '@lingui/core/macro'; +import { useContext, useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { IconBox, @@ -23,8 +24,8 @@ import { IconNorthStar, IconSettings, IconTable, - useIcons, } from 'twenty-ui/display'; +import { type SelectOption } from 'twenty-ui/input'; import { MenuItemSelect } from 'twenty-ui/navigation'; import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; @@ -85,9 +86,10 @@ export const WebhookEntitySelect = ({ dropdownId = WEBHOOK_ENTITY_DROPDOWN_ID, }: WebhookEntitySelectProps) => { const { theme } = useContext(ThemeContext); + const { getSelectIconPropsFromObjectMetadataItem } = + useObjectMetadataSelectHelpers(); const [searchInput, setSearchInput] = useState(''); const { objectMetadataItems } = useObjectMetadataItems(); - const { getIcon } = useIcons(); const { closeDropdown } = useCloseDropdown(); const selectedItemId = useAtomComponentStateValue( @@ -105,12 +107,12 @@ export const WebhookEntitySelect = ({ { label: t`Webhook`, value: 'metadata.webhook', icon: IconCode }, ]; - const objectOptions = [ + const objectOptions: SelectOption[] = [ { label: t`All Objects`, value: '*', Icon: IconNorthStar }, ...objectMetadataItems.map((item) => ({ label: item.labelPlural, value: item.nameSingular, - Icon: getIcon(item.icon), + ...getSelectIconPropsFromObjectMetadataItem(item), })), ]; @@ -199,6 +201,7 @@ export const WebhookEntitySelect = ({ > { setSearchFilter(text); }; @@ -125,27 +124,25 @@ export const SettingsRolePermissionsObjectLevelObjectPicker = ({ description={t`All the standard objects`} /> - {standardObjects.map((objectMetadataItem) => { - const Icon = getIcon(objectMetadataItem.icon); - return ( - - handleSelectObjectMetadata(objectMetadataItem.id) + {standardObjects.map((objectMetadataItem) => ( + + handleSelectObjectMetadata(objectMetadataItem.id) + } + > + } - > - - } - title={objectMetadataItem.labelPlural} - /> - - ); - })} + title={objectMetadataItem.labelPlural} + /> + + ))} )} @@ -153,28 +150,25 @@ export const SettingsRolePermissionsObjectLevelObjectPicker = ({
- {customObjects.map((objectMetadataItem) => { - const Icon = getIcon(objectMetadataItem.icon); - return ( - - handleSelectObjectMetadata(objectMetadataItem.id) + {customObjects.map((objectMetadataItem) => ( + + handleSelectObjectMetadata(objectMetadataItem.id) + } + > + } - > - - } - title={objectMetadataItem.labelPlural} - /> - - ); - })} + title={objectMetadataItem.labelPlural} + /> + + ))}
)} diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx index a678cc6ca8..000ff7125f 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx @@ -1,3 +1,4 @@ +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { SettingsRolePermissionsObjectLevelOverrideCellContainer } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelOverrideCellContainer'; import { SettingsRolePermissionsObjectLevelSeeFieldsValueForObject } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSeeFieldsValueForObject'; @@ -7,10 +8,10 @@ import { OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS } from '@/settings/role import { TableCell } from '@/ui/layout/table/components/TableCell'; import { TableRow } from '@/ui/layout/table/components/TableRow'; import { styled } from '@linaria/react'; -import { SettingsPath } from 'twenty-shared/types'; -import { isDefined, getSettingsPath } from 'twenty-shared/utils'; -import { OverflowingTextWithTooltip, useIcons } from 'twenty-ui/display'; import { useContext } from 'react'; +import { SettingsPath } from 'twenty-shared/types'; +import { getSettingsPath } from 'twenty-shared/utils'; +import { OverflowingTextWithTooltip } from 'twenty-ui/display'; import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; const StyledNameLabel = styled.div` @@ -33,10 +34,6 @@ export const SettingsRolePermissionsObjectLevelTableRow = ({ fromAgentId, }: SettingsRolePermissionsObjectLevelTableRowProps) => { const { theme } = useContext(ThemeContext); - const { getIcon } = useIcons(); - - const Icon = getIcon(objectMetadataItem.icon); - const objectLabelPlural = objectMetadataItem.labelPlural; const navigationPath = getSettingsPath(SettingsPath.RoleObjectLevel, { roleId: roleId, @@ -56,15 +53,11 @@ export const SettingsRolePermissionsObjectLevelTableRow = ({ color={themeCssVariables.font.color.primary} gap={themeCssVariables.spacing[1]} > - {isDefined(Icon) && ( - - )} + diff --git a/packages/twenty-front/src/modules/side-panel/components/SidePanelFolderInfo.tsx b/packages/twenty-front/src/modules/side-panel/components/SidePanelFolderInfo.tsx index 869fe123ba..e4b9689f5f 100644 --- a/packages/twenty-front/src/modules/side-panel/components/SidePanelFolderInfo.tsx +++ b/packages/twenty-front/src/modules/side-panel/components/SidePanelFolderInfo.tsx @@ -1,16 +1,15 @@ import { styled } from '@linaria/react'; import { useLingui } from '@lingui/react/macro'; -import { useIcons } from 'twenty-ui/display'; +import { NavigationMenuItemType } from 'twenty-shared/types'; +import { TintedIconTile, useIcons } from 'twenty-ui/display'; +import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/common/constants/FolderIconDefault'; +import { selectedNavigationMenuItemIdInEditModeState } from '@/navigation-menu-item/common/states/selectedNavigationMenuItemIdInEditModeState'; +import { useNavigationMenuItemSectionItems } from '@/navigation-menu-item/display/hooks/useNavigationMenuItemSectionItems'; +import { useUpdateFolderInDraft } from '@/navigation-menu-item/edit/folder/hooks/useUpdateFolderInDraft'; import { SidePanelPageInfoLayout } from '@/side-panel/components/SidePanelPageInfoLayout'; import { sidePanelPageInfoState } from '@/side-panel/states/sidePanelPageInfoState'; import { sidePanelShouldFocusTitleInputComponentState } from '@/side-panel/states/sidePanelShouldFocusTitleInputComponentState'; -import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; -import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/common/constants/FolderIconDefault'; -import { NavigationMenuItemType } from 'twenty-shared/types'; -import { useUpdateFolderInDraft } from '@/navigation-menu-item/edit/folder/hooks/useUpdateFolderInDraft'; -import { useNavigationMenuItemSectionItems } from '@/navigation-menu-item/display/hooks/useNavigationMenuItemSectionItems'; -import { selectedNavigationMenuItemIdInEditModeState } from '@/navigation-menu-item/common/states/selectedNavigationMenuItemIdInEditModeState'; import { IconPicker } from '@/ui/input/components/IconPicker'; import { TitleInput } from '@/ui/input/components/TitleInput'; import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState'; @@ -78,7 +77,7 @@ export const SidePanelFolderInfo = () => { } clickableComponent={ - diff --git a/packages/twenty-front/src/modules/side-panel/components/SidePanelObjectFilterDropdownContent.tsx b/packages/twenty-front/src/modules/side-panel/components/SidePanelObjectFilterDropdownContent.tsx index e6cebd8407..7b9754c0bb 100644 --- a/packages/twenty-front/src/modules/side-panel/components/SidePanelObjectFilterDropdownContent.tsx +++ b/packages/twenty-front/src/modules/side-panel/components/SidePanelObjectFilterDropdownContent.tsx @@ -1,12 +1,11 @@ import { useLingui } from '@lingui/react/macro'; import { useState } from 'react'; import { OBJECTS_WITH_CHANNEL_VISIBILITY_CONSTRAINTS } from 'twenty-shared/constants'; -import { IconCube, useIcons } from 'twenty-ui/display'; +import { IconCube, TintedIconTile } from 'twenty-ui/display'; import { MenuItemSelectAvatar, MenuItemToggle } from 'twenty-ui/navigation'; -import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { useReadableObjectMetadataItems } from '@/object-metadata/hooks/useReadableObjectMetadataItems'; -import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; import { OBJECT_FILTER_DROPDOWN_ID } from '@/side-panel/components/SidePanelObjectFilterDropdown'; import { sidePanelShowHiddenObjectsState } from '@/side-panel/states/sidePanelShowHiddenObjectsState'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; @@ -33,7 +32,6 @@ export const SidePanelObjectFilterDropdownContent = ({ onSelectObject, }: SidePanelObjectFilterDropdownContentProps) => { const { t } = useLingui(); - const { getIcon } = useIcons(); const [filterSearch, setFilterSearch] = useState(''); const [sidePanelShowHiddenObjects, setSidePanelShowHiddenObjects] = useAtomState(sidePanelShowHiddenObjectsState); @@ -93,9 +91,7 @@ export const SidePanelObjectFilterDropdownContent = ({ onEnter={() => handleSelect(null)} > - } + avatar={} text={t`All objects`} selected={selectedObjectNameSingular === null} onClick={() => handleSelect(null)} @@ -103,9 +99,6 @@ export const SidePanelObjectFilterDropdownContent = ({ />
{displayedObjects.map((objectMetadataItem) => { - const ObjectIcon = getIcon(objectMetadataItem.icon); - const iconColor = getObjectColorWithFallback(objectMetadataItem); - return ( } text={objectMetadataItem.labelPlural} diff --git a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/dropdown-content/ChartDataSourceDropdownContent.tsx b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/dropdown-content/ChartDataSourceDropdownContent.tsx index b974beb2a8..c903f81ed1 100644 --- a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/dropdown-content/ChartDataSourceDropdownContent.tsx +++ b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/dropdown-content/ChartDataSourceDropdownContent.tsx @@ -1,10 +1,11 @@ +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; +import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { filterReadableActiveObjectMetadataItems } from '@/object-metadata/utils/filterReadableActiveObjectMetadataItems'; +import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore'; import { useResetChartDraftFiltersSettings } from '@/side-panel/pages/page-layout/hooks/useResetChartDraftFiltersSettings'; import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig'; import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode'; -import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; -import { filterReadableActiveObjectMetadataItems } from '@/object-metadata/utils/filterReadableActiveObjectMetadataItems'; -import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; @@ -21,7 +22,7 @@ import { t } from '@lingui/core/macro'; import { Trans } from '@lingui/react/macro'; import { useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; -import { IconChevronLeft, IconSettings, useIcons } from 'twenty-ui/display'; +import { IconChevronLeft, IconSettings } from 'twenty-ui/display'; import { MenuItem, MenuItemSelect } from 'twenty-ui/navigation'; import { filterBySearchQuery } from '~/utils/filterBySearchQuery'; @@ -74,8 +75,6 @@ export const ChartDataSourceDropdownContent = () => { const { closeDropdown } = useCloseDropdown(); - const { getIcon } = useIcons(); - const { resetChartDraftFiltersSettings } = useResetChartDraftFiltersSettings(); @@ -162,7 +161,9 @@ export const ChartDataSourceDropdownContent = () => { text={objectMetadataItem.labelPlural} selected={currentObjectMetadataItemId === objectMetadataItem.id} focused={selectedItemId === objectMetadataItem.id} - LeftIcon={getIcon(objectMetadataItem.icon)} + LeftIcon={() => ( + + )} onClick={() => { handleSelectSource(objectMetadataItem.id); }} diff --git a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableDataSourceDropdownContent.tsx b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableDataSourceDropdownContent.tsx index 892fc21c64..5a0ef44b90 100644 --- a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableDataSourceDropdownContent.tsx +++ b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableDataSourceDropdownContent.tsx @@ -1,4 +1,6 @@ +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { filterReadableActiveObjectMetadataItems } from '@/object-metadata/utils/filterReadableActiveObjectMetadataItems'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { useUpdatePageLayoutWidget } from '@/page-layout/hooks/useUpdatePageLayoutWidget'; import { useCreateViewForRecordTableWidget } from '@/page-layout/widgets/record-table/hooks/useCreateViewForRecordTableWidget'; @@ -16,11 +18,9 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue'; -import { filterReadableActiveObjectMetadataItems } from '@/object-metadata/utils/filterReadableActiveObjectMetadataItems'; import { t } from '@lingui/core/macro'; import { useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; -import { useIcons } from 'twenty-ui/display'; import { MenuItemSelect } from 'twenty-ui/navigation'; import { filterBySearchQuery } from '~/utils/filterBySearchQuery'; @@ -56,7 +56,6 @@ export const RecordTableDataSourceDropdownContent = () => { const { updatePageLayoutWidget } = useUpdatePageLayoutWidget(pageLayoutId); const { closeDropdown } = useCloseDropdown(); - const { getIcon } = useIcons(); const readableActiveObjectMetadataItems = filterReadableActiveObjectMetadataItems( @@ -149,7 +148,9 @@ export const RecordTableDataSourceDropdownContent = () => { text={objectMetadataItem.labelPlural} selected={currentObjectMetadataItemId === objectMetadataItem.id} focused={selectedItemId === objectMetadataItem.id} - LeftIcon={getIcon(objectMetadataItem.icon)} + LeftIcon={() => ( + + )} onClick={() => { handleSelectSource(objectMetadataItem.id); }} diff --git a/packages/twenty-front/src/modules/ui/input/components/MultiSelectControl.tsx b/packages/twenty-front/src/modules/ui/input/components/MultiSelectControl.tsx index 0a3f27f496..0bff9122e0 100644 --- a/packages/twenty-front/src/modules/ui/input/components/MultiSelectControl.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/MultiSelectControl.tsx @@ -9,7 +9,9 @@ import { IconChevronDown, type IconComponent, OverflowingTextWithTooltip, + TintedIconTile, } from 'twenty-ui/display'; +import { type ThemeColor } from 'twenty-ui/theme'; import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; const StyledIconChevronDownWrapper = styled.div<{ @@ -25,6 +27,7 @@ const StyledIconChevronDownWrapper = styled.div<{ type MultiSelectOptionType = { label: string; Icon: IconComponent; + iconThemeColor?: ThemeColor | null; }; type MultiSelectControlProps = Omit & { @@ -59,11 +62,22 @@ export const MultiSelectControl = ({ stroke: theme.icon.stroke.sm, }) ) : isDefined(firstSelectedOption?.Icon) ? ( - + isDefined(firstSelectedOption.iconThemeColor) ? ( + + ) : ( + + ) ) : null} {isDefined(fixedText) ? ( diff --git a/packages/twenty-front/src/modules/ui/input/components/Select.tsx b/packages/twenty-front/src/modules/ui/input/components/Select.tsx index 96adf7ed27..2b74537b8f 100644 --- a/packages/twenty-front/src/modules/ui/input/components/Select.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/Select.tsx @@ -226,6 +226,7 @@ export const Select = ({ ({ > {isDefined(selectedOption?.Icon) ? ( - + isDefined(selectedOption.iconThemeColor) ? ( + + ) : ( + + ) ) : null} - + ) : ( diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowDropdownStepOutputItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowDropdownStepOutputItems.tsx index e06e71e16a..dc932cdea9 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowDropdownStepOutputItems.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowDropdownStepOutputItems.tsx @@ -5,6 +5,7 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM import { useGetFieldMetadataItemByIdOrThrow } from '@/object-metadata/hooks/useGetFieldMetadataItemById'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { useObjectMetadataSelectHelpers } from '@/object-metadata/hooks/useObjectMetadataSelectHelpers'; import { useGetInitialFilterValue } from '@/object-record/object-filter-dropdown/hooks/useGetInitialFilterValue'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; @@ -52,6 +53,8 @@ export const WorkflowDropdownStepOutputItems = ({ }: WorkflowDropdownStepOutputItemsProps) => { const { t } = useLingui(); const { getIcon } = useIcons(); + const { getSelectIconPropsFromObjectMetadataItem } = + useObjectMetadataSelectHelpers(); const { upsertStepFilterSettings } = useUpsertStepFilterSettings(); const { getFieldMetadataItemByIdOrThrow } = @@ -203,6 +206,10 @@ export const WorkflowDropdownStepOutputItems = ({ const objectLabel = subStepObjectMetadataItem?.labelSingular; + const subStepObjectIconProps = isDefined(subStepObjectMetadataItem) + ? getSelectIconPropsFromObjectMetadataItem(subStepObjectMetadataItem) + : undefined; + return ( )} diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/filters/components/WorkflowStepFilterFieldSelect.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/filters/components/WorkflowStepFilterFieldSelect.tsx index 9c9ab0cf4c..3d14eeb09c 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/filters/components/WorkflowStepFilterFieldSelect.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/filters/components/WorkflowStepFilterFieldSelect.tsx @@ -1,4 +1,5 @@ import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById'; +import { useObjectMetadataSelectHelpers } from '@/object-metadata/hooks/useObjectMetadataSelectHelpers'; import { SelectControl } from '@/ui/input/components/SelectControl'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; @@ -33,6 +34,8 @@ export const WorkflowStepFilterFieldSelect = ({ const { t } = useLingui(); const { closeDropdown } = useCloseDropdown(); const { getIcon } = useIcons(); + const { getSelectIconPropsFromObjectMetadataItem } = + useObjectMetadataSelectHelpers(); const availableVariablesInWorkflowStep = useAvailableVariablesInWorkflowStep({ shouldDisplayRecordFields: true, @@ -88,12 +91,20 @@ export const WorkflowStepFilterFieldSelect = ({ ? t`Select a field from a previous step` : variableLabel; + const fullRecordIconProps = stepFilter.isFullRecord + ? isDefined(filterObjectMetadataItem) + ? getSelectIconPropsFromObjectMetadataItem(filterObjectMetadataItem) + : undefined + : undefined; + const icon = stepFilter.isFullRecord - ? getIcon(filterObjectMetadataItem?.icon) + ? fullRecordIconProps?.Icon : filterFieldMetadataItem?.icon ? getIcon(filterFieldMetadataItem.icon) : undefined; + const iconThemeColor = fullRecordIconProps?.iconThemeColor; + if (readonly || noAvailableVariables) { const disabledLabel = noAvailableVariables ? t`No available fields to select` @@ -109,6 +120,7 @@ export const WorkflowStepFilterFieldSelect = ({ label: disabledLabel, fullLabel: variablePathLabel, Icon: icon, + iconThemeColor, }} isDisabled={true} /> @@ -128,6 +140,7 @@ export const WorkflowStepFilterFieldSelect = ({ fullLabel: variablePathLabel, value: stepFilter.stepOutputKey, Icon: icon, + iconThemeColor, }} textAccent={isSelectedFieldNotFound ? 'placeholder' : 'default'} isDisabled={readonly} diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowAiAgentPermissionsObjectRow.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowAiAgentPermissionsObjectRow.tsx index 87a37c122f..e9b79d8e51 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowAiAgentPermissionsObjectRow.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowAiAgentPermissionsObjectRow.tsx @@ -1,12 +1,12 @@ -import { useIcons } from 'twenty-ui/display'; +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; +import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { MenuItem } from 'twenty-ui/navigation'; type WorkflowAiAgentPermissionsObjectRowProps = { - objectMetadata: { - id: string; - icon?: string | null; - labelPlural: string; - }; + objectMetadata: Pick< + EnrichedObjectMetadataItem, + 'id' | 'icon' | 'labelPlural' | 'nameSingular' | 'color' | 'isSystem' + >; onClick?: () => void; readonly: boolean; }; @@ -16,13 +16,9 @@ export const WorkflowAiAgentPermissionsObjectRow = ({ onClick, readonly, }: WorkflowAiAgentPermissionsObjectRowProps) => { - const { getIcon } = useIcons(); - const IconComponent = getIcon(objectMetadata.icon); - return ( } text={objectMetadata.labelPlural} hasSubMenu={!readonly} onClick={!readonly ? onClick : undefined} diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowAiAgentPermissionsObjectsList.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowAiAgentPermissionsObjectsList.tsx index 99221abb83..38c1546041 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowAiAgentPermissionsObjectsList.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowAiAgentPermissionsObjectsList.tsx @@ -1,13 +1,15 @@ +import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup'; import { t } from '@lingui/core/macro'; import { WorkflowAiAgentPermissionsObjectRow } from './WorkflowAiAgentPermissionsObjectRow'; type WorkflowAiAgentPermissionsObjectsListProps = { - objects: Array<{ - id: string; - icon?: string | null; - labelPlural: string; - }>; + objects: Array< + Pick< + EnrichedObjectMetadataItem, + 'id' | 'icon' | 'labelPlural' | 'nameSingular' | 'color' | 'isSystem' + > + >; onObjectClick: (objectId: string) => void; readonly: boolean; }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord.tsx index b746e0a64b..d3236fee17 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord.tsx @@ -1,6 +1,6 @@ -import { t } from '@lingui/core/macro'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { useObjectMetadataSelectHelpers } from '@/object-metadata/hooks/useObjectMetadataSelectHelpers'; import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition'; import { FormFieldInput } from '@/object-record/record-field/ui/components/FormFieldInput'; import { isFieldRelation } from '@/object-record/record-field/ui/types/guards/isFieldRelation'; @@ -12,10 +12,11 @@ import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowS import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter'; import { shouldDisplayFormField } from '@/workflow/workflow-steps/workflow-actions/utils/shouldDisplayFormField'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; +import { t } from '@lingui/core/macro'; import { useEffect, useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { canObjectBeManagedByWorkflow } from 'twenty-shared/workflow'; -import { HorizontalSeparator, useIcons } from 'twenty-ui/display'; +import { HorizontalSeparator } from 'twenty-ui/display'; import { type SelectOption } from 'twenty-ui/input'; import { type JsonValue } from 'type-fest'; import { useDebouncedCallback } from 'use-debounce'; @@ -65,8 +66,8 @@ export const WorkflowEditActionCreateRecord = ({ action, actionOptions, }: WorkflowEditActionCreateRecordProps) => { - const { getIcon } = useIcons(); - + const { getSelectIconPropsFromObjectMetadataItem } = + useObjectMetadataSelectHelpers(); const { activeNonSystemObjectMetadataItems } = useFilteredObjectMetadataItems(); @@ -79,9 +80,9 @@ export const WorkflowEditActionCreateRecord = ({ }), ) .map((item) => ({ - Icon: getIcon(item.icon), label: item.labelPlural, value: item.nameSingular, + ...getSelectIconPropsFromObjectMetadataItem(item), })); const [formData, setFormData] = useState({ diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx index 8cc097afac..19badac48c 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx @@ -1,8 +1,9 @@ -import { t } from '@lingui/core/macro'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; +import { useObjectMetadataSelectHelpers } from '@/object-metadata/hooks/useObjectMetadataSelectHelpers'; import { FormSingleRecordPicker } from '@/object-record/record-field/ui/form-types/components/FormSingleRecordPicker'; import { Select } from '@/ui/input/components/Select'; import { type WorkflowDeleteRecordAction } from '@/workflow/types/Workflow'; +import { t } from '@lingui/core/macro'; import { useEffect, useState } from 'react'; import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; @@ -11,7 +12,7 @@ import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/Workflo import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; import { isDefined } from 'twenty-shared/utils'; import { canObjectBeManagedByWorkflow } from 'twenty-shared/workflow'; -import { HorizontalSeparator, useIcons } from 'twenty-ui/display'; +import { HorizontalSeparator } from 'twenty-ui/display'; import { type SelectOption } from 'twenty-ui/input'; import { type JsonValue } from 'type-fest'; import { useDebouncedCallback } from 'use-debounce'; @@ -37,8 +38,8 @@ export const WorkflowEditActionDeleteRecord = ({ action, actionOptions, }: WorkflowEditActionDeleteRecordProps) => { - const { getIcon } = useIcons(); - + const { getSelectIconPropsFromObjectMetadataItem } = + useObjectMetadataSelectHelpers(); const { activeNonSystemObjectMetadataItems } = useFilteredObjectMetadataItems(); @@ -51,9 +52,9 @@ export const WorkflowEditActionDeleteRecord = ({ }), ) .map((item) => ({ - Icon: getIcon(item.icon), label: item.labelPlural, value: item.nameSingular, + ...getSelectIconPropsFromObjectMetadataItem(item), })); const [formData, setFormData] = useState({ diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx index 1dce72e0a0..48eea59537 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx @@ -1,4 +1,5 @@ import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; +import { useObjectMetadataSelectHelpers } from '@/object-metadata/hooks/useObjectMetadataSelectHelpers'; import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition'; import { FormFieldInput } from '@/object-record/record-field/ui/components/FormFieldInput'; import { FormSingleRecordPicker } from '@/object-record/record-field/ui/form-types/components/FormSingleRecordPicker'; @@ -16,7 +17,7 @@ import { t } from '@lingui/core/macro'; import { useEffect, useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { canObjectBeManagedByWorkflow } from 'twenty-shared/workflow'; -import { HorizontalSeparator, useIcons } from 'twenty-ui/display'; +import { HorizontalSeparator } from 'twenty-ui/display'; import { type SelectOption } from 'twenty-ui/input'; import { type JsonValue } from 'type-fest'; import { useDebouncedCallback } from 'use-debounce'; @@ -38,8 +39,8 @@ export const WorkflowEditActionUpdateRecord = ({ action, actionOptions, }: WorkflowEditActionUpdateRecordProps) => { - const { getIcon } = useIcons(); - + const { getSelectIconPropsFromObjectMetadataItem } = + useObjectMetadataSelectHelpers(); const { activeNonSystemObjectMetadataItems } = useFilteredObjectMetadataItems(); @@ -52,9 +53,9 @@ export const WorkflowEditActionUpdateRecord = ({ }), ) .map((item) => ({ - Icon: getIcon(item.icon), label: item.labelPlural, value: item.nameSingular, + ...getSelectIconPropsFromObjectMetadataItem(item), })); const [formData, setFormData] = useState({ diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpsertRecord.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpsertRecord.tsx index 83fb7942b1..b7f89fec19 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpsertRecord.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpsertRecord.tsx @@ -1,5 +1,6 @@ import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { useObjectMetadataSelectHelpers } from '@/object-metadata/hooks/useObjectMetadataSelectHelpers'; import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition'; import { FormFieldInput } from '@/object-record/record-field/ui/components/FormFieldInput'; import { FormSingleRecordPicker } from '@/object-record/record-field/ui/form-types/components/FormSingleRecordPicker'; @@ -17,7 +18,7 @@ import { t } from '@lingui/core/macro'; import { useEffect, useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { canObjectBeManagedByWorkflow } from 'twenty-shared/workflow'; -import { HorizontalSeparator, useIcons } from 'twenty-ui/display'; +import { HorizontalSeparator } from 'twenty-ui/display'; import { type SelectOption } from 'twenty-ui/input'; import { type JsonValue } from 'type-fest'; import { useDebouncedCallback } from 'use-debounce'; @@ -75,8 +76,8 @@ export const WorkflowEditActionUpsertRecord = ({ action, actionOptions, }: WorkflowEditActionUpsertRecordProps) => { - const { getIcon } = useIcons(); - + const { getSelectIconPropsFromObjectMetadataItem } = + useObjectMetadataSelectHelpers(); const { activeNonSystemObjectMetadataItems } = useFilteredObjectMetadataItems(); @@ -89,9 +90,9 @@ export const WorkflowEditActionUpsertRecord = ({ }), ) .map((item) => ({ - Icon: getIcon(item.icon), label: item.labelPlural, value: item.nameSingular, + ...getSelectIconPropsFromObjectMetadataItem(item), })); const [formData, setFormData] = useState({ diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowEditActionFindRecords.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowEditActionFindRecords.tsx index fcb5facd43..9f60b3db27 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowEditActionFindRecords.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowEditActionFindRecords.tsx @@ -1,16 +1,17 @@ import { styled } from '@linaria/react'; -import { msg } from '@lingui/core/macro'; import { i18n } from '@lingui/core'; +import { msg } from '@lingui/core/macro'; import { useLingui } from '@lingui/react/macro'; import { isNumber } from '@sniptt/guards'; import { useEffect, useState } from 'react'; import { QUERY_MAX_RECORDS } from 'twenty-shared/constants'; import { isDefined } from 'twenty-shared/utils'; -import { HorizontalSeparator, useIcons } from 'twenty-ui/display'; +import { HorizontalSeparator } from 'twenty-ui/display'; import { type JsonValue } from 'type-fest'; import { useDebouncedCallback } from 'use-debounce'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; +import { useObjectMetadataSelectHelpers } from '@/object-metadata/hooks/useObjectMetadataSelectHelpers'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; import { FormNumberFieldInput } from '@/object-record/record-field/ui/form-types/components/FormNumberFieldInput'; @@ -82,6 +83,8 @@ export const WorkflowEditActionFindRecords = ({ actionOptions, }: WorkflowEditActionFindRecordsProps) => { const { t } = useLingui(); + const { getSelectIconPropsFromObjectMetadataItem } = + useObjectMetadataSelectHelpers(); const maxRecordsFormatted = QUERY_MAX_RECORDS.toLocaleString(); const dropdownId = 'workflow-edit-action-record-find-records-object-name'; @@ -108,13 +111,11 @@ export const WorkflowEditActionFindRecords = ({ const selectedObjectMetadataItem = objectMetadataItems.find( (item) => item.nameSingular === formData.objectNameSingular, ); - const { getIcon } = useIcons(); - const selectedOption = selectedObjectMetadataItem ? { - Icon: getIcon(selectedObjectMetadataItem?.icon), - label: selectedObjectMetadataItem?.labelPlural, - value: selectedObjectMetadataItem?.nameSingular, + label: selectedObjectMetadataItem.labelPlural, + value: selectedObjectMetadataItem.nameSingular, + ...getSelectIconPropsFromObjectMetadataItem(selectedObjectMetadataItem), } : { label: i18n._(defaultSelectedOptionMessage), value: '' }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowObjectDropdownContent.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowObjectDropdownContent.tsx index d80e5e8f2c..ce6c21bd47 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowObjectDropdownContent.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowObjectDropdownContent.tsx @@ -1,3 +1,4 @@ +import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; @@ -9,7 +10,7 @@ import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/Gene import { Trans } from '@lingui/react/macro'; import { isNonEmptyString } from '@sniptt/guards'; import { useState } from 'react'; -import { IconChevronLeft, IconSettings, useIcons } from 'twenty-ui/display'; +import { IconChevronLeft, IconSettings } from 'twenty-ui/display'; import { MenuItem } from 'twenty-ui/navigation'; type WorkflowObjectDropdownContentProps = { @@ -21,7 +22,6 @@ export const WorkflowObjectDropdownContent = ({ onOptionClick, showAdvancedOption = true, }: WorkflowObjectDropdownContentProps) => { - const { getIcon } = useIcons(); const [searchInputValue, setSearchInputValue] = useState(''); const [isSystemObjectsOpen, setIsSystemObjectsOpen] = useState(false); @@ -74,12 +74,6 @@ export const WorkflowObjectDropdownContent = ({ ? filteredSystemObjects : filteredNonSystemObjects; - const filteredOptions = filteredObjects.map((objectMetadataItem) => ({ - Icon: getIcon(objectMetadataItem.icon), - label: objectMetadataItem.labelPlural, - value: objectMetadataItem.nameSingular, - })); - const handleSystemObjectsClick = () => { setIsSystemObjectsOpen(true); setSearchInputValue(''); @@ -123,12 +117,14 @@ export const WorkflowObjectDropdownContent = ({ /> - {filteredOptions.map((option) => ( + {filteredObjects.map((objectMetadataItem) => ( onOptionClick(option.value)} + key={objectMetadataItem.nameSingular} + LeftIcon={() => ( + + )} + text={objectMetadataItem.labelPlural} + onClick={() => onOptionClick(objectMetadataItem.nameSingular)} /> ))} {shouldShowAdvanced && ( diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowFormFieldSettingsRecordPicker.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowFormFieldSettingsRecordPicker.tsx index 3c7f89786f..a0a9c1c81a 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowFormFieldSettingsRecordPicker.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowFormFieldSettingsRecordPicker.tsx @@ -1,4 +1,5 @@ import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; +import { useObjectMetadataSelectHelpers } from '@/object-metadata/hooks/useObjectMetadataSelectHelpers'; import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer'; import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormTextFieldInput'; import { InputLabel } from '@/ui/input/components/InputLabel'; @@ -6,10 +7,9 @@ import { Select } from '@/ui/input/components/Select'; import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; import { type WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField'; import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings'; -import { t } from '@lingui/core/macro'; import { styled } from '@linaria/react'; +import { t } from '@lingui/core/macro'; import camelCase from 'lodash.camelcase'; -import { useIcons } from 'twenty-ui/display'; import { type SelectOption } from 'twenty-ui/input'; import { themeCssVariables } from 'twenty-ui/theme-constants'; @@ -28,16 +28,16 @@ export const WorkflowFormFieldSettingsRecordPicker = ({ field, onChange, }: WorkflowFormFieldSettingsRecordPickerProps) => { - const { getIcon } = useIcons(); - + const { getSelectIconPropsFromObjectMetadataItem } = + useObjectMetadataSelectHelpers(); const { activeNonSystemObjectMetadataItems } = useFilteredObjectMetadataItems(); const availableMetadata: Array> = activeNonSystemObjectMetadataItems.map((item) => ({ - Icon: getIcon(item.icon), label: item.labelPlural, value: item.nameSingular, + ...getSelectIconPropsFromObjectMetadataItem(item), })); return ( diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerDatabaseEventForm.tsx b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerDatabaseEventForm.tsx index 619ca8155f..1f349c2436 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerDatabaseEventForm.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerDatabaseEventForm.tsx @@ -1,4 +1,5 @@ import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; +import { useObjectMetadataSelectHelpers } from '@/object-metadata/hooks/useObjectMetadataSelectHelpers'; import { type FieldMultiSelectValue } from '@/object-record/record-field/ui/types/FieldMetadata'; import { SelectControl } from '@/ui/input/components/SelectControl'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; @@ -20,7 +21,7 @@ import { Trans, useLingui } from '@lingui/react/macro'; import { useCallback, useMemo, useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { TRIGGER_STEP_ID } from 'twenty-shared/workflow'; -import { IconChevronLeft, IconSettings, useIcons } from 'twenty-ui/display'; +import { IconChevronLeft, IconSettings } from 'twenty-ui/display'; import { MenuItem } from 'twenty-ui/navigation'; import { themeCssVariables } from 'twenty-ui/theme-constants'; @@ -63,8 +64,9 @@ export const WorkflowEditTriggerDatabaseEventForm = ({ trigger, triggerOptions, }: WorkflowEditTriggerDatabaseEventFormProps) => { - const { getIcon } = useIcons(); const { t } = useLingui(); + const { getSelectIconPropsFromObjectMetadataItem } = + useObjectMetadataSelectHelpers(); const [searchInputValue, setSearchInputValue] = useState(''); const [isSystemObjectsOpen, setIsSystemObjectsOpen] = useState(false); const dropdownId = 'workflow-edit-trigger-record-type'; @@ -90,7 +92,7 @@ export const WorkflowEditTriggerDatabaseEventForm = ({ .map((item) => ({ label: item.labelPlural, value: item.nameSingular, - Icon: getIcon(item.icon), + ...getSelectIconPropsFromObjectMetadataItem(item), })); const systemObjects = objectMetadataItems @@ -98,7 +100,7 @@ export const WorkflowEditTriggerDatabaseEventForm = ({ .map((item) => ({ label: item.labelPlural, value: item.nameSingular, - Icon: getIcon(item.icon), + ...getSelectIconPropsFromObjectMetadataItem(item), })); const selectedOption = diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerManual.tsx b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerManual.tsx index 1f052b00a8..83a73ff185 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerManual.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerManual.tsx @@ -1,5 +1,5 @@ -import { isDefined } from 'twenty-shared/utils'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; +import { useObjectMetadataSelectHelpers } from '@/object-metadata/hooks/useObjectMetadataSelectHelpers'; import { IconPicker } from '@/ui/input/components/IconPicker'; import { Select } from '@/ui/input/components/Select'; import { SelectControl } from '@/ui/input/components/SelectControl'; @@ -13,6 +13,7 @@ import { getManualTriggerDefaultSettings } from '@/workflow/workflow-trigger/uti import { styled } from '@linaria/react'; import { useLingui } from '@lingui/react/macro'; import { QUERY_MAX_RECORDS } from 'twenty-shared/constants'; +import { isDefined } from 'twenty-shared/utils'; import { TRIGGER_STEP_ID } from 'twenty-shared/workflow'; import { useIcons } from 'twenty-ui/display'; import { type SelectOption } from 'twenty-ui/input'; @@ -56,6 +57,8 @@ export const WorkflowEditTriggerManual = ({ const { t } = useLingui(); const { getIcon } = useIcons(); + const { getSelectIconPropsFromObjectMetadataItem } = + useObjectMetadataSelectHelpers(); const maxRecordsFormatted = QUERY_MAX_RECORDS.toLocaleString(); const { activeNonSystemObjectMetadataItems } = @@ -65,7 +68,7 @@ export const WorkflowEditTriggerManual = ({ activeNonSystemObjectMetadataItems.map((item) => ({ label: item.labelPlural, value: item.nameSingular, - Icon: getIcon(item.icon), + ...getSelectIconPropsFromObjectMetadataItem(item), })); const availability = trigger.settings.availability; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownStepItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownStepItems.tsx index b404ac4f17..9f534bf714 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownStepItems.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownStepItems.tsx @@ -4,9 +4,11 @@ import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/Dropdow import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { useObjectMetadataSelectHelpers } from '@/object-metadata/hooks/useObjectMetadataSelectHelpers'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; +import { useVariableDropdown } from '@/workflow/workflow-variables/hooks/useVariableDropdown'; import { isRecordOutputSchemaV2 } from '@/workflow/workflow-variables/types/guards/isRecordOutputSchemaV2'; import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2'; import { getCurrentSubStepFromPath } from '@/workflow/workflow-variables/utils/getCurrentSubStepFromPath'; @@ -21,7 +23,6 @@ import { useIcons, } from 'twenty-ui/display'; import { MenuItemSelect } from 'twenty-ui/navigation'; -import { useVariableDropdown } from '@/workflow/workflow-variables/hooks/useVariableDropdown'; type WorkflowVariablesDropdownStepItemsProps = { step: StepOutputSchemaV2; @@ -38,6 +39,8 @@ export const WorkflowVariablesDropdownStepItems = ({ }: WorkflowVariablesDropdownStepItemsProps) => { const { t } = useLingui(); const { getIcon } = useIcons(); + const { getSelectIconPropsFromObjectMetadataItem } = + useObjectMetadataSelectHelpers(); const { searchInputValue, setSearchInputValue, @@ -97,6 +100,12 @@ export const WorkflowVariablesDropdownStepItems = ({ const shouldDisplaySubStepObject = shouldDisplayRecordObjects && isObjectFoundThroughSearch; + const displayedSubStepObjectIconProps = isDefined( + displayedSubStepObjectMetadata, + ) + ? getSelectIconPropsFromObjectMetadataItem(displayedSubStepObjectMetadata) + : undefined; + return ( )} diff --git a/packages/twenty-front/src/pages/settings/ai/components/SettingsToolIcon.tsx b/packages/twenty-front/src/pages/settings/ai/components/SettingsToolIcon.tsx index 6e01b41000..37d898b78b 100644 --- a/packages/twenty-front/src/pages/settings/ai/components/SettingsToolIcon.tsx +++ b/packages/twenty-front/src/pages/settings/ai/components/SettingsToolIcon.tsx @@ -1,19 +1,19 @@ import { styled } from '@linaria/react'; import { useContext } from 'react'; -import { getNavigationMenuItemIconStyleFromColor } from '@/navigation-menu-item/common/utils/getNavigationMenuItemIconStyleFromColor'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; import { isDefined } from 'twenty-shared/utils'; import { Avatar, - type IconComponent, + getIconTileColorShades, IconCode, IconEdit, IconPlus, IconSearch, IconTrash, useIcons, + type IconComponent, } from 'twenty-ui/display'; import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; @@ -134,9 +134,7 @@ export const SettingsToolIcon = ({ : undefined; const objectStyle = isDefined(objectMetadata) - ? getNavigationMenuItemIconStyleFromColor( - getObjectColorWithFallback(objectMetadata), - ) + ? getIconTileColorShades(getObjectColorWithFallback(objectMetadata)) : null; if (isDefined(OperationIcon)) { diff --git a/packages/twenty-ui/src/display/index.ts b/packages/twenty-ui/src/display/index.ts index 47072ecc53..1c0adee7ef 100644 --- a/packages/twenty-ui/src/display/index.ts +++ b/packages/twenty-ui/src/display/index.ts @@ -515,6 +515,11 @@ export { Info } from './info/components/Info'; export { Status } from './status/components/Status'; export { HorizontalSeparator } from './text/components/HorizontalSeparator'; export { SeparatorLineText } from './text/components/SeparatorLineText'; +export { StyledTintedIconTileContainer } from './tinted-icon-tile/components/StyledTintedIconTileContainer'; +export type { TintedIconTileProps } from './tinted-icon-tile/components/TintedIconTile'; +export { TintedIconTile } from './tinted-icon-tile/components/TintedIconTile'; +export type { IconTileColorShades } from './tinted-icon-tile/utils/getIconTileColorShades'; +export { getIconTileColorShades } from './tinted-icon-tile/utils/getIconTileColorShades'; export type { AppTooltipProps } from './tooltip/AppTooltip'; export { TooltipPosition, diff --git a/packages/twenty-ui/src/display/status/components/Status.tsx b/packages/twenty-ui/src/display/status/components/Status.tsx index 9cfb3126f3..23da012789 100644 --- a/packages/twenty-ui/src/display/status/components/Status.tsx +++ b/packages/twenty-ui/src/display/status/components/Status.tsx @@ -1,12 +1,8 @@ import { styled } from '@linaria/react'; import { Loader } from '@ui/feedback/loader/components/Loader'; -import { type ThemeColor, MAIN_COLOR_NAMES } from '@ui/theme'; +import { type ThemeColor } from '@ui/theme'; import { themeCssVariables } from '@ui/theme-constants'; - -const parseThemeColor = (color: string): ThemeColor => - (MAIN_COLOR_NAMES as string[]).includes(color) - ? (color as ThemeColor) - : 'gray'; +import { parseThemeColor } from '@ui/utilities'; const StyledStatus = styled.h3<{ color: ThemeColor; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemIconContainer.tsx b/packages/twenty-ui/src/display/tinted-icon-tile/components/StyledTintedIconTileContainer.tsx similarity index 58% rename from packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemIconContainer.tsx rename to packages/twenty-ui/src/display/tinted-icon-tile/components/StyledTintedIconTileContainer.tsx index bc917d4023..e3959a3fa3 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemIconContainer.tsx +++ b/packages/twenty-ui/src/display/tinted-icon-tile/components/StyledTintedIconTileContainer.tsx @@ -1,9 +1,10 @@ import { styled } from '@linaria/react'; -import { themeCssVariables } from 'twenty-ui/theme-constants'; +import { themeCssVariables } from '@ui/theme-constants'; -export const StyledNavigationMenuItemIconContainer = styled.div<{ +export const StyledTintedIconTileContainer = styled.div<{ $backgroundColor?: string; $borderColor?: string; + $dimension?: string; }>` align-items: center; background-color: ${({ $backgroundColor }) => @@ -14,8 +15,8 @@ export const StyledNavigationMenuItemIconContainer = styled.div<{ box-sizing: border-box; display: flex; flex-shrink: 0; - height: ${themeCssVariables.spacing[4]}; + height: ${({ $dimension }) => $dimension ?? themeCssVariables.spacing[4]}; justify-content: center; - width: ${themeCssVariables.spacing[4]}; + width: ${({ $dimension }) => $dimension ?? themeCssVariables.spacing[4]}; `; diff --git a/packages/twenty-ui/src/display/tinted-icon-tile/components/TintedIconTile.tsx b/packages/twenty-ui/src/display/tinted-icon-tile/components/TintedIconTile.tsx new file mode 100644 index 0000000000..eaf4e650ba --- /dev/null +++ b/packages/twenty-ui/src/display/tinted-icon-tile/components/TintedIconTile.tsx @@ -0,0 +1,39 @@ +import { type IconComponent } from '@ui/display/icon/types/IconComponent'; +import { StyledTintedIconTileContainer } from '@ui/display/tinted-icon-tile/components/StyledTintedIconTileContainer'; +import { getIconTileColorShades } from '@ui/display/tinted-icon-tile/utils/getIconTileColorShades'; +import { DEFAULT_THEME_COLOR_FALLBACK } from '@ui/theme'; +import { ThemeContext } from '@ui/theme-constants'; +import { useContext } from 'react'; +import { isDefined } from 'twenty-shared/utils'; + +export type TintedIconTileProps = { + Icon: IconComponent; + color?: string | null; + size?: number; + stroke?: number; +}; + +export const TintedIconTile = ({ + Icon, + color = DEFAULT_THEME_COLOR_FALLBACK, + size: sizeFromProps, + stroke: strokeFromProps, +}: TintedIconTileProps) => { + const { theme } = useContext(ThemeContext); + const style = getIconTileColorShades(color); + const iconSize = sizeFromProps ?? theme.icon.size.md; + const iconStroke = strokeFromProps ?? theme.icon.stroke.md; + const tileDimension = isDefined(sizeFromProps) + ? `${sizeFromProps}px` + : undefined; + + return ( + + + + ); +}; diff --git a/packages/twenty-ui/src/display/tinted-icon-tile/internal/constants/tintedIconTileColorShades.constant.ts b/packages/twenty-ui/src/display/tinted-icon-tile/internal/constants/tintedIconTileColorShades.constant.ts new file mode 100644 index 0000000000..3d2f44f3a6 --- /dev/null +++ b/packages/twenty-ui/src/display/tinted-icon-tile/internal/constants/tintedIconTileColorShades.constant.ts @@ -0,0 +1,5 @@ +export const TINTED_ICON_TILE_COLOR_SHADES = { + background: 5, + border: 6, + icon: 11, +} as const; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getColorFromTheme.ts b/packages/twenty-ui/src/display/tinted-icon-tile/internal/utils/getColorFromTheme.ts similarity index 75% rename from packages/twenty-front/src/modules/navigation-menu-item/common/utils/getColorFromTheme.ts rename to packages/twenty-ui/src/display/tinted-icon-tile/internal/utils/getColorFromTheme.ts index aa30425cb4..6325f91f15 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getColorFromTheme.ts +++ b/packages/twenty-ui/src/display/tinted-icon-tile/internal/utils/getColorFromTheme.ts @@ -1,5 +1,5 @@ -import { themeCssVariables } from 'twenty-ui/theme-constants'; -import { type ThemeColor } from 'twenty-ui/theme'; +import { type ThemeColor } from '@ui/theme'; +import { themeCssVariables } from '@ui/theme-constants'; export const getColorFromTheme = ( themeColor: ThemeColor, diff --git a/packages/twenty-ui/src/display/tinted-icon-tile/utils/getIconTileColorShades.ts b/packages/twenty-ui/src/display/tinted-icon-tile/utils/getIconTileColorShades.ts new file mode 100644 index 0000000000..0c225e87a6 --- /dev/null +++ b/packages/twenty-ui/src/display/tinted-icon-tile/utils/getIconTileColorShades.ts @@ -0,0 +1,29 @@ +import { TINTED_ICON_TILE_COLOR_SHADES } from '@ui/display/tinted-icon-tile/internal/constants/tintedIconTileColorShades.constant'; +import { getColorFromTheme } from '@ui/display/tinted-icon-tile/internal/utils/getColorFromTheme'; +import { parseThemeColor } from '@ui/utilities'; + +export type IconTileColorShades = { + backgroundColor: string; + iconColor: string; + borderColor: string; +}; + +export const getIconTileColorShades = ( + color: string | null | undefined, +): IconTileColorShades => { + const themeColor = parseThemeColor(color); + return { + backgroundColor: getColorFromTheme( + themeColor, + TINTED_ICON_TILE_COLOR_SHADES.background, + ), + iconColor: getColorFromTheme( + themeColor, + TINTED_ICON_TILE_COLOR_SHADES.icon, + ), + borderColor: getColorFromTheme( + themeColor, + TINTED_ICON_TILE_COLOR_SHADES.border, + ), + }; +}; diff --git a/packages/twenty-ui/src/input/types/SelectOption.ts b/packages/twenty-ui/src/input/types/SelectOption.ts index 3b3f9d39f2..e2a5190f18 100644 --- a/packages/twenty-ui/src/input/types/SelectOption.ts +++ b/packages/twenty-ui/src/input/types/SelectOption.ts @@ -5,6 +5,7 @@ export type SelectOption< Value extends string | number | boolean | null = string, > = { Icon?: IconComponent | null; + iconThemeColor?: ThemeColor | null; label: string; fullLabel?: string; value: Value; diff --git a/packages/twenty-ui/src/navigation/menu/menu-item/components/MenuItem.tsx b/packages/twenty-ui/src/navigation/menu/menu-item/components/MenuItem.tsx index e7530f46ce..d79a2f04a3 100644 --- a/packages/twenty-ui/src/navigation/menu/menu-item/components/MenuItem.tsx +++ b/packages/twenty-ui/src/navigation/menu/menu-item/components/MenuItem.tsx @@ -11,6 +11,7 @@ import { import { styled } from '@linaria/react'; import { MenuItemHotKeys } from '@ui/navigation/menu/menu-item/components/MenuItemHotKeys'; +import { type ThemeColor } from '@ui/theme'; import { ThemeContext } from '@ui/theme-constants'; import { motion } from 'framer-motion'; import { type Nullable } from 'twenty-shared/types'; @@ -39,6 +40,7 @@ export type MenuItemProps = { isIconDisplayedOnHoverOnly?: boolean; isTooltipOpen?: boolean; LeftIcon?: IconComponent | null; + iconThemeColor?: ThemeColor | null; LeftComponent?: ReactNode; RightIcon?: IconComponent | null; RightComponent?: ReactNode; @@ -71,6 +73,7 @@ export const MenuItem = ({ iconButtons, isIconDisplayedOnHoverOnly = true, LeftIcon, + iconThemeColor, LeftComponent, RightIcon, RightComponent, @@ -113,6 +116,7 @@ export const MenuItem = ({ > diff --git a/packages/twenty-ui/src/navigation/menu/menu-item/components/MenuItemSelect.tsx b/packages/twenty-ui/src/navigation/menu/menu-item/components/MenuItemSelect.tsx index 08ce106b50..b15b1a59b7 100644 --- a/packages/twenty-ui/src/navigation/menu/menu-item/components/MenuItemSelect.tsx +++ b/packages/twenty-ui/src/navigation/menu/menu-item/components/MenuItemSelect.tsx @@ -7,8 +7,9 @@ import { OverflowingTextWithTooltip, type IconComponent, } from '@ui/display'; -import { type ReactNode, useContext } from 'react'; +import { type ThemeColor } from '@ui/theme'; import { ThemeContext, themeCssVariables } from '@ui/theme-constants'; +import { useContext, type ReactNode } from 'react'; import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent'; import { StyledMenuItemLabel, @@ -60,6 +61,7 @@ export const StyledMenuItemSelect = styled.div<{ type MenuItemSelectProps = { LeftIcon?: IconComponent | null | undefined; + leftIconColor?: ThemeColor | null; withIconContainer?: boolean; selected: boolean; needIconCheck?: boolean; @@ -75,6 +77,7 @@ type MenuItemSelectProps = { export const MenuItemSelect = ({ LeftIcon, + leftIconColor, withIconContainer = false, text, selected, @@ -101,6 +104,7 @@ export const MenuItemSelect = ({ > { @@ -20,6 +24,17 @@ export const MenuItemIcon = ({ return null; } + if (isDefined(iconThemeColor)) { + return ( + + ); + } + const iconElement = ( ); diff --git a/packages/twenty-ui/src/navigation/menu/menu-item/internals/components/MenuItemIconWithGripSwap.tsx b/packages/twenty-ui/src/navigation/menu/menu-item/internals/components/MenuItemIconWithGripSwap.tsx index df8d051217..9c81c160c7 100644 --- a/packages/twenty-ui/src/navigation/menu/menu-item/internals/components/MenuItemIconWithGripSwap.tsx +++ b/packages/twenty-ui/src/navigation/menu/menu-item/internals/components/MenuItemIconWithGripSwap.tsx @@ -1,7 +1,13 @@ import { styled } from '@linaria/react'; -import { type IconComponent, IconGripVertical } from '@ui/display'; +import { + type IconComponent, + IconGripVertical, + TintedIconTile, +} from '@ui/display'; +import { type ThemeColor } from '@ui/theme'; import { ThemeContext, themeCssVariables } from '@ui/theme-constants'; import { useContext } from 'react'; +import { isDefined } from 'twenty-shared/utils'; import { MenuItemIconBoxContainer } from './MenuItemIconBoxContainer'; const StyledIconSwapContainer = styled.div` @@ -27,12 +33,14 @@ const StyledHoverIcon = styled.div` export type MenuItemIconWithGripSwapProps = { LeftIcon: IconComponent | null | undefined; + iconThemeColor?: ThemeColor | null; withIconContainer?: boolean; gripIconColor: string; }; export const MenuItemIconWithGripSwap = ({ LeftIcon, + iconThemeColor, withIconContainer = false, gripIconColor, }: MenuItemIconWithGripSwapProps) => { @@ -45,7 +53,16 @@ export const MenuItemIconWithGripSwap = ({ const iconContent = ( - + {isDefined(iconThemeColor) ? ( + + ) : ( + + )} ); - if (withIconContainer) { + if (withIconContainer && !isDefined(iconThemeColor)) { return {iconContent}; } diff --git a/packages/twenty-ui/src/navigation/menu/menu-item/internals/components/MenuItemLeftContent.tsx b/packages/twenty-ui/src/navigation/menu/menu-item/internals/components/MenuItemLeftContent.tsx index de2273c2a5..bf768f39f5 100644 --- a/packages/twenty-ui/src/navigation/menu/menu-item/internals/components/MenuItemLeftContent.tsx +++ b/packages/twenty-ui/src/navigation/menu/menu-item/internals/components/MenuItemLeftContent.tsx @@ -7,6 +7,7 @@ import { IconGripVertical, OverflowingTextWithTooltip, } from '@ui/display'; +import { type ThemeColor } from '@ui/theme'; import { ThemeContext, themeCssVariables } from '@ui/theme-constants'; import { type MenuItemDraggableGripMode } from '../../types/MenuItemDraggableGripMode'; import { MenuItemIcon } from './MenuItemIcon'; @@ -42,6 +43,7 @@ type MenuItemLeftContentProps = { className?: string; LeftComponent?: ReactNode; LeftIcon: IconComponent | null | undefined; + iconThemeColor?: ThemeColor | null; withIconContainer?: boolean; withIconContainerBackground?: boolean; gripMode?: MenuItemDraggableGripMode; @@ -55,6 +57,7 @@ export const MenuItemLeftContent = ({ className, LeftComponent, LeftIcon, + iconThemeColor, withIconContainer = false, withIconContainerBackground = true, text, @@ -96,12 +99,14 @@ export const MenuItemLeftContent = ({ {gripMode === 'onHover' ? ( ) : ( diff --git a/packages/twenty-ui/src/theme/constants/DefaultThemeColorFallback.ts b/packages/twenty-ui/src/theme/constants/DefaultThemeColorFallback.ts new file mode 100644 index 0000000000..ee3e470f07 --- /dev/null +++ b/packages/twenty-ui/src/theme/constants/DefaultThemeColorFallback.ts @@ -0,0 +1,3 @@ +import { type ThemeColor } from './MainColorNames'; + +export const DEFAULT_THEME_COLOR_FALLBACK: ThemeColor = 'gray'; diff --git a/packages/twenty-ui/src/theme/index.ts b/packages/twenty-ui/src/theme/index.ts index f004e40070..e134b8ccf6 100644 --- a/packages/twenty-ui/src/theme/index.ts +++ b/packages/twenty-ui/src/theme/index.ts @@ -24,6 +24,7 @@ export { CODE_DARK } from './constants/CodeDark'; export { CODE_LIGHT } from './constants/CodeLight'; export { COLOR_DARK } from './constants/ColorsDark'; export { COLOR_LIGHT } from './constants/ColorsLight'; +export { DEFAULT_THEME_COLOR_FALLBACK } from './constants/DefaultThemeColorFallback'; export { FONT_COMMON } from './constants/FontCommon'; export { FONT_DARK } from './constants/FontDark'; export { FONT_LIGHT } from './constants/FontLight'; diff --git a/packages/twenty-ui/src/utilities/color/utils/parseThemeColor.ts b/packages/twenty-ui/src/utilities/color/utils/parseThemeColor.ts new file mode 100644 index 0000000000..f6a3d656b7 --- /dev/null +++ b/packages/twenty-ui/src/utilities/color/utils/parseThemeColor.ts @@ -0,0 +1,10 @@ +import { DEFAULT_THEME_COLOR_FALLBACK, type ThemeColor } from '@ui/theme'; + +import { themeColorSchema } from './themeColorSchema'; + +export const parseThemeColor = ( + color: string | null | undefined, +): ThemeColor => { + const result = themeColorSchema.safeParse(color ?? ''); + return result.success ? result.data : DEFAULT_THEME_COLOR_FALLBACK; +}; diff --git a/packages/twenty-ui/src/utilities/index.ts b/packages/twenty-ui/src/utilities/index.ts index 71c09cc407..f4e12cc18b 100644 --- a/packages/twenty-ui/src/utilities/index.ts +++ b/packages/twenty-ui/src/utilities/index.ts @@ -17,6 +17,7 @@ export { AnimatedRotate } from './animation/components/AnimatedRotate'; export { AnimatedTextWord } from './animation/components/AnimatedTextWord'; export { AnimatedTranslation } from './animation/components/AnimatedTranslation'; export { getMainColorNameFromPaletteColorName } from './color/utils/getMainColorNameFromPaletteColorName'; +export { parseThemeColor } from './color/utils/parseThemeColor'; export { stringToThemeColor, stringToThemeColorP3String,