From fec8223ab86fcc6c4c8b82e4bb96a69db6c4fd55 Mon Sep 17 00:00:00 2001 From: Saba Shavidze Date: Sat, 2 Dec 2023 02:16:34 +0400 Subject: [PATCH] feat: improve mobile display by tab bar and other changes (#2304) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: improve mobile display by tab bar and other changes * fix: remove unused declaration in mobile navigation * fix: update desktop navbar stories title * fix: retrieve old titles for desktop-navbar stories * fix: styles, manage active tabs * fix: styles, manage active tabs * fix: styles, manage active tabs * fix: styles, manage active tabs * fix: styles, manage active tabs * fix: styles, manage active tabs * fix: styles, manage active tabs * fix: styles, manage active tabs * fix: update logic for tab bar menu icons * fix: remove Settings icon for mobile * fix: resolve comments in pl * feat: rework mobile navigation bar * Fix * Fixes --------- Co-authored-by: Thaïs Guigon Co-authored-by: Charles Bochet --- front/src/AppNavbar.tsx | 72 ---------------- .../favorites/components/Favorites.tsx | 4 +- .../components/DesktopNavigationDrawer.tsx | 64 ++++++++++++++ .../components/MobileNavigationBar.tsx | 85 +++++++++++++++++++ .../components/MobileNavigationDrawer.tsx | 21 +++++ .../components/WorkspaceNavItems.tsx | 23 +++++ .../DesktopNavigationDrawer.stories.tsx | 23 +++++ .../MobileNavigationDrawer.stories.tsx | 23 +++++ .../navigation/hooks/useIsSettingsPage.ts | 4 + .../navigation/hooks/useIsTasksPage.ts | 3 + .../components/ObjectMetadataNavItems.tsx | 2 +- .../components/RecordTableContainer.tsx | 14 +-- .../settings/components/SettingsNavbar.tsx | 6 +- ...tingsObjectFieldDisabledActionDropdown.tsx | 16 ++-- .../SettingsObjectDisabledMenuDropDown.tsx | 16 ++-- .../layout/hooks/useIsMenuNavbarDisplayed.ts | 6 ++ .../hooks/useIsSubMenuNavbarDisplayed.ts | 6 -- .../modules/ui/layout/page/DefaultLayout.tsx | 69 ++++++++------- front/src/modules/ui/layout/page/PageBody.tsx | 13 +-- .../modules/ui/layout/page/PageContainer.tsx | 8 +- .../src/modules/ui/layout/page/PageHeader.tsx | 68 +++++++-------- .../ui/layout/page/RightDrawerContainer.tsx | 22 +++-- .../ui/layout/page/SubMenuTopBarContainer.tsx | 2 +- .../right-drawer/components/RightDrawer.tsx | 9 +- .../ui/layout/states/isNavbarOpenedState.ts | 10 --- .../states/isNavbarSwitchingSizeState.ts | 6 -- .../components/NavbarAnimatedContainer.tsx | 63 -------------- .../ui/navigation/navbar/constants/index.ts | 11 --- .../components/NavigationBar.tsx | 33 +++++++ .../components/NavigationBarItem.tsx | 42 +++++++++ .../__stories__/NavigationBar.stories.tsx | 33 +++++++ .../components/MainNavbar.tsx | 2 + .../components/NavBackButton.tsx | 31 +++---- .../components/NavCollapseButton.tsx | 13 +-- .../components/NavItem.tsx | 22 ++--- .../components/NavItemsContainer.tsx | 0 .../components/NavTitle.tsx | 0 .../components/NavWorkspaceButton.tsx | 7 +- .../components/NavbarAnimatedContainer.tsx | 55 ++++++++++++ .../components/SubMenuNavbar.tsx | 23 +++-- .../components/SupportChat.tsx | 0 .../__stories__/MainNavbar.stories.tsx | 2 +- .../__stories__/NavCollapseButton.stories.tsx | 2 +- .../__stories__/NavItem.stories.tsx | 41 +++++---- .../__stories__/SubMenuNavbar.stories.tsx | 2 +- .../navigation-drawer/constants/index.ts | 6 ++ .../states/navigationDrawerState.ts | 6 ++ .../states/navigationMemorizedUrlState.ts | 6 ++ .../components/RecordTableBody.tsx | 13 +-- front/src/utils/measureTotalFrameLoad.ts | 12 --- 50 files changed, 640 insertions(+), 380 deletions(-) delete mode 100644 front/src/AppNavbar.tsx create mode 100644 front/src/modules/navigation/components/DesktopNavigationDrawer.tsx create mode 100644 front/src/modules/navigation/components/MobileNavigationBar.tsx create mode 100644 front/src/modules/navigation/components/MobileNavigationDrawer.tsx create mode 100644 front/src/modules/navigation/components/WorkspaceNavItems.tsx create mode 100644 front/src/modules/navigation/components/__stories__/DesktopNavigationDrawer.stories.tsx create mode 100644 front/src/modules/navigation/components/__stories__/MobileNavigationDrawer.stories.tsx create mode 100644 front/src/modules/navigation/hooks/useIsSettingsPage.ts create mode 100644 front/src/modules/navigation/hooks/useIsTasksPage.ts create mode 100644 front/src/modules/ui/layout/hooks/useIsMenuNavbarDisplayed.ts delete mode 100644 front/src/modules/ui/layout/hooks/useIsSubMenuNavbarDisplayed.ts delete mode 100644 front/src/modules/ui/layout/states/isNavbarOpenedState.ts delete mode 100644 front/src/modules/ui/layout/states/isNavbarSwitchingSizeState.ts delete mode 100644 front/src/modules/ui/navigation/navbar/components/NavbarAnimatedContainer.tsx delete mode 100644 front/src/modules/ui/navigation/navbar/constants/index.ts create mode 100644 front/src/modules/ui/navigation/navigation-bar/components/NavigationBar.tsx create mode 100644 front/src/modules/ui/navigation/navigation-bar/components/NavigationBarItem.tsx create mode 100644 front/src/modules/ui/navigation/navigation-bar/components/__stories__/NavigationBar.stories.tsx rename front/src/modules/ui/navigation/{navbar => navigation-drawer}/components/MainNavbar.tsx (93%) rename front/src/modules/ui/navigation/{navbar => navigation-drawer}/components/NavBackButton.tsx (61%) rename front/src/modules/ui/navigation/{navbar => navigation-drawer}/components/NavCollapseButton.tsx (81%) rename front/src/modules/ui/navigation/{navbar => navigation-drawer}/components/NavItem.tsx (92%) rename front/src/modules/ui/navigation/{navbar => navigation-drawer}/components/NavItemsContainer.tsx (100%) rename front/src/modules/ui/navigation/{navbar => navigation-drawer}/components/NavTitle.tsx (100%) rename front/src/modules/ui/navigation/{navbar => navigation-drawer}/components/NavWorkspaceButton.tsx (93%) create mode 100644 front/src/modules/ui/navigation/navigation-drawer/components/NavbarAnimatedContainer.tsx rename front/src/modules/ui/navigation/{navbar => navigation-drawer}/components/SubMenuNavbar.tsx (79%) rename front/src/modules/ui/navigation/{navbar => navigation-drawer}/components/SupportChat.tsx (100%) rename front/src/modules/ui/navigation/{navbar => navigation-drawer}/components/__stories__/MainNavbar.stories.tsx (96%) rename front/src/modules/ui/navigation/{navbar => navigation-drawer}/components/__stories__/NavCollapseButton.stories.tsx (89%) rename front/src/modules/ui/navigation/{navbar => navigation-drawer}/components/__stories__/NavItem.stories.tsx (76%) rename front/src/modules/ui/navigation/{navbar => navigation-drawer}/components/__stories__/SubMenuNavbar.stories.tsx (95%) create mode 100644 front/src/modules/ui/navigation/navigation-drawer/constants/index.ts create mode 100644 front/src/modules/ui/navigation/states/navigationDrawerState.ts create mode 100644 front/src/modules/ui/navigation/states/navigationMemorizedUrlState.ts delete mode 100644 front/src/utils/measureTotalFrameLoad.ts diff --git a/front/src/AppNavbar.tsx b/front/src/AppNavbar.tsx deleted file mode 100644 index d25e66cb4..000000000 --- a/front/src/AppNavbar.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { useLocation } from 'react-router-dom'; - -import { useCurrentUserTaskCount } from '@/activities/tasks/hooks/useCurrentUserDueTaskCount'; -import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; -import { Favorites } from '@/favorites/components/Favorites'; -import { ObjectMetadataNavItems } from '@/object-metadata/components/ObjectMetadataNavItems'; -import { SettingsNavbar } from '@/settings/components/SettingsNavbar'; -import { - IconBell, - IconCheckbox, - IconSearch, - IconSettings, - IconTargetArrow, -} from '@/ui/display/icon/index'; -import { useIsSubMenuNavbarDisplayed } from '@/ui/layout/hooks/useIsSubMenuNavbarDisplayed'; -import MainNavbar from '@/ui/navigation/navbar/components/MainNavbar'; -import NavItem from '@/ui/navigation/navbar/components/NavItem'; -import NavTitle from '@/ui/navigation/navbar/components/NavTitle'; - -export const AppNavbar = () => { - const currentPath = useLocation().pathname; - const { toggleCommandMenu } = useCommandMenu(); - - const isInSubMenu = useIsSubMenuNavbarDisplayed(); - const { currentUserDueTaskCount } = useCurrentUserTaskCount(); - - return ( - <> - {!isInSubMenu ? ( - - { - toggleCommandMenu(); - }} - keyboard={['⌘', 'K']} - /> - - - - - - - - - ) : ( - - )} - - ); -}; diff --git a/front/src/modules/favorites/components/Favorites.tsx b/front/src/modules/favorites/components/Favorites.tsx index 0e892990e..1b5c1b32d 100644 --- a/front/src/modules/favorites/components/Favorites.tsx +++ b/front/src/modules/favorites/components/Favorites.tsx @@ -2,8 +2,8 @@ import styled from '@emotion/styled'; import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem'; import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList'; -import NavItem from '@/ui/navigation/navbar/components/NavItem'; -import NavTitle from '@/ui/navigation/navbar/components/NavTitle'; +import NavItem from '@/ui/navigation/navigation-drawer/components/NavItem'; +import NavTitle from '@/ui/navigation/navigation-drawer/components/NavTitle'; import { Avatar } from '@/users/components/Avatar'; import { useFavorites } from '../hooks/useFavorites'; diff --git a/front/src/modules/navigation/components/DesktopNavigationDrawer.tsx b/front/src/modules/navigation/components/DesktopNavigationDrawer.tsx new file mode 100644 index 000000000..1d4339d56 --- /dev/null +++ b/front/src/modules/navigation/components/DesktopNavigationDrawer.tsx @@ -0,0 +1,64 @@ +import { useLocation, useNavigate } from 'react-router-dom'; +import { useSetRecoilState } from 'recoil'; + +import { useCurrentUserTaskCount } from '@/activities/tasks/hooks/useCurrentUserDueTaskCount'; +import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; +import { Favorites } from '@/favorites/components/Favorites'; +import { useIsTasksPage } from '@/navigation/hooks/useIsTasksPage'; +import { SettingsNavbar } from '@/settings/components/SettingsNavbar'; +import { + IconBell, + IconCheckbox, + IconSearch, + IconSettings, +} from '@/ui/display/icon/index'; +import MainNavbar from '@/ui/navigation/navigation-drawer/components/MainNavbar'; +import NavItem from '@/ui/navigation/navigation-drawer/components/NavItem'; +import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; + +import { useIsSettingsPage } from '../hooks/useIsSettingsPage'; + +import { WorkspaceNavItems } from './WorkspaceNavItems'; + +export const DesktopNavigationDrawer = () => { + const { toggleCommandMenu } = useCommandMenu(); + const isSettingsPage = useIsSettingsPage(); + const isTasksPage = useIsTasksPage(); + const { currentUserDueTaskCount } = useCurrentUserTaskCount(); + const navigate = useNavigate(); + const location = useLocation(); + const setNavigationMemorizedUrl = useSetRecoilState( + navigationMemorizedUrlState, + ); + + return isSettingsPage ? ( + + ) : ( + + + + { + setNavigationMemorizedUrl(location.pathname + location.search); + navigate('/settings/profile'); + }} + Icon={IconSettings} + /> + + + + + ); +}; diff --git a/front/src/modules/navigation/components/MobileNavigationBar.tsx b/front/src/modules/navigation/components/MobileNavigationBar.tsx new file mode 100644 index 000000000..0a34f66e4 --- /dev/null +++ b/front/src/modules/navigation/components/MobileNavigationBar.tsx @@ -0,0 +1,85 @@ +import { useNavigate } from 'react-router-dom'; +import { useRecoilState } from 'recoil'; + +import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; +import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; +import { + IconCheckbox, + IconList, + IconSearch, + IconSettings, +} from '@/ui/display/icon'; +import { IconComponent } from '@/ui/display/icon/types/IconComponent'; +import { NavigationBar } from '@/ui/navigation/navigation-bar/components/NavigationBar'; +import { navigationDrawerState } from '@/ui/navigation/states/navigationDrawerState'; + +import { useIsSettingsPage } from '../hooks/useIsSettingsPage'; +import { useIsTasksPage } from '../hooks/useIsTasksPage'; + +type NavigationBarItemName = 'main' | 'search' | 'tasks' | 'settings'; + +export const MobileNavigationBar = () => { + const [isCommandMenuOpened] = useRecoilState(isCommandMenuOpenedState); + const { closeCommandMenu, toggleCommandMenu } = useCommandMenu(); + const isTasksPage = useIsTasksPage(); + const isSettingsPage = useIsSettingsPage(); + const navigate = useNavigate(); + const [navigationDrawer, setNavigationDrawer] = useRecoilState( + navigationDrawerState, + ); + + const initialActiveItemName: NavigationBarItemName = isCommandMenuOpened + ? 'search' + : isTasksPage + ? 'tasks' + : isSettingsPage + ? 'settings' + : 'main'; + + const items: { + name: NavigationBarItemName; + Icon: IconComponent; + onClick: () => void; + }[] = [ + { + name: 'main', + Icon: IconList, + onClick: () => { + closeCommandMenu(); + setNavigationDrawer(navigationDrawer === 'main' ? '' : 'main'); + }, + }, + { + name: 'search', + Icon: IconSearch, + onClick: () => { + setNavigationDrawer(''); + toggleCommandMenu(); + }, + }, + { + name: 'tasks', + Icon: IconCheckbox, + onClick: () => { + closeCommandMenu(); + setNavigationDrawer(''); + navigate('/tasks'); + }, + }, + { + name: 'settings', + Icon: IconSettings, + onClick: () => { + closeCommandMenu(); + setNavigationDrawer(navigationDrawer === 'settings' ? '' : 'settings'); + }, + }, + ]; + + return ( + + ); +}; diff --git a/front/src/modules/navigation/components/MobileNavigationDrawer.tsx b/front/src/modules/navigation/components/MobileNavigationDrawer.tsx new file mode 100644 index 000000000..eb3264237 --- /dev/null +++ b/front/src/modules/navigation/components/MobileNavigationDrawer.tsx @@ -0,0 +1,21 @@ +import { useRecoilState } from 'recoil'; + +import { Favorites } from '@/favorites/components/Favorites'; +import { SettingsNavbar } from '@/settings/components/SettingsNavbar'; +import MainNavbar from '@/ui/navigation/navigation-drawer/components/MainNavbar'; +import { navigationDrawerState } from '@/ui/navigation/states/navigationDrawerState'; + +import { WorkspaceNavItems } from './WorkspaceNavItems'; + +export const MobileNavigationDrawer = () => { + const [navigationDrawer] = useRecoilState(navigationDrawerState); + + return navigationDrawer === 'settings' ? ( + + ) : ( + + + + + ); +}; diff --git a/front/src/modules/navigation/components/WorkspaceNavItems.tsx b/front/src/modules/navigation/components/WorkspaceNavItems.tsx new file mode 100644 index 000000000..4e8913588 --- /dev/null +++ b/front/src/modules/navigation/components/WorkspaceNavItems.tsx @@ -0,0 +1,23 @@ +import { useLocation } from 'react-router-dom'; + +import { ObjectMetadataNavItems } from '@/object-metadata/components/ObjectMetadataNavItems'; +import { IconTargetArrow } from '@/ui/display/icon/index'; +import NavItem from '@/ui/navigation/navigation-drawer/components/NavItem'; +import NavTitle from '@/ui/navigation/navigation-drawer/components/NavTitle'; + +export const WorkspaceNavItems = () => { + const { pathname } = useLocation(); + + return ( + <> + + + + + ); +}; diff --git a/front/src/modules/navigation/components/__stories__/DesktopNavigationDrawer.stories.tsx b/front/src/modules/navigation/components/__stories__/DesktopNavigationDrawer.stories.tsx new file mode 100644 index 000000000..f56cea7a6 --- /dev/null +++ b/front/src/modules/navigation/components/__stories__/DesktopNavigationDrawer.stories.tsx @@ -0,0 +1,23 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator'; + +import { DesktopNavigationDrawer } from '../DesktopNavigationDrawer'; +import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; +import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; + +const meta: Meta = { + title: 'Modules/Navigation/DesktopNavigationDrawer', + component: DesktopNavigationDrawer, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + decorators: [ + ComponentDecorator, + ComponentWithRouterDecorator, + SnackBarDecorator, + ], +}; diff --git a/front/src/modules/navigation/components/__stories__/MobileNavigationDrawer.stories.tsx b/front/src/modules/navigation/components/__stories__/MobileNavigationDrawer.stories.tsx new file mode 100644 index 000000000..dea61479b --- /dev/null +++ b/front/src/modules/navigation/components/__stories__/MobileNavigationDrawer.stories.tsx @@ -0,0 +1,23 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator'; + +import { MobileNavigationDrawer } from '../MobileNavigationDrawer'; +import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; +import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; + +const meta: Meta = { + title: 'Modules/Navigation/MobileNavigationDrawer', + component: MobileNavigationDrawer, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + decorators: [ + ComponentDecorator, + ComponentWithRouterDecorator, + SnackBarDecorator, + ], +}; diff --git a/front/src/modules/navigation/hooks/useIsSettingsPage.ts b/front/src/modules/navigation/hooks/useIsSettingsPage.ts new file mode 100644 index 000000000..d0335c428 --- /dev/null +++ b/front/src/modules/navigation/hooks/useIsSettingsPage.ts @@ -0,0 +1,4 @@ +import { useLocation } from 'react-router-dom'; + +export const useIsSettingsPage = () => + useLocation().pathname.match(/\/settings\//g) !== null; diff --git a/front/src/modules/navigation/hooks/useIsTasksPage.ts b/front/src/modules/navigation/hooks/useIsTasksPage.ts new file mode 100644 index 000000000..a3c0ac4ab --- /dev/null +++ b/front/src/modules/navigation/hooks/useIsTasksPage.ts @@ -0,0 +1,3 @@ +import { useLocation } from 'react-router-dom'; + +export const useIsTasksPage = () => useLocation().pathname === '/tasks'; diff --git a/front/src/modules/object-metadata/components/ObjectMetadataNavItems.tsx b/front/src/modules/object-metadata/components/ObjectMetadataNavItems.tsx index d32fd73ac..99d3382e1 100644 --- a/front/src/modules/object-metadata/components/ObjectMetadataNavItems.tsx +++ b/front/src/modules/object-metadata/components/ObjectMetadataNavItems.tsx @@ -3,7 +3,7 @@ import { useLocation, useNavigate } from 'react-router-dom'; import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings'; import { Icon123 } from '@/ui/input/constants/icons'; import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons'; -import NavItem from '@/ui/navigation/navbar/components/NavItem'; +import NavItem from '@/ui/navigation/navigation-drawer/components/NavItem'; export const ObjectMetadataNavItems = () => { const { activeObjectMetadataItems } = useObjectMetadataItemForSettings(); diff --git a/front/src/modules/object-record/components/RecordTableContainer.tsx b/front/src/modules/object-record/components/RecordTableContainer.tsx index 267d4e886..1ea6420c4 100644 --- a/front/src/modules/object-record/components/RecordTableContainer.tsx +++ b/front/src/modules/object-record/components/RecordTableContainer.tsx @@ -89,12 +89,14 @@ export const RecordTableContainer = ({ }} /> - + { + + } ); }; diff --git a/front/src/modules/settings/components/SettingsNavbar.tsx b/front/src/modules/settings/components/SettingsNavbar.tsx index 95bcac2f0..1cc731ba0 100644 --- a/front/src/modules/settings/components/SettingsNavbar.tsx +++ b/front/src/modules/settings/components/SettingsNavbar.tsx @@ -12,9 +12,9 @@ import { IconUserCircle, IconUsers, } from '@/ui/display/icon/index'; -import NavItem from '@/ui/navigation/navbar/components/NavItem'; -import NavTitle from '@/ui/navigation/navbar/components/NavTitle'; -import SubMenuNavbar from '@/ui/navigation/navbar/components/SubMenuNavbar'; +import NavItem from '@/ui/navigation/navigation-drawer/components/NavItem'; +import NavTitle from '@/ui/navigation/navigation-drawer/components/NavTitle'; +import SubMenuNavbar from '@/ui/navigation/navigation-drawer/components/SubMenuNavbar'; export const SettingsNavbar = () => { const navigate = useNavigate(); diff --git a/front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDisabledActionDropdown.tsx b/front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDisabledActionDropdown.tsx index d9fa6ebf0..c7f5030b5 100644 --- a/front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDisabledActionDropdown.tsx +++ b/front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDisabledActionDropdown.tsx @@ -1,4 +1,4 @@ -import { IconArchiveOff, IconDotsVertical, IconTrash } from '@/ui/display/icon'; +import { IconArchiveOff, IconDotsVertical } from '@/ui/display/icon'; import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; @@ -15,9 +15,7 @@ type SettingsObjectFieldDisabledActionDropdownProps = { }; export const SettingsObjectFieldDisabledActionDropdown = ({ - isCustomField, onActivate, - onErase, scopeKey, }: SettingsObjectFieldDisabledActionDropdownProps) => { const dropdownScopeId = `${scopeKey}-settings-field-disabled-action-dropdown`; @@ -29,10 +27,10 @@ export const SettingsObjectFieldDisabledActionDropdown = ({ closeDropdown(); }; - const handleErase = () => { - onErase(); - closeDropdown(); - }; + // const handleErase = () => { + // onErase(); + // closeDropdown(); + // }; return ( @@ -48,14 +46,14 @@ export const SettingsObjectFieldDisabledActionDropdown = ({ LeftIcon={IconArchiveOff} onClick={handleActivate} /> - {isCustomField && ( + {/* {isCustomField && ( - )} + )} */} } diff --git a/front/src/modules/settings/data-model/objects/SettingsObjectDisabledMenuDropDown.tsx b/front/src/modules/settings/data-model/objects/SettingsObjectDisabledMenuDropDown.tsx index 9ca462ddc..708905729 100644 --- a/front/src/modules/settings/data-model/objects/SettingsObjectDisabledMenuDropDown.tsx +++ b/front/src/modules/settings/data-model/objects/SettingsObjectDisabledMenuDropDown.tsx @@ -1,4 +1,4 @@ -import { IconDotsVertical, IconTrash } from '@/ui/display/icon'; +import { IconDotsVertical } from '@/ui/display/icon'; import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; import { IconArchiveOff } from '@/ui/input/constants/icons'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; @@ -16,9 +16,7 @@ type SettingsObjectDisabledMenuDropDownProps = { }; export const SettingsObjectDisabledMenuDropDown = ({ - isCustomObject, onActivate, - onErase, scopeKey, }: SettingsObjectDisabledMenuDropDownProps) => { const dropdownScopeId = `${scopeKey}-settings-object-disabled-menu-dropdown`; @@ -30,10 +28,10 @@ export const SettingsObjectDisabledMenuDropDown = ({ closeDropdown(); }; - const handleErase = () => { - onErase(); - closeDropdown(); - }; + // const handleErase = () => { + // onErase(); + // closeDropdown(); + // }; return ( @@ -49,14 +47,14 @@ export const SettingsObjectDisabledMenuDropDown = ({ LeftIcon={IconArchiveOff} onClick={handleActivate} /> - {isCustomObject && ( + {/* {isCustomObject && ( - )} + )} */} } diff --git a/front/src/modules/ui/layout/hooks/useIsMenuNavbarDisplayed.ts b/front/src/modules/ui/layout/hooks/useIsMenuNavbarDisplayed.ts new file mode 100644 index 000000000..08f6103f3 --- /dev/null +++ b/front/src/modules/ui/layout/hooks/useIsMenuNavbarDisplayed.ts @@ -0,0 +1,6 @@ +import { useLocation } from 'react-router-dom'; + +export const useIsMenuNavbarDisplayed = () => { + const currentPath = useLocation().pathname; + return currentPath.match(/^\/companies(\/.*)?$/) !== null; +}; diff --git a/front/src/modules/ui/layout/hooks/useIsSubMenuNavbarDisplayed.ts b/front/src/modules/ui/layout/hooks/useIsSubMenuNavbarDisplayed.ts deleted file mode 100644 index 70622b66f..000000000 --- a/front/src/modules/ui/layout/hooks/useIsSubMenuNavbarDisplayed.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { useLocation } from 'react-router-dom'; - -export const useIsSubMenuNavbarDisplayed = () => { - const currentPath = useLocation().pathname; - return currentPath.match(/\/settings\//g) !== null; -}; diff --git a/front/src/modules/ui/layout/page/DefaultLayout.tsx b/front/src/modules/ui/layout/page/DefaultLayout.tsx index a3ead82ba..023e929ca 100644 --- a/front/src/modules/ui/layout/page/DefaultLayout.tsx +++ b/front/src/modules/ui/layout/page/DefaultLayout.tsx @@ -1,6 +1,6 @@ +import { ReactNode } from 'react'; import styled from '@emotion/styled'; import { AnimatePresence, LayoutGroup } from 'framer-motion'; -import { useRecoilValue } from 'recoil'; import { AuthModal } from '@/auth/components/Modal'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; @@ -8,23 +8,22 @@ import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus'; import { CommandMenu } from '@/command-menu/components/CommandMenu'; import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary'; import { KeyboardShortcutMenu } from '@/keyboard-shortcut-menu/components/KeyboardShortcutMenu'; +import { DesktopNavigationDrawer } from '@/navigation/components/DesktopNavigationDrawer'; +import { MobileNavigationBar } from '@/navigation/components/MobileNavigationBar'; +import { MobileNavigationDrawer } from '@/navigation/components/MobileNavigationDrawer'; import { SignInBackgroundMockPage } from '@/sign-in-background-mock/components/SignInBackgroundMockPage'; -import { NavbarAnimatedContainer } from '@/ui/navigation/navbar/components/NavbarAnimatedContainer'; -import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme'; -import { AppNavbar } from '~/AppNavbar'; - -import { isNavbarOpenedState } from '../states/isNavbarOpenedState'; +import { NavbarAnimatedContainer } from '@/ui/navigation/navigation-drawer/components/NavbarAnimatedContainer'; +import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; const StyledLayout = styled.div` background: ${({ theme }) => theme.background.noisy}; display: flex; - flex-direction: row; + flex-direction: column; height: 100vh; position: relative; scrollbar-color: ${({ theme }) => theme.border.color.medium}; - scrollbar-width: 4px; - width: 100vw; + width: 100%; *::-webkit-scrollbar { height: 4px; @@ -41,43 +40,51 @@ const StyledLayout = styled.div` } `; -const StyledMainContainer = styled.div` +const StyledPageContainer = styled.div` display: flex; flex: 1; flex-direction: row; +`; + +const StyledMainContainer = styled.div` + display: flex; + flex: 0 1 100%; + flex-direction: row; overflow: hidden; - @media (max-width: ${MOBILE_VIEWPORT}px) { - width: ${() => (useRecoilValue(isNavbarOpenedState) ? '0' : '100%')}; - } `; type DefaultLayoutProps = { - children: React.ReactNode; + children: ReactNode; }; export const DefaultLayout = ({ children }: DefaultLayoutProps) => { const onboardingStatus = useOnboardingStatus(); + const isMobile = useIsMobile(); return ( - - - - - {onboardingStatus && onboardingStatus !== OnboardingStatus.Completed ? ( - <> - - - - {children} - - - - ) : ( - {children} - )} - + + + {isMobile ? : } + + + {onboardingStatus && + onboardingStatus !== OnboardingStatus.Completed ? ( + <> + + + + {children} + + + + ) : ( + {children} + )} + + + {isMobile && } ); }; diff --git a/front/src/modules/ui/layout/page/PageBody.tsx b/front/src/modules/ui/layout/page/PageBody.tsx index 65bcbc8c7..4e1269b3e 100644 --- a/front/src/modules/ui/layout/page/PageBody.tsx +++ b/front/src/modules/ui/layout/page/PageBody.tsx @@ -1,12 +1 @@ -import { PAGE_BAR_MIN_HEIGHT } from './PageHeader'; -import { RightDrawerContainer } from './RightDrawerContainer'; - -type PageBodyProps = { - children: JSX.Element | JSX.Element[]; -}; - -export const PageBody = ({ children }: PageBodyProps) => ( - - {children} - -); +export { RightDrawerContainer as PageBody } from './RightDrawerContainer'; diff --git a/front/src/modules/ui/layout/page/PageContainer.tsx b/front/src/modules/ui/layout/page/PageContainer.tsx index 6a552e0e7..9f1717ec5 100644 --- a/front/src/modules/ui/layout/page/PageContainer.tsx +++ b/front/src/modules/ui/layout/page/PageContainer.tsx @@ -1,15 +1,9 @@ import styled from '@emotion/styled'; -type PageContainerProps = { - children: JSX.Element | JSX.Element[]; -}; - const StyledContainer = styled.div` display: flex; flex-direction: column; width: 100%; `; -export const PageContainer = ({ children }: PageContainerProps) => ( - {children} -); +export { StyledContainer as PageContainer }; diff --git a/front/src/modules/ui/layout/page/PageHeader.tsx b/front/src/modules/ui/layout/page/PageHeader.tsx index de4a30f29..489ef07a4 100644 --- a/front/src/modules/ui/layout/page/PageHeader.tsx +++ b/front/src/modules/ui/layout/page/PageHeader.tsx @@ -1,4 +1,4 @@ -import { ComponentProps, useCallback } from 'react'; +import { ComponentProps, ReactNode } from 'react'; import { useNavigate } from 'react-router-dom'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; @@ -7,15 +7,12 @@ import { useRecoilValue } from 'recoil'; import { IconChevronLeft } from '@/ui/display/icon/index'; import { IconComponent } from '@/ui/display/icon/types/IconComponent'; import { OverflowingTextWithTooltip } from '@/ui/display/tooltip/OverflowingTextWithTooltip'; -import { - IconButton, - IconButtonSize, -} from '@/ui/input/button/components/IconButton'; -import NavCollapseButton from '@/ui/navigation/navbar/components/NavCollapseButton'; +import { IconButton } from '@/ui/input/button/components/IconButton'; +import NavCollapseButton from '@/ui/navigation/navigation-drawer/components/NavCollapseButton'; +import { navigationDrawerState } from '@/ui/navigation/states/navigationDrawerState'; +import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; -import { isNavbarOpenedState } from '../states/isNavbarOpenedState'; - export const PAGE_BAR_MIN_HEIGHT = 40; const StyledTopBarContainer = styled.div` @@ -31,13 +28,23 @@ const StyledTopBarContainer = styled.div` padding-left: 0; padding-right: ${({ theme }) => theme.spacing(3)}; z-index: 20; + + @media (max-width: ${MOBILE_VIEWPORT}px) { + padding-left: ${({ theme }) => theme.spacing(3)}; + } `; const StyledLeftContainer = styled.div` align-items: center; display: flex; flex-direction: row; + gap: ${({ theme }) => theme.spacing(1)}; + padding-left: ${({ theme }) => theme.spacing(2)}; width: 100%; + + @media (max-width: ${MOBILE_VIEWPORT}px) { + padding-left: ${({ theme }) => theme.spacing(1)}; + } `; const StyledTitleContainer = styled.div` @@ -47,24 +54,15 @@ const StyledTitleContainer = styled.div` max-width: 50%; `; -const StyledTopBarButtonContainer = styled.div` - margin-left: ${({ theme }) => theme.spacing(1)}; - margin-right: ${({ theme }) => theme.spacing(1)}; -`; - const StyledBackIconButton = styled(IconButton)` margin-right: ${({ theme }) => theme.spacing(1)}; `; -const StyledTopBarIconStyledTitleContainer = styled.div<{ - hideLeftPadding?: boolean; -}>` +const StyledTopBarIconStyledTitleContainer = styled.div` align-items: center; display: flex; + flex: 1 0 100%; flex-direction: row; - padding-left: ${({ theme, hideLeftPadding }) => - hideLeftPadding ? theme.spacing(2) : undefined}; - width: 100%; `; const StyledPageActionContainer = styled.div` @@ -72,11 +70,16 @@ const StyledPageActionContainer = styled.div` gap: ${({ theme }) => theme.spacing(2)}; `; +const StyledTopBarButtonContainer = styled.div` + margin-left: ${({ theme }) => theme.spacing(1)}; + margin-right: ${({ theme }) => theme.spacing(1)}; +`; + type PageHeaderProps = ComponentProps<'div'> & { title: string; hasBackButton?: boolean; Icon: IconComponent; - children?: JSX.Element | JSX.Element[]; + children?: ReactNode; }; export const PageHeader = ({ @@ -85,33 +88,28 @@ export const PageHeader = ({ Icon, children, }: PageHeaderProps) => { + const isMobile = useIsMobile(); const navigate = useNavigate(); - const navigateBack = useCallback(() => navigate(-1), [navigate]); - - const isNavbarOpened = useRecoilValue(isNavbarOpenedState); - - const iconSize: IconButtonSize = useIsMobile() ? 'small' : 'medium'; const theme = useTheme(); + const navigationDrawer = useRecoilValue(navigationDrawerState); return ( - {!isNavbarOpened && ( + {navigationDrawer === '' && ( )} {hasBackButton && ( - - - + navigate(-1)} + variant="tertiary" + /> )} - + {Icon && } diff --git a/front/src/modules/ui/layout/page/RightDrawerContainer.tsx b/front/src/modules/ui/layout/page/RightDrawerContainer.tsx index ce56190b5..353bc124a 100644 --- a/front/src/modules/ui/layout/page/RightDrawerContainer.tsx +++ b/front/src/modules/ui/layout/page/RightDrawerContainer.tsx @@ -1,25 +1,30 @@ +import { ReactNode } from 'react'; import styled from '@emotion/styled'; import { RightDrawer } from '@/ui/layout/right-drawer/components/RightDrawer'; +import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme'; import { PagePanel } from './PagePanel'; type RightDrawerContainerProps = { - children: JSX.Element | JSX.Element[]; - topMargin?: number; + children: ReactNode; }; -const StyledMainContainer = styled.div<{ topMargin: number }>` +const StyledMainContainer = styled.div` background: ${({ theme }) => theme.background.noisy}; + box-sizing: border-box; display: flex; - flex-direction: row; gap: ${({ theme }) => theme.spacing(2)}; - height: calc(100% - ${(props) => props.topMargin}px); - + height: 100%; padding-bottom: ${({ theme }) => theme.spacing(3)}; padding-right: ${({ theme }) => theme.spacing(3)}; - width: calc(100% - ${({ theme }) => theme.spacing(3)}); + width: 100%; + + @media (max-width: ${MOBILE_VIEWPORT}px) { + padding-left: ${({ theme }) => theme.spacing(3)}; + padding-bottom: 0; + } `; type LeftContainerProps = { @@ -35,9 +40,8 @@ const StyledLeftContainer = styled.div` export const RightDrawerContainer = ({ children, - topMargin, }: RightDrawerContainerProps) => ( - + {children} diff --git a/front/src/modules/ui/layout/page/SubMenuTopBarContainer.tsx b/front/src/modules/ui/layout/page/SubMenuTopBarContainer.tsx index cdd46c91e..e18b29ac3 100644 --- a/front/src/modules/ui/layout/page/SubMenuTopBarContainer.tsx +++ b/front/src/modules/ui/layout/page/SubMenuTopBarContainer.tsx @@ -30,7 +30,7 @@ export const SubMenuTopBarContainer = ({ return ( {isMobile && } - {children} + {children} ); }; diff --git a/front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx b/front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx index 59faefb7a..37ea8499c 100644 --- a/front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx +++ b/front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx @@ -13,7 +13,6 @@ import { import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { isDefined } from '~/utils/isDefined'; -import { leftNavbarWidth } from '../../../navigation/navbar/constants'; import { useRightDrawer } from '../hooks/useRightDrawer'; import { isRightDrawerExpandedState } from '../states/isRightDrawerExpandedState'; import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState'; @@ -70,15 +69,9 @@ export const RightDrawer = () => { const isMobile = useIsMobile(); - const rightDrawerWidthExpanded = `calc(100% - ${ - leftNavbarWidth.desktop - } - ${theme.spacing(2)})`; - const rightDrawerWidth = isRightDrawerOpen - ? isMobile + ? isMobile || isRightDrawerExpanded ? '100%' - : isRightDrawerExpanded - ? rightDrawerWidthExpanded : theme.rightDrawerWidth : '0'; diff --git a/front/src/modules/ui/layout/states/isNavbarOpenedState.ts b/front/src/modules/ui/layout/states/isNavbarOpenedState.ts deleted file mode 100644 index 9c3253770..000000000 --- a/front/src/modules/ui/layout/states/isNavbarOpenedState.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { atom } from 'recoil'; - -import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme'; - -const isMobile = window.innerWidth <= MOBILE_VIEWPORT; - -export const isNavbarOpenedState = atom({ - key: 'ui/isNavbarOpenedState', - default: !isMobile, -}); diff --git a/front/src/modules/ui/layout/states/isNavbarSwitchingSizeState.ts b/front/src/modules/ui/layout/states/isNavbarSwitchingSizeState.ts deleted file mode 100644 index c9a524484..000000000 --- a/front/src/modules/ui/layout/states/isNavbarSwitchingSizeState.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { atom } from 'recoil'; - -export const isNavbarSwitchingSizeState = atom({ - key: 'ui/isNavbarSwitchingSizeState', - default: true, -}); diff --git a/front/src/modules/ui/navigation/navbar/components/NavbarAnimatedContainer.tsx b/front/src/modules/ui/navigation/navbar/components/NavbarAnimatedContainer.tsx deleted file mode 100644 index 72324aa20..000000000 --- a/front/src/modules/ui/navigation/navbar/components/NavbarAnimatedContainer.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { useTheme } from '@emotion/react'; -import styled from '@emotion/styled'; -import { motion } from 'framer-motion'; -import { useRecoilState, useRecoilValue } from 'recoil'; - -import { useIsSubMenuNavbarDisplayed } from '@/ui/layout/hooks/useIsSubMenuNavbarDisplayed'; -import { isNavbarOpenedState } from '@/ui/layout/states/isNavbarOpenedState'; -import { isNavbarSwitchingSizeState } from '@/ui/layout/states/isNavbarSwitchingSizeState'; -import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; - -import { leftNavbarWidth, leftSubMenuNavbarWidth } from '../constants'; - -const StyledNavbarContainer = styled(motion.div)` - align-items: end; - display: flex; - flex-direction: column; - flex-shrink: 0; - overflow: hidden; - padding: ${({ theme }) => theme.spacing(2)}; -`; - -type NavbarAnimatedContainerProps = { - children: React.ReactNode; -}; - -export const NavbarAnimatedContainer = ({ - children, -}: NavbarAnimatedContainerProps) => { - const isNavbarOpened = useRecoilValue(isNavbarOpenedState); - const [, setIsNavbarSwitchingSize] = useRecoilState( - isNavbarSwitchingSizeState, - ); - const isInSubMenu = useIsSubMenuNavbarDisplayed(); - const theme = useTheme(); - - const isMobile = useIsMobile(); - - const leftBarWidth = isInSubMenu - ? isMobile - ? leftSubMenuNavbarWidth.mobile - : leftSubMenuNavbarWidth.desktop - : isMobile - ? leftNavbarWidth.mobile - : leftNavbarWidth.desktop; - - return ( - { - setIsNavbarSwitchingSize(false); - }} - initial={false} - animate={{ - width: isNavbarOpened ? leftBarWidth : '0', - opacity: isNavbarOpened ? 1 : 0, - }} - transition={{ - duration: theme.animation.duration.normal, - }} - > - {children} - - ); -}; diff --git a/front/src/modules/ui/navigation/navbar/constants/index.ts b/front/src/modules/ui/navigation/navbar/constants/index.ts deleted file mode 100644 index 664eac8de..000000000 --- a/front/src/modules/ui/navigation/navbar/constants/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const leftNavbarWidth = { - mobile: 'calc(100% - 16px)', - desktop: '220px', -}; - -export const leftSubMenuNavbarWidth = { - mobile: 'calc(100% - 16px)', - desktop: '520px', -}; - -export const githubLink = 'https://github.com/twentyhq/twenty'; diff --git a/front/src/modules/ui/navigation/navigation-bar/components/NavigationBar.tsx b/front/src/modules/ui/navigation/navigation-bar/components/NavigationBar.tsx new file mode 100644 index 000000000..8ab53a38b --- /dev/null +++ b/front/src/modules/ui/navigation/navigation-bar/components/NavigationBar.tsx @@ -0,0 +1,33 @@ +import styled from '@emotion/styled'; + +import { IconComponent } from '@/ui/display/icon/types/IconComponent'; + +import { NavigationBarItem } from './NavigationBarItem'; + +const StyledContainer = styled.div` + display: flex; + gap: ${({ theme }) => theme.spacing(4)}; + justify-content: center; + padding: ${({ theme }) => theme.spacing(3)}; +`; + +type NavigationBarProps = { + activeItemName: string; + items: { name: string; Icon: IconComponent; onClick: () => void }[]; +}; + +export const NavigationBar = ({ + activeItemName, + items, +}: NavigationBarProps) => ( + + {items.map(({ Icon, name, onClick }) => ( + + ))} + +); diff --git a/front/src/modules/ui/navigation/navigation-bar/components/NavigationBarItem.tsx b/front/src/modules/ui/navigation/navigation-bar/components/NavigationBarItem.tsx new file mode 100644 index 000000000..ccd6b7d3f --- /dev/null +++ b/front/src/modules/ui/navigation/navigation-bar/components/NavigationBarItem.tsx @@ -0,0 +1,42 @@ +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; + +import { IconComponent } from '@/ui/display/icon/types/IconComponent'; + +const StyledIconButton = styled.div<{ isActive?: boolean }>` + align-items: center; + background-color: ${({ isActive, theme }) => + isActive ? theme.background.transparent.light : 'none'}; + border-radius: ${({ theme }) => theme.spacing(1)}; + cursor: pointer; + display: flex; + height: ${({ theme }) => theme.spacing(10)}; + justify-content: center; + transition: background-color ${({ theme }) => theme.animation.duration.fast}s + ease; + width: ${({ theme }) => theme.spacing(10)}; + + &:hover { + background-color: ${({ theme }) => theme.background.transparent.light}; + } +`; + +type NavigationBarItemProps = { + Icon: IconComponent; + isActive: boolean; + onClick: () => void; +}; + +export const NavigationBarItem = ({ + Icon, + isActive, + onClick, +}: NavigationBarItemProps) => { + const theme = useTheme(); + + return ( + + + + ); +}; diff --git a/front/src/modules/ui/navigation/navigation-bar/components/__stories__/NavigationBar.stories.tsx b/front/src/modules/ui/navigation/navigation-bar/components/__stories__/NavigationBar.stories.tsx new file mode 100644 index 000000000..3276b08e7 --- /dev/null +++ b/front/src/modules/ui/navigation/navigation-bar/components/__stories__/NavigationBar.stories.tsx @@ -0,0 +1,33 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator'; + +import { NavigationBar } from '../NavigationBar'; +import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; +import { + IconList, + IconSearch, + IconCheckbox, + IconSettings, +} from '@/ui/display/icon'; + +const meta: Meta = { + title: 'UI/Navigation/NavigationBar/NavigationBar', + component: NavigationBar, + args: { + activeItemName: 'main', + items: [ + { name: 'main', Icon: IconList, onClick: () => undefined }, + { name: 'search', Icon: IconSearch, onClick: () => undefined }, + { name: 'tasks', Icon: IconCheckbox, onClick: () => undefined }, + { name: 'settings', Icon: IconSettings, onClick: () => undefined }, + ], + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + decorators: [ComponentDecorator, ComponentWithRouterDecorator], +}; diff --git a/front/src/modules/ui/navigation/navbar/components/MainNavbar.tsx b/front/src/modules/ui/navigation/navigation-drawer/components/MainNavbar.tsx similarity index 93% rename from front/src/modules/ui/navigation/navbar/components/MainNavbar.tsx rename to front/src/modules/ui/navigation/navigation-drawer/components/MainNavbar.tsx index 99043bee2..0a55b22db 100644 --- a/front/src/modules/ui/navigation/navbar/components/MainNavbar.tsx +++ b/front/src/modules/ui/navigation/navigation-drawer/components/MainNavbar.tsx @@ -10,11 +10,13 @@ type MainNavbarProps = { }; const StyledContainer = styled.div` + box-sizing: border-box; display: flex; flex-direction: column; height: 100%; justify-content: space-between; margin-bottom: ${({ theme }) => theme.spacing(2.5)}; + padding: ${({ theme }) => theme.spacing(2)}; width: 100%; `; diff --git a/front/src/modules/ui/navigation/navbar/components/NavBackButton.tsx b/front/src/modules/ui/navigation/navigation-drawer/components/NavBackButton.tsx similarity index 61% rename from front/src/modules/ui/navigation/navbar/components/NavBackButton.tsx rename to front/src/modules/ui/navigation/navigation-drawer/components/NavBackButton.tsx index eb4118cd1..6609b80ca 100644 --- a/front/src/modules/ui/navigation/navbar/components/NavBackButton.tsx +++ b/front/src/modules/ui/navigation/navigation-drawer/components/NavBackButton.tsx @@ -1,9 +1,9 @@ import { useNavigate } from 'react-router-dom'; import styled from '@emotion/styled'; -import { useRecoilState } from 'recoil'; +import { useRecoilValue } from 'recoil'; import { IconChevronLeft } from '@/ui/display/icon/index'; -import { isNavbarSwitchingSizeState } from '@/ui/layout/states/isNavbarSwitchingSizeState'; +import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; type NavBackButtonProps = { title: string; @@ -32,24 +32,19 @@ const StyledContainer = styled.div` const NavBackButton = ({ title }: NavBackButtonProps) => { const navigate = useNavigate(); - const [, setIsNavbarSwitchingSize] = useRecoilState( - isNavbarSwitchingSizeState, - ); + const navigationMemorizedUrl = useRecoilValue(navigationMemorizedUrlState); return ( - <> - - { - setIsNavbarSwitchingSize(true); - navigate('/', { replace: true }); - }} - > - - {title} - - - + + { + navigate(navigationMemorizedUrl, { replace: true }); + }} + > + + {title} + + ); }; diff --git a/front/src/modules/ui/navigation/navbar/components/NavCollapseButton.tsx b/front/src/modules/ui/navigation/navigation-drawer/components/NavCollapseButton.tsx similarity index 81% rename from front/src/modules/ui/navigation/navbar/components/NavCollapseButton.tsx rename to front/src/modules/ui/navigation/navigation-drawer/components/NavCollapseButton.tsx index e7f93407f..94e2bead5 100644 --- a/front/src/modules/ui/navigation/navbar/components/NavCollapseButton.tsx +++ b/front/src/modules/ui/navigation/navigation-drawer/components/NavCollapseButton.tsx @@ -1,14 +1,14 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { motion } from 'framer-motion'; -import { useRecoilState } from 'recoil'; +import { useSetRecoilState } from 'recoil'; import { IconLayoutSidebarLeftCollapse, IconLayoutSidebarRightCollapse, } from '@/ui/display/icon'; import { IconButton } from '@/ui/input/button/components/IconButton'; -import { isNavbarOpenedState } from '@/ui/layout/states/isNavbarOpenedState'; +import { navigationDrawerState } from '@/ui/navigation/states/navigationDrawerState'; const StyledCollapseButton = styled(motion.div)` align-items: center; @@ -41,8 +41,7 @@ const NavCollapseButton = ({ direction = 'left', show = true, }: NavCollapseButtonProps) => { - const [isNavbarOpened, setIsNavbarOpened] = - useRecoilState(isNavbarOpenedState); + const setNavigationDrawer = useSetRecoilState(navigationDrawerState); const iconSize = 'small'; const theme = useTheme(); @@ -57,7 +56,11 @@ const NavCollapseButton = ({ transition={{ duration: theme.animation.duration.normal, }} - onClick={() => setIsNavbarOpened(!isNavbarOpened)} + onClick={() => + setNavigationDrawer((navigationDrawer) => + navigationDrawer === '' ? 'main' : '', + ) + } > void; @@ -115,6 +115,7 @@ const StyledKeyBoardShortcut = styled.div` `; const NavItem = ({ + className, label, Icon, to, @@ -126,25 +127,26 @@ const NavItem = ({ keyboard, }: NavItemProps) => { const theme = useTheme(); - const navigate = useNavigate(); - const [, setIsNavbarOpened] = useRecoilState(isNavbarOpenedState); - const isMobile = useIsMobile(); + const navigate = useNavigate(); + const setNavigationDrawer = useSetRecoilState(navigationDrawerState); const handleItemClick = () => { if (isMobile) { - setIsNavbarOpened(false); + setNavigationDrawer(''); } if (onClick) { onClick(); - } else if (to) { - navigate(to); + return; } + + if (to) navigate(to); }; return ( {count}} {keyboard && ( - {keyboard.map((key) => key)} + {keyboard} )} diff --git a/front/src/modules/ui/navigation/navbar/components/NavItemsContainer.tsx b/front/src/modules/ui/navigation/navigation-drawer/components/NavItemsContainer.tsx similarity index 100% rename from front/src/modules/ui/navigation/navbar/components/NavItemsContainer.tsx rename to front/src/modules/ui/navigation/navigation-drawer/components/NavItemsContainer.tsx diff --git a/front/src/modules/ui/navigation/navbar/components/NavTitle.tsx b/front/src/modules/ui/navigation/navigation-drawer/components/NavTitle.tsx similarity index 100% rename from front/src/modules/ui/navigation/navbar/components/NavTitle.tsx rename to front/src/modules/ui/navigation/navigation-drawer/components/NavTitle.tsx diff --git a/front/src/modules/ui/navigation/navbar/components/NavWorkspaceButton.tsx b/front/src/modules/ui/navigation/navigation-drawer/components/NavWorkspaceButton.tsx similarity index 93% rename from front/src/modules/ui/navigation/navbar/components/NavWorkspaceButton.tsx rename to front/src/modules/ui/navigation/navigation-drawer/components/NavWorkspaceButton.tsx index ded7f00ac..3c3f9cc5c 100644 --- a/front/src/modules/ui/navigation/navbar/components/NavWorkspaceButton.tsx +++ b/front/src/modules/ui/navigation/navigation-drawer/components/NavWorkspaceButton.tsx @@ -2,6 +2,7 @@ import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; +import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { getImageAbsoluteURIOrBase64 } from '@/users/utils/getProfilePictureAbsoluteURI'; import NavCollapseButton from './NavCollapseButton'; @@ -53,7 +54,7 @@ const NavWorkspaceButton = ({ showCollapseButton, }: NavWorkspaceButtonProps) => { const currentWorkspace = useRecoilValue(currentWorkspaceState); - + const isMobile = useIsMobile(); const DEFAULT_LOGO = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACb0lEQVR4nO2VO4taQRTHr3AblbjxEVlwCwVhg7BoqqCIjy/gAyyFWNlYBOxsfH0KuxgQGwXRUkGuL2S7i1barGAgiwbdW93SnGOc4BonPiKahf3DwXFmuP/fPM4ZlvmlTxAhCBdzHnEQWYiv7Mr4C3NeuVYhQYDPzOUUQgDLBQGcLHNhvQK8DACPx8PTxiqVyvISG43GbyaT6Qfpn06n0m63e/tPAPF4vJ1MJu8kEsnWTCkWi1yr1RKGw+GDRqPBOTfr44vFQvD7/Q/lcpmaaVQAr9fLp1IpO22c47hGOBz+MB6PH+Vy+VYDAL8qlUoGtVotzOfzq4MAgsHgE/6KojiQyWR/bKVSqbSszHFM8Pl8z1YK48JsNltCOBwOnrYLO+8AAIjb+nHbycoTiUQfDJ7tFq4YAHiVSmXBxcD41u8flQU8z7fhzO0r83atVns3Go3u9Xr9x0O/RQXo9/tsIBBg6vX606a52Wz+bZ7P5/WwG29gxSJzhKgA6XTaDoFNF+krFAocmC//4yWEcSf2wTm7mCO19xFgSsKOLI16vV7b7XY7mRNoLwA0JymJ5uQIzgIAuX5PzDElT2m+E8BqtQ4ymcx7Yq7T6a6ZE4sKgOadTucaCwkxp1UzlEKh0GDxIXOwDWHAdi6Xe3swQDQa/Q7mywoolUpvsaptymazDWKxmBHTlWXZm405BFZoNpuGgwEmk4mE2SGtVivii4f1AO7J3ZopkQCQj7Ar1FeRChCJRJzVapX6DKNIfSc1Ax+wtQWQ55h6bH8FWDfYV4fO3wlwDr0C/BcADYiTPCxHqIEA2QsCZAkAKnRGkMbKN/sTX5YHPQ1e7SkAAAAASUVORK5CYII='; @@ -69,7 +70,9 @@ const NavWorkspaceButton = ({ > {currentWorkspace?.displayName ?? 'Twenty'} - + {!isMobile && ( + + )} ); }; diff --git a/front/src/modules/ui/navigation/navigation-drawer/components/NavbarAnimatedContainer.tsx b/front/src/modules/ui/navigation/navigation-drawer/components/NavbarAnimatedContainer.tsx new file mode 100644 index 000000000..52ebb3a54 --- /dev/null +++ b/front/src/modules/ui/navigation/navigation-drawer/components/NavbarAnimatedContainer.tsx @@ -0,0 +1,55 @@ +import { ReactNode } from 'react'; +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; +import { motion } from 'framer-motion'; +import { useRecoilValue } from 'recoil'; + +import { useIsSettingsPage } from '@/navigation/hooks/useIsSettingsPage'; +import { navigationDrawerState } from '@/ui/navigation/states/navigationDrawerState'; +import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; + +import { desktopNavDrawerWidths } from '../constants'; + +const StyledNavbarContainer = styled(motion.div)` + align-items: end; + display: flex; + flex-direction: column; + flex-shrink: 0; + overflow: hidden; +`; + +type NavbarAnimatedContainerProps = { + children: ReactNode; +}; + +export const NavbarAnimatedContainer = ({ + children, +}: NavbarAnimatedContainerProps) => { + const navigationDrawer = useRecoilValue(navigationDrawerState); + + const isInSubMenu = useIsSettingsPage(); + const theme = useTheme(); + const isMobile = useIsMobile(); + + const desktopWidth = + navigationDrawer === '' + ? 12 + : isInSubMenu + ? desktopNavDrawerWidths.submenu + : desktopNavDrawerWidths.menu; + + return ( + + {children} + + ); +}; diff --git a/front/src/modules/ui/navigation/navbar/components/SubMenuNavbar.tsx b/front/src/modules/ui/navigation/navigation-drawer/components/SubMenuNavbar.tsx similarity index 79% rename from front/src/modules/ui/navigation/navbar/components/SubMenuNavbar.tsx rename to front/src/modules/ui/navigation/navigation-drawer/components/SubMenuNavbar.tsx index d12bf9193..0868fa9a6 100644 --- a/front/src/modules/ui/navigation/navbar/components/SubMenuNavbar.tsx +++ b/front/src/modules/ui/navigation/navigation-drawer/components/SubMenuNavbar.tsx @@ -1,17 +1,19 @@ +import { ReactNode } from 'react'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { IconBrandGithub } from '@/ui/display/icon'; +import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import packageJson from '../../../../../../package.json'; -import { githubLink, leftNavbarWidth } from '../constants'; +import { desktopNavDrawerWidths, githubLink } from '../constants'; import NavBackButton from './NavBackButton'; import NavItemsContainer from './NavItemsContainer'; type SubMenuNavbarProps = { - children: React.ReactNode; + children: ReactNode; backButtonTitle: string; displayVersion?: boolean; }; @@ -25,10 +27,11 @@ const StyledVersionContainer = styled.div` const StyledVersion = styled.span` color: ${({ theme }) => theme.font.color.light}; + padding-left: ${({ theme }) => theme.spacing(1)}; + :hover { color: ${({ theme }) => theme.font.color.tertiary}; } - padding-left: ${({ theme }) => theme.spacing(1)}; `; const StyledVersionLink = styled.a` @@ -36,18 +39,25 @@ const StyledVersionLink = styled.a` color: ${({ theme }) => theme.font.color.light}; display: flex; text-decoration: none; + :hover { color: ${({ theme }) => theme.font.color.tertiary}; } `; const StyledContainer = styled.div` + box-sizing: border-box; display: flex; flex-direction: column; height: 100%; justify-content: space-between; - padding-top: ${({ theme }) => theme.spacing(9)}; - width: ${() => (useIsMobile() ? '100%' : leftNavbarWidth.desktop)}; + padding: ${({ theme }) => theme.spacing(2)}; + padding-top: ${({ theme }) => theme.spacing(11)}; + width: ${desktopNavDrawerWidths.menu}; + + @media (max-width: ${MOBILE_VIEWPORT}px) { + width: 100%; + } `; const SubMenuNavbar = ({ @@ -56,13 +66,14 @@ const SubMenuNavbar = ({ displayVersion, }: SubMenuNavbarProps) => { const version = packageJson.version; + const isMobile = useIsMobile(); const theme = useTheme(); return (
- + {!isMobile && } {children}
{displayVersion && ( diff --git a/front/src/modules/ui/navigation/navbar/components/SupportChat.tsx b/front/src/modules/ui/navigation/navigation-drawer/components/SupportChat.tsx similarity index 100% rename from front/src/modules/ui/navigation/navbar/components/SupportChat.tsx rename to front/src/modules/ui/navigation/navigation-drawer/components/SupportChat.tsx diff --git a/front/src/modules/ui/navigation/navbar/components/__stories__/MainNavbar.stories.tsx b/front/src/modules/ui/navigation/navigation-drawer/components/__stories__/MainNavbar.stories.tsx similarity index 96% rename from front/src/modules/ui/navigation/navbar/components/__stories__/MainNavbar.stories.tsx rename to front/src/modules/ui/navigation/navigation-drawer/components/__stories__/MainNavbar.stories.tsx index 42712aa6a..07390c9fe 100644 --- a/front/src/modules/ui/navigation/navbar/components/__stories__/MainNavbar.stories.tsx +++ b/front/src/modules/ui/navigation/navigation-drawer/components/__stories__/MainNavbar.stories.tsx @@ -18,7 +18,7 @@ import NavItem from '../NavItem'; import NavTitle from '../NavTitle'; const meta: Meta = { - title: 'UI/Navigation/Navbar/MainNavbar', + title: 'UI/Navigation/NavigationDrawer/MainNavbar', component: MainNavbar, decorators: [SnackBarDecorator], }; diff --git a/front/src/modules/ui/navigation/navbar/components/__stories__/NavCollapseButton.stories.tsx b/front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavCollapseButton.stories.tsx similarity index 89% rename from front/src/modules/ui/navigation/navbar/components/__stories__/NavCollapseButton.stories.tsx rename to front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavCollapseButton.stories.tsx index 996372eee..cab355aca 100644 --- a/front/src/modules/ui/navigation/navbar/components/__stories__/NavCollapseButton.stories.tsx +++ b/front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavCollapseButton.stories.tsx @@ -5,7 +5,7 @@ import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; import NavCollapseButton from '../NavCollapseButton'; const meta: Meta = { - title: 'UI/Navigation/Navbar/NavCollapseButton', + title: 'UI/Navigation/NavigationDrawer/NavCollapseButton', component: NavCollapseButton, }; diff --git a/front/src/modules/ui/navigation/navbar/components/__stories__/NavItem.stories.tsx b/front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavItem.stories.tsx similarity index 76% rename from front/src/modules/ui/navigation/navbar/components/__stories__/NavItem.stories.tsx rename to front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavItem.stories.tsx index 89933fe86..597e68289 100644 --- a/front/src/modules/ui/navigation/navbar/components/__stories__/NavItem.stories.tsx +++ b/front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavItem.stories.tsx @@ -9,8 +9,14 @@ import { CatalogStory } from '~/testing/types'; import NavItem from '../NavItem'; const meta: Meta = { - title: 'UI/Navigation/Navbar/NavItem', + title: 'UI/Navigation/NavigationDrawer/NavItem', component: NavItem, + args: { + label: 'Search', + Icon: IconSearch, + active: true, + }, + argTypes: { Icon: { control: false } }, }; const StyledNavItemContainer = styled.div` @@ -28,19 +34,11 @@ const ComponentDecorator: Decorator = (Story) => ( export default meta; type Story = StoryObj; -export const Default: Story = { - args: { - label: 'Search', - Icon: IconSearch, - onClick: () => console.log('clicked'), - active: true, - }, - argTypes: { Icon: { control: false }, onClick: { control: false } }, +export const Default: Story = { decorators: [ComponentDecorator, ComponentWithRouterDecorator], }; export const Catalog: CatalogStory = { - args: Default.args, decorators: [ ComponentDecorator, CatalogDecorator, @@ -75,21 +73,28 @@ export const Catalog: CatalogStory = { }, }; -export const Soon: Story = { +export const WithSoonPill: Story = { + ...Default, args: { - ...Default.args, active: false, soon: true, }, - argTypes: { Icon: { control: false }, onClick: { control: false } }, - decorators: [ComponentDecorator, ComponentWithRouterDecorator], }; -export const Count: Story = { +export const WithCount: Story = { + ...Default, args: { - ...Default.args, count: 3, }, - argTypes: { Icon: { control: false }, onClick: { control: false } }, - decorators: [ComponentDecorator, ComponentWithRouterDecorator], +}; + +export const WithKeyboardKeys: Story = { + ...Default, + args: { + className: "hover", + keyboard: ['⌘', 'K'], + }, + parameters: { + pseudo: { hover: [".hover"] }, + } }; diff --git a/front/src/modules/ui/navigation/navbar/components/__stories__/SubMenuNavbar.stories.tsx b/front/src/modules/ui/navigation/navigation-drawer/components/__stories__/SubMenuNavbar.stories.tsx similarity index 95% rename from front/src/modules/ui/navigation/navbar/components/__stories__/SubMenuNavbar.stories.tsx rename to front/src/modules/ui/navigation/navigation-drawer/components/__stories__/SubMenuNavbar.stories.tsx index e090f4a15..6da4375ab 100644 --- a/front/src/modules/ui/navigation/navbar/components/__stories__/SubMenuNavbar.stories.tsx +++ b/front/src/modules/ui/navigation/navigation-drawer/components/__stories__/SubMenuNavbar.stories.tsx @@ -14,7 +14,7 @@ import NavTitle from '../NavTitle'; import SubMenuNavbar from '../SubMenuNavbar'; const meta: Meta = { - title: 'UI/Navigation/Navbar/SubMenuNavbar', + title: 'UI/Navigation/NavigationDrawer/SubMenuNavbar', component: SubMenuNavbar, }; diff --git a/front/src/modules/ui/navigation/navigation-drawer/constants/index.ts b/front/src/modules/ui/navigation/navigation-drawer/constants/index.ts new file mode 100644 index 000000000..7c31a35ac --- /dev/null +++ b/front/src/modules/ui/navigation/navigation-drawer/constants/index.ts @@ -0,0 +1,6 @@ +export const desktopNavDrawerWidths = { + menu: '236px', + submenu: '536px', +}; + +export const githubLink = 'https://github.com/twentyhq/twenty'; diff --git a/front/src/modules/ui/navigation/states/navigationDrawerState.ts b/front/src/modules/ui/navigation/states/navigationDrawerState.ts new file mode 100644 index 000000000..bb3890f1a --- /dev/null +++ b/front/src/modules/ui/navigation/states/navigationDrawerState.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const navigationDrawerState = atom<'main' | 'settings' | ''>({ + key: 'ui/navigationDrawerState', + default: 'main', +}); diff --git a/front/src/modules/ui/navigation/states/navigationMemorizedUrlState.ts b/front/src/modules/ui/navigation/states/navigationMemorizedUrlState.ts new file mode 100644 index 000000000..6537c265a --- /dev/null +++ b/front/src/modules/ui/navigation/states/navigationMemorizedUrlState.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const navigationMemorizedUrlState = atom({ + key: 'navigationMemorizedUrlState', + default: '/', +}); diff --git a/front/src/modules/ui/object/record-table/components/RecordTableBody.tsx b/front/src/modules/ui/object/record-table/components/RecordTableBody.tsx index fbcbbc177..0c3c443af 100644 --- a/front/src/modules/ui/object/record-table/components/RecordTableBody.tsx +++ b/front/src/modules/ui/object/record-table/components/RecordTableBody.tsx @@ -6,16 +6,18 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural'; import { useObjectRecordTable } from '@/object-record/hooks/useObjectRecordTable'; import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState'; +import { + RecordTableRow, + StyledRow, +} from '@/ui/object/record-table/components/RecordTableRow'; +import { RowIdContext } from '@/ui/object/record-table/contexts/RowIdContext'; +import { RowIndexContext } from '@/ui/object/record-table/contexts/RowIndexContext'; +import { isFetchingRecordTableDataState } from '@/ui/object/record-table/states/isFetchingRecordTableDataState'; import { isDefined } from '~/utils/isDefined'; -import { RowIdContext } from '../contexts/RowIdContext'; -import { RowIndexContext } from '../contexts/RowIndexContext'; import { useRecordTable } from '../hooks/useRecordTable'; -import { isFetchingRecordTableDataState } from '../states/isFetchingRecordTableDataState'; import { tableRowIdsState } from '../states/tableRowIdsState'; -import { RecordTableRow, StyledRow } from './RecordTableRow'; - export const RecordTableBody = () => { const { ref: lastTableRowRef, inView: lastTableRowIsVisible } = useInView(); @@ -41,6 +43,7 @@ export const RecordTableBody = () => { isFetchingRecordTableDataState, ); + // Todo, move this to an effect to not trigger many re-renders const { fetchMoreRecords: fetchMoreObjects } = useObjectRecordTable(); useEffect(() => { diff --git a/front/src/utils/measureTotalFrameLoad.ts b/front/src/utils/measureTotalFrameLoad.ts deleted file mode 100644 index ee98812d5..000000000 --- a/front/src/utils/measureTotalFrameLoad.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* eslint-disable no-console */ -import afterFrame from 'afterframe'; - -export const measureTotalFrameLoad = (id: string) => { - const timerId = `Total loading time for : ${id}`; - - console.time(timerId); - - afterFrame(() => { - console.timeEnd(timerId); - }); -};