diff --git a/src/drive/web/modules/viewer/Footer/BottomSheet.jsx b/src/drive/web/modules/viewer/Footer/BottomSheet.jsx new file mode 100644 index 0000000000..dba3dc142c --- /dev/null +++ b/src/drive/web/modules/viewer/Footer/BottomSheet.jsx @@ -0,0 +1,110 @@ +import React, { useState, useEffect, useRef } from 'react' +import { BottomSheet } from 'mui-bottom-sheet' +import { makeStyles } from '@material-ui/core/styles' + +const useStyles = ({ isTopPosition }) => ({ + root: { + borderTopLeftRadius: '1rem', + borderTopRightRadius: '1rem', + transition: 'border-radius 0.5s', + boxShadow: '0 6px 16px 0 rgba(0, 0, 0, 0.5)', + ...(isTopPosition && { + borderTopLeftRadius: 0, + borderTopRightRadius: 0 + }) + } +}) + +const useClasses = makeStyles(theme => ({ + header: { + height: '2.5rem', + width: '100%', + position: 'relative', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }, + indicator: { + width: '4rem', + height: '0.25rem', + borderRadius: '99px', + backgroundColor: theme.palette.divider + } +})) + +const BottomSheetWrapper = ({ file, actionButtonsRef, children }) => { + const [isTopPosition, setIsTopPosition] = useState(false) + const [peekHeights, setPeekHeights] = useState(null) + const [initPos, setInitPos] = useState(null) + const [bottomSpacerHeight, setBottomSpacerHeight] = useState(0) + const classes = useClasses() + const styles = useStyles({ isTopPosition }) + const innerContentRef = useRef() + const headerRef = useRef() + + const toolbar = document.getElementById('viewer-toolbar') + + useEffect( + () => { + const maxHeight = toolbar + ? window.innerHeight - toolbar.offsetHeight + : window.innerHeight + const mediumHeight = maxHeight * 0.33 + const actionButtonsHeight = parseFloat( + getComputedStyle(actionButtonsRef.current).getPropertyValue('height') + ) + // this is the margin of action buttons without bottomSheet + const actionButtonsBottomMargin = 7 + const minHeight = + headerRef.current.offsetHeight + + actionButtonsHeight + + actionButtonsBottomMargin + + // Used so that the bottomSheet can be opened to the top, + // without stopping at the content height + const bottomSpacerHeight = + maxHeight - innerContentRef.current.offsetHeight + + setPeekHeights([minHeight, mediumHeight, maxHeight]) + setInitPos(mediumHeight) + setBottomSpacerHeight(bottomSpacerHeight) + }, + [toolbar, innerContentRef, file, actionButtonsRef] + ) + + const handleOnIndexChange = snapIndex => { + const maxHeightSnapIndex = peekHeights.length - 1 + + if (snapIndex === maxHeightSnapIndex && !isTopPosition) { + setIsTopPosition(true) + } + if (snapIndex !== maxHeightSnapIndex && isTopPosition) { + setIsTopPosition(false) + } + } + + return ( + handleOnIndexChange(snapIndex)} + styles={{ root: styles.root }} + > +
+
+
+
+ {children} +
+
+ + ) +} + +export default BottomSheetWrapper diff --git a/src/drive/web/modules/viewer/Footer/BottomSheetContent.jsx b/src/drive/web/modules/viewer/Footer/BottomSheetContent.jsx new file mode 100644 index 0000000000..c908b01c8d --- /dev/null +++ b/src/drive/web/modules/viewer/Footer/BottomSheetContent.jsx @@ -0,0 +1,55 @@ +import React, { forwardRef } from 'react' +import PropTypes from 'prop-types' +import cx from 'classnames' +import { makeStyles } from '@material-ui/core/styles' + +import { isMobileApp } from 'cozy-device-helper' +import Paper from 'cozy-ui/transpiled/react/Paper' +import Typography from 'cozy-ui/transpiled/react/Typography' +import Stack from 'cozy-ui/transpiled/react/Stack' + +import Sharing from './Sharing' +import ForwardButton from './ForwardButton' +import DownloadButton from './DownloadButton' +import getPanelBlocks, { panelBlocksSpecs } from '../Panel/getPanelBlocks' + +const useStyles = makeStyles(theme => ({ + stack: { + backgroundColor: theme.palette.background.default + } +})) + +const BottomSheetContent = forwardRef(({ file }, ref) => { + const panelBlocks = getPanelBlocks({ panelBlocksSpecs, file }) + const FileActionButton = isMobileApp() ? ForwardButton : DownloadButton + const styles = useStyles() + + return ( + + + + + + {panelBlocks.map((PanelBlock, index) => ( + + + + + + ))} + + ) +}) + +BottomSheetContent.propTypes = { + file: PropTypes.object.isRequired +} + +export default BottomSheetContent diff --git a/src/drive/web/modules/viewer/Footer/FooterContent.jsx b/src/drive/web/modules/viewer/Footer/FooterContent.jsx index a69830c818..b2a6291cff 100644 --- a/src/drive/web/modules/viewer/Footer/FooterContent.jsx +++ b/src/drive/web/modules/viewer/Footer/FooterContent.jsx @@ -1,20 +1,45 @@ -import React from 'react' +import React, { useRef } from 'react' import PropTypes from 'prop-types' +import { makeStyles } from '@material-ui/core/styles' import { isMobileApp } from 'cozy-device-helper' +import { showPanel } from '../helpers' import Sharing from './Sharing' import ForwardButton from './ForwardButton' import DownloadButton from './DownloadButton' +import BottomSheet from './BottomSheet' +import BottomSheetContent from './BottomSheetContent' + +const useStyles = makeStyles({ + footer: { + display: 'flex', + alignItems: 'center', + width: 'calc(100% - 2rem)', + height: '100%', + paddingLeft: '1rem', + paddingRight: '1rem', + borderTop: '1px solid var(--dividerColor)' + } +}) const FooterContent = ({ file }) => { + const styles = useStyles() const FileActionButton = isMobileApp() ? ForwardButton : DownloadButton + const actionButtonsRef = useRef() + + if (showPanel({ file })) + return ( + + + + ) return ( - <> +
- +
) } diff --git a/src/drive/web/modules/viewer/Footer/FooterContent.spec.jsx b/src/drive/web/modules/viewer/Footer/FooterContent.spec.jsx index a41ceae3ea..f1b8656830 100644 --- a/src/drive/web/modules/viewer/Footer/FooterContent.spec.jsx +++ b/src/drive/web/modules/viewer/Footer/FooterContent.spec.jsx @@ -13,7 +13,7 @@ jest.mock('cozy-device-helper', () => ({ isMobileApp: jest.fn() })) -const file = { +const staticFile = { id: 'fileId', name: 'Demo.pdf' } @@ -23,7 +23,8 @@ const client = createMockClient({}) const setup = ({ byDocId = { fileId: {} }, isOwner = false, - isMobileAppValue = false + isMobileAppValue = false, + file } = {}) => { const mockSharingContext = { byDocId, @@ -35,7 +36,7 @@ const setup = ({ const root = render( - + ) @@ -86,4 +87,13 @@ describe('FooterContent', () => { expect(getByText('Shared')) expect(queryByText('Share')).toBeFalsy() }) + + it('should show bottom sheet for file with certification or konnector', () => { + const { root } = setup({ + file: { metadata: { carbonCopy: true }, ...staticFile } + }) + const { getByTestId } = root + + expect(getByTestId('bottomSheet-header')).toBeTruthy() + }) })