Files
twenty/packages/twenty-front/src/modules/ui/layout/page/components/PageHeader.tsx
Raphaël Bosi 8213995887 Open showpage on workflow creation (#9714)
- Created an new component state
`isRecordEditableNameRenamingComponentState`
- Updated `useCreateNewTableRecord` to open the ShowPage on workflow
creation
- Refactored `RecordEditableName` and its components to remove the
useEffect (This was causing the recordName state to be updated after the
focus on `NavigationDrawerInput`, but we want the text so be selected
after the update).
- Introduced a new component `EditableBreadcrumbItem`
- Created an autosizing text input: This is done by a hack using a span
inside a div and the input position is set to absolute and takes the
size of the div. There are two problems that I didn't manage to fix:
If the text is too long, the title overflows, and the letter spacing is
different between the span and the input creating a small offset.


https://github.com/user-attachments/assets/4aa1e177-7458-4691-b0c8-96567b482206


New text input component:


https://github.com/user-attachments/assets/94565546-fe2b-457d-a1d8-907007e0e2ce
2025-01-22 14:44:10 +01:00

178 lines
4.9 KiB
TypeScript

import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { ReactNode } from 'react';
import { useRecoilValue } from 'recoil';
import {
IconButton,
IconChevronDown,
IconChevronUp,
IconComponent,
IconX,
LightIconButton,
MOBILE_VIEWPORT,
OverflowingTextWithTooltip,
} from 'twenty-ui';
import { NavigationDrawerCollapseButton } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerCollapseButton';
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { FeatureFlagKey } from '~/generated/graphql';
export const PAGE_BAR_MIN_HEIGHT = 40;
const StyledTopBarContainer = styled.div`
align-items: center;
background: ${({ theme }) => theme.background.noisy};
color: ${({ theme }) => theme.font.color.primary};
display: flex;
flex-direction: row;
font-size: ${({ theme }) => theme.font.size.lg};
justify-content: space-between;
min-height: ${PAGE_BAR_MIN_HEIGHT}px;
padding: ${({ theme }) => theme.spacing(2)};
padding-left: 0;
padding-right: ${({ theme }) => theme.spacing(3)};
gap: ${({ theme }) => theme.spacing(2)};
@media (max-width: ${MOBILE_VIEWPORT}px) {
box-sizing: border-box;
padding: ${({ theme }) => theme.spacing(3)};
}
`;
const StyledLeftContainer = styled.div`
align-items: center;
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(1)};
padding-left: ${({ theme }) => theme.spacing(1)};
overflow-x: hidden;
@media (max-width: ${MOBILE_VIEWPORT}px) {
padding-left: ${({ theme }) => theme.spacing(1)};
}
`;
const StyledTitleContainer = styled.div`
display: flex;
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.medium};
margin-left: ${({ theme }) => theme.spacing(1)};
`;
const StyledTopBarIconStyledTitleContainer = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
flex-direction: row;
`;
const StyledPageActionContainer = styled.div`
display: inline-flex;
gap: ${({ theme }) => theme.spacing(2)};
flex: 1 0 1;
`;
const StyledTopBarButtonContainer = styled.div`
margin-left: ${({ theme }) => theme.spacing(1)};
margin-right: ${({ theme }) => theme.spacing(1)};
`;
const StyledIconContainer = styled.div`
flex: 1 0 1;
display: flex;
flex-direction: row;
align-items: center;
`;
type PageHeaderProps = {
title?: ReactNode;
hasClosePageButton?: boolean;
onClosePage?: () => void;
hasPaginationButtons?: boolean;
hasPreviousRecord?: boolean;
hasNextRecord?: boolean;
navigateToPreviousRecord?: () => void;
navigateToNextRecord?: () => void;
Icon?: IconComponent;
children?: ReactNode;
};
export const PageHeader = ({
title,
hasClosePageButton,
onClosePage,
hasPaginationButtons,
navigateToPreviousRecord,
navigateToNextRecord,
Icon,
children,
}: PageHeaderProps) => {
const isMobile = useIsMobile();
const theme = useTheme();
const isNavigationDrawerExpanded = useRecoilValue(
isNavigationDrawerExpandedState,
);
const isCommandMenuV2Enabled = useIsFeatureEnabled(
FeatureFlagKey.IsCommandMenuV2Enabled,
);
return (
<StyledTopBarContainer>
<StyledLeftContainer>
{!isMobile && !isNavigationDrawerExpanded && (
<StyledTopBarButtonContainer>
<NavigationDrawerCollapseButton direction="right" />
</StyledTopBarButtonContainer>
)}
{hasClosePageButton && (
<LightIconButton
Icon={IconX}
size="small"
accent="tertiary"
onClick={() => onClosePage?.()}
/>
)}
<StyledTopBarIconStyledTitleContainer>
{!isCommandMenuV2Enabled && hasPaginationButtons && (
<>
<IconButton
Icon={IconChevronUp}
size="small"
variant="secondary"
onClick={() => navigateToPreviousRecord?.()}
/>
<IconButton
Icon={IconChevronDown}
size="small"
variant="secondary"
onClick={() => navigateToNextRecord?.()}
/>
</>
)}
<StyledIconContainer>
{Icon && <Icon size={theme.icon.size.md} />}
</StyledIconContainer>
{title && (
<StyledTitleContainer data-testid="top-bar-title">
{typeof title === 'string' ? (
<OverflowingTextWithTooltip text={title} />
) : (
title
)}
</StyledTitleContainer>
)}
</StyledTopBarIconStyledTitleContainer>
</StyledLeftContainer>
<StyledPageActionContainer className="page-action-container">
{children}
</StyledPageActionContainer>
</StyledTopBarContainer>
);
};