diff --git a/packages/client/src/pages/Home/Section/Header/index.js b/packages/client/src/pages/Home/Section/Header/index.js index e251d76b5b..c8ed17906d 100644 --- a/packages/client/src/pages/Home/Section/Header/index.js +++ b/packages/client/src/pages/Home/Section/Header/index.js @@ -40,7 +40,7 @@ import copy from "copy-to-clipboard"; import { useNavigate, useLocation } from "react-router-dom"; import Loaders from "@docspace/common/components/Loaders"; -import Navigation from "@docspace/common/components/Navigation"; +import Navigation from "@docspace/shared/components/Navigation"; import FilesFilter from "@docspace/shared/api/files/filter"; import { resendInvitesAgain } from "@docspace/shared/api/people"; diff --git a/packages/client/src/pages/Home/Section/Warning/index.js b/packages/client/src/pages/Home/Section/Warning/index.js index bba7167c15..0b7d7d6283 100644 --- a/packages/client/src/pages/Home/Section/Warning/index.js +++ b/packages/client/src/pages/Home/Section/Warning/index.js @@ -1,7 +1,7 @@ import React from "react"; import { useTranslation } from "react-i18next"; -import TrashWarning from "@docspace/common/components/Navigation/sub-components/trash-warning"; +import TrashWarning from "@docspace/shared/components/Navigation/sub-components/TrashWarning"; const Warning = () => { const { t } = useTranslation("Files"); diff --git a/packages/common/components/Navigation/StyledNavigation.js b/packages/common/components/Navigation/StyledNavigation.js deleted file mode 100644 index 4f4721fce8..0000000000 --- a/packages/common/components/Navigation/StyledNavigation.js +++ /dev/null @@ -1,202 +0,0 @@ -import styled, { css } from "styled-components"; - -import { tablet, mobile } from "@docspace/shared/utils"; - -const StyledContainer = styled.div` - ${(props) => - !props.isDropBoxComponent && - props.isDesktop && - css` - width: fit-content; - max-width: ${props.isInfoPanelVisible - ? `calc(100%)` - : `calc(100% - 72px)`}; - `} - - display: grid; - align-items: center; - - margin-right: ${(props) => (props.isTrashFolder ? "16px" : 0)}; - - grid-template-columns: ${({ isRootFolder, withLogo }) => - isRootFolder - ? withLogo - ? "1fr auto 1fr" - : "auto 1fr" - : withLogo - ? "1fr 49px auto 1fr" - : "49px auto 1fr"}; - - .navigation-logo { - display: flex; - height: 24px; - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-left: 16px; - ` - : css` - margin-right: 16px; - `} - - @media ${tablet} { - .logo-icon_svg { - display: none; - } - } - - .header_separator { - display: ${({ isRootFolder }) => (isRootFolder ? "block" : "none")}; - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - border-right: 1px solid #dfe2e3; - margin: 0 15px 0 0; - ` - : css` - border-left: 1px solid #dfe2e3; - margin: 0 0 0 15px; - `} - - height: 21px; - } - - .header-burger { - cursor: pointer; - display: none; - margin-top: -2px; - - img { - height: 28px; - width: 28px; - } - - @media ${tablet} { - display: flex; - } - - @media ${mobile} { - display: none; - } - } - } - - .drop-box-logo { - display: none; - - @media ${tablet} { - display: grid; - } - } - - height: 100%; - ${(props) => - props.isDesktopClient && - props.isDropBoxComponent && - css` - max-height: 32px; - `} - - .navigation-arrow-container { - display: flex; - } - - .arrow-button { - padding-top: 2px; - width: 17px; - min-width: 17px; - - svg { - ${({ theme }) => - theme.interfaceDirection === "rtl" && `transform: scaleX(-1);`} - } - } - - .title-container { - display: grid; - grid-template-columns: minmax(1px, max-content) auto; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - - .room-title { - cursor: pointer; - } - } - - .navigation-header-separator { - display: block; - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - padding-right: 16px; - border-left: ${(props) => - `1px solid ${props.theme.navigation.icon.stroke}`}; - ` - : css` - padding-left: 16px; - border-right: ${(props) => - `1px solid ${props.theme.navigation.icon.stroke}`}; - `} - - height: 21px; - @media ${mobile} { - display: none; - } - } - - .headline-heading { - display: flex; - height: 32px; - align-items: center; - } - - .title-block { - display: flex; - align-items: center; - flex-direction: row; - position: relative; - cursor: pointer; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - gap: 8px; - - .title-icon { - min-width: 17px; - min-height: 17px; - width: 17px; - height: 17px; - - svg { - path, - rect { - fill: ${({ theme }) => theme.navigation.publicIcon}; - } - } - } - } - - @media ${tablet} { - width: 100%; - grid-template-columns: ${({ isRootFolder, withLogo }) => - isRootFolder - ? withLogo - ? "59px 1fr auto" - : "1fr auto" - : withLogo - ? "43px 49px 1fr auto" - : "49px 1fr auto"}; - } - - @media ${mobile} { - .navigation-logo { - display: none; - } - - grid-template-columns: ${(props) => - props.isRootFolder ? "1fr auto" : "29px 1fr auto"}; - } -`; - -export default StyledContainer; diff --git a/packages/common/components/Navigation/index.js b/packages/common/components/Navigation/index.js deleted file mode 100644 index 6e316e173f..0000000000 --- a/packages/common/components/Navigation/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from "./Navigation"; diff --git a/packages/common/components/Navigation/sub-components/arrow-btn.js b/packages/common/components/Navigation/sub-components/arrow-btn.js deleted file mode 100644 index a92ede6c10..0000000000 --- a/packages/common/components/Navigation/sub-components/arrow-btn.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from "react"; -import { IconButton } from "@docspace/shared/components/icon-button"; - -import ArrowPathReactSvgUrl from "PUBLIC_DIR/images/arrow.path.react.svg?url"; - -const ArrowButton = ({ isRootFolder, onBackToParentFolder }) => { - return ( - <> - {!isRootFolder ? ( -
- -
-
- ) : ( - <> - )} - - ); -}; - -export default React.memo(ArrowButton); diff --git a/packages/common/components/Navigation/sub-components/control-btn.js b/packages/common/components/Navigation/sub-components/control-btn.js deleted file mode 100644 index eacbb1ff76..0000000000 --- a/packages/common/components/Navigation/sub-components/control-btn.js +++ /dev/null @@ -1,259 +0,0 @@ -import React from "react"; -import styled, { css } from "styled-components"; -import PropTypes from "prop-types"; - -import { tablet } from "@docspace/shared/utils"; -import { Base } from "@docspace/shared/themes"; -import ToggleInfoPanelButton from "./toggle-infopanel-btn"; -import PlusButton from "./plus-btn"; -import ContextButton from "./context-btn"; -import VerticalDotsReactSvgUrl from "PUBLIC_DIR/images/icons/17/vertical-dots.react.svg?url"; - -const StyledContainer = styled.div` - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-right: 16px; - ` - : css` - margin-left: 16px; - `} - display: flex; - align-items: center; - - height: 32px; - - .add-button { - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-left: 16px; - ` - : css` - margin-right: 16px; - `} - min-width: 15px; - - @media ${tablet} { - display: ${(props) => (props.isFrame ? "flex" : "none")}; - } - } - - .add-drop-down { - margin-top: 8px; - } - - .option-button { - min-width: 17px; - - /* ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-left: 16px; - ` - : css` - margin-right: 16px; - `} */ - - /* @media ${tablet} { - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-left: 9px; - ` - : css` - margin-right: 9px; - `} - } */ - } - - .trash-button { - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-left: 16px; - ` - : css` - margin-right: 16px; - `} - min-width: 15px; - } -`; - -const StyledInfoPanelToggleWrapper = styled.div` - display: flex; - align-items: center; - align-self: center; - justify-content: center; - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-right: auto; - ` - : css` - margin-left: auto; - `} - - @media ${tablet} { - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-right: ${(props) => (props.isRootFolder ? "auto" : "0")}; - ` - : css` - margin-left: ${(props) => (props.isRootFolder ? "auto" : "0")}; - `} - } - - .info-panel-toggle-bg { - height: 32px; - width: 32px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - background-color: ${(props) => - props.isInfoPanelVisible - ? props.theme.infoPanel.sectionHeaderToggleBgActive - : props.theme.infoPanel.sectionHeaderToggleBg}; - - path { - fill: ${(props) => - props.isInfoPanelVisible - ? props.theme.infoPanel.sectionHeaderToggleIconActive - : props.theme.infoPanel.sectionHeaderToggleIcon}; - } - } -`; -StyledInfoPanelToggleWrapper.defaultProps = { theme: Base }; - -const ControlButtons = ({ - personal, - isDropBoxComponent, - isRootFolder, - canCreate, - getContextOptionsFolder, - getContextOptionsPlus, - isEmptyFilesList, - clearTrash, - isInfoPanelVisible, - toggleInfoPanel, - toggleDropBox, - isDesktop, - titles, - withMenu, - onPlusClick, - isFrame, - isPublicRoom, - isTrashFolder, - isMobile, -}) => { - const toggleInfoPanelAction = () => { - toggleInfoPanel && toggleInfoPanel(); - toggleDropBox && toggleDropBox(); - }; - - return ( - - {!isRootFolder || (isTrashFolder && !isEmptyFilesList) ? ( - <> - {!isMobile && canCreate && ( - - )} - - {/* */} - - - - {!isDesktop && ( - - )} - - ) : canCreate ? ( - <> - {!isMobile && ( - - )} - {!isDesktop && ( - - )} - - ) : ( - <> - {!isDesktop && ( - - )} - - {isPublicRoom && ( - - )} - - )} - - ); -}; - -ControlButtons.propTypes = { - personal: PropTypes.bool, - isRootFolder: PropTypes.bool, - canCreate: PropTypes.bool, - getContextOptionsFolder: PropTypes.func, - getContextOptionsPlus: PropTypes.func, -}; - -export default React.memo(ControlButtons); diff --git a/packages/common/components/Navigation/sub-components/drop-box.js b/packages/common/components/Navigation/sub-components/drop-box.js deleted file mode 100644 index a0ab398327..0000000000 --- a/packages/common/components/Navigation/sub-components/drop-box.js +++ /dev/null @@ -1,246 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import styled, { css, useTheme } from "styled-components"; - -import { VariableSizeList } from "react-window"; -import { CustomScrollbarsVirtualList } from "@docspace/shared/components/scrollbar"; - -import ArrowButton from "./arrow-btn"; -import Text from "./text"; -import ControlButtons from "./control-btn"; -import Item from "./item"; -import StyledContainer from "../StyledNavigation"; -import NavigationLogo from "./logo-block"; - -import { tablet, mobile } from "@docspace/shared/utils"; -import { ReactSVG } from "react-svg"; - -import { Base } from "@docspace/shared/themes"; -import { DeviceType } from "@docspace/shared/enums"; - -const StyledBox = styled.div` - position: absolute; - top: 0px; - - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - right: -20px; - ${props.withLogo && `right: 207px;`}; - ` - : css` - left: -20px; - ${props.withLogo && `left: 207px;`}; - `} - padding: 0 20px; - padding-top: 18px; - - width: unset; - - height: ${(props) => (props.height ? `${props.height}px` : "fit-content")}; - max-height: calc(100vh - 48px); - - z-index: 401; - display: table; - margin: auto; - flex-direction: column; - - background: ${(props) => props.theme.navigation.background}; - - box-shadow: ${(props) => props.theme.navigation.boxShadow}; - border-radius: 0px 0px 6px 6px; - - .title-container { - display: grid; - grid-template-columns: minmax(1px, max-content) auto; - } - - @media ${tablet} { - width: ${({ dropBoxWidth }) => dropBoxWidth + "px"}; - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - right: -16px; - ` - : css` - left: -16px; - `} - padding: 0 16px; - padding-top: 14px; - } - - @media ${mobile} { - width: ${({ dropBoxWidth }) => dropBoxWidth + "px"}; - padding-top: 10px !important; - } -`; - -StyledBox.defaultProps = { theme: Base }; - -const Row = React.memo(({ data, index, style }) => { - const isRoot = index === data[0].length - 1; - return ( - - ); -}); - -const DropBox = React.forwardRef( - ( - { - sectionHeight, - showText, - dropBoxWidth, - isRootFolder, - onBackToParentFolder, - title, - personal, - canCreate, - navigationItems, - getContextOptionsFolder, - getContextOptionsPlus, - toggleDropBox, - toggleInfoPanel, - onClickAvailable, - isInfoPanelVisible, - maxHeight, - isOpen, - isDesktop, - isDesktopClient, - showRootFolderNavigation, - withLogo, - burgerLogo, - titleIcon, - currentDeviceType, - navigationTitleContainerNode, - }, - ref - ) => { - const [dropBoxHeight, setDropBoxHeight] = React.useState(0); - const countItems = navigationItems.length; - - const getItemSize = (index) => { - if (index === countItems - 1) return 51; - return currentDeviceType !== DeviceType.desktop ? 36 : 30; - }; - - const { interfaceDirection } = useTheme(); - React.useEffect(() => { - const itemsHeight = navigationItems.map((item, index) => - getItemSize(index) - ); - - const currentHeight = itemsHeight.reduce((a, b) => a + b); - - let navHeight = 41; - - if (currentDeviceType === DeviceType.tablet) { - navHeight = 49; - } - - if (currentDeviceType === DeviceType.mobile) { - navHeight = 45; - } - - setDropBoxHeight( - currentHeight + navHeight > sectionHeight - ? sectionHeight - navHeight - 20 - : currentHeight - ); - }, [sectionHeight, currentDeviceType]); - - const isTabletView = currentDeviceType === DeviceType.tablet; - - return ( - <> - - - {withLogo && ( - - )} - - - {navigationTitleContainerNode} - - - - - - {Row} - - - - ); - } -); - -DropBox.propTypes = { - width: PropTypes.number, - changeWidth: PropTypes.bool, - isRootFolder: PropTypes.bool, - onBackToParentFolder: PropTypes.func, - title: PropTypes.string, - personal: PropTypes.bool, - canCreate: PropTypes.bool, - navigationItems: PropTypes.arrayOf(PropTypes.object), - getContextOptionsFolder: PropTypes.func, - getContextOptionsPlus: PropTypes.func, - toggleDropBox: PropTypes.func, - onClickAvailable: PropTypes.func, -}; - -export default React.memo(DropBox); diff --git a/packages/common/components/Navigation/sub-components/item.js b/packages/common/components/Navigation/sub-components/item.js deleted file mode 100644 index 4a67f4ff90..0000000000 --- a/packages/common/components/Navigation/sub-components/item.js +++ /dev/null @@ -1,126 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import styled, { css } from "styled-components"; - -import { Text } from "@docspace/shared/components/text"; - -import DefaultIcon from "PUBLIC_DIR/images/default.react.svg"; -import RootIcon from "PUBLIC_DIR/images/root.react.svg"; -import DefaultTabletIcon from "PUBLIC_DIR/images/default.tablet.react.svg"; -import RootTabletIcon from "PUBLIC_DIR/images/root.tablet.react.svg"; - -import { tablet, mobile } from "@docspace/shared/utils"; -import { Base } from "@docspace/shared/themes"; - -import { ColorTheme, ThemeId } from "@docspace/shared/components/color-theme"; -import { DeviceType } from "@docspace/shared/enums"; - -const StyledItem = styled.div` - height: auto; - width: auto !important; - position: relative; - display: grid; - align-items: ${(props) => (props.isRoot ? "baseline" : "end")}; - grid-template-columns: 17px auto; - cursor: pointer; - - ${({ theme }) => - theme.interfaceDirection === "rtl" ? `margin-right: 0;` : `margin-left: 0;`} - - @media ${tablet} { - ${({ withLogo }) => - withLogo && - css` - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-right: 44px; - ` - : css` - margin-left: 44px; - `} - `}; - } - - @media ${mobile} { - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-right: 0; - ` - : css` - margin-left: 0; - `} - } -`; - -const StyledText = styled(Text)` - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-right: 10px; - ` - : css` - margin-left: 10px; - `} - position: relative; - bottom: ${(props) => (props.isRoot ? "2px" : "-1px")}; -`; - -const Item = ({ - id, - title, - isRoot, - isRootRoom, - onClick, - withLogo, - currentDeviceType, - ...rest -}) => { - const onClickAvailable = () => { - onClick && onClick(id, isRootRoom); - }; - - return ( - - - {currentDeviceType !== DeviceType.desktop ? ( - isRoot ? ( - - ) : ( - - ) - ) : isRoot ? ( - - ) : ( - - )} - - - - {title} - - - ); -}; - -Item.propTypes = { - id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - title: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - isRoot: PropTypes.bool, - onClick: PropTypes.func, -}; - -export default React.memo(Item); diff --git a/packages/common/components/Navigation/sub-components/logo-block.js b/packages/common/components/Navigation/sub-components/logo-block.js deleted file mode 100644 index 881b02e586..0000000000 --- a/packages/common/components/Navigation/sub-components/logo-block.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from "react"; - -const NavigationLogo = ({ logo, burgerLogo, ...rest }) => { - return ( -
- -
- -
-
-
- ); -}; - -export default NavigationLogo; diff --git a/packages/common/components/Navigation/sub-components/plus-btn.js b/packages/common/components/Navigation/sub-components/plus-btn.js deleted file mode 100644 index 333ead2558..0000000000 --- a/packages/common/components/Navigation/sub-components/plus-btn.js +++ /dev/null @@ -1,65 +0,0 @@ -import React, { useState, useRef } from "react"; -import PropTypes from "prop-types"; - -import PlusReactSvgUrl from "PUBLIC_DIR/images/icons/17/plus.svg?url"; - -import { IconButton } from "@docspace/shared/components/icon-button"; -import { ContextMenu } from "@docspace/shared/components/context-menu"; - -const PlusButton = (props) => { - const [isOpen, setIsOpen] = useState(false); - const ref = useRef(null); - const menuRef = useRef(null); - - const { className, getData, withMenu, onPlusClick, isFrame, ...rest } = props; - - const toggle = (e, isOpen) => { - isOpen ? menuRef.current.show(e) : menuRef.current.hide(e); - setIsOpen(isOpen); - }; - - const onClick = (e) => { - if (withMenu) toggle(e, !isOpen); - else onPlusClick && onPlusClick(); - }; - - const onHide = () => { - setIsOpen(false); - }; - - const model = getData(); - - return ( -
- - -
- ); -}; - -PlusButton.propTypes = { - className: PropTypes.string, - getData: PropTypes.func.isRequired, - onPlusClick: PropTypes.func, - id: PropTypes.string, -}; - -PlusButton.defaultProps = { - withMenu: true, -}; - -export default PlusButton; diff --git a/packages/common/components/Navigation/sub-components/text.js b/packages/common/components/Navigation/sub-components/text.js deleted file mode 100644 index 80236e2fca..0000000000 --- a/packages/common/components/Navigation/sub-components/text.js +++ /dev/null @@ -1,169 +0,0 @@ -import React from "react"; -import styled, { css } from "styled-components"; -import PropTypes from "prop-types"; - -import ExpanderDownIcon from "PUBLIC_DIR/images/expander-down.react.svg"; -import ArrowIcon from "PUBLIC_DIR/images/arrow.react.svg"; - -import { Heading } from "@docspace/shared/components/heading"; - -import { tablet, mobile, commonIconsStyles } from "@docspace/shared/utils"; - -import { Base } from "@docspace/shared/themes"; - -const StyledTextContainer = styled.div` - display: flex; - - align-items: center; - - flex-direction: row; - - position: relative; - - ${(props) => - !props.isRootFolder && !props.isRootFolderTitle && "cursor: pointer"}; - ${(props) => - props.isRootFolderTitle && - (props.theme.interfaceDirection === "rtl" - ? "padding-left: 3px;" - : "padding-right: 3px;")}; - - ${(props) => - !props.isRootFolderTitle && - css` - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - `}; -`; - -const StyledHeading = styled(Heading)` - font-weight: 700; - font-size: ${(props) => props.theme.getCorrectFontSize("18px")}; - line-height: 24px; - - margin: 0; - - ${(props) => - props.isRootFolderTitle && - `color: ${props.theme.navigation.rootFolderTitleColor}`}; - - @media ${tablet} { - font-size: ${(props) => props.theme.getCorrectFontSize("21px")}; - line-height: 28px; - } - - @media ${mobile} { - font-size: ${(props) => props.theme.getCorrectFontSize("18px")}; - line-height: 24px; - } -`; - -const StyledExpanderDownIcon = styled(ExpanderDownIcon)` - min-width: 8px !important; - width: 8px !important; - min-height: 18px !important; - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - padding: 0 4px 0 2px; - ` - : css` - padding: 0 2px 0 4px; - `} - path { - fill: ${(props) => props.theme.navigation.expanderColor}; - } - - ${commonIconsStyles}; -`; - -const StyledArrowIcon = styled(ArrowIcon)` - height: 12px; - min-width: 12px; - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - padding-right: 6px; - ` - : css` - padding-left: 6px; - `} - path { - fill: ${(props) => props.theme.navigation.rootFolderTitleColor}; - } -`; - -StyledExpanderDownIcon.defaultProps = { theme: Base }; - -const StyledExpanderDownIconRotate = styled(ExpanderDownIcon)` - min-width: 8px !important; - width: 8px !important; - min-height: 18px !important; - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - padding: 0 2px 0 4px; - ` - : css` - padding: 0 4px 0 2px; - `} - transform: rotate(-180deg); - - path { - fill: ${(props) => props.theme.navigation.expanderColor}; - } - - ${commonIconsStyles}; -`; - -StyledExpanderDownIconRotate.defaultProps = { theme: Base }; - -const Text = ({ - title, - isRootFolder, - isOpen, - isRootFolderTitle, - onClick, - ...rest -}) => { - return ( - - - {title} - - - {isRootFolderTitle && } - - {!isRootFolderTitle && !isRootFolder ? ( - isOpen ? ( - - ) : ( - - ) - ) : ( - <> - )} - - ); -}; - -Text.propTypes = { - title: PropTypes.string, - isOpen: PropTypes.bool, - isRootFolder: PropTypes.bool, - onCLick: PropTypes.func, -}; - -export default React.memo(Text); diff --git a/packages/common/components/Navigation/sub-components/toggle-infopanel-btn.js b/packages/common/components/Navigation/sub-components/toggle-infopanel-btn.js deleted file mode 100644 index 4ed854929f..0000000000 --- a/packages/common/components/Navigation/sub-components/toggle-infopanel-btn.js +++ /dev/null @@ -1,87 +0,0 @@ -import React from "react"; -import styled, { css } from "styled-components"; -import PanelReactSvgUrl from "PUBLIC_DIR/images/panel.react.svg?url"; -import { IconButton } from "@docspace/shared/components/icon-button"; -import { tablet } from "@docspace/shared/utils"; -import { Base } from "@docspace/shared/themes"; -import { ColorTheme, ThemeId } from "@docspace/shared/components/color-theme"; - -const StyledInfoPanelToggleColorThemeWrapper = styled(ColorTheme)` - align-self: center; - - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-right: auto; - transform: scaleX(-1); - ` - : css` - margin-left: auto; - `} - - margin-bottom: 1px; - padding: 0; - - .info-panel-toggle { - margin-inline-end: 8px; - } - - ${(props) => - props.isInfoPanelVisible && - css` - .info-panel-toggle-bg { - height: 30px; - width: 30px; - background: ${props.theme.backgroundAndSubstrateColor}; - border: 1px solid ${props.theme.backgroundAndSubstrateColor}; - border-radius: 50%; - .info-panel-toggle { - margin: auto; - margin-top: 25%; - } - } - `} - - @media ${tablet} { - display: none; - ${(props) => - props.theme.interfaceDirection === "rtl" - ? css` - margin-right: ${props.isRootFolder ? "auto" : "0"}; - ` - : css` - margin-left: ${props.isRootFolder ? "auto" : "0"}; - `} - } -`; -StyledInfoPanelToggleColorThemeWrapper.defaultProps = { theme: Base }; - -const ToggleInfoPanelButton = ({ - isRootFolder, - isInfoPanelVisible, - toggleInfoPanel, - id, - titles, -}) => { - return ( - -
- -
-
- ); -}; - -export default ToggleInfoPanelButton; diff --git a/packages/common/components/Navigation/sub-components/trash-warning.js b/packages/common/components/Navigation/sub-components/trash-warning.js deleted file mode 100644 index 51cf8a8de4..0000000000 --- a/packages/common/components/Navigation/sub-components/trash-warning.js +++ /dev/null @@ -1,35 +0,0 @@ -import React from "react"; -import styled, { css } from "styled-components"; -import { tablet } from "@docspace/shared/utils"; - -const StyledTrashWarning = styled.div` - box-sizing: border-box; - height: 32px; - padding: 8px 12px; - border-radius: 6px; - - display: flex; - align-items: center; - justify-content: ${({ theme }) => - theme.interfaceDirection === "rtl" ? `right` : `left`}; - - font-weight: 400; - font-size: ${(props) => props.theme.getCorrectFontSize("12px")}; - line-height: 16px; - - color: ${({ theme }) => theme.section.header.trashErasureLabelText}; - background: ${({ theme }) => - theme.section.header.trashErasureLabelBackground}; - - @media ${tablet} { - margin-bottom: 16px; - } -`; - -const TrashWarning = ({ title }) => { - return ( - {title} - ); -}; - -export default TrashWarning; diff --git a/packages/shared/components/context-menu/ContextMenu.types.ts b/packages/shared/components/context-menu/ContextMenu.types.ts index 345555ef17..d58b82c321 100644 --- a/packages/shared/components/context-menu/ContextMenu.types.ts +++ b/packages/shared/components/context-menu/ContextMenu.types.ts @@ -52,6 +52,8 @@ export type HeaderType = { export type ContextMenuModel = ContextMenuType | SeparatorType; +export type TGetContextMenuModel = () => ContextMenuModel[]; + export interface ContextMenuProps { /** Unique identifier of the element */ id?: string; @@ -98,7 +100,7 @@ export interface ContextMenuProps { /** Fills the icons with default colors */ fillIcon?: boolean; /** Function that returns an object containing the elements of the context menu */ - getContextModel?: () => ContextMenuModel[]; + getContextModel?: TGetContextMenuModel; /** Specifies the offset */ leftOffset?: number; rightOffset?: number; @@ -106,3 +108,8 @@ export interface ContextMenuProps { isArchive?: boolean; ref?: React.RefObject; } + +export type TContextMenuRef = { + show: (e: React.MouseEvent) => void; + hide: (e: React.MouseEvent) => {}; +}; diff --git a/packages/shared/components/context-menu/index.tsx b/packages/shared/components/context-menu/index.tsx index 112968a6fb..752e87b7ac 100644 --- a/packages/shared/components/context-menu/index.tsx +++ b/packages/shared/components/context-menu/index.tsx @@ -2,8 +2,16 @@ import { ContextMenuModel, ContextMenuType, SeparatorType, + TGetContextMenuModel, + TContextMenuRef, } from "./ContextMenu.types"; -export type { ContextMenuModel, ContextMenuType, SeparatorType }; +export type { + TContextMenuRef, + TGetContextMenuModel, + ContextMenuModel, + ContextMenuType, + SeparatorType, +}; export { ContextMenu } from "./ContextMenu"; diff --git a/packages/shared/components/navigation/Navigation.styled.ts b/packages/shared/components/navigation/Navigation.styled.ts new file mode 100644 index 0000000000..974c8fadfd --- /dev/null +++ b/packages/shared/components/navigation/Navigation.styled.ts @@ -0,0 +1,657 @@ +import styled, { css } from "styled-components"; + +import ExpanderDownIcon from "PUBLIC_DIR/images/expander-down.react.svg"; +import ArrowIcon from "PUBLIC_DIR/images/arrow.react.svg"; + +import { tablet, mobile, commonIconsStyles } from "../../utils"; +import { Base } from "../../themes"; + +import { ColorTheme } from "../color-theme"; +import { Heading } from "../heading"; +import { Text } from "../text"; + +const StyledContainer = styled.div<{ + isDropBoxComponent?: boolean; + isDesktop: boolean; + isInfoPanelVisible: boolean; + isTrashFolder?: boolean; + isRootFolder?: boolean; + withLogo: boolean; + isDesktopClient?: boolean; + width?: number; +}>` + ${(props) => + !props.isDropBoxComponent && + props.isDesktop && + css` + width: fit-content; + max-width: ${props.isInfoPanelVisible + ? `calc(100%)` + : `calc(100% - 72px)`}; + `} + + display: grid; + align-items: center; + + margin-right: ${(props) => (props.isTrashFolder ? "16px" : 0)}; + + grid-template-columns: ${({ isRootFolder, withLogo }) => + isRootFolder + ? withLogo + ? "1fr auto 1fr" + : "auto 1fr" + : withLogo + ? "1fr 49px auto 1fr" + : "49px auto 1fr"}; + + .navigation-logo { + display: flex; + height: 24px; + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-left: 16px; + ` + : css` + margin-right: 16px; + `} + + @media ${tablet} { + .logo-icon_svg { + display: none; + } + } + + .header_separator { + display: ${({ isRootFolder }) => (isRootFolder ? "block" : "none")}; + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + border-right: 1px solid #dfe2e3; + margin: 0 15px 0 0; + ` + : css` + border-left: 1px solid #dfe2e3; + margin: 0 0 0 15px; + `} + + height: 21px; + } + + .header-burger { + cursor: pointer; + display: none; + margin-top: -2px; + + img { + height: 28px; + width: 28px; + } + + @media ${tablet} { + display: flex; + } + + @media ${mobile} { + display: none; + } + } + } + + .drop-box-logo { + display: none; + + @media ${tablet} { + display: grid; + } + } + + height: 100%; + ${(props) => + props.isDesktopClient && + props.isDropBoxComponent && + css` + max-height: 32px; + `} + + .navigation-arrow-container { + display: flex; + } + + .arrow-button { + padding-top: 2px; + width: 17px; + min-width: 17px; + + svg { + ${({ theme }) => + theme.interfaceDirection === "rtl" && `transform: scaleX(-1);`} + } + } + + .title-container { + display: grid; + grid-template-columns: minmax(1px, max-content) auto; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + .room-title { + cursor: pointer; + } + } + + .navigation-header-separator { + display: block; + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + padding-right: 16px; + border-left: ${`1px solid ${props.theme.navigation.icon.stroke}`}; + ` + : css` + padding-left: 16px; + border-right: ${`1px solid ${props.theme.navigation.icon.stroke}`}; + `} + + height: 21px; + @media ${mobile} { + display: none; + } + } + + .headline-heading { + display: flex; + height: 32px; + align-items: center; + } + + .title-block { + display: flex; + align-items: center; + flex-direction: row; + position: relative; + cursor: pointer; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + gap: 8px; + + .title-icon { + min-width: 17px; + min-height: 17px; + width: 17px; + height: 17px; + + svg { + path, + rect { + fill: ${({ theme }) => theme.navigation.publicIcon}; + } + } + } + } + + @media ${tablet} { + width: 100%; + grid-template-columns: ${({ isRootFolder, withLogo }) => + isRootFolder + ? withLogo + ? "59px 1fr auto" + : "1fr auto" + : withLogo + ? "43px 49px 1fr auto" + : "49px 1fr auto"}; + } + + @media ${mobile} { + .navigation-logo { + display: none; + } + + grid-template-columns: ${(props) => + props.isRootFolder ? "1fr auto" : "29px 1fr auto"}; + } +`; + +const StyledInfoPanelToggleColorThemeWrapper = styled(ColorTheme)<{ + isInfoPanelVisible?: boolean; + isRootFolder?: boolean; +}>` + align-self: center; + + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-right: auto; + transform: scaleX(-1); + ` + : css` + margin-left: auto; + `} + + margin-bottom: 1px; + padding: 0; + + .info-panel-toggle { + margin-inline-end: 8px; + } + + ${(props) => + props.isInfoPanelVisible && + css` + .info-panel-toggle-bg { + height: 30px; + width: 30px; + background: ${props.theme.backgroundAndSubstrateColor}; + border: 1px solid ${props.theme.backgroundAndSubstrateColor}; + border-radius: 50%; + .info-panel-toggle { + margin: auto; + margin-top: 25%; + } + } + `} + + @media ${tablet} { + display: none; + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-right: ${props.isRootFolder ? "auto" : "0"}; + ` + : css` + margin-left: ${props.isRootFolder ? "auto" : "0"}; + `} + } +`; +StyledInfoPanelToggleColorThemeWrapper.defaultProps = { theme: Base }; + +const StyledControlButtonContainer = styled.div<{ isFrame?: boolean }>` + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-right: 16px; + ` + : css` + margin-left: 16px; + `} + display: flex; + align-items: center; + + height: 32px; + + .add-button { + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-left: 16px; + ` + : css` + margin-right: 16px; + `} + min-width: 15px; + + @media ${tablet} { + display: ${(props) => (props.isFrame ? "flex" : "none")}; + } + } + + .add-drop-down { + margin-top: 8px; + } + + .option-button { + min-width: 17px; + + /* ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-left: 16px; + ` + : css` + margin-right: 16px; + `} */ + + /* @media ${tablet} { + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-left: 9px; + ` + : css` + margin-right: 9px; + `} + } */ + } + + .trash-button { + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-left: 16px; + ` + : css` + margin-right: 16px; + `} + min-width: 15px; + } +`; + +const StyledInfoPanelToggleWrapper = styled.div<{ + isRootFolder: boolean; + isInfoPanelVisible: boolean; +}>` + display: flex; + align-items: center; + align-self: center; + justify-content: center; + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-right: auto; + ` + : css` + margin-left: auto; + `} + + @media ${tablet} { + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-right: ${props.isRootFolder ? "auto" : "0"}; + ` + : css` + margin-left: ${props.isRootFolder ? "auto" : "0"}; + `} + } + + .info-panel-toggle-bg { + height: 32px; + width: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + background-color: ${(props) => + props.isInfoPanelVisible + ? props.theme.infoPanel.sectionHeaderToggleBgActive + : props.theme.infoPanel.sectionHeaderToggleBg}; + + path { + fill: ${(props) => + props.isInfoPanelVisible + ? props.theme.infoPanel.sectionHeaderToggleIconActive + : props.theme.infoPanel.sectionHeaderToggleIcon}; + } + } +`; +StyledInfoPanelToggleWrapper.defaultProps = { theme: Base }; + +const StyledTrashWarning = styled.div` + box-sizing: border-box; + height: 32px; + padding: 8px 12px; + border-radius: 6px; + + display: flex; + align-items: center; + justify-content: ${({ theme }) => + theme.interfaceDirection === "rtl" ? `right` : `left`}; + + font-weight: 400; + font-size: ${(props) => props.theme.getCorrectFontSize("12px")}; + line-height: 16px; + + color: ${({ theme }) => theme.section.header.trashErasureLabelText}; + background: ${({ theme }) => + theme.section.header.trashErasureLabelBackground}; + + @media ${tablet} { + margin-bottom: 16px; + } +`; + +StyledTrashWarning.defaultProps = { theme: Base }; + +const StyledTextContainer = styled.div<{ + isRootFolder: boolean; + isRootFolderTitle: boolean; +}>` + display: flex; + + align-items: center; + + flex-direction: row; + + position: relative; + + ${(props) => + !props.isRootFolder && !props.isRootFolderTitle && "cursor: pointer"}; + ${(props) => + props.isRootFolderTitle && + (props.theme.interfaceDirection === "rtl" + ? "padding-left: 3px;" + : "padding-right: 3px;")}; + + ${(props) => + !props.isRootFolderTitle && + css` + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + `}; +`; + +const StyledHeading = styled(Heading)<{ isRootFolderTitle: boolean }>` + font-weight: 700; + font-size: ${(props) => props.theme.getCorrectFontSize("18px")}; + line-height: 24px; + + margin: 0; + + ${(props) => + props.isRootFolderTitle && + `color: ${props.theme.navigation.rootFolderTitleColor}`}; + + @media ${tablet} { + font-size: ${(props) => props.theme.getCorrectFontSize("21px")}; + line-height: 28px; + } + + @media ${mobile} { + font-size: ${(props) => props.theme.getCorrectFontSize("18px")}; + line-height: 24px; + } +`; + +const StyledExpanderDownIcon = styled(ExpanderDownIcon)` + min-width: 8px !important; + width: 8px !important; + min-height: 18px !important; + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + padding: 0 4px 0 2px; + ` + : css` + padding: 0 2px 0 4px; + `} + path { + fill: ${(props) => props.theme.navigation.expanderColor}; + } + + ${commonIconsStyles}; +`; + +const StyledArrowIcon = styled(ArrowIcon)` + height: 12px; + min-width: 12px; + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + padding-right: 6px; + ` + : css` + padding-left: 6px; + `} + path { + fill: ${(props) => props.theme.navigation.rootFolderTitleColor}; + } +`; + +StyledExpanderDownIcon.defaultProps = { theme: Base }; + +const StyledExpanderDownIconRotate = styled(ExpanderDownIcon)` + min-width: 8px !important; + width: 8px !important; + min-height: 18px !important; + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + padding: 0 2px 0 4px; + ` + : css` + padding: 0 4px 0 2px; + `} + transform: rotate(-180deg); + + path { + fill: ${(props) => props.theme.navigation.expanderColor}; + } + + ${commonIconsStyles}; +`; + +StyledExpanderDownIconRotate.defaultProps = { theme: Base }; + +const StyledItem = styled.div<{ isRoot: boolean; withLogo: boolean }>` + height: auto; + width: auto !important; + position: relative; + display: grid; + align-items: ${(props) => (props.isRoot ? "baseline" : "end")}; + grid-template-columns: 17px auto; + cursor: pointer; + + ${({ theme }) => + theme.interfaceDirection === "rtl" ? `margin-right: 0;` : `margin-left: 0;`} + + @media ${tablet} { + ${({ withLogo }) => + withLogo && + css` + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-right: 44px; + ` + : css` + margin-left: 44px; + `} + `}; + } + + @media ${mobile} { + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-right: 0; + ` + : css` + margin-left: 0; + `} + } +`; + +const StyledText = styled(Text)<{ isRoot: boolean }>` + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-right: 10px; + ` + : css` + margin-left: 10px; + `} + position: relative; + bottom: ${(props) => (props.isRoot ? "2px" : "-1px")}; +`; + +const StyledBox = styled.div<{ + withLogo: boolean; + height: number | null; + dropBoxWidth: number; +}>` + position: absolute; + top: 0px; + + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + right: -20px; + ${props.withLogo && `right: 207px;`}; + ` + : css` + left: -20px; + ${props.withLogo && `left: 207px;`}; + `} + padding: 0 20px; + padding-top: 18px; + + width: unset; + + height: ${(props) => (props.height ? `${props.height}px` : "fit-content")}; + max-height: calc(100vh - 48px); + + z-index: 401; + display: table; + margin: auto; + flex-direction: column; + + background: ${(props) => props.theme.navigation.background}; + + box-shadow: ${(props) => props.theme.navigation.boxShadow}; + border-radius: 0px 0px 6px 6px; + + .title-container { + display: grid; + grid-template-columns: minmax(1px, max-content) auto; + } + + @media ${tablet} { + width: ${({ dropBoxWidth }) => `${dropBoxWidth}px`}; + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + right: -16px; + ` + : css` + left: -16px; + `} + padding: 0 16px; + padding-top: 14px; + } + + @media ${mobile} { + width: ${({ dropBoxWidth }) => `${dropBoxWidth}px`}; + padding-top: 10px !important; + } +`; + +StyledBox.defaultProps = { theme: Base }; + +export { + StyledContainer, + StyledInfoPanelToggleColorThemeWrapper, + StyledControlButtonContainer, + StyledInfoPanelToggleWrapper, + StyledTrashWarning, + StyledTextContainer, + StyledArrowIcon, + StyledExpanderDownIcon, + StyledExpanderDownIconRotate, + StyledHeading, + StyledText, + StyledItem, + StyledBox, +}; diff --git a/packages/common/components/Navigation/Navigation.js b/packages/shared/components/navigation/Navigation.tsx similarity index 66% rename from packages/common/components/Navigation/Navigation.js rename to packages/shared/components/navigation/Navigation.tsx index d603ab02ba..8a867599c6 100644 --- a/packages/common/components/Navigation/Navigation.js +++ b/packages/shared/components/navigation/Navigation.tsx @@ -1,22 +1,21 @@ -import React from "react"; -import PropTypes from "prop-types"; - -import StyledContainer from "./StyledNavigation"; -import ArrowButton from "./sub-components/arrow-btn"; -import Text from "./sub-components/text"; -import ControlButtons from "./sub-components/control-btn"; -import DropBox from "./sub-components/drop-box"; - -import { Consumer, DomHelpers } from "@docspace/shared/utils"; - -import { Backdrop } from "@docspace/shared/components/backdrop"; - +import React, { useCallback } from "react"; import { ReactSVG } from "react-svg"; -import ToggleInfoPanelButton from "./sub-components/toggle-infopanel-btn"; -import TrashWarning from "./sub-components/trash-warning"; -import NavigationLogo from "./sub-components/logo-block"; -import { DeviceType } from "@docspace/shared/enums"; +import { Consumer, DomHelpers } from "../../utils"; +import { DeviceType } from "../../enums"; + +import { Backdrop } from "../backdrop"; + +import ArrowButton from "./sub-components/ArrowBtn"; +import Text from "./sub-components/Text"; +import ControlButtons from "./sub-components/ControlBtn"; +import ToggleInfoPanelButton from "./sub-components/ToggleInfoPanelBtn"; +import TrashWarning from "./sub-components/TrashWarning"; +import NavigationLogo from "./sub-components/LogoBlock"; +import DropBox from "./sub-components/DropBox"; + +import { StyledContainer } from "./Navigation.styled"; +import { INavigationProps } from "./Navigation.types"; const Navigation = ({ tReady, @@ -25,7 +24,7 @@ const Navigation = ({ title, canCreate, isTabletView, - personal, + onClickFolder, navigationItems, getContextOptionsPlus, @@ -55,61 +54,64 @@ const Navigation = ({ rootRoomTitle, ...rest -}) => { +}: INavigationProps) => { const [isOpen, setIsOpen] = React.useState(false); const [firstClick, setFirstClick] = React.useState(true); const [dropBoxWidth, setDropBoxWidth] = React.useState(0); - const [maxHeight, setMaxHeight] = React.useState(false); + // const [maxHeight, setMaxHeight] = React.useState(""); - const dropBoxRef = React.useRef(null); - const containerRef = React.useRef(null); + const dropBoxRef = React.useRef(null); + const containerRef = React.useRef(null); const isDesktop = currentDeviceType === DeviceType.desktop; const infoPanelIsVisible = React.useMemo( () => isDesktop && (!isEmptyPage || (isEmptyPage && isRoom)), - [isDesktop, isEmptyPage, isRoom] + [isDesktop, isEmptyPage, isRoom], ); - const onMissClick = React.useCallback( - (e) => { - e.preventDefault; - const path = e.path || (e.composedPath && e.composedPath()); - - if (!firstClick) { - !path.includes(dropBoxRef.current) ? toggleDropBox() : null; - } else { - setFirstClick((prev) => !prev); - } - }, - [firstClick, toggleDropBox, setFirstClick] - ); - - const onClickAvailable = React.useCallback( - (id, isRootRoom) => { - onClickFolder && onClickFolder(id, isRootRoom); - toggleDropBox(); - }, - [onClickFolder, toggleDropBox] - ); - - const toggleDropBox = () => { + const toggleDropBox = useCallback(() => { if (navigationItems.length === 0) return; if (isRootFolder) return setIsOpen(false); setIsOpen((prev) => !prev); - setDropBoxWidth(DomHelpers.getOuterWidth(containerRef.current)); + if (containerRef.current) + setDropBoxWidth(DomHelpers.getOuterWidth(containerRef.current)); - const { top } = DomHelpers.getOffset(containerRef.current); + // const { top } = DomHelpers.getOffset(containerRef.current); - setMaxHeight(`calc(100vh - ${top}px)`); + // setMaxHeight(`calc(100vh - ${top}px)`); setFirstClick(true); - }; + }, [isRootFolder, navigationItems?.length]); + + const onMissClick = React.useCallback( + (e: MouseEvent) => { + e.preventDefault(); + const path = e.composedPath && e.composedPath(); + + if (!firstClick) { + if (dropBoxRef.current && !path.includes(dropBoxRef.current)) + toggleDropBox(); + } else { + setFirstClick((prev) => !prev); + } + }, + [firstClick, toggleDropBox, setFirstClick], + ); + + const onClickAvailable = React.useCallback( + (id: number | string, isRootRoom: boolean) => { + onClickFolder?.(id, isRootRoom); + toggleDropBox(); + }, + [onClickFolder, toggleDropBox], + ); const onResize = React.useCallback(() => { - setDropBoxWidth(DomHelpers.getOuterWidth(containerRef.current)); - }, [containerRef.current]); + if (containerRef.current) + setDropBoxWidth(DomHelpers.getOuterWidth(containerRef.current)); + }, []); React.useEffect(() => { if (isOpen) { @@ -129,7 +131,7 @@ const Navigation = ({ const onBackToParentFolderAction = React.useCallback(() => { setIsOpen((val) => !val); - onBackToParentFolder && onBackToParentFolder(); + onBackToParentFolder?.(); }, [onBackToParentFolder]); const showRootFolderNavigation = @@ -146,10 +148,16 @@ const Navigation = ({ isOpen={isOpen} isRootFolder={isRootFolder} onClick={toggleDropBox} + isRootFolderTitle={false} />
); + const onTextClick = React.useCallback(() => { + onClickFolder(navigationItems[navigationItems.length - 2].id, false); + setIsOpen(false); + }, [navigationItems, onClickFolder]); + const navigationTitleContainerNode = showRootFolderNavigation ? (
{ - { - onClickFolder( - navigationItems[navigationItems.length - 2].id, - false - ); - setIsOpen(false); - } - }} + onClick={onTextClick} /> {navigationTitleNode}
@@ -185,7 +185,7 @@ const Navigation = ({ @@ -193,14 +193,10 @@ const Navigation = ({ {...rest} isDesktop={isDesktop} ref={dropBoxRef} - maxHeight={maxHeight} dropBoxWidth={dropBoxWidth} - sectionHeight={context.sectionHeight} - showText={showText} + sectionHeight={context.sectionHeight || 0} isRootFolder={isRootFolder} onBackToParentFolder={onBackToParentFolderAction} - title={title} - personal={personal} canCreate={canCreate} navigationItems={navigationItems} getContextOptionsFolder={getContextOptionsFolder} @@ -210,10 +206,8 @@ const Navigation = ({ isInfoPanelVisible={isInfoPanelVisible} onClickAvailable={onClickAvailable} isDesktopClient={isDesktopClient} - showRootFolderNavigation={showRootFolderNavigation} withLogo={withLogo} burgerLogo={burgerLogo} - titleIcon={titleIcon} currentDeviceType={currentDeviceType} navigationTitleContainerNode={navigationTitleContainerNode} /> @@ -221,10 +215,8 @@ const Navigation = ({ )} )} @@ -247,13 +238,11 @@ const Navigation = ({ {navigationTitleContainerNode} {isDesktop && isTrashFolder && !isEmptyPage && ( - + )} {infoPanelIsVisible && !hideInfoPanel && ( void; + +export type TTitles = { + infoPanel?: string; + actions?: string; + contextMenu?: string; + trashWarning?: string; +}; + +export interface IArrowButtonProps { + isRootFolder: boolean; + onBackToParentFolder: TOnBackToParenFolder; +} + +export interface IContextButtonProps { + className: string; + getData: TGetContextMenuModel; + withMenu?: boolean; + isTrashFolder?: boolean; + isMobile: boolean; + id: string; + title?: string; +} + +export interface IPlusButtonProps { + className: string; + getData: TGetContextMenuModel; + withMenu?: boolean; + id?: string; + title?: string; + onPlusClick?: () => void; + isFrame?: boolean; +} + +export interface IToggleInfoPanelButtonProps { + isRootFolder: boolean; + isInfoPanelVisible: boolean; + toggleInfoPanel: (e: React.MouseEvent) => void; + id?: string; + titles?: TTitles; +} + +export interface IControlButtonProps { + isRootFolder: boolean; + canCreate: boolean; + getContextOptionsFolder: TGetContextMenuModel; + getContextOptionsPlus: TGetContextMenuModel; + isEmptyFilesList?: boolean; + isInfoPanelVisible: boolean; + toggleInfoPanel: () => void; + toggleDropBox?: () => void; + isDesktop: boolean; + titles?: TTitles; + withMenu?: boolean; + onPlusClick?: () => void; + isFrame?: boolean; + isPublicRoom?: boolean; + isTrashFolder?: boolean; + isMobile?: boolean; +} + +export interface ITextProps { + title: string; + isOpen: boolean; + isRootFolder: boolean; + isRootFolderTitle: boolean; + onClick: () => void; + className?: string; +} + +export interface INavigationLogoProps { + logo?: string; + burgerLogo: string; + className: string; +} + +export type TOnNavigationItemClick = ( + id: string | number, + isRootRoom: boolean, +) => void; + +export interface INavigationItemProps { + id: string | number; + title: string; + isRoot: boolean; + isRootRoom: boolean; + onClick: TOnNavigationItemClick; + withLogo: boolean; + currentDeviceType: DeviceType; + style?: React.CSSProperties; +} + +export type TNavigationItem = { + id: string | number; + title: string; + isRootRoom: boolean; +}; + +export type TRowData = [ + TNavigationItem[], + TOnNavigationItemClick, + { withLogo: boolean; currentDeviceType: DeviceType }, +]; + +export interface IDropBoxProps { + sectionHeight: number; + dropBoxWidth: number; + isRootFolder: boolean; + onBackToParentFolder: TOnBackToParenFolder; + canCreate: boolean; + navigationItems: TNavigationItem[]; + getContextOptionsFolder: TGetContextMenuModel; + getContextOptionsPlus: TGetContextMenuModel; + toggleInfoPanel: () => void; + toggleDropBox: () => void; + onClickAvailable: TOnNavigationItemClick; + isInfoPanelVisible: boolean; + isDesktop: boolean; + isDesktopClient: boolean; + withLogo: boolean; + burgerLogo: string; + currentDeviceType: DeviceType; + navigationTitleContainerNode: React.ReactNode; +} + +export interface INavigationProps { + tReady: boolean; + showText: boolean; + isRootFolder: boolean; + title: string; + canCreate: boolean; + isTabletView: boolean; + onClickFolder: TOnNavigationItemClick; + navigationItems: TNavigationItem[]; + onBackToParentFolder: TOnBackToParenFolder; + getContextOptionsFolder: TGetContextMenuModel; + getContextOptionsPlus: TGetContextMenuModel; + isTrashFolder: boolean; + isEmptyFilesList: boolean; + clearTrash: () => void; + showFolderInfo: () => void; + isCurrentFolderInfo: boolean; + toggleInfoPanel: () => void; + isInfoPanelVisible: boolean; + titles: TTitles; + withMenu: boolean; + onPlusClick: () => void; + isEmptyPage: boolean; + isDesktop: boolean; + isRoom: boolean; + isFrame: boolean; + hideInfoPanel: () => void; + withLogo: boolean; + burgerLogo: string; + showRootFolderTitle: boolean; + isPublicRoom: boolean; + titleIcon: string; + currentDeviceType: DeviceType; + rootRoomTitle: string; +} diff --git a/packages/shared/components/navigation/index.ts b/packages/shared/components/navigation/index.ts new file mode 100644 index 0000000000..76067c7e0a --- /dev/null +++ b/packages/shared/components/navigation/index.ts @@ -0,0 +1,7 @@ +import Navigation from "./Navigation"; + +import { TTitles, TNavigationItem } from "./Navigation.types"; + +export type { TTitles, TNavigationItem }; + +export default Navigation; diff --git a/packages/shared/components/navigation/sub-components/ArrowBtn.tsx b/packages/shared/components/navigation/sub-components/ArrowBtn.tsx new file mode 100644 index 0000000000..44cd8d3393 --- /dev/null +++ b/packages/shared/components/navigation/sub-components/ArrowBtn.tsx @@ -0,0 +1,27 @@ +import React from "react"; + +import ArrowPathReactSvgUrl from "PUBLIC_DIR/images/arrow.path.react.svg?url"; + +import { IconButton } from "../../icon-button"; + +import { IArrowButtonProps } from "../Navigation.types"; + +const ArrowButton = ({ + isRootFolder, + onBackToParentFolder, +}: IArrowButtonProps) => { + return !isRootFolder ? ( +
+ +
+
+ ) : null; +}; + +export default React.memo(ArrowButton); diff --git a/packages/common/components/Navigation/sub-components/context-btn.js b/packages/shared/components/navigation/sub-components/ContextBtn.tsx similarity index 52% rename from packages/common/components/Navigation/sub-components/context-btn.js rename to packages/shared/components/navigation/sub-components/ContextBtn.tsx index ceab91c17f..156f6e3a7e 100644 --- a/packages/common/components/Navigation/sub-components/context-btn.js +++ b/packages/shared/components/navigation/sub-components/ContextBtn.tsx @@ -1,23 +1,36 @@ import React, { useState, useRef } from "react"; -import PropTypes from "prop-types"; + import VerticalDotsReactSvgUrl from "PUBLIC_DIR/images/icons/17/vertical-dots.react.svg?url"; -import { IconButton } from "@docspace/shared/components/icon-button"; -import { ContextMenu } from "@docspace/shared/components/context-menu"; -const ContextButton = (props) => { +import { IconButton } from "../../icon-button"; +import { ContextMenu, TContextMenuRef } from "../../context-menu"; + +import { IContextButtonProps } from "../Navigation.types"; + +const ContextButton = ({ + className, + getData, + withMenu = true, + isTrashFolder, + isMobile, + id, + ...rest +}: IContextButtonProps) => { const [isOpen, setIsOpen] = useState(false); - const ref = useRef(null); - const menuRef = useRef(null); + const ref = useRef(null); + const menuRef = useRef(null); - const { className, getData, withMenu, isTrashFolder, isMobile, ...rest } = - props; + const toggle = (e: React.MouseEvent, open: boolean) => { + if (open) { + menuRef.current?.show(e); + } else { + menuRef.current?.hide(e); + } - const toggle = (e, isOpen) => { - isOpen ? menuRef.current.show(e) : menuRef.current.hide(e); - setIsOpen(isOpen); + setIsOpen(open); }; - const onClick = (e) => { + const onClick = (e: React.MouseEvent) => { if (withMenu) toggle(e, !isOpen); }; @@ -32,7 +45,7 @@ const ContextButton = (props) => { @@ -48,14 +61,4 @@ const ContextButton = (props) => { ); }; -ContextButton.propTypes = { - className: PropTypes.string, - getData: PropTypes.func.isRequired, - id: PropTypes.string, -}; - -ContextButton.defaultProps = { - withMenu: true, -}; - export default ContextButton; diff --git a/packages/shared/components/navigation/sub-components/ControlBtn.tsx b/packages/shared/components/navigation/sub-components/ControlBtn.tsx new file mode 100644 index 0000000000..616eb3a848 --- /dev/null +++ b/packages/shared/components/navigation/sub-components/ControlBtn.tsx @@ -0,0 +1,131 @@ +import React from "react"; + +import { StyledControlButtonContainer } from "../Navigation.styled"; +import { IControlButtonProps } from "../Navigation.types"; + +import ToggleInfoPanelButton from "./ToggleInfoPanelBtn"; +import PlusButton from "./PlusBtn"; +import ContextButton from "./ContextBtn"; + +const ControlButtons = ({ + isRootFolder, + canCreate, + getContextOptionsFolder, + getContextOptionsPlus, + isEmptyFilesList, + isInfoPanelVisible, + toggleInfoPanel, + toggleDropBox, + isDesktop, + titles, + withMenu, + onPlusClick, + isFrame, + isPublicRoom, + isTrashFolder, + isMobile, +}: IControlButtonProps) => { + const toggleInfoPanelAction = () => { + toggleInfoPanel?.(); + toggleDropBox?.(); + }; + + return ( + + {!isRootFolder || (isTrashFolder && !isEmptyFilesList) ? ( + <> + {!isMobile && canCreate && ( + + )} + + {/* */} + + + + {!isDesktop && ( + + )} + + ) : canCreate ? ( + <> + {!isMobile && ( + + )} + {!isDesktop && ( + + )} + + ) : ( + <> + {!isDesktop && ( + + )} + + {isPublicRoom && ( + + )} + + )} + + ); +}; + +export default React.memo(ControlButtons); diff --git a/packages/shared/components/navigation/sub-components/DropBox.tsx b/packages/shared/components/navigation/sub-components/DropBox.tsx new file mode 100644 index 0000000000..cffd8944a3 --- /dev/null +++ b/packages/shared/components/navigation/sub-components/DropBox.tsx @@ -0,0 +1,146 @@ +import React, { useCallback } from "react"; +import { useTheme } from "styled-components"; +import { Direction, VariableSizeList } from "react-window"; + +import { DeviceType } from "../../../enums"; + +import { CustomScrollbarsVirtualList } from "../../scrollbar"; + +import { StyledBox, StyledContainer } from "../Navigation.styled"; +import { IDropBoxProps } from "../Navigation.types"; + +import NavigationLogo from "./LogoBlock"; +import ArrowButton from "./ArrowBtn"; +import ControlButtons from "./ControlBtn"; +import Row from "./Row"; + +const DropBox = React.forwardRef( + ( + { + sectionHeight, + + dropBoxWidth, + isRootFolder, + onBackToParentFolder, + + canCreate, + navigationItems, + getContextOptionsFolder, + getContextOptionsPlus, + toggleDropBox, + toggleInfoPanel, + onClickAvailable, + isInfoPanelVisible, + + isDesktop, + isDesktopClient, + + withLogo, + burgerLogo, + + currentDeviceType, + navigationTitleContainerNode, + }, + ref, + ) => { + const [dropBoxHeight, setDropBoxHeight] = React.useState(0); + const countItems = navigationItems.length; + + const getItemSize = useCallback( + (index: number): number => { + if (index === countItems - 1) return 51; + return currentDeviceType !== DeviceType.desktop ? 36 : 30; + }, + [countItems, currentDeviceType], + ); + + const { interfaceDirection } = useTheme(); + React.useEffect(() => { + const itemsHeight = navigationItems.map((item, index) => + getItemSize(index), + ); + + const currentHeight = itemsHeight.reduce((a, b) => a + b); + + let navHeight = 41; + + if (currentDeviceType === DeviceType.tablet) { + navHeight = 49; + } + + if (currentDeviceType === DeviceType.mobile) { + navHeight = 45; + } + + setDropBoxHeight( + currentHeight + navHeight > sectionHeight + ? sectionHeight - navHeight - 20 + : currentHeight, + ); + }, [sectionHeight, currentDeviceType, navigationItems, getItemSize]); + + const isTabletView = currentDeviceType === DeviceType.tablet; + + return ( + + + {withLogo && ( + + )} + + + {navigationTitleContainerNode} + + + + + + {Row} + + + ); + }, +); + +DropBox.displayName = "DropBox"; + +export default React.memo(DropBox); diff --git a/packages/shared/components/navigation/sub-components/Item.tsx b/packages/shared/components/navigation/sub-components/Item.tsx new file mode 100644 index 0000000000..4e2995bcb6 --- /dev/null +++ b/packages/shared/components/navigation/sub-components/Item.tsx @@ -0,0 +1,63 @@ +import React from "react"; + +import DefaultIcon from "PUBLIC_DIR/images/default.react.svg"; +import RootIcon from "PUBLIC_DIR/images/root.react.svg"; +import DefaultTabletIcon from "PUBLIC_DIR/images/default.tablet.react.svg"; +import RootTabletIcon from "PUBLIC_DIR/images/root.tablet.react.svg"; + +import { DeviceType } from "../../../enums"; + +import { ColorTheme, ThemeId } from "../../color-theme"; +import { StyledItem, StyledText } from "../Navigation.styled"; +import { INavigationItemProps } from "../Navigation.types"; + +const Item = ({ + id, + title, + isRoot, + isRootRoom, + onClick, + withLogo, + currentDeviceType, + ...rest +}: INavigationItemProps) => { + const onClickAvailable = () => { + onClick?.(id, isRootRoom); + }; + + return ( + + + {currentDeviceType !== DeviceType.desktop ? ( + isRoot ? ( + + ) : ( + + ) + ) : isRoot ? ( + + ) : ( + + )} + + + + {title} + + + ); +}; + +export default React.memo(Item); diff --git a/packages/shared/components/navigation/sub-components/LogoBlock.tsx b/packages/shared/components/navigation/sub-components/LogoBlock.tsx new file mode 100644 index 0000000000..954ff4f3fb --- /dev/null +++ b/packages/shared/components/navigation/sub-components/LogoBlock.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import { INavigationLogoProps } from "../Navigation.types"; + +const NavigationLogo = ({ + logo, + burgerLogo, + ...rest +}: INavigationLogoProps) => { + return ( +
+ logo +
+ burger logo +
+
+
+ ); +}; + +export default NavigationLogo; diff --git a/packages/shared/components/navigation/sub-components/PlusBtn.tsx b/packages/shared/components/navigation/sub-components/PlusBtn.tsx new file mode 100644 index 0000000000..1708cf6453 --- /dev/null +++ b/packages/shared/components/navigation/sub-components/PlusBtn.tsx @@ -0,0 +1,65 @@ +import React, { useState, useRef } from "react"; + +import PlusReactSvgUrl from "PUBLIC_DIR/images/icons/17/plus.svg?url"; + +import { IconButton } from "../../icon-button"; +import { ContextMenu, TContextMenuRef } from "../../context-menu"; +import { IPlusButtonProps } from "../Navigation.types"; + +const PlusButton = ({ + className, + getData, + withMenu = true, + onPlusClick, + isFrame, + id, + ...rest +}: IPlusButtonProps) => { + const [isOpen, setIsOpen] = useState(false); + const ref = useRef(null); + const menuRef = useRef(null); + + const toggle = (e: React.MouseEvent, open: boolean) => { + if (open) { + menuRef.current?.show(e); + } else { + menuRef.current?.hide(e); + } + + setIsOpen(open); + }; + + const onClick = (e: React.MouseEvent) => { + if (withMenu) toggle(e, !isOpen); + else onPlusClick?.(); + }; + + const onHide = () => { + setIsOpen(false); + }; + + const model = getData(); + + return ( +
+ + +
+ ); +}; + +export default PlusButton; diff --git a/packages/shared/components/navigation/sub-components/Row.tsx b/packages/shared/components/navigation/sub-components/Row.tsx new file mode 100644 index 0000000000..c477832d69 --- /dev/null +++ b/packages/shared/components/navigation/sub-components/Row.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import Item from "./Item"; + +import { TRowData } from "../Navigation.types"; + +const Row = React.memo( + ({ + data, + index, + style, + }: { + data: TRowData; + index: number; + style: React.CSSProperties; + }) => { + const isRoot = index === data[0].length - 1; + return ( + + ); + }, +); + +Row.displayName = "Row"; + +export default Row; diff --git a/packages/shared/components/navigation/sub-components/Text.tsx b/packages/shared/components/navigation/sub-components/Text.tsx new file mode 100644 index 0000000000..3b75b0267f --- /dev/null +++ b/packages/shared/components/navigation/sub-components/Text.tsx @@ -0,0 +1,49 @@ +import React from "react"; + +import { + StyledArrowIcon, + StyledExpanderDownIcon, + StyledExpanderDownIconRotate, + StyledHeading, + StyledTextContainer, +} from "../Navigation.styled"; + +import { ITextProps } from "../Navigation.types"; + +const Text = ({ + title, + isRootFolder, + isOpen, + isRootFolderTitle, + onClick, + ...rest +}: ITextProps) => { + return ( + + + {title} + + + {isRootFolderTitle && } + + {!isRootFolderTitle && !isRootFolder ? ( + isOpen ? ( + + ) : ( + + ) + ) : null} + + ); +}; + +export default React.memo(Text); diff --git a/packages/shared/components/navigation/sub-components/ToggleInfoPanelBtn.tsx b/packages/shared/components/navigation/sub-components/ToggleInfoPanelBtn.tsx new file mode 100644 index 0000000000..7737ade880 --- /dev/null +++ b/packages/shared/components/navigation/sub-components/ToggleInfoPanelBtn.tsx @@ -0,0 +1,38 @@ +import React from "react"; + +import PanelReactSvgUrl from "PUBLIC_DIR/images/panel.react.svg?url"; + +import { IconButton } from "../../icon-button"; +import { ThemeId } from "../../color-theme"; +import { IToggleInfoPanelButtonProps } from "../Navigation.types"; +import { StyledInfoPanelToggleColorThemeWrapper } from "../Navigation.styled"; + +const ToggleInfoPanelButton = ({ + isRootFolder, + isInfoPanelVisible, + toggleInfoPanel, + id, + titles, +}: IToggleInfoPanelButtonProps) => { + return ( + +
+ +
+
+ ); +}; + +export default ToggleInfoPanelButton; diff --git a/packages/shared/components/navigation/sub-components/TrashWarning.tsx b/packages/shared/components/navigation/sub-components/TrashWarning.tsx new file mode 100644 index 0000000000..4e2226f458 --- /dev/null +++ b/packages/shared/components/navigation/sub-components/TrashWarning.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import { StyledTrashWarning } from "../Navigation.styled"; + +const TrashWarning = ({ title }: { title?: string }) => { + return ( + {title} + ); +}; + +export default TrashWarning; diff --git a/packages/shared/utils/context.ts b/packages/shared/utils/context.ts index 17ae0faba7..5b7d8dee39 100644 --- a/packages/shared/utils/context.ts +++ b/packages/shared/utils/context.ts @@ -1,6 +1,6 @@ import React from "react"; -const defaultValue = {}; +const defaultValue: { sectionWidth?: number; sectionHeight?: number } = {}; export const Context = React.createContext(defaultValue);