Navbar customization followup (#17848)

Addresses review comments from the [first navbar customization
PR](https://github.com/twentyhq/twenty/pull/17728)

---------

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Co-authored-by: Devessier <baptiste@devessier.fr>
This commit is contained in:
Abdul Rahman
2026-02-12 08:05:43 +05:30
committed by GitHub
parent 91bfd45dc5
commit 3fb2352c78
67 changed files with 993 additions and 686 deletions
@@ -1,30 +1,21 @@
import { useTheme } from '@emotion/react';
import { useLingui } from '@lingui/react/macro';
import { useRecoilValue } from 'recoil';
import { IconFolder, IconLink } from 'twenty-ui/display';
import { IconFolder } from 'twenty-ui/display';
import { CommandMenuPageInfoLayout } from '@/command-menu/components/CommandMenuPageInfoLayout';
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
import { commandMenuShouldFocusTitleInputComponentState } from '@/command-menu/states/commandMenuShouldFocusTitleInputComponentState';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { StyledNavigationMenuItemIconContainer } from '@/navigation-menu-item/components/NavigationMenuItemIconContainer';
import { useUpdateFolderNameInDraft } from '@/navigation-menu-item/hooks/useUpdateFolderNameInDraft';
import { useUpdateLinkInDraft } from '@/navigation-menu-item/hooks/useUpdateLinkInDraft';
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors';
import { TitleInput } from '@/ui/input/components/TitleInput';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
const ICON_CONFIG = {
folder: { Icon: IconFolder, colorKey: 'folder' },
link: { Icon: IconLink, colorKey: 'link' },
} as const;
export const CommandMenuFolderLinkInfo = ({
type,
}: {
type: 'folder' | 'link';
}) => {
export const CommandMenuFolderInfo = () => {
const theme = useTheme();
const { t } = useLingui();
const commandMenuPageInfo = useRecoilValue(commandMenuPageInfoState);
@@ -38,15 +29,14 @@ export const CommandMenuFolderLinkInfo = ({
);
const items = useWorkspaceSectionItems();
const { updateFolderNameInDraft } = useUpdateFolderNameInDraft();
const { updateLinkInDraft } = useUpdateLinkInDraft();
const defaultLabel = type === 'folder' ? t`New folder` : t`Link label`;
const placeholder = type === 'folder' ? t`Folder name` : t`Link label`;
const defaultLabel = t`New folder`;
const placeholder = t`Folder name`;
const selectedItem = selectedNavigationMenuItemInEditMode
? items.find(
(item) =>
item.itemType === type &&
item.itemType === NavigationMenuItemType.FOLDER &&
item.id === selectedNavigationMenuItemInEditMode,
)
: undefined;
@@ -57,11 +47,7 @@ export const CommandMenuFolderLinkInfo = ({
const itemName = selectedItem.name ?? defaultLabel;
const handleChange = (text: string) => {
if (type === 'folder') {
updateFolderNameInDraft(itemId, text);
} else {
updateLinkInDraft(itemId, { name: text });
}
updateFolderNameInDraft(itemId, text);
};
const handleSave = () => {
@@ -69,23 +55,17 @@ export const CommandMenuFolderLinkInfo = ({
const finalName = trimmed.length > 0 ? trimmed : defaultLabel;
if (finalName !== itemName) {
if (type === 'folder') {
updateFolderNameInDraft(itemId, finalName);
} else {
updateLinkInDraft(itemId, { name: finalName });
}
updateFolderNameInDraft(itemId, finalName);
}
};
const { Icon, colorKey } = ICON_CONFIG[type];
return (
<CommandMenuPageInfoLayout
icon={
<StyledNavigationMenuItemIconContainer
$backgroundColor={getNavigationMenuItemIconColors(theme)[colorKey]}
$backgroundColor={getNavigationMenuItemIconColors(theme).folder}
>
<Icon
<IconFolder
size={theme.spacing(3.5)}
color={theme.grayScale.gray1}
stroke={theme.icon.stroke.md}
@@ -94,9 +74,7 @@ export const CommandMenuFolderLinkInfo = ({
}
title={
<TitleInput
instanceId={
type === 'folder' ? `folder-name-${itemId}` : `link-label-${itemId}`
}
instanceId={`folder-name-${itemId}`}
sizeVariant="sm"
value={itemName}
onChange={handleChange}
@@ -110,7 +88,6 @@ export const CommandMenuFolderLinkInfo = ({
onFocus={() => setShouldFocusTitleInput(false)}
/>
}
label={type === 'link' ? t`link` : undefined}
/>
);
};
@@ -0,0 +1,94 @@
import { useTheme } from '@emotion/react';
import { useLingui } from '@lingui/react/macro';
import { useRecoilValue } from 'recoil';
import { IconLink } from 'twenty-ui/display';
import { CommandMenuPageInfoLayout } from '@/command-menu/components/CommandMenuPageInfoLayout';
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
import { commandMenuShouldFocusTitleInputComponentState } from '@/command-menu/states/commandMenuShouldFocusTitleInputComponentState';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { StyledNavigationMenuItemIconContainer } from '@/navigation-menu-item/components/NavigationMenuItemIconContainer';
import { useUpdateLinkInDraft } from '@/navigation-menu-item/hooks/useUpdateLinkInDraft';
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors';
import { TitleInput } from '@/ui/input/components/TitleInput';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
export const CommandMenuLinkInfo = () => {
const theme = useTheme();
const { t } = useLingui();
const commandMenuPageInfo = useRecoilValue(commandMenuPageInfoState);
const [shouldFocusTitleInput, setShouldFocusTitleInput] =
useRecoilComponentState(
commandMenuShouldFocusTitleInputComponentState,
commandMenuPageInfo.instanceId,
);
const selectedNavigationMenuItemInEditMode = useRecoilValue(
selectedNavigationMenuItemInEditModeState,
);
const items = useWorkspaceSectionItems();
const { updateLinkInDraft } = useUpdateLinkInDraft();
const defaultLabel = t`Link label`;
const placeholder = t`Link label`;
const selectedItem = selectedNavigationMenuItemInEditMode
? items.find(
(item) =>
item.itemType === NavigationMenuItemType.LINK &&
item.id === selectedNavigationMenuItemInEditMode,
)
: undefined;
if (!selectedItem) return null;
const itemId = selectedItem.id;
const itemName = selectedItem.name ?? defaultLabel;
const handleChange = (text: string) => {
updateLinkInDraft(itemId, { name: text });
};
const handleSave = () => {
const trimmed = itemName.trim();
const finalName = trimmed.length > 0 ? trimmed : defaultLabel;
if (finalName !== itemName) {
updateLinkInDraft(itemId, { name: finalName });
}
};
return (
<CommandMenuPageInfoLayout
icon={
<StyledNavigationMenuItemIconContainer
$backgroundColor={getNavigationMenuItemIconColors(theme).link}
>
<IconLink
size={theme.spacing(3.5)}
color={theme.grayScale.gray1}
stroke={theme.icon.stroke.md}
/>
</StyledNavigationMenuItemIconContainer>
}
title={
<TitleInput
instanceId={`link-label-${itemId}`}
sizeVariant="sm"
value={itemName}
onChange={handleChange}
placeholder={placeholder}
onEnter={handleSave}
onEscape={handleSave}
onClickOutside={handleSave}
onTab={handleSave}
onShiftTab={handleSave}
shouldFocus={shouldFocusTitleInput}
onFocus={() => setShouldFocusTitleInput(false)}
/>
}
label={t`link`}
/>
);
};
@@ -2,27 +2,35 @@ import { useLingui } from '@lingui/react/macro';
import { OverflowingTextWithTooltip } from 'twenty-ui/display';
import { CommandMenuPageInfoLayout } from '@/command-menu/components/CommandMenuPageInfoLayout';
import { useSelectedNavigationMenuItemEditData } from '@/command-menu/pages/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditData';
import { NavigationMenuItemIcon } from '@/navigation-menu-item/components/NavigationMenuItemIcon';
import { useSelectedNavigationMenuItemEditItem } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItem';
import { useSelectedNavigationMenuItemEditItemLabel } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemLabel';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { ViewKey } from '@/views/types/ViewKey';
export const CommandMenuObjectViewRecordInfo = () => {
const { t } = useLingui();
const { processedItem, selectedItemLabel } =
useSelectedNavigationMenuItemEditData();
const { selectedItem } = useSelectedNavigationMenuItemEditItem();
const { selectedItemLabel } = useSelectedNavigationMenuItemEditItemLabel();
const processedItem =
selectedItem && selectedItem.itemType !== NavigationMenuItemType.FOLDER
? selectedItem
: undefined;
if (!processedItem || !selectedItemLabel) {
return null;
}
const isViewOrRecord =
processedItem.itemType === 'view' || processedItem.itemType === 'record';
processedItem.itemType === NavigationMenuItemType.VIEW ||
processedItem.itemType === NavigationMenuItemType.RECORD;
if (!isViewOrRecord) {
return null;
}
const label =
processedItem.itemType === 'record'
processedItem.itemType === NavigationMenuItemType.RECORD
? t`record`
: processedItem.viewKey === ViewKey.Index
? t`object`
@@ -3,13 +3,15 @@ import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import { OverflowingTextWithTooltip } from 'twenty-ui/display';
import { CommandMenuFolderLinkInfo } from '@/command-menu/components/CommandMenuFolderLinkInfo';
import { CommandMenuFolderInfo } from '@/command-menu/components/CommandMenuFolderInfo';
import { CommandMenuLinkInfo } from '@/command-menu/components/CommandMenuLinkInfo';
import { CommandMenuMultipleRecordsInfo } from '@/command-menu/components/CommandMenuMultipleRecordsInfo';
import { CommandMenuObjectViewRecordInfo } from '@/command-menu/components/CommandMenuObjectViewRecordInfo';
import { CommandMenuPageLayoutInfo } from '@/command-menu/components/CommandMenuPageLayoutInfo';
import { CommandMenuRecordInfo } from '@/command-menu/components/CommandMenuRecordInfo';
import { CommandMenuWorkflowStepInfo } from '@/command-menu/components/CommandMenuWorkflowStepInfo';
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
@@ -44,11 +46,18 @@ export const CommandMenuPageInfo = ({ pageChip }: CommandMenuPageInfoProps) => {
if (isNavigationMenuItemEditPage && isDefined(selectedNavItem)) {
const itemType = selectedNavItem.itemType;
if (itemType === 'folder' || itemType === 'link') {
return <CommandMenuFolderLinkInfo type={itemType} />;
if (itemType === NavigationMenuItemType.FOLDER) {
return <CommandMenuFolderInfo />;
}
if (itemType === 'view' || itemType === 'record') {
if (itemType === NavigationMenuItemType.LINK) {
return <CommandMenuLinkInfo />;
}
if (
itemType === NavigationMenuItemType.VIEW ||
itemType === NavigationMenuItemType.RECORD
) {
return <CommandMenuObjectViewRecordInfo />;
}
}