fix(twenty-front): new layout fast-follows — settings drawer, loading & command menu (#21389)

Second batch of new-layout fast-follows (master:
twentyhq/core-team-issues#2478). All changes verified live against a
running workspace.

## Settings drawer & header
- **twentyhq/core-team-issues#2489** — sidebar icons render as plain
16px icons, no background tiles.
- **twentyhq/core-team-issues#2488** — Advanced toggle spans the full
drawer width; yellow dot removed.
- **twentyhq/core-team-issues#2497** — page title stays centered in the
settings header (breadcrumb stays left).
- **twentyhq/core-team-issues#2490** — Exit Settings control aligned to
the workspace switcher (24px, matching padding/gap).
- **twentyhq/core-team-issues#2499** — 2px vertical gap restored between
collapsible drawer section items.
- **twentyhq/core-team-issues#2491** — settings drawer rhythm now
matches the main app (28px items, 2px gaps, 28px section headers).
- **twentyhq/core-team-issues#2492** — Home/Chat tab switch no longer
flickers: both tab subtrees stay mounted (a shared
`NavigationDrawerTabbedContent` toggles visibility instead of remounting
+ flashing the chat skeleton).

## Loading states
- **twentyhq/core-team-issues#2486** — metadata loading shows an empty
body (no dense skeleton rows).
- **twentyhq/core-team-issues#2487** — settings table keeps its layout
while loading, with the shimmer localized to the first row's first cell.

## Command menu & navigation
- **twentyhq/core-team-issues#2501** — navigation section header height
matches the nav item rhythm (28px).
- **twentyhq/core-team-issues#2502 (part 1)** — the page side-panel
toggle stays as the dots glyph while the command menu is open, instead
of morphing into a second close control.

## New-field flow
- **twentyhq/core-team-issues#2494** — the new-field stepper moved from
a breadcrumb dropdown into a centered secondary wizard bar (back chevron
+ Save on the configure step); breadcrumb stays clean and the object
label is the centered title.

## Descoped (substantive bugs already fixed)
- **twentyhq/core-team-issues#2500** — command-menu highlight right
gutter: the menu-item base measures full-width, so it's likely a
scrollbar gutter on the list, not the shared component. Left for a
focused follow-up.
- **twentyhq/core-team-issues#2502 part 2** — moving the command-menu
close from left to right is cosmetic (the duplicate-control bug is fixed
by part 1) and would touch the shared `SidePanelTopBar` used by
search/AI panels.

## Verification
typecheck (tsgo) + oxlint + oxfmt green for all changed files; each
change DOM-measured / screenshotted in the running app.
This commit is contained in:
Félix Malfait
2026-06-10 11:02:36 +02:00
committed by GitHub
parent 6525af5ba4
commit adba66caea
16 changed files with 161 additions and 245 deletions
@@ -19,6 +19,7 @@ type PageCardHeaderProps = {
title?: ReactNode;
tag?: ReactNode;
actionButton?: ReactNode;
centerTitle?: boolean;
};
const StyledHeader = styled.div`
@@ -30,6 +31,7 @@ const StyledHeader = styled.div`
gap: ${themeCssVariables.spacing[2]};
min-height: ${SIDE_PANEL_TOP_BAR_HEIGHT}px;
padding: 0 ${themeCssVariables.spacing[3]};
position: relative;
width: 100%;
`;
@@ -51,6 +53,15 @@ const StyledTitle = styled.div`
min-width: 0;
`;
const StyledCenteredTitle = styled(StyledTitle)`
bottom: 0;
justify-content: center;
left: 50%;
position: absolute;
top: 0;
transform: translateX(-50%);
`;
const StyledRight = styled.div`
align-items: center;
display: flex;
@@ -67,10 +78,22 @@ export const PageCardHeader = ({
title,
tag,
actionButton,
centerTitle = false,
}: PageCardHeaderProps) => {
const isMobile = useIsMobile();
const isNavigationDrawerExpanded = useNavigationDrawerExpanded();
const hasTitleContent =
!isMobile && (isDefined(icon) || isDefined(title) || isDefined(tag));
const titleContent = (
<>
{icon}
{isDefined(title) && title}
{tag}
</>
);
return (
<StyledHeader>
<StyledLeft>
@@ -80,15 +103,13 @@ export const PageCardHeader = ({
{isDefined(breadcrumb)
? breadcrumb
: isDefined(links) && <Breadcrumb links={links} />}
{!isMobile &&
(isDefined(icon) || isDefined(title) || isDefined(tag)) && (
<StyledTitle>
{icon}
{isDefined(title) && title}
{tag}
</StyledTitle>
)}
{!centerTitle && hasTitleContent && (
<StyledTitle>{titleContent}</StyledTitle>
)}
</StyledLeft>
{centerTitle && hasTitleContent && (
<StyledCenteredTitle>{titleContent}</StyledCenteredTitle>
)}
<StyledRight
data-click-outside-id={PAGE_ACTION_CONTAINER_CLICK_OUTSIDE_ID}
>
@@ -1,8 +1,16 @@
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
import { styled } from '@linaria/react';
import { type ReactNode } from 'react';
import { AnimatedExpandableContainer } from 'twenty-ui-deprecated/layout';
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
const StyledItems = styled.div`
display: flex;
flex-direction: column;
gap: ${themeCssVariables.betweenSiblingsGap};
`;
type CollapsibleNavigationDrawerSectionProps = {
// Namespaced id (e.g. 'settings/User') used to persist open/closed state.
@@ -39,7 +47,7 @@ export const CollapsibleNavigationDrawerSection = ({
containAnimation
initial={false}
>
{children}
<StyledItems>{children}</StyledItems>
</AnimatedExpandableContainer>
</NavigationDrawerSection>
);
@@ -31,7 +31,7 @@ const StyledIconAndButtonContainer = styled.button`
font-family: ${themeCssVariables.font.family};
font-weight: ${themeCssVariables.font.weight.medium};
gap: ${themeCssVariables.spacing[2]};
padding: ${themeCssVariables.spacing['1.5']} ${themeCssVariables.spacing[1]};
padding: ${themeCssVariables.spacing[1]};
width: 100%;
&:hover {
background: ${themeCssVariables.background.transparent.light};
@@ -16,7 +16,7 @@ const StyledTitle = styled.div`
align-items: center;
border-radius: ${themeCssVariables.border.radius.sm};
display: flex;
height: ${themeCssVariables.spacing[5]};
height: ${themeCssVariables.spacing[7]};
justify-content: space-between;
padding-bottom: ${themeCssVariables.spacing[1]};
padding-left: ${themeCssVariables.spacing[1]};