add: objectName in fav folder (#8785)

Closes: #8549 

It was quite complex to get this right. So, I went through Notion's
website to see how they implemented it.
Instead of using `display: none` or having a space reserved for the
Icon, I used clip-path & opacity trick to achieve the desired behaviour.
This maintains accessibility and helps in label or ObjectName to take
the full space.

Also, truncation now works for label & objectName as a whole instead of
separately, as seen in my previous PR.

**Caveats**

The only problem that now remains is not having
`NavigationDrawerAnimatedCollapseWrapper`. Having it on top of any text
or div won't let the flex or truncation property work.

[Screencast from 2024-11-28
13-37-31.webm](https://github.com/user-attachments/assets/29255cd2-3f15-4b1d-b1e1-c041c70052e5)

---------

Co-authored-by: ehconitin <nitinkoche03@gmail.com>
Co-authored-by: martmull <martmull@hotmail.fr>
This commit is contained in:
Harsh Singh 2024-12-19 16:21:03 +05:30 committed by GitHub
parent 65586a00cc
commit 784bc78ed0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 115 additions and 45 deletions

View File

@ -173,6 +173,7 @@ export const CurrentWorkspaceMemberFavorites = ({
itemComponent={
<NavigationDrawerSubItem
label={favorite.labelIdentifier}
objectName={favorite.objectNameSingular}
Icon={() => <FavoriteIcon favorite={favorite} />}
to={favorite.link}
active={index === selectedFavoriteIndex}

View File

@ -59,6 +59,7 @@ export const CurrentWorkspaceMemberOrphanFavorites = () => {
accent="tertiary"
/>
}
objectName={favorite.objectNameSingular}
isDragging={isDragging}
/>
</StyledOrphanFavoritesContainer>

View File

@ -6,7 +6,7 @@ import { NavigationDrawerSubItemState } from '@/ui/navigation/navigation-drawer/
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import isPropValid from '@emotion/is-prop-valid';
import { useTheme } from '@emotion/react';
import { css, useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { ReactNode } from 'react';
import { Link } from 'react-router-dom';
@ -18,6 +18,7 @@ import {
TablerIconsProps,
} from 'twenty-ui';
import { isDefined } from '~/utils/isDefined';
import { capitalize } from '~/utils/string/capitalize';
const DEFAULT_INDENTATION_LEVEL = 1;
@ -26,6 +27,7 @@ export type NavigationDrawerItemIndentationLevel = 1 | 2;
export type NavigationDrawerItemProps = {
className?: string;
label: string;
objectName?: string;
indentationLevel?: NavigationDrawerItemIndentationLevel;
subItemState?: NavigationDrawerSubItemState;
to?: string;
@ -87,13 +89,15 @@ const StyledItem = styled('button', {
width: ${(props) =>
!props.isNavigationDrawerExpanded
? `${NAV_DRAWER_WIDTHS.menu.desktop.collapsed - 24}px`
: '100%'};
? `calc(${NAV_DRAWER_WIDTHS.menu.desktop.collapsed}px - ${props.theme.spacing(6)})`
: `calc(100% - ${props.theme.spacing(2)})`};
${({ isDragging }) =>
isDragging &&
`
cursor: grabbing;
`}
:hover {
background: ${({ theme }) => theme.background.transparent.light};
color: ${(props) =>
@ -111,19 +115,37 @@ const StyledItem = styled('button', {
}
`;
const StyledItemElementsContainer = styled.span`
const StyledItemElementsContainer = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
width: 100%;
`;
const StyledItemLabel = styled.span`
font-weight: ${({ theme }) => theme.font.weight.medium};
const StyledLabelParent = styled.div`
display: flex;
align-items: center;
flex: 1 1 auto;
white-space: nowrap;
min-width: 0px;
overflow: hidden;
text-overflow: clip;
`;
const StyledEllipsisContainer = styled.div`
color: ${({ theme }) => theme.font.color.light};
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
const StyledItemLabel = styled.span`
color: ${({ theme }) => theme.font.color.secondary};
font-weight: ${({ theme }) => theme.font.weight.medium};
`;
const StyledItemObjectName = styled.span`
color: ${({ theme }) => theme.font.color.light};
font-weight: ${({ theme }) => theme.font.weight.regular};
`;
const StyledItemCount = styled.span`
align-items: center;
background-color: ${({ theme }) => theme.color.blue};
@ -149,7 +171,7 @@ const StyledKeyBoardShortcut = styled.span`
visibility: hidden;
`;
const StyledNavigationDrawerItemContainer = styled.span`
const StyledNavigationDrawerItemContainer = styled.div`
display: flex;
width: 100%;
`;
@ -158,30 +180,58 @@ const StyledSpacer = styled.span`
flex-grow: 1;
`;
const StyledRightOptionsContainer = styled.div<{
isMobile: boolean;
active: boolean;
}>`
margin-left: auto;
visibility: ${({ isMobile, active }) =>
isMobile || active ? 'visible' : 'hidden'};
const StyledIcon = styled.div`
flex-shrink: 0;
flex-grow: 0;
margin-right: ${({ theme }) => theme.spacing(2)};
`;
const StyledRightOptionsContainer = styled.div`
display: flex;
align-items: center;
justify-content: center;
:hover {
background: ${({ theme }) => theme.background.transparent.light};
}
width: ${({ theme }) => theme.spacing(6)};
flex-shrink: 0;
flex-grow: 0;
height: ${({ theme }) => theme.spacing(6)};
border-radius: ${({ theme }) => theme.border.radius.sm};
`;
const visibleStateStyles = css`
clip-path: unset;
display: flex;
height: unset;
opacity: 1;
overflow: unset;
position: unset;
width: unset;
`;
const StyledRightOptionsVisbility = styled.div<{
isMobile: boolean;
active: boolean;
}>`
display: block;
opacity: 0;
transition: opacity 150ms;
position: absolute;
padding-left: ${({ theme }) => theme.spacing(2)};
overflow: hidden;
clip-path: inset(1px);
white-space: nowrap;
height: 1px;
width: 1px;
${({ isMobile, active }) => (isMobile || active) && visibleStateStyles}
.navigation-drawer-item:hover & {
visibility: visible;
${visibleStateStyles}
}
`;
export const NavigationDrawerItem = ({
className,
label,
objectName,
indentationLevel = DEFAULT_INDENTATION_LEVEL,
Icon,
to,
@ -228,28 +278,41 @@ export const NavigationDrawerItem = ({
isNavigationDrawerExpanded={isNavigationDrawerExpanded}
isDragging={isDragging}
>
<StyledItemElementsContainer>
{showBreadcrumb && (
<NavigationDrawerAnimatedCollapseWrapper>
<NavigationDrawerItemBreadcrumb state={subItemState} />
</NavigationDrawerAnimatedCollapseWrapper>
)}
<StyledItemElementsContainer>
{Icon && (
<StyledIcon>
<Icon
style={{ minWidth: theme.icon.size.md }}
size={theme.icon.size.md}
stroke={theme.icon.stroke.md}
color={
showBreadcrumb && !isSettingsPage && !isNavigationDrawerExpanded
showBreadcrumb &&
!isSettingsPage &&
!isNavigationDrawerExpanded
? theme.font.color.light
: 'currentColor'
}
/>
</StyledIcon>
)}
<NavigationDrawerAnimatedCollapseWrapper>
<StyledLabelParent>
<StyledEllipsisContainer>
<StyledItemLabel>{label}</StyledItemLabel>
</NavigationDrawerAnimatedCollapseWrapper>
{objectName && (
<StyledItemObjectName>
{' · '}
{capitalize(objectName)}
</StyledItemObjectName>
)}
</StyledEllipsisContainer>
</StyledLabelParent>
<StyledSpacer />
@ -275,14 +338,17 @@ export const NavigationDrawerItem = ({
<NavigationDrawerAnimatedCollapseWrapper>
{rightOptions && (
<StyledRightOptionsContainer
isMobile={isMobile}
active={active || false}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<StyledRightOptionsVisbility
isMobile={isMobile}
active={active || false}
>
{rightOptions}
</StyledRightOptionsVisbility>
</StyledRightOptionsContainer>
)}
</NavigationDrawerAnimatedCollapseWrapper>

View File

@ -8,6 +8,7 @@ type NavigationDrawerSubItemProps = NavigationDrawerItemProps;
export const NavigationDrawerSubItem = ({
className,
label,
objectName,
Icon,
to,
onClick,
@ -24,6 +25,7 @@ export const NavigationDrawerSubItem = ({
<NavigationDrawerItem
className={className}
label={label}
objectName={objectName}
indentationLevel={2}
subItemState={subItemState}
Icon={Icon}