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
@@ -1,7 +1,7 @@
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { NavigationDrawerAiChatContent } from '@/ai/components/NavigationDrawerAiChatContent';
import { MainNavigationDrawerNavigationContent } from '@/navigation/components/MainNavigationDrawerNavigationContent';
import { MainNavigationDrawerTabsRow } from '@/navigation/components/MainNavigationDrawerTabsRow';
import { NavigationDrawerTabbedContent } from '@/navigation/components/NavigationDrawerTabbedContent';
import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag';
import { NavigationDrawer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawer';
import { NavigationDrawerFixedContent } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerFixedContent';
@@ -32,11 +32,10 @@ export const MainNavigationDrawer = ({ className }: { className?: string }) => {
</NavigationDrawerFixedContent>
<NavigationDrawerScrollableContent>
{showAiChatContent ? (
<NavigationDrawerAiChatContent />
) : (
<MainNavigationDrawerNavigationContent />
)}
<NavigationDrawerTabbedContent
showAiChatContent={showAiChatContent}
navigationContent={<MainNavigationDrawerNavigationContent />}
/>
</NavigationDrawerScrollableContent>
</NavigationDrawer>
);
@@ -0,0 +1,36 @@
import { NavigationDrawerAiChatContent } from '@/ai/components/NavigationDrawerAiChatContent';
import { styled } from '@linaria/react';
import { type ReactNode, useState } from 'react';
type NavigationDrawerTabbedContentProps = {
showAiChatContent: boolean;
navigationContent: ReactNode;
};
const StyledTabContent = styled.div<{ isHidden: boolean }>`
display: ${({ isHidden }) => (isHidden ? 'none' : 'contents')};
`;
export const NavigationDrawerTabbedContent = ({
showAiChatContent,
navigationContent,
}: NavigationDrawerTabbedContentProps) => {
const [hasOpenedAiChat, setHasOpenedAiChat] = useState(showAiChatContent);
if (showAiChatContent && !hasOpenedAiChat) {
setHasOpenedAiChat(true);
}
return (
<>
<StyledTabContent isHidden={showAiChatContent}>
{navigationContent}
</StyledTabContent>
{hasOpenedAiChat && (
<StyledTabContent isHidden={!showAiChatContent}>
<NavigationDrawerAiChatContent />
</StyledTabContent>
)}
</>
);
};
@@ -1,5 +1,5 @@
import { NavigationDrawerAiChatContent } from '@/ai/components/NavigationDrawerAiChatContent';
import { MainNavigationDrawerTabsRow } from '@/navigation/components/MainNavigationDrawerTabsRow';
import { NavigationDrawerTabbedContent } from '@/navigation/components/NavigationDrawerTabbedContent';
import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag';
import { SettingsNavigationDrawerItems } from '@/settings/components/SettingsNavigationDrawerItems';
import { NavigationDrawer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawer';
@@ -18,10 +18,8 @@ import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
import { PermissionFlagType } from '~/generated-metadata/graphql';
const StyledAdvancedToggleWrapper = styled.div<{ isMobile: boolean }>`
padding-left: ${({ isMobile }) =>
isMobile ? '0' : themeCssVariables.spacing[5]};
padding-right: ${({ isMobile }) =>
isMobile ? '0' : themeCssVariables.spacing[8]};
isMobile ? '0' : themeCssVariables.spacing[1]};
`;
export const SettingsNavigationDrawer = ({
@@ -52,11 +50,10 @@ export const SettingsNavigationDrawer = ({
)}
<NavigationDrawerScrollableContent>
{showAiChatContent ? (
<NavigationDrawerAiChatContent />
) : (
<SettingsNavigationDrawerItems />
)}
<NavigationDrawerTabbedContent
showAiChatContent={showAiChatContent}
navigationContent={<SettingsNavigationDrawerItems />}
/>
</NavigationDrawerScrollableContent>
{!showAiChatContent && (
@@ -65,7 +62,7 @@ export const SettingsNavigationDrawer = ({
<AdvancedSettingsToggle
isAdvancedModeEnabled={isAdvancedModeEnabled}
setIsAdvancedModeEnabled={setIsAdvancedModeEnabled}
label={t`Advanced:`}
label={t`Advanced`}
/>
</StyledAdvancedToggleWrapper>
</NavigationDrawerFixedContent>
@@ -39,7 +39,6 @@ export const SettingsNavigationDrawerItem = ({
label={item.label}
to={href}
Icon={item.Icon}
withIconBackground
active={isActive}
modifier={item.modifier}
onClick={item.onClick}
@@ -55,7 +54,6 @@ export const SettingsNavigationDrawerItem = ({
label={item.label}
to={href || undefined}
Icon={item.Icon}
withIconBackground
active={isActive}
modifier={item.modifier}
onClick={item.onClick}
@@ -1,6 +1,4 @@
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { SettingsSectionSkeletonLoader } from '@/settings/components/SettingsSectionSkeletonLoader';
import { PageCardHeader } from '@/ui/layout/page/components/PageCardHeader';
import { PageCardLayout } from '@/ui/layout/page/components/PageCardLayout';
import { useContext } from 'react';
@@ -39,9 +37,7 @@ export const SettingsSkeletonLoader = () => {
}
showInformationBanner={false}
>
<SettingsPageContainer>
<SettingsSectionSkeletonLoader />
</SettingsPageContainer>
{null}
</PageCardLayout>
</SkeletonTheme>
);
@@ -29,6 +29,7 @@ export const SettingsPageLayout = ({
title={title}
tag={tag}
actionButton={actionButton}
centerTitle
/>
}
secondaryBar={
@@ -1,135 +0,0 @@
import { type SettingsFieldType } from '@/settings/data-model/types/SettingsFieldType';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { useContext } from 'react';
import { useLocation, useParams, useSearchParams } from 'react-router-dom';
import { SettingsPath } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { IconChevronDown } from 'twenty-ui-deprecated/display';
import { Button } from 'twenty-ui-deprecated/input';
import { MenuItem } from 'twenty-ui-deprecated/navigation';
import {
ThemeContext,
themeCssVariables,
} from 'twenty-ui-deprecated/theme-constants';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
const StyledContainer = styled.div`
align-items: center;
color: ${themeCssVariables.font.color.tertiary};
cursor: default;
display: flex;
font-size: ${themeCssVariables.font.size.md};
`;
const StyledButtonContainer = styled.div`
position: relative;
width: 100%;
`;
const StyledDownChevronContainer = styled.span`
align-items: center;
color: ${themeCssVariables.font.color.primary};
display: flex;
position: absolute;
right: ${themeCssVariables.spacing['1.5']};
top: 50%;
transform: translateY(-50%);
`;
const StyledMenuItemWrapper = styled.div<{ disabled?: boolean }>`
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
width: 100%;
`;
const StyledSpan = styled.span`
margin-left: ${themeCssVariables.spacing[2]};
`;
const StyledButtonWrapper = styled.div`
button {
color: ${themeCssVariables.font.color.primary};
padding-right: ${themeCssVariables.spacing[6]};
}
`;
export const SettingsDataModelNewFieldBreadcrumbDropDown = () => {
const { theme } = useContext(ThemeContext);
const dropdownId = `settings-object-new-field-breadcrumb-dropdown`;
const { closeDropdown } = useCloseDropdown();
const navigate = useNavigateSettings();
const location = useLocation();
const { objectNamePlural = '' } = useParams();
const [searchParams] = useSearchParams();
const fieldType = searchParams.get('fieldType') as SettingsFieldType;
const isConfigureStep = location.pathname.includes('/configure');
const handleClick = (step: 'select' | 'configure') => {
if (step === 'configure' && isDefined(fieldType)) {
navigate(
SettingsPath.ObjectNewFieldConfigure,
{ objectNamePlural },
{ fieldType },
);
} else {
navigate(
SettingsPath.ObjectNewFieldSelect,
{ objectNamePlural },
fieldType ? { fieldType } : undefined,
);
}
closeDropdown(dropdownId);
};
return (
<StyledContainer>
{t`New Field`} <StyledSpan>-</StyledSpan>
<Dropdown
dropdownPlacement="bottom-start"
dropdownId={dropdownId}
clickableComponent={
<StyledButtonContainer>
<StyledDownChevronContainer>
<IconChevronDown size={theme.icon.size.md} />
</StyledDownChevronContainer>
{isConfigureStep ? (
<StyledButtonWrapper>
<Button variant="tertiary" title={t`2. Configure`} />
</StyledButtonWrapper>
) : (
<StyledButtonWrapper>
<Button variant="tertiary" title={t`1. Type`} />
</StyledButtonWrapper>
)}
</StyledButtonContainer>
}
dropdownComponents={
<DropdownContent>
<DropdownMenuItemsContainer>
<StyledMenuItemWrapper>
<MenuItem
text={t`1. Type`}
onClick={() => handleClick('select')}
selected={!isConfigureStep}
/>
</StyledMenuItemWrapper>
<StyledMenuItemWrapper disabled={!isDefined(fieldType)}>
<MenuItem
text={t`2. Configure`}
onClick={() => handleClick('configure')}
selected={isConfigureStep}
disabled={!isDefined(fieldType)}
/>
</StyledMenuItemWrapper>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
</StyledContainer>
);
};
@@ -71,15 +71,10 @@ const StyledLoadingMore = styled.div`
text-align: center;
`;
const StyledSkeletonContainer = styled.div`
padding: ${themeCssVariables.spacing[2]};
`;
const StyledIntersectionObserver = styled.div`
height: 1px;
`;
const SKELETON_ROW_COUNT = 8;
const EVENT_LOG_SCROLL_WRAPPER_INSTANCE_ID = 'event-log-results-table';
const buildGridTemplateColumns = (
@@ -192,19 +187,23 @@ export const EventLogResultsTable = ({
<TableHeader key={column.id}>{t(column.label)}</TableHeader>
))}
</TableRow>
<TableRow gridTemplateColumns={gridTemplateColumns}>
{baseColumns.map((column, index) => (
<TableCell key={column.id}>
{index === 0 && (
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
borderRadius={4}
>
<Skeleton width={120} height={16} />
</SkeletonTheme>
)}
</TableCell>
))}
</TableRow>
</Table>
</StyledTableContainer>
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
borderRadius={4}
>
<StyledSkeletonContainer>
{Array.from({ length: SKELETON_ROW_COUNT }).map((_, index) => (
<Skeleton height={40} key={index} style={{ marginBottom: 4 }} />
))}
</StyledSkeletonContainer>
</SkeletonTheme>
</ScrollWrapper>
</StyledScrollWrapperContainer>
);
@@ -1,6 +1,8 @@
import { SIDE_PANEL_TOP_BAR_HEIGHT_MOBILE } from '@/side-panel/constants/SidePanelTopBarHeightMobile';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { isSidePanelOpenedState } from '@/side-panel/states/isSidePanelOpenedState';
import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState';
import { SidePanelPages } from 'twenty-shared/types';
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { RootStackingContextZIndices } from '@/ui/layout/constants/RootStackingContextZIndices';
import { PAGE_HEADER_SIDE_PANEL_BUTTON_CLICK_OUTSIDE_ID } from '@/ui/layout/page-header/constants/PageHeaderSidePanelButtonClickOutsideId';
@@ -130,12 +132,23 @@ const AnimatedIcon = ({
export const SidePanelToggleButton = () => {
const { toggleSidePanelMenu } = useSidePanelMenu();
const isSidePanelOpened = useAtomStateValue(isSidePanelOpenedState);
const sidePanelPage = useAtomStateValue(sidePanelPageState);
const isLayoutCustomizationModeEnabled = useAtomStateValue(
isLayoutCustomizationModeEnabledState,
);
const isMobile = useIsMobile();
const isCommandMenuOpened =
isSidePanelOpened &&
[
SidePanelPages.CommandMenuDisplay,
SidePanelPages.CommandMenuEdit,
SidePanelPages.SearchRecords,
].includes(sidePanelPage);
const showAsOpen = isSidePanelOpened && !isCommandMenuOpened;
const alignWithSidePanelTopBar =
isMobile && isLayoutCustomizationModeEnabled && isSidePanelOpened;
@@ -148,7 +161,7 @@ export const SidePanelToggleButton = () => {
<StyledButtonWrapper alignToTop={alignWithSidePanelTopBar}>
<div id="toggle-side-panel-button">
<AnimatedButton
animatedSvg={<AnimatedIcon isSidePanelOpened={isSidePanelOpened} />}
animatedSvg={<AnimatedIcon isSidePanelOpened={showAsOpen} />}
dataClickOutsideId={PAGE_HEADER_SIDE_PANEL_BUTTON_CLICK_OUTSIDE_ID}
dataTestId="page-header-side-panel-button"
size={isMobile ? 'medium' : 'small'}
@@ -158,7 +171,7 @@ export const SidePanelToggleButton = () => {
ariaLabel={ariaLabel}
onClick={toggleSidePanelMenu}
animate={{
rotate: isSidePanelOpened ? 90 : 0,
rotate: showAsOpen ? 90 : 0,
}}
transition={{
duration: theme.animation.duration.normal,
@@ -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]};
@@ -1,9 +1,8 @@
import { isDDLLockedState } from '@/client-config/states/isDDLLockedState';
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { SettingsDataModelNewFieldBreadcrumbDropDown } from '@/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown';
import { SettingsWizardStepBar } from '@/settings/components/layout/SettingsWizardStepBar';
import { FIELD_NAME_MAXIMUM_LENGTH } from '@/settings/data-model/constants/FieldNameMaximumLength';
import { SettingsDataModelFieldIconLabelForm } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldIconLabelForm';
import { SettingsDataModelFieldSettingsFormCard } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard';
@@ -23,6 +22,7 @@ import {
} from 'twenty-shared/types';
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
import { H2Title } from 'twenty-ui-deprecated/display';
import { Button } from 'twenty-ui-deprecated/input';
import { Section } from 'twenty-ui-deprecated/layout';
import { type z } from 'zod';
import { FieldMetadataType } from '~/generated-metadata/graphql';
@@ -181,7 +181,7 @@ export const SettingsObjectNewFieldConfigure = () => {
{...formConfig}
>
<SettingsPageLayout
title={t`2. Configure field`}
title={activeObjectMetadataItem.labelPlural}
links={[
{
children: t`Workspace`,
@@ -197,26 +197,28 @@ export const SettingsObjectNewFieldConfigure = () => {
objectNamePlural,
}),
},
{ children: <SettingsDataModelNewFieldBreadcrumbDropDown /> },
{ children: t`New field` },
]}
actionButton={
<SaveAndCancelButtons
isLoading={isSaving}
isSaveDisabled={!canSave}
isCancelDisabled={isSubmitting}
onCancel={() =>
secondaryBar={
<SettingsWizardStepBar
label={t`2. Configure field`}
onBack={() =>
navigate(
SettingsPath.ObjectNewFieldSelect,
{
objectNamePlural,
},
{
fieldType,
},
{ objectNamePlural },
{ fieldType },
)
}
onSave={formConfig.handleSubmit(handleSave)}
trailing={
<Button
title={t`Save`}
variant="primary"
size="small"
accent="blue"
onClick={formConfig.handleSubmit(handleSave)}
disabled={!canSave || isSaving}
/>
}
/>
}
>
@@ -1,11 +1,11 @@
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { SettingsDataModelNewFieldBreadcrumbDropDown } from '@/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown';
import { SettingsPageLayout } from '@/settings/components/layout/SettingsPageLayout';
import { SettingsWizardStepBar } from '@/settings/components/layout/SettingsWizardStepBar';
import { SETTINGS_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsFieldTypeConfigs';
import { SettingsObjectNewFieldSelector } from '@/settings/data-model/fields/forms/components/SettingsObjectNewFieldSelector';
import { type FieldType } from '@/settings/data-model/types/FieldType';
import { type SettingsFieldType } from '@/settings/data-model/types/SettingsFieldType';
import { SettingsPageLayout } from '@/settings/components/layout/SettingsPageLayout';
import { zodResolver } from '@hookform/resolvers/zod';
import { t } from '@lingui/core/macro';
import { useEffect } from 'react';
@@ -65,7 +65,7 @@ export const SettingsObjectNewFieldSelect = () => {
{...formMethods}
>
<SettingsPageLayout
title={t`1. Select a field type`}
title={activeObjectMetadataItem.labelPlural}
links={[
{
children: t`Workspace`,
@@ -78,8 +78,11 @@ export const SettingsObjectNewFieldSelect = () => {
objectNamePlural,
}),
},
{ children: <SettingsDataModelNewFieldBreadcrumbDropDown /> },
{ children: t`New field` },
]}
secondaryBar={
<SettingsWizardStepBar label={t`1. Select a field type`} />
}
>
<SettingsPageContainer>
<SettingsObjectNewFieldSelector
@@ -1,16 +1,17 @@
import { styled } from '@linaria/react';
import { IconPoint } from '@ui/display';
import { Toggle } from '@ui/input';
import { ThemeContext, themeCssVariables } from '@ui/theme-constants';
import { useContext, useId } from 'react';
const StyledContainer = styled.div`
const StyledContainer = styled.label`
align-items: center;
box-sizing: border-box;
cursor: pointer;
display: flex;
gap: ${themeCssVariables.spacing[2]};
position: relative;
height: ${themeCssVariables.spacing[5]};
justify-content: space-between;
padding: ${themeCssVariables.spacing[1]};
width: 100%;
`;
const StyledText = styled.div`
@@ -19,21 +20,6 @@ const StyledText = styled.div`
font-weight: ${themeCssVariables.font.weight.medium};
`;
const StyledIconContainer = styled.div`
align-items: center;
display: flex;
left: calc(-1 * ${themeCssVariables.spacing[5]});
position: absolute;
`;
const StyledToggleContainer = styled.label`
align-items: center;
cursor: pointer;
display: flex;
justify-content: space-between;
width: 100%;
`;
type AdvancedSettingsToggleProps = {
isAdvancedModeEnabled: boolean;
setIsAdvancedModeEnabled: (enabled: boolean) => void;
@@ -43,7 +29,7 @@ type AdvancedSettingsToggleProps = {
export const AdvancedSettingsToggle = ({
isAdvancedModeEnabled,
setIsAdvancedModeEnabled,
label = 'Advanced:',
label = 'Advanced',
}: AdvancedSettingsToggleProps) => {
const { theme } = useContext(ThemeContext);
@@ -52,23 +38,15 @@ export const AdvancedSettingsToggle = ({
};
const instanceId = useId();
const yellowColor = theme.color.yellow;
return (
<StyledContainer>
<StyledIconContainer>
<IconPoint size={12} color={yellowColor} fill={yellowColor} />
</StyledIconContainer>
<StyledToggleContainer htmlFor={instanceId}>
<StyledText>{label}</StyledText>
<Toggle
id={instanceId}
onChange={onChange}
color={yellowColor}
value={isAdvancedModeEnabled}
/>
</StyledToggleContainer>
<StyledContainer htmlFor={instanceId}>
<StyledText>{label}</StyledText>
<Toggle
id={instanceId}
onChange={onChange}
color={theme.color.yellow}
value={isAdvancedModeEnabled}
/>
</StyledContainer>
);
};