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:
+3
-10
@@ -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} />;
|
||||
};
|
||||
|
||||
+10
-14
@@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user