Settings Advanced Mode (#7130)

### Description

- We implemented the Advanced Mode state and used this on a section of
the settings sidebar
- in DefaultLayout.tsx, was updated because of the 64 + 16(container
size of IconTool + the margins)

### <https://jam.dev/c/29bcec70-0b7f-4afa-98e6-9755657cf09d>

### Refs

#6147 

Fixes #6147

---------

Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: gitstart-twenty <140154534+gitstart-twenty@users.noreply.github.com>
This commit is contained in:
gitstart-app[bot]
2024-10-02 17:04:07 +02:00
committed by GitHub
parent 57eaa01d35
commit 23001ac17d
11 changed files with 221 additions and 30 deletions

View File

@ -4,7 +4,6 @@ import { useRecoilValue, useSetRecoilState } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { SettingsNavigationDrawerItems } from '@/settings/components/SettingsNavigationDrawerItems'; import { SettingsNavigationDrawerItems } from '@/settings/components/SettingsNavigationDrawerItems';
import { SupportDropdown } from '@/support/components/SupportDropdown'; import { SupportDropdown } from '@/support/components/SupportDropdown';
import { GithubVersionLink } from '@/ui/navigation/link/components/GithubVersionLink';
import { import {
NavigationDrawer, NavigationDrawer,
NavigationDrawerProps, NavigationDrawerProps,
@ -16,6 +15,7 @@ import { getImageAbsoluteURI } from '~/utils/image/getImageAbsoluteURI';
import { useIsSettingsPage } from '../hooks/useIsSettingsPage'; import { useIsSettingsPage } from '../hooks/useIsSettingsPage';
import { currentMobileNavigationDrawerState } from '../states/currentMobileNavigationDrawerState'; import { currentMobileNavigationDrawerState } from '../states/currentMobileNavigationDrawerState';
import { AdvancedSettingsToggle } from '@/ui/navigation/link/components/AdvancedSettingsToggle';
import { MainNavigationDrawerItems } from './MainNavigationDrawerItems'; import { MainNavigationDrawerItems } from './MainNavigationDrawerItems';
export type AppNavigationDrawerProps = { export type AppNavigationDrawerProps = {
@ -44,7 +44,7 @@ export const AppNavigationDrawer = ({
isSubMenu: true, isSubMenu: true,
title: 'Exit Settings', title: 'Exit Settings',
children: <SettingsNavigationDrawerItems />, children: <SettingsNavigationDrawerItems />,
footer: <GithubVersionLink />, footer: <AdvancedSettingsToggle />,
} }
: { : {
logo: logo:

View File

@ -13,13 +13,16 @@ import {
IconMail, IconMail,
IconRocket, IconRocket,
IconSettings, IconSettings,
IconTool,
IconUserCircle, IconUserCircle,
IconUsers, IconUsers,
MAIN_COLORS,
} from 'twenty-ui'; } from 'twenty-ui';
import { useAuth } from '@/auth/hooks/useAuth'; import { useAuth } from '@/auth/hooks/useAuth';
import { billingState } from '@/client-config/states/billingState'; import { billingState } from '@/client-config/states/billingState';
import { SettingsNavigationDrawerItem } from '@/settings/components/SettingsNavigationDrawerItem'; import { SettingsNavigationDrawerItem } from '@/settings/components/SettingsNavigationDrawerItem';
import { useExpandedHeightAnimation } from '@/settings/hooks/useExpandedHeightAnimation';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath'; import { SettingsPath } from '@/types/SettingsPath';
import { import {
@ -29,10 +32,38 @@ import {
import { NavigationDrawerItemGroup } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItemGroup'; import { NavigationDrawerItemGroup } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItemGroup';
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection'; import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle'; import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
import { getNavigationSubItemState } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemState'; import { getNavigationSubItemState } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemState';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import styled from '@emotion/styled';
import { AnimatePresence, motion } from 'framer-motion';
import { matchPath, resolvePath, useLocation } from 'react-router-dom'; import { matchPath, resolvePath, useLocation } from 'react-router-dom';
const StyledNavigationDrawerSection = styled(NavigationDrawerSection)<{
withLeftMargin?: boolean;
}>`
margin-left: ${({ withLeftMargin, theme }) =>
withLeftMargin && theme.spacing(5)};
margin-top: ${({ theme }) => theme.spacing(3)};
`;
const StyledIconContainer = styled.div`
border-right: 1px solid ${MAIN_COLORS.yellow};
display: flex;
margin-top: ${({ theme }) => theme.spacing(5)};
width: 16px;
`;
const StyledDeveloperSection = styled.div`
display: flex;
width: 100%;
gap: ${({ theme }) => theme.spacing(1)};
`;
const StyledIconTool = styled(IconTool)`
margin-right: ${({ theme }) => theme.spacing(0.5)};
`;
type SettingsNavigationItem = { type SettingsNavigationItem = {
label: string; label: string;
path: SettingsPath; path: SettingsPath;
@ -42,6 +73,10 @@ type SettingsNavigationItem = {
}; };
export const SettingsNavigationDrawerItems = () => { export const SettingsNavigationDrawerItems = () => {
const isAdvancedModeEnabled = useRecoilValue(isAdvancedModeEnabledState);
const { contentRef, motionAnimationVariants } = useExpandedHeightAnimation(
isAdvancedModeEnabled,
);
const { signOut } = useAuth(); const { signOut } = useAuth();
const billing = useRecoilValue(billingState); const billing = useRecoilValue(billingState);
@ -88,7 +123,7 @@ export const SettingsNavigationDrawerItems = () => {
return ( return (
<> <>
<NavigationDrawerSection> <StyledNavigationDrawerSection withLeftMargin>
<NavigationDrawerSectionTitle label="User" /> <NavigationDrawerSectionTitle label="User" />
<SettingsNavigationDrawerItem <SettingsNavigationDrawerItem
label="Profile" label="Profile"
@ -121,8 +156,8 @@ export const SettingsNavigationDrawerItems = () => {
/> />
))} ))}
</NavigationDrawerItemGroup> </NavigationDrawerItemGroup>
</NavigationDrawerSection> </StyledNavigationDrawerSection>
<NavigationDrawerSection> <StyledNavigationDrawerSection withLeftMargin>
<NavigationDrawerSectionTitle label="Workspace" /> <NavigationDrawerSectionTitle label="Workspace" />
<SettingsNavigationDrawerItem <SettingsNavigationDrawerItem
label="General" label="General"
@ -147,18 +182,6 @@ export const SettingsNavigationDrawerItems = () => {
Icon={IconHierarchy2} Icon={IconHierarchy2}
matchSubPages matchSubPages
/> />
<SettingsNavigationDrawerItem
label="Developers"
path={SettingsPath.Developers}
Icon={IconCode}
/>
{isFunctionSettingsEnabled && (
<SettingsNavigationDrawerItem
label="Functions"
path={SettingsPath.ServerlessFunctions}
Icon={IconFunction}
/>
)}
<SettingsNavigationDrawerItem <SettingsNavigationDrawerItem
label="Integrations" label="Integrations"
path={SettingsPath.Integrations} path={SettingsPath.Integrations}
@ -171,8 +194,40 @@ export const SettingsNavigationDrawerItems = () => {
Icon={IconCode} Icon={IconCode}
/> />
)} )}
</NavigationDrawerSection> </StyledNavigationDrawerSection>
<NavigationDrawerSection> <AnimatePresence>
{isAdvancedModeEnabled && (
<motion.div
ref={contentRef}
initial="initial"
animate="animate"
exit="exit"
variants={motionAnimationVariants}
>
<StyledDeveloperSection>
<StyledIconContainer>
<StyledIconTool size={12} color={MAIN_COLORS.yellow} />
</StyledIconContainer>
<StyledNavigationDrawerSection>
<NavigationDrawerSectionTitle label="Developers" />
<SettingsNavigationDrawerItem
label="API & Webhooks"
path={SettingsPath.Developers}
Icon={IconCode}
/>
{isFunctionSettingsEnabled && (
<SettingsNavigationDrawerItem
label="Functions"
path={SettingsPath.ServerlessFunctions}
Icon={IconFunction}
/>
)}
</StyledNavigationDrawerSection>
</StyledDeveloperSection>
</motion.div>
)}
</AnimatePresence>
<StyledNavigationDrawerSection withLeftMargin>
<NavigationDrawerSectionTitle label="Other" /> <NavigationDrawerSectionTitle label="Other" />
<SettingsNavigationDrawerItem <SettingsNavigationDrawerItem
label="Releases" label="Releases"
@ -184,7 +239,7 @@ export const SettingsNavigationDrawerItems = () => {
onClick={signOut} onClick={signOut}
Icon={IconDoorEnter} Icon={IconDoorEnter}
/> />
</NavigationDrawerSection> </StyledNavigationDrawerSection>
</> </>
); );
}; };

View File

@ -0,0 +1,57 @@
import { useEffect, useRef, useState } from 'react';
import { isDefined } from 'twenty-ui';
const transitionValues = {
transition: {
opactity: { duration: 0.2 },
height: { duration: 0.4 },
},
transitionEnd: {
overflow: 'visible',
},
};
const commonStyles = {
opacity: 0,
height: 0,
overflow: 'hidden',
...transitionValues,
};
const advancedSectionAnimationConfig = (
isExpanded: boolean,
measuredHeight: number,
) => ({
initial: {
...commonStyles,
},
animate: {
opacity: 1,
height: isExpanded ? measuredHeight : 0,
...transitionValues,
overflow: 'hidden',
},
exit: {
...commonStyles,
},
});
export const useExpandedHeightAnimation = (isExpanded: boolean) => {
const contentRef = useRef<HTMLDivElement>(null);
const [measuredHeight, setMeasuredHeight] = useState(0);
useEffect(() => {
if (isDefined(contentRef.current)) {
setMeasuredHeight(contentRef.current.scrollHeight);
}
}, [isExpanded]);
return {
contentRef,
measuredHeight,
motionAnimationVariants: advancedSectionAnimationConfig(
isExpanded,
measuredHeight,
),
};
};

View File

@ -1,7 +1,4 @@
import { css, Global, useTheme } from '@emotion/react'; import { AuthModal } from '@/auth/components/AuthModal';
import styled from '@emotion/styled';
import { AnimatePresence, LayoutGroup, motion } from 'framer-motion';
import { Outlet } from 'react-router-dom';
import { CommandMenu } from '@/command-menu/components/CommandMenu'; import { CommandMenu } from '@/command-menu/components/CommandMenu';
import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary'; import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary';
import { KeyboardShortcutMenu } from '@/keyboard-shortcut-menu/components/KeyboardShortcutMenu'; import { KeyboardShortcutMenu } from '@/keyboard-shortcut-menu/components/KeyboardShortcutMenu';
@ -14,7 +11,10 @@ import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal';
import { DESKTOP_NAV_DRAWER_WIDTHS } from '@/ui/navigation/navigation-drawer/constants/DesktopNavDrawerWidths'; import { DESKTOP_NAV_DRAWER_WIDTHS } from '@/ui/navigation/navigation-drawer/constants/DesktopNavDrawerWidths';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { useScreenSize } from '@/ui/utilities/screen-size/hooks/useScreenSize'; import { useScreenSize } from '@/ui/utilities/screen-size/hooks/useScreenSize';
import { AuthModal } from '@/auth/components/AuthModal'; import { css, Global, useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { AnimatePresence, LayoutGroup, motion } from 'framer-motion';
import { Outlet } from 'react-router-dom';
const StyledLayout = styled.div` const StyledLayout = styled.div`
background: ${({ theme }) => theme.background.noisy}; background: ${({ theme }) => theme.background.noisy};
@ -85,7 +85,7 @@ export const DefaultLayout = () => {
? (windowsWidth - ? (windowsWidth -
(OBJECT_SETTINGS_WIDTH + (OBJECT_SETTINGS_WIDTH +
DESKTOP_NAV_DRAWER_WIDTHS.menu + DESKTOP_NAV_DRAWER_WIDTHS.menu +
64)) / 88)) /
2 2
: 0, : 0,
}} }}

View File

@ -0,0 +1,62 @@
import { Toggle } from '@/ui/input/components/Toggle';
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { IconTool, MAIN_COLORS } from 'twenty-ui';
const StyledContainer = styled.div`
align-items: center;
display: flex;
width: 100%;
gap: ${({ theme }) => theme.spacing(2)};
`;
const StyledText = styled.span`
color: ${({ theme }) => theme.font.color.secondary};
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.medium};
padding: ${({ theme }) => theme.spacing(1)};
`;
const StyledIconContainer = styled.div`
border-right: 1px solid ${MAIN_COLORS.yellow};
display: flex;
height: 16px;
`;
const StyledToggleContainer = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
`;
const StyledIconTool = styled(IconTool)`
margin-right: ${({ theme }) => theme.spacing(0.5)};
`;
export const AdvancedSettingsToggle = () => {
const [isAdvancedModeEnabled, setIsAdvancedModeEnabled] = useRecoilState(
isAdvancedModeEnabledState,
);
const onChange = (newValue: boolean) => {
setIsAdvancedModeEnabled(newValue);
};
return (
<StyledContainer>
<StyledIconContainer>
<StyledIconTool size={12} color={MAIN_COLORS.yellow} />
</StyledIconContainer>
<StyledToggleContainer>
<StyledText>Advanced</StyledText>
<Toggle
onChange={onChange}
color={MAIN_COLORS.yellow}
value={isAdvancedModeEnabled}
/>
</StyledToggleContainer>
</StyledContainer>
);
};

View File

@ -40,6 +40,7 @@ const StyledContainer = styled.div<{ isSubMenu?: boolean }>`
${({ isSubMenu, theme }) => ${({ isSubMenu, theme }) =>
isSubMenu isSubMenu
? css` ? css`
padding-left: ${theme.spacing(0)};
padding-right: ${theme.spacing(8)}; padding-right: ${theme.spacing(8)};
` `
: ''} : ''}
@ -48,13 +49,12 @@ const StyledContainer = styled.div<{ isSubMenu?: boolean }>`
width: 100%; width: 100%;
} }
`; `;
const StyledItemsContainer = styled.div<{ isSubMenu?: boolean }>`
const StyledItemsContainer = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: ${({ theme }) => theme.spacing(3)};
margin-bottom: auto; margin-bottom: auto;
overflow-y: auto; overflow-y: auto;
${({ isSubMenu, theme }) => !isSubMenu && `gap: ${theme.spacing(3)}`}
`; `;
export const NavigationDrawer = ({ export const NavigationDrawer = ({
@ -111,7 +111,9 @@ export const NavigationDrawer = ({
showCollapseButton={isHovered} showCollapseButton={isHovered}
/> />
)} )}
<StyledItemsContainer>{children}</StyledItemsContainer> <StyledItemsContainer isSubMenu={isSubMenu}>
{children}
</StyledItemsContainer>
{footer} {footer}
</StyledContainer> </StyledContainer>
</StyledAnimatedContainer> </StyledAnimatedContainer>

View File

@ -35,6 +35,7 @@ const StyledContainer = styled.div`
flex-direction: row; flex-direction: row;
height: ${({ theme }) => theme.spacing(8)}; height: ${({ theme }) => theme.spacing(8)};
justify-content: space-between; justify-content: space-between;
margin-left: ${({ theme }) => theme.spacing(5)};
`; `;
export const NavigationDrawerBackButton = ({ export const NavigationDrawerBackButton = ({

View File

@ -4,6 +4,7 @@ const StyledSection = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: ${({ theme }) => theme.betweenSiblingsGap}; gap: ${({ theme }) => theme.betweenSiblingsGap};
width: 100%;
`; `;
export { StyledSection as NavigationDrawerSection }; export { StyledSection as NavigationDrawerSection };

View File

@ -0,0 +1,8 @@
import { atom } from 'recoil';
import { localStorageEffect } from '~/utils/recoil-effects';
export const isAdvancedModeEnabledState = atom<boolean>({
key: 'isAdvancedModeEnabledAtom',
default: false,
effects: [localStorageEffect()],
});

View File

@ -9,6 +9,7 @@ import { WorkspaceLogoUploader } from '@/settings/workspace/components/Workspace
import { SettingsPath } from '@/types/SettingsPath'; import { SettingsPath } from '@/types/SettingsPath';
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
import { Section } from '@/ui/layout/section/components/Section'; import { Section } from '@/ui/layout/section/components/Section';
import { GithubVersionLink } from '@/ui/navigation/link/components/GithubVersionLink';
export const SettingsWorkspace = () => ( export const SettingsWorkspace = () => (
<SubMenuTopBarContainer <SubMenuTopBarContainer
@ -41,6 +42,9 @@ export const SettingsWorkspace = () => (
<Section> <Section>
<DeleteWorkspace /> <DeleteWorkspace />
</Section> </Section>
<Section>
<GithubVersionLink />
</Section>
</SettingsPageContainer> </SettingsPageContainer>
</SubMenuTopBarContainer> </SubMenuTopBarContainer>
); );

View File

@ -173,6 +173,7 @@ export {
IconTestPipe, IconTestPipe,
IconTextSize, IconTextSize,
IconTimelineEvent, IconTimelineEvent,
IconTool,
IconTrash, IconTrash,
IconUnlink, IconUnlink,
IconUpload, IconUpload,