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:
@@ -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}
|
||||
/>
|
||||
|
||||
+5
-13
@@ -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}
|
||||
|
||||
+8
-7
@@ -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);
|
||||
}}
|
||||
|
||||
+5
-4
@@ -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);
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user