@manuscripts/body-editor 3.0.2 → 3.0.3-LEAN-4579.1
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/dist/cjs/components/affiliations/AffiliationsModal.js +79 -126
- package/dist/cjs/components/authors/AffiliationDrawer.js +24 -0
- package/dist/cjs/components/authors/AuthorDetailsForm.js +11 -9
- package/dist/cjs/components/authors/AuthorsModal.js +121 -204
- package/dist/cjs/components/authors/CreditDrawer.js +37 -0
- package/dist/cjs/components/authors/useManageAffiliations.js +41 -0
- package/dist/cjs/components/authors/useManageCredit.js +43 -0
- package/dist/cjs/components/form/FormFooter.js +2 -0
- package/dist/cjs/components/form/ModalFormActions.js +4 -4
- package/dist/cjs/components/modal-drawer/GenericDrawer.js +16 -0
- package/dist/cjs/components/modal-drawer/GenericDrawerGroup.js +75 -0
- package/dist/cjs/lib/normalize.js +49 -0
- package/dist/cjs/versions.js +1 -1
- package/dist/es/components/affiliations/AffiliationsModal.js +80 -127
- package/dist/es/components/authors/AffiliationDrawer.js +17 -0
- package/dist/es/components/authors/AuthorDetailsForm.js +9 -7
- package/dist/es/components/authors/AuthorsModal.js +123 -206
- package/dist/es/components/authors/CreditDrawer.js +30 -0
- package/dist/es/components/authors/useManageAffiliations.js +37 -0
- package/dist/es/components/authors/useManageCredit.js +39 -0
- package/dist/es/components/form/FormFooter.js +2 -0
- package/dist/es/components/form/ModalFormActions.js +4 -4
- package/dist/es/components/modal-drawer/GenericDrawer.js +9 -0
- package/dist/es/components/modal-drawer/GenericDrawerGroup.js +68 -0
- package/dist/es/lib/normalize.js +44 -0
- package/dist/es/versions.js +1 -1
- package/dist/types/components/authors/AffiliationDrawer.d.ts +12 -0
- package/dist/types/components/authors/AuthorDetailsForm.d.ts +4 -1
- package/dist/types/components/authors/AuthorsModal.d.ts +1 -2
- package/dist/types/components/authors/CreditDrawer.d.ts +14 -0
- package/dist/types/components/authors/useManageAffiliations.d.ts +18 -0
- package/dist/types/components/authors/useManageCredit.d.ts +13 -0
- package/dist/types/components/form/ModalFormActions.d.ts +2 -2
- package/dist/types/components/modal-drawer/GenericDrawer.d.ts +14 -0
- package/dist/types/components/modal-drawer/GenericDrawerGroup.d.ts +45 -0
- package/dist/types/lib/normalize.d.ts +25 -0
- package/dist/types/types.d.ts +1 -0
- package/dist/types/versions.d.ts +1 -1
- package/package.json +4 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* ©
|
|
2
|
+
* © 2025 Atypon Systems LLC
|
|
3
3
|
*
|
|
4
4
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
* you may not use this file except in compliance with the License.
|
|
@@ -13,113 +13,27 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { buildBibliographicName,
|
|
17
|
-
import { AddIcon, AddInstitutionIcon, AuthorPlaceholderIcon, CloseButton,
|
|
16
|
+
import { buildBibliographicName, } from '@manuscripts/json-schema';
|
|
17
|
+
import { AddIcon, AddInstitutionIcon, AddRoleIcon, AuthorPlaceholderIcon, CloseButton, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, ScrollableModalContent, SidebarContent, StyledModal, } from '@manuscripts/style-guide';
|
|
18
|
+
import { generateNodeID, schema } from '@manuscripts/transform';
|
|
18
19
|
import { cloneDeep, isEqual, omit } from 'lodash';
|
|
19
|
-
import React, { useEffect, useReducer, useRef, useState } from 'react';
|
|
20
|
+
import React, { useCallback, useEffect, useReducer, useRef, useState, } from 'react';
|
|
20
21
|
import styled from 'styled-components';
|
|
21
22
|
import { arrayReducer } from '../../lib/array-reducer';
|
|
22
23
|
import { authorComparator, } from '../../lib/authors';
|
|
24
|
+
import { normalizeAuthor } from '../../lib/normalize';
|
|
23
25
|
import { ConfirmationDialog, DialogType } from '../dialog/ConfirmationDialog';
|
|
24
26
|
import FormFooter from '../form/FormFooter';
|
|
25
27
|
import { FormPlaceholder } from '../form/FormPlaceholder';
|
|
26
28
|
import { ModalFormActions } from '../form/ModalFormActions';
|
|
29
|
+
import { DrawerGroup } from '../modal-drawer/GenericDrawerGroup';
|
|
30
|
+
import { AffiliationsDrawer } from './AffiliationDrawer';
|
|
27
31
|
import { AuthorDetailsForm } from './AuthorDetailsForm';
|
|
28
32
|
import { AuthorList } from './AuthorList';
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
padding: 12px 8px 12px 12px;
|
|
33
|
-
cursor: pointer;
|
|
34
|
-
&[data-active='true'] {
|
|
35
|
-
background: ${(props) => props.theme.colors.background.fifth};
|
|
36
|
-
border: 1px solid ${(props) => props.theme.colors.border.primary};
|
|
37
|
-
border-left: 0;
|
|
38
|
-
border-right: 0;
|
|
39
|
-
}
|
|
40
|
-
`;
|
|
41
|
-
const ActionTitle = styled.div `
|
|
42
|
-
padding-left: ${(props) => props.theme.grid.unit * 2}px;
|
|
43
|
-
`;
|
|
44
|
-
const FormLabel = styled.legend `
|
|
45
|
-
margin-bottom: 12px;
|
|
46
|
-
font: ${(props) => props.theme.font.weight.normal}
|
|
47
|
-
${(props) => props.theme.font.size.xlarge} /
|
|
48
|
-
${(props) => props.theme.font.lineHeight.large}
|
|
49
|
-
${(props) => props.theme.font.family.sans};
|
|
50
|
-
letter-spacing: -0.4px;
|
|
51
|
-
color: ${(props) => props.theme.colors.text.secondary};
|
|
52
|
-
`;
|
|
53
|
-
const AuthorForms = styled.div `
|
|
54
|
-
padding-left: ${(props) => props.theme.grid.unit * 3}px;
|
|
55
|
-
padding-right: ${(props) => props.theme.grid.unit * 3}px;
|
|
56
|
-
position: relative;
|
|
57
|
-
margin-top: 20px;
|
|
58
|
-
`;
|
|
59
|
-
const StyledSidebarContent = styled(SidebarContent) `
|
|
60
|
-
padding: 0;
|
|
61
|
-
`;
|
|
62
|
-
const AuthorsSection = styled.div `
|
|
63
|
-
margin-top: ${(props) => props.theme.grid.unit * 4}px;
|
|
64
|
-
padding-top: ${(props) => props.theme.grid.unit * 4}px;
|
|
65
|
-
border-top: 1px solid ${(props) => props.theme.colors.border.tertiary};
|
|
66
|
-
`;
|
|
67
|
-
const AuthorsHeader = styled.div `
|
|
68
|
-
display: flex;
|
|
69
|
-
justify-content: space-between;
|
|
70
|
-
align-items: flex-start;
|
|
71
|
-
flex-direction: column;
|
|
72
|
-
margin-bottom: ${(props) => props.theme.grid.unit * 2}px;
|
|
73
|
-
`;
|
|
74
|
-
const AuthorsTitle = styled.h3 `
|
|
75
|
-
margin: 0;
|
|
76
|
-
font-weight: ${(props) => props.theme.font.weight.normal};
|
|
77
|
-
font-size: ${(props) => props.theme.font.size.large};
|
|
78
|
-
font-family: ${(props) => props.theme.font.family.sans};
|
|
79
|
-
color: ${(props) => props.theme.colors.text.secondary};
|
|
80
|
-
`;
|
|
81
|
-
const AffiliateButton = styled.button `
|
|
82
|
-
color: ${(props) => props.theme.colors.brand.default};
|
|
83
|
-
background: none;
|
|
84
|
-
border: none;
|
|
85
|
-
cursor: pointer;
|
|
86
|
-
padding: 0;
|
|
87
|
-
font: ${(props) => props.theme.font.weight.normal}
|
|
88
|
-
${(props) => props.theme.font.size.normal}
|
|
89
|
-
${(props) => props.theme.font.family.sans};
|
|
90
|
-
display: flex;
|
|
91
|
-
align-items: center;
|
|
92
|
-
gap: 4px;
|
|
93
|
-
margin-top: ${(props) => props.theme.grid.unit * 2}px;
|
|
94
|
-
&:hover {
|
|
95
|
-
opacity: 0.8;
|
|
96
|
-
}
|
|
97
|
-
`;
|
|
98
|
-
const StyledModalBody = styled(ModalBody) `
|
|
99
|
-
position: relative;
|
|
100
|
-
height: calc(90vh - 40px);
|
|
101
|
-
`;
|
|
102
|
-
const StyledModalSidebarHeader = styled(ModalSidebarHeader) `
|
|
103
|
-
margin-bottom: 16px;
|
|
104
|
-
`;
|
|
33
|
+
import { CreditDrawer } from './CreditDrawer';
|
|
34
|
+
import { useManageAffiliations } from './useManageAffiliations';
|
|
35
|
+
import { useManageCredit } from './useManageCredit';
|
|
105
36
|
export const authorsReducer = arrayReducer((a, b) => a.id === b.id);
|
|
106
|
-
export const affiliationsReducer = arrayReducer((a, b) => a.id === b.id);
|
|
107
|
-
const normalize = (author) => ({
|
|
108
|
-
id: author.id,
|
|
109
|
-
role: author.role || '',
|
|
110
|
-
affiliations: (author.affiliations || []).sort(),
|
|
111
|
-
bibliographicName: author.bibliographicName,
|
|
112
|
-
email: author.email || '',
|
|
113
|
-
isCorresponding: author.isCorresponding || false,
|
|
114
|
-
ORCIDIdentifier: author.ORCIDIdentifier || '',
|
|
115
|
-
priority: author.priority || 0,
|
|
116
|
-
isJointContributor: author.isJointContributor || false,
|
|
117
|
-
userID: author.userID || '',
|
|
118
|
-
invitationID: author.invitationID || '',
|
|
119
|
-
footnote: author.footnote || [],
|
|
120
|
-
corresp: author.corresp || [],
|
|
121
|
-
prefix: author.prefix || '',
|
|
122
|
-
});
|
|
123
37
|
export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, author, onSaveAuthor, onDeleteAuthor, addNewAuthor = false, }) => {
|
|
124
38
|
const [isOpen, setOpen] = useState(true);
|
|
125
39
|
const [isDisableSave, setDisableSave] = useState(true);
|
|
@@ -127,48 +41,38 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
|
|
|
127
41
|
const [showConfirmationDialog, setShowConfirmationDialog] = useState(false);
|
|
128
42
|
const [showRequiredFieldConfirmationDialog, setShowRequiredFieldConfirmationDialog,] = useState(false);
|
|
129
43
|
const [lastSavedAuthor, setLastSavedAuthor] = useState(null);
|
|
130
|
-
const [
|
|
44
|
+
const [showingDeleteDialog, setShowDeleteDialog] = useState(false);
|
|
131
45
|
const [newAuthor, setNewAuthor] = useState(false);
|
|
132
46
|
const [unSavedChanges, setUnSavedChanges] = useState(false);
|
|
133
47
|
const [nextAuthor, setNextAuthor] = useState(null);
|
|
134
48
|
const [isSwitchingAuthor, setIsSwitchingAuthor] = useState(false);
|
|
135
49
|
const [isCreatingNewAuthor, setIsCreatingNewAuthor] = useState(false);
|
|
136
|
-
const [
|
|
137
|
-
const [selectedAffiliations, setSelectedAffiliations] = useState([]);
|
|
50
|
+
const [showCreditDrawer, setShowCreditDrawer] = useState(false);
|
|
138
51
|
const valuesRef = useRef();
|
|
139
52
|
const actionsRef = useRef();
|
|
140
53
|
const authorFormRef = useRef(null);
|
|
141
54
|
const [authors, dispatchAuthors] = useReducer(authorsReducer, $authors.sort(authorComparator));
|
|
142
|
-
const [affiliations] = useReducer(affiliationsReducer, $affiliations);
|
|
143
|
-
const affiliationItems = affiliations.map((affiliation) => ({
|
|
144
|
-
id: affiliation.id,
|
|
145
|
-
label: affiliation.institution,
|
|
146
|
-
country: affiliation.country,
|
|
147
|
-
city: affiliation.city,
|
|
148
|
-
state: affiliation.county,
|
|
149
|
-
}));
|
|
150
|
-
const [selectedAffiliationIds, setSelectedAffiliationIds] = useState([]);
|
|
151
55
|
useEffect(() => {
|
|
152
56
|
if (addNewAuthor) {
|
|
153
|
-
|
|
57
|
+
addAuthor();
|
|
154
58
|
}
|
|
155
59
|
}, [addNewAuthor]);
|
|
156
60
|
const [selection, setSelection] = useState(author);
|
|
61
|
+
const { showAffiliationDrawer, setShowAffiliationDrawer, selectedAffiliations, setSelectedAffiliations, removeAffiliation, selectAffiliation, affiliations, } = useManageAffiliations(selection, $affiliations);
|
|
157
62
|
useEffect(() => {
|
|
158
63
|
const currentAuthor = selection;
|
|
159
|
-
const relevantAffiliations =
|
|
64
|
+
const relevantAffiliations = affiliations.filter((item) => currentAuthor?.affiliations?.includes(item.id));
|
|
160
65
|
setSelectedAffiliations(relevantAffiliations);
|
|
161
|
-
setSelectedAffiliationIds(relevantAffiliations.map((item) => item.id));
|
|
162
66
|
}, []);
|
|
163
|
-
const
|
|
67
|
+
const selectAuthor = (author) => {
|
|
164
68
|
if (author.id === selection?.id) {
|
|
165
69
|
return;
|
|
166
70
|
}
|
|
167
71
|
const values = valuesRef.current;
|
|
168
72
|
setIsCreatingNewAuthor(false);
|
|
169
73
|
if (values && selection) {
|
|
170
|
-
const normalizedSelection =
|
|
171
|
-
const normalizedValues =
|
|
74
|
+
const normalizedSelection = normalizeAuthor(selection);
|
|
75
|
+
const normalizedValues = normalizeAuthor(values);
|
|
172
76
|
const hasChanges = !isEqual(normalizedSelection, normalizedValues);
|
|
173
77
|
if (hasChanges && !isDisableSave) {
|
|
174
78
|
setShowConfirmationDialog(true);
|
|
@@ -193,11 +97,10 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
|
|
|
193
97
|
}
|
|
194
98
|
};
|
|
195
99
|
const updateAffiliationSelection = (author) => {
|
|
196
|
-
const relevantAffiliations =
|
|
100
|
+
const relevantAffiliations = affiliations.filter((item) => author.affiliations?.includes(item.id));
|
|
197
101
|
setSelectedAffiliations(relevantAffiliations);
|
|
198
|
-
setSelectedAffiliationIds(relevantAffiliations.map((item) => item.id));
|
|
199
102
|
};
|
|
200
|
-
const
|
|
103
|
+
const close = () => {
|
|
201
104
|
if (unSavedChanges) {
|
|
202
105
|
if (isDisableSave) {
|
|
203
106
|
setShowRequiredFieldConfirmationDialog(true);
|
|
@@ -212,7 +115,7 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
|
|
|
212
115
|
setOpen(false);
|
|
213
116
|
}
|
|
214
117
|
};
|
|
215
|
-
const
|
|
118
|
+
const save = () => {
|
|
216
119
|
if (!authorFormRef.current?.checkValidity()) {
|
|
217
120
|
setShowConfirmationDialog(false);
|
|
218
121
|
setTimeout(() => {
|
|
@@ -221,7 +124,7 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
|
|
|
221
124
|
}
|
|
222
125
|
else {
|
|
223
126
|
if (valuesRef.current && selection) {
|
|
224
|
-
|
|
127
|
+
saveAuthor(valuesRef.current);
|
|
225
128
|
}
|
|
226
129
|
if (nextAuthor) {
|
|
227
130
|
setSelection(nextAuthor);
|
|
@@ -238,12 +141,11 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
|
|
|
238
141
|
setShowConfirmationDialog(false);
|
|
239
142
|
}
|
|
240
143
|
};
|
|
241
|
-
const
|
|
242
|
-
|
|
144
|
+
const cancel = () => {
|
|
145
|
+
resetAuthor();
|
|
243
146
|
if (nextAuthor) {
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
setSelectedAffiliations(affiliationItems.filter((item) => affiliations.includes(item.id)));
|
|
147
|
+
const nextAuthorAffiliations = nextAuthor.affiliations || [];
|
|
148
|
+
setSelectedAffiliations(affiliations.filter((item) => nextAuthorAffiliations.includes(item.id)));
|
|
247
149
|
setSelection(nextAuthor);
|
|
248
150
|
setNextAuthor(null);
|
|
249
151
|
setNewAuthor(false);
|
|
@@ -263,7 +165,7 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
|
|
|
263
165
|
setShowRequiredFieldConfirmationDialog(false);
|
|
264
166
|
setShowAffiliationDrawer(false);
|
|
265
167
|
};
|
|
266
|
-
const
|
|
168
|
+
const saveAuthor = (values) => {
|
|
267
169
|
if (!values || !selection) {
|
|
268
170
|
return;
|
|
269
171
|
}
|
|
@@ -287,7 +189,7 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
|
|
|
287
189
|
items: [author],
|
|
288
190
|
});
|
|
289
191
|
};
|
|
290
|
-
const
|
|
192
|
+
const moveAuthor = useCallback((from, to, shift) => {
|
|
291
193
|
const copy = cloneDeep(authors);
|
|
292
194
|
const order = copy.map((a, i) => {
|
|
293
195
|
if (a.id === from.id) {
|
|
@@ -311,38 +213,23 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
|
|
|
311
213
|
type: 'set',
|
|
312
214
|
state: copy,
|
|
313
215
|
});
|
|
314
|
-
};
|
|
216
|
+
}, [authors, dispatchAuthors, onSaveAuthor]);
|
|
315
217
|
const createNewAuthor = () => {
|
|
316
218
|
const name = buildBibliographicName({ given: '', family: '' });
|
|
317
|
-
const author =
|
|
318
|
-
id: generateID(ObjectTypes.Contributor),
|
|
319
|
-
role: '',
|
|
320
|
-
affiliations: [],
|
|
321
|
-
bibliographicName: name,
|
|
322
|
-
email: '',
|
|
323
|
-
isCorresponding: false,
|
|
324
|
-
ORCIDIdentifier: '',
|
|
325
|
-
priority: authors.length,
|
|
326
|
-
isJointContributor: false,
|
|
327
|
-
userID: '',
|
|
328
|
-
invitationID: '',
|
|
329
|
-
corresp: [],
|
|
330
|
-
footnote: [],
|
|
331
|
-
prefix: '',
|
|
332
|
-
};
|
|
219
|
+
const author = createEmptyAuthor(name, authors.length);
|
|
333
220
|
setIsSwitchingAuthor(!!selection);
|
|
334
221
|
setSelectedAffiliations([]);
|
|
335
|
-
|
|
222
|
+
setSelectedCreditRoles([]);
|
|
336
223
|
setSelection(author);
|
|
337
224
|
setNewAuthor(true);
|
|
338
225
|
};
|
|
339
|
-
const
|
|
226
|
+
const addAuthor = () => {
|
|
340
227
|
const values = valuesRef.current;
|
|
341
228
|
setIsSwitchingAuthor(!!selection);
|
|
342
229
|
setIsCreatingNewAuthor(true);
|
|
343
230
|
if (values &&
|
|
344
231
|
selection &&
|
|
345
|
-
!isEqual(
|
|
232
|
+
!isEqual(normalizeAuthor(values), normalizeAuthor(selection))) {
|
|
346
233
|
if (isDisableSave) {
|
|
347
234
|
setShowRequiredFieldConfirmationDialog(true);
|
|
348
235
|
}
|
|
@@ -356,7 +243,7 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
|
|
|
356
243
|
setShowAffiliationDrawer(false);
|
|
357
244
|
}
|
|
358
245
|
};
|
|
359
|
-
const
|
|
246
|
+
const deleteAuthor = () => {
|
|
360
247
|
if (!selection) {
|
|
361
248
|
return;
|
|
362
249
|
}
|
|
@@ -368,19 +255,10 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
|
|
|
368
255
|
item: selection,
|
|
369
256
|
});
|
|
370
257
|
};
|
|
371
|
-
const
|
|
372
|
-
if (!selection) {
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
const newAffiliations = selectedAffiliationIds.filter((id) => id !== affId);
|
|
376
|
-
setSelectedAffiliationIds(newAffiliations);
|
|
377
|
-
setSelectedAffiliations(affiliationItems.filter((item) => newAffiliations.includes(item.id)));
|
|
378
|
-
};
|
|
379
|
-
const handleResetAuthor = () => {
|
|
258
|
+
const resetAuthor = () => {
|
|
380
259
|
actionsRef.current?.reset();
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
setSelectedAffiliations(affiliationItems.filter((item) => affiliations.includes(item.id)));
|
|
260
|
+
const selectedAffs = selection?.affiliations || [];
|
|
261
|
+
setSelectedAffiliations(affiliations.filter((item) => selectedAffs.includes(item.id)));
|
|
384
262
|
setShowConfirmationDialog(false);
|
|
385
263
|
setShowRequiredFieldConfirmationDialog(false);
|
|
386
264
|
setUnSavedChanges(false);
|
|
@@ -393,17 +271,12 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
|
|
|
393
271
|
}
|
|
394
272
|
setIsCreatingNewAuthor(false);
|
|
395
273
|
};
|
|
396
|
-
const
|
|
397
|
-
const normalized = omit(
|
|
398
|
-
const updatedValues = omit(
|
|
274
|
+
const changeAuthor = (values) => {
|
|
275
|
+
const normalized = omit(normalizeAuthor(selection), 'priority');
|
|
276
|
+
const updatedValues = omit(normalizeAuthor(values), 'priority');
|
|
399
277
|
const isSameAuthor = updatedValues.id === normalized.id;
|
|
400
278
|
const hasChanges = !isEqual(updatedValues, normalized);
|
|
401
|
-
|
|
402
|
-
setUnSavedChanges(true);
|
|
403
|
-
}
|
|
404
|
-
else {
|
|
405
|
-
setUnSavedChanges(false);
|
|
406
|
-
}
|
|
279
|
+
setUnSavedChanges(isSameAuthor && hasChanges);
|
|
407
280
|
valuesRef.current = { ...updatedValues, priority: values.priority };
|
|
408
281
|
const { given, family } = values.bibliographicName;
|
|
409
282
|
const { email, isCorresponding } = values;
|
|
@@ -421,50 +294,94 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
|
|
|
421
294
|
}
|
|
422
295
|
setEmailRequired(isCorresponding);
|
|
423
296
|
};
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
};
|
|
427
|
-
const handleAffiliationSelect = (affiliationId) => {
|
|
428
|
-
if (!selection) {
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
const currentAffiliations = selectedAffiliationIds || [];
|
|
432
|
-
const isAlreadySelected = currentAffiliations.includes(affiliationId);
|
|
433
|
-
const newAffiliations = isAlreadySelected
|
|
434
|
-
? currentAffiliations.filter((id) => id !== affiliationId)
|
|
435
|
-
: [...currentAffiliations, affiliationId];
|
|
436
|
-
setSelectedAffiliationIds(newAffiliations);
|
|
437
|
-
setSelectedAffiliations(affiliationItems.filter((item) => newAffiliations.includes(item.id)));
|
|
438
|
-
};
|
|
439
|
-
return (React.createElement(StyledModal, { isOpen: isOpen, onRequestClose: () => handleClose(), shouldCloseOnOverlayClick: true },
|
|
297
|
+
const { removeCreditRole, selectCreditRole, selectedCreditRoles, setSelectedCreditRoles, vocabTermItems, } = useManageCredit(selection);
|
|
298
|
+
return (React.createElement(StyledModal, { isOpen: isOpen, onRequestClose: () => close(), shouldCloseOnOverlayClick: true },
|
|
440
299
|
React.createElement(ModalContainer, { "data-cy": "authors-modal" },
|
|
441
300
|
React.createElement(ModalHeader, null,
|
|
442
|
-
React.createElement(CloseButton, { onClick: () =>
|
|
301
|
+
React.createElement(CloseButton, { onClick: () => close(), "data-cy": "modal-close-button" })),
|
|
443
302
|
React.createElement(StyledModalBody, null,
|
|
444
303
|
React.createElement(ModalSidebar, { "data-cy": "authors-sidebar" },
|
|
445
304
|
React.createElement(StyledModalSidebarHeader, null,
|
|
446
305
|
React.createElement(ModalSidebarTitle, null, "Authors")),
|
|
447
306
|
React.createElement(StyledSidebarContent, null,
|
|
448
|
-
React.createElement(AddAuthorButton, { "data-cy": "add-author-button", onClick:
|
|
307
|
+
React.createElement(AddAuthorButton, { "data-cy": "add-author-button", onClick: addAuthor, "data-active": isCreatingNewAuthor || newAuthor },
|
|
449
308
|
React.createElement(AddIcon, { width: 18, height: 18 }),
|
|
450
309
|
React.createElement(ActionTitle, null, "New Author")),
|
|
451
|
-
React.createElement(AuthorList, { author: selection, authors: authors, onSelect:
|
|
452
|
-
React.createElement(
|
|
453
|
-
React.createElement(
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
React.createElement(
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
showAffiliationDrawer && (React.createElement(Drawer, { items: affiliationItems, selectedIds: selectedAffiliationIds, title: "Authors", onSelect: handleAffiliationSelect, onBack: () => setShowAffiliationDrawer(false), width: "100%" })))) : (React.createElement(FormPlaceholder, { type: "author", title: "Author Details", message: "Select an author from the list to display their details here.", placeholderIcon: React.createElement(AuthorPlaceholderIcon, null) })))),
|
|
469
|
-
React.createElement(FormFooter, { onCancel: handleClose }))));
|
|
310
|
+
React.createElement(AuthorList, { author: selection, authors: authors, onSelect: selectAuthor, onDelete: () => setShowDeleteDialog((prev) => !prev), moveAuthor: moveAuthor, lastSavedAuthor: lastSavedAuthor }))),
|
|
311
|
+
React.createElement(DrawerRelativeParent, null,
|
|
312
|
+
React.createElement(ScrollableModalContent, { "data-cy": "author-modal-content" }, selection ? (React.createElement(AuthorForms, null,
|
|
313
|
+
React.createElement(ConfirmationDialog, { isOpen: showRequiredFieldConfirmationDialog, onPrimary: () => setShowRequiredFieldConfirmationDialog(false), onSecondary: cancel, type: DialogType.REQUIRED, entityType: "author" }),
|
|
314
|
+
React.createElement(ConfirmationDialog, { isOpen: showConfirmationDialog, onPrimary: save, onSecondary: cancel, type: DialogType.SAVE, entityType: "author" }),
|
|
315
|
+
React.createElement(ModalFormActions, { form: 'author-details-form', type: "author", onDelete: deleteAuthor, showingDeleteDialog: showingDeleteDialog, showDeleteDialog: () => setShowDeleteDialog((prev) => !prev), newEntity: newAuthor ||
|
|
316
|
+
(isCreatingNewAuthor &&
|
|
317
|
+
!showConfirmationDialog &&
|
|
318
|
+
!showRequiredFieldConfirmationDialog), isDisableSave: isDisableSave }),
|
|
319
|
+
React.createElement(FormLabel, null, "Details"),
|
|
320
|
+
React.createElement(AuthorDetailsForm, { values: normalizeAuthor(selection), onChange: changeAuthor, onSave: saveAuthor, actionsRef: actionsRef, isEmailRequired: isEmailRequired, selectedAffiliations: selectedAffiliations.map((a) => a.id), authorFormRef: authorFormRef, selectedCreditRoles: selectedCreditRoles }),
|
|
321
|
+
React.createElement(DrawerGroup, { Drawer: AffiliationsDrawer, removeItem: removeAffiliation, selectedItems: selectedAffiliations, onSelect: selectAffiliation, items: affiliations, showDrawer: showAffiliationDrawer, setShowDrawer: setShowAffiliationDrawer, title: "Affiliations", buttonText: "Assign Institutions", cy: "affiliations", labelField: "institution", Icon: React.createElement(AddInstitutionIcon, { width: 16, height: 16 }) }),
|
|
322
|
+
React.createElement(DrawerGroup, { Drawer: CreditDrawer, removeItem: removeCreditRole, selectedItems: selectedCreditRoles.map((r) => ({
|
|
323
|
+
id: r.vocabTerm,
|
|
324
|
+
...r,
|
|
325
|
+
})), onSelect: selectCreditRole, items: vocabTermItems, showDrawer: showCreditDrawer, setShowDrawer: setShowCreditDrawer, title: "Contributions (Credit)", buttonText: "Assign Credit Roles", cy: "credit-taxnonomy", labelField: "vocabTerm", Icon: React.createElement(AddRoleIcon, { width: 16, height: 16 }) }))) : (React.createElement(FormPlaceholder, { type: "author", title: "Author Details", message: "Select an author from the list to display their details here.", placeholderIcon: React.createElement(AuthorPlaceholderIcon, null) }))))),
|
|
326
|
+
React.createElement(FormFooter, { onCancel: close }))));
|
|
470
327
|
};
|
|
328
|
+
function createEmptyAuthor(name, priority) {
|
|
329
|
+
return {
|
|
330
|
+
id: generateNodeID(schema.nodes.contributor),
|
|
331
|
+
role: '',
|
|
332
|
+
affiliations: [],
|
|
333
|
+
bibliographicName: name,
|
|
334
|
+
email: '',
|
|
335
|
+
isCorresponding: false,
|
|
336
|
+
ORCIDIdentifier: '',
|
|
337
|
+
priority,
|
|
338
|
+
isJointContributor: false,
|
|
339
|
+
userID: '',
|
|
340
|
+
invitationID: '',
|
|
341
|
+
corresp: [],
|
|
342
|
+
footnote: [],
|
|
343
|
+
prefix: '',
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
const AddAuthorButton = styled.div `
|
|
347
|
+
display: flex;
|
|
348
|
+
align-items: center;
|
|
349
|
+
padding: 12px 8px 12px 12px;
|
|
350
|
+
cursor: pointer;
|
|
351
|
+
&[data-active='true'] {
|
|
352
|
+
background: ${(props) => props.theme.colors.background.fifth};
|
|
353
|
+
border: 1px solid ${(props) => props.theme.colors.border.primary};
|
|
354
|
+
border-left: 0;
|
|
355
|
+
border-right: 0;
|
|
356
|
+
}
|
|
357
|
+
`;
|
|
358
|
+
const ActionTitle = styled.div `
|
|
359
|
+
padding-left: ${(props) => props.theme.grid.unit * 2}px;
|
|
360
|
+
`;
|
|
361
|
+
const FormLabel = styled.legend `
|
|
362
|
+
margin-bottom: 12px;
|
|
363
|
+
font: ${(props) => props.theme.font.weight.normal}
|
|
364
|
+
${(props) => props.theme.font.size.xlarge} /
|
|
365
|
+
${(props) => props.theme.font.lineHeight.large}
|
|
366
|
+
${(props) => props.theme.font.family.sans};
|
|
367
|
+
letter-spacing: -0.4px;
|
|
368
|
+
color: ${(props) => props.theme.colors.text.secondary};
|
|
369
|
+
`;
|
|
370
|
+
const AuthorForms = styled.div `
|
|
371
|
+
padding-left: ${(props) => props.theme.grid.unit * 3}px;
|
|
372
|
+
padding-right: ${(props) => props.theme.grid.unit * 3}px;
|
|
373
|
+
margin-top: 20px;
|
|
374
|
+
`;
|
|
375
|
+
const StyledSidebarContent = styled(SidebarContent) `
|
|
376
|
+
padding: 0;
|
|
377
|
+
`;
|
|
378
|
+
const StyledModalBody = styled(ModalBody) `
|
|
379
|
+
position: relative;
|
|
380
|
+
height: calc(90vh - 40px);
|
|
381
|
+
`;
|
|
382
|
+
const StyledModalSidebarHeader = styled(ModalSidebarHeader) `
|
|
383
|
+
margin-bottom: 16px;
|
|
384
|
+
`;
|
|
385
|
+
const DrawerRelativeParent = styled.div `
|
|
386
|
+
position: relative;
|
|
387
|
+
`;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { CheckboxField, CheckboxLabel, Drawer, DrawerItemsList, } from '@manuscripts/style-guide';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { CheckboxContainer, LabelText } from './AuthorDetailsForm';
|
|
5
|
+
export const CreditDrawer = ({ items, selectedItems = [], onSelect, ...drawerProps }) => {
|
|
6
|
+
return (React.createElement(Drawer, { ...drawerProps },
|
|
7
|
+
React.createElement(TwoColumnContainer, null, items.map((item, i) => (React.createElement(TwoColumnCheckbox, { key: item.id },
|
|
8
|
+
React.createElement(CheckboxLabel, null,
|
|
9
|
+
React.createElement(CheckboxField, { id: 'credit-role-' + i, name: item.id, checked: selectedItems?.map((a) => a.id).includes(item.id), onChange: () => {
|
|
10
|
+
onSelect(item.id);
|
|
11
|
+
} }),
|
|
12
|
+
React.createElement(LabelText, null, item.vocabTerm))))))));
|
|
13
|
+
};
|
|
14
|
+
const TwoColumnContainer = styled(DrawerItemsList) `
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-flow: row wrap;
|
|
17
|
+
padding: 0 ${(props) => props.theme.grid.unit * 4}px;
|
|
18
|
+
position: relative;
|
|
19
|
+
|
|
20
|
+
&:after {
|
|
21
|
+
content: '';
|
|
22
|
+
display: block;
|
|
23
|
+
border-bottom: 1px solid #f0f0f0;
|
|
24
|
+
min-width: 100%;
|
|
25
|
+
padding-top: 16px;
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
const TwoColumnCheckbox = styled(CheckboxContainer) `
|
|
29
|
+
flex 1 0 50%;
|
|
30
|
+
`;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useReducer, useState } from 'react';
|
|
2
|
+
import { arrayReducer } from '../../lib/array-reducer';
|
|
3
|
+
export const affiliationsReducer = arrayReducer((a, b) => a.id === b.id);
|
|
4
|
+
export const useManageAffiliations = (selection, $affiliations) => {
|
|
5
|
+
const [affiliations] = useReducer(affiliationsReducer, $affiliations);
|
|
6
|
+
const [showAffiliationDrawer, setShowAffiliationDrawer] = useState(false);
|
|
7
|
+
const [selectedAffiliations, setSelectedAffiliations] = useState([]);
|
|
8
|
+
const removeAffiliation = (affId) => {
|
|
9
|
+
if (!selection) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const newAffiliations = selectedAffiliations
|
|
13
|
+
.map((a) => a.id)
|
|
14
|
+
.filter((id) => id !== affId);
|
|
15
|
+
setSelectedAffiliations(affiliations.filter((item) => newAffiliations.includes(item.id)));
|
|
16
|
+
};
|
|
17
|
+
const selectAffiliation = (affiliationId) => {
|
|
18
|
+
if (!selection) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const currentAffiliations = selectedAffiliations.map((a) => a.id);
|
|
22
|
+
const isAlreadySelected = currentAffiliations.includes(affiliationId);
|
|
23
|
+
const newAffiliations = isAlreadySelected
|
|
24
|
+
? currentAffiliations.filter((id) => id !== affiliationId)
|
|
25
|
+
: [...currentAffiliations, affiliationId];
|
|
26
|
+
setSelectedAffiliations(affiliations.filter((item) => newAffiliations.includes(item.id)));
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
showAffiliationDrawer,
|
|
30
|
+
setShowAffiliationDrawer,
|
|
31
|
+
selectedAffiliations,
|
|
32
|
+
setSelectedAffiliations,
|
|
33
|
+
removeAffiliation,
|
|
34
|
+
selectAffiliation,
|
|
35
|
+
affiliations,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { CreditVocabTerm } from '@manuscripts/transform';
|
|
2
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
export const useManageCredit = (selection) => {
|
|
4
|
+
const vocabTermItems = useMemo(() => {
|
|
5
|
+
return Object.values(CreditVocabTerm).map((c) => ({
|
|
6
|
+
vocabTerm: c,
|
|
7
|
+
id: c,
|
|
8
|
+
}));
|
|
9
|
+
}, []);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
setSelectedCreditRoles(selection?.creditRoles ? selection?.creditRoles : []);
|
|
12
|
+
}, [selection]);
|
|
13
|
+
const [selectedCreditRoles, setSelectedCreditRoles] = useState([]);
|
|
14
|
+
const selectCreditRole = (role) => {
|
|
15
|
+
setSelectedCreditRoles((prev) => {
|
|
16
|
+
const clear = prev.filter((t) => t.vocabTerm !== role);
|
|
17
|
+
if (clear.length !== prev.length) {
|
|
18
|
+
return clear;
|
|
19
|
+
}
|
|
20
|
+
const newTerm = vocabTermItems.find((t) => t.vocabTerm === role);
|
|
21
|
+
if (newTerm) {
|
|
22
|
+
return [...prev, { vocabTerm: newTerm.vocabTerm }];
|
|
23
|
+
}
|
|
24
|
+
return prev;
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
const removeCreditRole = (role) => {
|
|
28
|
+
setSelectedCreditRoles((prev) => {
|
|
29
|
+
return prev.filter((r) => r.vocabTerm !== role);
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
removeCreditRole,
|
|
34
|
+
selectCreditRole,
|
|
35
|
+
selectedCreditRoles,
|
|
36
|
+
setSelectedCreditRoles,
|
|
37
|
+
vocabTermItems,
|
|
38
|
+
};
|
|
39
|
+
};
|
|
@@ -26,12 +26,12 @@ const StyledIconButton = styled(IconButton) `
|
|
|
26
26
|
margin-right: 4px;
|
|
27
27
|
}
|
|
28
28
|
`;
|
|
29
|
-
export const ModalFormActions = ({ type, form, onDelete,
|
|
29
|
+
export const ModalFormActions = ({ type, form, onDelete, showingDeleteDialog, showDeleteDialog, newEntity, isDisableSave, }) => {
|
|
30
30
|
return (React.createElement(ActionsContainer, { "data-cy": `${type}-action` },
|
|
31
|
-
React.createElement(ConfirmationDialog, { isOpen:
|
|
31
|
+
React.createElement(ConfirmationDialog, { isOpen: showingDeleteDialog, onPrimary: () => {
|
|
32
32
|
onDelete();
|
|
33
|
-
|
|
34
|
-
}, onSecondary:
|
|
33
|
+
showDeleteDialog();
|
|
34
|
+
}, onSecondary: showDeleteDialog, type: DialogType.DELETE, entityType: type }),
|
|
35
35
|
React.createElement(StyledButtonGroup, null,
|
|
36
36
|
React.createElement(StyledIconButton, { disabled: isDisableSave, type: "submit", form: form },
|
|
37
37
|
React.createElement(PlusIcon, null),
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AddedIcon, AddIcon, Drawer, DrawerIcon, DrawerItemLabel, DrawerItemsList, DrawerLabelContainer, DrawerListItem, } from '@manuscripts/style-guide';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export const GenericDrawer = ({ items, selectedItems = [], onSelect, ...drawerProps }) => {
|
|
4
|
+
return (React.createElement(Drawer, { ...drawerProps },
|
|
5
|
+
React.createElement(DrawerItemsList, null, items.map((item) => (React.createElement(DrawerListItem, { "data-cy": "item", key: item.id, selected: selectedItems?.map((a) => a.id).includes(item.id), onClick: () => onSelect(item.id) },
|
|
6
|
+
React.createElement(DrawerIcon, null, selectedItems?.map((a) => a.id).includes(item.id) ? (React.createElement(AddedIcon, { width: 22, height: 22 })) : (React.createElement(AddIcon, { width: 22, height: 22 }))),
|
|
7
|
+
React.createElement(DrawerLabelContainer, null,
|
|
8
|
+
React.createElement(DrawerItemLabel, null, item.label))))))));
|
|
9
|
+
};
|