Feat/improve mobile display (#843)

* Ok 1

* Finished

* Fix PR

* Fix PR

* Fix desktop

* Fix

* Fix absolute listen click outside

* console.log

* Fix according to code review

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Lucas Bordeau
2023-07-23 19:53:35 +02:00
committed by GitHub
parent 742791bd92
commit 21d5133564
45 changed files with 464 additions and 315 deletions

View File

@ -37,6 +37,7 @@
"react-hotkeys-hook": "^4.4.0",
"react-loading-skeleton": "^3.3.1",
"react-modal": "^3.16.1",
"react-responsive": "^9.0.2",
"react-router-dom": "^6.4.4",
"react-textarea-autosize": "^8.4.1",
"react-tooltip": "^5.13.1",

View File

@ -11,7 +11,7 @@ import {
IconTargetArrow,
IconUser,
} from '@/ui/icon/index';
import { useIsSubNavbarDisplayed } from '@/ui/layout/hooks/useIsSubNavbarDisplayed';
import { useIsSubMenuNavbarDisplayed } from '@/ui/layout/hooks/useIsSubMenuNavbarDisplayed';
import MainNavbar from '@/ui/navbar/components/MainNavbar';
import NavItem from '@/ui/navbar/components/NavItem';
import NavTitle from '@/ui/navbar/components/NavTitle';
@ -21,51 +21,49 @@ export function AppNavbar() {
const currentPath = useLocation().pathname;
const { openCommandMenu } = useCommandMenu();
const isSubNavbarDisplayed = useIsSubNavbarDisplayed();
const isInSubMenu = useIsSubMenuNavbarDisplayed();
return (
<>
{!isSubNavbarDisplayed ? (
{!isInSubMenu ? (
<MainNavbar>
<>
<NavItem
label="Search"
icon={<IconSearch size={theme.icon.size.md} />}
onClick={() => {
openCommandMenu();
}}
/>
<NavItem
label="Inbox"
to="/inbox"
icon={<IconInbox size={theme.icon.size.md} />}
soon={true}
/>
<NavItem
label="Settings"
to="/settings/profile"
icon={<IconSettings size={theme.icon.size.md} />}
/>
<NavTitle label="Workspace" />
<NavItem
label="Companies"
to="/companies"
icon={<IconBuildingSkyscraper size={theme.icon.size.md} />}
active={currentPath === '/companies'}
/>
<NavItem
label="People"
to="/people"
icon={<IconUser size={theme.icon.size.md} />}
active={currentPath === '/people'}
/>
<NavItem
label="Opportunities"
to="/opportunities"
icon={<IconTargetArrow size={theme.icon.size.md} />}
active={currentPath === '/opportunities'}
/>
</>
<NavItem
label="Search"
icon={<IconSearch size={theme.icon.size.md} />}
onClick={() => {
openCommandMenu();
}}
/>
<NavItem
label="Inbox"
to="/inbox"
icon={<IconInbox size={theme.icon.size.md} />}
soon={true}
/>
<NavItem
label="Settings"
to="/settings/profile"
icon={<IconSettings size={theme.icon.size.md} />}
/>
<NavTitle label="Workspace" />
<NavItem
label="Companies"
to="/companies"
icon={<IconBuildingSkyscraper size={theme.icon.size.md} />}
active={currentPath === '/companies'}
/>
<NavItem
label="People"
to="/people"
icon={<IconUser size={theme.icon.size.md} />}
active={currentPath === '/people'}
/>
<NavItem
label="Opportunities"
to="/opportunities"
icon={<IconTargetArrow size={theme.icon.size.md} />}
active={currentPath === '/opportunities'}
/>
</MainNavbar>
) : (
<SettingsNavbar />

View File

@ -4,6 +4,7 @@ import { useRecoilValue } from 'recoil';
import { v4 } from 'uuid';
import { currentUserState } from '@/auth/states/currentUserState';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import { AutosizeTextInput } from '@/ui/input/components/AutosizeTextInput';
import { CommentThread, useCreateCommentMutation } from '~/generated/graphql';
import { isNonEmptyString } from '~/utils/isNonEmptyString';
@ -38,7 +39,10 @@ const StyledCommentActionBar = styled.div`
border-top: 1px solid ${({ theme }) => theme.border.color.light};
display: flex;
padding: 16px 24px 16px 48px;
width: calc(${({ theme }) => theme.rightDrawerWidth} - 48px - 24px);
width: calc(
${({ theme }) => (useIsMobile() ? '100%' : theme.rightDrawerWidth)} - 48px -
24px
);
`;
const StyledThreadCommentTitle = styled.div`

View File

@ -12,7 +12,7 @@ import { CompanyChip } from '@/companies/components/CompanyChip';
import { useFilteredSearchCompanyQuery } from '@/companies/queries';
import { PersonChip } from '@/people/components/PersonChip';
import { useFilteredSearchPeopleQuery } from '@/people/queries';
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
import { useListenClickOutside } from '@/ui/hooks/useListenClickOutside';
import { usePreviousHotkeyScope } from '@/ui/hotkey/hooks/usePreviousHotkeyScope';
import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys';
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
@ -194,7 +194,7 @@ export function CommentThreadRelationPicker({ commentThread }: OwnProps) {
placement: 'bottom-start',
});
useListenClickOutsideArrayOfRef({
useListenClickOutside({
refs: [refs.floating, refs.domReference],
callback: () => {
exitEditMode();

View File

@ -9,6 +9,7 @@ import { CommentThreadTypeDropdown } from '@/activities/components/CommentThread
import { GET_COMMENT_THREAD } from '@/activities/queries';
import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox';
import { PropertyBoxItem } from '@/ui/editable-field/property-box/components/PropertyBoxItem';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import { IconArrowUpRight } from '@/ui/icon/index';
import {
useGetCommentThreadQuery,
@ -43,7 +44,8 @@ const StyledTopContainer = styled.div`
align-items: flex-start;
align-self: stretch;
background: ${({ theme }) => theme.background.secondary};
border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
border-bottom: ${({ theme }) =>
useIsMobile() ? 'none' : `1px solid ${theme.border.color.medium}`};
display: flex;
flex-direction: column;
gap: 24px;
@ -187,7 +189,6 @@ export function CommentThread({
onChange={updateTitleFromBody}
/>
</StyledUpperPartContainer>
{showComment && (
<CommentThreadComments
commentThread={{

View File

@ -7,6 +7,7 @@ import { useOpenCommentThreadRightDrawer } from '@/activities/hooks/useOpenComme
import { useOpenCreateCommentThreadDrawer } from '@/activities/hooks/useOpenCreateCommentThreadDrawer';
import { CommentableEntity } from '@/activities/types/CommentableEntity';
import { CommentThreadForDrawer } from '@/activities/types/CommentThreadForDrawer';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import { IconNotes } from '@/ui/icon/index';
import {
ActivityType,
@ -23,21 +24,26 @@ import { OverflowingTextWithTooltip } from '../../../ui/tooltip/OverflowingTextW
const StyledMainContainer = styled.div`
align-items: flex-start;
align-self: stretch;
border-top: ${({ theme }) =>
useIsMobile() ? `1px solid ${theme.border.color.medium}` : 'none'};
display: flex;
flex: 1 0 0;
flex-direction: column;
justify-content: center;
`;
const StyledTimelineContainer = styled.div`
align-items: center;
align-self: stretch;
display: flex;
flex: 1 0 0;
flex-direction: column;
gap: 4px;
justify-content: flex-start;
overflow-y: auto;
padding: 12px 16px 12px 16px;
`;
@ -175,9 +181,13 @@ const StyledTooltip = styled(Tooltip)`
const StyledTopActionBar = styled.div`
align-items: flex-start;
align-self: stretch;
backdrop-filter: blur(5px);
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
border-top-right-radius: 8px;
backdrop-filter: ${() => (useIsMobile() ? 'none' : `blur(5px)`)};
border-bottom: ${({ theme }) =>
useIsMobile() ? 'none' : `1px solid ${theme.border.color.medium}`};
border-top-right-radius: ${() => (useIsMobile() ? 'none' : `8px`)};
display: flex;
flex-direction: column;
left: 0px;

View File

@ -1,6 +1,8 @@
import styled from '@emotion/styled';
import { Command } from 'cmdk';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
export const StyledDialog = styled(Command.Dialog)`
background: ${({ theme }) => theme.background.primary};
border-radius: ${({ theme }) => theme.border.radius.md};
@ -13,8 +15,9 @@ export const StyledDialog = styled(Command.Dialog)`
padding: ${({ theme }) => theme.spacing(1)};
position: fixed;
top: 30%;
transform: translateX(-50%);
width: 100%;
transform: ${() =>
useIsMobile() ? 'translateX(-49.5%)' : 'translateX(-50%)'};
width: ${() => (useIsMobile() ? 'calc(100% - 40px)' : '100%')};
z-index: 1000;
`;

View File

@ -12,7 +12,7 @@ import {
} from '@/ui/icon/index';
import NavItem from '@/ui/navbar/components/NavItem';
import NavTitle from '@/ui/navbar/components/NavTitle';
import SubNavbar from '@/ui/navbar/components/SubNavbar';
import SubMenuNavbar from '@/ui/navbar/components/SubMenuNavbar';
export function SettingsNavbar() {
const theme = useTheme();
@ -24,62 +24,60 @@ export function SettingsNavbar() {
}, [signOut]);
return (
<SubNavbar backButtonTitle="Settings">
<>
<NavTitle label="User" />
<NavItem
label="Profile"
to="/settings/profile"
icon={<IconUser size={theme.icon.size.md} />}
active={
!!useMatch({
path: useResolvedPath('/settings/profile').pathname,
end: true,
})
}
/>
<NavItem
label="Experience"
to="/settings/profile/experience"
icon={<IconColorSwatch size={theme.icon.size.md} />}
active={
!!useMatch({
path: useResolvedPath('/settings/profile/experience').pathname,
end: true,
})
}
/>
<NavTitle label="Workspace" />
<NavItem
label="Members"
to="/settings/workspace-members"
icon={<IconUsers size={theme.icon.size.md} />}
active={
!!useMatch({
path: useResolvedPath('/settings/workspace-members').pathname,
end: true,
})
}
/>
<NavItem
label="General"
to="/settings/workspace"
icon={<IconSettings size={theme.icon.size.md} />}
active={
!!useMatch({
path: useResolvedPath('/settings/workspace').pathname,
end: true,
})
}
/>
<NavTitle label="Other" />
<NavItem
label="Logout"
onClick={handleLogout}
icon={<IconLogout size={theme.icon.size.md} />}
danger={true}
/>
</>
</SubNavbar>
<SubMenuNavbar backButtonTitle="Settings">
<NavTitle label="User" />
<NavItem
label="Profile"
to="/settings/profile"
icon={<IconUser size={theme.icon.size.md} />}
active={
!!useMatch({
path: useResolvedPath('/settings/profile').pathname,
end: true,
})
}
/>
<NavItem
label="Experience"
to="/settings/profile/experience"
icon={<IconColorSwatch size={theme.icon.size.md} />}
active={
!!useMatch({
path: useResolvedPath('/settings/profile/experience').pathname,
end: true,
})
}
/>
<NavTitle label="Workspace" />
<NavItem
label="Members"
to="/settings/workspace-members"
icon={<IconUsers size={theme.icon.size.md} />}
active={
!!useMatch({
path: useResolvedPath('/settings/workspace-members').pathname,
end: true,
})
}
/>
<NavItem
label="General"
to="/settings/workspace"
icon={<IconSettings size={theme.icon.size.md} />}
active={
!!useMatch({
path: useResolvedPath('/settings/workspace').pathname,
end: true,
})
}
/>
<NavTitle label="Other" />
<NavItem
label="Logout"
onClick={handleLogout}
icon={<IconLogout size={theme.icon.size.md} />}
danger={true}
/>
</SubMenuNavbar>
);
}

View File

@ -1,7 +1,7 @@
import { ReactElement, useRef } from 'react';
import styled from '@emotion/styled';
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
import { useListenClickOutside } from '@/ui/hooks/useListenClickOutside';
import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys';
import { overlayBackground } from '@/ui/themes/effects';
@ -43,7 +43,7 @@ export function BoardCardEditableFieldEditMode({
}: OwnProps) {
const wrapperRef = useRef(null);
useListenClickOutsideArrayOfRef({
useListenClickOutside({
refs: [wrapperRef],
callback: () => {
onExit();

View File

@ -7,7 +7,7 @@ import { DropdownMenu } from '@/ui/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSelectableItem } from '@/ui/dropdown/components/DropdownMenuSelectableItem';
import DropdownButton from '@/ui/filter-n-sort/components/DropdownButton';
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
import { useListenClickOutside } from '@/ui/hooks/useListenClickOutside';
import { BoardColumnEditTitleMenu } from './BoardColumnEditTitleMenu';
@ -35,7 +35,7 @@ export function BoardColumnMenu({
const [openMenu, setOpenMenu] = useState('actions');
const boardColumnMenuRef = useRef(null);
useListenClickOutsideArrayOfRef({
useListenClickOutside({
refs: [boardColumnMenuRef],
callback: onClose,
});

View File

@ -1,4 +1,4 @@
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
import { useListenClickOutside } from '@/ui/hooks/useListenClickOutside';
import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys';
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
@ -12,7 +12,7 @@ export function useRegisterCloseFieldHandlers(
) {
const { closeEditableField, isFieldInEditMode } = useEditableField();
useListenClickOutsideArrayOfRef({
useListenClickOutside({
refs: [wrapperRef],
callback: () => {
if (isFieldInEditMode) {

View File

@ -2,7 +2,7 @@ import { useRef } from 'react';
import styled from '@emotion/styled';
import { DropdownMenu } from '../../dropdown/components/DropdownMenu';
import { useListenClickOutsideArrayOfRef } from '../../hooks/useListenClickOutsideArrayOfRef';
import { useListenClickOutside } from '../../hooks/useListenClickOutside';
export const StyledDropdownMenuContainer = styled.ul`
position: absolute;
@ -19,7 +19,7 @@ export function DropdownMenuContainer({
}) {
const dropdownRef = useRef(null);
useListenClickOutsideArrayOfRef({
useListenClickOutside({
refs: [dropdownRef],
callback: () => {
onClose?.();

View File

@ -1,14 +1,14 @@
import { useRef } from 'react';
import { fireEvent, render } from '@testing-library/react';
import { useListenClickOutsideArrayOfRef } from '../useListenClickOutsideArrayOfRef';
import { useListenClickOutside } from '../useListenClickOutside';
const onOutsideClick = jest.fn();
function TestComponentDomMode() {
const buttonRef = useRef(null);
const buttonRef2 = useRef(null);
useListenClickOutsideArrayOfRef({
useListenClickOutside({
refs: [buttonRef, buttonRef2],
callback: onOutsideClick,
});
@ -22,7 +22,7 @@ function TestComponentDomMode() {
);
}
test('useListenClickOutsideArrayOfRef hook works in dom mode', async () => {
test('useListenClickOutside hook works in dom mode', async () => {
const { getByText } = render(<TestComponentDomMode />);
const inside = getByText('Inside');
const inside2 = getByText('Inside 2');

View File

@ -0,0 +1,7 @@
import { useMediaQuery } from 'react-responsive';
import { MOBILE_VIEWPORT } from '@/ui/themes/themes';
export function useIsMobile() {
return useMediaQuery({ query: `(max-width: ${MOBILE_VIEWPORT}px)` });
}

View File

@ -5,7 +5,7 @@ export enum ClickOutsideMode {
dom = 'dom',
}
export function useListenClickOutsideArrayOfRef<T extends Element>({
export function useListenClickOutside<T extends Element>({
refs,
callback,
mode = ClickOutsideMode.dom,
@ -37,9 +37,13 @@ export function useListenClickOutsideArrayOfRef<T extends Element>({
const { x, y, width, height } = ref.current.getBoundingClientRect();
const clientX =
'clientX' in event ? event.clientX : event.touches[0].clientX;
'clientX' in event
? event.clientX
: event.changedTouches[0].clientX;
const clientY =
'clientY' in event ? event.clientY : event.touches[0].clientY;
'clientY' in event
? event.clientY
: event.changedTouches[0].clientY;
if (
clientX < x ||

View File

@ -1,5 +1,3 @@
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import styled from '@emotion/styled';
import { AnimatePresence, LayoutGroup } from 'framer-motion';
import { useRecoilValue } from 'recoil';
@ -11,10 +9,9 @@ import { CommandMenu } from '@/command-menu/components/CommandMenu';
import { NavbarAnimatedContainer } from '@/ui/navbar/components/NavbarAnimatedContainer';
import { MOBILE_VIEWPORT } from '@/ui/themes/themes';
import { AppNavbar } from '~/AppNavbar';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { CompaniesMockMode } from '~/pages/companies/CompaniesMockMode';
import { AppPath } from '../../../types/AppPath';
import { useAutoNavigateOnboarding } from '../hooks/useAutoNavigateOnboarding';
import { isNavbarOpenedState } from '../states/isNavbarOpenedState';
const StyledLayout = styled.div`
@ -46,72 +43,30 @@ type OwnProps = {
};
export function DefaultLayout({ children }: OwnProps) {
const navigate = useNavigate();
const isMatchingLocation = useIsMatchingLocation();
useAutoNavigateOnboarding();
const onboardingStatus = useOnboardingStatus();
useEffect(() => {
const isMachinOngoingUserCreationRoute =
isMatchingLocation(AppPath.SignUp) ||
isMatchingLocation(AppPath.SignIn) ||
isMatchingLocation(AppPath.Invite) ||
isMatchingLocation(AppPath.Verify);
const isMatchingOnboardingRoute =
isMatchingLocation(AppPath.SignUp) ||
isMatchingLocation(AppPath.SignIn) ||
isMatchingLocation(AppPath.Invite) ||
isMatchingLocation(AppPath.Verify) ||
isMatchingLocation(AppPath.CreateWorkspace) ||
isMatchingLocation(AppPath.CreateProfile);
if (
onboardingStatus === OnboardingStatus.OngoingUserCreation &&
!isMachinOngoingUserCreationRoute
) {
navigate(AppPath.SignIn);
} else if (
onboardingStatus === OnboardingStatus.OngoingWorkspaceCreation &&
!isMatchingLocation(AppPath.CreateWorkspace)
) {
navigate(AppPath.CreateWorkspace);
} else if (
onboardingStatus === OnboardingStatus.OngoingProfileCreation &&
!isMatchingLocation(AppPath.CreateProfile)
) {
navigate(AppPath.CreateProfile);
} else if (
onboardingStatus === OnboardingStatus.Completed &&
isMatchingOnboardingRoute
) {
navigate('/');
}
}, [onboardingStatus, navigate, isMatchingLocation]);
return (
<StyledLayout>
<>
<CommandMenu />
<NavbarAnimatedContainer>
<AppNavbar />
</NavbarAnimatedContainer>
<MainContainer>
{onboardingStatus &&
onboardingStatus !== OnboardingStatus.Completed ? (
<>
<CompaniesMockMode />
<AnimatePresence mode="wait">
<LayoutGroup>
<AuthModal>{children}</AuthModal>
</LayoutGroup>
</AnimatePresence>
</>
) : (
<>{children}</>
)}
</MainContainer>
</>
<CommandMenu />
<NavbarAnimatedContainer>
<AppNavbar />
</NavbarAnimatedContainer>
<MainContainer>
{onboardingStatus && onboardingStatus !== OnboardingStatus.Completed ? (
<>
<CompaniesMockMode />
<AnimatePresence mode="wait">
<LayoutGroup>
<AuthModal>{children}</AuthModal>
</LayoutGroup>
</AnimatePresence>
</>
) : (
<>{children}</>
)}
</MainContainer>
</StyledLayout>
);
}

View File

@ -28,6 +28,7 @@ type LeftContainerProps = {
const StyledLeftContainer = styled.div<LeftContainerProps>`
display: flex;
flex-direction: column;
position: relative;
width: 100%;
`;

View File

@ -0,0 +1,11 @@
import styled from '@emotion/styled';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
export const ShowPageContainer = styled.div`
display: flex;
flex-direction: ${() => (useIsMobile() ? 'column' : 'row')};
gap: ${({ theme }) => (useIsMobile() ? theme.spacing(3) : '0')};
height: 100%;
width: ${() => (useIsMobile() ? `calc(100% - 2px);` : '100%')};
`;

View File

@ -12,7 +12,7 @@ const StyledContainer = styled.div`
width: 100%;
`;
export function NoTopBarContainer({ children }: OwnProps) {
export function SubMenuTopBarContainer({ children }: OwnProps) {
return (
<StyledContainer>
<RightDrawerContainer topMargin={16}>{children}</RightDrawerContainer>

View File

@ -0,0 +1,52 @@
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useIsMatchingLocation } from '../../../../hooks/useIsMatchingLocation';
import { useOnboardingStatus } from '../../../auth/hooks/useOnboardingStatus';
import { OnboardingStatus } from '../../../auth/utils/getOnboardingStatus';
import { AppPath } from '../../../types/AppPath';
export function useAutoNavigateOnboarding() {
const navigate = useNavigate();
const isMatchingLocation = useIsMatchingLocation();
const onboardingStatus = useOnboardingStatus();
useEffect(() => {
const isMachinOngoingUserCreationRoute =
isMatchingLocation(AppPath.SignUp) ||
isMatchingLocation(AppPath.SignIn) ||
isMatchingLocation(AppPath.Invite) ||
isMatchingLocation(AppPath.Verify);
const isMatchingOnboardingRoute =
isMatchingLocation(AppPath.SignUp) ||
isMatchingLocation(AppPath.SignIn) ||
isMatchingLocation(AppPath.Invite) ||
isMatchingLocation(AppPath.Verify) ||
isMatchingLocation(AppPath.CreateWorkspace) ||
isMatchingLocation(AppPath.CreateProfile);
if (
onboardingStatus === OnboardingStatus.OngoingUserCreation &&
!isMachinOngoingUserCreationRoute
) {
navigate(AppPath.SignIn);
} else if (
onboardingStatus === OnboardingStatus.OngoingWorkspaceCreation &&
!isMatchingLocation(AppPath.CreateWorkspace)
) {
navigate(AppPath.CreateWorkspace);
} else if (
onboardingStatus === OnboardingStatus.OngoingProfileCreation &&
!isMatchingLocation(AppPath.CreateProfile)
) {
navigate(AppPath.CreateProfile);
} else if (
onboardingStatus === OnboardingStatus.Completed &&
isMatchingOnboardingRoute
) {
navigate('/');
}
}, [onboardingStatus, navigate, isMatchingLocation]);
}

View File

@ -1,6 +1,6 @@
import { useLocation } from 'react-router-dom';
export function useIsSubNavbarDisplayed() {
export function useIsSubMenuNavbarDisplayed() {
const currentPath = useLocation().pathname;
return currentPath.match(/\/settings\//g) !== null;
}

View File

@ -1,13 +1,23 @@
import styled from '@emotion/styled';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
export const ShowPageLeftContainer = styled.div`
background: ${({ theme }) => theme.background.secondary};
border-bottom-left-radius: 8px;
border-right: 1px solid ${({ theme }) => theme.border.color.medium};
border-right: 1px solid
${({ theme }) => {
const isMobile = useIsMobile();
return !isMobile ? theme.border.color.medium : 0;
}};
border-top-left-radius: 8px;
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(3)};
padding: 0px ${({ theme }) => theme.spacing(3)};
width: 320px;
width: ${({ theme }) => {
const isMobile = useIsMobile();
return isMobile ? `calc(100% - ${theme.spacing(6)})` : '320px';
}};
`;

View File

@ -1,12 +1,16 @@
import { ReactNode, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { IconButton } from '@/ui/button/components/IconButton';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import { IconChevronLeft, IconPlus } from '@/ui/icon/index';
import NavCollapseButton from '@/ui/navbar/components/NavCollapseButton';
import { navbarIconSize } from '../../../navbar/constants';
import { OverflowingTextWithTooltip } from '../../../tooltip/OverflowingTextWithTooltip';
import { isNavbarOpenedState } from '../../states/isNavbarOpenedState';
export const TOP_BAR_MIN_HEIGHT = 40;
@ -20,6 +24,7 @@ const TopBarContainer = styled.div`
justify-content: space-between;
min-height: ${TOP_BAR_MIN_HEIGHT}px;
padding: ${({ theme }) => theme.spacing(2)};
padding-left: 0;
padding-right: ${({ theme }) => theme.spacing(3)};
`;
@ -37,10 +42,22 @@ const TitleContainer = styled.div`
max-width: 50%;
`;
const TopBarButtonContainer = styled.div`
margin-right: ${({ theme }) => theme.spacing(1)};
`;
const BackIconButton = styled(IconButton)`
margin-right: ${({ theme }) => theme.spacing(1)};
`;
const StyledTopBarIconTitleContainer = styled.div`
align-items: center;
display: flex;
flex-direction: row;
padding-left: ${({ theme }) => theme.spacing(2)};
width: 100%;
`;
type OwnProps = {
title: string;
hasBackButton?: boolean;
@ -57,21 +74,38 @@ export function TopBar({
const navigate = useNavigate();
const navigateBack = useCallback(() => navigate(-1), [navigate]);
const isMobile = useIsMobile();
const isNavbarOpened = useRecoilValue(isNavbarOpenedState);
const showNavCollapseButton = isMobile || !isNavbarOpened;
const iconSize = useIsMobile()
? navbarIconSize.mobile
: navbarIconSize.desktop;
return (
<>
<TopBarContainer>
<StyledLeftContainer>
<NavCollapseButton hideIfOpen={true} />
{hasBackButton && (
<BackIconButton
icon={<IconChevronLeft size={16} />}
onClick={navigateBack}
/>
{showNavCollapseButton && (
<TopBarButtonContainer>
<NavCollapseButton direction="right" />
</TopBarButtonContainer>
)}
{icon}
<TitleContainer data-testid="top-bar-title">
<OverflowingTextWithTooltip text={title} />
</TitleContainer>
{hasBackButton && (
<TopBarButtonContainer>
<BackIconButton
icon={<IconChevronLeft size={iconSize} />}
onClick={navigateBack}
/>
</TopBarButtonContainer>
)}
<StyledTopBarIconTitleContainer>
{icon}
<TitleContainer data-testid="top-bar-title">
<OverflowingTextWithTooltip text={title} />
</TitleContainer>
</StyledTopBarIconTitleContainer>
</StyledLeftContainer>
{onAddButtonClick && (
<IconButton

View File

@ -1,7 +1,5 @@
import styled from '@emotion/styled';
import NavCollapseButton from '@/ui/navbar/components/NavCollapseButton';
const TitleAndCollapseContainer = styled.div`
align-items: center;
display: flex;
@ -22,7 +20,6 @@ type OwnProps = {
export function TopTitle({ title }: OwnProps) {
return (
<TitleAndCollapseContainer>
<NavCollapseButton hideIfOpen={true} hideOnDesktop={true} />
<TitleContainer data-testid="top-bar-title">{title}</TitleContainer>
</TitleAndCollapseContainer>
);

View File

@ -4,11 +4,11 @@ import NavItemsContainer from './NavItemsContainer';
import NavWorkspaceButton from './NavWorkspaceButton';
type OwnProps = {
children: JSX.Element;
children: React.ReactNode;
};
const StyledContainer = styled.div`
width: ${({ theme }) => theme.leftNavBarWidth};
width: 100%;
`;
export default function MainNavbar({ children }: OwnProps) {

View File

@ -5,8 +5,6 @@ import { useRecoilState } from 'recoil';
import { IconChevronLeft } from '@/ui/icon/index';
import { isNavbarSwitchingSizeState } from '@/ui/layout/states/isNavbarSwitchingSizeState';
import NavCollapseButton from './NavCollapseButton';
type OwnProps = {
title: string;
};
@ -50,7 +48,6 @@ export default function NavBackButton({ title }: OwnProps) {
<IconChevronLeft />
<span>{title}</span>
</IconAndButtonContainer>
<NavCollapseButton hideOnDesktop={true} />
</StyledContainer>
</>
);

View File

@ -1,14 +1,16 @@
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import {
IconLayoutSidebarLeftCollapse,
IconLayoutSidebarRightCollapse,
} from '@/ui/icon';
import { isNavbarOpenedState } from '@/ui/layout/states/isNavbarOpenedState';
import { MOBILE_VIEWPORT } from '@/ui/themes/themes';
const CollapseButton = styled.button<{ hideOnDesktop: boolean | undefined }>`
import { navbarIconSize } from '../constants';
const CollapseButton = styled.button`
align-items: center;
background: inherit;
border: 0;
@ -25,46 +27,33 @@ const CollapseButton = styled.button<{ hideOnDesktop: boolean | undefined }>`
justify-content: center;
padding: 0;
user-select: none;
width: 32px;
${(props) =>
props.hideOnDesktop &&
`@media (min-width: ${MOBILE_VIEWPORT}px) {
display:none;
}
`}
`;
type CollapseButtonProps = {
hideIfOpen?: boolean;
hideIfClosed?: boolean;
hideOnDesktop?: boolean;
direction?: 'left' | 'right';
};
export default function NavCollapseButton({
hideIfOpen,
hideOnDesktop,
direction = 'left',
}: CollapseButtonProps) {
const [isNavOpen, setIsNavOpen] = useRecoilState(isNavbarOpenedState);
const iconSize = useIsMobile()
? navbarIconSize.mobile
: navbarIconSize.desktop;
return (
<>
{isNavOpen && !hideIfOpen && (
<CollapseButton
onClick={() => setIsNavOpen(!isNavOpen)}
hideOnDesktop={hideOnDesktop}
>
<IconLayoutSidebarLeftCollapse size={16} />
{direction === 'left' ? (
<CollapseButton onClick={() => setIsNavOpen(!isNavOpen)}>
<IconLayoutSidebarLeftCollapse size={iconSize} />
</CollapseButton>
)}
{!isNavOpen && (
<CollapseButton
onClick={() => setIsNavOpen(!isNavOpen)}
hideOnDesktop={hideOnDesktop}
>
<IconLayoutSidebarRightCollapse size={16} />
) : (
<CollapseButton onClick={() => setIsNavOpen(!isNavOpen)}>
<IconLayoutSidebarRightCollapse size={iconSize} />
</CollapseButton>
)}
</>

View File

@ -1,9 +1,13 @@
import { ReactNode } from 'react';
import { useNavigate } from 'react-router-dom';
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import { MOBILE_VIEWPORT } from '@/ui/themes/themes';
import { isNavbarOpenedState } from '../../layout/states/isNavbarOpenedState';
type OwnProps = {
label: string;
to?: string;
@ -79,21 +83,25 @@ const StyledSoonPill = styled.div`
function NavItem({ label, icon, to, onClick, active, danger, soon }: OwnProps) {
const navigate = useNavigate();
const [, setIsNavbarOpened] = useRecoilState(isNavbarOpenedState);
const isMobile = useIsMobile();
function handleItemClick() {
if (isMobile) {
setIsNavbarOpened(false);
}
const onItemClick = () => {
if (onClick) {
onClick();
return;
}
if (to) {
} else if (to) {
navigate(to);
return;
}
};
}
return (
<StyledItem
onClick={onItemClick}
onClick={handleItemClick}
active={active}
aria-selected={active}
danger={danger}

View File

@ -65,7 +65,7 @@ function NavWorkspaceButton() {
></StyledLogo>
<StyledName>{currentWorkspace?.displayName ?? 'Twenty'}</StyledName>
</LogoAndNameContainer>
<NavCollapseButton />
<NavCollapseButton direction="left" />
</StyledContainer>
);
}

View File

@ -3,10 +3,12 @@ import styled from '@emotion/styled';
import { motion } from 'framer-motion';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useIsSubNavbarDisplayed } from '@/ui/layout/hooks/useIsSubNavbarDisplayed';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import { useIsSubMenuNavbarDisplayed } from '@/ui/layout/hooks/useIsSubMenuNavbarDisplayed';
import { isNavbarOpenedState } from '@/ui/layout/states/isNavbarOpenedState';
import { isNavbarSwitchingSizeState } from '@/ui/layout/states/isNavbarSwitchingSizeState';
import { MOBILE_VIEWPORT } from '@/ui/themes/themes';
import { leftNavbarWidth, leftSubMenuNavbarWidth } from '../constants';
const StyledNavbarContainer = styled(motion.div)`
align-items: end;
@ -15,35 +17,37 @@ const StyledNavbarContainer = styled(motion.div)`
flex-shrink: 0;
overflow: hidden;
padding: ${({ theme }) => theme.spacing(2)};
@media (max-width: ${MOBILE_VIEWPORT}px) {
width: ${(props) =>
useRecoilValue(isNavbarOpenedState)
? `calc(100% - ` + props.theme.spacing(4) + `)`
: '0'};
}
`;
type NavbarProps = {
children: React.ReactNode;
layout?: string;
};
export function NavbarAnimatedContainer({ children, layout }: NavbarProps) {
export function NavbarAnimatedContainer({ children }: NavbarProps) {
const isMenuOpened = useRecoilValue(isNavbarOpenedState);
const [, setIsNavbarSwitchingSize] = useRecoilState(
isNavbarSwitchingSizeState,
);
const isSubNavbarDisplayed = useIsSubNavbarDisplayed();
const isInSubMenu = useIsSubMenuNavbarDisplayed();
const theme = useTheme();
const isMobile = useIsMobile();
const leftBarWidth = isInSubMenu
? isMobile
? leftSubMenuNavbarWidth.mobile
: leftSubMenuNavbarWidth.desktop
: isMobile
? leftNavbarWidth.mobile
: leftNavbarWidth.desktop;
return (
<StyledNavbarContainer
onAnimationComplete={() => {
setIsNavbarSwitchingSize(false);
}}
animate={{
width: isMenuOpened ? (isSubNavbarDisplayed ? '520px' : '220px') : '0',
width: isMenuOpened ? leftBarWidth : '0',
opacity: isMenuOpened ? 1 : 0,
}}
transition={{

View File

@ -1,21 +1,25 @@
import styled from '@emotion/styled';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import { leftNavbarWidth } from '../constants';
import NavBackButton from './NavBackButton';
import NavItemsContainer from './NavItemsContainer';
type OwnProps = {
children: JSX.Element;
children: React.ReactNode;
backButtonTitle: string;
};
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
padding-top: ${({ theme }) => theme.spacing(6)};
width: 220px;
padding-top: ${({ theme }) => theme.spacing(2)};
width: ${({ theme }) => (useIsMobile() ? '100%' : leftNavbarWidth.desktop)};
`;
export default function SubNavbar({ children, backButtonTitle }: OwnProps) {
export default function SubMenuNavbar({ children, backButtonTitle }: OwnProps) {
return (
<StyledContainer>
<NavBackButton title={backButtonTitle} />

View File

@ -0,0 +1,14 @@
export const leftNavbarWidth = {
mobile: 'calc(100% - 16px)',
desktop: '220px',
};
export const leftSubMenuNavbarWidth = {
mobile: 'calc(100% - 16px)',
desktop: '520px',
};
export const navbarIconSize = {
mobile: 18,
desktop: 16,
};

View File

@ -6,7 +6,7 @@ import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem';
import { DropdownMenuItemsContainer } from '@/ui/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearch } from '@/ui/dropdown/components/DropdownMenuSearch';
import { DropdownMenuSeparator } from '@/ui/dropdown/components/DropdownMenuSeparator';
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
import { useListenClickOutside } from '@/ui/hooks/useListenClickOutside';
import { IconPlus } from '@/ui/icon';
import { isDefined } from '~/utils/isDefined';
@ -46,7 +46,7 @@ export function SingleEntitySelect<
const showCreateButton = isDefined(onCreate) && searchFilter !== '';
useListenClickOutsideArrayOfRef({
useListenClickOutside({
refs: [containerRef],
callback: () => {
onCancel?.();

View File

@ -5,13 +5,15 @@ import { motion } from 'framer-motion';
import { useRecoilState } from 'recoil';
import { Key } from 'ts-key-enum';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import {
ClickOutsideMode,
useListenClickOutsideArrayOfRef,
} from '@/ui/hooks/useListenClickOutsideArrayOfRef';
useListenClickOutside,
} from '@/ui/hooks/useListenClickOutside';
import { isDefined } from '~/utils/isDefined';
import { useScopedHotkeys } from '../../hotkey/hooks/useScopedHotkeys';
import { leftNavbarWidth } from '../../navbar/constants';
import { useRightDrawer } from '../hooks/useRightDrawer';
import { isRightDrawerExpandedState } from '../states/isRightDrawerExpandedState';
import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState';
@ -51,7 +53,7 @@ export function RightDrawer() {
const rightDrawerRef = useRef(null);
useListenClickOutsideArrayOfRef({
useListenClickOutside({
refs: [rightDrawerRef],
callback: () => closeRightDrawer(),
mode: ClickOutsideMode.absolute,
@ -66,16 +68,22 @@ export function RightDrawer() {
[setIsRightDrawerOpen],
);
const isMobile = useIsMobile();
const rightDrawerWidthExpanded = `calc(100% - ${
theme.leftNavBarWidth
leftNavbarWidth.desktop
} - ${theme.spacing(2)})`;
const rightDrawerWidth = isRightDrawerOpen
? isRightDrawerExpanded
? isMobile
? '100%'
: isRightDrawerExpanded
? rightDrawerWidthExpanded
: theme.rightDrawerWidth
: '0';
console.log(rightDrawerWidth);
if (!isDefined(rightDrawerPage)) {
return <></>;
}

View File

@ -1,5 +1,7 @@
import styled from '@emotion/styled';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import { RightDrawerTopBarCloseButton } from './RightDrawerTopBarCloseButton';
import { RightDrawerTopBarExpandButton } from './RightDrawerTopBarExpandButton';
@ -20,10 +22,12 @@ const StyledRightDrawerTopBar = styled.div`
`;
export function RightDrawerTopBar() {
const isMobile = useIsMobile();
return (
<StyledRightDrawerTopBar>
<RightDrawerTopBarCloseButton />
<RightDrawerTopBarExpandButton />
{!isMobile && <RightDrawerTopBarExpandButton />}
</StyledRightDrawerTopBar>
);
}

View File

@ -3,7 +3,7 @@ import styled from '@emotion/styled';
import { TableColumn } from '@/people/table/components/peopleColumns';
import { SelectedSortType, SortType } from '@/ui/filter-n-sort/types/interface';
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
import { useListenClickOutside } from '@/ui/hooks/useListenClickOutside';
import { useLeaveTableFocus } from '../hooks/useLeaveTableFocus';
import { TableHeader } from '../table-header/components/TableHeader';
@ -90,7 +90,7 @@ export function EntityTable<SortField>({
const leaveTableFocus = useLeaveTableFocus();
useListenClickOutsideArrayOfRef({
useListenClickOutside({
refs: [tableBodyRef],
callback: () => {
leaveTableFocus();

View File

@ -1,4 +1,4 @@
import { useListenClickOutsideArrayOfRef } from '@/ui/hooks/useListenClickOutsideArrayOfRef';
import { useListenClickOutside } from '@/ui/hooks/useListenClickOutside';
import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys';
import { useMoveSoftFocus } from '../../hooks/useMoveSoftFocus';
@ -14,7 +14,7 @@ export function useRegisterCloseCellHandlers(
) {
const { closeEditableCell } = useEditableCell();
const { isCurrentCellInEditMode } = useCurrentCellEditMode();
useListenClickOutsideArrayOfRef({
useListenClickOutside({
refs: [wrapperRef],
callback: (event) => {
if (isCurrentCellInEditMode) {

View File

@ -37,7 +37,6 @@ const common = {
checkboxColumnWidth: '32px',
},
rightDrawerWidth: '500px',
leftNavBarWidth: '220px',
clickableElementBackgroundTransition: 'background 0.1s ease',
lastLayerZIndex: 2147483647,
};

View File

@ -18,6 +18,7 @@ import { CommentableType } from '~/generated/graphql';
import { getLogoUrlFromDomainName } from '~/utils';
import { CompanyNameEditableField } from '../../modules/companies/editable-field/components/CompanyNameEditableField';
import { ShowPageContainer } from '../../modules/ui/layout/components/ShowPageContainer';
export function CompanyShow() {
const companyId = useParams().companyId ?? '';
@ -35,29 +36,31 @@ export function CompanyShow() {
hasBackButton
icon={<IconBuildingSkyscraper size={theme.icon.size.md} />}
>
<ShowPageLeftContainer>
<ShowPageSummaryCard
id={company?.id}
logoOrAvatar={getLogoUrlFromDomainName(company?.domainName ?? '')}
title={company?.name ?? 'No name'}
date={company?.createdAt ?? ''}
renderTitleEditComponent={() => (
<CompanyNameEditableField company={company} />
)}
/>
<PropertyBox extraPadding={true}>
<CompanyDomainNameEditableField company={company} />
<CompanyAccountOwnerEditableField company={company} />
<CompanyEmployeesEditableField company={company} />
<CompanyAddressEditableField company={company} />
<CompanyCreatedAtEditableField company={company} />
</PropertyBox>
</ShowPageLeftContainer>
<ShowPageRightContainer>
<Timeline
entity={{ id: company?.id ?? '', type: CommentableType.Company }}
/>
</ShowPageRightContainer>
<ShowPageContainer>
<ShowPageLeftContainer>
<ShowPageSummaryCard
id={company?.id}
logoOrAvatar={getLogoUrlFromDomainName(company?.domainName ?? '')}
title={company?.name ?? 'No name'}
date={company?.createdAt ?? ''}
renderTitleEditComponent={() => (
<CompanyNameEditableField company={company} />
)}
/>
<PropertyBox extraPadding={true}>
<CompanyDomainNameEditableField company={company} />
<CompanyAccountOwnerEditableField company={company} />
<CompanyEmployeesEditableField company={company} />
<CompanyAddressEditableField company={company} />
<CompanyCreatedAtEditableField company={company} />
</PropertyBox>
</ShowPageLeftContainer>
<ShowPageRightContainer>
<Timeline
entity={{ id: company?.id ?? '', type: CommentableType.Company }}
/>
</ShowPageRightContainer>
</ShowPageContainer>
</WithTopBarContainer>
);
}

View File

@ -12,6 +12,7 @@ import { ShowPageSummaryCard } from '@/ui/layout/show-page/components/ShowPageSu
import { CommentableType } from '~/generated/graphql';
import { PeopleFullNameEditableField } from '../../modules/people/editable-field/components/PeopleFullNameEditableField';
import { ShowPageContainer } from '../../modules/ui/layout/components/ShowPageContainer';
export function PersonShow() {
const personId = useParams().personId ?? '';
@ -27,7 +28,7 @@ export function PersonShow() {
icon={<IconUser size={theme.icon.size.md} />}
hasBackButton
>
<>
<ShowPageContainer>
<ShowPageLeftContainer>
<ShowPageSummaryCard
id={person?.id}
@ -44,7 +45,7 @@ export function PersonShow() {
entity={{ id: person?.id ?? '', type: CommentableType.Person }}
/>
</ShowPageRightContainer>
</>
</ShowPageContainer>
</WithTopBarContainer>
);
}

View File

@ -1,7 +1,7 @@
import styled from '@emotion/styled';
import { ColorSchemePicker } from '@/ui/color-scheme/components/ColorSchemePicker';
import { NoTopBarContainer } from '@/ui/layout/components/NoTopBarContainer';
import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer';
import { useColorScheme } from '@/ui/themes/hooks/useColorScheme';
import { MainSectionTitle } from '@/ui/title/components/MainSectionTitle';
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle';
@ -27,7 +27,7 @@ export function SettingsExperience() {
const { colorScheme, setColorScheme } = useColorScheme();
return (
<NoTopBarContainer>
<SubMenuTopBarContainer>
<div>
<StyledContainer>
<MainSectionTitle>Experience</MainSectionTitle>
@ -37,6 +37,6 @@ export function SettingsExperience() {
</StyledSectionContainer>
</StyledContainer>
</div>
</NoTopBarContainer>
</SubMenuTopBarContainer>
);
}

View File

@ -3,7 +3,7 @@ import styled from '@emotion/styled';
import { EmailField } from '@/settings/profile/components/EmailField';
import { NameFields } from '@/settings/profile/components/NameFields';
import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader';
import { NoTopBarContainer } from '@/ui/layout/components/NoTopBarContainer';
import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer';
import { MainSectionTitle } from '@/ui/title/components/MainSectionTitle';
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle';
@ -26,7 +26,7 @@ const StyledSectionContainer = styled.div`
export function SettingsProfile() {
return (
<NoTopBarContainer>
<SubMenuTopBarContainer>
<div>
<StyledContainer>
<MainSectionTitle>Profile</MainSectionTitle>
@ -50,6 +50,6 @@ export function SettingsProfile() {
</StyledSectionContainer>
</StyledContainer>
</div>
</NoTopBarContainer>
</SubMenuTopBarContainer>
);
}

View File

@ -2,7 +2,7 @@ import styled from '@emotion/styled';
import { NameField } from '@/settings/workspace/components/NameField';
import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader';
import { NoTopBarContainer } from '@/ui/layout/components/NoTopBarContainer';
import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer';
import { MainSectionTitle } from '@/ui/title/components/MainSectionTitle';
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle';
@ -24,7 +24,7 @@ const StyledSectionContainer = styled.div`
export function SettingsWorksapce() {
return (
<NoTopBarContainer>
<SubMenuTopBarContainer>
<div>
<StyledContainer>
<MainSectionTitle>General</MainSectionTitle>
@ -41,6 +41,6 @@ export function SettingsWorksapce() {
</StyledSectionContainer>
</StyledContainer>
</div>
</NoTopBarContainer>
</SubMenuTopBarContainer>
);
}

View File

@ -5,7 +5,7 @@ import { useRecoilState } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
import { Button } from '@/ui/button/components/Button';
import { IconTrash } from '@/ui/icon';
import { NoTopBarContainer } from '@/ui/layout/components/NoTopBarContainer';
import { SubMenuTopBarContainer } from '@/ui/layout/components/SubMenuTopBarContainer';
import { MainSectionTitle } from '@/ui/title/components/MainSectionTitle';
import { SubSectionTitle } from '@/ui/title/components/SubSectionTitle';
import { WorkspaceInviteLink } from '@/workspace/components/WorkspaceInviteLink';
@ -75,7 +75,7 @@ export function SettingsWorkspaceMembers() {
};
return (
<NoTopBarContainer>
<SubMenuTopBarContainer>
<StyledContainer>
<MainSectionTitle>Members</MainSectionTitle>
{workspace?.inviteHash && (
@ -112,6 +112,6 @@ export function SettingsWorkspaceMembers() {
/>
))}
</StyledContainer>
</NoTopBarContainer>
</SubMenuTopBarContainer>
);
}

View File

@ -8275,6 +8275,11 @@ css-loader@^6.5.1, css-loader@^6.7.1, css-loader@^6.7.3:
postcss-value-parser "^4.2.0"
semver "^7.3.8"
css-mediaquery@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/css-mediaquery/-/css-mediaquery-0.1.2.tgz#6a2c37344928618631c54bd33cedd301da18bea0"
integrity sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==
css-minimizer-webpack-plugin@^3.2.0:
version "3.4.1"
resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz#ab78f781ced9181992fe7b6e4f3422e76429878f"
@ -11054,6 +11059,11 @@ human-signals@^2.1.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
hyphenate-style-name@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d"
integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==
iconv-lite@0.4.24, iconv-lite@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@ -13201,6 +13211,13 @@ markdown-to-jsx@^7.1.8:
resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-7.2.1.tgz#87061fd3176ad926ef3d99493e5c57f6335e0c51"
integrity sha512-9HrdzBAo0+sFz9ZYAGT5fB8ilzTW+q6lPocRxrIesMO+aB40V9MgFfbfMXxlGjf22OpRy+IXlvVaQenicdpgbg==
matchmediaquery@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/matchmediaquery/-/matchmediaquery-0.3.1.tgz#8247edc47e499ebb7c58f62a9ff9ccf5b815c6d7"
integrity sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ==
dependencies:
css-mediaquery "^0.1.2"
mdast-util-definitions@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2"
@ -15306,7 +15323,7 @@ prompts@^2.0.1, prompts@^2.4.0, prompts@^2.4.1, prompts@^2.4.2:
kleur "^3.0.3"
sisteransi "^1.0.5"
prop-types@^15.7.2, prop-types@^15.8.1:
prop-types@^15.6.1, prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@ -15850,6 +15867,16 @@ react-remove-scroll@2.5.4:
use-callback-ref "^1.3.0"
use-sidecar "^1.1.2"
react-responsive@^9.0.2:
version "9.0.2"
resolved "https://registry.yarnpkg.com/react-responsive/-/react-responsive-9.0.2.tgz#34531ca77a61e7a8775714016d21241df7e4205c"
integrity sha512-+4CCab7z8G8glgJoRjAwocsgsv6VA2w7JPxFWHRc7kvz8mec1/K5LutNC2MG28Mn8mu6+bu04XZxHv5gyfT7xQ==
dependencies:
hyphenate-style-name "^1.0.0"
matchmediaquery "^0.3.0"
prop-types "^15.6.1"
shallow-equal "^1.2.1"
react-router-dom@^6.4.4:
version "6.14.1"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.14.1.tgz#0ad7ba7abdf75baa61169d49f096f0494907a36f"
@ -16795,6 +16822,11 @@ shallow-clone@^3.0.0:
dependencies:
kind-of "^6.0.2"
shallow-equal@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.2.1.tgz#4c16abfa56043aa20d050324efa68940b0da79da"
integrity sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"