cozy-ui 111.21.0 → 112.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/package.json +4 -3
- package/react/FileImageLoader/Readme.md +66 -3
- package/react/FileImageLoader/index.jsx +3 -3
- package/react/FileImageLoader/index.spec.jsx +1 -1
- package/react/hooks/useClientErrors.jsx +140 -0
- package/react/hooks/useClientErrors.spec.jsx +102 -0
- package/react/index.js +0 -1
- package/transpiled/react/FileImageLoader/index.js +3 -3
- package/transpiled/react/hooks/useClientErrors.js +167 -0
- package/transpiled/react/index.js +0 -1
- package/transpiled/react/stylesheet.css +1 -1
- package/react/Viewer/Footer/BottomSheetContent.jsx +0 -29
- package/react/Viewer/Footer/DownloadButton.jsx +0 -67
- package/react/Viewer/Footer/FooterActionButtons.jsx +0 -22
- package/react/Viewer/Footer/FooterActionButtons.spec.jsx +0 -30
- package/react/Viewer/Footer/FooterContent.jsx +0 -99
- package/react/Viewer/Footer/ForwardButton.jsx +0 -95
- package/react/Viewer/Footer/ForwardButton.spec.jsx +0 -87
- package/react/Viewer/Footer/ForwardOrDownloadButton.jsx +0 -24
- package/react/Viewer/Footer/Sharing.jsx +0 -60
- package/react/Viewer/Footer/helpers.js +0 -107
- package/react/Viewer/Footer/helpers.spec.js +0 -77
- package/react/Viewer/NoViewer/DownloadButton.jsx +0 -28
- package/react/Viewer/NoViewer/FileIcon.jsx +0 -46
- package/react/Viewer/NoViewer/NoViewer.jsx +0 -29
- package/react/Viewer/NoViewer/NoViewer.spec.jsx +0 -44
- package/react/Viewer/NoViewer/__snapshots__/NoViewer.spec.jsx.snap +0 -82
- package/react/Viewer/NoViewer/index.jsx +0 -1
- package/react/Viewer/Panel/ActionMenuDesktop.jsx +0 -66
- package/react/Viewer/Panel/ActionMenuMobile.jsx +0 -74
- package/react/Viewer/Panel/ActionMenuWrapper.jsx +0 -104
- package/react/Viewer/Panel/Certifications.jsx +0 -62
- package/react/Viewer/Panel/PanelContent.jsx +0 -49
- package/react/Viewer/Panel/Qualification.jsx +0 -114
- package/react/Viewer/Panel/QualificationListItemContact.jsx +0 -85
- package/react/Viewer/Panel/QualificationListItemDate.jsx +0 -77
- package/react/Viewer/Panel/QualificationListItemInformation.jsx +0 -68
- package/react/Viewer/Panel/QualificationListItemInformation.spec.jsx +0 -73
- package/react/Viewer/Panel/QualificationListItemOther.jsx +0 -61
- package/react/Viewer/Panel/QualificationListItemText.jsx +0 -30
- package/react/Viewer/Panel/getPanelBlocks.jsx +0 -56
- package/react/Viewer/Panel/getPanelBlocks.spec.jsx +0 -79
- package/react/Viewer/Panel/styles.styl +0 -13
- package/react/Viewer/Readme.md +0 -352
- package/react/Viewer/Viewer.jsx +0 -134
- package/react/Viewer/ViewerContainer.jsx +0 -169
- package/react/Viewer/ViewerExposer.js +0 -3
- package/react/Viewer/ViewerInformationsWrapper.jsx +0 -69
- package/react/Viewer/ViewerInformationsWrapper.spec.jsx +0 -63
- package/react/Viewer/ViewerWithCustomPanelAndFooter.jsx +0 -55
- package/react/Viewer/ViewersByFile/AudioViewer.jsx +0 -21
- package/react/Viewer/ViewersByFile/AudioViewer.spec.jsx +0 -39
- package/react/Viewer/ViewersByFile/BlankPaperViewer.jsx +0 -46
- package/react/Viewer/ViewersByFile/ImageViewer.jsx +0 -330
- package/react/Viewer/ViewersByFile/ImageViewer.spec.jsx +0 -70
- package/react/Viewer/ViewersByFile/NoNetworkViewer.jsx +0 -17
- package/react/Viewer/ViewersByFile/OnlyOfficeViewer.jsx +0 -28
- package/react/Viewer/ViewersByFile/PdfJsViewer.jsx +0 -210
- package/react/Viewer/ViewersByFile/PdfJsViewer.spec.jsx +0 -160
- package/react/Viewer/ViewersByFile/PdfMobileViewer.jsx +0 -106
- package/react/Viewer/ViewersByFile/PdfMobileViewer.spec.jsx +0 -76
- package/react/Viewer/ViewersByFile/ShortcutViewer.jsx +0 -38
- package/react/Viewer/ViewersByFile/ShortcutViewer.spec.jsx +0 -32
- package/react/Viewer/ViewersByFile/TextViewer.jsx +0 -126
- package/react/Viewer/ViewersByFile/TextViewer.spec.jsx +0 -118
- package/react/Viewer/ViewersByFile/VideoViewer.jsx +0 -13
- package/react/Viewer/ViewersByFile/VideoViewer.spec.jsx +0 -39
- package/react/Viewer/ViewersByFile/__snapshots__/AudioViewer.spec.jsx.snap +0 -43
- package/react/Viewer/ViewersByFile/__snapshots__/ShortcutViewer.spec.jsx.snap +0 -57
- package/react/Viewer/ViewersByFile/__snapshots__/TextViewer.spec.jsx.snap +0 -100
- package/react/Viewer/ViewersByFile/__snapshots__/VideoViewer.spec.jsx.snap +0 -19
- package/react/Viewer/ViewersByFile/styles.styl +0 -87
- package/react/Viewer/assets/IlluGenericNewPage.svg +0 -10
- package/react/Viewer/components/ExpirationAlert.jsx +0 -86
- package/react/Viewer/components/ExpirationAnnotation.jsx +0 -40
- package/react/Viewer/components/Footer.jsx +0 -13
- package/react/Viewer/components/InformationPanel.jsx +0 -26
- package/react/Viewer/components/Navigation.jsx +0 -39
- package/react/Viewer/components/PdfToolbarButton.jsx +0 -26
- package/react/Viewer/components/PrintButton.jsx +0 -90
- package/react/Viewer/components/Toolbar.jsx +0 -111
- package/react/Viewer/components/ToolbarButtons.jsx +0 -11
- package/react/Viewer/components/ToolbarFilePath.jsx +0 -61
- package/react/Viewer/components/ViewerByFile.jsx +0 -112
- package/react/Viewer/components/ViewerByFile.spec.jsx +0 -100
- package/react/Viewer/components/ViewerControls.jsx +0 -190
- package/react/Viewer/components/ViewerControls.spec.jsx +0 -54
- package/react/Viewer/components/ViewerSpinner.jsx +0 -17
- package/react/Viewer/components/styles.styl +0 -93
- package/react/Viewer/helpers.js +0 -131
- package/react/Viewer/helpers.spec.js +0 -136
- package/react/Viewer/hoc/withFileUrl.jsx +0 -93
- package/react/Viewer/hoc/withViewerLocales.jsx +0 -4
- package/react/Viewer/hooks/useReferencedContactName.jsx +0 -26
- package/react/Viewer/index.jsx +0 -12
- package/react/Viewer/locales/en.json +0 -66
- package/react/Viewer/locales/fr.json +0 -66
- package/react/Viewer/locales/index.js +0 -4
- package/react/Viewer/proptypes.js +0 -12
- package/react/Viewer/providers/ActionMenuProvider.jsx +0 -35
- package/react/Viewer/queries.js +0 -20
- package/react/Viewer/styles.styl +0 -22
- package/react/Viewer/vars.styl +0 -6
- package/transpiled/react/Viewer/Footer/BottomSheetContent.js +0 -28
- package/transpiled/react/Viewer/Footer/DownloadButton.js +0 -91
- package/transpiled/react/Viewer/Footer/FooterActionButtons.js +0 -21
- package/transpiled/react/Viewer/Footer/FooterContent.js +0 -98
- package/transpiled/react/Viewer/Footer/ForwardButton.js +0 -143
- package/transpiled/react/Viewer/Footer/ForwardOrDownloadButton.js +0 -25
- package/transpiled/react/Viewer/Footer/Sharing.js +0 -57
- package/transpiled/react/Viewer/Footer/helpers.js +0 -151
- package/transpiled/react/Viewer/NoViewer/DownloadButton.js +0 -34
- package/transpiled/react/Viewer/NoViewer/FileIcon.js +0 -57
- package/transpiled/react/Viewer/NoViewer/NoViewer.js +0 -49
- package/transpiled/react/Viewer/NoViewer/index.js +0 -1
- package/transpiled/react/Viewer/Panel/ActionMenuDesktop.js +0 -68
- package/transpiled/react/Viewer/Panel/ActionMenuMobile.js +0 -70
- package/transpiled/react/Viewer/Panel/ActionMenuWrapper.js +0 -129
- package/transpiled/react/Viewer/Panel/Certifications.js +0 -56
- package/transpiled/react/Viewer/Panel/PanelContent.js +0 -48
- package/transpiled/react/Viewer/Panel/Qualification.js +0 -119
- package/transpiled/react/Viewer/Panel/QualificationListItemContact.js +0 -96
- package/transpiled/react/Viewer/Panel/QualificationListItemDate.js +0 -64
- package/transpiled/react/Viewer/Panel/QualificationListItemInformation.js +0 -59
- package/transpiled/react/Viewer/Panel/QualificationListItemOther.js +0 -53
- package/transpiled/react/Viewer/Panel/QualificationListItemText.js +0 -29
- package/transpiled/react/Viewer/Panel/getPanelBlocks.js +0 -62
- package/transpiled/react/Viewer/Viewer.js +0 -172
- package/transpiled/react/Viewer/ViewerContainer.js +0 -189
- package/transpiled/react/Viewer/ViewerExposer.js +0 -2
- package/transpiled/react/Viewer/ViewerInformationsWrapper.js +0 -49
- package/transpiled/react/Viewer/ViewerWithCustomPanelAndFooter.js +0 -56
- package/transpiled/react/Viewer/ViewersByFile/AudioViewer.js +0 -41
- package/transpiled/react/Viewer/ViewersByFile/BlankPaperViewer.js +0 -74
- package/transpiled/react/Viewer/ViewersByFile/ImageViewer.js +0 -367
- package/transpiled/react/Viewer/ViewersByFile/NoNetworkViewer.js +0 -38
- package/transpiled/react/Viewer/ViewersByFile/OnlyOfficeViewer.js +0 -29
- package/transpiled/react/Viewer/ViewersByFile/PdfJsViewer.js +0 -254
- package/transpiled/react/Viewer/ViewersByFile/PdfMobileViewer.js +0 -153
- package/transpiled/react/Viewer/ViewersByFile/ShortcutViewer.js +0 -42
- package/transpiled/react/Viewer/ViewersByFile/TextViewer.js +0 -219
- package/transpiled/react/Viewer/ViewersByFile/VideoViewer.js +0 -33
- package/transpiled/react/Viewer/assets/IlluGenericNewPage.svg +0 -10
- package/transpiled/react/Viewer/components/ExpirationAlert.js +0 -100
- package/transpiled/react/Viewer/components/ExpirationAnnotation.js +0 -41
- package/transpiled/react/Viewer/components/Footer.js +0 -29
- package/transpiled/react/Viewer/components/InformationPanel.js +0 -23
- package/transpiled/react/Viewer/components/Navigation.js +0 -47
- package/transpiled/react/Viewer/components/PdfToolbarButton.js +0 -28
- package/transpiled/react/Viewer/components/PrintButton.js +0 -137
- package/transpiled/react/Viewer/components/Toolbar.js +0 -115
- package/transpiled/react/Viewer/components/ToolbarButtons.js +0 -9
- package/transpiled/react/Viewer/components/ToolbarFilePath.js +0 -71
- package/transpiled/react/Viewer/components/ViewerByFile.js +0 -105
- package/transpiled/react/Viewer/components/ViewerControls.js +0 -226
- package/transpiled/react/Viewer/components/ViewerSpinner.js +0 -17
- package/transpiled/react/Viewer/helpers.js +0 -147
- package/transpiled/react/Viewer/hoc/withFileUrl.js +0 -207
- package/transpiled/react/Viewer/hoc/withViewerLocales.js +0 -3
- package/transpiled/react/Viewer/hooks/useReferencedContactName.js +0 -32
- package/transpiled/react/Viewer/index.js +0 -11
- package/transpiled/react/Viewer/locales/index.js +0 -136
- package/transpiled/react/Viewer/proptypes.js +0 -14
- package/transpiled/react/Viewer/providers/ActionMenuProvider.js +0 -34
- package/transpiled/react/Viewer/queries.js +0 -26
- /package/react/{Viewer/providers/EncryptedProvider.jsx → providers/Encrypted/index.jsx} +0 -0
- /package/transpiled/react/{Viewer/providers/EncryptedProvider.js → providers/Encrypted/index.js} +0 -0
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { useTheme } from '@material-ui/core'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import React from 'react'
|
|
4
|
-
|
|
5
|
-
import FooterContent from './Footer/FooterContent'
|
|
6
|
-
import PanelContent from './Panel/PanelContent'
|
|
7
|
-
import Footer from './components/Footer'
|
|
8
|
-
import InformationPanel from './components/InformationPanel'
|
|
9
|
-
import { useSetFlagshipUI } from '../hooks/useSetFlagshipUi/useSetFlagshipUI'
|
|
10
|
-
import { FileDoctype } from '../proptypes'
|
|
11
|
-
import { useCozyTheme } from '../providers/CozyTheme'
|
|
12
|
-
|
|
13
|
-
const ViewerInformationsWrapper = ({
|
|
14
|
-
currentFile,
|
|
15
|
-
disableFooter,
|
|
16
|
-
validForPanel,
|
|
17
|
-
toolbarRef,
|
|
18
|
-
isPublic,
|
|
19
|
-
children
|
|
20
|
-
}) => {
|
|
21
|
-
const theme = useTheme()
|
|
22
|
-
const { isLight } = useCozyTheme()
|
|
23
|
-
const sidebar = document.querySelector('[class*="sidebar"]')
|
|
24
|
-
|
|
25
|
-
useSetFlagshipUI(
|
|
26
|
-
{
|
|
27
|
-
bottomBackground: theme.palette.background.paper,
|
|
28
|
-
bottomTheme: isLight ? 'dark' : 'light'
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
bottomBackground: theme.palette.background[sidebar ? 'default' : 'paper']
|
|
32
|
-
}
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<>
|
|
37
|
-
{!disableFooter && (
|
|
38
|
-
<Footer>
|
|
39
|
-
<FooterContent
|
|
40
|
-
file={currentFile}
|
|
41
|
-
toolbarRef={toolbarRef}
|
|
42
|
-
isPublic={isPublic}
|
|
43
|
-
>
|
|
44
|
-
{children}
|
|
45
|
-
</FooterContent>
|
|
46
|
-
</Footer>
|
|
47
|
-
)}
|
|
48
|
-
{validForPanel && (
|
|
49
|
-
<InformationPanel>
|
|
50
|
-
<PanelContent file={currentFile} isPublic={isPublic} />
|
|
51
|
-
</InformationPanel>
|
|
52
|
-
)}
|
|
53
|
-
</>
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
ViewerInformationsWrapper.propTypes = {
|
|
58
|
-
currentFile: FileDoctype.isRequired,
|
|
59
|
-
disableFooter: PropTypes.bool,
|
|
60
|
-
validForPanel: PropTypes.bool,
|
|
61
|
-
toolbarRef: PropTypes.object,
|
|
62
|
-
isPublic: PropTypes.bool,
|
|
63
|
-
children: PropTypes.oneOfType([
|
|
64
|
-
PropTypes.node,
|
|
65
|
-
PropTypes.arrayOf(PropTypes.node)
|
|
66
|
-
])
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export default ViewerInformationsWrapper
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { render } from '@testing-library/react'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
|
|
4
|
-
import ViewerInformationsWrapper from './ViewerInformationsWrapper'
|
|
5
|
-
|
|
6
|
-
/* eslint-disable react/display-name */
|
|
7
|
-
jest.mock('./components/Footer', () => ({ children }) => (
|
|
8
|
-
<div data-testid="Footer">{children}</div>
|
|
9
|
-
))
|
|
10
|
-
jest.mock('./components/InformationPanel', () => ({ children }) => (
|
|
11
|
-
<div data-testid="InformationPanel">{children}</div>
|
|
12
|
-
))
|
|
13
|
-
jest.mock('./Panel/PanelContent', () => () => (
|
|
14
|
-
<div data-testid="PanelContent" />
|
|
15
|
-
))
|
|
16
|
-
jest.mock('./Footer/FooterContent', () => () => (
|
|
17
|
-
<div data-testid="FooterContent" />
|
|
18
|
-
))
|
|
19
|
-
/* eslint-enable react/display-name */
|
|
20
|
-
|
|
21
|
-
const setup = ({ validForPanel, disableFooter } = {}) => {
|
|
22
|
-
return render(
|
|
23
|
-
<ViewerInformationsWrapper
|
|
24
|
-
currentFile={{}}
|
|
25
|
-
disableFooter={disableFooter}
|
|
26
|
-
validForPanel={validForPanel}
|
|
27
|
-
/>
|
|
28
|
-
)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
describe('ViewerInformationsWrapper', () => {
|
|
32
|
-
describe('disableFooter', () => {
|
|
33
|
-
it('should render FooterContent components', () => {
|
|
34
|
-
const { getByTestId } = setup({ disableFooter: false })
|
|
35
|
-
|
|
36
|
-
expect(getByTestId('Footer'))
|
|
37
|
-
expect(getByTestId('FooterContent'))
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('should not render FooterContent components', () => {
|
|
41
|
-
const { queryByTestId } = setup({ disableFooter: true })
|
|
42
|
-
|
|
43
|
-
expect(queryByTestId('Footer')).toBeNull()
|
|
44
|
-
expect(queryByTestId('FooterContent')).toBeNull()
|
|
45
|
-
})
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
describe('validForPanel', () => {
|
|
49
|
-
it('should render InformationPanel & PanelContent components', () => {
|
|
50
|
-
const { getByTestId } = setup({ validForPanel: true })
|
|
51
|
-
|
|
52
|
-
expect(getByTestId('InformationPanel'))
|
|
53
|
-
expect(getByTestId('PanelContent'))
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it('should not render InformationPanel & PanelContent components', () => {
|
|
57
|
-
const { queryByTestId } = setup({ validForPanel: false })
|
|
58
|
-
|
|
59
|
-
expect(queryByTestId('InformationPanel')).toBeNull()
|
|
60
|
-
expect(queryByTestId('PanelContent')).toBeNull()
|
|
61
|
-
})
|
|
62
|
-
})
|
|
63
|
-
})
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import cx from 'classnames'
|
|
2
|
-
import React, { createRef } from 'react'
|
|
3
|
-
|
|
4
|
-
import Viewer from './Viewer'
|
|
5
|
-
import Footer from './components/Footer'
|
|
6
|
-
import InformationPanel from './components/InformationPanel'
|
|
7
|
-
import styles from './styles.styl'
|
|
8
|
-
import useBreakpoints from '../providers/Breakpoints'
|
|
9
|
-
|
|
10
|
-
const ViewerWithCustomPanelAndFooter = props => {
|
|
11
|
-
console.warn(
|
|
12
|
-
'Warning: Please do not use the "ViewerWithCustomPanelAndFooter" Component, replace it with the default export component'
|
|
13
|
-
)
|
|
14
|
-
const { footerProps, panelInfoProps, className, ...rest } = props
|
|
15
|
-
const { files, currentIndex } = props
|
|
16
|
-
const fileCount = files.length
|
|
17
|
-
const hasPrevious = currentIndex > 0
|
|
18
|
-
const hasNext = currentIndex < fileCount - 1
|
|
19
|
-
const { isDesktop } = useBreakpoints()
|
|
20
|
-
const toolbarRef = createRef()
|
|
21
|
-
const currentFile = files[currentIndex]
|
|
22
|
-
|
|
23
|
-
const showInfoPanel =
|
|
24
|
-
isDesktop &&
|
|
25
|
-
panelInfoProps &&
|
|
26
|
-
panelInfoProps.showPanel({ file: currentFile })
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<div
|
|
30
|
-
id="viewer-wrapper"
|
|
31
|
-
className={cx(styles['viewer-wrapper'], className)}
|
|
32
|
-
>
|
|
33
|
-
<Viewer
|
|
34
|
-
{...rest}
|
|
35
|
-
disablePanel={true}
|
|
36
|
-
disableFooter={true}
|
|
37
|
-
currentFile={currentFile}
|
|
38
|
-
hasPrevious={hasPrevious}
|
|
39
|
-
hasNext={hasNext}
|
|
40
|
-
validForPanel={showInfoPanel}
|
|
41
|
-
toolbarRef={toolbarRef}
|
|
42
|
-
/>
|
|
43
|
-
<Footer>
|
|
44
|
-
<footerProps.FooterContent file={currentFile} toolbarRef={toolbarRef} />
|
|
45
|
-
</Footer>
|
|
46
|
-
{showInfoPanel && (
|
|
47
|
-
<InformationPanel>
|
|
48
|
-
<panelInfoProps.PanelContent file={currentFile} />
|
|
49
|
-
</InformationPanel>
|
|
50
|
-
)}
|
|
51
|
-
</div>
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export default ViewerWithCustomPanelAndFooter
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
|
-
import styles from './styles.styl'
|
|
4
|
-
import Icon from '../../Icon'
|
|
5
|
-
import FileTypeAudioIcon from '../../Icons/FileTypeAudio'
|
|
6
|
-
import isTesting from '../../helpers/isTesting'
|
|
7
|
-
import withFileUrl from '../hoc/withFileUrl'
|
|
8
|
-
|
|
9
|
-
const AudioViewer = ({ file, url }) => (
|
|
10
|
-
<div className={styles['viewer-audioviewer']}>
|
|
11
|
-
<Icon icon={FileTypeAudioIcon} width={160} height={140} />
|
|
12
|
-
<p className={styles['viewer-filename']}>{file.name}</p>
|
|
13
|
-
<audio
|
|
14
|
-
src={url}
|
|
15
|
-
controls="controls"
|
|
16
|
-
preload={isTesting() ? 'none' : 'auto'}
|
|
17
|
-
/>
|
|
18
|
-
</div>
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
export default withFileUrl(AudioViewer)
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { render, waitFor } from '@testing-library/react'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
|
|
4
|
-
import AudioViewer from './AudioViewer'
|
|
5
|
-
import { BreakpointsProvider } from '../../providers/Breakpoints'
|
|
6
|
-
import DemoProvider from '../docs/DemoProvider'
|
|
7
|
-
|
|
8
|
-
const file = {
|
|
9
|
-
_id: 'audio',
|
|
10
|
-
class: 'audio',
|
|
11
|
-
mime: 'audio/mp3',
|
|
12
|
-
name: 'sample.mp3'
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const setup = () => {
|
|
16
|
-
const root = render(
|
|
17
|
-
<DemoProvider>
|
|
18
|
-
<BreakpointsProvider>
|
|
19
|
-
<AudioViewer file={file} />
|
|
20
|
-
</BreakpointsProvider>
|
|
21
|
-
</DemoProvider>
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
return { root }
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
describe('AudioViewer', () => {
|
|
28
|
-
it('should render a spinner then the audio viewer', async () => {
|
|
29
|
-
const { root } = setup()
|
|
30
|
-
const { container, queryByRole } = root
|
|
31
|
-
|
|
32
|
-
expect(queryByRole('progressbar')).toBeTruthy()
|
|
33
|
-
|
|
34
|
-
await waitFor(() => {
|
|
35
|
-
expect(queryByRole('progressbar')).toBeFalsy()
|
|
36
|
-
expect(container).toMatchSnapshot()
|
|
37
|
-
})
|
|
38
|
-
})
|
|
39
|
-
})
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react'
|
|
2
|
-
|
|
3
|
-
import styles from './styles.styl'
|
|
4
|
-
import Backdrop from '../../Backdrop'
|
|
5
|
-
import Button from '../../Buttons'
|
|
6
|
-
import Empty from '../../Empty'
|
|
7
|
-
import IntentDialogOpener from '../../IntentDialogOpener'
|
|
8
|
-
import IlluGenericNewPage from '../assets/IlluGenericNewPage.svg'
|
|
9
|
-
import { withViewerLocales } from '../hoc/withViewerLocales'
|
|
10
|
-
|
|
11
|
-
const BlankPaperViewer = ({ file, t }) => {
|
|
12
|
-
const [isLoading, setIsLoading] = useState(true)
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<div className={styles['viewer-noviewer']}>
|
|
16
|
-
<Empty
|
|
17
|
-
icon={<img src={IlluGenericNewPage} />}
|
|
18
|
-
text={t('Viewer.noImage')}
|
|
19
|
-
componentsProps={{
|
|
20
|
-
text: { color: 'inherit' }
|
|
21
|
-
}}
|
|
22
|
-
>
|
|
23
|
-
<IntentDialogOpener
|
|
24
|
-
action="OPEN"
|
|
25
|
-
doctype="io.cozy.files.paper"
|
|
26
|
-
Component={Backdrop}
|
|
27
|
-
invisible={!isLoading}
|
|
28
|
-
isOver
|
|
29
|
-
options={{
|
|
30
|
-
fileId: file._id,
|
|
31
|
-
qualificationLabel: file.metadata?.qualification?.label
|
|
32
|
-
}}
|
|
33
|
-
showCloseButton={false}
|
|
34
|
-
iframeProps={{
|
|
35
|
-
spinnerProps: { className: 'u-m-0', middle: true, color: 'white' },
|
|
36
|
-
setIsLoading
|
|
37
|
-
}}
|
|
38
|
-
>
|
|
39
|
-
<Button className="u-mt-1" label={t('Viewer.complete')} />
|
|
40
|
-
</IntentDialogOpener>
|
|
41
|
-
</Empty>
|
|
42
|
-
</div>
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export default withViewerLocales(BlankPaperViewer)
|
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
import Hammer from 'hammerjs'
|
|
2
|
-
import React, { Component } from 'react'
|
|
3
|
-
|
|
4
|
-
import NoNetworkViewer from './NoNetworkViewer'
|
|
5
|
-
import styles from './styles.styl'
|
|
6
|
-
import FileImageLoader from '../../FileImageLoader'
|
|
7
|
-
import ViewerSpinner from '../components/ViewerSpinner'
|
|
8
|
-
|
|
9
|
-
const MIN_SCALE = 1
|
|
10
|
-
const MAX_SCALE = 6
|
|
11
|
-
const MASS = 10 // If a paning gesture is released while the finger is still moving, the photo will keep paning for a little longer (a if you threw the photo). MASS determines how much the photo will keep paning (the higher the number, the more it will keep going)
|
|
12
|
-
const FRICTION = 0.9 // When the photo is paning after a pan gesture ended suddenly, FRICTION determines how quickly the movement slows down. 0 would stop it imediately, 1 doesn't slow it down at all.
|
|
13
|
-
|
|
14
|
-
const clamp = (min, value, max) => Math.max(min, Math.min(max, value))
|
|
15
|
-
|
|
16
|
-
class ImageViewer extends Component {
|
|
17
|
-
constructor(props) {
|
|
18
|
-
super(props)
|
|
19
|
-
this.state = {
|
|
20
|
-
loading: true,
|
|
21
|
-
canceled: false,
|
|
22
|
-
scale: 1,
|
|
23
|
-
offsetX: 0,
|
|
24
|
-
offsetY: 0,
|
|
25
|
-
initialOffset: {
|
|
26
|
-
x: 0,
|
|
27
|
-
y: 0
|
|
28
|
-
},
|
|
29
|
-
initialScale: 0,
|
|
30
|
-
momentum: {
|
|
31
|
-
x: 0,
|
|
32
|
-
y: 0
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
38
|
-
if (
|
|
39
|
-
nextProps.file &&
|
|
40
|
-
this.props.file &&
|
|
41
|
-
nextProps.file.id !== this.props.file.id
|
|
42
|
-
) {
|
|
43
|
-
this.tearDownGestures()
|
|
44
|
-
this.setState({
|
|
45
|
-
loading: true,
|
|
46
|
-
canceled: false,
|
|
47
|
-
scale: 1,
|
|
48
|
-
offsetX: 0,
|
|
49
|
-
offsetY: 0
|
|
50
|
-
})
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
componentDidMount() {
|
|
55
|
-
if (this.props.gestures) this.initGestures()
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
componentDidUpdate(prevProps, prevState) {
|
|
59
|
-
const wasLoading =
|
|
60
|
-
prevState.loading && !this.state.loading && !this.state.canceled
|
|
61
|
-
if (!prevProps.gestures) this.initGestures()
|
|
62
|
-
if (wasLoading) this.setupGestures()
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
componentWillUnmount() {
|
|
66
|
-
this.tearDownGestures()
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
reload = () => {
|
|
70
|
-
this.setState(state => ({
|
|
71
|
-
...state,
|
|
72
|
-
loading: true,
|
|
73
|
-
canceled: false
|
|
74
|
-
}))
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
onImageError = () => {
|
|
78
|
-
this.setState(state => ({ ...state, loading: false, canceled: true }))
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
onImageLoad = () => {
|
|
82
|
-
this.setState(state => ({ ...state, loading: false }))
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
tearDownGestures() {
|
|
86
|
-
if (this.gestures) {
|
|
87
|
-
this.gestures.off('swipe')
|
|
88
|
-
this.gestures.on('swipe', this.props.onSwipe)
|
|
89
|
-
this.gestures.off('panstart')
|
|
90
|
-
this.gestures.off('pinchstart')
|
|
91
|
-
this.gestures.off('pinchend')
|
|
92
|
-
this.gestures.off('pan')
|
|
93
|
-
this.gestures.off('pinch')
|
|
94
|
-
this.gestures.off('panend')
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
onSwipe = e => {
|
|
99
|
-
// when a swipe happens while zoomed into an image, it's most likely a pan gesture and not a swipe
|
|
100
|
-
if (this.state.scale > 1) return
|
|
101
|
-
// a pan event is triggered after the swipe and may trigger a getBoundingClientRect error
|
|
102
|
-
this.gestures.off('pan')
|
|
103
|
-
this.gestures.off('panend')
|
|
104
|
-
this.props.onSwipe(e)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
initGestures() {
|
|
108
|
-
this.gestures = this.props.gestures
|
|
109
|
-
this.viewer = this.props.gesturesRef
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
setupGestures() {
|
|
113
|
-
// We replace the swipe handler by ours
|
|
114
|
-
this.gestures.off('swipe')
|
|
115
|
-
this.gestures.on('swipe', this.onSwipe)
|
|
116
|
-
|
|
117
|
-
this.gestures.get('pinch').set({ enable: true })
|
|
118
|
-
this.gestures.get('pan').set({ direction: Hammer.DIRECTION_ALL })
|
|
119
|
-
|
|
120
|
-
// During a gesture, everything is computed with a base value (the state of the image when the gesture starts) and a delta (a translation / zoom, described by the gesture). When a gesture starts, we record the current state of the image.
|
|
121
|
-
this.gestures.on('panstart', this.prepareForGesture.bind(this))
|
|
122
|
-
this.gestures.on('pinchstart', this.prepareForGesture.bind(this))
|
|
123
|
-
// It frequently happens that at the end of a pinch gesture, a pan gesture is detected — because the fingers don't come off the screen at exactly the same time. Reseting the values at the end of the pinch makes sure the values are correct for the (accidental) pan event.
|
|
124
|
-
this.gestures.on('pinchend', this.prepareForGesture.bind(this))
|
|
125
|
-
|
|
126
|
-
// during a pan, we add the gestures delta to the initial offset to get the new offset. The new offset is then scaled : if the pan distance was 100px, but the image was scaled 2x, the actual offset should only be 50px. FInally, this value is clamped to make sure the user can't pan further than the edges.
|
|
127
|
-
this.gestures.on('pan', e => {
|
|
128
|
-
this.setState(state => {
|
|
129
|
-
const maxOffset = this.computeMaxOffset()
|
|
130
|
-
return {
|
|
131
|
-
offsetX: clamp(
|
|
132
|
-
-maxOffset.x,
|
|
133
|
-
state.initialOffset.x + e.deltaX / state.scale,
|
|
134
|
-
maxOffset.x
|
|
135
|
-
),
|
|
136
|
-
offsetY: clamp(
|
|
137
|
-
-maxOffset.y,
|
|
138
|
-
state.initialOffset.y + e.deltaY / state.scale,
|
|
139
|
-
maxOffset.y
|
|
140
|
-
)
|
|
141
|
-
}
|
|
142
|
-
})
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
// pinching / zooming / scaling is a bit more complicated, because the gesture's center has to be taken into account
|
|
146
|
-
this.gestures.on('pinch', e => {
|
|
147
|
-
if (e.isFinal) return // hard to reproduce, but the final event seems to be causing problems and since it's just replaying the previous event, it can safely be discared
|
|
148
|
-
|
|
149
|
-
this.setState(state => {
|
|
150
|
-
// first we compute the scale factor: this is the number by which we will multiply the initial scale (as it was before the gesture started) to get the final scaling value. So if the initial scale is 2, and the scale factor is 1.5, the final scale will be 3.
|
|
151
|
-
// this value is clamped so so it stays within reasonable zoom limits.
|
|
152
|
-
let scaleFactor = clamp(
|
|
153
|
-
MIN_SCALE / state.initialScale,
|
|
154
|
-
e.scale,
|
|
155
|
-
MAX_SCALE / state.initialScale
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
// When the user is zooming in or out, we want that the origin point of the gesture to stay in exactly the same place. The scaling origin is in the center of the viewer.
|
|
159
|
-
// If the gesture's origin is the same as the scaling origin, this works "out of the box" — you can imagine the pixels on all sides being "pushed" towards the outside. But if the gesture's origin is not in the center, we need to offset the whole image to produce the illusion that the scaling center is there.
|
|
160
|
-
|
|
161
|
-
// compute the center of the viewer
|
|
162
|
-
let wrapperBoundaries = this.viewer.getBoundingClientRect()
|
|
163
|
-
const viewerCenter = {
|
|
164
|
-
x: (wrapperBoundaries.right - wrapperBoundaries.left) / 2,
|
|
165
|
-
y: (wrapperBoundaries.bottom - wrapperBoundaries.top) / 2
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Compute the delta between the viewer's center and the gesture's center. This value is scaled back to the "natural" size — if the delta is 100px but the image is currently scale 2x, the real offset value is only 50px.
|
|
169
|
-
const offsetBeforeScale = {
|
|
170
|
-
x: (viewerCenter.x - e.center.x) / state.scale,
|
|
171
|
-
y: (viewerCenter.y - e.center.y) / state.scale
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Now we compute what this offset will be once we apply the new scale
|
|
175
|
-
const offsetAfterScale = {
|
|
176
|
-
x: offsetBeforeScale.x * scaleFactor,
|
|
177
|
-
y: offsetBeforeScale.y * scaleFactor
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// finally, we compute the actual offset we want to apply. This is the difference between the offset *after* scaling and the offset *before* scaling. We also add any existing offset to preserve it (otherwise it is reset to the center each time)
|
|
181
|
-
const finalOffset = {
|
|
182
|
-
x: offsetAfterScale.x - offsetBeforeScale.x + state.initialOffset.x,
|
|
183
|
-
y: offsetAfterScale.y - offsetBeforeScale.y + state.initialOffset.y
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// last thing: the offsets are clamped to make sure the offsetting doesn't go further than the edges
|
|
187
|
-
const maxOffset = this.computeMaxOffset()
|
|
188
|
-
|
|
189
|
-
return {
|
|
190
|
-
scale: state.initialScale * scaleFactor,
|
|
191
|
-
offsetX: clamp(-maxOffset.x, finalOffset.x, maxOffset.x),
|
|
192
|
-
offsetY: clamp(-maxOffset.y, finalOffset.y, maxOffset.y)
|
|
193
|
-
}
|
|
194
|
-
})
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
this.gestures.on('panend', e => {
|
|
198
|
-
// convert the remaining velocity into momentum
|
|
199
|
-
this.setState(
|
|
200
|
-
{
|
|
201
|
-
momentum: {
|
|
202
|
-
x: e.velocityX * MASS,
|
|
203
|
-
y: e.velocityY * MASS
|
|
204
|
-
}
|
|
205
|
-
},
|
|
206
|
-
this.applyMomentum.bind(this)
|
|
207
|
-
)
|
|
208
|
-
})
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
render() {
|
|
212
|
-
if (this.state.canceled) {
|
|
213
|
-
return <NoNetworkViewer onReload={this.reload} />
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const { file, url } = this.props
|
|
217
|
-
const { scale, offsetX, offsetY, loading } = this.state
|
|
218
|
-
const style = {
|
|
219
|
-
transform: `scale(${scale}) translate(${offsetX}px, ${offsetY}px)`
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return (
|
|
223
|
-
<div className={styles['viewer-imageviewer']}>
|
|
224
|
-
{loading && <ViewerSpinner />}
|
|
225
|
-
{file && (
|
|
226
|
-
<FileImageLoader
|
|
227
|
-
file={file}
|
|
228
|
-
url={url}
|
|
229
|
-
linkType="large"
|
|
230
|
-
onError={this.onImageError}
|
|
231
|
-
key={file.id}
|
|
232
|
-
render={src => (
|
|
233
|
-
<img
|
|
234
|
-
ref={photo => (this.photo = photo)}
|
|
235
|
-
style={style}
|
|
236
|
-
alt={file.name}
|
|
237
|
-
src={src}
|
|
238
|
-
onLoad={this.onImageLoad}
|
|
239
|
-
/>
|
|
240
|
-
)}
|
|
241
|
-
/>
|
|
242
|
-
)}
|
|
243
|
-
</div>
|
|
244
|
-
)
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Things to do when a gesture starts:
|
|
249
|
-
* - saves the current scale and offset, which will be used as base values for calculations
|
|
250
|
-
* - kill any remaining momentum from previous gestures
|
|
251
|
-
* - hide the actions
|
|
252
|
-
*/
|
|
253
|
-
prepareForGesture() {
|
|
254
|
-
this.setState(state => ({
|
|
255
|
-
initialScale: state.scale,
|
|
256
|
-
initialOffset: {
|
|
257
|
-
x: state.offsetX,
|
|
258
|
-
y: state.offsetY
|
|
259
|
-
},
|
|
260
|
-
momentum: {
|
|
261
|
-
x: 0,
|
|
262
|
-
y: 0
|
|
263
|
-
}
|
|
264
|
-
}))
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Gradually applies the momentum after a pan end
|
|
269
|
-
*/
|
|
270
|
-
applyMomentum() {
|
|
271
|
-
this.setState(
|
|
272
|
-
state => {
|
|
273
|
-
const maxOffset = this.computeMaxOffset()
|
|
274
|
-
|
|
275
|
-
return {
|
|
276
|
-
offsetX: clamp(
|
|
277
|
-
-maxOffset.x,
|
|
278
|
-
state.offsetX + state.momentum.x,
|
|
279
|
-
maxOffset.x
|
|
280
|
-
),
|
|
281
|
-
offsetY: clamp(
|
|
282
|
-
-maxOffset.y,
|
|
283
|
-
state.offsetY + state.momentum.y,
|
|
284
|
-
maxOffset.y
|
|
285
|
-
),
|
|
286
|
-
momentum: {
|
|
287
|
-
x: state.momentum.x * FRICTION,
|
|
288
|
-
y: state.momentum.y * FRICTION
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
},
|
|
292
|
-
() => {
|
|
293
|
-
if (
|
|
294
|
-
Math.abs(this.state.momentum.x) > 0.1 ||
|
|
295
|
-
Math.abs(this.state.momentum.y) > 0.1
|
|
296
|
-
)
|
|
297
|
-
requestAnimationFrame(this.applyMomentum.bind(this))
|
|
298
|
-
}
|
|
299
|
-
)
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Compute the maximum offset that can be applied to the photo on each axis before it goes over the edges
|
|
304
|
-
* @returns {object} A point with an x and y property
|
|
305
|
-
*/
|
|
306
|
-
computeMaxOffset() {
|
|
307
|
-
if (this.viewer && this.photo) {
|
|
308
|
-
const wrapperBoundaries = this.viewer.getBoundingClientRect()
|
|
309
|
-
const photoBoundaries = this.photo.getBoundingClientRect()
|
|
310
|
-
|
|
311
|
-
return {
|
|
312
|
-
x:
|
|
313
|
-
Math.max(photoBoundaries.width / 2 - wrapperBoundaries.width / 2, 0) /
|
|
314
|
-
this.state.scale,
|
|
315
|
-
y:
|
|
316
|
-
Math.max(
|
|
317
|
-
photoBoundaries.height / 2 - wrapperBoundaries.height / 2,
|
|
318
|
-
0
|
|
319
|
-
) / this.state.scale
|
|
320
|
-
}
|
|
321
|
-
} else {
|
|
322
|
-
return {
|
|
323
|
-
x: 0,
|
|
324
|
-
y: 0
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
export default ImageViewer
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { render, waitFor } from '@testing-library/react'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
|
|
4
|
-
import ImageViewer from './ImageViewer'
|
|
5
|
-
import { checkImageSource } from '../../FileImageLoader/checkImageSource'
|
|
6
|
-
import { BreakpointsProvider } from '../../providers/Breakpoints'
|
|
7
|
-
import DemoProvider from '../docs/DemoProvider'
|
|
8
|
-
import EncryptedProvider from '../providers/EncryptedProvider'
|
|
9
|
-
|
|
10
|
-
jest.mock('../../FileImageLoader/checkImageSource', () => ({
|
|
11
|
-
...jest.requireActual('../../FileImageLoader/checkImageSource'),
|
|
12
|
-
checkImageSource: jest.fn()
|
|
13
|
-
}))
|
|
14
|
-
|
|
15
|
-
const file = {
|
|
16
|
-
_id: 'image',
|
|
17
|
-
class: 'image',
|
|
18
|
-
mime: 'image/jpg',
|
|
19
|
-
name: 'sample.jpg',
|
|
20
|
-
links: {
|
|
21
|
-
large: 'https://viewerdemo.cozycloud.cc/IMG_0062.PNG'
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const gestures = {
|
|
26
|
-
on: jest.fn(),
|
|
27
|
-
off: jest.fn(),
|
|
28
|
-
get: jest.fn()
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const setup = () => {
|
|
32
|
-
const root = render(
|
|
33
|
-
<DemoProvider>
|
|
34
|
-
<BreakpointsProvider>
|
|
35
|
-
<EncryptedProvider>
|
|
36
|
-
<ImageViewer
|
|
37
|
-
file={file}
|
|
38
|
-
gestures={gestures}
|
|
39
|
-
gesturesRef={{}}
|
|
40
|
-
onSwipe={jest.fn()}
|
|
41
|
-
/>
|
|
42
|
-
</EncryptedProvider>
|
|
43
|
-
</BreakpointsProvider>
|
|
44
|
-
</DemoProvider>
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
return { root }
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
describe('ImageViewer', () => {
|
|
51
|
-
afterEach(() => {
|
|
52
|
-
jest.restoreAllMocks
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('should render a spinner then the image viewer', async () => {
|
|
56
|
-
const { root } = setup()
|
|
57
|
-
const { container, queryByRole } = root
|
|
58
|
-
|
|
59
|
-
expect(queryByRole('progressbar')).toBeTruthy()
|
|
60
|
-
|
|
61
|
-
// simulate a successfull image loading
|
|
62
|
-
checkImageSource.mockResolvedValue('ok')
|
|
63
|
-
|
|
64
|
-
await waitFor(() => container.querySelector('img'))
|
|
65
|
-
|
|
66
|
-
const img = container.querySelector('img')
|
|
67
|
-
expect(img.getAttribute('alt')).toBe(file.name)
|
|
68
|
-
expect(img.getAttribute('src')).toBe(file.links.large)
|
|
69
|
-
})
|
|
70
|
-
})
|