mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-22 19:41:53 +03:00
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:
parent
65586a00cc
commit
784bc78ed0
@ -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}
|
||||
|
@ -59,6 +59,7 @@ export const CurrentWorkspaceMemberOrphanFavorites = () => {
|
||||
accent="tertiary"
|
||||
/>
|
||||
}
|
||||
objectName={favorite.objectNameSingular}
|
||||
isDragging={isDragging}
|
||||
/>
|
||||
</StyledOrphanFavoritesContainer>
|
||||
|
@ -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;
|
||||
`}
|
||||
`
|
||||
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}
|
||||
>
|
||||
{showBreadcrumb && (
|
||||
<NavigationDrawerAnimatedCollapseWrapper>
|
||||
<NavigationDrawerItemBreadcrumb state={subItemState} />
|
||||
</NavigationDrawerAnimatedCollapseWrapper>
|
||||
)}
|
||||
<StyledItemElementsContainer>
|
||||
{Icon && (
|
||||
<Icon
|
||||
style={{ minWidth: theme.icon.size.md }}
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.md}
|
||||
color={
|
||||
showBreadcrumb && !isSettingsPage && !isNavigationDrawerExpanded
|
||||
? theme.font.color.light
|
||||
: 'currentColor'
|
||||
}
|
||||
/>
|
||||
{showBreadcrumb && (
|
||||
<NavigationDrawerAnimatedCollapseWrapper>
|
||||
<NavigationDrawerItemBreadcrumb state={subItemState} />
|
||||
</NavigationDrawerAnimatedCollapseWrapper>
|
||||
)}
|
||||
|
||||
<NavigationDrawerAnimatedCollapseWrapper>
|
||||
<StyledItemLabel>{label}</StyledItemLabel>
|
||||
</NavigationDrawerAnimatedCollapseWrapper>
|
||||
{Icon && (
|
||||
<StyledIcon>
|
||||
<Icon
|
||||
style={{ minWidth: theme.icon.size.md }}
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.md}
|
||||
color={
|
||||
showBreadcrumb &&
|
||||
!isSettingsPage &&
|
||||
!isNavigationDrawerExpanded
|
||||
? theme.font.color.light
|
||||
: 'currentColor'
|
||||
}
|
||||
/>
|
||||
</StyledIcon>
|
||||
)}
|
||||
|
||||
<StyledLabelParent>
|
||||
<StyledEllipsisContainer>
|
||||
<StyledItemLabel>{label}</StyledItemLabel>
|
||||
{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();
|
||||
}}
|
||||
>
|
||||
{rightOptions}
|
||||
<StyledRightOptionsVisbility
|
||||
isMobile={isMobile}
|
||||
active={active || false}
|
||||
>
|
||||
{rightOptions}
|
||||
</StyledRightOptionsVisbility>
|
||||
</StyledRightOptionsContainer>
|
||||
)}
|
||||
</NavigationDrawerAnimatedCollapseWrapper>
|
||||
|
@ -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}
|
||||
|
Loading…
Reference in New Issue
Block a user