Favorites Folders Fast Followups (#8920)
Fixes : <img width="716" alt="Screenshot 2024-12-06 at 3 45 41 PM" src="https://github.com/user-attachments/assets/61fdf355-0d0a-4ed7-befa-ada23341a58f"> Fixes: Reduce menu width to 160px and it should appear below three dots <img width="394" alt="Screenshot 2024-12-06 at 3 46 49 PM" src="https://github.com/user-attachments/assets/b1266f83-9b6f-445b-9409-d7f691776bd0"> Fixes: The right margin should be 2px <img width="1134" alt="Screenshot 2024-12-06 at 3 47 45 PM" src="https://github.com/user-attachments/assets/b6dd857c-6575-418d-8e32-64cd4e5d4e85"> Fixes: Requirement: Clicking the heart Icon should put the record as favorite. It shouldn't open the menu on first click. It should only on second click, when the record is already a favorite. --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,4 +1,3 @@
|
|||||||
import { useTheme } from '@emotion/react';
|
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
import {
|
import {
|
||||||
@ -26,15 +25,19 @@ import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/compo
|
|||||||
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
|
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
|
||||||
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
|
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
|
||||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
import { DragStart, DropResult, ResponderProvided } from '@hello-pangea/dnd';
|
import { DragStart, DropResult, ResponderProvided } from '@hello-pangea/dnd';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
const StyledOrphanFavoritesContainer = styled.div`
|
||||||
|
margin-bottom: ${({ theme }) => theme.betweenSiblingsGap};
|
||||||
|
`;
|
||||||
|
|
||||||
export const CurrentWorkspaceMemberFavoritesFolders = () => {
|
export const CurrentWorkspaceMemberFavoritesFolders = () => {
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
|
|
||||||
const currentPath = useLocation().pathname;
|
const currentPath = useLocation().pathname;
|
||||||
const currentViewPath = useLocation().pathname + useLocation().search;
|
const currentViewPath = useLocation().pathname + useLocation().search;
|
||||||
const theme = useTheme();
|
|
||||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||||
const { sortedFavorites: favorites } = useFavorites();
|
const { sortedFavorites: favorites } = useFavorites();
|
||||||
const { deleteFavorite } = useDeleteFavorite();
|
const { deleteFavorite } = useDeleteFavorite();
|
||||||
@ -98,12 +101,13 @@ export const CurrentWorkspaceMemberFavoritesFolders = () => {
|
|||||||
onClick={toggleNavigationSection}
|
onClick={toggleNavigationSection}
|
||||||
rightIcon={
|
rightIcon={
|
||||||
isFavoriteFolderEnabled ? (
|
isFavoriteFolderEnabled ? (
|
||||||
<IconFolderPlus size={theme.icon.size.sm} />
|
<LightIconButton
|
||||||
|
Icon={IconFolderPlus}
|
||||||
|
onClick={toggleNewFolder}
|
||||||
|
accent="tertiary"
|
||||||
|
/>
|
||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
onRightIconClick={
|
|
||||||
isFavoriteFolderEnabled ? toggleNewFolder : undefined
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</NavigationDrawerAnimatedCollapseWrapper>
|
</NavigationDrawerAnimatedCollapseWrapper>
|
||||||
|
|
||||||
@ -126,26 +130,28 @@ export const CurrentWorkspaceMemberFavoritesFolders = () => {
|
|||||||
index={index}
|
index={index}
|
||||||
isInsideScrollableContainer={true}
|
isInsideScrollableContainer={true}
|
||||||
itemComponent={
|
itemComponent={
|
||||||
<NavigationDrawerItem
|
<StyledOrphanFavoritesContainer>
|
||||||
key={favorite.id}
|
<NavigationDrawerItem
|
||||||
className="navigation-drawer-item"
|
key={favorite.id}
|
||||||
label={favorite.labelIdentifier}
|
className="navigation-drawer-item"
|
||||||
Icon={() => <FavoriteIcon favorite={favorite} />}
|
label={favorite.labelIdentifier}
|
||||||
active={isLocationMatchingFavorite(
|
Icon={() => <FavoriteIcon favorite={favorite} />}
|
||||||
currentPath,
|
active={isLocationMatchingFavorite(
|
||||||
currentViewPath,
|
currentPath,
|
||||||
favorite,
|
currentViewPath,
|
||||||
)}
|
favorite,
|
||||||
to={favorite.link}
|
)}
|
||||||
rightOptions={
|
to={favorite.link}
|
||||||
<LightIconButton
|
rightOptions={
|
||||||
Icon={IconHeartOff}
|
<LightIconButton
|
||||||
onClick={() => deleteFavorite(favorite.id)}
|
Icon={IconHeartOff}
|
||||||
accent="tertiary"
|
onClick={() => deleteFavorite(favorite.id)}
|
||||||
/>
|
accent="tertiary"
|
||||||
}
|
/>
|
||||||
isDragging={isDragging}
|
}
|
||||||
/>
|
isDragging={isDragging}
|
||||||
|
/>
|
||||||
|
</StyledOrphanFavoritesContainer>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
import { FavoriteFolderHotkeyScope } from '@/favorites/constants/FavoriteFolderRightIconDropdownHotkeyScope';
|
import { FavoriteFolderHotkeyScope } from '@/favorites/constants/FavoriteFolderRightIconDropdownHotkeyScope';
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { useTheme } from '@emotion/react';
|
|
||||||
import styled from '@emotion/styled';
|
|
||||||
|
|
||||||
import { IconDotsVertical, IconPencil, IconTrash, MenuItem } from 'twenty-ui';
|
import {
|
||||||
|
IconDotsVertical,
|
||||||
|
IconPencil,
|
||||||
|
IconTrash,
|
||||||
|
LightIconButton,
|
||||||
|
MenuItem,
|
||||||
|
} from 'twenty-ui';
|
||||||
|
|
||||||
type FavoriteFolderNavigationDrawerItemDropdownProps = {
|
type FavoriteFolderNavigationDrawerItemDropdownProps = {
|
||||||
folderId: string;
|
folderId: string;
|
||||||
@ -13,24 +17,12 @@ type FavoriteFolderNavigationDrawerItemDropdownProps = {
|
|||||||
closeDropdown: () => void;
|
closeDropdown: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledIconContainer = styled.div`
|
|
||||||
align-items: center;
|
|
||||||
background: transparent;
|
|
||||||
height: 24px;
|
|
||||||
width: 24px;
|
|
||||||
justify-content: center;
|
|
||||||
transition: background 0.1s ease;
|
|
||||||
display: flex;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const FavoriteFolderNavigationDrawerItemDropdown = ({
|
export const FavoriteFolderNavigationDrawerItemDropdown = ({
|
||||||
folderId,
|
folderId,
|
||||||
onRename,
|
onRename,
|
||||||
onDelete,
|
onDelete,
|
||||||
closeDropdown,
|
closeDropdown,
|
||||||
}: FavoriteFolderNavigationDrawerItemDropdownProps) => {
|
}: FavoriteFolderNavigationDrawerItemDropdownProps) => {
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const handleRename = () => {
|
const handleRename = () => {
|
||||||
onRename();
|
onRename();
|
||||||
closeDropdown();
|
closeDropdown();
|
||||||
@ -49,15 +41,9 @@ export const FavoriteFolderNavigationDrawerItemDropdown = ({
|
|||||||
}}
|
}}
|
||||||
data-select-disable
|
data-select-disable
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<StyledIconContainer>
|
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
||||||
<IconDotsVertical
|
|
||||||
size={theme.icon.size.sm}
|
|
||||||
color={theme.font.color.tertiary}
|
|
||||||
/>
|
|
||||||
</StyledIconContainer>
|
|
||||||
}
|
}
|
||||||
dropdownPlacement="right"
|
dropdownPlacement="bottom-start"
|
||||||
dropdownOffset={{ y: -15 }}
|
|
||||||
dropdownComponents={
|
dropdownComponents={
|
||||||
<DropdownMenuItemsContainer>
|
<DropdownMenuItemsContainer>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|||||||
@ -77,7 +77,7 @@ const StyledItem = styled('button', {
|
|||||||
|
|
||||||
padding-bottom: ${({ theme }) => theme.spacing(1)};
|
padding-bottom: ${({ theme }) => theme.spacing(1)};
|
||||||
padding-left: ${({ theme }) => theme.spacing(1)};
|
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||||
padding-right: ${({ theme }) => theme.spacing(1)};
|
padding-right: ${({ theme }) => theme.spacing(0.5)};
|
||||||
padding-top: ${({ theme }) => theme.spacing(1)};
|
padding-top: ${({ theme }) => theme.spacing(1)};
|
||||||
|
|
||||||
margin-top: ${({ indentationLevel }) =>
|
margin-top: ${({ indentationLevel }) =>
|
||||||
|
|||||||
@ -17,7 +17,10 @@ const StyledTitle = styled.div`
|
|||||||
font-size: ${({ theme }) => theme.font.size.xs};
|
font-size: ${({ theme }) => theme.font.size.xs};
|
||||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||||
height: ${({ theme }) => theme.spacing(5)};
|
height: ${({ theme }) => theme.spacing(5)};
|
||||||
padding: ${({ theme }) => theme.spacing(1)};
|
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||||
|
padding-right: ${({ theme }) => theme.spacing(0.5)};
|
||||||
|
padding-top: ${({ theme }) => theme.spacing(1)};
|
||||||
|
padding-bottom: ${({ theme }) => theme.spacing(1)};
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -36,38 +39,21 @@ type StyledRightIconProps = {
|
|||||||
|
|
||||||
const StyledRightIcon = styled.div<StyledRightIconProps>`
|
const StyledRightIcon = styled.div<StyledRightIconProps>`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-left: ${({ theme }) => theme.spacing(2)};
|
|
||||||
transition: opacity 150ms ease-in-out;
|
|
||||||
opacity: ${({ isMobile }) => (isMobile ? 1 : 0)};
|
opacity: ${({ isMobile }) => (isMobile ? 1 : 0)};
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
|
||||||
width: ${({ theme }) => theme.spacing(5)};
|
|
||||||
height: ${({ theme }) => theme.spacing(5)};
|
|
||||||
:hover {
|
|
||||||
background: ${({ theme }) => theme.background.transparent.light};
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title-container:hover & {
|
.section-title-container:hover & {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type NavigationDrawerSectionTitleProps = {
|
type NavigationDrawerSectionTitleProps = {
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
onRightIconClick?: () => void;
|
|
||||||
label: string;
|
label: string;
|
||||||
rightIcon?: React.ReactNode;
|
rightIcon?: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NavigationDrawerSectionTitle = ({
|
export const NavigationDrawerSectionTitle = ({
|
||||||
onClick,
|
onClick,
|
||||||
onRightIconClick,
|
|
||||||
label,
|
label,
|
||||||
rightIcon,
|
rightIcon,
|
||||||
}: NavigationDrawerSectionTitleProps) => {
|
}: NavigationDrawerSectionTitleProps) => {
|
||||||
@ -85,24 +71,15 @@ export const NavigationDrawerSectionTitle = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRightIconClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
if (isDefined(onRightIconClick)) {
|
|
||||||
onRightIconClick();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (loading && isDefined(currentUser)) {
|
if (loading && isDefined(currentUser)) {
|
||||||
return <NavigationDrawerSectionTitleSkeletonLoader />;
|
return <NavigationDrawerSectionTitleSkeletonLoader />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledTitle className="section-title-container" onClick={handleTitleClick}>
|
<StyledTitle className="section-title-container">
|
||||||
<StyledLabel>{label}</StyledLabel>
|
<StyledLabel onClick={handleTitleClick}>{label}</StyledLabel>
|
||||||
{rightIcon && (
|
{rightIcon && (
|
||||||
<StyledRightIcon isMobile={isMobile} onClick={handleRightIconClick}>
|
<StyledRightIcon isMobile={isMobile}>{rightIcon}</StyledRightIcon>
|
||||||
{rightIcon}
|
|
||||||
</StyledRightIcon>
|
|
||||||
)}
|
)}
|
||||||
</StyledTitle>
|
</StyledTitle>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -34,13 +34,20 @@ export const RecordShowPageBaseHeader = ({
|
|||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<>
|
<>
|
||||||
{isFavoriteFolderEnabled ? (
|
{isFavoriteFolderEnabled ? (
|
||||||
<PageFavoriteFoldersDropdown
|
isFavorite ? (
|
||||||
key={FAVORITE_FOLDER_PICKER_DROPDOWN_ID}
|
<PageFavoriteFoldersDropdown
|
||||||
dropdownId={FAVORITE_FOLDER_PICKER_DROPDOWN_ID}
|
key={FAVORITE_FOLDER_PICKER_DROPDOWN_ID}
|
||||||
isFavorite={isFavorite}
|
dropdownId={FAVORITE_FOLDER_PICKER_DROPDOWN_ID}
|
||||||
record={record}
|
isFavorite={isFavorite}
|
||||||
objectNameSingular={objectNameSingular}
|
record={record}
|
||||||
/>
|
objectNameSingular={objectNameSingular}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<PageFavoriteButton
|
||||||
|
isFavorite={isFavorite}
|
||||||
|
onClick={handleFavoriteButtonClick}
|
||||||
|
/>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<PageFavoriteButton
|
<PageFavoriteButton
|
||||||
isFavorite={isFavorite}
|
isFavorite={isFavorite}
|
||||||
|
|||||||
Reference in New Issue
Block a user