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>
This commit is contained in:
Abdul Rahman
2026-04-10 15:44:20 +05:30
committed by GitHub
parent aed81a54a2
commit 34a903b4fa
81 changed files with 587 additions and 456 deletions
@@ -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 <IconRestore />;
}
const IconComponent = getIcon(linkedObjectMetadataItem?.icon);
return <IconComponent />;
return <ObjectMetadataIcon objectMetadataItem={linkedObjectMetadataItem} />;
};
@@ -1,3 +0,0 @@
import type { ThemeColor } from 'twenty-ui/theme';
export const DEFAULT_NAV_ITEM_ICON_COLOR: ThemeColor = 'gray';
@@ -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;
};
@@ -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),
};
};
@@ -1 +0,0 @@
export const COLOR_SHADE_BACKGROUND = 5;
@@ -1 +0,0 @@
export const COLOR_SHADE_BORDER = 6;
@@ -1 +0,0 @@
export const COLOR_SHADE_ICON = 11;
@@ -1,5 +0,0 @@
export type NavigationMenuItemIconStyle = {
backgroundColor: string;
iconColor: string;
borderColor: string;
};
@@ -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;
};
@@ -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 (
<StyledNavigationMenuItemIconContainer
<StyledTintedIconTileContainer
$backgroundColor={iconStyle.backgroundColor}
$borderColor={iconStyle.borderColor}
>
{avatar}
</StyledNavigationMenuItemIconContainer>
</StyledTintedIconTileContainer>
);
};
@@ -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 (
<StyledNavigationMenuItemIconContainer
$backgroundColor={style.backgroundColor}
$borderColor={style.borderColor}
>
<Icon
size={theme.icon.size.md}
stroke={theme.icon.stroke.md}
color={style.iconColor}
/>
</StyledNavigationMenuItemIconContainer>
);
};
@@ -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 ? (
<NavigationMenuItemStyleIcon Icon={icon} color={effectiveColor} />
<TintedIconTile Icon={icon} color={effectiveColor} />
) : (
<AddToNavigationDragHandleIcon
icon={icon}
@@ -1,12 +1,11 @@
import { styled } from '@linaria/react';
import { useContext, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import type { IconComponent } from 'twenty-ui/display';
import { type IconComponent, getIconTileColorShades } from 'twenty-ui/display';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK } from '@/navigation-menu-item/common/constants/NavigationMenuItemDefaultColorLink';
import { getLinkFaviconUrl } from '@/navigation-menu-item/display/link/utils/getLinkFaviconUrl';
import { getNavigationMenuItemIconStyleFromColor } from '@/navigation-menu-item/common/utils/getNavigationMenuItemIconStyleFromColor';
const failedFaviconUrls = new Set<string>();
@@ -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,
);
@@ -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 (
<StyledCompositeContainer>
@@ -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 = () => {
>
<CommandMenuItem
LeftComponent={
<NavigationMenuItemStyleIcon
Icon={FolderIcon}
color={folderColor}
/>
<TintedIconTile Icon={FolderIcon} color={folderColor} />
}
label={folder.name}
id={folder.id}
@@ -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 & {
@@ -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';
@@ -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 = ({
>
<CommandMenuItem
Icon={() => (
<NavigationMenuItemStyleIcon
<TintedIconTile
Icon={IconBuildingSkyscraper}
color="blue"
/>
@@ -86,12 +86,7 @@ export const SidePanelNewSidebarItemMainMenu = ({
onEnter={onSelectView}
>
<CommandMenuItem
Icon={() => (
<NavigationMenuItemStyleIcon
Icon={IconTable}
color="gray"
/>
)}
Icon={() => <TintedIconTile Icon={IconTable} />}
label={t`View`}
id={NavigationMenuItemType.VIEW}
hasSubMenu={true}
@@ -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 = ({
>
<CommandMenuItem
Icon={() => (
<NavigationMenuItemStyleIcon
Icon={getIcon(objectMetadataItem.icon)}
color={getObjectColorWithFallback(objectMetadataItem)}
/>
<ObjectMetadataIcon objectMetadataItem={objectMetadataItem} />
)}
label={objectMetadataItem.labelPlural}
id={objectMetadataItem.id}
@@ -76,7 +71,7 @@ export const SidePanelNewSidebarItemViewObjectPickerSubView = ({
{showSystemObjectsOption && (
<SelectableListItem itemId="system" onEnter={onOpenSystemPicker}>
<CommandMenuItem
Icon={() => <NavigationMenuItemStyleIcon Icon={IconSettings} />}
Icon={() => <TintedIconTile Icon={IconSettings} />}
label={t`System objects`}
id="system"
hasSubMenu={true}
@@ -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 = ({
>
<CommandMenuItem
Icon={() => (
<NavigationMenuItemStyleIcon
Icon={getIcon(objectMetadataItem.icon)}
color={getObjectColorWithFallback(objectMetadataItem)}
/>
<ObjectMetadataIcon objectMetadataItem={objectMetadataItem} />
)}
label={objectMetadataItem.labelPlural}
id={objectMetadataItem.id}
@@ -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 = () => (
<NavigationMenuItemStyleIcon Icon={Icon} color={iconColor} />
<ObjectMetadataIcon objectMetadataItem={objectMetadataItem} />
);
return (
@@ -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 (
<SelectableListItem
@@ -39,10 +35,7 @@ export const SidePanelObjectPickerItem = ({
>
<CommandMenuItem
Icon={() => (
<NavigationMenuItemStyleIcon
Icon={getIcon(objectMetadataItem.icon)}
color={getObjectColorWithFallback(objectMetadataItem)}
/>
<ObjectMetadataIcon objectMetadataItem={objectMetadataItem} />
)}
label={objectMetadataItem.labelPlural}
id={objectMetadataItem.id}
@@ -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 = ({
))}
<SelectableListItem itemId="system" onEnter={onOpenSystemPicker}>
<CommandMenuItem
Icon={() => <NavigationMenuItemStyleIcon Icon={IconSettings} />}
Icon={() => <TintedIconTile Icon={IconSettings} />}
label={t`System objects`}
id="system"
hasSubMenu
@@ -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 (
<TintedIconTile
Icon={Icon}
color={getObjectColorWithFallback(objectMetadataItem)}
size={size}
stroke={stroke}
/>
);
};
@@ -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 };
};
@@ -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<EnrichedObjectMetadataItem, 'nameSingular' | 'color' | 'isSystem'>
| null
| undefined,
): ThemeColor => {
if (!isDefined(objectMetadataItem)) {
return SYSTEM_OBJECT_COLOR;
}
if (objectMetadataItem.isSystem) {
return SYSTEM_OBJECT_COLOR;
}
@@ -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 <NavigationMenuItemStyleIcon Icon={ObjectIcon} color={iconColor} />;
return <ObjectMetadataIcon objectMetadataItem={objectMetadataItem} />;
};
@@ -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) && (
<NavigationMenuItemStyleIcon Icon={HeaderIcon} color={iconColor} />
)}
<StyledBreadcrumbPrefixObjectIcon>
<ObjectMetadataIcon objectMetadataItem={objectMetadataItem} />
</StyledBreadcrumbPrefixObjectIcon>
{objectLabel}
<span>{' / '}</span>
</StyledEditableTitlePrefix>
@@ -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,
};
};
@@ -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<string[]>(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 = ({
<MenuItemMultiSelect
className=""
LeftIcon={option.Icon ?? undefined}
iconThemeColor={option.iconThemeColor}
text={option.label}
selected={selectedObjectMetadataIds.some(
(selectedObjectMetadataId) =>
@@ -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 (
<StyledRow>
{isDefined(Icon) && <Icon size={theme.icon.size.md} />}
{isDefined(relatedObject) && (
<ObjectMetadataIcon
objectMetadataItem={relatedObject}
size={theme.icon.size.md}
/>
)}
<StyledFieldName>{relatedObject?.labelPlural ?? ''}</StyledFieldName>
<Handle
type={
@@ -1,20 +1,21 @@
import { useContext, useState } from 'react';
import { styled } from '@linaria/react';
import { type Node, type NodeProps } from '@xyflow/react';
import { useContext, useState } from 'react';
import { Link } from 'react-router-dom';
import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon';
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
import { isHiddenSystemField } from '@/object-metadata/utils/isHiddenSystemField';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { ObjectFieldRow } from '@/settings/data-model/graph-overview/components/SettingsDataModelOverviewField';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { SettingsItemTypeTag } from '@/settings/components/SettingsItemTypeTag';
import { ObjectFieldRowWithoutRelation } from '@/settings/data-model/graph-overview/components/SettingsDataModelOverviewFieldWithoutRelation';
import '@xyflow/react/dist/style.css';
import { SettingsPath } from 'twenty-shared/types';
import { isDefined, getSettingsPath } from 'twenty-shared/utils';
import { IconChevronDown, IconChevronUp, useIcons } from 'twenty-ui/display';
import { SettingsItemTypeTag } from '@/settings/components/SettingsItemTypeTag';
import { getSettingsPath } from 'twenty-shared/utils';
import { IconChevronDown, IconChevronUp } from 'twenty-ui/display';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
type SettingsDataModelOverviewObjectNode = Node<
@@ -107,7 +108,6 @@ export const SettingsDataModelOverviewObject = ({
data: objectMetadataItem,
}: SettingsDataModelOverviewObjectProps) => {
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 (
<StyledNode>
<StyledHeader>
@@ -134,7 +132,10 @@ export const SettingsDataModelOverviewObject = ({
objectNamePlural: objectMetadataItem.namePlural,
})}
>
{isDefined(Icon) && <Icon size={theme.icon.size.md} />}
<ObjectMetadataIcon
objectMetadataItem={objectMetadataItem}
size={theme.icon.size.md}
/>
{objectMetadataItem.labelPlural}
</Link>
</StyledObjectLinkContainer>
@@ -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 (
<TableRow
@@ -49,7 +46,10 @@ export const SettingsAvailableStandardObjectItemTableRow = ({
color={themeCssVariables.font.color.primary}
gap={themeCssVariables.spacing[2]}
>
{isDefined(Icon) && <Icon size={theme.icon.size.md} />}
<ObjectMetadataIcon
objectMetadataItem={objectItem}
size={theme.icon.size.md}
/>
{objectItem.labelPlural}
</TableCell>
<TableCell>
@@ -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 (
<TableRow
@@ -73,15 +69,11 @@ export const SettingsObjectMetadataItemTableRow = ({
>
<StyledStickyFirstCell>
<StyledNameTableCell>
{isDefined(Icon) && (
<Icon
style={{
minWidth: theme.icon.size.md,
}}
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
)}
<ObjectMetadataIcon
objectMetadataItem={objectMetadataItem}
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
<StyledNameContainer>
<StyledNameLabel title={objectMetadataItem.labelPlural}>
{objectMetadataItem.labelPlural}
@@ -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';
@@ -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 = ({
<StyledObjectPreview key={`${objectMetadataItem.labelSingular}-${index}`}>
<StyledObjectName>
<StyledIconContainer>
<ObjectIcon
<ObjectMetadataIcon
objectMetadataItem={objectMetadataItem}
size={theme.icon.size.sm}
stroke={theme.icon.stroke.md}
/>
@@ -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';
@@ -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<string>[] = [
{ 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 = ({
>
<MenuItemSelect
LeftIcon={option.Icon}
leftIconColor={option.iconThemeColor}
text={option.label}
selected={value === option.value}
focused={selectedItemId === option.value}
@@ -1,14 +1,15 @@
/* oxlint-disable twenty/no-navigate-prefer-link */
import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon';
import { SettingsCard } from '@/settings/components/SettingsCard';
import { useFilterObjectMetadataItemsWithPermissionOverride } from '@/settings/roles/role-permissions/object-level-permissions/hooks/useFilterObjectWithPermissionOverride';
import { useObjectMetadataItemsThatCanHavePermission } from '@/settings/roles/role-permissions/object-level-permissions/hooks/useObjectMetadataItemsThatCanHavePermission';
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
import { useContext, useState } from 'react';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { useContext, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { SettingsPath } from 'twenty-shared/types';
import { H2Title, IconSearch, useIcons } from 'twenty-ui/display';
import { H2Title, IconSearch } from 'twenty-ui/display';
import { Section } from 'twenty-ui/layout';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
@@ -60,8 +61,6 @@ export const SettingsRolePermissionsObjectLevelObjectPicker = ({
const { objectMetadataItemsThatCanHavePermission } =
useObjectMetadataItemsThatCanHavePermission();
const { getIcon } = useIcons();
const handleSearchChange = (text: string) => {
setSearchFilter(text);
};
@@ -125,27 +124,25 @@ export const SettingsRolePermissionsObjectLevelObjectPicker = ({
description={t`All the standard objects`}
/>
<StyledContainer>
{standardObjects.map((objectMetadataItem) => {
const Icon = getIcon(objectMetadataItem.icon);
return (
<StyledCardContainer
key={objectMetadataItem.id}
onClick={() =>
handleSelectObjectMetadata(objectMetadataItem.id)
{standardObjects.map((objectMetadataItem) => (
<StyledCardContainer
key={objectMetadataItem.id}
onClick={() =>
handleSelectObjectMetadata(objectMetadataItem.id)
}
>
<SettingsCard
Icon={
<ObjectMetadataIcon
objectMetadataItem={objectMetadataItem}
size={theme.icon.size.lg}
stroke={theme.icon.stroke.sm}
/>
}
>
<SettingsCard
Icon={
<Icon
size={theme.icon.size.lg}
stroke={theme.icon.stroke.sm}
/>
}
title={objectMetadataItem.labelPlural}
/>
</StyledCardContainer>
);
})}
title={objectMetadataItem.labelPlural}
/>
</StyledCardContainer>
))}
</StyledContainer>
</Section>
)}
@@ -153,28 +150,25 @@ export const SettingsRolePermissionsObjectLevelObjectPicker = ({
<Section>
<H2Title title={t`Custom`} description={t`All your custom objects`} />
<StyledContainer>
{customObjects.map((objectMetadataItem) => {
const Icon = getIcon(objectMetadataItem.icon);
return (
<StyledCardContainer
key={objectMetadataItem.id}
onClick={() =>
handleSelectObjectMetadata(objectMetadataItem.id)
{customObjects.map((objectMetadataItem) => (
<StyledCardContainer
key={objectMetadataItem.id}
onClick={() =>
handleSelectObjectMetadata(objectMetadataItem.id)
}
>
<SettingsCard
Icon={
<ObjectMetadataIcon
objectMetadataItem={objectMetadataItem}
size={theme.icon.size.lg}
stroke={theme.icon.stroke.sm}
/>
}
>
<SettingsCard
key={objectMetadataItem.id}
Icon={
<Icon
size={theme.icon.size.lg}
stroke={theme.icon.stroke.sm}
/>
}
title={objectMetadataItem.labelPlural}
/>
</StyledCardContainer>
);
})}
title={objectMetadataItem.labelPlural}
/>
</StyledCardContainer>
))}
</StyledContainer>
</Section>
)}
@@ -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) && (
<Icon
style={{
minWidth: theme.icon.size.md,
}}
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
)}
<ObjectMetadataIcon
objectMetadataItem={objectMetadataItem}
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
<StyledNameLabel title={objectLabelPlural}>
<OverflowingTextWithTooltip text={objectLabelPlural} />
</StyledNameLabel>
@@ -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={
<StyledClickableIconWrapper>
<NavigationMenuItemStyleIcon
<TintedIconTile
Icon={FolderIconComponent}
color={selectedItem.color}
/>
@@ -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)}
>
<MenuItemSelectAvatar
avatar={
<NavigationMenuItemStyleIcon Icon={IconCube} color="gray" />
}
avatar={<TintedIconTile Icon={IconCube} />}
text={t`All objects`}
selected={selectedObjectNameSingular === null}
onClick={() => handleSelect(null)}
@@ -103,9 +99,6 @@ export const SidePanelObjectFilterDropdownContent = ({
/>
</SelectableListItem>
{displayedObjects.map((objectMetadataItem) => {
const ObjectIcon = getIcon(objectMetadataItem.icon);
const iconColor = getObjectColorWithFallback(objectMetadataItem);
return (
<SelectableListItem
key={objectMetadataItem.id}
@@ -114,9 +107,8 @@ export const SidePanelObjectFilterDropdownContent = ({
>
<MenuItemSelectAvatar
avatar={
<NavigationMenuItemStyleIcon
Icon={ObjectIcon}
color={iconColor}
<ObjectMetadataIcon
objectMetadataItem={objectMetadataItem}
/>
}
text={objectMetadataItem.labelPlural}
@@ -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={() => (
<ObjectMetadataIcon objectMetadataItem={objectMetadataItem} />
)}
onClick={() => {
handleSelectSource(objectMetadataItem.id);
}}
@@ -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={() => (
<ObjectMetadataIcon objectMetadataItem={objectMetadataItem} />
)}
onClick={() => {
handleSelectSource(objectMetadataItem.id);
}}
@@ -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<SelectControlProps, 'selectedOption'> & {
@@ -59,11 +62,22 @@ export const MultiSelectControl = ({
stroke: theme.icon.stroke.sm,
})
) : isDefined(firstSelectedOption?.Icon) ? (
<firstSelectedOption.Icon
color={isDisabled ? theme.font.color.light : theme.font.color.primary}
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
isDefined(firstSelectedOption.iconThemeColor) ? (
<TintedIconTile
Icon={firstSelectedOption.Icon}
color={firstSelectedOption.iconThemeColor}
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
) : (
<firstSelectedOption.Icon
color={
isDisabled ? theme.font.color.light : theme.font.color.primary
}
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
)
) : null}
{isDefined(fixedText) ? (
<OverflowingTextWithTooltip text={fixedText} />
@@ -226,6 +226,7 @@ export const Select = <Value extends SelectValue>({
<DropdownMenuItemsContainer scrollable={false}>
<MenuItemSelect
LeftIcon={pinnedOption.Icon}
leftIconColor={pinnedOption.iconThemeColor}
text={pinnedOption.label}
contextualText={pinnedOption.contextualText}
selected={
@@ -262,6 +263,7 @@ export const Select = <Value extends SelectValue>({
>
<MenuItemSelect
LeftIcon={option.Icon}
leftIconColor={option.iconThemeColor}
text={option.label}
contextualText={option.contextualText}
selected={
@@ -2,7 +2,11 @@ import { type SelectSizeVariant } from '@/ui/input/components/Select';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { IconChevronDown, OverflowingTextWithTooltip } from 'twenty-ui/display';
import {
IconChevronDown,
OverflowingTextWithTooltip,
TintedIconTile,
} from 'twenty-ui/display';
import { type SelectOption } from 'twenty-ui/input';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
@@ -88,11 +92,22 @@ export const SelectControl = ({
title={selectedOption.fullLabel}
>
{isDefined(selectedOption?.Icon) ? (
<selectedOption.Icon
color={isDisabled ? theme.font.color.light : theme.font.color.primary}
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
isDefined(selectedOption.iconThemeColor) ? (
<TintedIconTile
Icon={selectedOption.Icon}
color={selectedOption.iconThemeColor}
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
) : (
<selectedOption.Icon
color={
isDisabled ? theme.font.color.light : theme.font.color.primary
}
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
)
) : null}
<OverflowingTextWithTooltip
text={
@@ -1,4 +1,3 @@
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon';
import { useIsSettingsPage } from '@/navigation/hooks/useIsSettingsPage';
import { NAVIGATION_DRAWER_COLLAPSED_WIDTH } from '@/ui/layout/resizable-panel/constants/NavigationDrawerCollapsedWidth';
import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper';
@@ -21,6 +20,7 @@ import {
Label,
OverflowingTextWithTooltip,
type TablerIconsProps,
TintedIconTile,
TooltipDelay,
TooltipPosition,
} from 'twenty-ui/display';
@@ -355,7 +355,7 @@ export const NavigationDrawerItem = ({
{Icon &&
(isNonEmptyString(iconColor) ? (
<StyledIcon>
<NavigationMenuItemStyleIcon Icon={Icon} color={iconColor} />
<TintedIconTile Icon={Icon} color={iconColor} />
</StyledIcon>
) : (
<StyledIcon>
@@ -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 (
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
<DropdownMenuHeader
@@ -231,11 +238,8 @@ export const WorkflowDropdownStepOutputItems = ({
onClick={handleSelectObject}
text={objectLabel || ''}
hasSubMenu={false}
LeftIcon={
subStepObjectMetadataItem?.icon
? getIcon(subStepObjectMetadataItem.icon)
: undefined
}
LeftIcon={subStepObjectIconProps?.Icon}
leftIconColor={subStepObjectIconProps?.iconThemeColor}
contextualText={t`Pick a ${objectLabel} record`}
/>
)}
@@ -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}
@@ -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 (
<MenuItem
LeftIcon={IconComponent}
withIconContainer
LeftComponent={<ObjectMetadataIcon objectMetadataItem={objectMetadata} />}
text={objectMetadata.labelPlural}
hasSubMenu={!readonly}
onClick={!readonly ? onClick : undefined}
@@ -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;
};
@@ -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<CreateRecordFormData>({
@@ -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<DeleteRecordFormData>({
@@ -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<UpdateRecordFormData>({
@@ -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<UpsertRecordFormData>({
@@ -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: '' };
@@ -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 = ({
/>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer hasMaxHeight>
{filteredOptions.map((option) => (
{filteredObjects.map((objectMetadataItem) => (
<MenuItem
key={option.value}
LeftIcon={option.Icon}
text={option.label}
onClick={() => onOptionClick(option.value)}
key={objectMetadataItem.nameSingular}
LeftIcon={() => (
<ObjectMetadataIcon objectMetadataItem={objectMetadataItem} />
)}
text={objectMetadataItem.labelPlural}
onClick={() => onOptionClick(objectMetadataItem.nameSingular)}
/>
))}
{shouldShowAdvanced && (
@@ -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<SelectOption<string>> =
activeNonSystemObjectMetadataItems.map((item) => ({
Icon: getIcon(item.icon),
label: item.labelPlural,
value: item.nameSingular,
...getSelectIconPropsFromObjectMetadataItem(item),
}));
return (
@@ -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 =
@@ -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;
@@ -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 (
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
<DropdownMenuHeader
@@ -125,11 +134,8 @@ export const WorkflowVariablesDropdownStepItems = ({
onClick={handleSelectObject}
text={objectLabel || ''}
hasSubMenu={false}
LeftIcon={
displayedSubStepObjectMetadata?.icon
? getIcon(displayedSubStepObjectMetadata.icon)
: undefined
}
LeftIcon={displayedSubStepObjectIconProps?.Icon}
leftIconColor={displayedSubStepObjectIconProps?.iconThemeColor}
contextualText={t`Pick a ${objectLabel} record`}
/>
)}
@@ -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)) {
+5
View File
@@ -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,
@@ -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;
@@ -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]};
`;
@@ -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 (
<StyledTintedIconTileContainer
$backgroundColor={style.backgroundColor}
$borderColor={style.borderColor}
$dimension={tileDimension}
>
<Icon size={iconSize} stroke={iconStroke} color={style.iconColor} />
</StyledTintedIconTileContainer>
);
};
@@ -0,0 +1,5 @@
export const TINTED_ICON_TILE_COLOR_SHADES = {
background: 5,
border: 6,
icon: 11,
} as const;
@@ -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,
@@ -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,
),
};
};
@@ -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;
@@ -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 = ({
>
<MenuItemLeftContent
LeftIcon={LeftIcon ?? undefined}
iconThemeColor={iconThemeColor}
LeftComponent={LeftComponent}
withIconContainer={withIconContainer}
withIconContainerBackground={withIconContainerBackground}
@@ -4,9 +4,9 @@ import { Tag } from '@ui/components';
import { type IconComponent } from '@ui/display';
import { Checkbox } from '@ui/input/components/Checkbox';
import { MenuItemLeftContent } from '@ui/navigation/menu/menu-item/internals/components/MenuItemLeftContent';
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';
import { type ThemeColor } from '@ui/theme';
import { themeCssVariables } from '@ui/theme-constants';
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';
const StyledLeftContentWithCheckboxContainer = styled.div`
align-items: center;
@@ -20,6 +20,7 @@ const StyledLeftContentWithCheckboxContainer = styled.div`
type MenuItemMultiSelectProps = {
color?: ThemeColor;
LeftIcon?: IconComponent;
iconThemeColor?: ThemeColor | null;
selected: boolean;
isKeySelected?: boolean;
withIconContainer?: boolean;
@@ -31,6 +32,7 @@ type MenuItemMultiSelectProps = {
export const MenuItemMultiSelect = ({
color,
LeftIcon,
iconThemeColor,
withIconContainer = false,
text,
selected,
@@ -55,6 +57,7 @@ export const MenuItemMultiSelect = ({
) : (
<MenuItemLeftContent
LeftIcon={LeftIcon}
iconThemeColor={iconThemeColor}
text={text}
withIconContainer={withIconContainer}
/>
@@ -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 = ({
>
<MenuItemLeftContent
LeftIcon={LeftIcon}
iconThemeColor={leftIconColor}
text={text}
contextualText={
contextualTextPosition === 'left' ? contextualText : null
@@ -1,16 +1,20 @@
import { type IconComponent } from '@ui/display';
import { type IconComponent, TintedIconTile } from '@ui/display';
import { type ThemeColor } from '@ui/theme';
import { ThemeContext } from '@ui/theme-constants';
import { useContext } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { MenuItemIconBoxContainer } from './MenuItemIconBoxContainer';
export type MenuItemIconProps = {
Icon: IconComponent | null | undefined;
iconThemeColor?: ThemeColor | null;
withContainer?: boolean;
withContainerBackground?: boolean;
};
export const MenuItemIcon = ({
Icon,
iconThemeColor,
withContainer = false,
withContainerBackground = true,
}: MenuItemIconProps) => {
@@ -20,6 +24,17 @@ export const MenuItemIcon = ({
return null;
}
if (isDefined(iconThemeColor)) {
return (
<TintedIconTile
Icon={Icon}
color={iconThemeColor}
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
);
}
const iconElement = (
<Icon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
);
@@ -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 = (
<StyledIconSwapContainer>
<StyledDefaultIcon className="grip-swap-default-icon">
<LeftIcon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
{isDefined(iconThemeColor) ? (
<TintedIconTile
Icon={LeftIcon}
color={iconThemeColor}
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
) : (
<LeftIcon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
)}
</StyledDefaultIcon>
<StyledHoverIcon className="grip-swap-hover-icon">
<IconGripVertical
@@ -57,7 +74,7 @@ export const MenuItemIconWithGripSwap = ({
</StyledIconSwapContainer>
);
if (withIconContainer) {
if (withIconContainer && !isDefined(iconThemeColor)) {
return <MenuItemIconBoxContainer>{iconContent}</MenuItemIconBoxContainer>;
}
@@ -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' ? (
<MenuItemIconWithGripSwap
LeftIcon={LeftIcon}
iconThemeColor={iconThemeColor}
withIconContainer={withIconContainer}
gripIconColor={gripIconColor}
/>
) : (
<MenuItemIcon
Icon={LeftIcon}
iconThemeColor={iconThemeColor}
withContainer={withIconContainer}
withContainerBackground={withIconContainerBackground}
/>
@@ -0,0 +1,3 @@
import { type ThemeColor } from './MainColorNames';
export const DEFAULT_THEME_COLOR_FALLBACK: ThemeColor = 'gray';
+1
View File
@@ -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';
@@ -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;
};
@@ -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,