cozy-ui 130.8.1 → 130.10.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/package.json +1 -1
  3. package/react/CozyDialogs/SpecificDialogs/Readme.md +26 -0
  4. package/react/CozyDialogs/SpecificDialogs/ShortcutDialog.jsx +120 -0
  5. package/react/CozyDialogs/SpecificDialogs/helpers/shortcuts.js +61 -0
  6. package/react/CozyDialogs/SpecificDialogs/index.jsx +1 -0
  7. package/react/CozyDialogs/SpecificDialogs/locales/en.json +14 -0
  8. package/react/CozyDialogs/SpecificDialogs/locales/fr.json +14 -0
  9. package/react/CozyDialogs/SpecificDialogs/locales/ru.json +15 -1
  10. package/react/CozyDialogs/SpecificDialogs/locales/vi.json +15 -1
  11. package/react/CozyDialogs/index.jsx +2 -1
  12. package/react/EditBadge/EditMenu.jsx +102 -0
  13. package/react/EditBadge/Readme.md +22 -0
  14. package/react/EditBadge/StatusWrapper.jsx +32 -0
  15. package/react/EditBadge/helpers.js +102 -0
  16. package/react/EditBadge/index.jsx +81 -0
  17. package/react/EditBadge/locales/en.json +18 -0
  18. package/react/EditBadge/locales/fr.json +18 -0
  19. package/react/EditBadge/locales/index.js +11 -0
  20. package/react/EditBadge/locales/ru.json +18 -0
  21. package/react/EditBadge/locales/vi.json +18 -0
  22. package/react/providers/DemoProvider.jsx +6 -3
  23. package/react/utils/react.js +14 -1
  24. package/transpiled/react/CozyDialogs/SpecificDialogs/ShortcutDialog.d.ts +2 -0
  25. package/transpiled/react/CozyDialogs/SpecificDialogs/ShortcutDialog.js +114 -0
  26. package/transpiled/react/CozyDialogs/SpecificDialogs/helpers/shortcuts.d.ts +13 -0
  27. package/transpiled/react/CozyDialogs/SpecificDialogs/helpers/shortcuts.js +83 -0
  28. package/transpiled/react/CozyDialogs/SpecificDialogs/index.d.ts +1 -0
  29. package/transpiled/react/CozyDialogs/SpecificDialogs/index.js +2 -1
  30. package/transpiled/react/CozyDialogs/SpecificDialogs/withSpecificDialogsLocales.js +56 -0
  31. package/transpiled/react/CozyDialogs/index.d.ts +1 -1
  32. package/transpiled/react/CozyDialogs/index.js +1 -1
  33. package/transpiled/react/EditBadge/EditMenu.d.ts +11 -0
  34. package/transpiled/react/EditBadge/EditMenu.js +93 -0
  35. package/transpiled/react/EditBadge/StatusWrapper.d.ts +9 -0
  36. package/transpiled/react/EditBadge/StatusWrapper.js +44 -0
  37. package/transpiled/react/EditBadge/helpers.d.ts +20 -0
  38. package/transpiled/react/EditBadge/helpers.js +153 -0
  39. package/transpiled/react/EditBadge/index.d.ts +23 -0
  40. package/transpiled/react/EditBadge/index.js +89 -0
  41. package/transpiled/react/EditBadge/locales/index.d.ts +10 -0
  42. package/transpiled/react/EditBadge/locales/index.js +78 -0
  43. package/transpiled/react/providers/DemoProvider.js +3 -2
  44. package/transpiled/react/utils/react.d.ts +1 -0
  45. package/transpiled/react/utils/react.js +19 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ # [130.10.0](https://github.com/cozy/cozy-ui/compare/v130.9.0...v130.10.0) (2025-10-16)
2
+
3
+
4
+ ### Features
5
+
6
+ * Add EditBadge component ([47e98d2](https://github.com/cozy/cozy-ui/commit/47e98d2))
7
+ * **EditBadge:** We can now pass props to the main Badge component ([98eb169](https://github.com/cozy/cozy-ui/commit/98eb169))
8
+ * **Utils/React:** Add `AddPropsToChildren` function ([1724619](https://github.com/cozy/cozy-ui/commit/1724619))
9
+
10
+ # [130.9.0](https://github.com/cozy/cozy-ui/compare/v130.8.1...v130.9.0) (2025-10-16)
11
+
12
+
13
+ ### Features
14
+
15
+ * Add AlertProvider to DemoProvider ([1fea901](https://github.com/cozy/cozy-ui/commit/1fea901))
16
+ * Add ShortcutDialog ([8c501eb](https://github.com/cozy/cozy-ui/commit/8c501eb))
17
+ * Remove useless forwardRef ([7b5cf9b](https://github.com/cozy/cozy-ui/commit/7b5cf9b))
18
+
1
19
  ## [130.8.1](https://github.com/cozy/cozy-ui/compare/v130.8.0...v130.8.1) (2025-10-15)
2
20
 
3
21
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-ui",
3
- "version": "130.8.1",
3
+ "version": "130.10.0",
4
4
  "description": "Cozy apps UI SDK",
5
5
  "main": "./index.js",
6
6
  "bin": {
@@ -102,3 +102,29 @@ const onAuthentification = () => {
102
102
  )}
103
103
  </Variants>
104
104
  ```
105
+
106
+ ### ShortcutDialog dialog
107
+
108
+ ⚠️ Must be used within AlertProvider.
109
+
110
+ ```jsx
111
+ import { useState } from 'react'
112
+
113
+ import DemoProvider from 'cozy-ui/docs/components/DemoProvider'
114
+ import { ShortcutDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
115
+ import Buttons from 'cozy-ui/transpiled/react/Buttons'
116
+
117
+ const [open, setOpen] = useState(isTesting())
118
+
119
+ ;
120
+
121
+ <DemoProvider>
122
+ { open && (
123
+ <ShortcutDialog
124
+ onClose={() => { setOpen(false)}}
125
+ onSave={(...args) => { console.log('onSave called with args', args) }}
126
+ />
127
+ )}
128
+ <Buttons onClick={() => { setOpen(true) }} label="Open ShortcutDialog" />
129
+ </DemoProvider>
130
+ ```
@@ -0,0 +1,120 @@
1
+ import PropTypes from 'prop-types'
2
+ import React, { useState } from 'react'
3
+
4
+ import {
5
+ checkAndSaveShortcut,
6
+ makeHumanReadableFileName
7
+ } from './helpers/shortcuts'
8
+ import withSpecificDialogsLocales from './withSpecificDialogsLocales'
9
+ import { ConfirmDialog } from '..'
10
+ import Button from '../../Buttons'
11
+ import Stack from '../../Stack'
12
+ import TextField from '../../TextField'
13
+ import { useAlert } from '../../providers/Alert'
14
+ import { useI18n } from '../../providers/I18n'
15
+
16
+ const ENTER_KEY = 13
17
+
18
+ const ShortcutDialog = ({ shortcut, onSave, onClose }) => {
19
+ const { t } = useI18n()
20
+ const { showAlert } = useAlert()
21
+
22
+ const initialName = makeHumanReadableFileName(shortcut?.name || '')
23
+ const initialUrl = shortcut?.url || ''
24
+ const isEditing = !!shortcut
25
+
26
+ const [fileName, setFilename] = useState(initialName)
27
+ const [url, setUrl] = useState(initialUrl)
28
+
29
+ const saveShortcut = () => {
30
+ checkAndSaveShortcut({
31
+ fileName,
32
+ url,
33
+ isEditing,
34
+ onSave,
35
+ onClose,
36
+ showAlert,
37
+ t
38
+ })
39
+ }
40
+
41
+ const handleKeyDown = e => {
42
+ if (e.keyCode === ENTER_KEY) {
43
+ saveShortcut()
44
+ }
45
+ }
46
+
47
+ return (
48
+ <ConfirmDialog
49
+ open={true}
50
+ title={
51
+ isEditing
52
+ ? t('shortcut-dialog.edit-title')
53
+ : t('shortcut-dialog.create-title')
54
+ }
55
+ onClose={onClose}
56
+ content={
57
+ <Stack>
58
+ <div>
59
+ <TextField
60
+ label={t('shortcut-dialog.url')}
61
+ value={url}
62
+ id="shortcuturl"
63
+ variant="outlined"
64
+ onChange={e => setUrl(e.target.value)}
65
+ onKeyDown={e => handleKeyDown(e)}
66
+ autoComplete="off"
67
+ fullWidth
68
+ margin="normal"
69
+ autoFocus
70
+ />
71
+ </div>
72
+ <div>
73
+ <TextField
74
+ label={t('shortcut-dialog.filename')}
75
+ value={fileName}
76
+ id="shortcutfilename"
77
+ variant="outlined"
78
+ onChange={e => setFilename(e.target.value)}
79
+ onKeyDown={e => handleKeyDown(e)}
80
+ autoComplete="off"
81
+ fullWidth
82
+ margin="normal"
83
+ />
84
+ </div>
85
+ </Stack>
86
+ }
87
+ actions={
88
+ <>
89
+ <Button
90
+ variant="secondary"
91
+ onClick={onClose}
92
+ label={t('shortcut-dialog.cancel')}
93
+ />
94
+ <Button
95
+ variant="primary"
96
+ label={
97
+ isEditing
98
+ ? t('shortcut-dialog.edit')
99
+ : t('shortcut-dialog.create')
100
+ }
101
+ onClick={saveShortcut}
102
+ />
103
+ </>
104
+ }
105
+ />
106
+ )
107
+ }
108
+
109
+ ShortcutDialog.propTypes = {
110
+ /** An io.cozy.files.shortcut object if we want to prefill fields */
111
+ shortcut: PropTypes.object,
112
+ /** A function called when clicking the save button with filename and url */
113
+ onSave: PropTypes.func,
114
+ /** A function called when clicking the close button */
115
+ onClose: PropTypes.func
116
+ }
117
+
118
+ ShortcutDialog.displayName = 'ShortcutDialog'
119
+
120
+ export default withSpecificDialogsLocales(ShortcutDialog)
@@ -0,0 +1,61 @@
1
+ export const isValidURL = url => {
2
+ try {
3
+ new URL(url)
4
+ return true
5
+ } catch (e) {
6
+ return false
7
+ }
8
+ }
9
+
10
+ export const makeValidUrl = str => {
11
+ if (isValidURL(str)) return str
12
+ else if (isValidURL(`https://${str}`)) return `https://${str}`
13
+ return false
14
+ }
15
+
16
+ export const makeValidFileName = fileName =>
17
+ fileName.endsWith('.url') ? fileName : fileName + '.url'
18
+
19
+ export const makeHumanReadableFileName = fileName =>
20
+ fileName.endsWith('.url') ? fileName.slice(0, -4) : fileName
21
+
22
+ export const checkAndSaveShortcut = async ({
23
+ fileName,
24
+ url,
25
+ isEditing,
26
+ onSave,
27
+ onClose,
28
+ showAlert,
29
+ t
30
+ }) => {
31
+ if (!fileName || !url) {
32
+ showAlert({ message: t('shortcut-dialog.needs-info'), severity: 'error' })
33
+ return
34
+ }
35
+
36
+ const validFileName = makeValidFileName(fileName)
37
+
38
+ const validURL = makeValidUrl(url)
39
+
40
+ if (!validURL) {
41
+ showAlert({
42
+ message: t('shortcut-dialog.url-bad-format'),
43
+ severity: 'error'
44
+ })
45
+ return
46
+ }
47
+ try {
48
+ onSave(validFileName, validURL)
49
+
50
+ showAlert({
51
+ message: isEditing
52
+ ? t('shortcut-dialog.edited')
53
+ : t('shortcut-dialog.created'),
54
+ severity: 'success'
55
+ })
56
+ } catch {
57
+ showAlert({ message: t('shortcut-dialog.errored'), severity: 'error' })
58
+ }
59
+
60
+ onClose()
61
+ }
@@ -1,3 +1,4 @@
1
1
  export { default as AllowLocationDialog } from './AllowLocationDialog'
2
2
  export { default as InstallFlagshipAppDialog } from './InstallFlagshipAppDialog'
3
3
  export { default as AuthentificationDialog } from './AuthentificationDialog'
4
+ export { default as ShortcutDialog } from './ShortcutDialog'
@@ -23,5 +23,19 @@
23
23
  "invalid_password": "Incorrect password, try again.",
24
24
  "server_error": "Something went wrong with the server. Please, reload the page."
25
25
  }
26
+ },
27
+ "shortcut-dialog": {
28
+ "create-title": "Create a shortcut",
29
+ "edit-title": "Modify a shortcut",
30
+ "filename": "Shortcut name",
31
+ "url": "URL",
32
+ "cancel": "Cancel",
33
+ "create": "Create",
34
+ "edit": "Edit",
35
+ "created": "The shortcut has been created",
36
+ "edited": "The shortcut has been modified",
37
+ "errored": "An error occurred",
38
+ "needs-info": "A shortcut needs a name and a URL",
39
+ "url-bad-format": "The entered URL is not in the correct format"
26
40
  }
27
41
  }
@@ -23,5 +23,19 @@
23
23
  "invalid_password": "Mot de passe incorrect, essayer à nouveau.",
24
24
  "server_error": "Une erreur s'est produite. Merci de recharger la page."
25
25
  }
26
+ },
27
+ "shortcut-dialog": {
28
+ "create-title": "Créer un raccourci",
29
+ "edit-title": "Modifier un raccourci",
30
+ "filename": "Nom du raccourci",
31
+ "url": "URL",
32
+ "cancel": "Annuler",
33
+ "create": "Créer",
34
+ "edit": "Modifier",
35
+ "created": "Le raccourci a été créé",
36
+ "edited": "Le raccourci a été modifié",
37
+ "errored": "Une erreur s'est produite",
38
+ "needs-info": "Un raccourci a besoin d'un nom et d'une URL",
39
+ "url-bad-format": "L'URL saisie n'est pas dans le bon format"
26
40
  }
27
41
  }
@@ -23,5 +23,19 @@
23
23
  "invalid_password": "Неверный пароль, попробуйте еще раз.",
24
24
  "server_error": "Что-то пошло не так с сервером. Пожалуйста, перезагрузите страницу."
25
25
  }
26
+ },
27
+ "shortcut-dialog": {
28
+ "create-title": "Создать ярлык",
29
+ "edit-title": "Изменить ярлык",
30
+ "filename": "Имя ярлыка",
31
+ "url": "URL",
32
+ "cancel": "Отменить",
33
+ "create": "Создать",
34
+ "edit": "Изменить",
35
+ "created": "Ярлык был создан",
36
+ "edited": "Ярлык был изменен",
37
+ "errored": "Произошла ошибка",
38
+ "needs-info": "Для ярлыка нужны имя и URL",
39
+ "url-bad-format": "Введённый URL имеет неверный формат"
26
40
  }
27
- }
41
+ }
@@ -23,5 +23,19 @@
23
23
  "invalid_password": "Mật khẩu không chính xác, hãy thử lại.",
24
24
  "server_error": "Có lỗi xảy ra với máy chủ. Vui lòng tải lại trang."
25
25
  }
26
+ },
27
+ "shortcut-dialog": {
28
+ "create-title": "Tạo lối tắt",
29
+ "edit-title": "Chỉnh sửa lối tắt",
30
+ "filename": "Tên lối tắt",
31
+ "url": "URL",
32
+ "cancel": "Hủy",
33
+ "create": "Tạo",
34
+ "edit": "Chỉnh sửa",
35
+ "created": "Lối tắt đã được tạo",
36
+ "edited": "Lối tắt đã được chỉnh sửa",
37
+ "errored": "Đã xảy ra lỗi",
38
+ "needs-info": "Lối tắt cần có tên và URL",
39
+ "url-bad-format": "URL đã nhập không đúng định dạng"
26
40
  }
27
- }
41
+ }
@@ -12,5 +12,6 @@ export { default as TopAnchoredDialog } from './TopAnchoredDialog'
12
12
  export {
13
13
  AllowLocationDialog,
14
14
  InstallFlagshipAppDialog,
15
- AuthentificationDialog
15
+ AuthentificationDialog,
16
+ ShortcutDialog
16
17
  } from './SpecificDialogs'
@@ -0,0 +1,102 @@
1
+ import React, { useRef } from 'react'
2
+
3
+ import { handleDelete, handleUpload } from './helpers'
4
+ import { locales } from './locales'
5
+ import Icon from '../Icon'
6
+ import CameraIcon from '../Icons/Camera'
7
+ import TrashIcon from '../Icons/Trash'
8
+ import ListItemIcon from '../ListItemIcon'
9
+ import ListItemText from '../ListItemText'
10
+ import Menu from '../Menu'
11
+ import MenuItem from '../MenuItem'
12
+ import { useAlert } from '../providers/Alert'
13
+ import { useI18n, useExtendI18n } from '../providers/I18n'
14
+
15
+ const EditMenu = ({
16
+ anchorRef,
17
+ status,
18
+ showMenu,
19
+ setShowMenu,
20
+ setStatus,
21
+ setTimestamp,
22
+ onUpload,
23
+ onDelete
24
+ }) => {
25
+ useExtendI18n(locales)
26
+ const { t } = useI18n()
27
+ const { showAlert } = useAlert()
28
+
29
+ const fileInputRef = useRef(null)
30
+
31
+ return (
32
+ <>
33
+ <input
34
+ className="u-dn"
35
+ type="file"
36
+ ref={fileInputRef}
37
+ accept="image/*"
38
+ onChange={event =>
39
+ handleUpload({
40
+ event,
41
+ t,
42
+ fileInputRef,
43
+ status,
44
+ onUpload,
45
+ setStatus,
46
+ setTimestamp,
47
+ setShowMenu,
48
+ showAlert
49
+ })
50
+ }
51
+ />
52
+ {showMenu && (
53
+ <Menu
54
+ open
55
+ anchorEl={anchorRef}
56
+ getContentAnchorEl={null}
57
+ anchorOrigin={{
58
+ vertical: 'bottom',
59
+ horizontal: 'left'
60
+ }}
61
+ transformOrigin={{
62
+ vertical: 'top',
63
+ horizontal: 'left'
64
+ }}
65
+ onClose={() => setShowMenu(false)}
66
+ >
67
+ <MenuItem
68
+ onClick={() => {
69
+ setShowMenu(false)
70
+ fileInputRef.current.click() // triggers onChange of the input
71
+ }}
72
+ >
73
+ <ListItemIcon>
74
+ <Icon icon={CameraIcon} />
75
+ </ListItemIcon>
76
+ <ListItemText primary={t('EditBadge.menu.update')} />
77
+ </MenuItem>
78
+ <MenuItem
79
+ onClick={() =>
80
+ handleDelete({
81
+ t,
82
+ status,
83
+ onDelete,
84
+ setShowMenu,
85
+ setStatus,
86
+ setTimestamp,
87
+ showAlert
88
+ })
89
+ }
90
+ >
91
+ <ListItemIcon>
92
+ <Icon icon={TrashIcon} />
93
+ </ListItemIcon>
94
+ <ListItemText primary={t('EditBadge.menu.delete')} />
95
+ </MenuItem>
96
+ </Menu>
97
+ )}
98
+ </>
99
+ )
100
+ }
101
+
102
+ export default EditMenu
@@ -0,0 +1,22 @@
1
+ ⚠️ Must be used within `AlertProvider`.
2
+
3
+ ```jsx
4
+ import DemoProvider from 'cozy-ui/docs/components/DemoProvider'
5
+ import AlertProvider from 'cozy-ui/transpiled/react/providers/Alert'
6
+ import EditBadge from 'cozy-ui/transpiled/react/EditBadge'
7
+ import Avatar from 'cozy-ui/transpiled/react/Avatar'
8
+
9
+ ;
10
+
11
+ <DemoProvider>
12
+ <AlertProvider>
13
+ <EditBadge
14
+ src={timestamp => ""}
15
+ onUpload={file => {}}
16
+ onDelete={() => {}}
17
+ >
18
+ <Avatar size={94} alt="Avatar" />
19
+ </EditBadge>
20
+ </AlertProvider>
21
+ </DemoProvider>
22
+ ```
@@ -0,0 +1,32 @@
1
+ import cx from 'classnames'
2
+ import React from 'react'
3
+
4
+ import Spinner from '../Spinner'
5
+ import { AddPropsToChildren } from '../utils/react'
6
+
7
+ const StatusWrapper = ({ src, status, setStatus, timestamp, children }) => {
8
+ if (status === 'LOADING') {
9
+ return (
10
+ <>
11
+ {AddPropsToChildren(children, childProps => ({
12
+ className: cx(childProps.className, 'u-o-50')
13
+ }))}
14
+ <Spinner className="u-m-0" middle size="large" />
15
+ </>
16
+ )
17
+ }
18
+
19
+ if (status === 'PRESENT') {
20
+ return AddPropsToChildren(children, () => ({
21
+ key: timestamp,
22
+ src,
23
+ onError: () => setStatus('ABSENT')
24
+ }))
25
+ }
26
+
27
+ return AddPropsToChildren(children, () => ({
28
+ key: timestamp
29
+ }))
30
+ }
31
+
32
+ export default StatusWrapper
@@ -0,0 +1,102 @@
1
+ const MAX_FILE_SIZE = 5 * 1024 * 1024
2
+ const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']
3
+
4
+ export const handleUpload = async ({
5
+ event,
6
+ t,
7
+ fileInputRef,
8
+ status,
9
+ onUpload,
10
+ setStatus,
11
+ setTimestamp,
12
+ setShowMenu,
13
+ showAlert
14
+ }) => {
15
+ const file = event.target.files[0]
16
+ if (!file) return
17
+
18
+ if (file.size > MAX_FILE_SIZE) {
19
+ showAlert({
20
+ message: t('EditBadge.upload.file-size'),
21
+ severity: 'error'
22
+ })
23
+ return
24
+ }
25
+
26
+ if (!ALLOWED_TYPES.includes(file.type)) {
27
+ showAlert({
28
+ message: t('EditBadge.upload.file-type'),
29
+ severity: 'error'
30
+ })
31
+ return
32
+ }
33
+
34
+ const controller = new AbortController()
35
+ const timeoutId = setTimeout(() => controller.abort(), 30000)
36
+
37
+ const previouStatus = status
38
+ setStatus('LOADING')
39
+
40
+ try {
41
+ await onUpload(file)
42
+ clearTimeout(timeoutId)
43
+
44
+ const newTimestamp = Date.now()
45
+ setStatus('PRESENT')
46
+ setTimestamp(newTimestamp)
47
+ showAlert({
48
+ message: t('EditBadge.upload.success'),
49
+ severity: 'success'
50
+ })
51
+ } catch (error) {
52
+ clearTimeout(timeoutId)
53
+ setStatus(previouStatus)
54
+ showAlert({
55
+ message: t('EditBadge.upload.error'),
56
+ severity: 'error'
57
+ })
58
+ } finally {
59
+ setShowMenu(false)
60
+ if (fileInputRef.current) {
61
+ fileInputRef.current.value = ''
62
+ }
63
+ }
64
+ }
65
+
66
+ export const handleDelete = async ({
67
+ t,
68
+ status,
69
+ onDelete,
70
+ setShowMenu,
71
+ setStatus,
72
+ setTimestamp,
73
+ showAlert
74
+ }) => {
75
+ setShowMenu(false)
76
+
77
+ const controller = new AbortController()
78
+ const timeoutId = setTimeout(() => controller.abort(), 30000)
79
+ const previousStatus = status
80
+ setStatus('LOADING')
81
+
82
+ try {
83
+ await onDelete()
84
+ clearTimeout(timeoutId)
85
+
86
+ const checkTimestamp = Date.now()
87
+
88
+ setStatus('ABSENT')
89
+ setTimestamp(checkTimestamp)
90
+ showAlert({
91
+ message: t('EditBadge.delete.success'),
92
+ severity: 'success'
93
+ })
94
+ } catch (error) {
95
+ clearTimeout(timeoutId)
96
+ setStatus(previousStatus)
97
+ showAlert({
98
+ message: t('EditBadge.delete.error'),
99
+ severity: 'error'
100
+ })
101
+ }
102
+ }
@@ -0,0 +1,81 @@
1
+ import PropTypes from 'prop-types'
2
+ import React, { useState, useRef } from 'react'
3
+
4
+ import EditMenu from './EditMenu'
5
+ import StatusWrapper from './StatusWrapper'
6
+ import Badge from '../Badge'
7
+ import Button from '../Buttons'
8
+ import Icon from '../Icon'
9
+ import PenIcon from '../Icons/Pen'
10
+
11
+ const EditBadge = ({
12
+ src,
13
+ onUpload,
14
+ onDelete,
15
+ anchorOrigin,
16
+ children,
17
+ ...props
18
+ }) => {
19
+ const [showMenu, setShowMenu] = useState(false)
20
+ const [status, setStatus] = useState('PRESENT') // PRESENT || ABSENT || LOADING
21
+ const [timestamp, setTimestamp] = useState(Date.now())
22
+
23
+ const menuAnchorRef = useRef(null)
24
+
25
+ return (
26
+ <Badge
27
+ {...props}
28
+ anchorOrigin={anchorOrigin}
29
+ badgeContent={
30
+ <>
31
+ <Button
32
+ ref={menuAnchorRef}
33
+ component="div"
34
+ className="u-miw-auto u-w-2-half u-h-2-half u-bdrs-circle"
35
+ classes={{ label: 'u-flex u-w-auto' }}
36
+ style={{
37
+ outline: '4px solid var(--paperBackgroundColor)'
38
+ }}
39
+ label={<Icon icon={PenIcon} />}
40
+ size="small"
41
+ onClick={() => setShowMenu(v => !v)}
42
+ />
43
+ <EditMenu
44
+ anchorRef={menuAnchorRef.current}
45
+ status={status}
46
+ showMenu={showMenu}
47
+ setShowMenu={setShowMenu}
48
+ setStatus={setStatus}
49
+ setTimestamp={setTimestamp}
50
+ onUpload={onUpload}
51
+ onDelete={onDelete}
52
+ />
53
+ </>
54
+ }
55
+ >
56
+ <StatusWrapper
57
+ src={src(timestamp)}
58
+ status={status}
59
+ setStatus={setStatus}
60
+ timestamp={timestamp}
61
+ >
62
+ {children}
63
+ </StatusWrapper>
64
+ </Badge>
65
+ )
66
+ }
67
+
68
+ EditBadge.defaultProps = {
69
+ anchorOrigin: {
70
+ vertical: 'bottom',
71
+ horizontal: 'right'
72
+ }
73
+ }
74
+
75
+ EditBadge.propTypes = {
76
+ src: PropTypes.func.isRequired,
77
+ onUpload: PropTypes.func.isRequired,
78
+ onDelete: PropTypes.func.isRequired
79
+ }
80
+
81
+ export default EditBadge