Navbar customization improvements (#17863)

- Add folder icon editing support
- And other navbar customization fixes/improvements.

---------

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Co-authored-by: Devessier <baptiste@devessier.fr>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
Abdul Rahman
2026-02-18 01:23:52 +05:30
committed by GitHub
parent 3d362e6e01
commit 015ccbf0a7
44 changed files with 601 additions and 141 deletions
@@ -1,24 +1,32 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { useRecoilValue } from 'recoil';
import { IconFolder } from 'twenty-ui/display';
import { useIcons } 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 { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/constants/FolderIconDefault';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { useUpdateFolderInDraft } from '@/navigation-menu-item/hooks/useUpdateFolderInDraft';
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
import { selectedNavigationMenuItemInEditModeStateV2 } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeStateV2';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors';
import { IconPicker } from '@/ui/input/components/IconPicker';
import { TitleInput } from '@/ui/input/components/TitleInput';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
const StyledClickableIconWrapper = styled.div`
cursor: pointer;
`;
export const CommandMenuFolderInfo = () => {
const theme = useTheme();
const { t } = useLingui();
const { getIcon } = useIcons();
const commandMenuPageInfo = useRecoilValue(commandMenuPageInfoState);
const [shouldFocusTitleInput, setShouldFocusTitleInput] =
useRecoilComponentState(
@@ -29,7 +37,7 @@ export const CommandMenuFolderInfo = () => {
selectedNavigationMenuItemInEditModeStateV2,
);
const items = useWorkspaceSectionItems();
const { updateFolderNameInDraft } = useUpdateFolderNameInDraft();
const { updateFolderInDraft } = useUpdateFolderInDraft();
const defaultLabel = t`New folder`;
const placeholder = t`Folder name`;
@@ -48,7 +56,7 @@ export const CommandMenuFolderInfo = () => {
const itemName = selectedItem.name ?? defaultLabel;
const handleChange = (text: string) => {
updateFolderNameInDraft(itemId, text);
updateFolderInDraft(itemId, { name: text });
};
const handleSave = () => {
@@ -56,22 +64,36 @@ export const CommandMenuFolderInfo = () => {
const finalName = trimmed.length > 0 ? trimmed : defaultLabel;
if (finalName !== itemName) {
updateFolderNameInDraft(itemId, finalName);
updateFolderInDraft(itemId, { name: finalName });
}
};
const selectedIconKey = selectedItem.icon ?? FOLDER_ICON_DEFAULT;
const FolderIconComponent = getIcon(selectedIconKey);
return (
<CommandMenuPageInfoLayout
icon={
<StyledNavigationMenuItemIconContainer
$backgroundColor={getNavigationMenuItemIconColors(theme).folder}
>
<IconFolder
size={theme.spacing(3.5)}
color={theme.grayScale.gray1}
stroke={theme.icon.stroke.md}
/>
</StyledNavigationMenuItemIconContainer>
<IconPicker
dropdownId="command-menu-folder-icon-picker"
selectedIconKey={selectedIconKey}
onChange={({ iconKey }) =>
updateFolderInDraft(itemId, { icon: iconKey })
}
clickableComponent={
<StyledClickableIconWrapper>
<StyledNavigationMenuItemIconContainer
$backgroundColor={getNavigationMenuItemIconColors(theme).folder}
>
<FolderIconComponent
size={theme.spacing(3.5)}
color={theme.grayScale.gray1}
stroke={theme.icon.stroke.md}
/>
</StyledNavigationMenuItemIconContainer>
</StyledClickableIconWrapper>
}
/>
}
title={
<TitleInput
@@ -3,10 +3,9 @@ import { OverflowingTextWithTooltip } from 'twenty-ui/display';
import { CommandMenuPageInfoLayout } from '@/command-menu/components/CommandMenuPageInfoLayout';
import { NavigationMenuItemIcon } from '@/navigation-menu-item/components/NavigationMenuItemIcon';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
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();
@@ -22,19 +21,20 @@ export const CommandMenuObjectViewRecordInfo = () => {
return null;
}
const isViewOrRecord =
processedItem.itemType === NavigationMenuItemType.VIEW ||
processedItem.itemType === NavigationMenuItemType.RECORD;
const isViewOrRecord = [
NavigationMenuItemType.VIEW,
NavigationMenuItemType.RECORD,
].includes(processedItem.itemType);
if (!isViewOrRecord) {
return null;
}
const label =
processedItem.itemType === NavigationMenuItemType.RECORD
const label = isViewOrRecord
? processedItem.itemType === NavigationMenuItemType.RECORD
? t`record`
: processedItem.viewKey === ViewKey.Index
? t`object`
: t`view`;
: t`view`
: t`object`;
return (
<CommandMenuPageInfoLayout
@@ -10,6 +10,7 @@ import { CommandMenuSubViewWithSearch } from '@/command-menu/components/CommandM
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { useFilteredPickerItems } from '@/command-menu/hooks/useFilteredPickerItems';
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
import { ObjectIconWithViewOverlay } from '@/navigation-menu-item/components/ObjectIconWithViewOverlay';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { useAddViewToNavigationMenuDraft } from '@/navigation-menu-item/hooks/useAddViewToNavigationMenuDraft';
import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft';
@@ -110,7 +111,21 @@ export const CommandMenuNewSidebarItemViewPickerSubView = ({
onEnter={() => handleSelectView(view)}
>
<CommandMenuItemWithAddToNavigationDrag
icon={getIcon(view.icon)}
customIconContent={
selectedObjectMetadataItem ? (
<ObjectIconWithViewOverlay
ObjectIcon={getIcon(
selectedObjectMetadataItem.icon,
)}
ViewIcon={getIcon(view.icon)}
/>
) : undefined
}
icon={
selectedObjectMetadataItem
? undefined
: getIcon(view.icon)
}
label={view.name}
id={view.id}
onClick={() => handleSelectView(view)}