cozy-ui 121.1.1 → 121.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-ui",
3
- "version": "121.1.1",
3
+ "version": "121.2.0",
4
4
  "description": "Cozy apps UI SDK",
5
5
  "main": "./index.js",
6
6
  "bin": {
@@ -11,9 +11,10 @@ import Stack from 'cozy-ui/transpiled/react/Stack'
11
11
  import Variants from 'cozy-ui/docs/components/Variants'
12
12
 
13
13
  const props = [{ color: 'primary' }, { color: 'inherit', default: true }]
14
- const initialVariants = [{ small: false, medium: false, large: true }]
14
+ const initialVariants = [{ small: false, medium: true, large: false }]
15
15
 
16
16
  ;
17
+
17
18
  <Variants initialVariants={initialVariants} radio screenshotAllVariants>
18
19
  {variant => (
19
20
  <Grid container>
@@ -1,4 +1,10 @@
1
1
  import Fab from '@material-ui/core/Fab'
2
2
 
3
+ import { isTwakeTheme } from '../helpers/isTwakeTheme'
4
+
5
+ Fab.defaultProps = {
6
+ size: isTwakeTheme() ? 'medium' : 'large'
7
+ }
8
+
3
9
  export default Fab
4
10
  export { default as ExtendableFab } from './ExtendableFab'
@@ -3,13 +3,15 @@ import PropTypes from 'prop-types'
3
3
  import React, { Component } from 'react'
4
4
 
5
5
  import styles from './styles.styl'
6
+ import { isTwakeTheme } from '../helpers/isTwakeTheme'
6
7
 
7
8
  export const Layout = ({ children, className, monoColumn, ...rest }) => {
8
9
  return (
9
10
  <div
10
11
  className={cx(
11
12
  monoColumn ? styles['o-layout'] : styles['o-layout-2panes'],
12
- className
13
+ className,
14
+ { [styles['o-layout--rounded']]: isTwakeTheme() }
13
15
  )}
14
16
  {...rest}
15
17
  >
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types'
4
4
  import React from 'react'
5
5
 
6
6
  import styles from './styles.styl'
7
+ import { isTwakeTheme } from '../helpers/isTwakeTheme'
7
8
  import { useSetFlagshipUI } from '../hooks/useSetFlagshipUi/useSetFlagshipUI'
8
9
  import { useCozyTheme } from '../providers/CozyTheme'
9
10
 
@@ -25,7 +26,12 @@ const Sidebar = ({ children, className, ...restProps }) => {
25
26
  return (
26
27
  <aside
27
28
  id="sidebar"
28
- className={cx(styles['o-sidebar'], className)}
29
+ className={cx(
30
+ styles['o-sidebar'],
31
+ { [styles['o-sidebar--border']]: !isTwakeTheme() },
32
+ { [styles['o-sidebar--large']]: isTwakeTheme() },
33
+ className
34
+ )}
29
35
  {...restProps}
30
36
  >
31
37
  {children}
@@ -0,0 +1,49 @@
1
+ import LinearProgress from '@material-ui/core/LinearProgress'
2
+ import React, { useState } from 'react'
3
+ import { useIntervalWhen } from 'rooks'
4
+
5
+ import RemainingTime from './RemainingTime'
6
+ import styles from './styles.styl'
7
+ import { withStyles } from '../styles'
8
+
9
+ const FileLinearProgress = withStyles(theme => ({
10
+ root: {
11
+ borderRadius: theme.shape.borderRadius
12
+ },
13
+ colorPrimary: {
14
+ backgroundColor: theme.palette.background.default
15
+ },
16
+ barColorPrimary: {
17
+ backgroundColor: 'var(--emerald)'
18
+ }
19
+ }))(LinearProgress)
20
+
21
+ const FileUploadProgress = ({ progress: progressProps }) => {
22
+ const [progress, setProgress] = useState(progressProps)
23
+ useIntervalWhen(
24
+ () => {
25
+ setProgress(progressProps)
26
+ },
27
+ 1000,
28
+ true,
29
+ true
30
+ )
31
+
32
+ return (
33
+ <div className={styles['upload-queue__upload-progress']}>
34
+ <div className="u-flex-grow-1 u-pr-1">
35
+ <FileLinearProgress
36
+ variant="determinate"
37
+ value={(progress.loaded / progress.total) * 100}
38
+ />
39
+ </div>
40
+ <div className="u-flex-shrink">
41
+ {progress.remainingTime ? (
42
+ <RemainingTime durationInSec={progress.remainingTime} />
43
+ ) : null}
44
+ </div>
45
+ </div>
46
+ )
47
+ }
48
+
49
+ export default FileUploadProgress
@@ -0,0 +1,99 @@
1
+ import cx from 'classnames'
2
+ import React from 'react'
3
+
4
+ import { splitFilename } from 'cozy-client/dist/models/file'
5
+
6
+ import FileUploadProgress from './FileUploadProgress'
7
+ import Pending from './Pending'
8
+ import { uploadStatus } from './index'
9
+ import styles from './styles.styl'
10
+ import { useStatusIcon } from './useStatusIcon'
11
+ import Icon from '../Icon'
12
+ import ListItem from '../ListItem'
13
+ import ListItemIcon from '../ListItemIcon'
14
+ import ListItemText from '../ListItemText'
15
+ import Typography from '../Typography'
16
+ import { Img } from '../deprecated/Media'
17
+ import { translate } from '../providers/I18n'
18
+
19
+ const Item = ({ file, status, isDirectory, progress, getMimeTypeIcon }) => {
20
+ const { CANCEL, LOADING, DONE_STATUSES, ERROR_STATUSES, PENDING } =
21
+ uploadStatus
22
+ const { filename, extension } = splitFilename(file)
23
+ let done = false
24
+ let error = false
25
+ /**
26
+ * Status came from the Upload Queue, but sometimes we're using
27
+ * manual upload without using the Upload Queue system but we're still
28
+ * using the UI component. When this is the case, the file handles on
29
+ * his own its status.
30
+ */
31
+ const statusToUse = file.status ? file.status : status
32
+
33
+ if (statusToUse !== LOADING && statusToUse !== CANCEL) {
34
+ if (ERROR_STATUSES.includes(statusToUse)) {
35
+ error = true
36
+ } else if (DONE_STATUSES.includes(statusToUse)) {
37
+ done = true
38
+ }
39
+ }
40
+
41
+ const statusIcon = useStatusIcon(statusToUse, progress)
42
+
43
+ const isPending =
44
+ statusToUse !== LOADING &&
45
+ statusToUse !== CANCEL &&
46
+ !ERROR_STATUSES.includes(statusToUse) &&
47
+ !DONE_STATUSES.includes(statusToUse) &&
48
+ statusToUse === PENDING
49
+
50
+ return (
51
+ <ListItem
52
+ divider
53
+ data-testid="upload-queue-item"
54
+ className={cx({
55
+ [styles['upload-queue-item--done']]: done,
56
+ [styles['upload-queue-item--error']]: error
57
+ })}
58
+ >
59
+ {getMimeTypeIcon ? (
60
+ <ListItemIcon className="u-ta-center">
61
+ <Icon
62
+ icon={getMimeTypeIcon(isDirectory, file.name, file.type)}
63
+ size={32}
64
+ className="u-mr-1"
65
+ />
66
+ </ListItemIcon>
67
+ ) : null}
68
+ <ListItemText
69
+ disableTypography
70
+ primary={
71
+ <div data-testid="upload-queue-item-name" className="u-ellipsis">
72
+ <Typography variant="body1" className="u-ellipsis">
73
+ {filename}
74
+ {extension && (
75
+ <Typography
76
+ component="span"
77
+ variant="body1"
78
+ color="textSecondary"
79
+ className="u-dib"
80
+ >
81
+ {extension}
82
+ </Typography>
83
+ )}
84
+ </Typography>
85
+ </div>
86
+ }
87
+ secondary={progress ? <FileUploadProgress progress={progress} /> : null}
88
+ />
89
+ {statusIcon && (
90
+ <ListItemIcon>
91
+ <Img>{statusIcon}</Img>
92
+ </ListItemIcon>
93
+ )}
94
+ {isPending && <Pending />}
95
+ </ListItem>
96
+ )
97
+ }
98
+
99
+ export default translate()(Item)
@@ -0,0 +1,12 @@
1
+ import React from 'react'
2
+
3
+ import Typography from '../Typography'
4
+ import { translate } from '../providers/I18n'
5
+
6
+ const Pending = translate()(props => (
7
+ <Typography variant="subtitle1" color="primary">
8
+ {props.t('item.pending')}
9
+ </Typography>
10
+ ))
11
+
12
+ export default Pending
@@ -12,7 +12,8 @@ the upload queue:
12
12
  ```jsx
13
13
  import isTesting from '../helpers/isTesting'
14
14
  import FileIcon from 'cozy-ui/transpiled/react/Icons/File'
15
- import UploadQueue from 'cozy-ui/transpiled/react/UploadQueue';
15
+ import UploadQueue from 'cozy-ui/transpiled/react/UploadQueue'
16
+ import Checkbox from 'cozy-ui/transpiled/react/Checkbox'
16
17
 
17
18
  const initialState = {
18
19
  popover: false
@@ -34,6 +35,9 @@ const data = {
34
35
  }, {
35
36
  file: { name: 'Photo - generic failure error.jpg', type: 'file' },
36
37
  status: 'failed'
38
+ }, {
39
+ file: { name: 'Photo - generic failure error with a very long name - really long - 2020/04/16.txt', type: 'file' },
40
+ status: 'failed'
37
41
  }, {
38
42
  file: { name: 'File with a very long name - really long - 2020/04/16.txt', type: 'file' },
39
43
  progress: {
@@ -49,10 +53,12 @@ const data = {
49
53
  }],
50
54
  doneCount: 1,
51
55
  successCount: 1
52
- };
56
+ }
57
+
58
+ ;
53
59
 
54
60
  <>
55
- popover: <input type="checkbox" value={state.popover} onChange={() => setState({ popover: !state.popover })}/>
61
+ <Checkbox label="Popover" value={state.popover} onChange={() => setState({ popover: !state.popover })} />
56
62
  <UploadQueue
57
63
  lang='fr'
58
64
  app='Cozy Drive'
@@ -61,6 +67,7 @@ const data = {
61
67
  doneCount={data.doneCount}
62
68
  successCount={data.successCount}
63
69
  popover={state.popover}
70
+ purgeQueue={() => {}}
64
71
  />
65
72
  {isTesting() && (
66
73
  <>
@@ -0,0 +1,26 @@
1
+ import cx from 'classnames'
2
+ import React from 'react'
3
+
4
+ import { numberOfReferencesForPluralForm } from './helpers'
5
+ import { formatRemainingTime } from './index'
6
+ import styles from './styles.styl'
7
+ import Typography from '../Typography'
8
+ import { useI18n } from '../providers/I18n'
9
+
10
+ const RemainingTime = ({ durationInSec }) => {
11
+ const { t } = useI18n()
12
+
13
+ return (
14
+ <Typography
15
+ variant="caption"
16
+ className={cx(styles['upload-queue__progress-caption'], 'u-ellipsis')}
17
+ >
18
+ {t('item.remainingTime', {
19
+ time: formatRemainingTime(durationInSec),
20
+ smart_count: numberOfReferencesForPluralForm(durationInSec)
21
+ })}
22
+ </Typography>
23
+ )
24
+ }
25
+
26
+ export default RemainingTime
@@ -0,0 +1,3 @@
1
+ // https://date-fns.org/v2.28.0/docs/formatDistanceToNow
2
+ export const numberOfReferencesForPluralForm = durationInSec =>
3
+ durationInSec < 90 || (durationInSec > 2670 && durationInSec < 5370) ? 1 : 2
@@ -1,27 +1,15 @@
1
1
  import LinearProgress from '@material-ui/core/LinearProgress'
2
2
  import cx from 'classnames'
3
- import React, { Component, useState } from 'react'
4
- import { useIntervalWhen } from 'rooks'
5
-
6
- import { splitFilename } from 'cozy-client/dist/models/file'
3
+ import React, { Component } from 'react'
7
4
 
5
+ import Item from './Item'
8
6
  import localeEn from './locales/en.json'
9
7
  import localeEs from './locales/es.json'
10
8
  import localeFr from './locales/fr.json'
11
9
  import styles from './styles.styl'
12
- import Icon from '../Icon'
13
- import CheckIcon from '../Icons/Check'
14
- import CrossIcon from '../Icons/Cross'
15
- import WarningIcon from '../Icons/Warning'
16
- import Spinner from '../Spinner'
17
- import Typography from '../Typography'
18
10
  import List from '../List'
19
- import ListItem from '../ListItem'
20
- import ListItemText from '../ListItemText'
21
- import ListItemIcon from '../ListItemIcon'
22
- import { Img } from '../deprecated/Media'
11
+ import Typography from '../Typography'
23
12
  import Button from '../deprecated/Button'
24
- import { translate, useI18n } from '../providers/I18n'
25
13
  import { formatLocallyDistanceToNow } from '../providers/I18n/format'
26
14
  import withLocales from '../providers/I18n/withLocales'
27
15
  import { withStyles } from '../styles'
@@ -58,174 +46,17 @@ export const uploadStatus = {
58
46
  ERROR_STATUSES
59
47
  }
60
48
 
61
- const Pending = translate()(props => (
62
- <Typography variant="subtitle1" color="primary">
63
- {props.t('item.pending')}
64
- </Typography>
65
- ))
66
-
67
49
  export const formatRemainingTime = durationInSec => {
68
50
  const later = Date.now() + durationInSec * 1000
69
51
  return formatLocallyDistanceToNow(later)
70
52
  }
71
53
 
72
- // https://date-fns.org/v2.28.0/docs/formatDistanceToNow
73
- const numberOfReferencesForPluralForm = durationInSec =>
74
- durationInSec < 90 || (durationInSec > 2670 && durationInSec < 5370) ? 1 : 2
75
-
76
- const RemainingTime = ({ durationInSec }) => {
77
- const { t } = useI18n()
78
-
79
- return (
80
- <Typography
81
- variant="caption"
82
- className={cx(styles['upload-queue__progress-caption'], 'u-ellipsis')}
83
- >
84
- {t('item.remainingTime', {
85
- time: formatRemainingTime(durationInSec),
86
- smart_count: numberOfReferencesForPluralForm(durationInSec)
87
- })}
88
- </Typography>
89
- )
90
- }
91
-
92
- const FileLinearProgress = withStyles(theme => ({
93
- root: {
94
- borderRadius: theme.shape.borderRadius
95
- },
96
- colorPrimary: {
97
- backgroundColor: theme.palette.background.default
98
- },
99
- barColorPrimary: {
100
- backgroundColor: 'var(--emerald)'
101
- }
102
- }))(LinearProgress)
103
-
104
54
  const QueueLinearProgress = withStyles({
105
55
  root: {
106
56
  height: '2px'
107
57
  }
108
58
  })(LinearProgress)
109
59
 
110
- const FileUploadProgress = ({ progress: progressProps }) => {
111
- const [progress, setProgress] = useState(progressProps)
112
- useIntervalWhen(
113
- () => {
114
- setProgress(progressProps)
115
- },
116
- 1000,
117
- true,
118
- true
119
- )
120
-
121
- return (
122
- <div className={styles['upload-queue__upload-progress']}>
123
- <div className="u-flex-grow-1 u-pr-1">
124
- <FileLinearProgress
125
- variant="determinate"
126
- value={(progress.loaded / progress.total) * 100}
127
- />
128
- </div>
129
- <div className="u-flex-shrink">
130
- {progress.remainingTime ? (
131
- <RemainingTime durationInSec={progress.remainingTime} />
132
- ) : null}
133
- </div>
134
- </div>
135
- )
136
- }
137
-
138
- const Item = translate()(
139
- ({ file, status, isDirectory, progress, getMimeTypeIcon }) => {
140
- const { CANCEL, LOADING, DONE_STATUSES, ERROR_STATUSES } = uploadStatus
141
- const { filename, extension } = splitFilename(file)
142
- let statusIcon
143
- let done = false
144
- let error = false
145
- /**
146
- * Status came from the Upload Queue, but sometimes we're using
147
- * manual upload without using the Upload Queue system but we're still
148
- * using the UI component. When this is the case, the file handles on
149
- * his own its status.
150
- */
151
- const statusToUse = file.status ? file.status : status
152
-
153
- if (statusToUse === LOADING) {
154
- statusIcon = !progress ? (
155
- <Spinner className="u-ml-half" color="var(--primaryColor)" />
156
- ) : null
157
- } else if (statusToUse === CANCEL) {
158
- statusIcon = (
159
- <Icon
160
- className="u-ml-half"
161
- icon={CrossIcon}
162
- color="var(--errorColor)"
163
- />
164
- )
165
- } else if (ERROR_STATUSES.includes(statusToUse)) {
166
- error = true
167
- statusIcon = (
168
- <Icon
169
- className="u-ml-half"
170
- icon={WarningIcon}
171
- color="var(--errorColor)"
172
- />
173
- )
174
- } else if (DONE_STATUSES.includes(statusToUse)) {
175
- done = true
176
- statusIcon = (
177
- <Icon
178
- className="u-ml-half"
179
- icon={CheckIcon}
180
- color="var(--successColor)"
181
- />
182
- )
183
- } else if (statusToUse === PENDING) {
184
- statusIcon = <Pending />
185
- }
186
-
187
- return (
188
- <ListItem
189
- divider
190
- data-testid="upload-queue-item"
191
- className={cx('u-ph-1', {
192
- [styles['upload-queue-item--done']]: done,
193
- [styles['upload-queue-item--error']]: error
194
- })}
195
- >
196
- {getMimeTypeIcon ? (
197
- <ListItemIcon className="u-ta-center">
198
- <Icon
199
- icon={getMimeTypeIcon(isDirectory, file.name, file.type)}
200
- size={32}
201
- className="u-mr-1"
202
- />
203
- </ListItemIcon>
204
- ) : null}
205
- <ListItemText disableTypography>
206
- <div data-testid="upload-queue-item-name" className="u-ellipsis">
207
- <Typography variant="body1" className="u-ellipsis">
208
- {filename}
209
- {extension && (
210
- <Typography
211
- component="span"
212
- variant="body1"
213
- color="textSecondary"
214
- className="u-dib"
215
- >
216
- {extension}
217
- </Typography>
218
- )}
219
- </Typography>
220
- </div>
221
- {progress ? <FileUploadProgress progress={progress} /> : null}
222
- </ListItemText>
223
- <Img className={styles['item-status']}>{statusIcon}</Img>
224
- </ListItem>
225
- )
226
- }
227
- )
228
-
229
60
  export class UploadQueue extends Component {
230
61
  state = {
231
62
  collapsed: false
@@ -7,12 +7,13 @@ $coz-bar-size=3rem
7
7
 
8
8
  .upload-queue
9
9
  border .0625rem solid var(--dividerColor)
10
- border-radius .25rem
10
+ border-radius .5rem
11
11
  background-color var(--paperBackgroundColor)
12
12
  display flex
13
13
  flex-direction column
14
14
  max-width 90%
15
15
  width 30rem
16
+ overflow hidden
16
17
 
17
18
  .upload-queue__threshold-bar
18
19
  min-width 10rem
@@ -31,7 +32,7 @@ $coz-bar-size=3rem
31
32
  .upload-queue--popover
32
33
  z-index var(--zIndex-popover)
33
34
  border rem(1) solid var(--dividerColor)
34
- border-radius rem(4)
35
+ border-radius .5rem
35
36
  box-shadow rem(0 1 3 0) rgba(50, 54, 63, .19), rem(0 6 18 0) rgba(50, 54, 63, .19)
36
37
  background-color var(--paperBackgroundColor)
37
38
  position fixed
@@ -54,6 +55,7 @@ $coz-bar-size=3rem
54
55
  flex-direction column
55
56
  justify-content center
56
57
  height 2rem
58
+ min-height 2rem
57
59
  background-color var(--defaultBackgroundColor)
58
60
  font-weight bold
59
61
  margin 0
@@ -0,0 +1,34 @@
1
+ import React from 'react'
2
+
3
+ import { uploadStatus } from './index'
4
+ import Icon from '../Icon'
5
+ import CheckIcon from '../Icons/Check'
6
+ import CheckCircleIcon from '../Icons/CheckCircle'
7
+ import CrossIcon from '../Icons/Cross'
8
+ import CrossCircleIcon from '../Icons/CrossCircle'
9
+ import WarningIcon from '../Icons/Warning'
10
+ import Spinner from '../Spinner'
11
+ import { isTwakeTheme } from '../helpers/isTwakeTheme'
12
+
13
+ export const useStatusIcon = (statusToUse, progress) => {
14
+ const { CANCEL, LOADING, DONE_STATUSES, ERROR_STATUSES, PENDING } =
15
+ uploadStatus
16
+ const SuccessIcon = isTwakeTheme() ? CheckCircleIcon : CheckIcon
17
+ const ErrorIcon = isTwakeTheme() ? CrossCircleIcon : CrossIcon
18
+
19
+ let statusIcon
20
+
21
+ if (statusToUse === LOADING) {
22
+ statusIcon = !progress ? <Spinner color="var(--primaryColor)" /> : null
23
+ } else if (statusToUse === CANCEL) {
24
+ statusIcon = <Icon icon={ErrorIcon} color="var(--errorColor)" />
25
+ } else if (ERROR_STATUSES.includes(statusToUse)) {
26
+ statusIcon = <Icon icon={WarningIcon} color="var(--errorColor)" />
27
+ } else if (DONE_STATUSES.includes(statusToUse)) {
28
+ statusIcon = <Icon icon={SuccessIcon} color="var(--successColor)" />
29
+ } else if (statusToUse === PENDING) {
30
+ statusIcon = null
31
+ }
32
+
33
+ return statusIcon
34
+ }
@@ -65,6 +65,7 @@ $nav-item-icon
65
65
  +medium-screen()
66
66
  display block
67
67
  margin-right 0
68
+ color var(--secondaryTextColor)
68
69
 
69
70
  svg
70
71
  margin 4px auto 5px
@@ -126,6 +127,7 @@ $nav-link
126
127
  line-height rem(12)
127
128
  background-position center top
128
129
  background-size rem(24)
130
+ color color var(--secondaryTextColor)
129
131
 
130
132
  &.is-active
131
133
  &:hover
@@ -77,7 +77,17 @@ $app
77
77
  overflow-x hidden
78
78
  overflow-y auto
79
79
 
80
- +medium-screen()
80
+ &--rounded
81
+ +medium-screen('min') // desktop only
82
+ main
83
+ background-color var(--defaultBackgroundColor)
84
+
85
+ & > [role=main]
86
+ margin 1rem 1rem 1rem 0
87
+ border-radius 1rem
88
+ background-color var(--paperBackgroundColor)
89
+
90
+ +medium-screen() // mobile + tablet
81
91
  display block
82
92
 
83
93
  // iPhone X environment tweak
@@ -122,7 +132,7 @@ $app-2panes-sticky
122
132
  main > [role=main]
123
133
  height auto
124
134
 
125
- +medium-screen()
135
+ +medium-screen() // mobile + tablet
126
136
  > aside
127
137
  position fixed
128
138
  bottom 0
@@ -10,11 +10,16 @@
10
10
 
11
11
  $sidebar
12
12
  width rem(220)
13
- border-right rem(1) solid var(--dividerColor)
14
13
  background-color var(--defaultBackgroundColor)
15
14
  overflow-y auto
16
15
  overflow-x hidden
17
16
 
17
+ &--border
18
+ border-right rem(1) solid var(--dividerColor)
19
+
20
+ &--large
21
+ width rem(236)
22
+
18
23
  +medium-screen()
19
24
  justify-content space-between
20
25
  border 0
@@ -1,3 +1,7 @@
1
1
  import Fab from '@material-ui/core/Fab';
2
+ import { isTwakeTheme } from "cozy-ui/transpiled/react/helpers/isTwakeTheme";
3
+ Fab.defaultProps = {
4
+ size: isTwakeTheme() ? 'medium' : 'large'
5
+ };
2
6
  export default Fab;
3
7
  export { default as ExtendableFab } from './ExtendableFab';