cozy-ui 127.11.0 → 127.13.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 +14 -0
- package/package.json +1 -1
- package/react/Contacts/GroupsSelect/GroupCreation.jsx +95 -0
- package/react/Contacts/GroupsSelect/GroupsSelect.jsx +159 -0
- package/react/Contacts/GroupsSelect/GroupsSelect.spec.jsx +248 -0
- package/react/Contacts/GroupsSelect/GroupsSelectProvider.jsx +39 -0
- package/react/Contacts/GroupsSelect/Readme.md +1 -0
- package/react/Contacts/GroupsSelect/SelectBox/Control.jsx +36 -0
- package/react/Contacts/GroupsSelect/SelectBox/EditGroupName.jsx +52 -0
- package/react/Contacts/GroupsSelect/SelectBox/Menu.jsx +26 -0
- package/react/Contacts/GroupsSelect/SelectBox/Option.jsx +67 -0
- package/react/Contacts/GroupsSelect/SelectBox/SelectContainer.jsx +15 -0
- package/react/Contacts/GroupsSelect/SelectBox/styles.styl +2 -0
- package/react/Contacts/GroupsSelect/helpers.js +25 -0
- package/react/Contacts/GroupsSelect/locales/en.json +21 -0
- package/react/Contacts/GroupsSelect/locales/fr.json +21 -0
- package/react/Contacts/GroupsSelect/locales/index.jsx +7 -0
- package/react/Contacts/GroupsSelect/styles.styl +47 -0
- package/react/Contacts/GroupsSelect/useGroupSelect.jsx +88 -0
- package/react/Contacts/Header/GroupsSelection.jsx +74 -0
- package/react/Contacts/Header/ImportDropdown.jsx +78 -0
- package/react/Contacts/Header/Readme.md +40 -0
- package/react/Contacts/Header/SearchInput.jsx +19 -0
- package/react/Contacts/Header/index.jsx +79 -0
- package/react/Contacts/Header/locales/en.json +15 -0
- package/react/Contacts/Header/locales/fr.json +15 -0
- package/react/Contacts/Header/locales/index.jsx +7 -0
- package/react/Contacts/Readme.md +1 -0
- package/react/ContactsList/ContactCell.jsx +1 -1
- package/react/ContactsList/Contacts/ContactIdentity.jsx +5 -1
- package/react/ContactsList/ContactsList.jsx +3 -1
- package/react/ContactsList/helpers.js +11 -10
- package/react/ContactsList/helpers.spec.js +11 -2
- package/react/ContactsList/locales/en.json +6 -4
- package/react/ContactsList/locales/fr.json +7 -5
- package/transpiled/react/Contacts/GroupsSelect/GroupCreation.d.ts +3 -0
- package/transpiled/react/Contacts/GroupsSelect/GroupCreation.js +133 -0
- package/transpiled/react/Contacts/GroupsSelect/GroupsSelect.d.ts +56 -0
- package/transpiled/react/Contacts/GroupsSelect/GroupsSelect.js +181 -0
- package/transpiled/react/Contacts/GroupsSelect/GroupsSelect.spec.d.ts +22 -0
- package/transpiled/react/Contacts/GroupsSelect/GroupsSelectProvider.d.ts +5 -0
- package/transpiled/react/Contacts/GroupsSelect/GroupsSelectProvider.js +38 -0
- package/transpiled/react/Contacts/GroupsSelect/SelectBox/Control.d.ts +14 -0
- package/transpiled/react/Contacts/GroupsSelect/SelectBox/Control.js +37 -0
- package/transpiled/react/Contacts/GroupsSelect/SelectBox/EditGroupName.d.ts +7 -0
- package/transpiled/react/Contacts/GroupsSelect/SelectBox/EditGroupName.js +71 -0
- package/transpiled/react/Contacts/GroupsSelect/SelectBox/Menu.d.ts +7 -0
- package/transpiled/react/Contacts/GroupsSelect/SelectBox/Menu.js +34 -0
- package/transpiled/react/Contacts/GroupsSelect/SelectBox/Option.d.ts +18 -0
- package/transpiled/react/Contacts/GroupsSelect/SelectBox/Option.js +66 -0
- package/transpiled/react/Contacts/GroupsSelect/SelectBox/SelectContainer.d.ts +2 -0
- package/transpiled/react/Contacts/GroupsSelect/SelectBox/SelectContainer.js +12 -0
- package/transpiled/react/Contacts/GroupsSelect/helpers.d.ts +9 -0
- package/transpiled/react/Contacts/GroupsSelect/helpers.js +30 -0
- package/transpiled/react/Contacts/GroupsSelect/locales/index.d.ts +6 -0
- package/transpiled/react/Contacts/GroupsSelect/locales/index.js +46 -0
- package/transpiled/react/Contacts/GroupsSelect/useGroupSelect.d.ts +11 -0
- package/transpiled/react/Contacts/GroupsSelect/useGroupSelect.js +179 -0
- package/transpiled/react/Contacts/Header/GroupsSelection.d.ts +7 -0
- package/transpiled/react/Contacts/Header/GroupsSelection.js +84 -0
- package/transpiled/react/Contacts/Header/ImportDropdown.d.ts +4 -0
- package/transpiled/react/Contacts/Header/ImportDropdown.js +88 -0
- package/transpiled/react/Contacts/Header/SearchInput.d.ts +4 -0
- package/transpiled/react/Contacts/Header/SearchInput.js +24 -0
- package/transpiled/react/Contacts/Header/index.d.ts +26 -0
- package/transpiled/react/Contacts/Header/index.js +72 -0
- package/transpiled/react/Contacts/Header/locales/index.d.ts +6 -0
- package/transpiled/react/Contacts/Header/locales/index.js +34 -0
- package/transpiled/react/ContactsList/ContactCell.js +1 -1
- package/transpiled/react/ContactsList/Contacts/ContactIdentity.js +1 -1
- package/transpiled/react/ContactsList/ContactsList.js +4 -1
- package/transpiled/react/ContactsList/helpers.js +10 -10
- package/transpiled/react/ContactsList/locales/withContactsListLocales.js +12 -8
- package/transpiled/react/stylesheet.css +1 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import PropTypes from 'prop-types'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
import EditGroupName from './EditGroupName'
|
|
5
|
+
import { ActionsOption, Option as DefaultOption } from '../../../SelectBox'
|
|
6
|
+
|
|
7
|
+
const Option = props => {
|
|
8
|
+
const { name: groupName, id: groupId, withNoAction } = props.data
|
|
9
|
+
const {
|
|
10
|
+
editedGroupId,
|
|
11
|
+
setEditedGroupId,
|
|
12
|
+
deleteGroup,
|
|
13
|
+
renameGroup,
|
|
14
|
+
withCheckbox
|
|
15
|
+
} = props.selectProps
|
|
16
|
+
|
|
17
|
+
if (editedGroupId === groupId) {
|
|
18
|
+
return (
|
|
19
|
+
<EditGroupName
|
|
20
|
+
groupId={groupId}
|
|
21
|
+
groupName={groupName}
|
|
22
|
+
setEditedGroupId={setEditedGroupId}
|
|
23
|
+
renameGroup={renameGroup}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (withNoAction) {
|
|
29
|
+
return <DefaultOption {...props} />
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<ActionsOption
|
|
34
|
+
{...props}
|
|
35
|
+
withCheckbox={withCheckbox}
|
|
36
|
+
actions={[
|
|
37
|
+
{
|
|
38
|
+
icon: 'pen',
|
|
39
|
+
onClick: ({ data }) => setEditedGroupId(data.id),
|
|
40
|
+
iconProps: {
|
|
41
|
+
'data-testid': `ActionsOption_${props.children}-icon_pen`
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
icon: 'trash',
|
|
46
|
+
onClick: ({ data }) => deleteGroup(data)
|
|
47
|
+
}
|
|
48
|
+
]}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Option.propTypes = {
|
|
54
|
+
selectProps: PropTypes.shape({
|
|
55
|
+
editedGroupId: PropTypes.string.isRequired,
|
|
56
|
+
deleteGroup: PropTypes.func.isRequired,
|
|
57
|
+
setEditedGroupId: PropTypes.func.isRequired,
|
|
58
|
+
withCheckbox: PropTypes.bool
|
|
59
|
+
}),
|
|
60
|
+
data: PropTypes.shape({
|
|
61
|
+
name: PropTypes.string.isRequired,
|
|
62
|
+
id: PropTypes.string,
|
|
63
|
+
withNoAction: PropTypes.bool
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default Option
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
import { components } from '../../../SelectBox'
|
|
5
|
+
|
|
6
|
+
const SelectContainer = props => {
|
|
7
|
+
return (
|
|
8
|
+
<components.SelectContainer
|
|
9
|
+
{...props}
|
|
10
|
+
className={classNames(props.className, 'react-select__custom__container')}
|
|
11
|
+
/>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default SelectContainer
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const isExistingGroup = (groupsAlreadyCreated, groupToCreate) => {
|
|
2
|
+
const isNameAlreadyUsed =
|
|
3
|
+
groupsAlreadyCreated.find(
|
|
4
|
+
group => group.name.toLowerCase() === groupToCreate.name.toLowerCase()
|
|
5
|
+
) !== undefined
|
|
6
|
+
|
|
7
|
+
return isNameAlreadyUsed
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns the group defined as default in the group filter
|
|
12
|
+
*/
|
|
13
|
+
export const defaultSelectedGroup = {
|
|
14
|
+
name: 'Contacts.GroupsSelect.filter.all-groups',
|
|
15
|
+
withNoAction: true
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Returns the translated group defined as default in the group filter
|
|
20
|
+
* @param {function} t - Translate
|
|
21
|
+
*/
|
|
22
|
+
export const translatedDefaultSelectedGroup = t => ({
|
|
23
|
+
...defaultSelectedGroup,
|
|
24
|
+
name: t(defaultSelectedGroup.name)
|
|
25
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Contacts": {
|
|
3
|
+
"GroupsSelect": {
|
|
4
|
+
"create": "Create a group",
|
|
5
|
+
"name": "Name of the group",
|
|
6
|
+
"already_exists": "The group %{name} already exists.",
|
|
7
|
+
"manage": "Groups",
|
|
8
|
+
"filter": {
|
|
9
|
+
"all-groups": "All groups"
|
|
10
|
+
},
|
|
11
|
+
"created": {
|
|
12
|
+
"success": "The group has been created correctly.",
|
|
13
|
+
"error": "The group has not been created."
|
|
14
|
+
},
|
|
15
|
+
"renamed": {
|
|
16
|
+
"success": "The group has been renamed correctly.",
|
|
17
|
+
"error": "The group has not been renamed."
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Contacts": {
|
|
3
|
+
"GroupsSelect": {
|
|
4
|
+
"create": "Créer un groupe",
|
|
5
|
+
"name": "Nom du groupe",
|
|
6
|
+
"already_exists": "Le groupe %{name} existe déjà.",
|
|
7
|
+
"manage": "Groupes",
|
|
8
|
+
"filter": {
|
|
9
|
+
"all-groups": "Tous les groupes"
|
|
10
|
+
},
|
|
11
|
+
"created": {
|
|
12
|
+
"success": "Le groupe a bien été créé.",
|
|
13
|
+
"error": "Le groupe n'a pas été créé."
|
|
14
|
+
},
|
|
15
|
+
"renamed": {
|
|
16
|
+
"success": "Le groupe a bien été renommé.",
|
|
17
|
+
"error": "Le groupe n'a pas été renommé."
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
@require 'cozy-ui'
|
|
2
|
+
|
|
3
|
+
.container
|
|
4
|
+
height rem(48)
|
|
5
|
+
display flex
|
|
6
|
+
align-items center
|
|
7
|
+
color var(--primaryTextColor)
|
|
8
|
+
|
|
9
|
+
.contact-group-creation-divider
|
|
10
|
+
width 100%
|
|
11
|
+
border-top 1px solid var(--dividerColor)
|
|
12
|
+
|
|
13
|
+
.contact-group-create-div-icon
|
|
14
|
+
display flex
|
|
15
|
+
align-items center
|
|
16
|
+
flex 1
|
|
17
|
+
|
|
18
|
+
/*since we keep the existing style of
|
|
19
|
+
react-select for the menu and we only
|
|
20
|
+
want to override width & zindex... important
|
|
21
|
+
is important. Right positionning is just set,
|
|
22
|
+
it overrides nothing so there's no need for
|
|
23
|
+
!important */
|
|
24
|
+
:global(.react-select__menu) // @stylint ignore Naming imposed by react-select
|
|
25
|
+
width rem(384) !important
|
|
26
|
+
z-index 100000 !important
|
|
27
|
+
right 0
|
|
28
|
+
|
|
29
|
+
+small-screen()
|
|
30
|
+
top auto !important
|
|
31
|
+
width 100% !important
|
|
32
|
+
|
|
33
|
+
:global(.react-select__container) // @stylint ignore Naming imposed by react-select
|
|
34
|
+
display inline-block
|
|
35
|
+
vertical-align middle
|
|
36
|
+
|
|
37
|
+
:global(.react-select__menu-notice--no-options) // @stylint ignore Naming imposed by react-select
|
|
38
|
+
color var(--primaryTextColor)
|
|
39
|
+
margin-left .5rem !important
|
|
40
|
+
height rem(48)
|
|
41
|
+
display flex
|
|
42
|
+
align-items center
|
|
43
|
+
|
|
44
|
+
:global(.react-select__menu-list) // @stylint ignore Naming imposed by react-select
|
|
45
|
+
text-align initial
|
|
46
|
+
+small-screen()
|
|
47
|
+
max-width inherit !important
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import get from 'lodash/get'
|
|
2
|
+
|
|
3
|
+
import { useSelectedGroup } from './GroupsSelectProvider'
|
|
4
|
+
import { isExistingGroup } from './helpers'
|
|
5
|
+
import { locales } from './locales'
|
|
6
|
+
import { useAlert } from '../../providers/Alert'
|
|
7
|
+
import { useI18n, useExtendI18n } from '../../providers/I18n'
|
|
8
|
+
|
|
9
|
+
const useGroupsSelect = ({
|
|
10
|
+
allGroups,
|
|
11
|
+
onGroupCreated,
|
|
12
|
+
client,
|
|
13
|
+
onGroupCreate,
|
|
14
|
+
onGroupUpdate
|
|
15
|
+
}) => {
|
|
16
|
+
const { selectedGroup, setSelectedGroup } = useSelectedGroup()
|
|
17
|
+
const { showAlert } = useAlert()
|
|
18
|
+
useExtendI18n(locales)
|
|
19
|
+
const { t } = useI18n()
|
|
20
|
+
|
|
21
|
+
const createGroupSelf = async group => {
|
|
22
|
+
if (!group.name) return
|
|
23
|
+
|
|
24
|
+
if (isExistingGroup(allGroups, group)) {
|
|
25
|
+
return showAlert({
|
|
26
|
+
severity: 'error',
|
|
27
|
+
message: t('Contacts.GroupsSelect.already_exists', {
|
|
28
|
+
name: group.name
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const { data: createdGroup } = await onGroupCreate(client, group)
|
|
35
|
+
if (onGroupCreated) {
|
|
36
|
+
onGroupCreated(createdGroup)
|
|
37
|
+
}
|
|
38
|
+
return showAlert({
|
|
39
|
+
severity: 'success',
|
|
40
|
+
message: t('Contacts.GroupsSelect.created.success')
|
|
41
|
+
})
|
|
42
|
+
} catch {
|
|
43
|
+
return showAlert({
|
|
44
|
+
severity: 'error',
|
|
45
|
+
message: t('Contacts.GroupsSelect.created.error')
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const renameGroupSelf = async (groupId, newName) => {
|
|
51
|
+
const group = allGroups.find(group => group.id === groupId)
|
|
52
|
+
const allOtherGroups = allGroups.filter(group => group.id !== groupId)
|
|
53
|
+
const isRenamedGroupSelected =
|
|
54
|
+
get(group, '_id') === get(selectedGroup, '_id')
|
|
55
|
+
|
|
56
|
+
if (isExistingGroup(allOtherGroups, { name: newName })) {
|
|
57
|
+
return showAlert({
|
|
58
|
+
severity: 'error',
|
|
59
|
+
message: t('Contacts.GroupsSelect.already_exists', {
|
|
60
|
+
name: newName
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const { data } = await onGroupUpdate(client, { ...group, name: newName })
|
|
67
|
+
if (isRenamedGroupSelected) {
|
|
68
|
+
setSelectedGroup(data)
|
|
69
|
+
}
|
|
70
|
+
return showAlert({
|
|
71
|
+
severity: 'success',
|
|
72
|
+
message: t('Contacts.GroupsSelect.renamed.success')
|
|
73
|
+
})
|
|
74
|
+
} catch {
|
|
75
|
+
return showAlert({
|
|
76
|
+
severity: 'error',
|
|
77
|
+
message: t('Contacts.GroupsSelect.renamed.error')
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
createGroup: createGroupSelf,
|
|
84
|
+
renameGroup: renameGroupSelf
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export default useGroupsSelect
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { locales } from './locales'
|
|
4
|
+
import { ControlDefault } from '../../SelectBox'
|
|
5
|
+
import { useBreakpoints } from '../../providers/Breakpoints'
|
|
6
|
+
import { useI18n, useExtendI18n } from '../../providers/I18n'
|
|
7
|
+
import GroupsSelect from '../GroupsSelect/GroupsSelect'
|
|
8
|
+
import { useSelectedGroup } from '../GroupsSelect/GroupsSelectProvider'
|
|
9
|
+
import { translatedDefaultSelectedGroup } from '../GroupsSelect/helpers'
|
|
10
|
+
|
|
11
|
+
const setGroupsSelectOptions = (allGroups, defaultSelectedGroup) =>
|
|
12
|
+
allGroups.length > 0 ? [defaultSelectedGroup].concat(allGroups) : allGroups
|
|
13
|
+
|
|
14
|
+
const useGroupsSelectCustomStyles = () => {
|
|
15
|
+
const { isMobile } = useBreakpoints()
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
container: base => ({
|
|
19
|
+
...base,
|
|
20
|
+
width: isMobile ? '100%' : '50%'
|
|
21
|
+
}),
|
|
22
|
+
noOptionsMessage: base => ({ ...base, textAlign: 'left' })
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ControlDefaultWithTestId = ({ ...props }) => {
|
|
27
|
+
return (
|
|
28
|
+
<ControlDefault
|
|
29
|
+
{...props}
|
|
30
|
+
innerProps={{
|
|
31
|
+
...props.innerProps,
|
|
32
|
+
'data-testid': 'selectBox-controlDefault',
|
|
33
|
+
className: 'u-bdrs-4'
|
|
34
|
+
}}
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const GroupsSelection = ({
|
|
40
|
+
allGroups,
|
|
41
|
+
onGroupCreate,
|
|
42
|
+
onGroupUpdate,
|
|
43
|
+
onGroupDelete
|
|
44
|
+
}) => {
|
|
45
|
+
useExtendI18n(locales)
|
|
46
|
+
const { t } = useI18n()
|
|
47
|
+
const { selectedGroup, setSelectedGroup } = useSelectedGroup()
|
|
48
|
+
|
|
49
|
+
const groupsSelectOptions = setGroupsSelectOptions(
|
|
50
|
+
allGroups,
|
|
51
|
+
translatedDefaultSelectedGroup(t)
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
const groupsSelectCustomStyles = useGroupsSelectCustomStyles()
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<GroupsSelect
|
|
58
|
+
allGroups={groupsSelectOptions}
|
|
59
|
+
value={selectedGroup}
|
|
60
|
+
noOptionsMessage={() => t('Contacts.Header.filter.no-group')}
|
|
61
|
+
styles={groupsSelectCustomStyles}
|
|
62
|
+
closeMenuOnSelect={true}
|
|
63
|
+
components={{
|
|
64
|
+
Control: ControlDefaultWithTestId
|
|
65
|
+
}}
|
|
66
|
+
onChange={setSelectedGroup}
|
|
67
|
+
onGroupCreate={onGroupCreate}
|
|
68
|
+
onGroupUpdate={onGroupUpdate}
|
|
69
|
+
onGroupDelete={onGroupDelete}
|
|
70
|
+
/>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export default GroupsSelection
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React, { useState, useRef } from 'react'
|
|
2
|
+
|
|
3
|
+
import { useClient, generateWebLink } from 'cozy-client'
|
|
4
|
+
import { CONTACTS_DOCTYPE } from 'cozy-client/dist/models/contact'
|
|
5
|
+
|
|
6
|
+
import { locales } from './locales'
|
|
7
|
+
import AppIcon from '../../AppIcon'
|
|
8
|
+
import AppLinker from '../../AppLinker'
|
|
9
|
+
import Button from '../../Buttons'
|
|
10
|
+
import Icon from '../../Icon'
|
|
11
|
+
import BottomIcon from '../../Icons/Bottom'
|
|
12
|
+
import TeamIcon from '../../Icons/Team'
|
|
13
|
+
import Link from '../../Link'
|
|
14
|
+
import ActionMenu, { ActionMenuItem } from '../../deprecated/ActionMenu'
|
|
15
|
+
import { useI18n, useExtendI18n } from '../../providers/I18n'
|
|
16
|
+
|
|
17
|
+
const ImportDropdown = ({ onContactImport }) => {
|
|
18
|
+
useExtendI18n(locales)
|
|
19
|
+
const { t } = useI18n()
|
|
20
|
+
const client = useClient()
|
|
21
|
+
const anchorRef = useRef()
|
|
22
|
+
const [showMenu, setShowMenu] = useState(false)
|
|
23
|
+
|
|
24
|
+
const storeURL = generateWebLink({
|
|
25
|
+
cozyUrl: client.getStackClient().uri,
|
|
26
|
+
hash: `discover/?type=konnector&doctype=${CONTACTS_DOCTYPE}`,
|
|
27
|
+
pathname: '/',
|
|
28
|
+
slug: 'store',
|
|
29
|
+
subDomainType: client.getInstanceOptions().subdomain
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<>
|
|
34
|
+
<Button
|
|
35
|
+
ref={anchorRef}
|
|
36
|
+
variant="secondary"
|
|
37
|
+
label={t('Contacts.Header.import.title')}
|
|
38
|
+
endIcon={<Icon icon={BottomIcon} />}
|
|
39
|
+
fullWidth
|
|
40
|
+
onClick={() => setShowMenu(v => !v)}
|
|
41
|
+
/>
|
|
42
|
+
|
|
43
|
+
{showMenu && (
|
|
44
|
+
<ActionMenu
|
|
45
|
+
anchorElRef={anchorRef}
|
|
46
|
+
popperOptions={{ placement: 'bottom-end' }}
|
|
47
|
+
onClose={() => setShowMenu(false)}
|
|
48
|
+
>
|
|
49
|
+
<ActionMenuItem
|
|
50
|
+
left={<Icon icon={TeamIcon} />}
|
|
51
|
+
onClick={onContactImport}
|
|
52
|
+
>
|
|
53
|
+
{t('Contacts.Header.import.vcard')}
|
|
54
|
+
</ActionMenuItem>
|
|
55
|
+
|
|
56
|
+
<AppLinker app={{ slug: 'store' }} href={storeURL}>
|
|
57
|
+
{({ onClick, href }) => (
|
|
58
|
+
<ActionMenuItem
|
|
59
|
+
left={<AppIcon app="store" className="u-h-1 u-w-1" />}
|
|
60
|
+
onClick={onClick}
|
|
61
|
+
>
|
|
62
|
+
<Link
|
|
63
|
+
className="u-p-0"
|
|
64
|
+
href={href}
|
|
65
|
+
style={{ color: 'var(--primaryTextColor)' }}
|
|
66
|
+
>
|
|
67
|
+
{t('Contacts.Header.import.store')}
|
|
68
|
+
</Link>
|
|
69
|
+
</ActionMenuItem>
|
|
70
|
+
)}
|
|
71
|
+
</AppLinker>
|
|
72
|
+
</ActionMenu>
|
|
73
|
+
)}
|
|
74
|
+
</>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default ImportDropdown
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<!-- For styleguidist, a .md file is needed in directories -->
|
|
2
|
+
|
|
3
|
+
Must be used within `SelectedGroupProvider` and `AlertProvider`.
|
|
4
|
+
|
|
5
|
+
```jsx
|
|
6
|
+
import Header from 'cozy-ui/transpiled/react/Contacts/Header'
|
|
7
|
+
import SelectedGroupProvider from 'cozy-ui/transpiled/react/Contacts/GroupsSelect/GroupsSelectProvider'
|
|
8
|
+
import AlertProvider from 'cozy-ui/transpiled/react/providers/Alert'
|
|
9
|
+
import DemoProvider from 'cozy-ui/docs/components/DemoProvider'
|
|
10
|
+
|
|
11
|
+
;
|
|
12
|
+
|
|
13
|
+
<DemoProvider>
|
|
14
|
+
<AlertProvider>
|
|
15
|
+
<SelectedGroupProvider>
|
|
16
|
+
<Header
|
|
17
|
+
allGroups={[]}
|
|
18
|
+
onContactCreate={() => {
|
|
19
|
+
console.info('onCreateContact')
|
|
20
|
+
}}
|
|
21
|
+
onContactImport={() => {
|
|
22
|
+
console.info('onImportContact')
|
|
23
|
+
}}
|
|
24
|
+
onGroupCreate={() => {
|
|
25
|
+
console.info('onGroupCreate')
|
|
26
|
+
}}
|
|
27
|
+
onGroupUpdate={() => {
|
|
28
|
+
console.info('onGroupUpdate')
|
|
29
|
+
}}
|
|
30
|
+
onGroupDelete={() => {
|
|
31
|
+
console.info('onGroupDelete')
|
|
32
|
+
}}
|
|
33
|
+
onSearch={searchValue => {
|
|
34
|
+
console.info(`onSearch with ${searchValue}`)
|
|
35
|
+
}}
|
|
36
|
+
/>
|
|
37
|
+
</SelectedGroupProvider>
|
|
38
|
+
</AlertProvider>
|
|
39
|
+
</DemoProvider>
|
|
40
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import debounce from 'lodash/debounce'
|
|
2
|
+
import React, { useMemo } from 'react'
|
|
3
|
+
|
|
4
|
+
import SearchBar from '../../SearchBar'
|
|
5
|
+
|
|
6
|
+
const SearchInput = ({ setSearchValue }) => {
|
|
7
|
+
const delayedSetSearchValue = useMemo(
|
|
8
|
+
() => debounce(searchValue => setSearchValue(searchValue), 375),
|
|
9
|
+
[setSearchValue]
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
const handleOnChange = ev => {
|
|
13
|
+
delayedSetSearchValue(ev.target.value)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return <SearchBar size="small" elevation={0} onChange={handleOnChange} />
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default SearchInput
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import cx from 'classnames'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
|
|
5
|
+
import GroupsSelection from './GroupsSelection'
|
|
6
|
+
import ImportDropdown from './ImportDropdown'
|
|
7
|
+
import SearchInput from './SearchInput'
|
|
8
|
+
import { locales } from './locales'
|
|
9
|
+
import Button from '../../Buttons'
|
|
10
|
+
import Icon from '../../Icon'
|
|
11
|
+
import PersonAddIcon from '../../Icons/PersonAdd'
|
|
12
|
+
import { useBreakpoints } from '../../providers/Breakpoints'
|
|
13
|
+
import { useI18n, useExtendI18n } from '../../providers/I18n'
|
|
14
|
+
|
|
15
|
+
const Header = ({
|
|
16
|
+
allGroups,
|
|
17
|
+
onContactCreate,
|
|
18
|
+
onContactImport,
|
|
19
|
+
onSearch,
|
|
20
|
+
onGroupCreate,
|
|
21
|
+
onGroupDelete,
|
|
22
|
+
onGroupUpdate
|
|
23
|
+
}) => {
|
|
24
|
+
useExtendI18n(locales)
|
|
25
|
+
const { t } = useI18n()
|
|
26
|
+
const { isMobile } = useBreakpoints()
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className={!isMobile ? 'u-flex u-flex-justify-between' : undefined}>
|
|
30
|
+
<div
|
|
31
|
+
className={cx('u-flex u-flex-items-center u-w-auto-s u-w-5 u-maw-6', {
|
|
32
|
+
'u-mb-1': isMobile,
|
|
33
|
+
'u-mr-1': !isMobile
|
|
34
|
+
})}
|
|
35
|
+
>
|
|
36
|
+
<Button
|
|
37
|
+
className="u-mr-half"
|
|
38
|
+
variant="ghost"
|
|
39
|
+
startIcon={<Icon icon={PersonAddIcon} />}
|
|
40
|
+
label={t('Contacts.Header.create')}
|
|
41
|
+
fullWidth
|
|
42
|
+
onClick={onContactCreate}
|
|
43
|
+
/>
|
|
44
|
+
<ImportDropdown onContactImport={onContactImport} />
|
|
45
|
+
</div>
|
|
46
|
+
<div
|
|
47
|
+
className={
|
|
48
|
+
!isMobile
|
|
49
|
+
? 'u-flex u-flex-items-center u-flex-justify-end u-flex-grow-1 u-maw-7'
|
|
50
|
+
: undefined
|
|
51
|
+
}
|
|
52
|
+
>
|
|
53
|
+
<GroupsSelection
|
|
54
|
+
allGroups={allGroups}
|
|
55
|
+
onGroupCreate={onGroupCreate}
|
|
56
|
+
onGroupUpdate={onGroupUpdate}
|
|
57
|
+
onGroupDelete={onGroupDelete}
|
|
58
|
+
/>
|
|
59
|
+
<SearchInput setSearchValue={onSearch} />
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
Header.propTypes = {
|
|
66
|
+
allGroups: PropTypes.array,
|
|
67
|
+
onContactCreate: PropTypes.func,
|
|
68
|
+
onContactImport: PropTypes.func,
|
|
69
|
+
onSearch: PropTypes.func,
|
|
70
|
+
onGroupCreate: PropTypes.func,
|
|
71
|
+
onGroupUpdate: PropTypes.func,
|
|
72
|
+
onGroupDelete: PropTypes.func
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
Header.defaultProps = {
|
|
76
|
+
allGroups: []
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default Header
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Contacts": {
|
|
3
|
+
"Header": {
|
|
4
|
+
"create": "Create",
|
|
5
|
+
"filter": {
|
|
6
|
+
"no-group": "Create contact groups to facilitate filing and sharing!"
|
|
7
|
+
},
|
|
8
|
+
"import": {
|
|
9
|
+
"title": "Import",
|
|
10
|
+
"store": "From other sources",
|
|
11
|
+
"vcard": "From a vCard file"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Contacts": {
|
|
3
|
+
"Header": {
|
|
4
|
+
"create": "Créer",
|
|
5
|
+
"filter": {
|
|
6
|
+
"no-group": "Créer des groupes de contacts pour faciliter leur classement et le partage !"
|
|
7
|
+
},
|
|
8
|
+
"import": {
|
|
9
|
+
"title": "Importer",
|
|
10
|
+
"store": "Depuis d'autres sources",
|
|
11
|
+
"vcard": "Depuis un fichier vCard"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!-- For styleguidist, a .md file is needed in directories -->
|
|
@@ -14,7 +14,11 @@ const MyselfMarker = () => {
|
|
|
14
14
|
useExtendI18n(locales)
|
|
15
15
|
const { t } = useI18n()
|
|
16
16
|
|
|
17
|
-
return
|
|
17
|
+
return (
|
|
18
|
+
<span className={`${styles['contact-myself']}`}>
|
|
19
|
+
({t('ContactsList.me')})
|
|
20
|
+
</span>
|
|
21
|
+
)
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
const ContactIdentity = ({ contact }) => {
|
|
@@ -4,13 +4,15 @@ import React from 'react'
|
|
|
4
4
|
import ContactRow from './ContactRow'
|
|
5
5
|
import { sortContacts, categorizeContacts, sortHeaders } from './helpers'
|
|
6
6
|
import withContactsListLocales from './locales/withContactsListLocales'
|
|
7
|
+
import { locales } from './locales/withContactsListLocales'
|
|
7
8
|
import List from '../List'
|
|
8
9
|
import ListSubheader from '../ListSubheader'
|
|
9
10
|
import { Table } from '../deprecated/Table'
|
|
10
11
|
import useBreakpoints from '../providers/Breakpoints'
|
|
11
|
-
import { useI18n } from '../providers/I18n'
|
|
12
|
+
import { useI18n, useExtendI18n } from '../providers/I18n'
|
|
12
13
|
|
|
13
14
|
const ContactsList = ({ contacts, onItemClick, ...rest }) => {
|
|
15
|
+
useExtendI18n(locales)
|
|
14
16
|
const { t } = useI18n()
|
|
15
17
|
const sortedContacts = sortContacts(contacts)
|
|
16
18
|
const categorizedContacts = categorizeContacts(sortedContacts, t)
|