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 }}
+ >
+
+
+
+ )
+}
+
+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()
+ })
})